Files
nevimer 00ccf0c6b5 Merge remote-tracking branch 'tgstation/master' into upstream-feb12-2026
# Conflicts:
#	.github/CODEOWNERS
#	.github/workflows/compile_changelogs.yml
#	.github/workflows/stale.yml
#	SQL/database_changelog.md
#	_maps/map_files/CatwalkStation/CatwalkStation_2023.dmm
#	code/__DEFINES/atom_hud.dm
#	code/__DEFINES/inventory.dm
#	code/__DEFINES/mobs.dm
#	code/__DEFINES/species_clothing_paths.dm
#	code/__DEFINES/subsystems.dm
#	code/__DEFINES/surgery.dm
#	code/__HELPERS/global_lists.dm
#	code/_globalvars/lists/maintenance_loot.dm
#	code/_globalvars/traits/_traits.dm
#	code/controllers/subsystem/minor_mapping.dm
#	code/controllers/subsystem/processing/quirks.dm
#	code/controllers/subsystem/shuttle.dm
#	code/datums/components/palette.dm
#	code/datums/components/surgery_initiator.dm
#	code/datums/diseases/advance/advance.dm
#	code/datums/hud.dm
#	code/datums/mood.dm
#	code/datums/mutations/chameleon.dm
#	code/datums/quirks/negative_quirks/nyctophobia.dm
#	code/datums/status_effects/debuffs/debuffs.dm
#	code/datums/status_effects/debuffs/drunk.dm
#	code/datums/status_effects/debuffs/slime/slime_leech.dm
#	code/datums/weather/weather.dm
#	code/game/data_huds.dm
#	code/game/objects/items.dm
#	code/game/objects/items/devices/scanners/health_analyzer.dm
#	code/game/objects/items/frog_statue.dm
#	code/game/objects/items/rcd/RLD.dm
#	code/game/objects/items/robot/items/hypo.dm
#	code/game/objects/items/stacks/medical.dm
#	code/game/objects/items/stacks/wrap.dm
#	code/game/objects/items/storage/garment.dm
#	code/game/objects/items/tools/medical/defib.dm
#	code/game/objects/items/weaponry.dm
#	code/game/objects/items/weaponry/melee/misc.dm
#	code/game/objects/structures/crates_lockers/closets/secure/security.dm
#	code/game/objects/structures/curtains.dm
#	code/game/objects/structures/dresser.dm
#	code/game/objects/structures/girders.dm
#	code/game/objects/structures/maintenance.dm
#	code/game/objects/structures/mirror.dm
#	code/modules/admin/greyscale_modify_menu.dm
#	code/modules/admin/verbs/light_debug.dm
#	code/modules/antagonists/ashwalker/ashwalker.dm
#	code/modules/antagonists/heretic/knowledge/starting_lore.dm
#	code/modules/antagonists/ninja/ninjaDrainAct.dm
#	code/modules/art/paintings.dm
#	code/modules/client/preferences.dm
#	code/modules/client/verbs/ooc.dm
#	code/modules/clothing/head/wig.dm
#	code/modules/events/disease_outbreak.dm
#	code/modules/holodeck/holo_effect.dm
#	code/modules/jobs/job_types/head_of_security.dm
#	code/modules/jobs/job_types/security_officer.dm
#	code/modules/library/skill_learning/generic_skillchips/point.dm
#	code/modules/mining/lavaland/ash_flora.dm
#	code/modules/mining/lavaland/mining_loot/megafauna/ash_drake.dm
#	code/modules/mob/dead/new_player/new_player.dm
#	code/modules/mob/living/basic/guardian/guardian.dm
#	code/modules/mob/living/basic/space_fauna/space_dragon/space_dragon.dm
#	code/modules/mob/living/carbon/carbon.dm
#	code/modules/mob/living/carbon/human/human.dm
#	code/modules/mob/living/carbon/human/human_defines.dm
#	code/modules/mob/living/carbon/life.dm
#	code/modules/mob/living/living.dm
#	code/modules/mob/living/living_defines.dm
#	code/modules/mob/mob.dm
#	code/modules/mob_spawn/ghost_roles/mining_roles.dm
#	code/modules/mod/mod_control.dm
#	code/modules/mod/modules/modules_general.dm
#	code/modules/modular_computers/computers/item/computer_ui.dm
#	code/modules/paperwork/paper.dm
#	code/modules/paperwork/paperbin.dm
#	code/modules/power/lighting/light.dm
#	code/modules/projectiles/guns/energy/kinetic_accelerator.dm
#	code/modules/projectiles/projectile.dm
#	code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm
#	code/modules/reagents/chemistry/reagents/food_reagents.dm
#	code/modules/reagents/chemistry/reagents/other_reagents.dm
#	code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm
#	code/modules/research/xenobiology/crossbreeding/_clothing.dm
#	code/modules/research/xenobiology/crossbreeding/prismatic.dm
#	code/modules/surgery/advanced/brainwashing.dm
#	code/modules/surgery/advanced/lobotomy.dm
#	code/modules/surgery/amputation.dm
#	code/modules/surgery/blood_filter.dm
#	code/modules/surgery/bodyparts/_bodyparts.dm
#	code/modules/surgery/brain_surgery.dm
#	code/modules/surgery/cavity_implant.dm
#	code/modules/surgery/coronary_bypass.dm
#	code/modules/surgery/gastrectomy.dm
#	code/modules/surgery/healing.dm
#	code/modules/surgery/limb_augmentation.dm
#	code/modules/surgery/organ_manipulation.dm
#	code/modules/surgery/revival.dm
#	code/modules/surgery/sleeper_protocol.dm
#	code/modules/surgery/surgery_helpers.dm
#	code/modules/surgery/surgery_step.dm
#	code/modules/unit_tests/_unit_tests.dm
#	code/modules/unit_tests/designs.dm
#	code/modules/unit_tests/icon_state_worn.dm
#	code/modules/unit_tests/screenshots/screenshot_antag_icons_cultist.png
#	code/modules/unit_tests/screenshots/screenshot_antag_icons_headrevolutionary.png
#	code/modules/unit_tests/screenshots/screenshot_antag_icons_provocateur.png
#	code/modules/unit_tests/screenshots/screenshot_husk_body.png
#	code/modules/unit_tests/screenshots/screenshot_husk_body_missing_limbs.png
#	icons/map_icons/clothing/head/_head.dmi
#	icons/map_icons/clothing/shoes.dmi
#	icons/map_icons/items/_item.dmi
#	icons/mob/huds/hud.dmi
#	icons/mob/inhands/64x64_lefthand.dmi
#	icons/mob/inhands/64x64_righthand.dmi
#	icons/obj/machines/computer.dmi
#	tgui/packages/tgui/interfaces/OperatingComputer.jsx
#	tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferences/MainPage.tsx
#	tgui/packages/tgui/interfaces/PreferencesMenu/types.ts
#	tgui/packages/tgui/interfaces/SurgeryInitiator.tsx
#	tools/icon_cutter/check.py
2026-02-12 23:50:09 -05:00

551 lines
19 KiB
Plaintext

/**
* Applies damage to this mob.
*
* Sends [COMSIG_MOB_APPLY_DAMAGE]
*
* Arguuments:
* * damage - Amount of damage
* * damagetype - What type of damage to do. one of [BRUTE], [BURN], [TOX], [OXY], [STAMINA], [BRAIN].
* * def_zone - What body zone is being hit. Or a reference to what bodypart is being hit.
* * blocked - Percent modifier to damage. 100 = 100% less damage dealt, 50% = 50% less damage dealt.
* * forced - "Force" exactly the damage dealt. This means it skips damage modifier from blocked.
* * spread_damage - For carbons, spreads the damage across all bodyparts rather than just the targeted zone.
* * wound_bonus - Bonus modifier for wound chance.
* * exposed_wound_bonus - Bonus modifier for wound chance on bare skin.
* * sharpness - Sharpness of the weapon.
* * attack_direction - Direction of the attack from the attacker to [src].
* * attacking_item - Item that is attacking [src].
* * wound_clothing - If this should cause damage to clothing.
*
* Returns the amount of damage dealt.
*/
/mob/living/proc/apply_damage(
damage = 0,
damagetype = BRUTE,
def_zone = null,
blocked = 0,
forced = FALSE,
spread_damage = FALSE,
wound_bonus = 0,
exposed_wound_bonus = 0,
sharpness = NONE,
attack_direction = null,
attacking_item,
wound_clothing = TRUE,
)
SHOULD_CALL_PARENT(TRUE)
var/damage_amount = damage
if(!forced)
damage_amount *= ((100 - blocked) / 100)
damage_amount *= get_incoming_damage_modifier(damage_amount, damagetype, def_zone, sharpness, attack_direction, attacking_item)
SEND_SIGNAL(src, COMSIG_MOB_ALWAYS_APPLY_DAMAGE, damage_amount, damagetype, def_zone, blocked, wound_bonus, exposed_wound_bonus, sharpness, attack_direction, attacking_item, wound_clothing) // BUBBER ADDITION: FLAT DAMAGE MODIFIERS
if(damage_amount <= 0)
return 0
SEND_SIGNAL(src, COMSIG_MOB_APPLY_DAMAGE, damage_amount, damagetype, def_zone, blocked, wound_bonus, exposed_wound_bonus, sharpness, attack_direction, attacking_item, wound_clothing)
var/damage_dealt = 0
switch(damagetype)
if(BRUTE)
if(isbodypart(def_zone))
var/obj/item/bodypart/actual_hit = def_zone
var/delta = actual_hit.get_damage()
if(actual_hit.receive_damage(
brute = damage_amount,
burn = 0,
forced = forced,
wound_bonus = wound_bonus,
exposed_wound_bonus = exposed_wound_bonus,
sharpness = sharpness,
attack_direction = attack_direction,
damage_source = attacking_item,
wound_clothing = wound_clothing,
))
update_damage_overlays()
damage_dealt = actual_hit.get_damage() - delta // Unfortunately bodypart receive_damage doesn't return damage dealt so we do it manually
else
damage_dealt = -1 * adjust_brute_loss(damage_amount, forced = forced)
INVOKE_ASYNC(src, TYPE_PROC_REF(/mob/living, adjust_pain), damage_amount) // SKYRAT EDIT ADDITION - ERP Pain
if(BURN)
if(isbodypart(def_zone))
var/obj/item/bodypart/actual_hit = def_zone
var/delta = actual_hit.get_damage()
if(actual_hit.receive_damage(
brute = 0,
burn = damage_amount,
forced = forced,
wound_bonus = wound_bonus,
exposed_wound_bonus = exposed_wound_bonus,
sharpness = sharpness,
attack_direction = attack_direction,
damage_source = attacking_item,
wound_clothing = wound_clothing,
))
update_damage_overlays()
damage_dealt = actual_hit.get_damage() - delta // See above
else
damage_dealt = -1 * adjust_fire_loss(damage_amount, forced = forced)
INVOKE_ASYNC(src, TYPE_PROC_REF(/mob/living, adjust_pain), damage_amount) // SKYRAT EDIT ADDITION - ERP Pain
if(TOX)
damage_dealt = -1 * adjust_tox_loss(damage_amount, forced = forced)
if(OXY)
damage_dealt = -1 * adjust_oxy_loss(damage_amount, forced = forced)
if(STAMINA)
damage_dealt = -1 * adjust_stamina_loss(damage_amount, forced = forced)
if(BRAIN)
damage_dealt = -1 * adjust_organ_loss(ORGAN_SLOT_BRAIN, damage_amount)
SEND_SIGNAL(src, COMSIG_MOB_AFTER_APPLY_DAMAGE, damage_dealt, damagetype, def_zone, blocked, wound_bonus, exposed_wound_bonus, sharpness, attack_direction, attacking_item, wound_clothing)
return damage_dealt
/**
* Used in tandem with [/mob/living/proc/apply_damage] to calculate modifier applied into incoming damage
*/
/mob/living/proc/get_incoming_damage_modifier(
damage = 0,
damagetype = BRUTE,
def_zone = null,
sharpness = NONE,
attack_direction = null,
attacking_item,
)
SHOULD_CALL_PARENT(TRUE)
SHOULD_BE_PURE(TRUE)
var/list/damage_mods = list()
SEND_SIGNAL(src, COMSIG_MOB_APPLY_DAMAGE_MODIFIERS, damage_mods, damage, damagetype, def_zone, sharpness, attack_direction, attacking_item)
var/final_mod = 1
for(var/new_mod in damage_mods)
final_mod *= new_mod
return final_mod
/**
* Simply a wrapper for calling mob adjustXLoss() procs to heal a certain damage type,
* when you don't know what damage type you're healing exactly.
*/
/mob/living/proc/heal_damage_type(heal_amount = 0, damagetype = BRUTE, update_health = TRUE)
heal_amount = abs(heal_amount) * -1
switch(damagetype)
if(BRUTE)
return adjust_brute_loss(heal_amount, update_health)
if(BURN)
return adjust_fire_loss(heal_amount, update_health)
if(TOX)
return adjust_tox_loss(heal_amount, update_health)
if(OXY)
return adjust_oxy_loss(heal_amount, update_health)
if(STAMINA)
return adjust_stamina_loss(heal_amount, update_health)
/// return the damage amount for the type given
/**
* Simply a wrapper for calling mob getXLoss() procs to get a certain damage type,
* when you don't know what damage type you're getting exactly.
*/
/mob/living/proc/get_current_damage_of_type(damagetype = BRUTE)
switch(damagetype)
if(BRUTE)
return get_brute_loss()
if(BURN)
return get_fire_loss()
if(TOX)
return get_tox_loss()
if(OXY)
return get_oxy_loss()
if(STAMINA)
return get_stamina_loss()
/// return the total damage of all types which update your health
/mob/living/proc/get_total_damage(precision = DAMAGE_PRECISION)
return round(get_brute_loss() + get_fire_loss() + get_tox_loss() + get_oxy_loss(), precision)
/// Applies multiple damages at once via [apply_damage][/mob/living/proc/apply_damage]
/mob/living/proc/apply_damages(
brute = 0,
burn = 0,
tox = 0,
oxy = 0,
def_zone = null,
blocked = 0,
stamina = 0,
brain = 0,
)
var/total_damage = 0
if(brute)
total_damage += apply_damage(brute, BRUTE, def_zone, blocked)
if(burn)
total_damage += apply_damage(burn, BURN, def_zone, blocked)
if(tox)
total_damage += apply_damage(tox, TOX, def_zone, blocked)
if(oxy)
total_damage += apply_damage(oxy, OXY, def_zone, blocked)
if(stamina)
total_damage += apply_damage(stamina, STAMINA, def_zone, blocked)
if(brain)
total_damage += apply_damage(brain, BRAIN, def_zone, blocked)
return total_damage
/// applies various common status effects or common hardcoded mob effects
/mob/living/proc/apply_effect(effect = 0,effecttype = EFFECT_STUN, blocked = 0)
var/hit_percent = (100-blocked)/100
if(!effect || (hit_percent <= 0))
return FALSE
switch(effecttype)
if(EFFECT_STUN)
Stun(effect * hit_percent)
if(EFFECT_KNOCKDOWN)
Knockdown(effect * hit_percent)
if(EFFECT_PARALYZE)
Paralyze(effect * hit_percent)
if(EFFECT_IMMOBILIZE)
Immobilize(effect * hit_percent)
if(EFFECT_UNCONSCIOUS)
Unconscious(effect * hit_percent)
return TRUE
/**
* Applies multiple effects at once via [/mob/living/proc/apply_effect]
*
* Pretty much only used for projectiles applying effects on hit,
* don't use this for anything else please just cause the effects directly
*/
/mob/living/proc/apply_effects(
stun = 0,
knockdown = 0,
unconscious = 0,
slur = 0 SECONDS, // Speech impediment, not technically an effect
stutter = 0 SECONDS, // Ditto
eyeblur = 0 SECONDS,
drowsy = 0 SECONDS,
blocked = 0, // This one's not an effect, don't be confused - it's block chance
stamina = 0, // This one's a damage type, and not an effect
jitter = 0 SECONDS,
paralyze = 0,
immobilize = 0,
)
if(blocked >= 100)
return FALSE
if(stun)
apply_effect(stun, EFFECT_STUN, blocked)
if(knockdown)
apply_effect(knockdown, EFFECT_KNOCKDOWN, blocked)
if(unconscious)
apply_effect(unconscious, EFFECT_UNCONSCIOUS, blocked)
if(paralyze)
apply_effect(paralyze, EFFECT_PARALYZE, blocked)
if(immobilize)
apply_effect(immobilize, EFFECT_IMMOBILIZE, blocked)
if(stamina)
apply_damage(stamina, STAMINA, null, blocked)
if(drowsy)
adjust_drowsiness(drowsy)
if(eyeblur)
adjust_eye_blur_up_to(eyeblur, eyeblur)
if(jitter && !check_stun_immunity(CANSTUN))
adjust_jitter(jitter)
if(slur)
adjust_slurring(slur)
if(stutter)
adjust_stutter(stutter)
return TRUE
/// Returns a multiplier to apply to a specific kind of damage
/mob/living/proc/get_damage_mod(damage_type)
switch(damage_type)
if (OXY)
return HAS_TRAIT(src, TRAIT_NOBREATH) ? 0 : 1
if (TOX)
if (HAS_TRAIT(src, TRAIT_TOXINLOVER))
return -1
return HAS_TRAIT(src, TRAIT_TOXIMMUNE) ? 0 : 1
return 1
/mob/living/proc/get_brute_loss()
return bruteloss
/mob/living/proc/can_adjust_brute_loss(amount, forced, required_bodytype)
if(!forced && HAS_TRAIT(src, TRAIT_GODMODE))
return FALSE
if(SEND_SIGNAL(src, COMSIG_LIVING_ADJUST_BRUTE_DAMAGE, BRUTE, amount, forced) & COMPONENT_IGNORE_CHANGE)
return FALSE
return TRUE
/mob/living/proc/adjust_brute_loss(amount, updating_health = TRUE, forced = FALSE, required_bodytype = ALL)
if (!can_adjust_brute_loss(amount, forced, required_bodytype))
return 0
. = bruteloss
bruteloss = clamp((bruteloss + (amount * CONFIG_GET(number/damage_multiplier))), 0, maxHealth * 2)
. -= bruteloss
if(!.) // no change, no need to update
return 0
if(updating_health)
updatehealth()
/mob/living/proc/set_brute_loss(amount, updating_health = TRUE, forced = FALSE, required_bodytype = ALL)
if(!forced && HAS_TRAIT(src, TRAIT_GODMODE))
return FALSE
. = bruteloss
bruteloss = amount
if(!.) // no change, no need to update
return FALSE
if(updating_health)
updatehealth()
. -= bruteloss
/mob/living/proc/get_oxy_loss()
return oxyloss
/mob/living/proc/can_adjust_oxy_loss(amount, forced, required_biotype, required_respiration_type)
if(!forced)
if(HAS_TRAIT(src, TRAIT_GODMODE))
return FALSE
if (required_respiration_type)
var/obj/item/organ/lungs/affected_lungs = get_organ_slot(ORGAN_SLOT_LUNGS)
if(isnull(affected_lungs))
if(!(mob_respiration_type & required_respiration_type)) // if the mob has no lungs, use mob_respiration_type
return FALSE
else
if(!(affected_lungs.respiration_type & required_respiration_type)) // otherwise use the lungs' respiration_type
return FALSE
if(SEND_SIGNAL(src, COMSIG_LIVING_ADJUST_OXY_DAMAGE, OXY, amount, forced) & COMPONENT_IGNORE_CHANGE)
return FALSE
return TRUE
/mob/living/proc/adjust_oxy_loss(amount, updating_health = TRUE, forced = FALSE, required_biotype = ALL, required_respiration_type = ALL)
if(!can_adjust_oxy_loss(amount, forced, required_biotype, required_respiration_type))
return 0
. = oxyloss
oxyloss = clamp((oxyloss + (amount * CONFIG_GET(number/damage_multiplier))), 0, maxHealth * 2)
. -= oxyloss
if(!.) // no change, no need to update
return FALSE
if(updating_health)
updatehealth()
/mob/living/proc/set_oxy_loss(amount, updating_health = TRUE, forced = FALSE, required_biotype = ALL, required_respiration_type = ALL)
if(!forced)
if(HAS_TRAIT(src, TRAIT_GODMODE))
return FALSE
var/obj/item/organ/lungs/affected_lungs = get_organ_slot(ORGAN_SLOT_LUNGS)
if(isnull(affected_lungs))
if(!(mob_respiration_type & required_respiration_type))
return FALSE
else
if(!(affected_lungs.respiration_type & required_respiration_type))
return FALSE
. = oxyloss
oxyloss = amount
. -= oxyloss
if(!.) // no change, no need to update
return FALSE
if(updating_health)
updatehealth()
/mob/living/proc/get_tox_loss()
return toxloss
/mob/living/proc/can_adjust_tox_loss(amount, forced, required_biotype = ALL)
if(!forced && (HAS_TRAIT(src, TRAIT_GODMODE) || !(mob_biotypes & required_biotype)))
return FALSE
if(SEND_SIGNAL(src, COMSIG_LIVING_ADJUST_TOX_DAMAGE, TOX, amount, forced) & COMPONENT_IGNORE_CHANGE)
return FALSE
return TRUE
/mob/living/proc/adjust_tox_loss(amount, updating_health = TRUE, forced = FALSE, required_biotype = ALL)
if(!can_adjust_tox_loss(amount, forced, required_biotype))
return 0
if(!forced && HAS_TRAIT(src, TRAIT_TOXINLOVER)) //damage becomes healing and healing becomes damage
amount = -amount
if(HAS_TRAIT(src, TRAIT_TOXIMMUNE)) //Prevents toxin damage, but not healing
amount = min(amount, 0)
if(amount > 0)
adjust_blood_volume(-5 * amount)
else
adjust_blood_volume(-amount)
else if(!forced && HAS_TRAIT(src, TRAIT_TOXIMMUNE)) //Prevents toxin damage, but not healing
amount = min(amount, 0)
. = toxloss
toxloss = clamp((toxloss + (amount * CONFIG_GET(number/damage_multiplier))), 0, maxHealth * 2)
. -= toxloss
if(!.) // no change, no need to update
return FALSE
if(updating_health)
updatehealth()
/mob/living/proc/set_tox_loss(amount, updating_health = TRUE, forced = FALSE, required_biotype = ALL)
if(!forced && HAS_TRAIT(src, TRAIT_GODMODE))
return FALSE
if(!forced && !(mob_biotypes & required_biotype))
return FALSE
. = toxloss
toxloss = amount
. -= toxloss
if(!.) // no change, no need to update
return FALSE
if(updating_health)
updatehealth()
/mob/living/proc/get_fire_loss()
return fireloss
/mob/living/proc/can_adjust_fire_loss(amount, forced, required_bodytype)
if(!forced && HAS_TRAIT(src, TRAIT_GODMODE))
return FALSE
if(SEND_SIGNAL(src, COMSIG_LIVING_ADJUST_BURN_DAMAGE, BURN, amount, forced) & COMPONENT_IGNORE_CHANGE)
return FALSE
return TRUE
/mob/living/proc/adjust_fire_loss(amount, updating_health = TRUE, forced = FALSE, required_bodytype = ALL)
if(!can_adjust_fire_loss(amount, forced, required_bodytype))
return 0
. = fireloss
fireloss = clamp((fireloss + (amount * CONFIG_GET(number/damage_multiplier))), 0, maxHealth * 2)
. -= fireloss
if(. == 0) // no change, no need to update
return
if(updating_health)
updatehealth()
/mob/living/proc/set_fire_loss(amount, updating_health = TRUE, forced = FALSE, required_bodytype = ALL)
if(!forced && HAS_TRAIT(src, TRAIT_GODMODE))
return 0
. = fireloss
fireloss = amount
. -= fireloss
if(. == 0) // no change, no need to update
return 0
if(updating_health)
updatehealth()
/mob/living/proc/adjust_organ_loss(slot, amount, maximum, required_organ_flag)
return
/mob/living/proc/set_organ_loss(slot, amount, maximum, required_organ_flag)
return
/mob/living/proc/get_organ_loss(slot, required_organ_flag)
return
/mob/living/proc/get_stamina_loss()
return staminaloss
/mob/living/proc/can_adjust_stamina_loss(amount, forced, required_biotype = ALL)
if(!forced && (!(mob_biotypes & required_biotype) || HAS_TRAIT(src, TRAIT_GODMODE)))
return FALSE
if(SEND_SIGNAL(src, COMSIG_LIVING_ADJUST_STAMINA_DAMAGE, STAMINA, amount, forced) & COMPONENT_IGNORE_CHANGE)
return FALSE
return TRUE
/mob/living/proc/adjust_stamina_loss(amount, updating_stamina = TRUE, forced = FALSE, required_biotype = ALL)
if(!can_adjust_stamina_loss(amount, forced, required_biotype))
return 0
var/old_amount = staminaloss
staminaloss = clamp((staminaloss + (amount * CONFIG_GET(number/damage_multiplier))), 0, max_stamina)
var/delta = old_amount - staminaloss
if(delta <= 0)
// need to check for stamcrit AFTER canadjust but BEFORE early return here
received_stamina_damage(staminaloss, -1 * delta)
if(delta == 0) // no change, no need to update
return 0
if(updating_stamina)
updatehealth()
return delta
/mob/living/proc/set_stamina_loss(amount, updating_stamina = TRUE, forced = FALSE, required_biotype = ALL)
if(!forced && HAS_TRAIT(src, TRAIT_GODMODE))
return 0
if(!forced && !(mob_biotypes & required_biotype))
return 0
var/old_amount = staminaloss
staminaloss = amount
var/delta = old_amount - staminaloss
if(delta <= 0 && amount >= DAMAGE_PRECISION)
received_stamina_damage(staminaloss, -1 * delta, amount)
if(delta == 0) // no change, no need to update
return 0
if(updating_stamina)
updatehealth()
return delta
/// The mob has received stamina damage
///
/// - current_level: The mob's current stamina damage amount (to save unnecessary get_stamina_loss() calls)
/// - amount_actual: The amount of stamina damage received, in actuality
/// For example, if you are taking 50 stamina damage but are at 90, you would actually only receive 30 stamina damage (due to the cap)
/// - amount: The amount of stamina damage received, raw
/mob/living/proc/received_stamina_damage(current_level, amount_actual, amount)
addtimer(CALLBACK(src, PROC_REF(set_stamina_loss), 0, TRUE, TRUE), stamina_regen_time, TIMER_UNIQUE|TIMER_OVERRIDE)
/**
* heal ONE external organ, organ gets randomly selected from damaged ones.
*
* returns the net change in damage
*/
/mob/living/proc/heal_bodypart_damage(brute = 0, burn = 0, updating_health = TRUE, required_bodytype = NONE, target_zone = null)
. = (adjust_brute_loss(-abs(brute), updating_health = FALSE) + adjust_fire_loss(-abs(burn), updating_health = FALSE))
if(!.) // no change, no need to update
return FALSE
if(updating_health)
updatehealth()
/// damage ONE external organ, organ gets randomly selected from damaged ones.
/mob/living/proc/take_bodypart_damage(brute = 0, burn = 0, updating_health = TRUE, required_bodytype, check_armor = FALSE, wound_bonus = 0, exposed_wound_bonus = 0, sharpness = NONE)
. = (adjust_brute_loss(abs(brute), updating_health = FALSE) + adjust_fire_loss(abs(burn), updating_health = FALSE))
if(!.) // no change, no need to update
return FALSE
if(updating_health)
updatehealth()
/// heal MANY bodyparts, in random order. note: stamina arg nonfunctional for carbon mobs
/mob/living/proc/heal_overall_damage(brute = 0, burn = 0, stamina = 0, required_bodytype, updating_health = TRUE, forced = FALSE)
. = (adjust_brute_loss(-abs(brute), updating_health = FALSE, forced = forced) + \
adjust_fire_loss(-abs(burn), updating_health = FALSE, forced = forced) + \
adjust_stamina_loss(-abs(stamina), updating_stamina = FALSE, forced = forced))
if(!.) // no change, no need to update
return FALSE
if(updating_health)
updatehealth()
/// damage MANY bodyparts, in random order. note: stamina arg nonfunctional for carbon mobs
/mob/living/proc/take_overall_damage(brute = 0, burn = 0, stamina = 0, updating_health = TRUE, forced = FALSE, required_bodytype)
. = (adjust_brute_loss(abs(brute), updating_health = FALSE, forced = forced) + \
adjust_fire_loss(abs(burn), updating_health = FALSE, forced = forced) + \
adjust_stamina_loss(abs(stamina), updating_stamina = FALSE, forced = forced))
if(!.) // no change, no need to update
return FALSE
if(updating_health)
updatehealth()
///heal up to amount damage, in a given order
/mob/living/proc/heal_ordered_damage(amount, list/damage_types, update_health = TRUE)
. = 0 //we'll return the amount of damage healed
for(var/damagetype in damage_types)
var/amount_to_heal = min(abs(amount), get_current_damage_of_type(damagetype)) //heal only up to the amount of damage we have
if(amount_to_heal)
. += heal_damage_type(amount_to_heal, damagetype, FALSE)
amount -= amount_to_heal //remove what we healed from our current amount
if(!amount)
break
if(. && update_health)
updatehealth()