diff --git a/code/controllers/subsystem/persistence/_persistence.dm b/code/controllers/subsystem/persistence/_persistence.dm
index d494561d0f..c9898f2976 100644
--- a/code/controllers/subsystem/persistence/_persistence.dm
+++ b/code/controllers/subsystem/persistence/_persistence.dm
@@ -335,7 +335,7 @@ SUBSYSTEM_DEF(persistence)
if(!istype(ending_human) || !ending_human.mind || !ending_human.client || !ending_human.client.prefs || !ending_human.client.prefs.persistent_scars)
continue
- var/mob/living/carbon/human/original_human = ending_human.mind.original_character
+ var/mob/living/carbon/human/original_human = ending_human.mind.original_character.resolve()
if(!original_human || original_human.stat == DEAD || !original_human.all_scars || !(original_human == ending_human))
if(ending_human.client) // i was told if i don't check this every step of the way byond might decide a client ceases to exist mid proc so here we go
ending_human.client.prefs.scars_list["[ending_human.client.prefs.scars_index]"] = ""
@@ -356,7 +356,7 @@ SUBSYSTEM_DEF(persistence)
if(!istype(ending_human) || !ending_human.mind || !ending_human.client || !ending_human.client.prefs || !ending_human.client.prefs.tcg_cards)
continue
- var/mob/living/carbon/human/original_human = ending_human.mind.original_character
+ var/mob/living/carbon/human/original_human = ending_human.mind.original_character.resolve()
if(!original_human || original_human.stat == DEAD || !(original_human == ending_human))
continue
diff --git a/code/datums/dash_weapon.dm b/code/datums/dash_weapon.dm
index f143fea419..adf7b72c11 100644
--- a/code/datums/dash_weapon.dm
+++ b/code/datums/dash_weapon.dm
@@ -6,7 +6,6 @@
var/current_charges = 1
var/max_charges = 1
var/charge_rate = 250
- var/mob/living/carbon/human/holder
var/obj/item/dashing_item
var/dash_sound = 'sound/magic/blink.ogg'
var/recharge_sound = 'sound/magic/charge.ogg'
@@ -17,7 +16,6 @@
/datum/action/innate/dash/Grant(mob/user, obj/dasher)
. = ..()
dashing_item = dasher
- holder = user
/datum/action/innate/dash/IsAvailable(silent = FALSE)
if(current_charges > 0)
@@ -26,7 +24,7 @@
return FALSE
/datum/action/innate/dash/Activate()
- dashing_item.attack_self(holder) //Used to toggle dash behavior in the dashing item
+ dashing_item.attack_self(owner) //Used to toggle dash behavior in the dashing item
/datum/action/innate/dash/proc/Teleport(mob/user, atom/target)
if(!IsAvailable())
@@ -38,12 +36,12 @@
var/obj/spot2 = new phasein(get_turf(user), user.dir)
spot1.Beam(spot2,beam_effect,time=20)
current_charges--
- holder.update_action_buttons_icon()
+ owner.update_action_buttons_icon()
addtimer(CALLBACK(src, PROC_REF(charge)), charge_rate)
/datum/action/innate/dash/proc/charge()
current_charges = clamp(current_charges + 1, 0, max_charges)
- holder.update_action_buttons_icon()
+ owner.update_action_buttons_icon()
if(recharge_sound)
playsound(dashing_item, recharge_sound, 50, 1)
- to_chat(holder, "[src] now has [current_charges]/[max_charges] charges.")
+ to_chat(owner, "[src] now has [current_charges]/[max_charges] charges.")
diff --git a/code/datums/dna.dm b/code/datums/dna.dm
index 83e2de80b9..ebec1017f7 100644
--- a/code/datums/dna.dm
+++ b/code/datums/dna.dm
@@ -27,12 +27,16 @@
/datum/dna/Destroy()
if(iscarbon(holder))
var/mob/living/carbon/cholder = holder
+ // We do this because a lot of stuff keeps references on species, for some reason.
+ species.on_species_loss(holder)
if(cholder.dna == src)
cholder.dna = null
holder = null
if(delete_species)
QDEL_NULL(species)
+ else
+ species = null
mutations.Cut() //This only references mutations, just dereference.
temporary_mutations.Cut() //^
diff --git a/code/datums/elements/photosynthesis.dm b/code/datums/elements/photosynthesis.dm
index 8e1c8c53ff..b54525fb6b 100644
--- a/code/datums/elements/photosynthesis.dm
+++ b/code/datums/elements/photosynthesis.dm
@@ -39,12 +39,13 @@
attached_atoms[target]++
/datum/element/photosynthesis/Detach(datum/target)
- attached_atoms[target]--
- if(!attached_atoms[target])
- attached_atoms -= target
- if(!length(attached_atoms))
- STOP_PROCESSING(SSobj, src)
- attached_atoms = null
+ if(LAZYLEN(attached_atoms))
+ attached_atoms[target]--
+ if(!attached_atoms[target])
+ attached_atoms -= target
+ if(!length(attached_atoms))
+ STOP_PROCESSING(SSobj, src)
+ attached_atoms = null
return ..()
/datum/element/photosynthesis/process()
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 5308ef3189..c05d04ede8 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -81,13 +81,13 @@
var/list/ambitions
//ambition end
- ///What character we spawned in as- either at roundstart or latejoin, so we know for persistent scars if we ended as the same person or not
- var/mob/original_character
+ ///Weakref to the character we spawned in as- either at roundstart or latejoin, so we know for persistent scars if we ended as the same person or not
+ var/datum/weakref/original_character
/// A lazy list of statuses to add next to this mind in the traitor panel
var/list/special_statuses
-/datum/mind/New(var/key)
+/datum/mind/New(key)
skill_holder = new(src)
src.key = key
soulOwner = src
@@ -95,15 +95,29 @@
/datum/mind/Destroy()
SSticker.minds -= src
- if(islist(antag_datums))
- for(var/i in antag_datums)
- var/datum/antagonist/antag_datum = i
- if(antag_datum.delete_on_mind_deletion)
- qdel(i)
- antag_datums = null
+ QDEL_LIST(antag_datums)
+ QDEL_NULL(language_holder)
QDEL_NULL(skill_holder)
+ set_current(null)
+ soulOwner = null
return ..()
+/datum/mind/proc/set_current(mob/new_current)
+ if(new_current && QDELETED(new_current))
+ CRASH("Tried to set a mind's current var to a qdeleted mob, what the fuck")
+ if(current)
+ UnregisterSignal(src, COMSIG_PARENT_QDELETING)
+ current = new_current
+ if(current)
+ RegisterSignal(src, COMSIG_PARENT_QDELETING, PROC_REF(clear_current))
+
+/datum/mind/proc/clear_current(datum/source)
+ SIGNAL_HANDLER
+ set_current(null)
+
+/datum/mind/proc/set_original_character(new_original_character)
+ original_character = WEAKREF(new_original_character)
+
/datum/mind/proc/get_language_holder()
if(!language_holder)
language_holder = new (src)
@@ -126,13 +140,13 @@
key = new_character.key
if(new_character.mind) //disassociate any mind currently in our new body's mind variable
- new_character.mind.current = null
+ new_character.mind.set_current(null)
var/datum/atom_hud/antag/hud_to_transfer = antag_hud//we need this because leave_hud() will clear this list
var/mob/living/old_current = current
if(current)
current.transfer_observers_to(new_character) //transfer anyone observing the old character to the new one
- current = new_character //associate ourself with our new body
+ set_current(new_character) //associate ourself with our new body
new_character.mind = src //and associate our new body with ourself
for(var/a in antag_datums) //Makes sure all antag datums effects are applied in the new body
var/datum/antagonist/A = a
@@ -1715,7 +1729,7 @@ GLOBAL_LIST(objective_choices)
SEND_SIGNAL(src, COMSIG_MOB_ON_NEW_MIND)
if(!mind.name)
mind.name = real_name
- mind.current = src
+ mind.set_current(src)
mind.hide_ckey = client?.prefs?.hide_ckey
/mob/living/carbon/mind_initialize()
diff --git a/code/datums/mutations/actions.dm b/code/datums/mutations/actions.dm
index 0787c10874..37da0c80bf 100644
--- a/code/datums/mutations/actions.dm
+++ b/code/datums/mutations/actions.dm
@@ -422,13 +422,13 @@
embedding = list("embedded_pain_multiplier" = 4, "embed_chance" = 100, "embedded_fall_chance" = 0)
w_class = WEIGHT_CLASS_SMALL
sharpness = SHARP_POINTY
- var/mob/living/carbon/human/fired_by
+ var/datum/weakref/fired_by
/// if we missed our target
var/missed = TRUE
/obj/item/hardened_spike/Initialize(mapload, firedby)
. = ..()
- fired_by = firedby
+ fired_by = WEAKREF(firedby)
addtimer(CALLBACK(src, PROC_REF(checkembedded)), 5 SECONDS)
/obj/item/hardened_spike/proc/checkembedded()
@@ -478,13 +478,16 @@
return
been_places = TRUE
chems = new
- chems.transfered = embedded_mob
- chems.spikey = src
- to_chat(fired_by, "Link established! Use the \"Transfer Chemicals\" ability to send your chemicals to the linked target!")
- chems.Grant(fired_by)
+ chems.transfered = WEAKREF(embedded_mob)
+ var/mob/fired_by_mob = fired_by.resolve()
+ if(fired_by_mob)
+ to_chat(fired_by_mob, span_notice("Link established! Use the \"Transfer Chemicals\" ability to send your chemicals to the linked target!"))
+ chems.Grant(fired_by_mob)
/obj/item/hardened_spike/chem/unembedded()
- to_chat(fired_by, "Link lost!")
+ var/mob/fired_by_mob = fired_by.resolve()
+ if(fired_by_mob)
+ to_chat(fired_by_mob, span_warning("Link lost!"))
QDEL_NULL(chems)
..()
@@ -496,19 +499,17 @@
name = "Transfer Chemicals"
desc = "Send all of your reagents into whomever the chem spike is embedded in. One use."
var/obj/item/hardened_spike/chem/spikey
- var/mob/living/carbon/human/transfered
+ var/datum/weakref/transfered
/datum/action/innate/send_chems/Activate()
- if(!ishuman(transfered) || !ishuman(owner))
+ var/mob/living/carbon/human/transfered_mob = transfered?.resolve()
+ if(!ishuman(transfered_mob) || !ishuman(owner))
return
- var/mob/living/carbon/human/transferer = owner
-
- to_chat(transfered, "You feel a tiny prick!")
- transferer.reagents.trans_to(transfered, transferer.reagents.total_volume, 1, 1, 0)
+ to_chat(transfered_mob, span_warning("You feel a tiny prick!"))
+ owner.reagents.trans_to(transfered_mob, owner.reagents.total_volume, 1, 1, 0)
var/obj/item/bodypart/L = spikey.checkembedded()
//this is where it would deal damage, if it transfers chems it removes itself so no damage
spikey.forceMove(get_turf(L))
- transfered.visible_message("[spikey] falls out of [transfered]!")
-
+ transfered_mob.visible_message(span_notice("[spikey] falls out of [transfered_mob]!"))
diff --git a/code/datums/mutations/antenna.dm b/code/datums/mutations/antenna.dm
index ad08b8ebdc..866180ab4b 100644
--- a/code/datums/mutations/antenna.dm
+++ b/code/datums/mutations/antenna.dm
@@ -28,8 +28,7 @@
/datum/mutation/human/antenna/on_losing(mob/living/carbon/human/owner)
if(..())
return
- if(linked_radio)
- linked_radio.Destroy()
+ QDEL_NULL(linked_radio)
/datum/mutation/human/antenna/New(class_ = MUT_OTHER, timer, datum/mutation/human/copymut)
..()
diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm
index e3778811ea..05da7a7919 100644
--- a/code/game/machinery/cryopod.dm
+++ b/code/game/machinery/cryopod.dm
@@ -346,8 +346,10 @@ GLOBAL_LIST_EMPTY(cryopod_computers)
else
if(ishuman(mob_occupant))
var/mob/living/carbon/human/H = mob_occupant
- if(H.mind && H.client && H.client.prefs && H == H.mind.original_character)
- H.SaveTCGCards()
+ if(H.mind && H.client && H.client.prefs)
+ var/mob/living/carbon/human/H_original_character = H.mind.original_character?.resolve()
+ if(H_original_character && H == H_original_character)
+ H.SaveTCGCards()
var/list/gear = list()
if(iscarbon(mob_occupant)) // sorry simp-le-mobs deserve no mercy
diff --git a/code/modules/antagonists/blob/blob/blobstrains/_blobstrain.dm b/code/modules/antagonists/blob/blob/blobstrains/_blobstrain.dm
index 2c55cc6915..69011f0d86 100644
--- a/code/modules/antagonists/blob/blob/blobstrains/_blobstrain.dm
+++ b/code/modules/antagonists/blob/blob/blobstrains/_blobstrain.dm
@@ -22,6 +22,10 @@ GLOBAL_LIST_INIT(valid_blobstrains, subtypesof(/datum/blobstrain) - list(/datum/
stack_trace("blobstrain created without overmind")
overmind = new_overmind
+/datum/blobstrain/Destroy(force, ...)
+ overmind = null
+ return ..()
+
/datum/blobstrain/proc/on_gain()
overmind.color = complementary_color
for(var/BL in GLOB.blobs)
diff --git a/code/modules/antagonists/blob/blob/overmind.dm b/code/modules/antagonists/blob/blob/overmind.dm
index f15234cd48..80b7d097d0 100644
--- a/code/modules/antagonists/blob/blob/overmind.dm
+++ b/code/modules/antagonists/blob/blob/overmind.dm
@@ -177,6 +177,9 @@ GLOBAL_LIST_EMPTY(blob_nodes)
blob_mobs = null
resource_blobs = null
blobs_legit = null
+ blob_core = null
+ if(blobstrain)
+ QDEL_NULL(blobstrain)
SSshuttle.clearHostileEnvironment(src)
STOP_PROCESSING(SSobj, src)
diff --git a/code/modules/antagonists/cult/cult_items.dm b/code/modules/antagonists/cult/cult_items.dm
index 38cabce8e5..878fe4a443 100644
--- a/code/modules/antagonists/cult/cult_items.dm
+++ b/code/modules/antagonists/cult/cult_items.dm
@@ -231,7 +231,7 @@
phaseout = /obj/effect/temp_visual/dir_setting/cult/phase/out
/datum/action/innate/dash/cult/IsAvailable(silent = FALSE)
- if(iscultist(holder) && current_charges)
+ if(iscultist(owner) && current_charges)
return TRUE
else
return FALSE
diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm
index a45916aff9..46e80b3255 100644
--- a/code/modules/awaymissions/corpse.dm
+++ b/code/modules/awaymissions/corpse.dm
@@ -272,16 +272,26 @@
//Non-human spawners
-/obj/effect/mob_spawn/AICorpse/create(ckey, name) //Creates a corrupted AI
- var/A = locate(/mob/living/silicon/ai) in loc
- if(A)
+/obj/effect/mob_spawn/AICorpse //Creates a corrupted AI
+ mob_type = /mob/living/silicon/ai/spawned
+
+/obj/effect/mob_spawn/AICorpse/create(ckey, name)
+ var/ai_already_present = locate(/mob/living/silicon/ai) in loc
+ if(ai_already_present)
+ qdel(src)
return
- var/mob/living/silicon/ai/spawned/M = new(loc) //spawn new AI at landmark as var M
- M.name = src.name
- M.real_name = src.name
- M.aiPDA.toff = TRUE //turns the AI's PDA messenger off, stopping it showing up on player PDAs
- M.death() //call the AI's death proc
- qdel(src)
+ . = ..()
+
+// TODO: Port the upstream tgstation rewrite of this.
+/obj/effect/mob_spawn/AICorpse/equip(mob/living/silicon/ai/ai)
+ . = ..()
+ if(!isAI(ai)) // This should never happen.
+ stack_trace("[type] spawned a mob of type [ai?.type || "NULL"] that was not an AI!")
+ return
+ ai.name = name
+ ai.real_name = name
+ ai.aiPDA.toff = TRUE //turns the AI's PDA messenger off, stopping it showing up on player PDAs
+ ai.death() //call the AI's death proc
/obj/effect/mob_spawn/slime
mob_type = /mob/living/simple_animal/slime
diff --git a/code/modules/integrated_electronics/passive/power.dm b/code/modules/integrated_electronics/passive/power.dm
index adc99f0ff5..bd24135ea5 100644
--- a/code/modules/integrated_electronics/passive/power.dm
+++ b/code/modules/integrated_electronics/passive/power.dm
@@ -122,8 +122,9 @@
var/datum/reagent/blood/B = locate() in reagents.reagent_list
if(lfwb)
if(B && B.data["cloneable"])
- var/mob/M = B.data["donor"]
- if(M && (M.stat != DEAD) && (M.client))
+ var/datum/weakref/donor_ref = B.data["donor"]
+ var/mob/donor = donor_ref.resolve()
+ if(donor && (donor.stat != DEAD) && (donor.client))
bp = 500000
if((assembly.battery.maxcharge-assembly.battery.charge) / GLOB.CELLRATE > bp)
if(reagents.remove_reagent(/datum/reagent/blood, 1))
diff --git a/code/modules/mob/dead/new_player/login.dm b/code/modules/mob/dead/new_player/login.dm
index dbcc2cb2b4..e40b467a79 100644
--- a/code/modules/mob/dead/new_player/login.dm
+++ b/code/modules/mob/dead/new_player/login.dm
@@ -5,7 +5,7 @@
if(!mind)
mind = new /datum/mind(key)
mind.active = 1
- mind.current = src
+ mind.set_current(src)
..()
diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm
index 514dab55f3..e76a8f5a38 100644
--- a/code/modules/mob/dead/new_player/new_player.dm
+++ b/code/modules/mob/dead/new_player/new_player.dm
@@ -695,7 +695,7 @@
mind.late_joiner = TRUE
mind.active = 0 //we wish to transfer the key manually
mind.transfer_to(H) //won't transfer key since the mind is not active
- mind.original_character = H
+ mind.set_original_character(H)
H.name = real_name
client.init_verbs()
diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm
index 8d5bd40acc..70307520bc 100644
--- a/code/modules/mob/living/blood.dm
+++ b/code/modules/mob/living/blood.dm
@@ -185,7 +185,7 @@
/mob/living/carbon/get_blood_data()
var/blood_data = list()
//set the blood data
- blood_data["donor"] = src
+ blood_data["donor"] = WEAKREF(src)
blood_data["viruses"] = list()
for(var/thing in diseases)
diff --git a/code/modules/mob/living/brain/brain.dm b/code/modules/mob/living/brain/brain.dm
index a6a74fe4de..b00ab69bf7 100644
--- a/code/modules/mob/living/brain/brain.dm
+++ b/code/modules/mob/living/brain/brain.dm
@@ -34,10 +34,11 @@
if(stat!=DEAD) //If not dead.
death(1) //Brains can die again. AND THEY SHOULD AHA HA HA HA HA HA
if(mind) //You aren't allowed to return to brains that don't exist
- mind.current = null
+ mind.set_current(null)
mind.active = FALSE //No one's using it anymore.
ghostize() //Ghostize checks for key so nothing else is necessary.
container = null
+ QDEL_NULL(stored_dna)
return ..()
/mob/living/brain/update_mobility()
diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm
index 7e10ae1c39..9d16907467 100644
--- a/code/modules/mob/living/brain/brain_item.dm
+++ b/code/modules/mob/living/brain/brain_item.dm
@@ -282,6 +282,8 @@
if(brainmob)
QDEL_NULL(brainmob)
QDEL_LIST(traumas)
+ if(owner?.mind)
+ owner.mind.set_current(null)
return ..()
//other types of brains
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 42e8fbd77d..efa178de33 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -17,7 +17,6 @@
QDEL_LIST(internal_organs)
QDEL_LIST(stomach_contents)
QDEL_LIST(bodyparts)
- QDEL_LIST(implants)
hand_bodyparts = null //Just references out bodyparts, don't need to delete twice.
remove_from_all_data_huds()
QDEL_NULL(dna)
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index 3ad1101c96..e1af109877 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -553,7 +553,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
/datum/species/proc/on_species_loss(mob/living/carbon/human/C, datum/species/new_species, pref_load)
if(C.dna.species.exotic_bloodtype)
- if(!new_species.exotic_bloodtype)
+ if(!new_species?.exotic_bloodtype)
C.dna.blood_type = random_blood_type()
else
C.dna.blood_type = new_species.exotic_bloodtype
@@ -573,7 +573,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
C.type_of_meat = initial(meat)
//If their inert mutation is not the same, swap it out
- if((inert_mutation != new_species.inert_mutation) && LAZYLEN(C.dna.mutation_index) && (inert_mutation in C.dna.mutation_index))
+ if(new_species && (inert_mutation != new_species.inert_mutation) && LAZYLEN(C.dna.mutation_index) && (inert_mutation in C.dna.mutation_index))
C.dna.remove_mutation(inert_mutation)
//keep it at the right spot, so we can't have people taking shortcuts
var/location = C.dna.mutation_index.Find(inert_mutation)
@@ -582,7 +582,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
C.dna.mutation_index[new_species.inert_mutation] = create_sequence(new_species.inert_mutation)
C.dna.default_mutation_genes[new_species.inert_mutation] = C.dna.mutation_index[new_species.inert_mutation]
- if(!new_species.has_field_of_vision && has_field_of_vision && ishuman(C) && CONFIG_GET(flag/use_field_of_vision))
+ if(new_species && !new_species.has_field_of_vision && has_field_of_vision && ishuman(C) && CONFIG_GET(flag/use_field_of_vision))
var/datum/component/field_of_vision/F = C.GetComponent(/datum/component/field_of_vision)
if(F)
qdel(F)
diff --git a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
index 98dc00a2f4..550789a066 100644
--- a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
@@ -320,17 +320,17 @@
L["name"] = body.name
L["ref"] = "[REF(body)]"
L["occupied"] = occupied
- var/button
+ var/button_state
if(occupied == "owner")
- button = "selected"
+ button_state = "selected"
else if(occupied == "stranger")
- button = "danger"
+ button_state = "danger"
else if(can_swap(body))
- button = null
+ button_state = null
else
- button = "disabled"
+ button_state = "disabled"
- L["swap_button_state"] = button
+ L["swap_button_state"] = button_state
L["swappable"] = (occupied == "available") && can_swap(body)
data["bodies"] += list(L)
@@ -745,9 +745,9 @@
background_icon_state = "bg_alien"
var/datum/species/jelly/stargazer/species
-/datum/action/innate/link_minds/New(_species)
+/datum/action/innate/link_minds/New(species)
..()
- species = _species
+ src.species = species
/datum/action/innate/link_minds/Activate()
var/mob/living/carbon/human/H = owner
@@ -771,3 +771,7 @@
else
to_chat(H, "You can't seem to link [target]'s mind...")
to_chat(target, "The foreign presence leaves your mind.")
+
+/datum/action/innate/link_minds/Destroy()
+ species = null
+ return ..()
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index f426eac569..1615b80193 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -35,8 +35,10 @@
if(buckled)
buckled.unbuckle_mob(src,force=1)
QDEL_LIST_ASSOC_VAL(ability_actions)
-
+ QDEL_LIST(abilities)
+ QDEL_LIST(implants)
remove_from_all_data_huds()
+ cleanse_trait_datums()
GLOB.mob_living_list -= src
QDEL_LIST(diseases)
return ..()
@@ -909,7 +911,7 @@
else
throw_alert("gravity", /atom/movable/screen/alert/weightless)
if(!override && !is_flying())
- INVOKE_ASYNC(src, TYPE_PROC_REF(/atom/movable, float), !has_gravity)
+ float(!has_gravity)
/mob/living/float(on)
if(throwing)
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index 64b17b2622..6f0306350a 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -100,7 +100,9 @@
var/display_icon_override
var/emote_display = "Neutral" //text string of the current emote we set for the status displays, to prevent logins resetting it.
+ // TODO: Currently unused, needs port from TG.
var/datum/robot_control/robot_control
+ // TODO: Currently unused, needs port from TG.
/// Station alert datum for showing alerts UI
var/datum/station_alert/alert_control
///remember AI's last location
@@ -119,7 +121,7 @@
new/obj/structure/ai_core/deactivated(loc) //New empty terminal.
return INITIALIZE_HINT_QDEL //Delete AI.
- ADD_TRAIT(src, TRAIT_NO_TELEPORT, src)
+ ADD_TRAIT(src, TRAIT_NO_TELEPORT, INNATE_TRAIT)
if(L && istype(L, /datum/ai_laws))
laws = L
laws.associate(src)
@@ -189,20 +191,34 @@
GLOB.ai_list -= src
GLOB.shuttle_caller_list -= src
SSshuttle.autoEvac()
+ stop_controlling_display()
QDEL_NULL(eyeobj) // No AI, no Eye
QDEL_NULL(spark_system)
+ QDEL_NULL(deploy_action)
+ QDEL_NULL(redeploy_action)
+ QDEL_NULL(custom_holoform)
+ QDEL_NULL(master_multicam)
+ deployed_shell = null
+ parent = null
+ QDEL_NULL(spark_system)
QDEL_NULL(malf_picker)
QDEL_NULL(doomsday_device)
- // TODO: Why these no work?
+ // TODO: Port implementation of these from TG or remove the unused code.
// QDEL_NULL(robot_control)
- QDEL_NULL(aiMulti)
// QDEL_NULL(alert_control)
+ QDEL_NULL(aiMulti)
+ QDEL_NULL(aiPDA)
malfhack = null
current = null
Bot = null
controlled_equipment = null
linked_core = null
apc_override = null
+ for(var/mob/living/silicon/robot/linked_robot as anything in connected_robots)
+ if(QDELETED(linked_robot))
+ continue
+ linked_robot.set_connected_ai(null)
+ connected_robots.Cut()
return ..()
/mob/living/silicon/ai/IgniteMob()
@@ -357,12 +373,12 @@
is_anchored = !is_anchored
move_resist = MOVE_FORCE_NORMAL
status_flags |= CANPUSH
- REMOVE_TRAIT(src, TRAIT_NO_TELEPORT, src)
+ REMOVE_TRAIT(src, TRAIT_NO_TELEPORT, INNATE_TRAIT)
else
is_anchored = !is_anchored
move_resist = MOVE_FORCE_OVERPOWERING
status_flags &= ~CANPUSH
- ADD_TRAIT(src, TRAIT_NO_TELEPORT, src)
+ ADD_TRAIT(src, TRAIT_NO_TELEPORT, INNATE_TRAIT)
/mob/living/silicon/ai/proc/ai_mob_to_structure()
disconnect_shell()
@@ -1097,6 +1113,10 @@
else
Remove(owner) //If the last shell is blown, destroy it.
+/datum/action/innate/deploy_last_shell/Destroy()
+ last_used_shell = null
+ return ..()
+
/mob/living/silicon/ai/proc/disconnect_shell()
if(deployed_shell) //Forcibly call back AI in event of things such as damage, EMP or power loss.
to_chat(src, "Your remote connection has been reset!")
@@ -1107,9 +1127,7 @@
return
/mob/living/silicon/ai/spawned/Initialize(mapload, datum/ai_laws/L, mob/target_ai)
- . = ..()
- if(!target_ai)
- target_ai = src //cheat! just give... ourselves as the spawned AI, because that's technically correct
+ . = ..(mapload, L, src) //cheat! just give... ourselves as the spawned AI, because that's technically correct
/mob/living/silicon/ai/proc/camera_visibility(mob/camera/aiEye/moved_eye)
GLOB.cameranet.visibility(moved_eye, client, all_eyes, USE_STATIC_OPAQUE)
diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm
index cf995f07f0..26431ece5f 100644
--- a/code/modules/mob/living/silicon/pai/pai.dm
+++ b/code/modules/mob/living/silicon/pai/pai.dm
@@ -57,7 +57,6 @@
var/encryptmod = FALSE
var/holoform = FALSE
var/canholo = TRUE
- var/obj/item/card/id/access_card = null
var/chassis = "repairbot"
var/dynamic_chassis
var/dynamic_chassis_sit = FALSE //whether we're sitting instead of resting spritewise
@@ -85,13 +84,21 @@
var/icon/custom_holoform_icon
/mob/living/silicon/pai/Destroy()
+ QDEL_NULL(signaler)
+ QDEL_NULL(pda)
QDEL_NULL(internal_instrument)
+ if(cable)
+ QDEL_NULL(cable)
+ hackdoor = null
if (loc != card)
card.forceMove(drop_location())
card.pai = null
card.cut_overlays()
card.add_overlay("pai-off")
+ card = null
+ current = null
GLOB.pai_list -= src
+ STOP_PROCESSING(SSfastprocess, src)
return ..()
/mob/living/silicon/pai/Initialize(mapload)
diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm
index 4539c594a1..0a84d5afc8 100644
--- a/code/modules/mob/living/simple_animal/constructs.dm
+++ b/code/modules/mob/living/simple_animal/constructs.dm
@@ -401,22 +401,16 @@
buttontooltipstyle = "cult"
button_icon_state = "cult_mark"
var/tracking = FALSE
- var/mob/living/simple_animal/hostile/construct/the_construct
-
-
-/datum/action/innate/seek_master/Grant(var/mob/living/C)
- the_construct = C
- ..()
/datum/action/innate/seek_master/Activate()
var/datum/antagonist/cult/C = owner.mind.has_antag_datum(/datum/antagonist/cult)
if(!C)
return
if(!C.cult_team)
- to_chat(the_construct, "You are alone, and have no team.")
+ to_chat(owner, "You are alone, and have no team.")
return
var/datum/objective/eldergod/summon_objective = locate() in C.cult_team.objectives
-
+ var/mob/living/simple_animal/hostile/construct/the_construct = owner
if(summon_objective.check_completion())
the_construct.master = C.cult_team.blood_target
@@ -442,15 +436,11 @@
background_icon_state = "bg_demon"
buttontooltipstyle = "cult"
button_icon_state = "cult_mark"
- var/mob/living/simple_animal/hostile/construct/harvester/the_construct
-
-/datum/action/innate/seek_prey/Grant(var/mob/living/C)
- the_construct = C
- ..()
/datum/action/innate/seek_prey/Activate()
if(GLOB.cult_narsie == null)
return
+ var/mob/living/simple_animal/hostile/construct/the_construct = owner
if(the_construct.seeking)
desc = "None can hide from Nar'Sie, activate to track a survivor attempting to flee the red harvest!"
button_icon_state = "cult_mark"
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm
index 987a1f3e9c..bd97907337 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm
@@ -35,6 +35,10 @@
var/datum/action/innate/elite_attack/attack_action = new action_type()
attack_action.Grant(src)
+/mob/living/simple_animal/hostile/asteroid/elite/Destroy(force, ...)
+ owner = null
+ return ..()
+
//Prevents elites from attacking members of their faction (can't hurt themselves either) and lets them mine rock with an attack despite not being able to smash walls.
/mob/living/simple_animal/hostile/asteroid/elite/AttackingTarget()
if(istype(target, /mob/living/simple_animal/hostile))
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 784400a818..72d2f327eb 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -20,27 +20,38 @@
hook_vr("mob_new",list(src))
/mob/Destroy()//This makes sure that mobs with clients/keys are not just deleted from the game.
+ // if(client)
+ // stack_trace("Mob with client has been deleted.")
+ // else if(ckey)
+ // stack_trace("Mob without client but with associated ckey, [ckey], has been deleted.")
+ unset_machine()
remove_from_mob_list()
remove_from_dead_mob_list()
remove_from_alive_mob_list()
+ QDEL_LIST(mob_spell_list)
+ QDEL_LIST(actions)
GLOB.all_clockwork_mobs -= src
+ // remove_from_mob_suicide_list()
focus = null
LAssailant = null
movespeed_modification = null
+ if(length(progressbars))
+ stack_trace("[src] destroyed with elements in its progressbars list.")
+ progressbars = null
for (var/alert in alerts)
clear_alert(alert, TRUE)
- if(observers && observers.len)
- for(var/M in observers)
- var/mob/dead/observe = M
+ if(observers?.len)
+ for(var/mob/dead/observe as anything in observers)
observe.reset_perspective(null)
dispose_rendering()
qdel(hud_used)
- for(var/cc in client_colours)
- qdel(cc)
- client_colours = null
- ghostize()
- ..()
- return QDEL_HINT_HARDDEL
+ QDEL_LIST(client_colours)
+ ghostize(can_reenter_corpse = FALSE) //False, since we're deleting it currently
+ if(mind?.current == src) //Let's just be safe yeah? This will occasionally be cleared, but not always. Can't do it with ghostize without changing behavior
+ mind.set_current(null)
+ // if(mock_client)
+ // mock_client.mob = null
+ return ..()
/mob/GenerateTag()
tag = "mob_[next_mob_id++]"
diff --git a/code/modules/shuttle/arrivals.dm b/code/modules/shuttle/arrivals.dm
index 8d9c5a5f77..bd8fe83338 100644
--- a/code/modules/shuttle/arrivals.dm
+++ b/code/modules/shuttle/arrivals.dm
@@ -33,6 +33,14 @@
WARNING("More than one arrivals docking_port placed on map! Ignoring duplicates.")
SSshuttle.arrivals = src
+/obj/docking_port/mobile/arrivals/proc/on_console_deleted(datum/source)
+ SIGNAL_HANDLER
+ console = null
+ for(var/obj/machinery/requests_console/new_console as anything in GLOB.allConsoles)
+ var/area/console_area = get_area(new_console)
+ if(istype(console_area, /area/shuttle/arrival) && !QDELETED(new_console) && new_console != source)
+ console = new_console
+
/obj/docking_port/mobile/arrivals/LateInitialize()
areas = list()
@@ -40,10 +48,18 @@
for(var/area/shuttle/arrival/A in GLOB.sortedAreas)
for(var/obj/structure/chair/C in A)
new_latejoin += C
- if(!console)
- console = locate(/obj/machinery/requests_console) in A
areas += A
+ if(!console)
+ for(var/obj/machinery/requests_console/new_console as anything in GLOB.allConsoles)
+ var/area/console_area = get_area(new_console)
+ if(!istype(console_area, /area/shuttle/arrival) || QDELETED(new_console))
+ continue
+ console = new_console
+ RegisterSignal(console, COMSIG_PARENT_QDELETING, PROC_REF(on_console_deleted))
+ else if(istype(console))
+ RegisterSignal(console, COMSIG_PARENT_QDELETING, PROC_REF(on_console_deleted))
+
if(SSjob.latejoin_trackers.len)
WARNING("Map contains predefined latejoin spawn points and an arrivals shuttle. Using the arrivals shuttle.")
diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm
index 5cb4a083cb..73dcd4c6e9 100644
--- a/code/modules/shuttle/shuttle.dm
+++ b/code/modules/shuttle/shuttle.dm
@@ -941,13 +941,19 @@
/obj/docking_port/mobile/proc/count_engines()
. = 0
+ engine_list = list()
for(var/thing in shuttle_areas)
var/area/shuttle/areaInstance = thing
for(var/obj/structure/shuttle/engine/E in areaInstance.contents)
if(!QDELETED(E))
engine_list += E
+ RegisterSignal(E, COMSIG_PARENT_QDELETING, PROC_REF(on_engine_deleted))
. += E.engine_power
+/obj/docking_port/mobile/proc/on_engine_deleted(datum/source)
+ SIGNAL_HANDLER
+ engine_list -= source
+
// Double initial engines to get to 0.5 minimum
// Lose all initial engines to get to 2
//For 0 engine shuttles like BYOS 5 engines to get to doublespeed
diff --git a/code/modules/shuttle/shuttle_creation/shuttle_creator.dm b/code/modules/shuttle/shuttle_creation/shuttle_creator.dm
index ac604fbe49..222cbd2ed7 100644
--- a/code/modules/shuttle/shuttle_creation/shuttle_creator.dm
+++ b/code/modules/shuttle/shuttle_creation/shuttle_creator.dm
@@ -198,8 +198,8 @@ GLOBAL_LIST_EMPTY(custom_shuttle_machines) //Machines that require updating (He
var/invertedDir = invertDir(portDirection)
if(!portDirection || !invertedDir)
to_chat(usr, "Shuttle creation aborted, docking airlock must be on an external wall. Please select a new airlock.")
- port.Destroy()
- stationary_port.Destroy()
+ QDEL_NULL(port)
+ QDEL_NULL(stationary_port)
linkedShuttleId = null
return FALSE
port.dir = invertedDir
@@ -207,8 +207,8 @@ GLOBAL_LIST_EMPTY(custom_shuttle_machines) //Machines that require updating (He
if(!calculate_bounds(port))
to_chat(usr, "Bluespace calculations failed, please select a new airlock.")
- port.Destroy()
- stationary_port.Destroy()
+ QDEL_NULL(port)
+ QDEL_NULL(stationary_port)
linkedShuttleId = null
return FALSE
diff --git a/code/modules/spells/spell_types/ethereal_jaunt.dm b/code/modules/spells/spell_types/ethereal_jaunt.dm
index 4b02f9fbcd..8f0d0fd6c0 100644
--- a/code/modules/spells/spell_types/ethereal_jaunt.dm
+++ b/code/modules/spells/spell_types/ethereal_jaunt.dm
@@ -39,7 +39,7 @@
return
mobloc = get_turf(target.loc)
jaunt_steam(mobloc)
- ADD_TRAIT(target, TRAIT_MOBILITY_NOMOVE, src)
+ ADD_TRAIT(target, TRAIT_MOBILITY_NOMOVE, MAGIC_TRAIT)
target.update_mobility()
holder.reappearing = 1
play_sound("exit",target)
@@ -55,7 +55,7 @@
if(T)
if(target.Move(T))
break
- REMOVE_TRAIT(target, TRAIT_MOBILITY_NOMOVE, src)
+ REMOVE_TRAIT(target, TRAIT_MOBILITY_NOMOVE, MAGIC_TRAIT)
target.update_mobility()
/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/proc/jaunt_steam(mobloc)
diff --git a/code/modules/unit_tests/vore_tests.dm b/code/modules/unit_tests/vore_tests.dm
index 3a8df28479..65a8ad94db 100644
--- a/code/modules/unit_tests/vore_tests.dm
+++ b/code/modules/unit_tests/vore_tests.dm
@@ -97,8 +97,8 @@
endOxyloss = prey.getOxyLoss()
if(startOxyloss < endOxyloss)
Fail("Prey takes oxygen damage in a pred's belly! (Before: [startOxyloss]; after: [endOxyloss])")
- qdel(prey)
- qdel(pred)
+ QDEL_NULL(prey)
+ QDEL_NULL(pred)
return TRUE
////////////////////////////////////////////////////////////////
/datum/unit_test/belly_spacesafe
@@ -160,8 +160,8 @@
endBruteloss = prey.getBruteLoss()
if(startBruteloss < endBruteloss)
Fail("Prey takes brute damage in space! (Before: [startBruteloss]; after: [endBruteloss])")
- qdel(prey)
- qdel(pred)
+ QDEL_NULL(prey)
+ QDEL_NULL(pred)
return TRUE
////////////////////////////////////////////////////////////////
/datum/unit_test/belly_damage
@@ -213,6 +213,6 @@
endBruteBurn = prey.getBruteLoss() + prey.getFireLoss()
if(startBruteBurn >= endBruteBurn)
Fail("Prey doesn't take damage in digesting belly! (Before: [startBruteBurn]; after: [endBruteBurn])")
- qdel(prey)
- qdel(pred)
+ QDEL_NULL(prey)
+ QDEL_NULL(pred)
return TRUE
diff --git a/code/modules/vehicles/mecha/combat/neovgre.dm b/code/modules/vehicles/mecha/combat/neovgre.dm
index 918fc33f7c..bda8d586d9 100644
--- a/code/modules/vehicles/mecha/combat/neovgre.dm
+++ b/code/modules/vehicles/mecha/combat/neovgre.dm
@@ -53,7 +53,8 @@
/obj/vehicle/sealed/mecha/combat/neovgre/proc/go_critical()
explosion(get_turf(loc), 3, 5, 10, 20, 30)
- Destroy(src)
+ if(!QDELETED(src))
+ qdel(src)
/obj/vehicle/sealed/mecha/combat/neovgre/container_resist(mob/living/user)
to_chat(user, "Neovgre requires a lifetime commitment friend, no backing out now!")
diff --git a/code/modules/zombie/organs.dm b/code/modules/zombie/organs.dm
index 3011f9acd2..312735115d 100644
--- a/code/modules/zombie/organs.dm
+++ b/code/modules/zombie/organs.dm
@@ -28,11 +28,10 @@
START_PROCESSING(SSobj, src)
/obj/item/organ/zombie_infection/Remove(special = FALSE)
- if(owner)
- if(iszombie(owner) && old_species)
- owner.set_species(old_species)
- if(timer_id)
- deltimer(timer_id)
+ if(!QDELETED(owner) && iszombie(owner) && old_species)
+ owner.set_species(old_species)
+ if(timer_id)
+ deltimer(timer_id)
. = ..()
STOP_PROCESSING(SSobj, src) //Required to be done after the parent call to avoid conflicts with organ decay.