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
This commit is contained in:
Mothblocks
2021-11-25 14:32:54 -08:00
committed by GitHub
parent b25906f383
commit d9b874197b
11 changed files with 195 additions and 46 deletions
@@ -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"
+18 -3
View File
@@ -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
@@ -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, \
@@ -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
*
@@ -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)
-9
View File
@@ -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].")]<br>"
if(HAS_TRAIT(src,TRAIT_HUSK))
. += span_warning("This body has been reduced to a grotesque husk.")
+7 -3
View File
@@ -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"
+86
View File
@@ -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 = "<b>, but without a linked operating computer, you won't get any research!</b>"
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
+19 -8
View File
@@ -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
+3
View File
@@ -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"
@@ -22,7 +22,9 @@ const damageTypes = [
];
export const OperatingComputer = (props, context) => {
const { act } = useBackend(context);
const [tab, setTab] = useSharedState(context, 'tab', 1);
return (
<Window
width={350}
@@ -39,6 +41,10 @@ export const OperatingComputer = (props, context) => {
onClick={() => setTab(2)}>
Surgery Procedures
</Tabs.Tab>
<Tabs.Tab
onClick={() => act("open_experiments")}>
Experiments
</Tabs.Tab>
</Tabs>
{tab === 1 && (
<PatientStateView />