mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-31 00:58:26 +01:00
## About The Pull Request Fixes #94284 When someone walks away from you after you try to put an inhaler to their mouth, it'll cancel the do_after. Currently the do_after is based on the item in your hand and not the mob you're targeting, which leads to that issue. If you're just targeting yourself the effect is the same, so if the bar appearing on the item was a design intention I can revert it if you're only targeting yourself. Also, the function for using the inhaler returns ITEM_INTERACT_ stuff when it should so you stop whacking yourself and others with the inhaler after using it. ## Why It's Good For The Game bugfix ## Changelog 🆑 fix: inhalers no longer have infinite reach fix: using an inhaler no longer results in whacking the target with it afterwards /🆑 Co-authored-by: Fghj240 <fakeemail@notrealemail.com>
316 lines
12 KiB
Plaintext
316 lines
12 KiB
Plaintext
/obj/item/inhaler
|
|
name = "inhaler"
|
|
desc = "A small device capable of administering short bursts of aerosolized chemicals. Requires a canister to function."
|
|
w_class = WEIGHT_CLASS_SMALL
|
|
|
|
icon = 'icons/obj/medical/chemical.dmi'
|
|
icon_state = "inhaler_generic"
|
|
|
|
custom_materials = list(/datum/material/plastic = SHEET_MATERIAL_AMOUNT * 0.1)
|
|
|
|
/// The currently installed canister, from which we get our reagents. Nullable.
|
|
var/obj/item/reagent_containers/inhaler_canister/canister
|
|
/// The path for our initial canister to be generated by. If not null, we start with that canister type.
|
|
var/obj/item/reagent_containers/inhaler_canister/initial_casister_path
|
|
|
|
/// The underlay of our canister, if one is installed.
|
|
var/mutable_appearance/canister_underlay
|
|
/// The y offset to be applied to [canister_underlay].
|
|
var/canister_underlay_y_offset = -2
|
|
/// If true, we will show a rotary display with how many puffs we can be used for until the canister runs out.
|
|
var/show_puffs_left = TRUE // this is how real inhalers work
|
|
|
|
/obj/item/inhaler/Initialize(mapload)
|
|
. = ..()
|
|
if (ispath(initial_casister_path, /obj/item/reagent_containers/inhaler_canister))
|
|
set_canister(new initial_casister_path)
|
|
|
|
/obj/item/inhaler/Destroy(force)
|
|
QDEL_NULL(canister)
|
|
|
|
return ..()
|
|
|
|
/obj/item/inhaler/handle_deconstruct(disassembled)
|
|
. = ..()
|
|
|
|
canister?.forceMove(drop_location())
|
|
|
|
/obj/item/inhaler/proc/update_canister_underlay()
|
|
if (isnull(canister))
|
|
underlays -= canister_underlay
|
|
canister_underlay = null
|
|
else if (isnull(canister_underlay))
|
|
canister_underlay = mutable_appearance(canister.icon, canister.icon_state)
|
|
canister_underlay.pixel_z = canister_underlay_y_offset
|
|
underlays += canister_underlay
|
|
|
|
/obj/item/inhaler/examine(mob/user)
|
|
. = ..()
|
|
|
|
if (isnull(canister))
|
|
return
|
|
|
|
. += span_blue("It seems to have <b>[canister]</b> inserted.")
|
|
if (!show_puffs_left)
|
|
return
|
|
|
|
var/puffs_left = canister.get_puffs_left()
|
|
if (puffs_left > 0)
|
|
puffs_left = span_blue("[puffs_left]")
|
|
else
|
|
puffs_left = span_danger("[puffs_left]")
|
|
. += "Its rotary display shows its canister can be used [puffs_left] more times."
|
|
|
|
/obj/item/inhaler/Exited(atom/movable/gone, direction)
|
|
. = ..()
|
|
|
|
if (gone == canister)
|
|
set_canister(null, move_canister = FALSE)
|
|
|
|
|
|
/obj/item/inhaler/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
|
|
if (!isliving(interacting_with))
|
|
return ..() // default behavior
|
|
var/mob/living/target_mob = interacting_with
|
|
|
|
if (!can_puff(target_mob, user))
|
|
return ITEM_INTERACT_BLOCKING
|
|
|
|
var/puff_timer = 0
|
|
|
|
var/pre_use_visible_message
|
|
var/pre_use_self_message
|
|
var/pre_use_target_message
|
|
|
|
var/post_use_visible_message
|
|
var/post_use_self_message
|
|
var/post_use_target_message
|
|
|
|
if (target_mob == user) // no need for a target message
|
|
puff_timer = canister.self_administer_delay
|
|
|
|
pre_use_visible_message = span_notice("[user] puts [src] to [user.p_their()] lips, fingers on the canister...")
|
|
pre_use_self_message = span_notice("You put [src] to your lips and put pressure on the canister...")
|
|
|
|
post_use_visible_message = span_notice("[user] takes a puff of [src]!")
|
|
post_use_self_message = span_notice("You take a puff of [src]!")
|
|
else
|
|
puff_timer = canister.other_administer_delay
|
|
|
|
pre_use_visible_message = span_warning("[user] tries to force [src] between [target_mob]'s lips...")
|
|
pre_use_self_message = span_notice("You try to put [src] to [target_mob]'s lips...")
|
|
pre_use_target_message = span_userdanger("[user] tries to force [src] between your lips!")
|
|
|
|
post_use_visible_message = span_warning("[user] forces [src] between [target_mob]'s lips and pushes the canister down!")
|
|
post_use_self_message = span_notice("You force [src] between [target_mob]'s lips and press on the canister!")
|
|
post_use_target_message = span_userdanger("[user] forces [src] between your lips and presses on the canister, filling your lungs with aerosol!")
|
|
|
|
if (puff_timer > 0)
|
|
user.visible_message(pre_use_visible_message, ignored_mobs = list(user, target_mob))
|
|
to_chat(user, pre_use_self_message)
|
|
if (pre_use_target_message)
|
|
to_chat(target_mob, pre_use_target_message)
|
|
if (!do_after(user, puff_timer, target_mob))
|
|
return ITEM_INTERACT_BLOCKING
|
|
if (!can_puff(target_mob, user)) // sanity
|
|
return ITEM_INTERACT_BLOCKING
|
|
|
|
user.visible_message(post_use_visible_message, ignored_mobs = list(user, target_mob))
|
|
to_chat(user, post_use_self_message)
|
|
if (post_use_target_message)
|
|
to_chat(target_mob, post_use_target_message)
|
|
|
|
canister.puff(user, target_mob)
|
|
return ITEM_INTERACT_SUCCESS
|
|
|
|
/obj/item/inhaler/attack_self(mob/user, modifiers)
|
|
try_remove_canister(user, modifiers)
|
|
|
|
return ..()
|
|
|
|
/obj/item/inhaler/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
|
|
if (istype(tool, /obj/item/reagent_containers/inhaler_canister))
|
|
return try_insert_canister(tool, user, modifiers)
|
|
|
|
return ..()
|
|
|
|
/// Tries to remove the canister, if any is inserted.
|
|
/obj/item/inhaler/proc/try_remove_canister(mob/living/user, modifiers)
|
|
if (isnull(canister))
|
|
balloon_alert(user, "no canister inserted!")
|
|
return FALSE
|
|
|
|
if (canister.removal_time > 0)
|
|
balloon_alert(user, "removing canister...")
|
|
if (!do_after(user, canister.removal_time, src))
|
|
return FALSE
|
|
|
|
balloon_alert(user, "canister removed")
|
|
playsound(src, canister.post_insert_sound, canister.post_insert_volume)
|
|
set_canister(null, user)
|
|
|
|
// Tries to insert a canister, if none is already inserted.
|
|
/obj/item/inhaler/proc/try_insert_canister(obj/item/reagent_containers/inhaler_canister/new_canister, mob/living/user, params)
|
|
if (!isnull(canister))
|
|
balloon_alert(user, "remove the existing canister!")
|
|
return FALSE
|
|
|
|
balloon_alert(user, "inserting canister...")
|
|
playsound(src, new_canister.pre_insert_sound, new_canister.pre_insert_volume)
|
|
if (!do_after(user, new_canister.insertion_time, src))
|
|
return FALSE
|
|
playsound(src, new_canister.post_insert_sound, new_canister.post_insert_volume)
|
|
balloon_alert(user, "canister inserted")
|
|
set_canister(new_canister, user)
|
|
|
|
return TRUE
|
|
|
|
/// Setter proc for [canister]. Moves the existing canister out of the inhaler, while moving a new canister inside and registering it.
|
|
/obj/item/inhaler/proc/set_canister(obj/item/reagent_containers/inhaler_canister/new_canister, mob/living/user, move_canister = TRUE)
|
|
if (move_canister && !isnull(canister))
|
|
if (iscarbon(loc))
|
|
var/mob/living/carbon/carbon_loc = loc
|
|
INVOKE_ASYNC(carbon_loc, TYPE_PROC_REF(/mob/living/carbon, put_in_hands), canister)
|
|
else if (!isnull(loc))
|
|
canister.forceMove(loc)
|
|
|
|
canister = new_canister
|
|
canister?.forceMove(src)
|
|
update_canister_underlay()
|
|
|
|
/// Determines if we can be used. Fails on no canister, empty canister, invalid targets, or non-breathing targets.
|
|
/obj/item/inhaler/proc/can_puff(mob/living/target_mob, mob/living/user, silent = FALSE)
|
|
if (isnull(canister))
|
|
if (!silent)
|
|
balloon_alert(user, "no canister!")
|
|
return FALSE
|
|
if (isnull(canister.reagents) || canister.reagents.total_volume <= 0)
|
|
if (!silent)
|
|
balloon_alert(user, "canister is empty!")
|
|
return FALSE
|
|
if (!iscarbon(target_mob)) // maybe mix this into a general has mouth check
|
|
if (!silent)
|
|
balloon_alert(user, "not breathing!")
|
|
return FALSE
|
|
var/mob/living/carbon/carbon_target = target_mob
|
|
if (carbon_target.is_mouth_covered())
|
|
if (!silent)
|
|
balloon_alert(user, "expose the mouth!")
|
|
return FALSE
|
|
if (HAS_TRAIT(carbon_target, TRAIT_NOBREATH))
|
|
if (!silent)
|
|
balloon_alert(user, "not breathing!")
|
|
return FALSE
|
|
var/obj/item/organ/lungs/lungs = carbon_target.get_organ_slot(ORGAN_SLOT_LUNGS)
|
|
if (isnull(lungs) || lungs.received_pressure_mult <= 0)
|
|
if (!silent)
|
|
balloon_alert(user, "not breathing!")
|
|
return FALSE
|
|
|
|
return TRUE
|
|
|
|
/obj/item/reagent_containers/inhaler_canister
|
|
name = "inhaler canister"
|
|
desc = "A small canister filled with aerosolized reagents for use in a inhaler."
|
|
w_class = WEIGHT_CLASS_TINY
|
|
|
|
icon = 'icons/obj/medical/chemical.dmi'
|
|
icon_state = "canister_generic"
|
|
|
|
initial_reagent_flags = SEALED_CONTAINER | DRAINABLE | REFILLABLE | NO_SPLASH
|
|
has_variable_transfer_amount = FALSE
|
|
|
|
max_integrity = 60
|
|
|
|
custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 0.2)
|
|
|
|
/// The sound that plays when we are used.
|
|
var/puff_sound = 'sound/effects/spray.ogg'
|
|
/// The volume of [puff_sound]
|
|
var/puff_volume = 20
|
|
|
|
/// The sound that plays when someone TRIES to insert us.
|
|
var/pre_insert_sound = 'sound/items/taperecorder/tape_flip.ogg'
|
|
/// The sound that plays when we are removed or inserted.
|
|
var/post_insert_sound = 'sound/items/taperecorder/taperecorder_close.ogg'
|
|
|
|
/// The volume of [pre_insert_sound]
|
|
var/pre_insert_volume = 50
|
|
/// The volume of [post_insert_sound]
|
|
var/post_insert_volume = 50
|
|
|
|
/// The time it takes to insert us into a inhaler.
|
|
var/insertion_time = 2 SECONDS
|
|
/// The time it takes to remove us from a inhaler.
|
|
var/removal_time = 0.5 SECONDS
|
|
|
|
/// The time it takes for us to be used on someone else.
|
|
var/other_administer_delay = 3 SECONDS
|
|
/// The time it takes for us to be used on our owner.
|
|
var/self_administer_delay = 1 SECONDS
|
|
|
|
/// Called when a inhaler we are in is used on someone. Transfers reagents and plays the puff sound.
|
|
/obj/item/reagent_containers/inhaler_canister/proc/puff(mob/living/user, mob/living/carbon/target)
|
|
playsound(src, puff_sound, puff_volume, TRUE, -6)
|
|
reagents.trans_to(target, amount_per_transfer_from_this, transferred_by = user, methods = INHALE)
|
|
|
|
/// Returns a integer approximating how many puffs we can be used for.
|
|
/obj/item/reagent_containers/inhaler_canister/proc/get_puffs_left()
|
|
return ROUND_UP(reagents.total_volume / amount_per_transfer_from_this)
|
|
|
|
/obj/item/reagent_containers/inhaler_canister/handle_deconstruct(disassembled)
|
|
if (!reagents?.total_volume)
|
|
return ..()
|
|
|
|
var/datum/reagents/smoke_reagents = new/datum/reagents() // Lets be safe first, our own reagents may be qdelled if we get deleted
|
|
var/datum/effect_system/fluid_spread/smoke/chem/smoke_machine/smoke = new()
|
|
smoke_reagents.my_atom = src
|
|
for (var/datum/reagent/reagent as anything in reagents.reagent_list)
|
|
smoke_reagents.add_reagent(reagent.type, reagent.volume, added_purity = reagent.purity)
|
|
reagents.remove_reagent(reagent.type, reagent.volume)
|
|
if (smoke_reagents.reagent_list)
|
|
smoke.set_up(1, holder = src, location = get_turf(src), carry = smoke_reagents)
|
|
smoke.start(log = TRUE)
|
|
visible_message(span_warning("[src] breaks open and sprays its aerosilized contents everywhere!"))
|
|
else
|
|
visible_message(span_warning("[src] breaks open - but is empty!"))
|
|
|
|
return ..()
|
|
|
|
/obj/item/inhaler/medical
|
|
icon_state = "inhaler_medical"
|
|
|
|
/obj/item/inhaler/salbutamol
|
|
name = "salbutamol inhaler"
|
|
icon_state = "inhaler_medical"
|
|
initial_casister_path = /obj/item/reagent_containers/inhaler_canister/salbutamol
|
|
|
|
/obj/item/reagent_containers/inhaler_canister/salbutamol
|
|
name = "salbutamol canister"
|
|
icon_state = "canister_medical"
|
|
list_reagents = list(/datum/reagent/medicine/salbutamol = 30)
|
|
|
|
/obj/item/inhaler/albuterol
|
|
name = "albuterol inhaler"
|
|
icon_state = "inhaler_medical"
|
|
initial_casister_path = /obj/item/reagent_containers/inhaler_canister/albuterol
|
|
|
|
/obj/item/reagent_containers/inhaler_canister/albuterol
|
|
name = "albuterol canister"
|
|
desc = "A small canister filled with aerosolized reagents for use in a inhaler. This one contains albuterol, a potent bronchodilator that can stop \
|
|
asthma attacks in their tracks."
|
|
icon_state = "canister_medical"
|
|
list_reagents = list(/datum/reagent/medicine/albuterol = 30)
|
|
|
|
/obj/item/reagent_containers/inhaler_canister/albuterol/asthma
|
|
name = "low-pressure albuterol canister"
|
|
desc = "A small canister filled with aerosolized reagents for use in a inhaler. This one contains albuterol, a potent bronchodilator that can stop \
|
|
asthma attacks in their tracks. It seems to be a lower-pressure variant, and can only hold 20u."
|
|
list_reagents = list(/datum/reagent/medicine/albuterol = 20)
|
|
volume = 20
|
|
|
|
/obj/item/inhaler/albuterol/asthma
|
|
name = "rescue inhaler"
|
|
icon_state = "inhaler_generic"
|
|
initial_casister_path = /obj/item/reagent_containers/inhaler_canister/albuterol/asthma
|