diff --git a/code/game/objects/structures/medical_stand_vr.dm b/code/game/objects/structures/medical_stand_vr.dm new file mode 100644 index 00000000000..600113e1835 --- /dev/null +++ b/code/game/objects/structures/medical_stand_vr.dm @@ -0,0 +1,452 @@ +/obj/structure/medical_stand + name = "medical stand" + icon = 'icons/obj/medical_stand_vr.dmi' + desc = "Medical stand used to hang reagents for transfusion and to hold anesthetic tank." + icon_state = "medical_stand_empty" + + //gas stuff + var/obj/item/weapon/tank/tank + var/mob/living/carbon/human/breather + var/obj/screen/internalsHud + var/obj/item/clothing/mask/breath/contained + + var/spawn_type = null + var/mask_type = /obj/item/clothing/mask/breath/medical + + var/is_loosen = TRUE + var/valve_opened = FALSE + //blood stuff + var/mob/living/carbon/attached + var/mode = 1 // 1 is injecting, 0 is taking blood. + var/obj/item/weapon/reagent_containers/beaker + var/list/transfer_amounts = list(REM, 1, 2) + var/transfer_amount = 1 + +/obj/structure/medical_stand/New() + ..() + if (spawn_type) + tank = new spawn_type (src) + contained = new mask_type (src) + update_icon() + +/obj/structure/medical_stand/update_icon() + overlays.Cut() + + if (tank) + if (breather) + overlays += "tube_active" + else + overlays += "tube" + if(istype(tank,/obj/item/weapon/tank/anesthetic)) + overlays += "tank_anest" + else if(istype(tank,/obj/item/weapon/tank/nitrogen)) + overlays += "tank_nitro" + else if(istype(tank,/obj/item/weapon/tank/oxygen)) + overlays += "tank_oxyg" + else if(istype(tank,/obj/item/weapon/tank/phoron)) + overlays += "tank_plasma" + //else if(istype(tank,/obj/item/weapon/tank/hydrogen)) + // overlays += "tank_hydro" + else + overlays += "tank_other" + + if(beaker) + overlays += "beaker" + if(attached) + overlays += "line_active" + else + overlays += "line" + var/datum/reagents/reagents = beaker.reagents + var/percent = round((reagents.total_volume / beaker.volume) * 100) + if(reagents.total_volume) + var/image/filling = image('icons/obj/medical_stand_vr.dmi', src, "reagent") + + switch(percent) + if(10 to 24) filling.icon_state = "reagent10" + if(25 to 49) filling.icon_state = "reagent25" + if(50 to 74) filling.icon_state = "reagent50" + if(75 to 79) filling.icon_state = "reagent75" + if(80 to 90) filling.icon_state = "reagent80" + if(91 to INFINITY) filling.icon_state = "reagent100" + if (filling.icon) + filling.icon += reagents.get_color() + overlays += filling + +/obj/structure/medical_stand/Destroy() + STOP_PROCESSING(SSobj,src) + if(breather) + breather.internal = null + if(internalsHud) + internalsHud.icon_state = "internal0" + if(tank) + qdel(tank) + if(breather) + breather.remove_from_mob(contained) + src.visible_message("The mask rapidly retracts just before /the [src] is destroyed!") + qdel(contained) + contained = null + breather = null + + attached = null + qdel(beaker) + beaker = null + return ..() + +/obj/structure/medical_stand/attack_robot(var/mob/user) + if(Adjacent(user)) + attack_hand(user) + +/obj/structure/medical_stand/MouseDrop(var/mob/living/carbon/human/target, src_location, over_location) + ..() + if(istype(target)) + if(usr.stat == DEAD || !CanMouseDrop(target)) + return + var/list/available_options = list() + if (tank) + available_options += "Gas mask" + if (beaker) + available_options += "Drip needle" + + var/action_type + if(available_options.len > 1) + action_type = input(usr, "What do you want to attach/detach?") as null|anything in available_options + else if(available_options.len) + action_type = available_options[1] + if(usr.stat == DEAD || !CanMouseDrop(target)) + return + switch (action_type) + if("Gas mask") + if(!can_apply_to_target(target, usr)) // There is no point in attempting to apply a mask if it's impossible. + return + if (breather) + src.add_fingerprint(usr) + if(!do_mob(usr, target, 30) || !can_apply_to_target(target, usr)) + return + if(tank) + tank.forceMove(src) + if (breather.wear_mask == contained) + breather.remove_from_mob(contained) + contained.forceMove(src) + else + qdel(contained) + contained = new mask_type(src) + breather = null + src.visible_message("\The [contained] slips to \the [src]!") + update_icon() + return + usr.visible_message("\The [usr] begins carefully placing the mask onto [target].", + "You begin carefully placing the mask onto [target].") + if(!do_mob(usr, target, 100) || !can_apply_to_target(target, usr)) + return + // place mask and add fingerprints + usr.visible_message("\The [usr] has placed \the mask on [target]'s mouth.", + "You have placed \the mask on [target]'s mouth.") + if(attach_mask(target)) + src.add_fingerprint(usr) + update_icon() + START_PROCESSING(SSobj,src) + return + if("Drip needle") + if(attached) + if(!do_mob(usr, target, 20)) + return + visible_message("\The [attached] is taken off \the [src]") + attached = null + else if(ishuman(target)) + usr.visible_message("\The [usr] begins inserting needle into [target]'s vein.", + "You begin inserting needle into [target]'s vein.") + if(!do_mob(usr, target, 50)) + usr.visible_message("\The [usr]'s hand slips and pricks \the [target].", + "Your hand slips and pricks \the [target].") + target.apply_damage(3, BRUTE, pick(BP_R_ARM, BP_L_ARM)) + return + usr.visible_message("\The [usr] hooks \the [target] up to \the [src].", + "You hook \the [target] up to \the [src].") + attached = target + START_PROCESSING(SSobj,src) + update_icon() + + +/obj/structure/medical_stand/attack_hand(mob/user as mob) + var/list/available_options = list() + if (tank) + available_options += "Toggle valve" + available_options += "Remove tank" + if (beaker) + available_options += "Remove vessel" + + var/action_type + if(available_options.len > 1) + action_type = input(user, "What do you want to do?") as null|anything in available_options + else if(available_options.len) + action_type = available_options[1] + switch (action_type) + if ("Remove tank") + if (!tank) + to_chat(user, "There is no tank in \the [src]!") + return + else if (tank && is_loosen) + user.visible_message("\The [user] removes \the [tank] from \the [src].", "You remove \the [tank] from \the [src].>") + user.put_in_hands(tank) + tank = null + update_icon() + return + else if (!is_loosen) + user.visible_message("\The [user] tries to removes \the [tank] from \the [src] but it won't budge.", "You try to removes \the [tank] from \the [src] but it won't budge.>") + return + if ("Toggle valve") + if (!tank) + to_chat(user, "There is no tank in \the [src]!") + return + else + if (valve_opened) + src.visible_message("\The [user] closes valve on \the [src]!", + "You close valve on \the [src].") + if(breather) + if(internalsHud) + internalsHud.icon_state = "internal0" + breather.internal = null + valve_opened = FALSE + update_icon() + else + src.visible_message("\The [user] opens valve on \the [src]!", + "You open valve on \the [src].") + if(breather) + breather.internal = tank + if(internalsHud) + internalsHud.icon_state = "internal1" + valve_opened = TRUE + //playsound(get_turf(src), 'sound/effects/internals.ogg', 100, 1) + update_icon() + START_PROCESSING(SSobj,src) + if ("Remove vessel") + if(beaker) + beaker.forceMove(loc) + beaker = null + update_icon() + +/obj/structure/medical_stand/verb/toggle_mode() + set category = "Object" + set name = "Toggle IV Mode" + set src in view(1) + + if(!istype(usr, /mob/living)) + to_chat(usr, "You can't do that.") + return + + if(usr.incapacitated()) + return + + mode = !mode + to_chat(usr, "The IV drip is now [mode ? "injecting" : "taking blood"].") + +/obj/structure/medical_stand/verb/set_APTFT() + set name = "Set IV transfer amount" + set category = "Object" + set src in range(1) + var/N = input("Amount per transfer from this:","[src]") as null|anything in transfer_amounts + if(N) + transfer_amount = N + +/obj/structure/medical_stand/proc/attach_mask(var/mob/living/carbon/C) + if(C && istype(C)) + if(C.equip_to_slot_if_possible(contained, slot_wear_mask)) + if(tank) + tank.forceMove(C) + breather = C + //if(breather.HUDneed.Find("internal")) + //internalsHud = breather.HUDneed["internal"] + return TRUE + +/obj/structure/medical_stand/proc/can_apply_to_target(var/mob/living/carbon/human/target, var/mob/user) + if(!user) + user = target + // Check target validity + if(!istype(target)) + to_chat(user, "\The [target] not compatible with machine.") + return + if(!target.organs_by_name[BP_HEAD]) + to_chat(user, "\The [target] doesn't have a head.") + return + if(!target.check_has_mouth()) + to_chat(user, "\The [target] doesn't have a mouth.") + return + if(target.wear_mask && target != breather) + to_chat(user, "\The [target] is already wearing a mask.") + return + if(target.head && (target.head.body_parts_covered & FACE)) + to_chat(user, "Remove their [target.head] first.") + return + if(!tank) + to_chat(user, "There is no tank in \the [src].") + return + if(is_loosen) + to_chat(user, "Tighten \the nut with a wrench first.") + return + if(!Adjacent(target)) + return + //when there is a breather: + if(breather && target != breather) + to_chat(user, "\The [src] is already in use.") + return + //Checking if breather is still valid + if(target == breather && target.wear_mask != contained) + to_chat(user, "\The [target] is not using the supplied mask.") + return + return 1 + +/obj/structure/medical_stand/attackby(var/obj/item/weapon/W, var/mob/user) + if(istype (W, /obj/item/weapon/tool)) + if (valve_opened) + to_chat(user, "Close the valve first.") + return + if (tank) + if(!W.is_wrench()) + return + if (!is_loosen) + is_loosen = TRUE + else + is_loosen = FALSE + if (valve_opened) + START_PROCESSING(SSobj,src) + user.visible_message( + "The [user] [is_loosen == TRUE ? "loosen" : "tighten"] the nut holding [tank] in place.", + "You [is_loosen == TRUE ? "loosen" : "tighten"] the nut holding [tank] in place.") + + else + to_chat(user, "There is no tank in \the [src].") + + else if(istype(W, /obj/item/weapon/tank)) + if(tank) + to_chat(user, "\The [src] already has a tank installed!") + else if(!is_loosen) + to_chat(user, "Loosen the nut with a wrench first.") + else + user.drop_item() + W.forceMove(src) + tank = W + user.visible_message("\The [user] attaches \the [tank] to \the [src].", "You attach \the [tank] to \the [src].") + src.add_fingerprint(user) + update_icon() + + else if (istype(W, /obj/item/weapon/reagent_containers)) + if(!isnull(src.beaker)) + to_chat(user, "There is already a reagent container loaded!") + return + user.drop_item() + W.forceMove(src) + beaker = W + to_chat(user, "You attach \the [W] to \the [src].") + update_icon() + else + return ..() + +/obj/structure/medical_stand/examine(var/mob/user) + . = ..() + + if (get_dist(src, user) > 2) + return + + if(beaker) + to_chat(user, "The IV drip is [mode ? "injecting" : "taking blood"].") + to_chat(user, "It is set to transfer [transfer_amount]u of chemicals per cycle.") + if(beaker.reagents && beaker.reagents.total_volume) + to_chat(user, "Attached is \a [beaker] with [beaker.reagents.total_volume] units of liquid.") + else + to_chat(user, "Attached is an empty [beaker].") + to_chat(user, "[attached ? attached : "No one"] is hooked up to it.") + else + to_chat(user, "There is no vessel.") + + if(tank) + if (!is_loosen) + to_chat(user, "\The [tank] connected.") + to_chat(user, "The meter shows [round(tank.air_contents.return_pressure())]. The valve is [valve_opened == TRUE ? "open" : "closed"].") + if (tank.distribute_pressure == 0) + to_chat(user, "Use wrench to replace tank.") + else + to_chat(user, "There is no tank.") + +/obj/structure/medical_stand/process() + //Gas Stuff + if(breather) + if(!can_apply_to_target(breather)) + if(tank) + tank.forceMove(src) + if (breather.wear_mask == contained) + breather.remove_from_mob(contained) + contained.forceMove(src) + else + qdel(contained) + contained = new mask_type (src) + src.visible_message("\The [contained] slips to \the [src]!") + breather = null + update_icon() + return + if(valve_opened) + if (tank) + breather.internal = tank + if(internalsHud) + internalsHud.icon_state = "internal1" + else + if(internalsHud) + internalsHud.icon_state = "internal0" + breather.internal = null + else if (valve_opened) + var/datum/gas_mixture/removed = tank.remove_air(0.01) + var/datum/gas_mixture/environment = loc.return_air() + environment.merge(removed) + + //Reagent Stuff + if(attached) + if(!Adjacent(attached)) + visible_message("The needle is ripped out of [src.attached], doesn't that hurt?") + attached.apply_damage(3, BRUTE, pick(BP_R_ARM, BP_L_ARM)) + attached = null + update_icon() + + if(beaker) + if(mode) // Give blood + if(beaker.volume > 0) + beaker.reagents.trans_to_mob(attached, transfer_amount, CHEM_BLOOD) + update_icon() + else // Take blood + var/amount = beaker.reagents.maximum_volume - beaker.reagents.total_volume + amount = min(amount, 4) + + if(amount == 0) // If the beaker is full, ping + if(prob(5)) visible_message("\The [src] pings.") + return + + var/mob/living/carbon/human/H = attached + if(!istype(H)) + return + if(!H.dna) + return + if(NOCLONE in H.mutations) + return + if(H.species.flags & NO_BLOOD) + return + if(!H.should_have_organ(O_HEART)) + return + + // If the human is losing too much blood, beep. + if(((H.vessel.get_reagent_amount("blood")/H.species.blood_volume)*100) < BLOOD_VOLUME_SAFE) + visible_message("\The [src] beeps loudly.") + + var/datum/reagent/B = H.take_blood(beaker,amount) + if (B) + beaker.reagents.reagent_list |= B + beaker.reagents.update_total() + beaker.on_reagent_change() + beaker.reagents.handle_reactions() + update_icon() + + if ((!valve_opened || tank.distribute_pressure == 0) && !breather && !attached) + return PROCESS_KILL + +/obj/structure/medical_stand/anesthetic + spawn_type = /obj/item/weapon/tank/anesthetic + mask_type = /obj/item/clothing/mask/breath/medical + is_loosen = FALSE + diff --git a/icons/obj/medical_stand_vr.dmi b/icons/obj/medical_stand_vr.dmi new file mode 100644 index 00000000000..b7292fe9757 Binary files /dev/null and b/icons/obj/medical_stand_vr.dmi differ diff --git a/maps/tether/submaps/admin_use/aro.dmm b/maps/tether/submaps/admin_use/aro.dmm index 86cc03caca3..a3d3fb04baa 100644 --- a/maps/tether/submaps/admin_use/aro.dmm +++ b/maps/tether/submaps/admin_use/aro.dmm @@ -526,7 +526,7 @@ /turf/simulated/floor/tiled/techmaint, /area/houseboat) "bE" = ( -/obj/machinery/iv_drip, +/obj/structure/medical_stand, /turf/simulated/floor/tiled/white, /area/houseboat) "bF" = ( diff --git a/maps/tether/submaps/tether_misc.dmm b/maps/tether/submaps/tether_misc.dmm index 9563a5d194b..d3d8f06b83d 100644 --- a/maps/tether/submaps/tether_misc.dmm +++ b/maps/tether/submaps/tether_misc.dmm @@ -2514,7 +2514,7 @@ /turf/simulated/floor/plating, /area/syndicate_station/start) "gH" = ( -/obj/machinery/iv_drip, +/obj/structure/medical_stand, /obj/structure/window/reinforced{ dir = 8 }, @@ -4108,7 +4108,7 @@ /turf/simulated/shuttle/floor/black, /area/shuttle/trade/centcom) "kg" = ( -/obj/machinery/iv_drip, +/obj/structure/medical_stand, /turf/simulated/shuttle/floor/black, /area/shuttle/trade/centcom) "kh" = ( @@ -9522,7 +9522,7 @@ }, /area/antag/antag_base) "tX" = ( -/obj/machinery/iv_drip, +/obj/structure/medical_stand, /obj/effect/floor_decal/steeldecal/steel_decals10{ dir = 8 }, diff --git a/maps/tether/tether-02-surface2.dmm b/maps/tether/tether-02-surface2.dmm index 7a5f8b59957..333f8bb2c64 100644 --- a/maps/tether/tether-02-surface2.dmm +++ b/maps/tether/tether-02-surface2.dmm @@ -23183,7 +23183,7 @@ pixel_x = 0; pixel_y = -26 }, -/obj/machinery/iv_drip, +/obj/structure/medical_stand, /turf/simulated/floor/tiled/white, /area/tether/surfacebase/medical/surgery) "aQZ" = ( diff --git a/maps/tether/tether-06-station2.dmm b/maps/tether/tether-06-station2.dmm index fea2bb4960d..5cb11396636 100644 --- a/maps/tether/tether-06-station2.dmm +++ b/maps/tether/tether-06-station2.dmm @@ -16419,7 +16419,7 @@ /turf/simulated/floor/tiled/white, /area/medical/surgery) "zt" = ( -/obj/machinery/iv_drip, +/obj/structure/medical_stand, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/effect/floor_decal/borderfloorwhite{ dir = 4 @@ -16440,7 +16440,7 @@ /turf/simulated/floor/tiled/white, /area/medical/surgery) "zu" = ( -/obj/machinery/iv_drip, +/obj/structure/medical_stand, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/effect/floor_decal/borderfloorwhite{ dir = 8 diff --git a/maps/tether/tether-07-station3.dmm b/maps/tether/tether-07-station3.dmm index 93611024bf0..0e778b79f73 100644 --- a/maps/tether/tether-07-station3.dmm +++ b/maps/tether/tether-07-station3.dmm @@ -21316,7 +21316,7 @@ /turf/simulated/floor/wood, /area/crew_quarters/heads/cmo) "GU" = ( -/obj/machinery/iv_drip, +/obj/structure/medical_stand, /obj/machinery/power/apc{ dir = 1; name = "north bump"; @@ -23871,7 +23871,7 @@ /turf/simulated/floor/tiled/white, /area/medical/sleeper) "KP" = ( -/obj/machinery/iv_drip, +/obj/structure/medical_stand, /obj/effect/floor_decal/borderfloorwhite{ dir = 8 }, diff --git a/maps/tether/tether-10-colony.dmm b/maps/tether/tether-10-colony.dmm index 88e010d175a..604cdc83352 100644 --- a/maps/tether/tether-10-colony.dmm +++ b/maps/tether/tether-10-colony.dmm @@ -735,7 +735,7 @@ /turf/simulated/floor/tiled/techmaint, /area/mothership/sechallway) "bk" = ( -/obj/machinery/iv_drip, +/obj/structure/medical_stand, /turf/unsimulated/floor{ icon_state = "vault"; dir = 5 @@ -9053,7 +9053,7 @@ }, /area/centcom/medical) "qs" = ( -/obj/machinery/iv_drip, +/obj/structure/medical_stand, /obj/effect/floor_decal/borderfloorwhite, /obj/effect/floor_decal/corner/paleblue/border, /turf/unsimulated/floor/steel{ @@ -17174,7 +17174,7 @@ }, /area/mothership/dorm4) "Ey" = ( -/obj/machinery/iv_drip, +/obj/structure/medical_stand, /obj/machinery/alarm{ pixel_y = 22 }, diff --git a/vorestation.dme b/vorestation.dme index 06497c5ca3b..1a6bc8e6b78 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -1297,6 +1297,7 @@ #include "code\game\objects\structures\lightpost.dm" #include "code\game\objects\structures\loot_piles.dm" #include "code\game\objects\structures\map_blocker_vr.dm" +#include "code\game\objects\structures\medical_stand_vr.dm" #include "code\game\objects\structures\mirror.dm" #include "code\game\objects\structures\mop_bucket.dm" #include "code\game\objects\structures\morgue.dm"