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)