From a4478b0cdab72ca7b6066f62be53e5c4e6ebe38c Mon Sep 17 00:00:00 2001
From: Timothy Teakettle <59849408+timothyteakettle@users.noreply.github.com>
Date: Fri, 17 Jul 2020 22:38:28 +0100
Subject: [PATCH] it continues
---
code/game/objects/items/stacks/stack.dm | 9 +
code/game/objects/items/stacks/tape.dm | 16 +-
code/game/objects/items/storage/backpack.dm | 3 +
code/game/objects/items/storage/belt.dm | 5 +-
code/game/objects/items/storage/firstaid.dm | 20 ++-
code/game/objects/items/storage/toolbox.dm | 1 +
code/game/objects/items/weaponry.dm | 4 +-
code/game/objects/objs.dm | 5 +
code/game/objects/structures/guillotine.dm | 2 +-
code/game/objects/structures/tables_racks.dm | 32 ++--
code/modules/admin/verbs/randomverbs.dm | 32 +++-
.../blob/blob/blobstrains/blazing_oil.dm | 2 +-
.../blob/blob/blobstrains/cryogenic_poison.dm | 2 +-
.../blob/blobstrains/electromagnetic_web.dm | 2 +-
.../blob/blobstrains/explosive_lattice.dm | 6 +-
.../blob/blob/blobstrains/networked_fibers.dm | 4 +-
.../blob/blobstrains/pressurized_slime.dm | 2 +-
.../blob/blob/blobstrains/replicating_foam.dm | 2 +-
.../blob/blobstrains/shifting_fragments.dm | 2 +-
.../blob/blob/blobstrains/synchronous_mesh.dm | 4 +-
.../changeling/powers/fleshmend.dm | 2 +-
.../changeling/powers/mutations.dm | 2 +
.../changeling/powers/regenerate.dm | 3 +
code/modules/antagonists/cult/cult_items.dm | 6 +-
.../antagonists/slaughter/slaughter.dm | 48 ++++-
.../antagonists/slaughter/slaughter_antag.dm | 1 +
.../components/unary_devices/cryo.dm | 32 +++-
code/modules/cargo/packs/goodies.dm | 6 +
code/modules/cargo/packs/medical.dm | 4 +-
code/modules/client/preferences.dm | 21 +++
code/modules/client/preferences_savefile.dm | 23 +++
code/modules/clothing/clothing.dm | 165 ++++++++++++++++--
code/modules/clothing/gloves/_gloves.dm | 2 +-
code/modules/clothing/head/_head.dm | 2 +-
code/modules/clothing/masks/_masks.dm | 2 +-
code/modules/clothing/shoes/_shoes.dm | 2 +-
code/modules/clothing/suits/_suits.dm | 3 +-
code/modules/clothing/suits/armor.dm | 19 +-
code/modules/clothing/suits/wiz_robe.dm | 2 +-
code/modules/clothing/under/_under.dm | 5 +-
code/modules/clothing/under/jobs/cargo.dm | 3 +-
code/modules/clothing/under/jobs/command.dm | 1 +
code/modules/clothing/under/jobs/security.dm | 8 +-
.../browserassets/css/browserOutput.css | 2 +
code/modules/hydroponics/grown/nettle.dm | 1 +
.../jobs/job_types/chief_medical_officer.dm | 2 +-
code/modules/jobs/job_types/paramedic.dm | 2 +-
.../modules/mining/equipment/explorer_gear.dm | 4 +-
.../modules/mob/dead/new_player/new_player.dm | 13 ++
code/modules/mob/living/blood.dm | 57 +++---
code/modules/mob/living/brain/brain_item.dm | 4 +-
code/modules/mob/living/carbon/carbon.dm | 94 +++++++++-
52 files changed, 560 insertions(+), 136 deletions(-)
diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm
index 16b46567c7..bf6f2aba2e 100644
--- a/code/game/objects/items/stacks/stack.dm
+++ b/code/game/objects/items/stacks/stack.dm
@@ -25,9 +25,18 @@
var/list/mats_per_unit //list that tells you how much is in a single unit.
///Datum material type that this stack is made of
var/material_type
+ max_integrity = 100
//NOTE: When adding grind_results, the amounts should be for an INDIVIDUAL ITEM - these amounts will be multiplied by the stack size in on_grind()
var/obj/structure/table/tableVariant // we tables now (stores table variant to be built from this stack)
+ // The following are all for medical treatment, they're here instead of /stack/medical because sticky tape can be used as a makeshift bandage or splint
+ /// If set and this used as a splint for a broken bone wound, this is used as a multiplier for applicable slowdowns (lower = better) (also for speeding up burn recoveries)
+ var/splint_factor
+ /// How much blood flow this stack can absorb if used as a bandage on a cut wound, note that absorption is how much we lower the flow rate, not the raw amount of blood we suck up
+ var/absorption_capacity
+ /// How quickly we lower the blood flow on a cut wound we're bandaging. Expected lifetime of this bandage in ticks is thus absorption_capacity/absorption_rate, or until the cut heals, whichever comes first
+ var/absorption_rate
+
/obj/item/stack/on_grind()
for(var/i in 1 to grind_results.len) //This should only call if it's ground, so no need to check if grind_results exists
grind_results[grind_results[i]] *= get_amount() //Gets the key at position i, then the reagent amount of that key, then multiplies it by stack size
diff --git a/code/game/objects/items/stacks/tape.dm b/code/game/objects/items/stacks/tape.dm
index d33d106d11..ed5baf733b 100644
--- a/code/game/objects/items/stacks/tape.dm
+++ b/code/game/objects/items/stacks/tape.dm
@@ -7,10 +7,13 @@
icon = 'icons/obj/tapes.dmi'
icon_state = "tape_w"
var/prefix = "sticky"
+ w_class = WEIGHT_CLASS_TINY
+ full_w_class = WEIGHT_CLASS_TINY
item_flags = NOBLUDGEON
amount = 5
max_amount = 5
resistance_flags = FLAMMABLE
+ splint_factor = 0.8
var/list/conferred_embed = EMBED_HARMLESS
var/overwrite_existing = FALSE
@@ -53,6 +56,7 @@
icon_state = "tape_y"
prefix = "super sticky"
conferred_embed = EMBED_HARMLESS_SUPERIOR
+ splint_factor = 0.6
/obj/item/stack/sticky_tape/pointy
name = "pointy tape"
@@ -68,4 +72,14 @@
desc = "You didn't know tape could look so sinister. Welcome to Space Station 13."
icon_state = "tape_spikes"
prefix = "super pointy"
- conferred_embed = EMBED_POINTY_SUPERIOR
\ No newline at end of file
+ conferred_embed = EMBED_POINTY_SUPERIOR
+
+/obj/item/stack/sticky_tape/surgical
+ name = "surgical tape"
+ singular_name = "surgical tape"
+ desc = "Made for patching broken bones back together alongside bone gel, not for playing pranks."
+ //icon_state = "tape_spikes"
+ prefix = "surgical"
+ conferred_embed = list("embed_chance" = 30, "pain_mult" = 0, "jostle_pain_mult" = 0, "ignore_throwspeed_threshold" = TRUE)
+ splint_factor = 0.4
+ custom_price = 500
diff --git a/code/game/objects/items/storage/backpack.dm b/code/game/objects/items/storage/backpack.dm
index 3d5f0dc924..2c2c315fc9 100644
--- a/code/game/objects/items/storage/backpack.dm
+++ b/code/game/objects/items/storage/backpack.dm
@@ -369,6 +369,7 @@
new /obj/item/circular_saw(src)
new /obj/item/surgicaldrill(src)
new /obj/item/cautery(src)
+ new /obj/item/bonesetter(src)
new /obj/item/surgical_drapes(src)
new /obj/item/clothing/mask/surgical(src)
new /obj/item/reagent_containers/medspray/sterilizine(src)
@@ -391,6 +392,7 @@
new /obj/item/circular_saw(src)
new /obj/item/surgicaldrill(src)
new /obj/item/cautery(src)
+ new /obj/item/bonesetter(src)
new /obj/item/surgical_drapes(src)
new /obj/item/clothing/mask/surgical(src)
new /obj/item/reagent_containers/medspray/sterilizine(src)
@@ -485,6 +487,7 @@
new /obj/item/circular_saw(src)
new /obj/item/surgicaldrill(src)
new /obj/item/cautery(src)
+ new /obj/item/bonesetter(src)
new /obj/item/surgical_drapes(src)
new /obj/item/clothing/suit/straight_jacket(src)
new /obj/item/clothing/mask/muzzle(src)
diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm
index 4fa742df46..52e8e9193f 100755
--- a/code/game/objects/items/storage/belt.dm
+++ b/code/game/objects/items/storage/belt.dm
@@ -162,6 +162,7 @@
/obj/item/surgical_drapes, //for true paramedics
/obj/item/scalpel,
/obj/item/circular_saw,
+ /obj/item/bonesetter,
/obj/item/surgicaldrill,
/obj/item/retractor,
/obj/item/cautery,
@@ -181,7 +182,8 @@
/obj/item/implant,
/obj/item/implanter,
/obj/item/pinpointer/crew,
- /obj/item/reagent_containers/chem_pack
+ /obj/item/reagent_containers/chem_pack,
+ /obj/item/stack/sticky_tape //surgical tape
))
/obj/item/storage/belt/medical/surgery_belt_adv
@@ -811,4 +813,3 @@
attack_verb = list("bashed", "slashes", "prods", "pokes")
fitting_swords = list(/obj/item/melee/rapier)
starting_sword = /obj/item/melee/rapier
-
diff --git a/code/game/objects/items/storage/firstaid.dm b/code/game/objects/items/storage/firstaid.dm
index d7065df0f0..fb1313fc70 100644
--- a/code/game/objects/items/storage/firstaid.dm
+++ b/code/game/objects/items/storage/firstaid.dm
@@ -41,9 +41,26 @@
new /obj/item/stack/medical/suture(src)
new /obj/item/stack/medical/mesh(src)
new /obj/item/stack/medical/mesh(src)
- new /obj/item/reagent_containers/hypospray/medipen(src)
+ new /obj/item/reagent_containers/hypospray/medipen/ekit(src)
new /obj/item/healthanalyzer(src)
+/obj/item/storage/firstaid/emergency
+ icon_state = "medbriefcase"
+ name = "emergency first-aid kit"
+ desc = "A very simple first aid kit meant to secure and stabilize serious wounds for later treatment."
+
+/obj/item/storage/firstaid/emergency/PopulateContents()
+ if(empty)
+ return
+ var/static/items_inside = list(
+ /obj/item/healthanalyzer/wound = 1,
+ /obj/item/stack/medical/gauze = 1,
+ /obj/item/stack/medical/suture/emergency = 1,
+ /obj/item/stack/medical/ointment = 1,
+ /obj/item/reagent_containers/hypospray/medipen/ekit = 2,
+ /obj/item/storage/pill_bottle/iron = 1)
+ generate_items_inside(items_inside,src)
+
/obj/item/storage/firstaid/ancient
icon_state = "firstaid"
desc = "A first aid kit with the ability to heal common types of injuries."
@@ -416,6 +433,7 @@
/obj/item/retractor,
/obj/item/cautery,
/obj/item/surgical_drapes,
+ /obj/item/bonesetter,
/obj/item/autosurgeon,
/obj/item/organ,
/obj/item/implant,
diff --git a/code/game/objects/items/storage/toolbox.dm b/code/game/objects/items/storage/toolbox.dm
index fd50bd022f..5e3ff67f4c 100644
--- a/code/game/objects/items/storage/toolbox.dm
+++ b/code/game/objects/items/storage/toolbox.dm
@@ -20,6 +20,7 @@ GLOBAL_LIST_EMPTY(rubber_toolbox_icons)
var/has_latches = TRUE
var/can_rubberify = TRUE
rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE //very protecc too
+ wound_bonus = 5
/obj/item/storage/toolbox/greyscale
icon_state = "toolbox_default"
diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm
index 7524bb93e1..b23b7b0c24 100644
--- a/code/game/objects/items/weaponry.dm
+++ b/code/game/objects/items/weaponry.dm
@@ -574,6 +574,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
force = 10
+ wound_bonus = -10
throwforce = 12
attack_verb = list("beat", "smacked")
custom_materials = list(/datum/material/wood = MINERAL_MATERIAL_AMOUNT * 3.5)
@@ -626,7 +627,8 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
homerun_ready = 0
return
else if(!target.anchored)
- target.throw_at(throw_target, rand(1,2), 7, user)
+ var/whack_speed = (prob(60) ? 1 : 4)
+ target.throw_at(throw_target, rand(1, 2), whack_speed, user) // sorry friends, 7 speed batting caused wounds to absolutely delete whoever you knocked your target into (and said target)
/obj/item/melee/baseball_bat/ablative
name = "metal baseball bat"
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index ba3eba9bd3..c54c49b7e5 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -10,6 +10,11 @@
var/damtype = BRUTE
var/force = 0
+ /// How good a given object is at causing wounds on carbons. Higher values equal better shots at creating serious wounds.
+ var/wound_bonus = 0
+ /// If this attacks a human with no wound armor on the affected body part, add this to the wound mod. Some attacks may be significantly worse at wounding if there's even a slight layer of armor to absorb some of it vs bare flesh
+ var/bare_wound_bonus = 0
+
var/datum/armor/armor
var/obj_integrity //defaults to max_integrity
var/max_integrity = 500
diff --git a/code/game/objects/structures/guillotine.dm b/code/game/objects/structures/guillotine.dm
index 0a5ba6a68c..029cc67c81 100644
--- a/code/game/objects/structures/guillotine.dm
+++ b/code/game/objects/structures/guillotine.dm
@@ -108,7 +108,7 @@
if (QDELETED(head))
return
- playsound(src, 'sound/weapons/bladeslice.ogg', 100, 1)
+ playsound(src, 'sound/weapons/guillotine.ogg', 100, TRUE)
if (blade_sharpness >= GUILLOTINE_DECAP_MIN_SHARP || head.brute_dam >= 100)
head.dismember()
log_combat(user, H, "beheaded", src)
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index 777be608b5..50c58b0ade 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -71,7 +71,7 @@
if(user.grab_state < GRAB_AGGRESSIVE)
to_chat(user, "You need a better grip to do that!")
return
- tablepush(user, pushed_mob)
+ tablelimbsmash(user, pushed_mob)
if(user.a_intent == INTENT_HELP)
pushed_mob.visible_message("[user] begins to place [pushed_mob] onto [src]...", \
"[user] begins to place [pushed_mob] onto [src]...")
@@ -114,29 +114,17 @@
"[user] places [pushed_mob] onto [src].")
log_combat(user, pushed_mob, "placed")
-/obj/structure/table/proc/tablepush(mob/living/user, mob/living/pushed_mob)
- if(HAS_TRAIT(user, TRAIT_PACIFISM))
+/obj/structure/table/proc/tablelimbsmash(mob/living/user, mob/living/pushed_mob)
+ if(HAS_TRAIT(user, TRAIT_PACIFISM)) //this shouldnt even be possible anyway because you cant get them in an aggressive grab, but whatever
to_chat(user, "Throwing [pushed_mob] onto the table might hurt them!")
return
- var/added_passtable = FALSE
- if(!pushed_mob.pass_flags & PASSTABLE)
- added_passtable = TRUE
- pushed_mob.pass_flags |= PASSTABLE
- pushed_mob.Move(src.loc)
- if(added_passtable)
- pushed_mob.pass_flags &= ~PASSTABLE
- if(pushed_mob.loc != loc) //Something prevented the tabling
- return
- pushed_mob.DefaultCombatKnockdown(40)
- pushed_mob.visible_message("[user] slams [pushed_mob] onto [src]!", \
- "[user] slams you onto [src]!")
- log_combat(user, pushed_mob, "tabled", null, "onto [src]")
- if(!ishuman(pushed_mob))
- return
- var/mob/living/carbon/human/H = pushed_mob
- if(iscatperson(H))
- H.emote("nya")
- SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "table", /datum/mood_event/table)
+ pushed_mob.Knockdown(30)
+ var/obj/item/bodypart/banged_limb = pushed_mob.get_bodypart(user.zone_selected) || pushed_mob.get_bodypart(BODY_ZONE_HEAD)
+ var/extra_wound = 0
+ if(HAS_TRAIT(user, TRAIT_HULK))
+ extra_wound = 20
+ banged_limb.receive_damage(30, wound_bonus = extra_wound)
+ pushed_mob.apply_damage(60, STAMINA)
/obj/structure/table/shove_act(mob/living/target, mob/living/user)
if(CHECK_MOBILITY(target, MOBILITY_STAND))
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index d335cfb171..c5cde07761 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -1276,7 +1276,7 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
if(!check_rights(R_ADMIN) || !check_rights(R_FUN))
return
- var/list/punishment_list = list(ADMIN_PUNISHMENT_PIE, ADMIN_PUNISHMENT_CUSTOM_PIE, ADMIN_PUNISHMENT_FIREBALL, ADMIN_PUNISHMENT_LIGHTNING, ADMIN_PUNISHMENT_BRAINDAMAGE, ADMIN_PUNISHMENT_BSA, ADMIN_PUNISHMENT_GIB, ADMIN_PUNISHMENT_SUPPLYPOD_QUICK, ADMIN_PUNISHMENT_SUPPLYPOD, ADMIN_PUNISHMENT_MAZING, ADMIN_PUNISHMENT_ROD)
+ var/list/punishment_list = list(ADMIN_PUNISHMENT_PIE, ADMIN_PUNISHMENT_CUSTOM_PIE, ADMIN_PUNISHMENT_FIREBALL, ADMIN_PUNISHMENT_LIGHTNING, ADMIN_PUNISHMENT_BRAINDAMAGE, ADMIN_PUNISHMENT_BSA, ADMIN_PUNISHMENT_GIB, ADMIN_PUNISHMENT_SUPPLYPOD_QUICK, ADMIN_PUNISHMENT_SUPPLYPOD, ADMIN_PUNISHMENT_MAZING, ADMIN_PUNISHMENT_ROD, ADMIN_PUNISHMENT_CRACK, ADMIN_PUNISHMENT_BLEED, ADMIN_PUNISHMENT_SCARIFY)
var/punishment = input("Choose a punishment", "DIVINE SMITING") as null|anything in punishment_list
@@ -1341,7 +1341,7 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
if(ADMIN_PUNISHMENT_PIE)
var/obj/item/reagent_containers/food/snacks/pie/cream/nostun/creamy = new(get_turf(target))
creamy.splat(target)
- if (ADMIN_PUNISHMENT_CUSTOM_PIE)
+ if(ADMIN_PUNISHMENT_CUSTOM_PIE)
var/obj/item/reagent_containers/food/snacks/pie/cream/nostun/A = new()
if(!A.reagents)
var/amount = input(usr, "Specify the reagent size of [A]", "Set Reagent Size", 50) as num|null
@@ -1354,7 +1354,33 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
if(amount)
A.reagents.add_reagent(chosen_id, amount)
A.splat(target)
-
+ if(ADMIN_PUNISHMENT_CRACK)
+ 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/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))
+ 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))
+ 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))
+ slice_part.force_wound_upwards(type_wound, smited=TRUE)
+ type_wound = pick(list(/datum/wound/brute/cut/critical, /datum/wound/brute/cut/severe))
+ slice_part.force_wound_upwards(type_wound, smited=TRUE)
+ 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...")
punish_log(target, punishment)
/client/proc/punish_log(var/whom, var/punishment)
diff --git a/code/modules/antagonists/blob/blob/blobstrains/blazing_oil.dm b/code/modules/antagonists/blob/blob/blobstrains/blazing_oil.dm
index 97b974e28f..f97e271e72 100644
--- a/code/modules/antagonists/blob/blob/blobstrains/blazing_oil.dm
+++ b/code/modules/antagonists/blob/blob/blobstrains/blazing_oil.dm
@@ -36,6 +36,6 @@
M.adjust_fire_stacks(round(reac_volume/10))
M.IgniteMob()
if(M)
- M.apply_damage(0.8*reac_volume, BURN)
+ M.apply_damage(0.8*reac_volume, BURN, wound_bonus=CANT_WOUND)
if(iscarbon(M))
M.emote("scream")
diff --git a/code/modules/antagonists/blob/blob/blobstrains/cryogenic_poison.dm b/code/modules/antagonists/blob/blob/blobstrains/cryogenic_poison.dm
index 9b8edcd0e5..f8ef269986 100644
--- a/code/modules/antagonists/blob/blob/blobstrains/cryogenic_poison.dm
+++ b/code/modules/antagonists/blob/blob/blobstrains/cryogenic_poison.dm
@@ -22,7 +22,7 @@
M.reagents.add_reagent("frostoil", 0.3*reac_volume)
M.reagents.add_reagent("ice", 0.3*reac_volume)
M.reagents.add_reagent("cryogenic_poison", 0.3*reac_volume)
- M.apply_damage(0.2*reac_volume, BRUTE)
+ M.apply_damage(0.2*reac_volume, BRUTE, wound_bonus=CANT_WOUND)
/datum/reagent/blob/cryogenic_poison/on_mob_life(mob/living/carbon/M)
M.adjustBruteLoss(0.3*REAGENTS_EFFECT_MULTIPLIER, 0)
diff --git a/code/modules/antagonists/blob/blob/blobstrains/electromagnetic_web.dm b/code/modules/antagonists/blob/blob/blobstrains/electromagnetic_web.dm
index 0e665603c9..45bf2b1e1d 100644
--- a/code/modules/antagonists/blob/blob/blobstrains/electromagnetic_web.dm
+++ b/code/modules/antagonists/blob/blob/blobstrains/electromagnetic_web.dm
@@ -30,4 +30,4 @@
if(prob(reac_volume*2))
M.emp_act(EMP_LIGHT)
if(M)
- M.apply_damage(reac_volume, BURN)
+ M.apply_damage(reac_volume, BURN, wound_bonus=CANT_WOUND)
diff --git a/code/modules/antagonists/blob/blob/blobstrains/explosive_lattice.dm b/code/modules/antagonists/blob/blob/blobstrains/explosive_lattice.dm
index f8fd2e2f0d..3d005ba913 100644
--- a/code/modules/antagonists/blob/blob/blobstrains/explosive_lattice.dm
+++ b/code/modules/antagonists/blob/blob/blobstrains/explosive_lattice.dm
@@ -33,8 +33,8 @@
if(ROLE_BLOB in L.faction) //no friendly fire
continue
var/aoe_volume = ..(L, TOUCH, initial_volume, 0, L.get_permeability_protection(), O)
- L.apply_damage(0.4*aoe_volume, BRUTE)
+ L.apply_damage(0.4*aoe_volume, BRUTE, wound_bonus=CANT_WOUND)
if(M)
- M.apply_damage(0.6*reac_volume, BRUTE)
+ M.apply_damage(0.6*reac_volume, BRUTE, wound_bonus=CANT_WOUND)
else
- M.apply_damage(0.6*reac_volume, BRUTE)
+ M.apply_damage(0.6*reac_volume, BRUTE, wound_bonus=CANT_WOUND)
diff --git a/code/modules/antagonists/blob/blob/blobstrains/networked_fibers.dm b/code/modules/antagonists/blob/blob/blobstrains/networked_fibers.dm
index fac3470c7a..8ccf2b9c99 100644
--- a/code/modules/antagonists/blob/blob/blobstrains/networked_fibers.dm
+++ b/code/modules/antagonists/blob/blob/blobstrains/networked_fibers.dm
@@ -33,6 +33,6 @@
/datum/reagent/blob/networked_fibers/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
reac_volume = ..()
- M.apply_damage(0.6*reac_volume, BRUTE)
+ M.apply_damage(0.6*reac_volume, BRUTE, wound_bonus=CANT_WOUND)
if(M)
- M.apply_damage(0.6*reac_volume, BURN)
+ M.apply_damage(0.6*reac_volume, BURN, wound_bonus=CANT_WOUND)
diff --git a/code/modules/antagonists/blob/blob/blobstrains/pressurized_slime.dm b/code/modules/antagonists/blob/blob/blobstrains/pressurized_slime.dm
index 6a984e66a2..11477712e7 100644
--- a/code/modules/antagonists/blob/blob/blobstrains/pressurized_slime.dm
+++ b/code/modules/antagonists/blob/blob/blobstrains/pressurized_slime.dm
@@ -44,7 +44,7 @@
T.MakeSlippery(TURF_WET_LUBE, min_wet_time = 10 SECONDS, wet_time_to_add = 5 SECONDS)
M.adjust_fire_stacks(-(reac_volume / 10))
M.ExtinguishMob()
- M.apply_damage(0.4*reac_volume, BRUTE)
+ M.apply_damage(0.4*reac_volume, BRUTE, wound_bonus=CANT_WOUND)
if(M)
M.apply_damage(0.4*reac_volume, OXY)
if(M)
diff --git a/code/modules/antagonists/blob/blob/blobstrains/replicating_foam.dm b/code/modules/antagonists/blob/blob/blobstrains/replicating_foam.dm
index 00743c671e..5565135c63 100644
--- a/code/modules/antagonists/blob/blob/blobstrains/replicating_foam.dm
+++ b/code/modules/antagonists/blob/blob/blobstrains/replicating_foam.dm
@@ -31,4 +31,4 @@
/datum/reagent/blob/replicating_foam/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
reac_volume = ..()
- M.apply_damage(0.7*reac_volume, BRUTE)
+ M.apply_damage(0.7*reac_volume, BRUTE, wound_bonus=CANT_WOUND)
diff --git a/code/modules/antagonists/blob/blob/blobstrains/shifting_fragments.dm b/code/modules/antagonists/blob/blob/blobstrains/shifting_fragments.dm
index dbb3d6fb9b..9265158e1b 100644
--- a/code/modules/antagonists/blob/blob/blobstrains/shifting_fragments.dm
+++ b/code/modules/antagonists/blob/blob/blobstrains/shifting_fragments.dm
@@ -32,4 +32,4 @@
/datum/reagent/blob/shifting_fragments/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
reac_volume = ..()
- M.apply_damage(0.7*reac_volume, BRUTE)
+ M.apply_damage(0.7*reac_volume, BRUTE, wound_bonus=CANT_WOUND)
diff --git a/code/modules/antagonists/blob/blob/blobstrains/synchronous_mesh.dm b/code/modules/antagonists/blob/blob/blobstrains/synchronous_mesh.dm
index d58fb5b37d..daad0068e2 100644
--- a/code/modules/antagonists/blob/blob/blobstrains/synchronous_mesh.dm
+++ b/code/modules/antagonists/blob/blob/blobstrains/synchronous_mesh.dm
@@ -30,9 +30,9 @@
/datum/reagent/blob/synchronous_mesh/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/O)
reac_volume = ..()
- M.apply_damage(0.2*reac_volume, BRUTE)
+ M.apply_damage(0.2*reac_volume, BRUTE, wound_bonus=CANT_WOUND)
if(M && reac_volume)
for(var/obj/structure/blob/B in range(1, M)) //if the target is completely surrounded, this is 2.4*reac_volume bonus damage, total of 2.6*reac_volume
if(M)
B.blob_attack_animation(M) //show them they're getting a bad time
- M.apply_damage(0.3*reac_volume, BRUTE)
+ M.apply_damage(0.3*reac_volume, BRUTE, wound_bonus=CANT_WOUND)
diff --git a/code/modules/antagonists/changeling/powers/fleshmend.dm b/code/modules/antagonists/changeling/powers/fleshmend.dm
index afef2a10c7..0299abb09a 100644
--- a/code/modules/antagonists/changeling/powers/fleshmend.dm
+++ b/code/modules/antagonists/changeling/powers/fleshmend.dm
@@ -1,6 +1,6 @@
/obj/effect/proc_holder/changeling/fleshmend
name = "Fleshmend"
- desc = "Our flesh rapidly regenerates, healing our burns, bruises, and shortness of breath. Functions while unconscious. This ability is loud, and might cause our blood to react violently to heat."
+ desc = "Our flesh rapidly regenerates, healing our burns, bruises, and shortness of breath, as well as hiding all of our scars. Costs 20 chemicals."
helptext = "If we are on fire, the healing effect will not function. Does not regrow limbs or restore lost blood."
chemical_cost = 20
loudness = 2
diff --git a/code/modules/antagonists/changeling/powers/mutations.dm b/code/modules/antagonists/changeling/powers/mutations.dm
index e4417a6d64..7d50374e96 100644
--- a/code/modules/antagonists/changeling/powers/mutations.dm
+++ b/code/modules/antagonists/changeling/powers/mutations.dm
@@ -165,6 +165,8 @@
hitsound = 'sound/weapons/bladeslice.ogg'
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
sharpness = IS_SHARP
+ wound_bonus = -60
+ bare_wound_bonus = 20
var/can_drop = FALSE
var/fake = FALSE
total_mass = TOTAL_MASS_HAND_REPLACEMENT
diff --git a/code/modules/antagonists/changeling/powers/regenerate.dm b/code/modules/antagonists/changeling/powers/regenerate.dm
index 1b27fa9694..bcebd8c62e 100644
--- a/code/modules/antagonists/changeling/powers/regenerate.dm
+++ b/code/modules/antagonists/changeling/powers/regenerate.dm
@@ -29,6 +29,9 @@
C.emote("scream")
C.regenerate_limbs(1)
C.regenerate_organs()
+ for(var/i in C.all_wounds)
+ var/datum/wound/W = i
+ W.remove_wound()
if(!user.getorganslot(ORGAN_SLOT_BRAIN))
var/obj/item/organ/brain/B
if(C.has_dna() && C.dna.species.mutant_brain)
diff --git a/code/modules/antagonists/cult/cult_items.dm b/code/modules/antagonists/cult/cult_items.dm
index 5b2dd7d007..4e9d40d91c 100644
--- a/code/modules/antagonists/cult/cult_items.dm
+++ b/code/modules/antagonists/cult/cult_items.dm
@@ -34,6 +34,8 @@
w_class = WEIGHT_CLASS_SMALL
force = 15
throwforce = 25
+ wound_bonus = -30
+ bare_wound_bonus = 30
armour_penetration = 35
actions_types = list(/datum/action/item_action/cult_dagger)
@@ -53,8 +55,10 @@
flags_1 = CONDUCT_1
sharpness = IS_SHARP
w_class = WEIGHT_CLASS_BULKY
- force = 30
+ force = 30 // whoever balanced this got beat in the head by a bible too many times good lord
throwforce = 10
+ wound_bonus = -80
+ bare_wound_bonus = 30
hitsound = 'sound/weapons/bladeslice.ogg'
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "rended")
diff --git a/code/modules/antagonists/slaughter/slaughter.dm b/code/modules/antagonists/slaughter/slaughter.dm
index 6b063559dc..0c51c65a6f 100644
--- a/code/modules/antagonists/slaughter/slaughter.dm
+++ b/code/modules/antagonists/slaughter/slaughter.dm
@@ -34,8 +34,11 @@
healable = 0
environment_smash = ENVIRONMENT_SMASH_STRUCTURES
obj_damage = 50
- melee_damage_lower = 30
- melee_damage_upper = 30
+ melee_damage_lower = 15 // reduced from 30 to 15 with wounds since they get big buffs to slicing wounds
+ melee_damage_upper = 15
+ wound_bonus = -10
+ bare_wound_bonus = 0
+ sharpness = TRUE
see_in_dark = 8
blood_volume = 0 //No bleeding on getting shot, for skeddadles
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
@@ -43,13 +46,25 @@
var/playstyle_string = "You are a slaughter demon, a terrible creature from another realm. You have a single desire: To kill. \
You may use the \"Blood Crawl\" ability near blood pools to travel through them, appearing and disappearing from the station at will. \
Pulling a dead or unconscious mob while you enter a pool will pull them in with you, allowing you to feast and regain your health. \
- You move quickly upon leaving a pool of blood, but the material world will soon sap your strength and leave you sluggish. "
+ You move quickly upon leaving a pool of blood, but the material world will soon sap your strength and leave you sluggish. \
+ You gain strength the more attacks you land on live humanoids, though this resets when you return to the blood zone. You can also \
+ launch a devastating slam attack with ctrl+shift+click, capable of smashing bones in one strike."
loot = list(/obj/effect/decal/cleanable/blood, \
/obj/effect/decal/cleanable/blood/innards, \
/obj/item/organ/heart/demon)
del_on_death = 1
deathmessage = "screams in anger as it collapses into a puddle of viscera!"
+ // How long it takes for the alt-click slam attack to come off cooldown
+ var/slam_cooldown_time = 45 SECONDS
+ // The actual instance var for the cooldown
+ var/slam_cooldown = 0
+ // How many times we have hit humanoid targets since we last bloodcrawled, scaling wounding power
+ var/current_hitstreak = 0
+ // How much both our wound_bonus and bare_wound_bonus go up per hitstreak hit
+ var/wound_bonus_per_hit = 5
+ // How much our wound_bonus hitstreak bonus caps at (peak demonry)
+ var/wound_bonus_hitstreak_max = 12
/mob/living/simple_animal/slaughter/Initialize()
..()
@@ -58,6 +73,33 @@
if(istype(loc, /obj/effect/dummy/phased_mob/slaughter))
bloodspell.phased = TRUE
+/mob/living/simple_animal/slaughter/CtrlShiftClickOn(atom/A)
+ if(!isliving(A))
+ return ..()
+ if(slam_cooldown + slam_cooldown_time > world.time)
+ to_chat(src, "Your slam ability is still on cooldown!")
+ return
+
+ face_atom(A)
+ var/mob/living/victim = A
+ victim.take_bodypart_damage(brute=20, wound_bonus=wound_bonus) // don't worry, there's more punishment when they hit something
+ visible_message("[src] slams into [victim] with monstrous strength!", "You slam into [victim] with monstrous strength!", ignored_mobs=victim)
+ to_chat(victim, "[src] slams into you with monstrous strength, sending you flying like a ragdoll!")
+ var/turf/yeet_target = get_edge_target_turf(victim, dir)
+ victim.throw_at(yeet_target, 10, 5, src)
+ slam_cooldown = world.time
+ log_combat(src, victim, "slaughter slammed")
+
+/mob/living/simple_animal/slaughter/UnarmedAttack(atom/A, proximity)
+ if(iscarbon(A))
+ var/mob/living/carbon/target = A
+ if(target.stat != DEAD && target.mind && current_hitstreak < wound_bonus_hitstreak_max)
+ current_hitstreak++
+ wound_bonus += wound_bonus_per_hit
+ bare_wound_bonus += wound_bonus_per_hit
+
+ return ..()
+
/obj/effect/decal/cleanable/blood/innards
icon = 'icons/obj/surgery.dmi'
name = "pile of viscera"
diff --git a/code/modules/antagonists/slaughter/slaughter_antag.dm b/code/modules/antagonists/slaughter/slaughter_antag.dm
index 04f7167fa5..ddc4c1ebc3 100644
--- a/code/modules/antagonists/slaughter/slaughter_antag.dm
+++ b/code/modules/antagonists/slaughter/slaughter_antag.dm
@@ -14,6 +14,7 @@
/datum/antagonist/slaughter/greet()
. = ..()
owner.announce_objectives()
+ to_chat(owner, "You have a powerful alt-attack that slams people backwards that you can activate by shift+ctrl+clicking your target!")
/datum/antagonist/slaughter/proc/forge_objectives()
if(summoner)
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
index 1f9e4ccac3..2e999dd9dc 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
@@ -33,6 +33,8 @@
var/escape_in_progress = FALSE
var/message_cooldown
var/breakout_time = 300
+ ///Cryo will continue to treat people with 0 damage but existing wounds, but will sound off when damage healing is done in case doctors want to directly treat the wounds instead
+ var/treating_wounds = FALSE
fair_market_price = 10
payment_department = ACCOUNT_MED
@@ -174,15 +176,27 @@
return
if(mob_occupant.health >= mob_occupant.getMaxHealth()) // Don't bother with fully healed people.
- on = FALSE
- update_icon()
- playsound(src, 'sound/machines/cryo_warning.ogg', volume) // Bug the doctors.
- var/msg = "Patient fully restored."
- if(autoeject) // Eject if configured.
- msg += " Auto ejecting patient now."
- open_machine()
- radio.talk_into(src, msg, radio_channel)
- return
+ if(iscarbon(mob_occupant))
+ var/mob/living/carbon/C = mob_occupant
+ if(C.all_wounds)
+ if(!treating_wounds) // if we have wounds and haven't already alerted the doctors we're only dealing with the wounds, let them know
+ treating_wounds = TRUE
+ playsound(src, 'sound/machines/cryo_warning.ogg', volume) // Bug the doctors.
+ var/msg = "Patient vitals fully recovered, continuing automated wound treatment."
+ radio.talk_into(src, msg, radio_channel)
+ else // otherwise if we were only treating wounds and now we don't have any, turn off treating_wounds so we can boot 'em out
+ treating_wounds = FALSE
+
+ if(!treating_wounds)
+ on = FALSE
+ update_icon()
+ playsound(src, 'sound/machines/cryo_warning.ogg', volume) // Bug the doctors.
+ var/msg = "Patient fully restored."
+ if(autoeject) // Eject if configured.
+ msg += " Auto ejecting patient now."
+ open_machine()
+ radio.talk_into(src, msg, radio_channel)
+ return
var/datum/gas_mixture/air1 = airs[1]
diff --git a/code/modules/cargo/packs/goodies.dm b/code/modules/cargo/packs/goodies.dm
index ac8b1e9032..2ff02d0473 100644
--- a/code/modules/cargo/packs/goodies.dm
+++ b/code/modules/cargo/packs/goodies.dm
@@ -75,3 +75,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/hell_single
+ name = "Hellgun Single-Pack"
+ desc = "Contains one hellgun, an old pattern of laser gun infamous for its ability to horribly disfigure targets with burns. Technically violates the Space Geneva Convention when used on humanoids."
+ cost = 1500
+ contains = list(/obj/item/gun/energy/laser/hellgun)
diff --git a/code/modules/cargo/packs/medical.dm b/code/modules/cargo/packs/medical.dm
index 2bb629308c..5eacc2f583 100644
--- a/code/modules/cargo/packs/medical.dm
+++ b/code/modules/cargo/packs/medical.dm
@@ -114,7 +114,9 @@
/obj/item/storage/box/medsprays,
/obj/item/storage/box/syringes,
/obj/item/storage/box/bodybags,
- /obj/item/storage/pill_bottle/stimulant)
+ /obj/item/storage/pill_bottle/stimulant,
+ /obj/item/stack/medical/bone_gel,
+ /obj/item/stack/medical/bone_gel)
crate_name = "medical supplies crate"
/datum/supply_pack/medical/adv_surgery_tools
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 8b4c28a1f4..4278aea1d3 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -239,6 +239,13 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/autostand = TRUE
var/auto_ooc = FALSE
+ /// If we have persistent scars enabled
+ var/persistent_scars = TRUE
+ /// We have 5 slots for persistent scars, if enabled we pick a random one to load (empty by default) and scars at the end of the shift if we survived as our original person
+ var/list/scars_list = list("1" = "", "2" = "", "3" = "", "4" = "", "5" = "")
+ /// Which of the 5 persistent scar slots we randomly roll to load for this round, if enabled. Actually rolled in [/datum/preferences/proc/load_character(slot)]
+ var/scars_index = 1
+
/datum/preferences/New(client/C)
parent = C
@@ -825,6 +832,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "Socks Color: Change
"
dat += "Backpack:[backbag]"
dat += "Jumpsuit:
[jumpsuit_style]
"
+ if(CAN_SCAR in pref_species.species_traits)
+ dat += "
Temporal Scarring:
[(persistent_scars) ? "Enabled" : "Disabled"]"
+ dat += "Clear scar slots"
dat += "Uplink Location:[uplink_spawn_loc]"
dat += ""
@@ -2545,6 +2555,17 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if("hear_midis")
toggles ^= SOUND_MIDI
+ if("persistent_scars")
+ persistent_scars = !persistent_scars
+
+ if("clear_scars")
+ to_chat(user, "All scar slots cleared. Please save character to confirm.")
+ scars_list["1"] = ""
+ scars_list["2"] = ""
+ scars_list["3"] = ""
+ scars_list["4"] = ""
+ scars_list["5"] = ""
+
if("lobby_music")
toggles ^= SOUND_LOBBY
if((toggles & SOUND_LOBBY) && user.client && isnewplayer(user))
diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm
index 440ee1fbc1..10881242e7 100644
--- a/code/modules/client/preferences_savefile.dm
+++ b/code/modules/client/preferences_savefile.dm
@@ -453,6 +453,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
if(newtype)
pref_species = new newtype
+
+ scars_index = rand(1,5)
+
//Character
S["real_name"] >> real_name
S["nameless"] >> nameless
@@ -497,6 +500,12 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["feature_insect_markings"] >> features["insect_markings"]
S["feature_horns_color"] >> features["horns_color"]
S["feature_wings_color"] >> features["wings_color"]
+ S["persistent_scars"] >> persistent_scars
+ S["scars1"] >> scars_list["1"]
+ S["scars2"] >> scars_list["2"]
+ S["scars3"] >> scars_list["3"]
+ S["scars4"] >> scars_list["4"]
+ S["scars5"] >> scars_list["5"]
//Custom names
@@ -709,6 +718,13 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
features["silicon_flavor_text"] = copytext(features["silicon_flavor_text"], 1, MAX_FLAVOR_LEN)
features["ooc_notes"] = copytext(features["ooc_notes"], 1, MAX_FLAVOR_LEN)
+ persistent_scars = sanitize_integer(persistent_scars)
+ scars_list["1"] = sanitize_text(scars_list["1"])
+ scars_list["2"] = sanitize_text(scars_list["2"])
+ scars_list["3"] = sanitize_text(scars_list["3"])
+ scars_list["4"] = sanitize_text(scars_list["4"])
+ scars_list["5"] = sanitize_text(scars_list["5"])
+
joblessrole = sanitize_integer(joblessrole, 1, 3, initial(joblessrole))
//Validate job prefs
for(var/j in job_preferences)
@@ -840,6 +856,13 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["vore_taste"] , vore_taste)
WRITE_FILE(S["belly_prefs"] , belly_prefs)
+ WRITE_FILE(S["persistent_scars"] , persistent_scars)
+ WRITE_FILE(S["scars1"] , scars_list["1"])
+ WRITE_FILE(S["scars2"] , scars_list["2"])
+ WRITE_FILE(S["scars3"] , scars_list["3"])
+ WRITE_FILE(S["scars4"] , scars_list["4"])
+ WRITE_FILE(S["scars5"] , scars_list["5"])
+
//gear loadout
if(chosen_gear.len)
var/text_to_save = chosen_gear.Join("|")
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index a28061db10..e08886e71f 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -4,7 +4,7 @@
max_integrity = 200
integrity_failure = 0.4
block_priority = BLOCK_PRIORITY_CLOTHING
- var/damaged_clothes = 0 //similar to machine's BROKEN stat and structure's broken var
+ var/damaged_clothes = CLOTHING_PRISTINE //similar to machine's BROKEN stat and structure's broken var
var/flash_protect = 0 //What level of bright light protection item has. 1 = Flashers, Flashes, & Flashbangs | 2 = Welding | -1 = OH GOD WELDING BURNT OUT MY RETINAS
var/tint = 0 //Sets the item's level of visual impairment tint, normally set to the same as flash_protect
var/up = 0 //but separated to allow items to protect but not impair vision, like space helmets
@@ -28,6 +28,9 @@
var/clothing_flags = NONE
+ // What items can be consumed to repair this clothing (must by an /obj/item/stack)
+ var/repairable_by = /obj/item/stack/sheet/cloth
+
//Var modification - PLEASE be careful with this I know who you are and where you live
var/list/user_vars_to_edit //VARNAME = VARVALUE eg: "name" = "butts"
var/list/user_vars_remembered //Auto built by the above + dropped() + equipped()
@@ -45,7 +48,17 @@
var/list/species_restricted = null
//Basically syntax is species_restricted = list("Species Name","Species Name")
//Add a "exclude" string to do the opposite, making it only only species listed that can't wear it.
- //You append this to clothing objects.
+ //You append this to clothing objects
+
+
+
+ // How much clothing damage has been dealt to each of the limbs of the clothing, assuming it covers more than one limb
+ var/list/damage_by_parts
+ // How much integrity is in a specific limb before that limb is disabled (for use in [/obj/item/clothing/proc/take_damage_zone], and only if we cover multiple zones.) Set to 0 to disable shredding.
+ var/limb_integrity = 0
+ // How many zones (body parts, not precise) we have disabled so far, for naming purposes
+ var/zones_disabled
+ .
/obj/item/clothing/Initialize()
. = ..()
@@ -83,15 +96,105 @@
return ..()
/obj/item/clothing/attackby(obj/item/W, mob/user, params)
- if(damaged_clothes && istype(W, /obj/item/stack/sheet/cloth))
- var/obj/item/stack/sheet/cloth/C = W
- C.use(1)
- update_clothes_damaged_state(FALSE)
- obj_integrity = max_integrity
- to_chat(user, "You fix the damage on [src] with [C].")
+ if(damaged_clothes && istype(W, repairable_by))
+ var/obj/item/stack/S = W
+ switch(damaged_clothes)
+ if(CLOTHING_DAMAGED)
+ S.use(1)
+ repair(user, params)
+ if(CLOTHING_SHREDDED)
+ if(S.amount < 3)
+ to_chat(user, "You require 3 [S.name] to repair [src].")
+ return
+ to_chat(user, "You begin fixing the damage to [src] with [S]...")
+ if(do_after(user, 6 SECONDS, TRUE, src))
+ if(S.use(3))
+ repair(user, params)
return 1
return ..()
+// Set the clothing's integrity back to 100%, remove all damage to bodyparts, and generally fix it up
+/obj/item/clothing/proc/repair(mob/user, params)
+ damaged_clothes = CLOTHING_PRISTINE
+ update_clothes_damaged_state()
+ obj_integrity = max_integrity
+ name = initial(name) // remove "tattered" or "shredded" if there's a prefix
+ body_parts_covered = initial(body_parts_covered)
+ slot_flags = initial(slot_flags)
+ damage_by_parts = null
+ if(user)
+ UnregisterSignal(user, COMSIG_MOVABLE_MOVED)
+ to_chat(user, "You fix the damage on [src].")
+
+/**
+ * take_damage_zone() is used for dealing damage to specific bodyparts on a worn piece of clothing, meant to be called from [/obj/item/bodypart/proc/check_woundings_mods()]
+ *
+ * This proc only matters when a bodypart that this clothing is covering is harmed by a direct attack (being on fire or in space need not apply), and only if this clothing covers
+ * more than one bodypart to begin with. No point in tracking damage by zone for a hat, and I'm not cruel enough to let you fully break them in a few shots.
+ * Also if limb_integrity is 0, then this clothing doesn't have bodypart damage enabled so skip it.
+ *
+ * Arguments:
+ * * def_zone: The bodypart zone in question
+ * * damage_amount: Incoming damage
+ * * damage_type: BRUTE or BURN
+ * * armour_penetration: If the attack had armour_penetration
+ */
+/obj/item/clothing/proc/take_damage_zone(def_zone, damage_amount, damage_type, armour_penetration)
+ if(!def_zone || !limb_integrity || (initial(body_parts_covered) in GLOB.bitflags)) // the second check sees if we only cover one bodypart anyway and don't need to bother with this
+ return
+ var/list/covered_limbs = body_parts_covered2organ_names(body_parts_covered) // what do we actually cover?
+ if(!(def_zone in covered_limbs))
+ return
+
+ var/damage_dealt = take_damage(damage_amount * 0.1, damage_type, armour_penetration, FALSE) * 10 // only deal 10% of the damage to the general integrity damage, then multiply it by 10 so we know how much to deal to limb
+ LAZYINITLIST(damage_by_parts)
+ damage_by_parts[def_zone] += damage_dealt
+ if(damage_by_parts[def_zone] > limb_integrity)
+ disable_zone(def_zone, damage_type)
+
+/**
+ * disable_zone() is used to disable a given bodypart's protection on our clothing item, mainly from [/obj/item/clothing/proc/take_damage_zone()]
+ *
+ * This proc disables all protection on the specified bodypart for this piece of clothing: it'll be as if it doesn't cover it at all anymore (because it won't!)
+ * If every possible bodypart has been disabled on the clothing, we put it out of commission entirely and mark it as shredded, whereby it will have to be repaired in
+ * order to equip it again. Also note we only consider it damaged if there's more than one bodypart disabled.
+ *
+ * Arguments:
+ * * def_zone: The bodypart zone we're disabling
+ * * damage_type: Only really relevant for the verb for describing the breaking, and maybe obj_destruction()
+ */
+/obj/item/clothing/proc/disable_zone(def_zone, damage_type)
+ var/list/covered_limbs = body_parts_covered2organ_names(body_parts_covered)
+ if(!(def_zone in covered_limbs))
+ return
+
+ var/zone_name = parse_zone(def_zone)
+ var/break_verb = ((damage_type == BRUTE) ? "torn" : "burned")
+
+ if(iscarbon(loc))
+ var/mob/living/carbon/C = loc
+ C.visible_message("The [zone_name] on [C]'s [src.name] is [break_verb] away!", "The [zone_name] on your [src.name] is [break_verb] away!", vision_distance = COMBAT_MESSAGE_RANGE)
+ RegisterSignal(C, COMSIG_MOVABLE_MOVED, .proc/bristle)
+
+ zones_disabled++
+ for(var/i in zone2body_parts_covered(def_zone))
+ body_parts_covered &= ~i
+
+ if(body_parts_covered == NONE) // if there are no more parts to break then the whole thing is kaput
+ obj_destruction((damage_type == BRUTE ? "melee" : "laser")) // melee/laser is good enough since this only procs from direct attacks anyway and not from fire/bombs
+ return
+
+ damaged_clothes = CLOTHING_DAMAGED
+ switch(zones_disabled)
+ if(1)
+ name = "damaged [initial(name)]"
+ if(2)
+ name = "mangy [initial(name)]"
+ if(3 to INFINITY) // take better care of your shit, dude
+ name = "tattered [initial(name)]"
+
+ update_clothes_damaged_state()
+
/obj/item/clothing/Destroy()
user_vars_remembered = null //Oh god somebody put REFERENCES in here? not to worry, we'll clean it up
return ..()
@@ -100,6 +203,7 @@
..()
if(!istype(user))
return
+ UnregisterSignal(user, COMSIG_MOVABLE_MOVED)
if(LAZYLEN(user_vars_remembered))
for(var/variable in user_vars_remembered)
if(variable in user.vars)
@@ -112,7 +216,9 @@
if (!istype(user))
return
if(slot_flags & slotdefine2slotbit(slot)) //Was equipped to a valid slot for this item?
- if (LAZYLEN(user_vars_to_edit))
+ if(iscarbon(user) && LAZYLEN(zones_disabled))
+ RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/bristle)
+ if(LAZYLEN(user_vars_to_edit))
for(var/variable in user_vars_to_edit)
if(variable in user.vars)
LAZYSET(user_vars_remembered, variable, user.vars[variable])
@@ -120,8 +226,19 @@
/obj/item/clothing/examine(mob/user)
. = ..()
- if(damaged_clothes)
- . += "It looks damaged!"
+ if(damaged_clothes == CLOTHING_SHREDDED)
+ . += "It is completely shredded and requires mending before it can be worn again!"
+ return
+ for(var/zone in damage_by_parts)
+ var/pct_damage_part = damage_by_parts[zone] / limb_integrity * 100
+ var/zone_name = parse_zone(zone)
+ switch(pct_damage_part)
+ if(100 to INFINITY)
+ . += "The [zone_name] is useless and requires mending!"
+ if(60 to 99)
+ . += "The [zone_name] is heavily shredded!"
+ if(30 to 59)
+ . += "The [zone_name] is partially shredded."
var/datum/component/storage/pockets = GetComponent(/datum/component/storage)
if(pockets)
var/list/how_cool_are_your_threads = list("")
@@ -139,8 +256,8 @@
. += how_cool_are_your_threads.Join()
/obj/item/clothing/obj_break(damage_flag)
- if(!damaged_clothes)
- update_clothes_damaged_state(TRUE)
+ damaged_clothes = CLOTHING_DAMAGED
+ update_clothes_damaged_state()
if(ismob(loc)) //It's not important enough to warrant a message if nobody's wearing it
var/mob/M = loc
to_chat(M, "Your [name] starts to fall apart!")
@@ -222,16 +339,25 @@ BLIND // can't see anything
/obj/item/clothing/obj_destruction(damage_flag)
- if(damage_flag == "bomb" || damage_flag == "melee")
+ if(damage_flag == "bomb")
var/turf/T = get_turf(src)
spawn(1) //so the shred survives potential turf change from the explosion.
var/obj/effect/decal/cleanable/shreds/Shreds = new(T)
Shreds.desc = "The sad remains of what used to be [name]."
deconstruct(FALSE)
+ else if(!(damage_flag in list("acid", "fire")))
+ damaged_clothes = CLOTHING_SHREDDED
+ body_parts_covered = NONE
+ name = "shredded [initial(name)]"
+ slot_flags = NONE
+ update_clothes_damaged_state()
+ if(ismob(loc))
+ var/mob/M = loc
+ M.visible_message("[M]'s [src.name] falls off, completely shredded!", "Your [src.name] falls off, completely shredded!", vision_distance = COMBAT_MESSAGE_RANGE)
+ M.dropItemToGround(src)
else
..()
-
//Species-restricted clothing check. - Thanks Oraclestation, BS13, /vg/station etc.
/obj/item/clothing/mob_can_equip(mob/M, slot, disable_warning = TRUE)
@@ -265,3 +391,12 @@ BLIND // can't see anything
return FALSE
return TRUE
+
+
+
+/// If we're a clothing with at least 1 shredded/disabled zone, give the wearer a periodic heads up letting them know their clothes are damaged
+/obj/item/clothing/proc/bristle(mob/living/L)
+ if(!istype(L))
+ return
+ if(prob(0.2))
+ to_chat(L, "The damaged threads on your [src.name] chafe!")
diff --git a/code/modules/clothing/gloves/_gloves.dm b/code/modules/clothing/gloves/_gloves.dm
index ddf5e4b584..a206b9adc7 100644
--- a/code/modules/clothing/gloves/_gloves.dm
+++ b/code/modules/clothing/gloves/_gloves.dm
@@ -34,7 +34,7 @@
if(blood_DNA)
. += mutable_appearance('icons/effects/blood.dmi', "bloodyhands", color = blood_DNA_to_color())
-/obj/item/clothing/gloves/update_clothes_damaged_state(damaging = TRUE)
+/obj/item/clothing/gloves/update_clothes_damaged_state()
..()
if(ismob(loc))
var/mob/M = loc
diff --git a/code/modules/clothing/head/_head.dm b/code/modules/clothing/head/_head.dm
index 475e7a4e51..9682787de5 100644
--- a/code/modules/clothing/head/_head.dm
+++ b/code/modules/clothing/head/_head.dm
@@ -56,7 +56,7 @@
if(blood_DNA)
. += mutable_appearance('icons/effects/blood.dmi', "helmetblood", color = blood_DNA_to_color())
-/obj/item/clothing/head/update_clothes_damaged_state(damaging = TRUE)
+/obj/item/clothing/head/update_clothes_damaged_state()
..()
if(ismob(loc))
var/mob/M = loc
diff --git a/code/modules/clothing/masks/_masks.dm b/code/modules/clothing/masks/_masks.dm
index 7df38661e5..7a5a031e79 100644
--- a/code/modules/clothing/masks/_masks.dm
+++ b/code/modules/clothing/masks/_masks.dm
@@ -37,7 +37,7 @@
if(blood_DNA)
. += mutable_appearance('icons/effects/blood.dmi', "maskblood", color = blood_DNA_to_color())
-/obj/item/clothing/mask/update_clothes_damaged_state(damaging = TRUE)
+/obj/item/clothing/mask/update_clothes_damaged_state()
..()
if(ismob(loc))
var/mob/M = loc
diff --git a/code/modules/clothing/shoes/_shoes.dm b/code/modules/clothing/shoes/_shoes.dm
index 447a531717..d67aea11a1 100644
--- a/code/modules/clothing/shoes/_shoes.dm
+++ b/code/modules/clothing/shoes/_shoes.dm
@@ -85,7 +85,7 @@
restore_offsets(user)
. = ..()
-/obj/item/clothing/shoes/update_clothes_damaged_state(damaging = TRUE)
+/obj/item/clothing/shoes/update_clothes_damaged_state()
..()
if(ismob(loc))
var/mob/M = loc
diff --git a/code/modules/clothing/suits/_suits.dm b/code/modules/clothing/suits/_suits.dm
index 8de49c63de..0d16f9bdfa 100644
--- a/code/modules/clothing/suits/_suits.dm
+++ b/code/modules/clothing/suits/_suits.dm
@@ -10,6 +10,7 @@
var/blood_overlay_type = "suit"
var/togglename = null
var/suittoggled = FALSE
+ limb_integrity = 0 // disabled for most exo-suits
mutantrace_variation = STYLE_DIGITIGRADE
/obj/item/clothing/suit/worn_overlays(isinhands = FALSE, icon_file, used_state, style_flags = NONE)
@@ -28,7 +29,7 @@
if(A.above_suit)
. += U.accessory_overlay
-/obj/item/clothing/suit/update_clothes_damaged_state(damaging = TRUE)
+/obj/item/clothing/suit/update_clothes_damaged_state()
..()
if(ismob(loc))
var/mob/M = loc
diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm
index 0ef1a83bf5..1c753b15df 100644
--- a/code/modules/clothing/suits/armor.dm
+++ b/code/modules/clothing/suits/armor.dm
@@ -8,7 +8,8 @@
equip_delay_other = 40
max_integrity = 250
resistance_flags = NONE
- armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
+ armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50, "wound" = 15)
+
/obj/item/clothing/suit/armor/Initialize()
. = ..()
@@ -57,7 +58,7 @@
icon_state = "hos"
item_state = "greatcoat"
body_parts_covered = CHEST|GROIN|ARMS|LEGS
- armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 90)
+ armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 90, "wound" = 20)
cold_protection = CHEST|GROIN|LEGS|ARMS
heat_protection = CHEST|GROIN|LEGS|ARMS
strip_delay = 80
@@ -123,7 +124,7 @@
icon_state = "capcarapace"
item_state = "armor"
body_parts_covered = CHEST|GROIN
- armor = list("melee" = 50, "bullet" = 40, "laser" = 50, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 90)
+ armor = list("melee" = 50, "bullet" = 40, "laser" = 50, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 90, "wound" = 10)
dog_fashion = null
resistance_flags = FIRE_PROOF
@@ -147,7 +148,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)
+ armor = list("melee" = 50, "bullet" = 10, "laser" = 10, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 80, "wound" = 30)
blocks_shove_knockdown = TRUE
strip_delay = 80
equip_delay_other = 60
@@ -158,7 +159,7 @@
icon_state = "bonearmor"
item_state = "bonearmor"
blood_overlay_type = "armor"
- armor = list("melee" = 35, "bullet" = 25, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
+ armor = list("melee" = 35, "bullet" = 25, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50, "wound" = 10)
body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS
/obj/item/clothing/suit/armor/bulletproof
@@ -167,7 +168,7 @@
icon_state = "bulletproof"
item_state = "armor"
blood_overlay_type = "armor"
- armor = list("melee" = 15, "bullet" = 60, "laser" = 10, "energy" = 10, "bomb" = 40, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
+ armor = list("melee" = 15, "bullet" = 60, "laser" = 10, "energy" = 10, "bomb" = 40, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50 "wound" = 20)
strip_delay = 70
equip_delay_other = 50
mutantrace_variation = STYLE_DIGITIGRADE|STYLE_NO_ANTHRO_ICON
@@ -285,7 +286,7 @@
desc = "A classic suit of armour, able to be made from many different materials."
icon_state = "knight_greyscale"
item_state = "knight_greyscale"
- armor = list("melee" = 35, "bullet" = 10, "laser" = 10, "energy" = 10, "bomb" = 10, "bio" = 10, "rad" = 10, "fire" = 40, "acid" = 40)
+ armor = list("melee" = 35, "bullet" = 10, "laser" = 10, "energy" = 10, "bomb" = 10, "bio" = 10, "rad" = 10, "fire" = 40, "acid" = 40, "wound" = 15)
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS //Can change color and add prefix
/obj/item/clothing/suit/armor/vest/durathread
@@ -304,7 +305,7 @@
desc = "A bulletproof vest with forest camo. Good thing there's plenty of forests to hide in around here, right?"
icon_state = "rus_armor"
item_state = "rus_armor"
- armor = list("melee" = 25, "bullet" = 30, "laser" = 0, "energy" = 15, "bomb" = 10, "bio" = 0, "rad" = 20, "fire" = 20, "acid" = 50)
+ armor = list("melee" = 25, "bullet" = 30, "laser" = 0, "energy" = 15, "bomb" = 10, "bio" = 0, "rad" = 20, "fire" = 20, "acid" = 50, "wound" = 10)
/obj/item/clothing/suit/armor/vest/russian_coat
name = "russian battle coat"
@@ -315,4 +316,4 @@
body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
cold_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT
- armor = list("melee" = 25, "bullet" = 20, "laser" = 20, "energy" = 10, "bomb" = 20, "bio" = 50, "rad" = 20, "fire" = -10, "acid" = 50)
+ armor = list("melee" = 25, "bullet" = 20, "laser" = 20, "energy" = 10, "bomb" = 20, "bio" = 50, "rad" = 20, "fire" = -10, "acid" = 50, "wound" = 10)
diff --git a/code/modules/clothing/suits/wiz_robe.dm b/code/modules/clothing/suits/wiz_robe.dm
index 047dc7b7a3..fc783936da 100644
--- a/code/modules/clothing/suits/wiz_robe.dm
+++ b/code/modules/clothing/suits/wiz_robe.dm
@@ -73,7 +73,7 @@
gas_transfer_coefficient = 0.01
permeability_coefficient = 0.01
body_parts_covered = CHEST|GROIN|ARMS|LEGS
- armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 20, "bio" = 20, "rad" = 20, "fire" = 100, "acid" = 100)
+ armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 20, "bio" = 20, "rad" = 20, "fire" = 100, "acid" = 100, "wound" = 20)
allowed = list(/obj/item/teleportation_scroll)
flags_inv = HIDEJUMPSUIT
strip_delay = 50
diff --git a/code/modules/clothing/under/_under.dm b/code/modules/clothing/under/_under.dm
index e0ecf24204..23cb2b1c15 100644
--- a/code/modules/clothing/under/_under.dm
+++ b/code/modules/clothing/under/_under.dm
@@ -5,8 +5,9 @@
permeability_coefficient = 0.9
block_priority = BLOCK_PRIORITY_UNIFORM
slot_flags = ITEM_SLOT_ICLOTHING
- armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0)
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0, "wound" = 5)
mutantrace_variation = STYLE_DIGITIGRADE|USE_TAUR_CLIP_MASK
+ limb_integrity = 30
var/fitted = FEMALE_UNIFORM_FULL // For use in alternate clothing styles for women
var/has_sensor = HAS_SENSORS // For the crew computer
var/random_sensor = TRUE
@@ -39,7 +40,7 @@
if(!attach_accessory(I, user))
return ..()
-/obj/item/clothing/under/update_clothes_damaged_state(damaging = TRUE)
+/obj/item/clothing/under/update_clothes_damaged_state()
..()
if(ismob(loc))
var/mob/M = loc
diff --git a/code/modules/clothing/under/jobs/cargo.dm b/code/modules/clothing/under/jobs/cargo.dm
index aa4db2fd30..8fbc343598 100644
--- a/code/modules/clothing/under/jobs/cargo.dm
+++ b/code/modules/clothing/under/jobs/cargo.dm
@@ -34,8 +34,9 @@
mutantrace_variation = STYLE_DIGITIGRADE|STYLE_NO_ANTHRO_ICON
/obj/item/clothing/under/rank/cargo/miner
- desc = "It's a snappy jumpsuit with a sturdy set of overalls. It is very dirty."
name = "shaft miner's jumpsuit"
+ desc = "It's a snappy jumpsuit with a sturdy set of overalls. It is very dirty."
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 0, "wound" = 10)
icon_state = "miner"
item_state = "miner"
diff --git a/code/modules/clothing/under/jobs/command.dm b/code/modules/clothing/under/jobs/command.dm
index 1bc0f64373..8272c36cf3 100644
--- a/code/modules/clothing/under/jobs/command.dm
+++ b/code/modules/clothing/under/jobs/command.dm
@@ -19,6 +19,7 @@
/obj/item/clothing/under/rank/captain/suit
name = "captain's suit"
desc = "A green suit and yellow necktie. Exemplifies authority."
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0, "wound" = 15)
icon_state = "green_suit"
item_state = "dg_suit"
can_adjust = FALSE
diff --git a/code/modules/clothing/under/jobs/security.dm b/code/modules/clothing/under/jobs/security.dm
index 26fff7c8ed..7747d2e181 100644
--- a/code/modules/clothing/under/jobs/security.dm
+++ b/code/modules/clothing/under/jobs/security.dm
@@ -19,7 +19,7 @@
desc = "A tactical security jumpsuit for officers complete with Nanotrasen belt buckle."
icon_state = "rsecurity"
item_state = "r_suit"
- armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30)
+ armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30, "wound" = 10)
/obj/item/clothing/under/rank/security/officer/grey
name = "grey security jumpsuit"
@@ -67,7 +67,7 @@
desc = "A formal security suit for officers complete with Nanotrasen belt buckle."
icon_state = "rwarden"
item_state = "r_suit"
- armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30)
+ armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30, "wound" = 10)
/obj/item/clothing/under/rank/security/warden/grey
name = "grey security suit"
@@ -101,7 +101,7 @@
desc = "Someone who wears this means business."
icon_state = "detective"
item_state = "det"
- armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30)
+ armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30, "wound" = 10)
/obj/item/clothing/under/rank/security/detective/skirt
name = "detective's suitskirt"
@@ -138,7 +138,7 @@
desc = "A security jumpsuit decorated for those few with the dedication to achieve the position of Head of Security."
icon_state = "rhos"
item_state = "r_suit"
- armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
+ armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50, "wound" = 10)
strip_delay = 60
/obj/item/clothing/under/rank/security/head_of_security/skirt
diff --git a/code/modules/goonchat/browserassets/css/browserOutput.css b/code/modules/goonchat/browserassets/css/browserOutput.css
index 3455a97ba2..93c733391e 100644
--- a/code/modules/goonchat/browserassets/css/browserOutput.css
+++ b/code/modules/goonchat/browserassets/css/browserOutput.css
@@ -313,6 +313,8 @@ h1.alert, h2.alert {color: #000000;}
.rose {color: #ff5050;}
.info {color: #0000CC;}
.notice {color: #000099;}
+.tinynotice {color: #6685f5; font-style: italic; font-size: 85%;}
+.smallnotice {color: #6685f5; font-style: italic; font-size: 90%;}
.boldnotice {color: #000099; font-weight: bold;}
.adminnotice {color: #0000ff;}
.adminhelp {color: #ff0000; font-weight: bold;}
diff --git a/code/modules/hydroponics/grown/nettle.dm b/code/modules/hydroponics/grown/nettle.dm
index 0979ea483f..cee7748c59 100644
--- a/code/modules/hydroponics/grown/nettle.dm
+++ b/code/modules/hydroponics/grown/nettle.dm
@@ -90,6 +90,7 @@
icon_state = "deathnettle"
force = 30
throwforce = 15
+ wound_bonus = CANT_WOUND
/obj/item/reagent_containers/food/snacks/grown/nettle/death/add_juice()
..()
diff --git a/code/modules/jobs/job_types/chief_medical_officer.dm b/code/modules/jobs/job_types/chief_medical_officer.dm
index adee2856fa..9cbc15ca28 100644
--- a/code/modules/jobs/job_types/chief_medical_officer.dm
+++ b/code/modules/jobs/job_types/chief_medical_officer.dm
@@ -45,7 +45,7 @@
shoes = /obj/item/clothing/shoes/sneakers/brown
suit = /obj/item/clothing/suit/toggle/labcoat/cmo
l_hand = /obj/item/storage/firstaid/regular
- suit_store = /obj/item/flashlight/pen
+ suit_store = /obj/item/flashlight/pen/paramedic
backpack_contents = list(/obj/item/melee/classic_baton/telescopic=1)
backpack = /obj/item/storage/backpack/medic
diff --git a/code/modules/jobs/job_types/paramedic.dm b/code/modules/jobs/job_types/paramedic.dm
index 9bdfdfe279..c8188cae8a 100644
--- a/code/modules/jobs/job_types/paramedic.dm
+++ b/code/modules/jobs/job_types/paramedic.dm
@@ -36,7 +36,7 @@
suit = /obj/item/clothing/suit/toggle/labcoat/paramedic
belt = /obj/item/storage/belt/medical
l_hand = /obj/item/storage/firstaid/regular
- suit_store = /obj/item/flashlight/pen
+ suit_store = /obj/item/flashlight/pen/paramedic
id = /obj/item/card/id
r_pocket = /obj/item/pinpointer/crew
l_pocket = /obj/item/pda/medical
diff --git a/code/modules/mining/equipment/explorer_gear.dm b/code/modules/mining/equipment/explorer_gear.dm
index 39f8e296af..09562a2a52 100644
--- a/code/modules/mining/equipment/explorer_gear.dm
+++ b/code/modules/mining/equipment/explorer_gear.dm
@@ -26,7 +26,7 @@
cold_protection = HEAD
max_heat_protection_temperature = FIRE_HELM_MAX_TEMP_PROTECT
heat_protection = HEAD
- armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 50)
+ armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 50, "wound" = 10)
resistance_flags = FIRE_PROOF
/obj/item/clothing/suit/hooded/explorer/standard
@@ -50,7 +50,7 @@
visor_flags_inv = HIDEFACIALHAIR
visor_flags_cover = MASKCOVERSMOUTH
actions_types = list(/datum/action/item_action/adjust)
- armor = list("melee" = 10, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 5, "bio" = 50, "rad" = 0, "fire" = 20, "acid" = 40)
+ armor = list("melee" = 10, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 5, "bio" = 50, "rad" = 0, "fire" = 20, "acid" = 40, "wound" = 5)
resistance_flags = FIRE_PROOF
/obj/item/clothing/mask/gas/explorer/attack_self(mob/user)
diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm
index d3892cc282..323df097b2 100644
--- a/code/modules/mob/dead/new_player/new_player.dm
+++ b/code/modules/mob/dead/new_player/new_player.dm
@@ -558,12 +558,25 @@
client.prefs.random_character()
client.prefs.real_name = client.prefs.pref_species.random_name(gender,1)
client.prefs.copy_to(H)
+ var/cur_scar_index = client.prefs.scars_index
+ if(client.prefs.persistent_scars && client.prefs.scars_list["[cur_scar_index]"])
+ var/scar_string = client.prefs.scars_list["[cur_scar_index]"]
+ var/valid_scars = ""
+ for(var/scar_line in splittext(scar_string, ";"))
+ if(H.load_scar(scar_line))
+ valid_scars += "[scar_line];"
+
+ client.prefs.scars_list["[cur_scar_index]"] = valid_scars
+ client.prefs.save_character()
+
+ client.prefs.copy_to(H, antagonist = is_antag)
H.dna.update_dna_identity()
if(mind)
if(transfer_after)
mind.late_joiner = TRUE
mind.active = 0 //we wish to transfer the key manually
mind.transfer_to(H) //won't transfer key since the mind is not active
+ mind.original_character = H
H.name = real_name
diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm
index 8ca4e6a0e4..ee0b58f766 100644
--- a/code/modules/mob/living/blood.dm
+++ b/code/modules/mob/living/blood.dm
@@ -5,12 +5,27 @@
#define EXOTIC_BLEED_MULTIPLIER 4 //Multiplies the actually bled amount by this number for the purposes of turf reaction calculations.
-/mob/living/carbon/human/proc/suppress_bloodloss(amount)
- if(bleedsuppress)
- return
- else
- bleedsuppress = TRUE
- addtimer(CALLBACK(src, .proc/resume_bleeding), amount)
+/mob/living/carbon/monkey/handle_blood()
+ if(bodytemperature <= TCRYO || (HAS_TRAIT(src, TRAIT_HUSK))) //cryosleep or husked people do not pump the blood.
+ var/temp_bleed = 0
+ for(var/X in bodyparts)
+ var/obj/item/bodypart/BP = X
+ temp_bleed += BP.get_bleed_rate()
+ BP.generic_bleedstacks = max(0, BP.generic_bleedstacks - 1)
+ bleed(temp_bleed)
+
+ var/temp_bleed = 0
+ for(var/X in bodyparts)
+ var/obj/item/bodypart/BP = X
+ temp_bleed += BP.get_bleed_rate()
+ BP.generic_bleedstacks = max(0, BP.generic_bleedstacks - 1)
+ bleed(temp_bleed)
+
+ //Blood regeneration if there is some space
+ if(blood_volume < BLOOD_VOLUME_NORMAL)
+ blood_volume += 0.1 // regenerate blood VERY slowly
+ if(blood_volume < BLOOD_VOLUME_OKAY)
+ adjustOxyLoss(round((BLOOD_VOLUME_NORMAL - blood_volume) * 0.02, 1))
/mob/living/carbon/human/proc/resume_bleeding()
bleedsuppress = 0
@@ -29,8 +44,7 @@
// Takes care blood loss and regeneration
/mob/living/carbon/human/handle_blood()
- if(NOBLOOD in dna.species.species_traits)
- bleed_rate = 0
+ if(NOBLOOD in dna.species.species_traits || bleedsuppress || (HAS_TRAIT(src, TRAIT_FAKEDEATH)))
return
if(bleed_rate < 0)
@@ -87,24 +101,11 @@
//Bleeding out
for(var/X in bodyparts)
var/obj/item/bodypart/BP = X
- var/brutedamage = BP.brute_dam
+ temp_bleed += BP.get_bleed_rate()
+ BP.generic_bleedstacks = max(0, BP.generic_bleedstacks - 1)
- if(BP.status == BODYPART_ROBOTIC) //for the moment, synth limbs won't bleed, but soon, my pretty.
- continue
-
- //We want an accurate reading of .len
- listclearnulls(BP.embedded_objects)
- for(var/obj/item/embeddies in BP.embedded_objects)
- if(!embeddies.isEmbedHarmless())
- temp_bleed += 0.5
-
- if(brutedamage >= 20)
- temp_bleed += (brutedamage * 0.013)
-
- bleed_rate = max(bleed_rate - 0.5, temp_bleed)//if no wounds, other bleed effects (heparin) naturally decreases
-
- if(bleed_rate && !bleedsuppress && !(HAS_TRAIT(src, TRAIT_FAKEDEATH)))
- bleed(bleed_rate)
+ if(temp_bleed)
+ bleed(temp_bleed)
//Makes a blood drop, leaking amt units of blood from the mob
/mob/living/carbon/proc/bleed(amt)
@@ -128,9 +129,11 @@
/mob/living/proc/restore_blood()
blood_volume = initial(blood_volume)
-/mob/living/carbon/human/restore_blood()
+/mob/living/carbon/restore_blood()
blood_volume = (BLOOD_VOLUME_NORMAL * blood_ratio)
- bleed_rate = 0
+ for(var/i in bodyparts)
+ var/obj/item/bodypart/BP = i
+ BP.generic_bleedstacks = 0
/****************************************************
BLOOD TRANSFERS
diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm
index 4fda02317e..b6f8dd349b 100644
--- a/code/modules/mob/living/brain/brain_item.dm
+++ b/code/modules/mob/living/brain/brain_item.dm
@@ -331,6 +331,8 @@
max_traumas = TRAUMA_LIMIT_BASIC
if(TRAUMA_RESILIENCE_SURGERY)
max_traumas = TRAUMA_LIMIT_SURGERY
+ if(TRAUMA_RESILIENCE_WOUND)
+ max_traumas = TRAUMA_LIMIT_WOUND
if(TRAUMA_RESILIENCE_LOBOTOMY)
max_traumas = TRAUMA_LIMIT_LOBOTOMY
if(TRAUMA_RESILIENCE_MAGIC)
@@ -389,7 +391,7 @@
return
var/trauma_type = pick(possible_traumas)
- gain_trauma(trauma_type, resilience)
+ return gain_trauma(trauma_type, resilience)
//Cure a random trauma of a certain resilience level
/obj/item/organ/brain/proc/cure_trauma_type(brain_trauma_type = /datum/brain_trauma, resilience = TRAUMA_RESILIENCE_BASIC)
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 8472c8bdae..c8c5bba9f9 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -88,11 +88,32 @@
for(var/datum/surgery/S in surgeries)
if(S.next_step(user,user.a_intent))
return 1
+
+ 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))
+ if(W.try_treating(I, user))
+ return 1
+
return ..()
/mob/living/carbon/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
. = ..()
var/hurt = TRUE
+ var/extra_speed = 0
+ if(throwingdatum.thrower != src)
+ extra_speed = min(max(0, throwingdatum.speed - initial(throw_speed)), 3)
if(GetComponent(/datum/component/tackler))
return
if(throwingdatum?.thrower && iscyborg(throwingdatum.thrower))
@@ -102,18 +123,18 @@
if(hit_atom.density && isturf(hit_atom))
if(hurt)
DefaultCombatKnockdown(20)
- take_bodypart_damage(10)
+ take_bodypart_damage(10 + 5 * extra_speed, check_armor = TRUE, wound_bonus = extra_speed)
if(iscarbon(hit_atom) && hit_atom != src)
var/mob/living/carbon/victim = hit_atom
if(victim.movement_type & FLYING)
return
if(hurt)
- victim.take_bodypart_damage(10)
- take_bodypart_damage(10)
+ victim.take_bodypart_damage(10 + 5 * extra_speed, check_armor = TRUE, wound_bonus = extra_speed * 5)
+ take_bodypart_damage(10 + 5 * extra_speed, check_armor = TRUE, wound_bonus = extra_speed * 5)
victim.DefaultCombatKnockdown(20)
DefaultCombatKnockdown(20)
- visible_message("[src] crashes into [victim], knocking them both over!",\
- "You violently crash into [victim]!")
+ visible_message("[src] crashes into [victim] [extra_speed ? "really hard" : ""], knocking them both over!",\
+ "You violently crash into [victim] [extra_speed ? "extra hard" : ""]!")
playsound(src,'sound/weapons/punch1.ogg',50,1)
@@ -196,12 +217,18 @@
adjustStaminaLossBuffered(I.getweight(src, STAM_COST_THROW_MULT, SKILL_THROW_STAM_COST))
if(thrown_thing)
- visible_message("[src] has thrown [thrown_thing].")
- log_message("has thrown [thrown_thing]", LOG_ATTACK)
+ var/power_throw = 0
+ if(HAS_TRAIT(src, TRAIT_HULK))
+ power_throw++
+ if(pulling && grab_state >= GRAB_NECK)
+ power_throw++
+ visible_message("[src] throws [thrown_thing][power_throw ? " really hard!" : "."]", \
+ "You throw [thrown_thing][power_throw ? " really hard!" : "."]")
+ log_message("has thrown [thrown_thing] [power_throw ? "really hard" : ""]", LOG_ATTACK)
do_attack_animation(target, no_effect = 1)
playsound(loc, 'sound/weapons/punchmiss.ogg', 50, 1, -1)
newtonian_move(get_dir(target, src))
- thrown_thing.safe_throw_at(target, thrown_thing.throw_range, thrown_thing.throw_speed, src, null, null, null, move_force, random_turn)
+ thrown_thing.safe_throw_at(target, thrown_thing.throw_range, thrown_thing.throw_speed + power_throw, src, null, null, null, move_force)
/mob/living/carbon/restrained(ignore_grab)
. = (handcuffed || (!ignore_grab && pulledby && pulledby.grab_state >= GRAB_AGGRESSIVE))
@@ -892,6 +919,9 @@
var/datum/disease/D = thing
if(D.severity != DISEASE_SEVERITY_POSITIVE)
D.cure(FALSE)
+ for(var/thing in all_wounds)
+ var/datum/wound/W = thing
+ W.remove_wound()
if(admin_revive)
regenerate_limbs()
regenerate_organs()
@@ -982,6 +1012,10 @@
if(SANITY_NEUTRAL to SANITY_GREAT)
. *= 0.90
+ for(var/i in status_effects)
+ var/datum/status_effect/S = i
+ . *= S.interact_speed_modifier()
+
/mob/living/carbon/proc/create_internal_organs()
for(var/X in internal_organs)
@@ -1170,3 +1204,47 @@
if(wear_mask)
if(wear_mask.flags_inv & HIDEEYES)
LAZYOR(., SLOT_GLASSES)
+
+// if any of our bodyparts are bleeding
+/mob/living/carbon/proc/is_bleeding()
+ for(var/i in bodyparts)
+ var/obj/item/bodypart/BP = i
+ if(BP.get_bleed_rate())
+ return TRUE
+
+// get our total bleedrate
+/mob/living/carbon/proc/get_total_bleed_rate()
+ var/total_bleed_rate = 0
+ for(var/i in bodyparts)
+ var/obj/item/bodypart/BP = i
+ total_bleed_rate += BP.get_bleed_rate()
+
+ return total_bleed_rate
+
+/**
+ * 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.
+ *
+ * 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
+ */
+/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/wound_type
+ if(forced_type)
+ if(islist(forced_type))
+ wound_type = pick(forced_type)
+ else
+ wound_type = forced_type
+ else
+ wound_type = pick(WOUND_LIST_BONE + WOUND_LIST_CUT + WOUND_LIST_BURN)
+
+ var/datum/wound/W = new wound_type
+ S.generate(BP, W)
+ S.fake = TRUE
+ QDEL_NULL(W)