diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index f69f5f6b2ae..f54d069b9d4 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -68,10 +68,12 @@ anchored = 1 circuit = /obj/item/weapon/circuitboard/sleeper var/mob/living/carbon/human/occupant = null - var/list/available_chemicals = list("inaprovaline" = "Inaprovaline", "stoxin" = "Soporific", "paracetamol" = "Paracetamol", "anti_toxin" = "Dylovene", "dexalin" = "Dexalin") + var/list/available_chemicals = list("inaprovaline" = "Inaprovaline", "paracetamol" = "Paracetamol", "anti_toxin" = "Dylovene", "dexalin" = "Dexalin") var/obj/item/weapon/reagent_containers/glass/beaker = null var/filtering = 0 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) use_power = 1 idle_power_usage = 15 @@ -98,18 +100,23 @@ /obj/machinery/sleeper/process() if(stat & (NOPOWER|BROKEN)) return + if(occupant) + occupant.Stasis(stasis_level) + if(stasis_level >= 100 && occupant.timeofdeath) + occupant.timeofdeath += 1 SECOND + + 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(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() /obj/machinery/sleeper/update_icon() icon_state = "sleeper_[occupant ? "1" : "0"]" @@ -154,6 +161,13 @@ data["beaker"] = -1 data["filtering"] = filtering + 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 + ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) if(!ui) ui = new(user, src, ui_key, "sleeper.tmpl", "Sleeper UI", 600, 600, state = state) @@ -182,12 +196,20 @@ if(occupant && occupant.stat != DEAD) if(href_list["chemical"] in available_chemicals) // Your hacks are bad and you should feel bad inject_chemical(usr, href_list["chemical"], text2num(href_list["amount"])) + if(href_list["change_stasis"]) + var/new_stasis = input("Levels deeper than 50% stasis level will render the patient unconscious.","Stasis Level") as null|anything in stasis_choices + if(new_stasis && CanUseTopic(usr, default_state) == STATUS_INTERACTIVE) + stasis_level = stasis_choices[new_stasis] return 1 /obj/machinery/sleeper/attackby(var/obj/item/I, var/mob/user) add_fingerprint(user) - if(default_deconstruction_screwdriver(user, I)) + if(istype(I, /obj/item/weapon/grab)) + var/obj/item/weapon/grab/G = I + if(G.affecting) + go_in(G.affecting, user) + else if(default_deconstruction_screwdriver(user, I)) return else if(default_deconstruction_crowbar(user, I)) return @@ -201,6 +223,28 @@ user << "\The [src] has a beaker already." 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) + usr << "You struggle through the haze to hit the eject button. This will take a couple of minutes..." + sleep(2 MINUTES) + if(!src || !usr || !occupant || (occupant != usr)) //Check if someone's released/replaced/bombed him already + return + go_out() + if(CONSCIOUS) + go_out() + else + if(usr.stat != 0) + 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 @@ -261,6 +305,7 @@ if(occupant.client) occupant.client.eye = occupant.client.mob occupant.client.perspective = MOB_PERSPECTIVE + occupant.Stasis(0) occupant.loc = src.loc occupant = null for(var/atom/movable/A in src) // In case an object was dropped inside or something diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index 6441a5e72ae..672a6a425b7 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -534,7 +534,7 @@ time_entered = world.time if(ishuman(M) && applies_stasis) var/mob/living/carbon/human/H = M - H.in_stasis = 1 + H.Stasis(1000) // Book keeping! var/turf/location = get_turf(src) @@ -602,7 +602,7 @@ set_occupant(usr) if(ishuman(usr) && applies_stasis) var/mob/living/carbon/human/H = occupant - H.in_stasis = 1 + H.Stasis(1000) icon_state = occupied_icon_state @@ -638,7 +638,7 @@ occupant.forceMove(get_turf(src)) if(ishuman(occupant) && applies_stasis) var/mob/living/carbon/human/H = occupant - H.in_stasis = 0 + H.Stasis(0) set_occupant(null) icon_state = base_icon_state diff --git a/code/game/mecha/equipment/tools/medical_tools.dm b/code/game/mecha/equipment/tools/medical_tools.dm index ffe30bf3fd8..1e10e03f4f8 100644 --- a/code/game/mecha/equipment/tools/medical_tools.dm +++ b/code/game/mecha/equipment/tools/medical_tools.dm @@ -7,7 +7,7 @@ energy_drain = 20 range = MELEE equip_cooldown = 50 - var/mob/living/carbon/occupant = null + var/mob/living/carbon/human/occupant = null var/datum/global_iterator/pr_mech_sleeper var/inject_amount = 10 required_type = /obj/mecha/medical @@ -28,7 +28,7 @@ Exit(atom/movable/O) return 0 - action(var/mob/living/carbon/target) + action(var/mob/living/carbon/human/target) if(!action_checks(target)) return if(!istype(target)) @@ -56,6 +56,7 @@ target.forceMove(src) occupant = target target.reset_view(src) + occupant.Stasis(3) /* if(target.client) target.client.perspective = EYE_PERSPECTIVE @@ -80,6 +81,7 @@ occupant.client.eye = occupant.client.mob occupant.client.perspective = MOB_PERSPECTIVE */ + occupant.Stasis(0) occupant = null pr_mech_sleeper.stop() set_ready_state(1) diff --git a/code/game/objects/items/bodybag.dm b/code/game/objects/items/bodybag.dm index b4ba67e4c29..4d77da4ae93 100644 --- a/code/game/objects/items/bodybag.dm +++ b/code/game/objects/items/bodybag.dm @@ -77,14 +77,14 @@ /obj/structure/closet/body_bag/MouseDrop(over_object, src_location, over_location) ..() if((over_object == usr && (in_range(src, usr) || usr.contents.Find(src)))) - if(!ishuman(usr)) return + if(!ishuman(usr)) return 0 if(opened) return 0 if(contents.len) return 0 visible_message("[usr] folds up the [src.name]") - new item_path(get_turf(src)) + var/folded = new item_path(get_turf(src)) spawn(0) qdel(src) - return + return folded /obj/structure/closet/body_bag/relaymove(mob/user,direction) if(src.loc != get_turf(src)) @@ -115,34 +115,43 @@ /obj/item/bodybag/cryobag name = "stasis bag" - desc = "A folded, non-reusable bag designed to prevent additional damage to an occupant, especially useful if short on time or in \ - a hostile enviroment." + desc = "A non-reusable plastic bag designed to slow down bodily functions such as circulation and breathing, \ + especially useful if short on time or in a hostile enviroment." icon = 'icons/obj/cryobag.dmi' icon_state = "bodybag_folded" item_state = "bodybag_cryo_folded" origin_tech = list(TECH_BIO = 4) + var/obj/item/weapon/reagent_containers/syringe/syringe /obj/item/bodybag/cryobag/attack_self(mob/user) var/obj/structure/closet/body_bag/cryobag/R = new /obj/structure/closet/body_bag/cryobag(user.loc) R.add_fingerprint(user) + if(syringe) + R.syringe = syringe + syringe = null qdel(src) /obj/structure/closet/body_bag/cryobag name = "stasis bag" - desc = "A non-reusable plastic bag designed to prevent additional damage to an occupant, especially useful if short on time or in \ - a hostile enviroment." + desc = "A non-reusable plastic bag designed to slow down bodily functions such as circulation and breathing, \ + especially useful if short on time or in a hostile enviroment." icon = 'icons/obj/cryobag.dmi' item_path = /obj/item/bodybag/cryobag store_misc = 0 store_items = 0 var/used = 0 var/obj/item/weapon/tank/tank = null + var/stasis_level = 3 //Every 'this' life ticks are applied to the mob (when life_ticks%stasis_level == 1) + var/obj/item/weapon/reagent_containers/syringe/syringe /obj/structure/closet/body_bag/cryobag/New() tank = new /obj/item/weapon/tank/emergency/oxygen(null) //It's in nullspace to prevent ejection when the bag is opened. ..() /obj/structure/closet/body_bag/cryobag/Destroy() + if(syringe) + qdel(syringe) + syringe = null qdel(tank) tank = null ..() @@ -157,11 +166,19 @@ O.desc = "Pretty useless now.." qdel(src) +/obj/structure/closet/body_bag/cryobag/MouseDrop(over_object, src_location, over_location) + . = ..() + if(. && syringe) + var/obj/item/bodybag/cryobag/folded = . + folded.syringe = syringe + syringe = null + /obj/structure/closet/body_bag/cryobag/Entered(atom/movable/AM) if(ishuman(AM)) var/mob/living/carbon/human/H = AM - H.in_stasis = 1 + H.Stasis(stasis_level) src.used = 1 + inject_occupant(H) if(istype(AM, /obj/item/organ)) var/obj/item/organ/O = AM @@ -173,7 +190,7 @@ /obj/structure/closet/body_bag/cryobag/Exited(atom/movable/AM) if(ishuman(AM)) var/mob/living/carbon/human/H = AM - H.in_stasis = 0 + H.Stasis(0) if(istype(AM, /obj/item/organ)) var/obj/item/organ/O = AM @@ -187,10 +204,19 @@ return tank.air_contents ..() +/obj/structure/closet/body_bag/cryobag/proc/inject_occupant(var/mob/living/carbon/human/H) + if(!syringe) + return + + if(H.reagents) + syringe.reagents.trans_to_mob(H, 30, CHEM_BLOOD) + /obj/structure/closet/body_bag/cryobag/examine(mob/user) ..() if(Adjacent(user)) //The bag's rather thick and opaque from a distance. user << "You peer into \the [src]." + if(syringe) + user << "It has a syringe added to it." for(var/mob/living/L in contents) L.examine(user) @@ -202,5 +228,28 @@ var/obj/item/device/healthanalyzer/analyzer = W for(var/mob/living/L in contents) analyzer.attack(L,user) + + else if(istype(W,/obj/item/weapon/reagent_containers/syringe)) + if(syringe) + to_chat(user,"\The [src] already has an injector! Remove it first.") + else + var/obj/item/weapon/reagent_containers/syringe/syringe = W + to_chat(user,"You insert \the [syringe] into \the [src], and it locks into place.") + user.unEquip(syringe) + src.syringe = syringe + syringe.loc = null + for(var/mob/living/carbon/human/H in contents) + inject_occupant(H) + break + + else if(istype(W,/obj/item/weapon/screwdriver)) + if(syringe) + if(used) + to_chat(user,"The injector cannot be removed now that the stasis bag has been used!") + else + syringe.forceMove(src.loc) + to_chat(user,"You pry \the [syringe] out of \the [src].") + syringe = null + else ..() diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm index 02d85bbcfaf..30ee7c64d6e 100644 --- a/code/modules/mob/living/carbon/human/human_damage.dm +++ b/code/modules/mob/living/carbon/human/human_damage.dm @@ -185,6 +185,26 @@ wearing_rig.notify_ai("Warning: user consciousness failure. Mobility control passed to integrated intelligence system.") ..() +/mob/living/carbon/human/proc/Stasis(amount) + if((species.flags & NO_SCAN) || isSynthetic()) + in_stasis = 0 + else + in_stasis = amount + +/mob/living/carbon/human/proc/getStasis() + if((species.flags & NO_SCAN) || isSynthetic()) + return 0 + + return in_stasis + +//This determines if, RIGHT NOW, the life() tick is being skipped due to stasis +/mob/living/carbon/human/proc/inStasisNow() + var/stasisValue = getStasis() + if(stasisValue && (life_tick % stasisValue)) + return 1 + + return 0 + /mob/living/carbon/human/getCloneLoss() if((species.flags & NO_SCAN) || isSynthetic()) cloneloss = 0 diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 7ce62e1ae9a..2738686c38a 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -62,8 +62,12 @@ voice = GetVoice() + var/stasis = inStasisNow() + if(getStasis() > 2) + Sleeping(20) + //No need to update all of these procs if the guy is dead. - if(stat != DEAD && !in_stasis) + if(stat != DEAD && !stasis) //Updates the number of stored chemicals for powers handle_changeling() @@ -82,7 +86,6 @@ if(!client) species.handle_npc(src) - if(!handle_some_updates()) return //We go ahead and process them 5 times for HUD images and other stuff though. @@ -97,7 +100,7 @@ return 1 /mob/living/carbon/human/breathe() - if(!in_stasis) + if(!inStasisNow()) ..() // Calculate how vulnerable the human is to under- and overpressure. @@ -207,7 +210,7 @@ /mob/living/carbon/human/handle_mutations_and_radiation() - if(in_stasis) + if(inStasisNow()) return if(getFireLoss()) @@ -789,7 +792,7 @@ /mob/living/carbon/human/handle_chemicals_in_body() - if(in_stasis) + if(inStasisNow()) return if(reagents) @@ -1339,7 +1342,7 @@ if(!druggy && !seer) see_invisible = SEE_INVISIBLE_LIVING /mob/living/carbon/human/handle_random_events() - if(in_stasis) + if(inStasisNow()) return // Puke if toxloss is too high diff --git a/code/modules/organs/blood.dm b/code/modules/organs/blood.dm index 0f5ea190f26..c6670d2a30e 100644 --- a/code/modules/organs/blood.dm +++ b/code/modules/organs/blood.dm @@ -37,7 +37,7 @@ var/const/CE_STABLE_THRESHOLD = 0.5 // Takes care blood loss and regeneration /mob/living/carbon/human/handle_blood() - if(in_stasis) + if(inStasisNow()) return if(!should_have_organ(O_HEART)) diff --git a/html/changelogs/Arokha - Sleepers.yml b/html/changelogs/Arokha - Sleepers.yml new file mode 100644 index 00000000000..717b60f12fb --- /dev/null +++ b/html/changelogs/Arokha - Sleepers.yml @@ -0,0 +1,39 @@ +################################ +# Example Changelog File +# +# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. +# +# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) +# When it is, any changes listed below will disappear. +# +# Valid Prefixes: +# bugfix +# wip (For works in progress) +# tweak +# soundadd +# sounddel +# rscadd (general adding of nice things) +# rscdel (general deleting of nice things) +# imageadd +# imagedel +# maptweak +# spellcheck (typo fixes) +# experiment +################################# + +# Your name. +author: Arokha + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. +# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. +changes: + - rscadd: "Sleepers now have a 'stasis' level setting, that will ignore varying numbers of life() ticks on the patient." + - tweak: "Stasis bags and Ody sleepers now use a fixed level of this new stasis system (ignore 2/3 life ticks)." + - tweak: "You can escape from being asleep in a sleeper, similar to escaping from a cryotube." + - rscadd: "You can now use grabs on sleepers to insert patients, same as scanners." diff --git a/nano/templates/sleeper.tmpl b/nano/templates/sleeper.tmpl index 49fb0ae8d75..7ee370e890f 100644 --- a/nano/templates/sleeper.tmpl +++ b/nano/templates/sleeper.tmpl @@ -88,4 +88,12 @@ {{/if}} +