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"