From cfec0060a3b4904adcec124d03cb3a158d1b5132 Mon Sep 17 00:00:00 2001 From: dageavtobusnick <71216640+dageavtobusnick@users.noreply.github.com> Date: Wed, 22 Jan 2025 09:33:03 +0500 Subject: [PATCH 01/13] admin: some admin tweaks part 2 --- code/__DEFINES/gamemode.dm | 1 + code/__DEFINES/is_helpers.dm | 2 + code/__DEFINES/machines.dm | 3 + code/__DEFINES/traits/declarations.dm | 2 + code/__DEFINES/traits/sources.dm | 2 + code/__HELPERS/text.dm | 3 + code/__HELPERS/type2type.dm | 5 + code/_globalvars/game_modes.dm | 1 + code/_globalvars/traits.dm | 1 + code/controllers/globals.dm | 7 + code/controllers/subsystem/tickets/tickets.dm | 8 + code/datums/datumvars.dm | 15 + code/datums/emote/emote.dm | 2 +- code/datums/outfits/outfit_admin.dm | 27 + code/datums/outfits/outfit_debug.dm | 443 ++++++++++ code/game/atoms.dm | 7 + code/game/gamemodes/game_mode.dm | 10 +- .../game/machinery/computer/communications.dm | 423 ++++++---- .../game/objects/items/stacks/sheets/glass.dm | 15 + .../objects/items/stacks/sheets/mineral.dm | 12 + .../items/stacks/sheets/sheet_types.dm | 3 + code/game/objects/items/weapons/cards_ids.dm | 2 + .../items/weapons/grenades/chem_grenade.dm | 18 +- .../items/weapons/grenades/clusterbuster.dm | 13 + code/modules/admin/admin.dm | 57 +- code/modules/admin/admin_verbs.dm | 27 + code/modules/admin/holder2.dm | 24 + code/modules/admin/topic.dm | 17 +- code/modules/admin/verbs/adminsay.dm | 5 +- code/modules/admin/verbs/asays.dm | 27 +- code/modules/admin/verbs/pray.dm | 7 - code/modules/admin/verbs/randomverbs.dm | 34 +- code/modules/buildmode/submodes/fill.dm | 29 +- code/modules/buildmode/submodes/forcemove.dm | 78 ++ code/modules/buildmode/submodes/offer.dm | 28 + code/modules/buildmode/submodes/say.dm | 36 + code/modules/clothing/glasses/hud.dm | 12 + code/modules/clothing/suits/miscellaneous.dm | 8 +- code/modules/clothing/under/miscellaneous.dm | 9 +- code/modules/events/immovable_rod.dm | 23 + code/modules/mob/living/carbon/human/death.dm | 7 + code/modules/mob/living/silicon/ai/ai.dm | 8 + .../modules/mob/living/silicon/ai/latejoin.dm | 2 + code/modules/mob/mob_helpers.dm | 46 +- .../reagents/chemistry/reagents/misc.dm | 52 +- .../reagents/reagent_containers/spray.dm | 3 +- .../surgery/organs/augments_internal.dm | 3 + icons/misc/buildmode.dmi | Bin 1711 -> 1969 bytes paradise.dme | 6 +- .../tgui/interfaces/CommunicationsComputer.js | 769 ++++++++++++------ tgui/public/tgui.bundle.js | 162 ++-- 51 files changed, 1926 insertions(+), 578 deletions(-) create mode 100644 code/datums/outfits/outfit_debug.dm create mode 100644 code/modules/buildmode/submodes/forcemove.dm create mode 100644 code/modules/buildmode/submodes/offer.dm create mode 100644 code/modules/buildmode/submodes/say.dm diff --git a/code/__DEFINES/gamemode.dm b/code/__DEFINES/gamemode.dm index 121121aa158..5d1e3cad741 100644 --- a/code/__DEFINES/gamemode.dm +++ b/code/__DEFINES/gamemode.dm @@ -24,6 +24,7 @@ #define GAMEMODE_IS_REVOLUTION (SSticker && istype(SSticker.mode, /datum/game_mode/revolution)) #define GAMEMODE_IS_WIZARD (SSticker && istype(SSticker.mode, /datum/game_mode/wizard)) #define GAMEMODE_IS_RAGIN_MAGES (SSticker && istype(SSticker.mode, /datum/game_mode/wizard/raginmages)) +#define GAMEMODE_IS_METEOR (SSticker && istype(SSticker.mode, /datum/game_mode/meteor)) // special roles // Distinct from the ROLE_X defines because some antags have multiple special roles but only one ban type diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 827b6f86b97..4de13d17dd6 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -90,6 +90,8 @@ #define is_internal_organ(A) istype(A, /obj/item/organ/internal) +#define is_organ(A) istype((A), /obj/item/organ) + GLOBAL_LIST_INIT(pointed_types, typecacheof(list( /obj/item/pen, /obj/item/screwdriver, diff --git a/code/__DEFINES/machines.dm b/code/__DEFINES/machines.dm index 4ffa1a41511..8e66d212f37 100644 --- a/code/__DEFINES/machines.dm +++ b/code/__DEFINES/machines.dm @@ -96,6 +96,9 @@ #define LOGIN_TYPE_ROBOT 3 #define LOGIN_TYPE_ADMIN 4 +#define NUKE_STATUS_INTACT 0 +#define NUKE_CORE_MISSING 1 +#define NUKE_MISSING 2 // Bitflags for a machine's preferences on when it should start processing. For use with machinery's `processing_flags` var. /// Indicates the machine will automatically start processing right after it's `Initialize()` is ran. diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index 2f24d20b028..cb221f1cc21 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -295,3 +295,5 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_NO_VOCAL_CORDS "no_vocal_cords" #define TRAIT_BLOB_ZOMBIFIED "blob_zombified" + +#define TRAIT_BEING_OFFERED "offered" diff --git a/code/__DEFINES/traits/sources.dm b/code/__DEFINES/traits/sources.dm index 2d234457aa2..69b831e0405 100644 --- a/code/__DEFINES/traits/sources.dm +++ b/code/__DEFINES/traits/sources.dm @@ -164,3 +164,5 @@ #define WET_TRAIT "wet" #define BLOB_ZOMBIE_TRAIT "blob_zombie_trait" + +#define ADMIN_OFFER_TRAIT "admin_offer" diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index 7e693fa506f..1232028760f 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -498,6 +498,9 @@ text = replacetext(text, "\[/i\]", "") text = replacetext(text, "\[u\]", "") text = replacetext(text, "\[/u\]", "") + if(check_rights(R_EVENT)) + text = replacetext(text, "\[signfont\]", "") + text = replacetext(text, "\[/signfont\]", "") if(sign) text = replacetext(text, "\[sign\]", "[user ? user.real_name : "Anonymous"]") if(fields) diff --git a/code/__HELPERS/type2type.dm b/code/__HELPERS/type2type.dm index ee87844a82e..fe47f40d5ff 100644 --- a/code/__HELPERS/type2type.dm +++ b/code/__HELPERS/type2type.dm @@ -394,3 +394,8 @@ GLOBAL_LIST_INIT(modulo_angle_to_dir, list(NORTH,NORTHEAST,EAST,SOUTHEAST,SOUTH, else return /datum return text2path(copytext(string_type, 1, last_slash)) + +/proc/text2bool(input) + if(input == "true") + return TRUE + return FALSE // diff --git a/code/_globalvars/game_modes.dm b/code/_globalvars/game_modes.dm index ee0ce7e0a3e..2cb910595bd 100644 --- a/code/_globalvars/game_modes.dm +++ b/code/_globalvars/game_modes.dm @@ -28,3 +28,4 @@ GLOBAL_LIST(antag_paradise_weights) /// Weights for all special antags in ANTAG-PARADISE gamemode. GLOBAL_LIST(antag_paradise_special_weights) +GLOBAL_VAR_INIT(gamma_ship_location, 1) // 0 = station , 1 = space diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm index f61699a0c5e..163c57f10f0 100644 --- a/code/_globalvars/traits.dm +++ b/code/_globalvars/traits.dm @@ -37,6 +37,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_ACID_PROTECTED" = TRAIT_ACID_PROTECTED, "TRAIT_AI_UNTRACKABLE" = TRAIT_AI_UNTRACKABLE, "TRAIT_BADASS" = TRAIT_BADASS, + "TRAIT_BEING_OFFERED" = TRAIT_BEING_OFFERED, "TRAIT_BLIND" = TRAIT_BLIND, "TRAIT_BLOB_ALLY" = TRAIT_BLOB_ALLY, "TRAIT_BLOODCRAWL" = TRAIT_BLOODCRAWL, diff --git a/code/controllers/globals.dm b/code/controllers/globals.dm index 60c67a662bf..59b615d68da 100644 --- a/code/controllers/globals.dm +++ b/code/controllers/globals.dm @@ -34,6 +34,13 @@ GLOBAL_REAL(GLOB, /datum/controller/global_vars) return ..() /datum/controller/global_vars/can_vv_get(var_name) + var/static/list/protected_vars = list( + "asays", "admin_log", "logging", "open_logging_views" + ) + + if(!check_rights(R_ADMIN, FALSE, src) && (var_name in protected_vars)) + return FALSE + if(gvars_datum_protected_varlist[var_name]) return FALSE return ..() diff --git a/code/controllers/subsystem/tickets/tickets.dm b/code/controllers/subsystem/tickets/tickets.dm index fa1fa67118e..b28bc8c0204 100644 --- a/code/controllers/subsystem/tickets/tickets.dm +++ b/code/controllers/subsystem/tickets/tickets.dm @@ -620,5 +620,13 @@ UI STUFF else message_staff("[usr.client] / ([usr]) снят с тикета [ticket_name] номер [index]", TICKET_STAFF_MESSAGE_ADMIN_CHANNEL) +/datum/controller/subsystem/tickets/can_vv_get(var_name) + var/static/list/protected_vars = list( + "allTickets" + ) + if(!check_rights(R_ADMIN, FALSE, src) && (var_name in protected_vars)) + return FALSE + return TRUE + #undef TICKET_STAFF_MESSAGE_ADMIN_CHANNEL #undef TICKET_STAFF_MESSAGE_PREFIX diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm index da34625f18c..161180116c0 100644 --- a/code/datums/datumvars.dm +++ b/code/datums/datumvars.dm @@ -12,6 +12,21 @@ /datum/proc/can_vv_get(var_name) return TRUE +/mob/can_vv_get(var_name) + var/static/list/protected_vars = list( + "lastKnownIP", "computer_id", "attack_log_old" + ) + if(!check_rights(R_ADMIN, FALSE, src) && (var_name in protected_vars)) + return FALSE + return TRUE + +/client/can_vv_get(var_name) + var/static/list/protected_vars = list( + "address", "chatOutput", "computer_id", "connection", "jbh", "pm_tracker", "related_accounts_cid", "related_accounts_ip", "watchlisted" + ) + if(!check_rights(R_ADMIN, FALSE, mob) && (var_name in protected_vars)) + return FALSE + return TRUE /// Called when a var is edited with the new value to change to /datum/proc/vv_edit_var(var_name, var_value) diff --git a/code/datums/emote/emote.dm b/code/datums/emote/emote.dm index e8ee68d5a50..8b3dd3b8bc9 100644 --- a/code/datums/emote/emote.dm +++ b/code/datums/emote/emote.dm @@ -280,7 +280,7 @@ * * user - The user of the emote. * * text - The text of the emote. */ -/datum/emote/proc/runechat_emote(mob/user, text) +/proc/runechat_emote(atom/user, text) var/list/can_see = get_mobs_in_view(1, user) //Allows silicon & mmi mobs carried around to see the emotes of the person carrying them around. can_see |= viewers(user, null) for(var/mob/viewer in can_see) diff --git a/code/datums/outfits/outfit_admin.dm b/code/datums/outfits/outfit_admin.dm index 08055b354c4..455eb5c9902 100644 --- a/code/datums/outfits/outfit_admin.dm +++ b/code/datums/outfits/outfit_admin.dm @@ -24,6 +24,33 @@ if(special_icon) I.icon_state = special_icon +/datum/outfit/admin/observer + name = "Observer" + + uniform = /obj/item/clothing/under/color/random + back = /obj/item/storage/backpack/satchel + shoes = /obj/item/clothing/shoes/black + box = /obj/item/storage/box/survival + backpack_contents = list( + /obj/item/implanter/dust = 1 + ) + +/datum/outfit/admin/observer/plasmaman + name = "Observer (Plasma)" + + uniform = /obj/item/clothing/under/plasmaman + head = /obj/item/clothing/head/helmet/space/plasmaman + mask = /obj/item/clothing/mask/breath + belt = /obj/item/tank/internals/plasmaman/belt/full + box = /obj/item/storage/box/survival_plasmaman + +/datum/outfit/admin/observer/vox + name = "Observer (Vox)" + + mask = /obj/item/clothing/mask/breath/vox + belt = /obj/item/tank/internals/emergency_oxygen/double/vox + box = /obj/item/storage/box/survival_vox + /datum/outfit/admin/syndicate name = "Syndicate Agent" diff --git a/code/datums/outfits/outfit_debug.dm b/code/datums/outfits/outfit_debug.dm new file mode 100644 index 00000000000..4b593f66b97 --- /dev/null +++ b/code/datums/outfits/outfit_debug.dm @@ -0,0 +1,443 @@ +/datum/outfit/admin/debug + name = "Debug outfit" + + uniform = /obj/item/clothing/under/patriotsuit + suit = /obj/item/clothing/suit/space/deathsquad/officer + head = /obj/item/clothing/head/helmet/space/deathsquad/beret + back = /obj/item/storage/backpack/ert/security + backpack_contents = list( + /obj/item/melee/energy/axe = 1, + /obj/item/storage/part_replacer/bluespace/tier4 = 1, + /obj/item/gun/magic/wand/resurrection/debug = 1, + /obj/item/gun/magic/wand/death/debug = 1, + /obj/item/debug/human_spawner = 1 + ) + belt = /obj/item/storage/belt/military/abductor/full + l_ear = /obj/item/radio/headset/centcom/debug + glasses = /obj/item/clothing/glasses/hud/debug + mask = /obj/item/clothing/mask/gas/welding/advanced + shoes = /obj/item/clothing/shoes/combat/swat + + // shit down here is mine + box = /obj/item/storage/box/debug/debugtools + suit_store = /obj/item/tank/internals/oxygen + gloves = /obj/item/clothing/gloves/combat + id = /obj/item/card/id/admin + pda = /obj/item/pda/centcom + + toggle_helmet = TRUE + + cybernetic_implants = list( + /obj/item/organ/internal/cyberimp/arm/surgery/advanced, + /obj/item/organ/internal/cyberimp/chest/nutriment_old/plus/hardened, + /obj/item/organ/internal/cyberimp/arm/janitorial/advanced + ) + + +/datum/outfit/admin/debug/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + . = ..() + if(visualsOnly) + return + var/obj/item/card/id/I = H.wear_id + if(istype(I)) + apply_to_card(I, H, get_all_accesses(), "Debugger", "admin") + +/obj/item/radio/headset/centcom/debug + name = "AVD-CNED bowman headset" + ru_names = list( + NOMINATIVE = "наушники стрелка AVD-CNED", + GENITIVE = "наушников стрелка AVD-CNED", + DATIVE = "наушникам стрелка AVD-CNED", + ACCUSATIVE = "наушники стрелка AVD-CNED", + INSTRUMENTAL = "наушниками стрелка AVD-CNED", + PREPOSITIONAL = "наушниках стрелка AVD-CNED", + ) + ks2type = /obj/item/encryptionkey/all + + +/obj/item/encryptionkey/all + name = "AVD-CNED Encryption Key" + ru_names = list( + NOMINATIVE = "ключ шифрования AVD-CNED", + GENITIVE = "ключа шифрования AVD-CNED", + DATIVE = "ключу шифрования AVD-CNED", + ACCUSATIVE = "ключ шифрования AVD-CNED", + INSTRUMENTAL = "ключом шифрования AVD-CNED", + PREPOSITIONAL = "ключе шифрования AVD-CNED", + ) + channels = list("Response Team" = TRUE, "Special Ops" = TRUE, "Science" = TRUE, "Command" = TRUE, "Medical" = TRUE, "Engineering" = TRUE, "Security" = TRUE, "Supply" = TRUE, "Service" = TRUE, "Procedure" = TRUE) // just in case + syndie = TRUE + change_voice = FALSE + +/obj/item/encryptionkey/all/Initialize(mapload) + . = ..() + for(var/channel in SSradio.radiochannels) + channels[channel] = TRUE // yeah, all channels, sure, probably fine + + +/obj/item/clothing/mask/gas/welding/advanced + name = "AVD-CNED welding mask" + ru_names = list( + NOMINATIVE = "сварочная маска AVD-CNED", + GENITIVE = "сварочной маски AVD-CNED", + DATIVE = "сварочной маске AVD-CNED", + ACCUSATIVE = "сварочную маску AVD-CNED", + INSTRUMENTAL = "сварочной маской AVD-CNED", + PREPOSITIONAL = "сварочной маске AVD-CNED", + ) + desc = "Повреждение сетчатки – это не шутка." + tint = FLASH_PROTECTION_NONE + flags_cover = MASKCOVERSEYES|MASKCOVERSMOUTH // vomit prevention when being surrounded by tons of dead bodies + +/obj/item/gun/magic/wand/resurrection/debug + desc = "Возможно ли что-то более могущественное, чем обычная магия? Эта палочка." + max_charges = 500 + variable_charges = FALSE + can_charge = TRUE + recharge_rate = 1 + +/obj/item/gun/magic/wand/death/debug + desc = "В некоторых темных кругах это известно как «друг тестировщика-клонера»." + max_charges = 500 + variable_charges = FALSE + can_charge = TRUE + recharge_rate = 1 + +/obj/item/clothing/glasses/hud/debug + name = "AVD-CNED glasses" + ru_names = list( + NOMINATIVE = "очки AVD-CNED", + GENITIVE = "очков AVD-CNED", + DATIVE = "очкам AVD-CNED", + ACCUSATIVE = "очки AVD-CNED", + INSTRUMENTAL = "очками AVD-CNED", + PREPOSITIONAL = "очках AVD-CNED", + ) + desc = "Медицинский, охранно-диагностический худ. Щелкните Alt + ЛКМ, чтобы переключить иксрей" + icon_state = "nvgmeson" + flags_cover = GLASSESCOVERSEYES + flash_protect = FLASH_PROTECTION_WELDER + + prescription_upgradable = FALSE + + HUDType = list(DATA_HUD_MEDICAL_ADVANCED, DATA_HUD_DIAGNOSTIC_ADVANCED, DATA_HUD_SECURITY_ADVANCED, DATA_HUD_HYDROPONIC) + examine_extensions = EXAMINE_HUD_SECURITY_READ | EXAMINE_HUD_SECURITY_WRITE | EXAMINE_HUD_MEDICAL | EXAMINE_HUD_SKILLS + + var/xray = FALSE + +/obj/item/clothing/glasses/hud/debug/equipped(mob/living/carbon/human/user, slot) + . = ..() + if(xray) + add_xray(user) + +/obj/item/clothing/glasses/hud/debug/dropped(mob/living/carbon/human/user) + . = ..() + if(xray) + remove_xray(user) + +/obj/item/clothing/glasses/hud/debug/AltClick(mob/user) + if(!ishuman(user)) + return + var/mob/living/carbon/human/human_user = user + if(human_user.glasses != src) + return + if(xray) + remove_xray(human_user) + else + add_xray(human_user) + xray = !xray + to_chat(user, span_notice("Вы [!xray ? "де" : ""]активировали иксрей настройку в [declent_ru(PREPOSITIONAL)]")) // ctodo test + human_user.update_sight() + +/obj/item/clothing/glasses/hud/debug/visor_toggling(mob/living/carbon/human/user) + return + + +/obj/item/clothing/glasses/hud/debug/proc/remove_xray(mob/user) + see_in_dark = initial(see_in_dark) + lighting_alpha = initial(lighting_alpha) + REMOVE_TRAIT(user, TRAIT_XRAY, "debug_glasses[UID()]") + +/obj/item/clothing/glasses/hud/debug/proc/add_xray(mob/user) + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_INVISIBLE + ADD_TRAIT(user, TRAIT_XRAY, "debug_glasses[UID()]") + +/obj/item/debug/human_spawner + name = "human spawner" + ru_names = list( + NOMINATIVE = "создатель людей", + GENITIVE = "создателя людей", + DATIVE = "создателю людей", + ACCUSATIVE = "создатель людей", + INSTRUMENTAL = "создателем людей", + PREPOSITIONAL = "создателе людей", + ) + desc = "Создайте человека, нацелившись на турф и нажав ЛКМ. Используйте в руке, чтобы изменить расу." + icon = 'icons/obj/weapons/magic.dmi' + icon_state = "nothingwand" + lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' + righthand_file = 'icons/mob/inhands/items_righthand.dmi' + w_class = WEIGHT_CLASS_SMALL + var/datum/species/selected_species + var/valid_species = list() + +/obj/item/debug/human_spawner/afterattack(atom/target, mob/user, proximity) + ..() + if(isturf(target)) + var/mob/living/carbon/human/H = new /mob/living/carbon/human(target) + if(selected_species) + H.setup_dna(selected_species.type) + +/obj/item/debug/human_spawner/attack_self(mob/user) + ..() + var/choice = tgui_input_list(user, "Выберите расу", "Создатель людей", GLOB.all_species, null) + selected_species = GLOB.all_species[choice] + +/obj/item/rcd/combat/admin + name = "AVD-CNED RCD" + ru_names = list( + NOMINATIVE = "AVD-CNED РСД", + GENITIVE = "AVD-CNED РСД", + DATIVE = "AVD-CNED РСД", + ACCUSATIVE = "AVD-CNED РСД", + INSTRUMENTAL = "AVD-CNED РСД", + PREPOSITIONAL = "AVD-CNED РСД", + ) + max_matter = INFINITY + matter = INFINITY + locked = FALSE + +/obj/item/stack/spacecash/debug + amount = 50000 + max_amount = 50000 + +/obj/item/bodyanalyzer/debug + name = "AVD-CNED handheld body analyzer" + ru_names = list( + NOMINATIVE = "портативный анализатор тела AVD-CNED", + GENITIVE = "портативного анализатора тела AVD-CNED", + DATIVE = "портативному анализатору тела AVD-CNED", + ACCUSATIVE = "портативный анализатор тела AVD-CNED", + INSTRUMENTAL = "портативным анализатором тела AVD-CNED", + PREPOSITIONAL = "портативном анализаторе тела AVD-CNED", + ) + cell_type = /obj/item/stock_parts/cell/infinite + scan_time = 1 SECONDS + scan_cd = 0 SECONDS + +/obj/item/scalpel/laser/manager/debug + name = "AVD-CNED IMS" + ru_names = list( + NOMINATIVE = "лазерный скальпель AVD-CNED", + GENITIVE = "лазерного скальпеля AVD-CNED", + DATIVE = "лазерному скальпелю AVD-CNED", + ACCUSATIVE = "лазерный скальпель AVD-CNED", + INSTRUMENTAL = "лазерным скальпелем AVD-CNED", + PREPOSITIONAL = "лазерном скальпеле AVD-CNED", + ) + desc = "Чудо современной медицины. Этот инструмент действует как любой другой хирургический инструмент и заканчиает операции в кратчайшие сроки. Эй, а как ты вообще это заполучил?" + toolspeed = 0.01 + +/obj/item/scalpel/laser/manager/debug/attack_self(mob/user) + . = ..() + toolspeed = toolspeed == 0.5 ? 0.01 : 0.5 + to_chat(user, "[src] is now set to toolspeed [toolspeed]") + playsound(src, 'sound/effects/pop.ogg', 50, 0) //Change the mode + +/obj/item/organ/internal/cyberimp/arm/surgery/advanced + name = "AVD-CNED surgical toolset implant" + ru_names = list( + NOMINATIVE = "хирургический имплант AVD-CNED", + GENITIVE = "хирургического импланта AVD-CNED", + DATIVE = "хирургическому импланту AVD-CNED", + ACCUSATIVE = "хирургический имплант AVD-CNED", + INSTRUMENTAL = "хирургическим имплантом AVD-CNED", + PREPOSITIONAL = "хирургическом импланте AVD-CNED", + ) + contents = newlist( + /obj/item/scalpel/laser/manager/debug, + /obj/item/hemostat/alien, // its needed specifically for some surgeries + /obj/item/circular_saw/alien, + /obj/item/healthanalyzer/advanced, + /obj/item/gun/medbeam, + /obj/item/handheld_defibrillator, + /obj/item/bodyanalyzer/debug + ) + +/obj/item/organ/internal/cyberimp/arm/janitorial/advanced + name = "AVD-CNED janitorial toolset implant... is that a... tazer?" + ru_names = list( + NOMINATIVE = "имплант уборщика AVD-CNED", + GENITIVE = "импланта уборщика AVD-CNED", + DATIVE = "импланту уборщика AVD-CNED", + ACCUSATIVE = "имплант уборщика AVD-CNED", + INSTRUMENTAL = "имплантом уборщика AVD-CNED", + PREPOSITIONAL = "импланте уборщика AVD-CNED", + ) + desc = "Набор продвинутых инструментов для уборки, спрятанных за скрытой панелью на руке пользователя с электрошокером? Какого черта." + parent_organ_zone = BODY_ZONE_L_ARM + slot = INTERNAL_ORGAN_L_ARM_DEVICE + + contents = newlist( + /obj/item/mop/advanced/debug, + /obj/item/soap/syndie/debug, + /obj/item/lightreplacer/bluespace/debug, + /obj/item/reagent_containers/spray/cleaner/advanced/debug, + /obj/item/gun/energy/gun/advtaser/mounted // yeah why not + ) + +/obj/item/mop/advanced/debug + name = "AVD-CNED mop" + desc = "Я знаю, что ты хочешь это сделать. Сделайте дерьмо скользким." + ru_names = list( + NOMINATIVE = "швабра AVD-CNED", + GENITIVE = "швабры AVD-CNED", + DATIVE = "швабре AVD-CNED", + ACCUSATIVE = "швабру AVD-CNED", + INSTRUMENTAL = "шваброй AVD-CNED", + PREPOSITIONAL = "швабре AVD-CNED", + ) + mopcap = 100 + mopspeed = 1 + refill_rate = 50 + +/obj/item/soap/syndie/debug + name = "super soap" + ru_names = list( + NOMINATIVE = "супер-мыло", + GENITIVE = "супер-мыла", + DATIVE = "супер-мылу", + ACCUSATIVE = "супер-мыло", + INSTRUMENTAL = "супер-мылом", + PREPOSITIONAL = "супер-мыле", + ) + desc = "Самое быстрое мыло на космическом западе." + cleanspeed = 1 + +/obj/item/lightreplacer/bluespace/debug + name = "AVD-CNED light... thingy. You know what it is." + ru_names = list( + NOMINATIVE = "заменитель ламп AVD-CNED", + GENITIVE = "заменителя ламп AVD-CNED", + DATIVE = "заменителю ламп AVD-CNED", + ACCUSATIVE = "заменитель ламп AVD-CNED", + INSTRUMENTAL = "заменителем ламп AVD-CNED", + PREPOSITIONAL = "заменителе ламп AVD-CNED", + ) + max_uses = 20000 + uses = 20000 + +/obj/item/reagent_containers/spray/cleaner/advanced/debug + name = "AVD-CNED advanced space cleaner" + ru_names = list( + NOMINATIVE = "усовершенствованный космический очиститель AVD-CNED", + GENITIVE = "усовершенствованного космического очистителя AVD-CNED", + DATIVE = "усовершенствованному космическому очистителю AVD-CNED", + ACCUSATIVE = "усовершенствованный космический очиститель AVD-CNED", + INSTRUMENTAL = "усовершенствованным космическим очистителем AVD-CNED", + PREPOSITIONAL = "усовершенствованном космическом очистителе AVD-CNED", + ) + desc = "AVD-CNED! — непенящееся чистящее средство для помещений! Как чудесно." + volume = 50000 + spray_maxrange = 10 + spray_currentrange = 10 + list_reagents = list("cleaner" = 50000) + delay = 0.1 SECONDS // it costs 1000 reagents to fire this cleaner... for 12 seconds. + + +// +// Funny matryoshka doll boxes +// + +/obj/item/storage/box/debug + w_class = WEIGHT_CLASS_NORMAL + max_w_class = WEIGHT_CLASS_GIGANTIC + max_combined_w_class = 1000 + storage_slots = 99 + +/obj/item/storage/box/debug/debugtools + name = "debug tools" + ru_names = list( + NOMINATIVE = "инструменты отладки", + GENITIVE = "инструментов отладки", + DATIVE = "инструментам отладки", + ACCUSATIVE = "инструменты отладки", + INSTRUMENTAL = "инструментами отладки", + PREPOSITIONAL = "инструментах отладки", + ) + +/obj/item/storage/box/debug/debugtools/populate_contents() + new /obj/item/card/emag(src) + new /obj/item/rcd/combat/admin(src) + new /obj/item/healthanalyzer/advanced(src) + new /obj/item/rpd/bluespace(src) + new /obj/item/stack/spacecash/debug(src) + new /obj/item/storage/box/beakers/bluespace(src) + new /obj/item/storage/box/debug/material(src) + new /obj/item/storage/box/debug/misc_debug(src) + new /obj/item/storage/box/centcomofficer(src) + new /obj/item/radio/uplink/admin(src) + +/obj/item/storage/box/debug/material + name = "box of materials" + ru_names = list( + NOMINATIVE = "коробка с материалами", + GENITIVE = "коробки с материалами", + DATIVE = "коробке с материалами", + ACCUSATIVE = "коробку с материалами", + INSTRUMENTAL = "коробкой с материалами", + PREPOSITIONAL = "коробке с материалами", + ) + +/obj/item/storage/box/debug/material/populate_contents() + new /obj/item/stack/sheet/metal/fifty(src) + new /obj/item/stack/sheet/plasteel/fifty(src) + new /obj/item/stack/sheet/plastic/fifty(src) + new /obj/item/stack/sheet/runed_metal/fifty(src) + new /obj/item/stack/sheet/glass/fifty(src) + new /obj/item/stack/sheet/rglass/fifty(src) + new /obj/item/stack/sheet/plasmaglass/fifty(src) + new /obj/item/stack/sheet/plasmarglass/fifty(src) + new /obj/item/stack/sheet/titaniumglass/fifty(src) + new /obj/item/stack/sheet/plastitaniumglass/fifty(src) + new /obj/item/stack/sheet/mineral/sandstone/fifty(src) + new /obj/item/stack/sheet/mineral/diamond/fifty(src) + new /obj/item/stack/sheet/mineral/uranium/fifty(src) + new /obj/item/stack/sheet/mineral/plasma/fifty(src) + new /obj/item/stack/sheet/mineral/gold/fifty(src) + new /obj/item/stack/sheet/mineral/silver/fifty(src) + new /obj/item/stack/sheet/mineral/bananium/fifty(src) + new /obj/item/stack/sheet/mineral/tranquillite/fifty(src) + new /obj/item/stack/sheet/mineral/titanium/fifty(src) + new /obj/item/stack/sheet/mineral/plastitanium/fifty(src) + new /obj/item/stack/sheet/mineral/abductor/fifty(src) + new /obj/item/stack/sheet/mineral/adamantine/fifty(src) + new /obj/item/stack/sheet/mineral/snow/fifty(src) + new /obj/item/stack/sheet/mineral/mythril/fifty(src) + +/obj/item/storage/box/debug/misc_debug + name = "misc admin items" + ru_names = list( + NOMINATIVE = "прочие административные предметы", + GENITIVE = "прочих административных предметов", + DATIVE = "прочим административным предметам", + ACCUSATIVE = "прочие административные предметы", + INSTRUMENTAL = "прочими административными предметами", + PREPOSITIONAL = "прочих административных предметах", + ) + +// put cool admin-only shit here :) +/obj/item/storage/box/debug/misc_debug/populate_contents() + new /obj/item/badminBook(src) + new /obj/item/reagent_containers/food/drinks/bottle/vodka/badminka(src) + new /obj/item/crowbar/power(src) // >admin only lol + new /obj/item/clothing/gloves/fingerless/rapid/admin(src) + new /obj/item/clothing/under/acj(src) + new /obj/item/clothing/suit/advanced_protective_suit(src) + new /obj/item/multitool/ai_detect/admin(src) // for giggles and shits + new /obj/item/adminfu_scroll(src) + new /obj/item/teleporter/admin(src) + new /obj/item/storage/belt/bluespace/admin(src) // god i love storage nesting + new /obj/item/mining_scanner/admin(src) + new /obj/item/gun/energy/meteorgun/pen(src) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 71eff1cfe86..59b271496f4 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -1276,6 +1276,13 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons) /atom/proc/speech_bubble(bubble_state = "", bubble_loc = src, list/bubble_recipients = list()) return +/atom/proc/atom_emote(emote) + if(!emote) + return + visible_message("[src] [emote]", "Ты слышишь, как что-то [emote]") + + runechat_emote(src, emote) + /atom/vv_edit_var(var_name, var_value) var/old_light_flags = light_flags switch(var_name) diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 8ef7feab05c..760cfe2db52 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -1,7 +1,3 @@ -#define NUKE_INTACT 0 -#define NUKE_CORE_MISSING 1 -#define NUKE_MISSING 2 - /* * GAMEMODES (by Rastaf0) * @@ -585,7 +581,7 @@ if(is_station_level(bomb.z)) nuke_status = NUKE_CORE_MISSING if(bomb.core) - nuke_status = NUKE_INTACT + nuke_status = NUKE_STATUS_INTACT return nuke_status @@ -766,7 +762,3 @@ sleep(15 SECONDS) SSticker.force_ending = TRUE return - -#undef NUKE_INTACT -#undef NUKE_CORE_MISSING -#undef NUKE_MISSING diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm index 4487ab7fe0a..3bcb59e5813 100644 --- a/code/game/machinery/computer/communications.dm +++ b/code/game/machinery/computer/communications.dm @@ -1,17 +1,31 @@ #define COMM_SCREEN_MAIN 1 #define COMM_SCREEN_STAT 2 #define COMM_SCREEN_MESSAGES 3 +#define COMM_SCREEN_ANNOUNCER 4 #define COMM_AUTHENTICATION_NONE 0 -#define COMM_AUTHENTICATION_MIN 1 -#define COMM_AUTHENTICATION_MAX 2 +#define COMM_AUTHENTICATION_HEAD 1 +#define COMM_AUTHENTICATION_CAPT 2 +#define COMM_AUTHENTICATION_CENTCOM 3 // Admin-only access +#define COMM_AUTHENTICATION_AGHOST 4 #define COMM_MSGLEN_MINIMUM 6 #define COMM_CCMSGLEN_MINIMUM 20 +#define ADMIN_CHECK(user) ((check_rights_all(R_ADMIN, FALSE, user) && authenticated >= COMM_AUTHENTICATION_CENTCOM) || user.can_admin_interact()) +#define FULL_ADMIN_CHECK(user) (check_rights_all(R_ADMIN|R_EVENT, FALSE, user) && (authenticated >= COMM_AUTHENTICATION_CENTCOM || user.can_admin_interact())) + // The communications computer /obj/machinery/computer/communications name = "communications console" + ru_names = list( + NOMINATIVE = "консоль связи", + GENITIVE = "консоли связи", + DATIVE = "консоли связи", + ACCUSATIVE = "консоль связи", + INSTRUMENTAL = "консолью связи", + PREPOSITIONAL = "консоли связи", + ) desc = "Консоль, с помощью которой Капитан может связаться с Центральным Командованием или изменить уровень угрозы. Она так же позволяет командному составу вызвать эвакуационный шаттл." icon_keyboard = "tech_key" icon_screen = "comm" @@ -39,150 +53,167 @@ light_color = LIGHT_COLOR_LIGHTBLUE + /obj/machinery/computer/communications/New() GLOB.shuttle_caller_list += src ..() crew_announcement.newscast = 0 -/obj/machinery/computer/communications/proc/is_authenticated(var/mob/user, var/message = 1) - if(authenticated == COMM_AUTHENTICATION_MAX) - return COMM_AUTHENTICATION_MAX - else if(user.can_admin_interact()) - return COMM_AUTHENTICATION_MAX +/obj/machinery/computer/communications/proc/is_authenticated(mob/user, message = TRUE) + if(user.can_admin_interact()) + return COMM_AUTHENTICATION_AGHOST + else if(ADMIN_CHECK(user)) + return COMM_AUTHENTICATION_CENTCOM + else if(authenticated == COMM_AUTHENTICATION_CAPT) + return COMM_AUTHENTICATION_CAPT else if(authenticated) - return COMM_AUTHENTICATION_MIN + return COMM_AUTHENTICATION_HEAD else if(message) - to_chat(user, span_warning("Access denied.")) + to_chat(user, span_warning("Доступ запрещен.")) playsound(src, pick('sound/machines/button.ogg', 'sound/machines/button_alternate.ogg', 'sound/machines/button_meloboom.ogg'), 20) return COMM_AUTHENTICATION_NONE -/obj/machinery/computer/communications/proc/change_security_level(var/new_level) +/obj/machinery/computer/communications/proc/change_security_level(new_level, force) tmp_alertlevel = new_level var/old_level = GLOB.security_level - if(!tmp_alertlevel) tmp_alertlevel = SEC_LEVEL_GREEN - if(tmp_alertlevel < SEC_LEVEL_GREEN) tmp_alertlevel = SEC_LEVEL_GREEN - if(tmp_alertlevel > SEC_LEVEL_BLUE) tmp_alertlevel = SEC_LEVEL_BLUE //Cannot engage delta with this - set_security_level(tmp_alertlevel) + if(!force) + new_level = clamp(new_level, SEC_LEVEL_GREEN, SEC_LEVEL_BLUE) + set_security_level(new_level) if(GLOB.security_level != old_level) //Only notify the admins if an actual change happened add_game_logs("has changed the security level to [get_security_level()].", usr) message_admins("[key_name_admin(usr)] has changed the security level to [get_security_level()].") - tmp_alertlevel = 0 + if(new_level == SEC_LEVEL_EPSILON) + // episilon is delayed... but we still want to log it + log_game("[key_name(usr)] has changed the security level to epsilon.") + message_admins("[key_name_admin(usr)] has changed the security level to epsilon.") /obj/machinery/computer/communications/ui_act(action, params, datum/tgui/ui, datum/ui_state/state) if(..()) return if(!is_secure_level(z)) - to_chat(usr, span_warning("Unable to establish a connection: You're too far away from the station!")) + to_chat(ui.user, span_warning("Невозможно установить соединение: вы находитесь слишком далеко от станции!")) return . = TRUE if(action == "auth") - if(!ishuman(usr)) - to_chat(usr, span_warning("Access denied.")) + if(!ishuman(ui.user)) + to_chat(ui.user, span_warning("Доступ запрещен.")) playsound(src, pick('sound/machines/button.ogg', 'sound/machines/button_alternate.ogg', 'sound/machines/button_meloboom.ogg'), 20) return FALSE // Logout function. if(authenticated != COMM_AUTHENTICATION_NONE) authenticated = COMM_AUTHENTICATION_NONE crew_announcement.announcer = null - setMenuState(usr, COMM_SCREEN_MAIN) + setMenuState(ui.user, COMM_SCREEN_MAIN) return // Login function. - var/list/access = usr.get_access() - if(allowed(usr)) - authenticated = COMM_AUTHENTICATION_MIN + var/list/access = ui.user.get_access() + if(allowed(ui.user)) + authenticated = COMM_AUTHENTICATION_HEAD if(ACCESS_CAPTAIN in access) - authenticated = COMM_AUTHENTICATION_MAX - var/mob/living/carbon/human/H = usr + authenticated = COMM_AUTHENTICATION_CAPT + var/mob/living/carbon/human/H = ui.user var/obj/item/card/id = H.get_id_card() if(istype(id)) crew_announcement.announcer = GetNameAndAssignmentFromId(id) + + if(ACCESS_CENT_COMMANDER in access) + if(!check_rights_all(R_ADMIN|R_EVENT, FALSE, ui.user)) + to_chat(ui.user, span_warning("[capitalize(declent_ru(NOMINATIVE))] гудит, недействительное разрешение центрального командования.")) + return + authenticated = COMM_AUTHENTICATION_CENTCOM + + if(authenticated >= COMM_AUTHENTICATION_CAPT) + var/mob/living/carbon/human/H = ui.user + if(!istype(H)) + return + if(authenticated == COMM_AUTHENTICATION_NONE) - to_chat(usr, span_warning("You need to wear your ID.")) + to_chat(ui.user, span_warning("Вам необходимо носить своЮ ID карту.")) return // All functions below this point require authentication. - if(!is_authenticated(usr)) + if(!is_authenticated(ui.user)) return FALSE switch(action) if("main") - setMenuState(usr, COMM_SCREEN_MAIN) + setMenuState(ui.user, COMM_SCREEN_MAIN) if("newalertlevel") - if(isAI(usr) || isrobot(usr)) - to_chat(usr, span_warning("Firewalls prevent you from changing the alert level.")) + if(isAI(ui.user) || isrobot(ui.user)) + to_chat(ui.user, span_warning("Брандмауэры не позволяют вам изменить код уровня угрозы.")) return - else if(usr.can_admin_interact()) - change_security_level(text2num(params["level"])) + else if(FULL_ADMIN_CHECK(ui.user)) + change_security_level(text2num(params["level"]), force = TRUE) return - else if(!ishuman(usr)) - to_chat(usr, span_warning("Security measures prevent you from changing the alert level.")) + else if(!ishuman(ui.user)) + to_chat(ui.user, span_warning("Протоколы безопасности не позволяют вам изменить код уровня угрозы.")) return - var/mob/living/carbon/human/H = usr + var/mob/living/carbon/human/H = ui.user var/obj/item/card/id/I = H.get_id_card() if(istype(I)) if((GLOB.security_level > SEC_LEVEL_RED) && !(ACCESS_CENT_GENERAL in I.access)) //if gamma, epsilon or delta and no centcom access. Decline it - to_chat(usr, span_warning("CentCom security measures prevent you from changing the alert level.")) + to_chat(ui.user, span_warning("Протоколы безопасности Центрального Командования не позволяют вам изменить код уровня угрозы.")) return if(ACCESS_HEADS in I.access) change_security_level(text2num(params["level"])) else - to_chat(usr, span_warning("You are not authorized to do this.")) - setMenuState(usr, COMM_SCREEN_MAIN) + to_chat(ui.user, span_warning("Вы не имеете достаточного доступа, чтобы это делать.")) + setMenuState(ui.user, COMM_SCREEN_MAIN) else - to_chat(usr, span_warning("You need to wear your ID.")) + to_chat(ui.user, span_warning("Вам необходимо носить своЮ ID карту.")) if("announce") - if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX) + if(is_authenticated(ui.user) >= COMM_AUTHENTICATION_CAPT) if(message_cooldown > world.time) - to_chat(usr, span_warning("Please allow at least one minute to pass between announcements.")) + to_chat(ui.user, span_warning("Пожалуйста, подождите хотя бы одну минуту между объявлениями.")) return - var/input = input(usr, "Please write a message to announce to the station crew.", "Priority Announcement") as message|null - if(!input || message_cooldown > world.time || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) + var/input = tgui_input_text(ui.user, "Пожалуйста, напишите сообщение, чтобы сообщить экипажу станции.", "Приоритетное оповещение", multiline = TRUE) + if(!input || message_cooldown > world.time || ..() || !(is_authenticated(ui.user) == COMM_AUTHENTICATION_CAPT)) return if(length(input) < COMM_MSGLEN_MINIMUM) - to_chat(usr, span_warning("Message '[input]' is too short. [COMM_MSGLEN_MINIMUM] character minimum.")) + to_chat(ui.user, span_warning("Сообщение '[input]' слишком короткое. Минимальное число символов - [COMM_MSGLEN_MINIMUM].")) return crew_announcement.Announce(input) message_cooldown = world.time + 600 //One minute if("callshuttle") - var/input = clean_input("Please enter the reason for calling the shuttle.", "Shuttle Call Reason.","") - if(!input || ..() || !is_authenticated(usr)) + var/input = tgui_input_text(ui.user, "Пожалуйста, укажите причину вызова шаттла", "Причина вызова шаттла.","") + if(!input || ..() || !is_authenticated(ui.user)) return - call_shuttle_proc(usr, input) + call_shuttle_proc(ui.user, input) if(SSshuttle.emergency.timer) post_status(STATUS_DISPLAY_TRANSFER_SHUTTLE_TIME) - setMenuState(usr, COMM_SCREEN_MAIN) + setMenuState(ui.user, COMM_SCREEN_MAIN) if("cancelshuttle") - if(isAI(usr) || isrobot(usr)) - to_chat(usr, span_warning("Firewalls prevent you from recalling the shuttle.")) + if(isAI(ui.user) || isrobot(ui.user)) + to_chat(ui.user, span_warning("Брандмауэры не позволяют вам отозвать шаттл.")) return - var/response = tgui_alert(usr, "Are you sure you wish to recall the shuttle?", "Confirm", list("Yes", "No")) - if(response == "Yes") - cancel_call_proc(usr) + var/response = tgui_alert(ui.user, "Вы уверены, что хотите отозвать шаттл?", "Подтверждение", list("Да", "Нет")) + if(response == "Да") + cancel_call_proc(ui.user) if(SSshuttle.emergency.timer) post_status(STATUS_DISPLAY_TRANSFER_SHUTTLE_TIME) - setMenuState(usr, COMM_SCREEN_MAIN) + setMenuState(ui.user, COMM_SCREEN_MAIN) if("messagelist") currmsg = null aicurrmsg = null if(params["msgid"]) - setCurrentMessage(usr, text2num(params["msgid"])) - setMenuState(usr, COMM_SCREEN_MESSAGES) + setCurrentMessage(ui.user, text2num(params["msgid"])) + setMenuState(ui.user, COMM_SCREEN_MESSAGES) if("delmessage") if(params["msgid"]) currmsg = text2num(params["msgid"]) - var/response = alert("Are you sure you wish to delete this message?", "Confirm", "Yes", "No") - if(response == "Yes") + var/response = tgui_alert(ui.user, "Вы уверены, что хотите удалить это сообщение?", "Подтверждение", list("Да", "Нет")) + if(response == "Да") if(currmsg) var/id = getCurrentMessage() var/title = messagetitle[id] @@ -193,92 +224,167 @@ currmsg = null if(aicurrmsg == id) aicurrmsg = null - setMenuState(usr, COMM_SCREEN_MESSAGES) + setMenuState(ui.user, COMM_SCREEN_MESSAGES) if("status") - setMenuState(usr, COMM_SCREEN_STAT) + setMenuState(ui.user, COMM_SCREEN_STAT) // Status display stuff if("setstat") display_type = params["statdisp"] switch(display_type) - if("message") + if(STATUS_DISPLAY_MESSAGE) display_icon = null post_status(STATUS_DISPLAY_MESSAGE, stat_msg1, stat_msg2) - if("alert") + if(STATUS_DISPLAY_ALERT) display_icon = params["alert"] post_status(STATUS_DISPLAY_ALERT, params["alert"]) else display_icon = null post_status(display_type) - setMenuState(usr, COMM_SCREEN_STAT) + setMenuState(ui.user, COMM_SCREEN_STAT) if("setmsg1") - stat_msg1 = tgui_input_text(ui.user, "Line 1", stat_msg1, "Enter Message Text", encode = FALSE) - setMenuState(usr, COMM_SCREEN_STAT) + stat_msg1 = tgui_input_text(ui.user, "Строка 1", stat_msg1, "Введите текст сообщения", encode = FALSE) + setMenuState(ui.user, COMM_SCREEN_STAT) if("setmsg2") - stat_msg2 = tgui_input_text(ui.user, "Line 2", stat_msg2, "Enter Message Text", encode = FALSE) - setMenuState(usr, COMM_SCREEN_STAT) + stat_msg2 = tgui_input_text(ui.user, "Строка 2", stat_msg2, "Введите текст сообщения", encode = FALSE) + setMenuState(ui.user, COMM_SCREEN_STAT) if("nukerequest") - if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX) + if(is_authenticated(ui.user) >= COMM_AUTHENTICATION_CAPT) if(centcomm_message_cooldown > world.time) - to_chat(usr, span_warning("Arrays recycling. Please stand by.")) + to_chat(ui.user, span_warning("Обработка массивов. Пожалуйста, подождите.")) return - var/input = tgui_input_text(ui.user, "Please enter the reason for requesting the nuclear self-destruct codes. Misuse of the nuclear request system will not be tolerated under any circumstances. Transmission does not guarantee a response.", "Self Destruct Code Request.") - if(isnull(input) || ..() || !(is_authenticated(ui.user) >= COMM_AUTHENTICATION_MAX)) + var/input = tgui_input_text(ui.user, "Пожалуйста, укажите причину запроса кодов от устройства самоуничтожения. Злоупотребление системой запросов кодов недопустимо ни при каких обстоятельствах. Запрос не гарантирует ответа.", "Запрос кода самоуничтожения.") + if(isnull(input) || ..() || !(is_authenticated(ui.user) >= COMM_AUTHENTICATION_CAPT)) return if(length(input) < COMM_CCMSGLEN_MINIMUM) - to_chat(usr, span_warning("Message '[input]' is too short. [COMM_CCMSGLEN_MINIMUM] character minimum.")) + to_chat(ui.user, span_warning("Сообщение '[input]' слишком короткое. Минимальное число символов - [COMM_MSGLEN_MINIMUM].")) return - Nuke_request(input, usr) - to_chat(usr, span_notice("Request sent.")) + Nuke_request(input, ui.user) + to_chat(ui.user, span_notice("Запрос отправлен.")) add_game_logs("has requested the nuclear codes from Centcomm: [input]", usr) GLOB.priority_announcement.Announce("Коды активации ядерной боеголовки на станции были запрошены [usr]. Решение о подтверждении или отклонении данного запроса будет отправлено в ближайшее время.", "Запрошены коды активации ядерной боеголовки.",'sound/AI/commandreport.ogg') centcomm_message_cooldown = world.time + 6000 // 10 minutes - setMenuState(usr, COMM_SCREEN_MAIN) + setMenuState(ui.user, COMM_SCREEN_MAIN) if("MessageCentcomm") - if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX) + if(is_authenticated(ui.user) >= COMM_AUTHENTICATION_CAPT) if(centcomm_message_cooldown > world.time) - to_chat(usr, span_warning("Arrays recycling. Please stand by.")) + to_chat(ui.user, span_warning("Обработка массивов. Пожалуйста, подождите.")) return - var/input = tgui_input_text(ui.user, "Please choose a message to transmit to Centcomm via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response.", "CentComm Message") - if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) + var/input = tgui_input_text(ui.user, "Пожалуйста, выберите сообщение для передачи Центральному Командованию посредством квантовой запутанности. Имейте в виду, что этот процесс очень дорогостоящий, и злоупотребления приведут к... прекращению действия. Передача не гарантирует ответа", "Сообщение на ЦК") + if(!input || ..() || !(is_authenticated(ui.user) == COMM_AUTHENTICATION_CAPT)) return if(length(input) < COMM_CCMSGLEN_MINIMUM) - to_chat(usr, span_warning("Message '[input]' is too short. [COMM_CCMSGLEN_MINIMUM] character minimum.")) + to_chat(ui.user, span_warning("Сообщение '[input]' слишком короткое. Минимальное число символов - [COMM_MSGLEN_MINIMUM].")) return - Centcomm_announce(input, usr) - print_centcom_report(input, station_time_timestamp() + " Captain's Message") - to_chat(usr, "Message transmitted.") - add_game_logs("has made a Centcomm announcement: [input]", usr) + Centcomm_announce(input, ui.user) + print_centcom_report(input, station_time_timestamp() + " Сообщение капитана") + to_chat(ui.user, "Сообщение передано.") + add_game_logs("has made a Centcomm announcement: [input]", ui.user) centcomm_message_cooldown = world.time + 6000 // 10 minutes - setMenuState(usr, COMM_SCREEN_MAIN) + setMenuState(ui.user, COMM_SCREEN_MAIN) // OMG SYNDICATE ...LETTERHEAD if("MessageSyndicate") - if((is_authenticated(usr) == COMM_AUTHENTICATION_MAX) && (src.emagged)) + if((is_authenticated(ui.user) >= COMM_AUTHENTICATION_CAPT) && (src.emagged)) if(centcomm_message_cooldown > world.time) - to_chat(usr, "Arrays recycling. Please stand by.") + to_chat(ui.user, "Обработка массивов. Пожалуйста, подождите.") return - var/input = tgui_input_text(ui.user, "Please choose a message to transmit to \[ABNORMAL ROUTING CORDINATES\] via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response.", "Send Message") - if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) + var/input = tgui_input_text(ui.user, "Пожалуйста, выберите сообщение для передачи в \[АНОМАЛЬНЫЕ КОРДИНАТЫ МАРШРУТИЗАЦИИ\] посредством квантовой запутанности. Имейте в виду, что этот процесс очень дорогостоящий, и злоупотребления приведут к... прекращению действия. Передача не гарантирует ответа.", "Отправить сообщение") + if(!input || ..() || !(is_authenticated(ui.user) == COMM_AUTHENTICATION_CAPT)) return if(length(input) < COMM_CCMSGLEN_MINIMUM) - to_chat(usr, span_warning("Message '[input]' is too short. [COMM_CCMSGLEN_MINIMUM] character minimum.")) + to_chat(ui.user, span_warning("Сообщение '[input]' слишком короткое. Минимальное число символов - [COMM_MSGLEN_MINIMUM].")) return - Syndicate_announce(input, usr) - to_chat(usr, "Message transmitted.") - add_game_logs("has made a Syndicate announcement: [input]", usr) + Syndicate_announce(input, ui.user) + to_chat(ui.user, "Сообщение передано.") + add_game_logs("has made a Syndicate announcement: [input]", ui.user) centcomm_message_cooldown = world.time + 6000 // 10 minutes - setMenuState(usr, COMM_SCREEN_MAIN) + setMenuState(ui.user, COMM_SCREEN_MAIN) if("RestoreBackup") - to_chat(usr, "Backup routing data restored!") + to_chat(ui.user, "Данные маршрутизации восстановлены из резервной копии!") src.emagged = 0 - setMenuState(usr, COMM_SCREEN_MAIN) + setMenuState(ui.user, COMM_SCREEN_MAIN) + + // ADMIN CENTCOMM ONLY STUFF + + if("send_to_cc_announcement_page") + if(!ADMIN_CHECK(ui.user)) + return + setMenuState(ui.user, COMM_SCREEN_ANNOUNCER) + + if("make_other_announcement") + if(!FULL_ADMIN_CHECK(ui.user)) + return + ui.user.client.cmd_admin_create_centcom_report() + + if("dispatch_ert") + if(!FULL_ADMIN_CHECK(ui.user)) + return + ui.user.client.response_team() // check_rights is handled on the other side, if someone does get ahold of this + + if("send_nuke_codes") + if(!ADMIN_CHECK(ui.user)) + return + print_nuke_codes() + + if("move_gamma_armory") + if(!FULL_ADMIN_CHECK(ui.user)) + return + SSblackbox.record_feedback("tally", "admin_comms_console", 1, "Send Gamma Armory") + log_and_message_admins("moved the gamma armory") + if(!SSshuttle.toggleShuttle("gamma_shuttle","gamma_home","gamma_away", TRUE)) + GLOB.gamma_ship_location = !GLOB.gamma_ship_location + + if("toggle_ert_allowed") + if(!FULL_ADMIN_CHECK(ui.user)) + return + ui.user.client.toggle_ert_calling() + + + if("view_fax") + if(!ADMIN_CHECK(ui.user)) + return + ui.user.client.fax_panel() + + if("make_cc_announcement") + if(!ADMIN_CHECK(ui.user)) + return + if(!params["classified"]) + GLOB.command_announcement.Announce( + params["text"],\ + from = "Сообщение Центрального Командования",\ + new_title = params["subtitle"],\ + new_sound = 'sound/AI/commandreport.ogg' + ) + print_command_report(params["text"], params["subtitle"]) + else + GLOB.event_announcement.Announce("Отчёт был загружен и распечатан на всех консолях связи.", "Входящее засекреченное сообщение.", 'sound/AI/commandreport.ogg', from = "[command_name()] обновление") + print_command_report(params["text"], "Секретно: [params["subtitle"]]") + + log_and_message_admins("has created a communications report: [params["text"]]") + // Okay but this is just an IC way of accessing the same verb + SSblackbox.record_feedback("tally", "admin_comms_console", 1, "Create CC Report") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/obj/machinery/computer/communications/proc/print_nuke_codes() + playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, TRUE) + var/obj/item/paper/P = new /obj/item/paper(get_turf(src)) + P.name = "'КОНФИДЕНЦИАЛЬНО' - [station_name()] Коды от ядерной боеголовки" + P.info = "

КОНФИДЕНЦИАЛЬНО


" + + P.info += "Коды от ядерной боеголовки станции [station_name()] - [get_nuke_code()].
" + switch(get_nuke_status()) + if(NUKE_MISSING) + P.info += "Сканеры дальнего действия не могут обнаружить боеголовку на станции." + if(NUKE_CORE_MISSING) + P.info += "Сканеры дальнего действия не обнаруживают радиоактивных сигнатур внутри устройства." + + P.info += "

Несоблюдение нормативных требований компании по конфиденциальности может привести к немедленному увольнению по приказу сотрудников Центрального командования." @@ -287,13 +393,13 @@ add_attack_logs(user, src, "emagged") src.emagged = 1 if(user) - to_chat(user, span_notice("You scramble the communication routing circuits!")) + to_chat(user, span_notice("Вы шифруете схемы маршрутизации связи!")) SStgui.update_uis(src) -/obj/machinery/computer/communications/attack_ai(var/mob/user as mob) +/obj/machinery/computer/communications/attack_ai(mob/user as mob) return src.attack_hand(user) -/obj/machinery/computer/communications/attack_hand(var/mob/user as mob) +/obj/machinery/computer/communications/attack_hand(mob/user as mob) if(..(user)) return @@ -301,7 +407,7 @@ return if(!is_secure_level(src.z)) - to_chat(user, span_warning("Unable to establish a connection: You're too far away from the station!")) + to_chat(user, span_warning("Невозможно установить соединение: вы находитесь слишком далеко от станции!")) return ui_interact(user) @@ -315,10 +421,16 @@ /obj/machinery/computer/communications/ui_data(mob/user) var/list/data = list() data["is_ai"] = isAI(user) || isrobot(user) + data["noauthbutton"] = !ishuman(user) data["menu_state"] = data["is_ai"] ? ai_menu_state : menu_state data["emagged"] = emagged - data["authenticated"] = is_authenticated(user, 0) - data["authmax"] = data["authenticated"] == COMM_AUTHENTICATION_MAX ? TRUE : FALSE + data["authenticated"] = is_authenticated(user, FALSE) + data["authhead"] = data["authenticated"] >= COMM_AUTHENTICATION_HEAD && (data["authenticated"] == COMM_AUTHENTICATION_AGHOST || !isobserver(user)) + data["authcapt"] = data["authenticated"] >= COMM_AUTHENTICATION_CAPT && (data["authenticated"] == COMM_AUTHENTICATION_AGHOST || !isobserver(user)) + data["is_admin"] = data["authenticated"] >= COMM_AUTHENTICATION_CENTCOM && (data["authenticated"] == COMM_AUTHENTICATION_AGHOST || !isobserver(user)) + + data["gamma_armory_location"] = GLOB.gamma_ship_location + data["ert_allowed"] = !SSticker.mode.ert_disabled data["stat_display"] = list( "type" = display_type, @@ -327,16 +439,16 @@ "line_2" = (stat_msg2 ? stat_msg2 : "-----"), "presets" = list( - list("name" = "blank", "label" = "Clear", "desc" = "Blank slate"), - list("name" = "shuttle", "label" = "Shuttle ETA", "desc" = "Display how much time is left."), - list("name" = "message", "label" = "Message", "desc" = "A custom message.") + list("name" = "blank", "label" = "Чисто", "desc" = "Чистый лист"), + list("name" = "shuttle", "label" = "ETA шаттла", "desc" = "Показать, сколько времени осталось."), + list("name" = "message", "label" = "Сообщение", "desc" = "Пользовательское сообщение.") ), "alerts"=list( - list("alert" = "default", "label" = "Nanotrasen", "desc" = "Oh god."), - list("alert" = "redalert", "label" = "Red Alert", "desc" = "Nothing to do with communists."), - list("alert" = "lockdown", "label" = "Lockdown", "desc" = "Let everyone know they're on lockdown."), - list("alert" = "biohazard", "label" = "Biohazard", "desc" = "Great for virus outbreaks and parties."), + list("alert" = "default", "label" = "Нанотрейзен", "desc" = "О боже."), + list("alert" = "redalert", "label" = "Красная угроза", "desc" = "Ничего общего с коммунистами."), + list("alert" = "lockdown", "label" = "Локдаун", "desc" = "Сообщите всем, что они на карантине."), + list("alert" = "biohazard", "label" = "Биоугроза", "desc" = "Отлично подходит для вирусных вспышек и вечеринок."), ) ) @@ -351,10 +463,6 @@ else data["security_level_color"] = "purple"; data["str_security_level"] = capitalize(get_security_level()) - data["levels"] = list( - list("id" = SEC_LEVEL_GREEN, "name" = "Green", "icon" = "dove"), - list("id" = SEC_LEVEL_BLUE, "name" = "Blue", "icon" = "eye"), - ) var/list/msg_data = list() for(var/i = 1; i <= messagetext.len; i++) @@ -378,79 +486,92 @@ data["esc_status"] = FALSE if(SSshuttle.emergency.mode == SHUTTLE_CALL || SSshuttle.emergency.mode == SHUTTLE_RECALL) var/timeleft = SSshuttle.emergency.timeLeft() - data["esc_status"] = SSshuttle.emergency.mode == SHUTTLE_CALL ? "ETA:" : "RECALLING:" + data["esc_status"] = SSshuttle.emergency.mode == SHUTTLE_CALL ? "ETA:" : "ОТЗЫВ:" data["esc_status"] += " [timeleft / 60 % 60]:[add_zero(num2text(timeleft % 60), 2)]" else if(secondsToRefuel) - data["esc_status"] = "Refueling: [secondsToRefuel / 60 % 60]:[add_zero(num2text(secondsToRefuel % 60), 2)]" + data["esc_status"] = "Дозаправка: [secondsToRefuel / 60 % 60]:[add_zero(num2text(secondsToRefuel % 60), 2)]" data["esc_section"] = data["esc_status"] || data["esc_callable"] || data["esc_recallable"] || data["lastCallLoc"] return data -/obj/machinery/computer/communications/proc/setCurrentMessage(var/mob/user,var/value) +/obj/machinery/computer/communications/ui_static_data(mob/user) + var/list/data = list() + + data["levels"] = list( + list("id" = SEC_LEVEL_GREEN, "name" = "Зеленый", "icon" = "dove"), + list("id" = SEC_LEVEL_BLUE, "name" = "Синий", "icon" = "eye"), + ) + + data["admin_levels"] = list( + list("id" = SEC_LEVEL_RED, "name" = "Красный", "icon" = "exclamation"), + list("id" = SEC_LEVEL_GAMMA, "name" = "Гамма", "icon" = "biohazard"), + list("id" = SEC_LEVEL_EPSILON, "name" = "Эпсилон", "icon" = "skull", "tooltip" = "Код Эпсилон активируется только примерно через 15 секунд."), + list("id" = SEC_LEVEL_DELTA, "name" = "Дельта", "icon" = "bomb"), + ) + + return data + +/obj/machinery/computer/communications/proc/setCurrentMessage(mob/user, value) if(isAI(user) || isrobot(user)) aicurrmsg = value else currmsg = value -/obj/machinery/computer/communications/proc/getCurrentMessage(var/mob/user) +/obj/machinery/computer/communications/proc/getCurrentMessage(mob/user) if(isAI(user) || isrobot(user)) return aicurrmsg else return currmsg -/obj/machinery/computer/communications/proc/setMenuState(var/mob/user,var/value) +/obj/machinery/computer/communications/proc/setMenuState(mob/user, value) if(isAI(user) || isrobot(user)) ai_menu_state=value else menu_state=value /proc/call_shuttle_proc(mob/user, reason) - if(GLOB.sent_strike_team == TRUE || GLOB.security_level == SEC_LEVEL_EPSILON) - to_chat(user, span_warning("Central Command will not allow the shuttle to be called. Consider all contracts terminated.")) + if(!check_shuttle_ability(user)) return + SSshuttle.requestEvac(user, reason) + add_game_logs("has called the shuttle: [reason]", user) + message_admins("[key_name_admin(user)] has called the shuttle.") + + return + +/proc/check_shuttle_ability(mob/user) + if(GLOB.sent_strike_team == TRUE || GLOB.security_level == SEC_LEVEL_EPSILON) + to_chat(user, "Центральное командование не разрешит вызвать шаттл. Считать все контракты расторгнутыми.") + return FALSE + if(SSticker?.mode?.blob_stage >= BLOB_STAGE_FIRST && SSshuttle.emergencyNoEscape) - to_chat(user, span_warning("Under directive 7-10, [station_name()] is quarantined until further notice.")) - return + to_chat(user, span_warning("Согласно директиве 7-10, [station_name()] находится на карантине до дальнейшего уведомления.")) + return FALSE if(SSshuttle.emergencyNoEscape) - to_chat(user, span_warning("The emergency shuttle may not be sent at this time. Please try again later.")) - return + to_chat(user, "В настоящее время у Центрального командования нет свободного шаттла в вашем секторе. Пожалуйста, повторите попытку позже.") + return FALSE if(EMERGENCY_ESCAPED_OR_ENDGAMED) - to_chat(user, span_warning("The emergency shuttle may not be called while returning to Central Command.")) - return + to_chat(user, span_warning("Эвакуационный шаттл не может быть вызван при возвращении в Центральное командование.")) + return FALSE + if(world.time < 54000) // 30 minute grace period to let the game get going + to_chat(user, "Шаттл на дозаправке. Пожалуйста, подождите еще [round((54000-world.time)/600)] минут, прежде чем повторить попытку.") + return FALSE - SSshuttle.requestEvac(user, reason) - add_game_logs("has called the shuttle: [reason]", user) - message_admins("[key_name_admin(user)] has called the shuttle.") + return TRUE - return /proc/init_shift_change(mob/user, force = 0) // if force is 0, some things may stop the shuttle call - if(!force) - if(SSshuttle.emergencyNoEscape) - to_chat(user, "Central Command does not currently have a shuttle available in your sector. Please try again later.") - return - - if(GLOB.sent_strike_team == TRUE || GLOB.security_level == SEC_LEVEL_EPSILON) - to_chat(user, "Central Command will not allow the shuttle to be called. Consider all contracts terminated.") - return - - if(world.time < 54000) // 30 minute grace period to let the game get going - to_chat(user, "The shuttle is refueling. Please wait another [round((54000-world.time)/600)] minutes before trying again.") - return - - if(SSticker.mode.name == "epidemic") - to_chat(user, "Under directive 7-10, [station_name()] is quarantined until further notice.") - return + if(!force && !check_shuttle_ability(user)) + return if(seclevel2num(get_security_level()) >= SEC_LEVEL_RED) // There is a serious threat we gotta move no time to give them five minutes. - SSshuttle.emergency.request(null, 0.5, null, " Automatic Crew Transfer", 1) + SSshuttle.emergency.request(null, 0.5, null, " Автоматический Трансвер Экипажа", 1) SSshuttle.emergency.canRecall = FALSE else - SSshuttle.emergency.request(null, 1, null, " Automatic Crew Transfer", 0) + SSshuttle.emergency.request(null, 1, null, " Автоматический Трансвер Экипажа", 0) SSshuttle.emergency.canRecall = FALSE if(user) add_game_logs("has called the shuttle.", user) @@ -459,14 +580,14 @@ /proc/cancel_call_proc(mob/user) - if(SSticker.mode.name == "meteor") + if(GAMEMODE_IS_METEOR) return if(SSshuttle.cancelEvac(user)) add_game_logs("has recalled the shuttle.", user) message_admins("[ADMIN_LOOKUPFLW(user)] has recalled the shuttle .") else - to_chat(user, span_warning("Central Command has refused the recall request!")) + to_chat(user, span_warning("Центральное командование отклонило запрос об отзыве!")) add_game_logs("has tried and failed to recall the shuttle.", user) message_admins("[ADMIN_LOOKUPFLW(user)] has tried and failed to recall the shuttle.") @@ -503,7 +624,7 @@ SSshuttle.autoEvac() return ..() -/proc/print_command_report(text = "", title = "Central Command Update", add_to_records = TRUE, var/datum/station_goal/goal = null) +/proc/print_command_report(text = "", title = "Обновление Центрального Командования", add_to_records = TRUE, var/datum/station_goal/goal = null) for(var/obj/machinery/computer/communications/C in GLOB.shuttle_caller_list) if(!(C.stat & (BROKEN|NOPOWER)) && is_station_contact(C.z)) var/obj/item/paper/P = new (C.loc) @@ -516,7 +637,7 @@ P.stamp(/obj/item/stamp/navcom) goal.papers_list.Add(P) -/proc/print_centcom_report(text = "", title = "Incoming Message") +/proc/print_centcom_report(text = "", title = "Входящее сообщение") for(var/obj/machinery/computer/communications/C in GLOB.shuttle_caller_list) if(!(C.stat & (BROKEN|NOPOWER)) && is_admin_level(C.z)) var/obj/item/paper/P = new /obj/item/paper(C.loc) diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm index 1f0de6790d0..a12bbbf7104 100644 --- a/code/game/objects/items/stacks/sheets/glass.dm +++ b/code/game/objects/items/stacks/sheets/glass.dm @@ -116,6 +116,9 @@ GLOBAL_LIST_INIT(reinforced_glass_recipes, list ( \ merge_type = /obj/item/stack/sheet/rglass point_value = 4 +/obj/item/stack/sheet/rglass/fifty + amount = 50 + /obj/item/stack/sheet/rglass/cyborg materials = list() is_cyborg = 1 @@ -160,6 +163,9 @@ GLOBAL_LIST_INIT(pglass_recipes, list ( \ full_window = /obj/structure/window/full/plasmabasic point_value = 19 +/obj/item/stack/sheet/plasmaglass/fifty + amount = 50 + /obj/item/stack/sheet/plasmaglass/Initialize(mapload, new_amount, merge = TRUE) . = ..() recipes = GLOB.pglass_recipes @@ -209,6 +215,9 @@ GLOBAL_LIST_INIT(prglass_recipes, list ( \ full_window = /obj/structure/window/full/plasmareinforced point_value = 23 +/obj/item/stack/sheet/plasmarglass/fifty + amount = 50 + /obj/item/stack/sheet/plasmarglass/Initialize(mapload, new_amount, merge = TRUE) . = ..() recipes = GLOB.prglass_recipes @@ -229,6 +238,9 @@ GLOBAL_LIST_INIT(titaniumglass_recipes, list( merge_type = /obj/item/stack/sheet/titaniumglass full_window = /obj/structure/window/full/shuttle +/obj/item/stack/sheet/titaniumglass/fifty + amount = 50 + /obj/item/stack/sheet/titaniumglass/Initialize(mapload, new_amount, merge = TRUE) . = ..() recipes = GLOB.titaniumglass_recipes @@ -249,6 +261,9 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list( merge_type = /obj/item/stack/sheet/plastitaniumglass full_window = /obj/structure/window/plastitanium +/obj/item/stack/sheet/plastitaniumglass/fifty + amount = 50 + /obj/item/stack/sheet/plastitaniumglass/Initialize(mapload, new_amount, merge = TRUE) . = ..() recipes = GLOB.plastitaniumglass_recipes diff --git a/code/game/objects/items/stacks/sheets/mineral.dm b/code/game/objects/items/stacks/sheets/mineral.dm index 77862f73fb0..14cdfd12713 100644 --- a/code/game/objects/items/stacks/sheets/mineral.dm +++ b/code/game/objects/items/stacks/sheets/mineral.dm @@ -388,6 +388,9 @@ GLOBAL_LIST_INIT(titanium_recipes, list( materials = list(MAT_TITANIUM=2000, MAT_PLASMA=2000) point_value = 45 +/obj/item/stack/sheet/mineral/plastitanium/fifty + amount = 50 + GLOBAL_LIST_INIT(plastitanium_recipes, list( new/datum/stack_recipe("plas-titanium tile", /obj/item/stack/tile/mineral/plastitanium, 1, 4, 20), //adding syndie crate recipe here - Furu @@ -464,6 +467,9 @@ GLOBAL_LIST_INIT(plastitanium_recipes, list( merge_type = /obj/item/stack/sheet/mineral/adamantine wall_allowed = FALSE +/obj/item/stack/sheet/mineral/adamantine/fifty + amount = 50 + /obj/item/stack/sheet/mineral/adamantine/Initialize(mapload, new_amount, merge = TRUE) . = ..() recipes = GLOB.adamantine_recipes @@ -481,6 +487,9 @@ GLOBAL_LIST_INIT(plastitanium_recipes, list( merge_type = /obj/item/stack/sheet/mineral/mythril wall_allowed = FALSE +/obj/item/stack/sheet/mineral/mythril/fifty + amount = 50 + /* * Snow */ @@ -493,6 +502,9 @@ GLOBAL_LIST_INIT(plastitanium_recipes, list( throwforce = 2 merge_type = /obj/item/stack/sheet/mineral/snow +/obj/item/stack/sheet/mineral/snow/fifty + amount = 50 + /obj/item/stack/sheet/mineral/snow/Initialize(mapload, new_amount, merge = TRUE) . = ..() recipes = GLOB.snow_recipes diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm index 7cc81b6c4d8..0ec0bdaa95f 100644 --- a/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -174,6 +174,9 @@ GLOBAL_LIST_INIT(plasteel_recipes, list( merge_type = /obj/item/stack/sheet/plasteel point_value = 23 +/obj/item/stack/sheet/plasteel/fifty + amount = 50 + /obj/item/stack/sheet/plasteel/lowplasma desc = "This sheet is an alloy of iron and plasma. There are an special barcode 'Low Plasma Level'" materials = list(MAT_METAL=2000, MAT_PLASMA=400) diff --git a/code/game/objects/items/weapons/cards_ids.dm b/code/game/objects/items/weapons/cards_ids.dm index aee787d10a5..4912bfa0479 100644 --- a/code/game/objects/items/weapons/cards_ids.dm +++ b/code/game/objects/items/weapons/cards_ids.dm @@ -326,6 +326,7 @@ data["fprint_hash"] = fingerprint_hash data["access"] = access data["job"] = assignment + data["rank"] = rank data["account"] = associated_account_number data["owner"] = registered_name data["mining"] = mining_points @@ -340,6 +341,7 @@ fingerprint_hash = data["fprint_hash"] access = data["access"] // No need for a copy, the list isn't getting touched assignment = data["job"] + rank = data["rank"] associated_account_number = data["account"] registered_name = data["owner"] mining_points = data["mining"] diff --git a/code/game/objects/items/weapons/grenades/chem_grenade.dm b/code/game/objects/items/weapons/grenades/chem_grenade.dm index 9b254d6dde3..79f896c673b 100644 --- a/code/game/objects/items/weapons/grenades/chem_grenade.dm +++ b/code/game/objects/items/weapons/grenades/chem_grenade.dm @@ -589,6 +589,8 @@ icon = 'icons/obj/weapons/grenade.dmi' icon_state = "cleaner" stage = READY + /// The chemical used to clean things + var/cleaning_chem = "cleaner" /obj/item/grenade/chem_grenade/cleaner/Initialize(mapload) . = ..() @@ -597,12 +599,26 @@ var/obj/item/reagent_containers/glass/beaker/B2 = new(src) B1.reagents.add_reagent("fluorosurfactant", 40) - B2.reagents.add_reagent("cleaner", 10) + B2.reagents.add_reagent(cleaning_chem, 10) B2.reagents.add_reagent("water", 40) //when you make pre-designed foam reactions that carry the reagents, always add water last beakers += B1 beakers += B2 +/obj/item/grenade/chem_grenade/cleaner/everything + payload_name = "melter" + desc = "Внутри этой гранаты находятся наниты Синдиката с черного рынка, которые поглощают все, с чем сталкиваются. Органы, одежда, пульты, люди. Ничто не в безопасности.
Теперь с новым пенящимся аппликатором!" + cleaning_chem = "admincleaner_all" + +/obj/item/grenade/chem_grenade/cleaner/object + payload_name = "object dissolving" + desc = "Внутри этой гранаты находятся наниты Синдиката с черного рынка, которые, как ни странно, поглощают только предметы, оставляя живые существа и более крупные машины в покое.
Теперь с новым пенообразующим аппликатором!" + cleaning_chem = "admincleaner_item" + +/obj/item/grenade/chem_grenade/cleaner/organic + payload_name = "organic dissolving" + desc = "Внутри этой гранаты находятся наниты Синдиката с черного рынка, которые жаждут живых существ и их органов, кремниевых или органических, мертвых или живых.
Теперь с новым пенящимся аппликатором!" + cleaning_chem = "admincleaner_mob" /obj/item/grenade/chem_grenade/teargas payload_name = "teargas" diff --git a/code/game/objects/items/weapons/grenades/clusterbuster.dm b/code/game/objects/items/weapons/grenades/clusterbuster.dm index 7880b6753c5..503d6933b05 100644 --- a/code/game/objects/items/weapons/grenades/clusterbuster.dm +++ b/code/game/objects/items/weapons/grenades/clusterbuster.dm @@ -278,3 +278,16 @@ /obj/item/grenade/clusterbuster/mega_emp name = "Electromagnetic Storm" payload = /obj/item/grenade/clusterbuster/emp + + +/obj/item/grenade/clusterbuster/admincleaner + desc = "Для уборки действительно больших беспорядков." + payload = /obj/item/grenade/chem_grenade/cleaner/everything + +/obj/item/grenade/clusterbuster/admincleaner/organic + desc = "Для очистки множетсва остатков, на множетсве мест преступлений." + payload = /obj/item/grenade/chem_grenade/cleaner/organic + +/obj/item/grenade/clusterbuster/admincleaner/object + desc = "Для уборки типичной корпоративной вечеринки Nanotrasen." + payload = /obj/item/grenade/chem_grenade/cleaner/object diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index b61a2ad5162..53f34d46048 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -918,31 +918,31 @@ GLOBAL_VAR_INIT(nologevent, 0) //returns 1 to let the dragdrop code know we are trapping this event //returns 0 if we don't plan to trap the event -/datum/admins/proc/cmd_ghost_drag(var/mob/dead/observer/frommob, var/tothing) +/datum/admins/proc/cmd_ghost_drag(mob/dead/observer/frommob, atom/tothing) if(!istype(frommob)) return //extra sanity check to make sure only observers are shoved into things //same as assume-direct-control perm requirements. if(!check_rights(R_VAREDIT,0)) //no varedit, check if they have r_admin and r_debug if(!check_rights(R_ADMIN|R_DEBUG,0)) //if they don't have r_admin and r_debug, return - return 0 //otherwise, if they have no varedit, but do have r_admin and r_debug, execute the rest of the code + return FALSE //otherwise, if they have no varedit, but do have r_admin and r_debug, execute the rest of the code if(!frommob.ckey) - return 0 + return FALSE if(isitem(tothing)) var/mob/living/toitem = tothing - var/ask = alert("Are you sure you want to allow [frommob.name]([frommob.key]) to possess [toitem.name]?", "Place ghost in control of item?", "Yes", "No") - if(ask != "Yes") - return 1 + var/ask = tgui_alert(usr, "Вы уверены, что хотите разрешить [frommob.declent_ru(DATIVE)]([frommob.key]) управлять [toitem.declent_ru(INSTRUMENTAL)]?", "Поместить призрака управлять предметом?", list("Да", "Нет")) + if(ask != "Да") + return TRUE if(!frommob || !toitem) //make sure the mobs don't go away while we waited for a response - return 1 + return TRUE var/mob/living/simple_animal/possessed_object/tomob = new(toitem) - message_admins("[key_name_admin(usr)] has put [frommob.ckey] in control of [tomob.name].") + message_admins(span_adminnotice("[key_name_admin(usr)] has put [frommob.ckey] in control of [tomob.name].")) log_admin("[key_name(usr)] stuffed [frommob.ckey] into [tomob.name].") SSblackbox.record_feedback("tally", "admin_verb", 1, "Ghost Drag") @@ -956,26 +956,53 @@ GLOBAL_VAR_INIT(nologevent, 0) var/question = "" if(tomob.ckey) question = "This mob already has a user ([tomob.key]) in control of it! " - question += "Are you sure you want to place [frommob.name]([frommob.key]) in control of [tomob.name]?" + question += "Вы уверены, что хотите разрешить [frommob.declent_ru(DATIVE)]([frommob.key]) управлять [tomob.declent_ru(INSTRUMENTAL)]?" - var/ask = alert(question, "Place ghost in control of mob?", "Yes", "No") - if(ask != "Yes") - return 1 + var/ask = tgui_alert(usr, question, "Поместить призрака управлять существом?", list("Да", "Нет")) + if(ask != "Да") + return TRUE if(!frommob || !tomob) //make sure the mobs don't go away while we waited for a response - return 1 + return TRUE if(tomob.client) //no need to ghostize if there is no client tomob.ghostize(0) - message_admins("[key_name_admin(usr)] has put [frommob.ckey] in control of [tomob.name].") + message_admins(span_adminnotice("[key_name_admin(usr)] has put [frommob.ckey] in control of [tomob.name].")) log_admin("[key_name(usr)] stuffed [frommob.ckey] into [tomob.name].") SSblackbox.record_feedback("tally", "admin_verb", 1, "Ghost Drag") tomob.ckey = frommob.ckey qdel(frommob) - return 1 + return TRUE + + if(istype(tothing, /obj/structure/AIcore/deactivated)) + + var/question = "Вы уверены, что хотите разрешить [frommob.declent_ru(DATIVE)]([frommob.key]) управлять пустым ядром ИИ?" + + var/ask = tgui_alert(usr, question, "Поместить призрака управлять пустым ядром ИИ?", list("Да", "Нет")) + if(ask != "Да") + return TRUE + + if(QDELETED(frommob) || QDELETED(tothing)) //make sure the mobs don't go away while we waited for a response + return TRUE + + message_admins(span_adminnotice("[key_name_admin(usr)] has put [frommob.ckey] in control of an empty AI core.")) + log_admin("[key_name(usr)] stuffed [frommob.ckey] into an empty AI core.") + SSblackbox.record_feedback("tally", "admin_verb", 1, "Ghost Drag") + + var/transfer_key = frommob.key // frommob is qdel'd in frommob.AIize() + var/mob/living/silicon/ai/ai_character = frommob.AIize() + ai_character.key = transfer_key // this wont occur in mind transferring if the mind is not active, which causes some weird stuff. This fixes it. + GLOB.empty_playable_ai_cores -= tothing + + ai_character.forceMove(get_turf(tothing)) + ai_character.view_core() + + qdel(tothing) + + return TRUE // Returns a list of the number of admins in various categories // result[1] is the number of staff that match the rank mask and are active diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 33e7bbaa412..5cfd7971f3d 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -53,6 +53,7 @@ GLOBAL_LIST_INIT(admin_verbs_admin, list( /client/proc/delbook, /client/proc/view_flagged_books, /client/proc/view_asays, + /client/proc/view_msays, /client/proc/empty_ai_core_toggle_latejoin, /client/proc/aooc, /client/proc/freeze, @@ -215,6 +216,7 @@ GLOBAL_LIST_INIT(admin_verbs_mod, list( /client/proc/dsay, /datum/admins/proc/show_player_panel, /client/proc/ban_panel, + /client/proc/view_asays, /client/proc/debug_variables, /*allows us to -see- the variables of any instance in the game. +VAREDIT needed to modify*/ /client/proc/openAdminTicketUI, )) @@ -224,6 +226,7 @@ GLOBAL_LIST_INIT(admin_verbs_mentor, list( /client/proc/cmd_admin_pm_by_key_panel, /*admin-pm list by key*/ /client/proc/openMentorTicketUI, /client/proc/cmd_mentor_say, /* mentor say*/ + /client/proc/view_msays, // cmd_mentor_say is added/removed by the toggle_mentor_chat verb )) GLOBAL_LIST_INIT(admin_verbs_proccall, list( @@ -344,6 +347,7 @@ GLOBAL_LIST_INIT(view_runtimes_verbs, list( if(istype(mob,/mob/dead/observer)) //re-enter var/mob/dead/observer/ghost = mob + var/old_turf = get_turf(ghost) ghost.can_reenter_corpse = 1 //just in-case. ghost.reenter_corpse() log_admin("[key_name(usr)] re-entered their body") @@ -351,6 +355,9 @@ GLOBAL_LIST_INIT(view_runtimes_verbs, list( if(ishuman(mob)) var/mob/living/carbon/human/H = mob H.regenerate_icons() // workaround for #13269 + if(isAI(mob)) // client.mob, built in byond client var + var/mob/living/silicon/ai/ai = mob + ai.eyeobj.setLoc(old_turf) else if(isnewplayer(mob)) to_chat(src, "Error: Aghost: Can't admin-ghost whilst in the lobby. Join or observe first.", confidential=TRUE) else @@ -705,6 +712,8 @@ GLOBAL_LIST_INIT(view_runtimes_verbs, list( ptypes += "Crew Traitor" ptypes += "Floor Cluwne" ptypes += "Shamebrero" + ptypes += "Nugget" + ptypes += "Rod" ptypes += "Dust" ptypes += "Shitcurity Goblin" ptypes += "High RP" @@ -822,6 +831,24 @@ GLOBAL_LIST_INIT(view_runtimes_verbs, list( var/obj/item/clothing/head/sombrero/shamebrero/S = new(H.loc) H.equip_to_slot_or_del(S, ITEM_SLOT_HEAD) logmsg = "shamebrero" + + if("Nugget") + H.Weaken(12 SECONDS, TRUE) + H.AdjustJitter(40 SECONDS) + to_chat(H, span_danger("Вы чувствуете, как будто ваши конечности отрывают от вашего тела!")) + addtimer(CALLBACK(H, TYPE_PROC_REF(/mob/living/carbon/human, make_nugget)), 6 SECONDS) + logmsg = "nugget" + + if("Rod") + + var/starting_turf_x = M.x + rand(10, 15) * pick(1, -1) + var/starting_turf_y = M.y + rand(10, 15) * pick(1, -1) + var/turf/start = locate(starting_turf_x, starting_turf_y, M.z) + + var/obj/effect/immovablerod/smite/rod = new (start, M) + rod.go_for_a_walk(M) + logmsg = "a rod" + if("Dust") H.dust() logmsg = "dust" diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm index f2a941ead28..89d67b8829d 100644 --- a/code/modules/admin/holder2.dm +++ b/code/modules/admin/holder2.dm @@ -140,3 +140,27 @@ you will have to do something like if(client.holder.rights & R_ADMIN) yourself. /datum/admins/can_vv_delete() return FALSE // don't break shit either + + +/** + * Requires the holder to have all the rights specified + * + * rights_required = R_ADMIN|R_EVENT means they must have both flags, or it will return false + */ +/proc/check_rights_all(rights_required, show_msg = TRUE, mob/user = usr) + if(!user?.client) + return FALSE + if(!rights_required) + if(user.client.holder) + return TRUE + if(show_msg) + to_chat(user, "Ошибка: Вы не админ.") + return FALSE + + if(!user.client.holder) + return FALSE + if((user.client.holder.rights & rights_required) == rights_required) + return TRUE + if(show_msg) + to_chat(user, "Ошибка: У вас недостаточно прав для этого. Вам необходимы следущие флаги:[rights2text(rights_required, " ")].") + return FALSE diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index d2cb55f5f94..b746963fca7 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1572,11 +1572,17 @@ usr.client.update_mob_sprite(M) else if(href_list["asays"]) - if(!check_rights(R_ADMIN)) + if(!check_rights(R_ADMIN | R_MOD)) return usr.client.view_asays() + else if(href_list["msays"]) + if(!check_rights(R_ADMIN | R_MENTOR)) + return + + usr.client.view_msays() + else if(href_list["tdome1"]) if(!check_rights(R_SERVER|R_EVENT)) return @@ -3531,6 +3537,7 @@ if(!SSshuttle.toggleShuttle("gamma_shuttle","gamma_home","gamma_away", TRUE)) message_admins("[key_name_admin(usr)] moved the gamma armory") log_admin("[key_name(usr)] moved the gamma armory") + GLOB.gamma_ship_location = !GLOB.gamma_ship_location if(usr) log_admin("[key_name(usr)] used secret [href_list["secretsfun"]]") @@ -3884,7 +3891,13 @@ if(!target) return // The way admin jump links handle their src is weirdly inconsistent... - . = ADMIN_FLW(target,"FLW") + if(isclient(target)) + var/client/C = target + if(C.mob) + target = C.mob + + . = ADMIN_FLW(target, "FLW") + if(isAI(target)) // AI core/eye follow links var/mob/living/silicon/ai/A = target if(A.client && A.eyeobj) // No point following clientless AI eyes diff --git a/code/modules/admin/verbs/adminsay.dm b/code/modules/admin/verbs/adminsay.dm index 97cfd18f369..e0a83fc836c 100644 --- a/code/modules/admin/verbs/adminsay.dm +++ b/code/modules/admin/verbs/adminsay.dm @@ -10,7 +10,7 @@ msg = handleDiscordEmojis(msg) - var/datum/asays/asay = new(usr.ckey, usr.client.holder.rank, msg, world.timeofday) + var/datum/say/asay = new(usr.ckey, usr.client.holder.rank, msg, world.timeofday) GLOB.asays += asay log_adminsay(msg, src) @@ -44,7 +44,8 @@ msg = sanitize(copytext_char(msg, 1, MAX_MESSAGE_LEN)) log_mentorsay(msg, src) - + var/datum/say/msay = new(usr.ckey, usr.client.holder.rank, msg, world.timeofday) + GLOB.msays += msay if(!msg) return diff --git a/code/modules/admin/verbs/asays.dm b/code/modules/admin/verbs/asays.dm index cad474d4bcb..fe75f8ef958 100644 --- a/code/modules/admin/verbs/asays.dm +++ b/code/modules/admin/verbs/asays.dm @@ -1,25 +1,40 @@ GLOBAL_LIST_EMPTY(asays) +GLOBAL_LIST_EMPTY(msays) -/datum/asays +/datum/say var/ckey var/rank var/message var/time -/datum/asays/New(ckey = "", rank = "", message = "", time = 0) +/datum/say/New(ckey = "", rank = "", message = "", time = 0) src.ckey = ckey src.rank = rank src.message = message src.time = time +/client/proc/view_msays() + set name = "Msays" + set desc = "View Msays from the current round." + set category = "Admin" + + if(!check_rights(R_MENTOR | R_ADMIN)) + return + + display_says(GLOB.msays, "msay") + /client/proc/view_asays() set name = "Asays" set desc = "View Asays from the current round." set category = "Admin" - if(!check_rights(R_ADMIN)) + if(!check_rights(R_ADMIN | R_MOD)) return + display_says(GLOB.asays, "asay") + +/client/proc/display_says(list/say_list, title) + var/list/output = list({" - Refresh + Refresh "}) @@ -51,7 +66,7 @@ GLOBAL_LIST_EMPTY(asays) "} - for(var/datum/asays/A in GLOB.asays) + for(var/datum/say/A in say_list) var/timestr = time2text(A.time, "hh:mm:ss") output += {" @@ -65,6 +80,6 @@ GLOBAL_LIST_EMPTY(asays)
"} - var/datum/browser/popup = new(usr, "asays", "
Current Round Asays
", 1200, 825) + var/datum/browser/popup = new(usr, title, "
Current Round [capitalize(title)]s
", 1200, 825) popup.set_content(output.Join()) popup.open(0) diff --git a/code/modules/admin/verbs/pray.dm b/code/modules/admin/verbs/pray.dm index 1e5097383cc..b10da6d9201 100644 --- a/code/modules/admin/verbs/pray.dm +++ b/code/modules/admin/verbs/pray.dm @@ -1,7 +1,3 @@ -#define NUKE_INTACT 0 -#define NUKE_CORE_MISSING 1 -#define NUKE_MISSING 2 - /mob/living/verb/pray(msg as text) set category = "IC" set name = "Pray" @@ -105,6 +101,3 @@ to_chat(X, "The nuclear device does not have a core, and will not arm!") if(X.prefs.sound & SOUND_ADMINHELP) X << 'sound/effects/adminhelp.ogg' -#undef NUKE_INTACT -#undef NUKE_CORE_MISSING -#undef NUKE_MISSING diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 33ee500e67e..8cfe8631fce 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -627,39 +627,39 @@ Traitors and the like can also be revived with the previous role mostly intact. return //the stuff on the list is |"report type" = "report title"|, if that makes any sense - var/list/MsgType = list("Central Command Report" = "Nanotrasen Update", - "Syndicate Communique" = "Syndicate Message", - "Space Wizard Federation Message" = "Sorcerous Message", - "Spider Clan Сommunique" = "Spider Clan Message", - "Enemy Communications" = "Unknown Message", - "Custom" = "Cryptic Message") + var/list/MsgType = list("Сообщение Центрального Командования" = "Обновление Нанотрейзен", + "Официальное сообщение Синдиката" = "Сообщение Синдиката", + "Сообщение Федерации Космических Волшебников" = "Заколдованное сообщение", + "Официальное сообщение Клана Паука" = "Сообщение Клана Паука", + "Вражеское Сообщение" = "Неизвестное сообщение", + "Свой тип" = "Загадочное сообщение") var/list/MsgSound = list("Beep" = 'sound/misc/announce_dig.ogg', "Enemy Communications Intercepted" = 'sound/AI/intercept2.ogg', "New Command Report Created" = 'sound/AI/commandreport.ogg') - var/type = input(usr, "Pick a type of report to send", "Report Type", "") as anything in MsgType + var/type = tgui_input_list(usr, "Выберите тип сообщения для отправки.", "Тип сообщения", MsgType, "") - if(type == "Custom") - type = clean_input("What would you like the report type to be?", "Report Type", "Encrypted Transmission") + if(type == "Свой тип") + type = tgui_input_text(usr, "Введите тип сообщения.", "Тип сообщения", "Зашифрованная передача") - var/customname = input(usr, "Pick a title for the report.", "Title", MsgType[type]) as text|null + var/customname = tgui_input_text(usr, "Введите заголовок сообщения.", "Заголовок", MsgType[type]) if(!customname) return - var/input = input(usr, "Please enter anything you want. Anything. Serious.", "What's the message?") as message|null + var/input = tgui_input_text(usr, "Введите все, что хотите. Что угодно. Серьезно.", "Какое сообщение?", multiline = TRUE) if(!input) return - switch(alert("Should this be announced to the general population?",,"Yes","No", "Cancel")) - if("Yes") - var/beepsound = input(usr, "What sound should the announcement make?", "Announcement Sound", "") as anything in MsgSound + switch(tgui_alert(usr, "Должно ли это быть объявлено всем?",, list("Да","Нет", "Отмена"))) + if("Да") + var/beepsound = tgui_input_list(usr, "Какой звук должен издавать анонс?", "Звук анонса", MsgSound) GLOB.command_announcement.Announce(input, customname, MsgSound[beepsound], , , type) print_command_report(input, customname) - if("No") + if("Нет") //same thing as the blob stuff - it's not public, so it's classified, dammit - GLOB.command_announcer.autosay("A classified message has been printed out at all communication consoles.") - print_command_report(input, "Classified: [customname]") + GLOB.event_announcement.Announce("Отчёт был загружен и распечатан на всех консолях связи.", "Входящее засекреченное сообщение.", 'sound/AI/commandreport.ogg', from = "[command_name()] обновление") + print_command_report(input, "Секретно: [customname]") else return diff --git a/code/modules/buildmode/submodes/fill.dm b/code/modules/buildmode/submodes/fill.dm index 3c06a199e47..72b59845bd8 100644 --- a/code/modules/buildmode/submodes/fill.dm +++ b/code/modules/buildmode/submodes/fill.dm @@ -5,29 +5,40 @@ var/objholder = null /datum/buildmode_mode/fill/show_help(mob/user) - to_chat(user, "***********************************************************") - to_chat(user, "Left Mouse Button on turf/obj/mob = Select corner") - to_chat(user, "Left Mouse Button + Alt on turf/obj/mob = Delete region") - to_chat(user, "Right Mouse Button on buildmode button = Select object type") - to_chat(user, "***********************************************************") + to_chat(user, span_notice("***********************************************************")) + to_chat(user, span_notice("ЛКМ на turf/obj/mob = Выбрать угол")) + to_chat(user, span_notice("ЛКМ + Alt на turf/obj/mob = Удалить регион")) + to_chat(user, span_notice("ПКМ по кнопке билдмода = Выбрать тип")) + to_chat(user, span_notice("ЛКМ + Alt на turf/obj = Копировать тип объекта")) + to_chat(user, span_notice("***********************************************************")) /datum/buildmode_mode/fill/change_settings(mob/user) - var/target_path = input(user,"Enter typepath:" ,"Typepath","/obj/structure/closet") + var/target_path = tgui_input_text(user,"Введите путь типа:" ,"Путь типа", "/obj/structure/closet") objholder = text2path(target_path) if(!ispath(objholder)) objholder = pick_closest_path(target_path) if(!objholder) - alert("No path has been selected.") + tgui_alert(user, "Путь не выбран.") return else if(ispath(objholder, /area)) objholder = null - alert("Area paths are not supported for this mode, use the area edit mode instead.") + tgui_alert(user, "В этом режиме не поддерживаются пути к областям, вместо этого используйте режим редактирования областей.") return deselect_region() /datum/buildmode_mode/fill/handle_click(mob/user, params, obj/object) + var/list/pa = params2list(params) + var/alt_click = pa.Find("alt") + var/left_click = pa.Find("left") + if(left_click && alt_click) + if(isturf(object) || isobj(object) || ismob(object)) + objholder = object.type + to_chat(user, span_notice("[capitalize(object.declent_ru(NOMINATIVE))] ([object.type]) выбран[genderize_ru(object.gender, "", "a", "о", "ы")].")) + return + else + to_chat(user, span_notice("[capitalize(object.declent_ru(NOMINATIVE))] не турф, объект, или существо! Пожалуйста, выберите еще раз.")) if(isnull(objholder)) - to_chat(user, "Select an object type first.") + to_chat(user, span_warning("Сначала выберите тип объекта.")) deselect_region() return ..() diff --git a/code/modules/buildmode/submodes/forcemove.dm b/code/modules/buildmode/submodes/forcemove.dm new file mode 100644 index 00000000000..77a84e1fa27 --- /dev/null +++ b/code/modules/buildmode/submodes/forcemove.dm @@ -0,0 +1,78 @@ +// This tool calls forceMove() on an object/mob that is selected with right-click. +// Once the atom is moved, its selection is cleared to avoid any further moves by accident. +/datum/buildmode_mode/forcemove + key = "forcemove" + var/atom/movable/selected_atom // The atom we want to move + var/image/selected_overlay // Overlay for the selected atom only visible for the build mode user + +/datum/buildmode_mode/forcemove/show_help(mob/user) + to_chat(user, span_notice("***********************************************************")) + to_chat(user, span_notice("ЛКМ на obj/mob = Выбрать точку назначения")) + to_chat(user, span_notice("ПКМ на obj/mob = Выбрать атом для перемещения")) + to_chat(user, span_notice("Замечание: Сначала вам нужно выбрать атом, который может быть перемещен, а затем щелкнуть левой кнопкой мыши по месту назначения.")) + to_chat(user, span_notice("***********************************************************")) + +/datum/buildmode_mode/forcemove/handle_click(mob/user, params, atom/A) + var/list/pa = params2list(params) + var/left_click = pa.Find("left") + var/right_click = pa.Find("right") + + // Selecting the atom to move + if(right_click) + if(!ismovable(A) || iseffect(A)) + to_chat(user, span_danger("Вы мен можете перемещеть эффекты, турфы и зоны.")) + return + + // If we had something previously selected, handle its signal and overlay + if(selected_atom) + UnregisterSignal(selected_atom, COMSIG_QDELETING) + + if(selected_overlay) + remove_selected_overlay(user) + + // We ensure it properly gets GC'd + selected_atom = A + RegisterSignal(selected_atom, COMSIG_QDELETING, PROC_REF(on_selected_atom_deleted)) + + // Green overlay for selection + selected_overlay = image(icon = selected_atom.icon, loc = A, icon_state = selected_atom.icon_state) + selected_overlay.color = "#15d12d" + user.client.images += selected_overlay + + to_chat(user, span_notice("'[capitalize(selected_atom.declent_ru(NOMINATIVE))]' [genderize_ru(selected_atom.gender, "выбран", "выбрана", "выбрано", "выбраны")] для перемещения.")) + return + + // Selecting the destination to move to + if(!left_click) + return + + var/atom/destination = A + + if(!selected_atom) + to_chat(user, span_danger("Выберите атом для перемещения (ПКМ).")) + return + + // Block these as they can only lead to issues + if(iseffect(destination) || isobserver(destination)) + to_chat(user, span_danger("Нельзя перемещать атомы в эффекты или в призраков.")) + return + + selected_atom.forceMove(destination) + + to_chat(user, span_notice("'[capitalize(selected_atom.declent_ru(NOMINATIVE))]' [genderize_ru(selected_atom.gender, "перемещён", "перемещена", "перемещено", "перемещены")] '[destination.declent_ru(ACCUSATIVE)]'.")) + log_admin("Build Mode: [key_name(user)] forcemoved [selected_atom] to [destination] at ([destination.x],[destination.y],[destination.z]).") + + UnregisterSignal(selected_atom, COMSIG_QDELETING) + selected_atom = null + remove_selected_overlay(user) + +// Remove the green selection (only visible for the user) +/datum/buildmode_mode/forcemove/proc/remove_selected_overlay(mob/user) + user.client.images -= selected_overlay + selected_overlay = null + +// If it gets deleted mid-movement, remove the overlay and its attachment to the forcemove tool +/datum/buildmode_mode/forcemove/proc/on_selected_atom_deleted() + SIGNAL_HANDLER + UnregisterSignal(selected_atom, COMSIG_QDELETING) + selected_atom = null diff --git a/code/modules/buildmode/submodes/offer.dm b/code/modules/buildmode/submodes/offer.dm new file mode 100644 index 00000000000..94d0ad2b4c0 --- /dev/null +++ b/code/modules/buildmode/submodes/offer.dm @@ -0,0 +1,28 @@ +// Speeds up the offering process and optionally allows the admin to set up playtime requirement and "Show role" only once. +// Default setting is 20H like the default recommendation for offering from drop down menu. +/datum/buildmode_mode/offer + key = "offer" + var/hours = 20 + var/hide_role + +/datum/buildmode_mode/offer/show_help(mob/user) + to_chat(user, span_notice("***********************************************************")) + to_chat(user, span_notice("ЛКМ по существу, чтобы предложить его")) + to_chat(user, span_notice("ПКМ по кнопке билдмода, чтобы изменить необходимое количество часов, необходимое для управления существом")) + to_chat(user, span_notice("***********************************************************")) + +/datum/buildmode_mode/offer/change_settings(mob/user) + hours = tgui_input_number(user, "Необходимое число часов", "Ввод", 20) + if(tgui_alert(user, "Хотите показывать спецроль существа?", null, list("Да", "Нет")) == "Да") + hide_role = FALSE + else + hide_role = TRUE + +/datum/buildmode_mode/offer/handle_click(mob/user, params, atom/A) + var/list/modifiers = params2list(params) + var/left_click = LAZYACCESS(modifiers, LEFT_CLICK) + var/selected_atom + + if(left_click && ismob(A) && !isobserver(A)) + selected_atom = A + offer_control(selected_atom, hours, hide_role) diff --git a/code/modules/buildmode/submodes/say.dm b/code/modules/buildmode/submodes/say.dm new file mode 100644 index 00000000000..6098e5c86cf --- /dev/null +++ b/code/modules/buildmode/submodes/say.dm @@ -0,0 +1,36 @@ +/datum/buildmode_mode/say + key = "say" + +/datum/buildmode_mode/say/show_help(mob/user) + to_chat(user, span_notice("***********************************************************")) + to_chat(user, span_notice("ЛКМ = Сказать")) + to_chat(user, span_notice("ПКМ = Издать эмоцию")) + to_chat(user, span_notice("***********************************************************")) + +/datum/buildmode_mode/say/handle_click(mob/user, params, atom/object) + if(ismob(object)) + var/mob/target = object + if(!isnull(target.ckey)) + tgui_alert(usr, "Это нельзя использовать на мобах с сикеем. Вместо этого используйте Forcesay на панели игрока.") + return + + var/list/pa = params2list(params) + var/left_click = pa.Find("left") + var/right_click = pa.Find("right") + + if(left_click) + var/say = tgui_input_text(user, "Что должен [object.declent_ru(NOMINATIVE)] сказать?", "Что сказать?") + if(isnull(say)) + return + log_admin("Build Mode: [key_name(user)] made [object] at ([object.x],[object.y],[object.z] say [say].") + message_admins(span_notice("Build Mode: [key_name(user)] made [object] at ([object.x],[object.y],[object.z] say [say].")) + user.create_log(MISC_LOG, "Made [object] at ([object.x],[object.y],[object.z] say [say].") + object.atom_say(say) + else if(right_click) + var/emote = tgui_input_text(user, "Что должен [object.declent_ru(NOMINATIVE)] сделать?", "Какую эмоцию?") + if(isnull(emote)) + return + log_admin("Build Mode: [key_name(user)] made [object] at ([object.x],[object.y],[object.z] emote *[emote].") + message_admins(span_notice("Build Mode: [key_name(user)] made [object] at ([object.x],[object.y],[object.z] emote *[emote].")) + user.create_log(MISC_LOG, "Made [object] at ([object.x],[object.y],[object.z] emote *[emote].") + object.atom_emote(emote) diff --git a/code/modules/clothing/glasses/hud.dm b/code/modules/clothing/glasses/hud.dm index 672c8fa1dd6..e94b89f3281 100644 --- a/code/modules/clothing/glasses/hud.dm +++ b/code/modules/clothing/glasses/hud.dm @@ -13,6 +13,12 @@ if(!istype(user) || !HUDType || !(slot & (ITEM_SLOT_EYES|ITEM_SLOT_HEAD))) return . + if(islist(HUDType)) + for(var/new_hud in HUDType) + var/datum/atom_hud/hud = GLOB.huds[new_hud] + hud.add_hud_to(user) + return . + var/datum/atom_hud/hud = GLOB.huds[HUDType] hud.add_hud_to(user) @@ -22,6 +28,12 @@ if(!istype(user) || !HUDType || !(slot & (ITEM_SLOT_EYES|ITEM_SLOT_HEAD))) return . + if(islist(HUDType)) + for(var/new_hud in HUDType) + var/datum/atom_hud/hud = GLOB.huds[new_hud] + hud.remove_hud_from(user) + return . + var/datum/atom_hud/hud = GLOB.huds[HUDType] hud.remove_hud_from(user) diff --git a/code/modules/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm index 323c7ad9457..f0d09bcda3f 100644 --- a/code/modules/clothing/suits/miscellaneous.dm +++ b/code/modules/clothing/suits/miscellaneous.dm @@ -1121,12 +1121,6 @@ siemens_coefficient = 0 var/on = 0 - -/obj/item/clothing/suit/advanced_protective_suit/Initialize(mapload) - . = ..() - ADD_TRAIT(src, TRAIT_NODROP, INNATE_TRAIT) - - /obj/item/clothing/suit/advanced_protective_suit/Destroy() if(on) on = 0 @@ -1136,9 +1130,11 @@ /obj/item/clothing/suit/advanced_protective_suit/ui_action_click(mob/user, datum/action/action, leftclick) if(on) on = 0 + REMOVE_TRAIT(src, TRAIT_NODROP, INNATE_TRAIT) to_chat(usr, "You turn the suit's special processes off.") else on = 1 + ADD_TRAIT(src, TRAIT_NODROP, INNATE_TRAIT) to_chat(usr, "You turn the suit's special processes on.") START_PROCESSING(SSobj, src) diff --git a/code/modules/clothing/under/miscellaneous.dm b/code/modules/clothing/under/miscellaneous.dm index e84df73a48c..6d2aa511101 100644 --- a/code/modules/clothing/under/miscellaneous.dm +++ b/code/modules/clothing/under/miscellaneous.dm @@ -251,6 +251,14 @@ /obj/item/clothing/under/acj name = "administrative cybernetic jumpsuit" + ru_names = list( + NOMINATIVE = "административный кибернетический комбинезон", + GENITIVE = "административного кибернетического комбинезона", + DATIVE = "административному кибернетическому комбинезону", + ACCUSATIVE = "административный кибернетический комбинезон", + INSTRUMENTAL = "административным кибернетическим комбинезоном", + PREPOSITIONAL = "административном кибернетическом комбинезоне", + ) icon_state = "syndicate" item_state = "bl_suit" item_color = "syndicate" @@ -1216,4 +1224,3 @@ SPECIES_NEARA = 'icons/mob/clothing/species/monkey/uniform.dmi', SPECIES_STOK = 'icons/mob/clothing/species/monkey/uniform.dmi' ) - diff --git a/code/modules/events/immovable_rod.dm b/code/modules/events/immovable_rod.dm index 563af2dd79c..a239e2e54c9 100644 --- a/code/modules/events/immovable_rod.dm +++ b/code/modules/events/immovable_rod.dm @@ -291,6 +291,29 @@ In my current plan for it, 'solid' will be defined as anything with density == 1 SSmove_manager.stop_looping(src) +/obj/effect/immovablerod/smite + /// The target that we're gonna aim for between start and end + var/obj/effect/portal/exit + var/turf/end + admin_spawned = TRUE + +/obj/effect/immovablerod/smite/Initialize(mapload, atom/target_atom, atom/special_target, move_delay, force_looping) + new /obj/effect/portal(mapload, null, null, 2 SECONDS) + end = get_turf(target_atom) + return ..() + +/obj/effect/immovablerod/smite/Move() + . = ..() + if(get_turf(src) == end) + // our exit condition: get outta there kowalski + var/target_turf = get_ranged_target_turf(src, dir, rand(1, 10)) + walk(src, 0) + exit = new /obj/effect/portal(target_turf, null, null, 2 SECONDS) + walk_towards(src, exit, move_delay) + else if(locate(exit) in get_turf(src)) + QDEL_NULL(exit) + qdel(src) + /** * Allows your rod to release restraint level zero and go for a walk. * diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index 1f0cefb8662..ff6e8d6f2ae 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -187,3 +187,10 @@ for(var/trait_source in GET_TRAIT_SOURCES(src, TRAIT_NO_CLONE)) REMOVE_TRAIT(src, TRAIT_NO_CLONE, trait_source) +/mob/living/carbon/human/proc/make_nugget(mob/living) + for(var/obj/item/organ/external/limb as anything in bodyparts) + if(limb.limb_body_flag == LEG_RIGHT || limb.limb_body_flag == LEG_LEFT || limb.limb_body_flag == ARM_RIGHT || limb.limb_body_flag == ARM_LEFT) + limb.droplimb() + emote("scream") + playsound(src, 'sound/misc/desceration-03.ogg', 70) + diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index b221298d294..57285b53b3d 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -1521,3 +1521,11 @@ GLOBAL_LIST_INIT(ai_verbs_default, list( SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT) sync_lighting_plane_alpha() + + +/mob/living/silicon/ai/ghostize(can_reenter_corpse) + var/old_turf = get_turf(eyeobj) + . = ..() + if(isobserver(.)) + var/mob/dead/observer/ghost = . + ghost.forceMove(old_turf) diff --git a/code/modules/mob/living/silicon/ai/latejoin.dm b/code/modules/mob/living/silicon/ai/latejoin.dm index e7d73caf56a..8d547e0d37c 100644 --- a/code/modules/mob/living/silicon/ai/latejoin.dm +++ b/code/modules/mob/living/silicon/ai/latejoin.dm @@ -26,6 +26,8 @@ GLOBAL_LIST_EMPTY(empty_playable_ai_cores) mind.objectives.Cut() mind.special_role = null + view_core() + // Ghost the current player and allow or disallow them to respawn, depends on time if(TOO_EARLY_TO_GHOST) ghostize(FALSE) diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index fd397aea8e3..8a5864bb28d 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -94,30 +94,40 @@ return U.sensor_mode return SUIT_SENSOR_OFF -/proc/offer_control(mob/M) - to_chat(M, "Control of your mob has been offered to dead players.") - log_admin("[key_name(usr)] has offered control of ([key_name(M)]) to ghosts.") - var/minhours = input(usr, "Minimum hours required to play [M]?", "Set Min Hrs", 10) as null|num - if(isnull(minhours)) +/proc/offer_control(mob/offer_mob, hours, hide_role) + if(HAS_TRAIT(offer_mob, TRAIT_BEING_OFFERED)) return - message_admins("[key_name_admin(usr)] has offered control of ([key_name_admin(M)]) to ghosts with [minhours] hrs playtime") - var/question = "Do you want to play as [M.real_name ? M.real_name : M.name][M.job ? " ([M.job])" : ""]" - if(alert("Do you want to show the antag status?","Show antag status","Yes","No") == "Yes") - question += ", [M.mind?.special_role ? M.mind?.special_role : "No special role"]" - var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("[question]?", poll_time = 10 SECONDS, min_hours = minhours, source = M) + var/minhours + ADD_TRAIT(offer_mob, TRAIT_BEING_OFFERED, ADMIN_OFFER_TRAIT) + to_chat(offer_mob, span_warning("Призракам предложен контроль над вашим существом.")) + log_admin("[key_name(usr)] has offered control of ([key_name(offer_mob)]) to ghosts.") + if(!hours) + minhours = tgui_input_number(usr, "Минимальное количество часов, необходимое для игры на [offer_mob.declent_ru(PREPOSITIONAL)]?", "Установите число часов", 10) + else + minhours = hours + message_admins("[key_name_admin(usr)] has offered control of ([key_name_admin(offer_mob)]) to ghosts with [minhours] hrs playtime") + var/question = "Вы хотите войти в раунд как [offer_mob.real_name ? offer_mob.real_name : offer_mob.declent_ru(NOMINATIVE)][offer_mob.job ? " ([offer_mob.job])" : ""]" + if(isnull(hide_role)) + if(tgui_alert(usr, "Вы хотите показывать спецроль существа?","Показывать спецроль", list("Да","Нет")) == "Да") + question += ", [offer_mob.mind?.special_role || "Нет спец роли"]" + else if(!hide_role) + question += ", [offer_mob.mind?.special_role ? offer_mob.mind?.special_role : "Нет спецроли"]" + var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("[question]?", poll_time = 10 SECONDS, min_hours = minhours, source = offer_mob) var/mob/dead/observer/theghost = null + REMOVE_TRAIT(offer_mob, TRAIT_BEING_OFFERED, ADMIN_OFFER_TRAIT) + if(LAZYLEN(candidates)) theghost = pick(candidates) - to_chat(M, "Your mob has been taken over by a ghost!") - message_admins("[key_name_admin(theghost)] has taken control of ([key_name_admin(M)])") - log_game("[theghost.key] has taken control of [M] (ckey: [M.key])") - M.ghostize() - M.key = theghost.key + to_chat(offer_mob, span_notice("Контроль над вашем существом был передан призраку!")) + message_admins("[key_name_admin(theghost)] has taken control of ([key_name_admin(offer_mob)])") + log_game("[theghost.key] has taken control of [offer_mob] (ckey: [offer_mob.key])") + offer_mob.ghostize() + offer_mob.key = theghost.key else - to_chat(M, "There were no ghosts willing to take control.") - log_game("No one decided to take control of [M] (ckey: [M.key])") - message_admins("No ghosts were willing to take control of [key_name_admin(M)])") + to_chat(offer_mob, span_notice("Не было призраков, желающих взять под свой контроль ваше существо.")) + log_game("No one decided to take control of [offer_mob] (ckey: [offer_mob.key])") + message_admins("No ghosts were willing to take control of [key_name_admin(offer_mob)])") /proc/check_zone(zone) if(!zone) diff --git a/code/modules/reagents/chemistry/reagents/misc.dm b/code/modules/reagents/chemistry/reagents/misc.dm index 5d0eda7a673..1801e9a6ad2 100644 --- a/code/modules/reagents/chemistry/reagents/misc.dm +++ b/code/modules/reagents/chemistry/reagents/misc.dm @@ -161,7 +161,7 @@ var/mob/living/carbon/human/H = M if(!HAS_TRAIT(H, TRAIT_NO_BLOOD) && !HAS_TRAIT(H, TRAIT_NO_BLOOD_RESTORE) && H.blood_volume < BLOOD_VOLUME_NORMAL) H.AdjustBlood(0.8) - + return ..() //foam @@ -708,3 +708,53 @@ if(volume > 4) M.add_language(LANGUAGE_MONKEY_HUMAN) return ..() + +/datum/reagent/admin_cleaner + name = "WD-2381" + color = "#da9eda" + description = "Супер-пузырьковое чистящее средство, предназначенное для очистки всех предметов. Или, ну. Всего, что не прикручено. Или прикуручено, если уж на то пошло. Другими словами: если вы это видите, как вы это заполучили?" + +/datum/reagent/admin_cleaner/organic + name = "WD-2381-MOB" + id = "admincleaner_mob" + description = "Бутылочка со странными нанитами, мгновенно пожирающими тела, как живые, так и мертвые, а также органы." + +/datum/reagent/admin_cleaner/organic/reaction_mob(mob/living/M, method, volume, show_message) + . = ..() + if(method == REAGENT_TOUCH) + M.dust() + +/datum/reagent/admin_cleaner/organic/reaction_obj(obj/O, volume) + if(is_organ(O)) + qdel(O) + if(istype(O, /obj/effect/decal/cleanable/blood) || istype(O, /obj/effect/decal/cleanable/vomit)) + qdel(O) + if(istype(O, /obj/item/mmi)) + qdel(O) + +/datum/reagent/admin_cleaner/item + name = "WD-2381-ITM" + id = "admincleaner_item" + description = "Бутылочка со странными нанитами, которые мгновенно пожирают предметы, оставляя все остальное нетронутым." + +/datum/reagent/admin_cleaner/item/reaction_obj(obj/O, volume) + if(isitem(O) && !istype(O, /obj/item/grenade/clusterbuster/segment)) + qdel(O) + +/datum/reagent/admin_cleaner/all + name = "WD-2381-ALL" + id = "admincleaner_all" + description = "Невероятно опасный набор нанитов, созданный Уборщиками Синдиката, которые пожирают все, к чему прикасаются." + +/datum/reagent/admin_cleaner/all/reaction_obj(obj/O, volume) + if(istype(O, /obj/item/grenade/clusterbuster/segment)) + // don't clear clusterbang segments + // I'm allowed to make this hack because this is admin only anyway + return + if(!iseffect(O)) + qdel(O) + +/datum/reagent/admin_cleaner/all/reaction_mob(mob/living/M, method, volume, show_message) + . = ..() + if(method == REAGENT_TOUCH) + M.dust() diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm index 127c1c113ec..115b1580ed6 100644 --- a/code/modules/reagents/reagent_containers/spray.dm +++ b/code/modules/reagents/reagent_containers/spray.dm @@ -17,6 +17,7 @@ amount_per_transfer_from_this = 5 volume = 250 possible_transfer_amounts = null + var/delay = CLICK_CD_RANGE * 2 /obj/item/reagent_containers/spray/afterattack(atom/A, mob/user, proximity, params) if(isstorage(A) || istype(A, /obj/structure/table) || istype(A, /obj/structure/rack) || istype(A, /obj/structure/closet) \ @@ -47,7 +48,7 @@ INVOKE_ASYNC(src, PROC_REF(spray), A) playsound(loc, 'sound/effects/spray2.ogg', 50, 1, -6) - user.changeNext_move(CLICK_CD_RANGE*2) + user.changeNext_move(delay) user.newtonian_move(get_dir(A, user)) var/attack_log_type = ATKLOG_ALMOSTALL diff --git a/code/modules/surgery/organs/augments_internal.dm b/code/modules/surgery/organs/augments_internal.dm index 028a5124b41..15e99e388d5 100644 --- a/code/modules/surgery/organs/augments_internal.dm +++ b/code/modules/surgery/organs/augments_internal.dm @@ -399,6 +399,9 @@ poison_amount = 10 origin_tech = "materials=4;powerstorage=3;biotech=3" +/obj/item/organ/internal/cyberimp/chest/nutriment_old/plus/hardened + emp_proof = TRUE + /obj/item/organ/internal/cyberimp/chest/reviver name = "Reviver implant" diff --git a/icons/misc/buildmode.dmi b/icons/misc/buildmode.dmi index f0de428b6c55b37d8ad59cac0e9e5064e2d64ad7..cf62abf20aa774dbf95ffebfa4b822b72c2bb908 100644 GIT binary patch delta 1914 zcmV-=2Zi{r4Y3a)iBL{Q4GJ0x0000DNk~Le0002M0001>1Oos70H_9jZjm890qc=p z6@Rgg!Y~Ym=j16O*iXPQu-t;E5X12b$ZcF=k<`5;6y4iXU_hMO6UpXZ`aU^!lBrr3 z+udGmzIU=iABY?ErIsHvDK4a=>BHwj3WIbs@ex9VhG!TZ(8^Aycw!@Akk3f~Hw0~v; zPk^*el~Awq=BT@MwXU``A%yWckojXztk12M|89Hx=DTeBcD3FfKWE!>wcZ}j^n{P+ zYP~(a-;?dv=W4w@KKvoW_Sf%Er$n|l5u0ij+fU33x zGTCqhGL9hV0brZ{3-4?N+~?v7^6zPOQw~%eO>AW1TiMZ7%91g0M~u~zOLvM1b)b>rX7KB6Z;au?$%FT)819^>No8o z4jS`XK~=AaLFEcWZH-rYP0={5)t|vSzS{i0+`hl-gD<5%X#0Dg>8CO~(c8%1`^>zW z>D$QP^Xdt_R`DIo8V03+oPR@2rsdj6h9WNG4BV?BeWU;DQ;T@rNtJ&ni)L_r|1 zj>rCIN32XumjqpZPwP9lxbTR_GDv9CV$a z>F>jcA_3UF3V%#hOZo%`Q;7)r{yvN-#9){Q#3JH7x$o~iL7YYTHGi&Foi-XO*2#T; z??>2k9rj=xvdd^B%iou-L(t$fPJ5sE_9F-l z?HF|Yed+w{oTncZGG7_W1iyB;$ z>+kQn?``DouXQ1#Zd5q;DxRF+T_23h?`N+MKIQtS(f*PC0_%fc&K>`+JwI5cTV2OQ zi|AGT{Q=u{)0sT(Xn|Ldde!KY(AOFIUu!k(6F5t>NAJlxIDb8j6+-GVP7-)eBq6CE zB-=l}11hoCV0{e$8EkjW!2u-qY;OK~0wqroXzd(- zwd!=fi6&Bv0YEbbSRfiZU~A93*EBf+AyDD$eUozi>$!U}j@E5{mJmS_6THHELck63 znh&b_BAmuJ?0-PORaY{i^tqY}y?B=wm;$nw2L(Z8L`mif--~zMcsAN1cOeG?y6QNc z{j5RTrqXMgR{AuB;`0QaKq__y2*$WPsm>xU0PgyfDE^Pv0v83OS2Laf2u31;hqK7j{7gd{KM zS=wxU;J2zv_%qgQf5$E)0jzNqbGlam{{Hp}Mwxi~1fxv6PmpF_ zWi@;gd<_I>gsDj=YCX6G1pJl+zL64q5J>m-#}Wd~FRNgMB7(OhpQo1*0KZ)mo|6#( zJH3SjAVL=r2d~*Dts!tR2ycSeY9PE_vrCR3eNRpk!^>t7`OKFP>>u58vwbCjdmjrQ zr^%1+wdhT7iQqr)Dn|bP_6c5Y9bavJUv1z20eU%is@~<~yZ`_I07*qoM6N<$g7S@~ AlmGw# delta 1654 zcmV-+28sEx53da&iBL{Q4GJ0x0000DNk~Le0001>0001>1Oos70D0kh9+4qE0o0LS z6@SYL!Y~j;*U47|yBA*zH*Q1`7xot;@?|!A&QF4ZZ4d|JYW*B-tP9P z*c|q}1?Q0}u@Z~%gtHD+(0!QCIMaZo4v*l24A7UOtU;qjoGEm^&r8k6llx<++cMYfM-N@0PbZAL)HzJ{5L)k+jMjPlPlsI^L--G`K15=1zbr) zK~!jg?V3-r+cpfw)f=>KGtJiBb=f0iMl)P@lb#^r1z2L;o0k9xlA!*Ol=(X8jDO{q z#9u7SKZ79g5qirHKdIx#SM~DvO&!ly_40U@AMkj-s+Y&lYjOPXd{r-xPhYomeEahm zwe?!puUFavc-G6~m+}+7JfjBCx_(`2aO-M-bRgV{13z)gpO|$0y4IG3M}05Z6I|zgy=J3ZgAmY!egf7U3G#8y8n>s()gm4Di0TNsy)c&rm4+uk$ajKMr$=|j0j&OD88-FsP_IDk+CIk#& zG0NKCb!e4$hbwmJ?|O7OGY)2o_IEvcy>M$w?eDtuMc3rS!M5~O4B!6nKlg|KX#M%^ z4^R7txAj?9WI&tjeWJHf{_e((9z@0UQ2V855m=Y1jfDkrC82d6b&^d+x z{V}Wvsl^)aciyXd6m(6B^19)B9SHyiKHa-;3aeYFWej@U_} zO+j*B1nW0@N*KOM7-)2ul85Pnl84&CN`LTllxb?2;4mc*uL=DY`-7LGjM;0&eeDPV zO>>?(n$Y3R3ZhCoDJD$6Mc_4IO;8h(2Aob6VY&luG+`YhBBBXi#1xE4Spc+)aG9{m z!&crgF@GBIv-Pmb!&ct$sDm-5D|Sl6PCrO_X^&T<+?}J}U6?YLzb3?~T@kK(Z-02l zEKRp_yAyt`P8dC!Ut#!y4SzU(!Is|}zF;f!hcDR5{BgqFYNA(<350lq3GIq;lL$Ta zaXfcPW)MPbCEX?jzbB-ohrLPYDndDK6MBjm_(y4@4BUB}n1_Bc;X0cTJZIjA&6Mwd zF24!igl)p_zUsC7-tYxmesB1Kv&~Y|?+qUR0u9q-((emx{Qv*}07*qoM6N<$g0d7Y A^Z)<= diff --git a/paradise.dme b/paradise.dme index b92e233be22..ea3bb5145e9 100644 --- a/paradise.dme +++ b/paradise.dme @@ -588,8 +588,8 @@ #include "code\datums\elements\_element.dm" #include "code\datums\elements\connect_loc.dm" #include "code\datums\elements\devil_banishment.dm" -#include "code\datums\elements\diona_internals.dm" #include "code\datums\elements\devil_regen.dm" +#include "code\datums\elements\diona_internals.dm" #include "code\datums\elements\falling_hazard.dm" #include "code\datums\elements\footstep.dm" #include "code\datums\elements\give_turf_traits.dm" @@ -636,6 +636,7 @@ #include "code\datums\mapgen\LavalandGenerator.dm" #include "code\datums\outfits\outfit.dm" #include "code\datums\outfits\outfit_admin.dm" +#include "code\datums\outfits\outfit_debug.dm" #include "code\datums\outfits\outfit_security_clown.dm" #include "code\datums\outfits\plasmamen.dm" #include "code\datums\outfits\vv_outfit.dm" @@ -1957,9 +1958,12 @@ #include "code\modules\buildmode\submodes\boom.dm" #include "code\modules\buildmode\submodes\copy.dm" #include "code\modules\buildmode\submodes\fill.dm" +#include "code\modules\buildmode\submodes\forcemove.dm" #include "code\modules\buildmode\submodes\link.dm" #include "code\modules\buildmode\submodes\mapgen.dm" +#include "code\modules\buildmode\submodes\offer.dm" #include "code\modules\buildmode\submodes\save.dm" +#include "code\modules\buildmode\submodes\say.dm" #include "code\modules\buildmode\submodes\throwing.dm" #include "code\modules\buildmode\submodes\variable_edit.dm" #include "code\modules\cargo\centcom_podlauncher.dm" diff --git a/tgui/packages/tgui/interfaces/CommunicationsComputer.js b/tgui/packages/tgui/interfaces/CommunicationsComputer.js index 17b04fa6346..e855b238220 100644 --- a/tgui/packages/tgui/interfaces/CommunicationsComputer.js +++ b/tgui/packages/tgui/interfaces/CommunicationsComputer.js @@ -1,160 +1,474 @@ -import { useBackend } from '../backend'; -import { Button, LabeledList, Box, Section } from '../components'; +import { useBackend, useLocalState } from '../backend'; +import { + Button, + LabeledList, + Box, + Section, + Collapsible, + Input, + Stack, + Dropdown, +} from '../components'; import { Window } from '../layouts'; +const windows = { + 1: () => , + 2: () => , + 3: () => ( + +
+ +
+
+ ), + 4: () => , + default: () => + 'Ошибка. Неизвестное menu_state. Пожалуйста свяжитесь с Технической Поддержкой NT.', +}; + +const PickWindow = (index) => windows[index]; + export const CommunicationsComputer = (props, context) => { const { act, data } = useBackend(context); + const { menu_state } = data; + + return ( + + + + + {PickWindow(menu_state)()} + + + + ); +}; + +const AuthBlock = (props, context) => { + const { act, data } = useBackend(context); + + const { + authenticated, + noauthbutton, + esc_section, + esc_callable, + esc_recallable, + esc_status, + authhead, + is_ai, + lastCallLoc, + } = data; + + let hideLogButton = false; let authReadable; - if (!data.authenticated) { - authReadable = 'Not Logged In'; - } else if (data.is_ai) { - authReadable = 'AI'; - } else if (data.authenticated === 1) { - authReadable = 'Command'; - } else if (data.authenticated === 2) { - authReadable = 'Captain'; + if (!authenticated) { + authReadable = 'Не авторизован'; + } else if (authenticated === 1) { + authReadable = 'Командование'; + } else if (authenticated === 2) { + authReadable = 'Капитан'; + } else if (authenticated === 3) { + authReadable = 'Офицер Центрального Командования'; + } else if (authenticated === 4) { + authReadable = 'Безопасное подключение ЦК'; + hideLogButton = true; } else { - authReadable = 'ERROR: Report This Bug!'; + authReadable = 'ОШИБКА: Сообщите об этом баге!'; } - let reportText = 'View (' + data.messages.length + ')'; - let authBlock = ( + + return ( <> -
+ +
+ + {(hideLogButton && ( + {authReadable} + )) || ( + +
+
+ + {!!esc_section && ( +
+ + {!!esc_status && ( + {esc_status} + )} + {!!esc_callable && ( + +
+ )} +
+ + ); +}; + +const MainPage = (props, context) => { + const { act, data } = useBackend(context); + + const { is_admin } = data; + + if (is_admin) { + return ; + } + return ; +}; + +const AdminPage = (props, context) => { + const { act, data } = useBackend(context); + const { + is_admin, + gamma_armory_location, + admin_levels, + authenticated, + ert_allowed, + } = data; + + return ( + +
- {(data.is_ai && ( - AI - )) || ( - + + + + +
- {!!data.esc_section && ( -
+ + + + + ); +}; + +const PlayerPage = (props, context) => { + const { act, data } = useBackend(context); + + const { + msg_cooldown, + emagged, + cc_cooldown, + security_level_color, + str_security_level, + levels, + authcapt, + authhead, + messages, + } = data; + + let announceText = 'Сделать Приоритетное Оповещение'; + if (msg_cooldown > 0) { + announceText += ' (' + msg_cooldown + 's)'; + } + + let ccMessageText = emagged ? 'Сообщение [НЕИЗВЕСТНО]' : 'Сообщение ЦК'; + let nukeRequestText = 'Запросить коды аутентификации'; + if (cc_cooldown > 0) { + ccMessageText += ' (' + cc_cooldown + 's)'; + nukeRequestText += ' (' + cc_cooldown + 's)'; + } + + return ( + <> + +
- {!!data.esc_status && ( - - {data.esc_status} - - )} - {!!data.esc_callable && ( - + + {str_security_level} + + + + + +
+
+ +
+ + +
- )} +
); - let announceText = 'Make Priority Announcement'; - if (data.msg_cooldown > 0) { - announceText += ' (' + data.msg_cooldown + 's)'; - } - let ccMessageText = data.emagged ? 'Message [UNKNOWN]' : 'Message CentComm'; - let nukeRequestText = 'Request Authentication Codes'; - if (data.cc_cooldown > 0) { - ccMessageText += ' (' + data.cc_cooldown + 's)'; - nukeRequestText += ' (' + data.cc_cooldown + 's)'; - } - let alertLevelText = data.str_security_level; - let alertLevelButtons = data.levels.map((slevel) => { - return ( -
+
+ ); +}; + +const MessageView = (props, context) => { + const { act, data } = useBackend(context); + + const { + authhead, + current_message_title, + current_message, + messages, + security_level, + } = data; + + let messageView; + if (current_message_title) { + messageView = ( + +
act('messagelist')} + /> + } + > + {current_message} +
+
); } else { - let messageRows = data.messages.map((m) => { + let messageRows = messages.map((m) => { return (
-
- - -
- - - ); - // 2 = status screen - case 2: - return ( - - - {authBlock} -
act('main')} - /> - } - > - - - {presetButtons} - - - {iconButtons} - - -
-
-
- ); + return {messageView}; +}; - // 3 = messages screen - case 3: - return ( - - - {authBlock} - {messageView} - - - ); +const MappedAlertLevelButtons = (props, context) => { + const { act, data } = useBackend(context); - default: + const { levels, required_access, use_confirm } = props; + const { security_level } = data; + + if (use_confirm) { + return levels.map((slevel) => { return ( - - - {authBlock} - ERRROR. Unknown menu_state: {data.menu_state} - Please report this to NT Technical Support. - - + act('newalertlevel', { level: slevel.id })} + /> ); + }); } + + return levels.map((slevel) => { + return ( +