mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-07-13 00:47:31 +01:00
2f4a8d6f3d
title, h2 and manual font size brokey --------- Co-authored-by: Matt Atlas <liermattia@gmail.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=5>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)
|