From 2aca759d6aa23e98849c0c9353be76a6f71f35f5 Mon Sep 17 00:00:00 2001 From: SatinIsle <98125273+SatinIsle@users.noreply.github.com> Date: Thu, 24 Jul 2025 13:06:18 +0100 Subject: [PATCH] GM Tool - Create Medical Issue (#18033) * WIP medical issue GM tool * Progress * Basics are now working * Surgery! Needs more testing for each step, but the few I tried worked fine. * body scanner complete * Completed and tested! * Fixes my terrible TGUI stuff --- code/game/machinery/adv_med.dm | 27 +- .../objects/items/devices/scanners/health.dm | 25 + code/modules/admin/player_effects.dm | 12 + code/modules/eventkit/medical_issues.dm | 676 ++++++++++++++++++ code/modules/organs/organ.dm | 4 + .../BodyScannerMainOrgansExternal.tsx | 1 + .../BodyScannerMainOrgansInternal.tsx | 1 + .../tgui/interfaces/BodyScanner/types.ts | 2 + .../PlayerEffectsTabs/ControlMedical.tsx | 6 + vorestation.dme | 1 + 10 files changed, 752 insertions(+), 3 deletions(-) create mode 100644 code/modules/eventkit/medical_issues.dm diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm index 9d85735cfc..310f818c73 100644 --- a/code/game/machinery/adv_med.dm +++ b/code/game/machinery/adv_med.dm @@ -266,6 +266,13 @@ organData["bruised"] = E.min_bruised_damage organData["broken"] = E.min_broken_damage + var/list/medical_issueDataE = list() + for(var/datum/medical_issue/MI in E.medical_issues) + if(MI.showscanner) + medical_issueDataE += MI.name + + organData["medical_issues_E"] = medical_issueDataE + var/implantData[0] for(var/obj/thing in E.implants) var/implantSubData[0] @@ -345,6 +352,13 @@ intOrganData.Add(list(organData)) + var/list/medical_issueDataI = list() + for(var/datum/medical_issue/MI in I.medical_issues) + if(MI.showscanner) + medical_issueDataI += MI.name + + organData["medical_issues_I"] = medical_issueDataI + occupantData["intOrgan"] = intOrganData occupantData["blind"] = (H.sdisabilities & BLIND) @@ -479,6 +493,7 @@ var/internal_bleeding = "" var/lung_ruptured = "" var/o_dead = "" + var/mi = "" for(var/datum/wound/W in e.wounds) if(W.internal) internal_bleeding = "
Internal bleeding" break @@ -522,18 +537,22 @@ else unknown_body++ + for(var/datum/medical_issue/MI in e.medical_issues) + mi += "[MI.name] detected:" + if(unknown_body) imp += "Unknown body present:" - if(!AN && !open && !infected && !imp) + if(!AN && !open && !infected && !imp && !mi) AN = "None:" if(!(e.status & ORGAN_DESTROYED)) - dat += "[e.name][e.burn_dam][e.brute_dam][robot][bled][AN][splint][open][infected][imp][internal_bleeding][lung_ruptured][o_dead]" + dat += "[e.name][e.burn_dam][e.brute_dam][robot][bled][AN][splint][open][infected][imp][mi][internal_bleeding][lung_ruptured][o_dead]" else dat += "[e.name]--Not Found" dat += "" for(var/obj/item/organ/i in occupant.internal_organs) var/mech = "" var/i_dead = "" + var/mi = "" if(i.status & ORGAN_ASSISTED) mech = "Assisted:" if(i.robotic >= ORGAN_ROBOT) @@ -561,9 +580,11 @@ var/obj/item/organ/internal/appendix/A = i if(A.inflamed) infection = "Inflammation detected!" + for(var/datum/medical_issue/MI in i.medical_issues) + mi += "[MI.name] detected:" dat += "" - dat += "[i.name]N/A[i.damage][infection]:[mech][i_dead]" + dat += "[i.name]N/A[i.damage][infection]:[mi][mech][i_dead]" dat += "" for(var/organ_tag in occupant.species.has_organ) //Check to see if we are missing any organs var/organData[0] diff --git a/code/game/objects/items/devices/scanners/health.dm b/code/game/objects/items/devices/scanners/health.dm index 74badd0793..ba0f320686 100644 --- a/code/game/objects/items/devices/scanners/health.dm +++ b/code/game/objects/items/devices/scanners/health.dm @@ -418,6 +418,31 @@ dat+= span_danger("WARNING: Defib will cause extreme pain and set subject feral. Sedation recommended prior to defibrillation.") else // If they bop them and they're not dead or reviving, give 'em a little notice. dat += span_notice("Subject is a Xenochimera. Treat accordingly.") + + // Custom medical issues + for(var/obj/item/organ/I in H.internal_organs) + for(var/datum/medical_issue/MI in I.medical_issues) + if(advscan >= MI.advscan) + dat += span_danger("Warning: [MI.name] detected in [MI.affectedorgan].
") + if(advscan >= MI.advscan_cure) + if(MI.cure_reagent) + dat += span_notice("Suggested treatment: Prescription of [MI.cure_reagent].
") + else if(MI.cure_surgery) + dat += span_notice("Required surgery: [MI.cure_surgery].
") + else + dat += span_notice("[MI.affectedorgan] may require surgical removal or transplantation.
") + for(var/obj/item/organ/E in H.organs) + for(var/datum/medical_issue/MI in E.medical_issues) + if(advscan >= MI.advscan) + dat += span_danger("Warning: [MI.name] detected in [MI.affectedorgan].
") + if(advscan >= MI.advscan_cure) + if(MI.cure_reagent) + dat += span_notice("Suggested treatment: Prescription of [MI.cure_reagent].
") + else if(MI.cure_surgery) + dat += span_notice("Required surgery: [MI.cure_surgery].
") + else + dat += span_notice("[MI.affectedorgan] may require surgical removal or transplantation.
") + user.show_message(dat, 1) if(guide) guide(M, user) diff --git a/code/modules/admin/player_effects.dm b/code/modules/admin/player_effects.dm index 69243d2a66..f12ff24557 100644 --- a/code/modules/admin/player_effects.dm +++ b/code/modules/admin/player_effects.dm @@ -478,6 +478,18 @@ Tar.ingested.clear_reagents() Tar.touching.clear_reagents() + if("medical_issue") + var/mob/living/carbon/human/Tar = target + if(!istype(Tar)) + return + Tar.custom_medical_issue(user) + + if("clear_issue") + var/mob/living/carbon/human/Tar = target + if(!istype(Tar)) + return + Tar.clear_medical_issue(user) + ////////ABILITIES////////////// if("vent_crawl") diff --git a/code/modules/eventkit/medical_issues.dm b/code/modules/eventkit/medical_issues.dm new file mode 100644 index 0000000000..37d72770a1 --- /dev/null +++ b/code/modules/eventkit/medical_issues.dm @@ -0,0 +1,676 @@ +// These medical issues are designed to be simple, custom medical issues that can be created on the fly by GMs. +// Designed to be more simple than diseases, they use a separate system. +// They are not designed to replace any existing medical work, just allow GMs to make them up for events + +/datum/medical_issue + var/name = "medical issue" + var/mob/living/carbon/human/owner //Who is affected by this issue? Who's it attached to? + var/advscan = 0 //The required level of advanced scan to see this issue with health analysers, set to 4 to disable. + var/showscanner = FALSE //Should this issue show up on body scanners? + + var/obj/item/organ/affectedorgan //Which organ is this attached to? + + var/damagetype //What sort of damage should this deal to the owner? + var/damagestrength //How much damage should this deal over time to the owner? + var/maxdamage = 300 //What is the maximum amount of damage it can cause? + var/damageorgan = FALSE //Should this damage the organ it's attached to or the body in general? + + var/cure_reagent //Which reagent cures this issue, if any? + var/datum/surgery_step/cure_surgery //Which surgery step can be used to cure this? + var/unhealth = 100 //The amount of health the issue has, depleted by reagent + var/reagent_strength = 10 //How much health the reagent will remove per processing + var/advscan_cure = 0 //What level of advanced scan is required to reveal the cure? + + var/symptom_text //Messages relayed to the patient about their symptoms + var/symptom_affect //Visible effects on the patient such as vomiting or weakness + +/datum/medical_issue/proc/handle_effects() + if(!owner || !affectedorgan) + return + if(!istype(owner) || !istype(affectedorgan)) + return + + if(!(affectedorgan in owner.organs) && !(affectedorgan in owner.internal_organs)) + return + + if(unhealth <= 0) + cure_issue() + + if(damagestrength) + handle_damage() + + if(cure_reagent && reagent_strength) + handle_curing() + + if(symptom_text) + if(prob(5)) + to_chat(owner, span_danger("[symptom_text]")) + + if(symptom_affect) + handle_symptoms() + +/datum/medical_issue/proc/cure_issue() + LAZYREMOVE(affectedorgan.medical_issues,src) + qdel(src) + +/datum/medical_issue/proc/handle_damage() + if(damagestrength) + if(damageorgan) + var/maxtotal = max(maxdamage,affectedorgan.damage) //We don't want it to heal damage that's above the max by this + affectedorgan.damage = max((affectedorgan.damage + damagestrength),maxtotal) + else + switch(damagetype) + if(BRUTE) + if(maxdamage >= owner.getBruteLoss()) + owner.adjustBruteLoss(damagestrength) + if(BURN) + if(maxdamage >= owner.getFireLoss()) + owner.adjustFireLoss(damagestrength) + if(OXY) + if(maxdamage >= owner.getOxyLoss()) + owner.adjustFireLoss(damagestrength) + if(TOX) + if(maxdamage >= owner.getToxLoss()) + owner.adjustToxLoss(damagestrength) + if(CLONE) + if(maxdamage >= owner.getCloneLoss()) + owner.adjustCloneLoss(damagestrength) + if(HALLOSS) + if(maxdamage >= owner.getHalLoss()) + owner.adjustHalLoss(damagestrength) + +/datum/medical_issue/proc/handle_curing() + for(var/datum/reagent/R in owner.reagents.reagent_list) + if(R.name == cure_reagent) + unhealth = unhealth - reagent_strength + +/datum/medical_issue/proc/handle_symptoms() + switch(symptom_affect) + if("vomit") + if(prob(5)) + owner.vomit(10) + if("temporary weakness") + if(prob(5)) + owner.AdjustWeakened(5) + if("permanent weakness") + owner.weakened = min(owner.weakened,10) + if("temporary sleeping") + if(prob(5)) + owner.AdjustSleeping(5) + if("permanent sleeping") + owner.sleeping = min(owner.sleeping,10) + if("jittery") + owner.make_jittery(min(owner.jitteriness,10)) + if("paralysed") + owner.paralysis = min(owner.paralysis,10) + + +// Proc for setting all this up for GMs + +/mob/living/carbon/human/proc/custom_medical_issue(var/mob/user) + + var/list/external_organ_surgeries = list("bone reinforcement","remove growths","redirect blood vessels","extract object","flesh graft") + var/list/internal_organ_surgeries = list("remove growths","redirect blood vessels","close holes","ultrasound","reoxygenate tissue") + + var/issue_name = tgui_input_text(user,"What would you like to call this medical issue?","Name") + var/list/organ_options = list() + for(var/obj/item/organ/E in src.organs) + organ_options |= E + for(var/obj/item/organ/I in src.internal_organs) + organ_options |= I + var/obj/item/organ/issue_organ = tgui_input_list(user,"Which organ should this issue be attached to?","Affect organ",organ_options) + if(!issue_organ) + return + + var/damage = tgui_alert(user, "Should this apply damage?","Damage",list("Yes","No","Cancel")) + if(!damage || (damage == "Cancel")) + return + var/damage_organ + var/damage_value_pre + var/damage_value + var/damage_max + var/damage_type + if(damage == "Yes") + damage_organ = tgui_alert(user, "Should this damage the organ or body?","Damage",list("Organ","Body")) + if(!damage_organ) + return + damage_value_pre = tgui_input_number(user,"How much damage should this apply per processing. Low values are recommended, automatically divided by 10.","Damage",1) + damage_value = damage_value_pre / 10 + damage_max = tgui_input_number(user,"What is the maximum about of damage this issue can apply? It will not damage above this value.","Damage",300) + if(damage_organ == "Body") + damage_type = tgui_input_list(user, "Should this damage the organ or body?","Damage",list(BRUTE,BURN,OXY,TOX,CLONE,HALLOSS),BRUTE) + if(!damage_type) + return + + var/cure_q = tgui_alert(user, "Should this be cured by a reagent, surgery or organ removal only? Note that organ removal will always be an option if it's not a vital body part.","Cure",list("Reagent","Surgery","Removal","Cancel")) + if(!cure_q || (cure_q == "Cancel")) + return + var/datum/reagent/cure_reagent + var/cure_reagent_ID + var/cure_surgery + if(cure_q == "Reagent") + var/list/chem_list = typesof(/datum/reagent) + cure_reagent = tgui_input_list(user, "Which reagent should be the cure?", "Cure", chem_list) + if(!cure_reagent) + return + cure_reagent_ID = cure_reagent.name + + if(cure_q == "Surgery") + if(istype(issue_organ,/obj/item/organ/internal)) + cure_surgery = tgui_input_list(user, "Which surgery step should cure it?", "Cure", internal_organ_surgeries) + else + cure_surgery = tgui_input_list(user, "Which surgery step should cure it?", "Cure", external_organ_surgeries) + if(!cure_surgery) + return + + var/symptom_text = tgui_input_text(user,"What text should be displayed to the affected patient about their symptoms?","Symptoms") + var/symptom_affect = tgui_input_list(user, "What observable symptom should they display?", "Symptoms", list("vomit","temporary weakness","permanent weakness","temporary sleeping","permanent sleeping","jittery","paralysed","None")) + if(!symptom_affect) + return + + var/scanner_show = tgui_alert(user, "Should this show on body scanners?","Diagnosis",list("Yes","No","Cancel")) + if(!scanner_show || (scanner_show == "Cancel")) + return + + var/scanner_strength = tgui_input_number(user,"What level of health analyser is needed to see this? 0 for standard, 1 for improved, 2 for advanced, 3 for phasic and 4 for impossible.","Diagnosis",0) + var/advscan_cure = tgui_input_number(user, "What level of health analyser is required to display the cure? 0 for standard, 1 for improved, 2 for advanced, 3 for phasic and 4 for impossible.","Diagnosis",0) + + var/datum/medical_issue/M = new() + LAZYADD(issue_organ.medical_issues,M) + M.affectedorgan = issue_organ + M.name = issue_name + M.owner = src + M.advscan = scanner_strength + M.advscan_cure = advscan_cure + M.showscanner = scanner_show + + if(damage == "Yes") + if(damage_organ == "Body") + M.damagetype = damage_type + M.damageorgan = FALSE + else + M.damageorgan = TRUE + M.damagestrength = damage_value + M.maxdamage = damage_max + + if(cure_reagent) + M.cure_reagent = cure_reagent_ID + if(cure_surgery) + M.cure_surgery = cure_surgery + + if(symptom_text) + M.symptom_text = symptom_text + if(symptom_affect != "None") + M.symptom_affect = symptom_affect + + to_chat(user,"[issue_name] applied to [issue_organ] inside of [src]!") + if(damage == "Yes") + to_chat(user,"[issue_name] will damage the [damage_organ] with a strength of [damage_value], up to a maximum of [damage_max].") + if(cure_reagent) + to_chat(user,"[issue_name] can be cured with [cure_reagent_ID].") + else if(cure_surgery) + to_chat(user,"[issue_name] can be cured via the [cure_surgery] surgery.") + else + to_chat(user,"[issue_name] can only be cured by amputation or removal of \the [issue_organ]!") + +/mob/living/carbon/human/proc/clear_medical_issue(var/mob/user) + var/list/all_issues = list() + for(var/obj/item/organ/O in contents) + for(var/datum/medical_issue/MI in O.medical_issues) + all_issues |= MI + if(!all_issues.len) + to_chat(user,"No custom medical issues found in [src]!") + return + var/broad = tgui_alert(user, "Would you like to clear all custom medical issues or a specific one?","Damage",list("All","One","Cancel")) + if(!broad || (broad == "Cancel")) + return + + if(broad == "All") + for(var/datum/medical_issue/MI in all_issues) + MI.cure_issue() + to_chat(user,"[MI.name] removed from [MI.affectedorgan] in [src].") + + if(broad == "One") + var/datum/medical_issue/one_issue = tgui_input_list(user, "Which issue would you like to remove?", "Symptoms", all_issues) + if(!one_issue) + return + one_issue.cure_issue() + to_chat(user,"[one_issue.name] removed from [one_issue.affectedorgan] in [src].") + + +/////////////////////////////////////////////////////////////// +//////////////External Organ Surgeries///////////////////////// +/////////////////////////////////////////////////////////////// + +/datum/surgery_step/medical_issue + +/datum/surgery_step/medical_issue/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message(span_danger("[user]'s hand slips, damaging the bone in [target]'s [affected.name] with \the [tool]!") , \ + span_danger("Your hand slips, damaging the bone in [target]'s [affected.name] with \the [tool]!")) + user.balloon_alert_visible("slips, damaging the bone.", "your hand slips, damaging the bone") + affected.createwound(BRUISE, 5) + +//Bone-gel +/datum/surgery_step/medical_issue/strengthen_bone + surgery_name = "Reinforce Bone" + allowed_tools = list( + /obj/item/surgical/bonegel = 100 + ) + + allowed_procs = list(IS_SCREWDRIVER = 75) + + can_infect = 1 + blood_level = 1 + + min_duration = 50 + max_duration = 60 + +/datum/surgery_step/medical_issue/strengthen_bone/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + if (!hasorgans(target)) + return 0 + var/obj/item/organ/external/affected = target.get_organ(target_zone) + if(coverage_check(user, target, affected, tool)) + return 0 + for(var/datum/medical_issue/MI in affected.medical_issues) + if(MI.cure_surgery == "bone reinforcement") + return affected && (affected.robotic < ORGAN_ROBOT) && affected.open >= 2 + return 0 + +/datum/surgery_step/medical_issue/strengthen_bone/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message(span_notice("[user] is beginning to reinforce the bone in [target]'s [affected.name] in place with \the [tool].") , \ + span_notice("You are beginning to reinforce the bone in [target]'s [affected.name] in place with \the [tool].")) + user.balloon_alert_visible("begins to reinforce the bone.", "reinforcing the bone.") + target.custom_pain("The pain in your [affected.name] is going to make you pass out!", 50) + ..() + +/datum/surgery_step/medical_issue/strengthen_bone/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/obj/item/organ/external/affected = target.get_organ(target_zone) + for(var/datum/medical_issue/MI in affected.medical_issues) + if(MI.cure_surgery == "bone reinforcement") + user.visible_message(span_notice("[user] reinforces the bone in [target]'s [affected.name] with \the [tool]."), \ + span_notice("You reinforce the bone in [target]'s [affected.name] with \the [tool].")) + user.balloon_alert_visible("reinforces the bone.", "bone reinforced.") + MI.cure_issue() + +//scalpel +/datum/surgery_step/medical_issue/remove_growth + surgery_name = "Remove Growth" + allowed_tools = list( + /obj/item/surgical/scalpel = 100 + ) + + can_infect = 1 + blood_level = 1 + + min_duration = 50 + max_duration = 60 + +/datum/surgery_step/medical_issue/remove_growth/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + if (!hasorgans(target)) + return 0 + var/obj/item/organ/external/affected = target.get_organ(target_zone) + if(coverage_check(user, target, affected, tool)) + return 0 + for(var/datum/medical_issue/MI in affected.medical_issues) + if(MI.cure_surgery == "remove growths") + return affected && (affected.robotic < ORGAN_ROBOT) && affected.open >= 2 + return 0 + +/datum/surgery_step/medical_issue/remove_growth/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message(span_notice("[user] is beginning to remove growths in [target]'s [affected.name] with \the [tool].") , \ + span_notice("You are beginning to remove growths in [target]'s [affected.name] with \the [tool].")) + user.balloon_alert_visible("begins to remove growths.", "removing growths.") + target.custom_pain("The pain in your [affected.name] is going to make you pass out!", 50) + ..() + +/datum/surgery_step/medical_issue/remove_growth/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/obj/item/organ/external/affected = target.get_organ(target_zone) + for(var/datum/medical_issue/MI in affected.medical_issues) + if(MI.cure_surgery == "remove growths") + user.visible_message(span_notice("[user] removes the growths in [target]'s [affected.name] with \the [tool]."), \ + span_notice("You removes the growths in [target]'s [affected.name] with \the [tool].")) + user.balloon_alert_visible("removes the growth.", "removed growth.") + MI.cure_issue() + +//fixovein +/datum/surgery_step/medical_issue/redirect_vessels + surgery_name = "Redirect Blood Vessels" + allowed_tools = list( + /obj/item/surgical/FixOVein = 100 + ) + + can_infect = 1 + blood_level = 1 + + min_duration = 50 + max_duration = 60 + +/datum/surgery_step/medical_issue/redirect_vessels/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + if (!hasorgans(target)) + return 0 + var/obj/item/organ/external/affected = target.get_organ(target_zone) + if(coverage_check(user, target, affected, tool)) + return 0 + for(var/datum/medical_issue/MI in affected.medical_issues) + if(MI.cure_surgery == "redirect blood vessels") + return affected && (affected.robotic < ORGAN_ROBOT) && affected.open >= 2 + return 0 + +/datum/surgery_step/medical_issue/redirect_vessels/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message(span_notice("[user] is beginning to redirect blood vessels in [target]'s [affected.name] with \the [tool].") , \ + span_notice("You are beginning to redirect blood vessels in [target]'s [affected.name] with \the [tool].")) + user.balloon_alert_visible("begins to redirect blood vessels.", "redirecting blood vessels.") + target.custom_pain("The pain in your [affected.name] is going to make you pass out!", 50) + ..() + +/datum/surgery_step/medical_issue/redirect_vessels/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/obj/item/organ/external/affected = target.get_organ(target_zone) + for(var/datum/medical_issue/MI in affected.medical_issues) + if(MI.cure_surgery == "redirect blood vessels") + user.visible_message(span_notice("[user] redirected blood vessels in [target]'s [affected.name] with \the [tool]."), \ + span_notice("You redirected blood vessels in [target]'s [affected.name] with \the [tool].")) + user.balloon_alert_visible("redirected blood vessels.", "redirected blood vessels.") + MI.cure_issue() + +//hemostat +/datum/surgery_step/medical_issue/extract_object + surgery_name = "Extract Object" + allowed_tools = list( + /obj/item/surgical/hemostat = 100 + ) + + can_infect = 1 + blood_level = 1 + + min_duration = 50 + max_duration = 60 + +/datum/surgery_step/medical_issue/extract_object/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + if (!hasorgans(target)) + return 0 + var/obj/item/organ/external/affected = target.get_organ(target_zone) + if(coverage_check(user, target, affected, tool)) + return 0 + for(var/datum/medical_issue/MI in affected.medical_issues) + if(MI.cure_surgery == "extract object") + return affected && (affected.robotic < ORGAN_ROBOT) && affected.open >= 2 + return 0 + +/datum/surgery_step/medical_issue/extract_object/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message(span_notice("[user] is beginning to remove objects in [target]'s [affected.name] with \the [tool].") , \ + span_notice("You are beginning to remove objects in [target]'s [affected.name] with \the [tool].")) + user.balloon_alert_visible("begins to remove objects.", "removing objects.") + target.custom_pain("The pain in your [affected.name] is going to make you pass out!", 50) + ..() + +/datum/surgery_step/medical_issue/extract_object/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/obj/item/organ/external/affected = target.get_organ(target_zone) + for(var/datum/medical_issue/MI in affected.medical_issues) + if(MI.cure_surgery == "extract object") + user.visible_message(span_notice("[user] removes the objects in [target]'s [affected.name] with \the [tool]."), \ + span_notice("You removes the objects in [target]'s [affected.name] with \the [tool].")) + user.balloon_alert_visible("removes the objects.", "removed objects.") + MI.cure_issue() + +//brute kit +/datum/surgery_step/medical_issue/flesh_graft + surgery_name = "Graft Flesh" + allowed_tools = list( + /obj/item/stack/medical/advanced/bruise_pack = 100 + ) + + can_infect = 1 + blood_level = 1 + + min_duration = 50 + max_duration = 60 + +/datum/surgery_step/medical_issue/flesh_graft/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + if (!hasorgans(target)) + return 0 + var/obj/item/organ/external/affected = target.get_organ(target_zone) + if(coverage_check(user, target, affected, tool)) + return 0 + for(var/datum/medical_issue/MI in affected.medical_issues) + if(MI.cure_surgery == "flesh graft") + return affected && (affected.robotic < ORGAN_ROBOT) && affected.open >= 2 + return 0 + +/datum/surgery_step/medical_issue/flesh_graft/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message(span_notice("[user] is beginning to graft flesh in [target]'s [affected.name] with \the [tool].") , \ + span_notice("You are beginning to graft flesh in [target]'s [affected.name] with \the [tool].")) + user.balloon_alert_visible("begins to graft flesh.", "grafting flesh.") + target.custom_pain("The pain in your [affected.name] is going to make you pass out!", 50) + ..() + +/datum/surgery_step/medical_issue/flesh_graft/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/obj/item/organ/external/affected = target.get_organ(target_zone) + for(var/datum/medical_issue/MI in affected.medical_issues) + if(MI.cure_surgery == "flesh graft") + user.visible_message(span_notice("[user] grafts the flesh in [target]'s [affected.name] with \the [tool]."), \ + span_notice("You grafts the flesh in [target]'s [affected.name] with \the [tool].")) + user.balloon_alert_visible("grafted flesh.", "grafted flesh.") + MI.cure_issue() + +///////////////////Internal Organs + +//scalpel +/datum/surgery_step/medical_issue/remove_growth_internal + surgery_name = "Remove Growth on Organ" + allowed_tools = list( + /obj/item/surgical/scalpel = 100 + ) + + can_infect = 1 + blood_level = 1 + + min_duration = 50 + max_duration = 60 + +/datum/surgery_step/medical_issue/remove_growth_internal/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + if (!hasorgans(target)) + return 0 + var/obj/item/organ/external/affected = target.get_organ(target_zone) + if(coverage_check(user, target, affected, tool)) + return 0 + for(var/obj/item/organ/internal/I in affected.internal_organs) + for(var/datum/medical_issue/MI in I.medical_issues) + if(MI.cure_surgery == "remove growths") + return affected && (affected.robotic < ORGAN_ROBOT) && affected.open >= 2 + return 0 + +/datum/surgery_step/medical_issue/remove_growth_internal/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message(span_notice("[user] is beginning to remove growths in [target]'s [affected.name] with \the [tool].") , \ + span_notice("You are beginning to remove growths in [target]'s [affected.name] with \the [tool].")) + user.balloon_alert_visible("begins to remove growths.", "removing growths.") + target.custom_pain("The pain in your [affected.name] is going to make you pass out!", 50) + ..() + +/datum/surgery_step/medical_issue/remove_growth_internal/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/obj/item/organ/external/affected = target.get_organ(target_zone) + for(var/obj/item/organ/internal/I in affected.internal_organs) + for(var/datum/medical_issue/MI in I.medical_issues) + if(MI.cure_surgery == "remove growths") + user.visible_message(span_notice("[user] removes the growths in [target]'s [affected.name] with \the [tool]."), \ + span_notice("You removes the growths in [target]'s [affected.name] with \the [tool].")) + user.balloon_alert_visible("removes the growth.", "removed growth.") + MI.cure_issue() + +//fixovein +/datum/surgery_step/medical_issue/redirect_vessels_internal + surgery_name = "Redirect Blood Vessels" + allowed_tools = list( + /obj/item/surgical/FixOVein = 100 + ) + + can_infect = 1 + blood_level = 1 + + min_duration = 50 + max_duration = 60 + +/datum/surgery_step/medical_issue/redirect_vessels_internal/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + if (!hasorgans(target)) + return 0 + var/obj/item/organ/external/affected = target.get_organ(target_zone) + if(coverage_check(user, target, affected, tool)) + return 0 + for(var/obj/item/organ/internal/I in affected.internal_organs) + for(var/datum/medical_issue/MI in I.medical_issues) + if(MI.cure_surgery == "redirect blood vessels") + return affected && (affected.robotic < ORGAN_ROBOT) && affected.open >= 2 + return 0 + +/datum/surgery_step/medical_issue/redirect_vessels_internal/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message(span_notice("[user] is beginning to redirect blood vessels in [target]'s [affected.name] with \the [tool].") , \ + span_notice("You are beginning to redirect blood vessels in [target]'s [affected.name] with \the [tool].")) + user.balloon_alert_visible("begins to redirect blood vessels.", "redirecting blood vessels.") + target.custom_pain("The pain in your [affected.name] is going to make you pass out!", 50) + ..() + +/datum/surgery_step/medical_issue/redirect_vessels_internal/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/obj/item/organ/external/affected = target.get_organ(target_zone) + for(var/obj/item/organ/internal/I in affected.internal_organs) + for(var/datum/medical_issue/MI in I.medical_issues) + if(MI.cure_surgery == "redirect blood vessels") + user.visible_message(span_notice("[user] redirected blood vessels in [target]'s [affected.name] with \the [tool]."), \ + span_notice("You redirected blood vessels in [target]'s [affected.name] with \the [tool].")) + user.balloon_alert_visible("redirected blood vessels.", "redirected blood vessels.") + MI.cure_issue() + +//cautery +/datum/surgery_step/medical_issue/close_holes + surgery_name = "Close Holes" + allowed_tools = list( + /obj/item/surgical/cautery = 100 + ) + + can_infect = 1 + blood_level = 1 + + min_duration = 50 + max_duration = 60 + +/datum/surgery_step/medical_issue/close_holes/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + if (!hasorgans(target)) + return 0 + var/obj/item/organ/external/affected = target.get_organ(target_zone) + if(coverage_check(user, target, affected, tool)) + return 0 + for(var/obj/item/organ/internal/I in affected.internal_organs) + for(var/datum/medical_issue/MI in I.medical_issues) + if(MI.cure_surgery == "close holes") + return affected && (affected.robotic < ORGAN_ROBOT) && affected.open >= 2 + return 0 + +/datum/surgery_step/medical_issue/close_holes/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message(span_notice("[user] is beginning to close holes in [target]'s [affected.name] with \the [tool].") , \ + span_notice("You are beginning to close holes in [target]'s [affected.name] with \the [tool].")) + user.balloon_alert_visible("begins to close holes.", "closing holes.") + target.custom_pain("The pain in your [affected.name] is going to make you pass out!", 50) + ..() + +/datum/surgery_step/medical_issue/close_holes/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/obj/item/organ/external/affected = target.get_organ(target_zone) + for(var/obj/item/organ/internal/I in affected.internal_organs) + for(var/datum/medical_issue/MI in I.medical_issues) + if(MI.cure_surgery == "close holes") + user.visible_message(span_notice("[user] closed holes in [target]'s [affected.name] with \the [tool]."), \ + span_notice("You closed holes in [target]'s [affected.name] with \the [tool].")) + user.balloon_alert_visible("closed holes.", "closed holes.") + MI.cure_issue() + +//cautery +/datum/surgery_step/medical_issue/ultrasound + surgery_name = "Ultrasound" + allowed_tools = list( + /obj/item/autopsy_scanner = 100 + ) + + can_infect = 1 + blood_level = 1 + + min_duration = 50 + max_duration = 60 + +/datum/surgery_step/medical_issue/ultrasound/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + if (!hasorgans(target)) + return 0 + var/obj/item/organ/external/affected = target.get_organ(target_zone) + if(coverage_check(user, target, affected, tool)) + return 0 + for(var/obj/item/organ/internal/I in affected.internal_organs) + for(var/datum/medical_issue/MI in I.medical_issues) + if(MI.cure_surgery == "ultrasound") + return affected && (affected.robotic < ORGAN_ROBOT) && affected.open >= 2 + return 0 + +/datum/surgery_step/medical_issue/ultrasound/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message(span_notice("[user] is beginning to break up material using ultrasound in [target]'s [affected.name] with \the [tool].") , \ + span_notice("You are beginning to break up material using ultrasound in [target]'s [affected.name] with \the [tool].")) + user.balloon_alert_visible("begins to break up material using ultrasound.", "breaking up material using ultrasound.") + target.custom_pain("The pain in your [affected.name] is going to make you pass out!", 50) + ..() + +/datum/surgery_step/medical_issue/ultrasound/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/obj/item/organ/external/affected = target.get_organ(target_zone) + for(var/obj/item/organ/internal/I in affected.internal_organs) + for(var/datum/medical_issue/MI in I.medical_issues) + if(MI.cure_surgery == "ultrasound") + user.visible_message(span_notice("[user] broke up material with ultrasound in [target]'s [affected.name] with \the [tool]."), \ + span_notice("You broke up material with ultrasound in [target]'s [affected.name] with \the [tool].")) + user.balloon_alert_visible("broke up material with ultrasound.", "broke up material with ultrasound.") + MI.cure_issue() + +//cautery +/datum/surgery_step/medical_issue/reoxygenate_tissue + surgery_name = "Reoxygenate Tissue" + allowed_tools = list( + /obj/item/surgical/bioregen = 100 + ) + + can_infect = 1 + blood_level = 1 + + min_duration = 50 + max_duration = 60 + +/datum/surgery_step/medical_issue/reoxygenate_tissue/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + if (!hasorgans(target)) + return 0 + var/obj/item/organ/external/affected = target.get_organ(target_zone) + if(coverage_check(user, target, affected, tool)) + return 0 + for(var/obj/item/organ/internal/I in affected.internal_organs) + for(var/datum/medical_issue/MI in I.medical_issues) + if(MI.cure_surgery == "reoxygenate tissue") + return affected && (affected.robotic < ORGAN_ROBOT) && affected.open >= 2 + return 0 + +/datum/surgery_step/medical_issue/reoxygenate_tissue/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/obj/item/organ/external/affected = target.get_organ(target_zone) + user.visible_message(span_notice("[user] is beginning to reoxygenate tissue in [target]'s [affected.name] with \the [tool].") , \ + span_notice("You are beginning to reoxygenate tissue in [target]'s [affected.name] with \the [tool].")) + user.balloon_alert_visible("begins to reoxygenate tissue.", "reoxygenating tissue.") + target.custom_pain("The pain in your [affected.name] is going to make you pass out!", 50) + ..() + +/datum/surgery_step/medical_issue/reoxygenate_tissue/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/obj/item/organ/external/affected = target.get_organ(target_zone) + for(var/obj/item/organ/internal/I in affected.internal_organs) + for(var/datum/medical_issue/MI in I.medical_issues) + if(MI.cure_surgery == "reoxygenate tissue") + user.visible_message(span_notice("[user] reoxygenated tissue in [target]'s [affected.name] with \the [tool]."), \ + span_notice("You reoxygenated tissue in [target]'s [affected.name] with \the [tool].")) + user.balloon_alert_visible("reoxygenated tissue.", "reoxygenated tissue.") + MI.cure_issue() diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm index 177427dcac..21213e5f1e 100644 --- a/code/modules/organs/organ.dm +++ b/code/modules/organs/organ.dm @@ -45,6 +45,7 @@ var/list/organ_cache = list() var/butcherable = TRUE var/meat_type // What does butchering, if possible, make? + var/list/medical_issues = list() /obj/item/organ/Destroy() @@ -190,6 +191,9 @@ var/list/organ_cache = list() handle_rejection() handle_germ_effects() + for(var/datum/medical_issue/I in medical_issues) + I.handle_effects() + /obj/item/organ/examine(mob/user) . = ..() if(status & ORGAN_DEAD) diff --git a/tgui/packages/tgui/interfaces/BodyScanner/BodyScannerMainOrgansExternal.tsx b/tgui/packages/tgui/interfaces/BodyScanner/BodyScannerMainOrgansExternal.tsx index 00e3e2aec5..cce6d1d041 100644 --- a/tgui/packages/tgui/interfaces/BodyScanner/BodyScannerMainOrgansExternal.tsx +++ b/tgui/packages/tgui/interfaces/BodyScanner/BodyScannerMainOrgansExternal.tsx @@ -88,6 +88,7 @@ export const BodyScannerMainOrgansExternal = (props: { {reduceOrganStatus( o.implants.map((s) => (s.known ? s.name : 'Unknown object')), )} + {reduceOrganStatus(o.medical_issues_E)} diff --git a/tgui/packages/tgui/interfaces/BodyScanner/BodyScannerMainOrgansInternal.tsx b/tgui/packages/tgui/interfaces/BodyScanner/BodyScannerMainOrgansInternal.tsx index 198ed6c98b..181c2dac55 100644 --- a/tgui/packages/tgui/interfaces/BodyScanner/BodyScannerMainOrgansInternal.tsx +++ b/tgui/packages/tgui/interfaces/BodyScanner/BodyScannerMainOrgansInternal.tsx @@ -48,6 +48,7 @@ export const BodyScannerMainOrgansInternal = (props: { !!o.germ_level && germStatus(o.germ_level), !!o.inflamed && 'Appendicitis detected.', ])} + {reduceOrganStatus(o.medical_issues_I)} {reduceOrganStatus([ diff --git a/tgui/packages/tgui/interfaces/BodyScanner/types.ts b/tgui/packages/tgui/interfaces/BodyScanner/types.ts index ba9c9c46fd..13ebbfd463 100644 --- a/tgui/packages/tgui/interfaces/BodyScanner/types.ts +++ b/tgui/packages/tgui/interfaces/BodyScanner/types.ts @@ -54,6 +54,7 @@ export type internalOrgan = { dead: BooleanLike; inflamed: BooleanLike; missing: BooleanLike; + medical_issues_I: string[]; }; export type externalOrgan = { @@ -78,4 +79,5 @@ export type externalOrgan = { }; lungRuptured: BooleanLike; internalBleeding: BooleanLike; + medical_issues_E: string[]; }; diff --git a/tgui/packages/tgui/interfaces/PlayerEffects/PlayerEffectsTabs/ControlMedical.tsx b/tgui/packages/tgui/interfaces/PlayerEffects/PlayerEffectsTabs/ControlMedical.tsx index 2285f69747..736c80963f 100644 --- a/tgui/packages/tgui/interfaces/PlayerEffects/PlayerEffectsTabs/ControlMedical.tsx +++ b/tgui/packages/tgui/interfaces/PlayerEffects/PlayerEffectsTabs/ControlMedical.tsx @@ -39,6 +39,12 @@ export const ControlMedical = (props) => { + + ); }; diff --git a/vorestation.dme b/vorestation.dme index 941ac6f1c3..e3ee9a3e87 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -2524,6 +2524,7 @@ #include "code\modules\error_handler\error_viewer.dm" #include "code\modules\error_handler\~defines.dm" #include "code\modules\eventkit\event_machinery.dm" +#include "code\modules\eventkit\medical_issues.dm" #include "code\modules\eventkit\collector_event\admin_commands.dm" #include "code\modules\eventkit\collector_event\blockers.dm" #include "code\modules\eventkit\collector_event\event_collector.dm"