diff --git a/code/__DEFINES/organ_defines.dm b/code/__DEFINES/organ_defines.dm
index 34e97a90c74..e6e58f30cfc 100644
--- a/code/__DEFINES/organ_defines.dm
+++ b/code/__DEFINES/organ_defines.dm
@@ -33,6 +33,7 @@
#define INTERNAL_ORGAN_EYE_SHIELD_DEVICE "eye_shield"
#define INTERNAL_ORGAN_EYE_LING "eye_ling"
#define INTERNAL_ORGAN_BREATHING_TUBE "breathing_tube"
+#define INTERNAL_ORGAN_SPEECH_TRANSLATOR "voice_translator"
#define INTERNAL_ORGAN_STOMACH "stomach"
#define INTERNAL_ORGAN_HEART_DRIVE "heartdrive"
#define INTERNAL_ORGAN_BRAIN_ANTIDROP "brain_antidrop"
diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm
index f6eefaa8686..c87f659e89b 100644
--- a/code/__DEFINES/traits/declarations.dm
+++ b/code/__DEFINES/traits/declarations.dm
@@ -268,3 +268,12 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_SPECIES_LIMBS "only_species_limbs"
/// Phohibits using the "Book Of Babel"
#define TRAIT_NO_BABEL "cannot_use_babel"
+/// Improves the function of some cyberimps for the Grey species
+/// Rename and split into several if you want to make a different functionality to another species/etc
+#define TRAIT_ADVANCED_CYBERIMPLANTS "advanced_cyberimplants"
+/// Any movement of non-item objects or mobs expends stamina (10 run, 5 walk)
+#define TRAIT_WEAK_PULLING "weak_pulling"
+/// Makes species acid proof(not it's items), affects: acetic, sulfiric, fluorosulfuric acids
+#define TRAIT_ACID_PROTECTED "acid_protected"
+/// Species with no vocal cords can't speak without translator
+#define TRAIT_NO_VOCAL_CORDS "no_vocal_cords"
diff --git a/code/datums/action.dm b/code/datums/action.dm
index 415da08fa9f..51b1c8a887f 100644
--- a/code/datums/action.dm
+++ b/code/datums/action.dm
@@ -508,15 +508,20 @@
name = "Toggle Research Scanner"
/datum/action/item_action/toggle_research_scanner/Trigger(left_click = TRUE)
- if(IsAvailable())
- owner.research_scanner = !owner.research_scanner
- to_chat(owner, "Research analyzer is now [owner.research_scanner ? "active" : "deactivated"].")
- return TRUE
+ if(!IsAvailable())
+ return
+
+ owner.research_scanner = !owner.research_scanner
+ to_chat(owner, span_notice("Вы [owner.research_scanner ? "включили" : "отключили"] исследовательский анализатор."))
+
+ return TRUE
+
/datum/action/item_action/toggle_research_scanner/Remove(mob/living/L)
if(owner)
owner.research_scanner = 0
- ..()
+
+ . = ..()
/datum/action/item_action/toggle_research_scanner/ApplyIcon()
@@ -740,15 +745,20 @@
name = "Toggle Research Scanner"
/datum/action/innate/research_scanner/Trigger(left_click = TRUE)
- if(IsAvailable())
- owner.research_scanner = !owner.research_scanner
- to_chat(owner, "Research analyzer is now [owner.research_scanner ? "active" : "deactivated"].")
- return TRUE
+ if(!IsAvailable())
+ return
+
+ owner.research_scanner = !owner.research_scanner
+ to_chat(owner, span_notice("Вы [owner.research_scanner ? "включили" : "отключили"] исследовательский анализатор."))
+
+ return TRUE
+
/datum/action/innate/research_scanner/Remove(mob/living/L)
if(owner)
owner.research_scanner = 0
- ..()
+
+ . = ..()
/datum/action/innate/research_scanner/ApplyIcon()
diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm
index e651a83157c..25ec54c14d3 100644
--- a/code/game/machinery/machinery.dm
+++ b/code/game/machinery/machinery.dm
@@ -541,7 +541,9 @@ Class Procs:
. += span_notice("It appears heavily damaged.")
if(0 to 25)
. += span_warning("It's falling apart!")
- if(user.research_scanner && component_parts)
+
+ var/obj/item/organ/internal/brain/mobs_brain = user.get_organ_slot(INTERNAL_ORGAN_BRAIN)
+ if((user.research_scanner || mobs_brain?.smart_mind) && component_parts)
. += display_parts(user)
/obj/machinery/proc/on_assess_perp(mob/living/carbon/human/perp)
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 25b478b26a2..dfefbdf1b58 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -292,7 +292,8 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/g
. = ..(user, "", "It is a [size] item.")
- if(user.research_scanner) //Mob has a research scanner active.
+ var/obj/item/organ/internal/brain/mobs_brain = user.get_organ_slot(INTERNAL_ORGAN_BRAIN)
+ if(user.research_scanner || mobs_brain?.smart_mind) //Mob has a research scanner active.
var/msg = "*--------*
"
if(origin_tech)
diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm
index 45a1bd4ad9f..1fbc8d185ac 100644
--- a/code/game/objects/items/devices/radio/radio.dm
+++ b/code/game/objects/items/devices/radio/radio.dm
@@ -382,9 +382,13 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
return 0
if(M.is_muzzled())
+ var/obj/item/organ/internal/cyberimp/mouth/translator/translator = M.get_organ_slot(INTERNAL_ORGAN_SPEECH_TRANSLATOR)
+ if(translator) // you can't speak in radio with translator and gag
+ return FALSE
+
var/obj/item/clothing/mask/muzzle/muzzle = M.wear_mask
if(muzzle.radio_mute)
- return 0
+ return FALSE
var/jammed = FALSE
var/turf/position = get_turf(src)
diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm
index fd275dccef0..ede217d126e 100644
--- a/code/game/objects/structures/watercloset.dm
+++ b/code/game/objects/structures/watercloset.dm
@@ -587,6 +587,17 @@
user.visible_message("[user] washes [user.p_their()] [washing_face ? "face" : "hands"] using [src].", \
"You wash your [washing_face ? "face" : "hands"] using [src].")
+
+ if(isgrey(user)) // no water??
+ var/mob/living/carbon/human/H = user
+ var/grey_message = pick("Вы не ожидали, что в раковине окажется вода!", "Вы слишком поздно понимаете, что совершили ошибку!", "Вы чувствуете адскую боль по всему телу!")
+ H.adjustFireLoss(30 * H.get_permeability_protection())
+ to_chat(H, span_danger("[grey_message]"))
+ if(H.has_pain())
+ H.emote("scream")
+
+ return
+
if(washing_face)
if(ishuman(user))
var/mob/living/carbon/human/H = user
diff --git a/code/modules/antagonists/traitor/contractor/datums/syndicate_contract.dm b/code/modules/antagonists/traitor/contractor/datums/syndicate_contract.dm
index dce9b5007c5..6096f363fc7 100644
--- a/code/modules/antagonists/traitor/contractor/datums/syndicate_contract.dm
+++ b/code/modules/antagonists/traitor/contractor/datums/syndicate_contract.dm
@@ -368,7 +368,7 @@
// Cybernetic implants get removed first (to deal with NODROP stuff)
for(var/obj/item/organ/internal/cyberimp/I in H.internal_organs)
// Greys get to keep their implant
- if(isgrey(H) && istype(I, /obj/item/organ/internal/cyberimp/brain/speech_translator))
+ if(istype(I, /obj/item/organ/internal/cyberimp/mouth/translator/grey_retraslator))
continue
// Try removing it
I = I.remove(H)
diff --git a/code/modules/client/preference/loadout/loadout_racial.dm b/code/modules/client/preference/loadout/loadout_racial.dm
index 9bfa31cf463..384ab42ab73 100644
--- a/code/modules/client/preference/loadout/loadout_racial.dm
+++ b/code/modules/client/preference/loadout/loadout_racial.dm
@@ -28,6 +28,8 @@
return "\[Species: [english_list(whitelisted_species)]\] "
+ // TAJARAN //
+
/datum/gear/racial/taj
display_name = "embroidered veil"
description = "A common traditional nano-fiber veil worn by many Tajaran, It is rare and offensive to see it on other races."
@@ -95,3 +97,25 @@
allowed_roles = list(JOB_TITLE_HOP, JOB_TITLE_CAPTAIN)
+// GREY //
+
+/datum/gear/racial/language_chip
+ display_name = "selected language chip"
+ description = "Крошечный чип-переводчик с индикатором, содержащий в себе один из языков. Разработан греями, устанавливается в импланты-переводчики."
+ path = /obj/item/translator_chip/sol
+ whitelisted_species = list(SPECIES_GREY)
+
+
+/datum/gear/racial/language_chip/New()
+ . = ..()
+
+ var/list/available_chips = list()
+ for(var/path in subtypesof(/obj/item/translator_chip))
+ var/obj/item/translator_chip/chip = path
+ if(chip.stored_language == TRAIT_WINGDINGS) // you can select it in the prefs, so no need to double
+ continue
+
+ available_chips[chip.stored_language_rus] = chip
+
+ gear_tweaks += new /datum/gear_tweak/path(available_chips, src)
+
diff --git a/code/modules/client/preference/preferences.dm b/code/modules/client/preference/preferences.dm
index 473d6d5f907..51739c9e680 100644
--- a/code/modules/client/preference/preferences.dm
+++ b/code/modules/client/preference/preferences.dm
@@ -357,13 +357,14 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
if(SPECIES_VOX)
dat += "N2 Tank: [speciesprefs ? "Large N2 Tank" : "Specialized N2 Tank"]
"
if(SPECIES_GREY)
- dat += "Wingdings: Set in disabilities
"
- dat += "Voice Translator: [speciesprefs ? "Yes" : "No"]
"
+ dat += "Wingdings: [disabilities & DISABILITY_FLAG_WINGDINGS ? "Yes" : "No"]
"
+ dat += "Install Wingdings Decoder: [speciesprefs ? "Yes" : "No"]
"
if(SPECIES_MACNINEPERSON)
dat += "Synthetic Shell: Selections
"
if(SPECIES_WRYN)
dat += "Comb Deafness: [speciesprefs ? "Yes" : "No"]
"
- dat += "Secondary Language: [language]
"
+ if(species != SPECIES_GREY)
+ dat += "Secondary Language: [language]
"
if(S.autohiss_basic_map)
dat += "Auto-accent: [autohiss_mode == AUTOHISS_FULL ? "Full" : (autohiss_mode == AUTOHISS_BASIC ? "Basic" : "Off")]
"
dat += "Blood Type: [b_type]
"
@@ -1710,6 +1711,10 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
autohiss_mode = AUTOHISS_OFF
if("speciesprefs")
speciesprefs = !speciesprefs //Starts 0, so if someone clicks the button up top there, this won't be 0 anymore. If they click it again, it'll go back to 0.
+ if("toggle_wingdings")
+ var/dflag = text2num(DISABILITY_FLAG_WINGDINGS)
+ if(dflag >= 0)
+ disabilities ^= text2num(DISABILITY_FLAG_WINGDINGS)
if("language")
// var/languages_available
var/list/new_languages = list("None")
diff --git a/code/modules/mob/living/carbon/brain/brain_item.dm b/code/modules/mob/living/carbon/brain/brain_item.dm
index 18ac00640da..abf143ff855 100644
--- a/code/modules/mob/living/carbon/brain/brain_item.dm
+++ b/code/modules/mob/living/carbon/brain/brain_item.dm
@@ -18,6 +18,8 @@
var/mmi_icon_state = "mmi_full"
/// If it's a fake brain without a mob assigned that should still be treated like a real brain.
var/decoy_brain = FALSE
+ /// TRUE giving to a user sci hud and active research scanner
+ var/smart_mind = FALSE
/obj/item/organ/internal/brain/xeno
name = "xenomorph brain"
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index 1cfac94b198..79e4b2425e4 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -473,6 +473,10 @@
if(CIH?.examine_extensions)
have_hud_exam |= CIH.examine_extensions
+ var/obj/item/organ/internal/brain/mobs_brain = H.get_organ_slot(INTERNAL_ORGAN_BRAIN)
+ if(mobs_brain?.smart_mind)
+ have_hud_exam |= EXAMINE_HUD_SCIENCE
+
return (have_hud_exam & hud_exam)
else if(isrobot(M) || isAI(M)) //Stand-in/Stopgap to prevent pAIs from freely altering records, pending a more advanced Records system
diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm
index 62a0c337ef5..f65ab36084f 100644
--- a/code/modules/mob/living/carbon/human/human_movement.dm
+++ b/code/modules/mob/living/carbon/human/human_movement.dm
@@ -1,3 +1,9 @@
+#define PULL_STAMINADAM_WALK 4
+#define PULL_STAMINADAM_RUN 6
+#define PUSH_STAMINADAM_WALK 3
+#define PUSH_STAMINADAM_RUN 4
+
+
/mob/living/carbon/human/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE)
. = ..()
if(!forced && (!old_loc || !old_loc.has_gravity()) && has_gravity())
@@ -29,6 +35,7 @@
if(.) // did we actually move?
if(body_position != LYING_DOWN && !buckled && !throwing)
update_splints()
+
var/break_bones_chance = get_bones_symptom_prob()
if(break_bones_chance && (m_intent == MOVE_INTENT_RUN || pulling))
if(prob(break_bones_chance))
@@ -42,6 +49,40 @@
else if(prob(30))
playsound(src, "bonebreak", 10, TRUE)
+ // If we sooo weak to pull or push something, except items or tiny mobs, get stamina damage
+ var/weak_mob = FALSE
+ if((pulling || now_pushing) && (TRAIT_WEAK_PULLING in dna?.species.inherent_traits))
+ weak_mob = TRUE
+
+ if(weak_mob)
+ var/stamina_damage = 0
+ var/small_pushed = TRUE
+ // Handle pulling all non /obj/item stuff or tiny mobs
+ if(pulling && istype(pulling, /mob/living))
+ var/mob/living/pulled_mob = pulling
+ if(pulled_mob.mob_size) // small or bigger mobs
+ small_pushed = FALSE
+
+ if(pulling && !(small_pushed || (istype(pulling, /obj/item))))
+ if(m_intent == MOVE_INTENT_WALK)
+ stamina_damage += PULL_STAMINADAM_WALK
+ else
+ stamina_damage += PULL_STAMINADAM_RUN
+
+ if(staminaloss > 69)
+ balloon_alert(usr, "слишком тяжело тащить!")
+ stop_pulling()
+
+ // Handle pushing, NOT swapping sides with mobs in help intent
+ if(now_pushing)
+ if(!(istype(now_pushing, /mob/living) && a_intent == INTENT_HELP))
+ if(m_intent == MOVE_INTENT_WALK)
+ stamina_damage += PUSH_STAMINADAM_WALK
+ else
+ stamina_damage += PUSH_STAMINADAM_RUN
+
+ apply_damage(stamina_damage, STAMINA)
+
if(!has_gravity())
return .
@@ -202,3 +243,9 @@
return FALSE
return ..()
+
+
+#undef PULL_STAMINADAM_WALK
+#undef PULL_STAMINADAM_RUN
+#undef PUSH_STAMINADAM_WALK
+#undef PUSH_STAMINADAM_RUN
diff --git a/code/modules/mob/living/carbon/human/human_say.dm b/code/modules/mob/living/carbon/human/human_say.dm
index b9a48672e86..35207ae9185 100644
--- a/code/modules/mob/living/carbon/human/human_say.dm
+++ b/code/modules/mob/living/carbon/human/human_say.dm
@@ -85,21 +85,29 @@
/mob/living/carbon/human/IsVocal()
- var/obj/item/organ/internal/cyberimp/brain/speech_translator/translator = locate() in internal_organs
- if(translator?.active)
- return TRUE
+ var/obj/item/organ/internal/cyberimp/mouth/translator/translator = get_organ_slot(INTERNAL_ORGAN_SPEECH_TRANSLATOR)
+ if(translator?.active && !mind?.miming)
+ return TRUE // Cyberimps don't care if you need to breathe at all, but make some respect to mimes
+
if(HAS_TRAIT(src, TRAIT_MUTE))
return FALSE
+
+ if(TRAIT_NO_VOCAL_CORDS in dna?.species.inherent_traits)
+ return FALSE
+
// how do species that don't breathe talk? magic, that's what.
var/breathes = !HAS_TRAIT(src, TRAIT_NO_BREATH)
var/obj/item/organ/internal/lungs = get_organ_slot(INTERNAL_ORGAN_LUNGS)
if((breathes && !lungs) || (breathes && lungs && lungs.is_dead()))
return FALSE
+
if(getOxyLoss() > 10 || AmountLoseBreath() >= 8 SECONDS)
emote("gasp")
return FALSE
+
if(mind)
return !mind.miming
+
return TRUE
@@ -131,21 +139,22 @@
/mob/living/carbon/human/handle_speech_problems(list/message_pieces, verb)
var/span = ""
+ var/check_mute = TRUE
+ var/check_wingdings = TRUE
- var/obj/item/organ/internal/cyberimp/brain/speech_translator/translator = locate() in internal_organs
- if(translator?.active && !HAS_TRAIT(src, TRAIT_MUTE))
- span = translator.speech_span
- for(var/datum/multilingual_say_piece/S in message_pieces)
- S.message = "[S.message]"
- verb = translator.speech_verb
- return list("verb" = verb)
+ var/obj/item/organ/internal/cyberimp/mouth/translator/translator = get_organ_slot(INTERNAL_ORGAN_SPEECH_TRANSLATOR)
+ if(translator?.active) // Yes, we can speak even muted, unless being EMPed
+ check_mute = FALSE
+
+ if(translator.can_wingdings) // Active wingdings chip allowed us to speak normally
+ check_wingdings = FALSE
if(HAS_TRAIT(src, TRAIT_COMIC) \
|| (locate(/obj/item/organ/internal/cyberimp/brain/clown_voice) in internal_organs) \
|| HAS_TRAIT(src, TRAIT_JESTER))
span = "sans"
- if(HAS_TRAIT(src, TRAIT_WINGDINGS))
+ if(check_wingdings && HAS_TRAIT(src, TRAIT_WINGDINGS))
span = "wingdings"
var/list/parent = ..()
@@ -155,8 +164,9 @@
if(S.speaking?.flags & NO_STUTTER)
continue
- if(HAS_TRAIT(src, TRAIT_MUTE))
+ if(check_mute && (HAS_TRAIT(src, TRAIT_MUTE)))
S.message = ""
+ continue
if(istype(wear_mask, /obj/item/clothing/mask/horsehead))
var/obj/item/clothing/mask/horsehead/hoers = wear_mask
@@ -166,13 +176,21 @@
if(dna)
for(var/datum/dna/gene/gene as anything in GLOB.dna_genes)
if(gene.is_active(src))
+ if(!check_wingdings && istype(gene, /datum/dna/gene/disability/wingdings))
+ continue
+
S.message = gene.OnSay(src, S.message)
+ if(check_mute && (TRAIT_NO_VOCAL_CORDS in dna.species.inherent_traits)) // Species neither have vocal cords nor translator
+ S.message = ""
+ continue
+
var/braindam = getBrainLoss()
if(braindam >= 60)
if(prob(braindam / 4))
S.message = stutter(S.message)
verb = "gibbers"
+
if(prob(braindam))
S.message = uppertext(S.message)
verb = "yells loudly"
diff --git a/code/modules/mob/living/carbon/human/species/grey.dm b/code/modules/mob/living/carbon/human/species/grey.dm
index ab50b745c0e..3e8f7dc9b44 100644
--- a/code/modules/mob/living/carbon/human/species/grey.dm
+++ b/code/modules/mob/living/carbon/human/species/grey.dm
@@ -1,3 +1,6 @@
+#define GREYS_ADDITIONAL_GENE_STABILITY 20
+#define GREYS_WATER_DAMAGE 0.6 // 0.6 burn per unit
+
/datum/species/grey
name = SPECIES_GREY
name_plural = "Greys"
@@ -14,25 +17,31 @@
INTERNAL_ORGAN_KIDNEYS = /obj/item/organ/internal/kidneys/grey,
INTERNAL_ORGAN_BRAIN = /obj/item/organ/internal/brain/grey,
INTERNAL_ORGAN_APPENDIX = /obj/item/organ/internal/appendix,
- INTERNAL_ORGAN_EYES = /obj/item/organ/internal/eyes/grey, //5 darksight.
+ INTERNAL_ORGAN_EYES = /obj/item/organ/internal/eyes/grey, // 3 darksight.
INTERNAL_ORGAN_EARS = /obj/item/organ/internal/ears,
)
meat_type = /obj/item/reagent_containers/food/snacks/meat/humanoid/grey
- total_health = 90
- oxy_mod = 1.2 //greys are fragile
+ total_health = 80 // Greys are fragile
+ oxy_mod = 1.3
stamina_mod = 1.2
+ clone_mod = 0.7
- toolspeedmod = -0.2 //20% faster
- surgeryspeedmod = -0.2
+ toolspeedmod = -0.5 // 50% faster
+ surgeryspeedmod = -0.5
default_genes = list(/datum/dna/gene/basic/grant_spell/remotetalk)
inherent_traits = list(
+ TRAIT_WEAK_PULLING,
+ TRAIT_NO_VOCAL_CORDS,
TRAIT_HAS_LIPS,
TRAIT_HAS_REGENERATION,
+ TRAIT_ADVANCED_CYBERIMPLANTS,
+ TRAIT_ACID_PROTECTED,
)
+
blacklisted_disabilities = NONE
clothing_flags = HAS_UNDERWEAR | HAS_UNDERSHIRT | HAS_SOCKS
bodyflags = HAS_BODY_MARKINGS
@@ -48,12 +57,12 @@
/datum/species/grey/on_species_gain(mob/living/carbon/human/H)
. = ..()
- H.gene_stability += GENE_INSTABILITY_MODERATE
+ H.gene_stability += GREYS_ADDITIONAL_GENE_STABILITY
/datum/species/grey/on_species_loss(mob/living/carbon/human/H)
. = ..()
- H.gene_stability -= GENE_INSTABILITY_MODERATE
+ H.gene_stability -= GREYS_ADDITIONAL_GENE_STABILITY
/datum/species/grey/handle_dna(mob/living/carbon/human/H, remove = FALSE)
@@ -64,61 +73,74 @@
. = ..()
if(method == REAGENT_TOUCH)
- if(H.wear_mask)
- to_chat(H, "Ваша [H.wear_mask] защищает вас от кислоты!")
+ var/water_damage = (GREYS_WATER_DAMAGE * volume * H.get_permeability_protection())
+
+ H.adjustFireLoss(min(water_damage, 80))
+
+ if(H.has_pain())
+ H.emote("scream")
+ to_chat(H, span_danger("[water_damage > 30 ? "Вы чувствуете ужасающую боль по всему телу!" : "Вода жжёт вас!"]"))
+
+ if(volume > 24)
+ var/obj/item/organ/external/affecting = H.get_organ(BODY_ZONE_HEAD)
+ if(affecting)
+ affecting.disfigure()
+
+ else // IV bags and etc
+ H.adjustFireLoss(min((GREYS_WATER_DAMAGE * volume * 0.5), 80))
+
+ if(volume < 10)
return
- if(H.head)
- to_chat(H, "Ваша [H.wear_mask] защищает вас от кислоты!")
+ if(prob(75)) // Prevent emote and chat spam
return
- if(volume > 25)
- if(prob(75))
- H.take_organ_damage(5, 10)
- H.emote("scream")
- var/obj/item/organ/external/affecting = H.get_organ(BODY_ZONE_HEAD)
- if(affecting)
- affecting.disfigure()
- else
- H.take_organ_damage(5, 10)
- else
- H.take_organ_damage(5, 10)
- else
- to_chat(H, "Вода жжет вас[volume < 10 ? ", но она недостаточно сконцентрирована, чтобы вам навредить" : null]!")
- if(volume >= 10)
- H.adjustFireLoss(min(max(4, (volume - 10) * 2), 20))
+ if(H.has_pain())
H.emote("scream")
- to_chat(H, "Вода жжет вас[volume < 10 ? ", но она недостаточно сконцентрирована, чтобы вам навредить" : null]!")
+
+ to_chat(H, span_danger("Вы чувствуете острое жжение!"))
+
/datum/species/grey/after_equip_job(datum/job/J, mob/living/carbon/human/H)
+ var/obj/item/organ/internal/cyberimp/mouth/translator/grey_retraslator/retranslator = new
+ retranslator.insert(H)
+
var/translator_pref = H.client.prefs.speciesprefs
- if(translator_pref || ((ismindshielded(H) || J.is_command || J.supervisors == "the captain") && HAS_TRAIT(H, TRAIT_WINGDINGS)))
- if(J.title == JOB_TITLE_MIME)
- return
- if(J.title == JOB_TITLE_CLOWN)
- var/obj/item/organ/internal/cyberimp/brain/speech_translator/clown/implant = new
- implant.insert(H)
- else
- var/obj/item/organ/internal/cyberimp/brain/speech_translator/implant = new
- implant.insert(H)
- if(!translator_pref)
- to_chat(H, "Имплант переводчика речи был установлен вам, из-за вашей роли на станции.")
+ var/command_roles = FALSE
+
+ if(!HAS_TRAIT(H, TRAIT_WINGDINGS))
+ return handle_loadout_chip(H, retranslator)
+
+ if(ismindshielded(H) || J.is_command || J.supervisors == "the captain")
+ command_roles = TRUE
+
+ if(!translator_pref && !command_roles) // Not command and didn't want wingdings chip, so..
+ return handle_loadout_chip(H, retranslator)
+
+ var/obj/item/translator_chip/wingdings/chip = new
+ retranslator.install_chip(H, chip)
+ to_chat(H, span_notice("В связи с Ваш[translator_pref ? "им недугом" : "ей ответственной работой"], у Вас уже есть установленный чип Вингдингс."))
+ handle_loadout_chip(H, retranslator)
+
+
+/datum/species/grey/proc/handle_loadout_chip(mob/living/carbon/human/H, obj/item/organ/internal/cyberimp/mouth/translator/grey_retraslator/retranslator)
+ var/obj/item/translator_chip/chip = locate() in H.contents // we can take only one chip from loadout
+ if(chip && (LAZYLEN(retranslator.stored_chips) < retranslator.maximum_slots)) // install only if we have slot for chip
+ retranslator.install_chip(H, chip)
+
/datum/species/grey/handle_reagents(mob/living/carbon/human/H, datum/reagent/R)
- if(R.id == "sacid")
- H.reagents.remove_reagent(R.id, REAGENTS_METABOLISM)
- return FALSE
- if(R.id == "facid")
- H.reagents.remove_reagent(R.id, REAGENTS_METABOLISM)
- return FALSE
- if(R.id == "acetic_acid")
- H.reagents.remove_reagent(R.id, REAGENTS_METABOLISM)
- return FALSE
if(R.id == "water")
H.adjustFireLoss(1)
return TRUE
+
return ..()
+
/datum/species/grey/get_species_runechat_color(mob/living/carbon/human/H)
var/obj/item/organ/internal/eyes/E = H.get_int_organ(/obj/item/organ/internal/eyes)
return E.eye_colour
+
+
+#undef GREYS_ADDITIONAL_GENE_STABILITY
+#undef GREYS_WATER_DAMAGE
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index f966df04963..2388f0ff999 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -1799,6 +1799,11 @@
return
var/examine_time = target.get_examine_time()
+
+ var/obj/item/organ/internal/eyes/eyes = get_organ_slot(INTERNAL_ORGAN_EYES)
+ if(eyes)
+ examine_time *= eyes.examine_mod
+
if(examine_time && target != src)
var/visible_gender = target.get_visible_gender()
var/visible_species = "Unknown"
diff --git a/code/modules/mob/living/living_say.dm b/code/modules/mob/living/living_say.dm
index be1a0129fd8..a40536744db 100644
--- a/code/modules/mob/living/living_say.dm
+++ b/code/modules/mob/living/living_say.dm
@@ -270,6 +270,11 @@ GLOBAL_LIST_EMPTY(channel_to_radio_key)
ignore_atmospherics = TRUE
if(is_muzzled())
+ var/obj/item/organ/internal/cyberimp/mouth/translator/translator = get_organ_slot(INTERNAL_ORGAN_SPEECH_TRANSLATOR)
+ if(translator) // we can whisper with translator and muzzle
+ whisper_say(message_pieces)
+ return TRUE
+
var/obj/item/clothing/mask/muzzle/G = wear_mask
if(G.mute == MUZZLE_MUTE_ALL) //if the mask is supposed to mute you completely or just muffle you
to_chat(src, span_danger("You're muzzled and cannot speak!"))
@@ -446,7 +451,8 @@ GLOBAL_LIST_EMPTY(channel_to_radio_key)
if(stat)
return
- if(is_muzzled())
+ var/obj/item/organ/internal/cyberimp/mouth/translator/translator = get_organ_slot(INTERNAL_ORGAN_SPEECH_TRANSLATOR)
+ if(is_muzzled() && !translator?.active)
if(istype(wear_mask, /obj/item/clothing/mask/muzzle/tapegag)) //just for tape
to_chat(src, span_danger("Your mouth is taped and you cannot speak!"))
else
diff --git a/code/modules/reagents/chemistry/reagents/toxins.dm b/code/modules/reagents/chemistry/reagents/toxins.dm
index 7b31c15b90c..606fb18f3c4 100644
--- a/code/modules/reagents/chemistry/reagents/toxins.dm
+++ b/code/modules/reagents/chemistry/reagents/toxins.dm
@@ -355,122 +355,181 @@
taste_description = "ACID"
var/acidpwr = 10 //the amount of protection removed from the armour
+
/datum/reagent/acid/on_mob_life(mob/living/M)
var/update_flags = STATUS_UPDATE_NONE
- update_flags |= M.adjustFireLoss(1, FALSE)
+
+ if(!acid_proof_species(M))
+ update_flags |= M.adjustFireLoss(1, FALSE)
+
return ..() | update_flags
+
/datum/reagent/acid/reaction_mob(mob/living/M, method = REAGENT_TOUCH, volume)
- if(ishuman(M) && !isgrey(M))
- var/mob/living/carbon/human/H = M
- if(method == REAGENT_TOUCH)
- if(volume > 25)
- if(H.wear_mask)
- to_chat(H, "Your [H.wear_mask] protects you from the acid!")
- return
-
- if(H.head)
- to_chat(H, "Your [H.wear_mask] protects you from the acid!")
- return
-
- if(prob(75))
- H.take_organ_damage(5, 10)
- H.emote("scream")
- var/obj/item/organ/external/affecting = H.get_organ(BODY_ZONE_HEAD)
- if(affecting)
- affecting.disfigure()
- else
- H.take_organ_damage(5, 10)
+ if(!ishuman(M))
+ return
+
+ var/mob/living/carbon/human/H = M
+
+ if(acid_proof_species(H))
+ return
+
+ if(method == REAGENT_TOUCH)
+ if(volume > 25)
+ if(H.wear_mask)
+ to_chat(H, span_danger("Your [H.wear_mask] protects you from the acid!"))
+ return
+
+ if(H.head)
+ to_chat(H, span_danger("Your [H.head] protects you from the acid!"))
+ return
+
+ if(prob(75))
+ H.take_organ_damage(5, 10)
+ H.emote("scream")
+ var/obj/item/organ/external/affecting = H.get_organ(BODY_ZONE_HEAD)
+ if(affecting)
+ affecting.disfigure()
+
else
H.take_organ_damage(5, 10)
+
else
- to_chat(H, "The greenish acidic substance stings[volume < 10 ? " you, but isn't concentrated enough to harm you" : null]!")
- if(volume >= 10)
- H.adjustFireLoss(min(max(4, (volume - 10) * 2), 20))
- H.emote("scream")
+ H.take_organ_damage(5, 10)
+
+ else
+ to_chat(H, span_danger("The greenish acidic substance stings[volume < 10 ? " you, but isn't concentrated enough to harm you" : null]!"))
+ if(volume >= 10)
+ H.adjustFireLoss(min(max(4, (volume - 10) * 2), 20))
+ H.emote("scream")
+
/datum/reagent/acid/reaction_obj(obj/O, volume)
if(ismob(O.loc)) //handled in human acid_act()
return
+
volume = round(volume, 0.1)
O.acid_act(acidpwr, volume)
+
/datum/reagent/acid/reaction_turf(turf/T, volume)
if(!istype(T))
return
+
volume = round(volume, 0.1)
T.acid_act(acidpwr, volume)
+
/datum/reagent/acid/facid
- name = "Fluorosulfuric Acid"
+ name = "Fluorosulfuric acid"
id = "facid"
description = "Fluorosulfuric acid is a an extremely corrosive super-acid."
color = "#5050FF"
acidpwr = 42
+
/datum/reagent/acid/facid/on_mob_life(mob/living/M)
var/update_flags = STATUS_UPDATE_NONE
- update_flags |= M.adjustToxLoss(0.5, FALSE)
+
+ if(!acid_proof_species(M))
+ update_flags |= M.adjustToxLoss(0.5, FALSE)
+
return ..() | update_flags
+
/datum/reagent/acid/facid/reaction_mob(mob/living/M, method = REAGENT_TOUCH, volume)
- if(ishuman(M))
- var/mob/living/carbon/human/H = M
- if(method == REAGENT_TOUCH)
- if(volume >= 5)
- var/damage_coef = 0
- var/isDamaged = FALSE
- for(var/obj/item/organ/external/bodypart as anything in H.bodyparts)
- damage_coef = (100 - clamp(H.getarmor_organ(bodypart, "acid"), 0, 100))/100
- if(damage_coef > 0 && !isDamaged)
- isDamaged = TRUE
- if(H.has_pain())
- H.emote("scream")
- H.apply_damage(clamp((volume - 5) * 3, 8, 75) * damage_coef / length(H.bodyparts), BURN, def_zone = bodypart)
-
- if(volume > 9 && (H.wear_mask || H.head))
- if(H.wear_mask && !(H.wear_mask.resistance_flags & ACID_PROOF))
- to_chat(H, "Your [H.wear_mask.name] melts away!")
- qdel(H.wear_mask)
- H.update_inv_wear_mask()
- if(H.head && !(H.head.resistance_flags & ACID_PROOF))
- to_chat(H, "Your [H.head.name] melts away!")
- qdel(H.head)
- H.update_inv_head()
- return
- else
- if(volume >= 5)
- H.emote("scream")
- H.adjustFireLoss(clamp((volume - 5) * 3, 8, 75));
- to_chat(H, "The blueish acidic substance stings[volume < 5 ? " you, but isn't concentrated enough to harm you" : null]!")
+ if(!ishuman(M))
+ return
+
+ var/mob/living/carbon/human/H = M
+ var/damage_ignored = acid_proof_species(H)
+
+ if(method == REAGENT_TOUCH)
+ if(volume >= 5 && !damage_ignored) // Prevent damage to mob, but not to clothes
+ var/damage_coef = 0
+ var/isDamaged = FALSE
+
+ for(var/obj/item/organ/external/bodypart as anything in H.bodyparts)
+ damage_coef = (100 - clamp(H.getarmor_organ(bodypart, "acid"), 0, 100))/100
+ if(damage_coef > 0 && !isDamaged)
+ isDamaged = TRUE
+
+ if(H.has_pain())
+ H.emote("scream")
+
+ H.apply_damage(clamp((volume - 5) * 3, 8, 75) * damage_coef / length(H.bodyparts), BURN, def_zone = bodypart)
+
+ if(volume > 9)
+ if(H.wear_mask && !(H.wear_mask.resistance_flags & ACID_PROOF))
+ to_chat(H, span_danger("Your [H.wear_mask.name] melts away!"))
+ qdel(H.wear_mask)
+ H.update_inv_wear_mask()
+
+ if(H.head && !(H.head.resistance_flags & ACID_PROOF))
+ to_chat(H, span_danger("Your [H.head.name] melts away!"))
+ qdel(H.head)
+ H.update_inv_head()
+
+ return
+
+ else
+ if(damage_ignored)
+ return
+
+ if(volume >= 5)
+ H.emote("scream")
+ H.adjustFireLoss(clamp((volume - 5) * 3, 8, 75));
+
+ to_chat(H, span_warning("The blueish acidic substance stings[volume < 5 ? " you, but isn't concentrated enough to harm you" : null]!"))
+
/datum/reagent/acetic_acid
- name = "acetic acid"
+ name = "Acetic acid"
id = "acetic_acid"
description = "A weak acid that is the main component of vinegar and bad hangovers."
color = "#0080ff"
reagent_state = LIQUID
taste_description = "vinegar"
+
/datum/reagent/acetic_acid/reaction_mob(mob/M, method = REAGENT_TOUCH, volume)
- if(ishuman(M))
- var/mob/living/carbon/human/H = M
- if(method == REAGENT_TOUCH)
- if(H.wear_mask || H.head)
- return
- if(volume >= 50 && prob(75))
- var/obj/item/organ/external/affecting = H.get_organ(BODY_ZONE_HEAD)
- if(affecting)
- affecting.disfigure()
- H.take_overall_damage(5, 15)
- H.emote("scream")
- else
- H.adjustBruteLoss(min(5, volume * 0.25))
+ if(!ishuman(M))
+ return
+
+ var/mob/living/carbon/human/H = M
+ if(acid_proof_species(H))
+ return
+
+ if(method == REAGENT_TOUCH)
+ if(H.wear_mask || H.head)
+ return
+
+ if(volume >= 50 && prob(75))
+ var/obj/item/organ/external/affecting = H.get_organ(BODY_ZONE_HEAD)
+ if(affecting)
+ affecting.disfigure()
+
+ H.take_overall_damage(5, 15)
+ H.emote("scream")
+
else
- to_chat(H, "The transparent acidic substance stings[volume < 25 ? " you, but isn't concentrated enough to harm you" : null]!")
- if(volume >= 25)
- H.take_overall_damage(2)
- H.emote("scream")
+ H.adjustBruteLoss(min(5, volume * 0.25))
+
+ else
+ to_chat(H, span_warning("The transparent acidic substance stings[volume < 25 ? " you, but isn't concentrated enough to harm you" : null]!"))
+ if(volume >= 25)
+ H.take_overall_damage(2)
+ H.emote("scream")
+
+
+/datum/reagent/proc/acid_proof_species(mob/living/carbon/human/H)
+ if(!istype(H))
+ return FALSE // skip check
+
+ if(TRAIT_ACID_PROTECTED in H.dna?.species.inherent_traits)
+ return TRUE // acid proof species
+
+ return FALSE
/datum/reagent/carpotoxin
diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm
index 0e910583054..343398454d7 100644
--- a/code/modules/research/designs/medical_designs.dm
+++ b/code/modules/research/designs/medical_designs.dm
@@ -623,6 +623,17 @@
build_path = /obj/item/organ/internal/cyberimp/chest/reviver
category = list("Medical")
+/datum/design/voice_retranslator
+ name = "Psionic Voice Retranslator"
+ desc = "Имлпант для перевода псионической речи греев в более понятные звуковые волны. Разработан специально для греев."
+ id = "ci_retranslator"
+ req_tech = list("materials" = 5, "programming" = 6, "biotech" = 6, "engineering" = 6, "abductor" = 4)
+ build_type = PROTOLATHE | MECHFAB
+ construction_time = 50
+ materials = list(MAT_METAL = 2500, MAT_GLASS = 1500, MAT_TITANIUM = 1000, MAT_DIAMOND = 600, MAT_PLASMA = 500)
+ build_path = /obj/item/organ/internal/cyberimp/mouth/translator/grey_retraslator
+ category = list("Medical")
+
/////////////////////////////////////////
////////////Regular Implants/////////////
/////////////////////////////////////////
@@ -788,7 +799,7 @@
category = list("Medical")
/datum/design/modified_medical_gloves
- name = "modified medical gloves"
+ name = "Modified Medical Gloves"
desc = "They are very soft and light to the touch and do not hinder movement at all."
id = "modified_medical_gloves"
req_tech = list("magnets" = 7, "materials" = 7, "programming" = 5, "biotech" = 5)
diff --git a/code/modules/research/designs/misc_designs.dm b/code/modules/research/designs/misc_designs.dm
index 089cb6de111..ec31600f9aa 100644
--- a/code/modules/research/designs/misc_designs.dm
+++ b/code/modules/research/designs/misc_designs.dm
@@ -151,3 +151,24 @@
materials = list(MAT_METAL = 800, MAT_GLASS = 600)
build_path = /obj/item/vending_refill/custom
category = list("Miscellaneous")
+
+/datum/design/translator_chip
+ name = "PVR Language Chip"
+ desc = "Крошечный чип с индикатором. Устанавливается в импланты-переводчики."
+ id = "pvr_language_chip"
+ req_tech = list("materials" = 3, "programming" = 5, "abductor" = 1)
+ build_type = PROTOLATHE
+ build_path = /obj/item/translator_chip
+ materials = list(MAT_METAL = 1000, MAT_GLASS = 100, MAT_TITANIUM = 500, MAT_PLASMA = 500, MAT_DIAMOND = 100)
+ category = list("Miscellaneous")
+
+/datum/design/retranslator_upgrade
+ name = "PVR Storage Upgrade"
+ desc = "Маленькое устройство для расширения количества слотов голосовых чипов в ретрансляторе псионического голоса."
+ id = "pvr_storage_upgrade"
+ req_tech = list("materials" = 5, "programming" = 6, "bluespace" = 6, "abductor" = 2)
+ build_type = PROTOLATHE
+ build_path = /obj/item/translator_upgrade/grey_retraslator
+ materials = list(MAT_METAL = 1000, MAT_GLASS = 100, MAT_TITANIUM = 500, MAT_PLASMA = 500, MAT_DIAMOND = 100)
+ category = list("Miscellaneous")
+
diff --git a/code/modules/surgery/organs/augments_eyes.dm b/code/modules/surgery/organs/augments_eyes.dm
index c23dcee60b4..e1b36894421 100644
--- a/code/modules/surgery/organs/augments_eyes.dm
+++ b/code/modules/surgery/organs/augments_eyes.dm
@@ -42,11 +42,18 @@
/obj/item/organ/internal/cyberimp/eyes/emp_act(severity)
if(!owner || emp_proof)
return
+
if(severity > 1)
if(prob(10 * severity))
return
+
to_chat(owner, span_warning("Static obfuscates your vision!"))
- owner.flash_eyes(3, visual = TRUE)
+
+ if(TRAIT_ADVANCED_CYBERIMPLANTS in owner.dna?.species.inherent_traits)
+ owner.EyeBlurry(1.5 SECONDS)
+ else
+ owner.flash_eyes(3, visual = TRUE)
+
/obj/item/organ/internal/cyberimp/eyes/meson
name = "Meson scanner implant"
diff --git a/code/modules/surgery/organs/augments_internal.dm b/code/modules/surgery/organs/augments_internal.dm
index 2bc4b98d70a..8ee054a7b49 100644
--- a/code/modules/surgery/organs/augments_internal.dm
+++ b/code/modules/surgery/organs/augments_internal.dm
@@ -8,6 +8,7 @@
pickup_sound = 'sound/items/handling/component_pickup.ogg'
drop_sound = 'sound/items/handling/component_drop.ogg'
+
/obj/item/organ/internal/cyberimp/New(var/mob/M = null)
. = ..()
if(implant_overlay)
@@ -15,9 +16,15 @@
overlay.color = implant_color
overlays |= overlay
+
/obj/item/organ/internal/cyberimp/emp_act()
return // These shouldn't be hurt by EMPs in the standard way
+
+/obj/item/organ/internal/cyberimp/can_insert(mob/living/user, mob/living/carbon/target, fail_message = "Данное устройство не предусмотрено для существ с подобной анатомией.")
+ . = ..()
+
+
//[[[[BRAIN]]]]
/obj/item/organ/internal/cyberimp/brain
@@ -27,6 +34,7 @@
implant_overlay = "brain_implant_overlay"
parent_organ_zone = BODY_ZONE_HEAD
+
/obj/item/organ/internal/cyberimp/brain/emp_act(severity)
if(!owner || emp_proof)
return
@@ -49,6 +57,7 @@
origin_tech = "materials=4;programming=5;biotech=4"
actions_types = list(/datum/action/item_action/organ_action/toggle)
+
/obj/item/organ/internal/cyberimp/brain/anti_drop/ui_action_click(mob/user, datum/action/action, leftclick)
active = !active
if(active)
@@ -144,6 +153,7 @@
ui_action_click()
return ..()
+
/obj/item/organ/internal/cyberimp/brain/anti_stun
name = "CNS Rebooter implant"
desc = "This implant will automatically give you back control over your central nervous system, reducing downtime when stunned. Incompatible with the Neural Jumpstarter."
@@ -151,14 +161,17 @@
slot = INTERNAL_ORGAN_BRAIN_ANTISTUN
origin_tech = "materials=5;programming=4;biotech=5"
+
/obj/item/organ/internal/cyberimp/brain/anti_stun/hardened
name = "Hardened CNS Rebooter implant"
emp_proof = TRUE
+
/obj/item/organ/internal/cyberimp/brain/anti_stun/hardened/Initialize(mapload)
. = ..()
desc += " The implant has been hardened. It is invulnerable to EMPs."
+
/obj/item/organ/internal/cyberimp/brain/anti_stun/on_life()
..()
if(crit_fail)
@@ -166,6 +179,7 @@
if(owner.getStaminaLoss() > 60)
owner.adjustStaminaLoss(-9)
+
/obj/item/organ/internal/cyberimp/brain/anti_stun/emp_act(severity)
..()
if(crit_fail || emp_proof)
@@ -173,15 +187,18 @@
crit_fail = TRUE
addtimer(CALLBACK(src, PROC_REF(reboot)), 90 / severity)
+
/obj/item/organ/internal/cyberimp/brain/anti_stun/proc/reboot()
crit_fail = FALSE
+
/obj/item/organ/internal/cyberimp/brain/anti_stun/hardened
name = "Hardened CNS Rebooter implant"
desc = "A military-grade version of the standard implant, for NT's more elite forces."
origin_tech = "materials=6;programming=5;biotech=5"
emp_proof = TRUE
+
/obj/item/organ/internal/cyberimp/brain/anti_sleep
name = "Neural Jumpstarter implant"
desc = "This implant will automatically attempt to jolt you awake when it detects you have fallen unconscious. Has a short cooldown, incompatible with the CNS Rebooter."
@@ -190,6 +207,7 @@
origin_tech = "materials=5;programming=4;biotech=5"
var/cooldown = FALSE
+
/obj/item/organ/internal/cyberimp/brain/anti_sleep/on_life()
..()
if(crit_fail)
@@ -201,10 +219,12 @@
cooldown = TRUE
addtimer(CALLBACK(src, PROC_REF(sleepy_timer_end)), 50)
+
/obj/item/organ/internal/cyberimp/brain/anti_sleep/proc/sleepy_timer_end()
cooldown = FALSE
to_chat(owner, span_notice("You hear a small beep in your head as your Neural Jumpstarter finishes recharging."))
+
/obj/item/organ/internal/cyberimp/brain/anti_sleep/emp_act(severity)
. = ..()
if(crit_fail || emp_proof)
@@ -214,22 +234,26 @@
cooldown = TRUE
addtimer(CALLBACK(src, PROC_REF(reboot)), 90 / severity)
+
/obj/item/organ/internal/cyberimp/brain/anti_sleep/proc/reboot()
crit_fail = FALSE
cooldown = FALSE
+
/obj/item/organ/internal/cyberimp/brain/anti_sleep/hardened
name = "Hardened Neural Jumpstarter implant"
desc = "A military-grade version of the standard implant, for NT's more elite forces."
origin_tech = "materials=6;programming=5;biotech=5"
emp_proof = TRUE
+
/obj/item/organ/internal/cyberimp/brain/anti_sleep/hardened/compatible
name = "Hardened Neural Jumpstarter implant"
desc = "A military-grade version of the standard implant, for NT's more elite forces. This one is compatible with the CNS Rebooter implant."
slot = INTERNAL_ORGAN_BRAIN_ANTISLEEP
emp_proof = TRUE
+
/obj/item/organ/internal/cyberimp/brain/clown_voice
name = "Comical implant"
desc = "Uh oh."
@@ -237,42 +261,12 @@
slot = INTERNAL_ORGAN_BRAIN_CLOWNVOICE
origin_tech = "materials=2;biotech=2"
-/obj/item/organ/internal/cyberimp/brain/speech_translator //actual translating done in human/handle_speech_problems
- name = "Speech translator implant"
- desc = "While known as a translator, this implant actually generates speech based on the user's thoughts when activated, completely bypassing the need to speak."
- implant_color = "#C0C0C0"
- slot = INTERNAL_ORGAN_BRAIN_SPEECHTRANSLATOR
- w_class = WEIGHT_CLASS_TINY
- origin_tech = "materials=4;biotech=6"
- actions_types = list(/datum/action/item_action/organ_action/toggle)
- var/active = TRUE
- var/speech_span = ""
- var/speech_verb = "states"
-
-/obj/item/organ/internal/cyberimp/brain/speech_translator/clown
- name = "Comical speech translator implant"
- implant_color = "#DEDE00"
- speech_span = "sans"
-
-/obj/item/organ/internal/cyberimp/brain/speech_translator/emp_act(severity)
- if(emp_proof)
- return
- if(owner && active)
- to_chat(owner, span_notice("Your translator's safeties trigger, it is now turned off."))
- active = FALSE
-
-/obj/item/organ/internal/cyberimp/brain/speech_translator/ui_action_click(mob/user, datum/action/action, leftclick)
- if(owner && !active)
- to_chat(owner, span_notice("You turn on your translator implant."))
- active = TRUE
- else if(owner && active)
- to_chat(owner, span_notice("You turn off your translator implant."))
- active = FALSE
//[[[[MOUTH]]]]
/obj/item/organ/internal/cyberimp/mouth
parent_organ_zone = BODY_ZONE_PRECISE_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."
@@ -281,6 +275,7 @@
w_class = WEIGHT_CLASS_TINY
origin_tech = "materials=2;biotech=3"
+
/obj/item/organ/internal/cyberimp/mouth/breathing_tube/emp_act(severity)
if(emp_proof)
return
@@ -288,6 +283,7 @@
to_chat(owner, span_warning("Your breathing tube suddenly closes!"))
owner.AdjustLoseBreath(4 SECONDS)
+
//[[[[CHEST]]]]
/obj/item/organ/internal/cyberimp/chest
name = "cybernetic torso implant"
@@ -296,6 +292,7 @@
implant_overlay = "chest_implant_overlay"
parent_organ_zone = BODY_ZONE_CHEST
+
/obj/item/organ/internal/cyberimp/chest/nutriment
name = "Nutriment pump implant"
desc = "This implant will synthesize a small amount of nutriment and pumps it directly into your bloodstream when you are starving."
@@ -325,6 +322,7 @@
owner.reagents.add_reagent("????",poison_amount / severity) //food poisoning
to_chat(owner, span_warning("You feel like your insides are burning."))
+
/obj/item/organ/internal/cyberimp/chest/nutriment/plus
name = "Nutriment pump implant PLUS"
desc = "This implant will synthesize a small amount of nutriment and pumps it directly into your bloodstream when you are hungry."
@@ -334,6 +332,19 @@
poison_amount = 10
origin_tech = "materials=4;powerstorage=3;biotech=3"
+
+/obj/item/organ/internal/cyberimp/chest/nutriment/insert(mob/living/carbon/human/target, special = ORGAN_MANIPULATION_DEFAULT)
+ if(TRAIT_ADVANCED_CYBERIMPLANTS in target.dna?.species.inherent_traits)
+ hunger_modificator = 0.2
+ . = ..()
+
+
+/obj/item/organ/internal/cyberimp/chest/nutriment/remove(mob/living/carbon/human/target, special = ORGAN_MANIPULATION_DEFAULT)
+ if(TRAIT_ADVANCED_CYBERIMPLANTS in target.dna?.species.inherent_traits)
+ hunger_modificator = initial(hunger_modificator)
+ . = ..()
+
+
/obj/item/organ/internal/cyberimp/chest/nutriment_old
name = "Nutriment pump implant"
desc = "This implant with synthesize and pump into your bloodstream a small amount of nutriment when you are starving."
@@ -345,6 +356,7 @@
slot = INTERNAL_ORGAN_STOMACH
origin_tech = "materials=2;powerstorage=2;biotech=2"
+
/obj/item/organ/internal/cyberimp/chest/nutriment_old/on_life()
if(!owner)
return
@@ -362,15 +374,18 @@
owner.adjust_nutrition(50)
addtimer(CALLBACK(src, PROC_REF(synth_cool)), 50)
+
/obj/item/organ/internal/cyberimp/chest/nutriment_old/proc/synth_cool()
synthesizing = FALSE
+
/obj/item/organ/internal/cyberimp/chest/nutriment_old/emp_act(severity)
if(!owner || emp_proof)
return
owner.reagents.add_reagent("????",poison_amount / severity) //food poisoning
to_chat(owner, span_warning("You feel like your insides are burning."))
+
/obj/item/organ/internal/cyberimp/chest/nutriment_old/plus
name = "Nutriment pump implant PLUS"
desc = "This implant will synthesize and pump into your bloodstream a small amount of nutriment when you are hungry."
@@ -380,6 +395,7 @@
poison_amount = 10
origin_tech = "materials=4;powerstorage=3;biotech=3"
+
/obj/item/organ/internal/cyberimp/chest/reviver
name = "Reviver implant"
desc = "This implant will attempt to revive you if you lose consciousness. For the faint of heart!"
@@ -391,14 +407,17 @@
var/reviving = FALSE
var/cooldown = 0
+
/obj/item/organ/internal/cyberimp/chest/reviver/hardened
name = "Hardened reviver implant"
emp_proof = TRUE
+
/obj/item/organ/internal/cyberimp/chest/reviver/hardened/Initialize(mapload)
. = ..()
desc += " The implant has been hardened. It is invulnerable to EMPs."
+
/obj/item/organ/internal/cyberimp/chest/reviver/on_life()
if(cooldown > world.time || owner.suiciding) // don't heal while you're in cooldown!
return
@@ -448,6 +467,7 @@
H.set_heartattack(TRUE)
addtimer(CALLBACK(src, PROC_REF(undo_heart_attack)), 600 / severity)
+
/obj/item/organ/internal/cyberimp/chest/reviver/proc/undo_heart_attack()
var/mob/living/carbon/human/H = owner
if(!istype(H))
@@ -456,6 +476,7 @@
if(H.stat == CONSCIOUS)
to_chat(H, span_notice("You feel your heart beating again!"))
+
//BOX O' IMPLANTS
/obj/item/storage/box/cyber_implants
diff --git a/code/modules/surgery/organs/autoimplanter.dm b/code/modules/surgery/organs/autoimplanter.dm
index 7c305491918..8dbc7fc5cb9 100644
--- a/code/modules/surgery/organs/autoimplanter.dm
+++ b/code/modules/surgery/organs/autoimplanter.dm
@@ -8,6 +8,7 @@
usesound = 'sound/weapons/circsawhit.ogg'
var/obj/item/organ/internal/cyberimp/storedorgan
+
/obj/item/autoimplanter/old
icon_state = "autoimplanter"
@@ -22,15 +23,19 @@
/obj/item/autoimplanter/proc/autoimplant(mob/living/carbon/human/user)
if(!ishuman(user))
return FALSE
+
if(!storedorgan)
to_chat(user, span_warning("Киберимплант не обнаружен."))
return FALSE
+
if(!user.bodyparts_by_name[check_zone(storedorgan.parent_organ_zone)])
to_chat(user, span_warning("Отсутствует требуемая часть тела!"))
return FALSE
- if(HAS_TRAIT(user, TRAIT_NO_CYBERIMPLANTS))
- to_chat(user, span_warning("Ваш вид неспособен принять этот киберимплант!"))
+
+ if(!storedorgan.can_insert(target = user) || HAS_TRAIT(user, TRAIT_NO_CYBERIMPLANTS)) //make it silent
+ to_chat(user, span_warning("Ваш вид не подходит для установки этого киберимпланта!"))
return FALSE
+
storedorgan.insert(user)//insert stored organ into the user
user.visible_message(
span_notice("[user] активиру[pluralize_ru(user.gender,"ет","ют")] автоимплантер и вы слышите недолгий механический шум."),
@@ -38,32 +43,39 @@
)
playsound(get_turf(user), usesound, 50, TRUE)
storedorgan = null
+
return TRUE
/obj/item/autoimplanter/attackby(obj/item/I, mob/user, params)
- if(istype(I, /obj/item/organ/internal/cyberimp))
- add_fingerprint(user)
- if(storedorgan)
- to_chat(user, span_warning("В устройстве уже установлен другой киберимплант."))
- return ATTACK_CHAIN_PROCEED
- if(!user.drop_transfer_item_to_loc(I, src))
- return ..()
- storedorgan = I
- to_chat(user, span_notice("Вы установили [I.name] в автоимплантер."))
- return ATTACK_CHAIN_BLOCKED_ALL
+ if(!istype(I, /obj/item/organ/internal/cyberimp))
+ return ..()
+
+ add_fingerprint(user)
+
+ if(storedorgan)
+ to_chat(user, span_warning("В устройстве уже установлен другой киберимплант."))
+ return ATTACK_CHAIN_PROCEED
+
+ if(!user.drop_transfer_item_to_loc(I, src))
+ return ..()
- return ..()
+ storedorgan = I
+ to_chat(user, span_notice("Вы установили [I.name] в автоимплантер."))
+ return ATTACK_CHAIN_BLOCKED_ALL
/obj/item/autoimplanter/screwdriver_act(mob/living/user, obj/item/I)
. = TRUE
+
if(!storedorgan)
add_fingerprint(user)
to_chat(user, span_notice("Устройство не содержит киберимплантов."))
return .
+
if(!I.use_tool(src, user, volume = I.tool_volume))
return .
+
storedorgan.forceMove(drop_location())
storedorgan.add_fingerprint(user)
storedorgan = null
@@ -78,6 +90,7 @@
. = ..()
if(!.)
return .
+
visible_message(span_warning("Автоимплантер зловеще пищит и через мгновение вспыхивает, оставляя только пепел."))
new /obj/effect/decal/cleanable/ash(get_turf(src))
user.temporarily_remove_item_from_inventory(src, force = TRUE)
@@ -87,8 +100,10 @@
/obj/item/autoimplanter/oneuse/screwdriver_act(mob/living/user, obj/item/I)
var/self_destruct = !isnull(storedorgan)
. = ..()
+
if(!self_destruct)
return .
+
visible_message(span_warning("Автоимплантер зловеще пищит и через мгновение вспыхивает, оставляя только пепел."))
new /obj/effect/decal/cleanable/ash(get_turf(src))
user.temporarily_remove_item_from_inventory(src, force = TRUE)
@@ -124,9 +139,11 @@
. = ..()
if(!.)
return .
+
uses--
if(uses > 0)
return .
+
visible_message(span_warning("Автоимплантер зловеще пищит и через мгновение вспыхивает, оставляя только пепел."))
new /obj/effect/decal/cleanable/ash(get_turf(src))
user.temporarily_remove_item_from_inventory(src, force = TRUE)
@@ -135,5 +152,6 @@
/obj/item/autoimplanter/traitor/examine(mob/user)
. = ..()
+
if(uses)
. += span_notice("Осталось использований: [uses].")
diff --git a/code/modules/surgery/organs/ears.dm b/code/modules/surgery/organs/ears.dm
index 353c02d666f..2b90861edde 100644
--- a/code/modules/surgery/organs/ears.dm
+++ b/code/modules/surgery/organs/ears.dm
@@ -54,9 +54,17 @@
/obj/item/organ/internal/ears/cybernetic/emp_act(severity)
if(emp_proof)
return
+
..()
internal_receive_damage(30)
+
if(!iscarbon(owner))
return
+
var/mob/living/carbon/C = owner
- C.AdjustDeaf(120 SECONDS)
+ var/losstime = 120 SECONDS
+
+ if(TRAIT_ADVANCED_CYBERIMPLANTS in C.dna?.species.inherent_traits)
+ losstime /= 3
+
+ C.AdjustDeaf(losstime)
diff --git a/code/modules/surgery/organs/eyes.dm b/code/modules/surgery/organs/eyes.dm
index df6761333d9..721c1c2ba1b 100644
--- a/code/modules/surgery/organs/eyes.dm
+++ b/code/modules/surgery/organs/eyes.dm
@@ -1,3 +1,5 @@
+#define EXAMINE_INSTANT 0 // 0 seconds
+
/obj/item/organ/internal/eyes
name = "eyeballs"
icon_state = "eyes"
@@ -16,6 +18,8 @@
var/see_in_dark = 2
var/see_invisible = SEE_INVISIBLE_LIVING
var/lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE
+ /// Modifies examine time for living mobs. Uses in /mob/living/run_examinate(atom/target)
+ var/examine_mod = 1
/obj/item/organ/internal/eyes/proc/update_colour()
dna.write_eyes_attributes(src)
diff --git a/code/modules/surgery/organs/heart.dm b/code/modules/surgery/organs/heart.dm
index 999b0d2e5af..1b925772ccf 100644
--- a/code/modules/surgery/organs/heart.dm
+++ b/code/modules/surgery/organs/heart.dm
@@ -8,26 +8,31 @@
dead_icon = "heart-off"
var/icon_base = "heart"
+
/obj/item/organ/internal/heart/update_icon_state()
if(beating)
icon_state = "[icon_base]-on"
else
icon_state = "[icon_base]-off"
+
/obj/item/organ/internal/heart/remove(mob/living/carbon/human/target, special = ORGAN_MANIPULATION_DEFAULT)
if(!special)
addtimer(CALLBACK(src, PROC_REF(stop_if_unowned)), 12 SECONDS)
. = ..()
+
/obj/item/organ/internal/heart/emp_act(intensity)
if(!is_robotic() || emp_proof)
return
Stop()
+
/obj/item/organ/internal/heart/necrotize(silent = FALSE)
if(..())
Stop()
+
/obj/item/organ/internal/heart/attack_self(mob/user)
..()
if(is_dead())
@@ -37,29 +42,35 @@
Restart()
addtimer(CALLBACK(src, PROC_REF(stop_if_unowned)), 80)
+
/obj/item/organ/internal/heart/safe_replace(mob/living/carbon/human/target)
Restart()
..()
+
/obj/item/organ/internal/heart/proc/stop_if_unowned()
if(!owner)
Stop()
+
/obj/item/organ/internal/heart/proc/Stop()
beating = FALSE
update_icon()
return TRUE
+
/obj/item/organ/internal/heart/proc/Restart()
beating = TRUE
update_icon()
return TRUE
+
/obj/item/organ/internal/heart/prepare_eat()
var/obj/S = ..()
S.icon_state = dead_icon
return S
+
/obj/item/organ/internal/heart/cursed
name = "cursed heart"
desc = "it needs to be pumped..."
@@ -105,14 +116,17 @@
else
last_pump = world.time //lets be extra fair *sigh*
+
/obj/item/organ/internal/heart/cursed/insert(mob/living/carbon/M, special = ORGAN_MANIPULATION_DEFAULT)
. = ..()
if(owner)
to_chat(owner, span_userdanger("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(left_click = TRUE)
. = ..()
@@ -152,6 +166,7 @@
pickup_sound = 'sound/items/handling/component_pickup.ogg'
drop_sound = 'sound/items/handling/component_drop.ogg'
+
/obj/item/organ/internal/heart/cybernetic/upgraded
name = "upgraded cybernetic heart"
desc = "A more advanced version of a cybernetic heart. Grants the user additional stamina and heart stability, but the electronics are vulnerable to shock."
@@ -161,6 +176,21 @@
var/emagged = FALSE
var/attempted_restart = FALSE
+
+/obj/item/organ/internal/heart/cybernetic/upgraded/insert(mob/living/carbon/target, special)
+ . = ..()
+
+ if(TRAIT_ADVANCED_CYBERIMPLANTS in target.dna?.species.inherent_traits)
+ target.stam_regen_start_modifier *= 0.5
+
+
+/obj/item/organ/internal/heart/cybernetic/upgraded/remove(mob/living/carbon/human/target, special)
+ if(TRAIT_ADVANCED_CYBERIMPLANTS in target.dna?.species.inherent_traits)
+ target.stam_regen_start_modifier /= 0.5
+
+ . = ..()
+
+
/obj/item/organ/internal/heart/cybernetic/upgraded/on_life()
if(!ishuman(owner))
return
@@ -240,9 +270,14 @@
/obj/item/organ/internal/heart/cybernetic/upgraded/emp_act(severity)
..()
+
if(emp_proof)
return
- necrotize()
+
+ if(TRAIT_ADVANCED_CYBERIMPLANTS in owner.dna?.species.inherent_traits)
+ Stop()
+ else
+ necrotize()
/obj/item/organ/internal/heart/cybernetic/upgraded/shock_organ(intensity)
diff --git a/code/modules/surgery/organs/lungs.dm b/code/modules/surgery/organs/lungs.dm
index f348dd3cd8a..8fc4676c1c4 100644
--- a/code/modules/surgery/organs/lungs.dm
+++ b/code/modules/surgery/organs/lungs.dm
@@ -54,8 +54,13 @@
/obj/item/organ/internal/lungs/emp_act()
if(!is_robotic() || emp_proof)
return
+
if(owner)
- owner.LoseBreath(40 SECONDS)
+ var/losstime = 40 SECONDS
+ if(TRAIT_ADVANCED_CYBERIMPLANTS in owner.dna?.species.inherent_traits)
+ losstime /= 2
+
+ owner.LoseBreath(losstime)
/obj/item/organ/internal/lungs/insert(mob/living/carbon/target, special = ORGAN_MANIPULATION_DEFAULT)
..()
@@ -387,3 +392,15 @@
cold_level_1_threshold = 200
cold_level_2_threshold = 140
cold_level_3_threshold = 100
+
+/obj/item/organ/internal/lungs/cybernetic/upgraded/insert(mob/living/carbon/human/target, special)
+ . = ..()
+
+ if(TRAIT_ADVANCED_CYBERIMPLANTS in target.dna?.species.inherent_traits)
+ target.physiology.oxy_mod -= 0.5
+
+/obj/item/organ/internal/lungs/cybernetic/upgraded/remove(mob/living/carbon/human/target, special)
+ if(TRAIT_ADVANCED_CYBERIMPLANTS in target.dna?.species.inherent_traits)
+ target.physiology.oxy_mod += 0.5
+
+ . = ..()
diff --git a/code/modules/surgery/organs/organ_internal.dm b/code/modules/surgery/organs/organ_internal.dm
index c12203c2244..4b338ace053 100644
--- a/code/modules/surgery/organs/organ_internal.dm
+++ b/code/modules/surgery/organs/organ_internal.dm
@@ -8,6 +8,7 @@
/// Whether it shows up as an option to remove during surgery.
var/unremovable = FALSE
var/can_see_food = FALSE
+ var/list/whitelisted_species // empty list == all species alowed
light_system = MOVABLE_LIGHT
light_on = FALSE
@@ -21,6 +22,25 @@
AddComponent(/datum/component/diona_internals)
+// user = who operates on target. Optional for fail_message, can be null(silent check)
+// target = the carbon we're testing for suitability
+// fail_message = message that user will recieve if the checks failed. FALSE make it quiet even with "user"
+/obj/item/organ/internal/proc/can_insert(mob/living/user, mob/living/carbon/target, fail_message = "Данное существо не способно принять этот орган!")
+ if(!LAZYLEN(whitelisted_species))
+ return TRUE
+
+ if(!istype(target)) // only carbons have species
+ return TRUE
+
+ if(target.dna.species.name in whitelisted_species)
+ return TRUE
+
+ if(user && fail_message)
+ to_chat(user, span_warning(fail_message))
+
+ return FALSE
+
+
/obj/item/organ/internal/proc/insert(mob/living/carbon/target, special = ORGAN_MANIPULATION_DEFAULT)
if(!iscarbon(target) || owner == target)
return FALSE
diff --git a/code/modules/surgery/organs/subtypes/grey.dm b/code/modules/surgery/organs/subtypes/grey.dm
index 19b95cd4831..830978a8919 100644
--- a/code/modules/surgery/organs/subtypes/grey.dm
+++ b/code/modules/surgery/organs/subtypes/grey.dm
@@ -3,7 +3,7 @@
name = "grey liver"
desc = "A small, odd looking liver."
icon = 'icons/obj/species_organs/grey.dmi'
- alcohol_intensity = 1.6
+ alcohol_intensity = 1.4
/obj/item/organ/internal/brain/grey
species_type = /datum/species/grey
@@ -12,6 +12,7 @@
icon_state = "brain2"
mmi_icon = 'icons/obj/species_organs/grey.dmi'
mmi_icon_state = "mmi_full"
+ smart_mind = TRUE // nerd brains show us sci-hud and research scanner
/obj/item/organ/internal/brain/grey/insert(mob/living/carbon/M, special = ORGAN_MANIPULATION_DEFAULT)
. = ..()
@@ -26,7 +27,8 @@
name = "grey eyeballs"
desc = "They still look creepy and emotionless."
icon = 'icons/obj/species_organs/grey.dmi'
- see_in_dark = 5
+ see_in_dark = 3
+ examine_mod = EXAMINE_INSTANT // Insta carbon examine
/obj/item/organ/internal/heart/grey
species_type = /datum/species/grey
diff --git a/code/modules/surgery/organs/subtypes/wryn.dm b/code/modules/surgery/organs/subtypes/wryn.dm
index 2a106bd4d19..a4e33668497 100644
--- a/code/modules/surgery/organs/subtypes/wryn.dm
+++ b/code/modules/surgery/organs/subtypes/wryn.dm
@@ -6,6 +6,7 @@
icon_state = "antennae"
parent_organ_zone = BODY_ZONE_HEAD
slot = INTERNAL_ORGAN_HIVENODE
+ whitelisted_species = list(SPECIES_WRYN)
/obj/item/organ/internal/wryn/hivenode/insert(mob/living/carbon/human/M, special = ORGAN_MANIPULATION_DEFAULT)
..()
diff --git a/code/modules/surgery/organs/voice_translator.dm b/code/modules/surgery/organs/voice_translator.dm
new file mode 100644
index 00000000000..52762366389
--- /dev/null
+++ b/code/modules/surgery/organs/voice_translator.dm
@@ -0,0 +1,579 @@
+#define DEFAULT_CHIP_SLOTS 1
+#define UPGRADE_SLOTS_GREY 2
+
+
+ // TRANSLATORS //
+
+// Translators also fulfil the role of ‘vocal cords’ when there is a TRAIT_NO_VOCAL_CORDS in species inherent traits.
+// With translator any mob can speak even muted, unless being emped.
+// ANY Duct tape forcing mob with translator to whisper and keeps it off the radio
+
+/obj/item/organ/internal/cyberimp/mouth/translator // Lets make it some easier to make a new one. Write this if you want to make non-species translator.
+ name = "Just An Empty Translator" // You cant get it in-game. At least now
+ desc = "Может быть, учёные NanoTrasen заставят работать его позже..."
+ //icon =
+ //icon_state =
+ //origin_tech =
+ slot = INTERNAL_ORGAN_SPEECH_TRANSLATOR
+ w_class = WEIGHT_CLASS_TINY
+ /// List of languages, stored in this translator
+ var/list/given_languages
+ /// Russian list of languages, stored in this translator
+ var/list/given_languages_rus
+ /// What types of translator storage upgrades can be attached to this translator. Empty = nothing
+ var/list/upgrade_with
+ /// List of stored languages chips
+ var/list/stored_chips
+ /// You cant place anything without opening lid with screwriver
+ var/open = FALSE
+ /// Inactive translator don't give you any languages. Affects by EMP
+ var/active = TRUE
+ /// Slot for stored storage upgrade module
+ var/obj/item/translator_upgrade/stored_upgrade
+ /// Maximum basic slots for translator without storage upgrade
+ var/maximum_slots = DEFAULT_CHIP_SLOTS
+ /// Toggles by decoder action, allows you to speak clearly with Wingdings disability
+ var/can_wingdings = FALSE
+
+ action_icon = list(/datum/action/item_action/organ_action/translator_select_language = 'icons/mob/actions/actions.dmi',)
+ action_icon_state = list(/datum/action/item_action/organ_action/translator_select_language = "select_language")
+ actions_types = list(/datum/action/item_action/organ_action/translator_select_language)
+ var/datum/action/item_action/organ_action/wingdings_decoder/decoder
+
+
+/obj/item/organ/internal/cyberimp/mouth/translator/grey_retraslator
+ name = "Psionic Voice Retranslator"
+ desc = "Необычный инопланетный имплант с маленьким экранчиком. Судя по всему, создан специально для греев."
+ icon = 'icons/obj/voice_translator.dmi'
+ icon_state = "pvr_implant"
+ given_languages = list(LANGUAGE_GALACTIC_COMMON)
+ given_languages_rus = list("Общегалактический")
+ upgrade_with = list(/obj/item/translator_upgrade/grey_retraslator)
+ origin_tech = "materials=2;biotech=3;engineering=3;programming=3;abductor=2"
+ whitelisted_species = list(SPECIES_GREY, SPECIES_ABDUCTOR)
+ ru_names = list(
+ NOMINATIVE = "ретранслятор псионического голоса",
+ GENITIVE = "ретранслятора псионического голоса",
+ DATIVE = "ретранслятору псионического голоса",
+ ACCUSATIVE = "ретранслятор псионического голоса",
+ INSTRUMENTAL = "ретранслятором псионического голоса",
+ PREPOSITIONAL = "ретрансляторе псионического голоса",
+ )
+
+
+/obj/item/organ/internal/cyberimp/mouth/translator/examine(mob/user)
+ . = ..()
+ if(!Adjacent(user)) // Too far!
+ return
+
+ var/message = (open ? "Крышка открыта. " : "Крышка закрыта. ")
+ message += "Установленные языки: "
+ message += english_list(given_languages_rus, nothing_text = "Отсутствуют", and_text = "и", final_comma_text = ".")
+ . += span_notice(message)
+
+
+/obj/item/organ/internal/cyberimp/mouth/translator/can_insert(mob/living/user, mob/living/carbon/target)
+ if(!..())
+ return FALSE
+
+ if(!open)
+ return TRUE
+
+ if(user)
+ user.balloon_alert(user, "закройте крышку переводчика!")
+
+ return FALSE
+
+
+/obj/item/organ/internal/cyberimp/mouth/translator/insert(mob/living/carbon/target, special)
+ . = ..()
+
+ for(var/lang in given_languages)
+ if(lang == TRAIT_WINGDINGS)
+ continue
+
+ target.add_language(lang)
+
+
+/obj/item/organ/internal/cyberimp/mouth/translator/remove(mob/living/carbon/target, special)
+ if(!istype(target))
+ return
+
+ for(var/lang in given_languages)
+ if(lang == TRAIT_WINGDINGS)
+ continue
+
+ target.remove_language(lang)
+
+ . = ..()
+
+
+/obj/item/organ/internal/cyberimp/mouth/translator/update_desc(updates)
+ . = ..()
+
+ if(stored_upgrade)
+ desc += " Имеет установленный расширитель слотов."
+ else
+ desc = initial(desc)
+
+
+/obj/item/organ/internal/cyberimp/mouth/translator/attackby(obj/item/I, mob/user, params)
+ if((istype(I, /obj/item/translator_chip)))
+ var/obj/item/translator_chip/chip = I
+ return check_install_chip(user, chip)
+
+ else if(istype(I, /obj/item/translator_upgrade))
+ if(stored_upgrade)
+ user.balloon_alert(user, "модуль уже установлен!")
+ return FALSE
+
+ return install_upgrade(user, I)
+
+
+/obj/item/organ/internal/cyberimp/mouth/translator/proc/install_upgrade(mob/living/carbon/human/user, obj/item/translator_upgrade/upgrade)
+ if(!open)
+ user.balloon_alert(user, "сначала открутите крышку!")
+ return
+
+ if(!LAZYLEN(upgrade_with))
+ user.balloon_alert(user, "устройство нельзя улучшить!")
+ return
+
+ if(!(upgrade.type in upgrade_with))
+ user.balloon_alert(user, "учучшение не подходит!")
+ return
+
+ user.balloon_alert(user, "модуль расширения установлен!")
+ maximum_slots += upgrade.extra_slots
+ user.drop_transfer_item_to_loc(upgrade, src)
+ stored_upgrade = upgrade
+ update_appearance(UPDATE_DESC)
+
+
+/obj/item/organ/internal/cyberimp/mouth/translator/attack_self(mob/user)
+ if(!open)
+ return FALSE
+
+ if(!LAZYLEN(stored_chips))
+ user.balloon_alert(user, "пусто!")
+ return FALSE
+
+ var/obj/item/translator_chip/chip
+ if(LAZYLEN(stored_chips) == 1)
+ chip = stored_chips[1]
+ else
+ var/list/chip_languages = list()
+ for(var/obj/item/translator_chip/check_chip in stored_chips)
+ chip_languages[check_chip.stored_language_rus] = check_chip
+
+ chip = tgui_input_list(user, "Выберите, чип какого языка вы хотите достать:", "Извлечение чипа", chip_languages)
+ chip = chip_languages[chip]
+
+ if(!chip) // closed
+ return FALSE
+
+ remove_chip(user, chip)
+
+
+/obj/item/organ/internal/cyberimp/mouth/translator/proc/remove_chip(mob/living/carbon/human/user, obj/item/translator_chip/chip)
+ // user = who operates or who places the chip into translator
+ // chip = translator chip we are removing
+ if(!user || !chip)
+ return FALSE
+
+ if(owner) //if translator inside someone
+ owner.remove_language(chip.stored_language)
+
+ user.put_in_hands(chip)
+ LAZYREMOVE(stored_chips, chip)
+ LAZYREMOVE(given_languages, chip.stored_language)
+ LAZYREMOVE(given_languages_rus, chip.stored_language_rus)
+ remove_wingdings_chip(chip)
+
+
+/obj/item/organ/internal/cyberimp/mouth/translator/proc/check_install_chip(mob/living/carbon/human/user, obj/item/translator_chip/chip)
+ if(!user || !chip)
+ return
+
+ if(!open)
+ user.balloon_alert(user, "крышка закрыта!")
+ return
+
+ if(LAZYLEN(stored_chips) >= maximum_slots)
+ user.balloon_alert(user, "нет места под чип!")
+ return
+
+ if(!chip.stored_language)
+ user.balloon_alert(user, "чип пустой!")
+ return
+
+ if(chip.stored_language in given_languages)
+ user.balloon_alert(user, "чип уже установлен!")
+ return
+
+ user.balloon_alert(user, "чип установлен")
+ install_chip(user, chip)
+
+
+/obj/item/organ/internal/cyberimp/mouth/translator/proc/install_chip(mob/living/user, obj/item/translator_chip/chip)
+ user.drop_transfer_item_to_loc(chip, src)
+ LAZYADD(stored_chips, chip)
+ LAZYADD(given_languages, chip.stored_language)
+ LAZYADD(given_languages_rus, chip.stored_language_rus)
+ handle_wingdings_chip(chip)
+
+ if(owner)
+ owner.add_language(chip.stored_language)
+
+
+/obj/item/organ/internal/cyberimp/mouth/translator/proc/handle_wingdings_chip(obj/item/translator_chip/chip)
+ if(!owner)
+ return
+
+ if(!(chip.stored_language == TRAIT_WINGDINGS))
+ return
+
+ if(!decoder)
+ decoder = new(src)
+
+ decoder.Grant(owner)
+
+
+/obj/item/organ/internal/cyberimp/mouth/translator/proc/remove_wingdings_chip(obj/item/translator_chip/chip)
+ if(!(chip.stored_language == TRAIT_WINGDINGS))
+ return
+
+ if(owner)
+ decoder.Remove(owner)
+
+ can_wingdings = FALSE
+
+
+/obj/item/organ/internal/cyberimp/mouth/translator/screwdriver_act(mob/living/user, obj/item/I)
+ if(I.tool_behaviour != TOOL_SCREWDRIVER)
+ return
+
+ if(!(I.use_tool(src, user, 2 SECONDS, volume = 50)))
+ return
+
+ open = !open
+ user.balloon_alert(user, "крышка [open ? "откручена" : "закручена"]")
+
+
+/obj/item/organ/internal/cyberimp/mouth/translator/multitool_act(mob/living/user, obj/item/I)
+ // you can remove an upgrade with multitool
+ if(!open || (I.tool_behaviour != TOOL_MULTITOOL))
+ return
+
+ if(!(I.use_tool(src, user, 2 SECONDS, volume = 50)))
+ return
+
+ uninstall_upgrade(user)
+
+
+/obj/item/organ/internal/cyberimp/mouth/translator/proc/uninstall_upgrade(mob/living/carbon/human/user)
+ if(!stored_upgrade)
+ user.balloon_alert(user, "нечего доставать!")
+ return FALSE
+
+ if(LAZYLEN(stored_chips) > DEFAULT_CHIP_SLOTS)
+ user.balloon_alert(user, "сперва достаньте чипы!")
+ return FALSE
+
+ maximum_slots -= stored_upgrade.extra_slots
+ user.put_in_hands(stored_upgrade)
+ user.balloon_alert(user, "улучшение извлечено")
+ stored_upgrade = null
+
+
+/obj/item/organ/internal/cyberimp/mouth/translator/emp_act(severity)
+ if(emp_proof)
+ return
+
+ if(!owner)
+ return
+
+ turn_languages_off()
+ addtimer(CALLBACK(src, PROC_REF(turn_languages_on)), 20 SECONDS)
+
+
+/obj/item/organ/internal/cyberimp/mouth/translator/proc/turn_languages_on()
+ active = TRUE
+ if(!owner)
+ return
+
+ to_chat(owner, span_notice("[capitalize(declent_ru(NOMINATIVE))] снова работает!"))
+ for(var/lang in given_languages)
+ if(lang == TRAIT_WINGDINGS)
+ continue
+
+ owner.add_language(lang)
+
+ decoder.update_button_state()
+
+
+/obj/item/organ/internal/cyberimp/mouth/translator/proc/turn_languages_off()
+ active = FALSE
+ can_wingdings = FALSE
+ to_chat(owner, span_warning("[capitalize(declent_ru(NOMINATIVE))] временно вышел из строя от воздействия ЭМИ!"))
+ do_sparks(3, FALSE, owner)
+ for(var/lang in given_languages)
+ if(lang == TRAIT_WINGDINGS)
+ continue
+
+ owner.remove_language(lang)
+
+ decoder.update_button_state()
+
+
+ // TRANSLATOR ACTION BUTTONS //
+
+/datum/action/item_action/organ_action/translator_select_language
+ name = "Выбрать используемый язык"
+ icon_icon = 'icons/mob/actions/actions.dmi'
+ button_icon_state = "select_language"
+
+
+/datum/action/item_action/organ_action/translator_select_language/Trigger(left_click = TRUE)
+ if(!owner)
+ return
+
+ owner.check_languages()
+
+
+/datum/action/item_action/organ_action/wingdings_decoder
+ name = "Переключить дешифратор Вингдингс"
+ icon_icon = 'icons/mob/actions/actions.dmi'
+ button_icon_state = "wingdings_off"
+ use_itemicon = FALSE
+
+
+/datum/action/item_action/organ_action/wingdings_decoder/proc/update_button_state()
+ var/obj/item/organ/internal/cyberimp/mouth/translator/translator = owner.get_organ_slot(INTERNAL_ORGAN_SPEECH_TRANSLATOR)
+ if(!translator)
+ return FALSE
+
+ if(translator.can_wingdings)
+ button_icon_state = "wingdings_on"
+ else
+ button_icon_state = initial(button_icon_state)
+
+ UpdateButtonIcon()
+
+ return TRUE
+
+
+/datum/action/item_action/organ_action/wingdings_decoder/Trigger(left_click = TRUE)
+ if(!owner)
+ return FALSE
+
+ var/obj/item/organ/internal/cyberimp/mouth/translator/translator = owner.get_organ_slot(INTERNAL_ORGAN_SPEECH_TRANSLATOR)
+ if(!translator || !(TRAIT_WINGDINGS in translator.given_languages))
+ Remove(owner)
+
+ if(!translator.active)
+ owner.balloon_alert(owner, "дешифратор не работает!")
+ return FALSE
+
+ translator.can_wingdings = !translator.can_wingdings
+ owner.balloon_alert(owner, "дешифратор [translator.can_wingdings ? "включен" : "выключен"]")
+ update_button_state()
+
+ return TRUE
+
+
+/datum/action/item_action/organ_action/wingdings_decoder/IsAvailable()
+ if(!..())
+ return FALSE
+
+ var/obj/item/organ/internal/cyberimp/mouth/translator/translator = owner.get_organ_slot(INTERNAL_ORGAN_SPEECH_TRANSLATOR)
+ if(!translator)
+ Remove(owner)
+ return FALSE
+
+ if(!translator.active)
+ return FALSE
+
+ return TRUE
+
+
+ // TRANSLATOR STORAGE UPGRADES //
+
+/obj/item/translator_upgrade // just adminspawn now
+ name = "translator upgrade"
+ desc = "Учёные NanoTrasen ещё не поняли, как он работает. Может быть, позже..."
+ w_class = WEIGHT_CLASS_TINY
+ var/extra_slots = 1
+
+
+/obj/item/translator_upgrade/grey_retraslator
+ name = "PVR storage upgrade"
+ desc = "Маленькое инопланетное устройство с мелким экраном, показывающим только помехи. Видимо, что-то из технологий греев."
+ icon = 'icons/obj/voice_translator.dmi'
+ icon_state = "pvr_upgrade"
+ origin_tech = "materials=2;programming=3;abductor=1"
+ extra_slots = UPGRADE_SLOTS_GREY
+ ru_names = list(
+ NOMINATIVE = "модуль улучшения РПГ",
+ GENITIVE = "модуля улучшения РПГ",
+ DATIVE = "модулю улучшения РПГ",
+ ACCUSATIVE = "модуль улучшения РПГ",
+ INSTRUMENTAL = "модулем улучшения РПГ",
+ PREPOSITIONAL = "модуле улучшения РПГ",
+ )
+
+
+ // LANGUAGE TRANSLATOR CHIPS //
+
+/obj/item/translator_chip
+ name = "language chip"
+ desc = "Крошечный чип с мигающим индикатором."
+ icon = 'icons/obj/voice_translator.dmi'
+ icon_state = "chip_empty"
+ w_class = WEIGHT_CLASS_TINY
+ origin_tech = "materials=1;programming=2"
+ var/stored_language
+ var/stored_language_rus
+ ru_names = list(
+ NOMINATIVE = "языковой чип",
+ GENITIVE = "языкового чипа",
+ DATIVE = "языковому чипу",
+ ACCUSATIVE = "языковой чип",
+ INSTRUMENTAL = "языковым чипом",
+ PREPOSITIONAL = "языковом чипе",
+ )
+
+
+/obj/item/translator_chip/attack_self(mob/living/user)
+ if(stored_language)
+ return
+
+ var/list/available_languages = list()
+ for(var/path in subtypesof(/obj/item/translator_chip))
+ var/obj/item/translator_chip/chip = path
+ available_languages[chip.stored_language_rus] = chip
+
+ var/answer = tgui_input_list(user, "Выберите язык для загрузки в чип:", "Выбор прошивки", available_languages)
+ if(!answer || stored_language) //double check to prevent multispec
+ return
+
+ update_chip(available_languages[answer])
+
+
+/obj/item/translator_chip/proc/update_chip(obj/item/translator_chip/chip)
+ stored_language = chip.stored_language
+ stored_language_rus = chip.stored_language_rus
+ update_icon(UPDATE_ICON_STATE)
+
+
+/obj/item/translator_chip/examine(mob/user)
+ . = ..()
+
+ if(!Adjacent(user))
+ return
+
+ if(stored_language_rus)
+ . += span_notice("Загруженный язык: [stored_language_rus].")
+ else
+ . += span_notice("Судя по всему, не активирован.")
+
+
+/obj/item/translator_chip/update_icon_state()
+ for(var/path in subtypesof(/obj/item/translator_chip))
+ var/obj/item/translator_chip/chip = path
+ if(stored_language != chip.stored_language)
+ continue
+
+ icon_state = chip.icon_state
+ return
+
+
+ // CHIP SUBTYPES //
+
+/obj/item/translator_chip/sol
+ icon_state = "chip_solcom"
+ stored_language = LANGUAGE_SOL_COMMON
+ stored_language_rus = "Общесолнечный"
+
+/obj/item/translator_chip/neorus
+ icon_state = "chip_neorus"
+ stored_language = LANGUAGE_NEO_RUSSIAN
+ stored_language_rus = "Неорусский"
+
+/obj/item/translator_chip/gutter
+ icon_state = "chip_gutter"
+ stored_language = LANGUAGE_GUTTER
+ stored_language_rus = "Гангстерский"
+
+/obj/item/translator_chip/clownish
+ icon_state = "chip_clownish"
+ stored_language = LANGUAGE_CLOWN
+ stored_language_rus = "Клоунский"
+
+/obj/item/translator_chip/tradeband
+ icon_state = "chip_tradeband"
+ stored_language = LANGUAGE_TRADER
+ stored_language_rus = "Торговый"
+
+/obj/item/translator_chip/canilunzt
+ icon_state = "chip_canilunzt"
+ stored_language = LANGUAGE_VULPKANIN
+ stored_language_rus = "Канилунц"
+
+/obj/item/translator_chip/sintaunathi
+ icon_state = "chip_sintaunathi"
+ stored_language = LANGUAGE_UNATHI
+ stored_language_rus = "Синта'Унати"
+
+/obj/item/translator_chip/siiktajr
+ icon_state = "chip_siiktajr"
+ stored_language = LANGUAGE_TAJARAN
+ stored_language_rus = "Сик'тайр"
+
+/obj/item/translator_chip/skrellian
+ icon_state = "chip_skrellian"
+ stored_language = LANGUAGE_SKRELL
+ stored_language_rus = "Скреллианский"
+
+/obj/item/translator_chip/bubblish
+ icon_state = "chip_bubblish"
+ stored_language = LANGUAGE_SLIME
+ stored_language_rus = "Пузырчатый"
+
+/obj/item/translator_chip/voxpidgin
+ icon_state = "chip_voxpidgin"
+ stored_language = LANGUAGE_VOX
+ stored_language_rus = "Вокс-пиджин"
+
+/obj/item/translator_chip/chittin
+ icon_state = "chip_chittin"
+ stored_language = LANGUAGE_KIDAN
+ stored_language_rus = "Хитин"
+
+/obj/item/translator_chip/tkachi
+ icon_state = "chip_tkachi"
+ stored_language = LANGUAGE_MOTH
+ stored_language_rus = "Ткачий"
+
+/obj/item/translator_chip/orluum
+ icon_state = "chip_orluum"
+ stored_language = LANGUAGE_DRASK
+ stored_language_rus = "Орлуум"
+
+/obj/item/translator_chip/wingdings
+ icon_state = "chip_wingdings"
+ stored_language = TRAIT_WINGDINGS //weird but works
+ stored_language_rus = "Вингдингс"
+
+/* One day it will become a reality
+
+/obj/item/translator_chip/sintatajr
+ icon_state = "chip_sintatajr"
+ stored_language =
+ stored_language_rus = "Синта'Тайр"
+
+*/
+
+
+#undef DEFAULT_CHIP_SLOTS
+#undef UPGRADE_SLOTS_GREY
diff --git a/code/modules/surgery/organs_internal.dm b/code/modules/surgery/organs_internal.dm
index 77a40efd088..2c646bab6b9 100644
--- a/code/modules/surgery/organs_internal.dm
+++ b/code/modules/surgery/organs_internal.dm
@@ -179,6 +179,182 @@
if(affected && affected.encased) //no bones no problem.
return FALSE
+/datum/surgery/translator_manipulations
+ name = "Translator Manipulations"
+ possible_locs = list(BODY_ZONE_PRECISE_MOUTH)
+ restricted_speciestypes = null
+
+ steps = list(
+ /datum/surgery_step/generic/cut_open,
+ /datum/surgery_step/generic/clamp_bleeders,
+ /datum/surgery_step/generic/retract_skin,
+ /datum/surgery_step/screwdriver_use,
+ /datum/surgery_step/proxy/manipulate_translator,
+ /datum/surgery_step/screwdriver_use,
+ /datum/surgery_step/generic/cauterize
+ )
+
+/datum/surgery/translator_manipulations/can_start(mob/user, mob/living/carbon/target)
+ if(!..())
+ return FALSE
+
+ var/obj/item/organ/internal/cyberimp/mouth/translator/translator = target.get_organ_slot(INTERNAL_ORGAN_SPEECH_TRANSLATOR)
+ if(!translator) // nothing to maniplate with..
+ return FALSE
+
+ return TRUE
+
+
+/datum/surgery_step/screwdriver_use
+ name = "screw/unscrew translator"
+ allowed_tools = list(TOOL_SCREWDRIVER = 100)
+ time = 1 SECONDS
+
+/datum/surgery_step/screwdriver_use/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ var/obj/item/organ/internal/cyberimp/mouth/translator/translator = target.get_organ_slot(INTERNAL_ORGAN_SPEECH_TRANSLATOR)
+ user.visible_message(span_notice("[user] starts [translator.open ? "screwing" : "unscrewing"] the locking mechanism on the speech translator casing."),\
+ span_notice("You start [translator.open ? "screwing" : "unscrewing"] the locking mechanism on the speech translator casing."))
+ tool.play_tool_sound(target, 30)
+
+ return ..()
+
+/datum/surgery_step/screwdriver_use/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ var/obj/item/organ/internal/cyberimp/mouth/translator/translator = target.get_organ_slot(INTERNAL_ORGAN_SPEECH_TRANSLATOR)
+ user.visible_message(span_notice("[user] [translator.open ? "screwed" : "unscrewed"] the locking mechanism on the speech translator casing."),\
+ span_notice("You [translator.open ? "screwed" : "unscrewed"] the locking mechanism on the speech translator casing."))
+ translator.open = !translator.open
+
+ return SURGERY_STEP_CONTINUE
+
+
+/datum/surgery_step/proxy/manipulate_translator
+ name = "Manipulate translator (proxy)"
+ branches = list(
+ /datum/surgery/intermediate/manipulate_translator/install,
+ /datum/surgery/intermediate/manipulate_translator/uninstall,
+ )
+
+
+/datum/surgery/intermediate/manipulate_translator
+ requires_bodypart = TRUE
+ possible_locs = list(BODY_ZONE_PRECISE_MOUTH)
+
+
+/datum/surgery/intermediate/manipulate_translator/install
+ steps = list(/datum/surgery_step/internal/manipulate_translator/install)
+
+
+/datum/surgery_step/internal/manipulate_translator/install
+ name = "install chip/upgrade"
+ allowed_tools = list(
+ /obj/item/translator_chip = 100,
+ /obj/item/translator_upgrade = 100,
+ )
+ time = 5 SECONDS
+
+
+/datum/surgery_step/internal/manipulate_translator/install/begin_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ user.visible_message(span_notice("[user] starts connecting [tool] into the speech translator's slot."),\
+ span_notice("You start connecting [tool] into the speech translator's slot. "))
+
+ return ..()
+
+
+/datum/surgery_step/internal/manipulate_translator/install/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ var/obj/item/organ/internal/cyberimp/mouth/translator/translator = target.get_organ_slot(INTERNAL_ORGAN_SPEECH_TRANSLATOR)
+
+ if(istype(tool, /obj/item/translator_chip))
+ var/obj/item/translator_chip/chip = tool
+
+ if(!chip.stored_language)
+ to_chat(user, span_warning("Chip must be activated to connect with translator!"))
+ return SURGERY_STEP_INCOMPLETE
+
+ if(LAZYLEN(translator.stored_chips) >= translator.maximum_slots)
+ to_chat(user, span_warning("There is no place in translator to another language chip!"))
+ return SURGERY_STEP_INCOMPLETE
+
+ if(chip.stored_language in translator.given_languages)
+ to_chat(user, span_warning("This language chip already installed!"))
+ return SURGERY_STEP_INCOMPLETE
+
+ translator.install_chip(user, chip)
+
+ else if(istype(tool, /obj/item/translator_upgrade))
+ if(translator.stored_upgrade)
+ to_chat(user, span_warning("Translator already has an upgrade!"))
+ return SURGERY_STEP_INCOMPLETE
+
+ translator.install_upgrade(user, tool)
+
+ user.visible_message(span_notice("[user] succesfully connected [tool] into the speech translator's slot."),\
+ span_notice("You succesfully connected [tool] into the speech translator's slot. "))
+
+ return SURGERY_STEP_CONTINUE
+
+
+/datum/surgery/intermediate/manipulate_translator/uninstall
+ steps = list(/datum/surgery_step/internal/manipulate_translator/uninstall)
+
+
+/datum/surgery_step/internal/manipulate_translator/uninstall
+ name = "uninstall chip/upgrade"
+ allowed_tools = list(TOOL_MULTITOOL = 100)
+ time = 0
+
+
+/datum/surgery_step/internal/manipulate_translator/uninstall/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ var/obj/item/organ/internal/cyberimp/mouth/translator/translator = target.get_organ_slot(INTERNAL_ORGAN_SPEECH_TRANSLATOR)
+ var/list/choises = list()
+
+ if(translator.stored_upgrade)
+ choises["Улучшение"] = image(icon = translator.stored_upgrade.icon, icon_state = translator.stored_upgrade.icon_state)
+
+ for(var/obj/item/translator_chip/chip in translator.stored_chips)
+ choises[chip.stored_language_rus] = image(icon = chip.icon, icon_state = chip.icon_state)
+
+ if(!choises)
+ to_chat(user, span_notice("You can't find anything to uninstall from speech translator."))
+ return SURGERY_STEP_INCOMPLETE
+
+ var/choise
+ if(LAZYLEN(choises) == 1)
+ choise = choises[1]
+ else
+ choise = show_radial_menu(user, target, choises, require_near = TRUE)
+
+ if(!choise) //closed
+ return SURGERY_STEP_INCOMPLETE
+
+ user.visible_message(span_notice("[user] starts disconnecting wires from the speech translator."),\
+ span_notice("You start disconnecting wires from the speech translator. "))
+
+ if(!do_after(user, 4 SECONDS, target))
+ return SURGERY_STEP_INCOMPLETE
+
+ var/add_msg = ""
+ if(choise == "Улучшение")
+ if(LAZYLEN(translator.stored_chips) > initial(translator.maximum_slots))
+ to_chat(user, span_warning("You need to remove the extra chips first!"))
+ return SURGERY_STEP_INCOMPLETE
+
+ translator.uninstall_upgrade(user)
+ add_msg = "upgrade"
+
+ else
+ var/obj/item/translator_chip/chip
+ for(chip in translator.stored_chips)
+ if(chip.stored_language_rus == choise)
+ break
+
+ add_msg = "chip"
+ translator.remove_chip(user, chip)
+
+ user.visible_message(span_notice("[user] successfully removed the [add_msg] from the speech translator."),\
+ span_notice("You successfully removed the [add_msg] from the speech translator. "))
+
+ return ..()
+
// Intermediate steps for branching organ manipulation.
/datum/surgery/intermediate/manipulate
@@ -516,8 +692,7 @@
// dunno how you got here but okay
return SURGERY_BEGINSTEP_SKIP
- if(istype(organ, /obj/item/organ/internal/wryn/hivenode) && !iswryn(target)) // If they make more "unique" organs, I'll make some vars and a separate proc, but now..
- to_chat(user, span_warning("Данное существо не способно принять этот орган!"))
+ if(!organ.can_insert(user, target)) // checks species whitelist and special organ restrictions
return SURGERY_BEGINSTEP_SKIP
if(target_zone != organ.parent_organ_zone || target.get_organ_slot(organ.slot))
diff --git a/icons/mob/actions/actions.dmi b/icons/mob/actions/actions.dmi
index f2db1779130..a85741f6139 100644
Binary files a/icons/mob/actions/actions.dmi and b/icons/mob/actions/actions.dmi differ
diff --git a/icons/mob/inhands/items_lefthand.dmi b/icons/mob/inhands/items_lefthand.dmi
index dd601e35a22..3a501518dcf 100755
Binary files a/icons/mob/inhands/items_lefthand.dmi and b/icons/mob/inhands/items_lefthand.dmi differ
diff --git a/icons/mob/inhands/items_righthand.dmi b/icons/mob/inhands/items_righthand.dmi
index 0d01882dc95..d29e4b6b302 100755
Binary files a/icons/mob/inhands/items_righthand.dmi and b/icons/mob/inhands/items_righthand.dmi differ
diff --git a/icons/obj/voice_translator.dmi b/icons/obj/voice_translator.dmi
new file mode 100644
index 00000000000..312563b8efa
Binary files /dev/null and b/icons/obj/voice_translator.dmi differ
diff --git a/paradise.dme b/paradise.dme
index 14b4bb2f2ca..70c4aa9e12d 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -3082,6 +3082,7 @@
#include "code\modules\surgery\organs\robolimbs.dm"
#include "code\modules\surgery\organs\skeleton.dm"
#include "code\modules\surgery\organs\vocal_cords.dm"
+#include "code\modules\surgery\organs\voice_translator.dm"
#include "code\modules\surgery\organs\subtypes\abductor.dm"
#include "code\modules\surgery\organs\subtypes\diona.dm"
#include "code\modules\surgery\organs\subtypes\drask.dm"