From a97c51b30bb5afda501b5b2c80e00d335c341089 Mon Sep 17 00:00:00 2001 From: Ryll Ryll <3589655+Ryll-Ryll@users.noreply.github.com> Date: Thu, 23 Jul 2020 22:19:30 -0400 Subject: [PATCH 1/2] [READY] Wounds part 2: Piercing wounds, dismemberment changes, housekeeping (#51786) * pierce the heavens * starts doing projs * continue pierce * before armor * before sharpness redefine * rename sharp defines, before further implementation * finishing undoing atk_type back to sharpness * neatens up sharpness defines, FALSE -> SHARP_NONE * more piercing, removes brute damage bleed, bubblegum no longer wound * starts letting embeds get in on the fun * half with embed * work on dismembering * continued embed work * more moving bandaging to limbs * more dismemberment work * removing embed pierce stuff * tweaking bullets * more docs and work on dismemberment * spans, piercing, guns * dismemberment and scar fixes * bee changes * bullets embedding * more bullet and dismember work * dismemberment, surgery, piercing, formaldehyde, * pleases travis * pierce smite * nicer on blood * Auto stash before rebase of "tgstation/master" * more neatening * wounds only consider up to 35 damage, wounds on l6 and 762 * updates hulk * balance * defines * lower slug to 50 brute to accommodate wounds * adds differentiation for having flesh/bones/both in mobs * moves scar descs to json, renames organic_state * excises removed healing skill * fixes logs, inconsistencies, some balance changes * untab * slight compress * a * kills pointed global list * dmdoc * halfway through roh * finishes roh review * okay NOW i finished roh's reviews * roh roh roh your boat * gently down the stream * global lists * list ops, fix scanner for bone gel improvised fix * travis moment * sounds added and moved * pellet clouds can join the fun fully, slight gun balancing for wounds * doc moment * unconflicts myself * update hulk * Update code/_onclick/item_attack.dm Co-authored-by: Rohesie * crying ascii face * final rohview * oops * final final Co-authored-by: Rohesie --- code/__DEFINES/DNA.dm | 10 +- code/__DEFINES/admin.dm | 1 + code/__DEFINES/combat.dm | 6 +- code/__DEFINES/dcs/signals.dm | 2 +- code/__DEFINES/is_helpers.dm | 8 - code/__DEFINES/misc.dm | 3 + code/__DEFINES/wounds.dm | 113 ++++++-- code/_onclick/item_attack.dm | 4 +- code/controllers/subsystem/persistence.dm | 4 +- code/datums/components/butchering.dm | 2 +- code/datums/components/embedded.dm | 33 ++- code/datums/components/pellet_cloud.dm | 58 +++- code/datums/components/tackle.dm | 2 +- code/datums/elements/embed.dm | 20 +- code/datums/mutations/actions.dm | 2 +- code/datums/mutations/hulk.dm | 6 +- code/datums/status_effects/debuffs.dm | 4 +- code/datums/status_effects/wound_effects.dm | 38 +-- code/datums/wounds/_wounds.dm | 53 ++-- code/datums/wounds/bones.dm | 182 +++++------- code/datums/wounds/burns.dm | 92 +++---- code/datums/wounds/loss.dm | 42 +++ code/datums/wounds/pierce.dm | 171 ++++++++++++ code/datums/wounds/scars/_scars.dm | 38 ++- code/datums/wounds/{cuts.dm => slash.dm} | 190 ++++++------- .../game/gamemodes/clown_ops/clown_weapons.dm | 2 +- code/game/machinery/doors/door.dm | 3 +- code/game/objects/items.dm | 15 +- code/game/objects/items/chainsaw.dm | 6 +- code/game/objects/items/devices/scanners.dm | 14 +- code/game/objects/items/dualsaber.dm | 2 +- code/game/objects/items/fireaxe.dm | 5 +- code/game/objects/items/holy_weapons.dm | 20 +- code/game/objects/items/kitchen.dm | 7 +- code/game/objects/items/latexballoon.dm | 2 +- code/game/objects/items/melee/energy.dm | 6 +- code/game/objects/items/melee/misc.dm | 8 +- code/game/objects/items/melee/transforming.dm | 2 +- code/game/objects/items/pitchfork.dm | 2 +- code/game/objects/items/sharpener.dm | 2 +- code/game/objects/items/shrapnel.dm | 36 +-- code/game/objects/items/spear.dm | 4 +- code/game/objects/items/stacks/medical.dm | 37 ++- .../game/objects/items/stacks/sheets/glass.dm | 2 +- code/game/objects/items/storage/belt.dm | 2 +- code/game/objects/items/tools/screwdriver.dm | 1 + code/game/objects/items/tools/weldingtool.dm | 2 + code/game/objects/items/weaponry.dm | 18 +- code/modules/admin/verbs/randomverbs.dm | 96 ++++++- .../changeling/powers/mutations.dm | 2 +- .../changeling/powers/regenerate.dm | 4 +- code/modules/antagonists/cult/cult_items.dm | 6 +- .../devil/true_devil/_true_devil.dm | 2 +- .../eldritch_cult/eldritch_items.dm | 2 +- .../eldritch_cult/eldritch_magic.dm | 9 +- .../eldritch_cult/knowledge/flesh_lore.dm | 4 +- .../antagonists/slaughter/slaughter.dm | 2 +- .../antagonists/wizard/equipment/artefact.dm | 2 +- code/modules/cargo/goodies.dm | 6 + code/modules/cargo/packs.dm | 15 + code/modules/client/preferences.dm | 2 +- code/modules/clothing/clothing.dm | 4 +- code/modules/clothing/glasses/_glasses.dm | 8 +- code/modules/clothing/glasses/hud.dm | 2 +- code/modules/clothing/suits/armor.dm | 6 +- .../food_and_drinks/drinks/drinks/bottle.dm | 2 +- code/modules/food_and_drinks/food/snacks.dm | 15 +- .../browserassets/css/browserOutput.css | 8 +- .../browserassets/css/browserOutput_white.css | 6 +- code/modules/hydroponics/grown/banana.dm | 6 + code/modules/hydroponics/hydroitemdefines.dm | 2 +- .../mining/equipment/kinetic_crusher.dm | 2 +- code/modules/mining/equipment/mining_tools.dm | 4 +- .../mining/lavaland/necropolis_chests.dm | 4 +- code/modules/mob/living/bloodcrawl.dm | 4 +- code/modules/mob/living/carbon/carbon.dm | 39 ++- .../mob/living/carbon/carbon_defense.dm | 54 +++- .../modules/mob/living/carbon/damage_procs.dm | 4 +- code/modules/mob/living/carbon/examine.dm | 5 +- .../mob/living/carbon/human/damage_procs.dm | 2 +- .../mob/living/carbon/human/examine.dm | 27 +- .../mob/living/carbon/human/human_helpers.dm | 24 +- code/modules/mob/living/carbon/human/life.dm | 4 + .../mob/living/carbon/human/species.dm | 21 +- .../carbon/human/species_types/dullahan.dm | 2 +- .../carbon/human/species_types/ethereal.dm | 2 +- .../carbon/human/species_types/golems.dm | 6 +- .../carbon/human/species_types/humans.dm | 2 +- .../human/species_types/lizardpeople.dm | 4 +- .../carbon/human/species_types/mothmen.dm | 2 +- .../carbon/human/species_types/mushpeople.dm | 2 +- .../carbon/human/species_types/plasmamen.dm | 15 +- .../carbon/human/species_types/podpeople.dm | 2 +- .../human/species_types/shadowpeople.dm | 2 +- .../carbon/human/species_types/skeletons.dm | 5 +- .../carbon/human/species_types/snail.dm | 2 +- .../carbon/human/species_types/vampire.dm | 2 +- .../carbon/human/species_types/zombies.dm | 10 +- code/modules/mob/living/damage_procs.dm | 4 +- code/modules/mob/living/living.dm | 67 +++-- code/modules/mob/living/living_defense.dm | 16 +- .../mob/living/silicon/damage_procs.dm | 2 +- .../mob/living/simple_animal/hostile/bear.dm | 2 +- .../living/simple_animal/hostile/cockroach.dm | 2 +- .../living/simple_animal/hostile/syndicate.dm | 2 +- .../mob/living/simple_animal/simple_animal.dm | 2 +- code/modules/ninja/energy_katana.dm | 2 +- .../ninja/suit/n_suit_verbs/ninja_stars.dm | 2 +- code/modules/paperwork/pen.dm | 5 +- .../projectiles/boxes_magazines/ammo_boxes.dm | 2 +- code/modules/projectiles/gun.dm | 3 +- .../projectiles/guns/energy/special.dm | 2 +- code/modules/projectiles/guns/magic/staff.dm | 2 +- code/modules/projectiles/projectile.dm | 42 ++- .../modules/projectiles/projectile/bullets.dm | 10 +- .../projectiles/projectile/bullets/lmg.dm | 12 +- .../projectiles/projectile/bullets/pistol.dm | 2 + .../projectile/bullets/revolver.dm | 17 +- .../projectiles/projectile/bullets/rifle.dm | 3 + .../projectiles/projectile/bullets/shotgun.dm | 29 +- .../projectiles/projectile/bullets/smg.dm | 8 +- .../reagents/cat2_medicine_reagents.dm | 5 +- .../chemistry/reagents/drink_reagents.dm | 19 ++ .../chemistry/reagents/medicine_reagents.dm | 64 ++++- .../chemistry/reagents/other_reagents.dm | 8 + .../reagents/chemistry/recipes/medicine.dm | 12 + .../reagents/reagent_containers/hypospray.dm | 9 +- .../reagents/reagent_containers/syringes.dm | 1 + code/modules/surgery/bodyparts/_bodyparts.dm | 260 ++++++++++++++---- .../surgery/bodyparts/dismemberment.dm | 54 +++- code/modules/surgery/bodyparts/head.dm | 3 +- code/modules/surgery/bodyparts/parts.dm | 7 +- code/modules/surgery/bone_mending.dm | 4 +- code/modules/surgery/burn_dressing.dm | 3 +- code/modules/surgery/repair_puncture.dm | 108 ++++++++ code/modules/surgery/tools.dm | 8 +- code/modules/vending/_vending.dm | 3 +- code/modules/zombie/items.dm | 2 +- interface/stylesheet.dm | 6 + sound/effects/{ => wounds}/blood1.ogg | Bin sound/effects/{ => wounds}/blood2.ogg | Bin sound/effects/{ => wounds}/blood3.ogg | Bin sound/effects/{ => wounds}/crack1.ogg | Bin sound/effects/{ => wounds}/crack2.ogg | Bin sound/effects/wounds/crackandbleed.ogg | Bin 0 -> 15662 bytes sound/effects/wounds/pierce1.ogg | Bin 0 -> 8726 bytes sound/effects/wounds/pierce2.ogg | Bin 0 -> 17779 bytes sound/effects/wounds/pierce3.ogg | Bin 0 -> 19758 bytes sound/effects/{ => wounds}/sizzle1.ogg | Bin sound/effects/{ => wounds}/sizzle2.ogg | Bin strings/wounds/bone_scar_desc.json | 26 ++ strings/wounds/flesh_scar_desc.json | 86 ++++++ strings/wounds/scar_loc.json | 52 ++++ tgstation.dme | 5 +- 154 files changed, 1945 insertions(+), 783 deletions(-) create mode 100644 code/datums/wounds/loss.dm create mode 100644 code/datums/wounds/pierce.dm rename code/datums/wounds/{cuts.dm => slash.dm} (60%) create mode 100644 code/modules/surgery/repair_puncture.dm rename sound/effects/{ => wounds}/blood1.ogg (100%) rename sound/effects/{ => wounds}/blood2.ogg (100%) rename sound/effects/{ => wounds}/blood3.ogg (100%) rename sound/effects/{ => wounds}/crack1.ogg (100%) rename sound/effects/{ => wounds}/crack2.ogg (100%) create mode 100644 sound/effects/wounds/crackandbleed.ogg create mode 100644 sound/effects/wounds/pierce1.ogg create mode 100644 sound/effects/wounds/pierce2.ogg create mode 100644 sound/effects/wounds/pierce3.ogg rename sound/effects/{ => wounds}/sizzle1.ogg (100%) rename sound/effects/{ => wounds}/sizzle2.ogg (100%) create mode 100644 strings/wounds/bone_scar_desc.json create mode 100644 strings/wounds/flesh_scar_desc.json create mode 100644 strings/wounds/scar_loc.json diff --git a/code/__DEFINES/DNA.dm b/code/__DEFINES/DNA.dm index 6610e463163..85ede44936d 100644 --- a/code/__DEFINES/DNA.dm +++ b/code/__DEFINES/DNA.dm @@ -126,8 +126,13 @@ #define AGENDER 16 /// Do not draw eyes or eyeless overlay #define NOEYESPRITES 17 -/// If this species can be scarred (fleshy) -#define CAN_SCAR 18 + +/// Used for determining which wounds are applicable to this species. +/// if we have flesh (can suffer slash/piercing/burn wounds, requires they don't have NOBLOOD) +#define HAS_FLESH 18 +/// if we have bones (can suffer bone wounds) +#define HAS_BONE 19 + //organ slots #define ORGAN_SLOT_BRAIN "brain" @@ -166,6 +171,7 @@ #define CHROMOSOME_NONE 1 #define CHROMOSOME_USED 2 +//used for mob's genetic gender (mainly just for pronouns, members of sexed species with plural gender refer to their body_type for the actual sprites, which is not genetic) #define G_MALE 1 #define G_FEMALE 2 #define G_PLURAL 3 diff --git a/code/__DEFINES/admin.dm b/code/__DEFINES/admin.dm index a0e8a1f9420..cd8b8bff570 100644 --- a/code/__DEFINES/admin.dm +++ b/code/__DEFINES/admin.dm @@ -100,6 +100,7 @@ #define ADMIN_PUNISHMENT_NUGGET "Nugget" #define ADMIN_PUNISHMENT_CRACK ":B:oneless" #define ADMIN_PUNISHMENT_BLEED ":B:loodless" +#define ADMIN_PUNISHMENT_PERFORATE ":B:erforate" #define ADMIN_PUNISHMENT_SCARIFY "Scarify" #define ADMIN_PUNISHMENT_SHOES "Knot Shoes" diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index 8965715cbb7..db9007686da 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -189,9 +189,9 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list( #define REFLECT_FAKEPROJECTILE (1<<1) //Object/Item sharpness -#define IS_BLUNT 0 -#define IS_SHARP 1 -#define IS_SHARP_ACCURATE 2 +#define SHARP_NONE 0 +#define SHARP_EDGED 1 +#define SHARP_POINTY 2 //His Grace. #define HIS_GRACE_SATIATED 0 //He hungers not. If bloodthirst is set to this, His Grace is asleep. diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 14f13439d00..11b2655f2df 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -556,7 +556,7 @@ // /obj/projectile signals (sent to the firer) -///from base of /obj/projectile/proc/on_hit(): (atom/movable/firer, atom/target, Angle) +///from base of /obj/projectile/proc/on_hit(): (atom/movable/firer, atom/target, Angle, hit_limb) #define COMSIG_PROJECTILE_SELF_ON_HIT "projectile_self_on_hit" ///from base of /obj/projectile/proc/on_hit(): (atom/movable/firer, atom/target, Angle) #define COMSIG_PROJECTILE_ON_HIT "projectile_on_hit" diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 7deeda4c140..76c120a3373 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -180,14 +180,6 @@ GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list( #define iscash(A) (istype(A, /obj/item/coin) || istype(A, /obj/item/stack/spacecash) || istype(A, /obj/item/holochip)) -GLOBAL_LIST_INIT(pointed_types, typecacheof(list( - /obj/item/pen, - /obj/item/screwdriver, - /obj/item/reagent_containers/syringe, - /obj/item/kitchen/fork))) - -#define is_pointed(W) (is_type_in_typecache(W, GLOB.pointed_types)) - #define isbodypart(A) (istype(A, /obj/item/bodypart)) #define isprojectile(A) (istype(A, /obj/projectile)) diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 3ac969e4cd4..c0049150a49 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -411,6 +411,9 @@ GLOBAL_LIST_INIT(pda_styles, sortList(list(MONO, VT, ORBITRON, SHARE))) #define LOCATIONS_FILE "locations.json" #define WANTED_FILE "wanted_message.json" #define VISTA_FILE "steve.json" +#define FLESH_SCAR_FILE "wounds/flesh_scar_desc.json" +#define BONE_SCAR_FILE "wounds/bone_scar_desc.json" +#define SCAR_LOC_FILE "wounds/scar_loc.json" //Fullscreen overlay resolution in tiles. #define FULLSCREEN_OVERLAY_RESOLUTION_X 15 diff --git a/code/__DEFINES/wounds.dm b/code/__DEFINES/wounds.dm index b993429961d..5618439ae8b 100644 --- a/code/__DEFINES/wounds.dm +++ b/code/__DEFINES/wounds.dm @@ -1,43 +1,120 @@ + #define WOUND_DAMAGE_EXPONENT 1.4 +/// an attack must do this much damage after armor in order to roll for being a wound (incremental pressure damage need not apply) +#define WOUND_MINIMUM_DAMAGE 5 +/// an attack must do this much damage after armor in order to be eliigible to dismember a suitably mushed bodypart +#define DISMEMBER_MINIMUM_DAMAGE 10 +/// any damage dealt over this is ignored for damage rolls unless the target has the frail quirk (35^1.4=145) +#define WOUND_MAX_CONSIDERED_DAMAGE 35 + + #define WOUND_SEVERITY_TRIVIAL 0 // for jokey/meme wounds like stubbed toe, no standard messages/sounds or second winds #define WOUND_SEVERITY_MODERATE 1 #define WOUND_SEVERITY_SEVERE 2 #define WOUND_SEVERITY_CRITICAL 3 #define WOUND_SEVERITY_LOSS 4 // theoretical total limb loss, like dismemberment for cuts -#define WOUND_BRUTE 0 -#define WOUND_SHARP 1 -#define WOUND_BURN 2 +/// any brute weapon/attack that doesn't have sharpness. rolls for blunt bone wounds +#define WOUND_BLUNT 1 +/// any brute weapon/attack with sharpness = SHARP_EDGED. rolls for slash wounds +#define WOUND_SLASH 2 +/// any brute weapon/attack with sharpness = SHARP_POINTY. rolls for piercing wounds +#define WOUND_PIERCE 3 +/// any concentrated burn attack (lasers really). rolls for burning wounds +#define WOUND_BURN 4 // How much determination reagent to add each time someone gains a new wound in [/datum/wound/proc/second_wind()] #define WOUND_DETERMINATION_MODERATE 1 #define WOUND_DETERMINATION_SEVERE 2.5 #define WOUND_DETERMINATION_CRITICAL 5 +#define WOUND_DETERMINATION_LOSS 7.5 +/// the max amount of determination you can have #define WOUND_DETERMINATION_MAX 10 -// set wound_bonus on an item or attack to this to disable checking wounding for the attack +/// set wound_bonus on an item or attack to this to disable checking wounding for the attack #define CANT_WOUND -100 // list in order of highest severity to lowest -#define WOUND_LIST_BONE list(/datum/wound/brute/bone/critical, /datum/wound/brute/bone/severe, /datum/wound/brute/bone/moderate) -#define WOUND_LIST_CUT list(/datum/wound/brute/cut/loss, /datum/wound/brute/cut/critical, /datum/wound/brute/cut/severe, /datum/wound/brute/cut/moderate) -#define WOUND_LIST_BURN list(/datum/wound/burn/critical, /datum/wound/burn/severe, /datum/wound/burn/moderate) +GLOBAL_LIST_INIT(global_wound_types, list(WOUND_BLUNT = list(/datum/wound/blunt/critical, /datum/wound/blunt/severe, /datum/wound/blunt/moderate), + WOUND_SLASH = list(/datum/wound/slash/critical, /datum/wound/slash/severe, /datum/wound/slash/moderate), + WOUND_PIERCE = list(/datum/wound/pierce/critical, /datum/wound/pierce/severe, /datum/wound/pierce/moderate), + WOUND_BURN = list(/datum/wound/burn/critical, /datum/wound/burn/severe, /datum/wound/burn/moderate) + )) + +GLOBAL_LIST_INIT(global_all_wound_types, list(/datum/wound/blunt/critical, /datum/wound/blunt/severe, /datum/wound/blunt/moderate, + /datum/wound/slash/critical, /datum/wound/slash/severe, /datum/wound/slash/moderate, + /datum/wound/pierce/critical, /datum/wound/pierce/severe, /datum/wound/pierce/moderate, + /datum/wound/burn/critical, /datum/wound/burn/severe, /datum/wound/burn/moderate)) // Thresholds for infection for burn wounds, once infestation hits each threshold, things get steadily worse -#define WOUND_INFECTION_MODERATE 4 // below this has no ill effects from infection -#define WOUND_INFECTION_SEVERE 8 // then below here, you ooze some pus and suffer minor tox damage, but nothing serious -#define WOUND_INFECTION_CRITICAL 12 // then below here, your limb occasionally locks up from damage and infection and briefly becomes disabled. Things are getting really bad -#define WOUND_INFECTION_SEPTIC 20 // below here, your skin is almost entirely falling off and your limb locks up more frequently. You are within a stone's throw of septic paralysis and losing the limb +/// below this has no ill effects from infection +#define WOUND_INFECTION_MODERATE 4 +/// then below here, you ooze some pus and suffer minor tox damage, but nothing serious +#define WOUND_INFECTION_SEVERE 8 +/// then below here, your limb occasionally locks up from damage and infection and briefly becomes disabled. Things are getting really bad +#define WOUND_INFECTION_CRITICAL 12 +/// below here, your skin is almost entirely falling off and your limb locks up more frequently. You are within a stone's throw of septic paralysis and losing the limb +#define WOUND_INFECTION_SEPTIC 20 // above WOUND_INFECTION_SEPTIC, your limb is completely putrid and you start rolling to lose the entire limb by way of paralyzation. After 3 failed rolls (~4-5% each probably), the limb is paralyzed -#define WOUND_BURN_SANITIZATION_RATE 0.15 // how quickly sanitization removes infestation and decays per tick -#define WOUND_CUT_MAX_BLOODFLOW 8 // how much blood you can lose per tick per cut max. 8 is a LOT of blood for one cut so don't worry about hitting it easily -#define WOUND_BONE_HEAD_TIME_VARIANCE 20 // if we suffer a bone wound to the head that creates brain traumas, the timer for the trauma cycle is +/- by this percent (0-100) + +/// how quickly sanitization removes infestation and decays per tick +#define WOUND_BURN_SANITIZATION_RATE 0.15 +/// how much blood you can lose per tick per slash max. 8 is a LOT of blood for one cut so don't worry about hitting it easily +#define WOUND_SLASH_MAX_BLOODFLOW 8 +/// dead people don't bleed, but they can clot! this is the minimum amount of clotting per tick on dead people, so even critical cuts will slowly clot in dead people +#define WOUND_SLASH_DEAD_CLOT_MIN 0.05 +/// if we suffer a bone wound to the head that creates brain traumas, the timer for the trauma cycle is +/- by this percent (0-100) +#define WOUND_BONE_HEAD_TIME_VARIANCE 20 // The following are for persistent scar save formats -#define SCAR_SAVE_ZONE 1 // The body_zone we're applying to on granting -#define SCAR_SAVE_DESC 2 // The description we're loading -#define SCAR_SAVE_PRECISE_LOCATION 3 // The precise location we're loading -#define SCAR_SAVE_SEVERITY 4 // The severity the scar had +/// The version number of the scar we're saving +#define SCAR_SAVE_VERS 1 +/// The body_zone we're applying to on granting +#define SCAR_SAVE_ZONE 2 +/// The description we're loading +#define SCAR_SAVE_DESC 3 +/// The precise location we're loading +#define SCAR_SAVE_PRECISE_LOCATION 4 +/// The severity the scar had +#define SCAR_SAVE_SEVERITY 5 +///how many fields there are above (NOT INCLUDING THIS OBVIOUSLY) +#define SCAR_SAVE_LENGTH 5 + +// increment this number when you update the persistent scarring format in a way that invalidates previous saved scars (new fields, reordering, etc) +/// saved scars with a version lower than this will be discarded +#define SCAR_CURRENT_VERSION 1 + + +// 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. Skin 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 +#define BODYPART_MANGLED_NONE 0 +#define BODYPART_MANGLED_BONE 1 +#define BODYPART_MANGLED_FLESH 2 +#define BODYPART_MANGLED_BOTH 3 + +// What kind of biology we have, and what wounds we can suffer, mostly relies on the HAS_FLESH and HAS_BONE species traits on human species +/// golems and androids, cannot suffer any wounds +#define BIO_INORGANIC 0 +/// skeletons and plasmemes, can only suffer bone wounds, only needs mangled bone to be able to dismember +#define BIO_JUST_BONE 1 +/// nothing right now, maybe slimepeople in the future, can only suffer slashing, piercing, and burn wounds +#define BIO_JUST_FLESH 2 +/// standard humanoids, can suffer all wounds, needs mangled bone and flesh to dismember +#define BIO_FLESH_BONE 3 + +/// If this wound requires having the HAS_FLESH flag for humanoids +#define FLESH_WOUND (1<<0) +/// If this wound requires having the HAS_BONE flag for humanaoids +#define BONE_WOUND (1<<1) +/// If having this wound counts as mangled flesh for dismemberment +#define MANGLES_FLESH (1<<2) +/// If having this wound counts as mangled bone for dismemberment +#define MANGLES_BONE (1<<3) +/// If this wound marks the limb as being allowed to have gauze applied +#define ACCEPTS_GAUZE (1<<4) + diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index e02fd415b0d..64851ffbd7a 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -171,9 +171,9 @@ else return clamp(w_class * 6, 10, 100) // Multiply the item's weight class by 6, then clamp the value between 10 and 100 -/mob/living/proc/send_item_attack_message(obj/item/I, mob/living/user, hit_area) +/mob/living/proc/send_item_attack_message(obj/item/I, mob/living/user, hit_area, obj/item/bodypart/hit_bodypart) var/message_verb = "attacked" - if(I.attack_verb && I.attack_verb.len) + if(length(I.attack_verb)) message_verb = "[pick(I.attack_verb)]" else if(!I.force) return diff --git a/code/controllers/subsystem/persistence.dm b/code/controllers/subsystem/persistence.dm index 2e2ba59e9c5..c464d45a6ce 100644 --- a/code/controllers/subsystem/persistence.dm +++ b/code/controllers/subsystem/persistence.dm @@ -402,8 +402,8 @@ SUBSYSTEM_DEF(persistence) ending_human.client.prefs.scars_list["[ending_human.client.prefs.scars_index]"] = "" else for(var/k in ending_human.all_wounds) - var/datum/wound/W = k - W.remove_wound() // so we can get the scars for open wounds + var/datum/wound/iter_wound = k + iter_wound.remove_wound() // so we can get the scars for open wounds if(!ending_human.client) return ending_human.client.prefs.scars_list["[ending_human.client.prefs.scars_index]"] = ending_human.format_scars() diff --git a/code/datums/components/butchering.dm b/code/datums/components/butchering.dm index c34cd5bcd2f..f49a35c1124 100644 --- a/code/datums/components/butchering.dm +++ b/code/datums/components/butchering.dm @@ -71,7 +71,7 @@ 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/brute/cut/critical/screaming_through_a_slit_throat = new + var/datum/wound/slash/critical/screaming_through_a_slit_throat = new screaming_through_a_slit_throat.apply_wound(slit_throat) H.apply_status_effect(/datum/status_effect/neck_slice) diff --git a/code/datums/components/embedded.dm b/code/datums/components/embedded.dm index 06dcc0bfd1a..3f8a995e8f0 100644 --- a/code/datums/components/embedded.dm +++ b/code/datums/components/embedded.dm @@ -23,7 +23,6 @@ */ - /datum/component/embedded dupe_mode = COMPONENT_DUPE_ALLOWED var/obj/item/bodypart/limb @@ -90,14 +89,18 @@ RegisterSignal(weapon, list(COMSIG_MOVABLE_MOVED, COMSIG_PARENT_QDELETING), .proc/weaponDeleted) victim.visible_message("[weapon] [harmful ? "embeds" : "sticks"] itself [harmful ? "in" : "to"] [victim]'s [limb.name]!", "[weapon] [harmful ? "embeds" : "sticks"] itself [harmful ? "in" : "to"] your [limb.name]!") + var/damage = weapon.throwforce if(harmful) victim.throw_alert("embeddedobject", /obj/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! - var/damage = weapon.w_class * impact_pain_mult - limb.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage, wound_bonus = CANT_WOUND) + damage += weapon.w_class * impact_pain_mult SEND_SIGNAL(victim, COMSIG_ADD_MOOD_EVENT, "embedded", /datum/mood_event/embedded) + if(damage > 0) + var/armor = victim.run_armor_check(limb.body_zone, "melee", "Your armor has protected your [limb.name].", "Your armor has softened a hit to your [limb.name].",I.armour_penetration) + limb.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage, blocked=armor, sharpness = I.get_sharpness()) + /datum/component/embedded/Destroy() var/mob/living/carbon/victim = parent if(victim && !victim.has_embedded_objects()) @@ -129,16 +132,22 @@ return var/damage = weapon.w_class * pain_mult - var/chance = pain_chance + var/pain_chance_current = pain_chance if(pain_stam_pct && victim.stam_paralyzed) //if it's a less-lethal embed, give them a break if they're already stamcritted - chance *= 0.2 + pain_chance_current *= 0.2 damage *= 0.5 + else if(victim.mobility_flags & ~MOBILITY_STAND) + pain_chance_current *= 0.2 - if(harmful && prob(chance)) + if(harmful && prob(pain_chance_current)) limb.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage, sharpness=TRUE, wound_bonus = CANT_WOUND) to_chat(victim, "[weapon] embedded in your [limb.name] hurts!") - if(prob(fall_chance)) + var/fall_chance_current = fall_chance + if(victim.mobility_flags & ~MOBILITY_STAND) + fall_chance_current *= 0.2 + + if(prob(fall_chance_current)) fallOut() //////////////////////////////////////// @@ -155,7 +164,7 @@ if(harmful && prob(chance)) var/damage = weapon.w_class * jostle_pain_mult - limb.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage, sharpness=TRUE, wound_bonus = CANT_WOUND) + limb.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage, wound_bonus = CANT_WOUND) to_chat(victim, "[weapon] embedded in your [limb.name] jostles and stings!") @@ -165,29 +174,27 @@ if(harmful) var/damage = weapon.w_class * remove_pain_mult - limb.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage, sharpness=TRUE, wound_bonus = CANT_WOUND) + limb.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage, sharpness=SHARP_EDGED) // can wound victim.visible_message("[weapon] falls [harmful ? "out" : "off"] of [victim.name]'s [limb.name]!", "[weapon] falls [harmful ? "out" : "off"] of your [limb.name]!") safeRemove() + /// Called when a carbon with an object embedded/stuck to them inspects themselves and clicks the appropriate link to begin ripping the item out. This handles the ripping attempt, descriptors, and dealing damage, then calls safe_remove() /datum/component/embedded/proc/ripOut(datum/source, obj/item/I, obj/item/bodypart/limb) if(I != weapon || src.limb != limb) return - var/mob/living/carbon/victim = parent var/time_taken = rip_time * weapon.w_class victim.visible_message("[victim] attempts to remove [weapon] from [victim.p_their()] [limb.name].","You attempt to remove [weapon] from your [limb.name]... (It will take [DisplayTimeText(time_taken)].)") - if(!do_after(victim, time_taken, target = victim)) return if(!weapon || !limb || weapon.loc != victim || !(weapon in limb.embedded_objects)) qdel(src) return - if(harmful) var/damage = weapon.w_class * remove_pain_mult - limb.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage, sharpness=TRUE) //It hurts to rip it out, get surgery you dingus. + limb.receive_damage(brute=(1-pain_stam_pct) * damage, stamina=pain_stam_pct * damage, sharpness=SHARP_EDGED) //It hurts to rip it out, get surgery you dingus. victim.emote("scream") victim.visible_message("[victim] successfully rips [weapon] [harmful ? "out" : "off"] of [victim.p_their()] [limb.name]!", "You successfully remove [weapon] from your [limb.name].") diff --git a/code/datums/components/pellet_cloud.dm b/code/datums/components/pellet_cloud.dm index c817252ef69..30e880a693f 100644 --- a/code/datums/components/pellet_cloud.dm +++ b/code/datums/components/pellet_cloud.dm @@ -1,3 +1,8 @@ +// the following defines are used for [/datum/component/pellet_cloud/var/list/wound_info_by_part] to store the damage, wound_bonus, and bw_bonus for each bodypart hit +#define CLOUD_POSITION_DAMAGE 1 +#define CLOUD_POSITION_W_BONUS 2 +#define CLOUD_POSITION_BW_BONUS 3 + /* * This component is used when you want to create a bunch of shrapnel or projectiles (say, shrapnel from a fragmentation grenade, or buckshot from a shotgun) from a central point, * without necessarily printing a separate message for every single impact. This component should be instantiated right when you need it (like the moment of firing), then activated @@ -29,9 +34,11 @@ var/list/pellets = list() /// An associated list with the atom hit as the key and how many pellets they've eaten for the value, for printing aggregate messages var/list/targets_hit = list() - /// For grenades, any /mob/living's the grenade is moved onto, see [/datum/component/pellet_cloud/proc/handle_martyrs()] + /// Another associated list for hit bodyparts on carbons so we can track how much wounding potential we have for each bodypart + var/list/wound_info_by_part = list() + /// For grenades, any /mob/living's the grenade is moved onto, see [/datum/component/pellet_cloud/proc/handle_martyrs] var/list/bodies - /// For grenades, tracking people who die covering a grenade for achievement purposes, see [/datum/component/pellet_cloud/proc/handle_martyrs()] + /// For grenades, tracking people who die covering a grenade for achievement purposes, see [/datum/component/pellet_cloud/proc/handle_martyrs] var/list/purple_hearts /// For grenades, tracking how many pellets are removed due to martyrs and how many pellets are added due to the last person to touch it being on top of it @@ -65,6 +72,7 @@ purple_hearts = null pellets = null targets_hit = null + wound_info_by_part = null bodies = null return ..() @@ -86,7 +94,7 @@ /** * create_casing_pellets() is for directed pellet clouds for ammo casings that have multiple pellets (buckshot and scatter lasers for instance) * - * Honestly this is mostly just a rehash of [/obj/item/ammo_casing/proc/fire_casing()] for pellet counts > 1, except this lets us tamper with the pellets and hook onto them for tracking purposes. + * Honestly this is mostly just a rehash of [/obj/item/ammo_casing/proc/fire_casing] for pellet counts > 1, except this lets us tamper with the pellets and hook onto them for tracking purposes. * The arguments really don't matter, this proc is triggered by COMSIG_PELLET_CLOUD_INIT which is only for this really, it's just a big mess of the state vars we need for doing the stuff over here. */ /datum/component/pellet_cloud/proc/create_casing_pellets(obj/item/ammo_casing/shell, atom/target, mob/living/user, fired_from, randomspread, spread, zone_override, params, distro) @@ -114,7 +122,7 @@ /** * create_blast_pellets() is for when we have a central point we want to shred the surroundings of with a ring of shrapnel, namely frag grenades and landmines. * - * Note that grenades have extra handling for someone throwing themselves/being thrown on top of it, while landmines do not (obviously, it's a landmine!). See [/datum/component/pellet_cloud/proc/handle_martyrs()] + * Note that grenades have extra handling for someone throwing themselves/being thrown on top of it, while landmines do not (obviously, it's a landmine!). See [/datum/component/pellet_cloud/proc/handle_martyrs] */ /datum/component/pellet_cloud/proc/create_blast_pellets(obj/O, mob/living/lanced_by) var/atom/A = parent @@ -189,10 +197,26 @@ break ///One of our pellets hit something, record what it was and check if we're done (terminated == num_pellets) -/datum/component/pellet_cloud/proc/pellet_hit(obj/projectile/P, atom/movable/firer, atom/target, Angle) +/datum/component/pellet_cloud/proc/pellet_hit(obj/projectile/P, atom/movable/firer, atom/target, Angle, hit_zone) pellets -= P terminated++ hits++ + var/obj/item/bodypart/hit_part + if(iscarbon(target) && hit_zone) + var/mob/living/carbon/hit_carbon = target + hit_part = hit_carbon.get_bodypart(hit_zone) + if(hit_part) + target = hit_part + if(P.wound_bonus != CANT_WOUND) // handle wounding + // unfortunately, due to how pellet clouds handle finalizing only after every pellet is accounted for, that also means there might be a short delay in dealing wounds if one pellet goes wide + // while buckshot may reach a target or miss it all in one tick, we also have to account for possible ricochets that may take a bit longer to hit the target + if(isnull(wound_info_by_part[hit_part])) + wound_info_by_part[hit_part] = list(0, 0, 0) + wound_info_by_part[hit_part][CLOUD_POSITION_DAMAGE] += P.damage // these account for decay + wound_info_by_part[hit_part][CLOUD_POSITION_W_BONUS] += P.wound_bonus + wound_info_by_part[hit_part][CLOUD_POSITION_BW_BONUS] += P.bare_wound_bonus + P.wound_bonus = CANT_WOUND // actual wounding will be handled aggregate + targets_hit[target]++ if(targets_hit[target] == 1) RegisterSignal(target, COMSIG_PARENT_QDELETING, .proc/on_target_qdel, override=TRUE) @@ -233,12 +257,23 @@ for(var/atom/target in targets_hit) var/num_hits = targets_hit[target] UnregisterSignal(target, COMSIG_PARENT_QDELETING) + var/obj/item/bodypart/hit_part + if(isbodypart(target)) + hit_part = target + target = hit_part.owner + 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 + wound_info_by_part[hit_part] = null + hit_part.painless_wound_roll(wound_type, damage_dealt, w_bonus, bw_bonus, initial(P.sharpness)) + if(num_hits > 1) - target.visible_message("[target] is hit by [num_hits] [proj_name]s!", null, null, COMBAT_MESSAGE_RANGE, target) - to_chat(target, "You're hit by [num_hits] [proj_name]s!") + target.visible_message("[target] is hit by [num_hits] [proj_name]s[hit_part ? " in the [hit_part.name]" : ""]!", null, null, COMBAT_MESSAGE_RANGE, target) + to_chat(target, "You're hit by [num_hits] [proj_name]s[hit_part ? " in the [hit_part.name]" : ""]!") else - target.visible_message("[target] is hit by a [proj_name]!", null, null, COMBAT_MESSAGE_RANGE, target) - to_chat(target, "You're hit by a [proj_name]!") + target.visible_message("[target] is hit by a [proj_name][hit_part ? " in the [hit_part.name]" : ""]!", null, null, COMBAT_MESSAGE_RANGE, target) + to_chat(target, "You're hit by a [proj_name][hit_part ? " in the [hit_part.name]" : ""]!") for(var/M in purple_hearts) var/mob/living/martyr = M @@ -287,3 +322,8 @@ targets_hit -= target LAZYREMOVE(bodies, target) LAZYREMOVE(purple_hearts, target) + + +#undef CLOUD_POSITION_DAMAGE +#undef CLOUD_POSITION_W_BONUS +#undef CLOUD_POSITION_BW_BONUS diff --git a/code/datums/components/tackle.dm b/code/datums/components/tackle.dm index cc35c1f78af..84124c9878c 100644 --- a/code/datums/components/tackle.dm +++ b/code/datums/components/tackle.dm @@ -366,7 +366,7 @@ user.adjustBruteLoss(30) playsound(user, 'sound/effects/blobattack.ogg', 60, TRUE) playsound(user, 'sound/effects/splat.ogg', 70, TRUE) - playsound(user, 'sound/effects/crack2.ogg', 70, TRUE) + playsound(user, 'sound/effects/wounds/crack2.ogg', 70, TRUE) user.emote("scream") user.gain_trauma(/datum/brain_trauma/severe/paralysis/paraplegic) // oopsie indeed! shake_camera(user, 7, 7) diff --git a/code/datums/elements/embed.dm b/code/datums/elements/embed.dm index 301799316d3..24965d435f8 100644 --- a/code/datums/elements/embed.dm +++ b/code/datums/elements/embed.dm @@ -34,9 +34,9 @@ if(!isitem(target) && !isprojectile(target)) return ELEMENT_INCOMPATIBLE + RegisterSignal(target, COMSIG_ELEMENT_ATTACH, .proc/severancePackage) if(isitem(target)) RegisterSignal(target, COMSIG_MOVABLE_IMPACT_ZONE, .proc/checkEmbed) - RegisterSignal(target, COMSIG_ELEMENT_ATTACH, .proc/severancePackage) RegisterSignal(target, COMSIG_PARENT_EXAMINE, .proc/examined) RegisterSignal(target, COMSIG_EMBED_TRY_FORCE, .proc/tryForceEmbed) RegisterSignal(target, COMSIG_ITEM_DISABLE_EMBED, .proc/detachFromWeapon) @@ -63,7 +63,7 @@ if(isitem(target)) UnregisterSignal(target, list(COMSIG_MOVABLE_IMPACT_ZONE, COMSIG_ELEMENT_ATTACH, COMSIG_MOVABLE_IMPACT, COMSIG_PARENT_EXAMINE, COMSIG_EMBED_TRY_FORCE, COMSIG_ITEM_DISABLE_EMBED)) else - UnregisterSignal(target, list(COMSIG_PROJECTILE_SELF_ON_HIT)) + UnregisterSignal(target, list(COMSIG_PROJECTILE_SELF_ON_HIT, COMSIG_ELEMENT_ATTACH)) /// Checking to see if we're gonna embed into a human @@ -74,13 +74,13 @@ var/actual_chance = embed_chance if(!weapon.isEmbedHarmless()) // all the armor in the world won't save you from a kick me sign - var/armor = max(victim.run_armor_check(hit_zone, "bullet", silent=TRUE), victim.run_armor_check(hit_zone, "bomb", silent=TRUE)) // we'll be nice and take the better of bullet and bomb armor + var/armor = max(victim.run_armor_check(hit_zone, "bullet", silent=TRUE), victim.run_armor_check(hit_zone, "bomb", silent=TRUE)) * 0.5 // we'll be nice and take the better of bullet and bomb armor, halved if(armor) // we only care about armor penetration if there's actually armor to penetrate var/pen_mod = -armor + weapon.armour_penetration // even a little bit of armor can make a big difference for shrapnel with large negative armor pen actual_chance += pen_mod // doing the armor pen as a separate calc just in case this ever gets expanded on if(actual_chance <= 0) - victim.visible_message("[weapon] bounces off [victim]'s armor!", "[weapon] bounces off your armor!", vision_distance = COMBAT_MESSAGE_RANGE) + victim.visible_message("[weapon] bounces off [victim]'s armor, unable to embed!", "[weapon] bounces off your armor, unable to embed!", vision_distance = COMBAT_MESSAGE_RANGE) return var/roll_embed = prob(actual_chance) @@ -107,7 +107,7 @@ return TRUE ///A different embed element has been attached, so we'll detach and let them handle things -/datum/element/embed/proc/severancePackage(obj/item/weapon, datum/element/E) +/datum/element/embed/proc/severancePackage(obj/weapon, datum/element/E) if(istype(E, /datum/element/embed)) Detach(weapon) @@ -134,13 +134,16 @@ return // we don't care var/obj/item/payload = new payload_type(get_turf(hit)) + if(istype(payload, /obj/item/shrapnel/bullet)) + payload.name = P.name + payload.embedding = P.embedding + payload.updateEmbedding() var/mob/living/carbon/C = hit var/obj/item/bodypart/limb = C.get_bodypart(hit_zone) if(!limb) limb = C.get_bodypart() - if(!payload.tryEmbed(limb)) - payload.failedEmbed() + payload.tryEmbed(limb) Detach(P) /** @@ -172,4 +175,5 @@ hit_zone = limb.body_zone C = limb.owner - return checkEmbed(I, C, hit_zone, forced=TRUE) + checkEmbed(I, C, hit_zone, forced=TRUE) + return TRUE diff --git a/code/datums/mutations/actions.dm b/code/datums/mutations/actions.dm index 4dcb91e660b..9c4b4ff25f5 100644 --- a/code/datums/mutations/actions.dm +++ b/code/datums/mutations/actions.dm @@ -284,7 +284,7 @@ throw_speed = 4 embedding = list("embedded_pain_multiplier" = 4, "embed_chance" = 100, "embedded_fall_chance" = 0, "embedded_ignore_throwspeed_threshold" = TRUE) w_class = WEIGHT_CLASS_SMALL - sharpness = IS_SHARP + sharpness = SHARP_POINTY custom_materials = list(/datum/material/biomass = 500) var/mob/living/carbon/human/fired_by /// if we missed our target diff --git a/code/datums/mutations/hulk.dm b/code/datums/mutations/hulk.dm index b80b64b66ec..63d87ea4728 100644 --- a/code/datums/mutations/hulk.dm +++ b/code/datums/mutations/hulk.dm @@ -54,11 +54,11 @@ /datum/mutation/human/hulk/proc/break_an_arm(obj/item/bodypart/arm) switch(arm.brute_dam) if(45 to 50) - arm.force_wound_upwards(/datum/wound/brute/bone/critical) + arm.force_wound_upwards(/datum/wound/blunt/critical) if(41 to 45) - arm.force_wound_upwards(/datum/wound/brute/bone/severe) + arm.force_wound_upwards(/datum/wound/blunt/severe) if(35 to 41) - arm.force_wound_upwards(/datum/wound/brute/bone/moderate) + arm.force_wound_upwards(/datum/wound/blunt/moderate) /datum/mutation/human/hulk/on_life() if(owner.health < 0) diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm index 1f6cfa5e3c6..40b1f40f05c 100644 --- a/code/datums/status_effects/debuffs.dm +++ b/code/datums/status_effects/debuffs.dm @@ -350,7 +350,7 @@ if(ishuman(owner)) var/mob/living/carbon/human/H = owner var/obj/item/bodypart/bodypart = pick(H.bodyparts) - var/datum/wound/brute/cut/severe/crit_wound = new + var/datum/wound/slash/severe/crit_wound = new crit_wound.apply_wound(bodypart) return ..() @@ -434,7 +434,7 @@ var/still_bleeding = FALSE for(var/thing in throat.wounds) var/datum/wound/W = thing - if(W.wound_type == WOUND_LIST_CUT && W.severity > WOUND_SEVERITY_MODERATE) + if(W.wound_type == WOUND_SLASH && W.severity > WOUND_SEVERITY_MODERATE) still_bleeding = TRUE break if(!still_bleeding) diff --git a/code/datums/status_effects/wound_effects.dm b/code/datums/status_effects/wound_effects.dm index 751a3508a38..775b72699ab 100644 --- a/code/datums/status_effects/wound_effects.dm +++ b/code/datums/status_effects/wound_effects.dm @@ -116,8 +116,7 @@ /datum/status_effect/wound/on_creation(mob/living/new_owner, incoming_wound) . = ..() - var/datum/wound/W = incoming_wound - linked_wound = W + linked_wound = incoming_wound linked_limb = linked_wound.limb /datum/status_effect/wound/on_remove() @@ -138,9 +137,9 @@ // bones -/datum/status_effect/wound/bone +/datum/status_effect/wound/blunt -/datum/status_effect/wound/bone/interact_speed_modifier() +/datum/status_effect/wound/blunt/interact_speed_modifier() var/mob/living/carbon/C = owner if(C.get_active_hand() == linked_limb) @@ -149,7 +148,7 @@ return 1 -/datum/status_effect/wound/bone/nextmove_modifier() +/datum/status_effect/wound/blunt/nextmove_modifier() var/mob/living/carbon/C = owner if(C.get_active_hand() == linked_limb) @@ -157,30 +156,31 @@ return 1 -/datum/status_effect/wound/bone/moderate +// blunt +/datum/status_effect/wound/blunt/moderate id = "disjoint" -/datum/status_effect/wound/bone/severe +/datum/status_effect/wound/blunt/severe id = "hairline" - -/datum/status_effect/wound/bone/critical +/datum/status_effect/wound/blunt/critical id = "compound" - -// cuts -/datum/status_effect/wound/cut/moderate +// slash +/datum/status_effect/wound/slash/moderate id = "abrasion" - -/datum/status_effect/wound/cut/severe +/datum/status_effect/wound/slash/severe id = "laceration" - -/datum/status_effect/wound/cut/critical +/datum/status_effect/wound/slash/critical id = "avulsion" - +// pierce +/datum/status_effect/wound/pierce/moderate + id = "breakage" +/datum/status_effect/wound/pierce/severe + id = "puncture" +/datum/status_effect/wound/pierce/critical + id = "rupture" // burns /datum/status_effect/wound/burn/moderate id = "seconddeg" - /datum/status_effect/wound/burn/severe id = "thirddeg" - /datum/status_effect/wound/burn/critical id = "fourthdeg" diff --git a/code/datums/wounds/_wounds.dm b/code/datums/wounds/_wounds.dm index d70a4ebdb2d..29c87b32d4e 100644 --- a/code/datums/wounds/_wounds.dm +++ b/code/datums/wounds/_wounds.dm @@ -33,15 +33,13 @@ /// 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 list of wounds it belongs in, WOUND_LIST_BONE, WOUND_LIST_CUT, or WOUND_LIST_BURN + /// The list of wounds it belongs in, WOUND_LIST_BLUNT, WOUND_LIST_SLASH, or WOUND_LIST_BURN var/wound_type /// What body zones can we affect 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) /// Who owns the body part that we're wounding var/mob/living/carbon/victim = null - /// If we only work on organics (everything right now) - var/organic_only = TRUE /// The bodypart we're parented to var/obj/item/bodypart/limb = null @@ -51,8 +49,6 @@ var/list/treatable_by_grabbed /// Tools with the specified tool flag will also be able to try directly treating this wound var/treatable_tool - /// Set to TRUE if we don't give a shit about the patient's comfort and are allowed to just use any random sharp thing on this wound. Will require an aggressive grab or more to perform - var/treatable_sharp /// 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 @@ -65,17 +61,13 @@ /// How much we're contributing to this limb's bleed_rate var/blood_flow - /// The minimum we need to roll on [/obj/item/bodypart/proc/check_wounding()] to begin suffering this wound, see check_wounding_mods() for more + /// 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 /// If we need to process each life tick var/processes = FALSE - /// If TRUE and an item that can treat multiple different types of coexisting wounds (gauze can be used to splint broken bones, staunch bleeding, and cover burns), we get first dibs if we come up first for it, then become nonpriority. - /// Otherwise, if no untreated wound claims the item, we cycle through the non priority wounds and pick a random one who can use that item. - var/treat_priority = FALSE - /// If having this wound makes currently makes the parent bodypart unusable var/disabling @@ -89,12 +81,15 @@ var/cryo_progress /// What kind of scars this wound will create description wise once healed - var/list/scarring_descriptions = list("general disfigurement") + var/scar_keyword = "generic" /// If we've already tried scarring while removing (since remove_wound calls qdel, and qdel calls remove wound, .....) TODO: make this cleaner var/already_scarred = FALSE /// If we forced this wound through badmin smite, we won't count it towards the round totals var/from_smite + /// What flags apply to this wound + var/wound_flags = (FLESH_WOUND | BONE_WOUND | ACCEPTS_GAUZE) + /datum/wound/Destroy() if(attached_surgery) QDEL_NULL(attached_surgery) @@ -115,13 +110,13 @@ * * smited- If this is a smite, we don't care about this wound for stat tracking purposes (not yet implemented) */ /datum/wound/proc/apply_wound(obj/item/bodypart/L, silent = FALSE, datum/wound/old_wound = null, smited = FALSE) - if(!istype(L) || !L.owner || !(L.body_zone in viable_zones) || isalien(L.owner)) + if(!istype(L) || !L.owner || !(L.body_zone in viable_zones) || isalien(L.owner) || !L.is_organic_limb()) qdel(src) return if(ishuman(L.owner)) var/mob/living/carbon/human/H = L.owner - if(organic_only && ((NOBLOOD in H.dna.species.species_traits) || !L.is_organic_limb())) + if(((wound_flags & BONE_WOUND) && !(HAS_BONE in H.dna.species.species_traits)) || ((wound_flags & FLESH_WOUND) && !(HAS_FLESH in H.dna.species.species_traits))) qdel(src) return @@ -161,7 +156,7 @@ victim.visible_message(msg, "Your [limb.name] [occur_text]!", vision_distance = vis_dist) if(sound_effect) - playsound(L.owner, sound_effect, 60 + 20 * severity, TRUE) + playsound(L.owner, sound_effect, 70 + 20 * severity, TRUE) if(!demoted) wound_injury(old_wound) @@ -181,7 +176,7 @@ SEND_SIGNAL(victim, COMSIG_CARBON_LOSE_WOUND, src, limb) if(limb && !ignore_limb) LAZYREMOVE(limb.wounds, src) - limb.update_wounds() + limb.update_wounds(replaced) /** * replace_wound() is used when you want to replace the current wound with a new wound, presumably of the same category, just of a different severity (either up or down counts) @@ -189,7 +184,7 @@ * This proc actually instantiates the new wound based off the specific type path passed, then returns the new instantiated wound datum. * * Arguments: - * * new_type- The TYPE PATH of the wound you want to replace this, like /datum/wound/brute/cut/severe + * * new_type- The TYPE PATH of the wound you want to replace this, like /datum/wound/slash/severe * * smited- If this is a smite, we don't care about this wound for stat tracking purposes (not yet implemented) */ /datum/wound/proc/replace_wound(new_type, smited = FALSE) @@ -206,9 +201,6 @@ /// 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() - if(HAS_TRAIT(victim, TRAIT_NOMETABOLISM)) - return - switch(severity) if(WOUND_SEVERITY_MODERATE) victim.reagents.add_reagent(/datum/reagent/determination, WOUND_DETERMINATION_MODERATE) @@ -216,11 +208,13 @@ victim.reagents.add_reagent(/datum/reagent/determination, WOUND_DETERMINATION_SEVERE) if(WOUND_SEVERITY_CRITICAL) victim.reagents.add_reagent(/datum/reagent/determination, WOUND_DETERMINATION_CRITICAL) + if(WOUND_SEVERITY_LOSS) + victim.reagents.add_reagent(/datum/reagent/determination, WOUND_DETERMINATION_LOSS) /** - * try_treating() is an intercept run from [/mob/living/carbon/attackby()] right after surgeries but before anything else. Return TRUE here if the item is something that is relevant to treatment to take over the interaction. + * try_treating() is an intercept run from [/mob/living/carbon/proc/attackby] right after surgeries but before anything else. Return TRUE here if the item is something that is relevant to treatment to take over the interaction. * - * This proc leads into [/datum/wound/proc/treat()] and probably shouldn't be added onto in children types. You can specify what items or tools you want to be intercepted + * This proc leads into [/datum/wound/proc/treat] and probably shouldn't be added onto in children types. You can specify what items or tools you want to be intercepted * with var/list/treatable_by and var/treatable_tool, then if an item fulfills one of those requirements and our wound claims it first, it goes over to treat() and treat_self(). * * Arguments: @@ -260,7 +254,7 @@ treat(I, user) return TRUE -/// Return TRUE if we have an item that can only be used while aggro grabbed (unhanded aggro grab treatments go in [/datum/wound/proc/try_handling()]). Treatment is still is handled in [/datum/wound/proc/treat()] +/// Return TRUE if we have an item that can only be used while aggro grabbed (unhanded aggro grab treatments go in [/datum/wound/proc/try_handling]). Treatment is still is handled in [/datum/wound/proc/treat] /datum/wound/proc/check_grab_treatments(obj/item/I, mob/user) return FALSE @@ -290,10 +284,22 @@ if(cryo_progress > 33 * severity) qdel(src) +/// 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 + +/// Called when the patient is undergoing stasis, so that having fully treated a wound doesn't make you sit there helplessly until you think to unbuckle them +/datum/wound/proc/on_stasis() + return + /// Called when we're crushed in an airlock or firedoor, for one of the improvised joint dislocation fixes /datum/wound/proc/crush() return +/// Used when we're being dragged while bleeding, the value we return is how much bloodloss this wound causes from being dragged. Since it's a proc, you can let bandages soak some of the blood +/datum/wound/proc/drag_bleed_amount() + return + /** * get_examine_description() is used in carbon/examine and human/examine to show the status of this wound. Useful if you need to show some status like the wound being splinted or bandaged. * @@ -303,7 +309,8 @@ * * mob/user: The user examining the wound's owner, if that matters */ /datum/wound/proc/get_examine_description(mob/user) - return "[victim.p_their(TRUE)] [limb.name] [examine_desc]!" + . = "[victim.p_their(TRUE)] [limb.name] [examine_desc]" + . = severity <= WOUND_SEVERITY_MODERATE ? "[.]." : "[.]!" /datum/wound/proc/get_scanner_description(mob/user) return "Type: [name]\nSeverity: [severity_text()]\nDescription: [desc]\nRecommended Treatment: [treat_text]" diff --git a/code/datums/wounds/bones.dm b/code/datums/wounds/bones.dm index 93ca9c5b671..4d1c27b42e0 100644 --- a/code/datums/wounds/bones.dm +++ b/code/datums/wounds/bones.dm @@ -7,12 +7,10 @@ /* Base definition */ -/datum/wound/brute/bone - sound_effect = 'sound/effects/crack1.ogg' - wound_type = WOUND_LIST_BONE - - /// The item we're currently splinted with, if there is one - var/obj/item/stack/splinted +/datum/wound/blunt + sound_effect = 'sound/effects/wounds/crack1.ogg' + wound_type = WOUND_BLUNT + wound_flags = (BONE_WOUND | ACCEPTS_GAUZE) /// Have we been taped? var/taped @@ -31,12 +29,12 @@ /// How long do we wait +/- 20% for the next trauma? var/trauma_cycle_cooldown /// If this is a chest wound and this is set, we have this chance to cough up blood when hit in the chest - var/chance_internal_bleeding = 0 + var/internal_bleeding_chance = 0 /* Overwriting of base procs */ -/datum/wound/brute/bone/wound_injury(datum/wound/old_wound = null) +/datum/wound/blunt/wound_injury(datum/wound/old_wound = null) if(limb.body_zone == BODY_ZONE_HEAD && brain_trauma_group) processes = TRUE active_trauma = victim.gain_trauma_type(brain_trauma_group, TRAUMA_RESILIENCE_WOUND) @@ -53,14 +51,14 @@ update_inefficiencies() -/datum/wound/brute/bone/remove_wound(ignore_limb, replaced) +/datum/wound/blunt/remove_wound(ignore_limb, replaced) limp_slowdown = 0 QDEL_NULL(active_trauma) if(victim) UnregisterSignal(victim, COMSIG_HUMAN_EARLY_UNARMED_ATTACK) return ..() -/datum/wound/brute/bone/handle_process() +/datum/wound/blunt/handle_process() . = ..() if(limb.body_zone == BODY_ZONE_HEAD && brain_trauma_group && world.time > next_trauma_cycle) if(active_trauma) @@ -86,7 +84,7 @@ remove_wound() /// If we're a human who's punching something with a broken arm, we might hurt ourselves doing so -/datum/wound/brute/bone/proc/attack_with_hurt_hand(mob/M, atom/target, proximity) +/datum/wound/blunt/proc/attack_with_hurt_hand(mob/M, atom/target, proximity) if(victim.get_active_hand() != limb || victim.a_intent == INTENT_HELP || !ismob(target) || severity <= WOUND_SEVERITY_MODERATE) return @@ -104,61 +102,54 @@ limb.receive_damage(brute=rand(3,7)) return COMPONENT_NO_ATTACK_HAND -/datum/wound/brute/bone/receive_damage(wounding_type, wounding_dmg, wound_bonus) - if(!victim) +/datum/wound/blunt/receive_damage(wounding_type, wounding_dmg, wound_bonus) + if(!victim || wounding_dmg < WOUND_MINIMUM_DAMAGE) return + if(ishuman(victim)) + var/mob/living/carbon/human/human_victim = victim + if(NOBLOOD in human_victim.dna?.species.species_traits) + return - if(limb.body_zone == BODY_ZONE_CHEST && victim.blood_volume && prob(chance_internal_bleeding + wounding_dmg)) + if(limb.body_zone == BODY_ZONE_CHEST && victim.blood_volume && prob(internal_bleeding_chance + wounding_dmg)) var/blood_bled = rand(1, wounding_dmg * (severity == WOUND_SEVERITY_CRITICAL ? 2 : 1.5)) // 12 brute toolbox can cause up to 18/24 bleeding with a severe/critical chest wound switch(blood_bled) if(1 to 6) victim.bleed(blood_bled, TRUE) if(7 to 13) - victim.visible_message("[victim] coughs up a bit of blood from the blow to [victim.p_their()] chest.", "You cough up a bit of blood from the blow to your chest.") + victim.visible_message("[victim] coughs up a bit of blood from the blow to [victim.p_their()] chest.", "You cough up a bit of blood from the blow to your chest.", vision_distance=COMBAT_MESSAGE_RANGE) victim.bleed(blood_bled, TRUE) if(14 to 19) - victim.visible_message("[victim] spits out a string of blood from the blow to [victim.p_their()] chest!", "You spit out a string of blood from the blow to your chest!") + victim.visible_message("[victim] spits out a string of blood from the blow to [victim.p_their()] chest!", "You spit out a string of blood from the blow to your chest!", vision_distance=COMBAT_MESSAGE_RANGE) new /obj/effect/temp_visual/dir_setting/bloodsplatter(victim.loc, victim.dir) victim.bleed(blood_bled) if(20 to INFINITY) - victim.visible_message("[victim] chokes up a spray of blood from the blow to [victim.p_their()] chest!", "You choke up on a spray of blood from the blow to your chest!") + victim.visible_message("[victim] chokes up a spray of blood from the blow to [victim.p_their()] chest!", "You choke up on a spray of blood from the blow to your chest!", vision_distance=COMBAT_MESSAGE_RANGE) victim.bleed(blood_bled) new /obj/effect/temp_visual/dir_setting/bloodsplatter(victim.loc, victim.dir) victim.add_splatter_floor(get_step(victim.loc, victim.dir)) - if(!(wounding_type in list(WOUND_SHARP, WOUND_BURN)) || !splinted || wound_bonus == CANT_WOUND) - return - splinted.take_damage(wounding_dmg, damage_type = (wounding_type == WOUND_SHARP ? BRUTE : BURN), sound_effect = FALSE) - if(QDELETED(splinted)) - var/destroyed_verb = (wounding_type == WOUND_SHARP ? "torn" : "burned") - victim.visible_message("The splint securing [victim]'s [limb.name] is [destroyed_verb] away!", "The splint securing your [limb.name] is [destroyed_verb] away!", vision_distance=COMBAT_MESSAGE_RANGE) - splinted = null - treat_priority = TRUE - update_inefficiencies() - - -/datum/wound/brute/bone/get_examine_description(mob/user) - if(!splinted && !gelled && !taped) +/datum/wound/blunt/get_examine_description(mob/user) + if(!limb.current_gauze && !gelled && !taped) return ..() - var/msg = "" - if(!splinted) - msg = "[victim.p_their(TRUE)] [limb.name] [examine_desc]" + var/list/msg = list() + if(!limb.current_gauze) + msg += "[victim.p_their(TRUE)] [limb.name] [examine_desc]" else - var/splint_condition = "" + var/sling_condition = "" // how much life we have left in these bandages - switch(splinted.obj_integrity / splinted.max_integrity * 100) + switch(limb.current_gauze.obj_integrity / limb.current_gauze.max_integrity * 100) if(0 to 25) - splint_condition = "just barely " + sling_condition = "just barely " if(25 to 50) - splint_condition = "loosely " + sling_condition = "loosely " if(50 to 75) - splint_condition = "mostly " + sling_condition = "mostly " if(75 to INFINITY) - splint_condition = "tightly " + sling_condition = "tightly " - msg = "[victim.p_their(TRUE)] [limb.name] is [splint_condition] fastened in a splint of [splinted.name]" + msg += "[victim.p_their(TRUE)] [limb.name] is [sling_condition] fastened in a sling of [limb.current_gauze.name]" if(taped) msg += ", and appears to be reforming itself under some surgical tape!" @@ -166,58 +157,35 @@ msg += ", with fizzing flecks of blue bone gel sparking off the bone!" else msg += "!" - return "[msg]" + return "[msg.Join()]" /* - New common procs for /datum/wound/brute/bone/ + New common procs for /datum/wound/blunt/ */ -/datum/wound/brute/bone/proc/update_inefficiencies() +/datum/wound/blunt/proc/update_inefficiencies() if(limb.body_zone in list(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)) - if(splinted) - limp_slowdown = initial(limp_slowdown) * splinted.splint_factor + if(limb.current_gauze) + limp_slowdown = initial(limp_slowdown) * limb.current_gauze.splint_factor else limp_slowdown = initial(limp_slowdown) victim.apply_status_effect(STATUS_EFFECT_LIMP) else if(limb.body_zone in list(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM)) - if(splinted) - interaction_efficiency_penalty = 1 + ((interaction_efficiency_penalty - 1) * splinted.splint_factor) + if(limb.current_gauze) + interaction_efficiency_penalty = 1 + ((interaction_efficiency_penalty - 1) * limb.current_gauze.splint_factor) else interaction_efficiency_penalty = interaction_efficiency_penalty - if(initial(disabling) && splinted) - disabling = FALSE - else if(initial(disabling)) - disabling = TRUE + if(initial(disabling)) + disabling = !limb.current_gauze limb.update_wounds() -/* - BEWARE OF REDUNDANCY AHEAD THAT I MUST PARE DOWN -*/ - -/datum/wound/brute/bone/proc/splint(obj/item/stack/I, mob/user) - if(splinted && splinted.splint_factor >= I.splint_factor) - to_chat(user, "The splint already on [user == victim ? "your" : "[victim]'s"] [limb.name] is better than you can do with [I].") - return - - user.visible_message("[user] begins splinting [victim]'s [limb.name] with [I].", "You begin splinting [user == victim ? "your" : "[victim]'s"] [limb.name] with [I]...") - - if(!do_after(user, base_treat_time * (user == victim ? 1.5 : 1), target = victim, extra_checks=CALLBACK(src, .proc/still_exists))) - return - - user.visible_message("[user] finishes splinting [victim]'s [limb.name]!", "You finish splinting [user == victim ? "your" : "[victim]'s"] [limb.name]!") - treat_priority = FALSE - splinted = new I.type(limb) - splinted.amount = 1 - I.use(1) - update_inefficiencies() - /* Moderate (Joint Dislocation) */ -/datum/wound/brute/bone/moderate +/datum/wound/blunt/moderate name = "Joint Dislocation" desc = "Patient's bone has been unset from socket, causing pain and reduced motor function." treat_text = "Recommended application of bonesetter to affected limb, though manual relocation by applying an aggressive grab to the patient and helpfully interacting with afflicted limb may suffice." @@ -230,15 +198,16 @@ threshold_minimum = 35 threshold_penalty = 15 treatable_tool = TOOL_BONESET - status_effect_type = /datum/status_effect/wound/bone/moderate - scarring_descriptions = list("light discoloring", "a slight blue tint") + wound_flags = (BONE_WOUND) + status_effect_type = /datum/status_effect/wound/blunt/moderate + scar_keyword = "bluntmoderate" -/datum/wound/brute/bone/moderate/crush() +/datum/wound/blunt/moderate/crush() if(prob(33)) victim.visible_message("[victim]'s dislocated [limb.name] pops back into place!", "Your dislocated [limb.name] pops back into place! Ow!") remove_wound() -/datum/wound/brute/bone/moderate/try_handling(mob/living/carbon/human/user) +/datum/wound/blunt/moderate/try_handling(mob/living/carbon/human/user) if(user.pulling != victim || user.zone_selected != limb.body_zone || user.a_intent == INTENT_GRAB) return FALSE @@ -256,7 +225,7 @@ return TRUE /// If someone is snapping our dislocated joint back into place by hand with an aggro grab and help intent -/datum/wound/brute/bone/moderate/proc/chiropractice(mob/living/carbon/human/user) +/datum/wound/blunt/moderate/proc/chiropractice(mob/living/carbon/human/user) var/time = base_treat_time if(!do_after(user, time, target=victim, extra_checks = CALLBACK(src, .proc/still_exists))) @@ -275,7 +244,7 @@ chiropractice(user) /// If someone is snapping our dislocated joint into a fracture by hand with an aggro grab and harm or disarm intent -/datum/wound/brute/bone/moderate/proc/malpractice(mob/living/carbon/human/user) +/datum/wound/blunt/moderate/proc/malpractice(mob/living/carbon/human/user) var/time = base_treat_time if(!do_after(user, time, target=victim, extra_checks = CALLBACK(src, .proc/still_exists))) @@ -293,7 +262,7 @@ malpractice(user) -/datum/wound/brute/bone/moderate/treat(obj/item/I, mob/user) +/datum/wound/blunt/moderate/treat(obj/item/I, mob/user) if(victim == user) victim.visible_message("[user] begins resetting [victim.p_their()] [limb.name] with [I].", "You begin resetting your [limb.name] with [I]...") else @@ -317,56 +286,57 @@ Severe (Hairline Fracture) */ -/datum/wound/brute/bone/severe +/datum/wound/blunt/severe name = "Hairline Fracture" desc = "Patient's bone has suffered a crack in the foundation, causing serious pain and reduced limb functionality." - treat_text = "Recommended light surgical application of bone gel, though splinting will prevent worsening situation." - examine_desc = "appears bruised and grotesquely swollen" - + treat_text = "Recommended light surgical application of bone gel, though a sling of medical gauze will prevent worsening situation." + examine_desc = "appears grotesquely swollen, its attachment weakened" occur_text = "sprays chips of bone and develops a nasty looking bruise" + severity = WOUND_SEVERITY_SEVERE interaction_efficiency_penalty = 2 limp_slowdown = 6 threshold_minimum = 60 threshold_penalty = 30 - treatable_by = list(/obj/item/stack/sticky_tape/surgical, /obj/item/stack/medical/gauze, /obj/item/stack/medical/bone_gel) - status_effect_type = /datum/status_effect/wound/bone/severe - treat_priority = TRUE - scarring_descriptions = list("a faded, fist-sized bruise", "a vaguely triangular peel scar") + treatable_by = list(/obj/item/stack/sticky_tape/surgical, /obj/item/stack/medical/bone_gel) + status_effect_type = /datum/status_effect/wound/blunt/severe + scar_keyword = "bluntsevere" brain_trauma_group = BRAIN_TRAUMA_MILD trauma_cycle_cooldown = 1.5 MINUTES - chance_internal_bleeding = 40 + internal_bleeding_chance = 40 + wound_flags = (BONE_WOUND | ACCEPTS_GAUZE | MANGLES_BONE) -/datum/wound/brute/bone/critical +/datum/wound/blunt/critical name = "Compound Fracture" desc = "Patient's bones have suffered multiple gruesome fractures, causing significant pain and near uselessness of limb." treat_text = "Immediate binding of affected limb, followed by surgical intervention ASAP." - examine_desc = "has a cracked bone sticking out of it" + examine_desc = "is mangled and pulped, seemingly held together by tissue alone" occur_text = "cracks apart, exposing broken bones to open air" + severity = WOUND_SEVERITY_CRITICAL interaction_efficiency_penalty = 4 limp_slowdown = 9 - sound_effect = 'sound/effects/crack2.ogg' + 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/gauze, /obj/item/stack/medical/bone_gel) - status_effect_type = /datum/status_effect/wound/bone/critical - treat_priority = TRUE - scarring_descriptions = list("a section of janky skin lines and badly healed scars", "a large patch of uneven skin tone", "a cluster of calluses") + treatable_by = list(/obj/item/stack/sticky_tape/surgical, /obj/item/stack/medical/bone_gel) + status_effect_type = /datum/status_effect/wound/blunt/critical + scar_keyword = "bluntcritical" brain_trauma_group = BRAIN_TRAUMA_SEVERE trauma_cycle_cooldown = 2.5 MINUTES - chance_internal_bleeding = 60 + internal_bleeding_chance = 60 + wound_flags = (BONE_WOUND | ACCEPTS_GAUZE | MANGLES_BONE) // doesn't make much sense for "a" bone to stick out of your head -/datum/wound/brute/bone/critical/apply_wound(obj/item/bodypart/L, silent, datum/wound/old_wound, smited) +/datum/wound/blunt/critical/apply_wound(obj/item/bodypart/L, silent, datum/wound/old_wound, smited) if(L.body_zone == BODY_ZONE_HEAD) occur_text = "splits open, exposing a bare, cracked skull through the flesh and blood" examine_desc = "has an unsettling indent, with bits of skull poking out" . = ..() /// if someone is using bone gel on our wound -/datum/wound/brute/bone/proc/gel(obj/item/stack/medical/bone_gel/I, mob/user) +/datum/wound/blunt/proc/gel(obj/item/stack/medical/bone_gel/I, mob/user) if(gelled) to_chat(user, "[user == victim ? "Your" : "[victim]'s"] [limb.name] is already coated with bone gel!") return @@ -385,9 +355,9 @@ var/painkiller_bonus = 0 if(victim.drunkenness) painkiller_bonus += 5 - if(victim.reagents && victim.reagents.has_reagent(/datum/reagent/medicine/morphine)) + if(victim.reagents?.has_reagent(/datum/reagent/medicine/morphine)) painkiller_bonus += 10 - if(victim.reagents && victim.reagents.has_reagent(/datum/reagent/determination)) + if(victim.reagents?.has_reagent(/datum/reagent/determination)) painkiller_bonus += 5 if(prob(25 + (20 * severity - 2) - painkiller_bonus)) // 25%/45% chance to fail self-applying with severe and critical wounds, modded by painkillers @@ -401,7 +371,7 @@ gelled = TRUE /// if someone is using surgical tape on our wound -/datum/wound/brute/bone/proc/tape(obj/item/stack/sticky_tape/surgical/I, mob/user) +/datum/wound/blunt/proc/tape(obj/item/stack/sticky_tape/surgical/I, mob/user) if(!gelled) to_chat(user, "[user == victim ? "Your" : "[victim]'s"] [limb.name] must be coated with bone gel to perform this emergency operation!") return @@ -426,15 +396,13 @@ taped = TRUE processes = TRUE -/datum/wound/brute/bone/treat(obj/item/I, mob/user) +/datum/wound/blunt/treat(obj/item/I, mob/user) if(istype(I, /obj/item/stack/medical/bone_gel)) gel(I, user) else if(istype(I, /obj/item/stack/sticky_tape/surgical)) tape(I, user) - else if(istype(I, /obj/item/stack/medical/gauze)) - splint(I, user) -/datum/wound/brute/bone/get_scanner_description(mob/user) +/datum/wound/blunt/get_scanner_description(mob/user) . = ..() . += "
" @@ -444,7 +412,7 @@ else if(!taped) . += "Continue Alternative Treatment: Apply surgical tape directly to injured limb to begin bone regeneration. Note, this is both excruciatingly painful and slow.\n" else - . += "Note: Bone regeneration in effect. Bone is [round(regen_points_current/regen_points_needed)]% regenerated.\n" + . += "Note: Bone regeneration in effect. Bone is [round(regen_points_current*100/regen_points_needed)]% regenerated.\n" if(limb.body_zone == BODY_ZONE_HEAD) . += "Cranial Trauma Detected: Patient will suffer random bouts of [severity == WOUND_SEVERITY_SEVERE ? "mild" : "severe"] brain traumas until bone is repaired." diff --git a/code/datums/wounds/burns.dm b/code/datums/wounds/burns.dm index a09cf2fb7b0..e403ac2850a 100644 --- a/code/datums/wounds/burns.dm +++ b/code/datums/wounds/burns.dm @@ -4,11 +4,12 @@ // TODO: well, a lot really, but specifically I want to add potential fusing of clothing/equipment on the affected area, and limb infections, though those may go in body part code /datum/wound/burn a_or_from = "from" - wound_type = WOUND_LIST_BURN + wound_type = WOUND_BURN processes = TRUE - sound_effect = 'sound/effects/sizzle1.ogg' + sound_effect = 'sound/effects/wounds/sizzle1.ogg' + wound_flags = (FLESH_WOUND | ACCEPTS_GAUZE) - treatable_by = list(/obj/item/stack/medical/gauze, /obj/item/stack/medical/ointment, /obj/item/stack/medical/mesh) // sterilizer and alcohol will require reagent treatments, coming soon + 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 /// How much damage to our flesh we currently have. Once both this and infestation reach 0, the wound is considered healed @@ -27,8 +28,6 @@ /// Once we reach infestation beyond WOUND_INFESTATION_SEPSIS, we get this many warnings before the limb is completely paralyzed (you'd have to ignore a really bad burn for a really long time for this to happen) var/strikes_to_lose_limb = 3 - /// The current bandage we have for this wound (maybe move bandages to the limb?) - var/obj/item/stack/current_bandage /datum/wound/burn/handle_process() . = ..() @@ -47,15 +46,11 @@ sanitization += 0.3 flesh_healing += 0.5 - if(current_bandage) - current_bandage.absorption_capacity -= WOUND_BURN_SANITIZATION_RATE - if(current_bandage.absorption_capacity <= 0) - victim.visible_message("Pus soaks through \the [current_bandage] on [victim]'s [limb.name].", "Pus soaks through \the [current_bandage] on your [limb.name].", vision_distance=COMBAT_MESSAGE_RANGE) - QDEL_NULL(current_bandage) - treat_priority = TRUE + if(limb.current_gauze) + limb.seep_gauze(WOUND_BURN_SANITIZATION_RATE) if(flesh_healing > 0) - var/bandage_factor = (current_bandage ? current_bandage.splint_factor : 1) + var/bandage_factor = (limb.current_gauze ? limb.current_gauze.splint_factor : 1) flesh_damage = max(0, flesh_damage - 1) flesh_healing = max(0, flesh_healing - bandage_factor) // good bandages multiply the length of flesh healing @@ -67,7 +62,7 @@ // sanitization is checked after the clearing check but before the rest, because we freeze the effects of infection while we have sanitization if(sanitization > 0) - var/bandage_factor = (current_bandage ? current_bandage.splint_factor : 1) + var/bandage_factor = (limb.current_gauze ? limb.current_gauze.splint_factor : 1) infestation = max(0, infestation - WOUND_BURN_SANITIZATION_RATE) sanitization = max(0, sanitization - (WOUND_BURN_SANITIZATION_RATE * bandage_factor)) return @@ -122,10 +117,10 @@ if(strikes_to_lose_limb <= 0) return "[victim.p_their(TRUE)] [limb.name] is completely dead and unrecognizable as organic." - var/condition = "" - if(current_bandage) + var/list/condition = list("[victim.p_their(TRUE)] [limb.name] [examine_desc]") + if(limb.current_gauze) var/bandage_condition - switch(current_bandage.absorption_capacity) + switch(limb.current_gauze.absorption_capacity) if(0 to 1.25) bandage_condition = "nearly ruined " if(1.25 to 2.75) @@ -135,7 +130,7 @@ if(4 to INFINITY) bandage_condition = "clean " - condition += " underneath a dressing of [bandage_condition] [current_bandage.name]" + condition += " underneath a dressing of [bandage_condition] [limb.current_gauze.name]" else switch(infestation) if(WOUND_INFECTION_MODERATE to WOUND_INFECTION_SEVERE) @@ -149,7 +144,7 @@ else condition += "!" - return "[victim.p_their(TRUE)] [limb.name] [examine_desc][condition]" + return "[condition.Join()]" /datum/wound/burn/get_scanner_description(mob/user) if(strikes_to_lose_limb == 0) @@ -200,36 +195,6 @@ else try_treating(I, user) -/// for use in the burn dressing surgery since we don't want to make them do another do_after obviously -/datum/wound/burn/proc/force_bandage(obj/item/stack/medical/gauze/I, mob/user) - QDEL_NULL(current_bandage) - current_bandage = new I.type(limb) - current_bandage.amount = 1 - treat_priority = FALSE - sanitization += I.sanitization - I.use(1) - -/// if someone is wrapping gauze on our burns -/datum/wound/burn/proc/bandage(obj/item/stack/medical/gauze/I, mob/user) - if(current_bandage) - if(current_bandage.absorption_capacity > I.absorption_capacity + 1) - to_chat(user, "The [current_bandage] on [victim]'s [limb.name] is still in better condition than your [I.name]!") - return - user.visible_message("[user] begins to redress the burns on [victim]'s [limb.name] with [I]...", "You begin redressing the burns on [user == victim ? "your" : "[victim]'s"] [limb.name] with [I]...") - else - user.visible_message("[user] begins to dress the burns on [victim]'s [limb.name] with [I]...", "You begin dressing the burns on [user == victim ? "your" : "[victim]'s"] [limb.name] with [I]...") - - if(!do_after(user, (user == victim ? I.self_delay : I.other_delay), target=victim, extra_checks = CALLBACK(src, .proc/still_exists))) - return - - user.visible_message("[user] applies [I] to [victim].", "You apply [I] to [user == victim ? "your" : "[victim]'s"] [limb.name].") - QDEL_NULL(current_bandage) - current_bandage = new I.type(limb) - current_bandage.amount = 1 - treat_priority = FALSE - sanitization += I.sanitization - I.use(1) - /// if someone is using mesh on our burns /datum/wound/burn/proc/mesh(obj/item/stack/medical/mesh/I, mob/user) user.visible_message("[user] begins wrapping [victim]'s [limb.name] with [I]...", "You begin wrapping [user == victim ? "your" : "[victim]'s"] [limb.name] with [I]...") @@ -261,17 +226,26 @@ COOLDOWN_START(I, uv_cooldown, I.uv_cooldown_length) /datum/wound/burn/treat(obj/item/I, mob/user) - if(istype(I, /obj/item/stack/medical/gauze)) - bandage(I, user) - else if(istype(I, /obj/item/stack/medical/ointment)) + if(istype(I, /obj/item/stack/medical/ointment)) ointment(I, user) else if(istype(I, /obj/item/stack/medical/mesh)) mesh(I, user) else if(istype(I, /obj/item/flashlight/pen/paramedic)) uv(I, user) -/// basic support for instabitaluri/synthflesh healing flesh damage, more chem support in the future -/datum/wound/burn/proc/regenerate_flesh(amount) +// people complained about burns not healing on stasis beds, so in addition to checking if it's cured, they also get the special ability to very slowly heal on stasis beds if they have the healing effects stored +/datum/wound/burn/on_stasis() + . = ..() + if(flesh_healing > 0) + flesh_damage = max(0, flesh_damage - 0.2) + if((flesh_damage <= 0) && (infestation <= 1)) + to_chat(victim, "The burns on your [limb.name] have cleared up!") + qdel(src) + return + if(sanitization > 0) + infestation = max(0, infestation - WOUND_BURN_SANITIZATION_RATE * 0.2) + +/datum/wound/burn/on_synthflesh(amount) flesh_healing += amount * 0.5 // 20u patch will heal 10 flesh standard // we don't even care about first degree burns, straight to second @@ -287,7 +261,7 @@ threshold_penalty = 30 // burns cause significant decrease in limb integrity compared to other wounds status_effect_type = /datum/status_effect/wound/burn/moderate flesh_damage = 5 - scarring_descriptions = list("small amoeba-shaped skinmarks", "a faded streak of depressed skin") + scar_keyword = "burnmoderate" /datum/wound/burn/severe name = "Third Degree Burns" @@ -300,10 +274,10 @@ threshold_minimum = 80 threshold_penalty = 40 status_effect_type = /datum/status_effect/wound/burn/severe - treatable_by = list(/obj/item/flashlight/pen/paramedic, /obj/item/stack/medical/gauze, /obj/item/stack/medical/ointment, /obj/item/stack/medical/mesh) + treatable_by = list(/obj/item/flashlight/pen/paramedic, /obj/item/stack/medical/ointment, /obj/item/stack/medical/mesh) infestation_rate = 0.05 // appx 13 minutes to reach sepsis without any treatment flesh_damage = 12.5 - scarring_descriptions = list("a large, jagged patch of faded skin", "random spots of shiny, smooth skin", "spots of taut, leathery skin") + scar_keyword = "burnsevere" /datum/wound/burn/critical name = "Catastrophic Burns" @@ -313,11 +287,11 @@ occur_text = "vaporizes as flesh, bone, and fat melt together in a horrifying mess" severity = WOUND_SEVERITY_CRITICAL damage_mulitplier_penalty = 1.3 - sound_effect = 'sound/effects/sizzle2.ogg' + sound_effect = 'sound/effects/wounds/sizzle2.ogg' threshold_minimum = 140 threshold_penalty = 80 status_effect_type = /datum/status_effect/wound/burn/critical - treatable_by = list(/obj/item/flashlight/pen/paramedic, /obj/item/stack/medical/gauze, /obj/item/stack/medical/ointment, /obj/item/stack/medical/mesh) + treatable_by = list(/obj/item/flashlight/pen/paramedic, /obj/item/stack/medical/ointment, /obj/item/stack/medical/mesh) infestation_rate = 0.15 // appx 4.33 minutes to reach sepsis without any treatment flesh_damage = 20 - scarring_descriptions = list("massive, disfiguring keloid scars", "several long streaks of badly discolored and malformed skin", "unmistakeable splotches of dead tissue from serious burns") + scar_keyword = "burncritical" diff --git a/code/datums/wounds/loss.dm b/code/datums/wounds/loss.dm new file mode 100644 index 00000000000..bc7e18a1831 --- /dev/null +++ b/code/datums/wounds/loss.dm @@ -0,0 +1,42 @@ + +/datum/wound/loss + name = "Dismembered" + desc = "oof ouch!!" + + sound_effect = 'sound/effects/dismember.ogg' + severity = WOUND_SEVERITY_LOSS + threshold_minimum = 180 + status_effect_type = null + scar_keyword = "dismember" + wound_flags = null + +/// 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) + if(!istype(dismembered_part) || !dismembered_part.owner || !(dismembered_part.body_zone in viable_zones) || isalien(dismembered_part.owner) || !dismembered_part.can_dismember()) + qdel(src) + return + + already_scarred = TRUE // so we don't scar a limb we don't have. If I add different levels of amputation desc, do it here + + switch(wounding_type) + if(WOUND_BLUNT) + occur_text = "is shattered through the last bone holding it together, severing it completely!" + if(WOUND_SLASH) + occur_text = "is slashed through the last tissue holding it together, severing it completely!" + if(WOUND_PIERCE) + occur_text = "is pierced through the last tissue holding it together, severing it completely!" + if(WOUND_BURN) + occur_text = "is completely incinerated, falling to dust!" + + victim = dismembered_part.owner + + var/msg = "[victim]'s [dismembered_part.name] [occur_text]!" + + victim.visible_message(msg, "Your [dismembered_part.name] [occur_text]!") + + limb = dismembered_part + severity = WOUND_SEVERITY_LOSS + second_wind() + log_wound(victim, src) + dismembered_part.dismember(wounding_type == WOUND_BURN ? BURN : BRUTE) + qdel(src) diff --git a/code/datums/wounds/pierce.dm b/code/datums/wounds/pierce.dm new file mode 100644 index 00000000000..c307f9582de --- /dev/null +++ b/code/datums/wounds/pierce.dm @@ -0,0 +1,171 @@ + +/* + Pierce +*/ + +/datum/wound/pierce + sound_effect = 'sound/weapons/slice.ogg' + processes = TRUE + wound_type = WOUND_PIERCE + treatable_by = list(/obj/item/stack/medical/suture) + treatable_tool = TOOL_CAUTERY + base_treat_time = 3 SECONDS + wound_flags = (FLESH_WOUND | ACCEPTS_GAUZE) + + /// How much blood we start losing when this wound is first applied + var/initial_flow + /// If gauzed, what percent of the internal bleeding actually clots of the total absorption rate + var/gauzed_clot_rate + + /// When hit on this bodypart, we have this chance of losing some blood + the incoming damage + var/internal_bleeding_chance + /// If we let off blood when hit, the max blood lost is this * the incoming damage + var/internal_bleeding_coefficient + +/datum/wound/pierce/wound_injury(datum/wound/old_wound) + blood_flow = initial_flow + +/datum/wound/pierce/receive_damage(wounding_type, wounding_dmg, wound_bonus) + if(victim.stat == DEAD || wounding_dmg < 5) + return + if(victim.blood_volume && prob(internal_bleeding_chance + wounding_dmg)) + if(limb.current_gauze && limb.current_gauze.splint_factor) + wounding_dmg *= (1 - limb.current_gauze.splint_factor) + var/blood_bled = rand(1, wounding_dmg * internal_bleeding_coefficient) // 12 brute toolbox can cause up to 15/18/21 bloodloss on mod/sev/crit + switch(blood_bled) + if(1 to 6) + victim.bleed(blood_bled, TRUE) + if(7 to 13) + victim.visible_message("Blood droplets fly from the hole in [victim]'s [limb.name].", "You cough up a bit of blood from the blow to your [limb.name].", vision_distance=COMBAT_MESSAGE_RANGE) + victim.bleed(blood_bled, TRUE) + if(14 to 19) + victim.visible_message("A small stream of blood spurts from the hole in [victim]'s [limb.name]!", "You spit out a string of blood from the blow to your [limb.name]!", vision_distance=COMBAT_MESSAGE_RANGE) + new /obj/effect/temp_visual/dir_setting/bloodsplatter(victim.loc, victim.dir) + victim.bleed(blood_bled) + if(20 to INFINITY) + victim.visible_message("A spray of blood streams from the gash in [victim]'s [limb.name]!", "You choke up on a spray of blood from the blow to your [limb.name]!", vision_distance=COMBAT_MESSAGE_RANGE) + victim.bleed(blood_bled) + new /obj/effect/temp_visual/dir_setting/bloodsplatter(victim.loc, victim.dir) + victim.add_splatter_floor(get_step(victim.loc, victim.dir)) + +/datum/wound/pierce/handle_process() + blood_flow = min(blood_flow, WOUND_SLASH_MAX_BLOODFLOW) + + if(victim.bodytemperature < (BODYTEMP_NORMAL - 10)) + blood_flow -= 0.2 + if(prob(5)) + to_chat(victim, "You feel the [lowertext(name)] in your [limb.name] firming up from the cold!") + + if(victim.reagents?.has_reagent(/datum/reagent/toxin/heparin)) + blood_flow += 0.5 // old herapin used to just add +2 bleed stacks per tick, this adds 0.5 bleed flow to all open cuts which is probably even stronger as long as you can cut them first + + if(limb.current_gauze) + blood_flow -= limb.current_gauze.absorption_rate * gauzed_clot_rate + limb.current_gauze.absorption_capacity -= limb.current_gauze.absorption_rate + + if(blood_flow <= 0) + qdel(src) + +/datum/wound/pierce/on_stasis() + . = ..() + if(blood_flow <= 0) + qdel(src) + +/datum/wound/pierce/treat(obj/item/I, mob/user) + if(istype(I, /obj/item/stack/medical/suture)) + suture(I, user) + else if(I.tool_behaviour == TOOL_CAUTERY || I.get_temperature() > 300) + tool_cauterize(I, user) + +/datum/wound/pierce/on_xadone(power) + . = ..() + 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 + +/datum/wound/pierce/on_synthflesh(power) + . = ..() + blood_flow -= 0.05 * power // 20u * 0.05 = -1 blood flow, less than with slashes but still good considering smaller bleed rates + +/// If someone is using a suture to close this cut +/datum/wound/pierce/proc/suture(obj/item/stack/medical/suture/I, mob/user) + var/self_penalty_mult = (user == victim ? 1.4 : 1) + user.visible_message("[user] begins stitching [victim]'s [limb.name] with [I]...", "You begin stitching [user == victim ? "your" : "[victim]'s"] [limb.name] with [I]...") + if(!do_after(user, base_treat_time * self_penalty_mult, target=victim, extra_checks = CALLBACK(src, .proc/still_exists))) + return + user.visible_message("[user] stitches up some of the bleeding on [victim].", "You stitch up some of the bleeding on [user == victim ? "yourself" : "[victim]"].") + var/blood_sutured = I.stop_bleeding / self_penalty_mult * 0.5 + blood_flow -= blood_sutured + limb.heal_damage(I.heal_brute, I.heal_burn) + + if(blood_flow > 0) + try_treating(I, user) + else + to_chat(user, "You successfully close the hole in [user == victim ? "your" : "[victim]'s"] [limb.name].") + +/// If someone is using either a cautery tool or something with heat to cauterize this pierce +/datum/wound/pierce/proc/tool_cauterize(obj/item/I, mob/user) + var/self_penalty_mult = (user == victim ? 1.5 : 1) + user.visible_message("[user] begins cauterizing [victim]'s [limb.name] with [I]...", "You begin cauterizing [user == victim ? "your" : "[victim]'s"] [limb.name] with [I]...") + if(!do_after(user, base_treat_time * self_penalty_mult, target=victim, extra_checks = CALLBACK(src, .proc/still_exists))) + return + + user.visible_message("[user] cauterizes some of the bleeding on [victim].", "You cauterize some of the bleeding on [victim].") + limb.receive_damage(burn = 2 + severity, wound_bonus = CANT_WOUND) + if(prob(30)) + victim.emote("scream") + var/blood_cauterized = (0.6 / self_penalty_mult) * 0.5 + blood_flow -= blood_cauterized + + if(blood_flow > 0) + try_treating(I, user) + +/datum/wound/pierce/moderate + name = "Minor Breakage" + desc = "Patient's skin has been broken open, causing severe bruising and minor internal bleeding in affected area." + treat_text = "Treat affected site with bandaging or exposure to extreme cold. In dire cases, brief exposure to vacuum may suffice." // space is cold in ss13, so it's like an ice pack! + examine_desc = "has a small, circular hole, gently bleeding" + occur_text = "spurts out a thin stream of blood" + sound_effect = 'sound/effects/wounds/pierce1.ogg' + severity = WOUND_SEVERITY_MODERATE + initial_flow = 1.5 + gauzed_clot_rate = 0.8 + internal_bleeding_chance = 30 + internal_bleeding_coefficient = 1.25 + threshold_minimum = 30 + threshold_penalty = 15 + status_effect_type = /datum/status_effect/wound/pierce/moderate + scar_keyword = "piercemoderate" + +/datum/wound/pierce/severe + name = "Open Puncture" + desc = "Patient's internal tissue is penetrated, causing sizeable internal bleeding and reduced limb stability." + treat_text = "Repair punctures in skin by suture or cautery, extreme cold may also work." + examine_desc = "is pierced clear through, with bits of tissue obscuring the open hole" + occur_text = "looses a violent spray of blood, revealing a pierced wound" + sound_effect = 'sound/effects/wounds/pierce2.ogg' + severity = WOUND_SEVERITY_SEVERE + initial_flow = 2.25 + gauzed_clot_rate = 0.6 + internal_bleeding_chance = 60 + internal_bleeding_coefficient = 1.5 + threshold_minimum = 50 + threshold_penalty = 25 + status_effect_type = /datum/status_effect/wound/pierce/severe + scar_keyword = "piercesevere" + +/datum/wound/pierce/critical + name = "Ruptured Cavity" + desc = "Patient's internal tissue and circulatory system is shredded, causing significant internal bleeding and damage to internal organs." + treat_text = "Surgical repair of puncture wound, followed by supervised resanguination." + examine_desc = "is ripped clear through, barely held together by exposed bone" + occur_text = "blasts apart, sending chunks of viscera flying in all directions" + sound_effect = 'sound/effects/wounds/pierce3.ogg' + severity = WOUND_SEVERITY_CRITICAL + initial_flow = 3 + gauzed_clot_rate = 0.4 + internal_bleeding_chance = 80 + internal_bleeding_coefficient = 1.75 + threshold_minimum = 100 + threshold_penalty = 40 + status_effect_type = /datum/status_effect/wound/pierce/critical + scar_keyword = "piercecritical" + wound_flags = (FLESH_WOUND | ACCEPTS_GAUZE | MANGLES_FLESH) diff --git a/code/datums/wounds/scars/_scars.dm b/code/datums/wounds/scars/_scars.dm index bfbaab835e6..3365fc359d9 100644 --- a/code/datums/wounds/scars/_scars.dm +++ b/code/datums/wounds/scars/_scars.dm @@ -53,8 +53,12 @@ if(victim) LAZYADD(victim.all_scars, src) - description = pick(W.scarring_descriptions) - precise_location = pick(limb.specific_locations) + if(victim && victim.get_biological_state() == BIO_JUST_BONE) + description = pick(strings(BONE_SCAR_FILE, W.scar_keyword)) || "general disfigurement" + else + description = pick(strings(FLESH_SCAR_FILE, W.scar_keyword)) || "general disfigurement" + + precise_location = pick(strings(SCAR_LOC_FILE, limb.body_zone)) switch(W.severity) if(WOUND_SEVERITY_MODERATE) visibility = 2 @@ -62,6 +66,9 @@ visibility = 3 if(WOUND_SEVERITY_CRITICAL) visibility = 5 + if(WOUND_SEVERITY_LOSS) + visibility = 7 + precise_location = "amputation" /// Used when we finalize a scar from a healing cut /datum/scar/proc/lazy_attach(obj/item/bodypart/BP, datum/wound/W) @@ -71,10 +78,11 @@ LAZYADD(victim.all_scars, src) /// Used to "load" a persistent scar -/datum/scar/proc/load(obj/item/bodypart/BP, description, specific_location, severity=WOUND_SEVERITY_SEVERE) - if(!(BP.body_zone in applicable_zones)) +/datum/scar/proc/load(obj/item/bodypart/BP, version, description, specific_location, severity=WOUND_SEVERITY_SEVERE) + if(!(BP.body_zone in applicable_zones) || !BP.is_organic_limb()) qdel(src) return + limb = BP src.severity = severity LAZYADD(limb.scars, src) @@ -90,6 +98,8 @@ visibility = 3 if(WOUND_SEVERITY_CRITICAL) visibility = 5 + if(WOUND_SEVERITY_LOSS) + visibility = 7 return TRUE /// What will show up in examine_more() if this scar is visible @@ -102,9 +112,12 @@ if(WOUND_SEVERITY_MODERATE) msg = "[msg]" if(WOUND_SEVERITY_SEVERE) - msg = "[msg]" + msg = "[msg]" if(WOUND_SEVERITY_CRITICAL) - msg = "[msg]" + msg = "[msg]" + if(WOUND_SEVERITY_LOSS) + msg = "[victim.p_their(TRUE)] [limb.name] [description]." // different format + msg = "[msg]" return "\t[msg]" /// Whether a scar can currently be seen by the viewer @@ -117,12 +130,12 @@ if(!ishuman(victim) || isobserver(viewer) || victim == viewer) return TRUE - var/mob/living/carbon/human/H = victim + var/mob/living/carbon/human/human_victim = victim if(istype(limb, /obj/item/bodypart/head)) - if((H.wear_mask && (H.wear_mask.flags_inv & HIDEFACE)) || (H.head && (H.head.flags_inv & HIDEFACE))) + if((human_victim.wear_mask && (human_victim.wear_mask.flags_inv & HIDEFACE)) || (human_victim.head && (human_victim.head.flags_inv & HIDEFACE))) return FALSE else if(limb.scars_covered_by_clothes) - var/num_covers = LAZYLEN(H.clothingonpart(limb)) + var/num_covers = LAZYLEN(human_victim.clothingonpart(limb)) if(num_covers + get_dist(viewer, victim) >= visibility) return FALSE @@ -131,4 +144,9 @@ /// Used to format a scar to safe in preferences for persistent scars /datum/scar/proc/format() if(!fake) - return "[limb.body_zone]|[description]|[precise_location]|[severity]" + return "[SCAR_CURRENT_VERSION]|[limb.body_zone]|[description]|[precise_location]|[severity]" + +/// Used to format a scar to safe in preferences for persistent scars +/datum/scar/proc/format_amputated(body_zone) + description = pick(list("is several skintone shades paler than the rest of the body", "is a gruesome patchwork of artificial flesh", "has a large series of attachment scars at the articulation points")) + return "[SCAR_CURRENT_VERSION]|[body_zone]|[description]|amputated|[WOUND_SEVERITY_LOSS]" diff --git a/code/datums/wounds/cuts.dm b/code/datums/wounds/slash.dm similarity index 60% rename from code/datums/wounds/cuts.dm rename to code/datums/wounds/slash.dm index 5f9d7542386..6b1ded05ed1 100644 --- a/code/datums/wounds/cuts.dm +++ b/code/datums/wounds/slash.dm @@ -3,15 +3,15 @@ Cuts */ -/datum/wound/brute/cut +/datum/wound/slash sound_effect = 'sound/weapons/slice.ogg' processes = TRUE - wound_type = WOUND_LIST_CUT - treatable_by = list(/obj/item/stack/medical/suture, /obj/item/stack/medical/gauze) + 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 - treat_priority = TRUE base_treat_time = 3 SECONDS + wound_flags = (FLESH_WOUND | ACCEPTS_GAUZE) /// How much blood we start losing when this wound is first applied var/initial_flow @@ -27,75 +27,82 @@ var/max_per_type /// The maximum flow we've had so far var/highest_flow - /// How much flow we've already cauterized - var/cauterized - /// How much flow we've already sutured - var/sutured - /// The current bandage we have for this wound (maybe move bandages to the limb?) - var/obj/item/stack/current_bandage /// 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/brute/cut/wound_injury(datum/wound/brute/cut/old_wound = null) +/datum/wound/slash/wound_injury(datum/wound/slash/old_wound = null) blood_flow = initial_flow if(old_wound) blood_flow = max(old_wound.blood_flow, initial_flow) if(old_wound.severity > severity && old_wound.highest_scar) highest_scar = old_wound.highest_scar old_wound.highest_scar = null - if(old_wound.current_bandage) - current_bandage = old_wound.current_bandage - old_wound.current_bandage = null if(!highest_scar) highest_scar = new highest_scar.generate(limb, src, add_to_scars=FALSE) -/datum/wound/brute/cut/remove_wound(ignore_limb, replaced) +/datum/wound/slash/remove_wound(ignore_limb, replaced) if(!replaced && highest_scar) already_scarred = TRUE highest_scar.lazy_attach(limb) return ..() -/datum/wound/brute/cut/get_examine_description(mob/user) - if(!current_bandage) +/datum/wound/slash/get_examine_description(mob/user) + if(!limb.current_gauze) return ..() - var/bandage_condition = "" + var/list/msg = list("The cuts on [victim.p_their()] [limb.name] are wrapped with") // how much life we have left in these bandages - switch(current_bandage.absorption_capacity) + switch(limb.current_gauze.absorption_capacity) if(0 to 1.25) - bandage_condition = "nearly ruined " + msg += "nearly ruined " if(1.25 to 2.75) - bandage_condition = "badly worn " + msg += "badly worn " if(2.75 to 4) - bandage_condition = "slightly bloodied " + msg += "slightly bloodied " if(4 to INFINITY) - bandage_condition = "clean " - return "The cuts on [victim.p_their()] [limb.name] are wrapped with [bandage_condition] [current_bandage.name]!" + msg += "clean " + msg += "[limb.current_gauze.name]!" -/datum/wound/brute/cut/receive_damage(wounding_type, wounding_dmg, wound_bonus) - if(victim.stat != DEAD && wounding_type == WOUND_SHARP) // can't stab dead bodies to make it bleed faster this way + return "[msg.Join()]" + +/datum/wound/slash/receive_damage(wounding_type, wounding_dmg, wound_bonus) + if(victim.stat != DEAD && wounding_type == WOUND_SLASH) // can't stab dead bodies to make it bleed faster this way blood_flow += 0.05 * wounding_dmg -/datum/wound/brute/cut/handle_process() - blood_flow = min(blood_flow, WOUND_CUT_MAX_BLOODFLOW) +/datum/wound/slash/drag_bleed_amount() + // say we have 3 severe cuts with 3 blood flow each, pretty reasonable + // compare with being at 100 brute damage before, where you bled (brute/100 * 2), = 2 blood per tile + var/bleed_amt = min(blood_flow * 0.1, 1) // 3 * 3 * 0.1 = 0.9 blood total, less than before! the share here is .3 blood of course. - if(victim.reagents && victim.reagents.has_reagent(/datum/reagent/toxin/heparin)) + if(limb.current_gauze) // gauze stops all bleeding from dragging on this limb, but wears the gauze out quicker + limb.seep_gauze(bleed_amt * 0.33) + return + + return bleed_amt + +/datum/wound/slash/handle_process() + if(victim.stat == DEAD) + blood_flow -= max(clot_rate, WOUND_SLASH_DEAD_CLOT_MIN) + if(blood_flow < minimum_flow) + if(demotes_to) + replace_wound(demotes_to) + return + qdel(src) + return + + blood_flow = min(blood_flow, WOUND_SLASH_MAX_BLOODFLOW) + + if(victim.reagents?.has_reagent(/datum/reagent/toxin/heparin)) blood_flow += 0.5 // old herapin used to just add +2 bleed stacks per tick, this adds 0.5 bleed flow to all open cuts which is probably even stronger as long as you can cut them first - else if(victim.reagents && victim.reagents.has_reagent(/datum/reagent/medicine/coagulant)) - blood_flow -= 0.25 - if(current_bandage) + if(limb.current_gauze) if(clot_rate > 0) blood_flow -= clot_rate - blood_flow -= current_bandage.absorption_rate - current_bandage.absorption_capacity -= current_bandage.absorption_rate - if(current_bandage.absorption_capacity < 0) - victim.visible_message("Blood soaks through \the [current_bandage] on [victim]'s [limb.name].", "Blood soaks through \the [current_bandage] on your [limb.name].", vision_distance=COMBAT_MESSAGE_RANGE) - QDEL_NULL(current_bandage) - treat_priority = TRUE + blood_flow -= limb.current_gauze.absorption_rate + limb.seep_gauze(limb.current_gauze.absorption_rate) else blood_flow -= clot_rate @@ -109,23 +116,30 @@ to_chat(victim, "The cut on your [limb.name] has stopped bleeding!") qdel(src) + +/datum/wound/slash/on_stasis() + if(blood_flow >= minimum_flow) + return + if(demotes_to) + replace_wound(demotes_to) + return + qdel(src) + /* BEWARE, THE BELOW NONSENSE IS MADNESS. bones.dm looks more like what I have in mind and is sufficiently clean, don't pay attention to this messiness */ -/datum/wound/brute/cut/check_grab_treatments(obj/item/I, mob/user) +/datum/wound/slash/check_grab_treatments(obj/item/I, mob/user) if(istype(I, /obj/item/gun/energy/laser)) return TRUE -/datum/wound/brute/cut/treat(obj/item/I, mob/user) +/datum/wound/slash/treat(obj/item/I, mob/user) if(istype(I, /obj/item/gun/energy/laser)) las_cauterize(I, user) else if(I.tool_behaviour == TOOL_CAUTERY || I.get_temperature() > 300) tool_cauterize(I, user) - else if(istype(I, /obj/item/stack/medical/gauze)) - bandage(I, user) else if(istype(I, /obj/item/stack/medical/suture)) suture(I, user) -/datum/wound/brute/cut/try_handling(mob/living/carbon/human/user) +/datum/wound/slash/try_handling(mob/living/carbon/human/user) if(user.pulling != victim || user.zone_selected != limb.body_zone || user.a_intent == INTENT_GRAB) return FALSE @@ -136,7 +150,7 @@ return TRUE /// if a felinid is licking this cut to reduce bleeding -/datum/wound/brute/cut/proc/lick_wounds(mob/living/carbon/human/user) +/datum/wound/slash/proc/lick_wounds(mob/living/carbon/human/user) if(INTERACTING_WITH(user, victim)) to_chat(user, "You're already interacting with [victim]!") return @@ -155,12 +169,16 @@ else if(demotes_to) to_chat(user, "You successfully lower the severity of [victim]'s cuts.") -/datum/wound/brute/cut/on_xadone(power) +/datum/wound/slash/on_xadone(power) . = ..() 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 +/datum/wound/slash/on_synthflesh(power) + . = ..() + blood_flow -= 0.075 * power // 20u * 0.075 = -1.5 blood flow, pretty good for how little effort it is + /// If someone's putting a laser gun up to our cut to cauterize it -/datum/wound/brute/cut/proc/las_cauterize(obj/item/gun/energy/laser/lasgun, mob/user) +/datum/wound/slash/proc/las_cauterize(obj/item/gun/energy/laser/lasgun, mob/user) var/self_penalty_mult = (user == victim ? 1.25 : 1) user.visible_message("[user] begins aiming [lasgun] directly at [victim]'s [limb.name]...", "You begin aiming [lasgun] directly at [user == victim ? "your" : "[victim]'s"] [limb.name]...") if(!do_after(user, base_treat_time * self_penalty_mult, target=victim, extra_checks = CALLBACK(src, .proc/still_exists))) @@ -172,11 +190,10 @@ return victim.emote("scream") blood_flow -= damage / (5 * self_penalty_mult) // 20 / 5 = 4 bloodflow removed, p good - cauterized += damage / (5 * self_penalty_mult) victim.visible_message("The cuts on [victim]'s [limb.name] scar over!") /// If someone is using either a cautery tool or something with heat to cauterize this cut -/datum/wound/brute/cut/proc/tool_cauterize(obj/item/I, mob/user) +/datum/wound/slash/proc/tool_cauterize(obj/item/I, mob/user) var/self_penalty_mult = (user == victim ? 1.5 : 1) user.visible_message("[user] begins cauterizing [victim]'s [limb.name] with [I]...", "You begin cauterizing [user == victim ? "your" : "[victim]'s"] [limb.name] with [I]...") if(!do_after(user, base_treat_time * self_penalty_mult, target=victim, extra_checks = CALLBACK(src, .proc/still_exists))) @@ -188,7 +205,6 @@ victim.emote("scream") var/blood_cauterized = (0.6 / self_penalty_mult) blood_flow -= blood_cauterized - cauterized += blood_cauterized if(blood_flow > minimum_flow) try_treating(I, user) @@ -196,15 +212,15 @@ to_chat(user, "You successfully lower the severity of [user == victim ? "your" : "[victim]'s"] cuts.") /// If someone is using a suture to close this cut -/datum/wound/brute/cut/proc/suture(obj/item/stack/medical/suture/I, mob/user) +/datum/wound/slash/proc/suture(obj/item/stack/medical/suture/I, mob/user) var/self_penalty_mult = (user == victim ? 1.4 : 1) user.visible_message("[user] begins stitching [victim]'s [limb.name] with [I]...", "You begin stitching [user == victim ? "your" : "[victim]'s"] [limb.name] with [I]...") + if(!do_after(user, base_treat_time * self_penalty_mult, target=victim, extra_checks = CALLBACK(src, .proc/still_exists))) return user.visible_message("[user] stitches up some of the bleeding on [victim].", "You stitch up some of the bleeding on [user == victim ? "yourself" : "[victim]"].") var/blood_sutured = I.stop_bleeding / self_penalty_mult blood_flow -= blood_sutured - sutured += blood_sutured limb.heal_damage(I.heal_brute, I.heal_burn) if(blood_flow > minimum_flow) @@ -212,51 +228,31 @@ else if(demotes_to) to_chat(user, "You successfully lower the severity of [user == victim ? "your" : "[victim]'s"] cuts.") -/// If someone is using gauze on this cut -/datum/wound/brute/cut/proc/bandage(obj/item/stack/I, mob/user) - if(current_bandage) - if(current_bandage.absorption_capacity > I.absorption_capacity + 1) - to_chat(user, "The [current_bandage] on [victim]'s [limb.name] is still in better condition than your [I.name]!") - return - else - user.visible_message("[user] begins rewrapping the cuts on [victim]'s [limb.name] with [I]...", "You begin rewrapping the cuts on [user == victim ? "your" : "[victim]'s"] [limb.name] with [I]...") - else - user.visible_message("[user] begins wrapping the cuts on [victim]'s [limb.name] with [I]...", "You begin wrapping the cuts on [user == victim ? "your" : "[victim]'s"] [limb.name] with [I]...") - if(!do_after(user, base_treat_time, target=victim, extra_checks = CALLBACK(src, .proc/still_exists))) - return - user.visible_message("[user] applies [I] to [victim]'s [limb.name].", "You bandage some of the bleeding on [user == victim ? "yourself" : "[victim]"].") - QDEL_NULL(current_bandage) - current_bandage = new I.type(limb) - current_bandage.amount = 1 - treat_priority = FALSE - I.use(1) - - -/datum/wound/brute/cut/moderate +/datum/wound/slash/moderate name = "Rough Abrasion" desc = "Patient's skin has been badly scraped, generating moderate blood loss." treat_text = "Application of clean bandages or first-aid grade sutures, followed by food and rest." examine_desc = "has an open cut" occur_text = "is cut open, slowly leaking blood" - sound_effect = 'sound/effects/blood1.ogg' + sound_effect = 'sound/effects/wounds/blood1.ogg' severity = WOUND_SEVERITY_MODERATE initial_flow = 2 minimum_flow = 0.5 max_per_type = 3 - clot_rate = 0.15 + clot_rate = 0.12 threshold_minimum = 20 threshold_penalty = 10 - status_effect_type = /datum/status_effect/wound/cut/moderate - scarring_descriptions = list("light, faded lines", "minor cut marks", "a small faded slit", "a series of small scars") + status_effect_type = /datum/status_effect/wound/slash/moderate + scar_keyword = "slashmoderate" -/datum/wound/brute/cut/severe +/datum/wound/slash/severe name = "Open Laceration" desc = "Patient's skin is ripped clean open, allowing significant blood loss." treat_text = "Speedy application of first-aid grade sutures and clean bandages, followed by vitals monitoring to ensure recovery." examine_desc = "has a severe cut" occur_text = "is ripped open, veins spurting blood" - sound_effect = 'sound/effects/blood2.ogg' + sound_effect = 'sound/effects/wounds/blood2.ogg' severity = WOUND_SEVERITY_SEVERE initial_flow = 3.25 minimum_flow = 2.75 @@ -264,17 +260,17 @@ max_per_type = 4 threshold_minimum = 50 threshold_penalty = 25 - demotes_to = /datum/wound/brute/cut/moderate - status_effect_type = /datum/status_effect/wound/cut/severe - scarring_descriptions = list("a twisted line of faded gashes", "a gnarled sickle-shaped slice scar", "a long-faded puncture wound") + demotes_to = /datum/wound/slash/moderate + status_effect_type = /datum/status_effect/wound/slash/severe + scar_keyword = "slashsevere" -/datum/wound/brute/cut/critical +/datum/wound/slash/critical name = "Weeping Avulsion" desc = "Patient's skin is completely torn open, along with significant loss of tissue. Extreme blood loss will lead to quick death without intervention." treat_text = "Immediate bandaging and either suturing or cauterization, followed by supervised resanguination." - examine_desc = "is spurting blood at an alarming rate" + examine_desc = "is carved down to the bone, spraying blood wildly" occur_text = "is torn open, spraying blood wildly" - sound_effect = 'sound/effects/blood3.ogg' + sound_effect = 'sound/effects/wounds/blood3.ogg' severity = WOUND_SEVERITY_CRITICAL initial_flow = 4.25 minimum_flow = 4 @@ -282,25 +278,7 @@ max_per_type = 5 threshold_minimum = 80 threshold_penalty = 40 - demotes_to = /datum/wound/brute/cut/severe - status_effect_type = /datum/status_effect/wound/cut/critical - scarring_descriptions = list("a winding path of very badly healed scar tissue", "a series of peaks and valleys along a gruesome line of cut scar tissue", "a grotesque snake of indentations and stitching scars") - -// TODO: see about moving dismemberment over to this, i'll have to add judging dismembering power/wound potential wrt item size i guess -/datum/wound/brute/cut/loss - name = "Dismembered" - desc = "oof ouch!!" - occur_text = "is violently dismembered!" - sound_effect = 'sound/effects/dismember.ogg' - viable_zones = list(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG) - severity = WOUND_SEVERITY_LOSS - threshold_minimum = 180 - status_effect_type = null - -/datum/wound/brute/cut/loss/apply_wound(obj/item/bodypart/L, silent, datum/wound/brute/cut/old_wound, smited = FALSE) - if(!L.dismemberable) - qdel(src) - return - - L.dismember() - qdel(src) + demotes_to = /datum/wound/slash/severe + status_effect_type = /datum/status_effect/wound/slash/critical + scar_keyword = "slashcritical" + wound_flags = (FLESH_WOUND | ACCEPTS_GAUZE | MANGLES_FLESH) diff --git a/code/game/gamemodes/clown_ops/clown_weapons.dm b/code/game/gamemodes/clown_ops/clown_weapons.dm index 8a821edffb8..ae55a648b30 100644 --- a/code/game/gamemodes/clown_ops/clown_weapons.dm +++ b/code/game/gamemodes/clown_ops/clown_weapons.dm @@ -64,7 +64,7 @@ hitsound = null attack_verb_on = list("slipped") clumsy_check = FALSE - sharpness = IS_BLUNT + sharpness = SHARP_NONE sword_color = "yellow" heat = 0 light_color = "#ffff00" diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index c3a7e06586b..505aec3eac2 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -351,7 +351,8 @@ L.visible_message("[src] closes on [L], crushing [L.p_them()]!", "[src] closes on you and crushes you!") if(iscarbon(L)) var/mob/living/carbon/C = L - for(var/datum/wound/W in C.all_wounds) + for(var/i in C.all_wounds) // should probably replace with signal + var/datum/wound/W = i W.crush(DOOR_CRUSH_DAMAGE) if(isalien(L)) //For xenos L.adjustBruteLoss(DOOR_CRUSH_DAMAGE * 1.5) //Xenos go into crit after aproximately the same amount of crushes as humans. diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 996ab78ea54..808f08d281d 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -139,13 +139,13 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb mouse_drag_pointer = MOUSE_ACTIVE_POINTER ///Does it embed and if yes, what kind of embed - var/list/embedding = NONE + var/list/embedding ///for flags such as [GLASSESCOVERSEYES] var/flags_cover = 0 var/heat = 0 - ///All items with sharpness of IS_SHARP or higher will automatically get the butchering component. - var/sharpness = IS_BLUNT + ///All items with sharpness of SHARP_EDGED or higher will automatically get the butchering component. + var/sharpness = SHARP_NONE ///How a tool acts when you use it on something, such as wirecutters cutting wires while multitools measure power var/tool_behaviour = NONE @@ -241,7 +241,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb if(GLOB.rpg_loot_items) AddComponent(/datum/component/fantasy) - if(sharpness) //give sharp objects butchering functionality, for consistency + if(sharpness && force > 5) //give sharp objects butchering functionality, for consistency AddComponent(/datum/component/butchering, 80 * toolspeed) /**Makes cool stuff happen when you suicide with an item @@ -720,11 +720,6 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb /obj/item/proc/get_sharpness() return sharpness -/obj/item/proc/get_dismemberment_chance(obj/item/bodypart/affecting) - if(affecting.can_dismember(src)) - if((sharpness || damtype == BURN) && w_class >= WEIGHT_CLASS_NORMAL && force >= 10) - . = force * (affecting.get_damage() / affecting.max_damage) - /obj/item/proc/get_dismember_sound() if(damtype == BURN) . = 'sound/weapons/sear.ogg' @@ -995,7 +990,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb ///For when you want to add/update the embedding on an item. Uses the vars in [/obj/item/embedding], and defaults to config values for values that aren't set. Will automatically detach previous embed elements on this item. /obj/item/proc/updateEmbedding() - if(!islist(embedding) || !LAZYLEN(embedding)) + if(!LAZYLEN(embedding)) return AddElement(/datum/element/embed,\ diff --git a/code/game/objects/items/chainsaw.dm b/code/game/objects/items/chainsaw.dm index 6c597000545..42986bef5fb 100644 --- a/code/game/objects/items/chainsaw.dm +++ b/code/game/objects/items/chainsaw.dm @@ -16,7 +16,7 @@ custom_materials = list(/datum/material/iron=13000) attack_verb = list("sawed", "tore", "lacerated", "cut", "chopped", "diced") hitsound = "swing_hit" - sharpness = IS_SHARP + sharpness = SHARP_EDGED actions_types = list(/datum/action/item_action/startchainsaw) tool_behaviour = TOOL_SAW toolspeed = 0.5 @@ -73,10 +73,6 @@ var/datum/action/A = X A.UpdateButtonIcon() -/obj/item/chainsaw/get_dismemberment_chance() - if(wielded) - . = ..() - /obj/item/chainsaw/doomslayer name = "THE GREAT COMMUNICATOR" desc = "VRRRRRRR!!!" diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index 69bc7154e3b..797168179e3 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -186,8 +186,20 @@ GENE SCANNER render_list += "Subject appears to have [M.getCloneLoss() > 30 ? "Severe" : "Minor"] cellular damage.\n" if(advanced) render_list += "Cellular Damage Level: [M.getCloneLoss()].\n" - if (!M.getorgan(/obj/item/organ/brain)) + if (!M.getorganslot(ORGAN_SLOT_BRAIN)) // brain not added to carbon/human check because it's funny to get to bully simple mobs render_list += "Subject lacks a brain.\n" + if(ishuman(M)) + var/mob/living/carbon/human/the_dude = M + var/datum/species/the_dudes_species = the_dude.dna.species + if (!(NOBLOOD in the_dudes_species.species_traits) && !the_dude.getorganslot(ORGAN_SLOT_HEART)) + render_list += "Subject lacks a heart.\n" + if (!(TRAIT_NOBREATH in the_dudes_species.species_traits) && !the_dude.getorganslot(ORGAN_SLOT_LUNGS)) + render_list += "Subject lacks lungs.\n" + if (!(TRAIT_NOMETABOLISM in the_dudes_species.species_traits) && !the_dude.getorganslot(ORGAN_SLOT_LIVER)) + render_list += "Subject lacks a liver.\n" + if (!(NOSTOMACH in the_dudes_species.species_traits) && !the_dude.getorganslot(ORGAN_SLOT_STOMACH)) + render_list += "Subject lacks a stomach.\n" + if(iscarbon(M)) var/mob/living/carbon/C = M if(LAZYLEN(C.get_traumas())) diff --git a/code/game/objects/items/dualsaber.dm b/code/game/objects/items/dualsaber.dm index b8689c2faca..5e5b2f31af3 100644 --- a/code/game/objects/items/dualsaber.dm +++ b/code/game/objects/items/dualsaber.dm @@ -43,7 +43,7 @@ to_chat(user, "You lack the grace to wield this!") return COMPONENT_TWOHANDED_BLOCK_WIELD wielded = TRUE - sharpness = IS_SHARP + sharpness = SHARP_EDGED w_class = w_class_on hitsound = 'sound/weapons/blade1.ogg' START_PROCESSING(SSobj, src) diff --git a/code/game/objects/items/fireaxe.dm b/code/game/objects/items/fireaxe.dm index 792568bc9ec..ba47631e118 100644 --- a/code/game/objects/items/fireaxe.dm +++ b/code/game/objects/items/fireaxe.dm @@ -13,11 +13,12 @@ slot_flags = ITEM_SLOT_BACK attack_verb = list("attacked", "chopped", "cleaved", "tore", "lacerated", "cut") hitsound = 'sound/weapons/bladeslice.ogg' - sharpness = IS_SHARP + sharpness = SHARP_EDGED max_integrity = 200 armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 30) resistance_flags = FIRE_PROOF - wound_bonus = -20 + wound_bonus = -15 + bare_wound_bonus = 20 var/wielded = FALSE // track wielded status on item /obj/item/fireaxe/Initialize() diff --git a/code/game/objects/items/holy_weapons.dm b/code/game/objects/items/holy_weapons.dm index 19857ce675d..7485239e71f 100644 --- a/code/game/objects/items/holy_weapons.dm +++ b/code/game/objects/items/holy_weapons.dm @@ -316,7 +316,7 @@ w_class = WEIGHT_CLASS_HUGE slot_flags = ITEM_SLOT_BACK|ITEM_SLOT_BELT block_chance = 30 - sharpness = IS_SHARP + sharpness = SHARP_EDGED hitsound = 'sound/weapons/bladeslice.ogg' attack_verb = list("attacked", "slashed", "stabbed", "sliced", "tore", "lacerated", "ripped", "diced", "cut") @@ -418,7 +418,7 @@ w_class = WEIGHT_CLASS_BULKY armour_penetration = 35 slot_flags = ITEM_SLOT_BACK - sharpness = IS_SHARP + sharpness = SHARP_EDGED attack_verb = list("chopped", "sliced", "cut", "reaped") /obj/item/nullrod/scythe/Initialize() @@ -526,7 +526,7 @@ righthand_file = 'icons/mob/inhands/weapons/chainsaw_righthand.dmi' w_class = WEIGHT_CLASS_HUGE item_flags = ABSTRACT - sharpness = IS_SHARP + sharpness = SHARP_EDGED attack_verb = list("sawed", "tore", "lacerated", "cut", "chopped", "diced") hitsound = 'sound/weapons/chainsawhit.ogg' tool_behaviour = TOOL_SAW @@ -544,7 +544,7 @@ name = "clown dagger" desc = "Used for absolutely hilarious sacrifices." hitsound = 'sound/items/bikehorn.ogg' - sharpness = IS_SHARP + sharpness = SHARP_EDGED attack_verb = list("attacked", "slashed", "stabbed", "sliced", "tore", "lacerated", "ripped", "diced", "cut") /obj/item/nullrod/pride_hammer @@ -590,7 +590,7 @@ throw_speed = 4 throw_range = 7 throwforce = 30 - sharpness = IS_SHARP + sharpness = SHARP_EDGED attack_verb = list("enlightened", "redpilled") /obj/item/nullrod/armblade @@ -603,7 +603,7 @@ righthand_file = 'icons/mob/inhands/antag/changeling_righthand.dmi' item_flags = ABSTRACT w_class = WEIGHT_CLASS_HUGE - sharpness = IS_SHARP + sharpness = SHARP_EDGED wound_bonus = -20 bare_wound_bonus = 25 @@ -644,7 +644,7 @@ force = 15 block_chance = 40 slot_flags = ITEM_SLOT_BACK - sharpness = IS_BLUNT + sharpness = SHARP_NONE hitsound = "swing_hit" attack_verb = list("smashed", "slammed", "whacked", "thwacked") icon = 'icons/obj/items_and_weapons.dmi' @@ -662,7 +662,7 @@ name = "arrhythmic knife" w_class = WEIGHT_CLASS_HUGE desc = "They say fear is the true mind killer, but stabbing them in the head works too. Honour compels you to not sheathe it once drawn." - sharpness = IS_SHARP + sharpness = SHARP_EDGED slot_flags = null hitsound = 'sound/weapons/bladeslice.ogg' attack_verb = list("attacked", "slashed", "stabbed", "sliced", "tore", "lacerated", "ripped", "diced", "cut") @@ -693,7 +693,7 @@ desc = "Holding this makes you look absolutely devilish." attack_verb = list("poked", "impaled", "pierced", "jabbed") hitsound = 'sound/weapons/bladeslice.ogg' - sharpness = IS_SHARP + sharpness = SHARP_EDGED /obj/item/nullrod/egyptian name = "egyptian staff" @@ -730,7 +730,7 @@ icon = 'icons/obj/clockwork_objects.dmi' slot_flags = ITEM_SLOT_BELT armour_penetration = 10 - sharpness = IS_SHARP_ACCURATE + sharpness = SHARP_POINTY w_class = WEIGHT_CLASS_BULKY attack_verb = list("stabbed", "poked", "slashed", "clocked") hitsound = 'sound/weapons/bladeslice.ogg' diff --git a/code/game/objects/items/kitchen.dm b/code/game/objects/items/kitchen.dm index 1d849b6c887..598576a208f 100644 --- a/code/game/objects/items/kitchen.dm +++ b/code/game/objects/items/kitchen.dm @@ -19,7 +19,7 @@ name = "fork" desc = "Pointy." icon_state = "fork" - force = 5 + force = 4 w_class = WEIGHT_CLASS_TINY throwforce = 0 throw_speed = 3 @@ -30,6 +30,7 @@ hitsound = 'sound/weapons/bladeslice.ogg' armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 30) item_flags = EYE_STAB + sharpness = SHARP_POINTY var/datum/reagent/forkload //used to eat omelette /obj/item/kitchen/fork/suicide_act(mob/living/carbon/user) @@ -84,7 +85,7 @@ throw_range = 6 custom_materials = list(/datum/material/iron=12000) attack_verb = list("slashed", "stabbed", "sliced", "tore", "lacerated", "ripped", "diced", "cut") - sharpness = IS_SHARP_ACCURATE + sharpness = SHARP_EDGED armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) item_flags = EYE_STAB var/bayonet = FALSE //Can this be attached to a gun? @@ -117,7 +118,7 @@ throw_range = 5 custom_materials = list(/datum/material/plastic = 100) attack_verb = list("prodded", "whiffed","scratched", "poked") - sharpness = IS_SHARP + sharpness = SHARP_EDGED custom_price = 50 var/break_chance = 25 diff --git a/code/game/objects/items/latexballoon.dm b/code/game/objects/items/latexballoon.dm index 4951712547d..91c9677ff29 100644 --- a/code/game/objects/items/latexballoon.dm +++ b/code/game/objects/items/latexballoon.dm @@ -54,5 +54,5 @@ var/obj/item/tank/T = W blow(T, user) return - if (W.get_sharpness() || W.get_temperature() || is_pointed(W)) + if (W.get_sharpness() || W.get_temperature()) burst() diff --git a/code/game/objects/items/melee/energy.dm b/code/game/objects/items/melee/energy.dm index a25a7d607b1..bbdccc18ba1 100644 --- a/code/game/objects/items/melee/energy.dm +++ b/code/game/objects/items/melee/energy.dm @@ -98,7 +98,7 @@ attack_verb_off = list("tapped", "poked") throw_speed = 3 throw_range = 5 - sharpness = IS_SHARP + sharpness = SHARP_EDGED embedding = list("embed_chance" = 75, "impact_pain_mult" = 10) armour_penetration = 35 block_chance = 50 @@ -138,7 +138,7 @@ sword_color = null //stops icon from breaking when turned on. hitcost = 75 //Costs more than a standard cyborg esword w_class = WEIGHT_CLASS_NORMAL - sharpness = IS_SHARP + sharpness = SHARP_EDGED light_color = "#40ceff" tool_behaviour = TOOL_SAW toolspeed = 0.7 //faster as a saw @@ -219,7 +219,7 @@ throw_range = 1 w_class = WEIGHT_CLASS_BULKY//So you can't hide it in your pocket or some such. var/datum/effect_system/spark_spread/spark_system - sharpness = IS_SHARP + sharpness = SHARP_EDGED //Most of the other special functions are handled in their own files. aka special snowflake code so kewl /obj/item/melee/transforming/energy/blade/Initialize() diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm index d3dc42f8935..0e7486c705f 100644 --- a/code/game/objects/items/melee/misc.dm +++ b/code/game/objects/items/melee/misc.dm @@ -44,7 +44,7 @@ throwforce = 10 hitsound = 'sound/weapons/bladeslice.ogg' attack_verb = list("attacked", "slashed", "stabbed", "sliced", "tore", "lacerated", "ripped", "diced", "cut") - sharpness = IS_SHARP + sharpness = SHARP_EDGED /obj/item/melee/synthetic_arm_blade/Initialize() . = ..() @@ -64,10 +64,12 @@ w_class = WEIGHT_CLASS_BULKY block_chance = 50 armour_penetration = 75 - sharpness = IS_SHARP + sharpness = SHARP_EDGED attack_verb = list("slashed", "cut") hitsound = 'sound/weapons/rapierhit.ogg' custom_materials = list(/datum/material/iron = 1000) + wound_bonus = 10 + bare_wound_bonus = 25 /obj/item/melee/sabre/Initialize() . = ..() @@ -143,7 +145,7 @@ slot_flags = ITEM_SLOT_BELT force = 5 w_class = WEIGHT_CLASS_BULKY - sharpness = IS_SHARP + sharpness = SHARP_EDGED throwforce = 10 block_chance = 20 armour_penetration = 65 diff --git a/code/game/objects/items/melee/transforming.dm b/code/game/objects/items/melee/transforming.dm index bfed019f671..c6fe6c4eda4 100644 --- a/code/game/objects/items/melee/transforming.dm +++ b/code/game/objects/items/melee/transforming.dm @@ -1,5 +1,5 @@ /obj/item/melee/transforming - sharpness = IS_SHARP + sharpness = SHARP_EDGED var/active = FALSE var/force_on = 30 //force when active var/faction_bonus_force = 0 //Bonus force dealt against certain factions diff --git a/code/game/objects/items/pitchfork.dm b/code/game/objects/items/pitchfork.dm index d006fa7cab2..2a56df47fe4 100644 --- a/code/game/objects/items/pitchfork.dm +++ b/code/game/objects/items/pitchfork.dm @@ -9,7 +9,7 @@ w_class = WEIGHT_CLASS_BULKY attack_verb = list("attacked", "impaled", "pierced") hitsound = 'sound/weapons/bladeslice.ogg' - sharpness = IS_SHARP + sharpness = SHARP_EDGED max_integrity = 200 armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 30) resistance_flags = FIRE_PROOF diff --git a/code/game/objects/items/sharpener.dm b/code/game/objects/items/sharpener.dm index 62bc2a0b4b7..01593f81589 100644 --- a/code/game/objects/items/sharpener.dm +++ b/code/game/objects/items/sharpener.dm @@ -39,7 +39,7 @@ I.force = clamp(I.force + increment, 0, max) user.visible_message("[user] sharpens [I] with [src]!", "You sharpen [I], making it much more deadly than before.") playsound(src, 'sound/items/unsheath.ogg', 25, TRUE) - I.sharpness = IS_SHARP_ACCURATE + I.sharpness = SHARP_POINTY I.throwforce = clamp(I.throwforce + increment, 0, max) I.name = "[prefix] [I.name]" name = "worn out [name]" diff --git a/code/game/objects/items/shrapnel.dm b/code/game/objects/items/shrapnel.dm index 91df52a6ea5..2b1adaab6dd 100644 --- a/code/game/objects/items/shrapnel.dm +++ b/code/game/objects/items/shrapnel.dm @@ -7,47 +7,41 @@ icon_state = "large" w_class = WEIGHT_CLASS_TINY item_flags = DROPDEL - sharpness = TRUE + sharpness = SHARP_EDGED /obj/item/shrapnel/stingball // stingbang grenades name = "stingball" embedding = list(embed_chance=90, fall_chance=3, jostle_chance=7, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.7, pain_mult=5, jostle_pain_mult=6, rip_time=15) icon_state = "tiny" - sharpness = FALSE + sharpness = SHARP_NONE /obj/item/shrapnel/bullet // bullets name = "bullet" icon = 'icons/obj/ammo.dmi' icon_state = "s-casing" - item_flags = NONE - -/obj/item/shrapnel/bullet/c38 // .38 round - name = "\improper .38 bullet" - -/obj/item/shrapnel/bullet/c38/dumdum // .38 DumDum round - name = "\improper .38 DumDum bullet" - embedding = list(embed_chance=70, fall_chance=7, jostle_chance=7, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.4, pain_mult=5, jostle_pain_mult=6, rip_time=10) + embedding = null // embedding vars are taken from the projectile itself /obj/projectile/bullet/shrapnel name = "flying shrapnel shard" - damage = 8 - range = 10 + damage = 14 + range = 20 armour_penetration = -30 dismemberment = 5 ricochets_max = 2 - ricochet_chance = 40 + ricochet_chance = 70 shrapnel_type = /obj/item/shrapnel ricochet_incidence_leeway = 60 hit_stunned_targets = TRUE - sharpness = TRUE - wound_bonus = 30 + sharpness = SHARP_EDGED + wound_bonus = 40 /obj/projectile/bullet/shrapnel/mega name = "flying shrapnel hunk" - range = 25 - dismemberment = 10 - ricochets_max = 4 - ricochet_chance = 90 + range = 45 + dismemberment = 15 + ricochets_max = 6 + ricochet_chance = 130 + ricochet_incidence_leeway = 0 ricochet_decay_chance = 0.9 /obj/projectile/bullet/pellet/stingball @@ -72,13 +66,13 @@ name = "breakbang pellet" damage = 10 wound_bonus = 40 - sharpness = FALSE + sharpness = SHARP_NONE /obj/projectile/bullet/pellet/stingball/shred name = "shredbang pellet" damage = 10 wound_bonus = 30 - sharpness = TRUE + sharpness = SHARP_EDGED /obj/projectile/bullet/pellet/stingball/on_ricochet(atom/A) hit_stunned_targets = TRUE // ducking will save you from the first wave, but not the rebounds diff --git a/code/game/objects/items/spear.dm b/code/game/objects/items/spear.dm index 0c54185ae40..cf1ed2fa6dc 100644 --- a/code/game/objects/items/spear.dm +++ b/code/game/objects/items/spear.dm @@ -15,12 +15,12 @@ custom_materials = list(/datum/material/iron=1150, /datum/material/glass=2075) hitsound = 'sound/weapons/bladeslice.ogg' attack_verb = list("attacked", "poked", "jabbed", "tore", "lacerated", "gored") - sharpness = IS_SHARP + sharpness = SHARP_EDGED // i know the whole point of spears is that they're pointy, but edged is more devastating at the moment so max_integrity = 200 armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 30) var/war_cry = "AAAAARGH!!!" var/icon_prefix = "spearglass" - wound_bonus = -25 + wound_bonus = -15 bare_wound_bonus = 15 /obj/item/spear/ComponentInitialize() diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm index dadb5ada594..ebfd5b8b3a7 100644 --- a/code/game/objects/items/stacks/medical.dm +++ b/code/game/objects/items/stacks/medical.dm @@ -124,11 +124,34 @@ // gauze is only relevant for wounds, which are handled in the wounds themselves /obj/item/stack/medical/gauze/try_heal(mob/living/M, mob/user, silent) var/obj/item/bodypart/limb = M.get_bodypart(check_zone(user.zone_selected)) - if(limb) - if(limb.brute_dam > 40) - to_chat(user, "The bleeding on [user==M ? "your" : "[M]'s"] [limb.name] is from bruising, and cannot be treated with [src]!") - else - to_chat(user, "There's no bleeding on [user==M ? "your" : "[M]'s"] [limb.name]") + if(!limb) + to_chat(user, "There's nothing there to bandage!") + return + if(!LAZYLEN(limb.wounds)) + to_chat(user, "There's no wounds that require bandaging on [user==M ? "your" : "[M]'s"] [limb.name]!") // good problem to have imo + return + + var/gauzeable_wound = FALSE + for(var/i in limb.wounds) + var/datum/wound/woundies = i + if(woundies.wound_flags & ACCEPTS_GAUZE) + gauzeable_wound = TRUE + break + if(!gauzeable_wound) + to_chat(user, "There's no wounds that require bandaging on [user==M ? "your" : "[M]'s"] [limb.name]!") // good problem to have imo + return + + if(limb.current_gauze && (limb.current_gauze.absorption_capacity * 0.8 > absorption_capacity)) // ignore if our new wrap is < 20% better than the current one, so someone doesn't bandage it 5 times in a row + to_chat(user, "The bandage currently on [user==M ? "your" : "[M]'s"] [limb.name] is still in good condition!") + return + + user.visible_message("[user] begins wrapping the wounds on [M]'s [limb.name] with [src]...", "You begin wrapping the wounds on [user == M ? "your" : "[M]'s"] [limb.name] with [src]...") + + if(!do_after(user, (user == M ? self_delay : other_delay), target=M)) + return + + user.visible_message("[user] applies [src] to [M]'s [limb.name].", "You bandage the wounds on [user == M ? "yourself" : "[M]'s"] [limb.name].") + limb.apply_gauze(src) /obj/item/stack/medical/gauze/twelve amount = 12 @@ -399,9 +422,9 @@ C.emote("scream") for(var/i in C.bodyparts) var/obj/item/bodypart/bone = i - var/datum/wound/brute/bone/severe/oof_ouch = new + var/datum/wound/blunt/severe/oof_ouch = new oof_ouch.apply_wound(bone) - var/datum/wound/brute/bone/critical/oof_OUCH = new + var/datum/wound/blunt/critical/oof_OUCH = new oof_OUCH.apply_wound(bone) for(var/i in C.bodyparts) diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm index facca0f589a..16419573993 100644 --- a/code/game/objects/items/stacks/sheets/glass.dm +++ b/code/game/objects/items/stacks/sheets/glass.dm @@ -250,7 +250,7 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list( resistance_flags = ACID_PROOF armor = list("melee" = 100, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 100) max_integrity = 40 - sharpness = IS_SHARP + sharpness = SHARP_EDGED var/icon_prefix var/obj/item/stack/sheet/weld_material = /obj/item/stack/sheet/glass embedding = list("embed_chance" = 65) diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm index a5f39ce3522..5cbbb748f42 100644 --- a/code/game/objects/items/storage/belt.dm +++ b/code/game/objects/items/storage/belt.dm @@ -195,7 +195,7 @@ new /obj/item/reagent_containers/syringe(src) new /obj/item/stack/medical/bone_gel(src) new /obj/item/stack/sticky_tape/surgical(src) - new /obj/item/reagent_containers/glass/bottle/calomel(src) + new /obj/item/reagent_containers/glass/bottle/formaldehyde(src) update_icon() /obj/item/storage/belt/security diff --git a/code/game/objects/items/tools/screwdriver.dm b/code/game/objects/items/tools/screwdriver.dm index 59108d05c6e..b306cd229aa 100644 --- a/code/game/objects/items/tools/screwdriver.dm +++ b/code/game/objects/items/tools/screwdriver.dm @@ -24,6 +24,7 @@ drop_sound = 'sound/items/handling/screwdriver_drop.ogg' pickup_sound = 'sound/items/handling/screwdriver_pickup.ogg' item_flags = EYE_STAB + sharpness = SHARP_POINTY var/random_color = TRUE //if the screwdriver uses random coloring var/static/list/screwdriver_colors = list( "blue" = rgb(24, 97, 213), diff --git a/code/game/objects/items/tools/weldingtool.dm b/code/game/objects/items/tools/weldingtool.dm index 2c7d2664c94..623c1caacc9 100644 --- a/code/game/objects/items/tools/weldingtool.dm +++ b/code/game/objects/items/tools/weldingtool.dm @@ -35,6 +35,8 @@ heat = 3800 tool_behaviour = TOOL_WELDER toolspeed = 1 + wound_bonus = 10 + bare_wound_bonus = 15 /obj/item/weldingtool/Initialize() . = ..() diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm index cfb86786baf..ada02101cfa 100644 --- a/code/game/objects/items/weaponry.dm +++ b/code/game/objects/items/weaponry.dm @@ -65,7 +65,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 w_class = WEIGHT_CLASS_NORMAL attack_verb = list("attacked", "slashed", "stabbed", "sliced", "tore", "lacerated", "ripped", "diced", "cut") block_chance = 50 - sharpness = IS_SHARP + sharpness = SHARP_EDGED max_integrity = 200 armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50) resistance_flags = FIRE_PROOF @@ -219,7 +219,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 hitsound = 'sound/weapons/bladeslice.ogg' attack_verb = list("attacked", "slashed", "stabbed", "sliced", "tore", "lacerated", "ripped", "diced", "cut") block_chance = 50 - sharpness = IS_SHARP + sharpness = SHARP_EDGED max_integrity = 200 armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50) resistance_flags = FIRE_PROOF @@ -290,13 +290,13 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 lefthand_file = 'icons/mob/inhands/equipment/shields_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/shields_righthand.dmi' force = 2 - throwforce = 20 //20 + 2 (WEIGHT_CLASS_SMALL) * 4 (EMBEDDED_IMPACT_PAIN_MULTIPLIER) = 28 damage on hit due to guaranteed embedding + throwforce = 10 //10 + 2 (WEIGHT_CLASS_SMALL) * 9 (EMBEDDED_IMPACT_PAIN_MULTIPLIER) = 28 damage on hit due to guaranteed embedding throw_speed = 4 embedding = list("pain_mult" = 4, "embed_chance" = 100, "fall_chance" = 0) armour_penetration = 40 w_class = WEIGHT_CLASS_SMALL - sharpness = IS_SHARP + sharpness = SHARP_POINTY custom_materials = list(/datum/material/iron=500, /datum/material/glass=500) resistance_flags = FIRE_PROOF @@ -309,7 +309,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 /obj/item/throwing_star/toy name = "toy throwing star" desc = "An aerodynamic disc strapped with adhesive for sticking to people, good for playing pranks and getting yourself killed by security." - sharpness = IS_BLUNT + sharpness = SHARP_NONE force = 0 throwforce = 0 embedding = list("pain_mult" = 0, "jostle_pain_mult" = 0, "embed_chance" = 100, "fall_chance" = 0) @@ -353,7 +353,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 icon_state = "switchblade_ext" attack_verb = list("slashed", "stabbed", "sliced", "tore", "lacerated", "ripped", "diced", "cut") hitsound = 'sound/weapons/bladeslice.ogg' - sharpness = IS_SHARP + sharpness = SHARP_EDGED else force = 3 w_class = WEIGHT_CLASS_SMALL @@ -361,7 +361,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 icon_state = "switchblade" attack_verb = list("stubbed", "poked") hitsound = 'sound/weapons/genhit.ogg' - sharpness = IS_BLUNT + sharpness = SHARP_NONE /obj/item/switchblade/suicide_act(mob/user) user.visible_message("[user] is slitting [user.p_their()] own throat with [src]! It looks like [user.p_theyre()] trying to commit suicide!") @@ -466,7 +466,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 throwforce = 0 throw_range = 0 throw_speed = 0 - sharpness = IS_SHARP + sharpness = SHARP_EDGED attack_verb = list("sawed", "tore", "lacerated", "cut", "chopped", "diced") hitsound = 'sound/weapons/chainsawhit.ogg' tool_behaviour = TOOL_SAW @@ -855,7 +855,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 force = 20 throwforce = 20 throw_speed = 4 - sharpness = IS_SHARP + sharpness = SHARP_EDGED attack_verb = list("cut", "sliced", "diced") w_class = WEIGHT_CLASS_BULKY slot_flags = ITEM_SLOT_BACK diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index ef628ba299d..05953a5b959 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -1069,6 +1069,7 @@ Traitors and the like can also be revived with the previous role mostly intact. ADMIN_PUNISHMENT_NUGGET, ADMIN_PUNISHMENT_CRACK, ADMIN_PUNISHMENT_BLEED, + ADMIN_PUNISHMENT_PERFORATE, ADMIN_PUNISHMENT_SCARIFY, ADMIN_PUNISHMENT_SHOES ) @@ -1159,41 +1160,112 @@ Traitors and the like can also be revived with the previous role mostly intact. to_chat(usr,"This must be used on a carbon mob.", confidential = TRUE) return var/mob/living/carbon/C = target - for(var/obj/item/bodypart/squish_part in C.bodyparts) - var/type_wound = pick(list(/datum/wound/brute/bone/critical, /datum/wound/brute/bone/severe, /datum/wound/brute/bone/critical, /datum/wound/brute/bone/severe, /datum/wound/brute/bone/moderate)) + for(var/i in C.bodyparts) + var/obj/item/bodypart/squish_part = i + var/type_wound = pick(list(/datum/wound/blunt/critical, /datum/wound/blunt/severe, /datum/wound/blunt/critical, /datum/wound/blunt/severe, /datum/wound/blunt/moderate)) squish_part.force_wound_upwards(type_wound, smited=TRUE) if(ADMIN_PUNISHMENT_BLEED) if(!iscarbon(target)) to_chat(usr,"This must be used on a carbon mob.", confidential = TRUE) return var/mob/living/carbon/C = target - for(var/obj/item/bodypart/slice_part in C.bodyparts) - var/type_wound = pick(list(/datum/wound/brute/cut/severe, /datum/wound/brute/cut/moderate)) + for(var/i in C.bodyparts) + var/obj/item/bodypart/slice_part = i + var/type_wound = pick(list(/datum/wound/slash/severe, /datum/wound/slash/moderate)) slice_part.force_wound_upwards(type_wound, smited=TRUE) - type_wound = pick(list(/datum/wound/brute/cut/critical, /datum/wound/brute/cut/severe, /datum/wound/brute/cut/moderate)) + type_wound = pick(list(/datum/wound/slash/critical, /datum/wound/slash/severe, /datum/wound/slash/moderate)) slice_part.force_wound_upwards(type_wound, smited=TRUE) - type_wound = pick(list(/datum/wound/brute/cut/critical, /datum/wound/brute/cut/severe)) + type_wound = pick(list(/datum/wound/slash/critical, /datum/wound/slash/severe)) slice_part.force_wound_upwards(type_wound, smited=TRUE) + if(ADMIN_PUNISHMENT_PERFORATE) + if(!iscarbon(target)) + to_chat(usr,"This must be used on a carbon mob.", confidential = TRUE) + return + + var/list/how_fucked_is_this_dude = list("A little", "A lot", "So fucking much", "FUCK THIS DUDE") + var/hatred = input("How much do you hate this guy?") in how_fucked_is_this_dude + var/repetitions + var/shots_per_limb_per_rep = 2 + var/damage + switch(hatred) + if("A little") + repetitions = 1 + damage = 5 + if("A lot") + repetitions = 2 + damage = 8 + if("So fucking much") + repetitions = 3 + damage = 10 + if("FUCK THIS DUDE") + repetitions = 4 + damage = 10 + + var/mob/living/carbon/dude = target + var/list/open_adj_turfs = get_adjacent_open_turfs(dude) + var/list/wound_bonuses = list(15, 70, 110, 250) + + var/delay_per_shot = 1 + var/delay_counter = 1 + + dude.Immobilize(5 SECONDS) + for(var/wound_bonus_rep in 1 to repetitions) + for(var/i in dude.bodyparts) + var/obj/item/bodypart/slice_part = i + var/shots_this_limb = 0 + for(var/t in shuffle(open_adj_turfs)) + var/turf/iter_turf = t + addtimer(CALLBACK(GLOBAL_PROC, .proc/firing_squad, dude, iter_turf, slice_part.body_zone, wound_bonuses[wound_bonus_rep], damage), delay_counter) + delay_counter += delay_per_shot + shots_this_limb++ + if(shots_this_limb > shots_per_limb_per_rep) + break + if(ADMIN_PUNISHMENT_SCARIFY) if(!iscarbon(target)) to_chat(usr,"This must be used on a carbon mob.", confidential = TRUE) return - var/mob/living/carbon/C = target - C.generate_fake_scars(rand(1, 4)) - to_chat(C, "You feel your body grow jaded and torn...") + var/mob/living/carbon/dude = target + dude.generate_fake_scars(rand(1, 4)) + to_chat(dude, "You feel your body grow jaded and torn...") if(ADMIN_PUNISHMENT_SHOES) if(!iscarbon(target)) to_chat(usr,"This must be used on a carbon mob.", confidential = TRUE) return - var/mob/living/carbon/C = target - var/obj/item/clothing/shoes/sick_kicks = C.shoes + var/mob/living/carbon/dude = target + var/obj/item/clothing/shoes/sick_kicks = dude.shoes if(!sick_kicks?.can_be_tied) - to_chat(usr,"[C] does not have knottable shoes!", confidential = TRUE) + to_chat(usr,"[dude] does not have knottable shoes!", confidential = TRUE) return sick_kicks.adjust_laces(SHOES_KNOTTED) punish_log(target, punishment) +/** + * firing_squad is a proc for the :B:erforate smite to shoot each individual bullet at them, so that we can add actual delays without sleep() nonsense + * + * Hilariously, if you drag someone away mid smite, the bullets will still chase after them from the original spot, possibly hitting other people. Too funny to fix imo + * + * Arguments: + * * target- guy we're shooting obviously + * * source_turf- where the bullet begins, preferably on a turf next to the target + * * body_zone- which bodypart we're aiming for, if there is one there + * * wound_bonus- the wounding power we're assigning to the bullet, since we don't care about the base one + * * damage- the damage we're assigning to the bullet, since we don't care about the base one + */ +/proc/firing_squad(mob/living/carbon/target, turf/source_turf, body_zone, wound_bonus, damage) + if(!target.get_bodypart(body_zone)) + return + playsound(target, 'sound/weapons/gun/revolver/shot.ogg', 100) + var/obj/projectile/bullet/smite/divine_wrath = new(source_turf) + divine_wrath.damage = damage + divine_wrath.wound_bonus = wound_bonus + divine_wrath.original = target + divine_wrath.def_zone = body_zone + divine_wrath.spread = 0 + divine_wrath.preparePixelProjectile(target, source_turf) + divine_wrath.fire() + /client/proc/punish_log(whom, punishment) var/msg = "[key_name_admin(usr)] punished [key_name_admin(whom)] with [punishment]." message_admins(msg) diff --git a/code/modules/antagonists/changeling/powers/mutations.dm b/code/modules/antagonists/changeling/powers/mutations.dm index 93a8dec8d6c..290660e08d1 100644 --- a/code/modules/antagonists/changeling/powers/mutations.dm +++ b/code/modules/antagonists/changeling/powers/mutations.dm @@ -161,7 +161,7 @@ throw_speed = 0 hitsound = 'sound/weapons/bladeslice.ogg' attack_verb = list("attacked", "slashed", "stabbed", "sliced", "tore", "lacerated", "ripped", "diced", "cut") - sharpness = IS_SHARP + sharpness = SHARP_EDGED wound_bonus = -60 bare_wound_bonus = 20 var/can_drop = FALSE diff --git a/code/modules/antagonists/changeling/powers/regenerate.dm b/code/modules/antagonists/changeling/powers/regenerate.dm index d65d1181bed..a060cda7f8e 100644 --- a/code/modules/antagonists/changeling/powers/regenerate.dm +++ b/code/modules/antagonists/changeling/powers/regenerate.dm @@ -35,8 +35,8 @@ B.Insert(C) C.regenerate_organs() for(var/i in C.all_wounds) - var/datum/wound/W = i - W.remove_wound() + var/datum/wound/iter_wound = i + iter_wound.remove_wound() if(ishuman(user)) var/mob/living/carbon/human/H = user H.restore_blood() diff --git a/code/modules/antagonists/cult/cult_items.dm b/code/modules/antagonists/cult/cult_items.dm index af7a7493e28..7919fd125bd 100644 --- a/code/modules/antagonists/cult/cult_items.dm +++ b/code/modules/antagonists/cult/cult_items.dm @@ -39,7 +39,7 @@ lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' flags_1 = CONDUCT_1 - sharpness = IS_SHARP + sharpness = SHARP_EDGED w_class = WEIGHT_CLASS_BULKY force = 30 // whoever balanced this got beat in the head by a bible too many times good lord throwforce = 10 @@ -91,7 +91,7 @@ armour_penetration = 45 throw_speed = 1 throw_range = 3 - sharpness = IS_SHARP + sharpness = SHARP_EDGED light_color = "#ff0000" attack_verb = list("cleaved", "slashed", "tore", "lacerated", "hacked", "ripped", "diced", "carved") icon_state = "cultbastard" @@ -627,7 +627,7 @@ armour_penetration = 30 block_chance = 30 attack_verb = list("attacked", "impaled", "stabbed", "tore", "lacerated", "gored") - sharpness = IS_SHARP + sharpness = SHARP_EDGED hitsound = 'sound/weapons/bladeslice.ogg' var/datum/action/innate/cult/spear/spear_act var/wielded = FALSE // track wielded status on item diff --git a/code/modules/antagonists/devil/true_devil/_true_devil.dm b/code/modules/antagonists/devil/true_devil/_true_devil.dm index d2383d7d7e0..1a725daba5b 100644 --- a/code/modules/antagonists/devil/true_devil/_true_devil.dm +++ b/code/modules/antagonists/devil/true_devil/_true_devil.dm @@ -123,7 +123,7 @@ var/weakness = check_weakness(I, user) apply_damage(I.force * weakness, I.damtype, def_zone) var/message_verb = "" - if(I.attack_verb && I.attack_verb.len) + if(I.attack_verb && length(I.attack_verb)) message_verb = "[pick(I.attack_verb)]" else if(I.force) message_verb = "attacked" diff --git a/code/modules/antagonists/eldritch_cult/eldritch_items.dm b/code/modules/antagonists/eldritch_cult/eldritch_items.dm index f450d9d6a90..fba5de445a1 100644 --- a/code/modules/antagonists/eldritch_cult/eldritch_items.dm +++ b/code/modules/antagonists/eldritch_cult/eldritch_items.dm @@ -41,7 +41,7 @@ inhand_x_dimension = 64 inhand_y_dimension = 64 flags_1 = CONDUCT_1 - sharpness = IS_SHARP + sharpness = SHARP_EDGED w_class = WEIGHT_CLASS_NORMAL force = 17 throwforce = 10 diff --git a/code/modules/antagonists/eldritch_cult/eldritch_magic.dm b/code/modules/antagonists/eldritch_cult/eldritch_magic.dm index 15542de5b24..85e8cd67518 100644 --- a/code/modules/antagonists/eldritch_cult/eldritch_magic.dm +++ b/code/modules/antagonists/eldritch_cult/eldritch_magic.dm @@ -140,14 +140,15 @@ if(iscarbon(target)) var/mob/living/carbon/C1 = target for(var/obj/item/bodypart/bodypart in C2.bodyparts) - for(var/datum/wound/wound in bodypart.wounds) + for(var/i in bodypart.wounds) + var/datum/wound/iter_wound = i if(prob(50)) continue var/obj/item/bodypart/target_bodypart = locate(bodypart.type) in C1.bodyparts if(!target_bodypart) continue - wound.remove_wound() - wound.apply_wound(target_bodypart) + iter_wound.remove_wound() + iter_wound.apply_wound(target_bodypart) C1.blood_volume -= 20 if(C2.blood_volume < BLOOD_VOLUME_MAXIMUM) //we dont want to explode after all @@ -241,7 +242,7 @@ target.visible_message("[target]'s veins are shredded from within as an unholy blaze erupts from their blood!", \ "Your veins burst from within and unholy flame erupts from your blood!") var/obj/item/bodypart/bodypart = pick(target.bodyparts) - var/datum/wound/brute/cut/critical/crit_wound = new + var/datum/wound/slash/critical/crit_wound = new crit_wound.apply_wound(bodypart) target.adjustFireLoss(20) new /obj/effect/temp_visual/cleave(target.drop_location()) diff --git a/code/modules/antagonists/eldritch_cult/knowledge/flesh_lore.dm b/code/modules/antagonists/eldritch_cult/knowledge/flesh_lore.dm index 3d579853ba9..597054c4adc 100644 --- a/code/modules/antagonists/eldritch_cult/knowledge/flesh_lore.dm +++ b/code/modules/antagonists/eldritch_cult/knowledge/flesh_lore.dm @@ -86,7 +86,7 @@ if(iscarbon(target)) var/mob/living/carbon/carbon_target = target var/obj/item/bodypart/bodypart = pick(carbon_target.bodyparts) - var/datum/wound/brute/cut/severe/crit_wound = new + var/datum/wound/slash/severe/crit_wound = new crit_wound.apply_wound(bodypart) if(QDELETED(human_target) || human_target.stat != DEAD) @@ -157,7 +157,7 @@ if(iscarbon(target)) var/mob/living/carbon/carbon_target = target var/obj/item/bodypart/bodypart = pick(carbon_target.bodyparts) - var/datum/wound/brute/cut/severe/crit_wound = new + var/datum/wound/slash/severe/crit_wound = new crit_wound.apply_wound(bodypart) /datum/eldritch_knowledge/summon/raw_prophet diff --git a/code/modules/antagonists/slaughter/slaughter.dm b/code/modules/antagonists/slaughter/slaughter.dm index 13fa3793cde..b140c343c50 100644 --- a/code/modules/antagonists/slaughter/slaughter.dm +++ b/code/modules/antagonists/slaughter/slaughter.dm @@ -38,7 +38,7 @@ melee_damage_upper = 15 wound_bonus = -10 bare_wound_bonus = 0 - sharpness = TRUE + sharpness = SHARP_EDGED see_in_dark = 8 lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE var/playstyle_string = "You are a slaughter demon, a terrible creature from another realm. You have a single desire: To kill. \ diff --git a/code/modules/antagonists/wizard/equipment/artefact.dm b/code/modules/antagonists/wizard/equipment/artefact.dm index 585ec34c4a9..d2979a59c91 100644 --- a/code/modules/antagonists/wizard/equipment/artefact.dm +++ b/code/modules/antagonists/wizard/equipment/artefact.dm @@ -293,7 +293,7 @@ to_chat(target, "You suddenly feel very hot!") target.adjust_bodytemperature(50) GiveHint(target) - else if(is_pointed(I)) + else if(I.get_sharpness() == SHARP_POINTY) to_chat(target, "You feel a stabbing pain in [parse_zone(user.zone_selected)]!") target.Paralyze(40) GiveHint(target) diff --git a/code/modules/cargo/goodies.dm b/code/modules/cargo/goodies.dm index a942d20db69..c071cbb4753 100644 --- a/code/modules/cargo/goodies.dm +++ b/code/modules/cargo/goodies.dm @@ -129,3 +129,9 @@ desc = "The simple beach ball is one of Nanotrasen's most popular products. 'Why do we make beach balls? Because we can! (TM)' - Nanotrasen" cost = 200 contains = list(/obj/item/toy/beach_ball) + +/datum/supply_pack/goody/medipen_twopak + name = "Medipen Two-Pak" + desc = "Contains one standard epinephrine medipen and one standard emergency first-aid kit medipen. For when you want to prepare for the worst." + cost = 500 + contains = list(/obj/item/reagent_containers/hypospray/medipen, /obj/item/reagent_containers/hypospray/medipen/ekit) diff --git a/code/modules/cargo/packs.dm b/code/modules/cargo/packs.dm index 20140de0723..37a42049891 100644 --- a/code/modules/cargo/packs.dm +++ b/code/modules/cargo/packs.dm @@ -1160,6 +1160,21 @@ crate_name = "blood freezer" crate_type = /obj/structure/closet/crate/freezer +/datum/supply_pack/medical/medipen_variety + name = "Medipen Variety-Pak" + desc = "Contains eight different medipens in three different varieties, to assist in quickly treating seriously injured patients." + cost = 2000 + contains = list(/obj/item/reagent_containers/hypospray/medipen/, + /obj/item/reagent_containers/hypospray/medipen/, + /obj/item/reagent_containers/hypospray/medipen/ekit, + /obj/item/reagent_containers/hypospray/medipen/ekit, + /obj/item/reagent_containers/hypospray/medipen/ekit, + /obj/item/reagent_containers/hypospray/medipen/blood_loss, + /obj/item/reagent_containers/hypospray/medipen/blood_loss, + /obj/item/reagent_containers/hypospray/medipen/blood_loss +) + crate_name = "medipen crate" + /datum/supply_pack/medical/chemical name = "Chemical Starter Kit Crate" desc = "Contains thirteen different chemicals, for all the fun experiments you can make." diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index d1c3866bae2..6e71c8a2ec6 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -301,7 +301,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "
Backpack:
[backpack]" dat += "[(randomise[RANDOM_BACKPACK]) ? "Lock" : "Unlock"]" - if(CAN_SCAR in pref_species.species_traits) + if((HAS_FLESH in pref_species.species_traits) || (HAS_BONE in pref_species.species_traits)) dat += "
Temporal Scarring:
[(persistent_scars) ? "Enabled" : "Disabled"]" dat += "Clear scar slots" diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index 6b8f78f823c..5d76d91f57c 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -354,8 +354,8 @@ to_chat(M, "[src] start[p_s()] to fall apart!") //This mostly exists so subtypes can call appriopriate update icon calls on the wearer. -/obj/item/clothing/proc/update_clothes_damaged_state(damaged_state = CLOTHING_DAMAGED) - damaged_clothes = damaged_state +/obj/item/clothing/proc/update_clothes_damaged_state() + return /obj/item/clothing/update_overlays() . = ..() diff --git a/code/modules/clothing/glasses/_glasses.dm b/code/modules/clothing/glasses/_glasses.dm index 32a8d00c748..912a8b6f331 100644 --- a/code/modules/clothing/glasses/_glasses.dm +++ b/code/modules/clothing/glasses/_glasses.dm @@ -88,7 +88,7 @@ throw_speed = 4 attack_verb = list("sliced") hitsound = 'sound/weapons/bladeslice.ogg' - sharpness = IS_SHARP + sharpness = SHARP_EDGED /obj/item/clothing/glasses/science name = "science goggles" @@ -156,7 +156,7 @@ throw_speed = 4 attack_verb = list("sliced") hitsound = 'sound/weapons/bladeslice.ogg' - sharpness = IS_SHARP + sharpness = SHARP_EDGED glass_colour_type = /datum/client_colour/glass_colour/lightgreen /obj/item/clothing/glasses/regular @@ -228,7 +228,7 @@ throw_speed = 4 attack_verb = list("sliced") hitsound = 'sound/weapons/bladeslice.ogg' - sharpness = IS_SHARP + sharpness = SHARP_EDGED /obj/item/clothing/glasses/sunglasses/garb/supergarb name = "black giga gar glasses" @@ -248,7 +248,7 @@ throw_speed = 4 attack_verb = list("sliced") hitsound = 'sound/weapons/bladeslice.ogg' - sharpness = IS_SHARP + sharpness = SHARP_EDGED glass_colour_type = /datum/client_colour/glass_colour/orange /obj/item/clothing/glasses/sunglasses/gar/supergar diff --git a/code/modules/clothing/glasses/hud.dm b/code/modules/clothing/glasses/hud.dm index 6a86dd4aafe..316d29cdc02 100644 --- a/code/modules/clothing/glasses/hud.dm +++ b/code/modules/clothing/glasses/hud.dm @@ -159,7 +159,7 @@ throw_speed = 4 attack_verb = list("sliced") hitsound = 'sound/weapons/bladeslice.ogg' - sharpness = IS_SHARP + sharpness = SHARP_EDGED /obj/item/clothing/glasses/hud/security/sunglasses/gars/supergars name = "giga HUD gar glasses" diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm index 78514228098..35fb84a5745 100644 --- a/code/modules/clothing/suits/armor.dm +++ b/code/modules/clothing/suits/armor.dm @@ -9,7 +9,7 @@ equip_delay_other = 40 max_integrity = 250 resistance_flags = NONE - armor = list("melee" = 35, "bullet" = 30, "laser" = 30, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50, "wound" = 15) + armor = list("melee" = 35, "bullet" = 30, "laser" = 30, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50, "wound" = 10) /obj/item/clothing/suit/armor/Initialize() . = ..() @@ -49,7 +49,7 @@ icon_state = "hos" inhand_icon_state = "greatcoat" body_parts_covered = CHEST|GROIN|ARMS|LEGS - armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 90, "wound" = 20) + armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 40, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 90, "wound" = 10) cold_protection = CHEST|GROIN|LEGS|ARMS heat_protection = CHEST|GROIN|LEGS|ARMS strip_delay = 80 @@ -118,7 +118,7 @@ body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS cold_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS - armor = list("melee" = 50, "bullet" = 10, "laser" = 10, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 80, "wound" = 30) + armor = list("melee" = 50, "bullet" = 10, "laser" = 10, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 80, "wound" = 20) clothing_flags = BLOCKS_SHOVE_KNOCKDOWN strip_delay = 80 equip_delay_other = 60 diff --git a/code/modules/food_and_drinks/drinks/drinks/bottle.dm b/code/modules/food_and_drinks/drinks/drinks/bottle.dm index 8f2c39a4754..bbd45f01074 100644 --- a/code/modules/food_and_drinks/drinks/drinks/bottle.dm +++ b/code/modules/food_and_drinks/drinks/drinks/bottle.dm @@ -146,7 +146,7 @@ inhand_icon_state = "beer" hitsound = 'sound/weapons/bladeslice.ogg' attack_verb = list("stabbed", "slashed", "attacked") - sharpness = IS_SHARP + sharpness = SHARP_EDGED var/static/icon/broken_outline = icon('icons/obj/drinks.dmi', "broken") /obj/item/broken_bottle/Initialize() diff --git a/code/modules/food_and_drinks/food/snacks.dm b/code/modules/food_and_drinks/food/snacks.dm index cf47992834e..f9de7318480 100644 --- a/code/modules/food_and_drinks/food/snacks.dm +++ b/code/modules/food_and_drinks/food/snacks.dm @@ -230,21 +230,10 @@ All foods are distributed among various categories. Use common sense. to_chat(user, "You cannot slice [src] here! You need a table or at least a tray.") return FALSE - var/slices_lost = 0 - if (accuracy >= IS_SHARP_ACCURATE) - user.visible_message( \ - "[user] slices [src].", \ - "You slice [src]." \ - ) - else - user.visible_message( \ - "[user] inaccurately slices [src] with [W]!", \ - "You inaccurately slice [src] with your [W]!" \ - ) - slices_lost = rand(1,min(1,round(slices_num/2))) + user.visible_message("[user] slices [src].", "You slice [src].") var/reagents_per_slice = reagents.total_volume/slices_num - for(var/i=1 to (slices_num-slices_lost)) + for(var/i in 1 to slices_num) var/obj/item/reagent_containers/food/snacks/slice = new slice_path (loc) initialize_slice(slice, reagents_per_slice) qdel(src) diff --git a/code/modules/goonchat/browserassets/css/browserOutput.css b/code/modules/goonchat/browserassets/css/browserOutput.css index 1a990391dec..135ef6ea1e5 100644 --- a/code/modules/goonchat/browserassets/css/browserOutput.css +++ b/code/modules/goonchat/browserassets/css/browserOutput.css @@ -298,7 +298,10 @@ em {font-style: normal; font-weight: bold;} .emote { font-style: italic;} .userdanger {color: #c51e1e; font-weight: bold; font-size: 185%;} +.bolddanger {color: #c51e1e;font-weight: bold;} .danger {color: #c51e1e;} +.tinydanger {color: #c51e1e; font-size: 85%;} +.smalldanger {color: #c51e1e; font-size: 90%;} .warning {color: #c51e1e; font-style: italic;} .alertwarning {color: #FF0000; font-weight: bold} .boldwarning {color: #c51e1e; font-style: italic; font-weight: bold} @@ -308,8 +311,9 @@ em {font-style: normal; font-weight: bold;} .rose {color: #ff5050;} .info {color: #9ab0ff;} .notice {color: #6685f5;} -.tinynotice {color: #6685f5; font-style: italic; font-size: 85%;} -.smallnotice {color: #6685f5; font-style: italic; font-size: 90%;} +.tinynotice {color: #6685f5; font-style: italic; font-size: 85%;} +.smallnotice {color: #6685f5; font-size: 90%;} +.smallnoticeital {color: #6685f5; font-style: italic; font-size: 90%;} .boldnotice {color: #6685f5; font-weight: bold;} .hear {color: #6685f5; font-style: italic;} .adminnotice {color: #6685f5;} diff --git a/code/modules/goonchat/browserassets/css/browserOutput_white.css b/code/modules/goonchat/browserassets/css/browserOutput_white.css index 0bddfb761af..1c9d0cdfb29 100644 --- a/code/modules/goonchat/browserassets/css/browserOutput_white.css +++ b/code/modules/goonchat/browserassets/css/browserOutput_white.css @@ -296,7 +296,10 @@ h1.alert, h2.alert {color: #000000;} .emote { font-style: italic;} .userdanger {color: #ff0000; font-weight: bold; font-size: 185%;} +.bolddanger {color: #ff0000; font-weight: bold;} .danger {color: #ff0000;} +.tinydanger {color: #ff0000; font-size: 85%;} +.smalldanger {color: #ff0000; font-size: 90%;} .warning {color: #ff0000; font-style: italic;} .alertwarning {color: #FF0000; font-weight: bold} .boldwarning {color: #ff0000; font-style: italic; font-weight: bold} @@ -307,7 +310,8 @@ h1.alert, h2.alert {color: #000000;} .info {color: #0000CC;} .notice {color: #000099;} .tinynotice {color: #000099; font-style: italic; font-size: 85%;} -.smallnotice {color: #000099; font-style: italic; font-size: 90%;} +.smallnotice {color: #000099; font-size: 90%;} +.smallnoticeital {color: #000099; font-style: italic; font-size: 90%;} .boldnotice {color: #000099; font-weight: bold;} .hear {color: #000099; font-style: italic;} .adminnotice {color: #0000ff;} diff --git a/code/modules/hydroponics/grown/banana.dm b/code/modules/hydroponics/grown/banana.dm index 1837e37cb0e..c0998045425 100644 --- a/code/modules/hydroponics/grown/banana.dm +++ b/code/modules/hydroponics/grown/banana.dm @@ -29,6 +29,12 @@ juice_results = list(/datum/reagent/consumable/banana = 0) distill_reagent = /datum/reagent/consumable/ethanol/bananahonk +/obj/item/reagent_containers/food/snacks/grown/banana/generate_trash(atom/location) + . = ..() + var/obj/item/grown/bananapeel/peel = . + if(istype(peel)) + peel.grind_results = list(/datum/reagent/consumable/banana_peel = seed.potency * 0.2) + /obj/item/reagent_containers/food/snacks/grown/banana/suicide_act(mob/user) user.visible_message("[user] is aiming [src] at [user.p_them()]self! It looks like [user.p_theyre()] trying to commit suicide!") playsound(loc, 'sound/items/bikehorn.ogg', 50, TRUE, -1) diff --git a/code/modules/hydroponics/hydroitemdefines.dm b/code/modules/hydroponics/hydroitemdefines.dm index 056f9aa163a..137a49b8ded 100644 --- a/code/modules/hydroponics/hydroitemdefines.dm +++ b/code/modules/hydroponics/hydroitemdefines.dm @@ -127,7 +127,7 @@ custom_materials = list(/datum/material/iron = 15000) attack_verb = list("chopped", "tore", "lacerated", "cut") hitsound = 'sound/weapons/bladeslice.ogg' - sharpness = IS_SHARP + sharpness = SHARP_EDGED /obj/item/hatchet/Initialize() . = ..() diff --git a/code/modules/mining/equipment/kinetic_crusher.dm b/code/modules/mining/equipment/kinetic_crusher.dm index 2553edeb394..d74088f09b0 100644 --- a/code/modules/mining/equipment/kinetic_crusher.dm +++ b/code/modules/mining/equipment/kinetic_crusher.dm @@ -17,7 +17,7 @@ custom_materials = list(/datum/material/iron=1150, /datum/material/glass=2075) hitsound = 'sound/weapons/bladeslice.ogg' attack_verb = list("smashed", "crushed", "cleaved", "chopped", "pulped") - sharpness = IS_SHARP + sharpness = SHARP_EDGED actions_types = list(/datum/action/item_action/toggle_light) obj_flags = UNIQUE_RENAME var/list/trophies = list() diff --git a/code/modules/mining/equipment/mining_tools.dm b/code/modules/mining/equipment/mining_tools.dm index a2c004a09dc..cf292e4a82d 100644 --- a/code/modules/mining/equipment/mining_tools.dm +++ b/code/modules/mining/equipment/mining_tools.dm @@ -133,7 +133,7 @@ w_class = WEIGHT_CLASS_NORMAL custom_materials = list(/datum/material/iron=50) attack_verb = list("bashed", "bludgeoned", "thrashed", "whacked") - sharpness = IS_SHARP + sharpness = SHARP_EDGED /obj/item/shovel/Initialize() . = ..() @@ -169,4 +169,4 @@ w_class = WEIGHT_CLASS_NORMAL toolspeed = 0.7 attack_verb = list("slashed", "impaled", "stabbed", "sliced") - sharpness = IS_SHARP + sharpness = SHARP_EDGED diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm index 809796c8e3c..e1d2c332730 100644 --- a/code/modules/mining/lavaland/necropolis_chests.dm +++ b/code/modules/mining/lavaland/necropolis_chests.dm @@ -711,7 +711,7 @@ hitsound = 'sound/weapons/bladeslice.ogg' hitsound_on = 'sound/weapons/bladeslice.ogg' w_class = WEIGHT_CLASS_BULKY - sharpness = IS_SHARP + sharpness = SHARP_EDGED faction_bonus_force = 30 nemesis_factions = list("mining", "boss") var/transform_cooldown @@ -817,7 +817,7 @@ lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' flags_1 = CONDUCT_1 - sharpness = IS_SHARP + sharpness = SHARP_EDGED w_class = WEIGHT_CLASS_BULKY force = 1 throwforce = 1 diff --git a/code/modules/mob/living/bloodcrawl.dm b/code/modules/mob/living/bloodcrawl.dm index 75ee9c53c25..ae9ad1a560b 100644 --- a/code/modules/mob/living/bloodcrawl.dm +++ b/code/modules/mob/living/bloodcrawl.dm @@ -74,7 +74,7 @@ if(victim.stat == CONSCIOUS) visible_message("[victim] kicks free of the blood pool just before entering it!", null, "You hear splashing and struggling.") - else if(victim.reagents && victim.reagents.has_reagent(/datum/reagent/consumable/ethanol/demonsblood, needs_metabolizing = TRUE)) + else if(victim.reagents?.has_reagent(/datum/reagent/consumable/ethanol/demonsblood, needs_metabolizing = TRUE)) visible_message("Something prevents [victim] from entering the pool!", "A strange force is blocking [victim] from entering!", "You hear a splash and a thud.") else victim.forceMove(src) @@ -107,7 +107,7 @@ if(!victim) return FALSE - if(victim.reagents && victim.reagents.has_reagent(/datum/reagent/consumable/ethanol/devilskiss, needs_metabolizing = TRUE)) + if(victim.reagents?.has_reagent(/datum/reagent/consumable/ethanol/devilskiss, needs_metabolizing = TRUE)) to_chat(src, "AAH! THEIR FLESH! IT BURNS!") adjustBruteLoss(25) //I can't use adjustHealth() here because bloodcrawl affects /mob/living and adjustHealth() only affects simple mobs var/found_bloodpool = FALSE diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 6b7c183c7af..fec1ae9deef 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -65,17 +65,8 @@ if(!all_wounds || !(user.a_intent == INTENT_HELP || user == src)) return ..() - // The following priority/nonpriority searching is so that if we have two wounds on a limb that use the same item for treatment (gauze can bandage cuts AND splint broken bones), - // we prefer whichever wound is not already treated (ignore the splinted broken bone for the open cut). If there's no priority wounds that this can treat, go through the - // non-priority ones randomly. - var/list/nonpriority_wounds = list() - for(var/datum/wound/W in shuffle(all_wounds)) - if(!W.treat_priority) - nonpriority_wounds += W - else if(W.treat_priority && W.try_treating(I, user)) - return 1 - - for(var/datum/wound/W in shuffle(nonpriority_wounds)) + for(var/i in shuffle(all_wounds)) + var/datum/wound/W = i if(W.try_treating(I, user)) return 1 @@ -1164,16 +1155,16 @@ /** * generate_fake_scars()- for when you want to scar someone, but you don't want to hurt them first. These scars don't count for temporal scarring (hence, fake) * - * If you want a specific wound scar, pass that wound type as the second arg, otherwise you can pass a list like WOUND_LIST_CUT to generate a random cut scar. + * If you want a specific wound scar, pass that wound type as the second arg, otherwise you can pass a list like WOUND_LIST_SLASH to generate a random cut scar. * * Arguments: * * num_scars- A number for how many scars you want to add - * * forced_type- Which wound or category of wounds you want to choose from, WOUND_LIST_BONE, WOUND_LIST_CUT, or WOUND_LIST_BURN (or some combination). If passed a list, picks randomly from the listed wounds. Defaults to all 3 types + * * forced_type- Which wound or category of wounds you want to choose from, WOUND_LIST_BLUNT, WOUND_LIST_SLASH, or WOUND_LIST_BURN (or some combination). If passed a list, picks randomly from the listed wounds. Defaults to all 3 types */ /mob/living/carbon/proc/generate_fake_scars(num_scars, forced_type) for(var/i in 1 to num_scars) - var/datum/scar/S = new - var/obj/item/bodypart/BP = pick(bodyparts) + var/datum/scar/scaries = new + var/obj/item/bodypart/scar_part = pick(bodyparts) var/wound_type if(forced_type) @@ -1182,12 +1173,20 @@ else wound_type = forced_type else - wound_type = pick(WOUND_LIST_BONE + WOUND_LIST_CUT + WOUND_LIST_BURN) + wound_type = pick(GLOB.global_all_wound_types) - var/datum/wound/W = new wound_type - S.generate(BP, W) - S.fake = TRUE - QDEL_NULL(W) + var/datum/wound/phantom_wound = new wound_type + scaries.generate(scar_part, phantom_wound) + scaries.fake = TRUE + QDEL_NULL(phantom_wound) /mob/living/carbon/is_face_visible() return !(wear_mask?.flags_inv & HIDEFACE) && !(head?.flags_inv & HIDEFACE) + +/** + * get_biological_state is a helper used to see what kind of wounds we roll for. By default we just assume carbons (read:monkeys) are flesh and bone, but humans rely on their species datums + * + * go look at the species def for more info [/datum/species/proc/get_biological_state] + */ +/mob/living/carbon/proc/get_biological_state() + return BIO_FLESH_BONE diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 6d332704428..473415f8de8 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -75,7 +75,7 @@ if(!affecting) //missing limb? we select the first bodypart (you can never have zero, because of chest) affecting = bodyparts[1] SEND_SIGNAL(I, COMSIG_ITEM_ATTACK_ZONE, src, user, affecting) - send_item_attack_message(I, user, affecting.name) + send_item_attack_message(I, user, affecting.name, affecting) if(I.force) apply_damage(I.force, I.damtype, affecting, wound_bonus = I.wound_bonus, bare_wound_bonus = I.bare_wound_bonus, sharpness = I.get_sharpness()) if(I.damtype == BRUTE && affecting.status == BODYPART_ORGANIC) @@ -96,14 +96,41 @@ head.add_mob_blood(src) update_inv_head() - //dismemberment - var/probability = I.get_dismemberment_chance(affecting) - if(prob(probability)) - if(affecting.dismember(I.damtype)) - I.add_mob_blood(src) - playsound(get_turf(src), I.get_dismember_sound(), 80, TRUE) return TRUE //successful attack +/mob/living/carbon/send_item_attack_message(obj/item/I, mob/living/user, hit_area, obj/item/bodypart/hit_bodypart) + var/message_verb = "attacked" + if(length(I.attack_verb)) + message_verb = "[pick(I.attack_verb)]" + else if(!I.force) + return + + var/extra_wound_details = "" + if(I.damtype == BRUTE && hit_bodypart.can_dismember()) + var/mangled_state = hit_bodypart.get_mangled_state() + var/bio_state = get_biological_state() + if(mangled_state == BODYPART_MANGLED_BOTH) + 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_JUST_BONE)) + 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_JUST_FLESH)) + extra_wound_details = ", [I.get_sharpness() == SHARP_EDGED ? "slicing" : "piercing"] at the remaining tissue" + + var/message_hit_area = "" + if(hit_area) + message_hit_area = " in the [hit_area]" + var/attack_message = "[src] is [message_verb][message_hit_area] with [I][extra_wound_details]!" + var/attack_message_local = "You're [message_verb][message_hit_area] with [I][extra_wound_details]!" + if(user in viewers(src, null)) + attack_message = "[user] [message_verb] [src][message_hit_area] with [I][extra_wound_details]!" + attack_message_local = "[user] [message_verb] you[message_hit_area] with [I][extra_wound_details]!" + if(user == src) + attack_message_local = "You [message_verb] yourself[message_hit_area] with [I][extra_wound_details]" + visible_message("[attack_message]",\ + "[attack_message_local]", null, COMBAT_MESSAGE_RANGE) + return TRUE + + /mob/living/carbon/attack_drone(mob/living/simple_animal/drone/user) return //so we don't call the carbon's attack_hand(). @@ -124,13 +151,14 @@ if(!(mobility_flags & MOBILITY_STAND) || !S.lying_required) if(user.a_intent == INTENT_HELP || user.a_intent == INTENT_DISARM) if(S.next_step(user, user.a_intent)) - return 1 + return TRUE - for(var/datum/wound/W in all_wounds) + for(var/i in all_wounds) + var/datum/wound/W = i if(W.try_handling(user)) - return 1 + return TRUE - return 0 + return FALSE /mob/living/carbon/attack_paw(mob/living/carbon/monkey/M) @@ -148,13 +176,13 @@ if(M.a_intent == INTENT_HELP) help_shake_act(M) - return 0 + return FALSE if(..()) //successful monkey bite. for(var/thing in M.diseases) var/datum/disease/D = thing ForceContractDisease(D) - return 1 + return TRUE /mob/living/carbon/attack_slime(mob/living/simple_animal/slime/M) diff --git a/code/modules/mob/living/carbon/damage_procs.dm b/code/modules/mob/living/carbon/damage_procs.dm index c64dd449cfc..df5067da7d0 100644 --- a/code/modules/mob/living/carbon/damage_procs.dm +++ b/code/modules/mob/living/carbon/damage_procs.dm @@ -1,6 +1,6 @@ -/mob/living/carbon/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = FALSE) +/mob/living/carbon/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE) SEND_SIGNAL(src, COMSIG_MOB_APPLY_DAMGE, damage, damagetype, def_zone) var/hit_percent = (100-blocked)/100 if(!damage || (!forced && hit_percent <= 0)) @@ -209,7 +209,7 @@ * * It automatically updates health status */ -/mob/living/carbon/take_bodypart_damage(brute = 0, burn = 0, stamina = 0, updating_health = TRUE, required_status, check_armor = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = FALSE) +/mob/living/carbon/take_bodypart_damage(brute = 0, burn = 0, stamina = 0, updating_health = TRUE, required_status, check_armor = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE) var/list/obj/item/bodypart/parts = get_damageable_bodyparts(required_status) if(!parts.len) return diff --git a/code/modules/mob/living/carbon/examine.dm b/code/modules/mob/living/carbon/examine.dm index 0040bed767f..12ecaffcd3f 100644 --- a/code/modules/mob/living/carbon/examine.dm +++ b/code/modules/mob/living/carbon/examine.dm @@ -45,7 +45,8 @@ msg += "[t_He] [t_has] \a [icon2html(I, user)] [I] stuck to [t_his] [BP.name]!\n" else msg += "[t_He] [t_has] \a [icon2html(I, user)] [I] embedded in [t_his] [BP.name]!\n" - for(var/datum/wound/W in BP.wounds) + for(var/i in BP.wounds) + var/datum/wound/W = i msg += "[W.get_examine_description(user)]\n" for(var/X in disabled) @@ -111,7 +112,7 @@ switch(scar_severity) if(1 to 2) - msg += "[t_He] [t_has] visible scarring, you can look again to take a closer look...\n" + msg += "[t_He] [t_has] visible scarring, you can look again to take a closer look...\n" if(3 to 4) msg += "[t_He] [t_has] several bad scars, you can look again to take a closer look...\n" if(5 to 6) diff --git a/code/modules/mob/living/carbon/human/damage_procs.dm b/code/modules/mob/living/carbon/human/damage_procs.dm index 231f85097b0..0947cfe49db 100644 --- a/code/modules/mob/living/carbon/human/damage_procs.dm +++ b/code/modules/mob/living/carbon/human/damage_procs.dm @@ -1,4 +1,4 @@ /// depending on the species, it will run the corresponding apply_damage code there -/mob/living/carbon/human/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = FALSE) +/mob/living/carbon/human/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE) return dna.species.apply_damage(damage, damagetype, def_zone, blocked, src, forced, spread_damage, wound_bonus, bare_wound_bonus, sharpness) diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index b6e484e4a32..1a17d1d0603 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -151,8 +151,9 @@ else msg += "[t_He] [t_has] \a [icon2html(I, user)] [I] embedded in [t_his] [BP.name]!\n" - for(var/datum/wound/W in BP.wounds) - msg += "[W.get_examine_description(user)]\n" + for(var/i in BP.wounds) + var/datum/wound/iter_wound = i + msg += "[iter_wound.get_examine_description(user)]\n" for(var/X in disabled) var/obj/item/bodypart/BP = X @@ -259,7 +260,13 @@ bleeding_limbs += BP var/num_bleeds = LAZYLEN(bleeding_limbs) - var/bleed_text = "[t_He] [t_is] bleeding from [t_his]" + + var/list/bleed_text + if(appears_dead) + bleed_text = list("Blood is visible in [t_his] open") + else + bleed_text = list("[t_He] [t_is] bleeding from [t_his]") + switch(num_bleeds) if(1 to 2) bleed_text += " [bleeding_limbs[1].name][num_bleeds == 2 ? " and [bleeding_limbs[2].name]" : ""]" @@ -268,11 +275,15 @@ var/obj/item/bodypart/BP = bleeding_limbs[i] bleed_text += " [BP.name]," bleed_text += " and [bleeding_limbs[num_bleeds].name]" - if(reagents.has_reagent(/datum/reagent/toxin/heparin, needs_metabolizing = TRUE)) - bleed_text += " incredibly quickly" - bleed_text += "!\n" - msg += bleed_text + if(appears_dead) + bleed_text += ", but it has pooled and is not flowing.\n" + else + if(reagents.has_reagent(/datum/reagent/toxin/heparin, needs_metabolizing = TRUE)) + bleed_text += " incredibly quickly" + + bleed_text += "!\n" + msg += bleed_text.Join() if(reagents.has_reagent(/datum/reagent/teslium, needs_metabolizing = TRUE)) msg += "[t_He] [t_is] emitting a gentle blue glow!\n" @@ -349,7 +360,7 @@ switch(scar_severity) if(1 to 2) - msg += "[t_He] [t_has] visible scarring, you can look again to take a closer look...\n" + msg += "[t_He] [t_has] visible scarring, you can look again to take a closer look...\n" if(3 to 4) msg += "[t_He] [t_has] several bad scars, you can look again to take a closer look...\n" if(5 to 6) diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index cb13e795c63..6bf01049be9 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -178,19 +178,29 @@ /// For use formatting all of the scars this human has for saving for persistent scarring /mob/living/carbon/human/proc/format_scars() - if(!all_scars) + var/list/missing_bodyparts = get_missing_limbs() + if(!all_scars && !length(missing_bodyparts)) return var/scars = "" + for(var/i in missing_bodyparts) + var/datum/scar/scaries = new + scars += "[scaries.format_amputated(i)]" for(var/i in all_scars) - var/datum/scar/S = i - scars += "[S.format()];" + var/datum/scar/scaries = i + scars += "[scaries.format()];" return scars /// Takes a single scar from the persistent scar loader and recreates it from the saved data /mob/living/carbon/human/proc/load_scar(scar_line) var/list/scar_data = splittext(scar_line, "|") - if(LAZYLEN(scar_data) != 4) + if(LAZYLEN(scar_data) != SCAR_SAVE_LENGTH) return // invalid, should delete - var/obj/item/bodypart/BP = get_bodypart("[scar_data[SCAR_SAVE_ZONE]]") - var/datum/scar/S = new - return S.load(BP, scar_data[SCAR_SAVE_DESC], scar_data[SCAR_SAVE_PRECISE_LOCATION], text2num(scar_data[SCAR_SAVE_SEVERITY])) + var/version = text2num(scar_data[SCAR_SAVE_VERS]) + if(!version || version < SCAR_CURRENT_VERSION) // get rid of old scars + return + var/obj/item/bodypart/the_part = get_bodypart("[scar_data[SCAR_SAVE_ZONE]]") + var/datum/scar/scaries = new + return scaries.load(the_part, scar_data[SCAR_SAVE_VERS], scar_data[SCAR_SAVE_DESC], scar_data[SCAR_SAVE_PRECISE_LOCATION], text2num(scar_data[SCAR_SAVE_SEVERITY])) + +/mob/living/carbon/human/get_biological_state() + return dna.species.get_biological_state() diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 454eaea180f..70bf9cce8fa 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -40,6 +40,10 @@ handle_liver() dna.species.spec_life(src) // for mutantraces + else + for(var/i in all_wounds) + var/datum/wound/iter_wound = i + iter_wound.on_stasis() //Update our name based on whether our face is obscured/disfigured name = get_visible_name() diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index 8469bd8a219..dc2013bfd45 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -1565,18 +1565,11 @@ GLOBAL_LIST_EMPTY(roundstart_races) var/weakness = H.check_weakness(I, user) apply_damage(I.force * weakness, I.damtype, def_zone, armor_block, H, wound_bonus = Iwound_bonus, bare_wound_bonus = I.bare_wound_bonus, sharpness = I.get_sharpness()) - H.send_item_attack_message(I, user, hit_area) + H.send_item_attack_message(I, user, hit_area, affecting) if(!I.force) return 0 //item force is zero - //dismemberment - var/probability = I.get_dismemberment_chance(affecting) - if(prob(probability) || (HAS_TRAIT(H, TRAIT_EASYDISMEMBER) && prob(probability))) //try twice - if(affecting.dismember(I.damtype)) - I.add_mob_blood(H) - playsound(get_turf(H), I.get_dismember_sound(), 80, TRUE) - var/bloody = 0 if(((I.damtype == BRUTE) && I.force && prob(25 + (I.force * 2)))) if(affecting.status == BODYPART_ORGANIC) @@ -1637,7 +1630,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) return TRUE -/datum/species/proc/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H, forced = FALSE, spread_damage = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = FALSE) +/datum/species/proc/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H, forced = FALSE, spread_damage = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE) SEND_SIGNAL(H, COMSIG_MOB_APPLY_DAMGE, damage, damagetype, def_zone, wound_bonus, bare_wound_bonus, sharpness) // make sure putting wound_bonus here doesn't screw up other signals or uses for this signal var/hit_percent = (100-(blocked+armor))/100 hit_percent = (hit_percent * (100-H.physiology.damage_resistance))/100 @@ -2111,3 +2104,13 @@ GLOBAL_LIST_EMPTY(roundstart_races) else to_chat(H, "You beat your wings and begin to hover gently above the ground...") H.set_resting(FALSE, TRUE) + +/** + * The human species version of [/mob/living/carbon/proc/get_biological_state]. Depends on the HAS_FLESH and HAS_BONE species traits, having bones lets you have bone wounds, having flesh lets you have burn, slash, and piercing wounds + */ +/datum/species/proc/get_biological_state(mob/living/carbon/human/H) + . = BIO_INORGANIC + if(HAS_FLESH in species_traits) + . |= BIO_JUST_FLESH + if(HAS_BONE in species_traits) + . |= BIO_JUST_BONE diff --git a/code/modules/mob/living/carbon/human/species_types/dullahan.dm b/code/modules/mob/living/carbon/human/species_types/dullahan.dm index 9935717ba80..f977dd0c4aa 100644 --- a/code/modules/mob/living/carbon/human/species_types/dullahan.dm +++ b/code/modules/mob/living/carbon/human/species_types/dullahan.dm @@ -2,7 +2,7 @@ name = "Dullahan" id = "dullahan" default_color = "FFFFFF" - species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS) + species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS, HAS_FLESH, HAS_BONE) inherent_traits = list(TRAIT_NOHUNGER,TRAIT_NOBREATH) default_features = list("mcolor" = "FFF", "tail_human" = "None", "ears" = "None", "wings" = "None") use_skintones = TRUE diff --git a/code/modules/mob/living/carbon/human/species_types/ethereal.dm b/code/modules/mob/living/carbon/human/species_types/ethereal.dm index 7153cacfdcb..3db33a17cb6 100644 --- a/code/modules/mob/living/carbon/human/species_types/ethereal.dm +++ b/code/modules/mob/living/carbon/human/species_types/ethereal.dm @@ -15,7 +15,7 @@ payday_modifier = 0.75 attack_type = BURN //burn bish damage_overlay_type = "" //We are too cool for regular damage overlays - species_traits = list(DYNCOLORS, AGENDER, NO_UNDERWEAR, HAIR) + species_traits = list(DYNCOLORS, AGENDER, NO_UNDERWEAR, HAIR, HAS_FLESH, HAS_BONE) // i mean i guess they have blood so they can have wounds too changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_PRIDE | MIRROR_MAGIC | RACE_SWAP | ERT_SPAWN | SLIME_EXTRACT species_language_holder = /datum/language_holder/ethereal inherent_traits = list(TRAIT_NOHUNGER) diff --git a/code/modules/mob/living/carbon/human/species_types/golems.dm b/code/modules/mob/living/carbon/human/species_types/golems.dm index 3cb9bc4237d..8c59ab07567 100644 --- a/code/modules/mob/living/carbon/human/species_types/golems.dm +++ b/code/modules/mob/living/carbon/human/species_types/golems.dm @@ -914,7 +914,7 @@ special_names = list("Head", "Broth", "Fracture", "Rattler", "Appetit") liked_food = GROSS | MEAT | RAW toxic_food = null - species_traits = list(NOBLOOD,NO_UNDERWEAR,NOEYESPRITES) + species_traits = list(NOBLOOD,NO_UNDERWEAR,NOEYESPRITES,HAS_BONE) inherent_biotypes = MOB_UNDEAD|MOB_HUMANOID mutanttongue = /obj/item/organ/tongue/bone sexes = FALSE @@ -942,8 +942,10 @@ H.reagents.remove_reagent(chem.type, chem.volume - 10) to_chat(H, "The excess milk is dripping off your bones!") H.heal_bodypart_damage(1.5,0, 0) + for(var/i in H.all_wounds) + var/datum/wound/iter_wound = i + iter_wound.on_xadone(2) H.reagents.remove_reagent(chem.type, chem.metabolization_rate) - return TRUE if(chem.type == /datum/reagent/toxin/bonehurtingjuice) H.adjustStaminaLoss(7.5, 0) H.adjustBruteLoss(0.5, 0) diff --git a/code/modules/mob/living/carbon/human/species_types/humans.dm b/code/modules/mob/living/carbon/human/species_types/humans.dm index 2c61d6a5e61..2c80700c589 100644 --- a/code/modules/mob/living/carbon/human/species_types/humans.dm +++ b/code/modules/mob/living/carbon/human/species_types/humans.dm @@ -2,7 +2,7 @@ name = "Human" id = "human" default_color = "FFFFFF" - species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,CAN_SCAR) + species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,HAS_FLESH,HAS_BONE) default_features = list("mcolor" = "FFF", "wings" = "None") use_skintones = 1 skinned_type = /obj/item/stack/sheet/animalhide/human diff --git a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm index 3e8654bf684..ed7f3df50d3 100644 --- a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm @@ -4,7 +4,7 @@ id = "lizard" say_mod = "hisses" default_color = "00FF00" - species_traits = list(MUTCOLORS,EYECOLOR,LIPS,CAN_SCAR) + species_traits = list(MUTCOLORS,EYECOLOR,LIPS,HAS_FLESH,HAS_BONE) inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID|MOB_REPTILE mutant_bodyparts = list("tail_lizard", "snout", "spines", "horns", "frills", "body_markings", "legs") mutanttongue = /obj/item/organ/tongue/lizard @@ -84,6 +84,6 @@ name = "Ash Walker" id = "ashlizard" limbs_id = "lizard" - species_traits = list(MUTCOLORS,EYECOLOR,LIPS,DIGITIGRADE,CAN_SCAR) + species_traits = list(MUTCOLORS,EYECOLOR,LIPS,DIGITIGRADE,HAS_FLESH,HAS_BONE) inherent_traits = list(TRAIT_CHUNKYFINGERS,TRAIT_NOBREATH) species_language_holder = /datum/language_holder/lizard/ash diff --git a/code/modules/mob/living/carbon/human/species_types/mothmen.dm b/code/modules/mob/living/carbon/human/species_types/mothmen.dm index c28fa0768e0..c6704cef159 100644 --- a/code/modules/mob/living/carbon/human/species_types/mothmen.dm +++ b/code/modules/mob/living/carbon/human/species_types/mothmen.dm @@ -3,7 +3,7 @@ id = "moth" say_mod = "flutters" default_color = "00FF00" - species_traits = list(LIPS, NOEYESPRITES, CAN_SCAR) + species_traits = list(LIPS, NOEYESPRITES, HAS_FLESH, HAS_BONE) inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID|MOB_BUG mutant_bodyparts = list("moth_wings", "moth_markings") default_features = list("moth_wings" = "Plain", "moth_markings" = "None") diff --git a/code/modules/mob/living/carbon/human/species_types/mushpeople.dm b/code/modules/mob/living/carbon/human/species_types/mushpeople.dm index 8e302abbb29..ca20e5e6aa7 100644 --- a/code/modules/mob/living/carbon/human/species_types/mushpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/mushpeople.dm @@ -10,7 +10,7 @@ nojumpsuit = TRUE say_mod = "poofs" //what does a mushroom sound like - species_traits = list(MUTCOLORS, NOEYESPRITES, NO_UNDERWEAR) + species_traits = list(MUTCOLORS, NOEYESPRITES, NO_UNDERWEAR, HAS_FLESH, HAS_BONE) inherent_traits = list(TRAIT_NOBREATH, TRAIT_NOFLASH) inherent_factions = list("mushroom") speedmod = 1.5 //faster than golems but not by much diff --git a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm index 6e6a1b7ba4f..51327257ed5 100644 --- a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm +++ b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm @@ -4,7 +4,7 @@ say_mod = "rattles" sexes = 0 meat = /obj/item/stack/sheet/mineral/plasma - species_traits = list(NOBLOOD,NOTRANSSTING) + species_traits = list(NOBLOOD,NOTRANSSTING, HAS_BONE) inherent_traits = list(TRAIT_RESISTCOLD,TRAIT_RADIMMUNE,TRAIT_GENELESS,TRAIT_NOHUNGER,TRAIT_ALWAYS_CLEAN) inherent_biotypes = MOB_HUMANOID|MOB_MINERAL mutantlungs = /obj/item/organ/lungs/plasmaman @@ -150,14 +150,23 @@ /datum/species/plasmaman/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H) . = ..() - if(chem.type == /datum/reagent/consumable/milk) + if(istype(chem, /datum/reagent/consumable/milk)) if(chem.volume > 10) H.reagents.remove_reagent(chem.type, chem.volume - 10) to_chat(H, "The excess milk is dripping off your bones!") H.heal_bodypart_damage(1.5,0, 0) H.reagents.remove_reagent(chem.type, chem.metabolization_rate) + for(var/i in H.all_wounds) + var/datum/wound/iter_wound = i + iter_wound.on_xadone(2) return TRUE - if(chem.type == /datum/reagent/toxin/bonehurtingjuice) + if(istype(chem, /datum/reagent/toxin/plasma)) + H.reagents.remove_reagent(chem.type, chem.metabolization_rate) + for(var/i in H.all_wounds) + var/datum/wound/iter_wound = i + iter_wound.on_xadone(4) // plasmamen use plasma to reform their bones or whatever + return TRUE + if(istype(chem, /datum/reagent/toxin/bonehurtingjuice)) H.adjustStaminaLoss(7.5, 0) H.adjustBruteLoss(0.5, 0) if(prob(20)) diff --git a/code/modules/mob/living/carbon/human/species_types/podpeople.dm b/code/modules/mob/living/carbon/human/species_types/podpeople.dm index 4d9f557f02d..742ca01cd67 100644 --- a/code/modules/mob/living/carbon/human/species_types/podpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/podpeople.dm @@ -3,7 +3,7 @@ name = "Podperson" id = "pod" default_color = "59CE00" - species_traits = list(MUTCOLORS,EYECOLOR) + species_traits = list(MUTCOLORS,EYECOLOR, HAS_FLESH, HAS_BONE) inherent_traits = list(TRAIT_ALWAYS_CLEAN) inherent_factions = list("plants", "vines") attack_verb = "slash" diff --git a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm index 3c4b1428f81..b3102eab051 100644 --- a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm @@ -172,7 +172,7 @@ righthand_file = 'icons/mob/inhands/antag/changeling_righthand.dmi' item_flags = ABSTRACT | DROPDEL w_class = WEIGHT_CLASS_HUGE - sharpness = IS_SHARP + sharpness = SHARP_EDGED wound_bonus = -60 bare_wound_bonus = 20 diff --git a/code/modules/mob/living/carbon/human/species_types/skeletons.dm b/code/modules/mob/living/carbon/human/species_types/skeletons.dm index 3ea96e01468..7443224e7f6 100644 --- a/code/modules/mob/living/carbon/human/species_types/skeletons.dm +++ b/code/modules/mob/living/carbon/human/species_types/skeletons.dm @@ -5,7 +5,7 @@ say_mod = "rattles" sexes = 0 meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/skeleton - species_traits = list(NOBLOOD) + species_traits = list(NOBLOOD, HAS_BONE) inherent_traits = list(TRAIT_NOMETABOLISM,TRAIT_TOXIMMUNE,TRAIT_RESISTHEAT,TRAIT_NOBREATH,TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_RADIMMUNE,TRAIT_GENELESS,\ TRAIT_PIERCEIMMUNE,TRAIT_NOHUNGER,TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_FAKEDEATH,TRAIT_XENO_IMMUNE,TRAIT_NOCLONELOSS) inherent_biotypes = MOB_UNDEAD|MOB_HUMANOID @@ -29,6 +29,9 @@ if(chem.volume > 10) H.reagents.remove_reagent(chem.type, chem.volume - 10) to_chat(H, "The excess milk is dripping off your bones!") + for(var/i in H.all_wounds) + var/datum/wound/iter_wound = i + iter_wound.on_xadone(2) H.heal_bodypart_damage(1,1, 0) H.reagents.remove_reagent(chem.type, chem.metabolization_rate) return TRUE diff --git a/code/modules/mob/living/carbon/human/species_types/snail.dm b/code/modules/mob/living/carbon/human/species_types/snail.dm index 73b133f46f3..0b4c9537089 100644 --- a/code/modules/mob/living/carbon/human/species_types/snail.dm +++ b/code/modules/mob/living/carbon/human/species_types/snail.dm @@ -3,7 +3,7 @@ id = "snail" offset_features = list(OFFSET_UNIFORM = list(0,0), OFFSET_ID = list(0,0), OFFSET_GLOVES = list(0,0), OFFSET_GLASSES = list(0,4), OFFSET_EARS = list(0,0), OFFSET_SHOES = list(0,0), OFFSET_S_STORE = list(0,0), OFFSET_FACEMASK = list(0,0), OFFSET_HEAD = list(0,0), OFFSET_FACE = list(0,0), OFFSET_BELT = list(0,0), OFFSET_BACK = list(0,0), OFFSET_SUIT = list(0,0), OFFSET_NECK = list(0,0)) default_color = "336600" //vomit green - species_traits = list(MUTCOLORS, NO_UNDERWEAR) + species_traits = list(MUTCOLORS, NO_UNDERWEAR, HAS_FLESH, HAS_BONE) inherent_traits = list(TRAIT_ALWAYS_CLEAN, TRAIT_NOSLIPALL) attack_verb = "slap" say_mod = "slurs" diff --git a/code/modules/mob/living/carbon/human/species_types/vampire.dm b/code/modules/mob/living/carbon/human/species_types/vampire.dm index 1bd65fc01ab..e1ece348605 100644 --- a/code/modules/mob/living/carbon/human/species_types/vampire.dm +++ b/code/modules/mob/living/carbon/human/species_types/vampire.dm @@ -2,7 +2,7 @@ name = "Vampire" id = "vampire" default_color = "FFFFFF" - species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,DRINKSBLOOD) + species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,DRINKSBLOOD, HAS_FLESH, HAS_BONE) inherent_traits = list(TRAIT_NOHUNGER,TRAIT_NOBREATH) inherent_biotypes = MOB_UNDEAD|MOB_HUMANOID default_features = list("mcolor" = "FFF", "tail_human" = "None", "ears" = "None", "wings" = "None") diff --git a/code/modules/mob/living/carbon/human/species_types/zombies.dm b/code/modules/mob/living/carbon/human/species_types/zombies.dm index 31f2b715a18..2ebf0c5f729 100644 --- a/code/modules/mob/living/carbon/human/species_types/zombies.dm +++ b/code/modules/mob/living/carbon/human/species_types/zombies.dm @@ -7,7 +7,7 @@ say_mod = "moans" sexes = 0 meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/zombie - species_traits = list(NOBLOOD,NOZOMBIE,NOTRANSSTING) + species_traits = list(NOBLOOD,NOZOMBIE,NOTRANSSTING, HAS_FLESH, HAS_BONE) inherent_traits = list(TRAIT_NOMETABOLISM,TRAIT_TOXIMMUNE,TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_RADIMMUNE,TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_NOBREATH,TRAIT_NODEATH,TRAIT_FAKEDEATH,TRAIT_NOCLONELOSS) inherent_biotypes = MOB_UNDEAD|MOB_HUMANOID mutanttongue = /obj/item/organ/tongue/zombie @@ -46,7 +46,7 @@ /datum/species/zombie/infectious/spec_stun(mob/living/carbon/human/H,amount) . = min(20, amount) -/datum/species/zombie/infectious/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H, forced = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = FALSE) +/datum/species/zombie/infectious/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H, forced = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE) . = ..() if(.) regen_cooldown = world.time + REGENERATION_DELAY @@ -64,9 +64,9 @@ C.heal_overall_damage(heal_amt,heal_amt) C.adjustToxLoss(-heal_amt) for(var/i in C.all_wounds) - var/datum/wound/W = i - if(prob(4-W.severity)) - W.remove_wound() + var/datum/wound/iter_wound = i + if(prob(4-iter_wound.severity)) + iter_wound.remove_wound() if(!C.InCritical() && prob(4)) playsound(C, pick(spooks), 50, TRUE, 10) diff --git a/code/modules/mob/living/damage_procs.dm b/code/modules/mob/living/damage_procs.dm index 55cf8d56a1a..18cb1c5b236 100644 --- a/code/modules/mob/living/damage_procs.dm +++ b/code/modules/mob/living/damage_procs.dm @@ -14,7 +14,7 @@ * * Returns TRUE if damage applied */ -/mob/living/proc/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = FALSE) +/mob/living/proc/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE) SEND_SIGNAL(src, COMSIG_MOB_APPLY_DAMGE, damage, damagetype, def_zone) var/hit_percent = (100-blocked)/100 if(!damage || (!forced && hit_percent <= 0)) @@ -263,7 +263,7 @@ update_stamina() /// damage ONE external organ, organ gets randomly selected from damaged ones. -/mob/living/proc/take_bodypart_damage(brute = 0, burn = 0, stamina = 0, updating_health = TRUE, required_status, check_armor = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = FALSE) +/mob/living/proc/take_bodypart_damage(brute = 0, burn = 0, stamina = 0, updating_health = TRUE, required_status, check_armor = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE) adjustBruteLoss(brute, FALSE) //zero as argument for no instant health update adjustFireLoss(burn, FALSE) adjustStaminaLoss(stamina, FALSE) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 0bb380b7b76..df378b0f410 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -677,41 +677,56 @@ return /mob/living/proc/makeTrail(turf/target_turf, turf/start, direction) - if(!has_gravity()) + if(!has_gravity() || !isturf(start) || !blood_volume) return - var/blood_exists = FALSE - for(var/obj/effect/decal/cleanable/trail_holder/C in start) //checks for blood splatter already on the floor - blood_exists = TRUE - if(isturf(start)) - var/trail_type = getTrail() - if(trail_type) - var/brute_ratio = round(getBruteLoss() / maxHealth, 0.1) - if(blood_volume && blood_volume > max(BLOOD_VOLUME_NORMAL*(1 - brute_ratio * 0.25), 0))//don't leave trail if blood volume below a threshold - blood_volume = max(blood_volume - max(1, brute_ratio * 2), 0) //that depends on our brute damage. - var/newdir = get_dir(target_turf, start) - if(newdir != direction) - newdir = newdir | direction - if(newdir == 3) //N + S - newdir = NORTH - else if(newdir == 12) //E + W - newdir = EAST - if((newdir in GLOB.cardinals) && (prob(50))) - newdir = turn(get_dir(target_turf, start), 180) - if(!blood_exists) - new /obj/effect/decal/cleanable/trail_holder(start, get_static_viruses()) + var/blood_exists = locate(/obj/effect/decal/cleanable/trail_holder) in start - for(var/obj/effect/decal/cleanable/trail_holder/TH in start) - if((!(newdir in TH.existing_dirs) || trail_type == "trails_1" || trail_type == "trails_2") && TH.existing_dirs.len <= 16) //maximum amount of overlays is 16 (all light & heavy directions filled) - TH.existing_dirs += newdir - TH.add_overlay(image('icons/effects/blood.dmi', trail_type, dir = newdir)) - TH.transfer_mob_blood_dna(src) + var/trail_type = getTrail() + if(!trail_type) + return + + var/brute_ratio = round(getBruteLoss() / maxHealth, 0.1) + if(blood_volume < max(BLOOD_VOLUME_NORMAL*(1 - brute_ratio * 0.25), 0))//don't leave trail if blood volume below a threshold + return + + var/bleed_amount = bleedDragAmount() + blood_volume = max(blood_volume - bleed_amount, 0) //that depends on our brute damage. + var/newdir = get_dir(target_turf, start) + if(newdir != direction) + newdir = newdir | direction + if(newdir == (NORTH|SOUTH)) + newdir = NORTH + else if(newdir == (EAST|WEST)) + newdir = EAST + if((newdir in GLOB.cardinals) && (prob(50))) + newdir = turn(get_dir(target_turf, start), 180) + if(!blood_exists) + new /obj/effect/decal/cleanable/trail_holder(start, get_static_viruses()) + + for(var/obj/effect/decal/cleanable/trail_holder/TH in start) + if((!(newdir in TH.existing_dirs) || trail_type == "trails_1" || trail_type == "trails_2") && TH.existing_dirs.len <= 16) //maximum amount of overlays is 16 (all light & heavy directions filled) + TH.existing_dirs += newdir + TH.add_overlay(image('icons/effects/blood.dmi', trail_type, dir = newdir)) + TH.transfer_mob_blood_dna(src) /mob/living/carbon/human/makeTrail(turf/T) if((NOBLOOD in dna.species.species_traits) || !is_bleeding() || bleedsuppress) return ..() +///Returns how much blood we're losing from being dragged a tile, from [mob/living/proc/makeTrail] +/mob/living/proc/bleedDragAmount() + var/brute_ratio = round(getBruteLoss() / maxHealth, 0.1) + return max(1, brute_ratio * 2) + +/mob/living/carbon/bleedDragAmount() + var/bleed_amount = 0 + for(var/i in all_wounds) + var/datum/wound/iter_wound = i + bleed_amount += iter_wound.drag_bleed_amount() + return bleed_amount + /mob/living/proc/getTrail() if(getBruteLoss() < 300) return pick("ltrails_1", "ltrails_2") diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index b634bd42822..4748489e0c8 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -51,7 +51,7 @@ var/armor = run_armor_check(def_zone, P.flag, "","",P.armour_penetration) var/on_hit_state = P.on_hit(src, armor) if(!P.nodamage && on_hit_state != BULLET_ACT_BLOCK) - apply_damage(P.damage, P.damage_type, def_zone, armor, wound_bonus=P.wound_bonus, bare_wound_bonus=P.bare_wound_bonus, sharpness=P.sharpness) + apply_damage(P.damage, P.damage_type, def_zone, armor, wound_bonus=P.wound_bonus, bare_wound_bonus=P.bare_wound_bonus, sharpness = P.sharpness) apply_effects(P.stun, P.knockdown, P.unconscious, P.irradiate, P.slur, P.stutter, P.eyeblur, P.drowsy, armor, P.stamina, P.jitter, P.paralyze, P.immobilize) if(P.dismemberment) check_projectile_dismemberment(P, def_zone) @@ -80,14 +80,16 @@ dtype = I.damtype if(!blocked) - visible_message("[src] is hit by [I]!", \ - "You're hit by [I]!") - if(!I.throwforce) - return - var/armor = run_armor_check(zone, "melee", "Your armor has protected your [parse_zone(zone)].", "Your armor has softened hit to your [parse_zone(zone)].",I.armour_penetration) - apply_damage(I.throwforce, dtype, zone, armor, sharpness=I.sharpness) if(I.thrownby) log_combat(I.thrownby, src, "threw and hit", I) + if(!nosell_hit) + visible_message("[src] is hit by [I]!", \ + "You're hit by [I]!") + if(!I.throwforce) + return + var/armor = run_armor_check(zone, "melee", "Your armor has protected your [parse_zone(zone)].", "Your armor has softened hit to your [parse_zone(zone)].",I.armour_penetration) + apply_damage(I.throwforce, dtype, zone, armor, sharpness=I.get_sharpness(), wound_bonus=(nosell_hit * CANT_WOUND)) + else return 1 else diff --git a/code/modules/mob/living/silicon/damage_procs.dm b/code/modules/mob/living/silicon/damage_procs.dm index 3f0ce34d971..1a6e3e16492 100644 --- a/code/modules/mob/living/silicon/damage_procs.dm +++ b/code/modules/mob/living/silicon/damage_procs.dm @@ -1,5 +1,5 @@ -/mob/living/silicon/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = FALSE) +/mob/living/silicon/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE) var/hit_percent = (100-blocked)/100 if((!damage || (!forced && hit_percent <= 0))) return 0 diff --git a/code/modules/mob/living/simple_animal/hostile/bear.dm b/code/modules/mob/living/simple_animal/hostile/bear.dm index 7f3056ec998..8fad5c4f8cb 100644 --- a/code/modules/mob/living/simple_animal/hostile/bear.dm +++ b/code/modules/mob/living/simple_animal/hostile/bear.dm @@ -29,7 +29,7 @@ melee_damage_upper = 15 wound_bonus = -5 bare_wound_bonus = 10 // BEAR wound bonus am i right - sharpness = TRUE + sharpness = SHARP_EDGED attack_verb_continuous = "claws" attack_verb_simple = "claw" attack_sound = 'sound/weapons/bladeslice.ogg' diff --git a/code/modules/mob/living/simple_animal/hostile/cockroach.dm b/code/modules/mob/living/simple_animal/hostile/cockroach.dm index 8008858a8bd..f50d8fd9724 100644 --- a/code/modules/mob/living/simple_animal/hostile/cockroach.dm +++ b/code/modules/mob/living/simple_animal/hostile/cockroach.dm @@ -96,7 +96,7 @@ gold_core_spawnable = HOSTILE_SPAWN attack_sound = 'sound/weapons/bladeslice.ogg' faction = list("hostile") - sharpness = IS_SHARP + sharpness = SHARP_POINTY squish_chance = 0 // manual squish if relevant /mob/living/simple_animal/hostile/cockroach/hauberoach/ComponentInitialize() diff --git a/code/modules/mob/living/simple_animal/hostile/syndicate.dm b/code/modules/mob/living/simple_animal/hostile/syndicate.dm index 4804bb09e59..3deed6857b1 100644 --- a/code/modules/mob/living/simple_animal/hostile/syndicate.dm +++ b/code/modules/mob/living/simple_animal/hostile/syndicate.dm @@ -296,7 +296,7 @@ melee_damage_upper = 15 wound_bonus = -10 bare_wound_bonus = 20 - sharpness = TRUE + sharpness = SHARP_EDGED obj_damage = 0 environment_smash = ENVIRONMENT_SMASH_NONE attack_verb_continuous = "cuts" diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 4f3f481226a..a7625a7b6ef 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -150,7 +150,7 @@ ///How much bare wounding power it has var/bare_wound_bonus = 0 ///If the attacks from this are sharp - var/sharpness = FALSE + var/sharpness = SHARP_NONE ///Generic flags var/simple_mob_flags = NONE diff --git a/code/modules/ninja/energy_katana.dm b/code/modules/ninja/energy_katana.dm index 151952097fe..e21894eb0cc 100644 --- a/code/modules/ninja/energy_katana.dm +++ b/code/modules/ninja/energy_katana.dm @@ -14,7 +14,7 @@ hitsound = 'sound/weapons/bladeslice.ogg' attack_verb = list("attacked", "slashed", "stabbed", "sliced", "tore", "lacerated", "ripped", "diced", "cut") slot_flags = ITEM_SLOT_BACK|ITEM_SLOT_BELT - sharpness = IS_SHARP + sharpness = SHARP_EDGED max_integrity = 200 resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF var/datum/effect_system/spark_spread/spark_system diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_stars.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_stars.dm index ccce6c3af2b..a0c6073281a 100644 --- a/code/modules/ninja/suit/n_suit_verbs/ninja_stars.dm +++ b/code/modules/ninja/suit/n_suit_verbs/ninja_stars.dm @@ -14,5 +14,5 @@ /obj/item/throwing_star/ninja name = "ninja throwing star" - throwforce = 30 + throwforce = 20 embedding = list("pain_mult" = 6, "embed_chance" = 100, "fall_chance" = 0) diff --git a/code/modules/paperwork/pen.dm b/code/modules/paperwork/pen.dm index 013a025391b..898c133a8a9 100644 --- a/code/modules/paperwork/pen.dm +++ b/code/modules/paperwork/pen.dm @@ -29,6 +29,7 @@ var/degrees = 0 var/font = PEN_FONT embedding = list() + sharpness = SHARP_POINTY /obj/item/pen/suicide_act(mob/user) user.visible_message("[user] is scribbling numbers all over [user.p_them()]self with [src]! It looks like [user.p_theyre()] trying to commit sudoku...") @@ -101,7 +102,7 @@ throw_speed = 4 colour = "crimson" custom_materials = list(/datum/material/gold = 750) - sharpness = IS_SHARP + sharpness = SHARP_EDGED resistance_flags = FIRE_PROOF unique_reskin = list("Oak" = "pen-fountain-o", "Gold" = "pen-fountain-g", @@ -196,7 +197,7 @@ */ /obj/item/pen/edagger attack_verb = list("slashed", "stabbed", "sliced", "tore", "lacerated", "ripped", "diced", "cut") //these won't show up if the pen is off - sharpness = IS_SHARP + sharpness = SHARP_EDGED var/on = FALSE /obj/item/pen/edagger/ComponentInitialize() diff --git a/code/modules/projectiles/boxes_magazines/ammo_boxes.dm b/code/modules/projectiles/boxes_magazines/ammo_boxes.dm index cc5122052db..8ffa5b51a7a 100644 --- a/code/modules/projectiles/boxes_magazines/ammo_boxes.dm +++ b/code/modules/projectiles/boxes_magazines/ammo_boxes.dm @@ -38,7 +38,7 @@ /obj/item/ammo_box/c38/dumdum name = "speed loader (.38 DumDum)" - desc = "Designed to quickly reload revolvers. DumDum bullets shatter on impact and shred the target's innards, likely getting caught inside." + desc = "Designed to quickly reload revolvers. These rounds expand on impact, allowing them to shred the target and cause massive bleeding. Very weak against armor and distant targets." ammo_type = /obj/item/ammo_casing/c38/dumdum /obj/item/ammo_box/c38/hotshot diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 8b3860edab4..fc0e58659df 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -198,7 +198,8 @@ return if(iscarbon(target)) var/mob/living/carbon/C = target - for(var/datum/wound/W in C.all_wounds) + for(var/i in C.all_wounds) + var/datum/wound/W = i if(W.try_treating(src, user)) return // another coward cured! diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm index d279ef9cf6c..c08bd8e6afa 100644 --- a/code/modules/projectiles/guns/energy/special.dm +++ b/code/modules/projectiles/guns/energy/special.dm @@ -128,7 +128,7 @@ flags_1 = CONDUCT_1 attack_verb = list("attacked", "slashed", "cut", "sliced") force = 12 - sharpness = IS_SHARP + sharpness = SHARP_EDGED can_charge = FALSE heat = 3800 diff --git a/code/modules/projectiles/guns/magic/staff.dm b/code/modules/projectiles/guns/magic/staff.dm index b5135d66bd4..2046ecf1d22 100644 --- a/code/modules/projectiles/guns/magic/staff.dm +++ b/code/modules/projectiles/guns/magic/staff.dm @@ -87,7 +87,7 @@ force = 20 armour_penetration = 75 block_chance = 50 - sharpness = IS_SHARP + sharpness = SHARP_EDGED max_charges = 4 /obj/item/gun/magic/staff/spellblade/Initialize() diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index d18be3b119e..ea5fdbde6b6 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -11,6 +11,7 @@ pass_flags = PASSTABLE mouse_opacity = MOUSE_OPACITY_TRANSPARENT movement_type = FLYING + wound_bonus = CANT_WOUND // can't wound by default //The sound this plays on impact. var/hitsound = 'sound/weapons/pierce.ogg' var/hitsound_wall = "" @@ -124,23 +125,36 @@ var/temporary_unstoppable_movement = FALSE - ///If defined, on hit we create an item of this type then call hitby() on the hit target with this + ///If defined, on hit we create an item of this type then call hitby() on the hit target with this, mainly used for embedding items (bullets) in targets var/shrapnel_type + ///If we have a shrapnel_type defined, these embedding stats will be passed to the spawned shrapnel type, which will roll for embedding on the target + var/list/embedding + ///If TRUE, hit mobs even if they're on the floor and not our target var/hit_stunned_targets = FALSE - wound_bonus = CANT_WOUND - /// For telling whether we want to roll for bone breaking or lacerations if we're bothering with wounds - var/sharpness = FALSE + ///For what kind of brute wounds we're rolling for, if we're doing such a thing. Lasers obviously don't care since they do burn instead. + var/sharpness = SHARP_NONE + ///How much we want to drop both wound_bonus and bare_wound_bonus (to a minimum of 0 for the latter) per tile, for falloff purposes + var/wound_falloff_tile + ///How much we want to drop the embed_chance value, if we can embed, per tile, for falloff purposes + var/embed_falloff_tile /obj/projectile/Initialize() . = ..() permutated = list() decayedRange = range + if(embedding) + updateEmbedding() /obj/projectile/proc/Range() range-- + if(wound_bonus != CANT_WOUND) + wound_bonus += wound_falloff_tile + bare_wound_bonus = max(0, bare_wound_bonus + wound_falloff_tile) + if(embedding) + embedding["embed_chance"] += embed_falloff_tile if(range <= 0 && loc) on_range() @@ -743,3 +757,23 @@ /obj/projectile/experience_pressure_difference() return + +///Like [/obj/item/proc/updateEmbedding] but for projectiles instead, call this when you want to add embedding or update the stats on the embedding element +/obj/projectile/proc/updateEmbedding() + if(!shrapnel_type || !LAZYLEN(embedding)) + return + + AddElement(/datum/element/embed,\ + embed_chance = (!isnull(embedding["embed_chance"]) ? embedding["embed_chance"] : EMBED_CHANCE),\ + fall_chance = (!isnull(embedding["fall_chance"]) ? embedding["fall_chance"] : EMBEDDED_ITEM_FALLOUT),\ + pain_chance = (!isnull(embedding["pain_chance"]) ? embedding["pain_chance"] : EMBEDDED_PAIN_CHANCE),\ + pain_mult = (!isnull(embedding["pain_mult"]) ? embedding["pain_mult"] : EMBEDDED_PAIN_MULTIPLIER),\ + remove_pain_mult = (!isnull(embedding["remove_pain_mult"]) ? embedding["remove_pain_mult"] : EMBEDDED_UNSAFE_REMOVAL_PAIN_MULTIPLIER),\ + rip_time = (!isnull(embedding["rip_time"]) ? embedding["rip_time"] : EMBEDDED_UNSAFE_REMOVAL_TIME),\ + ignore_throwspeed_threshold = (!isnull(embedding["ignore_throwspeed_threshold"]) ? embedding["ignore_throwspeed_threshold"] : FALSE),\ + impact_pain_mult = (!isnull(embedding["impact_pain_mult"]) ? embedding["impact_pain_mult"] : EMBEDDED_IMPACT_PAIN_MULTIPLIER),\ + jostle_chance = (!isnull(embedding["jostle_chance"]) ? embedding["jostle_chance"] : EMBEDDED_JOSTLE_CHANCE),\ + jostle_pain_mult = (!isnull(embedding["jostle_pain_mult"]) ? embedding["jostle_pain_mult"] : EMBEDDED_JOSTLE_PAIN_MULTIPLIER),\ + pain_stam_pct = (!isnull(embedding["pain_stam_pct"]) ? embedding["pain_stam_pct"] : EMBEDDED_PAIN_STAM_PCT),\ + projectile_payload = shrapnel_type) + return TRUE diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm index b76ff677c56..8dac813f8e5 100644 --- a/code/modules/projectiles/projectile/bullets.dm +++ b/code/modules/projectiles/projectile/bullets.dm @@ -6,5 +6,13 @@ nodamage = FALSE flag = "bullet" hitsound_wall = "ricochet" - sharpness = TRUE + sharpness = SHARP_POINTY impact_effect_type = /obj/effect/temp_visual/impact_effect + shrapnel_type = /obj/item/shrapnel/bullet + embedding = list(embed_chance=15, fall_chance=2, jostle_chance=0, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.5, pain_mult=3, rip_time=10) + wound_falloff_tile = -5 + embed_falloff_tile = -5 + +/obj/projectile/bullet/smite + name = "divine retribution" + damage = 10 diff --git a/code/modules/projectiles/projectile/bullets/lmg.dm b/code/modules/projectiles/projectile/bullets/lmg.dm index a6e2f3386d0..e05240a7369 100644 --- a/code/modules/projectiles/projectile/bullets/lmg.dm +++ b/code/modules/projectiles/projectile/bullets/lmg.dm @@ -25,8 +25,10 @@ /obj/projectile/bullet/mm712x82 name = "7.12x82mm bullet" - damage = 45 + damage = 40 armour_penetration = 5 + wound_bonus = -50 + wound_falloff_tile = 0 /obj/projectile/bullet/mm712x82_ap name = "7.12x82mm armor-piercing bullet" @@ -35,8 +37,12 @@ /obj/projectile/bullet/mm712x82_hp name = "7.12x82mm hollow-point bullet" - damage = 60 + damage = 50 armour_penetration = -60 + sharpness = SHARP_EDGED + wound_bonus = -40 + bare_wound_bonus = 30 + wound_falloff_tile = -8 /obj/projectile/bullet/incendiary/mm712x82 name = "7.12x82mm incendiary bullet" @@ -50,4 +56,4 @@ ricochet_chance = 60 ricochet_auto_aim_range = 4 ricochet_incidence_leeway = 35 - + wound_bonus = -50 diff --git a/code/modules/projectiles/projectile/bullets/pistol.dm b/code/modules/projectiles/projectile/bullets/pistol.dm index 1e53db3cdbd..0752c0f8533 100644 --- a/code/modules/projectiles/projectile/bullets/pistol.dm +++ b/code/modules/projectiles/projectile/bullets/pistol.dm @@ -3,11 +3,13 @@ /obj/projectile/bullet/c9mm name = "9mm bullet" damage = 30 + embedding = list(embed_chance=15, fall_chance=3, jostle_chance=4, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.4, pain_mult=5, jostle_pain_mult=6, rip_time=10) /obj/projectile/bullet/c9mm_ap name = "9mm armor-piercing bullet" damage = 27 armour_penetration = 40 + embedding = null /obj/projectile/bullet/c9mm_hp name = "9mm hollow-point bullet" diff --git a/code/modules/projectiles/projectile/bullets/revolver.dm b/code/modules/projectiles/projectile/bullets/revolver.dm index 0c713426365..dc7b56d77ae 100644 --- a/code/modules/projectiles/projectile/bullets/revolver.dm +++ b/code/modules/projectiles/projectile/bullets/revolver.dm @@ -19,8 +19,9 @@ ricochet_chance = 50 ricochet_auto_aim_angle = 10 ricochet_auto_aim_range = 3 - wound_bonus = -35 - sharpness = TRUE + wound_bonus = -20 + bare_wound_bonus = 10 + embedding = list(embed_chance=15, fall_chance=2, jostle_chance=2, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.4, pain_mult=3, jostle_pain_mult=5, rip_time=10) /obj/projectile/bullet/c38/match name = ".38 Match bullet" @@ -42,14 +43,21 @@ ricochet_chance = 130 ricochet_decay_damage = 0.8 shrapnel_type = NONE + sharpness = SHARP_NONE + embedding = null +// premium .38 ammo from cargo, weak against armor, lower base damage, but excellent at embedding and causing slice wounds at close range /obj/projectile/bullet/c38/dumdum name = ".38 DumDum bullet" damage = 15 armour_penetration = -30 ricochets_max = 0 - wound_bonus = 0 - shrapnel_type = /obj/item/shrapnel/bullet/c38/dumdum + sharpness = SHARP_EDGED + wound_bonus = 20 + bare_wound_bonus = 20 + embedding = list(embed_chance=75, fall_chance=3, jostle_chance=4, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.4, pain_mult=5, jostle_pain_mult=6, rip_time=10) + wound_falloff_tile = -5 + embed_falloff_tile = -15 /obj/projectile/bullet/c38/trac name = ".38 TRAC bullet" @@ -98,6 +106,7 @@ /obj/projectile/bullet/a357 name = ".357 bullet" damage = 60 + wound_bonus = -70 // admin only really, for ocelot memes /obj/projectile/bullet/a357/match diff --git a/code/modules/projectiles/projectile/bullets/rifle.dm b/code/modules/projectiles/projectile/bullets/rifle.dm index 1f596e75f29..d1794ef3036 100644 --- a/code/modules/projectiles/projectile/bullets/rifle.dm +++ b/code/modules/projectiles/projectile/bullets/rifle.dm @@ -4,6 +4,7 @@ name = "5.56mm bullet" damage = 35 armour_penetration = 30 + wound_bonus = -40 /obj/projectile/bullet/a556/phasic name = "5.56mm phasic bullet" @@ -17,6 +18,8 @@ /obj/projectile/bullet/a762 name = "7.62 bullet" damage = 60 + wound_bonus = -35 + wound_falloff_tile = 0 /obj/projectile/bullet/a762_enchanted name = "enchanted 7.62 bullet" diff --git a/code/modules/projectiles/projectile/bullets/shotgun.dm b/code/modules/projectiles/projectile/bullets/shotgun.dm index 70ab3dd7aa3..4128eb39d22 100644 --- a/code/modules/projectiles/projectile/bullets/shotgun.dm +++ b/code/modules/projectiles/projectile/bullets/shotgun.dm @@ -1,23 +1,26 @@ /obj/projectile/bullet/shotgun_slug name = "12g shotgun slug" - damage = 60 + damage = 50 + sharpness = SHARP_POINTY + wound_bonus = 0 /obj/projectile/bullet/shotgun_slug/executioner name = "executioner slug" // admin only, can dismember limbs - sharpness = TRUE - wound_bonus = 0 + sharpness = SHARP_EDGED + wound_bonus = 80 /obj/projectile/bullet/shotgun_slug/pulverizer name = "pulverizer slug" // admin only, can crush bones - sharpness = FALSE - wound_bonus = 0 + sharpness = SHARP_NONE + wound_bonus = 80 /obj/projectile/bullet/shotgun_beanbag name = "beanbag slug" damage = 10 stamina = 55 wound_bonus = 20 - sharpness = FALSE + sharpness = SHARP_NONE + embedding = null /obj/projectile/bullet/incendiary/shotgun name = "incendiary slug" @@ -68,18 +71,22 @@ return BULLET_ACT_HIT /obj/projectile/bullet/pellet - var/tile_dropoff = 0.75 + var/tile_dropoff = 0.45 var/tile_dropoff_s = 0.5 /obj/projectile/bullet/pellet/shotgun_buckshot name = "buckshot pellet" - damage = 12.5 - wound_bonus = -10 + damage = 7.5 + wound_bonus = 5 + bare_wound_bonus = 5 + wound_falloff_tile = -2.5 // low damage + additional dropoff will already curb wounding potential anything past point blank /obj/projectile/bullet/pellet/shotgun_rubbershot name = "rubbershot pellet" damage = 3 stamina = 11 + sharpness = SHARP_NONE + embedding = null /obj/projectile/bullet/pellet/shotgun_incapacitate name = "incapacitating pellet" @@ -96,8 +103,10 @@ qdel(src) /obj/projectile/bullet/pellet/shotgun_improvised - tile_dropoff = 0.55 //Come on it does 6 damage don't be like that. + tile_dropoff = 0.35 //Come on it does 6 damage don't be like that. damage = 6 + wound_bonus = 0 + bare_wound_bonus = 7.5 /obj/projectile/bullet/pellet/shotgun_improvised/Initialize() . = ..() diff --git a/code/modules/projectiles/projectile/bullets/smg.dm b/code/modules/projectiles/projectile/bullets/smg.dm index faa0fd98ca2..3cdf3de789e 100644 --- a/code/modules/projectiles/projectile/bullets/smg.dm +++ b/code/modules/projectiles/projectile/bullets/smg.dm @@ -2,7 +2,9 @@ /obj/projectile/bullet/c45 name = ".45 bullet" - damage = 35 + damage = 30 + wound_bonus = -10 + wound_falloff_tile = -10 /obj/projectile/bullet/c45_ap name = ".45 armor-piercing bullet" @@ -19,11 +21,15 @@ /obj/projectile/bullet/c46x30mm name = "4.6x30mm bullet" damage = 20 + wound_bonus = -5 + bare_wound_bonus = 5 + embed_falloff_tile = -4 /obj/projectile/bullet/c46x30mm_ap name = "4.6x30mm armor-piercing bullet" damage = 15 armour_penetration = 40 + embedding = null /obj/projectile/bullet/incendiary/c46x30mm name = "4.6x30mm incendiary bullet" diff --git a/code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm index b655ae30568..b263b1f8f82 100644 --- a/code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm @@ -420,8 +420,9 @@ if(method in list(PATCH, TOUCH, VAPOR)) var/harmies = min(carbies.getBruteLoss(),carbies.adjustBruteLoss(-1.25 * reac_volume)*-1) var/burnies = min(carbies.getFireLoss(),carbies.adjustFireLoss(-1.25 * reac_volume)*-1) - for(var/datum/wound/burn/burn_wound in carbies.all_wounds) - burn_wound.regenerate_flesh(reac_volume) + for(var/i in carbies.all_wounds) + var/datum/wound/iter_wound = i + iter_wound.on_synthflesh(reac_volume) carbies.adjustToxLoss((harmies+burnies)*0.66) if(show_message) to_chat(carbies, "You feel your burns and bruises healing! It stings like hell!") diff --git a/code/modules/reagents/chemistry/reagents/drink_reagents.dm b/code/modules/reagents/chemistry/reagents/drink_reagents.dm index 19aa060636a..acbc59c3df3 100644 --- a/code/modules/reagents/chemistry/reagents/drink_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drink_reagents.dm @@ -132,6 +132,25 @@ . = 1 ..() +// i googled "natural coagulant" and a couple of results came up for banana peels, so after precisely 30 more seconds of research, i now dub grinding banana peels good for your blood +/datum/reagent/consumable/banana_peel + name = "Pulped Banana Peel" + description = "Okay, so you put a banana peel in a grinder... Why, exactly?" + color = "#863333" // rgb: 175, 175, 0 + reagent_state = SOLID + taste_description = "stringy, bitter pulp" + glass_name = "glass of banana peel pulp" + glass_desc = "Okay, so you put a banana peel in a grinder... Why, exactly?" + +/datum/reagent/consumable/baked_banana_peel + name = "Baked Banana Peel Powder" + description = "You took a banana peel... pulped it... baked it... Where are you going with this?" + color = "#863333" // rgb: 175, 175, 0 + reagent_state = SOLID + taste_description = "bitter powder" + glass_name = "glass of banana peel powder" + description = "You took a banana peel... pulped it... baked it... Where are you going with this?" + /datum/reagent/consumable/nothing name = "Nothing" description = "Absolutely nothing." diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index 592924afaea..e6b3c864593 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -151,8 +151,8 @@ M.adjustToxLoss(-power, 0, TRUE) //heals TOXINLOVERs M.adjustCloneLoss(-power, 0) for(var/i in M.all_wounds) - var/datum/wound/W = i - W.on_xadone(power) + var/datum/wound/iter_wound = i + iter_wound.on_xadone(power) REMOVE_TRAIT(M, TRAIT_DISFIGURED, TRAIT_GENERIC) //fixes common causes for disfiguration . = 1 metabolization_rate = REAGENTS_METABOLISM * (0.00001 * (M.bodytemperature ** 2) + 0.5) @@ -204,8 +204,8 @@ M.adjustToxLoss(-power, 0, TRUE) M.adjustCloneLoss(-power, 0) for(var/i in M.all_wounds) - var/datum/wound/W = i - W.on_xadone(power) + var/datum/wound/iter_wound = i + iter_wound.on_xadone(power) REMOVE_TRAIT(M, TRAIT_DISFIGURED, TRAIT_GENERIC) . = 1 ..() @@ -272,7 +272,7 @@ /datum/reagent/medicine/salglu_solution name = "Saline-Glucose Solution" - description = "Has a 33% chance per metabolism cycle to heal brute and burn damage. Can be used as a temporary blood substitute." + description = "Has a 33% chance per metabolism cycle to heal brute and burn damage. Can be used as a temporary blood substitute, as well as slowly speeding blood regeneration." reagent_state = LIQUID color = "#DCDCDC" metabolization_rate = 0.5 * REAGENTS_METABOLISM @@ -280,6 +280,7 @@ taste_description = "sweetness and salt" var/last_added = 0 var/maximum_reachable = BLOOD_VOLUME_NORMAL - 10 //So that normal blood regeneration can continue with salglu active + var/extra_regen = 0.25 // in addition to acting as temporary blood, also add this much to their actual blood per tick /datum/reagent/medicine/salglu_solution/on_mob_life(mob/living/carbon/M) if(last_added) @@ -289,7 +290,7 @@ var/amount_to_add = min(M.blood_volume, volume*5) var/new_blood_level = min(M.blood_volume + amount_to_add, maximum_reachable) last_added = new_blood_level - M.blood_volume - M.blood_volume = new_blood_level + M.blood_volume = new_blood_level + extra_regen if(prob(33)) M.adjustBruteLoss(-0.5*REM, 0) M.adjustFireLoss(-0.5*REM, 0) @@ -1337,10 +1338,57 @@ L.Dizzy(0) L.Jitter(0) -// handled in cut wounds process +// helps bleeding wounds clot faster /datum/reagent/medicine/coagulant name = "Sanguirite" - description = "A coagulant used to help open cuts clot faster." + description = "A proprietary coagulant used to help bleeding wounds clot faster." reagent_state = LIQUID color = "#bb2424" metabolization_rate = 0.25 * REAGENTS_METABOLISM + overdose_threshold = 20 + /// How much base clotting we do per bleeding wound, multiplied by the below number for each bleeding wound + var/clot_rate = 0.25 + /// If we have multiple bleeding wounds, we count the number of bleeding wounds, then multiply the clot rate by this^(n) before applying it to each cut, so more cuts = less clotting per cut (though still more total clotting) + var/clot_coeff_per_wound = 0.9 + +/datum/reagent/medicine/coagulant/on_mob_life(mob/living/carbon/M) + . = ..() + if(!M.blood_volume || !M.all_wounds) + return + + var/effective_clot_rate = clot_rate + + for(var/i in M.all_wounds) + var/datum/wound/iter_wound = i + if(iter_wound.blood_flow) + effective_clot_rate *= clot_coeff_per_wound + + for(var/i in M.all_wounds) + var/datum/wound/iter_wound = i + iter_wound.blood_flow = max(0, iter_wound.blood_flow - effective_clot_rate) + +/datum/reagent/medicine/coagulant/overdose_process(mob/living/M) + . = ..() + if(!M.blood_volume) + return + + if(prob(15)) + M.losebreath += rand(2,4) + M.adjustOxyLoss(rand(1,3)) + if(prob(30)) + to_chat(M, "You can feel your blood clotting up in your veins!") + else if(prob(10)) + to_chat(M, "You feel like your blood has stopped moving!") + + if(prob(50)) + var/obj/item/organ/lungs/our_lungs = M.getorganslot(ORGAN_SLOT_LUNGS) + our_lungs.applyOrganDamage(1) + else + var/obj/item/organ/heart/our_heart = M.getorganslot(ORGAN_SLOT_HEART) + our_heart.applyOrganDamage(1) + +// can be synthesized on station rather than bought. made by grinding a banana peel, heating it up, then mixing the banana peel powder with salglu +/datum/reagent/medicine/coagulant/weak + name = "Synthi-Sanguirite" + description = "A synthetic coagulant used to help bleeding wounds clot faster. Not quite as effective as name brand Sanguirite, especially on patients with lots of cuts." + clot_coeff_per_wound = 0.8 diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index 1f973772c40..9f46d5ec43f 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -201,6 +201,11 @@ M.ExtinguishMob() ..() +/datum/reagent/water/on_mob_life(mob/living/carbon/M) + . = ..() + if(M.blood_volume) + M.blood_volume += 0.1 // water is good for you! + /datum/reagent/water/holywater name = "Holy Water" description = "Water blessed by some deity." @@ -233,6 +238,8 @@ ..() /datum/reagent/water/holywater/on_mob_life(mob/living/carbon/M) + if(M.blood_volume) + M.blood_volume += 0.1 // water is good for you! if(!data) data = list("misc" = 1) data["misc"]++ @@ -2285,6 +2292,7 @@ reagent_state = LIQUID color = "#D2FFFA" metabolization_rate = 0.75 * REAGENTS_METABOLISM // 5u (WOUND_DETERMINATION_CRITICAL) will last for ~17 ticks + self_consuming = TRUE /// Whether we've had at least WOUND_DETERMINATION_SEVERE (2.5u) of determination at any given time. No damage slowdown immunity or indication we're having a second wind if it's just a single moderate wound var/significant = FALSE diff --git a/code/modules/reagents/chemistry/recipes/medicine.dm b/code/modules/reagents/chemistry/recipes/medicine.dm index 3165fe9f4a8..95322debcd0 100644 --- a/code/modules/reagents/chemistry/recipes/medicine.dm +++ b/code/modules/reagents/chemistry/recipes/medicine.dm @@ -30,6 +30,18 @@ results = list(/datum/reagent/medicine/salglu_solution = 3) required_reagents = list(/datum/reagent/consumable/sodiumchloride = 1, /datum/reagent/water = 1, /datum/reagent/consumable/sugar = 1) +/datum/chemical_reaction/baked_banana_peel + results = list(/datum/reagent/consumable/baked_banana_peel = 1) + required_temp = 413.15 // if it's good enough for caramel it's good enough for this + required_reagents = list(/datum/reagent/consumable/banana_peel = 1) + mix_message = "The pulp dries up and takes on a powdery state!" + mob_react = FALSE + +/datum/chemical_reaction/coagulant_weak + results = list(/datum/reagent/medicine/coagulant/weak = 3) + required_reagents = list(/datum/reagent/medicine/salglu_solution = 2, /datum/reagent/consumable/baked_banana_peel = 1) + mob_react = FALSE + /datum/chemical_reaction/mine_salve results = list(/datum/reagent/medicine/mine_salve = 3) required_reagents = list(/datum/reagent/fuel/oil = 1, /datum/reagent/water = 1, /datum/reagent/iron = 1) diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm index 6226e681e66..6711be55a1e 100644 --- a/code/modules/reagents/reagent_containers/hypospray.dm +++ b/code/modules/reagents/reagent_containers/hypospray.dm @@ -277,7 +277,14 @@ /obj/item/reagent_containers/hypospray/medipen/ekit name = "emergency first-aid autoinjector" - desc = "An epinephrine medipen with trace amounts of coagulants and antibiotics to help stabilize bad cuts and burns." + desc = "An epinephrine medipen with extra coagulant and antibiotics to help stabilize bad cuts and burns." volume = 15 amount_per_transfer_from_this = 15 list_reagents = list(/datum/reagent/medicine/epinephrine = 12, /datum/reagent/medicine/coagulant = 2.5, /datum/reagent/medicine/spaceacillin = 0.5) + +/obj/item/reagent_containers/hypospray/medipen/blood_loss + name = "hypovolemic-response autoinjector" + desc = "A medipen designed to stabilize and rapidly reverse severe bloodloss." + volume = 15 + amount_per_transfer_from_this = 15 + list_reagents = list(/datum/reagent/medicine/epinephrine = 5, /datum/reagent/medicine/coagulant = 2.5, /datum/reagent/iron = 3.5, /datum/reagent/medicine/salglu_solution = 4) diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index 38bddb8e3b8..0aa074a33cd 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -15,6 +15,7 @@ custom_materials = list(/datum/material/iron=10, /datum/material/glass=20) reagent_flags = TRANSPARENT custom_price = 150 + sharpness = SHARP_POINTY /obj/item/reagent_containers/syringe/Initialize() . = ..() diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index 55ca39ec0a6..0adebde80d8 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -81,12 +81,12 @@ /// A hat won't cover your face, but a shirt covering your chest will cover your... you know, chest var/scars_covered_by_clothes = TRUE - /// Descriptions for the locations on the limb for scars to be assigned, just cosmetic - var/list/specific_locations = list("general area") /// So we know if we need to scream if this limb hits max damage var/last_maxed /// How much generic bleedstacks we have on this bodypart var/generic_bleedstacks + /// If we have a gauze wrapping currently applied (not including splints) + var/obj/item/stack/current_gauze /obj/item/bodypart/examine(mob/user) @@ -149,9 +149,23 @@ var/turf/T = get_turf(src) if(status != BODYPART_ROBOTIC) playsound(T, 'sound/misc/splort.ogg', 50, TRUE, -1) + if(current_gauze) + QDEL_NULL(current_gauze) + for(var/obj/item/organ/drop_organ in get_organs()) + drop_organ.transfer_to_limb(src, owner) for(var/obj/item/I in src) I.forceMove(T) +///since organs aren't actually stored in the bodypart themselves while attached to a person, we have to query the owner for what we should have +/obj/item/bodypart/proc/get_organs() + if(!owner) + return + . = list() + for(var/i in owner.internal_organs) //internal organs inside the dismembered limb are dropped. + var/obj/item/organ/organ_check = i + if(check_zone(organ_check.zone) == body_zone) + . += organ_check + /obj/item/bodypart/proc/consider_processing() if(stamina_dam > DAMAGE_PRECISION) . = TRUE @@ -169,7 +183,7 @@ //Applies brute and burn damage to the organ. Returns 1 if the damage-icon states changed at all. //Damage will not exceed max_damage using this proc //Cannot apply negative damage -/obj/item/bodypart/proc/receive_damage(brute = 0, burn = 0, stamina = 0, blocked = 0, updating_health = TRUE, required_status = null, wound_bonus = 0, bare_wound_bonus = 0, sharpness = FALSE) // maybe separate BRUTE_SHARP and BRUTE_OTHER eventually somehow hmm +/obj/item/bodypart/proc/receive_damage(brute = 0, burn = 0, stamina = 0, blocked = 0, updating_health = TRUE, required_status = null, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE) // maybe separate BRUTE_SHARP and BRUTE_OTHER eventually somehow hmm var/hit_percent = (100-blocked)/100 if((!brute && !burn && !stamina) || hit_percent <= 0) return FALSE @@ -197,16 +211,64 @@ if(ALIEN_BODYPART,LARVA_BODYPART) //aliens take double burn //nothing can burn with so much snowflake code around burn *= 2 - var/wounding_type = (brute > burn ? WOUND_BRUTE : WOUND_BURN) + /* + // START WOUND HANDLING + */ + + // what kind of wounds we're gonna roll for, take the greater between brute and burn, then if it's brute, we subdivide based on sharpness + var/wounding_type = (brute > burn ? WOUND_BLUNT : WOUND_BURN) var/wounding_dmg = max(brute, burn) - if(wounding_type == WOUND_BRUTE && sharpness) - wounding_type = WOUND_SHARP - // i know this is effectively the same check as above but i don't know if those can null the damage by rounding and want to be safe - if(owner && wounding_dmg > 4 && wound_bonus != CANT_WOUND) - // if you want to make tox wounds or some other type, this will need to be expanded and made more modular - // handle all our wounding stuff + + var/mangled_state = get_mangled_state() + var/bio_state = owner.get_biological_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%) + + if(wounding_type == WOUND_BLUNT) + if(sharpness == SHARP_EDGED) + wounding_type = WOUND_SLASH + else if(sharpness == SHARP_POINTY) + wounding_type = WOUND_PIERCE + + //Handling for bone only/flesh only(none right now)/flesh and bone targets + switch(bio_state) + // if we're bone only, all cutting attacks go straight to the bone + if(BIO_JUST_BONE) + if(wounding_type == WOUND_SLASH) + wounding_type = WOUND_BLUNT + wounding_dmg *= (easy_dismember ? 1 : 0.5) + else if(wounding_type == WOUND_PIERCE) + wounding_type = WOUND_BLUNT + wounding_dmg *= (easy_dismember ? 1 : 0.75) + if((mangled_state & BODYPART_MANGLED_BONE) && try_dismember(wounding_type, wounding_dmg, wound_bonus, bare_wound_bonus)) + return + // note that there's no handling for BIO_JUST_FLESH since we don't have any that are that right now (slimepeople maybe someday) + // standard humanoids + if(BIO_FLESH_BONE) + // 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(mangled_state == BODYPART_MANGLED_FLESH && sharpness) + playsound(src, "sound/effects/wounds/crackandbleed.ogg", 100) + if(wounding_type == WOUND_SLASH && !easy_dismember) + wounding_dmg *= 0.5 // edged weapons pass along 50% 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(mangled_state == BODYPART_MANGLED_BOTH && 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(owner && wounding_dmg >= WOUND_MINIMUM_DAMAGE && wound_bonus != CANT_WOUND) check_wounding(wounding_type, wounding_dmg, wound_bonus, bare_wound_bonus) + for(var/i in wounds) + var/datum/wound/iter_wound = i + iter_wound.receive_damage(wounding_type, wounding_dmg, wound_bonus) + + /* + // END WOUND HANDLING + */ + + //back to our regularly scheduled program, we now actually apply damage if there's room below limb damage cap var/can_inflict = max_damage - get_damage() var/total_damage = brute + burn if(total_damage > can_inflict && total_damage > 0) // TODO: the second part of this check should be removed once disabling is all done @@ -219,10 +281,6 @@ brute_dam += brute burn_dam += burn - for(var/i in wounds) - var/datum/wound/W = i - W.receive_damage(wounding_type, wounding_dmg, wound_bonus) - //We've dealt the physical damages, if there's room lets apply the stamina damage. stamina_dam += round(clamp(stamina, 0, max_stamina_damage - stamina_dam), DAMAGE_PRECISION) @@ -236,14 +294,58 @@ update_disabled() return update_bodypart_damage_state() || . +/// 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=SHARP_NONE) + if(!owner || phantom_wounding_dmg <= WOUND_MINIMUM_DAMAGE || wound_bonus == CANT_WOUND) + return + + var/mangled_state = get_mangled_state() + var/bio_state = owner.get_biological_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%) + + if(wounding_type == WOUND_BLUNT) + if(sharpness == SHARP_EDGED) + wounding_type = WOUND_SLASH + else if(sharpness == SHARP_POINTY) + wounding_type = WOUND_PIERCE + + //Handling for bone only/flesh only(none right now)/flesh and bone targets + switch(bio_state) + // if we're bone only, all cutting attacks go straight to the bone + if(BIO_JUST_BONE) + if(wounding_type == WOUND_SLASH) + wounding_type = WOUND_BLUNT + phantom_wounding_dmg *= (easy_dismember ? 1 : 0.5) + else if(wounding_type == WOUND_PIERCE) + wounding_type = WOUND_BLUNT + phantom_wounding_dmg *= (easy_dismember ? 1 : 0.75) + if((mangled_state & BODYPART_MANGLED_BONE) && try_dismember(wounding_type, phantom_wounding_dmg, wound_bonus, bare_wound_bonus)) + return + // note that there's no handling for BIO_JUST_FLESH since we don't have any that are that right now (slimepeople maybe someday) + // standard humanoids + if(BIO_FLESH_BONE) + // 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(mangled_state == BODYPART_MANGLED_FLESH && sharpness) + playsound(src, "sound/effects/wounds/crackandbleed.ogg", 100) + if(wounding_type == WOUND_SLASH && !easy_dismember) + phantom_wounding_dmg *= 0.5 // edged weapons pass along 50% 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_type = WOUND_BLUNT + else if(mangled_state == BODYPART_MANGLED_BOTH && try_dismember(wounding_type, phantom_wounding_dmg, wound_bonus, bare_wound_bonus)) + return + + check_wounding(wounding_type, phantom_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 * - * We generate a "score" for how woundable the attack was based on the damage and other factors discussed in [check_wounding_mods()], then go down the list from most severe to least severe wounds in that category. + * We generate a "score" for how woundable the attack was based on the damage and other factors discussed in [/obj/item/bodypart/proc/check_wounding_mods], then go down the list from most severe to least severe wounds in that category. * We can promote a wound from a lesser to a higher severity this way, but we give up if we have a wound of the given type and fail to roll a higher severity, so no sidegrades/downgrades * * Arguments: - * * woundtype- Either WOUND_SHARP, WOUND_BRUTE, or WOUND_BURN based on the attack type. + * * woundtype- Either WOUND_BLUNT, WOUND_SLASH, WOUND_PIERCE, or WOUND_BURN based on the attack type. * * damage- How much damage is tied to this attack, since wounding potential scales with damage in an attack (see: WOUND_DAMAGE_EXPONENT) * * wound_bonus- The wound_bonus of an attack * * bare_wound_bonus- The bare_wound_bonus of an attack @@ -252,21 +354,29 @@ // actually roll wounds if applicable if(HAS_TRAIT(owner, TRAIT_EASYLIMBDISABLE)) damage *= 1.5 + else + damage = min(damage, WOUND_MAX_CONSIDERED_DAMAGE) if(HAS_TRAIT(owner,TRAIT_HARDLIMBDISABLE)) - damage *= 0.5 + damage *= 0.8 + + if(HAS_TRAIT(owner, TRAIT_EASYDISMEMBER)) + damage *= 1.25 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/wounds_checking + var/list/wounds_checking = GLOB.global_wound_types[woundtype] - switch(woundtype) - if(WOUND_SHARP) - wounds_checking = WOUND_LIST_CUT - if(WOUND_BRUTE) - wounds_checking = WOUND_LIST_BONE - if(WOUND_BURN) - wounds_checking = WOUND_LIST_BURN + // 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 + var/list/clothing = human_wearer.clothingonpart(src) + for(var/i in clothing) + var/obj/item/clothing/clothes_check = i + // unlike normal armor checks, we tabluate these piece-by-piece manually so we can also pass on appropriate damage the clothing's limbs if necessary + if(clothes_check.armor.getRating("wound")) + bare_wound_bonus = 0 + break //cycle through the wounds of the relevant category from the most severe down for(var/PW in wounds_checking) @@ -281,21 +391,22 @@ replaced_wound = existing_wound if(initial(possible_wound.threshold_minimum) < injury_roll) + var/datum/wound/new_wound if(replaced_wound) - var/datum/wound/new_wound = replaced_wound.replace_wound(possible_wound) - log_wound(owner, new_wound, damage, wound_bonus, bare_wound_bonus, base_roll) + new_wound = replaced_wound.replace_wound(possible_wound) + 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 else - var/datum/wound/new_wound = new possible_wound + new_wound = new possible_wound new_wound.apply_wound(src) log_wound(owner, new_wound, damage, wound_bonus, bare_wound_bonus, base_roll) - return + 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) var/datum/wound/potential_wound = specific_woundtype for(var/i in wounds) var/datum/wound/existing_wound = i - if(existing_wound.type in (initial(potential_wound.wound_type))) + if(existing_wound.wound_type == initial(potential_wound.wound_type)) 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(potential_wound, smited) return @@ -303,12 +414,20 @@ var/datum/wound/new_wound = new potential_wound new_wound.apply_wound(src, smited = smited) +/** + * check_wounding_mods() is where we handle the various modifiers of a wound roll + * + * A short list of things we consider: any armor a human target may be wearing, and if they have no wound armor on the limb, if we have a bare_wound_bonus to apply, plus the plain wound_bonus + * We also flick through all of the wounds we currently have on this limb and add their threshold penalties, so that having lots of bad wounds makes you more liable to get hurt worse + * Lastly, we add the inherent wound_resistance variable the bodypart has (heads and chests are slightly harder to wound), and a small bonus if the limb is already disabled + * + * Arguments: + * * It's the same ones on [receive_damage] + */ /obj/item/bodypart/proc/check_woundings_mods(wounding_type, damage, wound_bonus, bare_wound_bonus) var/armor_ablation = 0 var/injury_mod = 0 - //var/bwb = 0 - if(owner && ishuman(owner)) var/mob/living/carbon/human/H = owner var/list/clothing = H.clothingonpart(src) @@ -316,7 +435,7 @@ var/obj/item/clothing/C = c // unlike normal armor checks, we tabluate these piece-by-piece manually so we can also pass on appropriate damage the clothing's limbs if necessary armor_ablation += C.armor.getRating("wound") - if(wounding_type == WOUND_SHARP) + if(wounding_type == WOUND_SLASH) C.take_damage_zone(body_zone, damage, BRUTE, armour_penetration) else if(wounding_type == WOUND_BURN && damage >= 10) // lazy way to block freezing from shredding clothes without adding another var onto apply_damage() C.take_damage_zone(body_zone, damage, BURN, armour_penetration) @@ -332,7 +451,7 @@ injury_mod += W.threshold_penalty var/part_mod = -wound_resistance - if(is_disabled()) + if(get_damage(TRUE) >= max_damage) part_mod += disabled_wound_penalty injury_mod += part_mod @@ -382,12 +501,11 @@ if(can_dismember() && !HAS_TRAIT(owner, TRAIT_NOLIMBDISABLE)) . = disabled //inertia, to avoid limbs healing 0.1 damage and being re-enabled - // TODO: figure if i'm keeping disabling to broken bones only - if((get_damage(TRUE) >= max_damage) || (HAS_TRAIT(owner, TRAIT_EASYLIMBDISABLE) && (get_damage(TRUE) >= (max_damage * 0.6)))) //Easy limb disable disables the limb at 40% health instead of 0% + if(get_damage(TRUE) >= max_damage * (HAS_TRAIT(owner, TRAIT_EASYLIMBDISABLE) ? 0.6 : 1)) //Easy limb disable disables the limb at 40% health instead of 0% if(!last_maxed) owner.emote("scream") last_maxed = TRUE - if(!is_organic_limb()) + if(!is_organic_limb() || stamina_dam >= max_damage) return BODYPART_DISABLED_DAMAGE else if(disabled && (get_damage(TRUE) <= (max_damage * 0.8))) // reenabled at 80% now instead of 50% as of wounds update last_maxed = FALSE @@ -603,18 +721,29 @@ if(isnull(wounds)) return - for(var/thing in wounds) - var/datum/wound/W = thing - if(istype(W, checking_type)) - return W + for(var/i in wounds) + if(istype(i, checking_type)) + return i -/// very rough start for updating efficiency and other stats on a body part whenever a wound is gained/lost -/obj/item/bodypart/proc/update_wounds() +/** + * update_wounds() is called whenever a wound is gained or lost on this bodypart, as well as if there's a change of some kind on a bone wound possibly changing disabled status + * + * Covers tabulating the damage multipliers we have from wounds (burn specifically), as well as deleting our gauze wrapping if we don't have any wounds that can use bandaging + * + * Arguments: + * * replaced- If true, this is being called from the remove_wound() of a wound that's being replaced, so the bandage that already existed is still relevant, but the new wound hasn't been added yet + */ +/obj/item/bodypart/proc/update_wounds(replaced = FALSE) var/dam_mul = 1 //initial(wound_damage_multiplier) - // we can only have one wound per type, but remember there's multiple types - for(var/datum/wound/W in wounds) - dam_mul *= W.damage_mulitplier_penalty + // we can (normally) only have one wound per type, but remember there's multiple types (smites like :B:loodless can generate multiple cuts on a limb) + for(var/i in wounds) + var/datum/wound/iter_wound = i + dam_mul *= iter_wound.damage_mulitplier_penalty + + if(!LAZYLEN(wounds) && current_gauze && !replaced) + owner.visible_message("\The [current_gauze] on [owner]'s [name] fall away.", "The [current_gauze] on your [name] fall away.") + QDEL_NULL(current_gauze) wound_damage_multiplier = dam_mul update_disabled() @@ -627,9 +756,6 @@ if(generic_bleedstacks > 0) bleed_rate++ - if(brute_dam >= 40) - bleed_rate += (brute_dam * 0.008) - //We want an accurate reading of .len listclearnulls(embedded_objects) for(var/obj/item/embeddies in embedded_objects) @@ -640,4 +766,40 @@ var/datum/wound/W = thing bleed_rate += W.blood_flow + if(owner.mobility_flags & ~MOBILITY_STAND) + bleed_rate *= 0.75 return bleed_rate + +/** + * apply_gauze() is used to- well, apply gauze to a bodypart + * + * As of the Wounds 2 PR, all bleeding is now bodypart based rather than the old bleedstacks system, and 90% of standard bleeding comes from flesh wounds (the exception is embedded weapons). + * The same way bleeding is totaled up by bodyparts, gauze now applies to all wounds on the same part. Thus, having a slash wound, a pierce wound, and a broken bone wound would have the gauze + * applying blood staunching to the first two wounds, while also acting as a sling for the third one. Once enough blood has been absorbed or all wounds with the ACCEPTS_GAUZE flag have been cleared, + * the gauze falls off. + * + * Arguments: + * * gauze- Just the gauze stack we're taking a sheet from to apply here + */ +/obj/item/bodypart/proc/apply_gauze(obj/item/stack/gauze) + if(!istype(gauze) || !gauze.absorption_capacity) + return + QDEL_NULL(current_gauze) + current_gauze = new gauze.type(src, 1) + gauze.use(1) + +/** + * seep_gauze() is for when a gauze wrapping absorbs blood or pus from wounds, lowering its absorption capacity. + * + * The passed amount of seepage is deducted from the bandage's absorption capacity, and if we reach a negative absorption capacity, the bandages fall off and we're left with nothing. + * + * Arguments: + * * seep_amt - How much absorption capacity we're removing from our current bandages (think, how much blood or pus are we soaking up this tick?) + */ +/obj/item/bodypart/proc/seep_gauze(seep_amt = 0) + if(!current_gauze) + return + current_gauze.absorption_capacity -= seep_amt + if(current_gauze.absorption_capacity < 0) + owner.visible_message("\The [current_gauze] on [owner]'s [name] fall away in rags.", "\The [current_gauze] on your [name] fall away in rags.", vision_distance=COMBAT_MESSAGE_RANGE) + QDEL_NULL(current_gauze) diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm index 48312107dad..f02c7a86a36 100644 --- a/code/modules/surgery/bodyparts/dismemberment.dm +++ b/code/modules/surgery/bodyparts/dismemberment.dm @@ -4,7 +4,7 @@ return TRUE //Dismember a limb -/obj/item/bodypart/proc/dismember(dam_type = BRUTE) +/obj/item/bodypart/proc/dismember(dam_type = BRUTE, silent=TRUE) if(!owner) return FALSE var/mob/living/carbon/C = owner @@ -17,7 +17,8 @@ var/obj/item/bodypart/affecting = C.get_bodypart(BODY_ZONE_CHEST) affecting.receive_damage(clamp(brute_dam/2 * affecting.body_damage_coeff, 15, 50), clamp(burn_dam/2 * affecting.body_damage_coeff, 0, 50), wound_bonus=CANT_WOUND) //Damage the chest based on limb's existing damage - C.visible_message("[C]'s [src.name] is violently dismembered!") + if(!silent) + C.visible_message("[C]'s [name] is violently dismembered!") C.emote("scream") playsound(get_turf(C), 'sound/effects/dismember.ogg', 80, TRUE) SEND_SIGNAL(C, COMSIG_ADD_MOOD_EVENT, "dismembered", /datum/mood_event/dismembered) @@ -34,6 +35,7 @@ burn() return TRUE add_mob_blood(C) + C.bleed(rand(20, 40)) var/direction = pick(GLOB.cardinals) var/t_range = rand(2,max(throw_range/2, 2)) var/turf/target_turf = get_turf(src) @@ -154,7 +156,52 @@ forceMove(Tsec) +/** + * get_mangled_state() is relevant for flesh and bone bodyparts, and returns whether this bodypart has mangled skin, mangled bone, or both (or neither i guess) + * + * 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 + */ +/obj/item/bodypart/proc/get_mangled_state() + . = BODYPART_MANGLED_NONE + for(var/i in wounds) + var/datum/wound/iter_wound = i + if((iter_wound.wound_flags & MANGLES_BONE)) + . |= BODYPART_MANGLED_BONE + if((iter_wound.wound_flags & MANGLES_FLESH)) + . |= BODYPART_MANGLED_FLESH + +/** + * 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 + * + * Mangling is described in the above proc, [/obj/item/bodypart/proc/get_mangled_state()]. This simply makes the roll for whether we actually dismember or not + * using how damaged the limb already is, and how much damage this blow was for. If we have a critical bone wound instead of just a severe, we add +10% to the roll. + * Lastly, we choose which kind of dismember we want based on the wounding type we hit with. Note we don't care about all the normal mods or armor for this + * + * Arguments: + * * wounding_type: Either WOUND_BLUNT, WOUND_SLASH, or WOUND_PIERCE, basically only matters for the dismember message + * * wounding_dmg: The damage of the strike that prompted this roll, higher damage = higher chance + * * wound_bonus: Not actually used right now, but maybe someday + * * bare_wound_bonus: ditto above + */ +/obj/item/bodypart/proc/try_dismember(wounding_type, wounding_dmg, wound_bonus, bare_wound_bonus) + if(wounding_dmg < DISMEMBER_MINIMUM_DAMAGE) + return + + var/base_chance = wounding_dmg + (get_damage() / max_damage * 50) // how much damage we dealt with this blow, + 50% of the damage percentage we already had on this bodypart + if(locate(/datum/wound/blunt/critical) in wounds) // we only require a severe bone break, but if there's a critical bone break, we'll add 10% more + base_chance += 10 + + if(!prob(base_chance)) + return + + var/datum/wound/loss/dismembering = new + dismembering.apply_dismember(src, wounding_type) + + return TRUE //when a limb is dropped, the internal organs are removed from the mob and put into the limb /obj/item/organ/proc/transfer_to_limb(obj/item/bodypart/LB, mob/living/carbon/C) @@ -414,4 +461,7 @@ if(!L.attach_limb(src, 1)) qdel(L) return FALSE + var/datum/scar/scaries = new + var/datum/wound/loss/phantom_loss = new // stolen valor, really + scaries.generate(L, phantom_loss) return TRUE diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm index 9abbf3441b6..61ddecefb1f 100644 --- a/code/modules/surgery/bodyparts/head.dm +++ b/code/modules/surgery/bodyparts/head.dm @@ -14,7 +14,6 @@ stam_damage_coeff = 1 max_stamina_damage = 100 wound_resistance = 10 - specific_locations = list("left eyebrow", "cheekbone", "neck", "throat", "jawline", "entire face") scars_covered_by_clothes = FALSE var/mob/living/brain/brainmob = null //The current occupant. @@ -94,7 +93,7 @@ /obj/item/bodypart/head/can_dismember(obj/item/I) - if(!((owner.stat == DEAD) || owner.InFullCritical())) + if(owner && !((owner.stat == DEAD) || owner.InFullCritical())) return FALSE return ..() diff --git a/code/modules/surgery/bodyparts/parts.dm b/code/modules/surgery/bodyparts/parts.dm index bd4abaa7650..3dea81eff1d 100644 --- a/code/modules/surgery/bodyparts/parts.dm +++ b/code/modules/surgery/bodyparts/parts.dm @@ -11,11 +11,10 @@ stam_damage_coeff = 1 max_stamina_damage = 120 var/obj/item/cavity_item - specific_locations = list("upper chest", "lower abdomen", "midsection", "collarbone", "lower back") wound_resistance = 10 /obj/item/bodypart/chest/can_dismember(obj/item/I) - if(!((owner.stat == DEAD) || owner.InFullCritical())) + if(!((owner.stat == DEAD) || owner.InFullCritical()) || !get_organs()) return FALSE return ..() @@ -72,7 +71,6 @@ held_index = 1 px_x = -6 px_y = 0 - specific_locations = list("outer left forearm", "inner left wrist", "left elbow", "left bicep", "left shoulder") /obj/item/bodypart/l_arm/is_disabled() if(HAS_TRAIT(owner, TRAIT_PARALYSIS_L_ARM)) @@ -138,7 +136,6 @@ px_x = 6 px_y = 0 max_stamina_damage = 50 - specific_locations = list("outer right forearm", "inner right wrist", "right elbow", "right bicep", "right shoulder") /obj/item/bodypart/r_arm/is_disabled() if(HAS_TRAIT(owner, TRAIT_PARALYSIS_R_ARM)) @@ -201,7 +198,6 @@ px_x = -2 px_y = 12 max_stamina_damage = 50 - specific_locations = list("inner left thigh", "outer left calf", "outer left hip", " left kneecap", "lower left shin") /obj/item/bodypart/l_leg/is_disabled() if(HAS_TRAIT(owner, TRAIT_PARALYSIS_L_LEG)) @@ -261,7 +257,6 @@ px_x = 2 px_y = 12 max_stamina_damage = 50 - specific_locations = list("inner right thigh", "outer right calf", "outer right hip", "right kneecap", "lower right shin") /obj/item/bodypart/r_leg/is_disabled() if(HAS_TRAIT(owner, TRAIT_PARALYSIS_R_LEG)) diff --git a/code/modules/surgery/bone_mending.dm b/code/modules/surgery/bone_mending.dm index 81d9fa8d97d..0c0083575bb 100644 --- a/code/modules/surgery/bone_mending.dm +++ b/code/modules/surgery/bone_mending.dm @@ -8,7 +8,7 @@ target_mobtypes = list(/mob/living/carbon/human) possible_locs = list(BODY_ZONE_R_ARM,BODY_ZONE_L_ARM,BODY_ZONE_R_LEG,BODY_ZONE_L_LEG,BODY_ZONE_CHEST,BODY_ZONE_HEAD) requires_real_bodypart = TRUE - targetable_wound = /datum/wound/brute/bone/severe + targetable_wound = /datum/wound/blunt/severe /datum/surgery/repair_bone_hairline/can_start(mob/living/user, mob/living/carbon/target) if(..()) @@ -23,7 +23,7 @@ target_mobtypes = list(/mob/living/carbon/human) possible_locs = list(BODY_ZONE_R_ARM,BODY_ZONE_L_ARM,BODY_ZONE_R_LEG,BODY_ZONE_L_LEG,BODY_ZONE_CHEST,BODY_ZONE_HEAD) requires_real_bodypart = TRUE - targetable_wound = /datum/wound/brute/bone/critical + targetable_wound = /datum/wound/blunt/critical /datum/surgery/repair_bone_compound/can_start(mob/living/user, mob/living/carbon/target) if(..()) diff --git a/code/modules/surgery/burn_dressing.dm b/code/modules/surgery/burn_dressing.dm index 1688f7b5d17..8bfa52d2454 100644 --- a/code/modules/surgery/burn_dressing.dm +++ b/code/modules/surgery/burn_dressing.dm @@ -94,7 +94,8 @@ log_combat(user, target, "dressed burns in", addition="INTENT: [uppertext(user.a_intent)]") burn_wound.sanitization += 3 burn_wound.flesh_healing += 5 - burn_wound.force_bandage(tool) + var/obj/item/bodypart/the_part = target.get_bodypart(target_zone) + the_part.apply_gauze(tool) else to_chat(user, "[target] has no burns there!") return ..() diff --git a/code/modules/surgery/repair_puncture.dm b/code/modules/surgery/repair_puncture.dm new file mode 100644 index 00000000000..12aefefc822 --- /dev/null +++ b/code/modules/surgery/repair_puncture.dm @@ -0,0 +1,108 @@ + +/////BURN FIXING SURGERIES////// + +//the step numbers of each of these two, we only currently use the first to switch back and forth due to advancing after finishing steps anyway +#define REALIGN_INNARDS 1 +#define WELD_VEINS 2 + +///// Repair puncture wounds +/datum/surgery/repair_puncture + name = "Repair puncture" + steps = list(/datum/surgery_step/incise, /datum/surgery_step/repair_innards, /datum/surgery_step/seal_veins, /datum/surgery_step/close) // repeat between steps 2 and 3 until healed + target_mobtypes = list(/mob/living/carbon) + possible_locs = list(BODY_ZONE_R_ARM,BODY_ZONE_L_ARM,BODY_ZONE_R_LEG,BODY_ZONE_L_LEG,BODY_ZONE_CHEST,BODY_ZONE_HEAD) + requires_real_bodypart = TRUE + targetable_wound = /datum/wound/pierce + +/datum/surgery/repair_puncture/can_start(mob/living/user, mob/living/carbon/target) + . = ..() + if(.) + var/obj/item/bodypart/targeted_bodypart = target.get_bodypart(user.zone_selected) + var/datum/wound/burn/pierce_wound = targeted_bodypart.get_wound_type(targetable_wound) + return(pierce_wound && pierce_wound.blood_flow > 0) + +//SURGERY STEPS + +///// realign the blood vessels so we can reweld them +/datum/surgery_step/repair_innards + name = "realign blood vessels" + implements = list(TOOL_HEMOSTAT = 100, TOOL_SCALPEL = 85, TOOL_WIRECUTTER = 40) + time = 3 SECONDS + +/datum/surgery_step/repair_innards/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + var/datum/wound/pierce/pierce_wound = surgery.operated_wound + if(!pierce_wound) + user.visible_message("[user] looks for [target]'s [parse_zone(user.zone_selected)].", "You look for [target]'s [parse_zone(user.zone_selected)]...") + return + + if(pierce_wound.blood_flow <= 0) + to_chat(user, "[target]'s [parse_zone(user.zone_selected)] has no puncture to repair!") + surgery.status++ + return + + display_results(user, target, "You begin to realign the torn blood vessels in [target]'s [parse_zone(user.zone_selected)]...", + "[user] begins to realign the torn blood vessels in [target]'s [parse_zone(user.zone_selected)] with [tool].", + "[user] begins to realign the torn blood vessels in [target]'s [parse_zone(user.zone_selected)].") + +/datum/surgery_step/repair_innards/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery, default_display_results = FALSE) + var/datum/wound/pierce/pierce_wound = surgery.operated_wound + if(!pierce_wound) + to_chat(user, "[target] has no puncture wound there!") + return ..() + + display_results(user, target, "You successfully realign some of the blood vessels in [target]'s [parse_zone(target_zone)].", + "[user] successfully realigns some of the blood vessels in [target]'s [parse_zone(target_zone)] with [tool]!", + "[user] successfully realigns some of the blood vessels in [target]'s [parse_zone(target_zone)]!") + log_combat(user, target, "excised infected flesh in", addition="INTENT: [uppertext(user.a_intent)]") + surgery.operated_bodypart.receive_damage(brute=3, wound_bonus=CANT_WOUND) + pierce_wound.blood_flow -= 0.25 + return ..() + +/datum/surgery_step/repair_innards/failure(mob/user, mob/living/target, target_zone, obj/item/tool, datum/surgery/surgery, var/fail_prob = 0) + . = ..() + display_results(user, target, "You jerk apart some of the blood vessels in [target]'s [parse_zone(target_zone)].", + "[user] jerks apart some of the blood vessels in [target]'s [parse_zone(target_zone)] with [tool]!", + "[user] jerk apart some of the blood vessels in [target]'s [parse_zone(target_zone)]!") + surgery.operated_bodypart.receive_damage(brute=rand(4,8), sharpness=SHARP_EDGED, wound_bonus = 10) + +///// Sealing the vessels back together +/datum/surgery_step/seal_veins + name = "weld veins" // if your doctor says they're going to weld your blood vessels back together, you're either A) on SS13, or B) in grave mortal peril + implements = list(TOOL_CAUTERY = 100, /obj/item/gun/energy/laser = 90, TOOL_WELDER = 70, /obj/item = 30) + time = 4 SECONDS + +/datum/surgery_step/seal_veins/tool_check(mob/user, obj/item/tool) + if(implement_type == TOOL_WELDER || implement_type == /obj/item) + return tool.get_temperature() + + return TRUE + +/datum/surgery_step/seal_veins/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + var/datum/wound/pierce/pierce_wound = surgery.operated_wound + if(!pierce_wound) + user.visible_message("[user] looks for [target]'s [parse_zone(user.zone_selected)].", "You look for [target]'s [parse_zone(user.zone_selected)]...") + return + display_results(user, target, "You begin to meld some of the split blood vessels in [target]'s [parse_zone(user.zone_selected)]...", + "[user] begins to meld some of the split blood vessels in [target]'s [parse_zone(user.zone_selected)] with [tool].", + "[user] begins to meld some of the split blood vessels in [target]'s [parse_zone(user.zone_selected)].") + +/datum/surgery_step/seal_veins/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery, default_display_results = FALSE) + var/datum/wound/pierce/pierce_wound = surgery.operated_wound + if(!pierce_wound) + to_chat(user, "[target] has no puncture there!") + return ..() + + display_results(user, target, "You successfully meld some of the split blood vessels in [target]'s [parse_zone(target_zone)] with [tool].", + "[user] successfully melds some of the split blood vessels in [target]'s [parse_zone(target_zone)] with [tool]!", + "[user] successfully melds some of the split blood vessels in [target]'s [parse_zone(target_zone)]!") + log_combat(user, target, "dressed burns in", addition="INTENT: [uppertext(user.a_intent)]") + pierce_wound.blood_flow -= 0.5 + if(pierce_wound.blood_flow > 0) + surgery.status = REALIGN_INNARDS + to_chat(user, "There still seems to be misaligned blood vessels to finish...") + else + to_chat(user, "You've repaired all the internal damage in [target]'s [parse_zone(target_zone)]!") + return ..() + +#undef REALIGN_INNARDS +#undef WELD_VEINS diff --git a/code/modules/surgery/tools.dm b/code/modules/surgery/tools.dm index aad9b0a1da1..defacbe1cd4 100644 --- a/code/modules/surgery/tools.dm +++ b/code/modules/surgery/tools.dm @@ -110,7 +110,7 @@ custom_materials = list(/datum/material/iron=4000, /datum/material/glass=1000) attack_verb = list("attacked", "slashed", "stabbed", "sliced", "tore", "lacerated", "ripped", "diced", "cut") hitsound = 'sound/weapons/bladeslice.ogg' - sharpness = IS_SHARP_ACCURATE + sharpness = SHARP_EDGED tool_behaviour = TOOL_SCALPEL toolspeed = 1 bare_wound_bonus = 20 @@ -146,7 +146,7 @@ throw_range = 5 custom_materials = list(/datum/material/iron=1000) attack_verb = list("attacked", "slashed", "sawed", "cut") - sharpness = IS_SHARP + sharpness = SHARP_EDGED tool_behaviour = TOOL_SAW toolspeed = 1 wound_bonus = 10 @@ -259,7 +259,7 @@ force = 16 toolspeed = 0.7 light_color = LIGHT_COLOR_GREEN - sharpness = IS_SHARP_ACCURATE + sharpness = SHARP_EDGED /obj/item/scalpel/advanced/Initialize() . = ..() @@ -349,7 +349,7 @@ throw_range = 5 custom_materials = list(/datum/material/iron=8000, /datum/material/titanium=6000) attack_verb = list("sheared", "snipped") - sharpness = IS_SHARP + sharpness = SHARP_EDGED custom_premium_price = 1800 /obj/item/shears/attack(mob/living/M, mob/user) diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm index ee4ed492df1..fa4c2f20f77 100644 --- a/code/modules/vending/_vending.dm +++ b/code/modules/vending/_vending.dm @@ -570,8 +570,7 @@ GLOBAL_LIST_EMPTY(vending_products) for(var/i in C.bodyparts) var/obj/item/bodypart/squish_part = i if(squish_part.is_organic_limb()) - //var/type_wound = pick(WOUND_LIST_BONE) - var/type_wound = pick(list(/datum/wound/brute/bone/critical, /datum/wound/brute/bone/severe, /datum/wound/brute/bone/critical, /datum/wound/brute/bone/severe, /datum/wound/brute/bone/moderate)) + var/type_wound = pick(list(/datum/wound/blunt/critical, /datum/wound/blunt/severe, /datum/wound/blunt/moderate)) squish_part.force_wound_upwards(type_wound) else squish_part.receive_damage(brute=30) diff --git a/code/modules/zombie/items.dm b/code/modules/zombie/items.dm index 1c97853c466..ce844bf7db2 100644 --- a/code/modules/zombie/items.dm +++ b/code/modules/zombie/items.dm @@ -12,7 +12,7 @@ var/icon_right = "bloodhand_right" hitsound = 'sound/hallucinations/growl1.ogg' force = 21 // Just enough to break airlocks with melee attacks - sharpness = IS_SHARP + sharpness = SHARP_EDGED wound_bonus = -30 bare_wound_bonus = 15 damtype = "brute" diff --git a/interface/stylesheet.dm b/interface/stylesheet.dm index cbad9a89ff3..50c644f01cd 100644 --- a/interface/stylesheet.dm +++ b/interface/stylesheet.dm @@ -61,7 +61,10 @@ h1.alert, h2.alert {color: #000000;} .emote { font-style: italic;} .userdanger {color: #ff0000; font-weight: bold; font-size: 3;} +.danger {color: #ff0000; font-weight: bold;} .danger {color: #ff0000;} +.tinydanger {color: #ff0000; font-size: 85%;} +.smalldanger {color: #ff0000; font-size: 90%;} .warning {color: #ff0000; font-style: italic;} .boldwarning {color: #ff0000; font-style: italic; font-weight: bold} .announce {color: #228b22; font-weight: bold;} @@ -70,6 +73,9 @@ h1.alert, h2.alert {color: #000000;} .rose {color: #ff5050;} .info {color: #0000CC;} .notice {color: #000099;} +.tinynotice {color: #000099; font-size: 85%;} +.smallnotice {color: #000099; font-size: 90%;} +.smallnoticeital {color: #000099; font-style: italic; font-size: 90%;} .boldnotice {color: #000099; font-weight: bold;} .hear {color: #000099; font-style: italic;} .adminnotice {color: #0000ff;} diff --git a/sound/effects/blood1.ogg b/sound/effects/wounds/blood1.ogg similarity index 100% rename from sound/effects/blood1.ogg rename to sound/effects/wounds/blood1.ogg diff --git a/sound/effects/blood2.ogg b/sound/effects/wounds/blood2.ogg similarity index 100% rename from sound/effects/blood2.ogg rename to sound/effects/wounds/blood2.ogg diff --git a/sound/effects/blood3.ogg b/sound/effects/wounds/blood3.ogg similarity index 100% rename from sound/effects/blood3.ogg rename to sound/effects/wounds/blood3.ogg diff --git a/sound/effects/crack1.ogg b/sound/effects/wounds/crack1.ogg similarity index 100% rename from sound/effects/crack1.ogg rename to sound/effects/wounds/crack1.ogg diff --git a/sound/effects/crack2.ogg b/sound/effects/wounds/crack2.ogg similarity index 100% rename from sound/effects/crack2.ogg rename to sound/effects/wounds/crack2.ogg diff --git a/sound/effects/wounds/crackandbleed.ogg b/sound/effects/wounds/crackandbleed.ogg new file mode 100644 index 0000000000000000000000000000000000000000..ea07f13d482087be78a525d993bf912626925e46 GIT binary patch literal 15662 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�U|E2pS-PVJnM8WxMioIfus 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 S<^2F2lVd6ny$diMr~d^L?J`vW literal 0 HcmV?d00001 diff --git a/sound/effects/wounds/pierce1.ogg b/sound/effects/wounds/pierce1.ogg new file mode 100644 index 0000000000000000000000000000000000000000..cd7b7c39610214fd14ae4e6bb2e267f9f91fb3b7 GIT binary patch literal 8726 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 literal 0 HcmV?d00001 diff --git a/sound/effects/wounds/pierce2.ogg b/sound/effects/wounds/pierce2.ogg new file mode 100644 index 0000000000000000000000000000000000000000..4977cab299f273a74f00de7c2f7425e04c150ede GIT binary patch literal 17779 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 SldO-Z&s+!&4h{|u4h{~SE}iWF literal 0 HcmV?d00001 diff --git a/sound/effects/sizzle1.ogg b/sound/effects/wounds/sizzle1.ogg similarity index 100% rename from sound/effects/sizzle1.ogg rename to sound/effects/wounds/sizzle1.ogg diff --git a/sound/effects/sizzle2.ogg b/sound/effects/wounds/sizzle2.ogg similarity index 100% rename from sound/effects/sizzle2.ogg rename to sound/effects/wounds/sizzle2.ogg diff --git a/strings/wounds/bone_scar_desc.json b/strings/wounds/bone_scar_desc.json new file mode 100644 index 00000000000..b1eb84bb8b7 --- /dev/null +++ b/strings/wounds/bone_scar_desc.json @@ -0,0 +1,26 @@ +{ + "generic": ["general disfigurement"], + + "bluntmoderate": [ + "the bone equivalent of a faded bruise", + "a series of tiny chip marks" + ], + + "bluntsevere": [ + "a series of faded hairline cracks", + "a small bone dent" + ], + + "bluntcritical": [ + "large streaks of refilled cracks", + "a fractal of reformed stress marks", + "a cluster of calluses" + ], + + "dismember": [ + "is slightly misaligned", + "has clearly been dropped recently", + "has a damaged socket" + ] + +} diff --git a/strings/wounds/flesh_scar_desc.json b/strings/wounds/flesh_scar_desc.json new file mode 100644 index 00000000000..047e614b790 --- /dev/null +++ b/strings/wounds/flesh_scar_desc.json @@ -0,0 +1,86 @@ +{ + "generic": ["general disfigurement"], + + "bluntmoderate": [ + "light discoloring", + "a slight blue tint" + ], + + "bluntsevere": [ + "a faded, fist-sized bruise", + "a vaguely triangular peel scar" + ], + + "bluntcritical": [ + "a section of janky skin lines and badly healed scars", + "a large patch of uneven skin tone", + "a cluster of calluses" + ], + + + + "slashmoderate": [ + "light, faded lines", + "minor cut marks", + "a small faded slit", + "a series of small scars" + ], + + "slashsevere": [ + "a twisted line of faded gashes", + "a gnarled sickle-shaped slice scar" + ], + + "slashcritical": [ + "a winding path of very badly healed scar tissue", + "a series of peaks and valleys along a gruesome line of cut scar tissue", + "a grotesque snake of indentations and stitching scars" + ], + + + + "piercemoderate": [ + "a small, faded bruise", + "a small twist of reformed skin", + "a thumb-sized puncture scar" + ], + + "piercesevere": [ + "an ink-splat shaped pocket of scar tissue", + "a long-faded puncture wound", + "a tumbling puncture hole with evidence of faded stitching" + ], + + "piercecritical": [ + "a rippling shockwave of scar tissue", + "a wide, scattered cloud of shrapnel marks", + "a gruesome multi-pronged puncture scar" + ], + + + + "burnmoderate": [ + "small amoeba-shaped skinmarks", + "a faded streak of depressed skin" + ], + + "burnsevere": [ + "a large, jagged patch of faded skin", + "random spots of shiny, smooth skin", + "spots of taut, leathery skin" + ], + + "burncritical": [ + "massive, disfiguring keloid scars", + "several long streaks of badly discolored and malformed skin", + "unmistakeable splotches of dead tissue from serious burns" + ], + + + "dismember": [ + "is several skintone shades paler than the rest of the body", + "is a gruesome patchwork of artificial flesh", + "has a large series of attachment scars at the articulation points" + ] + +} diff --git a/strings/wounds/scar_loc.json b/strings/wounds/scar_loc.json new file mode 100644 index 00000000000..4d1a639a9a1 --- /dev/null +++ b/strings/wounds/scar_loc.json @@ -0,0 +1,52 @@ +{ + "": ["general area"], + + "head": [ + "left eyebrow", + "cheekbone", + "neck", + "throat", + "jawline", + "entire face" + ], + + "chest": [ + "upper chest", + "lower abdomen", + "midsection", + "collarbone", + "lower back" + ], + + "l_arm": [ + "outer left forearm", + "inner left wrist", + "left elbow", + "left bicep", + "left shoulder" + ], + + "r_arm": [ + "outer right forearm", + "inner right wrist", + "right elbow", + "right bicep", + "right shoulder" + ], + + "l_leg": [ + "inner left thigh", + "outer left calf", + "outer left hip", + "left kneecap", + "lower left shin" + ], + + "r_leg": [ + "inner right thigh", + "outer right calf", + "outer right hip", + "right kneecap", + "lower right shin" + ] +} diff --git a/tgstation.dme b/tgstation.dme index 678ef083b27..327f19a8065 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -654,7 +654,9 @@ #include "code\datums\wounds\_wounds.dm" #include "code\datums\wounds\bones.dm" #include "code\datums\wounds\burns.dm" -#include "code\datums\wounds\cuts.dm" +#include "code\datums\wounds\loss.dm" +#include "code\datums\wounds\pierce.dm" +#include "code\datums\wounds\slash.dm" #include "code\datums\wounds\scars\_scars.dm" #include "code\game\alternate_appearance.dm" #include "code\game\atoms.dm" @@ -3020,6 +3022,7 @@ #include "code\modules\surgery\plastic_surgery.dm" #include "code\modules\surgery\prosthetic_replacement.dm" #include "code\modules\surgery\remove_embedded_object.dm" +#include "code\modules\surgery\repair_puncture.dm" #include "code\modules\surgery\revival.dm" #include "code\modules\surgery\stomachpump.dm" #include "code\modules\surgery\surgery.dm"