diff --git a/code/__HELPERS/mob_helpers.dm b/code/__HELPERS/mob_helpers.dm
index 1728382defb..fb2e078e9fd 100644
--- a/code/__HELPERS/mob_helpers.dm
+++ b/code/__HELPERS/mob_helpers.dm
@@ -498,6 +498,14 @@ GLOBAL_LIST_EMPTY(do_after_once_tracker)
. = do_after(user, delay, needhand, target, progress, allow_moving, must_be_held, extra_checks = list(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(do_after_once_checks), cache_key, hidden)), interaction_key = interaction_key)
GLOB.do_after_once_tracker[cache_key] = FALSE
+// Please don't use this unless you absolutely need to. Just have a direct call to do_after_once whenever possible.
+/proc/interrupt_do_after_once(mob/user, atom/target, special_identifier)
+ var/cache_key = "[user.UID()][target.UID()][special_identifier]"
+ if(GLOB.do_after_once_tracker[cache_key])
+ GLOB.do_after_once_tracker[cache_key] = DOAFTERONCE_MAGIC
+ return TRUE
+ return FALSE
+
/proc/do_after_once_checks(cache_key)
if(GLOB.do_after_once_tracker[cache_key] && GLOB.do_after_once_tracker[cache_key] == DOAFTERONCE_MAGIC)
GLOB.do_after_once_tracker[cache_key] = FALSE
diff --git a/code/__HELPERS/pronouns.dm b/code/__HELPERS/pronouns.dm
index 2b3d41437c8..7d0c7e07487 100644
--- a/code/__HELPERS/pronouns.dm
+++ b/code/__HELPERS/pronouns.dm
@@ -248,6 +248,44 @@
if(temp_gender != PLURAL)
. = "s"
+// Internal dialogue pronouns. This allow replacements of "their" with "your", if the user and target are the same. Src should always be considered the "target".
+/mob/proc/i_you(user, capitalized)
+ ASSERT(user)
+ if(src != user)
+ return p_they(capitalized)
+ . = "you"
+ if(capitalized)
+ . = capitalize(.)
+
+/mob/proc/i_your(user, capitalized)
+ ASSERT(user)
+ if(src != user)
+ return p_their(capitalized)
+ . = "your"
+ if(capitalized)
+ . = capitalize(.)
+
+/mob/proc/i_yourself(user, capitalized)
+ ASSERT(user)
+ if(src != user)
+ return src // don't say "himself", just refer to them by name
+ . = "yourself"
+ if(capitalized)
+ . = capitalize(.)
+
+/mob/proc/i_do(user)
+ ASSERT(user)
+ if(src != user)
+ return p_do()
+ . = "do"
+
+// External dialogue pronouns, similar to internal dialogue pronouns but for when the observer is a 3rd party. These should only really be used in visible_message()
+/mob/proc/e_themselves(user, capitalized)
+ ASSERT(user)
+ if(src != user)
+ return src // refer to them by name, since its user acting on src
+ return p_themselves(capitalized)
+
//////////////////////////////
// MARK: Human procs
//////////////////////////////
diff --git a/code/game/objects/items/stacks/medical_packs.dm b/code/game/objects/items/stacks/medical_packs.dm
index e3840dbda7d..df59f896313 100644
--- a/code/game/objects/items/stacks/medical_packs.dm
+++ b/code/game/objects/items/stacks/medical_packs.dm
@@ -368,80 +368,95 @@
icon_state = "suture"
gender = PLURAL
merge_type = /obj/item/stack/medical/suture
- healverb = "suturing"
amount = 10
max_amount = 10
heal_brute = 10
stop_bleeding = 1200
dynamic_icon_state = TRUE
delay = 1 SECONDS
+ healverb = "suturing"
+ var/healverb_past = "sutured"
var/self_delay = 3 SECONDS
- var/applying = FALSE
+ var/mob/current_target
/obj/item/stack/medical/suture/apply(mob/living/carbon/human/target, mob/user)
. = TRUE
- if(applying)
- to_chat(user, "You're already applying [src].")
+ if(current_target)
+ if(current_target != target)
+ to_chat(user, "You're already suturing [current_target].")
+ return
+ // Allow for cancelling of the target by clicking again
+ interrupt_do_after_once(user, current_target)
+ to_chat(user, "You stop [healverb] [target.i_yourself(user)].")
return
if(!ishuman(target) || !target.can_inject(user, TRUE))
return
var/heal_type = (heal_brute ? BRUTE : BURN)
var/heal_display = (heal_brute ? BRUTE : "burn")
if(!target.get_damage_amount(heal_type))
- if(target == user)
- to_chat(user, "You don't have any [heal_display] damage.")
- else
- to_chat(user, "They don't have any [heal_display] damage.")
+ to_chat(user, "[target.i_you(user, TRUE)] [target.i_do(user)]n't have any [heal_display] damage.")
return
var/obj/item/organ/external/current_limb = target.get_organ(user.zone_selected)
if(!current_limb)
- to_chat(user, "That limb is missing!")
+ to_chat(user, "That limb is missing!")
return
if(current_limb.is_robotic())
- to_chat(user, "This can't be used on a robotic limb.")
+ to_chat(user, "This can't be used on a robotic limb.")
+ return
+ // Swap to another limb if the current one has no damage.
+ if(!most_damaged_limb(target))
+ to_chat(user, "[target.i_you(user, TRUE)] [target.i_do(user)]n't have any [heal_display] damage that could be [healverb_past].")
return
- if((heal_brute > 0 && !current_limb.brute_dam) || (heal_burn > 0 && !current_limb.burn_dam))
- if(!do_after(user, delay / 2, target = target))
- return
- current_limb = most_damaged_limb(target)
var/mend_time = delay
if(target == user)
- target.visible_message("[user] begins [healverb] [user.p_themselves()] with [src].", "You begin [healverb] your [current_limb.name] with [src].")
mend_time = self_delay
- else
- user.visible_message("[user] begins [healverb] [target] with [src].", "You begin [healverb] [target.p_their()] [current_limb.name] with [src].")
- applying = TRUE
- while(do_after(user, mend_time, target = target))
- if(!target.can_inject(user, TRUE))
+ user.visible_message("[user] begins [healverb] [target.e_themselves(user)] with [src].", "You begin [healverb] [target.i_your(user)] [current_limb.name] with [src].")
+ current_limb = try_swap_to_most_damaged_limb(target, user, current_limb)
+ if(!current_limb)
+ return
+
+ var/last_targetted_zone = user.zone_selected
+ current_target = target
+ while(do_after_once(user, mend_time, target = target))
+ if(!target.can_inject(user, TRUE, current_limb.limb_name))
break
+
var/cut_open = FALSE
if(stop_bleeding)
for(var/obj/item/organ/external/E in target.bodyparts)
if(E.open >= ORGAN_ORGANIC_OPEN)
cut_open = TRUE
- to_chat(user, "[target]'s [E.name] is cut open, you'll need more than some [name] to stop their bleeding.")
+ to_chat(user, "[target]'s [E.name] is cut open, you'll need more than some [name] to stop [target.i_your(user)] bleeding.")
break
if(!apply_to(target, user, current_limb, !cut_open))
break
if(is_zero_amount(TRUE))
break
- if((heal_brute > 0 && !current_limb.brute_dam) || (heal_burn > 0 && !current_limb.burn_dam))
- if(!target.get_damage_amount(heal_type))
- if(target == user)
- target.visible_message("[user] finishes [healverb] [user.p_themselves()] with [src].", "You finish [healverb] yourself with [src].")
- else
- user.visible_message("[user] finishes [healverb] [target] with [src].", "You finish [healverb] [target] with [src].")
- break
- if(!do_after(user, delay / 2, target = target))
- break
- current_limb = most_damaged_limb(target)
- to_chat(user, "You begin [healverb] [target == user ? "your" : target.p_their()] [current_limb.name] with [src].")
- if(!target.can_inject(user, TRUE))
+ if(!target.get_damage_amount(heal_type))
+ user.visible_message("[user] finishes [healverb] [target.e_themselves(user)] with [src].", "You finish [healverb] [target.i_yourself(user)] with [src].")
break
- applying = FALSE
+
+ var/skip_swap = FALSE
+ if(last_targetted_zone != user.zone_selected)
+ last_targetted_zone = user.zone_selected
+ var/new_limb = on_target_zone_change(target, user)
+ if(new_limb && current_limb != new_limb)
+ current_limb = new_limb
+ skip_swap = TRUE
+ to_chat(user, "You switch to [healverb] [target.i_your(user)] [current_limb.name].")
+
+ if(!skip_swap)
+ current_limb = try_swap_to_most_damaged_limb(target, user, current_limb)
+ if(!current_limb)
+ user.visible_message("[user] finishes [healverb] [target.e_themselves(user)] with [src].", "You finish [healverb] [target.i_yourself(user)] with [src].")
+ break
+
+ if(!target.can_inject(user, TRUE, current_limb.limb_name))
+ break
+ current_target = null
user.changeNext_move(CLICK_CD_MELEE)
/obj/item/stack/medical/suture/proc/apply_to(mob/living/carbon/human/target, mob/user, obj/item/organ/external/current_limb, allow_stop_bleeding = TRUE)
@@ -449,6 +464,7 @@
return FALSE
current_limb.heal_damage(heal_brute, heal_burn, FALSE, FALSE, updating_health = TRUE)
+ target.UpdateDamageIcon()
if(stop_bleeding && !target.bleedsuppress && allow_stop_bleeding)
target.suppress_bloodloss(stop_bleeding)
@@ -459,6 +475,10 @@
var/most_damaged_amount = 0
var/focus_brute = (heal_brute > 0)
for(var/obj/item/organ/external/E in target.bodyparts)
+ if(QDELETED(E))
+ continue
+ if(E.is_robotic())
+ continue
if(focus_brute)
if(E.brute_dam > most_damaged_amount)
most_damaged = E
@@ -468,10 +488,41 @@
most_damaged_amount = E.burn_dam
return most_damaged
+/obj/item/stack/medical/suture/proc/try_swap_to_most_damaged_limb(mob/living/carbon/human/target, mob/user, obj/item/organ/external/current_limb)
+ if(heal_brute && current_limb.brute_dam)
+ return current_limb
+ if(heal_burn && current_limb.burn_dam)
+ return current_limb
+
+ var/obj/item/organ/external/new_limb = most_damaged_limb(target)
+ if(!new_limb)
+ return
+ if(current_limb != new_limb)
+ to_chat(user, "You begin [healverb] [target.i_your(user)] [new_limb.name].")
+ if(!do_after(user, delay / 2, target = target))
+ return
+ return new_limb
+
+/obj/item/stack/medical/suture/proc/on_target_zone_change(mob/living/carbon/human/target, mob/user)
+ var/obj/item/organ/external/new_limb = target.get_organ(user.zone_selected)
+ if(!new_limb)
+ to_chat(user, "That limb is missing!")
+ return FALSE
+ if(new_limb.is_robotic())
+ to_chat(user, "This can't be used on a robotic limb.")
+ return FALSE
+
+ if(heal_brute && new_limb.brute_dam)
+ return new_limb
+ if(heal_burn && new_limb.burn_dam)
+ return new_limb
+ return FALSE
+
/obj/item/stack/medical/suture/emergency
name = "emergency sutures"
singular_name = "emergency suture"
desc = "A small bundle of cheap sutures. They're not pretty, but still quite effective at keeping the blood inside your body."
+ icon_state = "suture_emer"
merge_type = /obj/item/stack/medical/suture/emergency
heal_brute = 5
@@ -491,6 +542,7 @@
icon_state = "regen_mesh"
merge_type = /obj/item/stack/medical/suture/regen_mesh
healverb = "grafting"
+ healverb_past = "grafted"
heal_brute = 0
heal_burn = 10
/// This var determines if the sterile packaging of the mesh has been opened.
diff --git a/icons/obj/medical.dmi b/icons/obj/medical.dmi
index 348a22f61ca..23f67c06c1c 100644
Binary files a/icons/obj/medical.dmi and b/icons/obj/medical.dmi differ