From 9fb6c2f3a3607e26645b879bbd3508c18ac120d7 Mon Sep 17 00:00:00 2001
From: Fermi <>
Date: Tue, 19 Nov 2019 12:22:53 +0000
Subject: [PATCH] Okay I gotta hop
---
code/game/machinery/fersi.dm | 268 ++++++++++++++++++
.../reagent_containers/sleeper_buffer.dm | 10 +
tgui/src/interfaces/fersi.ract | 57 ++++
3 files changed, 335 insertions(+)
create mode 100644 code/game/machinery/fersi.dm
create mode 100644 code/modules/reagents/reagent_containers/sleeper_buffer.dm
create mode 100644 tgui/src/interfaces/fersi.ract
diff --git a/code/game/machinery/fersi.dm b/code/game/machinery/fersi.dm
new file mode 100644
index 0000000000..938ad42cc7
--- /dev/null
+++ b/code/game/machinery/fersi.dm
@@ -0,0 +1,268 @@
+/obj/machinery/sleep_console
+ name = "sleeper console"
+ icon = 'icons/obj/machines/sleeper.dmi'
+ icon_state = "console"
+ density = FALSE
+
+/obj/machinery/sleeper
+ name = "sleeper"
+ desc = "An enclosed machine used to stabilize and heal patients."
+ icon = 'icons/obj/machines/sleeper.dmi'
+ icon_state = "sleeper"
+ density = FALSE
+ state_open = TRUE
+ circuit = /obj/item/circuitboard/machine/sleeper
+ var/efficiency = 1
+ var/min_health = -25
+ var/list/available_chems
+ var/controls_inside = FALSE
+ var/list/possible_chems = list(
+ list("epinephrine", "morphine", "salbutamol", "bicaridine", "kelotane"),
+ list("oculine","inacusiate"),
+ list("antitoxin", "mutadone", "mannitol", "pen_acid"),
+ list("omnizine")
+ )
+ var/list/chem_buttons //Used when emagged to scramble which chem is used, eg: antitoxin -> morphine
+ var/scrambled_chems = FALSE //Are chem buttons scrambled? used as a warning
+ var/enter_message = "You feel cool air surround you. You go numb as your senses turn inward."
+
+/obj/machinery/sleeper/Initialize()
+ . = ..()
+ create_reagents(300)
+ occupant_typecache = GLOB.typecache_living
+ update_icon()
+ reset_chem_buttons()
+ add_inital_chems()
+
+/obj/machinery/sleeper/RefreshParts()
+ var/E
+ for(var/obj/item/stock_parts/matter_bin/B in component_parts)
+ E += B.rating
+ var/I
+ for(var/obj/item/stock_parts/manipulator/M in component_parts)
+ I += M.rating
+
+ efficiency = initial(efficiency)* E
+ min_health = initial(min_health) - (10*(E-1)) // CIT CHANGE - changes min health equation to be min_health - (matterbin rating * 10)
+ available_chems = list()
+ for(var/i in 1 to I)
+ available_chems |= possible_chems[i]
+ reset_chem_buttons()
+
+ //Total container size 300 - 1200u
+ reagents.maximum_volume = (300*E)
+
+/obj/machinery/sleeper/update_icon()
+ icon_state = initial(icon_state)
+ if(state_open)
+ icon_state += "-open"
+
+/obj/machinery/sleeper/container_resist(mob/living/user)
+ visible_message("[occupant] emerges from [src]!",
+ "You climb out of [src]!")
+ open_machine()
+
+/obj/machinery/sleeper/Exited(atom/movable/user)
+ if (!state_open && user == occupant)
+ container_resist(user)
+
+/obj/machinery/sleeper/relaymove(mob/user)
+ if (!state_open)
+ container_resist(user)
+
+/obj/machinery/sleeper/open_machine()
+ if(!state_open && !panel_open)
+ ..()
+
+/obj/machinery/sleeper/close_machine(mob/user)
+ if((isnull(user) || istype(user)) && state_open && !panel_open)
+ ..(user)
+ var/mob/living/mob_occupant = occupant
+ if(mob_occupant && mob_occupant.stat != DEAD)
+ to_chat(occupant, "[enter_message]")
+
+/obj/machinery/sleeper/emp_act(severity)
+ . = ..()
+ if (. & EMP_PROTECT_SELF)
+ return
+ if(is_operational() && occupant)
+ var/datum/reagent/R = pick(reagents.reagent_list)
+ inject_chem(R.id, occupant)
+ open_machine()
+
+
+/obj/machinery/sleeper/MouseDrop_T(mob/target, mob/user)
+ if(user.stat || user.lying || !Adjacent(user) || !user.Adjacent(target) || !iscarbon(target) || !user.IsAdvancedToolUser())
+ return
+ close_machine(target)
+
+/obj/machinery/sleeper/screwdriver_act(mob/living/user, obj/item/I)
+ . = TRUE
+ if(..())
+ return
+ if(occupant)
+ to_chat(user, "[src] is currently occupied!")
+ return
+ if(state_open)
+ to_chat(user, "[src] must be closed to [panel_open ? "close" : "open"] its maintenance hatch!")
+ return
+ if(default_deconstruction_screwdriver(user, "[initial(icon_state)]-o", initial(icon_state), I))
+ return
+ return FALSE
+
+/obj/machinery/sleeper/wrench_act(mob/living/user, obj/item/I)
+ . = ..()
+ if(default_change_direction_wrench(user, I))
+ return TRUE
+
+/obj/machinery/sleeper/crowbar_act(mob/living/user, obj/item/I)
+ . = ..()
+ if(default_pry_open(I))
+ return TRUE
+ if(default_deconstruction_crowbar(I))
+ return TRUE
+
+/obj/machinery/sleeper/default_pry_open(obj/item/I) //wew
+ . = !(state_open || panel_open || (flags_1 & NODECONSTRUCT_1)) && I.tool_behaviour == TOOL_CROWBAR
+ if(.)
+ I.play_tool_sound(src, 50)
+ visible_message("[usr] pries open [src].", "You pry open [src].")
+ open_machine()
+
+/obj/machinery/sleeper/AltClick(mob/user)
+ if(!user.canUseTopic(src, !issilicon(user)))
+ return
+ if(state_open)
+ close_machine()
+ else
+ open_machine()
+
+/obj/machinery/sleeper/examine(mob/user)
+ ..()
+ to_chat(user, "Alt-click [src] to [state_open ? "close" : "open"] it.")
+
+/obj/machinery/sleeper/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
+ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state)
+
+ if(controls_inside && state == GLOB.notcontained_state)
+ state = GLOB.default_state // If it has a set of controls on the inside, make it actually controllable by the mob in it.
+
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "fersi", name, 375, 650, master_ui, state)
+ ui.open()
+
+/obj/machinery/sleeper/ui_data()
+ var/list/data = list()
+ data["occupied"] = occupant ? 1 : 0
+ data["open"] = state_open
+
+ data["chems"] = list()
+ for(var/chem in reagents.reagent_list)
+ var/datum/reagent/R = GLOB.chemical_reagents_list[chem]
+ data["chems"] += list(list("name" = R.name, "id" = R.id, "allowed" = chem_allowed(chem)))
+
+ data["occupant"] = list()
+ var/mob/living/mob_occupant = occupant
+ if(mob_occupant)
+ data["occupant"]["name"] = mob_occupant.name
+ switch(mob_occupant.stat)
+ if(CONSCIOUS)
+ data["occupant"]["stat"] = "Conscious"
+ data["occupant"]["statstate"] = "good"
+ if(SOFT_CRIT)
+ data["occupant"]["stat"] = "Conscious"
+ data["occupant"]["statstate"] = "average"
+ if(UNCONSCIOUS)
+ data["occupant"]["stat"] = "Unconscious"
+ data["occupant"]["statstate"] = "average"
+ if(DEAD)
+ data["occupant"]["stat"] = "Dead"
+ data["occupant"]["statstate"] = "bad"
+ data["occupant"]["health"] = mob_occupant.health
+ data["occupant"]["maxHealth"] = mob_occupant.maxHealth
+ data["occupant"]["minHealth"] = HEALTH_THRESHOLD_DEAD
+ data["occupant"]["bruteLoss"] = mob_occupant.getBruteLoss()
+ data["occupant"]["oxyLoss"] = mob_occupant.getOxyLoss()
+ data["occupant"]["toxLoss"] = mob_occupant.getToxLoss()
+ data["occupant"]["fireLoss"] = mob_occupant.getFireLoss()
+ data["occupant"]["cloneLoss"] = mob_occupant.getCloneLoss()
+ data["occupant"]["brainLoss"] = mob_occupant.getOrganLoss(ORGAN_SLOT_BRAIN)
+ data["occupant"]["reagents"] = list()
+ if(mob_occupant.reagents && mob_occupant.reagents.reagent_list.len)
+ for(var/datum/reagent/R in mob_occupant.reagents.reagent_list)
+ data["occupant"]["reagents"] += list(list("name" = R.name, "volume" = R.volume))
+ if(mob_occupant.has_dna()) // Blood-stuff is mostly a copy-paste from the healthscanner.
+ var/mob/living/carbon/C = mob_occupant
+ var/blood_id = C.get_blood_id()
+ if(blood_id)
+ data["occupant"]["blood"] = list() // We can start populating this list.
+ var/blood_type = C.dna.blood_type
+ if(blood_id != "blood") // special blood substance
+ var/datum/reagent/R = GLOB.chemical_reagents_list[blood_id]
+ if(R)
+ blood_type = R.name
+ else
+ blood_type = blood_id
+ data["occupant"]["blood"]["maxBloodVolume"] = (BLOOD_VOLUME_NORMAL*C.blood_ratio)
+ data["occupant"]["blood"]["currentBloodVolume"] = C.blood_volume
+ data["occupant"]["blood"]["dangerBloodVolume"] = BLOOD_VOLUME_SAFE
+ data["occupant"]["blood"]["bloodType"] = blood_type
+ return data
+
+/obj/machinery/sleeper/ui_act(action, params)
+ if(..())
+ return
+ var/mob/living/mob_occupant = occupant
+
+ switch(action)
+ if("door")
+ if(state_open)
+ close_machine()
+ else
+ open_machine()
+ . = TRUE
+ if("inject")
+ var/chem = params["chem"]
+ if(!is_operational() || !mob_occupant)
+ return
+ if(mob_occupant.health < min_health && chem != "epinephrine")
+ return
+ if(inject_chem(chem, usr))
+ . = TRUE
+ if(scrambled_chems && prob(5))
+ to_chat(usr, "Chemical system re-route detected, results may not be as expected!")
+
+/obj/machinery/sleeper/emag_act(mob/user)
+ . = ..()
+ scramble_chem_buttons()
+ to_chat(user, "You scramble the sleeper's user interface!")
+ return TRUE
+
+//trans to
+/obj/machinery/sleeper/proc/inject_chem(chem, mob/user)
+ if((chem in available_chems) && chem_allowed(chem))
+ occupant.reagents.add_reagent(chem_buttons[chem], 10) //emag effect kicks in here so that the "intended" chem is used for all checks, for extra FUUU
+ if(user)
+ log_combat(user, occupant, "injected [chem] into", addition = "via [src]")
+ return TRUE
+
+/obj/machinery/sleeper/proc/chem_allowed(chem)
+ var/mob/living/mob_occupant = occupant
+ if(!mob_occupant || !mob_occupant.reagents)
+ return
+ var/amount = mob_occupant.reagents.get_reagent_amount(chem) + 10 <= 20 * efficiency
+ var/occ_health = mob_occupant.health > min_health || chem == "epinephrine"
+ return amount && occ_health
+
+/obj/machinery/sleeper/proc/reset_chem_buttons()
+ scrambled_chems = FALSE
+ LAZYINITLIST(chem_buttons)
+ for(var/chem in available_chems)
+ chem_buttons[chem] = chem
+
+/obj/machinery/sleeper/proc/scramble_chem_buttons()
+ scrambled_chems = TRUE
+ var/list/av_chem = available_chems.Copy()
+ for(var/chem in av_chem)
+ chem_buttons[chem] = pick_n_take(av_chem) //no dupes, allow for random buttons to still be correct
diff --git a/code/modules/reagents/reagent_containers/sleeper_buffer.dm b/code/modules/reagents/reagent_containers/sleeper_buffer.dm
new file mode 100644
index 0000000000..dfbb162aef
--- /dev/null
+++ b/code/modules/reagents/reagent_containers/sleeper_buffer.dm
@@ -0,0 +1,10 @@
+//Created if a sleeper is deconstructed, to contain the reagents within it.
+/obj/item/reagent_containers/sleeper_buffer
+ name = "Sleeper buffer container"
+ desc = "A closed container for insertion in the Medical Sleepers."
+ volume = 1200
+ reagent_flags = OPENCONTAINER
+ spillable = TRUE
+ resistance_flags = ACID_PROOF
+ amount_per_transfer_from_this = 0
+ possible_transfer_amounts = list(0)
diff --git a/tgui/src/interfaces/fersi.ract b/tgui/src/interfaces/fersi.ract
new file mode 100644
index 0000000000..3d50d7e364
--- /dev/null
+++ b/tgui/src/interfaces/fersi.ract
@@ -0,0 +1,57 @@
+
+
+ {{data.occupant.name ? data.occupant.name : "No Occupant"}}
+
+ {{#if data.occupied}}
+
+ {{data.occupant.stat}}
+
+
+ {{Math.round(adata.occupant.health)}}
+
+ {{#each [{label: "Brute", type: "bruteLoss"}, {label: "Respiratory", type: "oxyLoss"}, {label: "Toxin", type: "toxLoss"}, {label: "Burn", type: "fireLoss"}]}}
+
+ {{Math.round(adata.occupant[type])}}
+
+ {{/each}}
+ {{#if data.occupant.blood}}
+
+
+ {{& data.occupant.blood.currentBloodVolume <= data.occupant.blood.dangerBloodVolume ? "LOW" : "OK"}} - {{Math.round(data.occupant.blood.currentBloodVolume)}} cl
+
+
+ {{data.occupant.blood.bloodType}}
+
+
+ {{/if}}
+
+ {{data.occupant.cloneLoss ? "Damaged" : "Healthy"}}
+
+
+ {{data.occupant.brainLoss ? "Abnormal" : "Healthy"}}
+
+
+ {{#each adata.occupant.reagents}}
+ {{Math.fixed(volume, 1)}} units of {{name}}
+ {{else}}
+ Pure
+ {{/each}}
+
+ {{/if}}
+
+
+
+ {{data.open ? "Open" : "Closed"}}
+
+
+ {{#each data.chems}}
+ {{name}}
+ {{if adata.partRating > 3}}
+ 1
+ {{if adata.partRating > 2}}
+ 5
+ 10
+ {{/each}}
+
+