[TEST-MERGE FIRST] Wound refactor number two: Full synthetic support (#78124)

## About The Pull Request

Heavily refactors wounds AGAIN.

The primary thing this PR does is completely change how wounds are
generated and added - while the normal "hit a guy til they bleed" stuff
works about the same, asking for a specific type of wound, say, like how
vending machines try to apply a compound fracture sometimes, isnt going
to work if we ever get any non-organic wounds, which the previous
refactor allowed.

With this PR, however...
* You can now ask for a specific type of wound via
get_corresponding_wound_type(), which takes wound types, a limb, wound
series, etc. and will try to give you a wound fitting those
specifications - but also, a wound that can be applied to a limb.
* This will allow for a vending machine to apply a compound fracture to
a human, but a collapsed superstructure (assuming my synth wounds go in)
to a robot

There are now new "series types" and "wound specific types" that allow
us to designate what series are "mainline" and randomly generatable, and
what are "alternate types" and must be generated manually - you can see
the documentation in wounds.dm.

The behavior of limping and interaction delays has been abstracted to
datum/wound from bone wounds to allow just, general ease of development

Pregen data now allows for series-specific wound penalties. Example: You
could set a burn wound's series wound penalty to 40, which would make
wound progression in the burn category easier - but it would not make it
any easier to get a slashing wound. As it stands wound penalties are for
wounds globally

Scar files are now picked in a "priority" list, where the wound checks
to see if the limb has a biostate before moving down in said list.
Example: Wounds will check for flesh first, if it finds it - it will use
the flesh scar list. Failing that, they then check bone - if it uses
that, it will use the bone scar list. This allows for significantly more
modular scars that dont even need much proc editing when a new wound
type is added

Misc changes: most initial() usage has been replaced by singleton
datums, wound_type is now wound_types and thus wounds can accept
multiple wound types, wounds can now accept multiple tool behaviors for
treatment, wounds now have a picking weight so we can make certain
wounds rarer flatly,

This PR also allows for wounds to override lower severity wounds on
generation, allowing eswords to jump to avulsions - but in spirit of
refactoring, this has been disabled by default (see pregen data's
competition_mode var).
## Why It's Good For The Game

Code quality is good!

Also, all the changes above allow wounds to be a MUCH more modular
system, which is one of its biggest problems right now - everything is
kinda hardcoded and static, making creative work within wounds harder to
do.

## Changelog
🆑
refactor: Refactored wounds yet again
fix: Wounds are now picked from the most severe down again, meaning
eswords can jump to avulsions
fix: Scar descs are now properly applied
/🆑
This commit is contained in:
nikothedude
2023-09-09 19:20:21 -04:00
committed by GitHub
parent ec2e636a3d
commit 009af8c2ce
46 changed files with 1085 additions and 503 deletions
@@ -25,6 +25,8 @@
// /mob/living/carbon physiology signals
#define COMSIG_CARBON_GAIN_WOUND "carbon_gain_wound" //from /datum/wound/proc/apply_wound() (/mob/living/carbon/C, /datum/wound/W, /obj/item/bodypart/L)
#define COMSIG_CARBON_LOSE_WOUND "carbon_lose_wound" //from /datum/wound/proc/remove_wound() (/mob/living/carbon/C, /datum/wound/W, /obj/item/bodypart/L)
/// Called after limb AND victim has been unset
#define COMSIG_CARBON_POST_LOSE_WOUND "carbon_post_lose_wound" //from /datum/wound/proc/remove_wound() (/datum/wound/lost_wound, /obj/item/bodypart/part, ignore_limb, replaced)
///from base of /obj/item/bodypart/proc/can_attach_limb(): (new_limb, special) allows you to fail limb attachment
#define COMSIG_ATTEMPT_CARBON_ATTACH_LIMB "attempt_carbon_attach_limb"
#define COMPONENT_NO_ATTACH (1<<0)
+226 -43
View File
@@ -1,4 +1,3 @@
// ~wound damage/rolling defines
/// the cornerstone of the wound threshold system, your base wound roll for any attack is rand(1, damage^this), after armor reduces said damage. See [/obj/item/bodypart/proc/check_wounding]
#define WOUND_DAMAGE_EXPONENT 1.4
@@ -13,6 +12,10 @@
/// set wound_bonus on an item or attack to this to disable checking wounding for the attack
#define CANT_WOUND -100
/// If there are multiple possible and valid wounds for the same type and severity, weight will be used to pick among them. See _wound_pregen_data.dm for more details
/// This is used in pick_weight, so use integers
#define WOUND_DEFAULT_WEIGHT 50
// ~wound severities
/// for jokey/meme wounds like stubbed toe, no standard messages/sounds or second winds
#define WOUND_SEVERITY_TRIVIAL 0
@@ -22,16 +25,26 @@
/// outright dismemberment of limb
#define WOUND_SEVERITY_LOSS 4
/// A "chronological" list of wound severities, starting at the least severe.
GLOBAL_LIST_INIT(wound_severities_chronological, list(
"[WOUND_SEVERITY_TRIVIAL]",
"[WOUND_SEVERITY_MODERATE]",
"[WOUND_SEVERITY_SEVERE]",
"[WOUND_SEVERITY_CRITICAL]"
))
// ~wound categories
// ~wound categories: wounding_types
/// any brute weapon/attack that doesn't have sharpness. rolls for blunt bone wounds
#define WOUND_BLUNT 1
#define WOUND_BLUNT "wound_blunt"
/// any brute weapon/attack with sharpness = SHARP_EDGED. rolls for slash wounds
#define WOUND_SLASH 2
#define WOUND_SLASH "wound_slash"
/// any brute weapon/attack with sharpness = SHARP_POINTY. rolls for piercing wounds
#define WOUND_PIERCE 3
#define WOUND_PIERCE "wound_pierce"
/// any concentrated burn attack (lasers really). rolls for burning wounds
#define WOUND_BURN 4
#define WOUND_BURN "wound_burn"
/// Mainly a define used for wound_pregen_data, if a pregen data instance expects this, it will accept any and all wound types, even none at all
#define WOUND_ALL "wound_all"
// ~determination second wind defines
@@ -46,6 +59,11 @@
/// While someone has determination in their system, their bleed rate is slightly reduced
#define WOUND_DETERMINATION_BLEED_MOD 0.85
/// Wounds using this competition mode will remove any wounds of a greater severity than itself in a random wound roll. In most cases, you dont want to use this.
#define WOUND_COMPETITION_OVERPOWER_GREATERS "wound_submit"
/// Wounds using this competition mode will remove any wounds of a lower severity than itself in a random wound roll. Used for ensuring the worse case scenario of a given injury_roll.
#define WOUND_COMPETITION_OVERPOWER_LESSERS "wound_dominate"
// ~biology defines
// What kind of biology a limb has, and what wounds it can suffer
/// Has absolutely fucking nothing, no wounds
@@ -54,36 +72,43 @@
#define BIO_BONE (1<<0)
/// Has flesh - allows the victim to suffer fleshy slash pierce and burn wounds
#define BIO_FLESH (1<<1)
/// Self explanatory
#define BIO_FLESH_BONE (BIO_BONE | BIO_FLESH)
/// Has metal - allows the victim to suffer robotic blunt and burn wounds
#define BIO_METAL (1<<2)
/// Is wired internally - allows the victim to suffer electrical wounds (robotic T1-T3 slash/pierce)
#define BIO_WIRED (1<<3)
/// Robotic: shit like cyborg limbs, mostly
#define BIO_ROBOTIC (BIO_METAL|BIO_WIRED)
/// Has bloodflow - can suffer bleeding wounds and can bleed
#define BIO_BLOODED (1<<4)
/// Is connected by a joint - can suffer T1 bone blunt wounds (dislocation)
#define BIO_JOINTED (1<<5)
/// Standard humanoid - can suffer all flesh wounds, such as: T1-3 slash/pierce/burn/blunt. Can also bleed
#define BIO_STANDARD (BIO_FLESH_BONE|BIO_BLOODED)
/// Robotic - can suffer all metal/wired wounds, such as: UNIMPLEMENTED PLEASE UPDATE ONCE SYNTH WOUNDS 9/5/2023 ~Niko
#define BIO_ROBOTIC (BIO_METAL|BIO_WIRED)
/// Has flesh and bone - See BIO_BONE and BIO_FLESH
#define BIO_FLESH_BONE (BIO_BONE|BIO_FLESH)
/// Standard humanoid - can bleed and suffer all flesh/bone wounds, such as: T1-3 slash/pierce/burn/blunt, except dislocations. Think human heads/chests
#define BIO_STANDARD_UNJOINTED (BIO_FLESH_BONE|BIO_BLOODED)
/// Standard humanoid limbs - can bleed and suffer all flesh/bone wounds, such as: T1-3 slash/pierce/burn/blunt. Can also bleed, and be dislocated. Think human arms and legs
#define BIO_STANDARD_JOINTED (BIO_STANDARD_UNJOINTED|BIO_JOINTED)
// "Where" a specific "bio" feature is within a given limb
// Exterior is hard shit, the last line, shit lines bones
// Interior is soft shit, targetted by slashes and pierces (usually), protects exterior
// Yes, it makes no sense
/// The given biostate is on the "exterior" of the limb - hard shit, protected by interior
#define BIO_EXTERIOR (1<<0)
/// The given biostate is on the "exterior" of the limb - soft shit, protects exterior
#define BIO_INTERIOR (1<<1)
#define BIO_EXTERIOR_AND_INTERIOR (BIO_EXTERIOR|BIO_INTERIOR)
// "Where" a specific biostate is within a given limb
// Interior is hard shit, the last line, shit like bones
// Exterior is soft shit, targetted by slashes and pierces (usually), protects exterior
// A limb needs both mangled interior and exterior to be dismembered, but slash/pierce must mangle exterior to attack the interior
// Not having exterior/interior counts as mangled exterior/interior for the purposes of dismemberment
/// The given biostate is on the "interior" of the limb - hard shit, protected by exterior
#define ANATOMY_INTERIOR (1<<0)
/// The given biostate is on the "exterior" of the limb - soft shit, protects interior
#define ANATOMY_EXTERIOR (1<<1)
#define ANATOMY_EXTERIOR_AND_INTERIOR (ANATOMY_EXTERIOR|ANATOMY_INTERIOR)
GLOBAL_LIST_INIT(bio_state_states, list(
"[BIO_WIRED]" = BIO_INTERIOR,
"[BIO_METAL]" = BIO_EXTERIOR,
"[BIO_FLESH]" = BIO_INTERIOR,
"[BIO_BONE]" = BIO_EXTERIOR,
/// A assoc list of BIO_ define to EXTERIOR/INTERIOR defines.
/// This is where the interior/exterior state of a given biostate is set.
/// Note that not all biostates are guaranteed to be one of these - and in fact, many are not
/// IMPORTANT NOTE: All keys are stored as text and must be converted via text2num
GLOBAL_LIST_INIT(bio_state_anatomy, list(
"[BIO_WIRED]" = ANATOMY_EXTERIOR,
"[BIO_METAL]" = ANATOMY_INTERIOR,
"[BIO_FLESH]" = ANATOMY_EXTERIOR,
"[BIO_BONE]" = ANATOMY_INTERIOR,
))
// Wound series
@@ -91,13 +116,169 @@ GLOBAL_LIST_INIT(bio_state_states, list(
// Multiple wounds in a single series cannot be on a limb - the highest severity will always be prioritized, and lower ones will be skipped
/// T1-T3 Bleeding slash wounds. Requires flesh. Can cause bleeding, but doesn't require it. From: slash.dm
#define WOUND_SERIES_FLESH_SLASH_BLEED 1
#define WOUND_SERIES_FLESH_SLASH_BLEED "wound_series_flesh_slash_bled"
/// T1-T3 Basic blunt wounds. T1 requires jointed, but 2-3 require bone. From: bone.dm
#define WOUND_SERIES_BONE_BLUNT_BASIC 2
#define WOUND_SERIES_BONE_BLUNT_BASIC "wound_series_bone_blunt_basic"
/// T1-T3 Basic burn wounds. Requires flesh. From: burns.dm
#define WOUND_SERIES_FLESH_BURN_BASIC 3
/// T1-3 Bleeding puncture wounds. Requires flesh. Can cause bleeding, but doesn't require it. From: pierce.dm
#define WOUND_SERIES_FLESH_PUNCTURE_BLEED 4
#define WOUND_SERIES_FLESH_BURN_BASIC "wound_series_flesh_burn_basic"
/// T1-T3 Bleeding puncture wounds. Requires flesh. Can cause bleeding, but doesn't require it. From: pierce.dm
#define WOUND_SERIES_FLESH_PUNCTURE_BLEED "wound_series_flesh_puncture_bleed"
/// Generic loss wounds. See loss.dm
#define WOUND_SERIES_LOSS_BASIC "wound_series_loss_basic"
/// A assoc list of (wound typepath -> wound_pregen_data instance). Every wound should have a pregen data.
GLOBAL_LIST_INIT_TYPED(all_wound_pregen_data, /datum/wound_pregen_data, generate_wound_static_data())
/// Constructs [GLOB.all_wound_pregen_data] by iterating through a typecache of pregen data, ignoring abstract types, and instantiating the rest.
/proc/generate_wound_static_data()
RETURN_TYPE(/list/datum/wound_pregen_data)
var/list/datum/wound_pregen_data/all_pregen_data = list()
for (var/datum/wound_pregen_data/iterated_path as anything in typecacheof(path = /datum/wound_pregen_data, ignore_root_path = TRUE))
if (initial(iterated_path.abstract))
continue
if (!isnull(all_pregen_data[initial(iterated_path.wound_path_to_generate)]))
stack_trace("pre-existing pregen data for [initial(iterated_path.wound_path_to_generate)] when [iterated_path] was being considered: [all_pregen_data[initial(iterated_path.wound_path_to_generate)]]. \
this is definitely a bug, and is probably because one of the two pregen data have the wrong wound typepath defined. [iterated_path] will not be instantiated")
continue
var/datum/wound_pregen_data/pregen_data = new iterated_path
all_pregen_data[pregen_data.wound_path_to_generate] = pregen_data
return all_pregen_data
// A wound series "collection" is merely a way for us to track what is in what series, and what their types are.
// Without this, we have no centralized way to determine what type is in what series outside of iterating over every pregen data.
/// A branching assoc list of (series -> list(severity -> list(typepath -> weight))). Allows you to say "I want a generic slash wound",
/// then "Of severity 2", and get a wound of that description - via get_corresponding_wound_type()
/// Series: A generic wound_series, such as WOUND_SERIES_BONE_BLUNT_BASIC
/// Severity: Any wounds held within this will be of this severity.
/// Typepath, Weight: Merely a pairing of a given typepath to its weight, held for convenience in pickweight.
GLOBAL_LIST_INIT(wound_series_collections, generate_wound_series_collection())
// Series -> severity -> type -> weight
/// Generates [wound_series_collections] by iterating through all pregen_data. Refer to the mentioned list for documentation
/proc/generate_wound_series_collection()
RETURN_TYPE(/list/datum/wound)
var/list/datum/wound/wound_collection = list()
for (var/datum/wound/wound_typepath as anything in typecacheof(/datum/wound, FALSE, TRUE))
var/datum/wound_pregen_data/pregen_data = GLOB.all_wound_pregen_data[wound_typepath]
if (!pregen_data)
continue
if (pregen_data.abstract)
stack_trace("somehow, a abstract wound_pregen_data instance ([pregen_data.type]) was instantiated and made it to generate_wound_series_collection()! \
i literally have no idea how! please fix this!")
continue
var/series = pregen_data.wound_series
var/list/datum/wound/series_list = wound_collection[series]
if (isnull(series_list))
wound_collection[series] = list()
series_list = wound_collection[series]
var/severity = "[(initial(wound_typepath.severity))]"
var/list/datum/wound/severity_list = series_list[severity]
if (isnull(severity_list))
series_list[severity] = list()
severity_list = series_list[severity]
severity_list[wound_typepath] = pregen_data.weight
return wound_collection
/// A branching assoc list of (wounding_type -> list(wound_series)).
/// Allows for determining of which wound series are caused by what.
GLOBAL_LIST_INIT(wounding_types_to_series, list(
WOUND_BLUNT = list(
WOUND_SERIES_BONE_BLUNT_BASIC
),
WOUND_SLASH = list(
WOUND_SERIES_FLESH_SLASH_BLEED,
),
WOUND_BURN = list(
WOUND_SERIES_FLESH_BURN_BASIC,
),
WOUND_PUNCTURE = list(
WOUND_SERIES_FLESH_PUNCTURE_BLEED
),
))
/// Used in get_corresponding_wound_type(): Will pick the highest severity wound out of severity_min and severity_max
#define WOUND_PICK_HIGHEST_SEVERITY 1
/// Used in get_corresponding_wound_type(): Will pick the lowest severity wound out of severity_min and severity_max
#define WOUND_PICK_LOWEST_SEVERITY 2
/**
* Searches through all wounds for any of proper type, series, and biostate, and then returns a single one via pickweight.
* Is able to discern between, say, a flesh slash wound, and a metallic slash wound, and will return the respective one for the provided limb.
*
* The severity_max and severity_pick_mode args mostly exist in case you want a wound in a series that may not have your ideal severity wound, as it lets you
* essentially set a "fallback", where if your ideal wound doesnt exist, it'll still return something, trying to get closest to your ideal severity.
*
* Generally speaking, if you want a critical/severe/moderate wound, you should set severity_min to WOUND_SEVERITY_MODERATE, severity_max to your ideal wound,
* and severity_pick_mode to WOUND_PICK_HIGHEST_SEVERITY - UNLESS you for some reason want the LOWEST severity, in which case you should set
* severity_max to the highest wound you're willing to tolerate, and severity_pick_mode to WOUND_PICK_LOWEST_SEVERITY.
*
* Args:
* * list/wounding_types: A list of wounding_types. Only wounds that accept these wound types will be considered.
* * obj/item/bodypart/part: The limb we are considering. Extremely important for biostates.
* * severity_min: The minimum wound severity we will search for.
* * severity_max = severity_min: The maximum wound severity we will search for.
* * severity_pick_mode = WOUND_PICK_HIGHEST_SEVERITY: The "pick mode" we will use when considering multiple wounds of acceptable severity. See the above defines.
* * random_roll = TRUE: If this is considered a "random" consideration. If true, only wounds that can be randomly generated will be considered.
* * duplicates_allowed = FALSE: If exact duplicates of a given wound on part are tolerated. Useful for simply getting a path and not instantiating.
* * care_about_existing_wounds = TRUE: If we iterate over wounds to see if any are above or at a given wounds severity, and disregard it if any are. Useful for simply getting a path and not instantiating.
*
* Returns:
* A randomly picked wound typepath meeting all the above criteria and being applicable to the part's biotype - or null if there were none.
*/
/proc/get_corresponding_wound_type(list/wounding_types, obj/item/bodypart/part, severity_min, severity_max = severity_min, severity_pick_mode = WOUND_PICK_HIGHEST_SEVERITY, random_roll = TRUE, duplicates_allowed = FALSE, care_about_existing_wounds = TRUE)
RETURN_TYPE(/datum/wound) // note that just because its set to return this doesnt mean its non-nullable
var/list/wounding_type_list = list()
for (var/wounding_type as anything in wounding_types)
wounding_type_list += GLOB.wounding_types_to_series[wounding_type]
if (!length(wounding_type_list))
return null
var/list/datum/wound/paths_to_pick_from = list()
for (var/series as anything in shuffle(wounding_type_list))
var/list/severity_list = GLOB.wound_series_collections[series]
if (!length(severity_list))
continue
var/picked_severity
for (var/severity_text as anything in shuffle(GLOB.wound_severities_chronological))
var/severity = text2num(severity_text)
if (severity > severity_min || severity < severity_max)
continue
if (isnull(picked_severity) || ((severity_pick_mode == WOUND_PICK_HIGHEST_SEVERITY && severity > picked_severity) || (severity_pick_mode == WOUND_PICK_LOWEST_SEVERITY && severity < picked_severity)))
picked_severity = severity
var/list/wound_typepaths = severity_list["[picked_severity]"]
if (!length(wound_typepaths))
continue
for (var/datum/wound/iterated_path as anything in wound_typepaths)
var/datum/wound_pregen_data/pregen_data = GLOB.all_wound_pregen_data[iterated_path]
if (pregen_data.can_be_applied_to(part, wounding_types, random_roll = random_roll, duplicates_allowed = duplicates_allowed, care_about_existing_wounds = care_about_existing_wounds))
paths_to_pick_from[iterated_path] = wound_typepaths[iterated_path]
return pick_weight(paths_to_pick_from) // we found our winners!
/// Assoc list of biotype -> ideal scar file to be used and grab stuff from.
GLOBAL_LIST_INIT(biotypes_to_scar_file, list(
"[BIO_FLESH]" = FLESH_SCAR_FILE,
"[BIO_BONE]" = BONE_SCAR_FILE
))
// ~burn wound infection defines
// Thresholds for infection for burn wounds, once infestation hits each threshold, things get steadily worse
@@ -125,23 +306,25 @@ GLOBAL_LIST_INIT(bio_state_states, list(
// ~mangling defines
// With the wounds pt. 2 update, general dismemberment now requires 2 things for a limb to be dismemberable (bone only creatures just need the second):
// 1. Flesh is mangled: A critical slash or pierce wound on that limb
// 2. Bone is mangled: At least a severe bone wound on that limb
// see [/obj/item/bodypart/proc/get_mangled_state] for more information
// With the wounds pt. 2 update, general dismemberment now requires 2 things for a limb to be dismemberable (exterior/bone only creatures just need the second):
// 1. Exterior is mangled: A critical slash or pierce wound on that limb
// 2. Interior is mangled: At least a severe bone wound on that limb
// Lack of exterior or interior count as mangled exterior/interior respectively
// see [/obj/item/bodypart/proc/get_mangled_state] for more information, as well as GLOB.bio_state_anatomy
#define BODYPART_MANGLED_NONE NONE
#define BODYPART_MANGLED_BONE (1<<0)
#define BODYPART_MANGLED_FLESH (1<<1)
#define BODYPART_MANGLED_BOTH (BODYPART_MANGLED_BONE | BODYPART_MANGLED_FLESH)
#define BODYPART_MANGLED_INTERIOR (1<<0)
#define BODYPART_MANGLED_EXTERIOR (1<<1)
#define BODYPART_MANGLED_BOTH (BODYPART_MANGLED_INTERIOR | BODYPART_MANGLED_EXTERIOR)
// ~wound flag defines
/// If having this wound counts as mangled flesh for dismemberment
#define MANGLES_FLESH (1<<0)
/// If having this wound counts as mangled bone for dismemberment
#define MANGLES_BONE (1<<1)
/// If having this wound counts as mangled exterior for dismemberment
#define MANGLES_EXTERIOR (1<<0)
/// If having this wound counts as mangled interior for dismemberment
#define MANGLES_INTERIOR (1<<1)
/// If this wound marks the limb as being allowed to have gauze applied
#define ACCEPTS_GAUZE (1<<2)
/// If this wound allows the victim to grasp it
#define CAN_BE_GRASPED (1<<3)
// ~scar persistence defines
// The following are the order placements for persistent scar save formats
+2 -5
View File
@@ -91,11 +91,8 @@
log_combat(user, H, "wounded via throat slitting", source)
H.apply_damage(source.force, BRUTE, BODY_ZONE_HEAD, wound_bonus=CANT_WOUND) // easy tiger, we'll get to that in a sec
var/obj/item/bodypart/slit_throat = H.get_bodypart(BODY_ZONE_HEAD)
if(slit_throat)
var/datum/wound/slash/flesh/critical/screaming_through_a_slit_throat = new
if (!screaming_through_a_slit_throat.apply_wound(slit_throat, wound_source = "throat slit"))
qdel(screaming_through_a_slit_throat)
H.apply_status_effect(/datum/status_effect/neck_slice)
if (H.cause_wound_of_type_and_severity(WOUND_SLASH, slit_throat, WOUND_SEVERITY_CRITICAL))
H.apply_status_effect(/datum/status_effect/neck_slice)
/**
* Handles a user butchering a target
+3 -2
View File
@@ -93,7 +93,8 @@
if(harmful)
victim.throw_alert(ALERT_EMBEDDED_OBJECT, /atom/movable/screen/alert/embeddedobject)
playsound(victim,'sound/weapons/bladeslice.ogg', 40)
weapon.add_mob_blood(victim)//it embedded itself in you, of course it's bloody!
if (limb.can_bleed())
weapon.add_mob_blood(victim)//it embedded itself in you, of course it's bloody!
damage += weapon.w_class * impact_pain_mult
victim.add_mood_event("embedded", /datum/mood_event/embedded)
@@ -303,7 +304,7 @@
return
var/damage = weapon.w_class * remove_pain_mult
limb.receive_damage(brute=(1-pain_stam_pct) * damage * 1.5, sharpness=SHARP_EDGED) // Performs exit wounds and flings the user to the caster if nearby
limb.force_wound_upwards(/datum/wound/pierce/bleed/moderate)
victim.cause_wound_of_type_and_severity(WOUND_PIERCE, limb, WOUND_SEVERITY_MODERATE)
victim.adjustStaminaLoss(pain_stam_pct * damage)
playsound(get_turf(victim), 'sound/effects/wounds/blood2.ogg', 50, TRUE)
+2 -2
View File
@@ -307,7 +307,7 @@
var/damage_dealt = wound_info_by_part[hit_part][CLOUD_POSITION_DAMAGE]
var/w_bonus = wound_info_by_part[hit_part][CLOUD_POSITION_W_BONUS]
var/bw_bonus = wound_info_by_part[hit_part][CLOUD_POSITION_BW_BONUS]
var/wound_type = (initial(P.damage_type) == BRUTE) ? WOUND_BLUNT : WOUND_BURN // sharpness is handled in the wound rolling
var/wounding_type = (initial(P.damage_type) == BRUTE) ? WOUND_BLUNT : WOUND_BURN // sharpness is handled in the wound rolling
wound_info_by_part -= hit_part
// technically this only checks armor worn the moment that all the pellets resolve rather than as each one hits you,
@@ -320,7 +320,7 @@
armor_factor *= ARMOR_WEAKENED_MULTIPLIER
damage_dealt *= max(0, 1 - armor_factor*0.01)
hit_part.painless_wound_roll(wound_type, damage_dealt, w_bonus, bw_bonus, initial(P.sharpness))
hit_part.painless_wound_roll(wounding_type, damage_dealt, w_bonus, bw_bonus, initial(P.sharpness))
var/limb_hit_text = ""
if(hit_part)
+5 -3
View File
@@ -80,9 +80,11 @@
if(do_after(attacker, 3 SECONDS, target, interaction_key = weapon))
attacker.visible_message(span_warning("[attacker] swings [attacker.p_their()] [weapon] at [target]'s kneecaps!"), span_danger("You swing \the [weapon] at [target]'s kneecaps!"))
var/datum/wound/blunt/bone/severe/severe_wound_type = /datum/wound/blunt/bone/severe
var/datum/wound/blunt/bone/critical/critical_wound_type = /datum/wound/blunt/bone/critical
leg.receive_damage(brute = weapon.force, wound_bonus = rand(initial(severe_wound_type.threshold_minimum), initial(critical_wound_type.threshold_minimum) + 10), damage_source = "kneecapping")
var/min_wound = leg.get_wound_threshold_of_wound_type(WOUND_BLUNT, WOUND_SEVERITY_SEVERE, return_value_if_no_wound = 30, wound_source = weapon)
var/max_wound = leg.get_wound_threshold_of_wound_type(WOUND_BLUNT, WOUND_SEVERITY_CRITICAL, return_value_if_no_wound = 50, wound_source = weapon)
leg.receive_damage(brute = weapon.force, wound_bonus = rand(min_wound, max_wound + 10), damage_source = "kneecapping")
target.emote("scream")
log_combat(attacker, target, "broke the kneecaps of", weapon)
target.update_damage_overlays()
+9 -3
View File
@@ -63,13 +63,19 @@
*arg1 is the arm to evaluate damage of and possibly break.
*/
/datum/mutation/human/hulk/proc/break_an_arm(obj/item/bodypart/arm)
var/severity
switch(arm.brute_dam)
if(45 to 50)
arm.force_wound_upwards(/datum/wound/blunt/bone/critical, wound_source = "hulk smashing")
severity = WOUND_SEVERITY_CRITICAL
if(41 to 45)
arm.force_wound_upwards(/datum/wound/blunt/bone/severe, wound_source = "hulk smashing")
severity = WOUND_SEVERITY_SEVERE
if(35 to 41)
arm.force_wound_upwards(/datum/wound/blunt/bone/moderate, wound_source = "hulk smashing")
severity = WOUND_SEVERITY_MODERATE
if (isnull(severity))
return
owner.cause_wound_of_type_and_severity(WOUND_BLUNT, arm, severity, wound_source = "hulk smashing")
/datum/mutation/human/hulk/on_life(seconds_per_tick, times_fired)
if(owner.health < owner.crit_threshold)
+2 -2
View File
@@ -215,8 +215,8 @@
if(iscarbon(victim))
var/mob/living/carbon/carbon_victim = victim
var/obj/item/bodypart/chest = carbon_victim.get_bodypart(BODY_ZONE_CHEST)
if(chest)
chest.force_wound_upwards(/datum/wound/blunt/bone/severe, wound_source = "human force to the chest")
carbon_victim.cause_wound_of_type_and_severity(WOUND_BLUNT, chest, WOUND_SEVERITY_SEVERE, wound_source = "human force to the chest")
playsound(owner, 'sound/creatures/crack_vomit.ogg', 120, extrarange = 5, falloff_exponent = 4)
vomit_up()
@@ -408,7 +408,9 @@
var/still_bleeding = FALSE
for(var/datum/wound/bleeding_thing as anything in throat.wounds)
if(bleeding_thing.wound_type == WOUND_SLASH && bleeding_thing.severity > WOUND_SEVERITY_MODERATE)
var/datum/wound_pregen_data/pregen_data = GLOB.all_wound_pregen_data[bleeding_thing.type]
if(pregen_data.wounding_types_valid(list(WOUND_SLASH)) && bleeding_thing.severity > WOUND_SEVERITY_MODERATE && bleeding_thing.blood_flow > 0)
still_bleeding = TRUE
break
if(!still_bleeding)
+17 -29
View File
@@ -54,11 +54,11 @@
right = C.get_bodypart(BODY_ZONE_R_LEG)
update_limp()
RegisterSignal(C, COMSIG_MOVABLE_MOVED, PROC_REF(check_step))
RegisterSignals(C, list(COMSIG_CARBON_GAIN_WOUND, COMSIG_CARBON_LOSE_WOUND, COMSIG_CARBON_ATTACH_LIMB, COMSIG_CARBON_REMOVE_LIMB), PROC_REF(update_limp))
RegisterSignals(C, list(COMSIG_CARBON_GAIN_WOUND, COMSIG_CARBON_POST_LOSE_WOUND, COMSIG_CARBON_ATTACH_LIMB, COMSIG_CARBON_REMOVE_LIMB), PROC_REF(update_limp))
return TRUE
/datum/status_effect/limp/on_remove()
UnregisterSignal(owner, list(COMSIG_MOVABLE_MOVED, COMSIG_CARBON_GAIN_WOUND, COMSIG_CARBON_LOSE_WOUND, COMSIG_CARBON_ATTACH_LIMB, COMSIG_CARBON_REMOVE_LIMB))
UnregisterSignal(owner, list(COMSIG_MOVABLE_MOVED, COMSIG_CARBON_GAIN_WOUND, COMSIG_CARBON_POST_LOSE_WOUND, COMSIG_CARBON_ATTACH_LIMB, COMSIG_CARBON_REMOVE_LIMB))
/atom/movable/screen/alert/status_effect/limp
name = "Limping"
@@ -165,37 +165,25 @@
if(W == linked_wound)
qdel(src)
// bones
/datum/status_effect/wound/blunt/bone
/datum/status_effect/wound/blunt/bone/on_apply()
. = ..()
RegisterSignal(owner, COMSIG_MOB_SWAP_HANDS, PROC_REF(on_swap_hands))
on_swap_hands()
/datum/status_effect/wound/blunt/bone/on_remove()
. = ..()
UnregisterSignal(owner, COMSIG_MOB_SWAP_HANDS)
var/mob/living/carbon/wound_owner = owner
wound_owner.remove_actionspeed_modifier(/datum/actionspeed_modifier/blunt_wound)
/datum/status_effect/wound/blunt/bone/proc/on_swap_hands()
SIGNAL_HANDLER
var/mob/living/carbon/wound_owner = owner
if(wound_owner.get_active_hand() == linked_limb)
wound_owner.add_actionspeed_modifier(/datum/actionspeed_modifier/blunt_wound, (linked_wound.interaction_efficiency_penalty - 1))
else
wound_owner.remove_actionspeed_modifier(/datum/actionspeed_modifier/blunt_wound)
/datum/status_effect/wound/blunt/bone/nextmove_modifier()
/datum/status_effect/wound/nextmove_modifier()
var/mob/living/carbon/C = owner
if(C.get_active_hand() == linked_limb)
return linked_wound.interaction_efficiency_penalty
return linked_wound.get_action_delay_mult()
return 1
return ..()
/datum/status_effect/wound/nextmove_adjust()
var/mob/living/carbon/C = owner
if(C.get_active_hand() == linked_limb)
return linked_wound.get_action_delay_increment()
return ..()
// bones
/datum/status_effect/wound/blunt/bone
// blunt
/datum/status_effect/wound/blunt/bone/moderate
+97 -34
View File
@@ -2,28 +2,6 @@
// For example: You can make a pregen_data subtype for your wound that overrides can_be_applied_to to only apply to specifically slimeperson limbs.
// Without this, youre stuck with very static initial variables.
GLOBAL_LIST_INIT_TYPED(all_wound_pregen_data, /datum/wound_pregen_data, generate_wound_static_data())
/proc/generate_wound_static_data()
RETURN_TYPE(/list/datum/wound_pregen_data)
var/list/datum/wound_pregen_data/data = list()
for (var/datum/wound_pregen_data/path as anything in typecacheof(path = /datum/wound_pregen_data, ignore_root_path = TRUE))
if (initial(path.abstract))
continue
if (!isnull(data[initial(path.wound_path_to_generate)]))
stack_trace("pre-existing pregen data for [initial(path.wound_path_to_generate)] when [path] was being considered: [data[initial(path.wound_path_to_generate)]]. \
this is definitely a bug, and is probably because one of the two pregen data have the wrong wound typepath defined. [path] will not be instantiated")
continue
var/datum/wound_pregen_data/pregen_data = new path
data[pregen_data.wound_path_to_generate] = pregen_data
return data
/// A singleton datum that holds pre-gen and static data about a wound. Each wound datum should have a corresponding wound_pregen_data.
/datum/wound_pregen_data
/// The typepath of the wound we will be handling and storing data of. NECESSARY IF THIS IS A NON-ABSTRACT TYPE!
@@ -38,7 +16,7 @@ GLOBAL_LIST_INIT_TYPED(all_wound_pregen_data, /datum/wound_pregen_data, generate
/// A list of biostates a limb must have to receive our wound, in wounds.dm.
var/required_limb_biostate
/// If false, we will check if the limb has all of our required biostates instead of just any.
var/check_for_any = FALSE
var/require_any_biostate = FALSE
/// If false, we will iterate through wounds on a given limb, and if any match our type, we wont add our wound.
var/duplicates_allowed = FALSE
@@ -48,6 +26,30 @@ GLOBAL_LIST_INIT_TYPED(all_wound_pregen_data, /datum/wound_pregen_data, generate
/// A list of bodyzones we are applicable to.
var/list/viable_zones = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
/// The types of attack that can generate this wound. E.g. WOUND_SLASH = A sharp attack can cause this, WOUND_BLUNT = an attack with no sharpness/an attack with sharpness against a limb with mangled exterior can cause this.
var/list/required_wounding_types
/// If true, this wound can only be generated by all [required_wounding_types] at once, not just any.
var/match_all_wounding_types = FALSE
/// The weight that will be used if, by the end of wound selection, there are multiple valid wounds. This will be inserted into pick_weight, so use integers.
var/weight = WOUND_DEFAULT_WEIGHT
/// The minimum injury roll a attack must get to generate us. Affected by our wound's threshold_penalty and series_threshold_penalty, as well as the attack's wound_bonus. See check_wounding_mods().
var/threshold_minimum
/// The series of wounds this is in. See wounds.dm (the defines file) for a more detailed explanation - but tldr is that no 2 wounds of the same series can be on a limb.
var/wound_series
/// If true, we will attempt to, during a random wound roll, overpower and remove other wound typepaths from the possible wounds list using [competition_mode] and [overpower_wounds_of_even_severity].
var/compete_for_wounding = TRUE
/// The competition mode with which we will remove other wounds from a possible wound roll assuming [compete_for_wounding] is TRUE. See wounds.dm, the defines file, for more information on what these do.
var/competition_mode = WOUND_COMPETITION_OVERPOWER_LESSERS
/// If this and [compete_for_wounding] is true, we will remove wounds of an even severity to us during a random wound roll.
var/overpower_wounds_of_even_severity = FALSE
/// A list of BIO_ defines that will be iterated over in order to determine the scar file our wound will generate.
/// Use generate_scar_priorities to create a custom list.
var/list/scar_priorities
/datum/wound_pregen_data/New()
. = ..()
@@ -58,21 +60,33 @@ GLOBAL_LIST_INIT_TYPED(all_wound_pregen_data, /datum/wound_pregen_data, generate
if (wound_path_to_generate == null)
stack_trace("wound_path_to_generate null - please set it! occured on: [src.type]")
scar_priorities = generate_scar_priorities()
/// Should return a list of BIO_ biostate priorities, in order. See [scar_priorities] for further documentation.
/datum/wound_pregen_data/proc/generate_scar_priorities()
RETURN_TYPE(/list)
var/list/priorities = list(
"[BIO_FLESH]",
"[BIO_BONE]",
)
return priorities
// this proc is the primary reason this datum exists - a singleton instance so we can always run this proc even without the wound existing
/**
* Args:
* * obj/item/bodypart/limb: The limb we are considering.
* * wound_type: The type of the "wound acquisition attempt". Example: A slashing attack cannot proc a blunt wound, so wound_type = WOUND_SLASH would
* fail if we expect WOUND_BLUNT. Defaults to the wound type we expect.
* * list/suggested_wounding_types: The wounding types to be checked against the wounding types we require. Defaults to required_wounding_types.
* * datum/wound/old_wound: If we would replace a wound, this would be said wound. Nullable.
* * random_roll = FALSE: If this is in the context of a random wound generation, and this wound wasn't specifically checked.
*
* Returns:
* FALSE if the limb cannot be wounded, if wound_type is not ours, if we have a higher severity wound already in our series,
* FALSE if the limb cannot be wounded, if the wounding types dont match ours (via wounding_types_valid()), if we have a higher severity wound already in our series,
* if we have a biotype mismatch, if the limb isnt in a viable zone, or if theres any duplicate wound types.
* TRUE otherwise.
*/
/datum/wound_pregen_data/proc/can_be_applied_to(obj/item/bodypart/limb, wound_type = initial(wound_path_to_generate.wound_type), datum/wound/old_wound, random_roll = FALSE)
/datum/wound_pregen_data/proc/can_be_applied_to(obj/item/bodypart/limb, list/suggested_wounding_types = required_wounding_types, datum/wound/old_wound, random_roll = FALSE, duplicates_allowed = src.duplicates_allowed, care_about_existing_wounds = TRUE)
SHOULD_BE_PURE(TRUE)
if (!istype(limb) || !limb.owner)
@@ -84,11 +98,13 @@ GLOBAL_LIST_INIT_TYPED(all_wound_pregen_data, /datum/wound_pregen_data, generate
if (HAS_TRAIT(limb.owner, TRAIT_NEVER_WOUNDED) || (limb.owner.status_flags & GODMODE))
return FALSE
if (wound_type != initial(wound_path_to_generate.wound_type))
return
else
if (!wounding_types_valid(suggested_wounding_types))
return FALSE
if (care_about_existing_wounds)
for (var/datum/wound/preexisting_wound as anything in limb.wounds)
if (preexisting_wound.wound_series == initial(wound_path_to_generate.wound_series))
var/datum/wound_pregen_data/pregen_data = GLOB.all_wound_pregen_data[preexisting_wound.type]
if (pregen_data.wound_series == wound_series)
if (preexisting_wound.severity >= initial(wound_path_to_generate.severity))
return FALSE
@@ -111,7 +127,7 @@ GLOBAL_LIST_INIT_TYPED(all_wound_pregen_data, /datum/wound_pregen_data, generate
/// Returns true if we have the given biostates, or any biostate in it if check_for_any is true. False otherwise.
/datum/wound_pregen_data/proc/biostate_valid(biostate)
if (check_for_any)
if (require_any_biostate)
if (!(biostate & required_limb_biostate))
return FALSE
else if (!((biostate & required_limb_biostate) == required_limb_biostate)) // check for all
@@ -119,6 +135,50 @@ GLOBAL_LIST_INIT_TYPED(all_wound_pregen_data, /datum/wound_pregen_data, generate
return TRUE
/**
* A simple getter for [weight], with arguments supplied to allow custom behavior.
*
* Args:
* * obj/item/bodypart/limb: The limb we are contemplating being added to. Nullable.
* * woundtype: The woundtype of the assumed attack that would generate us. Nullable.
* * damage: The raw damage that would cause us. Nullable.
* * attack_direction: The direction of the attack that'd cause us. Nullable.
* * damage_source: The entity that would cause us. Nullable.
*
* Returns:
* Our weight.
*/
/datum/wound_pregen_data/proc/get_weight(obj/item/bodypart/limb, woundtype, damage, attack_direction, damage_source)
return weight
/// Returns TRUE if we use WOUND_ALL, or we require all types and have all/if we require any and have any, FALSE otherwise.
/datum/wound_pregen_data/proc/wounding_types_valid(list/suggested_wounding_types)
if (WOUND_ALL in required_wounding_types)
return TRUE
if (!length(suggested_wounding_types))
return FALSE
for (var/iter_wounding_type as anything in suggested_wounding_types)
if (!(iter_wounding_type in required_wounding_types))
if (match_all_wounding_types)
return FALSE
else
if (!match_all_wounding_types)
return TRUE
return match_all_wounding_types // if we get here, we've matched everything
/**
* A simple getter for [threshold_minimum], with arguments supplied to allow custom behavior.
*
* Args:
* * obj/item/bodypart/part: The limb we are contemplating being added to.
* * attack_direction: The direction of the attack that'd generate us. Nullable.
* * damage_source: The source of the damage that'd cause us. Nullable.
*/
/datum/wound_pregen_data/proc/get_threshold_for(obj/item/bodypart/part, attack_direction, damage_source)
return threshold_minimum
/// Returns a new instance of our wound datum.
/datum/wound_pregen_data/proc/generate_instance(obj/item/bodypart/limb, ...)
RETURN_TYPE(/datum/wound)
@@ -126,10 +186,13 @@ GLOBAL_LIST_INIT_TYPED(all_wound_pregen_data, /datum/wound_pregen_data, generate
return new wound_path_to_generate
/datum/wound_pregen_data/Destroy(force, ...)
stack_trace("[src], a singleton wound pregen data instance, was destroyed! This should not happen!")
var/error_message = "[src], a singleton wound pregen data instance, was destroyed! This should not happen!"
if (force)
error_message += " NOTE: This Destroy() was called with force == TRUE. This instance will be deleted and replaced with a new one."
stack_trace(error_message)
if (!force)
return
return QDEL_HINT_LETMELIVE
. = ..()
+224 -50
View File
@@ -16,6 +16,11 @@
#define WOUND_CRITICAL_BLUNT_DISMEMBER_BONUS 15
// Applied into wounds when they're scanned with the wound analyzer, halves time to treat them manually.
#define TRAIT_WOUND_SCANNED "wound_scanned"
// I dunno lol
#define ANALYZER_TRAIT "analyzer_trait"
/datum/wound
/// What it's named
var/name = "Wound"
@@ -29,8 +34,8 @@
/// If this wound can generate a scar.
var/can_scar = TRUE
/// The file we take our scar descriptions from.
var/scar_file
/// 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"
@@ -43,10 +48,6 @@
/// 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
/// The type of attack that can generate this wound. E.g. WOUND_SLASH = A sword can cause us, or WOUND_BLUNT = a hammer can cause us/a sword attacking mangled flesh.
var/wound_type
/// The series of wounds this is in. See wounds.dm (the defines file) for a more detailed explanation - but tldr is that no 2 wounds of the same series can be on a limb.
var/wound_series
/// Who owns the body part that we're wounding
var/mob/living/carbon/victim = null
@@ -57,8 +58,8 @@
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
/// Tools with the specified tool flag will also be able to try directly treating this wound
var/treatable_tool
/// 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
@@ -72,13 +73,11 @@
var/limp_chance
/// How much we're contributing to this limb's bleed_rate
var/blood_flow
/// Essentially, keeps track of whether or not this wound is capable of bleeding (in case the owner has the NOBLOOD species trait)
var/no_bleeding = FALSE
/// The minimum we need to roll on [/obj/item/bodypart/proc/check_wounding] to begin suffering this wound, see check_wounding_mods() for more
var/threshold_minimum
/// 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
@@ -89,9 +88,12 @@
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 * 25 power
/// 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
@@ -102,17 +104,66 @@
/// 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()
if(attached_surgery)
QDEL_NULL(attached_surgery)
QDEL_NULL(attached_surgery)
if (limb)
remove_wound()
QDEL_NULL(actionspeed_mod)
return ..()
// Applied into wounds when they're scanned with the wound analyzer, halves time to treat them manually.
#define TRAIT_WOUND_SCANNED "wound_scanned"
// I dunno lol
#define ANALYZER_TRAIT "analyzer_trait"
/// If we should have an actionspeed_mod, ensures we do and updates its slowdown. Otherwise, ensures we dont 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.
@@ -142,7 +193,6 @@
set_limb(L)
LAZYADD(victim.all_wounds, src)
LAZYADD(limb.wounds, src)
no_bleeding = HAS_TRAIT(victim, TRAIT_NOBLOOD)
update_descriptions()
limb.update_wounds()
if(status_effect_type)
@@ -162,7 +212,7 @@
var/msg = span_danger("[victim]'s [limb.plaintext_zone] [occur_text]!")
var/vis_dist = COMBAT_MESSAGE_RANGE
if(severity != WOUND_SEVERITY_MODERATE)
if(severity > WOUND_SEVERITY_MODERATE)
msg = "<b>[msg]</b>"
vis_dist = DEFAULT_MESSAGE_RANGE
@@ -183,7 +233,7 @@
// 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, wound_type, old_wound)
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()
@@ -205,13 +255,66 @@
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_GAUZE_DESTROYED))
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_GAUZE_DESTROYED), 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()
/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
@@ -220,18 +323,27 @@
/// Remove the wound from whatever it's afflicting, and cleans up whateverstatus 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, theres 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)
/datum/wound/proc/remove_wound_from_victim()
if(!victim)
return
@@ -259,28 +371,6 @@
/datum/wound/proc/wound_injury(datum/wound/old_wound = null, attack_direction = null)
return
/// 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)
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))
if(limb)
if(disabling)
limb.add_traits(list(TRAIT_PARALYSIS, TRAIT_DISABLED_BY_WOUND), REF(src))
/// Proc called to change the variable `disabling` and react to the event.
/datum/wound/proc/set_disabling(new_value)
if(disabling == new_value)
@@ -295,6 +385,60 @@
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()
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 list(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
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(!limb.current_gauze)
limb.update_wounds()
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()
@@ -349,9 +493,9 @@
/// 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 == treatable_tool)
if(potential_treater.tool_behaviour in treatable_tools)
return TRUE
if(treatable_tool == TOOL_CAUTERY && potential_treater.get_temperature() && user == victim) // allow improvised cauterization on yourself without an aggro grab
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))
@@ -388,9 +532,20 @@
/// 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
if(cryo_progress > 33 * severity)
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(power)
return
@@ -499,7 +654,6 @@
if(WOUND_SEVERITY_CRITICAL)
return "Critical"
/// 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()
@@ -511,7 +665,17 @@
/// 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)
return scar_file
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.)
@@ -522,7 +686,17 @@
/datum/wound/proc/get_dismember_chance_bonus(existing_chance)
SHOULD_BE_PURE(TRUE)
if (wound_type == WOUND_BLUNT && severity >= WOUND_SEVERITY_CRITICAL)
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
-1
View File
@@ -1,4 +1,3 @@
/datum/wound/blunt
name = "Blunt Wound"
sound_effect = 'sound/effects/wounds/crack1.ogg'
wound_type = WOUND_BLUNT
+15 -40
View File
@@ -8,11 +8,15 @@
abstract = TRUE
required_limb_biostate = BIO_BONE
required_wounding_types = list(WOUND_BLUNT)
wound_series = WOUND_SERIES_BONE_BLUNT_BASIC
/datum/wound/blunt/bone
name = "Blunt (Bone) Wound"
wound_flags = (ACCEPTS_GAUZE)
scar_file = BONE_SCAR_FILE
default_scar_file = BONE_SCAR_FILE
/// Have we been bone gel'd?
var/gelled
@@ -33,8 +37,6 @@
/// If this is a chest wound and this is set, we have this chance to cough up blood when hit in the chest
var/internal_bleeding_chance = 0
wound_series = WOUND_SERIES_BONE_BLUNT_BASIC
/*
Overwriting of base procs
*/
@@ -65,14 +67,6 @@
return ..()
/datum/wound/blunt/bone/set_limb(obj/item/bodypart/new_value)
if (limb)
UnregisterSignal(limb, list(COMSIG_BODYPART_GAUZED, COMSIG_BODYPART_GAUZE_DESTROYED))
if (new_value)
RegisterSignals(new_value, list(COMSIG_BODYPART_GAUZED, COMSIG_BODYPART_GAUZE_DESTROYED), PROC_REF(update_inefficiencies))
return ..()
/datum/wound/blunt/bone/remove_wound(ignore_limb, replaced)
limp_slowdown = 0
limp_chance = 0
@@ -180,28 +174,6 @@
New common procs for /datum/wound/blunt/bone/
*/
/datum/wound/blunt/bone/proc/update_inefficiencies()
SIGNAL_HANDLER
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)
victim.apply_status_effect(/datum/status_effect/limp)
else if(limb.body_zone in list(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
if(limb.current_gauze?.splint_factor)
interaction_efficiency_penalty = 1 + ((interaction_efficiency_penalty - 1) * limb.current_gauze.splint_factor)
else
interaction_efficiency_penalty = initial(interaction_efficiency_penalty)
if(initial(disabling))
set_disabling(!limb.current_gauze)
limb.update_wounds()
/datum/wound/blunt/bone/get_scar_file(obj/item/bodypart/scarred_limb, add_to_scars)
if (scarred_limb.biological_state & BIO_BONE && (!(scarred_limb.biological_state & BIO_FLESH))) // only bone
return BONE_SCAR_FILE
@@ -221,11 +193,10 @@
interaction_efficiency_penalty = 1.3
limp_slowdown = 3
limp_chance = 50
threshold_minimum = 35
threshold_penalty = 15
treatable_tool = TOOL_BONESET
treatable_tools = list(TOOL_BONESET)
status_effect_type = /datum/status_effect/wound/blunt/bone/moderate
scar_keyword = "bluntmoderate"
scar_keyword = "dislocate"
/datum/wound_pregen_data/bone/dislocate
abstract = FALSE
@@ -234,6 +205,8 @@
required_limb_biostate = BIO_JOINTED
threshold_minimum = 35
/datum/wound/blunt/bone/moderate/Destroy()
if(victim)
UnregisterSignal(victim, COMSIG_LIVING_DOORCRUSHED)
@@ -350,7 +323,6 @@
interaction_efficiency_penalty = 2
limp_slowdown = 6
limp_chance = 60
threshold_minimum = 60
threshold_penalty = 30
treatable_by = list(/obj/item/stack/sticky_tape/surgical, /obj/item/stack/medical/bone_gel)
status_effect_type = /datum/status_effect/wound/blunt/bone/severe
@@ -358,7 +330,7 @@
brain_trauma_group = BRAIN_TRAUMA_MILD
trauma_cycle_cooldown = 1.5 MINUTES
internal_bleeding_chance = 40
wound_flags = (ACCEPTS_GAUZE | MANGLES_BONE)
wound_flags = (ACCEPTS_GAUZE | MANGLES_INTERIOR)
regen_ticks_needed = 120 // ticks every 2 seconds, 240 seconds, so roughly 4 minutes default
/datum/wound_pregen_data/bone/hairline
@@ -366,6 +338,8 @@
wound_path_to_generate = /datum/wound/blunt/bone/severe
threshold_minimum = 60
/// Compound Fracture (Critical Blunt)
/datum/wound/blunt/bone/critical
name = "Compound Fracture"
@@ -379,7 +353,6 @@
limp_slowdown = 7
limp_chance = 70
sound_effect = 'sound/effects/wounds/crack2.ogg'
threshold_minimum = 115
threshold_penalty = 50
disabling = TRUE
treatable_by = list(/obj/item/stack/sticky_tape/surgical, /obj/item/stack/medical/bone_gel)
@@ -388,7 +361,7 @@
brain_trauma_group = BRAIN_TRAUMA_SEVERE
trauma_cycle_cooldown = 2.5 MINUTES
internal_bleeding_chance = 60
wound_flags = (ACCEPTS_GAUZE | MANGLES_BONE)
wound_flags = (ACCEPTS_GAUZE | MANGLES_INTERIOR)
regen_ticks_needed = 240 // ticks every 2 seconds, 480 seconds, so roughly 8 minutes default
/datum/wound_pregen_data/bone/compound
@@ -396,6 +369,8 @@
wound_path_to_generate = /datum/wound/blunt/bone/critical
threshold_minimum = 115
// doesn't make much sense for "a" bone to stick out of your head
/datum/wound/blunt/bone/critical/apply_wound(obj/item/bodypart/L, silent = FALSE, datum/wound/old_wound = null, smited = FALSE, attack_direction = null, wound_source = "Unknown")
if(L.body_zone == BODY_ZONE_HEAD)
+14 -12
View File
@@ -7,28 +7,24 @@
/datum/wound/burn
name = "Burn Wound"
a_or_from = "from"
wound_type = WOUND_BURN
sound_effect = 'sound/effects/wounds/sizzle1.ogg'
/datum/wound/burn/flesh
name = "Burn (Flesh) Wound"
a_or_from = "from"
wound_type = WOUND_BURN
processes = TRUE
scar_file = FLESH_SCAR_FILE
wound_series = WOUND_SERIES_FLESH_BURN_BASIC
default_scar_file = FLESH_SCAR_FILE
treatable_by = list(/obj/item/stack/medical/ointment, /obj/item/stack/medical/mesh) // sterilizer and alcohol will require reagent treatments, coming soon
// Flesh damage vars
// Flesh damage vars
/// How much damage to our flesh we currently have. Once both this and infestation reach 0, the wound is considered healed
var/flesh_damage = 5
/// Our current counter for how much flesh regeneration we have stacked from regenerative mesh/synthflesh/whatever, decrements each tick and lowers flesh_damage
var/flesh_healing = 0
// Infestation vars (only for severe and critical)
// Infestation vars (only for severe and critical)
/// How quickly infection breeds on this burn if we don't have disinfectant
var/infestation_rate = 0
/// Our current level of infection
@@ -272,8 +268,11 @@
/datum/wound_pregen_data/flesh_burn
abstract = TRUE
required_wounding_types = list(WOUND_BURN)
required_limb_biostate = BIO_FLESH
wound_series = WOUND_SERIES_FLESH_BURN_BASIC
/datum/wound/burn/get_limb_examine_description()
return span_warning("The flesh on this limb appears badly cooked.")
@@ -286,7 +285,6 @@
occur_text = "breaks out with violent red burns"
severity = WOUND_SEVERITY_MODERATE
damage_mulitplier_penalty = 1.1
threshold_minimum = 40
threshold_penalty = 30 // burns cause significant decrease in limb integrity compared to other wounds
status_effect_type = /datum/status_effect/wound/burn/flesh/moderate
flesh_damage = 5
@@ -297,6 +295,8 @@
wound_path_to_generate = /datum/wound/burn/flesh/moderate
threshold_minimum = 40
/datum/wound/burn/flesh/severe
name = "Third Degree Burns"
desc = "Patient is suffering extreme burns with full skin penetration, creating serious risk of infection and greatly reduced limb integrity."
@@ -305,7 +305,6 @@
occur_text = "chars rapidly, exposing ruined tissue and spreading angry red burns"
severity = WOUND_SEVERITY_SEVERE
damage_mulitplier_penalty = 1.2
threshold_minimum = 80
threshold_penalty = 40
status_effect_type = /datum/status_effect/wound/burn/flesh/severe
treatable_by = list(/obj/item/flashlight/pen/paramedic, /obj/item/stack/medical/ointment, /obj/item/stack/medical/mesh)
@@ -318,6 +317,8 @@
wound_path_to_generate = /datum/wound/burn/flesh/severe
threshold_minimum = 80
/datum/wound/burn/flesh/critical
name = "Catastrophic Burns"
desc = "Patient is suffering near complete loss of tissue and significantly charred muscle and bone, creating life-threatening risk of infection and negligible limb integrity."
@@ -327,7 +328,6 @@
severity = WOUND_SEVERITY_CRITICAL
damage_mulitplier_penalty = 1.3
sound_effect = 'sound/effects/wounds/sizzle2.ogg'
threshold_minimum = 140
threshold_penalty = 80
status_effect_type = /datum/status_effect/wound/burn/flesh/critical
treatable_by = list(/obj/item/flashlight/pen/paramedic, /obj/item/stack/medical/ointment, /obj/item/stack/medical/mesh)
@@ -340,6 +340,8 @@
wound_path_to_generate = /datum/wound/burn/flesh/critical
threshold_minimum = 140
///special severe wound caused by sparring interference or other god related punishments.
/datum/wound/burn/flesh/severe/brand
name = "Holy Brand"
@@ -347,7 +349,7 @@
examine_desc = "appears to have holy symbols painfully branded into their flesh, leaving severe burns."
occur_text = "chars rapidly into a strange pattern of holy symbols, burned into the flesh."
/datum/wound_pregen_data/flesh_burn/holy
/datum/wound_pregen_data/flesh_burn/third_degree/holy
abstract = FALSE
can_be_randomly_generated = FALSE
@@ -363,7 +365,7 @@
/datum/wound/burn/flesh/severe/cursed_brand/get_limb_examine_description()
return span_warning("The flesh on this limb has several ornate symbols burned into it, with pitting throughout.")
/datum/wound_pregen_data/flesh_burn/cursed_brand
/datum/wound_pregen_data/flesh_burn/third_degree/cursed_brand
abstract = FALSE
can_be_randomly_generated = FALSE
+13 -25
View File
@@ -3,7 +3,13 @@
wound_path_to_generate = /datum/wound/loss
required_limb_biostate = NONE
check_for_any = TRUE
require_any_biostate = TRUE
required_wounding_types = list(WOUND_ALL)
wound_series = WOUND_SERIES_LOSS_BASIC
threshold_minimum = WOUND_DISMEMBER_OUTRIGHT_THRESH // not actually used since dismembering is handled differently, but may as well assign it since we got it
/datum/wound/loss
name = "Dismemberment Wound"
@@ -11,14 +17,13 @@
sound_effect = 'sound/effects/dismember.ogg'
severity = WOUND_SEVERITY_LOSS
threshold_minimum = WOUND_DISMEMBER_OUTRIGHT_THRESH // not actually used since dismembering is handled differently, but may as well assign it since we got it
status_effect_type = null
scar_keyword = "dismember"
wound_flags = null
already_scarred = TRUE // We manually assign scars for dismembers through endround missing limbs and aheals
/// The wound_type of the attack that caused us. Used to generate the description of our scar. Currently unused, but primarily exists in case non-biological wounds are added.
var/loss_wound_type
/// The wounding_type of the attack that caused us. Used to generate the description of our scar. Currently unused, but primarily exists in case non-biological wounds are added.
var/loss_wounding_type
/// Our special proc for our special dismembering, the wounding type only matters for what text we have
/datum/wound/loss/proc/apply_dismember(obj/item/bodypart/dismembered_part, wounding_type = WOUND_SLASH, outright = FALSE, attack_direction)
@@ -39,14 +44,14 @@
victim.visible_message(msg, span_userdanger("Your [dismembered_part.plaintext_zone] [self_msg ? self_msg : occur_text]"))
loss_wound_type = wounding_type
loss_wounding_type = wounding_type
set_limb(dismembered_part)
second_wind()
log_wound(victim, src)
if(dismembered_part.can_bleed() && wounding_type != WOUND_BURN && victim.blood_volume)
victim.spray_blood(attack_direction, severity)
dismembered_part.dismember(wounding_type == WOUND_BURN ? BURN : BRUTE, wound_type = wounding_type)
dismembered_part.dismember(wounding_type == WOUND_BURN ? BURN : BRUTE, wounding_type = wounding_type)
qdel(src)
return TRUE
@@ -64,17 +69,8 @@
if(WOUND_BURN)
occur_text = "is outright incinerated, falling to dust!"
else
var/bone_text
if (biological_state & BIO_BONE)
bone_text = "bone"
else if (biological_state & BIO_METAL)
bone_text = "metal"
var/tissue_text
if (biological_state & BIO_FLESH)
tissue_text = "flesh"
else if (biological_state & BIO_WIRED)
tissue_text = "wire"
var/bone_text = get_internal_description()
var/tissue_text = get_external_description()
switch(wounding_type)
if(WOUND_BLUNT)
@@ -87,11 +83,3 @@
occur_text = "is completely incinerated, falling to dust!"
return occur_text
/datum/wound/loss/get_scar_file(obj/item/bodypart/scarred_limb, add_to_scars)
if (scarred_limb.biological_state & BIO_FLESH)
return FLESH_SCAR_FILE
if (scarred_limb.biological_state & BIO_BONE)
return BONE_SCAR_FILE
return ..()
+21 -18
View File
@@ -3,20 +3,17 @@
Piercing wounds
*/
/datum/wound/pierce
wound_type = WOUND_PIERCE
/datum/wound/pierce/bleed
name = "Piercing Wound"
sound_effect = 'sound/weapons/slice.ogg'
processes = TRUE
treatable_by = list(/obj/item/stack/medical/suture)
treatable_tool = TOOL_CAUTERY
treatable_tools = list(TOOL_CAUTERY)
base_treat_time = 3 SECONDS
wound_flags = (ACCEPTS_GAUZE)
wound_flags = (ACCEPTS_GAUZE | CAN_BE_GRASPED)
wound_series = WOUND_SERIES_FLESH_PUNCTURE_BLEED
scar_file = FLESH_SCAR_FILE
default_scar_file = FLESH_SCAR_FILE
/// How much blood we start losing when this wound is first applied
var/initial_flow
@@ -30,13 +27,13 @@
/datum/wound/pierce/bleed/wound_injury(datum/wound/old_wound = null, attack_direction = null)
set_blood_flow(initial_flow)
if(!no_bleeding && attack_direction && victim.blood_volume > BLOOD_VOLUME_OKAY)
if(limb.can_bleed() && attack_direction && victim.blood_volume > BLOOD_VOLUME_OKAY)
victim.spray_blood(attack_direction, severity)
return ..()
/datum/wound/pierce/bleed/receive_damage(wounding_type, wounding_dmg, wound_bonus)
if(victim.stat == DEAD || (wounding_dmg < 5) || no_bleeding || !victim.blood_volume || !prob(internal_bleeding_chance + wounding_dmg))
if(victim.stat == DEAD || (wounding_dmg < 5) || !limb.can_bleed() || !victim.blood_volume || !prob(internal_bleeding_chance + wounding_dmg))
return
if(limb.current_gauze?.splint_factor)
wounding_dmg *= (1 - limb.current_gauze.splint_factor)
@@ -59,7 +56,7 @@
/datum/wound/pierce/bleed/get_bleed_rate_of_change()
//basically if a species doesn't bleed, the wound is stagnant and will not heal on it's own (nor get worse)
if(no_bleeding)
if(!limb.can_bleed())
return BLOOD_FLOW_STEADY
if(HAS_TRAIT(victim, TRAIT_BLOODY_MESS))
return BLOOD_FLOW_INCREASING
@@ -73,7 +70,7 @@
set_blood_flow(min(blood_flow, WOUND_SLASH_MAX_BLOODFLOW))
if(!no_bleeding)
if(limb.can_bleed())
if(victim.bodytemperature < (BODYTEMP_NORMAL - 10))
adjust_blood_flow(-0.1 * seconds_per_tick)
if(SPT_PROB(2.5, seconds_per_tick))
@@ -127,7 +124,7 @@
if(!do_after(user, treatment_delay, target = victim, extra_checks = CALLBACK(src, PROC_REF(still_exists))))
return TRUE
var/bleeding_wording = (no_bleeding ? "holes" : "bleeding")
var/bleeding_wording = (!limb.can_bleed() ? "holes" : "bleeding")
user.visible_message(span_green("[user] stitches up some of the [bleeding_wording] on [victim]."), span_green("You stitch up some of the [bleeding_wording] on [user == victim ? "yourself" : "[victim]"]."))
var/blood_sutured = I.stop_bleeding / self_penalty_mult
adjust_blood_flow(-blood_sutured)
@@ -157,7 +154,7 @@
if(!do_after(user, treatment_delay, target = victim, extra_checks = CALLBACK(src, PROC_REF(still_exists))))
return TRUE
var/bleeding_wording = (no_bleeding ? "holes" : "bleeding")
var/bleeding_wording = (!limb.can_bleed() ? "holes" : "bleeding")
user.visible_message(span_green("[user] cauterizes some of the [bleeding_wording] on [victim]."), span_green("You cauterize some of the [bleeding_wording] on [victim]."))
limb.receive_damage(burn = 2 + severity, wound_bonus = CANT_WOUND)
if(prob(30))
@@ -173,6 +170,9 @@
abstract = TRUE
required_limb_biostate = (BIO_FLESH)
required_wounding_types = list(WOUND_PIERCE)
wound_series = WOUND_SERIES_FLESH_PUNCTURE_BLEED
/datum/wound/pierce/get_limb_examine_description()
return span_warning("The flesh on this limb appears badly perforated.")
@@ -189,7 +189,6 @@
gauzed_clot_rate = 0.8
internal_bleeding_chance = 30
internal_bleeding_coefficient = 1.25
threshold_minimum = 30
threshold_penalty = 20
status_effect_type = /datum/status_effect/wound/pierce/moderate
scar_keyword = "piercemoderate"
@@ -199,8 +198,10 @@
wound_path_to_generate = /datum/wound/pierce/bleed/moderate
threshold_minimum = 30
/datum/wound/pierce/bleed/moderate/update_descriptions()
if(no_bleeding)
if(!limb.can_bleed())
examine_desc = "has a small, circular hole"
occur_text = "splits a small hole open"
@@ -216,7 +217,6 @@
gauzed_clot_rate = 0.6
internal_bleeding_chance = 60
internal_bleeding_coefficient = 1.5
threshold_minimum = 50
threshold_penalty = 35
status_effect_type = /datum/status_effect/wound/pierce/severe
scar_keyword = "piercesevere"
@@ -226,8 +226,10 @@
wound_path_to_generate = /datum/wound/pierce/bleed/severe
threshold_minimum = 50
/datum/wound/pierce/bleed/severe/update_descriptions()
if(no_bleeding)
if(!limb.can_bleed())
occur_text = "tears a hole open"
/datum/wound/pierce/bleed/critical
@@ -242,13 +244,14 @@
gauzed_clot_rate = 0.4
internal_bleeding_chance = 80
internal_bleeding_coefficient = 1.75
threshold_minimum = 100
threshold_penalty = 50
status_effect_type = /datum/status_effect/wound/pierce/critical
scar_keyword = "piercecritical"
wound_flags = (ACCEPTS_GAUZE | MANGLES_FLESH)
wound_flags = (ACCEPTS_GAUZE | MANGLES_EXTERIOR | CAN_BE_GRASPED)
/datum/wound_pregen_data/flesh_pierce/cavity
abstract = FALSE
wound_path_to_generate = /datum/wound/pierce/bleed/critical
threshold_minimum = 100
+2 -2
View File
@@ -61,7 +61,7 @@
return
required_limb_biostate = pregen_data.required_limb_biostate
check_any_biostates = pregen_data.check_for_any
check_any_biostates = pregen_data.require_any_biostate
limb = BP
RegisterSignal(limb, COMSIG_QDELETING, PROC_REF(limb_gone))
@@ -103,7 +103,7 @@
LAZYADD(victim.all_scars, src)
/// Used to "load" a persistent scar
/datum/scar/proc/load(obj/item/bodypart/BP, version, description, specific_location, severity = WOUND_SEVERITY_SEVERE, required_limb_biostate = BIO_STANDARD, char_slot, check_any_biostates = FALSE)
/datum/scar/proc/load(obj/item/bodypart/BP, version, description, specific_location, severity = WOUND_SEVERITY_SEVERE, required_limb_biostate = BIO_STANDARD_UNJOINTED, char_slot, check_any_biostates = FALSE)
if(!BP.scarrable)
qdel(src)
return
+35 -24
View File
@@ -6,26 +6,25 @@
/datum/wound/slash
name = "Slashing (Cut) Wound"
sound_effect = 'sound/weapons/slice.ogg'
wound_type = WOUND_SLASH
/datum/wound_pregen_data/flesh_slash
abstract = TRUE
required_wounding_types = list(WOUND_SLASH)
required_limb_biostate = BIO_FLESH
wound_series = WOUND_SERIES_FLESH_SLASH_BLEED
/datum/wound/slash/flesh
name = "Slashing (Cut) Flesh Wound"
processes = TRUE
wound_type = WOUND_SLASH
treatable_by = list(/obj/item/stack/medical/suture)
treatable_by_grabbed = list(/obj/item/gun/energy/laser)
treatable_tool = TOOL_CAUTERY
treatable_tools = list(TOOL_CAUTERY)
base_treat_time = 3 SECONDS
wound_flags = (ACCEPTS_GAUZE)
wound_flags = (ACCEPTS_GAUZE|CAN_BE_GRASPED)
scar_file = FLESH_SCAR_FILE
wound_series = WOUND_SERIES_FLESH_SLASH_BLEED
default_scar_file = FLESH_SCAR_FILE
/// How much blood we start losing when this wound is first applied
var/initial_flow
@@ -43,6 +42,11 @@
/// A bad system I'm using to track the worst scar we earned (since we can demote, we want the biggest our wound has been, not what it was when it was cured (probably moderate))
var/datum/scar/highest_scar
/datum/wound/slash/flesh/Destroy()
highest_scar = null
return ..()
/datum/wound/slash/flesh/wound_injury(datum/wound/slash/flesh/old_wound = null, attack_direction = null)
if(old_wound)
set_blood_flow(max(old_wound.blood_flow, initial_flow))
@@ -51,7 +55,7 @@
old_wound.clear_highest_scar()
else
set_blood_flow(initial_flow)
if(!no_bleeding && attack_direction && victim.blood_volume > BLOOD_VOLUME_OKAY)
if(limb.can_bleed() && attack_direction && victim.blood_volume > BLOOD_VOLUME_OKAY)
victim.spray_blood(attack_direction, severity)
if(!highest_scar)
@@ -119,7 +123,7 @@
/datum/wound/slash/flesh/get_bleed_rate_of_change()
//basically if a species doesn't bleed, the wound is stagnant and will not heal on it's own (nor get worse)
if(no_bleeding)
if(!limb.can_bleed())
return BLOOD_FLOW_STEADY
if(HAS_TRAIT(victim, TRAIT_BLOODY_MESS))
return BLOOD_FLOW_INCREASING
@@ -134,7 +138,7 @@
return
// in case the victim has the NOBLOOD trait, the wound will simply not clot on it's own
if(!no_bleeding)
if(limb.can_bleed())
set_blood_flow(min(blood_flow, WOUND_SLASH_MAX_BLOODFLOW))
if(HAS_TRAIT(victim, TRAIT_BLOODY_MESS))
@@ -147,7 +151,7 @@
adjust_blood_flow(-limb.current_gauze.absorption_rate * seconds_per_tick)
limb.seep_gauze(limb.current_gauze.absorption_rate * seconds_per_tick)
//otherwise, only clot if it's a bleeder
else if(!no_bleeding)
else if(limb.can_bleed())
adjust_blood_flow(-clot_rate * seconds_per_tick)
if(blood_flow > highest_flow)
@@ -157,7 +161,7 @@
if(demotes_to)
replace_wound(new demotes_to)
else
to_chat(victim, span_green("The cut on your [limb.plaintext_zone] has [no_bleeding ? "healed up" : "stopped bleeding"]!"))
to_chat(victim, span_green("The cut on your [limb.plaintext_zone] has [!limb.can_bleed() ? "healed up" : "stopped bleeding"]!"))
qdel(src)
/datum/wound/slash/flesh/on_stasis(seconds_per_tick, times_fired)
@@ -225,7 +229,7 @@
/datum/wound/slash/flesh/on_xadone(power)
. = ..()
if (limb) // parent can cause us to be removed, so its reasonable to check if we're still applied
adjust_blood_flow(-0.03 * power) // i think it's like a minimum of 3 power, so .09 blood_flow reduction per tick is pretty good for 0 effort
@@ -264,7 +268,7 @@
if(!do_after(user, treatment_delay, target = victim, extra_checks = CALLBACK(src, PROC_REF(still_exists))))
return
var/bleeding_wording = (no_bleeding ? "cuts" : "bleeding")
var/bleeding_wording = (!limb.can_bleed() ? "cuts" : "bleeding")
user.visible_message(span_green("[user] cauterizes some of the [bleeding_wording] on [victim]."), span_green("You cauterize some of the [bleeding_wording] on [victim]."))
limb.receive_damage(burn = 2 + severity, wound_bonus = CANT_WOUND)
if(prob(30))
@@ -292,7 +296,7 @@
if(!do_after(user, treatment_delay, target = victim, extra_checks = CALLBACK(src, PROC_REF(still_exists))))
return TRUE
var/bleeding_wording = (no_bleeding ? "cuts" : "bleeding")
var/bleeding_wording = (!limb.can_bleed() ? "cuts" : "bleeding")
user.visible_message(span_green("[user] stitches up some of the [bleeding_wording] on [victim]."), span_green("You stitch up some of the [bleeding_wording] on [user == victim ? "yourself" : "[victim]"]."))
var/blood_sutured = I.stop_bleeding / self_penalty_mult
adjust_blood_flow(-blood_sutured)
@@ -320,13 +324,12 @@
initial_flow = 2
minimum_flow = 0.5
clot_rate = 0.05
threshold_minimum = 20
threshold_penalty = 10
status_effect_type = /datum/status_effect/wound/slash/flesh/moderate
scar_keyword = "slashmoderate"
/datum/wound/slash/flesh/moderate/update_descriptions()
if(no_bleeding)
if(!limb.can_bleed())
occur_text = "is cut open"
/datum/wound_pregen_data/flesh_slash/abrasion
@@ -334,6 +337,8 @@
wound_path_to_generate = /datum/wound/slash/flesh/moderate
threshold_minimum = 20
/datum/wound/slash/flesh/severe
name = "Open Laceration"
desc = "Patient's skin is ripped clean open, allowing significant blood loss."
@@ -345,7 +350,6 @@
initial_flow = 3.25
minimum_flow = 2.75
clot_rate = 0.03
threshold_minimum = 50
threshold_penalty = 25
demotes_to = /datum/wound/slash/flesh/moderate
status_effect_type = /datum/status_effect/wound/slash/flesh/severe
@@ -356,8 +360,10 @@
wound_path_to_generate = /datum/wound/slash/flesh/severe
threshold_minimum = 50
/datum/wound/slash/flesh/severe/update_descriptions()
if(no_bleeding)
if(!limb.can_bleed())
occur_text = "is ripped open"
/datum/wound/slash/flesh/critical
@@ -371,25 +377,30 @@
initial_flow = 4
minimum_flow = 3.85
clot_rate = -0.015 // critical cuts actively get worse instead of better
threshold_minimum = 80
threshold_penalty = 40
demotes_to = /datum/wound/slash/flesh/severe
status_effect_type = /datum/status_effect/wound/slash/flesh/critical
scar_keyword = "slashcritical"
wound_flags = (ACCEPTS_GAUZE | MANGLES_FLESH)
wound_flags = (ACCEPTS_GAUZE | MANGLES_EXTERIOR | CAN_BE_GRASPED)
/datum/wound/slash/flesh/critical/update_descriptions()
if (!limb.can_bleed())
occur_text = "is torn open"
/datum/wound_pregen_data/flesh_slash/avulsion
abstract = FALSE
wound_path_to_generate = /datum/wound/slash/flesh/critical
threshold_minimum = 80
/datum/wound/slash/flesh/moderate/many_cuts
name = "Numerous Small Slashes"
desc = "Patient's skin has numerous small slashes and cuts, generating moderate blood loss."
examine_desc = "has a ton of small cuts"
occur_text = "is cut numerous times, leaving many small slashes."
/datum/wound_pregen_data/flesh_slash/cuts
/datum/wound_pregen_data/flesh_slash/abrasion/cuts
abstract = FALSE
can_be_randomly_generated = FALSE
@@ -402,10 +413,10 @@
clot_rate = 0.01
/datum/wound/slash/flesh/critical/cleave/update_descriptions()
if(no_bleeding)
if(!limb.can_bleed())
occur_text = "is ruptured"
/datum/wound_pregen_data/flesh_slash/cleave
/datum/wound_pregen_data/flesh_slash/avulsion/clear
abstract = FALSE
can_be_randomly_generated = FALSE
+2 -2
View File
@@ -243,12 +243,12 @@
if(!(get_dist(src, attached) <= 1 && isturf(attached.loc)))
if(isliving(attached))
var/mob/living/attached_mob = attached
var/mob/living/carbon/attached_mob = attached
to_chat(attached, span_userdanger("The IV drip needle is ripped out of you, leaving an open bleeding wound!"))
var/list/arm_zones = shuffle(list(BODY_ZONE_R_ARM, BODY_ZONE_L_ARM))
var/obj/item/bodypart/chosen_limb = attached_mob.get_bodypart(arm_zones[1]) || attached_mob.get_bodypart(arm_zones[2]) || attached_mob.get_bodypart(BODY_ZONE_CHEST)
chosen_limb.receive_damage(3)
chosen_limb.force_wound_upwards(/datum/wound/pierce/bleed/moderate, wound_source = "IV needle")
attached_mob.cause_wound_of_type_and_severity(WOUND_PIERCE, chosen_limb, WOUND_SEVERITY_MODERATE, wound_source = "IV needle")
else
visible_message(span_warning("[attached] is detached from [src]."))
detach_iv()
+1 -1
View File
@@ -128,7 +128,7 @@
return FALSE
var/obj/item/bodypart/head/the_head = target.get_bodypart(BODY_ZONE_HEAD)
if(!(the_head.biological_state & BIO_FLESH) || !IS_ORGANIC_LIMB(the_head))
if(!(the_head.biological_state & BIO_FLESH))
to_chat(user, span_warning("You can't noogie [target], [target.p_they()] [target.p_have()] no skin on [target.p_their()] head!"))
return
+1 -1
View File
@@ -433,7 +433,7 @@
patient.emote("scream")
for(var/i in patient.bodyparts)
var/obj/item/bodypart/bone = i
var/obj/item/bodypart/bone = i // 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
@@ -37,8 +37,11 @@ can next move
/// Other modification datums this conflicts with.
var/conflicts_with
/datum/actionspeed_modifier/New()
/datum/actionspeed_modifier/New(init_id)
. = ..()
id = init_id
if(!id)
id = "[type]" //We turn the path into a string.
@@ -0,0 +1,10 @@
/datum/actionspeed_modifier/wound_interaction_inefficiency
variable = TRUE
var/datum/wound/parent
/datum/actionspeed_modifier/wound_interaction_inefficiency/New(new_id, datum/wound/parent)
src.parent = parent
return ..()
+1 -1
View File
@@ -9,7 +9,7 @@
return
var/mob/living/carbon/carbon_target = target
for(var/_limb in carbon_target.bodyparts)
var/obj/item/bodypart/limb = _limb
var/obj/item/bodypart/limb = _limb // fine to use this raw, its a meme smite
var/type_wound = pick(list(/datum/wound/slash/flesh/severe, /datum/wound/slash/flesh/moderate))
limb.force_wound_upwards(type_wound, smited = TRUE)
type_wound = pick(list(/datum/wound/slash/flesh/critical, /datum/wound/slash/flesh/severe, /datum/wound/slash/flesh/moderate))
+7 -7
View File
@@ -11,11 +11,11 @@
var/mob/living/carbon/carbon_target = target
for(var/obj/item/bodypart/limb as anything in carbon_target.bodyparts)
var/type_wound = pick(list(
/datum/wound/blunt/bone/critical,
/datum/wound/blunt/bone/severe,
/datum/wound/blunt/bone/critical,
/datum/wound/blunt/bone/severe,
/datum/wound/blunt/bone/moderate,
var/severity = pick(list(
"[WOUND_SEVERITY_MODERATE]",
"[WOUND_SEVERITY_SEVERE]",
"[WOUND_SEVERITY_SEVERE]",
"[WOUND_SEVERITY_CRITICAL]",
"[WOUND_SEVERITY_CRITICAL]",
))
limb.force_wound_upwards(type_wound, smited = TRUE)
carbon_target.cause_wound_of_type_and_severity(WOUND_BLUNT, limb, severity)
@@ -269,8 +269,7 @@
var/mob/living/carbon/carbon_target = target
var/obj/item/bodypart/bodypart = pick(carbon_target.bodyparts)
var/datum/wound/slash/flesh/severe/crit_wound = new()
crit_wound.apply_wound(bodypart, attack_direction = get_dir(source, target))
carbon_target.cause_wound_of_type_and_severity(WOUND_SLASH, bodypart, WOUND_SEVERITY_SEVERE, WOUND_SEVERITY_CRITICAL)
/datum/heretic_knowledge/summon/stalker
name = "Lonely Ritual"
@@ -74,7 +74,8 @@
heal_amt = 3
if(WOUND_SEVERITY_CRITICAL)
heal_amt = 6
if(wound.wound_type == WOUND_BURN)
var/datum/wound_pregen_data/pregen_data = GLOB.all_wound_pregen_data[wound.type]
if (pregen_data.wounding_types_valid(list(WOUND_BURN)))
carbie.adjustFireLoss(-heal_amt)
else
carbie.adjustBruteLoss(-heal_amt)
@@ -61,8 +61,7 @@
if(ishuman(owner))
var/mob/living/carbon/human/human_owner = owner
var/obj/item/bodypart/bodypart = pick(human_owner.bodyparts)
var/datum/wound/slash/flesh/severe/crit_wound = new()
crit_wound.apply_wound(bodypart)
human_owner.cause_wound_of_type_and_severity(WOUND_SLASH, bodypart, WOUND_SEVERITY_SEVERE)
return ..()
@@ -179,9 +179,10 @@
if(!do_after(user, eye_snatch_enthusiasm, target = target, extra_checks = CALLBACK(src, PROC_REF(eyeballs_exist), eyeballies, head, target)))
return
var/datum/wound/blunt/bone/severe/severe_wound_type = /datum/wound/blunt/bone/severe
var/datum/wound/blunt/bone/critical/critical_wound_type = /datum/wound/blunt/bone/critical
target.apply_damage(20, BRUTE, BODY_ZONE_HEAD, wound_bonus = rand(initial(severe_wound_type.threshold_minimum), initial(critical_wound_type.threshold_minimum) + 10), attacking_item = src)
var/min_wound = head.get_wound_threshold_of_wound_type(WOUND_BLUNT, WOUND_SEVERITY_SEVERE, return_value_if_no_wound = 30, wound_source = src)
var/max_wound = head.get_wound_threshold_of_wound_type(WOUND_BLUNT, WOUND_SEVERITY_CRITICAL, return_value_if_no_wound = 50, wound_source = src)
target.apply_damage(20, BRUTE, BODY_ZONE_HEAD, wound_bonus = rand(min_wound, max_wound + 10), attacking_item = src)
target.visible_message(
span_danger("[src] pierces through [target]'s skull, horribly mutilating their eyes!"),
span_userdanger("Something penetrates your skull, horribly mutilating your eyes! Holy fuck!"),
@@ -13,8 +13,12 @@
/obj/effect/mob_spawn/corpse/human/tigercultist/perforated/special(mob/living/carbon/human/spawned_human)
. = ..()
var/datum/wound/pierce/bleed/critical/exit_hole = new()
exit_hole.apply_wound(spawned_human.get_bodypart(BODY_ZONE_CHEST))
var/obj/item/bodypart/chest/their_chest = spawned_human.get_bodypart(BODY_ZONE_CHEST)
if (!their_chest)
return
spawned_human.cause_wound_of_type_and_severity(WOUND_PIERCE, their_chest, WOUND_SEVERITY_CRITICAL)
/// A fun drink enjoyed by the tiger cooperative, might corrode your brain if you drink the whole bottle
/obj/item/reagent_containers/cup/glass/bottle/ritual_wine
@@ -100,7 +100,7 @@
if(I.force)
var/attack_direction = get_dir(user, src)
apply_damage(I.force, I.damtype, affecting, wound_bonus = I.wound_bonus, bare_wound_bonus = I.bare_wound_bonus, sharpness = I.get_sharpness(), attack_direction = attack_direction, attacking_item = I)
if(I.damtype == BRUTE && IS_ORGANIC_LIMB(affecting))
if(I.damtype == BRUTE && affecting.can_bleed())
if(prob(33))
I.add_mob_blood(src)
var/turf/location = get_turf(src)
@@ -127,22 +127,35 @@
var/message_verb_simple = length(I.attack_verb_simple) ? "[pick(I.attack_verb_simple)]" : "attack"
var/extra_wound_details = ""
if(I.damtype == BRUTE && hit_bodypart.can_dismember())
var/mangled_state = hit_bodypart.get_mangled_state()
var/bio_state = hit_bodypart.biological_state
if((mangled_state & BODYPART_MANGLED_FLESH) && (mangled_state & BODYPART_MANGLED_BONE))
var/bio_status = hit_bodypart.get_bio_state_status()
var/has_exterior = ((bio_status & ANATOMY_EXTERIOR))
var/has_interior = ((bio_status & ANATOMY_INTERIOR))
var/exterior_ready_to_dismember = (!has_exterior || ((mangled_state & BODYPART_MANGLED_EXTERIOR)))
var/interior_ready_to_dismember = (!has_interior || ((mangled_state & BODYPART_MANGLED_INTERIOR)))
var/dismemberable = ((hit_bodypart.dismemberable_by_wound()) || hit_bodypart.dismemberable_by_total_damage())
if (dismemberable)
extra_wound_details = ", threatening to sever it entirely"
else if((mangled_state & BODYPART_MANGLED_FLESH && I.get_sharpness()) || ((mangled_state & BODYPART_MANGLED_BONE) && (bio_state & BIO_BONE) && !(bio_state & BIO_FLESH)))
extra_wound_details = ", [I.get_sharpness() == SHARP_EDGED ? "slicing" : "piercing"] through to the bone"
else if((mangled_state & BODYPART_MANGLED_BONE && I.get_sharpness()) || ((mangled_state & BODYPART_MANGLED_FLESH) && (bio_state & BIO_FLESH) && !(bio_state & BIO_BONE)))
extra_wound_details = ", [I.get_sharpness() == SHARP_EDGED ? "slicing" : "piercing"] at the remaining tissue"
else if((has_interior && (has_exterior && exterior_ready_to_dismember) && I.get_sharpness()))
var/bone_text = hit_bodypart.get_internal_description()
extra_wound_details = ", [I.get_sharpness() == SHARP_EDGED ? "slicing" : "piercing"] through to the [bone_text]"
else if(has_exterior && ((has_interior && interior_ready_to_dismember) && I.get_sharpness()))
var/tissue_text = hit_bodypart.get_external_description()
extra_wound_details = ", [I.get_sharpness() == SHARP_EDGED ? "slicing" : "piercing"] at the remaining [tissue_text]"
var/message_hit_area = ""
if(hit_area)
message_hit_area = " in the [hit_area]"
var/attack_message_spectator = "[src] [message_verb_continuous][message_hit_area] with [I][extra_wound_details]!"
var/attack_message_victim = "You're [message_verb_continuous][message_hit_area] with [I][extra_wound_details]!"
var/attack_message_attacker = "You [message_verb_simple] [src][message_hit_area] with [I]!"
var/attack_message_attacker = "You [message_verb_simple] [src][message_hit_area] with [I][extra_wound_details]!"
if(user in viewers(src, null))
attack_message_spectator = "[user] [message_verb_continuous] [src][message_hit_area] with [I][extra_wound_details]!"
attack_message_victim = "[user] [message_verb_continuous] you[message_hit_area] with [I][extra_wound_details]!"
@@ -698,14 +711,16 @@
return ..()
var/obj/item/bodypart/grasped_part = get_bodypart(zone_selected)
if(!grasped_part?.get_modified_bleed_rate())
if(!grasped_part?.can_be_grasped())
return
var/starting_hand_index = active_hand_index
if(starting_hand_index == grasped_part.held_index)
to_chat(src, span_danger("You can't grasp your [grasped_part.name] with itself!"))
return
to_chat(src, span_warning("You try grasping at your [grasped_part.name], trying to stop the bleeding..."))
var/bleed_rate = grasped_part.get_modified_bleed_rate()
var/bleeding_text = (bleed_rate ? ", trying to stop the bleeding" : "")
to_chat(src, span_warning("You try grasping at your [grasped_part.name][bleeding_text]..."))
if(!do_after(src, 0.75 SECONDS))
to_chat(src, span_danger("You fail to grasp your [grasped_part.name]."))
return
@@ -717,6 +732,17 @@
return
grasp.grasp_limb(grasped_part)
/// If TRUE, the owner of this bodypart can try grabbing it to slow bleeding, as well as various other effects.
/obj/item/bodypart/proc/can_be_grasped()
if (get_modified_bleed_rate())
return TRUE
for (var/datum/wound/iterated_wound as anything in wounds)
if (iterated_wound.wound_flags & CAN_BE_GRASPED)
return TRUE
return FALSE
/// an abstract item representing you holding your own limb to staunch the bleeding, see [/mob/living/carbon/proc/grabbedby] will probably need to find somewhere else to put this.
/obj/item/hand_item/self_grasp
name = "self-grasp"
@@ -761,7 +787,9 @@
RegisterSignal(user, COMSIG_QDELETING, PROC_REF(qdel_void))
RegisterSignals(grasped_part, list(COMSIG_CARBON_REMOVE_LIMB, COMSIG_QDELETING), PROC_REF(qdel_void))
user.visible_message(span_danger("[user] grasps at [user.p_their()] [grasped_part.name], trying to stop the bleeding."), span_notice("You grab hold of your [grasped_part.name] tightly."), vision_distance=COMBAT_MESSAGE_RANGE)
var/bleed_rate = grasped_part.get_modified_bleed_rate()
var/bleeding_text = (bleed_rate ? ", trying to stop the bleeding" : "")
user.visible_message(span_danger("[user] grasps at [user.p_their()] [grasped_part.name][bleeding_text]."), span_notice("You grab hold of your [grasped_part.name] tightly."), vision_distance=COMBAT_MESSAGE_RANGE)
playsound(get_turf(src), 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
return TRUE
@@ -104,7 +104,7 @@
/// All of the scars a carbon has afflicted throughout their limbs
var/list/all_scars
/// Assoc list of BODY_ZONE -> WOUND_TYPE. Set when a limb is dismembered, unset when one is attached. Used for determining what scar to add when it comes time to generate them.
/// Assoc list of BODY_ZONE -> wounding_type. Set when a limb is dismembered, unset when one is attached. Used for determining what scar to add when it comes time to generate them.
var/list/body_zone_dismembered_by
/// Simple modifier for whether this mob can handle greater or lesser skillchip complexity. See /datum/mutation/human/biotechcompat/ for example.
@@ -1667,19 +1667,26 @@ GLOBAL_LIST_EMPTY(features_by_species)
// Lets pick a random body part and check for an existing burn
var/obj/item/bodypart/bodypart = pick(humi.bodyparts)
var/datum/wound/burn/flesh/existing_burn = locate(/datum/wound/burn) in bodypart.wounds
var/datum/wound/existing_burn
for (var/datum/wound/iterated_wound as anything in bodypart.wounds)
var/datum/wound_pregen_data/pregen_data = iterated_wound.get_pregen_data()
if (pregen_data.wound_series in GLOB.wounding_types_to_series[WOUND_BURN])
existing_burn = iterated_wound
break
// If we have an existing burn try to upgrade it
var/severity
if(existing_burn)
switch(existing_burn.severity)
if(WOUND_SEVERITY_MODERATE)
if(humi.bodytemperature > BODYTEMP_HEAT_WOUND_LIMIT + 400) // 800k
bodypart.force_wound_upwards(/datum/wound/burn/flesh/severe, wound_source = "hot temperatures")
severity = WOUND_SEVERITY_SEVERE
if(WOUND_SEVERITY_SEVERE)
if(humi.bodytemperature > BODYTEMP_HEAT_WOUND_LIMIT + 2800) // 3200k
bodypart.force_wound_upwards(/datum/wound/burn/flesh/critical, wound_source = "hot temperatures")
severity = WOUND_SEVERITY_CRITICAL
else // If we have no burn apply the lowest level burn
bodypart.force_wound_upwards(/datum/wound/burn/flesh/moderate, wound_source = "hot temperatures")
severity = WOUND_SEVERITY_MODERATE
humi.cause_wound_of_type_and_severity(WOUND_BURN, bodypart, severity, wound_source = "hot temperatures")
// always take some burn damage
var/burn_damage = HEAT_DAMAGE_LEVEL_1
+3 -2
View File
@@ -171,8 +171,9 @@
to_chat(user, span_userdanger("You neatly cut [stored_paper][clumsy ? "... and your finger in the process!" : "."]"))
if(clumsy)
var/obj/item/bodypart/finger = user.get_active_hand()
var/datum/wound/slash/flesh/moderate/papercut = new
papercut.apply_wound(finger, wound_source = "paper cut")
if (iscarbon(user))
var/mob/living/carbon/carbon_user = user
carbon_user.cause_wound_of_type_and_severity(WOUND_SLASH, finger, WOUND_SEVERITY_MODERATE, wound_source = "paper cut")
stored_paper = null
qdel(stored_paper)
new /obj/item/paper/paperslip(get_turf(src))
@@ -21,7 +21,7 @@
span_userdanger("The spell bounces from [victim]'s skin back into your arm!"),
)
var/obj/item/bodypart/to_wound = caster.get_holding_bodypart_of_item(hand)
to_wound.force_wound_upwards(/datum/wound/slash/flesh/critical)
caster.cause_wound_of_type_and_severity(WOUND_SLASH, to_wound, WOUND_SEVERITY_MODERATE, WOUND_SEVERITY_CRITICAL)
/datum/action/cooldown/spell/touch/scream_for_me/cast_on_hand_hit(obj/item/melee/touch_attack/hand, mob/living/victim, mob/living/carbon/caster)
if(!ishuman(victim))
@@ -29,7 +29,7 @@
var/mob/living/carbon/human/human_victim = victim
human_victim.emote("scream")
for(var/obj/item/bodypart/to_wound as anything in human_victim.bodyparts)
to_wound.force_wound_upwards(/datum/wound/slash/flesh/critical)
human_victim.cause_wound_of_type_and_severity(WOUND_SLASH, to_wound, WOUND_SEVERITY_MODERATE, WOUND_SEVERITY_CRITICAL)
return TRUE
/obj/item/melee/touch_attack/scream_for_me
+109 -58
View File
@@ -31,9 +31,9 @@
/**
* A bitfield of biological states, exclusively used to determine which wounds this limb will get,
* as well as how easily it will happen.
* Set to BIO_STANDARD because most species have both flesh bone and blood in their limbs.
* Set to BIO_STANDARD_UNJOINTED because most species have both flesh bone and blood in their limbs.
*/
var/biological_state = BIO_STANDARD
var/biological_state = BIO_STANDARD_UNJOINTED
///A bitfield of bodytypes for clothing, surgery, and misc information
var/bodytype = BODYTYPE_HUMANOID | BODYTYPE_ORGANIC
///Defines when a bodypart should not be changed. Example: BP_BLOCK_CHANGE_SPECIES prevents the limb from being overwritten on species gain
@@ -195,10 +195,10 @@
var/hp_percent_to_dismemberable = 0.8
/// If true, we will use [hp_percent_to_dismemberable] even if we are dismemberable via wounds. Useful for things with extreme wound resistance.
var/use_alternate_dismemberment_calc_even_if_mangleable = FALSE
/// If false, no wound that can be applied to us can mangle our flesh. Used for determining if we should use [hp_percent_to_dismemberable] instead of normal dismemberment.
var/any_existing_wound_can_mangle_our_flesh
/// If false, no wound that can be applied to us can mangle our bone. Used for determining if we should use [hp_percent_to_dismemberable] instead of normal dismemberment.
var/any_existing_wound_can_mangle_our_bone
/// If false, no wound that can be applied to us can mangle our exterior. Used for determining if we should use [hp_percent_to_dismemberable] instead of normal dismemberment.
var/any_existing_wound_can_mangle_our_exterior
/// If false, no wound that can be applied to us can mangle our interior. Used for determining if we should use [hp_percent_to_dismemberable] instead of normal dismemberment.
var/any_existing_wound_can_mangle_our_interior
/obj/item/bodypart/apply_fantasy_bonuses(bonus)
. = ..()
@@ -495,73 +495,32 @@
var/mangled_state = get_mangled_state()
var/easy_dismember = HAS_TRAIT(owner, TRAIT_EASYDISMEMBER) // if we have easydismember, we don't reduce damage when redirecting damage to different types (slashing weapons on mangled/skinless limbs attack at 100% instead of 50%)
var/has_exterior = FALSE
var/has_interior = FALSE
var/bio_status = get_bio_state_status()
for (var/state as anything in GLOB.bio_state_states)
var/flag = text2num(state)
if (!(biological_state & flag))
continue
var/has_exterior = ((bio_status & ANATOMY_EXTERIOR))
var/has_interior = ((bio_status & ANATOMY_INTERIOR))
var/value = GLOB.bio_state_states[state]
if (value & BIO_EXTERIOR)
has_exterior = TRUE
if (value & BIO_INTERIOR)
has_interior = TRUE
if (has_exterior && has_interior)
break
// We put this here so we dont increase init time by doing this all at once on initialization
// Effectively, we "lazy load"
if (isnull(any_existing_wound_can_mangle_our_bone) || isnull(any_existing_wound_can_mangle_our_flesh))
any_existing_wound_can_mangle_our_bone = FALSE
any_existing_wound_can_mangle_our_flesh = FALSE
for (var/datum/wound/wound_type as anything in GLOB.all_wound_pregen_data)
var/datum/wound_pregen_data/pregen_data = GLOB.all_wound_pregen_data[wound_type]
if (!pregen_data.can_be_applied_to(src, random_roll = TRUE)) // we only consider randoms because non-randoms are usually really specific
continue
if (initial(pregen_data.wound_path_to_generate.wound_flags) & MANGLES_FLESH)
any_existing_wound_can_mangle_our_flesh = TRUE
if (initial(pregen_data.wound_path_to_generate.wound_flags) & MANGLES_BONE)
any_existing_wound_can_mangle_our_bone = TRUE
if (any_existing_wound_can_mangle_our_bone && any_existing_wound_can_mangle_our_flesh)
break
var/can_theoretically_be_dismembered = (any_existing_wound_can_mangle_our_bone || (any_existing_wound_can_mangle_our_flesh && !has_exterior))
var/exterior_ready_to_dismember = (!has_exterior || ((mangled_state & BODYPART_MANGLED_BONE) == BODYPART_MANGLED_BONE))
var/interior_ready_to_dismember = (!has_interior || ((mangled_state & BODYPART_MANGLED_FLESH) == BODYPART_MANGLED_FLESH))
var/exterior_ready_to_dismember = (!has_exterior || ((mangled_state & BODYPART_MANGLED_EXTERIOR)))
// if we're bone only, all cutting attacks go straight to the bone
if(has_exterior && interior_ready_to_dismember)
if(!has_exterior && has_interior)
if(wounding_type == WOUND_SLASH)
wounding_type = WOUND_BLUNT
wounding_dmg *= (easy_dismember ? 1 : 0.6)
else if(wounding_type == WOUND_PIERCE)
wounding_type = WOUND_BLUNT
wounding_dmg *= (easy_dismember ? 1 : 0.75)
if(exterior_ready_to_dismember && try_dismember(wounding_type, wounding_dmg, wound_bonus, bare_wound_bonus))
return
else
// if we've already mangled the skin (critical slash or piercing wound), then the bone is exposed, and we can damage it with sharp weapons at a reduced rate
// So a big sharp weapon is still all you need to destroy a limb
if(has_exterior && interior_ready_to_dismember && !(mangled_state & BODYPART_MANGLED_BONE) && sharpness)
playsound(src, "sound/effects/wounds/crackandbleed.ogg", 100)
if(has_interior && exterior_ready_to_dismember && !(mangled_state & BODYPART_MANGLED_INTERIOR) && sharpness)
if(wounding_type == WOUND_SLASH && !easy_dismember)
wounding_dmg *= 0.6 // edged weapons pass along 60% of their wounding damage to the bone since the power is spread out over a larger area
if(wounding_type == WOUND_PIERCE && !easy_dismember)
wounding_dmg *= 0.75 // piercing weapons pass along 75% of their wounding damage to the bone since it's more concentrated
wounding_type = WOUND_BLUNT
else if(interior_ready_to_dismember && exterior_ready_to_dismember && try_dismember(wounding_type, wounding_dmg, wound_bonus, bare_wound_bonus))
return
if (use_alternate_dismemberment_calc_even_if_mangleable || !can_theoretically_be_dismembered)
var/percent_to_total_max = (get_damage() / max_damage)
if (percent_to_total_max >= hp_percent_to_dismemberable)
if (try_dismember(wounding_type, wounding_dmg, wound_bonus, bare_wound_bonus))
return
if ((dismemberable_by_wound() || dismemberable_by_total_damage()) && try_dismember(wounding_type, wounding_dmg, wound_bonus, bare_wound_bonus))
return
// now we have our wounding_type and are ready to carry on with wounds and dealing the actual damage
if(wounding_dmg >= WOUND_MINIMUM_DAMAGE && wound_bonus != CANT_WOUND)
check_wounding(wounding_type, wounding_dmg, wound_bonus, bare_wound_bonus, attack_direction, damage_source = damage_source)
@@ -594,6 +553,83 @@
owner.updatehealth()
return update_bodypart_damage_state() || .
/// Returns a bitflag using ANATOMY_EXTERIOR or ANATOMY_INTERIOR. Used to determine if we as a whole have a interior or exterior biostate, or both.
/obj/item/bodypart/proc/get_bio_state_status()
SHOULD_BE_PURE(TRUE)
var/bio_status = NONE
for (var/state as anything in GLOB.bio_state_anatomy)
var/flag = text2num(state)
if (!(biological_state & flag))
continue
var/value = GLOB.bio_state_anatomy[state]
if (value & ANATOMY_EXTERIOR)
bio_status |= ANATOMY_EXTERIOR
if (value & ANATOMY_INTERIOR)
bio_status |= ANATOMY_INTERIOR
if ((bio_status & ANATOMY_EXTERIOR_AND_INTERIOR) == ANATOMY_EXTERIOR_AND_INTERIOR)
break
return bio_status
/// Returns if our current mangling status allows us to be dismembered. Requires both no exterior/mangled exterior and no interior/mangled interior.
/obj/item/bodypart/proc/dismemberable_by_wound()
SHOULD_BE_PURE(TRUE)
var/mangled_state = get_mangled_state()
var/bio_status = get_bio_state_status()
var/has_exterior = ((bio_status & ANATOMY_EXTERIOR))
var/has_interior = ((bio_status & ANATOMY_INTERIOR))
var/exterior_ready_to_dismember = (!has_exterior || ((mangled_state & BODYPART_MANGLED_EXTERIOR)))
var/interior_ready_to_dismember = (!has_interior || ((mangled_state & BODYPART_MANGLED_INTERIOR)))
return (exterior_ready_to_dismember && interior_ready_to_dismember)
/// Returns TRUE if our total percent damage is more or equal to our dismemberable percentage, but FALSE if a wound can cause us to be dismembered.
/obj/item/bodypart/proc/dismemberable_by_total_damage()
update_wound_theory()
var/bio_status = get_bio_state_status()
var/has_interior = ((bio_status & ANATOMY_INTERIOR))
var/can_theoretically_be_dismembered_by_wound = (any_existing_wound_can_mangle_our_interior || (any_existing_wound_can_mangle_our_exterior && has_interior))
var/wound_dismemberable = dismemberable_by_wound()
var/ready_to_use_alternate_formula = (use_alternate_dismemberment_calc_even_if_mangleable || (!wound_dismemberable && !can_theoretically_be_dismembered_by_wound))
if (ready_to_use_alternate_formula)
var/percent_to_total_max = (get_damage() / max_damage)
if (percent_to_total_max >= hp_percent_to_dismemberable)
return TRUE
return FALSE
/// Updates our "can be theoretically dismembered by wounds" variables by iterating through all wound static data.
/obj/item/bodypart/proc/update_wound_theory()
// We put this here so we dont increase init time by doing this all at once on initialization
// Effectively, we "lazy load"
if (isnull(any_existing_wound_can_mangle_our_interior) || isnull(any_existing_wound_can_mangle_our_exterior))
any_existing_wound_can_mangle_our_interior = FALSE
any_existing_wound_can_mangle_our_exterior = FALSE
for (var/datum/wound/wound_type as anything in GLOB.all_wound_pregen_data)
var/datum/wound_pregen_data/pregen_data = GLOB.all_wound_pregen_data[wound_type]
if (!pregen_data.can_be_applied_to(src, random_roll = TRUE)) // we only consider randoms because non-randoms are usually really specific
continue
if (initial(pregen_data.wound_path_to_generate.wound_flags) & MANGLES_EXTERIOR)
any_existing_wound_can_mangle_our_exterior = TRUE
if (initial(pregen_data.wound_path_to_generate.wound_flags) & MANGLES_INTERIOR)
any_existing_wound_can_mangle_our_interior = TRUE
if (any_existing_wound_can_mangle_our_interior && any_existing_wound_can_mangle_our_exterior)
break
//Heals brute and burn damage for the organ. Returns 1 if the damage-icon states changed at all.
//Damage cannot go below zero.
//Cannot remove negative damage (i.e. apply damage)
@@ -1105,9 +1141,6 @@
for(var/datum/wound/iter_wound as anything in wounds)
cached_bleed_rate += iter_wound.blood_flow
if(!cached_bleed_rate)
QDEL_NULL(grasped_by)
// Our bleed overlay is based directly off bleed_rate, so go aheead and update that would you?
if(cached_bleed_rate != old_bleed_rate)
update_part_wound_overlay()
@@ -1280,3 +1313,21 @@
/obj/item/bodypart/proc/un_paralyze()
REMOVE_TRAITS_IN(src, EMP_TRAIT)
/// Returns the generic description of our BIO_EXTERNAL feature(s), prioritizing certain ones over others. Returns error on failure.
/obj/item/bodypart/proc/get_external_description()
if (biological_state & BIO_FLESH)
return "flesh"
if (biological_state & BIO_WIRED)
return "wiring"
return "error"
/// Returns the generic description of our BIO_INTERNAL feature(s), prioritizing certain ones over others. Returns error on failure.
/obj/item/bodypart/proc/get_internal_description()
if (biological_state & BIO_BONE)
return "bone"
if (biological_state & BIO_METAL)
return "metal"
return "error"
+13 -13
View File
@@ -5,7 +5,7 @@
return TRUE
///Remove target limb from it's owner, with side effects.
/obj/item/bodypart/proc/dismember(dam_type = BRUTE, silent=TRUE, wound_type)
/obj/item/bodypart/proc/dismember(dam_type = BRUTE, silent=TRUE, wounding_type)
if(!owner || (bodypart_flags & BODYPART_UNREMOVABLE))
return FALSE
var/mob/living/carbon/limb_owner = owner
@@ -23,14 +23,14 @@
limb_owner.add_mood_event("dismembered_[body_zone]", /datum/mood_event/dismembered, src)
limb_owner.add_mob_memory(/datum/memory/was_dismembered, lost_limb = src)
if (wound_type)
LAZYSET(limb_owner.body_zone_dismembered_by, body_zone, wound_type)
if (wounding_type)
LAZYSET(limb_owner.body_zone_dismembered_by, body_zone, wounding_type)
drop_limb()
limb_owner.update_equipment_speed_mods() // Update in case speed affecting item unequipped by dismemberment
var/turf/owner_location = limb_owner.loc
if(wound_type != WOUND_BURN && istype(owner_location) && can_bleed())
if(wounding_type != WOUND_BURN && istype(owner_location) && can_bleed())
limb_owner.add_splatter_floor(owner_location)
if(QDELETED(src)) //Could have dropped into lava/explosion/chasm/whatever
@@ -55,7 +55,7 @@
return TRUE
/obj/item/bodypart/chest/dismember(dam_type = BRUTE, silent=TRUE, wound_type)
/obj/item/bodypart/chest/dismember(dam_type = BRUTE, silent=TRUE, wounding_type)
if(!owner)
return FALSE
var/mob/living/carbon/chest_owner = owner
@@ -64,7 +64,7 @@
if(HAS_TRAIT(chest_owner, TRAIT_NODISMEMBER))
return FALSE
. = list()
if(wound_type != WOUND_BURN && isturf(chest_owner.loc) && can_bleed())
if(wounding_type != WOUND_BURN && isturf(chest_owner.loc) && can_bleed())
chest_owner.add_splatter_floor(chest_owner.loc)
playsound(get_turf(chest_owner), 'sound/misc/splort.ogg', 80, TRUE)
for(var/obj/item/organ/organ as anything in chest_owner.organs)
@@ -158,16 +158,16 @@
* Dismemberment for flesh and bone requires the victim to have the skin on their bodypart destroyed (either a critical cut or piercing wound), and at least a hairline fracture
* (severe bone), at which point we can start rolling for dismembering. The attack must also deal at least 10 damage, and must be a brute attack of some kind (sorry for now, cakehat, maybe later)
*
* Returns: BODYPART_MANGLED_NONE if we're fine, BODYPART_MANGLED_FLESH if our skin is broken, BODYPART_MANGLED_BONE if our bone is broken, or BODYPART_MANGLED_BOTH if both are broken and we're up for dismembering
* Returns: BODYPART_MANGLED_NONE if we're fine, BODYPART_MANGLED_EXTERIOR if our skin is broken, BODYPART_MANGLED_INTERIOR if our bone is broken, or BODYPART_MANGLED_BOTH if both are broken and we're up for dismembering
*/
/obj/item/bodypart/proc/get_mangled_state()
. = BODYPART_MANGLED_NONE
for(var/datum/wound/iter_wound as anything in wounds)
if((iter_wound.wound_flags & MANGLES_BONE))
. |= BODYPART_MANGLED_BONE
if((iter_wound.wound_flags & MANGLES_FLESH))
. |= BODYPART_MANGLED_FLESH
if((iter_wound.wound_flags & MANGLES_INTERIOR))
. |= BODYPART_MANGLED_INTERIOR
if((iter_wound.wound_flags & MANGLES_EXTERIOR))
. |= BODYPART_MANGLED_EXTERIOR
/**
* try_dismember() is used, once we've confirmed that a flesh and bone bodypart has both the skin and bone mangled, to actually roll for it
@@ -445,8 +445,8 @@
if (LAZYLEN(dismembered_by_copy))
var/datum/scar/scaries = new
var/datum/wound/loss/phantom_loss = new // stolen valor, really
phantom_loss.loss_wound_type = dismembered_by_copy?[limb_zone]
if (phantom_loss.loss_wound_type)
phantom_loss.loss_wounding_type = dismembered_by_copy?[limb_zone]
if (phantom_loss.loss_wounding_type)
scaries.generate(limb, phantom_loss)
LAZYREMOVE(dismembered_by_copy, limb_zone) // in case we're using a passed list
else
+3 -3
View File
@@ -53,7 +53,7 @@
if(cavity_item)
cavity_item.forceMove(drop_location())
cavity_item = null
..()
return ..()
/obj/item/bodypart/chest/monkey
icon = 'icons/mob/human/species/monkey/bodyparts.dmi'
@@ -114,7 +114,7 @@
/// Datum describing how to offset things held in the hands of this arm, the x offset IS functional here
var/datum/worn_feature_offset/held_hand_offset
biological_state = (BIO_STANDARD|BIO_JOINTED)
biological_state = BIO_STANDARD_JOINTED
/obj/item/bodypart/arm/Destroy()
QDEL_NULL(worn_glove_offset)
@@ -346,7 +346,7 @@
/// Datum describing how to offset things worn on the foot of this leg, note that an x offset won't do anything here
var/datum/worn_feature_offset/worn_foot_offset
biological_state = (BIO_STANDARD|BIO_JOINTED)
biological_state = BIO_STANDARD_JOINTED
/obj/item/bodypart/leg/Destroy()
QDEL_NULL(worn_foot_offset)
+147 -75
View File
@@ -1,81 +1,40 @@
/// Allows us to roll for and apply a wound without actually dealing damage. Used for aggregate wounding power with pellet clouds
/obj/item/bodypart/proc/painless_wound_roll(wounding_type, phantom_wounding_dmg, wound_bonus, bare_wound_bonus, sharpness=NONE)
/obj/item/bodypart/proc/painless_wound_roll(wounding_type, wounding_dmg, wound_bonus, bare_wound_bonus, sharpness=NONE)
SHOULD_CALL_PARENT(TRUE)
if(!owner || phantom_wounding_dmg <= WOUND_MINIMUM_DAMAGE || wound_bonus == CANT_WOUND || (owner.status_flags & GODMODE))
if(!owner || wounding_dmg <= WOUND_MINIMUM_DAMAGE || wound_bonus == CANT_WOUND || (owner.status_flags & GODMODE))
return
var/mangled_state = get_mangled_state()
var/easy_dismember = HAS_TRAIT(owner, TRAIT_EASYDISMEMBER) // if we have easydismember, we don't reduce damage when redirecting damage to different types (slashing weapons on mangled/skinless limbs attack at 100% instead of 50%)
var/has_exterior = FALSE
var/has_interior = FALSE
var/bio_status = get_bio_state_status()
for (var/state as anything in GLOB.bio_state_states)
var/flag = text2num(state)
if (!(biological_state & flag))
continue
var/has_exterior = ((bio_status & ANATOMY_EXTERIOR))
var/has_interior = ((bio_status & ANATOMY_INTERIOR))
var/value = GLOB.bio_state_states[state]
if (value & BIO_EXTERIOR)
has_exterior = TRUE
if (value & BIO_INTERIOR)
has_interior = TRUE
if (has_exterior && has_interior)
break
// We put this here so we dont increase init time by doing this all at once on initialization
// Effectively, we "lazy load"
if (isnull(any_existing_wound_can_mangle_our_bone) || isnull(any_existing_wound_can_mangle_our_flesh))
any_existing_wound_can_mangle_our_bone = FALSE
any_existing_wound_can_mangle_our_flesh = FALSE
for (var/datum/wound/wound_type as anything in GLOB.all_wound_pregen_data)
var/datum/wound_pregen_data/pregen_data = GLOB.all_wound_pregen_data[wound_type]
if (!pregen_data.can_be_applied_to(src, random_roll = TRUE)) // we only consider randoms because non-randoms are usually really specific
continue
if (initial(pregen_data.wound_path_to_generate.wound_flags) & MANGLES_FLESH)
any_existing_wound_can_mangle_our_flesh = TRUE
if (initial(pregen_data.wound_path_to_generate.wound_flags) & MANGLES_BONE)
any_existing_wound_can_mangle_our_bone = TRUE
if (any_existing_wound_can_mangle_our_bone && any_existing_wound_can_mangle_our_flesh)
break
var/can_theoretically_be_dismembered = (any_existing_wound_can_mangle_our_bone || (any_existing_wound_can_mangle_our_flesh && !has_exterior))
var/exterior_ready_to_dismember = (!has_exterior || ((mangled_state & BODYPART_MANGLED_BONE) == BODYPART_MANGLED_BONE))
var/interior_ready_to_dismember = (!has_interior || ((mangled_state & BODYPART_MANGLED_FLESH) == BODYPART_MANGLED_FLESH))
var/exterior_ready_to_dismember = (!has_exterior || ((mangled_state & BODYPART_MANGLED_EXTERIOR)))
// if we're bone only, all cutting attacks go straight to the bone
if(has_exterior && interior_ready_to_dismember)
if(!has_exterior && has_interior)
if(wounding_type == WOUND_SLASH)
wounding_type = WOUND_BLUNT
phantom_wounding_dmg *= (easy_dismember ? 1 : 0.6)
wounding_dmg *= (easy_dismember ? 1 : 0.6)
else if(wounding_type == WOUND_PIERCE)
wounding_type = WOUND_BLUNT
phantom_wounding_dmg *= (easy_dismember ? 1 : 0.75)
if(exterior_ready_to_dismember && try_dismember(wounding_type, phantom_wounding_dmg, wound_bonus, bare_wound_bonus))
return
wounding_dmg *= (easy_dismember ? 1 : 0.75)
else
// if we've already mangled the skin (critical slash or piercing wound), then the bone is exposed, and we can damage it with sharp weapons at a reduced rate
// So a big sharp weapon is still all you need to destroy a limb
if(has_exterior && interior_ready_to_dismember && !(mangled_state & BODYPART_MANGLED_BONE) && sharpness)
playsound(src, "sound/effects/wounds/crackandbleed.ogg", 100)
if(has_interior && exterior_ready_to_dismember && !(mangled_state & BODYPART_MANGLED_INTERIOR) && sharpness)
if(wounding_type == WOUND_SLASH && !easy_dismember)
phantom_wounding_dmg *= 0.6 // edged weapons pass along 60% of their wounding damage to the bone since the power is spread out over a larger area
wounding_dmg *= 0.6 // edged weapons pass along 60% of their wounding damage to the bone since the power is spread out over a larger area
if(wounding_type == WOUND_PIERCE && !easy_dismember)
phantom_wounding_dmg *= 0.75 // piercing weapons pass along 75% of their wounding damage to the bone since it's more concentrated
wounding_dmg *= 0.75 // piercing weapons pass along 75% of their wounding damage to the bone since it's more concentrated
wounding_type = WOUND_BLUNT
else if(interior_ready_to_dismember && exterior_ready_to_dismember && try_dismember(wounding_type, phantom_wounding_dmg, wound_bonus, bare_wound_bonus))
if ((dismemberable_by_wound() || dismemberable_by_total_damage()) && try_dismember(wounding_type, wounding_dmg, wound_bonus, bare_wound_bonus))
return
if (use_alternate_dismemberment_calc_even_if_mangleable || !can_theoretically_be_dismembered)
var/percent_to_total_max = (get_damage() / max_damage)
if (percent_to_total_max >= hp_percent_to_dismemberable)
if (try_dismember(wounding_type, phantom_wounding_dmg, wound_bonus, bare_wound_bonus))
return
return check_wounding(wounding_type, phantom_wounding_dmg, wound_bonus, bare_wound_bonus)
return check_wounding(wounding_type, wounding_dmg, wound_bonus, bare_wound_bonus)
/**
* check_wounding() is where we handle rolling for, selecting, and applying a wound if we meet the criteria
@@ -111,6 +70,8 @@
var/base_roll = rand(1, round(damage ** WOUND_DAMAGE_EXPONENT))
var/injury_roll = base_roll
injury_roll += check_woundings_mods(woundtype, damage, wound_bonus, bare_wound_bonus)
var/list/series_wounding_mods = check_series_wounding_mods()
if(injury_roll > WOUND_DISMEMBER_OUTRIGHT_THRESH && prob(get_damage() / max_damage * 100))
var/datum/wound/loss/dismembering = new
dismembering.apply_dismember(src, woundtype, outright = TRUE, attack_direction = attack_direction)
@@ -119,8 +80,8 @@
var/list/datum/wound/possible_wounds = list()
for (var/datum/wound/type as anything in GLOB.all_wound_pregen_data)
var/datum/wound_pregen_data/pregen_data = GLOB.all_wound_pregen_data[type]
if (pregen_data.can_be_applied_to(src, woundtype, random_roll = TRUE))
possible_wounds += type
if (pregen_data.can_be_applied_to(src, list(woundtype), random_roll = TRUE))
possible_wounds[type] = pregen_data.get_weight(src, woundtype, damage, attack_direction, damage_source)
// quick re-check to see if bare_wound_bonus applies, for the benefit of log_wound(), see about getting the check from check_woundings_mods() somehow
if(ishuman(owner))
var/mob/living/carbon/human/human_wearer = owner
@@ -131,39 +92,137 @@
bare_wound_bonus = 0
break
//cycle through the wounds of the relevant category from the most severe down
for(var/datum/wound/possible_wound as anything in possible_wounds)
for (var/datum/wound/iterated_path as anything in possible_wounds)
for (var/datum/wound/existing_wound as anything in wounds)
if (iterated_path == existing_wound.type)
possible_wounds -= iterated_path
break // breaks out of the nested loop
var/datum/wound_pregen_data/pregen_data = GLOB.all_wound_pregen_data[iterated_path]
var/specific_injury_roll = (injury_roll + series_wounding_mods[pregen_data.wound_series])
if (pregen_data.get_threshold_for(src, attack_direction, damage_source) > specific_injury_roll)
possible_wounds -= iterated_path
continue
if (pregen_data.compete_for_wounding)
for (var/datum/wound/other_path as anything in possible_wounds)
if (other_path == iterated_path)
continue
if (initial(iterated_path.severity) == initial(other_path.severity) && pregen_data.overpower_wounds_of_even_severity)
possible_wounds -= other_path
continue
else if (pregen_data.competition_mode == WOUND_COMPETITION_OVERPOWER_LESSERS)
if (initial(iterated_path.severity) > initial(other_path.severity))
possible_wounds -= other_path
continue
else if (pregen_data.competition_mode == WOUND_COMPETITION_OVERPOWER_GREATERS)
if (initial(iterated_path.severity) < initial(other_path.severity))
possible_wounds -= other_path
continue
while (length(possible_wounds))
var/datum/wound/possible_wound = pick_weight(possible_wounds)
var/datum/wound_pregen_data/possible_pregen_data = GLOB.all_wound_pregen_data[possible_wound]
possible_wounds -= possible_wound
var/datum/wound/replaced_wound
for(var/datum/wound/existing_wound as anything in wounds)
if(existing_wound.wound_series == initial(possible_wound.wound_series))
var/datum/wound_pregen_data/existing_pregen_data = GLOB.all_wound_pregen_data[existing_wound.type]
if(existing_pregen_data.wound_series == possible_pregen_data.wound_series)
if(existing_wound.severity >= initial(possible_wound.severity))
return
continue
else
replaced_wound = existing_wound // if we find something we keep iterating untilw e're done or we find we're outclassed by something in our series
replaced_wound = existing_wound
// if we get through this whole loop without continuing, we found our winner
if(initial(possible_wound.threshold_minimum) < injury_roll)
var/datum/wound/new_wound
if(replaced_wound)
new_wound = replaced_wound.replace_wound(new possible_wound, attack_direction = attack_direction)
else
new_wound = new possible_wound
new_wound.apply_wound(src, attack_direction = attack_direction, wound_source = damage_source)
log_wound(owner, new_wound, damage, wound_bonus, bare_wound_bonus, base_roll) // dismembering wounds are logged in the apply_wound() for loss wounds since they delete themselves immediately, these will be immediately returned
return new_wound
var/datum/wound/new_wound = new possible_wound
if(replaced_wound)
new_wound = replaced_wound.replace_wound(new_wound, attack_direction = attack_direction)
else
new_wound.apply_wound(src, attack_direction = attack_direction, wound_source = damage_source)
log_wound(owner, new_wound, damage, wound_bonus, bare_wound_bonus, base_roll) // dismembering wounds are logged in the apply_wound() for loss wounds since they delete themselves immediately, these will be immediately returned
return new_wound
// try forcing a specific wound, but only if there isn't already a wound of that severity or greater for that type on this bodypart
/obj/item/bodypart/proc/force_wound_upwards(specific_woundtype, smited = FALSE, wound_source)
/obj/item/bodypart/proc/force_wound_upwards(datum/wound/potential_wound, smited = FALSE, wound_source)
SHOULD_NOT_OVERRIDE(TRUE)
var/datum/wound/potential_wound = specific_woundtype
if (isnull(potential_wound))
return
var/datum/wound_pregen_data/pregen_data = GLOB.all_wound_pregen_data[potential_wound]
for(var/datum/wound/existing_wound as anything in wounds)
if (existing_wound.wound_series == initial(potential_wound.wound_series))
var/datum/wound_pregen_data/existing_pregen_data = existing_wound.get_pregen_data()
if (existing_pregen_data.wound_series == pregen_data.wound_series)
if(existing_wound.severity < initial(potential_wound.severity)) // we only try if the existing one is inferior to the one we're trying to force
existing_wound.replace_wound(new potential_wound, smited)
return
var/datum/wound/new_wound = new potential_wound
new_wound.apply_wound(src, smited = smited, wound_source = wound_source)
return new_wound
/**
* A simple proc to force a type of wound onto this mob. If you just want to force a specific mainline (fractures, bleeding, etc.) wound, you only need to care about the first 3 args.
*
* Args:
* * wounding_type: The wounding_type, e.g. WOUND_BLUNT, WOUND_SLASH to force onto the mob. Can be a list.
* * obj/item/bodypart/limb: The limb we wil be applying the wound to. If null, a random bodypart will be picked.
* * min_severity: The minimum severity that will be considered.
* * max_severity: The maximum severity that will be considered.
* * severity_pick_mode: The "pick mode" to be used. See get_corresponding_wound_type's documentation
* * wound_source: The source of the wound to be applied. Nullable.
*
* For the rest of the args, refer to get_corresponding_wound_type().
*
* Returns:
* A new wound instance if the application was successful, null otherwise.
*/
/mob/living/carbon/proc/cause_wound_of_type_and_severity(wounding_type, obj/item/bodypart/limb, min_severity, max_severity = min_severity, severity_pick_mode = WOUND_PICK_HIGHEST_SEVERITY, wound_source)
if (isnull(limb))
limb = pick(bodyparts)
var/list/type_list = wounding_type
if (!islist(type_list))
type_list = list(type_list)
var/datum/wound/corresponding_typepath = get_corresponding_wound_type(type_list, limb, min_severity, max_severity, severity_pick_mode)
if (corresponding_typepath)
return limb.force_wound_upwards(corresponding_typepath, wound_source = wound_source)
/// Limb is nullable, but picks a random one. Defers to limb.get_wound_threshold_of_wound_type, see it for documentation.
/mob/living/carbon/proc/get_wound_threshold_of_wound_type(wounding_type, severity, default, obj/item/bodypart/limb, wound_source)
if (isnull(limb))
limb = pick(bodyparts)
if (!limb)
return default
return limb.get_wound_threshold_of_wound_type(wounding_type, severity, default, wound_source)
/**
* A simple proc that gets the best wound to fit the criteria laid out, then returns its wound threshold.
*
* Args:
* * wounding_type: The wounding_type, e.g. WOUND_BLUNT, WOUND_SLASH to force onto the mob. Can be a list of wounding_types.
* * severity: The severity that will be considered.
* * return_value_if_no_wound: If no wound is found, we will return this instead. (It is reccomended to use named args for this one, as its unclear what it is without)
* * wound_source: The theoretical source of the wound. Nullable.
*
* Returns:
* return_value_if_no_wound if no wound is found - if one IS found, the wound threshold for that wound.
*/
/obj/item/bodypart/proc/get_wound_threshold_of_wound_type(wounding_type, severity, return_value_if_no_wound, wound_source)
var/list/type_list = wounding_type
if (!islist(type_list))
type_list = list(type_list)
var/datum/wound/wound_path = get_corresponding_wound_type(type_list, src, severity, duplicates_allowed = TRUE, care_about_existing_wounds = FALSE)
if (wound_path)
var/datum/wound_pregen_data/pregen_data = GLOB.all_wound_pregen_data[wound_path]
return pregen_data.get_threshold_for(src, damage_source = wound_source)
return return_value_if_no_wound
/**
* check_wounding_mods() is where we handle the various modifiers of a wound roll
@@ -209,7 +268,20 @@
return injury_mod
/// Get whatever wound of the given type is currently attached to this limb, if any
/// Should return an assoc list of (wound_series -> penalty). Will be used in determining series-specific penalties for wounding.
/obj/item/bodypart/proc/check_series_wounding_mods()
RETURN_TYPE(/list)
var/list/series_mods = list()
for (var/datum/wound/iterated_wound as anything in wounds)
var/datum/wound_pregen_data/pregen_data = GLOB.all_wound_pregen_data[iterated_wound.type]
series_mods[pregen_data.wound_series] += iterated_wound.series_threshold_penalty
return series_mods
/// Get whatever wound of the given type is currently attached to this limb, if any
/obj/item/bodypart/proc/get_wound_type(checking_type)
RETURN_TYPE(checking_type)
SHOULD_NOT_OVERRIDE(TRUE)
+4 -2
View File
@@ -19,10 +19,11 @@
TEST_ASSERT_EQUAL(length(victim.all_wounds), 0, "Patient is somehow wounded before test")
var/datum/wound/iter_test_wound
var/datum/wound_pregen_data/iter_pregen_data = GLOB.all_wound_pregen_data[iter_test_wound]
var/threshold_penalty = 0
for(iter_test_wound in iter_test_wound_list)
var/threshold = initial(iter_test_wound.threshold_minimum) - threshold_penalty // just enough to guarantee the next tier of wound, given the existing wound threshold penalty
var/threshold = iter_pregen_data.threshold_minimum - threshold_penalty // just enough to guarantee the next tier of wound, given the existing wound threshold penalty
if(dam_types[i] == BRUTE)
tested_part.receive_damage(WOUND_MINIMUM_DAMAGE, 0, wound_bonus = threshold, sharpness=sharps[i])
else if(dam_types[i] == BURN)
@@ -59,10 +60,11 @@
TEST_ASSERT_EQUAL(length(victim.all_wounds), 0, "Patient is somehow wounded before test")
var/datum/wound/iter_test_wound
var/datum/wound_pregen_data/iter_pregen_data = GLOB.all_wound_pregen_data[iter_test_wound]
var/threshold_penalty = 0
for(iter_test_wound in iter_test_wound_list)
var/threshold = initial(iter_test_wound.threshold_minimum) - threshold_penalty // just enough to guarantee the next tier of wound, given the existing wound threshold penalty
var/threshold = iter_pregen_data.threshold_minimum - threshold_penalty // just enough to guarantee the next tier of wound, given the existing wound threshold penalty
if(dam_types[i] == BRUTE)
tested_part.receive_damage(WOUND_MINIMUM_DAMAGE, 0, wound_bonus = threshold, sharpness=sharps[i])
else if(dam_types[i] == BURN)
+5 -5
View File
@@ -121,11 +121,11 @@
if(prob(35)) //Note: The randomstep on dump_mobs throws occupants into each other and often causes wounds regardless.
continue
for(var/obj/item/bodypart/head/head_to_wound as anything in carbon_occupant.bodyparts)
var/type_wound = pick(list(
/datum/wound/blunt/bone/moderate,
/datum/wound/blunt/bone/severe,
))
head_to_wound.force_wound_upwards(type_wound, wound_source = src)
var/pick_mode = text2num(pick(list(
"[WOUND_PICK_LOWEST_SEVERITY]",
"[WOUND_PICK_HIGHEST_SEVERITY]"
)))
carbon_occupant.cause_wound_of_type_and_severity(WOUND_BLUNT, head_to_wound, WOUND_SEVERITY_MODERATE, WOUND_SEVERITY_SEVERE, pick_mode)
carbon_occupant.playsound_local(src, 'sound/weapons/flash_ring.ogg', 50)
carbon_occupant.set_eye_blur_if_lower(rand(10 SECONDS, 20 SECONDS))
+3 -7
View File
@@ -954,13 +954,9 @@
return FALSE
var/mob/living/carbon/carbon_target = atom_target
for(var/obj/item/bodypart/squish_part in carbon_target.bodyparts)
var/type_wound
if (squish_part.biological_state & BIO_BONE)
type_wound = pick(list(/datum/wound/blunt/bone/critical, /datum/wound/blunt/bone/severe, /datum/wound/blunt/bone/moderate))
else
squish_part.receive_damage(brute=30)
if (type_wound)
squish_part.force_wound_upwards(type_wound, wound_source = "crushed by [src]")
var/severity = pick(WOUND_SEVERITY_MODERATE, WOUND_SEVERITY_SEVERE, WOUND_SEVERITY_CRITICAL)
if (!carbon_target.cause_wound_of_type_and_severity(WOUND_BLUNT, squish_part, severity, wound_source = "crushed by [src]"))
squish_part.receive_damage(brute = 30)
carbon_target.visible_message(span_danger("[carbon_target]'s body is maimed underneath the mass of [src]!"), span_userdanger("Your body is maimed underneath the mass of [src]!"))
return TRUE
if(CRUSH_CRIT_HEADGIB) // skull squish!
+5
View File
@@ -1,6 +1,11 @@
{
"generic": ["general disfigurement"],
"dislocate": [
"the bone equivalent of a faded bruise",
"a series of tiny chip marks"
],
"bluntmoderate": [
"the bone equivalent of a faded bruise",
"a series of tiny chip marks"
+6
View File
@@ -1,6 +1,12 @@
{
"generic": ["general disfigurement"],
"dislocate": [
"light discoloring",
"a slight blue tint",
"a slightly deadened tint"
],
"bluntmoderate": [
"light discoloring",
"a slight blue tint",
+1
View File
@@ -2480,6 +2480,7 @@
#include "code\modules\actionspeed\modifiers\drugs.dm"
#include "code\modules\actionspeed\modifiers\mood.dm"
#include "code\modules\actionspeed\modifiers\status_effects.dm"
#include "code\modules\actionspeed\modifiers\wound.dm"
#include "code\modules\admin\admin.dm"
#include "code\modules\admin\admin_fax_panel.dm"
#include "code\modules\admin\admin_investigate.dm"