mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-08-31 09:00:25 +01:00
<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.
763 lines
28 KiB
Plaintext
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
|