Revives Organ Datumization (#23397)

* first runthrough lol

* okay make this shit actually work

* nerfs slime people

* fixes

* more changes

* fix

* bim bim bam bam

* ok back in the head becuz i dont give a shit

* Sirryan review

* Sirryan review

* Fixes CI

* Stealthy CI fail

---------

Co-authored-by: Contrabang <91113370+Contrabang@users.noreply.github.com>
This commit is contained in:
DGamerL
2024-01-21 23:23:51 +01:00
committed by GitHub
parent 0b8c196f67
commit 79a7558df7
32 changed files with 653 additions and 521 deletions
+4
View File
@@ -11,6 +11,10 @@
#define ORGAN_BURNT (1 << 7)
#define ORGAN_SALVED (1 << 8)
// Organ datum defines. Each one of these represents a slot for organ datums in internal_organ_datums
#define ORGAN_DATUM_HEART "heart"
#define ORGAN_DATUM_LUNGS "lungs"
// For limb resistance flags
#define CANNOT_BREAK (1 << 0)
#define CANNOT_DISMEMBER (1 << 1)
+5 -5
View File
@@ -205,12 +205,12 @@
return
if(target.undergoing_cardiac_arrest()) // Can have a heart attack and heart is either missing, necrotic, or not beating
var/obj/item/organ/internal/heart/heart = target.get_int_organ(/obj/item/organ/internal/heart)
var/datum/organ/heart/heart = target.get_int_organ_datum(ORGAN_DATUM_HEART)
if(!heart)
user.visible_message("<span class='boldnotice'>[defib_ref] buzzes: Resuscitation failed - Failed to pick up any heart electrical activity.</span>")
else if(heart.status & ORGAN_DEAD)
else if(heart.linked_organ.status & ORGAN_DEAD)
user.visible_message("<span class='boldnotice'>[defib_ref] buzzes: Resuscitation failed - Heart necrosis detected.</span>")
if(!heart || (heart.status & ORGAN_DEAD))
if(!heart || (heart.linked_organ.status & ORGAN_DEAD))
playsound(get_turf(defib_ref), 'sound/machines/defib_failed.ogg', 50, 0)
busy = FALSE
return
@@ -384,8 +384,8 @@
return
if(electrocute_mob(affecting, power_source, origin)) // shock anyone touching them >:)
var/obj/item/organ/internal/heart/HE = affecting.get_organ_slot("heart")
if(HE.parent_organ == "chest" && affecting.has_both_hands()) // making sure the shock will go through their heart (drask hearts are in their head), and that they have both arms so the shock can cross their heart inside their chest
var/datum/organ/heart/heart = affecting.get_int_organ_datum(ORGAN_DATUM_HEART)
if(heart.linked_organ.parent_organ == "chest" && affecting.has_both_hands()) // making sure the shock will go through their heart (drask hearts are in their head), and that they have both arms so the shock can cross their heart inside their chest
affecting.visible_message("<span class='danger'>[affecting]'s entire body shakes as a shock travels up [affecting.p_their()] arm!</span>", \
"<span class='userdanger'>You feel a powerful shock travel up your [affecting.hand ? affecting.get_organ("l_arm") : affecting.get_organ("r_arm")] and back down your [affecting.hand ? affecting.get_organ("r_arm") : affecting.get_organ("l_arm")]!</span>")
affecting.set_heartattack(TRUE)
+8 -3
View File
@@ -153,9 +153,14 @@
if(HAS_TRAIT(src, TRAIT_VIRUSIMMUNE) && !D.bypasses_immunity)
return FALSE
for(var/thing in D.required_organs)
if(!((locate(thing) in bodyparts) || (locate(thing) in internal_organs)))
return FALSE
for(var/organ in D.required_organs)
if(istext(organ) && get_int_organ_datum(organ))
continue
if(locate(organ) in internal_organs)
continue
if(locate(organ) in bodyparts)
continue
return FALSE
return ..()
/mob/living/carbon/human/monkey/CanContractDisease(datum/disease/D)
+4 -1
View File
@@ -87,7 +87,10 @@
cure_chance = 10
stage_prob = 5
severity = HARMFUL
required_organs = list(/obj/item/organ/internal/heart)
disease_flags = CURABLE
required_organs = list(ORGAN_DATUM_HEART)
bypasses_immunity = TRUE
virus_heal_resistant = TRUE
/datum/disease/critical/heart_failure/stage_act()
if(..())
+2 -2
View File
@@ -9,9 +9,9 @@
viable_mobtypes = list(/mob/living/carbon/human)
cure_chance = 5 // Like hell are you getting out of hell
desc = "A rare highly transmittable virulent virus. Few samples exist, rumoured to be carefully grown and cultured by clandestine bio-weapon specialists. Causes fever, blood vomiting, lung damage, weight loss, and fatigue."
required_organs = list(/obj/item/organ/internal/lungs)
required_organs = list(ORGAN_DATUM_LUNGS)
severity = HARMFUL
bypasses_immunity = TRUE // Fungal and bacterial in nature; also infects the lungs
bypasses_immunity = TRUE //Fungal and bacterial in nature; also infects the lungs
/datum/disease/tuberculosis/stage_act()
if(!..())
@@ -29,7 +29,7 @@
/datum/surgery_step/internal/extract_organ/begin_step(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
for(var/obj/item/I in target.internal_organs)
// Allows for multiple subtypes of heart.
// Allows for multiple subtypes of heart. Doesn't use the organ datum so that slimes dont get their brains pulled out of their head
if(istype(I, /obj/item/organ/internal/heart))
IC = I
break
@@ -6,14 +6,15 @@
dead_icon = null
status = ORGAN_ROBOT
origin_tech = "materials=4;biotech=7;abductor=3"
beating = TRUE
organ_datums = list(/datum/organ/heart/always_beating) // alien glands are immune to stopping
tough = TRUE //not easily broken by combat damage
var/cooldown_low = 300
var/cooldown_high = 300
var/next_activation = 0
var/uses // -1 For inifinite
var/human_only = FALSE
var/active = FALSE
tough = TRUE //not easily broken by combat damage
var/mind_control_uses = 1
var/mind_control_duration = 1800
@@ -84,9 +85,6 @@
update_gland_hud()
/obj/item/organ/internal/heart/gland/on_life()
if(!beating)
// alien glands are immune to stopping.
beating = TRUE
if(!active)
return
if(!ownerCheck())
@@ -212,6 +212,7 @@
icon = 'icons/obj/surgery.dmi'
icon_state = "demon_heart"
origin_tech = "combat=5;biotech=7"
organ_datums = list(/datum/organ/heart/always_beating)
/obj/item/organ/internal/heart/demon/update_icon_state()
return //always beating visually
@@ -219,9 +220,6 @@
/obj/item/organ/internal/heart/demon/prepare_eat()
return // Just so people don't accidentally waste it
/obj/item/organ/internal/heart/demon/Stop()
return 0 // Always beating.
/obj/item/organ/internal/heart/demon/attack_self(mob/living/user)
user.visible_message("<span class='warning'>[user] raises [src] to [user.p_their()] mouth and tears into it with [user.p_their()] teeth!</span>", \
"<span class='danger'>An unnatural hunger consumes you. You raise [src] to your mouth and devour it!</span>")
@@ -114,7 +114,7 @@
/obj/item/painter/suicide_act(mob/user)
user.visible_message("<span class='suicide'>[user] is inhaling toner from [src]! It looks like [user.p_theyre()] trying to commit suicide!</span>")
playsound(src, usesound, 50, TRUE)
var/obj/item/organ/internal/lungs/L = user.get_organ_slot("lungs")
var/obj/item/organ/internal/lungs/L = user.get_organ_slot("lungs") // not going to use an organ datum here, would be too easy for slime people to throw up their brains
var/turf/T = get_turf(user)
if(!do_mob(user, user, 3 SECONDS) || !L)
return SHAME
+3 -3
View File
@@ -200,11 +200,11 @@ REAGENT SCANNER
if(H.undergoing_cardiac_arrest())
var/obj/item/organ/internal/heart/heart = H.get_int_organ(/obj/item/organ/internal/heart)
if(heart && !(heart.status & ORGAN_DEAD))
msgs += "<span class='notice'><font color='red'><b>The patient's heart has stopped.</b>\nPossible Cure: Electric Shock</font></span>"
msgs += "<span class='notice'><font color='red'><b>The patient's heart has stopped.</b>\nPossible Cure: Electric Shock</font>"
else if(heart && (heart.status & ORGAN_DEAD))
msgs += "<span class='notice'><font color='red'><b>Subject's heart is necrotic.</b></font></span>"
msgs += "<span class='notice'><font color='red'><b>Subject's heart is necrotic.</b></font>"
else if(!heart)
msgs += "<span class='notice'><font color='red'><b>Subject has no heart.</b></font></span>"
msgs += "<span class='notice'><font color='red'><b>Subject has no heart.</b></font>"
if(H.getStaminaLoss())
msgs += "<span class='info'>Subject appears to be suffering from fatigue.</span>"
@@ -156,8 +156,10 @@
user.visible_message("[user] places [src] against [user.p_their()] chest and listens attentively.", "You place [src] against your chest...")
else
user.visible_message("[user] places \the [src] against [M]'s chest and listens attentively.", "You place \the [src] against [M]'s chest...")
var/obj/item/organ/internal/H = M.get_int_organ(/obj/item/organ/internal/heart)
var/obj/item/organ/internal/L = M.get_int_organ(/obj/item/organ/internal/lungs)
var/datum/organ/heart/heart_datum = M.get_int_organ_datum(ORGAN_DATUM_HEART)
var/obj/item/organ/internal/H = heart_datum.linked_organ
var/datum/organ/lungs/lung_datum = M.get_int_organ_datum(ORGAN_DATUM_LUNGS)
var/obj/item/organ/internal/L = lung_datum.linked_organ
if(M.pulse && (H || (L && !HAS_TRAIT(M, TRAIT_NOBREATH))))
var/color = "notice"
if(H)
@@ -3,8 +3,10 @@
pressure_resistance = 15
var/list/stomach_contents
var/list/processing_patches
var/list/internal_organs = list()
var/list/internal_organs_slot = list() //Same as above, but stores "slot ID" - "organ" pairs for easy access.
var/list/internal_organs = list()
var/list/internal_organs_slot = list() //Same as above, but stores "slot ID" - "organ" pairs for easy access.
/// An associated list of strings that associate it with the organ datum, e.g. [ORGAN_DATUM_HEART] = /datum/organ/heart
var/list/internal_organ_datums = list()
var/life_tick = 0 // The amount of life ticks that have processed on this mob.
@@ -105,7 +105,7 @@
if(status_flags & GODMODE)
return FALSE
var/lungs = get_organ_slot("lungs")
var/lungs = get_int_organ_datum(ORGAN_DATUM_LUNGS)
if(!lungs)
adjustOxyLoss(2)
@@ -201,6 +201,9 @@
for(var/thing in internal_organs)
var/obj/item/organ/internal/O = thing
O.on_life()
for(var/organ_tag in internal_organ_datums)
var/datum/organ/datum_organ_var_name_idk = internal_organ_datums[organ_tag]
datum_organ_var_name_idk.on_life()
/mob/living/carbon/handle_diseases()
for(var/thing in viruses)
@@ -131,31 +131,31 @@
/mob/living/carbon/human/check_breath(datum/gas_mixture/breath)
var/obj/item/organ/internal/L = get_organ_slot("lungs")
var/datum/organ/lungs/lung_datum = get_int_organ_datum(ORGAN_DATUM_LUNGS)
if(!L || L && (L.status & ORGAN_DEAD))
if(health >= HEALTH_THRESHOLD_CRIT)
adjustOxyLoss(HUMAN_MAX_OXYLOSS + 1)
else if(!HAS_TRAIT(src, TRAIT_NOCRITDAMAGE))
adjustOxyLoss(HUMAN_MAX_OXYLOSS)
if(lung_datum && !(lung_datum.linked_organ.status & ORGAN_DEAD))
lung_datum.check_breath(breath, src)
return
if(dna.species)
var/datum/species/S = dna.species
// We have no lungs, or our lungs are dead!
if(health >= HEALTH_THRESHOLD_CRIT)
adjustOxyLoss(HUMAN_MAX_OXYLOSS + 1)
else if(!HAS_TRAIT(src, TRAIT_NOCRITDAMAGE))
adjustOxyLoss(HUMAN_MAX_OXYLOSS)
if(S.breathid == "o2")
if(dna.species)
var/datum/species/S = dna.species
switch(S.breathid)
if("o2")
throw_alert("not_enough_oxy", /obj/screen/alert/not_enough_oxy)
else if(S.breathid == "tox")
if("tox")
throw_alert("not_enough_tox", /obj/screen/alert/not_enough_tox)
else if(S.breathid == "co2")
if("co2") // currently unused
throw_alert("not_enough_co2", /obj/screen/alert/not_enough_co2)
else if(S.breathid == "n2")
if("n2")
throw_alert("not_enough_nitro", /obj/screen/alert/not_enough_nitro)
return FALSE
else
if(istype(L, /obj/item/organ/internal/lungs))
var/obj/item/organ/internal/lungs/lun = L
lun.check_breath(breath, src)
return FALSE
// USED IN DEATHWHISPERS
/mob/living/carbon/human/proc/isInCrit()
@@ -796,14 +796,13 @@
H.fakevomit()
/mob/living/carbon/human/proc/handle_heartbeat()
var/client/C = src.client
if(C && C.prefs.sound & SOUND_HEARTBEAT) //disable heartbeat by pref
var/obj/item/organ/internal/heart/H = get_int_organ(/obj/item/organ/internal/heart)
if(client && client.prefs.sound & SOUND_HEARTBEAT) //disable heartbeat by pref
var/datum/organ/heart/H = get_int_organ_datum(ORGAN_DATUM_HEART)
if(!H) //H.status will runtime if there is no H (obviously)
return
if(H.is_robotic()) //Handle robotic hearts specially with a wuuuubb. This also applies to machine-people.
if(H.linked_organ.is_robotic()) //Handle robotic hearts specially with a wuuuubb. This also applies to machine-people.
if(isinspace())
//PULSE_THREADY - maximum value for pulse, currently it 5.
//High pulse value corresponds to a fast rate of heartbeat.
@@ -816,7 +815,6 @@
else
heartbeat++
return
return
if(pulse == PULSE_NONE)
@@ -851,21 +849,18 @@
if(!can_heartattack())
return FALSE
var/obj/item/organ/internal/heart/heart = get_int_organ(/obj/item/organ/internal/heart)
if(!istype(heart) || (heart.status & ORGAN_DEAD) || !heart.beating)
var/datum/organ/heart/heart_datum = get_int_organ_datum(ORGAN_DATUM_HEART)
if(!istype(heart_datum) || (heart_datum.linked_organ.status & ORGAN_DEAD))
return TRUE
return FALSE
return !heart_datum.beating
/mob/living/carbon/human/proc/set_heartattack(status)
if(!can_heartattack())
return FALSE
var/obj/item/organ/internal/heart/heart = get_int_organ(/obj/item/organ/internal/heart)
if(!istype(heart))
return FALSE
heart.beating = !status
var/datum/organ/heart/heart_datum = get_int_organ_datum(ORGAN_DATUM_HEART)
heart_datum?.change_beating(!status)
/mob/living/carbon/human/handle_heartattack()
if(!can_heartattack() || !undergoing_cardiac_arrest() || reagents.has_reagent("corazone"))
@@ -1207,20 +1207,16 @@
..()
/mob/living/carbon/human/proc/is_lung_ruptured()
var/obj/item/organ/internal/lungs/L = get_int_organ(/obj/item/organ/internal/lungs)
if(!L)
return 0
var/datum/organ/lungs/L = get_int_organ_datum(ORGAN_DATUM_LUNGS)
return L.is_bruised()
return L?.linked_organ.is_bruised()
/mob/living/carbon/human/proc/rupture_lung()
var/obj/item/organ/internal/lungs/L = get_int_organ(/obj/item/organ/internal/lungs)
if(!L)
return 0
if(!L.is_bruised())
L.custom_pain("You feel a stabbing pain in your chest!")
L.damage = L.min_bruised_damage
var/datum/organ/lungs/L = get_int_organ_datum(ORGAN_DATUM_LUNGS)
if(L && !L.linked_organ.is_bruised())
var/obj/item/organ/external/affected = get_organ("chest")
affected.custom_pain("You feel a stabbing pain in your chest!")
L.linked_organ.damage = L.linked_organ.min_bruised_damage
/mob/living/carbon/human/cuff_resist(obj/item/I)
if(HAS_TRAIT(src, TRAIT_HULK))
@@ -162,17 +162,6 @@ old_ue: Set this to a UE string, and this proc will overwrite the dna of organs
if(assimilate || O.dna.unique_enzymes == ue_to_compare)
O.set_dna(dna)
/*
Given the name of an organ, returns the external organ it's contained in
I use this to standardize shadowling dethrall code
-- Crazylemon
*/
/mob/living/carbon/human/proc/named_organ_parent(organ_name)
if(!get_int_organ_tag(organ_name))
return null
var/obj/item/organ/internal/O = get_int_organ_tag(organ_name)
return O.parent_organ
/mob/living/carbon/human/has_organic_damage()
var/robo_damage = 0
var/perma_injury_damage = 0
@@ -67,10 +67,10 @@
return TRUE
// how do species that don't breathe talk? magic, that's what.
var/breathes = (!HAS_TRAIT(src, TRAIT_NOBREATH))
var/obj/item/organ/internal/L = get_organ_slot("lungs")
var/datum/organ/lungs/L = get_int_organ_datum(ORGAN_DATUM_LUNGS)
if(HAS_TRAIT(src, TRAIT_MUTE))
return FALSE
if((breathes && !L) || breathes && L && (L.status & ORGAN_DEAD))
if(breathes && (!L || L.linked_organ.status & ORGAN_DEAD))
return FALSE
if(mind)
return !mind.miming
@@ -49,9 +49,7 @@
vision_organ = null
has_organ = list(
"heart" = /obj/item/organ/internal/heart/slime,
"brain" = /obj/item/organ/internal/brain/slime,
"lungs" = /obj/item/organ/internal/lungs/slime
"brain" = /obj/item/organ/internal/brain/slime
)
mutantears = null
suicide_messages = list(
@@ -124,6 +124,7 @@
if(volume > 4.5)
if(ishuman(M))
var/mob/living/carbon/human/H = M
// im not going to make this an organ datum, so that slime people dont get their brain instantly deleted
var/obj/item/organ/internal/heart/ate_heart = H.get_int_organ(/obj/item/organ/internal/heart)
if(ate_heart)
ate_heart.remove(H)
+1 -1
View File
@@ -305,7 +305,7 @@ GLOBAL_LIST_INIT(non_simple_animals, typecacheof(list(/mob/living/carbon/human/m
switch(upgrade_type)
if(VAULT_TOXIN)
to_chat(H, "<span class='notice'>You feel resistant to airborne toxins.</span>")
var/obj/item/organ/internal/lungs/L = H.get_int_organ(/obj/item/organ/internal/lungs)
var/datum/organ/lungs/L = H.get_int_organ_datum(ORGAN_DATUM_LUNGS)
if(L)
L.tox_breath_dam_min = 0
L.tox_breath_dam_max = 0
+2 -1
View File
@@ -187,13 +187,14 @@
/obj/item/organ/internal/brain/prepare_eat()
return // Too important to eat.
// Hello I am from the ministry of rubber forehead aliens how are you
/obj/item/organ/internal/brain/slime
name = "slime core"
desc = "A complex, organic knot of jelly and crystalline particles."
icon = 'icons/mob/slimes.dmi'
icon_state = "green slime extract"
mmi_icon_state = "slime_mmi"
// parent_organ = "chest" Hello I am from the ministry of rubber forehead aliens how are you
organ_datums = list(/datum/organ/heart, /datum/organ/lungs)
/obj/item/organ/internal/brain/golem
name = "Runic mind"
+41 -72
View File
@@ -5,74 +5,35 @@
parent_organ = "chest"
slot = "heart"
origin_tech = "biotech=5"
var/beating = TRUE
dead_icon = "heart-off"
var/icon_base = "heart"
base_icon_state = "heart"
organ_datums = list(/datum/organ/heart)
/obj/item/organ/internal/heart/update_icon_state()
if(beating)
icon_state = "[icon_base]-on"
var/datum/organ/heart/heart = organ_datums["heart"]
if(heart.beating)
icon_state = "[base_icon_state]-on"
else
icon_state = "[icon_base]-off"
/obj/item/organ/internal/heart/remove(mob/living/carbon/M, special = 0)
. = ..()
if(ishuman(M))
var/mob/living/carbon/human/H = M
if(H.stat == DEAD)
Stop()
return
if(!special)
addtimer(CALLBACK(src, PROC_REF(stop_if_unowned)), 120)
/obj/item/organ/internal/heart/emp_act(intensity)
if(!is_robotic() || emp_proof)
return
Stop()
/obj/item/organ/internal/heart/necrotize()
..()
Stop()
icon_state = "[base_icon_state]-off"
/obj/item/organ/internal/heart/attack_self(mob/user)
..()
if(status & ORGAN_DEAD)
to_chat(user, "<span class='warning'>You can't restart a dead heart.</span>")
return
if(!beating)
Restart()
addtimer(CALLBACK(src, PROC_REF(stop_if_unowned)), 80)
var/datum/organ/heart/heart = organ_datums["heart"]
heart.try_restart(8 SECONDS)
/obj/item/organ/internal/heart/safe_replace(mob/living/carbon/human/target)
Restart()
var/datum/organ/heart/heart = organ_datums["heart"]
heart.change_beating(TRUE)
..()
/obj/item/organ/internal/heart/proc/stop_if_unowned()
if(!owner)
Stop()
/obj/item/organ/internal/heart/proc/Stop()
beating = FALSE
update_icon(UPDATE_ICON_STATE)
return TRUE
/obj/item/organ/internal/heart/proc/Restart()
beating = TRUE
update_icon(UPDATE_ICON_STATE)
return TRUE
/obj/item/organ/internal/heart/prepare_eat()
var/obj/S = ..()
if(S)
S.icon_state = dead_icon
return S
/obj/item/organ/internal/heart/cursed
name = "cursed heart"
desc = "it needs to be pumped..."
icon_state = "cursedheart-off"
icon_base = "cursedheart"
base_icon_state = "cursedheart"
origin_tech = "biotech=6"
actions_types = list(/datum/action/item_action/organ_action/cursed_heart)
var/last_pump = 0
@@ -215,7 +176,7 @@
name = "cybernetic heart"
desc = "An electronic device designed to mimic the functions of an organic human heart. Offers no benefit over an organic heart other than being easy to make."
icon_state = "heart-c-on"
icon_base = "heart-c"
base_icon_state = "heart-c"
dead_icon = "heart-c-off"
status = ORGAN_ROBOT
@@ -223,7 +184,7 @@
name = "upgraded cybernetic heart"
desc = "A more advanced version of a cybernetic heart. Grants the user additional stamina and heart stability, but the electronics are vulnerable to shock."
icon_state = "heart-c-u-on"
icon_base = "heart-c-u"
base_icon_state = "heart-c-u"
dead_icon = "heart-c-u-off"
var/attempted_restart = FALSE
@@ -242,27 +203,42 @@
if(!ishuman(owner))
return
if(!(status & ORGAN_DEAD) && !attempted_restart && !beating)
if(status & ORGAN_DEAD)
return
var/boost = emagged ? 2 : 1
owner.AdjustParalysis(-2 SECONDS * boost)
owner.AdjustStunned(-2 SECONDS * boost)
owner.AdjustWeakened(-2 SECONDS * boost)
owner.AdjustKnockDown(-2 SECONDS * boost)
owner.adjustStaminaLoss(-10 * boost)
if(attempted_restart)
return
var/datum/organ/heart/heart_datum = organ_datums["heart"]
if(!heart_datum.beating)
to_chat(owner, "<span class='warning'>Your [name] detects a cardiac event and attempts to return to its normal rhythm!</span>")
if(prob(20) && emagged)
attempted_restart = TRUE
Restart()
addtimer(CALLBACK(src, PROC_REF(message_to_owner), owner, "<span class='warning'>Your [name] returns to its normal rhythm!</span>"), 30)
addtimer(CALLBACK(src, PROC_REF(recharge)), 200)
heart_datum.change_beating(TRUE) // Mötley Crüe - Kickstart My Heart
addtimer(CALLBACK(src, PROC_REF(message_to_owner), owner, "<span class='warning'>Your [name] returns to its normal rhythm!</span>"), 3 SECONDS)
addtimer(CALLBACK(src, PROC_REF(recharge)), 20 SECONDS)
else if(prob(10))
attempted_restart = TRUE
Restart()
addtimer(CALLBACK(src, PROC_REF(message_to_owner), owner, "<span class='warning'>Your [name] returns to its normal rhythm!</span>"), 30)
addtimer(CALLBACK(src, PROC_REF(recharge)), 300)
heart_datum.change_beating(TRUE)
addtimer(CALLBACK(src, PROC_REF(message_to_owner), owner, "<span class='warning'>Your [name] returns to its normal rhythm!</span>"), 3 SECONDS)
addtimer(CALLBACK(src, PROC_REF(recharge)), 30 SECONDS)
else
attempted_restart = TRUE
if(emagged)
addtimer(CALLBACK(src, PROC_REF(recharge)), 200)
addtimer(CALLBACK(src, PROC_REF(recharge)), 20 SECONDS)
else
addtimer(CALLBACK(src, PROC_REF(recharge)), 300)
addtimer(CALLBACK(src, PROC_REF(message_to_owner), owner, "<span class='warning'>Your [name] fails to return to its normal rhythm!</span>"), 30)
addtimer(CALLBACK(src, PROC_REF(recharge)), 30 SECONDS)
addtimer(CALLBACK(src, PROC_REF(message_to_owner), owner, "<span class='warning'>Your [name] fails to return to its normal rhythm!</span>"), 3 SECONDS)
if(!(status & ORGAN_DEAD) && !attempted_restart && owner.HasDisease(new /datum/disease/critical/heart_failure(0)))
if(owner.HasDisease(new /datum/disease/critical/heart_failure(0)))
to_chat(owner, "<span class='warning'>Your [name] detects a cardiac event and attempts to return to its normal rhythm!</span>")
if(prob(40) && emagged)
attempted_restart = TRUE
@@ -284,14 +260,6 @@
addtimer(CALLBACK(src, PROC_REF(recharge)), 300)
addtimer(CALLBACK(src, PROC_REF(message_to_owner), owner, "<span class='warning'>Your [name] fails to return to its normal rhythm!</span>"), 30)
if(!(status & ORGAN_DEAD))
var/boost = emagged ? 2 : 1
owner.AdjustParalysis(-2 SECONDS * boost)
owner.AdjustStunned(-2 SECONDS * boost)
owner.AdjustWeakened(-2 SECONDS * boost)
owner.AdjustKnockDown(-2 SECONDS * boost)
owner.adjustStaminaLoss(-10 * boost)
/obj/item/organ/internal/heart/cybernetic/upgraded/proc/message_to_owner(mob/M, message)
to_chat(M, message)
@@ -333,7 +301,8 @@
owner.ForceContractDisease(new /datum/disease/critical/heart_failure(0))
if(prob(numMid))
to_chat(owner, "<span class='danger'>Your [name] stops beating!</span>")
Stop()
var/datum/organ/heart/heart_datum = organ_datums["heart"]
heart_datum.change_beating(FALSE) // Rambunctious Crew - Stop My Fucking Heart
if(prob(numLow))
to_chat(owner, "<span class='danger'>Your [name] shuts down!</span>")
necrotize()
+24 -341
View File
@@ -7,306 +7,7 @@
gender = PLURAL
w_class = WEIGHT_CLASS_NORMAL
//Breath damage
var/safe_oxygen_min = 16 // Minimum safe partial pressure of O2, in kPa
var/safe_oxygen_max = 0
var/safe_nitro_min = 0
var/safe_nitro_max = 0
var/safe_co2_min = 0
var/safe_co2_max = 10 // Yes it's an arbitrary value who cares?
var/safe_toxins_min = 0
var/safe_toxins_max = 0.05
var/SA_para_min = 1 //Sleeping agent
var/SA_sleep_min = 5 //Sleeping agent
var/oxy_breath_dam_min = MIN_TOXIC_GAS_DAMAGE
var/oxy_breath_dam_max = MAX_TOXIC_GAS_DAMAGE
var/oxy_damage_type = OXY
var/nitro_breath_dam_min = MIN_TOXIC_GAS_DAMAGE
var/nitro_breath_dam_max = MAX_TOXIC_GAS_DAMAGE
var/nitro_damage_type = OXY
var/co2_breath_dam_min = MIN_TOXIC_GAS_DAMAGE
var/co2_breath_dam_max = MAX_TOXIC_GAS_DAMAGE
var/co2_damage_type = OXY
var/tox_breath_dam_min = MIN_TOXIC_GAS_DAMAGE
var/tox_breath_dam_max = MAX_TOXIC_GAS_DAMAGE
var/tox_damage_type = TOX
var/cold_message = "your face freezing and an icicle forming"
var/cold_level_1_threshold = 260
var/cold_level_2_threshold = 200
var/cold_level_3_threshold = 120
var/cold_level_1_damage = COLD_GAS_DAMAGE_LEVEL_1 //Keep in mind with gas damage levels, you can set these to be negative, if you want someone to heal, instead.
var/cold_level_2_damage = COLD_GAS_DAMAGE_LEVEL_2
var/cold_level_3_damage = COLD_GAS_DAMAGE_LEVEL_3
var/cold_damage_types = list(BURN = 1)
var/hot_message = "your face burning and a searing heat"
var/heat_level_1_threshold = 360
var/heat_level_2_threshold = 400
var/heat_level_3_threshold = 1000
var/heat_level_1_damage = HEAT_GAS_DAMAGE_LEVEL_1
var/heat_level_2_damage = HEAT_GAS_DAMAGE_LEVEL_2
var/heat_level_3_damage = HEAT_GAS_DAMAGE_LEVEL_3
var/heat_damage_types = list(BURN = 1)
/obj/item/organ/internal/lungs/emp_act()
if(!is_robotic() || emp_proof)
return
if(owner)
owner.LoseBreath(40 SECONDS)
/obj/item/organ/internal/lungs/insert(mob/living/carbon/M, special = 0, dont_remove_slot = 0)
..()
for(var/thing in list("oxy", "tox", "co2", "nitro"))
M.clear_alert("not_enough_[thing]")
M.clear_alert("too_much_[thing]")
/obj/item/organ/internal/lungs/remove(mob/living/carbon/M, special = 0)
for(var/thing in list("oxy", "tox", "co2", "nitro"))
M.clear_alert("not_enough_[thing]")
M.clear_alert("too_much_[thing]")
return ..()
/obj/item/organ/internal/lungs/on_life()
if(germ_level > INFECTION_LEVEL_ONE)
if(prob(5))
owner.emote("cough") //respitory tract infection
if(is_bruised())
if(prob(2) && !(NO_BLOOD in owner.dna.species.species_traits))
owner.custom_emote(EMOTE_VISIBLE, "coughs up blood!")
owner.bleed(1)
if(prob(4))
owner.custom_emote(EMOTE_VISIBLE, "gasps for air!")
owner.AdjustLoseBreath(10 SECONDS)
/obj/item/organ/internal/lungs/proc/check_breath(datum/gas_mixture/breath, mob/living/carbon/human/H)
if((H.status_flags & GODMODE))
return
if(HAS_TRAIT(H, TRAIT_NOBREATH))
return
if(!breath || (breath.total_moles() == 0))
if(isspaceturf(H.loc))
H.adjustOxyLoss(10)
else
H.adjustOxyLoss(5)
if(safe_oxygen_min)
H.throw_alert("not_enough_oxy", /obj/screen/alert/not_enough_oxy)
else if(safe_toxins_min)
H.throw_alert("not_enough_tox", /obj/screen/alert/not_enough_tox)
else if(safe_co2_min)
H.throw_alert("not_enough_co2", /obj/screen/alert/not_enough_co2)
else if(safe_nitro_min)
H.throw_alert("not_enough_nitro", /obj/screen/alert/not_enough_nitro)
return FALSE
if(H.health < HEALTH_THRESHOLD_CRIT)
return FALSE
var/gas_breathed = 0
//Partial pressures in our breath
var/O2_pp = breath.get_breath_partial_pressure(breath.oxygen)
var/N2_pp = breath.get_breath_partial_pressure(breath.nitrogen)
var/Toxins_pp = breath.get_breath_partial_pressure(breath.toxins)
var/CO2_pp = breath.get_breath_partial_pressure(breath.carbon_dioxide)
var/SA_pp = breath.get_breath_partial_pressure(breath.sleeping_agent)
//-- OXY --//
//Too much oxygen! //Yes, some species may not like it.
if(safe_oxygen_max)
if(O2_pp > safe_oxygen_max)
var/ratio = (breath.oxygen / safe_oxygen_max / safe_oxygen_max) * 10
H.apply_damage_type(clamp(ratio, oxy_breath_dam_min, oxy_breath_dam_max), oxy_damage_type)
H.throw_alert("too_much_oxy", /obj/screen/alert/too_much_oxy)
else
H.clear_alert("too_much_oxy")
//Too little oxygen!
if(safe_oxygen_min)
if(O2_pp < safe_oxygen_min)
gas_breathed = handle_too_little_breath(H, O2_pp, safe_oxygen_min, breath.oxygen)
H.throw_alert("not_enough_oxy", /obj/screen/alert/not_enough_oxy)
else
H.adjustOxyLoss(-HUMAN_MAX_OXYLOSS)
gas_breathed = breath.oxygen
H.clear_alert("not_enough_oxy")
//Exhale
breath.oxygen -= gas_breathed
breath.carbon_dioxide += gas_breathed
gas_breathed = 0
//-- Nitrogen --//
//Too much nitrogen!
if(safe_nitro_max)
if(N2_pp > safe_nitro_max)
var/ratio = (breath.nitrogen / safe_nitro_max) * 10
H.apply_damage_type(clamp(ratio, nitro_breath_dam_min, nitro_breath_dam_max), nitro_damage_type)
H.throw_alert("too_much_nitro", /obj/screen/alert/too_much_nitro)
else
H.clear_alert("too_much_nitro")
//Too little nitrogen!
if(safe_nitro_min)
if(N2_pp < safe_nitro_min)
gas_breathed = handle_too_little_breath(H, N2_pp, safe_nitro_min, breath.nitrogen)
H.throw_alert("not_enough_nitro", /obj/screen/alert/not_enough_nitro)
else
H.adjustOxyLoss(-HUMAN_MAX_OXYLOSS)
gas_breathed = breath.nitrogen
H.clear_alert("not_enough_nitro")
//Exhale
breath.nitrogen -= gas_breathed
breath.carbon_dioxide += gas_breathed
gas_breathed = 0
//-- CO2 --//
//CO2 does not affect failed_last_breath. So if there was enough oxygen in the air but too much co2, this will hurt you, but only once per 4 ticks, instead of once per tick.
if(safe_co2_max)
if(CO2_pp > safe_co2_max)
if(!H.co2overloadtime) // If it's the first breath with too much CO2 in it, lets start a counter, then have them pass out after 12s or so.
H.co2overloadtime = world.time
else if(world.time - H.co2overloadtime > 120)
H.Paralyse(6 SECONDS)
H.apply_damage_type(HUMAN_MAX_OXYLOSS, co2_damage_type) // Lets hurt em a little, let them know we mean business
if(world.time - H.co2overloadtime > 300) // They've been in here 30s now, lets start to kill them for their own good!
H.apply_damage_type(15, co2_damage_type)
H.throw_alert("too_much_co2", /obj/screen/alert/too_much_co2)
if(prob(20)) // Lets give them some chance to know somethings not right though I guess.
H.emote("cough")
else
H.co2overloadtime = 0
H.clear_alert("too_much_co2")
//Too little CO2!
if(safe_co2_min)
if(CO2_pp < safe_co2_min)
gas_breathed = handle_too_little_breath(H, CO2_pp, safe_co2_min, breath.carbon_dioxide)
H.throw_alert("not_enough_co2", /obj/screen/alert/not_enough_co2)
else
H.adjustOxyLoss(-HUMAN_MAX_OXYLOSS)
gas_breathed = breath.carbon_dioxide
H.clear_alert("not_enough_co2")
//Exhale
breath.carbon_dioxide -= gas_breathed
breath.oxygen += gas_breathed
gas_breathed = 0
//-- TOX --//
//Too much toxins!
if(safe_toxins_max)
if(Toxins_pp > safe_toxins_max)
var/ratio = (breath.toxins / safe_toxins_max) * 10
H.apply_damage_type(clamp(ratio, tox_breath_dam_min, tox_breath_dam_max), tox_damage_type)
H.throw_alert("too_much_tox", /obj/screen/alert/too_much_tox)
else
H.clear_alert("too_much_tox")
//Too little toxins!
if(safe_toxins_min)
if(Toxins_pp < safe_toxins_min)
gas_breathed = handle_too_little_breath(H, Toxins_pp, safe_toxins_min, breath.toxins)
H.throw_alert("not_enough_tox", /obj/screen/alert/not_enough_tox)
else
H.adjustOxyLoss(-HUMAN_MAX_OXYLOSS)
gas_breathed = breath.toxins
H.clear_alert("not_enough_tox")
//Exhale
breath.toxins -= gas_breathed
breath.carbon_dioxide += gas_breathed
gas_breathed = 0
//-- TRACES --//
if(breath.sleeping_agent) // If there's some other shit in the air lets deal with it here.
if(SA_pp > SA_para_min)
H.Paralyse(6 SECONDS) // 6 seconds gives them one second to wake up and run away a bit!
if(SA_pp > SA_sleep_min) // Enough to make us sleep as well
H.AdjustSleeping(16 SECONDS, bound_lower = 0, bound_upper = 20 SECONDS)
else if(SA_pp > 0.01) // There is sleeping gas in their lungs, but only a little, so give them a bit of a warning
if(prob(20))
H.emote(pick("giggle", "laugh"))
handle_breath_temperature(breath, H)
return TRUE
/obj/item/organ/internal/lungs/proc/handle_too_little_breath(mob/living/carbon/human/H = null, breath_pp = 0, safe_breath_min = 0, true_pp = 0)
. = 0
if(!H || !safe_breath_min) //the other args are either: Ok being 0 or Specifically handled.
return FALSE
if(prob(20))
H.emote("gasp")
if(breath_pp > 0)
var/ratio = safe_breath_min/breath_pp
H.adjustOxyLoss(min(5*ratio, HUMAN_MAX_OXYLOSS)) // Don't fuck them up too fast (space only does HUMAN_MAX_OXYLOSS after all!
. = true_pp*ratio/6
else
H.adjustOxyLoss(HUMAN_MAX_OXYLOSS)
/obj/item/organ/internal/lungs/proc/handle_breath_temperature(datum/gas_mixture/breath, mob/living/carbon/human/H) // called by human/life, handles temperatures
var/breath_temperature = breath.temperature
if(!HAS_TRAIT(H, TRAIT_RESISTCOLD)) // COLD DAMAGE
var/CM = abs(H.dna.species.coldmod)
var/TC = 0
if(breath_temperature < cold_level_3_threshold)
TC = cold_level_3_damage
if(breath_temperature > cold_level_3_threshold && breath_temperature < cold_level_2_threshold)
TC = cold_level_2_damage
if(breath_temperature > cold_level_2_threshold && breath_temperature < cold_level_1_threshold)
TC = cold_level_1_damage
if(TC)
for(var/D in cold_damage_types)
H.apply_damage_type(TC * CM * cold_damage_types[D], D)
if(breath_temperature < cold_level_1_threshold)
if(prob(20))
to_chat(H, "<span class='warning'>You feel [cold_message] in your [name]!</span>")
if(!HAS_TRAIT(H, TRAIT_RESISTHEAT)) // HEAT DAMAGE
var/HM = abs(H.dna.species.heatmod)
var/TH = 0
if(breath_temperature > heat_level_1_threshold && breath_temperature < heat_level_2_threshold)
TH = heat_level_1_damage
if(breath_temperature > heat_level_2_threshold && breath_temperature < heat_level_3_threshold)
TH = heat_level_2_damage
if(breath_temperature > heat_level_3_threshold)
TH = heat_level_3_damage
if(TH)
for(var/D in heat_damage_types)
H.apply_damage_type(TH * HM * heat_damage_types[D], D)
if(breath_temperature > heat_level_1_threshold)
if(prob(20))
to_chat(H, "<span class='warning'>You feel [hot_message] in your [name]!</span>")
/obj/item/organ/internal/lungs/prepare_eat()
var/obj/S = ..()
if(S)
S.reagents.add_reagent("salbutamol", 5)
return S
organ_datums = list(/datum/organ/lungs)
/obj/item/organ/internal/lungs/plasmaman
name = "plasma filter"
@@ -314,9 +15,7 @@
icon = 'icons/obj/species_organs/plasmaman.dmi'
icon_state = "lungs"
safe_oxygen_min = 0 //We don't breath this
safe_toxins_min = 16 //We breathe THIS!
safe_toxins_max = 0
organ_datums = list(/datum/organ/lungs/plasmamen)
/obj/item/organ/internal/lungs/vox
name = "vox lungs"
@@ -325,20 +24,12 @@
icon_state = "lungs"
sterile = TRUE
safe_oxygen_min = 0 //We don't breathe this
safe_oxygen_max = 0.05 //This is toxic to us
safe_nitro_min = 16 //We breathe THIS!
oxy_damage_type = TOX //And it poisons us
organ_datums = list(/datum/organ/lungs/vox)
/obj/item/organ/internal/lungs/drask
icon = 'icons/obj/species_organs/drask.dmi'
cold_message = "an invigorating coldness"
cold_level_3_threshold = 60
cold_level_1_damage = -COLD_GAS_DAMAGE_LEVEL_1 //They heal when the air is cold
cold_level_2_damage = -COLD_GAS_DAMAGE_LEVEL_2
cold_level_3_damage = -COLD_GAS_DAMAGE_LEVEL_3
cold_damage_types = list(BRUTE = 0.5, BURN = 0.25)
organ_datums = list(/datum/organ/lungs/drask)
/obj/item/organ/internal/lungs/cybernetic
name = "cybernetic lungs"
@@ -346,7 +37,7 @@
icon_state = "lungs-c"
origin_tech = "biotech=4"
status = ORGAN_ROBOT
var/species_state = "human"
var/species_state = "default"
/obj/item/organ/internal/lungs/cybernetic/examine(mob/user)
. = ..()
@@ -356,28 +47,20 @@
. = TRUE
if(!I.use_tool(src, user, 0, volume = I.tool_volume))
return
switch(species_state)
if("human") // from human to vox
safe_oxygen_min = 0
safe_oxygen_max = safe_toxins_max
safe_nitro_min = 16
oxy_damage_type = TOX
to_chat(user, "<span class='notice'>You configure [src] to replace vox lungs.</span>")
species_state = "vox"
if("vox") // from vox to plasmamen
safe_oxygen_max = initial(safe_oxygen_max)
safe_toxins_min = 16
safe_toxins_max = 0
safe_nitro_min = initial(safe_nitro_min)
oxy_damage_type = OXY
to_chat(user, "<span class='notice'>You configure [src] to replace plasmamen lungs.</span>")
species_state = "plasmamen"
if("plasmamen") // from plasmamen to human
safe_oxygen_min = initial(safe_oxygen_min)
safe_toxins_min = initial(safe_toxins_min)
safe_toxins_max = initial(safe_toxins_max)
to_chat(user, "<span class='notice'>You configure [src] back to default settings.</span>")
species_state = "human"
var/possible = list("default" = /datum/organ/lungs, "vox" = /datum/organ/lungs/vox, "plasmamen" = /datum/organ/lungs/plasmamen)
var/chosen = input(user, "Select lung type", "What kind of lung settings?") as null|anything in possible
if(isnull(chosen) || chosen == species_state || !Adjacent(user) || !I.use_tool(src, user, 0, volume = I.tool_volume))
return
species_state = chosen
to_chat(user, "<span class='notice'>You configure [src] to [chosen] settings.</span>")
var/typepath = possible[chosen]
var/datum/organ/lungs/lungs = new typepath(src)
qdel(organ_datums[lungs.organ_tag])
organ_datums[lungs.organ_tag] = lungs
if(owner) // this should never happen, but in case it somehow does...
owner.internal_organ_datums[lungs.organ_tag] = lungs
/obj/item/organ/internal/lungs/cybernetic/upgraded
name = "upgraded cybernetic lungs"
@@ -385,9 +68,9 @@
icon_state = "lungs-c-u"
origin_tech = "biotech=5"
safe_toxins_max = 20
safe_co2_max = 20
organ_datums = list(/datum/organ/lungs/advanced_cyber)
cold_level_1_threshold = 200
cold_level_2_threshold = 140
cold_level_3_threshold = 100
/obj/item/organ/internal/lungs/cybernetic/upgraded/multitool_act(mob/user, obj/item/I)
. = ..()
var/datum/organ/lungs/lungs = organ_datums["lungs"]
lungs.make_advanced()
@@ -0,0 +1,47 @@
/datum/organ/heart
organ_tag = ORGAN_DATUM_HEART
var/beating = TRUE
var/always_beating = FALSE
/datum/organ/heart/on_remove(mob/living/carbon/removed_from, special = FALSE)
. = ..()
if(ishuman(removed_from))
var/mob/living/carbon/human/H = removed_from
if(H.stat == DEAD)
change_beating(FALSE)
return
if(!special)
addtimer(CALLBACK(src, PROC_REF(stop_if_unowned)), 12 SECONDS)
/datum/organ/heart/proc/change_beating(change_to = FALSE)
if(always_beating)
beating = TRUE
return
beating = change_to
linked_organ.update_icon()
/datum/organ/heart/on_successful_emp()
change_beating(FALSE)
return TRUE
/datum/organ/heart/on_necrotize()
change_beating(FALSE)
return TRUE
/datum/organ/heart/proc/try_restart(duration = 8 SECONDS)
if(beating)
return
change_beating(TRUE)
addtimer(CALLBACK(src, PROC_REF(stop_if_unowned)), duration)
/datum/organ/heart/proc/stop_if_unowned()
if(!linked_organ.owner)
change_beating(FALSE)
/datum/organ/heart/on_prepare_eat(obj/item/food/snacks/organ/snorgan)
snorgan.icon_state = linked_organ.dead_icon
/// A subtype that is always beating. Abductor glands and demon hearts use this.
/datum/organ/heart/always_beating
always_beating = TRUE
@@ -0,0 +1,345 @@
/datum/organ/lungs
organ_tag = ORGAN_DATUM_LUNGS
//Breath damage
var/safe_oxygen_min = 16 // Minimum safe partial pressure of O2, in kPa
var/safe_oxygen_max = 0
var/safe_nitro_min = 0
var/safe_nitro_max = 0
var/safe_co2_min = 0
var/safe_co2_max = 10 // Yes it's an arbitrary value who cares?
var/safe_toxins_min = 0
var/safe_toxins_max = 0.05
var/SA_para_min = 1 //Sleeping agent
var/SA_sleep_min = 5 //Sleeping agent
var/oxy_breath_dam_min = MIN_TOXIC_GAS_DAMAGE
var/oxy_breath_dam_max = MAX_TOXIC_GAS_DAMAGE
var/oxy_damage_type = OXY
var/nitro_breath_dam_min = MIN_TOXIC_GAS_DAMAGE
var/nitro_breath_dam_max = MAX_TOXIC_GAS_DAMAGE
var/nitro_damage_type = OXY
var/co2_breath_dam_min = MIN_TOXIC_GAS_DAMAGE
var/co2_breath_dam_max = MAX_TOXIC_GAS_DAMAGE
var/co2_damage_type = OXY
var/tox_breath_dam_min = MIN_TOXIC_GAS_DAMAGE
var/tox_breath_dam_max = MAX_TOXIC_GAS_DAMAGE
var/tox_damage_type = TOX
var/cold_message = "your face freezing and an icicle forming"
var/cold_level_1_threshold = 260
var/cold_level_2_threshold = 200
var/cold_level_3_threshold = 120
var/cold_level_1_damage = COLD_GAS_DAMAGE_LEVEL_1 //Keep in mind with gas damage levels, you can set these to be negative, if you want someone to heal, instead.
var/cold_level_2_damage = COLD_GAS_DAMAGE_LEVEL_2
var/cold_level_3_damage = COLD_GAS_DAMAGE_LEVEL_3
var/cold_damage_types = list(BURN = 1)
var/hot_message = "your face burning and a searing heat"
var/heat_level_1_threshold = 360
var/heat_level_2_threshold = 400
var/heat_level_3_threshold = 1000
var/heat_level_1_damage = HEAT_GAS_DAMAGE_LEVEL_1
var/heat_level_2_damage = HEAT_GAS_DAMAGE_LEVEL_2
var/heat_level_3_damage = HEAT_GAS_DAMAGE_LEVEL_3
var/heat_damage_types = list(BURN = 1)
/**
* LUNG ATMOS CODE, VENTURE FURTHER IF YOU DARE!!!
*/
/datum/organ/lungs/proc/check_breath(datum/gas_mixture/breath, mob/living/carbon/human/H)
if((H.status_flags & GODMODE))
return
if(HAS_TRAIT(H, TRAIT_NOBREATH))
return
if(!breath || (breath.total_moles() == 0))
if(isspaceturf(H.loc))
H.adjustOxyLoss(10)
else
H.adjustOxyLoss(5)
if(safe_oxygen_min)
H.throw_alert("not_enough_oxy", /obj/screen/alert/not_enough_oxy)
else if(safe_toxins_min)
H.throw_alert("not_enough_tox", /obj/screen/alert/not_enough_tox)
else if(safe_co2_min)
H.throw_alert("not_enough_co2", /obj/screen/alert/not_enough_co2)
else if(safe_nitro_min)
H.throw_alert("not_enough_nitro", /obj/screen/alert/not_enough_nitro)
return FALSE
if(H.health < HEALTH_THRESHOLD_CRIT)
return FALSE
var/gas_breathed = 0
//Partial pressures in our breath
var/O2_pp = breath.get_breath_partial_pressure(breath.oxygen)
var/N2_pp = breath.get_breath_partial_pressure(breath.nitrogen)
var/Toxins_pp = breath.get_breath_partial_pressure(breath.toxins)
var/CO2_pp = breath.get_breath_partial_pressure(breath.carbon_dioxide)
var/SA_pp = breath.get_breath_partial_pressure(breath.sleeping_agent)
//-- OXY --//
//Too much oxygen! //Yes, some species may not like it.
if(safe_oxygen_max)
if(O2_pp > safe_oxygen_max)
var/ratio = (breath.oxygen / safe_oxygen_max / safe_oxygen_max) * 10
H.apply_damage_type(clamp(ratio, oxy_breath_dam_min, oxy_breath_dam_max), oxy_damage_type)
H.throw_alert("too_much_oxy", /obj/screen/alert/too_much_oxy)
else
H.clear_alert("too_much_oxy")
//Too little oxygen!
if(safe_oxygen_min)
if(O2_pp < safe_oxygen_min)
gas_breathed = handle_too_little_breath(H, O2_pp, safe_oxygen_min, breath.oxygen)
H.throw_alert("not_enough_oxy", /obj/screen/alert/not_enough_oxy)
else
H.adjustOxyLoss(-HUMAN_MAX_OXYLOSS)
gas_breathed = breath.oxygen
H.clear_alert("not_enough_oxy")
//Exhale
breath.oxygen -= gas_breathed
breath.carbon_dioxide += gas_breathed
gas_breathed = 0
//-- Nitrogen --//
//Too much nitrogen!
if(safe_nitro_max)
if(N2_pp > safe_nitro_max)
var/ratio = (breath.nitrogen / safe_nitro_max) * 10
H.apply_damage_type(clamp(ratio, nitro_breath_dam_min, nitro_breath_dam_max), nitro_damage_type)
H.throw_alert("too_much_nitro", /obj/screen/alert/too_much_nitro)
else
H.clear_alert("too_much_nitro")
//Too little nitrogen!
if(safe_nitro_min)
if(N2_pp < safe_nitro_min)
gas_breathed = handle_too_little_breath(H, N2_pp, safe_nitro_min, breath.nitrogen)
H.throw_alert("not_enough_nitro", /obj/screen/alert/not_enough_nitro)
else
H.adjustOxyLoss(-HUMAN_MAX_OXYLOSS)
gas_breathed = breath.nitrogen
H.clear_alert("not_enough_nitro")
//Exhale
breath.nitrogen -= gas_breathed
breath.carbon_dioxide += gas_breathed
gas_breathed = 0
//-- CO2 --//
//CO2 does not affect failed_last_breath. So if there was enough oxygen in the air but too much co2, this will hurt you, but only once per 4 ticks, instead of once per tick.
if(safe_co2_max)
if(CO2_pp > safe_co2_max)
if(!H.co2overloadtime) // If it's the first breath with too much CO2 in it, lets start a counter, then have them pass out after 12s or so.
H.co2overloadtime = world.time
else if(world.time - H.co2overloadtime > 120)
H.Paralyse(6 SECONDS)
H.apply_damage_type(HUMAN_MAX_OXYLOSS, co2_damage_type) // Lets hurt em a little, let them know we mean business
if(world.time - H.co2overloadtime > 300) // They've been in here 30s now, lets start to kill them for their own good!
H.apply_damage_type(15, co2_damage_type)
H.throw_alert("too_much_co2", /obj/screen/alert/too_much_co2)
if(prob(20)) // Lets give them some chance to know somethings not right though I guess.
H.emote("cough")
else
H.co2overloadtime = 0
H.clear_alert("too_much_co2")
//Too little CO2!
if(safe_co2_min)
if(CO2_pp < safe_co2_min)
gas_breathed = handle_too_little_breath(H, CO2_pp, safe_co2_min, breath.carbon_dioxide)
H.throw_alert("not_enough_co2", /obj/screen/alert/not_enough_co2)
else
H.adjustOxyLoss(-HUMAN_MAX_OXYLOSS)
gas_breathed = breath.carbon_dioxide
H.clear_alert("not_enough_co2")
//Exhale
breath.carbon_dioxide -= gas_breathed
breath.oxygen += gas_breathed
gas_breathed = 0
//-- TOX --//
//Too much toxins!
if(safe_toxins_max)
if(Toxins_pp > safe_toxins_max)
var/ratio = (breath.toxins / safe_toxins_max) * 10
H.apply_damage_type(clamp(ratio, tox_breath_dam_min, tox_breath_dam_max), tox_damage_type)
H.throw_alert("too_much_tox", /obj/screen/alert/too_much_tox)
else
H.clear_alert("too_much_tox")
//Too little toxins!
if(safe_toxins_min)
if(Toxins_pp < safe_toxins_min)
gas_breathed = handle_too_little_breath(H, Toxins_pp, safe_toxins_min, breath.toxins)
H.throw_alert("not_enough_tox", /obj/screen/alert/not_enough_tox)
else
H.adjustOxyLoss(-HUMAN_MAX_OXYLOSS)
gas_breathed = breath.toxins
H.clear_alert("not_enough_tox")
//Exhale
breath.toxins -= gas_breathed
breath.carbon_dioxide += gas_breathed
gas_breathed = 0
//-- TRACES --//
if(breath.sleeping_agent) // If there's some other shit in the air lets deal with it here.
if(SA_pp > SA_para_min)
H.Paralyse(6 SECONDS) // 6 seconds gives them one second to wake up and run away a bit!
if(SA_pp > SA_sleep_min) // Enough to make us sleep as well
H.AdjustSleeping(16 SECONDS, bound_lower = 0, bound_upper = 20 SECONDS)
else if(SA_pp > 0.01) // There is sleeping gas in their lungs, but only a little, so give them a bit of a warning
if(prob(20))
H.emote(pick("giggle", "laugh"))
handle_breath_temperature(breath, H)
return TRUE
/datum/organ/lungs/proc/handle_too_little_breath(mob/living/carbon/human/H = null, breath_pp = 0, safe_breath_min = 0, true_pp = 0)
. = 0
if(!H || !safe_breath_min) //the other args are either: Ok being 0 or Specifically handled.
return FALSE
if(prob(20))
H.emote("gasp")
if(breath_pp > 0)
var/ratio = safe_breath_min/breath_pp
H.adjustOxyLoss(min(5*ratio, HUMAN_MAX_OXYLOSS)) // Don't fuck them up too fast (space only does HUMAN_MAX_OXYLOSS after all!
. = true_pp*ratio/6
else
H.adjustOxyLoss(HUMAN_MAX_OXYLOSS)
/datum/organ/lungs/proc/handle_breath_temperature(datum/gas_mixture/breath, mob/living/carbon/human/H) // called by human/life, handles temperatures
var/breath_temperature = breath.temperature
if(!HAS_TRAIT(H, TRAIT_RESISTCOLD)) // COLD DAMAGE
var/CM = abs(H.dna.species.coldmod)
var/TC = 0
if(breath_temperature < cold_level_3_threshold)
TC = cold_level_3_damage
if(breath_temperature > cold_level_3_threshold && breath_temperature < cold_level_2_threshold)
TC = cold_level_2_damage
if(breath_temperature > cold_level_2_threshold && breath_temperature < cold_level_1_threshold)
TC = cold_level_1_damage
if(TC)
for(var/D in cold_damage_types)
H.apply_damage_type(TC * CM * cold_damage_types[D], D)
if(breath_temperature < cold_level_1_threshold)
if(prob(20))
to_chat(H, "<span class='warning'>You feel [cold_message] in your [linked_organ.name]!</span>")
if(!HAS_TRAIT(H, TRAIT_RESISTHEAT)) // HEAT DAMAGE
var/HM = abs(H.dna.species.heatmod)
var/TH = 0
if(breath_temperature > heat_level_1_threshold && breath_temperature < heat_level_2_threshold)
TH = heat_level_1_damage
if(breath_temperature > heat_level_2_threshold && breath_temperature < heat_level_3_threshold)
TH = heat_level_2_damage
if(breath_temperature > heat_level_3_threshold)
TH = heat_level_3_damage
if(TH)
for(var/D in heat_damage_types)
H.apply_damage_type(TH * HM * heat_damage_types[D], D)
if(breath_temperature > heat_level_1_threshold)
if(prob(20))
to_chat(H, "<span class='warning'>You feel [hot_message] in your [linked_organ.name]!</span>")
/**
* Lung atmos code ends here. Thank god...
*/
/datum/organ/lungs/on_successful_emp()
linked_organ.owner?.LoseBreath(40 SECONDS)
/datum/organ/lungs/on_insert(mob/living/carbon/given_to)
clear_alerts(given_to)
/datum/organ/lungs/on_replace(mob/living/carbon/human/organ_owner)
clear_alerts(organ_owner)
/datum/organ/lungs/on_remove(mob/living/carbon/removed_from, special = FALSE)
clear_alerts(removed_from)
/datum/organ/lungs/proc/clear_alerts(mob/living/carbon/alert_owner)
for(var/thing in list("oxy", "tox", "co2", "nitro"))
alert_owner.clear_alert("not_enough_[thing]")
alert_owner.clear_alert("too_much_[thing]")
/datum/organ/lungs/on_life()
if(linked_organ.germ_level > INFECTION_LEVEL_ONE)
if(prob(5))
linked_organ.owner.emote("cough") //respitory tract infection
if(linked_organ.is_bruised())
if(prob(2) && !(NO_BLOOD in linked_organ.owner.dna.species.species_traits))
linked_organ.owner.custom_emote(EMOTE_VISIBLE, "coughs up blood!")
linked_organ.owner.bleed(1)
if(prob(4))
linked_organ.owner.custom_emote(EMOTE_VISIBLE, "gasps for air!")
linked_organ.owner.AdjustLoseBreath(10 SECONDS)
/datum/organ/lungs/on_prepare_eat(obj/item/food/snacks/organ/snorgan)
snorgan.reagents.add_reagent("salbutamol", 5)
/datum/organ/lungs/vox
safe_oxygen_min = 0 //We don't breathe this
safe_oxygen_max = 0.05 //This is toxic to us
safe_nitro_min = 16 //We breathe THIS!
oxy_damage_type = TOX //And it poisons us
/datum/organ/lungs/plasmamen
safe_oxygen_min = 0 //We don't breath this
safe_toxins_min = 16 //We breathe THIS!
safe_toxins_max = 0
/datum/organ/lungs/drask
cold_message = "an invigorating coldness"
cold_level_3_threshold = 60
cold_level_1_damage = -COLD_GAS_DAMAGE_LEVEL_1 //They heal when the air is cold
cold_level_2_damage = -COLD_GAS_DAMAGE_LEVEL_2
cold_level_3_damage = -COLD_GAS_DAMAGE_LEVEL_3
cold_damage_types = list(BRUTE = 0.5, BURN = 0.25)
/datum/organ/lungs/ashwalker
safe_oxygen_min = 4 // 4x as efficient as regular Unathi, can comfortably breathe on lavaland
/datum/organ/lungs/advanced_cyber/New(obj/item/organ/internal/link_em)
. = ..()
make_advanced()
/datum/organ/lungs/proc/make_advanced()
safe_toxins_max = 20
safe_co2_max = 20
cold_level_1_threshold = 200
cold_level_2_threshold = 140
cold_level_3_threshold = 100
@@ -0,0 +1,36 @@
/datum/organ
var/organ_tag // heart, brain, lungs, etc
var/obj/item/organ/internal/linked_organ
/datum/organ/New(obj/item/organ/internal/link_em)
..()
linked_organ = link_em
/datum/organ/Destroy(force, ...)
linked_organ = null
return ..()
/// Called when the linked organ is inserted.
/datum/organ/proc/on_insert(mob/living/carbon/human/given_to)
return
/// Called when another organ is removed, and this organ datum takes its place in the organ_owner.
/datum/organ/proc/on_replace(mob/living/carbon/human/organ_owner)
return
/// Called when the linked organ is removed.
/datum/organ/proc/on_remove(mob/living/carbon/removed_from, special = FALSE)
return
/datum/organ/proc/on_life()
return
/// Only called when the organ is robotic AND is not emp proof. Return true to override default functions
/datum/organ/proc/on_successful_emp()
return FALSE
/datum/organ/proc/on_necrotize()
return FALSE
/datum/organ/proc/on_prepare_eat(obj/item/food/snacks/organ/snorgan)
return FALSE
@@ -16,6 +16,10 @@
/mob/living/carbon/get_int_organ(typepath)
return (locate(typepath) in internal_organs)
/mob/living/carbon/proc/get_int_organ_datum(tag_to_check)
RETURN_TYPE(/datum/organ)
return internal_organ_datums[tag_to_check]
/mob/living/carbon/get_organs_zone(zone, subzones = 0)
var/list/returnorg = list()
+67 -1
View File
@@ -7,12 +7,34 @@
// DO NOT add slots with matching names to different zones - it will break internal_organs_slot list!
var/non_primary = 0
var/unremovable = FALSE //Whether it shows up as an option to remove during surgery.
/// An associated list of organ datums that this organ has.
var/list/datum/organ/organ_datums
/obj/item/organ/internal/New(mob/living/carbon/holder)
..()
if(istype(holder))
insert(holder)
/obj/item/organ/internal/Initialize(mapload)
. = ..()
if(!organ_datums)
return
var/list/temp_list = organ_datums.Copy()
organ_datums = list()
for(var/path in temp_list)
var/datum/organ/organ_datum = new path(src)
if(!organ_datum.organ_tag)
stack_trace("There was an organ datum [organ_datum] ([organ_datum.type]), that had no organ tag.")
continue
organ_datums[organ_datum.organ_tag] = organ_datum
/obj/item/organ/internal/Destroy()
if(owner) // we have to remove BEFORE organ_datums are qdel'd, or we can just live even if our heart organ got deleted
remove(owner, TRUE)
QDEL_LIST_ASSOC_VAL(organ_datums) // The removal from internal_organ_datums should be handled when the organ is removed
. = ..()
/obj/item/organ/internal/proc/insert(mob/living/carbon/M, special = 0, dont_remove_slot = 0)
if(!iscarbon(M) || owner == M)
return
@@ -28,6 +50,12 @@
M.internal_organs |= src
M.internal_organs_slot[slot] = src
for(var/organ_tag in organ_datums)
var/datum/organ/new_organ = organ_datums[organ_tag]
M.internal_organ_datums[new_organ.organ_tag] = new_organ
new_organ.on_insert(M)
var/obj/item/organ/external/parent
if(ishuman(M))
var/mob/living/carbon/human/H = M
@@ -56,6 +84,23 @@
M.internal_organs -= src
if(M.internal_organs_slot[slot] == src)
M.internal_organs_slot.Remove(slot)
for(var/removal_tag in organ_datums)
if(M.internal_organ_datums[removal_tag] == organ_datums[removal_tag])
M.internal_organ_datums -= removal_tag
var/datum/organ/removed = organ_datums[removal_tag]
removed.on_remove(M)
// Lets see if we have any backup organ datums from other internal organs.
for(var/obj/item/organ/internal/backup_organ in M.internal_organs)
for(var/replacement_tag in backup_organ.organ_datums)
if(M.internal_organ_datums[replacement_tag]) // some other organ is already covering it
continue
var/datum/organ/replacement_organ = backup_organ.organ_datums[replacement_tag]
M.internal_organ_datums[replacement_organ.organ_tag] = replacement_organ
replacement_organ.on_replace(M)
if(vital && !special)
if(M.stat != DEAD)//safety check!
M.death()
@@ -90,6 +135,12 @@
/obj/item/organ/internal/replaced(mob/living/carbon/human/target)
insert(target)
/obj/item/organ/internal/necrotize(update_sprite)
for(var/organ_tag in organ_datums) // let the organ datums handle first
var/datum/organ/dead_organ = organ_datums[organ_tag]
dead_organ.on_necrotize()
return ..()
/obj/item/organ/internal/item_action_slot_check(slot, mob/user)
return
@@ -114,6 +165,10 @@
S.origin_tech = origin_tech
S.w_class = w_class
for(var/organ_tag in organ_datums)
var/datum/organ/delicious = organ_datums[organ_tag]
delicious.on_prepare_eat(S)
return S
/obj/item/organ/internal/attempt_become_organ(obj/item/organ/external/parent,mob/living/carbon/human/H)
@@ -160,7 +215,7 @@
if(slot == "heart" && ("[slot]-c-on" in states) && ("[slot]-c-off" in states)) //Give the robotic heart its robotic heart icons if they exist.
var/obj/item/organ/internal/heart/H = src
H.icon = icon('icons/obj/surgery.dmi')
H.icon_base = "[slot]-c"
H.base_icon_state = "[slot]-c"
H.dead_icon = "[slot]-c-off"
H.update_icon()
else if("[slot]-c" in states) //Give the robotic organ its robotic organ icons if they exist.
@@ -318,6 +373,17 @@
/obj/item/organ/internal/emp_act(severity)
if(!is_robotic() || emp_proof)
return
var/we_done = FALSE
for(var/organ_tag in organ_datums)
var/datum/organ/borgan = organ_datums[organ_tag]
if(borgan.on_successful_emp())
we_done = TRUE
if(we_done)
return
// No EMP handling was done, lets just give em damage
switch(severity)
if(1)
receive_damage(20, 1)
@@ -1,15 +0,0 @@
/obj/item/organ/internal/heart/slime
icon = 'icons/obj/species_organs/slime.dmi'
name = "slime heart"
icon_state = "heart"
desc = "This is a slime's osmotic pressure regulator, it appears to be some kind of biological pump that uses osmotic pressure to regulate water flow. It seems to work similar to a heart."
dead_icon = null
/obj/item/organ/internal/heart/slime/update_icon_state()
return
/obj/item/organ/internal/lungs/slime
icon = 'icons/obj/species_organs/slime.dmi'
name = "slime lungs"
icon_state = "lungs"
desc = "This is a slime's gas exchange membrane, this membrane used for oxygen intake and gas exchange. These seem to work similar to lungs."
@@ -30,4 +30,4 @@
icon = 'icons/obj/species_organs/unathi.dmi'
/obj/item/organ/internal/lungs/unathi/ash_walker
safe_oxygen_min = 4 // 4x as efficient as regular Unathi, can comfortably breathe on lavaland
organ_datums = list(/datum/organ/lungs/ashwalker)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

+3 -1
View File
@@ -2741,6 +2741,9 @@
#include "code\modules\surgery\organs\robolimbs.dm"
#include "code\modules\surgery\organs\skeleton_organs.dm"
#include "code\modules\surgery\organs\vocal_cords.dm"
#include "code\modules\surgery\organs\organ_datums\heart_datum.dm"
#include "code\modules\surgery\organs\organ_datums\lung_datum.dm"
#include "code\modules\surgery\organs\organ_datums\organ_datum.dm"
#include "code\modules\surgery\organs\subtypes\abductor_organs.dm"
#include "code\modules\surgery\organs\subtypes\diona_organs.dm"
#include "code\modules\surgery\organs\subtypes\drask_organs.dm"
@@ -2750,7 +2753,6 @@
#include "code\modules\surgery\organs\subtypes\moth_organs.dm"
#include "code\modules\surgery\organs\subtypes\plasmaman_organs.dm"
#include "code\modules\surgery\organs\subtypes\skrell_organs.dm"
#include "code\modules\surgery\organs\subtypes\slime_organs.dm"
#include "code\modules\surgery\organs\subtypes\standard_organs.dm"
#include "code\modules\surgery\organs\subtypes\tajaran_organs.dm"
#include "code\modules\surgery\organs\subtypes\unathi_organs.dm"