Cleans up /proc/find_safe_turf(), documenting and improving code (#93848)

## About The Pull Request

`/proc/find_safe_turf()` was cleaned up so that it did not have two
arguments that accomplished the same thing, removing opportunities for
user errors when calling the proc. Additionally, since I was here, I
decided to clean up some of the code and document it.

Some thresholds were changed in this PR (the max temperature threshold
was dropped from 360K to 340K), but I believe that it's better to keep
them consistent instead of using arbitrary values.

## Uncomfortably large gap

<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>

## Changelog

No user facing changes.

<!-- Both 🆑's are required for the changelog to work! You can put
your name to the right of the first 🆑 if you want to overwrite your
GitHub username as author ingame. -->
<!-- You can use multiple of the same prefix (they're only used for the
icon ingame) and delete the unneeded ones. Despite some of the tags,
changelogs should generally represent how a player might be affected by
the changes rather than a summary of the PR's contents. -->

---------

Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com>
This commit is contained in:
tonty
2025-11-09 18:21:57 -05:00
committed by GitHub
parent 18eec09669
commit aaead63f36
6 changed files with 75 additions and 43 deletions

View File

@@ -173,16 +173,20 @@ GLOBAL_LIST_EMPTY(gas_handbook)
return null return null
/** /**
* A simple helped proc that checks if the contents of a list of gases are within acceptable terms. * A simple helper proc that checks if the contents of a list of gases are within acceptable terms.
* *
* Arguments: * Arguments:
* * gases: The list of gases which contents are being checked * * gases: The list of gases which contents are being checked
* * gases to check: An associated list of gas types and acceptable boundaries in moles. e.g. /datum/gas/oxygen = list(16, 30) * * acceptable_gas_bounds: An associated list of gas types and acceptable boundaries in moles. e.g. /datum/gas/oxygen = list(16, 30)
* * * if the assoc list is null, then it'll be considered a safe gas and won't return FALSE. * * * if the assoc list is null, then it'll be considered a safe gas and won't return FALSE.
* * extraneous_gas_limit: If a gas not in gases is found, this is the limit above which the proc will return FALSE. * * extraneous_gas_limit: If a gas not in gases is found, this is the limit above which the proc will return FALSE.
*
* Returns TRUE if the list of gases is acceptable, FALSE otherwise.
*/ */
/proc/check_gases(list/gases, list/gases_to_check, extraneous_gas_limit = 0.1) /proc/check_gases(list/gases, list/acceptable_gas_bounds, extraneous_gas_limit = 0.1)
gases_to_check = gases_to_check.Copy() SHOULD_BE_PURE(TRUE)
var/list/gases_to_check = acceptable_gas_bounds.Copy() // thank you spaceman
for(var/id in gases) for(var/id in gases)
var/gas_moles = gases[id][MOLES] var/gas_moles = gases[id][MOLES]
if(!(id in gases_to_check)) if(!(id in gases_to_check))

View File

@@ -125,26 +125,53 @@
effect.attach(location) effect.attach(location)
effect.start() effect.start()
// Safe location finder /**
/proc/find_safe_turf(zlevel, list/zlevels, extended_safety_checks = FALSE, dense_atoms = FALSE) * Attempts to find a "safe" floor turf within some given z-levels
if(!zlevels) * * zlevel_or_levels: The list of z-levels we are searching though. You can supply just a number and it will be turned into a list.
if (zlevel) * * extended_safety_checks: Will do some additional checks to make sure the destination is safe, see [/proc/is_safe_turf].
zlevels = list(zlevel) * * dense_atoms: Will additionally check to see if the turf has any dense obstructions, like machines or structures.
else *
zlevels = SSmapping.levels_by_trait(ZTRAIT_STATION) * Returns a safe floor turf,
var/cycles = 1000 * **BUT** there is a chance of it being null if an extremely large portion of a z-level is unsafe or blocked.
for(var/cycle in 1 to cycles) */
// DRUNK DIALLING WOOOOOOOOO /proc/find_safe_turf(zlevel_or_levels, extended_safety_checks = FALSE, dense_atoms = FALSE) as /turf/open/floor
SHOULD_BE_PURE(TRUE)
RETURN_TYPE(/turf/open/floor)
var/list/zlevels
if(islist(zlevel_or_levels))
zlevels = zlevel_or_levels
else if(zlevel_or_levels)
zlevels = list(zlevel_or_levels)
else
zlevels = SSmapping.levels_by_trait(ZTRAIT_STATION)
for(var/cycle in 1 to 1000)
var/x = rand(1, world.maxx) var/x = rand(1, world.maxx)
var/y = rand(1, world.maxy) var/y = rand(1, world.maxy)
var/z = pick(zlevels) var/z = pick(zlevels)
var/random_location = locate(x,y,z) var/random_location = locate(x,y,z)
var/keep_trying_no_teleport = (cycle < 300) //if the area is mostly NOTELEPORT (centcom) we gotta give up on this fantasy at some point.
if(is_safe_turf(random_location, extended_safety_checks, dense_atoms, cycle < 300))//if the area is mostly NOTELEPORT (centcom) we gotta give up on this fantasy at some point. if(is_safe_turf(random_location, extended_safety_checks, dense_atoms, keep_trying_no_teleport))
return random_location return random_location
/// Checks if a given turf is a "safe" location /**
* Checks to see if a given turf is a "safe" location. Being safe requires the following to be true:
* * Must be a [floor][/turf/open/floor]
* * Must have air, and that air must have [breathable bounds][/proc/check_gases] for humans
* * Must have goldilocks temperature
* * Must have safe pressure
*
* Optionally:
* * extended_safety_checks: Will make additional checks for turfs that technically pass all previous requirements but still may not be safe
* * dense_atoms: Must be unobstructed (no blocking objects such as machines, structures or mobs)
* * no_teleport: Must not have [NOTELEPORT][/area/var/area_flag]
*
* Returns TRUE if all conditions pass, FALSE otherwise.
*/
/proc/is_safe_turf(turf/random_location, extended_safety_checks = FALSE, dense_atoms = FALSE, no_teleport = FALSE) /proc/is_safe_turf(turf/random_location, extended_safety_checks = FALSE, dense_atoms = FALSE, no_teleport = FALSE)
SHOULD_BE_PURE(TRUE)
. = FALSE . = FALSE
if(!isfloorturf(random_location)) if(!isfloorturf(random_location))
return return
@@ -160,18 +187,18 @@
var/list/floor_gases = floor_gas_mixture.gases var/list/floor_gases = floor_gas_mixture.gases
var/static/list/gases_to_check = list( var/static/list/gases_to_check = list(
/datum/gas/oxygen = list(16, 100), /datum/gas/oxygen = list(/obj/item/organ/lungs::safe_oxygen_min, 100),
/datum/gas/nitrogen, /datum/gas/nitrogen,
/datum/gas/carbon_dioxide = list(0, 10) /datum/gas/carbon_dioxide = list(0, /obj/item/organ/lungs::safe_co2_max)
) )
if(!check_gases(floor_gases, gases_to_check)) if(!check_gases(floor_gases, gases_to_check))
return FALSE return FALSE
// Aim for goldilocks temperatures and pressure // Aim for goldilocks temperatures and pressure
if((floor_gas_mixture.temperature <= 270) || (floor_gas_mixture.temperature >= 360)) if((floor_gas_mixture.temperature <= BODYTEMP_COLD_DAMAGE_LIMIT) || (floor_gas_mixture.temperature >= BODYTEMP_HEAT_DAMAGE_LIMIT))
return return
var/pressure = floor_gas_mixture.return_pressure() var/pressure = floor_gas_mixture.return_pressure()
if((pressure <= 20) || (pressure >= 550)) if((pressure <= HAZARD_LOW_PRESSURE) || (pressure >= HAZARD_HIGH_PRESSURE))
return return
if(extended_safety_checks) if(extended_safety_checks)

View File

@@ -135,11 +135,11 @@
var/effect_range = 5 var/effect_range = 5
/obj/effect/fun_balloon/scatter/effect() /obj/effect/fun_balloon/scatter/effect()
for(var/mob/living/M in range(effect_range, get_turf(src))) for(var/mob/living/dispersed_mob in range(effect_range, get_turf(src)))
var/turf/T = find_safe_turf(zlevel = src.z) var/turf/drop_off = find_safe_turf(z)
new /obj/effect/temp_visual/gravpush(get_turf(M)) new /obj/effect/temp_visual/gravpush(get_turf(dispersed_mob))
M.forceMove(T) dispersed_mob.forceMove(drop_off)
to_chat(M, span_notice("Pop!"), confidential = TRUE) dispersed_mob.balloon_alert(dispersed_mob, "pop!")
// ----------- Station Crash // ----------- Station Crash
// Can't think of anywhere better to put it right now // Can't think of anywhere better to put it right now

View File

@@ -81,7 +81,7 @@
/// Attempts to teleport the passed mob to somewhere safe on the station, if they can use the blade. /// Attempts to teleport the passed mob to somewhere safe on the station, if they can use the blade.
/obj/item/melee/sickly_blade/proc/seek_safety(mob/user) /obj/item/melee/sickly_blade/proc/seek_safety(mob/user)
var/turf/safe_turf = find_safe_turf(zlevels = z, extended_safety_checks = TRUE) var/turf/safe_turf = find_safe_turf(z, extended_safety_checks = TRUE)
if(check_usability(user)) if(check_usability(user))
if(do_teleport(user, safe_turf, channel = TELEPORT_CHANNEL_MAGIC)) if(do_teleport(user, safe_turf, channel = TELEPORT_CHANNEL_MAGIC))
to_chat(user, span_warning("As you shatter [src], you feel a gust of energy flow through your body. [after_use_message]")) to_chat(user, span_warning("As you shatter [src], you feel a gust of energy flow through your body. [after_use_message]"))

View File

@@ -16,19 +16,20 @@
. = ..() . = ..()
if(.) if(.)
return return
if(tgui_alert(usr,question,name,list("Yes","No")) == "Yes" && Adjacent(user)) if(tgui_alert(usr,question,name,list("Yes","No")) != "Yes" && !Adjacent(user))
var/turf/T = zlevels ? find_safe_turf(zlevels=zlevels) : get_safe_random_station_turf_equal_weight() return
if(T) var/turf/safe_dropoff = zlevels ? find_safe_turf(zlevels) : get_safe_random_station_turf_equal_weight()
var/atom/movable/AM = user.pulling if(!safe_dropoff)
if(AM) to_chat(user, "Nothing happens. You feel that this is a bad sign.")
AM.forceMove(T) return
user.forceMove(T)
if(AM) var/atom/movable/pulled = user.pulling
user.start_pulling(AM) user.forceMove(safe_dropoff)
to_chat(user, span_notice("You blink and find yourself in [get_area_name(T)].")) if(pulled)
else pulled.forceMove(safe_dropoff)
to_chat(user, "Nothing happens. You feel that this is a bad sign.") user.start_pulling(pulled)
to_chat(user, span_notice("You blink and find yourself in [get_area_name(safe_dropoff)]."))
/obj/structure/signpost/attackby(obj/item/W, mob/user, list/modifiers, list/attack_modifiers) /obj/structure/signpost/attackby(obj/item/W, mob/user, list/modifiers, list/attack_modifiers)
return interact(user) return interact(user)

View File

@@ -715,12 +715,12 @@
if(healthcheck && (healthcheck - owner.health) > 5) if(healthcheck && (healthcheck - owner.health) > 5)
owner.visible_message(span_warning("[linked_extract] notices the sudden change in [owner]'s physical health, and activates!")) owner.visible_message(span_warning("[linked_extract] notices the sudden change in [owner]'s physical health, and activates!"))
do_sparks(5,FALSE,owner) do_sparks(5,FALSE,owner)
var/F = find_safe_turf(zlevel = owner.z, extended_safety_checks = TRUE) var/turf/emergency_turf = find_safe_turf(owner.z, extended_safety_checks = TRUE)
var/range = 0 var/range = 0
if(!F) if(!emergency_turf)
F = get_turf(owner) emergency_turf = get_turf(owner)
range = 50 range = 50
if(do_teleport(owner, F, range, channel = TELEPORT_CHANNEL_BLUESPACE)) if(do_teleport(owner, emergency_turf, range, channel = TELEPORT_CHANNEL_BLUESPACE))
to_chat(owner, span_notice("[linked_extract] will take some time to re-align you on the bluespace axis.")) to_chat(owner, span_notice("[linked_extract] will take some time to re-align you on the bluespace axis."))
do_sparks(5,FALSE,owner) do_sparks(5,FALSE,owner)
owner.apply_status_effect(/datum/status_effect/bluespacestabilization) owner.apply_status_effect(/datum/status_effect/bluespacestabilization)