Files
VMSolidusandGitHub e117ef3f87 Nuke Limb Processing (#22814)
<img width="1549" height="857" alt="image"
src="https://github.com/user-attachments/assets/3830233d-0635-4942-b2aa-024651e6cffa"
/>

Limbs make up roughly half of the "Per additional player" processing
overhead, while Organs make up most of the other half.
Not every organ is an easy candidate for "Event-only processing", but
external limbs are significantly easier to do so.

So this PR makes it so that External Limbs no longer require Processing
unless an event happens that would justify them requiring processing.
Which SIGNIFICANTLY reduces the overall additional cost to the
Processing subsystem per player that joins the server.

Oh and I also made the Appendix and Kidneys not require constant
processing. The Appendix will only process if it is hit by an
Appendicitis event, and the Kidneys will only process if they are either
damaged, or you get poisoned.
2026-07-12 20:45:24 +00:00

763 lines
28 KiB
Plaintext

/obj/structure/machinery/iv_drip
name = "\improper IV drip"
desc = "A professional standard intravenous stand with supplemental gas support for medical use."
icon = 'icons/obj/iv_drip.dmi'
icon_state = "iv_stand"
anchored = 0
density = FALSE
pass_flags_self = PASSTABLE | LETPASSCLICKS
var/tipped = FALSE
var/last_full // Spam check
var/last_warning
/// Blood Stuff
var/mob/living/carbon/human/attached = null
var/obj/item/organ/external/vein = null
var/obj/item/reagent_containers/beaker = null
var/transfer_amount = REM
var/transfer_limit = 4
/// TRUE is injecting, FALSE is taking blood.
var/mode = TRUE
var/toggle_stop = TRUE
var/blood_message_sent = FALSE
var/attach_delay = 5
var/armor_check = TRUE
var/adv_scan = FALSE
/// Supplemental Gas Stuff
var/mob/living/carbon/human/breather = null
var/obj/item/clothing/mask/breath/breath_mask = null
var/obj/item/tank/tank = null
var/tank_type = null
var/is_loose = TRUE
var/list/tank_blacklist = list(/obj/item/tank/emergency_oxygen, /obj/item/tank/jetpack)
var/valve_open = FALSE
var/tank_active = FALSE
/// Emergency Positive Pressure system. Can be toggled if you want to turn it off
var/epp = TRUE
var/epp_active = FALSE
/// Matrix stuff
var/matrix/iv_matrix
/// The beam drawn from the IV to the patient.
var/datum/beam/patient_beam
/// What we accept as a container for IV transfers. Prevents attaching food and organs to IVs.
var/list/accepted_containers = list(
/obj/item/reagent_containers/blood,
/obj/item/reagent_containers/glass/beaker,
/obj/item/reagent_containers/glass/bottle)
var/list/mask_blacklist = list(
/obj/item/clothing/mask/gas/vaurca,
/obj/item/clothing/mask/breath/skrell,
/obj/item/clothing/mask/breath/lyodsuit,
/obj/item/clothing/mask/breath/infiltrator)
component_types = list(
/obj/item/circuitboard/iv_drip,
/obj/item/reagent_containers/syringe,
/obj/item/stock_parts/matter_bin,
/obj/item/stock_parts/manipulator,
/obj/item/stock_parts/scanning_module)
/obj/structure/machinery/iv_drip/mechanics_hints(mob/user, distance, is_adjacent)
. += ..()
. += "- IV drips can be supplied beakers/bloodpacks for reagent transfusions, as well as one breath mask and gas tank for supplemental gas therapy."
. += "- Use a wrench when it has a tank installed to secure it. Use it again to unsecure it before removal."
. += "- Click-drag to attach/detach the IV or secure/remove the breath mask on your target."
. += "- Click the stand with an empty hand to toggle between various modes."
. += "- ALT-Click the stand to remove items contained in the stand."
/obj/structure/machinery/iv_drip/upgrade_hints(mob/user, distance, is_adjacent)
. += ..()
. += "- Upgraded <b>scanning modules</b> will provide the exact volume and composition of attached beakers."
. += adv_scan ? SPAN_GOOD(" - Advanced Scan is enabled") : SPAN_ALERT(" - Advanced Scan is not enabled")
. += "- Upgraded <b>manipulators</b> will allow patients to be hooked to IV through armor and increase the maximum reagent transfer rate."
. += SPAN_NOTICE(" - Maximum transfer rate is <b>[transfer_limit]u/s</b>")
. += armor_check ? SPAN_ALERT(" - Armor punch-through is not enabled") : SPAN_GOOD(" - Armor punch-through is enabled")
/obj/structure/machinery/iv_drip/feedback_hints(mob/user, distance, is_adjacent)
. += ..()
. += "[src] is [mode ? "injecting" : "taking blood"] at a rate of <b>[src.transfer_amount] u/sec</b>, the automatic injection stop mode is <b>[toggle_stop ? "on" : "off"]</b>."
. += "The Emergency Positive Pressure system is [epp ? "on" : "off"]."
if(attached)
. += "\The [src] is attached to [attached]'s [vein.name]."
if(beaker)
if(LAZYLEN(beaker.reagents.reagent_volumes))
. += "Attached is \a [beaker] with [adv_scan ? "<b>[beaker.reagents.total_volume] units</b> of primarily <b>[beaker.reagents.get_primary_reagent_name()]</b>" : "some liquid"]."
else
. += "Attached is \a [beaker]. It is empty."
else
. += "No chemicals attached."
if(tank)
. += "Installed is [is_loose ? "\a [tank] sitting loose" : "\a [tank] secured"] on the stand. The meter shows <b>[round(XGM_PRESSURE(tank.air_contents))] kPa</b>, \
with the pressure set to <b>[round(tank.distribute_pressure)] kPa</b>. The valve is <b>[valve_open ? "open" : "closed"]</b>."
else
. += "No gas tank installed."
if(breath_mask)
. += "\The [src] has \a [breath_mask] installed. [breather ? breather : "No one"] is wearing it."
else
. += "No breath mask installed."
. += ..()
/obj/structure/machinery/iv_drip/Initialize(mapload)
. = ..()
var/static/list/loc_connections = list(
COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
)
AddElement(/datum/element/connect_loc, loc_connections)
/obj/structure/machinery/iv_drip/Destroy()
if(attached)
clear_attached()
QDEL_NULL(beaker)
if(breather)
breath_mask_rip()
QDEL_NULL(breath_mask)
QDEL_NULL(tank)
return ..()
/obj/structure/machinery/iv_drip/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
if(istype(mover, /obj/structure/machinery/iv_drip))
return FALSE
return ..()
/obj/structure/machinery/iv_drip/proc/on_entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
SIGNAL_HANDLER
if(ishuman(arrived))
var/mob/living/carbon/human/M = arrived
if(M.shoes?.item_flags & ITEM_FLAG_LIGHT_STEP)
return
if(M.incapacitated())
return
if(tipped)
return
if(M.m_intent == M_RUN && M.a_intent == I_HURT)
src.visible_message(SPAN_WARNING("[M] bumps into \the [src], knocking it over!"), SPAN_WARNING("You bump into \the [src], knocking it over!"))
do_crash()
/obj/structure/machinery/iv_drip/update_icon()
ClearOverlays()
if(beaker)
AddOverlays("beaker")
var/datum/reagents/reagents = beaker.reagents
if(reagents?.total_volume)
var/image/filling = image('icons/obj/iv_drip.dmi', src, "reagent")
var/percent = round((reagents.total_volume / beaker.volume) * 100)
var/fill_level = 0
switch(percent)
if(-INFINITY to 9) fill_level = 0
if(10 to 24) fill_level = 10
if(25 to 49) fill_level = 25
if(50 to 74) fill_level = 50
if(75 to 79) fill_level = 75
if(80 to 90) fill_level = 80
if(91 to INFINITY) fill_level = 100
filling.icon_state = "reagent[fill_level]"
var/reagent_color = reagents.get_color()
filling.icon += reagent_color
AddOverlays(filling)
if(attached)
AddOverlays("iv_in")
if(mode)
AddOverlays("light_green")
else
AddOverlays("light_red")
if(blood_message_sent)
AddOverlays("light_yellow")
else
AddOverlays("iv_out")
if(tank)
if(istype(tank, /obj/item/tank/oxygen))
tank_type = "oxy"
else if(istype(tank, /obj/item/tank/anesthetic))
tank_type = "anest"
else if(istype(tank, /obj/item/tank/phoron))
tank_type = "phoron"
else
tank_type = "other"
AddOverlays("tank_[tank_type]")
var/tank_level = 2
switch(tank.percent())
if(-INFINITY to 4) tank_level = 0
if(05 to 19) tank_level = 1
if(20 to 39) tank_level = 2
if(40 to 59) tank_level = 3
if(60 to 79) tank_level = 4
if(80 to 90) tank_level = 5
if(91 to INFINITY) tank_level = 6
AddOverlays("[tank.gauge_icon][tank_level]")
if(breath_mask)
if(breather)
AddOverlays("mask_on")
else
AddOverlays("mask_off")
if(epp_active)
AddOverlays("light_blue")
if(panel_open)
AddOverlays("panel_open")
/obj/structure/machinery/iv_drip/process()
breather_process()
attached_process()
/obj/structure/machinery/iv_drip/proc/breather_process()
if(breather)
if(!breather.Adjacent(src))
src.visible_message(SPAN_WARNING("\The [breath_mask] snaps back into \the [src]!"))
breath_mask_rip()
return
var/mask_check = breath_mask.get_equip_slot()
if(mask_check != slot_wear_mask)
src.visible_message(SPAN_NOTICE("\The [src] automatically retracts \the [breath_mask]."))
breath_mask_rip()
return
if(breath_mask.hanging)
src.visible_message(SPAN_NOTICE("\The [src] automatically retracts \the [breath_mask]."))
breath_mask_rip()
return
if(breath_mask.loc != breather)
src.visible_message(SPAN_NOTICE("\The [src] automatically retracts \the [breath_mask]."))
breath_mask_rip()
return
if(!tank)
return
if(breather.species.flags & NO_BREATHE)
return
if(valve_open)
var/obj/item/organ/internal/lungs/L = breather.internal_organs_by_name[BP_LUNGS]
if(!L)
src.visible_message(SPAN_NOTICE("\The [src] buzzes, automatically deactivating \the [tank]."))
playsound(src, 'sound/machines/buzz-two.ogg', 50, extrarange = SILENCED_SOUND_EXTRARANGE)
tank_off()
return
var/safe_pressure_min = breather.species.breath_pressure + 5
safe_pressure_min *= 1 + rand(1,4) * L.get_damage()/L.max_damage
if(!tank_active) // Activates and sets the kPa to a safe pressure. This keeps from it constantly resetting itself
tank.distribute_pressure = safe_pressure_min
src.visible_message(SPAN_NOTICE("\The [src] chimes and adjusts \the [tank]'s release pressure."))
playsound(src, 'sound/machines/chime.ogg', 50, extrarange = SILENCED_SOUND_EXTRARANGE)
tank_active = TRUE
if(L.checking_rupture == FALSE) // Safely retracts in case the lungs are about to rupture
src.visible_message(SPAN_WARNING("\The [src]'s flashes a warning light, automatically deactivating \the [tank] and retracting \the [breath_mask]."))
playsound(src, 'sound/machines/twobeep.ogg', 50, extrarange = SILENCED_SOUND_EXTRARANGE)
breath_mask_rip()
return
if(XGM_PRESSURE(tank.air_contents) <= 10)
src.visible_message(SPAN_WARNING("\The [src] buzzes, automatically deactivating \the [tank] and retracting \the [breath_mask]."))
playsound(src, 'sound/machines/buzz-two.ogg', 50, extrarange = SILENCED_SOUND_EXTRARANGE)
breath_mask_rip()
return
if(epp) // Emergency Positive Pressure system forces respiration
if(breather.losebreath > 0)
if(!epp_active)
src.visible_message(SPAN_WARNING("\The [src] flashes a blue light, activating it's Emergency Positive Pressure system!"))
playsound(breather, 'sound/machines/windowdoor.ogg', 50, extrarange = SILENCED_SOUND_EXTRARANGE)
epp_active = TRUE
update_icon()
tank.distribute_pressure = safe_pressure_min // Constantly adjusts the pressure to keep up with the damage
breather.losebreath = 0
to_chat(breather, SPAN_NOTICE("You feel fresh air being pushed into your lungs."))
update_icon()
/obj/structure/machinery/iv_drip/proc/attached_process()
if(attached)
if(!attached.Adjacent(src))
iv_rip()
update_icon()
return
if(!beaker)
return
if(!attached.dna)
return
if((attached.mutations & NOCLONE))
return
if(attached.species.flags & NO_BLOOD)
return
if(mode) // Injecting
if(beaker.reagents.total_volume > 0)
beaker.reagents.trans_to_mob(attached, transfer_amount, CHEM_BLOOD)
update_icon()
if(toggle_stop) // Automatically detaches if the blood volume is at 100%
if((beaker.reagents.has_reagent(/singleton/reagent/blood) || beaker.reagents.has_reagent(/singleton/reagent/saline)) && attached.get_blood_volume() >= 100)
visible_message("\The <b>[src]</b> flashes a warning light, disengaging from [attached]'s [vein.name] automatically!")
playsound(src, 'sound/machines/buzz-two.ogg', 100, extrarange = SILENCED_SOUND_EXTRARANGE)
clear_attached()
blood_message_sent = FALSE
update_icon()
return
else // Taking
var/amount = REAGENTS_FREE_SPACE(beaker.reagents)
amount = min(amount, transfer_amount)
if(amount == 0)
if(world.time > last_full + 10 SECONDS)
last_full = world.time
visible_message("\The <b>[src]</b> pings.")
playsound(src, 'sound/machines/ping.ogg', 100, extrarange = SILENCED_SOUND_EXTRARANGE)
return
if(attached.get_blood_volume() < 90 && !blood_message_sent)
visible_message(SPAN_WARNING("\The <b>[src]</b> flashes a warning light!"))
playsound(src, 'sound/machines/buzz-two.ogg', 100, extrarange = SHORT_RANGE_SOUND_EXTRARANGE)
blood_message_sent = TRUE
if(blood_message_sent)
if(world.time > last_warning + 5 SECONDS)
last_warning = world.time
visible_message(SPAN_WARNING("\The <b>[src]</b> flashes a warning light!"))
playsound(src, 'sound/machines/buzz-two.ogg', 100, extrarange = SHORT_RANGE_SOUND_EXTRARANGE)
if(attached.take_blood(beaker, amount))
update_icon()
/obj/structure/machinery/iv_drip/mouse_drop_dragged(atom/over, mob/user, src_location, over_location, params)
..()
if(use_check_and_message(user))
return
if(isDrone(user))
return
if(in_range(src, user) && ishuman(over) && in_range(over, src))
var/list/options = list(
"IV drip" = image('icons/hud/mob/radial.dmi', "iv_drip"),
"Breath mask" = image('icons/hud/mob/radial.dmi', "iv_mask"))
var/chosen_action = show_radial_menu(user, src, options, require_near = TRUE, radius = 42, tooltips = TRUE)
if(!chosen_action)
return
switch(chosen_action)
if("IV drip")
if(attached)
visible_message("[user] detaches \the [src] from [attached]'s [vein.name].")
clear_attached()
blood_message_sent = FALSE
update_icon()
return
set_attached(over)
vein = attached.get_organ(user.zone_sel.selecting)
var/checking = attached.can_inject(user, TRUE, user.zone_sel.selecting, armor_check)
if(!checking)
clear_attached()
return
if(armor_check)
var/attach_time = attach_delay
attach_time *= checking
if(!do_mob(user, attached, attach_time))
to_chat(user, SPAN_DANGER("Failed to insert \the [src]. You and [attached] must stay still!"))
clear_attached()
return
visible_message("[user][armor_check ? "" : " swiftly"] inserts \the [src] in \the [attached]'s [vein.name].")
update_icon()
return
if("Breath mask")
if(!breath_mask)
to_chat(user, SPAN_NOTICE("There is no breath mask installed into \the [src]!"))
return
if(breather)
visible_message("[usr] removes [breather]'s mask.[valve_open ? " \The [tank]'s valve automatically closes." : ""]")
breath_mask_rip()
return
breather = over
if(!breather.organs_by_name[BP_HEAD])
to_chat(user, SPAN_WARNING("\The [breather] doesn't have a head!"))
breather = null
return
if(!breather.check_has_mouth())
to_chat(user, SPAN_WARNING("\The [breather] doesn't have a mouth!"))
breather = null
return
if(breather.head && (breather.head.body_parts_covered & FACE))
to_chat(user, SPAN_WARNING("You must remove \the [breather]'s [breather.head] first!"))
breather = null
return
if(breather.wear_mask)
to_chat(user, SPAN_WARNING("You must remove \the [breather]'s [breather.wear_mask] first!"))
breather = null
return
if(tank)
tank_on()
visible_message("<b>[user]</b> secures the mask over \the <b>[breather]'s</b> face.")
playsound(breather, 'sound/effects/buckle.ogg', 50, extrarange = SILENCED_SOUND_EXTRARANGE)
breath_mask.forceMove(breather.loc)
breather.equip_to_slot(breath_mask, slot_wear_mask)
breather.update_inv_wear_mask()
update_icon()
tank_on()
return
/obj/structure/machinery/iv_drip/AltClick(mob/user)
. = ..()
if(use_check_and_message(user))
return
if(isDrone(user))
return
var/list/options = list(
"Transfer Rate" = image('icons/hud/mob/radial.dmi', "radial_transrate"),
"Remove Container" = image('icons/hud/mob/radial.dmi', "iv_beaker"),
"Remove Tank" = image('icons/hud/mob/radial.dmi', "iv_tank"),
"Remove Breath Mask" = image('icons/hud/mob/radial.dmi', "iv_mask"))
var/chosen_action = show_radial_menu(user, src, options, require_near = TRUE, radius = 42, tooltips = TRUE)
if(!chosen_action)
return
switch(chosen_action)
if("Transfer Rate")
transfer_rate()
if("Remove Container")
if(!beaker)
to_chat(user, SPAN_NOTICE("There is no reagent container to remove."))
return
user.visible_message(SPAN_NOTICE("[user] removes \the [beaker] from \the [src]."), SPAN_NOTICE("You remove \the [beaker] from \the [src]."))
beaker.forceMove(user.loc)
user.put_in_hands(beaker)
beaker = null
update_icon()
if("Remove Tank")
if(!tank)
to_chat(user, SPAN_NOTICE("There is no installed tank to remove."))
return
if(breather)
to_chat(user, SPAN_NOTICE("You cannot remove \the [tank] if someone's wearing \the [breath_mask]!"))
return
if(!is_loose)
to_chat(user, SPAN_NOTICE("You must loosen the nuts securing \the [tank] into place to remove it!"))
return
user.visible_message(SPAN_NOTICE("[user] removes \the [tank] from \the [src]."), SPAN_NOTICE("You remove \the [tank] from \the [src]."))
tank.forceMove(user.loc)
user.put_in_hands(tank)
tank = null
update_icon()
if("Remove Breath Mask")
if(!breath_mask)
to_chat(user, SPAN_NOTICE("There is no installed mask to remove."))
return
if(breather)
to_chat(user, SPAN_NOTICE("You cannot remove \the [breath_mask] if someone's wearing it!"))
return
user.visible_message(SPAN_NOTICE("[user] removes \the [breath_mask] from \the [src]."), SPAN_NOTICE("You remove \the [breath_mask] from the \the [src]."))
breath_mask.forceMove(user.loc)
user.put_in_hands(breath_mask)
breath_mask = null
update_icon()
/obj/structure/machinery/iv_drip/attackby(obj/item/attacking_item, mob/user)
if(istype(attacking_item, /obj/item/reagent_containers/blood/ripped))
to_chat(user, "You can't use a ripped bloodpack.")
return TRUE
if(is_type_in_list(attacking_item, accepted_containers))
if(beaker)
to_chat(user, "There is already a reagent container loaded!")
return TRUE
user.drop_from_inventory(attacking_item, src)
beaker = attacking_item
user.visible_message(SPAN_NOTICE("[user] attaches \the [attacking_item] to \the [src]."), SPAN_NOTICE("You attach \the [attacking_item] to \the [src]."))
update_icon()
return TRUE
if(istype(attacking_item, /obj/item/clothing/mask/breath))
if(is_type_in_list(attacking_item, mask_blacklist))
to_chat(user, "\The [attacking_item] is incompatible with \the [src].")
return TRUE
if(breath_mask)
to_chat(user, "There is already a mask installed.")
return TRUE
user.drop_from_inventory(attacking_item, src)
breath_mask = attacking_item
user.visible_message(SPAN_NOTICE("[user] places \the [attacking_item] in \the [src]."), SPAN_NOTICE("You place \the [attacking_item] in \the [src]."))
update_icon()
return TRUE
if(istype(attacking_item, /obj/item/tank))
if(is_type_in_list(attacking_item, tank_blacklist))
to_chat(user, "\The [attacking_item] is incompatible with \the [src].")
return TRUE
if(tank)
to_chat(user, "There is already a tank installed!")
return TRUE
if(istype(attacking_item, /obj/item/tank/phoron))
if(tipped)
to_chat(user, "You're not sure how to place \the [attacking_item] in the fallen [src].")
return TRUE
user.drop_from_inventory(attacking_item, src)
tank = attacking_item
user.visible_message(SPAN_NOTICE("[user] places \the [attacking_item] in \the [src]."), SPAN_NOTICE("You place \the [attacking_item] in \the [src]."))
update_icon()
return TRUE
if(attacking_item.tool_behaviour == TOOL_WRENCH)
if(!tank)
to_chat(user, "There isn't a tank installed for you to secure!")
return TRUE
if(tank_type == "phoron")
to_chat(user, "You can't properly secure this type of tank to \the [src]!")
return TRUE
user.visible_message(
SPAN_NOTICE("[user] [is_loose ? "tightens" : "loosens"] the nuts on [src]."),
SPAN_NOTICE("You [is_loose ? "tighten" : "loosen"] the nuts on [src], [is_loose ? "securing \the [tank]" : "allowing \the [tank] to be removed"]."))
playsound(src.loc, 'sound/items/wrench.ogg', 50, TRUE, extrarange = SILENCED_SOUND_EXTRARANGE)
is_loose = !is_loose
return TRUE
if(default_deconstruction_screwdriver(user, attacking_item))
return TRUE
if(default_part_replacement(user, attacking_item))
return TRUE
return ..()
/obj/structure/machinery/iv_drip/attack_ai(mob/user)
if(!ai_can_interact(user))
return
return attack_hand(user)
/obj/structure/machinery/iv_drip/attack_hand(mob/user)
if(use_check_and_message(user))
return
if(isDrone(user))
return
if(tipped)
user.visible_message("<b>[user]</b> pulls \the [src] upright.", "You pull \the [src] upright.")
icon_state = "iv_stand"
tipped = FALSE
animate(src, time = 3, transform = transform.Turn(-90), easing = SINE_EASING)
update_icon()
return
if(user.a_intent == I_HURT)
user.visible_message("<b>[user]</b> knocks \the [src] down!", "You knock \the [src] down!")
do_crash()
return
var/list/options = list(
"Transfer Rate" = image('icons/hud/mob/radial.dmi', "radial_transrate"),
"Toggle Mode" = image('icons/hud/mob/radial.dmi', "iv_mode"),
"Toggle Stop" = image('icons/hud/mob/radial.dmi', "iv_stop"),
"Toggle Valve" = image('icons/hud/mob/radial.dmi', "iv_valve"),
"Toggle EPP" = image('icons/hud/mob/radial.dmi', "iv_epp"))
var/chosen_action = show_radial_menu(user, src, options, require_near = TRUE, radius = 42, tooltips = TRUE)
if(!chosen_action)
return
switch(chosen_action)
if("Transfer Rate")
transfer_rate()
if("Toggle Mode")
toggle_mode()
if("Toggle Stop")
toggle_stop()
if("Toggle Valve")
toggle_valve()
if("Toggle EPP")
toggle_epp()
/obj/structure/machinery/iv_drip/proc/do_crash()
ClearOverlays()
visible_message(SPAN_WARNING("\The [src] falls over with a buzz, spilling out it's contents!"))
flick("iv_crash[is_loose ? "" : "_tank_[tank_type]"]", src)
playsound(src, 'sound/effects/table_slam.ogg', 50, extrarange = MEDIUM_RANGE_SOUND_EXTRARANGE)
spill()
tipped = TRUE
animate(src, time = 5, transform = transform.Turn(90), easing = BOUNCE_EASING)
update_icon()
/obj/structure/machinery/iv_drip/proc/spill()
var/turf/dropspot = get_turf(src)
if(breath_mask)
if(!breather)
breath_mask.forceMove(dropspot)
breath_mask.tumble(rand(1,3))
breath_mask.SpinAnimation(4, 2)
breath_mask = null
else
src.visible_message(SPAN_WARNING("\The [breath_mask] snaps away from \the [breather], retracting back into \the [src]!"), SPAN_WARNING("\The [breath_mask] snaps away from you, retracting back into \the [src]!"))
breath_mask_rip()
if(beaker)
beaker.forceMove(dropspot)
beaker.tumble(rand(1,3))
beaker.SpinAnimation(4, 2)
beaker = null
if(attached)
iv_rip()
if(tank)
if(is_loose)
tank.forceMove(dropspot)
tank.tumble(rand(1,2))
tank.SpinAnimation(4, 2)
tank = null
tank_type = null
if(breather)
if(breather.internals)
breather.internals.icon_state = "internal0"
breather.internal = null
valve_open = FALSE
tank_active = FALSE
epp_active = FALSE
else
src.visible_message("\The [tank] rattles, but remains firmly secured to \the [src].")
/obj/structure/machinery/iv_drip/proc/iv_rip()
attached.visible_message(SPAN_WARNING("The needle is ripped out of [attached]'s [vein.name]."), SPAN_DANGER("The needle <B>painfully</B> rips out of your [vein.name]."))
vein.take_damage(brute = 5, damage_flags = DAMAGE_FLAG_SHARP)
clear_attached()
/obj/structure/machinery/iv_drip/proc/breath_mask_rip()
if(valve_open)
tank_off()
if(breath_mask.hanging)
breath_mask.hanging = FALSE
breath_mask.adjust_sprites()
if(breath_mask.loc != breather)
var/loc_check = breath_mask.loc
if(ismob(loc_check))
var/mob/living/carbon/human/holder = loc_check
holder.remove_from_mob(breath_mask)
holder.update_inv_wear_mask()
holder.update_inv_l_hand()
holder.update_inv_r_hand()
breath_mask.forceMove(src)
breather = null
update_icon()
return
breather.remove_from_mob(breath_mask)
breather.update_inv_wear_mask()
breath_mask.forceMove(src)
breather = null
update_icon()
/obj/structure/machinery/iv_drip/proc/tank_on()
playsound(src, 'sound/effects/internals.ogg', 100, extrarange = SILENCED_SOUND_EXTRARANGE)
tank.forceMove(breather)
breather.internal = tank
if(breather.internals)
breather.internals.icon_state = "internal1"
valve_open = TRUE
update_icon()
return
/obj/structure/machinery/iv_drip/proc/tank_off()
playsound(src, 'sound/effects/internals.ogg', 100, extrarange = SILENCED_SOUND_EXTRARANGE)
tank.forceMove(src)
if(breather.internals)
breather.internals.icon_state = "internal0"
breather.internal = null
tank_active = FALSE
valve_open = FALSE
epp_active = FALSE
update_icon()
/obj/structure/machinery/iv_drip/verb/toggle_mode()
set category = "Object.IV Drip"
set name = "Toggle Mode"
set src in view(1)
if(use_check_and_message(usr))
return
mode = !mode
usr.visible_message("<b>[usr]</b> toggles \the [src] to [mode ? "inject" : "take blood"].", SPAN_NOTICE("You set \the [src] to [mode ? "injecting" : "taking blood"]."))
playsound(usr, 'sound/machines/buttonbeep.ogg', 50, extrarange = SILENCED_SOUND_EXTRARANGE)
update_icon()
/obj/structure/machinery/iv_drip/verb/toggle_stop()
set category = "Object.IV Drip"
set name = "Toggle Stop"
set src in view(1)
if(use_check_and_message(usr))
return
toggle_stop = !toggle_stop
usr.visible_message("<b>[usr]</b> toggles \the [src]'s automatic stop mode [toggle_stop ? "on" : "off"].", SPAN_NOTICE("You toggle \the [src]'s automatic stop mode [toggle_stop ? "on" : "off"]."))
playsound(usr, 'sound/machines/click.ogg', 50, extrarange = SILENCED_SOUND_EXTRARANGE)
/obj/structure/machinery/iv_drip/verb/toggle_valve()
set category = "Object.IV Drip"
set name = "Toggle Valve"
set src in view(1)
if(use_check_and_message(usr))
return
if(!tank)
to_chat(usr, SPAN_NOTICE("There is no tank for you to open the valve of!"))
return
if(!breather)
to_chat(usr, SPAN_NOTICE("There is no one wearing \the [src]'s [breath_mask] for you to open the valve for!"))
return
if(!valve_open)
usr.visible_message("<b>[usr]</b> opens \the [tank]'s valve.", SPAN_NOTICE("You open \the [tank]'s valve."))
tank_on()
return
if(valve_open)
if(epp_active)
var/response = alert(usr, "Are you sure you want to close \the [tank]'s valve? The Emergency Positive Pressure system is currently active!", "Toggle Valve", "Yes", "No")
if(response == "No")
return
epp_active = FALSE
usr.visible_message("<b>[usr]</b> closes \the [tank]'s valve.", SPAN_NOTICE("You close \the [tank]'s valve."))
tank_off()
return
/obj/structure/machinery/iv_drip/verb/toggle_epp()
set category = "Object.IV Drip"
set name = "Toggle EPP"
set src in view(1)
if(use_check_and_message(usr))
return
if(epp_active)
var/response = alert(usr, "Are you sure you want to turn off the Emergency Positive Pressure system? It is currently active!", "Toggle EPP", "Yes", "No")
if(response == "No")
return
epp_active = FALSE
update_icon()
epp = !epp
usr.visible_message("<b>[usr]</b> toggles \the [src]'s Emergency Positive Pressure system [epp ? "on" : "off"].", SPAN_NOTICE("You toggle \the [src]'s Emergency Positive Pressure system [epp ? "on" : "off"]."))
playsound(usr, 'sound/machines/click.ogg', 50, extrarange = SILENCED_SOUND_EXTRARANGE)
/obj/structure/machinery/iv_drip/verb/transfer_rate()
set category = "Object.IV Drip"
set name = "Set Transfer Rate"
set src in view(1)
if(use_check_and_message(usr))
return
transfer_amount = tgui_input_number( \
usr, \
"Set the IV drip's transfer rate between 0.001 and [transfer_limit].", \
"IV Drip", \
transfer_amount, \
transfer_limit, 0.001, \
round_value = FALSE)
to_chat(usr, SPAN_NOTICE("Transfer rate set to [src.transfer_amount] u/sec."))
/obj/structure/machinery/iv_drip/RefreshParts()
..()
var/manip = 0
var/scanner = 0
adv_scan = FALSE
armor_check = TRUE
transfer_limit = 4
for(var/obj/item/stock_parts/P in component_parts)
if(ismanipulator(P))
manip += P.rating
transfer_limit += P.rating
if(isscanner(P))
scanner += P.rating
if(manip >= 2)
armor_check = FALSE
if(scanner >= 2)
adv_scan = TRUE
/**
* Wrapper for setting the attached variable.
*/
/obj/structure/machinery/iv_drip/proc/set_attached(mob/attachee)
attached = attachee
patient_beam = Beam(attached, "iv_beam", 'icons/effects/beam.dmi', -1, 2)
/**
* Wrapper for clearing the attached variable.
*/
/obj/structure/machinery/iv_drip/proc/clear_attached()
attached = null
vein = null
if(patient_beam)
patient_beam.End()
patient_beam = null