mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-30 02:52:30 +00:00
## About The Pull Request The screen alert for being wounded is deleted Instead, your health doll will now glow red on any (and all) wounded limbs https://github.com/user-attachments/assets/83565684-3e19-4753-8034-d8de6574e2b8 To accomplish this, the doll was refactored a bit. No longer operates off of overlays, now uses vis contents across every limb, and just updates the icon state of all those vis contents ## Why It's Good For The Game Wounds really spam the hell out of you with screen alerts and it often blocks you from seeing more relevant alerts that you care about The mere presence of a minor dislocation on your leg prevents you from noticing that you are no longer breathing. This is a bit troublesome This can be resolved in other ways, of course - adding a priority value to alerts? - but instead, I think we can just make better use of our existing hud elements I find this decently intuitive, at a glance. - The old screen alert gave you a tooltip saying you could click the alert to examine yourself. - Clicking on the health doll examines yourself the exact same way. - So, players may see their doll glowing red, and click on it to self examine, to see the report of them having a wound on their leg or chest or whatever. ## Changelog 🆑 Melbert del: Having any wounds no longer gives you an alert in the top right qol: Having any wounds now make the corresponding bodypart on your health doll (the lil dude on the right side of the screen) glow red. refactor: Refactored how the hud's health doll shows up for humans. Report any oddities /🆑
730 lines
31 KiB
Plaintext
730 lines
31 KiB
Plaintext
/*
|
|
Wounds are specific medical complications that can arise and be applied to (currently) carbons, with a focus on humans. All of the code for and related to this is heavily WIP,
|
|
and the documentation will be slanted towards explaining what each part/piece is leading up to, until such a time as I finish the core implementations. The original design doc
|
|
can be found at https://hackmd.io/@Ryll/r1lb4SOwU
|
|
|
|
Wounds are datums that operate like a mix of diseases, brain traumas, and components, and are applied to a /obj/item/bodypart (preferably attached to a carbon) when they take large spikes of damage
|
|
or under other certain conditions (thrown hard against a wall, sustained exposure to plasma fire, etc). Wounds are categorized by the three following criteria:
|
|
1. Severity: Either MODERATE, SEVERE, or CRITICAL. See the hackmd for more details
|
|
2. Viable zones: What body parts the wound is applicable to. Generic wounds like broken bones and severe burns can apply to every zone, but you may want to add special wounds for certain limbs
|
|
like a twisted ankle for legs only, or open air exposure of the organs for particularly gruesome chest wounds. Wounds should be able to function for every zone they are marked viable for.
|
|
3. Damage type: Currently either BRUTE or BURN. Again, see the hackmd for a breakdown of my plans for each type.
|
|
|
|
When a body part suffers enough damage to get a wound, the severity (determined by a roll or something, worse damage leading to worse wounds), affected limb, and damage type sustained are factored into
|
|
deciding what specific wound will be applied. I'd like to have a few different types of wounds for at least some of the choices, but I'm just doing rough generals for now. Expect polishing
|
|
*/
|
|
|
|
#define WOUND_CRITICAL_BLUNT_DISMEMBER_BONUS 15
|
|
|
|
/datum/wound
|
|
/// What it's named
|
|
var/name = "Wound"
|
|
/// The description shown on the scanners
|
|
var/desc = ""
|
|
/// The basic treatment suggested by health analyzers
|
|
var/treat_text = ""
|
|
/// Even more basic treatment
|
|
var/treat_text_short = ""
|
|
/// What the limb looks like on a cursory examine
|
|
var/examine_desc = "is badly hurt"
|
|
|
|
/// Simple description, shortened for clarity if defined. Otherwise just takes the normal desc in the analyzer proc.
|
|
var/simple_desc
|
|
/// Simple analyzer's wound description, which focuses less on the clinical aspect of the wound and more on easily readable treatment instructions.
|
|
var/simple_treat_text = "Go to medbay idiot"
|
|
/// Improvised remedies indicated by the first aid analyzer only.
|
|
var/homemade_treat_text = "Remember to drink lots of water!"
|
|
|
|
|
|
/// If this wound can generate a scar.
|
|
var/can_scar = TRUE
|
|
|
|
/// The default file we take our scar descriptions from, if we fail to get the ideal file.
|
|
var/default_scar_file
|
|
|
|
/// needed for "your arm has a compound fracture" vs "your arm has some third degree burns"
|
|
var/a_or_from = "a"
|
|
/// The visible message when this happens
|
|
var/occur_text = ""
|
|
/// This sound will be played upon the wound being applied
|
|
var/sound_effect
|
|
/// The volume of [sound_effect]
|
|
var/sound_volume = 70
|
|
|
|
/// Either WOUND_SEVERITY_TRIVIAL (meme wounds like stubbed toe), WOUND_SEVERITY_MODERATE, WOUND_SEVERITY_SEVERE, or WOUND_SEVERITY_CRITICAL (or maybe WOUND_SEVERITY_LOSS)
|
|
var/severity = WOUND_SEVERITY_MODERATE
|
|
|
|
/// Who owns the body part that we're wounding
|
|
var/mob/living/carbon/victim = null
|
|
/// The bodypart we're parented to. Not guaranteed to be non-null, especially after/during removal or if we haven't been applied
|
|
var/obj/item/bodypart/limb = null
|
|
|
|
/// Specific items such as bandages or sutures that can try directly treating this wound
|
|
var/list/treatable_by
|
|
/// Specific items such as bandages or sutures that can try directly treating this wound only if the user has the victim in an aggressive grab or higher
|
|
var/list/treatable_by_grabbed
|
|
/// Any tools with any of the flags in this list will be usable to try directly treating this wound
|
|
var/list/treatable_tools
|
|
/// How long it will take to treat this wound with a standard effective tool, assuming it doesn't need surgery
|
|
var/base_treat_time = 5 SECONDS
|
|
|
|
/// Using this limb in a do_after interaction will multiply the length by this duration (arms)
|
|
var/interaction_efficiency_penalty = 1
|
|
/// Incoming damage on this limb will be multiplied by this, to simulate tenderness and vulnerability (mostly burns).
|
|
var/damage_multiplier_penalty = 1
|
|
/// If set and this wound is applied to a leg, we take this many deciseconds extra per step on this leg
|
|
var/limp_slowdown
|
|
/// If this wound has a limp_slowdown and is applied to a leg, it has this chance to limp each step
|
|
var/limp_chance
|
|
/// How much we're contributing to this limb's bleed_rate
|
|
var/blood_flow
|
|
|
|
/// How much having this wound will add to all future check_wounding() rolls on this limb, to allow progression to worse injuries with repeated damage
|
|
var/threshold_penalty
|
|
/// How much having this wound will add to all future check_wounding() rolls on this limb, but only for wounds of its own series
|
|
var/series_threshold_penalty = 0
|
|
/// If we need to process each life tick
|
|
var/processes = FALSE
|
|
|
|
/// If having this wound makes currently makes the parent bodypart unusable
|
|
var/disabling
|
|
|
|
/// What status effect we assign on application
|
|
var/status_effect_type
|
|
/// If we're operating on this wound and it gets healed, we'll nix the surgery too
|
|
var/datum/surgery/attached_surgery
|
|
/// if you're a lazy git and just throw them in cryo, the wound will go away after accumulating severity * [base_xadone_progress_to_qdel] power
|
|
var/cryo_progress
|
|
|
|
/// The base amount of [cryo_progress] required to have ourselves fully healed by cryo. Multiplied against severity.
|
|
var/base_xadone_progress_to_qdel = 33
|
|
|
|
/// What kind of scars this wound will create description wise once healed
|
|
var/scar_keyword = "generic"
|
|
/// If we've already tried scarring while removing (remove_wound can be called twice in a del chain, let's be nice to our code yeah?) TODO: make this cleaner
|
|
var/already_scarred = FALSE
|
|
/// The source of how we got the wound, typically a weapon.
|
|
var/wound_source
|
|
|
|
/// What flags apply to this wound
|
|
var/wound_flags = (ACCEPTS_GAUZE)
|
|
|
|
/// The unique ID of our wound for use with [actionspeed_mod]. Defaults to REF(src).
|
|
var/unique_id
|
|
/// The actionspeed modifier we will use in case we are on the arms and have a interaction penalty. Qdelled on destroy.
|
|
var/datum/actionspeed_modifier/wound_interaction_inefficiency/actionspeed_mod
|
|
|
|
/datum/wound/New()
|
|
. = ..()
|
|
|
|
unique_id = generate_unique_id()
|
|
update_actionspeed_modifier()
|
|
|
|
/datum/wound/Destroy()
|
|
QDEL_NULL(attached_surgery)
|
|
if (limb)
|
|
remove_wound()
|
|
|
|
QDEL_NULL(actionspeed_mod)
|
|
|
|
return ..()
|
|
|
|
/// If we should have an actionspeed_mod, ensures we do and updates its slowdown. Otherwise, ensures we don't have one
|
|
/// by qdeleting any existing modifier.
|
|
/datum/wound/proc/update_actionspeed_modifier()
|
|
if (should_have_actionspeed_modifier())
|
|
if (!actionspeed_mod)
|
|
generate_actionspeed_modifier()
|
|
actionspeed_mod.multiplicative_slowdown = get_effective_actionspeed_modifier()
|
|
victim?.update_actionspeed()
|
|
else
|
|
remove_actionspeed_modifier()
|
|
|
|
/// Returns TRUE if we have an interaction_efficiency_penalty, and if we are on the arms, FALSE otherwise.
|
|
/datum/wound/proc/should_have_actionspeed_modifier()
|
|
return (limb && victim && (limb.body_zone == BODY_ZONE_L_ARM || limb.body_zone == BODY_ZONE_R_ARM) && interaction_efficiency_penalty != 0)
|
|
|
|
/// If we have no actionspeed_mod, generates a new one with our unique ID, sets actionspeed_mod to it, then returns it.
|
|
/datum/wound/proc/generate_actionspeed_modifier()
|
|
RETURN_TYPE(/datum/actionspeed_modifier)
|
|
|
|
if (actionspeed_mod)
|
|
return actionspeed_mod
|
|
|
|
var/datum/actionspeed_modifier/wound_interaction_inefficiency/new_modifier = new /datum/actionspeed_modifier/wound_interaction_inefficiency(unique_id, src)
|
|
new_modifier.multiplicative_slowdown = get_effective_actionspeed_modifier()
|
|
victim?.add_actionspeed_modifier(new_modifier)
|
|
|
|
actionspeed_mod = new_modifier
|
|
return actionspeed_mod
|
|
|
|
/// If we have an actionspeed_mod, qdels it and sets our ref of it to null.
|
|
/datum/wound/proc/remove_actionspeed_modifier()
|
|
if (!actionspeed_mod)
|
|
return
|
|
|
|
victim?.remove_actionspeed_modifier(actionspeed_mod)
|
|
QDEL_NULL(actionspeed_mod)
|
|
|
|
/// Generates the ID we use for [unique_id], which is also set as our actionspeed mod's ID
|
|
/datum/wound/proc/generate_unique_id()
|
|
return REF(src) // unique, cannot change, a perfect id
|
|
|
|
/**
|
|
* apply_wound() is used once a wound type is instantiated to assign it to a bodypart, and actually come into play.
|
|
*
|
|
*
|
|
* Arguments:
|
|
* * L: The bodypart we're wounding, we don't care about the person, we can get them through the limb
|
|
* * silent: Not actually necessary I don't think, was originally used for demoting wounds so they wouldn't make new messages, but I believe old_wound took over that, I may remove this shortly
|
|
* * old_wound: If our new wound is a replacement for one of the same time (promotion or demotion), we can reference the old one just before it's removed to copy over necessary vars
|
|
* * smited- If this is a smite, we don't care about this wound for stat tracking purposes (not yet implemented)
|
|
* * attack_direction: For bloodsplatters, if relevant
|
|
* * wound_source: The source of the wound, such as a weapon.
|
|
*/
|
|
/datum/wound/proc/apply_wound(obj/item/bodypart/L, silent = FALSE, datum/wound/old_wound = null, smited = FALSE, attack_direction = null, wound_source = "Unknown", replacing = FALSE)
|
|
|
|
if (!can_be_applied_to(L, old_wound))
|
|
qdel(src)
|
|
return FALSE
|
|
|
|
if(isitem(wound_source))
|
|
var/obj/item/wound_item = wound_source
|
|
src.wound_source = wound_item.name
|
|
else
|
|
src.wound_source = wound_source
|
|
|
|
set_victim(L.owner)
|
|
set_limb(L, replacing)
|
|
LAZYADD(victim.all_wounds, src)
|
|
LAZYADD(limb.wounds, src)
|
|
update_descriptions()
|
|
limb.update_wounds()
|
|
if(status_effect_type)
|
|
victim.apply_status_effect(status_effect_type, src)
|
|
SEND_SIGNAL(victim, COMSIG_CARBON_GAIN_WOUND, src, limb)
|
|
victim.update_health_hud()
|
|
|
|
var/demoted
|
|
if(old_wound)
|
|
demoted = (severity <= old_wound.severity)
|
|
|
|
if(severity == WOUND_SEVERITY_TRIVIAL)
|
|
return
|
|
|
|
if(!silent && !demoted)
|
|
var/msg = span_danger("[victim]'s [limb.plaintext_zone] [occur_text]!")
|
|
var/vis_dist = COMBAT_MESSAGE_RANGE
|
|
|
|
if(severity > WOUND_SEVERITY_SEVERE)
|
|
msg = "<b>[msg]</b>"
|
|
vis_dist = DEFAULT_MESSAGE_RANGE
|
|
|
|
victim.visible_message(msg, span_userdanger("Your [limb.plaintext_zone] [occur_text]!"), vision_distance = vis_dist)
|
|
if(sound_effect)
|
|
playsound(L.owner, sound_effect, sound_volume + (20 * severity), TRUE, falloff_exponent = SOUND_FALLOFF_EXPONENT + 2, ignore_walls = FALSE, falloff_distance = 0)
|
|
|
|
wound_injury(old_wound, attack_direction = attack_direction)
|
|
if(!demoted)
|
|
second_wind()
|
|
|
|
return TRUE
|
|
|
|
/// Returns TRUE if we can be applied to the limb.
|
|
/datum/wound/proc/can_be_applied_to(obj/item/bodypart/L, datum/wound/old_wound)
|
|
var/datum/wound_pregen_data/pregen_data = GLOB.all_wound_pregen_data[type]
|
|
|
|
// We assume we aren't being randomly applied - we have no reason to believe we are
|
|
// And, besides, if we were, you could just as easily check our pregen data rather than run this proc
|
|
// Generally speaking this proc is called in apply_wound, which is called when the caller is already confidant in its ability to be applied
|
|
return pregen_data.can_be_applied_to(L, old_wound = old_wound)
|
|
|
|
/// Returns the zones we can be applied to.
|
|
/datum/wound/proc/get_viable_zones()
|
|
var/datum/wound_pregen_data/pregen_data = GLOB.all_wound_pregen_data[type]
|
|
|
|
return pregen_data.viable_zones
|
|
|
|
/// Returns the biostate we require to be applied.
|
|
/datum/wound/proc/get_required_biostate()
|
|
var/datum/wound_pregen_data/pregen_data = GLOB.all_wound_pregen_data[type]
|
|
|
|
return pregen_data.required_limb_biostate
|
|
|
|
// Updates descriptive texts for the wound, in case it can get altered for whatever reason
|
|
/datum/wound/proc/update_descriptions()
|
|
return
|
|
|
|
/datum/wound/proc/null_victim()
|
|
SIGNAL_HANDLER
|
|
set_victim(null)
|
|
|
|
/// Setter for [victim]. Should completely transfer signals, attributes, etc. To the new victim - if there is any, as it can be null.
|
|
/datum/wound/proc/set_victim(new_victim)
|
|
if(victim)
|
|
UnregisterSignal(victim, list(COMSIG_QDELETING, COMSIG_MOB_SWAP_HANDS, COMSIG_CARBON_POST_REMOVE_LIMB, COMSIG_CARBON_POST_ATTACH_LIMB))
|
|
UnregisterSignal(victim, COMSIG_QDELETING)
|
|
UnregisterSignal(victim, COMSIG_MOB_SWAP_HANDS)
|
|
UnregisterSignal(victim, COMSIG_CARBON_POST_REMOVE_LIMB)
|
|
if (actionspeed_mod)
|
|
victim.remove_actionspeed_modifier(actionspeed_mod) // no need to qdelete it, just remove it from our victim
|
|
|
|
remove_wound_from_victim()
|
|
victim = new_victim
|
|
if(victim)
|
|
RegisterSignal(victim, COMSIG_QDELETING, PROC_REF(null_victim))
|
|
RegisterSignals(victim, list(COMSIG_MOB_SWAP_HANDS, COMSIG_CARBON_POST_REMOVE_LIMB, COMSIG_CARBON_POST_ATTACH_LIMB), PROC_REF(add_or_remove_actionspeed_mod))
|
|
|
|
if (limb)
|
|
start_limping_if_we_should() // the status effect already handles removing itself
|
|
add_or_remove_actionspeed_mod()
|
|
|
|
/// Proc called to change the variable `limb` and react to the event.
|
|
/datum/wound/proc/set_limb(obj/item/bodypart/new_value, replaced = FALSE)
|
|
if(limb == new_value)
|
|
return FALSE //Limb can either be a reference to something or `null`. Returning the number variable makes it clear no change was made.
|
|
. = limb
|
|
if(limb) // if we're nulling limb, we're basically detaching from it, so we should remove ourselves in that case
|
|
UnregisterSignal(limb, COMSIG_QDELETING)
|
|
UnregisterSignal(limb, list(COMSIG_BODYPART_GAUZED, COMSIG_BODYPART_UNGAUZED))
|
|
LAZYREMOVE(limb.wounds, src)
|
|
limb.update_wounds(replaced)
|
|
if (disabling)
|
|
limb.remove_traits(list(TRAIT_PARALYSIS, TRAIT_DISABLED_BY_WOUND), REF(src))
|
|
|
|
limb = new_value
|
|
|
|
// POST-CHANGE
|
|
|
|
if (limb)
|
|
RegisterSignal(limb, COMSIG_QDELETING, PROC_REF(source_died))
|
|
RegisterSignals(limb, list(COMSIG_BODYPART_GAUZED, COMSIG_BODYPART_UNGAUZED), PROC_REF(gauze_state_changed))
|
|
if (disabling)
|
|
limb.add_traits(list(TRAIT_PARALYSIS, TRAIT_DISABLED_BY_WOUND), REF(src))
|
|
|
|
if (victim)
|
|
start_limping_if_we_should() // the status effect already handles removing itself
|
|
add_or_remove_actionspeed_mod()
|
|
|
|
update_inefficiencies(replaced)
|
|
|
|
/datum/wound/proc/add_or_remove_actionspeed_mod()
|
|
update_actionspeed_modifier()
|
|
if (actionspeed_mod)
|
|
if(victim.get_active_hand() == limb)
|
|
victim.add_actionspeed_modifier(actionspeed_mod, TRUE)
|
|
else
|
|
victim.remove_actionspeed_modifier(actionspeed_mod)
|
|
|
|
/datum/wound/proc/start_limping_if_we_should()
|
|
if ((limb.body_zone == BODY_ZONE_L_LEG || limb.body_zone == BODY_ZONE_R_LEG) && limp_slowdown > 0 && limp_chance > 0)
|
|
victim.apply_status_effect(/datum/status_effect/limp)
|
|
|
|
/datum/wound/proc/source_died()
|
|
SIGNAL_HANDLER
|
|
qdel(src)
|
|
|
|
/// Remove the wound from whatever it's afflicting, and cleans up whatever status effects it had or modifiers it had on interaction times. ignore_limb is used for detachments where we only want to forget the victim
|
|
/datum/wound/proc/remove_wound(ignore_limb, replaced = FALSE)
|
|
//TODO: have better way to tell if we're getting removed without replacement (full heal) scar stuff
|
|
var/old_victim = victim
|
|
var/old_limb = limb
|
|
|
|
set_disabling(FALSE)
|
|
if(limb && can_scar && !already_scarred && !replaced)
|
|
already_scarred = TRUE
|
|
var/datum/scar/new_scar = new
|
|
new_scar.generate(limb, src)
|
|
|
|
remove_actionspeed_modifier()
|
|
|
|
null_victim() // we use the proc here because some behaviors may depend on changing victim to some new value
|
|
|
|
if(limb && !ignore_limb)
|
|
set_limb(null, replaced) // since we're removing limb's ref to us, we should do the same
|
|
// if you want to keep the ref, do it externally, there's no reason for us to remember it
|
|
|
|
if (ismob(old_victim))
|
|
var/mob/mob_victim = old_victim
|
|
SEND_SIGNAL(mob_victim, COMSIG_CARBON_POST_LOSE_WOUND, src, old_limb, ignore_limb, replaced)
|
|
if(!replaced && !limb)
|
|
mob_victim.update_health_hud()
|
|
|
|
/datum/wound/proc/remove_wound_from_victim()
|
|
if(!victim)
|
|
return
|
|
LAZYREMOVE(victim.all_wounds, src)
|
|
SEND_SIGNAL(victim, COMSIG_CARBON_LOSE_WOUND, src, limb)
|
|
|
|
/**
|
|
* replace_wound() is used when you want to replace the current wound with a new wound, presumably of the same category, just of a different severity (either up or down counts)
|
|
*
|
|
* Arguments:
|
|
* * new_wound- The wound instance you want to replace this
|
|
* * smited- If this is a smite, we don't care about this wound for stat tracking purposes (not yet implemented)
|
|
*/
|
|
/datum/wound/proc/replace_wound(datum/wound/new_wound, smited = FALSE, attack_direction = attack_direction)
|
|
already_scarred = TRUE
|
|
var/obj/item/bodypart/cached_limb = limb // remove_wound() nulls limb so we have to track it locally
|
|
remove_wound(replaced=TRUE)
|
|
new_wound.apply_wound(cached_limb, old_wound = src, smited = smited, attack_direction = attack_direction, wound_source = wound_source, replacing = TRUE)
|
|
. = new_wound
|
|
qdel(src)
|
|
|
|
/// The immediate negative effects faced as a result of the wound
|
|
/datum/wound/proc/wound_injury(datum/wound/old_wound = null, attack_direction = null)
|
|
return
|
|
|
|
/// Proc called to change the variable `disabling` and react to the event.
|
|
/datum/wound/proc/set_disabling(new_value)
|
|
if(disabling == new_value)
|
|
return
|
|
. = disabling
|
|
disabling = new_value
|
|
if(disabling)
|
|
if(!. && limb) //Gained disabling.
|
|
limb.add_traits(list(TRAIT_PARALYSIS, TRAIT_DISABLED_BY_WOUND), REF(src))
|
|
else if(. && limb) //Lost disabling.
|
|
limb.remove_traits(list(TRAIT_PARALYSIS, TRAIT_DISABLED_BY_WOUND), REF(src))
|
|
if(limb?.can_be_disabled)
|
|
limb.update_disabled()
|
|
|
|
/// Setter for [interaction_efficiency_penalty]. Updates the actionspeed of our actionspeed mod.
|
|
/datum/wound/proc/set_interaction_efficiency_penalty(new_value)
|
|
var/should_update = (new_value != interaction_efficiency_penalty)
|
|
|
|
interaction_efficiency_penalty = new_value
|
|
|
|
if (should_update)
|
|
update_actionspeed_modifier()
|
|
|
|
/// Returns a "adjusted" interaction_efficiency_penalty that will be used for the actionspeed mod.
|
|
/datum/wound/proc/get_effective_actionspeed_modifier()
|
|
return interaction_efficiency_penalty - 1
|
|
|
|
/// Returns the decisecond multiplier of any click interactions, assuming our limb is being used.
|
|
/datum/wound/proc/get_action_delay_mult()
|
|
SHOULD_BE_PURE(TRUE)
|
|
|
|
return interaction_efficiency_penalty
|
|
|
|
/// Returns the decisecond increment of any click interactions, assuming our limb is being used.
|
|
/datum/wound/proc/get_action_delay_increment()
|
|
SHOULD_BE_PURE(TRUE)
|
|
|
|
return 0
|
|
|
|
/// Signal proc for if gauze has been applied or removed from our limb.
|
|
/datum/wound/proc/gauze_state_changed()
|
|
SIGNAL_HANDLER
|
|
|
|
if (wound_flags & ACCEPTS_GAUZE)
|
|
update_inefficiencies()
|
|
|
|
/// Updates our limping and interaction penalties in accordance with our gauze.
|
|
/datum/wound/proc/update_inefficiencies(replaced_or_replacing = FALSE)
|
|
if (wound_flags & ACCEPTS_GAUZE)
|
|
if(limb.body_zone in list(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG))
|
|
if(limb.current_gauze?.splint_factor)
|
|
limp_slowdown = initial(limp_slowdown) * limb.current_gauze.splint_factor
|
|
limp_chance = initial(limp_chance) * limb.current_gauze.splint_factor
|
|
else
|
|
limp_slowdown = initial(limp_slowdown)
|
|
limp_chance = initial(limp_chance)
|
|
else if(limb.body_zone in GLOB.arm_zones)
|
|
if(limb.current_gauze?.splint_factor)
|
|
set_interaction_efficiency_penalty(1 + ((get_effective_actionspeed_modifier()) * limb.current_gauze.splint_factor))
|
|
else
|
|
set_interaction_efficiency_penalty(initial(interaction_efficiency_penalty))
|
|
|
|
if(initial(disabling))
|
|
set_disabling(isnull(limb.current_gauze))
|
|
|
|
limb.update_wounds(replaced_or_replacing)
|
|
|
|
start_limping_if_we_should()
|
|
|
|
/// Additional beneficial effects when the wound is gained, in case you want to give a temporary boost to allow the victim to try an escape or last stand
|
|
/datum/wound/proc/second_wind()
|
|
switch(severity)
|
|
if(WOUND_SEVERITY_MODERATE)
|
|
victim.reagents.add_reagent(/datum/reagent/determination, WOUND_DETERMINATION_MODERATE)
|
|
if(WOUND_SEVERITY_SEVERE)
|
|
victim.reagents.add_reagent(/datum/reagent/determination, WOUND_DETERMINATION_SEVERE)
|
|
if(WOUND_SEVERITY_CRITICAL)
|
|
victim.reagents.add_reagent(/datum/reagent/determination, WOUND_DETERMINATION_CRITICAL)
|
|
if(WOUND_SEVERITY_LOSS)
|
|
victim.reagents.add_reagent(/datum/reagent/determination, WOUND_DETERMINATION_LOSS)
|
|
|
|
/**
|
|
* try_treating() is an intercept run from [/mob/living/carbon/proc/attackby] right after surgeries but before anything else. Return TRUE here if the item is something that is relevant to treatment to take over the interaction.
|
|
*
|
|
* This proc leads into [/datum/wound/proc/treat] and probably shouldn't be added onto in children types. You can specify what items or tools you want to be intercepted
|
|
* with var/list/treatable_by and var/treatable_tool, then if an item fulfills one of those requirements and our wound claims it first, it goes over to treat() and treat_self().
|
|
*
|
|
* Arguments:
|
|
* * I: The item we're trying to use
|
|
* * user: The mob trying to use it on us
|
|
*/
|
|
/datum/wound/proc/try_treating(obj/item/I, mob/user)
|
|
// first we weed out if we're not dealing with our wound's bodypart, or if it might be an attack
|
|
if(!I || limb.body_zone != user.zone_selected)
|
|
return FALSE
|
|
|
|
if(isliving(user))
|
|
var/mob/living/tendee = user
|
|
if(I.force && tendee.combat_mode)
|
|
return FALSE
|
|
|
|
if(!item_can_treat(I, user))
|
|
return FALSE
|
|
|
|
// now that we've determined we have a valid attempt at treating, we can stomp on their dreams if we're already interacting with the patient or if their part is obscured
|
|
if(DOING_INTERACTION_WITH_TARGET(user, victim))
|
|
to_chat(user, span_warning("You're already interacting with [victim]!"))
|
|
return TRUE
|
|
|
|
// next we check if the bodypart in actually accessible (not under thick clothing). We skip the species trait check since skellies
|
|
// & such may need to use bone gel but may be wearing a space suit for..... whatever reason a skeleton would wear a space suit for
|
|
if(ishuman(victim))
|
|
var/mob/living/carbon/human/victim_human = victim
|
|
if(!victim_human.try_inject(user, injection_flags = INJECT_CHECK_IGNORE_SPECIES | INJECT_TRY_SHOW_ERROR_MESSAGE))
|
|
return TRUE
|
|
|
|
// lastly, treat them
|
|
return treat(I, user) // we allow treat to return a value so it can control if the item does its normal interaction or not
|
|
|
|
/// Returns TRUE if the item can be used to treat our wounds. Hooks into treat() - only things that return TRUE here may be used there.
|
|
/datum/wound/proc/item_can_treat(obj/item/potential_treater, mob/user)
|
|
// check if we have a valid treatable tool
|
|
if(potential_treater.tool_behaviour in treatable_tools)
|
|
return TRUE
|
|
if((TOOL_CAUTERY in treatable_tools) && potential_treater.get_temperature() && (user == victim)) // allow improvised cauterization on yourself without an aggro grab
|
|
return TRUE
|
|
// failing that, see if we're aggro grabbing them and if we have an item that works for aggro grabs only
|
|
if(user.pulling == victim && user.grab_state >= GRAB_AGGRESSIVE && check_grab_treatments(potential_treater, user))
|
|
return TRUE
|
|
// failing THAT, we check if we have a generally allowed item
|
|
for(var/allowed_type in treatable_by)
|
|
if(istype(potential_treater, allowed_type))
|
|
return TRUE
|
|
|
|
/// Return TRUE if we have an item that can only be used while aggro grabbed (unhanded aggro grab treatments go in [/datum/wound/proc/try_handling]). Treatment is still is handled in [/datum/wound/proc/treat]
|
|
/datum/wound/proc/check_grab_treatments(obj/item/I, mob/user)
|
|
return FALSE
|
|
|
|
/// Like try_treating() but for unhanded interactions, used by joint dislocations for manual bodypart chiropractice for example. Ignores thick material checks since you can pop an arm into place through a thick suit unlike using sutures
|
|
/datum/wound/proc/try_handling(mob/living/user)
|
|
return FALSE
|
|
|
|
/// Someone is using something that might be used for treating the wound on this limb
|
|
/datum/wound/proc/treat(obj/item/I, mob/user)
|
|
return
|
|
|
|
/// If var/processing is TRUE, this is run on each life tick
|
|
/datum/wound/proc/handle_process(seconds_per_tick, times_fired)
|
|
return
|
|
|
|
/// For use in do_after callback checks
|
|
/datum/wound/proc/still_exists()
|
|
return (!QDELETED(src) && limb)
|
|
|
|
/// When our parent bodypart is hurt.
|
|
/datum/wound/proc/receive_damage(wounding_type, wounding_dmg, wound_bonus, attack_direction, damage_source)
|
|
return
|
|
|
|
/// Called from cryoxadone and pyroxadone when they're proc'ing. Wounds will slowly be fixed separately from other methods when these are in effect. crappy name but eh
|
|
/datum/wound/proc/on_xadone(power)
|
|
cryo_progress += power
|
|
|
|
return handle_xadone_progress()
|
|
|
|
/// Does various actions based on [cryo_progress]. By default, qdeletes the wound past a certain threshold.
|
|
/datum/wound/proc/handle_xadone_progress()
|
|
if(cryo_progress > get_xadone_progress_to_qdel())
|
|
qdel(src)
|
|
|
|
/// Returns the amount of [cryo_progress] we need to be qdeleted.
|
|
/datum/wound/proc/get_xadone_progress_to_qdel()
|
|
SHOULD_BE_PURE(TRUE)
|
|
|
|
return base_xadone_progress_to_qdel * severity
|
|
|
|
/// When synthflesh is applied to the victim, we call this. No sense in setting up an entire chem reaction system for wounds when we only care for a few chems. Probably will change in the future
|
|
/datum/wound/proc/on_synthflesh(reac_volume)
|
|
return
|
|
|
|
/// Called when the patient is undergoing stasis, so that having fully treated a wound doesn't make you sit there helplessly until you think to unbuckle them
|
|
/datum/wound/proc/on_stasis(seconds_per_tick, times_fired)
|
|
return
|
|
|
|
/// Sets our blood flow
|
|
/datum/wound/proc/set_blood_flow(set_to)
|
|
adjust_blood_flow(set_to - blood_flow)
|
|
|
|
/// Use this to modify blood flow. You must use this to change the variable
|
|
/// Takes the amount to adjust by, and the lowest amount we're allowed to have post adjust
|
|
/datum/wound/proc/adjust_blood_flow(adjust_by, minimum = 0)
|
|
if(!adjust_by)
|
|
return
|
|
var/old_flow = blood_flow
|
|
blood_flow = max(blood_flow + adjust_by, minimum)
|
|
|
|
if(old_flow == blood_flow)
|
|
return
|
|
|
|
/// Update our bleed rate
|
|
limb.refresh_bleed_rate()
|
|
|
|
/// Used when we're being dragged while bleeding, the value we return is how much bloodloss this wound causes from being dragged. Since it's a proc, you can let bandages soak some of the blood
|
|
/datum/wound/proc/drag_bleed_amount()
|
|
return
|
|
|
|
/**
|
|
* get_bleed_rate_of_change() is used in [/mob/living/carbon/proc/bleed_warn] to gauge whether this wound (if bleeding) is becoming worse, better, or staying the same over time
|
|
*
|
|
* Returns BLOOD_FLOW_STEADY if we're not bleeding or there's no change (like piercing), BLOOD_FLOW_DECREASING if we're clotting (non-critical slashes, gauzed, coagulant, etc), BLOOD_FLOW_INCREASING if we're opening up (crit slashes/heparin/nitrous oxide)
|
|
*/
|
|
/datum/wound/proc/get_bleed_rate_of_change()
|
|
if(blood_flow && HAS_TRAIT(victim, TRAIT_BLOODY_MESS))
|
|
return BLOOD_FLOW_INCREASING
|
|
return BLOOD_FLOW_STEADY
|
|
|
|
/**
|
|
* get_examine_description() is used in carbon/examine and human/examine to show the status of this wound. Useful if you need to show some status like the wound being splinted or bandaged.
|
|
*
|
|
* Return the full string line you want to show, note that we're already dealing with the 'warning' span at this point, and that \n is already appended for you in the place this is called from
|
|
*
|
|
* Arguments:
|
|
* * mob/user: The user examining the wound's owner, if that matters
|
|
*/
|
|
/datum/wound/proc/get_examine_description(mob/user)
|
|
. = get_wound_description(user)
|
|
if(HAS_TRAIT(src, TRAIT_WOUND_SCANNED))
|
|
. += span_notice("\nThere is a holo-image next to the wound that seems to contain indications for treatment.")
|
|
|
|
return .
|
|
|
|
/datum/wound/proc/get_wound_description(mob/user)
|
|
var/desc
|
|
|
|
if ((wound_flags & ACCEPTS_GAUZE) && limb.current_gauze)
|
|
var/sling_condition = get_gauze_condition()
|
|
desc = "[victim.p_Their()] [limb.plaintext_zone] is [sling_condition] fastened in a sling of [limb.current_gauze.name]"
|
|
else
|
|
desc = "[victim.p_Their()] [limb.plaintext_zone] [examine_desc]"
|
|
|
|
desc = modify_desc_before_span(desc, user)
|
|
|
|
return get_desc_intensity(desc)
|
|
|
|
/// A hook proc used to modify desc before it is spanned via [get_desc_intensity]. Useful for inserting spans yourself.
|
|
/datum/wound/proc/modify_desc_before_span(desc, mob/user)
|
|
return desc
|
|
|
|
/datum/wound/proc/get_gauze_condition()
|
|
SHOULD_BE_PURE(TRUE)
|
|
if (!limb.current_gauze)
|
|
return null
|
|
|
|
switch(limb.current_gauze.absorption_capacity)
|
|
if(0 to 1.25)
|
|
return "just barely"
|
|
if(1.25 to 2.75)
|
|
return "loosely"
|
|
if(2.75 to 4)
|
|
return "mostly"
|
|
if(4 to INFINITY)
|
|
return "tightly"
|
|
|
|
/// Spans [desc] based on our severity.
|
|
/datum/wound/proc/get_desc_intensity(desc)
|
|
SHOULD_BE_PURE(TRUE)
|
|
if (severity > WOUND_SEVERITY_MODERATE)
|
|
return span_bold("[desc]!")
|
|
return "[desc]."
|
|
|
|
/**
|
|
* Prints the details about the wound for the wound scanner on simple mode
|
|
*/
|
|
/datum/wound/proc/get_scanner_description(mob/user)
|
|
return "Type: [name]<br>\
|
|
Severity: [severity_text()]<br>\
|
|
Description: [desc]<br>\
|
|
Recommended Treatment: [treat_text]"
|
|
|
|
/**
|
|
* Prints the details about the wound for the wound scanner on complex mode
|
|
*/
|
|
/datum/wound/proc/get_simple_scanner_description(mob/user)
|
|
var/severity_text_formatted = severity_text()
|
|
for(var/i in 1 to severity)
|
|
severity_text_formatted += "!"
|
|
|
|
return "[name] detected!<br>\
|
|
Risk: [severity_text_formatted]<br>\
|
|
Description: [simple_desc || desc]<br>\
|
|
<i>Treatment Guide: [simple_treat_text]</i><br>\
|
|
<i>Homemade Remedies: [homemade_treat_text]</i>"
|
|
|
|
/**
|
|
* Returns what text describes this wound
|
|
*/
|
|
/datum/wound/proc/severity_text()
|
|
switch(severity)
|
|
if(WOUND_SEVERITY_TRIVIAL)
|
|
return "Trivial"
|
|
if(WOUND_SEVERITY_MODERATE)
|
|
return "Moderate"
|
|
if(WOUND_SEVERITY_SEVERE)
|
|
return "<b>Severe</b>"
|
|
if(WOUND_SEVERITY_CRITICAL)
|
|
return "<b>Critical</b>"
|
|
|
|
/// Returns TRUE if our limb is the head or chest, FALSE otherwise.
|
|
/// Essential in the sense of "we cannot live without it".
|
|
/datum/wound/proc/limb_essential()
|
|
return (limb.body_zone == BODY_ZONE_HEAD || limb.body_zone == BODY_ZONE_CHEST)
|
|
|
|
/// Getter proc for our scar_keyword, in case we might have some custom scar gen logic.
|
|
/datum/wound/proc/get_scar_keyword(obj/item/bodypart/scarred_limb, add_to_scars)
|
|
return scar_keyword
|
|
|
|
/// Getter proc for our scar_file, in case we might have some custom scar gen logic.
|
|
/datum/wound/proc/get_scar_file(obj/item/bodypart/scarred_limb, add_to_scars)
|
|
var/datum/wound_pregen_data/pregen_data = get_pregen_data()
|
|
// basically we iterate over biotypes until we find the one we want
|
|
// fleshy burns will look for flesh then bone
|
|
// dislocations will look for flesh, then bone, then metal
|
|
var/file = default_scar_file
|
|
for (var/biotype as anything in pregen_data.scar_priorities)
|
|
if (scarred_limb.biological_state & text2num(biotype))
|
|
file = GLOB.biotypes_to_scar_file[biotype]
|
|
break
|
|
|
|
return file
|
|
|
|
/// Returns what string is displayed when a limb that has sustained this wound is examined
|
|
/// (This is examining the LIMB ITSELF, when it's not attached to someone.)
|
|
/datum/wound/proc/get_limb_examine_description()
|
|
return
|
|
|
|
/// Gets the flat percentage chance increment of a dismember occurring, if a dismember is attempted (requires mangled flesh and bone). returning 15 = +15%.
|
|
/datum/wound/proc/get_dismember_chance_bonus(existing_chance)
|
|
SHOULD_BE_PURE(TRUE)
|
|
|
|
var/datum/wound_pregen_data/pregen_data = get_pregen_data()
|
|
|
|
if ((WOUND_BLUNT in pregen_data.required_wounding_types) && severity >= WOUND_SEVERITY_CRITICAL)
|
|
return WOUND_CRITICAL_BLUNT_DISMEMBER_BONUS // we only require mangled bone (T2 blunt), but if there's a critical blunt, we'll add 15% more
|
|
|
|
/// Returns our pregen data, which is practically guaranteed to exist, so this proc can safely be used raw.
|
|
/// In fact, since it's RETURN_TYPEd to wound_pregen_data, you can even directly access the variables without having to store the value of this proc in a typed variable.
|
|
/// Ex. get_pregen_data().wound_series
|
|
/datum/wound/proc/get_pregen_data()
|
|
RETURN_TYPE(/datum/wound_pregen_data)
|
|
|
|
return GLOB.all_wound_pregen_data[type]
|
|
|
|
#undef WOUND_CRITICAL_BLUNT_DISMEMBER_BONUS
|