From c4542c2085bd7af578802f3a4e9167f2b0cfc81d Mon Sep 17 00:00:00 2001 From: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Date: Thu, 27 Nov 2025 07:22:11 -0600 Subject: [PATCH] Maybe makes stuff like zombie powder more consistent / less buggy with status effects (#93836) --- code/__DEFINES/status_effects.dm | 1 - code/datums/status_effects/reagent_effect.dm | 84 +++++++++++++++++++ .../chemistry/reagents/impure_reagents.dm | 17 +--- .../chemistry/reagents/toxin_reagents.dm | 4 +- .../crossbreeding/_status_effects.dm | 31 ++++--- tgstation.dme | 1 + 6 files changed, 108 insertions(+), 30 deletions(-) create mode 100644 code/datums/status_effects/reagent_effect.dm diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm index ceecdb729da..4b0767091b9 100644 --- a/code/__DEFINES/status_effects.dm +++ b/code/__DEFINES/status_effects.dm @@ -57,7 +57,6 @@ // Grouped effect sources, see also code/__DEFINES/traits.dm #define STASIS_MACHINE_EFFECT "stasis_machine" -#define STASIS_CHEMICAL_EFFECT "stasis_chemical" #define STASIS_SHAPECHANGE_EFFECT "stasis_shapechange" #define STASIS_ADMIN "stasis_admin" #define STASIS_LEGION_EATEN "stasis_eaten" diff --git a/code/datums/status_effects/reagent_effect.dm b/code/datums/status_effects/reagent_effect.dm new file mode 100644 index 00000000000..82475d90dd2 --- /dev/null +++ b/code/datums/status_effects/reagent_effect.dm @@ -0,0 +1,84 @@ +/// Status effect that is tied to the existence of a reagent in a mob's system +/datum/status_effect/reagent_effect + abstract_type = /datum/status_effect/reagent_effect + id = STATUS_EFFECT_ID_ABSTRACT + alert_type = null + tick_interval = STATUS_EFFECT_NO_TICK + /// We need this reagent type + var/reagent_typepath + /// Whether subtypes of the reagent are allowed to keep the effect active + var/subtypes_allowed + +/datum/status_effect/reagent_effect/on_creation(mob/living/new_owner, reagent_typepath, subtypes_allowed = TRUE) + if(isnull(src.reagent_typepath)) + if(isnull(reagent_typepath)) + stack_trace("Reagent effect [src] created without a reagent typepath!") + src.reagent_typepath = reagent_typepath + if(isnull(src.subtypes_allowed)) + src.subtypes_allowed = subtypes_allowed + return ..() + +/datum/status_effect/reagent_effect/on_apply() + if(isnull(owner.reagents) || isnull(reagent_typepath) || !can_effect()) + return FALSE + + RegisterSignal(owner.reagents, COMSIG_REAGENTS_HOLDER_UPDATED, PROC_REF(check_reagents)) + add_effect() + return TRUE + +/datum/status_effect/reagent_effect/on_remove() + remove_effect() + UnregisterSignal(owner.reagents, COMSIG_REAGENTS_HOLDER_UPDATED) + +/datum/status_effect/reagent_effect/proc/check_reagents(datum/reagents/updated_reagents) + SIGNAL_HANDLER + + if(owner.reagents?.has_reagent(reagent_typepath, check_subtypes = subtypes_allowed)) + return + qdel(src) + +/// Can we add this effect to the owner? +/datum/status_effect/reagent_effect/proc/can_effect() + return TRUE + +/// Add the side effect to the owner +/datum/status_effect/reagent_effect/proc/add_effect() + return + +/// Remove the side effect from the owner +/datum/status_effect/reagent_effect/proc/remove_effect() + return + +/datum/status_effect/reagent_effect/fakedeath + id = "reagent_fake_death" + +/datum/status_effect/reagent_effect/fakedeath/add_effect() + owner.fakedeath(type) + +/datum/status_effect/reagent_effect/fakedeath/remove_effect() + owner.cure_fakedeath(type) + +/datum/status_effect/reagent_effect/freeze + id = "reagent_freeze" + +/datum/status_effect/reagent_effect/freeze/can_effect() + return !HAS_TRAIT(owner, TRAIT_RESISTCOLD) + +/datum/status_effect/reagent_effect/freeze/add_effect() + owner.apply_status_effect(/datum/status_effect/frozenstasis/irresistable) + owner.apply_status_effect(/datum/status_effect/grouped/stasis, type) + RegisterSignal(owner, COMSIG_MOVABLE_MOVED, PROC_REF(escape_prison)) + +/datum/status_effect/reagent_effect/freeze/remove_effect() + owner.remove_status_effect(/datum/status_effect/frozenstasis/irresistable) + owner.remove_status_effect(/datum/status_effect/grouped/stasis, type) + UnregisterSignal(owner, COMSIG_MOVABLE_MOVED) + +/datum/status_effect/reagent_effect/freeze/proc/escape_prison(...) + SIGNAL_HANDLER + + if(isturf(owner.loc)) // we escaped ice prison + owner.reagents?.del_reagent(reagent_typepath) + if(!QDELETED(src)) + stack_trace("Despite nuking the reagent from the mob, [owner] still has [type]") + qdel(src) diff --git a/code/modules/reagents/chemistry/reagents/impure_reagents.dm b/code/modules/reagents/chemistry/reagents/impure_reagents.dm index e21c0eada33..2330955b4c3 100644 --- a/code/modules/reagents/chemistry/reagents/impure_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/impure_reagents.dm @@ -106,30 +106,19 @@ holder.del_reagent(type) return - human_thing.apply_status_effect(/datum/status_effect/frozenstasis/irresistable) - if(!human_thing.has_status_effect(/datum/status_effect/grouped/stasis, STASIS_CHEMICAL_EFFECT)) - human_thing.apply_status_effect(/datum/status_effect/grouped/stasis, STASIS_CHEMICAL_EFFECT) + human_thing.apply_status_effect(/datum/status_effect/reagent_effect/freeze, type) /datum/reagent/inverse/cryostylane/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) . = ..() - if(!affected_mob.has_status_effect(/datum/status_effect/frozenstasis/irresistable)) - holder.remove_reagent(type, volume) // remove it all if we were broken out - return metabolization_rate += 0.01 //speed up our metabolism over time. Chop chop. /datum/reagent/inverse/cryostylane/metabolize_reagent(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) if(current_cycle >= 60) holder.remove_reagent(type, volume) // remove it all if we're past 60 cycles return + return ..() /datum/reagent/inverse/cryostylane/on_mob_end_metabolize(mob/living/affected_mob) . = ..() - affected_mob.remove_status_effect(/datum/status_effect/frozenstasis/irresistable) - affected_mob.remove_status_effect(/datum/status_effect/grouped/stasis, STASIS_CHEMICAL_EFFECT) - -/datum/reagent/inverse/cryostylane/on_mob_delete(mob/living/affected_mob, amount) - . = ..() - affected_mob.remove_status_effect(/datum/status_effect/frozenstasis/irresistable) - affected_mob.remove_status_effect(/datum/status_effect/grouped/stasis, STASIS_CHEMICAL_EFFECT) - + affected_mob.remove_status_effect(/datum/status_effect/reagent_effect/freeze) diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm index 4dc60c5c46f..3bdbfd45ece 100644 --- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm @@ -282,7 +282,7 @@ holder_mob.adjustOxyLoss(0.5*REM, FALSE, required_biotype = affected_biotype, required_respiration_type = affected_respiration_type) if((data?["method"] & (INGEST|INHALE)) && holder_mob.stat != DEAD) - holder_mob.fakedeath(type) + holder_mob.apply_status_effect(/datum/status_effect/reagent_effect/fakedeath, type) /datum/reagent/toxin/zombiepowder/on_mob_metabolize(mob/living/holder_mob) . = ..() @@ -290,7 +290,7 @@ /datum/reagent/toxin/zombiepowder/on_mob_end_metabolize(mob/living/affected_mob) . = ..() - affected_mob.cure_fakedeath(type) + affected_mob.remove_status_effect(/datum/status_effect/reagent_effect/fakedeath) /datum/reagent/toxin/zombiepowder/on_mob_life(mob/living/affected_mob, seconds_per_tick, times_fired) . = ..() diff --git a/code/modules/research/xenobiology/crossbreeding/_status_effects.dm b/code/modules/research/xenobiology/crossbreeding/_status_effects.dm index 8ed179a3207..40270fed287 100644 --- a/code/modules/research/xenobiology/crossbreeding/_status_effects.dm +++ b/code/modules/research/xenobiology/crossbreeding/_status_effects.dm @@ -98,34 +98,39 @@ status_type = STATUS_EFFECT_UNIQUE duration = STATUS_EFFECT_PERMANENT //Will remove self when block breaks. alert_type = /atom/movable/screen/alert/status_effect/freon/stasis + tick_interval = STATUS_EFFECT_NO_TICK /// The cube we will place our mob into. var/obj/structure/ice_stasis/cube /// Whether or not this version of the status effect can be resisted out of. var/resistable = TRUE /datum/status_effect/frozenstasis/on_apply() - if(resistable) - RegisterSignal(owner, COMSIG_LIVING_RESIST, PROC_REF(breakCube)) cube = new /obj/structure/ice_stasis(get_turf(owner)) owner.forceMove(cube) + RegisterSignal(cube, COMSIG_QDELETING, PROC_REF(clear_effect)) + RegisterSignal(owner, COMSIG_MOVABLE_MOVED, PROC_REF(has_escaped)) + if(resistable) + RegisterSignal(owner, COMSIG_LIVING_RESIST, PROC_REF(clear_effect)) ADD_TRAIT(owner, TRAIT_GODMODE, TRAIT_STATUS_EFFECT(id)) - return ..() + return TRUE -/datum/status_effect/frozenstasis/tick(seconds_between_ticks) - if(!cube || owner.loc != cube) - owner.remove_status_effect(src) - -/datum/status_effect/frozenstasis/proc/breakCube() +/datum/status_effect/frozenstasis/proc/clear_effect(...) SIGNAL_HANDLER - owner.remove_status_effect(src) + qdel(src) + +/datum/status_effect/frozenstasis/proc/has_escaped(...) + SIGNAL_HANDLER + + if(owner.loc != cube) + qdel(src) /datum/status_effect/frozenstasis/on_remove() - if(cube) - qdel(cube) REMOVE_TRAIT(owner, TRAIT_GODMODE, TRAIT_STATUS_EFFECT(id)) - if(resistable) - UnregisterSignal(owner, COMSIG_LIVING_RESIST) + UnregisterSignal(owner, COMSIG_MOVABLE_MOVED) + UnregisterSignal(owner, COMSIG_LIVING_RESIST) + UnregisterSignal(cube, COMSIG_QDELETING) + QDEL_NULL(cube) /datum/status_effect/frozenstasis/irresistable resistable = FALSE diff --git a/tgstation.dme b/tgstation.dme index c414f4cea3d..0a094ec8504 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1990,6 +1990,7 @@ #include "code\datums\status_effects\grouped_effect.dm" #include "code\datums\status_effects\limited_effect.dm" #include "code\datums\status_effects\neutral.dm" +#include "code\datums\status_effects\reagent_effect.dm" #include "code\datums\status_effects\song_effects.dm" #include "code\datums\status_effects\stacking_effect.dm" #include "code\datums\status_effects\wound_effects.dm"