mapgen_v6.cpp 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123
  1. /*
  2. Minetest
  3. Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
  4. Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
  5. Copyright (C) 2014-2017 paramat
  6. This program is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU Lesser General Public License as published by
  8. the Free Software Foundation; either version 2.1 of the License, or
  9. (at your option) any later version.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU Lesser General Public License for more details.
  14. You should have received a copy of the GNU Lesser General Public License along
  15. with this program; if not, write to the Free Software Foundation, Inc.,
  16. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  17. */
  18. #include "mapgen.h"
  19. #include "voxel.h"
  20. #include "noise.h"
  21. #include "mapblock.h"
  22. #include "mapnode.h"
  23. #include "map.h"
  24. //#include "serverobject.h"
  25. #include "content_sao.h"
  26. #include "nodedef.h"
  27. #include "voxelalgorithms.h"
  28. //#include "profiler.h" // For TimeTaker
  29. #include "settings.h" // For g_settings
  30. #include "emerge.h"
  31. #include "dungeongen.h"
  32. #include "cavegen.h"
  33. #include "treegen.h"
  34. #include "mg_ore.h"
  35. #include "mg_decoration.h"
  36. #include "mapgen_v6.h"
  37. FlagDesc flagdesc_mapgen_v6[] = {
  38. {"jungles", MGV6_JUNGLES},
  39. {"biomeblend", MGV6_BIOMEBLEND},
  40. {"mudflow", MGV6_MUDFLOW},
  41. {"snowbiomes", MGV6_SNOWBIOMES},
  42. {"flat", MGV6_FLAT},
  43. {"trees", MGV6_TREES},
  44. {NULL, 0}
  45. };
  46. /////////////////////////////////////////////////////////////////////////////
  47. MapgenV6::MapgenV6(int mapgenid, MapgenV6Params *params, EmergeManager *emerge)
  48. : Mapgen(mapgenid, params, emerge)
  49. {
  50. m_emerge = emerge;
  51. ystride = csize.X; //////fix this
  52. heightmap = new s16[csize.X * csize.Z];
  53. spflags = params->spflags;
  54. freq_desert = params->freq_desert;
  55. freq_beach = params->freq_beach;
  56. np_cave = &params->np_cave;
  57. np_humidity = &params->np_humidity;
  58. np_trees = &params->np_trees;
  59. np_apple_trees = &params->np_apple_trees;
  60. //// Create noise objects
  61. noise_terrain_base = new Noise(&params->np_terrain_base, seed, csize.X, csize.Y);
  62. noise_terrain_higher = new Noise(&params->np_terrain_higher, seed, csize.X, csize.Y);
  63. noise_steepness = new Noise(&params->np_steepness, seed, csize.X, csize.Y);
  64. noise_height_select = new Noise(&params->np_height_select, seed, csize.X, csize.Y);
  65. noise_mud = new Noise(&params->np_mud, seed, csize.X, csize.Y);
  66. noise_beach = new Noise(&params->np_beach, seed, csize.X, csize.Y);
  67. noise_biome = new Noise(&params->np_biome, seed,
  68. csize.X + 2 * MAP_BLOCKSIZE, csize.Y + 2 * MAP_BLOCKSIZE);
  69. noise_humidity = new Noise(&params->np_humidity, seed,
  70. csize.X + 2 * MAP_BLOCKSIZE, csize.Y + 2 * MAP_BLOCKSIZE);
  71. //// Resolve nodes to be used
  72. INodeDefManager *ndef = emerge->ndef;
  73. c_stone = ndef->getId("mapgen_stone");
  74. c_dirt = ndef->getId("mapgen_dirt");
  75. c_dirt_with_grass = ndef->getId("mapgen_dirt_with_grass");
  76. c_sand = ndef->getId("mapgen_sand");
  77. c_water_source = ndef->getId("mapgen_water_source");
  78. c_lava_source = ndef->getId("mapgen_lava_source");
  79. c_gravel = ndef->getId("mapgen_gravel");
  80. c_desert_stone = ndef->getId("mapgen_desert_stone");
  81. c_desert_sand = ndef->getId("mapgen_desert_sand");
  82. c_dirt_with_snow = ndef->getId("mapgen_dirt_with_snow");
  83. c_snow = ndef->getId("mapgen_snow");
  84. c_snowblock = ndef->getId("mapgen_snowblock");
  85. c_ice = ndef->getId("mapgen_ice");
  86. if (c_gravel == CONTENT_IGNORE)
  87. c_gravel = c_stone;
  88. if (c_desert_stone == CONTENT_IGNORE)
  89. c_desert_stone = c_stone;
  90. if (c_desert_sand == CONTENT_IGNORE)
  91. c_desert_sand = c_sand;
  92. if (c_dirt_with_snow == CONTENT_IGNORE)
  93. c_dirt_with_snow = c_dirt_with_grass;
  94. if (c_snow == CONTENT_IGNORE)
  95. c_snow = CONTENT_AIR;
  96. if (c_snowblock == CONTENT_IGNORE)
  97. c_snowblock = c_dirt_with_grass;
  98. if (c_ice == CONTENT_IGNORE)
  99. c_ice = c_water_source;
  100. c_cobble = ndef->getId("mapgen_cobble");
  101. c_mossycobble = ndef->getId("mapgen_mossycobble");
  102. c_stair_cobble = ndef->getId("mapgen_stair_cobble");
  103. c_stair_desert_stone = ndef->getId("mapgen_stair_desert_stone");
  104. if (c_mossycobble == CONTENT_IGNORE)
  105. c_mossycobble = c_cobble;
  106. if (c_stair_cobble == CONTENT_IGNORE)
  107. c_stair_cobble = c_cobble;
  108. if (c_stair_desert_stone == CONTENT_IGNORE)
  109. c_stair_desert_stone = c_desert_stone;
  110. }
  111. MapgenV6::~MapgenV6()
  112. {
  113. delete noise_terrain_base;
  114. delete noise_terrain_higher;
  115. delete noise_steepness;
  116. delete noise_height_select;
  117. delete noise_mud;
  118. delete noise_beach;
  119. delete noise_biome;
  120. delete noise_humidity;
  121. delete[] heightmap;
  122. }
  123. MapgenV6Params::MapgenV6Params():
  124. np_terrain_base (-4, 20.0, v3f(250.0, 250.0, 250.0), 82341, 5, 0.6, 2.0),
  125. np_terrain_higher (20, 16.0, v3f(500.0, 500.0, 500.0), 85039, 5, 0.6, 2.0),
  126. np_steepness (0.85, 0.5, v3f(125.0, 125.0, 125.0), -932, 5, 0.7, 2.0),
  127. np_height_select (0, 1.0, v3f(250.0, 250.0, 250.0), 4213, 5, 0.69, 2.0),
  128. np_mud (4, 2.0, v3f(200.0, 200.0, 200.0), 91013, 3, 0.55, 2.0),
  129. np_beach (0, 1.0, v3f(250.0, 250.0, 250.0), 59420, 3, 0.50, 2.0),
  130. np_biome (0, 1.0, v3f(500.0, 500.0, 500.0), 9130, 3, 0.50, 2.0),
  131. np_cave (6, 6.0, v3f(250.0, 250.0, 250.0), 34329, 3, 0.50, 2.0),
  132. np_humidity (0.5, 0.5, v3f(500.0, 500.0, 500.0), 72384, 3, 0.50, 2.0),
  133. np_trees (0, 1.0, v3f(125.0, 125.0, 125.0), 2, 4, 0.66, 2.0),
  134. np_apple_trees (0, 1.0, v3f(100.0, 100.0, 100.0), 342902, 3, 0.45, 2.0)
  135. {
  136. }
  137. void MapgenV6Params::readParams(const Settings *settings)
  138. {
  139. settings->getFlagStrNoEx("mgv6_spflags", spflags, flagdesc_mapgen_v6);
  140. settings->getFloatNoEx("mgv6_freq_desert", freq_desert);
  141. settings->getFloatNoEx("mgv6_freq_beach", freq_beach);
  142. settings->getNoiseParams("mgv6_np_terrain_base", np_terrain_base);
  143. settings->getNoiseParams("mgv6_np_terrain_higher", np_terrain_higher);
  144. settings->getNoiseParams("mgv6_np_steepness", np_steepness);
  145. settings->getNoiseParams("mgv6_np_height_select", np_height_select);
  146. settings->getNoiseParams("mgv6_np_mud", np_mud);
  147. settings->getNoiseParams("mgv6_np_beach", np_beach);
  148. settings->getNoiseParams("mgv6_np_biome", np_biome);
  149. settings->getNoiseParams("mgv6_np_cave", np_cave);
  150. settings->getNoiseParams("mgv6_np_humidity", np_humidity);
  151. settings->getNoiseParams("mgv6_np_trees", np_trees);
  152. settings->getNoiseParams("mgv6_np_apple_trees", np_apple_trees);
  153. }
  154. void MapgenV6Params::writeParams(Settings *settings) const
  155. {
  156. settings->setFlagStr("mgv6_spflags", spflags, flagdesc_mapgen_v6, U32_MAX);
  157. settings->setFloat("mgv6_freq_desert", freq_desert);
  158. settings->setFloat("mgv6_freq_beach", freq_beach);
  159. settings->setNoiseParams("mgv6_np_terrain_base", np_terrain_base);
  160. settings->setNoiseParams("mgv6_np_terrain_higher", np_terrain_higher);
  161. settings->setNoiseParams("mgv6_np_steepness", np_steepness);
  162. settings->setNoiseParams("mgv6_np_height_select", np_height_select);
  163. settings->setNoiseParams("mgv6_np_mud", np_mud);
  164. settings->setNoiseParams("mgv6_np_beach", np_beach);
  165. settings->setNoiseParams("mgv6_np_biome", np_biome);
  166. settings->setNoiseParams("mgv6_np_cave", np_cave);
  167. settings->setNoiseParams("mgv6_np_humidity", np_humidity);
  168. settings->setNoiseParams("mgv6_np_trees", np_trees);
  169. settings->setNoiseParams("mgv6_np_apple_trees", np_apple_trees);
  170. }
  171. //////////////////////// Some helper functions for the map generator
  172. // Returns Y one under area minimum if not found
  173. s16 MapgenV6::find_stone_level(v2s16 p2d)
  174. {
  175. const v3s16 &em = vm->m_area.getExtent();
  176. s16 y_nodes_max = vm->m_area.MaxEdge.Y;
  177. s16 y_nodes_min = vm->m_area.MinEdge.Y;
  178. u32 i = vm->m_area.index(p2d.X, y_nodes_max, p2d.Y);
  179. s16 y;
  180. for (y = y_nodes_max; y >= y_nodes_min; y--) {
  181. content_t c = vm->m_data[i].getContent();
  182. if (c != CONTENT_IGNORE && (c == c_stone || c == c_desert_stone))
  183. break;
  184. vm->m_area.add_y(em, i, -1);
  185. }
  186. return (y >= y_nodes_min) ? y : y_nodes_min - 1;
  187. }
  188. // Required by mapgen.h
  189. bool MapgenV6::block_is_underground(u64 seed, v3s16 blockpos)
  190. {
  191. /*s16 minimum_groundlevel = (s16)get_sector_minimum_ground_level(
  192. seed, v2s16(blockpos.X, blockpos.Z));*/
  193. // Nah, this is just a heuristic, just return something
  194. s16 minimum_groundlevel = water_level;
  195. if(blockpos.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE <= minimum_groundlevel)
  196. return true;
  197. return false;
  198. }
  199. //////////////////////// Base terrain height functions
  200. float MapgenV6::baseTerrainLevel(float terrain_base, float terrain_higher,
  201. float steepness, float height_select)
  202. {
  203. float base = 1 + terrain_base;
  204. float higher = 1 + terrain_higher;
  205. // Limit higher ground level to at least base
  206. if(higher < base)
  207. higher = base;
  208. // Steepness factor of cliffs
  209. float b = steepness;
  210. b = rangelim(b, 0.0, 1000.0);
  211. b = 5 * b * b * b * b * b * b * b;
  212. b = rangelim(b, 0.5, 1000.0);
  213. // Values 1.5...100 give quite horrible looking slopes
  214. if (b > 1.5 && b < 100.0)
  215. b = (b < 10.0) ? 1.5 : 100.0;
  216. float a_off = -0.20; // Offset to more low
  217. float a = 0.5 + b * (a_off + height_select);
  218. a = rangelim(a, 0.0, 1.0); // Limit
  219. return base * (1.0 - a) + higher * a;
  220. }
  221. float MapgenV6::baseTerrainLevelFromNoise(v2s16 p)
  222. {
  223. if (spflags & MGV6_FLAT)
  224. return water_level;
  225. float terrain_base = NoisePerlin2D_PO(&noise_terrain_base->np,
  226. p.X, 0.5, p.Y, 0.5, seed);
  227. float terrain_higher = NoisePerlin2D_PO(&noise_terrain_higher->np,
  228. p.X, 0.5, p.Y, 0.5, seed);
  229. float steepness = NoisePerlin2D_PO(&noise_steepness->np,
  230. p.X, 0.5, p.Y, 0.5, seed);
  231. float height_select = NoisePerlin2D_PO(&noise_height_select->np,
  232. p.X, 0.5, p.Y, 0.5, seed);
  233. return baseTerrainLevel(terrain_base, terrain_higher,
  234. steepness, height_select);
  235. }
  236. float MapgenV6::baseTerrainLevelFromMap(v2s16 p)
  237. {
  238. int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
  239. return baseTerrainLevelFromMap(index);
  240. }
  241. float MapgenV6::baseTerrainLevelFromMap(int index)
  242. {
  243. if (spflags & MGV6_FLAT)
  244. return water_level;
  245. float terrain_base = noise_terrain_base->result[index];
  246. float terrain_higher = noise_terrain_higher->result[index];
  247. float steepness = noise_steepness->result[index];
  248. float height_select = noise_height_select->result[index];
  249. return baseTerrainLevel(terrain_base, terrain_higher,
  250. steepness, height_select);
  251. }
  252. s16 MapgenV6::find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision)
  253. {
  254. return baseTerrainLevelFromNoise(p2d) + MGV6_AVERAGE_MUD_AMOUNT;
  255. }
  256. int MapgenV6::getGroundLevelAtPoint(v2s16 p)
  257. {
  258. return baseTerrainLevelFromNoise(p) + MGV6_AVERAGE_MUD_AMOUNT;
  259. }
  260. int MapgenV6::getSpawnLevelAtPoint(v2s16 p)
  261. {
  262. s16 level_at_point = baseTerrainLevelFromNoise(p) + MGV6_AVERAGE_MUD_AMOUNT;
  263. if (level_at_point <= water_level ||
  264. level_at_point > water_level + 16)
  265. return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
  266. return level_at_point;
  267. }
  268. //////////////////////// Noise functions
  269. float MapgenV6::getMudAmount(v2s16 p)
  270. {
  271. int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
  272. return getMudAmount(index);
  273. }
  274. bool MapgenV6::getHaveBeach(v2s16 p)
  275. {
  276. int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
  277. return getHaveBeach(index);
  278. }
  279. BiomeV6Type MapgenV6::getBiome(v2s16 p)
  280. {
  281. int index = (p.Y - full_node_min.Z) * (ystride + 2 * MAP_BLOCKSIZE)
  282. + (p.X - full_node_min.X);
  283. return getBiome(index, p);
  284. }
  285. float MapgenV6::getHumidity(v2s16 p)
  286. {
  287. /*double noise = noise2d_perlin(
  288. 0.5+(float)p.X/500, 0.5+(float)p.Y/500,
  289. seed+72384, 4, 0.66);
  290. noise = (noise + 1.0)/2.0;*/
  291. int index = (p.Y - full_node_min.Z) * (ystride + 2 * MAP_BLOCKSIZE)
  292. + (p.X - full_node_min.X);
  293. float noise = noise_humidity->result[index];
  294. if (noise < 0.0)
  295. noise = 0.0;
  296. if (noise > 1.0)
  297. noise = 1.0;
  298. return noise;
  299. }
  300. float MapgenV6::getTreeAmount(v2s16 p)
  301. {
  302. /*double noise = noise2d_perlin(
  303. 0.5+(float)p.X/125, 0.5+(float)p.Y/125,
  304. seed+2, 4, 0.66);*/
  305. float noise = NoisePerlin2D(np_trees, p.X, p.Y, seed);
  306. float zeroval = -0.39;
  307. if (noise < zeroval)
  308. return 0;
  309. return 0.04 * (noise - zeroval) / (1.0 - zeroval);
  310. }
  311. bool MapgenV6::getHaveAppleTree(v2s16 p)
  312. {
  313. /*is_apple_tree = noise2d_perlin(
  314. 0.5+(float)p.X/100, 0.5+(float)p.Z/100,
  315. data->seed+342902, 3, 0.45) > 0.2;*/
  316. float noise = NoisePerlin2D(np_apple_trees, p.X, p.Y, seed);
  317. return noise > 0.2;
  318. }
  319. float MapgenV6::getMudAmount(int index)
  320. {
  321. if (spflags & MGV6_FLAT)
  322. return MGV6_AVERAGE_MUD_AMOUNT;
  323. /*return ((float)AVERAGE_MUD_AMOUNT + 2.0 * noise2d_perlin(
  324. 0.5+(float)p.X/200, 0.5+(float)p.Y/200,
  325. seed+91013, 3, 0.55));*/
  326. return noise_mud->result[index];
  327. }
  328. bool MapgenV6::getHaveBeach(int index)
  329. {
  330. // Determine whether to have sand here
  331. /*double sandnoise = noise2d_perlin(
  332. 0.2+(float)p2d.X/250, 0.7+(float)p2d.Y/250,
  333. seed+59420, 3, 0.50);*/
  334. float sandnoise = noise_beach->result[index];
  335. return (sandnoise > freq_beach);
  336. }
  337. BiomeV6Type MapgenV6::getBiome(int index, v2s16 p)
  338. {
  339. // Just do something very simple as for now
  340. /*double d = noise2d_perlin(
  341. 0.6+(float)p2d.X/250, 0.2+(float)p2d.Y/250,
  342. seed+9130, 3, 0.50);*/
  343. float d = noise_biome->result[index];
  344. float h = noise_humidity->result[index];
  345. if (spflags & MGV6_SNOWBIOMES) {
  346. float blend = (spflags & MGV6_BIOMEBLEND) ? noise2d(p.X, p.Y, seed) / 40 : 0;
  347. if (d > MGV6_FREQ_HOT + blend) {
  348. if (h > MGV6_FREQ_JUNGLE + blend)
  349. return BT_JUNGLE;
  350. return BT_DESERT;
  351. }
  352. if (d < MGV6_FREQ_SNOW + blend) {
  353. if (h > MGV6_FREQ_TAIGA + blend)
  354. return BT_TAIGA;
  355. return BT_TUNDRA;
  356. }
  357. return BT_NORMAL;
  358. }
  359. if (d > freq_desert)
  360. return BT_DESERT;
  361. if ((spflags & MGV6_BIOMEBLEND) && (d > freq_desert - 0.10) &&
  362. ((noise2d(p.X, p.Y, seed) + 1.0) > (freq_desert - d) * 20.0))
  363. return BT_DESERT;
  364. if ((spflags & MGV6_JUNGLES) && h > 0.75)
  365. return BT_JUNGLE;
  366. return BT_NORMAL;
  367. }
  368. u32 MapgenV6::get_blockseed(u64 seed, v3s16 p)
  369. {
  370. s32 x = p.X, y = p.Y, z = p.Z;
  371. return (u32)(seed % 0x100000000ULL) + z * 38134234 + y * 42123 + x * 23;
  372. }
  373. //////////////////////// Map generator
  374. void MapgenV6::makeChunk(BlockMakeData *data)
  375. {
  376. // Pre-conditions
  377. assert(data->vmanip);
  378. assert(data->nodedef);
  379. assert(data->blockpos_requested.X >= data->blockpos_min.X &&
  380. data->blockpos_requested.Y >= data->blockpos_min.Y &&
  381. data->blockpos_requested.Z >= data->blockpos_min.Z);
  382. assert(data->blockpos_requested.X <= data->blockpos_max.X &&
  383. data->blockpos_requested.Y <= data->blockpos_max.Y &&
  384. data->blockpos_requested.Z <= data->blockpos_max.Z);
  385. this->generating = true;
  386. this->vm = data->vmanip;
  387. this->ndef = data->nodedef;
  388. // Hack: use minimum block coords for old code that assumes a single block
  389. v3s16 blockpos_min = data->blockpos_min;
  390. v3s16 blockpos_max = data->blockpos_max;
  391. // Area of central chunk
  392. node_min = blockpos_min * MAP_BLOCKSIZE;
  393. node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
  394. // Full allocated area
  395. full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
  396. full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
  397. central_area_size = node_max - node_min + v3s16(1, 1, 1);
  398. assert(central_area_size.X == central_area_size.Z);
  399. // Create a block-specific seed
  400. blockseed = get_blockseed(data->seed, full_node_min);
  401. // Make some noise
  402. calculateNoise();
  403. // Maximum height of the stone surface and obstacles.
  404. // This is used to guide the cave generation
  405. s16 stone_surface_max_y;
  406. // Generate general ground level to full area
  407. stone_surface_max_y = generateGround();
  408. // Create initial heightmap to limit caves
  409. updateHeightmap(node_min, node_max);
  410. const s16 max_spread_amount = MAP_BLOCKSIZE;
  411. // Limit dirt flow area by 1 because mud is flown into neighbors.
  412. s16 mudflow_minpos = -max_spread_amount + 1;
  413. s16 mudflow_maxpos = central_area_size.X + max_spread_amount - 2;
  414. // Loop this part, it will make stuff look older and newer nicely
  415. const u32 age_loops = 2;
  416. for (u32 i_age = 0; i_age < age_loops; i_age++) { // Aging loop
  417. // Make caves (this code is relatively horrible)
  418. if (flags & MG_CAVES)
  419. generateCaves(stone_surface_max_y);
  420. // Add mud to the central chunk
  421. addMud();
  422. // Flow mud away from steep edges
  423. if (spflags & MGV6_MUDFLOW)
  424. flowMud(mudflow_minpos, mudflow_maxpos);
  425. }
  426. // Update heightmap after mudflow
  427. updateHeightmap(node_min, node_max);
  428. // Add dungeons
  429. if ((flags & MG_DUNGEONS) && (stone_surface_max_y >= node_min.Y)) {
  430. DungeonParams dp;
  431. dp.seed = seed;
  432. dp.c_water = c_water_source;
  433. dp.c_river_water = c_water_source;
  434. dp.only_in_ground = true;
  435. dp.corridor_len_min = 1;
  436. dp.corridor_len_max = 13;
  437. dp.rooms_min = 2;
  438. dp.rooms_max = 16;
  439. dp.y_min = -MAX_MAP_GENERATION_LIMIT;
  440. dp.y_max = MAX_MAP_GENERATION_LIMIT;
  441. dp.np_density
  442. = NoiseParams(0.9, 0.5, v3f(500.0, 500.0, 500.0), 0, 2, 0.8, 2.0);
  443. dp.np_alt_wall
  444. = NoiseParams(-0.4, 1.0, v3f(40.0, 40.0, 40.0), 32474, 6, 1.1, 2.0);
  445. if (getBiome(0, v2s16(node_min.X, node_min.Z)) == BT_DESERT) {
  446. dp.c_wall = c_desert_stone;
  447. dp.c_alt_wall = CONTENT_IGNORE;
  448. dp.c_stair = c_stair_desert_stone;
  449. dp.diagonal_dirs = true;
  450. dp.holesize = v3s16(2, 3, 2);
  451. dp.room_size_min = v3s16(6, 9, 6);
  452. dp.room_size_max = v3s16(10, 11, 10);
  453. dp.room_size_large_min = v3s16(10, 13, 10);
  454. dp.room_size_large_max = v3s16(18, 21, 18);
  455. dp.notifytype = GENNOTIFY_TEMPLE;
  456. } else {
  457. dp.c_wall = c_cobble;
  458. dp.c_alt_wall = c_mossycobble;
  459. dp.c_stair = c_stair_cobble;
  460. dp.diagonal_dirs = false;
  461. dp.holesize = v3s16(1, 2, 1);
  462. dp.room_size_min = v3s16(4, 4, 4);
  463. dp.room_size_max = v3s16(8, 6, 8);
  464. dp.room_size_large_min = v3s16(8, 8, 8);
  465. dp.room_size_large_max = v3s16(16, 16, 16);
  466. dp.notifytype = GENNOTIFY_DUNGEON;
  467. }
  468. DungeonGen dgen(ndef, &gennotify, &dp);
  469. dgen.generate(vm, blockseed, full_node_min, full_node_max);
  470. }
  471. // Add top and bottom side of water to transforming_liquid queue
  472. updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
  473. // Add surface nodes
  474. growGrass();
  475. // Generate some trees, and add grass, if a jungle
  476. if (spflags & MGV6_TREES)
  477. placeTreesAndJungleGrass();
  478. // Generate the registered decorations
  479. if (flags & MG_DECORATIONS)
  480. m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
  481. // Generate the registered ores
  482. m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
  483. // Calculate lighting
  484. if (flags & MG_LIGHT)
  485. calcLighting(node_min - v3s16(1, 1, 1) * MAP_BLOCKSIZE,
  486. node_max + v3s16(1, 0, 1) * MAP_BLOCKSIZE,
  487. full_node_min, full_node_max);
  488. this->generating = false;
  489. }
  490. void MapgenV6::calculateNoise()
  491. {
  492. int x = node_min.X;
  493. int z = node_min.Z;
  494. int fx = full_node_min.X;
  495. int fz = full_node_min.Z;
  496. if (!(spflags & MGV6_FLAT)) {
  497. noise_terrain_base->perlinMap2D_PO(x, 0.5, z, 0.5);
  498. noise_terrain_higher->perlinMap2D_PO(x, 0.5, z, 0.5);
  499. noise_steepness->perlinMap2D_PO(x, 0.5, z, 0.5);
  500. noise_height_select->perlinMap2D_PO(x, 0.5, z, 0.5);
  501. noise_mud->perlinMap2D_PO(x, 0.5, z, 0.5);
  502. }
  503. noise_beach->perlinMap2D_PO(x, 0.2, z, 0.7);
  504. noise_biome->perlinMap2D_PO(fx, 0.6, fz, 0.2);
  505. noise_humidity->perlinMap2D_PO(fx, 0.0, fz, 0.0);
  506. // Humidity map does not need range limiting 0 to 1,
  507. // only humidity at point does
  508. }
  509. int MapgenV6::generateGround()
  510. {
  511. //TimeTaker timer1("Generating ground level");
  512. MapNode n_air(CONTENT_AIR), n_water_source(c_water_source);
  513. MapNode n_stone(c_stone), n_desert_stone(c_desert_stone);
  514. MapNode n_ice(c_ice);
  515. int stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
  516. u32 index = 0;
  517. for (s16 z = node_min.Z; z <= node_max.Z; z++)
  518. for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
  519. // Surface height
  520. s16 surface_y = (s16)baseTerrainLevelFromMap(index);
  521. // Log it
  522. if (surface_y > stone_surface_max_y)
  523. stone_surface_max_y = surface_y;
  524. BiomeV6Type bt = getBiome(v2s16(x, z));
  525. // Fill ground with stone
  526. const v3s16 &em = vm->m_area.getExtent();
  527. u32 i = vm->m_area.index(x, node_min.Y, z);
  528. for (s16 y = node_min.Y; y <= node_max.Y; y++) {
  529. if (vm->m_data[i].getContent() == CONTENT_IGNORE) {
  530. if (y <= surface_y) {
  531. vm->m_data[i] = (y >= MGV6_DESERT_STONE_BASE
  532. && bt == BT_DESERT) ?
  533. n_desert_stone : n_stone;
  534. } else if (y <= water_level) {
  535. vm->m_data[i] = (y >= MGV6_ICE_BASE
  536. && bt == BT_TUNDRA) ?
  537. n_ice : n_water_source;
  538. } else {
  539. vm->m_data[i] = n_air;
  540. }
  541. }
  542. vm->m_area.add_y(em, i, 1);
  543. }
  544. }
  545. return stone_surface_max_y;
  546. }
  547. void MapgenV6::addMud()
  548. {
  549. // 15ms @cs=8
  550. //TimeTaker timer1("add mud");
  551. MapNode n_dirt(c_dirt), n_gravel(c_gravel);
  552. MapNode n_sand(c_sand), n_desert_sand(c_desert_sand);
  553. MapNode addnode;
  554. u32 index = 0;
  555. for (s16 z = node_min.Z; z <= node_max.Z; z++)
  556. for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
  557. // Randomize mud amount
  558. s16 mud_add_amount = getMudAmount(index) / 2.0 + 0.5;
  559. // Find ground level
  560. s16 surface_y = find_stone_level(v2s16(x, z)); /////////////////optimize this!
  561. // Handle area not found
  562. if (surface_y == vm->m_area.MinEdge.Y - 1)
  563. continue;
  564. BiomeV6Type bt = getBiome(v2s16(x, z));
  565. addnode = (bt == BT_DESERT) ? n_desert_sand : n_dirt;
  566. if (bt == BT_DESERT && surface_y + mud_add_amount <= water_level + 1) {
  567. addnode = n_sand;
  568. } else if (mud_add_amount <= 0) {
  569. mud_add_amount = 1 - mud_add_amount;
  570. addnode = n_gravel;
  571. } else if (bt != BT_DESERT && getHaveBeach(index) &&
  572. surface_y + mud_add_amount <= water_level + 2) {
  573. addnode = n_sand;
  574. }
  575. if ((bt == BT_DESERT || bt == BT_TUNDRA) && surface_y > 20)
  576. mud_add_amount = MYMAX(0, mud_add_amount - (surface_y - 20) / 5);
  577. /* If topmost node is grass, change it to mud. It might be if it was
  578. // flown to there from a neighboring chunk and then converted.
  579. u32 i = vm->m_area.index(x, surface_y, z);
  580. if (vm->m_data[i].getContent() == c_dirt_with_grass)
  581. vm->m_data[i] = n_dirt;*/
  582. // Add mud on ground
  583. s16 mudcount = 0;
  584. const v3s16 &em = vm->m_area.getExtent();
  585. s16 y_start = surface_y + 1;
  586. u32 i = vm->m_area.index(x, y_start, z);
  587. for (s16 y = y_start; y <= node_max.Y; y++) {
  588. if (mudcount >= mud_add_amount)
  589. break;
  590. vm->m_data[i] = addnode;
  591. mudcount++;
  592. vm->m_area.add_y(em, i, 1);
  593. }
  594. }
  595. }
  596. void MapgenV6::flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos)
  597. {
  598. // 340ms @cs=8
  599. //TimeTaker timer1("flow mud");
  600. // Iterate a few times
  601. for (s16 k = 0; k < 3; k++) {
  602. for (s16 z = mudflow_minpos; z <= mudflow_maxpos; z++)
  603. for (s16 x = mudflow_minpos; x <= mudflow_maxpos; x++) {
  604. // Invert coordinates every 2nd iteration
  605. if (k % 2 == 0) {
  606. x = mudflow_maxpos - (x - mudflow_minpos);
  607. z = mudflow_maxpos - (z - mudflow_minpos);
  608. }
  609. // Node position in 2d
  610. v2s16 p2d = v2s16(node_min.X, node_min.Z) + v2s16(x, z);
  611. const v3s16 &em = vm->m_area.getExtent();
  612. u32 i = vm->m_area.index(p2d.X, node_max.Y, p2d.Y);
  613. s16 y = node_max.Y;
  614. while (y >= node_min.Y) {
  615. for (;; y--) {
  616. MapNode *n = NULL;
  617. // Find mud
  618. for (; y >= node_min.Y; y--) {
  619. n = &vm->m_data[i];
  620. if (n->getContent() == c_dirt ||
  621. n->getContent() == c_dirt_with_grass ||
  622. n->getContent() == c_gravel)
  623. break;
  624. vm->m_area.add_y(em, i, -1);
  625. }
  626. // Stop if out of area
  627. //if(vmanip.m_area.contains(i) == false)
  628. if (y < node_min.Y)
  629. break;
  630. if (n->getContent() == c_dirt ||
  631. n->getContent() == c_dirt_with_grass) {
  632. // Make it exactly mud
  633. n->setContent(c_dirt);
  634. // Don't flow it if the stuff under it is not mud
  635. {
  636. u32 i2 = i;
  637. vm->m_area.add_y(em, i2, -1);
  638. // Cancel if out of area
  639. if (!vm->m_area.contains(i2))
  640. continue;
  641. MapNode *n2 = &vm->m_data[i2];
  642. if (n2->getContent() != c_dirt &&
  643. n2->getContent() != c_dirt_with_grass)
  644. continue;
  645. }
  646. }
  647. v3s16 dirs4[4] = {
  648. v3s16(0, 0, 1), // back
  649. v3s16(1, 0, 0), // right
  650. v3s16(0, 0, -1), // front
  651. v3s16(-1, 0, 0), // left
  652. };
  653. // Check that upper is walkable. Cancel
  654. // dropping if upper keeps it in place.
  655. u32 i3 = i;
  656. vm->m_area.add_y(em, i3, 1);
  657. MapNode *n3 = NULL;
  658. if (vm->m_area.contains(i3)) {
  659. n3 = &vm->m_data[i3];
  660. if (ndef->get(*n3).walkable)
  661. continue;
  662. }
  663. // Drop mud on side
  664. for (const v3s16 &dirp : dirs4) {
  665. u32 i2 = i;
  666. // Move to side
  667. vm->m_area.add_p(em, i2, dirp);
  668. // Fail if out of area
  669. if (!vm->m_area.contains(i2))
  670. continue;
  671. // Check that side is air
  672. MapNode *n2 = &vm->m_data[i2];
  673. if (ndef->get(*n2).walkable)
  674. continue;
  675. // Check that under side is air
  676. vm->m_area.add_y(em, i2, -1);
  677. if (!vm->m_area.contains(i2))
  678. continue;
  679. n2 = &vm->m_data[i2];
  680. if (ndef->get(*n2).walkable)
  681. continue;
  682. // Loop further down until not air
  683. bool dropped_to_unknown = false;
  684. do {
  685. vm->m_area.add_y(em, i2, -1);
  686. n2 = &vm->m_data[i2];
  687. // if out of known area
  688. if (!vm->m_area.contains(i2) ||
  689. n2->getContent() == CONTENT_IGNORE) {
  690. dropped_to_unknown = true;
  691. break;
  692. }
  693. } while (!ndef->get(*n2).walkable);
  694. // Loop one up so that we're in air
  695. vm->m_area.add_y(em, i2, 1);
  696. // Move mud to new place. Outside mapchunk remove
  697. // any decorations above removed or placed mud.
  698. if (!dropped_to_unknown)
  699. moveMud(i, i2, i3, p2d, em);
  700. // Done
  701. break;
  702. }
  703. }
  704. }
  705. }
  706. }
  707. }
  708. void MapgenV6::moveMud(u32 remove_index, u32 place_index,
  709. u32 above_remove_index, v2s16 pos, v3s16 em)
  710. {
  711. MapNode n_air(CONTENT_AIR);
  712. // Copy mud from old place to new place
  713. vm->m_data[place_index] = vm->m_data[remove_index];
  714. // Set old place to be air
  715. vm->m_data[remove_index] = n_air;
  716. // Outside the mapchunk decorations may need to be removed if above removed
  717. // mud or if half-buried in placed mud. Placed mud is to the side of pos so
  718. // use 'pos.X >= node_max.X' etc.
  719. if (pos.X >= node_max.X || pos.X <= node_min.X ||
  720. pos.Y >= node_max.Z || pos.Y <= node_min.Z) {
  721. // 'above remove' node is above removed mud. If it is not air, water or
  722. // 'ignore' it is a decoration that needs removing. Also search upwards
  723. // to remove a possible stacked decoration.
  724. // Check for 'ignore' because stacked decorations can penetrate into
  725. // 'ignore' nodes above the mapchunk.
  726. while (vm->m_area.contains(above_remove_index) &&
  727. vm->m_data[above_remove_index].getContent() != CONTENT_AIR &&
  728. vm->m_data[above_remove_index].getContent() != c_water_source &&
  729. vm->m_data[above_remove_index].getContent() != CONTENT_IGNORE) {
  730. vm->m_data[above_remove_index] = n_air;
  731. vm->m_area.add_y(em, above_remove_index, 1);
  732. }
  733. // Mud placed may have partially-buried a stacked decoration, search
  734. // above and remove.
  735. vm->m_area.add_y(em, place_index, 1);
  736. while (vm->m_area.contains(place_index) &&
  737. vm->m_data[place_index].getContent() != CONTENT_AIR &&
  738. vm->m_data[place_index].getContent() != c_water_source &&
  739. vm->m_data[place_index].getContent() != CONTENT_IGNORE) {
  740. vm->m_data[place_index] = n_air;
  741. vm->m_area.add_y(em, place_index, 1);
  742. }
  743. }
  744. }
  745. void MapgenV6::placeTreesAndJungleGrass()
  746. {
  747. //TimeTaker t("placeTrees");
  748. if (node_max.Y < water_level)
  749. return;
  750. PseudoRandom grassrandom(blockseed + 53);
  751. content_t c_junglegrass = ndef->getId("mapgen_junglegrass");
  752. // if we don't have junglegrass, don't place cignore... that's bad
  753. if (c_junglegrass == CONTENT_IGNORE)
  754. c_junglegrass = CONTENT_AIR;
  755. MapNode n_junglegrass(c_junglegrass);
  756. const v3s16 &em = vm->m_area.getExtent();
  757. // Divide area into parts
  758. s16 div = 8;
  759. s16 sidelen = central_area_size.X / div;
  760. double area = sidelen * sidelen;
  761. // N.B. We must add jungle grass first, since tree leaves will
  762. // obstruct the ground, giving us a false ground level
  763. for (s16 z0 = 0; z0 < div; z0++)
  764. for (s16 x0 = 0; x0 < div; x0++) {
  765. // Center position of part of division
  766. v2s16 p2d_center(
  767. node_min.X + sidelen / 2 + sidelen * x0,
  768. node_min.Z + sidelen / 2 + sidelen * z0
  769. );
  770. // Minimum edge of part of division
  771. v2s16 p2d_min(
  772. node_min.X + sidelen * x0,
  773. node_min.Z + sidelen * z0
  774. );
  775. // Maximum edge of part of division
  776. v2s16 p2d_max(
  777. node_min.X + sidelen + sidelen * x0 - 1,
  778. node_min.Z + sidelen + sidelen * z0 - 1
  779. );
  780. // Get biome at center position of part of division
  781. BiomeV6Type bt = getBiome(p2d_center);
  782. // Amount of trees
  783. u32 tree_count;
  784. if (bt == BT_JUNGLE || bt == BT_TAIGA || bt == BT_NORMAL) {
  785. tree_count = area * getTreeAmount(p2d_center);
  786. if (bt == BT_JUNGLE)
  787. tree_count *= 4;
  788. } else {
  789. tree_count = 0;
  790. }
  791. // Add jungle grass
  792. if (bt == BT_JUNGLE) {
  793. float humidity = getHumidity(p2d_center);
  794. u32 grass_count = 5 * humidity * tree_count;
  795. for (u32 i = 0; i < grass_count; i++) {
  796. s16 x = grassrandom.range(p2d_min.X, p2d_max.X);
  797. s16 z = grassrandom.range(p2d_min.Y, p2d_max.Y);
  798. int mapindex = central_area_size.X * (z - node_min.Z)
  799. + (x - node_min.X);
  800. s16 y = heightmap[mapindex];
  801. if (y < water_level)
  802. continue;
  803. u32 vi = vm->m_area.index(x, y, z);
  804. // place on dirt_with_grass, since we know it is exposed to sunlight
  805. if (vm->m_data[vi].getContent() == c_dirt_with_grass) {
  806. vm->m_area.add_y(em, vi, 1);
  807. vm->m_data[vi] = n_junglegrass;
  808. }
  809. }
  810. }
  811. // Put trees in random places on part of division
  812. for (u32 i = 0; i < tree_count; i++) {
  813. s16 x = myrand_range(p2d_min.X, p2d_max.X);
  814. s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
  815. int mapindex = central_area_size.X * (z - node_min.Z)
  816. + (x - node_min.X);
  817. s16 y = heightmap[mapindex];
  818. // Don't make a tree under water level
  819. // Don't make a tree so high that it doesn't fit
  820. if (y < water_level || y > node_max.Y - 6)
  821. continue;
  822. v3s16 p(x, y, z);
  823. // Trees grow only on mud and grass
  824. {
  825. u32 i = vm->m_area.index(p);
  826. content_t c = vm->m_data[i].getContent();
  827. if (c != c_dirt &&
  828. c != c_dirt_with_grass &&
  829. c != c_dirt_with_snow)
  830. continue;
  831. }
  832. p.Y++;
  833. // Make a tree
  834. if (bt == BT_JUNGLE) {
  835. treegen::make_jungletree(*vm, p, ndef, myrand());
  836. } else if (bt == BT_TAIGA) {
  837. treegen::make_pine_tree(*vm, p - v3s16(0, 1, 0), ndef, myrand());
  838. } else if (bt == BT_NORMAL) {
  839. bool is_apple_tree = (myrand_range(0, 3) == 0) &&
  840. getHaveAppleTree(v2s16(x, z));
  841. treegen::make_tree(*vm, p, is_apple_tree, ndef, myrand());
  842. }
  843. }
  844. }
  845. //printf("placeTreesAndJungleGrass: %dms\n", t.stop());
  846. }
  847. void MapgenV6::growGrass() // Add surface nodes
  848. {
  849. MapNode n_dirt_with_grass(c_dirt_with_grass);
  850. MapNode n_dirt_with_snow(c_dirt_with_snow);
  851. MapNode n_snowblock(c_snowblock);
  852. MapNode n_snow(c_snow);
  853. const v3s16 &em = vm->m_area.getExtent();
  854. u32 index = 0;
  855. for (s16 z = full_node_min.Z; z <= full_node_max.Z; z++)
  856. for (s16 x = full_node_min.X; x <= full_node_max.X; x++, index++) {
  857. // Find the lowest surface to which enough light ends up to make
  858. // grass grow. Basically just wait until not air and not leaves.
  859. s16 surface_y = 0;
  860. {
  861. u32 i = vm->m_area.index(x, node_max.Y, z);
  862. s16 y;
  863. // Go to ground level
  864. for (y = node_max.Y; y >= full_node_min.Y; y--) {
  865. MapNode &n = vm->m_data[i];
  866. if (ndef->get(n).param_type != CPT_LIGHT ||
  867. ndef->get(n).liquid_type != LIQUID_NONE ||
  868. n.getContent() == c_ice)
  869. break;
  870. vm->m_area.add_y(em, i, -1);
  871. }
  872. surface_y = (y >= full_node_min.Y) ? y : full_node_min.Y;
  873. }
  874. BiomeV6Type bt = getBiome(index, v2s16(x, z));
  875. u32 i = vm->m_area.index(x, surface_y, z);
  876. content_t c = vm->m_data[i].getContent();
  877. if (surface_y >= water_level - 20) {
  878. if (bt == BT_TAIGA && c == c_dirt) {
  879. vm->m_data[i] = n_dirt_with_snow;
  880. } else if (bt == BT_TUNDRA) {
  881. if (c == c_dirt) {
  882. vm->m_data[i] = n_snowblock;
  883. vm->m_area.add_y(em, i, -1);
  884. vm->m_data[i] = n_dirt_with_snow;
  885. } else if (c == c_stone && surface_y < node_max.Y) {
  886. vm->m_area.add_y(em, i, 1);
  887. vm->m_data[i] = n_snowblock;
  888. }
  889. } else if (c == c_dirt) {
  890. vm->m_data[i] = n_dirt_with_grass;
  891. }
  892. }
  893. }
  894. }
  895. void MapgenV6::generateCaves(int max_stone_y)
  896. {
  897. float cave_amount = NoisePerlin2D(np_cave, node_min.X, node_min.Y, seed);
  898. int volume_nodes = (node_max.X - node_min.X + 1) *
  899. (node_max.Y - node_min.Y + 1) * MAP_BLOCKSIZE;
  900. cave_amount = MYMAX(0.0, cave_amount);
  901. u32 caves_count = cave_amount * volume_nodes / 50000;
  902. u32 bruises_count = 1;
  903. PseudoRandom ps(blockseed + 21343);
  904. PseudoRandom ps2(blockseed + 1032);
  905. if (ps.range(1, 6) == 1)
  906. bruises_count = ps.range(0, ps.range(0, 2));
  907. if (getBiome(v2s16(node_min.X, node_min.Z)) == BT_DESERT) {
  908. caves_count /= 3;
  909. bruises_count /= 3;
  910. }
  911. for (u32 i = 0; i < caves_count + bruises_count; i++) {
  912. CavesV6 cave(ndef, &gennotify, water_level, c_water_source, c_lava_source);
  913. bool large_cave = (i >= caves_count);
  914. cave.makeCave(vm, node_min, node_max, &ps, &ps2,
  915. large_cave, max_stone_y, heightmap);
  916. }
  917. }