Adds a bunch of owner.current nullchecks to antag code (#91524)

This could cause runtimes preventing antag datum removal in case your
body got destroyed. Ideally we'd unit test for this, but I can't get it
to work sanely without touching some dynamic code (due to some antag
datums requiring a bunch of snowflaky setups), so the unit test will
come after its refactored.

🆑
fix: Fixed some edge cases where antagonist datums would "stick" and not
get removed if your body got destroyed.
/🆑

---------

Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com>
This commit is contained in:
SmArtKar
2025-06-16 03:04:59 +02:00
committed by Roxy
parent a14a83b2c7
commit 33da2f07ea
19 changed files with 48 additions and 37 deletions
+4 -2
View File
@@ -34,14 +34,14 @@
var/datum/antagonist/A = has_antag_datum(datum_type)
if(A)
A.on_removal()
current.log_message("has lost antag datum [A.name]([A.type]).", LOG_GAME)
current?.log_message("has lost antag datum [A.name]([A.type]).", LOG_GAME)
return TRUE
/datum/mind/proc/remove_all_antag_datums() //For the Lazy amongst us.
for(var/a in antag_datums)
var/datum/antagonist/A = a
A.on_removal()
current.log_message("has lost all antag datums.", LOG_GAME)
current?.log_message("has lost all antag datums.", LOG_GAME)
/datum/mind/proc/has_antag_datum(datum_type, check_subtypes = TRUE)
if(!datum_type)
@@ -93,6 +93,8 @@
/datum/mind/proc/remove_antag_equip()
if(!current)
return
var/list/Mob_Contents = current.get_contents()
for(var/obj/item/I in Mob_Contents)
var/datum/component/uplink/O = I.GetComponent(/datum/component/uplink) //Todo make this reset signal
@@ -194,7 +194,8 @@ GLOBAL_LIST_EMPTY(antagonists)
///Called by the transfer_to() mind proc after the mind (mind.current and new_character.mind) has moved but before the player (key and client) is transferred.
/datum/antagonist/proc/on_body_transfer(mob/living/old_body, mob/living/new_body)
SHOULD_CALL_PARENT(TRUE)
remove_innate_effects(old_body)
if(old_body)
remove_innate_effects(old_body)
if(old_body && old_body.stat != DEAD && !LAZYLEN(old_body.mind?.antag_datums))
old_body.remove_from_current_living_antags()
var/datum/action/antag_info/info_button = info_button_ref?.resolve()
@@ -318,10 +319,11 @@ GLOBAL_LIST_EMPTY(antagonists)
if(!owner)
CRASH("Antag datum with no owner.")
remove_innate_effects()
if(owner.current)
remove_innate_effects()
clear_antag_moodies()
LAZYREMOVE(owner.antag_datums, src)
if(!LAZYLEN(owner.antag_datums))
if(!LAZYLEN(owner.antag_datums) && owner.current)
owner.current.remove_from_current_living_antags()
if(info_button_ref)
QDEL_NULL(info_button_ref)
@@ -362,7 +364,7 @@ GLOBAL_LIST_EMPTY(antagonists)
* Use this proc for playing sounds, sending alerts, or otherwise informing the player that they're no longer a specific antagonist type.
*/
/datum/antagonist/proc/farewell()
if(!silent)
if(!silent && owner.current)
to_chat(owner.current, span_userdanger("You are no longer \the [src]!"))
/**
@@ -34,6 +34,8 @@
/datum/antagonist/ashwalker/on_removal()
. = ..()
if(!owner.current)
return
UnregisterSignal(owner.current, COMSIG_MOB_EXAMINATE)
if(!(FACTION_NEUTRAL in owner.current.faction))
owner.current.faction.Add(FACTION_NEUTRAL)
@@ -242,7 +242,8 @@
return ..()
/datum/antagonist/changeling/farewell()
to_chat(owner.current, span_userdanger("You grow weak and lose your powers! You are no longer a changeling and are stuck in your current form!"))
if(owner.current)
to_chat(owner.current, span_userdanger("You grow weak and lose your powers! You are no longer a changeling and are stuck in your current form!"))
/*
* Instantiate the cellular emporium for the changeling.
@@ -24,7 +24,6 @@
/datum/antagonist/nukeop/clownop/remove_innate_effects(mob/living/mob_override)
var/mob/living/L = owner.current || mob_override
REMOVE_TRAIT(L.mind, TRAIT_NAIVE, CLOWNOP_TRAIT)
return ..()
/datum/antagonist/nukeop/clownop/equip_op()
. = ..()
@@ -56,7 +55,6 @@
/datum/antagonist/nukeop/leader/clownop/remove_innate_effects(mob/living/mob_override)
var/mob/living/L = owner.current || mob_override
REMOVE_TRAIT(L.mind, TRAIT_NAIVE, CLOWNOP_TRAIT)
return ..()
/datum/antagonist/nukeop/leader/clownop/equip_op()
. = ..()
@@ -39,6 +39,9 @@
current.log_message("has been converted to the cult of Nar'Sie!", LOG_ATTACK, color=COLOR_CULT_RED)
/datum/antagonist/cult/on_removal()
if (!owner.current)
return ..()
if(!silent)
owner.current.visible_message(span_deconversion_message("[owner.current] looks like [owner.current.p_theyve()] just reverted to [owner.current.p_their()] old faith!"), ignored_mobs = owner.current)
to_chat(owner.current, span_userdanger("An unfamiliar white light flashes through your mind, cleansing the taint of the Geometer and all your memories as her servant."))
@@ -238,7 +241,7 @@
/datum/antagonist/cult/proc/deathrattle(datum/source)
SIGNAL_HANDLER
if(owner.current.stat != DEAD)
if(owner.current?.stat != DEAD)
return
if(!QDELETED(GLOB.cult_narsie))
return
@@ -80,10 +80,9 @@
/datum/antagonist/fugitive_hunter/remove_innate_effects(mob/living/mob_override)
var/mob/living/owner_mob = mob_override || owner.current
//bubber edit; spinwarder removed
// BUBBER EDIT CHANGE BEGIN: spinwarder removed
owner_mob.remove_language(/datum/language/panslavic, source = LANGUAGE_BOUNTYHUNTER)
//edit end
return ..()
// BUBBER EDIT CHANGE END
/datum/team/fugitive_hunters
var/backstory = "error"
@@ -259,7 +259,7 @@
return finish_preview_icon(icon)
/datum/antagonist/heretic/farewell()
if(!silent)
if(!silent && owner.current)
to_chat(owner.current, span_userdanger("Your mind begins to flare as the otherwordly knowledge escapes your grasp!"))
return ..()
@@ -278,9 +278,10 @@
return ..()
/datum/antagonist/heretic/on_removal()
for(var/knowledge_index in researched_knowledge)
var/datum/heretic_knowledge/knowledge = researched_knowledge[knowledge_index]
knowledge.on_lose(owner.current, src)
if(owner.current)
for(var/knowledge_index in researched_knowledge)
var/datum/heretic_knowledge/knowledge = researched_knowledge[knowledge_index]
knowledge.on_lose(owner.current, src)
QDEL_LIST_ASSOC_VAL(researched_knowledge)
return ..()
+3 -4
View File
@@ -138,8 +138,6 @@
datum_owner.AddComponent(/datum/component/codeword_hearing, GLOB.syndicate_code_response_regex, "red", src)
/datum/antagonist/malf_ai/remove_innate_effects(mob/living/mob_override)
. = ..()
var/mob/living/silicon/ai/datum_owner = mob_override || owner.current
if(istype(datum_owner))
@@ -270,7 +268,8 @@
result += span_greentext("The [special_role_text] was successful!")
else
result += span_redtext("The [special_role_text] has failed!")
SEND_SOUND(owner.current, 'sound/ambience/misc/ambifailure.ogg')
if(owner.current)
SEND_SOUND(owner.current, 'sound/ambience/misc/ambifailure.ogg')
return result.Join("<br>")
@@ -288,7 +287,7 @@
SIGNAL_HANDLER
var/mob/living/silicon/ai/malf_owner = owner.current
if(malf_owner.linked_core)
if(malf_owner?.linked_core)
return COMPONENT_CORE_ALL_GOOD
return COMPONENT_CORE_DISCONNECTED
@@ -39,7 +39,7 @@
..()
/datum/objective/nightmare_fluff/check_completion()
return owner.current.stat != DEAD
return owner.current && owner.current.stat != DEAD
/datum/antagonist/nightmare/forge_objectives()
var/datum/objective/nightmare_fluff/objective = new
+2 -2
View File
@@ -48,8 +48,8 @@
/datum/antagonist/pirate/remove_innate_effects(mob/living/mob_override)
var/mob/living/owner_mob = mob_override || owner.current
owner_mob.remove_language(/datum/language/piratespeak, source = LANGUAGE_PIRATE)
return ..()
if (owner_mob)
owner_mob.remove_language(/datum/language/piratespeak, source = LANGUAGE_PIRATE)
/datum/team/pirate
name = "\improper Pirate crew"
@@ -18,7 +18,7 @@
explanation_text = "I am fire. I am hunger. The cold is agony. The living pulse with energy; their warmth fuels me. The dead are husks, their embers long faded. Water is death. Fire... fire is freedom."
/datum/objective/pyro_slime/check_completion()
return owner.current.stat != DEAD
return owner.current && owner.current.stat != DEAD
/datum/antagonist/pyro_slime/forge_objectives()
var/datum/objective/pyro_slime/objective = new
@@ -184,11 +184,14 @@
return COMPONENT_MINDSHIELD_RESISTED
/datum/antagonist/rev/head/on_removal()
if(give_hud)
var/mob/living/carbon/C = owner.current
var/obj/item/organ/cyberimp/eyes/hud/security/syndicate/S = C.get_organ_slot(ORGAN_SLOT_HUD)
if(S)
S.Remove(C)
if(!give_hud)
return ..()
var/mob/living/carbon/C = owner.current
if (!C)
return ..()
var/obj/item/organ/cyberimp/eyes/hud/security/syndicate/S = C.get_organ_slot(ORGAN_SLOT_HUD)
if(S)
S.Remove(C)
return ..()
/datum/antagonist/rev/head/apply_innate_effects(mob/living/mob_override)
@@ -310,6 +313,8 @@
to_chat(old_owner, span_userdanger("Revolution has been disappointed of your leader traits! You are a regular revolutionary now!"))
/datum/antagonist/rev/farewell()
if(!owner.current)
return
owner.current.balloon_alert_to_viewers("deconverted!")
if(ishuman(owner.current))
owner.current.visible_message(span_deconversion_message("[owner.current] looks like [owner.current.p_theyve()] just remembered [owner.current.p_their()] real allegiance!"), null, null, null, owner.current)
@@ -319,7 +324,7 @@
to_chat(owner, span_userdanger("The frame's firmware detects and deletes your neural reprogramming! You remember nothing but the name of the one who flashed you."))
/datum/antagonist/rev/head/farewell()
if (deconversion_source == DECONVERTER_STATION_WIN)
if (deconversion_source == DECONVERTER_STATION_WIN || !owner.current)
return
owner.current.balloon_alert_to_viewers("deconverted!")
if((ishuman(owner.current)))
+1 -1
View File
@@ -29,7 +29,7 @@
explanation_text = "Your queen has given you a directive! Follow it at all costs: [directive]"
/datum/objective/spider/check_completion()
return owner.current.stat != DEAD
return owner.current && owner.current.stat != DEAD
/datum/antagonist/spider/forge_objectives()
var/datum/objective/spider/objective = new(directive)
@@ -27,7 +27,7 @@
var/mob/living/monky_master
/datum/objective/syndicate_monkey/check_completion()
return monky_master.stat != DEAD
return monky_master && monky_master.stat != DEAD
/datum/antagonist/syndicate_monkey/forge_objectives(mob/monky_master)
var/datum/objective/syndicate_monkey/objective = new
@@ -209,7 +209,6 @@
datum_owner.AddComponent(/datum/component/codeword_hearing, GLOB.syndicate_code_response_regex, "red", src)
/datum/antagonist/traitor/remove_innate_effects(mob/living/mob_override)
. = ..()
var/mob/living/datum_owner = mob_override || owner.current
handle_clown_mutation(datum_owner, removing = FALSE)
@@ -18,7 +18,7 @@
explanation_text = "Spread the infestation."
/datum/objective/venus_human_trap/check_completion()
return owner.current.stat != DEAD
return owner.current && owner.current.stat != DEAD
/datum/antagonist/venus_human_trap/forge_objectives()
var/datum/objective/venus_human_trap/objective = new
@@ -57,4 +57,4 @@
..()
/datum/objective/voidwalker_objective/check_completion()
return owner.current.stat != DEAD
return owner.current && owner.current.stat != DEAD
+1 -1
View File
@@ -96,7 +96,7 @@
explanation_text = "Survive and advance the Hive."
/datum/objective/advance_hive/check_completion()
return owner.current.stat != DEAD
return owner.current && owner.current.stat != DEAD
///Captive Xenomorphs team
/datum/team/xeno/captive