From 3008b167b208ea281deb64df623716415ac974b5 Mon Sep 17 00:00:00 2001 From: Treer Date: Tue, 2 Feb 2021 19:15:51 +1100 Subject: [PATCH 1/4] climate_api support - add distance fog Add more appropriate/atmosphere distance fog to the nether, via climate_api mod to avoid conflicting with other mods. Any game or server with climate_api mod installed will be expecting climate_api to take control of sky values. --- depends.txt | 1 + init.lua | 90 +++++++++++++++++++++++++++++++++++++- mapgen.lua | 16 ++++++- mapgen_mantle.lua | 107 ++++++++++++++++++++++++++++++---------------- mod.conf | 2 +- 5 files changed, 174 insertions(+), 42 deletions(-) diff --git a/depends.txt b/depends.txt index 00f426a..2ccaf36 100644 --- a/depends.txt +++ b/depends.txt @@ -6,3 +6,4 @@ fire? loot? mesecons? moreblocks? +climate_api? diff --git a/init.lua b/init.lua index c09234e..7d75ad1 100644 --- a/init.lua +++ b/init.lua @@ -42,12 +42,18 @@ end -- Global Nether namespace nether = {} +nether.mapgen = {} -- Shared Nether mapgen namespace, for mapgen files to expose functions and constants nether.modname = minetest.get_current_modname() nether.path = minetest.get_modpath(nether.modname) nether.get_translator = S -- nether.useBiomes allows other mods to know whether they can register ores etc. in the Nether. -- See mapgen.lua for an explanation of why minetest.read_schematic is being checked nether.useBiomes = minetest.get_mapgen_setting("mg_name") ~= "v6" and minetest.read_schematic ~= nil +nether.fogColor = { -- only used if climate_api is installed + netherCaverns = "#1D0504", -- Distance-fog colour for classic nether + mantle = "#070916", -- Distance-fog colour for the Mantle region + geodes = "#300530" -- Distance-fog colour for secondary region +} -- Settings @@ -227,7 +233,89 @@ The expedition parties have found no diamonds or gold, and after an experienced end }) -end + + + -- Set appropriate nether distance-fog if climate_api is available + -- + -- Delegating to a mod like climate_api means nether won't unexpectedly stomp on the sky of + -- any other mod. + -- Skylayer is another mod which can perform this role, and skylayer support could be added + -- here as well. However skylayer doesn't provide a position-based method of specifying sky + -- colours out-of-the-box, so the nether mod will have to monitor when players enter and + -- leave the nether. + if minetest.get_modpath("climate_api") and minetest.global_exists("climate_api") and climate_api.register_weather ~= nil then + + climate_api.register_influence( + "nether_biome", + function(pos) + local result = "surface" + + if pos.y <= nether.DEPTH_CEILING and pos.y >= nether.DEPTH_FLOOR then + result = "nether" + + if nether.mapgen.getRegion ~= nil then + -- the biomes-based mapgen supports 2 extra regions + local regions = nether.mapgen.RegionEnum + local region = nether.mapgen.getRegion(pos) + if region == regions.Center or region == regions.CenterShell then + result = "mantle" + elseif region == regions.Negative or region == regions.NegativeShell then + result = "geode" + end + end + end + + return result + end + ) + + -- using sky type "plain" unfortunately means we don't get smooth fading transitions when + -- the color of the sky changes, but it seems to be the only way to obtain a sky colour + -- which doesn't brighten during the daytime. + local undergroundSky = { + sky_data = { + base_color = nil, + type = "plain", + textures = nil, + clouds = false, + }, + sun_data = { + visible = false, + sunrise_visible = false + }, + moon_data = { + visible = false + }, + star_data = { + visible = false + } + } + + local netherSky, mantleSky, geodeSky = table.copy(undergroundSky), table.copy(undergroundSky), table.copy(undergroundSky) + netherSky.sky_data.base_color = nether.fogColor.netherCaverns + mantleSky.sky_data.base_color = nether.fogColor.mantle + geodeSky.sky_data.base_color = nether.fogColor.geodes + + climate_api.register_weather( + "nether:nether", + { nether_biome = "nether" }, + { ["climate_api:skybox"] = netherSky } + ) + + climate_api.register_weather( + "nether:mantle", + { nether_biome = "mantle" }, + { ["climate_api:skybox"] = mantleSky } + ) + + climate_api.register_weather( + "nether:geode", + { nether_biome = "geode" }, + { ["climate_api:skybox"] = geodeSky } + ) + end + +end -- end of "if nether.NETHER_REALM_ENABLED..." -- Play bubbling lava sounds if player killed by lava diff --git a/mapgen.lua b/mapgen.lua index 12b50ba..022b6e3 100644 --- a/mapgen.lua +++ b/mapgen.lua @@ -45,7 +45,6 @@ local BASALT_COLUMN_LOWER_LIMIT = CENTER_CAVERN_LIMIT * 0.25 -- This value -- Shared Nether mapgen namespace -- For mapgen files to share functions and constants -nether.mapgen = {} local mapgen = nether.mapgen mapgen.TCAVE = TCAVE -- const needed in mapgen_mantle.lua @@ -257,6 +256,19 @@ mapgen.np_cave = { --flags = "" } +local cavePointPerlin = nil + +mapgen.getCavePointPerlin = function() + cavePointPerlin = cavePointPerlin or minetest.get_perlin(mapgen.np_cave) + return cavePointPerlin +end + +mapgen.getCavePerlinAt = function(pos) + cavePointPerlin = cavePointPerlin or minetest.get_perlin(mapgen.np_cave) + return cavePointPerlin:get3d(pos) +end + + -- Buffers and objects we shouldn't recreate every on_generate local nobj_cave = nil @@ -658,7 +670,7 @@ end -- use knowledge of the nether mapgen algorithm to return a suitable ground level for placing a portal. -- player_name is optional, allowing a player to spawn a remote portal in their own protected areas. function nether.find_nether_ground_y(target_x, target_z, start_y, player_name) - local nobj_cave_point = minetest.get_perlin(mapgen.np_cave) + local nobj_cave_point = mapgen.getCavePointPerlin() local air = 0 -- Consecutive air nodes found local minp_schem, maxp_schem = nether.get_schematic_volume({x = target_x, y = 0, z = target_z}, nil, "nether_portal") diff --git a/mapgen_mantle.lua b/mapgen_mantle.lua index e453e19..aa20187 100644 --- a/mapgen_mantle.lua +++ b/mapgen_mantle.lua @@ -49,7 +49,6 @@ local np_basalt = { local nobj_basalt = nil local nbuf_basalt = {} -local cavePerlin = nil -- Content ids @@ -414,52 +413,84 @@ mapgen.excavate_tunnel_to_center_of_the_nether = function(data, area, nvals_cave end +-- an enumerated list of the different regions in the nether +mapgen.RegionEnum = { + Overworld = "overworld", -- Outside the Nether / none of the regions in the Nether + Positive = "positive", -- The classic nether caverns are here - where cavePerlin > 0.6 + PositiveShell = "positive shell", -- the nether side of the wall/buffer area separating classic nether from the mantle + Center = "center", -- The Mantle caverns are here + CenterShell = "center shell", -- the mantle side of the wall/buffer area separating the positive and negative regions from the center region + Negative = "negative", -- Secondary/spare region - where cavePerlin < -0.6 + NegativeShell = "negative shell", -- the spare region side of the wall/buffer area separating the negative region from the mantle +} + + +-- Returns (region, noise) where region is a value from mapgen.RegionEnum +-- and noise is the unadjusted cave perlin value +mapgen.getRegion = function(pos) + + if pos.y > nether.DEPTH_CEILING or pos.y < nether.DEPTH_FLOOR then + return mapgen.RegionEnum.Overworld, nil + end + + local caveNoise = mapgen.getCavePerlinAt(pos) + local sea_level, cavern_limit_distance = mapgen.find_nearest_lava_sealevel(pos.y) + local tcave_adj, centerRegionLimit_adj = mapgen.get_mapgenblend_adjustments(pos.y) + local tcave = mapgen.TCAVE + tcave_adj + local tmantle = mapgen.CENTER_REGION_LIMIT + centerRegionLimit_adj + local cavern_noise_adj = + mapgen.CENTER_REGION_LIMIT * (cavern_limit_distance * cavern_limit_distance * cavern_limit_distance) - + centerRegionLimit_adj -- cavern_noise_adj gets added to noise value instead of added to the limit np_noise is compared against, so subtract centerRegionLimit_adj so subtract centerRegionLimit_adj instead of adding + + local region + if caveNoise > tcave then + region = mapgen.RegionEnum.Positive + elseif -caveNoise > tcave then + region = mapgen.RegionEnum.Negative + elseif math_abs(caveNoise) < tmantle then + + if math_abs(caveNoise) + cavern_noise_adj < mapgen.CENTER_CAVERN_LIMIT then + region = mapgen.RegionEnum.Center + else + region = mapgen.RegionEnum.CenterShell + end + + elseif caveNoise > 0 then + region = mapgen.RegionEnum.PositiveShell + else + region = mapgen.RegionEnum.NegativeShell + end + + return region, caveNoise +end + + minetest.register_chatcommand("nether_whereami", - { + { description = "Describes which region of the nether the player is in", privs = {debug = true}, func = function(name, param) local player = minetest.get_player_by_name(name) if player == nil then return false, "Unknown player position" end + local playerPos = vector.round(player:get_pos()) - local pos = vector.round(player:get_pos()) - if pos.y > nether.DEPTH_CEILING or pos.y < nether.DEPTH_FLOOR then - return true, "The Overworld" - end + local region, caveNoise = mapgen.getRegion(playerPos) + local seaLevel, cavernLimitDistance = mapgen.find_nearest_lava_sealevel(playerPos.y) + local tcave_adj, centerRegionLimit_adj = mapgen.get_mapgenblend_adjustments(playerPos.y) - cavePerlin = cavePerlin or minetest.get_perlin(mapgen.np_cave) - local densityNoise = cavePerlin:get_3d(pos) - local sea_level, cavern_limit_distance = mapgen.find_nearest_lava_sealevel(pos.y) - local tcave_adj, centerRegionLimit_adj = mapgen.get_mapgenblend_adjustments(pos.y) - local tcave = mapgen.TCAVE + tcave_adj - local tmantle = mapgen.CENTER_REGION_LIMIT + centerRegionLimit_adj - local cavern_noise_adj = - mapgen.CENTER_REGION_LIMIT * (cavern_limit_distance * cavern_limit_distance * cavern_limit_distance) - - centerRegionLimit_adj -- cavern_noise_adj gets added to noise value instead of added to the limit np_noise is compared against, so subtract centerRegionLimit_adj so subtract centerRegionLimit_adj instead of adding + local regionLabels = { + [mapgen.RegionEnum.Overworld] = "The Overworld", + [mapgen.RegionEnum.Positive] = "Positive nether", + [mapgen.RegionEnum.PositiveShell] = "Shell between positive nether and center region", + [mapgen.RegionEnum.Center] = "Center/Mantle, inside cavern", + [mapgen.RegionEnum.CenterShell] = "Center/Mantle, but outside the caverns", + [mapgen.RegionEnum.Negative] = "Negative nether", + [mapgen.RegionEnum.NegativeShell] = "Shell between negative nether and center region" + } + local desc = regionLabels[region] - local desc - - if densityNoise > tcave then - desc = "Positive nether" - elseif -densityNoise > tcave then - desc = "Negative nether" - elseif math_abs(densityNoise) < tmantle then - desc = "Mantle" - - if math_abs(densityNoise) + cavern_noise_adj < mapgen.CENTER_CAVERN_LIMIT then - desc = desc .. " inside cavern" - else - desc = desc .. " but outside cavern" - end - - elseif densityNoise > 0 then - desc = "Shell between positive nether and center region" - else - desc = "Shell between negative nether and center region" - end - - local sea_pos = pos.y - sea_level + local sea_pos = playerPos.y - seaLevel if sea_pos > 0 then desc = desc .. ", " .. sea_pos .. "m above lava-sea level" else @@ -470,7 +501,7 @@ minetest.register_chatcommand("nether_whereami", desc = desc .. ", approaching y boundary of Nether" end - return true, "[Perlin " .. (math_floor(densityNoise * 1000) / 1000) .. "] " .. desc + return true, "[Perlin " .. (math_floor(caveNoise * 1000) / 1000) .. "] " .. desc end } ) diff --git a/mod.conf b/mod.conf index 5b6c73a..84a7a01 100644 --- a/mod.conf +++ b/mod.conf @@ -1,4 +1,4 @@ name = nether description = Adds a deep underground realm with different mapgen that you can reach with obsidian portals. depends = stairs, default -optional_depends = moreblocks, mesecons, loot, dungeon_loot, doc_basics, fire +optional_depends = moreblocks, mesecons, loot, dungeon_loot, doc_basics, fire, climate_api From 87f0ea7a51ae7baeefddb51a7a8e29c47643485e Mon Sep 17 00:00:00 2001 From: Treer Date: Sun, 7 Feb 2021 23:18:19 +1100 Subject: [PATCH 2/4] Implement PR feedback --- .luacheckrc | 2 ++ init.lua | 6 ++-- locale/nether.fr.tr | 18 +++++++++- locale/template.txt | 18 +++++++++- mapgen.lua | 5 ++- mapgen_mantle.lua | 87 ++++++++++++++++++++++++--------------------- 6 files changed, 90 insertions(+), 46 deletions(-) diff --git a/.luacheckrc b/.luacheckrc index 99f4685..29d0418 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -2,10 +2,12 @@ unused_args = false allow_defined_top = true globals = { + "bucket", "nether" } read_globals = { + "climate_api", "core", "default", "DIR_DELIM", diff --git a/init.lua b/init.lua index 7d75ad1..8d7880b 100644 --- a/init.lua +++ b/init.lua @@ -253,13 +253,15 @@ The expedition parties have found no diamonds or gold, and after an experienced if pos.y <= nether.DEPTH_CEILING and pos.y >= nether.DEPTH_FLOOR then result = "nether" + -- since mapgen_nobiomes.lua has no regions it doesn't implement getRegion(), + -- so only use getRegion() if it exists if nether.mapgen.getRegion ~= nil then -- the biomes-based mapgen supports 2 extra regions local regions = nether.mapgen.RegionEnum local region = nether.mapgen.getRegion(pos) - if region == regions.Center or region == regions.CenterShell then + if region == regions.CENTER or region == regions.CENTERSHELL then result = "mantle" - elseif region == regions.Negative or region == regions.NegativeShell then + elseif region == regions.NEGATIVE or region == regions.NEGATIVESHELL then result = "geode" end end diff --git a/locale/nether.fr.tr b/locale/nether.fr.tr index 48201d8..f274f9b 100644 --- a/locale/nether.fr.tr +++ b/locale/nether.fr.tr @@ -13,9 +13,25 @@ Construction requires 14 blocks of obsidian, which we found deep underground whe Nether Portal=Portail du Nether +### mapgen_mantle.lua ### + +, @1m above lava-sea level= +, @1m below lava-sea level= +, approaching y boundary of Nether= +@1@2@3@4= +Center/Mantle, but outside the caverns= +Center/Mantle, inside cavern= +Describes which region of the nether the player is in= +Negative nether= +Positive nether= +Shell between negative nether and center region= +Shell between positive nether and center region= +The Overworld= +Unknown player position= +[Perlin @1] = + ### nodes.lua ### -= A finely finished block of solid Nether Basalt.= A rough cut solid block of Nether Basalt.= A thin crust of cooled lava with liquid lava beneath= diff --git a/locale/template.txt b/locale/template.txt index 0dfcf32..ef69c76 100644 --- a/locale/template.txt +++ b/locale/template.txt @@ -12,9 +12,25 @@ Construction requires 14 blocks of obsidian, which we found deep underground whe Nether Portal= +### mapgen_mantle.lua ### + +, @1m above lava-sea level= +, @1m below lava-sea level= +, approaching y boundary of Nether= +@1@2@3@4= +Center/Mantle, but outside the caverns= +Center/Mantle, inside cavern= +Describes which region of the nether the player is in= +Negative nether= +Positive nether= +Shell between negative nether and center region= +Shell between positive nether and center region= +The Overworld= +Unknown player position= +[Perlin @1] = + ### nodes.lua ### -= A finely finished block of solid Nether Basalt.= A rough cut solid block of Nether Basalt.= A thin crust of cooled lava with liquid lava beneath= diff --git a/mapgen.lua b/mapgen.lua index 022b6e3..364cc79 100644 --- a/mapgen.lua +++ b/mapgen.lua @@ -557,9 +557,12 @@ local function on_generated(minp, maxp, seed) local tcave_adj, centerRegionLimit_adj = mapgen.get_mapgenblend_adjustments(y) local tcave = TCAVE + tcave_adj local tmantle = CENTER_REGION_LIMIT + centerRegionLimit_adj -- cavern_noise_adj already contains central_region_limit_adj, so tmantle is only for comparisons when cavern_noise_adj hasn't been added to the noise value + + -- cavern_noise_adj gets added to noise value instead of added to the limit np_noise + -- is compared against, so subtract centerRegionLimit_adj instead of adding local cavern_noise_adj = CENTER_REGION_LIMIT * (cavern_limit_distance * cavern_limit_distance * cavern_limit_distance) - - centerRegionLimit_adj -- cavern_noise_adj gets added to noise value instead of added to the limit np_noise is compared against, so subtract centerRegionLimit_adj instead of adding + centerRegionLimit_adj for z = z0, z1 do local vi = area:index(x0, y, z) -- Initial voxelmanip index diff --git a/mapgen_mantle.lua b/mapgen_mantle.lua index aa20187..9d13016 100644 --- a/mapgen_mantle.lua +++ b/mapgen_mantle.lua @@ -27,6 +27,7 @@ local debugf = nether.debug local mapgen = nether.mapgen +local S = nether.get_translator local BASALT_COLUMN_UPPER_LIMIT = mapgen.BASALT_COLUMN_UPPER_LIMIT local BASALT_COLUMN_LOWER_LIMIT = mapgen.BASALT_COLUMN_LOWER_LIMIT @@ -118,7 +119,7 @@ mapgen.add_basalt_columns = function(data, area, minp, maxp) local yStride = area.ystride local yCaveStride = x1 - x0 + 1 - cavePerlin = cavePerlin or minetest.get_perlin(mapgen.np_cave) + local cavePerlin = mapgen.getCavePointPerlin() nobj_basalt = nobj_basalt or minetest.get_perlin_map(np_basalt, {x = yCaveStride, y = yCaveStride}) local nvals_basalt = nobj_basalt:get_2d_map_flat({x=minp.x, y=minp.z}, {x=yCaveStride, y=yCaveStride}, nbuf_basalt) @@ -398,11 +399,14 @@ mapgen.excavate_tunnel_to_center_of_the_nether = function(data, area, nvals_cave if lowest < mapgen.CENTER_CAVERN_LIMIT and highest > mapgen.TCAVE + 0.03 then local mantle_y = area:position(lowest_vi).y - local sealevel, cavern_limit_distance = mapgen.find_nearest_lava_sealevel(mantle_y) + local _, cavern_limit_distance = mapgen.find_nearest_lava_sealevel(mantle_y) local _, centerRegionLimit_adj = mapgen.get_mapgenblend_adjustments(mantle_y) + + -- cavern_noise_adj gets added to noise value instead of added to the limit np_noise + -- is compared against, so subtract centerRegionLimit_adj instead of adding local cavern_noise_adj = mapgen.CENTER_REGION_LIMIT * (cavern_limit_distance * cavern_limit_distance * cavern_limit_distance) - - centerRegionLimit_adj -- cavern_noise_adj gets added to noise value instead of added to the limit np_noise is compared against, so subtract centerRegionLimit_adj instead of adding + centerRegionLimit_adj if lowest + cavern_noise_adj < mapgen.CENTER_CAVERN_LIMIT then excavate_pathway(data, area, area:position(highest_vi), area:position(lowest_vi), minp, maxp) @@ -415,13 +419,13 @@ end -- an enumerated list of the different regions in the nether mapgen.RegionEnum = { - Overworld = "overworld", -- Outside the Nether / none of the regions in the Nether - Positive = "positive", -- The classic nether caverns are here - where cavePerlin > 0.6 - PositiveShell = "positive shell", -- the nether side of the wall/buffer area separating classic nether from the mantle - Center = "center", -- The Mantle caverns are here - CenterShell = "center shell", -- the mantle side of the wall/buffer area separating the positive and negative regions from the center region - Negative = "negative", -- Secondary/spare region - where cavePerlin < -0.6 - NegativeShell = "negative shell", -- the spare region side of the wall/buffer area separating the negative region from the mantle + OVERWORLD = {name = "overworld", desc = S("The Overworld") }, -- Outside the Nether / none of the regions in the Nether + POSITIVE = {name = "positive", desc = S("Positive nether") }, -- The classic nether caverns are here - where cavePerlin > 0.6 + POSITIVESHELL = {name = "positive shell", desc = S("Shell between positive nether and center region") }, -- the nether side of the wall/buffer area separating classic nether from the mantle + CENTER = {name = "center", desc = S("Center/Mantle, inside cavern") }, + CENTERSHELL = {name = "center shell", desc = S("Center/Mantle, but outside the caverns") }, -- the mantle side of the wall/buffer area separating the positive and negative regions from the center region + NEGATIVE = {name = "negative", desc = S("Negative nether") }, -- Secondary/spare region - where cavePerlin < -0.6 + NEGATIVESHELL = {name = "negative shell", desc = S("Shell between negative nether and center region") } -- the spare region side of the wall/buffer area separating the negative region from the mantle } @@ -430,35 +434,38 @@ mapgen.RegionEnum = { mapgen.getRegion = function(pos) if pos.y > nether.DEPTH_CEILING or pos.y < nether.DEPTH_FLOOR then - return mapgen.RegionEnum.Overworld, nil + return mapgen.RegionEnum.OVERWORLD, nil end local caveNoise = mapgen.getCavePerlinAt(pos) - local sea_level, cavern_limit_distance = mapgen.find_nearest_lava_sealevel(pos.y) + local sealevel, cavern_limit_distance = mapgen.find_nearest_lava_sealevel(pos.y) local tcave_adj, centerRegionLimit_adj = mapgen.get_mapgenblend_adjustments(pos.y) local tcave = mapgen.TCAVE + tcave_adj local tmantle = mapgen.CENTER_REGION_LIMIT + centerRegionLimit_adj + + -- cavern_noise_adj gets added to noise value instead of added to the limit np_noise + -- is compared against, so subtract centerRegionLimit_adj instead of adding local cavern_noise_adj = mapgen.CENTER_REGION_LIMIT * (cavern_limit_distance * cavern_limit_distance * cavern_limit_distance) - - centerRegionLimit_adj -- cavern_noise_adj gets added to noise value instead of added to the limit np_noise is compared against, so subtract centerRegionLimit_adj so subtract centerRegionLimit_adj instead of adding + centerRegionLimit_adj local region if caveNoise > tcave then - region = mapgen.RegionEnum.Positive + region = mapgen.RegionEnum.POSITIVE elseif -caveNoise > tcave then - region = mapgen.RegionEnum.Negative + region = mapgen.RegionEnum.NEGATIVE elseif math_abs(caveNoise) < tmantle then if math_abs(caveNoise) + cavern_noise_adj < mapgen.CENTER_CAVERN_LIMIT then - region = mapgen.RegionEnum.Center + region = mapgen.RegionEnum.CENTER else - region = mapgen.RegionEnum.CenterShell + region = mapgen.RegionEnum.CENTERSHELL end elseif caveNoise > 0 then - region = mapgen.RegionEnum.PositiveShell + region = mapgen.RegionEnum.POSITIVESHELL else - region = mapgen.RegionEnum.NegativeShell + region = mapgen.RegionEnum.NEGATIVESHELL end return region, caveNoise @@ -467,41 +474,39 @@ end minetest.register_chatcommand("nether_whereami", { - description = "Describes which region of the nether the player is in", + description = S("Describes which region of the nether the player is in"), privs = {debug = true}, func = function(name, param) local player = minetest.get_player_by_name(name) - if player == nil then return false, "Unknown player position" end + if player == nil then return false, S("Unknown player position") end local playerPos = vector.round(player:get_pos()) local region, caveNoise = mapgen.getRegion(playerPos) local seaLevel, cavernLimitDistance = mapgen.find_nearest_lava_sealevel(playerPos.y) local tcave_adj, centerRegionLimit_adj = mapgen.get_mapgenblend_adjustments(playerPos.y) - local regionLabels = { - [mapgen.RegionEnum.Overworld] = "The Overworld", - [mapgen.RegionEnum.Positive] = "Positive nether", - [mapgen.RegionEnum.PositiveShell] = "Shell between positive nether and center region", - [mapgen.RegionEnum.Center] = "Center/Mantle, inside cavern", - [mapgen.RegionEnum.CenterShell] = "Center/Mantle, but outside the caverns", - [mapgen.RegionEnum.Negative] = "Negative nether", - [mapgen.RegionEnum.NegativeShell] = "Shell between negative nether and center region" - } - local desc = regionLabels[region] + local seaDesc = "" + local boundaryDesc = "" + local perlinDesc = "" - local sea_pos = playerPos.y - seaLevel - if sea_pos > 0 then - desc = desc .. ", " .. sea_pos .. "m above lava-sea level" - else - desc = desc .. ", " .. sea_pos .. "m below lava-sea level" + if region ~= mapgen.RegionEnum.OVERWORLD then + + local seaPos = playerPos.y - seaLevel + if seaPos > 0 then + seaDesc = S(", @1m above lava-sea level", seaPos) + else + seaDesc = S(", @1m below lava-sea level", seaPos) + end + + if tcave_adj > 0 then + boundaryDesc = S(", approaching y boundary of Nether") + end + + perlinDesc = S("[Perlin @1] ", (math_floor(caveNoise * 1000) / 1000)) end - if tcave_adj > 0 then - desc = desc .. ", approaching y boundary of Nether" - end - - return true, "[Perlin " .. (math_floor(caveNoise * 1000) / 1000) .. "] " .. desc + return true, S("@1@2@3@4", perlinDesc, region.desc, seaDesc, boundaryDesc) end } ) From b30272d2dd6b3b319b19a381bf463f965126ba9b Mon Sep 17 00:00:00 2001 From: Treer Date: Sat, 6 Feb 2021 19:49:44 +1100 Subject: [PATCH 3/4] Move dungeon excavation functions to separate file --- mapgen.lua | 218 ++-------------------------------------- mapgen_dungeons.lua | 238 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 247 insertions(+), 209 deletions(-) create mode 100644 mapgen_dungeons.lua diff --git a/mapgen.lua b/mapgen.lua index 364cc79..85d5b81 100644 --- a/mapgen.lua +++ b/mapgen.lua @@ -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) \ No newline at end of file diff --git a/mapgen_dungeons.lua b/mapgen_dungeons.lua new file mode 100644 index 0000000..ca99ae2 --- /dev/null +++ b/mapgen_dungeons.lua @@ -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 From bafa4e8ea166fd01c3725ff436a252f3d29c0d42 Mon Sep 17 00:00:00 2001 From: Treer Date: Sat, 6 Feb 2021 21:53:42 +1100 Subject: [PATCH 4/4] Improve dungeons * clears netherrack from dungeon stairways * Adds pillars or mezzanine floors to large rooms * slightly more careful with window placement * glowstone sometimes added behind in windows --- mapgen.lua | 2 +- mapgen_dungeons.lua | 113 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 105 insertions(+), 10 deletions(-) diff --git a/mapgen.lua b/mapgen.lua index 85d5b81..3c41625 100644 --- a/mapgen.lua +++ b/mapgen.lua @@ -68,7 +68,7 @@ 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 mapgen helper functions for specialized areas of the mapgen +-- Load specialty helper functions dofile(nether.path .. "/mapgen_dungeons.lua") dofile(nether.path .. "/mapgen_mantle.lua") diff --git a/mapgen_dungeons.lua b/mapgen_dungeons.lua index ca99ae2..64a73af 100644 --- a/mapgen_dungeons.lua +++ b/mapgen_dungeons.lua @@ -2,8 +2,7 @@ Nether mod for minetest - Helper functions for excavating and decorating dungeons, in a - separate file to keep the size of mapgen.lua manageable. + All the Dungeon related functions used by the biomes-based mapgen are here. Copyright (C) 2021 Treer @@ -39,8 +38,10 @@ 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_glowstone_deep = minetest.get_content_id("nether:glowstone_deep") 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 @@ -54,6 +55,7 @@ function is_dungeon_brick(node_id) end + nether.mapgen.build_dungeon_room_list = function(data, area) local result = {} @@ -168,6 +170,21 @@ nether.mapgen.excavate_dungeons = function(data, area, rooms) end end end + + -- clear netherrack from dungeon stairways + if #rooms > 0 then + local stairPositions = minetest.find_nodes_in_area(area.MinEdge, area.MaxEdge, minetest.registered_biomes["nether_caverns"].node_dungeon_stair) + for _, stairPos in ipairs(stairPositions) do + vi = area:indexp(stairPos) + for i = 1, 4 do + if stairPos.y + i > area.MaxEdge.y then break end + vi = vi + area.ystride + node_id = data[vi] + -- searching forward of the stairs could also be done + if node_id == c_netherrack or node_id == c_netherrack_deep then data[vi] = c_air end + end + end + end end -- Since we already know where all the rooms and their walls are, and have all the nodes stored @@ -185,6 +202,8 @@ nether.mapgen.decorate_dungeons = function(data, area, rooms) 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) + local roomWidth = room_max.x - room_min.x + 1 + local roomLength = room_max.z - room_min.z + 1 if room_seed % 3 == 0 and room_max.y < maxEdge.y then -- Glowstone chandelier (feel free to replace with a fancy schematic) @@ -206,29 +225,105 @@ nether.mapgen.decorate_dungeons = function(data, area, rooms) end -- Barred windows - if room_seed % 7 < 5 and room_max.x - room_min.x >= 4 and room_max.z - room_min.z >= 4 + if room_seed % 7 < 5 and roomWidth >= 5 and roomLength >= 5 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 + -- Can't use glass panes because they need the param data set. -- 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 + --if c_netherglass ~= nil and room_seed % 20 >= 12 then window_node = c_crystallight end + + local function placeWindow(vi, viOutsideOffset, windowNo) + if is_dungeon_brick(data[vi]) and is_dungeon_brick(data[vi + yStride]) then + data[vi] = window_node + + if room_seed % 19 == windowNo then + -- place a glowstone light behind the window + local node_id = data[vi + viOutsideOffset] + if node_id == c_netherrack then + data[vi + viOutsideOffset] = c_glowstone + elseif node_id == c_netherrack_deep then + data[vi + viOutsideOffset] = c_glowstone_deep + end + end + end + end 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 + for i, offset in ipairs(locations) do + placeWindow(vi_min + offset, -1, i) + placeWindow(vi_max + offset, 1, i + #locations) 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 + for i, offset in ipairs(locations) do + placeWindow(vi_min + offset, -zStride, i + #locations * 2) + placeWindow(vi_max + offset, zStride, i + #locations * 3) + end + end + + -- pillars or mezzanine floor + if room_seed % 43 > 10 and roomWidth >= 6 and roomLength >= 6 then + + local pillar_vi = {} + local pillarHeight = 0 + local wallDist = 1 + math.floor((roomWidth + roomLength) / 14) + + local roomHeight = room_max.y - room_min.y + if roomHeight >= 7 then + -- mezzanine floor + local mezzMax = { + x = room_min.x + math.floor(roomWidth / 7 * 4), + y = room_min.y + math.floor(roomHeight / 5 * 3), + z = room_max.z + } + + pillarHeight = mezzMax.y - room_min.y - 1 + pillar_vi = { + area:index(mezzMax.x, room_min.y, room_min.z + wallDist), + area:index(mezzMax.x, room_min.y, room_max.z - wallDist), + } + + if is_dungeon_brick(data[pillar_vi[1] - yStride]) and is_dungeon_brick(data[pillar_vi[2] - yStride]) then + -- The floor of the dungeon looks like it exists (i.e. not erased by nether + -- cavern), so add the mezzanine floor + for z = 0, roomLength - 1 do + local vi = area:index(room_min.x, mezzMax.y, room_min.z + z) + for x = room_min.x, mezzMax.x do + if data[vi] == c_air then data[vi] = c_dungeonbrick end + vi = vi + 1 + end + end + end + + elseif roomHeight >= 4 then + -- 4 pillars + pillarHeight = roomHeight + pillar_vi = { + area:index(room_min.x + wallDist, room_min.y, room_min.z + wallDist), + area:index(room_min.x + wallDist, room_min.y, room_max.z - wallDist), + area:index(room_max.x - wallDist, room_min.y, room_min.z + wallDist), + area:index(room_max.x - wallDist, room_min.y, room_max.z - wallDist) + } + end + + for i = #pillar_vi, 1, -1 do + if not is_dungeon_brick(data[pillar_vi[i] - yStride]) then + -- there's no dungeon floor under this pillar so skip it, it's probably been cut away by nether cavern. + table.remove(pillar_vi, i) + end + end + for y = 0, pillarHeight do + for _, vi in ipairs(pillar_vi) do + if data[vi + y * yStride] == c_air then data[vi + y * yStride] = c_dungeonbrick end + end end end