diff --git a/code/__DEFINES/atmospherics/Connections.dm b/code/__DEFINES/atmospherics/Connections.dm
index 65ef7db1ee05..d3e1ff818e2c 100644
--- a/code/__DEFINES/atmospherics/Connections.dm
+++ b/code/__DEFINES/atmospherics/Connections.dm
@@ -1,6 +1,6 @@
#define WOOSH \
if(connecting_turfs.len && (REALTIMEOFDAY > last_woosh + 2 SECONDS)){ \
- playsound(pick(connecting_turfs),abs(differential) > zas_settings.airflow_heavy_pressure ? 'modular_pariah/master_files/sound/effects/space_wind_big.ogg' : 'modular_pariah/master_files/sound/effects/space_wind.ogg',100,TRUE,null,pressure_affected = FALSE); \
+ playsound(pick(connecting_turfs),abs(differential) > zas_settings.airflow_mob_pressure ? 'modular_pariah/master_files/sound/effects/space_wind_big.ogg' : 'modular_pariah/master_files/sound/effects/space_wind.ogg',100,TRUE,null,pressure_affected = FALSE); \
last_woosh = REALTIMEOFDAY;\
} \
diff --git a/code/__DEFINES/obj_flags.dm b/code/__DEFINES/obj_flags.dm
index 2743fb91c961..efa6799c8960 100644
--- a/code/__DEFINES/obj_flags.dm
+++ b/code/__DEFINES/obj_flags.dm
@@ -13,7 +13,6 @@
#define BLOCK_Z_IN_UP (1<<9) // Should this object block z uprise from below?
#define BLOCK_Z_FALL (1<<10) // Should this object block falling?
#define NO_BUILD (1<<11) // Can we build on this object?
-#define PLASMAGUARD (1<<12) //Immune to plasma contamination
// If you add new ones, be sure to add them to /obj/Initialize as well for complete mapping support
diff --git a/code/controllers/subsystem/airflow.dm b/code/controllers/subsystem/airflow.dm
index aa28c57fc72e..c710a30cf403 100644
--- a/code/controllers/subsystem/airflow.dm
+++ b/code/controllers/subsystem/airflow.dm
@@ -48,10 +48,7 @@ SUBSYSTEM_DEF(airflow)
target.airflow_process_delay -= 1
continue
- else if (target.airflow_process_delay)
- target.airflow_process_delay = 0
-
- target.airflow_speed = min(target.airflow_speed, 15)
+ target.airflow_speed = clamp(0, target.airflow_speed, 15)
target.airflow_speed -= zas_settings.airflow_speed_decay
if (target.airflow_skip_speedcheck)
@@ -119,43 +116,3 @@ SUBSYSTEM_DEF(airflow)
/datum/controller/subsystem/airflow/proc/HandleDel(datum/source)
SIGNAL_HANDLER
processing -= source
-
-/atom/movable/proc/prepare_airflow(strength)
- if (!airflow_dest || airflow_speed < 0 || last_airflow > world.time - zas_settings.airflow_delay)
- return FALSE
- if (airflow_speed)
- airflow_speed = strength / max(get_dist(src, airflow_dest), 1)
- return FALSE
- if(!check_airflow_movable(strength*10)) //Repel/Gotoairflowdest() divide the differential by a max of 10, so we're undoing that here
- return FALSE
- if (airflow_dest == loc)
- step_away(src, loc)
- if (ismob(src))
- to_chat(src, span_warning("You are pushed away by a rush of air!"))
-
- last_airflow = world.time
-
- var/airflow_falloff = 9 - sqrt((x - airflow_dest.x) ** 2 + (y - airflow_dest.y) ** 2)
- if (airflow_falloff < 1)
- airflow_dest = null
- return FALSE
-
- airflow_speed = min(max(strength * (9 / airflow_falloff), 1), 9)
- return TRUE
-
-
-/atom/movable/proc/GotoAirflowDest(strength)
- if (!prepare_airflow(strength))
- return
- airflow_xo = airflow_dest.x - x
- airflow_yo = airflow_dest.y - y
- airflow_dest = null
- SSairflow.Enqueue(src)
-
-/atom/movable/proc/RepelAirflowDest(strength)
- if (!prepare_airflow(strength))
- return
- airflow_xo = -(airflow_dest.x - x)
- airflow_yo = -(airflow_dest.y - y)
- airflow_dest = null
- SSairflow.Enqueue(src)
diff --git a/code/controllers/subsystem/airmachines.dm b/code/controllers/subsystem/airmachines.dm
index 5b1fd6462c1d..466c10652b60 100644
--- a/code/controllers/subsystem/airmachines.dm
+++ b/code/controllers/subsystem/airmachines.dm
@@ -2,9 +2,11 @@ SUBSYSTEM_DEF(airmachines)
name = "Air (Machines)"
priority = FIRE_PRIORITY_AIRMACHINES
init_order = INIT_ORDER_AIRMACHINES
- flags = SS_KEEP_TIMING
+ flags = SS_POST_FIRE_TIMING
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
+ wait = 0.5 SECONDS
+
var/cached_cost
var/list/pipe_init_dirs_cache = list()
diff --git a/code/controllers/subsystem/zas.dm b/code/controllers/subsystem/zas.dm
index 58050380629e..644c20c54729 100644
--- a/code/controllers/subsystem/zas.dm
+++ b/code/controllers/subsystem/zas.dm
@@ -63,7 +63,7 @@ SUBSYSTEM_DEF(zas)
name = "Air Core"
priority = FIRE_PRIORITY_AIR
init_order = INIT_ORDER_AIR
- flags = SS_KEEP_TIMING
+ flags = SS_POST_FIRE_TIMING
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
wait = 2 SECONDS
@@ -466,9 +466,9 @@ SUBSYSTEM_DEF(zas)
if(!B.connections)
B.connections = new
- if(A.connections.get(a_to_b))
+ if(A.connections.get_connection_for_dir(a_to_b))
return
- if(B.connections.get(b_to_a))
+ if(B.connections.get_connection_for_dir(b_to_a))
return
if(!space && (A.zone == B.zone))
return
diff --git a/code/game/atom/atoms.dm b/code/game/atom/atoms.dm
index 487459b87419..e873f88c5fa9 100644
--- a/code/game/atom/atoms.dm
+++ b/code/game/atom/atoms.dm
@@ -549,16 +549,12 @@
///Return the current air environment in this atom
/atom/proc/return_air()
- if(loc)
- return loc.return_air()
- else
- return null
+ return loc?.return_air()
///Return the current air environment in this atom. If this atom is a turf, it will not automatically update the zone.
/atom/proc/unsafe_return_air()
return return_air()
-
///Return the air if we can analyze it
/atom/proc/return_analyzable_air()
return null
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index c07b088985a6..c17211228f60 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -629,14 +629,23 @@
if(!bumped_atom)
CRASH("Bump was called with no argument.")
SEND_SIGNAL(src, COMSIG_MOVABLE_BUMP, bumped_atom)
+
. = ..()
+
if(!QDELETED(throwing))
throwing.finalize(hit = TRUE, target = bumped_atom)
. = TRUE
if(QDELETED(bumped_atom))
return
+
bumped_atom.BumpedBy(src)
+ if(moving_by_airflow && !QDELING(src))
+ if(airflow_speed >= 1 && airflow_dest)
+ AirflowBump(bumped_atom)
+ else
+ SSairflow.Dequeue(src)
+
/atom/movable/Exited(atom/movable/gone, direction)
. = ..()
diff --git a/code/game/machinery/computer/atmos_computers/_air_sensor.dm b/code/game/machinery/computer/atmos_computers/_air_sensor.dm
index 67d64aed9156..a3124dc39f0d 100644
--- a/code/game/machinery/computer/atmos_computers/_air_sensor.dm
+++ b/code/game/machinery/computer/atmos_computers/_air_sensor.dm
@@ -43,7 +43,7 @@
if(!on)
return
- var/datum/gas_mixture/air_sample = unsafe_return_air()
+ var/datum/gas_mixture/air_sample = loc.unsafe_return_air()
var/datum/signal/signal = new(src, list(
"sigtype" = "status",
"tag" = id_tag,
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 66156c57dcb7..7293c4c9e6d8 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -211,6 +211,7 @@ GLOBAL_PROTECT(admin_verbs_debug)
/client/proc/debug_spell_requirements,
/client/proc/analyze_openturf,
/client/proc/debug_health,
+ /client/proc/change_zas_settings,
)
GLOBAL_LIST_INIT(admin_verbs_possess, list(/proc/possess, GLOBAL_PROC_REF(release)))
@@ -1036,3 +1037,12 @@ GLOBAL_PROTECT(admin_verbs_hideable)
to_chat(world, span_boldannounce("The gamemode is now: [fake_name ? SSticker.mode_display_name : SSticker.mode.name]."))
message_admins("[key_name_admin(usr)] has set the gamemode to [SSticker.mode.type].")
+
+/client/proc/change_zas_settings()
+ set name = "ZAS Settings"
+ set category = "Debug"
+
+ if(!check_rights(R_DEBUG))
+ return
+
+ zas_settings.ui_interact(mob)
diff --git a/code/modules/atmospherics/ZAS/Airflow.dm b/code/modules/atmospherics/ZAS/Airflow.dm
index b97dbf3a3798..20e06d88934f 100644
--- a/code/modules/atmospherics/ZAS/Airflow.dm
+++ b/code/modules/atmospherics/ZAS/Airflow.dm
@@ -14,8 +14,8 @@ This entire system is an absolute mess.
var/tmp/airflow_speed = 0
///Time (ticks) spent in airflow
var/tmp/airflow_time = 0
- ///Time (ticks) since last airflow movement
- var/tmp/last_airflow = 0
+ /// Cooldown for airflow push.
+ COOLDOWN_DECLARE(airflow_push_cooldown)
var/tmp/airborne_acceleration = 0
var/tmp/airflow_xo
@@ -34,24 +34,26 @@ This entire system is an absolute mess.
return
/mob/living/airflow_stun(delta_p)
- if(stat == 2)
+ if(stat == DEAD)
return FALSE
+
if(last_airflow_stun > world.time - zas_settings.airflow_stun_cooldown)
return FALSE
- if(!(status_flags & CANSTUN) && !(status_flags & CANKNOCKDOWN))
- return FALSE
- if(buckled)
+
+ if(!(status_flags & CANSTUN) || !(status_flags & CANKNOCKDOWN))
return FALSE
+
if(HAS_TRAIT(src, TRAIT_NEGATES_GRAVITY)) //Magboots
return FALSE
- if(IsKnockdown()) //Uhhh maybe?
+
+ if(body_position == LYING_DOWN)
return FALSE
Knockdown(zas_settings.airflow_stun * clamp((delta_p / zas_settings.airflow_stun_pressure), 1, 3))
+
visible_message(
- span_danger("[src] is thrown to the floor by a gust of air!"),
- span_danger("A sudden rush of air knocks you over!"),
- span_hear("You hear a gust of air, followed by a soft thud.")
+ span_danger("[src] is thrown to the floor!"),
+ blind_message = span_hear("You hear a gust of air, followed by a soft thud.")
)
last_airflow_stun = world.time
@@ -62,42 +64,46 @@ This entire system is an absolute mess.
/mob/living/simple_animal/slime/airflow_stun(delta_p)
return
-///Checks to see if airflow can move this movable.
-/atom/movable/proc/check_airflow_movable(n)
+/// Checks to see if airflow can move this movable.
+/atom/movable/proc/can_airflow_move(delta_p)
//We're just hoping nothing goes wrong
return TRUE
-/mob/check_airflow_movable(n)
+/mob/can_airflow_move(delta_p)
if(status_flags & GODMODE)
return FALSE
- if(n < zas_settings.airflow_heavy_pressure)
+ if(buckled)
+ return FALSE
+ if(delta_p < zas_settings.airflow_mob_pressure)
return FALSE
if(HAS_TRAIT(src, TRAIT_NEGATES_GRAVITY)) //Magboots
return FALSE
return ..()
-/mob/living/silicon/check_airflow_movable()
- return 0
+/mob/living/silicon/can_airflow_move(delta_p)
+ return FALSE
-/obj/check_airflow_movable(n)
- if(anchored)
+/obj/can_airflow_move(delta_p)
+ if(anchored) // zone/proc/movables() checks this already for real spacewind, but other things may use this proc.
return FALSE
- if(density && n < zas_settings.airflow_dense_pressure)
+ if(density && (delta_p < zas_settings.airflow_dense_pressure))
return FALSE
return ..()
-/obj/item/check_airflow_movable(n)
+/obj/item/can_airflow_move(delta_p)
switch(w_class)
- if(0,1,2)
- if(n < zas_settings.airflow_lightest_pressure) return 0
- if(3)
- if(n < zas_settings.airflow_light_pressure) return 0
- if(4,5)
- if(n < zas_settings.airflow_medium_pressure) return 0
- if(6)
- if(n < zas_settings.airflow_heavy_pressure) return 0
- if(7 to INFINITY)
- if(n < zas_settings.airflow_dense_pressure) return 0
+ if(0,WEIGHT_CLASS_TINY,WEIGHT_CLASS_SMALL)
+ if(delta_p < zas_settings.airflow_lightest_pressure)
+ return FALSE
+ if(WEIGHT_CLASS_NORMAL)
+ if(delta_p < zas_settings.airflow_light_pressure)
+ return FALSE
+ if(WEIGHT_CLASS_BULKY, WEIGHT_CLASS_HUGE)
+ if(delta_p < zas_settings.airflow_medium_pressure)
+ return FALSE
+ if(WEIGHT_CLASS_GIGANTIC)
+ if(delta_p < zas_settings.airflow_mob_pressure)
+ return FALSE
return ..()
///The typecache of objects airflow can't push objects into the same tile of
@@ -106,49 +112,41 @@ GLOBAL_LIST_INIT(airflow_step_blacklist, typecacheof(list(
/obj/machinery/door
)))
-/atom/movable/Bump(atom/A)
- . = ..()
- if(!moving_by_airflow)
+/// Called when a movable Bump()s another atom due to forced airflow movement.
+/atom/movable/proc/AirflowBump(atom/A)
+ var/turf/T = get_turf(A)
+ if(airborne_acceleration > 1)
+ airflow_hit(A)
+ A.airflow_hit_act(src)
+
+ else if(istype(src, /mob/living/carbon/human) && A.density)
+ var/mob/living/carbon/human/human_src = src
+ to_chat(human_src, span_warning("You are pinned against \the [A] by airflow."))
+ human_src.Stun(2 SECONDS) // :)
+ SSairflow.Dequeue(src)
return
-
- if(airflow_speed > 0 && airflow_dest)
- var/turf/T = get_turf(A)
- if(airborne_acceleration > 1)
- airflow_hit(A)
- A.airflow_hit_act(src)
- else if(istype(src, /mob/living/carbon/human))
- if((A:density))
- to_chat(src, "You are pinned against \the [A] by airflow!")
- src:Stun(1 SECONDS) // :)
- airflow_speed = 0
- airflow_time = 0
- airborne_acceleration = 0
- return
- /*
- If the turf of the atom we bumped is NOT dense, then we check if the flying object is dense.
- We check the special var because flying objects gain density so they can Bump() objects.
- If the object is NOT normally dense, we remove our density and the target's density,
- enabling us to step into their turf. Then, we set the density back to the way its supposed to be for airflow.
- */
- if(!T.density)
- if(ismovable(A) && !(GLOB.airflow_step_blacklist[A.type]) && !A:airflow_old_density)
- set_density(FALSE)
- A.set_density(FALSE)
- step_towards(src, airflow_dest)
- set_density(TRUE)
- A.set_density(TRUE)
- else
- airflow_speed = 0
- airflow_time = 0
- airborne_acceleration = 0
+ /*
+ If the turf of the atom we bumped is NOT dense, then we check if the flying object is dense.
+ We check the special var because flying objects gain density so they can Bump() objects.
+ If the object is NOT normally dense, we remove our density and the target's density,
+ enabling us to step into their turf. Then, we set the density back to the way its supposed to be for airflow.
+ */
+ if(!T.density && ismovable(A) && !(GLOB.airflow_step_blacklist[A.type]))
+ var/atom/movable/bumped_movable = A
+ if(bumped_movable.airflow_old_density)
+ return
+
+ set_density(FALSE)
+ bumped_movable.set_density(FALSE)
+ step_towards(src, airflow_dest)
+ set_density(TRUE)
+ bumped_movable.set_density(TRUE)
///Called when src collides with A during airflow
/atom/movable/proc/airflow_hit(atom/A)
SHOULD_CALL_PARENT(TRUE)
- airflow_speed = 0
- airflow_dest = null
- airborne_acceleration = 0
+ SSairflow.Dequeue(src)
/mob/living/airflow_hit(atom/A)
var/b_loss = AIRBORNE_DAMAGE(src)
@@ -156,24 +154,26 @@ GLOBAL_LIST_INIT(airflow_step_blacklist, typecacheof(list(
return ..()
/mob/living/carbon/airflow_hit(atom/A)
- if(istype(A, /obj/structure) || iswallturf(A))
- if(airflow_speed > 10)
- Paralyze(round(airflow_speed * zas_settings.airflow_stun))
- Stun(round(airflow_speed * zas_settings.airflow_stun) + 3)
- loc.add_blood_DNA(return_blood_DNA())
- visible_message(
- span_danger("[src] splats against \the [A]!"),
- span_userdanger("You slam into \the [A] with tremendous force!"),
- span_hear("You hear a loud thud.")
- )
- INVOKE_ASYNC(emote("scream"))
- else
- Stun(round(airflow_speed * zas_settings.airflow_stun/2))
- visible_message(
- span_danger("[src] slams into \the [A]!"),
- span_userdanger("You're thrown against \the [A] by pressure!"),
- span_hear("You hear a loud thud.")
- )
+ if(!(istype(A, /obj/structure) || iswallturf(A)))
+ return ..()
+
+ if(airflow_speed > 10)
+ Paralyze(round(airflow_speed * zas_settings.airflow_stun))
+ Stun(round(airflow_speed * zas_settings.airflow_stun) + 3)
+ loc.add_blood_DNA(return_blood_DNA())
+ visible_message(
+ span_danger("[src] splats against \the [A]!"),
+ span_userdanger("You slam into \the [A] with tremendous force!"),
+ span_hear("You hear a loud thud.")
+ )
+ INVOKE_ASYNC(emote("scream"))
+ else
+ Stun(round(airflow_speed * zas_settings.airflow_stun/2))
+ visible_message(
+ span_danger("[src] slams into \the [A]!"),
+ span_userdanger("You're thrown against \the [A] by pressure!"),
+ span_hear("You hear a loud thud.")
+ )
return ..()
@@ -183,13 +183,12 @@ GLOBAL_LIST_INIT(airflow_step_blacklist, typecacheof(list(
/mob/living/airflow_hit_act(atom/movable/flying)
. = ..()
- src.visible_message(
+ visible_message(
span_danger("A flying [flying.name] slams into \the [src]!"),
- span_danger("You're hit by a flying [flying]!"),
- span_danger("You hear a soft thud.")
+ blind_message = span_danger("You hear a soft thud.")
)
- playsound(src.loc, "punch", 25, 1, -1)
+ playsound(loc, "punch", 25, 1, -1)
var/weak_amt
if(istype(flying,/obj/item))
weak_amt = flying:w_class*2 ///Heheheh
@@ -198,18 +197,18 @@ GLOBAL_LIST_INIT(airflow_step_blacklist, typecacheof(list(
else
weak_amt = rand(1, 3)
- src.Knockdown(weak_amt SECONDS)
+ Knockdown(weak_amt SECONDS)
/obj/airflow_hit_act(atom/movable/flying)
. = ..()
if(flying.airflow_old_density)
- src.visible_message(
+ visible_message(
span_danger("A flying [flying.name] slams into \the [src]!"),
null,
span_danger("You hear a loud slam!")
)
- playsound(src.loc, "smash.ogg", 25, 1, -1)
+ playsound(loc, "smash.ogg", 25, 1, -1)
if(!uses_integrity)
return
@@ -244,4 +243,42 @@ GLOBAL_LIST_INIT(airflow_step_blacklist, typecacheof(list(
continue
. += A
+/atom/movable/proc/prepare_airflow(strength)
+ if (!airflow_dest || airflow_dest == loc) // This should no longer happen, but just in case, ignore it.
+ return FALSE
+
+ COOLDOWN_START(src, airflow_push_cooldown, zas_settings.airflow_retrigger_delay)
+
+ var/airflow_falloff = 9 - get_dist_euclidean(loc, airflow_dest)
+ if (airflow_falloff < 1)
+ return FALSE
+
+ airflow_speed = clamp(strength * (9 / airflow_falloff), 1, 9)
+ return TRUE
+
+/mob/prepare_airflow(strength)
+ . = ..()
+ if(!.)
+ return
+
+ to_chat(src, span_warning("A strong air current drags you away."))
+
+/atom/movable/proc/GotoAirflowDest(strength)
+ if (!prepare_airflow(strength))
+ airflow_dest = null
+ return
+ airflow_xo = airflow_dest.x - x
+ airflow_yo = airflow_dest.y - y
+ airflow_dest = null
+ SSairflow.Enqueue(src)
+
+/atom/movable/proc/RepelAirflowDest(strength)
+ if (!prepare_airflow(strength))
+ airflow_dest = null
+ return
+ airflow_xo = -(airflow_dest.x - x)
+ airflow_yo = -(airflow_dest.y - y)
+ airflow_dest = null
+ SSairflow.Enqueue(src)
+
#undef AIRBORNE_DAMAGE
diff --git a/code/modules/atmospherics/ZAS/Connection.dm b/code/modules/atmospherics/ZAS/Connection.dm
index 8ea204dbfab7..5bf97dac4806 100644
--- a/code/modules/atmospherics/ZAS/Connection.dm
+++ b/code/modules/atmospherics/ZAS/Connection.dm
@@ -236,4 +236,5 @@ Class Procs:
#endif
/connection_edge/proc/queue_spacewind()
+ set waitfor = FALSE
return
diff --git a/code/modules/atmospherics/ZAS/ConnectionGroup.dm b/code/modules/atmospherics/ZAS/ConnectionGroup.dm
index 2af7cea9a2a2..a93100af2bf7 100644
--- a/code/modules/atmospherics/ZAS/ConnectionGroup.dm
+++ b/code/modules/atmospherics/ZAS/ConnectionGroup.dm
@@ -68,6 +68,8 @@ Class Procs:
///The last time the "woosh" airflow sound played, world.time
var/last_woosh
+ /// Prevents spacewind from happening until the last one is done.
+ var/flowing = FALSE
#ifdef ZASDBG
///Set this to TRUE during testing to get verbose debug information.
@@ -126,44 +128,80 @@ Class Procs:
///Airflow proc causing all objects in movable to be checked against a pressure differential. See file header for more info.
/connection_edge/proc/flow(list/movable, differential, repelled)
- set waitfor = FALSE
for(var/atom/movable/M as anything in movable)
//Non simulated objects dont get tossed
if(!M.simulated)
continue
//If they're already being tossed, don't do it again.
- if(M.last_airflow > world.time - zas_settings.airflow_delay)
+ if(!COOLDOWN_FINISHED(M, airflow_push_cooldown))
continue
if(M.airflow_speed)
continue
//Check for knocking people over
+ var/send_message = FALSE
if(ismob(M) && differential > zas_settings.airflow_stun_pressure)
- if(M:status_flags & GODMODE)
+ var/mob/living/living_mob = M
+ if(living_mob.status_flags & GODMODE)
continue
- if(!M:airflow_stun())
- to_chat(M, span_notice("A gust of air rushes past you."))
- if(M.check_airflow_movable(differential))
- //Check for things that are in range of the midpoint turfs.
- var/list/close_turfs = list()
- for(var/turf/T as anything in connecting_turfs)
- if(get_dist(M, T) < world.view)
- close_turfs += T
+ if(!living_mob.airflow_stun())
+ send_message = TRUE
- if(!length(close_turfs))
- continue
+ if(!M.can_airflow_move(differential))
+ if(send_message)
+ to_chat(M, span_warning("A gust of air rushes past you."))
+ continue
+
+ //Check for things that are in range of the midpoint turfs.
+ var/list/close_turfs = list()
+ for(var/turf/T as anything in connecting_turfs)
+ if(get_dist(M, T) < world.view)
+ close_turfs += T
+
+ if(!length(close_turfs))
+ continue
+
+ if(HAS_TRAIT(M, TRAIT_EXPERIENCING_AIRFLOW))
+ SSairflow.Dequeue(M)
+
+ M.airflow_dest = pick(close_turfs) //Pick a random midpoint to fly towards.
+ if(M.airflow_dest == M.loc)
+ M.airflow_dest = null
- if(HAS_TRAIT(M, TRAIT_EXPERIENCING_AIRFLOW))
- SSairflow.Dequeue(M)
+ var/list/nearby_turfs = RANGE_TURFS(1, M) - M.loc
+ shuffle_inplace(nearby_turfs)
+ for(var/turf/open/open_turf as anything in nearby_turfs)
+ // A < B (Move away from B)
+ if(repelled)
+ if(open_turf.zone != A)
+ M.airflow_dest = open_turf
+ break
+ continue
- M.airflow_dest = pick(close_turfs) //Pick a random midpoint to fly towards.
+ // A > B (Move towards B)
+ if(istype(src, /connection_edge/zone))
+ var/connection_edge/zone/zone_edge = src
- if(repelled)
- M.RepelAirflowDest(differential/5)
- else
- M.GotoAirflowDest(differential/10)
+ if(open_turf.zone == zone_edge.B)
+ M.airflow_dest = open_turf
+ break
+ continue
+
+ // A > B (Move towards B)
+ if(!open_turf.zone)
+ M.airflow_dest = open_turf
+ break
+
+ if(!M.airflow_dest)
+ continue
+
+
+ if(repelled)
+ M.RepelAirflowDest(differential/5)
+ else
+ M.GotoAirflowDest(differential/10)
CHECK_TICK
@@ -213,8 +251,9 @@ Class Procs:
return
var/equiv = A.air.shareRatio(B.air, coefficient)
-
+ #ifndef UNIT_TESTS
queue_spacewind()
+ #endif
if(equiv)
if(direct)
@@ -234,21 +273,28 @@ Class Procs:
SSzas.excite_edge(src)
/connection_edge/zone/queue_spacewind()
+ if(flowing)
+ return
+
var/differential = A.air.returnPressure() - B.air.returnPressure()
- if(abs(differential) >= zas_settings.airflow_lightest_pressure)
- var/list/attracted
- var/list/repelled
- if(differential > 0)
- attracted = A.movables()
- repelled = B.movables()
- else
- attracted = B.movables()
- repelled = A.movables()
+ if(abs(differential) < zas_settings.airflow_lightest_pressure)
+ return
+
+ flowing = TRUE
+ var/list/attracted
+ var/list/repelled
+ if(differential > 0)
+ attracted = A.movables()
+ repelled = B.movables()
+ else
+ attracted = B.movables()
+ repelled = A.movables()
- WOOSH
+ WOOSH
- flow(attracted, abs(differential), 0)
- flow(repelled, abs(differential), 1)
+ flow(attracted, abs(differential), 0)
+ flow(repelled, abs(differential), 1)
+ flowing = FALSE
/connection_edge/unsimulated
var/turf/B
@@ -299,7 +345,9 @@ Class Procs:
var/equiv = A.air.shareSpace(air)
+ #ifndef UNIT_TESTS
queue_spacewind()
+ #endif
if(equiv)
A.air.copyFrom(air)
@@ -315,17 +363,19 @@ Class Procs:
SSzas.excite_edge(src)
/connection_edge/unsimulated/queue_spacewind()
- #ifdef UNIT_TESTS
- return
- #endif
-
+ if(flowing)
+ return
var/differential = A.air.returnPressure() - air.returnPressure()
- if(abs(differential) >= zas_settings.airflow_lightest_pressure)
- var/list/attracted = A.movables()
+ if(abs(differential) < zas_settings.airflow_lightest_pressure)
+ return
+
+ flowing = TRUE
+ var/list/attracted = A.movables()
- WOOSH
+ WOOSH
- flow(attracted, abs(differential), differential < 0)
+ flow(attracted, abs(differential), differential < 0)
+ flowing = FALSE
/proc/ShareHeat(datum/gas_mixture/A, datum/gas_mixture/B, connecting_tiles)
//This implements a simplistic version of the Stefan-Boltzmann law.
diff --git a/code/modules/atmospherics/ZAS/ConnectionManager.dm b/code/modules/atmospherics/ZAS/ConnectionManager.dm
index c54099da70d8..ea8ca0fab01f 100644
--- a/code/modules/atmospherics/ZAS/ConnectionManager.dm
+++ b/code/modules/atmospherics/ZAS/ConnectionManager.dm
@@ -31,9 +31,6 @@ Macros:
*/
-// macro-ized to cut down on proc calls
-#define check(c) (c && c.valid())
-
/turf
var/tmp/connection_manager/connections
@@ -48,28 +45,30 @@ Macros:
var/connection/D
#endif
-/connection_manager/proc/get(d)
+/// Return a valid connection for a given direction.
+/connection_manager/proc/get_connection_for_dir(d)
switch(d)
if(NORTH)
- if(check(N)) return N
- else return null
+ if(N?.valid())
+ return N
+
if(SOUTH)
- if(check(S)) return S
- else return null
+ if(S?.valid())
+ return S
if(EAST)
- if(check(E)) return E
- else return null
+ if(E?.valid())
+ return E
if(WEST)
- if(check(W)) return W
- else return null
+ if(W?.valid())
+ return W
#ifdef MULTIZAS
if(UP)
- if(check(U)) return U
- else return null
+ if(U?.valid())
+ return U
if(DOWN)
- if(check(D)) return D
- else return null
+ if(D?.valid())
+ return D
#endif
/connection_manager/proc/place(connection/c, d)
@@ -85,23 +84,22 @@ Macros:
#endif
/connection_manager/proc/update_all()
- if(check(N)) N.update()
- if(check(S)) S.update()
- if(check(E)) E.update()
- if(check(W)) W.update()
+ if(N?.valid()) N.update()
+ if(S?.valid()) S.update()
+ if(E?.valid()) E.update()
+ if(W?.valid()) W.update()
#ifdef MULTIZAS
- if(check(U)) U.update()
- if(check(D)) D.update()
+ if(U?.valid()) U.update()
+ if(D?.valid()) D.update()
#endif
/connection_manager/proc/erase_all()
- if(check(N)) N.erase()
- if(check(S)) S.erase()
- if(check(E)) E.erase()
- if(check(W)) W.erase()
+ if(N?.valid()) N.erase()
+ if(S?.valid()) S.erase()
+ if(E?.valid()) E.erase()
+ if(W?.valid()) W.erase()
#ifdef MULTIZAS
- if(check(U)) U.erase()
- if(check(D)) D.erase()
+ if(U?.valid()) U.erase()
+ if(D?.valid()) D.erase()
#endif
-#undef check
diff --git a/code/modules/atmospherics/ZAS/Plasma.dm b/code/modules/atmospherics/ZAS/Plasma.dm
index 693e989b7b84..b48775de1407 100644
--- a/code/modules/atmospherics/ZAS/Plasma.dm
+++ b/code/modules/atmospherics/ZAS/Plasma.dm
@@ -1,14 +1,6 @@
GLOBAL_DATUM_INIT(contamination_overlay, /image, image('modular_pariah/master_files/icons/effects/contamination.dmi'))
/datum/pl_control
- var/plasma_dmg = 3
- var/plasma_dmg_name = "Plasma Damage Amount"
- var/plasma_dmg_desc = "Self Descriptive"
-
- var/plasmaguard_only = FALSE
- var/plamaguard_only_name = "\"PlasmaGuard Only\""
- var/plasmaguard_only_desc = "If this is on, only biosuits and spacesuits protect against contamination and ill effects."
-
var/genetic_corruption = 0
var/genetic_corruption_name = "Genetic Corruption Chance"
var/genetic_corruption_desc = "Chance of genetic corruption, X in 1,000."
@@ -28,7 +20,7 @@ GLOBAL_DATUM_INIT(contamination_overlay, /image, image('modular_pariah/master_fi
///Handles all the bad things phoron can do.
/mob/living/carbon/human/expose_plasma(exposed_amount)
//Anything else requires them to not be dead.
- if(stat >= 2)
+ if(stat == DEAD)
return
//Burn eyes if exposed.
@@ -49,34 +41,3 @@ GLOBAL_DATUM_INIT(contamination_overlay, /image, image('modular_pariah/master_fi
if(rand(1, 1000) < zas_settings.plc.genetic_corruption * exposed_amount)
easy_random_mutate(NEGATIVE)
to_chat(src, "High levels of toxins cause you to spontaneously mutate!")
-
-/mob/living/carbon/human/proc/pl_head_protected()
- //Checks if the head is adequately sealed.
- if(head)
- if(zas_settings.plc.plasmaguard_only)
- if(head.obj_flags & PLASMAGUARD)
- return TRUE
- else if(is_eyes_covered())
- return TRUE
- return FALSE
-
-/mob/living/carbon/human/proc/pl_suit_protected()
- //Checks if the suit is adequately sealed.
- var/coverage = NONE
- for(var/obj/item/protection in list(wear_suit, gloves, shoes))
- if(!protection)
- continue
- if(istype(protection, /obj/item/clothing))
- var/obj/item/clothing/clothing_item = protection
- if(zas_settings.plc.plasmaguard_only && !((clothing_item.clothing_flags & THICKMATERIAL) || (clothing_item.clothing_flags & GAS_FILTERING) || (clothing_item.obj_flags & PLASMAGUARD)))
- return FALSE
-
- else if(zas_settings.plc.plasmaguard_only && !(protection.obj_flags & PLASMAGUARD))
- return FALSE
-
- coverage |= protection.body_parts_covered
-
- if(zas_settings.plc.plasmaguard_only)
- return TRUE
-
- return (~(coverage) & (CHEST|LEGS|FEET|ARMS|HANDS) == 0)
diff --git a/code/modules/atmospherics/ZAS/ZAS_Settings.dm b/code/modules/atmospherics/ZAS/ZAS_Settings.dm
index 183d1f2b3326..f019576595cd 100644
--- a/code/modules/atmospherics/ZAS/ZAS_Settings.dm
+++ b/code/modules/atmospherics/ZAS/ZAS_Settings.dm
@@ -1,102 +1,52 @@
+#define SETTING_DEF(var_name, default_value, name, description, category) \
+ var/##var_name = default_value;\
+ var/name_##var_name = name;\
+ var/desc_##var_name = description;\
+ var/cat_##var_name = category;\
+
/datum/zas_controller
var/datum/pl_control/plc = new
- var/fire_consumption_rate = 0.25
- var/fire_consumption_rate_NAME = "Fire - Air Consumption Ratio"
- var/fire_consumption_rate_DESC = "Ratio of air removed and combusted per tick."
-
- var/fire_firelevel_multiplier = 25
- var/fire_firelevel_multiplier_NAME = "Fire - Firelevel Constant"
- var/fire_firelevel_multiplier_DESC = "Multiplied by the equation for firelevel, affects mainly the extingiushing of fires."
-
- //Note that this parameter and the phoron heat capacity have a significant impact on TTV yield.
- var/fire_fuel_energy_release = 866000 //J/mol. Adjusted to compensate for fire energy release being fixed, was 397000
- var/fire_fuel_energy_release_NAME = "Fire - Fuel energy release"
- var/fire_fuel_energy_release_DESC = "The energy in joule released when burning one mol of a burnable substance"
-
-
- var/IgnitionLevel = 0.5
- var/IgnitionLevel_DESC = "Determines point at which fire can ignite"
-
- var/airflow_lightest_pressure = 20
- var/airflow_lightest_pressure_NAME = "Airflow - Small Movement Threshold %"
- var/airflow_lightest_pressure_DESC = "Percent of 1 Atm. at which items with the small weight classes will move."
-
- var/airflow_light_pressure = 35
- var/airflow_light_pressure_NAME = "Airflow - Medium Movement Threshold %"
- var/airflow_light_pressure_DESC = "Percent of 1 Atm. at which items with the medium weight classes will move."
-
- var/airflow_medium_pressure = 50
- var/airflow_medium_pressure_NAME = "Airflow - Heavy Movement Threshold %"
- var/airflow_medium_pressure_DESC = "Percent of 1 Atm. at which items with the largest weight classes will move."
-
- var/airflow_heavy_pressure = 65
- var/airflow_heavy_pressure_NAME = "Airflow - Mob Movement Threshold %"
- var/airflow_heavy_pressure_DESC = "Percent of 1 Atm. at which mobs will move."
-
- var/airflow_dense_pressure = 85
- var/airflow_dense_pressure_NAME = "Airflow - Dense Movement Threshold %"
- var/airflow_dense_pressure_DESC = "Percent of 1 Atm. at which items with canisters and closets will move."
-
- var/airflow_stun_pressure = 60
- var/airflow_stun_pressure_NAME = "Airflow - Mob Stunning Threshold %"
- var/airflow_stun_pressure_DESC = "Percent of 1 Atm. at which mobs will be stunned by airflow."
-
- var/airflow_stun_cooldown = 60
- var/airflow_stun_cooldown_NAME = "Aiflow Stunning - Cooldown"
- var/airflow_stun_cooldown_DESC = "How long, in tenths of a second, to wait before stunning them again."
-
- var/airflow_stun = 1 SECONDS
- var/airflow_stun_NAME = "Airflow Impact - Stunning"
- var/airflow_stun_DESC = "How much a mob is stunned when hit by an object."
-
- var/airflow_damage = 3
- var/airflow_damage_NAME = "Airflow Impact - Damage"
- var/airflow_damage_DESC = "Damage from airflow impacts."
-
- var/airflow_speed_decay = 1.5
- var/airflow_speed_decay_NAME = "Airflow Speed Decay"
- var/airflow_speed_decay_DESC = "How rapidly the speed gained from airflow decays."
-
- var/airflow_delay = 30
- var/airflow_delay_NAME = "Airflow Retrigger Delay"
- var/airflow_delay_DESC = "Time in deciseconds before things can be moved by airflow again."
-
- var/airflow_mob_slowdown = 1
- var/airflow_mob_slowdown_NAME = "Airflow Slowdown"
- var/airflow_mob_slowdown_DESC = "Time in tenths of a second to add as a delay to each movement by a mob if they are fighting the pull of the airflow."
-
- var/connection_insulation = 1
- var/connection_insulation_NAME = "Connections - Insulation"
- var/connection_insulation_DESC = "Boolean, should doors forbid heat transfer?"
-
- var/connection_temperature_delta = 10
- var/connection_temperature_delta_NAME = "Connections - Temperature Difference"
- var/connection_temperature_delta_DESC = "The smallest temperature difference which will cause heat to travel through doors."
-
- var/maxex_devastation_range = 4
- var/max_explosion_range_NAME = "Explosion Devastation Range"
- var/max_explosion_range_DESC = "By default, 1/4th of fire range."
-
- var/maxex_heavy_range = 8
- var/max_heavy_range_NAME = "Explosion Heavy Range"
- var/max_heavy_range_DESC = "By default, 1/2 of light range"
-
- var/maxex_light_range = 16
- var/max_light_range_NAME = "Explosion Light Range"
- var/max_light_range_DESC = "By default, this is the baseline for other explosion values."
-
- var/maxex_fire_range = 16
- var/max_fire_range_NAME = "Explosion Fire Range"
- var/max_fire_range_DESC = "By default, equal to light range."
-
- var/maxex_flash_range = 20
- var/max_flash_range_NAME = "Explosion Flash Range"
- var/max_flash_range_DESC = "By default, 5/4ths of light range."
-
- var/airflow_speed_for_density = 5
- var/airflow_speed_for_density_NAME = "Airflow Speed For Density"
- var/airflow_speed_for_density_DESC = "The speed an object must be moving at to become dense to hit other objects"
+ //* Fire Settings *//
+ SETTING_DEF(fire_consumption_rate, 0.25, "Air Consumption Ratio", "Ratio of air removed and combusted per tick.", "Fire")
+ SETTING_DEF(fire_firelevel_multiplier, 25, "Firelevel Constant", "Multiplied by the equation for firelevel, affects mainly the extingiushing of fires.", "Fire")
+ /// Note that this parameter and the phoron heat capacity have a significant impact on TTV yield.
+ /// Value is in J/mol.
+ SETTING_DEF(fire_fuel_energy_release, 866000, "Fuel energy release", "The energy in joule released when burning one mol of a burnable substance", "Fire")
+
+ //* Airflow Settings *//
+ SETTING_DEF(airflow_lightest_pressure, 20, "Small Movement Threshold %", "Percent of 1 Atm. at which items with the small weight classes will move.", "Airflow")
+ SETTING_DEF(airflow_light_pressure, 35, "Medium Movement Threshold %", "Percent of 1 Atm. at which items with the medium weight classes will move.", "Airflow")
+ SETTING_DEF(airflow_medium_pressure, 50, "Heavy Movement Threshold %", "Percent of 1 Atm. at which items with the largest weight classes will move.", "Airflow")
+ SETTING_DEF(airflow_mob_pressure, 65, "Mob Movement Threshold %", "Percent of 1 Atm. at which mobs will move.", "Airflow")
+ SETTING_DEF(airflow_dense_pressure, 85, "Dense Movement Threshold %", "Percent of 1 Atm. at which dense objs can move.", "Airflow")
+ SETTING_DEF(airflow_speed_decay, 1.5, "Speed Decay Rate", "Speed removed from an airborne object per tick.", "Airflow")
+ SETTING_DEF(airflow_speed_for_density, 5, "Force Density Speed", "The speed value required to be able to impact objects during airflow, if not normally dense.", "Airflow")
+ SETTING_DEF(airflow_retrigger_delay, 0 SECONDS, "Airflow Retrigger Grace", "After being affected by spacewind, wait this long before affecting again.", "Airflow")
+ SETTING_DEF(airflow_mob_slowdown, 1, "Mob Slowdown", "Additive slowdown applied to mobs affected by spacewind.", "Airflow")
+
+ SETTING_DEF(airflow_stun_pressure, 60, "Mob Stunning Threshold %", "Percent of 1 Atm. at which mobs will be stunned by airflow.", "Airflow Impact")
+ SETTING_DEF(airflow_stun, 1 SECOND, "Stun Duration", "How long a mob is stunned when hit by an airborne object.", "Airflow Impact")
+ SETTING_DEF(airflow_stun_cooldown, 1 SECOND, "Stun Cooldown", "How long, in tenths of a second, to wait before stunning them again.", "Airflow Impact")
+ SETTING_DEF(airflow_damage, 3, "Damage", "Damage from airflow impacts.", "Airflow Impact")
+
+
+ //* Bomb Settings *//
+ SETTING_DEF(maxex_devastation_range, 4, "Devastation Cap", "By default, 1/4th of fire range.", "Bomb")
+ SETTING_DEF(maxex_heavy_range, 8, "Heavy Cap", "By default, 1/2 of light range.", "Bomb")
+ SETTING_DEF(maxex_light_range, 18, "Light Cap", "By default, this is the baseline for other explosion values.", "Bomb")
+ SETTING_DEF(maxex_fire_range, 16, "Fire Cap", "By default, equal to light range.", "Bomb")
+ SETTING_DEF(maxex_flash_range, 20, "Flash Cap", "By default, 5/4ths of light range.", "Bomb")
+
+ /// See /proc/compile_vars()
+ var/list/edittable_vars = list()
+
+/datum/zas_controller/New()
+ compile_vars()
+
+// Reject all VV edits, use the panel.
+/datum/zas_controller/vv_edit_var(var_name, var_value)
+ return FALSE
/datum/zas_controller/proc/set_bomb_cap(val)
if(!isnum(val))
@@ -107,3 +57,80 @@
maxex_light_range = val
maxex_fire_range = val
maxex_flash_range = val*1.2
+
+/datum/zas_controller/proc/compile_vars()
+ edittable_vars += nameof(fire_consumption_rate)
+ edittable_vars += nameof(fire_firelevel_multiplier)
+ edittable_vars += nameof(fire_fuel_energy_release)
+
+ edittable_vars += nameof(airflow_lightest_pressure)
+ edittable_vars += nameof(airflow_light_pressure)
+ edittable_vars += nameof(airflow_medium_pressure)
+ edittable_vars += nameof(airflow_mob_pressure)
+ edittable_vars += nameof(airflow_dense_pressure)
+ edittable_vars += nameof(airflow_speed_decay)
+ edittable_vars += nameof(airflow_speed_for_density)
+ edittable_vars += nameof(airflow_retrigger_delay)
+ edittable_vars += nameof(airflow_mob_slowdown)
+
+ edittable_vars += nameof(airflow_stun_pressure)
+ edittable_vars += nameof(airflow_stun)
+ edittable_vars += nameof(airflow_stun_cooldown)
+ edittable_vars += nameof(airflow_damage)
+
+ edittable_vars += nameof(maxex_devastation_range)
+ edittable_vars += nameof(maxex_heavy_range)
+ edittable_vars += nameof(maxex_light_range)
+ edittable_vars += nameof(maxex_fire_range)
+ edittable_vars += nameof(maxex_flash_range)
+
+/datum/zas_controller/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "ZasSettings")
+ ui.open()
+
+/datum/zas_controller/ui_state(mob/user)
+ return GLOB.admin_state
+
+/datum/zas_controller/ui_data(mob/user)
+ var/list/data = list()
+ var/list/settings = list()
+ var/list/categories = list()
+ data["settings"] = settings
+ data["categories"] = categories
+
+ for(var/var_name in edittable_vars)
+ var/var_category = vars["cat_[var_name]"]
+ settings[var_name] = list(
+ "value" = vars[var_name],
+ "name" = vars["name_[var_name]"],
+ "desc" = vars["desc_[var_name]"],
+ "category" = var_category,
+ )
+
+ categories |= var_category
+
+ return data
+
+
+/datum/zas_controller/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+ if(.)
+ return
+
+ switch(action)
+ if("change_var")
+ var/var_name = params["var_name"]
+ var/var_human_name = params["var_human_name"]
+ if(!(var_name in edittable_vars))
+ return TRUE
+
+ var/new_val = tgui_input_number(ui.user, "Change [var_human_name]", "ZAS Settings", vars[var_name], round_value = FALSE)
+ if(isnull(new_val))
+ return
+
+ log_admin("[key_name_admin(ui.user)] updated ZAS setting [var_human_name]: [vars[var_name]] -> [new_val]")
+ message_admins("[key_name_admin(ui.user)] updated ZAS setting [var_human_name]: [vars[var_name]] -> [new_val]")
+ vars[var_name] = new_val
+ return TRUE
diff --git a/code/modules/atmospherics/machinery/atmosmachinery.dm b/code/modules/atmospherics/machinery/atmosmachinery.dm
index 072a901ad199..eeefaccfbe40 100644
--- a/code/modules/atmospherics/machinery/atmosmachinery.dm
+++ b/code/modules/atmospherics/machinery/atmosmachinery.dm
@@ -393,7 +393,7 @@ GLOBAL_REAL_VAR(atmos_machinery_default_armor) = list(BLUNT = 25, PUNCTURE = 10,
to_chat(user, span_warning("As you begin unwrenching \the [src] a gush of air blows in your face... maybe you should reconsider?"))
unsafe_wrenching = TRUE //Oh dear oh dear
- if(I.use_tool(src, user, 20, volume=50))
+ if(I.use_tool(src, user, 2 SECONDS, volume=50))
user.visible_message( \
"[user] unfastens \the [src].", \
span_notice("You unfasten \the [src]."), \
@@ -428,12 +428,22 @@ GLOBAL_REAL_VAR(atmos_machinery_default_armor) = list(BLUNT = 25, PUNCTURE = 10,
/obj/machinery/atmospherics/proc/unsafe_pressure_release(mob/user, pressures = null)
if(!user)
return
+
if(!pressures)
var/datum/gas_mixture/int_air = return_air()
var/datum/gas_mixture/env_air = loc.return_air()
pressures = int_air.returnPressure() - env_air.returnPressure()
- user.visible_message(span_danger("[user] is sent flying by pressure!"),span_userdanger("The pressure sends you flying!"))
+ if(pressures < 0)
+ return
+
+ if(!user.can_airflow_move(pressures))
+ return
+
+ user.visible_message(
+ span_danger("[user] is sent flying by an explosion of pressure!"),
+ span_hear("You hear a gaseous hiss, followed by a thud.")
+ )
// if get_dir(src, user) is not 0, target is the edge_target_turf on that dir
// otherwise, edge_target_turf uses a random cardinal direction
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index f534b01a772e..a331d482f2ea 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -1237,7 +1237,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
humi.adjust_coretemperature(skin_core_change)
// get the enviroment details of where the mob is standing
- var/datum/gas_mixture/environment = humi.loc?.return_air()
+ var/datum/gas_mixture/environment = humi.loc?.unsafe_return_air()
if(!environment) // if there is no environment (nullspace) drop out here.
return
diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm
index 1845ed251b69..3ac30a8728ec 100644
--- a/code/modules/mob/living/life.dm
+++ b/code/modules/mob/living/life.dm
@@ -64,7 +64,7 @@
handle_random_events(delta_time, times_fired)
//Handle temperature/pressure differences between body and environment
- var/datum/gas_mixture/environment = loc.return_air()
+ var/datum/gas_mixture/environment = loc.unsafe_return_air()
if(environment)
if(handle_environment(environment, delta_time, times_fired))
updatehealth()
@@ -98,7 +98,7 @@
/mob/living/proc/handle_chemicals()
return
-// Base mob environment handler for body temperature
+// Base mob environment handler for body temperature. This proc assumes you're not changing the air mix, so don't do it!
/mob/living/proc/handle_environment(datum/gas_mixture/environment, delta_time, times_fired)
var/loc_temp = get_temperature(environment)
var/temp_delta = loc_temp - bodytemperature
diff --git a/tgui/packages/tgui/interfaces/ZasSettings.tsx b/tgui/packages/tgui/interfaces/ZasSettings.tsx
new file mode 100644
index 000000000000..240fbc4acbb0
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/ZasSettings.tsx
@@ -0,0 +1,91 @@
+import { toTitleCase } from 'common/string';
+
+import { useBackend, useLocalState } from '../backend';
+import { Button, Flex, Section, Table, Tabs, Tooltip } from '../components';
+import { Window } from '../layouts';
+
+type ZasSetting = {
+ category: string;
+ desc: string;
+ name: string;
+ value: number;
+};
+
+type ZasData = {
+ categories: string[];
+ settings: ZasSetting[];
+};
+
+export const ZasSettings = (props) => {
+ const { data } = useBackend();
+
+ const [tab, setTab] = useLocalState('tab', 0);
+ const settings = data.settings;
+ const categories = data.categories;
+ return (
+
+
+
+ {categories.map((category_name, i) => (
+ setTab(i)}>
+ {category_name}
+
+ ))}
+
+
+
+
+ );
+};
+
+export const ZasTab = (props) => {
+ const { act, data } = useBackend();
+ const [tab, setTab] = useLocalState('tab', 0);
+ const settings = data.settings;
+ const selected_category = data.categories[tab];
+
+ return (
+
+
+ {Object.values(settings).map(
+ (setting, i) =>
+ setting.category === selected_category && (
+
+
+
+
+
+ {setting.name}
+
+
+
+ {setting.value.toString()}
+
+
+
+
+
+
+
+ ),
+ )}
+
+
+ );
+};
+
+const getKeyByValue = (obj, value) =>
+ Object.keys(obj).find((key) => obj[key] === value);