Replaces lavaland generation (Cellular automata caves) with a more sophisticated cave network system (#96026)

This commit is contained in:
CabinetOnFire
2026-06-06 15:36:14 +00:00
committed by GitHub
parent f78a72d5bd
commit cfd1c8bb67
9 changed files with 101 additions and 24 deletions
@@ -1,13 +1,13 @@
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
"a" = (
/turf/closed/mineral/volcanic/lava_land_surface,
/turf/closed/mineral/volcanic/lava_land_surface/biome_replace,
/area/lavaland/surface/outdoors)
"b" = (
/turf/open/misc/asteroid/basalt/lava_land_surface,
/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace,
/area/lavaland/surface/outdoors)
"c" = (
/obj/effect/decal/remains/human,
/turf/open/misc/asteroid/basalt/lava_land_surface,
/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace,
/area/lavaland/surface/outdoors)
"d" = (
/turf/closed/indestructible/riveted,
@@ -15,7 +15,7 @@
"e" = (
/obj/machinery/wish_granter,
/obj/effect/mapping_helpers/no_lava,
/turf/open/misc/asteroid/basalt/lava_land_surface,
/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace,
/area/lavaland/surface/outdoors)
(1,1,1) = {"
+3
View File
@@ -17,6 +17,9 @@ SUBSYSTEM_DEF(mapping)
var/list/ruins_templates = list()
///Assoc list of all ruins spawned, key center of ruin spawn -> value ruin instance
var/list/active_ruins = alist()
///List of ruins, separated by their theme
var/list/themed_ruins = list()
+55 -12
View File
@@ -86,15 +86,31 @@
/// Radius around megafauna within which we avoid spawning tendrils
var/megafauna_exclusion_radius = 7
///Chance of cells starting closed
var/initial_closed_chance = 45
///Amount of smoothing iterations
var/smoothing_iterations = 20
///How much neighbours does a dead cell need to become alive
var/birth_limit = 4
///How little neighbours does a alive cell need to die
var/death_limit = 3
///Cave gen settings below!!
/// Minimum dimension of a BSP leaf in the generator. Raising this creates larger pockets but can end up making for big corridors
var/min_bsp_size = 25
/// Maximum aspect ratio for BSP splits for lavaland generator
var/max_ratio = 1.5
/// Room edge padding within BSP leaf for lavaland generator
var/padding = 1
/// How much of each BSP leaf is considered untouchable by the cellular automata. Raising this generally means bigger pockets
var/room_fill_percent = 30
/// Width of corridors between rooms for lavaland generator. Raising this just means corridors are AT LEAST this wide. but cellular automata can make them bigger
var/corridor_width = 1
/// Chance to add extra MST edges for loops for lavaland generator. This basically results in more corridors / mazier generation
var/loop_percent = 15
/// Initial random floor density for lavaland generator
var/noise_percent = 51
/// Cellular Automata smoothing iterations for lavaland generator
var/ca_steps = 8
/// Neighbors to create floor (>=) for lavaland generator
var/birth_limit = 6
/// Neighbors to survive as floor (>=) for lavaland generator
var/survival_limit = 4
///Whether out-of-boudns counts as being alive. Setting this to FALSE results in the edges of the generator generating more closed. Default behavior tends to open up tunnels outside.
var/edges_are_alive = TRUE
/datum/map_generator/cave_generator/New()
. = ..()
@@ -138,10 +154,11 @@
return generate_terrain_with_biomes(turfs, generate_in)
var/start_time = REALTIMEOFDAY
string_gen = rustg_cnoise_generate("[initial_closed_chance]", "[smoothing_iterations]", "[birth_limit]", "[death_limit]", "[world.maxx]", "[world.maxy]") //Generate the raw CA data
string_gen = generate_cave(generate_in)
for(var/turf/gen_turf as anything in turfs) //Go through all the turfs and generate them
var/closed = string_gen[world.maxx * (gen_turf.y - 1) + gen_turf.x] != "0"
var/closed = string_gen[world.maxx * (gen_turf.y - 1) + gen_turf.x] != "1"
var/turf/new_turf = pick(closed ? closed_turf_types : open_turf_types)
// The assumption is this will be faster then changeturf, and changeturf isn't required since by this point
@@ -190,7 +207,7 @@
heat_seed = rand(0, 50000)
var/start_time = REALTIMEOFDAY
string_gen = rustg_cnoise_generate("[initial_closed_chance]", "[smoothing_iterations]", "[birth_limit]", "[death_limit]", "[world.maxx]", "[world.maxy]") //Generate the raw CA data
string_gen = generate_cave(generate_in)
var/humidity_gen = list()
humidity_gen[BIOME_HIGH_HUMIDITY] = rustg_dbp_generate("[humidity_seed]", "60", "[biome_stamp_size]", "[world.maxx]", "[high_heat_threshold]", "1.1")
@@ -210,7 +227,7 @@
var/list/to_generate = list()
for(var/turf/gen_turf as anything in turfs) //Go through all the turfs and generate them
var/closed = string_gen[world.maxx * (gen_turf.y - 1) + gen_turf.x] != "0"
var/closed = string_gen[world.maxx * (gen_turf.y - 1) + gen_turf.x] != "1"
var/datum/biome/selected_biome
// Here comes the meat of the biome code.
@@ -405,6 +422,32 @@
to_chat(world, span_boldannounce("[message]"), MESSAGE_TYPE_DEBUG)
log_world(message)
///Generates the cave shape using Rust-G
/datum/map_generator/cave_generator/proc/generate_cave(area/generate_in)
///Loop through all the active ruins for this z-level and make a json format out of it so we can send it to the generator
var/list/active_ruins_list = list()
for(var/turf/bottom_left_turf in SSmapping.active_ruins)
var/datum/map_template/ruin/active_ruin = SSmapping.active_ruins[bottom_left_turf]
if(bottom_left_turf.z != generate_in.z)
continue
active_ruins_list += list(list(
"x" = max(1, bottom_left_turf.x - active_ruin.terrain_padding),
"y" = max(1, bottom_left_turf.y - active_ruin.terrain_padding),
"w" = active_ruin.width + active_ruin.terrain_padding * 2,
"h" = active_ruin.height + active_ruin.terrain_padding * 2,
"isEnclosed" = active_ruin.enclosed_for_terrain,
))
var/active_ruin_string = json_encode(active_ruins_list)
var/string_gen = rustg_cave_system_generator_generate("[world.maxx]", "[world.maxy]", active_ruin_string, "[min_bsp_size]","[max_ratio]", "[padding]", "[room_fill_percent]", "[corridor_width]","[loop_percent]", "[noise_percent]", "[ca_steps]", "[birth_limit]", "[survival_limit]", "[edges_are_alive]")
return string_gen
/datum/map_generator/cave_generator/jungle
possible_biomes = list(
BIOME_LOW_HEAT = list(
+2 -6
View File
@@ -39,10 +39,7 @@
weighted_open_turf_types = list(/turf/open/misc/asteroid/snow/icemoon = 1)
flora_spawn_chance = 60
weighted_mob_spawn_list = null
initial_closed_chance = 0
birth_limit = 5
death_limit = 4
smoothing_iterations = 10
noise_percent = 100 // Full floor
feature_spawn_chance = 0.15
weighted_feature_spawn_list = list(
@@ -62,7 +59,7 @@
/// Surface snow generator variant for forested station trait, WITH FORESTSSSS
/datum/map_generator/cave_generator/icemoon/surface/forested
initial_closed_chance = 10
noise_percent = 65 //Few small rocks, but mostly open floor for the trees to spawn on
flora_spawn_chance = 80
weighted_flora_spawn_list = list(
@@ -80,7 +77,6 @@
weighted_mob_spawn_list = list(/mob/living/basic/deer/ice = 99, /mob/living/basic/tree = 1, /obj/effect/spawner/random/lavaland_mob/raptor = 15)
/datum/map_generator/cave_generator/icemoon/surface/rocky
initial_closed_chance = 53
mob_spawn_chance = 0.5
/datum/map_generator/cave_generator/icemoon/surface/noruins //use this for when you don't want ruins to spawn in a certain area
@@ -23,8 +23,7 @@
high_heat_threshold = 0.15
high_humidity_threshold = 0.15
biome_stamp_size = 60
smoothing_iterations = 50
/datum/map_generator/cave_generator/lavaland/ruin_version
biome_population = FALSE
weighted_open_turf_types = list(/turf/open/misc/asteroid/basalt/lava_land_surface/no_ruins = 1)
+4
View File
@@ -28,6 +28,10 @@
var/suffix = null
///What flavor or ruin is this? eg ZTRAIT_SPACE_RUINS
var/ruin_type = null
///is this ruin "enclosed" by walls. This is relevant for terrain gen with cellular automata to know whether this ruin will spawn inside of walls, or should spawn in the open.
var/enclosed_for_terrain = FALSE
///Padding to be used to ensure extra space around the ruin for terrain gen purposes. If a ruin is NOT enclosed and this is set to 1; there will be at least one layer of open terrain around the ruin. If a ruin IS enclosed and this is set to 1; there will be at least one layer of wall terrain around the ruin.
var/terrain_padding = 0
/datum/map_template/ruin/New()
if(!name && id)
+10
View File
@@ -23,6 +23,7 @@
id = "lust"
description = "Not exactly what you expected."
suffix = "icemoon_surface_lust.dmm"
enclosed_for_terrain = TRUE
/datum/map_template/ruin/icemoon/asteroid
name = "Ice-Ruin Asteroid Site"
@@ -103,6 +104,7 @@
suffix = "icemoon_surface_mining_site.dmm"
always_place = TRUE
always_spawn_with = list(/datum/map_template/ruin/icemoon/underground/mining_site_below = PLACE_BELOW)
enclosed_for_terrain = TRUE
/datum/map_template/ruin/icemoon/underground/mining_site_below
name = "Ice-Ruin Mining Site Underground"
@@ -111,6 +113,7 @@
suffix = "icemoon_underground_mining_site.dmm"
has_ceiling = FALSE
unpickable = TRUE
enclosed_for_terrain = TRUE
// below ground only
@@ -124,18 +127,21 @@
id = "abandonedvillage"
description = "Who knows what lies within?"
suffix = "icemoon_underground_abandoned_village.dmm"
enclosed_for_terrain = TRUE
/datum/map_template/ruin/icemoon/underground/library
name = "Ice-Ruin Buried Library"
id = "buriedlibrary"
description = "A once grand library, now lost to the confines of the Ice Moon."
suffix = "icemoon_underground_library.dmm"
enclosed_for_terrain = TRUE
/datum/map_template/ruin/icemoon/underground/wrath
name = "Ice-Ruin Ruin of Wrath"
id = "wrath"
description = "You'll fight and fight and just keep fighting."
suffix = "icemoon_underground_wrath.dmm"
enclosed_for_terrain = TRUE
/datum/map_template/ruin/icemoon/underground/hermit
name = "Ice-Ruin Frozen Shack"
@@ -148,6 +154,7 @@
id = "lavalandsite"
description = "I guess we never really left you huh?"
suffix = "icemoon_underground_lavaland.dmm"
enclosed_for_terrain = TRUE
/datum/map_template/ruin/icemoon/underground/puzzle
name = "Ice-Ruin Ancient Puzzle"
@@ -180,6 +187,7 @@
id = "mailroom"
description = "This is where all of your paychecks went. Signed, the management."
suffix = "icemoon_underground_mailroom.dmm"
enclosed_for_terrain = TRUE
/datum/map_template/ruin/icemoon/underground/biodome
name = "Ice-Ruin Syndicate Bio-Dome"
@@ -204,6 +212,7 @@
id = "syndie_lab"
description = "A small laboratory and living space for Syndicate agents."
suffix = "icemoon_underground_syndielab.dmm"
enclosed_for_terrain = TRUE
/datum/map_template/ruin/icemoon/underground/o31
name = "Ice-Ruin Outpost 31"
@@ -224,6 +233,7 @@
id = "hotsprings"
description = "Just relax and take a dip, nothing will go wrong, I swear!"
suffix = "icemoon_underground_hotsprings.dmm"
enclosed_for_terrain = TRUE
/datum/map_template/ruin/icemoon/underground/vent
name = "Ice-Ruin Icemoon Ore Vent"
+19
View File
@@ -15,6 +15,7 @@
description = "Seemingly plucked from a tropical destination, this beach is calm and cool, with the salty waves roaring softly in the background. \
Comes with a rustic wooden bar and suicidal bartender."
suffix = "lavaland_biodome_beach.dmm"
enclosed_for_terrain = TRUE
/datum/map_template/ruin/lavaland/biodome/winter
name = "Lava-Ruin Biodome Winter"
@@ -43,6 +44,7 @@
suffix = "lavaland_surface_cube.dmm"
cost = 10
allow_duplicates = FALSE
enclosed_for_terrain = TRUE
/datum/map_template/ruin/lavaland/seed_vault
name = "Lava-Ruin Seed Vault"
@@ -52,6 +54,7 @@
suffix = "lavaland_surface_seed_vault.dmm"
cost = 10
allow_duplicates = FALSE
enclosed_for_terrain = TRUE
/datum/map_template/ruin/lavaland/ash_walker
name = "Lava-Ruin Ash Walker Nest"
@@ -61,6 +64,7 @@
suffix = "lavaland_surface_ash_walker1.dmm"
cost = 20
allow_duplicates = FALSE
enclosed_for_terrain = TRUE
/datum/map_template/ruin/lavaland/syndicate_base
name = "Lava-Ruin Syndicate Lava Base"
@@ -87,6 +91,7 @@
cost = 5
suffix = "lavaland_surface_gaia.dmm"
allow_duplicates = FALSE
enclosed_for_terrain = TRUE
/datum/map_template/ruin/lavaland/sin
cost = 10
@@ -124,6 +129,7 @@
suffix = "lavaland_surface_sloth.dmm"
// Generates nothing but atmos runtimes and salt
cost = 0
terrain_padding = 2
/datum/map_template/ruin/lavaland/ratvar
name = "Lava-Ruin Dead God"
@@ -140,6 +146,7 @@
suffix = "lavaland_surface_hierophant.dmm"
always_place = TRUE
allow_duplicates = FALSE
terrain_padding = 2
/datum/map_template/ruin/lavaland/blood_drunk_miner
name = "Lava-Ruin Blood-Drunk Miner"
@@ -148,14 +155,17 @@
suffix = "lavaland_surface_blooddrunk1.dmm"
cost = 0
allow_duplicates = FALSE //will only spawn one variant of the ruin
enclosed_for_terrain = TRUE
/datum/map_template/ruin/lavaland/blood_drunk_miner/guidance
name = "Lava-Ruin Blood-Drunk Miner (Guidance)"
suffix = "lavaland_surface_blooddrunk2.dmm"
enclosed_for_terrain = TRUE
/datum/map_template/ruin/lavaland/blood_drunk_miner/hunter
name = "Lava-Ruin Blood-Drunk Miner (Hunter)"
suffix = "lavaland_surface_blooddrunk3.dmm"
enclosed_for_terrain = TRUE
/datum/map_template/ruin/lavaland/blood_drunk_miner/random
name = "Lava-Ruin Blood-Drunk Miner (Random)"
@@ -172,6 +182,7 @@
description = "Turns out that keeping your abductees unconscious is really important. Who knew?"
suffix = "lavaland_surface_ufo_crash.dmm"
cost = 5
enclosed_for_terrain = TRUE
/datum/map_template/ruin/lavaland/xeno_nest
name = "Lava-Ruin Xenomorph Nest"
@@ -180,6 +191,7 @@
Quality memes."
suffix = "lavaland_surface_xeno_nest.dmm"
cost = 20
enclosed_for_terrain = TRUE
/datum/map_template/ruin/lavaland/fountain
name = "Lava-Ruin Fountain Hall"
@@ -227,6 +239,7 @@
suffix = "lavaland_surface_random_ripley.dmm"
allow_duplicates = FALSE
cost = 5
enclosed_for_terrain = TRUE
/datum/map_template/ruin/lavaland/dark_wizards
name = "Lava-Ruin Dark Wizard Altar"
@@ -234,6 +247,7 @@
description = "A ruin with dark wizards. What secret do they guard?"
suffix = "lavaland_surface_wizard.dmm"
cost = 5
enclosed_for_terrain = TRUE
/datum/map_template/ruin/lavaland/strong_stone
name = "Lava-Ruin Strong Stone"
@@ -266,6 +280,7 @@
suffix = "lavaland_surface_elephant_graveyard.dmm"
allow_duplicates = FALSE
cost = 10
enclosed_for_terrain = TRUE
/datum/map_template/ruin/lavaland/bileworm_nest
name = "Lava-Ruin Bileworm Nest"
@@ -274,6 +289,7 @@
cost = 5
suffix = "lavaland_surface_bileworm_nest.dmm"
allow_duplicates = FALSE
enclosed_for_terrain = TRUE
/datum/map_template/ruin/lavaland/lava_phonebooth
name = "Lava-Ruin Phonebooth"
@@ -323,6 +339,8 @@
description = "Not every shuttle makes it back to CentCom."
suffix = "lavaland_surface_shuttle_wreckage.dmm"
allow_duplicates = FALSE
enclosed_for_terrain = TRUE
/datum/map_template/ruin/lavaland/crashsite
name = "Lava-Ruin Pod Crashsite"
@@ -330,6 +348,7 @@
description = "They launched too early"
suffix = "lavaland_surface_crashsite.dmm"
allow_duplicates = FALSE
enclosed_for_terrain = TRUE
/datum/map_template/ruin/lavaland/shoe_facotry
name = "Lava-Ruin Shoe Factory"
+3
View File
@@ -212,6 +212,9 @@
var/top_right_y = bottom_left_y + current_pick.height - 1
log_mapping("Successfully placed [current_pick.name] ruin ([bottom_left_x],[bottom_left_y],[placed_turf.z] to [top_right_x],[top_right_y],[placed_turf.z]).")
///Keep track of the active ruins so we can take it in account for map generation. Using bottom lef turf as key
SSmapping.active_ruins[locate(bottom_left_x, bottom_left_y, placed_turf.z)] = current_pick
//Update the available list
for(var/datum/map_template/ruin/R in ruins_available)
if(R.cost > budget || R.mineral_cost > mineral_budget)