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"