1 local S
= minetest
.get_translator("tsm_pyramids")
3 -- Pyramid width (must be an odd number)
5 -- Pyramid width minus 1
6 local PYRA_Wm
= PYRA_W
- 1
7 -- Half of (Pyramid width minus 1)
8 local PYRA_Wh
= PYRA_Wm
/ 2
9 -- Minimum spawn height
14 dofile(minetest
.get_modpath("tsm_pyramids").."/mummy.lua")
15 dofile(minetest
.get_modpath("tsm_pyramids").."/nodes.lua")
16 dofile(minetest
.get_modpath("tsm_pyramids").."/room.lua")
18 local mg_name
= minetest
.get_mapgen_setting("mg_name")
21 {name
="default:apple", max = 3},
22 {name
="default:steel_ingot", max = 3},
23 {name
="default:copper_ingot", max = 3},
24 {name
="default:gold_ingot", max = 2},
25 {name
="default:diamond", max = 1},
26 {name
="default:pick_steel", max = 1},
27 {name
="default:pick_diamond", max = 1},
28 {name
="default:papyrus", max = 9},
31 if minetest
.get_modpath("farming") then
32 table.insert(chest_stuff
, {name
="farming:bread", max = 3})
33 table.insert(chest_stuff
, {name
="farming:cotton", max = 8})
35 table.insert(chest_stuff
, {name
="farming:apple", max = 8})
36 table.insert(chest_stuff
, {name
="farming:apple", max = 3})
38 if minetest
.get_modpath("tnt") then
39 table.insert(chest_stuff
, {name
="tnt:gunpowder", max = 6})
41 table.insert(chest_stuff
, {name
="farming:apple", max = 3})
44 function tsm_pyramids
.fill_chest(pos
, stype
, flood_sand
)
45 local sand
= "default:sand"
46 if stype
== "desert_sandstone" or stype
== "desert_stone" then
47 sand
= "default:desert_sand"
49 local n
= minetest
.get_node(pos
)
50 if n
and n
.name
and n
.name
== "default:chest" then
51 local meta
= minetest
.get_meta(pos
)
52 local inv
= meta
:get_inventory()
53 inv
:set_size("main", 8*4)
55 -- Fill with sand in sand-flooded pyramids
57 table.insert(stacks
, {name
=sand
, count
= math
.random(1,32)})
60 if math
.random(1,10) >= 7 then
61 if minetest
.get_modpath("treasurer") ~= nil then
62 stacks
= treasurer
.select_random_treasures(3,7,9,{"minetool", "food", "crafting_component"})
65 local stuff
= chest_stuff
[math
.random(1,#chest_stuff
)]
66 table.insert(stacks
, {name
=stuff
.name
, count
= math
.random(1,stuff
.max)})
71 if not inv
:contains_item("main", stacks
[s
]) then
72 inv
:set_stack("main", math
.random(1,32), stacks
[s
])
78 local function add_spawner(pos
, mummy_offset
)
79 minetest
.set_node(pos
, {name
="tsm_pyramids:spawner_mummy"})
80 if not minetest
.settings
:get_bool("only_peaceful_mobs") then tsm_pyramids
.spawn_mummy(vector
.add(pos
, mummy_offset
),2) end
83 local function can_replace(pos
)
84 local n
= minetest
.get_node_or_nil(pos
)
85 if n
and n
.name
and minetest
.registered_nodes
[n
.name
] and not minetest
.registered_nodes
[n
.name
].walkable
then
94 local function make_foundation_part(pos
, set_to_stone
)
98 while can_replace(p2
)==true do
103 table.insert(set_to_stone
, table.copy(p2
))
108 local function make_entrance(pos
, rot
, brick
, sand
, flood_sand
)
109 local roffset_arr
= {
110 { x
=0, y
=0, z
=1 }, -- front
111 { x
=-1, y
=0, z
=0 }, -- left
112 { x
=0, y
=0, z
=-1 }, -- back
113 { x
=1, y
=0, z
=0 }, -- right
115 local roffset
= roffset_arr
[rot
+ 1]
118 way
= vector
.add(pos
, {x
=PYRA_Wh
, y
=0, z
=0})
120 way
= vector
.add(pos
, {x
=PYRA_Wm
, y
=0, z
=PYRA_Wh
})
122 way
= vector
.add(pos
, {x
=PYRA_Wh
, y
=0, z
=PYRA_Wm
})
124 way
= vector
.add(pos
, {x
=0, y
=0, z
=PYRA_Wh
})
126 local max_sand_height
= math
.random(1,3)
128 local sand_height
= math
.random(1,max_sand_height
)
131 local way_dir
= vector
.add(vector
.add(way
, {x
=0,y
=iy
,z
=0}), vector
.multiply(roffset
, ie
))
132 if flood_sand
and sand
~= "ignore" and iy
<= sand_height
and ie
>= 3 then
133 minetest
.set_node(way_dir
, {name
=sand
})
135 minetest
.remove_node(way_dir
)
137 -- build decoration above entrance
138 if ie
>=3 and iy
== 3 then
139 local deco
= {x
=way_dir
.x
, y
=way_dir
.y
+1,z
=way_dir
.z
}
140 minetest
.set_node(deco
, {name
=brick
})
141 if rot
== 0 or rot
== 2 then
142 minetest
.set_node(vector
.add(deco
, {x
=-1, y
=0, z
=0}), {name
=brick
})
143 minetest
.set_node(vector
.add(deco
, {x
=1, y
=0, z
=0}), {name
=brick
})
145 minetest
.set_node(vector
.add(deco
, {x
=0, y
=0, z
=-1}), {name
=brick
})
146 minetest
.set_node(vector
.add(deco
, {x
=0, y
=0, z
=1}), {name
=brick
})
153 local function make_pyramid(pos
, brick
, sandstone
, stone
, sand
)
154 local set_to_brick
= {}
155 local set_to_sand
= {}
156 local set_to_stone
= {}
158 for iy
=0,math
.random(10,PYRA_Wh
),1 do
159 for ix
=iy
,PYRA_W
-1-iy
,1 do
160 for iz
=iy
,PYRA_W
-1-iy
,1 do
162 make_foundation_part({x
=pos
.x
+ix
,y
=pos
.y
,z
=pos
.z
+iz
}, set_to_stone
)
164 table.insert(set_to_brick
, {x
=pos
.x
+ix
,y
=pos
.y
+iy
,z
=pos
.z
+iz
})
168 minetest
.bulk_set_node(set_to_stone
, {name
=stone
})
169 minetest
.bulk_set_node(set_to_brick
, {name
=brick
})
170 if sand
~= "ignore" then
171 minetest
.bulk_set_node(set_to_sand
, {name
=sand
})
175 local function make(pos
, brick
, sandstone
, stone
, sand
, ptype
, room_id
)
176 local bpos
= table.copy(pos
)
178 make_pyramid(bpos
, brick
, sandstone
, stone
, sand
)
180 local rot
= math
.random(0, 3)
182 local ok
, msg
, flood_sand
= tsm_pyramids
.make_room(bpos
, ptype
, room_id
, rot
)
183 -- Place mummy spawner
184 local r
= math
.random(1,3)
185 -- 4 possible spawner positions
186 local spawner_posses
= {
188 {{x
=bpos
.x
+PYRA_Wh
,y
=bpos
.y
+2, z
=bpos
.z
+5}, {x
=0, y
=0, z
=2}},
190 {{x
=bpos
.x
+PYRA_Wm
-5,y
=bpos
.y
+2, z
=bpos
.z
+PYRA_Wh
}, {x
=-2, y
=0, z
=0}},
192 {{x
=bpos
.x
+PYRA_Wh
,y
=bpos
.y
+2, z
=bpos
.z
+PYRA_W
-5}, {x
=0, y
=0, z
=-2}},
194 {{x
=bpos
.x
+5,y
=bpos
.y
+2, z
=bpos
.z
+PYRA_Wh
}, {x
=2, y
=0, z
=0}},
196 -- Delete the spawner position in which the entrance will be placed
197 table.remove(spawner_posses
, (rot
% 4) + 1)
198 add_spawner(spawner_posses
[r
][1], spawner_posses
[r
][2])
200 make_entrance(bpos
, rot
, brick
, sand
, flood_sand
)
202 minetest
.log("action", "[tsm_pyramids] Created pyramid at "..minetest
.pos_to_string(bpos
)..".")
206 local perl1
= {SEED1
= 9130, OCTA1
= 3, PERS1
= 0.5, SCAL1
= 250} -- Values should match minetest mapgen V6 desert noise.
207 local perlin1
-- perlin noise buffer
209 local function hlp_fnct(pos
, name
)
210 local n
= minetest
.get_node_or_nil(pos
)
211 if n
and n
.name
and n
.name
== name
then
217 local function ground(pos
, old
)
218 local p2
= table.copy(pos
)
219 while hlp_fnct(p2
, "air") do
223 return {x
=old
.x
, y
=p2
.y
, z
=old
.z
}
229 -- Attempt to generate a pyramid in the generated area.
230 -- Up to one pyramid per mapchunk.
231 minetest
.register_on_generated(function(minp
, maxp
, seed
)
232 if maxp
.y
< PYRA_MIN_Y
then return end
234 -- TODO: Use Minetests pseudo-random tools
235 math
.randomseed(seed
)
238 perlin1
= minetest
.get_perlin(perl1
.SEED1
, perl1
.OCTA1
, perl1
.PERS1
, perl1
.SCAL1
)
240 --[[ Make sure the pyramid doesn't bleed outside of maxp,
241 so it doesn't get placed incompletely by the mapgen.
242 This creates a bias somewhat, as this means there are some coordinates in
243 which pyramids cannot spawn. But it's still better to have broken pyramids.
245 local limit
= function(pos
, maxp
)
246 pos
.x
= math
.min(pos
.x
, maxp
.x
- PYRA_W
+1)
247 pos
.y
= math
.min(pos
.y
, maxp
.y
- PYRA_Wh
)
248 pos
.z
= math
.min(pos
.z
, maxp
.z
- PYRA_W
+1)
251 local noise1
= perlin1
:get_2d({x
=minp
.x
,y
=minp
.y
})
253 if noise1
> 0.25 or noise1
< -0.26 then
254 -- Need a bit of luck to place a pyramid
255 if math
.random(0,10) > 7 then
256 minetest
.log("verbose", "[tsm_pyramids] Pyramid not placed, bad dice roll. minp="..minetest
.pos_to_string(minp
))
260 local mpos
= {x
=math
.random(minp
.x
,maxp
.x
), y
=math
.random(minp
.y
,maxp
.y
), z
=math
.random(minp
.z
,maxp
.z
)}
262 local sands
= {"default:sand", "default:desert_sand", "default:desert_stone"}
267 local sand_cnt_max
= 0
268 local sand_cnt_max_id
269 -- Look for sand or desert stone to place the pyramid on
274 psand
[s
] = minetest
.find_node_near(mpos
, 25, sand
)
277 mpos
= {x
=math
.random(minp
.x
,maxp
.x
), y
=math
.random(minp
.y
,maxp
.y
), z
=math
.random(minp
.z
,maxp
.z
)}
278 local spos
= minetest
.find_node_near(mpos
, 25, sand
)
280 sand_cnt
= sand_cnt
+ 1
281 if psand
[s
] == nil then
285 if sand_cnt
> sand_cnt_max
then
286 sand_cnt_max
= sand_cnt
293 minetest
.log("verbose", "[tsm_pyramids] Pyramid not placed, no suitable surface. minp="..minetest
.pos_to_string(minp
))
296 -- Select the material type by the most prominent node type
297 -- E.g. if desert sand is most prominent, we place a desert sandstone pyramid
298 if sand_cnt_max_id
then
299 sand
= sands
[sand_cnt_max_id
]
301 if p2
.y
< PYRA_MIN_Y
then
302 minetest
.log("info", "[tsm_pyramids] Pyramid not placed, too deep. p2="..minetest
.pos_to_string(p2
))
305 -- Now sink the pyramid until each corner of it is no longer floating in mid-air
308 {x
=p2
.x
,y
=p2
.y
-1,z
=p2
.z
},
309 {x
=p2
.x
+PYRA_Wm
,y
=p2
.y
-1,z
=p2
.z
+PYRA_Wm
},
310 {x
=p2
.x
+PYRA_Wm
,y
=p2
.y
-1,z
=p2
.z
},
311 {x
=p2
.x
,y
=p2
.y
-1,z
=p2
.z
+PYRA_Wm
},
314 local opos
= oposses
[o
]
315 local n
= minetest
.get_node_or_nil(opos
)
316 if n
and n
.name
and n
.name
== "air" then
317 local old
= table.copy(p2
)
318 p2
= ground(opos
, p2
)
321 -- Random bonus sinking
322 p2
.y
= math
.max(p2
.y
- math
.random(0,3), PYRA_MIN_Y
)
324 -- Bad luck, we have hit the chunk border!
325 if p2
.y
< minp
.y
then
326 minetest
.log("info", "[tsm_pyramids] Pyramid not placed, sunken too much. p2="..minetest
.pos_to_string(p2
))
330 -- Make sure the pyramid is not near a "killer" node, like water
331 local middle
= vector
.add(p2
, {x
=PYRA_Wh
, y
=0, z
=PYRA_Wh
})
332 if minetest
.find_node_near(p2
, 5, {"default:water_source"}) ~= nil or
333 minetest
.find_node_near(vector
.add(p2
, {x
=PYRA_W
, y
=0, z
=0}), 5, {"default:water_source"}) ~= nil or
334 minetest
.find_node_near(vector
.add(p2
, {x
=0, y
=0, z
=PYRA_W
}), 5, {"default:water_source"}) ~= nil or
335 minetest
.find_node_near(vector
.add(p2
, {x
=PYRA_W
, y
=0, z
=PYRA_W
}), 5, {"default:water_source"}) ~= nil or
337 minetest
.find_node_near(middle
, PYRA_W
, {"default:dirt_with_grass"}) ~= nil or
338 minetest
.find_node_near(middle
, 52, {"default:sandstonebrick", "default:desert_sandstone_brick", "default:desert_stonebrick"}) ~= nil or
339 minetest
.find_node_near(middle
, PYRA_Wh
+ 3, {"default:cactus", "group:leaves", "group:tree"}) ~= nil then
340 minetest
.log("info", "[tsm_pyramids] Pyramid not placed, inappropriate node nearby. p2="..minetest
.pos_to_string(p2
))
344 -- Bonus chance to spawn a sandstone pyramid in v6 desert because otherwise they would be too rare in v6
345 if (mg_name
== "v6" and sand
== "default:desert_sand" and math
.random(1, 2) == 1) then
346 sand
= "default:sand"
349 -- Desert stone pyramids only generate in areas with almost no sand
350 if sand
== "default:desert_stone" then
351 local nodes
= minetest
.find_nodes_in_area(vector
.add(p2
, {x
=-1, y
=-2, z
=-1}), vector
.add(p2
, {x
=PYRA_W
+1, y
=PYRA_Wh
, z
=PYRA_W
+1}), {"group:sand"})
353 sand
= "default:desert_sand"
357 -- Generate the pyramid!
358 if sand
== "default:desert_sand" then
359 -- Desert sandstone pyramid
360 make(p2
, "default:desert_sandstone_brick", "default:desert_sandstone", "default:desert_stone", "default:desert_sand", "desert_sandstone")
361 elseif sand
== "default:sand" then
363 make(p2
, "default:sandstonebrick", "default:sandstone", "default:sandstone", "default:sand", "sandstone")
365 -- Desert stone pyramid
366 make(p2
, "default:desert_stonebrick", "default:desert_stone_block", "default:desert_stone", "ignore", "desert_stone")
371 -- Add backwards-compability for nodes from the original pyramids mod
372 if minetest
.get_modpath("pyramids") == nil then
374 minetest
.register_alias("pyramids:trap", "tsm_pyramids:trap")
375 minetest
.register_alias("pyramids:trap_2", "tsm_pyramids:trap_2")
376 minetest
.register_alias("pyramids:deco_stone1", "tsm_pyramids:deco_stone1")
377 minetest
.register_alias("pyramids:deco_stone2", "tsm_pyramids:deco_stone2")
378 minetest
.register_alias("pyramids:deco_stone3", "tsm_pyramids:deco_stone3")
379 minetest
.register_alias("pyramids:spawner_mummy", "tsm_pyramids:spawner_mummy")
382 minetest
.register_chatcommand("spawnpyramid", {
383 description
= S("Generate a pyramid"),
384 params
= S("[<room_type>]"),
385 privs
= { server
= true },
386 func
= function(name
, param
)
387 local player
= minetest
.get_player_by_name(name
)
389 return false, S("No player.")
391 local pos
= player
:get_pos()
392 pos
= vector
.round(pos
)
393 local s
= math
.random(1,3)
394 local r
= tonumber(param
)
400 pos
= vector
.add(pos
, {x
=-PYRA_Wh
, y
=-1, z
=0})
403 ok
, msg
= make(pos
, "default:sandstonebrick", "default:sandstone", "default:sandstone", "default:sand", "sandstone", room_id
)
406 ok
, msg
= make(pos
, "default:desert_sandstone_brick", "default:desert_sandstone", "default:desert_stone", "default:desert_sand", "desert_sandstone", room_id
)
409 ok
, msg
= make(pos
, "default:desert_stonebrick", "default:desert_stone_block", "default:desert_stone", "ignore", "desert_stone", room_id
)
412 return true, S("Pyramid generated at @1.", minetest
.pos_to_string(pos
))