Files
Alexis 21b4095dfd [MDB IGNORE] [IDB IGNORE] Upstream Sync - 04/17/2026 (#5453)
Upstream 04/17/2026

fixes https://github.com/Bubberstation/Bubberstation/issues/5549

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: tgstation-ci[bot] <179393467+tgstation-ci[bot]@users.noreply.github.com>
Co-authored-by: ArcaneMusic <41715314+ArcaneMusic@users.noreply.github.com>
Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
Co-authored-by: Rhials <28870487+Rhials@users.noreply.github.com>
Co-authored-by: rageguy505 <54517726+rageguy505@users.noreply.github.com>
Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com>
Co-authored-by: John Willard <53777086+JohnFulpWillard@users.noreply.github.com>
Co-authored-by: Aliceee2ch <160794176+Aliceee2ch@users.noreply.github.com>
Co-authored-by: Time-Green <7501474+Time-Green@users.noreply.github.com>
Co-authored-by: Tsar-Salat <62388554+Tsar-Salat@users.noreply.github.com>
Co-authored-by: SmArtKar <44720187+SmArtKar@users.noreply.github.com>
Co-authored-by: Maxipat <108554989+Maxipat112@users.noreply.github.com>
Co-authored-by: SyncIt21 <110812394+SyncIt21@users.noreply.github.com>
Co-authored-by: deltanedas <39013340+deltanedas@users.noreply.github.com>
Co-authored-by: SimplyLogan <47579821+loganuk@users.noreply.github.com>
Co-authored-by: loganuk <fakeemail123@aol.com>
Co-authored-by: Leland Kemble <70413276+lelandkemble@users.noreply.github.com>
Co-authored-by: FalloutFalcon <86381784+FalloutFalcon@users.noreply.github.com>
Co-authored-by: Roxy <75404941+TealSeer@users.noreply.github.com>
Co-authored-by: Lucy <lucy@absolucy.moe>
Co-authored-by: siliconOpossum <138069572+siliconOpossum@users.noreply.github.com>
Co-authored-by: Isratosh <Isratosh@hotmail.com>
Co-authored-by: TheRyeGuyWhoWillNowDie <70169560+TheRyeGuyWhoWillNowDie@users.noreply.github.com>
Co-authored-by: Neocloudy <88008002+Neocloudy@users.noreply.github.com>
Co-authored-by: Alexander V. <volas@ya.ru>
Co-authored-by: ElGitificador <168473461+ElGitificador@users.noreply.github.com>
Co-authored-by: Twaticus <46540570+Twaticus@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Bloop <13398309+vinylspiders@users.noreply.github.com>
Co-authored-by: Cameron Lennox <killer65311@gmail.com>
Co-authored-by: Tim <timothymtorres@gmail.com>
Co-authored-by: Iamgoofball <iamgoofball@gmail.com>
Co-authored-by: Layzu666 <121319428+Layzu666@users.noreply.github.com>
Co-authored-by: Arturlang <24881678+Arturlang@users.noreply.github.com>
Co-authored-by: _0Steven <42909981+00-Steven@users.noreply.github.com>
Co-authored-by: mrmanlikesbt <99309552+mrmanlikesbt@users.noreply.github.com>
Co-authored-by: Ben10Omintrix <138636438+Ben10Omintrix@users.noreply.github.com>
Co-authored-by: John F. Kennedy <54908920+MacaroniCritter@users.noreply.github.com>
Co-authored-by: Cursor <102828457+theselfish@users.noreply.github.com>
Co-authored-by: Josh <josh.adam.powell@gmail.com>
Co-authored-by: Josh Powell <josh.powell@softwire.com>
Co-authored-by: Yobrocharlie <Charliemiller5617@gmail.com>
Co-authored-by: Hardly3D <66234359+Hardly3D@users.noreply.github.com>
Co-authored-by: shayoki <96078776+shayoki@users.noreply.github.com>
Co-authored-by: LT3 <83487515+lessthnthree@users.noreply.github.com>
2026-05-16 00:56:00 +02:00

856 lines
33 KiB
Plaintext

/obj/item/stack/medical
name = "medical pack"
singular_name = "medical pack"
icon = 'icons/obj/medical/stack_medical.dmi'
worn_icon_state = "nothing"
amount = 6
max_amount = 6
w_class = WEIGHT_CLASS_TINY
full_w_class = WEIGHT_CLASS_TINY
throw_speed = 3
throw_range = 7
resistance_flags = FLAMMABLE
max_integrity = 40
novariants = FALSE
item_flags = NOBLUDGEON|SKIP_FANTASY_ON_SPAWN
cost = 250
source = /datum/robot_energy_storage/medical
merge_type = /obj/item/stack/medical
/// Verb used when applying this object to someone
var/apply_verb = "applying"
/// If set and this used as a splint for a broken bone wound,
/// This is used as a multiplier for applicable slowdowns (lower = better) (also for speeding up burn recoveries)
var/splint_factor
/// Like splint_factor but for burns instead of bone wounds. This is a multiplier used to speed up burn recoveries
var/burn_cleanliness_bonus
/// How much blood flow this stack can absorb if used as a bandage on a cut wound.
/// note that absorption is how much we lower the flow rate, not the raw amount of blood we suck up
var/absorption_capacity
/// How quickly we lower the blood flow on a cut wound we're bandaging.
/// Expected lifetime of this bandage in seconds is thus absorption_capacity/absorption_rate,
/// or until the cut heals, whichever comes first
var/absorption_rate
/// How long it takes to apply it to yourself
var/self_delay = 5 SECONDS
/// How long it takes to apply it to someone else
var/other_delay = 0
/// If we've still got more and the patient is still hurt, should we keep going automatically?
var/repeating = FALSE
/// How much brute we heal per application. This is the only number that matters for simplemobs
var/heal_brute
/// How much burn we heal per application
var/heal_burn
/// How much we reduce bleeding per application on cut wounds
var/stop_bleeding
/// How much sanitization to apply to burn wounds on application
var/sanitization
/// How much we add to flesh_healing for burn wounds on application
var/flesh_regeneration
/// Whether this item can be used on dead bodies
var/works_on_dead = FALSE
/// The sound this makes when starting healing with this item
var/heal_begin_sound = null
/// The sound this makes when healed successfully with this item
var/heal_end_sound = null
/// The sound this makes when doing a continuous loop of healing with this item
var/heal_continuous_sound = null
/obj/item/stack/medical/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
if(!isliving(interacting_with))
return NONE
if(!begin_heal_loop(interacting_with, user, auto_change_zone = TRUE))
return NONE // [ITEM_INTERACT_BLOCKING] would be redundant as we are nobludgeon
return ITEM_INTERACT_SUCCESS
/obj/item/stack/medical/interact_with_atom_secondary(atom/interacting_with, mob/living/user, list/modifiers)
if(!isliving(interacting_with))
return NONE
if(!begin_heal_loop(interacting_with, user, auto_change_zone = FALSE))
return NONE // see above
return ITEM_INTERACT_SUCCESS
/obj/item/stack/medical/Initialize(mapload, new_amount, merge, list/mat_override, mat_amt)
. = ..()
register_item_context()
/obj/item/stack/medical/add_item_context(obj/item/source, list/context, atom/target, mob/living/user)
if(!isliving(target))
return NONE
if(iscarbon(target))
context[SCREENTIP_CONTEXT_LMB] = "Auto Heal"
context[SCREENTIP_CONTEXT_RMB] = "Manual Heal"
else
context[SCREENTIP_CONTEXT_LMB] = "Heal"
return CONTEXTUAL_SCREENTIP_SET
/obj/item/stack/medical/apply_fantasy_bonuses(bonus)
. = ..()
if(heal_brute)
heal_brute = modify_fantasy_variable("heal_brute", heal_brute, bonus)
if(heal_burn)
heal_burn = modify_fantasy_variable("heal_burn", heal_burn, bonus)
if(stop_bleeding)
stop_bleeding = modify_fantasy_variable("stop_bleeding", stop_bleeding, bonus/10)
if(sanitization)
sanitization = modify_fantasy_variable("sanitization", sanitization, bonus/10)
if(flesh_regeneration)
flesh_regeneration = modify_fantasy_variable("flesh_regeneration", flesh_regeneration, bonus/10)
/obj/item/stack/medical/remove_fantasy_bonuses(bonus)
heal_brute = reset_fantasy_variable("heal_brute", heal_brute)
heal_burn = reset_fantasy_variable("heal_burn", heal_burn)
stop_bleeding = reset_fantasy_variable("stop_bleeding", stop_bleeding)
sanitization = reset_fantasy_variable("sanitization", sanitization)
flesh_regeneration = reset_fantasy_variable("flesh_regeneration", flesh_regeneration)
return ..()
/// Used to begin the recursive healing loop.
/// Returns TRUE if we entered the loop, FALSE if we didn't
/obj/item/stack/medical/proc/begin_heal_loop(mob/living/patient, mob/living/user, auto_change_zone = TRUE)
if(DOING_INTERACTION_WITH_TARGET(user, patient))
return FALSE
var/heal_zone = check_zone(user.zone_selected)
if(!try_heal_checks(patient, user, heal_zone))
return FALSE
SSblackbox.record_feedback("nested tally", "medical_item_used", 1, list("[auto_change_zone ? "auto" : "manual"]", "[type]"))
patient.balloon_alert(user, "[apply_verb] [parse_zone(heal_zone)]...")
INVOKE_ASYNC(src, PROC_REF(try_heal), patient, user, heal_zone, FALSE, iscarbon(patient) && auto_change_zone) // auto change is useless for non-carbons
return TRUE
/**
* What actually handles printing the message that we're starting to heal someone, and trying to heal them
*
* This proc is recursively called until we run out of charges OR until the patient is fully healed
* OR until the target zone is fully healed (if auto_change_zone is FALSE)
*
* * patient - The mob we're trying to heal
* * user - The mob that's trying to heal the patient
* * healed_zone - The zone we're trying to heal on the patient
* Disregarded if auto_change_zone is TRUE
* * silent - If we should not print the message that we're starting to heal the patient
* Used so looping the proc doesn't spam messages
* * auto_change_zone - Handles the behavior when we finish healing a zone
* If auto_change_zone is set to TRUE, it picks the next most damaged zone to heal
* If auto_change_zone is set to FALSE, it'll give the user a chance to pick a new zone to heal
* If continuous is set to true, it will play the continuous sound for healing
*/
/obj/item/stack/medical/proc/try_heal(mob/living/patient, mob/living/user, healed_zone, silent = FALSE, auto_change_zone = TRUE, continuous = FALSE)
if(heal_begin_sound && !continuous)
playsound(patient, heal_begin_sound, 75, TRUE, MEDIUM_RANGE_SOUND_EXTRARANGE)
if(patient == user)
if(!silent)
user.visible_message(
span_notice("[user] starts to apply [src] on [user.p_them()]self..."),
span_notice("You begin applying [src] on yourself..."),
visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE,
)
if(!do_after(
user,
self_delay * (auto_change_zone ? 1 : 0.9),
patient,
extra_checks = CALLBACK(src, PROC_REF(can_heal), patient, user, healed_zone),
))
return
if(!auto_change_zone)
healed_zone = check_zone(user.zone_selected)
if(!try_heal_checks(patient, user, healed_zone))
return
else if(other_delay)
if(!silent)
user.visible_message(
span_notice("[user] starts to apply [src] on [patient]."),
span_notice("You begin applying [src] on [patient]..."),
visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE,
)
if(!do_after(
user,
other_delay * (auto_change_zone ? 1 : 0.9),
patient,
extra_checks = CALLBACK(src, PROC_REF(can_heal), patient, user, healed_zone),
))
return
if(!auto_change_zone)
healed_zone = check_zone(user.zone_selected)
if(!try_heal_checks(patient, user, healed_zone))
return
else
if(!silent)
user.visible_message(
span_notice("[user] applies [src] on [patient]."),
span_notice("You apply [src] on [patient]."),
visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE,
)
if(iscarbon(patient))
if(!heal_carbon(patient, user, healed_zone))
return
else if(isanimal_or_basicmob(patient))
if(!heal_simplemob(patient, user))
return
else
CRASH("Stack medical item healing a non-carbon, non-animal mob [patient] ([patient.type])")
if(!use(1) || !repeating || amount <= 0)
var/atom/alert_loc = QDELETED(src) ? user : src
alert_loc.balloon_alert(user, repeating ? "all used up!" : "treated [parse_zone(healed_zone)]")
if(heal_end_sound)
playsound(patient, heal_end_sound, 75, TRUE, MEDIUM_RANGE_SOUND_EXTRARANGE)
return
if(heal_continuous_sound && (continuous || !silent))
playsound(patient, heal_continuous_sound, 75, TRUE, MEDIUM_RANGE_SOUND_EXTRARANGE)
log_combat(user, patient, "healed", src)
// first, just try looping
// 1. we can keep healing the current target
// 2. the user's changed their target (and thus we should heal that limb instead)
var/preferred_target = check_zone(user.zone_selected)
if(try_heal_checks(patient, user, preferred_target, silent = TRUE))
if(preferred_target != healed_zone)
patient.balloon_alert(user, "[apply_verb] [parse_zone(preferred_target)]...")
try_heal(patient, user, preferred_target, TRUE, auto_change_zone, TRUE)
return
// second, handle what happens otherwise
if(!iscarbon(patient))
// behavior 0: non-carbons have no limbs so we can assume they are fully healed
patient.balloon_alert(user, "fully treated")
else if(auto_change_zone)
// behavior 1: automatically pick another zone to heal
try_heal_auto_change_zone(patient, user, preferred_target, healed_zone)
else
// behavior 2: assess injury, giving the user time to manually pick another zone
try_heal_manual_target(patient, user)
if(heal_end_sound)
playsound(patient, heal_end_sound, 75, TRUE, MEDIUM_RANGE_SOUND_EXTRARANGE)
/obj/item/stack/medical/proc/try_heal_auto_change_zone(mob/living/carbon/patient, mob/living/user, preferred_target, last_zone)
PRIVATE_PROC(TRUE)
var/list/other_affected_limbs = list()
for(var/obj/item/bodypart/limb as anything in patient.get_bodyparts())
if(!try_heal_checks(patient, user, limb.body_zone, silent = TRUE))
continue
other_affected_limbs += limb.body_zone
if(!length(other_affected_limbs))
patient.balloon_alert(user, "fully treated")
return
var/next_picked = (preferred_target in other_affected_limbs) ? preferred_target : other_affected_limbs[1]
if(next_picked != last_zone)
patient.balloon_alert(user, "[apply_verb] [parse_zone(next_picked)]...")
try_heal(patient, user, next_picked, silent = TRUE, auto_change_zone = TRUE, continuous = TRUE)
/obj/item/stack/medical/proc/try_heal_manual_target(mob/living/carbon/patient, mob/living/user)
PRIVATE_PROC(TRUE)
patient.balloon_alert(user, "assessing injury...")
if(!do_after(user, 1 SECONDS, patient))
return
var/new_zone = check_zone(user.zone_selected)
if(!try_heal_checks(patient, user, new_zone))
return
patient.balloon_alert(user, "[apply_verb] [parse_zone(new_zone)]...")
try_heal(patient, user, new_zone, silent = TRUE, auto_change_zone = FALSE, continuous = TRUE)
/// Checks if the passed patient can be healed by the passed user
/obj/item/stack/medical/proc/can_heal(mob/living/patient, mob/living/user, healed_zone, silent = FALSE)
return patient.try_inject(user, healed_zone, injection_flags = silent ? NONE : INJECT_TRY_SHOW_ERROR_MESSAGE)
/// Checks a bunch of stuff to see if we can heal the patient, including can_heal
/// Gives a feedback if we can't ultimatly heal the patient (unless silent is TRUE)
/obj/item/stack/medical/proc/try_heal_checks(mob/living/patient, mob/living/user, healed_zone, silent = FALSE)
if(!(healed_zone in patient.get_all_limbs()))
healed_zone = BODY_ZONE_CHEST
if(!can_heal(patient, user, healed_zone, silent))
// has its own feedback
return FALSE
if(!works_on_dead && patient.stat == DEAD)
if(!silent)
patient.balloon_alert(user, "[patient.p_theyre()] dead!")
return FALSE
if(iscarbon(patient))
var/mob/living/carbon/carbon_patient = patient
var/obj/item/bodypart/affecting = carbon_patient.get_bodypart(healed_zone)
if(!affecting) //Missing limb?
if(!silent)
carbon_patient.balloon_alert(user, "no [parse_zone(healed_zone)]!")
return FALSE
if(!IS_ORGANIC_LIMB(affecting)) //Limb must be organic to be healed - RR
if(!silent)
carbon_patient.balloon_alert(user, "[affecting.plaintext_zone] is not organic!")
return FALSE
var/datum/wound/burn/flesh/any_burn_wound = locate() in affecting.wounds
var/can_heal_burn_wounds = (flesh_regeneration || sanitization) && any_burn_wound?.can_be_ointmented_or_meshed()
var/can_suture_bleeding = stop_bleeding && affecting.cached_bleed_rate > 0
var/brute_to_heal = heal_brute && affecting.brute_dam > 0
var/burn_to_heal = heal_burn && affecting.burn_dam > 0
if(!brute_to_heal && !burn_to_heal && !can_heal_burn_wounds && !can_suture_bleeding)
if(!silent)
if(!brute_to_heal && stop_bleeding) // no brute, no bleeding
carbon_patient.balloon_alert(user, "[affecting.plaintext_zone] is not bleeding or bruised!")
else if(!burn_to_heal && (flesh_regeneration || sanitization) && any_burn_wound) // no burns, existing burn wounds are treated
carbon_patient.balloon_alert(user, "[affecting.plaintext_zone] is fully treated, give it time!")
else if(!affecting.brute_dam && !affecting.burn_dam) // not hurt at all
carbon_patient.balloon_alert(user, "[affecting.plaintext_zone] is not hurt!")
else // probably hurt in some way but we are not the right item for this
carbon_patient.balloon_alert(user, "can't heal [affecting.plaintext_zone] with [name]!")
return FALSE
return TRUE
if(isanimal_or_basicmob(patient))
if(!heal_brute) // only brute can heal
if(!silent)
patient.balloon_alert(user, "can't heal with [name]!")
return FALSE
if(!(patient.mob_biotypes & MOB_ORGANIC))
if(!silent)
patient.balloon_alert(user, "no organic tissue!")
return FALSE
if(patient.health == patient.maxHealth)
if(!silent)
patient.balloon_alert(user, "not hurt!")
return FALSE
return TRUE
return FALSE
/// The healing effects on a carbon patient.
/// Since we have extra details for dealing with bodyparts, we get our own fancy proc.
/// Still returns TRUE on success and FALSE on fail
/obj/item/stack/medical/proc/heal_carbon(mob/living/carbon/patient, mob/living/user, healed_zone)
var/obj/item/bodypart/affecting = patient.get_bodypart(healed_zone)
user.visible_message(
span_green("[user] applies [src] on [patient]'s [affecting.plaintext_zone]."),
span_green("You apply [src] on [patient]'s [affecting.plaintext_zone]."),
visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE,
)
var/previous_damage = affecting.get_damage()
if(affecting.heal_damage(heal_brute, heal_burn))
patient.update_damage_overlays()
if(stop_bleeding)
for(var/datum/wound/wound as anything in affecting.wounds)
if(wound.blood_flow)
wound.adjust_blood_flow(-1 * stop_bleeding * (user == patient ? 0.7 : 1))
break // one at a time
affecting.adjustBleedStacks(-1 * stop_bleeding, 0)
if(flesh_regeneration || sanitization)
for(var/datum/wound/burn/flesh/wound in affecting.wounds)
if(wound.can_be_ointmented_or_meshed())
wound.flesh_healing += flesh_regeneration
wound.sanitization += sanitization
break // one at a time
post_heal_effects(max(previous_damage - affecting.get_damage(), 0), patient, user)
return TRUE
/// Healing a simple mob, just an adjust_brute_loss() call
/obj/item/stack/medical/proc/heal_simplemob(mob/living/patient, mob/living/user)
patient.adjust_brute_loss(-1 * (heal_brute * patient.maxHealth / 100))
user.visible_message(
span_green("[user] applies [src] on [patient]."),
span_green("You apply [src] on [patient]."),
visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE,
)
return TRUE
///Override this proc for special post heal effects. Only called for carbon patients.
/obj/item/stack/medical/proc/post_heal_effects(amount_healed, mob/living/carbon/healed_mob, mob/living/user)
return
/obj/item/stack/medical/bruise_pack
name = "bruise pack"
singular_name = "bruise pack"
desc = "A therapeutic gel pack and bandages designed to treat blunt-force trauma."
icon_state = "brutepack"
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
heal_brute = 40
self_delay = 4 SECONDS
other_delay = 2 SECONDS
merge_type = /obj/item/stack/medical/bruise_pack
apply_verb = "applying to"
/obj/item/stack/medical/bruise_pack/grind_results()
return list(/datum/reagent/medicine/c2/libital = 10)
/obj/item/stack/medical/bruise_pack/suicide_act(mob/living/user)
user.visible_message(span_suicide("[user] is bludgeoning [user.p_them()]self with [src]! It looks like [user.p_theyre()] trying to commit suicide!"))
return BRUTELOSS
/obj/item/stack/medical/wrap
name = "wrap"
desc = "Something you can wrap around someone, like a hug."
gender = PLURAL
icon_state = "gauze"
apply_verb = "wrapping"
works_on_dead = TRUE
/obj/item/stack/medical/wrap/Initialize(mapload, new_amount, merge, list/mat_override, mat_amt)
. = ..()
AddComponent( \
/datum/component/limb_applicable, \
apply_category = LIMB_ITEM_GAUZE, \
override_existing = TRUE, \
can_apply = CALLBACK(src, PROC_REF(can_gauze_limb)), \
do_apply = CALLBACK(src, PROC_REF(do_gauze_limb)), \
on_apply = CALLBACK(src, PROC_REF(on_gauze_limb)), \
)
RegisterSignals(src, list(COMSIG_ITEM_APPLIED_TO_LIMB, COMSIG_ITEM_UNAPPLIED_FROM_LIMB), PROC_REF(update_wounds))
/obj/item/stack/medical/wrap/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
return NONE // uses component
/// Callback for limb applicability component
/obj/item/stack/medical/wrap/proc/can_gauze_limb(mob/user, mob/living/patient, obj/item/bodypart/limb)
var/can_gauze = FALSE
for(var/datum/wound/wound as anything in limb.wounds)
if(!(wound.wound_flags & ACCEPTS_GAUZE))
continue
can_gauze = TRUE
break
. = NONE
var/surgery_prepped = HAS_TRAIT(limb, TRAIT_READY_TO_OPERATE)
if(!surgery_prepped)
. |= LIMB_APPLICABLE_BLOCK_ITEM_INTERACTION
if(!can_gauze)
if(!surgery_prepped)
patient.balloon_alert(user, LAZYLEN(limb.wounds) ? "can't gauze!" : "no wounds!")
. |= LIMB_APPLICABLE_BLOCK_APPLICATION
return .
var/obj/item/stack/medical/wrap/current_gauze = LAZYACCESS(limb.applied_items, LIMB_ITEM_GAUZE)
if(current_gauze && (current_gauze.absorption_capacity * 1.2 > absorption_capacity)) // ignore if our new wrap is < 20% better than the current one, so someone doesn't bandage it 5 times in a row
if(!surgery_prepped)
patient.balloon_alert(user, pick("already bandaged!", "bandage is clean!")) // good enough
. |= LIMB_APPLICABLE_BLOCK_APPLICATION
return .
/// Callback for limb applicability component
/obj/item/stack/medical/wrap/proc/do_gauze_limb(mob/user, mob/living/patient, obj/item/bodypart/limb)
var/scanned_wound = FALSE
for(var/datum/wound/wound as anything in limb.wounds)
if(!HAS_TRAIT(wound, TRAIT_WOUND_SCANNED))
continue
scanned_wound = TRUE
break
var/treatment_delay = (user == patient ? self_delay : other_delay)
if(scanned_wound)
treatment_delay *= 0.5
if(user == patient)
user.visible_message(
span_warning("[user] begins expertly wrapping the wounds on [p_their()]'s [limb.plaintext_zone] with [src]..."),
span_warning("You begin quickly wrapping the wounds on your [limb.plaintext_zone] with [src], keeping the holo-image indications in mind..."),
visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE,
)
else
user.visible_message(
span_warning("[user] begins expertly wrapping the wounds on [patient]'s [limb.plaintext_zone] with [src]..."),
span_warning("You begin quickly wrapping the wounds on [patient]'s [limb.plaintext_zone] with [src], keeping the holo-image indications in mind..."),
visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE,
)
else
user.visible_message(
span_warning("[user] begins wrapping the wounds on [patient]'s [limb.plaintext_zone] with [src]..."),
span_warning("You begin wrapping the wounds on [user == patient ? "your" : "[patient]'s"] [limb.plaintext_zone] with [src]..."),
visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE,
)
if(heal_begin_sound)
playsound(src, heal_begin_sound, 75, TRUE, MEDIUM_RANGE_SOUND_EXTRARANGE)
if(!do_after(user, treatment_delay, patient))
return FALSE
if(heal_end_sound)
playsound(patient, heal_end_sound, 75, TRUE, MEDIUM_RANGE_SOUND_EXTRARANGE)
return TRUE
/// Callback for limb applicability component
/obj/item/stack/medical/wrap/proc/on_gauze_limb(mob/user, mob/living/patient, obj/item/bodypart/limb)
patient.balloon_alert(user, "wrapped [limb.plaintext_zone]")
user.visible_message(
span_green("[user] applies [src] to [patient]'s [limb.plaintext_zone]."),
span_green("You bandage the wounds on [user == patient ? "your" : "[patient]'s"] [limb.plaintext_zone]."),
visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE,
)
if(limb.cached_bleed_rate)
add_mob_blood(patient)
// Dressing burns provides a "one-time" bonus to sanitization and healing
// However, any notable infection will reduce the effectiveness of this bonus
for(var/datum/wound/burn/flesh/wound in limb.wounds)
wound.sanitization += sanitization * (wound.infection > 0.1 ? 0.2 : 1)
wound.flesh_healing += flesh_regeneration * (wound.infection > 0.1 ? 0 : 1)
/// Used via signal to update wounds
/obj/item/stack/medical/wrap/proc/update_wounds(datum/source, obj/item/bodypart/limb)
SIGNAL_HANDLER
for(var/datum/wound/gauzed as anything in limb.wounds)
gauzed.update_inefficiencies()
/obj/item/stack/medical/wrap/gauze
name = "medical gauze"
desc = "A roll of elastic cloth, perfect for stabilizing all kinds of wounds, from cuts and burns, to broken bones."
singular_name = "medical gauze"
self_delay = 5 SECONDS
other_delay = 2 SECONDS
max_amount = 12
amount = 6
custom_price = PAYCHECK_CREW * 2
absorption_rate = 0.125
absorption_capacity = 5
sanitization = 3
flesh_regeneration = 5
splint_factor = 0.7
burn_cleanliness_bonus = 0.35
merge_type = /obj/item/stack/medical/wrap/gauze
heal_end_sound = SFX_BANDAGE_END
heal_begin_sound = SFX_BANDAGE_BEGIN
drop_sound = SFX_CLOTH_DROP
pickup_sound = SFX_CLOTH_PICKUP
/obj/item/stack/medical/wrap/gauze/grind_results()
return list(/datum/reagent/cellulose = 2)
/obj/item/stack/medical/wrap/gauze/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
. = ..()
if(isnull(held_item))
return
if(held_item.tool_behaviour == TOOL_WIRECUTTER || held_item.get_sharpness())
context[SCREENTIP_CONTEXT_LMB] = "Shred Into Cloth"
return CONTEXTUAL_SCREENTIP_SET
/obj/item/stack/medical/wrap/gauze/twelve
amount = 12
/obj/item/stack/medical/wrap/gauze/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(tool.tool_behaviour == TOOL_WIRECUTTER || tool.get_sharpness())
if(get_amount() < 2)
balloon_alert(user, "not enough gauze!")
return ITEM_INTERACT_BLOCKING
new /obj/item/stack/sheet/cloth(tool.drop_location())
if(IsReachableBy(user))
user.visible_message(span_notice("[user] cuts [src] into pieces of cloth with [tool]."), \
span_notice("You cut [src] into pieces of cloth with [tool]."), \
span_hear("You hear cutting."))
else //telekinesis
visible_message(span_notice("[tool] cuts [src] into pieces of cloth."), \
blind_message = span_hear("You hear cutting."))
use(2)
return ITEM_INTERACT_SUCCESS
return NONE
/obj/item/stack/medical/wrap/gauze/suicide_act(mob/living/user)
user.visible_message(span_suicide("[user] begins tightening [src] around [user.p_their()] neck! It looks like [user.p_they()] forgot how to use medical supplies!"))
return OXYLOSS
/obj/item/stack/medical/wrap/gauze/improvised
name = "improvised gauze"
singular_name = "improvised gauze"
desc = "A roll of cloth roughly cut from something that does a decent job of stabilizing wounds, but less efficiently so than real medical gauze."
icon_state = "gauze_imp"
self_delay = 6 SECONDS
other_delay = 3 SECONDS
splint_factor = 0.85
burn_cleanliness_bonus = 0.7
absorption_rate = 0.075
absorption_capacity = 4
sanitization = 1
flesh_regeneration = 3
merge_type = /obj/item/stack/medical/wrap/gauze/improvised
/*
The idea is for the following medical devices to work like a hybrid of the old brute packs and tend wounds,
they heal a little at a time, have reduced healing density and does not allow for rapid healing while in combat.
However they provice graunular control of where the healing is directed, this makes them better for curing work-related cuts and scrapes.
The interesting limb targeting mechanic is retained and i still believe they will be a viable choice, especially when healing others in the field.
*/
/obj/item/stack/medical/suture
name = "suture"
desc = "Basic sterile sutures used to seal up cuts and lacerations and stop bleeding."
gender = PLURAL
singular_name = "suture"
icon_state = "suture"
self_delay = 3 SECONDS
other_delay = 1 SECONDS
amount = 10
max_amount = 10
repeating = TRUE
heal_brute = 10
stop_bleeding = 0.5
merge_type = /obj/item/stack/medical/suture
apply_verb = "suturing"
drop_sound = SFX_SUTURE_DROP
pickup_sound = SFX_SUTURE_PICKUP
heal_begin_sound = SFX_SUTURE_BEGIN
heal_continuous_sound = SFX_SUTURE_CONTINUOUS
heal_end_sound = SFX_SUTURE_END
/obj/item/stack/medical/suture/grind_results()
return list(/datum/reagent/medicine/spaceacillin = 2)
/obj/item/stack/medical/suture/medicated
name = "medicated suture"
icon_state = "suture_purp"
desc = "A suture infused with drugs that speed up wound healing of the treated laceration."
heal_brute = 15
stop_bleeding = 0.75
merge_type = /obj/item/stack/medical/suture/medicated
/obj/item/stack/medical/suture/medicated/grind_results()
return list(/datum/reagent/medicine/polypyr = 1)
/obj/item/stack/medical/ointment
name = "ointment"
desc = "Basic burn ointment, rated effective for second degree burns with proper bandaging, though it's still an effective stabilizer for worse burns. Not terribly good at outright healing burns though."
gender = PLURAL
singular_name = "ointment"
icon_state = "ointment"
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
amount = 8
max_amount = 8
self_delay = 4 SECONDS
other_delay = 2 SECONDS
heal_burn = 5
flesh_regeneration = 2.5
sanitization = 0.25
merge_type = /obj/item/stack/medical/ointment
apply_verb = "applying to"
/obj/item/stack/medical/ointment/grind_results()
return list(/datum/reagent/medicine/c2/lenturi = 10)
/obj/item/stack/medical/ointment/suicide_act(mob/living/user)
user.visible_message(span_suicide("[user] is squeezing [src] into [user.p_their()] mouth! [user.p_do(TRUE)]n't [user.p_they()] know that stuff is toxic?"))
return TOXLOSS
/obj/item/stack/medical/mesh
name = "regenerative mesh"
desc = "A bacteriostatic mesh used to dress burns."
gender = PLURAL
singular_name = "mesh piece"
icon_state = "regen_mesh"
self_delay = 3 SECONDS
other_delay = 1 SECONDS
amount = 15
heal_burn = 10
max_amount = 15
repeating = TRUE
sanitization = 0.75
flesh_regeneration = 3
pickup_sound = SFX_REGEN_MESH_PICKUP
drop_sound = SFX_REGEN_MESH_DROP
heal_begin_sound = SFX_REGEN_MESH_BEGIN
heal_continuous_sound = SFX_REGEN_MESH_CONTINUOUS
heal_end_sound = SFX_REGEN_MESH_END
merge_type = /obj/item/stack/medical/mesh
///This var determines if the sterile packaging of the mesh has been opened.
var/is_open = TRUE
/obj/item/stack/medical/mesh/Initialize(mapload, new_amount, merge = TRUE, list/mat_override=null, mat_amt=1)
. = ..()
if(amount == max_amount) //only seal full mesh packs
is_open = FALSE
update_appearance()
/obj/item/stack/medical/mesh/grind_results()
return list(/datum/reagent/medicine/spaceacillin = 2)
/obj/item/stack/medical/mesh/update_icon_state()
if(is_open)
return ..()
icon_state = "regen_mesh_closed"
/obj/item/stack/medical/mesh/try_heal_checks(mob/living/patient, mob/living/user, healed_zone, silent = FALSE)
if(!is_open)
if(!silent)
balloon_alert(user, "open it first!")
return FALSE
return ..()
/obj/item/stack/medical/mesh/click_alt(mob/living/user)
if(!is_open)
balloon_alert(user, "open it first!")
return CLICK_ACTION_BLOCKING
return CLICK_ACTION_SUCCESS
/obj/item/stack/medical/mesh/attack_hand(mob/user, list/modifiers)
if(!is_open && user.get_inactive_held_item() == src)
balloon_alert(user, "open it first!")
return
return ..()
/obj/item/stack/medical/mesh/attack_self(mob/user)
if(!is_open)
is_open = TRUE
balloon_alert(user, "opened")
update_appearance()
playsound(src, 'sound/items/poster/poster_ripped.ogg', 20, TRUE)
return
return ..()
/obj/item/stack/medical/mesh/advanced
name = "advanced regenerative mesh"
desc = "An advanced mesh made with aloe extracts and sterilizing chemicals, used to treat burns."
gender = PLURAL
icon_state = "aloe_mesh"
heal_burn = 15
sanitization = 1.25
flesh_regeneration = 3.5
merge_type = /obj/item/stack/medical/mesh/advanced
/obj/item/stack/medical/mesh/advanced/grind_results()
return list(/datum/reagent/consumable/aloejuice = 1)
/obj/item/stack/medical/mesh/advanced/update_icon_state()
if(is_open)
return ..()
icon_state = "aloe_mesh_closed"
/obj/item/stack/medical/aloe
name = "aloe cream"
desc = "A healing paste for minor cuts and burns."
gender = PLURAL
singular_name = "aloe cream"
icon_state = "aloe_paste"
self_delay = 2 SECONDS
other_delay = 1 SECONDS
novariants = TRUE
amount = 20
max_amount = 20
repeating = TRUE
heal_brute = 3
heal_burn = 3
merge_type = /obj/item/stack/medical/aloe
apply_verb = "applying to"
/obj/item/stack/medical/aloe/Initialize(mapload, new_amount, merge, list/mat_override, mat_amt)
. = ..()
AddComponent(/datum/component/bakeable, /obj/item/food/badrecipe, rand(10 SECONDS, 15 SECONDS), FALSE)
/obj/item/stack/medical/aloe/grind_results()
return list(/datum/reagent/consumable/aloejuice = 1)
/obj/item/stack/medical/aloe/fresh
amount = 2
/obj/item/stack/medical/bone_gel
name = "bone gel"
singular_name = "bone gel"
desc = "A potent medical gel that, when applied to a damaged bone in a proper surgical setting, triggers an intense melding reaction to repair the wound. Can be directly applied alongside surgical sticky tape to a broken bone in dire circumstances, though this is very harmful to the patient and not recommended."
icon = 'icons/obj/medical/surgery_tools.dmi'
icon_state = "bone-gel"
inhand_icon_state = "bone-gel"
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
amount = 5
self_delay = 20
novariants = TRUE
merge_type = /obj/item/stack/medical/bone_gel
apply_verb = "applying to"
/obj/item/stack/medical/bone_gel/grind_results()
return list(/datum/reagent/bone_dust = 10, /datum/reagent/carbon = 10)
/obj/item/stack/medical/bone_gel/get_surgery_tool_overlay(tray_extended)
return "gel" + (tray_extended ? "" : "_out")
/obj/item/stack/medical/bone_gel/attack(mob/living/patient, mob/user)
patient.balloon_alert(user, "no fractures!")
return
/obj/item/stack/medical/bone_gel/suicide_act(mob/living/user)
if(!iscarbon(user))
return
var/mob/living/carbon/patient = user
patient.visible_message(span_suicide("[patient] is squirting all of [src] into [patient.p_their()] mouth! That's not proper procedure! It looks like [patient.p_theyre()] trying to commit suicide!"))
if(!do_after(patient, 2 SECONDS))
patient.visible_message(span_suicide("[patient] screws up like an idiot and still dies anyway!"))
return BRUTELOSS
patient.emote("scream")
for(var/obj/item/bodypart/bone as anything in patient.get_bodyparts())
// fine to just, use these raw, its a meme anyway
var/datum/wound/blunt/bone/severe/oof_ouch = new
oof_ouch.apply_wound(bone, wound_source = "bone gel")
var/datum/wound/blunt/bone/critical/oof_OUCH = new
oof_OUCH.apply_wound(bone, wound_source = "bone gel")
for(var/zone in patient.get_all_limbs())
patient.apply_damage(60, BRUTE, zone)
use(1)
return BRUTELOSS
/obj/item/stack/medical/bone_gel/one
amount = 1
/obj/item/stack/medical/poultice
name = "mourning poultices"
singular_name = "mourning poultice"
desc = "A type of primitive herbal poultice.\n\
While traditionally used to prepare corpses for the mourning feast, \
it can also treat scrapes and burns on the living, however, \
it is liable to cause shortness of breath when employed in this manner.\n\
It is imbued with ancient wisdom."
icon_state = "poultice"
amount = 15
max_amount = 15
heal_brute = 10
heal_burn = 10
self_delay = 40
other_delay = 10
repeating = TRUE
drop_sound = 'sound/misc/moist_impact.ogg'
mob_throw_hit_sound = 'sound/misc/moist_impact.ogg'
hitsound = 'sound/misc/moist_impact.ogg'
merge_type = /obj/item/stack/medical/poultice
apply_verb = "applying to"
works_on_dead = TRUE
/obj/item/stack/medical/poultice/post_heal_effects(amount_healed, mob/living/carbon/healed_mob, mob/living/user)
. = ..()
playsound(src, 'sound/misc/soggy.ogg', 30, TRUE)
healed_mob.adjust_oxy_loss(amount_healed)
/obj/item/stack/medical/bandage
name = "first aid bandage"
desc = "A DeForest brand bandage designed for basic first aid on blunt-force trauma."
icon_state = "bandage"
inhand_icon_state = "bandage"
novariants = TRUE
amount = 1
max_amount = 1
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
heal_brute = 25
stop_bleeding = 0.2
self_delay = 3 SECONDS
other_delay = 1 SECONDS
apply_verb = "applying to"
pickup_sound = SFX_CLOTH_PICKUP
/obj/item/stack/medical/bandage/grind_results()
return list(/datum/reagent/medicine/c2/libital = 2)
/obj/item/stack/medical/bandage/makeshift
name = "makeshift bandage"
desc = "A hastily constructed bandage designed for basic first aid on blunt-force trauma."
icon_state = "bandage_makeshift"
icon_state_preview = "bandage_makeshift"
inhand_icon_state = "bandage"
novariants = TRUE