mapgen.lua 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. local set_node = minetest.set_node
  2. local get_node = minetest.get_node
  3. local swap_node = minetest.swap_node
  4. local ipairs = ipairs
  5. local math_random = math.random
  6. -- Content IDs used with the voxel manipulator.
  7. local c_air = minetest.get_content_id("air")
  8. local c_ignore = minetest.get_content_id("ignore")
  9. local c_stone = minetest.get_content_id("default:stone")
  10. -- These 2 types are placed at and below the level of the brimstone ocean.
  11. -- They disallow the C++ cavegen from running.
  12. local c_redrack = minetest.get_content_id("rackstone:redrack")
  13. local c_rackstone = minetest.get_content_id("rackstone:rackstone")
  14. -- These 2 types are placed above the brimstone ocean.
  15. -- The C++ cavegen is allowed to run in these nodes.
  16. local c_mgredrack = minetest.get_content_id("rackstone:mg_redrack")
  17. local c_mgrackstone = minetest.get_content_id("rackstone:mg_rackstone")
  18. local c_whitestone = minetest.get_content_id("whitestone:stone")
  19. local c_mossycobble = minetest.get_content_id("default:mossycobble")
  20. local c_cobble = minetest.get_content_id("default:cobble")
  21. local c_netherbrick = minetest.get_content_id("rackstone:brick")
  22. local c_cobblestair = minetest.get_content_id("stairs:stair_cobble")
  23. local c_netherstair = minetest.get_content_id("stairs:stair_rackstone_brick")
  24. local c_water = minetest.get_content_id("default:water_source")
  25. local c_waterflow = minetest.get_content_id("default:water_flowing")
  26. local c_lava = minetest.get_content_id("default:lava_source")
  27. local c_nlava = minetest.get_content_id("lbrim:lava_source")
  28. local c_lavaflow = minetest.get_content_id("default:lava_flowing")
  29. local c_obsidian = minetest.get_content_id("default:obsidian")
  30. local c_vine = minetest.get_content_id("nethervine:vine")
  31. local c_bedrock = minetest.get_content_id("bedrock:bedrock")
  32. -- Externally located tables for performance.
  33. local data = {}
  34. local noisemap1 = {}
  35. local noisemap2 = {}
  36. local noisemap3 = {}
  37. local noisemap4 = {}
  38. local noisemap5 = {}
  39. --[[
  40. Notes on mapgen function. IMPORTANT!
  41. The code loops through all positions in the mapchunk being generated *twice*, once to generate the nether,
  42. and the second time to repair problems at chunk borders caused by caves overgenerating in the C++ mapgen.
  43. These two loops work together, and must be understood together, since the loop which overgenerates can touch
  44. chunks previously generated by the first 'normal' loop.
  45. This 'hack' would not be necessary if we were using Singlenode for the C++ mapgen.
  46. However, we're using Valleys, so the Lua mapgen must work nicely with it.
  47. --]]
  48. nethermapgen.generate_realm = function(minp, maxp, seed)
  49. local nstart = nethermapgen.NETHER_START
  50. local bstart = nethermapgen.BRIMSTONE_OCEAN
  51. local rstart = nethermapgen.BEDROCK_DEPTH
  52. -- Don't run for out-of-bounds mapchunks.
  53. local mindepth = nstart + 100
  54. if minp.y > mindepth and maxp.y > mindepth then return end
  55. -- Grab the voxel manipulator.
  56. local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
  57. vm:get_data(data) -- Read current map data.
  58. local area = VoxelArea:new{MinEdge=emin, MaxEdge=emax}
  59. local area2 = VoxelArea:new{MinEdge=minp, MaxEdge=maxp}
  60. local pr = PseudoRandom(seed + 65)
  61. local x1 = maxp.x
  62. local y1 = maxp.y
  63. local z1 = maxp.z
  64. local x0 = minp.x
  65. local y0 = minp.y
  66. local z0 = minp.z
  67. -- Compute side lengths.
  68. local side_len_x = ((x1-x0)+1)
  69. local side_len_y = ((y1-y0)+1)
  70. local side_len_z = ((z1-z0)+1)
  71. local sides3D = {x=side_len_x, y=side_len_y, z=side_len_z}
  72. local sides2D = {x=side_len_x, y=side_len_z}
  73. local bp3d = {x=x0, y=y0, z=z0}
  74. local bp2d = {x=x0, y=z0}
  75. -- Get noisemaps.
  76. local perlin1 = minetest.get_perlin_map(nethermapgen.noise1param2d, sides2D)
  77. perlin1:get_2d_map_flat(bp2d, noisemap1)
  78. local perlin2 = minetest.get_perlin_map(nethermapgen.noise2param2d, sides2D)
  79. perlin2:get_2d_map_flat(bp2d, noisemap2)
  80. local perlin3 = minetest.get_perlin_map(nethermapgen.noise3param2d, sides2D)
  81. perlin3:get_2d_map_flat(bp2d, noisemap3)
  82. local perlin4 = minetest.get_perlin_map(nethermapgen.noise4param2d, sides2D)
  83. perlin4:get_2d_map_flat(bp2d, noisemap4)
  84. local perlin5 = minetest.get_perlin_map(nethermapgen.noise5param3d, sides3D)
  85. perlin5:get_3d_map_flat(bp3d, noisemap5)
  86. -- Localize commonly used functions.
  87. local floor = math.floor
  88. local ceil = math.ceil
  89. local abs = math.abs
  90. -- First mapgen pass.
  91. for z = z0, z1 do
  92. for x = x0, x1 do
  93. -- Get index into 2D noise arrays.
  94. local nx = (x-x0)
  95. local nz = (z-z0)
  96. local ni2 = (side_len_z*nz+nx)
  97. ni2 = ni2 + 1 -- Lua arrays start indexing at 1, not 0. Urrrrgh. >:(
  98. local n1 = noisemap1[ni2]
  99. local n2 = noisemap2[ni2]
  100. local n3 = noisemap3[ni2]
  101. local n4 = noisemap4[ni2]
  102. local rs_top = ceil(nstart+abs(n1)*64)
  103. local rs_bot = floor(nstart-abs(n1)*64)
  104. local lakecel = bstart + 30
  105. local cavernf = bstart + 40
  106. local lakelav = bstart
  107. local lakebed = bstart - 2
  108. local islandi = bstart - 2
  109. if n4 > -0.3 then
  110. if abs(n2) > 0.6 then
  111. islandi = bstart + 5
  112. islandi = islandi + pr:next(0, pr:next(1, 2))
  113. islandi = islandi + floor(n1*4)
  114. -- Create random holes.
  115. if pr:next(1, 100) == 1 then islandi = bstart - 2 end
  116. elseif abs(n2) > 0.5 then
  117. islandi = islandi + pr:next(3, 5)
  118. end
  119. end
  120. lakecel = lakecel - pr:next(0, 1+floor(abs(n3) * 3.5))
  121. lakecel = lakecel + floor(abs(n1) * 6)
  122. -- First pass through column.
  123. for y = y0, y1 do
  124. local vp = area:index(x, y, z)
  125. local vu = area:index(x, y+1, z)
  126. local vd = area:index(x, y-1, z)
  127. -- 3D noise.
  128. local np = area2:index(x, y, z)
  129. local n5 = noisemap5[np]
  130. if y <= rs_top then
  131. -- Get the type already generated at this position.
  132. local ip = data[vp]
  133. -- Using mapdata already generated by the C++ mapgen, convert regular nodes to nether types.
  134. if ip == c_cobble or ip == c_mossycobble then
  135. data[vp] = c_netherbrick
  136. elseif ip == c_cobblestair then
  137. data[vp] = c_netherstair
  138. elseif ip == c_water or ip == c_waterflow then
  139. data[vp] = c_lava
  140. elseif ip ~= c_air and ip ~= c_vine then -- Vines can cross chunk borders. Don't replace them.
  141. -- Generate rackstone layer between nether and underworld.
  142. if y >= rs_bot and y <= rs_top then
  143. data[vp] = c_mgrackstone
  144. elseif y < rs_bot then
  145. if abs(n5) < 0.015 then
  146. data[vp] = c_mgrackstone
  147. else
  148. data[vp] = c_mgredrack
  149. end
  150. else
  151. data[vp] = c_stone
  152. end
  153. end
  154. -- Generate the endless nether lava ocean, with islands.
  155. if y <= lakebed or y <= islandi then
  156. -- Fill in map below brimstone ocean & create islands.
  157. data[vp] = c_redrack
  158. elseif y <= lakelav then
  159. data[vp] = c_nlava -- Special lava for brimstone ocean.
  160. elseif y <= lakecel then
  161. -- Carve out horizontal cavern above brimstone ocean.
  162. data[vp] = c_air
  163. elseif ip == c_air and y > lakecel and y <= cavernf then
  164. if y <= (lakecel + 4) then
  165. -- Fill in huge ceiling holes created when huge caves
  166. -- intersect with the brimstone ocean realm.
  167. data[vp] = c_redrack
  168. else
  169. data[vp] = c_nlava
  170. end
  171. end
  172. end -- If Y coord is below nether start.
  173. end -- For all in Y coordinates.
  174. end -- For all in X coordinates.
  175. end -- For all in Z coordinates.
  176. -- Second mapgen pass, for things that need overgenerating.
  177. local ex1 = emax.x
  178. local ey1 = emax.y
  179. local ez1 = emax.z
  180. local ex0 = emin.x
  181. local ey0 = emin.y
  182. local ez0 = emin.z
  183. for z = ez0, ez1 do
  184. for x = ex0, ex1 do
  185. for y = ey0, ey1 do
  186. -- Get voxelmap indice.
  187. local vp = area:index(x, y, z)
  188. local ip = data[vp]
  189. -- Transform dungeons (over)-generated by the C++ mapgen.
  190. -- Also remove water.
  191. if y <= nstart then
  192. if ip == c_cobble or ip == c_mossycobble then
  193. data[vp] = c_netherbrick
  194. elseif ip == c_cobblestair then
  195. data[vp] = c_netherstair
  196. elseif ip == c_water or ip == c_waterflow then
  197. data[vp] = c_lava
  198. end
  199. end
  200. -- Bedrock layer.
  201. if y <= rstart+pr:next(1,pr:next(2, 3)) then
  202. data[vp] = c_bedrock
  203. end
  204. end
  205. end
  206. end
  207. -- These tables will be filled with positions for decorations,
  208. -- to be placed after the voxel manipulator code finishes.
  209. local chnk_pos_flame = {}
  210. local chnk_pos_crystal = {}
  211. local chnk_pos_tree = {}
  212. local chnk_pos_shroom = {}
  213. local chnk_pos_vine = {}
  214. local chnk_pos_grass = {}
  215. local chnk_pos_flower = {}
  216. local is_netherock = function(ip)
  217. if ip == c_redrack or
  218. ip == c_rackstone or
  219. ip == c_mgredrack or
  220. ip == c_mgrackstone then
  221. return true
  222. else
  223. return false
  224. end
  225. end
  226. -- Final mapgen pass, for finding floors and ceilings.
  227. -- Note: this has to be a seperate pass because otherwise the code
  228. -- will find floors and ceilings where there aren't any. Sad but true. :(
  229. for x = x0, x1 do
  230. for z = z0, z1 do
  231. for y = y0, y1 do
  232. local vp = area:index(x, y, z)
  233. local vu = area:index(x, y+1, z)
  234. local vd = area:index(x, y-1, z)
  235. local ip = data[vp]
  236. local iu = data[vu]
  237. local id = data[vd]
  238. -- Found floor.
  239. if is_netherock(ip) and iu == c_air then
  240. if pr:next(0, 2000) == 0 then
  241. chnk_pos_flame[#chnk_pos_flame+1] = {x=x, y=y+1, z=z}
  242. elseif pr:next(0, 5000) == 0 then
  243. chnk_pos_tree[#chnk_pos_tree+1] = {x=x, y=y+1, z=z}
  244. elseif pr:next(0, 3000) == 0 then
  245. chnk_pos_shroom[#chnk_pos_shroom+1] = {x=x, y=y+1, z=z}
  246. elseif pr:next(0, 200) == 0 then
  247. chnk_pos_grass[#chnk_pos_grass+1] = {x=x, y=y+1, z=z}
  248. elseif pr:next(0, 400) == 0 and y > -30750 then
  249. chnk_pos_flower[#chnk_pos_flower+1] = {x=x, y=y+1, z=z}
  250. end
  251. end
  252. -- Found roof.
  253. if is_netherock(ip) and id == c_air then
  254. if pr:next(0, 4000) == 0 then
  255. chnk_pos_crystal[#chnk_pos_crystal+1] = {x=x, y=y-1, z=z}
  256. elseif pr:next(0, 10) == 0 then
  257. chnk_pos_vine[#chnk_pos_vine+1] = {x=x, y=y-1, z=z}
  258. end
  259. end
  260. end
  261. end
  262. end
  263. -- Finalize voxel manipulator.
  264. vm:set_data(data)
  265. minetest.generate_ores(vm, minp, maxp)
  266. vm:set_lighting({day=0, night=0})
  267. vm:calc_lighting()
  268. vm:update_liquids()
  269. vm:write_to_map()
  270. for k, v in ipairs(chnk_pos_flame) do
  271. nethermapgen.scatter_flames(v)
  272. end
  273. for k, v in ipairs(chnk_pos_tree) do
  274. local p = table.copy(v)
  275. p.y = p.y - 1
  276. set_node(p, {name="rackstone:dauthsand"})
  277. firetree.create_firetree({x=p.x, y=p.y+1, z=p.z})
  278. end
  279. for k, v in ipairs(chnk_pos_shroom) do
  280. local p = table.copy(v)
  281. p.y = p.y - 1
  282. set_node(p, {name="rackstone:dauthsand"})
  283. redshroom.create_shroom({x=p.x, y=p.y+1, z=p.z})
  284. end
  285. for k, v in ipairs(chnk_pos_crystal) do
  286. nethermapgen.place_crystal(v)
  287. end
  288. for k, v in ipairs(chnk_pos_vine) do
  289. local p = table.copy(v)
  290. local r = pr:next(2, 7)
  291. for i = 1, r, 1 do
  292. if get_node(p).name ~= "air" then
  293. break
  294. end
  295. set_node(p, {name="nethervine:vine"})
  296. p.y = p.y - 1
  297. end
  298. end
  299. for k, v in ipairs(chnk_pos_grass) do
  300. -- Use swap_node to avoid triggering the construction timer.
  301. swap_node(v, {name="nether:grass_" .. math_random(1, 3)})
  302. end
  303. for k, v in ipairs(chnk_pos_flower) do
  304. -- Use swap_node to avoid triggering the construction timer.
  305. swap_node(v, {name="nether:glowflower"})
  306. end
  307. end