a5e61585ed80a31684a2ad71441a89cca3e7a006
[oweals/minetest_game.git] / mods / default / mapgen.lua
1 -- minetest/default/mapgen.lua
2
3 --
4 -- Aliases for map generator outputs
5 --
6
7 minetest.register_alias("mapgen_air", "air")
8 minetest.register_alias("mapgen_stone", "default:stone")
9 minetest.register_alias("mapgen_tree", "default:tree")
10 minetest.register_alias("mapgen_leaves", "default:leaves")
11 minetest.register_alias("mapgen_jungletree", "default:jungletree")
12 minetest.register_alias("mapgen_jungleleaves", "default:jungleleaves")
13 minetest.register_alias("mapgen_apple", "default:apple")
14 minetest.register_alias("mapgen_water_source", "default:water_source")
15 minetest.register_alias("mapgen_dirt", "default:dirt")
16 minetest.register_alias("mapgen_sand", "default:sand")
17 minetest.register_alias("mapgen_gravel", "default:gravel")
18 minetest.register_alias("mapgen_clay", "default:clay")
19 minetest.register_alias("mapgen_lava_source", "default:lava_source")
20 minetest.register_alias("mapgen_cobble", "default:cobble")
21 minetest.register_alias("mapgen_mossycobble", "default:mossycobble")
22 minetest.register_alias("mapgen_dirt_with_grass", "default:dirt_with_grass")
23 minetest.register_alias("mapgen_junglegrass", "default:junglegrass")
24 minetest.register_alias("mapgen_stone_with_coal", "default:stone_with_coal")
25 minetest.register_alias("mapgen_stone_with_iron", "default:stone_with_iron")
26 minetest.register_alias("mapgen_mese", "default:mese")
27 minetest.register_alias("mapgen_desert_sand", "default:desert_sand")
28 minetest.register_alias("mapgen_desert_stone", "default:desert_stone")
29
30 --
31 -- Ore generation
32 --
33
34 function default.generate_ore(name, wherein, minp, maxp, seed, chunks_per_volume, chunk_size, ore_per_chunk, height_min, height_max)
35         if maxp.y < height_min or minp.y > height_max then
36                 return
37         end
38         local y_min = math.max(minp.y, height_min)
39         local y_max = math.min(maxp.y, height_max)
40         local volume = (maxp.x-minp.x+1)*(y_max-y_min+1)*(maxp.z-minp.z+1)
41         local pr = PseudoRandom(seed)
42         local num_chunks = math.floor(chunks_per_volume * volume)
43         local inverse_chance = math.floor(chunk_size*chunk_size*chunk_size / ore_per_chunk)
44         --print("generate_ore num_chunks: "..dump(num_chunks))
45         for i=1,num_chunks do
46                 local y0 = pr:next(y_min, y_max-chunk_size+1)
47                 if y0 >= height_min and y0 <= height_max then
48                         local x0 = pr:next(minp.x, maxp.x-chunk_size+1)
49                         local z0 = pr:next(minp.z, maxp.z-chunk_size+1)
50                         local p0 = {x=x0, y=y0, z=z0}
51                         for x1=0,chunk_size-1 do
52                         for y1=0,chunk_size-1 do
53                         for z1=0,chunk_size-1 do
54                                 if pr:next(1,inverse_chance) == 1 then
55                                         local x2 = x0+x1
56                                         local y2 = y0+y1
57                                         local z2 = z0+z1
58                                         local p2 = {x=x2, y=y2, z=z2}
59                                         if minetest.env:get_node(p2).name == wherein then
60                                                 minetest.env:set_node(p2, {name=name})
61                                         end
62                                 end
63                         end
64                         end
65                         end
66                 end
67         end
68         --print("generate_ore done")
69 end
70
71 function default.make_papyrus(pos, size)
72         for y=0,size-1 do
73                 local p = {x=pos.x, y=pos.y+y, z=pos.z}
74                 local nn = minetest.env:get_node(p).name
75                 if minetest.registered_nodes[nn] and
76                         minetest.registered_nodes[nn].buildable_to then
77                         minetest.env:set_node(p, {name="default:papyrus"})
78                 else
79                         return
80                 end
81         end
82 end
83
84 function default.make_cactus(pos, size)
85         for y=0,size-1 do
86                 local p = {x=pos.x, y=pos.y+y, z=pos.z}
87                 local nn = minetest.env:get_node(p).name
88                 if minetest.registered_nodes[nn] and
89                         minetest.registered_nodes[nn].buildable_to then
90                         minetest.env:set_node(p, {name="default:cactus"})
91                 else
92                         return
93                 end
94         end
95 end
96
97 -- facedir: 0/1/2/3 (head node facedir value)
98 -- length: length of rainbow tail
99 function default.make_nyancat(pos, facedir, length)
100         local tailvec = {x=0, y=0, z=0}
101         if facedir == 0 then
102                 tailvec.z = 1
103         elseif facedir == 1 then
104                 tailvec.x = 1
105         elseif facedir == 2 then
106                 tailvec.z = -1
107         elseif facedir == 3 then
108                 tailvec.x = -1
109         else
110                 print("default.make_nyancat(): Invalid facedir: "+dump(facedir))
111                 facedir = 0
112                 tailvec.z = 1
113         end
114         local p = {x=pos.x, y=pos.y, z=pos.z}
115         minetest.env:set_node(p, {name="default:nyancat", param2=facedir})
116         for i=1,length do
117                 p.x = p.x + tailvec.x
118                 p.z = p.z + tailvec.z
119                 minetest.env:set_node(p, {name="default:nyancat_rainbow"})
120         end
121 end
122
123 function generate_nyancats(seed, minp, maxp)
124         local height_min = -31000
125         local height_max = -32
126         if maxp.y < height_min or minp.y > height_max then
127                 return
128         end
129         local y_min = math.max(minp.y, height_min)
130         local y_max = math.min(maxp.y, height_max)
131         local volume = (maxp.x-minp.x+1)*(y_max-y_min+1)*(maxp.z-minp.z+1)
132         local pr = PseudoRandom(seed + 9324342)
133         local max_num_nyancats = math.floor(volume / (16*16*16))
134         for i=1,max_num_nyancats do
135                 if pr:next(0, 1000) == 0 then
136                         local x0 = pr:next(minp.x, maxp.x)
137                         local y0 = pr:next(minp.y, maxp.y)
138                         local z0 = pr:next(minp.z, maxp.z)
139                         local p0 = {x=x0, y=y0, z=z0}
140                         default.make_nyancat(p0, pr:next(0,3), pr:next(3,15))
141                 end
142         end
143 end
144
145 minetest.register_on_generated(function(minp, maxp, seed)
146         -- Generate regular ores
147         default.generate_ore("default:stone_with_coal", "default:stone", minp, maxp, seed+0, 1/8/8/8,    3, 8, -31000,  64)
148         default.generate_ore("default:stone_with_iron", "default:stone", minp, maxp, seed+1, 1/12/12/12, 2, 3,    -15,   2)
149         default.generate_ore("default:stone_with_iron", "default:stone", minp, maxp, seed+2, 1/9/9/9,    3, 5,    -63, -16)
150         default.generate_ore("default:stone_with_iron", "default:stone", minp, maxp, seed+3, 1/7/7/7,    3, 5, -31000, -64)
151         
152         default.generate_ore("default:stone_with_mese", "default:stone", minp, maxp, seed+4, 1/16/16/16, 2, 3,   -127,  -64)
153         default.generate_ore("default:stone_with_mese", "default:stone", minp, maxp, seed+5, 1/9/9/9,    3, 5, -31000, -128)
154         default.generate_ore("default:mese",            "default:stone", minp, maxp, seed+8, 1/16/16/16, 2, 3, -31000,-1024)
155         
156         default.generate_ore("default:stone_with_coal", "default:stone", minp, maxp, seed+7, 1/24/24/24, 6,27, -31000,  0)
157         default.generate_ore("default:stone_with_iron", "default:stone", minp, maxp, seed+6, 1/24/24/24, 6,27, -31000, -64)
158
159         if maxp.y >= 2 and minp.y <= 0 then
160                 -- Generate clay
161                 -- Assume X and Z lengths are equal
162                 local divlen = 4
163                 local divs = (maxp.x-minp.x)/divlen+1;
164                 for divx=0+1,divs-1-1 do
165                 for divz=0+1,divs-1-1 do
166                         local cx = minp.x + math.floor((divx+0.5)*divlen)
167                         local cz = minp.z + math.floor((divz+0.5)*divlen)
168                         if minetest.env:get_node({x=cx,y=1,z=cz}).name == "default:water_source" and
169                                         minetest.env:get_node({x=cx,y=0,z=cz}).name == "default:sand" then
170                                 local is_shallow = true
171                                 local num_water_around = 0
172                                 if minetest.env:get_node({x=cx-divlen*2,y=1,z=cz+0}).name == "default:water_source" then
173                                         num_water_around = num_water_around + 1 end
174                                 if minetest.env:get_node({x=cx+divlen*2,y=1,z=cz+0}).name == "default:water_source" then
175                                         num_water_around = num_water_around + 1 end
176                                 if minetest.env:get_node({x=cx+0,y=1,z=cz-divlen*2}).name == "default:water_source" then
177                                         num_water_around = num_water_around + 1 end
178                                 if minetest.env:get_node({x=cx+0,y=1,z=cz+divlen*2}).name == "default:water_source" then
179                                         num_water_around = num_water_around + 1 end
180                                 if num_water_around >= 2 then
181                                         is_shallow = false
182                                 end     
183                                 if is_shallow then
184                                         for x1=-divlen,divlen do
185                                         for z1=-divlen,divlen do
186                                                 if minetest.env:get_node({x=cx+x1,y=0,z=cz+z1}).name == "default:sand" then
187                                                         minetest.env:set_node({x=cx+x1,y=0,z=cz+z1}, {name="default:clay"})
188                                                 end
189                                         end
190                                         end
191                                 end
192                         end
193                 end
194                 end
195                 -- Generate papyrus
196                 local perlin1 = minetest.env:get_perlin(354, 3, 0.7, 100)
197                 -- Assume X and Z lengths are equal
198                 local divlen = 8
199                 local divs = (maxp.x-minp.x)/divlen+1;
200                 for divx=0,divs-1 do
201                 for divz=0,divs-1 do
202                         local x0 = minp.x + math.floor((divx+0)*divlen)
203                         local z0 = minp.z + math.floor((divz+0)*divlen)
204                         local x1 = minp.x + math.floor((divx+1)*divlen)
205                         local z1 = minp.z + math.floor((divz+1)*divlen)
206                         -- Determine papyrus amount from perlin noise
207                         local papyrus_amount = math.floor(perlin1:get2d({x=x0, y=z0}) * 45 - 20)
208                         -- Find random positions for papyrus based on this random
209                         local pr = PseudoRandom(seed+1)
210                         for i=0,papyrus_amount do
211                                 local x = pr:next(x0, x1)
212                                 local z = pr:next(z0, z1)
213                                 if minetest.env:get_node({x=x,y=1,z=z}).name == "default:dirt_with_grass" and
214                                                 minetest.env:find_node_near({x=x,y=1,z=z}, 1, "default:water_source") then
215                                         default.make_papyrus({x=x,y=2,z=z}, pr:next(2, 4))
216                                 end
217                         end
218                 end
219                 end
220                 -- Generate cactuses
221                 local perlin1 = minetest.env:get_perlin(230, 3, 0.6, 100)
222                 -- Assume X and Z lengths are equal
223                 local divlen = 16
224                 local divs = (maxp.x-minp.x)/divlen+1;
225                 for divx=0,divs-1 do
226                 for divz=0,divs-1 do
227                         local x0 = minp.x + math.floor((divx+0)*divlen)
228                         local z0 = minp.z + math.floor((divz+0)*divlen)
229                         local x1 = minp.x + math.floor((divx+1)*divlen)
230                         local z1 = minp.z + math.floor((divz+1)*divlen)
231                         -- Determine cactus amount from perlin noise
232                         local cactus_amount = math.floor(perlin1:get2d({x=x0, y=z0}) * 6 - 3)
233                         -- Find random positions for cactus based on this random
234                         local pr = PseudoRandom(seed+1)
235                         for i=0,cactus_amount do
236                                 local x = pr:next(x0, x1)
237                                 local z = pr:next(z0, z1)
238                                 -- Find ground level (0...15)
239                                 local ground_y = nil
240                                 for y=30,0,-1 do
241                                         if minetest.env:get_node({x=x,y=y,z=z}).name ~= "air" then
242                                                 ground_y = y
243                                                 break
244                                         end
245                                 end
246                                 -- If desert sand, make cactus
247                                 if ground_y and minetest.env:get_node({x=x,y=ground_y,z=z}).name == "default:desert_sand" then
248                                         default.make_cactus({x=x,y=ground_y+1,z=z}, pr:next(3, 4))
249                                 end
250                         end
251                 end
252                 end
253                 -- Generate grass
254                 local perlin1 = minetest.env:get_perlin(329, 3, 0.6, 100)
255                 -- Assume X and Z lengths are equal
256                 local divlen = 16
257                 local divs = (maxp.x-minp.x)/divlen+1;
258                 for divx=0,divs-1 do
259                 for divz=0,divs-1 do
260                         local x0 = minp.x + math.floor((divx+0)*divlen)
261                         local z0 = minp.z + math.floor((divz+0)*divlen)
262                         local x1 = minp.x + math.floor((divx+1)*divlen)
263                         local z1 = minp.z + math.floor((divz+1)*divlen)
264                         -- Determine grass amount from perlin noise
265                         local grass_amount = math.floor(perlin1:get2d({x=x0, y=z0}) ^ 3 * 9)
266                         -- Find random positions for grass based on this random
267                         local pr = PseudoRandom(seed+1)
268                         for i=0,grass_amount do
269                                 local x = pr:next(x0, x1)
270                                 local z = pr:next(z0, z1)
271                                 -- Find ground level (0...15)
272                                 local ground_y = nil
273                                 for y=30,0,-1 do
274                                         if minetest.env:get_node({x=x,y=y,z=z}).name ~= "air" then
275                                                 ground_y = y
276                                                 break
277                                         end
278                                 end
279                                 
280                                 if ground_y then
281                                         local p = {x=x,y=ground_y+1,z=z}
282                                         local nn = minetest.env:get_node(p).name
283                                         -- Check if the node can be replaced
284                                         if minetest.registered_nodes[nn] and
285                                                 minetest.registered_nodes[nn].buildable_to then
286                                                 nn = minetest.env:get_node({x=x,y=ground_y,z=z}).name
287                                                 -- If desert sand, add dry shrub
288                                                 if nn == "default:desert_sand" then
289                                                         minetest.env:set_node(p,{name="default:dry_shrub"})
290                                                         
291                                                 -- If dirt with grass, add grass
292                                                 elseif nn == "default:dirt_with_grass" then
293                                                         minetest.env:set_node(p,{name="default:grass_"..pr:next(1, 5)})
294                                                 end
295                                         end
296                                 end
297                                 
298                         end
299                 end
300                 end
301         end
302
303         -- Generate nyan cats
304         generate_nyancats(seed, minp, maxp)
305 end)
306