From 6a2d22979577cad70fadbb9978017ed617f033f4 Mon Sep 17 00:00:00 2001 From: warriorstar-orion Date: Thu, 21 Mar 2024 14:42:35 -0400 Subject: [PATCH] Refactor Lavaland river spawner. (#24192) * Refactor lavaland river generator. * document var --- code/game/turfs/simulated/minerals.dm | 2 +- code/game/turfs/simulated/river.dm | 102 ++++++++++++++---------- code/modules/lavaland/lavaland_theme.dm | 22 ++--- 3 files changed, 70 insertions(+), 56 deletions(-) diff --git a/code/game/turfs/simulated/minerals.dm b/code/game/turfs/simulated/minerals.dm index 1ec9dd8fdd3..c221da9adcf 100644 --- a/code/game/turfs/simulated/minerals.dm +++ b/code/game/turfs/simulated/minerals.dm @@ -43,7 +43,7 @@ if(istype(T, /turf/simulated/mineral/random)) Spread(T) -/turf/simulated/mineral/Spread(turf/T) +/turf/simulated/mineral/proc/Spread(turf/T) T.ChangeTurf(type) /turf/simulated/mineral/shuttleRotate(rotation) diff --git a/code/game/turfs/simulated/river.dm b/code/game/turfs/simulated/river.dm index 6cb6f8677fa..b5b9a7d8acc 100644 --- a/code/game/turfs/simulated/river.dm +++ b/code/game/turfs/simulated/river.dm @@ -1,17 +1,53 @@ -#define RANDOM_UPPER_X 200 -#define RANDOM_UPPER_Y 200 +#define RIVER_MAX_X 200 +#define RIVER_MAX_Y 200 -#define RANDOM_LOWER_X 50 -#define RANDOM_LOWER_Y 50 +#define RIVER_MIN_X 50 +#define RIVER_MIN_Y 50 -/proc/spawn_rivers(target_z, nodes = 4, turf_type = /turf/simulated/floor/lava/mapping_lava, whitelist_area = /area/lavaland/surface/outdoors, min_x = RANDOM_LOWER_X, min_y = RANDOM_LOWER_Y, max_x = RANDOM_UPPER_X, max_y = RANDOM_UPPER_Y, prob = 25, prob_loss = 11) +/obj/effect/landmark/river_waypoint + name = "river waypoint" + /// Whether the turf of this landmark has already been linked to others during river generation. + var/connected = FALSE + invisibility = INVISIBILITY_ABSTRACT + +/// A straightforward system for making "rivers", paths made up of a specific +/// turf type that are generated in a random path on a z-level. +/datum/river_spawner + /// The z-level to generate the river on. There is theoretically nothing stopping + /// this from being used across z-levels, but we're keeping things simple. + var/target_z + /// The initial probability that a river tile will spread to adjacent tiles. + var/spread_prob + /// The amount reduced from spread_prob on every spread iteration to cause falloff. + var/spread_prob_loss + /// The base type that makes up the river. + var/river_turf_type = /turf/simulated/floor/lava/mapping_lava + /// The area that the spawner is allowed to spread or detour to. + var/whitelist_area_type = /area/lavaland/surface/outdoors + /// The type that the spawner is allowed to spread or detour to. + var/whitelist_turf_type = /turf/simulated/mineral + /// The turf used when a spread of the tile stops. + var/shoreline_turf_type = /turf/simulated/floor/plating/asteroid/basalt/lava_land_surface + +/datum/river_spawner/New(target_z_, spread_prob_ = 25, spread_prob_loss_ = 11) + target_z = target_z_ + spread_prob = spread_prob_ + spread_prob_loss = spread_prob_loss_ + +/// Generate a river between the bounds specified by (`min_x`, `min_y`) and +/// (`max_x`, `max_y`). +/// +/// `nodes` is the number of unique points in those bounds the river will +/// connect to. Note that `nodes` says little about the resultant size of the +/// river due to its ability to detour far away from the direct path between them. +/datum/river_spawner/proc/generate(nodes = 4, min_x = RIVER_MIN_X, min_y = RIVER_MIN_Y, max_x = RIVER_MAX_X, max_y = RIVER_MAX_Y) var/list/river_nodes = list() var/num_spawned = 0 var/list/possible_locs = block(locate(min_x, min_y, target_z), locate(max_x, max_y, target_z)) while(num_spawned < nodes && possible_locs.len) var/turf/T = pick(possible_locs) var/area/A = get_area(T) - if(!istype(A, whitelist_area) || (T.flags & NO_LAVA_GEN)) + if(!istype(A, whitelist_area_type) || (T.flags & NO_LAVA_GEN)) possible_locs -= T else river_nodes += new /obj/effect/landmark/river_waypoint(T) @@ -24,14 +60,13 @@ continue W.connected = TRUE var/turf/cur_turf = get_turf(W) - cur_turf.ChangeTurf(turf_type, ignore_air = TRUE) + cur_turf.ChangeTurf(river_turf_type, ignore_air = TRUE) var/turf/target_turf = get_turf(pick(river_nodes - W)) if(!target_turf) break var/detouring = 0 var/cur_dir = get_dir(cur_turf, target_turf) while(cur_turf != target_turf) - if(detouring) //randomly snake around a bit if(prob(20)) detouring = 0 @@ -50,68 +85,55 @@ stack_trace("Encountered a null turf in river loop, target turf was [target_turf], x=[target_turf.x], y=[target_turf.y].") break var/area/new_area = get_area(cur_turf) - if(!istype(new_area, whitelist_area) || (cur_turf.flags & NO_LAVA_GEN)) //Rivers will skip ruins + if(!istype(new_area, whitelist_area_type) || (cur_turf.flags & NO_LAVA_GEN)) //Rivers will skip ruins detouring = 0 cur_dir = get_dir(cur_turf, target_turf) cur_turf = get_step(cur_turf, cur_dir) continue else - var/turf/river_turf = cur_turf.ChangeTurf(turf_type, ignore_air = TRUE) + var/turf/river_turf = cur_turf.ChangeTurf(river_turf_type, ignore_air = TRUE) if(prob(1)) new /obj/effect/spawner/bridge(river_turf) - river_turf.Spread(prob, prob_loss, whitelist_area) + spread_turf(river_turf, spread_prob, spread_prob_loss, whitelist_area_type) for(var/WP in river_nodes) qdel(WP) - -/obj/effect/landmark/river_waypoint - name = "river waypoint" - var/connected = FALSE - invisibility = INVISIBILITY_ABSTRACT - - -/turf/proc/Spread(probability = 30, prob_loss = 25, whitelisted_area) +/datum/river_spawner/proc/spread_turf(turf/start_turf, probability = 30, prob_loss = 25, whitelisted_area) if(probability <= 0) return var/list/cardinal_turfs = list() var/list/diagonal_turfs = list() - var/logged_turf_type - for(var/F in RANGE_TURFS(1, src) - src) + + for(var/F in RANGE_TURFS(1, start_turf) - start_turf) var/turf/T = F var/area/new_area = get_area(T) - if(!T || (T.density && !ismineralturf(T)) || istype(T, /turf/simulated/floor/indestructible) || (whitelisted_area && !istype(new_area, whitelisted_area)) || (T.flags & NO_LAVA_GEN)) + if(!T || (T.density && !istype(T, whitelist_turf_type)) || istype(T, /turf/simulated/floor/indestructible) || (whitelisted_area && !istype(new_area, whitelisted_area)) || (T.flags & NO_LAVA_GEN)) continue - if(!logged_turf_type && ismineralturf(T)) - var/turf/simulated/mineral/M = T - logged_turf_type = M.turf_type - - if(get_dir(src, F) in GLOB.cardinal) + if(get_dir(start_turf, F) in GLOB.cardinal) cardinal_turfs += F else diagonal_turfs += F for(var/F in cardinal_turfs) //cardinal turfs are always changed but don't always spread var/turf/T = F - if(!istype(T, logged_turf_type) && T.ChangeTurf(type, ignore_air = TRUE) && prob(probability)) - T.Spread(probability - prob_loss, prob_loss, whitelisted_area) + if(!istype(T, start_turf.type) && T.ChangeTurf(start_turf.type, ignore_air = TRUE) && prob(probability)) + spread_turf(T, probability - prob_loss, prob_loss, whitelisted_area) if(prob(1)) new /obj/effect/spawner/bridge(T) for(var/F in diagonal_turfs) //diagonal turfs only sometimes change, but will always spread if changed var/turf/T = F - if(!istype(T, logged_turf_type) && prob(probability) && T.ChangeTurf(type, ignore_air = TRUE)) - T.Spread(probability - prob_loss, prob_loss, whitelisted_area) - else if(ismineralturf(T)) - var/turf/simulated/mineral/M = T - M.ChangeTurf(M.turf_type, ignore_air = TRUE) + if(!istype(T, shoreline_turf_type) && prob(probability) && T.ChangeTurf(start_turf.type, ignore_air = TRUE)) + spread_turf(T, probability - prob_loss, prob_loss, whitelisted_area) + else if(istype(T, whitelist_turf_type) && !istype(T, start_turf.type)) + T.ChangeTurf(shoreline_turf_type, ignore_air = TRUE) if(prob(1)) - new /obj/effect/spawner/bridge(M) + new /obj/effect/spawner/bridge(T) +#undef RIVER_MAX_X +#undef RIVER_MAX_Y -#undef RANDOM_UPPER_X -#undef RANDOM_UPPER_Y - -#undef RANDOM_LOWER_X -#undef RANDOM_LOWER_Y +#undef RIVER_MIN_X +#undef RIVER_MIN_Y diff --git a/code/modules/lavaland/lavaland_theme.dm b/code/modules/lavaland/lavaland_theme.dm index f1c4f16230b..7fa5a9088fa 100644 --- a/code/modules/lavaland/lavaland_theme.dm +++ b/code/modules/lavaland/lavaland_theme.dm @@ -25,7 +25,8 @@ planet_icon_state = "planet_lava" /datum/lavaland_theme/lava/setup() - spawn_rivers(level_name_to_num(MINING)) + var/datum/river_spawner/lava_spawner = new(level_name_to_num(MINING)) + lava_spawner.generate() /datum/lavaland_theme/plasma name = "plasma" @@ -33,8 +34,9 @@ planet_icon_state = "planet_plasma" /datum/lavaland_theme/plasma/setup() - spawn_rivers(level_name_to_num(MINING), nodes = 2) - spawn_rivers(level_name_to_num(MINING), nodes = 2) + var/datum/river_spawner/spawner = new(level_name_to_num(MINING)) + spawner.generate(nodes = 2) + spawner.generate(nodes = 2) // twice /datum/lavaland_theme/chasm name = "chasm" @@ -42,15 +44,5 @@ planet_icon_state = "planet_chasm" /datum/lavaland_theme/chasm/setup() - spawn_rivers( - level_name_to_num(MINING), - nodes = 6, - turf_type = /turf/simulated/floor/lava/mapping_lava, - whitelist_area = /area/lavaland/surface/outdoors, - min_x = 50, - min_y = 7, - max_x = 250, - max_y = 225, - prob = 10, - prob_loss = 5 - ) + var/datum/river_spawner/spawner = new(level_name_to_num(MINING), spread_prob_ = 10, spread_prob_loss_ = 5) + spawner.generate(nodes = 6, min_x = 50, min_y = 7, max_x = 250, max_y = 225)