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},
30 if minetest
.get_modpath("farming") then
31 table.insert(chest_stuff
, {name
="farming:bread", max = 3})
32 table.insert(chest_stuff
, {name
="farming:cotton", max = 8})
34 table.insert(chest_stuff
, {name
="farming:apple", max = 8})
35 table.insert(chest_stuff
, {name
="farming:apple", max = 3})
37 if minetest
.get_modpath("tnt") then
38 table.insert(chest_stuff
, {name
="tnt:gunpowder", max = 6})
40 table.insert(chest_stuff
, {name
="farming:apple", max = 3})
43 function tsm_pyramids
.fill_chest(pos
, stype
, flood_sand
, treasure_chance
)
44 local sand
= "default:sand"
45 if not treasure_chance
then
48 if stype
== "desert_sandstone" or stype
== "desert_stone" then
49 sand
= "default:desert_sand"
51 local n
= minetest
.get_node(pos
)
52 local treasure_added
= false
53 if n
and n
.name
and n
.name
== "default:chest" then
54 local meta
= minetest
.get_meta(pos
)
55 local inv
= meta
:get_inventory()
56 inv
:set_size("main", 8*4)
58 -- Fill with sand in sand-flooded pyramids
60 table.insert(stacks
, {name
=sand
, count
= math
.random(1,32)})
63 if math
.random(1,100) <= treasure_chance
then
64 if minetest
.get_modpath("treasurer") ~= nil then
65 stacks
= treasurer
.select_random_treasures(3,7,9,{"minetool", "food", "crafting_component"})
68 local stuff
= chest_stuff
[math
.random(1,#chest_stuff
)]
69 table.insert(stacks
, {name
=stuff
.name
, count
= math
.random(1,stuff
.max)})
75 if not inv
:contains_item("main", stacks
[s
]) then
76 inv
:set_stack("main", math
.random(1,32), stacks
[s
])
83 local function add_spawner(pos
, mummy_offset
)
84 minetest
.set_node(pos
, {name
="tsm_pyramids:spawner_mummy"})
85 if not minetest
.settings
:get_bool("only_peaceful_mobs") then
87 tsm_pyramids
.attempt_mummy_spawn(pos
, false)
92 local function can_replace(pos
)
93 local n
= minetest
.get_node_or_nil(pos
)
94 if n
and n
.name
and minetest
.registered_nodes
[n
.name
] and not minetest
.registered_nodes
[n
.name
].walkable
then
103 local function make_foundation_part(pos
, set_to_stone
)
107 while can_replace(p2
)==true do
112 table.insert(set_to_stone
, table.copy(p2
))
117 local function make_entrance(pos
, rot
, brick
, sand
, flood_sand
)
118 local roffset_arr
= {
119 { x
=0, y
=0, z
=1 }, -- front
120 { x
=-1, y
=0, z
=0 }, -- left
121 { x
=0, y
=0, z
=-1 }, -- back
122 { x
=1, y
=0, z
=0 }, -- right
124 local roffset
= roffset_arr
[rot
+ 1]
127 way
= vector
.add(pos
, {x
=PYRA_Wh
, y
=0, z
=0})
129 way
= vector
.add(pos
, {x
=PYRA_Wm
, y
=0, z
=PYRA_Wh
})
131 way
= vector
.add(pos
, {x
=PYRA_Wh
, y
=0, z
=PYRA_Wm
})
133 way
= vector
.add(pos
, {x
=0, y
=0, z
=PYRA_Wh
})
135 local max_sand_height
= math
.random(1,3)
137 local sand_height
= math
.random(1,max_sand_height
)
140 local way_dir
= vector
.add(vector
.add(way
, {x
=0,y
=iy
,z
=0}), vector
.multiply(roffset
, ie
))
141 if flood_sand
and sand
~= "ignore" and iy
<= sand_height
and ie
>= 3 then
142 minetest
.set_node(way_dir
, {name
=sand
})
144 minetest
.remove_node(way_dir
)
146 -- build decoration above entrance
147 if ie
== 3 and iy
== 3 then
148 local deco
= {x
=way_dir
.x
, y
=way_dir
.y
+1,z
=way_dir
.z
}
149 minetest
.set_node(deco
, {name
=brick
})
150 if rot
== 0 or rot
== 2 then
151 minetest
.set_node(vector
.add(deco
, {x
=-1, y
=0, z
=0}), {name
=brick
})
152 minetest
.set_node(vector
.add(deco
, {x
=1, y
=0, z
=0}), {name
=brick
})
154 minetest
.set_node(vector
.add(deco
, {x
=0, y
=0, z
=-1}), {name
=brick
})
155 minetest
.set_node(vector
.add(deco
, {x
=0, y
=0, z
=1}), {name
=brick
})
162 local function make_pyramid(pos
, brick
, sandstone
, stone
, sand
)
163 local set_to_brick
= {}
164 local set_to_stone
= {}
166 for iy
=0,math
.random(10,PYRA_Wh
),1 do
167 for ix
=iy
,PYRA_W
-1-iy
,1 do
168 for iz
=iy
,PYRA_W
-1-iy
,1 do
170 make_foundation_part({x
=pos
.x
+ix
,y
=pos
.y
,z
=pos
.z
+iz
}, set_to_stone
)
172 table.insert(set_to_brick
, {x
=pos
.x
+ix
,y
=pos
.y
+iy
,z
=pos
.z
+iz
})
176 minetest
.bulk_set_node(set_to_stone
, {name
=stone
})
177 minetest
.bulk_set_node(set_to_brick
, {name
=brick
})
180 local function make(pos
, brick
, sandstone
, stone
, sand
, ptype
, room_id
)
181 local bpos
= table.copy(pos
)
183 make_pyramid(bpos
, brick
, sandstone
, stone
, sand
)
185 local rot
= math
.random(0, 3)
187 local ok
, msg
, flood_sand
= tsm_pyramids
.make_room(bpos
, ptype
, room_id
, rot
)
188 -- Place mummy spawner
189 local r
= math
.random(1,3)
190 -- 4 possible spawner positions
191 local spawner_posses
= {
193 {{x
=bpos
.x
+PYRA_Wh
,y
=bpos
.y
+2, z
=bpos
.z
+5}, {x
=0, y
=0, z
=2}},
195 {{x
=bpos
.x
+PYRA_Wm
-5,y
=bpos
.y
+2, z
=bpos
.z
+PYRA_Wh
}, {x
=-2, y
=0, z
=0}},
197 {{x
=bpos
.x
+PYRA_Wh
,y
=bpos
.y
+2, z
=bpos
.z
+PYRA_W
-5}, {x
=0, y
=0, z
=-2}},
199 {{x
=bpos
.x
+5,y
=bpos
.y
+2, z
=bpos
.z
+PYRA_Wh
}, {x
=2, y
=0, z
=0}},
201 -- Delete the spawner position in which the entrance will be placed
202 table.remove(spawner_posses
, (rot
% 4) + 1)
203 add_spawner(spawner_posses
[r
][1], spawner_posses
[r
][2])
205 make_entrance(bpos
, rot
, brick
, sand
, flood_sand
)
207 minetest
.log("action", "[tsm_pyramids] Created pyramid at "..minetest
.pos_to_string(bpos
)..".")
211 local perl1
= {SEED1
= 9130, OCTA1
= 3, PERS1
= 0.5, SCAL1
= 250} -- Values should match minetest mapgen V6 desert noise.
212 local perlin1
-- perlin noise buffer
214 local function hlp_fnct(pos
, name
)
215 local n
= minetest
.get_node_or_nil(pos
)
216 if n
and n
.name
and n
.name
== name
then
222 local function ground(pos
, old
)
223 local p2
= table.copy(pos
)
224 while hlp_fnct(p2
, "air") do
228 return {x
=old
.x
, y
=p2
.y
, z
=old
.z
}
234 -- Attempt to generate a pyramid in the generated area.
235 -- Up to one pyramid per mapchunk.
236 minetest
.register_on_generated(function(minp
, maxp
, seed
)
237 if maxp
.y
< PYRA_MIN_Y
then return end
239 -- TODO: Use Minetests pseudo-random tools
240 math
.randomseed(seed
)
243 perlin1
= minetest
.get_perlin(perl1
.SEED1
, perl1
.OCTA1
, perl1
.PERS1
, perl1
.SCAL1
)
245 --[[ Make sure the pyramid doesn't bleed outside of maxp,
246 so it doesn't get placed incompletely by the mapgen.
247 This creates a bias somewhat, as this means there are some coordinates in
248 which pyramids cannot spawn. But it's still better to have broken pyramids.
250 local limit
= function(pos
, maxp
)
251 pos
.x
= math
.min(pos
.x
, maxp
.x
- PYRA_W
+1)
252 pos
.y
= math
.min(pos
.y
, maxp
.y
- PYRA_Wh
)
253 pos
.z
= math
.min(pos
.z
, maxp
.z
- PYRA_W
+1)
256 local noise1
= perlin1
:get_2d({x
=minp
.x
,y
=minp
.y
})
258 if noise1
> 0.25 or noise1
< -0.26 then
259 -- Need a bit of luck to place a pyramid
260 if math
.random(0,10) > 7 then
261 minetest
.log("verbose", "[tsm_pyramids] Pyramid not placed, bad dice roll. minp="..minetest
.pos_to_string(minp
))
265 local mpos
= {x
=math
.random(minp
.x
,maxp
.x
), y
=math
.random(minp
.y
,maxp
.y
), z
=math
.random(minp
.z
,maxp
.z
)}
267 local sands
= {"default:sand", "default:desert_sand", "default:desert_stone"}
272 local sand_cnt_max
= 0
273 local sand_cnt_max_id
274 -- Look for sand or desert stone to place the pyramid on
279 psand
[s
] = minetest
.find_node_near(mpos
, 25, sand
)
282 mpos
= {x
=math
.random(minp
.x
,maxp
.x
), y
=math
.random(minp
.y
,maxp
.y
), z
=math
.random(minp
.z
,maxp
.z
)}
283 local spos
= minetest
.find_node_near(mpos
, 25, sand
)
285 sand_cnt
= sand_cnt
+ 1
286 if psand
[s
] == nil then
290 if sand_cnt
> sand_cnt_max
then
291 sand_cnt_max
= sand_cnt
298 minetest
.log("verbose", "[tsm_pyramids] Pyramid not placed, no suitable surface. minp="..minetest
.pos_to_string(minp
))
301 -- Select the material type by the most prominent node type
302 -- E.g. if desert sand is most prominent, we place a desert sandstone pyramid
303 if sand_cnt_max_id
then
304 sand
= sands
[sand_cnt_max_id
]
306 if p2
.y
< PYRA_MIN_Y
then
307 minetest
.log("info", "[tsm_pyramids] Pyramid not placed, too deep. p2="..minetest
.pos_to_string(p2
))
310 -- Now sink the pyramid until each corner of it is no longer floating in mid-air
313 {x
=p2
.x
,y
=p2
.y
-1,z
=p2
.z
},
314 {x
=p2
.x
+PYRA_Wm
,y
=p2
.y
-1,z
=p2
.z
+PYRA_Wm
},
315 {x
=p2
.x
+PYRA_Wm
,y
=p2
.y
-1,z
=p2
.z
},
316 {x
=p2
.x
,y
=p2
.y
-1,z
=p2
.z
+PYRA_Wm
},
319 local opos
= oposses
[o
]
320 local n
= minetest
.get_node_or_nil(opos
)
321 if n
and n
.name
and n
.name
== "air" then
322 local old
= table.copy(p2
)
323 p2
= ground(opos
, p2
)
326 -- Random bonus sinking
327 p2
.y
= math
.max(p2
.y
- math
.random(0,3), PYRA_MIN_Y
)
329 -- Bad luck, we have hit the chunk border!
330 if p2
.y
< minp
.y
then
331 minetest
.log("info", "[tsm_pyramids] Pyramid not placed, sunken too much. p2="..minetest
.pos_to_string(p2
))
335 -- Make sure the pyramid is not near a "killer" node, like water
336 local middle
= vector
.add(p2
, {x
=PYRA_Wh
, y
=0, z
=PYRA_Wh
})
337 if minetest
.find_node_near(p2
, 5, {"default:water_source"}) ~= nil or
338 minetest
.find_node_near(vector
.add(p2
, {x
=PYRA_W
, y
=0, z
=0}), 5, {"default:water_source"}) ~= nil or
339 minetest
.find_node_near(vector
.add(p2
, {x
=0, y
=0, z
=PYRA_W
}), 5, {"default:water_source"}) ~= nil or
340 minetest
.find_node_near(vector
.add(p2
, {x
=PYRA_W
, y
=0, z
=PYRA_W
}), 5, {"default:water_source"}) ~= nil or
342 minetest
.find_node_near(middle
, PYRA_W
, {"default:dirt_with_grass"}) ~= nil or
343 minetest
.find_node_near(middle
, 52, {"default:sandstonebrick", "default:desert_sandstone_brick", "default:desert_stonebrick"}) ~= nil or
344 minetest
.find_node_near(middle
, PYRA_Wh
+ 3, {"default:cactus", "group:leaves", "group:tree"}) ~= nil then
345 minetest
.log("info", "[tsm_pyramids] Pyramid not placed, inappropriate node nearby. p2="..minetest
.pos_to_string(p2
))
349 -- Bonus chance to spawn a sandstone pyramid in v6 desert because otherwise they would be too rare in v6
350 if (mg_name
== "v6" and sand
== "default:desert_sand" and math
.random(1, 2) == 1) then
351 sand
= "default:sand"
354 -- Desert stone pyramids only generate in areas with almost no sand
355 if sand
== "default:desert_stone" then
356 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"})
358 sand
= "default:desert_sand"
362 -- Generate the pyramid!
363 if sand
== "default:desert_sand" then
364 -- Desert sandstone pyramid
365 make(p2
, "default:desert_sandstone_brick", "default:desert_sandstone", "default:desert_stone", "default:desert_sand", "desert_sandstone")
366 elseif sand
== "default:sand" then
368 make(p2
, "default:sandstonebrick", "default:sandstone", "default:sandstone", "default:sand", "sandstone")
370 -- Desert stone pyramid
371 make(p2
, "default:desert_stonebrick", "default:desert_stone_block", "default:desert_stone", "ignore", "desert_stone")
376 -- Add backwards-compability for nodes from the original pyramids mod
377 if minetest
.get_modpath("pyramids") == nil then
379 minetest
.register_alias("pyramids:trap", "tsm_pyramids:trap")
380 minetest
.register_alias("pyramids:trap_2", "tsm_pyramids:trap_2")
381 minetest
.register_alias("pyramids:deco_stone1", "tsm_pyramids:deco_stone1")
382 minetest
.register_alias("pyramids:deco_stone2", "tsm_pyramids:deco_stone2")
383 minetest
.register_alias("pyramids:deco_stone3", "tsm_pyramids:deco_stone3")
384 minetest
.register_alias("pyramids:spawner_mummy", "tsm_pyramids:spawner_mummy")
387 minetest
.register_chatcommand("spawnpyramid", {
388 description
= S("Generate a pyramid"),
389 params
= S("[<room_type>]"),
390 privs
= { server
= true },
391 func
= function(name
, param
)
392 local player
= minetest
.get_player_by_name(name
)
394 return false, S("No player.")
396 local pos
= player
:get_pos()
397 pos
= vector
.round(pos
)
398 local s
= math
.random(1,3)
399 local r
= tonumber(param
)
405 pos
= vector
.add(pos
, {x
=-PYRA_Wh
, y
=-1, z
=0})
408 ok
, msg
= make(pos
, "default:sandstonebrick", "default:sandstone", "default:sandstone", "default:sand", "sandstone", room_id
)
411 ok
, msg
= make(pos
, "default:desert_sandstone_brick", "default:desert_sandstone", "default:desert_stone", "default:desert_sand", "desert_sandstone", room_id
)
414 ok
, msg
= make(pos
, "default:desert_stonebrick", "default:desert_stone_block", "default:desert_stone", "ignore", "desert_stone", room_id
)
417 return true, S("Pyramid generated at @1.", minetest
.pos_to_string(pos
))