From b011e723c64e936178ffbf2df9197faf44af2d48 Mon Sep 17 00:00:00 2001 From: vincentiusvin <54709710+vincentiusvin@users.noreply.github.com> Date: Fri, 12 Aug 2022 21:09:07 +0700 Subject: [PATCH] Refactors operating tables to be event driven + QoL + Unit Test (#69015) * Event driven table * Operating computer fix + loosening of check * Unit testing * IT NEEDS TO BE FORCE MOVE YOU GOTTA CLIMB TABLES AAAAH * Migrated patient to carbon instead of human Has no real bearing on the experiments tbh * DNAs can be null apparently * Simplify replacement code * Move comments --- code/game/machinery/computer/Operating.dm | 8 +-- code/game/objects/structures/tables_racks.dm | 67 +++++++++++-------- code/modules/unit_tests/_unit_tests.dm | 1 + code/modules/unit_tests/operating_table.dm | 32 +++++++++ .../tgui/interfaces/OperatingComputer.js | 9 +-- 5 files changed, 82 insertions(+), 35 deletions(-) create mode 100644 code/modules/unit_tests/operating_table.dm diff --git a/code/game/machinery/computer/Operating.dm b/code/game/machinery/computer/Operating.dm index 04c9442dcb1..ae7d19d8580 100644 --- a/code/game/machinery/computer/Operating.dm +++ b/code/game/machinery/computer/Operating.dm @@ -91,10 +91,10 @@ return data data["table"] = table - if(!table.check_eligible_patient()) - return data data["patient"] = list() - var/mob/living/carbon/human/patient = table.patient + if(!table.patient) + return data + var/mob/living/carbon/patient = table.patient switch(patient.stat) if(CONSCIOUS) @@ -110,7 +110,7 @@ data["patient"]["stat"] = "Dead" data["patient"]["statstate"] = "bad" data["patient"]["health"] = patient.health - data["patient"]["blood_type"] = patient.dna.blood_type + data["patient"]["blood_type"] = patient.dna?.blood_type data["patient"]["maxHealth"] = patient.maxHealth data["patient"]["minHealth"] = HEALTH_THRESHOLD_DEAD data["patient"]["bruteLoss"] = patient.getBruteLoss() diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm index 8ffa6a27da9..ca68b31da51 100644 --- a/code/game/objects/structures/tables_racks.dm +++ b/code/game/objects/structures/tables_racks.dm @@ -681,7 +681,7 @@ buckle_lying = NO_BUCKLE_LYING buckle_requires_restraints = TRUE custom_materials = list(/datum/material/silver = 2000) - var/mob/living/carbon/human/patient = null + var/mob/living/carbon/patient = null var/obj/machinery/computer/operating/computer = null /obj/structure/table/optable/Initialize(mapload) @@ -691,44 +691,57 @@ if(computer) computer.table = src break + RegisterSignal(loc, COMSIG_ATOM_ENTERED, .proc/mark_patient) + RegisterSignal(loc, COMSIG_ATOM_EXITED, .proc/unmark_patient) /obj/structure/table/optable/Destroy() - . = ..() if(computer && computer.table == src) computer.table = null + patient = null + UnregisterSignal(loc, COMSIG_ATOM_ENTERED) + UnregisterSignal(loc, COMSIG_ATOM_EXITED) + return ..() /obj/structure/table/optable/tablepush(mob/living/user, mob/living/pushed_mob) pushed_mob.forceMove(loc) pushed_mob.set_resting(TRUE, TRUE) visible_message(span_notice("[user] lays [pushed_mob] on [src].")) - get_patient() -/obj/structure/table/optable/proc/get_patient() - var/mob/living/carbon/M = locate(/mob/living/carbon) in loc - if(M) - if(M.resting) - set_patient(M) - else - set_patient(null) - -/obj/structure/table/optable/proc/set_patient(new_patient) - if(patient) - UnregisterSignal(patient, COMSIG_PARENT_QDELETING) - patient = new_patient - if(patient) - RegisterSignal(patient, COMSIG_PARENT_QDELETING, .proc/patient_deleted) - -/obj/structure/table/optable/proc/patient_deleted(datum/source) +/// Any mob that enters our tile will be marked as a potential patient. They will be turned into a patient if they lie down. +/obj/structure/table/optable/proc/mark_patient(datum/source, mob/living/carbon/potential_patient) SIGNAL_HANDLER - set_patient(null) + if(!istype(potential_patient)) + return + RegisterSignal(potential_patient, COMSIG_LIVING_SET_BODY_POSITION, .proc/recheck_patient) + recheck_patient(potential_patient) // In case the mob is already lying down before they entered. -/obj/structure/table/optable/proc/check_eligible_patient() - get_patient() - if(!patient) - return FALSE - if(ishuman(patient)) - return TRUE - return FALSE +/// Unmark the potential patient. +/obj/structure/table/optable/proc/unmark_patient(datum/source, mob/living/carbon/potential_patient) + SIGNAL_HANDLER + if(!istype(potential_patient)) + return + if(potential_patient == patient) + recheck_patient(patient) // Can just set patient to null, but doing the recheck lets us find a replacement patient. + UnregisterSignal(potential_patient, COMSIG_LIVING_SET_BODY_POSITION) + +/// Someone on our tile just lied down, got up, moved in, or moved out. +/// potential_patient is the mob that had one of those four things change. +/// The check is a bit broad so we can find a replacement patient. +/obj/structure/table/optable/proc/recheck_patient(mob/living/carbon/potential_patient) + SIGNAL_HANDLER + if(patient && patient != potential_patient) + return + + if(potential_patient.body_position == LYING_DOWN && potential_patient.loc == loc) + patient = potential_patient + return + + // Find another lying mob as a replacement. + for (var/mob/living/carbon/replacement_patient in loc.contents) + if(replacement_patient.body_position == LYING_DOWN) + patient = replacement_patient + return + patient = null /* * Racks diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index 18c94422482..b02d5d5ba29 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -120,6 +120,7 @@ #include "ntnetwork_tests.dm" #include "nuke_cinematic.dm" #include "objectives.dm" +#include "operating_table.dm" #include "outfit_sanity.dm" #include "paintings.dm" #include "pills.dm" diff --git a/code/modules/unit_tests/operating_table.dm b/code/modules/unit_tests/operating_table.dm new file mode 100644 index 00000000000..fad2cbe9831 --- /dev/null +++ b/code/modules/unit_tests/operating_table.dm @@ -0,0 +1,32 @@ +/// Make a mob hop on an optable, rest, get up, rest again, and then move to another tile. +/// While the mob is still an active patient, move another mob in too. +/// This is so the replacement code can kick in when the original mob is no longer valid. +/datum/unit_test/operating_table + +/datum/unit_test/operating_table/Run() + var/obj/structure/table/optable/table = allocate(/obj/structure/table/optable) + var/mob/living/carbon/human/human = allocate(/mob/living/carbon/human, get_step(table, NORTH)) + var/mob/living/carbon/human/replacement_human = allocate(/mob/living/carbon/human, get_step(table, NORTH)) + + // Resting is a bit more high level than bodypos, gets us nicer coverage. + human.set_resting(new_resting = FALSE, instant = TRUE) + replacement_human.set_resting(new_resting = FALSE, instant = TRUE) + + human.forceMove(get_turf(table)) + TEST_ASSERT_NULL(table.patient, "Operating table is occupied by a non-resting patient.") + + human.set_resting(new_resting = TRUE, instant = TRUE) + TEST_ASSERT_EQUAL(table.patient, human, "Operating table failed to update for a resting patient.") + + human.set_resting(new_resting = FALSE, instant = TRUE) + TEST_ASSERT_NULL(table.patient, "Operating table is occupied by a non-resting patient.") + + human.set_resting(new_resting = TRUE, instant = TRUE) + TEST_ASSERT_EQUAL(table.patient, human, "Operating table failed to update for a resting patient.") + + replacement_human.forceMove(get_turf(table)) + replacement_human.set_resting(new_resting = TRUE, instant = TRUE) + TEST_ASSERT_EQUAL(table.patient, human, "Operating table patient unset by another patient jumping in.") + + human.forceMove(get_step(get_turf(table), NORTH)) + TEST_ASSERT_EQUAL(table.patient, replacement_human, "Operating table failed to find a replacement patient.") diff --git a/tgui/packages/tgui/interfaces/OperatingComputer.js b/tgui/packages/tgui/interfaces/OperatingComputer.js index a718cfc1669..078df2a0642 100644 --- a/tgui/packages/tgui/interfaces/OperatingComputer.js +++ b/tgui/packages/tgui/interfaces/OperatingComputer.js @@ -55,13 +55,13 @@ const PatientStateView = (props, context) => { return ( <>
- {(patient && ( + {Object.keys(patient).length ? ( {patient.stat} - {patient.blood_type} + {patient.blood_type || 'Unable to determine blood type'} { ))} - )) || - 'No Patient Detected'} + ) : ( + 'No Patient Detected' + )}
{procedures.length === 0 &&
No Active Procedures
} {procedures.map((procedure) => (