123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 |
- minetest.set_gen_notify({dungeon = true, temple = true})
- local function noise3d_integer(noise, pos)
- return math.abs(math.floor(noise:get_3d(pos) * 0x7fffffff))
- end
- local function random_sample(rand, list, count)
- local ret = {}
- for n = 1, count do
- local idx = rand:next(1, #list)
- table.insert(ret, list[idx])
- table.remove(list, idx)
- end
- return ret
- end
- local function find_walls(cpos, is_temple)
- local is_wall = function(node)
- return node.name ~= "air" and node.name ~= "ignore"
- end
- local dirs = {{x=1, z=0}, {x=-1, z=0}, {x=0, z=1}, {x=0, z=-1}}
- local get_node = minetest.get_node
- local ret = {}
- local mindist = {x=0, z=0}
- local min = function(a, b) return a ~= 0 and math.min(a, b) or b end
- for _, dir in ipairs(dirs) do
- for i = 1, 9 do
- local pos = vector.add(cpos, {x=dir.x*i, y=0, z=dir.z*i})
-
- local node = get_node(pos)
- if is_wall(node) then
- local front_below = vector.subtract(pos, {x=dir.x, y=1, z=dir.z})
- local above = vector.add(pos, {x=0, y=1, z=0})
-
-
-
- if is_wall(get_node(front_below)) and is_wall(get_node(above)) then
- table.insert(ret, {pos = pos, facing = {x=-dir.x, y=0, z=-dir.z}})
- if dir.z == 0 then
- mindist.x = min(mindist.x, i-1)
- else
- mindist.z = min(mindist.z, i-1)
- end
- end
-
- break
- end
- end
- end
- local biome = minetest.get_biome_data(cpos)
- biome = biome and minetest.get_biome_name(biome.biome) or ""
- local type = "normal"
- if is_temple or biome:find("desert") == 1 then
- type = "desert"
- elseif biome:find("sandstone_desert") == 1 then
- type = "sandstone"
- elseif biome:find("icesheet") == 1 then
- type = "ice"
- end
- return {
- walls = ret,
- size = {x=mindist.x*2, z=mindist.z*2},
- type = type,
- }
- end
- local function populate_chest(pos, rand, dungeontype)
-
-
- local item_list = dungeon_loot._internal_get_loot(pos.y, dungeontype)
-
- local sample_n = math.min(#item_list, dungeon_loot.STACKS_PER_CHEST_MAX)
- item_list = random_sample(rand, item_list, sample_n)
-
- local items = {}
- for _, loot in ipairs(item_list) do
- if rand:next(0, 1000) / 1000 <= loot.chance then
- local itemdef = minetest.registered_items[loot.name]
- local amount = 1
- if loot.count ~= nil then
- amount = rand:next(loot.count[1], loot.count[2])
- end
- if not itemdef then
- minetest.log("warning", "Registered loot item " .. loot.name .. " does not exist")
- elseif itemdef.tool_capabilities then
- for n = 1, amount do
- local wear = rand:next(0.20 * 65535, 0.75 * 65535)
- table.insert(items, ItemStack({name = loot.name, wear = wear}))
- end
- elseif itemdef.stack_max == 1 then
-
- for n = 1, amount do
- table.insert(items, loot.name)
- end
- else
- table.insert(items, ItemStack({name = loot.name, count = amount}))
- end
- end
- end
-
- local inv = minetest.get_meta(pos):get_inventory()
- local listsz = inv:get_size("main")
- assert(listsz >= #items)
- for _, item in ipairs(items) do
- local index = rand:next(1, listsz)
- if inv:get_stack("main", index):is_empty() then
- inv:set_stack("main", index, item)
- else
- inv:add_item("main", item)
- end
- end
- end
- minetest.register_on_generated(function(minp, maxp, blockseed)
- local gennotify = minetest.get_mapgen_object("gennotify")
- local poslist = gennotify["dungeon"] or {}
- local n_dungeons = #poslist
-
- for _, entry in ipairs(gennotify["temple"] or {}) do
- table.insert(poslist, entry)
- end
- if #poslist == 0 then return end
- local noise = minetest.get_perlin(10115, 4, 0.5, 1)
- local rand = PcgRandom(noise3d_integer(noise, poslist[1]))
- local candidates = {}
-
- local num_process = math.min(#poslist, 8)
- for i = 1, num_process do
- local room = find_walls(poslist[i], i > n_dungeons)
-
- if math.min(room.size.x, room.size.z) >= 4 and #room.walls >= 3 then
- table.insert(candidates, room)
- end
- end
- local num_chests = rand:next(dungeon_loot.CHESTS_MIN, dungeon_loot.CHESTS_MAX)
- num_chests = math.min(#candidates, num_chests)
- local rooms = random_sample(rand, candidates, num_chests)
- for _, room in ipairs(rooms) do
-
- local wall = room.walls[rand:next(1, #room.walls)]
- local v, vi
- if wall.facing.x ~= 0 then
- v, vi = {x=0, y=0, z=1}, "z"
- else
- v, vi = {x=1, y=0, z=0}, "x"
- end
- local chestpos = vector.add(wall.pos, wall.facing)
- local off = rand:next(-room.size[vi]/2 + 1, room.size[vi]/2 - 1)
- chestpos = vector.add(chestpos, vector.multiply(v, off))
- if minetest.get_node(chestpos).name == "air" then
-
- local facedir = minetest.dir_to_facedir(vector.multiply(wall.facing, -1))
- minetest.add_node(chestpos, {name = "default:chest", param2 = facedir})
- populate_chest(chestpos, PcgRandom(noise3d_integer(noise, chestpos)), room.type)
- end
- end
- end)
|