/obj/machinery/sleep_console name = "sleeper console" desc = "A control panel to operate a linked sleeper with." icon = 'icons/obj/Cryogenic2.dmi' icon_state = "sleeperconsole" var/obj/machinery/sleeper/sleeper anchored = TRUE //About time someone fixed this. density = TRUE unacidable = TRUE dir = 8 use_power = USE_POWER_IDLE idle_power_usage = 40 interact_offline = 1 circuit = /obj/item/circuitboard/sleeper_console clicksound = 'sound/machines/buttonbeep.ogg' clickvol = 30 /obj/machinery/sleep_console/Initialize(mapload) findsleeper() return ..() /obj/machinery/sleep_console/Destroy() if(sleeper) sleeper.console = null return ..() /obj/machinery/sleep_console/proc/findsleeper() var/obj/machinery/sleeper/sleepernew = null for(var/direction in GLOB.cardinal) // Loop through every direction sleepernew = locate(/obj/machinery/sleeper, get_step(src, direction)) // Try to find a scanner in that direction if(sleepernew) sleeper = sleepernew sleepernew.console = src break //VOREStation Edit /obj/machinery/sleep_console/attack_ai(var/mob/user) return attack_hand(user) /obj/machinery/sleep_console/attack_hand(var/mob/user) if(..()) return 1 if(!sleeper) findsleeper() if(!sleeper) to_chat(user, span_notice("Sleeper not found!")) return if(panel_open) to_chat(user, span_notice("Close the maintenance panel first.")) return if(sleeper) return tgui_interact(user) /obj/machinery/sleep_console/attackby(var/obj/item/I, var/mob/user) if(computer_deconstruction_screwdriver(user, I)) return else return attack_hand(user) /obj/machinery/sleep_console/power_change() ..() if(stat & (NOPOWER|BROKEN)) icon_state = "sleeperconsole-p" else icon_state = initial(icon_state) /obj/machinery/sleep_console/tgui_interact(mob/user, datum/tgui/ui = null) ui = SStgui.try_update_ui(user, src, ui) if(!ui) ui = new(user, src, "Sleeper", "Sleeper") ui.open() /obj/machinery/sleep_console/tgui_data(mob/user) if(sleeper) return sleeper.tgui_data(user) return null /obj/machinery/sleep_console/tgui_act(action, params, datum/tgui/ui, datum/tgui_state/state) if(sleeper) return sleeper.tgui_act(action, params, ui, state) return ..() /obj/machinery/sleeper name = "sleeper" desc = "A stasis pod with built-in injectors, a dialysis machine, and a limited health scanner." icon = 'icons/obj/Cryogenic2.dmi' icon_state = "sleeper_0" density = TRUE anchored = TRUE unacidable = TRUE flags = REMOTEVIEW_ON_ENTER circuit = /obj/item/circuitboard/sleeper var/mob/living/carbon/human/occupant = null var/list/available_chemicals = list() var/list/base_chemicals = list(REAGENT_ID_INAPROVALINE = REAGENT_INAPROVALINE, REAGENT_ID_PARACETAMOL = REAGENT_PARACETAMOL, REAGENT_ID_ANTITOXIN = REAGENT_ANTITOXIN, REAGENT_ID_DEXALIN = REAGENT_DEXALIN) var/amounts = list(5, 10) var/obj/item/reagent_containers/glass/beaker = null var/filtering = 0 var/pumping = 0 // Currently never changes. On Paradise, max_chem and min_health are based on the matter bins in the sleeper. var/max_chem = 20 var/initial_bin_rating = 1 var/min_health = -101 var/obj/machinery/sleep_console/console var/stasis_level = 0 //Every 'this' life ticks are applied to the mob (when life_ticks%stasis_level == 1) var/stasis_choices = list("Complete (1%)" = 100, "Deep (10%)" = 10, "Moderate (20%)" = 5, "Light (50%)" = 2, "None (100%)" = 0) var/controls_inside = FALSE var/auto_eject_dead = FALSE use_power = USE_POWER_IDLE idle_power_usage = 15 active_power_usage = 200 //builtin health analyzer, dialysis machine, injectors. /obj/machinery/sleeper/Initialize(mapload) . = ..() beaker = new /obj/item/reagent_containers/glass/beaker/large(src) default_apply_parts() update_icon() /obj/machinery/sleeper/Destroy() if(console) console.sleeper = null return ..() /obj/machinery/sleeper/RefreshParts(var/limited = 0) var/man_rating = 0 var/cap_rating = 0 available_chemicals.Cut() available_chemicals = base_chemicals.Copy() for(var/obj/item/stock_parts/P in component_parts) if(istype(P, /obj/item/stock_parts/capacitor)) cap_rating += P.rating cap_rating = max(1, round(cap_rating / 2)) update_idle_power_usage(initial(idle_power_usage) / cap_rating) update_active_power_usage(initial(active_power_usage) / cap_rating) if(!limited) for(var/obj/item/stock_parts/P in component_parts) if(istype(P, /obj/item/stock_parts/manipulator)) man_rating += P.rating - 1 var/list/new_chemicals = list() if(man_rating >= 4) // Alien tech. var/reag_ID = pickweight(list( REAGENT_ID_HEALINGNANITES = 10, REAGENT_ID_SHREDDINGNANITES = 5, REAGENT_ID_IRRADIATEDNANITES = 5, REAGENT_ID_NEUROPHAGENANITES = 2) ) new_chemicals[reag_ID] = "Nanite" if(man_rating >= 3) // Anomalous tech. new_chemicals[REAGENT_ID_IMMUNOSUPRIZINE] = REAGENT_IMMUNOSUPRIZINE if(man_rating >= 2) // Tier 3. new_chemicals[REAGENT_ID_SPACEACILLIN] = REAGENT_SPACEACILLIN if(man_rating >= 1) // Tier 2. new_chemicals[REAGENT_ID_LEPORAZINE] = REAGENT_LEPORAZINE if(new_chemicals.len) available_chemicals += new_chemicals return /obj/machinery/sleeper/attack_hand(var/mob/user) if(!controls_inside) return FALSE if(user == occupant) tgui_interact(user) /obj/machinery/sleeper/tgui_interact(mob/user, datum/tgui/ui = null) ui = SStgui.try_update_ui(user, src, ui) if(!ui) ui = new(user, src, "Sleeper", "Sleeper") ui.open() /obj/machinery/sleeper/tgui_data(mob/user) var/data[0] data["amounts"] = amounts data["hasOccupant"] = occupant ? 1 : 0 var/occupantData[0] // var/crisis = 0 if(occupant) occupantData["name"] = occupant.name occupantData["stat"] = occupant.stat occupantData["health"] = occupant.health occupantData["maxHealth"] = occupant.getMaxHealth() occupantData["minHealth"] = -(occupant.getMaxHealth()) occupantData["bruteLoss"] = occupant.getBruteLoss() occupantData["oxyLoss"] = occupant.getOxyLoss() occupantData["toxLoss"] = occupant.getToxLoss() occupantData["fireLoss"] = occupant.getFireLoss() occupantData["paralysis"] = occupant.paralysis occupantData["hasBlood"] = 0 occupantData["bodyTemperature"] = occupant.bodytemperature occupantData["maxTemp"] = 1000 // If you get a burning vox armalis into the sleeper, congratulations // Because we can put simple_animals in here, we need to do something tricky to get things working nice occupantData["temperatureSuitability"] = 0 // 0 is the baseline if(ishuman(occupant) && occupant.species) // I wanna do something where the bar gets bluer as the temperature gets lower // For now, I'll just use the standard format for the temperature status var/datum/species/sp = occupant.species if(occupant.bodytemperature < sp.cold_level_3) occupantData["temperatureSuitability"] = -3 else if(occupant.bodytemperature < sp.cold_level_2) occupantData["temperatureSuitability"] = -2 else if(occupant.bodytemperature < sp.cold_level_1) occupantData["temperatureSuitability"] = -1 else if(occupant.bodytemperature > sp.heat_level_3) occupantData["temperatureSuitability"] = 3 else if(occupant.bodytemperature > sp.heat_level_2) occupantData["temperatureSuitability"] = 2 else if(occupant.bodytemperature > sp.heat_level_1) occupantData["temperatureSuitability"] = 1 else if(isanimal(occupant)) var/mob/living/simple_mob/silly = occupant if(silly.bodytemperature < silly.minbodytemp) occupantData["temperatureSuitability"] = -3 else if(silly.bodytemperature > silly.maxbodytemp) occupantData["temperatureSuitability"] = 3 // Blast you, imperial measurement system occupantData["btCelsius"] = occupant.bodytemperature - T0C occupantData["btFaren"] = ((occupant.bodytemperature - T0C) * (9.0/5.0))+ 32 // crisis = (occupant.health < min_health) // I'm not sure WHY you'd want to put a simple_animal in a sleeper, but precedent is precedent // Runtime is aptly named, isn't she? if(ishuman(occupant) && !(NO_BLOOD in occupant.species.flags) && occupant.vessel) occupantData["pulse"] = occupant.get_pulse(GETPULSE_TOOL) occupantData["hasBlood"] = 1 var/blood_volume = round(occupant.vessel.get_reagent_amount(REAGENT_ID_BLOOD)) occupantData["bloodLevel"] = blood_volume occupantData["bloodMax"] = occupant.species.blood_volume occupantData["bloodPercent"] = round(100*(blood_volume/occupant.species.blood_volume), 0.01) //copy pasta ends here occupantData["bloodType"] = occupant.dna.b_type data["occupant"] = occupantData data["maxchem"] = max_chem data["minhealth"] = min_health data["dialysis"] = filtering data["stomachpumping"] = pumping data["auto_eject_dead"] = auto_eject_dead if(beaker) data["isBeakerLoaded"] = 1 if(beaker.reagents) data["beakerMaxSpace"] = beaker.reagents.maximum_volume data["beakerFreeSpace"] = beaker.reagents.get_free_space() else data["beakerMaxSpace"] = 0 data["beakerFreeSpace"] = 0 else data["isBeakerLoaded"] = FALSE var/stasis_level_name = "Error!" for(var/N in stasis_choices) if(stasis_choices[N] == stasis_level) stasis_level_name = N break data["stasis"] = stasis_level_name var/chemicals[0] for(var/re in available_chemicals) var/datum/reagent/temp = SSchemistry.chemical_reagents[re] if(temp) var/reagent_amount = 0 var/pretty_amount var/injectable = occupant ? 1 : 0 var/overdosing = 0 var/caution = 0 // To make things clear that you're coming close to an overdose // if(crisis && !(temp.id in emergency_chems)) // injectable = 0 if(occupant && occupant.reagents) reagent_amount = occupant.reagents.get_reagent_amount(temp.id) // If they're mashing the highest concentration, they get one warning if(temp.overdose && reagent_amount + 10 > (temp.overdose * occupant?.species.chemOD_threshold)) caution = 1 if(temp.overdose && reagent_amount > (temp.overdose * occupant?.species.chemOD_threshold)) overdosing = 1 pretty_amount = round(reagent_amount, 0.05) chemicals.Add(list(list("title" = temp.name, "id" = temp.id, "commands" = list("chemical" = temp.id), "occ_amount" = reagent_amount, "pretty_amount" = pretty_amount, "injectable" = injectable, "overdosing" = overdosing, "od_warning" = caution))) data["chemicals"] = chemicals return data /obj/machinery/sleeper/tgui_act(action, params, datum/tgui/ui, datum/tgui_state/state) if(..()) return TRUE if(!controls_inside && ui.user == occupant) return if(panel_open) to_chat(ui.user, span_notice("Close the maintenance panel first.")) return . = TRUE switch(action) if("chemical") if(!occupant) return if(occupant.stat == DEAD) to_chat(ui.user, span_danger("This person has no life to preserve anymore. Take [occupant.p_them()] to a department capable of reanimating [occupant.p_them()].")) return var/chemical = params["chemid"] var/amount = text2num(params["amount"]) if(!length(chemical) || amount <= 0) return if(occupant.health > min_health) //|| (chemical in emergency_chems)) inject_chemical(ui.user, chemical, amount) else to_chat(ui.user, span_danger("This person is not in good enough condition for sleepers to be effective! Use another means of treatment, such as cryogenics!")) if("removebeaker") remove_beaker() if("togglefilter") toggle_filter() if("togglepump") toggle_pump() if("ejectify") go_out() if("changestasis") var/new_stasis = tgui_input_list(ui.user, "Levels deeper than 50% stasis level will render the patient unconscious.","Stasis Level", stasis_choices) if(new_stasis) stasis_level = stasis_choices[new_stasis] if("auto_eject_dead_on") auto_eject_dead = TRUE if("auto_eject_dead_off") auto_eject_dead = FALSE else return FALSE add_fingerprint(ui.user) /obj/machinery/sleeper/process() if(stat & (NOPOWER|BROKEN)) return if(occupant) if(auto_eject_dead && occupant.stat == DEAD) playsound(loc, 'sound/machines/buzz-sigh.ogg', 40) go_out() return occupant.Stasis(stasis_level) if(filtering > 0) if(beaker) if(beaker.reagents.total_volume < beaker.reagents.maximum_volume) var/pumped = 0 for(var/datum/reagent/x in occupant.reagents.reagent_list) occupant.reagents.trans_to_obj(beaker, 3) pumped++ if(ishuman(occupant)) occupant.vessel.trans_to_obj(beaker, pumped + 1) else toggle_filter() if(pumping > 0) if(beaker) if(beaker.reagents.total_volume < beaker.reagents.maximum_volume) for(var/datum/reagent/x in occupant.ingested.reagent_list) occupant.ingested.trans_to_obj(beaker, 3) else toggle_pump() /obj/machinery/sleeper/update_icon() icon_state = "sleeper_[occupant ? "1" : "0"]" /obj/machinery/sleeper/attackby(var/obj/item/I, var/mob/user) add_fingerprint(user) if(istype(I, /obj/item/grab)) var/obj/item/grab/G = I if(G.affecting) go_in(G.affecting, user) return if(istype(I, /obj/item/reagent_containers/glass)) if(!beaker) beaker = I user.drop_item() I.forceMove(src) user.visible_message(span_infoplain(span_bold("\The [user]") + " adds \a [I] to \the [src]."), span_notice("You add \a [I] to \the [src].")) else to_chat(user, span_warning("\The [src] has a beaker already.")) return if(!occupant) if(default_deconstruction_screwdriver(user, I)) return if(default_deconstruction_crowbar(user, I)) return if(default_part_replacement(user, I)) return /obj/machinery/sleeper/verb/move_eject() set name = "Eject occupant" set category = "Object" set src in oview(1) if(usr == occupant) switch(usr.stat) if(DEAD) return if(UNCONSCIOUS) to_chat(usr, span_notice("You struggle through the haze to hit the eject button. This will take a couple of minutes...")) if(do_after(usr, 2 MINUTES, target = src)) go_out() if(CONSCIOUS) go_out() else if(usr.stat != CONSCIOUS) return go_out() add_fingerprint(usr) /obj/machinery/sleeper/MouseDrop_T(var/mob/target, var/mob/user) if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user) || !ishuman(target)) return go_in(target, user) /obj/machinery/sleeper/relaymove(var/mob/user) ..() if(user.incapacitated()) return go_out() /obj/machinery/sleeper/emp_act(severity, recursive) if(filtering) toggle_filter() if(pumping) toggle_pump() if(stat & (BROKEN|NOPOWER)) ..(severity, recursive) return if(occupant) go_out() ..(severity, recursive) /obj/machinery/sleeper/proc/toggle_filter() if(!occupant || !beaker) filtering = 0 return filtering = !filtering /obj/machinery/sleeper/proc/toggle_pump() if(!occupant || !beaker) pumping = 0 return pumping = !pumping /obj/machinery/sleeper/proc/go_in(var/mob/M, var/mob/user) if(!M) return if(stat & (BROKEN|NOPOWER)) return if(M.buckled) return if(occupant) to_chat(user, span_warning("\The [src] is already occupied.")) return if(!ishuman(M)) to_chat(user, span_warning("\The [src] is not designed for that organism!")) return if(M == user) visible_message("\The [user] starts climbing into \the [src].") else visible_message("\The [user] starts putting [M] into \the [src].") if(do_after(user, 2 SECONDS, target = src)) if(M.buckled) return if(occupant) to_chat(user, span_warning("\The [src] is already occupied.")) return M.stop_pulling() M.forceMove(src) update_use_power(USE_POWER_ACTIVE) occupant = M occupant.cozyloop.start() // CHOMPStation Add: Cozy Music update_icon() /obj/machinery/sleeper/proc/go_out() if(!occupant || occupant.loc != src) occupant.cozyloop.stop() // CHOMPStation Add: Cozy Music occupant = null // JUST IN CASE return occupant.Stasis(0) occupant.forceMove(get_turf(src)) occupant.cozyloop.stop() // CHOMPStation Add: Cozy Music occupant = null for(var/atom/movable/A in src) // In case an object was dropped inside or something if(A == beaker || A == circuit) continue if(A in component_parts) continue A.forceMove(get_turf(src)) update_use_power(USE_POWER_IDLE) update_icon() toggle_filter() toggle_pump() /obj/machinery/sleeper/proc/remove_beaker() if(beaker) beaker.forceMove(get_turf(src)) beaker = null toggle_filter() /obj/machinery/sleeper/proc/inject_chemical(var/mob/living/user, var/chemical, var/amount) if(stat & (BROKEN|NOPOWER)) return if(!(amount in amounts)) return if(occupant && occupant.reagents) if(occupant.reagents.get_reagent_amount(chemical) + amount <= max_chem) use_power(amount * CHEM_SYNTH_ENERGY) occupant.reagents.add_reagent(chemical, amount) to_chat(user, "Occupant now has [occupant.reagents.get_reagent_amount(chemical)] units of [available_chemicals[chemical]] in their bloodstream.") else to_chat(user, "The subject has too many chemicals in their bloodstream.") else to_chat(user, "There's no suitable occupant in \the [src].") //Survival/Stasis sleepers /obj/machinery/sleeper/survival_pod desc = "A limited functionality sleeper, all it can do is put patients into stasis. It lacks the medication and configuration of the larger units." icon_state = "sleeper" stasis_level = 100 //Just one setting /obj/machinery/sleeper/survival_pod/Initialize(mapload) . = ..() RefreshParts(1)