diff --git a/_maps/RandomRuins/SpaceRuins/russian_derelict.dmm b/_maps/RandomRuins/SpaceRuins/russian_derelict.dmm index 65c1413bec8..e1671448e34 100644 --- a/_maps/RandomRuins/SpaceRuins/russian_derelict.dmm +++ b/_maps/RandomRuins/SpaceRuins/russian_derelict.dmm @@ -519,6 +519,10 @@ "fU" = ( /turf/open/floor/iron, /area/ruin/space/ks13/ai/corridor) +"gd" = ( +/obj/machinery/portable_atmospherics/canister/plasma, +/turf/open/floor/iron/airless, +/area/ruin/space/ks13/engineering/singulo) "ge" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/cable, @@ -741,11 +745,6 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating/airless, /area/ruin/space/ks13/engineering/singulo) -"jD" = ( -/obj/machinery/light/small/directional/east, -/obj/structure/closet/emcloset, -/turf/open/floor/iron/airless, -/area/ruin/space/ks13/dorms) "jV" = ( /obj/effect/spawner/structure/window/hollow/reinforced/middle, /turf/open/floor/plating/airless, @@ -3625,15 +3624,6 @@ /obj/structure/cable, /turf/open/floor/plating/airless, /area/ruin/space/ks13/security/cell) -"Cd" = ( -/obj/machinery/light/small/directional/west, -/obj/structure/table_frame, -/obj/item/wallframe/apc{ - pixel_x = -5; - pixel_y = 9 - }, -/turf/open/floor/iron/airless, -/area/ruin/space/ks13/engineering/tech_storage) "Ce" = ( /obj/effect/mapping_helpers/burnt_floor, /obj/machinery/airalarm/directional/north, @@ -4075,10 +4065,6 @@ }, /turf/open/floor/plating/airless, /area/ruin/space/ks13/science/rnd) -"EC" = ( -/obj/machinery/portable_atmospherics/canister/plasma, -/turf/open/floor/iron/airless, -/area/ruin/space/ks13/engineering/singulo) "ED" = ( /obj/machinery/door/airlock/maintenance{ name = "Atmospherics Access" @@ -4248,11 +4234,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron/airless, /area/ruin/space/ks13/hallway/aft) -"Fy" = ( -/obj/machinery/light/small/directional/west, -/obj/effect/mapping_helpers/burnt_floor, -/turf/open/floor/iron/airless, -/area/ruin/space/ks13/engineering/singulo) "FB" = ( /obj/machinery/light/small/directional/west, /obj/structure/closet, @@ -4549,6 +4530,13 @@ }, /turf/open/floor/plating/airless, /area/ruin/space/ks13/command/bridge) +"GW" = ( +/obj/item/circuitboard/machine/smes, +/obj/structure/table, +/obj/effect/spawner/random/maintenance, +/obj/effect/mapping_helpers/burnt_floor, +/turf/open/floor/iron/airless, +/area/ruin/space/ks13/engineering/secure_storage) "GY" = ( /obj/structure/cable, /turf/open/floor/iron/chapel{ @@ -4888,6 +4876,10 @@ /obj/structure/cable, /turf/open/floor/plating/airless, /area/ruin/space/ks13/engineering/singulo) +"IZ" = ( +/obj/effect/mapping_helpers/burnt_floor, +/turf/open/floor/iron/airless, +/area/ruin/space/ks13/engineering/singulo) "Jc" = ( /obj/effect/mapping_helpers/broken_floor, /obj/effect/mapping_helpers/broken_floor, @@ -4930,11 +4922,6 @@ /obj/item/wallframe/firealarm, /turf/open/floor/iron/airless, /area/ruin/space/ks13/engineering/atmos) -"Jm" = ( -/obj/machinery/light/small/directional/east, -/obj/effect/mapping_helpers/burnt_floor, -/turf/open/floor/plating/airless, -/area/ruin/space/ks13/engineering/singulo) "Jp" = ( /obj/effect/spawner/structure/window/hollow/reinforced/middle{ dir = 4 @@ -6061,14 +6048,6 @@ /obj/structure/cable, /turf/open/floor/iron, /area/ruin/space/ks13/security/court) -"Ps" = ( -/obj/machinery/light/small/directional/east, -/obj/item/circuitboard/machine/smes, -/obj/structure/table, -/obj/effect/spawner/random/maintenance, -/obj/effect/mapping_helpers/burnt_floor, -/turf/open/floor/iron/airless, -/area/ruin/space/ks13/engineering/secure_storage) "Pt" = ( /obj/structure/cable, /obj/effect/mapping_helpers/broken_floor, @@ -6731,6 +6710,10 @@ /obj/structure/chair, /turf/open/floor/iron/airless, /area/ruin/space/ks13/security/court_hall) +"SI" = ( +/obj/structure/closet/emcloset, +/turf/open/floor/iron/airless, +/area/ruin/space/ks13/dorms) "SJ" = ( /obj/structure/frame/computer{ anchored = 1; @@ -7596,6 +7579,15 @@ "Xa" = ( /turf/open/floor/plating/airless, /area/ruin/space/ks13/ai/corridor) +"Xb" = ( +/obj/machinery/light/small/directional/west, +/obj/structure/table_frame, +/obj/item/wallframe/apc{ + pixel_x = -5; + pixel_y = 9 + }, +/turf/open/floor/iron/airless, +/area/ruin/space/ks13/engineering/tech_storage) "Xd" = ( /obj/structure/closet/crate/bin, /obj/effect/decal/cleanable/dirt, @@ -9601,7 +9593,7 @@ Fq yq rm uf -jD +SI oU sm wc @@ -10939,7 +10931,7 @@ rk QG QG Xt -Cd +Xb DT QG QG @@ -11381,7 +11373,7 @@ rk rk sU sU -EC +gd CX PI PI @@ -11726,7 +11718,7 @@ PI Ex pi FI -Fy +IZ FI FI zv @@ -12856,7 +12848,7 @@ fg Jc gB PI -Jm +zv zv zv HX @@ -13651,7 +13643,7 @@ rk rr rr ab -Ps +GW DL rr rr diff --git a/code/__DEFINES/movement.dm b/code/__DEFINES/movement.dm index f71871f964a..540e5f7914c 100644 --- a/code/__DEFINES/movement.dm +++ b/code/__DEFINES/movement.dm @@ -39,6 +39,14 @@ GLOBAL_VAR_INIT(glide_size_multiplier, 1.0) ///Is the loop moving the movable outside its control, like it's an external force? e.g. footsteps won't play if enabled. #define MOVEMENT_LOOP_OUTSIDE_CONTROL (1<<4) +// Movement loop status flags +/// Has the loop been paused, soon to be resumed? +#define MOVELOOP_STATUS_PAUSED (1<<0) +/// Is the loop running? (Is true even when paused) +#define MOVELOOP_STATUS_RUNNING (1<<1) +/// Is the loop queued in a subsystem? +#define MOVELOOP_STATUS_QUEUED (1<<2) + /** * Returns a bitfield containing flags both present in `flags` arg and the `processing_move_loop_flags` move_packet variable. * Has no use outside of procs called within the movement proc chain. diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm index 3e6790aee6b..4886c2c86ae 100644 --- a/code/_onclick/hud/alert.dm +++ b/code/_onclick/hud/alert.dm @@ -17,13 +17,17 @@ if(!category || QDELETED(src)) return + var/datum/weakref/master_ref + if(isdatum(new_master)) + master_ref = WEAKREF(new_master) var/atom/movable/screen/alert/thealert if(alerts[category]) thealert = alerts[category] if(thealert.override_alerts) return thealert - if(new_master && new_master != thealert.master) - WARNING("[src] threw alert [category] with new_master [new_master] while already having that alert with master [thealert.master]") + if(master_ref && thealert.master_ref && master_ref != thealert.master_ref) + var/datum/current_master = thealert.master_ref.resolve() + WARNING("[src] threw alert [category] with new_master [new_master] while already having that alert with master [current_master]") clear_alert(category) return .() @@ -55,7 +59,7 @@ new_master.layer = old_layer new_master.plane = old_plane thealert.icon_state = "template" // We'll set the icon to the client's ui pref in reorganize_alerts() - thealert.master = new_master + thealert.master_ref = master_ref else thealert.icon_state = "[initial(thealert.icon_state)][severity]" thealert.severity = severity @@ -774,13 +778,14 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." desc = "This can be clicked on to perform an action." icon_state = "template" timeout = 30 SECONDS - /// The target to use the action on - var/atom/target + /// Weakref to the target atom to use the action on + var/datum/weakref/target_ref /// Which on click action to use var/action = NOTIFY_JUMP /atom/movable/screen/alert/notify_action/Click() . = ..() + var/atom/target = target_ref?.resolve() if(isnull(target)) return @@ -910,14 +915,15 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." if(LAZYACCESS(modifiers, SHIFT_CLICK)) // screen objects don't do the normal Click() stuff so we'll cheat to_chat(usr, span_boldnotice("[name] - [desc]")) return FALSE - if(master && click_master) - return usr.client.Click(master, location, control, params) + var/datum/our_master = master_ref?.resolve() + if(our_master && click_master) + return usr.client.Click(our_master, location, control, params) return TRUE /atom/movable/screen/alert/Destroy() . = ..() severity = 0 - master = null + master_ref = null owner = null screen_loc = "" diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index a87014a49d3..5053b4672f3 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -15,8 +15,8 @@ animate_movement = SLIDE_STEPS speech_span = SPAN_ROBOT appearance_flags = APPEARANCE_UI - /// A reference to the object in the slot. Grabs or items, generally. - var/obj/master = null + /// A reference to the object in the slot. Grabs or items, generally, but any datum will do. + var/datum/weakref/master_ref = null /// A reference to the owner HUD, if any. VAR_PRIVATE/datum/hud/hud = null /** @@ -42,7 +42,7 @@ hud = hud_owner /atom/movable/screen/Destroy() - master = null + master_ref = null hud = null return ..() @@ -249,10 +249,12 @@ /atom/movable/screen/close/Initialize(mapload, datum/hud/hud_owner, new_master) . = ..() - master = new_master + master_ref = WEAKREF(new_master) /atom/movable/screen/close/Click() - var/datum/storage/storage = master + var/datum/storage/storage = master_ref?.resolve() + if(!storage) + return storage.hide_contents(usr) return TRUE @@ -394,10 +396,10 @@ /atom/movable/screen/storage/Initialize(mapload, datum/hud/hud_owner, new_master) . = ..() - master = new_master + master_ref = WEAKREF(new_master) /atom/movable/screen/storage/Click(location, control, params) - var/datum/storage/storage_master = master + var/datum/storage/storage_master = master_ref?.resolve() if(!istype(storage_master)) return FALSE diff --git a/code/controllers/subsystem/movement/move_handler.dm b/code/controllers/subsystem/movement/move_handler.dm index 90266dd3139..fcc5c1c6504 100644 --- a/code/controllers/subsystem/movement/move_handler.dm +++ b/code/controllers/subsystem/movement/move_handler.dm @@ -121,9 +121,12 @@ SUBSYSTEM_DEF(move_manager) var/datum/controller/subsystem/movement/current_subsystem = running_loop.controller - current_subsystem.remove_loop(running_loop) - contesting_subsystem.add_loop(contestant) + var/current_running_loop = running_loop running_loop = contestant + current_subsystem.remove_loop(current_running_loop) + if(running_loop != contestant) // A signal registrant could have messed with things + return FALSE + contesting_subsystem.add_loop(contestant) return TRUE ///Tries to figure out the current favorite loop to run. More complex then just deciding between two different loops, assumes no running loop currently exists @@ -138,7 +141,7 @@ SUBSYSTEM_DEF(move_manager) var/datum/move_loop/checking = existing_loops[owner] if(checking.flags & MOVEMENT_LOOP_IGNORE_PRIORITY) continue - if(favorite && favorite.priority < checking.priority) + if(favorite && favorite.priority > checking.priority) continue favorite = checking @@ -152,8 +155,8 @@ SUBSYSTEM_DEF(move_manager) /datum/movement_packet/proc/remove_loop(datum/controller/subsystem/movement/remove_from, datum/move_loop/loop_to_remove) if(loop_to_remove == running_loop) - remove_from.remove_loop(loop_to_remove) running_loop = null + remove_from.remove_loop(loop_to_remove) if(loop_to_remove.flags & MOVEMENT_LOOP_IGNORE_PRIORITY) remove_from.remove_loop(loop_to_remove) if(QDELETED(src)) diff --git a/code/controllers/subsystem/movement/movement.dm b/code/controllers/subsystem/movement/movement.dm index c43debda543..425c67a0c47 100644 --- a/code/controllers/subsystem/movement/movement.dm +++ b/code/controllers/subsystem/movement/movement.dm @@ -52,8 +52,11 @@ SUBSYSTEM_DEF(movement) while(processing.len) var/datum/move_loop/loop = processing[processing.len] processing.len-- + // No longer queued since we just got removed from the loop + loop.queued_time = null loop.process() //This shouldn't get nulls, if it does, runtime - if(!QDELETED(loop)) //Re-Insert the loop + if(!QDELETED(loop) && loop.status & MOVELOOP_STATUS_QUEUED) //Re-Insert the loop + loop.status &= ~MOVELOOP_STATUS_QUEUED loop.timer = world.time + loop.delay queue_loop(loop) if (MC_TICK_CHECK) @@ -86,26 +89,42 @@ SUBSYSTEM_DEF(movement) buckets -= "[bucket_time]" /datum/controller/subsystem/movement/proc/queue_loop(datum/move_loop/loop) - var/target_time = loop.timer - var/string_time = "[target_time]" + if(loop.status & MOVELOOP_STATUS_QUEUED) + stack_trace("A move loop attempted to queue while already queued") + return + loop.queued_time = loop.timer + loop.status |= MOVELOOP_STATUS_QUEUED + var/list/our_bucket = buckets["[loop.queued_time]"] // If there's no bucket for this, lets set them up - if(!buckets[string_time]) - buckets[string_time] = list() + if(!our_bucket) + buckets["[loop.queued_time]"] = list() + our_bucket = buckets["[loop.queued_time]"] // This makes assoc buckets and sorted buckets point to the same place, allowing for quicker inserts - var/list/new_bucket = list(list(target_time, buckets[string_time])) - BINARY_INSERT_DEFINE(new_bucket, sorted_buckets, SORT_VAR_NO_TYPE, list(target_time), SORT_FIRST_INDEX, COMPARE_KEY) + var/list/new_bucket = list(list(loop.queued_time, our_bucket)) + var/list/compare_item = list(loop.queued_time) + BINARY_INSERT_DEFINE(new_bucket, sorted_buckets, SORT_VAR_NO_TYPE, compare_item, SORT_FIRST_INDEX, COMPARE_KEY) - buckets[string_time] += loop + our_bucket += loop /datum/controller/subsystem/movement/proc/dequeue_loop(datum/move_loop/loop) - var/list/our_entries = buckets["[loop.timer]"] + // Go home, you're not here anyway + if(!(loop.status & MOVELOOP_STATUS_QUEUED)) + return + if(isnull(loop.queued_time)) // This happens if a moveloop is dequeued while handling process() + loop.status &= ~MOVELOOP_STATUS_QUEUED + return + var/list/our_entries = buckets["[loop.queued_time]"] our_entries -= loop if(!length(our_entries)) - smash_bucket(bucket_time = loop.timer) // We can't pass an index in for context because we don't know our position + smash_bucket(bucket_time = loop.queued_time) // We can't pass an index in for context because we don't know our position + loop.queued_time = null + loop.status &= ~MOVELOOP_STATUS_QUEUED /datum/controller/subsystem/movement/proc/add_loop(datum/move_loop/add) + if(add.status & MOVELOOP_STATUS_QUEUED) + CRASH("Loop being added that is already queued.") add.loop_started() - if(QDELETED(add)) + if(QDELETED(add) || add.status & MOVELOOP_STATUS_QUEUED) return queue_loop(add) diff --git a/code/controllers/subsystem/movement/movement_types.dm b/code/controllers/subsystem/movement/movement_types.dm index 6e18d35dd8f..de8e4531236 100644 --- a/code/controllers/subsystem/movement/movement_types.dm +++ b/code/controllers/subsystem/movement/movement_types.dm @@ -21,12 +21,12 @@ var/delay = 1 ///The next time we should process ///Used primarially as a hint to be reasoned about by our [controller], and as the id of our bucket - ///Should not be modified directly outside of [start_loop] var/timer = 0 - ///Is this loop running or not - var/running = FALSE - ///Track if we're currently paused - var/paused = FALSE + ///The time we are CURRENTLY queued for processing + ///Do not modify this directly + var/queued_time = -1 + /// Status bitfield for what state the move loop is currently in + var/status = NONE /datum/move_loop/New(datum/movement_packet/owner, datum/controller/subsystem/movement/controller, atom/moving, priority, flags, datum/extra_info) src.owner = owner @@ -57,7 +57,7 @@ /datum/move_loop/proc/loop_started() SHOULD_CALL_PARENT(TRUE) SEND_SIGNAL(src, COMSIG_MOVELOOP_START) - running = TRUE + status |= MOVELOOP_STATUS_RUNNING //If this is our first time starting to move with this loop //And we're meant to start instantly if(!timer && flags & MOVEMENT_LOOP_START_FAST) @@ -68,7 +68,7 @@ ///Called when a loop is stopped, doesn't stop the loop itself /datum/move_loop/proc/loop_stopped() SHOULD_CALL_PARENT(TRUE) - running = FALSE + status &= ~MOVELOOP_STATUS_RUNNING SEND_SIGNAL(src, COMSIG_MOVELOOP_STOP) /datum/move_loop/proc/info_deleted(datum/source) @@ -91,7 +91,7 @@ ///Pauses the move loop for some passed in period ///This functionally means shifting its timer up, and clearing it from its current bucket /datum/move_loop/proc/pause_for(time) - if(!controller || !running) //No controller or not running? go away + if(!controller || !(status & MOVELOOP_STATUS_RUNNING)) //No controller or not running? go away return //Dequeue us from our current bucket controller.dequeue_loop(src) @@ -141,21 +141,21 @@ ///Pause our loop untill restarted with resume_loop() /datum/move_loop/proc/pause_loop() - if(!controller || !running || paused) //we dead + if(!controller || !(status & MOVELOOP_STATUS_RUNNING) || (status & MOVELOOP_STATUS_PAUSED)) //we dead return //Dequeue us from our current bucket controller.dequeue_loop(src) - paused = TRUE + status |= MOVELOOP_STATUS_PAUSED ///Resume our loop after being paused by pause_loop() /datum/move_loop/proc/resume_loop() - if(!controller || !running || !paused) + if(!controller || (status & MOVELOOP_STATUS_RUNNING|MOVELOOP_STATUS_PAUSED) != (MOVELOOP_STATUS_RUNNING|MOVELOOP_STATUS_PAUSED)) return - controller.queue_loop(src) timer = world.time - paused = FALSE + controller.queue_loop(src) + status &= ~MOVELOOP_STATUS_PAUSED ///Removes the atom from some movement subsystem. Defaults to SSmovement /datum/controller/subsystem/move_manager/proc/stop_looping(atom/movable/moving, datum/controller/subsystem/movement/subsystem = SSmovement) diff --git a/code/datums/ai/movement/_ai_movement.dm b/code/datums/ai/movement/_ai_movement.dm index dac9ecac001..af29e83f1a4 100644 --- a/code/datums/ai/movement/_ai_movement.dm +++ b/code/datums/ai/movement/_ai_movement.dm @@ -24,7 +24,7 @@ if(controller.pathing_attempts >= max_pathing_attempts) controller.CancelActions() -///Should the movement be allowed to happen? +///Should the movement be allowed to happen? return TRUE if it can, FALSE otherwise /datum/ai_movement/proc/allowed_to_move(datum/move_loop/source) SHOULD_BE_PURE(TRUE) @@ -60,10 +60,8 @@ source.delay = controller.movement_delay - // Why doesn't this return TRUE? - // MOVELOOP_SKIP_STEP is defined as (1<<0) and TRUE are defined as the same "1", returning TRUE would be the equivalent of skipping the move if(allowed_to_move(source)) - return + return NONE increment_pathing_failures(controller) return MOVELOOP_SKIP_STEP diff --git a/code/datums/ai/movement/ai_movement_complete_stop.dm b/code/datums/ai/movement/ai_movement_complete_stop.dm index f47b202b5a1..dcae93f1ba0 100644 --- a/code/datums/ai/movement/ai_movement_complete_stop.dm +++ b/code/datums/ai/movement/ai_movement_complete_stop.dm @@ -12,4 +12,4 @@ RegisterSignal(loop, COMSIG_MOVELOOP_PREPROCESS_CHECK, PROC_REF(pre_move)) /datum/ai_movement/complete_stop/allowed_to_move(datum/move_loop/source) - return FALSE + return FALSE diff --git a/code/datums/components/drift.dm b/code/datums/components/drift.dm index e974a3465ff..6b91a83534f 100644 --- a/code/datums/components/drift.dm +++ b/code/datums/components/drift.dm @@ -34,7 +34,7 @@ RegisterSignal(drifting_loop, COMSIG_MOVELOOP_POSTPROCESS, PROC_REF(after_move)) RegisterSignal(drifting_loop, COMSIG_QDELETING, PROC_REF(loop_death)) RegisterSignal(movable_parent, COMSIG_MOVABLE_NEWTONIAN_MOVE, PROC_REF(newtonian_impulse)) - if(drifting_loop.running) + if(drifting_loop.status & MOVELOOP_STATUS_RUNNING) drifting_start(drifting_loop) // There's a good chance it'll autostart, gotta catch that var/visual_delay = movable_parent.inertia_move_delay diff --git a/code/datums/components/shuttle_cling.dm b/code/datums/components/shuttle_cling.dm index bc6f022ab86..9cbff17c0a3 100644 --- a/code/datums/components/shuttle_cling.dm +++ b/code/datums/components/shuttle_cling.dm @@ -90,9 +90,9 @@ return //Do pause/unpause/nothing for the hyperloop - if(should_loop && hyperloop.paused) + if(should_loop && hyperloop.status & MOVELOOP_STATUS_PAUSED) hyperloop.resume_loop() - else if(!should_loop && !hyperloop.paused) + else if(!should_loop && !(hyperloop.status & MOVELOOP_STATUS_PAUSED)) hyperloop.pause_loop() ///Check if we're "holding on" to the shuttle diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 190a6ea2b95..0a719d727a2 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -815,6 +815,9 @@ * */ /mob/living/proc/revive(full_heal_flags = NONE, excess_healing = 0, force_grab_ghost = FALSE) + if(QDELETED(src)) + // Bro just like, don't ok + return FALSE if(excess_healing) adjustOxyLoss(-excess_healing, updating_health = FALSE) adjustToxLoss(-excess_healing, updating_health = FALSE, forced = TRUE) //slime friendly diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 6b894958876..d9bfef8f72b 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -308,7 +308,7 @@ toast.add_overlay(alert_overlay) toast.desc = "[message] -- Click to [action]." toast.name = header - toast.target = source + toast.target_ref = WEAKREF(source) /// Heals a robotic limb on a mob /proc/item_heal_robotic(mob/living/carbon/human/human, mob/user, brute_heal, burn_heal) diff --git a/code/modules/unit_tests/strange_reagent.dm b/code/modules/unit_tests/strange_reagent.dm index 345be4befe9..6c3add3092d 100644 --- a/code/modules/unit_tests/strange_reagent.dm +++ b/code/modules/unit_tests/strange_reagent.dm @@ -97,6 +97,8 @@ /datum/unit_test/strange_reagent/proc/test_death_no_damage(target_type) var/mob/living/target = allocate_new_target(target_type) target.death() + if(QDELETED(target)) + return update_amounts(target) strange_reagent.expose_mob(target, INGEST, amount_needed_to_revive) TEST_ASSERT_NOTEQUAL(target.stat, DEAD, "Strange Reagent did not revive a dead target type [target.type].") @@ -107,6 +109,8 @@ return target.death() + if(QDELETED(target)) + return update_amounts(target) strange_reagent.expose_mob(target, INGEST, amount_needed_to_revive) TEST_ASSERT_NOTEQUAL(target.stat, DEAD, "Strange Reagent did not revive a dead target type [target.type].") @@ -126,6 +130,8 @@ return target.death() + if(QDELETED(target)) + return update_amounts(target) strange_reagent.expose_mob(target, INGEST, amount_needed_to_full_heal) TEST_ASSERT_EQUAL(target_max_health, get_target_organic_health_manual(target), "Strange Reagent did not fully heal a dead target type [target.type] with the expected amount.") @@ -134,6 +140,8 @@ var/mob/living/target = allocate_new_target(target_type) if(!damage_target_to_percentage(target, strange_reagent.max_revive_damage_ratio * 0.9)) // 10% under the damage cap return + if(QDELETED(target)) + return update_amounts(target) strange_reagent.expose_mob(target, INGEST, amount_needed_to_revive) @@ -143,7 +151,9 @@ var/mob/living/target = allocate_new_target(target_type) if(!damage_target_to_percentage(target, strange_reagent.max_revive_damage_ratio * 1.1)) // 10% over the damage cap return - + if(QDELETED(target)) + return + update_amounts(target) strange_reagent.expose_mob(target, INGEST, amount_needed_to_revive) TEST_ASSERT_EQUAL(target.stat, DEAD, "Strange Reagent revived a target type [target.type] with more than double their max health in damage.") diff --git a/icons/obj/machines/computer.dmi b/icons/obj/machines/computer.dmi index 9cb0dda4967..cba0069cf6f 100644 Binary files a/icons/obj/machines/computer.dmi and b/icons/obj/machines/computer.dmi differ