From 96bd6af79744a84881199581860fc4b0f26f9c54 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Sat, 9 May 2026 19:59:41 -0700 Subject: [PATCH 01/59] potato time --- code/__DEFINES/DNA.dm | 2 +- code/__DEFINES/mobs.dm | 3 + code/__DEFINES/pain.dm | 6 +- code/datums/injury/_injury.dm | 5 ++ code/datums/wounds/special.dm | 2 +- .../covens/coven_powers/bloodheal.dm | 2 +- .../spells/regeneration_cycle.dm | 2 +- .../crafting/alchemy/herbal_recipes.dm | 4 +- code/modules/crafting/alchemy/reagents.dm | 4 +- .../modules/mob/living/carbon/carbon_shock.dm | 19 +++--- .../mob/living/carbon/human/human_defense.dm | 5 ++ .../chemistry/reagents/medicine_reagents.dm | 8 ++- .../chemistry/reagents/toxin_reagents.dm | 2 +- .../spells/spell_types/pointed/absolve.dm | 2 +- .../spell_types/pointed/attach_bodypart.dm | 2 +- .../spells/spell_types/pointed/healing.dm | 2 +- .../spells/spell_types/pointed/lux_tamper.dm | 2 +- .../undirected/bardic/rejuvenation_song.dm | 4 +- .../surgery/bodyparts/bodypart_wounds.dm | 3 +- code/modules/surgery/organs/_organ.dm | 30 ++++++++-- code/modules/surgery/organs/internal/brain.dm | 58 +++++-------------- .../modules/surgery/organs/internal/spleen.dm | 2 +- .../surgery/organs/organ_processing/spleen.dm | 19 +++--- 23 files changed, 98 insertions(+), 90 deletions(-) diff --git a/code/__DEFINES/DNA.dm b/code/__DEFINES/DNA.dm index c9ba435039a..5b3b084287a 100644 --- a/code/__DEFINES/DNA.dm +++ b/code/__DEFINES/DNA.dm @@ -78,7 +78,7 @@ //organ defines #define STANDARD_ORGAN_THRESHOLD 100 -#define STANDARD_ORGAN_HEALING 0.001 +#define STANDARD_ORGAN_HEALING (50 / 100000) /// designed to fail organs when left to decay for ~15 minutes #define STANDARD_ORGAN_DECAY 0.00222 diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index cf1822f65bf..342183b048d 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -32,6 +32,9 @@ #define BLOOD_VOLUME_BLEEDOUT_PASSOUT BLOOD_VOLUME_NORMAL * 0.25 #define BLOOD_VOLUME_SURVIVE BLOOD_VOLUME_NORMAL * 0.2 +/// How efficiently humans regenerate blood. +#define BLOOD_REGEN_FACTOR 0.01 + //Sizes of mobs, used by mob/living/var/mob_size #define MOB_SIZE_TINY 0 #define MOB_SIZE_SMALL 1 diff --git a/code/__DEFINES/pain.dm b/code/__DEFINES/pain.dm index aa23811a184..f58412e99df 100644 --- a/code/__DEFINES/pain.dm +++ b/code/__DEFINES/pain.dm @@ -1,9 +1,5 @@ // ~pain levels when using the custom_pain proc and shit #define PAIN_EMOTE_MINIMUM 10 -#define PAIN_LEVEL_1 0 -#define PAIN_LEVEL_2 10 -#define PAIN_LEVEL_3 40 -#define PAIN_LEVEL_4 70 // ~shock stages #define SHOCK_STAGE_1 10 @@ -47,4 +43,4 @@ #define SHOCK_PENALTY_COOLDOWN_DURATION 5 SECONDS #define COOLDOWN_CARBON_ENDORPHINATION "carbon_endorphination" /// Cooldown before our body endorphinates itself again -#define ENDORPHINATION_COOLDOWN_DURATION 2 MINUTES +#define ENDORPHINATION_COOLDOWN_DURATION 1 MINUTES diff --git a/code/datums/injury/_injury.dm b/code/datums/injury/_injury.dm index 197dbe30ee0..8c84cc09e62 100644 --- a/code/datums/injury/_injury.dm +++ b/code/datums/injury/_injury.dm @@ -248,6 +248,11 @@ return FALSE return CEILING(get_bleed_rate() * 0.2, 0.1) +/datum/injury/proc/can_heal() + if(damage_type == WOUND_DIVINE) + return FALSE + return TRUE + // heal the given amount of damage, and if the given amount of damage was more // than what needed to be healed, return how much heal was left /datum/injury/proc/heal_damage(amount_heal) diff --git a/code/datums/wounds/special.dm b/code/datums/wounds/special.dm index a77ca8e2f9c..ace7bc822ef 100644 --- a/code/datums/wounds/special.dm +++ b/code/datums/wounds/special.dm @@ -174,7 +174,7 @@ "The tongue flies off in an arc!" ) woundpain = 8 - bleed_rate = 10 + bleed_rate = 5 can_cauterize = FALSE critical = TRUE associated_bclasses = ARTERY_BCLASSES diff --git a/code/modules/antagonists/villain/neu_vampires/covens/coven_powers/bloodheal.dm b/code/modules/antagonists/villain/neu_vampires/covens/coven_powers/bloodheal.dm index 1fdcdb7151d..f1422580f0d 100644 --- a/code/modules/antagonists/villain/neu_vampires/covens/coven_powers/bloodheal.dm +++ b/code/modules/antagonists/villain/neu_vampires/covens/coven_powers/bloodheal.dm @@ -55,7 +55,7 @@ for(var/datum/injury/injury in owner.all_injuries) if(!bashing_lethal_heal && !aggravated_heal) break - if(injury.damage_type == WOUND_DIVINE) + if(!injury.can_heal()) continue if(injury.damage_type == WOUND_BURN) aggravated_heal = injury.heal_damage(aggravated_heal) diff --git a/code/modules/crafting/alchemy/essence_machines/essence_gauntlet/spells/regeneration_cycle.dm b/code/modules/crafting/alchemy/essence_machines/essence_gauntlet/spells/regeneration_cycle.dm index d2c5f83ca50..6add52755f6 100644 --- a/code/modules/crafting/alchemy/essence_machines/essence_gauntlet/spells/regeneration_cycle.dm +++ b/code/modules/crafting/alchemy/essence_machines/essence_gauntlet/spells/regeneration_cycle.dm @@ -32,6 +32,6 @@ if(!iscarbon(owner)) return for(var/datum/injury/injury as anything in carbon.all_injuries) - if(injury.damage_type == WOUND_DIVINE) + if(!injury.can_heal()) continue injury.heal_damage(0.1) diff --git a/code/modules/crafting/alchemy/herbal_recipes.dm b/code/modules/crafting/alchemy/herbal_recipes.dm index 3ab83389530..3ae25d59563 100644 --- a/code/modules/crafting/alchemy/herbal_recipes.dm +++ b/code/modules/crafting/alchemy/herbal_recipes.dm @@ -77,7 +77,7 @@ /datum/reagent/medicine/herbal/calendula_salve/on_bodypart_absorb(obj/item/bodypart/bodypart, mob/living/carbon/M, amount_to_transfer) for(var/datum/injury/injury in bodypart.injuries) - if(injury.damage_type == WOUND_DIVINE) + if(!injury.can_heal()) continue injury.heal_damage(1) injury.salve_injury() @@ -398,7 +398,7 @@ /datum/reagent/medicine/herbal/paris_poultice/on_bodypart_absorb(obj/item/bodypart/bodypart, mob/living/carbon/M, amount_to_transfer) for(var/datum/injury/injury in bodypart.injuries) - if(injury.damage_type == WOUND_DIVINE) + if(!injury.can_heal()) continue if(injury.damage_type == WOUND_BURN) injury.heal_damage(0.5) diff --git a/code/modules/crafting/alchemy/reagents.dm b/code/modules/crafting/alchemy/reagents.dm index feb989dd591..417afd89b27 100644 --- a/code/modules/crafting/alchemy/reagents.dm +++ b/code/modules/crafting/alchemy/reagents.dm @@ -11,7 +11,7 @@ /datum/reagent/medicine/healthpot/on_bodypart_absorb(obj/item/bodypart/bodypart, mob/living/carbon/M, amount_to_transfer) for(var/datum/injury/injury in bodypart.injuries) - if(injury.damage_type == WOUND_DIVINE) + if(!injury.can_heal()) continue injury.heal_damage(1) @@ -48,7 +48,7 @@ /datum/reagent/medicine/healthpot/on_bodypart_absorb(obj/item/bodypart/bodypart, mob/living/carbon/M, amount_to_transfer) for(var/datum/injury/injury in bodypart.injuries) - if(injury.damage_type == WOUND_DIVINE) + if(!injury.can_heal()) continue injury.heal_damage(2) for(var/datum/wound/wound in bodypart.wounds) diff --git a/code/modules/mob/living/carbon/carbon_shock.dm b/code/modules/mob/living/carbon/carbon_shock.dm index a6764a13051..2e6d2999679 100644 --- a/code/modules/mob/living/carbon/carbon_shock.dm +++ b/code/modules/mob/living/carbon/carbon_shock.dm @@ -173,8 +173,6 @@ if((shock_stage >= SHOCK_STAGE_2) && (previous_shock_stage < SHOCK_STAGE_2)) emote("is having trouble keeping [p_their()] eyes open.") - //Attempt to inject combat cocktail for the first time - endorphinate() if((shock_stage >= SHOCK_STAGE_2) && (previous_shock_stage >= SHOCK_STAGE_2)) if(DT_PROB(3, delta_time)) @@ -189,14 +187,14 @@ emote("becomes limp.") if(!HAS_TRAIT(src, TRAIT_NOPAINSTUN)) Immobilize(rand(2, 3) SECONDS) - //Attempt to inject combat cocktail a second time - endorphinate() + endorphinate() if((shock_stage >= SHOCK_STAGE_4) && (previous_shock_stage >= SHOCK_STAGE_4)) if(DT_PROB(1, delta_time)) custom_pain("[pick("The pain is excruciating", "Please, just end the pain", "My whole body is going numb")]!", shock_stage, nopainloss = TRUE) if(!HAS_TRAIT(src, TRAIT_NOPAINSTUN)) Knockdown(2 SECONDS) + endorphinate() if(DT_PROB(2, delta_time)) emote("gasp") @@ -205,6 +203,7 @@ custom_pain("[pick("The pain is excruciating", "Please, just end the pain", "My whole body is going numb")]!", shock_stage, nopainloss = TRUE) if(!HAS_TRAIT(src, TRAIT_NOPAINSTUN)) Paralyze(5 SECONDS) + endorphinate() if((shock_stage >= SHOCK_STAGE_6) && (previous_shock_stage >= SHOCK_STAGE_6)) if(DT_PROB(1, delta_time)) @@ -212,29 +211,32 @@ custom_pain("[pick("I black out", "I feel like I could die at any moment now", "I'm about to lose consciousness")]!", shock_stage, nopainloss = TRUE) if(!HAS_TRAIT(src, TRAIT_NOPAINSTUN)) Unconscious(1 SECONDS) + endorphinate() if((shock_stage >= SHOCK_STAGE_7) && (previous_shock_stage < SHOCK_STAGE_7)) emote("gargle") if(!HAS_TRAIT(src, TRAIT_NOPAINSTUN)) Paralyze(5 SECONDS) - //Attempt to inject combat cocktail, even though at this point it won't help much - endorphinate() + //Attempt to inject combat cocktail, even though at this point it won't help much + endorphinate() if((shock_stage >= SHOCK_STAGE_7) && (previous_shock_stage >= SHOCK_STAGE_7)) if(!HAS_TRAIT(src, TRAIT_NOPAINSTUN)) Paralyze(5 SECONDS) + endorphinate() if(DT_PROB(1, delta_time)) Unconscious(5) + endorphinate() if(DT_PROB(4, delta_time)) emote("gargle") if((shock_stage >= SHOCK_STAGE_8) && (previous_shock_stage < SHOCK_STAGE_8)) - //Attempt to inject combat cocktail - ONE FINAL TIME - endorphinate() //Death is near... emote("scream") if(!HAS_TRAIT(src, TRAIT_NOPAINSTUN)) Unconscious(10 SECONDS) + //Attempt to inject combat cocktail - ONE FINAL TIME + endorphinate() if((shock_stage >= SHOCK_STAGE_8) && (previous_shock_stage >= SHOCK_STAGE_8)) //How the fuck are we still alive? @@ -244,3 +246,4 @@ //death_rattle() if(!HAS_TRAIT(src, TRAIT_NOPAINSTUN)) Unconscious(15 SECONDS) + endorphinate() diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 215c7597616..325773f90a5 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -772,3 +772,8 @@ if(C.body_parts_covered & def_zone.body_part) covering_part += C return covering_part + +/mob/living/carbon/human/getShock(painkiller_included) + . = ..() + if(dna?.species) + return . * dna?.species.pain_mod diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index 88f8e8e9322..4ecbf4aca12 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -169,7 +169,9 @@ /datum/reagent/medicine/ichor_of_mending/on_bodypart_absorb(obj/item/bodypart/BP, mob/living/carbon/M, amount_to_transfer) for(var/datum/injury/injury in BP.injuries) - if(injury.damage_type == WOUND_DIVINE || injury.damage_type == WOUND_BURN) + if(!injury.can_heal()) + continue + if(injury.damage_type == WOUND_BURN) continue injury.heal_damage(2) @@ -275,7 +277,7 @@ /datum/reagent/medicine/woundwrack_oil/on_bodypart_absorb(obj/item/bodypart/BP, mob/living/carbon/M, amount_to_transfer) for(var/datum/injury/injury in BP.injuries) - if(injury.damage_type == WOUND_DIVINE) + if(!injury.can_heal()) continue injury.heal_damage(2) injury.salve_injury() @@ -407,7 +409,7 @@ /datum/reagent/medicine/witchknit_paste/on_bodypart_absorb(obj/item/bodypart/BP, mob/living/carbon/M, amount_to_transfer) for(var/datum/injury/injury in BP.injuries) - if(injury.damage_type == WOUND_DIVINE) + if(!injury.can_heal()) continue injury.heal_damage(1.5) injury.salve_injury() diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm index d26ba2e436e..52929b0e99a 100644 --- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm @@ -484,7 +484,7 @@ if(!length(carbon.all_injuries)) return for(var/datum/injury/injury as anything in carbon.all_injuries) - if(injury.damage_type == WOUND_DIVINE) + if(!injury.can_heal()) continue injury.adjust_germ_level(reac_volume * 4) diff --git a/code/modules/spells/spell_types/pointed/absolve.dm b/code/modules/spells/spell_types/pointed/absolve.dm index d345826bba4..ffee38bdbe0 100644 --- a/code/modules/spells/spell_types/pointed/absolve.dm +++ b/code/modules/spells/spell_types/pointed/absolve.dm @@ -83,7 +83,7 @@ user.adjustCloneLoss(clone_transfer) for(var/datum/injury/injury in H.all_injuries) - if(injury.damage_type == WOUND_DIVINE) + if(!injury.can_heal()) continue injury.transfer_injury(user) diff --git a/code/modules/spells/spell_types/pointed/attach_bodypart.dm b/code/modules/spells/spell_types/pointed/attach_bodypart.dm index 7019eed9edc..86da0b74789 100644 --- a/code/modules/spells/spell_types/pointed/attach_bodypart.dm +++ b/code/modules/spells/spell_types/pointed/attach_bodypart.dm @@ -30,7 +30,7 @@ for(var/datum/wound/limb_wounds as anything in limb.wounds) qdel(limb_wounds) for(var/datum/injury/limb_wounds as anything in limb.injuries) - if(limb_wounds.damage_type == WOUND_DIVINE) + if(!limb_wounds.can_heal()) continue qdel(limb_wounds) limb.update_damages() diff --git a/code/modules/spells/spell_types/pointed/healing.dm b/code/modules/spells/spell_types/pointed/healing.dm index 546550bff91..04bf78408b2 100644 --- a/code/modules/spells/spell_types/pointed/healing.dm +++ b/code/modules/spells/spell_types/pointed/healing.dm @@ -266,7 +266,7 @@ if(affecting) affecting.heal_wounds(amount_healed * wound_modifier, src) for(var/datum/injury/injury as anything in affecting.injuries) - if(injury.damage_type == WOUND_DIVINE) + if(!injury.can_heal()) continue injury.heal_damage(amount_healed) C.update_damage_overlays() diff --git a/code/modules/spells/spell_types/pointed/lux_tamper.dm b/code/modules/spells/spell_types/pointed/lux_tamper.dm index caa5c978bf1..4f7318b5b41 100644 --- a/code/modules/spells/spell_types/pointed/lux_tamper.dm +++ b/code/modules/spells/spell_types/pointed/lux_tamper.dm @@ -56,7 +56,7 @@ t_BP.remove_wound(targetwound.type) for(var/datum/injury/injury in C_target.all_injuries) - if(injury.damage_type == WOUND_DIVINE) + if(!injury.can_heal()) continue injury.transfer_injury(C_caster) diff --git a/code/modules/spells/spell_types/undirected/bardic/rejuvenation_song.dm b/code/modules/spells/spell_types/undirected/bardic/rejuvenation_song.dm index 5893a6c8c71..0bff41dac51 100644 --- a/code/modules/spells/spell_types/undirected/bardic/rejuvenation_song.dm +++ b/code/modules/spells/spell_types/undirected/bardic/rejuvenation_song.dm @@ -48,6 +48,6 @@ for(var/datum/injury/injury in human.all_injuries) if(!healing_on_tick) break - if(injury.damage_type == WOUND_DIVINE) - continue + if(!injury.can_heal()) + continue healing_on_tick = injury.heal_damage(healing_on_tick) diff --git a/code/modules/surgery/bodyparts/bodypart_wounds.dm b/code/modules/surgery/bodyparts/bodypart_wounds.dm index 8f5dccf05d7..5cc46b24e67 100644 --- a/code/modules/surgery/bodyparts/bodypart_wounds.dm +++ b/code/modules/surgery/bodyparts/bodypart_wounds.dm @@ -255,7 +255,8 @@ var/list/candidates = list() for(var/wound_type in GLOB.primordial_wounds) var/datum/wound/primordial = GLOB.primordial_wounds[wound_type] - if(IS_ABSTRACT(primordial)) + var/datum/primoridial_type = primordial.type + if(IS_ABSTRACT(primoridial_type)) continue if(!primordial.can_roll) continue diff --git a/code/modules/surgery/organs/_organ.dm b/code/modules/surgery/organs/_organ.dm index 3bebcafd18c..8de9a95f6b0 100644 --- a/code/modules/surgery/organs/_organ.dm +++ b/code/modules/surgery/organs/_organ.dm @@ -109,6 +109,9 @@ /// The above, but for tool behaviors var/list/healing_tools = list(TOOL_SUTURE) + /// Thresholds organs can naturally heal down to + var/self_heal_thresholds = list(0.3, 0.6, 0.9) + /obj/item/organ/Initialize() . = ..() START_PROCESSING(SSobj, src) @@ -507,7 +510,7 @@ // Damage decrements by a percent of maxhealth if(can_heal(delta_time, times_fired) && damage) - handle_healing(delta_time, times_fired) + handle_self_healing(delta_time, times_fired) ///Organs don't die instantly, and neither should you when you get fucked up /obj/item/organ/proc/handle_failing_organ(delta_time, times_fired) @@ -517,7 +520,6 @@ failure_time += delta_time organ_failure(delta_time) - /// healing checks /obj/item/organ/proc/can_heal(delta_time, times_fired) . = TRUE @@ -525,8 +527,8 @@ return FALSE if(healing_factor <= 0) return FALSE - if((damage > maxHealth/5) && !owner.get_chem_effect(CE_ORGAN_REGEN)) - return FALSE + if(owner.get_chem_effect(CE_ORGAN_REGEN)) + return TRUE if(is_dead()) return FALSE if(current_blood <= 0) @@ -536,10 +538,26 @@ if(owner.get_chem_effect(CE_TOXIN)) return FALSE -/obj/item/organ/proc/handle_healing(delta_time, times_fired) +/obj/item/organ/proc/handle_self_healing(delta_time, times_fired) if(damage <= 0) return - applyOrganDamage(-healing_factor * delta_time, damage) + + ///Damage decrements by a percent of its maxhealth + var/healing_amount = healing_factor * delta_time * maxHealth + ///Damage decrements again by a percent of its maxhealth, depending on the owner's health + healing_amount += (owner.satiety > 0) ? (healing_factor * (owner.satiety / MAX_SATIETY)) : 0 + + var/max_healing_amount = 0 + for(var/i in self_heal_thresholds) + var/limit = i * maxHealth + if(damage >= limit) + max_healing_amount = damage - limit + if(max_healing_amount) + healing_amount = min(max_healing_amount, healing_amount) + + if(healing_amount <= 0) + return + applyOrganDamage(-healing_amount, damage) // pass curent damage incase we are over cap //this doesn't seem very right at all... owner.adjust_nutrition(-nutriment_req/100 * (0.5 * delta_time)) owner.adjust_hydration(-hydration_req/100 * (0.5 * delta_time)) diff --git a/code/modules/surgery/organs/internal/brain.dm b/code/modules/surgery/organs/internal/brain.dm index f20d2f4707e..3de6c183826 100644 --- a/code/modules/surgery/organs/internal/brain.dm +++ b/code/modules/surgery/organs/internal/brain.dm @@ -300,26 +300,22 @@ return ..() /obj/item/organ/brain/can_heal(delta_time, times_fired) - . = TRUE - if(!owner) - return FALSE - if(healing_factor <= 0) - return FALSE - if(is_dead()) - return FALSE - if(current_blood <= 0) - return FALSE - if(owner.undergoing_cardiac_arrest()) - return FALSE - var/effective_blood_oxygenation = GET_EFFECTIVE_BLOOD_VOL(owner.get_blood_oxygenation(), owner.total_blood_req) - if(effective_blood_oxygenation < BLOOD_VOLUME_SAFE) - return FALSE - // if stable and not too damaged we can heal - if(!past_damage_threshold(3) && owner.get_chem_effect(CE_STABLE)) - return TRUE - // else, we only naturally regen to basically get rounded - if(!(damage % damage_threshold_value) || owner.get_chem_effect(CE_BRAIN_REGEN)) - return FALSE + . = TRUE + if(!owner) + return FALSE + if(healing_factor <= 0) + return FALSE + if(owner.get_chem_effect(CE_BRAIN_REGEN)) + return TRUE + if(is_dead()) + return FALSE + if(current_blood <= 0) + return FALSE + if(owner.undergoing_cardiac_arrest()) + return FALSE + var/effective_blood_oxygenation = GET_EFFECTIVE_BLOOD_VOL(owner.get_blood_oxygenation(), owner.total_blood_req) + if(effective_blood_oxygenation < BLOOD_VOLUME_SAFE) + return FALSE /obj/item/organ/brain/proc/past_damage_threshold(threshold) return (get_current_damage_threshold() > threshold) @@ -367,28 +363,6 @@ else return brain_message -/obj/item/organ/brain/can_heal(delta_time, times_fired) - . = TRUE - if(!owner) - return FALSE - if(healing_factor <= 0) - return FALSE - if(is_dead()) - return FALSE - if(current_blood <= 0) - return FALSE - if(owner.undergoing_cardiac_arrest()) - return FALSE - var/effective_blood_oxygenation = GET_EFFECTIVE_BLOOD_VOL(owner.get_blood_oxygenation(), owner.total_blood_req) - if(effective_blood_oxygenation < BLOOD_VOLUME_SAFE) - return FALSE - // if stable and not too damaged we can heal - if(!past_damage_threshold(3) && owner.get_chem_effect(CE_STABLE)) - return TRUE - // else, we only naturally regen to basically get rounded - if(!(damage % damage_threshold_value) || owner.get_chem_effect(CE_BRAIN_REGEN)) - return FALSE - /obj/item/organ/brain/applyOrganDamage(amount, maximum = maxHealth, silent = FALSE) if(!amount) //Micro-optimization. return diff --git a/code/modules/surgery/organs/internal/spleen.dm b/code/modules/surgery/organs/internal/spleen.dm index 073a4b55a2b..341c253af91 100644 --- a/code/modules/surgery/organs/internal/spleen.dm +++ b/code/modules/surgery/organs/internal/spleen.dm @@ -21,7 +21,7 @@ nutriment_req = 2.5 hydration_req = 1 - var/blood_regen_factor = 0.01 // how much blood the spleen regenerates per efficiency point, per 2 seconds + var/blood_regen_factor = BLOOD_REGEN_FACTOR // how much blood the spleen regenerates per efficiency point, per 2 seconds /obj/item/organ/spleen/on_owner_examine(datum/source, mob/user, list/examine_list) if(!ishuman(owner)) diff --git a/code/modules/surgery/organs/organ_processing/spleen.dm b/code/modules/surgery/organs/organ_processing/spleen.dm index 203f3575b6b..037fda5b7fd 100644 --- a/code/modules/surgery/organs/organ_processing/spleen.dm +++ b/code/modules/surgery/organs/organ_processing/spleen.dm @@ -11,13 +11,13 @@ var/nutrition_ratio = 0 switch(owner.nutrition) if(0 to NUTRITION_LEVEL_STARVING) - nutrition_ratio = 0.2 - if(NUTRITION_LEVEL_STARVING to NUTRITION_LEVEL_HUNGRY) nutrition_ratio = 0.4 - if(NUTRITION_LEVEL_HUNGRY to NUTRITION_LEVEL_FED) + if(NUTRITION_LEVEL_STARVING to NUTRITION_LEVEL_HUNGRY) nutrition_ratio = 0.6 - if(NUTRITION_LEVEL_FED to NUTRITION_LEVEL_WELL_FED) + if(NUTRITION_LEVEL_HUNGRY to NUTRITION_LEVEL_FED) nutrition_ratio = 0.8 + if(NUTRITION_LEVEL_FED to NUTRITION_LEVEL_WELL_FED) + nutrition_ratio = 1 else nutrition_ratio = 1 if(owner.satiety > 80) @@ -29,13 +29,14 @@ for(var/thing in spleens) var/obj/item/organ/spleen/spleen = thing blood_regen += (spleen.get_slot_efficiency(ORGAN_SLOT_SPLEEN) * spleen.blood_regen_factor) - combined_nutrition_requirement += (spleen.nutriment_req/20) - blood_regen *= (1 + owner.get_chem_effect(CE_BLOODRESTORE)) - combined_nutrition_requirement *= (1 + (owner.get_chem_effect(CE_BLOODRESTORE) * 0.5)) + combined_nutrition_requirement += (spleen.nutriment_req/40) + var/blood_restore_multiplier = owner.get_chem_effect(CE_BLOODRESTORE) + blood_regen *= (1 + blood_restore_multiplier) + combined_nutrition_requirement *= (1 + (blood_restore_multiplier * 0.5)) if(!blood_regen) return - owner.adjust_nutrition(-combined_nutrition_requirement * nutrition_ratio * 0.5 * delta_time) - owner.adjust_bloodvolume(CEILING(blood_regen * nutrition_ratio * 0.5 * delta_time, 0.1)) + owner.adjust_nutrition(-combined_nutrition_requirement * nutrition_ratio * delta_time) + owner.adjust_bloodvolume(CEILING(blood_regen * nutrition_ratio * delta_time, 0.1)) return TRUE /// Blood volume adjust proc From d9794788efa645959e453e543fdaec482073de4d Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Sat, 9 May 2026 20:02:45 -0700 Subject: [PATCH 02/59] seconds --- code/__DEFINES/pain.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/__DEFINES/pain.dm b/code/__DEFINES/pain.dm index f58412e99df..8b416a19350 100644 --- a/code/__DEFINES/pain.dm +++ b/code/__DEFINES/pain.dm @@ -43,4 +43,4 @@ #define SHOCK_PENALTY_COOLDOWN_DURATION 5 SECONDS #define COOLDOWN_CARBON_ENDORPHINATION "carbon_endorphination" /// Cooldown before our body endorphinates itself again -#define ENDORPHINATION_COOLDOWN_DURATION 1 MINUTES +#define ENDORPHINATION_COOLDOWN_DURATION 30 SECONDS From 265972d41adf70c0ca807cc11bc45dcf578dbfad Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Sat, 9 May 2026 21:19:33 -0700 Subject: [PATCH 03/59] arteries --- code/datums/components/butchering.dm | 2 +- code/datums/wounds/arteries.dm | 26 ++++++++----------- .../objects/items/weapons/melee/godweapons.dm | 2 +- code/modules/mob/living/carbon/human/human.dm | 2 +- code/modules/mob/living/living_wounds.dm | 2 +- 5 files changed, 15 insertions(+), 19 deletions(-) diff --git a/code/datums/components/butchering.dm b/code/datums/components/butchering.dm index d8bdf4fac71..31573d8ab27 100644 --- a/code/datums/components/butchering.dm +++ b/code/datums/components/butchering.dm @@ -74,7 +74,7 @@ H.apply_damage(source.force, BRUTE, BODY_ZONE_HEAD) // easy tiger, we'll get to that in a sec var/obj/item/bodypart/slit_throat = H.get_bodypart(BODY_ZONE_PRECISE_NECK) if(slit_throat) - slit_throat.add_wound(/datum/wound/artery/dissect/neck) + slit_throat.add_wound(/datum/wound/artery/neck_slice) /datum/component/butchering/proc/Butcher(mob/living/butcher, mob/living/meat) var/turf/T = meat.drop_location() diff --git a/code/datums/wounds/arteries.dm b/code/datums/wounds/arteries.dm index e382d2a67ef..7cd6a162bf0 100644 --- a/code/datums/wounds/arteries.dm +++ b/code/datums/wounds/arteries.dm @@ -19,19 +19,12 @@ crit_message = "Blood sprays from %VICTIM's %BODYPART!" var/artery_type_override -/datum/wound/artery/get_crit_prob(bclass, dam, damage_dividend, mob/living/user, obj/item/bodypart/affected, zone_precise, list/modifiers) - if(affected.limb_flags & BODYPART_BONE_ENCASED && !affected.has_wound(/datum/wound/fracture)) - return 0 - return ..() - /datum/wound/artery/can_apply_to_bodypart(obj/item/bodypart/affected) - . = ..() if(affected.status == BODYPART_ROBOTIC) return FALSE if(!affected.get_cut()) return FALSE - if(affected.limb_flags & BODYPART_BONE_ENCASED && !affected.has_wound(/datum/wound/fracture)) - return FALSE + return ..() /datum/wound/artery/can_stack_with(datum/wound/other) if(istype(other, /datum/wound/artery) && (type == other.type)) @@ -62,18 +55,21 @@ artery.tear() qdel(src) -/datum/wound/artery/neck +/datum/wound/artery/neck_slice + severity = WOUND_SEVERITY_CRITICAL artery_type_override = /obj/item/organ/artery/neck can_roll = FALSE //snowflake used for neck slit -/datum/wound/artery/chest +/datum/wound/artery/heart + name = "aortic dissection" + severity = WOUND_SEVERITY_FATAL artery_type_override = /obj/item/organ/artery/chest associated_bclasses = ARTERY_HEART_BCLASSES viable_zones = list(BODY_ZONE_CHEST) + mortal = TRUE -/datum/wound/artery/dissect - severity = WOUND_SEVERITY_CRITICAL +/datum/wound/artery/heart/can_apply_to_bodypart(obj/item/bodypart/affected) + if(affected.limb_flags & BODYPART_BONE_ENCASED && !affected.has_wound(/datum/wound/fracture)) + return FALSE + return ..() -/datum/wound/artery/dissect/neck - artery_type_override = /obj/item/organ/artery/neck - can_roll = FALSE //snowflake used for neck slit diff --git a/code/game/objects/items/weapons/melee/godweapons.dm b/code/game/objects/items/weapons/melee/godweapons.dm index f497c7c3fb7..690d280ca03 100644 --- a/code/game/objects/items/weapons/melee/godweapons.dm +++ b/code/game/objects/items/weapons/melee/godweapons.dm @@ -75,7 +75,7 @@ if(check_zone(user.zone_selected) != BODY_ZONE_CHEST) return var/mob/living/carbon/human/H = target - var/heart_crit = H.has_wound(/datum/wound/artery/chest) + var/heart_crit = H.has_wound(/datum/wound/artery/heart) var/dead = H.stat == DEAD if((H.health < H.crit_threshold) || heart_crit || dead) var/fast = heart_crit || dead diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 10303f6969c..9f9f2cc537d 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -17,7 +17,7 @@ if(do_after(user, 5 SECONDS, src)) var/obj/item/bodypart/part = src.get_bodypart(BODY_ZONE_PRECISE_NECK) part.add_wound(/datum/wound/slash) - part.add_wound(/datum/wound/artery/neck) + part.add_wound(/datum/wound/artery/neck_slice) else if(held_item && (user.zone_selected == BODY_ZONE_PRECISE_SKULL)) if(held_item.get_sharpness() && held_item.wlength == WLENGTH_SHORT) diff --git a/code/modules/mob/living/living_wounds.dm b/code/modules/mob/living/living_wounds.dm index 17d491ec879..368b1785619 100644 --- a/code/modules/mob/living/living_wounds.dm +++ b/code/modules/mob/living/living_wounds.dm @@ -207,7 +207,7 @@ dam += 30 used = round(max(dam / 3, 1), 1) if(prob(used)) - LAZYADD(attempted_wounds, /datum/wound/artery/chest) + LAZYADD(attempted_wounds, /datum/wound/artery/heart) if(!LAZYLEN(attempted_wounds)) return FALSE From 665eb6d5a5d65a4a4acfc2d96351afea409dca45 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Sat, 9 May 2026 21:27:08 -0700 Subject: [PATCH 04/59] artery guh mortal try this --- code/datums/injury/_injury.dm | 1 - code/datums/wounds/arteries.dm | 9 ++------- code/modules/surgery/bodyparts/_bodyparts.dm | 1 - code/modules/surgery/bodyparts/bodypart_examine.dm | 4 ++-- code/modules/surgery/bodyparts/bodypart_wounds.dm | 7 +++++-- 5 files changed, 9 insertions(+), 13 deletions(-) diff --git a/code/datums/injury/_injury.dm b/code/datums/injury/_injury.dm index 8c84cc09e62..cae0834bb45 100644 --- a/code/datums/injury/_injury.dm +++ b/code/datums/injury/_injury.dm @@ -393,7 +393,6 @@ bad_embeddies += 1 return max(0.1, (bleed_rate * damage)/10 + bad_embeddies) - /datum/injury/proc/is_surgical() if(CHECK_BITFIELD(injury_flags, INJURY_SURGICAL)) return TRUE diff --git a/code/datums/wounds/arteries.dm b/code/datums/wounds/arteries.dm index 7cd6a162bf0..03b4eaf7489 100644 --- a/code/datums/wounds/arteries.dm +++ b/code/datums/wounds/arteries.dm @@ -5,12 +5,6 @@ 'sound/gore/artery3.ogg') severity = WOUND_SEVERITY_SEVERE critical = TRUE - mortal = FALSE - can_sew = TRUE - can_cauterize = TRUE - embed_chance = 0 - werewolf_infection_probability = 50 - sleep_healing = 0 associated_bclasses = ARTERY_BCLASSES min_damage = 5 min_damage_dividend = 0 @@ -59,9 +53,10 @@ severity = WOUND_SEVERITY_CRITICAL artery_type_override = /obj/item/organ/artery/neck can_roll = FALSE //snowflake used for neck slit + show_in_book = FALSE /datum/wound/artery/heart - name = "aortic dissection" + name = "Aortic Dissection" severity = WOUND_SEVERITY_FATAL artery_type_override = /obj/item/organ/artery/chest associated_bclasses = ARTERY_HEART_BCLASSES diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index c68f00d7718..30698b81f63 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -1570,7 +1570,6 @@ return internal_incision return incision - /obj/item/bodypart/proc/get_cut(strict = FALSE, ignore_gauze = FALSE) if(ignore_gauze && (bandage)) return diff --git a/code/modules/surgery/bodyparts/bodypart_examine.dm b/code/modules/surgery/bodyparts/bodypart_examine.dm index 3fe68ec36a6..029c7aa8e3a 100644 --- a/code/modules/surgery/bodyparts/bodypart_examine.dm +++ b/code/modules/surgery/bodyparts/bodypart_examine.dm @@ -235,9 +235,9 @@ for(var/obj/item/organ/possible_artery in shuffle(getorganslotlist(ORGAN_SLOT_ARTERY))) if(possible_artery.is_bruised()) if(get_cut()) - status += span_bloody("[possible_artery.name]'s been cut") + status += uppertext(span_bloody("cut [parse_zone(possible_artery.zone)]")) else - status += span_bloody("internal bleeding") + status += uppertext(span_bloody("bruised [parse_zone(possible_artery.zone)]")) if(skeletonized) status += "SKELETON" diff --git a/code/modules/surgery/bodyparts/bodypart_wounds.dm b/code/modules/surgery/bodyparts/bodypart_wounds.dm index 5cc46b24e67..e1d1a3c9719 100644 --- a/code/modules/surgery/bodyparts/bodypart_wounds.dm +++ b/code/modules/surgery/bodyparts/bodypart_wounds.dm @@ -119,13 +119,14 @@ /// Returns the total bleed rate on this bodypart /obj/item/bodypart/proc/get_bleed_rate(artifical = FALSE) - if(NOBLOOD in owner?.dna?.species?.species_traits) + var/datum/species/physiology = owner?.dna?.species + if(NOBLOOD in physiology?.species_traits) return 0 if(!bleeds) return 0 var/bleed_rate = 0 for(var/datum/wound/wound as anything in wounds) - bleed_rate += (wound.bleed_rate * owner.dna.species.bleed_mod) + bleed_rate += wound.bleed_rate for(var/datum/injury/injury as anything in injuries) if(!artifical) @@ -138,6 +139,8 @@ if(!embedded.embedding.embedded_bloodloss) continue bleed_rate += embedded.embedding.embedded_bloodloss + if(physiology) + bleed_rate *= physiology.bleed_mod if(bandage) bleed_rate *= bandage?.bandage_effectiveness for(var/obj/item/grabbing/grab in grabbedby) From 80831980cc942bbdef12d9414456d8158e72dbdf Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Sun, 10 May 2026 08:09:42 -0700 Subject: [PATCH 05/59] pull back on injury bleeds --- code/datums/injury/organic/bite.dm | 1 - code/datums/injury/organic/puncture.dm | 1 - 2 files changed, 2 deletions(-) diff --git a/code/datums/injury/organic/bite.dm b/code/datums/injury/organic/bite.dm index a3ada224acb..d5f318ff104 100644 --- a/code/datums/injury/organic/bite.dm +++ b/code/datums/injury/organic/bite.dm @@ -1,7 +1,6 @@ /** BITES **/ /datum/injury/bite bleed_threshold = 5 - bleed_rate = 2 damage_type = WOUND_BITE /datum/injury/bite/small diff --git a/code/datums/injury/organic/puncture.dm b/code/datums/injury/organic/puncture.dm index 429e355946b..8cdb534a74d 100644 --- a/code/datums/injury/organic/puncture.dm +++ b/code/datums/injury/organic/puncture.dm @@ -1,6 +1,5 @@ /datum/injury/puncture bleed_threshold = 10 - bleed_rate = 3 damage_type = WOUND_PIERCE /datum/injury/puncture/can_worsen(damage_type, damage) From 1dc67aa34eef69616290b727d0c4d808e1c8ef37 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Sun, 10 May 2026 08:26:12 -0700 Subject: [PATCH 06/59] still wrong --- code/modules/crafting/alchemy/reagents.dm | 4 ++-- code/modules/mob/living/carbon/damage_procs.dm | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/code/modules/crafting/alchemy/reagents.dm b/code/modules/crafting/alchemy/reagents.dm index 417afd89b27..69a7f6fca42 100644 --- a/code/modules/crafting/alchemy/reagents.dm +++ b/code/modules/crafting/alchemy/reagents.dm @@ -15,12 +15,12 @@ continue injury.heal_damage(1) -/datum/reagent/medicine/healthpot/healthpot/on_mob_metabolize(mob/living/L) +/datum/reagent/medicine/healthpot/on_mob_metabolize(mob/living/L) . = ..() L.add_chem_effect(CE_BLOODRESTORE, 5, "[type]") L.add_chem_effect(CE_STABLE, 1, "[type]") -/datum/reagent/medicine/healthpot/healthpot/on_mob_end_metabolize(mob/living/L) +/datum/reagent/medicine/healthpot/on_mob_end_metabolize(mob/living/L) . = ..() L.remove_chem_effect(CE_BLOODRESTORE, "[type]") L.remove_chem_effect(CE_STABLE, "[type]") diff --git a/code/modules/mob/living/carbon/damage_procs.dm b/code/modules/mob/living/carbon/damage_procs.dm index 02cacb6d418..f6a978a85ac 100644 --- a/code/modules/mob/living/carbon/damage_procs.dm +++ b/code/modules/mob/living/carbon/damage_procs.dm @@ -250,7 +250,9 @@ if(!forced && (TIMER_COOLDOWN_CHECK(src, COOLDOWN_CARBON_ENDORPHINATION) || (diceroll(endurance, context = DICE_CONTEXT_MENTAL) <= DICE_FAILURE))) return + var/current_body_amount = reagents.get_reagent_amount(/datum/reagent/medicine/endorphin) var/endorphin_amount = clamp(endurance, 5, 29) + endorphin_amount = min(endorphin_amount, 30 - current_body_amount) reagents?.add_reagent(/datum/reagent/medicine/endorphin, endorphin_amount) TIMER_COOLDOWN_START(src, COOLDOWN_CARBON_ENDORPHINATION, HAS_TRAIT(src, TRAIT_PSYDONIAN_GRIT) ? ENDORPHINATION_COOLDOWN_DURATION * 0.75 : ENDORPHINATION_COOLDOWN_DURATION) if(!silent) From c48d24caea7cc4285b030d0fc3c5f7b1f8d55820 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Sun, 10 May 2026 08:29:50 -0700 Subject: [PATCH 07/59] doubly effective painkillers --- code/__DEFINES/pain.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/__DEFINES/pain.dm b/code/__DEFINES/pain.dm index 8b416a19350..79415763f7c 100644 --- a/code/__DEFINES/pain.dm +++ b/code/__DEFINES/pain.dm @@ -32,7 +32,7 @@ #define PAIN_NO_SPEAK 250 /// Divisor used in several pain calculations -#define PAINKILLER_DIVISOR 4 +#define PAINKILLER_DIVISOR 2 #define PAIN_KNOCKDOWN_MESSAGE "gives in to the pain!" #define PAIN_KNOCKDOWN_MESSAGE_SELF "I give in to the pain!" From 6c0fe9ed8a7faa905fca93231cbc80770d13034b Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Sun, 10 May 2026 08:47:57 -0700 Subject: [PATCH 08/59] heal injuries when drunk --- code/modules/crafting/alchemy/reagents.dm | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/code/modules/crafting/alchemy/reagents.dm b/code/modules/crafting/alchemy/reagents.dm index 69a7f6fca42..eb6741d8c90 100644 --- a/code/modules/crafting/alchemy/reagents.dm +++ b/code/modules/crafting/alchemy/reagents.dm @@ -31,6 +31,10 @@ var/list/wCount = M.get_wounds() if(wCount.len > 0) M.heal_wounds(3 * efficiency) //at a motabalism of .5 U a tick this translates to 120WHP healing with 20 U Most wounds are unsewn 15-100. This is powerful on single wounds but rapidly weakens at multi wounds. + for(var/datum/injury/injury in M.all_injuries) + if(!injury.can_heal()) + continue + injury.heal_damage(1/10 * efficiency) if(volume > 0.99) M.adjustBruteLoss(-1.75*REM * efficiency, 0) M.adjustFireLoss(-1.75*REM * efficiency, 0) @@ -70,6 +74,10 @@ if(volume >= 60) M.remove_reagent(/datum/reagent/medicine/stronghealth, 2) //No overhealing. M.heal_wounds(6 * efficiency) //at a motabalism of .5 U a tick this translates to 240WHP healing with 20 U Most wounds are unsewn 15-100. + for(var/datum/injury/injury in M.all_injuries) + if(!injury.can_heal()) + continue + injury.heal_damage(2/10 * efficiency) if(volume > 0.99) M.adjustBruteLoss(-7*REM * efficiency, 0) M.adjustFireLoss(-7*REM * efficiency, 0) From a5c413eaec5c8d0d47e5865032b660278c3bbc65 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Sun, 10 May 2026 14:07:06 -0700 Subject: [PATCH 09/59] other way --- code/modules/surgery/bodyparts/bodypart_examine.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/modules/surgery/bodyparts/bodypart_examine.dm b/code/modules/surgery/bodyparts/bodypart_examine.dm index 029c7aa8e3a..31860eb1b6c 100644 --- a/code/modules/surgery/bodyparts/bodypart_examine.dm +++ b/code/modules/surgery/bodyparts/bodypart_examine.dm @@ -235,9 +235,9 @@ for(var/obj/item/organ/possible_artery in shuffle(getorganslotlist(ORGAN_SLOT_ARTERY))) if(possible_artery.is_bruised()) if(get_cut()) - status += uppertext(span_bloody("cut [parse_zone(possible_artery.zone)]")) + status += span_bloody(uppertext("cut [parse_zone(possible_artery.zone)]")) else - status += uppertext(span_bloody("bruised [parse_zone(possible_artery.zone)]")) + status += span_bloody(uppertext("bruised [parse_zone(possible_artery.zone)]")) if(skeletonized) status += "SKELETON" From c20a967de24d03a4a2b62c4e342adac88a04f4c6 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Mon, 11 May 2026 01:36:12 -0700 Subject: [PATCH 10/59] blood refactor --- code/__DEFINES/DNA.dm | 1 - .../dcs/signals/signals_mob/signals_living.dm | 6 + code/__DEFINES/living.dm | 13 + code/__DEFINES/medical.dm | 3 +- code/__DEFINES/mobs.dm | 8 +- code/__DEFINES/traits/definitions.dm | 2 + code/_globalvars/special_traits/traits.dm | 2 +- code/_globalvars/traits.dm | 1 + code/datums/ai/behaviours/recuperate.dm | 3 +- code/datums/ai/controllers/mirespider.dm | 5 +- code/datums/ai/subtrees/human_basic_attack.dm | 2 +- code/datums/components/aura_healing.dm | 3 +- code/datums/components/rotting.dm | 3 +- code/datums/enchantments/bloodthirsty.dm | 4 +- code/datums/injury/_injury.dm | 23 +- code/datums/injury/organic/burn.dm | 2 +- code/datums/mana/mana_pool.dm | 2 +- code/datums/mana/overload.dm | 2 +- code/datums/quirks/vices/mental_vice.dm | 4 +- code/datums/status_effects/rogue/roguebuff.dm | 3 +- code/datums/wounds/black_briar_curse.dm | 2 +- code/game/atom/atoms.dm | 2 +- code/game/objects/items/inquisition_relics.dm | 11 +- code/game/objects/items/mageitems.dm | 2 +- code/game/objects/items/natural/cloth.dm | 2 +- code/game/turfs/open/water.dm | 4 +- code/modules/antagonists/villain/lich/lich.dm | 2 +- .../villain/lich/spells/raise_undead.dm | 2 +- .../villain/neu_vampires/bloodsuck.dm | 6 +- .../covens/coven_powers/bloodheal.dm | 3 +- .../villain/neu_vampires/frenzy.dm | 2 +- .../neu_vampires/living_modifications.dm | 8 +- .../villain/neu_vampires/use_blood.dm | 42 ++- .../villain/overlord/_antagonist.dm | 2 +- .../antagonists/villain/vampire/_vampire.dm | 7 +- .../antagonists/villain/vampire/actions.dm | 5 +- .../werewolf/werewolf_transformation.dm | 4 +- .../antagonists/villain/zizo_cult/rituals.dm | 4 +- code/modules/antagonists/villain/zomble.dm | 4 +- code/modules/fishing/leeches.dm | 15 +- code/modules/jobs/job_types/other/skeleton.dm | 2 +- code/modules/mob/living/blood.dm | 273 ++++++++++++++---- code/modules/mob/living/carbon/blood.dm | 6 +- code/modules/mob/living/carbon/carbon.dm | 8 +- .../mob/living/carbon/carbon_defines.dm | 1 - .../modules/mob/living/carbon/damage_procs.dm | 4 +- code/modules/mob/living/carbon/examine.dm | 4 +- code/modules/mob/living/carbon/human/human.dm | 6 +- .../mob/living/carbon/human/human_defense.dm | 19 +- .../mob/living/carbon/human/init_signals.dm | 21 ++ .../carbon/human/npc/skeleton/_skeleton.dm | 2 +- .../mob/living/carbon/human/species.dm | 4 +- .../species_types/automatons/_automaton.dm | 2 +- code/modules/mob/living/carbon/life.dm | 18 +- code/modules/mob/living/life.dm | 5 +- code/modules/mob/living/living.dm | 56 ++-- code/modules/mob/living/living_defines.dm | 8 +- .../mob/living/simple_animal/examine.dm | 19 +- .../mob/living/simple_animal/friendly/pet.dm | 1 - .../hostile/retaliate/creacher/shade.dm | 2 +- .../hostile/retaliate/retaliate.dm | 1 - code/modules/projectiles/projectile.dm | 2 +- .../chemistry/reagents/other_reagents.dm | 8 +- .../spells/spell_types/pointed/absolve.dm | 9 +- .../spells/spell_types/pointed/avert.dm | 3 +- .../spell_types/pointed/create_abyssoid.dm | 6 +- .../spells/spell_types/pointed/healing.dm | 2 +- .../spell_types/pointed/ocean_embrace.dm | 2 +- .../pointed/projectile/blood_steal.dm | 2 +- .../spells/spell_types/pointed/revive.dm | 2 +- .../undirected/bardic/rejuvenation_song.dm | 3 +- .../undirected/shapeshift/_shape_status.dm | 4 +- code/modules/surgery/bodyparts/_bodyparts.dm | 5 +- .../surgery/bodyparts/bodypart_examine.dm | 4 +- .../surgery/bodyparts/bodypart_wounds.dm | 22 +- code/modules/surgery/bodyparts/head.dm | 7 +- code/modules/surgery/organs/_organ_visible.dm | 2 +- .../surgery/organs/internal/artery/_artery.dm | 6 +- code/modules/surgery/organs/internal/heart.dm | 4 +- .../modules/surgery/organs/internal/spleen.dm | 4 +- .../surgery/organs/organ_processing/heart.dm | 29 +- .../surgery/organs/organ_processing/spleen.dm | 13 +- 82 files changed, 498 insertions(+), 324 deletions(-) diff --git a/code/__DEFINES/DNA.dm b/code/__DEFINES/DNA.dm index 5b3b084287a..179fee74522 100644 --- a/code/__DEFINES/DNA.dm +++ b/code/__DEFINES/DNA.dm @@ -57,7 +57,6 @@ #define FACEHAIR 3 #define EYECOLOR 4 #define LIPS 5 -#define NOBLOOD 6 #define NOTRANSSTING 7 #define NOZOMBIE 9 #define NO_UNDERWEAR 11 diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_living.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_living.dm index a5dc72ea0ef..cebf46c13eb 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_living.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_living.dm @@ -19,6 +19,12 @@ ///from base of mob/update_transform() #define COMSIG_LIVING_POST_UPDATE_TRANSFORM "living_post_update_transform" +/// From /mob/living/proc/update_blood_status(), sent when the return value of /mob/living/proc/can_have_blood() changes, but before the new blood status is applied : (had_blood, has_blood, old_blood_volume) +#define COMSIG_LIVING_PRE_UPDATE_BLOOD_STATUS "living_pre_update_blood_status" + +/// From /mob/living/proc/update_blood_status(), sent when the return value of /mob/living/proc/can_have_blood() changes : (had_blood, has_blood, old_blood_volume, new_blood_volume) +#define COMSIG_LIVING_UPDATE_BLOOD_STATUS "living_update_blood_status" + ///Signal sent when a keybind is deactivated #define DEACTIVATE_KEYBIND(A) "[A]_DEACTIVATED" #define COMSIG_KB_LIVING_VIEW_PET_COMMANDS "keybinding_living_view_pet_commands" diff --git a/code/__DEFINES/living.dm b/code/__DEFINES/living.dm index a89151f586c..52e55e21190 100644 --- a/code/__DEFINES/living.dm +++ b/code/__DEFINES/living.dm @@ -4,3 +4,16 @@ #define PIXEL_X_OFFSET "x" #define PIXEL_Y_OFFSET "y" #define PIXEL_Z_OFFSET "z" + +// Bleed check results +/// We cannot bleed (here, or in general) at all +#define BLEED_NONE 0 +/// We cannot make a splatter, but we can add our DNA +#define BLEED_ADD_DNA 1 +/// We can bleed just fine +#define BLEED_SPLATTER 2 + +/// Checks if the mob can have blood +#define CAN_HAVE_BLOOD(mob) (mob.living_flags & LIVING_CAN_HAVE_BLOOD) +/// Queues a blood update for the next life tick for the mob +#define QUEUE_BLOOD_UPDATE(mob) mob.living_flags |= BLOOD_UPDATE_QUEUED diff --git a/code/__DEFINES/medical.dm b/code/__DEFINES/medical.dm index db56bc9f396..d3766b1b89f 100644 --- a/code/__DEFINES/medical.dm +++ b/code/__DEFINES/medical.dm @@ -286,7 +286,6 @@ DEFINE_BITFIELD(organ_flags, list( #define GETPULSE_ADVANCED 1 // More accurate. (med scanner, sleeper, etc.) #define GETPULSE_PERFECT 2 // Perfectly accurate. (currently no non-adminbus means, you get the exact value 0-5) - // ~CPR types /// Mouth to mouth - Heals oxygen deprivation #define CPR_MOUTH "m2m" @@ -381,3 +380,5 @@ DEFINE_BITFIELD(organ_flags, list( /// How often can we annoy the player about their bleeding? This duration is extended if it's not serious bleeding #define BLEEDING_MESSAGE_BASE_CD 15 SECONDS +/// Arbitrary value for "noticeable bleeding" +#define BLEED_RATE_NOTICABLE 1.5 diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 342183b048d..c4de4bf440a 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -21,9 +21,10 @@ #define SUBMIT_INTENT 1 //Blood levels +#define BLOOD_VOLUME_MAXIMUM BLOOD_VOLUME_NORMAL * 3.5 #define BLOOD_VOLUME_MAX_LETHAL BLOOD_VOLUME_NORMAL * 3 #define BLOOD_VOLUME_EXCESS BLOOD_VOLUME_NORMAL * 2.5 -#define BLOOD_VOLUME_MAXIMUM BLOOD_VOLUME_NORMAL * 2 +#define BLOOD_VOLUME_SAFE_MAXIMUM BLOOD_VOLUME_NORMAL * 2 #define BLOOD_VOLUME_NORMAL 1200 #define BLOOD_VOLUME_SAFE BLOOD_VOLUME_NORMAL * 0.8 #define BLOOD_VOLUME_OKAY BLOOD_VOLUME_NORMAL * 0.6 @@ -383,6 +384,11 @@ #define MOVES_ON_ITS_OWN (1<<0) /// Simple mob trait, can be fireman carried #define CAN_BE_FIREMANNED (1<<1) +/// Blood volume or status has changed since the last [proc/update_blood_effects] call. +/// Nowhere near guaranteed to happen only once per life tick, or at all. +#define BLOOD_UPDATE_QUEUED (1<<4) +/// This mob can have blood, cached value of [proc/can_have_blood] +#define LIVING_CAN_HAVE_BLOOD (1<<5) // Body position defines. /// Mob is standing up, usually associated with lying_angle value of 0. diff --git a/code/__DEFINES/traits/definitions.dm b/code/__DEFINES/traits/definitions.dm index e1b72bda8c8..552ac01d0c2 100644 --- a/code/__DEFINES/traits/definitions.dm +++ b/code/__DEFINES/traits/definitions.dm @@ -153,6 +153,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_NOGUNS "no_guns" /// Doesn't use nutrition #define TRAIT_NOHUNGER "no_hunger" +/// This carbon doesn't have blood +#define TRAIT_NOBLOOD "noblood" /// Doesn't use hygine #define TRAIT_NOHYGIENE "no_hygiene" /// Can't metabolise reagents diff --git a/code/_globalvars/special_traits/traits.dm b/code/_globalvars/special_traits/traits.dm index efe4f08daa4..c266e5bec68 100644 --- a/code/_globalvars/special_traits/traits.dm +++ b/code/_globalvars/special_traits/traits.dm @@ -841,7 +841,7 @@ character.grant_undead_eyes() character.mob_biotypes |= MOB_UNDEAD - character.dna?.species?.species_traits |= NOBLOOD + ADD_TRAIT(character, TRAIT_NOBLOOD, SPECIES_TRAIT) character.dna?.species?.soundpack_m = new /datum/voicepack/skeleton() character.dna?.species?.soundpack_f = new /datum/voicepack/skeleton() diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm index 8b274173c15..54801df2c4d 100644 --- a/code/_globalvars/traits.dm +++ b/code/_globalvars/traits.dm @@ -66,6 +66,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_NOLIMBDISABLE" = TRAIT_NOLIMBDISABLE, "TRAIT_EASYLIMBDISABLE" = TRAIT_EASYLIMBDISABLE, "TRAIT_TOXINLOVER" = TRAIT_TOXINLOVER, + "TRAIT_NOBLOOD" = TRAIT_NOBLOOD, "TRAIT_NOBREATH" = TRAIT_NOBREATH, "TRAIT_HOLY" = TRAIT_HOLY, "TRAIT_NOAMBUSH" = TRAIT_NOAMBUSH, diff --git a/code/datums/ai/behaviours/recuperate.dm b/code/datums/ai/behaviours/recuperate.dm index 521664402d0..d6e10183d7c 100644 --- a/code/datums/ai/behaviours/recuperate.dm +++ b/code/datums/ai/behaviours/recuperate.dm @@ -30,8 +30,7 @@ pawn.adjustBruteLoss( (max_hp * -brute_heal) ) pawn.health = clamp(pawn.health, 0, max_hp) pawn.adjust_fire_stacks(-DEFAULT_FIRE_HEAL) - pawn.blood_volume += pawn.blood_volume + DEFAULT_BLOOD_RECOVERY - pawn.blood_volume = clamp(pawn.blood_volume, 0, BLOOD_VOLUME_NORMAL) + pawn.adjust_blood_volume(DEFAULT_BLOOD_RECOVERY, 0, BLOOD_VOLUME_NORMAL) finish_action(controller, TRUE) diff --git a/code/datums/ai/controllers/mirespider.dm b/code/datums/ai/controllers/mirespider.dm index 980090213e0..f556f5282bc 100644 --- a/code/datums/ai/controllers/mirespider.dm +++ b/code/datums/ai/controllers/mirespider.dm @@ -271,9 +271,8 @@ var/obj/effect/temp_visual/heal/H = new /obj/effect/temp_visual/heal_rogue(get_turf(owner)) H.color = "#4e4c4c00" var/list/wCount = owner.get_wounds() - if(owner.blood_volume < BLOOD_VOLUME_NORMAL) - //Keeps the user alive - owner.blood_volume = min(owner.blood_volume+blood_healing_on_tick, BLOOD_VOLUME_NORMAL) + //Keeps the user alive + owner.adjust_blood_volume(blood_healing_on_tick, maximum = BLOOD_VOLUME_NORMAL) if(wCount.len > 0) owner.heal_wounds(healing_on_tick) owner.update_damage_overlays() diff --git a/code/datums/ai/subtrees/human_basic_attack.dm b/code/datums/ai/subtrees/human_basic_attack.dm index 236497c83d8..6b63db9db4b 100644 --- a/code/datums/ai/subtrees/human_basic_attack.dm +++ b/code/datums/ai/subtrees/human_basic_attack.dm @@ -177,7 +177,7 @@ if(!(pawn.mobility_flags & MOBILITY_STAND)) pawn.aimheight_change(rand(1, 4)) return - if(HAS_TRAIT(target, TRAIT_BLOODLOSS_IMMUNE)) + if(HAS_TRAIT(target, TRAIT_BLOODLOSS_IMMUNE) || !CAN_HAVE_BLOOD(target)) pawn.aimheight_change(rand(12, 19)) return pawn.aimheight_change(pick(rand(5, 8), rand(9, 11), rand(12, 19))) diff --git a/code/datums/components/aura_healing.dm b/code/datums/components/aura_healing.dm index 68213349808..d07269838cc 100644 --- a/code/datums/components/aura_healing.dm +++ b/code/datums/components/aura_healing.dm @@ -133,8 +133,7 @@ var/mob/living/simple_animal/animal_candidate = candidate animal_candidate.adjustHealth(-simple_heal, updating_health = FALSE) - if (candidate.blood_volume < BLOOD_VOLUME_NORMAL) - candidate.blood_volume += blood_heal + candidate.adjust_blood_volume(blood_heal, maximum = BLOOD_VOLUME_NORMAL) candidate.updatehealth() diff --git a/code/datums/components/rotting.dm b/code/datums/components/rotting.dm index 587645eae66..71d364889fc 100644 --- a/code/datums/components/rotting.dm +++ b/code/datums/components/rotting.dm @@ -80,8 +80,7 @@ if(amount > 45 MINUTES) if(!is_zombie) B.skeletonize() - if(C.dna && C.dna.species) - C.dna.species.species_traits |= NOBLOOD + ADD_TRAIT(C, TRAIT_NOBLOOD, SPECIES_TRAIT) C.change_stat(STAT_CONSTITUTION, -99) shouldupdate = TRUE else diff --git a/code/datums/enchantments/bloodthirsty.dm b/code/datums/enchantments/bloodthirsty.dm index be86eb46c0f..d1c800bc429 100644 --- a/code/datums/enchantments/bloodthirsty.dm +++ b/code/datums/enchantments/bloodthirsty.dm @@ -18,7 +18,7 @@ if(enchanted_item.get_integrity() >= enchanted_item.max_integrity) return var/mob/living/carbon/carbon = enchanted_item.loc - if(NOBLOOD in carbon.dna?.species.species_traits) + if(!CAN_HAVE_BLOOD(carbon)) return var/bleeding_bite = FALSE @@ -44,7 +44,7 @@ bodypart?.bodypart_attacked_by(BCLASS_BITE, 20, modifiers = list(CRIT_MOD_CHANCE = -100)) var/missing_integrity = enchanted_item.max_integrity - enchanted_item.get_integrity() - carbon.adjust_bloodvolume(-missing_integrity * 0.5) + carbon.adjust_blood_volume(-missing_integrity * 0.5) enchanted_item.update_integrity(enchanted_item.max_integrity) playsound(enchanted_item,'sound/items/weapons/bite.ogg', 45, TRUE, -1) to_chat(carbon, span_danger("[enchanted_item] gnaws at you!")) diff --git a/code/datums/injury/_injury.dm b/code/datums/injury/_injury.dm index cae0834bb45..bc508b1eff9 100644 --- a/code/datums/injury/_injury.dm +++ b/code/datums/injury/_injury.dm @@ -1,3 +1,5 @@ +#define BLEED_DAMAGE_RATIO 10 + //This is basically the baystation wound datum, which i thought would synergize well with the TG wounds /**************************************************** INJURY DATUM @@ -13,7 +15,7 @@ var/current_stage = 0 /// Amount of damage this injury is currently causing var/damage = 0 - /// How much we bleed on each tick per 10 damage + /// How much we bleed on each tick per BLEED_DAMAGE_RATIO damage var/bleed_rate = 1 /// Ticks of bleeding left var/bleed_timer = 0 @@ -367,6 +369,8 @@ return TRUE /datum/injury/proc/is_bleeding() + if(!CAN_HAVE_BLOOD(parent_mob)) + return for(var/thing in embedded_objects) var/obj/item/item = thing if(item.w_class >= WEIGHT_CLASS_SMALL) @@ -377,21 +381,16 @@ return FALSE return ((bleed_timer > 0 || damage_per_injury() > bleed_threshold) && current_stage <= max_bleeding_stage) -/datum/injury/proc/get_bleed_rate() - if(!is_bleeding()) +/datum/injury/proc/get_bleed_rate(ignore_is_bleeding = FALSE) + if(!CAN_HAVE_BLOOD(parent_mob)) + return 0 + if(!ignore_is_bleeding && !is_bleeding()) return 0 var/bad_embeddies = 0 for(var/obj/item/item in embedded_objects) if((item.w_class < WEIGHT_CLASS_SMALL)) bad_embeddies += 1 - return max(0.1, (bleed_rate * damage)/10 + bad_embeddies) - -/datum/injury/proc/get_artifical_bleed_rate() - var/bad_embeddies = 0 - for(var/obj/item/item in embedded_objects) - if((item.w_class < WEIGHT_CLASS_SMALL)) - bad_embeddies += 1 - return max(0.1, (bleed_rate * damage)/10 + bad_embeddies) + return max(0.1, (bleed_rate * (damage/BLEED_DAMAGE_RATIO)) + bad_embeddies) /datum/injury/proc/is_surgical() if(CHECK_BITFIELD(injury_flags, INJURY_SURGICAL)) @@ -422,3 +421,5 @@ if(CHECK_BITFIELD(injury_flags, INJURY_BANDAGED)) return TRUE return FALSE + +#undef BLEED_DAMAGE_RATIO diff --git a/code/datums/injury/organic/burn.dm b/code/datums/injury/organic/burn.dm index 3781ed2c797..2a7be72b075 100644 --- a/code/datums/injury/organic/burn.dm +++ b/code/datums/injury/organic/burn.dm @@ -35,7 +35,7 @@ . = ..() //Burn damage can cause fluid loss due to blistering and cook-off if(limb.owner && (damage_per_injury() >= 5 || damage + limb.burn_dam >= 20)) - limb.owner.adjust_bloodvolume(-CEILING(BLOOD_VOLUME_SURVIVE * damage/100, 1)) + limb.owner.adjust_blood_volume(-CEILING(BLOOD_VOLUME_SURVIVE * damage/100, 1)) */ /* diff --git a/code/datums/mana/mana_pool.dm b/code/datums/mana/mana_pool.dm index 408412e5847..0095d9351f3 100644 --- a/code/datums/mana/mana_pool.dm +++ b/code/datums/mana/mana_pool.dm @@ -524,7 +524,7 @@ to_chat(parent, span_warning("I feel woozy after casting that spell.")) if(10 to 30) to_chat(parent, span_danger("I feel sharp pains coursing through my body!")) - parent:blood_volume -= backlash_intensity + parent:adjust_blood_volume(-backlash_intensity) if(30 to 50) parent.visible_message(span_danger("[parent] collapses as they vomit blood from the recoil."), span_danger("I feel my organs being ripped apart!")) parent:vomit(1, blood = TRUE, stun = FALSE) diff --git a/code/datums/mana/overload.dm b/code/datums/mana/overload.dm index 66c3af8ea47..46e8b97f862 100644 --- a/code/datums/mana/overload.dm +++ b/code/datums/mana/overload.dm @@ -17,7 +17,7 @@ if (effect_mult > MANA_OVERLOAD_DAMAGE_THRESHOLD) apply_damage(MANA_OVERLOAD_BASE_DAMAGE * adjusted_mult, damagetype = BRUTE, forced = TRUE, spread_damage = TRUE) - blood_volume = max(BLOOD_VOLUME_BAD, blood_volume - round(effect_mult * 0.25, 1)) + adjust_blood_volume(-round(effect_mult * 0.25, 1), minimum = BLOOD_VOLUME_BAD) /atom/movable/proc/stop_mana_overload() mana_overloaded = FALSE diff --git a/code/datums/quirks/vices/mental_vice.dm b/code/datums/quirks/vices/mental_vice.dm index 98be35fa8df..a0d75a71a2f 100644 --- a/code/datums/quirks/vices/mental_vice.dm +++ b/code/datums/quirks/vices/mental_vice.dm @@ -313,7 +313,9 @@ H.apply_status_effect(/datum/status_effect/debuff/addiction) var/current_pain = H.getShock() - var/bloodloss_factor = clamp(1.0 - (H.blood_volume / BLOOD_VOLUME_NORMAL), 0.0, 0.5) + var/bloodloss_factor = 1 + if(CAN_HAVE_BLOOD(H)) + bloodloss_factor = clamp(1.0 - (H.get_blood_volume() / BLOOD_VOLUME_NORMAL), 0.0, 0.5) var/new_pain_threshold = get_pain_threshold(current_pain * (1.0 + (bloodloss_factor * 1.4)) * clamp(2 - (GET_MOB_ATTRIBUTE_VALUE(H, STAT_ENDURANCE) / 10), 0.5, 1.5)) if(last_pain_threshold == NONE) diff --git a/code/datums/status_effects/rogue/roguebuff.dm b/code/datums/status_effects/rogue/roguebuff.dm index 1c5cffe4aff..7e56b708531 100644 --- a/code/datums/status_effects/rogue/roguebuff.dm +++ b/code/datums/status_effects/rogue/roguebuff.dm @@ -581,8 +581,7 @@ return "SUBJECTPRONOUN is bathed in a restorative aura!" /datum/status_effect/buff/matthioshealing/tick() - if(owner.blood_volume < BLOOD_VOLUME_NORMAL) - owner.blood_volume = min(owner.blood_volume+10, BLOOD_VOLUME_NORMAL) + owner.adjust_blood_volume(10, maximum = BLOOD_VOLUME_NORMAL) if(owner.get_wounds()) owner.heal_wounds(healing_on_tick) owner.update_damage_overlays() diff --git a/code/datums/wounds/black_briar_curse.dm b/code/datums/wounds/black_briar_curse.dm index f03d163c6d7..e3448fdbab6 100644 --- a/code/datums/wounds/black_briar_curse.dm +++ b/code/datums/wounds/black_briar_curse.dm @@ -87,7 +87,7 @@ if(!. || !iscarbon(affected)) return FALSE var/mob/living/carbon/C = affected - if(NOBLOOD in C.dna?.species?.species_traits) + if(!CAN_HAVE_BLOOD(C)) return FALSE if(is_species(C, /datum/species/werewolf) || C.mind?.has_antag_datum(/datum/antagonist/werewolf)) // Dendor protects return FALSE diff --git a/code/game/atom/atoms.dm b/code/game/atom/atoms.dm index 3e13f4542e6..280080c5157 100644 --- a/code/game/atom/atoms.dm +++ b/code/game/atom/atoms.dm @@ -703,7 +703,7 @@ /mob/living/carbon/get_blood_dna_list() if(isnull(dna)) // Xenos return ..() - if(NOBLOOD in dna.species.species_traits) //no skeletons bleeding + if(!CAN_HAVE_BLOOD(src)) //no skeletons bleeding return null var/datum/blood_type/blood = get_blood_type() return list("[dna.unique_enzymes]" = blood.type) diff --git a/code/game/objects/items/inquisition_relics.dm b/code/game/objects/items/inquisition_relics.dm index 408b4005d32..ed9a00d1ced 100644 --- a/code/game/objects/items/inquisition_relics.dm +++ b/code/game/objects/items/inquisition_relics.dm @@ -582,8 +582,7 @@ visible_message(span_warning("[src] draws from [M]!")) playsound(M, 'sound/combat/hits/bladed/genstab (1).ogg', 30, FALSE, -1) timestaken++ - M.adjust_bloodvolume(-30) - M.handle_blood() + M.adjust_blood_volume(-30) if(M.mind) if(M.mind.has_antag_datum(/datum/antagonist/werewolf, FALSE)) cursedblood = 3 @@ -609,14 +608,9 @@ if(!active) to_chat(user, span_warning("It's not primed.")) return - if(HAS_TRAIT(M, TRAIT_BLOODLOSS_IMMUNE)) + if(!CAN_HAVE_BLOOD(M) || !M.get_blood_volume()) to_chat(user, span_warning("They don't have any blood to sample.")) return - if(iscarbon(M)) - var/mob/living/carbon/C = M - if(NOBLOOD in C.dna.species.species_traits) - to_chat(user, span_warning("They don't have any blood to sample.")) - return if(full) to_chat(user, span_warning("It's full.")) return @@ -1329,7 +1323,6 @@ attacked.flash_fullscreen("redflash3") attacked.adjustBruteLoss(40, damage_type = BCLASS_PIERCE) attacked.adjust_bloodpool(-240) - attacked.handle_blood() feeder = WEAKREF(attacked) openstate = "bloody" fedblood = TRUE diff --git a/code/game/objects/items/mageitems.dm b/code/game/objects/items/mageitems.dm index 55223f8a0e5..024e8371c53 100644 --- a/code/game/objects/items/mageitems.dm +++ b/code/game/objects/items/mageitems.dm @@ -193,7 +193,7 @@ playsound(src, get_sfx("genslash"), 100, TRUE) user.visible_message(span_warning("[user] cuts open [user.p_their()] palm!"), \ span_cult("I slice open my palm!")) - if(user.blood_volume) + if(user.get_blood_volume()) user.apply_damage(pickrune.scribe_damage, BRUTE, pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM), damage_type = BCLASS_CUT) is_bled = TRUE var/crafttime = (10 SECONDS - ((GET_MOB_SKILL_VALUE_OLD(user, /datum/attribute/skill/magic/arcane)) * 5)) diff --git a/code/game/objects/items/natural/cloth.dm b/code/game/objects/items/natural/cloth.dm index 0f7a27a3ba2..f88d8fbab2b 100644 --- a/code/game/objects/items/natural/cloth.dm +++ b/code/game/objects/items/natural/cloth.dm @@ -23,7 +23,7 @@ var/volume = 9 // Effectiveness when used as a bandage, how much it'll lower the bloodloss, bloodloss will get multiplied by this. - var/bandage_effectiveness = 0.5 + var/bandage_effectiveness = 0 // EXPERIMENTAL CHANGE: BANDAGES STOP ALL BLEEDING ///how long it will take to bandage something with this var/bandage_speed = 7 SECONDS ///How much you can bleed into the bandage until it needs to be changed (Blood loss is measured in 50% of the health) diff --git a/code/game/turfs/open/water.dm b/code/game/turfs/open/water.dm index ef72f3cb5ef..45df030a47d 100644 --- a/code/game/turfs/open/water.dm +++ b/code/game/turfs/open/water.dm @@ -695,7 +695,7 @@ return if(iscarbon(arrived)) var/mob/living/carbon/C = arrived - if(C.blood_volume <= 0) + if(!C.get_blood_volume()) return var/list/zonee = list(BODY_ZONE_R_LEG,BODY_ZONE_L_LEG) for(var/i = 1, i <= zonee.len, i++) @@ -745,7 +745,7 @@ return if(iscarbon(arrived)) var/mob/living/carbon/C = arrived - if(C.blood_volume <= 0) + if(!C.get_blood_volume()) return var/list/zonee = list(BODY_ZONE_R_LEG,BODY_ZONE_L_LEG) for(var/i = 1, i <= zonee.len, i++) diff --git a/code/modules/antagonists/villain/lich/lich.dm b/code/modules/antagonists/villain/lich/lich.dm index f52f8400fca..e553fc74e12 100644 --- a/code/modules/antagonists/villain/lich/lich.dm +++ b/code/modules/antagonists/villain/lich/lich.dm @@ -128,7 +128,7 @@ if(length(L.quirks)) L.clear_quirks() L.mob_biotypes |= MOB_UNDEAD - L.dna.species.species_traits |= NOBLOOD + ADD_TRAIT(L, TRAIT_NOBLOOD, SPECIES_TRAIT) L.grant_undead_eyes() L.skeletonize(FALSE) L.equipOutfit(/datum/outfit/lich) diff --git a/code/modules/antagonists/villain/lich/spells/raise_undead.dm b/code/modules/antagonists/villain/lich/spells/raise_undead.dm index e6b6e9b4514..543626caa53 100644 --- a/code/modules/antagonists/villain/lich/spells/raise_undead.dm +++ b/code/modules/antagonists/villain/lich/spells/raise_undead.dm @@ -83,7 +83,7 @@ mind.current.job = null mind.add_antag_datum(/datum/antagonist/skeleton) - dna.species.species_traits |= NOBLOOD + ADD_TRAIT(src, TRAIT_NOBLOOD, SPECIES_TRAIT) dna.species.soundpack_m = new /datum/voicepack/skeleton() dna.species.soundpack_f = new /datum/voicepack/skeleton() diff --git a/code/modules/antagonists/villain/neu_vampires/bloodsuck.dm b/code/modules/antagonists/villain/neu_vampires/bloodsuck.dm index c3abb49a20a..e27f20c7e17 100644 --- a/code/modules/antagonists/villain/neu_vampires/bloodsuck.dm +++ b/code/modules/antagonists/villain/neu_vampires/bloodsuck.dm @@ -16,7 +16,7 @@ return 0 if(!force && !COOLDOWN_FINISHED(src, drinkblood_use)) return 0 - if(HAS_TRAIT(victim, TRAIT_HUSK) || (NOBLOOD in victim.dna?.species?.species_traits) || victim.blood_volume <= 0) + if(HAS_TRAIT(victim, TRAIT_HUSK) || !CAN_HAVE_BLOOD(victim) || !victim.get_blood_volume()) to_chat(src, span_warning("Sigh. No blood.")) return 0 var/datum/antagonist/vampire/VDrinker = mind?.has_antag_datum(/datum/antagonist/vampire) @@ -52,7 +52,7 @@ to_chat(src, span_userdanger("YOU TRY TO COMMIT DIABLERIE ON [victim].")) used_vitae = min(250, victim.bloodpool) - drink_amt = min(victim.blood_volume, drink_amt) + drink_amt = min(victim.get_blood_volume(), drink_amt) if(ingest) drink_amt = victim.transfer_blood_impurities(reagents, drink_amt, 1.5, src) if(used_vitae > 0) @@ -63,7 +63,7 @@ clan?.handle_bloodsuck(src, blood_data?["preferences"]) adjust_bloodpool(used_vitae) victim.adjust_bloodpool(-used_vitae) - victim.adjust_bloodvolume(-drink_amt) + victim.adjust_blood_volume(-drink_amt) playsound(src, 'sound/misc/drink_blood.ogg', 100, FALSE, -4) diff --git a/code/modules/antagonists/villain/neu_vampires/covens/coven_powers/bloodheal.dm b/code/modules/antagonists/villain/neu_vampires/covens/coven_powers/bloodheal.dm index f1422580f0d..23a3b66b6b4 100644 --- a/code/modules/antagonists/villain/neu_vampires/covens/coven_powers/bloodheal.dm +++ b/code/modules/antagonists/villain/neu_vampires/covens/coven_powers/bloodheal.dm @@ -62,8 +62,7 @@ else bashing_lethal_heal = injury.heal_damage(bashing_lethal_heal) - if(owner.blood_volume <= BLOOD_VOLUME_NORMAL) - owner.adjust_bloodvolume(vitae_cost) + owner.adjust_blood_volume(vitae_cost, maximum = BLOOD_VOLUME_NORMAL) //this is quadratic so expect it to scale like crazy owner.heal_wounds((bashing_lethal_heal + aggravated_heal) * level * 0.6, source=src) diff --git a/code/modules/antagonists/villain/neu_vampires/frenzy.dm b/code/modules/antagonists/villain/neu_vampires/frenzy.dm index d81a48f1c58..6c72a255a05 100644 --- a/code/modules/antagonists/villain/neu_vampires/frenzy.dm +++ b/code/modules/antagonists/villain/neu_vampires/frenzy.dm @@ -156,7 +156,7 @@ B = mouth else B.bitelimb(src) - if(C.blood_volume <= 0 || HAS_TRAIT(C, TRAIT_HUSK) || (NOBLOOD in C.dna?.species?.species_traits)) + if(HAS_TRAIT(C, TRAIT_HUSK) || !CAN_HAVE_BLOOD(C) || !C.get_blood_volume()) return B.drinklimb(src) diff --git a/code/modules/antagonists/villain/neu_vampires/living_modifications.dm b/code/modules/antagonists/villain/neu_vampires/living_modifications.dm index 1f9561d18f1..68f6d135e82 100644 --- a/code/modules/antagonists/villain/neu_vampires/living_modifications.dm +++ b/code/modules/antagonists/villain/neu_vampires/living_modifications.dm @@ -333,10 +333,10 @@ artery.applyOrganDamage(-5) if(prob(3)) regenerate_limb(silent=FALSE) - if(blood_volume <= BLOOD_VOLUME_NORMAL) - if(blood_volume < BLOOD_VOLUME_SAFE) - blood_volume = BLOOD_VOLUME_SAFE - adjust_bloodvolume(10) + if(get_blood_volume() <= BLOOD_VOLUME_NORMAL) + if(get_blood_volume() < BLOOD_VOLUME_SAFE) + set_blood_volume(BLOOD_VOLUME_SAFE) + adjust_blood_volume(10) set_bloodpool(max(bloodpool, min(maxbloodpool * 0.25, bloodpool + 5))) else if(HAS_TRAIT(src, TRAIT_DEATHCOMA) && (!InCritical() || (!istype(coffin) || !(src in coffin.contents)))) REMOVE_TRAIT(src, TRAIT_DEATHCOMA, VAMPIRE_TRAIT) diff --git a/code/modules/antagonists/villain/neu_vampires/use_blood.dm b/code/modules/antagonists/villain/neu_vampires/use_blood.dm index 4ac7ffa1fab..a9070426e94 100644 --- a/code/modules/antagonists/villain/neu_vampires/use_blood.dm +++ b/code/modules/antagonists/villain/neu_vampires/use_blood.dm @@ -140,12 +140,12 @@ data[BLOODCOST_TOTAL] += data[BLOODCOST_AMOUNT_GRAB] if (ishuman(data[BLOODCOST_TARGET_GRAB])) var/mob/living/carbon/human/H = data[BLOODCOST_TARGET_GRAB] - H.adjust_bloodvolume(-data[BLOODCOST_AMOUNT_GRAB]) + H.adjust_blood_volume(-data[BLOODCOST_AMOUNT_GRAB]) H.adjust_bloodpool(-data[BLOODCOST_AMOUNT_GRAB]) if (data[BLOODCOST_TARGET_BLEEDER]) data[BLOODCOST_TOTAL] += data[BLOODCOST_AMOUNT_BLEEDER] var/mob/living/carbon/human/H = data[BLOODCOST_TARGET_BLEEDER] - H.adjust_bloodvolume(-data[BLOODCOST_AMOUNT_BLEEDER]) + H.adjust_blood_volume(-data[BLOODCOST_AMOUNT_BLEEDER]) H.adjust_bloodpool(-data[BLOODCOST_AMOUNT_BLEEDER]) if (data[BLOODCOST_TARGET_HELD]) data[BLOODCOST_TOTAL] += data[BLOODCOST_AMOUNT_HELD] @@ -163,18 +163,19 @@ data[BLOODCOST_TOTAL] += data[BLOODCOST_AMOUNT_USER] if (ishuman(user)) var/mob/living/carbon/human/H = user - var/blood_before = H.blood_volume - H.adjust_bloodvolume(-data[BLOODCOST_AMOUNT_USER]) + var/blood_before = H.get_blood_volume() + H.adjust_blood_volume(-data[BLOODCOST_AMOUNT_USER]) H.adjust_bloodpool(-data[BLOODCOST_AMOUNT_USER]) - var/blood_after = H.blood_volume - if (blood_before > BLOOD_VOLUME_SAFE && blood_after < BLOOD_VOLUME_SAFE) - to_chat(user, span_cult("You start looking pale.") ) - else if (blood_before > BLOOD_VOLUME_OKAY && blood_after < BLOOD_VOLUME_OKAY) - to_chat(user, span_cult("You are about to pass out from the lack of blood.") ) - else if (blood_before > BLOOD_VOLUME_BAD && blood_after < BLOOD_VOLUME_BAD) - to_chat(user, span_cult("You have trouble focusing, things will go bad if you keep using your blood.") ) - else if (blood_before > BLOOD_VOLUME_SURVIVE && blood_after < BLOOD_VOLUME_SURVIVE) - to_chat(user, span_cult("It will be all over soon.") ) + var/blood_after = H.get_blood_volume() + if(CAN_HAVE_BLOOD(H)) + if (blood_before > BLOOD_VOLUME_SAFE && blood_after < BLOOD_VOLUME_SAFE) + to_chat(user, span_cult("You start looking pale.") ) + else if (blood_before > BLOOD_VOLUME_OKAY && blood_after < BLOOD_VOLUME_OKAY) + to_chat(user, span_cult("You are about to pass out from the lack of blood.") ) + else if (blood_before > BLOOD_VOLUME_BAD && blood_after < BLOOD_VOLUME_BAD) + to_chat(user, span_cult("You have trouble focusing, things will go bad if you keep using your blood.") ) + else if (blood_before > BLOOD_VOLUME_SURVIVE && blood_after < BLOOD_VOLUME_SURVIVE) + to_chat(user, span_cult("It will be all over soon.") ) else if (ismonkey(user)) var/mob/living/carbon/C = user var/blood_before = C.health @@ -251,9 +252,8 @@ if (user.pulling) if(ishuman(user.pulling)) var/mob/living/carbon/human/H = user.pulling - if(!(NOBLOOD in H.dna.species.species_traits)) - var/blood_volume = H.blood_volume - var/blood_gathered = min(amount_needed-amount_gathered, blood_volume) + if(CAN_HAVE_BLOOD(H)) + var/blood_gathered = min(amount_needed-amount_gathered, H.get_blood_volume()) data[BLOODCOST_TARGET_GRAB] = H data[BLOODCOST_AMOUNT_GRAB] = blood_gathered amount_gathered += blood_gathered @@ -264,12 +264,11 @@ //Is there a bleeding mob/corpse on the turf that still has blood in it? for (var/mob/living/carbon/human/H in T) - if((NOBLOOD in H.dna.species.species_traits)) + if(!CAN_HAVE_BLOOD(H)) continue if(user != H) if(H.get_bleed_rate() > 0) - var/blood_volume = H.blood_volume - var/blood_gathered = min(amount_needed-amount_gathered, blood_volume) + var/blood_gathered = min(amount_needed-amount_gathered, H.get_blood_volume()) data[BLOODCOST_TARGET_BLEEDER] = H data[BLOODCOST_AMOUNT_BLEEDER] = blood_gathered amount_gathered += blood_gathered @@ -319,9 +318,8 @@ return data //Does the user have blood? (the user can pay in blood without having to bleed first) - if(istype(H_user)) - var/blood_volume = H_user.blood_volume - var/blood_gathered = min(amount_needed-amount_gathered, blood_volume) + if(CAN_HAVE_BLOOD(H_user) && istype(H_user)) + var/blood_gathered = min(amount_needed-amount_gathered, H_user.get_blood_volume()) data[BLOODCOST_TARGET_USER] = H_user data[BLOODCOST_AMOUNT_USER] = blood_gathered amount_gathered += blood_gathered diff --git a/code/modules/antagonists/villain/overlord/_antagonist.dm b/code/modules/antagonists/villain/overlord/_antagonist.dm index 1ab98d404bf..578faa614fb 100644 --- a/code/modules/antagonists/villain/overlord/_antagonist.dm +++ b/code/modules/antagonists/villain/overlord/_antagonist.dm @@ -116,7 +116,7 @@ if(length(L.quirks)) L.clear_quirks() L.mob_biotypes |= MOB_UNDEAD - L.dna.species.species_traits |= NOBLOOD + ADD_TRAIT(L, TRAIT_NOBLOOD, SPECIES_TRAIT) L.grant_undead_eyes() L.skeletonize(FALSE) L.equipOutfit(/datum/outfit/overlord) diff --git a/code/modules/antagonists/villain/vampire/_vampire.dm b/code/modules/antagonists/villain/vampire/_vampire.dm index 10c2bfe5cf8..00161ab8429 100644 --- a/code/modules/antagonists/villain/vampire/_vampire.dm +++ b/code/modules/antagonists/villain/vampire/_vampire.dm @@ -36,13 +36,14 @@ GLOBAL_LIST_EMPTY(vampire_objects) . = ..() if(!istype(examined)) return - if(NOBLOOD in examined.dna?.species?.species_traits) + if(!CAN_HAVE_BLOOD(examined)) return + var/cached_blood_volume = examined.get_blood_volume() var/vitae = 0 var/datum/blood_type/BT = examined.get_blood_type() if(istype(BT) && BT.vitae) - vitae = round(examined.blood_volume * BT.vitae) - LAZYADDASSOCLIST(examine_contents, EXAMINE_SECT_PREGEAR, span_bloody("Blood Volume: [round(examined.blood_volume)] ([vitae] VT)")) + vitae = round(cached_blood_volume * BT.vitae) + LAZYADDASSOCLIST(examine_contents, EXAMINE_SECT_PREGEAR, span_bloody("Blood Volume: [round(cached_blood_volume)] ([vitae] VT)")) /datum/antagonist/vampire/outcast name = "Outcast Vampire" diff --git a/code/modules/antagonists/villain/vampire/actions.dm b/code/modules/antagonists/villain/vampire/actions.dm index 40a5e561022..9333b68418f 100644 --- a/code/modules/antagonists/villain/vampire/actions.dm +++ b/code/modules/antagonists/villain/vampire/actions.dm @@ -29,7 +29,10 @@ if(victim_brain.brain_death) to_chat(src, span_warning("[victim.p_their(TRUE)] brain is too damaged.")) return - if(victim.blood_volume > BLOOD_VOLUME_BAD) + if(!CAN_HAVE_BLOOD(victim)) + to_chat(src, span_warning("[victim.p_their(TRUE)] does not have blood.")) + return + if(victim.get_blood_volume() > BLOOD_VOLUME_BAD) to_chat(src, span_warning("[victim.p_their(TRUE)] blood is not thin enough to be sired.")) return var/datum/antagonist/zombie/Z = victim.mind.has_antag_datum(/datum/antagonist/zombie) diff --git a/code/modules/antagonists/villain/werewolf/werewolf_transformation.dm b/code/modules/antagonists/villain/werewolf/werewolf_transformation.dm index 39f52b58e90..f9e05f4d9f4 100644 --- a/code/modules/antagonists/villain/werewolf/werewolf_transformation.dm +++ b/code/modules/antagonists/villain/werewolf/werewolf_transformation.dm @@ -15,7 +15,7 @@ if(transformed && !HAS_TRAIT(human_user, TRAIT_PARALYSIS) && !human_user.has_status_effect(/datum/status_effect/debuff/silver_bane)) if(human_user.rage_datum.check_rage(WW_RAGE_MEDIUM)) - if(human_user.blood_volume > BLOOD_VOLUME_SURVIVE) + if(!CAN_HAVE_BLOOD(human_user) || human_user.get_blood_volume() > BLOOD_VOLUME_SURVIVE) for(var/datum/wound/wound as anything in human_user.get_wounds()) wound.heal_wound(1.2) @@ -83,7 +83,7 @@ new_werewolf.adjustToxLoss(human_user.getToxLoss() / 2) new_werewolf.adjustOxyLoss(human_user.getOxyLoss() / 2) new_werewolf.adjustCloneLoss(human_user.getCloneLoss() / 2) - new_werewolf.blood_volume = human_user.blood_volume + new_werewolf.set_blood_volume(human_user.blood_volume, minimum = BLOOD_VOLUME_SURVIVE) human_user.fully_heal(HEAL_BLOOD|HEAL_WOUNDS|HEAL_RESTRAINTS) playsound(new_werewolf, pick('sound/combat/gib (1).ogg','sound/combat/gib (2).ogg'), 200, FALSE, 3) diff --git a/code/modules/antagonists/villain/zizo_cult/rituals.dm b/code/modules/antagonists/villain/zizo_cult/rituals.dm index 2eaa1771e07..d23bf5eb256 100644 --- a/code/modules/antagonists/villain/zizo_cult/rituals.dm +++ b/code/modules/antagonists/villain/zizo_cult/rituals.dm @@ -131,8 +131,8 @@ GLOBAL_LIST_INIT(ritualslist, build_zizo_rituals()) /obj/item/corruptedheart/attack(mob/living/target, mob/living/user, list/modifiers) if(!istype(user.patron, /datum/patron/inhumen/zizo)) return - if(istype(target.patron, /datum/patron/inhumen/zizo)) - target.blood_volume = BLOOD_VOLUME_NORMAL + if(istype(target.patron, /datum/patron/inhumen/zizo) && CAN_HAVE_BLOOD(target) && target.get_blood_volume() < BLOOD_VOLUME_NORMAL) + target.set_blood_volume(BLOOD_VOLUME_NORMAL) to_chat(target, span_notice("My elixir of life is stagnant once again.")) qdel(src) return diff --git a/code/modules/antagonists/villain/zomble.dm b/code/modules/antagonists/villain/zomble.dm index 9c5a4ea367d..71225614650 100644 --- a/code/modules/antagonists/villain/zomble.dm +++ b/code/modules/antagonists/villain/zomble.dm @@ -272,7 +272,7 @@ return record_round_statistic(STATS_DEADITES_WOKEN_UP) - zombie.blood_volume = BLOOD_VOLUME_NORMAL + zombie.set_blood_volume(BLOOD_VOLUME_NORMAL) zombie.setOxyLoss(0, updating_health = FALSE, forced = TRUE) //zombles dont breathe zombie.setToxLoss(0, updating_health = FALSE, forced = TRUE) //zombles are immune to poison if(!infected_wake) //if we died, heal all of this too @@ -332,7 +332,7 @@ if(stat >= DEAD) //do shit the natural way i guess return to_chat(src, "I feel horrible... REALLY horrible after that...") - if(blood_volume) + if(get_blood_volume()) MOBTIMER_SET(src, MT_PUKE) vomit(1, blood = TRUE, stun = FALSE) addtimer(CALLBACK(src, PROC_REF(wake_zombie)), 1 MINUTES) diff --git a/code/modules/fishing/leeches.dm b/code/modules/fishing/leeches.dm index d831a00d5ce..c952c87c755 100644 --- a/code/modules/fishing/leeches.dm +++ b/code/modules/fishing/leeches.dm @@ -104,11 +104,14 @@ bodypart.remove_embedded_object(src) return TRUE + if(!CAN_HAVE_BLOOD(user)) + return TRUE + if(giving) - var/blood_given = min(BLOOD_VOLUME_NORMAL - user.blood_volume, blood_storage, blood_sucking) - user.adjust_bloodvolume(blood_given) + var/blood_given = min(BLOOD_VOLUME_NORMAL - user.get_blood_volume(), blood_storage, blood_sucking) + user.adjust_blood_volume(blood_given) blood_storage = max(blood_storage - blood_given, 0) - if((blood_storage <= 0) || (user.blood_volume >= BLOOD_VOLUME_MAXIMUM)) + if((blood_storage <= 0) || (user.get_blood_volume() >= BLOOD_VOLUME_SAFE_MAXIMUM)) if(bodypart) bodypart.remove_embedded_object(src) else @@ -117,12 +120,12 @@ else var/modifier = bodypart.get_cut() ? 1.5 : 1 user.adjustToxLoss(-1 * toxin_healing * modifier) - var/blood_extracted = min(blood_maximum - blood_storage, user.blood_volume, blood_sucking) * modifier + var/blood_extracted = min(blood_maximum - blood_storage, user.get_blood_volume(), blood_sucking) * modifier if(HAS_TRAIT(user, TRAIT_LEECHIMMUNE)) blood_extracted *= 0.05 // 95% drain reduction - user.adjust_bloodvolume(-blood_extracted) + user.adjust_blood_volume(-blood_extracted) blood_storage += blood_extracted - if((blood_storage >= blood_maximum) || (user.blood_volume <= 0)) + if((blood_storage >= blood_maximum) || (user.get_blood_volume() <= 0)) if(bodypart) bodypart.remove_embedded_object(src) else diff --git a/code/modules/jobs/job_types/other/skeleton.dm b/code/modules/jobs/job_types/other/skeleton.dm index e64a49c4515..32fa50746a5 100644 --- a/code/modules/jobs/job_types/other/skeleton.dm +++ b/code/modules/jobs/job_types/other/skeleton.dm @@ -45,8 +45,8 @@ spawned.mind.special_role = "Skeleton" spawned.mind?.current.job = null + ADD_TRAIT(spawned, TRAIT_NOBLOOD, SPECIES_TRAIT) if(spawned.dna && spawned.dna.species) - spawned.dna.species.species_traits |= NOBLOOD spawned.dna.species.soundpack_m = new /datum/voicepack/skeleton() spawned.dna.species.soundpack_f = new /datum/voicepack/skeleton() diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm index 4cd9b92e29b..80052d5b4e9 100644 --- a/code/modules/mob/living/blood.dm +++ b/code/modules/mob/living/blood.dm @@ -2,40 +2,32 @@ BLOOD SYSTEM ****************************************************/ -/mob/living/proc/suppress_bloodloss(amount) - if(bleedsuppress) - return - else - bleedsuppress = TRUE - addtimer(CALLBACK(src, PROC_REF(resume_bleeding)), amount) - -/mob/living/proc/resume_bleeding() - bleedsuppress = 0 - if(stat != DEAD && bleed_rate) - to_chat(src, span_warning("The blood soaks through my bandage.")) - /mob/living/carbon/monkey/handle_blood() - if(HAS_TRAIT(src, TRAIT_HUSK)) //cryosleep or husked people do not pump the blood. + if(!CAN_HAVE_BLOOD(src)) //cryosleep or husked people do not pump the blood. + adjustOxyLoss(round((BLOOD_VOLUME_NORMAL - get_blood_volume()) * 0.02, 1)) return //Blood regeneration if there is some space - if(blood_volume < BLOOD_VOLUME_NORMAL && !bleed_rate) - blood_volume += 0.1 // regenerate blood VERY slowly - if((blood_volume < BLOOD_VOLUME_OKAY) && !HAS_TRAIT(src, TRAIT_BLOODLOSS_IMMUNE)) - adjustOxyLoss(round((BLOOD_VOLUME_NORMAL - blood_volume) * 0.02, 1)) + if(get_blood_volume() < BLOOD_VOLUME_NORMAL && !bleed_rate) + adjust_blood_volume(0.1, maximum = BLOOD_VOLUME_SAFE_MAXIMUM) // regenerate blood VERY slowly + var/cached_blood_volume = get_blood_volume() + if((cached_blood_volume < BLOOD_VOLUME_OKAY) && !HAS_TRAIT(src, TRAIT_BLOODLOSS_IMMUNE)) + adjustOxyLoss(round((BLOOD_VOLUME_NORMAL - cached_blood_volume) * 0.02, 1)) /mob/living/proc/handle_blood() - if(HAS_TRAIT(src, TRAIT_HUSK)) //cryosleep or husked people do not pump the blood. + if(!CAN_HAVE_BLOOD(src)) //cryosleep or husked people do not pump the blood. + bleed_rate = 0 + adjustOxyLoss(1.6) return - blood_volume = min(blood_volume, BLOOD_VOLUME_MAX_LETHAL) bleed_rate = min(get_bleed_rate(), 10) + var/cached_blood_volume = get_blood_volume() - if(blood_volume < BLOOD_VOLUME_NORMAL && blood_volume && !bleed_rate) - blood_volume = min(blood_volume+0.5, BLOOD_VOLUME_MAX_LETHAL) + if(cached_blood_volume < BLOOD_VOLUME_NORMAL && blood_volume && !bleed_rate) + adjust_blood_volume(0.5, maximum = BLOOD_VOLUME_SAFE_MAXIMUM) //Effects of bloodloss if(!HAS_TRAIT(src, TRAIT_BLOODLOSS_IMMUNE) && stat != DEAD) - switch(blood_volume) + switch(cached_blood_volume) if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE) if(prob(3)) to_chat(src, span_warning("I feel dizzy.")) @@ -59,9 +51,9 @@ remove_status_effect(/datum/status_effect/debuff/bleedingworse) remove_status_effect(/datum/status_effect/debuff/bleeding) apply_status_effect(/datum/status_effect/debuff/bleedingworst) - if(blood_volume <= BLOOD_VOLUME_BAD) + if(cached_blood_volume <= BLOOD_VOLUME_BAD) adjustOxyLoss(2) - if(blood_volume <= BLOOD_VOLUME_SURVIVE) + if(cached_blood_volume <= BLOOD_VOLUME_SURVIVE) adjustOxyLoss(4) else remove_status_effect(/datum/status_effect/debuff/bleeding) @@ -71,14 +63,16 @@ if(bleed_rate) bleed(bleed_rate) - //if(blood_volume in -INFINITY to BLOOD_VOLUME_SURVIVE) - //adjustOxyLoss(1.6) + if(cached_blood_volume in -INFINITY to BLOOD_VOLUME_SURVIVE) + adjustOxyLoss(1.6) // Takes care blood loss and regeneration /mob/living/carbon/handle_blood() return // we handle this in our organs now /mob/living/proc/get_bleed_rate() + if(!CAN_HAVE_BLOOD(src)) + return 0 var/bleed_rate = 0 for(var/datum/wound/wound as anything in get_wounds()) bleed_rate += wound.bleed_rate @@ -87,63 +81,83 @@ return bleed_rate /mob/living/carbon/get_bleed_rate() - if(NOBLOOD in dna?.species?.species_traits) + if(!CAN_HAVE_BLOOD(src)) return 0 var/bleed_rate = 0 for(var/obj/item/bodypart/bodypart as anything in bodyparts) bleed_rate += bodypart.get_bleed_rate() return bleed_rate +/// Check if a mob can bleed, and possibly if they're capable of leaving decals on turfs/mobs/items +/mob/living/proc/can_bleed(bleed_flag = NONE) + if (!CAN_HAVE_BLOOD(src)) + return BLEED_NONE + + if(HAS_TRAIT(src, TRAIT_SIMPLE_WOUNDS)) + return BLEED_NONE + + if(!iscarbon(src)) + return BLEED_NONE + + if (!bleed_flag) + return BLEED_SPLATTER + + var/datum/blood_type/blood_type = get_blood_type() + if (!blood_type) + return BLEED_NONE + + // if (blood_type.blood_flags & bleed_flag) + // return BLEED_SPLATTER + + // if (blood_type.blood_flags & BLOOD_ADD_DNA) + // return BLEED_ADD_DNA + + return BLEED_NONE + + /// How much slower we'll be bleeding for every CON point. 0.1 = 10% slower. #define CONSTITUTION_BLEEDRATE_MOD 0.03 +#define BLOOD_DRIP_RATE_MOD 90 //Greater number means creating blood drips more often while bleeding /// Makes a blood drop, leaking amt units of blood from the mob -/mob/living/proc/bleed(amt) - if(!iscarbon(src) && !HAS_TRAIT(src, TRAIT_SIMPLE_WOUNDS)) +/mob/living/proc/bleed(amount) + if((status_flags & GODMODE) || !can_bleed()) return - if(blood_volume <= 0) + if(!get_blood_volume()) return // For each CON above 10, we bleed slower. // Consequently, for each CON under 10 we bleed faster. - var/con_modifier = 1 - var/our_con = GET_MOB_ATTRIBUTE_VALUE(src, STAT_CONSTITUTION) - if(our_con != 10) - con_modifier = our_con - 10 - - amt -= amt * con_modifier * CONSTITUTION_BLEEDRATE_MOD + var/con_modifier = clamp(1 - ((GET_MOB_ATTRIBUTE_VALUE(src, STAT_CONSTITUTION) - 10) * CONSTITUTION_BLEEDRATE_MOD), 0.1, 2) + amount *= con_modifier - blood_volume = max(blood_volume - amt, 0) + var/amount_bled = -adjust_blood_volume(-amount) if(client) record_featured_stat(FEATURED_STATS_BLEEDERS, src) - record_round_statistic(STATS_BLOOD_SPILT, amt / 100) + record_round_statistic(STATS_BLOOD_SPILT, amount_bled / 100) - if(amt > 1) - if(isturf(loc)) // Blood loss still happens in locker, floor stays clean - add_drip_floor(get_turf(src), amt) + if(isturf(loc) && prob(sqrt(amount_bled) * BLOOD_DRIP_RATE_MOD)) + add_drip_floor(get_turf(src), amount_bled) - if(body_position != LYING_DOWN && !stat) - playsound(src, pick('sound/misc/bleed (1).ogg', 'sound/misc/bleed (2).ogg', 'sound/misc/bleed (3).ogg'), 100, FALSE) + if(body_position != LYING_DOWN && stat == CONSCIOUS) + playsound(src, pick('sound/misc/bleed (1).ogg', 'sound/misc/bleed (2).ogg', 'sound/misc/bleed (3).ogg'), 100, FALSE) updatehealth() - return TRUE #undef CONSTITUTION_BLEEDRATE_MOD +#undef BLOOD_DRIP_RATE_MOD /mob/living/carbon/human/bleed(amt) - if(NOBLOOD in dna?.species?.species_traits) - return FALSE - if(physiology) - amt *= physiology.bleed_mod + amt *= physiology?.bleed_mod return ..() /mob/living/proc/restore_blood() - blood_volume = initial(blood_volume) + set_blood_volume(default_blood_volume) /mob/living/carbon/human/restore_blood() - blood_volume = BLOOD_VOLUME_NORMAL + . = ..() bleed_rate = 0 /**************************************************** @@ -151,35 +165,44 @@ ****************************************************/ //Gets blood from mob to a container or other mob, preserving all data in it. -/mob/living/proc/transfer_blood_to(atom/movable/AM, amount, forced) - var/datum/blood_type/blood = get_blood_type() - if(isnull(blood) || !AM.reagents) +/mob/living/proc/transfer_blood_to(atom/movable/receiver, amount, ignore_low_blood) + var/cached_blood_volume = get_blood_volume() + + if(!cached_blood_volume || !receiver.reagents || amount <= 0) return 0 - if(blood_volume < BLOOD_VOLUME_BAD && !forced) + + if(cached_blood_volume < BLOOD_VOLUME_BAD && !ignore_low_blood) return 0 - if(blood_volume < amount) - amount = blood_volume + var/datum/blood_type/blood = get_blood_type() + + // Caps the amount to how much blood we have. + amount = min(amount, get_blood_volume()) + + if (!ignore_low_blood) + // Caps the amount to how much we can transfer before reaching low blood. + amount = min(amount, get_blood_volume() - BLOOD_VOLUME_BAD) - adjust_bloodvolume(-amount) + receiver.reagents.add_reagent(blood.reagent_type, amount, blood.get_blood_data(src), bodytemperature) - AM.reagents.add_reagent(blood.reagent_type, amount, blood.get_blood_data(src), bodytemperature) - return 1 + adjust_blood_volume(-amount) + return amount /// Transfers the blood of a mob factoring in the impure reagents in their blood /// Returns the actual amount of blood transferred /mob/living/proc/transfer_blood_impurities(datum/reagents/transfer_to, amount, impurity_mult = BLOODLETTING_MULT, mob/transferred_by) var/blacklisted_reagents = list(/datum/reagent/steam, /datum/reagent/water, /datum/reagent/blood, /datum/reagent/consumable/nutriment, /datum/reagent/consumable/soup) var/blood_purity = 1 // what % of the amt are we actually taking as blood? + var/cached_blood_volume = get_blood_volume() amount = min(amount, transfer_to.maximum_volume - transfer_to.total_volume) // the volume of our transfer if(reagents.total_volume) var/impurity_volume = reagents.total_volume for(var/reagent_type in blacklisted_reagents) impurity_volume -= reagents.get_reagent_amount(reagent_type, FALSE) if(impurity_volume > 0) - blood_purity = blood_volume / (blood_volume + impurity_volume) + blood_purity = cached_blood_volume / (cached_blood_volume + impurity_volume) reagents.trans_to(transfer_to, amount * impurity_mult * (1 - blood_purity), transfered_by=transferred_by, ignored_reagents=blacklisted_reagents) - var/blood_transferred = min(blood_volume, amount * blood_purity) // how much of the drip is straight up blood, final value + var/blood_transferred = min(cached_blood_volume, amount * blood_purity) // how much of the drip is straight up blood, final value var/datum/blood_type/blood = get_blood_type() var/list/blood_data = blood?.get_blood_data(src) var/datum/reagents/holder = new(maximum = blood_transferred) @@ -312,9 +335,133 @@ return TRUE /mob/living/carbon/human/add_splatter_floor(turf/T, amt) - if(!(NOBLOOD in dna.species.species_traits)) + if(CAN_HAVE_BLOOD(src)) . = ..() /mob/living/carbon/human/add_splatter_floor(turf/T, small_drip) - if(!(NOBLOOD in dna.species.species_traits)) + if(CAN_HAVE_BLOOD(src)) . = ..() + +/// Returns whether this mob can have blood. +/// Use the CAN_HAVE_BLOOD(mob) macro instead, this is used to update the cached value. +/mob/living/proc/can_have_blood() + return default_blood_volume > 0 + +/mob/living/carbon/can_have_blood() + return !HAS_TRAIT(src, TRAIT_NOBLOOD) + +/// Returns the blood volume of the mob. +/// Apply modifiers when reading blood volume for oxyloss damage, HUDs and analyzers. +/// Don't apply modifiers when using blood itself, like in spells and reagent transfers. +/mob/living/proc/get_blood_volume(apply_modifiers = FALSE) + return CAN_HAVE_BLOOD(src) ? blood_volume : 0 // Overriding blood setting code can cause blood_volume to be non-zero even when a mob shouldn't have blood. + +/mob/living/carbon/get_blood_volume(apply_modifiers = FALSE) + if (!CAN_HAVE_BLOOD(src)) + return 0 // Overriding blood setting code can cause blood_volume to be non-zero even when a mob shouldn't have blood. + if (!apply_modifiers) + return blood_volume // Default behavior, returns the real blood volume. + if (status_flags & GODMODE) + return default_blood_volume // Makes TRAIT_GODMODE grant immunity to the effects of bleeding. (oxyloss, passing out, etc.) + + var/amount = blood_volume + + /* + // For simple multipliers, like a blood worm in a mob. + for (var/source in blood_volume_modifiers) + amount *= blood_volume_modifiers[source] + + // Handled here instead of in the saline reagent datum, because this way the modification order is consistent. + // E.g. if you have an effect that modifies blood volume over the dilution cap, then saline should do nothing. + var/datum/reagent/medicine/salglu_solution/saline = reagents?.has_reagent(/datum/reagent/medicine/salglu_solution) + if (saline && amount < saline.dilution_cap) + var/datum/blood_type/blood_type = get_bloodtype() + if (blood_type?.restoration_chem == saline.required_restoration_chem) + amount = min(amount + saline.volume * saline.dilution_per_unit, BLOOD_VOLUME_NORMAL) + */ + + return amount + +/// Sets the base blood volume of the mob, returns the blood volume of the mob after. +/mob/living/proc/set_blood_volume(amount, minimum = 0, maximum = BLOOD_VOLUME_MAXIMUM, cached_blood_volume = null) + if (!isnum(cached_blood_volume)) + cached_blood_volume = get_blood_volume() + + if (!CAN_HAVE_BLOOD(src) && amount != 0) + return cached_blood_volume + + // Don't return early even if "amount == cached_blood_volume", because we don't know if minimum or maximum would change it anyway. + // Putting this here because I made that mistake and it led to a bug. + + blood_volume = clamp(amount, minimum, maximum) + + var/updated_blood_volume = get_blood_volume() + + if (cached_blood_volume != updated_blood_volume) + QUEUE_BLOOD_UPDATE(src) + + return updated_blood_volume + +/// Adjusts the base blood volume of the mob and returns the change. +/// Increases in blood volume give a positive return value and vice versa. +/// Maximum only applies on positive amounts and vice versa. +/mob/living/proc/adjust_blood_volume(amount, minimum = 0, maximum = BLOOD_VOLUME_MAXIMUM) + if (!CAN_HAVE_BLOOD(src) || amount == 0) + return 0 + + var/cached_blood_volume = get_blood_volume() + + if (amount < 0) + if (cached_blood_volume <= minimum) + // Already at or below the minimum, don't decrease further. + return 0 + // Decreases shouldn't jump the pre-existing value to the maximum. + maximum = INFINITY + else + if (cached_blood_volume >= maximum) + // Already at or above the maximum, don't increase further. + return 0 + // Increases shouldn't jump the pre-existing value to the minimum. + minimum = -INFINITY + + var/updated_blood_volume = set_blood_volume(cached_blood_volume + amount, minimum = minimum, maximum = maximum, cached_blood_volume = cached_blood_volume) + return updated_blood_volume - cached_blood_volume + +/// Updates effects that rely on whether the mob can have blood. +/mob/living/proc/update_blood_status() + var/had_blood = CAN_HAVE_BLOOD(src) + var/has_blood = can_have_blood() + + // Must not return early on first init for mobs that can have blood. (otherwise they will miss being added to the blood hud) + if (had_blood == has_blood) + return + + var/old_blood_volume = get_blood_volume() + + // Must be sent before living flags are updated. + SEND_SIGNAL(src, COMSIG_LIVING_PRE_UPDATE_BLOOD_STATUS, had_blood, has_blood, old_blood_volume) + + living_flags = has_blood ? (living_flags | LIVING_CAN_HAVE_BLOOD) : (living_flags & ~LIVING_CAN_HAVE_BLOOD) + + set_blood_volume(has_blood ? default_blood_volume : 0) + + var/new_blood_volume = get_blood_volume() + + // Must be sent after living flags are updated. + SEND_SIGNAL(src, COMSIG_LIVING_UPDATE_BLOOD_STATUS, had_blood, has_blood, old_blood_volume, new_blood_volume) + + /* + var/datum/atom_hud/data/human/blood/blood_hud = GLOB.huds[DATA_HUD_BLOOD] + + if (has_blood) + blood_hud.add_atom_to_hud(src) + else + blood_hud.remove_atom_from_hud(src) + */ + + update_blood_effects() + +/// Updates effects that rely on blood volume or status, like blood HUDs. +/mob/living/proc/update_blood_effects() + living_flags &= ~BLOOD_UPDATE_QUEUED + // blood_hud_set_status() diff --git a/code/modules/mob/living/carbon/blood.dm b/code/modules/mob/living/carbon/blood.dm index eae4ba89032..db267784928 100644 --- a/code/modules/mob/living/carbon/blood.dm +++ b/code/modules/mob/living/carbon/blood.dm @@ -8,10 +8,10 @@ if(HAS_TRAIT(src, TRAIT_BLOODLOSS_IMMUNE)) return BLOOD_VOLUME_NORMAL if(!needs_heart()) - return blood_volume + return BLOOD_VOLUME_NORMAL var/heart_efficiency = getorganslotefficiency(ORGAN_SLOT_HEART) - var/apparent_blood_volume = blood_volume + var/apparent_blood_volume = get_blood_volume() var/pulse_mod = 1 if(HAS_TRAIT(src, TRAIT_FAKEDEATH)) @@ -55,7 +55,7 @@ /// Blood volume, affected by the condition of circulation organs, affected by the oxygen loss - What ultimately matters for brain. /mob/living/carbon/proc/get_blood_oxygenation() - if(HAS_TRAIT(src, TRAIT_BLOODLOSS_IMMUNE)) + if(HAS_TRAIT(src, TRAIT_BLOODLOSS_IMMUNE) || !CAN_HAVE_BLOOD(src)) return BLOOD_VOLUME_NORMAL var/apparent_blood_volume = get_blood_circulation() diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 57f3c4cc671..a00b1195766 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -625,7 +625,7 @@ var/mob/living/carbon/C = src C.add_stress(/datum/stress_event/vomit) else - if(NOBLOOD in dna?.species?.species_traits) + if(!CAN_HAVE_BLOOD(src)) return TRUE if(message) visible_message("[src] coughs up blood!", "I cough up blood!") @@ -848,7 +848,7 @@ else clear_fullscreen("CMODE") - if(health <= crit_threshold || ((blood_volume in -INFINITY to BLOOD_VOLUME_SURVIVE) && !HAS_TRAIT(src, TRAIT_BLOODLOSS_IMMUNE))) + if(health <= crit_threshold || (!HAS_TRAIT(src, TRAIT_BLOODLOSS_IMMUNE) && CAN_HAVE_BLOOD(src) && (get_blood_volume() in -INFINITY to BLOOD_VOLUME_SURVIVE))) var/severity = 0 switch(health) if(-20 to -10) @@ -1030,8 +1030,8 @@ /mob/living/carbon/revive(full_heal_flags = NONE, excess_healing = 0, force_grab_ghost = FALSE) if(excess_healing) - if(dna && !(NOBLOOD in dna.species.species_traits)) - adjust_bloodvolume(excess_healing * 2) + if(CAN_HAVE_BLOOD(src)) + adjust_blood_volume(excess_healing * 2) for(var/obj/item/organ/organ as anything in internal_organs) organ.applyOrganDamage(excess_healing * -1) diff --git a/code/modules/mob/living/carbon/carbon_defines.dm b/code/modules/mob/living/carbon/carbon_defines.dm index 2a9219066c2..d7608b7a7a5 100644 --- a/code/modules/mob/living/carbon/carbon_defines.dm +++ b/code/modules/mob/living/carbon/carbon_defines.dm @@ -1,5 +1,4 @@ /mob/living/carbon - blood_volume = BLOOD_VOLUME_NORMAL gender = MALE base_intents = list(INTENT_HELP, INTENT_HARM) hud_possible = list(ANTAG_HUD) diff --git a/code/modules/mob/living/carbon/damage_procs.dm b/code/modules/mob/living/carbon/damage_procs.dm index f6a978a85ac..1fbe0efa2d2 100644 --- a/code/modules/mob/living/carbon/damage_procs.dm +++ b/code/modules/mob/living/carbon/damage_procs.dm @@ -130,9 +130,9 @@ if(!forced && HAS_TRAIT(src, TRAIT_TOXINLOVER)) //damage becomes healing and healing becomes damage amount = -amount if(amount > 0) - blood_volume -= 5*amount + adjust_blood_volume(-amount * 5) else - blood_volume -= amount + adjust_blood_volume(-amount) if(HAS_TRAIT(src, TRAIT_TOXIMMUNE)) //Prevents toxin damage, but not healing amount = min(amount, 0) return ..() diff --git a/code/modules/mob/living/carbon/examine.dm b/code/modules/mob/living/carbon/examine.dm index 858b0beeb9c..65c7f9d2391 100644 --- a/code/modules/mob/living/carbon/examine.dm +++ b/code/modules/mob/living/carbon/examine.dm @@ -519,9 +519,9 @@ . += span_warning("[P[THEYRE]] barely unconscious.") // Blood volume - if(!SEND_SIGNAL(src, COMSIG_DISGUISE_STATUS)) + if(!SEND_SIGNAL(src, COMSIG_DISGUISE_STATUS) && CAN_HAVE_BLOOD(src)) var/blood_lvl_msg - switch(blood_volume) + switch(get_blood_volume()) if(-INFINITY to BLOOD_VOLUME_SURVIVE) blood_lvl_msg = html_tag("B", "[P[THEYRE]] extremely pale and sickly.") if(BLOOD_VOLUME_SURVIVE to BLOOD_VOLUME_BAD) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 9f9f2cc537d..c16d2c062c9 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -497,7 +497,9 @@ return else if(hud_used.bloods) - var/bloodloss = ((BLOOD_VOLUME_NORMAL - blood_volume) / BLOOD_VOLUME_NORMAL) * 100 + var/bloodloss = 0 + if(CAN_HAVE_BLOOD(src)) + bloodloss = ((BLOOD_VOLUME_NORMAL - get_blood_volume()) / BLOOD_VOLUME_NORMAL) * 100 var/toxloss = getToxLoss() var/oxyloss = getOxyLoss() @@ -640,7 +642,7 @@ return TRUE /mob/living/carbon/human/vomit(lost_nutrition = 10, blood = 0, stun = 1, distance = 0, message = 1, toxic = 0) - if(blood && (NOBLOOD in dna.species.species_traits) && !HAS_TRAIT(src, TRAIT_TOXINLOVER)) + if(blood && !CAN_HAVE_BLOOD(src) && !HAS_TRAIT(src, TRAIT_TOXINLOVER)) if(message) visible_message("[src] dry heaves!", \ "I try to throw up, but there's nothing in your stomach!") diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 325773f90a5..14a0ec1615f 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -611,15 +611,16 @@ else examination += "[m1] dead." - switch(blood_volume) - if(-INFINITY to BLOOD_VOLUME_SURVIVE) - examination += "[m1] extremely anemic." - if(BLOOD_VOLUME_SURVIVE to BLOOD_VOLUME_BAD) - examination += "[m1] very anemic." - if(BLOOD_VOLUME_BAD to BLOOD_VOLUME_OKAY) - examination += "[m1] anemic." - if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE) - examination += "[m1] a little anemic." + if(CAN_HAVE_BLOOD(src)) + switch(get_blood_volume()) + if(-INFINITY to BLOOD_VOLUME_SURVIVE) + examination += "[m1] extremely anemic." + if(BLOOD_VOLUME_SURVIVE to BLOOD_VOLUME_BAD) + examination += "[m1] very anemic." + if(BLOOD_VOLUME_BAD to BLOOD_VOLUME_OKAY) + examination += "[m1] anemic." + if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE) + examination += "[m1] a little anemic." if(HAS_TRAIT(src, TRAIT_PARALYSIS)) if(HAS_TRAIT(src, TRAIT_NO_BITE)) diff --git a/code/modules/mob/living/carbon/human/init_signals.dm b/code/modules/mob/living/carbon/human/init_signals.dm index d259201b68d..f85566e2c41 100644 --- a/code/modules/mob/living/carbon/human/init_signals.dm +++ b/code/modules/mob/living/carbon/human/init_signals.dm @@ -3,12 +3,33 @@ RegisterSignal(src, SIGNAL_ADDTRAIT(TRAIT_NO_SPLIT_PERSONALITY), PROC_REF(on_no_split_personality_trait_gain)) + //Traits that register add and remove + RegisterSignal(src, SIGNAL_ADDTRAIT(TRAIT_NOBLOOD), PROC_REF(on_noblood_trait_gain)) + RegisterSignal(src, SIGNAL_REMOVETRAIT(TRAIT_NOBLOOD), PROC_REF(on_noblood_trait_loss)) + RegisterSignal(src, SIGNAL_ADDCHEMEFFECT(CE_STIMULANT), PROC_REF(receive_actionboost)) RegisterSignal(src, SIGNAL_REMOVECHEMEFFECT(CE_STIMULANT), PROC_REF(remove_actionboost)) RegisterSignal(src, SIGNAL_ADDTRAIT(TRAIT_PSYDONIAN_GRIT), PROC_REF(on_psydonian_gain)) RegisterSignal(src, SIGNAL_REMOVETRAIT(TRAIT_PSYDONIAN_GRIT), PROC_REF(on_psydonian_lose)) +/** + * On gain of TRAIT_NOBLOOD + * + * This will make the mob update its blood state. + */ +/mob/living/carbon/proc/on_noblood_trait_gain(datum/source) + SIGNAL_HANDLER + update_blood_status() + +/** + * On removal of TRAIT_NOBLOOD + * + * This will make the mob update its blood state. + */ +/mob/living/carbon/proc/on_noblood_trait_loss(datum/source) + SIGNAL_HANDLER + update_blood_status() /** * On gain of TRAIT_NO_SPLIT_PERSONALITY diff --git a/code/modules/mob/living/carbon/human/npc/skeleton/_skeleton.dm b/code/modules/mob/living/carbon/human/npc/skeleton/_skeleton.dm index 82d4fb5ef63..6eb5ee3ab34 100644 --- a/code/modules/mob/living/carbon/human/npc/skeleton/_skeleton.dm +++ b/code/modules/mob/living/carbon/human/npc/skeleton/_skeleton.dm @@ -49,8 +49,8 @@ faction |= "islander" if(length(quirks)) clear_quirks() + ADD_TRAIT(src, TRAIT_NOBLOOD, SPECIES_TRAIT) if(dna?.species) - dna.species.species_traits |= NOBLOOD dna.species.soundpack_m = new /datum/voicepack/skeleton() dna.species.soundpack_f = new /datum/voicepack/skeleton() var/obj/item/bodypart/head/headdy = get_bodypart("head") diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index 7bea449d2de..cc79bf1d5e6 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -625,7 +625,7 @@ GLOBAL_LIST_EMPTY(roundstart_species) var/used_neworgan = FALSE var/should_have if(neworgan) - should_have = neworgan.get_availability(src) + should_have = neworgan.get_availability(src, C) else should_have = TRUE @@ -1246,7 +1246,7 @@ GLOBAL_LIST_EMPTY(roundstart_species) /datum/species/proc/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H) if(chem.type == exotic_bloodtype) - H.blood_volume = min(H.blood_volume + round(chem.volume, 0.1), BLOOD_VOLUME_MAXIMUM) + H.adjust_blood_volume(round(chem.volume, 0.1), maximum = BLOOD_VOLUME_SAFE_MAXIMUM) H.reagents.del_reagent(chem.type) return TRUE if(chem.overdose_threshold && chem.volume >= chem.overdose_threshold) diff --git a/code/modules/mob/living/carbon/human/species_types/automatons/_automaton.dm b/code/modules/mob/living/carbon/human/species_types/automatons/_automaton.dm index 8182595ac2a..2ba4838a21c 100644 --- a/code/modules/mob/living/carbon/human/species_types/automatons/_automaton.dm +++ b/code/modules/mob/living/carbon/human/species_types/automatons/_automaton.dm @@ -85,9 +85,9 @@ species_traits = list( NO_UNDERWEAR, NOTRANSSTING, - NOBLOOD, ) inherent_traits = list( + TRAIT_NOBLOOD, TRAIT_BLOODLOSS_IMMUNE, TRAIT_NORMALIZED_BLOOD, TRAIT_NOMOOD, diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index 679d8034da2..0332cc7f833 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -37,7 +37,7 @@ handle_shock(delta_time, times_fired) handle_shock_stage(delta_time, times_fired) - if((blood_volume > BLOOD_VOLUME_SURVIVE) || HAS_TRAIT(src, TRAIT_BLOODLOSS_IMMUNE)) + if(!CAN_HAVE_BLOOD(src) || (get_blood_volume() > BLOOD_VOLUME_SURVIVE) || HAS_TRAIT(src, TRAIT_BLOODLOSS_IMMUNE)) if(!heart_attacking) if(oxyloss) adjustOxyLoss(-5) @@ -385,8 +385,8 @@ All effects don't start immediately, but rather get worse over time; the rate is if(limb && !limb.skeletonized) if(limb.get_damage() >= (limb.max_damage - 5)) limb.cremation_progress += rand(2,5) - if(dna && dna.species && !(NOBLOOD in dna.species.species_traits)) - blood_volume = max(blood_volume - 10, 0) + if(CAN_HAVE_BLOOD(src)) + adjust_blood_volume(-10) if(limb.cremation_progress >= 50) if(limb.status == BODYPART_ORGANIC) //Non-organic limbs don't burn limb.skeletonize() @@ -427,9 +427,9 @@ All effects don't start immediately, but rather get worse over time; the rate is H.underwear = "Nude" should_update_body = TRUE if(dna && dna.species) - if(dna && dna.species && !(NOBLOOD in dna.species.species_traits)) - blood_volume = 0 - dna.species.species_traits |= NOBLOOD + if(CAN_HAVE_BLOOD(src)) + set_blood_volume(0) + ADD_TRAIT(src, TRAIT_NOBLOOD, TRAIT_GENERIC) if(should_update_body) update_body() @@ -457,8 +457,10 @@ All effects don't start immediately, but rather get worse over time; the rate is /mob/living/carbon/proc/needs_heart() if(HAS_TRAIT(src, TRAIT_STABLEHEART)) return FALSE - if(NOBLOOD in dna?.species?.species_traits) //not all carbons have species! + if(!CAN_HAVE_BLOOD(src)) return FALSE + // if(dna && dna.species && isnull(dna.species.mutantheart)) //not all carbons have species! + // return FALSE return TRUE /* @@ -543,7 +545,7 @@ All effects don't start immediately, but rather get worse over time; the rate is adjust_energy(sleepy_mod * (max_energy * 0.004)) if(hydration > 0 || yess) if(!bleed_rate) - adjust_bloodvolume(4 * sleepy_mod, BLOOD_VOLUME_NORMAL) + adjust_blood_volume(4 * sleepy_mod, maximum = BLOOD_VOLUME_NORMAL) for(var/obj/item/bodypart/affecting as anything in bodyparts) //for context, it takes 5 small cuts (0.4 x 5) or 3 normal cuts (0.8 x 3) for a bodypart to not be able to heal itself if(affecting.get_bleed_rate() >= 2) diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 0ebe8140b32..47d12bede7a 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -49,7 +49,7 @@ if(!stat && HAS_TRAIT(src, TRAIT_PSYDONIAN_GRIT) && !HAS_TRAIT(src, TRAIT_PARALYSIS)) handle_wounds() //passively heal wounds, but not if you're skullcracked OR DEAD. - if(blood_volume > BLOOD_VOLUME_SURVIVE) + if(!CAN_HAVE_BLOOD(src) || get_blood_volume() > BLOOD_VOLUME_SURVIVE) for(var/datum/wound/wound as anything in get_wounds()) wound.heal_wound(wound.passive_healing * 0.25) @@ -73,6 +73,9 @@ last_island_check = world.time update_island_cache() + if (living_flags & BLOOD_UPDATE_QUEUED) + update_blood_effects() + if(stat != DEAD) return 1 diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 5969f530b22..a4d67aebb0a 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -22,6 +22,7 @@ if(unique_name) name = "[name] ([rand(1, 1000)])" real_name = name + update_blood_status() add_ally(src) GLOB.mob_living_list += src AddElement(/datum/element/movetype_handler) @@ -713,7 +714,7 @@ return if(!reaper) return - if (InCritical() || health <= 0 || (blood_volume < BLOOD_VOLUME_SURVIVE)) + if (InCritical() || health <= 0) log_message("Has [whispered ? "whispered his final words" : "succumbed to death"] while in [InFullCritical() ? "hard":"soft"] critical with [round(health, 0.1)] points of health!", LOG_ATTACK) if(istype(src.loc, /turf/open/water) && !HAS_TRAIT(src, TRAIT_NOBREATH) && body_position == LYING_DOWN && client) @@ -961,9 +962,9 @@ if(status_flags & GODMODE) return set_health(maxHealth - getOxyLoss() - getToxLoss() - getFireLoss() - getBruteLoss() - getCloneLoss()) - if(HAS_TRAIT(src, TRAIT_SIMPLE_WOUNDS) && !HAS_TRAIT(src, TRAIT_BLOODLOSS_IMMUNE)) + if(CAN_HAVE_BLOOD(src) && HAS_TRAIT(src, TRAIT_SIMPLE_WOUNDS) && !HAS_TRAIT(src, TRAIT_BLOODLOSS_IMMUNE)) // You dont have any blood and your not bloodloss immune? Dead. - if(blood_volume <= 0) + if(get_blood_volume() <= 0) set_health(NONE) update_stat() update_pain() @@ -1247,32 +1248,33 @@ blood_exists = TRUE if(isturf(start)) var/trail_type = getTrail() - if(trail_type) - var/brute_ratio = round(getBruteLoss() / maxHealth, 0.1) - if(blood_volume && blood_volume > max(BLOOD_VOLUME_NORMAL*(1 - brute_ratio * 0.25), 0))//don't leave trail if blood volume below a threshold - blood_volume = max(blood_volume - max(1, brute_ratio * 2), 0) //that depends on our brute damage. - var/newdir = get_dir(target_turf, start) - if(newdir != direction) - newdir = newdir | direction - if(newdir == 3) //N + S - newdir = NORTH - else if(newdir == 12) //E + W - newdir = EAST - if((newdir in GLOB.cardinals) && (prob(50))) - newdir = turn(get_dir(target_turf, start), 180) - if(!blood_exists) - new /obj/effect/decal/cleanable/trail_holder(start, get_blood_type().color) - - for(var/obj/effect/decal/cleanable/trail_holder/TH in start) - if((!(newdir in TH.existing_dirs) || trail_type == "trails_1" || trail_type == "trails_2") && TH.existing_dirs.len <= 16) //maximum amount of overlays is 16 (all light & heavy directions filled) - TH.existing_dirs += newdir - var/image/bloodthing = image('icons/effects/blood.dmi', trail_type, dir = newdir) - bloodthing.color = get_blood_type().color - TH.add_overlay(bloodthing) - TH.transfer_mob_blood_dna(src) + if(!trail_type) + return + var/brute_ratio = round(getBruteLoss() / maxHealth, 0.1) + if(get_blood_volume() > max(BLOOD_VOLUME_NORMAL*(1 - brute_ratio * 0.25), 0))//don't leave trail if blood volume below a threshold + adjust_blood_volume(-max(1, brute_ratio * 2)) //that depends on our brute damage. + var/newdir = get_dir(target_turf, start) + if(newdir != direction) + newdir = newdir | direction + if(newdir == 3) //N + S + newdir = NORTH + else if(newdir == 12) //E + W + newdir = EAST + if((newdir in GLOB.cardinals) && (prob(50))) + newdir = turn(get_dir(target_turf, start), 180) + if(!blood_exists) + new /obj/effect/decal/cleanable/trail_holder(start, get_blood_type().color) + + for(var/obj/effect/decal/cleanable/trail_holder/TH in start) + if((!(newdir in TH.existing_dirs) || trail_type == "trails_1" || trail_type == "trails_2") && TH.existing_dirs.len <= 16) //maximum amount of overlays is 16 (all light & heavy directions filled) + TH.existing_dirs += newdir + var/image/bloodthing = image('icons/effects/blood.dmi', trail_type, dir = newdir) + bloodthing.color = get_blood_type().color + TH.add_overlay(bloodthing) + TH.transfer_mob_blood_dna(src) /mob/living/carbon/human/makeTrail(turf/T) - if((NOBLOOD in dna.species.species_traits) || !bleed_rate || bleedsuppress) + if(!CAN_HAVE_BLOOD(src) || !bleed_rate || bleedsuppress) return ..() diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index 584e2ee437d..c61bc933114 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -112,7 +112,13 @@ var/stun_absorption = null //converted to a list of stun absorption sources this mob has when one is added - var/blood_volume = BLOOD_VOLUME_NORMAL //how much blood the mob has + /// How much blood the mob currently has. + /// Don't read directly, use get_blood_volume() and get_blood_volume(apply_modifiers = TRUE). + /// Don't write directly either, use set_blood_volume() and adjust_blood_volume(). + /// Also don't initialize this. Initialize default_blood_volume instead. + var/blood_volume = 0 + /// The default blood volume of the mob. Used primarily for healing bloodloss. + var/default_blood_volume = BLOOD_VOLUME_NORMAL var/see_override = 0 //0 for no override, sets see_invisible = see_override in silicon & carbon life process via update_sight() diff --git a/code/modules/mob/living/simple_animal/examine.dm b/code/modules/mob/living/simple_animal/examine.dm index c0113b7ffa3..f711e809261 100644 --- a/code/modules/mob/living/simple_animal/examine.dm +++ b/code/modules/mob/living/simple_animal/examine.dm @@ -32,15 +32,16 @@ msg += "[m1] gravely wounded." // Blood volume - switch(blood_volume) - if(-INFINITY to BLOOD_VOLUME_SURVIVE) - msg += "[m1] extremely pale and sickly." - if(BLOOD_VOLUME_SURVIVE to BLOOD_VOLUME_BAD) - msg += "[m1] very pale." - if(BLOOD_VOLUME_BAD to BLOOD_VOLUME_OKAY) - msg += "[m1] pale." - if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE) - msg += "[m1] a little pale." + if(CAN_HAVE_BLOOD(src)) + switch(get_blood_volume()) + if(-INFINITY to BLOOD_VOLUME_SURVIVE) + msg += "[m1] extremely pale and sickly." + if(BLOOD_VOLUME_SURVIVE to BLOOD_VOLUME_BAD) + msg += "[m1] very pale." + if(BLOOD_VOLUME_BAD to BLOOD_VOLUME_OKAY) + msg += "[m1] pale." + if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE) + msg += "[m1] a little pale." // Bleeding var/bleed_rate = get_bleed_rate() diff --git a/code/modules/mob/living/simple_animal/friendly/pet.dm b/code/modules/mob/living/simple_animal/friendly/pet.dm index df7b5f38e52..744604a99ba 100644 --- a/code/modules/mob/living/simple_animal/friendly/pet.dm +++ b/code/modules/mob/living/simple_animal/friendly/pet.dm @@ -2,7 +2,6 @@ icon = 'icons/roguetown/mob/monster/pets.dmi' mob_size = MOB_SIZE_SMALL mob_biotypes = MOB_ORGANIC|MOB_BEAST - blood_volume = BLOOD_VOLUME_NORMAL var/unique_pet = FALSE // if the mob can be renamed density = FALSE //pass_flags = PASSTABLE diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/creacher/shade.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/creacher/shade.dm index da9f37e0860..bc4fe432a55 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/creacher/shade.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/creacher/shade.dm @@ -68,7 +68,7 @@ defprob = 0 defdrain = 0 dodgetime = 0 - blood_volume = 0 + default_blood_volume = 0 del_on_deaggro = 999 SECONDS attack_sound = list('sound/magic/magic_nulled.ogg') diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm index 5765de4f261..cd0889070f7 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm @@ -33,7 +33,6 @@ attack_sound = PUNCHWOOSH harm_intent_damage = 5 environment_smash = ENVIRONMENT_SMASH_NONE - blood_volume = BLOOD_VOLUME_NORMAL tame_chance = 0 retreat_distance = 10 diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 833e4b1102b..fbd03eafb1c 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -272,7 +272,7 @@ var/mob/living/L = target if(blocked != 100) // not completely blocked - if(damage && L.blood_volume && damage_type == BRUTE) + if(damage && L.get_blood_volume() && damage_type == BRUTE) var/splatter_dir = dir if(starting) splatter_dir = get_dir(starting, target_loca) diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index ffe267bcd6a..90e58173540 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -48,22 +48,20 @@ exposed_mob.adjust_hydration(vitae * 0.1) var/mob/living/carbon/exposed_carbon = exposed_mob - if(istype(exposed_carbon) && (NOBLOOD in exposed_carbon.dna?.species?.species_traits)) + if(istype(exposed_carbon) && !CAN_HAVE_BLOOD(exposed_carbon)) return //if it's non-toxic, drink up, otherwise, you need the blooddrinker trait and it has to be a blood you're compatible with or you need to be a nasty eater if(methods & INJECT) var/modifier = 1 //TODO: Borbop ~ Once we get a proper transfusion system this will become unneeded basically means instead of 5 units we inject 100 units which is 4 injections to suriving level. This is 100% blood duping but like... its this or 80 syringes of blood to get someone restarted if(exposed_mob.stat >= DEAD) modifier = 20 - if(exposed_mob.blood_volume <= BLOOD_VOLUME_MAXIMUM) - exposed_mob.adjust_bloodvolume(round(reac_volume, 0.1) * modifier) + exposed_mob.adjust_blood_volume(round(reac_volume, 0.1) * modifier, maximum = BLOOD_VOLUME_SAFE_MAXIMUM) return if(methods & INGEST) if(!drinking_self && (toxicity <= 0 || (HAS_TRAIT(exposed_mob, TRAIT_BLOODDRINKER) || HAS_TRAIT(exposed_mob, TRAIT_NASTY_EATER)))) if(!HAS_TRAIT(exposed_mob, TRAIT_NOHUNGER)) exposed_mob.adjust_hydration(reac_volume * 0.2) - if(exposed_mob.blood_volume < BLOOD_VOLUME_NORMAL) - exposed_mob.adjust_bloodvolume(reac_volume * 0.2) + exposed_mob.adjust_blood_volume(reac_volume * 0.2, maximum = BLOOD_VOLUME_NORMAL) return var/tox = toxicity * reac_volume if(HAS_TRAIT(exposed_carbon, TRAIT_POISON_RESILIENCE)) diff --git a/code/modules/spells/spell_types/pointed/absolve.dm b/code/modules/spells/spell_types/pointed/absolve.dm index ffee38bdbe0..0c4abeebc7a 100644 --- a/code/modules/spells/spell_types/pointed/absolve.dm +++ b/code/modules/spells/spell_types/pointed/absolve.dm @@ -92,10 +92,11 @@ // Transfer blood var/blood_transfer = 0 - if(H.blood_volume < BLOOD_VOLUME_NORMAL) - blood_transfer = BLOOD_VOLUME_NORMAL - H.blood_volume - H.blood_volume = BLOOD_VOLUME_NORMAL - user.blood_volume -= blood_transfer + var/cached_blood_volume = H.get_blood_volume() + if(CAN_HAVE_BLOOD(H) && cached_blood_volume < BLOOD_VOLUME_NORMAL) + blood_transfer = BLOOD_VOLUME_NORMAL - cached_blood_volume + H.set_blood_volume(BLOOD_VOLUME_NORMAL) + user.adjust_blood_volume(-blood_transfer) to_chat(user, span_warning("You feel your blood drain into [H]!")) to_chat(H, span_notice("You feel your blood replenish!")) diff --git a/code/modules/spells/spell_types/pointed/avert.dm b/code/modules/spells/spell_types/pointed/avert.dm index a2cd6f1d96f..c03bde47121 100644 --- a/code/modules/spells/spell_types/pointed/avert.dm +++ b/code/modules/spells/spell_types/pointed/avert.dm @@ -67,7 +67,8 @@ while(do_after(owner, tickspeed, cast_on)) cast_on.adjustOxyLoss(-10) - cast_on.blood_volume = max((BLOOD_VOLUME_SURVIVE * 1.5), cast_on.blood_volume) + if(cast_on.get_blood_volume() < BLOOD_VOLUME_SURVIVE * 1.5) + cast_on.set_blood_volume(BLOOD_VOLUME_SURVIVE * 1.5) if(prob(5) && cast_on.health <= 5) to_chat(cast_on, span_small(pick(near_death_lines))) diff --git a/code/modules/spells/spell_types/pointed/create_abyssoid.dm b/code/modules/spells/spell_types/pointed/create_abyssoid.dm index ae0acfde197..3221c2ddd46 100644 --- a/code/modules/spells/spell_types/pointed/create_abyssoid.dm +++ b/code/modules/spells/spell_types/pointed/create_abyssoid.dm @@ -16,7 +16,7 @@ return FALSE var/mob/living/follower = owner - if(follower.blood_volume < BLOOD_VOLUME_BAD) + if(follower.get_blood_volume() < BLOOD_VOLUME_BAD) if(feedback) to_chat(follower, span_warning("You don't have enough blood to sacrifice!")) return FALSE @@ -49,11 +49,11 @@ to_chat(follower, span_warning("You must keep holding the leech during the ritual!")) return FALSE - if(follower.blood_volume < BLOOD_VOLUME_BAD) + if(follower.get_blood_volume() < BLOOD_VOLUME_BAD) to_chat(follower, span_warning("You don't have enough blood to complete the ritual!")) return FALSE - follower.blood_volume = max(follower.blood_volume - 70, 0) + follower.adjust_blood_volume(-70) var/obj/item/natural/worms/leech/abyssoid/new_leech = new(owner.drop_location()) qdel(target) follower.put_in_hands(new_leech) diff --git a/code/modules/spells/spell_types/pointed/healing.dm b/code/modules/spells/spell_types/pointed/healing.dm index 04bf78408b2..ed2502790aa 100644 --- a/code/modules/spells/spell_types/pointed/healing.dm +++ b/code/modules/spells/spell_types/pointed/healing.dm @@ -255,7 +255,7 @@ SEND_SIGNAL(owner, COMSIG_LIVING_HEALED_OTHER, amount_healed) cast_on.adjustToxLoss(-amount_healed) cast_on.adjustOxyLoss(-amount_healed) - cast_on.adjust_bloodvolume(blood_restoration + situational_blood, BLOOD_VOLUME_NORMAL) + cast_on.adjust_blood_volume(blood_restoration + situational_blood, maximum = BLOOD_VOLUME_NORMAL) if(!iscarbon(cast_on)) cast_on.adjustBruteLoss(-amount_healed) cast_on.adjustFireLoss(-amount_healed) diff --git a/code/modules/spells/spell_types/pointed/ocean_embrace.dm b/code/modules/spells/spell_types/pointed/ocean_embrace.dm index f9baad9ff0b..5e7f2cc7062 100644 --- a/code/modules/spells/spell_types/pointed/ocean_embrace.dm +++ b/code/modules/spells/spell_types/pointed/ocean_embrace.dm @@ -54,4 +54,4 @@ situational_bonus = min(situational_bonus + 0.1, 2) if(situational_bonus > 1) to_chat(owner, span_greentext("Channeling Abyssor's power is easier in these conditions!")) - cast_on.blood_volume = max(cast_on.blood_volume, min(cast_on.blood_volume + BLOOD_VOLUME_OKAY * situational_bonus, BLOOD_VOLUME_NORMAL)) + cast_on.adjust_blood_volume(BLOOD_VOLUME_OKAY * situational_bonus, maximum = BLOOD_VOLUME_NORMAL) diff --git a/code/modules/spells/spell_types/pointed/projectile/blood_steal.dm b/code/modules/spells/spell_types/pointed/projectile/blood_steal.dm index 33a6e6f4e74..e4e7272c89a 100644 --- a/code/modules/spells/spell_types/pointed/projectile/blood_steal.dm +++ b/code/modules/spells/spell_types/pointed/projectile/blood_steal.dm @@ -54,7 +54,7 @@ if(ishuman(target)) var/mob/living/carbon/human/H = target - H.blood_volume = max(H.blood_volume - 45, 0) + H.adjust_blood_volume(-45) H.visible_message( span_danger("[H] has their blood ripped from their body!"), diff --git a/code/modules/spells/spell_types/pointed/revive.dm b/code/modules/spells/spell_types/pointed/revive.dm index a2ddd93b322..4c8297463a8 100644 --- a/code/modules/spells/spell_types/pointed/revive.dm +++ b/code/modules/spells/spell_types/pointed/revive.dm @@ -94,7 +94,7 @@ add_abstract_elastic_data(ELASCAT_MEDICAL, ELASDATA_ANASTASIS_REVIVE, 1) cast_on.emote("breathgasp") cast_on.adjust_jitter(100 SECONDS) - cast_on.adjust_bloodvolume(BLOOD_VOLUME_OKAY, BLOOD_VOLUME_OKAY) + cast_on.adjust_blood_volume(BLOOD_VOLUME_OKAY, maximum = BLOOD_VOLUME_OKAY) cast_on.visible_message(span_notice("[cast_on] is revived by holy light!"), span_green("I awake from the void.")) cast_on.apply_status_effect(/datum/status_effect/debuff/revive) cast_on.remove_client_colour(/datum/client_colour/monochrome/death) diff --git a/code/modules/spells/spell_types/undirected/bardic/rejuvenation_song.dm b/code/modules/spells/spell_types/undirected/bardic/rejuvenation_song.dm index 0bff41dac51..52e16ac7e9e 100644 --- a/code/modules/spells/spell_types/undirected/bardic/rejuvenation_song.dm +++ b/code/modules/spells/spell_types/undirected/bardic/rejuvenation_song.dm @@ -25,8 +25,7 @@ var/obj/effect/temp_visual/heal/H = new /obj/effect/temp_visual/heal_rogue(get_turf(owner)) H.color = "#660759" var/list/wCount = owner.get_wounds() - if(owner.blood_volume < BLOOD_VOLUME_NORMAL) - owner.adjust_bloodvolume(healing_on_tick + 1) + owner.adjust_blood_volume(healing_on_tick + 1, maximum = BLOOD_VOLUME_NORMAL) if(length(wCount) > 0) if(iscarbon(owner)) var/mob/living/carbon/carbon = owner diff --git a/code/modules/spells/spell_types/undirected/shapeshift/_shape_status.dm b/code/modules/spells/spell_types/undirected/shapeshift/_shape_status.dm index 32188a4c45c..9fd76290aeb 100644 --- a/code/modules/spells/spell_types/undirected/shapeshift/_shape_status.dm +++ b/code/modules/spells/spell_types/undirected/shapeshift/_shape_status.dm @@ -189,7 +189,7 @@ var/damage_to_apply = owner.maxHealth * ((caster_mob.maxHealth - caster_mob.health) / caster_mob.maxHealth) owner.apply_damage(damage_to_apply, source_spell.convert_damage_type, forced = TRUE) - owner.blood_volume = caster_mob.blood_volume + owner.set_blood_volume(caster_mob.get_blood_volume()) for(var/datum/action/bodybound_action as anything in caster_mob.actions) if(bodybound_action.target != caster_mob) @@ -225,7 +225,7 @@ caster_mob.apply_damage(damage_to_apply, source_spell.convert_damage_type, forced = TRUE, spread_damage = TRUE) if(iscarbon(owner)) - caster_mob.blood_volume = owner.blood_volume + caster_mob.set_blood_volume(owner.get_blood_volume()) /datum/status_effect/shapechange_mob/from_spell/on_shape_death(datum/source, gibbed) var/datum/action/cooldown/spell/undirected/shapeshift/source_spell = source_weakref.resolve() diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index 30698b81f63..4b07204855a 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -19,6 +19,8 @@ var/fingerprint var/status = BODYPART_ORGANIC + ///Random flags that describe this bodypart + var/bodypart_flags var/static_icon = FALSE var/body_zone //BODY_ZONE_CHEST, BODY_ZONE_L_ARM, etc , used for def_zone var/aux_zone // used for hands @@ -773,7 +775,7 @@ /obj/item/bodypart/chest/skeletonize(lethal = TRUE) . = ..() - if(lethal && owner && !(NOBLOOD in owner.dna?.species?.species_traits)) + if(lethal && owner && CAN_HAVE_BLOOD(owner)) owner.death() /obj/item/bodypart/proc/update_HP() @@ -1219,7 +1221,6 @@ species_icon = S.limbs_icon_f if(H.age == AGE_CHILD) species_icon = S.child_icon - species_flags_list = H.dna.species.species_traits if(S.use_skintones) diff --git a/code/modules/surgery/bodyparts/bodypart_examine.dm b/code/modules/surgery/bodyparts/bodypart_examine.dm index 31860eb1b6c..029c7aa8e3a 100644 --- a/code/modules/surgery/bodyparts/bodypart_examine.dm +++ b/code/modules/surgery/bodyparts/bodypart_examine.dm @@ -235,9 +235,9 @@ for(var/obj/item/organ/possible_artery in shuffle(getorganslotlist(ORGAN_SLOT_ARTERY))) if(possible_artery.is_bruised()) if(get_cut()) - status += span_bloody(uppertext("cut [parse_zone(possible_artery.zone)]")) + status += uppertext(span_bloody("cut [parse_zone(possible_artery.zone)]")) else - status += span_bloody(uppertext("bruised [parse_zone(possible_artery.zone)]")) + status += uppertext(span_bloody("bruised [parse_zone(possible_artery.zone)]")) if(skeletonized) status += "SKELETON" diff --git a/code/modules/surgery/bodyparts/bodypart_wounds.dm b/code/modules/surgery/bodyparts/bodypart_wounds.dm index e1d1a3c9719..55f68860fb1 100644 --- a/code/modules/surgery/bodyparts/bodypart_wounds.dm +++ b/code/modules/surgery/bodyparts/bodypart_wounds.dm @@ -113,14 +113,13 @@ return FALSE if(!is_organic_limb()) return FALSE - if(NOBLOOD in owner?.dna?.species?.species_traits) + if(!CAN_HAVE_BLOOD(owner)) return FALSE return TRUE /// Returns the total bleed rate on this bodypart -/obj/item/bodypart/proc/get_bleed_rate(artifical = FALSE) - var/datum/species/physiology = owner?.dna?.species - if(NOBLOOD in physiology?.species_traits) +/obj/item/bodypart/proc/get_bleed_rate(ignore_is_bleeding = FALSE) + if(!CAN_HAVE_BLOOD(owner)) return 0 if(!bleeds) return 0 @@ -129,19 +128,13 @@ bleed_rate += wound.bleed_rate for(var/datum/injury/injury as anything in injuries) - if(!artifical) - if(injury.is_bleeding()) - bleed_rate += injury.get_bleed_rate() - else - bleed_rate += injury.get_artifical_bleed_rate() + bleed_rate += injury.get_bleed_rate(ignore_is_bleeding) for(var/obj/item/embedded as anything in embedded_objects) if(!embedded.embedding.embedded_bloodloss) continue bleed_rate += embedded.embedding.embedded_bloodloss - if(physiology) - bleed_rate *= physiology.bleed_mod - if(bandage) + if(!ignore_is_bleeding && bandage) bleed_rate *= bandage?.bandage_effectiveness for(var/obj/item/grabbing/grab in grabbedby) bleed_rate *= grab.bleed_suppressing @@ -149,9 +142,6 @@ var/surgery_flags = get_surgery_flags() if(surgery_flags & SURGERY_CLAMPED) bleed_rate = min(bleed_rate, 0.5) - switch(burn_dam/max_damage) - if(0.75 to INFINITY) - bleed_rate += 5 return bleed_rate /obj/item/bodypart/proc/skeletonized_mod(bclass) @@ -175,8 +165,6 @@ return dam *= damage_multiplier if(dam < 5 && bclass != WOUND_INTERNAL_BRUISE) - if(CEILING(dam, 1) < 5) - return dam = CEILING(dam, 1) var/do_crit = (modifiers[CRIT_MOD_CHANCE] <= -100) ? FALSE : TRUE diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm index 24282a0cca3..72a4d4358c2 100644 --- a/code/modules/surgery/bodyparts/head.dm +++ b/code/modules/surgery/bodyparts/head.dm @@ -66,7 +66,7 @@ . = ..() sellprice = round((sellprice || 0) * 0.2) - if(lethal && owner && !(NOBLOOD in owner.dna?.species?.species_traits)) + if(lethal && owner && CAN_HAVE_BLOOD(owner)) owner.death() /obj/item/bodypart/head/grabbedintents(mob/living/user, atom/grabbed, precise) @@ -193,9 +193,8 @@ //Applies the debrained overlay if there is no brain if(!brain) var/image/debrain_overlay = image(layer = -HAIR_LAYER, dir = SOUTH) - if(!(NOBLOOD in species_flags_list)) - debrain_overlay.icon = 'icons/mob/human_face.dmi' - debrain_overlay.icon_state = "debrained" + debrain_overlay.icon = 'icons/mob/human_face.dmi' + debrain_overlay.icon_state = "debrained" . += debrain_overlay //ROGTODO add accessories (earrings, piercings) here diff --git a/code/modules/surgery/organs/_organ_visible.dm b/code/modules/surgery/organs/_organ_visible.dm index 56408d5a444..5e6556c7500 100644 --- a/code/modules/surgery/organs/_organ_visible.dm +++ b/code/modules/surgery/organs/_organ_visible.dm @@ -93,7 +93,7 @@ * owner_species - species, needed to return the mutant slot as true or false. stomach set to null means it shouldn't have one. * owner_mob - for more specific checks, like nightmares. */ -/obj/item/organ/proc/get_availability(datum/species/owner_species) +/obj/item/organ/proc/get_availability(datum/species/owner_species, mob/living/carbon/owner_mob) return slot in owner_species.organs /// Sets an accessory type and optionally colors too. diff --git a/code/modules/surgery/organs/internal/artery/_artery.dm b/code/modules/surgery/organs/internal/artery/_artery.dm index e0dfefcd82e..bc916b33004 100644 --- a/code/modules/surgery/organs/internal/artery/_artery.dm +++ b/code/modules/surgery/organs/internal/artery/_artery.dm @@ -55,10 +55,6 @@ bleed_mod *= 1.25 if(PULSE_FASTER, PULSE_THREADY) bleed_mod *= 1.5 - if(ishuman(owner)) - var/mob/living/carbon/human/human_owner = owner - if(human_owner.physiology) - bleed_mod *= human_owner.physiology.bleed_mod var/final_bleed_rate = CEILING(blood_flow * bleed_mod, 0.1) if(!final_bleed_rate) return @@ -128,4 +124,4 @@ owner.bleed(amount) // No open wound, even less drama else - owner.adjust_bloodvolume(-amount) + owner.adjust_blood_volume(-amount) diff --git a/code/modules/surgery/organs/internal/heart.dm b/code/modules/surgery/organs/internal/heart.dm index a876595261a..49d66b95040 100644 --- a/code/modules/surgery/organs/internal/heart.dm +++ b/code/modules/surgery/organs/internal/heart.dm @@ -148,8 +148,8 @@ to_chat(owner, span_userdanger("My [name] beats again!")) return TRUE -/obj/item/organ/heart/get_availability(datum/species/S) - return (!(NOBLOOD in S.species_traits) && !(TRAIT_STABLEHEART in S.inherent_traits)) +/obj/item/organ/heart/get_availability(datum/species/S, mob/living/carbon/owner_mob) + return (CAN_HAVE_BLOOD(owner_mob) && !(TRAIT_STABLEHEART in S.inherent_traits)) /obj/item/organ/heart/get_mechanics_examine(mob/user) . = ..() diff --git a/code/modules/surgery/organs/internal/spleen.dm b/code/modules/surgery/organs/internal/spleen.dm index 341c253af91..e2df0b64265 100644 --- a/code/modules/surgery/organs/internal/spleen.dm +++ b/code/modules/surgery/organs/internal/spleen.dm @@ -33,5 +33,5 @@ else if(damage >= low_threshold) examine_list += span_notice("[owner] looks a little wan.") -/obj/item/organ/spleen/get_availability(datum/species/S) - return (!(NOBLOOD in S.species_traits)) +/obj/item/organ/spleen/get_availability(datum/species/S, mob/living/carbon/owner_mob) + return CAN_HAVE_BLOOD(owner_mob) diff --git a/code/modules/surgery/organs/organ_processing/heart.dm b/code/modules/surgery/organs/organ_processing/heart.dm index 59f398f30de..32ef0752ad6 100644 --- a/code/modules/surgery/organs/organ_processing/heart.dm +++ b/code/modules/surgery/organs/organ_processing/heart.dm @@ -133,7 +133,7 @@ var/effective_blood_circulation = GET_EFFECTIVE_BLOOD_VOL(owner.get_blood_circulation(), owner.total_blood_req) switch(effective_blood_circulation) - if(BLOOD_VOLUME_MAXIMUM to BLOOD_VOLUME_EXCESS) + if(BLOOD_VOLUME_EXCESS to BLOOD_VOLUME_MAXIMUM) owner.status_flags &= ~BLEEDOUT if(DT_PROB(2.5, delta_time)) to_chat(owner, span_userdanger("Blood starts to tear my arteries apart!")) @@ -156,33 +156,26 @@ var/temp_bleed = 0 var/bleed_mod = 1 - if(ishuman(owner)) - var/mob/living/carbon/human/human_owner = owner - if(human_owner.physiology) - bleed_mod *= human_owner.physiology.bleed_mod for(var/obj/item/bodypart/bleed_part as anything in owner.bodyparts) - var/resulting_bleed = bleed_part.get_bleed_rate(TRUE) * 0.5 * delta_time var/true_bleed = bleed_part.get_bleed_rate() * 0.5 * delta_time switch(owner.pulse) if(PULSE_SLOW) - resulting_bleed *= 0.8 + true_bleed *= 0.8 if(PULSE_FAST) - resulting_bleed *= 1.25 + true_bleed *= 1.25 if(PULSE_FASTER, PULSE_THREADY) - resulting_bleed *= 1.5 - resulting_bleed = CEILING(resulting_bleed * bleed_mod, 0.1) + true_bleed *= 1.5 + true_bleed = CEILING(true_bleed * bleed_mod, 0.1) + temp_bleed += true_bleed if(bleed_part.bandage) bleed_part.try_bandage_expire() - else if(true_bleed > 0) - temp_bleed += true_bleed - if(temp_bleed) - if(owner.bleed(temp_bleed) && (temp_bleed >= 1.5)) + if(temp_bleed > 0) + if(owner.bleed(temp_bleed) && temp_bleed >= BLEED_RATE_NOTICABLE && owner.body_position == STANDING_UP) var/bleed_sound = "sound/gore/blood[rand(1, 6)].ogg" - if((temp_bleed > 1) && (owner.body_position == STANDING_UP)) - playsound(owner, bleed_sound, 60, FALSE) + playsound(owner, bleed_sound, 60, FALSE) - if(!HAS_TRAIT(owner, TRAIT_BLOODLOSS_IMMUNE) && owner.stat != DEAD) - switch(owner.blood_volume) + if(CAN_HAVE_BLOOD(owner) && !HAS_TRAIT(owner, TRAIT_BLOODLOSS_IMMUNE) && owner.stat != DEAD) + switch(owner.get_blood_volume()) if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE) owner.remove_status_effect(/datum/status_effect/debuff/bleedingworse) owner.remove_status_effect(/datum/status_effect/debuff/bleedingworst) diff --git a/code/modules/surgery/organs/organ_processing/spleen.dm b/code/modules/surgery/organs/organ_processing/spleen.dm index 037fda5b7fd..f4ed1f1bd58 100644 --- a/code/modules/surgery/organs/organ_processing/spleen.dm +++ b/code/modules/surgery/organs/organ_processing/spleen.dm @@ -3,10 +3,10 @@ mob_types = list(/mob/living/carbon/human) /datum/organ_process/spleen/needs_process(mob/living/carbon/owner) - return (..() && !HAS_TRAIT(owner, TRAIT_NOHUNGER)) + return (..() && !HAS_TRAIT(owner, TRAIT_NOHUNGER) && CAN_HAVE_BLOOD(owner)) /datum/organ_process/spleen/handle_process(mob/living/carbon/owner, delta_time, times_fired) - if(owner.blood_volume >= BLOOD_VOLUME_NORMAL) + if(owner.get_blood_volume() >= BLOOD_VOLUME_NORMAL) return var/nutrition_ratio = 0 switch(owner.nutrition) @@ -36,12 +36,5 @@ if(!blood_regen) return owner.adjust_nutrition(-combined_nutrition_requirement * nutrition_ratio * delta_time) - owner.adjust_bloodvolume(CEILING(blood_regen * nutrition_ratio * delta_time, 0.1)) - return TRUE - -/// Blood volume adjust proc -/mob/living/proc/adjust_bloodvolume(amount, cap) - if(cap && (blood_volume >= cap)) - return TRUE - blood_volume = max(blood_volume + amount, 0) + owner.adjust_blood_volume(CEILING(blood_regen * nutrition_ratio * delta_time, 0.1)) return TRUE From 530e58cfad2209abc3a063379c360af571f98398 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Mon, 11 May 2026 01:59:52 -0700 Subject: [PATCH 11/59] injury delta_time, bandage buff --- code/__DEFINES/medical.dm | 2 +- code/datums/injury/_injury.dm | 2 +- code/datums/wounds/_wound.dm | 2 +- code/datums/wounds/disembowel.dm | 2 +- code/datums/wounds/fractures.dm | 12 ++++++------ code/datums/wounds/simple_wounds/bites.dm | 2 +- code/datums/wounds/simple_wounds/bruises.dm | 2 +- code/datums/wounds/simple_wounds/punctures.dm | 8 ++++---- code/datums/wounds/simple_wounds/slashes.dm | 12 ++++++------ code/datums/wounds/special.dm | 6 +++--- code/game/objects/items/natural/bandage.dm | 2 +- code/game/objects/items/natural/cloth.dm | 2 +- .../surgery/organs/internal/artery/_artery.dm | 4 ++++ .../modules/surgery/organs/organ_processing/heart.dm | 2 +- 14 files changed, 32 insertions(+), 28 deletions(-) diff --git a/code/__DEFINES/medical.dm b/code/__DEFINES/medical.dm index d3766b1b89f..cc58f7621ca 100644 --- a/code/__DEFINES/medical.dm +++ b/code/__DEFINES/medical.dm @@ -157,7 +157,7 @@ DEFINE_BITFIELD(organ_flags, list( #define LIMB_EFFICIENCY_DISABLING 50 /// This is used as a reference point for dynamic wounds, so it's better off as a define. -#define ARTERY_LIMB_BLEEDRATE 25 +#define ARTERY_LIMB_BLEEDRATE 12.5 /// Multiplier applied to reagents in blood when factoring in total volume for "purity" #define BLOODLETTING_MULT 5 diff --git a/code/datums/injury/_injury.dm b/code/datums/injury/_injury.dm index bc508b1eff9..ddae1692b40 100644 --- a/code/datums/injury/_injury.dm +++ b/code/datums/injury/_injury.dm @@ -1,4 +1,4 @@ -#define BLEED_DAMAGE_RATIO 10 +#define BLEED_DAMAGE_RATIO 20 //This is basically the baystation wound datum, which i thought would synergize well with the TG wounds /**************************************************** diff --git a/code/datums/wounds/_wound.dm b/code/datums/wounds/_wound.dm index 596b286f63b..9f69c70ec83 100644 --- a/code/datums/wounds/_wound.dm +++ b/code/datums/wounds/_wound.dm @@ -44,7 +44,7 @@ GLOBAL_LIST_INIT(primordial_wounds, init_primordial_wounds()) /// How many "health points" this wound has, AKA how hard it is to heal var/whp = 60 - /// How much this wound bleeds + /// How much this wound bleeds. Remember this is multiplied by delta_time var/bleed_rate = 0 /// Some wounds clot over time, reducing bleeding - This is the rate at which they do so var/clotting_rate = 0.01 diff --git a/code/datums/wounds/disembowel.dm b/code/datums/wounds/disembowel.dm index 9b6dc557fcd..538d315d5c8 100644 --- a/code/datums/wounds/disembowel.dm +++ b/code/datums/wounds/disembowel.dm @@ -6,7 +6,7 @@ sound_effect = 'sound/combat/crit2.ogg' whp = 100 sewn_whp = 35 - bleed_rate = 30 + bleed_rate = 15 sewn_bleed_rate = 0.8 clotting_rate = 0.02 sewn_clotting_rate = 0.02 diff --git a/code/datums/wounds/fractures.dm b/code/datums/wounds/fractures.dm index aa25703b146..ad48c54d216 100644 --- a/code/datums/wounds/fractures.dm +++ b/code/datums/wounds/fractures.dm @@ -20,7 +20,7 @@ // Limbs hemorrhage but clot quickly // Lose 164.3 blood over 19 ticks then clot - bleed_rate = 16.3 + bleed_rate = 4.1 clotting_threshold = 0.6 clotting_rate = 0.85 @@ -81,7 +81,7 @@ ) sound_effect = "headcrush" whp = 80 - bleed_rate = 3.2 + bleed_rate = 1.6 clotting_threshold = null mortal = TRUE @@ -128,7 +128,7 @@ "The cranium is shattered!", ) whp = 150 - bleed_rate = 4.6 + bleed_rate = 2.3 paralysis = TRUE knockout = 25 SECONDS min_damage_dividend = 0.95 @@ -198,7 +198,7 @@ ) whp = 50 - bleed_rate = 1.6 + bleed_rate = 0.8 clotting_threshold = 0.4 clotting_rate = 0.04 viable_zones = list(BODY_ZONE_PRECISE_MOUTH) @@ -258,7 +258,7 @@ ) whp = 50 // Lose 224.6 blood over 18 ticks then clot - bleed_rate = 23.1 + bleed_rate = 5.8 clotting_threshold = 0.8 clotting_rate = 1.25 viable_zones = list(BODY_ZONE_CHEST) @@ -278,7 +278,7 @@ ) whp = 50 gain_emote = "groin" - bleed_rate = 3.1 + bleed_rate = 1.6 clotting_threshold = 1.2 clotting_rate = 0.04 viable_zones = list(BODY_ZONE_PRECISE_GROIN) diff --git a/code/datums/wounds/simple_wounds/bites.dm b/code/datums/wounds/simple_wounds/bites.dm index f7ebd042631..86a63fb0c66 100644 --- a/code/datums/wounds/simple_wounds/bites.dm +++ b/code/datums/wounds/simple_wounds/bites.dm @@ -30,7 +30,7 @@ name = "gnarly bite" whp = 40 sewn_whp = 15 - bleed_rate = 2 + bleed_rate = 1 sewn_bleed_rate = 0.2 clotting_rate = 0.01 sewn_clotting_rate = 0.01 diff --git a/code/datums/wounds/simple_wounds/bruises.dm b/code/datums/wounds/simple_wounds/bruises.dm index 55048db4c0f..be3618fbbb6 100644 --- a/code/datums/wounds/simple_wounds/bruises.dm +++ b/code/datums/wounds/simple_wounds/bruises.dm @@ -32,7 +32,7 @@ /datum/wound/bruise/large name = "massive hematoma" whp = 40 - bleed_rate = 0.9 + bleed_rate = 0.45 clotting_rate = 0.02 clotting_threshold = 0.3 sew_threshold = 75 diff --git a/code/datums/wounds/simple_wounds/punctures.dm b/code/datums/wounds/simple_wounds/punctures.dm index 5db3471ae4f..615af2cbcc1 100644 --- a/code/datums/wounds/simple_wounds/punctures.dm +++ b/code/datums/wounds/simple_wounds/punctures.dm @@ -2,7 +2,7 @@ name = "puncture" whp = 40 sewn_whp = 20 - bleed_rate = 0.8 + bleed_rate = 0.4 sewn_bleed_rate = 0.04 clotting_rate = 0.01 sewn_clotting_rate = 0.01 @@ -26,7 +26,7 @@ name = "small puncture" whp = 20 sewn_whp = 10 - bleed_rate = 0.4 + bleed_rate = 0.2 sewn_bleed_rate = 0.02 clotting_rate = 0.01 sewn_clotting_rate = 0.01 @@ -40,7 +40,7 @@ name = "gaping puncture" whp = 40 sewn_whp = 20 - bleed_rate = 2 + bleed_rate = 1 sewn_bleed_rate = 0.1 clotting_rate = 0.01 sewn_clotting_rate = 0.01 @@ -56,7 +56,7 @@ severity = WOUND_SEVERITY_SUPERFICIAL whp = 40 sewn_whp = 20 - bleed_rate = 2 + bleed_rate = 1 sewn_bleed_rate = 0.1 clotting_rate = null clotting_threshold = null diff --git a/code/datums/wounds/simple_wounds/slashes.dm b/code/datums/wounds/simple_wounds/slashes.dm index e03cd6726dc..cf323b313d7 100644 --- a/code/datums/wounds/simple_wounds/slashes.dm +++ b/code/datums/wounds/simple_wounds/slashes.dm @@ -2,7 +2,7 @@ name = "slash" whp = 30 sewn_whp = 10 - bleed_rate = 0.8 + bleed_rate = 0.4 sewn_bleed_rate = 0.02 clotting_rate = 0.02 sewn_clotting_rate = 0.02 @@ -26,7 +26,7 @@ name = "small slash" whp = 15 sewn_whp = 5 - bleed_rate = 0.4 + bleed_rate = 0.2 sewn_bleed_rate = 0.01 clotting_rate = 0.02 sewn_clotting_rate = 0.02 @@ -40,7 +40,7 @@ name = "gruesome slash" whp = 40 sewn_whp = 12 - bleed_rate = 2 + bleed_rate = 1 sewn_bleed_rate = 0.05 clotting_rate = 0.02 sewn_clotting_rate = 0.02 @@ -54,7 +54,7 @@ name = "lashing" whp = 30 sewn_whp = 12 - bleed_rate = 0.6 + bleed_rate = 0.3 sewn_bleed_rate = 0.02 clotting_rate = 0.02 sewn_clotting_rate = 0.02 @@ -72,7 +72,7 @@ name = "superficial lashing" whp = 15 sewn_whp = 5 - bleed_rate = 0.2 + bleed_rate = 0.1 sewn_bleed_rate = 0.01 clotting_rate = 0.02 sewn_clotting_rate = 0.02 @@ -86,7 +86,7 @@ name = "excruciating lashing" whp = 45 sewn_whp = 15 - bleed_rate = 1.2 //Intended for combat, might kill if used for punishment. Force can be controlled by not charging the whip lash fully. + bleed_rate = 0.6 //Intended for combat, might kill if used for punishment. Force can be controlled by not charging the whip lash fully. sewn_bleed_rate = 0.05 clotting_rate = 0.02 sewn_clotting_rate = 0.02 diff --git a/code/datums/wounds/special.dm b/code/datums/wounds/special.dm index ace7bc822ef..96f229efa49 100644 --- a/code/datums/wounds/special.dm +++ b/code/datums/wounds/special.dm @@ -23,7 +23,7 @@ "The eardrums are ruptured!", ) woundpain = 50 - bleed_rate = 8 + bleed_rate = 4 can_cauterize = TRUE critical = TRUE associated_bclasses = STAB_BCLASSES @@ -59,7 +59,7 @@ "The eye is destroyed!", ) woundpain = 30 - bleed_rate = 8 + bleed_rate = 4 can_cauterize = FALSE critical = TRUE viable_zones = list(BODY_ZONE_PRECISE_R_EYE, BODY_ZONE_PRECISE_L_EYE) @@ -174,7 +174,7 @@ "The tongue flies off in an arc!" ) woundpain = 8 - bleed_rate = 5 + bleed_rate = 2.5 can_cauterize = FALSE critical = TRUE associated_bclasses = ARTERY_BCLASSES diff --git a/code/game/objects/items/natural/bandage.dm b/code/game/objects/items/natural/bandage.dm index 00fa69232df..a473ba477e4 100644 --- a/code/game/objects/items/natural/bandage.dm +++ b/code/game/objects/items/natural/bandage.dm @@ -5,7 +5,7 @@ desc = "A fabric treated and specially made to help with bleeding wounds. Better and faster at stopping bleeding than your regular piece of cloth." bundletype = /obj/item/natural/bundle/cloth/bandage bandage_effectiveness = 0.25 - bandage_health = 300 + bandage_health = 600 bandage_speed = 4 SECONDS volume = 18 item_weight = 18 GRAMS diff --git a/code/game/objects/items/natural/cloth.dm b/code/game/objects/items/natural/cloth.dm index f88d8fbab2b..7e971ad4531 100644 --- a/code/game/objects/items/natural/cloth.dm +++ b/code/game/objects/items/natural/cloth.dm @@ -27,7 +27,7 @@ ///how long it will take to bandage something with this var/bandage_speed = 7 SECONDS ///How much you can bleed into the bandage until it needs to be changed (Blood loss is measured in 50% of the health) - var/bandage_health = 150 + var/bandage_health = 300 obj_flags = CAN_BE_HIT //enables splashing on by containers /obj/item/natural/cloth/Initialize(mapload, vol) diff --git a/code/modules/surgery/organs/internal/artery/_artery.dm b/code/modules/surgery/organs/internal/artery/_artery.dm index bc916b33004..d257f139ed7 100644 --- a/code/modules/surgery/organs/internal/artery/_artery.dm +++ b/code/modules/surgery/organs/internal/artery/_artery.dm @@ -46,6 +46,10 @@ bleed_mod *= grab.bleed_suppressing if(limb.bandage) bleed_mod *= limb.bandage.bandage_effectiveness + if(ishuman(owner)) + var/mob/living/carbon/human/human_owner = owner + if(human_owner.physiology) + bleed_mod *= human_owner.physiology.bleed_mod switch(owner.pulse) if(PULSE_NONE) bleed_mod *= 0 diff --git a/code/modules/surgery/organs/organ_processing/heart.dm b/code/modules/surgery/organs/organ_processing/heart.dm index 32ef0752ad6..3facfce7006 100644 --- a/code/modules/surgery/organs/organ_processing/heart.dm +++ b/code/modules/surgery/organs/organ_processing/heart.dm @@ -157,7 +157,7 @@ var/temp_bleed = 0 var/bleed_mod = 1 for(var/obj/item/bodypart/bleed_part as anything in owner.bodyparts) - var/true_bleed = bleed_part.get_bleed_rate() * 0.5 * delta_time + var/true_bleed = bleed_part.get_bleed_rate() * delta_time switch(owner.pulse) if(PULSE_SLOW) true_bleed *= 0.8 From edbed4a67989575e276b7ce99aceaca8f6b260d3 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Mon, 11 May 2026 02:42:27 -0700 Subject: [PATCH 12/59] cleanup --- code/datums/wounds/arteries.dm | 1 - code/modules/surgery/organs/internal/artery/base_types.dm | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/code/datums/wounds/arteries.dm b/code/datums/wounds/arteries.dm index 03b4eaf7489..60f5a4ca459 100644 --- a/code/datums/wounds/arteries.dm +++ b/code/datums/wounds/arteries.dm @@ -61,7 +61,6 @@ artery_type_override = /obj/item/organ/artery/chest associated_bclasses = ARTERY_HEART_BCLASSES viable_zones = list(BODY_ZONE_CHEST) - mortal = TRUE /datum/wound/artery/heart/can_apply_to_bodypart(obj/item/bodypart/affected) if(affected.limb_flags & BODYPART_BONE_ENCASED && !affected.has_wound(/datum/wound/fracture)) diff --git a/code/modules/surgery/organs/internal/artery/base_types.dm b/code/modules/surgery/organs/internal/artery/base_types.dm index 81fcaf4e713..2c085370077 100644 --- a/code/modules/surgery/organs/internal/artery/base_types.dm +++ b/code/modules/surgery/organs/internal/artery/base_types.dm @@ -42,6 +42,11 @@ ) to_chat(owner, "[pick(heartaches)]") +/obj/item/organ/artery/chest/dissect() + . = ..() + if(HAS_TRAIT(owner, TRAIT_CRITICAL_WEAKNESS)) + owner.death() + /obj/item/organ/artery/neck name = "carotid artery" zone = BODY_ZONE_PRECISE_NECK From 095417af2f13386693a53f6c93cd7ebb9230e3e8 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Mon, 11 May 2026 02:53:34 -0700 Subject: [PATCH 13/59] artery delta_time --- code/__DEFINES/medical.dm | 3 --- code/datums/wounds/dismemberment.dm | 2 +- code/modules/surgery/organs/internal/artery/_artery.dm | 6 +++--- code/modules/surgery/organs/internal/artery/base_types.dm | 1 + 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/code/__DEFINES/medical.dm b/code/__DEFINES/medical.dm index cc58f7621ca..3120e9c9e5c 100644 --- a/code/__DEFINES/medical.dm +++ b/code/__DEFINES/medical.dm @@ -156,9 +156,6 @@ DEFINE_BITFIELD(organ_flags, list( #define LIMB_EFFICIENCY_OPTIMAL 100 #define LIMB_EFFICIENCY_DISABLING 50 -/// This is used as a reference point for dynamic wounds, so it's better off as a define. -#define ARTERY_LIMB_BLEEDRATE 12.5 - /// Multiplier applied to reagents in blood when factoring in total volume for "purity" #define BLOODLETTING_MULT 5 diff --git a/code/datums/wounds/dismemberment.dm b/code/datums/wounds/dismemberment.dm index 21cb346a612..b8de8d5d563 100644 --- a/code/datums/wounds/dismemberment.dm +++ b/code/datums/wounds/dismemberment.dm @@ -4,7 +4,7 @@ severity = WOUND_SEVERITY_CRITICAL whp = 75 sewn_whp = 25 - bleed_rate = ARTERY_LIMB_BLEEDRATE + bleed_rate = ARTERIAL_BLOOD_FLOW sewn_bleed_rate = 0.25 clotting_threshold = null sewn_clotting_threshold = null diff --git a/code/modules/surgery/organs/internal/artery/_artery.dm b/code/modules/surgery/organs/internal/artery/_artery.dm index d257f139ed7..74f6adec1ad 100644 --- a/code/modules/surgery/organs/internal/artery/_artery.dm +++ b/code/modules/surgery/organs/internal/artery/_artery.dm @@ -21,7 +21,7 @@ nutriment_req = 0.1 hydration_req = 0.05 - /// How much blood we gush when torn + /// How much blood we gush when torn. This will be multiplied by delta_time var/blood_flow = ARTERIAL_BLOOD_FLOW /// If torn, this is basically the time until we gush again COOLDOWN_DECLARE(next_squirt) @@ -59,8 +59,8 @@ bleed_mod *= 1.25 if(PULSE_FASTER, PULSE_THREADY) bleed_mod *= 1.5 - var/final_bleed_rate = CEILING(blood_flow * bleed_mod, 0.1) - if(!final_bleed_rate) + var/final_bleed_rate = CEILING(blood_flow * bleed_mod * delta_time, 0.1) + if(!final_bleed_rate <= 0) return if(COOLDOWN_FINISHED(src, next_squirt)) squirt(final_bleed_rate) diff --git a/code/modules/surgery/organs/internal/artery/base_types.dm b/code/modules/surgery/organs/internal/artery/base_types.dm index 2c085370077..c65d956cdbd 100644 --- a/code/modules/surgery/organs/internal/artery/base_types.dm +++ b/code/modules/surgery/organs/internal/artery/base_types.dm @@ -29,6 +29,7 @@ name = "thoracic aorta" desc = "Shot through the heart, and you're to blame - Darlin', you give love a bad name." zone = BODY_ZONE_CHEST + blood_flow = ARTERIAL_BLOOD_FLOW * 2.5 /obj/item/organ/artery/chest/tear() . = ..() From a69e6b94f7a16f8f820bf02c98421ac5f4c38890 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Mon, 11 May 2026 03:45:17 -0700 Subject: [PATCH 14/59] simplify --- code/modules/surgery/bodyparts/_bodyparts.dm | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index 4b07204855a..7261092cb7f 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -419,7 +419,7 @@ /// Proc for damaging organs inside a limb based on damage values -/obj/item/bodypart/proc/damage_internal_organs(wounding_type = WOUND_BLUNT, amount = 0, organ_bonus = 0, bare_organ_bonus = 0, forced = FALSE, wound_messages = TRUE) +/obj/item/bodypart/proc/damage_internal_organs(wounding_type, amount = 0, organ_bonus = 0, bare_organ_bonus = 0, forced = FALSE, wound_messages = TRUE) . = FALSE if(organ_bonus == CANT_ORGAN) return @@ -456,16 +456,12 @@ var/organ_damage_minimum = organ_damage_hit_minimum var/organ_damaged_required = organ_damage_requirement switch(wounding_type) - // Piercing damage is more likely to damage internal organs - if(WOUND_PIERCE) - organ_damage_minimum *= 0.5 - // Slashing damage is *slightly* more likely to damage internal organs - if(WOUND_SLASH) + // Penetrating and blunt injuries are more likely to damage internal organs + if(WOUND_PIERCE, WOUND_BLUNT) organ_damage_minimum *= 0.75 // Burn damage is unlikely to damage organs if(WOUND_BURN) organ_damage_minimum *= 1.5 - // Organ damage minimum is assumed to be the case for blunt anyway else organ_damage_hit_minimum *= 1 From ac45a21e7eeb1f1e96dcaeebe5e471af9d920121 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Mon, 11 May 2026 10:26:38 -0700 Subject: [PATCH 15/59] oxyloss fix --- code/modules/mob/living/carbon/blood.dm | 2 +- code/modules/mob/living/taste.dm | 43 +++++++++++++++---- code/modules/surgery/organs/internal/heart.dm | 2 +- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/code/modules/mob/living/carbon/blood.dm b/code/modules/mob/living/carbon/blood.dm index db267784928..56abcd356ea 100644 --- a/code/modules/mob/living/carbon/blood.dm +++ b/code/modules/mob/living/carbon/blood.dm @@ -65,7 +65,7 @@ else apparent_blood_volume = BLOOD_VOLUME_NORMAL - var/apparent_blood_volume_mod = max(0, 1 - (getOxyLoss() * 0.1) /max(maxHealth, 1)) + var/apparent_blood_volume_mod = max(0, 1 - getOxyLoss() / max(maxHealth, 1)) var/oxygenated = get_chem_effect(CE_OXYGENATED) if(oxygenated == 1) // Tirimol apparent_blood_volume_mod += 0.5 diff --git a/code/modules/mob/living/taste.dm b/code/modules/mob/living/taste.dm index 3c033627270..8a731cecbe1 100644 --- a/code/modules/mob/living/taste.dm +++ b/code/modules/mob/living/taste.dm @@ -14,16 +14,41 @@ else . = 101 // can't taste anything without a tongue +/** + * Check for anything blocking/overriding our tasting. + * Returns TRUE on a block, FALSE if not. + **/ +/mob/living/proc/check_tasting_blocks() + if(stat >= UNCONSCIOUS) + return TRUE + + if(last_taste_time + 5 SECONDS >= world.time) + return TRUE + + // Sometimes, try send a replacement message if we're hallucinating + /* + if(get_timed_status_effect_duration(/datum/status_effect/hallucination) > 100 SECONDS && prob(25)) + var/text_output = pick("spiders","dreams","nightmares","the future","the past","victory",\ + "defeat","pain","bliss","revenge","poison","time","space","death","life","truth","lies","justice","memory",\ + "regrets","your soul","suffering","music","noise","blood","hunger","the american way") + send_taste_message(text_output) + return TRUE + */ + + return FALSE + // non destructively tastes a reagent container /mob/living/proc/taste(datum/reagents/from) - if(last_taste_time + 50 < world.time) - var/taste_sensitivity = get_taste_sensitivity() - var/text_output = from.generate_taste_message(src, taste_sensitivity) - if(text_output != last_taste_text || last_taste_time + 100 < world.time) - to_chat(src, "I can taste [text_output].") - // "something indescribable" -> too many tastes, not enough flavor. - - last_taste_time = world.time - last_taste_text = text_output + if(check_tasting_blocks()) + return + + var/taste_sensitivity = get_taste_sensitivity() + var/text_output = from.generate_taste_message(src, taste_sensitivity) + if(text_output != last_taste_text || last_taste_time + 100 < world.time) + to_chat(src, "I can taste [text_output].") + // "something indescribable" -> too many tastes, not enough flavor. + + last_taste_time = world.time + last_taste_text = text_output #undef DEFAULT_TASTE_SENSITIVITY diff --git a/code/modules/surgery/organs/internal/heart.dm b/code/modules/surgery/organs/internal/heart.dm index 49d66b95040..1771ac5e6ea 100644 --- a/code/modules/surgery/organs/internal/heart.dm +++ b/code/modules/surgery/organs/internal/heart.dm @@ -137,7 +137,7 @@ if(heart.beating) deathsdoor = FALSE if(deathsdoor) - to_chat(owner, span_danger("I'm knocking on death's door!")) + to_chat(owner, span_danger("I'm knocking on Necra's door!")) return TRUE /obj/item/organ/heart/proc/Restart() From 2e34e75bb10b5199db472e7c00a9e4fc36583511 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Mon, 11 May 2026 11:39:48 -0700 Subject: [PATCH 16/59] more adjustments --- code/datums/injury/_injury.dm | 27 +++++++------------ code/modules/surgery/bodyparts/_bodyparts.dm | 2 +- .../surgery/bodyparts/bodypart_wounds.dm | 3 --- 3 files changed, 11 insertions(+), 21 deletions(-) diff --git a/code/datums/injury/_injury.dm b/code/datums/injury/_injury.dm index ddae1692b40..ce77d7dc428 100644 --- a/code/datums/injury/_injury.dm +++ b/code/datums/injury/_injury.dm @@ -375,7 +375,7 @@ var/obj/item/item = thing if(item.w_class >= WEIGHT_CLASS_SMALL) return FALSE - if(is_bandaged() || is_clamped() || is_sutured()) + if(is_bandaged() || is_sutured()) return FALSE if(required_status & BODYPART_ROBOTIC) return FALSE @@ -390,12 +390,13 @@ for(var/obj/item/item in embedded_objects) if((item.w_class < WEIGHT_CLASS_SMALL)) bad_embeddies += 1 - return max(0.1, (bleed_rate * (damage/BLEED_DAMAGE_RATIO)) + bad_embeddies) + var/bleed_modifier = damage/BLEED_DAMAGE_RATIO + if(is_clamped()) + bleed_modifier *= (BLEED_DAMAGE_RATIO/200) + return max(0.1, (bleed_rate * bleed_modifier) + bad_embeddies) /datum/injury/proc/is_surgical() - if(CHECK_BITFIELD(injury_flags, INJURY_SURGICAL)) - return TRUE - return FALSE + return CHECK_BITFIELD(injury_flags, INJURY_SURGICAL) /datum/injury/proc/is_disinfected() if(CHECK_BITFIELD(injury_flags, INJURY_DISINFECTED) && (germ_level <= 0)) @@ -403,23 +404,15 @@ return FALSE /datum/injury/proc/is_salved() - if(CHECK_BITFIELD(injury_flags, INJURY_SALVED)) - return TRUE - return FALSE + return CHECK_BITFIELD(injury_flags, INJURY_SALVED) /datum/injury/proc/is_clamped() - if(CHECK_BITFIELD(injury_flags, INJURY_CLAMPED)) - return TRUE - return FALSE + return CHECK_BITFIELD(injury_flags, INJURY_CLAMPED) /datum/injury/proc/is_sutured() - if(CHECK_BITFIELD(injury_flags, INJURY_SUTURED)) - return TRUE - return FALSE + return CHECK_BITFIELD(injury_flags, INJURY_SUTURED) /datum/injury/proc/is_bandaged() - if(CHECK_BITFIELD(injury_flags, INJURY_BANDAGED)) - return TRUE - return FALSE + return CHECK_BITFIELD(injury_flags, INJURY_BANDAGED) #undef BLEED_DAMAGE_RATIO diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index 7261092cb7f..fab5a563de6 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -563,7 +563,7 @@ if(!toxins && injury.can_autoheal()) heal_amt += (GET_MOB_ATTRIBUTE_VALUE(owner, STAT_ENDURANCE) * 0.01) if(owner?.IsSleeping()) - heal_amt *= 4 + heal_amt *= 2 if(heal_amt) injury.heal_damage(heal_amt * (0.5 * delta_time)) diff --git a/code/modules/surgery/bodyparts/bodypart_wounds.dm b/code/modules/surgery/bodyparts/bodypart_wounds.dm index 55f68860fb1..127a51f0527 100644 --- a/code/modules/surgery/bodyparts/bodypart_wounds.dm +++ b/code/modules/surgery/bodyparts/bodypart_wounds.dm @@ -139,9 +139,6 @@ for(var/obj/item/grabbing/grab in grabbedby) bleed_rate *= grab.bleed_suppressing bleed_rate = max(round(bleed_rate, 0.1), 0) - var/surgery_flags = get_surgery_flags() - if(surgery_flags & SURGERY_CLAMPED) - bleed_rate = min(bleed_rate, 0.5) return bleed_rate /obj/item/bodypart/proc/skeletonized_mod(bclass) From b3c7cf6af0937610ad5aa53320ea62466bfb9ec9 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Mon, 11 May 2026 11:50:26 -0700 Subject: [PATCH 17/59] nutrition_ratio --- .../surgery/organs/organ_processing/spleen.dm | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/code/modules/surgery/organs/organ_processing/spleen.dm b/code/modules/surgery/organs/organ_processing/spleen.dm index f4ed1f1bd58..ac4db30dda2 100644 --- a/code/modules/surgery/organs/organ_processing/spleen.dm +++ b/code/modules/surgery/organs/organ_processing/spleen.dm @@ -8,18 +8,9 @@ /datum/organ_process/spleen/handle_process(mob/living/carbon/owner, delta_time, times_fired) if(owner.get_blood_volume() >= BLOOD_VOLUME_NORMAL) return - var/nutrition_ratio = 0 - switch(owner.nutrition) - if(0 to NUTRITION_LEVEL_STARVING) - nutrition_ratio = 0.4 - if(NUTRITION_LEVEL_STARVING to NUTRITION_LEVEL_HUNGRY) - nutrition_ratio = 0.6 - if(NUTRITION_LEVEL_HUNGRY to NUTRITION_LEVEL_FED) - nutrition_ratio = 0.8 - if(NUTRITION_LEVEL_FED to NUTRITION_LEVEL_WELL_FED) - nutrition_ratio = 1 - else - nutrition_ratio = 1 + + var/nutrition_ratio = clamp(round(owner.nutrition / NUTRITION_LEVEL_WELL_FED, 0.2), 0.4, 1) + if(owner.satiety > 80) nutrition_ratio *= 1.25 From 89177cdf08a48867ed5b6f191675f99e3ab76aa8 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Mon, 11 May 2026 12:28:18 -0700 Subject: [PATCH 18/59] splits lifeblood blood --- code/modules/crafting/alchemy/reagents.dm | 8 ++++++-- .../modules/reagents/chemistry/reagents/other_reagents.dm | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/code/modules/crafting/alchemy/reagents.dm b/code/modules/crafting/alchemy/reagents.dm index eb6741d8c90..b78b6e23bdb 100644 --- a/code/modules/crafting/alchemy/reagents.dm +++ b/code/modules/crafting/alchemy/reagents.dm @@ -8,6 +8,7 @@ scent_description = "metal" metabolization_rate = REAGENTS_METABOLISM alpha = 173 + liver_chemical = FALSE /datum/reagent/medicine/healthpot/on_bodypart_absorb(obj/item/bodypart/bodypart, mob/living/carbon/M, amount_to_transfer) for(var/datum/injury/injury in bodypart.injuries) @@ -17,7 +18,7 @@ /datum/reagent/medicine/healthpot/on_mob_metabolize(mob/living/L) . = ..() - L.add_chem_effect(CE_BLOODRESTORE, 5, "[type]") + L.add_chem_effect(CE_BLOODRESTORE, 3, "[type]") L.add_chem_effect(CE_STABLE, 1, "[type]") /datum/reagent/medicine/healthpot/on_mob_end_metabolize(mob/living/L) @@ -28,6 +29,7 @@ /datum/reagent/medicine/healthpot/on_mob_life(mob/living/carbon/M, efficiency) if(volume >= 60) M.remove_reagent(/datum/reagent/medicine/healthpot, 2) //No overhealing. + M.adjust_blood_volume(BLOOD_REGEN_FACTOR * 200 * efficiency * 3, maximum = BLOOD_VOLUME_NORMAL) var/list/wCount = M.get_wounds() if(wCount.len > 0) M.heal_wounds(3 * efficiency) //at a motabalism of .5 U a tick this translates to 120WHP healing with 20 U Most wounds are unsewn 15-100. This is powerful on single wounds but rapidly weakens at multi wounds. @@ -49,6 +51,7 @@ taste_description = "rich lifeblood" scent_description = "metal" metabolization_rate = REAGENTS_METABOLISM * 2 + liver_chemical = FALSE /datum/reagent/medicine/healthpot/on_bodypart_absorb(obj/item/bodypart/bodypart, mob/living/carbon/M, amount_to_transfer) for(var/datum/injury/injury in bodypart.injuries) @@ -60,7 +63,7 @@ /datum/reagent/medicine/stronghealth/on_mob_metabolize(mob/living/L) . = ..() - L.add_chem_effect(CE_BLOODRESTORE, 30, "[type]") + L.add_chem_effect(CE_BLOODRESTORE, 5, "[type]") L.add_chem_effect(CE_STABLE, 1, "[type]") L.add_chem_effect(CE_BRAIN_REGEN, 1, "[type]") @@ -73,6 +76,7 @@ /datum/reagent/medicine/stronghealth/on_mob_life(mob/living/carbon/M, efficiency) if(volume >= 60) M.remove_reagent(/datum/reagent/medicine/stronghealth, 2) //No overhealing. + M.adjust_blood_volume(BLOOD_REGEN_FACTOR * 200 * efficiency * 5, maximum = BLOOD_VOLUME_NORMAL) M.heal_wounds(6 * efficiency) //at a motabalism of .5 U a tick this translates to 240WHP healing with 20 U Most wounds are unsewn 15-100. for(var/datum/injury/injury in M.all_injuries) if(!injury.can_heal()) diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index 90e58173540..0ffba9719fe 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -140,7 +140,7 @@ /datum/reagent/water/on_mob_metabolize(mob/living/L) . = ..() - L.add_chem_effect(CE_BLOODRESTORE, 0.1, "[type]") + L.add_chem_effect(CE_BLOODRESTORE, 1, "[type]") /datum/reagent/water/on_mob_end_metabolize(mob/living/L) . = ..() From 30ae89ac9cf90d2864438484b4f039500320c3ee Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Mon, 11 May 2026 15:09:17 -0700 Subject: [PATCH 19/59] fracture bleeds and break ribs --- code/datums/wounds/fractures.dm | 6 +++--- code/modules/mob/living/carbon/human/human.dm | 12 +++++++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/code/datums/wounds/fractures.dm b/code/datums/wounds/fractures.dm index ad48c54d216..881f48e65c6 100644 --- a/code/datums/wounds/fractures.dm +++ b/code/datums/wounds/fractures.dm @@ -20,7 +20,7 @@ // Limbs hemorrhage but clot quickly // Lose 164.3 blood over 19 ticks then clot - bleed_rate = 4.1 + bleed_rate = 2.3 clotting_threshold = 0.6 clotting_rate = 0.85 @@ -128,7 +128,7 @@ "The cranium is shattered!", ) whp = 150 - bleed_rate = 2.3 + bleed_rate = 5.8 paralysis = TRUE knockout = 25 SECONDS min_damage_dividend = 0.95 @@ -258,7 +258,7 @@ ) whp = 50 // Lose 224.6 blood over 18 ticks then clot - bleed_rate = 5.8 + bleed_rate = 1.6 clotting_threshold = 0.8 clotting_rate = 1.25 viable_zones = list(BODY_ZONE_CHEST) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index c16d2c062c9..521fc88450f 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -425,7 +425,7 @@ if(GETBRAINLOSS(target) >= 100) SETBRAINLOSS(target, 99) if(HAS_TRAIT(target, TRAIT_NECRA_CURSE)) - to_chat(target, span_warning("Necra holds tight to this one.")) + to_chat(src, span_warning("Necra holds tight to this one.")) return FALSE if(diceroll >= DICE_CRIT_SUCCESS) if(target.revive()) @@ -437,14 +437,16 @@ add_abstract_elastic_data(ELASCAT_MEDICAL, ELASDATA_CPR_REVIVE, 1) target.apply_status_effect(/datum/status_effect/debuff/revive) target.remove_client_colour(/datum/client_colour/monochrome/death) + chest.add_wound(/datum/wound/fracture/chest) record_round_statistic(STATS_CPR_REVIVALS, 1) + else + to_chat(src, span_warning("[target] isn't responding to my resuscitation...")) else if(diceroll <= DICE_CRIT_FAILURE) - visible_message(span_danger("[src] botches the chest compressions, cracking [target]'s ribs!"), \ - span_danger("I botch the chest compressions, cracking [target]'s ribs!"), - span_hear("I hear a loud crack!"), + visible_message(span_danger("[src] botches the chest compressions!"), \ + span_danger("I botch the chest compressions!"), + span_hear("I hear frantic pressing!"), ignored_mobs = target) - to_chat(target, span_userdanger("[src] botches the chest compressions and cracks my ribs!")) /mob/living/carbon/human/cuff_resist(obj/item/I, breakouttime = 1 MINUTES, cuff_break = 0, instant = FALSE) if(..()) From 3f0863f267fed09faa840543258ee96f48376258 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Mon, 11 May 2026 18:04:33 -0700 Subject: [PATCH 20/59] continue fixing safety check --- code/datums/injury/_injury.dm | 8 +- code/game/objects/items/needle.dm | 3 +- .../covens/coven_powers/bloodheal.dm | 1 + .../spells/regeneration_cycle.dm | 1 + .../crafting/alchemy/herbal_recipes.dm | 11 +- code/modules/crafting/alchemy/reagents.dm | 10 +- .../mob/living/carbon/carbon_defense.dm | 120 ------------------ .../mob/living/carbon/carbon_medical.dm | 107 ++++++++++++++++ .../modules/mob/living/carbon/damage_procs.dm | 86 ++++++------- code/modules/mob/living/carbon/human/human.dm | 4 +- code/modules/mob/living/carbon/life.dm | 8 +- .../chemistry/reagents/medicine_reagents.dm | 36 ++++-- .../spells/spell_types/pointed/healing.dm | 15 +-- .../undirected/bardic/rejuvenation_song.dm | 5 +- code/modules/surgery/bodyparts/_bodyparts.dm | 49 +++---- .../surgery/organs/organ_processing/spleen.dm | 8 +- code/modules/surgery/surgeries/healing.dm | 1 + vanderlin.dme | 1 + 18 files changed, 231 insertions(+), 243 deletions(-) create mode 100644 code/modules/mob/living/carbon/carbon_medical.dm diff --git a/code/datums/injury/_injury.dm b/code/datums/injury/_injury.dm index ce77d7dc428..46dc419b664 100644 --- a/code/datums/injury/_injury.dm +++ b/code/datums/injury/_injury.dm @@ -296,8 +296,8 @@ if(damage > min_damage) heal_damage(damage-min_damage) injury_flags &= ~INJURY_RETRACTED - parent_bodypart?.update_bodypart_damage_state() - parent_bodypart?.owner?.update_damage_overlays() + if(parent_bodypart?.post_damage_change()) + parent_bodypart?.owner?.update_damage_overlays() // opens the injury and worsens it /datum/injury/proc/open_injury(damage, retracting = FALSE) @@ -313,8 +313,8 @@ injury_flags |= INJURY_RETRACTED if(parent_bodypart) parent_bodypart.last_injury = src - parent_bodypart.update_bodypart_damage_state() - parent_bodypart.owner?.update_damage_overlays() + if(parent_bodypart?.post_damage_change()) + parent_bodypart?.owner?.update_damage_overlays() // disinfects the injury /datum/injury/proc/disinfect_injury() diff --git a/code/game/objects/items/needle.dm b/code/game/objects/items/needle.dm index fcca3e72123..1968e482c04 100644 --- a/code/game/objects/items/needle.dm +++ b/code/game/objects/items/needle.dm @@ -255,8 +255,7 @@ injury.heal_damage(10) var/amt2raise = GET_MOB_ATTRIBUTE_VALUE(doctor, STAT_INTELLIGENCE) user.adjust_experience(/datum/attribute/skill/misc/medicine, amt2raise * doctor.get_learning_boon(/datum/attribute/skill/misc/medicine)) - affecting.update_damages() - if(affecting.update_bodypart_damage_state()) + if(affecting.post_damage_change()) target.update_damage_overlays() if(injury.damage_per_injury() > injury.autoheal_cutoff) user.visible_message(span_green("[user] partially stitches \a [injury.get_desc()] on [target]'s [affecting.name] with \the [src]."), \ diff --git a/code/modules/antagonists/villain/neu_vampires/covens/coven_powers/bloodheal.dm b/code/modules/antagonists/villain/neu_vampires/covens/coven_powers/bloodheal.dm index 23a3b66b6b4..ca4253a8ced 100644 --- a/code/modules/antagonists/villain/neu_vampires/covens/coven_powers/bloodheal.dm +++ b/code/modules/antagonists/villain/neu_vampires/covens/coven_powers/bloodheal.dm @@ -62,6 +62,7 @@ else bashing_lethal_heal = injury.heal_damage(bashing_lethal_heal) + owner.update_all_limb_states() owner.adjust_blood_volume(vitae_cost, maximum = BLOOD_VOLUME_NORMAL) //this is quadratic so expect it to scale like crazy diff --git a/code/modules/crafting/alchemy/essence_machines/essence_gauntlet/spells/regeneration_cycle.dm b/code/modules/crafting/alchemy/essence_machines/essence_gauntlet/spells/regeneration_cycle.dm index 6add52755f6..d192517e5a1 100644 --- a/code/modules/crafting/alchemy/essence_machines/essence_gauntlet/spells/regeneration_cycle.dm +++ b/code/modules/crafting/alchemy/essence_machines/essence_gauntlet/spells/regeneration_cycle.dm @@ -35,3 +35,4 @@ if(!injury.can_heal()) continue injury.heal_damage(0.1) + carbon.update_all_limb_states() diff --git a/code/modules/crafting/alchemy/herbal_recipes.dm b/code/modules/crafting/alchemy/herbal_recipes.dm index 3ae25d59563..d9be12fe0cf 100644 --- a/code/modules/crafting/alchemy/herbal_recipes.dm +++ b/code/modules/crafting/alchemy/herbal_recipes.dm @@ -75,7 +75,7 @@ taste_description = "bitter flowers" scent_description = "marigold" -/datum/reagent/medicine/herbal/calendula_salve/on_bodypart_absorb(obj/item/bodypart/bodypart, mob/living/carbon/M, amount_to_transfer) +/datum/reagent/medicine/herbal/calendula_salve/on_bodypart_absorb(obj/item/bodypart/bodypart, mob/living/carbon/carbon_mob, amount_to_transfer) for(var/datum/injury/injury in bodypart.injuries) if(!injury.can_heal()) continue @@ -85,6 +85,8 @@ injury.heal_damage(3) injury.adjust_germ_level(-5) bodypart.disinfect_limb(20 SECONDS) + if(bodypart.post_damage_change()) + carbon_mob.update_damage_overlays() // Weak Mana/Stamina Potions (based on hypericum/benedictus/mentha) /datum/reagent/medicine/herbal/hypericum_tonic @@ -396,7 +398,7 @@ overdose_threshold = 30 taste_description = "bitter numbness" -/datum/reagent/medicine/herbal/paris_poultice/on_bodypart_absorb(obj/item/bodypart/bodypart, mob/living/carbon/M, amount_to_transfer) +/datum/reagent/medicine/herbal/paris_poultice/on_bodypart_absorb(obj/item/bodypart/bodypart, mob/living/carbon/carbon_mob, amount_to_transfer) for(var/datum/injury/injury in bodypart.injuries) if(!injury.can_heal()) continue @@ -405,6 +407,8 @@ if(injury.damage_type != WOUND_BURN) injury.heal_damage(0.75) bodypart.add_pain(-amount_to_transfer * 0.3) + if(bodypart.post_damage_change()) + carbon_mob.update_damage_overlays() /datum/reagent/medicine/herbal/paris_poultice/overdose_process(mob/living/M) M.adjustToxLoss(0.5) @@ -450,8 +454,7 @@ break total_healing = injury.heal_damage(total_healing) - if(prob(15)) - M.heal_bodypart_damage(1, 1, 0) + M.update_all_limb_states() . = ..() // Anti-Poison Blend diff --git a/code/modules/crafting/alchemy/reagents.dm b/code/modules/crafting/alchemy/reagents.dm index b78b6e23bdb..ce6729d502f 100644 --- a/code/modules/crafting/alchemy/reagents.dm +++ b/code/modules/crafting/alchemy/reagents.dm @@ -10,11 +10,13 @@ alpha = 173 liver_chemical = FALSE -/datum/reagent/medicine/healthpot/on_bodypart_absorb(obj/item/bodypart/bodypart, mob/living/carbon/M, amount_to_transfer) +/datum/reagent/medicine/healthpot/on_bodypart_absorb(obj/item/bodypart/bodypart, mob/living/carbon/carbon_mob, amount_to_transfer) for(var/datum/injury/injury in bodypart.injuries) if(!injury.can_heal()) continue injury.heal_damage(1) + if(bodypart.post_damage_change()) + carbon_mob.update_damage_overlays() /datum/reagent/medicine/healthpot/on_mob_metabolize(mob/living/L) . = ..() @@ -37,6 +39,7 @@ if(!injury.can_heal()) continue injury.heal_damage(1/10 * efficiency) + M.update_all_limb_states() if(volume > 0.99) M.adjustBruteLoss(-1.75*REM * efficiency, 0) M.adjustFireLoss(-1.75*REM * efficiency, 0) @@ -53,13 +56,15 @@ metabolization_rate = REAGENTS_METABOLISM * 2 liver_chemical = FALSE -/datum/reagent/medicine/healthpot/on_bodypart_absorb(obj/item/bodypart/bodypart, mob/living/carbon/M, amount_to_transfer) +/datum/reagent/medicine/healthpot/on_bodypart_absorb(obj/item/bodypart/bodypart, mob/living/carbon/carbon_mob, amount_to_transfer) for(var/datum/injury/injury in bodypart.injuries) if(!injury.can_heal()) continue injury.heal_damage(2) for(var/datum/wound/wound in bodypart.wounds) wound.heal_wound(2) + if(bodypart.post_damage_change()) + carbon_mob.update_damage_overlays() /datum/reagent/medicine/stronghealth/on_mob_metabolize(mob/living/L) . = ..() @@ -82,6 +87,7 @@ if(!injury.can_heal()) continue injury.heal_damage(2/10 * efficiency) + M.update_all_limb_states() if(volume > 0.99) M.adjustBruteLoss(-7*REM * efficiency, 0) M.adjustFireLoss(-7*REM * efficiency, 0) diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 8915b0df748..79b1f880b51 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -552,123 +552,3 @@ var/obj/item/organ/ears/ears = getorganslot(ORGAN_SLOT_EARS) if((istype(ears) && !ears.deaf) || (src.stat == DEAD)) // 2nd check so you can hear messages when beheaded . = TRUE - -/mob/living/carbon/adjustOxyLoss(amount, updating_health = TRUE, forced = FALSE) - . = ..() - if(isnull(.)) - return - if(. <= 75) - if(getOxyLoss() > 75) - ADD_TRAIT(src, TRAIT_KNOCKEDOUT, OXYLOSS_TRAIT) - else if(getOxyLoss() <= 75) - REMOVE_TRAIT(src, TRAIT_KNOCKEDOUT, OXYLOSS_TRAIT) - -/mob/living/carbon/setOxyLoss(amount, updating_health = TRUE, forced = FALSE) - . = ..() - if(isnull(.)) - return - if(. <= 75) - if(getOxyLoss() > 75) - ADD_TRAIT(src, TRAIT_KNOCKEDOUT, OXYLOSS_TRAIT) - else if(getOxyLoss() <= 75) - REMOVE_TRAIT(src, TRAIT_KNOCKEDOUT, OXYLOSS_TRAIT) - - -/mob/living/carbon/proc/pump_heart(mob/user, forced_pump) - if(!forced_pump) - var/heymedic = GET_MOB_SKILL_VALUE(user, /datum/attribute/skill/misc/medicine)/SKILL_MASTER - recent_heart_pump = list("[world.time]" = (0.3 + CEILING(heymedic, 0.1))) - else - recent_heart_pump = list("[world.time]" = (0.3 + CEILING(forced_pump, 0.1))) - var/obj/item/organ/heart/heart = getorganslot(ORGAN_SLOT_HEART) - if(!heart.current_blood) - heart.current_blood = heart.max_blood_storage - return TRUE - -/mob/living/carbon/proc/check_pulse(mob/living/carbon/user) - . = TRUE - var/self = FALSE - if(user == src) - self = TRUE - - var/obj/item/bodypart/pulsating_part = get_bodypart(check_zone(user.zone_selected)) - if(!pulsating_part) - to_chat(user, span_warning("I cannot measure [self ? "my" : p_their()] pulse without \a [parse_zone(user.zone_selected)].")) - return - if(DOING_INTERACTION_WITH_TARGET(user, src)) - to_chat(user, span_warning("I'm unable to check [self ? "my" : "[src]'s"] pulse.[user] puts \his hand on [src]'s wrist and begins counting their pulse."),\ - span_notice("I begin counting [src]'s pulse...")) - else - user.visible_message(span_notice("[user] begins counting their own pulse."),\ - span_notice("I begin counting my pulse...")) - - - if(!do_after(user, 0.5 SECONDS, src)) - to_chat(user, span_warning("I failed to check [self ? "my" : "[src]'s"] pulse.")) - return - - if(pulse) - to_chat(user, span_notice("[self ? "I have a" : "[src] has a"] pulse! Counting...")) - else - to_chat(user, span_danger("[self ? "I have no" : "[src] has no"] pulse!")) - return - - if(do_after(user, 2.5 SECONDS, src)) - to_chat(user, span_notice("[self ? "My" : "[src]'s"] pulse is approximately [src.get_pulse(GETPULSE_BASIC)] BPM.")) - else - to_chat(user, span_warning("I failed to check [self ? "my" : "[src]'s"] pulse.")) - - -/// A pulse to be read by players -/mob/living/carbon/proc/get_pulse_as_number(raw_pulse = pulse) - switch(raw_pulse) - if(PULSE_NONE) - return 0 - if(PULSE_SLOW) - return rand(40, 60) - if(PULSE_NORM) - return rand(60, 90) - if(PULSE_FAST) - return rand(90, 120) - if(PULSE_FASTER) - return rand(120, 160) - if(PULSE_THREADY) - return PULSE_MAX_BPM - CRASH("For some reason, on a get_pulse_as_number() call, someone's pulse is not a valid integer!") - -/// Generates realistic-ish pulse output based on preset levels as text -/mob/living/carbon/proc/get_pulse(method) //method 0 is for hands, 1 is for machines, more accurate - if(method == GETPULSE_PERFECT) - return pulse - - var/list/hearts = getorganslotlist(ORGAN_SLOT_HEART) - if(!length(hearts)) - // No heart, no pulse - return "0" - - var/bypassed_heart = FALSE - for(var/thing in hearts) - var/obj/item/organ/heart/heart = thing - if(heart.open) - bypassed_heart = TRUE - - if(bypassed_heart && (method <= GETPULSE_BASIC)) - // Heart is a open type (?) and cannot be checked unless it's a machine - return "muddled and unclear" - - var/bpm = get_pulse_as_number() - if(bpm >= PULSE_MAX_BPM) - if(method == GETPULSE_ADVANCED) - return ">[PULSE_MAX_BPM]" - else - return "extremely weak and fast" - - if(method == GETPULSE_ADVANCED) - return "[bpm]" - else - return "[bpm > 0 ? max(0, bpm + rand(-10, 10)) : 0]" diff --git a/code/modules/mob/living/carbon/carbon_medical.dm b/code/modules/mob/living/carbon/carbon_medical.dm new file mode 100644 index 00000000000..13e168b28e6 --- /dev/null +++ b/code/modules/mob/living/carbon/carbon_medical.dm @@ -0,0 +1,107 @@ + +/mob/living/carbon/proc/pump_heart(mob/user, forced_pump) + if(!forced_pump) + var/heymedic = GET_MOB_SKILL_VALUE(user, /datum/attribute/skill/misc/medicine)/SKILL_MASTER + recent_heart_pump = list("[world.time]" = (0.3 + CEILING(heymedic, 0.1))) + else + recent_heart_pump = list("[world.time]" = (0.3 + CEILING(forced_pump, 0.1))) + var/obj/item/organ/heart/heart = getorganslot(ORGAN_SLOT_HEART) + if(!heart.current_blood) + heart.current_blood = heart.max_blood_storage + return TRUE + +/mob/living/carbon/proc/check_pulse(mob/living/carbon/user) + . = TRUE + var/self = FALSE + if(user == src) + self = TRUE + + var/obj/item/bodypart/pulsating_part = get_bodypart(check_zone(user.zone_selected)) + if(!pulsating_part) + to_chat(user, span_warning("I cannot measure [self ? "my" : p_their()] pulse without \a [parse_zone(user.zone_selected)].")) + return + if(DOING_INTERACTION_WITH_TARGET(user, src)) + to_chat(user, span_warning("I'm unable to check [self ? "my" : "[src]'s"] pulse.[user] puts \his hand on [src]'s wrist and begins counting their pulse."),\ + span_notice("I begin counting [src]'s pulse...")) + else + user.visible_message(span_notice("[user] begins counting their own pulse."),\ + span_notice("I begin counting my pulse...")) + + + if(!do_after(user, 0.5 SECONDS, src)) + to_chat(user, span_warning("I failed to check [self ? "my" : "[src]'s"] pulse.")) + return + + if(pulse) + to_chat(user, span_notice("[self ? "I have a" : "[src] has a"] pulse! Counting...")) + else + to_chat(user, span_danger("[self ? "I have no" : "[src] has no"] pulse!")) + return + + if(do_after(user, 2.5 SECONDS, src)) + to_chat(user, span_notice("[self ? "My" : "[src]'s"] pulse is approximately [src.get_pulse(GETPULSE_BASIC)] BPM.")) + else + to_chat(user, span_warning("I failed to check [self ? "my" : "[src]'s"] pulse.")) + + +/// A pulse to be read by players +/mob/living/carbon/proc/get_pulse_as_number(raw_pulse = pulse) + switch(raw_pulse) + if(PULSE_NONE) + return 0 + if(PULSE_SLOW) + return rand(40, 60) + if(PULSE_NORM) + return rand(60, 90) + if(PULSE_FAST) + return rand(90, 120) + if(PULSE_FASTER) + return rand(120, 160) + if(PULSE_THREADY) + return PULSE_MAX_BPM + CRASH("For some reason, on a get_pulse_as_number() call, someone's pulse is not a valid integer!") + +/// Generates realistic-ish pulse output based on preset levels as text +/mob/living/carbon/proc/get_pulse(method) //method 0 is for hands, 1 is for machines, more accurate + if(method == GETPULSE_PERFECT) + return pulse + + var/list/hearts = getorganslotlist(ORGAN_SLOT_HEART) + if(!length(hearts)) + // No heart, no pulse + return "0" + + var/bypassed_heart = FALSE + for(var/thing in hearts) + var/obj/item/organ/heart/heart = thing + if(heart.open) + bypassed_heart = TRUE + + if(bypassed_heart && (method <= GETPULSE_BASIC)) + // Heart is a open type (?) and cannot be checked unless it's a machine + return "muddled and unclear" + + var/bpm = get_pulse_as_number() + if(bpm >= PULSE_MAX_BPM) + if(method == GETPULSE_ADVANCED) + return ">[PULSE_MAX_BPM]" + else + return "extremely weak and fast" + + if(method == GETPULSE_ADVANCED) + return "[bpm]" + else + return "[bpm > 0 ? max(0, bpm + rand(-10, 10)) : 0]" + +/// USE THIS SPARINGLY, FOR EXAMPLE IF YOU'RE HEALING ALL INJURIES +/mob/living/carbon/proc/update_all_limb_states() + . = FALSE + for(var/obj/item/bodypart/bodypart as anything in bodyparts) + . |= bodypart.post_damage_change() + if(.) + update_damage_overlays() diff --git a/code/modules/mob/living/carbon/damage_procs.dm b/code/modules/mob/living/carbon/damage_procs.dm index 1fbe0efa2d2..fe077910c38 100644 --- a/code/modules/mob/living/carbon/damage_procs.dm +++ b/code/modules/mob/living/carbon/damage_procs.dm @@ -137,6 +137,26 @@ amount = min(amount, 0) return ..() +/mob/living/carbon/adjustOxyLoss(amount, updating_health = TRUE, forced = FALSE) + . = ..() + if(isnull(.)) + return + if(. <= 75) + if(getOxyLoss() > 75) + ADD_TRAIT(src, TRAIT_KNOCKEDOUT, OXYLOSS_TRAIT) + else if(getOxyLoss() <= 75) + REMOVE_TRAIT(src, TRAIT_KNOCKEDOUT, OXYLOSS_TRAIT) + +/mob/living/carbon/setOxyLoss(amount, updating_health = TRUE, forced = FALSE) + . = ..() + if(isnull(.)) + return + if(. <= 75) + if(getOxyLoss() > 75) + ADD_TRAIT(src, TRAIT_KNOCKEDOUT, OXYLOSS_TRAIT) + else if(getOxyLoss() <= 75) + REMOVE_TRAIT(src, TRAIT_KNOCKEDOUT, OXYLOSS_TRAIT) + /** adjustOrganLoss * inputs: slot (organ slot, like ORGAN_SLOT_HEART), amount (damage to be done), and maximum (currently an arbitrarily large number, can be set so as to limit damage) @@ -171,41 +191,29 @@ /mob/living/carbon/getPainLoss() var/amount = 0 - for(var/X in bodyparts) - var/obj/item/bodypart/bodypart = X + for(var/obj/item/bodypart/bodypart as anything in bodyparts) amount += bodypart.pain_dam * bodypart.pain_damage_coeff return amount /mob/living/carbon/adjustPainLoss(amount, updating_health = TRUE, forced = FALSE, required_status = null) if(!forced && (status_flags & GODMODE)) - return FALSE + return 0 var/old_amount = amount - var/list/obj/item/bodypart/parts - if(amount > 0) - parts = get_painable_bodyparts() - else - parts = get_pained_bodyparts() + var/list/obj/item/bodypart/parts = get_painable_bodyparts(adding_pain = (amount > 0 ? TRUE : FALSE)) + if(!parts) + return 0 var/update = FALSE - while(parts.len && amount) - var/obj/item/bodypart/picked = pick(parts) - var/pain_per_part - if(amount < 0) - pain_per_part = FLOOR(amount/parts.len, DAMAGE_PRECISION) - else - pain_per_part = CEILING(amount/parts.len, DAMAGE_PRECISION) - - var/pain_was = picked.pain_dam - if(amount < 0) + var/pain_per_part = amount / length(parts) + if(pain_per_part < 0) + pain_per_part = FLOOR(pain_per_part, DAMAGE_PRECISION) + else + pain_per_part = CEILING(pain_per_part, DAMAGE_PRECISION) + while(length(parts)) + var/obj/item/bodypart/picked = pick_n_take(parts) + if(pain_per_part < 0) update |= picked.remove_pain(abs(pain_per_part)) else update |= picked.add_pain(abs(pain_per_part)) - - if(pain_per_part < 0) - pain_per_part = FLOOR(amount - (picked.pain_dam - pain_was), DAMAGE_PRECISION) - else - pain_per_part = CEILING(amount - (picked.pain_dam - pain_was), DAMAGE_PRECISION) - - parts -= picked if(updating_health) updatehealth() if(update) @@ -225,24 +233,17 @@ /mob/living/carbon/proc/InFullShock() return (shock_stage >= SHOCK_STAGE_6) -/mob/living/carbon/proc/get_painable_bodyparts(status) +/mob/living/carbon/proc/get_painable_bodyparts(status, adding_pain) var/list/obj/item/bodypart/parts = list() - for(var/X in bodyparts) - var/obj/item/bodypart/BP = X - if(status && (BP.status != status)) + for(var/obj/item/bodypart/bodypart as anything in bodyparts) + if(status && (bodypart.status != status)) continue - if(BP.pain_dam < BP.max_pain_damage) - parts += BP - return parts - -/mob/living/carbon/proc/get_pained_bodyparts(status) - var/list/obj/item/bodypart/parts = list() - for(var/X in bodyparts) - var/obj/item/bodypart/BP = X - if(status && (BP.status != status)) - continue - if(BP.pain_dam) - parts += BP + if(adding_pain) + if(bodypart.pain_dam < bodypart.max_pain_damage) + parts += bodypart + else + if(bodypart.pain_dam) + parts += bodypart return parts /mob/living/carbon/proc/endorphinate(forced = FALSE, silent = FALSE, local_sound = TRUE, flash = TRUE, special_sound) @@ -321,8 +322,7 @@ var/shock = 0 shock += SHOCK_MOD_CLONE * getCloneLoss() - for(var/X in bodyparts) - var/obj/item/bodypart/bodypart = X + for(var/obj/item/bodypart/bodypart as anything in bodyparts) shock += bodypart.get_shock(FALSE, TRUE) if(painkiller_included) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 521fc88450f..101ffece3a6 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -422,8 +422,8 @@ if(prob(35) || (diceroll >= DICE_SUCCESS)) target?.pump_heart(src) target.set_heartattack(FALSE) - if(GETBRAINLOSS(target) >= 100) - SETBRAINLOSS(target, 99) + if(GETBRAINLOSS(target) >= BRAIN_DAMAGE_DEATH) + SETBRAINLOSS(target, BRAIN_DAMAGE_DEATH - 1) if(HAS_TRAIT(target, TRAIT_NECRA_CURSE)) to_chat(src, span_warning("Necra holds tight to this one.")) return FALSE diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index 0332cc7f833..fc16b55b0c5 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -185,11 +185,9 @@ return FALSE /mob/living/carbon/proc/handle_bodyparts(delta_time, times_fired) - for(var/I in bodyparts) - var/obj/item/bodypart/BP = I - if(BP.needs_processing) - . |= BP.on_life(delta_time, times_fired) - + for(var/obj/item/bodypart/bodypart as anything in bodyparts) + if(bodypart.needs_processing) + . |= bodypart.on_life(delta_time, times_fired) /mob/living/carbon/proc/handle_organs(delta_time, times_fired) if(HAS_TRAIT(src, TRAIT_NO_ORGAN_PROCESS)) //internal stasis basically diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index 4ecbf4aca12..0f2a62a6947 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -140,10 +140,12 @@ . = ..() L.remove_chem_effect(CE_PAINKILLER, "[type]") -/datum/reagent/medicine/coldvein_compress/on_bodypart_absorb(obj/item/bodypart/BP, mob/living/carbon/M, amount_to_transfer) - for(var/datum/injury/injury in BP.injuries) +/datum/reagent/medicine/coldvein_compress/on_bodypart_absorb(obj/item/bodypart/bodypart, mob/living/carbon/carbon_mob, amount_to_transfer) + for(var/datum/injury/injury in bodypart.injuries) if(injury.damage_type == WOUND_BURN) injury.heal_damage(2) + if(bodypart.post_damage_change()) + carbon_mob.update_damage_overlays() /datum/reagent/medicine/coldvein_compress/on_mob_life(mob/living/carbon/M, efficiency) if(volume > 0.99) @@ -167,13 +169,15 @@ M.heal_wounds(4 * efficiency) . = ..() -/datum/reagent/medicine/ichor_of_mending/on_bodypart_absorb(obj/item/bodypart/BP, mob/living/carbon/M, amount_to_transfer) - for(var/datum/injury/injury in BP.injuries) +/datum/reagent/medicine/ichor_of_mending/on_bodypart_absorb(obj/item/bodypart/bodypart, mob/living/carbon/carbon_mob, amount_to_transfer) + for(var/datum/injury/injury in bodypart.injuries) if(!injury.can_heal()) continue if(injury.damage_type == WOUND_BURN) continue injury.heal_damage(2) + if(bodypart.post_damage_change()) + carbon_mob.update_damage_overlays() /datum/reagent/medicine/ashbinders_salve name = "Ashbinder's Salve" @@ -189,13 +193,15 @@ M.adjustFireLoss(-4 * REM * efficiency, 0) . = ..() -/datum/reagent/medicine/ashbinders_salve/on_bodypart_absorb(obj/item/bodypart/BP, mob/living/carbon/M, amount_to_transfer) - for(var/datum/injury/injury in BP.injuries) +/datum/reagent/medicine/ashbinders_salve/on_bodypart_absorb(obj/item/bodypart/bodypart, mob/living/carbon/carbon_mob, amount_to_transfer) + for(var/datum/injury/injury in bodypart.injuries) if(injury.damage_type != WOUND_BURN) continue injury.heal_damage(3) injury.adjust_germ_level(-10) - BP.disinfect_limb(30 SECONDS) + bodypart.disinfect_limb(30 SECONDS) + if(bodypart.post_damage_change()) + carbon_mob.update_damage_overlays() /datum/reagent/medicine/vitalroot_draught name = "Vitalroot Draught" @@ -275,13 +281,15 @@ scent_description = "resinous amber" metabolization_rate = REAGENTS_METABOLISM -/datum/reagent/medicine/woundwrack_oil/on_bodypart_absorb(obj/item/bodypart/BP, mob/living/carbon/M, amount_to_transfer) - for(var/datum/injury/injury in BP.injuries) +/datum/reagent/medicine/woundwrack_oil/on_bodypart_absorb(obj/item/bodypart/bodypart, mob/living/carbon/carbon_mob, amount_to_transfer) + for(var/datum/injury/injury in bodypart.injuries) if(!injury.can_heal()) continue injury.heal_damage(2) injury.salve_injury() - BP.adjust_germ_level(-10) + bodypart.adjust_germ_level(-10) + if(bodypart.post_damage_change()) + carbon_mob.update_damage_overlays() /datum/reagent/medicine/woundwrack_oil/on_mob_life(mob/living/carbon/M, efficiency) if(volume > 0.99) @@ -407,13 +415,15 @@ scent_description = "dust and old herbs" metabolization_rate = REAGENTS_METABOLISM -/datum/reagent/medicine/witchknit_paste/on_bodypart_absorb(obj/item/bodypart/BP, mob/living/carbon/M, amount_to_transfer) - for(var/datum/injury/injury in BP.injuries) +/datum/reagent/medicine/witchknit_paste/on_bodypart_absorb(obj/item/bodypart/bodypart, mob/living/carbon/carbon_mob, amount_to_transfer) + for(var/datum/injury/injury in bodypart.injuries) if(!injury.can_heal()) continue injury.heal_damage(1.5) injury.salve_injury() - BP.adjust_germ_level(-15) + bodypart.adjust_germ_level(-15) + if(bodypart.post_damage_change()) + carbon_mob.update_damage_overlays() /datum/reagent/medicine/witchknit_paste/on_mob_life(mob/living/carbon/M, efficiency) if(volume > 0.99) diff --git a/code/modules/spells/spell_types/pointed/healing.dm b/code/modules/spells/spell_types/pointed/healing.dm index ed2502790aa..100c58b6187 100644 --- a/code/modules/spells/spell_types/pointed/healing.dm +++ b/code/modules/spells/spell_types/pointed/healing.dm @@ -20,7 +20,7 @@ spell_cost = 10 /// Base healing before adjustments - var/base_healing = 25 + var/base_healing = 12.5 /// Wound healing modifier var/wound_modifier = 0.25 /// Blood healing amount @@ -263,12 +263,11 @@ var/mob/living/carbon/C = cast_on var/obj/item/bodypart/affecting = C.get_bodypart(check_zone(owner.zone_selected)) - if(affecting) - affecting.heal_wounds(amount_healed * wound_modifier, src) - for(var/datum/injury/injury as anything in affecting.injuries) - if(!injury.can_heal()) - continue - injury.heal_damage(amount_healed) + if(!affecting) + to_chat(owner, span_danger("[C] is missing their [affecting]!")) + return + + if(affecting.heal_damage(brute = amount_healed, burn = amount_healed)) C.update_damage_overlays() for(var/obj/item/organ/possible_organ in affecting.getorganslotlist(ORGAN_SLOT_ARTERY)) @@ -306,7 +305,7 @@ cooldown_time = 20 SECONDS spell_cost = 45 - base_healing = 50 + base_healing = 25 wound_modifier = 0.5 blood_restoration = BLOOD_VOLUME_SURVIVE stun_undead = TRUE diff --git a/code/modules/spells/spell_types/undirected/bardic/rejuvenation_song.dm b/code/modules/spells/spell_types/undirected/bardic/rejuvenation_song.dm index 52e16ac7e9e..fd167429160 100644 --- a/code/modules/spells/spell_types/undirected/bardic/rejuvenation_song.dm +++ b/code/modules/spells/spell_types/undirected/bardic/rejuvenation_song.dm @@ -30,11 +30,11 @@ if(iscarbon(owner)) var/mob/living/carbon/carbon = owner for(var/datum/injury/injury in carbon.all_injuries) - if(!(injury.damage_type in list(WOUND_SLASH, WOUND_PIERCE, WOUND_BITE, WOUND_BLUNT))) + if(!(injury.damage_type in list(WOUND_SLASH, WOUND_PIERCE, WOUND_BITE, WOUND_BLUNT)) || !injury.can_heal()) continue injury.heal_damage(healing_on_tick) + carbon.update_all_limb_states() owner.heal_wounds(healing_on_tick, list(/datum/wound/slash, /datum/wound/puncture, /datum/wound/bite, /datum/wound/bruise)) - owner.update_damage_overlays() owner.adjustOxyLoss(-healing_on_tick, 0) owner.adjustToxLoss(-healing_on_tick, 0) @@ -50,3 +50,4 @@ if(!injury.can_heal()) continue healing_on_tick = injury.heal_damage(healing_on_tick) + human.update_all_limb_states() diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index fab5a563de6..c5b4fb2ff1b 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -417,7 +417,6 @@ . = TRUE needs_processing = . - /// Proc for damaging organs inside a limb based on damage values /obj/item/bodypart/proc/damage_internal_organs(wounding_type, amount = 0, organ_bonus = 0, bare_organ_bonus = 0, forced = FALSE, wound_messages = TRUE) . = FALSE @@ -572,11 +571,8 @@ if(owner) injury.bleed_timer = max(0, injury.bleed_timer - (0.5 * delta_time)) - // Sync the limb's damage with its injuries - update_damages() - // Also update efficiency - update_limb_efficiency() - owner.update_damage_overlays() + if(post_damage_change()) + owner.update_damage_overlays() /// Updates brute_damn and burn_damn from injuries /obj/item/bodypart/proc/update_damages() @@ -930,21 +926,7 @@ if(shock_penalty) owner.update_shock_penalty(shock_penalty) - - if(owner) - if(can_be_disabled) - update_disabled() - update_limb_efficiency() - if(updating_health) - owner.updatehealth() - - if(get_shock(FALSE, TRUE) >= DAMAGE_PRECISION) - owner.update_shock() - . = TRUE - - update_damages() - consider_processing() - return update_bodypart_damage_state() || . + return post_damage_change(TRUE, FALSE) //Heals brute and burn damage for the organ. Returns 1 if the damage-icon states changed at all. //Damage cannot go below zero. @@ -954,7 +936,6 @@ if(required_status && (status != required_status)) //So we can only heal certain kinds of limbs, ie robotic vs organic. return - for(var/thing in injuries) if((brute <= 0) && (burn <= 0)) break @@ -967,23 +948,23 @@ else if(injury.damage_type == WOUND_BURN) burn = injury.heal_damage(burn) - update_damages() - - if(brute) - set_brute_dam(round(max(brute_dam - brute, 0), DAMAGE_PRECISION)) - if(burn) - set_burn_dam(round(max(burn_dam - burn, 0), DAMAGE_PRECISION)) + return post_damage_change(updating_health) +/// Call this after you damage or heal injuries to update the bodypart's damage properties properly. Returns whether limb need their overlays updated +/obj/item/bodypart/proc/post_damage_change(updating_health = TRUE, updating_shock = FALSE) + . = FALSE update_damages() - if(owner) update_limb_efficiency() if(can_be_disabled) update_disabled() if(updating_health) owner.updatehealth() + if(updating_shock && get_shock(FALSE, TRUE) >= DAMAGE_PRECISION) + owner.update_shock() + . = TRUE consider_processing() - return update_bodypart_damage_state() + return update_bodypart_damage_state() || . ///Proc to hook behavior associated to the change of the brute_dam variable's value. /obj/item/bodypart/proc/set_brute_dam(new_value) @@ -1147,11 +1128,11 @@ SIGNAL_HANDLER set_can_be_disabled(initial(can_be_disabled)) -//Updates an organ's brute/burn states for use by update_damage_overlays() -//Returns 1 if we need to update overlays. 0 otherwise. +/// Updates an organ's brute/burn states for use by update_damage_overlays() +/// Returns 1 if we need to update overlays. 0 otherwise. /obj/item/bodypart/proc/update_bodypart_damage_state() - var/tbrute = round( (brute_dam/max_damage)*3, 1 ) - var/tburn = round( (burn_dam/max_damage)*3, 1 ) + var/tbrute = round( (brute_dam/max_damage) * 3, 1 ) + var/tburn = round( (burn_dam/max_damage) * 3, 1 ) if((tbrute != brutestate) || (tburn != burnstate)) brutestate = tbrute burnstate = tburn diff --git a/code/modules/surgery/organs/organ_processing/spleen.dm b/code/modules/surgery/organs/organ_processing/spleen.dm index ac4db30dda2..2fdf2866f59 100644 --- a/code/modules/surgery/organs/organ_processing/spleen.dm +++ b/code/modules/surgery/organs/organ_processing/spleen.dm @@ -20,10 +20,10 @@ for(var/thing in spleens) var/obj/item/organ/spleen/spleen = thing blood_regen += (spleen.get_slot_efficiency(ORGAN_SLOT_SPLEEN) * spleen.blood_regen_factor) - combined_nutrition_requirement += (spleen.nutriment_req/40) - var/blood_restore_multiplier = owner.get_chem_effect(CE_BLOODRESTORE) - blood_regen *= (1 + blood_restore_multiplier) - combined_nutrition_requirement *= (1 + (blood_restore_multiplier * 0.5)) + combined_nutrition_requirement += (spleen.nutriment_req/40) * 0.5 + var/blood_restore_multiplier = 1 + owner.get_chem_effect(CE_BLOODRESTORE) + blood_regen *= blood_restore_multiplier + combined_nutrition_requirement *= blood_restore_multiplier if(!blood_regen) return owner.adjust_nutrition(-combined_nutrition_requirement * nutrition_ratio * delta_time) diff --git a/code/modules/surgery/surgeries/healing.dm b/code/modules/surgery/surgeries/healing.dm index a41fe9891e2..f4b3b352d91 100644 --- a/code/modules/surgery/surgeries/healing.dm +++ b/code/modules/surgery/surgeries/healing.dm @@ -96,6 +96,7 @@ urhealedamt_burn = injury.heal_damage(urhealedamt_burn) if(urhealedamt_brute && (injury.damage_type in list(WOUND_BLUNT, WOUND_LASH, WOUND_INTERNAL_BRUISE)) && injury.required_status == BODYPART_ORGANIC) urhealedamt_brute = injury.heal_damage(urhealedamt_brute) + target.update_all_limb_states() else target.heal_bodypart_damage(urhealedamt_brute, urhealedamt_burn, required_status = BODYPART_ORGANIC) SEND_SIGNAL(user, COMSIG_LIVING_HEALED_OTHER, urhealedamt_brute + urhealedamt_burn) diff --git a/vanderlin.dme b/vanderlin.dme index baeafa51729..1f7a2146b6d 100644 --- a/vanderlin.dme +++ b/vanderlin.dme @@ -3439,6 +3439,7 @@ #include "code\modules\mob\living\carbon\carbon.dm" #include "code\modules\mob\living\carbon\carbon_defense.dm" #include "code\modules\mob\living\carbon\carbon_defines.dm" +#include "code\modules\mob\living\carbon\carbon_medical.dm" #include "code\modules\mob\living\carbon\carbon_movement.dm" #include "code\modules\mob\living\carbon\carbon_shock.dm" #include "code\modules\mob\living\carbon\carbon_wounds.dm" From 326507d6f49e4af58e5b36c0732a5fa1ce006856 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Mon, 11 May 2026 18:23:16 -0700 Subject: [PATCH 21/59] eat chems through bandage --- .../crafting/alchemy/herbal_recipes.dm | 24 ++++---- code/modules/crafting/alchemy/reagents.dm | 25 ++++---- code/modules/reagents/chemistry/reagents.dm | 13 +++- .../chemistry/reagents/alcohol_reagents.dm | 9 +-- .../chemistry/reagents/drug_reagents.dm | 20 +++---- .../chemistry/reagents/medicine_reagents.dm | 60 ++++++++++--------- .../chemistry/reagents/other_reagents.dm | 7 ++- .../spells/spell_types/cone/eldritch_blast.dm | 2 +- .../surgery/bodyparts/bodypart_wounds.dm | 4 +- .../surgery/organs/organ_processing/spleen.dm | 2 +- 10 files changed, 90 insertions(+), 76 deletions(-) diff --git a/code/modules/crafting/alchemy/herbal_recipes.dm b/code/modules/crafting/alchemy/herbal_recipes.dm index d9be12fe0cf..091671c2e8b 100644 --- a/code/modules/crafting/alchemy/herbal_recipes.dm +++ b/code/modules/crafting/alchemy/herbal_recipes.dm @@ -75,8 +75,8 @@ taste_description = "bitter flowers" scent_description = "marigold" -/datum/reagent/medicine/herbal/calendula_salve/on_bodypart_absorb(obj/item/bodypart/bodypart, mob/living/carbon/carbon_mob, amount_to_transfer) - for(var/datum/injury/injury in bodypart.injuries) +/datum/reagent/medicine/herbal/calendula_salve/on_bodypart_absorb(mob/living/carbon/affected_mob, obj/item/bodypart/affected_bodypart, amount_to_transfer) + for(var/datum/injury/injury in affected_bodypart.injuries) if(!injury.can_heal()) continue injury.heal_damage(1) @@ -84,9 +84,9 @@ if(injury.damage_type == WOUND_BURN) injury.heal_damage(3) injury.adjust_germ_level(-5) - bodypart.disinfect_limb(20 SECONDS) - if(bodypart.post_damage_change()) - carbon_mob.update_damage_overlays() + affected_bodypart.disinfect_limb(20 SECONDS) + if(affected_bodypart.post_damage_change()) + affected_mob.update_damage_overlays() // Weak Mana/Stamina Potions (based on hypericum/benedictus/mentha) /datum/reagent/medicine/herbal/hypericum_tonic @@ -398,17 +398,17 @@ overdose_threshold = 30 taste_description = "bitter numbness" -/datum/reagent/medicine/herbal/paris_poultice/on_bodypart_absorb(obj/item/bodypart/bodypart, mob/living/carbon/carbon_mob, amount_to_transfer) - for(var/datum/injury/injury in bodypart.injuries) +/datum/reagent/medicine/herbal/paris_poultice/on_bodypart_absorb(mob/living/carbon/affected_mob, obj/item/bodypart/affected_bodypart, amount_to_transfer) + for(var/datum/injury/injury in affected_bodypart.injuries) if(!injury.can_heal()) continue if(injury.damage_type == WOUND_BURN) injury.heal_damage(0.5) if(injury.damage_type != WOUND_BURN) injury.heal_damage(0.75) - bodypart.add_pain(-amount_to_transfer * 0.3) - if(bodypart.post_damage_change()) - carbon_mob.update_damage_overlays() + affected_bodypart.add_pain(-amount_to_transfer * 0.3) + if(affected_bodypart.post_damage_change()) + affected_mob.update_damage_overlays() /datum/reagent/medicine/herbal/paris_poultice/overdose_process(mob/living/M) M.adjustToxLoss(0.5) @@ -530,8 +530,8 @@ metabolization_rate = 0.3 taste_description = "cooling mint" -/datum/reagent/medicine/herbal/mentha_oil/on_bodypart_absorb(obj/item/bodypart/bodypart, mob/living/carbon/M, amount_to_transfer) - bodypart.add_pain(-(amount_to_transfer * 0.3)) +/datum/reagent/medicine/herbal/mentha_oil/on_bodypart_absorb(mob/living/carbon/affected_mob, obj/item/bodypart/affected_bodypart, amount_to_transfer) + affected_bodypart.add_pain(-(amount_to_transfer * 0.3)) /datum/reagent/medicine/herbal/mentha_oil/on_mob_life(mob/living/carbon/M, efficiency) M.adjust_stamina(1.5 * efficiency) diff --git a/code/modules/crafting/alchemy/reagents.dm b/code/modules/crafting/alchemy/reagents.dm index ce6729d502f..3c59f940420 100644 --- a/code/modules/crafting/alchemy/reagents.dm +++ b/code/modules/crafting/alchemy/reagents.dm @@ -10,13 +10,12 @@ alpha = 173 liver_chemical = FALSE -/datum/reagent/medicine/healthpot/on_bodypart_absorb(obj/item/bodypart/bodypart, mob/living/carbon/carbon_mob, amount_to_transfer) - for(var/datum/injury/injury in bodypart.injuries) +/datum/reagent/medicine/healthpot/on_bodypart_absorb(mob/living/carbon/affected_mob, obj/item/bodypart/affected_bodypart, amount_to_transfer) + for(var/datum/injury/injury in affected_bodypart.injuries) if(!injury.can_heal()) continue injury.heal_damage(1) - if(bodypart.post_damage_change()) - carbon_mob.update_damage_overlays() + . = ..() /datum/reagent/medicine/healthpot/on_mob_metabolize(mob/living/L) . = ..() @@ -56,15 +55,14 @@ metabolization_rate = REAGENTS_METABOLISM * 2 liver_chemical = FALSE -/datum/reagent/medicine/healthpot/on_bodypart_absorb(obj/item/bodypart/bodypart, mob/living/carbon/carbon_mob, amount_to_transfer) - for(var/datum/injury/injury in bodypart.injuries) +/datum/reagent/medicine/healthpot/on_bodypart_absorb(mob/living/carbon/affected_mob, obj/item/bodypart/affected_bodypart, amount_to_transfer) + for(var/datum/injury/injury in affected_bodypart.injuries) if(!injury.can_heal()) continue injury.heal_damage(2) - for(var/datum/wound/wound in bodypart.wounds) + for(var/datum/wound/wound in affected_bodypart.wounds) wound.heal_wound(2) - if(bodypart.post_damage_change()) - carbon_mob.update_damage_overlays() + . = ..() /datum/reagent/medicine/stronghealth/on_mob_metabolize(mob/living/L) . = ..() @@ -238,11 +236,12 @@ scent_description = "saiga droppings" metabolization_rate = REAGENTS_METABOLISM * 3 -/datum/reagent/medicine/diseasecure/on_bodypart_absorb(obj/item/bodypart/BP, mob/living/carbon/M, amount_to_transfer) - BP.disinfect_limb(20 MINUTES) - for(var/datum/injury/injury in BP.injuries) +/datum/reagent/medicine/diseasecure/on_bodypart_absorb(mob/living/carbon/affected_mob, obj/item/bodypart/affected_bodypart, amount_to_transfer) + affected_bodypart.disinfect_limb(20 MINUTES) + for(var/datum/injury/injury in affected_bodypart.injuries) injury.adjust_germ_level(-30) - BP.adjust_germ_level(-30) + affected_bodypart.adjust_germ_level(-30) + . = ..() /datum/reagent/medicine/diseasecure/on_mob_metabolize(mob/living/L) . = ..() diff --git a/code/modules/reagents/chemistry/reagents.dm b/code/modules/reagents/chemistry/reagents.dm index c0ee408389a..26c1916b563 100644 --- a/code/modules/reagents/chemistry/reagents.dm +++ b/code/modules/reagents/chemistry/reagents.dm @@ -99,9 +99,16 @@ GLOBAL_LIST_INIT(name2reagent, build_name2reagent()) /datum/reagent/proc/reaction_turf(turf/T, volume) return -/datum/reagent/proc/on_bodypart_absorb(obj/item/bodypart, mob/living/carbon/M, amount_to_transfer) - SHOULD_CALL_PARENT(FALSE) - on_mob_life(M) +/// Call parent to simulate transfering reagent to the mob and instantly metabolizing it. This seems awful. +/datum/reagent/proc/on_bodypart_absorb(mob/living/carbon/affected_mob, obj/item/bodypart/affected_bodypart, amount_to_transfer) + volume = amount_to_transfer + if(!affected_mob) + return + on_mob_add(affected_mob) + on_mob_metabolize(affected_mob) + on_mob_life(affected_mob, efficiency = 1) + on_mob_end_metabolize(affected_mob) + on_mob_delete(affected_mob) /datum/reagent/proc/on_mob_life(mob/living/carbon/M, efficiency) SHOULD_CALL_PARENT(TRUE) diff --git a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm index b175ca40e2d..92d83f5bf46 100644 --- a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm @@ -19,11 +19,12 @@ var/age_time = 10 MINUTES var/age_timer -/datum/reagent/consumable/ethanol/on_bodypart_absorb(obj/item/bodypart/BP, mob/living/carbon/M, amount_to_transfer) - BP.disinfect_limb(boozepwr) - for(var/datum/injury/injury in BP.injuries) +/datum/reagent/consumable/ethanol/on_bodypart_absorb(mob/living/carbon/affected_mob, obj/item/bodypart/affected_bodypart, amount_to_transfer) + affected_bodypart.disinfect_limb(boozepwr) + for(var/datum/injury/injury in affected_bodypart.injuries) injury.adjust_germ_level(-boozepwr * 0.5) - BP.adjust_germ_level(-boozepwr * 0.1) + affected_bodypart.adjust_germ_level(-boozepwr * 0.1) + . = ..() /datum/reagent/consumable/ethanol/New() . = ..() diff --git a/code/modules/reagents/chemistry/reagents/drug_reagents.dm b/code/modules/reagents/chemistry/reagents/drug_reagents.dm index e4403b1cde7..c18f6fcea77 100644 --- a/code/modules/reagents/chemistry/reagents/drug_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drug_reagents.dm @@ -95,21 +95,21 @@ taste_description = "the clouds" overdose_threshold = 30 -/datum/reagent/drug/hallucinogen/on_mob_life(mob/living/carbon/psychonaut, seconds_per_tick, metabolization_ratio) +/datum/reagent/drug/hallucinogen/on_mob_life(mob/living/carbon/psychonaut, efficiency) . = ..() - psychonaut.slurring = max(psychonaut.slurring, 2.5 SECONDS * metabolization_ratio * seconds_per_tick) + psychonaut.slurring = max(psychonaut.slurring, 2.5 SECONDS * efficiency) switch(current_cycle) if(2 to 6) - if(SPT_PROB(5, seconds_per_tick)) + if(SPT_PROB(5, 2)) psychonaut.emote(pick("twitch","giggle")) if(6 to 11) - psychonaut.set_jitter_if_lower(50 SECONDS * metabolization_ratio * seconds_per_tick) - if(SPT_PROB(10, seconds_per_tick)) + psychonaut.set_jitter_if_lower(50 SECONDS * efficiency) + if(SPT_PROB(10, 2)) psychonaut.emote(pick("twitch","giggle")) if (11 to INFINITY) - psychonaut.set_jitter_if_lower(100 SECONDS * metabolization_ratio * seconds_per_tick) - if(SPT_PROB(16, seconds_per_tick)) + psychonaut.set_jitter_if_lower(100 SECONDS * efficiency) + if(SPT_PROB(16, 2)) psychonaut.emote(pick("twitch","giggle")) /datum/reagent/drug/hallucinogen/on_mob_metabolize(mob/living/psychonaut) @@ -169,9 +169,9 @@ metabolization_rate = 0.15 * REAGENTS_METABOLISM taste_description = " something deeply wrong" -/datum/reagent/drug/hallucinogen_concetrate/on_mob_life(mob/living/carbon/psychonaut, seconds_per_tick, metabolization_ratio) +/datum/reagent/drug/hallucinogen_concetrate/on_mob_life(mob/living/carbon/psychonaut, efficiency) . = ..() // weaker version of base hallucinogen — slurring only, mild jitter at high cycle - psychonaut.slurring = max(psychonaut.slurring, 1 SECONDS * metabolization_ratio * seconds_per_tick) + psychonaut.slurring = max(psychonaut.slurring, 1 SECONDS * efficiency) if(current_cycle >= 8) - psychonaut.set_jitter_if_lower(20 SECONDS * metabolization_ratio * seconds_per_tick) + psychonaut.set_jitter_if_lower(20 SECONDS * efficiency) diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index 0f2a62a6947..c1643ebf43a 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -140,12 +140,13 @@ . = ..() L.remove_chem_effect(CE_PAINKILLER, "[type]") -/datum/reagent/medicine/coldvein_compress/on_bodypart_absorb(obj/item/bodypart/bodypart, mob/living/carbon/carbon_mob, amount_to_transfer) - for(var/datum/injury/injury in bodypart.injuries) +/datum/reagent/medicine/coldvein_compress/on_bodypart_absorb(mob/living/carbon/affected_mob, obj/item/bodypart/affected_bodypart, amount_to_transfer) + for(var/datum/injury/injury in affected_bodypart.injuries) if(injury.damage_type == WOUND_BURN) injury.heal_damage(2) - if(bodypart.post_damage_change()) - carbon_mob.update_damage_overlays() + if(affected_bodypart.post_damage_change()) + affected_mob.update_damage_overlays() + . = ..() /datum/reagent/medicine/coldvein_compress/on_mob_life(mob/living/carbon/M, efficiency) if(volume > 0.99) @@ -169,15 +170,16 @@ M.heal_wounds(4 * efficiency) . = ..() -/datum/reagent/medicine/ichor_of_mending/on_bodypart_absorb(obj/item/bodypart/bodypart, mob/living/carbon/carbon_mob, amount_to_transfer) - for(var/datum/injury/injury in bodypart.injuries) +/datum/reagent/medicine/ichor_of_mending/on_bodypart_absorb(mob/living/carbon/affected_mob, obj/item/bodypart/affected_bodypart, amount_to_transfer) + for(var/datum/injury/injury in affected_bodypart.injuries) if(!injury.can_heal()) continue if(injury.damage_type == WOUND_BURN) continue injury.heal_damage(2) - if(bodypart.post_damage_change()) - carbon_mob.update_damage_overlays() + if(affected_bodypart.post_damage_change()) + affected_mob.update_damage_overlays() + . = ..() /datum/reagent/medicine/ashbinders_salve name = "Ashbinder's Salve" @@ -193,15 +195,16 @@ M.adjustFireLoss(-4 * REM * efficiency, 0) . = ..() -/datum/reagent/medicine/ashbinders_salve/on_bodypart_absorb(obj/item/bodypart/bodypart, mob/living/carbon/carbon_mob, amount_to_transfer) - for(var/datum/injury/injury in bodypart.injuries) +/datum/reagent/medicine/ashbinders_salve/on_bodypart_absorb(mob/living/carbon/affected_mob, obj/item/bodypart/affected_bodypart, amount_to_transfer) + for(var/datum/injury/injury in affected_bodypart.injuries) if(injury.damage_type != WOUND_BURN) continue injury.heal_damage(3) injury.adjust_germ_level(-10) - bodypart.disinfect_limb(30 SECONDS) - if(bodypart.post_damage_change()) - carbon_mob.update_damage_overlays() + affected_bodypart.disinfect_limb(30 SECONDS) + if(affected_bodypart.post_damage_change()) + affected_mob.update_damage_overlays() + . = ..() /datum/reagent/medicine/vitalroot_draught name = "Vitalroot Draught" @@ -261,11 +264,12 @@ scent_description = "stagnant bog" metabolization_rate = REAGENTS_METABOLISM * 0.5 -/datum/reagent/medicine/mirewort_compress/on_bodypart_absorb(obj/item/bodypart/BP, mob/living/carbon/M, amount_to_transfer) - for(var/datum/injury/injury in BP.injuries) +/datum/reagent/medicine/mirewort_compress/on_bodypart_absorb(mob/living/carbon/affected_mob, obj/item/bodypart/affected_bodypart, amount_to_transfer) + for(var/datum/injury/injury in affected_bodypart.injuries) injury.adjust_germ_level(-20) - BP.disinfect_limb(3 MINUTES) - BP.adjust_germ_level(-25) + affected_bodypart.disinfect_limb(3 MINUTES) + affected_bodypart.adjust_germ_level(-25) + . = ..() /datum/reagent/medicine/mirewort_compress/on_mob_life(mob/living/carbon/M, efficiency) if(volume > 0.99) @@ -281,15 +285,16 @@ scent_description = "resinous amber" metabolization_rate = REAGENTS_METABOLISM -/datum/reagent/medicine/woundwrack_oil/on_bodypart_absorb(obj/item/bodypart/bodypart, mob/living/carbon/carbon_mob, amount_to_transfer) - for(var/datum/injury/injury in bodypart.injuries) +/datum/reagent/medicine/woundwrack_oil/on_bodypart_absorb(mob/living/carbon/affected_mob, obj/item/bodypart/affected_bodypart, amount_to_transfer) + for(var/datum/injury/injury in affected_bodypart.injuries) if(!injury.can_heal()) continue injury.heal_damage(2) injury.salve_injury() - bodypart.adjust_germ_level(-10) - if(bodypart.post_damage_change()) - carbon_mob.update_damage_overlays() + affected_bodypart.adjust_germ_level(-10) + if(affected_bodypart.post_damage_change()) + affected_mob.update_damage_overlays() + . = ..() /datum/reagent/medicine/woundwrack_oil/on_mob_life(mob/living/carbon/M, efficiency) if(volume > 0.99) @@ -415,15 +420,16 @@ scent_description = "dust and old herbs" metabolization_rate = REAGENTS_METABOLISM -/datum/reagent/medicine/witchknit_paste/on_bodypart_absorb(obj/item/bodypart/bodypart, mob/living/carbon/carbon_mob, amount_to_transfer) - for(var/datum/injury/injury in bodypart.injuries) +/datum/reagent/medicine/witchknit_paste/on_bodypart_absorb(mob/living/carbon/affected_mob, obj/item/bodypart/affected_bodypart, amount_to_transfer) + for(var/datum/injury/injury in affected_bodypart.injuries) if(!injury.can_heal()) continue injury.heal_damage(1.5) injury.salve_injury() - bodypart.adjust_germ_level(-15) - if(bodypart.post_damage_change()) - carbon_mob.update_damage_overlays() + affected_bodypart.adjust_germ_level(-15) + if(affected_bodypart.post_damage_change()) + affected_mob.update_damage_overlays() + . = ..() /datum/reagent/medicine/witchknit_paste/on_mob_life(mob/living/carbon/M, efficiency) if(volume > 0.99) diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index 0ffba9719fe..7d167a85ee9 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -158,10 +158,11 @@ color = "#98934bc6" sanitization = -SANITIZATION_PER_UNIT_WATER -/datum/reagent/water/gross/on_bodypart_absorb(obj/item/bodypart/BP, mob/living/carbon/M, amount_to_transfer) - BP.undisinfect_limb() - for(var/datum/injury/injury in BP.injuries) +/datum/reagent/water/gross/on_bodypart_absorb(mob/living/carbon/affected_mob, obj/item/bodypart/affected_bodypart, amount_to_transfer) + affected_bodypart.undisinfect_limb() + for(var/datum/injury/injury in affected_bodypart.injuries) injury.adjust_germ_level(SANITIZATION_PER_UNIT_WATER) + . = ..() /datum/reagent/water/gross/on_aeration(volume, turf/turf) turf.pollute_turf(/datum/pollutant/rot/sewage, volume * 3) diff --git a/code/modules/spells/spell_types/cone/eldritch_blast.dm b/code/modules/spells/spell_types/cone/eldritch_blast.dm index 14d7c3d2ca4..13622f5c786 100644 --- a/code/modules/spells/spell_types/cone/eldritch_blast.dm +++ b/code/modules/spells/spell_types/cone/eldritch_blast.dm @@ -7,7 +7,7 @@ color = "#1f8016" metabolization_rate = 2.5 * REAGENTS_METABOLISM //0.5u/second -/datum/reagent/eldritch/on_mob_life(mob/living/carbon/drinker) +/datum/reagent/eldritch/on_mob_life(mob/living/carbon/drinker, efficiency) drinker.adjustOrganLoss(ORGAN_SLOT_BRAIN, 3 * REM, 150) drinker.adjustToxLoss(2 * REM, FALSE) drinker.adjustFireLoss(2 * REM, FALSE) diff --git a/code/modules/surgery/bodyparts/bodypart_wounds.dm b/code/modules/surgery/bodyparts/bodypart_wounds.dm index 127a51f0527..fe02a929f7a 100644 --- a/code/modules/surgery/bodyparts/bodypart_wounds.dm +++ b/code/modules/surgery/bodyparts/bodypart_wounds.dm @@ -382,9 +382,9 @@ return TRUE /obj/item/bodypart/proc/try_bandage_expire() - var/bleed_rate = get_bleed_rate(TRUE) if(!bandage) return FALSE + var/bleed_rate = get_bleed_rate(TRUE) if(!bleed_rate) return FALSE @@ -398,7 +398,7 @@ var/amount_to_transfer = min(R.volume, R.metabolization_rate) if(amount_to_transfer > 0) - R.on_bodypart_absorb(src, owner, amount_to_transfer) + R.on_bodypart_absorb(owner, src, amount_to_transfer) cloth.reagents.remove_reagent(R.type, amount_to_transfer) if(owner) diff --git a/code/modules/surgery/organs/organ_processing/spleen.dm b/code/modules/surgery/organs/organ_processing/spleen.dm index 2fdf2866f59..ac7058e58ed 100644 --- a/code/modules/surgery/organs/organ_processing/spleen.dm +++ b/code/modules/surgery/organs/organ_processing/spleen.dm @@ -20,7 +20,7 @@ for(var/thing in spleens) var/obj/item/organ/spleen/spleen = thing blood_regen += (spleen.get_slot_efficiency(ORGAN_SLOT_SPLEEN) * spleen.blood_regen_factor) - combined_nutrition_requirement += (spleen.nutriment_req/40) * 0.5 + combined_nutrition_requirement += spleen.nutriment_req / 80 var/blood_restore_multiplier = 1 + owner.get_chem_effect(CE_BLOODRESTORE) blood_regen *= blood_restore_multiplier combined_nutrition_requirement *= blood_restore_multiplier From 3390e7dd285f5e3d78aebfdee9545d1a1f418969 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Tue, 12 May 2026 09:10:47 -0700 Subject: [PATCH 22/59] borbop why --- code/modules/surgery/bodyparts/_bodyparts.dm | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index c5b4fb2ff1b..b233449b963 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -936,17 +936,15 @@ if(required_status && (status != required_status)) //So we can only heal certain kinds of limbs, ie robotic vs organic. return - for(var/thing in injuries) + for(var/datum/injury/injury as anything in injuries) if((brute <= 0) && (burn <= 0)) break - var/datum/injury/injury = thing - var/list/heal_list = list(WOUND_SLASH, WOUND_PIERCE, WOUND_BLUNT, WOUND_INTERNAL_BRUISE) - if(true_heal) - heal_list |= list(WOUND_BITE, WOUND_BLUNT, WOUND_DIVINE, WOUND_LASH) - if(injury.damage_type in heal_list) - brute = injury.heal_damage(brute) - else if(injury.damage_type == WOUND_BURN) + if(!true_heal && !injury.can_heal()) + continue + if(injury.damage_type == WOUND_BURN) burn = injury.heal_damage(burn) + else + brute = injury.heal_damage(brute) return post_damage_change(updating_health) From 06a7ba648bb1567900f670361433a6662d7967f5 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Tue, 12 May 2026 14:38:28 -0700 Subject: [PATCH 23/59] potatos attempt yo we try this needs to always fire cooldown --- code/__DEFINES/chemical_effects.dm | 2 +- code/__DEFINES/combat.dm | 2 +- code/__DEFINES/heal_flags.dm | 4 +- code/__DEFINES/pain.dm | 29 ++++---- code/__DEFINES/traits/definitions.dm | 2 + code/__HELPERS/round_statistics.dm | 1 + code/_globalvars/traits.dm | 1 + code/_onclick/click.dm | 6 +- code/datums/components/butchering.dm | 23 ------- code/datums/injury/_injury.dm | 2 +- code/datums/quirks/vices/mental_vice.dm | 5 -- code/datums/quirks/vices/special.dm | 1 - code/datums/status_effects/debuffs.dm | 2 +- code/datums/wounds/black_briar_curse.dm | 4 +- code/datums/wounds/fractures.dm | 1 + code/datums/wounds/spill.dm | 4 +- .../objects/items/chimeric_organs/ifak.dm | 5 +- .../objects/items/weapons/melee/godweapons.dm | 4 +- .../objects/items/weapons/melee/knives.dm | 2 +- code/modules/admin/topic.dm | 2 +- .../neu_vampires/living_modifications.dm | 4 +- .../crafting/alchemy/herbal_recipes.dm | 1 + code/modules/crafting/alchemy/reagents.dm | 2 +- code/modules/flufftext/Dreaming.dm | 2 +- .../job_types/apprentices/clinicapprentice.dm | 5 +- code/modules/mob/living/carbon/carbon.dm | 2 +- .../mob/living/carbon/carbon_medical.dm | 1 + .../modules/mob/living/carbon/carbon_shock.dm | 16 ++--- .../modules/mob/living/carbon/damage_procs.dm | 20 +++--- code/modules/mob/living/carbon/examine.dm | 10 ++- code/modules/mob/living/carbon/human/human.dm | 50 +++++++------- .../mob/living/carbon/human/species.dm | 2 +- code/modules/mob/living/carbon/life.dm | 4 +- code/modules/mob/living/living.dm | 26 +++++-- code/modules/mob/living/living_say.dm | 4 +- code/modules/mob/mob_helpers.dm | 59 ++++++++++------ code/modules/reagents/chemistry/reagents.dm | 28 +++++--- .../chemistry/reagents/alcohol_reagents.dm | 19 ++--- .../chemistry/reagents/drug_reagents.dm | 9 +-- .../chemistry/reagents/other_reagents.dm | 9 +-- .../reagents/reagent_containers/powder.dm | 69 ++++++++++--------- code/modules/surgery/bodyparts/_bodyparts.dm | 42 ++++++----- code/modules/surgery/organs/_organ.dm | 4 +- code/modules/surgery/organs/_organ_storage.dm | 2 +- code/modules/surgery/organs/internal/heart.dm | 9 +-- .../surgery/organs/organ_processing/heart.dm | 5 +- 46 files changed, 267 insertions(+), 239 deletions(-) diff --git a/code/__DEFINES/chemical_effects.dm b/code/__DEFINES/chemical_effects.dm index 637d9a67a79..c33bfff038e 100644 --- a/code/__DEFINES/chemical_effects.dm +++ b/code/__DEFINES/chemical_effects.dm @@ -17,7 +17,7 @@ #define CE_STABLE "stable" /// Boosts spleen blood regen #define CE_BLOODRESTORE "bloodrestore" -/// Reduces shock +/// Reduces shock by a flat amount, applied across bodyparts #define CE_PAINKILLER "painkiller" /// Liver filtering #define CE_ALCOHOL "alcohol" diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index cf14cdc8869..594cb834b02 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -73,7 +73,7 @@ #define BLEEDOUT (1<<6) //Health Defines -#define HEALTH_THRESHOLD_CRIT 0 +#define HEALTH_THRESHOLD_CRIT 100 #define HEALTH_THRESHOLD_FULLCRIT 0 #define HEALTH_THRESHOLD_DEAD -100 diff --git a/code/__DEFINES/heal_flags.dm b/code/__DEFINES/heal_flags.dm index 3fec25461b5..dccd9e577dd 100644 --- a/code/__DEFINES/heal_flags.dm +++ b/code/__DEFINES/heal_flags.dm @@ -45,9 +45,11 @@ #define HEAL_POSTIVE_DISEASES (1<<20) /// Restores hydration, hunger, etc. #define HEAL_ESSENTIALS (1<<21) +/// Heals pain and shock +#define HEAL_PAIN_SHOCK (1<<22) /// Combination flag to only heal the main damage types. -#define HEAL_DAMAGE (HEAL_BRUTE|HEAL_BURN|HEAL_TOX|HEAL_OXY|HEAL_CLONE|HEAL_STAM) +#define HEAL_DAMAGE (HEAL_BRUTE|HEAL_BURN|HEAL_TOX|HEAL_OXY|HEAL_CLONE|HEAL_STAM|HEAL_PAIN_SHOCK) /// Combination flag to only heal things messed up things about the mob's body itself. #define HEAL_BODY (HEAL_LIMBS|HEAL_ORGANS|HEAL_REFRESH_ORGANS|HEAL_WOUNDS|HEAL_TRAUMAS|HEAL_BLOOD|HEAL_TEMP|HEAL_ESSENTIALS) /// Combination flag to heal negative things affecting the mob. diff --git a/code/__DEFINES/pain.dm b/code/__DEFINES/pain.dm index 79415763f7c..9d914606b72 100644 --- a/code/__DEFINES/pain.dm +++ b/code/__DEFINES/pain.dm @@ -2,19 +2,19 @@ #define PAIN_EMOTE_MINIMUM 10 // ~shock stages -#define SHOCK_STAGE_1 10 -#define SHOCK_STAGE_2 30 -#define SHOCK_STAGE_3 40 -#define SHOCK_STAGE_4 60 // "Softcrit" -#define SHOCK_STAGE_5 80 -#define SHOCK_STAGE_6 120 // "Hardcrit" -#define SHOCK_STAGE_7 150 -#define SHOCK_STAGE_8 200 +#define SHOCK_STAGE_1 20 +#define SHOCK_STAGE_2 40 +#define SHOCK_STAGE_3 50 +#define SHOCK_STAGE_4 70 // "Softcrit" +#define SHOCK_STAGE_5 90 +#define SHOCK_STAGE_6 130 // "Hardcrit" +#define SHOCK_STAGE_7 160 +#define SHOCK_STAGE_8 210 #define SHOCK_STAGE_MAX SHOCK_STAGE_8 // ~shock modifiers -#define SHOCK_MOD_BRUTE 0.7 -#define SHOCK_MOD_BURN 0.8 +#define SHOCK_MOD_BRUTE 0.5 +#define SHOCK_MOD_BURN 0.75 #define SHOCK_MOD_TOXIN 1 #define SHOCK_MOD_CLONE 1.25 @@ -31,8 +31,11 @@ /// Above or equal to this amount of pain, we can only speak in whispers #define PAIN_NO_SPEAK 250 -/// Divisor used in several pain calculations -#define PAINKILLER_DIVISOR 2 +/// Divisor used in pain calculations, since carbon pain is a flat amount and spread across bodyparts +#define PAINKILLER_DIVISOR 1.5 + +/// Use this to keep the speed of pain-related systems consistent relatively +#define PAIN_SYSTEM_SPEED_MODIFIER 3 #define PAIN_KNOCKDOWN_MESSAGE "gives in to the pain!" #define PAIN_KNOCKDOWN_MESSAGE_SELF "I give in to the pain!" @@ -43,4 +46,4 @@ #define SHOCK_PENALTY_COOLDOWN_DURATION 5 SECONDS #define COOLDOWN_CARBON_ENDORPHINATION "carbon_endorphination" /// Cooldown before our body endorphinates itself again -#define ENDORPHINATION_COOLDOWN_DURATION 30 SECONDS +#define ENDORPHINATION_COOLDOWN_DURATION 60 SECONDS diff --git a/code/__DEFINES/traits/definitions.dm b/code/__DEFINES/traits/definitions.dm index 552ac01d0c2..8411eb75b09 100644 --- a/code/__DEFINES/traits/definitions.dm +++ b/code/__DEFINES/traits/definitions.dm @@ -173,6 +173,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_SOFT_CRITICAL_CONDITION "soft_critical_condition" /// Toxin damage heals, toxin healing does damage #define TRAIT_TOXINLOVER "toxinlover" +/// Doesn't get overlays from being in critical. +#define TRAIT_NOCRITOVERLAY "no_crit_overlay" /// Doesn't need to breathe #define TRAIT_NOBREATH "no_breath" #define TRAIT_HOLY "holy" diff --git a/code/__HELPERS/round_statistics.dm b/code/__HELPERS/round_statistics.dm index 1ab151282e3..0bef6b7844b 100644 --- a/code/__HELPERS/round_statistics.dm +++ b/code/__HELPERS/round_statistics.dm @@ -322,6 +322,7 @@ GLOBAL_LIST_INIT(vanderlin_round_stats, list( STATS_STARTING_TREASURY = 0, STATS_HUNTED_PEOPLE = 0, STATS_FOREIGNERS = 0, + STATS_CPR_REVIVALS = 0, )) GLOBAL_LIST_EMPTY(patron_follower_counts) diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm index 54801df2c4d..85784f687e6 100644 --- a/code/_globalvars/traits.dm +++ b/code/_globalvars/traits.dm @@ -71,6 +71,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_HOLY" = TRAIT_HOLY, "TRAIT_NOAMBUSH" = TRAIT_NOAMBUSH, "TRAIT_NOCRITDAMAGE" = TRAIT_NOCRITDAMAGE, + "TRAIT_NOCRITOVERLAY" = TRAIT_NOCRITOVERLAY, "TRAIT_NOSLIPWATER" = TRAIT_NOSLIPWATER, "TRAIT_NOSLIPALL" = TRAIT_NOSLIPALL, "TRAIT_NODEATH" = TRAIT_NODEATH, diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index 6581e8a6e9f..12e01676a95 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -27,14 +27,14 @@ mod *= S.nextmove_modifier() adj += S.nextmove_adjust() if(!hand) - next_move = world.time + ((num + adj)*mod * (InCritical()? 3 : 1)) + next_move = world.time + ((num + adj)*mod * (HAS_TRAIT(src, TRAIT_CRITICAL_CONDITION) ? 3 : 1)) hud_used?.cdmid?.mark_dirty() return if(hand == 1) - next_lmove = world.time + ((num + adj)*mod * (InCritical()? 3 : 1)) + next_lmove = world.time + ((num + adj)*mod * (HAS_TRAIT(src, TRAIT_CRITICAL_CONDITION) ? 3 : 1)) hud_used?.cdleft?.mark_dirty() else - next_rmove = world.time + ((num + adj)*mod * (InCritical()? 3 : 1)) + next_rmove = world.time + ((num + adj)*mod * (HAS_TRAIT(src, TRAIT_CRITICAL_CONDITION) ? 3 : 1)) hud_used?.cdright?.mark_dirty() /* diff --git a/code/datums/components/butchering.dm b/code/datums/components/butchering.dm index 31573d8ab27..78f151b9cf5 100644 --- a/code/datums/components/butchering.dm +++ b/code/datums/components/butchering.dm @@ -19,29 +19,6 @@ butchering_enabled = FALSE if(_can_be_blunt) can_be_blunt = _can_be_blunt - if(isitem(parent)) - RegisterSignal(parent, COMSIG_ITEM_ATTACK, PROC_REF(onItemAttack)) - -/datum/component/butchering/proc/onItemAttack(obj/item/source, mob/living/M, mob/living/user) - return -/* - if(user.used_intent.type != INTENT_HARM) - return - if(M.stat == DEAD && (M.butcher_results || M.guaranteed_butcher_results)) //can we butcher it? - if(butchering_enabled && (can_be_blunt || source.get_sharpness())) - INVOKE_ASYNC(src, PROC_REF(startButcher), source, M, user) - return COMPONENT_ITEM_NO_ATTACK - - if(ishuman(M) && source.force && source.get_sharpness()) - var/mob/living/carbon/human/H = M - if((H.health <= H.crit_threshold || (user.pulling == H && user.grab_state >= GRAB_NECK) || H.IsSleeping()) && user.zone_selected == BODY_ZONE_HEAD) // Only sleeping, neck grabbed, or crit, can be sliced. - if(H.has_status_effect(/datum/status_effect/neck_slice)) - user.show_message("[H]'s neck has already been cut, you can't make the bleeding any worse!", MSG_VISUAL, \ - "Their neck has already been cut, you can't make the bleeding any worse!") - return COMPONENT_ITEM_NO_ATTACK - INVOKE_ASYNC(src, PROC_REF(startNeckSlice), source, H, user) - return COMPONENT_ITEM_NO_ATTACK -*/ /datum/component/butchering/proc/startButcher(obj/item/source, mob/living/M, mob/living/user) to_chat(user, "I begin to butcher [M]...") diff --git a/code/datums/injury/_injury.dm b/code/datums/injury/_injury.dm index 46dc419b664..a41ff5b111d 100644 --- a/code/datums/injury/_injury.dm +++ b/code/datums/injury/_injury.dm @@ -1,4 +1,4 @@ -#define BLEED_DAMAGE_RATIO 20 +#define BLEED_DAMAGE_RATIO 30 //This is basically the baystation wound datum, which i thought would synergize well with the TG wounds /**************************************************** diff --git a/code/datums/quirks/vices/mental_vice.dm b/code/datums/quirks/vices/mental_vice.dm index a0d75a71a2f..12ec051cba0 100644 --- a/code/datums/quirks/vices/mental_vice.dm +++ b/code/datums/quirks/vices/mental_vice.dm @@ -370,7 +370,6 @@ var/affected_parts = min(rand(1, 3), joint_parts.len) for(var/i = 1 to affected_parts) var/obj/item/bodypart/BP = pick_n_take(joint_parts) - BP.add_pain(rand(10, 20)) BP.limb_flags |= BODYPART_CHRONIC_ARTHRITIS BP.update_chronic() @@ -386,7 +385,6 @@ return var/mob/living/carbon/human/H = owner var/obj/item/bodypart/BP = H.get_bodypart(BODY_ZONE_CHEST) - BP?.add_pain(rand(20, 32.5)) BP?.limb_flags |= pick(BODYPART_CHRONIC_FRACTURE, BODYPART_CHRONIC_SCAR) BP?.update_chronic() to_chat(H, span_warning("Your lower back aches with familiar, persistent pain.")) @@ -408,14 +406,11 @@ if(length(major_parts)) for(var/rand in 1 to rand(1, 2)) var/obj/item/bodypart/wounded = pick(major_parts) - wounded.add_pain(rand(10, 17.5)) var/list/remove_one = list(BODYPART_CHRONIC_FRACTURE, BODYPART_CHRONIC_SCAR, BODYPART_CHRONIC_NERVE_DAMAGE) pick_n_take(remove_one) for(var/i in remove_one) wounded.limb_flags |= i wounded.update_chronic() - var/damage = rand(5, 9) - wounded.bodypart_attacked_by(pick(BCLASS_BLUNT, BCLASS_BITE, BCLASS_CUT, BCLASS_LASHING, BCLASS_BURN), damage, modifiers = list(CRIT_MOD_CHANCE = -100)) var/wound_location = wounded.name var/wound_desc = pick("shrapnel wound", "arrow wound", "deep scar", "poorly healed fracture") to_chat(H, span_warning("You feel the familiar ache of your old [wound_desc] in your [wound_location].")) diff --git a/code/datums/quirks/vices/special.dm b/code/datums/quirks/vices/special.dm index fd105439030..4c1d1908b96 100644 --- a/code/datums/quirks/vices/special.dm +++ b/code/datums/quirks/vices/special.dm @@ -88,7 +88,6 @@ return var/mob/living/carbon/human/H = owner var/obj/item/bodypart/BP = H.get_bodypart(BODY_ZONE_HEAD) - BP?.add_pain(rand(17.5, 27.5)) BP?.limb_flags |= BODYPART_CHRONIC_MIGRAINE BP?.update_chronic() to_chat(H, span_warning("You feel the familiar pressure building behind your eyes.")) diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm index 3bd8370916d..ba1fdeb6f54 100644 --- a/code/datums/status_effects/debuffs.dm +++ b/code/datums/status_effects/debuffs.dm @@ -179,7 +179,7 @@ human_owner?.drunkenness *= 0.997 //reduce drunkenness by 0.3% per tick, 6% per 2 seconds if(prob(20)) carbon_owner?.handle_dreams() - if(prob(10) && owner.health > owner.crit_threshold) + if(prob(10) && !HAS_TRAIT(carbon_owner, TRAIT_CRITICAL_CONDITION)) owner.emote("snore") /atom/movable/screen/alert/status_effect/asleep diff --git a/code/datums/wounds/black_briar_curse.dm b/code/datums/wounds/black_briar_curse.dm index e3448fdbab6..ef64c3158d3 100644 --- a/code/datums/wounds/black_briar_curse.dm +++ b/code/datums/wounds/black_briar_curse.dm @@ -180,7 +180,7 @@ owner.emote("firescream") else if(infection_percent >= BBC_STAGE_MID && prob(50)) owner.emote("agony") - bodypart_owner.add_pain(5) + bodypart_owner.add_pain(SHOCK_STAGE_1/2) if(/datum/patron/divine/dendor, /datum/patron/divine/pestra) var/infection_min = 0 var/list/stages = list(BBC_STAGE_MID, BBC_STAGE_LATE, 1) @@ -227,7 +227,7 @@ mob_overlay = infection_overlay bodypart_owner.bodypart_attacked_by(BCLASS_CUT, 50, null, bodypart_owner.body_zone, TRUE, FALSE, list(CRIT_MOD_CHANCE = -100)) playsound(owner, pick('sound/gore/flesh_eat_01.ogg', 'sound/gore/flesh_eat_02.ogg'), 70, FALSE, -1) - bodypart_owner.add_pain(20) + bodypart_owner.add_pain((SHOCK_STAGE_2-SHOCK_STAGE_1)/2) owner.update_damage_overlays() bodypart_owner.LoadComponent(/datum/component/cursedrosa) else diff --git a/code/datums/wounds/fractures.dm b/code/datums/wounds/fractures.dm index 881f48e65c6..5ae779d6c32 100644 --- a/code/datums/wounds/fractures.dm +++ b/code/datums/wounds/fractures.dm @@ -256,6 +256,7 @@ "The ribs are mauled!", "The ribcage caves in!", ) + woundpain = 50 whp = 50 // Lose 224.6 blood over 18 ticks then clot bleed_rate = 1.6 diff --git a/code/datums/wounds/spill.dm b/code/datums/wounds/spill.dm index 7c72f80418f..4874197adc6 100644 --- a/code/datums/wounds/spill.dm +++ b/code/datums/wounds/spill.dm @@ -2,11 +2,11 @@ name = "intestines" /mob/living/carbon/proc/gut_cut() - if(get_chem_effect(CE_PAINKILLER) < 100) + if(get_chem_effect(CE_PAINKILLER) < 80) emote("scream") CombatKnockdown(30) var/obj/item/bodypart/vitals = get_bodypart(BODY_ZONE_CHEST) - vitals?.add_pain(25) + vitals?.add_pain(SHOCK_STAGE_2) /datum/wound/spill name = "Spill" diff --git a/code/game/objects/items/chimeric_organs/ifak.dm b/code/game/objects/items/chimeric_organs/ifak.dm index d66fdcf9136..08faeca93ce 100644 --- a/code/game/objects/items/chimeric_organs/ifak.dm +++ b/code/game/objects/items/chimeric_organs/ifak.dm @@ -10,7 +10,10 @@ slot_flags = ITEM_SLOT_HIP populate_contents = list( /obj/item/reagent_containers/syringe, - /obj/item/reagent_containers/syringe, + /obj/item/natural/cloth/bandage, + /obj/item/natural/cloth/bandage, + /obj/item/natural/cloth/bandage, + /obj/item/natural/cloth/bandage, /obj/item/storage/fancy/pilltin/sate, /obj/item/storage/fancy/pilltin/devour, /obj/item/candle/yellow, diff --git a/code/game/objects/items/weapons/melee/godweapons.dm b/code/game/objects/items/weapons/melee/godweapons.dm index 690d280ca03..bab7d7606b1 100644 --- a/code/game/objects/items/weapons/melee/godweapons.dm +++ b/code/game/objects/items/weapons/melee/godweapons.dm @@ -77,7 +77,7 @@ var/mob/living/carbon/human/H = target var/heart_crit = H.has_wound(/datum/wound/artery/heart) var/dead = H.stat == DEAD - if((H.health < H.crit_threshold) || heart_crit || dead) + if(HAS_TRAIT(H, TRAIT_CRITICAL_CONDITION) || heart_crit || dead) var/fast = heart_crit || dead var/obj/item/organ/heart/heart = H.getorganslot(ORGAN_SLOT_HEART) if(!heart) @@ -148,7 +148,7 @@ if(H.get_lux_status() != LUX_HAS_LUX) return var/dead = H.stat == DEAD - if((H.health < H.crit_threshold) || dead) + if(HAS_TRAIT(H, TRAIT_CRITICAL_CONDITION) || dead) var/speed = dead ? 3 SECONDS : 7 SECONDS visible_message(user, span_notice("Neant lights up and begins to tear at [target]...")) if(!do_after(user, speed, H)) diff --git a/code/game/objects/items/weapons/melee/knives.dm b/code/game/objects/items/weapons/melee/knives.dm index 8fa50ffb0f5..2d2da3af8f4 100644 --- a/code/game/objects/items/weapons/melee/knives.dm +++ b/code/game/objects/items/weapons/melee/knives.dm @@ -447,7 +447,7 @@ . = ..() if(!ishuman(target)) return - if(target.stat == DEAD || (target.health < target.crit_threshold)) // Trigger soul steal or identity theft if the target is either dead or in crit + if(target.stat == DEAD || HAS_TRAIT(target, TRAIT_CRITICAL_CONDITION)) // Trigger soul steal or identity theft if the target is either dead or in crit if(istype(user.used_intent, /datum/intent/peculate)) if(!ishuman(user)) // carbons don't have all features of a human to_chat(user, span_danger("You can't do that!")) diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 6e39a6d8c67..ab89035d6e2 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -629,7 +629,7 @@ if(SOFT_CRIT) status = "Dying" if(UNCONSCIOUS) - status = "[L.InCritical() ? "Unconscious and Dying" : "Unconscious"]" + status = "[HAS_TRAIT(L, TRAIT_CRITICAL_CONDITION) ? "Unconscious and Dying" : "Unconscious"]" if(DEAD) status = "Dead" health_description = "Status = [status]" diff --git a/code/modules/antagonists/villain/neu_vampires/living_modifications.dm b/code/modules/antagonists/villain/neu_vampires/living_modifications.dm index 68f6d135e82..95835dfa1c5 100644 --- a/code/modules/antagonists/villain/neu_vampires/living_modifications.dm +++ b/code/modules/antagonists/villain/neu_vampires/living_modifications.dm @@ -323,7 +323,7 @@ // Coffin regeneration var/obj/structure/closet/crate/coffin/coffin = loc if(istype(coffin) && (src in coffin.contents)) - if(InCritical() && !HAS_TRAIT(src, TRAIT_DEATHCOMA)) + if(HAS_TRAIT(src, TRAIT_CRITICAL_CONDITION) && !HAS_TRAIT(src, TRAIT_DEATHCOMA)) to_chat(src, span_notice("You enter the horrible slumber of deathless Torpor. You will heal until you are renewed.")) ADD_TRAIT(src, TRAIT_DEATHCOMA, VAMPIRE_TRAIT) heal_overall_damage(5, 5) @@ -338,7 +338,7 @@ set_blood_volume(BLOOD_VOLUME_SAFE) adjust_blood_volume(10) set_bloodpool(max(bloodpool, min(maxbloodpool * 0.25, bloodpool + 5))) - else if(HAS_TRAIT(src, TRAIT_DEATHCOMA) && (!InCritical() || (!istype(coffin) || !(src in coffin.contents)))) + else if(HAS_TRAIT(src, TRAIT_DEATHCOMA) && (!HAS_TRAIT(src, TRAIT_CRITICAL_CONDITION) || (!istype(coffin) || !(src in coffin.contents)))) REMOVE_TRAIT(src, TRAIT_DEATHCOMA, VAMPIRE_TRAIT) to_chat(src, span_warning("You have recovered from Torpor.")) diff --git a/code/modules/crafting/alchemy/herbal_recipes.dm b/code/modules/crafting/alchemy/herbal_recipes.dm index 091671c2e8b..0808763c19e 100644 --- a/code/modules/crafting/alchemy/herbal_recipes.dm +++ b/code/modules/crafting/alchemy/herbal_recipes.dm @@ -532,6 +532,7 @@ /datum/reagent/medicine/herbal/mentha_oil/on_bodypart_absorb(mob/living/carbon/affected_mob, obj/item/bodypart/affected_bodypart, amount_to_transfer) affected_bodypart.add_pain(-(amount_to_transfer * 0.3)) + . = ..() /datum/reagent/medicine/herbal/mentha_oil/on_mob_life(mob/living/carbon/M, efficiency) M.adjust_stamina(1.5 * efficiency) diff --git a/code/modules/crafting/alchemy/reagents.dm b/code/modules/crafting/alchemy/reagents.dm index 3c59f940420..83255da5715 100644 --- a/code/modules/crafting/alchemy/reagents.dm +++ b/code/modules/crafting/alchemy/reagents.dm @@ -453,7 +453,7 @@ If you want to expand on poisons theres tons of fun effects TG chemistry has tha if(prob(30)) to_chat(graggar_lover, span_bloody("More... More...")) var/obj/item/bodypart/bp = graggar_lover.get_bodypart() - bp?.add_pain(10 * efficiency) + bp?.add_pain(SHOCK_STAGE_1 * efficiency) bp?.bodypart_attacked_by(BCLASS_BLUNT, 12 * efficiency, null, BODY_ZONE_CHEST, crit_message = FALSE, modifiers = list(CRIT_MOD_CHANCE = -10)) M.do_jitter_animation(100 * efficiency) if(60) diff --git a/code/modules/flufftext/Dreaming.dm b/code/modules/flufftext/Dreaming.dm index 565d231e376..26efbc9ef36 100644 --- a/code/modules/flufftext/Dreaming.dm +++ b/code/modules/flufftext/Dreaming.dm @@ -57,7 +57,7 @@ dream_sequence(dream_fragments) /mob/living/carbon/proc/dream_sequence(list/dream_fragments) - if(stat != UNCONSCIOUS || InCritical()) + if(stat != UNCONSCIOUS || HAS_TRAIT(src, TRAIT_CRITICAL_CONDITION)) dreaming = FALSE return var/next_message = dream_fragments[1] diff --git a/code/modules/jobs/job_types/apprentices/clinicapprentice.dm b/code/modules/jobs/job_types/apprentices/clinicapprentice.dm index 40425a6eb77..e7647675b83 100644 --- a/code/modules/jobs/job_types/apprentices/clinicapprentice.dm +++ b/code/modules/jobs/job_types/apprentices/clinicapprentice.dm @@ -8,7 +8,7 @@ /datum/attribute/skill/labor/farming = 20, /datum/attribute/skill/misc/reading = 30, /datum/attribute/skill/craft/alchemy = 20, - /datum/attribute/skill/misc/medicine = 20 + /datum/attribute/skill/misc/medicine = 30 ) /datum/attribute_holder/sheet/job/clinicapprentice @@ -21,7 +21,7 @@ /datum/attribute/skill/labor/farming = 20, /datum/attribute/skill/misc/reading = 30, /datum/attribute/skill/craft/alchemy = 20, - /datum/attribute/skill/misc/medicine = 20 + /datum/attribute/skill/misc/medicine = 35 ) /datum/job/clinicapprentice @@ -84,3 +84,4 @@ neck = /obj/item/storage/belt/pouch/cloth wrists = /obj/item/storage/keyring/clinicapprentice belt = /obj/item/storage/belt/leather/rope + beltl = /obj/item/storage/fancy/ifak diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index a00b1195766..ea6d80b160e 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -848,7 +848,7 @@ else clear_fullscreen("CMODE") - if(health <= crit_threshold || (!HAS_TRAIT(src, TRAIT_BLOODLOSS_IMMUNE) && CAN_HAVE_BLOOD(src) && (get_blood_volume() in -INFINITY to BLOOD_VOLUME_SURVIVE))) + if(HAS_TRAIT(src, TRAIT_CRITICAL_CONDITION) && !HAS_TRAIT(src, TRAIT_NOCRITOVERLAY)) var/severity = 0 switch(health) if(-20 to -10) diff --git a/code/modules/mob/living/carbon/carbon_medical.dm b/code/modules/mob/living/carbon/carbon_medical.dm index 13e168b28e6..7db682608f5 100644 --- a/code/modules/mob/living/carbon/carbon_medical.dm +++ b/code/modules/mob/living/carbon/carbon_medical.dm @@ -8,6 +8,7 @@ var/obj/item/organ/heart/heart = getorganslot(ORGAN_SLOT_HEART) if(!heart.current_blood) heart.current_blood = heart.max_blood_storage + set_heartattack(FALSE) return TRUE /mob/living/carbon/proc/check_pulse(mob/living/carbon/user) diff --git a/code/modules/mob/living/carbon/carbon_shock.dm b/code/modules/mob/living/carbon/carbon_shock.dm index 2e6d2999679..3467cef8660 100644 --- a/code/modules/mob/living/carbon/carbon_shock.dm +++ b/code/modules/mob/living/carbon/carbon_shock.dm @@ -58,7 +58,7 @@ var/maxbpshock = 0 var/obj/item/bodypart/damaged_bodypart for(var/obj/item/bodypart/bodypart as anything in bodyparts) - var/bpshock = bodypart.get_shock(FALSE, TRUE) + var/bpshock = bodypart.get_shock(FALSE) // make the choice of the organ depend on damage, // but also sometimes use one of the less damaged ones if((bpshock >= maxbpshock) && ((maxbpshock <= 0) || prob(70)) ) @@ -79,7 +79,7 @@ // Damage to internal organs hurts a lot. for(var/obj/item/organ/organ as anything in internal_organs) - if(DT_PROB(1, delta_time) && organ.can_feel_pain() && (organ.get_shock() >= 5)) + if(DT_PROB(1, delta_time) && organ.get_shock(TRUE) >= 5) var/obj/item/bodypart/parent = get_bodypart(organ.current_zone) if(parent) var/pain = 10 @@ -144,16 +144,16 @@ remove_movespeed_modifier(MOVESPEED_ID_CARDIAC_ARREST, TRUE) if(traumatic_shock >= max(SHOCK_STAGE_2, 0.8 * shock_stage)) - adjustShockStage(0.5 * delta_time * (ATTRIBUTE_MIDDLING/our_endurance)) + adjustShockStage(delta_time * (ATTRIBUTE_MIDDLING/our_endurance) * PAIN_SYSTEM_SPEED_MODIFIER) else if(!undergoing_cardiac_arrest()) setShockStage(min(shock_stage, SHOCK_STAGE_7)) - var/recovery = 0.5 * delta_time + var/recovery = delta_time //Lower shock faster the less pain we feel - if(traumatic_shock < 0.5 * shock_stage) - recovery += 0.5 * delta_time + if(traumatic_shock < shock_stage) + recovery += 1 if(traumatic_shock < 0.25 * shock_stage) - recovery += 0.5 * delta_time - adjustShockStage(-recovery * (our_endurance/ATTRIBUTE_MIDDLING)) + recovery += 1 + adjustShockStage(-recovery * (our_endurance/ATTRIBUTE_MIDDLING) * PAIN_SYSTEM_SPEED_MODIFIER/2) //Shock makes us slow if(shock_stage >= (SHOCK_STAGE_2 * (our_endurance/ATTRIBUTE_MIDDLING))) diff --git a/code/modules/mob/living/carbon/damage_procs.dm b/code/modules/mob/living/carbon/damage_procs.dm index fe077910c38..d93d645420a 100644 --- a/code/modules/mob/living/carbon/damage_procs.dm +++ b/code/modules/mob/living/carbon/damage_procs.dm @@ -198,10 +198,12 @@ /mob/living/carbon/adjustPainLoss(amount, updating_health = TRUE, forced = FALSE, required_status = null) if(!forced && (status_flags & GODMODE)) return 0 - var/old_amount = amount var/list/obj/item/bodypart/parts = get_painable_bodyparts(adding_pain = (amount > 0 ? TRUE : FALSE)) - if(!parts) + if(!length(parts)) return 0 + var/old_amount = amount + . = old_amount + amount *= CONFIG_GET(number/damage_multiplier) var/update = FALSE var/pain_per_part = amount / length(parts) if(pain_per_part < 0) @@ -218,7 +220,6 @@ updatehealth() if(update) update_damage_overlays() - return old_amount /mob/living/carbon/setPainLoss(amount, updating_health = TRUE, forced = FALSE) var/current = getPainLoss() @@ -247,13 +248,13 @@ return parts /mob/living/carbon/proc/endorphinate(forced = FALSE, silent = FALSE, local_sound = TRUE, flash = TRUE, special_sound) - var/endurance = GET_MOB_ATTRIBUTE_VALUE(src, STAT_ENDURANCE) - if(!forced && (TIMER_COOLDOWN_CHECK(src, COOLDOWN_CARBON_ENDORPHINATION) || (diceroll(endurance, context = DICE_CONTEXT_MENTAL) <= DICE_FAILURE))) + if(!forced && (TIMER_COOLDOWN_CHECK(src, COOLDOWN_CARBON_ENDORPHINATION))) return + var/endurance = GET_MOB_ATTRIBUTE_VALUE(src, STAT_ENDURANCE) var/current_body_amount = reagents.get_reagent_amount(/datum/reagent/medicine/endorphin) - var/endorphin_amount = clamp(endurance, 5, 29) - endorphin_amount = min(endorphin_amount, 30 - current_body_amount) + var/endorphin_amount = clamp(endurance, 10, 29) + endorphin_amount = min(endorphin_amount, 29 - current_body_amount) reagents?.add_reagent(/datum/reagent/medicine/endorphin, endorphin_amount) TIMER_COOLDOWN_START(src, COOLDOWN_CARBON_ENDORPHINATION, HAS_TRAIT(src, TRAIT_PSYDONIAN_GRIT) ? ENDORPHINATION_COOLDOWN_DURATION * 0.75 : ENDORPHINATION_COOLDOWN_DURATION) if(!silent) @@ -323,10 +324,7 @@ var/shock = 0 shock += SHOCK_MOD_CLONE * getCloneLoss() for(var/obj/item/bodypart/bodypart as anything in bodyparts) - shock += bodypart.get_shock(FALSE, TRUE) - - if(painkiller_included) - shock = max(0, shock - get_chem_effect(CE_PAINKILLER)) + shock += bodypart.get_shock(painkiller_included) return max(0, shock) diff --git a/code/modules/mob/living/carbon/examine.dm b/code/modules/mob/living/carbon/examine.dm index 65c7f9d2391..02d48574de8 100644 --- a/code/modules/mob/living/carbon/examine.dm +++ b/code/modules/mob/living/carbon/examine.dm @@ -513,10 +513,14 @@ if(!getorganslot(ORGAN_SLOT_BRAIN) || (stat == DEAD && (IsAdminGhost(user) || self_inspect))) . += span_boldred("[P[THEYRE]] dead.") - else if(appears_dead || stat >= UNCONSCIOUS) + else if(appears_dead || HAS_TRAIT(src, TRAIT_CRITICAL_CONDITION)) . += span_boldwarning("[P[THEYRE]] unconscious.") - else if(InCritical()) - . += span_warning("[P[THEYRE]] barely unconscious.") + else + switch(stat) + if(UNCONSCIOUS) + . += span_notice("[P[THEYRE]]n't responding to anything around [P[THEM]] and seem[p_s()] to be asleep.") + if(SOFT_CRIT) + . += span_notice("[P[THEYRE]] barely conscious.") // Blood volume if(!SEND_SIGNAL(src, COMSIG_DISGUISE_STATUS) && CAN_HAVE_BLOOD(src)) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 101ffece3a6..3e84d4d8b89 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -417,36 +417,38 @@ if(target.reagents?.get_reagent_amount(/datum/reagent/adrenaline) >= 1) epinephrine_mod += 3 - var/diceroll = diceroll(medical_skill+heart_exposed_mod+epinephrine_mod, dice_num = 8, context = DICE_CONTEXT_PHYSICAL) - if((diceroll >= DICE_SUCCESS) || !attributes) - if(prob(35) || (diceroll >= DICE_SUCCESS)) - target?.pump_heart(src) - target.set_heartattack(FALSE) - if(GETBRAINLOSS(target) >= BRAIN_DAMAGE_DEATH) - SETBRAINLOSS(target, BRAIN_DAMAGE_DEATH - 1) - if(HAS_TRAIT(target, TRAIT_NECRA_CURSE)) - to_chat(src, span_warning("Necra holds tight to this one.")) - return FALSE - if(diceroll >= DICE_CRIT_SUCCESS) - if(target.revive()) - target.grab_ghost(TRUE) - target.visible_message(span_warning("[target] limply spasms their muscles."), \ - span_userdanger("My muscles spasm as i am brought back to life!")) - target.emote("breathgasp") - target.adjust_jitter(100 SECONDS) - add_abstract_elastic_data(ELASCAT_MEDICAL, ELASDATA_CPR_REVIVE, 1) - target.apply_status_effect(/datum/status_effect/debuff/revive) - target.remove_client_colour(/datum/client_colour/monochrome/death) - chest.add_wound(/datum/wound/fracture/chest) - record_round_statistic(STATS_CPR_REVIVALS, 1) - else - to_chat(src, span_warning("[target] isn't responding to my resuscitation...")) + /// Always pump heart and break ribs unless you crit fail + /// Journeymen (average 35) have a 5% chance of doing a CPR revive + var/diceroll = diceroll(medical_skill+heart_exposed_mod+epinephrine_mod, dice_num = 13, context = DICE_CONTEXT_PHYSICAL) + if((diceroll >= DICE_SUCCESS) || (!attributes && prob(35))) + target.pump_heart(src) + if(HAS_TRAIT(target, TRAIT_NECRA_CURSE)) + to_chat(src, span_warning("Necra holds tight to this one.")) + return FALSE + if(target.stat < DEAD) // No point in running the revive check + return + if(GETBRAINLOSS(target) >= BRAIN_DAMAGE_DEATH) + SETBRAINLOSS(target, BRAIN_DAMAGE_DEATH - 1) + if(target.revive()) + chest.add_wound(/datum/wound/fracture/chest) + target.grab_ghost(TRUE) + target.visible_message(span_warning("[target] limply spasms their muscles."), \ + span_userdanger("My muscles spasm as i am brought back to life!")) + target.emote("breathgasp") + target.adjust_jitter(100 SECONDS) + add_abstract_elastic_data(ELASCAT_MEDICAL, ELASDATA_CPR_REVIVE, 1) + target.apply_status_effect(/datum/status_effect/debuff/revive) + record_round_statistic(STATS_CPR_REVIVALS, 1) + else + to_chat(src, span_warning("[target] isn't responding to my resuscitation...")) else if(diceroll <= DICE_CRIT_FAILURE) visible_message(span_danger("[src] botches the chest compressions!"), \ span_danger("I botch the chest compressions!"), span_hear("I hear frantic pressing!"), ignored_mobs = target) + else + target.pump_heart(src) /mob/living/carbon/human/cuff_resist(obj/item/I, breakouttime = 1 MINUTES, cuff_break = 0, instant = FALSE) if(..()) diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index cc79bf1d5e6..20982758c89 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -1013,7 +1013,7 @@ GLOBAL_LIST_EMPTY(roundstart_species) if(HAS_TRAIT(H, TRAIT_NOBREATH)) H.setOxyLoss(0) H.losebreath = 0 - else if((H.health < H.crit_threshold) && !HAS_TRAIT(H, TRAIT_NOCRITDAMAGE)) + else if(HAS_TRAIT(H, TRAIT_CRITICAL_CONDITION) && !HAS_TRAIT(H, TRAIT_NOCRITDAMAGE)) H.adjustOxyLoss(1) /datum/species/proc/spec_death(gibbed, mob/living/carbon/human/H) diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index fc16b55b0c5..d3a8f8dcf91 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -469,11 +469,11 @@ All effects don't start immediately, but rather get worse over time; the rate is * related situations (i.e not just cardiac arrest) */ /mob/living/carbon/proc/undergoing_cardiac_arrest() + if(!needs_heart()) + return FALSE var/obj/item/organ/heart/heart = getorganslot(ORGAN_SLOT_HEART) if(istype(heart) && heart.beating) return FALSE - else if(!needs_heart()) - return FALSE return TRUE /mob/living/proc/set_heartattack(status) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index a4d67aebb0a..8b59a1297f5 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -714,7 +714,7 @@ return if(!reaper) return - if (InCritical() || health <= 0) + if (HAS_TRAIT(src, TRAIT_CRITICAL_CONDITION)) log_message("Has [whispered ? "whispered his final words" : "succumbed to death"] while in [InFullCritical() ? "hard":"soft"] critical with [round(health, 0.1)] points of health!", LOG_ATTACK) if(istype(src.loc, /turf/open/water) && !HAS_TRAIT(src, TRAIT_NOBREATH) && body_position == LYING_DOWN && client) @@ -758,9 +758,6 @@ return FALSE return TRUE -/mob/living/proc/InCritical() - return (health <= crit_threshold && (stat == SOFT_CRIT || stat == UNCONSCIOUS || stat == HARD_CRIT)) - /mob/living/proc/InFullCritical() return ((health <= HEALTH_THRESHOLD_FULLCRIT) && (stat == UNCONSCIOUS || stat == HARD_CRIT)) @@ -1076,6 +1073,9 @@ setFireLoss(0, FALSE, TRUE) if(heal_flags & HEAL_STAM) adjust_stamina(-maximum_stamina, internal_regen = FALSE) + if(heal_flags & HEAL_PAIN_SHOCK) + setPainLoss(0, FALSE, TRUE) + setShockStage(0, FALSE, TRUE) if(heal_flags & HEAL_ESSENTIALS) set_nutrition(NUTRITION_LEVEL_FED + 50) @@ -2739,6 +2739,10 @@ . = ..() if(isnull(.)) return + + if(. <= UNCONSCIOUS || new_stat >= UNCONSCIOUS) + update_body() // to update eyes + switch(.) //Previous stat. if(CONSCIOUS) if(stat >= UNCONSCIOUS) @@ -2750,10 +2754,14 @@ if(pulledby) REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, PULLED_WHILE_SOFTCRIT_TRAIT) if(UNCONSCIOUS) - cure_blind(UNCONSCIOUS_TRAIT) + if(stat != HARD_CRIT) + cure_blind(UNCONSCIOUS_TRAIT) if(HARD_CRIT) if(stat != UNCONSCIOUS) cure_blind(UNCONSCIOUS_TRAIT) + REMOVE_TRAIT(src, TRAIT_DEAF, STAT_TRAIT) + if(DEAD) + REMOVE_TRAIT(src, TRAIT_DEAF, STAT_TRAIT) switch(stat) //Current stat. if(CONSCIOUS) if(. >= UNCONSCIOUS) @@ -2768,18 +2776,22 @@ ADD_TRAIT(src, TRAIT_CRITICAL_CONDITION, STAT_TRAIT) log_combat(src, src, "entered soft crit") if(UNCONSCIOUS) - become_blind(UNCONSCIOUS_TRAIT) - log_combat(src, src, "lost consciousness") + if(. != HARD_CRIT) + become_blind(UNCONSCIOUS_TRAIT) if(health <= crit_threshold && !HAS_TRAIT(src, TRAIT_NOSOFTCRIT)) ADD_TRAIT(src, TRAIT_CRITICAL_CONDITION, STAT_TRAIT) else REMOVE_TRAIT(src, TRAIT_CRITICAL_CONDITION, STAT_TRAIT) + log_combat(src, src, "lost consciousness") if(HARD_CRIT) if(. != UNCONSCIOUS) become_blind(UNCONSCIOUS_TRAIT) ADD_TRAIT(src, TRAIT_CRITICAL_CONDITION, STAT_TRAIT) + ADD_TRAIT(src, TRAIT_DEAF, STAT_TRAIT) + log_combat(src, src, "entered hard crit") if(DEAD) REMOVE_TRAIT(src, TRAIT_CRITICAL_CONDITION, STAT_TRAIT) + ADD_TRAIT(src, TRAIT_DEAF, STAT_TRAIT) log_combat(src, src, "died") if(!can_hear()) stop_sound_channel(CHANNEL_AMBIENCE) diff --git a/code/modules/mob/living/living_say.dm b/code/modules/mob/living/living_say.dm index da156aea4fa..c8c2472a956 100644 --- a/code/modules/mob/living/living_say.dm +++ b/code/modules/mob/living/living_say.dm @@ -88,7 +88,7 @@ if(check_whisper(original_message, forced)) return - var/in_crit = InCritical() + var/in_crit = HAS_TRAIT(src, TRAIT_CRITICAL_CONDITION) if(in_crit) // There are cheaper ways to do this, but they're less flexible, and this isn't ran all that often var/end = TRUE for(var/index in message_mods) @@ -466,7 +466,7 @@ message = derpspeech(message, stuttering) if(stuttering) - message = stutter(message) + message = stutter(message, stuttering) if(slurring) message = slur(message) diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 81b72393666..7b76c0875f8 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -327,28 +327,43 @@ return newphrase ///Adds stuttering to the message passed in -/proc/stutter(n) - var/te = html_decode(n) - var/t = ""//placed before the message. Not really sure what it's for. - n = length(n)//length of the entire word - var/p = null - p = 1//1 is the start of any word - while(p <= n)//while P, which starts at 1 is less or equal to N which is the length. - var/n_letter = copytext(te, p, p + 1)//copies text from a certain distance. In this case, only one letter at a time. - if (prob(80) && (ckey(n_letter) in list("b","c","d","f","g","h","j","k","l","m","n","p","q","r","s","t","v","w","x","y","z"))) - if (prob(10)) - n_letter = text("[n_letter]-[n_letter]-[n_letter]-[n_letter]")//replaces the current letter with this instead. - else - if (prob(20)) - n_letter = text("[n_letter]-[n_letter]-[n_letter]") - else - if (prob(5)) - n_letter = null - else - n_letter = text("[n_letter]-[n_letter]") - t = text("[t][n_letter]")//since the above is ran through for each letter, the text just adds up back to the original word. - p++//for each letter p is increased to find where the next letter will be. - return copytext(sanitize(t),1,MAX_MESSAGE_LEN) +/proc/stutter(phrase, power = 5) + phrase = html_decode(phrase) + var/leng = length(phrase) + . = "" + var/newletter = "" + var/rawchar = "" + var/static/regex/nostutter = regex(@@[aeiouAEIOU ""''()[\]{}.!?,:;_`~-]@) + var/word_start = TRUE // track if we're at the start of a new word + + for(var/i = 1, i <= leng, i += length(rawchar)) + rawchar = newletter = phrase[i] + + // spaces/punctuation reset the word boundary + if(nostutter.Find(rawchar)) + if(rawchar == " ") + word_start = TRUE + . += newletter + continue + + if(word_start) + word_start = FALSE + // stutter more aggressively on word-initial consonants + if(prob(50 + power)) + if(prob(10) && power >= 10) + newletter = "[newletter]-[newletter]-[newletter]-[newletter]" + else if(prob(25) && power >= 6) + newletter = "[newletter]-[newletter]-[newletter]" + else + newletter = "[newletter]-[newletter]" + else + // rare mid-word stutter, much lower chance + if(prob(1 + power)) + newletter = "[newletter]-[newletter]" + + . += newletter + + return sanitize(.) ///Convert a message to derpy speak /proc/derpspeech(message, stuttering) diff --git a/code/modules/reagents/chemistry/reagents.dm b/code/modules/reagents/chemistry/reagents.dm index 26c1916b563..f624aa647df 100644 --- a/code/modules/reagents/chemistry/reagents.dm +++ b/code/modules/reagents/chemistry/reagents.dm @@ -70,6 +70,10 @@ GLOBAL_LIST_INIT(name2reagent, build_name2reagent()) var/liver_chemical = TRUE /// Boiling point in Kelvin. Used by chem_separator to determine distillation order. var/boiling_point = T0C + 100 + /// A list of traits to apply while the reagent is being metabolized. + var/list/metabolized_traits + /// A list of traits to apply while the reagent is in a mob. + var/list/added_traits /datum/reagent/Destroy() // This should only be called by the holder, so it's already handled clearing its references . = ..() @@ -143,25 +147,29 @@ GLOBAL_LIST_INIT(name2reagent, build_name2reagent()) return recipe_quality ? recipe_quality : base_recipe_quality // Called when this reagent is first added to a mob -/datum/reagent/proc/on_mob_add(mob/living/L) - return +/datum/reagent/proc/on_mob_add(mob/living/affected_mob) + if(added_traits) + affected_mob.add_traits(added_traits, "base:[type]") // Called when this reagent is removed while inside a mob -/datum/reagent/proc/on_mob_delete(mob/living/L) - return +/datum/reagent/proc/on_mob_delete(mob/living/affected_mob) + REMOVE_TRAITS_IN(affected_mob, "base:[type]") // Called when this reagent first starts being metabolized by a liver -/datum/reagent/proc/on_mob_metabolize(mob/living/L) - return +/datum/reagent/proc/on_mob_metabolize(mob/living/affected_mob) + SHOULD_CALL_PARENT(TRUE) + if(metabolized_traits) + affected_mob.add_traits(metabolized_traits, "metabolize:[type]") + +// Called when this reagent stops being metabolized by a liver +/datum/reagent/proc/on_mob_end_metabolize(mob/living/affected_mob) + SHOULD_CALL_PARENT(TRUE) + REMOVE_TRAITS_IN(affected_mob, "metabolize:[type]") /// Called when this liquid is aerated (sprinklers vents and pumps for now) /datum/reagent/proc/on_aeration(volume, turf/turf) return -// Called when this reagent stops being metabolized by a liver -/datum/reagent/proc/on_mob_end_metabolize(mob/living/L) - return - /// Called when a reagent is inside of a mob when they are dead /datum/reagent/proc/on_mob_dead(mob/living/carbon/C, delta_time) if(!dead_head) diff --git a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm index 92d83f5bf46..d4a233d098d 100644 --- a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm @@ -45,11 +45,11 @@ /datum/reagent/consumable/ethanol/on_mob_metabolize(mob/living/L) . = ..() - L.increase_chem_effect(CE_PAINKILLER, boozepwr/2, "[type]") + L.increase_chem_effect(CE_PAINKILLER, boozepwr * 0.3, "[type]") /datum/reagent/consumable/ethanol/on_mob_end_metabolize(mob/living/L) . = ..() - L.decrease_chem_effect(CE_PAINKILLER, boozepwr/2, "[type]") + L.decrease_chem_effect(CE_PAINKILLER, boozepwr * 0.3, "[type]") /datum/reagent/consumable/ethanol/reaction_obj(obj/O, reac_volume) . = ..() @@ -592,8 +592,9 @@ All effects don't start immediately, but rather get worse over time; the rate is ..() . = 1 -/datum/reagent/consumable/ethanol/murkwine/on_mob_end_metabolize(mob/living/M) - M.remove_status_effect(/datum/status_effect/buff/murkwine) +/datum/reagent/consumable/ethanol/murkwine/on_mob_end_metabolize(mob/living/affected_mob) + . = ..() + affected_mob.remove_status_effect(/datum/status_effect/buff/murkwine) /datum/reagent/consumable/ethanol/nocshine // wait, no, NOCSHINE name = "Noc's Shine" @@ -612,8 +613,9 @@ All effects don't start immediately, but rather get worse over time; the rate is ..() . = 1 -/datum/reagent/consumable/ethanol/nocshine/on_mob_end_metabolize(mob/living/M) - M.remove_status_effect(/datum/status_effect/buff/nocshine) +/datum/reagent/consumable/ethanol/nocshine/on_mob_end_metabolize(mob/living/affected_mob) + . = ..() + affected_mob.remove_status_effect(/datum/status_effect/buff/nocshine) /datum/reagent/consumable/ethanol/luxwine // oh no. name = "Luxintenebre" // lux left w/ sugar in a darkened place for quite some time... U could say... Light in Darkness..... @@ -631,8 +633,9 @@ All effects don't start immediately, but rather get worse over time; the rate is M.adjustFireLoss(-1*REM * efficiency, 0) ..() -/datum/reagent/consumable/ethanol/luxwine/on_mob_end_metabolize(mob/living/M) - M.remove_status_effect(/datum/status_effect/buff/lux_drank) +/datum/reagent/consumable/ethanol/luxwine/on_mob_end_metabolize(mob/living/affected_mob) + . = ..() + affected_mob.remove_status_effect(/datum/status_effect/buff/lux_drank) /datum/reagent/consumable/ethanol/luxwine/aged name = "Aged Luxintenebre" diff --git a/code/modules/reagents/chemistry/reagents/drug_reagents.dm b/code/modules/reagents/chemistry/reagents/drug_reagents.dm index c18f6fcea77..1dfedf1d652 100644 --- a/code/modules/reagents/chemistry/reagents/drug_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drug_reagents.dm @@ -27,10 +27,11 @@ M.apply_status_effect(/datum/status_effect/buff/weed) M.overlay_fullscreen("weedsm", /atom/movable/screen/fullscreen/weedsm) -/datum/reagent/drug/space_drugs/on_mob_end_metabolize(mob/living/M) - M.set_drugginess(0) - M.clear_fullscreen("weedsm") - M.remove_status_effect(/datum/status_effect/buff/weed) +/datum/reagent/drug/space_drugs/on_mob_end_metabolize(mob/living/affected_mob) + . = ..() + affected_mob.set_drugginess(0) + affected_mob.clear_fullscreen("weedsm") + affected_mob.remove_status_effect(/datum/status_effect/buff/weed) /atom/movable/screen/fullscreen/weedsm icon_state = "smok" diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index 7d167a85ee9..8eb366fd159 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -358,14 +358,7 @@ name = "SATE" color = "#e46363" glows = TRUE - -/datum/reagent/sate/on_mob_add(mob/living/L) - . = ..() - ADD_TRAIT(L, TRAIT_SATE, type) - -/datum/reagent/sate/on_mob_delete(mob/living/L) - . = ..() - REMOVE_TRAIT(L, TRAIT_SATE, type) + added_traits = list(TRAIT_SATE) /datum/reagent/devour name = "DEVOUR" diff --git a/code/modules/reagents/reagent_containers/powder.dm b/code/modules/reagents/reagent_containers/powder.dm index 2b68316031f..88086525c36 100644 --- a/code/modules/reagents/reagent_containers/powder.dm +++ b/code/modules/reagents/reagent_containers/powder.dm @@ -132,6 +132,7 @@ color = "#60A584" // rgb: 96, 165, 132 overdose_threshold = 16 metabolization_rate = 0.2 + metabolized_traits = list(TRAIT_DRUQK) /atom/movable/screen/fullscreen/druqks icon_state = "spa" @@ -153,20 +154,20 @@ M.sate_addiction(/datum/quirk/vice/junkie) ..() -/datum/reagent/druqks/on_mob_metabolize(mob/living/M) - M.overlay_fullscreen("druqk", /atom/movable/screen/fullscreen/druqks) - M.set_drugginess(30 SECONDS) - if(M.client) - ADD_TRAIT(M, TRAIT_DRUQK, "based") - M.refresh_looping_ambience() - -/datum/reagent/druqks/on_mob_end_metabolize(mob/living/M) - M.clear_fullscreen("druqk") - M.set_drugginess(0) - M.remove_status_effect(/datum/status_effect/buff/druqks) - if(M.client) - REMOVE_TRAIT(M, TRAIT_DRUQK, "based") - M.refresh_looping_ambience() +/datum/reagent/druqks/on_mob_metabolize(mob/living/affected_mob) + . = ..() + affected_mob.overlay_fullscreen("druqk", /atom/movable/screen/fullscreen/druqks) + affected_mob.set_drugginess(30 SECONDS) + if(affected_mob.client) + affected_mob.refresh_looping_ambience() + +/datum/reagent/druqks/on_mob_end_metabolize(mob/living/affected_mob) + . = ..() + affected_mob.clear_fullscreen("druqk") + affected_mob.set_drugginess(0) + affected_mob.remove_status_effect(/datum/status_effect/buff/druqks) + if(affected_mob.client) + affected_mob.refresh_looping_ambience() /datum/reagent/druqks/overdose_process(mob/living/M) M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 0.25*REM) @@ -194,7 +195,7 @@ /datum/reagent/ozium/on_mob_metabolize(mob/living/L) . = ..() - L.add_chem_effect(CE_PAINKILLER, 100, "[type]") + L.add_chem_effect(CE_PAINKILLER, 20, "[type]") L.add_chem_effect(CE_STIMULANT, 2, "[type]") /datum/reagent/ozium/on_mob_end_metabolize(mob/living/L) @@ -235,15 +236,17 @@ overdose_threshold = 50 metabolization_rate = 0.2 -/datum/reagent/moondust/on_mob_metabolize(mob/living/M) - animate(M.client, pixel_y = 1, time = 1, loop = -1, flags = ANIMATION_RELATIVE) +/datum/reagent/moondust/on_mob_metabolize(mob/living/affected_mob) + . = ..() + animate(affected_mob.client, pixel_y = 1, time = 1, loop = -1, flags = ANIMATION_RELATIVE) animate(pixel_y = -1, time = 1, flags = ANIMATION_RELATIVE) - M.add_chem_effect(CE_PULSE, 1, "[type]") + affected_mob.add_chem_effect(CE_PULSE, 1, "[type]") -/datum/reagent/moondust/on_mob_end_metabolize(mob/living/M) - M.remove_status_effect(/datum/status_effect/buff/moondust) - animate(M.client) - M.remove_chem_effect(CE_PULSE, "[type]") +/datum/reagent/moondust/on_mob_end_metabolize(mob/living/affected_mob) + . = ..() + affected_mob.remove_status_effect(/datum/status_effect/buff/moondust) + animate(affected_mob.client) + affected_mob.remove_chem_effect(CE_PULSE, "[type]") /datum/reagent/moondust/on_mob_life(mob/living/carbon/M, efficiency) SEND_SIGNAL(src, COMSIG_DRUG_INDULGE) @@ -280,18 +283,20 @@ overdose_threshold = 50 metabolization_rate = 0.2 -/datum/reagent/moondust_purest/on_mob_metabolize(mob/living/M) - M.playsound_local(M, 'sound/ravein/small/hello_my_friend.ogg', 100, FALSE) - M.overlay_fullscreen("purest_kaif", /atom/movable/screen/fullscreen/purest) - animate(M.client, pixel_y = 1, time = 1, loop = -1, flags = ANIMATION_RELATIVE) +/datum/reagent/moondust_purest/on_mob_metabolize(mob/living/affected_mob) + . = ..() + affected_mob.playsound_local(affected_mob, 'sound/ravein/small/hello_my_friend.ogg', 100, FALSE) + affected_mob.overlay_fullscreen("purest_kaif", /atom/movable/screen/fullscreen/purest) + animate(affected_mob.client, pixel_y = 1, time = 1, loop = -1, flags = ANIMATION_RELATIVE) animate(pixel_y = -1, time = 1, flags = ANIMATION_RELATIVE) - M.add_chem_effect(CE_PULSE, 2, "[type]") + affected_mob.add_chem_effect(CE_PULSE, 2, "[type]") -/datum/reagent/moondust_purest/on_mob_end_metabolize(mob/living/M) - animate(M.client) - M.clear_fullscreen("purest_kaif") - M.remove_status_effect(/datum/status_effect/buff/moondust_purest) - M.remove_chem_effect(CE_PULSE, "[type]") +/datum/reagent/moondust_purest/on_mob_end_metabolize(mob/living/affected_mob) + . = ..() + animate(affected_mob.client) + affected_mob.clear_fullscreen("purest_kaif") + affected_mob.remove_status_effect(/datum/status_effect/buff/moondust_purest) + affected_mob.remove_chem_effect(CE_PULSE, "[type]") /datum/reagent/moondust_purest/on_mob_life(mob/living/carbon/M, efficiency) SEND_SIGNAL(src, COMSIG_DRUG_INDULGE) diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index b233449b963..6907016066e 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -247,6 +247,8 @@ /obj/item/bodypart/proc/on_chronic_fracture_life() if(!prob(2)) return + if(pain_dam >= SHOCK_STAGE_3) + return if(owner.encumbrance >= ENCUMBRANCE_HEAVY) var/pain_amount = rand(3, 5) if(owner.encumbrance >= ENCUMBRANCE_EXTREME) @@ -257,21 +259,21 @@ add_pain(pain_amount) /obj/item/bodypart/proc/on_arthritis_life() - if(prob(2) && pain_dam < max_pain_damage * 0.1) - add_pain(rand(1, 2)) + if(prob(2) && pain_dam < SHOCK_STAGE_1) + add_pain(rand(SHOCK_STAGE_1 * 0.5, SHOCK_STAGE_1)) var/pain_msg = pick("Your [name] throbs with arthritic pain!", "A sharp ache shoots through your [name]!", "Your [name] feels stiff and painful!") to_chat(owner, span_warning(pain_msg)) - if(prob(1) && owner.loc && pain_dam < max_pain_damage * 0.15) + if(prob(1) && owner.loc && pain_dam < (SHOCK_STAGE_2 / 2)) if(SSParticleWeather.runningWeather && SSParticleWeather.runningWeather.can_weather(owner)) - add_pain(rand(2, 3)) + add_pain(rand(SHOCK_STAGE_1 * 0.5, SHOCK_STAGE_1)) to_chat(owner, span_warning("The weather makes your arthritis act up.")) /obj/item/bodypart/proc/on_migraine_life() - if(prob(2) && pain_dam < max_pain_damage * 0.2) - add_pain(rand(2, 3)) + if(prob(2) && pain_dam < SHOCK_STAGE_2) + add_pain(rand(SHOCK_STAGE_1 * 0.5, SHOCK_STAGE_2 * 0.5)) if(prob(30)) owner.set_eye_blur_if_lower(rand(6 SECONDS, 12 SECONDS)) @@ -280,8 +282,8 @@ to_chat(owner, span_warning("A migraine headache begins to build.")) if(prob(1)) - if(pain_dam > max_pain_damage * 0.2 && owner.loc?.luminosity > 2) - add_pain(rand(3, 5)) + if(pain_dam < SHOCK_STAGE_2 && owner.loc?.luminosity > 2) + add_pain(rand(SHOCK_STAGE_1 * 0.3, SHOCK_STAGE_1 * 0.5)) to_chat(owner, span_warning("The flickering flames make your migraine worse!")) /obj/item/bodypart/proc/update_pain_coeff() @@ -396,7 +398,7 @@ var/multiplier = 1 if(owner.body_position == LYING_DOWN) multiplier *= pain_heal_rest_multiplier - remove_pain(amount = (pain_heal_tick * multiplier * (0.5 * delta_time)), updating_health = FALSE) + remove_pain(amount = (pain_heal_tick * multiplier * delta_time * (PAIN_SYSTEM_SPEED_MODIFIER/10)), updating_health = FALSE) if(can_decay()) if(germ_level || (getorganslotefficiency(ORGAN_SLOT_ARTERY) < ORGAN_FAILING_EFFICIENCY)) update_germs(delta_time, times_fired) @@ -796,13 +798,13 @@ /obj/item/bodypart/proc/add_pain(amount = 0, updating_health = TRUE, required_status = null) if(required_status && (status != required_status)) return + if(amount <= 0) + return if(!can_feel_pain()) return - var/can_inflict = max_pain_damage - pain_dam - amount *= CONFIG_GET(number/damage_multiplier) + amount = min(max_pain_damage - pain_dam, amount) amount -= owner.get_chem_effect(CE_PAINKILLER)/PAINKILLER_DIVISOR - amount = min(can_inflict, amount) - pain_dam = round(pain_dam + max(amount, 0), DAMAGE_PRECISION) + pain_dam = round(pain_dam + amount, DAMAGE_PRECISION) if(updating_health) owner.update_shock() if(can_be_disabled) @@ -814,9 +816,9 @@ /obj/item/bodypart/proc/remove_pain(amount = 0, updating_health = TRUE, required_status = null) if(required_status && (status != required_status)) return - if(amount > pain_dam) - amount = pain_dam - pain_dam = FLOOR(pain_dam - max(abs(amount), 0), DAMAGE_PRECISION) + if(amount <= 0) + return + pain_dam = max(FLOOR(pain_dam - min(amount, pain_dam), DAMAGE_PRECISION), 0) if(updating_health) owner?.update_shock() if(can_be_disabled) @@ -835,7 +837,7 @@ return remove_pain(abs(diff), updating_health, required_status) /// Returns how much pain we are dealing with right now, taking other damage types into account -/obj/item/bodypart/proc/get_shock(painkiller_included = FALSE, nerve_included = TRUE) +/obj/item/bodypart/proc/get_shock(painkiller_included = FALSE) if(!can_feel_pain()) return 0 //Multiply our total pain damage by this @@ -845,10 +847,6 @@ multiplier *= 0.75 if(multiplier <= 0) return 0 - if(ishuman(owner)) - var/mob/living/carbon/human/human_owner = owner - if(human_owner.dna?.species) - multiplier *= human_owner.dna?.species.pain_mod var/constant_pain = 0 constant_pain += SHOCK_MOD_BRUTE * brute_dam constant_pain += SHOCK_MOD_BURN * burn_dam @@ -958,7 +956,7 @@ update_disabled() if(updating_health) owner.updatehealth() - if(updating_shock && get_shock(FALSE, TRUE) >= DAMAGE_PRECISION) + if(updating_shock && get_shock(FALSE) >= DAMAGE_PRECISION) owner.update_shock() . = TRUE consider_processing() diff --git a/code/modules/surgery/organs/_organ.dm b/code/modules/surgery/organs/_organ.dm index 8de9a95f6b0..116b5526ed8 100644 --- a/code/modules/surgery/organs/_organ.dm +++ b/code/modules/surgery/organs/_organ.dm @@ -630,7 +630,7 @@ var/effective_efficiency = LAZYACCESS(organ_efficiency, slot) if(isnull(effective_efficiency)) return effective_efficiency - var/static/list/no_bleedout_organs = list(ORGAN_SLOT_ARTERY) + var/static/list/no_bleedout_organs = list(ORGAN_SLOT_ARTERY, ORGAN_SLOT_HEART) if(slot in no_bleedout_organs) if(is_failing_without_bleedout()) return 0 @@ -803,7 +803,7 @@ return round(maxHealth * pain_multiplier, DAMAGE_PRECISION) var/constant_pain = damage if(painkiller_included) - constant_pain -= (owner.get_chem_effect(CE_PAINKILLER)/PAINKILLER_DIVISOR) + constant_pain -= owner.get_chem_effect(CE_PAINKILLER)/PAINKILLER_DIVISOR return max(FLOOR(constant_pain * pain_multiplier, DAMAGE_PRECISION), 0) GLOBAL_LIST_INIT(all_organ_slots, get_all_slots()) diff --git a/code/modules/surgery/organs/_organ_storage.dm b/code/modules/surgery/organs/_organ_storage.dm index a8c3e47f5bc..0e13c9c8203 100644 --- a/code/modules/surgery/organs/_organ_storage.dm +++ b/code/modules/surgery/organs/_organ_storage.dm @@ -343,7 +343,7 @@ update_insides() return var/mob/living/carbon/carbon_parent = parent - if(!carbon_parent.IsUnconscious() && (carbon_parent.get_chem_effect(CE_PAINKILLER) < 50)) + if(!carbon_parent.IsUnconscious() && (carbon_parent.get_chem_effect(CE_PAINKILLER) < 30)) carbon_parent.emote("scream") /* if(!CHECK_BITFIELD(O.organ_flags, ORGAN_CUT_AWAY)) diff --git a/code/modules/surgery/organs/internal/heart.dm b/code/modules/surgery/organs/internal/heart.dm index 1771ac5e6ea..3c1335713a0 100644 --- a/code/modules/surgery/organs/internal/heart.dm +++ b/code/modules/surgery/organs/internal/heart.dm @@ -108,6 +108,7 @@ . = ..() if(!special) addtimer(CALLBACK(src, PROC_REF(stop_if_unowned)), 12 SECONDS) + owner?.stop_sound_channel(CHANNEL_HEARTBEAT) /obj/item/organ/heart/attack_self(mob/user) . = ..() @@ -118,12 +119,12 @@ addtimer(CALLBACK(src, PROC_REF(stop_if_unowned)), 8 SECONDS) /obj/item/organ/heart/proc/can_stop() - if(beating) - return TRUE - return FALSE + return beating /obj/item/organ/heart/proc/stop_if_unowned() - if(!owner) + if(QDELETED(src)) + return + if(isnull(owner)) Stop() /obj/item/organ/heart/proc/Stop() diff --git a/code/modules/surgery/organs/organ_processing/heart.dm b/code/modules/surgery/organs/organ_processing/heart.dm index 3facfce7006..a5e4d3a415d 100644 --- a/code/modules/surgery/organs/organ_processing/heart.dm +++ b/code/modules/surgery/organs/organ_processing/heart.dm @@ -98,9 +98,10 @@ // Finally, check if we should go into cardiac arrest // cardiovascular shock, not enough liquid to pump - var/should_stop = (owner.get_blood_circulation() < GET_EFFECTIVE_BLOOD_VOL(BLOOD_VOLUME_SURVIVE, owner.total_blood_req)) && DT_PROB(40, delta_time) + /* Your heart doesn't stop because you ran out of blood, it stops because it's dead */ + // var/should_stop = (owner.get_blood_circulation() < GET_EFFECTIVE_BLOOD_VOL(BLOOD_VOLUME_SURVIVE, owner.total_blood_req)) && DT_PROB(40, delta_time) // brain failing to work heart properly - should_stop = should_stop || DT_PROB(CEILING(max(0, GETBRAINLOSS(owner) - (owner.maxHealth * 0.5)) / 2, 1), delta_time) + var/should_stop = DT_PROB(CEILING(max(0, GETBRAINLOSS(owner) - (owner.maxHealth * 0.5)) / 2, 1), delta_time) // erratic heart patterns, usually caused by oxyloss should_stop = should_stop || ((owner.pulse >= PULSE_THREADY) && DT_PROB(6, delta_time)) From c30b2605934ab4061eb8ddb3dfee9f0645377ea0 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Tue, 12 May 2026 17:02:20 -0700 Subject: [PATCH 24/59] can_feel_pain --- code/_onclick/hud/screen_objects.dm | 2 +- code/datums/wounds/black_briar_curse.dm | 4 +- code/game/objects/items/inquisition_relics.dm | 2 +- code/game/objects/structures/maneater.dm | 2 +- code/game/objects/structures/rogueflora.dm | 37 +++++++++++-------- code/game/objects/structures/tangler.dm | 2 +- .../mob/living/carbon/carbon_defense.dm | 4 +- .../mob/living/carbon/human/species.dm | 2 +- code/modules/mob/living/life.dm | 2 +- .../hostile/retaliate/creacher/zizoid.dm | 7 +++- .../pointed/aoe/on_turf/flower_field.dm | 2 +- code/modules/surgery/bodyparts/_bodyparts.dm | 5 +-- .../bodyparts/bodypart_dismemberment.dm | 2 +- code/modules/surgery/organs/_organ.dm | 4 +- 14 files changed, 42 insertions(+), 35 deletions(-) diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index f092510d286..dd4357e4fc7 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -1266,7 +1266,7 @@ for(var/obj/item/bodypart/BP as anything in H.bodyparts) if(BP.body_zone in missing_bodyparts_zones) continue - if(HAS_TRAIT(H, TRAIT_NOPAIN)) + if(!H.can_feel_pain()) var/mutable_appearance/limby = mutable_appearance('icons/mob/roguehud64.dmi', "[H.gender == "male" ? "m" : "f"]-[BP.body_zone]") limby.color = "#78a8ba" . += limby diff --git a/code/datums/wounds/black_briar_curse.dm b/code/datums/wounds/black_briar_curse.dm index ef64c3158d3..221491f73c0 100644 --- a/code/datums/wounds/black_briar_curse.dm +++ b/code/datums/wounds/black_briar_curse.dm @@ -175,7 +175,7 @@ infection_percent = min(1, infection_percent + heal_percent) if(can_examine) owner.visible_message(span_danger("The briar gets worse!"), span_briar("I feel thorns digging into me!")) //don't heal as malum, he likes this shit - if(!HAS_TRAIT(owner, TRAIT_NOPAIN)) + if(owner.can_feel_pain()) if(infection_percent >= BBC_STAGE_LATE && prob(30)) owner.emote("firescream") else if(infection_percent >= BBC_STAGE_MID && prob(50)) @@ -276,7 +276,7 @@ return owner.adjust_energy(max(0, (GET_MOB_ATTRIBUTE_VALUE(owner, STAT_ENDURANCE) - 20)) * (SSmobs.wait * 0.1) * infection_percent) if(infection_percent >= 1) - if(!HAS_TRAIT(owner, TRAIT_NOPAIN)) + if(owner.can_feel_pain()) to_chat(owner, span_briar("IT HURTS! IT HURTS!")) if(prob(80)) owner.emote(pick("agony", "painscream", "firescream", "laugh")) diff --git a/code/game/objects/items/inquisition_relics.dm b/code/game/objects/items/inquisition_relics.dm index ed9a00d1ced..081398788ad 100644 --- a/code/game/objects/items/inquisition_relics.dm +++ b/code/game/objects/items/inquisition_relics.dm @@ -571,7 +571,7 @@ if(active && working && !full) if(do_after(user, 20, M)) M.flash_fullscreen("redflash3") - if(!HAS_TRAIT(M, TRAIT_NOPAIN) || !HAS_TRAIT(M, TRAIT_NOPAINSTUN)) + if(M.can_feel_pain()) if(prob(15)) M.emote("whimper", forced = TRUE) else if(prob(15)) diff --git a/code/game/objects/structures/maneater.dm b/code/game/objects/structures/maneater.dm index d6bffc9d76c..7bd85640d74 100644 --- a/code/game/objects/structures/maneater.dm +++ b/code/game/objects/structures/maneater.dm @@ -146,7 +146,7 @@ return buckle_mob(L, TRUE, check_loc = FALSE) START_PROCESSING(SSobj, src) - if(!HAS_TRAIT(L, TRAIT_NOPAIN)) + if(L.can_feel_pain()) L.emote("painscream", forced = TRUE) visible_message(span_danger("[src] snatches [L]!")) playsound(src, pick(attack_sounds), 100, FALSE, -1) diff --git a/code/game/objects/structures/rogueflora.dm b/code/game/objects/structures/rogueflora.dm index 4a1a29ae788..156628ae7e5 100644 --- a/code/game/objects/structures/rogueflora.dm +++ b/code/game/objects/structures/rogueflora.dm @@ -484,21 +484,28 @@ else to_chat(L, span_warning("I get stuck in \a [src].")) - if(ishuman(L)) - var/mob/living/carbon/human/H = L - var/was_hard_collision = (H.m_intent == MOVE_INTENT_RUN || H.throwing || H.currently_z_moving || HAS_TRAIT(H, TRAIT_STUMBLE)) - if(was_hard_collision) - var/obj/item/bodypart/BP = pick(H.bodyparts) - BP.receive_damage(10) - to_chat(H, span_warning("A thorn [pick("slices","cuts","nicks")] my [BP.name].")) - if((prob(20)) && !HAS_TRAIT(src, TRAIT_PIERCEIMMUNE)) - var/obj/item/natural/thorn/TH = new(src.loc) - BP.add_embedded_object(TH, silent = TRUE) - to_chat(H, span_danger("\A [TH] impales my [BP.name].")) - if(!HAS_TRAIT(H, TRAIT_NOPAIN)) - H.emote("painscream") - L.Stun(3 SECONDS) //that fucking hurt - H.consider_ambush() + if(!L.client) + return + if(L.m_intent != MOVE_INTENT_SNEAK && !HAS_TRAIT(L, TRAIT_MOVE_FLYING) && prob(20)) + L.consider_ambush() + + if(!ishuman(L)) + return + var/mob/living/carbon/human/H = L + var/was_hard_collision = (H.m_intent == MOVE_INTENT_RUN || H.throwing || H.currently_z_moving || HAS_TRAIT(H, TRAIT_STUMBLE)) + if(!was_hard_collision) + return + var/obj/item/bodypart/BP = pick(H.bodyparts) + BP.receive_damage(10) + to_chat(H, span_warning("A thorn [pick("slices","cuts","nicks")] my [BP.name].")) + if(HAS_TRAIT(src, TRAIT_PIERCEIMMUNE)) + return + var/obj/item/natural/thorn/TH = new(src.loc) + BP.add_embedded_object(TH, silent = TRUE) + to_chat(H, span_danger("\A [TH] impales my [BP.name].")) + if(H.can_feel_pain()) + H.emote("painscream") + L.Stun(3 SECONDS) //that fucking hurt /obj/structure/flora/grass/bush/wall name = "great bush" diff --git a/code/game/objects/structures/tangler.dm b/code/game/objects/structures/tangler.dm index eea06d30a53..7b153031558 100644 --- a/code/game/objects/structures/tangler.dm +++ b/code/game/objects/structures/tangler.dm @@ -126,7 +126,7 @@ update_appearance(UPDATE_ICON_STATE | UPDATE_NAME) buckle_mob(L, TRUE, check_loc = FALSE) START_PROCESSING(SSobj, src) - if(!HAS_TRAIT(L, TRAIT_NOPAIN)) + if(L.can_feel_pain()) L.emote("painscream", forced = FALSE) src.visible_message("[src] snatches [L]!") playsound(src, "plantcross", 100, FALSE, -1) diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 79b1f880b51..945823f3071 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -395,7 +395,7 @@ C.electrocute_act(shock_damage*0.75, src, 1, flags) //Stun var/should_stun = (!(flags & SHOCK_TESLA) || siemens_coeff > 0.5) && !(flags & SHOCK_NOSTUN) - if(!HAS_TRAIT(src, TRAIT_NOPAIN)) + if(can_feel_pain()) if(should_stun && !HAS_TRAIT(src, TRAIT_NOPAINSTUN) && !has_status_effect(/datum/status_effect/shock_recovery)) Paralyze(3 SECONDS) //Jitter and other fluff. @@ -408,7 +408,7 @@ ///Called slightly after electrocute act to apply a secondary stun. /mob/living/carbon/proc/secondary_shock(should_stun) - if(should_stun && !HAS_TRAIT(src, TRAIT_NOPAINSTUN) && !has_status_effect(/datum/status_effect/shock_recovery)) + if(should_stun && !HAS_TRAIT(src, TRAIT_NOPAINSTUN) && can_feel_pain() && !has_status_effect(/datum/status_effect/shock_recovery)) Paralyze(6 SECONDS) apply_shock_paralyze_immunity(12 SECONDS) diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index 20982758c89..7227e89bdf5 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -1991,7 +1991,7 @@ GLOBAL_LIST_EMPTY(roundstart_species) if(BRUTE) H.damageoverlaytemp = 20 damage_amount = forced ? damage : damage * hit_percent * H.physiology.brute_mod - if(!HAS_TRAIT(H, TRAIT_NOPAIN)) + if(H.can_feel_pain()) if(damage_amount > 5) H.AdjustSleeping(-50) if(prob(damage_amount * 3)) diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 47d12bede7a..caf0d82653b 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -126,7 +126,7 @@ /mob/living/proc/handle_random_events() //random painstun - if(stat || HAS_TRAIT(src, TRAIT_NOPAINSTUN)) + if(stat || HAS_TRAIT(src, TRAIT_NOPAINSTUN) || !can_feel_pain()) return if(!MOBTIMER_FINISHED(src, MT_PAINSTUN, 60 SECONDS)) return diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/creacher/zizoid.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/creacher/zizoid.dm index 39e9a8c7bd3..ab829001f3b 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/creacher/zizoid.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/creacher/zizoid.dm @@ -69,6 +69,7 @@ base_strength = 66 base_speed = 66 base_endurance = 66 + move_resist = MOVE_FORCE_OVERPOWERING /mob/living/simple_animal/hostile/retaliate/blood/ascended/examine(mob/user) . = ..() @@ -77,8 +78,10 @@ /mob/living/simple_animal/hostile/retaliate/blood/ascended/Initialize() . = ..() set_light(5,5,5, l_color = LIGHT_COLOR_RED) - ADD_TRAIT(src, TRAIT_CRITICAL_RESISTANCE, TRAIT_GENERIC) - ADD_TRAIT(src, TRAIT_BLOODLOSS_IMMUNE, TRAIT_GENERIC) + ADD_TRAIT(src, TRAIT_CRITICAL_RESISTANCE, INNATE_TRAIT) + ADD_TRAIT(src, TRAIT_NOPAIN, INNATE_TRAIT) + ADD_TRAIT(src, TRAIT_NOPAINSTUN, INNATE_TRAIT) + ADD_TRAIT(src, TRAIT_NOBREATH, INNATE_TRAIT) /mob/living/simple_animal/hostile/retaliate/blood/ascended/get_sound(input) switch(input) diff --git a/code/modules/spells/spell_types/pointed/aoe/on_turf/flower_field.dm b/code/modules/spells/spell_types/pointed/aoe/on_turf/flower_field.dm index 7c5c7111748..61e24c894ca 100644 --- a/code/modules/spells/spell_types/pointed/aoe/on_turf/flower_field.dm +++ b/code/modules/spells/spell_types/pointed/aoe/on_turf/flower_field.dm @@ -167,7 +167,7 @@ if (!L.buckled && prob(35)) L.visible_message(span_warning("The euphorbia vines entwine [L]!")) if (buckle_mob(L, TRUE, check_loc = FALSE)) - if (!HAS_TRAIT(L, TRAIT_NOPAIN)) + if(L.can_feel_pain()) L.emote("agony") L.Stun(2 SECONDS) if (!HAS_TRAIT(L, TRAIT_PIERCEIMMUNE)) diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index 6907016066e..8ff32f71e44 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -783,15 +783,14 @@ /// Returns whether or not the bodypart can feel pain /obj/item/bodypart/proc/can_feel_pain() - . = FALSE /* if(CHECK_BITFIELD(limb_flags, BODYPART_CUT_AWAY|BODYPART_DEAD)) return */ if(HAS_TRAIT(src, TRAIT_ROTTEN)) - return + return FALSE if(HAS_TRAIT(src, TRAIT_NOPAIN)) - return + return FALSE return owner?.can_feel_pain() /// Add pain_dam to a bodypart diff --git a/code/modules/surgery/bodyparts/bodypart_dismemberment.dm b/code/modules/surgery/bodyparts/bodypart_dismemberment.dm index 23426e14d36..ba1991175b5 100644 --- a/code/modules/surgery/bodyparts/bodypart_dismemberment.dm +++ b/code/modules/surgery/bodyparts/bodypart_dismemberment.dm @@ -56,7 +56,7 @@ C.visible_message("[C] is [pick("BRUTALLY","VIOLENTLY","BLOODILY","MESSILY")] DECAPITATED!") else C.visible_message("The [src.name] is [pick("torn off", "sundered", "severed", "separated", "unsewn")]!") - if(!HAS_TRAIT(C, TRAIT_NOPAIN)) + if(C.can_feel_pain()) C.emote("painscream") src.add_mob_blood(C) C.add_stress(/datum/stress_event/dismembered) diff --git a/code/modules/surgery/organs/_organ.dm b/code/modules/surgery/organs/_organ.dm index 116b5526ed8..22b639ff37a 100644 --- a/code/modules/surgery/organs/_organ.dm +++ b/code/modules/surgery/organs/_organ.dm @@ -785,15 +785,13 @@ * they don't have many painkillers */ /obj/item/organ/proc/can_feel_pain() - . = FALSE if(pain_multiplier <= 0) return FALSE if(CHECK_BITFIELD(organ_flags, ORGAN_CUT_AWAY | ORGAN_DEAD)) return FALSE if(HAS_TRAIT(src, TRAIT_NOPAIN)) return FALSE - if(owner?.can_feel_pain()) - return TRUE + return owner?.can_feel_pain() /obj/item/organ/proc/get_shock(painkiller_included = FALSE) if(!can_feel_pain()) From dabe40c5b3597413d2d96ca15ba1216ce35f2482 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Tue, 12 May 2026 18:01:43 -0700 Subject: [PATCH 25/59] continue drawing less --- code/datums/ai/controllers/mirespider.dm | 12 +-- code/datums/injury/_injury.dm | 4 +- code/datums/status_effects/rogue/healing.dm | 25 ++++++ .../covens/coven_powers/bloodheal.dm | 5 +- .../crafting/alchemy/herbal_recipes.dm | 7 -- code/modules/crafting/alchemy/reagents.dm | 2 +- code/modules/mob/living/carbon/human/human.dm | 79 ++++++++++--------- .../undirected/bardic/rejuvenation_song.dm | 28 +------ code/modules/surgery/bodyparts/_bodyparts.dm | 2 +- 9 files changed, 75 insertions(+), 89 deletions(-) diff --git a/code/datums/ai/controllers/mirespider.dm b/code/datums/ai/controllers/mirespider.dm index f556f5282bc..0d592d8ddd4 100644 --- a/code/datums/ai/controllers/mirespider.dm +++ b/code/datums/ai/controllers/mirespider.dm @@ -268,19 +268,9 @@ return TRUE /datum/status_effect/buff/healing/spider_cocoon/tick() + . = ..() var/obj/effect/temp_visual/heal/H = new /obj/effect/temp_visual/heal_rogue(get_turf(owner)) H.color = "#4e4c4c00" - var/list/wCount = owner.get_wounds() - //Keeps the user alive owner.adjust_blood_volume(blood_healing_on_tick, maximum = BLOOD_VOLUME_NORMAL) - if(wCount.len > 0) - owner.heal_wounds(healing_on_tick) - owner.update_damage_overlays() - owner.adjustBruteLoss(-healing_on_tick, 0) - owner.adjustFireLoss(-healing_on_tick, 0) - owner.adjustOxyLoss(-healing_on_tick * 5, 0) - owner.adjustToxLoss(-healing_on_tick, 0) - owner.adjustOrganLoss(ORGAN_SLOT_BRAIN, -healing_on_tick) - owner.adjustCloneLoss(-healing_on_tick, 0) #undef COCOON_FILTER diff --git a/code/datums/injury/_injury.dm b/code/datums/injury/_injury.dm index a41ff5b111d..63ff4ac1119 100644 --- a/code/datums/injury/_injury.dm +++ b/code/datums/injury/_injury.dm @@ -258,7 +258,7 @@ // heal the given amount of damage, and if the given amount of damage was more // than what needed to be healed, return how much heal was left /datum/injury/proc/heal_damage(amount_heal) - var/healed_damage = min(src.damage, amount_heal) + var/healed_damage = min(damage, amount_heal) damage -= healed_damage while(damage_per_injury() < damage_list[current_stage] && current_stage < length(desc_list)) current_stage++ @@ -294,7 +294,7 @@ desc = desc_list[current_stage] min_damage = damage_list[current_stage] if(damage > min_damage) - heal_damage(damage-min_damage) + heal_damage(damage - min_damage) injury_flags &= ~INJURY_RETRACTED if(parent_bodypart?.post_damage_change()) parent_bodypart?.owner?.update_damage_overlays() diff --git a/code/datums/status_effects/rogue/healing.dm b/code/datums/status_effects/rogue/healing.dm index 781d10c5cf5..8dcc31917d3 100644 --- a/code/datums/status_effects/rogue/healing.dm +++ b/code/datums/status_effects/rogue/healing.dm @@ -33,3 +33,28 @@ if(!isnull(new_healing_on_tick)) healing_on_tick = new_healing_on_tick return ..() + +/datum/status_effect/buff/healing/tick(seconds_between_ticks) + owner.adjust_blood_volume(healing_on_tick + 1, maximum = BLOOD_VOLUME_NORMAL) + owner.adjustOxyLoss(-healing_on_tick, 0) + owner.adjustToxLoss(-healing_on_tick, 0) + owner.adjustOrganLoss(ORGAN_SLOT_BRAIN, -healing_on_tick) + owner.adjustCloneLoss(-healing_on_tick, 0) + + if(length(owner.get_wounds()) && owner.heal_wounds(healing_on_tick, src)) + owner.update_damage_overlays() + + if(!iscarbon(owner)) + return + + var/mob/living/carbon/carbon_owner = owner + var/healing_amount = healing_on_tick + for(var/datum/injury/injury as anything in carbon_owner.all_injuries) + if(!healing_on_tick) + break + if(!injury.can_heal()) + continue + healing_on_tick = injury.heal_damage(healing_on_tick) + if(healing_amount != healing_on_tick) // we healed something + carbon_owner.update_all_limb_states() + diff --git a/code/modules/antagonists/villain/neu_vampires/covens/coven_powers/bloodheal.dm b/code/modules/antagonists/villain/neu_vampires/covens/coven_powers/bloodheal.dm index ca4253a8ced..e8cd6bd813d 100644 --- a/code/modules/antagonists/villain/neu_vampires/covens/coven_powers/bloodheal.dm +++ b/code/modules/antagonists/villain/neu_vampires/covens/coven_powers/bloodheal.dm @@ -47,7 +47,9 @@ /datum/coven_power/bloodheal/proc/trigger_healing() // Calculate healing amounts based on level var/bashing_lethal_heal = HEAL_BASHING_LETHAL * level + var/original_brute_heal = bashing_lethal_heal var/aggravated_heal = HEAL_AGGRAVATED * level + var/original_burn_heal = aggravated_heal owner.adjustToxLoss(-aggravated_heal * 0.5) @@ -62,7 +64,8 @@ else bashing_lethal_heal = injury.heal_damage(bashing_lethal_heal) - owner.update_all_limb_states() + if(bashing_lethal_heal != original_brute_heal || aggravated_heal != original_burn_heal) + owner.update_all_limb_states() owner.adjust_blood_volume(vitae_cost, maximum = BLOOD_VOLUME_NORMAL) //this is quadratic so expect it to scale like crazy diff --git a/code/modules/crafting/alchemy/herbal_recipes.dm b/code/modules/crafting/alchemy/herbal_recipes.dm index 0808763c19e..568e9654434 100644 --- a/code/modules/crafting/alchemy/herbal_recipes.dm +++ b/code/modules/crafting/alchemy/herbal_recipes.dm @@ -448,13 +448,6 @@ M.adjustToxLoss(-1*REM*efficiency) M.adjustOxyLoss(-1*efficiency) M.adjust_stamina(2*REM*efficiency) - var/total_healing = 1.5 * efficiency * REM - for(var/datum/injury/injury in M.all_injuries) - if(!total_healing) - break - total_healing = injury.heal_damage(total_healing) - - M.update_all_limb_states() . = ..() // Anti-Poison Blend diff --git a/code/modules/crafting/alchemy/reagents.dm b/code/modules/crafting/alchemy/reagents.dm index 83255da5715..fa98adfe3ab 100644 --- a/code/modules/crafting/alchemy/reagents.dm +++ b/code/modules/crafting/alchemy/reagents.dm @@ -14,7 +14,7 @@ for(var/datum/injury/injury in affected_bodypart.injuries) if(!injury.can_heal()) continue - injury.heal_damage(1) + injury.heal_damage(1) // update in on_mob_life . = ..() /datum/reagent/medicine/healthpot/on_mob_metabolize(mob/living/L) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 3e84d4d8b89..c7a393a4ed8 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -393,18 +393,6 @@ return var/they_beat = !HAS_TRAIT(target, TRAIT_STABLEHEART) var/obj/item/organ/heart/they_heart = target.getorganslot(ORGAN_SLOT_HEART) - var/heart_exposed_mod = 0 - if(CHECK_MULTIPLE_BITFIELDS(chest.get_surgery_flags(), SURGERY_INCISED|SURGERY_RETRACTED|SURGERY_BROKEN) && istype(they_heart)) - heart_exposed_mod += 5 - visible_message(span_notice("[src] massages [target]'s [they_heart]!"), \ - span_notice("I massage [target]'s [they_heart]."), \ - vision_distance = COMBAT_MESSAGE_RANGE, \ - ignored_mobs = target) - else - visible_message(span_notice("[src] performs chest compressions on [target]!"), \ - span_notice("I perform chest compressions on [target]."), \ - vision_distance = COMBAT_MESSAGE_RANGE, \ - ignored_mobs = target) target.last_cpr = world.time log_combat(src, target, "CPRed") if(they_beat && they_heart) @@ -416,39 +404,52 @@ var/epinephrine_mod = 0 if(target.reagents?.get_reagent_amount(/datum/reagent/adrenaline) >= 1) epinephrine_mod += 3 + var/heart_exposed_mod = 0 + if(CHECK_MULTIPLE_BITFIELDS(chest.get_surgery_flags(), SURGERY_INCISED|SURGERY_RETRACTED|SURGERY_BROKEN) && istype(they_heart)) + heart_exposed_mod += 5 - /// Always pump heart and break ribs unless you crit fail /// Journeymen (average 35) have a 5% chance of doing a CPR revive var/diceroll = diceroll(medical_skill+heart_exposed_mod+epinephrine_mod, dice_num = 13, context = DICE_CONTEXT_PHYSICAL) - if((diceroll >= DICE_SUCCESS) || (!attributes && prob(35))) - target.pump_heart(src) - if(HAS_TRAIT(target, TRAIT_NECRA_CURSE)) - to_chat(src, span_warning("Necra holds tight to this one.")) - return FALSE - if(target.stat < DEAD) // No point in running the revive check - return - if(GETBRAINLOSS(target) >= BRAIN_DAMAGE_DEATH) - SETBRAINLOSS(target, BRAIN_DAMAGE_DEATH - 1) - if(target.revive()) - chest.add_wound(/datum/wound/fracture/chest) - target.grab_ghost(TRUE) - target.visible_message(span_warning("[target] limply spasms their muscles."), \ - span_userdanger("My muscles spasm as i am brought back to life!")) - target.emote("breathgasp") - target.adjust_jitter(100 SECONDS) - add_abstract_elastic_data(ELASCAT_MEDICAL, ELASDATA_CPR_REVIVE, 1) - target.apply_status_effect(/datum/status_effect/debuff/revive) - record_round_statistic(STATS_CPR_REVIVALS, 1) - else - to_chat(src, span_warning("[target] isn't responding to my resuscitation...")) + if(diceroll <= DICE_CRIT_FAILURE) + visible_message(span_danger("[src] botches the chest compressions!"), \ + span_danger("I botch the chest compressions!"), + span_hear("I hear pushing."), + vision_distance = COMBAT_MESSAGE_RANGE, \ + ignored_mobs = target) else - if(diceroll <= DICE_CRIT_FAILURE) - visible_message(span_danger("[src] botches the chest compressions!"), \ - span_danger("I botch the chest compressions!"), - span_hear("I hear frantic pressing!"), + if(heart_exposed_mod) + visible_message(span_notice("[src] massages [target]'s [they_heart]!"), \ + span_notice("I massage [target]'s [they_heart]."), \ + span_hear("I hear pushing."), + vision_distance = COMBAT_MESSAGE_RANGE, \ ignored_mobs = target) else - target.pump_heart(src) + visible_message(span_notice("[src] performs chest compressions on [target]!"), \ + span_notice("I perform chest compressions on [target]."), \ + span_hear("I hear pushing."), + vision_distance = COMBAT_MESSAGE_RANGE, \ + ignored_mobs = target) + target.pump_heart(src) + if(target.stat < DEAD) // No point in running the revive check + return + if((diceroll >= DICE_SUCCESS) || (!attributes && prob(35))) + if(HAS_TRAIT(target, TRAIT_NECRA_CURSE)) + to_chat(src, span_warning("Necra holds tight to this one.")) + return FALSE + if(GETBRAINLOSS(target) >= BRAIN_DAMAGE_DEATH) + SETBRAINLOSS(target, BRAIN_DAMAGE_DEATH - 1) + if(target.revive()) + chest.add_wound(/datum/wound/fracture/chest) + target.grab_ghost(TRUE) + target.visible_message(span_warning("[target] limply spasms their muscles."), \ + span_userdanger("My muscles spasm as i am brought back to life!")) + target.emote("breathgasp") + target.adjust_jitter(100 SECONDS) + add_abstract_elastic_data(ELASCAT_MEDICAL, ELASDATA_CPR_REVIVE, 1) + target.apply_status_effect(/datum/status_effect/debuff/revive) + record_round_statistic(STATS_CPR_REVIVALS, 1) + else + to_chat(src, span_warning("[target] isn't responding to my resuscitation...")) /mob/living/carbon/human/cuff_resist(obj/item/I, breakouttime = 1 MINUTES, cuff_break = 0, instant = FALSE) if(..()) diff --git a/code/modules/spells/spell_types/undirected/bardic/rejuvenation_song.dm b/code/modules/spells/spell_types/undirected/bardic/rejuvenation_song.dm index fd167429160..62b0262d58d 100644 --- a/code/modules/spells/spell_types/undirected/bardic/rejuvenation_song.dm +++ b/code/modules/spells/spell_types/undirected/bardic/rejuvenation_song.dm @@ -22,32 +22,6 @@ healing_on_tick = 1 // Full bard (100%) /datum/status_effect/buff/healing/rejuvenationsong/tick() + . = ..() var/obj/effect/temp_visual/heal/H = new /obj/effect/temp_visual/heal_rogue(get_turf(owner)) H.color = "#660759" - var/list/wCount = owner.get_wounds() - owner.adjust_blood_volume(healing_on_tick + 1, maximum = BLOOD_VOLUME_NORMAL) - if(length(wCount) > 0) - if(iscarbon(owner)) - var/mob/living/carbon/carbon = owner - for(var/datum/injury/injury in carbon.all_injuries) - if(!(injury.damage_type in list(WOUND_SLASH, WOUND_PIERCE, WOUND_BITE, WOUND_BLUNT)) || !injury.can_heal()) - continue - injury.heal_damage(healing_on_tick) - carbon.update_all_limb_states() - owner.heal_wounds(healing_on_tick, list(/datum/wound/slash, /datum/wound/puncture, /datum/wound/bite, /datum/wound/bruise)) - - owner.adjustOxyLoss(-healing_on_tick, 0) - owner.adjustToxLoss(-healing_on_tick, 0) - owner.adjustOrganLoss(ORGAN_SLOT_BRAIN, -healing_on_tick) - owner.adjustCloneLoss(-healing_on_tick, 0) - - var/mob/living/carbon/human/human = owner - if(!istype(human)) - return - for(var/datum/injury/injury in human.all_injuries) - if(!healing_on_tick) - break - if(!injury.can_heal()) - continue - healing_on_tick = injury.heal_damage(healing_on_tick) - human.update_all_limb_states() diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index 8ff32f71e44..9fa0ccf49e7 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -923,7 +923,7 @@ if(shock_penalty) owner.update_shock_penalty(shock_penalty) - return post_damage_change(TRUE, FALSE) + return post_damage_change(FALSE, TRUE) //Heals brute and burn damage for the organ. Returns 1 if the damage-icon states changed at all. //Damage cannot go below zero. From 4277b17704aa96056f10ff1b55f2251180c84ab4 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Tue, 12 May 2026 18:21:50 -0700 Subject: [PATCH 26/59] harmless delta_times --- code/__DEFINES/medical.dm | 4 +-- code/datums/injury/_injury.dm | 6 ++--- code/datums/injury/organic/burn.dm | 2 +- code/datums/injury/organic/lash.dm | 2 +- code/modules/surgery/bodyparts/_bodyparts.dm | 26 +++++++++---------- code/modules/surgery/organs/_organ.dm | 20 +++++++------- .../surgery/organs/internal/appendix.dm | 2 +- code/modules/surgery/organs/internal/brain.dm | 2 +- 8 files changed, 32 insertions(+), 32 deletions(-) diff --git a/code/__DEFINES/medical.dm b/code/__DEFINES/medical.dm index 3120e9c9e5c..c28d63e2c91 100644 --- a/code/__DEFINES/medical.dm +++ b/code/__DEFINES/medical.dm @@ -257,9 +257,9 @@ DEFINE_BITFIELD(organ_flags, list( /// Water sanitization per unit #define SANITIZATION_PER_UNIT_WATER 10 /// CE_ANTIBIOTIC bodypart/organ sanitization per CE unit -#define SANITIZATION_ANTIBIOTIC 0.1 +#define SANITIZATION_ANTIBIOTIC 0.05 /// Bodypart/organ sanitization for laying down -#define SANITIZATION_LYING 1 +#define SANITIZATION_LYING 0.5 //~brain damage related defines /// We need to take at least this much brainloss gained at once to roll for brain traumas, any less it won't roll diff --git a/code/datums/injury/_injury.dm b/code/datums/injury/_injury.dm index 63ff4ac1119..0901a9de1f2 100644 --- a/code/datums/injury/_injury.dm +++ b/code/datums/injury/_injury.dm @@ -32,7 +32,7 @@ /// Amount of germs in the injury var/germ_level = 0 /// Rate of infection for this injury - var/infection_rate = 1 + var/infection_rate = 0.5 /// Time it takes for the injury to fade away once healed up var/fade_away_time = 1 MINUTES /// The bodypart the injury is on, if on a bodypart @@ -129,7 +129,7 @@ damage = our_damage //initialize with the appropriate stage and bleeding ticks - bleed_timer += our_damage + bleed_timer += our_damage * 2 init_stage(our_damage) if(istype(parent)) @@ -302,7 +302,7 @@ // opens the injury and worsens it /datum/injury/proc/open_injury(damage, retracting = FALSE) src.damage += damage - bleed_timer += damage + bleed_timer += damage * 2 while(current_stage > 1 && damage_list[current_stage-1] < damage_per_injury()) current_stage-- diff --git a/code/datums/injury/organic/burn.dm b/code/datums/injury/organic/burn.dm index 2a7be72b075..6f178af34cd 100644 --- a/code/datums/injury/organic/burn.dm +++ b/code/datums/injury/organic/burn.dm @@ -3,7 +3,7 @@ damage_type = WOUND_BURN autoheal_cutoff = 3 max_bleeding_stage = 0 - infection_rate = 2.5 + infection_rate = 1.25 /datum/injury/burn/infection_check() //anything less than a FUCK burn isn't infectable if treated properly diff --git a/code/datums/injury/organic/lash.dm b/code/datums/injury/organic/lash.dm index 325eb204488..0e1c9657182 100644 --- a/code/datums/injury/organic/lash.dm +++ b/code/datums/injury/organic/lash.dm @@ -1,7 +1,7 @@ /datum/injury/lash damage_type = WOUND_LASH autoheal_cutoff = 10 - infection_rate = 1.15 + infection_rate = 0.58 bleed_threshold = 20 /datum/injury/lash/welt diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index 9fa0ccf49e7..02ad27bf676 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -562,16 +562,16 @@ var/heal_amt = 0 if(!toxins && injury.can_autoheal()) - heal_amt += (GET_MOB_ATTRIBUTE_VALUE(owner, STAT_ENDURANCE) * 0.01) + heal_amt += (GET_MOB_ATTRIBUTE_VALUE(owner, STAT_ENDURANCE) * 0.005) if(owner?.IsSleeping()) heal_amt *= 2 if(heal_amt) - injury.heal_damage(heal_amt * (0.5 * delta_time)) + injury.heal_damage(heal_amt * delta_time) // Bleeding if(owner) - injury.bleed_timer = max(0, injury.bleed_timer - (0.5 * delta_time)) + injury.bleed_timer = max(0, injury.bleed_timer - delta_time) if(post_damage_change()) owner.update_damage_overlays() @@ -616,7 +616,7 @@ if(istype(open_turf)) for(var/datum/injury/injury as anything in injuries) if(injury.infection_check(delta_time, times_fired) && (max(open_turf.germ_level, owner_germ_level) > injury.germ_level)) - injury.adjust_germ_level(injury.infection_rate * (0.5 * delta_time)) + injury.adjust_germ_level(injury.infection_rate * delta_time) // If we have sufficient antibiotics, then skip over this stuff, the infection is going away var/antibiotics = owner.get_antibiotics() @@ -626,7 +626,7 @@ for(var/datum/injury/injury as anything in injuries) //Infected injuries raise the bodypart's germ level if(injury.germ_level > germ_level || DT_PROB(CEILING(min(injury.germ_level/5, 40)/2, 1), delta_time)) - adjust_germ_level(injury.infection_rate * (0.5 * delta_time)) + adjust_germ_level(injury.infection_rate * delta_time) break //limit increase to a maximum of one injury infection increase per 2 seconds @@ -640,11 +640,11 @@ // Being properly oxygenated if(!artery_needed() || (arterial_efficiency >= ORGAN_FAILING_EFFICIENCY)) if(germ_level > 0 && (germ_level < INFECTION_LEVEL_ONE/2) && DT_PROB(immunity*0.3, delta_time)) - adjust_germ_level(-1 * (0.5 * delta_time)) + adjust_germ_level(-0.5 * delta_time) return // Dry gangrene else - adjust_germ_level(1 * (0.5 * delta_time)) + adjust_germ_level(0.5 * delta_time) if(germ_level >= INFECTION_LEVEL_ONE/2) //Warn the user that they're a bit fucked @@ -654,10 +654,10 @@ if(antibiotics < 5 && DT_PROB(FLOOR(germ_level/6 * immunity_weakness * 0.005, 1), delta_time)) if(immunity > 0) //Immunity starts at 100. This doubles infection rate at 50% immunity. Rounded to nearest whole. - adjust_germ_level(clamp(FLOOR(1/immunity, 1), 1, 10) * (0.5 * delta_time)) + adjust_germ_level(clamp(FLOOR(0.5/immunity, 1), 1, 10) * delta_time) else //Will only trigger if immunity has hit zero. Once it does, 10x infection rate. - adjust_germ_level(10 * (0.5 * delta_time)) + adjust_germ_level(5 * delta_time) if(germ_level >= INFECTION_LEVEL_ONE && (antibiotics < 20)) if(DT_PROB(3, delta_time) && (owner.stat < DEAD) && germ_level <= INFECTION_LEVEL_TWO) @@ -684,7 +684,7 @@ // Infect the target organ if(target_organ) - target_organ.adjust_germ_level(1 * (0.5 * delta_time)) + target_organ.adjust_germ_level(0.5 * delta_time) // Spread the infection to child and parent organs var/zones = list() @@ -694,7 +694,7 @@ var/obj/item/bodypart/bodypart = owner.get_bodypart(zone) if(bodypart && (bodypart.germ_level < germ_level)) if(bodypart.germ_level < INFECTION_LEVEL_TWO || DT_PROB(15, delta_time)) - bodypart.adjust_germ_level(1 * (0.5 * delta_time)) + bodypart.adjust_germ_level(0.5 * delta_time) /// Handle the antibiotic chem effect /obj/item/bodypart/proc/handle_antibiotics(delta_time, times_fired) @@ -709,9 +709,9 @@ if(getorganslotefficiency(ORGAN_SLOT_ARTERY) >= ORGAN_FAILING_EFFICIENCY) set_germ_level(0) //cure instantly else - adjust_germ_level(-antibiotics * SANITIZATION_ANTIBIOTIC * (0.5 * delta_time)) //at germ_level == 500 and 50 antibiotic, this should cure the infection in 5 minutes + adjust_germ_level(-antibiotics * SANITIZATION_ANTIBIOTIC * delta_time) //at germ_level == 500 and 50 antibiotic, this should cure the infection in 5 minutes if(owner?.body_position == LYING_DOWN) - adjust_germ_level(-SANITIZATION_LYING * (0.5 * delta_time)) + adjust_germ_level(-SANITIZATION_LYING * delta_time) /obj/item/bodypart/proc/create_base_organs() if(CHECK_BITFIELD(limb_flags, BODYPART_HAS_ARTERY)) diff --git a/code/modules/surgery/organs/_organ.dm b/code/modules/surgery/organs/_organ.dm index 22b639ff37a..be53ff56dd8 100644 --- a/code/modules/surgery/organs/_organ.dm +++ b/code/modules/surgery/organs/_organ.dm @@ -220,7 +220,7 @@ failer = is_failing() if(arterial_efficiency && !failer && !in_bleedout) // Arteries get an extra flat 10 blood regen - current_blood = min(current_blood + 5 * (0.5 * delta_time) * (arterial_efficiency/ORGAN_OPTIMAL_EFFICIENCY), max_blood_storage) + current_blood = min(current_blood + (2.5 * delta_time) * (arterial_efficiency/ORGAN_OPTIMAL_EFFICIENCY), max_blood_storage) return if(!blood_req) return @@ -435,16 +435,16 @@ var/antibiotics = owner?.get_antibiotics() if(germ_level > 0 && germ_level < INFECTION_LEVEL_ONE/2 && DT_PROB(virus_immunity*0.15, delta_time)) - adjust_germ_level(-1 * (0.5 * delta_time)) + adjust_germ_level(-0.5 * delta_time) return if(germ_level >= INFECTION_LEVEL_ONE/2) //Aiming for germ level to go from ambient to INFECTION_LEVEL_TWO in an average of 15 minutes, when immunity is full. if(antibiotics < 5 && DT_PROB(round(germ_level/6 * owner.immunity_weakness() * 0.005), delta_time)) if(virus_immunity > 0) - adjust_germ_level(clamp(round(1/virus_immunity), 1, 10) * (0.5 * delta_time)) // Immunity starts at 100. This doubles infection rate at 50% immunity. Rounded to nearest whole. + adjust_germ_level(clamp(round(0.5/virus_immunity), 1, 10) * delta_time) // Immunity starts at 100. This doubles infection rate at 50% immunity. Rounded to nearest whole. else // Will only trigger if immunity has hit zero. Once it does, 10x infection rate. - adjust_germ_level(10 * (0.5 * delta_time)) + adjust_germ_level(5 * delta_time) if(germ_level >= INFECTION_LEVEL_ONE && antibiotics < 20) var/fever_temperature = (BODYTEMP_HEAT_DAMAGE_LIMIT - BODYTEMP_NORMAL - 5)* min(germ_level/INFECTION_LEVEL_TWO, 1) + BODYTEMP_NORMAL @@ -455,7 +455,7 @@ if(bodypart) //Spread germs if(antibiotics < 5 && bodypart.germ_level < germ_level && (bodypart.germ_level < INFECTION_LEVEL_ONE*2 || DT_PROB(owner.immunity_weakness() * 0.15, delta_time))) - bodypart.adjust_germ_level(1 * (0.5 * delta_time)) + bodypart.adjust_germ_level(0.5 * delta_time) //Cause organ damage about once every ~30 seconds //The bodypart deals with dealing raw toxin damage, let's not stack onto the problem now if(DT_PROB(2, delta_time)) @@ -467,7 +467,7 @@ if(bodypart) // Spread germs really badly if(antibiotics < 10 && bodypart.germ_level < germ_level && (bodypart.germ_level < INFECTION_LEVEL_THREE)) - bodypart.adjust_germ_level(1 * (0.5 * delta_time)) + bodypart.adjust_germ_level(0.5 * delta_time) /// Antibiotics combating germs and stuff /obj/item/organ/proc/handle_antibiotics(delta_time, times_fired) @@ -481,9 +481,9 @@ if((germ_level < INFECTION_LEVEL_ONE) && (antibiotics >= 20)) set_germ_level(GERM_LEVEL_STERILE) else - adjust_germ_level(-antibiotics * SANITIZATION_ANTIBIOTIC * (0.5 * delta_time)) //at germ_level == 500 and 50 antibiotic, this should cure the infection in 5 minutes + adjust_germ_level(-antibiotics * SANITIZATION_ANTIBIOTIC * delta_time) //at germ_level == 500 and 50 antibiotic, this should cure the infection in 5 minutes if(owner?.body_position == LYING_DOWN) - adjust_germ_level(-SANITIZATION_LYING * (0.5 * delta_time)) + adjust_germ_level(-SANITIZATION_LYING * delta_time) /obj/item/organ/proc/on_life(delta_time, times_fired) //repair organ damage if the organ is not failing SHOULD_CALL_PARENT(TRUE) @@ -559,8 +559,8 @@ return applyOrganDamage(-healing_amount, damage) // pass curent damage incase we are over cap //this doesn't seem very right at all... - owner.adjust_nutrition(-nutriment_req/100 * (0.5 * delta_time)) - owner.adjust_hydration(-hydration_req/100 * (0.5 * delta_time)) + owner.adjust_nutrition(-nutriment_req/200 * delta_time) + owner.adjust_hydration(-hydration_req/200 * delta_time) /** organ_failure * generic proc for handling dying organs diff --git a/code/modules/surgery/organs/internal/appendix.dm b/code/modules/surgery/organs/internal/appendix.dm index fe2ad265b73..079c9f7c190 100644 --- a/code/modules/surgery/organs/internal/appendix.dm +++ b/code/modules/surgery/organs/internal/appendix.dm @@ -39,7 +39,7 @@ /obj/item/organ/appendix/organ_failure(delta_time) . = ..() inflamation_stage = TRUE - owner.adjustToxLoss(0.5 * delta_time, TRUE, TRUE) //forced to ensure people don't use it to gain tox as slime person + owner.adjustToxLoss(2 * delta_time, TRUE, TRUE) //forced to ensure people don't use it to gain tox as slime person /obj/item/organ/appendix/prepare_eat() var/obj/S = ..() diff --git a/code/modules/surgery/organs/internal/brain.dm b/code/modules/surgery/organs/internal/brain.dm index 3de6c183826..7608c55a8e1 100644 --- a/code/modules/surgery/organs/internal/brain.dm +++ b/code/modules/surgery/organs/internal/brain.dm @@ -85,7 +85,7 @@ var/in_bleedout = owner.in_bleedout() if(arterial_efficiency && !is_failing()) // Arteries get an extra flat 5 blood regen - current_blood = min(current_blood + 5 * (0.5 * delta_time) * (arterial_efficiency/ORGAN_OPTIMAL_EFFICIENCY), max_blood_storage) + current_blood = min(current_blood + 2.5 * delta_time) * (arterial_efficiency/ORGAN_OPTIMAL_EFFICIENCY), max_blood_storage) return if(!blood_req) return From acfe325ff476c8393ed8dceae7bf7d4aa9860f79 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Tue, 12 May 2026 18:31:11 -0700 Subject: [PATCH 27/59] blood_req delta_time --- code/__DEFINES/medical.dm | 2 +- code/modules/surgery/organs/_organ.dm | 6 +++--- code/modules/surgery/organs/external/ears.dm | 2 +- code/modules/surgery/organs/internal/appendix.dm | 2 +- code/modules/surgery/organs/internal/brain.dm | 12 ++++++------ code/modules/surgery/organs/internal/eyes.dm | 2 +- code/modules/surgery/organs/internal/heart.dm | 2 +- code/modules/surgery/organs/internal/liver.dm | 2 +- code/modules/surgery/organs/internal/lungs.dm | 2 +- code/modules/surgery/organs/internal/spleen.dm | 2 +- code/modules/surgery/organs/internal/stomach.dm | 2 +- code/modules/surgery/organs/internal/tongue.dm | 2 +- code/modules/surgery/organs/internal/vocal_cords.dm | 2 +- 13 files changed, 20 insertions(+), 20 deletions(-) diff --git a/code/__DEFINES/medical.dm b/code/__DEFINES/medical.dm index c28d63e2c91..1c9753d6718 100644 --- a/code/__DEFINES/medical.dm +++ b/code/__DEFINES/medical.dm @@ -204,7 +204,7 @@ DEFINE_BITFIELD(organ_flags, list( // ~organ requirements /// Normally 50% of the default blood volume (230cl) -#define DEFAULT_TOTAL_BLOOD_REQ BLOOD_VOLUME_NORMAL * 0.33 +#define DEFAULT_TOTAL_BLOOD_REQ BLOOD_VOLUME_NORMAL * 0.5 #define DEFAULT_TOTAL_OXYGEN_REQ 50 #define DEFAULT_TOTAL_NUTRIMENT_REQ HUNGER_FACTOR #define DEFAULT_TOTAL_HYDRATION_REQ THIRST_FACTOR diff --git a/code/modules/surgery/organs/_organ.dm b/code/modules/surgery/organs/_organ.dm index be53ff56dd8..581496d21eb 100644 --- a/code/modules/surgery/organs/_organ.dm +++ b/code/modules/surgery/organs/_organ.dm @@ -225,9 +225,9 @@ if(!blood_req) return if(!in_bleedout) - current_blood = min(current_blood + (blood_req * (0.6 * delta_time)), max_blood_storage) //very slow refill + current_blood = min(current_blood + (blood_req * delta_time), max_blood_storage) //very slow refill return - current_blood = max(current_blood - (blood_req * (0.5 * delta_time)), 0) + current_blood = max(current_blood - (blood_req * delta_time), 0) // When all blood is lost, take blood from blood vessels if(!current_blood) var/obj/item/organ/artery @@ -239,7 +239,7 @@ break if(artery?.current_blood) var/prev_blood = artery.current_blood - artery.current_blood = max(artery.current_blood - (blood_req * 0.5 * delta_time), 0) + artery.current_blood = max(artery.current_blood - (blood_req * delta_time), 0) current_blood = max(prev_blood - artery.current_blood, 0) if((current_blood <= 0) && !(organ_flags & ORGAN_LIMB_SUPPORTER)) applyOrganDamage(0.2 * delta_time) diff --git a/code/modules/surgery/organs/external/ears.dm b/code/modules/surgery/organs/external/ears.dm index a9d33f6c67a..18359cb494e 100644 --- a/code/modules/surgery/organs/external/ears.dm +++ b/code/modules/surgery/organs/external/ears.dm @@ -15,7 +15,7 @@ organ_volume = 0.25 max_blood_storage = 2.5 current_blood = 2.5 - blood_req = 0.5 + blood_req = 0.125 oxygen_req = 0.5 nutriment_req = 0.15 hydration_req = 0.15 diff --git a/code/modules/surgery/organs/internal/appendix.dm b/code/modules/surgery/organs/internal/appendix.dm index 079c9f7c190..60f89f94b74 100644 --- a/code/modules/surgery/organs/internal/appendix.dm +++ b/code/modules/surgery/organs/internal/appendix.dm @@ -17,7 +17,7 @@ organ_volume = 0.5 max_blood_storage = 2.5 current_blood = 2.5 - blood_req = 0.5 + blood_req = 0.25 oxygen_req = 0.25 nutriment_req = 0.35 hydration_req = 0.2 diff --git a/code/modules/surgery/organs/internal/brain.dm b/code/modules/surgery/organs/internal/brain.dm index 7608c55a8e1..26676c223a1 100644 --- a/code/modules/surgery/organs/internal/brain.dm +++ b/code/modules/surgery/organs/internal/brain.dm @@ -29,7 +29,7 @@ organ_volume = 0.5 max_blood_storage = 100 current_blood = 100 - blood_req = 5 + blood_req = 2.5 oxygen_req = 5 nutriment_req = 3.5 hydration_req = 2 @@ -85,17 +85,17 @@ var/in_bleedout = owner.in_bleedout() if(arterial_efficiency && !is_failing()) // Arteries get an extra flat 5 blood regen - current_blood = min(current_blood + 2.5 * delta_time) * (arterial_efficiency/ORGAN_OPTIMAL_EFFICIENCY), max_blood_storage) + current_blood = min(current_blood + (2.5 * delta_time * (arterial_efficiency/ORGAN_OPTIMAL_EFFICIENCY)), max_blood_storage) return if(!blood_req) return if(!in_bleedout && (effective_blood_oxygenation >= BLOOD_VOLUME_SAFE)) - current_blood = min(current_blood + (blood_req * (0.5 * delta_time)), max_blood_storage) + current_blood = min(current_blood + (blood_req * delta_time), max_blood_storage) return if(in_bleedout) - current_blood = max(current_blood - (blood_req * (0.5 * delta_time)), 0) + current_blood = max(current_blood - (blood_req * delta_time), 0) else - current_blood = max(current_blood - (blood_req * ((BLOOD_VOLUME_NORMAL-effective_blood_oxygenation)/BLOOD_VOLUME_NORMAL) * (0.5 * delta_time)), 0) + current_blood = max(current_blood - (blood_req * ((BLOOD_VOLUME_NORMAL-effective_blood_oxygenation)/BLOOD_VOLUME_NORMAL) * delta_time), 0) // When all blood is lost, take blood from blood vessels if(!current_blood) var/obj/item/organ/artery @@ -107,7 +107,7 @@ break if(artery?.current_blood) var/prev_blood = artery.current_blood - artery.current_blood = max(artery.current_blood - (blood_req * 0.5 * delta_time), 0) + artery.current_blood = max(artery.current_blood - (blood_req * delta_time), 0) current_blood = max(prev_blood - artery.current_blood, 0) //Don't apply damage, this is handled by the organ process datum, if necessary diff --git a/code/modules/surgery/organs/internal/eyes.dm b/code/modules/surgery/organs/internal/eyes.dm index 75b9c4f8119..65672ff74eb 100644 --- a/code/modules/surgery/organs/internal/eyes.dm +++ b/code/modules/surgery/organs/internal/eyes.dm @@ -31,7 +31,7 @@ organ_volume = 0.25 max_blood_storage = 5 current_blood = 5 - blood_req = 1 + blood_req = 0.5 oxygen_req = 0.5 nutriment_req = 0.15 hydration_req = 0.15 diff --git a/code/modules/surgery/organs/internal/heart.dm b/code/modules/surgery/organs/internal/heart.dm index 3c1335713a0..0978b7a8b0d 100644 --- a/code/modules/surgery/organs/internal/heart.dm +++ b/code/modules/surgery/organs/internal/heart.dm @@ -12,7 +12,7 @@ organ_volume = 0.5 max_blood_storage = 100 current_blood = 100 - blood_req = 10 + blood_req = 5 oxygen_req = 5 nutriment_req = 3.5 hydration_req = 2 diff --git a/code/modules/surgery/organs/internal/liver.dm b/code/modules/surgery/organs/internal/liver.dm index e21bce6b80f..747c9632fb1 100644 --- a/code/modules/surgery/organs/internal/liver.dm +++ b/code/modules/surgery/organs/internal/liver.dm @@ -15,7 +15,7 @@ organ_volume = 2 max_blood_storage = 25 current_blood = 25 - blood_req = 4 + blood_req = 2 oxygen_req = 4 nutriment_req = 1.5 hydration_req = 1.5 diff --git a/code/modules/surgery/organs/internal/lungs.dm b/code/modules/surgery/organs/internal/lungs.dm index 2c8fa2aca12..b7c2bf92e87 100644 --- a/code/modules/surgery/organs/internal/lungs.dm +++ b/code/modules/surgery/organs/internal/lungs.dm @@ -16,7 +16,7 @@ organ_volume = 1 max_blood_storage = 25 current_blood = 25 - blood_req = 4 + blood_req = 2 oxygen_req = 4 nutriment_req = 1.5 hydration_req = 1.5 diff --git a/code/modules/surgery/organs/internal/spleen.dm b/code/modules/surgery/organs/internal/spleen.dm index e2df0b64265..de7676607bb 100644 --- a/code/modules/surgery/organs/internal/spleen.dm +++ b/code/modules/surgery/organs/internal/spleen.dm @@ -16,7 +16,7 @@ organ_volume = 0.5 max_blood_storage = 20 current_blood = 20 - blood_req = 2 + blood_req = 1 oxygen_req = 2 nutriment_req = 2.5 hydration_req = 1 diff --git a/code/modules/surgery/organs/internal/stomach.dm b/code/modules/surgery/organs/internal/stomach.dm index fa9b6e724fa..7e1ba1e1e01 100644 --- a/code/modules/surgery/organs/internal/stomach.dm +++ b/code/modules/surgery/organs/internal/stomach.dm @@ -13,7 +13,7 @@ organ_volume = 1 max_blood_storage = 20 current_blood = 20 - blood_req = 4 + blood_req = 2 oxygen_req = 4 nutriment_req = 2 hydration_req = 1.5 diff --git a/code/modules/surgery/organs/internal/tongue.dm b/code/modules/surgery/organs/internal/tongue.dm index 912b380fb15..ff6a689c37f 100644 --- a/code/modules/surgery/organs/internal/tongue.dm +++ b/code/modules/surgery/organs/internal/tongue.dm @@ -10,7 +10,7 @@ organ_volume = 0.5 max_blood_storage = 5 current_blood = 5 - blood_req = 1 + blood_req = 0.5 oxygen_req = 0.5 nutriment_req = 0.3 hydration_req = 0.6 diff --git a/code/modules/surgery/organs/internal/vocal_cords.dm b/code/modules/surgery/organs/internal/vocal_cords.dm index dce4eef8777..0ad743d59b0 100644 --- a/code/modules/surgery/organs/internal/vocal_cords.dm +++ b/code/modules/surgery/organs/internal/vocal_cords.dm @@ -10,7 +10,7 @@ organ_volume = 1 max_blood_storage = 10 current_blood = 10 - blood_req = 2 + blood_req = 1 oxygen_req = 2.5 nutriment_req = 1.5 From f6c3355f83cc296cacd82ff5993cec9bca17ef3e Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Tue, 12 May 2026 18:53:34 -0700 Subject: [PATCH 28/59] nutrient delta_time guh e --- code/modules/mob/mob.dm | 52 +++++++++---------- .../surgery/organs/internal/appendix.dm | 2 +- .../surgery/organs/internal/artery/_artery.dm | 4 +- code/modules/surgery/organs/internal/brain.dm | 4 +- code/modules/surgery/organs/internal/heart.dm | 4 +- code/modules/surgery/organs/internal/liver.dm | 4 +- code/modules/surgery/organs/internal/lungs.dm | 4 +- .../modules/surgery/organs/internal/spleen.dm | 4 +- .../surgery/organs/internal/stomach.dm | 4 +- .../organs/organ_processing/stomach.dm | 4 +- 10 files changed, 43 insertions(+), 43 deletions(-) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index ca1534cd1b8..021dd3072c1 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1156,34 +1156,34 @@ GLOBAL_VAR_INIT(mobids, 1) H.open_language_menu(usr) ///Adjust the nutrition of a mob -/mob/proc/adjust_nutrition(change) //Honestly FUCK the oldcoders for putting nutrition on /mob someone else can move it up because holy hell I'd have to fix SO many typechecks - if(HAS_TRAIT(src, TRAIT_NOHUNGER)) - nutrition = NUTRITION_LEVEL_FULL - nutrition = max(0, nutrition + change) - if(nutrition > NUTRITION_LEVEL_FULL) - nutrition = NUTRITION_LEVEL_FULL +/mob/proc/adjust_nutrition(change, forced) //Honestly FUCK the oldcoders for putting nutrition on /mob someone else can move it up because holy hell I'd have to fix SO many typechecks + if(HAS_TRAIT(src, TRAIT_NOHUNGER) && !forced) + nutrition = NUTRITION_LEVEL_WELL_FED + return + + nutrition = clamp(nutrition + change, 0, NUTRITION_LEVEL_FULL) ///Force set the mob nutrition -/mob/proc/set_nutrition(change) //Seriously fuck you oldcoders. - if(HAS_TRAIT(src, TRAIT_NOHUNGER)) - nutrition = NUTRITION_LEVEL_FULL - nutrition = max(0, change) - if(nutrition > NUTRITION_LEVEL_FULL) - nutrition = NUTRITION_LEVEL_FULL - -/mob/proc/adjust_hydration(change) - if(HAS_TRAIT(src, TRAIT_NOHUNGER)) - hydration = HYDRATION_LEVEL_FULL - hydration = max(0, hydration + change) - if(hydration > HYDRATION_LEVEL_FULL) - hydration = HYDRATION_LEVEL_FULL - -/mob/proc/set_hydration(change) - if(HAS_TRAIT(src, TRAIT_NOHUNGER)) - hydration = HYDRATION_LEVEL_FULL - hydration = max(0, change) - if(hydration > HYDRATION_LEVEL_FULL) - hydration = HYDRATION_LEVEL_FULL +/mob/proc/set_nutrition(set_to, forced) //Seriously fuck you oldcoders. + if(HAS_TRAIT(src, TRAIT_NOHUNGER) && !forced) + nutrition = NUTRITION_LEVEL_WELL_FED + return + + nutrition = clamp(set_to, 0, NUTRITION_LEVEL_FULL) + +/mob/proc/adjust_hydration(change, forced) + if(HAS_TRAIT(src, TRAIT_NOHUNGER) && !forced) + hydration = HYDRATION_LEVEL_HYDRATED + return + + hydration = clamp(hydration + change, 0, HYDRATION_LEVEL_FULL) + +/mob/proc/set_hydration(set_to, forced) + if(HAS_TRAIT(src, TRAIT_NOHUNGER) && !forced) + hydration = HYDRATION_LEVEL_HYDRATED + return + + hydration = clamp(set_to, 0, HYDRATION_LEVEL_FULL) /mob/proc/update_equipment_speed_mods() var/speedies = equipped_speed_mods() diff --git a/code/modules/surgery/organs/internal/appendix.dm b/code/modules/surgery/organs/internal/appendix.dm index 60f89f94b74..7cb6f739da6 100644 --- a/code/modules/surgery/organs/internal/appendix.dm +++ b/code/modules/surgery/organs/internal/appendix.dm @@ -39,7 +39,7 @@ /obj/item/organ/appendix/organ_failure(delta_time) . = ..() inflamation_stage = TRUE - owner.adjustToxLoss(2 * delta_time, TRUE, TRUE) //forced to ensure people don't use it to gain tox as slime person + owner.adjustToxLoss(0.5 * delta_time, TRUE, TRUE) //forced to ensure people don't use it to gain tox as slime person /obj/item/organ/appendix/prepare_eat() var/obj/S = ..() diff --git a/code/modules/surgery/organs/internal/artery/_artery.dm b/code/modules/surgery/organs/internal/artery/_artery.dm index 74f6adec1ad..2cb592fa5ba 100644 --- a/code/modules/surgery/organs/internal/artery/_artery.dm +++ b/code/modules/surgery/organs/internal/artery/_artery.dm @@ -18,8 +18,8 @@ max_blood_storage = 100 current_blood = 100 oxygen_req = 0.25 - nutriment_req = 0.1 - hydration_req = 0.05 + nutriment_req = 0.09 + hydration_req = 0.03 /// How much blood we gush when torn. This will be multiplied by delta_time var/blood_flow = ARTERIAL_BLOOD_FLOW diff --git a/code/modules/surgery/organs/internal/brain.dm b/code/modules/surgery/organs/internal/brain.dm index 26676c223a1..3ad5b7301d2 100644 --- a/code/modules/surgery/organs/internal/brain.dm +++ b/code/modules/surgery/organs/internal/brain.dm @@ -31,8 +31,8 @@ current_blood = 100 blood_req = 2.5 oxygen_req = 5 - nutriment_req = 3.5 - hydration_req = 2 + nutriment_req = 3 + hydration_req = 1.5 COOLDOWN_DECLARE(trauma_cooldown) diff --git a/code/modules/surgery/organs/internal/heart.dm b/code/modules/surgery/organs/internal/heart.dm index 0978b7a8b0d..7d0d9519ce6 100644 --- a/code/modules/surgery/organs/internal/heart.dm +++ b/code/modules/surgery/organs/internal/heart.dm @@ -14,8 +14,8 @@ current_blood = 100 blood_req = 5 oxygen_req = 5 - nutriment_req = 3.5 - hydration_req = 2 + nutriment_req = 3 + hydration_req = 1.5 /// Have we been bypassed to avoid nasty blockages? var/open = FALSE /// If we're not beating that is not a good sign diff --git a/code/modules/surgery/organs/internal/liver.dm b/code/modules/surgery/organs/internal/liver.dm index 747c9632fb1..17e006dd36d 100644 --- a/code/modules/surgery/organs/internal/liver.dm +++ b/code/modules/surgery/organs/internal/liver.dm @@ -17,8 +17,8 @@ current_blood = 25 blood_req = 2 oxygen_req = 4 - nutriment_req = 1.5 - hydration_req = 1.5 + nutriment_req = 1.2 + hydration_req = 1.2 var/alcohol_tolerance = ALCOHOL_RATE //affects how much damage the liver takes from alcohol var/toxTolerance = LIVER_DEFAULT_TOX_TOLERANCE //maximum amount of toxins the liver can just shrug off diff --git a/code/modules/surgery/organs/internal/lungs.dm b/code/modules/surgery/organs/internal/lungs.dm index b7c2bf92e87..f1d6c1f6f42 100644 --- a/code/modules/surgery/organs/internal/lungs.dm +++ b/code/modules/surgery/organs/internal/lungs.dm @@ -18,8 +18,8 @@ current_blood = 25 blood_req = 2 oxygen_req = 4 - nutriment_req = 1.5 - hydration_req = 1.5 + nutriment_req = 1.2 + hydration_req = 1.2 high_threshold_passed = "I feel some sort of constriction around my chest as my breathing becomes shallow and rapid." now_fixed = "My lungs seem to once again be able to hold air." diff --git a/code/modules/surgery/organs/internal/spleen.dm b/code/modules/surgery/organs/internal/spleen.dm index de7676607bb..b35b7251e0d 100644 --- a/code/modules/surgery/organs/internal/spleen.dm +++ b/code/modules/surgery/organs/internal/spleen.dm @@ -18,8 +18,8 @@ current_blood = 20 blood_req = 1 oxygen_req = 2 - nutriment_req = 2.5 - hydration_req = 1 + nutriment_req = 2.4 + hydration_req = 0.9 var/blood_regen_factor = BLOOD_REGEN_FACTOR // how much blood the spleen regenerates per efficiency point, per 2 seconds diff --git a/code/modules/surgery/organs/internal/stomach.dm b/code/modules/surgery/organs/internal/stomach.dm index 7e1ba1e1e01..2019fdd77cd 100644 --- a/code/modules/surgery/organs/internal/stomach.dm +++ b/code/modules/surgery/organs/internal/stomach.dm @@ -15,8 +15,8 @@ current_blood = 20 blood_req = 2 oxygen_req = 4 - nutriment_req = 2 - hydration_req = 1.5 + nutriment_req = 1.5 + hydration_req = 1.2 low_threshold_passed = "My stomach flashes with pain before subsiding. Food doesn't seem like a good idea right now." high_threshold_passed = "My stomach flares up with constant pain. I can hardly stomach the idea of food right now!" diff --git a/code/modules/surgery/organs/organ_processing/stomach.dm b/code/modules/surgery/organs/organ_processing/stomach.dm index ab60b4e3f78..d2c4b1e160f 100644 --- a/code/modules/surgery/organs/organ_processing/stomach.dm +++ b/code/modules/surgery/organs/organ_processing/stomach.dm @@ -43,11 +43,11 @@ owner.satiety += (0.5 * delta_time) hunger_rate *= 2 hunger_rate *= owner.physiology.hunger_mod - owner.adjust_nutrition(-hunger_rate * (0.5 * delta_time)) + owner.adjust_nutrition(-hunger_rate * delta_time) if(owner.hydration > 0) var/thirst_rate = owner.total_hydration_req thirst_rate *= optimal_threshold/max(stomach_efficiency, 25) - owner.adjust_hydration(-thirst_rate * (0.5 * delta_time)) + owner.adjust_hydration(-thirst_rate * delta_time) if(owner.nutrition > NUTRITION_LEVEL_FULL) if(owner.overeatduration < 20 MINUTES) From 55316b8d5946356670b52b553065f67e02b4f127 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Tue, 12 May 2026 20:43:44 -0700 Subject: [PATCH 29/59] revert blood_req --- code/__DEFINES/economy.dm | 1 + code/__DEFINES/medical.dm | 2 +- code/datums/quality/_base_quality_manager.dm | 2 +- code/game/objects/items/ore.dm | 24 +++++++++---------- code/modules/surgery/organs/_organ.dm | 6 ++--- code/modules/surgery/organs/external/ears.dm | 2 +- .../surgery/organs/internal/appendix.dm | 2 +- code/modules/surgery/organs/internal/brain.dm | 10 ++++---- code/modules/surgery/organs/internal/eyes.dm | 2 +- code/modules/surgery/organs/internal/heart.dm | 2 +- code/modules/surgery/organs/internal/liver.dm | 2 +- code/modules/surgery/organs/internal/lungs.dm | 2 +- .../modules/surgery/organs/internal/spleen.dm | 2 +- .../surgery/organs/internal/stomach.dm | 2 +- .../modules/surgery/organs/internal/tongue.dm | 2 +- .../surgery/organs/internal/vocal_cords.dm | 2 +- 16 files changed, 33 insertions(+), 32 deletions(-) diff --git a/code/__DEFINES/economy.dm b/code/__DEFINES/economy.dm index 4288ac18d5e..a8ba2799241 100644 --- a/code/__DEFINES/economy.dm +++ b/code/__DEFINES/economy.dm @@ -20,6 +20,7 @@ #define M_STEEL M_IRON+W_MODERATE // one steel bar #define M_SILVER M_IRON*3 // one silver bar #define M_GOLD M_IRON*5 // one gold bar +#define M_BLACKSTEEL M_IRON*7 // one blacksteel bar // Skill costs - a rarity value add, items requiring a high skill to produce are rarer and has more intrinsic value. So craftsmen can make a profit. #define SKILL_1 2 diff --git a/code/__DEFINES/medical.dm b/code/__DEFINES/medical.dm index 1c9753d6718..c28d63e2c91 100644 --- a/code/__DEFINES/medical.dm +++ b/code/__DEFINES/medical.dm @@ -204,7 +204,7 @@ DEFINE_BITFIELD(organ_flags, list( // ~organ requirements /// Normally 50% of the default blood volume (230cl) -#define DEFAULT_TOTAL_BLOOD_REQ BLOOD_VOLUME_NORMAL * 0.5 +#define DEFAULT_TOTAL_BLOOD_REQ BLOOD_VOLUME_NORMAL * 0.33 #define DEFAULT_TOTAL_OXYGEN_REQ 50 #define DEFAULT_TOTAL_NUTRIMENT_REQ HUNGER_FACTOR #define DEFAULT_TOTAL_HYDRATION_REQ THIRST_FACTOR diff --git a/code/datums/quality/_base_quality_manager.dm b/code/datums/quality/_base_quality_manager.dm index bfb52db27e0..f6b1654a447 100644 --- a/code/datums/quality/_base_quality_manager.dm +++ b/code/datums/quality/_base_quality_manager.dm @@ -63,7 +63,7 @@ // Apply sellprice modifier var/modifier = quality_data["price_modifier"] - if(target.sellprice) + if(modifier && target.sellprice) target.sellprice *= modifier if(track_creation) diff --git a/code/game/objects/items/ore.dm b/code/game/objects/items/ore.dm index 533b0b334f2..c5993cf7a39 100644 --- a/code/game/objects/items/ore.dm +++ b/code/game/objects/items/ore.dm @@ -223,7 +223,7 @@ icon_state = "ingotgold" smeltresult = /obj/item/ingot/gold melting_material = /datum/material/gold - sellprice = 100 + sellprice = M_GOLD item_weight = 12.25 KILOGRAMS /obj/item/ingot/iron @@ -232,7 +232,7 @@ icon_state = "ingotiron" smeltresult = /obj/item/ingot/iron melting_material = /datum/material/iron - sellprice = 25 + sellprice = M_IRON item_weight = 5 KILOGRAMS /obj/item/ingot/thaumic @@ -242,7 +242,7 @@ icon = 'icons/roguetown/misc/alchemy.dmi' smeltresult = /obj/item/ingot/thaumic melting_material = /datum/material/thaumic_iron - sellprice = 25 + sellprice = M_IRON item_weight = 5 KILOGRAMS /obj/item/ingot/copper @@ -251,7 +251,7 @@ icon_state = "ingotcop" smeltresult = /obj/item/ingot/copper melting_material = /datum/material/copper - sellprice = 10 + sellprice = M_IRON * 0.5 item_weight = 5.7 KILOGRAMS /obj/item/ingot/tin @@ -260,7 +260,7 @@ icon_state = "ingottin" smeltresult = /obj/item/ingot/tin melting_material = /datum/material/tin - sellprice = 15 + sellprice = M_IRON * 0.75 item_weight = 4.6 KILOGRAMS /obj/item/ingot/bronze @@ -269,7 +269,7 @@ icon_state = "ingotbronze" smeltresult = /obj/item/ingot/bronze melting_material = /datum/material/bronze - sellprice = 30 + sellprice = M_IRON * 2 item_weight = 5.55 KILOGRAMS /obj/item/ingot/silver @@ -278,7 +278,7 @@ icon_state = "ingotsilv" smeltresult = /obj/item/ingot/silver melting_material = /datum/material/silver - sellprice = 60 + sellprice = M_SILVER item_weight = 6.65 KILOGRAMS /obj/item/ingot/silver/Initialize(mapload) @@ -291,7 +291,7 @@ icon_state = "ingotsteel" smeltresult = /obj/item/ingot/steel melting_material = /datum/material/steel - sellprice = 40 + sellprice = M_STEEL item_weight = 5 KILOGRAMS /obj/item/ingot/steelholy @@ -300,7 +300,7 @@ icon_state = "ingotsteelholy" smeltresult = /obj/item/ingot/steel melting_material = /datum/material/steel //Smelting it removes the blessing - sellprice = 60 + sellprice = M_STEEL * 1.5 item_weight = 5 KILOGRAMS /obj/item/ingot/silverblessed @@ -309,14 +309,14 @@ icon_state = "ingotsilvblessed" smeltresult = /obj/item/ingot/silver melting_material = /datum/material/silver //Smelting it removes the blessing - sellprice = 100 + sellprice = M_SILVER * 1.5 item_weight = 6.65 KILOGRAMS /obj/item/ingot/blacksteel name = "blacksteel bar" desc = "Sacrificing the holy elements of silver for raw strength, this strange and powerful ingot's origin carries dark rumors..." icon_state = "ingotblacksteel" - sellprice = 90 + sellprice = M_BLACKSTEEL smeltresult = /obj/item/ingot/blacksteel melting_material = /datum/material/blacksteel item_weight = 5.2 KILOGRAMS @@ -327,5 +327,5 @@ icon_state = "steel_slag" smeltresult = /obj/item/ingot/steel melting_material = /datum/material/steel - sellprice = 40 + sellprice = M_STEEL - 5 item_weight = 5.5 KILOGRAMS diff --git a/code/modules/surgery/organs/_organ.dm b/code/modules/surgery/organs/_organ.dm index 581496d21eb..dd7877658b2 100644 --- a/code/modules/surgery/organs/_organ.dm +++ b/code/modules/surgery/organs/_organ.dm @@ -225,9 +225,9 @@ if(!blood_req) return if(!in_bleedout) - current_blood = min(current_blood + (blood_req * delta_time), max_blood_storage) //very slow refill + current_blood = min(current_blood + (blood_req * 0.5 * delta_time), max_blood_storage) //very slow refill return - current_blood = max(current_blood - (blood_req * delta_time), 0) + current_blood = max(current_blood - (blood_req * 0.5 * delta_time), 0) // When all blood is lost, take blood from blood vessels if(!current_blood) var/obj/item/organ/artery @@ -239,7 +239,7 @@ break if(artery?.current_blood) var/prev_blood = artery.current_blood - artery.current_blood = max(artery.current_blood - (blood_req * delta_time), 0) + artery.current_blood = max(artery.current_blood - (blood_req * 0.5 * delta_time), 0) current_blood = max(prev_blood - artery.current_blood, 0) if((current_blood <= 0) && !(organ_flags & ORGAN_LIMB_SUPPORTER)) applyOrganDamage(0.2 * delta_time) diff --git a/code/modules/surgery/organs/external/ears.dm b/code/modules/surgery/organs/external/ears.dm index 18359cb494e..a9d33f6c67a 100644 --- a/code/modules/surgery/organs/external/ears.dm +++ b/code/modules/surgery/organs/external/ears.dm @@ -15,7 +15,7 @@ organ_volume = 0.25 max_blood_storage = 2.5 current_blood = 2.5 - blood_req = 0.125 + blood_req = 0.5 oxygen_req = 0.5 nutriment_req = 0.15 hydration_req = 0.15 diff --git a/code/modules/surgery/organs/internal/appendix.dm b/code/modules/surgery/organs/internal/appendix.dm index 7cb6f739da6..fe2ad265b73 100644 --- a/code/modules/surgery/organs/internal/appendix.dm +++ b/code/modules/surgery/organs/internal/appendix.dm @@ -17,7 +17,7 @@ organ_volume = 0.5 max_blood_storage = 2.5 current_blood = 2.5 - blood_req = 0.25 + blood_req = 0.5 oxygen_req = 0.25 nutriment_req = 0.35 hydration_req = 0.2 diff --git a/code/modules/surgery/organs/internal/brain.dm b/code/modules/surgery/organs/internal/brain.dm index 3ad5b7301d2..6ce193d2df3 100644 --- a/code/modules/surgery/organs/internal/brain.dm +++ b/code/modules/surgery/organs/internal/brain.dm @@ -29,7 +29,7 @@ organ_volume = 0.5 max_blood_storage = 100 current_blood = 100 - blood_req = 2.5 + blood_req = 5 oxygen_req = 5 nutriment_req = 3 hydration_req = 1.5 @@ -90,12 +90,12 @@ if(!blood_req) return if(!in_bleedout && (effective_blood_oxygenation >= BLOOD_VOLUME_SAFE)) - current_blood = min(current_blood + (blood_req * delta_time), max_blood_storage) + current_blood = min(current_blood + (blood_req * 0.5 * delta_time), max_blood_storage) return if(in_bleedout) - current_blood = max(current_blood - (blood_req * delta_time), 0) + current_blood = max(current_blood - (blood_req * 0.5 * delta_time), 0) else - current_blood = max(current_blood - (blood_req * ((BLOOD_VOLUME_NORMAL-effective_blood_oxygenation)/BLOOD_VOLUME_NORMAL) * delta_time), 0) + current_blood = max(current_blood - (blood_req * ((BLOOD_VOLUME_NORMAL-effective_blood_oxygenation)/BLOOD_VOLUME_NORMAL) * (0.5 * delta_time)), 0) // When all blood is lost, take blood from blood vessels if(!current_blood) var/obj/item/organ/artery @@ -107,7 +107,7 @@ break if(artery?.current_blood) var/prev_blood = artery.current_blood - artery.current_blood = max(artery.current_blood - (blood_req * delta_time), 0) + artery.current_blood = max(artery.current_blood - (blood_req * 0.5 * delta_time), 0) current_blood = max(prev_blood - artery.current_blood, 0) //Don't apply damage, this is handled by the organ process datum, if necessary diff --git a/code/modules/surgery/organs/internal/eyes.dm b/code/modules/surgery/organs/internal/eyes.dm index 65672ff74eb..75b9c4f8119 100644 --- a/code/modules/surgery/organs/internal/eyes.dm +++ b/code/modules/surgery/organs/internal/eyes.dm @@ -31,7 +31,7 @@ organ_volume = 0.25 max_blood_storage = 5 current_blood = 5 - blood_req = 0.5 + blood_req = 1 oxygen_req = 0.5 nutriment_req = 0.15 hydration_req = 0.15 diff --git a/code/modules/surgery/organs/internal/heart.dm b/code/modules/surgery/organs/internal/heart.dm index 7d0d9519ce6..7f97cf013d9 100644 --- a/code/modules/surgery/organs/internal/heart.dm +++ b/code/modules/surgery/organs/internal/heart.dm @@ -12,7 +12,7 @@ organ_volume = 0.5 max_blood_storage = 100 current_blood = 100 - blood_req = 5 + blood_req = 10 oxygen_req = 5 nutriment_req = 3 hydration_req = 1.5 diff --git a/code/modules/surgery/organs/internal/liver.dm b/code/modules/surgery/organs/internal/liver.dm index 17e006dd36d..55e82c42f4d 100644 --- a/code/modules/surgery/organs/internal/liver.dm +++ b/code/modules/surgery/organs/internal/liver.dm @@ -15,7 +15,7 @@ organ_volume = 2 max_blood_storage = 25 current_blood = 25 - blood_req = 2 + blood_req = 4 oxygen_req = 4 nutriment_req = 1.2 hydration_req = 1.2 diff --git a/code/modules/surgery/organs/internal/lungs.dm b/code/modules/surgery/organs/internal/lungs.dm index f1d6c1f6f42..aefe26e1659 100644 --- a/code/modules/surgery/organs/internal/lungs.dm +++ b/code/modules/surgery/organs/internal/lungs.dm @@ -16,7 +16,7 @@ organ_volume = 1 max_blood_storage = 25 current_blood = 25 - blood_req = 2 + blood_req = 4 oxygen_req = 4 nutriment_req = 1.2 hydration_req = 1.2 diff --git a/code/modules/surgery/organs/internal/spleen.dm b/code/modules/surgery/organs/internal/spleen.dm index b35b7251e0d..59d4a37aca2 100644 --- a/code/modules/surgery/organs/internal/spleen.dm +++ b/code/modules/surgery/organs/internal/spleen.dm @@ -16,7 +16,7 @@ organ_volume = 0.5 max_blood_storage = 20 current_blood = 20 - blood_req = 1 + blood_req = 2 oxygen_req = 2 nutriment_req = 2.4 hydration_req = 0.9 diff --git a/code/modules/surgery/organs/internal/stomach.dm b/code/modules/surgery/organs/internal/stomach.dm index 2019fdd77cd..01b3ac1a133 100644 --- a/code/modules/surgery/organs/internal/stomach.dm +++ b/code/modules/surgery/organs/internal/stomach.dm @@ -13,7 +13,7 @@ organ_volume = 1 max_blood_storage = 20 current_blood = 20 - blood_req = 2 + blood_req = 4 oxygen_req = 4 nutriment_req = 1.5 hydration_req = 1.2 diff --git a/code/modules/surgery/organs/internal/tongue.dm b/code/modules/surgery/organs/internal/tongue.dm index ff6a689c37f..912b380fb15 100644 --- a/code/modules/surgery/organs/internal/tongue.dm +++ b/code/modules/surgery/organs/internal/tongue.dm @@ -10,7 +10,7 @@ organ_volume = 0.5 max_blood_storage = 5 current_blood = 5 - blood_req = 0.5 + blood_req = 1 oxygen_req = 0.5 nutriment_req = 0.3 hydration_req = 0.6 diff --git a/code/modules/surgery/organs/internal/vocal_cords.dm b/code/modules/surgery/organs/internal/vocal_cords.dm index 0ad743d59b0..dce4eef8777 100644 --- a/code/modules/surgery/organs/internal/vocal_cords.dm +++ b/code/modules/surgery/organs/internal/vocal_cords.dm @@ -10,7 +10,7 @@ organ_volume = 1 max_blood_storage = 10 current_blood = 10 - blood_req = 1 + blood_req = 2 oxygen_req = 2.5 nutriment_req = 1.5 From a94c5f45286e287f9e7d83dbdd24dda38592f7bd Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Wed, 13 May 2026 15:02:35 -0700 Subject: [PATCH 30/59] another audit and balancing --- code/__DEFINES/medical.dm | 38 +++-- code/__DEFINES/pain.dm | 2 +- code/datums/enchantments/bloodthirsty.dm | 2 +- code/datums/injury/_injury.dm | 59 ++++---- code/datums/injury/bodypart_injury.dm | 4 +- code/datums/injury/organic/bruise.dm | 1 + code/datums/injury/organic/burn.dm | 4 +- code/datums/injury/organic/divine_lash.dm | 3 - code/datums/injury/organic/puncture.dm | 2 +- code/datums/status_effects/rogue/healing.dm | 23 +-- code/datums/wounds/arteries.dm | 2 +- code/datums/wounds/disembowel.dm | 2 +- code/datums/wounds/spill.dm | 2 +- .../objects/items/chimeric_organs/ifak.dm | 3 +- code/game/objects/items/natural.dm | 16 +- .../objects/items/natural/clothfibersthorn.dm | 4 +- code/game/objects/items/needle.dm | 140 +++++++++--------- .../objects/items/weapons/melee/godweapons.dm | 5 +- code/game/objects/structures/traps.dm | 2 +- code/modules/admin/view_variables/topic.dm | 2 +- .../covens/coven_powers/bloodheal.dm | 25 +--- code/modules/clothing/belt/misc.dm | 1 - .../spells/regeneration_cycle.dm | 8 +- .../crafting/alchemy/herbal_recipes.dm | 29 ++-- code/modules/crafting/alchemy/reagents.dm | 53 +++---- code/modules/fishing/leeches.dm | 2 +- .../mob/living/carbon/carbon_medical.dm | 8 - .../modules/mob/living/carbon/damage_procs.dm | 8 +- .../mob/living/carbon/human/human_defense.dm | 2 +- .../mob/living/carbon/human/human_suicide.dm | 2 +- code/modules/mob/living/carbon/life.dm | 6 +- code/modules/mob/living/damage_procs.dm | 8 +- code/modules/mob/living/inhand_holder.dm | 2 +- .../mob/living/simple_animal/damage_procs.dm | 2 +- .../chemistry/reagents/medicine_reagents.dm | 45 ++---- code/modules/surgery/bodyparts/_bodyparts.dm | 139 +++++++++-------- .../surgery/bodyparts/bodypart_examine.dm | 14 +- .../surgery/bodyparts/bodypart_wounds.dm | 18 +-- code/modules/surgery/surgeries/bestow_lux.dm | 3 +- code/modules/surgery/surgeries/cure_rot.dm | 2 +- code/modules/surgery/surgeries/extract_lux.dm | 2 + code/modules/surgery/surgeries/healing.dm | 27 +++- .../surgery/surgeries/organic_steps.dm | 6 +- code/modules/surgery/surgeries/revival.dm | 2 + code/modules/surgery/surgery_tools_rogue.dm | 8 +- 45 files changed, 335 insertions(+), 403 deletions(-) diff --git a/code/__DEFINES/medical.dm b/code/__DEFINES/medical.dm index c28d63e2c91..ab591df7c96 100644 --- a/code/__DEFINES/medical.dm +++ b/code/__DEFINES/medical.dm @@ -317,33 +317,38 @@ DEFINE_BITFIELD(organ_flags, list( // ~wound categories /// doesn't actually wound -#define WOUND_NONE 0 +#define WOUND_NONE 0 /// any brute weapon/attack that doesn't have sharpness. rolls for blunt bone wounds -#define WOUND_BLUNT 1 +#define WOUND_BLUNT (1<<0) /// any sharp weapon, edged or pointy, can cause arteries to be torn -#define WOUND_ARTERY 2 +#define WOUND_ARTERY (1<<1) /// any sharp weapon, edged or pointy, can cause tendons to be torn -#define WOUND_TENDON 3 +#define WOUND_TENDON (1<<2) /// any sharp weapon, edged or pointy, can cause nerves to be torn -#define WOUND_NERVE 4 +#define WOUND_NERVE (1<<3) /// britification lol -#define WOUND_TEETH 5 +#define WOUND_TEETH (1<<4) /// any kind of organ spilling -#define WOUND_SPILL 6 +#define WOUND_SPILL (1<<5) /// any brute weapon/attack with sharpness = SHARP_EDGED. rolls for slash wounds -#define WOUND_SLASH 7 +#define WOUND_SLASH (1<<6) /// any brute weapon/attack with sharpness = SHARP_POINTY. rolls for piercing wounds -#define WOUND_PIERCE 8 +#define WOUND_PUNCTURE (1<<7) /// any concentrated burn attack (lasers really). rolls for burning wounds -#define WOUND_BURN 9 +#define WOUND_BURN (1<<8) ///any wounds from bites -#define WOUND_BITE 10 +#define WOUND_BITE (1<<9) ///any wounds from lashes -#define WOUND_LASH 11 +#define WOUND_LASH (1<<10) ///any wounds from internal bruising (only really a thing for weird chems that should cause brutes) -#define WOUND_INTERNAL_BRUISE 12 +#define WOUND_INTERNAL_BRUISE (1<<11) ///wounds coming from divine sources -#define WOUND_DIVINE 13 +#define WOUND_DIVINE (1<<12) + +#define SEWABLE_WOUND_TYPES (WOUND_SLASH|WOUND_PUNCTURE|WOUND_BITE|WOUND_LASH) +#define BRUTE_WOUND_TYPES (SEWABLE_WOUND_TYPES|WOUND_BLUNT|WOUND_INTERNAL_BRUISE) +#define FIRE_WOUND_TYPES (WOUND_BURN) +#define CUT_WOUND_TYPES (WOUND_SLASH|WOUND_PUNCTURE|WOUND_BITE) // ~injury flags /// This injury creates sounds hints when applied @@ -358,7 +363,7 @@ DEFINE_BITFIELD(organ_flags, list( #define INJURY_SALVED (1<<4) /// This injury is disinfected, and the infection has been wiped AND won't progress #define INJURY_DISINFECTED (1<<5) -/// This is a surgical injury and will not autoheal +/// This is a surgical injury and will not autoheal. Also not be healed by healing surgery step. #define INJURY_SURGICAL (1<<6) /// This injury is retracted and gives access to people's yummy guts and bones #define INJURY_RETRACTED (1<<7) @@ -379,3 +384,6 @@ DEFINE_BITFIELD(organ_flags, list( #define BLEEDING_MESSAGE_BASE_CD 15 SECONDS /// Arbitrary value for "noticeable bleeding" #define BLEED_RATE_NOTICABLE 1.5 + +/// Injuries bleed at (bleed_rate / BLEED_DAMAGE_RATIO) per tick +#define BLEED_DAMAGE_RATIO 30 diff --git a/code/__DEFINES/pain.dm b/code/__DEFINES/pain.dm index 9d914606b72..87123f1be34 100644 --- a/code/__DEFINES/pain.dm +++ b/code/__DEFINES/pain.dm @@ -32,7 +32,7 @@ #define PAIN_NO_SPEAK 250 /// Divisor used in pain calculations, since carbon pain is a flat amount and spread across bodyparts -#define PAINKILLER_DIVISOR 1.5 +#define PAINKILLER_DIVISOR 1.75 /// Use this to keep the speed of pain-related systems consistent relatively #define PAIN_SYSTEM_SPEED_MODIFIER 3 diff --git a/code/datums/enchantments/bloodthirsty.dm b/code/datums/enchantments/bloodthirsty.dm index d1c800bc429..1f8812ecc9c 100644 --- a/code/datums/enchantments/bloodthirsty.dm +++ b/code/datums/enchantments/bloodthirsty.dm @@ -23,7 +23,7 @@ var/bleeding_bite = FALSE for(var/datum/injury/injury in carbon.all_injuries) - if(injury.damage_type != WOUND_BITE) + if(!(injury.damage_type & WOUND_BITE)) continue if(!injury.get_bleed_rate()) continue diff --git a/code/datums/injury/_injury.dm b/code/datums/injury/_injury.dm index 0901a9de1f2..b89d2017833 100644 --- a/code/datums/injury/_injury.dm +++ b/code/datums/injury/_injury.dm @@ -1,4 +1,3 @@ -#define BLEED_DAMAGE_RATIO 30 //This is basically the baystation wound datum, which i thought would synergize well with the TG wounds /**************************************************** @@ -27,7 +26,7 @@ var/injury_flags = (INJURY_SOUND_HINTS) /// world.time when this injury was created var/created = 0 - /// Number of inuries stored in this datum + /// Number of injuries stored in this datum var/amount = 1 /// Amount of germs in the injury var/germ_level = 0 @@ -45,8 +44,10 @@ var/list/stages /// Maximum stage at which bleeding should still happen - Beyond this stage bleeding is prevented var/max_bleeding_stage = 0 - /// One of WOUND_BLUNT, WOUND_SLASH, WOUND_PIERCE, WOUND_BURN + /// One of WOUND_BLUNT, WOUND_SLASH, WOUND_PUNCTURE, WOUND_BURN var/damage_type = WOUND_SLASH + /// The base amount of autoheal this injury has. + var/base_autoheal_amount = 0 /// The maximum amount of damage that this injury can have and still autoheal var/autoheal_cutoff = 15 /// How much having this injury will add to all future check_wounding() rolls on this limb @@ -171,8 +172,12 @@ return FALSE if(required_status != BODYPART_ORGANIC) return FALSE + if(!can_heal()) + return FALSE if(parent_bodypart.is_retracted()) return FALSE + if(is_surgical()) + return FALSE if(germ_level > INFECTION_LEVEL_ONE) return FALSE return ((damage_per_injury() <= autoheal_cutoff) ? TRUE : (is_treated() || parent_bodypart?.limb_flags & BODYPART_GOOD_HEALER)) @@ -180,16 +185,17 @@ // checks whether the injury has been appropriately treated /datum/injury/proc/is_treated() switch(damage_type) - if(WOUND_SLASH, WOUND_PIERCE, WOUND_BITE) - return (is_bandaged() || is_sutured() || parent_bodypart.bandage) - if(WOUND_BLUNT, WOUND_LASH, WOUND_DIVINE) - return (is_bandaged() || parent_bodypart.bandage) + if(WOUND_SLASH, WOUND_PUNCTURE, WOUND_BITE, WOUND_LASH) + return (is_bandaged() || is_sutured()) + if(WOUND_DIVINE) + return (is_bandaged()) if(WOUND_BURN) - return (is_salved() || (is_disinfected() && (is_bandaged() || parent_bodypart.bandage) ) ) + return (is_salved() || (is_disinfected() && is_bandaged()) ) + return TRUE // Checks whether other other can be merged into src. /datum/injury/proc/can_merge(datum/injury/other) - if(other.damage_type != damage_type) + if(!(other.damage_type & damage_type)) return FALSE if(other.type != type) return FALSE @@ -227,10 +233,9 @@ if(required_status & BODYPART_ROBOTIC) //Robotic injury return FALSE - if(damage_type == WOUND_BLUNT && !is_bleeding()) //bruises only infectable if bleeding + if(damage_type & WOUND_BLUNT && !is_bleeding()) //bruises only infectable if bleeding return FALSE - switch(damage_type) if(WOUND_BLUNT) return DT_PROB(damage/2, delta_time) @@ -238,7 +243,7 @@ return DT_PROB(damage*2, delta_time) if(WOUND_SLASH) return DT_PROB(damage, delta_time) - if(WOUND_PIERCE) + if(WOUND_PUNCTURE) return DT_PROB(damage*1.25, delta_time) if(WOUND_LASH) return DT_PROB(damage * 1.15, delta_time) @@ -251,29 +256,35 @@ return CEILING(get_bleed_rate() * 0.2, 0.1) /datum/injury/proc/can_heal() - if(damage_type == WOUND_DIVINE) - return FALSE - return TRUE + return !(damage_type & WOUND_DIVINE) -// heal the given amount of damage, and if the given amount of damage was more -// than what needed to be healed, return how much heal was left -/datum/injury/proc/heal_damage(amount_heal) +/// Heal the given amount of damagem and returns how much is left over from amount_heal. +/// Keep update_bodypart FALSE if you are looping through injuries. +/// CAN QDELETE INJURY. +/datum/injury/proc/heal_damage(amount_heal, update_bodypart = TRUE, updating_health = FALSE) + if(amount_heal <= 0) + return var/healed_damage = min(damage, amount_heal) damage -= healed_damage while(damage_per_injury() < damage_list[current_stage] && current_stage < length(desc_list)) current_stage++ desc = desc_list[current_stage] min_damage = damage_list[current_stage] + var/obj/item/bodypart/cached_bodypart = parent_bodypart + var/mob/living/carbon/cached_parent_mob = parent_mob if(!damage) qdel(src) - + if(update_bodypart && cached_bodypart) + updating_health &= cached_bodypart?.post_damage_change(updating_health) + if(updating_health) + cached_parent_mob?.update_damage_overlays() // return amount of healing still leftover, can be used for other injuries return (amount_heal - healed_damage) // returns whether this injury can absorb the given amount of damage. // this will prevent large amounts of damage being trapped in less severe injury types /datum/injury/proc/can_worsen(damage_type, damage) - if(src.damage_type != damage_type) + if(!(src.damage_type & damage_type)) return FALSE //incompatible damage types if(amount > 1) @@ -293,11 +304,9 @@ current_stage = min(max_bleeding_stage + 1, length(damage_list)) desc = desc_list[current_stage] min_damage = damage_list[current_stage] - if(damage > min_damage) - heal_damage(damage - min_damage) injury_flags &= ~INJURY_RETRACTED - if(parent_bodypart?.post_damage_change()) - parent_bodypart?.owner?.update_damage_overlays() + if(damage > min_damage) + heal_damage(damage - min_damage, TRUE) // opens the injury and worsens it /datum/injury/proc/open_injury(damage, retracting = FALSE) @@ -414,5 +423,3 @@ /datum/injury/proc/is_bandaged() return CHECK_BITFIELD(injury_flags, INJURY_BANDAGED) - -#undef BLEED_DAMAGE_RATIO diff --git a/code/datums/injury/bodypart_injury.dm b/code/datums/injury/bodypart_injury.dm index 184b5b33680..3328d244623 100644 --- a/code/datums/injury/bodypart_injury.dm +++ b/code/datums/injury/bodypart_injury.dm @@ -16,7 +16,7 @@ return /datum/injury/slash/deep if(0 to 15) return /datum/injury/slash/small - if(WOUND_PIERCE) + if(WOUND_PUNCTURE) switch(damage) if(60 to INFINITY) return /datum/injury/puncture/massive @@ -119,7 +119,7 @@ return /datum/injury/slash/deep/mechanical if(0 to 15) return /datum/injury/slash/small/mechanical - if(WOUND_PIERCE) + if(WOUND_PUNCTURE) switch(damage) if(60 to INFINITY) return /datum/injury/puncture/massive/mechanical diff --git a/code/datums/injury/organic/bruise.dm b/code/datums/injury/organic/bruise.dm index b64eb04320f..f68af65a4a8 100644 --- a/code/datums/injury/organic/bruise.dm +++ b/code/datums/injury/organic/bruise.dm @@ -3,6 +3,7 @@ bleed_threshold = 20 autoheal_cutoff = 30 damage_type = WOUND_BLUNT + bleed_rate = 0.9 /datum/injury/bruise/small stages = list( diff --git a/code/datums/injury/organic/burn.dm b/code/datums/injury/organic/burn.dm index 6f178af34cd..d3863235780 100644 --- a/code/datums/injury/organic/burn.dm +++ b/code/datums/injury/organic/burn.dm @@ -22,7 +22,7 @@ return prob(damage*2) if(WOUND_SLASH) return prob(damage) - if(WOUND_PIERCE) + if(WOUND_PUNCTURE) return prob(damage*1.25) return FALSE @@ -41,7 +41,7 @@ /* /datum/injury/burn/receive_damage(damage_received = 0, pain_received = 0, wounding_type = WOUND_BLUNT) . = ..() - if((wounding_type == WOUND_BURN) && (damage + damage_received >= 50) && parent_bodypart) + if((wounding_type & WOUND_BURN) && (damage + damage_received >= 50) && parent_bodypart) if(!parent_bodypart.is_dead()) if(parent_bodypart.is_organic_limb()) parent_bodypart.kill_limb() diff --git a/code/datums/injury/organic/divine_lash.dm b/code/datums/injury/organic/divine_lash.dm index 480fb054f54..1a597b96445 100644 --- a/code/datums/injury/organic/divine_lash.dm +++ b/code/datums/injury/organic/divine_lash.dm @@ -5,9 +5,6 @@ bleed_threshold = INFINITY fade_away_time = INFINITY -/datum/injury/divine/can_autoheal() - return FALSE // nuh uh - /datum/injury/divine/smite stages = list( "raw smite wound" = 10, diff --git a/code/datums/injury/organic/puncture.dm b/code/datums/injury/organic/puncture.dm index 8cdb534a74d..fe90b6fedae 100644 --- a/code/datums/injury/organic/puncture.dm +++ b/code/datums/injury/organic/puncture.dm @@ -1,6 +1,6 @@ /datum/injury/puncture bleed_threshold = 10 - damage_type = WOUND_PIERCE + damage_type = WOUND_PUNCTURE /datum/injury/puncture/can_worsen(damage_type, damage) return FALSE //cannot be enlargened diff --git a/code/datums/status_effects/rogue/healing.dm b/code/datums/status_effects/rogue/healing.dm index 8dcc31917d3..ce54d13fd9e 100644 --- a/code/datums/status_effects/rogue/healing.dm +++ b/code/datums/status_effects/rogue/healing.dm @@ -36,25 +36,12 @@ /datum/status_effect/buff/healing/tick(seconds_between_ticks) owner.adjust_blood_volume(healing_on_tick + 1, maximum = BLOOD_VOLUME_NORMAL) - owner.adjustOxyLoss(-healing_on_tick, 0) - owner.adjustToxLoss(-healing_on_tick, 0) + owner.adjustOxyLoss(-healing_on_tick, FALSE) + owner.adjustToxLoss(-healing_on_tick, FALSE) owner.adjustOrganLoss(ORGAN_SLOT_BRAIN, -healing_on_tick) - owner.adjustCloneLoss(-healing_on_tick, 0) + owner.adjustCloneLoss(-healing_on_tick, FALSE) + owner.adjustBruteLoss(-healing_on_tick, FALSE) + owner.adjustFireLoss(-healing_on_tick, TRUE) if(length(owner.get_wounds()) && owner.heal_wounds(healing_on_tick, src)) owner.update_damage_overlays() - - if(!iscarbon(owner)) - return - - var/mob/living/carbon/carbon_owner = owner - var/healing_amount = healing_on_tick - for(var/datum/injury/injury as anything in carbon_owner.all_injuries) - if(!healing_on_tick) - break - if(!injury.can_heal()) - continue - healing_on_tick = injury.heal_damage(healing_on_tick) - if(healing_amount != healing_on_tick) // we healed something - carbon_owner.update_all_limb_states() - diff --git a/code/datums/wounds/arteries.dm b/code/datums/wounds/arteries.dm index 60f5a4ca459..aa6de16281b 100644 --- a/code/datums/wounds/arteries.dm +++ b/code/datums/wounds/arteries.dm @@ -16,7 +16,7 @@ /datum/wound/artery/can_apply_to_bodypart(obj/item/bodypart/affected) if(affected.status == BODYPART_ROBOTIC) return FALSE - if(!affected.get_cut()) + if(!affected.get_cut(ignore_gauze = TRUE)) return FALSE return ..() diff --git a/code/datums/wounds/disembowel.dm b/code/datums/wounds/disembowel.dm index 538d315d5c8..44d67d90156 100644 --- a/code/datums/wounds/disembowel.dm +++ b/code/datums/wounds/disembowel.dm @@ -45,7 +45,7 @@ break var/gaping_injury = FALSE for(var/datum/injury/injury as anything in new_limb.injuries) - if(injury.damage_type != WOUND_SLASH) + if(!(injury.damage_type & WOUND_SLASH)) continue if(injury.damage && (injury.damage >= 30)) gaping_wound = TRUE diff --git a/code/datums/wounds/spill.dm b/code/datums/wounds/spill.dm index 4874197adc6..d6eedac2817 100644 --- a/code/datums/wounds/spill.dm +++ b/code/datums/wounds/spill.dm @@ -41,7 +41,7 @@ break var/gaping_injury = FALSE for(var/datum/injury/injury as anything in new_limb.injuries) - if(injury.damage_type != WOUND_SLASH) + if(!(injury.damage_type & WOUND_SLASH)) continue if(injury.damage && (injury.damage >= 30)) gaping_wound = TRUE diff --git a/code/game/objects/items/chimeric_organs/ifak.dm b/code/game/objects/items/chimeric_organs/ifak.dm index 08faeca93ce..6cd0981e9ab 100644 --- a/code/game/objects/items/chimeric_organs/ifak.dm +++ b/code/game/objects/items/chimeric_organs/ifak.dm @@ -12,8 +12,7 @@ /obj/item/reagent_containers/syringe, /obj/item/natural/cloth/bandage, /obj/item/natural/cloth/bandage, - /obj/item/natural/cloth/bandage, - /obj/item/natural/cloth/bandage, + /obj/item/natural/bundle/fibers/full, /obj/item/storage/fancy/pilltin/sate, /obj/item/storage/fancy/pilltin/devour, /obj/item/candle/yellow, diff --git a/code/game/objects/items/natural.dm b/code/game/objects/items/natural.dm index 2af44522b7f..ba604eb1639 100644 --- a/code/game/objects/items/natural.dm +++ b/code/game/objects/items/natural.dm @@ -64,11 +64,16 @@ var/obj/item/stacktype = /obj/item/natural/fibers var/stackname = "fibers" var/bundle_verb = "bundle" + /// For every amount / items_per_increase, increase a storage dimension by 1. var/items_per_increase = 5 var/base_width = 32 var/base_height = 32 +/obj/item/natural/bundle/Initialize(mapload) + . = ..() + update_bundle() + /obj/item/natural/bundle/get_carry_weight(atom/carrier) . = initial(stacktype.item_weight) * amount @@ -204,18 +209,21 @@ else if(icon3 != null) icon_state = icon3 + + if(!items_per_increase) + return + var/increases = FLOOR(amount / items_per_increase, 1) - var/height = FALSE + var/dimension = FALSE grid_height = base_height grid_width = base_width for(var/i = 1 to increases) - if(height) - height = FALSE + if(dimension) grid_height += 32 else - height = TRUE grid_width += 32 + dimension = !dimension if(item_flags & IN_STORAGE) var/obj/item/location = loc var/datum/component/storage/storage = location.GetComponent(/datum/component/storage) diff --git a/code/game/objects/items/natural/clothfibersthorn.dm b/code/game/objects/items/natural/clothfibersthorn.dm index 8475fe03084..fcdc5029432 100644 --- a/code/game/objects/items/natural/clothfibersthorn.dm +++ b/code/game/objects/items/natural/clothfibersthorn.dm @@ -130,11 +130,11 @@ stacktype = /obj/item/natural/fibers icon1step = 3 icon2step = 6 + items_per_increase = 7 /obj/item/natural/bundle/fibers/full/Initialize() - . = ..() amount = maxamount - update_bundle() + . = ..() /obj/item/natural/bundle/fibers/sinew name = "sinew fiber bundle" diff --git a/code/game/objects/items/needle.dm b/code/game/objects/items/needle.dm index 1968e482c04..5a5b9727cf7 100644 --- a/code/game/objects/items/needle.dm +++ b/code/game/objects/items/needle.dm @@ -53,25 +53,22 @@ stringamt = stringamt - used update_appearance(UPDATE_OVERLAYS) return TRUE -// if(stringamt <= 0) -// qdel(src) /obj/item/needle/attack(mob/living/M, mob/user, list/modifiers) sew_wounds(M, user) /obj/item/needle/attackby(obj/item/I, mob/user, list/modifiers) if(istype(I, /obj/item/natural/fibers)) - if(maxstring - stringamt < 5) + if(stringamt >= maxstring) to_chat(user, span_warning("Not enough room for more thread!")) return - else - to_chat(user, "I begin threading the needle with additional fibers...") - if(do_after(user, 6 SECONDS - GET_MOB_SKILL_VALUE_OLD(user, /datum/attribute/skill/misc/sewing), I)) - stringamt += 5 - to_chat(user, "I replenish the needle's thread!") - qdel(I) - update_appearance(UPDATE_OVERLAYS) - return + to_chat(user, "I begin threading the needle with additional fibers...") + if(do_after(user, 6 SECONDS - GET_MOB_SKILL_VALUE_OLD(user, /datum/attribute/skill/misc/sewing), I)) + stringamt = min(stringamt + 12, maxstring) + to_chat(user, "I replenish the needle's thread!") + qdel(I) + update_appearance(UPDATE_OVERLAYS) + return return ..() /obj/item/needle/pre_attack(atom/A, mob/living/user, list/modifiers) @@ -180,14 +177,14 @@ user.visible_message(span_warning("[user] damages [I] further!")) playsound(src, 'sound/foley/cloth_rip.ogg', 50, TRUE) - if(prob(10 * (7 - skill_level))) - use(1) + use(1) var/amt2raise = GET_MOB_ATTRIBUTE_VALUE(user, STAT_INTELLIGENCE) * 0.25 if(repair_percent <= 0) amt2raise *= 0.25 user.mind.add_sleep_experience(I.sewrepair, amt2raise) return TRUE return ..() + /obj/item/needle/proc/sew_wounds(mob/living/carbon/target, mob/living/user) if(!istype(user) || !istype(target)) return FALSE @@ -207,41 +204,54 @@ to_chat(doctor, span_warning("There is a bandage in the way.")) return FALSE - if(affecting.get_cut()) - if(affecting.is_artery_torn()) - var/time = 5 SECONDS - time *= (ATTRIBUTE_MIDDLING/max(GET_MOB_ATTRIBUTE_VALUE(doctor, STAT_PERCEPTION), 1)) - playsound(patient, 'sound/foley/sewflesh.ogg', 100, TRUE, -2) - if(!do_after(doctor, time, patient)) - to_chat(doctor, span_warning("I must stand still!")) - return FALSE - if(stringamt < 1) - to_chat(doctor, span_warning("The needle has no thread left!")) - return FALSE - var/amt2raise = GET_MOB_ATTRIBUTE_VALUE(doctor, STAT_INTELLIGENCE) - if(doctor.diceroll(GET_MOB_SKILL_VALUE(doctor, /datum/attribute/skill/misc/medicine)-1, context = DICE_CONTEXT_PHYSICAL) <= DICE_FAILURE) - to_chat(doctor, span_warning("My hand slips!")) - user.adjust_experience(/datum/attribute/skill/misc/medicine, amt2raise * 0.2 * doctor.get_learning_boon(/datum/attribute/skill/misc/medicine)) - return FALSE - user.adjust_experience(/datum/attribute/skill/misc/medicine, amt2raise * doctor.get_learning_boon(/datum/attribute/skill/misc/medicine)) - doctor.visible_message( - span_green("[doctor] sutures [patient]'s [affecting.name] arteries with \the [src]."), - span_green("I suture [patient]'s [affecting.name] arteries with \the [src].") - ) - use(1) - for(var/obj/item/organ/artery in affecting.getorganslotlist(ORGAN_SLOT_ARTERY)) - if(artery.damage) - artery.applyOrganDamage(-min(artery.maxHealth/2, 50)) - return TRUE + var/doctor_skill = GET_MOB_SKILL_VALUE(doctor, /datum/attribute/skill/misc/medicine) + var/perception_mod = 1 - 0.5 * (GET_MOB_ATTRIBUTE_VALUE(doctor, STAT_PERCEPTION) - ATTRIBUTE_MIDDLING)/(ATTRIBUTE_MAX - SKILL_MIDDLING) + var/doctor_mod = 1 - 0.9 * (doctor_skill - SKILL_MIDDLING)/(SKILL_MAX - SKILL_MIDDLING) + // First try to fix arteries + if(affecting.get_cut() && affecting.is_artery_torn()) + var/time = 5 SECONDS + time *= perception_mod * doctor_mod + playsound(patient, 'sound/foley/sewflesh.ogg', 100, TRUE, -2) + if(!do_after(doctor, time, patient)) + to_chat(doctor, span_warning("I must stand still!")) + return FALSE + if(stringamt < 1) + to_chat(doctor, span_warning("The needle has no thread left!")) + return FALSE + var/amt2raise = GET_MOB_ATTRIBUTE_VALUE(doctor, STAT_INTELLIGENCE) * doctor.get_learning_boon(/datum/attribute/skill/misc/medicine) + if(doctor.diceroll(doctor_skill - 1, context = DICE_CONTEXT_PHYSICAL) <= DICE_FAILURE) + to_chat(doctor, span_warning("My hand slips!")) + user.adjust_experience(/datum/attribute/skill/misc/medicine, amt2raise * 0.2) + return FALSE + user.adjust_experience(/datum/attribute/skill/misc/medicine, amt2raise) + doctor.visible_message( + span_green("[doctor] sutures [patient]'s [affecting.name] arteries with \the [src]."), + span_green("I suture [patient]'s [affecting.name] arteries with \the [src].")) + use(1) + for(var/obj/item/organ/artery in affecting.getorganslotlist(ORGAN_SLOT_ARTERY)) + if(artery.damage) + artery.applyOrganDamage(-min(artery.maxHealth/2, 50)) + return TRUE + + // Then try to sew wounds (crits) + var/list/sewable = affecting.get_sewable_wounds() + if(length(sewable)) + var/datum/wound/target_wound = browser_input_list(doctor, "Which critical wound?", "WOUND CRAFT", sewable) + if(QDELETED(target_wound) || QDELETED(src) || QDELETED(doctor) || QDELETED(user)) + return FALSE + if(target_wound && target_wound.do_sewing_step(doctor, src)) return TRUE - var/injury_healed = FALSE - for(var/thing in affecting.injuries) - var/datum/injury/injury = thing - if(!(injury.damage_type in list(WOUND_SLASH, WOUND_PIERCE, WOUND_BITE))) + // Finally injuries + for(var/datum/injury/injury as anything in affecting.injuries) + if(!(injury.damage_type & SEWABLE_WOUND_TYPES)) + continue + if(!injury.can_heal()) + continue + if(injury.is_sutured()) continue - var/time = 2 SECONDS + (injury.damage * 0.5) - time *= min(time * 1.5, (ATTRIBUTE_MIDDLING/max(GET_MOB_ATTRIBUTE_VALUE(user, STAT_PERCEPTION), 1))) + var/time = 2 SECONDS + min(injury.damage * 0.1, 2 SECONDS) + time *= perception_mod * doctor_mod playsound(target, 'sound/foley/sewflesh.ogg', 65, FALSE) if(!do_after(user, time, target)) to_chat(user, span_warning("I must stand still!")) @@ -249,44 +259,32 @@ if(!use(1)) to_chat(user, span_warning("All used up...")) return - injury.suture_injury() - if((injury.damage_per_injury() <= injury.autoheal_cutoff)) - continue - injury.heal_damage(10) - var/amt2raise = GET_MOB_ATTRIBUTE_VALUE(doctor, STAT_INTELLIGENCE) - user.adjust_experience(/datum/attribute/skill/misc/medicine, amt2raise * doctor.get_learning_boon(/datum/attribute/skill/misc/medicine)) - if(affecting.post_damage_change()) - target.update_damage_overlays() + var/amt2raise = GET_MOB_ATTRIBUTE_VALUE(doctor, STAT_INTELLIGENCE) * doctor.get_learning_boon(/datum/attribute/skill/misc/medicine) + user.adjust_experience(/datum/attribute/skill/misc/medicine, amt2raise) + . = TRUE + var/injury_heal = min(10, injury.damage_per_injury() - injury.autoheal_cutoff) + injury.heal_damage(injury_heal) if(injury.damage_per_injury() > injury.autoheal_cutoff) user.visible_message(span_green("[user] partially stitches \a [injury.get_desc()] on [target]'s [affecting.name] with \the [src]."), \ span_green("I partially stitch \a [injury.get_desc()] on \the [affecting.name] with \the [src].")) else user.visible_message(span_green("[user] stitches \a [injury.get_desc()] shut on [target]'s [affecting.name] with \the [src]."), \ span_green("I stitch \a [injury.get_desc()] shut on \the [affecting.name] with \the [src].")) - injury_healed = TRUE + injury.suture_injury() + break - var/list/sewable = affecting.get_sewable_wounds() - if(!length(sewable)) - if(!injury_healed) - to_chat(doctor, span_warning("There aren't any wounds to be sewn.")) - return FALSE - var/datum/wound/target_wound - if(length(sewable) > 1) - target_wound = browser_input_list(doctor, "Which wound?", "WOUND CRAFT", sewable) - else - target_wound = sewable[1] - if(!target_wound || QDELETED(target_wound) || QDELETED(src) || QDELETED(doctor) || QDELETED(user)) - return FALSE - if(!target_wound.do_sewing_step(doctor, src)) - return FALSE - return TRUE + if(.) + return TRUE + + to_chat(doctor, span_warning("There aren't any wounds or injuries left to be sewn.")) + return FALSE /obj/item/needle/thorn name = "needle" icon_state = "thornneedle" desc = "This rough needle can be used to sew cloth and wounds." - stringamt = 8 - maxstring = 8 + stringamt = 12 + maxstring = 12 anvilrepair = null melting_material = null item_weight = 3 GRAMS diff --git a/code/game/objects/items/weapons/melee/godweapons.dm b/code/game/objects/items/weapons/melee/godweapons.dm index bab7d7606b1..2d492946438 100644 --- a/code/game/objects/items/weapons/melee/godweapons.dm +++ b/code/game/objects/items/weapons/melee/godweapons.dm @@ -158,10 +158,7 @@ return playsound(user, 'sound/surgery/scalpel2.ogg', 70) if(do_after(user, 0.5 SECONDS, target)) - var/datum/injury/ouchie = C.create_injury(WOUND_SLASH, C.max_damage * 0.3, TRUE) - if(!ouchie) - return - ouchie.injury_flags |= INJURY_SURGICAL + C.create_injury(WOUND_SLASH, C.max_damage * 0.3, TRUE) playsound(user, 'sound/surgery/organ2.ogg', 70) if(do_after(user, 0.5 SECONDS, target)) diff --git a/code/game/objects/structures/traps.dm b/code/game/objects/structures/traps.dm index 0ac72b9b467..9ecee4d073f 100644 --- a/code/game/objects/structures/traps.dm +++ b/code/game/objects/structures/traps.dm @@ -185,7 +185,7 @@ var/obj/item/bodypart/part = victim.get_bodypart(prob(50) ? BODY_ZONE_L_LEG : BODY_ZONE_R_LEG) if(isnull(part)) part = victim.get_bodypart(BODY_ZONE_CHEST) - part?.create_injury(WOUND_PIERCE, 30, TRUE) + part?.create_injury(WOUND_PUNCTURE, 30, TRUE) victim.emote("scream") post_triggered() diff --git a/code/modules/admin/view_variables/topic.dm b/code/modules/admin/view_variables/topic.dm index 8b1d9246a64..76611433b20 100644 --- a/code/modules/admin/view_variables/topic.dm +++ b/code/modules/admin/view_variables/topic.dm @@ -82,7 +82,7 @@ var/newamt switch(Text) if("brute") - L.adjustBruteLoss(amount, forced = TRUE, damage_type = WOUND_DIVINE, true_heal = TRUE) + L.adjustBruteLoss(amount, forced = TRUE) newamt = L.getBruteLoss() if("fire") L.adjustFireLoss(amount, forced = TRUE) diff --git a/code/modules/antagonists/villain/neu_vampires/covens/coven_powers/bloodheal.dm b/code/modules/antagonists/villain/neu_vampires/covens/coven_powers/bloodheal.dm index e8cd6bd813d..d0b5e3ec478 100644 --- a/code/modules/antagonists/villain/neu_vampires/covens/coven_powers/bloodheal.dm +++ b/code/modules/antagonists/villain/neu_vampires/covens/coven_powers/bloodheal.dm @@ -47,29 +47,16 @@ /datum/coven_power/bloodheal/proc/trigger_healing() // Calculate healing amounts based on level var/bashing_lethal_heal = HEAL_BASHING_LETHAL * level - var/original_brute_heal = bashing_lethal_heal var/aggravated_heal = HEAL_AGGRAVATED * level - var/original_burn_heal = aggravated_heal - - owner.adjustToxLoss(-aggravated_heal * 0.5) - - // Heal different damage types - for(var/datum/injury/injury in owner.all_injuries) - if(!bashing_lethal_heal && !aggravated_heal) - break - if(!injury.can_heal()) - continue - if(injury.damage_type == WOUND_BURN) - aggravated_heal = injury.heal_damage(aggravated_heal) - else - bashing_lethal_heal = injury.heal_damage(bashing_lethal_heal) - - if(bashing_lethal_heal != original_brute_heal || aggravated_heal != original_burn_heal) - owner.update_all_limb_states() + + owner.adjustToxLoss(-aggravated_heal * 0.5, FALSE) + owner.adjustBruteLoss(-aggravated_heal, FALSE) + owner.adjustFireLoss(-bashing_lethal_heal, TRUE) + owner.adjust_blood_volume(vitae_cost, maximum = BLOOD_VOLUME_NORMAL) //this is quadratic so expect it to scale like crazy - owner.heal_wounds((bashing_lethal_heal + aggravated_heal) * level * 0.6, source=src) + owner.heal_wounds((bashing_lethal_heal + aggravated_heal) * level * 0.6, source = src) for(var/obj/item/organ/artery/artery in owner.getorganslotlist(ORGAN_SLOT_ARTERY)) artery.applyOrganDamage(-(bashing_lethal_heal + aggravated_heal) * level) diff --git a/code/modules/clothing/belt/misc.dm b/code/modules/clothing/belt/misc.dm index 7bfa12d2e95..483b8ff5f44 100644 --- a/code/modules/clothing/belt/misc.dm +++ b/code/modules/clothing/belt/misc.dm @@ -404,7 +404,6 @@ /obj/item/weapon/surgery/bonesetter, /obj/item/weapon/surgery/cautery, /obj/item/natural/worms/leech, - /obj/item/natural/worms/leech, /obj/item/weapon/surgery/hammer, /obj/item/natural/bundle/fibers/full, ) diff --git a/code/modules/crafting/alchemy/essence_machines/essence_gauntlet/spells/regeneration_cycle.dm b/code/modules/crafting/alchemy/essence_machines/essence_gauntlet/spells/regeneration_cycle.dm index d192517e5a1..70609d23300 100644 --- a/code/modules/crafting/alchemy/essence_machines/essence_gauntlet/spells/regeneration_cycle.dm +++ b/code/modules/crafting/alchemy/essence_machines/essence_gauntlet/spells/regeneration_cycle.dm @@ -31,8 +31,6 @@ var/mob/living/carbon/carbon = owner if(!iscarbon(owner)) return - for(var/datum/injury/injury as anything in carbon.all_injuries) - if(!injury.can_heal()) - continue - injury.heal_damage(0.1) - carbon.update_all_limb_states() + + carbon.adjustBruteLoss(0.1, FALSE) + carbon.adjustFireLoss(0.1, TRUE) diff --git a/code/modules/crafting/alchemy/herbal_recipes.dm b/code/modules/crafting/alchemy/herbal_recipes.dm index 568e9654434..f0337f4e8c9 100644 --- a/code/modules/crafting/alchemy/herbal_recipes.dm +++ b/code/modules/crafting/alchemy/herbal_recipes.dm @@ -79,14 +79,14 @@ for(var/datum/injury/injury in affected_bodypart.injuries) if(!injury.can_heal()) continue - injury.heal_damage(1) + injury.adjust_germ_level(-5) injury.salve_injury() - if(injury.damage_type == WOUND_BURN) + if(injury.damage_type & FIRE_WOUND_TYPES) injury.heal_damage(3) - injury.adjust_germ_level(-5) + if(QDELETED(injury)) + continue + injury.heal_damage(1) affected_bodypart.disinfect_limb(20 SECONDS) - if(affected_bodypart.post_damage_change()) - affected_mob.update_damage_overlays() // Weak Mana/Stamina Potions (based on hypericum/benedictus/mentha) /datum/reagent/medicine/herbal/hypericum_tonic @@ -399,16 +399,9 @@ taste_description = "bitter numbness" /datum/reagent/medicine/herbal/paris_poultice/on_bodypart_absorb(mob/living/carbon/affected_mob, obj/item/bodypart/affected_bodypart, amount_to_transfer) - for(var/datum/injury/injury in affected_bodypart.injuries) - if(!injury.can_heal()) - continue - if(injury.damage_type == WOUND_BURN) - injury.heal_damage(0.5) - if(injury.damage_type != WOUND_BURN) - injury.heal_damage(0.75) - affected_bodypart.add_pain(-amount_to_transfer * 0.3) - if(affected_bodypart.post_damage_change()) + if(affected_bodypart.heal_damage(0.5, 0.75, TRUE, required_status = BODYPART_ORGANIC)) affected_mob.update_damage_overlays() + affected_bodypart.add_pain(-amount_to_transfer * 0.3) /datum/reagent/medicine/herbal/paris_poultice/overdose_process(mob/living/M) M.adjustToxLoss(0.5) @@ -443,10 +436,10 @@ M.remove_chem_effect(CE_OXYGENATED, "[type]") /datum/reagent/medicine/herbal/herbalist_panacea/on_mob_life(mob/living/carbon/M, efficiency) - M.adjustBruteLoss(-1.5*REM*efficiency) - M.adjustFireLoss(-1.5*REM*efficiency) - M.adjustToxLoss(-1*REM*efficiency) - M.adjustOxyLoss(-1*efficiency) + M.adjustBruteLoss(-1.5*REM*efficiency, FALSE) + M.adjustFireLoss(-1.5*REM*efficiency, FALSE) + M.adjustToxLoss(-1*REM*efficiency, FALSE) + M.adjustOxyLoss(-1*efficiency, TRUE) M.adjust_stamina(2*REM*efficiency) . = ..() diff --git a/code/modules/crafting/alchemy/reagents.dm b/code/modules/crafting/alchemy/reagents.dm index fa98adfe3ab..9ea2ac6daee 100644 --- a/code/modules/crafting/alchemy/reagents.dm +++ b/code/modules/crafting/alchemy/reagents.dm @@ -11,10 +11,8 @@ liver_chemical = FALSE /datum/reagent/medicine/healthpot/on_bodypart_absorb(mob/living/carbon/affected_mob, obj/item/bodypart/affected_bodypart, amount_to_transfer) - for(var/datum/injury/injury in affected_bodypart.injuries) - if(!injury.can_heal()) - continue - injury.heal_damage(1) // update in on_mob_life + if(affected_bodypart.heal_damage(1 * REM, 1 * REM, TRUE, required_status = BODYPART_ORGANIC)) + affected_mob.update_damage_overlays() . = ..() /datum/reagent/medicine/healthpot/on_mob_metabolize(mob/living/L) @@ -30,21 +28,14 @@ /datum/reagent/medicine/healthpot/on_mob_life(mob/living/carbon/M, efficiency) if(volume >= 60) M.remove_reagent(/datum/reagent/medicine/healthpot, 2) //No overhealing. - M.adjust_blood_volume(BLOOD_REGEN_FACTOR * 200 * efficiency * 3, maximum = BLOOD_VOLUME_NORMAL) - var/list/wCount = M.get_wounds() - if(wCount.len > 0) - M.heal_wounds(3 * efficiency) //at a motabalism of .5 U a tick this translates to 120WHP healing with 20 U Most wounds are unsewn 15-100. This is powerful on single wounds but rapidly weakens at multi wounds. - for(var/datum/injury/injury in M.all_injuries) - if(!injury.can_heal()) - continue - injury.heal_damage(1/10 * efficiency) - M.update_all_limb_states() + M.adjust_blood_volume(6 * efficiency, maximum = BLOOD_VOLUME_NORMAL) + M.heal_wounds(3 * efficiency) //at a motabalism of .5 U a tick this translates to 120WHP healing with 20 U Most wounds are unsewn 15-100. This is powerful on single wounds but rapidly weakens at multi wounds. if(volume > 0.99) - M.adjustBruteLoss(-1.75*REM * efficiency, 0) - M.adjustFireLoss(-1.75*REM * efficiency, 0) - M.adjustOxyLoss(-1.25 * efficiency, 0) - M.adjustCloneLoss(-1.75*REM * efficiency, 0) - ..() + M.adjustOxyLoss(-1.25 * efficiency, FALSE) + M.adjustCloneLoss(-1.25 * REM * efficiency, FALSE) + M.adjustBruteLoss(-1.75*REM * efficiency, FALSE, required_status = BODYPART_ORGANIC) + M.adjustFireLoss(-1.75*REM * efficiency, TRUE, required_status = BODYPART_ORGANIC) + . = ..() /datum/reagent/medicine/stronghealth name = "Strong Health Potion" @@ -56,12 +47,8 @@ liver_chemical = FALSE /datum/reagent/medicine/healthpot/on_bodypart_absorb(mob/living/carbon/affected_mob, obj/item/bodypart/affected_bodypart, amount_to_transfer) - for(var/datum/injury/injury in affected_bodypart.injuries) - if(!injury.can_heal()) - continue - injury.heal_damage(2) - for(var/datum/wound/wound in affected_bodypart.wounds) - wound.heal_wound(2) + if(affected_bodypart.heal_damage(3 * REM, 3 * REM, TRUE, required_status = BODYPART_ORGANIC)) + affected_mob.update_damage_overlays() . = ..() /datum/reagent/medicine/stronghealth/on_mob_metabolize(mob/living/L) @@ -79,20 +66,14 @@ /datum/reagent/medicine/stronghealth/on_mob_life(mob/living/carbon/M, efficiency) if(volume >= 60) M.remove_reagent(/datum/reagent/medicine/stronghealth, 2) //No overhealing. - M.adjust_blood_volume(BLOOD_REGEN_FACTOR * 200 * efficiency * 5, maximum = BLOOD_VOLUME_NORMAL) + M.adjust_blood_volume(10 * efficiency, maximum = BLOOD_VOLUME_NORMAL) M.heal_wounds(6 * efficiency) //at a motabalism of .5 U a tick this translates to 240WHP healing with 20 U Most wounds are unsewn 15-100. - for(var/datum/injury/injury in M.all_injuries) - if(!injury.can_heal()) - continue - injury.heal_damage(2/10 * efficiency) - M.update_all_limb_states() if(volume > 0.99) - M.adjustBruteLoss(-7*REM * efficiency, 0) - M.adjustFireLoss(-7*REM * efficiency, 0) - M.adjustOxyLoss(-5 * efficiency, 0) - M.adjustCloneLoss(-7*REM * efficiency, 0) - ..() - . = 1 + M.adjustOxyLoss(-5 * efficiency, FALSE) + M.adjustCloneLoss(-5 * REM * efficiency, FALSE) + M.adjustBruteLoss(-7*REM * efficiency, FALSE, required_status = BODYPART_ORGANIC) + M.adjustFireLoss(-7*REM * efficiency, TRUE, required_status = BODYPART_ORGANIC) + . = ..() /datum/reagent/medicine/rosawater name = "Rosa Water" diff --git a/code/modules/fishing/leeches.dm b/code/modules/fishing/leeches.dm index c952c87c755..39ecbc82e9f 100644 --- a/code/modules/fishing/leeches.dm +++ b/code/modules/fishing/leeches.dm @@ -118,7 +118,7 @@ user.simple_remove_embedded_object(src) return TRUE else - var/modifier = bodypart.get_cut() ? 1.5 : 1 + var/modifier = bodypart.get_cut(TRUE, TRUE) ? 1.5 : 1 //ignore bandage because leech is embedded user.adjustToxLoss(-1 * toxin_healing * modifier) var/blood_extracted = min(blood_maximum - blood_storage, user.get_blood_volume(), blood_sucking) * modifier if(HAS_TRAIT(user, TRAIT_LEECHIMMUNE)) diff --git a/code/modules/mob/living/carbon/carbon_medical.dm b/code/modules/mob/living/carbon/carbon_medical.dm index 7db682608f5..600896e6cf1 100644 --- a/code/modules/mob/living/carbon/carbon_medical.dm +++ b/code/modules/mob/living/carbon/carbon_medical.dm @@ -98,11 +98,3 @@ return "[bpm]" else return "[bpm > 0 ? max(0, bpm + rand(-10, 10)) : 0]" - -/// USE THIS SPARINGLY, FOR EXAMPLE IF YOU'RE HEALING ALL INJURIES -/mob/living/carbon/proc/update_all_limb_states() - . = FALSE - for(var/obj/item/bodypart/bodypart as anything in bodyparts) - . |= bodypart.post_damage_change() - if(.) - update_damage_overlays() diff --git a/code/modules/mob/living/carbon/damage_procs.dm b/code/modules/mob/living/carbon/damage_procs.dm index d93d645420a..ac7b9aed2b0 100644 --- a/code/modules/mob/living/carbon/damage_procs.dm +++ b/code/modules/mob/living/carbon/damage_procs.dm @@ -99,13 +99,13 @@ return adjustBruteLoss(diff, updating_health, forced, required_bodytype, damage_type) -/mob/living/carbon/adjustBruteLoss(amount, updating_health = TRUE, forced = FALSE, required_status, damage_type = WOUND_INTERNAL_BRUISE, can_crit = FALSE, true_heal = FALSE) +/mob/living/carbon/adjustBruteLoss(amount, updating_health = TRUE, forced = FALSE, required_status, damage_type = WOUND_INTERNAL_BRUISE, can_crit = FALSE) if(!forced && (status_flags & GODMODE)) return FALSE if(amount > 0) take_overall_damage(amount, 0, updating_health, required_status, damage_type = damage_type, no_crit = can_crit) else - heal_overall_damage(abs(amount), 0, required_status ? required_status : BODYPART_ORGANIC, updating_health, true_heal) + heal_overall_damage(abs(amount), 0, required_status ? required_status : BODYPART_ORGANIC, updating_health, forced) return amount /mob/living/carbon/setFireLoss(amount, updating_health = TRUE, forced = FALSE, required_bodytype) @@ -374,7 +374,7 @@ update_damage_overlays() //Heal MANY bodyparts, in random order -/mob/living/carbon/heal_overall_damage(brute = 0, burn = 0, required_status, updating_health = TRUE, true_heal = FALSE) +/mob/living/carbon/heal_overall_damage(brute = 0, burn = 0, required_status, updating_health = TRUE, forced = FALSE) . = FALSE var/list/obj/item/bodypart/parts = get_damaged_bodyparts(brute, burn, required_status) @@ -386,7 +386,7 @@ var/burn_was = picked.burn_dam . += picked.get_damage() - update |= picked.heal_damage(brute, burn, required_status, FALSE, true_heal) + update |= picked.heal_damage(brute, burn, updating_health = FALSE, forced = forced, required_status = required_status) . -= picked.get_damage() // return the net amount of damage healed diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 14a0ec1615f..04f5f635331 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -652,7 +652,7 @@ var/list/mechanics_result = list() for(var/wound_type in all_untreated) switch(wound_type) - if(WOUND_SLASH, WOUND_PIERCE, WOUND_BITE) + if(WOUND_SLASH, WOUND_PUNCTURE, WOUND_BITE) mechanics_result += "Suture or bandage cuts, bites, or punctures to allow them to heal." if(WOUND_BLUNT, WOUND_LASH) mechanics_result += "Bandage bruises and lashes to allow them to heal." diff --git a/code/modules/mob/living/carbon/human/human_suicide.dm b/code/modules/mob/living/carbon/human/human_suicide.dm index 54c2aa431f1..a546a00ff3d 100644 --- a/code/modules/mob/living/carbon/human/human_suicide.dm +++ b/code/modules/mob/living/carbon/human/human_suicide.dm @@ -17,7 +17,7 @@ /mob/living/carbon/human/apply_suicide_damage(obj/item/suicide_tool, damage_type = NONE) // if we don't have any damage_type passed in, default to parent. - if(damage_type == NONE) + if(damage_type & NONE) return ..() if(damage_type & MANUAL_SUICIDE_NONLETHAL) diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index d3a8f8dcf91..41c667671b4 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -548,13 +548,13 @@ All effects don't start immediately, but rather get worse over time; the rate is //for context, it takes 5 small cuts (0.4 x 5) or 3 normal cuts (0.8 x 3) for a bodypart to not be able to heal itself if(affecting.get_bleed_rate() >= 2) continue - if(affecting.heal_damage(sleepy_mod * 1.5, sleepy_mod * 1.5, required_status = BODYPART_ORGANIC, updating_health = FALSE)) // multiplier due to removing healing from sleep effect - src.update_damage_overlays() + // if(affecting.heal_damage(sleepy_mod * 1.5, sleepy_mod * 1.5, required_status = BODYPART_ORGANIC, updating_health = FALSE)) // multiplier due to removing healing from sleep effect + // src.update_damage_overlays() for(var/datum/wound/wound as anything in affecting.wounds) if(!wound.sleep_healing) continue wound.heal_wound(wound.sleep_healing * sleepy_mod) - adjustToxLoss( - ( sleepy_mod * 0.15) ) + adjustToxLoss(-(sleepy_mod * 0.15)) updatehealth() if(eyesclosed && !HAS_TRAIT(src, TRAIT_NOSLEEP)) Sleeping(300) diff --git a/code/modules/mob/living/damage_procs.dm b/code/modules/mob/living/damage_procs.dm index 27ae6fe59e0..6a46c06a280 100644 --- a/code/modules/mob/living/damage_procs.dm +++ b/code/modules/mob/living/damage_procs.dm @@ -141,7 +141,7 @@ /mob/living/proc/getBruteLoss() return bruteloss -/mob/living/proc/adjustBruteLoss(amount, updating_health = TRUE, forced = FALSE, required_status, damage_type, can_crit = FALSE, true_heal = FALSE) +/mob/living/proc/adjustBruteLoss(amount, updating_health = TRUE, forced = FALSE, required_status, damage_type, can_crit = FALSE) if(!forced && (status_flags & GODMODE)) return FALSE bruteloss = CLAMP((bruteloss + (amount * CONFIG_GET(number/damage_multiplier))), 0, maxHealth * 2) @@ -304,9 +304,9 @@ updatehealth(brute + burn) // heal MANY bodyparts, in random order -/mob/living/proc/heal_overall_damage(brute = 0, burn = 0, required_status, updating_health = TRUE) - adjustBruteLoss(-brute, FALSE) //zero as argument for no instant health update - adjustFireLoss(-burn, FALSE) +/mob/living/proc/heal_overall_damage(brute = 0, burn = 0, required_status, updating_health = TRUE, forced = FALSE) + adjustBruteLoss(-brute, FALSE, forced = forced) //zero as argument for no instant health update + adjustFireLoss(-burn, FALSE, forced = forced) if(updating_health) updatehealth(brute + burn) diff --git a/code/modules/mob/living/inhand_holder.dm b/code/modules/mob/living/inhand_holder.dm index 573a48420d2..cf8e1a79fc2 100644 --- a/code/modules/mob/living/inhand_holder.dm +++ b/code/modules/mob/living/inhand_holder.dm @@ -76,7 +76,7 @@ qdel(src) return FALSE if(organ_stored) - if(!organ_stored.get_cut()) + if(!organ_stored.get_cut(ignore_gauze = TRUE)) if(!do_after(held_mob, 15 SECONDS, loc)) return organ_stored.owner.emote("scream") diff --git a/code/modules/mob/living/simple_animal/damage_procs.dm b/code/modules/mob/living/simple_animal/damage_procs.dm index 01e573cfdd8..e80e878ee9e 100644 --- a/code/modules/mob/living/simple_animal/damage_procs.dm +++ b/code/modules/mob/living/simple_animal/damage_procs.dm @@ -7,7 +7,7 @@ updatehealth(amount) return amount -/mob/living/simple_animal/adjustBruteLoss(amount, updating_health = TRUE, forced = FALSE, damage_type, true_heal = FALSE) +/mob/living/simple_animal/adjustBruteLoss(amount, updating_health = TRUE, forced = FALSE, damage_type) if(forced) . = adjustHealth(amount * CONFIG_GET(number/damage_multiplier), updating_health, forced) else if(damage_coeff[BRUTE]) diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index c1643ebf43a..0d88a9e2752 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -89,9 +89,7 @@ M.adjustBruteLoss(-1 * REM * efficiency, 0) M.adjustFireLoss(-1 * REM * efficiency, 0) M.adjustToxLoss(-0.5 * efficiency, 0) - var/list/wCount = M.get_wounds() - if(wCount.len > 0) - M.heal_wounds(1 * efficiency) + M.heal_wounds(1 * efficiency) . = ..() /datum/reagent/medicine/soulweave_distillate @@ -141,10 +139,7 @@ L.remove_chem_effect(CE_PAINKILLER, "[type]") /datum/reagent/medicine/coldvein_compress/on_bodypart_absorb(mob/living/carbon/affected_mob, obj/item/bodypart/affected_bodypart, amount_to_transfer) - for(var/datum/injury/injury in affected_bodypart.injuries) - if(injury.damage_type == WOUND_BURN) - injury.heal_damage(2) - if(affected_bodypart.post_damage_change()) + if(affected_bodypart.heal_damage(0, 2 * REM, required_status = BODYPART_ORGANIC)) affected_mob.update_damage_overlays() . = ..() @@ -165,19 +160,11 @@ /datum/reagent/medicine/ichor_of_mending/on_mob_life(mob/living/carbon/M, efficiency) if(volume > 0.99) M.adjustBruteLoss(-3.5 * REM * efficiency, 0) - var/list/wCount = M.get_wounds() - if(wCount.len > 0) - M.heal_wounds(4 * efficiency) + M.heal_wounds(4 * efficiency) . = ..() /datum/reagent/medicine/ichor_of_mending/on_bodypart_absorb(mob/living/carbon/affected_mob, obj/item/bodypart/affected_bodypart, amount_to_transfer) - for(var/datum/injury/injury in affected_bodypart.injuries) - if(!injury.can_heal()) - continue - if(injury.damage_type == WOUND_BURN) - continue - injury.heal_damage(2) - if(affected_bodypart.post_damage_change()) + if(affected_bodypart.heal_damage(3.5 * REM, 0, required_status = BODYPART_ORGANIC)) affected_mob.update_damage_overlays() . = ..() @@ -197,13 +184,13 @@ /datum/reagent/medicine/ashbinders_salve/on_bodypart_absorb(mob/living/carbon/affected_mob, obj/item/bodypart/affected_bodypart, amount_to_transfer) for(var/datum/injury/injury in affected_bodypart.injuries) - if(injury.damage_type != WOUND_BURN) + if(!injury.can_heal()) + continue + if(!(injury.damage_type & FIRE_WOUND_TYPES)) continue - injury.heal_damage(3) injury.adjust_germ_level(-10) + injury.heal_damage(3) affected_bodypart.disinfect_limb(30 SECONDS) - if(affected_bodypart.post_damage_change()) - affected_mob.update_damage_overlays() . = ..() /datum/reagent/medicine/vitalroot_draught @@ -289,11 +276,9 @@ for(var/datum/injury/injury in affected_bodypart.injuries) if(!injury.can_heal()) continue - injury.heal_damage(2) injury.salve_injury() + injury.heal_damage(2) affected_bodypart.adjust_germ_level(-10) - if(affected_bodypart.post_damage_change()) - affected_mob.update_damage_overlays() . = ..() /datum/reagent/medicine/woundwrack_oil/on_mob_life(mob/living/carbon/M, efficiency) @@ -355,9 +340,7 @@ if(volume > 0.99) M.adjustBruteLoss(-2.5 * REM * efficiency, 0) M.adjustFireLoss(-2.5 * REM * efficiency, 0) - var/list/wCount = M.get_wounds() - if(wCount.len > 0) - M.heal_wounds(5 * efficiency) + M.heal_wounds(5 * efficiency) . = ..() /datum/reagent/medicine/marrowbrew @@ -424,11 +407,9 @@ for(var/datum/injury/injury in affected_bodypart.injuries) if(!injury.can_heal()) continue - injury.heal_damage(1.5) injury.salve_injury() + injury.heal_damage(1.5) affected_bodypart.adjust_germ_level(-15) - if(affected_bodypart.post_damage_change()) - affected_mob.update_damage_overlays() . = ..() /datum/reagent/medicine/witchknit_paste/on_mob_life(mob/living/carbon/M, efficiency) @@ -474,9 +455,7 @@ if(volume > 0.99) M.adjustBruteLoss(-2 * REM * efficiency, 0) M.adjustCloneLoss(-1.5 * REM * efficiency, 0) - var/list/wCount = M.get_wounds() - if(wCount.len > 0) - M.heal_wounds(2 * efficiency) + M.heal_wounds(2 * efficiency) . = ..() /datum/reagent/medicine/sunpetal_decoction diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index 02ad27bf676..b2c129a07dc 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -458,7 +458,7 @@ var/organ_damaged_required = organ_damage_requirement switch(wounding_type) // Penetrating and blunt injuries are more likely to damage internal organs - if(WOUND_PIERCE, WOUND_BLUNT) + if(WOUND_PUNCTURE, WOUND_BLUNT) organ_damage_minimum *= 0.75 // Burn damage is unlikely to damage organs if(WOUND_BURN) @@ -530,8 +530,10 @@ var/datum/injury/new_injury = new new_injury_type() // Check whether we can add the wound to an existing wound if(surgical) - new_injury.autoheal_cutoff = 0 new_injury.injury_flags |= INJURY_SURGICAL + new_injury.stages = list("surgical incision" = 0) + new_injury.desc_list = list("surgical incision") + new_injury.damage_list = list(0) else for(var/datum/injury/other in injuries) if(other.can_merge(new_injury)) @@ -542,6 +544,9 @@ last_injury = new_injury . = new_injury + if(.) + post_damage_change() + /// Deal with injury healing and other updates /obj/item/bodypart/proc/update_injuries(delta_time, times_fired) var/toxins = 0 @@ -558,21 +563,19 @@ qdel(injury) continue - // Slow healing - var/heal_amt = 0 + // Bleeding + if(owner) + injury.bleed_timer = max(0, injury.bleed_timer - delta_time) + // Slow healing + var/heal_amt = injury.base_autoheal_amount if(!toxins && injury.can_autoheal()) - heal_amt += (GET_MOB_ATTRIBUTE_VALUE(owner, STAT_ENDURANCE) * 0.005) + heal_amt += (GET_MOB_ATTRIBUTE_VALUE(owner, STAT_ENDURANCE) * 0.004) if(owner?.IsSleeping()) heal_amt *= 2 - if(heal_amt) injury.heal_damage(heal_amt * delta_time) - // Bleeding - if(owner) - injury.bleed_timer = max(0, injury.bleed_timer - delta_time) - if(post_damage_change()) owner.update_damage_overlays() @@ -585,7 +588,7 @@ if(injury.damage <= 0) continue - if(injury.damage_type == WOUND_BURN) + if(injury.damage_type & FIRE_WOUND_TYPES) burn_dam += injury.damage else brute_dam += injury.damage @@ -928,7 +931,7 @@ //Heals brute and burn damage for the organ. Returns 1 if the damage-icon states changed at all. //Damage cannot go below zero. //Cannot remove negative damage (i.e. apply damage) -/obj/item/bodypart/proc/heal_damage(brute, burn, required_status, updating_health = TRUE, true_heal = FALSE) +/obj/item/bodypart/proc/heal_damage(brute, burn, updating_health = TRUE, forced = FALSE, required_status) update_HP() if(required_status && (status != required_status)) //So we can only heal certain kinds of limbs, ie robotic vs organic. return @@ -936,9 +939,9 @@ for(var/datum/injury/injury as anything in injuries) if((brute <= 0) && (burn <= 0)) break - if(!true_heal && !injury.can_heal()) + if(!forced && !injury.can_heal()) continue - if(injury.damage_type == WOUND_BURN) + if(injury.damage_type & FIRE_WOUND_TYPES) burn = injury.heal_damage(burn) else brute = injury.heal_damage(brute) @@ -949,16 +952,18 @@ /obj/item/bodypart/proc/post_damage_change(updating_health = TRUE, updating_shock = FALSE) . = FALSE update_damages() + if(owner) update_limb_efficiency() if(can_be_disabled) update_disabled() if(updating_health) owner.updatehealth() - if(updating_shock && get_shock(FALSE) >= DAMAGE_PRECISION) - owner.update_shock() - . = TRUE + if(updating_shock && get_shock(FALSE) >= DAMAGE_PRECISION) + owner.update_shock() + . = TRUE consider_processing() + return update_bodypart_damage_state() || . ///Proc to hook behavior associated to the change of the brute_dam variable's value. @@ -1126,8 +1131,10 @@ /// Updates an organ's brute/burn states for use by update_damage_overlays() /// Returns 1 if we need to update overlays. 0 otherwise. /obj/item/bodypart/proc/update_bodypart_damage_state() - var/tbrute = round( (brute_dam/max_damage) * 3, 1 ) - var/tburn = round( (burn_dam/max_damage) * 3, 1 ) + SHOULD_CALL_PARENT(TRUE) + + var/tbrute = round((brute_dam/max_damage) * 3, 1) + var/tburn = round((burn_dam/max_damage) * 3, 1) if((tbrute != brutestate) || (tburn != burnstate)) brutestate = tbrute burnstate = tburn @@ -1513,68 +1520,61 @@ if(artery.is_broken()) return TRUE -/obj/item/bodypart/proc/get_incision(strict = FALSE, ignore_gauze = FALSE) - if(ignore_gauze && (bandage)) +/obj/item/bodypart/proc/get_incision(surgical_only = FALSE, ignore_gauze = FALSE) + if(!ignore_gauze && bandage) return - var/datum/wound/incision + for(var/datum/wound/slash/slash in wounds) if(slash.is_sewn()) continue - incision = slash - break + return slash - if(!incision) - var/datum/injury/internal_incision - for(var/datum/injury/slash/slash in injuries) - if(slash.is_bandaged() || slash.current_stage > slash.max_bleeding_stage) // Shit's unusable - continue - if(strict && !slash.is_surgical()) //We don't need dirty ones - continue - if(!internal_incision) - internal_incision = slash - continue - if(slash.is_surgical() && internal_incision.is_surgical()) //If they're both dirty or both are surgical, just get bigger one - if(slash.damage > internal_incision.damage) - internal_incision = slash - break - else if(slash.is_surgical()) //otherwise surgical one takes priority + var/datum/injury/internal_incision + for(var/datum/injury/slash/slash in injuries) + if(surgical_only && !slash.is_surgical()) //We don't need dirty ones + continue + if(slash.is_bandaged() || slash.current_stage > slash.max_bleeding_stage) // Shit's unusable + continue + if(!internal_incision) + internal_incision = slash + continue + if(slash.is_surgical() && internal_incision.is_surgical()) //If they're both dirty or both are surgical, just get bigger one + if(slash.damage > internal_incision.damage) internal_incision = slash break - return internal_incision - return incision + else if(slash.is_surgical()) //otherwise surgical one takes priority + internal_incision = slash + break + return internal_incision -/obj/item/bodypart/proc/get_cut(strict = FALSE, ignore_gauze = FALSE) - if(ignore_gauze && (bandage)) +/obj/item/bodypart/proc/get_cut(surgical_only = FALSE, ignore_gauze = FALSE) + if(!ignore_gauze && bandage) return - var/datum/wound/incision + for(var/datum/wound/slash/slash in wounds) if(slash.is_sewn()) continue - incision = slash - break + return slash - if(!incision) - var/datum/injury/internal_incision - for(var/datum/injury/slash in injuries) - if(!(slash.damage_type in list(WOUND_SLASH, WOUND_BITE, WOUND_PIERCE))) - continue - if(slash.is_bandaged() || slash.current_stage > slash.max_bleeding_stage) // Shit's unusable - continue - if(strict && !slash.is_surgical()) //We don't need dirty ones - continue - if(!internal_incision) - internal_incision = slash - continue - if(slash.is_surgical() && internal_incision.is_surgical()) //If they're both dirty or both are surgical, just get bigger one - if(slash.damage > internal_incision.damage) - internal_incision = slash - break - else if(slash.is_surgical()) //otherwise surgical one takes priority + var/datum/injury/internal_incision + for(var/datum/injury/slash in injuries) + if(!(slash.damage_type & CUT_WOUND_TYPES)) + continue + if(slash.is_sutured() || slash.current_stage > slash.max_bleeding_stage) // Shit's unusable + continue + if(surgical_only && !slash.is_surgical()) //We don't need dirty ones + continue + if(!internal_incision) + internal_incision = slash + continue + if(slash.is_surgical() && internal_incision.is_surgical()) //If they're both dirty or both are surgical, just get bigger one + if(slash.damage > internal_incision.damage) internal_incision = slash break - return internal_incision - return incision - + else if(slash.is_surgical()) //otherwise surgical one takes priority + internal_incision = slash + break + return internal_incision /obj/item/bodypart/proc/is_bandaged() . = TRUE @@ -1594,13 +1594,6 @@ if(!injury.is_disinfected()) return FALSE - -/obj/item/bodypart/proc/is_clamped() - . = TRUE - for(var/datum/injury/injury in injuries) - if(!injury.is_clamped()) - return FALSE - /obj/item/bodypart/proc/clamp_limb() for(var/datum/injury/injury as anything in injuries) injury.clamp_injury() diff --git a/code/modules/surgery/bodyparts/bodypart_examine.dm b/code/modules/surgery/bodyparts/bodypart_examine.dm index 029c7aa8e3a..e87f4dbb100 100644 --- a/code/modules/surgery/bodyparts/bodypart_examine.dm +++ b/code/modules/surgery/bodyparts/bodypart_examine.dm @@ -22,7 +22,7 @@ for(var/wound_type in untreated_types) switch(wound_type) - if(WOUND_SLASH, WOUND_PIERCE, WOUND_BITE) + if(WOUND_SLASH, WOUND_PUNCTURE, WOUND_BITE) . += "Suture or bandage cuts, bites, or punctures to allow them to heal." if(WOUND_BLUNT, WOUND_LASH) . += "Bandage bruises and lashes to allow them to heal." @@ -182,8 +182,8 @@ /obj/item/bodypart/proc/get_injury_status(mob/user, advanced = FALSE) var/list/status = list() - var/brute = brute_dam - var/burn = burn_dam + var/brute = round(brute_dam, DAMAGE_PRECISION) + var/burn = round(burn_dam, DAMAGE_PRECISION) if(advanced) if(brute) @@ -191,7 +191,7 @@ if(burn) status += "[burn] BURN" else - if(brute >= DAMAGE_PRECISION) + if(brute) switch(brute/max_damage) if(0.75 to INFINITY) status += "[heavy_brute_msg]" @@ -201,8 +201,7 @@ status += "[medium_brute_msg]" else status += "[light_brute_msg]" - - if(burn >= DAMAGE_PRECISION) + if(burn) switch(burn/max_damage) if(0.75 to INFINITY) status += "[heavy_burn_msg]" @@ -234,7 +233,7 @@ for(var/obj/item/organ/possible_artery in shuffle(getorganslotlist(ORGAN_SLOT_ARTERY))) if(possible_artery.is_bruised()) - if(get_cut()) + if(get_cut(ignore_gauze = TRUE)) status += uppertext(span_bloody("cut [parse_zone(possible_artery.zone)]")) else status += uppertext(span_bloody("bruised [parse_zone(possible_artery.zone)]")) @@ -278,7 +277,6 @@ return status - /obj/item/bodypart/proc/get_injuries_desc() var/list/flavor_text = list() var/list/injury_descriptors = list() diff --git a/code/modules/surgery/bodyparts/bodypart_wounds.dm b/code/modules/surgery/bodyparts/bodypart_wounds.dm index fe02a929f7a..76c5e8811d9 100644 --- a/code/modules/surgery/bodyparts/bodypart_wounds.dm +++ b/code/modules/surgery/bodyparts/bodypart_wounds.dm @@ -151,7 +151,7 @@ return 0.7 if(WOUND_BITE) return 1.1 - if(WOUND_PIERCE) + if(WOUND_PUNCTURE) return 0.8 else return 1 @@ -184,11 +184,11 @@ if(BCLASS_BLUNT, BCLASS_SMASH, BCLASS_PUNCH) wounding_type = WOUND_BLUNT if(BCLASS_DRILL, BCLASS_PICK, BCLASS_PIERCE, BCLASS_SHOT) - wounding_type = WOUND_PIERCE + wounding_type = WOUND_PUNCTURE if(BCLASS_CUT, BCLASS_CHOP) wounding_type = WOUND_SLASH if(BCLASS_STAB) - wounding_type = WOUND_PIERCE + wounding_type = WOUND_PUNCTURE if(BCLASS_TWIST) wounding_type = WOUND_BLUNT if(BCLASS_BITE) @@ -200,10 +200,10 @@ dam *= skeletonized_mod(wounding_type) - if(wounding_type == WOUND_NONE) + if(wounding_type & WOUND_NONE) return - if((zone_precise in list(BODY_ZONE_PRECISE_L_EYE, BODY_ZONE_PRECISE_R_EYE)) && wounding_type == WOUND_PIERCE) + if((zone_precise in list(BODY_ZONE_PRECISE_L_EYE, BODY_ZONE_PRECISE_R_EYE)) && (wounding_type & WOUND_PUNCTURE)) organ_bonus = CANT_ORGAN if(organ_bonus != CANT_ORGAN) @@ -219,8 +219,6 @@ if(incoming_germ && injury) injury.adjust_germ_level(incoming_germ * 0.1) - update_damages() - /* for(var/datum/wound/iter_wound as anything in wounds) iter_wound.receive_damage(wounding_type, dam, 0) @@ -462,11 +460,9 @@ if(can_bloody_wound()) returned_flags |= SURGERY_BLOODY - for(var/datum/injury/slash/slash in injuries) - if(slash.is_bandaged() || slash.current_stage > slash.max_bleeding_stage) // Shit's unusable - continue + if(get_incision()) returned_flags |= SURGERY_INCISED - break + var/static/list/retracting_behaviors = list( TOOL_RETRACTOR, TOOL_CROWBAR, diff --git a/code/modules/surgery/surgeries/bestow_lux.dm b/code/modules/surgery/surgeries/bestow_lux.dm index 7243341b042..9da3783b98e 100644 --- a/code/modules/surgery/surgeries/bestow_lux.dm +++ b/code/modules/surgery/surgeries/bestow_lux.dm @@ -29,7 +29,8 @@ /datum/surgery_step/bestow_lux/validate_target(mob/user, mob/living/target, target_zone, datum/intent/intent) . = ..() - + if(!.) + return if(target.stat == DEAD) return FALSE diff --git a/code/modules/surgery/surgeries/cure_rot.dm b/code/modules/surgery/surgeries/cure_rot.dm index e75f7285b8c..8c43b25afc3 100644 --- a/code/modules/surgery/surgeries/cure_rot.dm +++ b/code/modules/surgery/surgeries/cure_rot.dm @@ -19,7 +19,7 @@ target_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/monkey) minimum_time = 7 SECONDS maximum_time = 9 SECONDS - surgery_flags = SURGERY_INCISED + surgery_flags = SURGERY_INCISED | SURGERY_CLAMPED | SURGERY_RETRACTED skill_min = SKILL_LEVEL_APPRENTICE skill_median = SKILL_LEVEL_JOURNEYMAN preop_sound = 'sound/surgery/cautery1.ogg' diff --git a/code/modules/surgery/surgeries/extract_lux.dm b/code/modules/surgery/surgeries/extract_lux.dm index fb11f7b8ef2..7ed158bf9ce 100644 --- a/code/modules/surgery/surgeries/extract_lux.dm +++ b/code/modules/surgery/surgeries/extract_lux.dm @@ -30,6 +30,8 @@ /datum/surgery_step/extract_lux/validate_target(mob/user, mob/living/target, target_zone, datum/intent/intent) . = ..() + if(!.) + return if(target.stat == DEAD) to_chat(user, "They're dead!") return FALSE diff --git a/code/modules/surgery/surgeries/healing.dm b/code/modules/surgery/surgeries/healing.dm index f4b3b352d91..50eaf223746 100644 --- a/code/modules/surgery/surgeries/healing.dm +++ b/code/modules/surgery/surgeries/healing.dm @@ -50,12 +50,17 @@ for(var/datum/injury/injury as anything in target.all_injuries) if(brute && burn) break - if(injury.damage_type in list(WOUND_BLUNT, WOUND_INTERNAL_BRUISE, WOUND_LASH)) - brute = TRUE + if(injury.required_status != BODYPART_ORGANIC) continue - if(injury.damage_type == WOUND_BURN) - burn = TRUE + if(!injury.can_heal()) + continue + if(injury.is_surgical()) continue + if(injury.damage_type & FIRE_WOUND_TYPES) + burn = TRUE + else if(injury.damage_type & BRUTE_WOUND_TYPES) + brute = TRUE + if(!((brutehealing && brute) || (burnhealing && burn))) return FALSE @@ -92,11 +97,19 @@ tmsg += " as best as they can while [target] has clothing on" if(iscarbon(target)) for(var/datum/injury/injury as anything in target.all_injuries) - if(urhealedamt_burn && injury.damage_type == WOUND_BURN && injury.required_status == BODYPART_ORGANIC) + if(!urhealedamt_burn && !urhealedamt_brute) + break + if(injury.required_status != BODYPART_ORGANIC) + continue + if(!injury.can_heal()) + continue + if(injury.is_surgical()) + continue + + if(injury.damage_type & FIRE_WOUND_TYPES) urhealedamt_burn = injury.heal_damage(urhealedamt_burn) - if(urhealedamt_brute && (injury.damage_type in list(WOUND_BLUNT, WOUND_LASH, WOUND_INTERNAL_BRUISE)) && injury.required_status == BODYPART_ORGANIC) + else if(injury.damage_type & BRUTE_WOUND_TYPES) urhealedamt_brute = injury.heal_damage(urhealedamt_brute) - target.update_all_limb_states() else target.heal_bodypart_damage(urhealedamt_brute, urhealedamt_burn, required_status = BODYPART_ORGANIC) SEND_SIGNAL(user, COMSIG_LIVING_HEALED_OTHER, urhealedamt_brute + urhealedamt_burn) diff --git a/code/modules/surgery/surgeries/organic_steps.dm b/code/modules/surgery/surgeries/organic_steps.dm index b76817829f1..f467564c1ee 100644 --- a/code/modules/surgery/surgeries/organic_steps.dm +++ b/code/modules/surgery/surgeries/organic_steps.dm @@ -28,11 +28,7 @@ "Blood pools around the incision in [target]'s [parse_zone(target_zone)].") var/obj/item/bodypart/gotten_part = target.get_bodypart(check_zone(target_zone)) if(gotten_part) - var/datum/injury/ouchie = gotten_part.create_injury(WOUND_SLASH, 49, TRUE) - gotten_part.update_damages() - if(!ouchie) - return - ouchie.injury_flags |= INJURY_SURGICAL + gotten_part.create_injury(WOUND_SLASH, BLEED_DAMAGE_RATIO, surgical = TRUE) return TRUE /// Clamping diff --git a/code/modules/surgery/surgeries/revival.dm b/code/modules/surgery/surgeries/revival.dm index d60e0b2ce9c..a6284b2abc3 100644 --- a/code/modules/surgery/surgeries/revival.dm +++ b/code/modules/surgery/surgeries/revival.dm @@ -31,6 +31,8 @@ /datum/surgery_step/infuse_lux/validate_target(mob/user, mob/living/target, target_zone, datum/intent/intent) . = ..() + if(!.) + return if(target.stat < DEAD) to_chat(user, span_notice("They're not dead!")) return FALSE diff --git a/code/modules/surgery/surgery_tools_rogue.dm b/code/modules/surgery/surgery_tools_rogue.dm index c8dba6b85ac..7ae16d83188 100644 --- a/code/modules/surgery/surgery_tools_rogue.dm +++ b/code/modules/surgery/surgery_tools_rogue.dm @@ -190,11 +190,11 @@ return ..() if(ishuman(A)) if(A == user) - user.visible_message("[user] begins smacking themself with a small hammer.") + user.visible_message(span_info("[user] begins smacking themself with a small hammer.")) else - user.visible_message("[user] begins to smack [A] with a small hammer.") - if(do_after(user, 2.5 SECONDS, A)) - A.visible_message("[A] jerks their knee after the hammer strikes!") + user.visible_message(span_info("[user] begins to smack [A] with a small hammer.")) + if(do_after(user, 1 SECONDS, A)) + A.visible_message(span_info("[A] jerks their knee after the hammer strikes!")) if(prob(1)) playsound(user, 'sound/misc/bonk.ogg', 100, FALSE, -1) var/mob/living/carbon/human/human_target = A From e6e20892c43d9b488683e38c1943949db8d8cb3a Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Wed, 13 May 2026 18:53:12 -0700 Subject: [PATCH 31/59] embed --- code/modules/mob/living/carbon/carbon_shock.dm | 2 +- code/modules/surgery/bodyparts/_bodyparts.dm | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/code/modules/mob/living/carbon/carbon_shock.dm b/code/modules/mob/living/carbon/carbon_shock.dm index 3467cef8660..03abe502d41 100644 --- a/code/modules/mob/living/carbon/carbon_shock.dm +++ b/code/modules/mob/living/carbon/carbon_shock.dm @@ -66,7 +66,7 @@ maxbpshock = bpshock if(damaged_bodypart && (get_chem_effect(CE_PAINKILLER) < maxbpshock)) - var/burning = (damaged_bodypart.burn_dam >= damaged_bodypart.brute_dam) + var/burning = (damaged_bodypart.burn_dam > damaged_bodypart.brute_dam) var/message switch(CEILING(maxbpshock, 1)) if(1 to 10) diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index b2c129a07dc..1b8d5f2096d 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -860,10 +860,9 @@ for(var/thing in get_organs()) organ = thing constant_pain += organ.get_shock(FALSE) - var/obj/item/item - for(var/thing in embedded_objects) - item = thing - constant_pain += 3 * item.w_class + for(var/obj/item/embebbed as anything in embedded_objects) + if(embebbed.embedding) + constant_pain += embebbed.embedding.embedded_pain_multiplier * embebbed.w_class if(painkiller_included) constant_pain -= owner.get_chem_effect(CE_PAINKILLER)/PAINKILLER_DIVISOR return clamp(FLOOR((pain_dam + constant_pain) * multiplier, DAMAGE_PRECISION), 0, max_pain_damage) From fa84f6ded7212922bcc23f74962bbd37dc84e4e2 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Wed, 13 May 2026 21:11:07 -0700 Subject: [PATCH 32/59] artery stuff --- code/__DEFINES/medical.dm | 3 +-- code/datums/injury/organic/burn.dm | 3 --- code/datums/wounds/arteries.dm | 7 +++---- code/game/objects/items/needle.dm | 7 +++---- code/modules/surgery/bodyparts/_bodyparts.dm | 1 + code/modules/surgery/bodyparts/bodypart_examine.dm | 14 +++++++------- .../surgery/organs/internal/artery/_artery.dm | 3 --- code/modules/surgery/surgeries/organic_steps.dm | 12 ++++++++++-- 8 files changed, 25 insertions(+), 25 deletions(-) diff --git a/code/__DEFINES/medical.dm b/code/__DEFINES/medical.dm index ab591df7c96..a17610ef184 100644 --- a/code/__DEFINES/medical.dm +++ b/code/__DEFINES/medical.dm @@ -303,8 +303,7 @@ DEFINE_BITFIELD(organ_flags, list( // ~arteries -#define ARTERY_MAX_HEALTH 100 -#define ARTERIAL_BLOOD_FLOW 10 +#define ARTERIAL_BLOOD_FLOW 20 #define ARTERY_HEAD /obj/item/organ/artery/head #define ARTERY_MOUTH /obj/item/organ/artery/mouth diff --git a/code/datums/injury/organic/burn.dm b/code/datums/injury/organic/burn.dm index d3863235780..ca8471206e2 100644 --- a/code/datums/injury/organic/burn.dm +++ b/code/datums/injury/organic/burn.dm @@ -27,9 +27,6 @@ return FALSE -/datum/injury/burn/is_bleeding() - return FALSE //burns cannot bleed - /* /datum/injury/burn/apply_injury(our_damage, obj/item/bodypart/limb) . = ..() diff --git a/code/datums/wounds/arteries.dm b/code/datums/wounds/arteries.dm index aa6de16281b..5fb894379bd 100644 --- a/code/datums/wounds/arteries.dm +++ b/code/datums/wounds/arteries.dm @@ -26,9 +26,6 @@ return TRUE /datum/wound/artery/apply_to_bodypart(obj/item/bodypart/affected, silent, crit_message) - . = ..() - if(!.) - return var/obj/item/organ/artery/artery for(var/obj/item/organ/possible_artery in shuffle(affected.getorganslotlist(ORGAN_SLOT_ARTERY))) if(!possible_artery) @@ -40,13 +37,15 @@ artery = possible_artery break if(!artery) - return + qdel(src) + return FALSE var/dissection = (severity >= WOUND_SEVERITY_CRITICAL) || (artery?.damage >= (artery?.maxHealth * 0.5)) if(artery) if(dissection) artery.dissect() else artery.tear() + . = ..() qdel(src) /datum/wound/artery/neck_slice diff --git a/code/game/objects/items/needle.dm b/code/game/objects/items/needle.dm index 5a5b9727cf7..5c7a12423bd 100644 --- a/code/game/objects/items/needle.dm +++ b/code/game/objects/items/needle.dm @@ -215,7 +215,7 @@ if(!do_after(doctor, time, patient)) to_chat(doctor, span_warning("I must stand still!")) return FALSE - if(stringamt < 1) + if(!use(1)) to_chat(doctor, span_warning("The needle has no thread left!")) return FALSE var/amt2raise = GET_MOB_ATTRIBUTE_VALUE(doctor, STAT_INTELLIGENCE) * doctor.get_learning_boon(/datum/attribute/skill/misc/medicine) @@ -227,10 +227,9 @@ doctor.visible_message( span_green("[doctor] sutures [patient]'s [affecting.name] arteries with \the [src]."), span_green("I suture [patient]'s [affecting.name] arteries with \the [src].")) - use(1) for(var/obj/item/organ/artery in affecting.getorganslotlist(ORGAN_SLOT_ARTERY)) if(artery.damage) - artery.applyOrganDamage(-min(artery.maxHealth/2, 50)) + artery.applyOrganDamage(-artery.maxHealth/3) return TRUE // Then try to sew wounds (crits) @@ -257,7 +256,7 @@ to_chat(user, span_warning("I must stand still!")) return if(!use(1)) - to_chat(user, span_warning("All used up...")) + to_chat(doctor, span_warning("The needle has no thread left!")) return var/amt2raise = GET_MOB_ATTRIBUTE_VALUE(doctor, STAT_INTELLIGENCE) * doctor.get_learning_boon(/datum/attribute/skill/misc/medicine) user.adjust_experience(/datum/attribute/skill/misc/medicine, amt2raise) diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index 1b8d5f2096d..6f6cb1ae817 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -870,6 +870,7 @@ //Applies brute and burn damage to the organ. Returns 1 if the damage-icon states changed at all. //Damage will not exceed max_damage using this proc //Cannot apply negative damage +/// DEPRECIATED PROC: Replace with bodypart_attacked_by /obj/item/bodypart/proc/receive_damage(brute = 0, burn = 0, blocked = 0, updating_health = TRUE, required_status = null, flashes = TRUE) update_HP() var/hit_percent = (100-blocked)/100 diff --git a/code/modules/surgery/bodyparts/bodypart_examine.dm b/code/modules/surgery/bodyparts/bodypart_examine.dm index e87f4dbb100..32b64cb3bde 100644 --- a/code/modules/surgery/bodyparts/bodypart_examine.dm +++ b/code/modules/surgery/bodyparts/bodypart_examine.dm @@ -214,10 +214,10 @@ var/bleed_rate = get_bleed_rate() if(bleed_rate) - if(bleed_rate > 1) //Totally arbitrary value - status += "BLEEDING" + if(bleed_rate > BLEED_RATE_NOTICABLE) //Totally arbitrary value + status += span_bloody("BLEEDING") else - status += "BLEEDING" + status += span_bloody("BLEEDING") var/list/wound_strings = list() for(var/datum/wound/wound as anything in wounds) @@ -234,9 +234,9 @@ for(var/obj/item/organ/possible_artery in shuffle(getorganslotlist(ORGAN_SLOT_ARTERY))) if(possible_artery.is_bruised()) if(get_cut(ignore_gauze = TRUE)) - status += uppertext(span_bloody("cut [parse_zone(possible_artery.zone)]")) + status += span_artery(uppertext("cut [parse_zone(possible_artery.zone)]")) else - status += uppertext(span_bloody("bruised [parse_zone(possible_artery.zone)]")) + status += span_bloody(uppertext("bruised [parse_zone(possible_artery.zone)]")) if(skeletonized) status += "SKELETON" @@ -289,9 +289,9 @@ this_injury_desc = "[this_injury_desc]" if(injury.is_bleeding()) if(is_artery_torn()) - this_injury_desc = "blood-gushing [this_injury_desc]" + this_injury_desc = span_artery("blood-gushing [this_injury_desc]") //Completely arbitrary value - else if(injury.get_bleed_rate() > 1) + else if(injury.get_bleed_rate() > BLEED_RATE_NOTICABLE) this_injury_desc = "badly bleeding [this_injury_desc]" else this_injury_desc = "bleeding [this_injury_desc]" diff --git a/code/modules/surgery/organs/internal/artery/_artery.dm b/code/modules/surgery/organs/internal/artery/_artery.dm index 2cb592fa5ba..3d39a3c7116 100644 --- a/code/modules/surgery/organs/internal/artery/_artery.dm +++ b/code/modules/surgery/organs/internal/artery/_artery.dm @@ -9,9 +9,6 @@ organ_efficiency = list(ORGAN_SLOT_ARTERY = 100) needs_processing = TRUE - maxHealth = ARTERY_MAX_HEALTH - high_threshold = ARTERY_MAX_HEALTH * 0.8 - low_threshold = ARTERY_MAX_HEALTH * 0.2 pain_multiplier = 0.05 organ_volume = 0.5 diff --git a/code/modules/surgery/surgeries/organic_steps.dm b/code/modules/surgery/surgeries/organic_steps.dm index f467564c1ee..30042e1b50d 100644 --- a/code/modules/surgery/surgeries/organic_steps.dm +++ b/code/modules/surgery/surgeries/organic_steps.dm @@ -108,7 +108,12 @@ . = ..() if(!.) return - return length(bodypart.wounds) + if(length(bodypart.wounds)) + return TRUE + for(var/obj/item/organ/artery in bodypart.getorganslotlist(ORGAN_SLOT_ARTERY)) + if(artery.damage) + return TRUE + return FALSE /datum/surgery_step/cauterize/preop(mob/user, mob/living/target, target_zone, obj/item/tool, datum/intent/intent) display_results(user, target, "I begin to cauterize the wounds on [target]'s [parse_zone(target_zone)]...", @@ -124,7 +129,10 @@ if(bodypart) for(var/datum/wound/bleeder in bodypart.wounds) bleeder.cauterize_wound() - bodypart.receive_damage(burn = 40) //painful, but the wounds go away eh? + for(var/obj/item/organ/artery in bodypart.getorganslotlist(ORGAN_SLOT_ARTERY)) + if(artery.damage) + artery.applyOrganDamage(-artery.damage) + bodypart.bodypart_attacked_by(BCLASS_BURN, dam = 25, modifiers = list(CRIT_MOD_CHANCE = -100)) //painful, but the wounds go away eh? target.emote("scream") return TRUE From 30ca4a101867de1f8dbe659473d2dd5d26ab29e5 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Wed, 13 May 2026 21:41:31 -0700 Subject: [PATCH 33/59] DRAW IT LESS --- code/datums/injury/_injury.dm | 9 +++------ code/game/objects/items/needle.dm | 2 +- code/modules/crafting/alchemy/herbal_recipes.dm | 2 ++ .../reagents/chemistry/reagents/medicine_reagents.dm | 6 ++++++ code/modules/surgery/bodyparts/_bodyparts.dm | 2 +- code/modules/surgery/surgeries/healing.dm | 7 +++++++ 6 files changed, 20 insertions(+), 8 deletions(-) diff --git a/code/datums/injury/_injury.dm b/code/datums/injury/_injury.dm index b89d2017833..e6fa0686ebb 100644 --- a/code/datums/injury/_injury.dm +++ b/code/datums/injury/_injury.dm @@ -258,10 +258,9 @@ /datum/injury/proc/can_heal() return !(damage_type & WOUND_DIVINE) -/// Heal the given amount of damagem and returns how much is left over from amount_heal. -/// Keep update_bodypart FALSE if you are looping through injuries. +/// Heal the given amount of damage and returns how much is left over from amount_heal. /// CAN QDELETE INJURY. -/datum/injury/proc/heal_damage(amount_heal, update_bodypart = TRUE, updating_health = FALSE) +/datum/injury/proc/heal_damage(amount_heal, update_bodypart = FALSE, updating_health = TRUE) if(amount_heal <= 0) return var/healed_damage = min(damage, amount_heal) @@ -274,9 +273,7 @@ var/mob/living/carbon/cached_parent_mob = parent_mob if(!damage) qdel(src) - if(update_bodypart && cached_bodypart) - updating_health &= cached_bodypart?.post_damage_change(updating_health) - if(updating_health) + if(update_bodypart && cached_bodypart && cached_bodypart.post_damage_change(updating_health)) cached_parent_mob?.update_damage_overlays() // return amount of healing still leftover, can be used for other injuries return (amount_heal - healed_damage) diff --git a/code/game/objects/items/needle.dm b/code/game/objects/items/needle.dm index 5c7a12423bd..7c774db26f6 100644 --- a/code/game/objects/items/needle.dm +++ b/code/game/objects/items/needle.dm @@ -262,7 +262,7 @@ user.adjust_experience(/datum/attribute/skill/misc/medicine, amt2raise) . = TRUE var/injury_heal = min(10, injury.damage_per_injury() - injury.autoheal_cutoff) - injury.heal_damage(injury_heal) + injury.heal_damage(injury_heal, TRUE) if(injury.damage_per_injury() > injury.autoheal_cutoff) user.visible_message(span_green("[user] partially stitches \a [injury.get_desc()] on [target]'s [affecting.name] with \the [src]."), \ span_green("I partially stitch \a [injury.get_desc()] on \the [affecting.name] with \the [src].")) diff --git a/code/modules/crafting/alchemy/herbal_recipes.dm b/code/modules/crafting/alchemy/herbal_recipes.dm index f0337f4e8c9..03b3c9a51b3 100644 --- a/code/modules/crafting/alchemy/herbal_recipes.dm +++ b/code/modules/crafting/alchemy/herbal_recipes.dm @@ -86,6 +86,8 @@ if(QDELETED(injury)) continue injury.heal_damage(1) + if(affected_bodypart.post_damage_change()) + affected_mob.update_damage_overlays() affected_bodypart.disinfect_limb(20 SECONDS) // Weak Mana/Stamina Potions (based on hypericum/benedictus/mentha) diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index 0d88a9e2752..82b72386493 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -190,6 +190,8 @@ continue injury.adjust_germ_level(-10) injury.heal_damage(3) + if(affected_bodypart.post_damage_change()) + affected_mob.update_damage_overlays() affected_bodypart.disinfect_limb(30 SECONDS) . = ..() @@ -278,6 +280,8 @@ continue injury.salve_injury() injury.heal_damage(2) + if(affected_bodypart.post_damage_change()) + affected_mob.update_damage_overlays() affected_bodypart.adjust_germ_level(-10) . = ..() @@ -409,6 +413,8 @@ continue injury.salve_injury() injury.heal_damage(1.5) + if(affected_bodypart.post_damage_change()) + affected_mob.update_damage_overlays() affected_bodypart.adjust_germ_level(-15) . = ..() diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index 6f6cb1ae817..0090208ebbb 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -926,7 +926,7 @@ if(shock_penalty) owner.update_shock_penalty(shock_penalty) - return post_damage_change(FALSE, TRUE) + return post_damage_change(updating_health, TRUE) //Heals brute and burn damage for the organ. Returns 1 if the damage-icon states changed at all. //Damage cannot go below zero. diff --git a/code/modules/surgery/surgeries/healing.dm b/code/modules/surgery/surgeries/healing.dm index 50eaf223746..aa1a5b836f6 100644 --- a/code/modules/surgery/surgeries/healing.dm +++ b/code/modules/surgery/surgeries/healing.dm @@ -110,6 +110,13 @@ urhealedamt_burn = injury.heal_damage(urhealedamt_burn) else if(injury.damage_type & BRUTE_WOUND_TYPES) urhealedamt_brute = injury.heal_damage(urhealedamt_brute) + + var/update_overlays = FALSE + for(var/obj/item/bodypart/bodypart as anything in target.bodyparts) + update_overlays |= bodypart.post_damage_change(FALSE) + if(update_overlays) + target.update_damage_overlays() + target.updatehealth() else target.heal_bodypart_damage(urhealedamt_brute, urhealedamt_burn, required_status = BODYPART_ORGANIC) SEND_SIGNAL(user, COMSIG_LIVING_HEALED_OTHER, urhealedamt_brute + urhealedamt_burn) From ec23800fd4ebe391def415d209ab281420e7af05 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Thu, 14 May 2026 18:33:44 -0700 Subject: [PATCH 34/59] more fixes --- code/__DEFINES/chat/span.dm | 1 + code/__DEFINES/combat.dm | 2 + code/__DEFINES/medical.dm | 41 ++++++++++--------- code/__DEFINES/movespeed_modification.dm | 2 + code/__DEFINES/pain.dm | 15 +++---- code/__HELPERS/medical.dm | 2 +- code/datums/injury/_injury.dm | 13 +++--- code/datums/wounds/_wound.dm | 20 +++++---- code/datums/wounds/arteries.dm | 10 ++++- code/datums/wounds/disembowel.dm | 5 ++- code/datums/wounds/dismemberment.dm | 2 +- code/datums/wounds/fractures.dm | 23 +++++++++++ code/datums/wounds/special.dm | 2 +- code/datums/wounds/teeth.dm | 12 +++--- .../modules/mob/living/carbon/carbon_shock.dm | 11 +++-- .../modules/mob/living/carbon/damage_procs.dm | 5 +-- code/modules/surgery/bodyparts/_bodyparts.dm | 4 +- .../surgery/bodyparts/bodypart_wounds.dm | 9 +--- code/modules/surgery/organs/_organ.dm | 3 +- .../surgery/organs/internal/artery/_artery.dm | 2 +- .../surgery/organs/internal/stomach.dm | 1 + .../surgery/organs/organ_processing/heart.dm | 12 ++++-- 22 files changed, 118 insertions(+), 79 deletions(-) diff --git a/code/__DEFINES/chat/span.dm b/code/__DEFINES/chat/span.dm index 3e755cc5c2b..e8fff227b9c 100644 --- a/code/__DEFINES/chat/span.dm +++ b/code/__DEFINES/chat/span.dm @@ -106,6 +106,7 @@ #define span_artery(str) ("" + str + "") #define span_love(str) ("" + str + "") #define span_animatedpain(str) ("" + str + "") +#define span_bolddanger(str) ("" + str + "") //Sizes // arbitary names. ordered by smallest to biggest diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index 594cb834b02..7f68f092dd4 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -116,6 +116,8 @@ #define CRAWLING_ADD_SLOWDOWN 7 //slowdown for dislocated limbs #define DISLOCATED_ADD_SLOWDOWN 2 +//slowdown for fractured limbs +#define FRACTURED_ADD_SLOWDOWN 3 //Attack types for checking shields/hit reactions #define MELEE_ATTACK 1 diff --git a/code/__DEFINES/medical.dm b/code/__DEFINES/medical.dm index a17610ef184..b85b1da4c79 100644 --- a/code/__DEFINES/medical.dm +++ b/code/__DEFINES/medical.dm @@ -22,6 +22,27 @@ #define BODY_ZONE_PRECISE_L_FOOT "l_foot" #define BODY_ZONE_PRECISE_R_FOOT "r_foot" +#define ALL_BODYPARTS list(\ + BODY_ZONE_PRECISE_SKULL, BODY_ZONE_PRECISE_EARS, \ + BODY_ZONE_PRECISE_L_EYE, BODY_ZONE_PRECISE_R_EYE, \ + BODY_ZONE_PRECISE_NOSE, BODY_ZONE_PRECISE_MOUTH, \ + BODY_ZONE_PRECISE_NECK, \ + BODY_ZONE_HEAD, \ + BODY_ZONE_PRECISE_STOMACH, BODY_ZONE_PRECISE_GROIN, \ + BODY_ZONE_CHEST, \ + BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_PRECISE_L_HAND, \ + BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, \ + BODY_ZONE_PRECISE_R_FOOT, BODY_ZONE_PRECISE_L_FOOT, \ + BODY_ZONE_R_LEG, BODY_ZONE_L_LEG, \ +) + +#define GENERIC_FRACTURE_BODYPARTS list(\ + BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, \ + BODY_ZONE_R_LEG, BODY_ZONE_L_LEG, \ + BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_PRECISE_L_HAND, \ + BODY_ZONE_PRECISE_R_FOOT, BODY_ZONE_PRECISE_L_FOOT, \ +) + ///the side we are facing #define BODY_ZONE_FACING_FRONT "front_face" #define BODY_ZONE_FACING_L_ARM "l_arm_face" @@ -169,24 +190,6 @@ DEFINE_BITFIELD(organ_flags, list( #define BBC_STAGE_DETECTABLE 0.15 #define BBC_SPREAD_RATE BBC_STAGE_DETECTABLE * 0.5 -#define ALL_BODYPARTS list(\ - BODY_ZONE_PRECISE_L_EYE, BODY_ZONE_PRECISE_R_EYE, \ - BODY_ZONE_PRECISE_MOUTH, \ - BODY_ZONE_HEAD, BODY_ZONE_PRECISE_NECK, \ - BODY_ZONE_CHEST, \ - BODY_ZONE_PRECISE_GROIN, \ - BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, \ - BODY_ZONE_R_LEG, BODY_ZONE_L_LEG, \ - BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_PRECISE_L_HAND, \ - BODY_ZONE_PRECISE_R_FOOT, BODY_ZONE_PRECISE_L_FOOT, \ -) - -#define GENERIC_FRACTURE_BODYPARTS list(\ - BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, \ - BODY_ZONE_R_LEG, BODY_ZONE_L_LEG, \ - BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_PRECISE_L_HAND, \ - BODY_ZONE_PRECISE_R_FOOT, BODY_ZONE_PRECISE_L_FOOT, \ -) // ~should take around 20 minutes for a body to fully rot #define MIN_ORGAN_DECAY_INFECTION 0.25 #define MAX_ORGAN_DECAY_INFECTION 0.5 @@ -303,7 +306,7 @@ DEFINE_BITFIELD(organ_flags, list( // ~arteries -#define ARTERIAL_BLOOD_FLOW 20 +#define ARTERIAL_BLOOD_FLOW 40 #define ARTERY_HEAD /obj/item/organ/artery/head #define ARTERY_MOUTH /obj/item/organ/artery/mouth diff --git a/code/__DEFINES/movespeed_modification.dm b/code/__DEFINES/movespeed_modification.dm index a0b108e3f33..664eece2366 100644 --- a/code/__DEFINES/movespeed_modification.dm +++ b/code/__DEFINES/movespeed_modification.dm @@ -90,6 +90,8 @@ #define MOVESPEED_ID_DISLOCATION_RIGHT_FOOT "DISLOCATION_RIGHT_FOOT" #define MOVESPEED_ID_DISLOCATION_LEFT_LEG "DISLOCATION_LEFT_LEG" #define MOVESPEED_ID_DISLOCATION_LEFT_FOOT "DISLOCATION_LEFT_FOOT" +#define MOVESPEED_ID_FRACTURE_RIGHT_LEG "FRACTURE_RIGHT_LEG" +#define MOVESPEED_ID_FRACTURE_LEFT_LEG "FRACTURE_LEFT_LEG" #define MOVESPEED_ID_SPELL_CASTING "SPELL_CASTING" #define MOVESPEED_ID_FROST_DEBUFF "FROST_DEBUFF" diff --git a/code/__DEFINES/pain.dm b/code/__DEFINES/pain.dm index 87123f1be34..32d06649f7a 100644 --- a/code/__DEFINES/pain.dm +++ b/code/__DEFINES/pain.dm @@ -6,10 +6,10 @@ #define SHOCK_STAGE_2 40 #define SHOCK_STAGE_3 50 #define SHOCK_STAGE_4 70 // "Softcrit" -#define SHOCK_STAGE_5 90 +#define SHOCK_STAGE_5 100 #define SHOCK_STAGE_6 130 // "Hardcrit" -#define SHOCK_STAGE_7 160 -#define SHOCK_STAGE_8 210 +#define SHOCK_STAGE_7 170 +#define SHOCK_STAGE_8 220 #define SHOCK_STAGE_MAX SHOCK_STAGE_8 // ~shock modifiers @@ -32,18 +32,13 @@ #define PAIN_NO_SPEAK 250 /// Divisor used in pain calculations, since carbon pain is a flat amount and spread across bodyparts -#define PAINKILLER_DIVISOR 1.75 +#define PAINKILLER_DIVISOR 1.5 /// Use this to keep the speed of pain-related systems consistent relatively #define PAIN_SYSTEM_SPEED_MODIFIER 3 -#define PAIN_KNOCKDOWN_MESSAGE "gives in to the pain!" -#define PAIN_KNOCKDOWN_MESSAGE_SELF "I give in to the pain!" -#define PAIN_KNOCKOUT_MESSAGE "caves in to the pain!" -#define PAIN_KNOCKOUT_MESSAGE_SELF "OH LORD! The PAIN!" - /// Cooldown before resetting the injury penalty #define SHOCK_PENALTY_COOLDOWN_DURATION 5 SECONDS #define COOLDOWN_CARBON_ENDORPHINATION "carbon_endorphination" /// Cooldown before our body endorphinates itself again -#define ENDORPHINATION_COOLDOWN_DURATION 60 SECONDS +#define ENDORPHINATION_COOLDOWN_DURATION 45 SECONDS diff --git a/code/__HELPERS/medical.dm b/code/__HELPERS/medical.dm index 2115bfb61e2..e95611d1014 100644 --- a/code/__HELPERS/medical.dm +++ b/code/__HELPERS/medical.dm @@ -49,7 +49,7 @@ return BODY_ZONE_CHEST if(BODY_ZONE_PRECISE_L_EYE, BODY_ZONE_PRECISE_R_EYE) return BODY_ZONE_HEAD - if(BODY_ZONE_PRECISE_MOUTH, BODY_ZONE_PRECISE_NOSE, BODY_ZONE_PRECISE_EARS) + if(BODY_ZONE_PRECISE_NOSE, BODY_ZONE_PRECISE_EARS) return BODY_ZONE_HEAD if(BODY_ZONE_PRECISE_SKULL, BODY_ZONE_PRECISE_NECK) return BODY_ZONE_HEAD diff --git a/code/datums/injury/_injury.dm b/code/datums/injury/_injury.dm index e6fa0686ebb..3fd4cdf6f98 100644 --- a/code/datums/injury/_injury.dm +++ b/code/datums/injury/_injury.dm @@ -184,13 +184,12 @@ // checks whether the injury has been appropriately treated /datum/injury/proc/is_treated() - switch(damage_type) - if(WOUND_SLASH, WOUND_PUNCTURE, WOUND_BITE, WOUND_LASH) - return (is_bandaged() || is_sutured()) - if(WOUND_DIVINE) - return (is_bandaged()) - if(WOUND_BURN) - return (is_salved() || (is_disinfected() && is_bandaged()) ) + if(damage_type & SEWABLE_WOUND_TYPES) + return (is_bandaged() || is_sutured()) + if(damage_type & (WOUND_BLUNT|WOUND_DIVINE)) + return is_bandaged() + if(damage_type & WOUND_BURN) + return (is_salved() || (is_disinfected() && is_bandaged()) ) return TRUE // Checks whether other other can be merged into src. diff --git a/code/datums/wounds/_wound.dm b/code/datums/wounds/_wound.dm index 9f69c70ec83..0f2f6cab5f2 100644 --- a/code/datums/wounds/_wound.dm +++ b/code/datums/wounds/_wound.dm @@ -3,7 +3,9 @@ GLOBAL_LIST_INIT(primordial_wounds, init_primordial_wounds()) /proc/init_primordial_wounds() var/list/primordial_wounds = list() - for(var/wound_type in typesof(/datum/wound)) + for(var/datum/wound/wound_type as anything in typesof(/datum/wound)) + if(IS_ABSTRACT(wound_type)) + continue primordial_wounds[wound_type] = new wound_type() return primordial_wounds @@ -127,7 +129,9 @@ GLOBAL_LIST_INIT(primordial_wounds, init_primordial_wounds()) ///list of viable zones for this var/list/viable_zones = ALL_BODYPARTS - /// These are effectively try_crit moved onto the wound + /** + * These are effectively try_crit moved onto the wound + * */ /// Minimum damage required to attempt this wound var/min_damage = 5 @@ -210,20 +214,16 @@ GLOBAL_LIST_INIT(primordial_wounds, init_primordial_wounds()) final_message = "Critical hit! [final_message]" return final_message -/datum/wound/proc/get_crit_prob(bclass, dam, damage_dividend, mob/living/user, obj/item/bodypart/affected, zone_precise, list/modifiers) +/datum/wound/proc/get_crit_prob(bclass, dam, damage_dividend, mob/living/user, obj/item/bodypart/affected, list/modifiers) if(!can_roll) return 0 if(!(bclass in associated_bclasses)) return 0 if(dam < min_damage) return 0 - if(deprecise_zone(zone_precise) != affected.body_zone) - return 0 // we are in a weird place if(damage_dividend < min_damage_dividend) if(!(brittle_bonus && HAS_TRAIT(affected, TRAIT_BRITTLE))) // brittle skips the dividend gate return 0 - if(length(viable_zones) && !(zone_precise in viable_zones) && viable_zones != ALL_BODYPARTS) - return 0 var/used = base_prob_weight + (modifiers?[CRIT_MOD_CHANCE] || 0) var/calc_dam = dam @@ -251,7 +251,7 @@ GLOBAL_LIST_INIT(primordial_wounds, init_primordial_wounds()) return pick(sound_effect) /// Returns whether or not this wound can be applied to a given bodypart -/datum/wound/proc/can_apply_to_bodypart(obj/item/bodypart/affected) +/datum/wound/proc/can_apply_to_bodypart(obj/item/bodypart/affected, zone_precise) if(bodypart_owner || owner || QDELETED(affected) || QDELETED(affected.owner)) return FALSE if(!ignore_bloody && !isnull(bleed_rate) && !affected.can_bloody_wound()) @@ -259,6 +259,10 @@ GLOBAL_LIST_INIT(primordial_wounds, init_primordial_wounds()) for(var/datum/wound/other_wound as anything in affected.wounds) if(!can_stack_with(other_wound)) return FALSE + if(length(viable_zones) && !(zone_precise in viable_zones)) + return FALSE + if(deprecise_zone(zone_precise) != affected.body_zone) + return FALSE // we are in a weird place return TRUE /// Returns whether or not this wound can be applied while this other wound is present diff --git a/code/datums/wounds/arteries.dm b/code/datums/wounds/arteries.dm index 5fb894379bd..3d7704448ef 100644 --- a/code/datums/wounds/arteries.dm +++ b/code/datums/wounds/arteries.dm @@ -16,8 +16,8 @@ /datum/wound/artery/can_apply_to_bodypart(obj/item/bodypart/affected) if(affected.status == BODYPART_ROBOTIC) return FALSE - if(!affected.get_cut(ignore_gauze = TRUE)) - return FALSE + // if(!affected.get_cut(ignore_gauze = TRUE)) + // return FALSE return ..() /datum/wound/artery/can_stack_with(datum/wound/other) @@ -46,6 +46,7 @@ else artery.tear() . = ..() + affected.temporary_crit_paralysis(10 SECONDS) qdel(src) /datum/wound/artery/neck_slice @@ -64,5 +65,10 @@ /datum/wound/artery/heart/can_apply_to_bodypart(obj/item/bodypart/affected) if(affected.limb_flags & BODYPART_BONE_ENCASED && !affected.has_wound(/datum/wound/fracture)) return FALSE + // Must be vitals zone + if(affected.body_zone != BODY_ZONE_CHEST) + return FALSE + if(!affected.getorganslot(ORGAN_SLOT_HEART)) + return FALSE return ..() diff --git a/code/datums/wounds/disembowel.dm b/code/datums/wounds/disembowel.dm index 44d67d90156..420e6c2c206 100644 --- a/code/datums/wounds/disembowel.dm +++ b/code/datums/wounds/disembowel.dm @@ -2,7 +2,10 @@ name = "disembowelment" check_name = "GUTS" severity = WOUND_SEVERITY_FATAL - crit_message = "%VICTIM spills %P_THEIR organs!" + crit_message = list( + "%VICTIM spills %P_THEIR organs!", + "%VICTIM spills %P_THEIR entrails!", + ) sound_effect = 'sound/combat/crit2.ogg' whp = 100 sewn_whp = 35 diff --git a/code/datums/wounds/dismemberment.dm b/code/datums/wounds/dismemberment.dm index b8de8d5d563..5cb7cb771dd 100644 --- a/code/datums/wounds/dismemberment.dm +++ b/code/datums/wounds/dismemberment.dm @@ -4,7 +4,7 @@ severity = WOUND_SEVERITY_CRITICAL whp = 75 sewn_whp = 25 - bleed_rate = ARTERIAL_BLOOD_FLOW + bleed_rate = ARTERIAL_BLOOD_FLOW / 2 sewn_bleed_rate = 0.25 clotting_threshold = null sewn_clotting_threshold = null diff --git a/code/datums/wounds/fractures.dm b/code/datums/wounds/fractures.dm index 5ae779d6c32..20831296edf 100644 --- a/code/datums/wounds/fractures.dm +++ b/code/datums/wounds/fractures.dm @@ -53,6 +53,29 @@ return FALSE return TRUE +/datum/wound/fracture/on_bodypart_gain(obj/item/bodypart/affected) + . = ..() + affected.temporary_crit_paralysis(20 SECONDS) + ADD_TRAIT(affected, TRAIT_FINGERLESS, "[type]") + ADD_TRAIT(affected, TRAIT_BRITTLE, "[type]") + switch(affected.body_zone) + if(BODY_ZONE_R_LEG) + affected.owner.add_movespeed_modifier(MOVESPEED_ID_FRACTURE_RIGHT_LEG, multiplicative_slowdown = FRACTURED_ADD_SLOWDOWN) + if(BODY_ZONE_L_LEG) + affected.owner.add_movespeed_modifier(MOVESPEED_ID_FRACTURE_LEFT_LEG, multiplicative_slowdown = FRACTURED_ADD_SLOWDOWN) + +/datum/wound/fracture/on_bodypart_loss(obj/item/bodypart/affected) + . = ..() + REMOVE_TRAIT(affected, TRAIT_FINGERLESS, "[type]") + REMOVE_TRAIT(affected, TRAIT_BRITTLE, "[type]") + if(!affected.owner) + return + switch(affected.body_zone) + if(BODY_ZONE_R_LEG) + affected.owner.remove_movespeed_modifier(MOVESPEED_ID_FRACTURE_RIGHT_LEG) + if(BODY_ZONE_L_LEG) + affected.owner.remove_movespeed_modifier(MOVESPEED_ID_FRACTURE_LEFT_LEG) + /datum/wound/fracture/on_mob_gain(mob/living/affected) . = ..() if(gain_emote) diff --git a/code/datums/wounds/special.dm b/code/datums/wounds/special.dm index 96f229efa49..cf5feabeb93 100644 --- a/code/datums/wounds/special.dm +++ b/code/datums/wounds/special.dm @@ -270,7 +270,7 @@ /datum/wound/cbt/get_crit_prob(bclass, dam, damage_dividend, mob/living/user, obj/item/bodypart/affected, zone_precise, list/modifiers) if(!(bclass in associated_bclasses)) return 0 - if(length(viable_zones) && !(zone_precise in viable_zones) && viable_zones != ALL_BODYPARTS) + if(length(viable_zones) && !(zone_precise in viable_zones)) return 0 if(HAS_TRAIT(affected.owner, TRAIT_CRITICAL_RESISTANCE)) return 0 diff --git a/code/datums/wounds/teeth.dm b/code/datums/wounds/teeth.dm index d36efab681e..7289f50ff7c 100644 --- a/code/datums/wounds/teeth.dm +++ b/code/datums/wounds/teeth.dm @@ -4,6 +4,10 @@ severity = WOUND_SEVERITY_LIGHT associated_bclasses = FRACTURE_BCLASSES viable_zones = list(BODY_ZONE_PRECISE_MOUTH) + strong_intent_bonus = TRUE + aimed_intent_bonus = TRUE + brittle_bonus = TRUE + damage_divisor = 3 /datum/wound/teeth/can_apply_to_bodypart(obj/item/bodypart/mouth/affected) . = ..() @@ -11,7 +15,8 @@ return FALSE if(!istype(affected)) return FALSE - + if(!affected.max_teeth) + return FALSE if(!affected.get_teeth_amount()) return FALSE return TRUE @@ -20,11 +25,6 @@ . = ..() if(!.) return - if(!istype(affected)) - return FALSE - if(!affected.max_teeth) - qdel(src) - return if(!silent && sound_effect) playsound(affected.owner, pick(sound_effect), 90, TRUE) affected.knock_out_teeth(rand(1, 4)) diff --git a/code/modules/mob/living/carbon/carbon_shock.dm b/code/modules/mob/living/carbon/carbon_shock.dm index 03abe502d41..e225372108e 100644 --- a/code/modules/mob/living/carbon/carbon_shock.dm +++ b/code/modules/mob/living/carbon/carbon_shock.dm @@ -226,7 +226,7 @@ endorphinate() if(DT_PROB(1, delta_time)) Unconscious(5) - endorphinate() + endorphinate(TRUE) if(DT_PROB(4, delta_time)) emote("gargle") @@ -235,15 +235,14 @@ emote("scream") if(!HAS_TRAIT(src, TRAIT_NOPAINSTUN)) Unconscious(10 SECONDS) - //Attempt to inject combat cocktail - ONE FINAL TIME - endorphinate() + endorphinate(TRUE) if((shock_stage >= SHOCK_STAGE_8) && (previous_shock_stage >= SHOCK_STAGE_8)) //How the fuck are we still alive? if(!IsUnconscious()) - visible_message(PAIN_KNOCKOUT_MESSAGE) - custom_pain(PAIN_KNOCKOUT_MESSAGE_SELF, 100, nopainloss = TRUE) + visible_message(span_bolddanger("[src] scrunchs [p_their()] and collapses!"), ignored_mobs = src) + custom_pain(span_animatedpain("OH LORD! The PAIN!"), 100, nopainloss = TRUE) //death_rattle() if(!HAS_TRAIT(src, TRAIT_NOPAINSTUN)) Unconscious(15 SECONDS) - endorphinate() + endorphinate(TRUE) diff --git a/code/modules/mob/living/carbon/damage_procs.dm b/code/modules/mob/living/carbon/damage_procs.dm index ac7b9aed2b0..94087cb4c20 100644 --- a/code/modules/mob/living/carbon/damage_procs.dm +++ b/code/modules/mob/living/carbon/damage_procs.dm @@ -269,7 +269,7 @@ * Adds pain onto a limb while giving the player a message styled depending on the powerf of the pain added. * * Arguments: - * * Message is the custom message to be displayed + * * Message is the custom message to be displayed to the source * * Power decides how much painkillers will stop the message, as well as how much pain it causes * * Forced means it ignores anti-spam timer */ @@ -281,8 +281,7 @@ return FALSE // Take the edge off - power -= get_chem_effect(CE_PAINKILLER) - if(power <= 0) + if(power - get_chem_effect(CE_PAINKILLER)/PAINKILLER_DIVISOR <= 0) return FALSE // Share the pain diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index 0090208ebbb..f75d3b86c9b 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -806,6 +806,8 @@ return amount = min(max_pain_damage - pain_dam, amount) amount -= owner.get_chem_effect(CE_PAINKILLER)/PAINKILLER_DIVISOR + if(amount <= 0) + return pain_dam = round(pain_dam + amount, DAMAGE_PRECISION) if(updating_health) owner.update_shock() @@ -1447,7 +1449,7 @@ if(owner) for(var/thing in shuffle(owner.getorganslotlist(slot))) var/obj/item/organ/organ = thing - if(organ.current_zone == body_zone) + if(deprecise_zone(organ.current_zone) == body_zone) return organ else var/list/organs = list() diff --git a/code/modules/surgery/bodyparts/bodypart_wounds.dm b/code/modules/surgery/bodyparts/bodypart_wounds.dm index 76c5e8811d9..a94a582ebf6 100644 --- a/code/modules/surgery/bodyparts/bodypart_wounds.dm +++ b/code/modules/surgery/bodyparts/bodypart_wounds.dm @@ -241,16 +241,11 @@ var/list/candidates = list() for(var/wound_type in GLOB.primordial_wounds) var/datum/wound/primordial = GLOB.primordial_wounds[wound_type] - var/datum/primoridial_type = primordial.type - if(IS_ABSTRACT(primoridial_type)) - continue if(!primordial.can_roll) continue - var/chance = primordial.get_crit_prob(bclass, dam, damage_dividend, user, src, zone_precise, modifiers) - if(chance <= 0) - continue - if(!primordial.can_apply_to_bodypart(src)) + if(!primordial.can_apply_to_bodypart(src, zone_precise)) continue + var/chance = primordial.get_crit_prob(bclass, dam, damage_dividend, user, src, modifiers) if(prob(chance)) candidates += wound_type diff --git a/code/modules/surgery/organs/_organ.dm b/code/modules/surgery/organs/_organ.dm index dd7877658b2..c222f00a301 100644 --- a/code/modules/surgery/organs/_organ.dm +++ b/code/modules/surgery/organs/_organ.dm @@ -14,6 +14,7 @@ /// Time we have spent failing var/failure_time = 0 + /// The body zone this organ is supposed to inhabit. var/zone = BODY_ZONE_CHEST var/unique_slot var/unique_side_sprite = FALSE @@ -78,7 +79,7 @@ /// Needs to get processed on next life() tick var/needs_processing = TRUE - /// Efficiency attached to each slot + /// When an efficiency is associated with a slot, it is added to that zones internal_organs_slot. Efficiency varies from 0 to 100. var/list/organ_efficiency = list() ///this is just an easy to access list of modification sources going slot = list(type = val) var/list/organ_efficiency_modification diff --git a/code/modules/surgery/organs/internal/artery/_artery.dm b/code/modules/surgery/organs/internal/artery/_artery.dm index 3d39a3c7116..3c81d01ccca 100644 --- a/code/modules/surgery/organs/internal/artery/_artery.dm +++ b/code/modules/surgery/organs/internal/artery/_artery.dm @@ -18,7 +18,7 @@ nutriment_req = 0.09 hydration_req = 0.03 - /// How much blood we gush when torn. This will be multiplied by delta_time + /// How much blood we gush when torn. Multiplied by damage/maxHealth var/blood_flow = ARTERIAL_BLOOD_FLOW /// If torn, this is basically the time until we gush again COOLDOWN_DECLARE(next_squirt) diff --git a/code/modules/surgery/organs/internal/stomach.dm b/code/modules/surgery/organs/internal/stomach.dm index 01b3ac1a133..0a82acb8b47 100644 --- a/code/modules/surgery/organs/internal/stomach.dm +++ b/code/modules/surgery/organs/internal/stomach.dm @@ -85,6 +85,7 @@ slot = ORGAN_SLOT_GUTS attack_verb = list("gored", "squished", "slapped", "digested") desc = "" + organ_efficiency = list(ORGAN_SLOT_GUTS = 100) healing_factor = STANDARD_ORGAN_HEALING low_threshold_passed = "My guts flashes with pain before subsiding." diff --git a/code/modules/surgery/organs/organ_processing/heart.dm b/code/modules/surgery/organs/organ_processing/heart.dm index a5e4d3a415d..69b0aedbd21 100644 --- a/code/modules/surgery/organs/organ_processing/heart.dm +++ b/code/modules/surgery/organs/organ_processing/heart.dm @@ -66,9 +66,9 @@ owner.set_heartattack(TRUE) ADD_TRAIT(owner, TRAIT_DEATHS_DOOR, ASYSTOLE_TRAIT) return - if(owner.pulse <= PULSE_NONE) - ADD_TRAIT(owner, TRAIT_DEATHS_DOOR, ASYSTOLE_TRAIT) - return + // if(owner.pulse <= PULSE_NONE) + // ADD_TRAIT(owner, TRAIT_DEATHS_DOOR, ASYSTOLE_TRAIT) + // return // Pulse normally shouldn't go above PULSE_FASTER unless you get extremely doped up if(pulse_mod < 5) @@ -189,6 +189,10 @@ owner.remove_status_effect(/datum/status_effect/debuff/bleedingworse) owner.remove_status_effect(/datum/status_effect/debuff/bleeding) owner.apply_status_effect(/datum/status_effect/debuff/bleedingworst) + else + owner.remove_status_effect(/datum/status_effect/debuff/bleeding) + owner.remove_status_effect(/datum/status_effect/debuff/bleedingworse) + owner.remove_status_effect(/datum/status_effect/debuff/bleedingworst) else owner.remove_status_effect(/datum/status_effect/debuff/bleeding) owner.remove_status_effect(/datum/status_effect/debuff/bleedingworse) @@ -201,7 +205,7 @@ owner.remove_stress(/datum/stress_event/bleeding) /datum/organ_process/heart/proc/handle_heartbeat(mob/living/carbon/owner, delta_time, times_fired) - var/cardiac_arrest = owner.undergoing_nervous_system_failure() + var/cardiac_arrest = owner.undergoing_cardiac_arrest() var/nervous_failure = owner.undergoing_nervous_system_failure() if((owner.heartbeat_sound != BEAT_SLOW) && (cardiac_arrest || nervous_failure)) owner.heartbeat_sound = BEAT_SLOW From 349baacd2ce620f5f18532b910ae19da6cc9bbce Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Thu, 14 May 2026 18:52:59 -0700 Subject: [PATCH 35/59] germ defines --- code/__DEFINES/medical.dm | 10 ---------- code/datums/injury/_injury.dm | 4 ++-- code/game/atom/atoms.dm | 4 ++-- code/game/objects/items.dm | 2 +- code/game/objects/items/natural/cloth.dm | 4 ++-- code/modules/surgery/bodyparts/_bodyparts.dm | 4 ++-- code/modules/surgery/organs/_organ.dm | 8 ++++---- code/modules/surgery/organs/_organ_attack.dm | 2 +- 8 files changed, 14 insertions(+), 24 deletions(-) diff --git a/code/__DEFINES/medical.dm b/code/__DEFINES/medical.dm index b85b1da4c79..9cb372842cf 100644 --- a/code/__DEFINES/medical.dm +++ b/code/__DEFINES/medical.dm @@ -231,16 +231,6 @@ DEFINE_BITFIELD(organ_flags, list( /// Maximum amount of germs surgery can cause #define SURGERY_GERM_MAXIMUM 800 - -// ~germ defines -/// Medical equipment should start out as this -#define GERM_LEVEL_STERILE 0 -/// Maximum germ level you can reach by standing still. -#define GERM_LEVEL_AMBIENT 250 -/// Maximum germ level any atom can normally achieve -#define GERM_LEVEL_MAXIMUM 1000 - - /// Germ levels for carbon mob hygiene #define GERM_LEVEL_START_MIN 0 #define GERM_LEVEL_START_MAX 100 diff --git a/code/datums/injury/_injury.dm b/code/datums/injury/_injury.dm index 3fd4cdf6f98..645e4536e6c 100644 --- a/code/datums/injury/_injury.dm +++ b/code/datums/injury/_injury.dm @@ -139,7 +139,7 @@ set_mob(parent_bodypart.owner) //increase or decrease infection -/datum/injury/proc/adjust_germ_level(add_germs, minimum_germs = 0, maximum_germs = GERM_LEVEL_MAXIMUM) +/datum/injury/proc/adjust_germ_level(add_germs, minimum_germs = 0, maximum_germs = INFECTION_LEVEL_THREE) germ_level = clamp(germ_level + add_germs, minimum_germs, maximum_germs) //makes the injury get infected more when the victim is moving around @@ -404,7 +404,7 @@ return CHECK_BITFIELD(injury_flags, INJURY_SURGICAL) /datum/injury/proc/is_disinfected() - if(CHECK_BITFIELD(injury_flags, INJURY_DISINFECTED) && (germ_level <= 0)) + if(CHECK_BITFIELD(injury_flags, INJURY_DISINFECTED) || germ_level < INFECTION_LEVEL_ONE) return TRUE return FALSE diff --git a/code/game/atom/atoms.dm b/code/game/atom/atoms.dm index 280080c5157..f1e88e354d2 100644 --- a/code/game/atom/atoms.dm +++ b/code/game/atom/atoms.dm @@ -166,7 +166,7 @@ /** * Basically the level of dirtiness on an atom, which will spread to wounds and stuff and cause infections */ - var/germ_level = GERM_LEVEL_AMBIENT + var/germ_level = INFECTION_LEVEL_ONE /** @@ -898,7 +898,7 @@ atom_colours[colour_priority] = null update_atom_colour() -/atom/proc/adjust_germ_level(add_germs, minimum_germs = 0, maximum_germs = GERM_LEVEL_MAXIMUM) +/atom/proc/adjust_germ_level(add_germs, minimum_germs = 0, maximum_germs = INFECTION_LEVEL_THREE) germ_level = clamp(germ_level + add_germs, minimum_germs, maximum_germs) /// Force set the germ level diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index f349edcfc0c..5340afaaac9 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -1491,7 +1491,7 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/e if(ismob(loc)) update_slot_icon() if(clean_types & CLEAN_WASH) - set_germ_level(GERM_LEVEL_STERILE) + set_germ_level(0) /obj/item/proc/do_pickup_animation(atom/target, turf/source) set waitfor = FALSE diff --git a/code/game/objects/items/natural/cloth.dm b/code/game/objects/items/natural/cloth.dm index 7e971ad4531..6c40310e5c3 100644 --- a/code/game/objects/items/natural/cloth.dm +++ b/code/game/objects/items/natural/cloth.dm @@ -221,9 +221,9 @@ H.update_damage_overlays() if(M == user) - user.visible_message("[user] bandages [user.p_their()] [affecting].", "I bandage my [affecting].") + user.visible_message(span_notice("[user] bandages [user.p_their()] [affecting.name]."), span_notice("I bandage my [affecting.name].")) else - user.visible_message("[user] bandages [M]'s [affecting].", "I bandage [M]'s [affecting].") + user.visible_message(span_notice("[user] bandages [M]'s [affecting.name]."), span_notice("I bandage [M]'s [affecting.name].")) /obj/item/natural/cloth/update_overlays() diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index f75d3b86c9b..7d65d452e1a 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -10,7 +10,7 @@ icon_state = "" layer = BELOW_MOB_LAYER //so it isn't hidden behind objects when on the floor - germ_level = GERM_LEVEL_STERILE + germ_level = 0 var/disinfects_in var/mob/living/carbon/owner @@ -367,7 +367,7 @@ update_icon_dropped() /// Adding/removing germs -/obj/item/bodypart/adjust_germ_level(add_germs, minimum_germs = 0, maximum_germs = GERM_LEVEL_MAXIMUM) +/obj/item/bodypart/adjust_germ_level(add_germs, minimum_germs = 0, maximum_germs = INFECTION_LEVEL_THREE) . = ..() if(germ_level >= INFECTION_LEVEL_THREE && !CHECK_BITFIELD(limb_flags, BODYPART_DEAD)) kill_limb() diff --git a/code/modules/surgery/organs/_organ.dm b/code/modules/surgery/organs/_organ.dm index c222f00a301..c8decbf9607 100644 --- a/code/modules/surgery/organs/_organ.dm +++ b/code/modules/surgery/organs/_organ.dm @@ -10,7 +10,7 @@ grid_width = 32 grid_height = 32 - germ_level = GERM_LEVEL_STERILE + germ_level = 0 /// Time we have spent failing var/failure_time = 0 @@ -207,7 +207,7 @@ /obj/item/organ/proc/unnecrose_organ() . = FALSE if(CHECK_BITFIELD(organ_flags, ORGAN_DEAD)) - set_germ_level(GERM_LEVEL_STERILE) + set_germ_level(0) organ_flags &= ~ORGAN_DEAD return TRUE @@ -372,7 +372,7 @@ /obj/item/organ/proc/decay(delta_time) adjust_germ_level(rand(min_decay_factor,max_decay_factor) * delta_time) -/obj/item/organ/adjust_germ_level(add_germs, minimum_germs = 0, maximum_germs = GERM_LEVEL_MAXIMUM) +/obj/item/organ/adjust_germ_level(add_germs, minimum_germs = 0, maximum_germs = INFECTION_LEVEL_THREE) . = ..() if((germ_level >= INFECTION_LEVEL_THREE) && !CHECK_BITFIELD(organ_flags, ORGAN_DEAD)) kill_organ() @@ -480,7 +480,7 @@ return if((germ_level < INFECTION_LEVEL_ONE) && (antibiotics >= 20)) - set_germ_level(GERM_LEVEL_STERILE) + set_germ_level(0) else adjust_germ_level(-antibiotics * SANITIZATION_ANTIBIOTIC * delta_time) //at germ_level == 500 and 50 antibiotic, this should cure the infection in 5 minutes if(owner?.body_position == LYING_DOWN) diff --git a/code/modules/surgery/organs/_organ_attack.dm b/code/modules/surgery/organs/_organ_attack.dm index d4a9a5df38e..c629aa1b0a9 100644 --- a/code/modules/surgery/organs/_organ_attack.dm +++ b/code/modules/surgery/organs/_organ_attack.dm @@ -140,5 +140,5 @@ user.visible_message(span_notice("[user] burn the rot away from \the [src]."), \ span_notice("I burn the rot away from \the [src]."), \ vision_distance = COMBAT_MESSAGE_RANGE) - set_germ_level(GERM_LEVEL_STERILE) + set_germ_level(0) return TRUE From 09036f2d27fd5ed2343feb32f554d7c7ed118e10 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Thu, 14 May 2026 19:17:13 -0700 Subject: [PATCH 36/59] bandage --- code/datums/injury/_injury.dm | 6 ++--- code/datums/wounds/spill.dm | 2 +- code/game/objects/items/natural/bandage.dm | 2 +- code/game/objects/items/natural/cloth.dm | 26 +++++++++++++------ .../surgery/bodyparts/bodypart_wounds.dm | 15 ++++++----- 5 files changed, 32 insertions(+), 19 deletions(-) diff --git a/code/datums/injury/_injury.dm b/code/datums/injury/_injury.dm index 645e4536e6c..e45434f1a13 100644 --- a/code/datums/injury/_injury.dm +++ b/code/datums/injury/_injury.dm @@ -185,11 +185,11 @@ // checks whether the injury has been appropriately treated /datum/injury/proc/is_treated() if(damage_type & SEWABLE_WOUND_TYPES) - return (is_bandaged() || is_sutured()) + return is_bandaged() || is_sutured() if(damage_type & (WOUND_BLUNT|WOUND_DIVINE)) return is_bandaged() if(damage_type & WOUND_BURN) - return (is_salved() || (is_disinfected() && is_bandaged()) ) + return is_salved() || (is_bandaged() && (is_disinfected() || germ_level <= 0)) return TRUE // Checks whether other other can be merged into src. @@ -404,7 +404,7 @@ return CHECK_BITFIELD(injury_flags, INJURY_SURGICAL) /datum/injury/proc/is_disinfected() - if(CHECK_BITFIELD(injury_flags, INJURY_DISINFECTED) || germ_level < INFECTION_LEVEL_ONE) + if(CHECK_BITFIELD(injury_flags, INJURY_DISINFECTED) && germ_level <= 0) return TRUE return FALSE diff --git a/code/datums/wounds/spill.dm b/code/datums/wounds/spill.dm index d6eedac2817..defa026f106 100644 --- a/code/datums/wounds/spill.dm +++ b/code/datums/wounds/spill.dm @@ -51,7 +51,7 @@ return TRUE /datum/wound/spill/gut/on_crit_applied(obj/item/bodypart/affected, mob/living/user, zone_precise, list/modifiers) - affected.add_wound(/datum/wound/slash/disembowel) + affected.add_wound(/datum/wound/slash/disembowel, crit_message = TRUE) /datum/wound/spill/gut/on_bodypart_gain(obj/item/bodypart/new_limb) . = ..() diff --git a/code/game/objects/items/natural/bandage.dm b/code/game/objects/items/natural/bandage.dm index a473ba477e4..c864a71e2fd 100644 --- a/code/game/objects/items/natural/bandage.dm +++ b/code/game/objects/items/natural/bandage.dm @@ -5,7 +5,7 @@ desc = "A fabric treated and specially made to help with bleeding wounds. Better and faster at stopping bleeding than your regular piece of cloth." bundletype = /obj/item/natural/bundle/cloth/bandage bandage_effectiveness = 0.25 - bandage_health = 600 + bandage_health = 500 bandage_speed = 4 SECONDS volume = 18 item_weight = 18 GRAMS diff --git a/code/game/objects/items/natural/cloth.dm b/code/game/objects/items/natural/cloth.dm index 6c40310e5c3..d3fc803c399 100644 --- a/code/game/objects/items/natural/cloth.dm +++ b/code/game/objects/items/natural/cloth.dm @@ -2,7 +2,7 @@ name = "cloth" desc = "A square of cloth mended from fibers." icon_state = "cloth" - possible_item_intents = list(/datum/intent/use, /datum/intent/soak, /datum/intent/wring) + possible_item_intents = list(/datum/intent/use, INTENT_SOAK, INTENT_WRING) force = 0 throwforce = 0 firefuel = 3 MINUTES @@ -26,10 +26,14 @@ var/bandage_effectiveness = 0 // EXPERIMENTAL CHANGE: BANDAGES STOP ALL BLEEDING ///how long it will take to bandage something with this var/bandage_speed = 7 SECONDS - ///How much you can bleed into the bandage until it needs to be changed (Blood loss is measured in 50% of the health) - var/bandage_health = 300 + ///How much you can bleed into the bandage until it needs to be changed + var/bandage_health = 250 obj_flags = CAN_BE_HIT //enables splashing on by containers +/obj/item/natural/cloth/examine(mob/user) + . = ..() + . += span_notice("[src] has [PERCENT(bandage_health/initial(bandage_health))]% of its absorption left.") + /obj/item/natural/cloth/Initialize(mapload, vol) . = ..() if(isnum(vol) && vol > 0) @@ -76,11 +80,13 @@ return TRUE /obj/item/natural/cloth/proc/on_clean_success(datum/source, atom/target, mob/living/user, clean_succeeded) - if(clean_succeeded) - if(prob(50) && isturf(target)) // to prevent infinitely renewable water - var/turf/T = target - T.add_liquid_from_reagents(reagents, amount = 1) - reagents.remove_all(1) + if(!clean_succeeded) + return + if(prob(50) && isturf(target)) // to prevent infinitely renewable water + var/turf/T = target + T.add_liquid_from_reagents(reagents, amount = 1) + reagents.remove_all(1) + if(!reagents.total_volume) bandage_health = initial(bandage_health) bandage_effectiveness = initial(bandage_effectiveness) @@ -178,6 +184,8 @@ reagents.trans_to(O, reagents.total_volume, 1, transfered_by = user) user.visible_message(span_small("[user] wrings out \the [src] in \the [O]."), span_small("I wring out \the [src] in \the [O]."), vision_distance = 2) playsound(O, pick('sound/foley/waterwash (1).ogg','sound/foley/waterwash (2).ogg'), 25, FALSE) + bandage_health = initial(bandage_health) + bandage_effectiveness = initial(bandage_effectiveness) else if(isturf(target)) var/turf/T = target if(istype(T, /turf/open/water)) @@ -191,6 +199,8 @@ reagents.clear_reagents() user.visible_message(span_small("[user] wrings out \the [src]."), span_small("I wring out \the [src]."), vision_distance = 2) playsound(T, pick('sound/foley/waterwash (1).ogg','sound/foley/waterwash (2).ogg'), 25, FALSE) + bandage_health = initial(bandage_health) + bandage_effectiveness = initial(bandage_effectiveness) update_appearance() diff --git a/code/modules/surgery/bodyparts/bodypart_wounds.dm b/code/modules/surgery/bodyparts/bodypart_wounds.dm index a94a582ebf6..966c56dc63d 100644 --- a/code/modules/surgery/bodyparts/bodypart_wounds.dm +++ b/code/modules/surgery/bodyparts/bodypart_wounds.dm @@ -366,13 +366,15 @@ update_disabled() return TRUE -/obj/item/bodypart/proc/try_bandage(obj/item/new_bandage) - if(!new_bandage) +/obj/item/bodypart/proc/try_bandage(obj/item/natural/cloth/new_bandage) + if(!istype(new_bandage)) return FALSE + . = TRUE bandage = new_bandage - bandage_limb() new_bandage.forceMove(src) - return TRUE + if(!new_bandage.bandage_health) + return + bandage_limb() /obj/item/bodypart/proc/try_bandage_expire() if(!bandage) @@ -409,9 +411,10 @@ return FALSE if(!bandage) return FALSE - if(owner.stat != DEAD) + bandage.bandage_effectiveness = 1 + unbandage_limb() + if(owner.stat < UNCONSCIOUS) to_chat(owner, span_warning("Blood soaks through the bandage on my [name].")) - bandage.bandage_effectiveness = 1 return bandage.add_mob_blood(owner) /obj/item/bodypart/proc/remove_bandage() From 36fb33ba9a4d70c4200615073fc9098d3ebfdd0e Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Fri, 15 May 2026 16:27:19 -0700 Subject: [PATCH 37/59] we all die this time --- code/datums/injury/_injury.dm | 2 +- code/datums/injury/organic/burn.dm | 2 +- code/datums/wounds/_wound.dm | 5 ++++- code/modules/surgery/bodyparts/bodypart_wounds.dm | 10 ++++++---- .../surgery/organs/internal/artery/_artery.dm | 15 ++++++++------- .../surgery/organs/organ_processing/spleen.dm | 2 +- 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/code/datums/injury/_injury.dm b/code/datums/injury/_injury.dm index e45434f1a13..72af99d3f72 100644 --- a/code/datums/injury/_injury.dm +++ b/code/datums/injury/_injury.dm @@ -189,7 +189,7 @@ if(damage_type & (WOUND_BLUNT|WOUND_DIVINE)) return is_bandaged() if(damage_type & WOUND_BURN) - return is_salved() || (is_bandaged() && (is_disinfected() || germ_level <= 0)) + return is_salved() || is_bandaged() return TRUE // Checks whether other other can be merged into src. diff --git a/code/datums/injury/organic/burn.dm b/code/datums/injury/organic/burn.dm index ca8471206e2..dbeef495564 100644 --- a/code/datums/injury/organic/burn.dm +++ b/code/datums/injury/organic/burn.dm @@ -1,7 +1,7 @@ /** BURNS **/ /datum/injury/burn damage_type = WOUND_BURN - autoheal_cutoff = 3 + autoheal_cutoff = 6 max_bleeding_stage = 0 infection_rate = 1.25 diff --git a/code/datums/wounds/_wound.dm b/code/datums/wounds/_wound.dm index 0f2f6cab5f2..2a98055355b 100644 --- a/code/datums/wounds/_wound.dm +++ b/code/datums/wounds/_wound.dm @@ -250,7 +250,8 @@ GLOBAL_LIST_INIT(primordial_wounds, init_primordial_wounds()) return 'sound/combat/CriticalHit.ogg' return pick(sound_effect) -/// Returns whether or not this wound can be applied to a given bodypart +/// Returns whether or not this wound can be applied to a given bodypart. +/// Setting zone_precise will check whether its in viable_zones and if it matches limb body_zone /datum/wound/proc/can_apply_to_bodypart(obj/item/bodypart/affected, zone_precise) if(bodypart_owner || owner || QDELETED(affected) || QDELETED(affected.owner)) return FALSE @@ -259,6 +260,8 @@ GLOBAL_LIST_INIT(primordial_wounds, init_primordial_wounds()) for(var/datum/wound/other_wound as anything in affected.wounds) if(!can_stack_with(other_wound)) return FALSE + if(!zone_precise) + return TRUE if(length(viable_zones) && !(zone_precise in viable_zones)) return FALSE if(deprecise_zone(zone_precise) != affected.body_zone) diff --git a/code/modules/surgery/bodyparts/bodypart_wounds.dm b/code/modules/surgery/bodyparts/bodypart_wounds.dm index 966c56dc63d..5c2af403f25 100644 --- a/code/modules/surgery/bodyparts/bodypart_wounds.dm +++ b/code/modules/surgery/bodyparts/bodypart_wounds.dm @@ -73,20 +73,22 @@ healed_any = TRUE return healed_any -/// Adds a wound to this bodypart, applying any necessary effects +/// Adds a wound to this bodypart, applying any necessary effects. IS NOT SAFE FOR CHECKING LIMB ZONES. /obj/item/bodypart/proc/add_wound(datum/wound/wound, silent = FALSE, crit_message = FALSE, forced = FALSE) if(!wound || !owner) return if(!forced && (owner.status_flags & GODMODE)) return + if(!ispath(wound) && !istype(wound)) + return + if(ispath(wound, /datum/wound)) var/datum/wound/primordial_wound = GLOB.primordial_wounds[wound] if(!primordial_wound.can_apply_to_bodypart(src)) return wound = new wound() - else if(!istype(wound)) - return - else if(!wound.can_apply_to_bodypart(src)) + + if(!wound.can_apply_to_bodypart(src)) qdel(wound) return if(!wound.apply_to_bodypart(src, silent, crit_message)) diff --git a/code/modules/surgery/organs/internal/artery/_artery.dm b/code/modules/surgery/organs/internal/artery/_artery.dm index 3c81d01ccca..5554f113242 100644 --- a/code/modules/surgery/organs/internal/artery/_artery.dm +++ b/code/modules/surgery/organs/internal/artery/_artery.dm @@ -22,10 +22,11 @@ var/blood_flow = ARTERIAL_BLOOD_FLOW /// If torn, this is basically the time until we gush again COOLDOWN_DECLARE(next_squirt) + // On average 2 seconds, like life ticks /// Minimum time until we squirt again - var/squirt_delay_min_seconds = 4 + var/squirt_delay_min_seconds = 1.5 SECONDS /// Maximum time until we squirt again - var/squirt_delay_max_seconds = 10 + var/squirt_delay_max_seconds = 2.5 SECONDS ///squirting sound var/squirt_sound = list('sound/gore/artery1.ogg', 'sound/gore/artery2.ogg', 'sound/gore/artery3.ogg') @@ -57,7 +58,7 @@ if(PULSE_FASTER, PULSE_THREADY) bleed_mod *= 1.5 var/final_bleed_rate = CEILING(blood_flow * bleed_mod * delta_time, 0.1) - if(!final_bleed_rate <= 0) + if(final_bleed_rate <= 0) return if(COOLDOWN_FINISHED(src, next_squirt)) squirt(final_bleed_rate) @@ -72,7 +73,7 @@ owner.bleed(blood_flow) current_blood = 0 applyOrganDamage(maxHealth * 0.5) - var/cd_time = rand(squirt_delay_min_seconds, squirt_delay_max_seconds) SECONDS + var/cd_time = rand(squirt_delay_min_seconds, squirt_delay_max_seconds) COOLDOWN_START(src, next_squirt, cd_time) /obj/item/organ/artery/dissect() @@ -83,7 +84,7 @@ owner.bleed(blood_flow) current_blood = 0 applyOrganDamage(maxHealth) - var/cd_time = rand(squirt_delay_min_seconds, squirt_delay_max_seconds) SECONDS + var/cd_time = rand(squirt_delay_min_seconds, squirt_delay_max_seconds) COOLDOWN_START(src, next_squirt, cd_time) /obj/item/organ/artery/applyOrganDamage(amount, maximum = maxHealth, silent = FALSE) @@ -112,9 +113,9 @@ playsound(owner, squirt_sound, 75, 0) owner.bleed(amount) //owner.do_arterygush() - COOLDOWN_START(src, next_squirt, rand(squirt_delay_min_seconds, squirt_delay_max_seconds) SECONDS) + COOLDOWN_START(src, next_squirt, rand(squirt_delay_min_seconds, squirt_delay_max_seconds)) else - COOLDOWN_START(src, next_squirt, rand(squirt_delay_min_seconds, squirt_delay_max_seconds) SECONDS) + COOLDOWN_START(src, next_squirt, rand(squirt_delay_min_seconds, squirt_delay_max_seconds)) return squirt_less(amount, open_wound) else return squirt_less(amount, open_wound) diff --git a/code/modules/surgery/organs/organ_processing/spleen.dm b/code/modules/surgery/organs/organ_processing/spleen.dm index ac7058e58ed..10e237eb43a 100644 --- a/code/modules/surgery/organs/organ_processing/spleen.dm +++ b/code/modules/surgery/organs/organ_processing/spleen.dm @@ -20,7 +20,7 @@ for(var/thing in spleens) var/obj/item/organ/spleen/spleen = thing blood_regen += (spleen.get_slot_efficiency(ORGAN_SLOT_SPLEEN) * spleen.blood_regen_factor) - combined_nutrition_requirement += spleen.nutriment_req / 80 + combined_nutrition_requirement += spleen.nutriment_req / 100 var/blood_restore_multiplier = 1 + owner.get_chem_effect(CE_BLOODRESTORE) blood_regen *= blood_restore_multiplier combined_nutrition_requirement *= blood_restore_multiplier From 5fedfaed7797afd4bd7d2469c19b97e97f4ae099 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Fri, 15 May 2026 23:11:07 -0700 Subject: [PATCH 38/59] stack tracing --- code/datums/wounds/arteries.dm | 4 ++-- code/modules/surgery/bodyparts/bodypart_wounds.dm | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/code/datums/wounds/arteries.dm b/code/datums/wounds/arteries.dm index 3d7704448ef..1bda16da58c 100644 --- a/code/datums/wounds/arteries.dm +++ b/code/datums/wounds/arteries.dm @@ -16,8 +16,8 @@ /datum/wound/artery/can_apply_to_bodypart(obj/item/bodypart/affected) if(affected.status == BODYPART_ROBOTIC) return FALSE - // if(!affected.get_cut(ignore_gauze = TRUE)) - // return FALSE + if(!affected.get_cut(ignore_gauze = TRUE)) + return FALSE return ..() /datum/wound/artery/can_stack_with(datum/wound/other) diff --git a/code/modules/surgery/bodyparts/bodypart_wounds.dm b/code/modules/surgery/bodyparts/bodypart_wounds.dm index 5c2af403f25..294c8c37dd7 100644 --- a/code/modules/surgery/bodyparts/bodypart_wounds.dm +++ b/code/modules/surgery/bodyparts/bodypart_wounds.dm @@ -87,10 +87,11 @@ if(!primordial_wound.can_apply_to_bodypart(src)) return wound = new wound() - - if(!wound.can_apply_to_bodypart(src)) + else if(!wound.can_apply_to_bodypart(src)) qdel(wound) return + if(!(body_zone in wound.viable_zones)) + stack_trace("Call to add_wound added [wound] to [src] that isn't part of its viable zones!") if(!wound.apply_to_bodypart(src, silent, crit_message)) qdel(wound) return From 149fc36addf5f9563ecb6a917b91e3be57fada56 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Fri, 15 May 2026 23:14:18 -0700 Subject: [PATCH 39/59] emote fix --- code/datums/emotes.dm | 104 +++++++++++----------- code/datums/quirks/vices/physical_vice.dm | 2 +- 2 files changed, 54 insertions(+), 52 deletions(-) diff --git a/code/datums/emotes.dm b/code/datums/emotes.dm index 7272b7b6415..1fb42b66c18 100644 --- a/code/datums/emotes.dm +++ b/code/datums/emotes.dm @@ -24,7 +24,8 @@ var/stat_allowed = CONSCIOUS var/sound //Sound to play when emote is called var/vary = FALSE //used for the honk borg emote - var/only_forced_audio = FALSE //can only code call this event instead of the player. + /// Can only code call this event instead of the player. + var/only_forced_audio = FALSE /// The cooldown between the uses of the emote. var/cooldown = 0.8 SECONDS /// Blocks any intentional emote use for this time. @@ -60,12 +61,17 @@ /datum/emote/proc/adjacentaction(mob/user, mob/target) return +/** + * Handles the modifications and execution of emotes. + * + * Arguments: + * * user - Person that is trying to send the emote. + * * params - Parameters added after the emote. + * * type_override - Override to the current emote_type. + * * intentional - Bool that says whether the emote was forced (FALSE) or not (TRUE). + * * targeted - The emote targets adjacent mobs. Runs asynchronously. + */ /datum/emote/proc/run_emote(mob/user, params, type_override, intentional = FALSE, targeted = FALSE) - . = TRUE - if(!can_run_emote(user, TRUE, intentional)) - return FALSE - if(only_forced_audio && intentional) - return FALSE if(targeted) INVOKE_ASYNC(src, PROC_REF(async_targeted_emote), user, params, type_override, intentional) return @@ -92,9 +98,9 @@ var/sound/tmp_sound = get_sound(user) if(!istype(tmp_sound)) tmp_sound = sound(get_sfx(tmp_sound)) - tmp_sound.frequency = pitch - if(tmp_sound && (!only_forced_audio || !intentional)) - if (ishuman(user)) + if(tmp_sound && should_play_sound(user, intentional)) + tmp_sound.frequency = pitch + if(ishuman(user)) var/mob/living/carbon/human/H = user if(H.voice_type == VOICE_TYPE_ANDRO) tmp_sound.frequency = pitch * 0.92 @@ -146,9 +152,6 @@ pitch_modifier += (10 - GET_MOB_ATTRIBUTE_VALUE(src, STAT_STRENGTH)) * 0.03 return clamp(final_pitch + pitch_modifier, 0.5, 2) - - - /datum/emote/proc/get_env(mob/living/user) return @@ -161,50 +164,49 @@ return I.emote_environment /datum/emote/proc/get_sound(mob/living/user) - if(sound) - return sound + return sound //by default just return this var. /datum/emote/living/get_sound(mob/living/user) if(sound) return sound - else - if(ishuman(user)) - var/mob/living/carbon/human/H = user - var/used_sound - var/possible_sounds - var/modifier - if(H.age == AGE_OLD) - modifier = "old" - if(!ignore_silent && !H.can_speak() || (!ignore_silent && HAS_TRAIT(H, TRAIT_MUTE)) || (!ignore_silent && HAS_TRAIT(H, TRAIT_BAGGED))) - modifier = "silenced" - if(user.gender == FEMALE && H.dna.species.soundpack_f) - possible_sounds = H.dna.species.soundpack_f.get_sound(key,modifier) - else if(H.dna.species.soundpack_m) - possible_sounds = H.dna.species.soundpack_m.get_sound(key,modifier) - if(H.voice_type) - switch (H.voice_type) - if (VOICE_TYPE_MASC, VOICE_TYPE_MASC_FOP) - possible_sounds = H.dna.species.soundpack_m.get_sound(key, modifier) - if (VOICE_TYPE_FEM, VOICE_TYPE_FEM_DAINTY, VOICE_TYPE_FEM_HAUGHTY, VOICE_TYPE_ANDRO) - if (H.dna.species.soundpack_f) - possible_sounds = H.dna.species.soundpack_f.get_sound(key, modifier) - else - possible_sounds = H.dna.species.soundpack_m.get_sound(key, modifier) - if(possible_sounds) - if(islist(possible_sounds)) - var/list/PS = possible_sounds - if(PS.len > 1) - used_sound = pick_n_take(possible_sounds) - if(used_sound == H.last_sound) - used_sound = pick(possible_sounds) + else if(ishuman(user)) + var/mob/living/carbon/human/H = user + var/used_sound + var/possible_sounds + var/modifier + if(H.age == AGE_OLD) + modifier = "old" + if(!ignore_silent && !H.can_speak() || (!ignore_silent && HAS_TRAIT(H, TRAIT_MUTE)) || (!ignore_silent && HAS_TRAIT(H, TRAIT_BAGGED))) + modifier = "silenced" + if(user.gender == FEMALE && H.dna.species.soundpack_f) + possible_sounds = H.dna.species.soundpack_f.get_sound(key,modifier) + else if(H.dna.species.soundpack_m) + possible_sounds = H.dna.species.soundpack_m.get_sound(key,modifier) + if(H.voice_type) + switch (H.voice_type) + if (VOICE_TYPE_MASC, VOICE_TYPE_MASC_FOP) + possible_sounds = H.dna.species.soundpack_m.get_sound(key, modifier) + if (VOICE_TYPE_FEM, VOICE_TYPE_FEM_DAINTY, VOICE_TYPE_FEM_HAUGHTY, VOICE_TYPE_ANDRO) + if (H.dna.species.soundpack_f) + possible_sounds = H.dna.species.soundpack_f.get_sound(key, modifier) else - used_sound = pick(possible_sounds) - else //direct file - used_sound = possible_sounds - H.last_sound = used_sound - return used_sound - else - return user.get_sound(key) + possible_sounds = H.dna.species.soundpack_m.get_sound(key, modifier) + if(!possible_sounds) + return + if(islist(possible_sounds)) + var/list/PS = possible_sounds + if(PS.len > 1) + used_sound = pick_n_take(possible_sounds) + if(used_sound == H.last_sound) + used_sound = pick(possible_sounds) + else + used_sound = pick(possible_sounds) + else //direct file + used_sound = possible_sounds + H.last_sound = used_sound + return used_sound + else + return user.get_sound(key) /mob/living/proc/get_sound(input) return diff --git a/code/datums/quirks/vices/physical_vice.dm b/code/datums/quirks/vices/physical_vice.dm index 1d050826c3d..d92b2a591ec 100644 --- a/code/datums/quirks/vices/physical_vice.dm +++ b/code/datums/quirks/vices/physical_vice.dm @@ -375,7 +375,7 @@ if(owner.stat == UNCONSCIOUS && owner.IsSleeping()) if(world.time >= next_scream) next_scream = world.time + rand(30 SECONDS, 60 SECONDS) - owner.emote("scream") + owner.emote("scream", forced = TRUE) /datum/quirk/vice/nightmares/on_remove() STOP_PROCESSING(SSobj, src) From d823f12219e8f4faf7ea8551c8dab3b1efd70c59 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Fri, 15 May 2026 23:59:14 -0700 Subject: [PATCH 40/59] sleep soul --- code/__HELPERS/pronouns.dm | 7 +++++-- code/modules/combat/clash/clash.dm | 2 +- code/modules/mob/living/carbon/carbon_defense.dm | 4 ++++ code/modules/mob/living/carbon/examine.dm | 2 +- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/code/__HELPERS/pronouns.dm b/code/__HELPERS/pronouns.dm index 1c9723d85c1..1df1a003a78 100644 --- a/code/__HELPERS/pronouns.dm +++ b/code/__HELPERS/pronouns.dm @@ -32,8 +32,11 @@ /datum/proc/p_theyve(capitalized, temp_gender) . = p_they(capitalized, temp_gender) + "'" + copytext(p_have(temp_gender), 3) -/datum/proc/p_theyre(capitalized, temp_gender) - . = p_they(capitalized, temp_gender) + "'" + copytext(p_are(temp_gender), 2) +/datum/proc/p_theyre(capitalized, temp_gender, expand) + if(expand) + . = "[p_they(capitalized, temp_gender)] [p_are(temp_gender)]" + else + . = p_they(capitalized, temp_gender) + "'" + copytext(p_are(temp_gender), 2) /datum/proc/p_s(temp_gender) //is this a descriptive proc name, or what? . = "s" diff --git a/code/modules/combat/clash/clash.dm b/code/modules/combat/clash/clash.dm index f9cc9c14a85..b0bd3310f52 100644 --- a/code/modules/combat/clash/clash.dm +++ b/code/modules/combat/clash/clash.dm @@ -33,7 +33,7 @@ their_item.take_damage(max(damage, 1), BRUTE, our_item.damage_type) visible_message(span_suicide("[src] ripostes [user] with \the [our_item]!")) - span_notice("[capitalize(user.p_theyre())] exposed!") + span_notice("[user.p_theyre(TRUE)] exposed!") playsound(src, 'sound/combat/clash_struck.ogg', 100) user.apply_status_effect(/datum/status_effect/debuff/exposed, 3 SECONDS) diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 945823f3071..a234d7c14dc 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -454,6 +454,10 @@ playsound(src, 'sound/blank.ogg', 50, TRUE, -1) + if(!getorganslot(ORGAN_SLOT_BRAIN) || HAS_TRAIT(src, TRAIT_CRITICAL_CONDITION) || (HAS_TRAIT(src, TRAIT_FAKEDEATH))) + return + if(stat == UNCONSCIOUS) + to_chat(M, span_notice("[p_theyre(capitalized = TRUE, expand = TRUE)]n't responding to anything around [p_them()] and seem[p_s()] to be asleep.")) /mob/living/carbon/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0) var/obj/item/organ/eyes/eyes = getorganslot(ORGAN_SLOT_EYES) diff --git a/code/modules/mob/living/carbon/examine.dm b/code/modules/mob/living/carbon/examine.dm index 02d48574de8..f98237682f1 100644 --- a/code/modules/mob/living/carbon/examine.dm +++ b/code/modules/mob/living/carbon/examine.dm @@ -518,7 +518,7 @@ else switch(stat) if(UNCONSCIOUS) - . += span_notice("[P[THEYRE]]n't responding to anything around [P[THEM]] and seem[p_s()] to be asleep.") + . += span_boldwarning("[P[THEYRE]] unconscious.") if(SOFT_CRIT) . += span_notice("[P[THEYRE]] barely conscious.") From ddeb4f538535a6838e15fd98e980fdcc698d464b Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Sat, 16 May 2026 07:08:47 -0700 Subject: [PATCH 41/59] too fast --- code/__DEFINES/medical.dm | 2 +- code/datums/wounds/dismemberment.dm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/__DEFINES/medical.dm b/code/__DEFINES/medical.dm index 9cb372842cf..ea064875ea6 100644 --- a/code/__DEFINES/medical.dm +++ b/code/__DEFINES/medical.dm @@ -296,7 +296,7 @@ DEFINE_BITFIELD(organ_flags, list( // ~arteries -#define ARTERIAL_BLOOD_FLOW 40 +#define ARTERIAL_BLOOD_FLOW 20 #define ARTERY_HEAD /obj/item/organ/artery/head #define ARTERY_MOUTH /obj/item/organ/artery/mouth diff --git a/code/datums/wounds/dismemberment.dm b/code/datums/wounds/dismemberment.dm index 5cb7cb771dd..b8de8d5d563 100644 --- a/code/datums/wounds/dismemberment.dm +++ b/code/datums/wounds/dismemberment.dm @@ -4,7 +4,7 @@ severity = WOUND_SEVERITY_CRITICAL whp = 75 sewn_whp = 25 - bleed_rate = ARTERIAL_BLOOD_FLOW / 2 + bleed_rate = ARTERIAL_BLOOD_FLOW sewn_bleed_rate = 0.25 clotting_threshold = null sewn_clotting_threshold = null From 8a542493b4a54884e8b8901da63e267c6f09c570 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Sat, 16 May 2026 07:35:18 -0700 Subject: [PATCH 42/59] continue refining pain --- code/__DEFINES/pain.dm | 12 ++++++------ code/modules/clothing/neck/misc.dm | 1 - code/modules/mob/living/carbon/carbon_shock.dm | 8 ++++---- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/code/__DEFINES/pain.dm b/code/__DEFINES/pain.dm index 32d06649f7a..7163569a68f 100644 --- a/code/__DEFINES/pain.dm +++ b/code/__DEFINES/pain.dm @@ -4,12 +4,12 @@ // ~shock stages #define SHOCK_STAGE_1 20 #define SHOCK_STAGE_2 40 -#define SHOCK_STAGE_3 50 -#define SHOCK_STAGE_4 70 // "Softcrit" -#define SHOCK_STAGE_5 100 -#define SHOCK_STAGE_6 130 // "Hardcrit" -#define SHOCK_STAGE_7 170 -#define SHOCK_STAGE_8 220 +#define SHOCK_STAGE_3 60 +#define SHOCK_STAGE_4 80 // "Softcrit" +#define SHOCK_STAGE_5 130 +#define SHOCK_STAGE_6 160 // "Hardcrit" +#define SHOCK_STAGE_7 200 +#define SHOCK_STAGE_8 250 #define SHOCK_STAGE_MAX SHOCK_STAGE_8 // ~shock modifiers diff --git a/code/modules/clothing/neck/misc.dm b/code/modules/clothing/neck/misc.dm index a864556724b..6061221d72b 100644 --- a/code/modules/clothing/neck/misc.dm +++ b/code/modules/clothing/neck/misc.dm @@ -521,7 +521,6 @@ desc = "A heavy collar of great age, meant to protect the neck." icon_state = "aasimarneck" smeltresult = /obj/item/ingot/bronze - melting_material = /datum/material/bronze armor = ARMOR_MAILLE_GOOD /obj/item/clothing/neck/highcollier diff --git a/code/modules/mob/living/carbon/carbon_shock.dm b/code/modules/mob/living/carbon/carbon_shock.dm index e225372108e..ee7fa0c0d2d 100644 --- a/code/modules/mob/living/carbon/carbon_shock.dm +++ b/code/modules/mob/living/carbon/carbon_shock.dm @@ -202,7 +202,7 @@ if(DT_PROB(2.5, delta_time)) custom_pain("[pick("The pain is excruciating", "Please, just end the pain", "My whole body is going numb")]!", shock_stage, nopainloss = TRUE) if(!HAS_TRAIT(src, TRAIT_NOPAINSTUN)) - Paralyze(5 SECONDS) + Paralyze(2.5 SECONDS) endorphinate() if((shock_stage >= SHOCK_STAGE_6) && (previous_shock_stage >= SHOCK_STAGE_6)) @@ -218,15 +218,15 @@ if(!HAS_TRAIT(src, TRAIT_NOPAINSTUN)) Paralyze(5 SECONDS) //Attempt to inject combat cocktail, even though at this point it won't help much - endorphinate() + endorphinate(TRUE) if((shock_stage >= SHOCK_STAGE_7) && (previous_shock_stage >= SHOCK_STAGE_7)) if(!HAS_TRAIT(src, TRAIT_NOPAINSTUN)) Paralyze(5 SECONDS) - endorphinate() + endorphinate(TRUE) if(DT_PROB(1, delta_time)) Unconscious(5) - endorphinate(TRUE) + endorphinate() if(DT_PROB(4, delta_time)) emote("gargle") From f904cde340af6eeafa9e2251241d3ec45b825d9b Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Sat, 16 May 2026 07:41:37 -0700 Subject: [PATCH 43/59] fix --- code/datums/injury/_injury.dm | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/code/datums/injury/_injury.dm b/code/datums/injury/_injury.dm index 72af99d3f72..4185674f61a 100644 --- a/code/datums/injury/_injury.dm +++ b/code/datums/injury/_injury.dm @@ -114,6 +114,8 @@ if(!parent_mob) return LAZYREMOVE(parent_mob.all_injuries, src) + parent_mob.updatehealth() + parent_mob.update_damage_overlays() parent_mob = null /datum/injury/proc/remove_from_bodypart() @@ -122,6 +124,7 @@ LAZYREMOVE(parent_bodypart.injuries, src) if(parent_bodypart.last_injury == src) parent_bodypart.last_injury = null + parent_bodypart.post_damage_change() parent_bodypart = null //applies the injury on a limb proper @@ -268,12 +271,10 @@ current_stage++ desc = desc_list[current_stage] min_damage = damage_list[current_stage] - var/obj/item/bodypart/cached_bodypart = parent_bodypart - var/mob/living/carbon/cached_parent_mob = parent_mob if(!damage) qdel(src) - if(update_bodypart && cached_bodypart && cached_bodypart.post_damage_change(updating_health)) - cached_parent_mob?.update_damage_overlays() + if(update_bodypart && parent_bodypart?.post_damage_change(updating_health)) // no need to cache since qdel will update limbs and owner + parent_mob?.update_damage_overlays() // return amount of healing still leftover, can be used for other injuries return (amount_heal - healed_damage) From afc657efa159f29dcbb4483569604847b76c2c51 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Sat, 16 May 2026 21:29:29 -0700 Subject: [PATCH 44/59] more balanceslop --- code/__DEFINES/pain.dm | 6 +- code/datums/injury/_injury.dm | 4 +- code/datums/wounds/fractures.dm | 1 + .../objects/items/weapons/melee/godweapons.dm | 2 +- .../modules/mob/living/carbon/carbon_shock.dm | 83 ++++++++++--------- .../modules/mob/living/carbon/damage_procs.dm | 36 ++++---- code/modules/mob/living/living_defines.dm | 2 - code/modules/surgery/bodyparts/_bodyparts.dm | 10 +-- .../surgery/surgeries/organic_steps.dm | 2 +- 9 files changed, 77 insertions(+), 69 deletions(-) diff --git a/code/__DEFINES/pain.dm b/code/__DEFINES/pain.dm index 7163569a68f..5df06431bea 100644 --- a/code/__DEFINES/pain.dm +++ b/code/__DEFINES/pain.dm @@ -1,5 +1,7 @@ // ~pain levels when using the custom_pain proc and shit #define PAIN_EMOTE_MINIMUM 10 +#define PAIN_MESSAGE_COOLDOWN 40 SECONDS +#define PAIN_EMOTE_COOLDOWN 60 SECONDS // ~shock stages #define SHOCK_STAGE_1 20 @@ -7,8 +9,8 @@ #define SHOCK_STAGE_3 60 #define SHOCK_STAGE_4 80 // "Softcrit" #define SHOCK_STAGE_5 130 -#define SHOCK_STAGE_6 160 // "Hardcrit" -#define SHOCK_STAGE_7 200 +#define SHOCK_STAGE_6 160 +#define SHOCK_STAGE_7 200 // "Hardcrit" #define SHOCK_STAGE_8 250 #define SHOCK_STAGE_MAX SHOCK_STAGE_8 diff --git a/code/datums/injury/_injury.dm b/code/datums/injury/_injury.dm index 4185674f61a..939d0bde706 100644 --- a/code/datums/injury/_injury.dm +++ b/code/datums/injury/_injury.dm @@ -189,9 +189,9 @@ /datum/injury/proc/is_treated() if(damage_type & SEWABLE_WOUND_TYPES) return is_bandaged() || is_sutured() - if(damage_type & (WOUND_BLUNT|WOUND_DIVINE)) + if(damage_type & (WOUND_BLUNT|WOUND_INTERNAL_BRUISE)) return is_bandaged() - if(damage_type & WOUND_BURN) + if(damage_type & FIRE_WOUND_TYPES) return is_salved() || is_bandaged() return TRUE diff --git a/code/datums/wounds/fractures.dm b/code/datums/wounds/fractures.dm index 20831296edf..5314e95f267 100644 --- a/code/datums/wounds/fractures.dm +++ b/code/datums/wounds/fractures.dm @@ -144,6 +144,7 @@ /datum/wound/fracture/head/brain name = "depressed cranial fracture" + check_name = "CROWNCRACK" severity = WOUND_SEVERITY_FATAL crit_message = list( "The cranium is fractured!", diff --git a/code/game/objects/items/weapons/melee/godweapons.dm b/code/game/objects/items/weapons/melee/godweapons.dm index 2d492946438..f87ee678b60 100644 --- a/code/game/objects/items/weapons/melee/godweapons.dm +++ b/code/game/objects/items/weapons/melee/godweapons.dm @@ -158,7 +158,7 @@ return playsound(user, 'sound/surgery/scalpel2.ogg', 70) if(do_after(user, 0.5 SECONDS, target)) - C.create_injury(WOUND_SLASH, C.max_damage * 0.3, TRUE) + C.create_injury(WOUND_SLASH, BLEED_DAMAGE_RATIO/6, surgical = TRUE) playsound(user, 'sound/surgery/organ2.ogg', 70) if(do_after(user, 0.5 SECONDS, target)) diff --git a/code/modules/mob/living/carbon/carbon_shock.dm b/code/modules/mob/living/carbon/carbon_shock.dm index ee7fa0c0d2d..af0aecc9f5e 100644 --- a/code/modules/mob/living/carbon/carbon_shock.dm +++ b/code/modules/mob/living/carbon/carbon_shock.dm @@ -37,7 +37,7 @@ if(stat >= UNCONSCIOUS) return - var/our_endurance = GET_MOB_ATTRIBUTE_VALUE(src, STAT_ENDURANCE) + var/our_endurance = max(GET_MOB_ATTRIBUTE_VALUE(src, STAT_ENDURANCE), 1) /* if(traumatic_shock >= (PAIN_GIVES_IN * (our_endurance/ATTRIBUTE_MIDDLING))) @@ -65,17 +65,21 @@ damaged_bodypart = bodypart maxbpshock = bpshock - if(damaged_bodypart && (get_chem_effect(CE_PAINKILLER) < maxbpshock)) + if(damaged_bodypart) var/burning = (damaged_bodypart.burn_dam > damaged_bodypart.brute_dam) var/message + var/message_prob = 1 switch(CEILING(maxbpshock, 1)) if(1 to 10) message = "My [damaged_bodypart.name] [burning ? "burns" : "hurts"]." if(11 to 90) + message_prob = 2 message = "My [damaged_bodypart.name] [burning ? "burns" : "hurts"] badly!" if(91 to INFINITY) - message = "[pick("WHAT A PAIN!", "OH GOD!", "OH LORD!")]! My [damaged_bodypart.name] [damaged_bodypart.p_are()] [burning ? "on fire" : "hurting terribly"]!" - custom_pain(message, maxbpshock, TRUE, damaged_bodypart, TRUE) + message_prob = 2 + message = "[pick("WHAT A PAIN!", "OH THE PAIN!!", "I SUFFER!")]! My [damaged_bodypart.name] [damaged_bodypart.p_are()] [burning ? "on fire" : "hurting terribly"]!" + if(message && DT_PROB(message_prob, delta_time)) + custom_pain(message, maxbpshock, TRUE, damaged_bodypart, TRUE) // Damage to internal organs hurts a lot. for(var/obj/item/organ/organ as anything in internal_organs) @@ -153,7 +157,7 @@ recovery += 1 if(traumatic_shock < 0.25 * shock_stage) recovery += 1 - adjustShockStage(-recovery * (our_endurance/ATTRIBUTE_MIDDLING) * PAIN_SYSTEM_SPEED_MODIFIER/2) + adjustShockStage(-recovery * (our_endurance/ATTRIBUTE_MIDDLING) * PAIN_SYSTEM_SPEED_MODIFIER * 0.75) //Shock makes us slow if(shock_stage >= (SHOCK_STAGE_2 * (our_endurance/ATTRIBUTE_MIDDLING))) @@ -171,7 +175,7 @@ */ custom_pain("[pick("It hurts so much", "I really need some opium", "Ooh, the pain")]!", 10, nopainloss = TRUE) - if((shock_stage >= SHOCK_STAGE_2) && (previous_shock_stage < SHOCK_STAGE_2)) + if((shock_stage >= SHOCK_STAGE_2) && (previous_shock_stage < SHOCK_STAGE_2)) // Crossed stage 2 emote("is having trouble keeping [p_their()] eyes open.") if((shock_stage >= SHOCK_STAGE_2) && (previous_shock_stage >= SHOCK_STAGE_2)) @@ -179,69 +183,72 @@ //adjust_eye_blur(rand(1, 2)) stuttering = max(stuttering, 5) - if((shock_stage >= SHOCK_STAGE_3) && (previous_shock_stage < SHOCK_STAGE_3)) + if((shock_stage >= SHOCK_STAGE_3) && (previous_shock_stage < SHOCK_STAGE_3)) // Crossed stage 3 custom_pain("[pick("The pain is excruciating", "Please, just end the pain", "My whole body is going numb")]!", 40, nopainloss = TRUE) add_stress(/datum/stress_event/painmax) - if((shock_stage >= SHOCK_STAGE_4) && (previous_shock_stage < SHOCK_STAGE_4)) - emote("becomes limp.") + /** + * Stage 4 begins mimicking "soft crit" + */ + if((shock_stage >= SHOCK_STAGE_4) && (previous_shock_stage < SHOCK_STAGE_4)) // Crossed stage 4 + emote("freezes and goes limp.", intentional = TRUE) if(!HAS_TRAIT(src, TRAIT_NOPAINSTUN)) Immobilize(rand(2, 3) SECONDS) - endorphinate() if((shock_stage >= SHOCK_STAGE_4) && (previous_shock_stage >= SHOCK_STAGE_4)) if(DT_PROB(1, delta_time)) custom_pain("[pick("The pain is excruciating", "Please, just end the pain", "My whole body is going numb")]!", shock_stage, nopainloss = TRUE) if(!HAS_TRAIT(src, TRAIT_NOPAINSTUN)) Knockdown(2 SECONDS) - endorphinate() - if(DT_PROB(2, delta_time)) - emote("gasp") + endorphinate() if((shock_stage >= SHOCK_STAGE_5) && (previous_shock_stage >= SHOCK_STAGE_5)) - if(DT_PROB(2.5, delta_time)) + if(DT_PROB(2, delta_time)) custom_pain("[pick("The pain is excruciating", "Please, just end the pain", "My whole body is going numb")]!", shock_stage, nopainloss = TRUE) if(!HAS_TRAIT(src, TRAIT_NOPAINSTUN)) - Paralyze(2.5 SECONDS) - endorphinate() + Knockdown(3 SECONDS) + endorphinate() + if(DT_PROB(0.5, delta_time)) + emote("gasp") if((shock_stage >= SHOCK_STAGE_6) && (previous_shock_stage >= SHOCK_STAGE_6)) - if(DT_PROB(1, delta_time)) - if(!IsUnconscious()) - custom_pain("[pick("I black out", "I feel like I could die at any moment now", "I'm about to lose consciousness")]!", shock_stage, nopainloss = TRUE) + if(DT_PROB(2.5, delta_time)) + custom_pain("[pick("The pain is excruciating", "Please, just end the pain", "My whole body is going numb")]!", shock_stage, nopainloss = TRUE) if(!HAS_TRAIT(src, TRAIT_NOPAINSTUN)) - Unconscious(1 SECONDS) - endorphinate() + Knockdown(5 SECONDS) + endorphinate() + if(DT_PROB(1, delta_time)) + emote("gasp") - if((shock_stage >= SHOCK_STAGE_7) && (previous_shock_stage < SHOCK_STAGE_7)) - emote("gargle") + /** + * Stage 7 begins mimicking "hard crit" + */ + if((shock_stage >= SHOCK_STAGE_7) && (previous_shock_stage < SHOCK_STAGE_7)) // Crossed stage 7 + if(!IsUnconscious()) + custom_pain("[pick("I feel like I could die at any moment now", "I'm about to lose consciousness")]!", shock_stage, nopainloss = TRUE) if(!HAS_TRAIT(src, TRAIT_NOPAINSTUN)) - Paralyze(5 SECONDS) - //Attempt to inject combat cocktail, even though at this point it won't help much - endorphinate(TRUE) + emote("agony") + Immobilize(5 SECONDS) if((shock_stage >= SHOCK_STAGE_7) && (previous_shock_stage >= SHOCK_STAGE_7)) - if(!HAS_TRAIT(src, TRAIT_NOPAINSTUN)) + if(DT_PROB(1, delta_time)) Paralyze(5 SECONDS) endorphinate(TRUE) - if(DT_PROB(1, delta_time)) - Unconscious(5) - endorphinate() - if(DT_PROB(4, delta_time)) - emote("gargle") - if((shock_stage >= SHOCK_STAGE_8) && (previous_shock_stage < SHOCK_STAGE_8)) + if((shock_stage >= SHOCK_STAGE_8) && (previous_shock_stage < SHOCK_STAGE_8)) // Crossed stage 8 + if(!IsUnconscious()) + visible_message(span_bolddanger("[src] scrunchs [p_their()] body and collapses!"), ignored_mobs = src) + custom_pain(span_animatedpain("OH LORD! The PAIN!"), 100, nopainloss = TRUE) //Death is near... - emote("scream") if(!HAS_TRAIT(src, TRAIT_NOPAINSTUN)) - Unconscious(10 SECONDS) + Unconscious(5 SECONDS) endorphinate(TRUE) if((shock_stage >= SHOCK_STAGE_8) && (previous_shock_stage >= SHOCK_STAGE_8)) //How the fuck are we still alive? - if(!IsUnconscious()) - visible_message(span_bolddanger("[src] scrunchs [p_their()] and collapses!"), ignored_mobs = src) - custom_pain(span_animatedpain("OH LORD! The PAIN!"), 100, nopainloss = TRUE) + // if(!IsUnconscious()) + // visible_message(span_bolddanger("[src] scrunchs [p_their()] body and collapses!"), ignored_mobs = src) + // custom_pain(span_animatedpain("OH LORD! The PAIN!"), 100, nopainloss = TRUE) //death_rattle() if(!HAS_TRAIT(src, TRAIT_NOPAINSTUN)) Unconscious(15 SECONDS) diff --git a/code/modules/mob/living/carbon/damage_procs.dm b/code/modules/mob/living/carbon/damage_procs.dm index 94087cb4c20..105d61445fc 100644 --- a/code/modules/mob/living/carbon/damage_procs.dm +++ b/code/modules/mob/living/carbon/damage_procs.dm @@ -280,10 +280,6 @@ if(affecting && !affecting.can_feel_pain()) return FALSE - // Take the edge off - if(power - get_chem_effect(CE_PAINKILLER)/PAINKILLER_DIVISOR <= 0) - return FALSE - // Share the pain if(!nopainloss && power) if(affecting) @@ -291,26 +287,30 @@ else adjustPainLoss(CEILING(power, 1)) + // Take the edge off + power -= get_chem_effect(CE_PAINKILLER)/PAINKILLER_DIVISOR + if(power < PAIN_EMOTE_MINIMUM) + return FALSE + // Anti message spam checks - if(forced || (message != last_pain_message) || (world.time >= next_pain_message_time)) - last_pain_message = message + if(forced || world.time >= next_pain_message_time) if(world.time >= next_pain_message_time) to_chat(src, span_animatedpain("[message]")) - next_pain_message_time = world.time + (60 SECONDS + power) - - if(pain_emote && world.time >= next_pain_emote_time) - var/force_emote - if(ishuman(src)) - var/mob/living/carbon/human/human_src = src - if(human_src.dna?.species) - force_emote = human_src.dna.species.get_pain_emote(power) - if(force_emote && prob(power)) - INVOKE_ASYNC(src, PROC_REF(emote), force_emote) - next_pain_emote_time = world.time + (90 SECONDS + power) + next_pain_message_time = world.time + PAIN_MESSAGE_COOLDOWN + power + + if(pain_emote && world.time >= next_pain_emote_time) + var/force_emote + if(ishuman(src)) + var/mob/living/carbon/human/human_src = src + if(human_src.dna?.species) + force_emote = human_src.dna.species.get_pain_emote(power) + if(force_emote && prob(power)) + INVOKE_ASYNC(src, PROC_REF(emote), force_emote) + next_pain_emote_time = world.time + PAIN_EMOTE_COOLDOWN + power // Briefly flash the pain overlay //flash_pain(power) - next_pain_time = world.time + (rand(100, 150) + power) + next_pain_time = world.time + (rand(10 SECONDS, 15 SECONDS) + power) return TRUE /mob/living/carbon/can_feel_pain() diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index c61bc933114..7c593204dee 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -278,8 +278,6 @@ var/traumatic_shock = 0 /// Shock stage, as in how much our crit has progressed var/shock_stage = 0 - /// Last pain related message we have received - Used to prevent spam - var/last_pain_message = "" /// Next time we are able to trigger custom_pain() var/next_pain_time = 0 /// Next time we are able to send a custom_pain() chat message diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index 7d65d452e1a..b8e2c4cdad7 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -394,7 +394,7 @@ /// Return TRUE to get whatever mob this is in to update health. /obj/item/bodypart/proc/on_life(delta_time, times_fired) - if(pain_heal_tick && (pain_dam >= DAMAGE_PRECISION)) + if(pain_heal_tick) var/multiplier = 1 if(owner.body_position == LYING_DOWN) multiplier *= pain_heal_rest_multiplier @@ -570,9 +570,9 @@ // Slow healing var/heal_amt = injury.base_autoheal_amount if(!toxins && injury.can_autoheal()) - heal_amt += (GET_MOB_ATTRIBUTE_VALUE(owner, STAT_ENDURANCE) * 0.004) + heal_amt += max(GET_MOB_ATTRIBUTE_VALUE(owner, STAT_CONSTITUTION), 1) * 0.005 if(owner?.IsSleeping()) - heal_amt *= 2 + heal_amt *= 3 if(heal_amt) injury.heal_damage(heal_amt * delta_time) @@ -945,8 +945,8 @@ continue if(injury.damage_type & FIRE_WOUND_TYPES) burn = injury.heal_damage(burn) - else - brute = injury.heal_damage(brute) + else if(!forced || injury.damage_type & BRUTE_WOUND_TYPES) + brute = injury.heal_damage(brute) // serve as fallback healing amount for forced heal return post_damage_change(updating_health) diff --git a/code/modules/surgery/surgeries/organic_steps.dm b/code/modules/surgery/surgeries/organic_steps.dm index 30042e1b50d..06455dce0a1 100644 --- a/code/modules/surgery/surgeries/organic_steps.dm +++ b/code/modules/surgery/surgeries/organic_steps.dm @@ -28,7 +28,7 @@ "Blood pools around the incision in [target]'s [parse_zone(target_zone)].") var/obj/item/bodypart/gotten_part = target.get_bodypart(check_zone(target_zone)) if(gotten_part) - gotten_part.create_injury(WOUND_SLASH, BLEED_DAMAGE_RATIO, surgical = TRUE) + gotten_part.create_injury(WOUND_SLASH, BLEED_DAMAGE_RATIO/6, surgical = TRUE) return TRUE /// Clamping From ddfce79859c23e52a9f14e14b5605d79d1127fa7 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Sun, 17 May 2026 01:37:35 -0700 Subject: [PATCH 45/59] fixes --- code/datums/injury/_injury.dm | 2 -- code/game/objects/items/natural/cloth.dm | 4 +++- code/game/objects/structures/traps.dm | 4 ++-- code/modules/surgery/bodyparts/bodypart_examine.dm | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/code/datums/injury/_injury.dm b/code/datums/injury/_injury.dm index 939d0bde706..871af9b2437 100644 --- a/code/datums/injury/_injury.dm +++ b/code/datums/injury/_injury.dm @@ -179,8 +179,6 @@ return FALSE if(parent_bodypart.is_retracted()) return FALSE - if(is_surgical()) - return FALSE if(germ_level > INFECTION_LEVEL_ONE) return FALSE return ((damage_per_injury() <= autoheal_cutoff) ? TRUE : (is_treated() || parent_bodypart?.limb_flags & BODYPART_GOOD_HEALER)) diff --git a/code/game/objects/items/natural/cloth.dm b/code/game/objects/items/natural/cloth.dm index d3fc803c399..cae38194bc4 100644 --- a/code/game/objects/items/natural/cloth.dm +++ b/code/game/objects/items/natural/cloth.dm @@ -32,7 +32,7 @@ /obj/item/natural/cloth/examine(mob/user) . = ..() - . += span_notice("[src] has [PERCENT(bandage_health/initial(bandage_health))]% of its absorption left.") + . += span_notice("[src] is [PERCENT(bandage_health/initial(bandage_health))]% soaked in blood.") /obj/item/natural/cloth/Initialize(mapload, vol) . = ..() @@ -158,6 +158,8 @@ reagents.add_reagent(W.water_reagent, reagents.maximum_volume) user.visible_message(span_small("[user] soaks \the [src] in \the [T]."), span_small("I soak \the [src] in \the [T]."), vision_distance = 2) playsound(T, pick('sound/foley/waterwash (1).ogg','sound/foley/waterwash (2).ogg'), 25, FALSE) + bandage_health = initial(bandage_health) + bandage_effectiveness = initial(bandage_effectiveness) else var/datum/liquid_group/lg = T.liquids?.liquid_group if(!lg) diff --git a/code/game/objects/structures/traps.dm b/code/game/objects/structures/traps.dm index 9ecee4d073f..21ce23ee3df 100644 --- a/code/game/objects/structures/traps.dm +++ b/code/game/objects/structures/traps.dm @@ -185,7 +185,7 @@ var/obj/item/bodypart/part = victim.get_bodypart(prob(50) ? BODY_ZONE_L_LEG : BODY_ZONE_R_LEG) if(isnull(part)) part = victim.get_bodypart(BODY_ZONE_CHEST) - part?.create_injury(WOUND_PUNCTURE, 30, TRUE) + part?.create_injury(WOUND_PUNCTURE, 30) victim.emote("scream") post_triggered() @@ -246,7 +246,7 @@ var/obj/item/bodypart/part = victim.get_bodypart(prob(50) ? BODY_ZONE_L_LEG : BODY_ZONE_R_LEG) if(isnull(part)) part = victim.get_bodypart(BODY_ZONE_CHEST) - part?.create_injury(WOUND_SLASH, part?.max_damage * 0.4, TRUE) + part?.create_injury(WOUND_SLASH, part?.max_damage * 0.4) victim.emote("scream") /obj/structure/trap/wall_projectile diff --git a/code/modules/surgery/bodyparts/bodypart_examine.dm b/code/modules/surgery/bodyparts/bodypart_examine.dm index 32b64cb3bde..23330e4fc5b 100644 --- a/code/modules/surgery/bodyparts/bodypart_examine.dm +++ b/code/modules/surgery/bodyparts/bodypart_examine.dm @@ -236,7 +236,7 @@ if(get_cut(ignore_gauze = TRUE)) status += span_artery(uppertext("cut [parse_zone(possible_artery.zone)]")) else - status += span_bloody(uppertext("bruised [parse_zone(possible_artery.zone)]")) + status += span_bloody(uppertext("internal bleeding")) if(skeletonized) status += "SKELETON" From be548985bb915bafa9158a9b0af2ed591bf27958 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Sun, 17 May 2026 02:08:30 -0700 Subject: [PATCH 46/59] burn bleed --- code/datums/injury/_injury.dm | 2 +- code/datums/injury/organic/burn.dm | 9 ++++----- code/modules/mob/living/carbon/damage_procs.dm | 2 -- code/modules/mob/living/carbon/human/species.dm | 4 ++-- code/modules/surgery/bodyparts/bodypart_wounds.dm | 3 +++ 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/code/datums/injury/_injury.dm b/code/datums/injury/_injury.dm index 871af9b2437..5f14bd8001d 100644 --- a/code/datums/injury/_injury.dm +++ b/code/datums/injury/_injury.dm @@ -40,7 +40,7 @@ var/mob/living/carbon/parent_mob // ~these are defined by the injury type and should not be changed here - /// Stages such as "cut", "deep cut", etc. + /// Stages such as "cut", "deep cut", etc. Stages should be listed in decreasing order of severity var/list/stages /// Maximum stage at which bleeding should still happen - Beyond this stage bleeding is prevented var/max_bleeding_stage = 0 diff --git a/code/datums/injury/organic/burn.dm b/code/datums/injury/organic/burn.dm index dbeef495564..b95572ef92a 100644 --- a/code/datums/injury/organic/burn.dm +++ b/code/datums/injury/organic/burn.dm @@ -2,8 +2,9 @@ /datum/injury/burn damage_type = WOUND_BURN autoheal_cutoff = 6 - max_bleeding_stage = 0 + max_bleeding_stage = 1 infection_rate = 1.25 + bleed_rate = BLEED_DAMAGE_RATIO / 100 // burns generally don't bleed much /datum/injury/burn/infection_check() //anything less than a FUCK burn isn't infectable if treated properly @@ -27,13 +28,11 @@ return FALSE -/* /datum/injury/burn/apply_injury(our_damage, obj/item/bodypart/limb) . = ..() //Burn damage can cause fluid loss due to blistering and cook-off - if(limb.owner && (damage_per_injury() >= 5 || damage + limb.burn_dam >= 20)) - limb.owner.adjust_blood_volume(-CEILING(BLOOD_VOLUME_SURVIVE * damage/100, 1)) -*/ + if(limb.owner && (limb.burn_dam/limb.max_damage) >= 0.25) // medium burn damage + limb.owner.adjust_blood_volume(-CEILING(BLOOD_VOLUME_SURVIVE * damage_per_injury()/100, 1)) /* /datum/injury/burn/receive_damage(damage_received = 0, pain_received = 0, wounding_type = WOUND_BLUNT) diff --git a/code/modules/mob/living/carbon/damage_procs.dm b/code/modules/mob/living/carbon/damage_procs.dm index 105d61445fc..583064cf973 100644 --- a/code/modules/mob/living/carbon/damage_procs.dm +++ b/code/modules/mob/living/carbon/damage_procs.dm @@ -119,8 +119,6 @@ if(!forced && (status_flags & GODMODE)) return FALSE if(amount > 0) - if(getFireLoss() >= 800) - return //we are already capped no point take_overall_damage(0, amount, updating_health, required_status) else heal_overall_damage(0, abs(amount), required_status ? required_status : BODYPART_ORGANIC, updating_health) diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index 7227e89bdf5..dda1fbe1680 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -2156,8 +2156,8 @@ GLOBAL_LIST_EMPTY(roundstart_species) if(burn_damage > 0) var/final_damage = CLAMP(burn_damage * H.physiology.heat_mod, 0, CONFIG_GET(number/per_tick/max_fire_damage)) H.apply_damage(final_damage, BURN, spread_damage = TRUE, flashes = FALSE) - if(!H.has_smoke_protection()) - H.apply_damage(final_damage/4, OXY, flashes = FALSE) // Smoke inhalation + // if(!H.has_smoke_protection()) + // H.apply_damage(final_damage/4, OXY, flashes = FALSE) // Smoke inhalation if(H.stat < UNCONSCIOUS && prob(burn_damage * 10 / 4)) H.emote("pain") // Apply building heat debuffs diff --git a/code/modules/surgery/bodyparts/bodypart_wounds.dm b/code/modules/surgery/bodyparts/bodypart_wounds.dm index 294c8c37dd7..e72b3674621 100644 --- a/code/modules/surgery/bodyparts/bodypart_wounds.dm +++ b/code/modules/surgery/bodyparts/bodypart_wounds.dm @@ -142,6 +142,9 @@ for(var/obj/item/grabbing/grab in grabbedby) bleed_rate *= grab.bleed_suppressing bleed_rate = max(round(bleed_rate, 0.1), 0) + // switch(burn_dam/max_damage) + // if(0.75 to INFINITY) + // bleed_rate += 5 return bleed_rate /obj/item/bodypart/proc/skeletonized_mod(bclass) From 52c82db14ad66ccd81973f69c26ea5242722d46f Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Sun, 17 May 2026 10:56:22 -0700 Subject: [PATCH 47/59] heart crit fix --- code/datums/wounds/arteries.dm | 8 ++++++++ code/modules/mob/living/carbon/carbon_shock.dm | 2 +- .../mob/living/simple_animal/hostile/bosses/fishboss.dm | 2 ++ code/modules/surgery/organs/internal/artery/_artery.dm | 1 + 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/code/datums/wounds/arteries.dm b/code/datums/wounds/arteries.dm index 1bda16da58c..1266ab04e6b 100644 --- a/code/datums/wounds/arteries.dm +++ b/code/datums/wounds/arteries.dm @@ -12,6 +12,13 @@ aimed_intent_bonus = TRUE crit_message = "Blood sprays from %VICTIM's %BODYPART!" var/artery_type_override + viable_zones = list(\ + BODY_ZONE_R_ARM, \ + BODY_ZONE_R_LEG, \ + BODY_ZONE_PRECISE_MOUTH, \ + BODY_ZONE_L_LEG, \ + BODY_ZONE_L_ARM, \ + BODY_ZONE_HEAD) /datum/wound/artery/can_apply_to_bodypart(obj/item/bodypart/affected) if(affected.status == BODYPART_ROBOTIC) @@ -54,6 +61,7 @@ artery_type_override = /obj/item/organ/artery/neck can_roll = FALSE //snowflake used for neck slit show_in_book = FALSE + viable_zones = list(BODY_ZONE_PRECISE_NECK) /datum/wound/artery/heart name = "Aortic Dissection" diff --git a/code/modules/mob/living/carbon/carbon_shock.dm b/code/modules/mob/living/carbon/carbon_shock.dm index af0aecc9f5e..ec231c0cbb8 100644 --- a/code/modules/mob/living/carbon/carbon_shock.dm +++ b/code/modules/mob/living/carbon/carbon_shock.dm @@ -191,7 +191,7 @@ * Stage 4 begins mimicking "soft crit" */ if((shock_stage >= SHOCK_STAGE_4) && (previous_shock_stage < SHOCK_STAGE_4)) // Crossed stage 4 - emote("freezes and goes limp.", intentional = TRUE) + // emote("freezes and goes limp.", intentional = TRUE) if(!HAS_TRAIT(src, TRAIT_NOPAINSTUN)) Immobilize(rand(2, 3) SECONDS) diff --git a/code/modules/mob/living/simple_animal/hostile/bosses/fishboss.dm b/code/modules/mob/living/simple_animal/hostile/bosses/fishboss.dm index 10c6192d622..c8eca83a6ad 100644 --- a/code/modules/mob/living/simple_animal/hostile/bosses/fishboss.dm +++ b/code/modules/mob/living/simple_animal/hostile/bosses/fishboss.dm @@ -160,6 +160,8 @@ flag = "piercing" speed = 10 +/obj/projectile/bullet/reusable/deepone/handle_drop() + return /mob/living/simple_animal/hostile/boss/fishboss/death() visible_message("[src] convulses violently as eldritch energy pours from its wounds! The bloated, grotesque fishman explodes in a cataclysmic shower of gore and sea water!") diff --git a/code/modules/surgery/organs/internal/artery/_artery.dm b/code/modules/surgery/organs/internal/artery/_artery.dm index 5554f113242..8021b50557d 100644 --- a/code/modules/surgery/organs/internal/artery/_artery.dm +++ b/code/modules/surgery/organs/internal/artery/_artery.dm @@ -4,6 +4,7 @@ icon_state = "artery" base_icon_state = "artery" sellprice = 1 + dropshrink = 0.6 organ_flags = ORGAN_LIMB_SUPPORTER|ORGAN_INDESTRUCTIBLE|ORGAN_NO_VIOLENT_DAMAGE organ_efficiency = list(ORGAN_SLOT_ARTERY = 100) From e9bc798837747a575f74d039bae6ac2c02449957 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Sun, 17 May 2026 12:30:28 -0700 Subject: [PATCH 48/59] i hate thresholds --- code/modules/mob/living/carbon/carbon_shock.dm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/modules/mob/living/carbon/carbon_shock.dm b/code/modules/mob/living/carbon/carbon_shock.dm index ec231c0cbb8..18057bd86e0 100644 --- a/code/modules/mob/living/carbon/carbon_shock.dm +++ b/code/modules/mob/living/carbon/carbon_shock.dm @@ -193,7 +193,7 @@ if((shock_stage >= SHOCK_STAGE_4) && (previous_shock_stage < SHOCK_STAGE_4)) // Crossed stage 4 // emote("freezes and goes limp.", intentional = TRUE) if(!HAS_TRAIT(src, TRAIT_NOPAINSTUN)) - Immobilize(rand(2, 3) SECONDS) + Immobilize(0.5 SECONDS) if((shock_stage >= SHOCK_STAGE_4) && (previous_shock_stage >= SHOCK_STAGE_4)) if(DT_PROB(1, delta_time)) @@ -227,8 +227,8 @@ if(!IsUnconscious()) custom_pain("[pick("I feel like I could die at any moment now", "I'm about to lose consciousness")]!", shock_stage, nopainloss = TRUE) if(!HAS_TRAIT(src, TRAIT_NOPAINSTUN)) - emote("agony") - Immobilize(5 SECONDS) + // emote("agony") + Immobilize(rand(2, 3) SECONDS) if((shock_stage >= SHOCK_STAGE_7) && (previous_shock_stage >= SHOCK_STAGE_7)) if(DT_PROB(1, delta_time)) From f6b48acdac00dbe0e6e59e0998b74825fbd2f044 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Mon, 18 May 2026 11:50:16 -0700 Subject: [PATCH 49/59] fixes rous --- code/_globalvars/special_traits/traits.dm | 3 ++- code/datums/components/rotting.dm | 2 +- code/modules/antagonists/villain/lich/lich.dm | 16 ++++++++++------ .../villain/lich/spells/raise_undead.dm | 2 +- .../antagonists/villain/overlord/_antagonist.dm | 3 ++- code/modules/antagonists/villain/zomble.dm | 1 + code/modules/client/round_end_panel.dm | 2 +- code/modules/jobs/job_types/other/skeleton.dm | 4 ++-- code/modules/mob/living/carbon/human/human.dm | 2 -- .../carbon/human/npc/skeleton/_skeleton.dm | 3 ++- code/modules/mob/living/living.dm | 10 +++++----- code/modules/surgery/organs/internal/heart.dm | 2 +- code/modules/surgery/organs/internal/spleen.dm | 2 +- 13 files changed, 29 insertions(+), 23 deletions(-) diff --git a/code/_globalvars/special_traits/traits.dm b/code/_globalvars/special_traits/traits.dm index c266e5bec68..84c12150525 100644 --- a/code/_globalvars/special_traits/traits.dm +++ b/code/_globalvars/special_traits/traits.dm @@ -841,7 +841,7 @@ character.grant_undead_eyes() character.mob_biotypes |= MOB_UNDEAD - ADD_TRAIT(character, TRAIT_NOBLOOD, SPECIES_TRAIT) + character.dna?.species?.inherent_traits |= TRAIT_NOBLOOD character.dna?.species?.soundpack_m = new /datum/voicepack/skeleton() character.dna?.species?.soundpack_f = new /datum/voicepack/skeleton() @@ -854,6 +854,7 @@ ADD_TRAIT(character, TRAIT_TOXIMMUNE, BE_SPECIAL_TRAIT) ADD_TRAIT(character, TRAIT_NOSLEEP, BE_SPECIAL_TRAIT) ADD_TRAIT(character, TRAIT_SHOCKIMMUNE, BE_SPECIAL_TRAIT) + ADD_TRAIT(character, TRAIT_NOBLOOD, BE_SPECIAL_TRAIT) character.update_body() diff --git a/code/datums/components/rotting.dm b/code/datums/components/rotting.dm index 71d364889fc..b1eb061ec4c 100644 --- a/code/datums/components/rotting.dm +++ b/code/datums/components/rotting.dm @@ -80,7 +80,7 @@ if(amount > 45 MINUTES) if(!is_zombie) B.skeletonize() - ADD_TRAIT(C, TRAIT_NOBLOOD, SPECIES_TRAIT) + ADD_TRAIT(C, TRAIT_NOBLOOD, TRAIT_GENERIC) C.change_stat(STAT_CONSTITUTION, -99) shouldupdate = TRUE else diff --git a/code/modules/antagonists/villain/lich/lich.dm b/code/modules/antagonists/villain/lich/lich.dm index e553fc74e12..43c00c9f648 100644 --- a/code/modules/antagonists/villain/lich/lich.dm +++ b/code/modules/antagonists/villain/lich/lich.dm @@ -121,19 +121,23 @@ L.mana_pool.set_intrinsic_recharge(MANA_SOULS) L.mana_pool.ethereal_recharge_rate += 0.2 - L.cmode_music = 'sound/music/cmode/antag/CombatLich.ogg' - if(prob(10)) - L.cmode_music = 'sound/music/cmode/antag/combat_evilwizard.ogg' + ADD_TRAIT(L, TRAIT_NOBLOOD, TRAIT_GENERIC) + L.set_faction(FACTION_UNDEAD) - if(length(L.quirks)) - L.clear_quirks() L.mob_biotypes |= MOB_UNDEAD - ADD_TRAIT(L, TRAIT_NOBLOOD, SPECIES_TRAIT) L.grant_undead_eyes() + L.dna.species.inherent_traits |= TRAIT_NOBLOOD L.skeletonize(FALSE) + L.equipOutfit(/datum/outfit/lich) L.set_patron(/datum/patron/inhumen/zizo) + L.cmode_music = 'sound/music/cmode/antag/CombatLich.ogg' + if(prob(10)) + L.cmode_music = 'sound/music/cmode/antag/combat_evilwizard.ogg' + if(length(L.quirks)) + L.clear_quirks() + /datum/outfit/lich/pre_equip(mob/living/carbon/human/H) ..() head = /obj/item/clothing/head/helmet/skullcap/cult diff --git a/code/modules/antagonists/villain/lich/spells/raise_undead.dm b/code/modules/antagonists/villain/lich/spells/raise_undead.dm index 543626caa53..3974e89faa4 100644 --- a/code/modules/antagonists/villain/lich/spells/raise_undead.dm +++ b/code/modules/antagonists/villain/lich/spells/raise_undead.dm @@ -83,7 +83,6 @@ mind.current.job = null mind.add_antag_datum(/datum/antagonist/skeleton) - ADD_TRAIT(src, TRAIT_NOBLOOD, SPECIES_TRAIT) dna.species.soundpack_m = new /datum/voicepack/skeleton() dna.species.soundpack_f = new /datum/voicepack/skeleton() @@ -126,6 +125,7 @@ ADD_TRAIT(src, TRAIT_NOSLEEP, TRAIT_GENERIC) ADD_TRAIT(src, TRAIT_SHOCKIMMUNE, TRAIT_GENERIC) ADD_TRAIT(src, TRAIT_CRITICAL_WEAKNESS, TRAIT_GENERIC) + ADD_TRAIT(src, TRAIT_NOBLOOD, TRAIT_GENERIC) update_body() diff --git a/code/modules/antagonists/villain/overlord/_antagonist.dm b/code/modules/antagonists/villain/overlord/_antagonist.dm index 578faa614fb..5504b63a45f 100644 --- a/code/modules/antagonists/villain/overlord/_antagonist.dm +++ b/code/modules/antagonists/villain/overlord/_antagonist.dm @@ -116,11 +116,12 @@ if(length(L.quirks)) L.clear_quirks() L.mob_biotypes |= MOB_UNDEAD - ADD_TRAIT(L, TRAIT_NOBLOOD, SPECIES_TRAIT) + L.dna.species.inherent_traits |= TRAIT_NOBLOOD L.grant_undead_eyes() L.skeletonize(FALSE) L.equipOutfit(/datum/outfit/overlord) L.set_patron(/datum/patron/inhumen/zizo) + ADD_TRAIT(L, TRAIT_NOBLOOD, SPECIES_TRAIT) /datum/antagonist/overlord/proc/on_death(datum/source) SIGNAL_HANDLER diff --git a/code/modules/antagonists/villain/zomble.dm b/code/modules/antagonists/villain/zomble.dm index 71225614650..723ceaf5014 100644 --- a/code/modules/antagonists/villain/zomble.dm +++ b/code/modules/antagonists/villain/zomble.dm @@ -120,6 +120,7 @@ zombie.cure_all_traumas(TRAUMA_RESILIENCE_ABSOLUTE) for(var/obj/item/organ/organ as anything in zombie.internal_organs) organ.setOrganDamage(0) + zombie.update_eyes() return ..() /datum/antagonist/zombie/on_removal() diff --git a/code/modules/client/round_end_panel.dm b/code/modules/client/round_end_panel.dm index d13b3aac6f9..2f4e8f45cf9 100644 --- a/code/modules/client/round_end_panel.dm +++ b/code/modules/client/round_end_panel.dm @@ -132,7 +132,7 @@ data += "Noble Deaths: [GLOB.vanderlin_round_stats[STATS_NOBLE_DEATHS]]
" data += "Holy Revivals: [GLOB.vanderlin_round_stats[STATS_ASTRATA_REVIVALS]]
" data += "Lux Revivals: [GLOB.vanderlin_round_stats[STATS_LUX_REVIVALS]]
" - data += "CPR Revivals: [GLOB.vanderlin_round_stats[STATS_CPR_REVIVALS]]
" + data += "Successful CPRs: [GLOB.vanderlin_round_stats[STATS_CPR_REVIVALS]]
" data += "Wounds sewed: [GLOB.vanderlin_round_stats[STATS_WOUNDS_SEWED]]
" data += "Moat Fallers: [GLOB.vanderlin_round_stats[STATS_MOAT_FALLERS]]
" data += "Ankles Broken: [GLOB.vanderlin_round_stats[STATS_ANKLES_BROKEN]]
" diff --git a/code/modules/jobs/job_types/other/skeleton.dm b/code/modules/jobs/job_types/other/skeleton.dm index 32fa50746a5..6045e50ace6 100644 --- a/code/modules/jobs/job_types/other/skeleton.dm +++ b/code/modules/jobs/job_types/other/skeleton.dm @@ -35,7 +35,8 @@ TRAIT_NOPAIN, TRAIT_TOXIMMUNE, TRAIT_NOSLEEP, - TRAIT_SHOCKIMMUNE + TRAIT_SHOCKIMMUNE, + TRAIT_NOBLOOD, ) @@ -45,7 +46,6 @@ spawned.mind.special_role = "Skeleton" spawned.mind?.current.job = null - ADD_TRAIT(spawned, TRAIT_NOBLOOD, SPECIES_TRAIT) if(spawned.dna && spawned.dna.species) spawned.dna.species.soundpack_m = new /datum/voicepack/skeleton() spawned.dna.species.soundpack_f = new /datum/voicepack/skeleton() diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index c7a393a4ed8..c150e4bbdb2 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -448,8 +448,6 @@ add_abstract_elastic_data(ELASCAT_MEDICAL, ELASDATA_CPR_REVIVE, 1) target.apply_status_effect(/datum/status_effect/debuff/revive) record_round_statistic(STATS_CPR_REVIVALS, 1) - else - to_chat(src, span_warning("[target] isn't responding to my resuscitation...")) /mob/living/carbon/human/cuff_resist(obj/item/I, breakouttime = 1 MINUTES, cuff_break = 0, instant = FALSE) if(..()) diff --git a/code/modules/mob/living/carbon/human/npc/skeleton/_skeleton.dm b/code/modules/mob/living/carbon/human/npc/skeleton/_skeleton.dm index 6eb5ee3ab34..cbeb2955c89 100644 --- a/code/modules/mob/living/carbon/human/npc/skeleton/_skeleton.dm +++ b/code/modules/mob/living/carbon/human/npc/skeleton/_skeleton.dm @@ -49,8 +49,8 @@ faction |= "islander" if(length(quirks)) clear_quirks() - ADD_TRAIT(src, TRAIT_NOBLOOD, SPECIES_TRAIT) if(dna?.species) + dna.species.inherent_traits |= TRAIT_NOBLOOD dna.species.soundpack_m = new /datum/voicepack/skeleton() dna.species.soundpack_f = new /datum/voicepack/skeleton() var/obj/item/bodypart/head/headdy = get_bodypart("head") @@ -72,6 +72,7 @@ ADD_TRAIT(src, TRAIT_LIMBATTACHMENT, TRAIT_GENERIC) ADD_TRAIT(src, TRAIT_CRITICAL_WEAKNESS, TRAIT_GENERIC) ADD_TRAIT(src, TRAIT_NO_ORGAN_PROCESS, TRAIT_GENERIC) + ADD_TRAIT(src, TRAIT_NOBLOOD, TRAIT_GENERIC) if(skel_outfit) var/datum/outfit/OU = new skel_outfit if(OU) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 8b59a1297f5..578e7bd7abb 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1011,8 +1011,8 @@ set_stat(UNCONSCIOUS) //the mob starts unconscious, timeofdeath = 0 updatehealth() //then we check if the mob should wake up. - // if(full_heal_flags & HEAL_ADMIN) - // get_up(TRUE) + if(full_heal_flags & HEAL_ADMIN) + get_up(TRUE) update_sight() clear_alert("not_enough_oxy") reload_fullscreen() @@ -1030,9 +1030,9 @@ INVOKE_ASYNC(src, PROC_REF(emote), "breathgasp") log_combat(src, src, "revived") - // else if(full_heal_flags & HEAL_ADMIN) - // updatehealth() - // get_up(TRUE) + else if(full_heal_flags & HEAL_ADMIN) + updatehealth() + get_up(TRUE) // The signal is called after everything else so components can properly check the updated values SEND_SIGNAL(src, COMSIG_LIVING_REVIVE, full_heal_flags) diff --git a/code/modules/surgery/organs/internal/heart.dm b/code/modules/surgery/organs/internal/heart.dm index 7f97cf013d9..36cc083e172 100644 --- a/code/modules/surgery/organs/internal/heart.dm +++ b/code/modules/surgery/organs/internal/heart.dm @@ -150,7 +150,7 @@ return TRUE /obj/item/organ/heart/get_availability(datum/species/S, mob/living/carbon/owner_mob) - return (CAN_HAVE_BLOOD(owner_mob) && !(TRAIT_STABLEHEART in S.inherent_traits)) + return (!(TRAIT_NOBLOOD in S.inherent_traits) && !(TRAIT_STABLEHEART in S.inherent_traits)) /obj/item/organ/heart/get_mechanics_examine(mob/user) . = ..() diff --git a/code/modules/surgery/organs/internal/spleen.dm b/code/modules/surgery/organs/internal/spleen.dm index 59d4a37aca2..c24ed85b02b 100644 --- a/code/modules/surgery/organs/internal/spleen.dm +++ b/code/modules/surgery/organs/internal/spleen.dm @@ -34,4 +34,4 @@ examine_list += span_notice("[owner] looks a little wan.") /obj/item/organ/spleen/get_availability(datum/species/S, mob/living/carbon/owner_mob) - return CAN_HAVE_BLOOD(owner_mob) + return !(TRAIT_NOBLOOD in S.inherent_traits) From 4f6e7ba1cd535e6e171ce19c4aa76ec10eb5fc89 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Mon, 18 May 2026 14:33:25 -0700 Subject: [PATCH 50/59] death to cpr --- code/__DEFINES/medical.dm | 8 - .../mob/living/carbon/carbon_defines.dm | 4 - code/modules/mob/living/carbon/human/human.dm | 174 ++++++++++-------- .../surgery/bodyparts/bodypart_wounds.dm | 2 - code/modules/surgery/organs/_organ.dm | 8 +- .../surgery/organs/internal/artery/_artery.dm | 2 +- code/modules/surgery/organs/internal/brain.dm | 6 +- 7 files changed, 106 insertions(+), 98 deletions(-) diff --git a/code/__DEFINES/medical.dm b/code/__DEFINES/medical.dm index ea064875ea6..cd24908c11f 100644 --- a/code/__DEFINES/medical.dm +++ b/code/__DEFINES/medical.dm @@ -281,14 +281,6 @@ DEFINE_BITFIELD(organ_flags, list( #define CPR_MOUTH "m2m" #define CPR_CHEST "cardio" -/// Mouth to mouth cooldown duration -#define M2M_COOLDOWN 0.3 SECONDS -///Cpr cooldown duration -#define CPR_COOLDOWN 0.3 SECONDS - -#define CPR_TIME 4 SECONDS -#define M2M_TIME 0.5 SECONDS - // ~simple brainloss defines #define GETBRAINLOSS(mob) mob.getOrganLoss(ORGAN_SLOT_BRAIN) #define ADJUSTBRAINLOSS(mob, amount) mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, amount) diff --git a/code/modules/mob/living/carbon/carbon_defines.dm b/code/modules/mob/living/carbon/carbon_defines.dm index d7608b7a7a5..7e3daf0da46 100644 --- a/code/modules/mob/living/carbon/carbon_defines.dm +++ b/code/modules/mob/living/carbon/carbon_defines.dm @@ -16,10 +16,6 @@ var/obj/item/legcuffed = null //Same as handcuffs but for legs. Bear traps use this. COOLDOWN_DECLARE(adrenaline_burst) - /// Last time we got mouth to mouthed - COOLDOWN_DECLARE(last_mtom) - /// Last time we got CPR'd - COOLDOWN_DECLARE(last_cpr) /// Pulse can't be handled on an organ-by-organ basis, since we can have multiple hearts var/pulse = PULSE_NORM diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index c150e4bbdb2..e3f5fba1472 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -324,7 +324,7 @@ var/obj/item/bodypart/mouth/jaw = target.get_bodypart(BODY_ZONE_PRECISE_MOUTH) var/obj/item/bodypart/chest/chest = target.get_bodypart(BODY_ZONE_CHEST) - var/medical_skill = GET_MOB_SKILL_VALUE(src, /datum/attribute/skill/misc/medicine) + var/medical_skill = max(GET_MOB_SKILL_VALUE(src, /datum/attribute/skill/misc/medicine), 0) if(DOING_INTERACTION_WITH_TARGET(src, target)) return FALSE @@ -352,28 +352,27 @@ to_chat(src, span_warning("I have no lungs!")) return FALSE - if(world.time >= target.last_mtom + M2M_COOLDOWN) - if(!do_after(src, M2M_TIME, target)) - return - var/they_breathe = !HAS_TRAIT(target, TRAIT_NOBREATH) - var/obj/item/organ/lungs/they_lung = target.getorganslot(ORGAN_SLOT_LUNGS) - visible_message(span_notice("[src] performs mouth to mouth on [target]!"), \ - span_notice("I perform mouth to mouth on [target]."), - span_hear("I hear loud breathing."), - vision_distance = COMBAT_MESSAGE_RANGE, - ignored_mobs = target) - target.last_mtom = world.time - log_combat(src, target, "M2Med") - if(they_breathe && they_lung) - var/epinephrine_mod = 0 - if(target.reagents?.get_reagent_amount(/datum/reagent/adrenaline) >= 1) - epinephrine_mod += 5 - target.adjustOxyLoss(-((medical_skill * 0.2) + epinephrine_mod)) - to_chat(target, span_unconscious("I feel a breath of fresh air enter my lungs... It feels good...")) - else if(they_breathe && !they_lung) - to_chat(target, span_unconscious("I feel a breath of fresh air... But i don't feel any better...")) - else - to_chat(target, span_unconscious("I feel a breath of fresh air... Which is a sensation i don't recognise...")) + if(!do_after(src, 3 SECONDS, target)) + return + var/they_breathe = !HAS_TRAIT(target, TRAIT_NOBREATH) + var/obj/item/organ/lungs/they_lung = target.getorganslot(ORGAN_SLOT_LUNGS) + visible_message(span_notice("[src] performs mouth to mouth on [target]!"), \ + span_notice("I perform mouth to mouth on [target]."), + span_hear("I hear loud breathing."), + vision_distance = COMBAT_MESSAGE_RANGE, + ignored_mobs = target) + log_combat(src, target, "M2Med") + if(they_breathe && they_lung) + var/epinephrine_mod = 0 + if(target.reagents?.get_reagent_amount(/datum/reagent/adrenaline) >= 1) + epinephrine_mod += 5 + target.adjustOxyLoss(-((medical_skill * 0.2) + epinephrine_mod)) + to_chat(target, span_unconscious("I feel a breath of fresh air enter my lungs... It feels good...")) + else if(they_breathe && !they_lung) + to_chat(target, span_unconscious("I feel a breath of fresh air... But i don't feel any better...")) + else + to_chat(target, span_unconscious("I feel a breath of fresh air... Which is a sensation i don't recognise...")) + if(CPR_CHEST) var/mob/living/carbon/human/humie = target if(istype(humie)) @@ -386,68 +385,83 @@ to_chat(src, span_warning("I need to take [humie.p_their()] [suit] off!")) return - if(world.time >= target.last_cpr + CPR_COOLDOWN) - var/compression_time = CPR_TIME - compression_time *= GENERAL_SKILL_TIME_MULITPLIER(src, /datum/attribute/skill/misc/medicine) - if(!do_after(src, min(compression_time, 4 SECONDS), target)) - return - var/they_beat = !HAS_TRAIT(target, TRAIT_STABLEHEART) - var/obj/item/organ/heart/they_heart = target.getorganslot(ORGAN_SLOT_HEART) - target.last_cpr = world.time - log_combat(src, target, "CPRed") - if(they_beat && they_heart) - to_chat(target, span_unconscious("I feel my heart being pumped...")) - else if(they_beat && !they_heart) - to_chat(target, span_unconscious("I feel my chest being pumped... But i don't feel any better...")) - else - to_chat(target, span_unconscious("I feel my chest being pushed on...")) - var/epinephrine_mod = 0 - if(target.reagents?.get_reagent_amount(/datum/reagent/adrenaline) >= 1) - epinephrine_mod += 3 - var/heart_exposed_mod = 0 - if(CHECK_MULTIPLE_BITFIELDS(chest.get_surgery_flags(), SURGERY_INCISED|SURGERY_RETRACTED|SURGERY_BROKEN) && istype(they_heart)) - heart_exposed_mod += 5 - - /// Journeymen (average 35) have a 5% chance of doing a CPR revive - var/diceroll = diceroll(medical_skill+heart_exposed_mod+epinephrine_mod, dice_num = 13, context = DICE_CONTEXT_PHYSICAL) - if(diceroll <= DICE_CRIT_FAILURE) - visible_message(span_danger("[src] botches the chest compressions!"), \ - span_danger("I botch the chest compressions!"), + var/compression_time = 10 SECONDS + compression_time *= 1 - max(medical_skill * 0.01, 0.4) + if(!do_after(src, compression_time, target)) + return + var/they_beat = !HAS_TRAIT(target, TRAIT_STABLEHEART) + var/obj/item/organ/heart/they_heart = target.getorganslot(ORGAN_SLOT_HEART) + log_combat(src, target, "CPRed") + if(they_beat && they_heart) + to_chat(target, span_unconscious("I feel my heart being pumped...")) + else if(they_beat && !they_heart) + to_chat(target, span_unconscious("I feel my chest being pumped... But i don't feel any better...")) + else + to_chat(target, span_unconscious("I feel my chest being pushed on...")) + var/epinephrine_mod = 0 + if(target.reagents?.get_reagent_amount(/datum/reagent/adrenaline) >= 1) + epinephrine_mod += 3 + var/heart_exposed_mod = 0 + if(CHECK_MULTIPLE_BITFIELDS(chest.get_surgery_flags(), SURGERY_INCISED|SURGERY_RETRACTED|SURGERY_BROKEN) && istype(they_heart)) + heart_exposed_mod += 5 + + /// Journeymen (average 35) have a 5% chance of doing a CPR revive + var/diceroll = diceroll(medical_skill+heart_exposed_mod+epinephrine_mod, dice_num = 13, context = DICE_CONTEXT_PHYSICAL) + if(diceroll <= DICE_CRIT_FAILURE) + visible_message(span_danger("[src] botches the chest compressions!"), \ + span_danger("I botch the chest compressions!"), + span_hear("I hear pushing."), + vision_distance = COMBAT_MESSAGE_RANGE, \ + ignored_mobs = target) + if(target.stat >= DEAD) + /** + * y = 15 * e^(-0.022x) + * Aiming for points (0, 15) (30, 10) (50, 5) + */ + they_heart.applyOrganDamage(15 * (NUM_E ** (-0.022 * medical_skill)), they_heart.high_threshold) + else + if(heart_exposed_mod) + visible_message(span_notice("[src] massages [target]'s [they_heart]!"), \ + span_notice("I massage [target]'s [they_heart]."), \ span_hear("I hear pushing."), vision_distance = COMBAT_MESSAGE_RANGE, \ ignored_mobs = target) else - if(heart_exposed_mod) - visible_message(span_notice("[src] massages [target]'s [they_heart]!"), \ - span_notice("I massage [target]'s [they_heart]."), \ - span_hear("I hear pushing."), - vision_distance = COMBAT_MESSAGE_RANGE, \ - ignored_mobs = target) + visible_message(span_notice("[src] performs chest compressions on [target]!"), \ + span_notice("I perform chest compressions on [target]."), \ + span_hear("I hear pushing."), + vision_distance = COMBAT_MESSAGE_RANGE, \ + ignored_mobs = target) + target.pump_heart(src) + if(target.stat < DEAD) // No point in running the revive check + return + if(HAS_TRAIT(target, TRAIT_NECRA_CURSE)) + to_chat(src, span_warning("Necra holds tight to this one.")) + return FALSE + chest.add_wound(/datum/wound/fracture/chest) + if((diceroll >= DICE_SUCCESS) || (!attributes && prob(35))) + if(they_heart.is_failing_without_bleedout()) + to_chat(src, span_warning("[target] isn't responding to my resuscitation...")) + return FALSE + + if(GETBRAINLOSS(target) >= BRAIN_DAMAGE_DEATH) + SETBRAINLOSS(target, BRAIN_DAMAGE_DEATH - 1) + if(target.revive()) + target.grab_ghost(TRUE) + target.visible_message(span_warning("[target] limply spasms their muscles."), \ + span_userdanger("My muscles spasm as i am brought back to life!")) + target.emote("breathgasp") + target.adjust_jitter(100 SECONDS) + add_abstract_elastic_data(ELASCAT_MEDICAL, ELASDATA_CPR_REVIVE, 1) + target.apply_status_effect(/datum/status_effect/debuff/revive) + record_round_statistic(STATS_CPR_REVIVALS, 1) + /** + * y = 15 * e^(-0.022x) + * Aiming for points (0, 15) (30, 10) (50, 5) + */ + they_heart.applyOrganDamage(15 * (NUM_E ** (-0.022 * medical_skill)), they_heart.high_threshold) else - visible_message(span_notice("[src] performs chest compressions on [target]!"), \ - span_notice("I perform chest compressions on [target]."), \ - span_hear("I hear pushing."), - vision_distance = COMBAT_MESSAGE_RANGE, \ - ignored_mobs = target) - target.pump_heart(src) - if(target.stat < DEAD) // No point in running the revive check - return - if((diceroll >= DICE_SUCCESS) || (!attributes && prob(35))) - if(HAS_TRAIT(target, TRAIT_NECRA_CURSE)) - to_chat(src, span_warning("Necra holds tight to this one.")) - return FALSE - if(GETBRAINLOSS(target) >= BRAIN_DAMAGE_DEATH) - SETBRAINLOSS(target, BRAIN_DAMAGE_DEATH - 1) - if(target.revive()) - chest.add_wound(/datum/wound/fracture/chest) - target.grab_ghost(TRUE) - target.visible_message(span_warning("[target] limply spasms their muscles."), \ - span_userdanger("My muscles spasm as i am brought back to life!")) - target.emote("breathgasp") - target.adjust_jitter(100 SECONDS) - add_abstract_elastic_data(ELASCAT_MEDICAL, ELASDATA_CPR_REVIVE, 1) - target.apply_status_effect(/datum/status_effect/debuff/revive) - record_round_statistic(STATS_CPR_REVIVALS, 1) + to_chat(src, span_warning("[target] isn't responding to my resuscitation...")) /mob/living/carbon/human/cuff_resist(obj/item/I, breakouttime = 1 MINUTES, cuff_break = 0, instant = FALSE) if(..()) diff --git a/code/modules/surgery/bodyparts/bodypart_wounds.dm b/code/modules/surgery/bodyparts/bodypart_wounds.dm index e72b3674621..c922321f770 100644 --- a/code/modules/surgery/bodyparts/bodypart_wounds.dm +++ b/code/modules/surgery/bodyparts/bodypart_wounds.dm @@ -90,8 +90,6 @@ else if(!wound.can_apply_to_bodypart(src)) qdel(wound) return - if(!(body_zone in wound.viable_zones)) - stack_trace("Call to add_wound added [wound] to [src] that isn't part of its viable zones!") if(!wound.apply_to_bodypart(src, silent, crit_message)) qdel(wound) return diff --git a/code/modules/surgery/organs/_organ.dm b/code/modules/surgery/organs/_organ.dm index c8decbf9607..9d7752431a0 100644 --- a/code/modules/surgery/organs/_organ.dm +++ b/code/modules/surgery/organs/_organ.dm @@ -510,7 +510,7 @@ failure_time = max(0, failure_time - delta_time) // Damage decrements by a percent of maxhealth - if(can_heal(delta_time, times_fired) && damage) + if(can_self_heal(delta_time, times_fired) && damage) handle_self_healing(delta_time, times_fired) ///Organs don't die instantly, and neither should you when you get fucked up @@ -522,14 +522,16 @@ organ_failure(delta_time) /// healing checks -/obj/item/organ/proc/can_heal(delta_time, times_fired) +/obj/item/organ/proc/can_self_heal(delta_time, times_fired) . = TRUE if(!owner) return FALSE if(healing_factor <= 0) return FALSE + if(owner.get_chem_effect(CE_ORGAN_REGEN)) return TRUE + if(is_dead()) return FALSE if(current_blood <= 0) @@ -538,6 +540,8 @@ return FALSE if(owner.get_chem_effect(CE_TOXIN)) return FALSE + if(owner.stat >= DEAD) + return FALSE /obj/item/organ/proc/handle_self_healing(delta_time, times_fired) if(damage <= 0) diff --git a/code/modules/surgery/organs/internal/artery/_artery.dm b/code/modules/surgery/organs/internal/artery/_artery.dm index 8021b50557d..83879dbe586 100644 --- a/code/modules/surgery/organs/internal/artery/_artery.dm +++ b/code/modules/surgery/organs/internal/artery/_artery.dm @@ -31,7 +31,7 @@ ///squirting sound var/squirt_sound = list('sound/gore/artery1.ogg', 'sound/gore/artery2.ogg', 'sound/gore/artery3.ogg') -/obj/item/organ/artery/can_heal(delta_time, times_fired) +/obj/item/organ/artery/can_self_heal(delta_time, times_fired) return FALSE /obj/item/organ/artery/on_life(delta_time, times_fired) diff --git a/code/modules/surgery/organs/internal/brain.dm b/code/modules/surgery/organs/internal/brain.dm index 6ce193d2df3..65912a02be1 100644 --- a/code/modules/surgery/organs/internal/brain.dm +++ b/code/modules/surgery/organs/internal/brain.dm @@ -299,14 +299,16 @@ QDEL_LIST(traumas) return ..() -/obj/item/organ/brain/can_heal(delta_time, times_fired) +/obj/item/organ/brain/can_self_heal(delta_time, times_fired) . = TRUE if(!owner) return FALSE if(healing_factor <= 0) return FALSE + if(owner.get_chem_effect(CE_BRAIN_REGEN)) return TRUE + if(is_dead()) return FALSE if(current_blood <= 0) @@ -316,6 +318,8 @@ var/effective_blood_oxygenation = GET_EFFECTIVE_BLOOD_VOL(owner.get_blood_oxygenation(), owner.total_blood_req) if(effective_blood_oxygenation < BLOOD_VOLUME_SAFE) return FALSE + if(owner.stat >= DEAD) + return FALSE /obj/item/organ/brain/proc/past_damage_threshold(threshold) return (get_current_damage_threshold() > threshold) From aaa47c7851559b76bdfabd59a8b27ac03531397d Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Mon, 18 May 2026 15:28:37 -0700 Subject: [PATCH 51/59] fix the boat please --- code/__HELPERS/_lists.dm | 24 +++++++++---------- .../trams_and_elevators/industrial_lift.dm | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm index d3eb4dd1003..330ab29b72f 100644 --- a/code/__HELPERS/_lists.dm +++ b/code/__HELPERS/_lists.dm @@ -267,28 +267,28 @@ if (length(L) < I) { \ L -= V //No return here so that it removes all strings of that type return -//returns a new list with only atoms that are in typecache L +///returns a new list with only atoms that are in the typecache list /proc/typecache_filter_list(list/atoms, list/typecache) RETURN_TYPE(/list) . = list() - for(var/atom/A as anything in atoms) - if(isnull(A)) - continue - if (typecache[A.type]) - . += A + for(var/atom/atom_checked as anything in atoms) + if (typecache[atom_checked.type]) + . += atom_checked +///return a new list with atoms that are not in the typecache list /proc/typecache_filter_list_reverse(list/atoms, list/typecache) RETURN_TYPE(/list) . = list() - for(var/atom/A as anything in atoms) - if(!typecache[A.type]) - . += A + for(var/atom/atom_checked as anything in atoms) + if(!typecache[atom_checked.type]) + . += atom_checked +///similar to typecache_filter_list and typecache_filter_list_reverse but it supports an inclusion list and and exclusion list /proc/typecache_filter_multi_list_exclusion(list/atoms, list/typecache_include, list/typecache_exclude) . = list() - for(var/atom/A as anything in atoms) - if(typecache_include[A.type] && !typecache_exclude[A.type]) - . += A + for(var/atom/atom_checked as anything in atoms) + if(typecache_include[atom_checked.type] && !typecache_exclude[atom_checked.type]) + . += atom_checked //Like typesof() or subtypesof(), but returns a typecache instead of a list /proc/typecacheof(path, ignore_root_path, only_root_path = FALSE) diff --git a/code/game/machinery/trams_and_elevators/industrial_lift.dm b/code/game/machinery/trams_and_elevators/industrial_lift.dm index 9275be48582..3475ee3298a 100644 --- a/code/game/machinery/trams_and_elevators/industrial_lift.dm +++ b/code/game/machinery/trams_and_elevators/industrial_lift.dm @@ -145,7 +145,7 @@ GLOBAL_LIST_INIT(all_radial_directions, list( /obj/structure/industrial_lift/proc/AddItemOnLift(datum/source, atom/movable/new_lift_contents) SIGNAL_HANDLER - var/static/list/blacklisted_types = typecacheof(list(/obj/effect/decal/cleanable, /atom/movable/outdoor_effect, /obj/structure/industrial_lift, /mob/camera, /atom/movable/lighting_object)) + var/static/list/blacklisted_types = typecacheof(list(/obj/effect, /atom/movable/outdoor_effect, /obj/structure/industrial_lift, /mob/camera, /atom/movable/lighting_object)) - typecacheof(list(/obj/effect/decal, /obj/effect/turf_decal)) if(is_type_in_typecache(new_lift_contents, blacklisted_types) || new_lift_contents.invisibility == INVISIBILITY_ABSTRACT) //prevents the tram from stealing things like landmarks return FALSE if(new_lift_contents in lift_load) From 021ee1ca9da96493e129fe52e0375e53ff083fb9 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Mon, 18 May 2026 22:11:06 -0700 Subject: [PATCH 52/59] cpr rework --- code/__DEFINES/footsteps.dm | 10 +- code/datums/injury/organic/bruise.dm | 2 +- code/game/objects/items/natural/bandage.dm | 2 +- .../mob/living/carbon/carbon_medical.dm | 2 +- code/modules/mob/living/carbon/human/human.dm | 267 ++++++++++-------- .../chemistry/reagents/other_reagents.dm | 2 +- 6 files changed, 154 insertions(+), 131 deletions(-) diff --git a/code/__DEFINES/footsteps.dm b/code/__DEFINES/footsteps.dm index 1bc91ba27cc..ca36753770c 100644 --- a/code/__DEFINES/footsteps.dm +++ b/code/__DEFINES/footsteps.dm @@ -93,7 +93,7 @@ GLOBAL_LIST_INIT(footstep, list( 'sound/foley/footsteps/FTGRA_A3.ogg', 'sound/foley/footsteps/FTGRA_A4.ogg'), 15, 0), FOOTSTEP_WATER = list(list( - 'sound/foley/waterenter.ogg'), 50, FALSE), + 'sound/foley/waterenter.ogg'), 40, FALSE), FOOTSTEP_SHALLOW = list(list( 'sound/foley/watermove (1).ogg', 'sound/foley/watermove (2).ogg'), 100, FALSE), @@ -129,7 +129,7 @@ GLOBAL_LIST_INIT(barefootstep, list( 'sound/foley/footsteps/softbarefoot (2).ogg', 'sound/foley/footsteps/softbarefoot (3).ogg'), 50, 0), FOOTSTEP_WATER = list(list( - 'sound/foley/waterenter.ogg'), 50, FALSE), + 'sound/foley/waterenter.ogg'), 40, FALSE), FOOTSTEP_SHALLOW = list(list( 'sound/foley/watermove (1).ogg', 'sound/foley/watermove (2).ogg'), 100, FALSE), @@ -164,7 +164,7 @@ GLOBAL_LIST_INIT(clawfootstep, list( FOOTSTEP_GRASS = list(list( 'sound/blank.ogg'), 25, 0), FOOTSTEP_WATER = list(list( - 'sound/foley/waterenter.ogg'), 50, FALSE), + 'sound/foley/waterenter.ogg'), 40, FALSE), FOOTSTEP_SHALLOW = list(list( 'sound/foley/watermove (1).ogg', 'sound/foley/watermove (2).ogg'), 100, FALSE), @@ -180,7 +180,7 @@ GLOBAL_LIST_INIT(heavyfootstep, list( 'sound/foley/footsteps/bigwalk (3).ogg', 'sound/foley/footsteps/bigwalk (4).ogg'), 100, 0), FOOTSTEP_WATER = list(list( - 'sound/foley/waterenter.ogg'), 50, FALSE), + 'sound/foley/waterenter.ogg'), 40, FALSE), FOOTSTEP_SHALLOW = list(list( 'sound/foley/watermove (1).ogg', 'sound/foley/watermove (2).ogg'), 100, FALSE), @@ -200,7 +200,7 @@ GLOBAL_LIST_INIT(metalfootstep, list( 'sound/foley/footsteps/armor/powerarmor (2).ogg', 'sound/foley/footsteps/armor/powerarmor (3).ogg',), 100, 0), FOOTSTEP_WATER = list(list( - 'sound/foley/waterenter.ogg'), 50, FALSE), + 'sound/foley/waterenter.ogg'), 40, FALSE), FOOTSTEP_SHALLOW = list(list( 'sound/foley/watermove (1).ogg', 'sound/foley/watermove (2).ogg'), 100, FALSE), diff --git a/code/datums/injury/organic/bruise.dm b/code/datums/injury/organic/bruise.dm index f68af65a4a8..af91f5bbfbc 100644 --- a/code/datums/injury/organic/bruise.dm +++ b/code/datums/injury/organic/bruise.dm @@ -3,7 +3,7 @@ bleed_threshold = 20 autoheal_cutoff = 30 damage_type = WOUND_BLUNT - bleed_rate = 0.9 + bleed_rate = 0.8 /datum/injury/bruise/small stages = list( diff --git a/code/game/objects/items/natural/bandage.dm b/code/game/objects/items/natural/bandage.dm index c864a71e2fd..a473ba477e4 100644 --- a/code/game/objects/items/natural/bandage.dm +++ b/code/game/objects/items/natural/bandage.dm @@ -5,7 +5,7 @@ desc = "A fabric treated and specially made to help with bleeding wounds. Better and faster at stopping bleeding than your regular piece of cloth." bundletype = /obj/item/natural/bundle/cloth/bandage bandage_effectiveness = 0.25 - bandage_health = 500 + bandage_health = 600 bandage_speed = 4 SECONDS volume = 18 item_weight = 18 GRAMS diff --git a/code/modules/mob/living/carbon/carbon_medical.dm b/code/modules/mob/living/carbon/carbon_medical.dm index 600896e6cf1..130d1971f2a 100644 --- a/code/modules/mob/living/carbon/carbon_medical.dm +++ b/code/modules/mob/living/carbon/carbon_medical.dm @@ -1,7 +1,7 @@ /mob/living/carbon/proc/pump_heart(mob/user, forced_pump) if(!forced_pump) - var/heymedic = GET_MOB_SKILL_VALUE(user, /datum/attribute/skill/misc/medicine)/SKILL_MASTER + var/heymedic = max(GET_MOB_SKILL_VALUE(user, /datum/attribute/skill/misc/medicine), 0)/SKILL_MASTER recent_heart_pump = list("[world.time]" = (0.3 + CEILING(heymedic, 0.1))) else recent_heart_pump = list("[world.time]" = (0.3 + CEILING(forced_pump, 0.1))) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index e3f5fba1472..bca07416113 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -320,148 +320,171 @@ if(target == src) return - CHECK_DNA_AND_SPECIES(target) - - var/obj/item/bodypart/mouth/jaw = target.get_bodypart(BODY_ZONE_PRECISE_MOUTH) - var/obj/item/bodypart/chest/chest = target.get_bodypart(BODY_ZONE_CHEST) var/medical_skill = max(GET_MOB_SKILL_VALUE(src, /datum/attribute/skill/misc/medicine), 0) - - if(DOING_INTERACTION_WITH_TARGET(src, target)) - return FALSE - target.add_fingerprint(src) - switch(cpr_type) - if(CPR_MOUTH) - if(is_mouth_covered()) - to_chat(src, span_warning("I need to uncover my mouth first!")) - return FALSE - if(target.is_mouth_covered()) - to_chat(src, span_warning("I need to uncover [p_their()] mouth first!")) - return FALSE + var/looping = FALSE - if(!jaw) - to_chat(src, span_warning("I have no mouth!")) - return FALSE + do + CHECK_DNA_AND_SPECIES(target) - if(HAS_TRAIT(src, TRAIT_NOBREATH)) - to_chat(src, span_warning("I can't breathe!")) - return FALSE + if(DOING_INTERACTION_WITH_TARGET(src, target)) + return FALSE - if(!getorganslot(ORGAN_SLOT_LUNGS)) - to_chat(src, span_warning("I have no lungs!")) - return FALSE + switch(cpr_type) + if(CPR_MOUTH) + if(is_mouth_covered()) + to_chat(src, span_warning("I need to uncover my mouth first!")) + return FALSE + if(target.is_mouth_covered()) + to_chat(src, span_warning("I need to uncover [p_their()] mouth first!")) + return FALSE + if(!get_bodypart(BODY_ZONE_PRECISE_MOUTH)) + to_chat(src, span_warning("I have no mouth!")) + return FALSE + if(!target.get_bodypart(BODY_ZONE_PRECISE_MOUTH)) + to_chat(src, span_warning("[target] have no mouth!")) + return FALSE + if(HAS_TRAIT(src, TRAIT_NOBREATH)) + to_chat(src, span_warning("I can't breathe!")) + return FALSE + if(!getorganslot(ORGAN_SLOT_LUNGS)) + to_chat(src, span_warning("I have no lungs!")) + return FALSE - if(!do_after(src, 3 SECONDS, target)) - return - var/they_breathe = !HAS_TRAIT(target, TRAIT_NOBREATH) - var/obj/item/organ/lungs/they_lung = target.getorganslot(ORGAN_SLOT_LUNGS) - visible_message(span_notice("[src] performs mouth to mouth on [target]!"), \ - span_notice("I perform mouth to mouth on [target]."), - span_hear("I hear loud breathing."), - vision_distance = COMBAT_MESSAGE_RANGE, - ignored_mobs = target) - log_combat(src, target, "M2Med") - if(they_breathe && they_lung) - var/epinephrine_mod = 0 - if(target.reagents?.get_reagent_amount(/datum/reagent/adrenaline) >= 1) - epinephrine_mod += 5 - target.adjustOxyLoss(-((medical_skill * 0.2) + epinephrine_mod)) - to_chat(target, span_unconscious("I feel a breath of fresh air enter my lungs... It feels good...")) - else if(they_breathe && !they_lung) - to_chat(target, span_unconscious("I feel a breath of fresh air... But i don't feel any better...")) - else - to_chat(target, span_unconscious("I feel a breath of fresh air... Which is a sensation i don't recognise...")) - - if(CPR_CHEST) - var/mob/living/carbon/human/humie = target - if(istype(humie)) - var/obj/item/clothing/suit = humie.wear_armor - var/obj/item/clothing/under = humie.wear_shirt - if(istype(under) && CHECK_BITFIELD(under.clothing_flags, THICKMATERIAL)) - to_chat(src, span_warning("I need to take [humie.p_their()] [under] off!")) - return - else if(istype(suit) && CHECK_BITFIELD(suit.clothing_flags, THICKMATERIAL)) - to_chat(src, span_warning("I need to take [humie.p_their()] [suit] off!")) - return + if(!looping) + visible_message(span_notice("[src] is trying to perform mouth to mouth on [target.name]!"), \ + span_notice("I try to perform mouth to mouth on [target.name]... Hold still!"), \ + vision_distance = COMBAT_MESSAGE_RANGE) - var/compression_time = 10 SECONDS - compression_time *= 1 - max(medical_skill * 0.01, 0.4) - if(!do_after(src, compression_time, target)) - return - var/they_beat = !HAS_TRAIT(target, TRAIT_STABLEHEART) - var/obj/item/organ/heart/they_heart = target.getorganslot(ORGAN_SLOT_HEART) - log_combat(src, target, "CPRed") - if(they_beat && they_heart) - to_chat(target, span_unconscious("I feel my heart being pumped...")) - else if(they_beat && !they_heart) - to_chat(target, span_unconscious("I feel my chest being pumped... But i don't feel any better...")) - else - to_chat(target, span_unconscious("I feel my chest being pushed on...")) - var/epinephrine_mod = 0 - if(target.reagents?.get_reagent_amount(/datum/reagent/adrenaline) >= 1) - epinephrine_mod += 3 - var/heart_exposed_mod = 0 - if(CHECK_MULTIPLE_BITFIELDS(chest.get_surgery_flags(), SURGERY_INCISED|SURGERY_RETRACTED|SURGERY_BROKEN) && istype(they_heart)) - heart_exposed_mod += 5 - - /// Journeymen (average 35) have a 5% chance of doing a CPR revive - var/diceroll = diceroll(medical_skill+heart_exposed_mod+epinephrine_mod, dice_num = 13, context = DICE_CONTEXT_PHYSICAL) - if(diceroll <= DICE_CRIT_FAILURE) - visible_message(span_danger("[src] botches the chest compressions!"), \ - span_danger("I botch the chest compressions!"), - span_hear("I hear pushing."), - vision_distance = COMBAT_MESSAGE_RANGE, \ - ignored_mobs = target) - if(target.stat >= DEAD) - /** - * y = 15 * e^(-0.022x) - * Aiming for points (0, 15) (30, 10) (50, 5) - */ - they_heart.applyOrganDamage(15 * (NUM_E ** (-0.022 * medical_skill)), they_heart.high_threshold) - else - if(heart_exposed_mod) - visible_message(span_notice("[src] massages [target]'s [they_heart]!"), \ - span_notice("I massage [target]'s [they_heart]."), \ - span_hear("I hear pushing."), - vision_distance = COMBAT_MESSAGE_RANGE, \ + if(!do_after(src, 3 SECONDS, target)) + return FALSE + log_combat(src, target, "M2Med") + + visible_message(span_notice("[src] performs mouth to mouth on [target]!"), \ + span_notice("I perform mouth to mouth on [target]."), + span_hear("I hear loud breathing."), + vision_distance = COMBAT_MESSAGE_RANGE, ignored_mobs = target) + + if(HAS_TRAIT(target, TRAIT_NOBREATH)) + to_chat(target, span_unconscious("I feel a breath of fresh air... which is a sensation I don't recognise...")) + else if(!target.getorganslot(ORGAN_SLOT_LUNGS)) + to_chat(target, span_unconscious("I feel a breath of fresh air... But I don't feel any better...")) else - visible_message(span_notice("[src] performs chest compressions on [target]!"), \ - span_notice("I perform chest compressions on [target]."), \ + var/epinephrine_mod = 0 + if(target.reagents?.get_reagent_amount(/datum/reagent/adrenaline) >= 1) + epinephrine_mod += 5 + target.adjustOxyLoss(-((medical_skill * 0.2) + epinephrine_mod)) + to_chat(target, span_unconscious("I feel a breath of fresh air enter my lungs... It feels good...")) + + looping = TRUE + + if(CPR_CHEST) + var/obj/item/bodypart/chest/chest = target.get_bodypart(BODY_ZONE_CHEST) + var/obj/item/organ/heart/they_heart = target.getorganslot(ORGAN_SLOT_HEART) + var/mob/living/carbon/human/humie = target + if(istype(humie)) + var/obj/item/clothing/suit = humie.wear_armor + var/obj/item/clothing/under = humie.wear_shirt + if(istype(under) && CHECK_BITFIELD(under.clothing_flags, THICKMATERIAL)) + to_chat(src, span_warning("I need to take [humie.p_their()] [under] off!")) + return FALSE + else if(istype(suit) && CHECK_BITFIELD(suit.clothing_flags, THICKMATERIAL)) + to_chat(src, span_warning("I need to take [humie.p_their()] [suit] off!")) + return FALSE + + if(!looping) + visible_message(span_notice("[src] is trying to perform chest compressions on [target.name]!"), \ + span_notice("I try to perform chest compressions on [target.name]... Hold still!"), \ + vision_distance = COMBAT_MESSAGE_RANGE) + + var/compression_time = 10 SECONDS + compression_time *= 1 - max(medical_skill * 0.01, 0.4) + if(!do_after(src, compression_time, target)) + return FALSE + log_combat(src, target, "CPRed") + + if (HAS_TRAIT(target, TRAIT_STABLEHEART)) + to_chat(target, span_unconscious("I feel my heart being pumped...")) + else if(!target.getorganslot(ORGAN_SLOT_HEART)) + to_chat(target, span_unconscious("I feel my chest being pumped... But I don't feel any better...")) + to_chat(src, span_warning("[target] isn't responding to my resuscitation...")) + return FALSE + else + to_chat(target, span_unconscious("I feel my chest being pushed on...")) + + var/epinephrine_mod = 0 + if(target.reagents?.get_reagent_amount(/datum/reagent/adrenaline) >= 1) + epinephrine_mod += 3 + var/heart_exposed_mod = 0 + if(CHECK_MULTIPLE_BITFIELDS(chest.get_surgery_flags(), SURGERY_INCISED|SURGERY_RETRACTED|SURGERY_BROKEN) && istype(they_heart)) + heart_exposed_mod += 5 + + /// Master (55) have a 5% chance of reviving through CPR each attempt. + var/diceroll = diceroll(medical_skill+heart_exposed_mod+epinephrine_mod, crit = SKILL_MIDDLING, dice_num = 19, context = DICE_CONTEXT_PHYSICAL) + looping = TRUE + + if(diceroll <= DICE_CRIT_FAILURE) // can't even break ribs correctly + looping = FALSE + visible_message(span_danger("[src] botches the chest compressions!"), \ + span_danger("I botch the chest compressions!"), span_hear("I hear pushing."), vision_distance = COMBAT_MESSAGE_RANGE, \ ignored_mobs = target) - target.pump_heart(src) - if(target.stat < DEAD) // No point in running the revive check - return - if(HAS_TRAIT(target, TRAIT_NECRA_CURSE)) - to_chat(src, span_warning("Necra holds tight to this one.")) - return FALSE - chest.add_wound(/datum/wound/fracture/chest) - if((diceroll >= DICE_SUCCESS) || (!attributes && prob(35))) - if(they_heart.is_failing_without_bleedout()) - to_chat(src, span_warning("[target] isn't responding to my resuscitation...")) - return FALSE - - if(GETBRAINLOSS(target) >= BRAIN_DAMAGE_DEATH) - SETBRAINLOSS(target, BRAIN_DAMAGE_DEATH - 1) - if(target.revive()) - target.grab_ghost(TRUE) - target.visible_message(span_warning("[target] limply spasms their muscles."), \ - span_userdanger("My muscles spasm as i am brought back to life!")) - target.emote("breathgasp") - target.adjust_jitter(100 SECONDS) - add_abstract_elastic_data(ELASCAT_MEDICAL, ELASDATA_CPR_REVIVE, 1) - target.apply_status_effect(/datum/status_effect/debuff/revive) - record_round_statistic(STATS_CPR_REVIVALS, 1) + if(target.stat >= DEAD) /** * y = 15 * e^(-0.022x) * Aiming for points (0, 15) (30, 10) (50, 5) */ they_heart.applyOrganDamage(15 * (NUM_E ** (-0.022 * medical_skill)), they_heart.high_threshold) + else + if(heart_exposed_mod) + visible_message(span_notice("[src] massages [target]'s [they_heart]!"), \ + span_notice("I massage [target]'s [they_heart]."), \ + span_hear("I hear pushing."), + vision_distance = COMBAT_MESSAGE_RANGE, \ + ignored_mobs = target) else + visible_message(span_notice("[src] performs chest compressions on [target]!"), \ + span_notice("I perform chest compressions on [target]."), \ + span_hear("I hear pushing."), + vision_distance = COMBAT_MESSAGE_RANGE, \ + ignored_mobs = target) + + target.pump_heart(src) + if(target.stat < DEAD) // No point in running the revive check + return FALSE + + chest.add_wound(/datum/wound/fracture/chest) + if(HAS_TRAIT(target, TRAIT_NECRA_CURSE)) + to_chat(src, span_warning("Necra holds tight to this one.")) + return FALSE + if(they_heart.is_failing_without_bleedout()) to_chat(src, span_warning("[target] isn't responding to my resuscitation...")) + return FALSE + + if((diceroll >= DICE_SUCCESS) || (!attributes && prob(35))) + looping = FALSE + if(GETBRAINLOSS(target) >= BRAIN_DAMAGE_DEATH) + SETBRAINLOSS(target, BRAIN_DAMAGE_DEATH - 1) + if(target.revive()) + target.grab_ghost(TRUE) + target.visible_message(span_warning("[target] limply spasms their muscles."), \ + span_userdanger("My muscles spasm as i am brought back to life!")) + target.emote("breathgasp") + target.adjust_jitter(100 SECONDS) + add_abstract_elastic_data(ELASCAT_MEDICAL, ELASDATA_CPR_REVIVE, 1) + target.apply_status_effect(/datum/status_effect/debuff/revive) + record_round_statistic(STATS_CPR_REVIVALS, 1) + /** + * y = 15 * e^(-0.022x) + * Aiming for points (0, 15) (30, 10) (50, 5) + */ + they_heart.applyOrganDamage(15 * (NUM_E ** (-0.022 * medical_skill)), they_heart.high_threshold) + else + to_chat(src, span_warning("[target] isn't responding to my resuscitation...")) + while (looping) /mob/living/carbon/human/cuff_resist(obj/item/I, breakouttime = 1 MINUTES, cuff_break = 0, instant = FALSE) if(..()) diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index 8eb366fd159..9ab8123de03 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -54,7 +54,7 @@ if(methods & INJECT) var/modifier = 1 //TODO: Borbop ~ Once we get a proper transfusion system this will become unneeded basically means instead of 5 units we inject 100 units which is 4 injections to suriving level. This is 100% blood duping but like... its this or 80 syringes of blood to get someone restarted if(exposed_mob.stat >= DEAD) - modifier = 20 + modifier = 10 exposed_mob.adjust_blood_volume(round(reac_volume, 0.1) * modifier, maximum = BLOOD_VOLUME_SAFE_MAXIMUM) return if(methods & INGEST) From cf573cdb80e2e2f368804dab2aaea9f1b77e84e0 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Tue, 19 May 2026 12:45:41 -0700 Subject: [PATCH 53/59] chest arteries and misc --- code/__DEFINES/medical.dm | 1 + code/__DEFINES/wounds.dm | 1 + code/controllers/subsystem/skills.dm | 12 ++--- code/datums/attributes/helpers.dm | 5 ++ .../elements/gun_launches_little_guy.dm | 51 +++++++++++++++++++ code/datums/status_effects/debuffs.dm | 30 +++++++++++ code/datums/wounds/_wound.dm | 12 +++-- code/datums/wounds/arteries.dm | 17 ++++--- code/datums/wounds/fractures.dm | 20 ++++---- code/game/objects/items/natural/bandage.dm | 2 +- code/game/objects/items/natural/cloth.dm | 3 -- .../crafting/quality_of_crafting/sewing.dm | 4 ++ .../jobs/job_types/nobility/merchant.dm | 3 +- .../mob/living/carbon/human/species.dm | 6 +-- .../mob/living/carbon/human/status_procs.dm | 2 +- code/modules/mob/living/status_procs.dm | 19 ++++--- code/modules/projectiles/gun.dm | 15 ++---- code/modules/projectiles/guns/ballistic.dm | 3 +- .../projectiles/guns/ballistic/_powder.dm | 10 ++-- .../guns/ballistic/powder/musket.dm | 17 ++++++- .../projectiles/projectile/reusable/bullet.dm | 9 ++-- .../surgery/bodyparts/bodypart_wounds.dm | 3 +- code/modules/surgery/bodyparts/chest.dm | 2 +- .../surgery/organs/internal/artery/_artery.dm | 2 +- .../organs/internal/artery/base_types.dm | 22 +++++--- vanderlin.dme | 1 + 26 files changed, 194 insertions(+), 78 deletions(-) create mode 100644 code/datums/elements/gun_launches_little_guy.dm diff --git a/code/__DEFINES/medical.dm b/code/__DEFINES/medical.dm index cd24908c11f..caf09f13537 100644 --- a/code/__DEFINES/medical.dm +++ b/code/__DEFINES/medical.dm @@ -293,6 +293,7 @@ DEFINE_BITFIELD(organ_flags, list( #define ARTERY_HEAD /obj/item/organ/artery/head #define ARTERY_MOUTH /obj/item/organ/artery/mouth #define ARTERY_CHEST /obj/item/organ/artery/chest +#define ARTERY_HEART /obj/item/organ/artery/heart #define ARTERY_NECK /obj/item/organ/artery/neck #define ARTERY_L_ARM /obj/item/organ/artery/l_arm #define ARTERY_R_ARM /obj/item/organ/artery/r_arm diff --git a/code/__DEFINES/wounds.dm b/code/__DEFINES/wounds.dm index 326a82a87d3..5e16c5fb604 100644 --- a/code/__DEFINES/wounds.dm +++ b/code/__DEFINES/wounds.dm @@ -5,6 +5,7 @@ #define STAB_BCLASSES list(BCLASS_STAB, BCLASS_SHOT, BCLASS_PICK, BCLASS_PIERCE) #define ARTERY_BCLASSES list(BCLASS_CUT, BCLASS_CHOP, BCLASS_STAB, BCLASS_PICK, BCLASS_BITE, BCLASS_SHOT, BCLASS_PIERCE) #define ARTERY_STRONG_BCLASSES list(BCLASS_CHOP, BCLASS_PICK, BCLASS_SHOT) +/// Bypasses heart artery crit fractures restrictions #define ARTERY_HEART_BCLASSES list(BCLASS_PICK, BCLASS_SHOT, BCLASS_PIERCE) #define CHARRING_BCLASSES list(BCLASS_BURN) #define WHIPPING_BCLASSES list(BCLASS_LASHING) diff --git a/code/controllers/subsystem/skills.dm b/code/controllers/subsystem/skills.dm index d5364d69e9a..e66d2f5c95c 100644 --- a/code/controllers/subsystem/skills.dm +++ b/code/controllers/subsystem/skills.dm @@ -11,18 +11,18 @@ SUBSYSTEM_DEF(skills) var/list/all_skills = list() ///Static assoc list of levels (ints) - strings var/list/level_names = list( - span_info("Weak"), \ - span_info("Average"), \ - span_biginfo("Skilled"), \ + span_info("Novice"), \ + span_info("Apprentice"), \ + span_biginfo("Journeyman"), \ span_biginfo("Expert"), \ "Master", \ span_greentext("Legendary"))//This list is already in the right order, due to indexing /// All level plain names without span var/static/alist/level_names_plain = alist( SKILL_RANK_NONE = "None", - SKILL_RANK_NOVICE = "Weak", - SKILL_RANK_APPRENTICE = "Average", - SKILL_RANK_JOURNEYMAN = "Skilled", + SKILL_RANK_NOVICE = "Novice", + SKILL_RANK_APPRENTICE = "Apprentice", + SKILL_RANK_JOURNEYMAN = "Journeyman", SKILL_RANK_EXPERT = "Expert", SKILL_RANK_MASTER = "Master", SKILL_RANK_LEGENDARY = "Legendary", diff --git a/code/datums/attributes/helpers.dm b/code/datums/attributes/helpers.dm index d50a4397612..3e3e0e5aaca 100644 --- a/code/datums/attributes/helpers.dm +++ b/code/datums/attributes/helpers.dm @@ -60,6 +60,11 @@ return FALSE return attributes?.adjust_experience(skill_type, amount, silent, check_apprentice, daily_xp = daily_xp) +/mob/proc/add_sleep_experience(skill, amt, silent = FALSE, check_apprentice = TRUE) + if(HAS_TRAIT(src, TRAIT_NO_EXPERIENCE)) + return FALSE + return mind?.add_sleep_experience(skill, amt, silent, check_apprentice) + /** * Adjusts a skill by a delta in the new 0-60 range, with an optional cap. * diff --git a/code/datums/elements/gun_launches_little_guy.dm b/code/datums/elements/gun_launches_little_guy.dm new file mode 100644 index 00000000000..ee99ba311dc --- /dev/null +++ b/code/datums/elements/gun_launches_little_guy.dm @@ -0,0 +1,51 @@ +/// An element that makes guns throw their user back if they are just a little guy +/datum/element/gun_launches_little_guys + element_flags = ELEMENT_BESPOKE + argument_hash_start_idx = 2 + + /// The throwing force applied to the gun's user + var/throwing_force + /// The throwing range applied to the gun's user + var/throwing_range + +/datum/element/gun_launches_little_guys/Attach(datum/target, throwing_force = 2, throwing_range = 3) + . = ..() + if(!isgun(target)) + return ELEMENT_INCOMPATIBLE + + src.throwing_force = throwing_force + src.throwing_range = throwing_range + + RegisterSignal(target, COMSIG_ATOM_EXAMINE, PROC_REF(examine)) + RegisterSignal(target, COMSIG_GUN_FIRED, PROC_REF(throw_it_back)) + +/datum/element/gun_launches_little_guys/Detach(datum/target) + . = ..() + UnregisterSignal(target, COMSIG_ATOM_EXAMINE) + UnregisterSignal(target, COMSIG_GUN_FIRED) + +/// Warns that this gun might throw you away really hard +/datum/element/gun_launches_little_guys/proc/examine(datum/source, mob/user, list/examine_list) + SIGNAL_HANDLER + + examine_list += span_notice("It has some serious kick to it, smaller users and those one-handing should take caution while firing.") + +/// Checks if the shooter is just a little guy. If so? Throw it back. +/datum/element/gun_launches_little_guys/proc/throw_it_back(obj/item/gun/weapon, mob/living/carbon/user, atom/target, params, zone_override) + SIGNAL_HANDLER + + var/is_smol = FALSE + if(ishuman(user)) + var/mob/living/carbon/human/human_user = user + if(HAS_TRAIT(human_user, TRAIT_TINY)) + is_smol = TRUE + if(!is_smol && HAS_TRAIT(weapon, TRAIT_WIELDED)) + return + + var/fling_direction = REVERSE_DIR(user.dir) + var/atom/throw_target = get_edge_target_turf(user, fling_direction) + user.Knockdown(1 SECONDS, prevent_drop = TRUE) + user.throw_at(throw_target, throwing_range, throwing_force, spin = FALSE) + + user.visible_message(span_warning("[weapon] sends [user] flying back as it fires!"), \ + span_warning("[weapon] sends you flying back as it fires!")) diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm index ba1fdeb6f54..687f3932da0 100644 --- a/code/datums/status_effects/debuffs.dm +++ b/code/datums/status_effects/debuffs.dm @@ -44,15 +44,45 @@ /datum/status_effect/incapacitating/knockdown id = "knockdown" alert_type = /atom/movable/screen/alert/status_effect/knocked_down + ///Boolean that, if TRUE, will prevent the person getting this effect from dropping items. + var/prevent_drop = FALSE + +/datum/status_effect/incapacitating/knockdown/on_creation(mob/living/new_owner, duration_override, prevent_drop) + src.prevent_drop = prevent_drop + . = ..() /datum/status_effect/incapacitating/knockdown/on_apply() . = ..() if(!.) return + + // anti-drop logic (for shuttle dock or tripped) + if(prevent_drop) + for(var/obj/item/held_item in owner.held_items) + ADD_TRAIT(held_item, TRAIT_NODROP, TRAIT_STATUS_EFFECT(id)) + + // Apply floored trait so the mob falls over ADD_TRAIT(owner, TRAIT_FLOORED, TRAIT_STATUS_EFFECT(id)) + // Clean up no-drop immediately after application + if(prevent_drop) + addtimer(CALLBACK(src, PROC_REF(clear_prevent_drop)), 0) + +/datum/status_effect/incapacitating/knockdown/proc/clear_prevent_drop() + for(var/obj/item/held_item in owner.held_items) + REMOVE_TRAIT(held_item, TRAIT_NODROP, TRAIT_STATUS_EFFECT(id)) + /datum/status_effect/incapacitating/knockdown/on_remove() REMOVE_TRAIT(owner, TRAIT_FLOORED, TRAIT_STATUS_EFFECT(id)) + // owner.knockdown_diminish = 1 + return ..() + +// cleaner tripped version — now uses prevent_drop flag instead of the hack +/datum/status_effect/incapacitating/knockdown/tripped + id = "tripped" + +/datum/status_effect/incapacitating/knockdown/tripped/on_apply() + prevent_drop = TRUE return ..() /atom/movable/screen/alert/status_effect/knocked_down diff --git a/code/datums/wounds/_wound.dm b/code/datums/wounds/_wound.dm index 2a98055355b..b489b7c5750 100644 --- a/code/datums/wounds/_wound.dm +++ b/code/datums/wounds/_wound.dm @@ -152,6 +152,8 @@ GLOBAL_LIST_INIT(primordial_wounds, init_primordial_wounds()) ///how much we divide our calculated damage by for odds var/damage_divisor = 6 + var/required_bodypart_status + /datum/wound/Destroy(force) . = ..() if(bodypart_owner) @@ -252,7 +254,7 @@ GLOBAL_LIST_INIT(primordial_wounds, init_primordial_wounds()) /// Returns whether or not this wound can be applied to a given bodypart. /// Setting zone_precise will check whether its in viable_zones and if it matches limb body_zone -/datum/wound/proc/can_apply_to_bodypart(obj/item/bodypart/affected, zone_precise) +/datum/wound/proc/can_apply_to_bodypart(obj/item/bodypart/affected, zone_precise, damage_bclass) if(bodypart_owner || owner || QDELETED(affected) || QDELETED(affected.owner)) return FALSE if(!ignore_bloody && !isnull(bleed_rate) && !affected.can_bloody_wound()) @@ -260,11 +262,11 @@ GLOBAL_LIST_INIT(primordial_wounds, init_primordial_wounds()) for(var/datum/wound/other_wound as anything in affected.wounds) if(!can_stack_with(other_wound)) return FALSE - if(!zone_precise) - return TRUE - if(length(viable_zones) && !(zone_precise in viable_zones)) + if(required_bodypart_status && affected.status != required_bodypart_status) + return + if(zone_precise && length(viable_zones) && !(zone_precise in viable_zones)) return FALSE - if(deprecise_zone(zone_precise) != affected.body_zone) + if(zone_precise && deprecise_zone(zone_precise) != affected.body_zone) return FALSE // we are in a weird place return TRUE diff --git a/code/datums/wounds/arteries.dm b/code/datums/wounds/arteries.dm index 1266ab04e6b..39116670fcd 100644 --- a/code/datums/wounds/arteries.dm +++ b/code/datums/wounds/arteries.dm @@ -12,17 +12,20 @@ aimed_intent_bonus = TRUE crit_message = "Blood sprays from %VICTIM's %BODYPART!" var/artery_type_override + var/list/artery_type_blacklist = list(ARTERY_HEART) viable_zones = list(\ BODY_ZONE_R_ARM, \ BODY_ZONE_R_LEG, \ BODY_ZONE_PRECISE_MOUTH, \ BODY_ZONE_L_LEG, \ BODY_ZONE_L_ARM, \ + BODY_ZONE_CHEST, \ + BODY_ZONE_PRECISE_NECK, \ BODY_ZONE_HEAD) -/datum/wound/artery/can_apply_to_bodypart(obj/item/bodypart/affected) - if(affected.status == BODYPART_ROBOTIC) - return FALSE + required_bodypart_status = BODYPART_ORGANIC + +/datum/wound/artery/can_apply_to_bodypart(obj/item/bodypart/affected, zone_precise, bclass) if(!affected.get_cut(ignore_gauze = TRUE)) return FALSE return ..() @@ -41,6 +44,8 @@ continue if(artery_type_override && !istype(possible_artery, artery_type_override)) continue + if(artery_type_blacklist && (possible_artery.type in artery_type_blacklist)) + continue artery = possible_artery break if(!artery) @@ -67,11 +72,11 @@ name = "Aortic Dissection" severity = WOUND_SEVERITY_FATAL artery_type_override = /obj/item/organ/artery/chest - associated_bclasses = ARTERY_HEART_BCLASSES + artery_type_blacklist = list(ARTERY_CHEST) viable_zones = list(BODY_ZONE_CHEST) -/datum/wound/artery/heart/can_apply_to_bodypart(obj/item/bodypart/affected) - if(affected.limb_flags & BODYPART_BONE_ENCASED && !affected.has_wound(/datum/wound/fracture)) +/datum/wound/artery/heart/can_apply_to_bodypart(obj/item/bodypart/affected, zone_precise, bclass) + if(affected.limb_flags & BODYPART_BONE_ENCASED && !affected.has_wound(/datum/wound/fracture) && !(bclass in ARTERY_HEART_BCLASSES)) return FALSE // Must be vitals zone if(affected.body_zone != BODY_ZONE_CHEST) diff --git a/code/datums/wounds/fractures.dm b/code/datums/wounds/fractures.dm index 5314e95f267..bb444cd0b1b 100644 --- a/code/datums/wounds/fractures.dm +++ b/code/datums/wounds/fractures.dm @@ -20,9 +20,9 @@ // Limbs hemorrhage but clot quickly // Lose 164.3 blood over 19 ticks then clot - bleed_rate = 2.3 - clotting_threshold = 0.6 - clotting_rate = 0.85 + bleed_rate = 1.6 + clotting_threshold = 0.4 + clotting_rate = 0.04 limb_efficiency_reduction = 30 @@ -104,7 +104,7 @@ ) sound_effect = "headcrush" whp = 80 - bleed_rate = 1.6 + bleed_rate = 3.2 clotting_threshold = null mortal = TRUE @@ -152,7 +152,7 @@ "The cranium is shattered!", ) whp = 150 - bleed_rate = 5.8 + bleed_rate = 4.6 paralysis = TRUE knockout = 25 SECONDS min_damage_dividend = 0.95 @@ -224,7 +224,7 @@ bleed_rate = 0.8 clotting_threshold = 0.4 - clotting_rate = 0.04 + clotting_rate = 0.67 viable_zones = list(BODY_ZONE_PRECISE_MOUTH) /datum/wound/fracture/mouth/on_mob_gain(mob/living/affected) @@ -283,9 +283,9 @@ woundpain = 50 whp = 50 // Lose 224.6 blood over 18 ticks then clot - bleed_rate = 1.6 - clotting_threshold = 0.8 - clotting_rate = 1.25 + bleed_rate = 0.8 + clotting_threshold = 0.4 + clotting_rate = 0.67 viable_zones = list(BODY_ZONE_CHEST) /datum/wound/fracture/chest/on_mob_gain(mob/living/affected) @@ -303,7 +303,7 @@ ) whp = 50 gain_emote = "groin" - bleed_rate = 1.6 + bleed_rate = 3.1 clotting_threshold = 1.2 clotting_rate = 0.04 viable_zones = list(BODY_ZONE_PRECISE_GROIN) diff --git a/code/game/objects/items/natural/bandage.dm b/code/game/objects/items/natural/bandage.dm index a473ba477e4..c864a71e2fd 100644 --- a/code/game/objects/items/natural/bandage.dm +++ b/code/game/objects/items/natural/bandage.dm @@ -5,7 +5,7 @@ desc = "A fabric treated and specially made to help with bleeding wounds. Better and faster at stopping bleeding than your regular piece of cloth." bundletype = /obj/item/natural/bundle/cloth/bandage bandage_effectiveness = 0.25 - bandage_health = 600 + bandage_health = 500 bandage_speed = 4 SECONDS volume = 18 item_weight = 18 GRAMS diff --git a/code/game/objects/items/natural/cloth.dm b/code/game/objects/items/natural/cloth.dm index cae38194bc4..af35bdbeb20 100644 --- a/code/game/objects/items/natural/cloth.dm +++ b/code/game/objects/items/natural/cloth.dm @@ -86,9 +86,6 @@ var/turf/T = target T.add_liquid_from_reagents(reagents, amount = 1) reagents.remove_all(1) - if(!reagents.total_volume) - bandage_health = initial(bandage_health) - bandage_effectiveness = initial(bandage_effectiveness) /obj/item/natural/cloth/mob_can_equip(mob/living/M, mob/living/equipper, slot, disable_warning, bypass_equip_delay_self) . = ..() diff --git a/code/modules/crafting/quality_of_crafting/sewing.dm b/code/modules/crafting/quality_of_crafting/sewing.dm index b2d2d4de31d..395b8103205 100644 --- a/code/modules/crafting/quality_of_crafting/sewing.dm +++ b/code/modules/crafting/quality_of_crafting/sewing.dm @@ -1767,6 +1767,10 @@ skillcraft = /datum/attribute/skill/misc/medicine blacklisted_paths = list(/obj/item/natural/cloth/bandage) +// You do not get medical skill for making bandages +/datum/repeatable_crafting_recipe/sewing/bandage/add_skill_experience(mob/user) + return + /datum/repeatable_crafting_recipe/sewing/barding name = "padded barding (saiga)" category = "Armor" diff --git a/code/modules/jobs/job_types/nobility/merchant.dm b/code/modules/jobs/job_types/nobility/merchant.dm index d53b1654f7d..bc1d2c3eae7 100644 --- a/code/modules/jobs/job_types/nobility/merchant.dm +++ b/code/modules/jobs/job_types/nobility/merchant.dm @@ -10,7 +10,8 @@ /datum/attribute/skill/misc/stealing = 60, /datum/attribute/skill/misc/lockpicking = 20, /datum/attribute/skill/misc/riding = 10, - /datum/attribute/skill/labor/mathematics = 50 + /datum/attribute/skill/labor/mathematics = 50, + /datum/attribute/skill/combat/firearms = 20, ) /datum/job/merchant diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index dda1fbe1680..34d96d5f503 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -1730,20 +1730,20 @@ GLOBAL_LIST_EMPTY(roundstart_species) directional_blocked = TRUE break if((!target_table && !target_collateral_mob) || directional_blocked) - target.Knockdown(SHOVE_KNOCKDOWN_SOLID) + target.Knockdown(SHOVE_KNOCKDOWN_SOLID, prevent_drop = TRUE) target.visible_message("[user.name] kicks [target.name], knocking them down!", "I'm knocked down from a kick by [user.name]!", "I hear aggressive shuffling followed by a loud thud!", COMBAT_MESSAGE_RANGE, user) to_chat(user, "I kick [target.name], knocking them down!") log_combat(user, target, "kicked", "knocking them down") else if(target_table) - target.Knockdown(SHOVE_KNOCKDOWN_TABLE) + target.Knockdown(SHOVE_KNOCKDOWN_TABLE, prevent_drop = TRUE) target.visible_message("[user.name] kicked [target.name] onto \the [target_table]!", "I'm kicked onto \the [target_table] by [user.name]!", "I hear aggressive shuffling followed by a loud thud!", COMBAT_MESSAGE_RANGE, user) to_chat(user, "I kick [target.name] onto \the [target_table]!") target.throw_at(target_table, 1, 1, null, FALSE) //1 speed throws with no spin are basically just forcemoves with a hard collision check log_combat(user, target, "kicked", "onto [target_table] (table)") else if(target_collateral_mob) - target.Knockdown(SHOVE_KNOCKDOWN_HUMAN) + target.Knockdown(SHOVE_KNOCKDOWN_HUMAN, prevent_drop = TRUE) target_collateral_mob.Knockdown(SHOVE_KNOCKDOWN_COLLATERAL) target.visible_message("[user.name] kicks [target.name] into [target_collateral_mob.name]!", "I'm kicked into [target_collateral_mob.name] by [user.name]!", "I hear aggressive shuffling followed by a loud thud!", COMBAT_MESSAGE_RANGE, user) diff --git a/code/modules/mob/living/carbon/human/status_procs.dm b/code/modules/mob/living/carbon/human/status_procs.dm index ce78eeb0521..b02751d9d19 100644 --- a/code/modules/mob/living/carbon/human/status_procs.dm +++ b/code/modules/mob/living/carbon/human/status_procs.dm @@ -3,7 +3,7 @@ amount = dna?.species?.spec_stun(src, amount) return ..() -/mob/living/carbon/human/Knockdown(amount, ignore_canstun = FALSE) +/mob/living/carbon/human/Knockdown(amount, ignore_canstun = FALSE, prevent_drop = FALSE) amount = dna?.species?.spec_stun(src, amount) return ..() diff --git a/code/modules/mob/living/status_procs.dm b/code/modules/mob/living/status_procs.dm index a79c518b471..9e5f8a0e9aa 100644 --- a/code/modules/mob/living/status_procs.dm +++ b/code/modules/mob/living/status_procs.dm @@ -110,7 +110,7 @@ return K.duration return 0 -/mob/living/proc/Knockdown(amount, ignore_canstun = FALSE) //Can't go below remaining duration +/mob/living/proc/Knockdown(amount, ignore_canstun = FALSE, prevent_drop = FALSE) //Can't go below remaining duration if(SEND_SIGNAL(src, COMSIG_LIVING_STATUS_KNOCKDOWN, amount, ignore_canstun) & COMPONENT_NO_STUN) return if(((status_flags & CANKNOCKDOWN) && !HAS_TRAIT(src, TRAIT_STUNIMMUNE)) || ignore_canstun) @@ -120,10 +120,10 @@ if(K) K.duration = max(amount, K.duration) else if(amount > 0) - K = apply_status_effect(STATUS_EFFECT_KNOCKDOWN, amount) + K = apply_status_effect(STATUS_EFFECT_KNOCKDOWN, amount, prevent_drop) return K -/mob/living/proc/SetKnockdown(amount, ignore_canstun = FALSE) //Sets remaining duration +/mob/living/proc/SetKnockdown(amount, ignore_canstun = FALSE, prevent_drop = FALSE) //Sets remaining duration if(SEND_SIGNAL(src, COMSIG_LIVING_STATUS_KNOCKDOWN, amount, ignore_canstun) & COMPONENT_NO_STUN) return if(((status_flags & CANKNOCKDOWN) && !HAS_TRAIT(src, TRAIT_STUNIMMUNE)) || ignore_canstun) @@ -137,10 +137,10 @@ if(K) K.duration = amount else - K = apply_status_effect(STATUS_EFFECT_KNOCKDOWN, amount) + K = apply_status_effect(STATUS_EFFECT_KNOCKDOWN, amount, prevent_drop) return K -/mob/living/proc/AdjustKnockdown(amount, ignore_canstun = FALSE) //Adds to remaining duration +/mob/living/proc/AdjustKnockdown(amount, ignore_canstun = FALSE, prevent_drop = FALSE) //Adds to remaining duration if(SEND_SIGNAL(src, COMSIG_LIVING_STATUS_KNOCKDOWN, amount, ignore_canstun) & COMPONENT_NO_STUN) return if(((status_flags & CANKNOCKDOWN) && !HAS_TRAIT(src, TRAIT_STUNIMMUNE)) || ignore_canstun) @@ -150,7 +150,7 @@ if(K) K.duration += amount else if(amount > 0) - K = apply_status_effect(STATUS_EFFECT_KNOCKDOWN, amount) + K = apply_status_effect(STATUS_EFFECT_KNOCKDOWN, amount, prevent_drop) return K ///////////////////////////////// IMMOBILIZED //////////////////////////////////// @@ -624,13 +624,12 @@ if(isnum(max_duration) && duration > 0) // Check the duration remaining on the existing status effect // If it's greater than / equal to our passed max duration, we don't need to do anything - var/remaining_duration = existing.duration - if(remaining_duration >= max_duration) + if(existing.duration >= max_duration) return // Otherwise, add duration up to the max (max_duration - remaining_duration), // or just add duration if it doesn't exceed our max at all - existing.duration += min(max_duration - remaining_duration, duration) + existing.duration += min(max_duration - existing.duration, duration) else existing.duration += duration @@ -638,7 +637,7 @@ // If the duration was decreased and is now less 0 seconds, // qdel it / clean up the status effect immediately // (rather than waiting for the process tick to handle it) - if(existing.duration <= world.time) + if(existing.duration <= 0) qdel(existing) else if(duration > 0) diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 9ce8c27efe3..aeb0693e27e 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -101,12 +101,6 @@ QDEL_NULL(chambered) return ..() -/obj/item/gun/Exited(atom/movable/gone, atom/new_loc) - . = ..() - if(gone == chambered) - chambered = null - update_appearance() - //called after the gun has successfully fired its chambered ammo. /obj/item/gun/proc/after_firing(atom/target, mob/living/user) return FALSE @@ -196,7 +190,7 @@ // This is essentially a workaround for no wielding, thank you TG var/obj/item/bodypart/other_hand = user.has_hand_for_held_index(user.get_inactive_hand_index()) //returns non-disabled inactive hands - if(weapon_weight == WEAPON_HEAVY && (user.get_inactive_held_item() || !other_hand)) + if(weapon_weight == WEAPON_HEAVY && !HAS_TRAIT(src, TRAIT_WIELDED) && (user.get_inactive_held_item() || !other_hand)) balloon_alert(user, "use both hands!") return @@ -262,8 +256,6 @@ if(user) SEND_SIGNAL(user, COMSIG_MOB_FIRED_GUN, src, target, modifiers, zone_override) - SEND_SIGNAL(src, COMSIG_GUN_FIRED, user, target, modifiers, zone_override) - add_fingerprint(user) var/real_spread = get_spread(user) + spread @@ -276,6 +268,7 @@ if(burst_size > 1) firing_burst = TRUE fire_cd = TRUE + SEND_SIGNAL(src, COMSIG_GUN_FIRED, user, target, modifiers, zone_override) for(var/i = 1 to burst_size) addtimer(CALLBACK(src, PROC_REF(process_burst), user, target, message, modifiers, zone_override, total_random_spread, burst_spread_mult, i), burst_delay * (i - 1)) addtimer(CALLBACK(src, PROC_REF(reset_fire_cd)), fire_delay) // for the case of fire delay longer than burst @@ -289,9 +282,11 @@ to_chat(user, span_warning("[src] is lethally chambered! You don't want to risk harming anyone...")) return FALSE - var/sprd = round((rand(0, 1) - 0.5) * DUALWIELD_PENALTY_EXTRA_MULTIPLIER * total_random_spread) before_firing(target, user) + SEND_SIGNAL(src, COMSIG_GUN_FIRED, user, target, modifiers, zone_override) + + var/sprd = round((rand(0, 1) - 0.5) * DUALWIELD_PENALTY_EXTRA_MULTIPLIER * total_random_spread) if(!chambered.fire_casing(target, user, modifiers, 0, FALSE, zone_override, sprd, src)) shoot_with_empty_chamber(user) return FALSE diff --git a/code/modules/projectiles/guns/ballistic.dm b/code/modules/projectiles/guns/ballistic.dm index 92fb1298956..84f4fd8ecef 100644 --- a/code/modules/projectiles/guns/ballistic.dm +++ b/code/modules/projectiles/guns/ballistic.dm @@ -178,7 +178,7 @@ var/boon = user.get_learning_boon(associated_skill) var/amt2raise = GET_MOB_ATTRIBUTE_VALUE(user, STAT_INTELLIGENCE) / 2 - user.adjust_experience(associated_skill, amt2raise * boon, FALSE) + user.add_sleep_experience(associated_skill, amt2raise * boon) // Rechambering cycle if(!semi_auto) @@ -220,6 +220,7 @@ SIGNAL_HANDLER UnregisterSignal(chambered, COMSIG_MOVABLE_MOVED) chambered = null + update_appearance() ///updates a bunch of racking related stuff and also handles the sound effects and the like /obj/item/gun/ballistic/proc/rack(mob/living/user) diff --git a/code/modules/projectiles/guns/ballistic/_powder.dm b/code/modules/projectiles/guns/ballistic/_powder.dm index c692f4b116b..230aba2d9b7 100644 --- a/code/modules/projectiles/guns/ballistic/_powder.dm +++ b/code/modules/projectiles/guns/ballistic/_powder.dm @@ -67,7 +67,7 @@ if(exited == ramrod) ramrod = null -/obj/item/gun/ballistic/powder/clear_chambered(datum/source) +/obj/item/gun/ballistic/powder/after_firing(atom/target, mob/living/user, empty_chamber, from_firing, chamber_next_round) . = ..() bullet_rammed = FALSE @@ -102,13 +102,13 @@ . += span_warning("Whatever is in the barrel, it's not powder.") return - var/extra_string = "loaded" + var/extra_string = "" if(powder_amount > powder_required) - extra_string += span_boldwarning("over-loaded") + extra_string += span_boldwarning("over-") else if (powder_amount < powder_required) - extra_string += span_warning("under-loaded") + extra_string += span_warning("under-") - . += span_notice("The barrel is [extra_string] with powder.") + . += span_notice("The barrel is [extra_string]loaded with powder.") /obj/item/gun/ballistic/powder/update_icon_state() . = ..() diff --git a/code/modules/projectiles/guns/ballistic/powder/musket.dm b/code/modules/projectiles/guns/ballistic/powder/musket.dm index 488797e766d..cc968169c0a 100644 --- a/code/modules/projectiles/guns/ballistic/powder/musket.dm +++ b/code/modules/projectiles/guns/ballistic/powder/musket.dm @@ -17,6 +17,7 @@ item_weight = 4.5 KILOGRAMS possible_item_intents = list(/datum/intent/shoot/musket, /datum/intent/shoot/musket/arc, POLEARM_BASH) + gripped_intents = list(/datum/intent/shoot/musket, /datum/intent/shoot/musket/arc, POLEARM_BASH) force = 10 can_parry = TRUE wdefense = AVERAGE_PARRY @@ -35,7 +36,10 @@ /obj/item/gun/ballistic/powder/musket/Initialize(mapload) bayonet = new(src) - return ..() + possible_item_intents = list(/datum/intent/shoot/musket, /datum/intent/shoot/musket/arc, SPEAR_THRUST) + gripped_intents = list(/datum/intent/shoot/musket, /datum/intent/shoot/musket/arc, POLEARM_THRUST) + . = ..() + AddElement(/datum/element/gun_launches_little_guys, throwing_force = 1, throwing_range = 1) /obj/item/gun/ballistic/powder/musket/Destroy(force) if(!QDELETED(bayonet)) @@ -57,8 +61,16 @@ balloon_alert(user, "attached!") user.transferItemToLoc(attacking_item, src) bayonet = attacking_item + possible_item_intents = list(/datum/intent/shoot/musket, /datum/intent/shoot/musket/arc, SPEAR_THRUST) + gripped_intents = list(/datum/intent/shoot/musket, /datum/intent/shoot/musket/arc, POLEARM_THRUST) + user.update_a_intents() update_appearance(UPDATE_ICON_STATE) +// We're going to sacrifice unloading the musket so you can wield it. Sorry +/obj/item/gun/ballistic/powder/musket/attack_self(mob/living/user, list/modifiers) + if(SEND_SIGNAL(src, COMSIG_ITEM_ATTACK_SELF, user, modifiers) & COMPONENT_CANCEL_ATTACK_CHAIN) + return TRUE + /obj/item/gun/ballistic/powder/musket/attack_hand_secondary(mob/user, list/modifiers) . = ..() if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN) @@ -69,6 +81,9 @@ balloon_alert(user, "removed!") user.put_in_hands(bayonet) + possible_item_intents = list(/datum/intent/shoot/musket, /datum/intent/shoot/musket/arc, POLEARM_BASH) + gripped_intents = list(/datum/intent/shoot/musket, /datum/intent/shoot/musket/arc, POLEARM_BASH) + user.update_a_intents() update_appearance(UPDATE_ICON_STATE) return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN diff --git a/code/modules/projectiles/projectile/reusable/bullet.dm b/code/modules/projectiles/projectile/reusable/bullet.dm index 179071f2786..9f8639c9eff 100644 --- a/code/modules/projectiles/projectile/reusable/bullet.dm +++ b/code/modules/projectiles/projectile/reusable/bullet.dm @@ -39,16 +39,17 @@ if(!iscarbon(target_mob)) return + // Bullets always inflict a fracture var/mob/living/carbon/C = target_mob - var/obj/item/bodypart/BP = C.get_bodypart(def_zone) - if(BP) + var/obj/item/bodypart/bodypart = C.get_bodypart(def_zone) + if(bodypart) var/fracture_type = /datum/wound/fracture - switch(BP.body_zone) + switch(bodypart.body_zone) if(BODY_ZONE_HEAD) fracture_type = /datum/wound/fracture/head if(BODY_ZONE_CHEST) fracture_type = /datum/wound/fracture/chest - BP.add_wound(fracture_type) + bodypart.add_wound(fracture_type) /obj/projectile/bullet/fragment name = "smaller lead ball" diff --git a/code/modules/surgery/bodyparts/bodypart_wounds.dm b/code/modules/surgery/bodyparts/bodypart_wounds.dm index c922321f770..99c4b5e3770 100644 --- a/code/modules/surgery/bodyparts/bodypart_wounds.dm +++ b/code/modules/surgery/bodyparts/bodypart_wounds.dm @@ -247,7 +247,7 @@ var/datum/wound/primordial = GLOB.primordial_wounds[wound_type] if(!primordial.can_roll) continue - if(!primordial.can_apply_to_bodypart(src, zone_precise)) + if(!primordial.can_apply_to_bodypart(src, zone_precise, bclass)) continue var/chance = primordial.get_crit_prob(bclass, dam, damage_dividend, user, src, modifiers) if(prob(chance)) @@ -415,6 +415,7 @@ return FALSE if(!bandage) return FALSE + bandage.bandage_health = 0 bandage.bandage_effectiveness = 1 unbandage_limb() if(owner.stat < UNCONSCIOUS) diff --git a/code/modules/surgery/bodyparts/chest.dm b/code/modules/surgery/bodyparts/chest.dm index ecdf36eafb6..e19207c34bc 100644 --- a/code/modules/surgery/bodyparts/chest.dm +++ b/code/modules/surgery/bodyparts/chest.dm @@ -21,7 +21,7 @@ grid_width = 64 grid_height = 96 - artery_type = ARTERY_CHEST + artery_type = list(ARTERY_CHEST, ARTERY_HEART) limb_flags = BODYPART_HAS_ARTERY | BODYPART_BONE_ENCASED /obj/item/bodypart/chest/set_disabled(new_disabled) diff --git a/code/modules/surgery/organs/internal/artery/_artery.dm b/code/modules/surgery/organs/internal/artery/_artery.dm index 83879dbe586..80f36aed464 100644 --- a/code/modules/surgery/organs/internal/artery/_artery.dm +++ b/code/modules/surgery/organs/internal/artery/_artery.dm @@ -4,7 +4,7 @@ icon_state = "artery" base_icon_state = "artery" sellprice = 1 - dropshrink = 0.6 + dropshrink = 0.5 organ_flags = ORGAN_LIMB_SUPPORTER|ORGAN_INDESTRUCTIBLE|ORGAN_NO_VIOLENT_DAMAGE organ_efficiency = list(ORGAN_SLOT_ARTERY = 100) diff --git a/code/modules/surgery/organs/internal/artery/base_types.dm b/code/modules/surgery/organs/internal/artery/base_types.dm index c65d956cdbd..cc66471f325 100644 --- a/code/modules/surgery/organs/internal/artery/base_types.dm +++ b/code/modules/surgery/organs/internal/artery/base_types.dm @@ -1,37 +1,43 @@ /obj/item/organ/artery/r_arm - name = "right brachial artery" + name = "right brachial arteries" zone = BODY_ZONE_R_ARM blood_flow = ARTERIAL_BLOOD_FLOW * 0.75 /obj/item/organ/artery/r_leg - name = "right femoral artery" + name = "right femoral arteries" zone = BODY_ZONE_R_LEG /obj/item/organ/artery/mouth - name = "facial artery" + name = "facial arteries" zone = BODY_ZONE_PRECISE_MOUTH /obj/item/organ/artery/l_leg - name = "left femoral artery" + name = "left femoral arteries" zone = BODY_ZONE_L_LEG /obj/item/organ/artery/l_arm - name = "left brachial artery" + name = "left brachial arteries" zone = BODY_ZONE_L_ARM blood_flow = ARTERIAL_BLOOD_FLOW * 0.75 /obj/item/organ/artery/head - name = "temporal artery" + name = "temporal arteries" desc = "Well, this one was certainly temporal." zone = BODY_ZONE_HEAD /obj/item/organ/artery/chest + name = "intercostal arteries" + desc = "These run along the spaces between ribs." + zone = BODY_ZONE_CHEST + blood_flow = ARTERIAL_BLOOD_FLOW * 0.4 + +/obj/item/organ/artery/heart name = "thoracic aorta" desc = "Shot through the heart, and you're to blame - Darlin', you give love a bad name." zone = BODY_ZONE_CHEST blood_flow = ARTERIAL_BLOOD_FLOW * 2.5 -/obj/item/organ/artery/chest/tear() +/obj/item/organ/artery/heart/tear() . = ..() owner.vomit(blood = TRUE) var/static/list/heartaches = list( @@ -43,7 +49,7 @@ ) to_chat(owner, "[pick(heartaches)]") -/obj/item/organ/artery/chest/dissect() +/obj/item/organ/artery/heart/dissect() . = ..() if(HAS_TRAIT(owner, TRAIT_CRITICAL_WEAKNESS)) owner.death() diff --git a/vanderlin.dme b/vanderlin.dme index 1f7a2146b6d..f1b7f4e132b 100644 --- a/vanderlin.dme +++ b/vanderlin.dme @@ -1202,6 +1202,7 @@ #include "code\datums\elements\footstep_override.dm" #include "code\datums\elements\frozen.dm" #include "code\datums\elements\give_turf_traits.dm" +#include "code\datums\elements\gun_launches_little_guy.dm" #include "code\datums\elements\hat_wearer.dm" #include "code\datums\elements\holy_weakness.dm" #include "code\datums\elements\immerse.dm" From 48135a6d070f45d28fc08fd465c35110a04f5e54 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Fri, 22 May 2026 21:37:25 -0700 Subject: [PATCH 54/59] to make lungs work --- .../dcs/signals/signals_mob/signals_carbon.dm | 9 + code/__DEFINES/medical.dm | 4 +- code/__DEFINES/mobs.dm | 4 +- code/datums/elements/footstep.dm | 4 +- code/datums/elements/swimming_tile.dm | 19 +- code/datums/injury/organic/burn.dm | 2 +- .../datum_types/snow_storm.dm | 12 - code/datums/wounds/arteries.dm | 1 + code/datums/wounds/fractures.dm | 28 +- code/datums/wounds/special.dm | 2 +- code/game/objects/items/inquisition_relics.dm | 4 + code/game/turfs/open/water.dm | 2 +- code/modules/mob/living/carbon/blood.dm | 4 +- .../mob/living/carbon/carbon_defines.dm | 3 +- code/modules/mob/living/carbon/human/human.dm | 2 +- .../species_types/automatons/_automaton.dm | 3 +- code/modules/mob/living/carbon/life.dm | 153 ++--------- code/modules/mob/living/emote.dm | 1 + code/modules/mob/living/life.dm | 4 - code/modules/mob/living/living.dm | 16 +- .../chemistry/reagents/medicine_reagents.dm | 35 ++- code/modules/surgery/bodyparts/_bodyparts.dm | 6 - code/modules/surgery/organs/_organ.dm | 62 ++--- code/modules/surgery/organs/_organ_attack.dm | 2 +- code/modules/surgery/organs/external/ears.dm | 2 +- code/modules/surgery/organs/helpers.dm | 2 +- .../surgery/organs/internal/appendix.dm | 2 +- code/modules/surgery/organs/internal/brain.dm | 33 +-- code/modules/surgery/organs/internal/eyes.dm | 2 +- code/modules/surgery/organs/internal/heart.dm | 8 +- code/modules/surgery/organs/internal/liver.dm | 2 +- code/modules/surgery/organs/internal/lungs.dm | 2 +- .../modules/surgery/organs/internal/spleen.dm | 2 +- .../surgery/organs/internal/stomach.dm | 2 +- .../modules/surgery/organs/internal/tongue.dm | 2 +- .../surgery/organs/internal/vocal_cords.dm | 2 +- .../surgery/organs/organ_processing/brain.dm | 22 +- .../surgery/organs/organ_processing/heart.dm | 59 ++--- .../surgery/organs/organ_processing/lungs.dm | 247 +++++++++++++++++- 39 files changed, 430 insertions(+), 341 deletions(-) diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_carbon.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_carbon.dm index 4752f573437..4124d72aa77 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_carbon.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_carbon.dm @@ -6,6 +6,15 @@ ///From mob/living/carbon/human/suicide() #define COMSIG_HUMAN_SUICIDE_ACT "human_suicide_act" +///Called when a carbon attempts to breath, before the breath has actually occurred +#define COMSIG_CARBON_ATTEMPT_BREATHE "carbon_attempt_breathe" + // Prevents the breath + #define COMSIG_CARBON_BLOCK_BREATH (1 << 0) + /// Allow the breath but prevent inake, think losebreath + #define BREATHE_SKIP_BREATH (1<<1) +///Called when a carbon breathes, before the breath has actually occurred +#define COMSIG_CARBON_PRE_BREATHE "carbon_pre_breathe" + ///from base of /mob/living/carbon/regenerate_limbs(): (excluded_limbs) #define COMSIG_CARBON_REGENERATE_LIMBS "living_regen_limbs" diff --git a/code/__DEFINES/medical.dm b/code/__DEFINES/medical.dm index caf09f13537..143870cd46a 100644 --- a/code/__DEFINES/medical.dm +++ b/code/__DEFINES/medical.dm @@ -127,7 +127,7 @@ /// Destroyed organs don't function and cannot be repaired, needs a transplant #define ORGAN_DESTROYED (1<<5) /// Not only is the organ failing, it is completely septic and spreading germs around -#define ORGAN_DEAD (1<<6) +#define ORGAN_NECROTIC (1<<6) /// Organ has been cut away from the owner and can be safely removed during surgery #define ORGAN_CUT_AWAY (1<<7) /// Organ should update limb efficiency when damaged or healed @@ -139,7 +139,7 @@ DEFINE_BITFIELD(organ_flags, list( "ORGAN_DESTROYED" = ORGAN_DESTROYED, - "ORGAN_DEAD" = ORGAN_DEAD, + "ORGAN_NECROTIC" = ORGAN_NECROTIC, "ORGAN_CUT_AWAY" = ORGAN_CUT_AWAY, "ORGAN_FROZEN" = ORGAN_FROZEN, "ORGAN_INDESTRUCTIBLE" = ORGAN_INDESTRUCTIBLE, diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index c4de4bf440a..1c7b7b6f7d6 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -30,7 +30,6 @@ #define BLOOD_VOLUME_OKAY BLOOD_VOLUME_NORMAL * 0.6 #define BLOOD_VOLUME_BAD BLOOD_VOLUME_NORMAL * 0.4 #define BLOOD_VOLUME_BLEEDOUT BLOOD_VOLUME_NORMAL * 0.35 -#define BLOOD_VOLUME_BLEEDOUT_PASSOUT BLOOD_VOLUME_NORMAL * 0.25 #define BLOOD_VOLUME_SURVIVE BLOOD_VOLUME_NORMAL * 0.2 /// How efficiently humans regenerate blood. @@ -390,6 +389,9 @@ /// This mob can have blood, cached value of [proc/can_have_blood] #define LIVING_CAN_HAVE_BLOOD (1<<5) +/// Returns whether or not the given mob can succumb +#define CAN_SUCCUMB(target) ((HAS_TRAIT(target, TRAIT_CRITICAL_CONDITION) || HAS_TRAIT(target, TRAIT_DEATHS_DOOR)) && !HAS_TRAIT(target, TRAIT_NODEATH)) + // Body position defines. /// Mob is standing up, usually associated with lying_angle value of 0. #define STANDING_UP 0 diff --git a/code/datums/elements/footstep.dm b/code/datums/elements/footstep.dm index b48895abdf5..98964f146c8 100644 --- a/code/datums/elements/footstep.dm +++ b/code/datums/elements/footstep.dm @@ -56,7 +56,7 @@ if(!istype(turf)) return - if(source.buckled || source.throwing || source.movement_type & (VENTCRAWLING | FLYING) || HAS_TRAIT(source, TRAIT_IMMOBILIZED) || CHECK_MOVE_LOOP_FLAGS(source, MOVEMENT_LOOP_OUTSIDE_CONTROL)) + if(source.buckled || source.throwing || source.movement_type & (VENTCRAWLING | FLYING) || HAS_TRAIT(source, TRAIT_IMMOBILIZED) || (CHECK_MOVE_LOOP_FLAGS(source, MOVEMENT_LOOP_OUTSIDE_CONTROL) && !turf.force_footstep_sound)) return if(source.body_position == LYING_DOWN) //play crawling sound if we're lying @@ -76,7 +76,7 @@ steps_for_living[source] = 0 steps = 0 - if(steps % 2 && !turf.force_footstep_sound) + if(steps % 2) return var/list/footstep_data = list( diff --git a/code/datums/elements/swimming_tile.dm b/code/datums/elements/swimming_tile.dm index 9981eeca3eb..0d5e9cadde3 100644 --- a/code/datums/elements/swimming_tile.dm +++ b/code/datums/elements/swimming_tile.dm @@ -169,22 +169,18 @@ return // You might not be swimming but you can breathe - if(HAS_TRAIT(owner, TRAIT_NODROWN) || HAS_TRAIT(owner, TRAIT_NOBREATH)) + if(HAS_TRAIT(owner, TRAIT_NODROWN) || HAS_TRAIT(owner, TRAIT_NOBREATH) || (owner.mob_size >= MOB_SIZE_HUMAN && owner.body_position == STANDING_UP && !drowning_process_ignore_standing)) return - var/is_drowning = prob(drowning_process_probability) - - if(owner.mob_size >= MOB_SIZE_HUMAN && owner.body_position == STANDING_UP) - if(drowning_process_ignore_standing && is_drowning) - owner.losebreath += floor(oxygen_per_interval / 2) - return + if(prob(50)) + owner.emote("drown") var/drowning_multiplier = has_world_trait(/datum/world_trait/abyssor_rage) ? (is_ascendant(ABYSSOR) ? 3 : 2) : 1 - - owner.emote("drown") owner.apply_damage(oxygen_per_interval * drowning_multiplier * seconds_between_ticks, OXY) + + var/is_drowning = prob(drowning_process_probability) if(is_drowning) - owner.losebreath += floor(oxygen_per_interval / 2) + owner.losebreath += oxygen_per_interval if(iswaterturf(owner_turf)) var/turf/open/water/water_turf = owner_turf @@ -198,6 +194,9 @@ if(!owner.client) return + if(HAS_TRAIT(owner, TRAIT_NOBREATH)) + return + if(old_stat == DEAD || new_stat != DEAD) // If you die while this status effect is active, we're going to assume you drowned return diff --git a/code/datums/injury/organic/burn.dm b/code/datums/injury/organic/burn.dm index b95572ef92a..c8937a3884e 100644 --- a/code/datums/injury/organic/burn.dm +++ b/code/datums/injury/organic/burn.dm @@ -32,7 +32,7 @@ . = ..() //Burn damage can cause fluid loss due to blistering and cook-off if(limb.owner && (limb.burn_dam/limb.max_damage) >= 0.25) // medium burn damage - limb.owner.adjust_blood_volume(-CEILING(BLOOD_VOLUME_SURVIVE * damage_per_injury()/100, 1)) + limb.owner.adjust_blood_volume(-CEILING(BLOOD_VOLUME_BLEEDOUT * damage_per_injury()/100, 1)) /* /datum/injury/burn/receive_damage(damage_received = 0, pain_received = 0, wounding_type = WOUND_BLUNT) diff --git a/code/datums/particle_weathers/datum_types/snow_storm.dm b/code/datums/particle_weathers/datum_types/snow_storm.dm index 42b445575ce..dbc179b856e 100644 --- a/code/datums/particle_weathers/datum_types/snow_storm.dm +++ b/code/datums/particle_weathers/datum_types/snow_storm.dm @@ -33,12 +33,6 @@ temperature_modification = -10 - -//Makes you a little chilly -/datum/particle_weather/snow_gentle/weather_act(mob/living/L) - L.snow_shiver = world.time + 7 SECONDS - - /datum/particle_weather/snow_storm name = "Snow Storm" desc = "Snow Storm, la la description." @@ -75,12 +69,6 @@ else target_turf.snow.weathered(src) -//Makes you a lot little chilly -/mob/living/var/snow_shiver - -/datum/particle_weather/snow_storm/weather_act(mob/living/L) - L.snow_shiver = world.time + 10 SECONDS - /particles/fog icon = 'icons/effects/particles/smoke.dmi' icon_state = list("chill_1" = 2, "chill_2" = 2, "chill_3" = 1) diff --git a/code/datums/wounds/arteries.dm b/code/datums/wounds/arteries.dm index 39116670fcd..3651c74ab6c 100644 --- a/code/datums/wounds/arteries.dm +++ b/code/datums/wounds/arteries.dm @@ -19,6 +19,7 @@ BODY_ZONE_PRECISE_MOUTH, \ BODY_ZONE_L_LEG, \ BODY_ZONE_L_ARM, \ + BODY_ZONE_PRECISE_STOMACH, BODY_ZONE_PRECISE_GROIN, \ BODY_ZONE_CHEST, \ BODY_ZONE_PRECISE_NECK, \ BODY_ZONE_HEAD) diff --git a/code/datums/wounds/fractures.dm b/code/datums/wounds/fractures.dm index bb444cd0b1b..8aeae78b241 100644 --- a/code/datums/wounds/fractures.dm +++ b/code/datums/wounds/fractures.dm @@ -20,9 +20,9 @@ // Limbs hemorrhage but clot quickly // Lose 164.3 blood over 19 ticks then clot - bleed_rate = 1.6 - clotting_threshold = 0.4 - clotting_rate = 0.04 + bleed_rate = 0.8 + clotting_threshold = 0.2 + clotting_rate = 0.02 limb_efficiency_reduction = 30 @@ -104,7 +104,7 @@ ) sound_effect = "headcrush" whp = 80 - bleed_rate = 3.2 + bleed_rate = 1.6 clotting_threshold = null mortal = TRUE @@ -152,7 +152,7 @@ "The cranium is shattered!", ) whp = 150 - bleed_rate = 4.6 + bleed_rate = 2.3 paralysis = TRUE knockout = 25 SECONDS min_damage_dividend = 0.95 @@ -222,9 +222,9 @@ ) whp = 50 - bleed_rate = 0.8 - clotting_threshold = 0.4 - clotting_rate = 0.67 + bleed_rate = 0.4 + clotting_threshold = 0.2 + clotting_rate = 0.33 viable_zones = list(BODY_ZONE_PRECISE_MOUTH) /datum/wound/fracture/mouth/on_mob_gain(mob/living/affected) @@ -283,9 +283,9 @@ woundpain = 50 whp = 50 // Lose 224.6 blood over 18 ticks then clot - bleed_rate = 0.8 - clotting_threshold = 0.4 - clotting_rate = 0.67 + bleed_rate = 0.4 + clotting_threshold = 0.2 + clotting_rate = 0.33 viable_zones = list(BODY_ZONE_CHEST) /datum/wound/fracture/chest/on_mob_gain(mob/living/affected) @@ -303,9 +303,9 @@ ) whp = 50 gain_emote = "groin" - bleed_rate = 3.1 - clotting_threshold = 1.2 - clotting_rate = 0.04 + bleed_rate = 1.6 + clotting_threshold = 0.6 + clotting_rate = 0.02 viable_zones = list(BODY_ZONE_PRECISE_GROIN) /datum/wound/fracture/groin/on_mob_gain(mob/living/affected) diff --git a/code/datums/wounds/special.dm b/code/datums/wounds/special.dm index cf5feabeb93..c6d8f47a1b2 100644 --- a/code/datums/wounds/special.dm +++ b/code/datums/wounds/special.dm @@ -174,7 +174,7 @@ "The tongue flies off in an arc!" ) woundpain = 8 - bleed_rate = 2.5 + bleed_rate = 1.25 can_cauterize = FALSE critical = TRUE associated_bclasses = ARTERY_BCLASSES diff --git a/code/game/objects/items/inquisition_relics.dm b/code/game/objects/items/inquisition_relics.dm index 081398788ad..aa51255f733 100644 --- a/code/game/objects/items/inquisition_relics.dm +++ b/code/game/objects/items/inquisition_relics.dm @@ -949,6 +949,7 @@ ADD_TRAIT(target, TRAIT_MUTE, "garroteCordage") RegisterSignal(target, COMSIG_LIVING_RESIST_GRAB, PROC_REF(on_victim_resist)) RegisterSignal(target, COMSIG_QDELETING, PROC_REF(reset_garrote)) + RegisterSignal(target, COMSIG_CARBON_ATTEMPT_BREATHE, PROC_REF(block_breath)) RegisterSignal(user, COMSIG_ATOM_NO_LONGER_PULLING, PROC_REF(reset_garrote)) victim = WEAKREF(target) lastuser = WEAKREF(user) @@ -966,6 +967,9 @@ else take_damage(max_integrity * 0.1) +/obj/item/inqarticles/garrote/proc/block_breath(datum/source) + return BREATHE_SKIP_BREATH + /obj/item/inqarticles/garrote/razor // To yische, who said not to give this out constantly, I respectfully disagree when it comes to assassin name = "profane razor" // It's very not non lethal now. Strangle your prey with glee desc = "A thin strand of phantom black wire strung between steel grasps. Cold to the touch even through gloves. The strand of wire, while appearing fragile, is seemingly unbreakable." diff --git a/code/game/turfs/open/water.dm b/code/game/turfs/open/water.dm index 45df030a47d..247706dc66a 100644 --- a/code/game/turfs/open/water.dm +++ b/code/game/turfs/open/water.dm @@ -87,7 +87,7 @@ var/is_swimming_tile = TRUE var/stamina_entry_cost var/ticking_stamina_cost - var/ticking_oxy_damage = 4.2 + var/ticking_oxy_damage = 2 var/exhaust_swimmer_prob = 100 /// Randomize direction when initializing diff --git a/code/modules/mob/living/carbon/blood.dm b/code/modules/mob/living/carbon/blood.dm index 56abcd356ea..bc9742b220e 100644 --- a/code/modules/mob/living/carbon/blood.dm +++ b/code/modules/mob/living/carbon/blood.dm @@ -1,7 +1,7 @@ // bleedout checks /mob/living/carbon/proc/in_bleedout() - return (CHECK_BITFIELD(status_flags, BLEEDOUT)) + return (CHECK_BITFIELD(status_flags, BLEEDOUT)) || undergoing_cardiac_arrest() /// Blood volume, affected by the heart /mob/living/carbon/proc/get_blood_circulation() @@ -65,7 +65,7 @@ else apparent_blood_volume = BLOOD_VOLUME_NORMAL - var/apparent_blood_volume_mod = max(0, 1 - getOxyLoss() / max(maxHealth, 1)) + var/apparent_blood_volume_mod = max(0, 1 - (getOxyLoss() / max(maxHealth, 1))) var/oxygenated = get_chem_effect(CE_OXYGENATED) if(oxygenated == 1) // Tirimol apparent_blood_volume_mod += 0.5 diff --git a/code/modules/mob/living/carbon/carbon_defines.dm b/code/modules/mob/living/carbon/carbon_defines.dm index 7e3daf0da46..623a6e845a0 100644 --- a/code/modules/mob/living/carbon/carbon_defines.dm +++ b/code/modules/mob/living/carbon/carbon_defines.dm @@ -62,7 +62,8 @@ var/datum/dna/dna = null//Carbon var/datum/mind/last_mind = null //last mind to control this mob, for blood-based cloning - var/failed_last_breath = 0 //This is used to determine if the mob failed a breath. If they did fail a brath, they will attempt to breathe each tick, otherwise just once per 4 ticks. + ///This is used to determine if the mob failed a breath. If they did fail a brath, they will attempt to breathe each tick, otherwise just once per 4 ticks. + var/failed_last_breath = 0 var/co2overloadtime = null var/obj/item/reagent_containers/food/snacks/meat/steak/type_of_meat = /obj/item/reagent_containers/food/snacks/meat/steak diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index bca07416113..55a60311367 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -422,7 +422,7 @@ heart_exposed_mod += 5 /// Master (55) have a 5% chance of reviving through CPR each attempt. - var/diceroll = diceroll(medical_skill+heart_exposed_mod+epinephrine_mod, crit = SKILL_MIDDLING, dice_num = 19, context = DICE_CONTEXT_PHYSICAL) + var/diceroll = diceroll(medical_skill+heart_exposed_mod+epinephrine_mod, crit = SKILL_MIDDLING, dice_num = 20, context = DICE_CONTEXT_PHYSICAL) looping = TRUE if(diceroll <= DICE_CRIT_FAILURE) // can't even break ribs correctly diff --git a/code/modules/mob/living/carbon/human/species_types/automatons/_automaton.dm b/code/modules/mob/living/carbon/human/species_types/automatons/_automaton.dm index 2ba4838a21c..f7c37a2a717 100644 --- a/code/modules/mob/living/carbon/human/species_types/automatons/_automaton.dm +++ b/code/modules/mob/living/carbon/human/species_types/automatons/_automaton.dm @@ -104,7 +104,8 @@ TRAIT_NOSLEEP, TRAIT_SLEEPIMMUNE, TRAIT_TOXIMMUNE, - TRAIT_FEARLESS + TRAIT_FEARLESS, + TRAIT_NO_ORGAN_PROCESS ) statsheet_male = /datum/attribute_holder/sheet/job/species/automaton diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index 41c667671b4..f633bf66b14 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -30,21 +30,12 @@ handle_wounds() handle_embedded_objects() - handle_roguebreath() update_stress() handle_nausea() handle_shock(delta_time, times_fired) handle_shock_stage(delta_time, times_fired) - if(!CAN_HAVE_BLOOD(src) || (get_blood_volume() > BLOOD_VOLUME_SURVIVE) || HAS_TRAIT(src, TRAIT_BLOODLOSS_IMMUNE)) - if(!heart_attacking) - if(oxyloss) - adjustOxyLoss(-5) - else - if(getOxyLoss() < 20) - heart_attacking = FALSE - handle_sleep() check_cremation() @@ -69,116 +60,10 @@ /mob/living/carbon/handle_random_events() //BP/WOUND BASED PAIN return - -/mob/living/carbon/proc/handle_roguebreath() - return - -/mob/living/carbon/human/handle_roguebreath() - ..() - if(HAS_TRAIT(src, TRAIT_NOBREATH)) - return TRUE - if(istype(loc, /obj/structure/closet/dirthole)) - adjustOxyLoss(5) - if(istype(loc, /obj/structure/closet/burial_shroud)) - var/obj/O = loc - if(istype(O.loc, /obj/structure/closet/dirthole)) - adjustOxyLoss(5) - if(isopenturf(loc)) - var/turf/open/T = loc - if(reagents && T.pollution) - T.pollution.breathe_act(src) - if(HAS_TRAIT(src, TRAIT_DEADNOSE)) - return - if(next_smell <= world.time) - next_smell = world.time + 30 SECONDS - T.pollution.smell_act(src) - /////////////// // BREATHING // /////////////// -/mob/living/carbon/handle_temperature() - var/turf/open/turf = get_turf(src) - if(!istype(turf)) - return - var/temp = turf.return_temperature() - - if(temp < 0 ) - snow_shiver = world.time + 3 SECONDS + abs(temp) - -//Start of a breath chain, calls breathe() -/mob/living/carbon/handle_breathing(times_fired) - var/breath_effect_prob = 0 - var/turf/turf = get_turf(src) - var/turf_temp = turf ? turf.return_temperature() : BODYTEMP_NORMAL - - // Breath visibility based on ambient temperature - // Only visible when it's actually cold enough for condensation - if(turf_temp <= -10) - breath_effect_prob = 100 // Always visible in extreme cold - else if(turf_temp <= -5) - breath_effect_prob = 90 // Very likely in freezing temps - else if(turf_temp <= 0) - breath_effect_prob = 40 // Common at freezing point - else if(turf_temp <= 5) - breath_effect_prob = 15 // Sometimes visible in cold - - // Body temperature effects - if(bodytemperature < BODYTEMP_COLD_DAMAGE_LIMIT) - var/cold_severity = (BODYTEMP_COLD_DAMAGE_LIMIT - bodytemperature) - breath_effect_prob += min(cold_severity * 15, 40) - - // Environmental modifiers - var/turf/snow_turf = get_turf(src) - if(snow_shiver > world.time || snow_turf?.snow) - breath_effect_prob = min(breath_effect_prob + 30, 100) - - // Heavy breathing from exertion or cold body - if(bodytemperature < BODYTEMP_COLD_DAMAGE_LIMIT - 3) - breath_effect_prob = min(breath_effect_prob + 50, 100) - if(prob(15) && !is_mouth_covered()) - to_chat(src, span_warning("Your breath comes out in heavy puffs of vapor.")) - - if(prob(breath_effect_prob) && !is_mouth_covered()) - emit_breath_particle(/particles/fog/breath) - - return - -/mob/living/proc/emit_breath_particle(particle_type) - ASSERT(ispath(particle_type, /particles)) - - var/obj/effect/abstract/particle_holder/holder = new(src, particle_type) - var/particles/breath_particle = holder.particles - var/breath_dir = dir - - var/list/particle_grav = list(0, 0.1, 0) - var/list/particle_pos = list(0, 6, 0) - if(breath_dir & NORTH) - particle_grav[2] = 0.2 - breath_particle.rotation = pick(-45, 45) - // Layer it behind the mob since we're facing away from the camera - holder.pixel_w -= 4 - holder.pixel_y += 4 - if(breath_dir & WEST) - particle_grav[1] = -0.2 - particle_pos[1] = -5 - breath_particle.rotation = -45 - if(breath_dir & EAST) - particle_grav[1] = 0.2 - particle_pos[1] = 5 - breath_particle.rotation = 45 - if(breath_dir & SOUTH) - particle_grav[2] = 0.2 - breath_particle.rotation = pick(-45, 45) - // Shouldn't be necessary but just for parity - holder.pixel_w += 4 - holder.pixel_y -= 4 - - breath_particle.gravity = particle_grav - breath_particle.position = particle_pos - - QDEL_IN(holder, breath_particle.lifespan) - /mob/living/carbon/proc/has_smoke_protection() if(HAS_TRAIT(src, TRAIT_NOBREATH)) return TRUE @@ -192,34 +77,31 @@ /mob/living/carbon/proc/handle_organs(delta_time, times_fired) if(HAS_TRAIT(src, TRAIT_NO_ORGAN_PROCESS)) //internal stasis basically return - if(stat < DEAD) - var/list/already_processed_life = list() - var/list/organlist - var/obj/item/organ/organ - for(var/organ_slot in GLOB.organ_process_order) + + // This is no longer tied to mob stat since organs can live on their own + var/list/already_processed_life = list() + for(var/organ_slot in GLOB.organ_process_order) + if(QDELETED(src)) + break + var/list/organlist = LAZYACCESS(internal_organs_slot, organ_slot) + for(var/obj/item/organ/organ as anything in organlist) if(QDELETED(src)) break - organlist = LAZYACCESS(internal_organs_slot, organ_slot) - for(var/thing in organlist) - if(QDELETED(src)) - break - organ = thing - // This exists mostly because reagent metabolization can cause organ shuffling - if(!QDELETED(organ) && !already_processed_life[organ_slot] && (organ.owner == src)) - if(organ.needs_processing) - organ.on_life(delta_time, times_fired) - already_processed_life[organ] = TRUE - var/datum/organ_process/organ_process + // This exists mostly because reagent metabolization can cause organ shuffling + if(!QDELETED(organ) && !already_processed_life[organ_slot] && (organ.owner == src)) + if(organ.needs_processing) + organ.on_life(delta_time, times_fired) + already_processed_life[organ] = TRUE + + if(stat < DEAD) for(var/thing in GLOB.organ_process_datum_order) if(QDELETED(src)) break - organ_process = GLOB.organ_processes_by_slot[thing] + var/datum/organ_process/organ_process = GLOB.organ_processes_by_slot[thing] if(organ_process.needs_process(src)) organ_process.handle_process(src, delta_time, times_fired) else - var/obj/item/organ/organ - for(var/thing in internal_organs) - organ = thing + for(var/obj/item/organ/organ as anything in internal_organs) //Needed so organs decay while inside the body organ.on_death(delta_time, times_fired) @@ -486,6 +368,7 @@ All effects don't start immediately, but rather get worse over time; the rate is var/list/hearts = getorganslotlist(ORGAN_SLOT_HEART) if(status) pulse = PULSE_NONE + ADD_TRAIT(src, TRAIT_DEATHS_DOOR, ASYSTOLE_TRAIT) for(var/obj/item/organ/heart/heart in hearts) heart.Stop() else diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm index f2a48b77e12..4a0ad7181dd 100644 --- a/code/modules/mob/living/emote.dm +++ b/code/modules/mob/living/emote.dm @@ -410,6 +410,7 @@ nomsg = TRUE only_forced_audio = TRUE ignore_silent = TRUE + stat_allowed = HARD_CRIT // ............... E .................. /datum/emote/living/embed diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index caf0d82653b..2316ddcf76d 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -31,7 +31,6 @@ if(!HAS_TRAIT(src, TRAIT_STASIS)) //Breathing, if applicable handle_temperature() - handle_breathing(times_fired) if(HAS_TRAIT(src, TRAIT_SIMPLE_WOUNDS)) handle_wounds() handle_embedded_objects() @@ -121,9 +120,6 @@ /mob/living/proc/handle_temperature() return -/mob/living/proc/handle_breathing(times_fired) - return - /mob/living/proc/handle_random_events() //random painstun if(stat || HAS_TRAIT(src, TRAIT_NOPAINSTUN) || !can_feel_pain()) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 578e7bd7abb..c92d96fe3d0 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -714,17 +714,19 @@ return if(!reaper) return - if (HAS_TRAIT(src, TRAIT_CRITICAL_CONDITION)) - log_message("Has [whispered ? "whispered his final words" : "succumbed to death"] while in [InFullCritical() ? "hard":"soft"] critical with [round(health, 0.1)] points of health!", LOG_ATTACK) - if(istype(src.loc, /turf/open/water) && !HAS_TRAIT(src, TRAIT_NOBREATH) && body_position == LYING_DOWN && client) - record_round_statistic(STATS_PEOPLE_DROWNED) + if (!CAN_SUCCUMB(src)) + to_chat(src, span_warning("You are unable to succumb to death! This life continues."), type=MESSAGE_TYPE_INFO) + return - adjustOxyLoss(201) - updatehealth() + log_message("Has [whispered ? "whispered his final words" : "succumbed to death"] while in [InFullCritical() ? "hard":"soft"] critical with [round(health, 0.1)] points of health!", LOG_ATTACK) + + adjustOxyLoss(201) + updatehealth() // if(!whispered) // to_chat(src, "I have given up life and succumbed to death.") - death() + investigate_log("has succumbed to death.", INVESTIGATE_DEATHS) + death() /** * Checks if a mob is incapacitated diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index 82b72386493..af1136ec421 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -17,26 +17,25 @@ metabolization_rate = 0.25 * REAGENTS_METABOLISM overdose_threshold = 35 -/datum/reagent/medicine/atropine/on_mob_metabolize(mob/living/L) +/datum/reagent/medicine/atropine/on_mob_metabolize(mob/living/affected_mob) . = ..() - if(!iscarbon(L)) + if(!iscarbon(affected_mob)) return - var/mob/living/carbon/C = L - var/numbing = min(50, CEILING(C.getShock(TRUE)/2, 1)) - C.add_chem_effect(CE_BLOODRESTORE, 1, "[type]") - C.add_chem_effect(CE_PAINKILLER, numbing, "[type]") - C.add_chem_effect(CE_STABLE, 1, "[type]") - if(C.undergoing_cardiac_arrest() || C.undergoing_nervous_system_failure()) - C.add_chem_effect(CE_ORGAN_REGEN, 1, "[type]") - -/datum/reagent/medicine/atropine/on_mob_end_metabolize(mob/living/L) - . = ..() - L.remove_chem_effect(CE_BLOODRESTORE, "[type]") - L.remove_chem_effect(CE_ORGAN_REGEN, "[type] ") - L.remove_chem_effect(CE_PAINKILLER, "[type]") - L.remove_chem_effect(CE_TOXIN, "[type]") - L.remove_chem_effect(CE_BLOCKAGE, "[type]") - L.remove_chem_effect(CE_STABLE, "[type]") + var/mob/living/carbon/carbon_mob = affected_mob + var/numbing = min(50, CEILING(carbon_mob.getShock(FALSE)/2, 1)) + carbon_mob.add_chem_effect(CE_PAINKILLER, numbing, "[type]") + carbon_mob.add_chem_effect(CE_STABLE, 1, "[type]") + carbon_mob.add_chem_effect(CE_ORGAN_REGEN, 1, "[type]") + carbon_mob.add_chem_effect(CE_BRAIN_REGEN, 1, "[type]") + carbon_mob.add_chem_effect(CE_OXYGENATED, 1, "[type]") + +/datum/reagent/medicine/atropine/on_mob_end_metabolize(mob/living/affected_mob) + . = ..() + affected_mob.remove_chem_effect(CE_PAINKILLER, "[type]") + affected_mob.remove_chem_effect(CE_STABLE, "[type]") + affected_mob.remove_chem_effect(CE_ORGAN_REGEN, "[type] ") + affected_mob.add_chem_effect(CE_BRAIN_REGEN, 1, "[type]") + affected_mob.remove_chem_effect(CE_OXYGENATED, "[type]") /datum/reagent/medicine/atropine/on_mob_life(mob/living/carbon/affected_mob, efficiency) if(affected_mob.health <= affected_mob.crit_threshold) diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index b8e2c4cdad7..d05dfc4fa6e 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -1516,12 +1516,6 @@ if(artery.damage) return TRUE -/obj/item/bodypart/proc/is_artery_dissected() - . = FALSE - for(var/obj/item/organ/artery/artery as anything in getorganslotlist(ORGAN_SLOT_ARTERY)) - if(artery.is_broken()) - return TRUE - /obj/item/bodypart/proc/get_incision(surgical_only = FALSE, ignore_gauze = FALSE) if(!ignore_gauze && bandage) return diff --git a/code/modules/surgery/organs/_organ.dm b/code/modules/surgery/organs/_organ.dm index 9d7752431a0..ac1f7decb7c 100644 --- a/code/modules/surgery/organs/_organ.dm +++ b/code/modules/surgery/organs/_organ.dm @@ -98,7 +98,7 @@ /// The space we occupy inside a limb - unaffected by w_class for balance reasons var/organ_volume = 0 - /// How much blood an organ can store - Base is 5 * blood_req, so the organ can survive without blood for 10 seconds before taking damage (+ blood supply of arteries) + /// How much blood an organ can store - Base is 10 * blood_req, so the organ can survive without blood for 10 seconds before taking damage (+ blood supply of arteries) var/max_blood_storage = 0 /// How much blood is currently in the organ var/current_blood = 0 @@ -112,6 +112,8 @@ /// Thresholds organs can naturally heal down to var/self_heal_thresholds = list(0.3, 0.6, 0.9) + /// If the mob has this chem effect, ignore all other checks for can_self_heal and ignore self_heal_thresholds + var/self_healing_effect = CE_ORGAN_REGEN /obj/item/organ/Initialize() . = ..() @@ -159,32 +161,20 @@ /obj/item/organ/proc/update_organ_efficiency(slot) return -/obj/item/organ/proc/is_working() - return (!CHECK_BITFIELD(organ_flags, ORGAN_FAILING|ORGAN_DESTROYED|ORGAN_DEAD|ORGAN_CUT_AWAY) && (damage < high_threshold) && (current_blood || !max_blood_storage)) - -/obj/item/organ/proc/is_working_without_bleedout() - return (!CHECK_BITFIELD(organ_flags, ORGAN_FAILING|ORGAN_DESTROYED|ORGAN_DEAD|ORGAN_CUT_AWAY) && (damage < high_threshold)) - /obj/item/organ/proc/is_failing() - return (CHECK_BITFIELD(organ_flags, ORGAN_FAILING|ORGAN_DESTROYED|ORGAN_DEAD|ORGAN_CUT_AWAY) || (damage >= high_threshold) || (!current_blood && max_blood_storage)) + return (CHECK_BITFIELD(organ_flags, ORGAN_FAILING|ORGAN_DESTROYED|ORGAN_NECROTIC|ORGAN_CUT_AWAY) || (damage >= high_threshold) || (!current_blood && max_blood_storage)) /obj/item/organ/proc/is_failing_without_bleedout() - return (CHECK_BITFIELD(organ_flags, ORGAN_FAILING|ORGAN_DESTROYED|ORGAN_DEAD|ORGAN_CUT_AWAY) || (damage >= high_threshold)) + return (CHECK_BITFIELD(organ_flags, ORGAN_FAILING|ORGAN_DESTROYED|ORGAN_NECROTIC|ORGAN_CUT_AWAY) || (damage >= high_threshold)) /obj/item/organ/proc/is_dead() - return (CHECK_BITFIELD(organ_flags, ORGAN_DESTROYED|ORGAN_DEAD) || (damage >= maxHealth)) + return (CHECK_BITFIELD(organ_flags, ORGAN_DESTROYED|ORGAN_NECROTIC) || (damage >= maxHealth)) /obj/item/organ/proc/is_bruised() return (damage >= low_threshold) -/obj/item/organ/proc/is_broken() - return (CHECK_BITFIELD(organ_flags, ORGAN_FAILING) || (damage >= high_threshold)) - -/obj/item/organ/proc/is_destroyed() - return (CHECK_BITFIELD(organ_flags, ORGAN_DESTROYED)) - /obj/item/organ/proc/is_necrotic() - return (CHECK_BITFIELD(organ_flags, ORGAN_DEAD) || (germ_level >= INFECTION_LEVEL_THREE)) + return (CHECK_BITFIELD(organ_flags, ORGAN_NECROTIC) || (germ_level >= INFECTION_LEVEL_THREE)) /obj/item/organ/proc/scar_organ(amount, cap) for(var/slot in organ_efficiency) @@ -200,15 +190,16 @@ /obj/item/organ/proc/necrose_organ() . = FALSE - if(!CHECK_BITFIELD(organ_flags, ORGAN_DEAD)) + if(!CHECK_BITFIELD(organ_flags, ORGAN_NECROTIC)) set_germ_level(INFECTION_LEVEL_THREE) + organ_flags |= ORGAN_NECROTIC return TRUE /obj/item/organ/proc/unnecrose_organ() . = FALSE - if(CHECK_BITFIELD(organ_flags, ORGAN_DEAD)) + if(CHECK_BITFIELD(organ_flags, ORGAN_NECROTIC)) set_germ_level(0) - organ_flags &= ~ORGAN_DEAD + organ_flags &= ~ORGAN_NECROTIC return TRUE /obj/item/organ/proc/handle_blood(delta_time, times_fired) @@ -226,9 +217,9 @@ if(!blood_req) return if(!in_bleedout) - current_blood = min(current_blood + (blood_req * 0.5 * delta_time), max_blood_storage) //very slow refill + current_blood = min(current_blood + (blood_req * delta_time), max_blood_storage) //very slow refill return - current_blood = max(current_blood - (blood_req * 0.5 * delta_time), 0) + current_blood = max(current_blood - (blood_req * delta_time), 0) // When all blood is lost, take blood from blood vessels if(!current_blood) var/obj/item/organ/artery @@ -240,7 +231,7 @@ break if(artery?.current_blood) var/prev_blood = artery.current_blood - artery.current_blood = max(artery.current_blood - (blood_req * 0.5 * delta_time), 0) + artery.current_blood = max(artery.current_blood - (blood_req * delta_time), 0) current_blood = max(prev_blood - artery.current_blood, 0) if((current_blood <= 0) && !(organ_flags & ORGAN_LIMB_SUPPORTER)) applyOrganDamage(0.2 * delta_time) @@ -374,7 +365,7 @@ /obj/item/organ/adjust_germ_level(add_germs, minimum_germs = 0, maximum_germs = INFECTION_LEVEL_THREE) . = ..() - if((germ_level >= INFECTION_LEVEL_THREE) && !CHECK_BITFIELD(organ_flags, ORGAN_DEAD)) + if((germ_level >= INFECTION_LEVEL_THREE) && !CHECK_BITFIELD(organ_flags, ORGAN_NECROTIC)) kill_organ() /obj/item/organ/proc/kill_organ() @@ -399,7 +390,7 @@ if(isreagentcontainer(loc)) return FALSE /// preserving ah. check_cold() - if(CHECK_BITFIELD(organ_flags, ORGAN_FROZEN|ORGAN_DEAD|ORGAN_SYNTHETIC|ORGAN_INDESTRUCTIBLE))//I'll let arteries not rot to make life easier + if(CHECK_BITFIELD(organ_flags, ORGAN_FROZEN|ORGAN_NECROTIC|ORGAN_SYNTHETIC|ORGAN_INDESTRUCTIBLE))//I'll let arteries not rot to make life easier return FALSE return TRUE @@ -529,7 +520,7 @@ if(healing_factor <= 0) return FALSE - if(owner.get_chem_effect(CE_ORGAN_REGEN)) + if(self_healing_effect && owner.get_chem_effect(self_healing_effect)) return TRUE if(is_dead()) @@ -552,13 +543,14 @@ ///Damage decrements again by a percent of its maxhealth, depending on the owner's health healing_amount += (owner.satiety > 0) ? (healing_factor * (owner.satiety / MAX_SATIETY)) : 0 - var/max_healing_amount = 0 - for(var/i in self_heal_thresholds) - var/limit = i * maxHealth - if(damage >= limit) - max_healing_amount = damage - limit - if(max_healing_amount) - healing_amount = min(max_healing_amount, healing_amount) + if(self_healing_effect && !owner.get_chem_effect(self_healing_effect)) + var/max_healing_amount = 0 + for(var/i in self_heal_thresholds) + var/limit = i * maxHealth + if(damage >= limit) + max_healing_amount = damage - limit + if(max_healing_amount) + healing_amount = min(max_healing_amount, healing_amount) if(healing_amount <= 0) return @@ -635,7 +627,7 @@ var/effective_efficiency = LAZYACCESS(organ_efficiency, slot) if(isnull(effective_efficiency)) return effective_efficiency - var/static/list/no_bleedout_organs = list(ORGAN_SLOT_ARTERY, ORGAN_SLOT_HEART) + var/static/list/no_bleedout_organs = list(ORGAN_SLOT_ARTERY) if(slot in no_bleedout_organs) if(is_failing_without_bleedout()) return 0 @@ -792,7 +784,7 @@ /obj/item/organ/proc/can_feel_pain() if(pain_multiplier <= 0) return FALSE - if(CHECK_BITFIELD(organ_flags, ORGAN_CUT_AWAY | ORGAN_DEAD)) + if(CHECK_BITFIELD(organ_flags, ORGAN_CUT_AWAY)) return FALSE if(HAS_TRAIT(src, TRAIT_NOPAIN)) return FALSE diff --git a/code/modules/surgery/organs/_organ_attack.dm b/code/modules/surgery/organs/_organ_attack.dm index c629aa1b0a9..893cfe931b1 100644 --- a/code/modules/surgery/organs/_organ_attack.dm +++ b/code/modules/surgery/organs/_organ_attack.dm @@ -68,7 +68,7 @@ /obj/item/organ/proc/handle_healing_item(obj/item/tool, mob/living/user, params) var/obj/item/natural/stack = tool /* - if(organ_flags & (ORGAN_DESTROYED|ORGAN_DEAD)) + if(organ_flags & (ORGAN_DESTROYED|ORGAN_NECROTIC)) to_chat(user, span_warning("\The [src] is damaged beyond the point of no return.")) return */ diff --git a/code/modules/surgery/organs/external/ears.dm b/code/modules/surgery/organs/external/ears.dm index a9d33f6c67a..5dd89a0d47b 100644 --- a/code/modules/surgery/organs/external/ears.dm +++ b/code/modules/surgery/organs/external/ears.dm @@ -15,7 +15,7 @@ organ_volume = 0.25 max_blood_storage = 2.5 current_blood = 2.5 - blood_req = 0.5 + blood_req = 0.25 oxygen_req = 0.5 nutriment_req = 0.15 hydration_req = 0.15 diff --git a/code/modules/surgery/organs/helpers.dm b/code/modules/surgery/organs/helpers.dm index a76a73fcc71..c1183a96d18 100644 --- a/code/modules/surgery/organs/helpers.dm +++ b/code/modules/surgery/organs/helpers.dm @@ -143,7 +143,7 @@ total_hydration_req = 0 for(var/thing in internal_organs) var/obj/item/organ/organ = thing - total_blood_req += (organ.blood_req/100 * BLOOD_VOLUME_NORMAL) + total_blood_req += (organ.blood_req/50 * BLOOD_VOLUME_NORMAL) total_oxygen_req += organ.oxygen_req total_nutriment_req += (organ.nutriment_req/100) total_hydration_req += (organ.hydration_req/100) diff --git a/code/modules/surgery/organs/internal/appendix.dm b/code/modules/surgery/organs/internal/appendix.dm index fe2ad265b73..7cb6f739da6 100644 --- a/code/modules/surgery/organs/internal/appendix.dm +++ b/code/modules/surgery/organs/internal/appendix.dm @@ -17,7 +17,7 @@ organ_volume = 0.5 max_blood_storage = 2.5 current_blood = 2.5 - blood_req = 0.5 + blood_req = 0.25 oxygen_req = 0.25 nutriment_req = 0.35 hydration_req = 0.2 diff --git a/code/modules/surgery/organs/internal/brain.dm b/code/modules/surgery/organs/internal/brain.dm index 65912a02be1..8e495ec2d69 100644 --- a/code/modules/surgery/organs/internal/brain.dm +++ b/code/modules/surgery/organs/internal/brain.dm @@ -29,10 +29,11 @@ organ_volume = 0.5 max_blood_storage = 100 current_blood = 100 - blood_req = 5 - oxygen_req = 5 + blood_req = 2.5 + oxygen_req = 6 nutriment_req = 3 hydration_req = 1.5 + self_healing_effect = CE_BRAIN_REGEN COOLDOWN_DECLARE(trauma_cooldown) @@ -90,14 +91,18 @@ if(!blood_req) return if(!in_bleedout && (effective_blood_oxygenation >= BLOOD_VOLUME_SAFE)) - current_blood = min(current_blood + (blood_req * 0.5 * delta_time), max_blood_storage) + current_blood = min(current_blood + (blood_req * delta_time), max_blood_storage) return + if(in_bleedout) - current_blood = max(current_blood - (blood_req * 0.5 * delta_time), 0) + current_blood = max(current_blood - (blood_req * delta_time * 2), 0) + if(DT_PROB(5, delta_time)) + owner.adjust_eye_blur_up_to(4, 4) else - current_blood = max(current_blood - (blood_req * ((BLOOD_VOLUME_NORMAL-effective_blood_oxygenation)/BLOOD_VOLUME_NORMAL) * (0.5 * delta_time)), 0) + current_blood = max(current_blood - (blood_req * ((BLOOD_VOLUME_NORMAL-effective_blood_oxygenation)/BLOOD_VOLUME_NORMAL) * delta_time * 2), 0) + // When all blood is lost, take blood from blood vessels - if(!current_blood) + if(!current_blood && (effective_blood_oxygenation >= BLOOD_VOLUME_SAFE)) var/obj/item/organ/artery var/obj/item/bodypart/parent = owner.get_bodypart(current_zone) for(var/thing in shuffle(parent?.getorganslotlist(ORGAN_SLOT_ARTERY))) @@ -107,7 +112,7 @@ break if(artery?.current_blood) var/prev_blood = artery.current_blood - artery.current_blood = max(artery.current_blood - (blood_req * 0.5 * delta_time), 0) + artery.current_blood = max(artery.current_blood - (blood_req * delta_time * 2), 0) current_blood = max(prev_blood - artery.current_blood, 0) //Don't apply damage, this is handled by the organ process datum, if necessary @@ -300,26 +305,16 @@ return ..() /obj/item/organ/brain/can_self_heal(delta_time, times_fired) - . = TRUE if(!owner) return FALSE if(healing_factor <= 0) return FALSE - - if(owner.get_chem_effect(CE_BRAIN_REGEN)) + if(self_healing_effect && owner.get_chem_effect(self_healing_effect)) return TRUE - - if(is_dead()) - return FALSE - if(current_blood <= 0) - return FALSE - if(owner.undergoing_cardiac_arrest()) - return FALSE var/effective_blood_oxygenation = GET_EFFECTIVE_BLOOD_VOL(owner.get_blood_oxygenation(), owner.total_blood_req) if(effective_blood_oxygenation < BLOOD_VOLUME_SAFE) return FALSE - if(owner.stat >= DEAD) - return FALSE + return ..() /obj/item/organ/brain/proc/past_damage_threshold(threshold) return (get_current_damage_threshold() > threshold) diff --git a/code/modules/surgery/organs/internal/eyes.dm b/code/modules/surgery/organs/internal/eyes.dm index 75b9c4f8119..65672ff74eb 100644 --- a/code/modules/surgery/organs/internal/eyes.dm +++ b/code/modules/surgery/organs/internal/eyes.dm @@ -31,7 +31,7 @@ organ_volume = 0.25 max_blood_storage = 5 current_blood = 5 - blood_req = 1 + blood_req = 0.5 oxygen_req = 0.5 nutriment_req = 0.15 hydration_req = 0.15 diff --git a/code/modules/surgery/organs/internal/heart.dm b/code/modules/surgery/organs/internal/heart.dm index 36cc083e172..be5716bbd89 100644 --- a/code/modules/surgery/organs/internal/heart.dm +++ b/code/modules/surgery/organs/internal/heart.dm @@ -12,7 +12,7 @@ organ_volume = 0.5 max_blood_storage = 100 current_blood = 100 - blood_req = 10 + blood_req = 5 oxygen_req = 5 nutriment_req = 3 hydration_req = 1.5 @@ -94,11 +94,6 @@ loose_names += node.name . += span_warning("You can see a few humors loosely pressed against [src], [english_list(loose_names)].") -/obj/item/organ/heart/is_working() - if(owner) - return (..() && beating) - return ..() - /obj/item/organ/heart/is_failing() if(owner) return (..() || !beating) @@ -108,7 +103,6 @@ . = ..() if(!special) addtimer(CALLBACK(src, PROC_REF(stop_if_unowned)), 12 SECONDS) - owner?.stop_sound_channel(CHANNEL_HEARTBEAT) /obj/item/organ/heart/attack_self(mob/user) . = ..() diff --git a/code/modules/surgery/organs/internal/liver.dm b/code/modules/surgery/organs/internal/liver.dm index 55e82c42f4d..17e006dd36d 100644 --- a/code/modules/surgery/organs/internal/liver.dm +++ b/code/modules/surgery/organs/internal/liver.dm @@ -15,7 +15,7 @@ organ_volume = 2 max_blood_storage = 25 current_blood = 25 - blood_req = 4 + blood_req = 2 oxygen_req = 4 nutriment_req = 1.2 hydration_req = 1.2 diff --git a/code/modules/surgery/organs/internal/lungs.dm b/code/modules/surgery/organs/internal/lungs.dm index aefe26e1659..f1d6c1f6f42 100644 --- a/code/modules/surgery/organs/internal/lungs.dm +++ b/code/modules/surgery/organs/internal/lungs.dm @@ -16,7 +16,7 @@ organ_volume = 1 max_blood_storage = 25 current_blood = 25 - blood_req = 4 + blood_req = 2 oxygen_req = 4 nutriment_req = 1.2 hydration_req = 1.2 diff --git a/code/modules/surgery/organs/internal/spleen.dm b/code/modules/surgery/organs/internal/spleen.dm index c24ed85b02b..552c2cbbee4 100644 --- a/code/modules/surgery/organs/internal/spleen.dm +++ b/code/modules/surgery/organs/internal/spleen.dm @@ -16,7 +16,7 @@ organ_volume = 0.5 max_blood_storage = 20 current_blood = 20 - blood_req = 2 + blood_req = 1 oxygen_req = 2 nutriment_req = 2.4 hydration_req = 0.9 diff --git a/code/modules/surgery/organs/internal/stomach.dm b/code/modules/surgery/organs/internal/stomach.dm index 0a82acb8b47..a8d203cfaf3 100644 --- a/code/modules/surgery/organs/internal/stomach.dm +++ b/code/modules/surgery/organs/internal/stomach.dm @@ -13,7 +13,7 @@ organ_volume = 1 max_blood_storage = 20 current_blood = 20 - blood_req = 4 + blood_req = 2 oxygen_req = 4 nutriment_req = 1.5 hydration_req = 1.2 diff --git a/code/modules/surgery/organs/internal/tongue.dm b/code/modules/surgery/organs/internal/tongue.dm index 912b380fb15..ff6a689c37f 100644 --- a/code/modules/surgery/organs/internal/tongue.dm +++ b/code/modules/surgery/organs/internal/tongue.dm @@ -10,7 +10,7 @@ organ_volume = 0.5 max_blood_storage = 5 current_blood = 5 - blood_req = 1 + blood_req = 0.5 oxygen_req = 0.5 nutriment_req = 0.3 hydration_req = 0.6 diff --git a/code/modules/surgery/organs/internal/vocal_cords.dm b/code/modules/surgery/organs/internal/vocal_cords.dm index dce4eef8777..0ad743d59b0 100644 --- a/code/modules/surgery/organs/internal/vocal_cords.dm +++ b/code/modules/surgery/organs/internal/vocal_cords.dm @@ -10,7 +10,7 @@ organ_volume = 1 max_blood_storage = 10 current_blood = 10 - blood_req = 2 + blood_req = 1 oxygen_req = 2.5 nutriment_req = 1.5 diff --git a/code/modules/surgery/organs/organ_processing/brain.dm b/code/modules/surgery/organs/organ_processing/brain.dm index 197f6b52341..360703bd193 100644 --- a/code/modules/surgery/organs/organ_processing/brain.dm +++ b/code/modules/surgery/organs/organ_processing/brain.dm @@ -12,36 +12,36 @@ switch(effective_blood_oxygenation) if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE) - if(DT_PROB(2.5, delta_time)) + if(DT_PROB(1, delta_time)) to_chat(owner, span_warning("I feel [pick("dizzy","woozy","faint")].")) - owner.adjustOxyLoss(round(0.005 * (BLOOD_VOLUME_NORMAL - effective_blood_oxygenation) * delta_time * 0.3, 1)) + // owner.adjustOxyLoss(round(0.005 * (BLOOD_VOLUME_NORMAL - effective_blood_oxygenation) * delta_time * 0.3, 1)) damprob = is_stable ? 10 : 30 if((brain.current_blood <= 0) && !brain.past_damage_threshold(2) && DT_PROB(damprob, delta_time)) brain.applyOrganDamage(BRAIN_DAMAGE_LOW_OXYGENATION) if(BLOOD_VOLUME_BAD to BLOOD_VOLUME_OKAY) owner.adjust_eye_blur_up_to(4, 4) - if(DT_PROB(5, delta_time)) + if(DT_PROB(2.5, delta_time)) to_chat(owner, span_userdanger("I feel very [pick("dizzy","woozy","faint")]...")) - owner.adjustOxyLoss(FLOOR(0.01 * (BLOOD_VOLUME_NORMAL - effective_blood_oxygenation) * delta_time * 0.3, 1)) - damprob = is_stable ? 15 : 50 + // owner.adjustOxyLoss(FLOOR(0.01 * (BLOOD_VOLUME_NORMAL - effective_blood_oxygenation) * delta_time * 0.3, 1)) + damprob = is_stable ? 15 : 45 if((brain.current_blood <= 0) && !brain.past_damage_threshold(4) && DT_PROB(damprob, delta_time)) brain.applyOrganDamage(BRAIN_DAMAGE_LOW_OXYGENATION) if(BLOOD_VOLUME_SURVIVE to BLOOD_VOLUME_BAD) owner.adjust_eye_blur_up_to(6, 6) - if(DT_PROB(5, delta_time)) + if(DT_PROB(2.5, delta_time)) to_chat(owner, span_userdanger("I feel extremely [pick("dizzy","woozy","faint")]...")) - owner.adjustOxyLoss(2.5 * delta_time) - damprob = is_stable ? 40 : 75 + // owner.adjustOxyLoss(2.5 * delta_time) + damprob = is_stable ? 25 : 75 if((brain.current_blood <= 0) && !brain.past_damage_threshold(6) && DT_PROB(damprob, delta_time)) brain.applyOrganDamage(BRAIN_DAMAGE_LOWER_OXYGENATION) if(-INFINITY to BLOOD_VOLUME_SURVIVE) owner.adjust_eye_blur_up_to(6, 6) - if((brain.current_blood <= 0) && DT_PROB(7.5, delta_time)) + if(DT_PROB(2.5, delta_time)) to_chat(owner, span_userdanger("I feel [pick("heavy", "dehydrated", "empty")] and [pick("faint", "weak", "lightheaded", "dizzy")]!")) - owner.adjustOxyLoss(5 * delta_time) + // owner.adjustOxyLoss(5 * delta_time) owner.Unconscious(rand(6,12) SECONDS) - damprob = is_stable ? 65 : 100 + damprob = is_stable ? 33 : 100 if((brain.current_blood <= 0) && DT_PROB(damprob, delta_time)) brain.applyOrganDamage(BRAIN_DAMAGE_LOWEST_OXYGENATION) diff --git a/code/modules/surgery/organs/organ_processing/heart.dm b/code/modules/surgery/organs/organ_processing/heart.dm index 69b0aedbd21..73b662ea1b8 100644 --- a/code/modules/surgery/organs/organ_processing/heart.dm +++ b/code/modules/surgery/organs/organ_processing/heart.dm @@ -32,11 +32,12 @@ /datum/organ_process/heart/proc/handle_pulse(mob/living/carbon/owner, delta_time, times_fired) // Pulse mod starts out as just the chemical effect amount + // Your heart will fail and stop beating if it runs out of current_blood. Pumping heart resets current_blood and heart beating. var/heart_efficiency = owner.getorganslotefficiency(ORGAN_SLOT_HEART) - var/is_stable = owner.get_chem_effect(CE_STABLE) || HAS_TRAIT(owner, TRAIT_STABLEHEART) + var/is_stable = owner.get_chem_effect(CE_STABLE) var/pulse_mod = (is_stable ? 0 : owner.get_chem_effect(CE_PULSE)) - // If you have enough heart chemicals to be over 2, you're likely to take extra damage. + // If you have enough heart pulse chemicals to be over 2, you're likely to take extra damage. if(pulse_mod > 2 && !is_stable) var/damage_chance = (pulse_mod - 2) ** 2 if(DT_PROB(damage_chance / 2, delta_time)) @@ -61,10 +62,9 @@ owner.pulse = clamp(PULSE_NONE + pulse_mod, PULSE_NONE, PULSE_FASTER) //Pretend that we're dead. unlike actual death, can be influenced by meds return - // If our heart is stopped, it isn't going to restart itself randomly. - if(heart_efficiency < failing_threshold) + // If our heart is stopped or failed, it isn't going to restart itself randomly. + if(heart_efficiency < failing_threshold && !is_stable) owner.set_heartattack(TRUE) - ADD_TRAIT(owner, TRAIT_DEATHS_DOOR, ASYSTOLE_TRAIT) return // if(owner.pulse <= PULSE_NONE) // ADD_TRAIT(owner, TRAIT_DEATHS_DOOR, ASYSTOLE_TRAIT) @@ -88,20 +88,18 @@ else owner.pulse++ - // Thready pulse can damage us - if(owner.pulse >= PULSE_THREADY) - if(DT_PROB(2.5, delta_time)) - owner.adjustOrganLoss(ORGAN_SLOT_HEART, 1) - else if(owner.pulse >= PULSE_FASTER) - if(DT_PROB(0.5, delta_time)) - owner.adjustOrganLoss(ORGAN_SLOT_HEART, 1) + // High pulse can cause heart damage + if(owner.pulse >= PULSE_THREADY && DT_PROB(2.5, delta_time)) + owner.adjustOrganLoss(ORGAN_SLOT_HEART, 1) + else if(owner.pulse >= PULSE_FASTER && DT_PROB(0.5, delta_time)) + owner.adjustOrganLoss(ORGAN_SLOT_HEART, 1) // Finally, check if we should go into cardiac arrest // cardiovascular shock, not enough liquid to pump - /* Your heart doesn't stop because you ran out of blood, it stops because it's dead */ - // var/should_stop = (owner.get_blood_circulation() < GET_EFFECTIVE_BLOOD_VOL(BLOOD_VOLUME_SURVIVE, owner.total_blood_req)) && DT_PROB(40, delta_time) + /* + var/should_stop = (owner.get_blood_circulation() < GET_EFFECTIVE_BLOOD_VOL(BLOOD_VOLUME_SURVIVE, owner.total_blood_req)) && DT_PROB(40, delta_time) // brain failing to work heart properly - var/should_stop = DT_PROB(CEILING(max(0, GETBRAINLOSS(owner) - (owner.maxHealth * 0.5)) / 2, 1), delta_time) + should_stop = should_stop || DT_PROB(CEILING(max(0, GETBRAINLOSS(owner) - (owner.maxHealth * 0.5)) / 2, 1), delta_time) // erratic heart patterns, usually caused by oxyloss should_stop = should_stop || ((owner.pulse >= PULSE_THREADY) && DT_PROB(6, delta_time)) @@ -114,17 +112,16 @@ if(heart.can_stop()) heart.Stop() break + */ // No pulse means we are already having a cardiac arrest moment if(owner.pulse <= PULSE_NONE) owner.set_heartattack(TRUE) - ADD_TRAIT(owner, TRAIT_DEATHS_DOOR, ASYSTOLE_TRAIT) - // High pulse can cause heart damage else - if((owner.pulse == PULSE_FASTER) && DT_PROB(0.5, delta_time)) - owner.adjustOrganLoss(ORGAN_SLOT_HEART, 1) - else if((owner.pulse >= PULSE_THREADY) && DT_PROB(2.5, delta_time)) - owner.adjustOrganLoss(ORGAN_SLOT_HEART, 1) + // if((owner.pulse == PULSE_FASTER) && DT_PROB(0.5, delta_time)) + // owner.adjustOrganLoss(ORGAN_SLOT_HEART, 1) + // else if((owner.pulse >= PULSE_THREADY) && DT_PROB(2.5, delta_time)) + // owner.adjustOrganLoss(ORGAN_SLOT_HEART, 1) REMOVE_TRAIT(owner, TRAIT_DEATHS_DOOR, ASYSTOLE_TRAIT) /datum/organ_process/heart/proc/handle_blood(mob/living/carbon/owner, delta_time, times_fired) @@ -149,11 +146,6 @@ to_chat(owner, span_userdanger("Not... Enough... Blood...")) else owner.status_flags &= ~BLEEDOUT - if((owner.status_flags & BLEEDOUT) && DT_PROB(5, delta_time)) - owner.adjust_eye_blur_up_to(4, 4) - - if((effective_blood_circulation <= BLOOD_VOLUME_BLEEDOUT_PASSOUT) && DT_PROB(10, delta_time)) - owner.Unconscious(4 SECONDS) var/temp_bleed = 0 var/bleed_mod = 1 @@ -175,6 +167,7 @@ var/bleed_sound = "sound/gore/blood[rand(1, 6)].ogg" playsound(owner, bleed_sound, 60, FALSE) + // This is not effective_blood_circulation because it represents the physical trauma from not having enough blood if(CAN_HAVE_BLOOD(owner) && !HAS_TRAIT(owner, TRAIT_BLOODLOSS_IMMUNE) && owner.stat != DEAD) switch(owner.get_blood_volume()) if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE) @@ -198,8 +191,7 @@ owner.remove_status_effect(/datum/status_effect/debuff/bleedingworse) owner.remove_status_effect(/datum/status_effect/debuff/bleedingworst) - var/bleed_rate = owner.get_bleed_rate() - if(bleed_rate) + if(owner.get_bleed_rate()) owner.add_stress(/datum/stress_event/bleeding) else owner.remove_stress(/datum/stress_event/bleeding) @@ -212,16 +204,13 @@ SEND_SOUND(owner, slowbeat) if(cardiac_arrest || nervous_failure) to_chat(owner, span_notice("I feel the grim reaper's cold gaze...")) - return - if((owner.heartbeat_sound == BEAT_SLOW) && !cardiac_arrest && !nervous_failure) + else if((owner.heartbeat_sound == BEAT_SLOW) && !cardiac_arrest && !nervous_failure) owner.stop_sound_channel(CHANNEL_HEARTBEAT) owner.heartbeat_sound = BEAT_NONE - return - if((owner.heartbeat_sound != BEAT_FAST) && (owner.has_status_effect(/datum/status_effect/jitter)) && !cardiac_arrest && !nervous_failure) + else if((owner.heartbeat_sound != BEAT_FAST) && (owner.has_status_effect(/datum/status_effect/jitter)) && !cardiac_arrest && !nervous_failure) SEND_SOUND(owner, fastbeat) owner.heartbeat_sound = BEAT_FAST - return - if((owner.heartbeat_sound == BEAT_FAST) && (owner.has_status_effect(/datum/status_effect/jitter))) + else if((owner.heartbeat_sound == BEAT_FAST) && (owner.has_status_effect(/datum/status_effect/jitter))) owner.stop_sound_channel(CHANNEL_HEARTBEAT) owner.heartbeat_sound = BEAT_NONE - return + diff --git a/code/modules/surgery/organs/organ_processing/lungs.dm b/code/modules/surgery/organs/organ_processing/lungs.dm index 8e8c9c1a4a1..8565277de91 100644 --- a/code/modules/surgery/organs/organ_processing/lungs.dm +++ b/code/modules/surgery/organs/organ_processing/lungs.dm @@ -2,19 +2,40 @@ slot = ORGAN_SLOT_LUNGS /datum/organ_process/lungs/needs_process(mob/living/carbon/owner) - var/needlung = owner.needs_lungs() - if(!needlung || (owner.stat >= DEAD)) + if(!owner.needs_lungs()) + if(owner.getOxyLoss()) + owner.setOxyLoss(0) + owner.losebreath = 0 owner.failed_last_breath = FALSE return FALSE return ..() /datum/organ_process/lungs/handle_process(mob/living/carbon/owner, delta_time, times_fired) - handle_oxygenation(owner, delta_time, times_fired) + handle_breathing(owner, delta_time, times_fired) return TRUE -/datum/organ_process/lungs/proc/handle_oxygenation(mob/living/carbon/owner, delta_time, times_fired) +/datum/organ_process/lungs/proc/handle_breathing(mob/living/carbon/owner, delta_time, times_fired) + var/next_breath = 4 + var/obj/item/organ/lungs/lungs = owner.getorganslot(ORGAN_SLOT_LUNGS) + var/obj/item/organ/heart/heart = owner.getorganslot(ORGAN_SLOT_HEART) + if(lungs && lungs.get_slot_efficiency(ORGAN_SLOT_LUNGS) <= failing_threshold) + next_breath-- + if(heart && heart.get_slot_efficiency(ORGAN_SLOT_HEART) <= failing_threshold) + next_breath-- + + var/owner_failed_breath = owner.failed_last_breath + if((times_fired % next_breath) == 0 || owner_failed_breath) + // Breathe per 4 ticks if healthy, down to 2 if our lungs or heart are damaged, unless suffocating + breathe(owner, delta_time, times_fired, owner_failed_breath ? 1 : next_breath) + else if(isobj(owner.loc)) + var/obj/location_as_object = owner.loc + location_as_object.handle_internal_lifeform(owner, 0) + + +/* var/lung_efficiency = owner.getorganslotefficiency(ORGAN_SLOT_LUNGS) var/effective_oxygenation = ((100 - owner.getOxyLoss()) * (lung_efficiency/optimal_threshold)) + if(effective_oxygenation < owner.total_oxygen_req) if(DT_PROB(0.5, delta_time)) if(owner.body_position != LYING_DOWN) @@ -48,3 +69,221 @@ else victim.emote("choke") sleep(1 SECONDS) +*/ + +/datum/organ_process/lungs/proc/breathe(mob/living/carbon/owner, delta_time, times_fired, next_breath = 4) + var/obj/item/organ/lungs/lungs = owner.getorganslot(ORGAN_SLOT_LUNGS) + if((owner.pulledby?.grab_state >= GRAB_KILL) || (lungs.is_failing())) + owner.losebreath++ //You can't breath at all when being choked or if your lungs are failing, so you're going to miss a breath + + var/pre_sig_return = SEND_SIGNAL(owner, COMSIG_CARBON_ATTEMPT_BREATHE, delta_time, times_fired) + if(pre_sig_return & COMSIG_CARBON_BLOCK_BREATH) + return + if(pre_sig_return & BREATHE_SKIP_BREATH) + owner.losebreath = max(owner.losebreath, 1) + + SEND_SIGNAL(owner, COMSIG_CARBON_PRE_BREATHE, delta_time) + + var/breath + + // Suffocate + var/skip_breath = FALSE + if(owner.losebreath >= 1) + owner.losebreath -= 1 + skip_breath = TRUE + if(isobj(owner.loc)) + var/obj/location_as_object = owner.loc + location_as_object.handle_internal_lifeform(owner, 0, delta_time) + else + breath = TRUE + if(isobj(owner.loc)) + var/obj/loc_as_obj = owner.loc + loc_as_obj.handle_internal_lifeform(owner, 1.99, delta_time) + + owner.check_breath(breath, skip_breath, delta_time) + +/** + * This proc tests if the lungs can breathe, if the mob can breathe a given gas mixture, and throws/clears gas alerts. + * If there are moles of gas in the given gas mixture, side-effects may be applied/removed on the mob. + * This proc expects a lungs organ in order to breathe successfully, but does not defer any work to it. + * + * Returns TRUE if the breath was successful, or FALSE if otherwise. + * + * Arguments: + * * breath: A gas mixture to test (TRUE), or null. + * * skip_breath: Used to differentiate between a failed breath and a lack of breath. + * A mob suffocating due to being in a vacuum may be treated differently than a mob suffocating due to lung failure. + */ +/mob/living/carbon/proc/check_breath(breath, skip_breath = FALSE, delta_time) + if(status_flags & GODMODE) + failed_last_breath = FALSE + return FALSE + + if(HAS_TRAIT(src, TRAIT_NOBREATH)) + return FALSE + return TRUE + +/mob/living/carbon/human/check_breath(breath, skip_breath = FALSE, delta_time) + . = ..() + if(!.) + return + + var/obj/item/organ/lungs/human_lungs = getorganslot(ORGAN_SLOT_LUNGS) + if(human_lungs) + return human_lungs.check_breath(breath, src, skip_breath, delta_time) + + // Lungs are missing! Can't breathe. + // Extra damage, let God sort ’em out! + adjustOxyLoss((total_oxygen_req / 10) * delta_time) + failed_last_breath = TRUE + return FALSE + +/obj/item/organ/lungs/proc/check_breath(breath, mob/living/carbon/human/breather, skip_breath, delta_time) + . = TRUE + if(breather.status_flags & GODMODE) + breather.failed_last_breath = FALSE + return FALSE + + if(HAS_TRAIT(breather, TRAIT_NOBREATH)) + return FALSE + + // If the breath is null, it's actually a failed breath + var/no_breath = isnull(breath) || skip_breath + if(no_breath) + breath = null + + if(breath) + breather.failed_last_breath = FALSE + handle_breath_temperature(breath, breather) + else + // Can't breathe! + . = FALSE // Returning FALSE indicates the breath failed. + breather.failed_last_breath = TRUE + + breathe_air(breather, breath, delta_time) + breathe_pollution(breather, breath, delta_time) + +/obj/item/organ/lungs/proc/breathe_air(mob/living/carbon/breather, breath, delta_time) + if(!breath) + handle_suffocation(breather, delta_time) + return + + if(breather.getOxyLoss()) + var/lung_efficiency = get_slot_efficiency(ORGAN_SLOT_LUNGS)/ORGAN_OPTIMAL_EFFICIENCY + // Less blood so breaths give you less oxygen + var/blood_modifier = 1 + if(CAN_HAVE_BLOOD(breather)) + blood_modifier = BLOOD_VOLUME_NORMAL / breather.get_blood_volume() + var/oxygen_req_modifier = 0.97 + (30/max(breather.total_oxygen_req, 1)) * 0.03 + breather.adjustOxyLoss(-5 * blood_modifier * lung_efficiency * oxygen_req_modifier * delta_time) + +/obj/item/organ/lungs/proc/breathe_pollution(mob/living/carbon/breather, breath, delta_time) + if(!breath) + return + + var/turf/open/breather_loc = breather.loc + if(!istype(breather_loc)) + return + if(!breather_loc.pollution) + return + + + breather_loc.pollution.breathe_act(breather) + if(HAS_TRAIT(breather, TRAIT_DEADNOSE)) + return + if(breather.next_smell <= world.time) + breather.next_smell = world.time + 30 SECONDS + breather_loc.pollution.smell_act(breather) + +/obj/item/organ/lungs/proc/handle_suffocation(mob/living/carbon/human/suffocator = null, delta_time) + // Can't suffocate without a Human, or without minimum breath pressure. + if(!suffocator) + return + // Mob is suffocating. + suffocator.failed_last_breath = TRUE + // Give them a chance to notice something is wrong. + if(prob(20)) + suffocator.emote("gasp") + suffocator.adjustOxyLoss((suffocator.total_oxygen_req / 10) * delta_time) + +/obj/item/organ/lungs/proc/handle_breath_temperature(breath, mob/living/carbon/human/breather) + var/breath_effect_prob = 0 + var/turf/turf = get_turf(breather) + var/turf_temp = turf ? turf.return_temperature() : BODYTEMP_NORMAL + + // Breath visibility based on ambient temperature + // Only visible when it's actually cold enough for condensation + if(turf_temp <= -10) + breath_effect_prob = 100 // Always visible in extreme cold + else if(turf_temp <= -5) + breath_effect_prob = 90 // Very likely in freezing temps + else if(turf_temp <= 0) + breath_effect_prob = 40 // Common at freezing point + else if(turf_temp <= 5) + breath_effect_prob = 15 // Sometimes visible in cold + + // Body temperature effects + if(breather.bodytemperature < BODYTEMP_COLD_DAMAGE_LIMIT) + var/cold_severity = (BODYTEMP_COLD_DAMAGE_LIMIT - breather.bodytemperature) + breath_effect_prob += min(cold_severity * 15, 40) + + // Environmental modifiers + var/turf/snow_turf = get_turf(breather) + if(snow_turf?.snow) + breath_effect_prob = min(breath_effect_prob + 30, 100) + + // Heavy breathing from exertion or cold body + if(breather.bodytemperature < BODYTEMP_COLD_DAMAGE_LIMIT - 3) + breath_effect_prob = min(breath_effect_prob + 50, 100) + if(prob(15) && !breather.is_mouth_covered()) + to_chat(breather, span_warning("My breath comes out in heavy puffs of vapor.")) + + if(prob(breath_effect_prob) && !breather.is_mouth_covered()) + emit_breath_particle(/particles/fog/breath) + +/obj/item/organ/lungs/proc/emit_breath_particle(mob/living/carbon/human/breather, particle_type) + ASSERT(ispath(particle_type, /particles)) + + var/obj/effect/abstract/particle_holder/holder = new(breather, particle_type) + var/particles/breath_particle = holder.particles + var/breath_dir = breather.dir + + var/list/particle_grav = list(0, 0.1, 0) + var/list/particle_pos = list(0, 6, 0) + if(breath_dir & NORTH) + particle_grav[2] = 0.2 + breath_particle.rotation = pick(-45, 45) + // Layer it behind the mob since we're facing away from the camera + holder.pixel_w -= 4 + holder.pixel_y += 4 + if(breath_dir & WEST) + particle_grav[1] = -0.2 + particle_pos[1] = -5 + breath_particle.rotation = -45 + if(breath_dir & EAST) + particle_grav[1] = 0.2 + particle_pos[1] = 5 + breath_particle.rotation = 45 + if(breath_dir & SOUTH) + particle_grav[2] = 0.2 + breath_particle.rotation = pick(-45, 45) + // Shouldn't be necessary but just for parity + holder.pixel_w += 4 + holder.pixel_y -= 4 + + breath_particle.gravity = particle_grav + breath_particle.position = particle_pos + + QDEL_IN(holder, breath_particle.lifespan) + +/obj/proc/handle_internal_lifeform(mob/living/lifeform_inside_me, breath_request, delta_time) + return + +/obj/structure/closet/dirthole/handle_internal_lifeform(mob/living/lifeform_inside_me, breath_request, delta_time) + var/suffocation_damage = 5 * (breath_request ? 2 : 1) + return lifeform_inside_me.adjustOxyLoss(suffocation_damage * delta_time) + +/obj/structure/closet/burial_shroud/handle_internal_lifeform(mob/living/lifeform_inside_me, breath_request, delta_time) + if(recursive_loc_check(lifeform_inside_me, /obj/structure/closet/dirthole)) + var/suffocation_damage = 5 * (breath_request ? 2 : 1) + return lifeform_inside_me.adjustOxyLoss(suffocation_damage * delta_time) From 858c99b8bb19c901c2890a0b5b907d44ef056141 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Sat, 23 May 2026 11:01:27 -0700 Subject: [PATCH 55/59] emergency fiex --- code/game/objects/items/inquisition_relics.dm | 6 +++++- code/game/objects/items/natural/cloth.dm | 2 +- code/modules/surgery/organs/internal/brain.dm | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/code/game/objects/items/inquisition_relics.dm b/code/game/objects/items/inquisition_relics.dm index aa51255f733..8860fd203a9 100644 --- a/code/game/objects/items/inquisition_relics.dm +++ b/code/game/objects/items/inquisition_relics.dm @@ -805,6 +805,10 @@ desc = "Used to wrap around the target." no_attack = TRUE +/obj/item/inqarticles/garrote/Destroy() + reset_garrote() + . = ..() + /obj/item/inqarticles/garrote/atom_break(damage_flag, silent) . = ..() if(!ismob(loc)) @@ -851,7 +855,7 @@ var/mob/living/garrote_victim = victim?.resolve() if(garrote_victim) REMOVE_TRAIT(garrote_victim, TRAIT_MUTE, "garroteCordage") - UnregisterSignal(garrote_victim, list(COMSIG_LIVING_RESIST_GRAB, COMSIG_QDELETING)) + UnregisterSignal(garrote_victim, list(COMSIG_LIVING_RESIST_GRAB, COMSIG_QDELETING, COMSIG_CARBON_ATTEMPT_BREATHE)) victim = null var/mob/living/last_garrote_user = lastuser?.resolve() diff --git a/code/game/objects/items/natural/cloth.dm b/code/game/objects/items/natural/cloth.dm index af35bdbeb20..2a5b0df3391 100644 --- a/code/game/objects/items/natural/cloth.dm +++ b/code/game/objects/items/natural/cloth.dm @@ -32,7 +32,7 @@ /obj/item/natural/cloth/examine(mob/user) . = ..() - . += span_notice("[src] is [PERCENT(bandage_health/initial(bandage_health))]% soaked in blood.") + . += span_notice("[src] is [PERCENT((initial(bandage_health) - bandage_health)/initial(bandage_health))]% soaked in blood.") /obj/item/natural/cloth/Initialize(mapload, vol) . = ..() diff --git a/code/modules/surgery/organs/internal/brain.dm b/code/modules/surgery/organs/internal/brain.dm index 8e495ec2d69..49f960a8863 100644 --- a/code/modules/surgery/organs/internal/brain.dm +++ b/code/modules/surgery/organs/internal/brain.dm @@ -102,7 +102,7 @@ current_blood = max(current_blood - (blood_req * ((BLOOD_VOLUME_NORMAL-effective_blood_oxygenation)/BLOOD_VOLUME_NORMAL) * delta_time * 2), 0) // When all blood is lost, take blood from blood vessels - if(!current_blood && (effective_blood_oxygenation >= BLOOD_VOLUME_SAFE)) + if(!current_blood && (effective_blood_oxygenation >= BLOOD_VOLUME_SURVIVE)) var/obj/item/organ/artery var/obj/item/bodypart/parent = owner.get_bodypart(current_zone) for(var/thing in shuffle(parent?.getorganslotlist(ORGAN_SLOT_ARTERY))) From fbe897f6004a66769b90ba4de9ec89ad296edf7d Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Sat, 23 May 2026 13:01:39 -0700 Subject: [PATCH 56/59] fix runtimes --- code/datums/world_factions/traders/trade_component.dm | 3 ++- code/modules/mob/living/carbon/carbon.dm | 2 +- code/modules/mob/living/grabbing.dm | 2 +- code/modules/surgery/organs/organ_processing/lungs.dm | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/code/datums/world_factions/traders/trade_component.dm b/code/datums/world_factions/traders/trade_component.dm index da8fa290a0d..29de87b26ba 100644 --- a/code/datums/world_factions/traders/trade_component.dm +++ b/code/datums/world_factions/traders/trade_component.dm @@ -230,7 +230,8 @@ Can accept both a type path, and an instance of a datum. Type path has priority. return item_to_buy = new item_to_buy(get_turf(customer)) - customer.put_in_hands(item_to_buy) + if(isitem(item_to_buy)) + customer.put_in_hands(item_to_buy) playsound(trader, trader_data.sell_sound, 50, TRUE) product_info[TRADER_PRODUCT_INFO_QUANTITY] -= 1 trader.say(trader_data.return_trader_phrase(BUY_PHRASE)) diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index ea6d80b160e..4de65bd8a6c 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -252,7 +252,7 @@ return thrown_thing = throwable_mob thrown_speed = 1 - thrown_range = round((GET_MOB_ATTRIBUTE_VALUE(src, STAT_STRENGTH)/GET_MOB_ATTRIBUTE_VALUE(throwable_mob, STAT_CONSTITUTION))*2) + thrown_range = round(( max(GET_MOB_ATTRIBUTE_VALUE(src, STAT_STRENGTH), 1) / max(GET_MOB_ATTRIBUTE_VALUE(throwable_mob, STAT_CONSTITUTION), 1) ) * 2) if(body_position == LYING_DOWN || (!HAS_TRAIT(thrown_thing, TRAIT_TINY) && throwable_mob.cmode && (throwable_mob.body_position != LYING_DOWN || GET_MOB_ATTRIBUTE_VALUE(src, STAT_STRENGTH) < 15))) while(end_T.z > start_T.z) end_T = GET_TURF_BELOW(end_T) diff --git a/code/modules/mob/living/grabbing.dm b/code/modules/mob/living/grabbing.dm index d494cd6b36b..e104c93ac1e 100644 --- a/code/modules/mob/living/grabbing.dm +++ b/code/modules/mob/living/grabbing.dm @@ -305,7 +305,7 @@ var/mob/living/carbon/C = M var/obj/item/clothing/neck/neck_armor = C.wear_neck var/throat_protected = FALSE - if(neck_armor) + if(istype(neck_armor)) throat_protected = (neck_armor.armor_class != ARMOR_CLASS_NONE) if(C.head && istype(C.head, /obj/item/clothing/head/helmet/heavy/necked)) throat_protected = TRUE diff --git a/code/modules/surgery/organs/organ_processing/lungs.dm b/code/modules/surgery/organs/organ_processing/lungs.dm index 8565277de91..4b342bbcf9b 100644 --- a/code/modules/surgery/organs/organ_processing/lungs.dm +++ b/code/modules/surgery/organs/organ_processing/lungs.dm @@ -173,7 +173,7 @@ // Less blood so breaths give you less oxygen var/blood_modifier = 1 if(CAN_HAVE_BLOOD(breather)) - blood_modifier = BLOOD_VOLUME_NORMAL / breather.get_blood_volume() + blood_modifier = breather.get_blood_volume() / BLOOD_VOLUME_NORMAL var/oxygen_req_modifier = 0.97 + (30/max(breather.total_oxygen_req, 1)) * 0.03 breather.adjustOxyLoss(-5 * blood_modifier * lung_efficiency * oxygen_req_modifier * delta_time) From 0f6167a48994618239fa7037a60aebd4d82cbb6e Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Sat, 23 May 2026 14:37:55 -0700 Subject: [PATCH 57/59] brain pain --- code/modules/surgery/organs/internal/brain.dm | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/code/modules/surgery/organs/internal/brain.dm b/code/modules/surgery/organs/internal/brain.dm index 49f960a8863..48d5bc626a8 100644 --- a/code/modules/surgery/organs/internal/brain.dm +++ b/code/modules/surgery/organs/internal/brain.dm @@ -20,6 +20,9 @@ organ_flags = ORGAN_VITAL attack_verb = list("attacked", "slapped", "whacked") + pain_multiplier = 0 // you can't feel your brain being fried + internal_damage_modifier = 0.25 + maxHealth = BRAIN_DAMAGE_DEATH healing_factor = BRAIN_DAMAGE_DEATH/200 low_threshold = BRAIN_DAMAGE_DEATH * 0.25 @@ -90,7 +93,8 @@ return if(!blood_req) return - if(!in_bleedout && (effective_blood_oxygenation >= BLOOD_VOLUME_SAFE)) + // Very low blood, danger!! + if(!in_bleedout && (effective_blood_oxygenation >= BLOOD_VOLUME_BAD)) current_blood = min(current_blood + (blood_req * delta_time), max_blood_storage) return @@ -304,18 +308,6 @@ QDEL_LIST(traumas) return ..() -/obj/item/organ/brain/can_self_heal(delta_time, times_fired) - if(!owner) - return FALSE - if(healing_factor <= 0) - return FALSE - if(self_healing_effect && owner.get_chem_effect(self_healing_effect)) - return TRUE - var/effective_blood_oxygenation = GET_EFFECTIVE_BLOOD_VOL(owner.get_blood_oxygenation(), owner.total_blood_req) - if(effective_blood_oxygenation < BLOOD_VOLUME_SAFE) - return FALSE - return ..() - /obj/item/organ/brain/proc/past_damage_threshold(threshold) return (get_current_damage_threshold() > threshold) From c4fd3f67cdf07625146f44009a716bcc82bb18c3 Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Sat, 23 May 2026 19:07:12 -0700 Subject: [PATCH 58/59] continued work --- code/__DEFINES/DNA.dm | 4 +- code/__DEFINES/maths.dm | 3 + code/__DEFINES/medical.dm | 10 +- code/datums/ai/controllers/mirespider.dm | 6 +- code/datums/blood_types.dm | 9 +- .../status_effects/debuffs/blindness.dm | 11 +- code/datums/status_effects/rogue/healing.dm | 19 ++- code/datums/status_effects/rogue/roguebuff.dm | 44 ------- code/datums/wounds/special.dm | 2 +- .../personal_objectives/zizo/kick_man.dm | 2 +- code/game/objects/items/inquisition_relics.dm | 4 +- code/game/objects/items/needle.dm | 4 +- code/game/objects/structures/guillotine.dm | 2 +- code/game/objects/structures/pillory.dm | 2 +- code/modules/antagonists/villain/zomble.dm | 1 + .../crafting/alchemy/herbal_recipes.dm | 4 +- code/modules/crafting/alchemy/reagents.dm | 6 +- .../crafting/quality_of_crafting/dendor.dm | 1 + code/modules/mob/living/blood.dm | 46 ++++++- code/modules/mob/living/carbon/carbon.dm | 1 + code/modules/mob/living/carbon/human/death.dm | 2 +- code/modules/mob/living/carbon/human/human.dm | 7 +- code/modules/mob/living/carbon/life.dm | 18 ++- code/modules/mob/living/emote.dm | 1 - code/modules/mob/living/grabbing.dm | 2 +- code/modules/mob/living/living.dm | 4 +- code/modules/reagents/chemistry/holder.dm | 75 ++++++++--- code/modules/reagents/chemistry/reagents.dm | 14 +- .../chemistry/reagents/alcohol_reagents.dm | 2 +- .../chemistry/reagents/medicine_reagents.dm | 17 ++- .../chemistry/reagents/other_reagents.dm | 8 +- .../spells/spell_types/pointed/healing.dm | 4 +- .../spells/spell_types/pointed/revive.dm | 2 +- .../spells/spell_types/pointed/transact.dm | 19 ++- .../undirected/bardic/rejuvenation_song.dm | 6 +- code/modules/surgery/bodyparts/_bodyparts.dm | 3 +- .../surgery/bodyparts/bodypart_examine.dm | 2 +- .../surgery/bodyparts/bodypart_wounds.dm | 15 ++- code/modules/surgery/organs/_organ.dm | 122 ++++++++++++------ .../surgery/organs/internal/artery/_artery.dm | 2 +- code/modules/surgery/organs/internal/eyes.dm | 2 +- code/modules/surgery/organs/internal/lungs.dm | 52 ++++++-- .../surgery/organs/organ_processing/lungs.dm | 3 +- 43 files changed, 347 insertions(+), 216 deletions(-) diff --git a/code/__DEFINES/DNA.dm b/code/__DEFINES/DNA.dm index 179fee74522..76c34fdc74c 100644 --- a/code/__DEFINES/DNA.dm +++ b/code/__DEFINES/DNA.dm @@ -78,8 +78,8 @@ //organ defines #define STANDARD_ORGAN_THRESHOLD 100 #define STANDARD_ORGAN_HEALING (50 / 100000) -/// designed to fail organs when left to decay for ~15 minutes -#define STANDARD_ORGAN_DECAY 0.00222 +/// designed to fail organs when left to decay for ~20 minutes +#define STANDARD_ORGAN_DECAY (111 / 100000) * 0.75 //used for the can_chromosome var on mutations #define CHROMOSOME_NEVER 0 diff --git a/code/__DEFINES/maths.dm b/code/__DEFINES/maths.dm index 763ab760e42..40f4afc1e90 100644 --- a/code/__DEFINES/maths.dm +++ b/code/__DEFINES/maths.dm @@ -1,3 +1,6 @@ +#define IS_FINITE__UNSAFE(a) (!isinf(a) && !isnan(a)) +#define IS_FINITE(a) (isnum(a) && IS_FINITE__UNSAFE(a)) + // Credits to Nickr5 for the useful procs I've taken from his library resource. // This file is quadruple wrapped for your pleasure // ( diff --git a/code/__DEFINES/medical.dm b/code/__DEFINES/medical.dm index 143870cd46a..03061d55ee6 100644 --- a/code/__DEFINES/medical.dm +++ b/code/__DEFINES/medical.dm @@ -156,7 +156,7 @@ DEFINE_BITFIELD(organ_flags, list( /// Max damage we consider for damage_organs() #define MAX_CONSIDERED_ORGAN_DAMAGE_ROLL 75 /// ditto but for internal organ damage -#define ORGAN_MINIMUM_DAMAGE 25 +#define ORGAN_MINIMUM_DAMAGE 12.5 //wound severities for /datum/wound /// Wounds that are either surgically induced or too minor to matter @@ -256,7 +256,7 @@ DEFINE_BITFIELD(organ_flags, list( //~brain damage related defines /// We need to take at least this much brainloss gained at once to roll for brain traumas, any less it won't roll -#define TRAUMA_ROLL_THRESHOLD 4.5 +#define TRAUMA_ROLL_THRESHOLD 5 /// Brainloss caused by mildly low blood oxygenation #define BRAIN_DAMAGE_LOW_OXYGENATION 1.5 /// Brainloss caused by lower than low blood oxygenation @@ -281,12 +281,6 @@ DEFINE_BITFIELD(organ_flags, list( #define CPR_MOUTH "m2m" #define CPR_CHEST "cardio" -// ~simple brainloss defines -#define GETBRAINLOSS(mob) mob.getOrganLoss(ORGAN_SLOT_BRAIN) -#define ADJUSTBRAINLOSS(mob, amount) mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, amount) -#define SETBRAINLOSS(mob, amount) mob.setOrganLoss(ORGAN_SLOT_BRAIN, amount) - - // ~arteries #define ARTERIAL_BLOOD_FLOW 20 diff --git a/code/datums/ai/controllers/mirespider.dm b/code/datums/ai/controllers/mirespider.dm index 0d592d8ddd4..4a649a21669 100644 --- a/code/datums/ai/controllers/mirespider.dm +++ b/code/datums/ai/controllers/mirespider.dm @@ -250,6 +250,7 @@ examine_text = "SUBJECTPRONOUN is covered in spider silk... eww!" healing_on_tick = 1 outline_colour = "#4e4c4c00" + effect_color = "#4e4c4c00" var/blood_healing_on_tick = 20 /datum/status_effect/buff/healing/spider_cocoon/on_apply() @@ -262,15 +263,10 @@ if(stat_bonus > 0) healing_on_tick += stat_bonus blood_healing_on_tick += (stat_bonus * 10) - var/filter = owner.get_filter(COCOON_FILTER) - if (!filter) - owner.add_filter(COCOON_FILTER, 2, list("type" = "outline", "color" = outline_colour, "alpha" = 60, "size" = 1)) return TRUE /datum/status_effect/buff/healing/spider_cocoon/tick() . = ..() - var/obj/effect/temp_visual/heal/H = new /obj/effect/temp_visual/heal_rogue(get_turf(owner)) - H.color = "#4e4c4c00" owner.adjust_blood_volume(blood_healing_on_tick, maximum = BLOOD_VOLUME_NORMAL) #undef COCOON_FILTER diff --git a/code/datums/blood_types.dm b/code/datums/blood_types.dm index 8e4dff4ae89..578f9255880 100644 --- a/code/datums/blood_types.dm +++ b/code/datums/blood_types.dm @@ -30,8 +30,15 @@ GLOBAL_LIST_INIT_TYPED(blood_types, /datum/blood_type, init_subtypes_w_path_keys /datum/blood_type/New() . = ..() - compatible_types |= type + compatible_types |= type_key() +/** + * Key used to identify this blood type in compatible_types + * + * Allows for more complex or dynamically generated blood types + */ +/datum/blood_type/proc/type_key() + return type /// Gets data to pass to a reagent /datum/blood_type/proc/get_blood_data(mob/living/sampled_from) diff --git a/code/datums/status_effects/debuffs/blindness.dm b/code/datums/status_effects/debuffs/blindness.dm index a81a6fc94ce..66d1266e6c9 100644 --- a/code/datums/status_effects/debuffs/blindness.dm +++ b/code/datums/status_effects/debuffs/blindness.dm @@ -79,14 +79,11 @@ /datum/status_effect/grouped/blindness/tick() . = ..() - // if(owner.stat == CONSCIOUS && !owner.has_status_effect(STATUS_EFFECT_SLEEPING)) - // owner.overlay_fullscreen(id, /atom/movable/screen/fullscreen/blind) - // else if(owner.has_status_effect(STATUS_EFFECT_SLEEPING)) - // owner.overlay_fullscreen(id, /atom/movable/screen/fullscreen/blind/sleeper) - // else - // owner.overlay_fullscreen(id, /atom/movable/screen/fullscreen/blackimageoverlay) if(owner.stat == CONSCIOUS || owner.has_status_effect(STATUS_EFFECT_SLEEPING)) - owner.overlay_fullscreen(id, /atom/movable/screen/fullscreen/blind/sleeper) + if((length(sources) == 1) && (EYE_DAMAGE in sources)) + owner.overlay_fullscreen(id, /atom/movable/screen/fullscreen/blind) + else + owner.overlay_fullscreen(id, /atom/movable/screen/fullscreen/blind/sleeper) else owner.overlay_fullscreen(id, /atom/movable/screen/fullscreen/blackimageoverlay) diff --git a/code/datums/status_effects/rogue/healing.dm b/code/datums/status_effects/rogue/healing.dm index ce54d13fd9e..17bc5c4c823 100644 --- a/code/datums/status_effects/rogue/healing.dm +++ b/code/datums/status_effects/rogue/healing.dm @@ -22,11 +22,14 @@ /datum/status_effect/buff/healing id = "healing" alert_type = /atom/movable/screen/alert/status_effect/buff/healing - var/visual_type = /obj/effect/temp_visual/heal_rogue duration = 10 SECONDS + var/healing_on_tick = 1 + var/outline_colour = "#c42424" var/outline_alpha = 60 + + var/visual_type = /obj/effect/temp_visual/heal_rogue var/effect_color = "#FF0000" /datum/status_effect/buff/healing/on_creation(mob/living/new_owner, duration_override, new_healing_on_tick) @@ -34,7 +37,16 @@ healing_on_tick = new_healing_on_tick return ..() +/datum/status_effect/buff/healing/on_apply() + . = ..() + if(outline_colour) + owner.add_filter(id, 2, list("type" = "outline", "color" = outline_colour, "alpha" = outline_alpha, "size" = 1)) + /datum/status_effect/buff/healing/tick(seconds_between_ticks) + if(visual_type) + var/obj/effect/temp_visual/heal/H = new /obj/effect/temp_visual/heal_rogue(get_turf(owner)) + H.color = effect_color + owner.adjust_blood_volume(healing_on_tick + 1, maximum = BLOOD_VOLUME_NORMAL) owner.adjustOxyLoss(-healing_on_tick, FALSE) owner.adjustToxLoss(-healing_on_tick, FALSE) @@ -45,3 +57,8 @@ if(length(owner.get_wounds()) && owner.heal_wounds(healing_on_tick, src)) owner.update_damage_overlays() + +/datum/status_effect/buff/healing/on_remove() + . = ..() + if(outline_colour) + owner.remove_filter(id) diff --git a/code/datums/status_effects/rogue/roguebuff.dm b/code/datums/status_effects/rogue/roguebuff.dm index 7e56b708531..363e280c38a 100644 --- a/code/datums/status_effects/rogue/roguebuff.dm +++ b/code/datums/status_effects/rogue/roguebuff.dm @@ -549,50 +549,6 @@ #undef BLOODRAGE_FILTER -/atom/movable/screen/alert/status_effect/buff/matthioshealing - name = "Healing Miracle" - desc = "Strange Divine intervention relieves me of my ailments." - icon_state = "buff" - -#define MIRACLE_HEALING_FILTER "miracle_heal_glow" - -/datum/status_effect/buff/matthioshealing - id = "healing" - alert_type = /atom/movable/screen/alert/status_effect/buff/matthioshealing - duration = 10 SECONDS - var/healing_on_tick = 1 - var/outline_colour = "#c42424" - -/datum/status_effect/buff/matthioshealing/on_creation(mob/living/new_owner, new_healing_on_tick) - healing_on_tick = new_healing_on_tick - return ..() - -/datum/status_effect/buff/matthioshealing/on_apply() - . = ..() - owner.add_filter(MIRACLE_HEALING_FILTER, 2, outline_filter(2, outline_colour)) - return TRUE - -/datum/status_effect/buff/matthioshealing/on_remove() - . = ..() - owner.remove_filter(MIRACLE_HEALING_FILTER) - return TRUE - -/datum/status_effect/buff/matthioshealing/get_examine_text() - return "SUBJECTPRONOUN is bathed in a restorative aura!" - -/datum/status_effect/buff/matthioshealing/tick() - owner.adjust_blood_volume(10, maximum = BLOOD_VOLUME_NORMAL) - if(owner.get_wounds()) - owner.heal_wounds(healing_on_tick) - owner.update_damage_overlays() - owner.adjustBruteLoss(-healing_on_tick, 0) - owner.adjustFireLoss(-healing_on_tick, FALSE) - owner.adjustOxyLoss(-healing_on_tick, FALSE) - owner.adjustToxLoss(-healing_on_tick, FALSE) - owner.adjustOrganLoss(ORGAN_SLOT_BRAIN, -healing_on_tick) - -#undef MIRACLE_HEALING_FILTER //Why is this a thing? - #define CRANKBOX_FILTER "crankboxbuff_glow" /atom/movable/screen/alert/status_effect/buff/churnerprotection name = "Magick Distorted" diff --git a/code/datums/wounds/special.dm b/code/datums/wounds/special.dm index c6d8f47a1b2..00c42ecd64a 100644 --- a/code/datums/wounds/special.dm +++ b/code/datums/wounds/special.dm @@ -174,7 +174,7 @@ "The tongue flies off in an arc!" ) woundpain = 8 - bleed_rate = 1.25 + bleed_rate = 1 can_cauterize = FALSE critical = TRUE associated_bclasses = ARTERY_BCLASSES diff --git a/code/game/gamemodes/personal_objectives/zizo/kick_man.dm b/code/game/gamemodes/personal_objectives/zizo/kick_man.dm index 53d776f2c84..9896a01ea20 100644 --- a/code/game/gamemodes/personal_objectives/zizo/kick_man.dm +++ b/code/game/gamemodes/personal_objectives/zizo/kick_man.dm @@ -17,7 +17,7 @@ /datum/objective/personal/kick_groin/proc/on_kick_attempted(datum/source, mob/living/target, zone_hit, damage_blocked) SIGNAL_HANDLER - if(completed || target.gender != MALE || target.stat == DEAD || zone_hit != BODY_ZONE_PRECISE_GROIN) + if(completed || !target.client || target.gender != MALE || target.stat == DEAD || zone_hit != BODY_ZONE_PRECISE_GROIN) return if(damage_blocked) diff --git a/code/game/objects/items/inquisition_relics.dm b/code/game/objects/items/inquisition_relics.dm index 8860fd203a9..29e1f22dc1a 100644 --- a/code/game/objects/items/inquisition_relics.dm +++ b/code/game/objects/items/inquisition_relics.dm @@ -573,9 +573,9 @@ M.flash_fullscreen("redflash3") if(M.can_feel_pain()) if(prob(15)) - M.emote("whimper", forced = TRUE) + M.emote("whimper") else if(prob(15)) - M.emote("painmoan", forced = TRUE) + M.emote("painmoan") desc = initial(desc) subject = WEAKREF(M) desc += span_notice(" It contains the blood of [M.real_name]!") diff --git a/code/game/objects/items/needle.dm b/code/game/objects/items/needle.dm index 7c774db26f6..126935f63b4 100644 --- a/code/game/objects/items/needle.dm +++ b/code/game/objects/items/needle.dm @@ -1,6 +1,6 @@ /obj/item/needle name = "needle" - desc = "A firm needle affixed with a simple thread, Pestra's most favored tool." + desc = "A firm needle affixed with a simple thread, used to sew up cloth and wounds alike." icon_state = "needle" icon = 'icons/roguetown/items/misc.dmi' w_class = WEIGHT_CLASS_TINY @@ -281,7 +281,7 @@ /obj/item/needle/thorn name = "needle" icon_state = "thornneedle" - desc = "This rough needle can be used to sew cloth and wounds." + desc = "This needle uses a rough thorn, limiting the amount of thread that can be threaded." stringamt = 12 maxstring = 12 anvilrepair = null diff --git a/code/game/objects/structures/guillotine.dm b/code/game/objects/structures/guillotine.dm index d674ccdaab1..b98da0498e1 100644 --- a/code/game/objects/structures/guillotine.dm +++ b/code/game/objects/structures/guillotine.dm @@ -215,7 +215,7 @@ return ..(carbon, force, FALSE) for(var/obj/item/grabbing/G in M.grabbedby) - if(G.grab_state == GRAB_AGGRESSIVE) + if(G.grab_state >= GRAB_AGGRESSIVE) return ..(M, force, FALSE) to_chat(usr, span_warning("I must grab them more forcefully to put them in [src].")) diff --git a/code/game/objects/structures/pillory.dm b/code/game/objects/structures/pillory.dm index 9219a2d4f74..b1dc7147161 100644 --- a/code/game/objects/structures/pillory.dm +++ b/code/game/objects/structures/pillory.dm @@ -98,7 +98,7 @@ return ..(carbon, force, FALSE) for(var/obj/item/grabbing/G in M.grabbedby) - if(G.grab_state == GRAB_AGGRESSIVE) + if(G.grab_state >= GRAB_AGGRESSIVE) return ..(M, force, FALSE) to_chat(usr, span_warning("I must grab them more forcefully to put them in [src].")) diff --git a/code/modules/antagonists/villain/zomble.dm b/code/modules/antagonists/villain/zomble.dm index 723ceaf5014..4cb77bf0882 100644 --- a/code/modules/antagonists/villain/zomble.dm +++ b/code/modules/antagonists/villain/zomble.dm @@ -274,6 +274,7 @@ record_round_statistic(STATS_DEADITES_WOKEN_UP) zombie.set_blood_volume(BLOOD_VOLUME_NORMAL) + zombie.set_heartattack(FALSE) zombie.setOxyLoss(0, updating_health = FALSE, forced = TRUE) //zombles dont breathe zombie.setToxLoss(0, updating_health = FALSE, forced = TRUE) //zombles are immune to poison if(!infected_wake) //if we died, heal all of this too diff --git a/code/modules/crafting/alchemy/herbal_recipes.dm b/code/modules/crafting/alchemy/herbal_recipes.dm index 03b3c9a51b3..efd17a89417 100644 --- a/code/modules/crafting/alchemy/herbal_recipes.dm +++ b/code/modules/crafting/alchemy/herbal_recipes.dm @@ -89,6 +89,7 @@ if(affected_bodypart.post_damage_change()) affected_mob.update_damage_overlays() affected_bodypart.disinfect_limb(20 SECONDS) + return FALSE // Weak Mana/Stamina Potions (based on hypericum/benedictus/mentha) /datum/reagent/medicine/herbal/hypericum_tonic @@ -404,6 +405,7 @@ if(affected_bodypart.heal_damage(0.5, 0.75, TRUE, required_status = BODYPART_ORGANIC)) affected_mob.update_damage_overlays() affected_bodypart.add_pain(-amount_to_transfer * 0.3) + return FALSE /datum/reagent/medicine/herbal/paris_poultice/overdose_process(mob/living/M) M.adjustToxLoss(0.5) @@ -520,7 +522,7 @@ /datum/reagent/medicine/herbal/mentha_oil/on_bodypart_absorb(mob/living/carbon/affected_mob, obj/item/bodypart/affected_bodypart, amount_to_transfer) affected_bodypart.add_pain(-(amount_to_transfer * 0.3)) - . = ..() + return ..() /datum/reagent/medicine/herbal/mentha_oil/on_mob_life(mob/living/carbon/M, efficiency) M.adjust_stamina(1.5 * efficiency) diff --git a/code/modules/crafting/alchemy/reagents.dm b/code/modules/crafting/alchemy/reagents.dm index 9ea2ac6daee..dfca26cc9f2 100644 --- a/code/modules/crafting/alchemy/reagents.dm +++ b/code/modules/crafting/alchemy/reagents.dm @@ -13,7 +13,7 @@ /datum/reagent/medicine/healthpot/on_bodypart_absorb(mob/living/carbon/affected_mob, obj/item/bodypart/affected_bodypart, amount_to_transfer) if(affected_bodypart.heal_damage(1 * REM, 1 * REM, TRUE, required_status = BODYPART_ORGANIC)) affected_mob.update_damage_overlays() - . = ..() + return ..() /datum/reagent/medicine/healthpot/on_mob_metabolize(mob/living/L) . = ..() @@ -49,7 +49,7 @@ /datum/reagent/medicine/healthpot/on_bodypart_absorb(mob/living/carbon/affected_mob, obj/item/bodypart/affected_bodypart, amount_to_transfer) if(affected_bodypart.heal_damage(3 * REM, 3 * REM, TRUE, required_status = BODYPART_ORGANIC)) affected_mob.update_damage_overlays() - . = ..() + return ..() /datum/reagent/medicine/stronghealth/on_mob_metabolize(mob/living/L) . = ..() @@ -222,7 +222,7 @@ for(var/datum/injury/injury in affected_bodypart.injuries) injury.adjust_germ_level(-30) affected_bodypart.adjust_germ_level(-30) - . = ..() + return ..() /datum/reagent/medicine/diseasecure/on_mob_metabolize(mob/living/L) . = ..() diff --git a/code/modules/crafting/quality_of_crafting/dendor.dm b/code/modules/crafting/quality_of_crafting/dendor.dm index e225aac90f6..1b92b62fa4d 100644 --- a/code/modules/crafting/quality_of_crafting/dendor.dm +++ b/code/modules/crafting/quality_of_crafting/dendor.dm @@ -1,5 +1,6 @@ /datum/repeatable_crafting_recipe/dendor abstract_type = /datum/repeatable_crafting_recipe/dendor + subtypes_allowed = TRUE category = "Dendor" /datum/repeatable_crafting_recipe/dendor/sacrifice_growing diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm index 80052d5b4e9..1545d819244 100644 --- a/code/modules/mob/living/blood.dm +++ b/code/modules/mob/living/blood.dm @@ -88,12 +88,20 @@ bleed_rate += bodypart.get_bleed_rate() return bleed_rate +/// Returns the reagent type this mob has for blood +/mob/living/proc/get_blood_reagent() + if (!can_bleed()) + return + + var/datum/blood_type/blood_type = get_blood_type() + return blood_type?.reagent_type + /// Check if a mob can bleed, and possibly if they're capable of leaving decals on turfs/mobs/items /mob/living/proc/can_bleed(bleed_flag = NONE) if (!CAN_HAVE_BLOOD(src)) return BLEED_NONE - if(HAS_TRAIT(src, TRAIT_SIMPLE_WOUNDS)) + if(!iscarbon(src) && HAS_TRAIT(src, TRAIT_SIMPLE_WOUNDS)) return BLEED_NONE if(!iscarbon(src)) @@ -164,8 +172,9 @@ BLOOD TRANSFERS ****************************************************/ -//Gets blood from mob to a container or other mob, preserving all data in it. -/mob/living/proc/transfer_blood_to(atom/movable/receiver, amount, ignore_low_blood) +/// Transfers blood from mob to a container or another mob, preserving all data in it. +/// Returns how much blood was able to be transferred. +/mob/living/proc/transfer_blood_to(atom/movable/receiver, amount, ignore_low_blood = FALSE, ignore_incompatibility = FALSE,) var/cached_blood_volume = get_blood_volume() if(!cached_blood_volume || !receiver.reagents || amount <= 0) @@ -174,7 +183,12 @@ if(cached_blood_volume < BLOOD_VOLUME_BAD && !ignore_low_blood) return 0 - var/datum/blood_type/blood = get_blood_type() + var/datum/blood_type/blood_type = get_blood_type() + if (!blood_type) + return 0 + + var/blood_reagent = get_blood_reagent() + var/list/blood_data = get_blood_data() // Caps the amount to how much blood we have. amount = min(amount, get_blood_volume()) @@ -183,11 +197,33 @@ // Caps the amount to how much we can transfer before reaching low blood. amount = min(amount, get_blood_volume() - BLOOD_VOLUME_BAD) - receiver.reagents.add_reagent(blood.reagent_type, amount, blood.get_blood_data(src), bodytemperature) + var/mob/living/target = receiver + if (!isliving(receiver) || target.get_blood_reagent() != blood_reagent) + // Further caps the amount to how much blood we were able to add to the target. + amount = receiver.reagents.add_reagent(blood_reagent, amount, blood_data, bodytemperature) + adjust_blood_volume(-amount) + return amount + + if(!ignore_incompatibility && !(blood_type.type_key() in target.get_blood_type().compatible_types)) + // Yes, we cap it to the amount of toxin. This is ridiculously niche, but we do it anyway. + amount = target.reagents.add_reagent(/datum/reagent/toxin, amount * 0.5) * 2 + adjust_blood_volume(-amount) + return amount + + receiver.reagents.add_reagent(blood_type.reagent_type, amount, blood_data, bodytemperature) adjust_blood_volume(-amount) return amount +/mob/living/proc/get_blood_data() + SHOULD_CALL_PARENT(TRUE) + RETURN_TYPE(/list) + + var/datum/blood_type/blood_type = get_blood_type() + if (!blood_type || !can_bleed()) + return + return blood_type.get_blood_data(src) + /// Transfers the blood of a mob factoring in the impure reagents in their blood /// Returns the actual amount of blood transferred /mob/living/proc/transfer_blood_impurities(datum/reagents/transfer_to, amount, impurity_mult = BLOODLETTING_MULT, mob/transferred_by) diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 4de65bd8a6c..11189275103 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -1038,6 +1038,7 @@ for(var/obj/item/organ/parent in internal_organs)//we treat this like the initial heart beat filling all the arteries with blood again parent.current_blood = min(parent.current_blood, (parent.current_blood + (parent.max_blood_storage * 0.4))) + pump_heart(forced_pump = 1.3) return ..() diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index a4628a0900c..963b541b5b1 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -64,6 +64,7 @@ zombie_check() stop_sound_channel(CHANNEL_HEARTBEAT) + heartbeat_sound = BEAT_NONE pulse = PULSE_NONE for(var/thing in getorganslotlist(ORGAN_SLOT_HEART)) var/obj/item/organ/heart/heart = thing @@ -156,7 +157,6 @@ . = ..() if(!.) return - pump_heart(forced_pump = 1.3) var/datum/job/human_job = SSjob.GetJob(job) if(human_job) switch(human_job.type) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 55a60311367..51db9d6b516 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -466,8 +466,8 @@ if((diceroll >= DICE_SUCCESS) || (!attributes && prob(35))) looping = FALSE - if(GETBRAINLOSS(target) >= BRAIN_DAMAGE_DEATH) - SETBRAINLOSS(target, BRAIN_DAMAGE_DEATH - 1) + if(target.getOrganLoss(ORGAN_SLOT_BRAIN) >= BRAIN_DAMAGE_DEATH) + target.setOrganLoss(ORGAN_SLOT_BRAIN, BRAIN_DAMAGE_DEATH - 1) if(target.revive()) target.grab_ghost(TRUE) target.visible_message(span_warning("[target] limply spasms their muscles."), \ @@ -660,7 +660,6 @@ hud_used.zone_select.update_appearance(UPDATE_OVERLAYS) /mob/living/carbon/human/fully_heal(heal_flags = HEAL_ALL) - // set_heartattack(FALSE) if(heal_flags & HEAL_ESSENTIALS) set_hygiene(HYGIENE_LEVEL_NORMAL) return ..() @@ -1150,7 +1149,7 @@ used_damage = total_tox if(used_damage < total_oxy) used_damage = total_oxy - set_health(maxHealth - GETBRAINLOSS(src)) + set_health(maxHealth - getOrganLoss(ORGAN_SLOT_BRAIN)) update_stat() update_pain() update_shock() diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index f633bf66b14..150b2a29aa5 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -358,14 +358,21 @@ All effects don't start immediately, but rather get worse over time; the rate is return FALSE return TRUE -/mob/living/proc/set_heartattack(status) - return - -/mob/living/carbon/set_heartattack(status) - if(!can_heartattack()) +/** + * Causes the mob to either start or stop having a heart attack. + * + * status - Pass TRUE to start a heart attack, or FALSE to stop one. + * + * Returns TRUE if heart status was changed (heart attack -> no heart attack, or visa versa) + */ +/mob/living/carbon/proc/set_heartattack(status) + if(status && !can_heartattack()) return FALSE var/list/hearts = getorganslotlist(ORGAN_SLOT_HEART) + if(!length(hearts)) + return FALSE + if(status) pulse = PULSE_NONE ADD_TRAIT(src, TRAIT_DEATHS_DOOR, ASYSTOLE_TRAIT) @@ -375,6 +382,7 @@ All effects don't start immediately, but rather get worse over time; the rate is pulse = PULSE_NORM for(var/obj/item/organ/heart/heart in hearts) heart.Restart() + return TRUE /// Brain is poopy (hardcrit) /mob/living/proc/undergoing_nervous_system_failure() diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm index 4a0ad7181dd..f2a48b77e12 100644 --- a/code/modules/mob/living/emote.dm +++ b/code/modules/mob/living/emote.dm @@ -410,7 +410,6 @@ nomsg = TRUE only_forced_audio = TRUE ignore_silent = TRUE - stat_allowed = HARD_CRIT // ............... E .................. /datum/emote/living/embed diff --git a/code/modules/mob/living/grabbing.dm b/code/modules/mob/living/grabbing.dm index e104c93ac1e..aaa5c93784f 100644 --- a/code/modules/mob/living/grabbing.dm +++ b/code/modules/mob/living/grabbing.dm @@ -387,7 +387,7 @@ var/tackle_time = max(10 + (skill_diff * 2), 1) M.Knockdown(tackle_time) playsound(src,"genblunt",100,TRUE) - if(user.l_grab && user.l_grab.grabbed == M && user.r_grab && user.r_grab.grabbed == M && user.r_grab.grab_state == GRAB_AGGRESSIVE ) + if(user.l_grab && user.l_grab.grabbed == M && user.r_grab && user.r_grab.grabbed == M && user.r_grab.grab_state >= GRAB_AGGRESSIVE) M.visible_message(span_danger("[user] throws [M] to the ground!"), \ span_userdanger("[user] throws me to the ground!"), span_hear("I hear a sickening sound of pugilism!"), COMBAT_MESSAGE_RANGE) else diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index c92d96fe3d0..dd27a6a35ac 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -2211,7 +2211,7 @@ // to_chat(src, span_warning("You can't climb into [over] whilst it's there.")) // return for(var/obj/item/grabbing/G in grabbedby) - if(G.grab_state == GRAB_AGGRESSIVE) + if(G.grab_state >= GRAB_AGGRESSIVE) return var/datum/component/storage = over.GetComponent(/datum/component/storage) if(storage && !istype(storage, /datum/component/storage/concrete/organ)) @@ -2229,7 +2229,7 @@ if(incapacitated()) return for(var/obj/item/grabbing/G in grabbedby) - if(G.grab_state == GRAB_AGGRESSIVE) + if(G.grab_state >= GRAB_AGGRESSIVE) return var/list/pickable_items = list() for(var/obj/item/item in over.get_all_contents()) diff --git a/code/modules/reagents/chemistry/holder.dm b/code/modules/reagents/chemistry/holder.dm index 9b2704add6f..bab8235b4c1 100644 --- a/code/modules/reagents/chemistry/holder.dm +++ b/code/modules/reagents/chemistry/holder.dm @@ -301,10 +301,19 @@ src.handle_reactions() return amount -/datum/reagents/proc/trans_id_to(obj/target, reagent, amount=1, preserve_data=1)//Not sure why this proc didn't exist before. It does now! /N +/datum/reagents/proc/trans_id_to(atom/target, reagent, amount = 1, preserve_data = TRUE)//Not sure why this proc didn't exist before. It does now! /N + if(QDELETED(target) || !total_volume) + return FALSE + + if(!IS_FINITE(amount)) + stack_trace("non finite amount passed to trans_to [amount] amount of reagents") + return FALSE + + if(!isnull(reagent) && !ispath(reagent)) + stack_trace("invalid target reagent id [reagent] passed to trans_to") + return FALSE + var/list/cached_reagents = reagent_list - if (!target) - return var/datum/reagents/R if(!istype(target, /datum/reagents)) if (!target.reagents || src.total_volume<=0 || !src.get_reagent_amount(reagent)) @@ -735,32 +744,56 @@ var/amt = list_reagents[r_id] add_reagent(r_id, amt, data) -/datum/reagents/proc/remove_reagent(reagent, amount, safety)//Added a safety check for the trans_id_to - if(isnull(amount)) - amount = 0 - . = FALSE - CRASH("null amount passed to reagent code") +/** + * Removes a specific reagent. can supress reactions if needed + * Arguments + * + * * [reagent_type][datum/reagent] - the type of reagent + * * amount - the volume to remove + * * include_subtypes - if TRUE will remove the specified amount from all subtypes of reagent_type as well + */ +/datum/reagents/proc/remove_reagent(datum/reagent/reagent_type, amount, safety, include_subtypes = FALSE)//Added a safety check for the trans_id_to + if(!ispath(reagent_type)) + stack_trace("invalid reagent passed to remove reagent [reagent_type]") + return FALSE - if(!isnum(amount)) + if(!IS_FINITE(amount)) + stack_trace("non finite amount passed to remove reagent [amount] [reagent_type]") return FALSE - if(amount < 0) + amount = round(amount, CHEMICAL_QUANTISATION_LEVEL) + if(amount <= 0) return FALSE + var/total_removed_amount = 0 + var/remove_amount = 0 var/list/cached_reagents = reagent_list for(var/datum/reagent/cached_reagent as anything in cached_reagents) - if(cached_reagent.type == reagent) - //clamp the removal amount to be between current reagent amount - //and zero, to prevent removing more than the holder has stored - amount = clamp(amount, 0, cached_reagent.volume) - cached_reagent.volume -= amount - update_total() - if(!safety)//So it does not handle reactions when it need not to - handle_reactions() - SEND_SIGNAL(src, COMSIG_REAGENTS_REM_REAGENT, QDELING(cached_reagent) ? reagent : cached_reagent, amount) - return TRUE + //check for specific type or subtypes + if(!include_subtypes) + if(cached_reagent.type != reagent_type) + continue + else if(!istype(cached_reagent, reagent_type)) + continue - return FALSE + //reduce the volume + remove_amount = min(cached_reagent.volume, amount) + cached_reagent.volume -= remove_amount + + total_removed_amount += remove_amount + + SEND_SIGNAL(src, COMSIG_REAGENTS_REM_REAGENT, QDELING(cached_reagent) ? reagent_type : cached_reagent, amount) + + //if we reached here means we have found our specific reagent type so break + if(!include_subtypes) + break + + //update the holder & handle reactions + update_total() + if(!safety)//So it does not handle reactions when it need not to + handle_reactions() + + return total_removed_amount /** * Check if this holder contains this reagent. diff --git a/code/modules/reagents/chemistry/reagents.dm b/code/modules/reagents/chemistry/reagents.dm index f624aa647df..cf935870c02 100644 --- a/code/modules/reagents/chemistry/reagents.dm +++ b/code/modules/reagents/chemistry/reagents.dm @@ -34,7 +34,8 @@ GLOBAL_LIST_INIT(name2reagent, build_name2reagent()) var/random_reagent_color = FALSE var/alpha = 255 var/can_synth = TRUE // can this reagent be synthesized? (for example: odysseus syringe gun) - var/metabolization_rate = REAGENTS_METABOLISM //how fast the reagent is metabolized by the mob + /// How fast the reagent is metabolized by the mob + var/metabolization_rate = REAGENTS_METABOLISM var/overrides_metab = 0 var/overdose_threshold = 0 var/addiction_threshold = 0 @@ -103,16 +104,9 @@ GLOBAL_LIST_INIT(name2reagent, build_name2reagent()) /datum/reagent/proc/reaction_turf(turf/T, volume) return -/// Call parent to simulate transfering reagent to the mob and instantly metabolizing it. This seems awful. +/// Return TRUE if reagent should be transfered to affected_mob when absorbed through a bodypart /datum/reagent/proc/on_bodypart_absorb(mob/living/carbon/affected_mob, obj/item/bodypart/affected_bodypart, amount_to_transfer) - volume = amount_to_transfer - if(!affected_mob) - return - on_mob_add(affected_mob) - on_mob_metabolize(affected_mob) - on_mob_life(affected_mob, efficiency = 1) - on_mob_end_metabolize(affected_mob) - on_mob_delete(affected_mob) + return TRUE /datum/reagent/proc/on_mob_life(mob/living/carbon/M, efficiency) SHOULD_CALL_PARENT(TRUE) diff --git a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm index d4a233d098d..0f2c9fade8f 100644 --- a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm @@ -24,7 +24,7 @@ for(var/datum/injury/injury in affected_bodypart.injuries) injury.adjust_germ_level(-boozepwr * 0.5) affected_bodypart.adjust_germ_level(-boozepwr * 0.1) - . = ..() + return ..() /datum/reagent/consumable/ethanol/New() . = ..() diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index af1136ec421..40521976911 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -38,12 +38,15 @@ affected_mob.remove_chem_effect(CE_OXYGENATED, "[type]") /datum/reagent/medicine/atropine/on_mob_life(mob/living/carbon/affected_mob, efficiency) - if(affected_mob.health <= affected_mob.crit_threshold) + if(HAS_TRAIT(affected_mob, TRAIT_CRITICAL_CONDITION)) affected_mob.adjustToxLoss(-2 * REM * efficiency , FALSE) affected_mob.adjustBruteLoss(-2* REM * efficiency, FALSE) affected_mob.adjustFireLoss(-2 * REM * efficiency, FALSE) affected_mob.adjustOxyLoss(-5 * REM * efficiency, FALSE) . = TRUE + var/obj/item/organ/lungs/affected_lungs = affected_mob.getorganslot(ORGAN_SLOT_LUNGS) + if(affected_lungs) + affected_mob.losebreath = 0 if(prob(10)) affected_mob.set_dizzy(10 SECONDS * efficiency) affected_mob.adjust_jitter(10 SECONDS * efficiency) @@ -140,7 +143,7 @@ /datum/reagent/medicine/coldvein_compress/on_bodypart_absorb(mob/living/carbon/affected_mob, obj/item/bodypart/affected_bodypart, amount_to_transfer) if(affected_bodypart.heal_damage(0, 2 * REM, required_status = BODYPART_ORGANIC)) affected_mob.update_damage_overlays() - . = ..() + return ..() /datum/reagent/medicine/coldvein_compress/on_mob_life(mob/living/carbon/M, efficiency) if(volume > 0.99) @@ -165,7 +168,7 @@ /datum/reagent/medicine/ichor_of_mending/on_bodypart_absorb(mob/living/carbon/affected_mob, obj/item/bodypart/affected_bodypart, amount_to_transfer) if(affected_bodypart.heal_damage(3.5 * REM, 0, required_status = BODYPART_ORGANIC)) affected_mob.update_damage_overlays() - . = ..() + return ..() /datum/reagent/medicine/ashbinders_salve name = "Ashbinder's Salve" @@ -192,7 +195,7 @@ if(affected_bodypart.post_damage_change()) affected_mob.update_damage_overlays() affected_bodypart.disinfect_limb(30 SECONDS) - . = ..() + return ..() /datum/reagent/medicine/vitalroot_draught name = "Vitalroot Draught" @@ -257,7 +260,7 @@ injury.adjust_germ_level(-20) affected_bodypart.disinfect_limb(3 MINUTES) affected_bodypart.adjust_germ_level(-25) - . = ..() + return ..() /datum/reagent/medicine/mirewort_compress/on_mob_life(mob/living/carbon/M, efficiency) if(volume > 0.99) @@ -282,7 +285,7 @@ if(affected_bodypart.post_damage_change()) affected_mob.update_damage_overlays() affected_bodypart.adjust_germ_level(-10) - . = ..() + return ..() /datum/reagent/medicine/woundwrack_oil/on_mob_life(mob/living/carbon/M, efficiency) if(volume > 0.99) @@ -415,7 +418,7 @@ if(affected_bodypart.post_damage_change()) affected_mob.update_damage_overlays() affected_bodypart.adjust_germ_level(-15) - . = ..() + return ..() /datum/reagent/medicine/witchknit_paste/on_mob_life(mob/living/carbon/M, efficiency) if(volume > 0.99) diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index 9ab8123de03..e3a559c83f8 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -11,6 +11,7 @@ glass_desc = "" shot_glass_icon_state = "shotglassred" penetrates_skin = NONE + liver_chemical = FALSE var/toxicity = 0.7 // how toxic will this be to digest to people who cannot drink it /datum/reagent/blood/tiefling @@ -25,6 +26,9 @@ taste_mult = 1.8 toxicity = 3 +/datum/reagent/blood/on_bodypart_absorb(mob/living/carbon/affected_mob, obj/item/bodypart/affected_bodypart, amount_to_transfer) + return FALSE + /datum/reagent/blood/on_transfer(atom/A, method=TOUCH, trans_volume) if(!ismob(A)) data["preferences"] &= ~(BLOOD_PREFERENCE_LIVING|BLOOD_PREFERENCE_SLEEPING) @@ -162,13 +166,13 @@ affected_bodypart.undisinfect_limb() for(var/datum/injury/injury in affected_bodypart.injuries) injury.adjust_germ_level(SANITIZATION_PER_UNIT_WATER) - . = ..() + return ..() /datum/reagent/water/gross/on_aeration(volume, turf/turf) turf.pollute_turf(/datum/pollutant/rot/sewage, volume * 3) /datum/reagent/water/gross/on_mob_life(mob/living/carbon/M, efficiency) - ..() + . = ..() if(HAS_TRAIT(M, TRAIT_NASTY_EATER)) // lets orcs and goblins drink bogwater return M.adjustToxLoss(1 * efficiency) diff --git a/code/modules/spells/spell_types/pointed/healing.dm b/code/modules/spells/spell_types/pointed/healing.dm index 100c58b6187..4294da09e3e 100644 --- a/code/modules/spells/spell_types/pointed/healing.dm +++ b/code/modules/spells/spell_types/pointed/healing.dm @@ -24,7 +24,7 @@ /// Wound healing modifier var/wound_modifier = 0.25 /// Blood healing amount - var/blood_restoration = 0 + var/blood_restoration = BLOOD_VOLUME_SURVIVE / 6 /// Stuns undead var/stun_undead = FALSE /// What kind of healing is it? @@ -307,7 +307,7 @@ base_healing = 25 wound_modifier = 0.5 - blood_restoration = BLOOD_VOLUME_SURVIVE + blood_restoration = BLOOD_VOLUME_SURVIVE / 2 stun_undead = TRUE patron_restrictive = TRUE diff --git a/code/modules/spells/spell_types/pointed/revive.dm b/code/modules/spells/spell_types/pointed/revive.dm index 4c8297463a8..f53b92d156e 100644 --- a/code/modules/spells/spell_types/pointed/revive.dm +++ b/code/modules/spells/spell_types/pointed/revive.dm @@ -87,7 +87,7 @@ return if(cast_on.health > HALFWAYCRITDEATH) cast_on.adjustOxyLoss(cast_on.health - HALFWAYCRITDEATH) - ADJUSTBRAINLOSS(cast_on, -100) + cast_on.adjustOrganLoss(ORGAN_SLOT_BRAIN, -100) cast_on.reagents.add_reagent(/datum/reagent/medicine/atropine, 20) cast_on.grab_ghost(force = TRUE, grab_spirit = TRUE) // even suicides record_round_statistic(STATS_ASTRATA_REVIVALS) diff --git a/code/modules/spells/spell_types/pointed/transact.dm b/code/modules/spells/spell_types/pointed/transact.dm index 0b375ed24fe..b24ad3ea4fb 100644 --- a/code/modules/spells/spell_types/pointed/transact.dm +++ b/code/modules/spells/spell_types/pointed/transact.dm @@ -51,7 +51,7 @@ to_chat(owner, "[held_item] burns into the air suddenly, my Transaction is accepted.") if(iscarbon(cast_on)) var/mob/living/carbon/C = cast_on - var/datum/status_effect/buff/matthioshealing/heal_effect = C.apply_status_effect(/datum/status_effect/buff/matthioshealing) + var/datum/status_effect/buff/healing/matthioshealing/heal_effect = C.apply_status_effect(/datum/status_effect/buff/healing/matthioshealing) heal_effect.healing_on_tick = helditemvalue/2 else cast_on.adjustBruteLoss(helditemvalue / 2) @@ -59,3 +59,20 @@ playsound(owner, 'sound/combat/hits/burn (2).ogg', 100, TRUE) if(!QDELETED(held_item)) qdel(held_item) // we might already be qdeleting from mob holder + +/datum/status_effect/buff/healing/matthioshealing + id = "healing" + alert_type = /atom/movable/screen/alert/status_effect/buff/matthioshealing + examine_text = "SUBJECTPRONOUN is bathed in a restorative aura!" + duration = 10 SECONDS + healing_on_tick = 1 + outline_colour = "#c42424" + +/datum/status_effect/buff/healing/matthioshealing/tick() + . = ..() + owner.adjust_blood_volume(10, maximum = BLOOD_VOLUME_NORMAL) + +/atom/movable/screen/alert/status_effect/buff/matthioshealing + name = "Healing Miracle" + desc = "Strange Divine intervention relieves me of my ailments." + icon_state = "buff" diff --git a/code/modules/spells/spell_types/undirected/bardic/rejuvenation_song.dm b/code/modules/spells/spell_types/undirected/bardic/rejuvenation_song.dm index 62b0262d58d..606c0d1cd23 100644 --- a/code/modules/spells/spell_types/undirected/bardic/rejuvenation_song.dm +++ b/code/modules/spells/spell_types/undirected/bardic/rejuvenation_song.dm @@ -17,11 +17,7 @@ duration = 15 SECONDS healing_on_tick = 0.6 // Lesser bard (66%) outline_colour = "#c92f2f" + effect_color = "#660759" /datum/status_effect/buff/healing/rejuvenationsong/full healing_on_tick = 1 // Full bard (100%) - -/datum/status_effect/buff/healing/rejuvenationsong/tick() - . = ..() - var/obj/effect/temp_visual/heal/H = new /obj/effect/temp_visual/heal_rogue(get_turf(owner)) - H.color = "#660759" diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index d05dfc4fa6e..a2abb3e22b4 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -172,7 +172,7 @@ if(isnull(max_pain_damage)) max_pain_damage = max_damage * 1.5 if(isnull(organ_damage_requirement)) - organ_damage_requirement = max_damage * 0.4 + organ_damage_requirement = max_damage * 0.2 if(isnull(organ_damage_hit_minimum)) organ_damage_hit_minimum = ORGAN_MINIMUM_DAMAGE @@ -534,6 +534,7 @@ new_injury.stages = list("surgical incision" = 0) new_injury.desc_list = list("surgical incision") new_injury.damage_list = list(0) + new_injury.autoheal_cutoff = 0 else for(var/datum/injury/other in injuries) if(other.can_merge(new_injury)) diff --git a/code/modules/surgery/bodyparts/bodypart_examine.dm b/code/modules/surgery/bodyparts/bodypart_examine.dm index 23330e4fc5b..08b35c16e9a 100644 --- a/code/modules/surgery/bodyparts/bodypart_examine.dm +++ b/code/modules/surgery/bodyparts/bodypart_examine.dm @@ -236,7 +236,7 @@ if(get_cut(ignore_gauze = TRUE)) status += span_artery(uppertext("cut [parse_zone(possible_artery.zone)]")) else - status += span_bloody(uppertext("internal bleeding")) + status += span_artery(uppertext("internal bleeding")) if(skeletonized) status += "SKELETON" diff --git a/code/modules/surgery/bodyparts/bodypart_wounds.dm b/code/modules/surgery/bodyparts/bodypart_wounds.dm index 99c4b5e3770..2f0aaa4afc8 100644 --- a/code/modules/surgery/bodyparts/bodypart_wounds.dm +++ b/code/modules/surgery/bodyparts/bodypart_wounds.dm @@ -393,15 +393,18 @@ if(cloth.reagents && cloth.reagents.total_volume > 0) if(owner && owner.reagents) - for(var/datum/reagent/R in cloth.reagents.reagent_list) - var/amount_to_transfer = min(R.volume, R.metabolization_rate) + for(var/datum/reagent/reagent in cloth.reagents.reagent_list) + if(istype(reagent, /datum/reagent/blood)) + continue + var/amount_to_transfer = min(reagent.volume, reagent.metabolization_rate) if(amount_to_transfer > 0) - - R.on_bodypart_absorb(owner, src, amount_to_transfer) - cloth.reagents.remove_reagent(R.type, amount_to_transfer) + if(reagent.on_bodypart_absorb(owner, src, amount_to_transfer)) + cloth.reagents.trans_id_to(owner, reagent.type, amount_to_transfer) + else + cloth.reagents.remove_reagent(reagent.type, amount_to_transfer) if(owner) - owner.transfer_blood_to(cloth, bleed_rate * 0.25) + owner.transfer_blood_to(cloth, bleed_rate * 0.1) cloth.bandage_health -= bleed_rate bandage_health = cloth.bandage_health diff --git a/code/modules/surgery/organs/_organ.dm b/code/modules/surgery/organs/_organ.dm index ac1f7decb7c..fb5ba578f3a 100644 --- a/code/modules/surgery/organs/_organ.dm +++ b/code/modules/surgery/organs/_organ.dm @@ -26,12 +26,13 @@ // DO NOT add slots with matching names to different zones - it will break internal_organs_slot list! var/organ_flags = 0 - /// Damage healed per second - var/healing_factor = STANDARD_ORGAN_HEALING /// Minimum amount of germ_level we gain when rotting - var/min_decay_factor = MIN_ORGAN_DECAY_INFECTION + var/min_germ_factor = MIN_ORGAN_DECAY_INFECTION /// Maximum amount of germ_level we gain when rotting - var/max_decay_factor = MAX_ORGAN_DECAY_INFECTION + var/max_germ_factor = MAX_ORGAN_DECAY_INFECTION + /// Healing factor and decay factor function on % of maxhealth, and do not work by applying a static number per tick + var/healing_factor = STANDARD_ORGAN_HEALING + var/decay_factor = STANDARD_ORGAN_DECAY /// Maximum amount of damage we can suffer var/maxHealth = STANDARD_ORGAN_THRESHOLD /// Total damage this organ has sustained @@ -89,7 +90,7 @@ /// How much blood (percent of BLOOD_VOLUME_NORMAL) an organ takes to funcion var/blood_req = 0 - /// If oxygen reqs are not satisfied, get debuffs and brain starts taking damage + /// Determines lung oxygen restoration and suffocation amount var/oxygen_req = 0 /// Controls passive nutriment loss. Units are nutriment_req/100 per second var/nutriment_req = 0 @@ -234,7 +235,10 @@ artery.current_blood = max(artery.current_blood - (blood_req * delta_time), 0) current_blood = max(prev_blood - artery.current_blood, 0) if((current_blood <= 0) && !(organ_flags & ORGAN_LIMB_SUPPORTER)) - applyOrganDamage(0.2 * delta_time) + var/temperature_mod = 1 + if(owner?.bodytemperature > BODYTEMP_NORMAL) + temperature_mod += round((owner.bodytemperature - BODYTEMP_NORMAL) / (BODYTEMP_MAX_TEMPERATURE - BODYTEMP_NORMAL), 0.1) + applyOrganDamage(decay_factor * maxHealth * temperature_mod * delta_time) /obj/item/organ/proc/generate_chimeric_organ(mob/living/source_mob) if(!source_mob) @@ -361,7 +365,7 @@ /// proper decaying /obj/item/organ/proc/decay(delta_time) - adjust_germ_level(rand(min_decay_factor,max_decay_factor) * delta_time) + adjust_germ_level(rand(min_germ_factor, max_germ_factor) * delta_time) /obj/item/organ/adjust_germ_level(add_germs, minimum_germs = 0, maximum_germs = INFECTION_LEVEL_THREE) . = ..() @@ -382,8 +386,8 @@ organ_flags |= ORGAN_CUT_AWAY if(can_decay()) decay(delta_time) - else - STOP_PROCESSING(SSobj, src) + // else + // STOP_PROCESSING(SSobj, src) /// Infection/rot checks /obj/item/organ/proc/can_decay() @@ -451,7 +455,7 @@ //Cause organ damage about once every ~30 seconds //The bodypart deals with dealing raw toxin damage, let's not stack onto the problem now if(DT_PROB(2, delta_time)) - applyOrganDamage(2) + applyOrganDamage(decay_factor * maxHealth * delta_time) // Organ is just completely dead by this point if(germ_level >= INFECTION_LEVEL_THREE && antibiotics < 40) @@ -501,7 +505,7 @@ failure_time = max(0, failure_time - delta_time) // Damage decrements by a percent of maxhealth - if(can_self_heal(delta_time, times_fired) && damage) + if(can_self_heal(delta_time, times_fired)) handle_self_healing(delta_time, times_fired) ///Organs don't die instantly, and neither should you when you get fucked up @@ -651,7 +655,7 @@ ///SETS an organ's damage to the amount "d", and in doing so clears or sets the failing flag, good for when you have an effect that should fix an organ if broken /obj/item/organ/proc/setOrganDamage(d) //use mostly for admin heals - applyOrganDamage(d - damage) + return applyOrganDamage(d - damage) /// This should only be used by arteries, tendons and nerves /obj/item/organ/proc/tear() @@ -672,40 +676,74 @@ if(damage == prev_damage) return var/delta = damage - prev_damage + var/message = "" if(delta > 0) - if(damage >= maxHealth && prev_damage < maxHealth) + if(damage >= low_threshold && prev_damage < low_threshold) + on_low_damage_received() + message = low_threshold_passed + if(damage >= medium_threshold && prev_damage < medium_threshold) + on_medium_damage_received() + message = medium_threshold_passed + if(damage >= high_threshold && prev_damage < high_threshold) organ_flags |= ORGAN_FAILING + on_begin_failure() + message = high_threshold_passed + if(damage >= maxHealth && prev_damage < maxHealth) if(!(organ_flags & ORGAN_INDESTRUCTIBLE)) organ_flags |= ORGAN_DESTROYED - return now_failing - if(damage >= high_threshold && prev_damage < high_threshold) - organ_flags |= ORGAN_FAILING - return high_threshold_passed - if(damage >= medium_threshold && prev_damage < medium_threshold) - return medium_threshold_passed - if(damage >= low_threshold && prev_damage < low_threshold) - return low_threshold_passed - if(delta < 0) - if(prev_damage >= low_threshold && damage < low_threshold) - organ_flags &= ~ORGAN_FAILING - if(organ_flags & ORGAN_DESTROYED) - organ_flags &= ~ORGAN_DESTROYED //I am having pity on people here at this point I won't force you to get new organs unless they fully necrose. - scar_organ(10, 60) - return low_threshold_cleared - if(prev_damage >= medium_threshold && damage < medium_threshold) - organ_flags &= ~ORGAN_FAILING - if(organ_flags & ORGAN_DESTROYED) - organ_flags &= ~ORGAN_DESTROYED //I am having pity on people here at this point I won't force you to get new organs unless they fully necrose. - scar_organ(10, 60) - return medium_threshold_cleared - if(prev_damage >= high_threshold && damage < high_threshold) - organ_flags &= ~ORGAN_FAILING - if(organ_flags & ORGAN_DESTROYED) - organ_flags &= ~ORGAN_DESTROYED //I am having pity on people here at this point I won't force you to get new organs unless they fully necrose. - scar_organ(10, 60) - return high_threshold_cleared - if(prev_damage >= maxHealth && damage < maxHealth) - return now_fixed + on_destroy_damage() + message = now_failing + return message + + if(prev_damage >= maxHealth && damage < maxHealth) + if(organ_flags & ORGAN_DESTROYED) + organ_flags &= ~ORGAN_DESTROYED //I am having pity on people here at this point I won't force you to get new organs unless they fully necrose. + scar_organ(10, 60) + on_destroy_fixed() + message = now_fixed + if(prev_damage >= high_threshold && damage < high_threshold) + organ_flags &= ~ORGAN_FAILING + on_failure_recovery() + message = high_threshold_cleared + if(prev_damage >= medium_threshold && damage < medium_threshold) + on_medium_damage_healed() + message = medium_threshold_cleared + if(prev_damage >= low_threshold && damage < low_threshold) + on_low_damage_healed() + message = low_threshold_cleared + return message + +/** + * Called when the damage surpasses the low damage threshold. + * + * This and other procs like this one merely exist to make it easier to keep a standard on + * damage thresholds for organs. This doesn't mean you cannot make custom thresholds for various stuff, + * and you're more than welcome to improve or refactor any portion of the code around these mechanics + */ +/obj/item/organ/proc/on_low_damage_received() + return + +///Called when the damage goes below the low damage threshold +/obj/item/organ/proc/on_low_damage_healed() + return + +/obj/item/organ/proc/on_medium_damage_received() + return + +/obj/item/organ/proc/on_medium_damage_healed() + return + +/obj/item/organ/proc/on_begin_failure() + return + +/obj/item/organ/proc/on_failure_recovery() + return + +/obj/item/organ/proc/on_destroy_damage() + return + +/obj/item/organ/proc/on_destroy_fixed() + return /obj/item/organ/on_enter_storage(datum/component/storage/concrete/S) . = ..() diff --git a/code/modules/surgery/organs/internal/artery/_artery.dm b/code/modules/surgery/organs/internal/artery/_artery.dm index 80f36aed464..c118097fc27 100644 --- a/code/modules/surgery/organs/internal/artery/_artery.dm +++ b/code/modules/surgery/organs/internal/artery/_artery.dm @@ -37,7 +37,7 @@ /obj/item/organ/artery/on_life(delta_time, times_fired) . = ..() // Dead, pulseless or cryosleep people do not pump blood - if(!(is_bruised() || is_failing()) || !owner.pulse || (owner.bodytemperature <= -15)) + if(!is_bruised() || !owner.pulse || (owner.bodytemperature <= -15)) return var/bleed_mod = 1 * (damage/maxHealth) var/obj/item/bodypart/limb = owner.get_bodypart(current_zone) diff --git a/code/modules/surgery/organs/internal/eyes.dm b/code/modules/surgery/organs/internal/eyes.dm index 65672ff74eb..bef40d9bf94 100644 --- a/code/modules/surgery/organs/internal/eyes.dm +++ b/code/modules/surgery/organs/internal/eyes.dm @@ -22,7 +22,7 @@ low_threshold_passed = "Distant objects become somewhat less tangible." high_threshold_passed = "Everything starts to look a lot less clear." - now_failing = "Darkness envelops me, as my eye goes blind!" + now_failing = "Darkness envelops me, as my eyes goes blind!" now_fixed = "Color and shapes are once again perceivable." high_threshold_cleared = "My vision functions passably once more." low_threshold_cleared = "My vision is cleared of any ailment." diff --git a/code/modules/surgery/organs/internal/lungs.dm b/code/modules/surgery/organs/internal/lungs.dm index f1d6c1f6f42..eee49f611cc 100644 --- a/code/modules/surgery/organs/internal/lungs.dm +++ b/code/modules/surgery/organs/internal/lungs.dm @@ -27,22 +27,46 @@ food_type = /obj/item/reagent_containers/food/snacks/meat/organ/lungs -/obj/item/organ/lungs/on_life(delta_time, times_fired) +/obj/item/organ/lungs/applyOrganDamage(d, maximum) . = ..() - if(failed) - if(!is_failing()) - failed = FALSE - return - else if(is_failing()) - if(owner.stat == CONSCIOUS) - owner.visible_message("[owner] grabs [owner.p_their()] throat, struggling for breath!", \ - "I suddenly feel like you can't breathe!") - to_chat(owner, span_userdanger("I CAN'T BREATHE!")) + if(!.) + return + + if(is_failing()) + if(!owner?.incapacitated()) + owner.visible_message( + span_danger("[owner] grabs [owner.p_their()] throat, struggling for breath!"), + span_userdanger("You suddenly feel like you can't breathe!"), + ) failed = TRUE - if(damage >= low_threshold) - var/do_i_cough = DT_PROB((damage < high_threshold) ? 2.5 : 5, delta_time) // between : past high - if(do_i_cough) - owner.emote("cough") + + else if(failed) + failed = FALSE + +/obj/item/organ/lungs/on_life(seconds_per_tick, times_fired) + . = ..() + if(!is_bruised()) + return + + var/cough_prob = 2.5 + if(damage >= medium_threshold) + cough_prob = 5 + + if(!SPT_PROB(cough_prob, seconds_per_tick)) // between : past high + return + + if((damage >= medium_threshold) && prob(33)) + owner.visible_message(span_danger("[owner] coughs up blood!"), span_userdanger("You cough up blood!")) + var/obj/item/covering = owner.is_mouth_covered() + if(covering) + covering.add_mob_blood(owner) + else if(isturf(owner.loc)) + owner.add_splatter_floor() + owner.bleed(round(damage / 8)) + playsound(owner, pick('sound/vo/throat.ogg','sound/vo/throat2.ogg','sound/vo/throat3.ogg'), 33, TRUE) + else + owner.emote(pick("weeze", "cough")) + owner.losebreath = min(owner.losebreath + round(damage / 100, 0.1), 4) /obj/item/organ/lungs/on_owner_examine(datum/source, mob/user, list/examine_list) if(!ishuman(owner)) diff --git a/code/modules/surgery/organs/organ_processing/lungs.dm b/code/modules/surgery/organs/organ_processing/lungs.dm index 4b342bbcf9b..c6ff4a405f7 100644 --- a/code/modules/surgery/organs/organ_processing/lungs.dm +++ b/code/modules/surgery/organs/organ_processing/lungs.dm @@ -174,7 +174,8 @@ var/blood_modifier = 1 if(CAN_HAVE_BLOOD(breather)) blood_modifier = breather.get_blood_volume() / BLOOD_VOLUME_NORMAL - var/oxygen_req_modifier = 0.97 + (30/max(breather.total_oxygen_req, 1)) * 0.03 + + var/oxygen_req_modifier = clamp(30/max(breather.total_oxygen_req, 1), 0.5, 1.5) breather.adjustOxyLoss(-5 * blood_modifier * lung_efficiency * oxygen_req_modifier * delta_time) /obj/item/organ/lungs/proc/breathe_pollution(mob/living/carbon/breather, breath, delta_time) From 5a938716ca3d9c0b4463b46ed444a9e5453371ef Mon Sep 17 00:00:00 2001 From: PotatoTomahto Date: Sat, 23 May 2026 20:31:30 -0700 Subject: [PATCH 59/59] organ stuff --- code/datums/wounds/special.dm | 10 +- code/modules/surgery/organs/_organ.dm | 19 ++- code/modules/surgery/organs/external/ears.dm | 2 - code/modules/surgery/organs/internal/brain.dm | 143 ++++++++++-------- code/modules/surgery/organs/internal/eyes.dm | 2 - code/modules/surgery/organs/internal/liver.dm | 3 - .../surgery/organs/internal/stomach.dm | 4 - .../surgery/organs/internal/vocal_cords.dm | 1 - 8 files changed, 97 insertions(+), 87 deletions(-) diff --git a/code/datums/wounds/special.dm b/code/datums/wounds/special.dm index 00c42ecd64a..7cb127e2976 100644 --- a/code/datums/wounds/special.dm +++ b/code/datums/wounds/special.dm @@ -8,8 +8,15 @@ sewn_bleed_rate = 0 can_cauterize = FALSE critical = FALSE + associated_bclasses = STAB_BCLASSES viable_zones = list(BODY_ZONE_HEAD) + // Most of these crits dismember organs and permanently hamper the target, so we're making them harder + min_damage = 10 + min_damage_dividend = 0.5 + dividend_multi = 10 + damage_divisor = 12 + /datum/wound/facial/can_stack_with(datum/wound/other) if(istype(other, /datum/wound/facial) && (type == other.type)) return FALSE @@ -26,7 +33,6 @@ bleed_rate = 4 can_cauterize = TRUE critical = TRUE - associated_bclasses = STAB_BCLASSES viable_zones = list(BODY_ZONE_PRECISE_EARS) /datum/wound/facial/ears/can_apply_to_bodypart(obj/item/bodypart/affected) @@ -177,7 +183,6 @@ bleed_rate = 1 can_cauterize = FALSE critical = TRUE - associated_bclasses = ARTERY_BCLASSES viable_zones = list(BODY_ZONE_PRECISE_MOUTH) var/permanent = FALSE @@ -219,7 +224,6 @@ can_sew = FALSE can_cauterize = FALSE critical = TRUE - associated_bclasses = STAB_BCLASSES viable_zones = list(BODY_ZONE_HEAD) /datum/wound/facial/disfigurement/on_mob_gain(mob/living/affected) diff --git a/code/modules/surgery/organs/_organ.dm b/code/modules/surgery/organs/_organ.dm index fb5ba578f3a..8a705598d9e 100644 --- a/code/modules/surgery/organs/_organ.dm +++ b/code/modules/surgery/organs/_organ.dm @@ -642,16 +642,19 @@ return effective_efficiency ///Adjusts an organ's damage by the amount "d", up to a maximum amount, which is by default max damage -/obj/item/organ/proc/applyOrganDamage(d, maximum = maxHealth) //use for damaging effects - if(!d) //Micro-optimization. - return +/obj/item/organ/proc/applyOrganDamage(damage_amount, maximum = maxHealth) //use for damaging effects + if(!damage_amount) //Micro-optimization. + return FALSE + maximum = clamp(maximum, 0, maxHealth) // the logical max is, our max if(maximum < damage) - return - damage = CLAMP(damage + d, 0, maximum) - var/mess = check_damage_thresholds(owner) + return FALSE + damage = clamp(damage + damage_amount, 0, maximum) + . = (prev_damage - damage) // return net damage + var/message = check_damage_thresholds() prev_damage = damage - if(mess && owner) - to_chat(owner, mess) + + if(message && owner) + to_chat(owner, message) ///SETS an organ's damage to the amount "d", and in doing so clears or sets the failing flag, good for when you have an effect that should fix an organ if broken /obj/item/organ/proc/setOrganDamage(d) //use mostly for admin heals diff --git a/code/modules/surgery/organs/external/ears.dm b/code/modules/surgery/organs/external/ears.dm index 5dd89a0d47b..586ed8eb42a 100644 --- a/code/modules/surgery/organs/external/ears.dm +++ b/code/modules/surgery/organs/external/ears.dm @@ -10,8 +10,6 @@ side = RIGHT_SIDE sellprice = DEFAULT_ORGAN_VALUE/2 - healing_factor = STANDARD_ORGAN_HEALING - organ_volume = 0.25 max_blood_storage = 2.5 current_blood = 2.5 diff --git a/code/modules/surgery/organs/internal/brain.dm b/code/modules/surgery/organs/internal/brain.dm index 48d5bc626a8..620f282c3aa 100644 --- a/code/modules/surgery/organs/internal/brain.dm +++ b/code/modules/surgery/organs/internal/brain.dm @@ -21,11 +21,14 @@ attack_verb = list("attacked", "slapped", "whacked") pain_multiplier = 0 // you can't feel your brain being fried - internal_damage_modifier = 0.25 + + ///The brain's organ variables are significantly more different than the other organs, with half the decay rate for balance reasons, and twice the maxHealth + decay_factor = STANDARD_ORGAN_DECAY * 0.5 maxHealth = BRAIN_DAMAGE_DEATH - healing_factor = BRAIN_DAMAGE_DEATH/200 + healing_factor = 0.01 //1% low_threshold = BRAIN_DAMAGE_DEATH * 0.25 + medium_threshold = BRAIN_DAMAGE_DEATH * 0.5 high_threshold = BRAIN_DAMAGE_DEATH * 0.75 // the volume shouldn't worry you, the chest is full of organs - also getting shot in the heart sucks @@ -38,8 +41,6 @@ hydration_req = 1.5 self_healing_effect = CE_BRAIN_REGEN - COOLDOWN_DECLARE(trauma_cooldown) - /// This is stuff var/damage_threshold_value = BRAIN_DAMAGE_DEATH/10 @@ -71,9 +72,12 @@ //Update the body's icon so it doesnt appear debrained anymore C.update_body() + if(damage >= medium_threshold) + C.add_stress(/datum/stress_event/brain_damage) /obj/item/organ/brain/Remove(mob/living/carbon/C, special = 0, no_id_transfer = FALSE) . = ..() + update_brain_color(animate = FALSE) // once it's out in the world we need to make sure it's the right color for(var/datum/brain_trauma/BT as anything in traumas) BT.on_lose(TRUE) BT.owner = null @@ -81,7 +85,7 @@ if((!gc_destroyed || (owner && !owner.gc_destroyed)) && !no_id_transfer) transfer_identity(C) C.update_body() - + C.remove_stress(/datum/stress_event/brain_damage) /obj/item/organ/brain/handle_blood(delta_time, times_fired) var/effective_blood_oxygenation = GET_EFFECTIVE_BLOOD_VOL(owner.get_blood_oxygenation(), owner.total_blood_req) @@ -314,30 +318,78 @@ /obj/item/organ/brain/proc/get_current_damage_threshold() return FLOOR(damage / damage_threshold_value, 1) -/obj/item/organ/brain/check_damage_thresholds(mob/M) +/obj/item/organ/brain/applyOrganDamage(amount, maximum, silent) . = ..() - // if we're not more injured than before, return without gambling for a trauma - if(damage <= prev_damage) + var/delta_dam = . //for the sake of clarity + if(delta_dam < 0 && damage >= BRAIN_DAMAGE_MILD && (-delta_dam >= TRAUMA_ROLL_THRESHOLD)) + roll_for_brain_trauma(-delta_dam) // parent call returns negative numbers if take damage and positive if we heal + + if(-delta_dam >= 10) + var/damage_side_effect = CEILING(-delta_dam / 2, 1) + if(damage_side_effect >= 1) + //owner.flash_pain(damage_side_effect*4) + owner.adjust_eye_blur(damage_side_effect) + owner.adjust_confusion(damage_side_effect) + switch(rand(1,3)) + if(1) + owner.stuttering += damage_side_effect + if(2) + owner.slurring += damage_side_effect + if(3) + owner.cultslurring += damage_side_effect + owner.CombatKnockdown(damage_side_effect*2, damage_side_effect, (damage_side_effect >= 5 ? damage_side_effect : null), damage_side_effect >= 5) + +/// Rolls a random chance to gain a brain trauma based on damage taken and current damage level +/obj/item/organ/brain/proc/roll_for_brain_trauma(delta_dam) + var/is_boosted = FALSE + var/intelligence_modifier = (owner ? -(GET_MOB_ATTRIBUTE_VALUE(owner, STAT_INTELLIGENCE)-ATTRIBUTE_MIDDLING) : 0) + if(damage >= BRAIN_DAMAGE_SEVERE) + // Base chance is the hit damage, plus intelligence mod; for every point of damage past the threshold the chance is increased by 1% + if(prob((damage_delta+intelligence_modifier) * (1 + max(0, (damage - BRAIN_DAMAGE_SEVERE)/100)))) + if(prob(20 + (is_boosted * 30) - (intelligence_modifier * 2))) + gain_trauma_type(BRAIN_TRAUMA_SPECIAL, is_boosted ? TRAUMA_RESILIENCE_SURGERY : null, natural_gain = TRUE) + else + gain_trauma_type(BRAIN_TRAUMA_SEVERE, natural_gain = TRUE) + else + // Base chance is the hit damage, plus intelligence mod; for every point of damage past the threshold the chance is increased by 1% + if(prob((damage_delta+intelligence_modifier) * (1 + max(0, (damage - BRAIN_DAMAGE_MILD)/100)))) + gain_trauma_type(BRAIN_TRAUMA_MILD, natural_gain = TRUE) + +#define BRAIN_DAMAGE_FILTER "brain_damage_color_filter" + +/// Updates the brain's color based on damage level - the more damaged, the darker and grayer it gets +/obj/item/organ/brain/proc/update_brain_color(animate = TRUE) + if(damage <= 0) + if(get_filter(BRAIN_DAMAGE_FILTER)) + if(animate) + transition_filter(BRAIN_DAMAGE_FILTER, color_matrix_filter("#ffffff"), time = 1 SECONDS) + addtimer(CALLBACK(src, TYPE_PROC_REF(/datum, remove_filter), "brain_damage_color_filter"), 1.2 SECONDS, TIMER_UNIQUE) + else + remove_filter(BRAIN_DAMAGE_FILTER) return - var/damage_delta = damage - prev_damage - // Safeguard to prevent traumas from low damage - if((damage_delta >= TRAUMA_ROLL_THRESHOLD) && (damage >= BRAIN_DAMAGE_MILD) && COOLDOWN_FINISHED(src, trauma_cooldown)) - var/is_boosted = FALSE - var/intelligence_modifier = (owner ? -(GET_MOB_ATTRIBUTE_VALUE(owner, STAT_INTELLIGENCE)-ATTRIBUTE_MIDDLING) : 0) - if(damage >= BRAIN_DAMAGE_SEVERE) - // Base chance is the hit damage, plus intelligence mod; for every point of damage past the threshold the chance is increased by 1% - if(prob((damage_delta+intelligence_modifier) * (1 + max(0, (damage - BRAIN_DAMAGE_SEVERE)/100)))) - if(prob(20 + (is_boosted * 30) - (intelligence_modifier * 2))) - gain_trauma_type(BRAIN_TRAUMA_SPECIAL, is_boosted ? TRAUMA_RESILIENCE_SURGERY : null, natural_gain = TRUE) - COOLDOWN_START(src, trauma_cooldown, 5 MINUTES) - else - gain_trauma_type(BRAIN_TRAUMA_SEVERE, natural_gain = TRUE) - COOLDOWN_START(src, trauma_cooldown, 5 MINUTES) - else - // Base chance is the hit damage, plus intelligence mod; for every point of damage past the threshold the chance is increased by 1% - if(prob((damage_delta+intelligence_modifier) * (1 + max(0, (damage - BRAIN_DAMAGE_MILD)/100)))) - gain_trauma_type(BRAIN_TRAUMA_MILD, natural_gain = TRUE) - COOLDOWN_START(src, trauma_cooldown, 5 MINUTES) + + var/gradient = BlendRGB("#ffffff", "#7f7f7f", round(damage / maxHealth, 0.01)) + if(animate) + if(!get_filter(BRAIN_DAMAGE_FILTER)) + add_filter(BRAIN_DAMAGE_FILTER, 1, color_matrix_filter("#ffffff")) + transition_filter(BRAIN_DAMAGE_FILTER, color_matrix_filter(gradient), time = 1 SECONDS) + else if(get_filter(BRAIN_DAMAGE_FILTER)) + modify_filter(BRAIN_DAMAGE_FILTER, color_matrix_filter(gradient)) + else + add_filter(BRAIN_DAMAGE_FILTER, 1, color_matrix_filter(gradient)) + +#undef BRAIN_DAMAGE_FILTER + +/obj/item/organ/brain/on_medium_damage_received() + . = ..() + owner?.add_stress(/datum/stress_event/brain_damage) + +/obj/item/organ/brain/on_medium_damage_healed() + . = ..() + owner?.remove_stress(/datum/stress_event/brain_damage) + +/obj/item/organ/brain/check_damage_thresholds(mob/M) + . = ..() if(owner) if(damage >= BRAIN_DAMAGE_DEATH && prev_damage < BRAIN_DAMAGE_DEATH && (organ_flags & ORGAN_VITAL)) owner.death() @@ -354,43 +406,6 @@ else return brain_message -/obj/item/organ/brain/applyOrganDamage(amount, maximum = maxHealth, silent = FALSE) - if(!amount) //Micro-optimization. - return - if(maximum < damage) - damage = maximum - if(damage < 0 && owner?.get_chem_effect(CE_BRAIN_REGEN)) - damage *= 2 - prev_damage = damage - damage = clamp(damage + amount, 0, maximum) - var/mess = check_damage_thresholds(owner) - if(owner) - if(mess && !silent) - to_chat(owner, mess) - if(organ_flags & ORGAN_LIMB_SUPPORTER) - var/obj/item/bodypart/affected = owner.get_bodypart(current_zone) - affected?.update_limb_efficiency() - if(amount >= 10) - var/damage_side_effect = CEILING(amount/2, 1) - if(damage_side_effect >= 1) - //owner.flash_pain(damage_side_effect*4) - owner.adjust_eye_blur(damage_side_effect) - owner.adjust_confusion(damage_side_effect) - switch(rand(1,3)) - if(1) - owner.stuttering += damage_side_effect - if(2) - owner.slurring += damage_side_effect - if(3) - owner.cultslurring += damage_side_effect - owner.CombatKnockdown(damage_side_effect*2, damage_side_effect, (damage_side_effect >= 5 ? damage_side_effect : null), damage_side_effect >= 5) - if(!is_failing()) - REMOVE_TRAIT(owner, TRAIT_KNOCKEDOUT, CRIT_HEALTH_TRAIT) - if(damage >= 60) - owner?.add_stress(/datum/stress_event/brain_damage) - else - owner?.remove_stress(/datum/stress_event/brain_damage) - ////////////////////////////////////TRAUMAS//////////////////////////////////////// /obj/item/organ/brain/proc/has_trauma_type(brain_trauma_type = /datum/brain_trauma, resilience = TRAUMA_RESILIENCE_ABSOLUTE) diff --git a/code/modules/surgery/organs/internal/eyes.dm b/code/modules/surgery/organs/internal/eyes.dm index bef40d9bf94..f913d1ec83e 100644 --- a/code/modules/surgery/organs/internal/eyes.dm +++ b/code/modules/surgery/organs/internal/eyes.dm @@ -14,8 +14,6 @@ accessory_type = /datum/sprite_accessory/eyes/humanoid organ_efficiency = list(ORGAN_SLOT_EYES = 100) - healing_factor = STANDARD_ORGAN_HEALING - maxHealth = 0.5 * STANDARD_ORGAN_THRESHOLD //half the normal health max since we go blind at 30, a permanent blindness at 50 therefore makes sense unless medicine is administered high_threshold = 0.3 * STANDARD_ORGAN_THRESHOLD //threshold at 30 low_threshold = 0.2 * STANDARD_ORGAN_THRESHOLD //threshold at 20 diff --git a/code/modules/surgery/organs/internal/liver.dm b/code/modules/surgery/organs/internal/liver.dm index 17e006dd36d..382fcf22505 100644 --- a/code/modules/surgery/organs/internal/liver.dm +++ b/code/modules/surgery/organs/internal/liver.dm @@ -9,9 +9,6 @@ slot = ORGAN_SLOT_LIVER desc = "" organ_efficiency = list(ORGAN_SLOT_LIVER = 100) - maxHealth = STANDARD_ORGAN_THRESHOLD - healing_factor = STANDARD_ORGAN_HEALING - organ_volume = 2 max_blood_storage = 25 current_blood = 25 diff --git a/code/modules/surgery/organs/internal/stomach.dm b/code/modules/surgery/organs/internal/stomach.dm index a8d203cfaf3..559005a0355 100644 --- a/code/modules/surgery/organs/internal/stomach.dm +++ b/code/modules/surgery/organs/internal/stomach.dm @@ -8,8 +8,6 @@ attack_verb = list("gored", "squished", "slapped", "digested") desc = "" - healing_factor = STANDARD_ORGAN_HEALING - organ_volume = 1 max_blood_storage = 20 current_blood = 20 @@ -86,8 +84,6 @@ attack_verb = list("gored", "squished", "slapped", "digested") desc = "" organ_efficiency = list(ORGAN_SLOT_GUTS = 100) - - healing_factor = STANDARD_ORGAN_HEALING low_threshold_passed = "My guts flashes with pain before subsiding." high_threshold_passed = "My guts flares up with constant pain." high_threshold_cleared = "The pain in my guts die down for now." diff --git a/code/modules/surgery/organs/internal/vocal_cords.dm b/code/modules/surgery/organs/internal/vocal_cords.dm index 0ad743d59b0..9fa49d1a108 100644 --- a/code/modules/surgery/organs/internal/vocal_cords.dm +++ b/code/modules/surgery/organs/internal/vocal_cords.dm @@ -5,7 +5,6 @@ slot = ORGAN_SLOT_VOICE organ_efficiency = list(ORGAN_SLOT_VOICE = 100) gender = PLURAL - healing_factor = 0 organ_volume = 1 max_blood_storage = 10