diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm
index 260df783bf9..d184f765eac 100644
--- a/code/_onclick/hud/screen_objects.dm
+++ b/code/_onclick/hud/screen_objects.dm
@@ -108,11 +108,12 @@
icon_state = "internal0"
else
var/no_mask = FALSE
- if(!C.wear_mask || !(C.wear_mask.flags & AIRTIGHT))
- if(ishuman(C))
- var/mob/living/carbon/human/H = C
- if(!H.head || !(H.head.flags & AIRTIGHT))
- no_mask = TRUE
+ if(!C.get_organ_slot("breathing_tube"))
+ if(!C.wear_mask || !(C.wear_mask.flags & AIRTIGHT))
+ if(ishuman(C))
+ var/mob/living/carbon/human/H = C
+ if(!H.head || !(H.head.flags & AIRTIGHT))
+ no_mask = TRUE
if(no_mask)
to_chat(C, "You are not wearing a suitable mask or helmet.")
diff --git a/code/game/objects/items/weapons/tanks/tanks.dm b/code/game/objects/items/weapons/tanks/tanks.dm
index 3b99936b0a8..27cbff53547 100644
--- a/code/game/objects/items/weapons/tanks/tanks.dm
+++ b/code/game/objects/items/weapons/tanks/tanks.dm
@@ -54,7 +54,9 @@
C.update_internals_hud_icon(0)
else
var/can_open_valve = 0
- if(C.wear_mask && C.wear_mask.flags & AIRTIGHT)
+ if(C.get_organ_slot("breathing_tube"))
+ can_open_valve = 1
+ else if(C.wear_mask && C.wear_mask.flags & AIRTIGHT)
can_open_valve = 1
else if(ishuman(C))
var/mob/living/carbon/human/H = C
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index 3d11f40b1c9..596259b47da 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -332,7 +332,7 @@ BLIND // can't see anything
mask_adjusted = 1
if(adjusted_flags)
slot_flags = adjusted_flags
- if(ishuman(user) && H.internal && user.wear_mask == src) /*If the user was wearing the mask providing internals on their face at the time it was adjusted, turn off internals.
+ if(ishuman(user) && H.internal && !H.get_organ_slot("breathing_tube") && user.wear_mask == src) /*If the user was wearing the mask providing internals on their face at the time it was adjusted, turn off internals.
Otherwise, they adjusted it while it was in their hands or some such so we won't be needing to turn off internals.*/
H.update_internals_hud_icon(0)
H.internal = null
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 1407fba6998..8caecdb42d7 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -650,9 +650,10 @@ var/list/ventcrawl_machinery = list(/obj/machinery/atmospherics/unary/vent_pump,
"[usr] tries to [internal ? "close" : "open"] the valve on [src]'s [ITEM].")
var/no_mask
- if(!(wear_mask && wear_mask.flags & AIRTIGHT))
- if(!(head && head.flags & AIRTIGHT))
- no_mask = 1
+ if(!get_organ_slot("breathing_tube"))
+ if(!(wear_mask && wear_mask.flags & AIRTIGHT))
+ if(!(head && head.flags & AIRTIGHT))
+ no_mask = 1
if(no_mask)
to_chat(usr, "[src] is not wearing a suitable mask or helmet!")
return
@@ -663,9 +664,10 @@ var/list/ventcrawl_machinery = list(/obj/machinery/atmospherics/unary/vent_pump,
update_internals_hud_icon(0)
else
var/no_mask2
- if(!(wear_mask && wear_mask.flags & AIRTIGHT))
- if(!(head && head.flags & AIRTIGHT))
- no_mask2 = 1
+ if(!get_organ_slot("breathing_tube"))
+ if(!(wear_mask && wear_mask.flags & AIRTIGHT))
+ if(!(head && head.flags & AIRTIGHT))
+ no_mask2 = 1
if(no_mask2)
to_chat(usr, "[src] is not wearing a suitable mask or helmet!")
return
diff --git a/code/modules/mob/living/carbon/carbon_defines.dm b/code/modules/mob/living/carbon/carbon_defines.dm
index 6e34291caae..ab7a5f1d5a9 100644
--- a/code/modules/mob/living/carbon/carbon_defines.dm
+++ b/code/modules/mob/living/carbon/carbon_defines.dm
@@ -3,6 +3,7 @@
hud_possible = list(HEALTH_HUD,STATUS_HUD,SPECIALROLE_HUD)
var/list/stomach_contents = list()
var/list/internal_organs = list()
+ var/list/internal_organs_slot = list() //Same as above, but stores "slot ID" - "organ" pairs for easy access.
var/antibodies = 0
var/life_tick = 0 // The amount of life ticks that have processed on this mob.
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index ba55801c522..2fec182504a 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -428,7 +428,7 @@
/mob/living/carbon/human/show_inv(mob/user)
user.set_machine(src)
- var/has_breathable_mask = istype(wear_mask, /obj/item/clothing/mask)
+ var/has_breathable_mask = istype(wear_mask, /obj/item/clothing/mask) || get_organ_slot("breathing_tube")
var/list/obscured = check_obscured_slots()
var/dat = {"
diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm
index 8e1c32f931a..6edc2f0ae75 100644
--- a/code/modules/mob/living/carbon/human/inventory.dm
+++ b/code/modules/mob/living/carbon/human/inventory.dm
@@ -151,7 +151,7 @@
update_hair() //rebuild hair
update_fhair()
update_head_accessory()
- if(internal)
+ if(internal && !get_organ_slot("breathing_tube"))
internal = null
update_internals_hud_icon(0)
wear_mask_update(I, toggle_off = FALSE)
diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm
index f51ba71bff8..5a67b1c09a2 100644
--- a/code/modules/mob/living/carbon/human/life.dm
+++ b/code/modules/mob/living/carbon/human/life.dm
@@ -301,9 +301,10 @@
var/null_internals = 0 //internals are invalid, therefore turn them off
var/skip_contents_check = 0 //rigsuit snowflake, oxygen tanks aren't stored inside the mob, so the 'contents.Find' check has to be skipped.
- if(!(wear_mask && wear_mask.flags & AIRTIGHT)) //if NOT (wear_mask AND wear_mask.flags CONTAIN AIRTIGHT)
- if(!(head && head.flags & AIRTIGHT)) //if NOT (head AND head.flags CONTAIN AIRTIGHT)
- null_internals = 1 //not wearing a mask or suitable helmet
+ if(!get_organ_slot("breathing_tube"))
+ if(!(wear_mask && wear_mask.flags & AIRTIGHT)) //if NOT (wear_mask AND wear_mask.flags CONTAIN AIRTIGHT)
+ if(!(head && head.flags & AIRTIGHT)) //if NOT (head AND head.flags CONTAIN AIRTIGHT)
+ null_internals = 1 //not wearing a mask or suitable helmet
if(istype(back, /obj/item/weapon/rig)) //wearing a rigsuit
var/obj/item/weapon/rig/rig = back //needs to be typecasted because this doesn't use get_rig() for some reason
diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm
index da764cafda3..cbfc9ddbfdf 100644
--- a/code/modules/mob/living/carbon/life.dm
+++ b/code/modules/mob/living/carbon/life.dm
@@ -183,9 +183,10 @@
if(internal)
if(internal.loc != src)
internal = null
- if(!wear_mask || !(wear_mask.flags & AIRTIGHT)) //not wearing mask or non-breath mask
- if(!head || !(head.flags & AIRTIGHT)) //not wearing helmet or non-breath helmet
- internal = null //turn off internals
+ if(!get_organ_slot("breathing_tube"))
+ if(!wear_mask || !(wear_mask.flags & AIRTIGHT)) //not wearing mask or non-breath mask
+ if(!head || !(head.flags & AIRTIGHT)) //not wearing helmet or non-breath helmet
+ internal = null //turn off internals
if(internal)
update_internals_hud_icon(1)
diff --git a/code/modules/mob/mob_grab.dm b/code/modules/mob/mob_grab.dm
index ecc987530ab..4b41b5084c6 100644
--- a/code/modules/mob/mob_grab.dm
+++ b/code/modules/mob/mob_grab.dm
@@ -167,9 +167,11 @@
*/
+ var/breathing_tube = affecting.get_organ_slot("breathing_tube")
+
if(state >= GRAB_NECK)
affecting.Stun(5) //It will hamper your voice, being choked and all.
- if(isliving(affecting))
+ if(isliving(affecting) && !breathing_tube)
var/mob/living/L = affecting
L.adjustOxyLoss(1)
@@ -177,7 +179,8 @@
//affecting.apply_effect(STUTTER, 5) //would do this, but affecting isn't declared as mob/living for some stupid reason.
affecting.Stuttering(5) //It will hamper your voice, being choked and all.
affecting.Weaken(5) //Should keep you down unless you get help.
- affecting.AdjustLoseBreath(2, bound_lower = 0, bound_upper = 3)
+ if(!breathing_tube)
+ affecting.AdjustLoseBreath(2, bound_lower = 0, bound_upper = 3)
adjust_position()
@@ -290,7 +293,8 @@
msg_admin_attack("[key_name(assailant)] strangled (kill intent) [key_name(affecting)]")
assailant.next_move = world.time + 10
- affecting.AdjustLoseBreath(1)
+ if(!affecting.get_organ_slot("breathing_tube"))
+ affecting.AdjustLoseBreath(1)
affecting.setDir(WEST)
adjust_position()
diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm
index 66c150f636d..601ad98346a 100644
--- a/code/modules/research/designs/medical_designs.dm
+++ b/code/modules/research/designs/medical_designs.dm
@@ -260,6 +260,17 @@
build_path = /obj/item/organ/internal/cyberimp/eyes/shield
category = list("Misc", "Medical")
+/datum/design/cyberimp_breather
+ name = "Breathing Tube Implant"
+ desc = "This simple implant adds an internals connector to your back, allowing you to use internals without a mask and protecting you from being choked."
+ id = "ci-breather"
+ req_tech = list("materials" = 2, "biotech" = 3)
+ build_type = PROTOLATHE | MECHFAB
+ construction_time = 35
+ materials = list(MAT_METAL = 600, MAT_GLASS = 250)
+ build_path = /obj/item/organ/internal/cyberimp/mouth/breathing_tube
+ category = list("Misc", "Medical Designs")
+
/datum/design/cyberimp_medical_hud
name = "Medical HUD implant"
desc = "These cybernetic eyes will display a medical HUD over everything you see. Wiggle eyes to control."
diff --git a/code/modules/surgery/organs/augments_internal.dm b/code/modules/surgery/organs/augments_internal.dm
index 4a7aa2554fc..13061db2c4d 100644
--- a/code/modules/surgery/organs/augments_internal.dm
+++ b/code/modules/surgery/organs/augments_internal.dm
@@ -144,6 +144,24 @@
spawn(90 / severity)
crit_fail = 0
+
+//[[[[MOUTH]]]]
+/obj/item/organ/internal/cyberimp/mouth
+ parent_organ = "mouth"
+
+/obj/item/organ/internal/cyberimp/mouth/breathing_tube
+ name = "breathing tube implant"
+ desc = "This simple implant adds an internals connector to your back, allowing you to use internals without a mask and protecting you from being choked."
+ icon_state = "implant_mask"
+ slot = "breathing_tube"
+ w_class = WEIGHT_CLASS_TINY
+ origin_tech = "materials=2;biotech=3"
+
+/obj/item/organ/internal/cyberimp/mouth/breathing_tube/emp_act(severity)
+ if(prob(60/severity) && owner)
+ to_chat(owner, "Your breathing tube suddenly closes!")
+ owner.AdjustLoseBreath(2)
+
/obj/item/organ/internal/cyberimp/brain/clown_voice
name = "Comical implant"
desc = "Uh oh."
diff --git a/code/modules/surgery/organs/helpers.dm b/code/modules/surgery/organs/helpers.dm
index 94b0de5e066..61ed78d0e3b 100644
--- a/code/modules/surgery/organs/helpers.dm
+++ b/code/modules/surgery/organs/helpers.dm
@@ -30,9 +30,7 @@
return returnorg
/mob/living/carbon/get_organ_slot(slot)
- for(var/obj/item/organ/internal/O in internal_organs)
- if(slot == O.slot)
- return O
+ return internal_organs_slot[slot]
/mob/living/carbon/get_int_organ_tag(tag)
for(var/obj/item/organ/internal/O in internal_organs)
diff --git a/code/modules/surgery/organs/organ_internal.dm b/code/modules/surgery/organs/organ_internal.dm
index 071d579b0c0..47535c044eb 100644
--- a/code/modules/surgery/organs/organ_internal.dm
+++ b/code/modules/surgery/organs/organ_internal.dm
@@ -7,6 +7,7 @@
throwforce = 0
var/zone = "chest"
var/slot
+ // DO NOT add slots with matching names to different zones - it will break internal_organs_slot list!
vital = 0
var/non_primary = 0
@@ -29,6 +30,7 @@
owner = M
M.internal_organs |= src
+ M.internal_organs_slot[slot] = src
var/obj/item/organ/external/parent
if(istype(M, /mob/living/carbon/human))
var/mob/living/carbon/human/H = M
@@ -52,6 +54,8 @@
owner = null
if(M)
M.internal_organs -= src
+ if(M.internal_organs_slot[slot] == src)
+ M.internal_organs_slot.Remove(slot)
if(vital && !special)
if(M.stat != DEAD)//safety check!
M.death()
diff --git a/icons/obj/surgery.dmi b/icons/obj/surgery.dmi
index ac42a7d538c..2d1bb9ac749 100644
Binary files a/icons/obj/surgery.dmi and b/icons/obj/surgery.dmi differ