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")
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},
29 {name
="default:mese_crystal", max = 4},
30 {name
="default:gold_ingot", max = 10},
31 {name
="default:pick_diamond", max = 1},
34 {name
="default:apple", max = 1},
35 {name
="default:stick", max = 64},
36 {name
="default:acacia_bush_sapling", max = 1},
37 {name
="default:paper", max = 9},
38 {name
="default:shovel_bronze", max = 1},
39 {name
="default:pick_mese", max = 1},
42 {name
="default:obsidian_shard", max = 5},
43 {name
="default:apple", max = 3},
44 {name
="default:blueberries", max = 9},
45 {name
="default:glass", max = 64},
46 {name
="default:bush_sapling", max = 1},
47 {name
="default:pick_bronze", max = 1},
51 if minetest
.get_modpath("farming") then
52 table.insert(chest_stuff
.desert_sandstone
, {name
="farming:bread", max = 3})
53 table.insert(chest_stuff
.sandstone
, {name
="farming:bread", max = 4})
54 table.insert(chest_stuff
.normal
, {name
="farming:cotton", max = 32})
55 table.insert(chest_stuff
.desert_sandstone
, {name
="farming:seed_cotton", max = 3})
56 table.insert(chest_stuff
.desert_sandstone
, {name
="farming:hoe_stone", max = 1})
58 table.insert(chest_stuff
.normal
, {name
="farming:apple", max = 8})
59 table.insert(chest_stuff
.normal
, {name
="farming:apple", max = 3})
61 if minetest
.get_modpath("tnt") then
62 table.insert(chest_stuff
.normal
, {name
="tnt:gunpowder", max = 6})
63 table.insert(chest_stuff
.desert_stone
, {name
="tnt:gunpowder", max = 6})
65 table.insert(chest_stuff
.normal
, {name
="farming:apple", max = 3})
68 function tsm_pyramids
.fill_chest(pos
, stype
, flood_sand
, treasure_chance
)
69 local sand
= "default:sand"
70 if not treasure_chance
then
73 if stype
== "desert_sandstone" or stype
== "desert_stone" then
74 sand
= "default:desert_sand"
76 local n
= minetest
.get_node(pos
)
77 local treasure_added
= false
78 if n
and n
.name
and n
.name
== "default:chest" then
79 local meta
= minetest
.get_meta(pos
)
80 local inv
= meta
:get_inventory()
81 inv
:set_size("main", 8*4)
83 -- Fill with sand in sand-flooded pyramids
85 table.insert(stacks
, {name
=sand
, count
= math
.random(1,32)})
88 if math
.random(1,100) <= treasure_chance
then
89 if minetest
.get_modpath("treasurer") ~= nil then
90 stacks
= treasurer
.select_random_treasures(3,7,9,{"minetool", "food", "crafting_component"})
93 local stuff
= chest_stuff
.normal
[math
.random(1,#chest_stuff
.normal
)]
94 table.insert(stacks
, {name
=stuff
.name
, count
= math
.random(1,stuff
.max)})
96 if math
.random(1,100) <= 75 then
97 local stuff
= chest_stuff
[stype
][math
.random(1,#chest_stuff
[stype
])]
98 table.insert(stacks
, {name
=stuff
.name
, count
= math
.random(1,stuff
.max)})
100 treasure_added
= true
104 if not inv
:contains_item("main", stacks
[s
]) then
105 inv
:set_stack("main", math
.random(1,32), stacks
[s
])
109 return treasure_added
112 local function add_spawner(pos
, mummy_offset
)
113 minetest
.set_node(pos
, {name
="tsm_pyramids:spawner_mummy"})
114 if not minetest
.settings
:get_bool("only_peaceful_mobs") then
116 tsm_pyramids
.attempt_mummy_spawn(pos
, false)
121 local function can_replace(pos
)
122 local n
= minetest
.get_node_or_nil(pos
)
123 if n
and n
.name
and minetest
.registered_nodes
[n
.name
] and not minetest
.registered_nodes
[n
.name
].walkable
then
132 local function make_foundation_part(pos
, set_to_stone
)
136 while can_replace(p2
)==true do
141 table.insert(set_to_stone
, table.copy(p2
))
146 local function make_entrance(pos
, rot
, brick
, sand
, flood_sand
)
147 local roffset_arr
= {
148 { x
=0, y
=0, z
=1 }, -- front
149 { x
=-1, y
=0, z
=0 }, -- left
150 { x
=0, y
=0, z
=-1 }, -- back
151 { x
=1, y
=0, z
=0 }, -- right
153 local roffset
= roffset_arr
[rot
+ 1]
156 way
= vector
.add(pos
, {x
=PYRA_Wh
, y
=0, z
=0})
158 way
= vector
.add(pos
, {x
=PYRA_Wm
, y
=0, z
=PYRA_Wh
})
160 way
= vector
.add(pos
, {x
=PYRA_Wh
, y
=0, z
=PYRA_Wm
})
162 way
= vector
.add(pos
, {x
=0, y
=0, z
=PYRA_Wh
})
164 local max_sand_height
= math
.random(1,3)
166 local sand_height
= math
.random(1,max_sand_height
)
169 local way_dir
= vector
.add(vector
.add(way
, {x
=0,y
=iy
,z
=0}), vector
.multiply(roffset
, ie
))
170 if flood_sand
and sand
~= "ignore" and iy
<= sand_height
and ie
>= 3 then
171 minetest
.set_node(way_dir
, {name
=sand
})
173 minetest
.remove_node(way_dir
)
175 -- build decoration above entrance
176 if ie
== 3 and iy
== 3 then
177 local deco
= {x
=way_dir
.x
, y
=way_dir
.y
+1,z
=way_dir
.z
}
178 minetest
.set_node(deco
, {name
=brick
})
179 if rot
== 0 or rot
== 2 then
180 minetest
.set_node(vector
.add(deco
, {x
=-1, y
=0, z
=0}), {name
=brick
})
181 minetest
.set_node(vector
.add(deco
, {x
=1, y
=0, z
=0}), {name
=brick
})
183 minetest
.set_node(vector
.add(deco
, {x
=0, y
=0, z
=-1}), {name
=brick
})
184 minetest
.set_node(vector
.add(deco
, {x
=0, y
=0, z
=1}), {name
=brick
})
191 local function make_pyramid(pos
, brick
, sandstone
, stone
, sand
)
192 local set_to_brick
= {}
193 local set_to_stone
= {}
195 for iy
=0,math
.random(10,PYRA_Wh
),1 do
196 for ix
=iy
,PYRA_W
-1-iy
,1 do
197 for iz
=iy
,PYRA_W
-1-iy
,1 do
199 make_foundation_part({x
=pos
.x
+ix
,y
=pos
.y
,z
=pos
.z
+iz
}, set_to_stone
)
201 table.insert(set_to_brick
, {x
=pos
.x
+ix
,y
=pos
.y
+iy
,z
=pos
.z
+iz
})
205 minetest
.bulk_set_node(set_to_stone
, {name
=stone
})
206 minetest
.bulk_set_node(set_to_brick
, {name
=brick
})
209 local function make(pos
, brick
, sandstone
, stone
, sand
, ptype
, room_id
)
210 local bpos
= table.copy(pos
)
212 make_pyramid(bpos
, brick
, sandstone
, stone
, sand
)
214 local rot
= math
.random(0, 3)
216 local ok
, msg
, flood_sand
= tsm_pyramids
.make_room(bpos
, ptype
, room_id
, rot
)
217 -- Place mummy spawner
218 local r
= math
.random(1,3)
219 -- 4 possible spawner positions
220 local spawner_posses
= {
222 {{x
=bpos
.x
+PYRA_Wh
,y
=bpos
.y
+2, z
=bpos
.z
+5}, {x
=0, y
=0, z
=2}},
224 {{x
=bpos
.x
+PYRA_Wm
-5,y
=bpos
.y
+2, z
=bpos
.z
+PYRA_Wh
}, {x
=-2, y
=0, z
=0}},
226 {{x
=bpos
.x
+PYRA_Wh
,y
=bpos
.y
+2, z
=bpos
.z
+PYRA_W
-5}, {x
=0, y
=0, z
=-2}},
228 {{x
=bpos
.x
+5,y
=bpos
.y
+2, z
=bpos
.z
+PYRA_Wh
}, {x
=2, y
=0, z
=0}},
230 -- Delete the spawner position in which the entrance will be placed
231 table.remove(spawner_posses
, (rot
% 4) + 1)
232 add_spawner(spawner_posses
[r
][1], spawner_posses
[r
][2])
234 make_entrance(bpos
, rot
, brick
, sand
, flood_sand
)
236 minetest
.log("action", "[tsm_pyramids] Created pyramid at "..minetest
.pos_to_string(bpos
)..".")
240 local perl1
= {SEED1
= 9130, OCTA1
= 3, PERS1
= 0.5, SCAL1
= 250} -- Values should match minetest mapgen V6 desert noise.
241 local perlin1
-- perlin noise buffer
243 local function hlp_fnct(pos
, name
)
244 local n
= minetest
.get_node_or_nil(pos
)
245 if n
and n
.name
and n
.name
== name
then
251 local function ground(pos
, old
)
252 local p2
= table.copy(pos
)
253 while hlp_fnct(p2
, "air") do
257 return {x
=old
.x
, y
=p2
.y
, z
=old
.z
}
263 -- Select the recommended type of pyramid to use, based on the environment.
264 -- One of sandstone, desert sandstone, desert stone.
265 local select_pyramid_type
= function(minp
, maxp
)
266 local mpos
= {x
=math
.random(minp
.x
,maxp
.x
), y
=math
.random(minp
.y
,maxp
.y
), z
=math
.random(minp
.z
,maxp
.z
)}
269 local sands
= {"default:sand", "default:desert_sand", "default:desert_stone"}
274 local sand_cnt_max
= 0
275 local sand_cnt_max_id
276 -- Look for sand or desert stone to place the pyramid on
281 psand
[s
] = minetest
.find_node_near(mpos
, 25, sand
)
284 mpos
= {x
=math
.random(minp
.x
,maxp
.x
), y
=math
.random(minp
.y
,maxp
.y
), z
=math
.random(minp
.z
,maxp
.z
)}
285 local spos
= minetest
.find_node_near(mpos
, 25, sand
)
287 sand_cnt
= sand_cnt
+ 1
288 if psand
[s
] == nil then
292 if sand_cnt
> sand_cnt_max
then
293 sand_cnt_max
= sand_cnt
300 -- Select the material type by the most prominent node type
301 -- E.g. if desert sand is most prominent, we place a desert sandstone pyramid
302 if sand_cnt_max_id
then
303 sand
= sands
[sand_cnt_max_id
]
311 -- Attempt to generate a pyramid in the generated area.
312 -- Up to one pyramid per mapchunk.
313 minetest
.register_on_generated(function(minp
, maxp
, seed
)
314 if maxp
.y
< PYRA_MIN_Y
then return end
316 -- TODO: Use Minetests pseudo-random tools
317 math
.randomseed(seed
)
320 perlin1
= minetest
.get_perlin(perl1
.SEED1
, perl1
.OCTA1
, perl1
.PERS1
, perl1
.SCAL1
)
322 --[[ Make sure the pyramid doesn't bleed outside of maxp,
323 so it doesn't get placed incompletely by the mapgen.
324 This creates a bias somewhat, as this means there are some coordinates in
325 which pyramids cannot spawn. But it's still better to have broken pyramids.
327 local limit
= function(pos
, maxp
)
328 pos
.x
= math
.min(pos
.x
, maxp
.x
- PYRA_W
+1)
329 pos
.y
= math
.min(pos
.y
, maxp
.y
- PYRA_Wh
)
330 pos
.z
= math
.min(pos
.z
, maxp
.z
- PYRA_W
+1)
333 local noise1
= perlin1
:get_2d({x
=minp
.x
,y
=minp
.y
})
335 if noise1
> 0.25 or noise1
< -0.26 then
336 -- Need a bit of luck to place a pyramid
337 if math
.random(0,10) > 7 then
338 minetest
.log("verbose", "[tsm_pyramids] Pyramid not placed, bad dice roll. minp="..minetest
.pos_to_string(minp
))
342 sand
, p2
= select_pyramid_type(minp
, maxp
)
345 minetest
.log("verbose", "[tsm_pyramids] Pyramid not placed, no suitable surface. minp="..minetest
.pos_to_string(minp
))
348 if p2
.y
< PYRA_MIN_Y
then
349 minetest
.log("info", "[tsm_pyramids] Pyramid not placed, too deep. p2="..minetest
.pos_to_string(p2
))
352 -- Now sink the pyramid until each corner of it is no longer floating in mid-air
355 {x
=p2
.x
,y
=p2
.y
-1,z
=p2
.z
},
356 {x
=p2
.x
+PYRA_Wm
,y
=p2
.y
-1,z
=p2
.z
+PYRA_Wm
},
357 {x
=p2
.x
+PYRA_Wm
,y
=p2
.y
-1,z
=p2
.z
},
358 {x
=p2
.x
,y
=p2
.y
-1,z
=p2
.z
+PYRA_Wm
},
361 local opos
= oposses
[o
]
362 local n
= minetest
.get_node_or_nil(opos
)
363 if n
and n
.name
and n
.name
== "air" then
364 local old
= table.copy(p2
)
365 p2
= ground(opos
, p2
)
368 -- Random bonus sinking
369 p2
.y
= math
.max(p2
.y
- math
.random(0,3), PYRA_MIN_Y
)
371 -- Bad luck, we have hit the chunk border!
372 if p2
.y
< minp
.y
then
373 minetest
.log("info", "[tsm_pyramids] Pyramid not placed, sunken too much. p2="..minetest
.pos_to_string(p2
))
377 -- Make sure the pyramid is not near a "killer" node, like water
378 local middle
= vector
.add(p2
, {x
=PYRA_Wh
, y
=0, z
=PYRA_Wh
})
379 if minetest
.find_node_near(p2
, 5, {"default:water_source"}) ~= nil or
380 minetest
.find_node_near(vector
.add(p2
, {x
=PYRA_W
, y
=0, z
=0}), 5, {"default:water_source"}) ~= nil or
381 minetest
.find_node_near(vector
.add(p2
, {x
=0, y
=0, z
=PYRA_W
}), 5, {"default:water_source"}) ~= nil or
382 minetest
.find_node_near(vector
.add(p2
, {x
=PYRA_W
, y
=0, z
=PYRA_W
}), 5, {"default:water_source"}) ~= nil or
384 minetest
.find_node_near(middle
, PYRA_W
, {"default:dirt_with_grass"}) ~= nil or
385 minetest
.find_node_near(middle
, 52, {"default:sandstonebrick", "default:desert_sandstone_brick", "default:desert_stonebrick"}) ~= nil or
386 minetest
.find_node_near(middle
, PYRA_Wh
+ 3, {"default:cactus", "group:leaves", "group:tree"}) ~= nil then
387 minetest
.log("info", "[tsm_pyramids] Pyramid not placed, inappropriate node nearby. p2="..minetest
.pos_to_string(p2
))
391 -- Bonus chance to spawn a sandstone pyramid in v6 desert because otherwise they would be too rare in v6
392 if (mg_name
== "v6" and sand
== "default:desert_sand" and math
.random(1, 2) == 1) then
393 sand
= "default:sand"
396 -- Desert stone pyramids only generate in areas with almost no sand
397 if sand
== "default:desert_stone" then
398 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"})
400 sand
= "default:desert_sand"
404 -- Generate the pyramid!
405 if sand
== "default:desert_sand" then
406 -- Desert sandstone pyramid
407 make(p2
, "default:desert_sandstone_brick", "default:desert_sandstone", "default:desert_stone", "default:desert_sand", "desert_sandstone")
408 elseif sand
== "default:sand" then
410 make(p2
, "default:sandstonebrick", "default:sandstone", "default:sandstone", "default:sand", "sandstone")
412 -- Desert stone pyramid
413 make(p2
, "default:desert_stonebrick", "default:desert_stone_block", "default:desert_stone", "ignore", "desert_stone")
418 -- Add backwards-compability for nodes from the original pyramids mod
419 if minetest
.get_modpath("pyramids") == nil then
421 minetest
.register_alias("pyramids:trap", "tsm_pyramids:trap")
422 minetest
.register_alias("pyramids:trap_2", "tsm_pyramids:trap_2")
423 minetest
.register_alias("pyramids:deco_stone1", "tsm_pyramids:deco_stone1")
424 minetest
.register_alias("pyramids:deco_stone2", "tsm_pyramids:deco_stone2")
425 minetest
.register_alias("pyramids:deco_stone3", "tsm_pyramids:deco_stone3")
426 minetest
.register_alias("pyramids:spawner_mummy", "tsm_pyramids:spawner_mummy")
429 minetest
.register_chatcommand("spawnpyramid", {
430 description
= S("Generate a pyramid"),
431 params
= S("[<room_type>]"),
432 privs
= { server
= true },
433 func
= function(name
, param
)
434 local player
= minetest
.get_player_by_name(name
)
436 return false, S("No player.")
438 local pos
= player
:get_pos()
439 pos
= vector
.round(pos
)
440 local s
= math
.random(1,3)
441 local r
= tonumber(param
)
447 pos
= vector
.add(pos
, {x
=-PYRA_Wh
, y
=-1, z
=0})
450 ok
, msg
= make(pos
, "default:sandstonebrick", "default:sandstone", "default:sandstone", "default:sand", "sandstone", room_id
)
453 ok
, msg
= make(pos
, "default:desert_sandstone_brick", "default:desert_sandstone", "default:desert_stone", "default:desert_sand", "desert_sandstone", room_id
)
456 ok
, msg
= make(pos
, "default:desert_stonebrick", "default:desert_stone_block", "default:desert_stone", "ignore", "desert_stone", room_id
)
459 return true, S("Pyramid generated at @1.", minetest
.pos_to_string(pos
))