Move dungeon excavation functions to separate file

This commit is contained in:
Treer 2021-02-06 19:49:44 +11:00
parent 87f0ea7a51
commit b30272d2dd
2 changed files with 247 additions and 209 deletions

View File

@ -68,7 +68,8 @@ if minetest.read_schematic == nil then
error("This " .. nether.modname .. " mapgen requires Minetest v5.1 or greater, use mapgen_nobiomes.lua instead.", 0)
end
-- Load helper functions for generating the mantle / center region
-- Load mapgen helper functions for specialized areas of the mapgen
dofile(nether.path .. "/mapgen_dungeons.lua")
dofile(nether.path .. "/mapgen_mantle.lua")
@ -281,206 +282,11 @@ local dbuf = {}
local c_air = minetest.get_content_id("air")
local c_netherrack = minetest.get_content_id("nether:rack")
local c_netherrack_deep = minetest.get_content_id("nether:rack_deep")
local c_dungeonbrick = minetest.get_content_id("nether:brick")
local c_dungeonbrick_alt = minetest.get_content_id("nether:brick_cracked")
local c_netherbrick_slab = minetest.get_content_id("stairs:slab_nether_brick")
local c_netherfence = minetest.get_content_id("nether:fence_nether_brick")
local c_glowstone = minetest.get_content_id("nether:glowstone")
local c_lava_source = minetest.get_content_id("default:lava_source")
local c_lavasea_source = minetest.get_content_id("nether:lava_source") -- same as lava but with staggered animation to look better as an ocean
local c_lava_crust = minetest.get_content_id("nether:lava_crust")
local c_native_mapgen = minetest.get_content_id("nether:native_mapgen")
-- Dungeon excavation functions
function is_dungeon_brick(node_id)
return node_id == c_dungeonbrick or node_id == c_dungeonbrick_alt
end
function build_dungeon_room_list(data, area)
local result = {}
-- Unfortunately gennotify only returns dungeon rooms, not corridors.
-- We don't need to check for temples because only dungeons are generated in biomes
-- that define their own dungeon nodes.
local gennotify = minetest.get_mapgen_object("gennotify")
local roomLocations = gennotify["dungeon"] or {}
-- Excavation should still know to stop if a cave or corridor has removed the dungeon wall.
-- See MapgenBasic::generateDungeons in mapgen.cpp for max room sizes.
local maxRoomSize = 18
local maxRoomRadius = math.ceil(maxRoomSize / 2)
local xStride, yStride, zStride = 1, area.ystride, area.zstride
local minEdge, maxEdge = area.MinEdge, area.MaxEdge
for _, roomPos in ipairs(roomLocations) do
if area:containsp(roomPos) then -- this safety check does not appear to be necessary, but lets make it explicit
local room_vi = area:indexp(roomPos)
--data[room_vi] = minetest.get_content_id("default:torch") -- debug
local startPos = vector.new(roomPos)
if roomPos.y + 1 <= maxEdge.y and data[room_vi + yStride] == c_air then
-- The roomPos coords given by gennotify are at floor level, but whenever possible we
-- want to be performing searches a node higher than floor level to avoids dungeon chests.
startPos.y = startPos.y + 1
room_vi = area:indexp(startPos)
end
local bound_min_x = math_max(minEdge.x, roomPos.x - maxRoomRadius)
local bound_min_y = math_max(minEdge.y, roomPos.y - 1) -- room coords given by gennotify are on the floor
local bound_min_z = math_max(minEdge.z, roomPos.z - maxRoomRadius)
local bound_max_x = math_min(maxEdge.x, roomPos.x + maxRoomRadius)
local bound_max_y = math_min(maxEdge.y, roomPos.y + maxRoomSize) -- room coords given by gennotify are on the floor
local bound_max_z = math_min(maxEdge.z, roomPos.z + maxRoomRadius)
local room_min = vector.new(startPos)
local room_max = vector.new(startPos)
local vi = room_vi
while room_max.y < bound_max_y and data[vi + yStride] == c_air do
room_max.y = room_max.y + 1
vi = vi + yStride
end
vi = room_vi
while room_min.y > bound_min_y and data[vi - yStride] == c_air do
room_min.y = room_min.y - 1
vi = vi - yStride
end
vi = room_vi
while room_max.z < bound_max_z and data[vi + zStride] == c_air do
room_max.z = room_max.z + 1
vi = vi + zStride
end
vi = room_vi
while room_min.z > bound_min_z and data[vi - zStride] == c_air do
room_min.z = room_min.z - 1
vi = vi - zStride
end
vi = room_vi
while room_max.x < bound_max_x and data[vi + xStride] == c_air do
room_max.x = room_max.x + 1
vi = vi + xStride
end
vi = room_vi
while room_min.x > bound_min_x and data[vi - xStride] == c_air do
room_min.x = room_min.x - 1
vi = vi - xStride
end
local roomInfo = vector.new(roomPos)
roomInfo.minp = room_min
roomInfo.maxp = room_max
result[#result + 1] = roomInfo
end
end
return result;
end
-- Only partially excavates dungeons, the rest is left as an exercise for the player ;)
-- (Corridors and the parts of rooms which extend beyond the emerge boundary will remain filled)
function excavate_dungeons(data, area, rooms)
local vi, node_id
-- any air from the native mapgen has been replaced by netherrack, but
-- we don't want this inside dungeons, so fill dungeon rooms with air
for _, roomInfo in ipairs(rooms) do
local room_min = roomInfo.minp
local room_max = roomInfo.maxp
for z = room_min.z, room_max.z do
for y = room_min.y, room_max.y do
vi = area:index(room_min.x, y, z)
for x = room_min.x, room_max.x do
node_id = data[vi]
if node_id == c_netherrack or node_id == c_netherrack_deep then data[vi] = c_air end
vi = vi + 1
end
end
end
end
end
-- Since we already know where all the rooms and their walls are, and have all the nodes stored
-- in a voxelmanip already, we may as well add a little Nether flair to the dungeons found here.
function decorate_dungeons(data, area, rooms)
local xStride, yStride, zStride = 1, area.ystride, area.zstride
local minEdge, maxEdge = area.MinEdge, area.MaxEdge
for _, roomInfo in ipairs(rooms) do
local room_min, room_max = roomInfo.minp, roomInfo.maxp
local room_size = vector.distance(room_min, room_max)
if room_size > 10 then
local room_seed = roomInfo.x + 3 * roomInfo.z + 13 * roomInfo.y
local window_y = roomInfo.y + math_min(2, room_max.y - roomInfo.y - 1)
if room_seed % 3 == 0 and room_max.y < maxEdge.y then
-- Glowstone chandelier (feel free to replace with a fancy schematic)
local vi = area:index(roomInfo.x, room_max.y + 1, roomInfo.z)
if is_dungeon_brick(data[vi]) then data[vi] = c_glowstone end
elseif room_seed % 4 == 0 and room_min.y > minEdge.y
and room_min.x > minEdge.x and room_max.x < maxEdge.x
and room_min.z > minEdge.z and room_max.z < maxEdge.z then
-- lava well (feel free to replace with a fancy schematic)
local vi = area:index(roomInfo.x, room_min.y, roomInfo.z)
if is_dungeon_brick(data[vi - yStride]) then
data[vi - yStride] = c_lava_source
if data[vi - zStride] == c_air then data[vi - zStride] = c_netherbrick_slab end
if data[vi + zStride] == c_air then data[vi + zStride] = c_netherbrick_slab end
if data[vi - xStride] == c_air then data[vi - xStride] = c_netherbrick_slab end
if data[vi + xStride] == c_air then data[vi + xStride] = c_netherbrick_slab end
end
end
-- Barred windows
if room_seed % 7 < 5 and room_max.x - room_min.x >= 4 and room_max.z - room_min.z >= 4
and window_y >= minEdge.y and window_y + 1 <= maxEdge.y
and room_min.x > minEdge.x and room_max.x < maxEdge.x
and room_min.z > minEdge.z and room_max.z < maxEdge.z then
--data[area:indexp(roomInfo)] = minetest.get_content_id("default:mese_post_light") -- debug
-- Until whisper glass is added, every window will be made of netherbrick fence (rather
-- than material depending on room_seed)
local window_node = c_netherfence
local vi_min = area:index(room_min.x - 1, window_y, roomInfo.z)
local vi_max = area:index(room_max.x + 1, window_y, roomInfo.z)
local locations = {-zStride, zStride, -zStride + yStride, zStride + yStride}
for _, offset in ipairs(locations) do
if is_dungeon_brick(data[vi_min + offset]) then data[vi_min + offset] = window_node end
if is_dungeon_brick(data[vi_max + offset]) then data[vi_max + offset] = window_node end
end
vi_min = area:index(roomInfo.x, window_y, room_min.z - 1)
vi_max = area:index(roomInfo.x, window_y, room_max.z + 1)
locations = {-xStride, xStride, -xStride + yStride, xStride + yStride}
for _, offset in ipairs(locations) do
if is_dungeon_brick(data[vi_min + offset]) then data[vi_min + offset] = window_node end
if is_dungeon_brick(data[vi_max + offset]) then data[vi_max + offset] = window_node end
end
end
-- Weeds on the floor once Nether weeds are added
end
end
end
local yblmin = NETHER_FLOOR + BLEND * 2
local yblmax = NETHER_CEILING - BLEND * 2
@ -539,18 +345,17 @@ local function on_generated(minp, maxp, seed)
nobj_cave = nobj_cave or minetest.get_perlin_map(mapgen.np_cave, chulens)
local nvals_cave = nobj_cave:get_3d_map_flat(minp, nbuf_cave)
local dungeonRooms = build_dungeon_room_list(data, area)
local dungeonRooms = mapgen.build_dungeon_room_list(data, area) -- function from mapgen_dungeons.lua
local abs_cave_noise, abs_cave_noise_adjusted
local contains_nether = false
local contains_shell = false
local contains_mantle = false
local contains_ocean = false
local contains_ocean = false
for y = y0, y1 do -- Y loop first to minimise tcave & lava-sea calculations
local sea_level, cavern_limit_distance = mapgen.find_nearest_lava_sealevel(y) -- function provided by mapgen_mantle.lua
local sea_level, cavern_limit_distance = mapgen.find_nearest_lava_sealevel(y) -- function from mapgen_mantle.lua
local above_lavasea = y > sea_level
local below_lavasea = y < sea_level
@ -609,7 +414,6 @@ local function on_generated(minp, maxp, seed)
else
-- the shell seperating the mantle from the rest of the nether...
data[vi] = c_netherrack -- excavate_dungeons() will mostly reverse this inside dungeons
contains_shell = true
end
end
@ -633,12 +437,12 @@ local function on_generated(minp, maxp, seed)
end
if contains_mantle or contains_ocean then
mapgen.add_basalt_columns(data, area, minp, maxp) -- function provided by mapgen_mantle.lua
mapgen.add_basalt_columns(data, area, minp, maxp) -- function from mapgen_mantle.lua
end
if contains_nether and contains_mantle then
tunnelCandidate_count = tunnelCandidate_count + 1
local success = mapgen.excavate_tunnel_to_center_of_the_nether(data, area, nvals_cave, minp, maxp) -- function provided by mapgen_mantle.lua
local success = mapgen.excavate_tunnel_to_center_of_the_nether(data, area, nvals_cave, minp, maxp) -- function from mapgen_mantle.lua
if success then tunnel_count = tunnel_count + 1 end
end
total_chunk_count = total_chunk_count + 1
@ -655,8 +459,8 @@ local function on_generated(minp, maxp, seed)
-- any air from the native mapgen has been replaced by netherrack, but we
-- don't want netherrack inside dungeons, so fill known dungeon rooms with air.
excavate_dungeons(data, area, dungeonRooms)
decorate_dungeons(data, area, dungeonRooms)
mapgen.excavate_dungeons(data, area, dungeonRooms) -- function from mapgen_dungeons.lua
mapgen.decorate_dungeons(data, area, dungeonRooms) -- function from mapgen_dungeons.lua
vm:set_data(data)
@ -705,8 +509,4 @@ function nether.find_nether_ground_y(target_x, target_z, start_y, player_name)
return math_max(start_y, NETHER_FLOOR + BLEND) -- Fallback
end
-- We don't need to be gen-notified of temples because only dungeons will be generated
-- if a biome defines the dungeon nodes
minetest.set_gen_notify({dungeon = true})
minetest.register_on_generated(on_generated)

238
mapgen_dungeons.lua Normal file
View File

@ -0,0 +1,238 @@
--[[
Nether mod for minetest
Helper functions for excavating and decorating dungeons, in a
separate file to keep the size of mapgen.lua manageable.
Copyright (C) 2021 Treer
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.
]]--
-- We don't need to be gen-notified of temples because only dungeons will be generated
-- if a biome defines the dungeon nodes
minetest.set_gen_notify({dungeon = true})
-- Content ids
local c_air = minetest.get_content_id("air")
local c_netherrack = minetest.get_content_id("nether:rack")
local c_netherrack_deep = minetest.get_content_id("nether:rack_deep")
local c_dungeonbrick = minetest.get_content_id("nether:brick")
local c_dungeonbrick_alt = minetest.get_content_id("nether:brick_cracked")
local c_netherbrick_slab = minetest.get_content_id("stairs:slab_nether_brick")
local c_netherfence = minetest.get_content_id("nether:fence_nether_brick")
local c_glowstone = minetest.get_content_id("nether:glowstone")
local c_lava_source = minetest.get_content_id("default:lava_source")
-- Misc math functions
-- avoid needing table lookups each time a common math function is invoked
local math_max, math_min = math.max, math.min
-- Dungeon excavation functions
function is_dungeon_brick(node_id)
return node_id == c_dungeonbrick or node_id == c_dungeonbrick_alt
end
nether.mapgen.build_dungeon_room_list = function(data, area)
local result = {}
-- Unfortunately gennotify only returns dungeon rooms, not corridors.
-- We don't need to check for temples because only dungeons are generated in biomes
-- that define their own dungeon nodes.
local gennotify = minetest.get_mapgen_object("gennotify")
local roomLocations = gennotify["dungeon"] or {}
-- Excavation should still know to stop if a cave or corridor has removed the dungeon wall.
-- See MapgenBasic::generateDungeons in mapgen.cpp for max room sizes.
local maxRoomSize = 18
local maxRoomRadius = math.ceil(maxRoomSize / 2)
local xStride, yStride, zStride = 1, area.ystride, area.zstride
local minEdge, maxEdge = area.MinEdge, area.MaxEdge
for _, roomPos in ipairs(roomLocations) do
if area:containsp(roomPos) then -- this safety check does not appear to be necessary, but lets make it explicit
local room_vi = area:indexp(roomPos)
--data[room_vi] = minetest.get_content_id("default:torch") -- debug
local startPos = vector.new(roomPos)
if roomPos.y + 1 <= maxEdge.y and data[room_vi + yStride] == c_air then
-- The roomPos coords given by gennotify are at floor level, but whenever possible we
-- want to be performing searches a node higher than floor level to avoids dungeon chests.
startPos.y = startPos.y + 1
room_vi = area:indexp(startPos)
end
local bound_min_x = math_max(minEdge.x, roomPos.x - maxRoomRadius)
local bound_min_y = math_max(minEdge.y, roomPos.y - 1) -- room coords given by gennotify are on the floor
local bound_min_z = math_max(minEdge.z, roomPos.z - maxRoomRadius)
local bound_max_x = math_min(maxEdge.x, roomPos.x + maxRoomRadius)
local bound_max_y = math_min(maxEdge.y, roomPos.y + maxRoomSize) -- room coords given by gennotify are on the floor
local bound_max_z = math_min(maxEdge.z, roomPos.z + maxRoomRadius)
local room_min = vector.new(startPos)
local room_max = vector.new(startPos)
local vi = room_vi
while room_max.y < bound_max_y and data[vi + yStride] == c_air do
room_max.y = room_max.y + 1
vi = vi + yStride
end
vi = room_vi
while room_min.y > bound_min_y and data[vi - yStride] == c_air do
room_min.y = room_min.y - 1
vi = vi - yStride
end
vi = room_vi
while room_max.z < bound_max_z and data[vi + zStride] == c_air do
room_max.z = room_max.z + 1
vi = vi + zStride
end
vi = room_vi
while room_min.z > bound_min_z and data[vi - zStride] == c_air do
room_min.z = room_min.z - 1
vi = vi - zStride
end
vi = room_vi
while room_max.x < bound_max_x and data[vi + xStride] == c_air do
room_max.x = room_max.x + 1
vi = vi + xStride
end
vi = room_vi
while room_min.x > bound_min_x and data[vi - xStride] == c_air do
room_min.x = room_min.x - 1
vi = vi - xStride
end
local roomInfo = vector.new(roomPos)
roomInfo.minp = room_min
roomInfo.maxp = room_max
result[#result + 1] = roomInfo
end
end
return result;
end
-- Only partially excavates dungeons, the rest is left as an exercise for the player ;)
-- (Corridors and the parts of rooms which extend beyond the emerge boundary will remain filled)
nether.mapgen.excavate_dungeons = function(data, area, rooms)
local vi, node_id
-- any air from the native mapgen has been replaced by netherrack, but
-- we don't want this inside dungeons, so fill dungeon rooms with air
for _, roomInfo in ipairs(rooms) do
local room_min = roomInfo.minp
local room_max = roomInfo.maxp
for z = room_min.z, room_max.z do
for y = room_min.y, room_max.y do
vi = area:index(room_min.x, y, z)
for x = room_min.x, room_max.x do
node_id = data[vi]
if node_id == c_netherrack or node_id == c_netherrack_deep then data[vi] = c_air end
vi = vi + 1
end
end
end
end
end
-- Since we already know where all the rooms and their walls are, and have all the nodes stored
-- in a voxelmanip already, we may as well add a little Nether flair to the dungeons found here.
nether.mapgen.decorate_dungeons = function(data, area, rooms)
local xStride, yStride, zStride = 1, area.ystride, area.zstride
local minEdge, maxEdge = area.MinEdge, area.MaxEdge
for _, roomInfo in ipairs(rooms) do
local room_min, room_max = roomInfo.minp, roomInfo.maxp
local room_size = vector.distance(room_min, room_max)
if room_size > 10 then
local room_seed = roomInfo.x + 3 * roomInfo.z + 13 * roomInfo.y
local window_y = roomInfo.y + math_min(2, room_max.y - roomInfo.y - 1)
if room_seed % 3 == 0 and room_max.y < maxEdge.y then
-- Glowstone chandelier (feel free to replace with a fancy schematic)
local vi = area:index(roomInfo.x, room_max.y + 1, roomInfo.z)
if is_dungeon_brick(data[vi]) then data[vi] = c_glowstone end
elseif room_seed % 4 == 0 and room_min.y > minEdge.y
and room_min.x > minEdge.x and room_max.x < maxEdge.x
and room_min.z > minEdge.z and room_max.z < maxEdge.z then
-- lava well (feel free to replace with a fancy schematic)
local vi = area:index(roomInfo.x, room_min.y, roomInfo.z)
if is_dungeon_brick(data[vi - yStride]) then
data[vi - yStride] = c_lava_source
if data[vi - zStride] == c_air then data[vi - zStride] = c_netherbrick_slab end
if data[vi + zStride] == c_air then data[vi + zStride] = c_netherbrick_slab end
if data[vi - xStride] == c_air then data[vi - xStride] = c_netherbrick_slab end
if data[vi + xStride] == c_air then data[vi + xStride] = c_netherbrick_slab end
end
end
-- Barred windows
if room_seed % 7 < 5 and room_max.x - room_min.x >= 4 and room_max.z - room_min.z >= 4
and window_y >= minEdge.y and window_y + 1 <= maxEdge.y
and room_min.x > minEdge.x and room_max.x < maxEdge.x
and room_min.z > minEdge.z and room_max.z < maxEdge.z then
--data[area:indexp(roomInfo)] = minetest.get_content_id("default:mese_post_light") -- debug
-- Until whisper glass is added, every window will be made of netherbrick fence (rather
-- than material depending on room_seed)
local window_node = c_netherfence
local vi_min = area:index(room_min.x - 1, window_y, roomInfo.z)
local vi_max = area:index(room_max.x + 1, window_y, roomInfo.z)
local locations = {-zStride, zStride, -zStride + yStride, zStride + yStride}
for _, offset in ipairs(locations) do
if is_dungeon_brick(data[vi_min + offset]) then data[vi_min + offset] = window_node end
if is_dungeon_brick(data[vi_max + offset]) then data[vi_max + offset] = window_node end
end
vi_min = area:index(roomInfo.x, window_y, room_min.z - 1)
vi_max = area:index(roomInfo.x, window_y, room_max.z + 1)
locations = {-xStride, xStride, -xStride + yStride, xStride + yStride}
for _, offset in ipairs(locations) do
if is_dungeon_brick(data[vi_min + offset]) then data[vi_min + offset] = window_node end
if is_dungeon_brick(data[vi_max + offset]) then data[vi_max + offset] = window_node end
end
end
-- Weeds on the floor once Nether weeds are added
end
end
end