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 d1adc29196..19d6275d78 100644 Binary files a/icons/obj/surgery.dmi and b/icons/obj/surgery.dmi differ 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"