mirror of
https://github.com/luanti-org/minetest_game.git
synced 2025-04-30 13:11:41 -04:00
Move dungeon room scanning to mapgen thread
This commit is contained in:
parent
2ce8ff3306
commit
8b8ad1106c
2 changed files with 117 additions and 78 deletions
|
@ -14,62 +14,6 @@ local function random_sample(rand, list, count)
|
||||||
return ret
|
return ret
|
||||||
end
|
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 -- 9 = max room size / 2
|
|
||||||
local pos = vector.add(cpos, {x=dir.x*i, y=0, z=dir.z*i})
|
|
||||||
|
|
||||||
-- continue in that direction until we find a wall-like node
|
|
||||||
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})
|
|
||||||
|
|
||||||
-- check that it:
|
|
||||||
--- is at least 2 nodes high (not a staircase)
|
|
||||||
--- has a floor
|
|
||||||
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
|
|
||||||
-- abort even if it wasn't a wall cause something is in the way
|
|
||||||
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 function populate_chest(pos, rand, dungeontype)
|
||||||
--minetest.chat_send_all("chest placed at " .. minetest.pos_to_string(pos) .. " [" .. dungeontype .. "]")
|
--minetest.chat_send_all("chest placed at " .. minetest.pos_to_string(pos) .. " [" .. dungeontype .. "]")
|
||||||
--minetest.add_node(vector.add(pos, {x=0, y=1, z=0}), {name="default:torch", param2=1})
|
--minetest.add_node(vector.add(pos, {x=0, y=1, z=0}), {name="default:torch", param2=1})
|
||||||
|
@ -121,30 +65,13 @@ local function populate_chest(pos, rand, dungeontype)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function place_chests(candidates, blockseed)
|
||||||
minetest.register_on_generated(function(minp, maxp, blockseed)
|
if #candidates == 0 then
|
||||||
local gennotify = minetest.get_mapgen_object("gennotify")
|
return
|
||||||
local poslist = gennotify["dungeon"] or {}
|
|
||||||
local n_dungeons = #poslist
|
|
||||||
-- Add MGv6 desert temples to the list too
|
|
||||||
for _, entry in ipairs(gennotify["temple"] or {}) do
|
|
||||||
table.insert(poslist, entry)
|
|
||||||
end
|
end
|
||||||
if #poslist == 0 then return end
|
|
||||||
|
|
||||||
local noise = minetest.get_perlin(10115, 4, 0.5, 1)
|
local noise = minetest.get_perlin(10115, 4, 0.5, 1)
|
||||||
local rand = PcgRandom(noise3d_integer(noise, poslist[1]))
|
local rand = PcgRandom(blockseed)
|
||||||
|
|
||||||
local candidates = {}
|
|
||||||
-- process at most 8 rooms to keep runtime of this predictable
|
|
||||||
local num_process = math.min(#poslist, 8)
|
|
||||||
for i = 1, num_process do
|
|
||||||
local room = find_walls(poslist[i], i > n_dungeons)
|
|
||||||
-- skip small rooms and everything that doesn't at least have 3 walls
|
|
||||||
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)
|
local num_chests = rand:next(dungeon_loot.CHESTS_MIN, dungeon_loot.CHESTS_MAX)
|
||||||
num_chests = math.min(#candidates, num_chests)
|
num_chests = math.min(#candidates, num_chests)
|
||||||
|
@ -170,4 +97,24 @@ minetest.register_on_generated(function(minp, maxp, blockseed)
|
||||||
populate_chest(chestpos, PcgRandom(noise3d_integer(noise, chestpos)), room.type)
|
populate_chest(chestpos, PcgRandom(noise3d_integer(noise, chestpos)), room.type)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end
|
||||||
|
|
||||||
|
if minetest.register_mapgen_script then
|
||||||
|
-- dungeon rooms are scanned in mapgen thread
|
||||||
|
minetest.set_gen_notify({custom=true}, nil, {"dungeon_loot:candidates"})
|
||||||
|
minetest.register_mapgen_script(minetest.get_modpath("dungeon_loot") .. "/scan.lua")
|
||||||
|
minetest.register_on_generated(function(minp, maxp, blockseed)
|
||||||
|
local gennotify = minetest.get_mapgen_object("gennotify")
|
||||||
|
local candidates = gennotify.custom["dungeon_loot:candidates"]
|
||||||
|
assert(type(candidates) == "table", "candidates must exist")
|
||||||
|
place_chests(candidates, blockseed)
|
||||||
|
end)
|
||||||
|
else
|
||||||
|
-- dungeon rooms are scanned in callback here
|
||||||
|
dofile(minetest.get_modpath("dungeon_loot") .. "/scan.lua")
|
||||||
|
minetest.register_on_generated(function(minp, maxp, blockseed)
|
||||||
|
-- process at most 8 rooms to keep runtime of this predictable
|
||||||
|
local candidates = dungeon_loot._internal_find_rooms(8)
|
||||||
|
place_chests(candidates, blockseed)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
92
mods/dungeon_loot/scan.lua
Normal file
92
mods/dungeon_loot/scan.lua
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
if minetest.save_gen_notify then
|
||||||
|
dungeon_loot = {}
|
||||||
|
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 -- 9 = max room size / 2
|
||||||
|
local pos = vector.add(cpos, {x=dir.x*i, y=0, z=dir.z*i})
|
||||||
|
|
||||||
|
-- continue in that direction until we find a wall-like node
|
||||||
|
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})
|
||||||
|
|
||||||
|
-- check that it:
|
||||||
|
--- is at least 2 nodes high (not a staircase)
|
||||||
|
--- has a floor
|
||||||
|
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
|
||||||
|
-- abort even if it wasn't a wall cause something is in the way
|
||||||
|
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
|
||||||
|
|
||||||
|
function dungeon_loot._internal_find_rooms(max_rooms)
|
||||||
|
local gennotify = minetest.get_mapgen_object("gennotify")
|
||||||
|
local poslist = gennotify["dungeon"] or {}
|
||||||
|
local n_dungeons = #poslist
|
||||||
|
-- Add MGv6 desert temples to the list too
|
||||||
|
for _, entry in ipairs(gennotify["temple"] or {}) do
|
||||||
|
table.insert(poslist, entry)
|
||||||
|
end
|
||||||
|
if #poslist == 0 then
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
local candidates = {}
|
||||||
|
local num_process = math.min(#poslist, max_rooms)
|
||||||
|
for i = 1, num_process do
|
||||||
|
local room = find_walls(poslist[i], i > n_dungeons)
|
||||||
|
-- skip small rooms and everything that doesn't at least have 3 walls
|
||||||
|
if math.min(room.size.x, room.size.z) >= 4 and #room.walls >= 3 then
|
||||||
|
table.insert(candidates, room)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return candidates
|
||||||
|
end
|
||||||
|
|
||||||
|
-- if loaded inside mapgen env, do our job there and export the result
|
||||||
|
if minetest.save_gen_notify then
|
||||||
|
minetest.register_on_generated(function(vmanip, minp, maxp, blockseed)
|
||||||
|
local candidates = dungeon_loot._internal_find_rooms(9999)
|
||||||
|
minetest.save_gen_notify("dungeon_loot:candidates", candidates)
|
||||||
|
end)
|
||||||
|
end
|
Loading…
Add table
Reference in a new issue