mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-08-20 11:31:12 +01:00
https://www.youtube.com/watch?v=ZpUYjpKg9KY As per title. The goal is to add health to most objects in the game and thus allow them to be dynamically destructible, while also avoiding shitcode like each structure having its own snowflake health update proc. --------- Signed-off-by: Matt Atlas <mattiathebest2000@hotmail.it> Co-authored-by: Matt Atlas <liermattia@gmail.com> Co-authored-by: SleepyGemmy <99297919+SleepyGemmy@users.noreply.github.com>
430 lines
16 KiB
Plaintext
430 lines
16 KiB
Plaintext
/obj/item/organ/internal/heart
|
|
name = "heart"
|
|
desc = "When it's calling to you, you'd better listen."
|
|
icon_state = "heart-on"
|
|
organ_tag = BP_HEART
|
|
parent_organ = BP_CHEST
|
|
dead_icon = "heart-off"
|
|
robotic_name = "circulatory pump"
|
|
toxin_type = CE_CARDIOTOXIC
|
|
|
|
max_damage = 60
|
|
min_broken_damage = 45
|
|
relative_size = 5
|
|
damage_reduction = 0.7
|
|
|
|
var/pulse = PULSE_NORM //current pulse level
|
|
var/heartbeat = 0
|
|
var/next_blood_squirt = 0
|
|
var/list/external_pump
|
|
|
|
/**
|
|
* The amount of damage a heart takes per second while in fibrillation(red pulse).
|
|
* A Heart risks stopping as it takes damage.
|
|
*/
|
|
var/heart_fibrillation_damage = 0.16666
|
|
|
|
/**
|
|
* The amount of damage a heart takes per second while in tachycardia(yellow pulse).
|
|
* A Heart risks stopping as it takes damage.
|
|
*/
|
|
var/heart_tachycardia_damage = 0.033333
|
|
|
|
/// The percent chance each tick that a heart in fibrillation will immediately stop
|
|
var/fibrillation_stop_risk = 5
|
|
|
|
/// How much pain is required for a heart attack to be possible.
|
|
var/shock_stage_for_fibrillation = 120
|
|
|
|
/// Percent chance of a heart attack when above a certain amount of pain.
|
|
var/shock_risk_from_pain = 30
|
|
|
|
/// Over this amount of shock, worsen the heart's condition step by one step
|
|
var/first_shock_stage = 30
|
|
|
|
/// Over this amount of shock, worsen the heart's condition step by one step, cumulative with the previous stage.
|
|
var/second_shock_stage = 80
|
|
|
|
/// How much this heart modifies the owner's blood regeneration, needed for Bioaugmented hearts
|
|
var/blood_regen_modifier = 1
|
|
|
|
/// How much this heart modifies the rate of bloodloss per arterial wound.
|
|
var/base_arterial_bloodloss_modifier = 1
|
|
|
|
/// How much this heart modifies the rate of bloodloss per cut wound.
|
|
var/base_cut_bloodloss_modifier = 1
|
|
|
|
/// How much this heart modifies nutrition loss while regenerating blood.
|
|
var/nutrition_cost_per_blood_regen = 2
|
|
|
|
/// How much this heart modifies hydration loss while regenerating blood.
|
|
var/hydration_cost_per_blood_regen = 1
|
|
|
|
/// The temperature at which the heart "Freezes" in Cryostasis.
|
|
var/minimum_temperature_to_pump_blood = 170
|
|
|
|
/// The amount of time (in seconds) that pass between arterial sprays if injured as such.
|
|
var/time_between_arterial_sprays = 100
|
|
|
|
/// How much this heart modifies the rate of bloodloss from external dripping. This is a more general clotting factor.
|
|
var/bleed_drip_modifier = 1
|
|
|
|
/// The threshold of pain for which the player will experience "Heart palpitations", IE: Hearing your own heartbeat.
|
|
var/palpitations_threshold = 10
|
|
|
|
/// How much "Slow" pulse modifies bleed rate
|
|
var/slow_bleeding_modifier = 0.8
|
|
|
|
/// How much "Fast" pulse modifies bleed rate
|
|
var/fast_bleeding_modifier = 1.25
|
|
|
|
/// How much Tachycardia modifies bleed rate
|
|
var/too_fast_bleeding_modifier = 1.5
|
|
|
|
/// How much fibrillation modifies bleed rate
|
|
var/thready_bleeding_modifier = 1.5
|
|
|
|
/// The base rate at which this heart pumps blood regardless of pulse rate.
|
|
var/base_pump_rate = 1
|
|
|
|
/// The "base blood circulation" for if the heart is fully stopped. This can get floored later to as little as 0.075 on default settings.
|
|
var/none_pump_modifier = 0.25
|
|
|
|
/// Modifier on how much blood this heart pumps while at a "slow" pulse rate.
|
|
var/slow_pump_modifier = 0.9
|
|
|
|
/// Modifier on how much blood this heart pumps at a "normal" pulse rate.
|
|
var/norm_pump_modifier = 1
|
|
|
|
/// Modifier on how much blood this heart pumps at a "fast" pulse rate.
|
|
var/fast_pump_modifier = 1.1
|
|
|
|
/// Modifier on how much blood this heart pumps at a "2fast" pulse rate.
|
|
var/too_fast_pump_modifier = 1.25
|
|
|
|
/// Modifier on how much blood this heart pumps at a "thready" pulse rate.
|
|
var/thready_pump_modifier = 1.25
|
|
|
|
/// Useful for high end robotic hearts, whether they have ways of faking the appearance of a pulse so long as they're active.
|
|
var/fake_pulse = FALSE
|
|
|
|
/// How much damage this heart can take from chemically induced arythmia per instance.
|
|
var/damage_from_chemicals = 0.5
|
|
|
|
/// Sound effect used for heartbeat warnings, such as when the user is in pain/risk of dying.
|
|
var/heartbeat_sound = 'sound/effects/singlebeat.ogg'
|
|
|
|
/// Distance effects for arterial sprays.
|
|
var/blood_spray_distance = 2
|
|
|
|
/obj/item/organ/internal/heart/process(seconds_per_tick)
|
|
if(!owner)
|
|
return ..()
|
|
if(owner.stasis_value > 0) // Decrease the effective tickrate when in stasis.
|
|
seconds_per_tick /= owner.stasis_value
|
|
handle_pulse()
|
|
if(pulse)
|
|
if(pulse == PULSE_2FAST)
|
|
take_internal_damage(heart_tachycardia_damage * seconds_per_tick)
|
|
if(pulse == PULSE_THREADY)
|
|
take_internal_damage(heart_fibrillation_damage * seconds_per_tick)
|
|
handle_heartbeat()
|
|
handle_blood()
|
|
..()
|
|
|
|
/obj/item/organ/internal/heart/proc/handle_pulse()
|
|
if(owner.stat == DEAD || (species && species.flags & NO_BLOOD) || BP_IS_ROBOTIC(src)) //No heart, no pulse, buddy. Or if the heart is robotic. Or you're dead.
|
|
pulse = PULSE_NONE
|
|
return
|
|
|
|
// pulse mod starts out as just the chemical effect amount
|
|
var/pulse_mod = owner.chem_effects[CE_PULSE] // TODO: Make chems go through signals.
|
|
var/is_stable = owner.chem_effects[CE_STABLE]
|
|
var/oxy = owner.get_blood_oxygenation()
|
|
var/circulation = owner.get_blood_circulation()
|
|
var/canceled = FALSE
|
|
|
|
// Check if any components on the user wish to mess with the pulse calculations.
|
|
SEND_SIGNAL(owner, COMSIG_HEART_PULSE_EVENT, &pulse_mod, &is_stable, &oxy, &circulation, &canceled)
|
|
if(canceled)
|
|
return // Oh hey someone stopped my heart from beating via a SIGNAL response!
|
|
|
|
// If you have enough heart chemicals to be over 2, take extra damage per tick.
|
|
if(pulse_mod > 2 && !is_stable)
|
|
var/damage_chance = (pulse_mod - 2) ** 2
|
|
if(prob(damage_chance))
|
|
take_internal_damage(damage_from_chemicals)
|
|
|
|
// Now pulse mod is impacted by shock stage and other things too
|
|
if(owner.shock_stage > first_shock_stage)
|
|
pulse_mod++
|
|
if(owner.shock_stage > second_shock_stage)
|
|
pulse_mod++
|
|
|
|
if(oxy < BLOOD_VOLUME_OKAY) //brain wants us to get moar oxygen
|
|
pulse_mod++
|
|
if(oxy < BLOOD_VOLUME_BAD) //MOAR
|
|
pulse_mod++
|
|
|
|
if(owner.status_flags & FAKEDEATH)
|
|
pulse = clamp(PULSE_NONE + pulse_mod, PULSE_NONE, PULSE_2FAST) //pretend that we're dead. unlike actual death, can be inflienced by meds
|
|
return
|
|
|
|
//If heart is stopped, it isn't going to restart itself randomly.
|
|
if(pulse == PULSE_NONE)
|
|
return
|
|
else //and if it's beating, let's see if it should
|
|
var/should_stop = prob(80) && circulation < BLOOD_VOLUME_SURVIVE //cardiovascular shock, not enough liquid to pump
|
|
should_stop = should_stop || prob(max(0, owner.getBrainLoss() - owner.maxhealth * 0.75)) //brain failing to work heart properly
|
|
should_stop = should_stop || (prob(fibrillation_stop_risk) && pulse == PULSE_THREADY) //erratic heart patterns, usually caused by oxyloss
|
|
should_stop = should_stop || owner.chem_effects[CE_NOPULSE]
|
|
if(should_stop) // The heart has stopped due to going into traumatic or cardiovascular shock.
|
|
to_chat(owner, SPAN_DANGER("Your heart has stopped!"))
|
|
pulse = PULSE_NONE
|
|
return
|
|
|
|
// Pulse normally shouldn't go above PULSE_2FAST
|
|
pulse = clamp(PULSE_NORM + pulse_mod, PULSE_SLOW, PULSE_2FAST)
|
|
|
|
// If fibrillation, then it can be PULSE_THREADY
|
|
var/fibrillation = oxy <= BLOOD_VOLUME_SURVIVE || (owner.shock_stage > shock_stage_for_fibrillation && prob(shock_risk_from_pain))
|
|
if(pulse && fibrillation) //I SAID MOAR OXYGEN
|
|
pulse = PULSE_THREADY
|
|
|
|
// Stablising chemicals pull the heartbeat towards the center
|
|
if(pulse != PULSE_NORM && is_stable)
|
|
if(pulse > PULSE_NORM)
|
|
pulse--
|
|
else
|
|
pulse++
|
|
|
|
/obj/item/organ/internal/heart/proc/handle_heartbeat()
|
|
if(pulse >= PULSE_2FAST || owner.shock_stage >= palpitations_threshold)
|
|
//PULSE_THREADY - maximum value for pulse, currently it is 5.
|
|
//High pulse value corresponds to a fast rate of heartbeat.
|
|
//Divided by 2, otherwise it is too slow.
|
|
var/rate = (PULSE_THREADY - pulse)/2
|
|
switch(owner.chem_effects[CE_PULSE])
|
|
if(2 to INFINITY)
|
|
heartbeat++
|
|
if(-INFINITY to -2)
|
|
heartbeat--
|
|
|
|
if(heartbeat >= rate)
|
|
heartbeat = 0
|
|
sound_to(owner, sound(heartbeat_sound, 0, 0, 0, 50))
|
|
else
|
|
heartbeat++
|
|
|
|
/obj/item/organ/internal/heart/proc/handle_blood()
|
|
if(!owner)
|
|
return
|
|
|
|
if(owner.species && owner.species.flags & NO_BLOOD)
|
|
return
|
|
|
|
if(!owner || owner.stat == DEAD || owner.bodytemperature < minimum_temperature_to_pump_blood || owner.InStasis()) //Dead or cryosleep people do not pump the blood.
|
|
return
|
|
|
|
if(pulse != PULSE_NONE || BP_IS_ROBOTIC(src))
|
|
var/blood_volume = round(REAGENT_VOLUME(owner.vessel, /singleton/reagent/blood))
|
|
var/cut_bloodloss_modifier = base_cut_bloodloss_modifier
|
|
var/arterial_bloodloss_modifier = base_arterial_bloodloss_modifier
|
|
|
|
SEND_SIGNAL(owner, COMSIG_HEART_BLEED_EVENT, &blood_volume, &cut_bloodloss_modifier, &arterial_bloodloss_modifier)
|
|
|
|
//Blood regeneration if there is some space
|
|
if(blood_volume < species.blood_volume && blood_volume)
|
|
if(REAGENT_DATA(owner.vessel, /singleton/reagent/blood)) // Make sure there's blood at all
|
|
owner.vessel.add_reagent(/singleton/reagent/blood, (BLOOD_REGEN_RATE * blood_regen_modifier) + LAZYACCESS(owner.chem_effects, CE_BLOODRESTORE), temperature = species?.body_temperature)
|
|
if(blood_volume <= BLOOD_VOLUME_SAFE) //We lose nutrition and hydration very slowly if our blood is too low
|
|
owner.adjustNutritionLoss(nutrition_cost_per_blood_regen)
|
|
owner.adjustHydrationLoss(hydration_cost_per_blood_regen)
|
|
|
|
//Bleeding out
|
|
var/blood_max = 0
|
|
var/open_wound
|
|
var/list/do_spray = list()
|
|
for(var/obj/item/organ/external/temp in owner.bad_external_organs)
|
|
if((temp.status & ORGAN_BLEEDING) && !BP_IS_ROBOTIC(temp))
|
|
for(var/datum/wound/W as anything in temp.wounds)
|
|
if(W.bleeding())
|
|
open_wound = TRUE
|
|
if(temp.applied_pressure)
|
|
if(ishuman(temp.applied_pressure))
|
|
var/mob/living/carbon/human/H = temp.applied_pressure
|
|
H.bloody_hands(src, 0)
|
|
var/min_eff_damage = max(0, W.damage - 10) / 6
|
|
blood_max += max(min_eff_damage, W.damage - 30) / 40 * cut_bloodloss_modifier
|
|
else
|
|
blood_max += ((W.damage / 40) * species.bleed_mod * cut_bloodloss_modifier)
|
|
|
|
if(temp.status & ORGAN_ARTERY_CUT)
|
|
var/bleed_amount = FLOOR((owner.vessel.total_volume / (temp.applied_pressure || !open_wound ? 450 : 250)) * temp.arterial_bleed_severity, 0.1)
|
|
if(bleed_amount)
|
|
if((CE_BLOODCLOT in owner.chem_effects) && !(owner.chem_effects[CE_BLOODTHIN]))
|
|
bleed_amount *= 0.8 // won't do much, but it'll help
|
|
if(CE_BLOODTHIN in owner.chem_effects)
|
|
bleed_amount *= 1+(owner.chem_effects[CE_BLOODTHIN]/100)
|
|
if(open_wound)
|
|
blood_max += bleed_amount
|
|
do_spray += "[temp.name]"
|
|
else
|
|
owner.vessel.remove_reagent(/singleton/reagent/blood, bleed_amount * arterial_bloodloss_modifier)
|
|
|
|
switch(pulse)
|
|
if(PULSE_SLOW)
|
|
blood_max *= slow_bleeding_modifier
|
|
if(PULSE_FAST)
|
|
blood_max *= fast_bleeding_modifier
|
|
if(PULSE_2FAST)
|
|
blood_max *= too_fast_bleeding_modifier
|
|
if (PULSE_THREADY)
|
|
blood_max *= thready_bleeding_modifier
|
|
|
|
blood_max *= bleed_drip_modifier
|
|
|
|
if(CE_BLOODCLOT in owner.chem_effects)
|
|
blood_max *= 0.7
|
|
else if(CE_STABLE in owner.chem_effects)
|
|
blood_max *= 0.8
|
|
|
|
if(world.time >= next_blood_squirt && istype(owner.loc, /turf) && do_spray.len)
|
|
owner.visible_message(SPAN_DANGER("Blood squirts from \the [owner]'s [pick(do_spray)]!"), \
|
|
SPAN_DANGER("<font size=3>Blood sprays out of your [pick(do_spray)]!</font>"))
|
|
owner.eye_blurry = 2
|
|
owner.Stun(1)
|
|
next_blood_squirt = world.time + time_between_arterial_sprays
|
|
var/turf/sprayloc = get_turf(owner)
|
|
blood_max -= owner.drip(Ceiling(blood_max/3), sprayloc)
|
|
if(blood_max > 0)
|
|
blood_max -= owner.blood_squirt(blood_max, sprayloc, blood_spray_distance)
|
|
if(blood_max > 0)
|
|
owner.drip(blood_max, get_turf(owner))
|
|
else
|
|
owner.drip(blood_max)
|
|
|
|
/obj/item/organ/internal/heart/proc/is_working()
|
|
if(!is_usable())
|
|
return FALSE
|
|
|
|
return pulse > PULSE_NONE || BP_IS_ROBOTIC(src) || (owner.status_flags & FAKEDEATH)
|
|
|
|
/obj/item/organ/internal/heart/do_surge_effects()
|
|
var/obj/item/organ/external/E = owner.organs_by_name[parent_organ]
|
|
if(prob(surge_damage))
|
|
owner.custom_pain(SPAN_DANGER("Your [E.name] stings horribly!"), 15, FALSE, E)
|
|
sound_to(owner, sound(heartbeat_sound, 0, 0, 0, 100))
|
|
|
|
/obj/item/organ/internal/heart/listen()
|
|
if((BP_IS_ROBOTIC(src) && is_working()) && !fake_pulse)
|
|
if(is_bruised())
|
|
return "sputtering pump"
|
|
else
|
|
return "steady whirr of the pump"
|
|
|
|
if (owner.status_flags & FAKEDEATH)
|
|
return "no pulse"
|
|
|
|
if (fake_pulse)
|
|
return "normal pulse"
|
|
|
|
if(!pulse)
|
|
return "no pulse"
|
|
|
|
var/pulsesound = "normal"
|
|
if(is_bruised())
|
|
pulsesound = "irregular"
|
|
|
|
switch(pulse)
|
|
if(PULSE_SLOW)
|
|
pulsesound = "slow"
|
|
if(PULSE_FAST)
|
|
pulsesound = "fast"
|
|
if(PULSE_2FAST)
|
|
pulsesound = "very fast"
|
|
if(PULSE_THREADY)
|
|
pulsesound = "extremely fast and faint"
|
|
|
|
. = "[pulsesound] pulse"
|
|
|
|
// Galatean boosted heart
|
|
/obj/item/organ/internal/heart/boosted_heart
|
|
name = "boosted heart"
|
|
desc = "Intended for athletes, some workers, and soldiers, this improved heart increases blood flow and circulation." \
|
|
+ "It provides an improvement to blood oxygenation and stamina, at the cost of requiring more food and water." \
|
|
+ "Outside of Galatea, this augment is popular among professional athletes."
|
|
icon = 'icons/obj/organs/bioaugs.dmi'
|
|
icon_state = "boosted_heart"
|
|
max_damage = 80
|
|
min_broken_damage = 60
|
|
shock_stage_for_fibrillation = 140
|
|
fibrillation_stop_risk = 3.5
|
|
base_pump_rate = 1.15
|
|
nutrition_cost_per_blood_regen = 4
|
|
hydration_cost_per_blood_regen = 3
|
|
blood_regen_modifier = 1.1
|
|
bleed_drip_modifier = 1.1
|
|
blood_spray_distance = 3
|
|
|
|
// Example heart item that has significantly lowered statistics.
|
|
// TODO: After refactoring the organ selector, make it so that this is a selectable heart type.
|
|
/obj/item/organ/internal/heart/scarred_heart
|
|
name = "scarred heart"
|
|
desc = "Life has not been good to this old ticker."
|
|
icon = 'icons/obj/organs/bioaugs.dmi'
|
|
icon_state = "scarred_heart"
|
|
max_damage = 45
|
|
min_broken_damage = 30
|
|
fibrillation_stop_risk = 7.5
|
|
shock_stage_for_fibrillation = 100
|
|
blood_regen_modifier = 0.9
|
|
base_pump_rate = 0.9
|
|
thready_pump_modifier = 1.5
|
|
damage_from_chemicals = 0.7
|
|
blood_spray_distance = 1
|
|
|
|
/obj/item/organ/internal/heart/alien_heart
|
|
name = "anomalous mercurial flesh"
|
|
desc = "A slab of flesh made seemingly from mercury, yet with a recognizably organic shape. It is soft to the touch, pliable like skin, yet is as tough as steel."
|
|
icon = 'icons/obj/organs/bioaugs.dmi'
|
|
icon_state = "alien_heart"
|
|
max_damage = 200
|
|
min_bruised_damage = 150
|
|
min_broken_damage = 175
|
|
|
|
/obj/item/organ/internal/heart/alien_heart/Initialize()
|
|
. = ..()
|
|
// RANDOMIZE EVERYTHING.
|
|
// DEAR READER, I HOPE YOU HAVE FUN IF YOU MAKE THE MISTAKE OF PUTTING THIS IN A HUMAN.
|
|
// BECAUSE YOU ARE NOT GUARANTEED IT CAN SUSTAIN LIFE.
|
|
heart_fibrillation_damage *= rand(0.5, 2)
|
|
heart_tachycardia_damage *= rand(0.5, 2)
|
|
fibrillation_stop_risk = rand(1, 99)
|
|
shock_stage_for_fibrillation *= rand(0.5, 2)
|
|
shock_risk_from_pain = rand(1, 99)
|
|
first_shock_stage *= rand(0.75, 1.33)
|
|
second_shock_stage *= rand(0.75, 1.33)
|
|
blood_regen_modifier *= rand(0.5, 2)
|
|
base_arterial_bloodloss_modifier *= rand(0.5, 2)
|
|
base_cut_bloodloss_modifier *= rand(0.5, 2)
|
|
nutrition_cost_per_blood_regen *= rand(0.5, 2)
|
|
hydration_cost_per_blood_regen *= rand(0.5, 2)
|
|
minimum_temperature_to_pump_blood *= rand(0.75, 1.33)
|
|
time_between_arterial_sprays *= rand(0.5, 2)
|
|
bleed_drip_modifier *= rand(0.5, 2)
|
|
palpitations_threshold *= rand(0.5, 2)
|
|
slow_bleeding_modifier *= rand(0.75, 1.33)
|
|
fast_bleeding_modifier *= rand(0.75, 1.33)
|
|
thready_bleeding_modifier *= rand(0.75, 1.33)
|
|
base_pump_rate *= rand(0.75, 1.33)
|
|
none_pump_modifier *= rand(0.75, 1.33)
|
|
slow_pump_modifier *= rand(0.75, 1.33)
|
|
fast_pump_modifier *= rand(0.75, 1.33)
|
|
too_fast_pump_modifier *= rand(0.75, 1.33)
|
|
thready_pump_modifier *= rand(0.75, 1.33)
|
|
if(prob(50))
|
|
fake_pulse = TRUE
|
|
damage_from_chemicals *= rand(0.75, 1.33)
|
|
blood_spray_distance = rand(1, 9)
|