From 84321916a5c7741691a0a27d32a31050bf7f8c5e Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Wed, 26 Apr 2017 09:52:35 -0500 Subject: [PATCH] Refactors ears into organs (#513) * Refactors ear damage into ear organs * Update human.dm * Update species.dm * Delete species.dm.rej * Update tgstation.dme * Delete tgstation.dme.rej * Delete human.dm.rej * Update tongue.dm * Delete surgery.dmi * upload fixed surgery.dmi --- code/__DEFINES/flags.dm | 4 + code/__DEFINES/mobs.dm | 4 +- code/__HELPERS/priority_announce.dm | 4 +- .../diseases/advance/symptoms/deafness.dm | 11 +- .../diseases/advance/symptoms/sensory.dm | 5 +- .../gamemodes/changeling/powers/shriek.dm | 11 +- code/game/gamemodes/miniantags/borer/borer.dm | 2 +- code/game/gamemodes/wizard/artefact.dm | 2 +- .../game/objects/items/weapons/clown_items.dm | 17 +- code/game/sound.dm | 2 +- code/modules/clothing/clothing.dm | 1 + code/modules/mob/living/brain/status_procs.dm | 10 +- code/modules/mob/living/carbon/alien/alien.dm | 3 + code/modules/mob/living/carbon/carbon.dm | 3 + .../mob/living/carbon/carbon_defense.dm | 24 +- code/modules/mob/living/carbon/human/human.dm | 1 + code/modules/mob/living/carbon/human/life.dm | 12 +- .../mob/living/carbon/human/species.dm | 9 + .../mob/living/carbon/monkey/monkey.dm | 10 +- code/modules/mob/living/life.dm | 8 - code/modules/mob/living/living.dm | 2 - code/modules/mob/living/silicon/ai/say.dm | 2 +- .../mob/living/silicon/status_procs.dm | 8 - .../mob/living/simple_animal/status_procs.dm | 9 - code/modules/mob/living/status_procs.dm | 18 +- code/modules/mob/mob.dm | 8 +- code/modules/mob/mob_defines.dm | 2 - code/modules/mob/mob_helpers.dm | 3 + code/modules/mob/status_procs.dm | 8 - .../chemistry/reagents/medicine_reagents.dm | 2 +- code/modules/surgery/organs/appendix.dm | 32 + code/modules/surgery/organs/ears.dm | 69 ++ code/modules/surgery/organs/eyes.dm | 130 +++ code/modules/surgery/organs/heart.dm | 126 +++ code/modules/surgery/organs/lungs.dm | 267 ++++++ code/modules/surgery/organs/organ_internal.dm | 796 +----------------- code/modules/surgery/organs/tongue.dm | 200 +++++ code/modules/surgery/organs/vocal_cords.dm | 2 +- icons/obj/surgery.dmi | Bin 28021 -> 29118 bytes tgstation.dme | 6 + 40 files changed, 925 insertions(+), 908 deletions(-) create mode 100644 code/modules/surgery/organs/appendix.dm create mode 100644 code/modules/surgery/organs/ears.dm create mode 100644 code/modules/surgery/organs/eyes.dm create mode 100644 code/modules/surgery/organs/heart.dm create mode 100644 code/modules/surgery/organs/lungs.dm create mode 100644 code/modules/surgery/organs/tongue.dm diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm index dc2e575f5d..6745645115 100644 --- a/code/__DEFINES/flags.dm +++ b/code/__DEFINES/flags.dm @@ -36,6 +36,10 @@ #define INFORM_ADMINS_ON_RELOCATE "inform_admins_on_relocate" #define BANG_PROTECT "bang_protect" +// An item worn in the ear slot with HEALS_EARS will heal your ears each +// Life() tick, even if normally your ears would be too damaged to heal. +#define HEALS_EARS "heals_ears" + // A mob with OMNITONGUE has no restriction in the ability to speak // languages that they know. So even if they wouldn't normally be able to // through mob or tongue restrictions, this flag allows them to ignore diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 21740c1b2b..a3570d840d 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -119,4 +119,6 @@ #define GALOSHES_DONT_HELP 4 #define SLIDE_ICE 8 -#define MAX_CHICKENS 50 \ No newline at end of file +#define MAX_CHICKENS 50 + +#define UNHEALING_EAR_DAMAGE 100 diff --git a/code/__HELPERS/priority_announce.dm b/code/__HELPERS/priority_announce.dm index 46d71dc0e2..c62a59ed77 100644 --- a/code/__HELPERS/priority_announce.dm +++ b/code/__HELPERS/priority_announce.dm @@ -25,7 +25,7 @@ announcement += "
" for(var/mob/M in GLOB.player_list) - if(!isnewplayer(M) && !M.ear_deaf) + if(!isnewplayer(M) && M.can_hear()) to_chat(M, announcement) if(M.client.prefs.toggles & SOUND_ANNOUNCEMENTS) M << sound(sound) @@ -51,7 +51,7 @@ return for(var/mob/M in GLOB.player_list) - if(!isnewplayer(M) && !M.ear_deaf) + if(!isnewplayer(M) && M.can_hear()) to_chat(M, "[title]
[message]

") if(M.client.prefs.toggles & SOUND_ANNOUNCEMENTS) if(alert) diff --git a/code/datums/diseases/advance/symptoms/deafness.dm b/code/datums/diseases/advance/symptoms/deafness.dm index f970ebe04c..cd9cb8350b 100644 --- a/code/datums/diseases/advance/symptoms/deafness.dm +++ b/code/datums/diseases/advance/symptoms/deafness.dm @@ -33,12 +33,5 @@ Bonus if(3, 4) to_chat(M, "[pick("You hear a ringing in your ear.", "Your ears pop.")]") if(5) - if(!(M.ear_deaf)) - to_chat(M, "Your ears pop and begin ringing loudly!") - M.setEarDamage(-1,INFINITY) //Shall be enough - addtimer(CALLBACK(src, .proc/Undeafen, M), 200) - -/datum/symptom/deafness/proc/Undeafen(mob/living/M) - if(M) - to_chat(M, "The ringing in your ears fades...") - M.setEarDamage(-1,0) \ No newline at end of file + to_chat(M, "Your ears pop and begin ringing loudly!") + M.minimumDeafTicks(20) diff --git a/code/datums/diseases/advance/symptoms/sensory.dm b/code/datums/diseases/advance/symptoms/sensory.dm index 536184c766..321d6691b3 100644 --- a/code/datums/diseases/advance/symptoms/sensory.dm +++ b/code/datums/diseases/advance/symptoms/sensory.dm @@ -28,7 +28,7 @@ Bonus ..() var/mob/living/M = A.affected_mob if(A.stage >= 2) - M.setEarDamage(0,0) + M.restoreEars() if(A.stage >= 3) M.dizziness = 0 @@ -50,7 +50,6 @@ Bonus if(A.stage >= 5) M.adjustBrainLoss(-3) - return /* ////////////////////////////////////// @@ -127,5 +126,3 @@ Bonus M.drowsyness += 1 if(24 to INFINITY) M.Sleeping(2, 0) - - return diff --git a/code/game/gamemodes/changeling/powers/shriek.dm b/code/game/gamemodes/changeling/powers/shriek.dm index 29be040929..0b106b0b12 100644 --- a/code/game/gamemodes/changeling/powers/shriek.dm +++ b/code/game/gamemodes/changeling/powers/shriek.dm @@ -10,12 +10,13 @@ /obj/effect/proc_holder/changeling/resonant_shriek/sting_action(mob/user) for(var/mob/living/M in get_hearers_in_view(4, user)) if(iscarbon(M)) - if(!M.mind || !M.mind.changeling) - M.adjustEarDamage(0,30) - M.confused += 25 - M.Jitter(50) + var/mob/living/carbon/C = M + if(!C.mind || !C.mind.changeling) + C.adjustEarDamage(0, 30) + C.confused += 25 + C.Jitter(50) else - M << sound('sound/effects/screech.ogg') + C << sound('sound/effects/screech.ogg') if(issilicon(M)) M << sound('sound/weapons/flash.ogg') diff --git a/code/game/gamemodes/miniantags/borer/borer.dm b/code/game/gamemodes/miniantags/borer/borer.dm index 122d2c34ba..b10fb16768 100644 --- a/code/game/gamemodes/miniantags/borer/borer.dm +++ b/code/game/gamemodes/miniantags/borer/borer.dm @@ -770,7 +770,7 @@ GLOBAL_VAR_INIT(total_borer_hosts_needed, 10) if("Blindness") victim.blind_eyes(2) if("Deafness") - victim.ear_deaf = 20 + victim.minimumDeafTicks(20) if("Stun") victim.Weaken(10) diff --git a/code/game/gamemodes/wizard/artefact.dm b/code/game/gamemodes/wizard/artefact.dm index 13084fab64..c925a5cbd5 100644 --- a/code/game/gamemodes/wizard/artefact.dm +++ b/code/game/gamemodes/wizard/artefact.dm @@ -471,7 +471,7 @@ else if(istype(I,/obj/item/weapon/bikehorn)) to_chat(target, "HONK") target << 'sound/items/AirHorn.ogg' - target.adjustEarDamage(0,3) + target.adjustEarDamage(0,3) GiveHint(target) cooldown = world.time +cooldown_time return diff --git a/code/game/objects/items/weapons/clown_items.dm b/code/game/objects/items/weapons/clown_items.dm index 1c2bf7511c..8c8b476113 100644 --- a/code/game/objects/items/weapons/clown_items.dm +++ b/code/game/objects/items/weapons/clown_items.dm @@ -105,7 +105,7 @@ throw_speed = 3 throw_range = 7 attack_verb = list("HONKED") - var/spam_flag = 0 + var/next_usable = 0 var/honksound = 'sound/items/bikehorn.ogg' var/cooldowntime = 20 @@ -115,18 +115,15 @@ return (BRUTELOSS) /obj/item/weapon/bikehorn/attack(mob/living/carbon/M, mob/living/carbon/user) - if(!spam_flag) + if(!(next_usable > world.time)) playsound(loc, honksound, 50, 1, -1) //plays instead of tap.ogg! return ..() /obj/item/weapon/bikehorn/attack_self(mob/user) - if(!spam_flag) - spam_flag = 1 + if(!(next_usable > world.time)) + next_usable = world.time + cooldowntime playsound(src.loc, honksound, 50, 1) src.add_fingerprint(user) - spawn(cooldowntime) - spam_flag = 0 - return /obj/item/weapon/bikehorn/Crossed(mob/living/L) if(isliving(L)) @@ -156,12 +153,12 @@ ..() /obj/item/weapon/bikehorn/golden/proc/flip_mobs(mob/living/carbon/M, mob/user) - if (!spam_flag) + if(!(next_usable > world.time)) var/turf/T = get_turf(src) for(M in ohearers(7, T)) - if(ishuman(M)) + if(ishuman(M) && M.can_hear()) var/mob/living/carbon/human/H = M - if((istype(H.ears, /obj/item/clothing/ears/earmuffs)) || H.ear_deaf) + if(istype(H.ears, /obj/item/clothing/ears/earmuffs)) continue M.emote("flip") diff --git a/code/game/sound.dm b/code/game/sound.dm index 0c9e8d189a..448f3cd85b 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -81,7 +81,7 @@ src << S /mob/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff, surround = 1, channel = 0, pressure_affected = TRUE) - if(!client || ear_deaf > 0) + if(!client || !can_hear()) return ..() diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index 3b5b93ce6f..26b052d2ce 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -201,6 +201,7 @@ /obj/item/clothing/ears/earmuffs/Initialize(mapload) ..() SET_SECONDARY_FLAG(src, BANG_PROTECT) + SET_SECONDARY_FLAG(src, HEALS_EARS) //Glasses /obj/item/clothing/glasses diff --git a/code/modules/mob/living/brain/status_procs.dm b/code/modules/mob/living/brain/status_procs.dm index caf2bfe22c..4c40973c20 100644 --- a/code/modules/mob/living/brain/status_procs.dm +++ b/code/modules/mob/living/brain/status_procs.dm @@ -1,15 +1,7 @@ //Here are the procs used to modify status effects of a mob. -//The effects include: stunned, weakened, paralysis, sleeping, resting, jitteriness, dizziness, ear damage, +//The effects include: stunned, weakened, paralysis, sleeping, resting, jitteriness, dizziness // eye damage, eye_blind, eye_blurry, druggy, BLIND disability, and NEARSIGHT disability. -/////////////////////////////////// EAR DAMAGE //////////////////////////////////// - -/mob/living/brain/adjustEarDamage() - return - -/mob/living/brain/setEarDamage() // no ears to damage or heal - return - /////////////////////////////////// EYE_BLIND //////////////////////////////////// /mob/living/brain/blind_eyes() // no eyes to damage or heal diff --git a/code/modules/mob/living/carbon/alien/alien.dm b/code/modules/mob/living/carbon/alien/alien.dm index 78c3e4e1df..5e1a91113f 100644 --- a/code/modules/mob/living/carbon/alien/alien.dm +++ b/code/modules/mob/living/carbon/alien/alien.dm @@ -48,6 +48,7 @@ internal_organs += new /obj/item/organ/alien/hivenode internal_organs += new /obj/item/organ/tongue/alien internal_organs += new /obj/item/organ/eyes/night_vision/alien + internal_organs += new /obj/item/organ/ears ..() /mob/living/carbon/alien/assess_threat() // beepsky won't hunt aliums @@ -147,6 +148,8 @@ Des: Removes all infected images from the alien. mind.transfer_to(new_xeno) qdel(src) + // TODO make orbiters orbit the new xeno, or make xenos species rather than types + #undef HEAT_DAMAGE_LEVEL_1 #undef HEAT_DAMAGE_LEVEL_2 #undef HEAT_DAMAGE_LEVEL_3 diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 97686940bd..36035b9ae1 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -722,6 +722,9 @@ if(reagents) reagents.addiction_list = list() ..() + // heal ears after healing disabilities, since ears check DEAF disability + // when healing. + restoreEars() /mob/living/carbon/can_be_revived() . = ..() diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index bce7889758..0d8cfeb98d 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -277,20 +277,24 @@ /mob/living/carbon/soundbang_act(intensity = 1, stun_pwr = 1, damage_pwr = 5, deafen_pwr = 15) var/ear_safety = get_ear_protection() + var/obj/item/organ/ears/ears = getorganslot("ears") if(ear_safety < 2) //has ears var/effect_amount = intensity - ear_safety if(effect_amount > 0) if(stun_pwr) Stun(stun_pwr*effect_amount) Weaken(stun_pwr*effect_amount) - if(deafen_pwr || damage_pwr) - setEarDamage(ear_damage + damage_pwr*effect_amount, max(ear_deaf, deafen_pwr*effect_amount)) - if (ear_damage >= 15) + if(istype(ears) && (deafen_pwr || damage_pwr)) + ears.ear_damage += damage_pwr * effect_amount + ears.deaf = max(ears.deaf, deafen_pwr * effect_amount) + + if(ears.ear_damage >= 15) to_chat(src, "Your ears start to ring badly!") - if(prob(ear_damage - 5)) - to_chat(src, "You can't hear anything!") - disabilities |= DEAF - else if(ear_damage >= 5) + if(prob(ears.ear_damage - 5)) + to_chat(src, "You can't hear anything!") + ears.ear_damage = min(ears.ear_damage, UNHEALING_EAR_DAMAGE) + // you need earmuffs, inacusiate, or replacement + else if(ears.ear_damage >= 5) to_chat(src, "Your ears start to ring!") src << sound('sound/weapons/flash_ring.ogg',0,1,0,250) return effect_amount //how soundbanged we are @@ -310,3 +314,9 @@ hit_clothes = head if(hit_clothes) hit_clothes.take_damage(damage_amount, damage_type, damage_flag, 0) + +/mob/living/carbon/can_hear() + . = FALSE + var/obj/item/organ/ears/ears = getorganslot("ears") + if(istype(ears) && !ears.deaf) + . = TRUE diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 8b9899d067..ebbe18ff11 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -59,6 +59,7 @@ internal_organs += new /obj/item/organ/heart internal_organs += new dna.species.mutanteyes() + internal_organs += new dna.species.mutantears internal_organs += new /obj/item/organ/brain give_genitals() ..() diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index af815756c1..7324e156a4 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -70,18 +70,8 @@ else if(eye_blurry) //blurry eyes heal slowly adjust_blurriness(-1) - //Ears - if(disabilities & DEAF) //disabled-deaf, doesn't get better on its own - setEarDamage(-1, max(ear_deaf, 1)) - else - if(istype(ears, /obj/item/clothing/ears/earmuffs)) // earmuffs rest your ears, healing ear_deaf faster and ear_damage, but keeping you deaf. - setEarDamage(max(ear_damage-0.10, 0), max(ear_deaf - 1, 1)) - // deafness heals slowly over time, unless ear_damage is over 100 - if(ear_damage < 100) - adjustEarDamage(-0.05,-1) - if (getBrainLoss() >= 60 && stat != DEAD) - if (prob(3)) + if(prob(3)) if(prob(25)) emote("drool") else diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index 71b607c928..2f5cddb4d7 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -67,6 +67,9 @@ //Eyes var/obj/item/organ/eyes/mutanteyes = /obj/item/organ/eyes + + //Ears + var/obj/item/organ/ears/mutantears = /obj/item/organ/ears //Citadel snowflake var/fixed_mut_color2 = "" @@ -127,6 +130,7 @@ var/obj/item/organ/lungs/lungs = C.getorganslot("lungs") var/obj/item/organ/appendix/appendix = C.getorganslot("appendix") var/obj/item/organ/eyes/eyes = C.getorganslot("eye_sight") + var/obj/item/organ/ears/ears = C.getorganslot("ears") if((NOBLOOD in species_traits) && heart) heart.Remove(C) @@ -144,6 +148,11 @@ eyes = new mutanteyes eyes.Insert(C) + if(ears) + qdel(ears) + ears = new mutantears + ears.Insert(C) + if((!(NOBREATH in species_traits)) && !lungs) if(mutantlungs) lungs = new mutantlungs() diff --git a/code/modules/mob/living/carbon/monkey/monkey.dm b/code/modules/mob/living/carbon/monkey/monkey.dm index e5af3d5904..34017d6728 100644 --- a/code/modules/mob/living/carbon/monkey/monkey.dm +++ b/code/modules/mob/living/carbon/monkey/monkey.dm @@ -45,6 +45,7 @@ internal_organs += new /obj/item/organ/brain internal_organs += new /obj/item/organ/tongue internal_organs += new /obj/item/organ/eyes + internal_organs += new /obj/item/organ/ears ..() /mob/living/carbon/monkey/movement_delay() @@ -134,15 +135,6 @@ protection = protection/7 //the rest of the body isn't covered. return protection -/mob/living/carbon/monkey/fully_heal(admin_revive = 0) - if(!getorganslot("lungs")) - var/obj/item/organ/lungs/L = new() - L.Insert(src) - if(!getorganslot("tongue")) - var/obj/item/organ/tongue/T = new() - T.Insert(src) - ..() - /mob/living/carbon/monkey/IsVocal() if(!getorganslot("lungs")) return 0 diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 1ab7177b72..8a15572aa0 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -126,14 +126,6 @@ if(client && !eye_blurry) clear_fullscreen("blurry") - //Ears - if(disabilities & DEAF) //disabled-deaf, doesn't get better on its own - setEarDamage(-1, max(ear_deaf, 1)) - else - // deafness heals slowly over time, unless ear_damage is over 100 - if(ear_damage < 100) - adjustEarDamage(-0.05,-1) - /mob/living/proc/update_damage_hud() return diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 8187dde9aa..e3502e7a90 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -379,8 +379,6 @@ cure_blind() cure_husk() disabilities = 0 - ear_deaf = 0 - ear_damage = 0 hallucination = 0 heal_overall_damage(100000, 100000, 0, 0, 1) //heal brute and burn dmg on both organic and robotic limbs, and update health right away. ExtinguishMob() diff --git a/code/modules/mob/living/silicon/ai/say.dm b/code/modules/mob/living/silicon/ai/say.dm index 3aeec1035d..4080cea4fb 100644 --- a/code/modules/mob/living/silicon/ai/say.dm +++ b/code/modules/mob/living/silicon/ai/say.dm @@ -153,7 +153,7 @@ if(!only_listener) // Play voice for all mobs in the z level for(var/mob/M in GLOB.player_list) - if(M.client && !M.ear_deaf && (M.client.prefs.toggles & SOUND_ANNOUNCEMENTS)) + if(M.client && M.can_hear() && (M.client.prefs.toggles & SOUND_ANNOUNCEMENTS)) var/turf/T = get_turf(M) if(T.z == z_level) M << voice diff --git a/code/modules/mob/living/silicon/status_procs.dm b/code/modules/mob/living/silicon/status_procs.dm index c14e6c254f..3947cb020f 100644 --- a/code/modules/mob/living/silicon/status_procs.dm +++ b/code/modules/mob/living/silicon/status_procs.dm @@ -36,11 +36,3 @@ . = ..() if(. && updating) update_stat() - -/////////////////////////////////// EAR DAMAGE //////////////////////////////////// - -/mob/living/silicon/adjustEarDamage() - return - -/mob/living/silicon/setEarDamage() - return \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/status_procs.dm b/code/modules/mob/living/simple_animal/status_procs.dm index fd90f825a3..62a262f9e6 100644 --- a/code/modules/mob/living/simple_animal/status_procs.dm +++ b/code/modules/mob/living/simple_animal/status_procs.dm @@ -23,12 +23,3 @@ /mob/living/simple_animal/become_blind() return - - - -/mob/living/simple_animal/adjustEarDamage() - return - -/mob/living/simple_animal/setEarDamage() - return - diff --git a/code/modules/mob/living/status_procs.dm b/code/modules/mob/living/status_procs.dm index f26fd04e59..9fe8443506 100644 --- a/code/modules/mob/living/status_procs.dm +++ b/code/modules/mob/living/status_procs.dm @@ -1,21 +1,7 @@ //Here are the procs used to modify status effects of a mob. -//The effects include: stunned, weakened, paralysis, sleeping, resting, jitteriness, dizziness, ear damage, +//The effects include: stunned, weakened, paralysis, sleeping, resting, jitteriness, dizziness, // eye damage, eye_blind, eye_blurry, druggy, BLIND disability, and NEARSIGHT disability. -/////////////////////////////////// EAR DAMAGE //////////////////////////////////// - -//damage/heal the mob ears and adjust the deaf amount -/mob/living/adjustEarDamage(damage, deaf) - ear_damage = max(0, ear_damage + damage) - ear_deaf = max(0, ear_deaf + deaf) - -//pass a negative argument to skip one of the variable -/mob/living/setEarDamage(damage, deaf) - if(damage >= 0) - ear_damage = damage - if(deaf >= 0) - ear_deaf = deaf - //////////////////////////////STUN //////////////////////////////////// @@ -71,4 +57,4 @@ to_chat(src, "[priority_absorb_key["self_message"]]") priority_absorb_key["stuns_absorbed"] += amount return 0 - return ..() \ No newline at end of file + return ..() diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 07aaa98fbc..95e034fd96 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -73,7 +73,7 @@ msg = alt_msg type = alt_type - if(type & 2 && ear_deaf)//Hearing related + if(type & 2 && !can_hear())//Hearing related if(!alt_msg) return else @@ -972,10 +972,6 @@ set_eye_damage(var_value) if("eye_blurry") set_blurriness(var_value) - if("ear_deaf") - setEarDamage(-1, var_value) - if("ear_damage") - setEarDamage(var_value, -1) if("maxHealth") updatehealth() if("resize") @@ -1013,4 +1009,4 @@ switch(var_name) if("logging") return debug_variable(var_name, logging, 0, src, FALSE) - . = ..() \ No newline at end of file + . = ..() diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 998613bb37..82dca7986b 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -42,8 +42,6 @@ var/notransform = null //Carbon var/eye_blind = 0 //Carbon var/eye_blurry = 0 //Carbon - var/ear_deaf = 0 //Carbon - var/ear_damage = 0 //Carbon var/stuttering = 0 //Carbon var/slurring = 0 //Carbon var/cultslurring = 0 //Carbon diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 729e924261..1e748776bf 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -482,3 +482,6 @@ It's fairly easy to fix if dealing with single letters but not so much with comp var/list/timestamped_message = list("[LAZYLEN(logging[message_type]) + 1]\[[time_stamp()]\] [key_name(src)]" = message) logging[message_type] += timestamped_message + +/mob/proc/can_hear() + . = TRUE diff --git a/code/modules/mob/status_procs.dm b/code/modules/mob/status_procs.dm index caa5a38326..f12e7d6d4e 100644 --- a/code/modules/mob/status_procs.dm +++ b/code/modules/mob/status_procs.dm @@ -143,14 +143,6 @@ /mob/proc/Dizzy(amount) dizziness = max(dizziness,amount,0) -/////////////////////////////////// EAR DAMAGE //////////////////////////////////// - -/mob/proc/adjustEarDamage() - return - -/mob/proc/setEarDamage() - return - /////////////////////////////////// EYE DAMAGE //////////////////////////////////// /mob/proc/damage_eyes(amount) diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index 29ca2df931..dee5b7e68a 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -118,7 +118,7 @@ color = "#6600FF" // rgb: 100, 165, 255 /datum/reagent/medicine/inacusiate/on_mob_life(mob/living/M) - M.setEarDamage(0,0) + M.restoreEars() ..() /datum/reagent/medicine/cryoxadone diff --git a/code/modules/surgery/organs/appendix.dm b/code/modules/surgery/organs/appendix.dm new file mode 100644 index 0000000000..ad51c48d85 --- /dev/null +++ b/code/modules/surgery/organs/appendix.dm @@ -0,0 +1,32 @@ +/obj/item/organ/appendix + name = "appendix" + icon_state = "appendix" + zone = "groin" + slot = "appendix" + var/inflamed = 0 + +/obj/item/organ/appendix/update_icon() + if(inflamed) + icon_state = "appendixinflamed" + name = "inflamed appendix" + else + icon_state = "appendix" + name = "appendix" + +/obj/item/organ/appendix/Remove(mob/living/carbon/M, special = 0) + for(var/datum/disease/appendicitis/A in M.viruses) + A.cure() + inflamed = 1 + update_icon() + ..() + +/obj/item/organ/appendix/Insert(mob/living/carbon/M, special = 0) + ..() + if(inflamed) + M.AddDisease(new /datum/disease/appendicitis) + +/obj/item/organ/appendix/prepare_eat() + var/obj/S = ..() + if(inflamed) + S.reagents.add_reagent("bad_food", 5) + return S diff --git a/code/modules/surgery/organs/ears.dm b/code/modules/surgery/organs/ears.dm new file mode 100644 index 0000000000..c35cf5c670 --- /dev/null +++ b/code/modules/surgery/organs/ears.dm @@ -0,0 +1,69 @@ +/obj/item/organ/ears + name = "ears" + icon_state = "ears" + desc = "There are three parts to the ear. Inner, middle and outer. Only one of these parts should be normally visible." + zone = "head" + slot = "ears" + + // `deaf` measures "ticks" of deafness. While > 0, the person is unable + // to hear anything. + var/deaf = 0 + + // `ear_damage` measures long term damage to the ears, if too high, + // the person will not have either `deaf` or `ear_damage` decrease + // without external aid (earmuffs, drugs) + var/ear_damage = 0 + +/obj/item/organ/ears/on_life() + if(!iscarbon(owner)) + return + var/mob/living/carbon/C = owner + // genetic deafness prevents the body from using the ears, even if healthy + if(C.disabilities & DEAF) + deaf = max(deaf, 1) + else + if(HAS_SECONDARY_FLAG(C.ears, HEALS_EARS)) + deaf = max(deaf - 1, 1) + ear_damage = max(ear_damage - 0.10, 0) + // if higher than UNHEALING_EAR_DAMAGE, no natural healing occurs. + if(ear_damage < UNHEALING_EAR_DAMAGE) + ear_damage = max(ear_damage - 0.05, 0) + deaf = max(deaf - 1, 0) + +/obj/item/organ/ears/proc/restoreEars() + deaf = 0 + ear_damage = 0 + + var/mob/living/carbon/C = owner + + if(iscarbon(owner) && C.disabilities & DEAF) + deaf = 1 + +/obj/item/organ/ears/proc/adjustEarDamage(ddmg, ddeaf) + ear_damage = max(ear_damage + ddmg, 0) + deaf = max(deaf + ddeaf, 0) + +/obj/item/organ/ears/proc/minimumDeafTicks(value) + deaf = max(deaf, value) + + +/mob/proc/restoreEars() + +/mob/living/carbon/restoreEars() + var/obj/item/organ/ears/ears = getorgan(/obj/item/organ/ears) + if(ears) + ears.restoreEars() + +/mob/proc/adjustEarDamage() + +/mob/living/carbon/adjustEarDamage(ddmg, ddeaf) + var/obj/item/organ/ears/ears = getorgan(/obj/item/organ/ears) + if(ears) + ears.adjustEarDamage(ddmg, ddeaf) + +/mob/proc/minimumDeafTicks() + +/mob/living/carbon/minimumDeafTicks(value) + var/obj/item/organ/ears/ears = getorgan(/obj/item/organ/ears) + if(ears) + ears.minimumDeafTicks(value) diff --git a/code/modules/surgery/organs/eyes.dm b/code/modules/surgery/organs/eyes.dm new file mode 100644 index 0000000000..672ac924ad --- /dev/null +++ b/code/modules/surgery/organs/eyes.dm @@ -0,0 +1,130 @@ +/obj/item/organ/eyes + name = "eyes" + icon_state = "eyeballs" + desc = "I see you!" + zone = "eyes" + slot = "eye_sight" + + var/sight_flags = 0 + var/see_in_dark = 2 + var/tint = 0 + var/eye_color = "" //set to a hex code to override a mob's eye color + var/old_eye_color = "fff" + var/flash_protect = 0 + var/see_invisible = SEE_INVISIBLE_LIVING + var/lighting_alpha + +/obj/item/organ/eyes/Insert(mob/living/carbon/M, special = 0) + ..() + if(ishuman(owner)) + var/mob/living/carbon/human/HMN = owner + old_eye_color = HMN.eye_color + if(eye_color) + HMN.eye_color = eye_color + HMN.regenerate_icons() + else + eye_color = HMN.eye_color + M.update_tint() + owner.update_sight() + +/obj/item/organ/eyes/Remove(mob/living/carbon/M, special = 0) + ..() + if(ishuman(M) && eye_color) + var/mob/living/carbon/human/HMN = M + HMN.eye_color = old_eye_color + HMN.regenerate_icons() + M.update_tint() + M.update_sight() + +/obj/item/organ/eyes/night_vision + name = "shadow eyes" + desc = "A spooky set of eyes that can see in the dark." + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + actions_types = list(/datum/action/item_action/organ_action/use) + var/night_vision = TRUE + +/obj/item/organ/eyes/night_vision/ui_action_click() + switch(lighting_alpha) + if (LIGHTING_PLANE_ALPHA_VISIBLE) + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + if (LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE) + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + if (LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE) + lighting_alpha = LIGHTING_PLANE_ALPHA_INVISIBLE + else + lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE + owner.update_sight() + +/obj/item/organ/eyes/night_vision/alien + name = "alien eyes" + desc = "It turned out they had them after all!" + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + sight_flags = SEE_MOBS + + +///Robotic + +/obj/item/organ/eyes/robotic + name = "robotic eyes" + icon_state = "cybernetic_eyeballs" + desc = "Your vision is augmented." + status = ORGAN_ROBOTIC + +/obj/item/organ/eyes/robotic/emp_act(severity) + if(!owner) + return + if(severity > 1) + if(prob(10 * severity)) + return + to_chat(owner, "Static obfuscates your vision!") + owner.flash_act(visual = 1) + +/obj/item/organ/eyes/robotic/xray + name = "X-ray eyes" + desc = "These cybernetic eyes will give you X-ray vision. Blinking is futile." + eye_color = "000" + see_in_dark = 8 + sight_flags = SEE_MOBS | SEE_OBJS | SEE_TURFS + +/obj/item/organ/eyes/robotic/thermals + name = "Thermals eyes" + desc = "These cybernetic eye implants will give you Thermal vision. Vertical slit pupil included." + eye_color = "FC0" + origin_tech = "materials=5;programming=4;biotech=4;magnets=4;syndicate=1" + sight_flags = SEE_MOBS + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + flash_protect = -1 + see_in_dark = 8 + +/obj/item/organ/eyes/robotic/flashlight + name = "flashlight eyes" + desc = "It's two flashlights rigged together with some wire. Why would you put these in someones head?" + eye_color ="fee5a3" + icon = 'icons/obj/lighting.dmi' + icon_state = "flashlight_eyes" + flash_protect = 2 + tint = INFINITY + +/obj/item/organ/eyes/robotic/flashlight/emp_act(severity) + return + +/obj/item/organ/eyes/robotic/flashlight/Insert(var/mob/living/carbon/M, var/special = 0) + ..() + M.set_light(M.light_range + 15, M.light_power + 1) + + +/obj/item/organ/eyes/robotic/flashlight/Remove(var/mob/living/carbon/M, var/special = 0) + M.set_light(M.light_range -15, M.light_power - 1) + ..() + +// Welding shield implant +/obj/item/organ/eyes/robotic/shield + name = "shielded robotic eyes" + desc = "These reactive micro-shields will protect you from welders and flashes without obscuring your vision." + origin_tech = "materials=4;biotech=3;engineering=4;plasmatech=3" + flash_protect = 2 + +/obj/item/organ/eyes/robotic/shield/emp_act(severity) + return diff --git a/code/modules/surgery/organs/heart.dm b/code/modules/surgery/organs/heart.dm new file mode 100644 index 0000000000..9ffe8b6d06 --- /dev/null +++ b/code/modules/surgery/organs/heart.dm @@ -0,0 +1,126 @@ +/obj/item/organ/heart + name = "heart" + icon_state = "heart-on" + zone = "chest" + slot = "heart" + origin_tech = "biotech=5" + // Heart attack code is in code/modules/mob/living/carbon/human/life.dm + var/beating = 1 + var/icon_base = "heart" + attack_verb = list("beat", "thumped") + +/obj/item/organ/heart/update_icon() + if(beating) + icon_state = "[icon_base]-on" + else + icon_state = "[icon_base]-off" + +/obj/item/organ/heart/Remove(mob/living/carbon/M, special = 0) + ..() + if(!special) + addtimer(CALLBACK(src, .proc/stop_if_unowned), 120) + +/obj/item/organ/heart/proc/stop_if_unowned() + if(!owner) + Stop() + +/obj/item/organ/heart/attack_self(mob/user) + ..() + if(!beating) + visible_message("[user] squeezes [src] to \ + make it beat again!", "You squeeze \ + [src] to make it beat again!") + Restart() + addtimer(CALLBACK(src, .proc/stop_if_unowned), 80) + +/obj/item/organ/heart/proc/Stop() + beating = 0 + update_icon() + return 1 + +/obj/item/organ/heart/proc/Restart() + beating = 1 + update_icon() + return 1 + +/obj/item/organ/heart/prepare_eat() + var/obj/S = ..() + S.icon_state = "heart-off" + return S + + +/obj/item/organ/heart/cursed + name = "cursed heart" + desc = "A heart that, when inserted, will force you to pump it manually." + icon_state = "cursedheart-off" + icon_base = "cursedheart" + origin_tech = "biotech=6" + actions_types = list(/datum/action/item_action/organ_action/cursed_heart) + var/last_pump = 0 + var/add_colour = TRUE //So we're not constantly recreating colour datums + var/pump_delay = 30 //you can pump 1 second early, for lag, but no more (otherwise you could spam heal) + var/blood_loss = 100 //600 blood is human default, so 5 failures (below 122 blood is where humans die because reasons?) + + //How much to heal per pump, negative numbers would HURT the player + var/heal_brute = 0 + var/heal_burn = 0 + var/heal_oxy = 0 + + +/obj/item/organ/heart/cursed/attack(mob/living/carbon/human/H, mob/living/carbon/human/user, obj/target) + if(H == user && istype(H)) + playsound(user,'sound/effects/singlebeat.ogg',40,1) + user.drop_item() + Insert(user) + else + return ..() + +/obj/item/organ/heart/cursed/on_life() + if(world.time > (last_pump + pump_delay)) + if(ishuman(owner) && owner.client) //While this entire item exists to make people suffer, they can't control disconnects. + var/mob/living/carbon/human/H = owner + if(H.dna && !(NOBLOOD in H.dna.species.species_traits)) + H.blood_volume = max(H.blood_volume - blood_loss, 0) + to_chat(H, "You have to keep pumping your blood!") + if(add_colour) + H.add_client_colour(/datum/client_colour/cursed_heart_blood) //bloody screen so real + add_colour = FALSE + else + last_pump = world.time //lets be extra fair *sigh* + +/obj/item/organ/heart/cursed/Insert(mob/living/carbon/M, special = 0) + ..() + if(owner) + to_chat(owner, "Your heart has been replaced with a cursed one, you have to pump this one manually otherwise you'll die!") + +/datum/action/item_action/organ_action/cursed_heart + name = "Pump your blood" + +//You are now brea- pumping blood manually +/datum/action/item_action/organ_action/cursed_heart/Trigger() + . = ..() + if(. && istype(target,/obj/item/organ/heart/cursed)) + var/obj/item/organ/heart/cursed/cursed_heart = target + + if(world.time < (cursed_heart.last_pump + (cursed_heart.pump_delay-10))) //no spam + to_chat(owner, "Too soon!") + return + + cursed_heart.last_pump = world.time + playsound(owner,'sound/effects/singlebeat.ogg',40,1) + to_chat(owner, "Your heart beats.") + + var/mob/living/carbon/human/H = owner + if(istype(H)) + if(H.dna && !(NOBLOOD in H.dna.species.species_traits)) + H.blood_volume = min(H.blood_volume + cursed_heart.blood_loss*0.5, BLOOD_VOLUME_MAXIMUM) + H.remove_client_colour(/datum/client_colour/cursed_heart_blood) + cursed_heart.add_colour = TRUE + H.adjustBruteLoss(-cursed_heart.heal_brute) + H.adjustFireLoss(-cursed_heart.heal_burn) + H.adjustOxyLoss(-cursed_heart.heal_oxy) + + +/datum/client_colour/cursed_heart_blood + priority = 100 //it's an indicator you're dieing, so it's very high priority + colour = "red" diff --git a/code/modules/surgery/organs/lungs.dm b/code/modules/surgery/organs/lungs.dm new file mode 100644 index 0000000000..ece2ad3f40 --- /dev/null +++ b/code/modules/surgery/organs/lungs.dm @@ -0,0 +1,267 @@ +#define HUMAN_MAX_OXYLOSS 3 +#define HUMAN_CRIT_MAX_OXYLOSS (SSmobs.wait/30) +#define HEAT_GAS_DAMAGE_LEVEL_1 2 +#define HEAT_GAS_DAMAGE_LEVEL_2 4 +#define HEAT_GAS_DAMAGE_LEVEL_3 8 + +#define COLD_GAS_DAMAGE_LEVEL_1 0.5 +#define COLD_GAS_DAMAGE_LEVEL_2 1.5 +#define COLD_GAS_DAMAGE_LEVEL_3 3 + +/obj/item/organ/lungs + name = "lungs" + icon_state = "lungs" + zone = "chest" + slot = "lungs" + gender = PLURAL + w_class = WEIGHT_CLASS_NORMAL + var/list/breathlevels = list("safe_oxygen_min" = 16,"safe_oxygen_max" = 0,"safe_co2_min" = 0,"safe_co2_max" = 10, + "safe_toxins_min" = 0,"safe_toxins_max" = 0.05,"SA_para_min" = 1,"SA_sleep_min" = 5,"BZ_trip_balls_min" = 1) + + //Breath damage + + var/safe_oxygen_min = 16 // Minimum safe partial pressure of O2, in kPa + var/safe_oxygen_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/BZ_trip_balls_min = 1 //BZ gas. + + var/oxy_breath_dam_min = 1 + var/oxy_breath_dam_max = 10 + var/co2_breath_dam_min = 1 + var/co2_breath_dam_max = 10 + var/tox_breath_dam_min = MIN_PLASMA_DAMAGE + var/tox_breath_dam_max = MAX_PLASMA_DAMAGE + + + +/obj/item/organ/lungs/proc/check_breath(datum/gas_mixture/breath, var/mob/living/carbon/human/H) + if((H.status_flags & GODMODE)) + return + + var/species_traits = list() + if(H && H.dna && H.dna.species && H.dna.species.species_traits) + species_traits = H.dna.species.species_traits + + if(!breath || (breath.total_moles() == 0)) + if(H.reagents.has_reagent("epinephrine")) + return + if(H.health >= HEALTH_THRESHOLD_CRIT) + H.adjustOxyLoss(HUMAN_MAX_OXYLOSS) + else if(!(NOCRITDAMAGE in species_traits)) + H.adjustOxyLoss(HUMAN_CRIT_MAX_OXYLOSS) + + H.failed_last_breath = 1 + if(safe_oxygen_min) + H.throw_alert("oxy", /obj/screen/alert/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) + return 0 + + var/gas_breathed = 0 + + var/list/breath_gases = breath.gases + + breath.assert_gases("o2", "plasma", "co2", "n2o", "bz") + + //Partial pressures in our breath + var/O2_pp = breath.get_breath_partial_pressure(breath_gases["o2"][MOLES]) + var/Toxins_pp = breath.get_breath_partial_pressure(breath_gases["plasma"][MOLES]) + var/CO2_pp = breath.get_breath_partial_pressure(breath_gases["co2"][MOLES]) + + + //-- OXY --// + + //Too much oxygen! //Yes, some species may not like it. + if(safe_oxygen_max) + if(O2_pp > safe_oxygen_max) + var/ratio = (breath_gases["o2"][MOLES]/safe_oxygen_max) * 10 + H.adjustOxyLoss(Clamp(ratio,oxy_breath_dam_min,oxy_breath_dam_max)) + 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_gases["o2"][MOLES]) + H.throw_alert("oxy", /obj/screen/alert/oxy) + else + H.failed_last_breath = 0 + if(H.getOxyLoss()) + H.adjustOxyLoss(-5) + gas_breathed = breath_gases["o2"][MOLES] + H.clear_alert("oxy") + + //Exhale + breath_gases["o2"][MOLES] -= gas_breathed + breath_gases["co2"][MOLES] += 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(3) + H.adjustOxyLoss(3) // 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.adjustOxyLoss(8) + 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(breathlevels["safe_co2_min"]) + if(CO2_pp < safe_co2_min) + gas_breathed = handle_too_little_breath(H,CO2_pp, safe_co2_min,breath_gases["co2"][MOLES]) + H.throw_alert("not_enough_co2", /obj/screen/alert/not_enough_co2) + else + H.failed_last_breath = 0 + H.adjustOxyLoss(-5) + gas_breathed = breath_gases["co2"][MOLES] + H.clear_alert("not_enough_co2") + + //Exhale + breath_gases["co2"][MOLES] -= gas_breathed + breath_gases["o2"][MOLES] += gas_breathed + gas_breathed = 0 + + + //-- TOX --// + + //Too much toxins! + if(safe_toxins_max) + if(Toxins_pp > safe_toxins_max) + var/ratio = (breath_gases["plasma"][MOLES]/safe_toxins_max) * 10 + if(H.reagents) + H.reagents.add_reagent("plasma", Clamp(ratio, tox_breath_dam_min, tox_breath_dam_max)) + H.throw_alert("tox_in_air", /obj/screen/alert/tox_in_air) + else + H.clear_alert("tox_in_air") + + + //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_gases["plasma"][MOLES]) + H.throw_alert("not_enough_tox", /obj/screen/alert/not_enough_tox) + else + H.failed_last_breath = 0 + H.adjustOxyLoss(-5) + gas_breathed = breath_gases["plasma"][MOLES] + H.clear_alert("not_enough_tox") + + //Exhale + breath_gases["plasma"][MOLES] -= gas_breathed + breath_gases["co2"][MOLES] += gas_breathed + gas_breathed = 0 + + + //-- TRACES --// + + if(breath) // If there's some other shit in the air lets deal with it here. + + // N2O + + var/SA_pp = breath.get_breath_partial_pressure(breath_gases["n2o"][MOLES]) + if(SA_pp > SA_para_min) // Enough to make us paralysed for a bit + H.Paralyse(3) // 3 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.Sleeping(max(H.sleeping+2, 10)) + 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")) + + // BZ + + var/bz_pp = breath.get_breath_partial_pressure(breath_gases["bz"][MOLES]) + if(bz_pp > BZ_trip_balls_min) + H.hallucination += 20 + if(prob(33)) + H.adjustBrainLoss(3) + else if(bz_pp > 0.01) + H.hallucination += 5//Removed at 2 per tick so this will slowly build up + handle_breath_temperature(breath, H) + breath.garbage_collect() + + return 1 + + +/obj/item/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 0 + + 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! + H.failed_last_breath = 1 + . = true_pp*ratio/6 + else + H.adjustOxyLoss(HUMAN_MAX_OXYLOSS) + H.failed_last_breath = 1 + + +/obj/item/organ/lungs/proc/handle_breath_temperature(datum/gas_mixture/breath, mob/living/carbon/human/H) // called by human/life, handles temperatures + if(abs(310.15 - breath.temperature) > 50) + + var/species_traits = list() + if(H && H.dna && H.dna.species && H.dna.species.species_traits) + species_traits = H.dna.species.species_traits + + if(!(GLOB.mutations_list[COLDRES] in H.dna.mutations) && !(RESISTCOLD in species_traits)) // COLD DAMAGE + switch(breath.temperature) + if(-INFINITY to 120) + H.apply_damage(COLD_GAS_DAMAGE_LEVEL_3, BURN, "head") + if(120 to 200) + H.apply_damage(COLD_GAS_DAMAGE_LEVEL_2, BURN, "head") + if(200 to 260) + H.apply_damage(COLD_GAS_DAMAGE_LEVEL_1, BURN, "head") + + if(!(RESISTHOT in species_traits)) // HEAT DAMAGE + switch(breath.temperature) + if(360 to 400) + H.apply_damage(HEAT_GAS_DAMAGE_LEVEL_1, BURN, "head") + if(400 to 1000) + H.apply_damage(HEAT_GAS_DAMAGE_LEVEL_2, BURN, "head") + if(1000 to INFINITY) + H.apply_damage(HEAT_GAS_DAMAGE_LEVEL_3, BURN, "head") + +/obj/item/organ/lungs/prepare_eat() + var/obj/S = ..() + S.reagents.add_reagent("salbutamol", 5) + return S + +/obj/item/organ/lungs/plasmaman + name = "plasma filter" + + safe_oxygen_min = 0 //We don't breath this + safe_toxins_min = 16 //We breath THIS! + safe_toxins_max = 0 + +#undef HUMAN_MAX_OXYLOSS +#undef HUMAN_CRIT_MAX_OXYLOSS +#undef HEAT_GAS_DAMAGE_LEVEL_1 +#undef HEAT_GAS_DAMAGE_LEVEL_2 +#undef HEAT_GAS_DAMAGE_LEVEL_3 + +#undef COLD_GAS_DAMAGE_LEVEL_1 +#undef COLD_GAS_DAMAGE_LEVEL_2 +#undef COLD_GAS_DAMAGE_LEVEL_3 diff --git a/code/modules/surgery/organs/organ_internal.dm b/code/modules/surgery/organs/organ_internal.dm index c6c2a86728..f2503252b4 100644 --- a/code/modules/surgery/organs/organ_internal.dm +++ b/code/modules/surgery/organs/organ_internal.dm @@ -102,666 +102,34 @@ //Looking for brains? //Try code/modules/mob/living/carbon/brain/brain_item.dm - - -/obj/item/organ/heart - name = "heart" - icon_state = "heart-on" - zone = "chest" - slot = "heart" - origin_tech = "biotech=5" - // Heart attack code is in code/modules/mob/living/carbon/human/life.dm - var/beating = 1 - var/icon_base = "heart" - attack_verb = list("beat", "thumped") - -/obj/item/organ/heart/update_icon() - if(beating) - icon_state = "[icon_base]-on" - else - icon_state = "[icon_base]-off" - -/obj/item/organ/heart/Remove(mob/living/carbon/M, special = 0) - ..() - if(!special) - addtimer(CALLBACK(src, .proc/stop_if_unowned), 120) - -/obj/item/organ/heart/proc/stop_if_unowned() - if(!owner) - Stop() - -/obj/item/organ/heart/attack_self(mob/user) - ..() - if(!beating) - visible_message("[user] squeezes [src] to \ - make it beat again!", "You squeeze \ - [src] to make it beat again!") - Restart() - addtimer(CALLBACK(src, .proc/stop_if_unowned), 80) - -/obj/item/organ/heart/proc/Stop() - beating = 0 - update_icon() - return 1 - -/obj/item/organ/heart/proc/Restart() - beating = 1 - update_icon() - return 1 - -/obj/item/organ/heart/prepare_eat() - var/obj/S = ..() - S.icon_state = "heart-off" - return S - - -/obj/item/organ/heart/cursed - name = "cursed heart" - desc = "A heart that, when inserted, will force you to pump it manually." - icon_state = "cursedheart-off" - icon_base = "cursedheart" - origin_tech = "biotech=6" - actions_types = list(/datum/action/item_action/organ_action/cursed_heart) - var/last_pump = 0 - var/add_colour = TRUE //So we're not constantly recreating colour datums - var/pump_delay = 30 //you can pump 1 second early, for lag, but no more (otherwise you could spam heal) - var/blood_loss = 100 //600 blood is human default, so 5 failures (below 122 blood is where humans die because reasons?) - - //How much to heal per pump, negative numbers would HURT the player - var/heal_brute = 0 - var/heal_burn = 0 - var/heal_oxy = 0 - - -/obj/item/organ/heart/cursed/attack(mob/living/carbon/human/H, mob/living/carbon/human/user, obj/target) - if(H == user && istype(H)) - playsound(user,'sound/effects/singlebeat.ogg',40,1) - user.drop_item() - Insert(user) - else - return ..() - -/obj/item/organ/heart/cursed/on_life() - if(world.time > (last_pump + pump_delay)) - if(ishuman(owner) && owner.client) //While this entire item exists to make people suffer, they can't control disconnects. - var/mob/living/carbon/human/H = owner - if(H.dna && !(NOBLOOD in H.dna.species.species_traits)) - H.blood_volume = max(H.blood_volume - blood_loss, 0) - to_chat(H, "You have to keep pumping your blood!") - if(add_colour) - H.add_client_colour(/datum/client_colour/cursed_heart_blood) //bloody screen so real - add_colour = FALSE - else - last_pump = world.time //lets be extra fair *sigh* - -/obj/item/organ/heart/cursed/Insert(mob/living/carbon/M, special = 0) - ..() - if(owner) - to_chat(owner, "Your heart has been replaced with a cursed one, you have to pump this one manually otherwise you'll die!") - -/datum/action/item_action/organ_action/cursed_heart - name = "Pump your blood" - -//You are now brea- pumping blood manually -/datum/action/item_action/organ_action/cursed_heart/Trigger() - . = ..() - if(. && istype(target,/obj/item/organ/heart/cursed)) - var/obj/item/organ/heart/cursed/cursed_heart = target - - if(world.time < (cursed_heart.last_pump + (cursed_heart.pump_delay-10))) //no spam - to_chat(owner, "Too soon!") - return - - cursed_heart.last_pump = world.time - playsound(owner,'sound/effects/singlebeat.ogg',40,1) - to_chat(owner, "Your heart beats.") - - var/mob/living/carbon/human/H = owner - if(istype(H)) - if(H.dna && !(NOBLOOD in H.dna.species.species_traits)) - H.blood_volume = min(H.blood_volume + cursed_heart.blood_loss*0.5, BLOOD_VOLUME_MAXIMUM) - H.remove_client_colour(/datum/client_colour/cursed_heart_blood) - cursed_heart.add_colour = TRUE - H.adjustBruteLoss(-cursed_heart.heal_brute) - H.adjustFireLoss(-cursed_heart.heal_burn) - H.adjustOxyLoss(-cursed_heart.heal_oxy) - - -/datum/client_colour/cursed_heart_blood - priority = 100 //it's an indicator you're dieing, so it's very high priority - colour = "red" - -#define HUMAN_MAX_OXYLOSS 3 -#define HUMAN_CRIT_MAX_OXYLOSS (SSmobs.wait/30) -#define HEAT_GAS_DAMAGE_LEVEL_1 2 -#define HEAT_GAS_DAMAGE_LEVEL_2 4 -#define HEAT_GAS_DAMAGE_LEVEL_3 8 - -#define COLD_GAS_DAMAGE_LEVEL_1 0.5 -#define COLD_GAS_DAMAGE_LEVEL_2 1.5 -#define COLD_GAS_DAMAGE_LEVEL_3 3 - -/obj/item/organ/lungs - name = "lungs" - icon_state = "lungs" - zone = "chest" - slot = "lungs" - gender = PLURAL - w_class = WEIGHT_CLASS_NORMAL - var/list/breathlevels = list("safe_oxygen_min" = 16,"safe_oxygen_max" = 0,"safe_co2_min" = 0,"safe_co2_max" = 10, - "safe_toxins_min" = 0,"safe_toxins_max" = 0.05,"SA_para_min" = 1,"SA_sleep_min" = 5,"BZ_trip_balls_min" = 1) - - //Breath damage - - var/safe_oxygen_min = 16 // Minimum safe partial pressure of O2, in kPa - var/safe_oxygen_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/BZ_trip_balls_min = 1 //BZ gas. - - var/oxy_breath_dam_min = 1 - var/oxy_breath_dam_max = 10 - var/co2_breath_dam_min = 1 - var/co2_breath_dam_max = 10 - var/tox_breath_dam_min = MIN_PLASMA_DAMAGE - var/tox_breath_dam_max = MAX_PLASMA_DAMAGE - - - -/obj/item/organ/lungs/proc/check_breath(datum/gas_mixture/breath, var/mob/living/carbon/human/H) - if((H.status_flags & GODMODE)) - return - - var/species_traits = list() - if(H && H.dna && H.dna.species && H.dna.species.species_traits) - species_traits = H.dna.species.species_traits - - if(!breath || (breath.total_moles() == 0)) - if(H.reagents.has_reagent("epinephrine")) - return - if(H.health >= HEALTH_THRESHOLD_CRIT) - H.adjustOxyLoss(HUMAN_MAX_OXYLOSS) - else if(!(NOCRITDAMAGE in species_traits)) - H.adjustOxyLoss(HUMAN_CRIT_MAX_OXYLOSS) - - H.failed_last_breath = 1 - if(safe_oxygen_min) - H.throw_alert("oxy", /obj/screen/alert/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) - return 0 - - var/gas_breathed = 0 - - var/list/breath_gases = breath.gases - - breath.assert_gases("o2", "plasma", "co2", "n2o", "bz") - - //Partial pressures in our breath - var/O2_pp = breath.get_breath_partial_pressure(breath_gases["o2"][MOLES]) - var/Toxins_pp = breath.get_breath_partial_pressure(breath_gases["plasma"][MOLES]) - var/CO2_pp = breath.get_breath_partial_pressure(breath_gases["co2"][MOLES]) - - - //-- OXY --// - - //Too much oxygen! //Yes, some species may not like it. - if(safe_oxygen_max) - if(O2_pp > safe_oxygen_max) - var/ratio = (breath_gases["o2"][MOLES]/safe_oxygen_max) * 10 - H.adjustOxyLoss(Clamp(ratio,oxy_breath_dam_min,oxy_breath_dam_max)) - 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_gases["o2"][MOLES]) - H.throw_alert("oxy", /obj/screen/alert/oxy) - else - H.failed_last_breath = 0 - if(H.getOxyLoss()) - H.adjustOxyLoss(-5) - gas_breathed = breath_gases["o2"][MOLES] - H.clear_alert("oxy") - - //Exhale - breath_gases["o2"][MOLES] -= gas_breathed - breath_gases["co2"][MOLES] += 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(3) - H.adjustOxyLoss(3) // 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.adjustOxyLoss(8) - 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(breathlevels["safe_co2_min"]) - if(CO2_pp < safe_co2_min) - gas_breathed = handle_too_little_breath(H,CO2_pp, safe_co2_min,breath_gases["co2"][MOLES]) - H.throw_alert("not_enough_co2", /obj/screen/alert/not_enough_co2) - else - H.failed_last_breath = 0 - H.adjustOxyLoss(-5) - gas_breathed = breath_gases["co2"][MOLES] - H.clear_alert("not_enough_co2") - - //Exhale - breath_gases["co2"][MOLES] -= gas_breathed - breath_gases["o2"][MOLES] += gas_breathed - gas_breathed = 0 - - - //-- TOX --// - - //Too much toxins! - if(safe_toxins_max) - if(Toxins_pp > safe_toxins_max) - var/ratio = (breath_gases["plasma"][MOLES]/safe_toxins_max) * 10 - if(H.reagents) - H.reagents.add_reagent("plasma", Clamp(ratio, tox_breath_dam_min, tox_breath_dam_max)) - H.throw_alert("tox_in_air", /obj/screen/alert/tox_in_air) - else - H.clear_alert("tox_in_air") - - - //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_gases["plasma"][MOLES]) - H.throw_alert("not_enough_tox", /obj/screen/alert/not_enough_tox) - else - H.failed_last_breath = 0 - H.adjustOxyLoss(-5) - gas_breathed = breath_gases["plasma"][MOLES] - H.clear_alert("not_enough_tox") - - //Exhale - breath_gases["plasma"][MOLES] -= gas_breathed - breath_gases["co2"][MOLES] += gas_breathed - gas_breathed = 0 - - - //-- TRACES --// - - if(breath) // If there's some other shit in the air lets deal with it here. - - // N2O - - var/SA_pp = breath.get_breath_partial_pressure(breath_gases["n2o"][MOLES]) - if(SA_pp > SA_para_min) // Enough to make us paralysed for a bit - H.Paralyse(3) // 3 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.Sleeping(max(H.sleeping+2, 10)) - 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")) - - // BZ - - var/bz_pp = breath.get_breath_partial_pressure(breath_gases["bz"][MOLES]) - if(bz_pp > BZ_trip_balls_min) - H.hallucination += 20 - if(prob(33)) - H.adjustBrainLoss(3) - else if(bz_pp > 0.01) - H.hallucination += 5//Removed at 2 per tick so this will slowly build up - handle_breath_temperature(breath, H) - breath.garbage_collect() - - return 1 - - -/obj/item/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 0 - - 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! - H.failed_last_breath = 1 - . = true_pp*ratio/6 - else - H.adjustOxyLoss(HUMAN_MAX_OXYLOSS) - H.failed_last_breath = 1 - - -/obj/item/organ/lungs/proc/handle_breath_temperature(datum/gas_mixture/breath, mob/living/carbon/human/H) // called by human/life, handles temperatures - if(abs(310.15 - breath.temperature) > 50) - - var/species_traits = list() - if(H && H.dna && H.dna.species && H.dna.species.species_traits) - species_traits = H.dna.species.species_traits - - if(!(GLOB.mutations_list[COLDRES] in H.dna.mutations) && !(RESISTCOLD in species_traits)) // COLD DAMAGE - switch(breath.temperature) - if(-INFINITY to 120) - H.apply_damage(COLD_GAS_DAMAGE_LEVEL_3, BURN, "head") - if(120 to 200) - H.apply_damage(COLD_GAS_DAMAGE_LEVEL_2, BURN, "head") - if(200 to 260) - H.apply_damage(COLD_GAS_DAMAGE_LEVEL_1, BURN, "head") - - if(!(RESISTHOT in species_traits)) // HEAT DAMAGE - switch(breath.temperature) - if(360 to 400) - H.apply_damage(HEAT_GAS_DAMAGE_LEVEL_1, BURN, "head") - if(400 to 1000) - H.apply_damage(HEAT_GAS_DAMAGE_LEVEL_2, BURN, "head") - if(1000 to INFINITY) - H.apply_damage(HEAT_GAS_DAMAGE_LEVEL_3, BURN, "head") - - - - -/obj/item/organ/lungs/prepare_eat() - var/obj/S = ..() - S.reagents.add_reagent("salbutamol", 5) - return S - - -/obj/item/organ/lungs/plasmaman - name = "plasma filter" - - safe_oxygen_min = 0 //We don't breath this - safe_toxins_min = 16 //We breath THIS! - safe_toxins_max = 0 - - - - - -#undef HUMAN_MAX_OXYLOSS -#undef HUMAN_CRIT_MAX_OXYLOSS -#undef HEAT_GAS_DAMAGE_LEVEL_1 -#undef HEAT_GAS_DAMAGE_LEVEL_2 -#undef HEAT_GAS_DAMAGE_LEVEL_3 - -#undef COLD_GAS_DAMAGE_LEVEL_1 -#undef COLD_GAS_DAMAGE_LEVEL_2 -#undef COLD_GAS_DAMAGE_LEVEL_3 - -/obj/item/organ/tongue - name = "tongue" - desc = "A fleshy muscle mostly used for lying." - icon_state = "tonguenormal" - zone = "mouth" - slot = "tongue" - attack_verb = list("licked", "slobbered", "slapped", "frenched", "tongued") - var/list/languages_possible - var/say_mod = null - var/taste_sensitivity = 15 // lower is more sensitive. - -/obj/item/organ/tongue/Initialize(mapload) - ..() - languages_possible = typecacheof(list( - /datum/language/common, - /datum/language/monkey, - /datum/language/ratvar - )) - -/obj/item/organ/tongue/get_spans() - return list() - -/obj/item/organ/tongue/proc/TongueSpeech(var/message) - return message - -/obj/item/organ/tongue/Insert(mob/living/carbon/M, special = 0) - ..() - if(say_mod && M.dna && M.dna.species) - M.dna.species.say_mod = say_mod - -/obj/item/organ/tongue/Remove(mob/living/carbon/M, special = 0) - ..() - if(say_mod && M.dna && M.dna.species) - M.dna.species.say_mod = initial(M.dna.species.say_mod) - -/obj/item/organ/tongue/can_speak_in_language(datum/language/dt) - . = is_type_in_typecache(dt, languages_possible) - -/obj/item/organ/tongue/lizard - name = "forked tongue" - desc = "A thin and long muscle typically found in reptilian races, apparently moonlights as a nose." - icon_state = "tonguelizard" - say_mod = "hisses" - taste_sensitivity = 10 // combined nose + tongue, extra sensitive - -/obj/item/organ/tongue/lizard/TongueSpeech(var/message) - var/regex/lizard_hiss = new("s+", "g") - var/regex/lizard_hiSS = new("S+", "g") - if(copytext(message, 1, 2) != "*") - message = lizard_hiss.Replace(message, "sss") - message = lizard_hiSS.Replace(message, "SSS") - return message - -/obj/item/organ/tongue/fly - name = "proboscis" - desc = "A freakish looking meat tube that apparently can take in liquids." - icon_state = "tonguefly" - say_mod = "buzzes" - taste_sensitivity = 25 // you eat vomit, this is a mercy - -/obj/item/organ/tongue/fly/TongueSpeech(var/message) - var/regex/fly_buzz = new("z+", "g") - var/regex/fly_buZZ = new("Z+", "g") - if(copytext(message, 1, 2) != "*") - message = fly_buzz.Replace(message, "zzz") - message = fly_buZZ.Replace(message, "ZZZ") - return message - -/obj/item/organ/tongue/abductor - name = "superlingual matrix" - desc = "A mysterious structure that allows for instant communication between users. Pretty impressive until you need to eat something." - icon_state = "tongueayylmao" - say_mod = "gibbers" - taste_sensitivity = 101 // ayys cannot taste anything. - -/obj/item/organ/tongue/abductor/TongueSpeech(var/message) - //Hacks - var/mob/living/carbon/human/user = usr - var/rendered = "[user.name]: [message]" - for(var/mob/living/carbon/human/H in GLOB.living_mob_list) - var/obj/item/organ/tongue/T = H.getorganslot("tongue") - if(!T || T.type != type) - continue - else if(H.dna && H.dna.species.id == "abductor" && user.dna && user.dna.species.id == "abductor") - var/datum/species/abductor/Ayy = user.dna.species - var/datum/species/abductor/Byy = H.dna.species - if(Ayy.team != Byy.team) - continue - to_chat(H, rendered) - for(var/mob/M in GLOB.dead_mob_list) - var/link = FOLLOW_LINK(M, user) - to_chat(M, "[link] [rendered]") - return "" - -/obj/item/organ/tongue/zombie - name = "rotting tongue" - desc = "Between the decay and the fact that it's just lying there you doubt a tongue has ever seemed less sexy." - icon_state = "tonguezombie" - say_mod = "moans" - taste_sensitivity = 32 - -/obj/item/organ/tongue/zombie/TongueSpeech(var/message) - var/list/message_list = splittext(message, " ") - var/maxchanges = max(round(message_list.len / 1.5), 2) - - for(var/i = rand(maxchanges / 2, maxchanges), i > 0, i--) - var/insertpos = rand(1, message_list.len - 1) - var/inserttext = message_list[insertpos] - - if(!(copytext(inserttext, length(inserttext) - 2) == "...")) - message_list[insertpos] = inserttext + "..." - - if(prob(20) && message_list.len > 3) - message_list.Insert(insertpos, "[pick("BRAINS", "Brains", "Braaaiinnnsss", "BRAAAIIINNSSS")]...") - - return jointext(message_list, " ") - -/obj/item/organ/tongue/alien - name = "alien tongue" - desc = "According to leading xenobiologists the evolutionary benefit of having a second mouth in your mouth is \"that it looks badass\"." - icon_state = "tonguexeno" - say_mod = "hisses" - taste_sensitivity = 10 // LIZARDS ARE ALIENS CONFIRMED - -/obj/item/organ/tongue/alien/Initialize(mapload) - ..() - languages_possible = typecacheof(list( - /datum/language/xenocommon, - /datum/language/common, - /datum/language/ratvar, - /datum/language/monkey)) - -/obj/item/organ/tongue/alien/TongueSpeech(var/message) - playsound(owner, "hiss", 25, 1, 1) - return message - -/obj/item/organ/tongue/bone - name = "bone \"tongue\"" - desc = "Apparently skeletons alter the sounds they produce \ - through oscillation of their teeth, hence their characteristic \ - rattling." - icon_state = "tonguebone" - say_mod = "rattles" - attack_verb = list("bitten", "chattered", "chomped", "enamelled", "boned") - taste_sensitivity = 101 // skeletons cannot taste anything - - var/chattering = FALSE - var/phomeme_type = "sans" - var/list/phomeme_types = list("sans", "papyrus") - -/obj/item/organ/tongue/bone/New() - . = ..() - phomeme_type = pick(phomeme_types) - -/obj/item/organ/tongue/bone/TongueSpeech(var/message) - . = message - - if(chattering) - //Annoy everyone nearby with your chattering. - chatter(message, phomeme_type, usr) - -/obj/item/organ/tongue/bone/get_spans() - . = ..() - // Feature, if the tongue talks directly, it will speak with its span - switch(phomeme_type) - if("sans") - . |= SPAN_SANS - if("papyrus") - . |= SPAN_PAPYRUS - -/obj/item/organ/tongue/bone/plasmaman - name = "plasma bone \"tongue\"" - desc = "Like animated skeletons, Plasmamen vibrate their teeth in order to produce speech." - icon_state = "tongueplasma" - -/obj/item/organ/tongue/bone/plasmaman/get_spans() - return - -/obj/item/organ/tongue/robot - name = "robotic voicebox" - desc = "A voice synthesizer that can interface with organic lifeforms." - status = ORGAN_ROBOTIC - icon_state = "tonguerobot" - say_mod = "states" - attack_verb = list("beeped", "booped") - taste_sensitivity = 25 // not as good as an organic tongue - -/obj/item/organ/tongue/robot/Initialize(mapload) - ..() - languages_possible = typecacheof(list( - /datum/language/xenocommon, - /datum/language/common, - /datum/language/ratvar, - /datum/language/monkey, - /datum/language/drone, - /datum/language/machine, - /datum/language/swarmer)) - -/obj/item/organ/tongue/robot/get_spans() - return ..() | SPAN_ROBOT - -/obj/item/organ/appendix - name = "appendix" - icon_state = "appendix" - zone = "groin" - slot = "appendix" - var/inflamed = 0 - -/obj/item/organ/appendix/update_icon() - if(inflamed) - icon_state = "appendixinflamed" - name = "inflamed appendix" - else - icon_state = "appendix" - name = "appendix" - -/obj/item/organ/appendix/Remove(mob/living/carbon/M, special = 0) - for(var/datum/disease/appendicitis/A in M.viruses) - A.cure() - inflamed = 1 - update_icon() - ..() - -/obj/item/organ/appendix/Insert(mob/living/carbon/M, special = 0) - ..() - if(inflamed) - M.AddDisease(new /datum/disease/appendicitis) - -/obj/item/organ/appendix/prepare_eat() - var/obj/S = ..() - if(inflamed) - S.reagents.add_reagent("bad_food", 5) - return S - /mob/living/proc/regenerate_organs() return 0 /mob/living/carbon/regenerate_organs() - CHECK_DNA_AND_SPECIES(src) + var/breathes = TRUE + var/blooded = TRUE + if(dna && dna.species) + if(NOBREATH in dna.species.species_traits) + breathes = FALSE + if(NOBLOOD in dna.species.species_traits) + blooded = FALSE - if(!(NOBREATH in dna.species.species_traits) && !getorganslot("lungs")) + if(breathes && !getorganslot("lungs")) var/obj/item/organ/lungs/L = new() L.Insert(src) - if(!(NOBLOOD in dna.species.species_traits) && !getorganslot("heart")) + if(blooded && !getorganslot("heart")) var/obj/item/organ/heart/H = new() H.Insert(src) if(!getorganslot("tongue")) var/obj/item/organ/tongue/T - for(var/tongue_type in dna.species.mutant_organs) - if(ispath(tongue_type, /obj/item/organ/tongue)) - T = new tongue_type() - T.Insert(src) + if(dna && dna.species) + for(var/tongue_type in dna.species.mutant_organs) + if(ispath(tongue_type, /obj/item/organ/tongue)) + T = new tongue_type() + T.Insert(src) // if they have no mutant tongues, give them a regular one if(!T) @@ -778,135 +146,11 @@ E = new() E.Insert(src) -//Eyes - -/obj/item/organ/eyes - name = "eyes" - icon_state = "eyeballs" - desc = "I see you!" - zone = "eyes" - slot = "eye_sight" - - var/sight_flags = 0 - var/see_in_dark = 2 - var/tint = 0 - var/eye_color = "" //set to a hex code to override a mob's eye color - var/old_eye_color = "fff" - var/flash_protect = 0 - var/see_invisible = SEE_INVISIBLE_LIVING - var/lighting_alpha - -/obj/item/organ/eyes/Insert(mob/living/carbon/M, special = 0) - ..() - if(ishuman(owner)) - var/mob/living/carbon/human/HMN = owner - old_eye_color = HMN.eye_color - if(eye_color) - HMN.eye_color = eye_color - HMN.regenerate_icons() + if(!getorganslot("ears")) + var/obj/item/organ/ears/ears + if(dna && dna.species && dna.species.mutantears) + ears = new dna.species.mutantears else - eye_color = HMN.eye_color - M.update_tint() - owner.update_sight() + ears = new -/obj/item/organ/eyes/Remove(mob/living/carbon/M, special = 0) - ..() - if(ishuman(M) && eye_color) - var/mob/living/carbon/human/HMN = M - HMN.eye_color = old_eye_color - HMN.regenerate_icons() - M.update_tint() - M.update_sight() - -/obj/item/organ/eyes/night_vision - name = "shadow eyes" - desc = "A spooky set of eyes that can see in the dark." - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - actions_types = list(/datum/action/item_action/organ_action/use) - var/night_vision = TRUE - -/obj/item/organ/eyes/night_vision/ui_action_click() - switch(lighting_alpha) - if (LIGHTING_PLANE_ALPHA_VISIBLE) - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - if (LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE) - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - if (LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE) - lighting_alpha = LIGHTING_PLANE_ALPHA_INVISIBLE - else - lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE - owner.update_sight() - -/obj/item/organ/eyes/night_vision/alien - name = "alien eyes" - desc = "It turned out they had them after all!" - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - sight_flags = SEE_MOBS - - -///Robotic - -/obj/item/organ/eyes/robotic - name = "robotic eyes" - icon_state = "cybernetic_eyeballs" - desc = "Your vision is augmented." - status = ORGAN_ROBOTIC - -/obj/item/organ/eyes/robotic/emp_act(severity) - if(!owner) - return - if(severity > 1) - if(prob(10 * severity)) - return - to_chat(owner, "Static obfuscates your vision!") - owner.flash_act(visual = 1) - -/obj/item/organ/eyes/robotic/xray - name = "X-ray eyes" - desc = "These cybernetic eyes will give you X-ray vision. Blinking is futile." - eye_color = "000" - see_in_dark = 8 - sight_flags = SEE_MOBS | SEE_OBJS | SEE_TURFS - -/obj/item/organ/eyes/robotic/thermals - name = "Thermals eyes" - desc = "These cybernetic eye implants will give you Thermal vision. Vertical slit pupil included." - eye_color = "FC0" - origin_tech = "materials=5;programming=4;biotech=4;magnets=4;syndicate=1" - sight_flags = SEE_MOBS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - flash_protect = -1 - see_in_dark = 8 - -/obj/item/organ/eyes/robotic/flashlight - name = "flashlight eyes" - desc = "It's two flashlights rigged together with some wire. Why would you put these in someones head?" - eye_color ="fee5a3" - icon = 'icons/obj/lighting.dmi' - icon_state = "flashlight_eyes" - flash_protect = 2 - tint = INFINITY - -/obj/item/organ/eyes/robotic/flashlight/emp_act(severity) - return - -/obj/item/organ/eyes/robotic/flashlight/Insert(var/mob/living/carbon/M, var/special = 0) - ..() - M.set_light(M.light_range + 15, M.light_power + 1) - - -/obj/item/organ/eyes/robotic/flashlight/Remove(var/mob/living/carbon/M, var/special = 0) - M.set_light(M.light_range -15, M.light_power - 1) - ..() - -// Welding shield implant -/obj/item/organ/eyes/robotic/shield - name = "shielded robotic eyes" - desc = "These reactive micro-shields will protect you from welders and flashes without obscuring your vision." - origin_tech = "materials=4;biotech=3;engineering=4;plasmatech=3" - flash_protect = 2 - -/obj/item/organ/eyes/robotic/shield/emp_act(severity) - return + ears.Insert(src) \ No newline at end of file diff --git a/code/modules/surgery/organs/tongue.dm b/code/modules/surgery/organs/tongue.dm new file mode 100644 index 0000000000..1ef36f3fe2 --- /dev/null +++ b/code/modules/surgery/organs/tongue.dm @@ -0,0 +1,200 @@ +/obj/item/organ/tongue + name = "tongue" + desc = "A fleshy muscle mostly used for lying." + icon_state = "tonguenormal" + zone = "mouth" + slot = "tongue" + attack_verb = list("licked", "slobbered", "slapped", "frenched", "tongued") + var/list/languages_possible + var/say_mod = null + var/taste_sensitivity = 15 // lower is more sensitive. + +/obj/item/organ/tongue/Initialize(mapload) + ..() + languages_possible = typecacheof(list( + /datum/language/common, + /datum/language/monkey, + /datum/language/ratvar + )) + +/obj/item/organ/tongue/get_spans() + return list() + +/obj/item/organ/tongue/proc/TongueSpeech(var/message) + return message + +/obj/item/organ/tongue/Insert(mob/living/carbon/M, special = 0) + ..() + if(say_mod && M.dna && M.dna.species) + M.dna.species.say_mod = say_mod + +/obj/item/organ/tongue/Remove(mob/living/carbon/M, special = 0) + ..() + if(say_mod && M.dna && M.dna.species) + M.dna.species.say_mod = initial(M.dna.species.say_mod) + +/obj/item/organ/tongue/can_speak_in_language(datum/language/dt) + . = is_type_in_typecache(dt, languages_possible) + +/obj/item/organ/tongue/lizard + name = "forked tongue" + desc = "A thin and long muscle typically found in reptilian races, apparently moonlights as a nose." + icon_state = "tonguelizard" + say_mod = "hisses" + taste_sensitivity = 10 // combined nose + tongue, extra sensitive + +/obj/item/organ/tongue/lizard/TongueSpeech(var/message) + var/regex/lizard_hiss = new("s+", "g") + var/regex/lizard_hiSS = new("S+", "g") + if(copytext(message, 1, 2) != "*") + message = lizard_hiss.Replace(message, "sss") + message = lizard_hiSS.Replace(message, "SSS") + return message + +/obj/item/organ/tongue/fly + name = "proboscis" + desc = "A freakish looking meat tube that apparently can take in liquids." + icon_state = "tonguefly" + say_mod = "buzzes" + taste_sensitivity = 25 // you eat vomit, this is a mercy + +/obj/item/organ/tongue/fly/TongueSpeech(var/message) + var/regex/fly_buzz = new("z+", "g") + var/regex/fly_buZZ = new("Z+", "g") + if(copytext(message, 1, 2) != "*") + message = fly_buzz.Replace(message, "zzz") + message = fly_buZZ.Replace(message, "ZZZ") + return message + +/obj/item/organ/tongue/abductor + name = "superlingual matrix" + desc = "A mysterious structure that allows for instant communication between users. Pretty impressive until you need to eat something." + icon_state = "tongueayylmao" + say_mod = "gibbers" + taste_sensitivity = 101 // ayys cannot taste anything. + +/obj/item/organ/tongue/abductor/TongueSpeech(var/message) + //Hacks + var/mob/living/carbon/human/user = usr + var/rendered = "[user.name]: [message]" + for(var/mob/living/carbon/human/H in GLOB.living_mob_list) + var/obj/item/organ/tongue/T = H.getorganslot("tongue") + if(!T || T.type != type) + continue + else if(H.dna && H.dna.species.id == "abductor" && user.dna && user.dna.species.id == "abductor") + var/datum/species/abductor/Ayy = user.dna.species + var/datum/species/abductor/Byy = H.dna.species + if(Ayy.team != Byy.team) + continue + to_chat(H, rendered) + for(var/mob/M in GLOB.dead_mob_list) + var/link = FOLLOW_LINK(M, user) + to_chat(M, "[link] [rendered]") + return "" + +/obj/item/organ/tongue/zombie + name = "rotting tongue" + desc = "Between the decay and the fact that it's just lying there you doubt a tongue has ever seemed less sexy." + icon_state = "tonguezombie" + say_mod = "moans" + taste_sensitivity = 32 + +/obj/item/organ/tongue/zombie/TongueSpeech(var/message) + var/list/message_list = splittext(message, " ") + var/maxchanges = max(round(message_list.len / 1.5), 2) + + for(var/i = rand(maxchanges / 2, maxchanges), i > 0, i--) + var/insertpos = rand(1, message_list.len - 1) + var/inserttext = message_list[insertpos] + + if(!(copytext(inserttext, length(inserttext) - 2) == "...")) + message_list[insertpos] = inserttext + "..." + + if(prob(20) && message_list.len > 3) + message_list.Insert(insertpos, "[pick("BRAINS", "Brains", "Braaaiinnnsss", "BRAAAIIINNSSS")]...") + + return jointext(message_list, " ") + +/obj/item/organ/tongue/alien + name = "alien tongue" + desc = "According to leading xenobiologists the evolutionary benefit of having a second mouth in your mouth is \"that it looks badass\"." + icon_state = "tonguexeno" + say_mod = "hisses" + taste_sensitivity = 10 // LIZARDS ARE ALIENS CONFIRMED + +/obj/item/organ/tongue/alien/Initialize(mapload) + ..() + languages_possible = typecacheof(list( + /datum/language/xenocommon, + /datum/language/common, + /datum/language/ratvar, + /datum/language/monkey)) + +/obj/item/organ/tongue/alien/TongueSpeech(var/message) + playsound(owner, "hiss", 25, 1, 1) + return message + +/obj/item/organ/tongue/bone + name = "bone \"tongue\"" + desc = "Apparently skeletons alter the sounds they produce \ + through oscillation of their teeth, hence their characteristic \ + rattling." + icon_state = "tonguebone" + say_mod = "rattles" + attack_verb = list("bitten", "chattered", "chomped", "enamelled", "boned") + taste_sensitivity = 101 // skeletons cannot taste anything + + var/chattering = FALSE + var/phomeme_type = "sans" + var/list/phomeme_types = list("sans", "papyrus") + +/obj/item/organ/tongue/bone/New() + . = ..() + phomeme_type = pick(phomeme_types) + +/obj/item/organ/tongue/bone/TongueSpeech(var/message) + . = message + + if(chattering) + //Annoy everyone nearby with your chattering. + chatter(message, phomeme_type, usr) + +/obj/item/organ/tongue/bone/get_spans() + . = ..() + // Feature, if the tongue talks directly, it will speak with its span + switch(phomeme_type) + if("sans") + . |= SPAN_SANS + if("papyrus") + . |= SPAN_PAPYRUS + +/obj/item/organ/tongue/bone/plasmaman + name = "plasma bone \"tongue\"" + desc = "Like animated skeletons, Plasmamen vibrate their teeth in order to produce speech." + icon_state = "tongueplasma" + +/obj/item/organ/tongue/bone/plasmaman/get_spans() + return + +/obj/item/organ/tongue/robot + name = "robotic voicebox" + desc = "A voice synthesizer that can interface with organic lifeforms." + status = ORGAN_ROBOTIC + icon_state = "tonguerobot" + say_mod = "states" + attack_verb = list("beeped", "booped") + taste_sensitivity = 25 // not as good as an organic tongue + +/obj/item/organ/tongue/robot/Initialize(mapload) + ..() + languages_possible = typecacheof(list( + /datum/language/xenocommon, + /datum/language/common, + /datum/language/ratvar, + /datum/language/monkey, + /datum/language/drone, + /datum/language/machine, + /datum/language/swarmer)) + +/obj/item/organ/tongue/robot/get_spans() + return ..() | SPAN_ROBOT diff --git a/code/modules/surgery/organs/vocal_cords.dm b/code/modules/surgery/organs/vocal_cords.dm index e844918eeb..2413a45870 100644 --- a/code/modules/surgery/organs/vocal_cords.dm +++ b/code/modules/surgery/organs/vocal_cords.dm @@ -107,7 +107,7 @@ message = lowertext(message) var/mob/living/list/listeners = list() for(var/mob/living/L in get_hearers_in_view(8, user)) - if(!L.ear_deaf && !L.null_rod_check() && L != user && L.stat != DEAD) + if(L.can_hear() && !L.null_rod_check() && L != user && L.stat != DEAD) if(ishuman(L)) var/mob/living/carbon/human/H = L if(istype(H.ears, /obj/item/clothing/ears/earmuffs)) diff --git a/icons/obj/surgery.dmi b/icons/obj/surgery.dmi index d1adc2919618d53772c33f0547247ddbac01e200..19d6275d785108cfeac96ebff99ccaeeddd9bbd8 100644 GIT binary patch delta 9915 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

-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 diff --git a/tgstation.dme b/tgstation.dme index 4eba97eef4..1647912f6c 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -2093,14 +2093,20 @@ #include "code\modules\surgery\bodyparts\head.dm" #include "code\modules\surgery\bodyparts\helpers.dm" #include "code\modules\surgery\bodyparts\robot_bodyparts.dm" +#include "code\modules\surgery\organs\appendix.dm" #include "code\modules\surgery\organs\augments_arms.dm" #include "code\modules\surgery\organs\augments_chest.dm" #include "code\modules\surgery\organs\augments_eyes.dm" #include "code\modules\surgery\organs\augments_internal.dm" #include "code\modules\surgery\organs\autoimplanter.dm" #include "code\modules\surgery\organs\autosurgeon.dm" +#include "code\modules\surgery\organs\ears.dm" +#include "code\modules\surgery\organs\eyes.dm" +#include "code\modules\surgery\organs\heart.dm" #include "code\modules\surgery\organs\helpers.dm" +#include "code\modules\surgery\organs\lungs.dm" #include "code\modules\surgery\organs\organ_internal.dm" +#include "code\modules\surgery\organs\tongue.dm" #include "code\modules\surgery\organs\vocal_cords.dm" #include "code\modules\telesci\telepad.dm" #include "code\modules\telesci\telesci_computer.dm"