From 8f98972b19fdde5aebbddcb0aa7310399b2a9b23 Mon Sep 17 00:00:00 2001 From: NovaBot <154629622+NovaBot13@users.noreply.github.com> Date: Thu, 8 Feb 2024 19:53:53 -0500 Subject: [PATCH] [MIRROR] Fixes ore vents spawning without ores on icebox, sets up map specific ore configurations (#805) * Fixes ore vents spawning without ores on icebox, sets up map specific ore configurations (#81103) ## About The Pull Request In short, we used a static list previously within the ore_generation subsystem that held the amount of each ore that we expected a single map to uniformly need. We held this number constant, since we were spawning 15 vents per map. **Pros:** This worked flawlessly for Lavaland since 15 vents on a single Z level makes it pretty densely packed map with a good amount of map-based ore spawns, and it worked consistently. **Cons:** 15 vents did not work well on Icebox however, even when split so that the majority of the ores were spawning on the lower levels, players did not feel like icebox spawned nearly enough ores and reported the map spawning empty. **Result:** As a result, we adjusted the ratio, so that we spawned vastly more ores on the lower levels, now up to 4 vents on the upper level, and 21 vents on the lower level. However, as we were still using the ore distribution list based on lavaland, icebox vents were quickly running out of ores to distribute between them, resulting in empty vents -> which produced empty boulders -> which not only don't really let you process them properly, but also just result in a metric ton of runtimes. Icebox now has it's own list of ore distributions. These distributions are now moved to a set of global lists as opposed to being saved on the subsystem as a static list, which will make going and setting up new ore distribution lists very very easy. Additionally, we've moved the setting and getting of those ore_distributions over to the seedRuins proc, so that we're actually setting the list of ores right before we actually place them to make sure that the order that it's set is roughly as it's needed, while still setting the list at the same time the map-appropriate ruin placements are dropped in. **Plus some misc cleanup fixes:** `var/list/ore_vent_sizes` in SSore_generation wasn't being treated as a similar budget list as `ore_vent_minerals`, since it `pick()`s off it's own static size list. Which is honestly fine for this five seconds, I can handle that later while we make sure the rest of the code code is stable. In the meantime, I've just tweak it so that it's easy to see at a glance how many of each random vent has spawned into the map. Tweaked the description to not include anything about chemical processing, as I'm planning on hitting on that in a part 2 PR that I'll be picking back up after the freeze. ## Why It's Good For The Game Cleans up the code a bit, but primarily fixes ores not spawning on icebox as they should. Should fix #81058. Improves description to not mention mechanics that aren't in game. Also, cleans up a piece of code that currently isn't serving much of a purpose. ## Changelog :cl: fix: Icebox should have it's ore distribution and it's ore vents fixed, so that vents should now produce ore. spellcheck: Boulder processing machines now don't mention things they don't do. /:cl: * Fixes ore vents spawning without ores on icebox, sets up map specific ore configurations --------- Co-authored-by: ArcaneMusic <41715314+ArcaneMusic@users.noreply.github.com> --- code/__DEFINES/mining.dm | 4 +- code/_globalvars/lists/ores_spawned.dm | 35 ++++++++++++ code/controllers/subsystem/mapping.dm | 4 +- code/controllers/subsystem/ore_generation.dm | 40 ++++++-------- code/datums/mapgen/CaveGenerator.dm | 1 - code/datums/mapgen/Cavegens/IcemoonCaves.dm | 7 +++ .../objects/structures/lavaland/ore_vent.dm | 55 ++++++++++--------- code/game/turfs/closed/minerals.dm | 2 - code/modules/mapping/ruins.dm | 9 ++- .../mining/boulder_processing/refinery.dm | 12 +--- tgstation.dme | 1 + 11 files changed, 106 insertions(+), 64 deletions(-) create mode 100644 code/_globalvars/lists/ores_spawned.dm diff --git a/code/__DEFINES/mining.dm b/code/__DEFINES/mining.dm index 2f4b5446476..11d150a453d 100644 --- a/code/__DEFINES/mining.dm +++ b/code/__DEFINES/mining.dm @@ -44,4 +44,6 @@ /// The multiplier that gets applied for automatically generated mining points. #define MINING_POINT_MACHINE_MULTIPLIER 0.8 - +//String defines to use with CaveGenerator presets for what ore breakdown to use. +#define OREGEN_PRESET_LAVALAND "lavaland" +#define OREGEN_PRESET_TRIPLE_Z "triple_z" diff --git a/code/_globalvars/lists/ores_spawned.dm b/code/_globalvars/lists/ores_spawned.dm new file mode 100644 index 00000000000..227e06061ad --- /dev/null +++ b/code/_globalvars/lists/ores_spawned.dm @@ -0,0 +1,35 @@ +/** + * Sets of global lists breaking down the base spawning distributions for various maps and stations. + * + * Currently used for ore vents on roundstart when the map is generated. (See ore_vent.dm, seedRuins() and ore_generation.dm) + * Ore vent lists here are copied to ore_vent_minerals on ruin seeding, + * in order to dynamically adjust the spawn rates as materials are picked and set a global ore distribution from vents. + * + * By default vents pull 4 unique materials each, but this can vary with subtypes. + */ + +GLOBAL_LIST_INIT(ore_vent_minerals_lavaland, list( + /datum/material/iron = 13, + /datum/material/glass = 12, + /datum/material/plasma = 9, + /datum/material/titanium = 6, + /datum/material/silver = 5, + /datum/material/gold = 5, + /datum/material/diamond = 3, + /datum/material/uranium = 3, + /datum/material/bluespace = 3, + /datum/material/plastic = 1, + )) + +GLOBAL_LIST_INIT(ore_vent_minerals_triple_z, list( + /datum/material/iron = 24, + /datum/material/glass = 23, + /datum/material/plasma = 16, + /datum/material/titanium = 10, + /datum/material/silver = 8, + /datum/material/gold = 7, + /datum/material/diamond = 4, + /datum/material/uranium = 4, + /datum/material/bluespace = 3, + /datum/material/plastic = 1, + )) diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm index 83daf2881db..7dd40a87a50 100644 --- a/code/controllers/subsystem/mapping.dm +++ b/code/controllers/subsystem/mapping.dm @@ -247,12 +247,12 @@ SUBSYSTEM_DEF(mapping) // Generate mining ruins var/list/lava_ruins = levels_by_trait(ZTRAIT_LAVA_RUINS) if (lava_ruins.len) - seedRuins(lava_ruins, CONFIG_GET(number/lavaland_budget), list(/area/lavaland/surface/outdoors/unexplored), themed_ruins[ZTRAIT_LAVA_RUINS], clear_below = TRUE) + seedRuins(lava_ruins, CONFIG_GET(number/lavaland_budget), list(/area/lavaland/surface/outdoors/unexplored), themed_ruins[ZTRAIT_LAVA_RUINS], clear_below = TRUE, mineral_budget = 15, mineral_budget_update = OREGEN_PRESET_LAVALAND) var/list/ice_ruins = levels_by_trait(ZTRAIT_ICE_RUINS) if (ice_ruins.len) // needs to be whitelisted for underground too so place_below ruins work - seedRuins(ice_ruins, CONFIG_GET(number/icemoon_budget), list(/area/icemoon/surface/outdoors/unexplored, /area/icemoon/underground/unexplored), themed_ruins[ZTRAIT_ICE_RUINS], clear_below = TRUE, mineral_budget = 4) + seedRuins(ice_ruins, CONFIG_GET(number/icemoon_budget), list(/area/icemoon/surface/outdoors/unexplored, /area/icemoon/underground/unexplored), themed_ruins[ZTRAIT_ICE_RUINS], clear_below = TRUE, mineral_budget = 4, mineral_budget_update = OREGEN_PRESET_TRIPLE_Z) var/list/ice_ruins_underground = levels_by_trait(ZTRAIT_ICE_RUINS_UNDERGROUND) if (ice_ruins_underground.len) diff --git a/code/controllers/subsystem/ore_generation.dm b/code/controllers/subsystem/ore_generation.dm index e659b10d873..162bc2e99d9 100644 --- a/code/controllers/subsystem/ore_generation.dm +++ b/code/controllers/subsystem/ore_generation.dm @@ -11,29 +11,18 @@ SUBSYSTEM_DEF(ore_generation) var/list/available_boulders = list() /// All the ore vents that are currently in the game, not just the ones that are producing boulders. var/list/possible_vents = list() - /// A list of all the minerals that are being mined by ore vents. We reset this list every time cave generation is done. - var/list/ore_vent_minerals = list() /** - * Associated list of minerals to be associated with our ore vents. + * A list of all the minerals that are being mined by ore vents. We reset this list every time cave generation is done. * Generally Should be empty by the time initialize ends on lavaland. * Each key value is the number of vents that will have this ore as a unique possible choice. + * If we call cave_generation more than once, we copy a list from the lists in lists/ores_spawned.dm */ - var/static/list/ore_vent_minerals_default = list( - /datum/material/iron = 13, - /datum/material/glass = 12, - /datum/material/plasma = 9, - /datum/material/titanium = 6, - /datum/material/silver = 5, - /datum/material/gold = 5, - /datum/material/diamond = 3, - /datum/material/uranium = 3, - /datum/material/bluespace = 3, - /datum/material/plastic = 1, - ) + var/list/ore_vent_minerals = list() + /// A tracker of how many of each ore vent size we have in the game. Useful for tracking purposes. var/list/ore_vent_sizes = list( - LARGE_VENT_TYPE = 3, - MEDIUM_VENT_TYPE = 5, - SMALL_VENT_TYPE = 7, + LARGE_VENT_TYPE = 0, + MEDIUM_VENT_TYPE = 0, + SMALL_VENT_TYPE = 0, ) /// Ores spawned by proximity to an ore vent. Useful for logging purposes. var/list/post_ore_random = list( @@ -54,13 +43,20 @@ SUBSYSTEM_DEF(ore_generation) /datum/controller/subsystem/ore_generation/Initialize() //Basically, we're going to round robin through the list of ore vents and assign a mineral to them until complete. - while(ore_vent_minerals.len > 0) + while(length(ore_vent_minerals) > 0) //Keep looping if there's more to assign + var/stallbreaker = 0 for(var/obj/structure/ore_vent/vent as anything in possible_vents) + if(length(ore_vent_minerals) <= 0) //But break early if there's none left. + break if(vent.unique_vent) continue //Ya'll already got your minerals. - if(ore_vent_minerals.len <= 0) - break - vent.generate_mineral_breakdown(max_minerals = 1, map_loading = TRUE) + if(length(difflist(first = ore_vent_minerals, second = vent.mineral_breakdown, skiprep = 1))) + vent.generate_mineral_breakdown(new_minerals = 1, map_loading = TRUE) + else + stallbreaker++ + if(stallbreaker >= length(possible_vents)) + return SS_INIT_SUCCESS //We've done all we can here. + continue return SS_INIT_SUCCESS /datum/controller/subsystem/ore_generation/fire(resumed) diff --git a/code/datums/mapgen/CaveGenerator.dm b/code/datums/mapgen/CaveGenerator.dm index 748ffdfddc9..001d22293d6 100644 --- a/code/datums/mapgen/CaveGenerator.dm +++ b/code/datums/mapgen/CaveGenerator.dm @@ -118,7 +118,6 @@ var/megas_allowed = (generate_in.area_flags & MEGAFAUNA_SPAWN_ALLOWED) && length(megafauna_spawn_list) var/start_time = REALTIMEOFDAY - SSore_generation.ore_vent_minerals = (SSore_generation.ore_vent_minerals_default).Copy() //reset the ore vent minerals to the default for(var/turf/target_turf as anything in turfs) if(!(target_turf.type in open_turf_types)) //only put stuff on open turfs we generated, so closed walls and rivers and stuff are skipped diff --git a/code/datums/mapgen/Cavegens/IcemoonCaves.dm b/code/datums/mapgen/Cavegens/IcemoonCaves.dm index afabc7ffd43..9d7fb39af71 100644 --- a/code/datums/mapgen/Cavegens/IcemoonCaves.dm +++ b/code/datums/mapgen/Cavegens/IcemoonCaves.dm @@ -39,6 +39,13 @@ birth_limit = 5 death_limit = 4 smoothing_iterations = 10 + weighted_feature_spawn_list = list( + /obj/structure/geyser/hollowwater = 10, + /obj/structure/geyser/plasma_oxide = 10, + /obj/structure/geyser/protozine = 10, + /obj/structure/geyser/random = 2, + /obj/structure/geyser/wittel = 10, + ) /// Surface snow generator variant for forested station trait, WITH FORESTSSSS /datum/map_generator/cave_generator/icemoon/surface/forested diff --git a/code/game/objects/structures/lavaland/ore_vent.dm b/code/game/objects/structures/lavaland/ore_vent.dm index 72cdd7d4ba5..fe663efec48 100644 --- a/code/game/objects/structures/lavaland/ore_vent.dm +++ b/code/game/objects/structures/lavaland/ore_vent.dm @@ -18,7 +18,7 @@ var/tapped = FALSE /// Has this vent been scanned by a mining scanner? Cannot be scanned again. Adds ores to the vent's description. var/discovered = FALSE - /// Is this type of vent exempt from the 15 vent limit? Think the free iron/glass vent or boss vents. This also causes it to not roll for random mineral breakdown. + /// Is this type of vent exempt from the map's vent budget/limit? Think the free iron/glass vent or boss vents. This also causes it to not roll for random mineral breakdown. var/unique_vent = FALSE /// What icon_state do we use when the ore vent has been tapped? var/icon_state_tapped = "ore_vent_active" @@ -74,7 +74,8 @@ if(mapload) generate_description() register_context() - SSore_generation.possible_vents += src + if(!unique_vent) + SSore_generation.possible_vents += src boulder_icon_state = pick(list( "boulder", "rock", @@ -168,33 +169,37 @@ /** * This proc is called when the ore vent is initialized, in order to determine what minerals boulders it spawns can contain. * The materials available are determined by SSore_generation.ore_vent_minerals, which is a list of all minerals that can be contained in ore vents for a given cave generation. - * As a result, minerals use a weighted list as seen by ore_vent_minerals_default, which is then copied to ore_vent_minerals. + * As a result, minerals use a weighted list as seen by ore_vent_minerals_lavaland, which is then copied to ore_vent_minerals. * Once a material is picked from the weighted list, it's removed from ore_vent_minerals, so that it can't be picked again and provided it's own internal weight used when assigning minerals to boulders spawned by this vent. * May also be called after the fact, as seen in SSore_generation's initialize, to add more minerals to an existing vent. * - * The above applies only when spawning in at mapload, otherwise we pick randomly from ore_vent_minerals_default. + * The above applies only when spawning in at mapload, otherwise we pick randomly from ore_vent_minerals_lavaland. * - * @params max_minerals How many minerals should be added to this vent? Defaults to MINERAL_TYPE_OPTIONS_RANDOM, which is 4. - * @params map_loading Is this vent being spawned in at mapload? If so, we use the ore_generation subsystem's ore_vent_minerals list to pick minerals. Otherwise, we pick randomly from ore_vent_minerals_default. + * @params new_minerals How many minerals should be added to this vent? Defaults to MINERAL_TYPE_OPTIONS_RANDOM, which is 4. + * @params map_loading Is this vent being spawned in at mapload? If so, we use the ore_generation subsystem's ore_vent_minerals list to pick minerals. Otherwise, we pick randomly from ore_vent_minerals_lavaland. */ -/obj/structure/ore_vent/proc/generate_mineral_breakdown(max_minerals = MINERAL_TYPE_OPTIONS_RANDOM, map_loading = FALSE) - if(max_minerals < 1) - CRASH("generate_mineral_breakdown called with max_minerals < 1.") - for(var/iterator in 1 to max_minerals) - if(!SSore_generation.ore_vent_minerals.len && map_loading) - CRASH("No minerals left to pick from! We may have spawned too many ore vents in init, or added too many ores to the existing vents.") - var/datum/material/material +/obj/structure/ore_vent/proc/generate_mineral_breakdown(new_minerals = MINERAL_TYPE_OPTIONS_RANDOM, map_loading = FALSE) + if(new_minerals < 1) + CRASH("generate_mineral_breakdown called with new_minerals < 1.") + var/list/available_mats = difflist(first = SSore_generation.ore_vent_minerals, second = mineral_breakdown, skiprep = 1) + for(var/i in 1 to new_minerals) + if(!length(SSore_generation.ore_vent_minerals) && map_loading) + // We should prevent this from happening in SSore_generation, but if not then we crash here + CRASH("No minerals left to pick from! We may have spawned too many ore vents in init, or the map config in seedRuins may not have enough resources for the mineral budget.") + var/datum/material/new_material if(map_loading) - material = pick_weight(SSore_generation.ore_vent_minerals) - if(is_type_in_list(mineral_breakdown, material)) - continue - if(map_loading) - SSore_generation.ore_vent_minerals[material] -= 1 //We remove 1 from the ore vent's mineral breakdown weight, so that it can't be picked again. - if(SSore_generation.ore_vent_minerals[material] <= 0) - SSore_generation.ore_vent_minerals -= material + if(length(available_mats)) + new_material = pick(GLOB.ore_vent_minerals_lavaland) + var/datum/material/surrogate_mat = pick(SSore_generation.ore_vent_minerals) + available_mats -= surrogate_mat + SSore_generation.ore_vent_minerals -= surrogate_mat + else + new_material = pick(available_mats) + available_mats -= new_material + SSore_generation.ore_vent_minerals -= new_material else - material = pick_weight(SSore_generation.ore_vent_minerals_default) - mineral_breakdown[material] = rand(1, 4) + new_material = pick(GLOB.ore_vent_minerals_lavaland) + mineral_breakdown[new_material] = rand(1, 4) /** @@ -416,15 +421,15 @@ if(LARGE_VENT_TYPE) boulder_size = BOULDER_SIZE_LARGE if(mapload) - SSore_generation.ore_vent_sizes["large"] -= 1 + SSore_generation.ore_vent_sizes["large"] += 1 if(MEDIUM_VENT_TYPE) boulder_size = BOULDER_SIZE_MEDIUM if(mapload) - SSore_generation.ore_vent_sizes["medium"] -= 1 + SSore_generation.ore_vent_sizes["medium"] += 1 if(SMALL_VENT_TYPE) boulder_size = BOULDER_SIZE_SMALL if(mapload) - SSore_generation.ore_vent_sizes["small"] -= 1 + SSore_generation.ore_vent_sizes["small"] += 1 else boulder_size = BOULDER_SIZE_SMALL //Might as well set a default value name = initial(name) diff --git a/code/game/turfs/closed/minerals.dm b/code/game/turfs/closed/minerals.dm index e1f0dd375b6..2e64276ea52 100644 --- a/code/game/turfs/closed/minerals.dm +++ b/code/game/turfs/closed/minerals.dm @@ -105,8 +105,6 @@ var/distance = 128 // Max distance for a get_dist is 127 for(var/obj/structure/ore_vent/vent as anything in SSore_generation.possible_vents) - if(vent.unique_vent) - continue if(vent.z != src.z) continue //Silly var/temp_distance = get_dist(src, vent) diff --git a/code/modules/mapping/ruins.dm b/code/modules/mapping/ruins.dm index d5ffa9f968d..4987016c5a0 100644 --- a/code/modules/mapping/ruins.dm +++ b/code/modules/mapping/ruins.dm @@ -71,8 +71,9 @@ * @param potentialRuins A list of ruins to choose from. * @param clear_below Whether to clear the area below the ruin. Used for multiz ruins. * @param mineral_budget The budget to spend on ruins that spawn ore vents. Map templates with vents have that defined by mineral_cost. + * @param mineral_budget_update What type of ore distribution should spawn from ruins picked by this cave generator? This list is copied from ores_spawned.dm into SSore_generation.ore_vent_minerals. */ -/proc/seedRuins(list/z_levels = null, budget = 0, whitelist = list(/area/space), list/potentialRuins, clear_below = FALSE, mineral_budget = 15) +/proc/seedRuins(list/z_levels = null, budget = 0, whitelist = list(/area/space), list/potentialRuins, clear_below = FALSE, mineral_budget = 15, mineral_budget_update) if(!z_levels || !z_levels.len) WARNING("No Z levels provided - Not generating ruins") return @@ -92,6 +93,12 @@ if(PERFORM_ALL_TESTS(log_mapping)) log_mapping("All ruins being loaded for map testing.") + switch(mineral_budget_update) //If we use more map configurations, add another case + if(OREGEN_PRESET_LAVALAND) + SSore_generation.ore_vent_minerals = expand_weights(GLOB.ore_vent_minerals_lavaland) + if(OREGEN_PRESET_TRIPLE_Z) + SSore_generation.ore_vent_minerals = expand_weights(GLOB.ore_vent_minerals_triple_z) + //Set up the starting ruin list for(var/key in ruins) var/datum/map_template/ruin/R = ruins[key] diff --git a/code/modules/mining/boulder_processing/refinery.dm b/code/modules/mining/boulder_processing/refinery.dm index 828ace377e9..a6c6c2e7dfc 100644 --- a/code/modules/mining/boulder_processing/refinery.dm +++ b/code/modules/mining/boulder_processing/refinery.dm @@ -1,15 +1,11 @@ /** * Your new favorite industrial waste magnet! * Accepts boulders and produces sheets of non-metalic materials. - * Can be upgraded with stock parts or through chemical inputs. * When upgraded, it can hold more boulders and process more at once. - * - * Chemical inputs can be used to boost the refinery's efficiency, but produces industrial waste, which eats through the station and is generally difficult to store. */ - /obj/machinery/bouldertech/refinery name = "boulder refinery" - desc = "BR for short. Accepts boulders and refines non-metallic ores into sheets using internal chemicals. Can be upgraded with stock parts or through chemical inputs." + desc = "BR for short. Accepts boulders and refines non-metallic ores into sheets using internal chemicals." icon_state = "stacker" holds_minerals = TRUE processable_materials = list( @@ -53,14 +49,11 @@ /** * Your other new favorite industrial waste magnet! * Accepts boulders and produces sheets of metalic materials. - * Can be upgraded with stock parts or through chemical inputs. * When upgraded, it can hold more boulders and process more at once. - * - * Chemical inputs can be used to boost the refinery's efficiency, but produces industrial waste, which eats through the station and is generally difficult to store. */ /obj/machinery/bouldertech/refinery/smelter name = "boulder smelter" - desc = "BS for short. Accept boulders and refines metallic ores into sheets. Can be upgraded with stock parts or through gas inputs." + desc = "BS for short. Accept boulders and refines metallic ores into sheets." icon_state = "smelter" processable_materials = list( /datum/material/iron, @@ -90,7 +83,6 @@ set_light_on(TRUE) return TRUE - /obj/machinery/bouldertech/refinery/smelter/process() . = ..() if(. == PROCESS_KILL) diff --git a/tgstation.dme b/tgstation.dme index da0e385e0e9..58754096bef 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -629,6 +629,7 @@ #include "code\_globalvars\lists\mobs.dm" #include "code\_globalvars\lists\names.dm" #include "code\_globalvars\lists\objects.dm" +#include "code\_globalvars\lists\ores_spawned.dm" #include "code\_globalvars\lists\plumbing.dm" #include "code\_globalvars\lists\poll_ignore.dm" #include "code\_globalvars\lists\quirks.dm"