From d9b874197be4bbb330caeeaef7186d7b76bbad2a Mon Sep 17 00:00:00 2001
From: Mothblocks <35135081+Mothblocks@users.noreply.github.com>
Date: Thu, 25 Nov 2021 14:32:54 -0800
Subject: [PATCH] Replace slime extract experiments, required for biotech, with
dissection surgery (#63033)
About The Pull Request
Adds back dissection surgery for the purposes of performing experiments. Replaces the existing slime extract fetch quests with dissection surgery for biotech, and adds xenomorph dissection as an extra discount (though I would like to see a node require it some day).
Why It's Good For The Game
Science has no incentive to actually scan slime extracts for biotech, as the primary consumer is medical.
In practice, this means that the grey slime extract experiment, which can be done round-start, is frequently left unperformed. This lets medical have some level of control over their research, while still leaving the actual act of researching the node into the hands of the science department, or heads of staff.
Changelog
cl
add: Added back dissection surgery, which can be used with an operating computer to perform experiments for research.
add: Biological technology's requirement has been changed from scanning one grey slime extract to performing a dissection surgery.
add: Advanced biotech's requirement has been changed from scanning three slime extracts to performing a dissection surgery on a nonhuman.
add: Xeno-organ biology and Alien Biological Tools' now have additional discounts for performing a dissection surgery on a xenomorph.
/cl
---
.../dcs/signals/signals_operating_computer.dm | 5 ++
code/game/machinery/computer/Operating.dm | 21 ++++-
.../experisci/experiment/experiments.dm | 23 -----
.../experiment/handlers/experiment_handler.dm | 12 +++
.../experiment/types/dissection_experiment.dm | 39 +++++++++
code/modules/mob/mob_helpers.dm | 9 --
code/modules/research/techweb/all_nodes.dm | 10 ++-
code/modules/surgery/dissection.dm | 86 +++++++++++++++++++
code/modules/surgery/surgery.dm | 27 ++++--
tgstation.dme | 3 +
.../tgui/interfaces/OperatingComputer.js | 6 ++
11 files changed, 195 insertions(+), 46 deletions(-)
create mode 100644 code/__DEFINES/dcs/signals/signals_operating_computer.dm
create mode 100644 code/modules/experisci/experiment/types/dissection_experiment.dm
create mode 100644 code/modules/surgery/dissection.dm
diff --git a/code/__DEFINES/dcs/signals/signals_operating_computer.dm b/code/__DEFINES/dcs/signals/signals_operating_computer.dm
new file mode 100644
index 00000000000..94f2f52ede2
--- /dev/null
+++ b/code/__DEFINES/dcs/signals/signals_operating_computer.dm
@@ -0,0 +1,5 @@
+// /obj/machinery/computer/operating signals
+
+/// Fired when a dissection surgery completes.
+/// (mob/living/target)
+#define COMSIG_OPERATING_COMPUTER_DISSECTION_COMPLETE "operating_computer_dissection_complete"
diff --git a/code/game/machinery/computer/Operating.dm b/code/game/machinery/computer/Operating.dm
index 4dc6b421e4e..c0f83dffa49 100644
--- a/code/game/machinery/computer/Operating.dm
+++ b/code/game/machinery/computer/Operating.dm
@@ -13,16 +13,30 @@
var/datum/techweb/linked_techweb
light_color = LIGHT_COLOR_BLUE
+ var/datum/component/experiment_handler/experiment_handler
+
/obj/machinery/computer/operating/Initialize(mapload)
- . = ..()
+ ..()
linked_techweb = SSresearch.science_tech
find_table()
+ return INITIALIZE_HINT_LATELOAD
+
+/obj/machinery/computer/operating/LateInitialize()
+ . = ..()
+
+ experiment_handler = AddComponent( \
+ /datum/component/experiment_handler, \
+ allowed_experiments = list(/datum/experiment/dissection), \
+ config_flags = EXPERIMENT_CONFIG_ALWAYS_ACTIVE, \
+ config_mode = EXPERIMENT_CONFIG_ALTCLICK, \
+ )
/obj/machinery/computer/operating/Destroy()
for(var/direction in GLOB.alldirs)
table = locate(/obj/structure/table/optable) in get_step(src, direction)
if(table && table.computer == src)
table.computer = null
+ QDEL_NULL(experiment_handler)
. = ..()
/obj/machinery/computer/operating/attackby(obj/item/O, mob/user, params)
@@ -134,8 +148,9 @@
switch(action)
if("sync")
sync_surgeries()
- . = TRUE
- . = TRUE
+ if("open_experiments")
+ experiment_handler.ui_interact(usr)
+ return TRUE
#undef MENU_OPERATION
#undef MENU_SURGERIES
diff --git a/code/modules/experisci/experiment/experiments.dm b/code/modules/experisci/experiment/experiments.dm
index 349b161f385..b0d9f2ab035 100644
--- a/code/modules/experisci/experiment/experiments.dm
+++ b/code/modules/experisci/experiment/experiments.dm
@@ -2,29 +2,6 @@
name = "Base Slime Experiment"
required_points = 1
-/datum/experiment/scanning/points/slime/calibration
- name = "Slime Sample Test"
- description = "Let's see if our scanners can pick up the genetic data from a simple slime extract."
- required_atoms = list(/obj/item/slime_extract/grey = 1)
-
-/datum/experiment/scanning/points/slime/easy
- name = "Easy Slime Survey"
- description = "A wealthy client has requested that we provide samples of data from several basic slime cores."
- required_points = 3
- required_atoms = list(/obj/item/slime_extract/orange = 1,
- /obj/item/slime_extract/purple = 1,
- /obj/item/slime_extract/blue = 1,
- /obj/item/slime_extract/metal = 1)
-
-/datum/experiment/scanning/points/slime/moderate
- name = "Moderate Slime Survey"
- description = "Central Command has asked that you collect data from several common slime cores."
- required_points = 5
- required_atoms = list(/obj/item/slime_extract/yellow = 1,
- /obj/item/slime_extract/darkpurple = 1,
- /obj/item/slime_extract/darkblue = 1,
- /obj/item/slime_extract/silver = 1)
-
/datum/experiment/scanning/points/slime/hard
name = "Challenging Slime Survey"
description = "Another station has challenged your research team to collect several challenging slime cores, \
diff --git a/code/modules/experisci/experiment/handlers/experiment_handler.dm b/code/modules/experisci/experiment/handlers/experiment_handler.dm
index 4cf93bc9484..cc177a79eb4 100644
--- a/code/modules/experisci/experiment/handlers/experiment_handler.dm
+++ b/code/modules/experisci/experiment/handlers/experiment_handler.dm
@@ -56,6 +56,8 @@
RegisterSignal(parent, COMSIG_DOPPLER_ARRAY_EXPLOSION_DETECTED, .proc/try_run_doppler_experiment)
if(istype(parent, /obj/machinery/destructive_scanner))
RegisterSignal(parent, COMSIG_MACHINERY_DESTRUCTIVE_SCAN, .proc/try_run_destructive_experiment)
+ if(istype(parent, /obj/machinery/computer/operating))
+ RegisterSignal(parent, COMSIG_OPERATING_COMPUTER_DISSECTION_COMPLETE, .proc/try_run_dissection_experiment)
// Determine UI display mode
switch(config_mode)
@@ -173,6 +175,16 @@
playsound(src, 'sound/machines/buzz-sigh.ogg', 25)
our_array.say("Insufficient explosion to contribute to current experiment.")
+/// Hooks on a successful dissection experiment
+/datum/component/experiment_handler/proc/try_run_dissection_experiment(obj/source, mob/living/target)
+ SIGNAL_HANDLER
+
+ if (action_experiment(source, target))
+ playsound(source, 'sound/machines/ping.ogg', 25)
+ else
+ playsound(source, 'sound/machines/buzz-sigh.ogg', 25)
+ source.say("The dissection did not result in anything, either prior dissections have not been complete, or this one has already been researched.")
+
/**
* Announces a message to all experiment handlers
*
diff --git a/code/modules/experisci/experiment/types/dissection_experiment.dm b/code/modules/experisci/experiment/types/dissection_experiment.dm
new file mode 100644
index 00000000000..7e077920321
--- /dev/null
+++ b/code/modules/experisci/experiment/types/dissection_experiment.dm
@@ -0,0 +1,39 @@
+/datum/experiment/dissection
+ name = "Dissection Experiment"
+ description = "An experiment requiring a dissection surgery to progress"
+ exp_tag = "Dissection"
+ performance_hint = "Perform a dissection surgery while connected to an operating computer."
+
+/datum/experiment/dissection/is_complete()
+ return completed
+
+/datum/experiment/dissection/perform_experiment_actions(datum/component/experiment_handler/experiment_handler, mob/target)
+ if (is_valid_dissection(target))
+ completed = TRUE
+ return TRUE
+ else
+ return FALSE
+
+/datum/experiment/dissection/proc/is_valid_dissection(mob/target)
+ return TRUE
+
+/datum/experiment/dissection/human
+ name = "Human Dissection Experiment"
+ description = "We don't want to invest in a station that doesn't know their coccyx from their cochlea. Send us back data dissecting a human to receive more funding."
+
+/datum/experiment/dissection/human/is_valid_dissection(mob/target)
+ return ishumanbasic(target)
+
+/datum/experiment/dissection/nonhuman
+ name = "Non-human Dissection Experiment"
+ description = "When we asked for a tail bone, we didn't mean...look, just send us back data from something OTHER than a human. It could be a monkey for all we care, just send us research."
+
+/datum/experiment/dissection/nonhuman/is_valid_dissection(mob/target)
+ return ishuman(target) && !ishumanbasic(target)
+
+/datum/experiment/dissection/xenomorph
+ name = "Xenomorph Dissection Experiment"
+ description = "Our understanding of the xenomorph only scratches the surface. Send us research from dissecting a xenomorph."
+
+/datum/experiment/dissection/xenomorph/is_valid_dissection(mob/target)
+ return isalien(target)
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index ff0b4e995ec..16b2792b27f 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -502,15 +502,6 @@
* I wish examine was less copypasted. (oranges say, be the change you want to see buddy)
*/
/mob/proc/common_trait_examine()
- if(HAS_TRAIT(src, TRAIT_DISSECTED))
- var/dissectionmsg = ""
- if(HAS_TRAIT_FROM(src, TRAIT_DISSECTED,"Extraterrestrial Dissection"))
- dissectionmsg = " via Extraterrestrial Dissection. It is no longer worth experimenting on"
- else if(HAS_TRAIT_FROM(src, TRAIT_DISSECTED,"Experimental Dissection"))
- dissectionmsg = " via Experimental Dissection"
- else if(HAS_TRAIT_FROM(src, TRAIT_DISSECTED,"Thorough Dissection"))
- dissectionmsg = " via Thorough Dissection"
- . += "[span_notice("This body has been dissected and analyzed[dissectionmsg].")]
"
if(HAS_TRAIT(src,TRAIT_HUSK))
. += span_warning("This body has been reduced to a grotesque husk.")
diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm
index c69c9340154..d99cb469eea 100644
--- a/code/modules/research/techweb/all_nodes.dm
+++ b/code/modules/research/techweb/all_nodes.dm
@@ -290,7 +290,7 @@
"soda_dispenser",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
- required_experiments = list(/datum/experiment/scanning/points/slime/calibration)
+ required_experiments = list(/datum/experiment/dissection/human)
/datum/techweb_node/adv_biotech
id = "adv_biotech"
@@ -312,7 +312,7 @@
"smoke_machine",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000)
- required_experiments = list(/datum/experiment/scanning/points/slime/easy)
+ required_experiments = list(/datum/experiment/dissection/nonhuman)
discount_experiments = list(/datum/experiment/scanning/random/material/meat = 4000) //Big discount to reinforce doing it.
/datum/techweb_node/xenoorgan_biotech
@@ -327,7 +327,11 @@
"limbdesign_plasmaman",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 6500)
- discount_experiments = list(/datum/experiment/scanning/random/cytology/easy = 1000, /datum/experiment/scanning/points/slime/expert = 5000)
+ discount_experiments = list(
+ /datum/experiment/scanning/random/cytology/easy = 1000,
+ /datum/experiment/scanning/points/slime/hard = 5000,
+ /datum/experiment/dissection/xenomorph = 5000,
+ )
/datum/techweb_node/bio_process
id = "bio_process"
diff --git a/code/modules/surgery/dissection.dm b/code/modules/surgery/dissection.dm
new file mode 100644
index 00000000000..09d176f35e3
--- /dev/null
+++ b/code/modules/surgery/dissection.dm
@@ -0,0 +1,86 @@
+/datum/surgery/dissection
+ name = "Dissection"
+ target_mobtypes = list(
+ /mob/living/carbon/human,
+ /mob/living/carbon/alien,
+ )
+ possible_locs = list(BODY_ZONE_CHEST)
+ requires_real_bodypart = TRUE
+ steps = list(
+ /datum/surgery_step/incise,
+ /datum/surgery_step/retract_skin,
+ /datum/surgery_step/clamp_bleeders,
+ /datum/surgery_step/dissection,
+ /datum/surgery_step/close,
+ )
+
+/datum/surgery/dissection/can_start(mob/user, mob/living/patient)
+ . = ..()
+
+ // This isn't a real advanced tech, but it doesn't make sense using it without an operating computer
+ if (isnull(locate_operating_computer(get_turf(patient))))
+ return FALSE
+
+ if (HAS_TRAIT(patient, TRAIT_DISSECTED))
+ return FALSE
+
+ if (patient.stat != DEAD)
+ return FALSE
+
+ return TRUE
+
+/datum/surgery_step/dissection
+ name = "dissect"
+ time = 16 SECONDS
+ implements = list(
+ TOOL_SCALPEL = 100,
+ /obj/item/melee/energy/sword = 75,
+ /obj/item/knife = 65,
+ /obj/item/shard = 45,
+ /obj/item = 30,
+ )
+
+/datum/surgery_step/dissection/preop(mob/user, mob/living/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ var/ending = "..."
+ if (isnull(surgery.locate_operating_computer(get_turf(target))))
+ ending = ", but without a linked operating computer, you won't get any research!"
+
+ display_results(
+ user,
+ target,
+ span_notice("You start to dissect [target][ending]"),
+ span_notice("[user] starts to dissect [target]..."),
+ span_notice("[user] begins to start poking around inside your corpse...hey, wait a minute!"),
+ )
+
+/datum/surgery_step/dissection/success(mob/user, mob/living/target, target_zone, obj/item/tool, datum/surgery/surgery, default_display_results)
+ ADD_TRAIT(target, TRAIT_DISSECTED, REF(src))
+
+ var/obj/machinery/computer/operating/operating_computer = surgery.locate_operating_computer(get_turf(target))
+ if (!isnull(operating_computer))
+ SEND_SIGNAL(operating_computer, COMSIG_OPERATING_COMPUTER_DISSECTION_COMPLETE, target)
+
+ return TRUE
+
+/datum/surgery_step/dissection/failure(mob/user, mob/living/target, target_zone, obj/item/tool, datum/surgery/surgery, fail_prob)
+ display_results(
+ user,
+ target,
+ span_notice("You mess up, damaging some of the internal organs!"),
+ span_notice("[user] messes up, damaging some of the internal organs!"),
+ span_notice("[user] messes up, damaging some of your internal organs!")
+ )
+
+ target.adjustOrganLoss(pick(
+ ORGAN_SLOT_APPENDIX,
+ ORGAN_SLOT_BRAIN,
+ ORGAN_SLOT_HEART,
+ ORGAN_SLOT_LIVER,
+ ORGAN_SLOT_LUNGS,
+ ORGAN_SLOT_STOMACH,
+ ), 20)
+
+ return FALSE
+
+/datum/surgery_step/dissection/tool_check(mob/user, obj/item/tool)
+ return implement_type != /obj/item || tool.get_sharpness() > 0
diff --git a/code/modules/surgery/surgery.dm b/code/modules/surgery/surgery.dm
index 1462a436274..81db8718d2f 100644
--- a/code/modules/surgery/surgery.dm
+++ b/code/modules/surgery/surgery.dm
@@ -76,14 +76,9 @@
var/turf/patient_turf = get_turf(patient)
//Get the relevant operating computer
- var/obj/machinery/computer/operating/opcomputer
- var/obj/structure/table/optable/optable = locate(/obj/structure/table/optable, patient_turf)
- if(optable?.computer)
- opcomputer = optable.computer
- if(!opcomputer)
- return
- if(opcomputer.machine_stat & (NOPOWER|BROKEN))
- return .
+ var/obj/machinery/computer/operating/opcomputer = locate_operating_computer(patient_turf)
+ if (isnull(opcomputer))
+ return FALSE
if(replaced_by in opcomputer.advanced_surgeries)
return FALSE
if(type in opcomputer.advanced_surgeries)
@@ -133,6 +128,22 @@
)
qdel(src)
+/// Returns a nearby operating computer linked to an operating table
+/datum/surgery/proc/locate_operating_computer(turf/patient_turf)
+ if (isnull(patient_turf))
+ return null
+
+ var/obj/structure/table/optable/operating_table = locate(/obj/structure/table/optable, patient_turf)
+ var/obj/machinery/computer/operating/operating_computer = operating_table?.computer
+
+ if (isnull(operating_computer))
+ return null
+
+ if(operating_computer.machine_stat & (NOPOWER|BROKEN))
+ return null
+
+ return operating_computer
+
/datum/surgery/advanced
name = "advanced surgery"
requires_tech = TRUE
diff --git a/tgstation.dme b/tgstation.dme
index 8dd830072e5..7be398da256 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -197,6 +197,7 @@
#include "code\__DEFINES\dcs\signals\signals_music.dm"
#include "code\__DEFINES\dcs\signals\signals_NTNet.dm"
#include "code\__DEFINES\dcs\signals\signals_object.dm"
+#include "code\__DEFINES\dcs\signals\signals_operating_computer.dm"
#include "code\__DEFINES\dcs\signals\signals_radiation.dm"
#include "code\__DEFINES\dcs\signals\signals_reagent.dm"
#include "code\__DEFINES\dcs\signals\signals_restaurant.dm"
@@ -2499,6 +2500,7 @@
#include "code\modules\experisci\experiment\experiments.dm"
#include "code\modules\experisci\experiment\physical_experiments.dm"
#include "code\modules\experisci\experiment\handlers\experiment_handler.dm"
+#include "code\modules\experisci\experiment\types\dissection_experiment.dm"
#include "code\modules\experisci\experiment\types\experiment.dm"
#include "code\modules\experisci\experiment\types\exploration.dm"
#include "code\modules\experisci\experiment\types\explosive.dm"
@@ -3754,6 +3756,7 @@
#include "code\modules\surgery\core_removal.dm"
#include "code\modules\surgery\coronary_bypass.dm"
#include "code\modules\surgery\dental_implant.dm"
+#include "code\modules\surgery\dissection.dm"
#include "code\modules\surgery\eye_surgery.dm"
#include "code\modules\surgery\gastrectomy.dm"
#include "code\modules\surgery\healing.dm"
diff --git a/tgui/packages/tgui/interfaces/OperatingComputer.js b/tgui/packages/tgui/interfaces/OperatingComputer.js
index 455c035de04..07898019c09 100644
--- a/tgui/packages/tgui/interfaces/OperatingComputer.js
+++ b/tgui/packages/tgui/interfaces/OperatingComputer.js
@@ -22,7 +22,9 @@ const damageTypes = [
];
export const OperatingComputer = (props, context) => {
+ const { act } = useBackend(context);
const [tab, setTab] = useSharedState(context, 'tab', 1);
+
return (
{
onClick={() => setTab(2)}>
Surgery Procedures
+ act("open_experiments")}>
+ Experiments
+
{tab === 1 && (