From f993f9ce367819f29efb46a415e1e67fe2c27dcb Mon Sep 17 00:00:00 2001 From: coiax Date: Sat, 22 Apr 2017 13:51:03 +0100 Subject: [PATCH] Refactors ear damage into ear organs (#26044) * Refactors ear damage into ear organs :cl: coiax add: Centcom would like to inform all employees that they have ears. add: Adds "ear" organs to all carbons. These organs store ear damage and deafness. A carbon without any ears is deaf. Genetic deafness functions as before. /:cl: - `ear_damage` and `ear_deaf` vars removed from /mob. - All mobs have a `can_hear` proc, which returns TRUE always except for carbons. - Carbons need to have an ear organ that has 0 `deaf` var. - Explanation of how ear damage works is in the code, it hasn't been changed from previously. Deafness is applied in number of Life ticks until you regain hearing, while damage is long team, heals slower, and when high enough, can cause flashbangs to make you go permamently deaf, as before. - Wearing earmuffs halves the healing time of deafness, and promotes healing long term ear damage, as before. Earmuffs now have a secondary flag HEALS_EARS, which currently only they own. * Changes how soundbang deafness works slightly * Ear organ icon * Code review I * Makes fully healing carbons not dependent on having a dna and species * Gives monkeys and aliens ears * Whoops * Split organs into seperate files * Tweaks. * Un-removes brain damage lines * Moved procs onto /mob for ear stuff * Massages things into compiling * Replacement of spam_flag with world.time tracker --- 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 +- .../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 | 20 +- .../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 | 788 +----------------- code/modules/surgery/organs/tongue.dm | 192 +++++ code/modules/surgery/organs/vocal_cords.dm | 2 +- icons/obj/surgery.dmi | Bin 28021 -> 28574 bytes tgstation.dme | 6 + 39 files changed, 922 insertions(+), 904 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 56f00b4e23a..ac40dc75590 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 5b0bdbc11e2..fb11e17b9d0 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 46d71dc0e29..c62a59ed77e 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 b6729175a99..92269ce8db2 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 536184c766d..321d6691b3a 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 8b267247617..d94be6f4db7 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 0ce77211f59..c6e6534667b 100644 --- a/code/game/gamemodes/miniantags/borer/borer.dm +++ b/code/game/gamemodes/miniantags/borer/borer.dm @@ -764,7 +764,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/objects/items/weapons/clown_items.dm b/code/game/objects/items/weapons/clown_items.dm index 150435bdafa..0a4ef23d9fe 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 8d0c20d63d7..2e716194132 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 ede686f7661..9a21f3c4a4e 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 caf2bfe22c4..4c40973c206 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 292aeb54751..cc4ebcda5f0 100644 --- a/code/modules/mob/living/carbon/alien/alien.dm +++ b/code/modules/mob/living/carbon/alien/alien.dm @@ -47,6 +47,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 @@ -146,6 +147,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 1cccfa20d58..b040c59d0c9 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -720,6 +720,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 2e1bfd27bf8..7963273e6db 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -275,20 +275,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 @@ -308,3 +312,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 7b604bc3699..e2aa7535d2d 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -58,6 +58,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 ..() diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 41fd173f83f..8a3360911ed 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -66,18 +66,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 4bc00091aca..efee3652574 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -17,10 +17,10 @@ var/default_color = "#FFF" // if alien colors are disabled, this is the color that will be used by that race var/sexes = 1 // whether or not the race has sexual characteristics. at the moment this is only 0 for skeletons and shadows - + var/face_y_offset = 0 var/hair_y_offset = 0 - + var/hair_color = null // this allows races to have specific hair colors... if null, it uses the H's hair/facial hair colors. if "mutcolor", it uses the H's mutant_color var/hair_alpha = 255 // the alpha used by the hair. 255 is completely solid, 0 is transparent. @@ -69,9 +69,13 @@ //Eyes var/obj/item/organ/eyes/mutanteyes = /obj/item/organ/eyes - /////////// - // PROCS // - /////////// + + //Ears + var/obj/item/organ/ears/mutantears = /obj/item/organ/ears + +/////////// +// PROCS // +/////////// /datum/species/New() @@ -122,6 +126,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) @@ -139,6 +144,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 920a0cade3b..152f708ab57 100644 --- a/code/modules/mob/living/carbon/monkey/monkey.dm +++ b/code/modules/mob/living/carbon/monkey/monkey.dm @@ -44,6 +44,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() @@ -133,15 +134,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 944f5c6a9cd..bcde1ff39a7 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -123,14 +123,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 d77c3fff863..b06223a7cbf 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 15216278cf5..fe3a24cef05 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 c14e6c254f9..3947cb020fe 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 fd90f825a3e..62a262f9e60 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 f26fd04e592..9fe84435063 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 5d0d7bac1d4..34e977acfc4 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -69,7 +69,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 @@ -958,10 +958,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") @@ -999,4 +995,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 193ea82948e..20d98027c7d 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 f64be70a8db..3942014fb9f 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -489,3 +489,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 caa5a38326e..f12e7d6d4e5 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 29ca2df9316..dee5b7e68ad 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 00000000000..ad51c48d859 --- /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 00000000000..c35cf5c6706 --- /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 00000000000..672ac924adb --- /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 00000000000..9ffe8b6d064 --- /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 00000000000..ece2ad3f402 --- /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 ac306ee7c4b..5f8f6609d70 100644 --- a/code/modules/surgery/organs/organ_internal.dm +++ b/code/modules/surgery/organs/organ_internal.dm @@ -100,658 +100,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/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) @@ -768,135 +144,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) diff --git a/code/modules/surgery/organs/tongue.dm b/code/modules/surgery/organs/tongue.dm new file mode 100644 index 00000000000..4c6ce0ab0e4 --- /dev/null +++ b/code/modules/surgery/organs/tongue.dm @@ -0,0 +1,192 @@ +/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/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 e844918eeba..2413a45870b 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..9532ecbcccd14d7cf7cd4e0e714b4211a04b735d 100644 GIT binary patch delta 8681 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 diff --git a/tgstation.dme b/tgstation.dme index c229ef57a0a..001cfdb6415 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -2034,13 +2034,19 @@ #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\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"