From f8aad14ae87c73edb7c4cba8644cbdd6977cd8d9 Mon Sep 17 00:00:00 2001
From: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com>
Date: Wed, 12 Jan 2022 13:46:13 -0800
Subject: [PATCH] Harddel Fix Pack #42 + Better Live Reftracking Support
(#63877)
* Hard Del Fixes, Ref Tracking Changes
---
code/__DEFINES/is_helpers.dm | 2 +
code/__HELPERS/_logging.dm | 11 ++-
code/__HELPERS/memory_helpers.dm | 2 +-
code/_compile_options.dm | 10 ++
code/_globalvars/logging.dm | 4 +
code/_onclick/hud/fullscreen.dm | 8 ++
code/controllers/subsystem/air.dm | 3 +
code/controllers/subsystem/garbage.dm | 2 +-
code/controllers/subsystem/parallax.dm | 3 +-
code/datums/action.dm | 21 +++--
code/datums/ai/_item_behaviors.dm | 17 ++--
code/datums/ai/cursed/cursed_behaviors.dm | 10 +-
code/datums/ai/cursed/cursed_subtrees.dm | 5 +-
code/datums/ai/generic/generic_behaviors.dm | 30 +++---
code/datums/ai/generic/generic_subtrees.dm | 10 +-
code/datums/ai/hauntium/hauntium_subtrees.dm | 8 +-
code/datums/ai/monkey/monkey_behaviors.dm | 44 +++++----
code/datums/ai/monkey/monkey_controller.dm | 2 +-
code/datums/ai/monkey/monkey_subtrees.dm | 3 +-
.../ai/objects/cursed/cursed_subtrees.dm | 5 +-
code/datums/ai/objects/item_behaviors.dm | 42 ---------
.../robot_customer_behaviors.dm | 10 +-
.../robot_customer_controller.dm | 5 +-
.../robot_customer/robot_customer_subtrees.dm | 7 +-
code/datums/components/aura_healing.dm | 7 +-
code/datums/components/shrink.dm | 2 +-
code/datums/components/tackle.dm | 17 ++--
code/datums/dna.dm | 5 +-
code/datums/memory/memory.dm | 10 +-
code/datums/mind.dm | 10 +-
code/datums/mutations/_mutations.dm | 4 +-
code/game/atoms_movable.dm | 21 +++--
code/game/machinery/civilian_bounties.dm | 17 ++--
.../machinery/computer/camera_advanced.dm | 84 ++++++++---------
code/game/machinery/computer/crew.dm | 5 +-
code/game/machinery/medical_kiosk.dm | 87 +++++++++---------
code/game/objects/effects/anomalies.dm | 86 ++++++++----------
.../effects/effect_system/effects_foam.dm | 15 ++-
.../effects/effect_system/effects_smoke.dm | 14 ++-
code/game/objects/items.dm | 12 ++-
code/game/objects/items/devices/PDA/cart.dm | 91 ++++++++++---------
code/game/objects/items/flamethrower.dm | 6 +-
.../construction_console.dm | 13 +--
.../construction_console_aux.dm | 13 ++-
.../construction_console_centcom.dm | 5 +-
.../structures/crates_lockers/crates.dm | 6 +-
code/game/world.dm | 7 ++
code/modules/admin/verbs/SDQL2/SDQL_2.dm | 15 ++-
.../view_variables/reference_tracking.dm | 2 +
.../antagonists/_common/antag_datum.dm | 28 +++---
.../abductor/equipment/abduction_gear.dm | 6 +-
.../antagonists/abductor/machinery/camera.dm | 52 ++++-------
.../antagonists/abductor/machinery/console.dm | 23 ++++-
.../abductor/machinery/experiment.dm | 6 ++
.../antagonists/abductor/machinery/pad.dm | 7 ++
code/modules/antagonists/creep/creep.dm | 2 +-
.../revenant/revenant_abilities.dm | 5 +-
.../antagonists/traitor/datum_traitor.dm | 12 +--
code/modules/assembly/bomb.dm | 5 +
code/modules/assembly/holder.dm | 12 +++
.../machinery/bluespace_vendor.dm | 5 +
.../machinery/portable/canister.dm | 2 +-
code/modules/cargo/order.dm | 6 +-
code/modules/client/client_procs.dm | 7 +-
code/modules/clothing/suits/toggles.dm | 3 +-
code/modules/events/pirates.dm | 17 ++--
code/modules/events/spacevine.dm | 62 +++++++------
code/modules/hydroponics/grown/cotton.dm | 14 ++-
code/modules/hydroponics/seeds.dm | 2 +-
code/modules/language/language_manuals.dm | 5 +-
code/modules/mob/dead/observer/observer.dm | 10 +-
code/modules/mob/living/carbon/life.dm | 2 +-
code/modules/mob/living/living.dm | 1 +
code/modules/mob/living/silicon/ai/ai.dm | 13 +--
.../mob/living/silicon/ai/robot_control.dm | 13 ++-
.../modules/mob/living/silicon/robot/robot.dm | 1 +
.../simple_animal/friendly/robot_customer.dm | 5 +-
.../hostile/bosses/paperwizard.dm | 10 +-
code/modules/mob/login.dm | 3 +-
code/modules/mob/logout.dm | 8 +-
code/modules/mob/mob.dm | 26 ++++--
code/modules/mob/mob_defines.dm | 7 ++
.../computers/item/computer.dm | 3 +
.../file_system/programs/configurator.dm | 22 ++---
.../file_system/programs/ntdownloader.dm | 7 +-
.../file_system/programs/powermonitor.dm | 13 ++-
.../file_system/programs/robocontrol.dm | 8 +-
.../file_system/programs/robotact.dm | 27 +++---
.../file_system/programs/secureye.dm | 9 +-
.../modular_computers/hardware/hard_drive.dm | 2 +
code/modules/paperwork/ticketmachine.dm | 34 ++++---
code/modules/photography/_pictures.dm | 8 +-
code/modules/photography/photos/photo.dm | 13 ++-
.../modules/power/lighting/light_construct.dm | 2 +-
.../projectiles/guns/special/medbeam.dm | 6 +-
code/modules/reagents/chemistry/holder.dm | 4 +
code/modules/research/experimentor.dm | 24 +++--
.../research/xenobiology/xenobio_camera.dm | 58 ++----------
code/modules/shuttle/navigation_computer.dm | 41 +++------
code/modules/spells/spell_types/conjure.dm | 21 +++--
.../spells/spell_types/personality_commune.dm | 9 +-
code/modules/surgery/surgery.dm | 22 +++--
code/modules/tgui/tgui.dm | 2 +-
code/modules/unit_tests/_unit_tests.dm | 2 +-
code/modules/vehicles/_vehicle.dm | 10 ++
code/modules/vehicles/mecha/mech_bay.dm | 37 +++++---
code/modules/vehicles/vehicle_actions.dm | 12 ++-
.../wiremod/components/list/assoc_literal.dm | 2 +-
.../wiremod/components/list/list_literal.dm | 2 +-
109 files changed, 865 insertions(+), 733 deletions(-)
diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm
index 550c03d08ea..f6792362302 100644
--- a/code/__DEFINES/is_helpers.dm
+++ b/code/__DEFINES/is_helpers.dm
@@ -7,6 +7,8 @@
#define isatom(A) (isloc(A))
+#define isdatum(thing) (istype(thing, /datum))
+
#define isweakref(D) (istype(D, /datum/weakref))
//Turfs
diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm
index e6ee3de48d3..dd35694a1f5 100644
--- a/code/__HELPERS/_logging.dm
+++ b/code/__HELPERS/_logging.dm
@@ -57,9 +57,16 @@ GLOBAL_LIST_INIT(testing_global_profiler, list("_PROFILE_NAME" = "Global"))
SEND_TEXT(world.log, text)
#endif
-#ifdef REFERENCE_TRACKING
+#if defined(REFERENCE_DOING_IT_LIVE)
+#define log_reftracker(msg) log_harddel("## REF SEARCH [msg]")
+
+/proc/log_harddel(text)
+ WRITE_LOG(GLOB.harddel_log, text)
+
+#elif defined(REFERENCE_TRACKING) // Doing it locally
#define log_reftracker(msg) log_world("## REF SEARCH [msg]")
-#else
+
+#else //Not tracking at all
#define log_reftracker(msg)
#endif
diff --git a/code/__HELPERS/memory_helpers.dm b/code/__HELPERS/memory_helpers.dm
index 1d1d94609be..ff66ab12bf1 100644
--- a/code/__HELPERS/memory_helpers.dm
+++ b/code/__HELPERS/memory_helpers.dm
@@ -16,7 +16,7 @@
* Adds a memory to a mob's mind if conditions are met, called wherever the memory takes place (memory for catching on fire in mob's fire code, for example)
* Argument:
* * memory_type: defined string in memory_defines.dm, shows the memories.json file which story parts to use (and generally what type it is)
- * * extra_info: the contents of the story. You're gonna want at least the protagonist for who is the main character in the story
+ * * extra_info: the contents of the story. You're gonna want at least the protagonist for who is the main character in the story (Any non basic type will be converted to a string on insertion)
* * story_value: the quality of the memory, make easy or roundstart memories have a low value so they don't flood persistence
* * memory_flags: special specifications for skipping parts of the memory like moods for stories where showing moods doesn't make sense
* Returns the datum memory created, null otherwise.
diff --git a/code/_compile_options.dm b/code/_compile_options.dm
index 7fee122655e..7e45033bf65 100644
--- a/code/_compile_options.dm
+++ b/code/_compile_options.dm
@@ -39,6 +39,16 @@
#define TRACK_MAX_SHARE //Allows max share tracking, for use in the atmos debugging ui
#endif //ifdef TESTING
+/// If this is uncommented, we set up the ref tracker to be used in a live environment
+/// And to log events to [log_dir]/harddels.log
+//#define REFERENCE_DOING_IT_LIVE
+#ifdef REFERENCE_DOING_IT_LIVE
+// compile the backend
+#define REFERENCE_TRACKING
+// actually look for refs
+#define GC_FAILURE_HARD_LOOKUP
+#endif // REFERENCE_DOING_IT_LIVE
+
//#define UNIT_TESTS //If this is uncommented, we do a single run though of the game setup and tear down process with unit tests in between
#ifndef PRELOAD_RSC //set to:
diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm
index c97d6862024..e9549d2b5b4 100644
--- a/code/_globalvars/logging.dm
+++ b/code/_globalvars/logging.dm
@@ -89,3 +89,7 @@ GLOBAL_PROTECT(picture_logging_id)
GLOBAL_VAR(picture_logging_prefix)
GLOBAL_PROTECT(picture_logging_prefix)
/////
+#ifdef REFERENCE_DOING_IT_LIVE
+GLOBAL_LIST_EMPTY(harddel_log)
+GLOBAL_PROTECT(harddel_log)
+#endif
diff --git a/code/_onclick/hud/fullscreen.dm b/code/_onclick/hud/fullscreen.dm
index e9b8900d307..f3882e7054d 100644
--- a/code/_onclick/hud/fullscreen.dm
+++ b/code/_onclick/hud/fullscreen.dm
@@ -150,6 +150,14 @@
icon_state = "flash"
alpha = 80
+/atom/movable/screen/fullscreen/bluespace_sparkle
+ icon = 'icons/effects/effects.dmi'
+ screen_loc = "WEST,SOUTH to EAST,NORTH"
+ icon_state = "shieldsparkles"
+ layer = FLASH_LAYER
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ show_when_dead = TRUE
+
/atom/movable/screen/fullscreen/color_vision/green
color = "#00ff00"
diff --git a/code/controllers/subsystem/air.dm b/code/controllers/subsystem/air.dm
index 320347a562a..3bf6fc1cd07 100644
--- a/code/controllers/subsystem/air.dm
+++ b/code/controllers/subsystem/air.dm
@@ -632,6 +632,9 @@ GLOBAL_LIST_EMPTY(colored_images)
/datum/controller/subsystem/air/proc/start_processing_machine(obj/machinery/machine)
if(machine.atmos_processing)
return
+ if(QDELETED(machine))
+ stack_trace("We tried to add a garbage collecting machine to SSair. Don't")
+ return
machine.atmos_processing = TRUE
atmos_machinery += machine
diff --git a/code/controllers/subsystem/garbage.dm b/code/controllers/subsystem/garbage.dm
index b49256d2b11..238103bd07c 100644
--- a/code/controllers/subsystem/garbage.dm
+++ b/code/controllers/subsystem/garbage.dm
@@ -372,7 +372,7 @@ SUBSYSTEM_DEF(garbage)
#ifdef REFERENCE_TRACKING
if (QDEL_HINT_FINDREFERENCE) //qdel will, if REFERENCE_TRACKING is enabled, display all references to this object, then queue the object for deletion.
SSgarbage.Queue(D)
- D.find_references()
+ D.find_references() //This breaks ci. Consider it insurance against somehow pring reftracking on accident
if (QDEL_HINT_IFFAIL_FINDREFERENCE) //qdel will, if REFERENCE_TRACKING is enabled and the object fails to collect, display all references to this object.
SSgarbage.Queue(D)
SSgarbage.reference_find_on_fail["\ref[D]"] = TRUE
diff --git a/code/controllers/subsystem/parallax.dm b/code/controllers/subsystem/parallax.dm
index 9d42217fbb4..c45462dd216 100644
--- a/code/controllers/subsystem/parallax.dm
+++ b/code/controllers/subsystem/parallax.dm
@@ -40,7 +40,8 @@ SUBSYSTEM_DEF(parallax)
if(!istype(movable_eye))
continue
- for (movable_eye; isloc(movable_eye.loc) && !isturf(movable_eye.loc); movable_eye = movable_eye.loc);
+ while(isloc(movable_eye.loc) && !isturf(movable_eye.loc))
+ movable_eye = movable_eye.loc
//get the last movable holding the mobs eye
if(movable_eye == processing_client.movingmob)
diff --git a/code/datums/action.dm b/code/datums/action.dm
index a00b97bbb59..395b673fd74 100644
--- a/code/datums/action.dm
+++ b/code/datums/action.dm
@@ -33,7 +33,8 @@
/datum/action/proc/link_to(Target)
target = Target
- RegisterSignal(Target, COMSIG_ATOM_UPDATED_ICON, .proc/OnUpdatedIcon)
+ RegisterSignal(target, COMSIG_ATOM_UPDATED_ICON, .proc/OnUpdatedIcon)
+ RegisterSignal(target, COMSIG_PARENT_QDELETING, .proc/clear_ref, override = TRUE)
/datum/action/Destroy()
if(owner)
@@ -49,7 +50,7 @@
return
Remove(owner)
owner = M
- RegisterSignal(owner, COMSIG_PARENT_QDELETING, .proc/owner_deleted)
+ RegisterSignal(owner, COMSIG_PARENT_QDELETING, .proc/clear_ref, override = TRUE)
//button id generation
var/counter = 0
@@ -75,9 +76,12 @@
else
Remove(owner)
-/datum/action/proc/owner_deleted(datum/source)
+/datum/action/proc/clear_ref(datum/ref)
SIGNAL_HANDLER
- Remove(owner)
+ if(ref == owner)
+ Remove(owner)
+ if(ref == target)
+ qdel(src)
/datum/action/proc/Remove(mob/M)
for(var/datum/weakref/reference as anything in sharers)
@@ -93,10 +97,13 @@
M.update_action_buttons()
if(owner)
UnregisterSignal(owner, COMSIG_PARENT_QDELETING)
+ if(target == owner)
+ RegisterSignal(target, COMSIG_PARENT_QDELETING, .proc/clear_ref)
owner = null
- button.moved = FALSE //so the button appears in its normal position when given to another owner.
- button.locked = FALSE
- button.id = null
+ if(button)
+ button.moved = FALSE //so the button appears in its normal position when given to another owner.
+ button.locked = FALSE
+ button.id = null
/datum/action/proc/Trigger()
if(!IsAvailable())
diff --git a/code/datums/ai/_item_behaviors.dm b/code/datums/ai/_item_behaviors.dm
index de255cd448a..f575edaea2a 100644
--- a/code/datums/ai/_item_behaviors.dm
+++ b/code/datums/ai/_item_behaviors.dm
@@ -22,16 +22,16 @@
///Max attemps to make
var/max_attempts = 3
-
/datum/ai_behavior/item_move_close_and_attack/setup(datum/ai_controller/controller, target_key, throw_count_key)
. = ..()
- controller.current_movement_target = controller.blackboard[target_key]
-
+ var/datum/weakref/target_ref = controller.blackboard[target_key]
+ controller.current_movement_target = target_ref?.resolve()
/datum/ai_behavior/item_move_close_and_attack/perform(delta_time, datum/ai_controller/controller, target_key, throw_count_key)
. = ..()
var/obj/item/item_pawn = controller.pawn
- var/atom/throw_target = controller.blackboard[target_key]
+ var/datum/weakref/target_ref = controller.blackboard[target_key]
+ var/atom/throw_target = target_ref?.resolve()
item_pawn.visible_message(span_warning("[item_pawn] hurls towards [throw_target]!"))
item_pawn.throw_at(throw_target, rand(4,5), 9)
@@ -48,12 +48,15 @@
controller.blackboard -= target_key
controller.blackboard[throw_count_key] = 0
-/datum/ai_behavior/item_move_close_and_attack/haunted
+/datum/ai_behavior/item_move_close_and_attack/ghostly
attack_sound = 'sound/items/haunted/ghostitemattack.ogg'
max_attempts = 4
-/datum/ai_behavior/item_move_close_and_attack/haunted/finish_action(datum/ai_controller/controller, succeeded, target_key, throw_count_key)
- var/atom/throw_target = controller.blackboard[target_key]
+/datum/ai_behavior/item_move_close_and_attack/ghostly/haunted
+
+/datum/ai_behavior/item_move_close_and_attack/ghostly/haunted/finish_action(datum/ai_controller/controller, succeeded, target_key, throw_count_key)
+ var/datum/weakref/target_ref = controller.blackboard[target_key]
+ var/atom/throw_target = target_ref?.resolve()
var/list/hauntee_list = controller.blackboard[BB_TO_HAUNT_LIST]
hauntee_list[throw_target]--
return ..()
diff --git a/code/datums/ai/cursed/cursed_behaviors.dm b/code/datums/ai/cursed/cursed_behaviors.dm
index 876b99a09cc..be04d534c92 100644
--- a/code/datums/ai/cursed/cursed_behaviors.dm
+++ b/code/datums/ai/cursed/cursed_behaviors.dm
@@ -1,10 +1,8 @@
+/datum/ai_behavior/item_move_close_and_attack/ghostly/cursed
-/datum/ai_behavior/item_move_close_and_attack/cursed
- attack_sound = 'sound/items/haunted/ghostitemattack.ogg'
- max_attempts = 4
-
-/datum/ai_behavior/item_move_close_and_attack/cursed/reset_blackboard(datum/ai_controller/controller, succeeded, target_key, throw_count_key)
- var/atom/throw_target = controller.blackboard[target_key]
+/datum/ai_behavior/item_move_close_and_attack/ghostly/cursed/reset_blackboard(datum/ai_controller/controller, succeeded, target_key, throw_count_key)
+ var/datum/weakref/target_ref = controller.blackboard[target_key]
+ var/atom/throw_target = target_ref?.resolve()
//dropping our target from the blackboard if they are no longer a valid target after the attack behavior
if(get_dist(throw_target, controller.pawn) > CURSED_VIEW_RANGE)
controller.blackboard[target_key] = null
diff --git a/code/datums/ai/cursed/cursed_subtrees.dm b/code/datums/ai/cursed/cursed_subtrees.dm
index 85a85bdb895..34c8066d808 100644
--- a/code/datums/ai/cursed/cursed_subtrees.dm
+++ b/code/datums/ai/cursed/cursed_subtrees.dm
@@ -2,7 +2,8 @@
var/obj/item/item_pawn = controller.pawn
//make sure we have a target
- var/mob/living/carbon/curse_target = controller.blackboard[BB_CURSE_TARGET]
+ var/datum/weakref/target_ref = controller.blackboard[BB_CURSE_TARGET]
+ var/mob/living/carbon/curse_target = target_ref?.resolve()
if(!curse_target)
controller.queue_behavior(/datum/ai_behavior/find_and_set, BB_CURSE_TARGET, /mob/living/carbon, CURSED_VIEW_RANGE)
return
@@ -11,4 +12,4 @@
controller.blackboard[BB_CURSE_TARGET] = null
return
controller.current_movement_target = curse_target
- controller.queue_behavior(/datum/ai_behavior/item_move_close_and_attack/cursed)
+ controller.queue_behavior(/datum/ai_behavior/item_move_close_and_attack/ghostly/cursed)
diff --git a/code/datums/ai/generic/generic_behaviors.dm b/code/datums/ai/generic/generic_behaviors.dm
index faf28988869..89f6d933c64 100644
--- a/code/datums/ai/generic/generic_behaviors.dm
+++ b/code/datums/ai/generic/generic_behaviors.dm
@@ -79,20 +79,22 @@
pawn.activate_hand(pawn.get_active_hand())
finish_action(controller, TRUE)
-/// Use the currently held item, or unarmed, on an object in the world
+/// Use the currently held item, or unarmed, on a weakref to an object in the world
/datum/ai_behavior/use_on_object
required_distance = 1
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT
/datum/ai_behavior/use_on_object/setup(datum/ai_controller/controller, target_key)
. = ..()
- controller.current_movement_target = controller.blackboard[target_key]
+ var/datum/weakref/target_ref = controller.blackboard[target_key]
+ controller.current_movement_target = target_ref?.resolve()
/datum/ai_behavior/use_on_object/perform(delta_time, datum/ai_controller/controller, target_key)
. = ..()
var/mob/living/pawn = controller.pawn
var/obj/item/held_item = pawn.get_item_by_slot(pawn.get_active_hand())
- var/atom/target = controller.blackboard[BB_MONKEY_CURRENT_PRESS_TARGET]
+ var/datum/weakref/target_ref = controller.blackboard[target_key]
+ var/atom/target = target_ref?.resolve()
if(!target || !pawn.CanReach(target))
finish_action(controller, FALSE)
@@ -113,13 +115,15 @@
/datum/ai_behavior/give/setup(datum/ai_controller/controller, target_key)
. = ..()
- controller.current_movement_target = controller.blackboard[target_key]
+ var/datum/weakref/target_ref = controller.blackboard[target_key]
+ controller.current_movement_target = target_ref?.resolve()
/datum/ai_behavior/give/perform(delta_time, datum/ai_controller/controller, target_key)
. = ..()
var/mob/living/pawn = controller.pawn
var/obj/item/held_item = pawn.get_item_by_slot(pawn.get_active_hand())
- var/atom/target = controller.blackboard[target_key]
+ var/datum/weakref/target_ref = controller.blackboard[target_key]
+ var/atom/target = target_ref?.resolve()
if(!target || !pawn.CanReach(target) || !isliving(target))
finish_action(controller, FALSE)
@@ -151,12 +155,14 @@
/datum/ai_behavior/consume/setup(datum/ai_controller/controller, target_key)
. = ..()
- controller.current_movement_target = controller.blackboard[target_key]
+ var/datum/weakref/target_ref = controller.blackboard[target_key]
+ controller.current_movement_target = target_ref?.resolve()
/datum/ai_behavior/consume/perform(delta_time, datum/ai_controller/controller, target_key, hunger_timer_key)
. = ..()
var/mob/living/living_pawn = controller.pawn
- var/obj/item/target = controller.blackboard[target_key]
+ var/datum/weakref/target_ref = controller.blackboard[target_key]
+ var/obj/item/target = target_ref.resolve()
if(!(target in living_pawn.held_items))
if(!living_pawn.put_in_hand_check(target))
@@ -187,7 +193,7 @@
. = ..()
var/find_this_thing = search_tactic(controller, locate_path, search_range)
if(find_this_thing)
- controller.blackboard[set_key] = find_this_thing
+ controller.blackboard[set_key] = WEAKREF(find_this_thing)
finish_action(controller, TRUE)
else
finish_action(controller, FALSE)
@@ -341,7 +347,8 @@
/datum/ai_behavior/setup_instrument/perform(delta_time, datum/ai_controller/controller, song_instrument_key, song_lines_key)
. = ..()
- var/obj/item/instrument/song_instrument = controller.blackboard[song_instrument_key]
+ var/datum/weakref/instrument_ref = controller.blackboard[song_instrument_key]
+ var/obj/item/instrument/song_instrument = instrument_ref.resolve()
var/datum/song/song = song_instrument.song
var/song_lines = controller.blackboard[song_lines_key]
@@ -357,7 +364,8 @@
/datum/ai_behavior/play_instrument/perform(delta_time, datum/ai_controller/controller, song_instrument_key)
. = ..()
- var/obj/item/instrument/song_instrument = controller.blackboard[song_instrument_key]
+ var/datum/weakref/instrument_ref = controller.blackboard[song_instrument_key]
+ var/obj/item/instrument/song_instrument = instrument_ref.resolve()
var/datum/song/song = song_instrument.song
song.start_playing(controller.pawn)
@@ -377,5 +385,5 @@
possible_targets += thing
if(!possible_targets.len)
finish_action(controller, FALSE)
- controller.blackboard[target_key] = pick(possible_targets)
+ controller.blackboard[target_key] = WEAKREF(pick(possible_targets))
finish_action(controller, TRUE)
diff --git a/code/datums/ai/generic/generic_subtrees.dm b/code/datums/ai/generic/generic_subtrees.dm
index 138b47e2efe..c2ad49f8ea4 100644
--- a/code/datums/ai/generic/generic_subtrees.dm
+++ b/code/datums/ai/generic/generic_subtrees.dm
@@ -8,12 +8,13 @@
* * BB_SONG_LINES - not set by this subtree, is the song loaded into the song datum.
*/
/datum/ai_planning_subtree/generic_play_instrument/SelectBehaviors(datum/ai_controller/controller, delta_time)
- if(!controller.blackboard[BB_SONG_INSTRUMENT])
+ var/datum/weakref/player_ref = controller.blackboard[BB_SONG_INSTRUMENT]
+ var/obj/item/instrument/song_player = player_ref?.resolve()
+
+ if(!song_player)
controller.queue_behavior(/datum/ai_behavior/find_and_set/in_hands, BB_SONG_INSTRUMENT, /obj/item/instrument)
return //we can't play a song since we do not have an instrument
- var/obj/item/instrument/song_player = controller.blackboard[BB_SONG_INSTRUMENT]
-
var/list/parsed_song_lines = splittext(controller.blackboard[BB_SONG_LINES], "\n")
popleft(parsed_song_lines) //remove BPM as it is parsed out
if(!compare_list(song_player.song.lines, parsed_song_lines) || !song_player.song.repeat)
@@ -53,7 +54,8 @@
if(world.time < controller.blackboard[BB_NEXT_HUNGRY])
return
- if(!controller.blackboard[BB_FOOD_TARGET])
+ var/datum/weakref/food_ref = controller.blackboard[BB_FOOD_TARGET]
+ if(!food_ref?.resolve())
controller.queue_behavior(/datum/ai_behavior/find_and_set/edible, BB_FOOD_TARGET, /obj/item, 2)
return
diff --git a/code/datums/ai/hauntium/hauntium_subtrees.dm b/code/datums/ai/hauntium/hauntium_subtrees.dm
index ac6673e025e..95a37f25094 100644
--- a/code/datums/ai/hauntium/hauntium_subtrees.dm
+++ b/code/datums/ai/hauntium/hauntium_subtrees.dm
@@ -13,11 +13,11 @@
var/list/to_haunt_list = controller.blackboard[BB_TO_HAUNT_LIST]
- for(var/i in to_haunt_list)
- if(to_haunt_list[i] <= 0)
+ for(var/mob/living/potential_target as anything in to_haunt_list)
+ if(to_haunt_list[potential_target] <= 0)
+ to_haunt_list -= potential_target
continue
- var/mob/living/potential_target = i
if(get_dist(potential_target, item_pawn) <= 7)
controller.blackboard[BB_HAUNT_TARGET] = potential_target
- controller.queue_behavior(/datum/ai_behavior/item_move_close_and_attack/haunted, BB_HAUNT_TARGET, BB_HAUNTED_THROW_ATTEMPT_COUNT)
+ controller.queue_behavior(/datum/ai_behavior/item_move_close_and_attack/ghostly/haunted, BB_HAUNT_TARGET, BB_HAUNTED_THROW_ATTEMPT_COUNT)
return SUBTREE_RETURN_FINISH_PLANNING
diff --git a/code/datums/ai/monkey/monkey_behaviors.dm b/code/datums/ai/monkey/monkey_behaviors.dm
index 54dc41377ee..00b7a52a93e 100644
--- a/code/datums/ai/monkey/monkey_behaviors.dm
+++ b/code/datums/ai/monkey/monkey_behaviors.dm
@@ -127,7 +127,7 @@
// flee from anyone who attacked us and we didn't beat down
for(var/mob/living/L in view(living_pawn, MONKEY_FLEE_VISION))
- if(controller.blackboard[BB_MONKEY_ENEMIES][L] && L.stat == CONSCIOUS)
+ if(controller.blackboard[BB_MONKEY_ENEMIES][WEAKREF(L)] && L.stat == CONSCIOUS)
target = L
break
@@ -141,12 +141,14 @@
/datum/ai_behavior/monkey_attack_mob/setup(datum/ai_controller/controller, target_key)
. = ..()
- controller.current_movement_target = controller.blackboard[BB_MONKEY_CURRENT_ATTACK_TARGET]
+ var/datum/weakref/target_ref = controller.blackboard[target_key]
+ controller.current_movement_target = target_ref?.resolve()
/datum/ai_behavior/monkey_attack_mob/perform(delta_time, datum/ai_controller/controller, target_key)
. = ..()
- var/mob/living/target = controller.blackboard[BB_MONKEY_CURRENT_ATTACK_TARGET]
+ var/datum/weakref/target_ref = controller.blackboard[target_key]
+ var/mob/living/target = target_ref?.resolve()
var/mob/living/living_pawn = controller.pawn
if(!target || target.stat != CONSCIOUS)
@@ -167,11 +169,11 @@
monkey_attack(controller, target, delta_time, FALSE)
-/datum/ai_behavior/monkey_attack_mob/finish_action(datum/ai_controller/controller, succeeded)
+/datum/ai_behavior/monkey_attack_mob/finish_action(datum/ai_controller/controller, succeeded, target_key)
. = ..()
var/mob/living/living_pawn = controller.pawn
walk(living_pawn, 0)
- controller.blackboard[BB_MONKEY_CURRENT_ATTACK_TARGET] = null
+ controller.blackboard[target_key] = null
/// attack using a held weapon otherwise bite the enemy, then if we are angry there is a chance we might calm down a little
/datum/ai_behavior/monkey_attack_mob/proc/monkey_attack(datum/ai_controller/controller, mob/living/target, delta_time, disarm)
@@ -219,14 +221,16 @@
if(controller.blackboard[BB_MONKEY_AGGRESSIVE])
return
+ /// mob refs are uids, so this is safe
+ var/datum/weakref/target_ref = WEAKREF(target)
if(DT_PROB(MONKEY_HATRED_REDUCTION_PROB, delta_time))
- controller.blackboard[BB_MONKEY_ENEMIES][target]--
+ controller.blackboard[BB_MONKEY_ENEMIES][target_ref]--
// if we are not angry at our target, go back to idle
- if(controller.blackboard[BB_MONKEY_ENEMIES][target] <= 0)
+ if(controller.blackboard[BB_MONKEY_ENEMIES][target_ref] <= 0)
var/list/enemies = controller.blackboard[BB_MONKEY_ENEMIES]
- enemies.Remove(target)
- if(controller.blackboard[BB_MONKEY_CURRENT_ATTACK_TARGET] == target)
+ enemies.Remove(target_ref)
+ if(controller.blackboard[BB_MONKEY_CURRENT_ATTACK_TARGET] == WEAKREF(target))
finish_action(controller, TRUE)
/datum/ai_behavior/disposal_mob
@@ -234,8 +238,8 @@
/datum/ai_behavior/disposal_mob/setup(datum/ai_controller/controller, attack_target_key, disposal_target_key)
. = ..()
- controller.current_movement_target = controller.blackboard[BB_MONKEY_CURRENT_ATTACK_TARGET]
-
+ var/datum/weakref/target_ref = controller.blackboard[attack_target_key]
+ controller.current_movement_target = target_ref?.resolve()
/datum/ai_behavior/disposal_mob/finish_action(datum/ai_controller/controller, succeeded, attack_target_key, disposal_target_key)
. = ..()
@@ -249,7 +253,8 @@
if(controller.blackboard[BB_MONKEY_DISPOSING]) //We are disposing, don't do ANYTHING!!!!
return
- var/mob/living/target = controller.blackboard[attack_target_key]
+ var/datum/weakref/target_ref = controller.blackboard[attack_target_key]
+ var/mob/living/target = target_ref?.resolve()
var/mob/living/living_pawn = controller.pawn
controller.current_movement_target = target
@@ -259,7 +264,8 @@
target.grabbedby(living_pawn)
return //Do the rest next turn
- var/obj/machinery/disposal/disposal = controller.blackboard[disposal_target_key]
+ var/datum/weakref/disposal_ref = controller.blackboard[disposal_target_key]
+ var/obj/machinery/disposal/disposal = disposal_ref.resolve()
controller.current_movement_target = disposal
if(living_pawn.Adjacent(disposal))
@@ -269,7 +275,8 @@
/datum/ai_behavior/disposal_mob/proc/try_disposal_mob(datum/ai_controller/controller, attack_target_key, disposal_target_key)
var/mob/living/living_pawn = controller.pawn
- var/mob/living/target = controller.blackboard[attack_target_key]
+ var/datum/weakref/target_ref = controller.blackboard[attack_target_key]
+ var/mob/living/target = target_ref?.resolve()
var/obj/machinery/disposal/disposal = controller.blackboard[disposal_target_key]
controller.blackboard[BB_MONKEY_DISPOSING] = TRUE
@@ -292,9 +299,9 @@
if(!DT_PROB(MONKEY_RECRUIT_PROB, delta_time))
continue
var/datum/ai_controller/monkey/monkey_ai = L.ai_controller
- var/atom/your_enemy = controller.blackboard[BB_MONKEY_CURRENT_ATTACK_TARGET]
+ var/datum/weakref/enemy_ref = controller.blackboard[BB_MONKEY_CURRENT_ATTACK_TARGET]
var/list/enemies = L.ai_controller.blackboard[BB_MONKEY_ENEMIES]
- enemies[your_enemy] = MONKEY_RECRUIT_HATED_AMOUNT
+ enemies[enemy_ref] = MONKEY_RECRUIT_HATED_AMOUNT
monkey_ai.blackboard[BB_MONKEY_RECRUIT_COOLDOWN] = world.time + MONKEY_RECRUIT_COOLDOWN
finish_action(controller, TRUE)
@@ -302,10 +309,11 @@
var/list/enemies = controller.blackboard[enemies_key]
var/list/valids = list()
for(var/mob/living/possible_enemy in view(MONKEY_ENEMY_VISION, controller.pawn))
- if(possible_enemy == controller.pawn || (!enemies[possible_enemy] && (!controller.blackboard[BB_MONKEY_AGGRESSIVE] || HAS_AI_CONTROLLER_TYPE(possible_enemy, /datum/ai_controller/monkey)))) //Are they an enemy? (And do we even care?)
+ var/datum/weakref/enemy_ref = WEAKREF(possible_enemy)
+ if(possible_enemy == controller.pawn || (!enemies[enemy_ref] && (!controller.blackboard[BB_MONKEY_AGGRESSIVE] || HAS_AI_CONTROLLER_TYPE(possible_enemy, /datum/ai_controller/monkey)))) //Are they an enemy? (And do we even care?)
continue
// Weighted list, so the closer they are the more likely they are to be chosen as the enemy
- valids[possible_enemy] = CEILING(100 / (get_dist(controller.pawn, possible_enemy) || 1), 1)
+ valids[enemy_ref] = CEILING(100 / (get_dist(controller.pawn, possible_enemy) || 1), 1)
if(!valids.len)
finish_action(controller, FALSE)
diff --git a/code/datums/ai/monkey/monkey_controller.dm b/code/datums/ai/monkey/monkey_controller.dm
index ff4fc5c219f..ceaa6d5fd81 100644
--- a/code/datums/ai/monkey/monkey_controller.dm
+++ b/code/datums/ai/monkey/monkey_controller.dm
@@ -125,7 +125,7 @@ have ways of interacting with a specific mob and control it.
///Reactive events to being hit
/datum/ai_controller/monkey/proc/retaliate(mob/living/L)
var/list/enemies = blackboard[BB_MONKEY_ENEMIES]
- enemies[L] += MONKEY_HATRED_AMOUNT
+ enemies[WEAKREF(L)] += MONKEY_HATRED_AMOUNT
/datum/ai_controller/monkey/proc/on_attackby(datum/source, obj/item/I, mob/user)
SIGNAL_HANDLER
diff --git a/code/datums/ai/monkey/monkey_subtrees.dm b/code/datums/ai/monkey/monkey_subtrees.dm
index 3655c02df75..443d927127a 100644
--- a/code/datums/ai/monkey/monkey_subtrees.dm
+++ b/code/datums/ai/monkey/monkey_subtrees.dm
@@ -36,7 +36,8 @@
controller.queue_behavior(/datum/ai_behavior/monkey_set_combat_target, BB_MONKEY_CURRENT_ATTACK_TARGET, BB_MONKEY_ENEMIES)
return SUBTREE_RETURN_FINISH_PLANNING
- var/mob/living/selected_enemy = controller.blackboard[BB_MONKEY_CURRENT_ATTACK_TARGET]
+ var/datum/weakref/target_ref = controller.blackboard[BB_MONKEY_CURRENT_ATTACK_TARGET]
+ var/mob/living/selected_enemy = target_ref?.resolve()
if(!selected_enemy.stat) //He's up, get him!
if(living_pawn.health < MONKEY_FLEE_HEALTH) //Time to skeddadle
diff --git a/code/datums/ai/objects/cursed/cursed_subtrees.dm b/code/datums/ai/objects/cursed/cursed_subtrees.dm
index 75cc202c9ec..b884334cb03 100644
--- a/code/datums/ai/objects/cursed/cursed_subtrees.dm
+++ b/code/datums/ai/objects/cursed/cursed_subtrees.dm
@@ -2,13 +2,14 @@
var/obj/item/item_pawn = controller.pawn
//make sure we have a target
- var/mob/living/carbon/curse_target = controller.blackboard[BB_ITEM_TARGET]
+ var/datum/weakref/target_ref = controller.blackboard[BB_ITEM_TARGET]
+ var/mob/living/carbon/curse_target = target_ref?.resolve()
if(curse_target && get_dist(curse_target, item_pawn) > ITEM_AGGRO_VIEW_RANGE)
controller.blackboard[BB_ITEM_TARGET] = null
return
- if(!controller.blackboard[BB_ITEM_TARGET])
+ if(!curse_target)
controller.queue_behavior(/datum/ai_behavior/find_and_set/item_target, BB_ITEM_TARGET, /mob/living/carbon, ITEM_AGGRO_VIEW_RANGE)
controller.queue_behavior(/datum/ai_behavior/item_move_close_and_attack/ghostly, BB_ITEM_TARGET, BB_ITEM_THROW_ATTEMPT_COUNT)
diff --git a/code/datums/ai/objects/item_behaviors.dm b/code/datums/ai/objects/item_behaviors.dm
index 5db7f2eb789..2d98593743f 100644
--- a/code/datums/ai/objects/item_behaviors.dm
+++ b/code/datums/ai/objects/item_behaviors.dm
@@ -11,45 +11,3 @@
item_pawn.visible_message(span_warning("[item_pawn] slips out of the hands of [item_holder]!"))
item_holder.dropItemToGround(item_pawn, TRUE)
finish_action(controller, TRUE)
-
-///This behavior is for obj/items, it is used to move closer to a target and throw themselves towards them.
-/datum/ai_behavior/item_move_close_and_attack
- required_distance = 3
- behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT
- action_cooldown = 20
- ///Sound to use
- var/attack_sound
- ///Max attempts to make
- var/max_attempts = 3
-
-
-/datum/ai_behavior/item_move_close_and_attack/setup(datum/ai_controller/controller, aggro_list_key, target_key, throw_count_key)
- . = ..()
- controller.current_movement_target = controller.blackboard[target_key]
-
-
-/datum/ai_behavior/item_move_close_and_attack/perform(delta_time, datum/ai_controller/controller, aggro_list_key, target_key, throw_count_key)
- . = ..()
- var/obj/item/item_pawn = controller.pawn
- var/atom/throw_target = controller.blackboard[target_key]
-
- item_pawn.visible_message(span_warning("[item_pawn] hurls towards [throw_target]!"))
- item_pawn.throw_at(throw_target, rand(4,5), 9)
- playsound(item_pawn.loc, attack_sound, 100, TRUE)
- controller.blackboard[throw_count_key]++
- if(controller.blackboard[throw_count_key] >= max_attempts)
- finish_action(controller, TRUE, target_key, throw_count_key)
-
-/datum/ai_behavior/item_move_close_and_attack/finish_action(datum/ai_controller/controller, succeeded, aggro_list_key, target_key, throw_count_key)
- . = ..()
- var/atom/throw_target = controller.blackboard[target_key]
-
- controller.blackboard -= target_key
- controller.blackboard[throw_count_key] = 0
- if(aggro_list_key)
- var/list/aggro_list = controller.blackboard[aggro_list_key]
- aggro_list[throw_target]--
-
-/datum/ai_behavior/item_move_close_and_attack/ghostly
- attack_sound = 'sound/items/haunted/ghostitemattack.ogg'
- max_attempts = 4
diff --git a/code/datums/ai/robot_customer/robot_customer_behaviors.dm b/code/datums/ai/robot_customer/robot_customer_behaviors.dm
index ece537b3ba6..c9a5bd03005 100644
--- a/code/datums/ai/robot_customer/robot_customer_behaviors.dm
+++ b/code/datums/ai/robot_customer/robot_customer_behaviors.dm
@@ -26,7 +26,7 @@
if(found_seat)
customer_pawn.say(pick(customer_data.found_seat_lines))
- controller.blackboard[BB_CUSTOMER_MY_SEAT] = found_seat
+ controller.blackboard[BB_CUSTOMER_MY_SEAT] = WEAKREF(found_seat)
attending_venue.linked_seats[found_seat] = customer_pawn
finish_action(controller, TRUE)
return
@@ -47,7 +47,9 @@
var/mob/living/simple_animal/robot_customer/customer_pawn = controller.pawn
var/datum/customer_data/customer_data = controller.blackboard[BB_CUSTOMER_CUSTOMERINFO]
- if(get_turf(controller.blackboard[BB_CUSTOMER_MY_SEAT]) == get_turf(customer_pawn))
+ var/datum/weakref/seat_ref = controller.blackboard[BB_CUSTOMER_MY_SEAT]
+ var/obj/structure/holosign/robot_seat/seat_marker = seat_ref?.resolve()
+ if(get_turf(seat_marker) == get_turf(customer_pawn))
var/obj/structure/chair/my_seat = locate(/obj/structure/chair) in get_turf(customer_pawn)
if(my_seat)
controller.pawn.setDir(my_seat.dir) //Sit in your seat
@@ -79,7 +81,9 @@
var/datum/customer_data/customer_data = controller.blackboard[BB_CUSTOMER_CUSTOMERINFO]
customer_pawn.say(pick(customer_data.wait_for_food_lines))
- if(get_turf(controller.blackboard[BB_CUSTOMER_MY_SEAT]) == get_turf(controller.pawn))
+ var/datum/weakref/seat_ref = controller.blackboard[BB_CUSTOMER_MY_SEAT]
+ var/obj/structure/holosign/robot_seat/seat_marker = seat_ref?.resolve()
+ if(get_turf(seat_marker) == get_turf(controller.pawn))
var/obj/structure/chair/my_seat = locate(/obj/structure/chair) in get_turf(controller.pawn)
if(my_seat)
controller.pawn.setDir(my_seat.dir) //Sit in your seat
diff --git a/code/datums/ai/robot_customer/robot_customer_controller.dm b/code/datums/ai/robot_customer/robot_customer_controller.dm
index 2c59fe5d94c..01bee770340 100644
--- a/code/datums/ai/robot_customer/robot_customer_controller.dm
+++ b/code/datums/ai/robot_customer/robot_customer_controller.dm
@@ -67,9 +67,10 @@
var/mob/living/simple_animal/robot_customer/customer = pawn
var/datum/venue/attending_venue = blackboard[BB_CUSTOMER_ATTENDING_VENUE]
var/datum/customer_data/customer_data = blackboard[BB_CUSTOMER_CUSTOMERINFO]
- attending_venue.mob_blacklist[greytider] += 1
+ //Living mobs are tagged, so these will always be valid
+ attending_venue.mob_blacklist[REF(greytider)] += 1
- switch(attending_venue.mob_blacklist[greytider])
+ switch(attending_venue.mob_blacklist[REF(greytider)])
if(1)
customer.say(customer_data.first_warning_line)
return
diff --git a/code/datums/ai/robot_customer/robot_customer_subtrees.dm b/code/datums/ai/robot_customer/robot_customer_subtrees.dm
index bfde3afa2aa..7560516e44c 100644
--- a/code/datums/ai/robot_customer/robot_customer_subtrees.dm
+++ b/code/datums/ai/robot_customer/robot_customer_subtrees.dm
@@ -8,13 +8,14 @@
controller.queue_behavior(/datum/ai_behavior/break_spine, BB_CUSTOMER_CURRENT_TARGET)
return SUBTREE_RETURN_FINISH_PLANNING
- var/obj/my_seat = controller.blackboard[BB_CUSTOMER_MY_SEAT]
+ var/datum/weakref/seat_ref = controller.blackboard[BB_CUSTOMER_MY_SEAT]
+ var/obj/structure/holosign/robot_seat/seat_marker = seat_ref?.resolve()
- if(!my_seat) //We havn't got a seat yet! find one!
+ if(!seat_marker) //We havn't got a seat yet! find one!
controller.queue_behavior(/datum/ai_behavior/find_seat)
return SUBTREE_RETURN_FINISH_PLANNING
- controller.current_movement_target = my_seat
+ controller.current_movement_target = seat_marker
if(!controller.blackboard[BB_CUSTOMER_CURRENT_ORDER]) //We havn't ordered yet even ordered yet. go on! go over there and go do it!
controller.queue_behavior(/datum/ai_behavior/order_food)
diff --git a/code/datums/components/aura_healing.dm b/code/datums/components/aura_healing.dm
index 9127d8e4680..44a6956895d 100644
--- a/code/datums/components/aura_healing.dm
+++ b/code/datums/components/aura_healing.dm
@@ -85,8 +85,11 @@
/datum/component/aura_healing/Destroy(force, silent)
STOP_PROCESSING(SSaura_healing, src)
+ var/alert_category = "aura_healing_[REF(src)]"
- QDEL_LIST_ASSOC_VAL(current_alerts)
+ for(var/mob/living/alert_holder in current_alerts)
+ alert_holder.clear_alert(alert_category)
+ current_alerts.Cut()
return ..()
@@ -108,7 +111,7 @@
if (!(candidate in current_alerts))
var/atom/movable/screen/alert/aura_healing/alert = candidate.throw_alert(alert_category, /atom/movable/screen/alert/aura_healing, new_master = parent)
alert.desc = "You are being healed by [parent]."
- current_alerts[candidate] = alert
+ current_alerts += candidate
if (should_show_effect && candidate.health < candidate.maxHealth)
new /obj/effect/temp_visual/heal(get_turf(candidate), healing_color)
diff --git a/code/datums/components/shrink.dm b/code/datums/components/shrink.dm
index 03b454809aa..9c1c5f76dcd 100644
--- a/code/datums/components/shrink.dm
+++ b/code/datums/components/shrink.dm
@@ -38,4 +38,4 @@
if(ishuman(L))
var/mob/living/carbon/human/H = L
H.physiology.damage_resistance += 100
- ..()
+ return ..()
diff --git a/code/datums/components/tackle.dm b/code/datums/components/tackle.dm
index dd5d0fcaf07..4f8e6cfce25 100644
--- a/code/datums/components/tackle.dm
+++ b/code/datums/components/tackle.dm
@@ -28,8 +28,8 @@
var/skill_mod
///Some gloves, generally ones that increase mobility, may have a minimum distance to fly. Rocket gloves are especially dangerous with this, be sure you'll hit your target or have a clear background if you miss, or else!
var/min_distance
- ///The throwdatum we're currently dealing with, if we need it
- var/datum/thrownthing/tackle
+ ///A wearkef to the throwdatum we're currently dealing with, if we need it
+ var/datum/weakref/tackle_ref
/datum/component/tackler/Initialize(stamina_cost = 25, base_knockdown = 1 SECONDS, range = 4, speed = 1, skill_mod = 0, min_distance = min_distance)
if(!iscarbon(parent))
@@ -61,10 +61,10 @@
UnregisterSignal(parent, list(COMSIG_MOB_CLICKON, COMSIG_MOVABLE_IMPACT, COMSIG_MOVABLE_MOVED, COMSIG_MOVABLE_POST_THROW))
///Store the thrownthing datum for later use
-/datum/component/tackler/proc/registerTackle(mob/living/carbon/user, datum/thrownthing/TT)
+/datum/component/tackler/proc/registerTackle(mob/living/carbon/user, datum/thrownthing/tackle)
SIGNAL_HANDLER
- tackle = TT
+ tackle_ref = WEAKREF(tackle)
tackle.thrower = user
///See if we can tackle or not. If we can, leap!
@@ -143,7 +143,9 @@
/datum/component/tackler/proc/sack(mob/living/carbon/user, atom/hit)
SIGNAL_HANDLER
+ var/datum/thrownthing/tackle = tackle_ref?.resolve()
if(!tackling || !tackle)
+ tackle = null
return
user.toggle_throw_mode()
@@ -445,7 +447,7 @@
/datum/component/tackler/proc/resetTackle()
tackling = FALSE
- QDEL_NULL(tackle)
+ QDEL_NULL(tackle_ref)
UnregisterSignal(parent, COMSIG_MOVABLE_MOVED)
///A special case for splatting for handling windows
@@ -526,8 +528,11 @@
I.throw_at(get_ranged_target_turf(I, pick(GLOB.alldirs), range = dist), range = dist, speed = sp)
I.visible_message(span_danger("[I] goes flying[sp > 3 ? " dangerously fast" : ""]!")) // standard embed speed
+ var/datum/thrownthing/tackle = tackle_ref?.resolve()
+
playsound(owner, 'sound/weapons/smash.ogg', 70, TRUE)
- tackle.finalize(hit=TRUE)
+ if(tackle)
+ tackle.finalize(hit=TRUE)
resetTackle()
#undef MAX_TABLE_MESSES
diff --git a/code/datums/dna.dm b/code/datums/dna.dm
index fab840c2240..3cc36dc5973 100644
--- a/code/datums/dna.dm
+++ b/code/datums/dna.dm
@@ -112,7 +112,10 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
new_dna.species = new species.type
new_dna.species.species_traits = species.species_traits
new_dna.real_name = real_name
- new_dna.mutations = mutations.Copy()
+ // Mutations aren't gc managed, but they still aren't templates
+ // Let's do a proper copy
+ for(var/datum/mutation/human/mutation in mutations)
+ new_dna.add_mutation(mutation, mutation.class, mutation.timeout)
//See mutation.dm for what 'class' does. 'time' is time till it removes itself in decimals. 0 for no timer
/datum/dna/proc/add_mutation(mutation, class = MUT_OTHER, time)
diff --git a/code/datums/memory/memory.dm b/code/datums/memory/memory.dm
index 50310bf0924..f2cbdd3bb31 100644
--- a/code/datums/memory/memory.dm
+++ b/code/datums/memory/memory.dm
@@ -10,7 +10,7 @@
var/datum/mind/memorizer_mind
///the action done to the target, see memory.dm in _DEFINES
var/action
- ///extra information used in the memories to more accurately describe what happened. Assoc list of key -> string identifying what kind of info it is, value is an atom or string identifying the detail.
+ ///extra information used in the memories to more accurately describe what happened. Assoc list of key -> string identifying what kind of info it is, value is a string identifying the detail.
var/list/extra_info
///mood of the person memorizing the event when it happend. can change the style.
var/memorizer_mood
@@ -24,6 +24,14 @@
src.memorizer_mind = memorizer_mind
src.memorizer = memorizer
src.action = action
+ //You can feed atoms in, but they're gonna be reduced to text
+ for(var/key in extra_info)
+ var/thing = extra_info[key]
+ if(!isdatum(thing))
+ continue
+ var/datum/reduce_to_string = thing
+ extra_info[key] = "[reduce_to_string]"
+
src.extra_info = extra_info
src.memorizer_mood = memorizer_mood
src.story_value = story_value
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 9c605f84afe..77576a5c980 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -705,13 +705,13 @@
* and gives them a fallback spell if no uplink was found
*/
/datum/mind/proc/try_give_equipment_fallback()
- var/datum/component/uplink/uplink
+ var/uplink_exists
var/datum/antagonist/traitor/traitor_datum = has_antag_datum(/datum/antagonist/traitor)
if(traitor_datum)
- uplink = traitor_datum.uplink
- if(!uplink)
- uplink = find_syndicate_uplink(check_unlocked = TRUE)
- if(!uplink && !(locate(/obj/effect/proc_holder/spell/self/special_equipment_fallback) in spell_list))
+ uplink_exists = traitor_datum.uplink_ref
+ if(!uplink_exists)
+ uplink_exists = find_syndicate_uplink(check_unlocked = TRUE)
+ if(!uplink_exists && !(locate(/obj/effect/proc_holder/spell/self/special_equipment_fallback) in spell_list))
AddSpell(new /obj/effect/proc_holder/spell/self/special_equipment_fallback(null, src))
/datum/mind/proc/take_uplink()
diff --git a/code/datums/mutations/_mutations.dm b/code/datums/mutations/_mutations.dm
index 79208a20175..ccf052b24ec 100644
--- a/code/datums/mutations/_mutations.dm
+++ b/code/datums/mutations/_mutations.dm
@@ -23,7 +23,7 @@
var/instability = 0 //instability the holder gets when the mutation is not native
var/blocks = 4 //Amount of those big blocks with gene sequences
var/difficulty = 8 //Amount of missing sequences. Sometimes it removes an entire pair for 2 points
- var/timed = FALSE //Boolean to easily check if we're going to self-destruct
+ var/timeout //Time between mutation creation and removal. If this exists, we have a timer
var/alias //'Mutation #49', decided every round to get some form of distinction between undiscovered mutations
var/scrambled = FALSE //Wheter we can read it if it's active. To avoid cheesing with mutagen
var/class //Decides player accesibility, sorta
@@ -51,7 +51,7 @@
class = class_
if(timer)
addtimer(CALLBACK(src, .proc/remove), timer)
- timed = TRUE
+ timeout = timer
if(copymut && istype(copymut, /datum/mutation/human))
copy_mutation(copymut)
update_valid_chromosome_list()
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index b6b3e747912..1b4455d5a3a 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -137,8 +137,6 @@
LAZYCLEARLIST(client_mobs_in_contents)
- LAZYCLEARLIST(important_recursive_contents)//has to be before moveToNullspace() so that we can exit our spatial_grid cell if we're in it
-
. = ..()
for(var/movable_content in contents)
@@ -146,6 +144,12 @@
moveToNullspace()
+ //This absolutely must be after moveToNullspace()
+ //We rely on Entered and Exited to manage this list, and the copy of this list that is on any /atom/movable "Containers"
+ //If we clear this before the nullspace move, a ref to this object will be hung in any of its movable containers
+ LAZYCLEARLIST(important_recursive_contents)
+
+
vis_locs = null //clears this atom out of all viscontents
vis_contents.Cut()
@@ -814,9 +818,9 @@
for(var/atom/movable/location as anything in get_nested_locs(src) + src)
LAZYREMOVEASSOC(location.important_recursive_contents, RECURSIVE_CONTENTS_AREA_SENSITIVE, src)
-///propogates new_client's mob through our nested contents, similar to other important_recursive_contents procs
+///propogates ourselves through our nested contents, similar to other important_recursive_contents procs
///main difference is that client contents need to possibly duplicate recursive contents for the clients mob AND its eye
-/atom/movable/proc/enable_client_mobs_in_contents(client/new_client)
+/mob/proc/enable_client_mobs_in_contents()
var/turf/our_turf = get_turf(src)
if(our_turf && SSspatial_grid.initialized)
@@ -825,11 +829,10 @@
SSspatial_grid.enter_pre_init_queue(src, RECURSIVE_CONTENTS_CLIENT_MOBS)
for(var/atom/movable/movable_loc as anything in get_nested_locs(src) + src)
- LAZYORASSOCLIST(movable_loc.important_recursive_contents, RECURSIVE_CONTENTS_CLIENT_MOBS, new_client.mob)
-
-///Clears the clients channel of this movables important_recursive_contents list and all nested locs
-/atom/movable/proc/clear_important_client_contents(client/former_client)
+ LAZYORASSOCLIST(movable_loc.important_recursive_contents, RECURSIVE_CONTENTS_CLIENT_MOBS, src)
+///Clears the clients channel of this mob
+/mob/proc/clear_important_client_contents()
var/turf/our_turf = get_turf(src)
if(our_turf && SSspatial_grid.initialized)
@@ -838,7 +841,7 @@
SSspatial_grid.remove_from_pre_init_queue(src, RECURSIVE_CONTENTS_CLIENT_MOBS)
for(var/atom/movable/movable_loc as anything in get_nested_locs(src) + src)
- LAZYREMOVEASSOC(movable_loc.important_recursive_contents, RECURSIVE_CONTENTS_CLIENT_MOBS, former_client.mob)
+ LAZYREMOVEASSOC(movable_loc.important_recursive_contents, RECURSIVE_CONTENTS_CLIENT_MOBS, src)
///Sets the anchored var and returns if it was sucessfully changed or not.
/atom/movable/proc/set_anchored(anchorvalue)
diff --git a/code/game/machinery/civilian_bounties.dm b/code/game/machinery/civilian_bounties.dm
index add748fc4dd..d56dccfecc8 100644
--- a/code/game/machinery/civilian_bounties.dm
+++ b/code/game/machinery/civilian_bounties.dm
@@ -19,10 +19,6 @@
var/obj/item/card/id/inserted_scan_id
circuit = /obj/item/circuitboard/computer/bountypad
-/obj/machinery/computer/piratepad_control/civilian/Initialize(mapload)
- . = ..()
- pad = /obj/machinery/piratepad/civilian
-
/obj/machinery/computer/piratepad_control/civilian/attackby(obj/item/I, mob/living/user, params)
if(isidcard(I))
if(id_insert(user, I, inserted_scan_id))
@@ -33,7 +29,7 @@
/obj/machinery/computer/piratepad_control/multitool_act(mob/living/user, obj/item/multitool/I)
if(istype(I) && istype(I.buffer,/obj/machinery/piratepad/civilian))
to_chat(user, span_notice("You link [src] with [I.buffer] in [I] buffer."))
- pad = I.buffer
+ pad_ref = WEAKREF(I.buffer)
return TRUE
/obj/machinery/computer/piratepad_control/civilian/LateInitialize()
@@ -41,10 +37,11 @@
if(cargo_hold_id)
for(var/obj/machinery/piratepad/civilian/C in GLOB.machines)
if(C.cargo_hold_id == cargo_hold_id)
- pad = C
+ pad_ref = WEAKREF(C)
return
else
- pad = locate() in range(4,src)
+ var/obj/machinery/piratepad/civilian/pad = locate() in range(4,src)
+ pad_ref = WEAKREF(pad)
/obj/machinery/computer/piratepad_control/civilian/recalc()
if(sending)
@@ -58,6 +55,7 @@
playsound(loc, 'sound/machines/synth_no.ogg', 30 , TRUE)
return FALSE
status_report = "Civilian Bounty: "
+ var/obj/machinery/piratepad/pad = pad_ref?.resolve()
for(var/atom/movable/AM in get_turf(pad))
if(AM == pad)
continue
@@ -83,6 +81,7 @@
return FALSE
var/datum/bounty/curr_bounty = inserted_scan_id.registered_account.civilian_bounty
var/active_stack = 0
+ var/obj/machinery/piratepad/pad = pad_ref?.resolve()
for(var/atom/movable/AM in get_turf(pad))
if(AM == pad)
continue
@@ -152,7 +151,7 @@
/obj/machinery/computer/piratepad_control/civilian/ui_data(mob/user)
var/list/data = list()
data["points"] = points
- data["pad"] = pad ? TRUE : FALSE
+ data["pad"] = pad_ref?.resolve() ? TRUE : FALSE
data["sending"] = sending
data["status_report"] = status_report
data["id_inserted"] = inserted_scan_id
@@ -178,7 +177,7 @@
. = ..()
if(.)
return
- if(!pad)
+ if(!pad_ref?.resolve())
return
if(!usr.canUseTopic(src, BE_CLOSE) || (machine_stat & (NOPOWER|BROKEN)))
return
diff --git a/code/game/machinery/computer/camera_advanced.dm b/code/game/machinery/computer/camera_advanced.dm
index 4b0f51193de..1ab635e2cd2 100644
--- a/code/game/machinery/computer/camera_advanced.dm
+++ b/code/game/machinery/computer/camera_advanced.dm
@@ -9,12 +9,17 @@
var/mob/camera/ai_eye/remote/eyeobj
var/mob/living/current_user = null
var/list/networks = list("ss13")
- var/datum/action/innate/camera_off/off_action = new
- var/datum/action/innate/camera_jump/jump_action = new
- ///Camera action button to move up a Z level
- var/datum/action/innate/camera_multiz_up/move_up_action = new
- ///Camera action button to move down a Z level
- var/datum/action/innate/camera_multiz_down/move_down_action = new
+ /// Typepath of the action button we use as "off"
+ /// It's a typepath so subtypes can give it fun new names
+ var/datum/action/innate/camera_off/off_action
+ /// Typepath for jumping
+ var/datum/action/innate/camera_jump/jump_action
+ /// Typepath of the move up action
+ var/datum/action/innate/camera_multiz_up/move_up_action
+ /// Typepath of the move down action
+ var/datum/action/innate/camera_multiz_down/move_down_action
+
+ /// List of all actions to give to a user when they're well, granted actions
var/list/actions = list()
///Should we supress any view changes?
var/should_supress_view_changes = TRUE
@@ -34,6 +39,17 @@
if(lock_override & CAMERA_LOCK_CENTCOM)
z_lock |= SSmapping.levels_by_trait(ZTRAIT_CENTCOM)
+ if(off_action)
+ actions += new off_action(src)
+ if(jump_action)
+ actions += new jump_action(src)
+ //Camera action button to move up a Z level
+ if(move_up_action)
+ actions += new move_up_action(src)
+ //Camera action button to move down a Z level
+ if(move_down_action)
+ actions += new move_down_action(src)
+
/obj/machinery/computer/camera_advanced/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock)
for(var/i in networks)
networks -= i
@@ -51,25 +67,8 @@
eyeobj.origin = src
/obj/machinery/computer/camera_advanced/proc/GrantActions(mob/living/user)
- if(off_action)
- off_action.target = user
- off_action.Grant(user)
- actions += off_action
-
- if(jump_action)
- jump_action.target = user
- jump_action.Grant(user)
- actions += jump_action
-
- if(move_up_action)
- move_up_action.target = user
- move_up_action.Grant(user)
- actions += move_up_action
-
- if(move_down_action)
- move_down_action.target = user
- move_down_action.Grant(user)
- actions += move_down_action
+ for(var/datum/action/to_grant as anything in actions)
+ to_grant.Grant(user)
/obj/machinery/proc/remove_eye_control(mob/living/user)
CRASH("[type] does not implement ai eye handling")
@@ -80,7 +79,6 @@
for(var/V in actions)
var/datum/action/A = V
A.Remove(user)
- actions.Cut()
for(var/V in eyeobj.visibleCameraChunks)
var/datum/camerachunk/C = V
C.remove(eyeobj)
@@ -252,12 +250,11 @@
button_icon_state = "camera_off"
/datum/action/innate/camera_off/Activate()
- if(!target || !isliving(target))
+ if(!owner || !isliving(owner))
return
- var/mob/living/C = target
- var/mob/camera/ai_eye/remote/remote_eye = C.remote_control
+ var/mob/camera/ai_eye/remote/remote_eye = owner.remote_control
var/obj/machinery/computer/camera_advanced/console = remote_eye.origin
- console.remove_eye_control(target)
+ console.remove_eye_control(owner)
/datum/action/innate/camera_jump
name = "Jump To Camera"
@@ -265,10 +262,9 @@
button_icon_state = "camera_jump"
/datum/action/innate/camera_jump/Activate()
- if(!target || !isliving(target))
+ if(!owner || !isliving(owner))
return
- var/mob/living/C = target
- var/mob/camera/ai_eye/remote/remote_eye = C.remote_control
+ var/mob/camera/ai_eye/remote/remote_eye = owner.remote_control
var/obj/machinery/computer/camera_advanced/origin = remote_eye.origin
var/list/L = list()
@@ -300,8 +296,8 @@
if(final)
playsound(origin, 'sound/machines/terminal_prompt_confirm.ogg', 25, FALSE)
remote_eye.setLoc(get_turf(final))
- C.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/flash/static)
- C.clear_fullscreen("flash", 3) //Shorter flash than normal since it's an ~~advanced~~ console!
+ owner.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/flash/static)
+ owner.clear_fullscreen("flash", 3) //Shorter flash than normal since it's an ~~advanced~~ console!
else
playsound(origin, 'sound/machines/terminal_prompt_deny.ogg', 25, FALSE)
@@ -311,14 +307,13 @@
button_icon_state = "move_up"
/datum/action/innate/camera_multiz_up/Activate()
- if(!target || !isliving(target))
+ if(!owner || !isliving(owner))
return
- var/mob/living/user_mob = target
- var/mob/camera/ai_eye/remote/remote_eye = user_mob.remote_control
+ var/mob/camera/ai_eye/remote/remote_eye = owner.remote_control
if(remote_eye.zMove(UP))
- to_chat(user_mob, span_notice("You move upwards."))
+ to_chat(owner, span_notice("You move upwards."))
else
- to_chat(user_mob, span_notice("You couldn't move upwards!"))
+ to_chat(owner, span_notice("You couldn't move upwards!"))
/datum/action/innate/camera_multiz_down
name = "Move down a floor"
@@ -326,11 +321,10 @@
button_icon_state = "move_down"
/datum/action/innate/camera_multiz_down/Activate()
- if(!target || !isliving(target))
+ if(!owner || !isliving(owner))
return
- var/mob/living/user_mob = target
- var/mob/camera/ai_eye/remote/remote_eye = user_mob.remote_control
+ var/mob/camera/ai_eye/remote/remote_eye = owner.remote_control
if(remote_eye.zMove(DOWN))
- to_chat(user_mob, span_notice("You move downwards."))
+ to_chat(owner, span_notice("You move downwards."))
else
- to_chat(user_mob, span_notice("You couldn't move downwards!"))
+ to_chat(owner, span_notice("You couldn't move downwards!"))
diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm
index 45b1d63f721..09475e42b52 100644
--- a/code/game/machinery/computer/crew.dm
+++ b/code/game/machinery/computer/crew.dm
@@ -167,11 +167,12 @@ GLOBAL_DATUM_INIT(crewmonitor, /datum/crewmonitor, new)
ui.open()
/datum/crewmonitor/proc/show(mob/M, source)
- ui_sources[WEAKREF(M)] = source
+ ui_sources[WEAKREF(M)] = WEAKREF(source)
ui_interact(M)
/datum/crewmonitor/ui_host(mob/user)
- return ui_sources[WEAKREF(user)]
+ var/datum/weakref/host_ref = ui_sources[WEAKREF(user)]
+ return host_ref?.resolve()
/datum/crewmonitor/ui_data(mob/user)
var/z = user.z
diff --git a/code/game/machinery/medical_kiosk.dm b/code/game/machinery/medical_kiosk.dm
index b745379af5e..789718fe3fd 100644
--- a/code/game/machinery/medical_kiosk.dm
+++ b/code/game/machinery/medical_kiosk.dm
@@ -36,14 +36,10 @@
/// Do we have someone paying to use this?
var/paying_customer = FALSE //Ticked yes if passing inuse()
- /// Who's pockets are we sifting through when we're used?
- var/datum/bank_account/account //payer's account.
/// Who's paying?
- var/mob/living/carbon/human/H //The person using the console in each instance. Used for paying for the kiosk.
+ var/datum/weakref/paying_ref //The person using the console in each instance. Used for paying for the kiosk.
/// Who's getting scanned?
- var/mob/living/carbon/human/altPatient //If scanning someone else, this will be the target.
- /// Used to find the money.
- var/obj/item/card/id/C //the account of the person using the console.
+ var/datum/weakref/patient_ref //If scanning someone else, this will be the target.
/obj/machinery/medical_kiosk/Initialize(mapload) //loaded subtype for mapping use
. = ..()
@@ -51,16 +47,20 @@
scanner_wand = new/obj/item/scanner_wand(src)
/obj/machinery/medical_kiosk/proc/inuse() //Verifies that the user can use the interface, followed by showing medical information.
- if(C?.registered_account)
- account = C.registered_account
- if(account?.account_job?.paycheck_department == payment_department)
+ var/mob/living/carbon/human/paying = paying_ref?.resolve()
+ if(!paying)
+ paying_ref = null
+ return
+
+ var/obj/item/card/id/card = paying.get_idcard(TRUE)
+ if(card?.registered_account?.account_job?.paycheck_department == payment_department)
use_power(20)
paying_customer = TRUE
say("Hello, esteemed medical staff!")
RefreshParts()
return
var/bonus_fee = pandemonium ? rand(10,30) : 0
- if(attempt_charge(src, H, bonus_fee) & COMPONENT_OBJ_CANCEL_CHARGE )
+ if(attempt_charge(src, paying, bonus_fee) & COMPONENT_OBJ_CANCEL_CHARGE )
return
use_power(20)
paying_customer = TRUE
@@ -113,9 +113,10 @@
span_notice("You press [O] into the side of [src], clicking into place."))
//This will be the scanner returning scanner_wand's selected_target variable and assigning it to the altPatient var
if(W.selected_target)
- if(!(altPatient == W.return_patient()))
+ var/datum/weakref/target_ref = WEAKREF(W.return_patient())
+ if(patient_ref != target_ref)
clearScans()
- altPatient = W.return_patient()
+ patient_ref = target_ref
user.visible_message(span_notice("[W.return_patient()] has been set as the current patient."))
W.selected_target = null
playsound(src, 'sound/machines/click.ogg', 50, TRUE)
@@ -168,12 +169,13 @@
if (ui)
ui.close()
return
- patient_distance = get_dist(src.loc,altPatient)
- if(altPatient == null)
+ var/mob/living/carbon/human/patient = patient_ref?.resolve()
+ patient_distance = get_dist(src.loc, patient)
+ if(patient == null)
say("Scanner reset.")
- altPatient = user
+ patient_ref = WEAKREF(user)
else if(patient_distance>5)
- altPatient = null
+ patient_ref = null
say("Patient out of range. Resetting biometrics.")
clearScans()
return
@@ -183,19 +185,22 @@
ui.open()
icon_state = "[base_icon_state]_active"
RefreshParts()
- H = user
- C = H.get_idcard(TRUE)
+ var/mob/living/carbon/human/paying = user
+ paying_ref = WEAKREF(paying)
/obj/machinery/medical_kiosk/ui_data(mob/living/carbon/human/user)
+ var/mob/living/carbon/human/patient = patient_ref?.resolve()
var/list/data = list()
- var/patient_name = altPatient.name
+ if(!patient)
+ return
+ var/patient_name = patient.name
var/patient_status = "Alive."
- var/max_health = altPatient.maxHealth
- var/total_health = altPatient.health
- var/brute_loss = altPatient.getBruteLoss()
- var/fire_loss = altPatient.getFireLoss()
- var/tox_loss = altPatient.getToxLoss()
- var/oxy_loss = altPatient.getOxyLoss()
+ var/max_health = patient.maxHealth
+ var/total_health = patient.health
+ var/brute_loss = patient.getBruteLoss()
+ var/fire_loss = patient.getFireLoss()
+ var/tox_loss = patient.getToxLoss()
+ var/oxy_loss = patient.getOxyLoss()
var/chaos_modifier = 0
var/sickness = "Patient does not show signs of disease."
@@ -203,18 +208,18 @@
var/bleed_status = "Patient is not currently bleeding."
var/blood_status = " Patient either has no blood, or does not require it to function."
- var/blood_percent = round((altPatient.blood_volume / BLOOD_VOLUME_NORMAL)*100)
- var/blood_type = altPatient.dna.blood_type
+ var/blood_percent = round((patient.blood_volume / BLOOD_VOLUME_NORMAL)*100)
+ var/blood_type = patient.dna.blood_type
var/blood_warning = " "
- for(var/thing in altPatient.diseases) //Disease Information
+ for(var/thing in patient.diseases) //Disease Information
var/datum/disease/D = thing
if(!(D.visibility_flags & HIDDEN_SCANNER))
sickness = "Warning: Patient is harboring some form of viral disease. Seek further medical attention."
sickness_data = "\nName: [D.name].\nType: [D.spread_text].\nStage: [D.stage]/[D.max_stages].\nPossible Cure: [D.cure_text]"
- if(altPatient.has_dna()) //Blood levels Information
- if(altPatient.is_bleeding())
+ if(patient.has_dna()) //Blood levels Information
+ if(patient.is_bleeding())
bleed_status = "Patient is currently bleeding!"
if(blood_percent <= 80)
blood_warning = " Patient has low blood levels. Seek a large meal, or iron supplements."
@@ -223,12 +228,12 @@
blood_status = "Patient blood levels are currently reading [blood_percent]%. Patient has [ blood_type] type blood. [blood_warning]"
var/trauma_status = "Patient is free of unique brain trauma."
- var/clone_loss = altPatient.getCloneLoss()
- var/brain_loss = altPatient.getOrganLoss(ORGAN_SLOT_BRAIN)
+ var/clone_loss = patient.getCloneLoss()
+ var/brain_loss = patient.getOrganLoss(ORGAN_SLOT_BRAIN)
var/brain_status = "Brain patterns normal."
- if(LAZYLEN(altPatient.get_traumas()))
+ if(LAZYLEN(patient.get_traumas()))
var/list/trauma_text = list()
- for(var/t in altPatient.get_traumas())
+ for(var/t in patient.get_traumas())
var/datum/brain_trauma/trauma = t
var/trauma_desc = ""
switch(trauma.resilience)
@@ -247,15 +252,15 @@
var/addict_list = list()
var/hallucination_status = "Patient is not hallucinating."
- if(altPatient.reagents.reagent_list.len) //Chemical Analysis details.
- for(var/r in altPatient.reagents.reagent_list)
+ if(patient.reagents.reagent_list.len) //Chemical Analysis details.
+ for(var/r in patient.reagents.reagent_list)
var/datum/reagent/reagent = r
if(reagent.chemical_flags & REAGENT_INVISIBLE) //Don't show hidden chems
continue
chemical_list += list(list("name" = reagent.name, "volume" = round(reagent.volume, 0.01)))
if(reagent.overdosed)
overdose_list += list(list("name" = reagent.name))
- var/obj/item/organ/stomach/belly = altPatient.getorganslot(ORGAN_SLOT_STOMACH)
+ var/obj/item/organ/stomach/belly = patient.getorganslot(ORGAN_SLOT_STOMACH)
if(belly?.reagents.reagent_list.len) //include the stomach contents if it exists
for(var/bile in belly.reagents.reagent_list)
var/datum/reagent/bit = bile
@@ -267,13 +272,13 @@
var/bit_vol = bit.volume - belly.food_reagents[bit.type]
if(bit_vol > 0)
chemical_list += list(list("name" = bit.name, "volume" = round(bit_vol, 0.01)))
- for(var/datum/addiction/addiction_type as anything in altPatient.mind.active_addictions)
+ for(var/datum/addiction/addiction_type as anything in patient.mind.active_addictions)
addict_list += list(list("name" = initial(addiction_type.name)))
- if (altPatient.hallucinating())
+ if (patient.hallucinating())
hallucination_status = "Subject appears to be hallucinating. Suggested treatments: bedrest, mannitol or psicodine."
- if(altPatient.stat == DEAD || HAS_TRAIT(altPatient, TRAIT_FAKEDEATH) || ((brute_loss+fire_loss+tox_loss+oxy_loss+clone_loss) >= 200)) //Patient status checks.
+ if(patient.stat == DEAD || HAS_TRAIT(patient, TRAIT_FAKEDEATH) || ((brute_loss+fire_loss+tox_loss+oxy_loss+clone_loss) >= 200)) //Patient status checks.
patient_status = "Dead."
if((brute_loss+fire_loss+tox_loss+oxy_loss+clone_loss) >= 80)
patient_status = "Gravely Injured"
@@ -357,6 +362,6 @@
scan_active |= KIOSK_SCANNING_REAGENTS
paying_customer = FALSE
if("clearTarget")
- altPatient = null
+ patient_ref = null
clearScans()
. = TRUE
diff --git a/code/game/objects/effects/anomalies.dm b/code/game/objects/effects/anomalies.dm
index bf49e6a18b8..a9591252e69 100644
--- a/code/game/objects/effects/anomalies.dm
+++ b/code/game/objects/effects/anomalies.dm
@@ -254,61 +254,55 @@
/obj/effect/anomaly/bluespace/detonate()
var/turf/T = pick(get_area_turfs(impact_area))
- if(T)
- // Calculate new position (searches through beacons in world)
- var/obj/item/beacon/chosen
- var/list/possible = list()
- for(var/obj/item/beacon/W in GLOB.teleportbeacons)
- possible += W
+ if(!T)
+ return
- if(possible.len > 0)
- chosen = pick(possible)
+ // Calculate new position (searches through beacons in world)
+ var/obj/item/beacon/chosen
+ var/list/possible = list()
+ for(var/obj/item/beacon/W in GLOB.teleportbeacons)
+ possible += W
- if(chosen)
- // Calculate previous position for transition
+ if(possible.len > 0)
+ chosen = pick(possible)
- var/turf/FROM = T // the turf of origin we're travelling FROM
- var/turf/TO = get_turf(chosen) // the turf of origin we're travelling TO
+ if(!chosen)
+ return
- playsound(TO, 'sound/effects/phasein.ogg', 100, TRUE)
- priority_announce("Massive bluespace translocation detected.", "Anomaly Alert")
+ // Calculate previous position for transition
+ var/turf/FROM = T // the turf of origin we're travelling FROM
+ var/turf/TO = get_turf(chosen) // the turf of origin we're travelling TO
- var/list/flashers = list()
- for(var/mob/living/carbon/C in viewers(TO, null))
- if(C.flash_act())
- flashers += C
+ playsound(TO, 'sound/effects/phasein.ogg', 100, TRUE)
+ priority_announce("Massive bluespace translocation detected.", "Anomaly Alert")
- var/y_distance = TO.y - FROM.y
- var/x_distance = TO.x - FROM.x
- for (var/atom/movable/A in urange(12, FROM )) // iterate thru list of mobs in the area
- if(istype(A, /obj/item/beacon))
- continue // don't teleport beacons because that's just insanely stupid
- if(iscameramob(A))
- continue // Don't mess with AI eye, blob eye, xenobio or advanced cameras
- if(A.anchored)
- continue
+ var/list/flashers = list()
+ for(var/mob/living/carbon/C in viewers(TO, null))
+ if(C.flash_act())
+ flashers += C
- var/turf/newloc = locate(A.x + x_distance, A.y + y_distance, TO.z) // calculate the new place
- if(!A.Move(newloc) && newloc) // if the atom, for some reason, can't move, FORCE them to move! :) We try Move() first to invoke any movement-related checks the atom needs to perform after moving
- A.forceMove(newloc)
+ var/y_distance = TO.y - FROM.y
+ var/x_distance = TO.x - FROM.x
+ for (var/atom/movable/A in urange(12, FROM )) // iterate thru list of mobs in the area
+ if(istype(A, /obj/item/beacon))
+ continue // don't teleport beacons because that's just insanely stupid
+ if(iscameramob(A))
+ continue // Don't mess with AI eye, blob eye, xenobio or advanced cameras
+ if(A.anchored)
+ continue
- if(ismob(A) && !(A in flashers)) // don't flash if we're already doing an effect
- var/mob/M = A
- if(M.client)
- INVOKE_ASYNC(src, .proc/blue_effect, M)
+ var/turf/newloc = locate(A.x + x_distance, A.y + y_distance, TO.z) // calculate the new place
+ if(!A.Move(newloc) && newloc) // if the atom, for some reason, can't move, FORCE them to move! :) We try Move() first to invoke any movement-related checks the atom needs to perform after moving
+ A.forceMove(newloc)
-/obj/effect/anomaly/bluespace/proc/blue_effect(mob/M)
- var/obj/blueeffect = new /obj(src)
- blueeffect.screen_loc = "WEST,SOUTH to EAST,NORTH"
- blueeffect.icon = 'icons/effects/effects.dmi'
- blueeffect.icon_state = "shieldsparkles"
- blueeffect.layer = FLASH_LAYER
- blueeffect.plane = FULLSCREEN_PLANE
- blueeffect.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
- M.client.screen += blueeffect
- sleep(20)
- M.client.screen -= blueeffect
- qdel(blueeffect)
+ if(ismob(A) && !(A in flashers)) // don't flash if we're already doing an effect
+ var/mob/give_sparkles = A
+ if(give_sparkles.client)
+ blue_effect(give_sparkles)
+
+/obj/effect/anomaly/bluespace/proc/blue_effect(mob/make_sparkle)
+ make_sparkle.overlay_fullscreen("bluespace_flash", /atom/movable/screen/fullscreen/bluespace_sparkle, 1)
+ addtimer(CALLBACK(make_sparkle, /mob/.proc/clear_fullscreen, "bluespace_flash"), 2 SECONDS)
/////////////////////
diff --git a/code/game/objects/effects/effect_system/effects_foam.dm b/code/game/objects/effects/effect_system/effects_foam.dm
index 69db61e3afd..1d4d481e28d 100644
--- a/code/game/objects/effects/effect_system/effects_foam.dm
+++ b/code/game/objects/effects/effect_system/effects_foam.dm
@@ -202,8 +202,10 @@
///////////////////////////////////////////////
//FOAM EFFECT DATUM
/datum/effect_system/foam_spread
- var/amount = 10 // the size of the foam spread.
- var/obj/chemholder
+ /// the size of the foam spread.
+ var/amount = 10
+ /// Stupid hack alertm exists to hold chems for us
+ var/atom/movable/chem_holder/chemholder
effect_type = /obj/effect/particle_effect/foam
var/metal = 0
@@ -221,14 +223,11 @@
/datum/effect_system/foam_spread/New()
..()
- chemholder = new /obj()
- var/datum/reagents/R = new/datum/reagents(1000, REAGENT_HOLDER_INSTANT_REACT) //same as above
- chemholder.reagents = R
- R.my_atom = chemholder
+ chemholder = new()
+ chemholder.create_reagents(1000, REAGENT_HOLDER_INSTANT_REACT)
/datum/effect_system/foam_spread/Destroy()
- qdel(chemholder)
- chemholder = null
+ QDEL_NULL(chemholder)
return ..()
/datum/effect_system/foam_spread/set_up(amt=5, loca, datum/reagents/carry = null, metaltype = 0)
diff --git a/code/game/objects/effects/effect_system/effects_smoke.dm b/code/game/objects/effects/effect_system/effects_smoke.dm
index f3f1f01cc71..44502faf915 100644
--- a/code/game/objects/effects/effect_system/effects_smoke.dm
+++ b/code/game/objects/effects/effect_system/effects_smoke.dm
@@ -265,20 +265,18 @@
/datum/effect_system/smoke_spread/chem
- var/obj/chemholder
+ /// Evil evil hack so we have something to "hold" our reagents
+ var/atom/movable/chem_holder/chemholder
effect_type = /obj/effect/particle_effect/smoke/chem
/datum/effect_system/smoke_spread/chem/New()
..()
- chemholder = new /obj()
- var/datum/reagents/R = new (500, REAGENT_HOLDER_INSTANT_REACT) //This is a safety for now to prevent smoke generating more smoke as the smoke reagents react in the smoke. This is prevented naturally from happening even if this is off, but I want to be sure that any edge cases are prevented before I get a chance to rework smoke reactions (specifically adding water or reacting away stabilizing agent in the middle of it).
- chemholder.reagents = R
-
- R.my_atom = chemholder
+ chemholder = new()
+ //This is a safety for now to prevent smoke generating more smoke as the smoke reagents react in the smoke. This is prevented naturally from happening even if this is off, but I want to be sure that any edge cases are prevented before I get a chance to rework smoke reactions (specifically adding water or reacting away stabilizing agent in the middle of it).
+ chemholder.create_reagents(500, REAGENT_HOLDER_INSTANT_REACT)
/datum/effect_system/smoke_spread/chem/Destroy()
- qdel(chemholder)
- chemholder = null
+ QDEL_NULL(chemholder)
return ..()
/datum/effect_system/smoke_spread/chem/set_up(datum/reagents/carry = null, radius = 1, loca, silent = FALSE)
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index bfccdc97d9c..a101cd7b0b7 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -87,6 +87,8 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/e
var/slot_flags = 0
pass_flags = PASSTABLE
pressure_resistance = 4
+ /// This var exists as a weird proxy "owner" ref
+ /// It's used in a few places. Stop using it, and optimially replace all uses please
var/obj/item/master = null
///Price of an item in a vending machine, overriding the base vending machine price. Define in terms of paycheck defines as opposed to raw numbers.
@@ -239,7 +241,9 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/e
updateEmbedding()
/obj/item/Destroy()
- item_flags &= ~DROPDEL //prevent reqdels
+ // This var exists as a weird proxy "owner" ref
+ // It's used in a few places. Stop using it, and optimially replace all uses please
+ master = null
if(ismob(loc))
var/mob/m = loc
m.temporarilyRemoveItemFromInventory(src, TRUE)
@@ -575,7 +579,7 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/e
for(var/X in actions)
var/datum/action/A = X
A.Remove(user)
- if(item_flags & DROPDEL)
+ if(item_flags & DROPDEL && !QDELETED(src))
qdel(src)
item_flags &= ~IN_INVENTORY
SEND_SIGNAL(src, COMSIG_ITEM_DROPPED, user)
@@ -1033,7 +1037,7 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/e
return
/obj/item/proc/unembedded()
- if(item_flags & DROPDEL)
+ if(item_flags & DROPDEL && !QDELETED(src))
qdel(src)
return TRUE
@@ -1051,7 +1055,7 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/e
///In case we want to do something special (like self delete) upon failing to embed in something.
/obj/item/proc/failedEmbed()
- if(item_flags & DROPDEL)
+ if(item_flags & DROPDEL && !QDELETED(src))
qdel(src)
///Called by the carbon throw_item() proc. Returns null if the item negates the throw, or a reference to the thing to suffer the throw else.
diff --git a/code/game/objects/items/devices/PDA/cart.dm b/code/game/objects/items/devices/PDA/cart.dm
index ea293dd4e61..9f0a007f3ca 100644
--- a/code/game/objects/items/devices/PDA/cart.dm
+++ b/code/game/objects/items/devices/PDA/cart.dm
@@ -31,7 +31,8 @@
var/list/stored_data = list()
var/current_channel
- var/mob/living/simple_animal/bot/active_bot
+ /// Weakref to the currently controlled bot
+ var/datum/weakref/active_bot_ref
var/list/botlist = list()
/obj/item/cartridge/engineering
@@ -680,15 +681,16 @@
var/parse = emoji_parse(":[href_list["emoji"]]:")
to_chat(usr, parse)
+ var/mob/living/simple_animal/bot/active_bot = active_bot_ref?.resolve()
+
//Bot control section! Viciously ripped from radios for being laggy and terrible.
if(href_list["op"])
switch(href_list["op"])
if("control")
- active_bot = locate(href_list["bot"]) in GLOB.bots_list
-
+ active_bot_ref = WEAKREF(locate(href_list["bot"]) in GLOB.bots_list)
if("botlist")
- active_bot = null
+ active_bot_ref = null
if("summon") //Args are in the correct order, they are stated here just as an easy reminder.
active_bot.bot_control("summon", usr, host_pda.GetAccess())
else //Forward all other bot commands to the bot itself!
@@ -705,45 +707,8 @@
/obj/item/cartridge/proc/bot_control()
- if(active_bot)
- menu += "[active_bot]
Status: ([PDAIMG(refresh)]refresh)
"
- menu += "Model: [active_bot.bot_type]
"
- menu += "Location: [get_area(active_bot)]
"
- menu += "Mode: [active_bot.get_mode()]"
- if(active_bot.bot_mode_flags & BOT_MODE_PAI_CONTROLLABLE)
- menu += "
pAI: "
- if(active_bot.paicard && active_bot.paicard.pai)
- menu += "[active_bot.paicard.pai.name]"
- if(active_bot.check_access(usr))
- menu += " (eject)"
- else
- menu += "none"
-
- //MULEs!
- if(active_bot.bot_type == MULE_BOT)
- var/mob/living/simple_animal/bot/mulebot/MULE = active_bot
- var/atom/Load = MULE.load
- menu += "
Current Load: [ !Load ? "none" : "[Load.name] (unload)" ]
"
- menu += "Destination: [MULE.destination ? MULE.destination : "None"] (set)
"
- menu += "Set ID: [MULE.suffix] Modify
"
- menu += "Power: [MULE.cell ? MULE.cell.percent() : 0]%
"
- menu += "Home: [!MULE.home_destination ? "none" : MULE.home_destination ]
"
- menu += "Delivery Reporting: [MULE.report_delivery ? "(On)": "(Off)"]
"
- menu += "Auto Return Home: [MULE.auto_return ? "(On)": "(Off)"]
"
- menu += "Auto Pickup Crate: [MULE.auto_pickup ? "(On)": "(Off)"]
" //Hue.
-
- menu += "\[Stop\] "
- menu += "\[Proceed\] "
- menu += "\[Return Home\]
"
-
- else
- menu += "
\[Stop Patrol\] " //patrolon
- menu += "\[Start Patrol\] " //patroloff
- menu += "\[Summon Bot\]
" //summon
- menu += "Keep an ID inserted to upload access codes upon summoning."
-
- menu += "
[PDAIMG(back)]Return to bot list"
- else
+ var/mob/living/simple_animal/bot/active_bot = active_bot_ref?.resolve()
+ if(!active_bot)
menu += "
[PDAIMG(refresh)]Scan for active bots
"
var/turf/current_turf = get_turf(src)
var/zlevel = current_turf.z
@@ -756,8 +721,46 @@
botcount++
if(!botcount) //No bots at all? Lame.
menu += "No bots found.
"
- return
+ return menu
+ menu += "[active_bot]
Status: ([PDAIMG(refresh)]refresh)
"
+ menu += "Model: [active_bot.bot_type]
"
+ menu += "Location: [get_area(active_bot)]
"
+ menu += "Mode: [active_bot.get_mode()]"
+ if(active_bot.bot_mode_flags & BOT_MODE_PAI_CONTROLLABLE)
+ menu += "
pAI: "
+ if(active_bot.paicard && active_bot.paicard.pai)
+ menu += "[active_bot.paicard.pai.name]"
+ if(active_bot.check_access(usr))
+ menu += " (eject)"
+ else
+ menu += "none"
+
+ //MULEs!
+ if(active_bot.bot_type == MULE_BOT)
+ var/mob/living/simple_animal/bot/mulebot/MULE = active_bot
+ var/atom/Load = MULE.load
+ menu += "
Current Load: [ !Load ? "none" : "[Load.name] (unload)" ]
"
+ menu += "Destination: [MULE.destination ? MULE.destination : "None"] (set)
"
+ menu += "Set ID: [MULE.suffix] Modify
"
+ menu += "Power: [MULE.cell ? MULE.cell.percent() : 0]%
"
+ menu += "Home: [!MULE.home_destination ? "none" : MULE.home_destination ]
"
+ menu += "Delivery Reporting: [MULE.report_delivery ? "(On)": "(Off)"]
"
+ menu += "Auto Return Home: [MULE.auto_return ? "(On)": "(Off)"]
"
+ menu += "Auto Pickup Crate: [MULE.auto_pickup ? "(On)": "(Off)"]
" //Hue.
+
+ menu += "\[Stop\] "
+ menu += "\[Proceed\] "
+ menu += "\[Return Home\]
"
+
+ else
+ menu += "
\[Stop Patrol\] " //patrolon
+ menu += "\[Start Patrol\] " //patroloff
+ menu += "\[Summon Bot\]
" //summon
+ menu += "Keep an ID inserted to upload access codes upon summoning."
+
+ menu += "
[PDAIMG(back)]Return to bot list"
+
return menu
//If the cartridge adds a special line to the top of the messaging app
diff --git a/code/game/objects/items/flamethrower.dm b/code/game/objects/items/flamethrower.dm
index 6e3a6118334..4789abc60e1 100644
--- a/code/game/objects/items/flamethrower.dm
+++ b/code/game/objects/items/flamethrower.dm
@@ -37,11 +37,11 @@
/obj/item/flamethrower/Destroy()
if(weldtool)
- qdel(weldtool)
+ QDEL_NULL(weldtool)
if(igniter)
- qdel(igniter)
+ QDEL_NULL(igniter)
if(ptank)
- qdel(ptank)
+ QDEL_NULL(ptank)
return ..()
/obj/item/flamethrower/process()
diff --git a/code/game/objects/structures/construction_console/construction_console.dm b/code/game/objects/structures/construction_console/construction_console.dm
index f9321db279e..62b8bce221c 100644
--- a/code/game/objects/structures/construction_console/construction_console.dm
+++ b/code/game/objects/structures/construction_console/construction_console.dm
@@ -12,7 +12,7 @@
desc = "An industrial computer integrated with a camera-assisted rapid construction drone."
networks = list("ss13")
circuit = /obj/item/circuitboard/computer/base_construction
- off_action = new/datum/action/innate/camera_off/base_construction
+ off_action = /datum/action/innate/camera_off/base_construction
jump_action = null
icon_screen = "mining"
icon_keyboard = "rd_key"
@@ -23,8 +23,6 @@
var/list/structures = list()
///Internal RCD. Some construction actions rely on having this.
var/obj/item/construction/rcd/internal/internal_rcd
- ///Actions given to the console user to help with base building. Actions are generally carried out at the location of the eyeobj
- var/list/datum/action/innate/construction_actions
/obj/machinery/computer/camera_advanced/base_construction/Initialize(mapload)
. = ..()
@@ -38,10 +36,10 @@
* Fill the construction_actios list with actions
*
* Instantiate each action object that we'll be giving to users of
- * this console, and put it in the construction actions list.
+ * this console, and put it in the actions list
*/
/obj/machinery/computer/camera_advanced/base_construction/proc/populate_actions_list()
- construction_actions = list()
+ return
/**
* Reload materials used by the console
@@ -81,11 +79,6 @@
///Go through every action object in the construction_action list (which should be fully initialized by now) and grant it to the user.
/obj/machinery/computer/camera_advanced/base_construction/GrantActions(mob/living/user)
..()
- for (var/datum/action/innate/construction_action in construction_actions)
- if(construction_action)
- construction_action.target = src
- construction_action.Grant(user)
- actions += construction_action
//When the eye is in use, make it visible to players so they know when someone is building.
eyeobj.invisibility = 0
diff --git a/code/game/objects/structures/construction_console/construction_console_aux.dm b/code/game/objects/structures/construction_console/construction_console_aux.dm
index 16179fbb02e..28262d29621 100644
--- a/code/game/objects/structures/construction_console/construction_console_aux.dm
+++ b/code/game/objects/structures/construction_console/construction_console_aux.dm
@@ -15,13 +15,12 @@
structures["turrets"] = 4
/obj/machinery/computer/camera_advanced/base_construction/aux/populate_actions_list()
- construction_actions = list()
- construction_actions.Add(new /datum/action/innate/construction/switch_mode())//Action for switching the RCD's build modes
- construction_actions.Add(new /datum/action/innate/construction/build()) //Action for using the RCD
- construction_actions.Add(new /datum/action/innate/construction/airlock_type()) //Action for setting the airlock type
- construction_actions.Add(new /datum/action/innate/construction/window_type()) //Action for setting the window type
- construction_actions.Add(new /datum/action/innate/construction/place_structure/fan()) //Action for spawning fans
- construction_actions.Add(new /datum/action/innate/construction/place_structure/turret()) //Action for spawning turrets
+ actions += new /datum/action/innate/construction/switch_mode(src) //Action for switching the RCD's build modes
+ actions += new /datum/action/innate/construction/build(src) //Action for using the RCD
+ actions += new /datum/action/innate/construction/airlock_type(src) //Action for setting the airlock type
+ actions += new /datum/action/innate/construction/window_type(src) //Action for setting the window type
+ actions += new /datum/action/innate/construction/place_structure/fan(src) //Action for spawning fans
+ actions += new /datum/action/innate/construction/place_structure/turret(src) //Action for spawning turrets
/obj/machinery/computer/camera_advanced/base_construction/aux/find_spawn_spot()
//Aux base controller. Where the eyeobj will spawn.
diff --git a/code/game/objects/structures/construction_console/construction_console_centcom.dm b/code/game/objects/structures/construction_console/construction_console_centcom.dm
index 8cab9f3379c..e2acfe351dc 100644
--- a/code/game/objects/structures/construction_console/construction_console_centcom.dm
+++ b/code/game/objects/structures/construction_console/construction_console_centcom.dm
@@ -11,6 +11,5 @@
internal_rcd.matter = internal_rcd.max_matter
/obj/machinery/computer/camera_advanced/base_construction/centcom/populate_actions_list()
- construction_actions = list()
- construction_actions.Add(new /datum/action/innate/construction/switch_mode())//Action for switching the RCD's build modes
- construction_actions.Add(new /datum/action/innate/construction/build()) //Action for using the RCD
+ actions += new /datum/action/innate/construction/switch_mode(src) //Action for switching the RCD's build modes
+ actions += new /datum/action/innate/construction/build(src) //Action for using the RCD
diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm
index 824b30e6353..ba6003ff939 100644
--- a/code/game/objects/structures/crates_lockers/crates.dm
+++ b/code/game/objects/structures/crates_lockers/crates.dm
@@ -29,6 +29,10 @@
AddElement(/datum/element/climbable, climb_time = crate_climb_time, climb_stun = 0)
update_appearance()
+/obj/structure/closet/crate/Destroy()
+ QDEL_NULL(manifest)
+ return ..()
+
/obj/structure/closet/crate/update_overlays()
. = ..()
if(broken)
@@ -80,7 +84,7 @@
/obj/structure/closet/crate/open(mob/living/user, force = FALSE)
. = ..()
- if(. && manifest)
+ if(. && !QDELETED(manifest))
to_chat(user, span_notice("The manifest is torn off [src]."))
playsound(src, 'sound/items/poster_ripped.ogg', 75, TRUE)
manifest.forceMove(get_turf(src))
diff --git a/code/game/world.dm b/code/game/world.dm
index 903c361c08c..1949058f821 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -35,6 +35,9 @@ GLOBAL_VAR(restart_counter)
make_datum_references_lists() //initialises global lists for referencing frequently used datums (so that we only ever do it once)
GLOB.config_error_log = GLOB.world_manifest_log = GLOB.world_pda_log = GLOB.world_job_debug_log = GLOB.sql_error_log = GLOB.world_href_log = GLOB.world_runtime_log = GLOB.world_attack_log = GLOB.world_game_log = GLOB.world_econ_log = GLOB.world_shuttle_log = "data/logs/config_error.[GUID()].log" //temporary file used to record errors with loading config, moved to log directory once logging is set bl
+ #ifdef REFERENCE_DOING_IT_LIVE
+ GLOB.harddel_log = GLOB.world_game_log
+ #endif
GLOB.revdata = new
@@ -147,6 +150,10 @@ GLOBAL_VAR(restart_counter)
#ifdef UNIT_TESTS
GLOB.test_log = "[GLOB.log_directory]/tests.log"
start_log(GLOB.test_log)
+#endif
+#ifdef REFERENCE_DOING_IT_LIVE
+ GLOB.harddel_log = "[GLOB.log_directory]/harddels.log"
+ start_log(GLOB.harddel_log)
#endif
start_log(GLOB.world_game_log)
start_log(GLOB.world_attack_log)
diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2.dm b/code/modules/admin/verbs/SDQL2/SDQL_2.dm
index cd631b688f5..88eff5d9406 100644
--- a/code/modules/admin/verbs/SDQL2/SDQL_2.dm
+++ b/code/modules/admin/verbs/SDQL2/SDQL_2.dm
@@ -378,7 +378,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/sdql2_vv_all, new(null
//print the key
if(islist(key))
recursive_list_print(output, key, datum_handler, atom_handler)
- else if(is_proper_datum(key) && (datum_handler || (isatom(key) && atom_handler)))
+ else if(isdatum(key) && (datum_handler || (isatom(key) && atom_handler)))
if(isatom(key) && atom_handler)
output += atom_handler.Invoke(key)
else
@@ -392,7 +392,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/sdql2_vv_all, new(null
var/value = input[key]
if(islist(value))
recursive_list_print(output, value, datum_handler, atom_handler)
- else if(is_proper_datum(value) && (datum_handler || (isatom(value) && atom_handler)))
+ else if(isdatum(value) && (datum_handler || (isatom(value) && atom_handler)))
if(isatom(value) && atom_handler)
output += atom_handler.Invoke(value)
else
@@ -684,7 +684,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/sdql2_vv_all, new(null
switch(query_tree[1])
if("call")
for(var/i in found)
- if(!is_proper_datum(i))
+ if(!isdatum(i))
continue
world.SDQL_var(i, query_tree["call"][1], null, i, superuser, src)
obj_count_finished++
@@ -713,7 +713,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/sdql2_vv_all, new(null
if("set" in query_tree)
var/list/set_list = query_tree["set"]
for(var/d in found)
- if(!is_proper_datum(d))
+ if(!isdatum(d))
continue
SDQL_internal_vv(d, set_list)
obj_count_finished++
@@ -724,7 +724,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/sdql2_vv_all, new(null
state = SDQL2_STATE_SWITCHING
/datum/sdql2_query/proc/SDQL_print(object, list/text_list, print_nulls = TRUE)
- if(is_proper_datum(object))
+ if(isdatum(object))
text_list += "[REF(object)] : [object]"
if(istype(object, /atom))
var/atom/A = object
@@ -1012,7 +1012,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/sdql2_vv_all, new(null
var/static/list/exclude = list("usr", "src", "marked", "global", "MC", "FS", "CFG")
var/long = start < expression.len
var/datum/D
- if(is_proper_datum(object))
+ if(isdatum(object))
D = object
if (object == world && (!long || expression[start + 1] == ".") && !(expression[start] in exclude) && copytext(expression[start], 1, 3) != "SS") //3 == length("SS") + 1
@@ -1201,9 +1201,6 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/sdql2_vv_all, new(null
query_list += word
return query_list
-/proc/is_proper_datum(thing)
- return istype(thing, /datum) || istype(thing, /client)
-
/obj/effect/statclick/SDQL2_delete/Click()
if(!usr.client?.holder)
message_admins("[key_name_admin(usr)] non-holder clicked on a statclick! ([src])")
diff --git a/code/modules/admin/view_variables/reference_tracking.dm b/code/modules/admin/view_variables/reference_tracking.dm
index dcc093c5c90..c9d87718af4 100644
--- a/code/modules/admin/view_variables/reference_tracking.dm
+++ b/code/modules/admin/view_variables/reference_tracking.dm
@@ -48,9 +48,11 @@
log_reftracker("Finished searching datums")
//Warning, attempting to search clients like this will cause crashes if done on live. Watch yourself
+#ifndef REFERENCE_DOING_IT_LIVE
for(var/client/thing) //clients
DoSearchVar(thing, "Clients -> [thing.type]", search_time = starting_time)
log_reftracker("Finished searching clients")
+#endif
log_reftracker("Completed search for references to a [type].")
diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm
index e928b76830d..d7fb789734a 100644
--- a/code/modules/antagonists/_common/antag_datum.dm
+++ b/code/modules/antagonists/_common/antag_datum.dm
@@ -55,11 +55,11 @@ GLOBAL_LIST_EMPTY(antagonists)
///name of the UI that will try to open, right now using a generic ui
var/ui_name = "AntagInfoGeneric"
- ///button to access antag interface
- var/datum/action/antag_info/info_button
+ ///weakref to button to access antag interface
+ var/datum/weakref/info_button_ref
- /// The HUD shown to teammates, created by `add_team_hud`
- var/datum/atom_hud/alternate_appearance/team_hud
+ /// A weakref to the HUD shown to teammates, created by `add_team_hud`
+ var/datum/weakref/team_hud_ref
/datum/antagonist/New()
GLOB.antagonists += src
@@ -69,7 +69,7 @@ GLOBAL_LIST_EMPTY(antagonists)
GLOB.antagonists -= src
if(owner)
LAZYREMOVE(owner.antag_datums, src)
- QDEL_NULL(team_hud)
+ QDEL_NULL(team_hud_ref)
owner = null
return ..()
@@ -94,8 +94,10 @@ GLOBAL_LIST_EMPTY(antagonists)
remove_innate_effects(old_body)
if(!soft_antag && old_body && old_body.stat != DEAD && !LAZYLEN(old_body.mind?.antag_datums))
old_body.remove_from_current_living_antags()
- info_button.Remove(old_body)
- info_button.Grant(new_body)
+ var/datum/action/antag_info/info_button = info_button_ref?.resolve()
+ if(info_button)
+ info_button.Remove(old_body)
+ info_button.Grant(new_body)
apply_innate_effects(new_body)
if(!soft_antag && new_body.stat != DEAD)
new_body.add_to_current_living_antags()
@@ -138,6 +140,7 @@ GLOBAL_LIST_EMPTY(antagonists)
///Called by the add_antag_datum() mind proc after the instanced datum is added to the mind's antag_datums list.
/datum/antagonist/proc/on_gain()
SHOULD_CALL_PARENT(TRUE)
+ var/datum/action/antag_info/info_button
if(!owner)
CRASH("[src] ran on_gain() without a mind")
if(!owner.current)
@@ -145,6 +148,7 @@ GLOBAL_LIST_EMPTY(antagonists)
if(ui_name)//in the future, this should entirely replace greet.
info_button = new(src)
info_button.Grant(owner.current)
+ info_button_ref = WEAKREF(info_button)
if(!silent)
greet()
if(ui_name)
@@ -201,8 +205,8 @@ GLOBAL_LIST_EMPTY(antagonists)
LAZYREMOVE(owner.antag_datums, src)
if(!LAZYLEN(owner.antag_datums) && !soft_antag)
owner.current.remove_from_current_living_antags()
- if(info_button)
- QDEL_NULL(info_button)
+ if(info_button_ref)
+ QDEL_NULL(info_button_ref)
if(!silent && owner.current)
farewell()
UnregisterSignal(owner, COMSIG_PRE_MINDSHIELD_IMPLANT)
@@ -408,14 +412,14 @@ GLOBAL_LIST_EMPTY(antagonists)
/// Adds a HUD that will show you other members with the same antagonist.
/// If an antag typepath is passed to `antag_to_check`, will check that, otherwise will use the source type.
/datum/antagonist/proc/add_team_hud(mob/target, antag_to_check)
- QDEL_NULL(team_hud)
+ QDEL_NULL(team_hud_ref)
- team_hud = target.add_alt_appearance(
+ team_hud_ref = WEAKREF(target.add_alt_appearance(
/datum/atom_hud/alternate_appearance/basic/has_antagonist,
"antag_team_hud_[REF(src)]",
image(hud_icon, target, antag_hud_name),
antag_to_check || type,
- )
+ ))
// Add HUDs that they couldn't see before
for (var/datum/atom_hud/alternate_appearance/basic/has_antagonist/antag_hud as anything in GLOB.has_antagonist_huds)
diff --git a/code/modules/antagonists/abductor/equipment/abduction_gear.dm b/code/modules/antagonists/abductor/equipment/abduction_gear.dm
index 2fab327af31..61c99b2ad88 100644
--- a/code/modules/antagonists/abductor/equipment/abduction_gear.dm
+++ b/code/modules/antagonists/abductor/equipment/abduction_gear.dm
@@ -167,7 +167,7 @@
icon_state = "gizmo_scan"
inhand_icon_state = "silencer"
var/mode = GIZMO_SCAN
- var/mob/living/marked = null
+ var/datum/weakref/marked_target
var/obj/machinery/abductor/console/console
/obj/item/abductor/gizmo/attack_self(mob/user)
@@ -221,6 +221,7 @@
to_chat(user, span_notice("You scan [target] and add [target.p_them()] to the database."))
/obj/item/abductor/gizmo/proc/mark(atom/target, mob/living/user)
+ var/mob/living/marked = marked_target?.resolve()
if(marked == target)
to_chat(user, span_warning("This specimen is already marked!"))
return
@@ -236,12 +237,13 @@
return
to_chat(user, span_notice("You begin preparing [target] for transport..."))
if(do_after(user, 100, target = target))
- marked = target
+ marked_target = WEAKREF(target)
to_chat(user, span_notice("You finish preparing [target] for transport."))
/obj/item/abductor/gizmo/Destroy()
if(console)
console.gizmo = null
+ console = null
. = ..()
diff --git a/code/modules/antagonists/abductor/machinery/camera.dm b/code/modules/antagonists/abductor/machinery/camera.dm
index 252d2dceabc..db7ce55b1ab 100644
--- a/code/modules/antagonists/abductor/machinery/camera.dm
+++ b/code/modules/antagonists/abductor/machinery/camera.dm
@@ -2,13 +2,10 @@
name = "Human Observation Console"
var/team_number = 0
networks = list("ss13", "abductor")
- var/datum/action/innate/teleport_in/tele_in_action = new
- var/datum/action/innate/teleport_out/tele_out_action = new
- var/datum/action/innate/teleport_self/tele_self_action = new
- var/datum/action/innate/vest_mode_swap/vest_mode_action = new
- var/datum/action/innate/vest_disguise_swap/vest_disguise_action = new
- var/datum/action/innate/set_droppoint/set_droppoint_action = new
var/obj/machinery/abductor/console/console
+ /// We can't create our actions until after LateInitialize
+ /// So we instead do it on the first call to GrantActions
+ var/abduct_created = FALSE
lock_override = TRUE
icon = 'icons/obj/abductor.dmi'
@@ -16,6 +13,12 @@
icon_keyboard = null
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
+/obj/machinery/computer/camera_advanced/abductor/Destroy()
+ if(console)
+ console.camera = null
+ console = null
+ return ..()
+
/obj/machinery/computer/camera_advanced/abductor/CreateEye()
..()
eyeobj.visible_icon = TRUE
@@ -24,38 +27,15 @@
eyeobj.invisibility = INVISIBILITY_OBSERVER
/obj/machinery/computer/camera_advanced/abductor/GrantActions(mob/living/carbon/user)
+ if(!abduct_created)
+ actions += new /datum/action/innate/teleport_in(console.pad)
+ actions += new /datum/action/innate/teleport_out(console)
+ actions += new /datum/action/innate/teleport_self(console.pad)
+ actions += new /datum/action/innate/vest_mode_swap(console)
+ actions += new /datum/action/innate/vest_disguise_swap(console)
+ actions += new /datum/action/innate/set_droppoint(console)
..()
- if(tele_in_action)
- tele_in_action.target = console.pad
- tele_in_action.Grant(user)
- actions += tele_in_action
-
- if(tele_out_action)
- tele_out_action.target = console
- tele_out_action.Grant(user)
- actions += tele_out_action
-
- if(tele_self_action)
- tele_self_action.target = console.pad
- tele_self_action.Grant(user)
- actions += tele_self_action
-
- if(vest_mode_action)
- vest_mode_action.target = console
- vest_mode_action.Grant(user)
- actions += vest_mode_action
-
- if(vest_disguise_action)
- vest_disguise_action.target = console
- vest_disguise_action.Grant(user)
- actions += vest_disguise_action
-
- if(set_droppoint_action)
- set_droppoint_action.target = console
- set_droppoint_action.Grant(user)
- actions += set_droppoint_action
-
/obj/machinery/computer/camera_advanced/abductor/proc/IsScientist(mob/living/carbon/human/H)
return HAS_TRAIT(H, TRAIT_ABDUCTOR_SCIENTIST_TRAINING)
diff --git a/code/modules/antagonists/abductor/machinery/console.dm b/code/modules/antagonists/abductor/machinery/console.dm
index 47148565a68..df7e317d53a 100644
--- a/code/modules/antagonists/abductor/machinery/console.dm
+++ b/code/modules/antagonists/abductor/machinery/console.dm
@@ -34,6 +34,21 @@
. = ..()
possible_gear = get_abductor_gear()
+/obj/machinery/abductor/console/Destroy()
+ if(gizmo)
+ gizmo.console = null
+ gizmo = null
+ if(experiment)
+ experiment.console = null
+ experiment = null
+ if(pad)
+ pad.console = null
+ pad = null
+ if(camera)
+ camera.console = null
+ camera = null
+ return ..()
+
/**
* get_abductor_gear: Returns a list of a filtered abductor gear sorted by categories
*/
@@ -95,7 +110,7 @@
data["credits"] = experiment.credits
data["pad"] = pad ? TRUE : FALSE
if(pad)
- data["gizmo"] = gizmo && gizmo.marked ? TRUE : FALSE
+ data["gizmo"] = gizmo && gizmo.marked_target?.resolve() ? TRUE : FALSE
data["vest"] = vest ? TRUE : FALSE
if(vest)
data["vest_mode"] = vest.mode
@@ -143,8 +158,9 @@
return TRUE
/obj/machinery/abductor/console/proc/TeleporterRetrieve()
- if(pad && gizmo?.marked)
- pad.Retrieve(gizmo.marked)
+ var/mob/living/marked = gizmo.marked_target?.resolve()
+ if(pad && marked)
+ pad.Retrieve(marked)
/obj/machinery/abductor/console/proc/TeleporterSend()
if(pad)
@@ -192,6 +208,7 @@
for(var/obj/machinery/abductor/pad/p in GLOB.machines)
if(p.team_number == team_number)
pad = p
+ pad.console = src
break
for(var/obj/machinery/abductor/experiment/e in GLOB.machines)
diff --git a/code/modules/antagonists/abductor/machinery/experiment.dm b/code/modules/antagonists/abductor/machinery/experiment.dm
index f57f2244e83..74028e3522a 100644
--- a/code/modules/antagonists/abductor/machinery/experiment.dm
+++ b/code/modules/antagonists/abductor/machinery/experiment.dm
@@ -15,6 +15,12 @@
var/message_cooldown = 0
var/breakout_time = 450
+/obj/machinery/abductor/experiment/Destroy()
+ if(console)
+ console.experiment = null
+ console = null
+ return ..()
+
/obj/machinery/abductor/experiment/MouseDrop_T(mob/target, mob/user)
if(user.stat != CONSCIOUS || HAS_TRAIT(user, TRAIT_UI_BLOCKED) || !Adjacent(user) || !target.Adjacent(user) || !ishuman(target))
return
diff --git a/code/modules/antagonists/abductor/machinery/pad.dm b/code/modules/antagonists/abductor/machinery/pad.dm
index 5fde3c554c7..b484056591f 100644
--- a/code/modules/antagonists/abductor/machinery/pad.dm
+++ b/code/modules/antagonists/abductor/machinery/pad.dm
@@ -4,6 +4,13 @@
icon = 'icons/obj/abductor.dmi'
icon_state = "alien-pad-idle"
var/turf/teleport_target
+ var/obj/machinery/abductor/console/console
+
+/obj/machinery/abductor/pad/Destroy()
+ if(console)
+ console.pad = null
+ console = null
+ return ..()
/obj/machinery/abductor/pad/proc/Warp(mob/living/target)
if(!target.buckled)
diff --git a/code/modules/antagonists/creep/creep.dm b/code/modules/antagonists/creep/creep.dm
index 5ab4e7d2602..5c52106442b 100644
--- a/code/modules/antagonists/creep/creep.dm
+++ b/code/modules/antagonists/creep/creep.dm
@@ -263,7 +263,7 @@
for(var/obj/I in all_items) //Check for wanted items
if(istype(I, /obj/item/photo))
var/obj/item/photo/P = I
- if(P.picture && (target.current in P.picture.mobs_seen) && !(target.current in P.picture.dead_seen)) //Does the picture exist and is the target in it and is the target not dead
+ if(P.picture && (WEAKREF(target.current) in P.picture.mobs_seen) && !(WEAKREF(target.current) in P.picture.dead_seen)) //Does the picture exist and is the target in it and is the target not dead
return TRUE
return FALSE
diff --git a/code/modules/antagonists/revenant/revenant_abilities.dm b/code/modules/antagonists/revenant/revenant_abilities.dm
index c1c9caadae4..8250450a4d4 100644
--- a/code/modules/antagonists/revenant/revenant_abilities.dm
+++ b/code/modules/antagonists/revenant/revenant_abilities.dm
@@ -12,7 +12,8 @@
return
if(ishuman(A))
- if(A in drained_mobs)
+ //Humans are tagged, so this is fine
+ if(REF(A) in drained_mobs)
to_chat(src, span_revenwarning("[A]'s soul is dead and empty.") )
else if(in_range(src, A))
Harvest(A)
@@ -97,7 +98,7 @@
to_chat(src, span_revennotice("[target]'s soul has been considerably weakened and will yield no more essence for the time being."))
target.visible_message(span_warning("[target] slumps onto the ground."), \
span_revenwarning("Violets lights, dancing in your vision, getting clo--"))
- drained_mobs.Add(target)
+ drained_mobs += REF(target)
target.death(0)
else
to_chat(src, span_revenwarning("[target ? "[target] has":"[target.p_theyve(TRUE)]"] been drawn out of your grasp. The link has been broken."))
diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm
index 0379f389b0b..365072fb2f0 100644
--- a/code/modules/antagonists/traitor/datum_traitor.dm
+++ b/code/modules/antagonists/traitor/datum_traitor.dm
@@ -23,7 +23,7 @@
var/list/traitor_flavor
///reference to the uplink this traitor was given, if they were.
- var/datum/component/uplink/uplink
+ var/datum/weakref/uplink_ref
/// The uplink handler that this traitor belongs to.
var/datum/uplink_handler/uplink_handler
@@ -40,7 +40,8 @@
if(give_uplink)
owner.give_uplink(silent = TRUE, antag_datum = src)
- uplink = owner.find_syndicate_uplink()
+ var/datum/component/uplink/uplink = owner.find_syndicate_uplink()
+ uplink_ref = WEAKREF(uplink)
if(uplink)
if(uplink_handler)
uplink.uplink_handler = uplink_handler
@@ -63,8 +64,6 @@
uplink_items += item
uplink_handler.extra_purchasable += create_uplink_sales(uplink_sale_count, /datum/uplink_category/discounts, -1, uplink_items)
- RegisterSignal(uplink, COMSIG_PARENT_QDELETING, .proc/on_uplink_lost)
-
if(give_objectives)
forge_traitor_objectives()
@@ -78,10 +77,6 @@
return ..()
-/datum/antagonist/traitor/proc/on_uplink_lost(datum/source)
- SIGNAL_HANDLER
- uplink = null
-
/datum/antagonist/traitor/on_removal()
owner.special_role = null
return ..()
@@ -181,6 +176,7 @@
component.delete_if_from_source(src)
/datum/antagonist/traitor/ui_static_data(mob/user)
+ var/datum/component/uplink/uplink = uplink_ref?.resolve()
var/list/data = list()
data["has_codewords"] = should_give_codewords
if(should_give_codewords)
diff --git a/code/modules/assembly/bomb.dm b/code/modules/assembly/bomb.dm
index 4f5d05f2e8d..f2d504d5942 100644
--- a/code/modules/assembly/bomb.dm
+++ b/code/modules/assembly/bomb.dm
@@ -13,6 +13,11 @@
var/obj/item/assembly_holder/bombassembly = null //The first part of the bomb is an assembly holder, holding an igniter+some device
var/obj/item/tank/bombtank = null //the second part of the bomb is a plasma tank
+/obj/item/onetankbomb/Destroy()
+ bombassembly = null
+ bombtank = null
+ return ..()
+
/obj/item/onetankbomb/IsSpecialAssembly()
return TRUE
diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm
index 3982d0564d7..19cfecdabf0 100644
--- a/code/modules/assembly/holder.dm
+++ b/code/modules/assembly/holder.dm
@@ -19,6 +19,18 @@
var/static/rotation_flags = ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_FLIP | ROTATION_VERBS
AddComponent(/datum/component/simple_rotation, rotation_flags)
+/obj/item/assembly_holder/Destroy()
+ QDEL_NULL(a_left)
+ QDEL_NULL(a_right)
+ return ..()
+
+/obj/item/assembly_holder/Exited(atom/movable/gone, direction)
+ . = ..()
+ if(gone == a_left)
+ a_left = null
+ else if(gone == a_right)
+ a_right = null
+
/obj/item/assembly_holder/IsAssemblyHolder()
return TRUE
diff --git a/code/modules/atmospherics/machinery/bluespace_vendor.dm b/code/modules/atmospherics/machinery/bluespace_vendor.dm
index 94ff633a4aa..89f54413d53 100644
--- a/code/modules/atmospherics/machinery/bluespace_vendor.dm
+++ b/code/modules/atmospherics/machinery/bluespace_vendor.dm
@@ -89,6 +89,11 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/bluespace_vendor, 30)
icon_state = "[base_icon]_open"
return ..()
+/obj/machinery/bluespace_vendor/Exited(atom/movable/gone, direction)
+ if(gone == internal_tank)
+ internal_tank = null
+ return ..()
+
/obj/machinery/bluespace_vendor/process()
if(mode == BS_MODE_OPEN)
return
diff --git a/code/modules/atmospherics/machinery/portable/canister.dm b/code/modules/atmospherics/machinery/portable/canister.dm
index dde97728eae..145f1203763 100644
--- a/code/modules/atmospherics/machinery/portable/canister.dm
+++ b/code/modules/atmospherics/machinery/portable/canister.dm
@@ -496,7 +496,7 @@ GLOBAL_LIST_INIT(gas_id_to_canister, init_gas_id_to_canister())
/obj/machinery/portable_atmospherics/canister/take_damage(damage_amount, damage_type = BRUTE, damage_flag = "", sound_effect = TRUE, attack_dir, armour_penetration = 0)
. = ..()
- if(!.)
+ if(!. || QDELETED(src))
return
SSair.start_processing_machine(src)
diff --git a/code/modules/cargo/order.dm b/code/modules/cargo/order.dm
index 1955cc5fd7a..500e43136fa 100644
--- a/code/modules/cargo/order.dm
+++ b/code/modules/cargo/order.dm
@@ -3,8 +3,8 @@
var/order_id = 0
var/errors = 0
-/obj/item/paper/fluff/jobs/cargo/manifest/New(atom/A, id, cost)
- ..()
+/obj/item/paper/fluff/jobs/cargo/manifest/Initialize(mapload, id, cost)
+ . = ..()
order_id = id
order_cost = cost
@@ -65,7 +65,7 @@
return P
/datum/supply_order/proc/generateManifest(obj/container, owner, packname, cost) //generates-the-manifests.
- var/obj/item/paper/fluff/jobs/cargo/manifest/P = new(container, id, cost)
+ var/obj/item/paper/fluff/jobs/cargo/manifest/P = new(null, id, cost)
var/station_name = (P.errors & MANIFEST_ERROR_NAME) ? new_station_name() : station_name()
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index f94e5cc2bdb..44f45680979 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -500,7 +500,8 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
var/announce_join = mob.client?.prefs?.read_preference(/datum/preference/toggle/broadcast_login_logout)
if (!stealth_admin)
deadchat_broadcast(" has disconnected.", "[mob][mob.get_realname_string()]", follow_target = mob, turf_target = get_turf(mob), message_type = DEADCHAT_LOGIN_LOGOUT, admin_only=!announce_join)
-
+ mob.become_uncliented()
+
GLOB.clients -= src
GLOB.directory -= ckey
log_access("Logout: [key_name(src)]")
@@ -533,10 +534,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
send2adminchat("Server", "[cheesy_message] (No admins online)")
QDEL_LIST_ASSOC_VAL(char_render_holders)
- if(movingmob != null)
- LAZYREMOVE(movingmob.client_mobs_in_contents, mob)
- movingmob = null
-
active_mousedown_item = null
SSambience.remove_ambience_client(src)
SSmouse_entered.hovers -= src
diff --git a/code/modules/clothing/suits/toggles.dm b/code/modules/clothing/suits/toggles.dm
index c684738c6a9..8273ec8e7db 100644
--- a/code/modules/clothing/suits/toggles.dm
+++ b/code/modules/clothing/suits/toggles.dm
@@ -16,8 +16,7 @@
/obj/item/clothing/suit/hooded/Destroy()
. = ..()
- qdel(hood)
- hood = null
+ QDEL_NULL(hood)
/obj/item/clothing/suit/hooded/proc/MakeHood()
if(!hood)
diff --git a/code/modules/events/pirates.dm b/code/modules/events/pirates.dm
index e38be714091..d28cbcdac0c 100644
--- a/code/modules/events/pirates.dm
+++ b/code/modules/events/pirates.dm
@@ -263,7 +263,7 @@
/obj/machinery/computer/piratepad_control
name = "cargo hold control terminal"
var/status_report = "Ready for delivery."
- var/obj/machinery/piratepad/pad
+ var/datum/weakref/pad_ref
var/warmup_time = 100
var/sending = FALSE
var/points = 0
@@ -279,7 +279,7 @@
. = ..()
if (istype(I) && istype(I.buffer,/obj/machinery/piratepad))
to_chat(user, span_notice("You link [src] with [I.buffer] in [I] buffer."))
- pad = I.buffer
+ pad_ref = WEAKREF(I.buffer)
return TRUE
/obj/machinery/computer/piratepad_control/LateInitialize()
@@ -287,10 +287,11 @@
if(cargo_hold_id)
for(var/obj/machinery/piratepad/P in GLOB.machines)
if(P.cargo_hold_id == cargo_hold_id)
- pad = P
+ pad_ref = WEAKREF(P)
return
else
- pad = locate() in range(4,src)
+ var/obj/machinery/piratepad/pad = locate() in range(4, src)
+ pad_ref = WEAKREF(pad)
/obj/machinery/computer/piratepad_control/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
@@ -301,7 +302,7 @@
/obj/machinery/computer/piratepad_control/ui_data(mob/user)
var/list/data = list()
data["points"] = points
- data["pad"] = pad ? TRUE : FALSE
+ data["pad"] = pad_ref?.resolve() ? TRUE : FALSE
data["sending"] = sending
data["status_report"] = status_report
return data
@@ -310,7 +311,7 @@
. = ..()
if(.)
return
- if(!pad)
+ if(!pad_ref?.resolve())
return
switch(action)
@@ -331,6 +332,7 @@
status_report = "Predicted value: "
var/value = 0
var/datum/export_report/ex = new
+ var/obj/machinery/piratepad/pad = pad_ref?.resolve()
for(var/atom/movable/AM in get_turf(pad))
if(AM == pad)
continue
@@ -349,6 +351,7 @@
return
var/datum/export_report/ex = new
+ var/obj/machinery/piratepad/pad = pad_ref?.resolve()
for(var/atom/movable/AM in get_turf(pad))
if(AM == pad)
@@ -390,6 +393,7 @@
return
sending = TRUE
status_report = "Sending... "
+ var/obj/machinery/piratepad/pad = pad_ref?.resolve()
pad.visible_message(span_notice("[pad] starts charging up."))
pad.icon_state = pad.warmup_state
sending_timer = addtimer(CALLBACK(src,.proc/send),warmup_time, TIMER_STOPPABLE)
@@ -401,6 +405,7 @@
status_report = "Ready for delivery."
if(custom_report)
status_report = custom_report
+ var/obj/machinery/piratepad/pad = pad_ref?.resolve()
pad.icon_state = pad.idle_state
deltimer(sending_timer)
diff --git a/code/modules/events/spacevine.dm b/code/modules/events/spacevine.dm
index 6de8427c603..0ea50f527b1 100644
--- a/code/modules/events/spacevine.dm
+++ b/code/modules/events/spacevine.dm
@@ -422,8 +422,12 @@
eat(user)
/datum/spacevine_controller
+ ///Canonical list of all the vines we "own"
var/list/obj/structure/spacevine/vines
+ ///Queue of vines to process
var/list/growth_queue
+ //List of currently processed vines, on this level to prevent runtime tomfoolery
+ var/list/obj/structure/spacevine/queue_end
var/spread_multiplier = 5
var/spread_cap = 30
var/list/vine_mutations_list
@@ -432,6 +436,7 @@
/datum/spacevine_controller/New(turf/location, list/muts, potency, production, datum/round_event/event = null)
vines = list()
growth_queue = list()
+ queue_end = list()
var/obj/structure/spacevine/vine = spawn_spacevine_piece(location, null, muts)
if(event)
event.announce_to_ghosts(vine)
@@ -459,6 +464,9 @@
/datum/spacevine_controller/Destroy()
STOP_PROCESSING(SSobj, src)
+ vines.Cut()
+ growth_queue.Cut()
+ queue_end.Cut()
return ..()
/datum/spacevine_controller/proc/spawn_spacevine_piece(turf/location, obj/structure/spacevine/parent, list/muts)
@@ -466,9 +474,8 @@
growth_queue += vine
vines += vine
vine.master = src
- if(length(muts))
- for(var/datum/spacevine_mutation/mutation in muts)
- mutation.add_mutation_to_vinepiece(vine)
+ for(var/datum/spacevine_mutation/mutation in muts)
+ mutation.add_mutation_to_vinepiece(vine)
if(parent)
vine.mutations |= parent.mutations
var/parentcolor = parent.atom_colours[FIXED_COLOUR_PRIORITY]
@@ -486,45 +493,44 @@
vine.master = null
vines -= vine
growth_queue -= vine
- if(!length(vines))
- var/obj/item/seeds/kudzu/seed = new(vine.loc)
- seed.mutations |= vine.mutations
- seed.set_potency(mutativeness * 10)
- seed.set_production(11 - (spread_cap / initial(spread_cap)) * 5) //Reverts spread_cap formula so resulting seed gets original production stat or equivalent back.
- qdel(src)
+ queue_end -= vine
+ if(length(vines))
+ return
+ var/obj/item/seeds/kudzu/seed = new(vine.loc)
+ seed.mutations |= vine.mutations
+ seed.set_potency(mutativeness * 10)
+ seed.set_production(11 - (spread_cap / initial(spread_cap)) * 5) //Reverts spread_cap formula so resulting seed gets original production stat or equivalent back.
+ qdel(src)
/// Life cycle of a space vine
/datum/spacevine_controller/process(delta_time)
- if(!LAZYLEN(vines))
+ var/vine_count = length(vines)
+ if(!vine_count)
qdel(src) //space vines exterminated. Remove the controller
return
- if(!growth_queue)
- qdel(src) //Sanity check
- return
- var/length = round(clamp(delta_time * 0.5 * length(vines) / spread_multiplier, 1, spread_cap))
- var/index = 0
- var/list/obj/structure/spacevine/queue_end = list()
-
- for(var/obj/structure/spacevine/vine in growth_queue)
- if(QDELETED(vine))
- continue
- index++
- queue_end += vine
+ var/spread_max = round(clamp(delta_time * 0.5 * vine_count / spread_multiplier, 1, spread_cap))
+ var/amount_processed = 0
+ for(var/obj/structure/spacevine/vine as anything in growth_queue)
growth_queue -= vine
+ queue_end += vine
for(var/datum/spacevine_mutation/mutation in vine.mutations)
mutation.process_mutation(vine)
- if(vine.energy < 2) //If tile isn't fully grown
- if(DT_PROB(10, delta_time))
- vine.grow()
- else //If tile is fully grown
+
+ if(vine.energy >= 2) //If tile is fully grown
vine.entangle_mob()
+ else if(DT_PROB(10, delta_time)) //If tile isn't fully grown
+ vine.grow()
vine.spread()
- if(index >= length)
+
+ amount_processed++
+ if(amount_processed >= spread_max)
break
- growth_queue = growth_queue + queue_end
+ //We can only do so much work per process, but we still want to process everything at some point
+ //So we shift the queue a bit
+ growth_queue += queue_end
/// Updates the icon as the space vine grows
/obj/structure/spacevine/proc/grow()
diff --git a/code/modules/hydroponics/grown/cotton.dm b/code/modules/hydroponics/grown/cotton.dm
index d25fc565c6b..394b7646b82 100644
--- a/code/modules/hydroponics/grown/cotton.dm
+++ b/code/modules/hydroponics/grown/cotton.dm
@@ -40,9 +40,17 @@
seed_modifier = round(seed.potency / 25)
var/obj/item/stack/cotton = new cotton_type(user.loc, 1 + seed_modifier)
var/old_cotton_amount = cotton.amount
- for(var/obj/item/stack/ST in user.loc)
- if(ST != cotton && istype(ST, cotton_type) && ST.amount < ST.max_amount)
- ST.attackby(cotton, user)
+ for(var/obj/item/stack/potential_stack in user.loc)
+ if(QDELETED(potential_stack))
+ continue
+ if(potential_stack == cotton)
+ continue
+ if(!istype(potential_stack, cotton_type))
+ continue
+ if(potential_stack.amount >= potential_stack.max_amount)
+ continue
+ potential_stack.attackby(cotton, user)
+
if(cotton.amount > old_cotton_amount)
to_chat(user, span_notice("You add the newly-formed [cotton_name] to the stack. It now contains [cotton.amount] [cotton_name]."))
qdel(src)
diff --git a/code/modules/hydroponics/seeds.dm b/code/modules/hydroponics/seeds.dm
index 46ac0c5c6ce..8fe694352c5 100644
--- a/code/modules/hydroponics/seeds.dm
+++ b/code/modules/hydroponics/seeds.dm
@@ -211,7 +211,7 @@
mutated_seed = new mutated_seed
for(var/datum/plant_gene/trait/trait in parent.myseed.genes)
if((trait.mutability_flags & PLANT_GENE_MUTATABLE) && trait.can_add(mutated_seed))
- mutated_seed.genes += trait
+ mutated_seed.genes += trait.Copy()
t_prod = new t_prod(output_loc, mutated_seed)
t_prod.transform = initial(t_prod.transform)
t_prod.transform *= TRANSFORM_USING_VARIABLE(t_prod.seed.potency, 100) + 0.5
diff --git a/code/modules/language/language_manuals.dm b/code/modules/language/language_manuals.dm
index 652ccdf5214..6693b51455c 100644
--- a/code/modules/language/language_manuals.dm
+++ b/code/modules/language/language_manuals.dm
@@ -43,11 +43,10 @@
charges--
if(!charges)
var/turf/T = get_turf(src)
- T.visible_message(span_warning("The cover and contents of [src] start shifting and changing!"))
+ T.visible_message(span_warning("The cover and contents of [src] start shifting and changing! It slips out of your hands!"))
+ new /obj/item/book/manual/random(T)
qdel(src)
- var/obj/item/book/manual/random/book = new(T)
- user.put_in_active_hand(book)
/obj/item/language_manual/codespeak_manual
name = "codespeak manual"
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index 76a6c0e8c2b..e264aad1d24 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -867,10 +867,9 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
observetarget = null
client?.perspective = initial(client.perspective)
sight = initial(sight)
- UnregisterSignal(target, COMSIG_MOVABLE_Z_CHANGED)
- if(target.observers)
- target.observers -= src
- UNSETEMPTY(target.observers)
+ if(target)
+ UnregisterSignal(target, COMSIG_MOVABLE_Z_CHANGED)
+ LAZYREMOVE(target.observers, src)
/mob/dead/observer/verb/observe()
set name = "Observe"
@@ -913,8 +912,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
RegisterSignal(mob_eye, COMSIG_MOVABLE_Z_CHANGED, .proc/on_observing_z_changed)
if(mob_eye.hud_used)
client.screen = list()
- LAZYINITLIST(mob_eye.observers)
- mob_eye.observers |= src
+ LAZYOR(mob_eye.observers, src)
mob_eye.hud_used.show_hud(mob_eye.hud_used.hud_version, src)
observetarget = mob_eye
diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm
index 8f9fa8f0b00..09418a80a3a 100644
--- a/code/modules/mob/living/carbon/life.dm
+++ b/code/modules/mob/living/carbon/life.dm
@@ -398,7 +398,7 @@
dna.temporary_mutations.Remove(mut)
continue
for(var/datum/mutation/human/HM in dna.mutations)
- if(HM?.timed)
+ if(HM?.timeout)
dna.remove_mutation(HM.type)
/*
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 57017adc212..3124452d312 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -40,6 +40,7 @@
remove_from_all_data_huds()
GLOB.mob_living_list -= src
QDEL_LAZYLIST(diseases)
+ QDEL_LIST(surgeries)
return ..()
/mob/living/onZImpact(turf/T, levels, message = TRUE)
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index 7343569a2a9..bad54cf1681 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -44,7 +44,8 @@
radiomod = ";" //AIs will, by default, state their laws on the internal radio.
///Used as a fake multitoool in tcomms machinery
var/obj/item/multitool/aiMulti
- var/mob/living/simple_animal/bot/Bot
+ ///Weakref to the bot the ai's commanding right now
+ var/datum/weakref/bot_ref
var/tracking = FALSE //this is 1 if the AI is currently tracking somebody, but the track has not yet been completed.
var/datum/effect_system/spark_spread/spark_system //So they can initialize sparks whenever
@@ -220,7 +221,7 @@
QDEL_NULL(alert_control)
malfhack = null
current = null
- Bot = null
+ bot_ref = null
controlled_equipment = null
linked_core = null
apc_override = null
@@ -500,16 +501,16 @@
to_chat(src, span_danger("Selected location is not visible."))
/mob/living/silicon/ai/proc/call_bot(turf/waypoint)
-
- if(!Bot)
+ var/mob/living/simple_animal/bot/bot = bot_ref?.resolve()
+ if(!bot)
return
- if(Bot.calling_ai && Bot.calling_ai != src) //Prevents an override if another AI is controlling this bot.
+ if(bot.calling_ai && bot.calling_ai != src) //Prevents an override if another AI is controlling this bot.
to_chat(src, span_danger("Interface error. Unit is already in use."))
return
to_chat(src, span_notice("Sending command to bot..."))
call_bot_cooldown = world.time + CALL_BOT_COOLDOWN
- Bot.call_bot(src, waypoint)
+ bot.call_bot(src, waypoint)
call_bot_cooldown = 0
/mob/living/silicon/ai/proc/alarm_triggered(datum/source, alarm_type, area/source_area)
diff --git a/code/modules/mob/living/silicon/ai/robot_control.dm b/code/modules/mob/living/silicon/ai/robot_control.dm
index f8bcab9cfdd..c60a42d409f 100644
--- a/code/modules/mob/living/silicon/ai/robot_control.dm
+++ b/code/modules/mob/living/silicon/ai/robot_control.dm
@@ -58,20 +58,23 @@
if(!is_interactable(usr))
return
+ var/mob/living/simple_animal/bot/bot
switch(action)
if("callbot") //Command a bot to move to a selected location.
if(owner.call_bot_cooldown > world.time)
to_chat(usr, span_danger("Error: Your last call bot command is still processing, please wait for the bot to finish calculating a route."))
return
- owner.Bot = locate(params["ref"]) in GLOB.bots_list
- if(!owner.Bot || !(owner.Bot.bot_mode_flags & BOT_MODE_REMOTE_ENABLED) || owner.control_disabled)
+ bot = locate(params["ref"]) in GLOB.bots_list
+ owner.bot_ref = WEAKREF(bot)
+ if(!bot || !(bot.bot_mode_flags & BOT_MODE_REMOTE_ENABLED) || owner.control_disabled)
return
owner.waypoint_mode = TRUE
to_chat(usr, span_notice("Set your waypoint by clicking on a valid location free of obstructions."))
. = TRUE
if("interface") //Remotely connect to a bot!
- owner.Bot = locate(params["ref"]) in GLOB.bots_list
- if(!owner.Bot || !(owner.Bot.bot_mode_flags & BOT_MODE_REMOTE_ENABLED) || owner.control_disabled)
+ bot = locate(params["ref"]) in GLOB.bots_list
+ owner.bot_ref = WEAKREF(bot)
+ if(!bot || !(bot.bot_mode_flags & BOT_MODE_REMOTE_ENABLED) || owner.control_disabled)
return
- owner.Bot.attack_ai(usr)
+ bot.attack_ai(usr)
. = TRUE
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 3fc79c95295..5462e589087 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -142,6 +142,7 @@
QDEL_NULL(inv1)
QDEL_NULL(inv2)
QDEL_NULL(inv3)
+ QDEL_NULL(hands)
QDEL_NULL(spark_system)
QDEL_NULL(alert_control)
cell = null
diff --git a/code/modules/mob/living/simple_animal/friendly/robot_customer.dm b/code/modules/mob/living/simple_animal/friendly/robot_customer.dm
index c5ba70ce4a1..64e97980a0b 100644
--- a/code/modules/mob/living/simple_animal/friendly/robot_customer.dm
+++ b/code/modules/mob/living/simple_animal/friendly/robot_customer.dm
@@ -43,7 +43,10 @@
/mob/living/simple_animal/robot_customer/Destroy()
var/datum/venue/attending_venue = ai_controller.blackboard[BB_CUSTOMER_ATTENDING_VENUE]
attending_venue.current_visitors -= src
- attending_venue.linked_seats[ai_controller.blackboard[BB_CUSTOMER_MY_SEAT]] = null
+ var/datum/weakref/seat_ref = ai_controller.blackboard[BB_CUSTOMER_MY_SEAT]
+ var/obj/structure/holosign/robot_seat/our_seat = seat_ref?.resolve()
+ if(attending_venue.linked_seats[our_seat])
+ attending_venue.linked_seats[our_seat] = null
QDEL_NULL(hud_to_show_on_hover)
return ..()
diff --git a/code/modules/mob/living/simple_animal/hostile/bosses/paperwizard.dm b/code/modules/mob/living/simple_animal/hostile/bosses/paperwizard.dm
index 06468f81bb1..5067643bc37 100644
--- a/code/modules/mob/living/simple_animal/hostile/bosses/paperwizard.dm
+++ b/code/modules/mob/living/simple_animal/hostile/bosses/paperwizard.dm
@@ -25,6 +25,9 @@
footstep_type = FOOTSTEP_MOB_SHOE
+/mob/living/simple_animal/hostile/boss/paper_wizard/Destroy()
+ QDEL_LIST(copies)
+ return ..()
//Summon Ability
//Lets the wizard summon his art to fight for him
@@ -126,13 +129,18 @@
loot = list()
var/mob/living/simple_animal/hostile/boss/paper_wizard/original
+/mob/living/simple_animal/hostile/boss/paper_wizard/copy/Destroy()
+ if(original)
+ original.copies -= src
+ original = null
+ return ..()
+
//Hit a fake? eat pain!
/mob/living/simple_animal/hostile/boss/paper_wizard/copy/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
if(amount > 0) //damage
if(original)
original.minimum_distance = 3
original.retreat_distance = 3
- original.copies -= src
for(var/c in original.copies)
qdel(c)
for(var/mob/living/L in range(5,src))
diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm
index 5a941f06e49..463c093cdff 100644
--- a/code/modules/mob/login.dm
+++ b/code/modules/mob/login.dm
@@ -27,6 +27,7 @@
/mob/Login()
if(!client)
return FALSE
+ canon_client = client
add_to_player_list()
lastKnownIP = client.address
computer_id = client.computer_id
@@ -61,8 +62,6 @@
if(!client)
return FALSE
- //We do this here to prevent hanging refs from ghostize or whatever, since if we were in another mob before this'll take care of it
- clear_important_client_contents(client)
enable_client_mobs_in_contents(client)
SEND_SIGNAL(src, COMSIG_MOB_LOGIN)
diff --git a/code/modules/mob/logout.dm b/code/modules/mob/logout.dm
index 25f202e8a14..b8ceb33a37d 100644
--- a/code/modules/mob/logout.dm
+++ b/code/modules/mob/logout.dm
@@ -4,17 +4,11 @@
SStgui.on_logout(src)
unset_machine()
remove_from_player_list()
- clear_client_in_contents()
..()
if(loc)
loc.on_log(FALSE)
- if(client)
- for(var/foo in client.player_details.post_logout_callbacks)
- var/datum/callback/CB = foo
- CB.Invoke()
-
- clear_important_client_contents(client)
+ become_uncliented()
return TRUE
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 6429386f2fc..80be9d43a6e 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -1032,7 +1032,10 @@
if(oldname == newname)
log_message("[src] failed name change as the new name was the same as the old one: [oldname]", LOG_OWNERSHIP)
return FALSE
-
+ if(!istext(newname) && !isnull(newname))
+ stack_trace("[src] attempted to change its name from [oldname] to the non string value [newname]")
+ return FALSE
+
log_message("[src] name changed from [oldname] to [newname]", LOG_OWNERSHIP)
log_played_names(ckey, newname)
@@ -1317,11 +1320,22 @@
SIGNAL_HANDLER
set_active_storage(null)
-///clears the client mob in our client_mobs_in_contents list
-/mob/proc/clear_client_in_contents()
- if(client?.movingmob)
- LAZYREMOVE(client.movingmob.client_mobs_in_contents, src)
- client.movingmob = null
+/// Cleanup proc that's called when a mob loses a client, either through client destroy or logout
+/// Logout happens post client del, so we can't just copypaste this there. This keeps things clean and consistent
+/mob/proc/become_uncliented()
+ if(!canon_client)
+ return
+
+ for(var/foo in canon_client.player_details.post_logout_callbacks)
+ var/datum/callback/CB = foo
+ CB.Invoke()
+
+ if(canon_client?.movingmob)
+ LAZYREMOVE(canon_client.movingmob.client_mobs_in_contents, src)
+ canon_client.movingmob = null
+
+ clear_important_client_contents()
+ canon_client = null
///Shows a tgui window with memories
/mob/verb/memory()
diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm
index 5e2fbec462b..6f51015fa5c 100644
--- a/code/modules/mob/mob_defines.dm
+++ b/code/modules/mob/mob_defines.dm
@@ -17,6 +17,13 @@
throwforce = 10
blocks_emissive = EMISSIVE_BLOCK_GENERIC
pass_flags_self = PASSMOB
+ /// The current client inhabiting this mob. Managed by login/logout
+ /// This exists so we can do cleanup in logout for occasions where a client was transfere rather then destroyed
+ /// We need to do this because the mob on logout never actually has a reference to client
+ /// We also need to clear this var/do other cleanup in client/Destroy, since that happens before logout
+ /// HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
+ var/client/canon_client
+
var/shift_to_open_context_menu = TRUE
///when this be added to vis_contents of something it inherit something.plane, important for visualisation of mob in openspace.
diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm
index b9e21d6428a..63829177fd1 100644
--- a/code/modules/modular_computers/computers/item/computer.dm
+++ b/code/modules/modular_computers/computers/item/computer.dm
@@ -65,6 +65,9 @@
/obj/item/modular_computer/Destroy()
wipe_program(forced = TRUE)
+ for(var/datum/computer_file/program/idle as anything in idle_threads)
+ idle.kill_program(TRUE)
+ idle_threads.Cut()
STOP_PROCESSING(SSobj, src)
for(var/port in all_components)
var/obj/item/computer_hardware/component = all_components[port]
diff --git a/code/modules/modular_computers/file_system/programs/configurator.dm b/code/modules/modular_computers/file_system/programs/configurator.dm
index cf5d950c0f6..459ddc1b791 100644
--- a/code/modules/modular_computers/file_system/programs/configurator.dm
+++ b/code/modules/modular_computers/file_system/programs/configurator.dm
@@ -15,25 +15,19 @@
tgui_id = "NtosConfiguration"
program_icon = "cog"
- var/obj/item/modular_computer/movable = null
-
-
/datum/computer_file/program/computerconfig/ui_data(mob/user)
- movable = computer
- var/obj/item/computer_hardware/hard_drive/hard_drive = movable.all_components[MC_HDD]
- var/obj/item/computer_hardware/battery/battery_module = movable.all_components[MC_CELL]
- if(!istype(movable))
- movable = null
-
// No computer connection, we can't get data from that.
- if(!movable)
+ if(!computer)
return 0
+ var/obj/item/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD]
+ var/obj/item/computer_hardware/battery/battery_module = computer.all_components[MC_CELL]
+
var/list/data = get_header_data()
data["disk_size"] = hard_drive.max_capacity
data["disk_used"] = hard_drive.used_capacity
- data["power_usage"] = movable.last_power_usage
+ data["power_usage"] = computer.last_power_usage
data["battery_exists"] = battery_module ? 1 : 0
if(battery_module?.battery)
data["battery_rating"] = battery_module.battery.maxcharge
@@ -43,8 +37,8 @@
data["battery"] = list("max" = battery_module.battery.maxcharge, "charge" = round(battery_module.battery.charge))
var/list/all_entries[0]
- for(var/I in movable.all_components)
- var/obj/item/computer_hardware/H = movable.all_components[I]
+ for(var/I in computer.all_components)
+ var/obj/item/computer_hardware/H = computer.all_components[I]
all_entries.Add(list(list(
"name" = H.name,
"desc" = H.desc,
@@ -63,7 +57,7 @@
return
switch(action)
if("PC_toggle_component")
- var/obj/item/computer_hardware/H = movable.find_hardware_by_name(params["name"])
+ var/obj/item/computer_hardware/H = computer.find_hardware_by_name(params["name"])
if(H && istype(H))
H.enabled = !H.enabled
. = TRUE
diff --git a/code/modules/modular_computers/file_system/programs/ntdownloader.dm b/code/modules/modular_computers/file_system/programs/ntdownloader.dm
index 56283e3c8be..15dd3278031 100644
--- a/code/modules/modular_computers/file_system/programs/ntdownloader.dm
+++ b/code/modules/modular_computers/file_system/programs/ntdownloader.dm
@@ -18,7 +18,6 @@
var/download_completion = FALSE //GQ of downloaded data.
var/download_netspeed = 0
var/downloaderror = ""
- var/obj/item/modular_computer/my_computer = null
var/emagged = FALSE
var/list/main_repo
var/list/antag_repo
@@ -130,9 +129,7 @@
return FALSE
/datum/computer_file/program/ntnetdownload/ui_data(mob/user)
- my_computer = computer
-
- if(!istype(my_computer))
+ if(!istype(computer))
return
var/obj/item/computer_hardware/card_slot/card_slot = computer.all_components[MC_CARD]
var/list/access = card_slot?.GetAccess()
@@ -150,7 +147,7 @@
data["downloadspeed"] = download_netspeed
data["downloadcompletion"] = round(download_completion, 0.1)
- var/obj/item/computer_hardware/hard_drive/hard_drive = my_computer.all_components[MC_HDD]
+ var/obj/item/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD]
data["disk_size"] = hard_drive.max_capacity
data["disk_used"] = hard_drive.used_capacity
data["emagged"] = emagged
diff --git a/code/modules/modular_computers/file_system/programs/powermonitor.dm b/code/modules/modular_computers/file_system/programs/powermonitor.dm
index e5f350f8609..810499985e8 100644
--- a/code/modules/modular_computers/file_system/programs/powermonitor.dm
+++ b/code/modules/modular_computers/file_system/programs/powermonitor.dm
@@ -15,8 +15,8 @@
program_icon = "plug"
var/has_alert = 0
- var/obj/structure/cable/attached_wire
- var/obj/machinery/power/apc/local_apc
+ var/datum/weakref/attached_wire_ref
+ var/datum/weakref/local_apc_ref
var/list/history = list()
var/record_size = 60
var/record_interval = 50
@@ -38,19 +38,22 @@
/datum/computer_file/program/power_monitor/proc/search() //keep in sync with /obj/machinery/computer/monitor's version
var/turf/T = get_turf(computer)
- attached_wire = locate(/obj/structure/cable) in T
- if(attached_wire)
+ attached_wire_ref = WEAKREF(locate(/obj/structure/cable) in T)
+ if(attached_wire_ref)
return
var/area/A = get_area(computer) //if the computer isn't directly connected to a wire, attempt to find the APC powering it to pull it's powernet instead
if(!A)
return
- local_apc = A.apc
+ var/obj/machinery/power/apc/local_apc = WEAKREF(A.apc)
if(!local_apc)
return
if(!local_apc.terminal) //this really shouldn't happen without badminnery.
local_apc = null
+ local_apc_ref = WEAKREF(local_apc)
/datum/computer_file/program/power_monitor/proc/get_powernet() //keep in sync with /obj/machinery/computer/monitor's version
+ var/obj/structure/cable/attached_wire = attached_wire_ref?.resolve()
+ var/obj/machinery/power/apc/local_apc = local_apc_ref?.resolve()
if(attached_wire || (local_apc?.terminal))
return attached_wire ? attached_wire.powernet : local_apc.terminal.powernet
return FALSE
diff --git a/code/modules/modular_computers/file_system/programs/robocontrol.dm b/code/modules/modular_computers/file_system/programs/robocontrol.dm
index 1c77ff4cc1d..5d2ec5d422f 100644
--- a/code/modules/modular_computers/file_system/programs/robocontrol.dm
+++ b/code/modules/modular_computers/file_system/programs/robocontrol.dm
@@ -12,8 +12,6 @@
program_icon = "robot"
///Number of simple robots on-station.
var/botcount = 0
- ///Used to find the location of the user for the purposes of summoning robots.
- var/mob/current_user
///Access granted by the used to summon robots.
var/list/current_access = list()
@@ -33,12 +31,11 @@
data["access_on_card"] = id_card ? id_card.access : null
botcount = 0
- current_user = user
for(var/mob/living/simple_animal/bot/simple_bot as anything in GLOB.bots_list)
if(simple_bot.z != zlevel || !(simple_bot.bot_mode_flags & BOT_MODE_REMOTE_ENABLED)) //Only non-emagged bots on the same Z-level are detected!
continue
- if(computer && !simple_bot.check_access(current_user)) // Only check Bots we can access)
+ if(computer && !simple_bot.check_access(user)) // Only check Bots we can access)
continue
var/list/newbot = list(
"name" = simple_bot.name,
@@ -71,10 +68,11 @@
return data
-/datum/computer_file/program/robocontrol/ui_act(action, list/params)
+/datum/computer_file/program/robocontrol/ui_act(action, list/params, datum/tgui/ui)
. = ..()
if(.)
return
+ var/mob/current_user = ui.user
var/obj/item/computer_hardware/card_slot/card_slot
var/obj/item/card/id/id_card
if(computer)
diff --git a/code/modules/modular_computers/file_system/programs/robotact.dm b/code/modules/modular_computers/file_system/programs/robotact.dm
index 43753345af6..339aa01649b 100644
--- a/code/modules/modular_computers/file_system/programs/robotact.dm
+++ b/code/modules/modular_computers/file_system/programs/robotact.dm
@@ -14,12 +14,6 @@
size = 5
tgui_id = "NtosRobotact"
program_icon = "terminal"
- ///A typed reference to the computer, specifying the borg tablet type
- var/obj/item/modular_computer/tablet/integrated/tablet
-
-/datum/computer_file/program/robotact/Destroy()
- tablet = null
- return ..()
/datum/computer_file/program/robotact/run_program(mob/living/user)
if(!istype(computer, /obj/item/modular_computer/tablet/integrated))
@@ -27,7 +21,7 @@
return FALSE
. = ..()
if(.)
- tablet = computer
+ var/obj/item/modular_computer/tablet/integrated/tablet = computer
if(tablet.device_theme == "syndicate")
program_icon_state = "command-syndicate"
return TRUE
@@ -37,6 +31,10 @@
var/list/data = get_header_data()
if(!iscyborg(user))
return data
+
+ //Implied, since we can't run on non tablets
+ var/obj/item/modular_computer/tablet/integrated/tablet = computer
+
var/mob/living/silicon/robot/borgo = tablet.borgo
data["name"] = borgo.name
@@ -80,6 +78,8 @@
if(!iscyborg(user))
return data
var/mob/living/silicon/robot/borgo = user
+ //Implied
+ var/obj/item/modular_computer/tablet/integrated/tablet = computer
data["Laws"] = borgo.laws.get_law_list(TRUE, TRUE, FALSE)
data["borgLog"] = tablet.borglog
@@ -90,7 +90,8 @@
. = ..()
if(.)
return
-
+ //Implied type, memes
+ var/obj/item/modular_computer/tablet/integrated/tablet = computer
var/mob/living/silicon/robot/borgo = tablet.borgo
switch(action)
@@ -149,7 +150,9 @@
* law changes and borg log additions.
*/
/datum/computer_file/program/robotact/proc/force_full_update()
- if(tablet)
- var/datum/tgui/active_ui = SStgui.get_open_ui(tablet.borgo, src)
- if(active_ui)
- active_ui.send_full_update()
+ if(!istype(computer, /obj/item/modular_computer/tablet/integrated))
+ return
+ var/obj/item/modular_computer/tablet/integrated/tablet = computer
+ var/datum/tgui/active_ui = SStgui.get_open_ui(tablet.borgo, src)
+ if(active_ui)
+ active_ui.send_full_update()
diff --git a/code/modules/modular_computers/file_system/programs/secureye.dm b/code/modules/modular_computers/file_system/programs/secureye.dm
index 21d33498eab..717d30ff0b4 100644
--- a/code/modules/modular_computers/file_system/programs/secureye.dm
+++ b/code/modules/modular_computers/file_system/programs/secureye.dm
@@ -15,7 +15,8 @@
program_icon = "eye"
var/list/network = list("ss13")
- var/obj/machinery/camera/active_camera
+ /// Weakref to the active camera
+ var/datum/weakref/camera_ref
/// The turf where the camera was last updated.
var/turf/last_camera_turf
var/list/concurrent_users = list()
@@ -92,6 +93,7 @@
var/list/data = get_header_data()
data["network"] = network
data["activeCamera"] = null
+ var/obj/machinery/camera/active_camera = camera_ref?.resolve()
if(active_camera)
data["activeCamera"] = list(
name = active_camera.c_tag,
@@ -121,7 +123,7 @@
var/c_tag = params["name"]
var/list/cameras = get_available_cameras()
var/obj/machinery/camera/selected_camera = cameras[c_tag]
- active_camera = selected_camera
+ camera_ref = WEAKREF(selected_camera)
playsound(src, get_sfx("terminal_type"), 25, FALSE)
if(!selected_camera)
@@ -141,10 +143,11 @@
user.client.clear_map(map_name)
// Turn off the console
if(length(concurrent_users) == 0 && is_living)
- active_camera = null
+ camera_ref = null
playsound(src, 'sound/machines/terminal_off.ogg', 25, FALSE)
/datum/computer_file/program/secureye/proc/update_active_camera_screen()
+ var/obj/machinery/camera/active_camera = camera_ref?.resolve()
// Show static if can't use the camera
if(!active_camera?.can_use())
show_camera_static()
diff --git a/code/modules/modular_computers/hardware/hard_drive.dm b/code/modules/modular_computers/hardware/hard_drive.dm
index 15959bb8f51..4dc40ef4466 100644
--- a/code/modules/modular_computers/hardware/hard_drive.dm
+++ b/code/modules/modular_computers/hardware/hard_drive.dm
@@ -12,6 +12,8 @@
/obj/item/computer_hardware/hard_drive/on_remove(obj/item/modular_computer/remove_from, mob/user)
remove_from.shutdown_computer()
+ for(var/datum/computer_file/program/program in stored_files)
+ program.computer = null
/obj/item/computer_hardware/hard_drive/proc/install_default_programs()
store_file(new/datum/computer_file/program/computerconfig(src)) // Computer configuration utility, allows hardware control and displays more info than status bar
diff --git a/code/modules/paperwork/ticketmachine.dm b/code/modules/paperwork/ticketmachine.dm
index 746f836dff9..36dec11944e 100644
--- a/code/modules/paperwork/ticketmachine.dm
+++ b/code/modules/paperwork/ticketmachine.dm
@@ -22,6 +22,16 @@
var/list/ticket_holders = list()
var/list/obj/item/ticket_machine_ticket/tickets = list()
+/obj/machinery/ticket_machine/Initialize(mapload)
+ . = ..()
+ update_appearance()
+
+/obj/machinery/ticket_machine/Destroy()
+ for(var/obj/item/ticket_machine_ticket/ticket in tickets)
+ ticket.source = null
+ tickets.Cut()
+ return ..()
+
MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/ticket_machine, 32)
/obj/machinery/ticket_machine/multitool_act(mob/living/user, obj/item/I)
@@ -46,10 +56,6 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/ticket_machine, 32)
tickets.Cut()
update_appearance()
-/obj/machinery/ticket_machine/Initialize(mapload)
- . = ..()
- update_appearance()
-
/obj/machinery/ticket_machine/proc/increment()
if(current_number > ticket_number)
return
@@ -178,21 +184,21 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/ticket_machine, 32)
if(ticket_number >= max_number)
to_chat(user,span_warning("Ticket supply depleted, please refill this unit with a hand labeller refill cartridge!"))
return
- if((user in ticket_holders) && !(obj_flags & EMAGGED))
+ var/user_ref = REF(user)
+ if((user_ref in ticket_holders) && !(obj_flags & EMAGGED))
to_chat(user, span_warning("You already have a ticket!"))
return
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 100, FALSE)
- ticket_number ++
+ ticket_number++
to_chat(user, span_notice("You take a ticket from [src], looks like you're ticket number #[ticket_number]..."))
var/obj/item/ticket_machine_ticket/theirticket = new /obj/item/ticket_machine_ticket(get_turf(src))
theirticket.name = "Ticket #[ticket_number]"
theirticket.maptext = MAPTEXT(ticket_number)
theirticket.saved_maptext = MAPTEXT(ticket_number)
- theirticket.ticket_number = ticket_number
theirticket.source = src
- theirticket.owner = user
+ theirticket.owner_ref = user_ref
user.put_in_hands(theirticket)
- ticket_holders += user
+ ticket_holders += user_ref
tickets += theirticket
if(obj_flags & EMAGGED) //Emag the machine to destroy the HOP's life.
ready = FALSE
@@ -214,9 +220,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/ticket_machine, 32)
resistance_flags = FLAMMABLE
max_integrity = 50
var/saved_maptext = null
- var/mob/living/carbon/owner
+ var/owner_ref // A ref to our owner. Doesn't need to be weak because mobs have unique refs
var/obj/machinery/ticket_machine/source
- var/ticket_number
/obj/item/ticket_machine_ticket/attack_hand(mob/user, list/modifiers)
. = ..()
@@ -233,9 +238,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/ticket_machine, 32)
update_appearance()
/obj/item/ticket_machine_ticket/Destroy()
- if(owner && source)
- source.ticket_holders -= owner
- source.tickets[ticket_number] = null
- owner = null
+ if(source)
+ source.ticket_holders -= owner_ref
+ source.tickets -= src
source = null
return ..()
diff --git a/code/modules/photography/_pictures.dm b/code/modules/photography/_pictures.dm
index fd5630206d6..9499712b07e 100644
--- a/code/modules/photography/_pictures.dm
+++ b/code/modules/photography/_pictures.dm
@@ -1,7 +1,9 @@
/datum/picture
var/picture_name = "picture"
var/picture_desc = "This is a picture."
+ /// List of weakrefs pointing at mobs that appear in this photo
var/list/mobs_seen = list()
+ /// List of weakrefs pointing at dead mobs that appear in this photo
var/list/dead_seen = list()
var/caption
var/icon/picture_image
@@ -20,9 +22,11 @@
if(!isnull(desc))
picture_desc = desc
if(!isnull(mobs_spotted))
- mobs_seen = mobs_spotted
+ for(var/mob/seen as anything in mobs_spotted)
+ mobs_seen += WEAKREF(seen)
if(!isnull(dead_spotted))
- dead_seen = dead_spotted
+ for(var/mob/seen as anything in dead_spotted)
+ dead_seen += WEAKREF(seen)
if(!isnull(image))
picture_image = image
if(!isnull(icon))
diff --git a/code/modules/photography/photos/photo.dm b/code/modules/photography/photos/photo.dm
index 277d380a63f..5448be3360b 100644
--- a/code/modules/photography/photos/photo.dm
+++ b/code/modules/photography/photos/photo.dm
@@ -35,10 +35,15 @@
if(!P.see_ghosts) ///Dont bother with this last bit if we can't see ghosts
return
- for(var/i in P.mobs_seen) //Any ghosts in the pic? its a haunted photo ooooo~
- if(isobserver(i))
- set_custom_materials(list(/datum/material/hauntium = 2000))
- break
+ for(var/datum/weakref/seen_ref in P.mobs_seen) //Any ghosts in the pic? its a haunted photo ooooo~
+ var/mob/seen = seen_ref.resolve()
+ if(!seen)
+ P.mobs_seen -= seen_ref
+ continue
+ if(!isobserver(seen))
+ continue
+ set_custom_materials(list(/datum/material/hauntium = 2000))
+ break
/obj/item/photo/update_icon_state()
if(!istype(picture) || !picture.picture_image)
diff --git a/code/modules/power/lighting/light_construct.dm b/code/modules/power/lighting/light_construct.dm
index 6a3297176da..2b65c75c87d 100644
--- a/code/modules/power/lighting/light_construct.dm
+++ b/code/modules/power/lighting/light_construct.dm
@@ -142,7 +142,7 @@
new_light = new /obj/machinery/light/small/built(loc)
new_light.setDir(dir)
transfer_fingerprints_to(new_light)
- if(cell)
+ if(!QDELETED(cell))
new_light.cell = cell
cell.forceMove(new_light)
cell = null
diff --git a/code/modules/projectiles/guns/special/medbeam.dm b/code/modules/projectiles/guns/special/medbeam.dm
index f7773b25e42..26a41fddee0 100644
--- a/code/modules/projectiles/guns/special/medbeam.dm
+++ b/code/modules/projectiles/guns/special/medbeam.dm
@@ -38,8 +38,7 @@
*/
/obj/item/gun/medbeam/proc/LoseTarget()
if(active)
- qdel(current_beam)
- current_beam = null
+ QDEL_NULL(current_beam)
active = FALSE
on_beam_release(current_target)
current_target = null
@@ -51,6 +50,7 @@
*/
/obj/item/gun/medbeam/proc/beam_died()
SIGNAL_HANDLER
+ current_beam = null
active = FALSE //skip qdelling the beam again if we're doing this proc, because
if(isliving(loc))
to_chat(loc, span_warning("You lose control of the beam!"))
@@ -87,7 +87,7 @@
last_check = world.time
if(!los_check(loc, current_target))
- qdel(current_beam)//this will give the target lost message
+ QDEL_NULL(current_beam)//this will give the target lost message
return
if(current_target)
diff --git a/code/modules/reagents/chemistry/holder.dm b/code/modules/reagents/chemistry/holder.dm
index 41b629b6203..b387ca9fd55 100644
--- a/code/modules/reagents/chemistry/holder.dm
+++ b/code/modules/reagents/chemistry/holder.dm
@@ -1948,6 +1948,10 @@
reagents = new /datum/reagents(max_vol, flags)
reagents.my_atom = src
+/atom/movable/chem_holder
+ name = "This atom exists to hold chems. If you can see this, make an issue report"
+ desc = "God this is stupid"
+
#undef REAGENT_TRANSFER_AMOUNT
#undef REAGENT_PURITY
diff --git a/code/modules/research/experimentor.dm b/code/modules/research/experimentor.dm
index 0b272639e01..932542c90d8 100644
--- a/code/modules/research/experimentor.dm
+++ b/code/modules/research/experimentor.dm
@@ -27,8 +27,10 @@
use_power = IDLE_POWER_USE
circuit = /obj/item/circuitboard/machine/experimentor
var/recentlyExperimented = 0
- var/mob/trackedIan
- var/mob/trackedRuntime
+ /// Weakref to the first ian we can find at init
+ var/datum/weakref/tracked_ian_ref
+ /// Weakref to the first runtime we can find at init
+ var/datum/weakref/tracked_runtime_ref
var/badThingCoeff = 0
var/resetTime = 15
var/cloneMode = FALSE
@@ -72,8 +74,8 @@
/obj/machinery/rnd/experimentor/Initialize(mapload)
. = ..()
- trackedIan = locate(/mob/living/simple_animal/pet/dog/corgi/ian) in GLOB.mob_living_list
- trackedRuntime = locate(/mob/living/simple_animal/pet/cat/runtime) in GLOB.mob_living_list
+ tracked_ian_ref = WEAKREF(locate(/mob/living/simple_animal/pet/dog/corgi/ian) in GLOB.mob_living_list)
+ tracked_runtime_ref = WEAKREF(locate(/mob/living/simple_animal/pet/cat/runtime) in GLOB.mob_living_list)
SetTypeReactions()
critical_items_typecache = typecacheof(list(
@@ -477,9 +479,10 @@
if(globalMalf > 16 && globalMalf < 35)
visible_message(span_warning("[src] melts [exp_on], ian-izing the air around it!"))
throwSmoke(loc)
- if(trackedIan)
- throwSmoke(trackedIan.loc)
- trackedIan.forceMove(loc)
+ var/mob/living/tracked_ian = tracked_ian_ref?.resolve()
+ if(tracked_ian)
+ throwSmoke(tracked_ian.loc)
+ tracked_ian.forceMove(loc)
investigate_log("Experimentor has stolen Ian!", INVESTIGATE_EXPERIMENTOR) //...if anyone ever fixes it...
else
new /mob/living/simple_animal/pet/dog/corgi(loc)
@@ -494,9 +497,10 @@
if(globalMalf > 51 && globalMalf < 75)
visible_message(span_warning("[src] encounters a run-time error!"))
throwSmoke(loc)
- if(trackedRuntime)
- throwSmoke(trackedRuntime.loc)
- trackedRuntime.forceMove(drop_location())
+ var/mob/living/tracked_runtime = tracked_runtime_ref?.resolve()
+ if(tracked_runtime)
+ throwSmoke(tracked_runtime.loc)
+ tracked_runtime.forceMove(drop_location())
investigate_log("Experimentor has stolen Runtime!", INVESTIGATE_EXPERIMENTOR)
else
new /mob/living/simple_animal/pet/cat(loc)
diff --git a/code/modules/research/xenobiology/xenobio_camera.dm b/code/modules/research/xenobiology/xenobio_camera.dm
index 31dc8051818..d8329e537f1 100644
--- a/code/modules/research/xenobiology/xenobio_camera.dm
+++ b/code/modules/research/xenobiology/xenobio_camera.dm
@@ -30,13 +30,6 @@
desc = "A computer used for remotely handling slimes."
networks = list("ss13")
circuit = /obj/item/circuitboard/computer/xenobiology
- var/datum/action/innate/slime_place/slime_place_action
- var/datum/action/innate/slime_pick_up/slime_up_action
- var/datum/action/innate/feed_slime/feed_slime_action
- var/datum/action/innate/monkey_recycle/monkey_recycle_action
- var/datum/action/innate/slime_scan/scan_action
- var/datum/action/innate/feed_potion/potion_action
- var/datum/action/innate/hotkey_help/hotkey_help
var/obj/machinery/monkey_recycler/connected_recycler
var/list/stored_slimes
@@ -51,13 +44,14 @@
/obj/machinery/computer/camera_advanced/xenobio/Initialize(mapload)
. = ..()
- slime_place_action = new
- slime_up_action = new
- feed_slime_action = new
- monkey_recycle_action = new
- scan_action = new
- potion_action = new
- hotkey_help = new
+ actions += new /datum/action/innate/slime_place(src)
+ actions += new /datum/action/innate/slime_pick_up(src)
+ actions += new /datum/action/innate/feed_slime(src)
+ actions += new /datum/action/innate/monkey_recycle(src)
+ actions += new /datum/action/innate/slime_scan(src)
+ actions += new /datum/action/innate/feed_potion(src)
+ actions += new /datum/action/innate/hotkey_help(src)
+
stored_slimes = list()
for(var/obj/machinery/monkey_recycler/recycler in GLOB.monkey_recyclers)
if(get_area(recycler.loc) == get_area(loc))
@@ -91,42 +85,6 @@
/obj/machinery/computer/camera_advanced/xenobio/GrantActions(mob/living/user)
..()
-
- if(slime_up_action)
- slime_up_action.target = src
- slime_up_action.Grant(user)
- actions += slime_up_action
-
- if(slime_place_action)
- slime_place_action.target = src
- slime_place_action.Grant(user)
- actions += slime_place_action
-
- if(feed_slime_action)
- feed_slime_action.target = src
- feed_slime_action.Grant(user)
- actions += feed_slime_action
-
- if(monkey_recycle_action)
- monkey_recycle_action.target = src
- monkey_recycle_action.Grant(user)
- actions += monkey_recycle_action
-
- if(scan_action)
- scan_action.target = src
- scan_action.Grant(user)
- actions += scan_action
-
- if(potion_action)
- potion_action.target = src
- potion_action.Grant(user)
- actions += potion_action
-
- if(hotkey_help)
- hotkey_help.target = src
- hotkey_help.Grant(user)
- actions += hotkey_help
-
RegisterSignal(user, COMSIG_XENO_SLIME_CLICK_CTRL, .proc/XenoSlimeClickCtrl)
RegisterSignal(user, COMSIG_XENO_TURF_CLICK_CTRL, .proc/XenoTurfClickCtrl)
RegisterSignal(user, COMSIG_XENO_MONKEY_CLICK_CTRL, .proc/XenoMonkeyClickCtrl)
diff --git a/code/modules/shuttle/navigation_computer.dm b/code/modules/shuttle/navigation_computer.dm
index 55574ce1f6b..8821fe7286d 100644
--- a/code/modules/shuttle/navigation_computer.dm
+++ b/code/modules/shuttle/navigation_computer.dm
@@ -8,8 +8,6 @@
move_up_action = null
move_down_action = null
- var/datum/action/innate/shuttledocker_rotate/rotate_action = new
- var/datum/action/innate/shuttledocker_place/place_action = new
var/shuttleId = ""
var/shuttlePortId = ""
var/shuttlePortName = "custom location"
@@ -29,6 +27,8 @@
/obj/machinery/computer/camera_advanced/shuttle_docker/Initialize(mapload)
. = ..()
GLOB.navigation_computers += src
+ actions += new /datum/action/innate/shuttledocker_rotate(src)
+ actions += new /datum/action/innate/shuttledocker_place(src)
if(!mapload)
connect_to_shuttle(SSshuttle.get_containing_shuttle(src))
@@ -68,19 +68,9 @@
/obj/machinery/computer/camera_advanced/shuttle_docker/GrantActions(mob/living/user)
if(jumpto_ports.len)
- jump_action = new /datum/action/innate/camera_jump/shuttle_docker
+ actions += new /datum/action/innate/camera_jump/shuttle_docker(src)
..()
- if(rotate_action)
- rotate_action.target = user
- rotate_action.Grant(user)
- actions += rotate_action
-
- if(place_action)
- place_action.target = user
- place_action.Grant(user)
- actions += place_action
-
/obj/machinery/computer/camera_advanced/shuttle_docker/CreateEye()
shuttle_port = SSshuttle.getShuttle(shuttleId)
if(QDELETED(shuttle_port))
@@ -321,10 +311,9 @@
button_icon_state = "mech_cycle_equip_off"
/datum/action/innate/shuttledocker_rotate/Activate()
- if(QDELETED(target) || !isliving(target))
+ if(QDELETED(owner) || !isliving(owner))
return
- var/mob/living/C = target
- var/mob/camera/ai_eye/remote/remote_eye = C.remote_control
+ var/mob/camera/ai_eye/remote/remote_eye = owner.remote_control
var/obj/machinery/computer/camera_advanced/shuttle_docker/origin = remote_eye.origin
origin.rotateLandingSpot()
@@ -334,22 +323,20 @@
button_icon_state = "mech_zoom_off"
/datum/action/innate/shuttledocker_place/Activate()
- if(QDELETED(target) || !isliving(target))
+ if(QDELETED(owner) || !isliving(owner))
return
- var/mob/living/C = target
- var/mob/camera/ai_eye/remote/remote_eye = C.remote_control
+ var/mob/camera/ai_eye/remote/remote_eye = owner.remote_control
var/obj/machinery/computer/camera_advanced/shuttle_docker/origin = remote_eye.origin
- origin.placeLandingSpot(target)
+ origin.placeLandingSpot(owner)
/datum/action/innate/camera_jump/shuttle_docker
name = "Jump to Location"
button_icon_state = "camera_jump"
/datum/action/innate/camera_jump/shuttle_docker/Activate()
- if(QDELETED(target) || !isliving(target))
+ if(QDELETED(owner) || !isliving(owner))
return
- var/mob/living/C = target
- var/mob/camera/ai_eye/remote/remote_eye = C.remote_control
+ var/mob/camera/ai_eye/remote/remote_eye = owner.remote_control
var/obj/machinery/computer/camera_advanced/shuttle_docker/console = remote_eye.origin
playsound(console, 'sound/machines/terminal_prompt_deny.ogg', 25, FALSE)
@@ -382,7 +369,7 @@
if(isnull(selected))
playsound(console, 'sound/machines/terminal_prompt_deny.ogg', 25, FALSE)
return
- if(QDELETED(src) || QDELETED(target) || !isliving(target))
+ if(QDELETED(src) || QDELETED(owner) || !isliving(owner))
return
playsound(src, "terminal_type", 25, FALSE)
var/turf/T = get_turf(L[selected])
@@ -390,6 +377,6 @@
return
playsound(console, 'sound/machines/terminal_prompt_confirm.ogg', 25, FALSE)
remote_eye.setLoc(T)
- to_chat(target, span_notice("Jumped to [selected]."))
- C.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/flash/static)
- C.clear_fullscreen("flash", 3)
+ to_chat(owner, span_notice("Jumped to [selected]."))
+ owner.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/flash/static)
+ owner.clear_fullscreen("flash", 3)
diff --git a/code/modules/spells/spell_types/conjure.dm b/code/modules/spells/spell_types/conjure.dm
index dd1a7d7cb36..27902706a40 100644
--- a/code/modules/spells/spell_types/conjure.dm
+++ b/code/modules/spells/spell_types/conjure.dm
@@ -83,7 +83,8 @@
include_user = TRUE
range = -1
clothes_req = FALSE
- var/obj/item/item
+ ///List of weakrefs to items summoned
+ var/list/datum/weakref/item_refs = list()
var/item_type = /obj/item/banhammer
school = SCHOOL_CONJURATION
charge_max = 150
@@ -91,18 +92,18 @@
var/delete_old = TRUE //TRUE to delete the last summoned object if it's still there, FALSE for infinite item stream weeeee
/obj/effect/proc_holder/spell/targeted/conjure_item/cast(list/targets, mob/user = usr)
- if (delete_old && item && !QDELETED(item))
- QDEL_NULL(item)
- else
- for(var/mob/living/carbon/C in targets)
- if(C.dropItemToGround(C.get_active_held_item()))
- C.put_in_hands(make_item(), TRUE)
+ if (delete_old && length(item_refs))
+ QDEL_LIST(item_refs)
+ return
+ for(var/mob/living/carbon/C in targets)
+ if(C.dropItemToGround(C.get_active_held_item()))
+ C.put_in_hands(make_item(), TRUE)
/obj/effect/proc_holder/spell/targeted/conjure_item/Destroy()
- if(item)
- qdel(item)
+ QDEL_LIST(item_refs)
return ..()
/obj/effect/proc_holder/spell/targeted/conjure_item/proc/make_item()
- item = new item_type
+ var/obj/item/item = new item_type
+ item_refs += WEAKREF(item)
return item
diff --git a/code/modules/spells/spell_types/personality_commune.dm b/code/modules/spells/spell_types/personality_commune.dm
index f063dc8f641..d776bb0aef1 100644
--- a/code/modules/spells/spell_types/personality_commune.dm
+++ b/code/modules/spells/spell_types/personality_commune.dm
@@ -7,19 +7,22 @@
include_user = TRUE
action_icon_state = "telepathy"
action_background_icon_state = "bg_spell"
- var/datum/brain_trauma/severe/split_personality/trauma
+ // Bidaly reminder that spells are not really "owned" by anyone
+ /// Weakref to the trauma that owns this spell
+ var/datum/weakref/trauma_ref
var/flufftext = "You hear an echoing voice in the back of your head..."
/obj/effect/proc_holder/spell/targeted/personality_commune/New(datum/brain_trauma/severe/split_personality/T)
. = ..()
- trauma = T
+ trauma_ref = WEAKREF(T)
/obj/effect/proc_holder/spell/targeted/personality_commune/Destroy()
- trauma = null
+ trauma_ref = null
return ..()
// Pillaged and adapted from telepathy code
/obj/effect/proc_holder/spell/targeted/personality_commune/cast(list/targets, mob/user)
+ var/datum/brain_trauma/severe/split_personality/trauma = trauma_ref?.resolve()
if(!istype(trauma))
to_chat(user, span_warning("Something is wrong; Either due a bug or admemes, you are trying to cast this spell without a split personality!"))
return
diff --git a/code/modules/surgery/surgery.dm b/code/modules/surgery/surgery.dm
index e9213e92b54..f333825ab43 100644
--- a/code/modules/surgery/surgery.dm
+++ b/code/modules/surgery/surgery.dm
@@ -26,16 +26,18 @@
/datum/surgery/New(surgery_target, surgery_location, surgery_bodypart)
..()
- if(surgery_target)
- target = surgery_target
- target.surgeries += src
- if(surgery_location)
- location = surgery_location
- if(surgery_bodypart)
- operated_bodypart = surgery_bodypart
- if(targetable_wound)
- operated_wound = operated_bodypart.get_wound_type(targetable_wound)
- operated_wound.attached_surgery = src
+ if(!surgery_target)
+ return
+ target = surgery_target
+ target.surgeries += src
+ if(surgery_location)
+ location = surgery_location
+ if(!surgery_bodypart)
+ return
+ operated_bodypart = surgery_bodypart
+ if(targetable_wound)
+ operated_wound = operated_bodypart.get_wound_type(targetable_wound)
+ operated_wound.attached_surgery = src
/datum/surgery/Destroy()
if(operated_wound)
diff --git a/code/modules/tgui/tgui.dm b/code/modules/tgui/tgui.dm
index ace5b5ecec2..f6295984ccc 100644
--- a/code/modules/tgui/tgui.dm
+++ b/code/modules/tgui/tgui.dm
@@ -254,7 +254,7 @@
return
var/datum/host = src_object.ui_host(user)
// If the object or user died (or something else), abort.
- if(!src_object || !host || !user || !window)
+ if(!QDELETED(src_object) || !QDELETED(host) || !QDELETED(user) || !QDELETED(window))
close(can_be_suspended = FALSE)
return
// Validate ping
diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm
index 9ece3458a2d..e3e8e812b6b 100644
--- a/code/modules/unit_tests/_unit_tests.dm
+++ b/code/modules/unit_tests/_unit_tests.dm
@@ -112,7 +112,7 @@
#include "timer_sanity.dm"
#include "unit_test.dm"
#include "wizard.dm"
-#ifdef REFERENCE_TRACKING //Don't try and parse this file if ref tracking isn't turned on. IE: don't parse ref tracking please mr linter
+#ifdef REFERENCE_TRACKING_DEBUG //Don't try and parse this file if ref tracking isn't turned on. IE: don't parse ref tracking please mr linter
#include "find_reference_sanity.dm"
#endif
diff --git a/code/modules/vehicles/_vehicle.dm b/code/modules/vehicles/_vehicle.dm
index aa14db17c2a..e4a70786cff 100644
--- a/code/modules/vehicles/_vehicle.dm
+++ b/code/modules/vehicles/_vehicle.dm
@@ -43,6 +43,16 @@
occupant_actions = list()
generate_actions()
+/obj/vehicle/Destroy(force)
+ QDEL_NULL(trailer)
+ inserted_key = null
+ return ..()
+
+/obj/vehicle/Exited(atom/movable/gone, direction)
+ if(gone == inserted_key)
+ inserted_key = null
+ return ..()
+
/obj/vehicle/examine(mob/user)
. = ..()
if(resistance_flags & ON_FIRE)
diff --git a/code/modules/vehicles/mecha/mech_bay.dm b/code/modules/vehicles/mecha/mech_bay.dm
index afd019897f2..0f342903ce3 100644
--- a/code/modules/vehicles/mecha/mech_bay.dm
+++ b/code/modules/vehicles/mecha/mech_bay.dm
@@ -6,7 +6,7 @@
icon = 'icons/mecha/mech_bay.dmi'
icon_state = "recharge_port"
circuit = /obj/item/circuitboard/machine/mech_recharger
- var/obj/vehicle/sealed/mecha/recharging_mech
+ var/datum/weakref/recharging_mech_ref
var/obj/machinery/computer/mech_bay_power_console/recharge_console
var/recharge_power = 25
var/on = FALSE
@@ -39,9 +39,11 @@
/obj/machinery/mech_bay_recharge_port/process(delta_time)
if(machine_stat & NOPOWER || !recharge_console)
return
+ var/obj/vehicle/sealed/mecha/recharging_mech = recharging_mech_ref?.resolve()
if(!recharging_mech)
recharging_mech = locate(/obj/vehicle/sealed/mecha) in recharging_turf
if(recharging_mech)
+ recharging_mech_ref = WEAKREF(recharging_mech)
recharge_console.update_appearance()
if(recharging_mech && recharging_mech.cell)
if(recharging_mech.cell.charge < recharging_mech.cell.maxcharge)
@@ -51,7 +53,7 @@
else
recharge_console.update_appearance()
if(recharging_mech.loc != recharging_turf)
- recharging_mech = null
+ recharging_mech_ref = null
recharge_console.update_appearance()
@@ -94,15 +96,22 @@
/obj/machinery/computer/mech_bay_power_console/ui_data(mob/user)
var/list/data = list()
- if(recharge_port && !QDELETED(recharge_port))
- data["recharge_port"] = list("mech" = null)
- if(recharge_port.recharging_mech && !QDELETED(recharge_port.recharging_mech))
- data["recharge_port"]["mech"] = list("health" = recharge_port.recharging_mech.get_integrity(), "maxhealth" = recharge_port.recharging_mech.max_integrity, "cell" = null, "name" = recharge_port.recharging_mech.name,)
- if(recharge_port.recharging_mech.cell && !QDELETED(recharge_port.recharging_mech.cell))
- data["recharge_port"]["mech"]["cell"] = list(
- "charge" = recharge_port.recharging_mech.cell.charge,
- "maxcharge" = recharge_port.recharging_mech.cell.maxcharge
- )
+ if(QDELETED(recharge_port))
+ return data
+
+ data["recharge_port"] = list("mech" = null)
+ var/obj/vehicle/sealed/mecha/recharging_mech = recharge_port.recharging_mech_ref?.resolve()
+
+ if(!recharging_mech)
+ return data
+ data["recharge_port"]["mech"] = list("health" = recharging_mech.get_integrity(), "maxhealth" = recharging_mech.max_integrity, "cell" = null, "name" = recharging_mech.name,)
+
+ if(QDELETED(recharging_mech.cell))
+ return data
+ data["recharge_port"]["mech"]["cell"] = list(
+ "charge" = recharging_mech.cell.charge,
+ "maxcharge" = recharging_mech.cell.maxcharge
+ )
return data
@@ -127,9 +136,11 @@
. = ..()
if(machine_stat & (NOPOWER|BROKEN))
return
- if(!recharge_port?.recharging_mech?.cell)
+ var/obj/vehicle/sealed/mecha/recharging_mech = recharge_port?.recharging_mech_ref?.resolve()
+
+ if(!recharging_mech?.cell)
return
- if(recharge_port.recharging_mech.cell.charge >= recharge_port.recharging_mech.cell.maxcharge)
+ if(recharging_mech.cell.charge >= recharging_mech.cell.maxcharge)
return
. += "recharge_comp_on"
diff --git a/code/modules/vehicles/vehicle_actions.dm b/code/modules/vehicles/vehicle_actions.dm
index 5b0229d373f..055d81afc19 100644
--- a/code/modules/vehicles/vehicle_actions.dm
+++ b/code/modules/vehicles/vehicle_actions.dm
@@ -89,7 +89,9 @@
LAZYINITLIST(occupant_actions[take_from])
if(occupant_actions[take_from][actiontype])
var/datum/action/action = occupant_actions[take_from][actiontype]
- action.Remove(take_from)
+ // Actions don't dissipate on removal, they just sit around assuming they'll be reusued
+ // Gotta qdel
+ qdel(action)
occupant_actions[take_from] -= actiontype
return TRUE
@@ -165,10 +167,18 @@
button_icon_state = "vehicle_eject"
var/obj/vehicle/vehicle_target
+/datum/action/vehicle/Destroy()
+ vehicle_target = null
+ return ..()
+
/datum/action/vehicle/sealed
check_flags = AB_CHECK_IMMOBILE | AB_CHECK_CONSCIOUS
var/obj/vehicle/sealed/vehicle_entered_target
+/datum/action/vehicle/sealed/Destroy()
+ vehicle_entered_target = null
+ return ..()
+
/datum/action/vehicle/sealed/climb_out
name = "Climb Out"
desc = "Climb out of your vehicle!"
diff --git a/code/modules/wiremod/components/list/assoc_literal.dm b/code/modules/wiremod/components/list/assoc_literal.dm
index 251c1078c60..89b13a8e6c3 100644
--- a/code/modules/wiremod/components/list/assoc_literal.dm
+++ b/code/modules/wiremod/components/list/assoc_literal.dm
@@ -55,7 +55,7 @@
visible_message("[src] begins to overheat!")
return
var/value_to_add = entry_ports[index].value
- if(is_proper_datum(value_to_add))
+ if(isdatum(value_to_add))
value_to_add = WEAKREF(value_to_add)
new_literal[key_ports[index].value] = entry_ports[index].value
diff --git a/code/modules/wiremod/components/list/list_literal.dm b/code/modules/wiremod/components/list/list_literal.dm
index 8810d63b770..bb9f3c57b34 100644
--- a/code/modules/wiremod/components/list/list_literal.dm
+++ b/code/modules/wiremod/components/list/list_literal.dm
@@ -103,7 +103,7 @@
if(islist(value) && get_list_count(value, max_list_count) >= max_list_count)
visible_message("[src] begins to overheat!")
return
- if(is_proper_datum(value))
+ if(isdatum(value))
new_literal += WEAKREF(value)
else
new_literal += list(value)