From 7554b5f4541231ddbdc77e66a493fc04752c93e4 Mon Sep 17 00:00:00 2001 From: coiax Date: Sun, 2 Apr 2017 20:03:40 +0100 Subject: [PATCH] Language datums; removes the talk wheel (#25333) * Initial burst of languages * Scratchings of beginnings * Code review I * Compilation! * You can now understand your own speech * Fixes whispering * Gets typecaches working again * Remie's `PASS`ing * Back to pass() to stop the compiler whining * Why can't drones check their languages * Everyone speaks how they should * Removes world string debug stuff * Currently failing to massage radio code into working * The radio transmits the languages! * ,0 to talk common * Replaces speech wheel with language menu * Observers can speak all languages * pAIs now speak languages FOREVER * New action button for language menu * pAIs have an action button to open their language menu * AIs can talk and all that * AIs have a language menu button * Fixes supermatter making strange noises * Fixes AI holopads * Fixes request consoles * Fixes bots making strange sounds * Meaningless MMI change * Some caching of datums * Brains in MMIs speak common * Ratvarian AIs can only speak Ratvarian * Tables can speak common, apparently * Removes var in args * Fixes the (AI Eye) problem Thanks to Shadowmobile for their help with this one. * Fixes tape recorders * Fixes humans being able to speak in languages they did not know * Adds some new posibrain names Honk. * The voice analyzer replies in the language you spoke to it * Gives swarmers only swarmer language * `initial_languages` var. * OMNITONGUE, clockwork restrictions * Fixes barmaid and bardrone language abilities * Code review I * Omnitongue correction * Code review II * Removes force_compose var --- code/__DEFINES/flags.dm | 22 +- code/__DEFINES/language.dm | 2 + code/__HELPERS/_lists.dm | 5 +- code/__HELPERS/flags.dm | 1 + code/__HELPERS/unsorted.dm | 3 + code/_globalvars/lists/mobs.dm | 3 +- code/_globalvars/lists/names.dm | 1 + code/_onclick/hud/_defines.dm | 5 +- code/_onclick/hud/ai.dm | 5 + code/_onclick/hud/alien.dm | 5 +- code/_onclick/hud/alien_larva.dm | 5 +- code/_onclick/hud/hud.dm | 14 - code/_onclick/hud/human.dm | 3 +- code/_onclick/hud/monkey.dm | 3 +- code/_onclick/hud/robot.dm | 5 +- code/_onclick/hud/screen_objects.dm | 149 +---------- code/datums/action.dm | 13 + code/datums/antagonists/datum_clockcult.dm | 8 +- code/game/atoms_movable.dm | 61 ++++- .../clock_helpers/ratvarian_language.dm | 7 +- .../clock_cult/clock_items/soul_vessel.dm | 1 - code/game/gamemodes/clock_cult/clock_mobs.dm | 4 +- .../gamemodes/devil/true_devil/_true_devil.dm | 10 +- .../gamemodes/miniantags/bot_swarm/swarmer.dm | 3 +- .../gamemodes/miniantags/revenant/revenant.dm | 2 - code/game/machinery/announcement_system.dm | 4 +- code/game/machinery/cloning.dm | 2 +- code/game/machinery/doors/brigdoors.dm | 2 +- code/game/machinery/hologram.dm | 6 +- code/game/machinery/requests_console.dm | 14 +- code/game/machinery/telecomms/broadcasting.dm | 240 +----------------- .../machinery/telecomms/machines/allinone.dm | 3 +- .../telecomms/machines/broadcaster.dm | 16 +- .../machinery/telecomms/machines/server.dm | 5 +- .../machinery/telecomms/telecomunications.dm | 3 +- code/game/mecha/mecha.dm | 7 +- code/game/objects/items.dm | 2 +- .../game/objects/items/devices/radio/radio.dm | 28 +- .../objects/items/devices/taperecorder.dm | 16 +- code/game/objects/items/toys.dm | 4 +- code/game/objects/objs.dm | 2 - code/game/say.dm | 42 ++- code/modules/admin/admin.dm | 5 +- code/modules/admin/admin_verbs.dm | 3 - code/modules/admin/topic.dm | 10 + code/modules/assembly/voice.dm | 20 +- .../components/unary_devices/cryo.dm | 4 +- code/modules/events/sentience.dm | 6 +- code/modules/flufftext/Hallucination.dm | 5 +- code/modules/language/common.dm | 70 +++++ code/modules/language/drone.dm | 14 + code/modules/language/language.dm | 95 +++++++ code/modules/language/language_menu.dm | 88 +++++++ code/modules/language/machine.dm | 17 ++ code/modules/language/monkey.dm | 10 + code/modules/language/ratvar.dm | 12 + code/modules/language/slime.dm | 10 + code/modules/language/swarmer.dm | 42 +++ code/modules/language/xenocommon.dm | 10 + code/modules/mining/laborcamp/laborstacker.dm | 2 +- code/modules/mob/dead/observer/observer.dm | 4 +- code/modules/mob/dead/observer/say.dm | 11 +- code/modules/mob/living/brain/MMI.dm | 2 - code/modules/mob/living/brain/brain.dm | 4 +- code/modules/mob/living/brain/posibrain.dm | 7 +- code/modules/mob/living/brain/say.dm | 17 +- code/modules/mob/living/carbon/alien/alien.dm | 3 +- code/modules/mob/living/carbon/human/human.dm | 2 + .../mob/living/carbon/human/human_defines.dm | 3 +- code/modules/mob/living/carbon/human/say.dm | 8 +- .../mob/living/carbon/human/whisper.dm | 11 +- .../mob/living/carbon/monkey/monkey.dm | 5 +- code/modules/mob/living/carbon/say.dm | 12 + code/modules/mob/living/living.dm | 4 + code/modules/mob/living/living_defines.dm | 7 +- code/modules/mob/living/say.dm | 84 ++++-- code/modules/mob/living/silicon/ai/ai.dm | 7 +- .../mob/living/silicon/ai/freelook/eye.dm | 4 +- code/modules/mob/living/silicon/ai/say.dm | 24 +- code/modules/mob/living/silicon/pai/pai.dm | 3 + .../mob/living/silicon/pai/pai_shell.dm | 2 +- .../mob/living/silicon/pai/software.dm | 16 +- code/modules/mob/living/silicon/say.dm | 6 +- code/modules/mob/living/silicon/silicon.dm | 3 +- .../mob/living/simple_animal/bot/bot.dm | 15 +- .../simple_animal/friendly/drone/_drone.dm | 4 +- .../friendly/drone/extra_drone_types.dm | 6 +- .../hostile/megafauna/colossus.dm | 4 - .../mob/living/simple_animal/parrot.dm | 9 +- .../mob/living/simple_animal/simple_animal.dm | 3 + .../mob/living/simple_animal/slime/powers.dm | 2 - .../mob/living/simple_animal/slime/slime.dm | 5 +- code/modules/mob/mob_defines.dm | 2 - code/modules/mob/mob_helpers.dm | 13 +- code/modules/mob/say.dm | 8 +- code/modules/power/supermatter/supermatter.dm | 17 +- code/modules/projectiles/projectile/magic.dm | 5 +- .../research/xenobiology/xenobiology.dm | 4 - code/modules/shuttle/special.dm | 10 +- code/modules/surgery/organs/organ_internal.dm | 31 +++ code/modules/tgui/states/language_menu.dm | 14 + icons/mob/actions.dmi | Bin 185813 -> 186647 bytes interface/stylesheet.dm | 1 + tgstation.dme | 12 + tgui/assets/tgui.css | 2 +- tgui/assets/tgui.js | 32 +-- tgui/src/interfaces/language_menu.ract | 41 +++ 107 files changed, 941 insertions(+), 707 deletions(-) create mode 100644 code/__DEFINES/language.dm create mode 100644 code/modules/language/common.dm create mode 100644 code/modules/language/drone.dm create mode 100644 code/modules/language/language.dm create mode 100644 code/modules/language/language_menu.dm create mode 100644 code/modules/language/machine.dm create mode 100644 code/modules/language/monkey.dm create mode 100644 code/modules/language/ratvar.dm create mode 100644 code/modules/language/slime.dm create mode 100644 code/modules/language/swarmer.dm create mode 100644 code/modules/language/xenocommon.dm create mode 100644 code/modules/tgui/states/language_menu.dm create mode 100644 tgui/src/interfaces/language_menu.ract diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm index 88dca50becc..e9a88e2bea5 100644 --- a/code/__DEFINES/flags.dm +++ b/code/__DEFINES/flags.dm @@ -36,6 +36,12 @@ #define INFORM_ADMINS_ON_RELOCATE "inform_admins_on_relocate" #define BANG_PROTECT "bang_protect" +// A mob with OMNITONGUE has no restriction in the ability to speak +// languages that they know. So even if they wouldn't normally be able to +// through mob or tongue restrictions, this flag allows them to ignore +// those restrictions. +#define OMNITONGUE "omnitongue" + //turf-only flags #define NOJAUNT 1 #define UNUSED_TRANSIT_TURF 2 @@ -60,20 +66,6 @@ #define GROUND 1 #define FLYING 2 - -/* - These defines are used specifically with the atom/movable/languages bitmask. - They are used in atom/movable/Hear() and atom/movable/say() to determine whether hearers can understand a message. -*/ -#define HUMAN 1 -#define MONKEY 2 -#define ALIEN 4 -#define ROBOT 8 -#define SLIME 16 -#define DRONE 32 -#define SWARMER 64 -#define RATVAR 128 - // Flags for reagents #define REAGENT_NOREACT 1 @@ -85,3 +77,5 @@ #define UNACIDABLE 16 //acid can't even appear on it, let alone melt it. #define ACID_PROOF 32 //acid stuck on it doesn't melt it. #define INDESTRUCTIBLE 64 //doesn't take damage + +// language secondary flags for atoms diff --git a/code/__DEFINES/language.dm b/code/__DEFINES/language.dm new file mode 100644 index 00000000000..3f09f46817d --- /dev/null +++ b/code/__DEFINES/language.dm @@ -0,0 +1,2 @@ +#define NO_STUTTER 1 +#define TONGUELESS_SPEECH 2 diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm index 65dd14bf57b..bd3752271ea 100644 --- a/code/__HELPERS/_lists.dm +++ b/code/__HELPERS/_lists.dm @@ -65,7 +65,10 @@ if(!L || !L.len || !A) return 0 - return L[A.type] + if(ispath(A)) + . = L[A] + else + . = L[A.type] //Checks for a string in a list /proc/is_string_in_list(string, list/L) diff --git a/code/__HELPERS/flags.dm b/code/__HELPERS/flags.dm index 8ff099ef27e..5a6c1f3ab43 100644 --- a/code/__HELPERS/flags.dm +++ b/code/__HELPERS/flags.dm @@ -1,3 +1,4 @@ #define HAS_SECONDARY_FLAG(atom, sflag) (atom.secondary_flags ? atom.secondary_flags[sflag] : FALSE) #define SET_SECONDARY_FLAG(atom, sflag) if(!atom.secondary_flags) { atom.secondary_flags = list(); } atom.secondary_flags[sflag] = TRUE; #define CLEAR_SECONDARY_FLAG(atom, sflag) if(atom.secondary_flags) atom.secondary_flags[sflag] = null +#define TOGGLE_SECONDARY_FLAG(atom, sflag) if(HAS_SECONDARY_FLAG(atom, sflag)) { CLEAR_SECONDARY_FLAG(atom, sflag); } else {SET_SECONDARY_FLAG(atom, sflag) ; } diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 5c2bd6a8887..640f6df10f9 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -1429,3 +1429,6 @@ var/valid_HTTPSGet = FALSE /proc/to_chat(target, message) target << message + +/proc/pass() + return diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm index e4fdb79bb8f..91d5b62f64f 100644 --- a/code/_globalvars/lists/mobs.dm +++ b/code/_globalvars/lists/mobs.dm @@ -14,4 +14,5 @@ var/global/list/dead_mob_list = list() //all dead mobs, including clientless. var/global/list/joined_player_list = list() //all clients that have joined the game at round-start or as a latejoin. var/global/list/silicon_mobs = list() //all silicon mobs var/global/list/pai_list = list() -var/global/list/available_ai_shells = list() \ No newline at end of file +var/global/list/available_ai_shells = list() +var/global/list/language_datums = list() diff --git a/code/_globalvars/lists/names.dm b/code/_globalvars/lists/names.dm index e38d7050ef4..902aff9f628 100644 --- a/code/_globalvars/lists/names.dm +++ b/code/_globalvars/lists/names.dm @@ -14,6 +14,7 @@ var/list/mime_names = file2list("config/names/mime.txt") var/list/carp_names = file2list("config/names/carp.txt") var/list/golem_names = file2list("config/names/golem.txt") var/list/plasmaman_names = file2list("config/names/plasmaman.txt") +var/list/posibrain_names = list("PBU","HIU","SINA","ARMA","OSI","HBL","MSO","RR","CHRI","CDB","HG","XSI","ORNG","GUN","KOR","MET","FRE","XIS","SLI","PKP","HOG","RZH","GOOF","MRPR","JJR","FIRC","INC","PHL","BGB","ANTR","MIW","WJ","JRD","CHOC","ANCL","JLLO","JNLG","KOS","TKRG","XAL","STLP","CBOS","DUNC","FXMC","DRSD","COI") var/list/verbs = file2list("config/names/verbs.txt") diff --git a/code/_onclick/hud/_defines.dm b/code/_onclick/hud/_defines.dm index 1f004f31ac3..50541b9a603 100644 --- a/code/_onclick/hud/_defines.dm +++ b/code/_onclick/hud/_defines.dm @@ -25,6 +25,7 @@ #define ui_lingstingdisplay "WEST:6,CENTER-3:11" #define ui_crafting "12:-10,1:5" #define ui_building "12:-10,1:21" +#define ui_language_menu "11:6,2:-11" #define ui_devilsouldisplay "WEST:6,CENTER-1:15" @@ -61,7 +62,7 @@ #define ui_borg_store "CENTER+2:16,SOUTH:5" //borgs #define ui_borg_camera "CENTER+3:21,SOUTH:5" //borgs #define ui_borg_album "CENTER+4:21,SOUTH:5" //borgs -#define ui_borg_talk_wheel "CENTER+4:21,SOUTH+1:5" //borgs +#define ui_borg_language_menu "CENTER+4:21,SOUTH+1:5" //borgs #define ui_monkey_head "CENTER-4:13,SOUTH:5" //monkey #define ui_monkey_mask "CENTER-3:14,SOUTH:5" //monkey @@ -70,7 +71,7 @@ #define ui_alien_storage_l "CENTER-2:14,SOUTH:5"//alien #define ui_alien_storage_r "CENTER+1:18,SOUTH:5"//alien -#define ui_alien_talk_wheel "EAST-3:26,SOUTH:5" //alien +#define ui_alien_language_menu "EAST-3:26,SOUTH:5" //alien #define ui_drone_drop "CENTER+1:18,SOUTH:5" //maintenance drones #define ui_drone_pull "CENTER+2:2,SOUTH:5" //maintenance drones diff --git a/code/_onclick/hud/ai.dm b/code/_onclick/hud/ai.dm index c26e54eae96..abaafd386a9 100644 --- a/code/_onclick/hud/ai.dm +++ b/code/_onclick/hud/ai.dm @@ -167,6 +167,11 @@ ..() var/obj/screen/using +// Language menu + using = new /obj/screen/language_menu + using.screen_loc = ui_borg_language_menu + static_inventory += using + //AI core using = new /obj/screen/ai/aicore() using.screen_loc = ui_ai_core diff --git a/code/_onclick/hud/alien.dm b/code/_onclick/hud/alien.dm index e2bfe128741..4791b7a77ee 100644 --- a/code/_onclick/hud/alien.dm +++ b/code/_onclick/hud/alien.dm @@ -63,9 +63,8 @@ H.leap_icon.screen_loc = ui_alien_storage_r static_inventory += H.leap_icon - using = new/obj/screen/wheel/talk - using.screen_loc = ui_alien_talk_wheel - wheels += using + using = new/obj/screen/language_menu + using.screen_loc = ui_alien_language_menu static_inventory += using using = new /obj/screen/drop() diff --git a/code/_onclick/hud/alien_larva.dm b/code/_onclick/hud/alien_larva.dm index 0879ec8f313..def88b7ed72 100644 --- a/code/_onclick/hud/alien_larva.dm +++ b/code/_onclick/hud/alien_larva.dm @@ -18,9 +18,8 @@ pull_icon.screen_loc = ui_pull_resist hotkeybuttons += pull_icon - using = new/obj/screen/wheel/talk - using.screen_loc = ui_alien_talk_wheel - wheels += using + using = new/obj/screen/language_menu + using.screen_loc = ui_alien_language_menu static_inventory += using zone_select = new /obj/screen/zone_sel/alien() diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm index 4f80adeab00..f30c9e05aab 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -33,8 +33,6 @@ var/obj/screen/throw_icon var/obj/screen/module_store_icon - var/list/wheels = list() //list of the wheel screen objects - var/list/static_inventory = list() //the screen objects which are static var/list/toggleable_inventory = list() //the screen objects which can be hidden var/list/obj/screen/hotkeybuttons = list() //the buttons that can be used via hotkeys @@ -78,8 +76,6 @@ qdel(module_store_icon) module_store_icon = null - wheels = null //all wheels are also in static_inventory - if(static_inventory.len) for(var/thing in static_inventory) qdel(thing) @@ -226,16 +222,6 @@ /datum/hud/proc/persistent_inventory_update(mob/viewer) if(!mymob) return - var/mob/living/L = mymob - - var/mob/screenmob = viewer || L - - for(var/X in wheels) - var/obj/screen/wheel/W = X - if(W.toggled) - screenmob.client.screen |= W.buttons_list - else - screenmob.client.screen -= W.buttons_list //Triggered when F12 is pressed (Unless someone changed something in the DMF) /mob/verb/button_pressed_F12() diff --git a/code/_onclick/hud/human.dm b/code/_onclick/hud/human.dm index 6e0afd9bb7b..296a911770b 100644 --- a/code/_onclick/hud/human.dm +++ b/code/_onclick/hud/human.dm @@ -96,9 +96,8 @@ using.icon = ui_style static_inventory += using - using = new/obj/screen/wheel/talk + using = new/obj/screen/language_menu using.icon = ui_style - wheels += using static_inventory += using using = new /obj/screen/area_creator diff --git a/code/_onclick/hud/monkey.dm b/code/_onclick/hud/monkey.dm index 60ee61e33da..d1fd0e46eeb 100644 --- a/code/_onclick/hud/monkey.dm +++ b/code/_onclick/hud/monkey.dm @@ -15,9 +15,8 @@ using.screen_loc = ui_movi static_inventory += using - using = new/obj/screen/wheel/talk + using = new/obj/screen/language_menu using.icon = ui_style - wheels += using static_inventory += using using = new /obj/screen/drop() diff --git a/code/_onclick/hud/robot.dm b/code/_onclick/hud/robot.dm index db4cb540580..0f056e581db 100644 --- a/code/_onclick/hud/robot.dm +++ b/code/_onclick/hud/robot.dm @@ -96,9 +96,8 @@ var/mob/living/silicon/robot/mymobR = mymob var/obj/screen/using - using = new/obj/screen/wheel/talk - using.screen_loc = ui_borg_talk_wheel - wheels += using + using = new/obj/screen/language_menu + using.screen_loc = ui_borg_language_menu static_inventory += using //Radio diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index 616037c13e8..0c2871a26b0 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -84,6 +84,18 @@ return 1 create_area(usr) +/obj/screen/language_menu + name = "language menu" + icon = 'icons/mob/screen_midnight.dmi' + icon_state = "talk_wheel" + screen_loc = ui_language_menu + +/obj/screen/language_menu/Click() + var/mob/living/L = usr + if(!istype(L)) + return + L.open_language_menu(usr) + /obj/screen/inventory var/slot_id // The indentifier for the slot. It has nothing to do with ID cards. var/icon_empty // Icon when empty. For now used only by humans. @@ -529,143 +541,6 @@ name = "health doll" screen_loc = ui_healthdoll - - -/obj/screen/wheel - name = "wheel" - layer = HUD_LAYER - plane = HUD_PLANE - icon_state = "" - screen_loc = null //if you make a new wheel, remember to give it a screen_loc - var/list/buttons_names = list() //list of the names for each button, its length is the amount of buttons. - var/toggled = 0 //wheel is hidden/shown - var/wheel_buttons_type //the type of buttons used with this wheel. - var/list/buttons_list = list() - -/obj/screen/wheel/New() - ..() - build_options() - - -//we create the buttons for the wheel and place them in a square spiral fashion. -/obj/screen/wheel/proc/build_options() - var/obj/screen/wheel_button/close_wheel/CW = new () - buttons_list += CW //the close option - CW.wheel = src - - var/list/offset_x_list = list() - var/list/offset_y_list = list() - var/num = 1 - var/N = 1 - var/M = 0 - var/sign = -1 - my_loop: - while(offset_y_list.len < buttons_names.len) - for(var/i=1, i<=num, i++) - offset_y_list += N - offset_x_list += M - if(offset_y_list.len == buttons_names.len) - break my_loop - if(N != 0) - N = 0 - M = -sign - else - N = sign - M = 0 - sign = -sign - num++ - - var/screenx = 8 - var/screeny = 8 - for(var/i = 1, i <= buttons_names.len, i++) - var/obj/screen/wheel_button/WB = new wheel_buttons_type() - WB.wheel = src - buttons_list += WB - screenx += offset_x_list[i] - screeny += offset_y_list[i] - WB.screen_loc = "[screenx], [screeny]" - set_button(WB, i) - -/obj/screen/wheel/proc/set_button(obj/screen/wheel_button/WB, button_number) - WB.name = buttons_names[button_number] - return - -/obj/screen/wheel/Destroy() - for(var/obj/screen/S in buttons_list) - qdel(S) - return ..() - -/obj/screen/wheel/Click() - if(world.time <= usr.next_move) - return - if(usr.stat) - return - if(isliving(usr)) - var/mob/living/L = usr - if(toggled) - L.client.screen -= buttons_list - else - L.client.screen |= buttons_list - toggled = !toggled - - -/obj/screen/wheel/talk - name = "talk wheel" - icon_state = "talk_wheel" - screen_loc = "11:6,2:-11" - wheel_buttons_type = /obj/screen/wheel_button/talk - buttons_names = list("help","hello","bye","stop","thanks","come","out", "yes", "no") - var/list/word_messages = list(list("Help!","Help me!"), list("Hello.", "Hi."), list("Bye.", "Goodbye."),\ - list("Stop!", "Halt!"), list("Thanks.", "Thanks!", "Thank you."), \ - list("Come.", "Follow me."), list("Out!", "Go away!", "Get out!"), \ - list("Yes.", "Affirmative."), list("No.", "Negative")) - -/obj/screen/wheel/talk/set_button(obj/screen/wheel_button/WB, button_number) - ..() - var/obj/screen/wheel_button/talk/T = WB //we already know what type the button is exactly. - T.icon_state = "talk_[T.name]" - T.word_messages = word_messages[button_number] - - -/obj/screen/wheel_button - name = "default wheel button" - screen_loc = "8,8" - layer = HUD_LAYER - plane = HUD_PLANE - mouse_opacity = 2 - var/obj/screen/wheel/wheel - -/obj/screen/wheel_button/Destroy() - wheel = null - return ..() - -/obj/screen/wheel_button/close_wheel - name = "close wheel" - icon_state = "x3" - -/obj/screen/wheel_button/close_wheel/Click() - if(isliving(usr)) - var/mob/living/L = usr - L.client.screen -= wheel.buttons_list - wheel.toggled = !wheel.toggled - - -/obj/screen/wheel_button/talk - name = "talk option" - icon_state = "talk_help" - var/talk_cooldown = 0 - var/list/word_messages = list() - -/obj/screen/wheel_button/talk/Click(location, control,params) - if(isliving(usr)) - var/mob/living/L = usr - if(L.stat) - return - - if(word_messages.len && talk_cooldown < world.time) - talk_cooldown = world.time + 10 - L.say(pick(word_messages)) - /obj/screen/splash icon = 'config/title_screens/images/blank.png' icon_state = "" diff --git a/code/datums/action.dm b/code/datums/action.dm index db459b2aabd..03b293b13dc 100644 --- a/code/datums/action.dm +++ b/code/datums/action.dm @@ -472,3 +472,16 @@ name = "Activate Jump Boots" desc = "Activates the jump boot's internal propulsion system, allowing the user to dash over 4-wide gaps." button_icon_state = "jetboot" + +/datum/action/language_menu + name = "Language Menu" + desc = "Open the language menu to review your languages, their keys, and select your default language." + button_icon_state = "language_menu" + check_flags = 0 + +/datum/action/language_menu/Trigger() + if(!..()) + return FALSE + if(isliving(owner)) + var/mob/living/L = owner + L.open_language_menu(usr) diff --git a/code/datums/antagonists/datum_clockcult.dm b/code/datums/antagonists/datum_clockcult.dm index 3552211f73c..91c61b24f83 100644 --- a/code/datums/antagonists/datum_clockcult.dm +++ b/code/datums/antagonists/datum_clockcult.dm @@ -70,8 +70,7 @@ /datum/antagonist/clockcultist/apply_innate_effects() all_clockwork_mobs += owner owner.faction |= "ratvar" - owner.languages_spoken |= RATVAR - owner.languages_understood |= RATVAR + owner.grant_language(/datum/language/ratvar) owner.update_action_buttons_icon() //because a few clockcult things are action buttons and we may be wearing/holding them for whatever reason, we need to update buttons if(issilicon(owner)) var/mob/living/silicon/S = owner @@ -84,7 +83,6 @@ var/mob/living/silicon/ai/A = S A.can_be_carded = FALSE A.requires_power = POWER_REQ_CLOCKCULT - A.languages_spoken &= ~HUMAN var/list/AI_frame = list(image('icons/mob/clockwork_mobs.dmi', A, "aiframe")) //make the AI's cool frame for(var/d in cardinal) AI_frame += image('icons/mob/clockwork_mobs.dmi', A, "eye[rand(1, 10)]", dir = d) //the eyes are randomly fast or slow @@ -126,8 +124,7 @@ /datum/antagonist/clockcultist/remove_innate_effects() all_clockwork_mobs -= owner owner.faction -= "ratvar" - owner.languages_spoken &= ~RATVAR - owner.languages_understood &= ~RATVAR + owner.remove_language(/datum/language/ratvar) owner.clear_alert("clockinfo") owner.clear_alert("scripturereq") for(var/datum/action/innate/function_call/F in owner.actions) //Removes any bound Ratvarian spears @@ -138,7 +135,6 @@ var/mob/living/silicon/ai/A = S A.can_be_carded = initial(A.can_be_carded) A.requires_power = initial(A.requires_power) - A.languages_spoken |= HUMAN A.cut_overlays() S.make_laws() S.update_icons() diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index e481731c751..84a097594b1 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -6,8 +6,9 @@ var/throw_speed = 2 //How many tiles to move per ds when being thrown. Float values are fully supported var/throw_range = 7 var/mob/pulledby = null - var/languages_spoken = 0 //For say() and Hear() - var/languages_understood = 0 + var/list/languages + var/list/initial_languages = list(/datum/language/common) + var/only_speaks_language = null var/verb_say = "says" var/verb_ask = "asks" var/verb_exclaim = "exclaims" @@ -35,6 +36,11 @@ return FALSE return ..() +/atom/movable/Initialize(mapload) + ..() + for(var/L in initial_languages) + grant_language(L) + /atom/movable/Move(atom/newloc, direct = 0) if(!loc || !newloc) return 0 var/atom/oldloc = loc @@ -550,3 +556,54 @@ var/turf/currentturf = get_turf(src) if(currentturf && (currentturf.z == ZLEVEL_CENTCOM || currentturf.z == ZLEVEL_STATION)) . = TRUE + + +/* Language procs */ +/atom/movable/proc/grant_language(datum/language/dt) + LAZYINITLIST(languages) + languages[dt] = TRUE + +/atom/movable/proc/grant_all_languages(omnitongue=FALSE) + for(var/la in subtypesof(/datum/language)) + grant_language(la) + + if(omnitongue) + SET_SECONDARY_FLAG(src, OMNITONGUE) + +/atom/movable/proc/get_random_understood_language() + var/list/possible = list() + for(var/dt in languages) + possible += dt + . = safepick(possible) + +/atom/movable/proc/remove_language(datum/language/dt) + LAZYREMOVE(languages, dt) + +/atom/movable/proc/remove_all_languages() + LAZYCLEARLIST(languages) + +/atom/movable/proc/has_language(datum/language/dt) + . = is_type_in_typecache(dt, languages) + +/atom/movable/proc/can_speak_in_language(datum/language/dt) + . = has_language(dt) + if(only_speaks_language && !HAS_SECONDARY_FLAG(src, OMNITONGUE)) + . = . && ispath(only_speaks_language, dt) + +/atom/movable/proc/get_default_language() + // if no language is specified, and we want to say() something, which + // language do we use? + var/datum/language/chosen_langtype + var/highest_priority + + for(var/lt in languages) + var/datum/language/langtype = lt + if(!can_speak_in_language(langtype)) + continue + + var/pri = initial(langtype.default_priority) + if(!highest_priority || (pri > highest_priority)) + chosen_langtype = langtype + highest_priority = pri + + . = chosen_langtype diff --git a/code/game/gamemodes/clock_cult/clock_helpers/ratvarian_language.dm b/code/game/gamemodes/clock_cult/clock_helpers/ratvarian_language.dm index 3ca0a042b52..fcb30a096da 100644 --- a/code/game/gamemodes/clock_cult/clock_helpers/ratvarian_language.dm +++ b/code/game/gamemodes/clock_cult/clock_helpers/ratvarian_language.dm @@ -101,14 +101,11 @@ List of nuances: /proc/clockwork_say(atom/movable/AM, message, whisper=FALSE) var/list/spans = list(SPAN_ROBOT) - var/old_languages_spoken = AM.languages_spoken - AM.languages_spoken = HUMAN //anyone who can understand HUMAN will hear weird shitty ratvar speak, otherwise it'll get starred out if(isliving(AM)) var/mob/living/L = AM if(!whisper) - L.say(message, "clock", spans) + L.say(message, "clock", spans, language=/datum/language/common) else L.whisper(message) else - AM.say(message) - AM.languages_spoken = old_languages_spoken + AM.say(message, language=/datum/language/common) diff --git a/code/game/gamemodes/clock_cult/clock_items/soul_vessel.dm b/code/game/gamemodes/clock_cult/clock_items/soul_vessel.dm index 1c5a2dd5f8a..505f068d368 100644 --- a/code/game/gamemodes/clock_cult/clock_items/soul_vessel.dm +++ b/code/game/gamemodes/clock_cult/clock_items/soul_vessel.dm @@ -29,7 +29,6 @@ laws = new /datum/ai_laws/ratvar() braintype = picked_fluff_name all_clockwork_objects += src - brainmob.languages_spoken = RATVAR /obj/item/device/mmi/posibrain/soul_vessel/Destroy() all_clockwork_objects -= src diff --git a/code/game/gamemodes/clock_cult/clock_mobs.dm b/code/game/gamemodes/clock_cult/clock_mobs.dm index 3538a8a557c..a5e7dee4cb9 100644 --- a/code/game/gamemodes/clock_cult/clock_mobs.dm +++ b/code/game/gamemodes/clock_cult/clock_mobs.dm @@ -8,14 +8,14 @@ unsuitable_atmos_damage = 0 atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) //Robotic damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0) - languages_spoken = RATVAR - languages_understood = HUMAN|RATVAR healable = FALSE del_on_death = TRUE speak_emote = list("clanks", "clinks", "clunks", "clangs") verb_ask = "requests" verb_exclaim = "proclaims" verb_yell = "harangues" + initial_languages = list(/datum/language/common, /datum/language/ratvar) + only_speaks_language = /datum/language/ratvar bubble_icon = "clock" light_color = "#E42742" death_sound = 'sound/magic/clockwork/anima_fragment_death.ogg' diff --git a/code/game/gamemodes/devil/true_devil/_true_devil.dm b/code/game/gamemodes/devil/true_devil/_true_devil.dm index 5fbf329141c..7f908756b9c 100644 --- a/code/game/gamemodes/devil/true_devil/_true_devil.dm +++ b/code/game/gamemodes/devil/true_devil/_true_devil.dm @@ -14,11 +14,9 @@ ventcrawler = VENTCRAWLER_NONE density = 1 pass_flags = 0 - var/ascended = 0 + var/ascended = FALSE sight = (SEE_TURFS | SEE_OBJS) status_flags = CANPUSH - languages_spoken = ALL //The devil speaks all languages meme - languages_understood = ALL //The devil speaks all languages meme mob_size = MOB_SIZE_LARGE var/mob/living/oldform var/list/devil_overlays[DEVIL_TOTAL_LAYERS] @@ -31,6 +29,8 @@ create_bodyparts() //initialize bodyparts create_internal_organs() + + grant_all_languages(omnitongue=TRUE) ..() /mob/living/carbon/true_devil/create_internal_organs() @@ -42,7 +42,7 @@ /mob/living/carbon/true_devil/proc/convert_to_archdevil() maxHealth = 5000 // not an IMPOSSIBLE amount, but still near impossible. - ascended = 1 + ascended = TRUE health = maxHealth icon_state = "arch_devil" @@ -220,4 +220,4 @@ return /mob/living/carbon/true_devil/update_damage_overlays() //devils don't have damage overlays. - return \ No newline at end of file + return diff --git a/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm index 3cde9bcec75..f424b225f04 100644 --- a/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm +++ b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm @@ -62,6 +62,7 @@ icon = 'icons/mob/swarmer.dmi' desc = "A robot of unknown design, they seek only to consume materials and replicate themselves indefinitely." speak_emote = list("tones") + initial_languages = list(/datum/language/swarmer) bubble_icon = "swarmer" health = 40 maxHealth = 40 @@ -81,8 +82,6 @@ melee_damage_type = STAMINA damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0) hud_possible = list(ANTAG_HUD, DIAG_STAT_HUD, DIAG_HUD) - languages_spoken = SWARMER - languages_understood = SWARMER obj_damage = 0 environment_smash = 0 attacktext = "shocks" diff --git a/code/game/gamemodes/miniantags/revenant/revenant.dm b/code/game/gamemodes/miniantags/revenant/revenant.dm index 3c597e7f325..000686fc5d8 100644 --- a/code/game/gamemodes/miniantags/revenant/revenant.dm +++ b/code/game/gamemodes/miniantags/revenant/revenant.dm @@ -23,8 +23,6 @@ sight = SEE_SELF see_invisible = SEE_INVISIBLE_NOLIGHTING see_in_dark = 8 - languages_spoken = ALL - languages_understood = ALL response_help = "passes through" response_disarm = "swings through" response_harm = "punches through" diff --git a/code/game/machinery/announcement_system.dm b/code/game/machinery/announcement_system.dm index 851b47f3a29..4542f7e610e 100644 --- a/code/game/machinery/announcement_system.dm +++ b/code/game/machinery/announcement_system.dm @@ -103,10 +103,10 @@ var/list/announcement_systems = list() message = "The arrivals shuttle has been damaged. Docking for repairs..." if(channels.len == 0) - radio.talk_into(src, message, null, list(SPAN_ROBOT)) + radio.talk_into(src, message, null, list(SPAN_ROBOT), get_default_language()) else for(var/channel in channels) - radio.talk_into(src, message, channel, list(SPAN_ROBOT)) + radio.talk_into(src, message, channel, list(SPAN_ROBOT), get_default_language()) //config stuff diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index ce80e490e10..73f5cc4c209 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -6,7 +6,7 @@ #define CLONE_INITIAL_DAMAGE 190 //Clones in clonepods start with 190 cloneloss damage and 190 brainloss damage, thats just logical #define MINIMUM_HEAL_LEVEL 40 -#define SPEAK(message) radio.talk_into(src, message, radio_channel, get_spans()) +#define SPEAK(message) radio.talk_into(src, message, radio_channel, get_spans(), get_default_language()) /obj/machinery/clonepod anchored = 1 diff --git a/code/game/machinery/doors/brigdoors.dm b/code/game/machinery/doors/brigdoors.dm index de94bbbc85a..af0a4940e70 100644 --- a/code/game/machinery/doors/brigdoors.dm +++ b/code/game/machinery/doors/brigdoors.dm @@ -113,7 +113,7 @@ if(!forced) Radio.set_frequency(SEC_FREQ) - Radio.talk_into(src, "Timer has expired. Releasing prisoner.", SEC_FREQ) + Radio.talk_into(src, "Timer has expired. Releasing prisoner.", SEC_FREQ, get_default_language()) timing = FALSE activation_time = null diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index 17d849932d1..6a5a2da8c34 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -39,8 +39,6 @@ var/list/holopads = list() icon_state = "holopad0" layer = LOW_OBJ_LAYER flags = HEAR - languages_spoken = ROBOT | HUMAN - languages_understood = ROBOT | HUMAN anchored = 1 use_power = 1 idle_power_usage = 5 @@ -189,11 +187,11 @@ var/list/holopads = list() /*This is the proc for special two-way communication between AI and holopad/people talking near holopad. For the other part of the code, check silicon say.dm. Particularly robot talk.*/ -/obj/machinery/holopad/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans) +/obj/machinery/holopad/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans) if(speaker && masters.len && !radio_freq)//Master is mostly a safety in case lag hits or something. Radio_freq so AIs dont hear holopad stuff through radios. for(var/mob/living/silicon/ai/master in masters) if(masters[master] && speaker != master) - master.relay_speech(message, speaker, message_langs, raw_message, radio_freq, spans) + master.relay_speech(message, speaker, message_language, raw_message, radio_freq, spans) /obj/machinery/holopad/proc/create_holo(mob/living/silicon/ai/A, turf/T = loc) var/obj/effect/overlay/holo_pad_hologram/h = new(T)//Spawn a blank effect at the location. diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm index a583e579924..ee72082f1ea 100644 --- a/code/game/machinery/requests_console.dm +++ b/code/game/machinery/requests_console.dm @@ -326,11 +326,9 @@ var/list/obj/machinery/requests_console/allConsoles = list() emergency = "Medical" if(radio_freq) Radio.set_frequency(radio_freq) - Radio.talk_into(src,"[emergency] emergency in [department]!!",radio_freq) + Radio.talk_into(src,"[emergency] emergency in [department]!!",radio_freq,get_spans(),get_default_language()) update_icon() - spawn(3000) - emergency = null - update_icon() + addtimer(CALLBACK(src, .proc/clear_emergency), 3000) if( href_list["department"] && message ) var/log_msg = message @@ -386,7 +384,7 @@ var/list/obj/machinery/requests_console/allConsoles = list() screen = 6 if(radio_freq) - Radio.talk_into(src,"[alert]: [message]",radio_freq) + Radio.talk_into(src,"[alert]: [message]",radio_freq,get_spans(),get_default_language()) switch(priority) if(2) @@ -441,13 +439,17 @@ var/list/obj/machinery/requests_console/allConsoles = list() updateUsrDialog() return -/obj/machinery/say_quote(input, list/spans) +/obj/machinery/requests_console/say_quote(input, list/spans) var/ending = copytext(input, length(input) - 2) if (ending == "!!!") return "blares, \"[attach_spans(input, spans)]\"" return ..() +/obj/machinery/requests_console/proc/clear_emergency() + emergency = null + update_icon() + /obj/machinery/requests_console/proc/createmessage(source, title, message, priority) var/linkedsender if(istype(source, /obj/machinery/requests_console)) diff --git a/code/game/machinery/telecomms/broadcasting.dm b/code/game/machinery/telecomms/broadcasting.dm index 94d17a71812..566fe74f0f9 100644 --- a/code/game/machinery/telecomms/broadcasting.dm +++ b/code/game/machinery/telecomms/broadcasting.dm @@ -58,7 +58,7 @@ var/vmask, var/obj/item/device/radio/radio, var/message, var/name, var/job, var/realname, var/data, var/compression, var/list/level, var/freq, var/list/spans, - var/verb_say, var/verb_ask, var/verb_exclaim, var/verb_yell) + var/verb_say, var/verb_ask, var/verb_exclaim, var/verb_yell, var/datum/language/language) message = copytext(message, 1, MAX_BROADCAST_LEN) @@ -70,8 +70,6 @@ var/atom/movable/virtualspeaker/virt = new /atom/movable/virtualspeaker(null) virt.name = name virt.job = job - virt.languages_spoken = AM.languages_spoken - virt.languages_understood = AM.languages_understood virt.source = AM virt.radio = radio virt.verb_say = verb_say @@ -136,9 +134,9 @@ if(isobserver(M) && M.client && (M.client.prefs.chat_toggles & CHAT_GHOSTRADIO)) receive |= M - var/rendered = virt.compose_message(virt, virt.languages_spoken, message, freq, spans) //Always call this on the virtualspeaker to advoid issues. + var/rendered = virt.compose_message(virt, language, message, freq, spans) //Always call this on the virtualspeaker to advoid issues. for(var/atom/movable/hearer in receive) - hearer.Hear(rendered, virt, AM.languages_spoken, message, freq, spans) + hearer.Hear(rendered, virt, language, message, freq, spans) if(length(receive)) // --- This following recording is intended for research and feedback in the use of department radio channels --- @@ -172,238 +170,6 @@ spawn(50) qdel(virt) -/proc/Broadcast_SimpleMessage(source, frequency, text, data, mob/M, compression, level) - - /* ###### Prepare the radio connection ###### */ - - if(!M) - var/mob/living/carbon/human/H = new - M = H - - var/datum/radio_frequency/connection = SSradio.return_frequency(frequency) - - var/display_freq = connection.frequency - - var/list/receive = list() - - - // --- Broadcast only to intercom devices --- - - if(data == 1) - for (var/obj/item/device/radio/intercom/R in connection.devices["[RADIO_CHAT]"]) - var/turf/position = get_turf(R) - if(position && position.z == level) - receive |= R.send_hear(display_freq, level) - - - // --- Broadcast only to intercoms and station-bounced radios --- - - else if(data == 2) - for (var/obj/item/device/radio/R in connection.devices["[RADIO_CHAT]"]) - if(R.subspace_transmission) - continue - var/turf/position = get_turf(R) - if(position && position.z == level) - receive |= R.send_hear(display_freq) - - - // --- Broadcast to syndicate radio! --- - - else if(data == 3) - var/datum/radio_frequency/syndicateconnection = SSradio.return_frequency(SYND_FREQ) - - for (var/obj/item/device/radio/R in syndicateconnection.devices["[RADIO_CHAT]"]) - var/turf/position = get_turf(R) - if(position && position.z == level) - receive |= R.send_hear(SYND_FREQ) - - // --- Centcom radio, yo. --- - - else if(data == 5) - - for(var/obj/item/device/radio/R in all_radios["[RADIO_CHAT]"]) - if(R.independent) - receive |= R.send_hear(display_freq) - - // --- Broadcast to ALL radio devices --- - - else - for (var/obj/item/device/radio/R in connection.devices["[RADIO_CHAT]"]) - var/turf/position = get_turf(R) - if(position && position.z == level) - receive |= R.send_hear(display_freq) - - - /* ###### Organize the receivers into categories for displaying the message ###### */ - - // Understood the message: - var/list/heard_normal = list() // normal message - - // Did not understand the message: - var/list/heard_garbled = list() // garbled message (ie "f*c* **u, **i*er!") - var/list/heard_gibberish= list() // completely screwed over message (ie "F%! (O*# *#!<>&**%!") - - for (var/mob/R in receive) - - /* --- Loop through the receivers and categorize them --- */ - - if (R.client && !(R.client.prefs.chat_toggles & CHAT_RADIO)) //Adminning with 80 people on can be fun when you're trying to talk and all you can hear is radios. - continue - - - // --- Check for compression --- - if(compression > 0) - - heard_gibberish += R - continue - - // --- Can understand the speech --- - - if (R.languages_understood & M.languages_spoken) - - heard_normal += R - - // --- Can't understand the speech --- - - else - // - Just display a garbled message - - - heard_garbled += R - - - /* ###### Begin formatting and sending the message ###### */ - if (length(heard_normal) || length(heard_garbled) || length(heard_gibberish)) - - /* --- Some miscellaneous variables to format the string output --- */ - var/part_a = "" // goes in the actual output - var/freq_text // the name of the channel - - // --- Set the name of the channel --- - switch(display_freq) - - if(SYND_FREQ) - freq_text = "#unkn" - if(COMM_FREQ) - freq_text = "Command" - if(SCI_FREQ) - freq_text = "Science" - if(MED_FREQ) - freq_text = "Medical" - if(ENG_FREQ) - freq_text = "Engineering" - if(SEC_FREQ) - freq_text = "Security" - if(SERV_FREQ) - freq_text = "Service" - if(SUPP_FREQ) - freq_text = "Supply" - if(AIPRIV_FREQ) - freq_text = "AI Private" - //There's probably a way to use the list var of channels in code\game\communications.dm to make the dept channels non-hardcoded, but I wasn't in an experimentive mood. --NEO - - - // --- If the frequency has not been assigned a name, just use the frequency as the name --- - - if(!freq_text) - freq_text = format_frequency(display_freq) - - // --- Some more pre-message formatting --- - - var/part_b_extra = "" - if(data == 3) // intercepted radio message - part_b_extra = " (Intercepted)" - - // Create a radio headset for the sole purpose of using its icon - var/obj/item/device/radio/headset/radio = new - - var/part_b = " \icon[radio]\[[freq_text]\][part_b_extra] " - var/part_c = "" - - if (display_freq==SYND_FREQ) - part_a = "" - else if (display_freq==COMM_FREQ) - part_a = "" - else if (display_freq==SCI_FREQ) - part_a = "" - else if (display_freq==MED_FREQ) - part_a = "" - else if (display_freq==ENG_FREQ) - part_a = "" - else if (display_freq==SEC_FREQ) - part_a = "" - else if (display_freq==SERV_FREQ) - part_a = "" - else if (display_freq==SUPP_FREQ) - part_a = "" - else if (display_freq==CENTCOM_FREQ) - part_a = "" - else if (display_freq==AIPRIV_FREQ) - part_a = "" - - // --- This following recording is intended for research and feedback in the use of department radio channels --- - - var/part_blackbox_b = " \[[freq_text]\] " - var/blackbox_msg = "[part_a][source][part_blackbox_b]\"[text]\"[part_c]" - //var/blackbox_admin_msg = "[part_a][M.name] (Real name: [M.real_name])[part_blackbox_b][quotedmsg][part_c]" - - //BR.messages_admin += blackbox_admin_msg - if(istype(blackbox)) - switch(display_freq) - if(1459) - blackbox.msg_common += blackbox_msg - if(1351) - blackbox.msg_science += blackbox_msg - if(1353) - blackbox.msg_command += blackbox_msg - if(1355) - blackbox.msg_medical += blackbox_msg - if(1357) - blackbox.msg_engineering += blackbox_msg - if(1359) - blackbox.msg_security += blackbox_msg - if(1441) - blackbox.msg_deathsquad += blackbox_msg - if(1213) - blackbox.msg_syndicate += blackbox_msg - if(1349) - blackbox.msg_service += blackbox_msg - if(1347) - blackbox.msg_cargo += blackbox_msg - else - blackbox.messages += blackbox_msg - - //End of research and feedback code. - - /* ###### Send the message ###### */ - - /* --- Process all the mobs that heard the voice normally (understood) --- */ - - if (length(heard_normal)) - var/rendered = "[part_a][source][part_b]\"[text]\"[part_c]" - - for (var/mob/R in heard_normal) - R.show_message(rendered, 2) - - /* --- Process all the mobs that heard a garbled voice (did not understand) --- */ - // Displays garbled message (ie "f*c* **u, **i*er!") - - if (length(heard_garbled)) - var/quotedmsg = "\"[stars(text)]\"" - var/rendered = "[part_a][source][part_b][quotedmsg][part_c]" - - for (var/mob/R in heard_garbled) - R.show_message(rendered, 2) - - - /* --- Complete gibberish. Usually happens when there's a compressed message --- */ - - if (length(heard_gibberish)) - var/quotedmsg = "\"[Gibberish(text, compression + 50)]\"" - var/rendered = "[part_a][Gibberish(source, compression + 50)][part_b][quotedmsg][part_c]" - - for (var/mob/R in heard_gibberish) - R.show_message(rendered, 2) - //Use this to test if an obj can communicate with a Telecommunications Network /atom/proc/test_telecomms() diff --git a/code/game/machinery/telecomms/machines/allinone.dm b/code/game/machinery/telecomms/machines/allinone.dm index 1e04ee649f6..367a9896f01 100644 --- a/code/game/machinery/telecomms/machines/allinone.dm +++ b/code/game/machinery/telecomms/machines/allinone.dm @@ -39,7 +39,8 @@ signal.data["radio"], signal.data["message"], signal.data["name"], signal.data["job"], signal.data["realname"],, signal.data["compression"], list(0, z), signal.frequency, signal.data["spans"], - signal.data["verb_say"], signal.data["verb_ask"], signal.data["verb_exclaim"], signal.data["verb_yell"]) + signal.data["verb_say"], signal.data["verb_ask"], signal.data["verb_exclaim"], signal.data["verb_yell"], + signal.data["language"]) /obj/machinery/telecomms/allinone/attackby(obj/item/P, mob/user, params) diff --git a/code/game/machinery/telecomms/machines/broadcaster.dm b/code/game/machinery/telecomms/machines/broadcaster.dm index 12712211bb3..877f80dbcb7 100644 --- a/code/game/machinery/telecomms/machines/broadcaster.dm +++ b/code/game/machinery/telecomms/machines/broadcaster.dm @@ -56,17 +56,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept signal.data["vmask"], signal.data["radio"], signal.data["message"], signal.data["name"], signal.data["job"], signal.data["realname"], 0, signal.data["compression"], signal.data["level"], signal.frequency, signal.data["spans"], - signal.data["verb_say"], signal.data["verb_ask"], signal.data["verb_exclaim"], signal.data["verb_yell"]) - - - /** #### - Simple Broadcast - #### **/ - - if(signal.data["type"] == 1) - - /* ###### Broadcast a message using signal.data ###### */ - Broadcast_SimpleMessage(signal.data["name"], signal.frequency, - signal.data["message"],null, null, - signal.data["compression"], listening_level) + signal.data["verb_say"], signal.data["verb_ask"], signal.data["verb_exclaim"], signal.data["verb_yell"], signal.data["language"]) /** #### - Artificial Broadcast - #### **/ @@ -81,7 +71,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept signal.data["radio"], signal.data["message"], signal.data["name"], signal.data["job"], signal.data["realname"], 4, signal.data["compression"], signal.data["level"], signal.frequency, signal.data["spans"], - signal.data["verb_say"], signal.data["verb_ask"], signal.data["verb_exclaim"], signal.data["verb_yell"]) + signal.data["verb_say"], signal.data["verb_ask"], signal.data["verb_exclaim"], signal.data["verb_yell"], signal.data["language"]) if(!message_delay) message_delay = 1 @@ -133,4 +123,4 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept autolinkers = list("broadcasterB") /obj/machinery/telecomms/broadcaster/preset_left/birdstation - name = "Broadcaster" \ No newline at end of file + name = "Broadcaster" diff --git a/code/game/machinery/telecomms/machines/server.dm b/code/game/machinery/telecomms/machines/server.dm index 2039ead4c68..f26e7cfe4d0 100644 --- a/code/game/machinery/telecomms/machines/server.dm +++ b/code/game/machinery/telecomms/machines/server.dm @@ -72,7 +72,8 @@ log.parameters["name"] = signal.data["name"] log.parameters["realname"] = signal.data["realname"] - log.parameters["uspeech"] = signal.data["languages"] & HUMAN //good enough + //log.parameters["uspeech"] = signal.data["languages"] & HUMAN //good enough + // TODO languages: ^ I don't know what this does // If the signal is still compressed, make the log entry gibberish if(signal.data["compression"] > 0) @@ -188,4 +189,4 @@ /obj/machinery/telecomms/server/presets/common/birdstation/New() ..() - freq_listening = list() \ No newline at end of file + freq_listening = list() diff --git a/code/game/machinery/telecomms/telecomunications.dm b/code/game/machinery/telecomms/telecomunications.dm index 0bb9f414ed3..d7792ec314b 100644 --- a/code/game/machinery/telecomms/telecomunications.dm +++ b/code/game/machinery/telecomms/telecomunications.dm @@ -86,7 +86,8 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() "verb_say" = signal.data["verb_say"], "verb_ask" = signal.data["verb_ask"], "verb_exclaim" = signal.data["verb_exclaim"], - "verb_yell" = signal.data["verb_yell"] + "verb_yell" = signal.data["verb_yell"], + "language" = signal.data["language"] ) // Keep the "original" signal constant diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 9ec72374407..b2fac45c4c8 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -382,17 +382,16 @@ /obj/mecha/proc/drop_item()//Derpfix, but may be useful in future for engineering exosuits. return -/obj/mecha/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans) +/obj/mecha/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans) if(speaker == occupant) if(radio.broadcasting) - radio.talk_into(speaker, text, , spans) + radio.talk_into(speaker, text, , spans, message_language) //flick speech bubble var/list/speech_bubble_recipients = list() for(var/mob/M in get_hearers_in_view(7,src)) if(M.client) speech_bubble_recipients.Add(M.client) - spawn(0) - flick_overlay(image('icons/mob/talk.dmi', src, "machine[say_test(raw_message)]",MOB_LAYER+1), speech_bubble_recipients, 30) + INVOKE_ASYNC(GLOBAL_PROC, /.proc/flick_overlay, image('icons/mob/talk.dmi', src, "machine[say_test(raw_message)]",MOB_LAYER+1), speech_bubble_recipients, 30) //////////////////////////// ///// Action processing //// diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 51965b46a05..85b1886e6c9 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -349,7 +349,7 @@ var/global/image/fire_overlay = image("icon" = 'icons/effects/fire.dmi', "icon_s return 1 return 0 -/obj/item/proc/talk_into(mob/M, input, channel, spans) +/obj/item/proc/talk_into(mob/M, input, channel, spans, datum/language/language) return ITALICS | REDUCE_RANGE /obj/item/proc/dropped(mob/user) diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index a5dfc478ae6..5d36d76de97 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -29,8 +29,6 @@ // "Example" = FREQ_LISTENING|FREQ_BROADCASTING flags = CONDUCT | HEAR slot_flags = SLOT_BELT - languages_spoken = HUMAN | ROBOT - languages_understood = HUMAN | ROBOT throw_speed = 3 throw_range = 7 w_class = WEIGHT_CLASS_SMALL @@ -190,11 +188,15 @@ recalculateChannels() . = TRUE -/obj/item/device/radio/talk_into(atom/movable/M, message, channel, list/spans) - INVOKE_ASYNC(src, .proc/talk_into_impl, M, message, channel, spans) +/obj/item/device/radio/talk_into(atom/movable/M, message, channel, list/spans, datum/language/language) + if(!spans) + spans = M.get_spans() + if(!language) + language = M.get_default_language() + INVOKE_ASYNC(src, .proc/talk_into_impl, M, message, channel, spans, language) return ITALICS | REDUCE_RANGE -/obj/item/device/radio/proc/talk_into_impl(atom/movable/M, message, channel, list/spans) +/obj/item/device/radio/proc/talk_into_impl(atom/movable/M, message, channel, list/spans, datum/language/language) if(!on) return // the device has to be on // Fix for permacell radios, but kinda eh about actually fixing them. if(!M || !message) return @@ -322,18 +324,18 @@ "server" = null, "reject" = 0, "level" = 0, - "languages" = languages_spoken, + "language" = language, "spans" = spans, "verb_say" = M.verb_say, "verb_ask" = M.verb_ask, "verb_exclaim" = M.verb_exclaim, - "verb_yell" = M.verb_yell + "verb_yell" = M.verb_yell, ) signal.frequency = freqnum // Quick frequency set Broadcast_Message(M, voicemask, src, message, voice, jobname, real_name, 5, signal.data["compression"], list(position.z, 0), freq, spans, - verb_say, verb_ask, verb_exclaim, verb_yell) + verb_say, verb_ask, verb_exclaim, verb_yell, language) return /* ###### Radio headsets can only broadcast through subspace ###### */ @@ -367,7 +369,7 @@ "server" = null, // the last server to log this signal "reject" = 0, // if nonzero, the signal will not be accepted by any broadcasting machinery "level" = position.z, // The source's z level - "languages" = M.languages_spoken, //The languages M is talking in. + "language" = language, "spans" = spans, //the span classes of this message. "verb_say" = M.verb_say, //the verb used when talking normally "verb_ask" = M.verb_ask, //the verb used when asking @@ -417,7 +419,7 @@ "server" = null, "reject" = 0, "level" = position.z, - "languages" = languages_spoken, + "language" = language, "spans" = spans, "verb_say" = M.verb_say, "verb_ask" = M.verb_ask, @@ -440,14 +442,14 @@ Broadcast_Message(M, voicemask, src, message, voice, jobname, real_name, filter_type, signal.data["compression"], list(position.z), freq, spans, - verb_say, verb_ask, verb_exclaim, verb_yell) + verb_say, verb_ask, verb_exclaim, verb_yell, language) -/obj/item/device/radio/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans) +/obj/item/device/radio/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans) if(radio_freq) return if(broadcasting) if(get_dist(src, speaker) <= canhear_range) - talk_into(speaker, raw_message, , spans) + talk_into(speaker, raw_message, , spans, language=message_language) /* /obj/item/device/radio/proc/accept_rad(obj/item/device/radio/R as obj, message) diff --git a/code/game/objects/items/devices/taperecorder.dm b/code/game/objects/items/devices/taperecorder.dm index 74d3838a4da..08db2d3fab9 100644 --- a/code/game/objects/items/devices/taperecorder.dm +++ b/code/game/objects/items/devices/taperecorder.dm @@ -6,8 +6,6 @@ w_class = WEIGHT_CLASS_SMALL flags = HEAR slot_flags = SLOT_BELT - languages_spoken = ALL //this is a translator, after all. - languages_understood = ALL //this is a translator, after all. materials = list(MAT_METAL=60, MAT_GLASS=30) force = 2 throwforce = 0 @@ -15,14 +13,16 @@ var/playing = 0 var/playsleepseconds = 0 var/obj/item/device/tape/mytape + var/starting_tape_type = /obj/item/device/tape/random var/open_panel = 0 var/canprint = 1 -/obj/item/device/taperecorder/New() - mytape = new /obj/item/device/tape/random(src) - update_icon() +/obj/item/device/taperecorder/Initialize(mapload) ..() + if(starting_tape_type) + mytape = new starting_tape_type(src) + update_icon() /obj/item/device/taperecorder/examine(mob/user) @@ -229,8 +229,8 @@ //empty tape recorders -/obj/item/device/taperecorder/empty/New() - return +/obj/item/device/taperecorder/empty + starting_tape_type = null /obj/item/device/tape @@ -287,4 +287,4 @@ //Random colour tapes /obj/item/device/tape/random/New() icon_state = "tape_[pick("white", "blue", "red", "yellow", "purple")]" - ..() \ No newline at end of file + ..() diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index a2218658651..04c90fdf317 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -1306,9 +1306,9 @@ to_chat(user, "You name the dummy as \"[doll_name]\"") name = "[initial(name)] - [doll_name]" -/obj/item/toy/dummy/talk_into(atom/movable/M, message, channel, list/spans) +/obj/item/toy/dummy/talk_into(atom/movable/M, message, channel, list/spans, datum/language/language) log_say("[key_name(M)] : through dummy : [message]") - say(message) + say(message, language) return NOPASS /obj/item/toy/dummy/GetVoice() diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index 02dff1b445d..0a4c310e75f 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -1,6 +1,4 @@ /obj - languages_spoken = HUMAN - languages_understood = HUMAN var/crit_fail = FALSE animate_movement = 2 var/throwforce = 0 diff --git a/code/game/say.dm b/code/game/say.dm index 1366cf7435b..1cb400d694e 100644 --- a/code/game/say.dm +++ b/code/game/say.dm @@ -18,30 +18,32 @@ var/list/freqtospan = list( "1217" = "blueteamradio" ) -/atom/movable/proc/say(message) +/atom/movable/proc/say(message, datum/language/language = null) if(!can_speak()) return if(message == "" || !message) return var/list/spans = get_spans() - send_speech(message, 7, src, , spans) + if(!language) + language = get_default_language() + send_speech(message, 7, src, , spans, message_language=language) -/atom/movable/proc/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans) +/atom/movable/proc/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans) return /atom/movable/proc/can_speak() return 1 -/atom/movable/proc/send_speech(message, range = 7, obj/source = src, bubble_type, list/spans) - var/rendered = compose_message(src, languages_spoken, message, , spans) +/atom/movable/proc/send_speech(message, range = 7, obj/source = src, bubble_type, list/spans, datum/language/message_language = null) + var/rendered = compose_message(src, message_language, message, , spans) for(var/atom/movable/AM in get_hearers_in_view(range, src)) - AM.Hear(rendered, src, languages_spoken, message, , spans) + AM.Hear(rendered, src, message_language, message, , spans) //To get robot span classes, stuff like that. /atom/movable/proc/get_spans() return list() -/atom/movable/proc/compose_message(atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans) +/atom/movable/proc/compose_message(atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans) //This proc uses text() because it is faster than appending strings. Thanks BYOND. //Basic span var/spanpart1 = "" @@ -54,9 +56,9 @@ var/list/freqtospan = list( //End name span. var/endspanpart = "" //Message - var/messagepart = " [lang_treat(speaker, message_langs, raw_message, spans)]" + var/messagepart = " [lang_treat(speaker, message_language, raw_message, spans)]" - return "[spanpart1][spanpart2][freqpart][compose_track_href(speaker, namepart)][namepart][compose_job(speaker, message_langs, raw_message, radio_freq)][endspanpart][messagepart]" + return "[spanpart1][spanpart2][freqpart][compose_track_href(speaker, namepart)][namepart][compose_job(speaker, message_language, raw_message, radio_freq)][endspanpart][messagepart]" /atom/movable/proc/compose_track_href(atom/movable/speaker, message_langs, raw_message, radio_freq) return "" @@ -79,8 +81,8 @@ var/list/freqtospan = list( return "[verb_say], \"[input]\"" -/atom/movable/proc/lang_treat(atom/movable/speaker, message_langs, raw_message, list/spans) - if(languages_understood & message_langs) +/atom/movable/proc/lang_treat(atom/movable/speaker, datum/language/language, raw_message, list/spans) + if(has_language(language)) var/atom/movable/AM = speaker.GetSource() if(AM) //Basically means "if the speaker is virtual" if(AM.verb_say != speaker.verb_say || AM.verb_ask != speaker.verb_ask || AM.verb_exclaim != speaker.verb_exclaim || AM.verb_yell != speaker.verb_yell) //If the saymod was changed @@ -88,26 +90,14 @@ var/list/freqtospan = list( return AM.say_quote(raw_message, spans) else return speaker.say_quote(raw_message, spans) - else if((message_langs & HUMAN) || (message_langs & RATVAR)) //it's human or ratvar language + else if(language) var/atom/movable/AM = speaker.GetSource() - if(message_langs & HUMAN) - raw_message = stars(raw_message) - if(message_langs & RATVAR) - raw_message = text2ratvar(raw_message) + var/datum/language/D = new language + raw_message = D.scramble(raw_message) if(AM) return AM.say_quote(raw_message, spans) else return speaker.say_quote(raw_message, spans) - else if(message_langs & MONKEY) - return "chimpers." - else if(message_langs & ALIEN) - return "hisses." - else if(message_langs & ROBOT) - return "beeps rapidly." - else if(message_langs & DRONE) - return "chitters." - else if(message_langs & SWARMER) - return "hums." else return "makes a strange sound." diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index ba146f90861..5230443c9ee 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -83,7 +83,8 @@ var/global/BSACooldown = 0 body += "Traitor panel | " body += "Narrate to | " body += "Subtle message | " - body += "Individual Round Logs" + body += "Individual Round Logs | " + body += "Language Menu" if (M.client) if(!isnewplayer(M)) @@ -823,4 +824,4 @@ var/global/BSACooldown = 0 string = pick( "Admin login: [key_name(src)]") if(string) - message_admins("[string]") \ No newline at end of file + message_admins("[string]") diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 6433f127aeb..976678e443f 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -622,10 +622,7 @@ var/list/admin_verbs_hideable = list( var/message = input(usr, "What do you want the message to be?", "Make Sound") as text | null if(!message) return - var/templanguages = O.languages_spoken - O.languages_spoken |= ALL O.say(message) - O.languages_spoken = templanguages log_admin("[key_name(usr)] made [O] at [O.x], [O.y], [O.z] say \"[message]\"") message_admins("[key_name_admin(usr)] made [O] at [O.x], [O.y], [O.z]. say \"[message]\"") feedback_add_details("admin_verb","OS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index c7f221d85a0..ca558a9565a 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1814,6 +1814,16 @@ return show_individual_logging_panel(M, href_list["log_type"]) + else if(href_list["languagemenu"]) + if(!check_rights(R_ADMIN)) + return + + var/mob/living/L = locate(href_list["languagemenu"]) in mob_list + if(!isliving(L)) + to_chat(usr, "This can only be used on instances of type /mob/living.") + return + + L.open_language_menu(usr) else if(href_list["traitor"]) if(!check_rights(R_ADMIN)) diff --git a/code/modules/assembly/voice.dm b/code/modules/assembly/voice.dm index f7c3cbf9301..d8c1ee69561 100644 --- a/code/modules/assembly/voice.dm +++ b/code/modules/assembly/voice.dm @@ -12,7 +12,7 @@ var/listening = 0 var/recorded = "" //the activation message var/mode = 1 - var/global/list/modes = list("inclusive", + var/static/list/modes = list("inclusive", "exclusive", "recognizer", "voice sensor") @@ -21,35 +21,33 @@ ..() to_chat(user, "Use a multitool to swap between \"inclusive\", \"exclusive\", \"recognizer\", and \"voice sensor\" mode.") -/obj/item/device/assembly/voice/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans) +/obj/item/device/assembly/voice/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans) if(speaker == src) return if(listening && !radio_freq) - record_speech(speaker, raw_message) + record_speech(speaker, raw_message, message_language) else if(check_activation(speaker, raw_message)) - spawn(10) - pulse(0) + addtimer(CALLBACK(src, .proc/pulse, 0), 10) -/obj/item/device/assembly/voice/proc/record_speech(atom/movable/speaker, raw_message) +/obj/item/device/assembly/voice/proc/record_speech(atom/movable/speaker, raw_message, datum/language/message_language) switch(mode) if(1) recorded = raw_message listening = 0 - say("Activation message is '[recorded]'.") + say("Activation message is '[recorded]'.", message_language) if(2) recorded = raw_message listening = 0 - say("Activation message is '[recorded]'.") + say("Activation message is '[recorded]'.", message_language) if(3) recorded = speaker.GetVoice() listening = 0 - say("Your voice pattern is saved.") + say("Your voice pattern is saved.", message_language) if(4) if(length(raw_message)) - spawn(10) - pulse(0) + addtimer(CALLBACK(src, .proc/pulse, 0), 10) /obj/item/device/assembly/voice/proc/check_activation(atom/movable/speaker, raw_message) . = 0 diff --git a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm index 9c21c5b5a8e..29e2455f206 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm @@ -115,9 +115,9 @@ on = FALSE update_icon() playsound(T, 'sound/machines/cryo_warning.ogg', volume) // Bug the doctors. - radio.talk_into(src, "Patient fully restored", radio_channel) + radio.talk_into(src, "Patient fully restored", radio_channel, get_spans(), get_default_language()) if(autoeject) // Eject if configured. - radio.talk_into(src, "Auto ejecting patient now", radio_channel) + radio.talk_into(src, "Auto ejecting patient now", radio_channel, get_spans(), get_default_language()) open_machine() return else if(occupant.stat == DEAD) // We don't bother with dead people. diff --git a/code/modules/events/sentience.dm b/code/modules/events/sentience.dm index 8dbd9bf0c88..0b607e87e62 100644 --- a/code/modules/events/sentience.dm +++ b/code/modules/events/sentience.dm @@ -49,8 +49,10 @@ spawned_animals++ SA.key = SG.key - SA.languages_spoken |= HUMAN - SA.languages_understood |= HUMAN + + SA.grant_language(/datum/language/common) + SET_SECONDARY_FLAG(SA, OMNITONGUE) + SA.sentience_act() SA.maxHealth = max(SA.maxHealth, 200) diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index 4516461cb51..a1a64f1f3bb 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -707,6 +707,7 @@ var/list/non_fakeattack_weapons = list(/obj/item/weapon/gun/ballistic, /obj/item var/list/mob/living/carbon/people = list() var/list/mob/living/carbon/person = null + var/datum/language/understood_language = target.get_random_understood_language() for(var/mob/living/carbon/H in view(target)) if(H == target) continue @@ -718,7 +719,7 @@ var/list/non_fakeattack_weapons = list(/obj/item/weapon/gun/ballistic, /obj/item people += H if(person) //Basic talk var/image/speech_overlay = image('icons/mob/talk.dmi', person, "default0", layer = ABOVE_MOB_LAYER) - to_chat(target, target.compose_message(person,person.languages_understood,pick(speak_messages),null,person.get_spans())) + to_chat(target, target.compose_message(person,understood_language,pick(speak_messages),null,person.get_spans())) if(target.client) target.client.images |= speech_overlay sleep(30) @@ -728,7 +729,7 @@ var/list/non_fakeattack_weapons = list(/obj/item/weapon/gun/ballistic, /obj/item for(var/mob/living/carbon/human/H in living_mob_list) humans += H person = pick(humans) - to_chat(target, target.compose_message(person,person.languages_understood,pick(radio_messages),"1459",person.get_spans())) + to_chat(target, target.compose_message(person,understood_language,pick(radio_messages),"1459",person.get_spans())) qdel(src) /obj/effect/hallucination/message diff --git a/code/modules/language/common.dm b/code/modules/language/common.dm new file mode 100644 index 00000000000..b6fb555729a --- /dev/null +++ b/code/modules/language/common.dm @@ -0,0 +1,70 @@ +// 'basic' language; spoken by default. +/datum/language/common + name = "Galactic Common" + desc = "The common galactic tongue." + speech_verb = "says" + whisper_verb = "whispers" + key = "0" + flags = TONGUELESS_SPEECH + default_priority = 100 + +//Syllable Lists +/* + This list really long, mainly because I can't make up my mind about which mandarin syllables should be removed, + and the english syllables had to be duplicated so that there is roughly a 50-50 weighting. + + Sources: + http://www.sttmedia.com/syllablefrequency-english + http://www.chinahighlights.com/travelguide/learning-chinese/pinyin-syllables.htm +*/ +/datum/language/common/syllables = list( +"a", "ai", "an", "ang", "ao", "ba", "bai", "ban", "bang", "bao", "bei", "ben", "beng", "bi", "bian", "biao", +"bie", "bin", "bing", "bo", "bu", "ca", "cai", "can", "cang", "cao", "ce", "cei", "cen", "ceng", "cha", "chai", +"chan", "chang", "chao", "che", "chen", "cheng", "chi", "chong", "chou", "chu", "chua", "chuai", "chuan", "chuang", "chui", "chun", +"chuo", "ci", "cong", "cou", "cu", "cuan", "cui", "cun", "cuo", "da", "dai", "dan", "dang", "dao", "de", "dei", +"den", "deng", "di", "dian", "diao", "die", "ding", "diu", "dong", "dou", "du", "duan", "dui", "dun", "duo", "e", +"ei", "en", "er", "fa", "fan", "fang", "fei", "fen", "feng", "fo", "fou", "fu", "ga", "gai", "gan", "gang", +"gao", "ge", "gei", "gen", "geng", "gong", "gou", "gu", "gua", "guai", "guan", "guang", "gui", "gun", "guo", "ha", +"hai", "han", "hang", "hao", "he", "hei", "hen", "heng", "hm", "hng", "hong", "hou", "hu", "hua", "huai", "huan", +"huang", "hui", "hun", "huo", "ji", "jia", "jian", "jiang", "jiao", "jie", "jin", "jing", "jiong", "jiu", "ju", "juan", +"jue", "jun", "ka", "kai", "kan", "kang", "kao", "ke", "kei", "ken", "keng", "kong", "kou", "ku", "kua", "kuai", +"kuan", "kuang", "kui", "kun", "kuo", "la", "lai", "lan", "lang", "lao", "le", "lei", "leng", "li", "lia", "lian", +"liang", "liao", "lie", "lin", "ling", "liu", "long", "lou", "lu", "luan", "lun", "luo", "ma", "mai", "man", "mang", +"mao", "me", "mei", "men", "meng", "mi", "mian", "miao", "mie", "min", "ming", "miu", "mo", "mou", "mu", "na", +"nai", "nan", "nang", "nao", "ne", "nei", "nen", "neng", "ng", "ni", "nian", "niang", "niao", "nie", "nin", "ning", +"niu", "nong", "nou", "nu", "nuan", "nuo", "o", "ou", "pa", "pai", "pan", "pang", "pao", "pei", "pen", "peng", +"pi", "pian", "piao", "pie", "pin", "ping", "po", "pou", "pu", "qi", "qia", "qian", "qiang", "qiao", "qie", "qin", +"qing", "qiong", "qiu", "qu", "quan", "que", "qun", "ran", "rang", "rao", "re", "ren", "reng", "ri", "rong", "rou", +"ru", "rua", "ruan", "rui", "run", "ruo", "sa", "sai", "san", "sang", "sao", "se", "sei", "sen", "seng", "sha", +"shai", "shan", "shang", "shao", "she", "shei", "shen", "sheng", "shi", "shou", "shu", "shua", "shuai", "shuan", "shuang", "shui", +"shun", "shuo", "si", "song", "sou", "su", "suan", "sui", "sun", "suo", "ta", "tai", "tan", "tang", "tao", "te", +"teng", "ti", "tian", "tiao", "tie", "ting", "tong", "tou", "tu", "tuan", "tui", "tun", "tuo", "wa", "wai", "wan", +"wang", "wei", "wen", "weng", "wo", "wu", "xi", "xia", "xian", "xiang", "xiao", "xie", "xin", "xing", "xiong", "xiu", +"xu", "xuan", "xue", "xun", "ya", "yan", "yang", "yao", "ye", "yi", "yin", "ying", "yong", "you", "yu", "yuan", +"yue", "yun", "za", "zai", "zan", "zang", "zao", "ze", "zei", "zen", "zeng", "zha", "zhai", "zhan", "zhang", "zhao", +"zhe", "zhei", "zhen", "zheng", "zhi", "zhong", "zhou", "zhu", "zhua", "zhuai", "zhuan", "zhuang", "zhui", "zhun", "zhuo", "zi", +"zong", "zou", "zuan", "zui", "zun", "zuo", "zu", +"al", "an", "ar", "as", "at", "ea", "ed", "en", "er", "es", "ha", "he", "hi", "in", "is", "it", +"le", "me", "nd", "ne", "ng", "nt", "on", "or", "ou", "re", "se", "st", "te", "th", "ti", "to", +"ve", "wa", "all", "and", "are", "but", "ent", "era", "ere", "eve", "for", "had", "hat", "hen", "her", "hin", +"his", "ing", "ion", "ith", "not", "ome", "oul", "our", "sho", "ted", "ter", "tha", "the", "thi", +"al", "an", "ar", "as", "at", "ea", "ed", "en", "er", "es", "ha", "he", "hi", "in", "is", "it", +"le", "me", "nd", "ne", "ng", "nt", "on", "or", "ou", "re", "se", "st", "te", "th", "ti", "to", +"ve", "wa", "all", "and", "are", "but", "ent", "era", "ere", "eve", "for", "had", "hat", "hen", "her", "hin", +"his", "ing", "ion", "ith", "not", "ome", "oul", "our", "sho", "ted", "ter", "tha", "the", "thi", +"al", "an", "ar", "as", "at", "ea", "ed", "en", "er", "es", "ha", "he", "hi", "in", "is", "it", +"le", "me", "nd", "ne", "ng", "nt", "on", "or", "ou", "re", "se", "st", "te", "th", "ti", "to", +"ve", "wa", "all", "and", "are", "but", "ent", "era", "ere", "eve", "for", "had", "hat", "hen", "her", "hin", +"his", "ing", "ion", "ith", "not", "ome", "oul", "our", "sho", "ted", "ter", "tha", "the", "thi", +"al", "an", "ar", "as", "at", "ea", "ed", "en", "er", "es", "ha", "he", "hi", "in", "is", "it", +"le", "me", "nd", "ne", "ng", "nt", "on", "or", "ou", "re", "se", "st", "te", "th", "ti", "to", +"ve", "wa", "all", "and", "are", "but", "ent", "era", "ere", "eve", "for", "had", "hat", "hen", "her", "hin", +"his", "ing", "ion", "ith", "not", "ome", "oul", "our", "sho", "ted", "ter", "tha", "the", "thi", +"al", "an", "ar", "as", "at", "ea", "ed", "en", "er", "es", "ha", "he", "hi", "in", "is", "it", +"le", "me", "nd", "ne", "ng", "nt", "on", "or", "ou", "re", "se", "st", "te", "th", "ti", "to", +"ve", "wa", "all", "and", "are", "but", "ent", "era", "ere", "eve", "for", "had", "hat", "hen", "her", "hin", +"his", "ing", "ion", "ith", "not", "ome", "oul", "our", "sho", "ted", "ter", "tha", "the", "thi", +"al", "an", "ar", "as", "at", "ea", "ed", "en", "er", "es", "ha", "he", "hi", "in", "is", "it", +"le", "me", "nd", "ne", "ng", "nt", "on", "or", "ou", "re", "se", "st", "te", "th", "ti", "to", +"ve", "wa", "all", "and", "are", "but", "ent", "era", "ere", "eve", "for", "had", "hat", "hen", "her", "hin", +"his", "ing", "ion", "ith", "not", "ome", "oul", "our", "sho", "ted", "ter", "tha", "the", "thi") diff --git a/code/modules/language/drone.dm b/code/modules/language/drone.dm new file mode 100644 index 00000000000..5128a35d524 --- /dev/null +++ b/code/modules/language/drone.dm @@ -0,0 +1,14 @@ +/datum/language/drone + name = "Drone" + desc = "A heavily encoded damage control coordination stream, with special flags for hats." + speech_verb = "chitters" + ask_verb = "chitters inquisitively" + exclaim_verb = "chitters loudly" + spans = list(SPAN_ROBOT) + key = "d" + flags = NO_STUTTER + syllables = list(".", "|") + // ...|..||.||||.|.||.|.|.|||.||| + space_chance = 0 + sentence_chance = 0 + default_priority = 20 diff --git a/code/modules/language/language.dm b/code/modules/language/language.dm new file mode 100644 index 00000000000..893ed13e93b --- /dev/null +++ b/code/modules/language/language.dm @@ -0,0 +1,95 @@ +#define SCRAMBLE_CACHE_LEN 20 + +/* + Datum based languages. Easily editable and modular. +*/ + +/datum/language + var/name = "an unknown language" // Fluff name of language if any. + var/desc = "A language." // Short description for 'Check Languages'. + var/speech_verb = "says" // 'says', 'hisses', 'farts'. + var/ask_verb = "asks" // Used when sentence ends in a ? + var/exclaim_verb = "exclaims" // Used when sentence ends in a ! + var/whisper_verb = "whispers" // Optional. When not specified speech_verb + quietly/softly is used instead. + var/list/signlang_verb = list("signs", "gestures") // list of emotes that might be displayed if this language has NONVERBAL or SIGNLANG flags + var/key = "x" // Character used to speak in language + var/flags // Various language flags. + var/list/syllables // Used when scrambling text for a non-speaker. + var/list/sentence_chance = 5 // Likelihood of making a new sentence after each syllable. + var/list/space_chance = 55 // Likelihood of getting a space in the random scramble string + var/list/spans = list() + var/static/list/scramble_cache = list() + var/default_priority = 0 // the language that an atom knows with the highest "default_priority" is selected by default. + +/datum/language/proc/get_random_name(gender, name_count=2, syllable_count=4, syllable_divisor=2) + if(!syllables || !syllables.len) + if(gender==FEMALE) + return capitalize(pick(first_names_female)) + " " + capitalize(pick(last_names)) + else + return capitalize(pick(first_names_male)) + " " + capitalize(pick(last_names)) + + var/full_name = "" + var/new_name = "" + + for(var/i in 0 to name_count) + new_name = "" + var/Y = rand(Floor(syllable_count/syllable_divisor), syllable_count) + for(var/x in Y to 0) + new_name += pick(syllables) + full_name += " [capitalize(lowertext(new_name))]" + + return "[trim(full_name)]" + +/datum/language/proc/scramble(input) + + if(!syllables || !syllables.len) + return stars(input) + + // If the input is cached already, move it to the end of the cache and return it + var/lookup = scramble_cache[input] + if(lookup) + scramble_cache -= input + scramble_cache[input] = lookup + return lookup + + var/input_size = length(input) + var/scrambled_text = "" + var/capitalize = TRUE + + while(length(scrambled_text) < input_size) + var/next = pick(syllables) + if(capitalize) + next = capitalize(next) + capitalize = FALSE + scrambled_text += next + var/chance = rand(100) + if(chance <= sentence_chance) + scrambled_text += ". " + capitalize = TRUE + else if(chance > sentence_chance && chance <= space_chance) + scrambled_text += " " + + scrambled_text = trim(scrambled_text) + var/ending = copytext(scrambled_text, length(scrambled_text)) + if(ending == ".") + scrambled_text = copytext(scrambled_text,1,length(scrambled_text)-1) + var/input_ending = copytext(input, input_size) + if(input_ending in list("!","?",".")) + scrambled_text += input_ending + + // Add it to cache, cutting old entries if the list is too long + scramble_cache[input] = scrambled_text + if(scramble_cache.len > SCRAMBLE_CACHE_LEN) + scramble_cache.Cut(1, scramble_cache.len-SCRAMBLE_CACHE_LEN-1) + + return scrambled_text + +/datum/language/proc/get_spoken_verb(msg_end) + switch(msg_end) + if("!") + return exclaim_verb + if("?") + return ask_verb + return speech_verb + +#undef SCRAMBLE_CACHE_LEN diff --git a/code/modules/language/language_menu.dm b/code/modules/language/language_menu.dm new file mode 100644 index 00000000000..66bc9cb06ca --- /dev/null +++ b/code/modules/language/language_menu.dm @@ -0,0 +1,88 @@ +/datum/language_menu + var/mob/living/owner + +/datum/language_menu/New(new_owner) + owner = new_owner + +/datum/language_menu/Destroy() + owner = null + . = ..() + +/datum/language_menu/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = language_menu_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "language_menu", "Language Menu", 700, 800, master_ui, state) + ui.open() + +/datum/language_menu/ui_data(mob/user) + var/list/data = list() + + var/datum/language/mob_default_language = owner.get_default_language() + + data["languages"] = list() + for(var/ld in owner.languages) + var/datum/language/LD = ld + var/list/L = list() + + L["name"] = initial(LD.name) + L["desc"] = initial(LD.desc) + L["key"] = initial(LD.key) + L["is_default"] = (LD == mob_default_language) + L["can_speak"] = owner.can_speak_in_language(LD) + + data["languages"] += list(L) + + if(check_rights_for(user.client, R_ADMIN)) + data["admin_mode"] = TRUE + data["omnitongue"] = HAS_SECONDARY_FLAG(owner, OMNITONGUE) + + data["unknown_languages"] = list() + for(var/ld in subtypesof(/datum/language)) + if(owner.has_language(ld)) + continue + var/datum/language/LD = ld + var/list/L = list() + + L["name"] = initial(LD.name) + L["desc"] = initial(LD.desc) + L["key"] = initial(LD.key) + + data["unknown_languages"] += list(L) + return data + +/datum/language_menu/ui_act(action, params) + if(..()) + return + var/mob/user = usr + + var/language_name = params["language_name"] + var/datum/language/language_datum + for(var/ld in subtypesof(/datum/language)) + var/datum/language/LD = ld + if(language_name == initial(LD.name)) + language_datum = LD + var/is_admin = check_rights_for(user.client, R_ADMIN) + + switch(action) + if("select_default") + if(language_datum) + owner.selected_default_language = language_datum + . = TRUE + if("grant_language") + if(is_admin && language_datum) + owner.grant_language(language_datum) + message_admins("[key_name_admin(user)] granted the [language_name] language to [key_name_admin(owner)].") + log_admin("[key_name(user)] granted the language [language_name] to [key_name(owner)].") + . = TRUE + if("remove_language") + if(is_admin && language_datum) + owner.remove_language(language_datum) + message_admins("[key_name_admin(user)] removed the [language_name] language to [key_name_admin(owner)].") + log_admin("[key_name(user)] removed the language [language_name] to [key_name(owner)].") + . = TRUE + if("toggle_omnitongue") + if(is_admin) + TOGGLE_SECONDARY_FLAG(owner, OMNITONGUE) + message_admins("[key_name_admin(user)] [HAS_SECONDARY_FLAG(owner, OMNITONGUE) ? "enabled" : "disabled"] the ability to speak all languages (that they know) of [key_name_admin(owner)].") + log_admin("[key_name(user)] [HAS_SECONDARY_FLAG(owner, OMNITONGUE) ? "enabled" : "disabled"] the ability to speak all languages (that_they know) of [key_name(owner)].") + . = TRUE diff --git a/code/modules/language/machine.dm b/code/modules/language/machine.dm new file mode 100644 index 00000000000..ea8597e5c21 --- /dev/null +++ b/code/modules/language/machine.dm @@ -0,0 +1,17 @@ +/datum/language/machine + name = "Encoded Audio Language" + desc = "An efficient language of encoded tones developed by synthetics and cyborgs." + speech_verb = "whistles" + ask_verb = "chirps" + exclaim_verb = "whistles loudly" + spans = list(SPAN_ROBOT, "changeling") + key = "6" + flags = NO_STUTTER + syllables = list("beep","beep","beep","beep","beep","boop","boop","boop","bop","bop","dee","dee","doo","doo","hiss","hss","buzz","buzz","bzz","ksssh","keey","wurr","wahh","tzzz") + space_chance = 10 + default_priority = 90 + +/datum/language/machine/get_random_name() + if(prob(70)) + return "[pick(posibrain_names)]-[rand(100, 999)]" + return pick(ai_names) diff --git a/code/modules/language/monkey.dm b/code/modules/language/monkey.dm new file mode 100644 index 00000000000..263d92b8265 --- /dev/null +++ b/code/modules/language/monkey.dm @@ -0,0 +1,10 @@ +/datum/language/monkey + name = "Chimpanzee" + desc = "Ook ook ook." + speech_verb = "chimpers" + ask_verb = "chimpers" + exclaim_verb = "screeches" + key = "1" + space_chance = 100 + syllables = list("oop", "aak", "chee", "eek") + default_priority = 80 diff --git a/code/modules/language/ratvar.dm b/code/modules/language/ratvar.dm new file mode 100644 index 00000000000..2319b408892 --- /dev/null +++ b/code/modules/language/ratvar.dm @@ -0,0 +1,12 @@ +/datum/language/ratvar + name = "Ratvarian" + desc = "A timeless language full of power and incomprehensible to the unenlightened." + speech_verb = "clinks" + ask_verb = "clunks" + exclaim_verb = "clanks" + key = "r" + default_priority = 10 + spans = list(SPAN_ROBOT, "brass") + +/datum/language/ratvar/scramble(var/input) + . = text2ratvar(input) diff --git a/code/modules/language/slime.dm b/code/modules/language/slime.dm new file mode 100644 index 00000000000..cc4c36e2ef7 --- /dev/null +++ b/code/modules/language/slime.dm @@ -0,0 +1,10 @@ +/datum/language/slime + name = "Slime" + desc = "A melodic and complex language spoken by slimes. Some of the notes are inaudible to humans." + speech_verb = "warbles" + ask_verb = "warbles" + exclaim_verb = "warbles" + spans = list("slime") + key = "k" + syllables = list("qr","qrr","xuq","qil","quum","xuqm","vol","xrim","zaoo","qu-uu","qix","qoo","zix","*","!") + default_priority = 70 diff --git a/code/modules/language/swarmer.dm b/code/modules/language/swarmer.dm new file mode 100644 index 00000000000..597f803775f --- /dev/null +++ b/code/modules/language/swarmer.dm @@ -0,0 +1,42 @@ +/datum/language/swarmer + name = "Swarmer" + desc = "A language only consisting of musical notes." + speech_verb = "tones" + ask_verb = "tones inquisitively" + exclaim_verb = "tones loudly" + spans = list(SPAN_ROBOT) + key = "s" + flags = NO_STUTTER + space_chance = 100 + sentence_chance = 0 + default_priority = 60 + // since various flats and sharps are the same, + // all non-accidental notes are doubled in the list + /* The list with unicode symbols for the accents. + syllables = list( + "C", "C", + "C♯", "D♭", + "D", "D", + "D♯", "E♭", + "E", "E", + "F", "F", + "F♯", "G♭", + "G", "G", + "G♯", "A♭", + "A", "A", + "A♯", "B♭", + "B", "B") + */ + syllables = list( + "C", "C", + "C#", "Db", + "D", "D", + "D#", "Eb", + "E", "E", + "F", "F", + "F#", "Gb", + "G", "G", + "G#", "Ab", + "A", "A", + "A#", "Bb", + "B", "B") diff --git a/code/modules/language/xenocommon.dm b/code/modules/language/xenocommon.dm new file mode 100644 index 00000000000..263c3d0a9e0 --- /dev/null +++ b/code/modules/language/xenocommon.dm @@ -0,0 +1,10 @@ +/datum/language/xenocommon + name = "Xenomorph" + desc = "The common tongue of the xenomorphs." + speech_verb = "hisses" + ask_verb = "hisses" + exclaim_verb = "hisses" + spans = list("alien") + key = "4" + syllables = list("sss","sSs","SSS") + default_priority = 50 diff --git a/code/modules/mining/laborcamp/laborstacker.dm b/code/modules/mining/laborcamp/laborstacker.dm index ec71322d3c3..4d58f8207d4 100644 --- a/code/modules/mining/laborcamp/laborstacker.dm +++ b/code/modules/mining/laborcamp/laborstacker.dm @@ -105,7 +105,7 @@ else if(!emagged) Radio.set_frequency(SEC_FREQ) - Radio.talk_into(src, "[inserted_id.registered_name] has returned to the station. Minerals and Prisoner ID card ready for retrieval.", SEC_FREQ) + Radio.talk_into(src, "[inserted_id.registered_name] has returned to the station. Minerals and Prisoner ID card ready for retrieval.", SEC_FREQ, get_spans(), get_default_language()) to_chat(usr, "Shuttle received message and will be sent shortly.") /obj/machinery/mineral/labor_claim_console/proc/check_auth() diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 4cc3bf80c9f..4aa19c9f134 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -19,8 +19,6 @@ var/global/static/observer_default_invisibility = INVISIBILITY_OBSERVER see_invisible = SEE_INVISIBLE_OBSERVER see_in_dark = 100 invisibility = INVISIBILITY_OBSERVER - languages_spoken = ALL - languages_understood = ALL var/can_reenter_corpse var/datum/hud/living/carbon/hud = null // hud var/bootime = 0 @@ -123,6 +121,8 @@ var/global/static/observer_default_invisibility = INVISIBILITY_OBSERVER verbs -= /mob/dead/observer/verb/possess animate(src, pixel_y = 2, time = 10, loop = -1) + + grant_all_languages() ..() /mob/dead/observer/narsie_act() diff --git a/code/modules/mob/dead/observer/say.dm b/code/modules/mob/dead/observer/say.dm index c7ceff6111e..7ae318c264a 100644 --- a/code/modules/mob/dead/observer/say.dm +++ b/code/modules/mob/dead/observer/say.dm @@ -8,15 +8,18 @@ . = src.say_dead(message) -/mob/dead/observer/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans) +/mob/dead/observer/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans) + var/atom/movable/to_follow = speaker if(radio_freq) var/atom/movable/virtualspeaker/V = speaker if(isAI(V.source)) var/mob/living/silicon/ai/S = V.source - speaker = S.eyeobj + to_follow = S.eyeobj else - speaker = V.source - var/link = FOLLOW_LINK(src, speaker) + to_follow = V.source + var/link = FOLLOW_LINK(src, to_follow) + // Recompose the message, because it's scrambled by default + message = compose_message(speaker, message_language, raw_message, radio_freq, spans) to_chat(src, "[link] [message]") diff --git a/code/modules/mob/living/brain/MMI.dm b/code/modules/mob/living/brain/MMI.dm index 100bec5cb98..18372d5e301 100644 --- a/code/modules/mob/living/brain/MMI.dm +++ b/code/modules/mob/living/brain/MMI.dm @@ -1,5 +1,3 @@ - - /obj/item/device/mmi name = "Man-Machine Interface" desc = "The Warrior's bland acronym, MMI, obscures the true horror of this monstrosity, that nevertheless has become standard-issue on Nanotrasen stations." diff --git a/code/modules/mob/living/brain/brain.dm b/code/modules/mob/living/brain/brain.dm index 089efe4444f..33c3d00d2ee 100644 --- a/code/modules/mob/living/brain/brain.dm +++ b/code/modules/mob/living/brain/brain.dm @@ -1,8 +1,6 @@ /mob/living/brain - languages_spoken = HUMAN - languages_understood = HUMAN var/obj/item/device/mmi/container = null var/timeofhostdeath = 0 var/emp_damage = 0//Handles a type of MMI damage @@ -64,4 +62,4 @@ /mob/living/brain/fully_replace_character_name(oldname,newname) ..() if(stored_dna) - stored_dna.real_name = real_name \ No newline at end of file + stored_dna.real_name = real_name diff --git a/code/modules/mob/living/brain/posibrain.dm b/code/modules/mob/living/brain/posibrain.dm index 483f9076390..6708f7c11e0 100644 --- a/code/modules/mob/living/brain/posibrain.dm +++ b/code/modules/mob/living/brain/posibrain.dm @@ -25,7 +25,7 @@ var/global/posibrain_notif_cooldown = 0 Remember, the purpose of your existence is to serve the crew and the station. Above all else, do no harm." var/new_mob_message = "The positronic brain chimes quietly." var/dead_message = "It appears to be completely inactive. The reset light is blinking." - var/list/fluff_names = list("PBU","HIU","SINA","ARMA","OSI","HBL","MSO","RR","CHRI","CDB","HG","XSI","ORNG","GUN","KOR","MET","FRE","XIS","SLI","PKP","HOG","RZH","GOOF","MRPR","JJR","FIRC","INC","PHL","BGB","ANTR","MIW","WJ","JRD","CHOC","ANCL","JLLO","JNLG","KOS","TKRG","XAL","STLP","CBOS","DUNC","FXMC","DRSD") + var/list/fluff_names var/picked_fluff_name //which fluff name we picked @@ -135,7 +135,10 @@ var/global/posibrain_notif_cooldown = 0 /obj/item/device/mmi/posibrain/New() brainmob = new(src) - picked_fluff_name = pick(fluff_names) + if(!fluff_names || !fluff_names.len) + picked_fluff_name = pick(posibrain_names) + else + picked_fluff_name = pick(fluff_names) brainmob.name = "[picked_fluff_name]-[rand(100, 999)]" brainmob.real_name = brainmob.name brainmob.loc = src diff --git a/code/modules/mob/living/brain/say.dm b/code/modules/mob/living/brain/say.dm index 632716e7776..5751c65b364 100644 --- a/code/modules/mob/living/brain/say.dm +++ b/code/modules/mob/living/brain/say.dm @@ -1,4 +1,4 @@ -/mob/living/brain/say(message) +/mob/living/brain/say(message, language) if(!(container && istype(container, /obj/item/device/mmi))) return //No MMI, can't speak, bucko./N else @@ -7,16 +7,17 @@ return else message = Gibberish(message, (emp_damage*6))//scrambles the message, gets worse when emp_damage is higher + ..() /mob/living/brain/get_spans() return ..() | SPAN_ROBOT -/mob/living/brain/radio(message, message_mode, list/spans) +/mob/living/brain/radio(message, message_mode, list/spans, language) if(message_mode && istype(container, /obj/item/device/mmi)) var/obj/item/device/mmi/R = container if(R.radio) - R.radio.talk_into(src, message, , spans) + R.radio.talk_into(src, message, , get_spans(), language) return ITALICS | REDUCE_RANGE /mob/living/brain/lingcheck() @@ -24,4 +25,12 @@ /mob/living/brain/treat_message(message) message = capitalize(message) - return message \ No newline at end of file + return message + +/mob/living/brain/can_speak_in_language(datum/language/dt) + if(HAS_SECONDARY_FLAG(src, OMNITONGUE)) + . = has_language(dt) + else if(istype(container, /obj/item/device/mmi/posibrain/soul_vessel)) + . = has_language(dt) && ispath(dt, /datum/language/ratvar) + else + . = ..() diff --git a/code/modules/mob/living/carbon/alien/alien.dm b/code/modules/mob/living/carbon/alien/alien.dm index 3dd354050f9..43a909df807 100644 --- a/code/modules/mob/living/carbon/alien/alien.dm +++ b/code/modules/mob/living/carbon/alien/alien.dm @@ -11,11 +11,10 @@ dna = null faction = list("alien") ventcrawler = VENTCRAWLER_ALWAYS - languages_spoken = ALIEN - languages_understood = ALIEN sight = SEE_MOBS see_in_dark = 4 verb_say = "hisses" + initial_languages = list(/datum/language/xenocommon) bubble_icon = "alien" type_of_meat = /obj/item/weapon/reagent_containers/food/snacks/meat/slab/xeno var/nightvision = 1 diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index db366bfec91..d41f949e22f 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -42,6 +42,8 @@ handcrafting = new() + grant_language(/datum/language/common) // ME TARZAN, YOU JANEBOT + ..() /mob/living/carbon/human/create_internal_organs() diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index 7fe75dcd96c..1f9beb52bf7 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -1,7 +1,6 @@ var/global/default_martial_art = new/datum/martial_art /mob/living/carbon/human - languages_spoken = HUMAN - languages_understood = HUMAN + initial_languages = list(/datum/language/common) hud_possible = list(HEALTH_HUD,STATUS_HUD,ID_HUD,WANTED_HUD,IMPLOYAL_HUD,IMPCHEM_HUD,IMPTRACK_HUD,ANTAG_HUD) possible_a_intents = list(INTENT_HELP, INTENT_DISARM, INTENT_GRAB, INTENT_HARM) pressure_resistance = 25 diff --git a/code/modules/mob/living/carbon/human/say.dm b/code/modules/mob/living/carbon/human/say.dm index 45120f74fb0..4d5d1c26eb5 100644 --- a/code/modules/mob/living/carbon/human/say.dm +++ b/code/modules/mob/living/carbon/human/say.dm @@ -75,7 +75,7 @@ if(!istype(dongle)) return 0 if(dongle.translate_binary) return 1 -/mob/living/carbon/human/radio(message, message_mode, list/spans) +/mob/living/carbon/human/radio(message, message_mode, list/spans, language) . = ..() if(. != 0) return . @@ -83,17 +83,17 @@ switch(message_mode) if(MODE_HEADSET) if (ears) - ears.talk_into(src, message, , spans) + ears.talk_into(src, message, , spans, language) return ITALICS | REDUCE_RANGE if(MODE_DEPARTMENT) if (ears) - ears.talk_into(src, message, message_mode, spans) + ears.talk_into(src, message, message_mode, spans, language) return ITALICS | REDUCE_RANGE if(message_mode in radiochannels) if(ears) - ears.talk_into(src, message, message_mode, spans) + ears.talk_into(src, message, message_mode, spans, language) return ITALICS | REDUCE_RANGE return 0 diff --git a/code/modules/mob/living/carbon/human/whisper.dm b/code/modules/mob/living/carbon/human/whisper.dm index 115a985710d..f392981ed4e 100644 --- a/code/modules/mob/living/carbon/human/whisper.dm +++ b/code/modules/mob/living/carbon/human/whisper.dm @@ -1,8 +1,13 @@ -/mob/living/carbon/human/whisper(message as text) +/mob/living/carbon/human/whisper_verb(message as text) + whisper(message) + +/mob/living/carbon/human/whisper(message, datum/language/language=null) if(!IsVocal()) return if(!message) return + if(!language) + language = get_default_language() if(say_disabled) //This is here to try to identify lag problems to_chat(usr, "Speech is currently admin-disabled.") @@ -73,14 +78,14 @@ for(var/atom/movable/AM in listening) if(istype(AM,/obj/item/device/radio)) continue - AM.Hear(rendered, src, languages_spoken, message, , spans) + AM.Hear(rendered, src, language, message, , spans) message = stars(message) rendered = "[GetVoice()][alt_name] [whispers], \"[attach_spans(message, spans)]\"" for(var/atom/movable/AM in eavesdropping) if(istype(AM,/obj/item/device/radio)) continue - AM.Hear(rendered, src, languages_spoken, message, , spans) + AM.Hear(rendered, src, language, message, , spans) if(critical) //Dying words. succumb(1) diff --git a/code/modules/mob/living/carbon/monkey/monkey.dm b/code/modules/mob/living/carbon/monkey/monkey.dm index 156ac27da66..920a0cade3b 100644 --- a/code/modules/mob/living/carbon/monkey/monkey.dm +++ b/code/modules/mob/living/carbon/monkey/monkey.dm @@ -2,12 +2,11 @@ name = "monkey" voice_name = "monkey" verb_say = "chimpers" + initial_languages = list(/datum/language/monkey) icon = 'icons/mob/monkey.dmi' icon_state = "" gender = NEUTER pass_flags = PASSTABLE - languages_spoken = MONKEY - languages_understood = MONKEY ventcrawler = VENTCRAWLER_NUDE butcher_results = list(/obj/item/weapon/reagent_containers/food/snacks/meat/slab/monkey = 5, /obj/item/stack/sheet/animalhide/monkey = 1) type_of_meat = /obj/item/weapon/reagent_containers/food/snacks/meat/slab/monkey @@ -159,4 +158,4 @@ if(prob(10)) var/obj/item/clothing/head/helmet/justice/escape/helmet = new(src) equip_to_slot_or_del(helmet,slot_head) - helmet.attack_self(src) // todo encapsulate toggle \ No newline at end of file + helmet.attack_self(src) // todo encapsulate toggle diff --git a/code/modules/mob/living/carbon/say.dm b/code/modules/mob/living/carbon/say.dm index 8b1e3b65298..039b40b2341 100644 --- a/code/modules/mob/living/carbon/say.dm +++ b/code/modules/mob/living/carbon/say.dm @@ -28,3 +28,15 @@ var/obj/item/I = get_active_held_item() if(I) . |= I.get_held_item_speechspans(src) + +/mob/living/carbon/can_speak_in_language(datum/language/dt) + if(HAS_SECONDARY_FLAG(src, OMNITONGUE)) + . = has_language(dt) + else if(has_language(dt)) + var/obj/item/organ/tongue/T = getorganslot("tongue") + if(T) + . = T.can_speak_in_language(dt) + else + . = initial(dt.flags) & TONGUELESS_SPEECH + else + . = FALSE diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index abad6b4084e..f808eb65ef1 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -17,6 +17,8 @@ medhud.add_to_hud(src) faction += "\ref[src]" + language_menu = new(src) + /mob/living/prepare_huds() ..() @@ -41,6 +43,8 @@ staticOverlays.len = 0 remove_from_all_data_huds() + QDEL_NULL(language_menu) + return ..() /mob/living/ghostize(can_reenter_corpse = 1) diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index 3caabec5ff9..3aa604649da 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -1,7 +1,5 @@ /mob/living see_invisible = SEE_INVISIBLE_LIVING - languages_spoken = HUMAN - languages_understood = HUMAN sight = 0 see_in_dark = 2 hud_possible = list(HEALTH_HUD,STATUS_HUD,ANTAG_HUD) @@ -74,4 +72,7 @@ var/list/implants = null var/tesla_ignore = FALSE - var/datum/riding/riding_datum \ No newline at end of file + var/datum/riding/riding_datum + + var/datum/language/selected_default_language + var/datum/language_menu/language_menu diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index 35183bbe4a2..c6ff1ca9a03 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -64,7 +64,7 @@ var/list/department_radio_keys = list( var/list/crit_allowed_modes = list(MODE_WHISPER,MODE_CHANGELING,MODE_ALIEN) -/mob/living/say(message, bubble_type,var/list/spans = list(), sanitize = TRUE) +/mob/living/say(message, bubble_type,var/list/spans = list(), sanitize = TRUE, datum/language/language = null) if(sanitize) message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) if(!message || message == "") @@ -103,7 +103,26 @@ var/list/crit_allowed_modes = list(MODE_WHISPER,MODE_CHANGELING,MODE_ALIEN) if(stat && !(message_mode in crit_allowed_modes)) return - if(handle_inherent_channels(message, message_mode)) //Hiveminds, binary chat & holopad. + // language comma detection. + var/datum/language/message_language = get_message_language(message) + if(message_language) + // No, you cannot speak in xenocommon just because you know the key + if(can_speak_in_language(message_language)) + language = message_language + message = copytext(message, 3) + + // Trim the space if they said ",0 I LOVE LANGUAGES" + if(findtext(message, " ", 1, 2)) + message = copytext(message, 2) + + if(!language) + language = get_default_language() + + // Detection of language needs to be before inherent channels, because + // AIs use inherent channels for the holopad. Most inherent channels + // ignore the language argument however. + + if(handle_inherent_channels(message, message_mode, language)) //Hiveminds, binary chat & holopad. return if(!can_speak_vocal(message)) @@ -117,11 +136,19 @@ var/list/crit_allowed_modes = list(MODE_WHISPER,MODE_CHANGELING,MODE_ALIEN) spans += get_spans() + if(language) + var/datum/language/L = language_datums[language] + if(!istype(L)) + L = new language + language_datums[language] = L + + spans |= L.spans + //Log what we've said with an associated timestamp, using the list's len for safety/to prevent overwriting messages log_message(message, INDIVIDUAL_SAY_LOG) var/message_range = 7 - var/radio_return = radio(message, message_mode, spans) + var/radio_return = radio(message, message_mode, spans, language) if(radio_return & NOPASS) //There's a whisper() message_mode, no need to continue the proc if that is called return if(radio_return & ITALICS) @@ -139,12 +166,12 @@ var/list/crit_allowed_modes = list(MODE_WHISPER,MODE_CHANGELING,MODE_ALIEN) if(pressure < ONE_ATMOSPHERE*0.4) //Thin air, let's italicise the message spans |= SPAN_ITALICS - send_speech(message, message_range, src, bubble_type, spans) + send_speech(message, message_range, src, bubble_type, spans, message_language=language) log_say("[name]/[key] : [message]") return 1 -/mob/living/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans) +/mob/living/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans) if(!client) return var/deaf_message @@ -156,20 +183,21 @@ var/list/crit_allowed_modes = list(MODE_WHISPER,MODE_CHANGELING,MODE_ALIEN) else deaf_message = "You can't hear yourself!" deaf_type = 2 // Since you should be able to hear yourself without looking - if(!(message_langs & languages_understood) || force_compose) //force_compose is so AIs don't end up without their hrefs. - message = compose_message(speaker, message_langs, raw_message, radio_freq, spans) + + // Recompose message for AI hrefs, language incomprehension. + message = compose_message(speaker, message_language, raw_message, radio_freq, spans) show_message(message, 2, deaf_message, deaf_type) return message -/mob/living/send_speech(message, message_range = 7, obj/source = src, bubble_type = bubble_icon, list/spans) +/mob/living/send_speech(message, message_range = 7, obj/source = src, bubble_type = bubble_icon, list/spans, datum/language/message_language=null) var/list/listening = get_hearers_in_view(message_range, source) for(var/mob/M in player_list) if(M.stat == DEAD && M.client && ((M.client.prefs.chat_toggles & CHAT_GHOSTEARS) || (get_dist(M, src) <= 7 && M.z == z)) && client) // client is so that ghosts don't have to listen to mice listening |= M - var/rendered = compose_message(src, languages_spoken, message, , spans) + var/rendered = compose_message(src, message_language, message, , spans) for(var/atom/movable/AM in listening) - AM.Hear(rendered, src, languages_spoken, message, , spans) + AM.Hear(rendered, src, message_language, message, , spans, message_language) //speech bubble var/list/speech_bubble_recipients = list() @@ -178,8 +206,7 @@ var/list/crit_allowed_modes = list(MODE_WHISPER,MODE_CHANGELING,MODE_ALIEN) speech_bubble_recipients.Add(M.client) var/image/I = image('icons/mob/talk.dmi', src, "[bubble_type][say_test(message)]", FLY_LAYER) I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA - spawn(0) - flick_overlay(I, speech_bubble_recipients, 30) + INVOKE_ASYNC(GLOBAL_PROC, /.proc/flick_overlay, I, speech_bubble_recipients, 30) /mob/proc/binarycheck() return 0 @@ -221,6 +248,19 @@ var/list/crit_allowed_modes = list(MODE_WHISPER,MODE_CHANGELING,MODE_ALIEN) else if(length(message) > 2) return department_radio_keys[copytext(message, 1, 3)] +/mob/living/proc/get_message_language(message) + var/static/list/langlist + if(!langlist) + langlist = subtypesof(/datum/language) + + if(copytext(message, 1, 2) == ",") + var/key = copytext(message, 2, 3) + for(var/ld in langlist) + var/datum/language/LD = ld + if(initial(LD.key) == key) + return LD + return null + /mob/living/proc/handle_inherent_channels(message, message_mode) if(message_mode == MODE_CHANGELING) switch(lingcheck()) @@ -289,22 +329,22 @@ var/list/crit_allowed_modes = list(MODE_WHISPER,MODE_CHANGELING,MODE_ALIEN) return message -/mob/living/proc/radio(message, message_mode, list/spans) +/mob/living/proc/radio(message, message_mode, list/spans, language) switch(message_mode) if(MODE_R_HAND) for(var/obj/item/r_hand in get_held_items_for_side("r", all = TRUE)) if (r_hand) - return r_hand.talk_into(src, message, , spans) + return r_hand.talk_into(src, message, , spans, language) return ITALICS | REDUCE_RANGE if(MODE_L_HAND) for(var/obj/item/l_hand in get_held_items_for_side("l", all = TRUE)) if (l_hand) - return l_hand.talk_into(src, message, , spans) + return l_hand.talk_into(src, message, , spans, language) return ITALICS | REDUCE_RANGE if(MODE_INTERCOM) for (var/obj/item/device/radio/intercom/I in view(1, null)) - I.talk_into(src, message, , spans) + I.talk_into(src, message, , spans, language) return ITALICS | REDUCE_RANGE if(MODE_BINARY) @@ -333,3 +373,15 @@ var/list/crit_allowed_modes = list(MODE_WHISPER,MODE_CHANGELING,MODE_ALIEN) if (getBrainLoss() >= 60) return "gibbers, \"[tempinput]\"" return ..() + +/mob/living/get_default_language() + if(selected_default_language) + if(has_language(selected_default_language)) + return selected_default_language + else + selected_default_language = null + + . = ..() + +/mob/living/proc/open_language_menu(mob/user) + language_menu.ui_interact(user) diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 41a097b6e02..4e97c5babd5 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -22,7 +22,6 @@ var/list/ai_list = list() canmove = 0 status_flags = CANSTUN|CANPUSH a_intent = INTENT_HARM //so we always get pushed instead of trying to swap - force_compose = 1 //This ensures that the AI always composes it's own hear message. Needed for hrefs and job display. sight = SEE_TURFS | SEE_MOBS | SEE_OBJS see_in_dark = 8 med_hud = DATA_HUD_MEDICAL_BASIC @@ -831,8 +830,8 @@ var/list/ai_list = list() return return 1 -/mob/living/silicon/ai/proc/relay_speech(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans) - raw_message = lang_treat(speaker, message_langs, raw_message, spans) +/mob/living/silicon/ai/proc/relay_speech(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans) + raw_message = lang_treat(speaker, message_language, raw_message, spans) var/name_used = speaker.GetVoice() var/rendered = "Relayed Speech: [name_used] [raw_message]" show_message(rendered, 2) @@ -985,4 +984,4 @@ var/list/ai_list = list() /mob/living/silicon/ai/spawned/Initialize(mapload, datum/ai_laws/L, mob/target_ai) if(!target_ai) target_ai = src //cheat! just give... ourselves as the spawned AI, because that's technically correct - ..() \ No newline at end of file + ..() diff --git a/code/modules/mob/living/silicon/ai/freelook/eye.dm b/code/modules/mob/living/silicon/ai/freelook/eye.dm index 35bec8d6698..75d87d73231 100644 --- a/code/modules/mob/living/silicon/ai/freelook/eye.dm +++ b/code/modules/mob/living/silicon/ai/freelook/eye.dm @@ -104,6 +104,6 @@ acceleration = !acceleration to_chat(usr, "Camera acceleration has been toggled [acceleration ? "on" : "off"].") -/mob/camera/aiEye/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans) +/mob/camera/aiEye/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans) if(relay_speech && speaker && ai && !radio_freq && speaker != ai && near_camera(speaker)) - ai.relay_speech(message, speaker, message_langs, raw_message, radio_freq, spans) + ai.relay_speech(message, speaker, message_language, raw_message, radio_freq, spans) diff --git a/code/modules/mob/living/silicon/ai/say.dm b/code/modules/mob/living/silicon/ai/say.dm index 599eb71e5bb..60b8e3040ba 100644 --- a/code/modules/mob/living/silicon/ai/say.dm +++ b/code/modules/mob/living/silicon/ai/say.dm @@ -1,6 +1,6 @@ -/mob/living/silicon/ai/say(message) +/mob/living/silicon/ai/say(message, language) if(parent && istype(parent) && parent.stat != 2) //If there is a defined "parent" AI, it is actually an AI, and it is alive, anything the AI tries to say is said by the parent instead. - parent.say(message) + parent.say(message, language) return ..(message) @@ -17,7 +17,7 @@ /mob/living/silicon/ai/IsVocal() return !config.silent_ai -/mob/living/silicon/ai/radio(message, message_mode, list/spans) +/mob/living/silicon/ai/radio(message, message_mode, list/spans, language) if(!radio_enabled || aiRestorePowerRoutine || stat) //AI cannot speak if radio is disabled (via intellicard) or depowered. to_chat(src, "Your radio transmitter is offline!") return 0 @@ -29,17 +29,17 @@ else return ..() -/mob/living/silicon/ai/handle_inherent_channels(message, message_mode) +/mob/living/silicon/ai/handle_inherent_channels(message, message_mode, language) . = ..() if(.) return . if(message_mode == MODE_HOLOPAD) - holopad_talk(message) + holopad_talk(message, language) return 1 //For holopads only. Usable by AI. -/mob/living/silicon/ai/proc/holopad_talk(message) +/mob/living/silicon/ai/proc/holopad_talk(message, language) log_say("[key_name(src)] : [message]") message = trim(message) @@ -49,11 +49,10 @@ var/obj/machinery/holopad/T = current if(istype(T) && T.masters[src])//If there is a hologram and its master is the user. - send_speech(message, 7, T, "robot", get_spans()) + send_speech(message, 7, T, "robot", get_spans(), message_language=language) to_chat(src, "Holopad transmitted, [real_name] \"[message]\"") else to_chat(src, "No holopad connected.") - return // Make sure that the code compiles with AI_VOX undefined @@ -167,3 +166,12 @@ var/const/VOX_DELAY = 600 return 0 #endif + +/mob/living/silicon/ai/can_speak_in_language(datum/language/dt) + if(HAS_SECONDARY_FLAG(src, OMNITONGUE)) + . = has_language(dt) + else if(is_servant_of_ratvar(src)) + // Ratvarian AIs can only speak Ratvarian + . = ispath(dt, /datum/language/ratvar) && has_language(dt) + else + . = ..() diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index 59d901cb88b..b02b51e9696 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -110,10 +110,13 @@ var/datum/action/innate/pai/chassis/AC = new /datum/action/innate/pai/chassis var/datum/action/innate/pai/rest/AR = new /datum/action/innate/pai/rest var/datum/action/innate/pai/light/AL = new /datum/action/innate/pai/light + + var/datum/action/language_menu/ALM = new AS.Grant(src) AC.Grant(src) AR.Grant(src) AL.Grant(src) + ALM.Grant(src) emittersemicd = TRUE addtimer(CALLBACK(src, .proc/emittercool), 600) diff --git a/code/modules/mob/living/silicon/pai/pai_shell.dm b/code/modules/mob/living/silicon/pai/pai_shell.dm index 6f6e449522f..5c50a87b454 100644 --- a/code/modules/mob/living/silicon/pai/pai_shell.dm +++ b/code/modules/mob/living/silicon/pai/pai_shell.dm @@ -102,4 +102,4 @@ /mob/living/silicon/pai/movement_delay() . = ..() - . += 1 //A bit slower than humans, so they're easier to smash \ No newline at end of file + . += 1 //A bit slower than humans, so they're easier to smash diff --git a/code/modules/mob/living/silicon/pai/software.dm b/code/modules/mob/living/silicon/pai/software.dm index 5ea2dc2491e..2d8e36e7a61 100644 --- a/code/modules/mob/living/silicon/pai/software.dm +++ b/code/modules/mob/living/silicon/pai/software.dm @@ -253,9 +253,9 @@ med.remove_hud_from(src) if("translator") if(href_list["toggle"]) - var/on_already = ((languages_understood == ALL) && (languages_spoken == ALL)) - languages_spoken = on_already ? (HUMAN | ROBOT) : ALL - languages_understood = on_already ? (HUMAN | ROBOT) : ALL + if(!HAS_SECONDARY_FLAG(src, OMNITONGUE)) + grant_all_languages(TRUE) + // this is PERMAMENT. if("doorjack") if(href_list["jack"]) if(src.cable && src.cable.machine) @@ -313,7 +313,8 @@ if(s == "medical HUD") dat += "Medical Analysis Suite[(src.medHUD) ? " On" : " Off"]
" if(s == "universal translator") - dat += "Universal Translator[((languages_spoken == ALL) && (languages_understood == ALL)) ? " On" : " Off"]
" + var/translator_on = HAS_SECONDARY_FLAG(src, OMNITONGUE) + dat += "Universal Translator[translator_on ? " On" : " Off"]
" if(s == "projection array") dat += "Projection Array
" if(s == "camera jack") @@ -464,11 +465,10 @@ // Universal Translator /mob/living/silicon/pai/proc/softwareTranslator() + var/translator_on = HAS_SECONDARY_FLAG(src, OMNITONGUE) . = {"

Universal Translator


- When enabled, this device will automatically convert all spoken and written language into a format that any known recipient can understand.

- The device is currently [ ((languages_spoken == ALL) && (languages_understood == ALL)) ? "en" : "dis" ]abled.
- Toggle Device
- "} + When enabled, this device will permamently be able to speak and understand all known forms of communication.

+ The device is currently [translator_on ? "en" : "dis" ]abled.
[translator_on ? "" : "Activate Translation Module
"]"} return . // Security HUD diff --git a/code/modules/mob/living/silicon/say.dm b/code/modules/mob/living/silicon/say.dm index 7b57090c1d1..d7633f4107c 100644 --- a/code/modules/mob/living/silicon/say.dm +++ b/code/modules/mob/living/silicon/say.dm @@ -33,19 +33,19 @@ /mob/living/silicon/lingcheck() return 0 //Borged or AI'd lings can't speak on the ling channel. -/mob/living/silicon/radio(message, message_mode, list/spans) +/mob/living/silicon/radio(message, message_mode, list/spans, language) . = ..() if(. != 0) return . if(message_mode == "robot") if (radio) - radio.talk_into(src, message, , spans) + radio.talk_into(src, message, , spans, language) return REDUCE_RANGE else if(message_mode in radiochannels) if(radio) - radio.talk_into(src, message, message_mode, spans) + radio.talk_into(src, message, message_mode, spans, language) return ITALICS | REDUCE_RANGE return 0 diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index de6bb7c827e..bc6d1d7fe04 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -1,13 +1,12 @@ /mob/living/silicon gender = NEUTER voice_name = "synthesized voice" - languages_spoken = ROBOT | HUMAN - languages_understood = ROBOT | HUMAN has_unlimited_silicon_privilege = 1 verb_say = "states" verb_ask = "queries" verb_exclaim = "declares" verb_yell = "alarms" + initial_languages = list(/datum/language/common, /datum/language/machine) see_in_dark = 8 bubble_icon = "machine" weather_immunities = list("ash") diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm index 9f746c0f5b5..e41d7f07e60 100644 --- a/code/modules/mob/living/simple_animal/bot/bot.dm +++ b/code/modules/mob/living/simple_animal/bot/bot.dm @@ -20,6 +20,7 @@ verb_ask = "queries" verb_exclaim = "declares" verb_yell = "alarms" + initial_languages = list(/datum/language/common, /datum/language/machine) bubble_icon = "machine" faction = list("neutral", "silicon" , "turret") @@ -140,6 +141,9 @@ //Gives a HUD view to player bots that use a HUD. activate_data_hud() + grant_language(/datum/language/common) + grant_language(/datum/language/machine) + /mob/living/simple_animal/bot/update_canmove() . = ..() @@ -323,30 +327,29 @@ if((!on) || (!message)) return if(channel && Radio.channels[channel])// Use radio if we have channel key - Radio.talk_into(src, message, channel, get_spans()) + Radio.talk_into(src, message, channel, get_spans(), get_default_language()) else say(message) - return /mob/living/simple_animal/bot/get_spans() return ..() | SPAN_ROBOT -/mob/living/simple_animal/bot/radio(message, message_mode, list/spans) +/mob/living/simple_animal/bot/radio(message, message_mode, list/spans, language) . = ..() if(. != 0) return . switch(message_mode) if(MODE_HEADSET) - Radio.talk_into(src, message, , spans) + Radio.talk_into(src, message, , spans, language) return REDUCE_RANGE if(MODE_DEPARTMENT) - Radio.talk_into(src, message, message_mode, spans) + Radio.talk_into(src, message, message_mode, spans, language) return REDUCE_RANGE if(message_mode in radiochannels) - Radio.talk_into(src, message, message_mode, spans) + Radio.talk_into(src, message, message_mode, spans, language) return REDUCE_RANGE return 0 diff --git a/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm b/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm index 9d8631e972a..74c1f962134 100644 --- a/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm +++ b/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm @@ -37,8 +37,8 @@ voice_name = "synthesized chirp" speak_emote = list("chirps") bubble_icon = "machine" - languages_spoken = DRONE - languages_understood = DRONE|HUMAN + initial_languages = list(/datum/language/common, /datum/language/machine, /datum/language/drone) + only_speaks_language = /datum/language/drone mob_size = MOB_SIZE_SMALL has_unlimited_silicon_privilege = 1 damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0) diff --git a/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm b/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm index abd9a7ec954..837258b5738 100644 --- a/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm +++ b/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm @@ -97,8 +97,6 @@ icon_living = "drone_clock" icon_dead = "drone_clock_dead" picked = TRUE - languages_spoken = RATVAR - languages_understood = HUMAN|RATVAR pass_flags = PASSTABLE health = 50 maxHealth = 50 @@ -112,6 +110,8 @@ verb_exclaim = "proclaims" verb_yell = "harangues" bubble_icon = "clock" + initial_languages = list(/datum/language/common, /datum/language/ratvar) + only_speaks_language = /datum/language/ratvar light_color = "#E42742" heavy_emp_damage = 0 laws = "0. Purge all untruths and honor Ratvar." @@ -139,6 +139,8 @@ verbs -= /mob/living/simple_animal/drone/verb/toggle_light verbs -= /mob/living/simple_animal/drone/verb/drone_ping + grant_language(/datum/language/ratvar) + /mob/living/simple_animal/drone/cogscarab/Login() ..() add_servant_of_ratvar(src, TRUE) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm index 5a9c719d234..6fdb8f694ab 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm @@ -369,8 +369,6 @@ Difficulty: Very Hard resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF use_power = 0 density = 1 - languages_spoken = ALL - languages_understood = ALL flags = HEAR var/activation_method = "touch" var/activation_damage_type = null @@ -618,8 +616,6 @@ Difficulty: Very Hard damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0) light_range = 4 faction = list("neutral") - languages_spoken = SLIME - languages_understood = ALL del_on_death = 1 unsuitable_atmos_damage = 0 movement_type = FLYING diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index 2ccd7a08265..7468c8e21c0 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -154,7 +154,7 @@ return message ..() -/mob/living/simple_animal/parrot/radio(message, message_mode, list/spans) //literally copied from human/radio(), but there's no other way to do this. at least it's better than it used to be. +/mob/living/simple_animal/parrot/radio(message, message_mode, list/spans, language) //literally copied from human/radio(), but there's no other way to do this. at least it's better than it used to be. . = ..() if(. != 0) return . @@ -162,17 +162,17 @@ switch(message_mode) if(MODE_HEADSET) if (ears) - ears.talk_into(src, message, , spans) + ears.talk_into(src, message, , spans, language) return ITALICS | REDUCE_RANGE if(MODE_DEPARTMENT) if (ears) - ears.talk_into(src, message, message_mode, spans) + ears.talk_into(src, message, message_mode, spans, language) return ITALICS | REDUCE_RANGE if(message_mode in radiochannels) if(ears) - ears.talk_into(src, message, message_mode, spans) + ears.talk_into(src, message, message_mode, spans, language) return ITALICS | REDUCE_RANGE return 0 @@ -894,6 +894,7 @@ desc += " Over [rounds_survived] shifts without a \"terrible\" \"accident\"!" else speak += pick("...alive?", "This isn't parrot heaven!", "I live, I die, I live again!", "The void fades!") + ..() /mob/living/simple_animal/parrot/Poly/Life() diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index afaf7718e0d..3c45c5a8bda 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -95,6 +95,9 @@ if(!loc) stack_trace("Simple animal being instantiated in nullspace") + // goats bray, cows go moo, and the fox says Geckers + grant_language(/datum/language/common) + /mob/living/simple_animal/Login() if(src && src.client) src.client.screen = list() diff --git a/code/modules/mob/living/simple_animal/slime/powers.dm b/code/modules/mob/living/simple_animal/slime/powers.dm index 3585d989dc5..aec971fb37f 100644 --- a/code/modules/mob/living/simple_animal/slime/powers.dm +++ b/code/modules/mob/living/simple_animal/slime/powers.dm @@ -170,8 +170,6 @@ var/mob/living/simple_animal/slime/new_slime = pick(babies) new_slime.a_intent = INTENT_HARM - new_slime.languages_spoken = languages_spoken - new_slime.languages_understood = languages_understood if(src.mind) src.mind.transfer_to(new_slime) else diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm index 62600cc512f..b3e664c89d9 100644 --- a/code/modules/mob/living/simple_animal/slime/slime.dm +++ b/code/modules/mob/living/simple_animal/slime/slime.dm @@ -13,8 +13,6 @@ var/list/slime_colours = list("rainbow", "grey", "purple", "metal", "orange", gender = NEUTER var/is_adult = 0 var/docile = 0 - languages_spoken = SLIME | HUMAN - languages_understood = SLIME | HUMAN faction = list("slime","neutral") harm_intent_damage = 5 @@ -94,6 +92,7 @@ var/list/slime_colours = list("rainbow", "grey", "purple", "metal", "orange", E.Grant(src) create_reagents(100) set_colour(new_colour) + grant_language(/datum/language/slime) ..() /mob/living/simple_animal/slime/proc/set_colour(new_colour) @@ -424,4 +423,4 @@ var/list/slime_colours = list("rainbow", "grey", "purple", "metal", "orange", return 3 /mob/living/simple_animal/slime/random/Initialize(mapload, new_colour, new_is_adult) - . = ..(mapload, pick(slime_colours), prob(50)) \ No newline at end of file + . = ..(mapload, pick(slime_colours), prob(50)) diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 25992568a2c..ef416c938fc 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -131,8 +131,6 @@ var/has_unlimited_silicon_privilege = 0 // Can they interact with station electronics - var/force_compose = 0 //If this is nonzero, the mob will always compose it's own hear message instead of using the one given in the arguments. - var/obj/control_object //Used by admins to possess objects. All mobs should have this var var/atom/movable/remote_control //Calls relaymove() to whatever it is diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 8eb9382aaf0..332f10d7757 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -464,17 +464,6 @@ var/static/regex/firstname = new("^\[^\\s-\]+") //First word before whitespace o message_admins("No ghosts were willing to take control of [key_name_admin(M)])") return FALSE -//toggles the talk wheel -/mob/verb/toggle_talk_wheel() - set name = "talk-wheel" - set hidden = 1 - - if(isliving(src)) - var/mob/living/L = src - if(L.hud_used) - for(var/obj/screen/wheel/talk/TW in L.hud_used.wheels) - TW.Click() - /mob/proc/is_flying(mob/M = src) if(M.movement_type & FLYING) return 1 @@ -499,4 +488,4 @@ var/static/regex/firstname = new("^\[^\\s-\]+") //First word before whitespace o var/list/timestamped_message = list("[LAZYLEN(logging[message_type]) + 1]\[[time_stamp()]\] [key_name(src)]" = message) - logging[message_type] += timestamped_message \ No newline at end of file + logging[message_type] += timestamped_message diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index 85e6e65f999..c6449c7b5a3 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -7,13 +7,17 @@ return usr.say(message) -/mob/verb/whisper(message as text) + +/mob/verb/whisper_verb(message as text) set name = "Whisper" set category = "IC" if(say_disabled) //This is here to try to identify lag problems to_chat(usr, "Speech is currently admin-disabled.") return - say(message) //only carbons actually whisper, everything else just talks + whisper(message) + +/mob/proc/whisper(message, datum/language/language=null) + say(message, language) //only carbons actually whisper, everything else just talks /mob/verb/me_verb(message as text) set name = "Me" diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm index 451977772f5..e50ef28fa6f 100644 --- a/code/modules/power/supermatter/supermatter.dm +++ b/code/modules/power/supermatter/supermatter.dm @@ -43,10 +43,14 @@ #define HALLUCINATION_RANGE(P) (min(7, round(P ** 0.25))) + #define GRAVITATIONAL_ANOMALY "gravitational_anomaly" #define FLUX_ANOMALY "flux_anomaly" #define PYRO_ANOMALY "pyro_anomaly" +#define SPEAK(message) radio.talk_into(src, message, null, get_spans(), get_default_language()) + + /obj/machinery/power/supermatter_shard name = "supermatter shard" desc = "A strangely translucent and iridescent crystal that looks like it used to be part of a larger structure." @@ -333,28 +337,27 @@ var/stability = num2text(round((damage / explosion_point) * 100)) if(damage > emergency_point) - - radio.talk_into(src, "[emergency_alert] Instability: [stability]%") + SPEAK("[emergency_alert] Instability: [stability]%") lastwarning = REALTIMEOFDAY if(!has_reached_emergency) investigate_log("has reached the emergency point for the first time.", "supermatter") message_admins("[src] has reached the emergency point (JMP).") has_reached_emergency = 1 else if(damage >= damage_archived) // The damage is still going up - radio.talk_into(src, "[warning_alert] Instability: [stability]%") + SPEAK("[warning_alert] Instability: [stability]%") lastwarning = REALTIMEOFDAY - (WARNING_DELAY * 5) else // Phew, we're safe - radio.talk_into(src, "[safe_alert] Instability: [stability]%") + SPEAK("[safe_alert] Instability: [stability]%") lastwarning = REALTIMEOFDAY if(power > POWER_PENALTY_THRESHOLD) - radio.talk_into(src, "Warning: Hyperstructure has reached dangerous power level.") + SPEAK("Warning: Hyperstructure has reached dangerous power level.") if(powerloss_inhibitor < 0.5) - radio.talk_into(src, "DANGER: CHARGE INERTIA CHAIN REACTION IN PROGRESS.") + SPEAK("DANGER: CHARGE INERTIA CHAIN REACTION IN PROGRESS.") if(combined_gas > MOLE_PENALTY_THRESHOLD) - radio.talk_into(src, "Warning: Critical coolant mass reached.") + SPEAK("Warning: Critical coolant mass reached.") if(damage > explosion_point) for(var/mob in living_mob_list) diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm index 9649e13029d..fbba77e9818 100644 --- a/code/modules/projectiles/projectile/magic.dm +++ b/code/modules/projectiles/projectile/magic.dm @@ -261,9 +261,8 @@ if(!new_mob) return - - new_mob.languages_spoken |= HUMAN - new_mob.languages_understood |= HUMAN + new_mob.grant_language(/datum/language/common) + SET_SECONDARY_FLAG(new_mob, OMNITONGUE) new_mob.logging = M.logging // Some forms can still wear some items diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm index 0e3daed388d..ac33e8c9132 100644 --- a/code/modules/research/xenobiology/xenobiology.dm +++ b/code/modules/research/xenobiology/xenobiology.dm @@ -190,8 +190,6 @@ if(candidates.len) theghost = pick(candidates) SM.key = theghost.key - SM.languages_spoken |= HUMAN - SM.languages_understood |= HUMAN SM.mind.enslave_mind_to_creator(user) SM.sentience_act() to_chat(SM, "All at once it makes sense: you know what you are and who you are! Self awareness is yours!") @@ -238,8 +236,6 @@ user.mind.transfer_to(SM) - SM.languages_spoken = user.languages_spoken - SM.languages_understood = user.languages_understood SM.faction = user.faction.Copy() SM.sentience_act() //Same deal here as with sentience user.death() diff --git a/code/modules/shuttle/special.dm b/code/modules/shuttle/special.dm index d039929d2fc..19cd3b1cfd1 100644 --- a/code/modules/shuttle/special.dm +++ b/code/modules/shuttle/special.dm @@ -65,9 +65,10 @@ var/never_spoken = TRUE flags = NODECONSTRUCT -/obj/structure/table/abductor/wabbajack/New() +/obj/structure/table/abductor/wabbajack/Initialize(mapload) . = ..() START_PROCESSING(SSobj, src) + grant_language(/datum/language/common) /obj/structure/table/abductor/wabbajack/Destroy() STOP_PROCESSING(SSobj, src) @@ -145,14 +146,13 @@ laws = "1. Serve drinks.\n\ 2. Talk to patrons.\n\ 3. Don't get messed up in their affairs." - languages_spoken = ALL - languages_understood = ALL status_flags = GODMODE // Please don't punch the barkeeper unique_name = FALSE // disables the (123) number suffix /mob/living/simple_animal/drone/snowflake/bardrone/Initialize() . = ..() access_card.access |= access_cent_bar + grant_all_languages(omnitongue=TRUE) /mob/living/simple_animal/hostile/alien/maid/barmaid gold_core_spawnable = 0 @@ -160,8 +160,6 @@ desc = "A barmaid, a maiden found in a bar." pass_flags = PASSTABLE status_flags = GODMODE - languages_spoken = ALL - languages_understood = ALL unique_name = FALSE AIStatus = AI_OFF stop_automated_movement = TRUE @@ -174,6 +172,8 @@ access_card.access |= access_cent_bar access_card.flags |= NODROP + grant_all_languages(omnitongue=TRUE) + /mob/living/simple_animal/hostile/alien/maid/barmaid/Destroy() qdel(access_card) . = ..() diff --git a/code/modules/surgery/organs/organ_internal.dm b/code/modules/surgery/organs/organ_internal.dm index 4a7f30138b2..4c066919e84 100644 --- a/code/modules/surgery/organs/organ_internal.dm +++ b/code/modules/surgery/organs/organ_internal.dm @@ -512,9 +512,18 @@ zone = "mouth" slot = "tongue" attack_verb = list("licked", "slobbered", "slapped", "frenched", "tongued") + var/list/languages_possible var/say_mod = null var/taste_sensitivity = 15 // lower is more sensitive. +/obj/item/organ/tongue/Initialize(mapload) + ..() + languages_possible = typecacheof(list( + /datum/language/common, + /datum/language/monkey, + /datum/language/ratvar + )) + /obj/item/organ/tongue/get_spans() return list() @@ -531,6 +540,9 @@ if(say_mod && M.dna && M.dna.species) M.dna.species.say_mod = initial(M.dna.species.say_mod) +/obj/item/organ/tongue/can_speak_in_language(datum/language/dt) + . = is_type_in_typecache(dt, languages_possible) + /obj/item/organ/tongue/lizard name = "forked tongue" desc = "A thin and long muscle typically found in reptilian races, apparently moonlights as a nose." @@ -617,6 +629,14 @@ say_mod = "hisses" taste_sensitivity = 10 // LIZARDS ARE ALIENS CONFIRMED +/obj/item/organ/tongue/alien/Initialize(mapload) + ..() + languages_possible = typecacheof(list( + /datum/language/xenocommon, + /datum/language/common, + /datum/language/ratvar, + /datum/language/monkey)) + /obj/item/organ/tongue/alien/TongueSpeech(var/message) playsound(owner, "hiss", 25, 1, 1) return message @@ -664,6 +684,17 @@ attack_verb = list("beeped", "booped") taste_sensitivity = 25 // not as good as an organic tongue +/obj/item/organ/tongue/robot/Initialize(mapload) + ..() + languages_possible = typecacheof(list( + /datum/language/xenocommon, + /datum/language/common, + /datum/language/ratvar, + /datum/language/monkey, + /datum/language/drone, + /datum/language/machine, + /datum/language/swarmer)) + /obj/item/organ/tongue/robot/get_spans() return ..() | SPAN_ROBOT diff --git a/code/modules/tgui/states/language_menu.dm b/code/modules/tgui/states/language_menu.dm new file mode 100644 index 00000000000..e7dea65024a --- /dev/null +++ b/code/modules/tgui/states/language_menu.dm @@ -0,0 +1,14 @@ + /** + * tgui state: language_menu_state + */ + +/var/global/datum/ui_state/language_menu/language_menu_state = new() + +/datum/ui_state/language_menu/can_use_topic(src_object, mob/user) + . = UI_CLOSE + if(check_rights_for(user.client, R_ADMIN)) + . = UI_INTERACTIVE + else if(istype(src_object, /datum/language_menu)) + var/datum/language_menu/LM = src_object + if(LM.owner == user) + . = UI_INTERACTIVE diff --git a/icons/mob/actions.dmi b/icons/mob/actions.dmi index c24aeccf5480b50919a30fd075773d65a4bdac19..3059ac422de2bae416bd081563eb71e444804670 100644 GIT binary patch delta 30080 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

^ZX;fEtEvSy1oNR-YaP+BH z*KgPR3pGR&%#_iqx2)%5P1dNN;VEldno!V)^)%%{P#wa&sG?Paflt|xGzT_%*ym{> zAZ2x{B^PVvcIe=EM*(ux9vRA(2Bgf!$p6d^J16~wu$7#;7UvnfZt3-SU3>PrHHR`< z?key1xtbj2rDyoxFsr}eUXp^;v1ihw2PrX0$p2P0){`*Oq2wsX>Y&zp6+Yor&tQKG zn1S9I71_-3cpt+-4E4Kd+3yqyi8V{RZffd%Xq0rtmNKV*xN7Tj*Od%2swF-*c`Wh3 zDuGq=kEwf+B_e0dPat*6yAIJ)v*R&r{2!N)Ml&jQ@6A!+hFPq4)%8Y3FRlp!qAn+ly#yK!h^4@ zUvo2DV5erMl~J;h;+~I&=}H_C3%n505maO+-_Ibfgl({x`J543Bq{SO#MU!om=sVE zj!n0_MDX9ri1{-;e=dy*eL1j}hYR^lFoF)*5t0uloean zI!II1q{r)Ps|{Gqggmwo7Vc07uM3Vt1uTSF(hSN>wf?HhRxH0k917d%b3j%Gli7x% zLbo2%_jhYp$OPBE*<`_kNT`eMayJoRb^q)*6v%M5`HE?>?yLq{s_3l^<(NvQJUahW v2>FWeO*Q3~UDjF;T~4_Cah94rm%78c!g;x}R4`r-0Y3^ds;@u4G7I=$b_!!g delta 29239 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 Y*(Y>}WJK19AmERZoVskqYZJf!1q|HfIsgCw diff --git a/interface/stylesheet.dm b/interface/stylesheet.dm index de2fcf5fbe4..d3e518159f3 100644 --- a/interface/stylesheet.dm +++ b/interface/stylesheet.dm @@ -146,5 +146,6 @@ BIG IMG.icon {width: 32px; height: 32px;} .memo {color: #638500; text-align: center;} .memoedit {text-align: center; font-size: 2;} .abductor {color: #800080; font-style: italic;} +.slime {color: #00CED1;} "} diff --git a/tgstation.dme b/tgstation.dme index ad28e91fb19..9320e6ce63a 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -35,6 +35,7 @@ #include "code\__DEFINES\hud.dm" #include "code\__DEFINES\inventory.dm" #include "code\__DEFINES\is_helpers.dm" +#include "code\__DEFINES\language.dm" #include "code\__DEFINES\layers.dm" #include "code\__DEFINES\lighting.dm" #include "code\__DEFINES\machines.dm" @@ -1361,6 +1362,16 @@ #include "code\modules\jobs\job_types\security.dm" #include "code\modules\jobs\job_types\silicon.dm" #include "code\modules\jobs\map_changes\map_changes.dm" +#include "code\modules\language\common.dm" +#include "code\modules\language\drone.dm" +#include "code\modules\language\language.dm" +#include "code\modules\language\language_menu.dm" +#include "code\modules\language\machine.dm" +#include "code\modules\language\monkey.dm" +#include "code\modules\language\ratvar.dm" +#include "code\modules\language\slime.dm" +#include "code\modules\language\swarmer.dm" +#include "code\modules\language\xenocommon.dm" #include "code\modules\library\lib_codex_gigas.dm" #include "code\modules\library\lib_items.dm" #include "code\modules\library\lib_machines.dm" @@ -2043,6 +2054,7 @@ #include "code\modules\tgui\states\hands.dm" #include "code\modules\tgui\states\human_adjacent.dm" #include "code\modules\tgui\states\inventory.dm" +#include "code\modules\tgui\states\language_menu.dm" #include "code\modules\tgui\states\not_incapacitated.dm" #include "code\modules\tgui\states\notcontained.dm" #include "code\modules\tgui\states\observer.dm" diff --git a/tgui/assets/tgui.css b/tgui/assets/tgui.css index 03651d3a6b6..d52500b9d64 100644 --- a/tgui/assets/tgui.css +++ b/tgui/assets/tgui.css @@ -1 +1 @@ -@charset "utf-8";body,html{box-sizing:border-box;height:100%;margin:0}html{overflow:hidden;cursor:default}body{overflow:auto;font-family:Verdana,Geneva,sans-serif;font-size:12px;color:#fff;background-color:#2a2a2a;background-image:linear-gradient(180deg,#2a2a2a 0,#202020);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff2a2a2a',endColorstr='#ff202020',GradientType=0)}*,:after,:before{box-sizing:inherit}h1,h2,h3,h4{display:inline-block;margin:0;padding:6px 0}h1{font-size:18px}h2{font-size:16px}h3{font-size:14px}h4{font-size:12px}body.clockwork{background:linear-gradient(180deg,#b18b25 0,#5f380e);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffb18b25',endColorstr='#ff5f380e',GradientType=0)}body.clockwork .normal{color:#b18b25}body.clockwork .good{color:#cfba47}body.clockwork .average{color:#896b19}body.clockwork .bad{color:#5f380e}body.clockwork .highlight{color:#b18b25}body.clockwork main{display:block;margin-top:32px;padding:2px 6px 0}body.clockwork hr{height:2px;background-color:#b18b25;border:none}body.clockwork .hidden{display:none}body.clockwork .bar .barText,body.clockwork span.button{color:#b18b25;font-size:12px;font-weight:400;font-style:normal;text-decoration:none}body.clockwork .bold{font-weight:700}body.clockwork .italic{font-style:italic}body.clockwork [unselectable=on]{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}body.clockwork div[data-tooltip],body.clockwork span[data-tooltip]{position:relative}body.clockwork div[data-tooltip]:after,body.clockwork span[data-tooltip]:after{position:absolute;display:block;z-index:2;width:250px;padding:10px;-ms-transform:translateX(-50%);transform:translateX(-50%);visibility:hidden;opacity:0;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";white-space:normal;text-align:left;content:attr(data-tooltip);transition:all .5s;border:1px solid #170800;background-color:#2d1400}body.clockwork div[data-tooltip]:hover:after,body.clockwork span[data-tooltip]:hover:after{visibility:visible;opacity:1;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"}body.clockwork div[data-tooltip].tooltip-top:after,body.clockwork span[data-tooltip].tooltip-top:after{bottom:100%;left:50%;-ms-transform:translateX(-50%) translateY(8px);transform:translateX(-50%) translateY(8px)}body.clockwork div[data-tooltip].tooltip-top:hover:after,body.clockwork span[data-tooltip].tooltip-top:hover:after{-ms-transform:translateX(-50%) translateY(-8px);transform:translateX(-50%) translateY(-8px)}body.clockwork div[data-tooltip].tooltip-bottom:after,body.clockwork span[data-tooltip].tooltip-bottom:after{top:100%;left:50%;-ms-transform:translateX(-50%) translateY(-8px);transform:translateX(-50%) translateY(-8px)}body.clockwork div[data-tooltip].tooltip-bottom:hover:after,body.clockwork span[data-tooltip].tooltip-bottom:hover:after{-ms-transform:translateX(-50%) translateY(8px);transform:translateX(-50%) translateY(8px)}body.clockwork div[data-tooltip].tooltip-left:after,body.clockwork span[data-tooltip].tooltip-left:after{top:50%;right:100%;-ms-transform:translateX(8px) translateY(-50%);transform:translateX(8px) translateY(-50%)}body.clockwork div[data-tooltip].tooltip-left:hover:after,body.clockwork span[data-tooltip].tooltip-left:hover:after{-ms-transform:translateX(-8px) translateY(-50%);transform:translateX(-8px) translateY(-50%)}body.clockwork div[data-tooltip].tooltip-right:after,body.clockwork span[data-tooltip].tooltip-right:after{top:50%;left:100%;-ms-transform:translateX(-8px) translateY(-50%);transform:translateX(-8px) translateY(-50%)}body.clockwork div[data-tooltip].tooltip-right:hover:after,body.clockwork span[data-tooltip].tooltip-right:hover:after{-ms-transform:translateX(8px) translateY(-50%);transform:translateX(8px) translateY(-50%)}body.clockwork .bar{display:inline-block;position:relative;vertical-align:middle;width:100%;height:20px;line-height:17px;padding:1px;border:1px solid #170800;background:#2d1400}body.clockwork .bar .barText{position:absolute;top:0;right:3px}body.clockwork .bar .barFill{display:block;height:100%;transition:background-color 1s;background-color:#b18b25}body.clockwork .bar .barFill.good{background-color:#cfba47}body.clockwork .bar .barFill.average{background-color:#896b19}body.clockwork .bar .barFill.bad{background-color:#5f380e}body.clockwork span.button{display:inline-block;vertical-align:middle;min-height:20px;line-height:17px;padding:0 5px;white-space:nowrap;border:1px solid #170800}body.clockwork span.button .fa{padding-right:2px}body.clockwork span.button.normal{transition:background-color .5s;background-color:#5f380e}body.clockwork span.button.normal.active:focus,body.clockwork span.button.normal.active:hover{transition:background-color .25s;background-color:#704211;outline:0}body.clockwork span.button.disabled{transition:background-color .5s;background-color:#2d1400}body.clockwork span.button.disabled.active:focus,body.clockwork span.button.disabled.active:hover{transition:background-color .25s;background-color:#441e00;outline:0}body.clockwork span.button.selected{transition:background-color .5s;background-color:#cfba47}body.clockwork span.button.selected.active:focus,body.clockwork span.button.selected.active:hover{transition:background-color .25s;background-color:#d1bd50;outline:0}body.clockwork span.button.toggle{transition:background-color .5s;background-color:#cfba47}body.clockwork span.button.toggle.active:focus,body.clockwork span.button.toggle.active:hover{transition:background-color .25s;background-color:#d1bd50;outline:0}body.clockwork span.button.caution{transition:background-color .5s;background-color:#be6209}body.clockwork span.button.caution.active:focus,body.clockwork span.button.caution.active:hover{transition:background-color .25s;background-color:#cd6a0a;outline:0}body.clockwork span.button.danger{transition:background-color .5s;background-color:#9a9d00}body.clockwork span.button.danger.active:focus,body.clockwork span.button.danger.active:hover{transition:background-color .25s;background-color:#abaf00;outline:0}body.clockwork span.button.gridable{width:125px;margin:2px 0}body.clockwork span.button+span:not(.button),body.clockwork span:not(.button)+span.button{margin-left:5px}body.clockwork div.display{width:100%;padding:4px;margin:6px 0;background-color:#2d1400;-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorStr=#e62d1400,endColorStr=#e62d1400)";filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#e62d1400,endColorStr=#e62d1400);background-color:rgba(45,20,0,.9);box-shadow:inset 0 0 5px rgba(0,0,0,.3)}body.clockwork div.display header,body.clockwork div.subdisplay header{display:block;position:relative;width:100%;padding:0 4px;margin-bottom:6px;color:#cfba47;border-bottom:2px solid #b18b25}body.clockwork div.display header .buttonRight,body.clockwork div.subdisplay header .buttonRight{position:absolute;bottom:6px;right:4px}body.clockwork div.display article,body.clockwork div.subdisplay article{display:table;width:100%;border-collapse:collapse}body.clockwork input{display:inline-block;vertical-align:middle;height:20px;line-height:17px;padding:0 5px;white-space:nowrap;color:#b18b25;background-color:#cfba47;border:1px solid #272727}body.clockwork input::-webkit-input-placeholder{color:#999}body.clockwork input::-moz-placeholder{color:#999}body.clockwork input:-ms-input-placeholder{color:#999}body.clockwork input::placeholder{color:#999}body.clockwork input::-ms-clear{display:none}body.clockwork svg.linegraph{overflow:hidden}body.clockwork div.notice{margin:8px 0;padding:4px;box-shadow:none;color:#2d1400;font-weight:700;font-style:italic;background-color:#000;background-image:repeating-linear-gradient(-45deg,#000,#000 10px,#170800 0,#170800 20px)}body.clockwork div.notice .label{color:#2d1400}body.clockwork div.notice .content:only-of-type{padding:0}body.clockwork div.notice hr{background-color:#896b19}body.clockwork div.resize{position:fixed;bottom:0;right:0;width:0;height:0;border-style:solid;border-width:0 0 45px 45px;border-color:transparent transparent #5f380e;-ms-transform:rotate(1turn);transform:rotate(1turn)}body.clockwork section .content,body.clockwork section .label,body.clockwork section .line,body.nanotrasen section .content,body.nanotrasen section .label,body.nanotrasen section .line,body.syndicate section .content,body.syndicate section .label,body.syndicate section .line{display:table-cell;margin:0;text-align:left;vertical-align:middle;padding:3px 2px}body.clockwork section{display:table-row;width:100%}body.clockwork section:not(:first-child){padding-top:4px}body.clockwork section.candystripe:nth-child(even){background-color:#000;-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorStr=#33000000,endColorStr=#33000000)";filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#33000000,endColorStr=#33000000);background-color:rgba(0,0,0,.2)}body.clockwork section .label{width:1%;padding-right:32px;white-space:nowrap;color:#b18b25}body.clockwork section .content:not(:last-child){padding-right:16px}body.clockwork section .line{width:100%}body.clockwork div.subdisplay{width:100%;margin:0}body.clockwork header.titlebar .close,body.clockwork header.titlebar .minimize{display:inline-block;position:relative;padding:7px;margin:-7px;color:#cfba47}body.clockwork header.titlebar .close:hover,body.clockwork header.titlebar .minimize:hover{color:#d1bd50}body.clockwork header.titlebar{position:fixed;z-index:1;top:0;left:0;width:100%;height:32px;background-color:#5f380e;border-bottom:1px solid #170800;box-shadow:0 3px 3px rgba(0,0,0,.1)}body.clockwork header.titlebar .statusicon{position:absolute;top:4px;left:12px;transition:color .5s}body.clockwork header.titlebar .title{position:absolute;top:6px;left:46px;color:#cfba47;font-size:16px;white-space:nowrap}body.clockwork header.titlebar .minimize{position:absolute;top:6px;right:46px}body.clockwork header.titlebar .close{position:absolute;top:4px;right:12px}body.nanotrasen{background:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+DQo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmVyc2lvbj0iMS4wIiB2aWV3Qm94PSIwIDAgNDI1IDIwMCIgb3BhY2l0eT0iLjMzIj4NCiAgPHBhdGggZD0ibSAxNzguMDAzOTksMC4wMzg2OSAtNzEuMjAzOTMsMCBhIDYuNzYxMzQyMiw2LjAyNTU0OTUgMCAwIDAgLTYuNzYxMzQsNi4wMjU1NSBsIDAsMTg3Ljg3MTQ3IGEgNi43NjEzNDIyLDYuMDI1NTQ5NSAwIDAgMCA2Ljc2MTM0LDYuMDI1NTQgbCA1My4xMDcyLDAgYSA2Ljc2MTM0MjIsNi4wMjU1NDk1IDAgMCAwIDYuNzYxMzUsLTYuMDI1NTQgbCAwLC0xMDEuNTQ0MDE4IDcyLjIxNjI4LDEwNC42OTkzOTggYSA2Ljc2MTM0MjIsNi4wMjU1NDk1IDAgMCAwIDUuNzYwMTUsMi44NzAxNiBsIDczLjU1NDg3LDAgYSA2Ljc2MTM0MjIsNi4wMjU1NDk1IDAgMCAwIDYuNzYxMzUsLTYuMDI1NTQgbCAwLC0xODcuODcxNDcgYSA2Ljc2MTM0MjIsNi4wMjU1NDk1IDAgMCAwIC02Ljc2MTM1LC02LjAyNTU1IGwgLTU0LjcxNjQ0LDAgYSA2Ljc2MTM0MjIsNi4wMjU1NDk1IDAgMCAwIC02Ljc2MTMzLDYuMDI1NTUgbCAwLDEwMi42MTkzNSBMIDE4My43NjQxMywyLjkwODg2IGEgNi43NjEzNDIyLDYuMDI1NTQ5NSAwIDAgMCAtNS43NjAxNCwtMi44NzAxNyB6IiAvPg0KICA8cGF0aCBkPSJNIDQuODQ0NjMzMywyMi4xMDg3NSBBIDEzLjQxMjAzOSwxMi41MDE4NDIgMCAwIDEgMTMuNDc3NTg4LDAuMDM5MjQgbCA2Ni4xMTgzMTUsMCBhIDUuMzY0ODE1OCw1LjAwMDczNyAwIDAgMSA1LjM2NDgyMyw1LjAwMDczIGwgMCw3OS44NzkzMSB6IiAvPg0KICA8cGF0aCBkPSJtIDQyMC4xNTUzNSwxNzcuODkxMTkgYSAxMy40MTIwMzgsMTIuNTAxODQyIDAgMCAxIC04LjYzMjk1LDIyLjA2OTUxIGwgLTY2LjExODMyLDAgYSA1LjM2NDgxNTIsNS4wMDA3MzcgMCAwIDEgLTUuMzY0ODIsLTUuMDAwNzQgbCAwLC03OS44NzkzMSB6IiAvPg0KPC9zdmc+DQo8IS0tIFRoaXMgd29yayBpcyBsaWNlbnNlZCB1bmRlciBhIENyZWF0aXZlIENvbW1vbnMgQXR0cmlidXRpb24tU2hhcmVBbGlrZSA0LjAgSW50ZXJuYXRpb25hbCBMaWNlbnNlLiAtLT4NCjwhLS0gaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnktc2EvNC4wLyAtLT4NCg==") no-repeat fixed 50%/70% 70%,linear-gradient(180deg,#2a2a2a 0,#202020);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff2a2a2a',endColorstr='#ff202020',GradientType=0)}body.nanotrasen .normal{color:#40628a}body.nanotrasen .good{color:#537d29}body.nanotrasen .average{color:#be6209}body.nanotrasen .bad{color:#b00e0e}body.nanotrasen .highlight{color:#8ba5c4}body.nanotrasen main{display:block;margin-top:32px;padding:2px 6px 0}body.nanotrasen hr{height:2px;background-color:#40628a;border:none}body.nanotrasen .hidden{display:none}body.nanotrasen .bar .barText,body.nanotrasen span.button{color:#fff;font-size:12px;font-weight:400;font-style:normal;text-decoration:none}body.nanotrasen .bold{font-weight:700}body.nanotrasen .italic{font-style:italic}body.nanotrasen [unselectable=on]{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}body.nanotrasen div[data-tooltip],body.nanotrasen span[data-tooltip]{position:relative}body.nanotrasen div[data-tooltip]:after,body.nanotrasen span[data-tooltip]:after{position:absolute;display:block;z-index:2;width:250px;padding:10px;-ms-transform:translateX(-50%);transform:translateX(-50%);visibility:hidden;opacity:0;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";white-space:normal;text-align:left;content:attr(data-tooltip);transition:all .5s;border:1px solid #272727;background-color:#363636}body.nanotrasen div[data-tooltip]:hover:after,body.nanotrasen span[data-tooltip]:hover:after{visibility:visible;opacity:1;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"}body.nanotrasen div[data-tooltip].tooltip-top:after,body.nanotrasen span[data-tooltip].tooltip-top:after{bottom:100%;left:50%;-ms-transform:translateX(-50%) translateY(8px);transform:translateX(-50%) translateY(8px)}body.nanotrasen div[data-tooltip].tooltip-top:hover:after,body.nanotrasen span[data-tooltip].tooltip-top:hover:after{-ms-transform:translateX(-50%) translateY(-8px);transform:translateX(-50%) translateY(-8px)}body.nanotrasen div[data-tooltip].tooltip-bottom:after,body.nanotrasen span[data-tooltip].tooltip-bottom:after{top:100%;left:50%;-ms-transform:translateX(-50%) translateY(-8px);transform:translateX(-50%) translateY(-8px)}body.nanotrasen div[data-tooltip].tooltip-bottom:hover:after,body.nanotrasen span[data-tooltip].tooltip-bottom:hover:after{-ms-transform:translateX(-50%) translateY(8px);transform:translateX(-50%) translateY(8px)}body.nanotrasen div[data-tooltip].tooltip-left:after,body.nanotrasen span[data-tooltip].tooltip-left:after{top:50%;right:100%;-ms-transform:translateX(8px) translateY(-50%);transform:translateX(8px) translateY(-50%)}body.nanotrasen div[data-tooltip].tooltip-left:hover:after,body.nanotrasen span[data-tooltip].tooltip-left:hover:after{-ms-transform:translateX(-8px) translateY(-50%);transform:translateX(-8px) translateY(-50%)}body.nanotrasen div[data-tooltip].tooltip-right:after,body.nanotrasen span[data-tooltip].tooltip-right:after{top:50%;left:100%;-ms-transform:translateX(-8px) translateY(-50%);transform:translateX(-8px) translateY(-50%)}body.nanotrasen div[data-tooltip].tooltip-right:hover:after,body.nanotrasen span[data-tooltip].tooltip-right:hover:after{-ms-transform:translateX(8px) translateY(-50%);transform:translateX(8px) translateY(-50%)}body.nanotrasen .bar{display:inline-block;position:relative;vertical-align:middle;width:100%;height:20px;line-height:17px;padding:1px;border:1px solid #40628a;background:#272727}body.nanotrasen .bar .barText{position:absolute;top:0;right:3px}body.nanotrasen .bar .barFill{display:block;height:100%;transition:background-color 1s;background-color:#40628a}body.nanotrasen .bar .barFill.good{background-color:#537d29}body.nanotrasen .bar .barFill.average{background-color:#be6209}body.nanotrasen .bar .barFill.bad{background-color:#b00e0e}body.nanotrasen span.button{display:inline-block;vertical-align:middle;min-height:20px;line-height:17px;padding:0 5px;white-space:nowrap;border:1px solid #272727}body.nanotrasen span.button .fa{padding-right:2px}body.nanotrasen span.button.normal{transition:background-color .5s;background-color:#40628a}body.nanotrasen span.button.normal.active:focus,body.nanotrasen span.button.normal.active:hover{transition:background-color .25s;background-color:#4f78aa;outline:0}body.nanotrasen span.button.disabled{transition:background-color .5s;background-color:#999}body.nanotrasen span.button.disabled.active:focus,body.nanotrasen span.button.disabled.active:hover{transition:background-color .25s;background-color:#a8a8a8;outline:0}body.nanotrasen span.button.selected{transition:background-color .5s;background-color:#2f943c}body.nanotrasen span.button.selected.active:focus,body.nanotrasen span.button.selected.active:hover{transition:background-color .25s;background-color:#3ab84b;outline:0}body.nanotrasen span.button.toggle{transition:background-color .5s;background-color:#2f943c}body.nanotrasen span.button.toggle.active:focus,body.nanotrasen span.button.toggle.active:hover{transition:background-color .25s;background-color:#3ab84b;outline:0}body.nanotrasen span.button.caution{transition:background-color .5s;background-color:#9a9d00}body.nanotrasen span.button.caution.active:focus,body.nanotrasen span.button.caution.active:hover{transition:background-color .25s;background-color:#ced200;outline:0}body.nanotrasen span.button.danger{transition:background-color .5s;background-color:#9d0808}body.nanotrasen span.button.danger.active:focus,body.nanotrasen span.button.danger.active:hover{transition:background-color .25s;background-color:#ce0b0b;outline:0}body.nanotrasen span.button.gridable{width:125px;margin:2px 0}body.nanotrasen span.button+span:not(.button),body.nanotrasen span:not(.button)+span.button{margin-left:5px}body.nanotrasen div.display{width:100%;padding:4px;margin:6px 0;background-color:#000;-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorStr=#54000000,endColorStr=#54000000)";filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#54000000,endColorStr=#54000000);background-color:rgba(0,0,0,.33);box-shadow:inset 0 0 5px rgba(0,0,0,.5)}body.nanotrasen div.display header,body.nanotrasen div.subdisplay header{display:block;position:relative;width:100%;padding:0 4px;margin-bottom:6px;color:#fff;border-bottom:2px solid #40628a}body.nanotrasen div.display header .buttonRight,body.nanotrasen div.subdisplay header .buttonRight{position:absolute;bottom:6px;right:4px}body.nanotrasen div.display article,body.nanotrasen div.subdisplay article{display:table;width:100%;border-collapse:collapse}body.nanotrasen input{display:inline-block;vertical-align:middle;height:20px;line-height:17px;padding:0 5px;white-space:nowrap;color:#000;background-color:#fff;border:1px solid #272727}body.nanotrasen input::-webkit-input-placeholder{color:#999}body.nanotrasen input::-moz-placeholder{color:#999}body.nanotrasen input:-ms-input-placeholder{color:#999}body.nanotrasen input::placeholder{color:#999}body.nanotrasen input::-ms-clear{display:none}body.nanotrasen svg.linegraph{overflow:hidden}body.nanotrasen div.notice{margin:8px 0;padding:4px;box-shadow:none;color:#000;font-weight:700;font-style:italic;background-color:#bb9b68;background-image:repeating-linear-gradient(-45deg,#bb9b68,#bb9b68 10px,#b1905d 0,#b1905d 20px)}body.nanotrasen div.notice .label{color:#000}body.nanotrasen div.notice .content:only-of-type{padding:0}body.nanotrasen div.notice hr{background-color:#272727}body.nanotrasen div.resize{position:fixed;bottom:0;right:0;width:0;height:0;border-style:solid;border-width:0 0 45px 45px;border-color:transparent transparent #363636;-ms-transform:rotate(1turn);transform:rotate(1turn)}body.nanotrasen section .content,body.nanotrasen section .label,body.nanotrasen section .line,body.syndicate section .content,body.syndicate section .label,body.syndicate section .line{display:table-cell;margin:0;text-align:left;vertical-align:middle;padding:3px 2px}body.nanotrasen section{display:table-row;width:100%}body.nanotrasen section:not(:first-child){padding-top:4px}body.nanotrasen section.candystripe:nth-child(even){background-color:#000;-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorStr=#33000000,endColorStr=#33000000)";filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#33000000,endColorStr=#33000000);background-color:rgba(0,0,0,.2)}body.nanotrasen section .label{width:1%;padding-right:32px;white-space:nowrap;color:#8ba5c4}body.nanotrasen section .content:not(:last-child){padding-right:16px}body.nanotrasen section .line{width:100%}body.nanotrasen div.subdisplay{width:100%;margin:0}body.nanotrasen header.titlebar .close,body.nanotrasen header.titlebar .minimize{display:inline-block;position:relative;padding:7px;margin:-7px;color:#8ba5c4}body.nanotrasen header.titlebar .close:hover,body.nanotrasen header.titlebar .minimize:hover{color:#9cb2cd}body.nanotrasen header.titlebar{position:fixed;z-index:1;top:0;left:0;width:100%;height:32px;background-color:#363636;border-bottom:1px solid #161616;box-shadow:0 3px 3px rgba(0,0,0,.1)}body.nanotrasen header.titlebar .statusicon{position:absolute;top:4px;left:12px;transition:color .5s}body.nanotrasen header.titlebar .title{position:absolute;top:6px;left:46px;color:#8ba5c4;font-size:16px;white-space:nowrap}body.nanotrasen header.titlebar .minimize{position:absolute;top:6px;right:46px}body.nanotrasen header.titlebar .close{position:absolute;top:4px;right:12px}body.syndicate{background:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+DQo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmVyc2lvbj0iMS4wIiB2aWV3Qm94PSIwIDAgMjAwIDI4OS43NDIiIG9wYWNpdHk9Ii4zMyI+DQogIDxwYXRoIGQ9Im0gOTMuNTM3Njc3LDAgYyAtMTguMTEzMTI1LDAgLTM0LjIyMDEzMywzLjExMTY0IC00OC4zMjM0ODQsOS4zMzQzNyAtMTMuOTY1MDkyLDYuMjIxNjcgLTI0LjYxMjQ0MiwxNS4wNzExNCAtMzEuOTQwNjUxLDI2LjU0NzEgLTcuMTg5OTM5OCwxMS4zMzc4OSAtMTAuMzAxMjI2NiwyNC43NDkxMSAtMTAuMzAxMjI2Niw0MC4yMzQ3OCAwLDEwLjY0NjYyIDIuNzI1MDAyNiwyMC40NjQ2NSA4LjE3NTExMTYsMjkuNDUyNTggNS42MTUyNzcsOC45ODY4NiAxNC4wMzgyNzcsMTcuMzUyMDQgMjUuMjY4ODIxLDI1LjA5NDM2IDExLjIzMDU0NCw3LjYwNTMxIDI2LjUwNzQyMSwxNS40MTgzNSA0NS44MzA1MTQsMjMuNDM3ODIgMTkuOTgzNzQ4LDguMjk1NTcgMzQuODQ4ODQ4LDE1LjU1NDcxIDQ0LjU5Mjk5OCwyMS43NzYzOCA5Ljc0NDE0LDYuMjIyNzMgMTYuNzYxNywxMi44NTg1IDIxLjA1NTcyLDE5LjkwOTUxIDQuMjk0MDQsNy4wNTIwOCA2LjQ0MTkzLDE1Ljc2NDA4IDYuNDQxOTMsMjYuMTM0NTkgMCwxNi4xNzcwMiAtNS4yMDE5NiwyOC40ODIyMiAtMTUuNjA2NzMsMzYuOTE2ODIgLTEwLjIzOTYsOC40MzQ3IC0yNS4wMjIwMywxMi42NTIzIC00NC4zNDUxNjksMTIuNjUyMyAtMTQuMDM4MTcxLDAgLTI1LjUxNTI0NywtMS42NTk0IC0zNC40MzM2MTgsLTQuOTc3NyAtOC45MTgzNywtMy40NTY2IC0xNi4xODU1NzIsLTguNzExMyAtMjEuODAwODM5LC0xNS43NjMzIC01LjYxNTI3NywtNy4wNTIxIC0xMC4wNzQ3OTUsLTE2LjY2MDg4IC0xMy4zNzc4OTksLTI4LjgyODEyIGwgLTI0Ljc3MzE2MjYyOTM5NDUsMCAwLDU2LjgyNjMyIEMgMzMuODU2NzY5LDI4Ni4wNzYwMSA2My43NDkwNCwyODkuNzQyMDEgODkuNjc4MzgzLDI4OS43NDIwMSBjIDE2LjAyMDAyNywwIDMwLjcxOTc4NywtMS4zODI3IDQ0LjA5NzMzNywtNC4xNDc5IDEzLjU0MjcyLC0yLjkwNDMgMjUuMTA0MSwtNy40Njc2IDM0LjY4MzA5LC0xMy42ODkzIDkuNzQ0MTMsLTYuMzU5NyAxNy4zNDA0MiwtMTQuNTE5NSAyMi43OTA1MiwtMjQuNDc0OCA1LjQ1MDEsLTEwLjA5MzMyIDguMTc1MTEsLTIyLjM5OTU5IDguMTc1MTEsLTM2LjkxNjgyIDAsLTEyLjk5NzY0IC0zLjMwMjEsLTI0LjMzNTM5IC05LjkwODI5LC0zNC4wMTQ2IC02LjQ0MTA1LC05LjgxNzI1IC0xNS41MjU0NSwtMTguNTI3MDcgLTI3LjI1MTQ2LC0yNi4xMzEzMyAtMTEuNTYwODUsLTcuNjA0MjcgLTI3LjkxMDgzLC0xNS44MzE0MiAtNDkuMDUwNjYsLTI0LjY4MDIyIC0xNy41MDY0NCwtNy4xOTAxMiAtMzAuNzE5NjY4LC0xMy42ODk0OCAtMzkuNjM4MDM4LC0xOS40OTcwMSAtOC45MTgzNzEsLTUuODA3NTIgLTE4LjYwNzQ3NCwtMTIuNDM0MDkgLTI0LjA5NjUyNCwtMTguODc0MTcgLTUuNDI2MDQzLC02LjM2NjE2IC05LjY1ODgyNiwtMTUuMDcwMDMgLTkuNjU4ODI2LC0yNC44ODcyOSAwLC05LjI2NDAxIDIuMDc1NDE0LC0xNy4yMTM0NSA2LjIyMzQ1NCwtMjMuODUwMzMgMTEuMDk4Mjk4LC0xNC4zOTc0OCA0MS4yODY2MzgsLTEuNzk1MDcgNDUuMDc1NjA5LDI0LjM0NzYyIDQuODM5MzkyLDYuNzc0OTEgOC44NDkzNSwxNi4yNDcyOSAxMi4wMjk1MTUsMjguNDE1NiBsIDIwLjUzMjM0LDAgMCwtNTUuOTk5NjcgYyAtNC40NzgyNSwtNS45MjQ0OCAtOS45NTQ4OCwtMTAuNjMyMjIgLTE1LjkwODM3LC0xNC4zNzQxMSAxLjY0MDU1LDAuNDc5MDUgMy4xOTAzOSwxLjAyMzc2IDQuNjM4NjUsMS42NDAyNCA2LjQ5ODYxLDIuNjI2MDcgMTIuMTY3OTMsNy4zMjc0NyAxNy4wMDczLDE0LjEwMzQ1IDQuODM5MzksNi43NzQ5MSA4Ljg0OTM1LDE2LjI0NTY3IDEyLjAyOTUyLDI4LjQxMzk3IDAsMCA4LjQ4MTI4LC0wLjEyODk0IDguNDg5NzgsLTAuMDAyIDAuNDE3NzYsNi40MTQ5NCAtMS43NTMzOSw5LjQ1Mjg2IC00LjEyMzQyLDEyLjU2MTA0IC0yLjQxNzQsMy4xNjk3OCAtNS4xNDQ4Niw2Ljc4OTczIC00LjAwMjc4LDEzLjAwMjkgMS41MDc4Niw4LjIwMzE4IDEwLjE4MzU0LDEwLjU5NjQyIDE0LjYyMTk0LDkuMzExNTQgLTMuMzE4NDIsLTAuNDk5MTEgLTUuMzE4NTUsLTEuNzQ5NDggLTUuMzE4NTUsLTEuNzQ5NDggMCwwIDEuODc2NDYsMC45OTg2OCA1LjY1MTE3LC0xLjM1OTgxIC0zLjI3Njk1LDAuOTU1NzEgLTEwLjcwNTI5LC0wLjc5NzM4IC0xMS44MDEyNSwtNi43NjMxMyAtMC45NTc1MiwtNS4yMDg2MSAwLjk0NjU0LC03LjI5NTE0IDMuNDAxMTMsLTEwLjUxNDgyIDIuNDU0NjIsLTMuMjE5NjggNS4yODQyNiwtNi45NTgzMSA0LjY4NDMsLTE0LjQ4ODI0IGwgMC4wMDMsMC4wMDIgOC45MjY3NiwwIDAsLTU1Ljk5OTY3IGMgLTE1LjA3MTI1LC0zLjg3MTY4IC0yNy42NTMxNCwtNi4zNjA0MiAtMzcuNzQ2NzEsLTcuNDY1ODYgLTkuOTU1MzEsLTEuMTA3NTUgLTIwLjE4ODIzLC0xLjY1OTgxIC0zMC42OTY2MTMsLTEuNjU5ODEgeiBtIDcwLjMyMTYwMywxNy4zMDg5MyAwLjIzODA1LDQwLjMwNDkgYyAxLjMxODA4LDEuMjI2NjYgMi40Mzk2NSwyLjI3ODE1IDMuMzQwODEsMy4xMDYwMiA0LjgzOTM5LDYuNzc0OTEgOC44NDkzNCwxNi4yNDU2NiAxMi4wMjk1MSwyOC40MTM5NyBsIDIwLjUzMjM0LDAgMCwtNTUuOTk5NjcgYyAtNi42NzczMSwtNC41OTM4MSAtMTkuODM2NDMsLTEwLjQ3MzA5IC0zNi4xNDA3MSwtMTUuODI1MjIgeiBtIC0yOC4xMjA0OSw1LjYwNTUxIDguNTY0NzksMTcuNzE2NTUgYyAtMTEuOTcwMzcsLTYuNDY2OTcgLTEzLjg0Njc4LC05LjcxNzI2IC04LjU2NDc5LC0xNy43MTY1NSB6IG0gMjIuNzk3MDUsMCBjIDIuNzcxNSw3Ljk5OTI5IDEuNzg3NDEsMTEuMjQ5NTggLTQuNDkzNTQsMTcuNzE2NTUgbCA0LjQ5MzU0LC0xNy43MTY1NSB6IG0gMTUuMjIxOTUsMjQuMDA4NDggOC41NjQ3OSwxNy43MTY1NSBjIC0xMS45NzAzOCwtNi40NjY5NyAtMTMuODQ2NzksLTkuNzE3MjYgLTguNTY0NzksLTE3LjcxNjU1IHogbSAyMi43OTcwNCwwIGMgMi43NzE1LDcuOTk5MjkgMS43ODc0MSwxMS4yNDk1OCAtNC40OTM1NCwxNy43MTY1NSBsIDQuNDkzNTQsLTE3LjcxNjU1IHogbSAtOTkuMTEzODQsMi4yMDc2NCA4LjU2NDc5LDE3LjcxNjU1IGMgLTExLjk3MDM4MiwtNi40NjY5NyAtMTMuODQ2NzgyLC05LjcxNzI2IC04LjU2NDc5LC0xNy43MTY1NSB6IG0gMjIuNzk1NDIsMCBjIDIuNzcxNSw3Ljk5OTI5IDEuNzg3NDEsMTEuMjQ5NTggLTQuNDkzNTQsMTcuNzE2NTUgbCA0LjQ5MzU0LC0xNy43MTY1NSB6IiAvPg0KPC9zdmc+DQo8IS0tIFRoaXMgd29yayBpcyBsaWNlbnNlZCB1bmRlciBhIENyZWF0aXZlIENvbW1vbnMgQXR0cmlidXRpb24tU2hhcmVBbGlrZSA0LjAgSW50ZXJuYXRpb25hbCBMaWNlbnNlLiAtLT4NCjwhLS0gaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnktc2EvNC4wLyAtLT4NCg==") no-repeat fixed 50%/70% 70%,linear-gradient(180deg,#750000 0,#340404);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff750000',endColorstr='#ff340404',GradientType=0)}body.syndicate .normal{color:#40628a}body.syndicate .good{color:#73e573}body.syndicate .average{color:#be6209}body.syndicate .bad{color:#b00e0e}body.syndicate .highlight{color:#000}body.syndicate main{display:block;margin-top:32px;padding:2px 6px 0}body.syndicate hr{height:2px;background-color:#272727;border:none}body.syndicate .hidden{display:none}body.syndicate .bar .barText,body.syndicate span.button{color:#fff;font-size:12px;font-weight:400;font-style:normal;text-decoration:none}body.syndicate .bold{font-weight:700}body.syndicate .italic{font-style:italic}body.syndicate [unselectable=on]{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}body.syndicate div[data-tooltip],body.syndicate span[data-tooltip]{position:relative}body.syndicate div[data-tooltip]:after,body.syndicate span[data-tooltip]:after{position:absolute;display:block;z-index:2;width:250px;padding:10px;-ms-transform:translateX(-50%);transform:translateX(-50%);visibility:hidden;opacity:0;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";white-space:normal;text-align:left;content:attr(data-tooltip);transition:all .5s;border:1px solid #272727;background-color:#363636}body.syndicate div[data-tooltip]:hover:after,body.syndicate span[data-tooltip]:hover:after{visibility:visible;opacity:1;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"}body.syndicate div[data-tooltip].tooltip-top:after,body.syndicate span[data-tooltip].tooltip-top:after{bottom:100%;left:50%;-ms-transform:translateX(-50%) translateY(8px);transform:translateX(-50%) translateY(8px)}body.syndicate div[data-tooltip].tooltip-top:hover:after,body.syndicate span[data-tooltip].tooltip-top:hover:after{-ms-transform:translateX(-50%) translateY(-8px);transform:translateX(-50%) translateY(-8px)}body.syndicate div[data-tooltip].tooltip-bottom:after,body.syndicate span[data-tooltip].tooltip-bottom:after{top:100%;left:50%;-ms-transform:translateX(-50%) translateY(-8px);transform:translateX(-50%) translateY(-8px)}body.syndicate div[data-tooltip].tooltip-bottom:hover:after,body.syndicate span[data-tooltip].tooltip-bottom:hover:after{-ms-transform:translateX(-50%) translateY(8px);transform:translateX(-50%) translateY(8px)}body.syndicate div[data-tooltip].tooltip-left:after,body.syndicate span[data-tooltip].tooltip-left:after{top:50%;right:100%;-ms-transform:translateX(8px) translateY(-50%);transform:translateX(8px) translateY(-50%)}body.syndicate div[data-tooltip].tooltip-left:hover:after,body.syndicate span[data-tooltip].tooltip-left:hover:after{-ms-transform:translateX(-8px) translateY(-50%);transform:translateX(-8px) translateY(-50%)}body.syndicate div[data-tooltip].tooltip-right:after,body.syndicate span[data-tooltip].tooltip-right:after{top:50%;left:100%;-ms-transform:translateX(-8px) translateY(-50%);transform:translateX(-8px) translateY(-50%)}body.syndicate div[data-tooltip].tooltip-right:hover:after,body.syndicate span[data-tooltip].tooltip-right:hover:after{-ms-transform:translateX(8px) translateY(-50%);transform:translateX(8px) translateY(-50%)}body.syndicate .bar{display:inline-block;position:relative;vertical-align:middle;width:100%;height:20px;line-height:17px;padding:1px;border:1px solid #000;background:#272727}body.syndicate .bar .barText{position:absolute;top:0;right:3px}body.syndicate .bar .barFill{display:block;height:100%;transition:background-color 1s;background-color:#000}body.syndicate .bar .barFill.good{background-color:#73e573}body.syndicate .bar .barFill.average{background-color:#be6209}body.syndicate .bar .barFill.bad{background-color:#b00e0e}body.syndicate span.button{display:inline-block;vertical-align:middle;min-height:20px;line-height:17px;padding:0 5px;white-space:nowrap;border:1px solid #272727}body.syndicate span.button .fa{padding-right:2px}body.syndicate span.button.normal{transition:background-color .5s;background-color:#397439}body.syndicate span.button.normal.active:focus,body.syndicate span.button.normal.active:hover{transition:background-color .25s;background-color:#4a964a;outline:0}body.syndicate span.button.disabled{transition:background-color .5s;background-color:#363636}body.syndicate span.button.disabled.active:focus,body.syndicate span.button.disabled.active:hover{transition:background-color .25s;background-color:#545454;outline:0}body.syndicate span.button.selected{transition:background-color .5s;background-color:#9d0808}body.syndicate span.button.selected.active:focus,body.syndicate span.button.selected.active:hover{transition:background-color .25s;background-color:#ce0b0b;outline:0}body.syndicate span.button.toggle{transition:background-color .5s;background-color:#9d0808}body.syndicate span.button.toggle.active:focus,body.syndicate span.button.toggle.active:hover{transition:background-color .25s;background-color:#ce0b0b;outline:0}body.syndicate span.button.caution{transition:background-color .5s;background-color:#be6209}body.syndicate span.button.caution.active:focus,body.syndicate span.button.caution.active:hover{transition:background-color .25s;background-color:#eb790b;outline:0}body.syndicate span.button.danger{transition:background-color .5s;background-color:#9a9d00}body.syndicate span.button.danger.active:focus,body.syndicate span.button.danger.active:hover{transition:background-color .25s;background-color:#ced200;outline:0}body.syndicate span.button.gridable{width:125px;margin:2px 0}body.syndicate span.button+span:not(.button),body.syndicate span:not(.button)+span.button{margin-left:5px}body.syndicate div.display{width:100%;padding:4px;margin:6px 0;background-color:#000;-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorStr=#80000000,endColorStr=#80000000)";filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#80000000,endColorStr=#80000000);background-color:rgba(0,0,0,.5);box-shadow:inset 0 0 5px rgba(0,0,0,.75)}body.syndicate div.display header,body.syndicate div.subdisplay header{display:block;position:relative;width:100%;padding:0 4px;margin-bottom:6px;color:#fff;border-bottom:2px solid #272727}body.syndicate div.display header .buttonRight,body.syndicate div.subdisplay header .buttonRight{position:absolute;bottom:6px;right:4px}body.syndicate div.display article,body.syndicate div.subdisplay article{display:table;width:100%;border-collapse:collapse}body.syndicate input{display:inline-block;vertical-align:middle;height:20px;line-height:17px;padding:0 5px;white-space:nowrap;color:#fff;background-color:#9d0808;border:1px solid #272727}body.syndicate input::-webkit-input-placeholder{color:#999}body.syndicate input::-moz-placeholder{color:#999}body.syndicate input:-ms-input-placeholder{color:#999}body.syndicate input::placeholder{color:#999}body.syndicate input::-ms-clear{display:none}body.syndicate svg.linegraph{overflow:hidden}body.syndicate div.notice{margin:8px 0;padding:4px;box-shadow:none;color:#000;font-weight:700;font-style:italic;background-color:#750000;background-image:repeating-linear-gradient(-45deg,#750000,#750000 10px,#910101 0,#910101 20px)}body.syndicate div.notice .label{color:#000}body.syndicate div.notice .content:only-of-type{padding:0}body.syndicate div.notice hr{background-color:#272727}body.syndicate div.resize{position:fixed;bottom:0;right:0;width:0;height:0;border-style:solid;border-width:0 0 45px 45px;border-color:transparent transparent #363636;-ms-transform:rotate(1turn);transform:rotate(1turn)}body.syndicate section .content,body.syndicate section .label,body.syndicate section .line{display:table-cell;margin:0;text-align:left;vertical-align:middle;padding:3px 2px}body.syndicate section{display:table-row;width:100%}body.syndicate section:not(:first-child){padding-top:4px}body.syndicate section.candystripe:nth-child(even){background-color:#000;-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorStr=#33000000,endColorStr=#33000000)";filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#33000000,endColorStr=#33000000);background-color:rgba(0,0,0,.2)}body.syndicate section .label{width:1%;padding-right:32px;white-space:nowrap;color:#fff}body.syndicate section .content:not(:last-child){padding-right:16px}body.syndicate section .line{width:100%}body.syndicate div.subdisplay{width:100%;margin:0}body.syndicate header.titlebar .close,body.syndicate header.titlebar .minimize{display:inline-block;position:relative;padding:7px;margin:-7px;color:#e74242}body.syndicate header.titlebar .close:hover,body.syndicate header.titlebar .minimize:hover{color:#eb5e5e}body.syndicate header.titlebar{position:fixed;z-index:1;top:0;left:0;width:100%;height:32px;background-color:#363636;border-bottom:1px solid #161616;box-shadow:0 3px 3px rgba(0,0,0,.1)}body.syndicate header.titlebar .statusicon{position:absolute;top:4px;left:12px;transition:color .5s}body.syndicate header.titlebar .title{position:absolute;top:6px;left:46px;color:#e74242;font-size:16px;white-space:nowrap}body.syndicate header.titlebar .minimize{position:absolute;top:6px;right:46px}body.syndicate header.titlebar .close{position:absolute;top:4px;right:12px}.no-icons header.titlebar .statusicon{font-size:20px}.no-icons header.titlebar .statusicon:after{content:"O"}.no-icons header.titlebar .minimize{top:-2px;font-size:20px}.no-icons header.titlebar .minimize:after{content:"—"}.no-icons header.titlebar .close{font-size:20px}.no-icons header.titlebar .close:after{content:"X"} \ No newline at end of file +@charset "utf-8";body,html{box-sizing:border-box;height:100%;margin:0}html{overflow:hidden;cursor:default}body{overflow:auto;font-family:Verdana,Geneva,sans-serif;font-size:12px;color:#fff;background-color:#2a2a2a;background-image:linear-gradient(180deg,#2a2a2a 0,#202020);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff2a2a2a',endColorstr='#ff202020',GradientType=0)}*,:after,:before{box-sizing:inherit}h1,h2,h3,h4{display:inline-block;margin:0;padding:6px 0}h1{font-size:18px}h2{font-size:16px}h3{font-size:14px}h4{font-size:12px}body.clockwork{background:linear-gradient(180deg,#b18b25 0,#5f380e);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffb18b25',endColorstr='#ff5f380e',GradientType=0)}body.clockwork .normal{color:#b18b25}body.clockwork .good{color:#cfba47}body.clockwork .average{color:#896b19}body.clockwork .bad{color:#5f380e}body.clockwork .highlight{color:#b18b25}body.clockwork main{display:block;margin-top:32px;padding:2px 6px 0}body.clockwork hr{height:2px;background-color:#b18b25;border:none}body.clockwork .hidden{display:none}body.clockwork .bar .barText,body.clockwork span.button{color:#b18b25;font-size:12px;font-weight:400;font-style:normal;text-decoration:none}body.clockwork .bold{font-weight:700}body.clockwork .italic{font-style:italic}body.clockwork [unselectable=on]{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}body.clockwork div[data-tooltip],body.clockwork span[data-tooltip]{position:relative}body.clockwork div[data-tooltip]:after,body.clockwork span[data-tooltip]:after{position:absolute;display:block;z-index:2;width:250px;padding:10px;-webkit-transform:translateX(-50%);-ms-transform:translateX(-50%);transform:translateX(-50%);visibility:hidden;opacity:0;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";white-space:normal;text-align:left;content:attr(data-tooltip);transition:all .5s;border:1px solid #170800;background-color:#2d1400}body.clockwork div[data-tooltip]:hover:after,body.clockwork span[data-tooltip]:hover:after{visibility:visible;opacity:1;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"}body.clockwork div[data-tooltip].tooltip-top:after,body.clockwork span[data-tooltip].tooltip-top:after{bottom:100%;left:50%;-webkit-transform:translateX(-50%) translateY(8px);-ms-transform:translateX(-50%) translateY(8px);transform:translateX(-50%) translateY(8px)}body.clockwork div[data-tooltip].tooltip-bottom:after,body.clockwork div[data-tooltip].tooltip-top:hover:after,body.clockwork span[data-tooltip].tooltip-bottom:after,body.clockwork span[data-tooltip].tooltip-top:hover:after{-webkit-transform:translateX(-50%) translateY(-8px);-ms-transform:translateX(-50%) translateY(-8px);transform:translateX(-50%) translateY(-8px)}body.clockwork div[data-tooltip].tooltip-bottom:after,body.clockwork span[data-tooltip].tooltip-bottom:after{top:100%;left:50%}body.clockwork div[data-tooltip].tooltip-bottom:hover:after,body.clockwork span[data-tooltip].tooltip-bottom:hover:after{-webkit-transform:translateX(-50%) translateY(8px);-ms-transform:translateX(-50%) translateY(8px);transform:translateX(-50%) translateY(8px)}body.clockwork div[data-tooltip].tooltip-left:after,body.clockwork span[data-tooltip].tooltip-left:after{top:50%;right:100%;-webkit-transform:translateX(8px) translateY(-50%);-ms-transform:translateX(8px) translateY(-50%);transform:translateX(8px) translateY(-50%)}body.clockwork div[data-tooltip].tooltip-left:hover:after,body.clockwork div[data-tooltip].tooltip-right:after,body.clockwork span[data-tooltip].tooltip-left:hover:after,body.clockwork span[data-tooltip].tooltip-right:after{-webkit-transform:translateX(-8px) translateY(-50%);-ms-transform:translateX(-8px) translateY(-50%);transform:translateX(-8px) translateY(-50%)}body.clockwork div[data-tooltip].tooltip-right:after,body.clockwork span[data-tooltip].tooltip-right:after{top:50%;left:100%}body.clockwork div[data-tooltip].tooltip-right:hover:after,body.clockwork span[data-tooltip].tooltip-right:hover:after{-webkit-transform:translateX(8px) translateY(-50%);-ms-transform:translateX(8px) translateY(-50%);transform:translateX(8px) translateY(-50%)}body.clockwork .bar{display:inline-block;position:relative;vertical-align:middle;width:100%;height:20px;line-height:17px;padding:1px;border:1px solid #170800;background:#2d1400}body.clockwork .bar .barText{position:absolute;top:0;right:3px}body.clockwork .bar .barFill{display:block;height:100%;transition:background-color 1s;background-color:#b18b25}body.clockwork .bar .barFill.good{background-color:#cfba47}body.clockwork .bar .barFill.average{background-color:#896b19}body.clockwork .bar .barFill.bad{background-color:#5f380e}body.clockwork span.button{display:inline-block;vertical-align:middle;min-height:20px;line-height:17px;padding:0 5px;white-space:nowrap;border:1px solid #170800}body.clockwork span.button .fa{padding-right:2px}body.clockwork span.button.normal{transition:background-color .5s;background-color:#5f380e}body.clockwork span.button.normal.active:focus,body.clockwork span.button.normal.active:hover{transition:background-color .25s;background-color:#704211;outline:0}body.clockwork span.button.disabled{transition:background-color .5s;background-color:#2d1400}body.clockwork span.button.disabled.active:focus,body.clockwork span.button.disabled.active:hover{transition:background-color .25s;background-color:#441e00;outline:0}body.clockwork span.button.selected{transition:background-color .5s;background-color:#cfba47}body.clockwork span.button.selected.active:focus,body.clockwork span.button.selected.active:hover{transition:background-color .25s;background-color:#d1bd50;outline:0}body.clockwork span.button.toggle{transition:background-color .5s;background-color:#cfba47}body.clockwork span.button.toggle.active:focus,body.clockwork span.button.toggle.active:hover{transition:background-color .25s;background-color:#d1bd50;outline:0}body.clockwork span.button.caution{transition:background-color .5s;background-color:#be6209}body.clockwork span.button.caution.active:focus,body.clockwork span.button.caution.active:hover{transition:background-color .25s;background-color:#cd6a0a;outline:0}body.clockwork span.button.danger{transition:background-color .5s;background-color:#9a9d00}body.clockwork span.button.danger.active:focus,body.clockwork span.button.danger.active:hover{transition:background-color .25s;background-color:#abaf00;outline:0}body.clockwork span.button.gridable{width:125px;margin:2px 0}body.clockwork span.button+span:not(.button),body.clockwork span:not(.button)+span.button{margin-left:5px}body.clockwork div.display{width:100%;padding:4px;margin:6px 0;background-color:#2d1400;-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorStr=#e62d1400,endColorStr=#e62d1400)";filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#e62d1400,endColorStr=#e62d1400);background-color:rgba(45,20,0,.9);box-shadow:inset 0 0 5px rgba(0,0,0,.3)}body.clockwork div.display header,body.clockwork div.subdisplay header{display:block;position:relative;width:100%;padding:0 4px;margin-bottom:6px;color:#cfba47;border-bottom:2px solid #b18b25}body.clockwork div.display header .buttonRight,body.clockwork div.subdisplay header .buttonRight{position:absolute;bottom:6px;right:4px}body.clockwork div.display article,body.clockwork div.subdisplay article{display:table;width:100%;border-collapse:collapse}body.clockwork input{display:inline-block;vertical-align:middle;height:20px;line-height:17px;padding:0 5px;white-space:nowrap;color:#b18b25;background-color:#cfba47;border:1px solid #272727}body.clockwork input::-webkit-input-placeholder{color:#999}body.clockwork input::-moz-placeholder{color:#999}body.clockwork input:-ms-input-placeholder{color:#999}body.clockwork input::placeholder{color:#999}body.clockwork input::-ms-clear{display:none}body.clockwork svg.linegraph{overflow:hidden}body.clockwork div.notice{margin:8px 0;padding:4px;box-shadow:none;color:#2d1400;font-weight:700;font-style:italic;background-color:#000;background-image:repeating-linear-gradient(-45deg,#000,#000 10px,#170800 0,#170800 20px)}body.clockwork div.notice .label{color:#2d1400}body.clockwork div.notice .content:only-of-type{padding:0}body.clockwork div.notice hr{background-color:#896b19}body.clockwork div.resize{position:fixed;bottom:0;right:0;width:0;height:0;border-style:solid;border-width:0 0 45px 45px;border-color:transparent transparent #5f380e;-webkit-transform:rotate(1turn);-ms-transform:rotate(1turn);transform:rotate(1turn)}body.clockwork section .content,body.clockwork section .label,body.clockwork section .line,body.nanotrasen section .content,body.nanotrasen section .label,body.nanotrasen section .line,body.syndicate section .content,body.syndicate section .label,body.syndicate section .line{display:table-cell;margin:0;text-align:left;vertical-align:middle;padding:3px 2px}body.clockwork section{display:table-row;width:100%}body.clockwork section:not(:first-child){padding-top:4px}body.clockwork section.candystripe:nth-child(even){background-color:#000;-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorStr=#33000000,endColorStr=#33000000)";filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#33000000,endColorStr=#33000000);background-color:rgba(0,0,0,.2)}body.clockwork section .label{width:1%;padding-right:32px;white-space:nowrap;color:#b18b25}body.clockwork section .content:not(:last-child){padding-right:16px}body.clockwork section .line{width:100%}body.clockwork div.subdisplay{width:100%;margin:0}body.clockwork header.titlebar .close,body.clockwork header.titlebar .minimize{display:inline-block;position:relative;padding:7px;margin:-7px;color:#cfba47}body.clockwork header.titlebar .close:hover,body.clockwork header.titlebar .minimize:hover{color:#d1bd50}body.clockwork header.titlebar{position:fixed;z-index:1;top:0;left:0;width:100%;height:32px;background-color:#5f380e;border-bottom:1px solid #170800;box-shadow:0 3px 3px rgba(0,0,0,.1)}body.clockwork header.titlebar .statusicon{position:absolute;top:4px;left:12px;transition:color .5s}body.clockwork header.titlebar .title{position:absolute;top:6px;left:46px;color:#cfba47;font-size:16px;white-space:nowrap}body.clockwork header.titlebar .minimize{position:absolute;top:6px;right:46px}body.clockwork header.titlebar .close{position:absolute;top:4px;right:12px}body.nanotrasen{background:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2ZXJzaW9uPSIxLjAiIHZpZXdCb3g9IjAgMCA0MjUgMjAwIiBvcGFjaXR5PSIuMzMiPgogIDxwYXRoIGQ9Im0gMTc4LjAwMzk5LDAuMDM4NjkgLTcxLjIwMzkzLDAgYSA2Ljc2MTM0MjIsNi4wMjU1NDk1IDAgMCAwIC02Ljc2MTM0LDYuMDI1NTUgbCAwLDE4Ny44NzE0NyBhIDYuNzYxMzQyMiw2LjAyNTU0OTUgMCAwIDAgNi43NjEzNCw2LjAyNTU0IGwgNTMuMTA3MiwwIGEgNi43NjEzNDIyLDYuMDI1NTQ5NSAwIDAgMCA2Ljc2MTM1LC02LjAyNTU0IGwgMCwtMTAxLjU0NDAxOCA3Mi4yMTYyOCwxMDQuNjk5Mzk4IGEgNi43NjEzNDIyLDYuMDI1NTQ5NSAwIDAgMCA1Ljc2MDE1LDIuODcwMTYgbCA3My41NTQ4NywwIGEgNi43NjEzNDIyLDYuMDI1NTQ5NSAwIDAgMCA2Ljc2MTM1LC02LjAyNTU0IGwgMCwtMTg3Ljg3MTQ3IGEgNi43NjEzNDIyLDYuMDI1NTQ5NSAwIDAgMCAtNi43NjEzNSwtNi4wMjU1NSBsIC01NC43MTY0NCwwIGEgNi43NjEzNDIyLDYuMDI1NTQ5NSAwIDAgMCAtNi43NjEzMyw2LjAyNTU1IGwgMCwxMDIuNjE5MzUgTCAxODMuNzY0MTMsMi45MDg4NiBhIDYuNzYxMzQyMiw2LjAyNTU0OTUgMCAwIDAgLTUuNzYwMTQsLTIuODcwMTcgeiIgLz4KICA8cGF0aCBkPSJNIDQuODQ0NjMzMywyMi4xMDg3NSBBIDEzLjQxMjAzOSwxMi41MDE4NDIgMCAwIDEgMTMuNDc3NTg4LDAuMDM5MjQgbCA2Ni4xMTgzMTUsMCBhIDUuMzY0ODE1OCw1LjAwMDczNyAwIDAgMSA1LjM2NDgyMyw1LjAwMDczIGwgMCw3OS44NzkzMSB6IiAvPgogIDxwYXRoIGQ9Im0gNDIwLjE1NTM1LDE3Ny44OTExOSBhIDEzLjQxMjAzOCwxMi41MDE4NDIgMCAwIDEgLTguNjMyOTUsMjIuMDY5NTEgbCAtNjYuMTE4MzIsMCBhIDUuMzY0ODE1Miw1LjAwMDczNyAwIDAgMSAtNS4zNjQ4MiwtNS4wMDA3NCBsIDAsLTc5Ljg3OTMxIHoiIC8+Cjwvc3ZnPgo8IS0tIFRoaXMgd29yayBpcyBsaWNlbnNlZCB1bmRlciBhIENyZWF0aXZlIENvbW1vbnMgQXR0cmlidXRpb24tU2hhcmVBbGlrZSA0LjAgSW50ZXJuYXRpb25hbCBMaWNlbnNlLiAtLT4KPCEtLSBodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS1zYS80LjAvIC0tPgo=") no-repeat fixed 50%/70% 70%,linear-gradient(180deg,#2a2a2a 0,#202020);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff2a2a2a',endColorstr='#ff202020',GradientType=0)}body.nanotrasen .normal{color:#40628a}body.nanotrasen .good{color:#537d29}body.nanotrasen .average{color:#be6209}body.nanotrasen .bad{color:#b00e0e}body.nanotrasen .highlight{color:#8ba5c4}body.nanotrasen main{display:block;margin-top:32px;padding:2px 6px 0}body.nanotrasen hr{height:2px;background-color:#40628a;border:none}body.nanotrasen .hidden{display:none}body.nanotrasen .bar .barText,body.nanotrasen span.button{color:#fff;font-size:12px;font-weight:400;font-style:normal;text-decoration:none}body.nanotrasen .bold{font-weight:700}body.nanotrasen .italic{font-style:italic}body.nanotrasen [unselectable=on]{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}body.nanotrasen div[data-tooltip],body.nanotrasen span[data-tooltip]{position:relative}body.nanotrasen div[data-tooltip]:after,body.nanotrasen span[data-tooltip]:after{position:absolute;display:block;z-index:2;width:250px;padding:10px;-webkit-transform:translateX(-50%);-ms-transform:translateX(-50%);transform:translateX(-50%);visibility:hidden;opacity:0;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";white-space:normal;text-align:left;content:attr(data-tooltip);transition:all .5s;border:1px solid #272727;background-color:#363636}body.nanotrasen div[data-tooltip]:hover:after,body.nanotrasen span[data-tooltip]:hover:after{visibility:visible;opacity:1;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"}body.nanotrasen div[data-tooltip].tooltip-top:after,body.nanotrasen span[data-tooltip].tooltip-top:after{bottom:100%;left:50%;-webkit-transform:translateX(-50%) translateY(8px);-ms-transform:translateX(-50%) translateY(8px);transform:translateX(-50%) translateY(8px)}body.nanotrasen div[data-tooltip].tooltip-bottom:after,body.nanotrasen div[data-tooltip].tooltip-top:hover:after,body.nanotrasen span[data-tooltip].tooltip-bottom:after,body.nanotrasen span[data-tooltip].tooltip-top:hover:after{-webkit-transform:translateX(-50%) translateY(-8px);-ms-transform:translateX(-50%) translateY(-8px);transform:translateX(-50%) translateY(-8px)}body.nanotrasen div[data-tooltip].tooltip-bottom:after,body.nanotrasen span[data-tooltip].tooltip-bottom:after{top:100%;left:50%}body.nanotrasen div[data-tooltip].tooltip-bottom:hover:after,body.nanotrasen span[data-tooltip].tooltip-bottom:hover:after{-webkit-transform:translateX(-50%) translateY(8px);-ms-transform:translateX(-50%) translateY(8px);transform:translateX(-50%) translateY(8px)}body.nanotrasen div[data-tooltip].tooltip-left:after,body.nanotrasen span[data-tooltip].tooltip-left:after{top:50%;right:100%;-webkit-transform:translateX(8px) translateY(-50%);-ms-transform:translateX(8px) translateY(-50%);transform:translateX(8px) translateY(-50%)}body.nanotrasen div[data-tooltip].tooltip-left:hover:after,body.nanotrasen div[data-tooltip].tooltip-right:after,body.nanotrasen span[data-tooltip].tooltip-left:hover:after,body.nanotrasen span[data-tooltip].tooltip-right:after{-webkit-transform:translateX(-8px) translateY(-50%);-ms-transform:translateX(-8px) translateY(-50%);transform:translateX(-8px) translateY(-50%)}body.nanotrasen div[data-tooltip].tooltip-right:after,body.nanotrasen span[data-tooltip].tooltip-right:after{top:50%;left:100%}body.nanotrasen div[data-tooltip].tooltip-right:hover:after,body.nanotrasen span[data-tooltip].tooltip-right:hover:after{-webkit-transform:translateX(8px) translateY(-50%);-ms-transform:translateX(8px) translateY(-50%);transform:translateX(8px) translateY(-50%)}body.nanotrasen .bar{display:inline-block;position:relative;vertical-align:middle;width:100%;height:20px;line-height:17px;padding:1px;border:1px solid #40628a;background:#272727}body.nanotrasen .bar .barText{position:absolute;top:0;right:3px}body.nanotrasen .bar .barFill{display:block;height:100%;transition:background-color 1s;background-color:#40628a}body.nanotrasen .bar .barFill.good{background-color:#537d29}body.nanotrasen .bar .barFill.average{background-color:#be6209}body.nanotrasen .bar .barFill.bad{background-color:#b00e0e}body.nanotrasen span.button{display:inline-block;vertical-align:middle;min-height:20px;line-height:17px;padding:0 5px;white-space:nowrap;border:1px solid #272727}body.nanotrasen span.button .fa{padding-right:2px}body.nanotrasen span.button.normal{transition:background-color .5s;background-color:#40628a}body.nanotrasen span.button.normal.active:focus,body.nanotrasen span.button.normal.active:hover{transition:background-color .25s;background-color:#4f78aa;outline:0}body.nanotrasen span.button.disabled{transition:background-color .5s;background-color:#999}body.nanotrasen span.button.disabled.active:focus,body.nanotrasen span.button.disabled.active:hover{transition:background-color .25s;background-color:#a8a8a8;outline:0}body.nanotrasen span.button.selected{transition:background-color .5s;background-color:#2f943c}body.nanotrasen span.button.selected.active:focus,body.nanotrasen span.button.selected.active:hover{transition:background-color .25s;background-color:#3ab84b;outline:0}body.nanotrasen span.button.toggle{transition:background-color .5s;background-color:#2f943c}body.nanotrasen span.button.toggle.active:focus,body.nanotrasen span.button.toggle.active:hover{transition:background-color .25s;background-color:#3ab84b;outline:0}body.nanotrasen span.button.caution{transition:background-color .5s;background-color:#9a9d00}body.nanotrasen span.button.caution.active:focus,body.nanotrasen span.button.caution.active:hover{transition:background-color .25s;background-color:#ced200;outline:0}body.nanotrasen span.button.danger{transition:background-color .5s;background-color:#9d0808}body.nanotrasen span.button.danger.active:focus,body.nanotrasen span.button.danger.active:hover{transition:background-color .25s;background-color:#ce0b0b;outline:0}body.nanotrasen span.button.gridable{width:125px;margin:2px 0}body.nanotrasen span.button+span:not(.button),body.nanotrasen span:not(.button)+span.button{margin-left:5px}body.nanotrasen div.display{width:100%;padding:4px;margin:6px 0;background-color:#000;-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorStr=#54000000,endColorStr=#54000000)";filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#54000000,endColorStr=#54000000);background-color:rgba(0,0,0,.33);box-shadow:inset 0 0 5px rgba(0,0,0,.5)}body.nanotrasen div.display header,body.nanotrasen div.subdisplay header{display:block;position:relative;width:100%;padding:0 4px;margin-bottom:6px;color:#fff;border-bottom:2px solid #40628a}body.nanotrasen div.display header .buttonRight,body.nanotrasen div.subdisplay header .buttonRight{position:absolute;bottom:6px;right:4px}body.nanotrasen div.display article,body.nanotrasen div.subdisplay article{display:table;width:100%;border-collapse:collapse}body.nanotrasen input{display:inline-block;vertical-align:middle;height:20px;line-height:17px;padding:0 5px;white-space:nowrap;color:#000;background-color:#fff;border:1px solid #272727}body.nanotrasen input::-webkit-input-placeholder{color:#999}body.nanotrasen input::-moz-placeholder{color:#999}body.nanotrasen input:-ms-input-placeholder{color:#999}body.nanotrasen input::placeholder{color:#999}body.nanotrasen input::-ms-clear{display:none}body.nanotrasen svg.linegraph{overflow:hidden}body.nanotrasen div.notice{margin:8px 0;padding:4px;box-shadow:none;color:#000;font-weight:700;font-style:italic;background-color:#bb9b68;background-image:repeating-linear-gradient(-45deg,#bb9b68,#bb9b68 10px,#b1905d 0,#b1905d 20px)}body.nanotrasen div.notice .label{color:#000}body.nanotrasen div.notice .content:only-of-type{padding:0}body.nanotrasen div.notice hr{background-color:#272727}body.nanotrasen div.resize{position:fixed;bottom:0;right:0;width:0;height:0;border-style:solid;border-width:0 0 45px 45px;border-color:transparent transparent #363636;-webkit-transform:rotate(1turn);-ms-transform:rotate(1turn);transform:rotate(1turn)}body.nanotrasen section .content,body.nanotrasen section .label,body.nanotrasen section .line,body.syndicate section .content,body.syndicate section .label,body.syndicate section .line{display:table-cell;margin:0;text-align:left;vertical-align:middle;padding:3px 2px}body.nanotrasen section{display:table-row;width:100%}body.nanotrasen section:not(:first-child){padding-top:4px}body.nanotrasen section.candystripe:nth-child(even){background-color:#000;-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorStr=#33000000,endColorStr=#33000000)";filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#33000000,endColorStr=#33000000);background-color:rgba(0,0,0,.2)}body.nanotrasen section .label{width:1%;padding-right:32px;white-space:nowrap;color:#8ba5c4}body.nanotrasen section .content:not(:last-child){padding-right:16px}body.nanotrasen section .line{width:100%}body.nanotrasen div.subdisplay{width:100%;margin:0}body.nanotrasen header.titlebar .close,body.nanotrasen header.titlebar .minimize{display:inline-block;position:relative;padding:7px;margin:-7px;color:#8ba5c4}body.nanotrasen header.titlebar .close:hover,body.nanotrasen header.titlebar .minimize:hover{color:#9cb2cd}body.nanotrasen header.titlebar{position:fixed;z-index:1;top:0;left:0;width:100%;height:32px;background-color:#363636;border-bottom:1px solid #161616;box-shadow:0 3px 3px rgba(0,0,0,.1)}body.nanotrasen header.titlebar .statusicon{position:absolute;top:4px;left:12px;transition:color .5s}body.nanotrasen header.titlebar .title{position:absolute;top:6px;left:46px;color:#8ba5c4;font-size:16px;white-space:nowrap}body.nanotrasen header.titlebar .minimize{position:absolute;top:6px;right:46px}body.nanotrasen header.titlebar .close{position:absolute;top:4px;right:12px}body.syndicate{background:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2ZXJzaW9uPSIxLjAiIHZpZXdCb3g9IjAgMCAyMDAgMjg5Ljc0MiIgb3BhY2l0eT0iLjMzIj4KICA8cGF0aCBkPSJtIDkzLjUzNzY3NywwIGMgLTE4LjExMzEyNSwwIC0zNC4yMjAxMzMsMy4xMTE2NCAtNDguMzIzNDg0LDkuMzM0MzcgLTEzLjk2NTA5Miw2LjIyMTY3IC0yNC42MTI0NDIsMTUuMDcxMTQgLTMxLjk0MDY1MSwyNi41NDcxIC03LjE4OTkzOTgsMTEuMzM3ODkgLTEwLjMwMTIyNjYsMjQuNzQ5MTEgLTEwLjMwMTIyNjYsNDAuMjM0NzggMCwxMC42NDY2MiAyLjcyNTAwMjYsMjAuNDY0NjUgOC4xNzUxMTE2LDI5LjQ1MjU4IDUuNjE1Mjc3LDguOTg2ODYgMTQuMDM4Mjc3LDE3LjM1MjA0IDI1LjI2ODgyMSwyNS4wOTQzNiAxMS4yMzA1NDQsNy42MDUzMSAyNi41MDc0MjEsMTUuNDE4MzUgNDUuODMwNTE0LDIzLjQzNzgyIDE5Ljk4Mzc0OCw4LjI5NTU3IDM0Ljg0ODg0OCwxNS41NTQ3MSA0NC41OTI5OTgsMjEuNzc2MzggOS43NDQxNCw2LjIyMjczIDE2Ljc2MTcsMTIuODU4NSAyMS4wNTU3MiwxOS45MDk1MSA0LjI5NDA0LDcuMDUyMDggNi40NDE5MywxNS43NjQwOCA2LjQ0MTkzLDI2LjEzNDU5IDAsMTYuMTc3MDIgLTUuMjAxOTYsMjguNDgyMjIgLTE1LjYwNjczLDM2LjkxNjgyIC0xMC4yMzk2LDguNDM0NyAtMjUuMDIyMDMsMTIuNjUyMyAtNDQuMzQ1MTY5LDEyLjY1MjMgLTE0LjAzODE3MSwwIC0yNS41MTUyNDcsLTEuNjU5NCAtMzQuNDMzNjE4LC00Ljk3NzcgLTguOTE4MzcsLTMuNDU2NiAtMTYuMTg1NTcyLC04LjcxMTMgLTIxLjgwMDgzOSwtMTUuNzYzMyAtNS42MTUyNzcsLTcuMDUyMSAtMTAuMDc0Nzk1LC0xNi42NjA4OCAtMTMuMzc3ODk5LC0yOC44MjgxMiBsIC0yNC43NzMxNjI2MjkzOTQ1LDAgMCw1Ni44MjYzMiBDIDMzLjg1Njc2OSwyODYuMDc2MDEgNjMuNzQ5MDQsMjg5Ljc0MjAxIDg5LjY3ODM4MywyODkuNzQyMDEgYyAxNi4wMjAwMjcsMCAzMC43MTk3ODcsLTEuMzgyNyA0NC4wOTczMzcsLTQuMTQ3OSAxMy41NDI3MiwtMi45MDQzIDI1LjEwNDEsLTcuNDY3NiAzNC42ODMwOSwtMTMuNjg5MyA5Ljc0NDEzLC02LjM1OTcgMTcuMzQwNDIsLTE0LjUxOTUgMjIuNzkwNTIsLTI0LjQ3NDggNS40NTAxLC0xMC4wOTMzMiA4LjE3NTExLC0yMi4zOTk1OSA4LjE3NTExLC0zNi45MTY4MiAwLC0xMi45OTc2NCAtMy4zMDIxLC0yNC4zMzUzOSAtOS45MDgyOSwtMzQuMDE0NiAtNi40NDEwNSwtOS44MTcyNSAtMTUuNTI1NDUsLTE4LjUyNzA3IC0yNy4yNTE0NiwtMjYuMTMxMzMgLTExLjU2MDg1LC03LjYwNDI3IC0yNy45MTA4MywtMTUuODMxNDIgLTQ5LjA1MDY2LC0yNC42ODAyMiAtMTcuNTA2NDQsLTcuMTkwMTIgLTMwLjcxOTY2OCwtMTMuNjg5NDggLTM5LjYzODAzOCwtMTkuNDk3MDEgLTguOTE4MzcxLC01LjgwNzUyIC0xOC42MDc0NzQsLTEyLjQzNDA5IC0yNC4wOTY1MjQsLTE4Ljg3NDE3IC01LjQyNjA0MywtNi4zNjYxNiAtOS42NTg4MjYsLTE1LjA3MDAzIC05LjY1ODgyNiwtMjQuODg3MjkgMCwtOS4yNjQwMSAyLjA3NTQxNCwtMTcuMjEzNDUgNi4yMjM0NTQsLTIzLjg1MDMzIDExLjA5ODI5OCwtMTQuMzk3NDggNDEuMjg2NjM4LC0xLjc5NTA3IDQ1LjA3NTYwOSwyNC4zNDc2MiA0LjgzOTM5Miw2Ljc3NDkxIDguODQ5MzUsMTYuMjQ3MjkgMTIuMDI5NTE1LDI4LjQxNTYgbCAyMC41MzIzNCwwIDAsLTU1Ljk5OTY3IGMgLTQuNDc4MjUsLTUuOTI0NDggLTkuOTU0ODgsLTEwLjYzMjIyIC0xNS45MDgzNywtMTQuMzc0MTEgMS42NDA1NSwwLjQ3OTA1IDMuMTkwMzksMS4wMjM3NiA0LjYzODY1LDEuNjQwMjQgNi40OTg2MSwyLjYyNjA3IDEyLjE2NzkzLDcuMzI3NDcgMTcuMDA3MywxNC4xMDM0NSA0LjgzOTM5LDYuNzc0OTEgOC44NDkzNSwxNi4yNDU2NyAxMi4wMjk1MiwyOC40MTM5NyAwLDAgOC40ODEyOCwtMC4xMjg5NCA4LjQ4OTc4LC0wLjAwMiAwLjQxNzc2LDYuNDE0OTQgLTEuNzUzMzksOS40NTI4NiAtNC4xMjM0MiwxMi41NjEwNCAtMi40MTc0LDMuMTY5NzggLTUuMTQ0ODYsNi43ODk3MyAtNC4wMDI3OCwxMy4wMDI5IDEuNTA3ODYsOC4yMDMxOCAxMC4xODM1NCwxMC41OTY0MiAxNC42MjE5NCw5LjMxMTU0IC0zLjMxODQyLC0wLjQ5OTExIC01LjMxODU1LC0xLjc0OTQ4IC01LjMxODU1LC0xLjc0OTQ4IDAsMCAxLjg3NjQ2LDAuOTk4NjggNS42NTExNywtMS4zNTk4MSAtMy4yNzY5NSwwLjk1NTcxIC0xMC43MDUyOSwtMC43OTczOCAtMTEuODAxMjUsLTYuNzYzMTMgLTAuOTU3NTIsLTUuMjA4NjEgMC45NDY1NCwtNy4yOTUxNCAzLjQwMTEzLC0xMC41MTQ4MiAyLjQ1NDYyLC0zLjIxOTY4IDUuMjg0MjYsLTYuOTU4MzEgNC42ODQzLC0xNC40ODgyNCBsIDAuMDAzLDAuMDAyIDguOTI2NzYsMCAwLC01NS45OTk2NyBjIC0xNS4wNzEyNSwtMy44NzE2OCAtMjcuNjUzMTQsLTYuMzYwNDIgLTM3Ljc0NjcxLC03LjQ2NTg2IC05Ljk1NTMxLC0xLjEwNzU1IC0yMC4xODgyMywtMS42NTk4MSAtMzAuNjk2NjEzLC0xLjY1OTgxIHogbSA3MC4zMjE2MDMsMTcuMzA4OTMgMC4yMzgwNSw0MC4zMDQ5IGMgMS4zMTgwOCwxLjIyNjY2IDIuNDM5NjUsMi4yNzgxNSAzLjM0MDgxLDMuMTA2MDIgNC44MzkzOSw2Ljc3NDkxIDguODQ5MzQsMTYuMjQ1NjYgMTIuMDI5NTEsMjguNDEzOTcgbCAyMC41MzIzNCwwIDAsLTU1Ljk5OTY3IGMgLTYuNjc3MzEsLTQuNTkzODEgLTE5LjgzNjQzLC0xMC40NzMwOSAtMzYuMTQwNzEsLTE1LjgyNTIyIHogbSAtMjguMTIwNDksNS42MDU1MSA4LjU2NDc5LDE3LjcxNjU1IGMgLTExLjk3MDM3LC02LjQ2Njk3IC0xMy44NDY3OCwtOS43MTcyNiAtOC41NjQ3OSwtMTcuNzE2NTUgeiBtIDIyLjc5NzA1LDAgYyAyLjc3MTUsNy45OTkyOSAxLjc4NzQxLDExLjI0OTU4IC00LjQ5MzU0LDE3LjcxNjU1IGwgNC40OTM1NCwtMTcuNzE2NTUgeiBtIDE1LjIyMTk1LDI0LjAwODQ4IDguNTY0NzksMTcuNzE2NTUgYyAtMTEuOTcwMzgsLTYuNDY2OTcgLTEzLjg0Njc5LC05LjcxNzI2IC04LjU2NDc5LC0xNy43MTY1NSB6IG0gMjIuNzk3MDQsMCBjIDIuNzcxNSw3Ljk5OTI5IDEuNzg3NDEsMTEuMjQ5NTggLTQuNDkzNTQsMTcuNzE2NTUgbCA0LjQ5MzU0LC0xNy43MTY1NSB6IG0gLTk5LjExMzg0LDIuMjA3NjQgOC41NjQ3OSwxNy43MTY1NSBjIC0xMS45NzAzODIsLTYuNDY2OTcgLTEzLjg0Njc4MiwtOS43MTcyNiAtOC41NjQ3OSwtMTcuNzE2NTUgeiBtIDIyLjc5NTQyLDAgYyAyLjc3MTUsNy45OTkyOSAxLjc4NzQxLDExLjI0OTU4IC00LjQ5MzU0LDE3LjcxNjU1IGwgNC40OTM1NCwtMTcuNzE2NTUgeiIgLz4KPC9zdmc+CjwhLS0gVGhpcyB3b3JrIGlzIGxpY2Vuc2VkIHVuZGVyIGEgQ3JlYXRpdmUgQ29tbW9ucyBBdHRyaWJ1dGlvbi1TaGFyZUFsaWtlIDQuMCBJbnRlcm5hdGlvbmFsIExpY2Vuc2UuIC0tPgo8IS0tIGh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LXNhLzQuMC8gLS0+Cg==") no-repeat fixed 50%/70% 70%,linear-gradient(180deg,#750000 0,#340404);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff750000',endColorstr='#ff340404',GradientType=0)}body.syndicate .normal{color:#40628a}body.syndicate .good{color:#73e573}body.syndicate .average{color:#be6209}body.syndicate .bad{color:#b00e0e}body.syndicate .highlight{color:#000}body.syndicate main{display:block;margin-top:32px;padding:2px 6px 0}body.syndicate hr{height:2px;background-color:#272727;border:none}body.syndicate .hidden{display:none}body.syndicate .bar .barText,body.syndicate span.button{color:#fff;font-size:12px;font-weight:400;font-style:normal;text-decoration:none}body.syndicate .bold{font-weight:700}body.syndicate .italic{font-style:italic}body.syndicate [unselectable=on]{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}body.syndicate div[data-tooltip],body.syndicate span[data-tooltip]{position:relative}body.syndicate div[data-tooltip]:after,body.syndicate span[data-tooltip]:after{position:absolute;display:block;z-index:2;width:250px;padding:10px;-webkit-transform:translateX(-50%);-ms-transform:translateX(-50%);transform:translateX(-50%);visibility:hidden;opacity:0;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";white-space:normal;text-align:left;content:attr(data-tooltip);transition:all .5s;border:1px solid #272727;background-color:#363636}body.syndicate div[data-tooltip]:hover:after,body.syndicate span[data-tooltip]:hover:after{visibility:visible;opacity:1;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"}body.syndicate div[data-tooltip].tooltip-top:after,body.syndicate span[data-tooltip].tooltip-top:after{bottom:100%;left:50%;-webkit-transform:translateX(-50%) translateY(8px);-ms-transform:translateX(-50%) translateY(8px);transform:translateX(-50%) translateY(8px)}body.syndicate div[data-tooltip].tooltip-bottom:after,body.syndicate div[data-tooltip].tooltip-top:hover:after,body.syndicate span[data-tooltip].tooltip-bottom:after,body.syndicate span[data-tooltip].tooltip-top:hover:after{-webkit-transform:translateX(-50%) translateY(-8px);-ms-transform:translateX(-50%) translateY(-8px);transform:translateX(-50%) translateY(-8px)}body.syndicate div[data-tooltip].tooltip-bottom:after,body.syndicate span[data-tooltip].tooltip-bottom:after{top:100%;left:50%}body.syndicate div[data-tooltip].tooltip-bottom:hover:after,body.syndicate span[data-tooltip].tooltip-bottom:hover:after{-webkit-transform:translateX(-50%) translateY(8px);-ms-transform:translateX(-50%) translateY(8px);transform:translateX(-50%) translateY(8px)}body.syndicate div[data-tooltip].tooltip-left:after,body.syndicate span[data-tooltip].tooltip-left:after{top:50%;right:100%;-webkit-transform:translateX(8px) translateY(-50%);-ms-transform:translateX(8px) translateY(-50%);transform:translateX(8px) translateY(-50%)}body.syndicate div[data-tooltip].tooltip-left:hover:after,body.syndicate div[data-tooltip].tooltip-right:after,body.syndicate span[data-tooltip].tooltip-left:hover:after,body.syndicate span[data-tooltip].tooltip-right:after{-webkit-transform:translateX(-8px) translateY(-50%);-ms-transform:translateX(-8px) translateY(-50%);transform:translateX(-8px) translateY(-50%)}body.syndicate div[data-tooltip].tooltip-right:after,body.syndicate span[data-tooltip].tooltip-right:after{top:50%;left:100%}body.syndicate div[data-tooltip].tooltip-right:hover:after,body.syndicate span[data-tooltip].tooltip-right:hover:after{-webkit-transform:translateX(8px) translateY(-50%);-ms-transform:translateX(8px) translateY(-50%);transform:translateX(8px) translateY(-50%)}body.syndicate .bar{display:inline-block;position:relative;vertical-align:middle;width:100%;height:20px;line-height:17px;padding:1px;border:1px solid #000;background:#272727}body.syndicate .bar .barText{position:absolute;top:0;right:3px}body.syndicate .bar .barFill{display:block;height:100%;transition:background-color 1s;background-color:#000}body.syndicate .bar .barFill.good{background-color:#73e573}body.syndicate .bar .barFill.average{background-color:#be6209}body.syndicate .bar .barFill.bad{background-color:#b00e0e}body.syndicate span.button{display:inline-block;vertical-align:middle;min-height:20px;line-height:17px;padding:0 5px;white-space:nowrap;border:1px solid #272727}body.syndicate span.button .fa{padding-right:2px}body.syndicate span.button.normal{transition:background-color .5s;background-color:#397439}body.syndicate span.button.normal.active:focus,body.syndicate span.button.normal.active:hover{transition:background-color .25s;background-color:#4a964a;outline:0}body.syndicate span.button.disabled{transition:background-color .5s;background-color:#363636}body.syndicate span.button.disabled.active:focus,body.syndicate span.button.disabled.active:hover{transition:background-color .25s;background-color:#545454;outline:0}body.syndicate span.button.selected{transition:background-color .5s;background-color:#9d0808}body.syndicate span.button.selected.active:focus,body.syndicate span.button.selected.active:hover{transition:background-color .25s;background-color:#ce0b0b;outline:0}body.syndicate span.button.toggle{transition:background-color .5s;background-color:#9d0808}body.syndicate span.button.toggle.active:focus,body.syndicate span.button.toggle.active:hover{transition:background-color .25s;background-color:#ce0b0b;outline:0}body.syndicate span.button.caution{transition:background-color .5s;background-color:#be6209}body.syndicate span.button.caution.active:focus,body.syndicate span.button.caution.active:hover{transition:background-color .25s;background-color:#eb790b;outline:0}body.syndicate span.button.danger{transition:background-color .5s;background-color:#9a9d00}body.syndicate span.button.danger.active:focus,body.syndicate span.button.danger.active:hover{transition:background-color .25s;background-color:#ced200;outline:0}body.syndicate span.button.gridable{width:125px;margin:2px 0}body.syndicate span.button+span:not(.button),body.syndicate span:not(.button)+span.button{margin-left:5px}body.syndicate div.display{width:100%;padding:4px;margin:6px 0;background-color:#000;-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorStr=#80000000,endColorStr=#80000000)";filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#80000000,endColorStr=#80000000);background-color:rgba(0,0,0,.5);box-shadow:inset 0 0 5px rgba(0,0,0,.75)}body.syndicate div.display header,body.syndicate div.subdisplay header{display:block;position:relative;width:100%;padding:0 4px;margin-bottom:6px;color:#fff;border-bottom:2px solid #272727}body.syndicate div.display header .buttonRight,body.syndicate div.subdisplay header .buttonRight{position:absolute;bottom:6px;right:4px}body.syndicate div.display article,body.syndicate div.subdisplay article{display:table;width:100%;border-collapse:collapse}body.syndicate input{display:inline-block;vertical-align:middle;height:20px;line-height:17px;padding:0 5px;white-space:nowrap;color:#fff;background-color:#9d0808;border:1px solid #272727}body.syndicate input::-webkit-input-placeholder{color:#999}body.syndicate input::-moz-placeholder{color:#999}body.syndicate input:-ms-input-placeholder{color:#999}body.syndicate input::placeholder{color:#999}body.syndicate input::-ms-clear{display:none}body.syndicate svg.linegraph{overflow:hidden}body.syndicate div.notice{margin:8px 0;padding:4px;box-shadow:none;color:#000;font-weight:700;font-style:italic;background-color:#750000;background-image:repeating-linear-gradient(-45deg,#750000,#750000 10px,#910101 0,#910101 20px)}body.syndicate div.notice .label{color:#000}body.syndicate div.notice .content:only-of-type{padding:0}body.syndicate div.notice hr{background-color:#272727}body.syndicate div.resize{position:fixed;bottom:0;right:0;width:0;height:0;border-style:solid;border-width:0 0 45px 45px;border-color:transparent transparent #363636;-webkit-transform:rotate(1turn);-ms-transform:rotate(1turn);transform:rotate(1turn)}body.syndicate section .content,body.syndicate section .label,body.syndicate section .line{display:table-cell;margin:0;text-align:left;vertical-align:middle;padding:3px 2px}body.syndicate section{display:table-row;width:100%}body.syndicate section:not(:first-child){padding-top:4px}body.syndicate section.candystripe:nth-child(even){background-color:#000;-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorStr=#33000000,endColorStr=#33000000)";filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#33000000,endColorStr=#33000000);background-color:rgba(0,0,0,.2)}body.syndicate section .label{width:1%;padding-right:32px;white-space:nowrap;color:#fff}body.syndicate section .content:not(:last-child){padding-right:16px}body.syndicate section .line{width:100%}body.syndicate div.subdisplay{width:100%;margin:0}body.syndicate header.titlebar .close,body.syndicate header.titlebar .minimize{display:inline-block;position:relative;padding:7px;margin:-7px;color:#e74242}body.syndicate header.titlebar .close:hover,body.syndicate header.titlebar .minimize:hover{color:#eb5e5e}body.syndicate header.titlebar{position:fixed;z-index:1;top:0;left:0;width:100%;height:32px;background-color:#363636;border-bottom:1px solid #161616;box-shadow:0 3px 3px rgba(0,0,0,.1)}body.syndicate header.titlebar .statusicon{position:absolute;top:4px;left:12px;transition:color .5s}body.syndicate header.titlebar .title{position:absolute;top:6px;left:46px;color:#e74242;font-size:16px;white-space:nowrap}body.syndicate header.titlebar .minimize{position:absolute;top:6px;right:46px}body.syndicate header.titlebar .close{position:absolute;top:4px;right:12px}.no-icons header.titlebar .statusicon{font-size:20px}.no-icons header.titlebar .statusicon:after{content:"O"}.no-icons header.titlebar .minimize{top:-2px;font-size:20px}.no-icons header.titlebar .minimize:after{content:"—"}.no-icons header.titlebar .close{font-size:20px}.no-icons header.titlebar .close:after{content:"X"} \ No newline at end of file diff --git a/tgui/assets/tgui.js b/tgui/assets/tgui.js index 498ce97ea63..a6e58022815 100644 --- a/tgui/assets/tgui.js +++ b/tgui/assets/tgui.js @@ -1,16 +1,16 @@ -require=function t(e,n,a){function r(o,s){if(!n[o]){if(!e[o]){var u="function"==typeof require&&require;if(!s&&u)return u(o,!0);if(i)return i(o,!0);var p=Error("Cannot find module '"+o+"'");throw p.code="MODULE_NOT_FOUND",p}var c=n[o]={exports:{}};e[o][0].call(c.exports,function(t){var n=e[o][1][t];return r(n?n:t)},c,c.exports,t,e,n,a)}return n[o].exports}for(var i="function"==typeof require&&require,o=0;o2?p[2]:void 0,l=Math.min((void 0===c?o:r(c,o))-u,o-s),f=1;for(s>u&&u+l>s&&(f=-1,u+=l-1,s+=l-1);l-- >0;)u in n?n[s]=n[u]:delete n[s],s+=f,u+=f;return n}},{76:76,79:79,80:80}],6:[function(t,e,n){"use strict";var a=t(80),r=t(76),i=t(79);e.exports=[].fill||function(t){for(var e=a(this),n=i(e.length),o=arguments,s=o.length,u=r(s>1?o[1]:void 0,n),p=s>2?o[2]:void 0,c=void 0===p?n:r(p,n);c>u;)e[u++]=t;return e}},{76:76,79:79,80:80}],7:[function(t,e,n){var a=t(78),r=t(79),i=t(76);e.exports=function(t){return function(e,n,o){var s,u=a(e),p=r(u.length),c=i(o,p);if(t&&n!=n){for(;p>c;)if(s=u[c++],s!=s)return!0}else for(;p>c;c++)if((t||c in u)&&u[c]===n)return t||c;return!t&&-1}}},{76:76,78:78,79:79}],8:[function(t,e,n){var a=t(17),r=t(34),i=t(80),o=t(79),s=t(9);e.exports=function(t){var e=1==t,n=2==t,u=3==t,p=4==t,c=6==t,l=5==t||c;return function(f,d,h){for(var m,v,g=i(f),b=r(g),y=a(d,h,3),_=o(b.length),x=0,w=e?s(f,_):n?s(f,0):void 0;_>x;x++)if((l||x in b)&&(m=b[x],v=y(m,x,g),t))if(e)w[x]=v;else if(v)switch(t){case 3:return!0;case 5:return m;case 6:return x;case 2:w.push(m)}else if(p)return!1;return c?-1:u||p?p:w}}},{17:17,34:34,79:79,80:80,9:9}],9:[function(t,e,n){var a=t(38),r=t(36),i=t(83)("species");e.exports=function(t,e){var n;return r(t)&&(n=t.constructor,"function"!=typeof n||n!==Array&&!r(n.prototype)||(n=void 0),a(n)&&(n=n[i],null===n&&(n=void 0))),new(void 0===n?Array:n)(e)}},{36:36,38:38,83:83}],10:[function(t,e,n){var a=t(11),r=t(83)("toStringTag"),i="Arguments"==a(function(){return arguments}());e.exports=function(t){var e,n,o;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(n=(e=Object(t))[r])?n:i?a(e):"Object"==(o=a(e))&&"function"==typeof e.callee?"Arguments":o}},{11:11,83:83}],11:[function(t,e,n){var a={}.toString;e.exports=function(t){return a.call(t).slice(8,-1)}},{}],12:[function(t,e,n){"use strict";var a=t(46),r=t(31),i=t(60),o=t(17),s=t(69),u=t(18),p=t(27),c=t(42),l=t(44),f=t(82)("id"),d=t(30),h=t(38),m=t(65),v=t(19),g=Object.isExtensible||h,b=v?"_s":"size",y=0,_=function(t,e){if(!h(t))return"symbol"==typeof t?t:("string"==typeof t?"S":"P")+t;if(!d(t,f)){if(!g(t))return"F";if(!e)return"E";r(t,f,++y)}return"O"+t[f]},x=function(t,e){var n,a=_(e);if("F"!==a)return t._i[a];for(n=t._f;n;n=n.n)if(n.k==e)return n};e.exports={getConstructor:function(t,e,n,r){var c=t(function(t,i){s(t,c,e),t._i=a.create(null),t._f=void 0,t._l=void 0,t[b]=0,void 0!=i&&p(i,n,t[r],t)});return i(c.prototype,{clear:function(){for(var t=this,e=t._i,n=t._f;n;n=n.n)n.r=!0,n.p&&(n.p=n.p.n=void 0),delete e[n.i];t._f=t._l=void 0,t[b]=0},"delete":function(t){var e=this,n=x(e,t);if(n){var a=n.n,r=n.p;delete e._i[n.i],n.r=!0,r&&(r.n=a),a&&(a.p=r),e._f==n&&(e._f=a),e._l==n&&(e._l=r),e[b]--}return!!n},forEach:function(t){for(var e,n=o(t,arguments.length>1?arguments[1]:void 0,3);e=e?e.n:this._f;)for(n(e.v,e.k,this);e&&e.r;)e=e.p},has:function(t){return!!x(this,t)}}),v&&a.setDesc(c.prototype,"size",{get:function(){return u(this[b])}}),c},def:function(t,e,n){var a,r,i=x(t,e);return i?i.v=n:(t._l=i={i:r=_(e,!0),k:e,v:n,p:a=t._l,n:void 0,r:!1},t._f||(t._f=i),a&&(a.n=i),t[b]++,"F"!==r&&(t._i[r]=i)),t},getEntry:x,setStrong:function(t,e,n){c(t,e,function(t,e){this._t=t,this._k=e,this._l=void 0},function(){for(var t=this,e=t._k,n=t._l;n&&n.r;)n=n.p;return t._t&&(t._l=n=n?n.n:t._t._f)?"keys"==e?l(0,n.k):"values"==e?l(0,n.v):l(0,[n.k,n.v]):(t._t=void 0,l(1))},n?"entries":"values",!n,!0),m(e)}}},{17:17,18:18,19:19,27:27,30:30,31:31,38:38,42:42,44:44,46:46,60:60,65:65,69:69,82:82}],13:[function(t,e,n){var a=t(27),r=t(10);e.exports=function(t){return function(){if(r(this)!=t)throw TypeError(t+"#toJSON isn't generic");var e=[];return a(this,!1,e.push,e),e}}},{10:10,27:27}],14:[function(t,e,n){"use strict";var a=t(31),r=t(60),i=t(4),o=t(38),s=t(69),u=t(27),p=t(8),c=t(30),l=t(82)("weak"),f=Object.isExtensible||o,d=p(5),h=p(6),m=0,v=function(t){return t._l||(t._l=new g)},g=function(){this.a=[]},b=function(t,e){return d(t.a,function(t){return t[0]===e})};g.prototype={get:function(t){var e=b(this,t);return e?e[1]:void 0},has:function(t){return!!b(this,t)},set:function(t,e){var n=b(this,t);n?n[1]=e:this.a.push([t,e])},"delete":function(t){var e=h(this.a,function(e){return e[0]===t});return~e&&this.a.splice(e,1),!!~e}},e.exports={getConstructor:function(t,e,n,a){var i=t(function(t,r){s(t,i,e),t._i=m++,t._l=void 0,void 0!=r&&u(r,n,t[a],t)});return r(i.prototype,{"delete":function(t){return o(t)?f(t)?c(t,l)&&c(t[l],this._i)&&delete t[l][this._i]:v(this)["delete"](t):!1},has:function(t){return o(t)?f(t)?c(t,l)&&c(t[l],this._i):v(this).has(t):!1}}),i},def:function(t,e,n){return f(i(e))?(c(e,l)||a(e,l,{}),e[l][t._i]=n):v(t).set(e,n),t},frozenStore:v,WEAK:l}},{27:27,30:30,31:31,38:38,4:4,60:60,69:69,8:8,82:82}],15:[function(t,e,n){"use strict";var a=t(29),r=t(22),i=t(61),o=t(60),s=t(27),u=t(69),p=t(38),c=t(24),l=t(43),f=t(66);e.exports=function(t,e,n,d,h,m){var v=a[t],g=v,b=h?"set":"add",y=g&&g.prototype,_={},x=function(t){var e=y[t];i(y,t,"delete"==t?function(t){return m&&!p(t)?!1:e.call(this,0===t?0:t)}:"has"==t?function(t){return m&&!p(t)?!1:e.call(this,0===t?0:t)}:"get"==t?function(t){return m&&!p(t)?void 0:e.call(this,0===t?0:t)}:"add"==t?function(t){return e.call(this,0===t?0:t),this}:function(t,n){return e.call(this,0===t?0:t,n),this})};if("function"==typeof g&&(m||y.forEach&&!c(function(){(new g).entries().next()}))){var w,k=new g,P=k[b](m?{}:-0,1)!=k,C=c(function(){k.has(1)}),E=l(function(t){new g(t)});E||(g=e(function(e,n){u(e,g,t);var a=new v;return void 0!=n&&s(n,h,a[b],a),a}),g.prototype=y,y.constructor=g),m||k.forEach(function(t,e){w=1/e===-(1/0)}),(C||w)&&(x("delete"),x("has"),h&&x("get")),(w||P)&&x(b),m&&y.clear&&delete y.clear}else g=d.getConstructor(e,t,h,b),o(g.prototype,n);return f(g,t),_[t]=g,r(r.G+r.W+r.F*(g!=v),_),m||d.setStrong(g,t,h),g}},{22:22,24:24,27:27,29:29,38:38,43:43,60:60,61:61,66:66,69:69}],16:[function(t,e,n){var a=e.exports={version:"1.2.6"};"number"==typeof __e&&(__e=a)},{}],17:[function(t,e,n){var a=t(2);e.exports=function(t,e,n){if(a(t),void 0===e)return t;switch(n){case 1:return function(n){return t.call(e,n)};case 2:return function(n,a){return t.call(e,n,a)};case 3:return function(n,a,r){return t.call(e,n,a,r)}}return function(){return t.apply(e,arguments)}}},{2:2}],18:[function(t,e,n){e.exports=function(t){if(void 0==t)throw TypeError("Can't call method on "+t);return t}},{}],19:[function(t,e,n){e.exports=!t(24)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},{24:24}],20:[function(t,e,n){var a=t(38),r=t(29).document,i=a(r)&&a(r.createElement);e.exports=function(t){return i?r.createElement(t):{}}},{29:29,38:38}],21:[function(t,e,n){var a=t(46);e.exports=function(t){var e=a.getKeys(t),n=a.getSymbols;if(n)for(var r,i=n(t),o=a.isEnum,s=0;i.length>s;)o.call(t,r=i[s++])&&e.push(r);return e}},{46:46}],22:[function(t,e,n){var a=t(29),r=t(16),i=t(31),o=t(61),s=t(17),u="prototype",p=function(t,e,n){var c,l,f,d,h=t&p.F,m=t&p.G,v=t&p.S,g=t&p.P,b=t&p.B,y=m?a:v?a[e]||(a[e]={}):(a[e]||{})[u],_=m?r:r[e]||(r[e]={}),x=_[u]||(_[u]={});m&&(n=e);for(c in n)l=!h&&y&&c in y,f=(l?y:n)[c],d=b&&l?s(f,a):g&&"function"==typeof f?s(Function.call,f):f,y&&!l&&o(y,c,f),_[c]!=f&&i(_,c,d),g&&x[c]!=f&&(x[c]=f)};a.core=r,p.F=1,p.G=2,p.S=4,p.P=8,p.B=16,p.W=32,e.exports=p},{16:16,17:17,29:29,31:31,61:61}],23:[function(t,e,n){var a=t(83)("match");e.exports=function(t){var e=/./;try{"/./"[t](e)}catch(n){try{return e[a]=!1,!"/./"[t](e)}catch(r){}}return!0}},{83:83}],24:[function(t,e,n){e.exports=function(t){try{return!!t()}catch(e){return!0}}},{}],25:[function(t,e,n){"use strict";var a=t(31),r=t(61),i=t(24),o=t(18),s=t(83);e.exports=function(t,e,n){var u=s(t),p=""[t];i(function(){var e={};return e[u]=function(){return 7},7!=""[t](e)})&&(r(String.prototype,t,n(o,u,p)),a(RegExp.prototype,u,2==e?function(t,e){return p.call(t,this,e)}:function(t){return p.call(t,this)}))}},{18:18,24:24,31:31,61:61,83:83}],26:[function(t,e,n){"use strict";var a=t(4);e.exports=function(){var t=a(this),e="";return t.global&&(e+="g"),t.ignoreCase&&(e+="i"),t.multiline&&(e+="m"),t.unicode&&(e+="u"),t.sticky&&(e+="y"),e}},{4:4}],27:[function(t,e,n){var a=t(17),r=t(40),i=t(35),o=t(4),s=t(79),u=t(84);e.exports=function(t,e,n,p){var c,l,f,d=u(t),h=a(n,p,e?2:1),m=0;if("function"!=typeof d)throw TypeError(t+" is not iterable!");if(i(d))for(c=s(t.length);c>m;m++)e?h(o(l=t[m])[0],l[1]):h(t[m]);else for(f=d.call(t);!(l=f.next()).done;)r(f,h,l.value,e)}},{17:17,35:35,4:4,40:40,79:79,84:84}],28:[function(t,e,n){var a=t(78),r=t(46).getNames,i={}.toString,o="object"==typeof window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[],s=function(t){try{return r(t)}catch(e){return o.slice()}};e.exports.get=function(t){return o&&"[object Window]"==i.call(t)?s(t):r(a(t))}},{46:46,78:78}],29:[function(t,e,n){var a=e.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=a)},{}],30:[function(t,e,n){var a={}.hasOwnProperty;e.exports=function(t,e){return a.call(t,e)}},{}],31:[function(t,e,n){var a=t(46),r=t(59);e.exports=t(19)?function(t,e,n){return a.setDesc(t,e,r(1,n))}:function(t,e,n){return t[e]=n,t}},{19:19,46:46,59:59}],32:[function(t,e,n){e.exports=t(29).document&&document.documentElement},{29:29}],33:[function(t,e,n){e.exports=function(t,e,n){var a=void 0===n;switch(e.length){case 0:return a?t():t.call(n);case 1:return a?t(e[0]):t.call(n,e[0]);case 2:return a?t(e[0],e[1]):t.call(n,e[0],e[1]);case 3:return a?t(e[0],e[1],e[2]):t.call(n,e[0],e[1],e[2]);case 4:return a?t(e[0],e[1],e[2],e[3]):t.call(n,e[0],e[1],e[2],e[3])}return t.apply(n,e)}},{}],34:[function(t,e,n){var a=t(11);e.exports=Object("z").propertyIsEnumerable(0)?Object:function(t){return"String"==a(t)?t.split(""):Object(t)}},{11:11}],35:[function(t,e,n){var a=t(45),r=t(83)("iterator"),i=Array.prototype;e.exports=function(t){return void 0!==t&&(a.Array===t||i[r]===t)}},{45:45,83:83}],36:[function(t,e,n){var a=t(11);e.exports=Array.isArray||function(t){return"Array"==a(t)}},{11:11}],37:[function(t,e,n){var a=t(38),r=Math.floor;e.exports=function(t){return!a(t)&&isFinite(t)&&r(t)===t}},{38:38}],38:[function(t,e,n){e.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},{}],39:[function(t,e,n){var a=t(38),r=t(11),i=t(83)("match");e.exports=function(t){var e;return a(t)&&(void 0!==(e=t[i])?!!e:"RegExp"==r(t))}},{11:11,38:38,83:83}],40:[function(t,e,n){var a=t(4);e.exports=function(t,e,n,r){try{return r?e(a(n)[0],n[1]):e(n)}catch(i){var o=t["return"];throw void 0!==o&&a(o.call(t)),i}}},{4:4}],41:[function(t,e,n){"use strict";var a=t(46),r=t(59),i=t(66),o={};t(31)(o,t(83)("iterator"),function(){return this}),e.exports=function(t,e,n){t.prototype=a.create(o,{next:r(1,n)}),i(t,e+" Iterator")}},{31:31,46:46,59:59,66:66,83:83}],42:[function(t,e,n){"use strict";var a=t(48),r=t(22),i=t(61),o=t(31),s=t(30),u=t(45),p=t(41),c=t(66),l=t(46).getProto,f=t(83)("iterator"),d=!([].keys&&"next"in[].keys()),h="@@iterator",m="keys",v="values",g=function(){return this};e.exports=function(t,e,n,b,y,_,x){p(n,e,b);var w,k,P=function(t){if(!d&&t in A)return A[t];switch(t){case m:return function(){return new n(this,t)};case v:return function(){return new n(this,t)}}return function(){return new n(this,t)}},C=e+" Iterator",E=y==v,S=!1,A=t.prototype,O=A[f]||A[h]||y&&A[y],T=O||P(y);if(O){var M=l(T.call(new t));c(M,C,!0),!a&&s(A,h)&&o(M,f,g),E&&O.name!==v&&(S=!0,T=function(){return O.call(this)})}if(a&&!x||!d&&!S&&A[f]||o(A,f,T),u[e]=T,u[C]=g,y)if(w={values:E?T:P(v),keys:_?T:P(m),entries:E?P("entries"):T},x)for(k in w)k in A||i(A,k,w[k]);else r(r.P+r.F*(d||S),e,w);return w}},{22:22,30:30,31:31,41:41,45:45,46:46,48:48,61:61,66:66,83:83}],43:[function(t,e,n){var a=t(83)("iterator"),r=!1;try{var i=[7][a]();i["return"]=function(){r=!0},Array.from(i,function(){throw 2})}catch(o){}e.exports=function(t,e){if(!e&&!r)return!1;var n=!1;try{var i=[7],o=i[a]();o.next=function(){return{done:n=!0}},i[a]=function(){return o},t(i)}catch(s){}return n}},{83:83}],44:[function(t,e,n){e.exports=function(t,e){return{value:e,done:!!t}}},{}],45:[function(t,e,n){e.exports={}},{}],46:[function(t,e,n){var a=Object;e.exports={create:a.create,getProto:a.getPrototypeOf,isEnum:{}.propertyIsEnumerable,getDesc:a.getOwnPropertyDescriptor,setDesc:a.defineProperty,setDescs:a.defineProperties,getKeys:a.keys,getNames:a.getOwnPropertyNames,getSymbols:a.getOwnPropertySymbols,each:[].forEach}},{}],47:[function(t,e,n){var a=t(46),r=t(78);e.exports=function(t,e){for(var n,i=r(t),o=a.getKeys(i),s=o.length,u=0;s>u;)if(i[n=o[u++]]===e)return n}},{46:46,78:78}],48:[function(t,e,n){e.exports=!1},{}],49:[function(t,e,n){e.exports=Math.expm1||function(t){return 0==(t=+t)?t:t>-1e-6&&1e-6>t?t+t*t/2:Math.exp(t)-1}},{}],50:[function(t,e,n){e.exports=Math.log1p||function(t){return(t=+t)>-1e-8&&1e-8>t?t-t*t/2:Math.log(1+t)}},{}],51:[function(t,e,n){e.exports=Math.sign||function(t){return 0==(t=+t)||t!=t?t:0>t?-1:1}},{}],52:[function(t,e,n){var a,r,i,o=t(29),s=t(75).set,u=o.MutationObserver||o.WebKitMutationObserver,p=o.process,c=o.Promise,l="process"==t(11)(p),f=function(){var t,e,n;for(l&&(t=p.domain)&&(p.domain=null,t.exit());a;)e=a.domain,n=a.fn,e&&e.enter(),n(),e&&e.exit(),a=a.next;r=void 0,t&&t.enter()};if(l)i=function(){p.nextTick(f)};else if(u){var d=1,h=document.createTextNode("");new u(f).observe(h,{characterData:!0}),i=function(){h.data=d=-d}}else i=c&&c.resolve?function(){c.resolve().then(f)}:function(){s.call(o,f)};e.exports=function(t){var e={fn:t,next:void 0,domain:l&&p.domain};r&&(r.next=e),a||(a=e,i()),r=e}},{11:11,29:29,75:75}],53:[function(t,e,n){var a=t(46),r=t(80),i=t(34);e.exports=t(24)(function(){var t=Object.assign,e={},n={},a=Symbol(),r="abcdefghijklmnopqrst";return e[a]=7,r.split("").forEach(function(t){n[t]=t}),7!=t({},e)[a]||Object.keys(t({},n)).join("")!=r})?function(t,e){for(var n=r(t),o=arguments,s=o.length,u=1,p=a.getKeys,c=a.getSymbols,l=a.isEnum;s>u;)for(var f,d=i(o[u++]),h=c?p(d).concat(c(d)):p(d),m=h.length,v=0;m>v;)l.call(d,f=h[v++])&&(n[f]=d[f]);return n}:Object.assign},{24:24,34:34,46:46,80:80}],54:[function(t,e,n){var a=t(22),r=t(16),i=t(24);e.exports=function(t,e){var n=(r.Object||{})[t]||Object[t],o={};o[t]=e(n),a(a.S+a.F*i(function(){n(1)}),"Object",o)}},{16:16,22:22,24:24}],55:[function(t,e,n){var a=t(46),r=t(78),i=a.isEnum;e.exports=function(t){return function(e){for(var n,o=r(e),s=a.getKeys(o),u=s.length,p=0,c=[];u>p;)i.call(o,n=s[p++])&&c.push(t?[n,o[n]]:o[n]);return c}}},{46:46,78:78}],56:[function(t,e,n){var a=t(46),r=t(4),i=t(29).Reflect;e.exports=i&&i.ownKeys||function(t){var e=a.getNames(r(t)),n=a.getSymbols;return n?e.concat(n(t)):e}},{29:29,4:4,46:46}],57:[function(t,e,n){"use strict";var a=t(58),r=t(33),i=t(2);e.exports=function(){for(var t=i(this),e=arguments.length,n=Array(e),o=0,s=a._,u=!1;e>o;)(n[o]=arguments[o++])===s&&(u=!0);return function(){var a,i=this,o=arguments,p=o.length,c=0,l=0;if(!u&&!p)return r(t,n,i);if(a=n.slice(),u)for(;e>c;c++)a[c]===s&&(a[c]=o[l++]);for(;p>l;)a.push(o[l++]);return r(t,a,i)}}},{2:2,33:33,58:58}],58:[function(t,e,n){e.exports=t(29)},{29:29}],59:[function(t,e,n){e.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},{}],60:[function(t,e,n){var a=t(61);e.exports=function(t,e){for(var n in e)a(t,n,e[n]);return t}},{61:61}],61:[function(t,e,n){var a=t(29),r=t(31),i=t(82)("src"),o="toString",s=Function[o],u=(""+s).split(o);t(16).inspectSource=function(t){return s.call(t)},(e.exports=function(t,e,n,o){"function"==typeof n&&(n.hasOwnProperty(i)||r(n,i,t[e]?""+t[e]:u.join(e+"")),n.hasOwnProperty("name")||r(n,"name",e)),t===a?t[e]=n:(o||delete t[e],r(t,e,n))})(Function.prototype,o,function(){return"function"==typeof this&&this[i]||s.call(this)})},{16:16,29:29,31:31,82:82}],62:[function(t,e,n){e.exports=function(t,e){var n=e===Object(e)?function(t){return e[t]}:e;return function(e){return(e+"").replace(t,n)}}},{}],63:[function(t,e,n){e.exports=Object.is||function(t,e){return t===e?0!==t||1/t===1/e:t!=t&&e!=e}},{}],64:[function(t,e,n){var a=t(46).getDesc,r=t(38),i=t(4),o=function(t,e){if(i(t),!r(e)&&null!==e)throw TypeError(e+": can't set as prototype!")};e.exports={set:Object.setPrototypeOf||("__proto__"in{}?function(e,n,r){try{r=t(17)(Function.call,a(Object.prototype,"__proto__").set,2),r(e,[]),n=!(e instanceof Array)}catch(i){n=!0}return function(t,e){return o(t,e),n?t.__proto__=e:r(t,e),t}}({},!1):void 0),check:o}},{17:17,38:38,4:4,46:46}],65:[function(t,e,n){"use strict";var a=t(29),r=t(46),i=t(19),o=t(83)("species");e.exports=function(t){var e=a[t];i&&e&&!e[o]&&r.setDesc(e,o,{configurable:!0,get:function(){return this}})}},{19:19,29:29,46:46,83:83}],66:[function(t,e,n){var a=t(46).setDesc,r=t(30),i=t(83)("toStringTag");e.exports=function(t,e,n){t&&!r(t=n?t:t.prototype,i)&&a(t,i,{configurable:!0,value:e})}},{30:30,46:46,83:83}],67:[function(t,e,n){var a=t(29),r="__core-js_shared__",i=a[r]||(a[r]={});e.exports=function(t){return i[t]||(i[t]={})}},{29:29}],68:[function(t,e,n){var a=t(4),r=t(2),i=t(83)("species");e.exports=function(t,e){var n,o=a(t).constructor;return void 0===o||void 0==(n=a(o)[i])?e:r(n)}},{2:2,4:4,83:83}],69:[function(t,e,n){e.exports=function(t,e,n){if(!(t instanceof e))throw TypeError(n+": use the 'new' operator!");return t}},{}],70:[function(t,e,n){var a=t(77),r=t(18);e.exports=function(t){return function(e,n){var i,o,s=r(e)+"",u=a(n),p=s.length;return 0>u||u>=p?t?"":void 0:(i=s.charCodeAt(u),55296>i||i>56319||u+1===p||(o=s.charCodeAt(u+1))<56320||o>57343?t?s.charAt(u):i:t?s.slice(u,u+2):(i-55296<<10)+(o-56320)+65536)}}},{18:18,77:77}],71:[function(t,e,n){var a=t(39),r=t(18);e.exports=function(t,e,n){if(a(e))throw TypeError("String#"+n+" doesn't accept regex!");return r(t)+""}},{18:18,39:39}],72:[function(t,e,n){var a=t(79),r=t(73),i=t(18);e.exports=function(t,e,n,o){var s=i(t)+"",u=s.length,p=void 0===n?" ":n+"",c=a(e);if(u>=c)return s;""==p&&(p=" ");var l=c-u,f=r.call(p,Math.ceil(l/p.length));return f.length>l&&(f=f.slice(0,l)),o?f+s:s+f}},{18:18,73:73,79:79}],73:[function(t,e,n){"use strict";var a=t(77),r=t(18);e.exports=function(t){var e=r(this)+"",n="",i=a(t);if(0>i||i==1/0)throw RangeError("Count can't be negative");for(;i>0;(i>>>=1)&&(e+=e))1&i&&(n+=e);return n}},{18:18,77:77}],74:[function(t,e,n){var a=t(22),r=t(18),i=t(24),o=" \n\x0B\f\r   ᠎              \u2028\u2029\ufeff",s="["+o+"]",u="​…",p=RegExp("^"+s+s+"*"),c=RegExp(s+s+"*$"),l=function(t,e){var n={};n[t]=e(f),a(a.P+a.F*i(function(){return!!o[t]()||u[t]()!=u}),"String",n)},f=l.trim=function(t,e){return t=r(t)+"",1&e&&(t=t.replace(p,"")),2&e&&(t=t.replace(c,"")),t};e.exports=l},{18:18,22:22,24:24}],75:[function(t,e,n){var a,r,i,o=t(17),s=t(33),u=t(32),p=t(20),c=t(29),l=c.process,f=c.setImmediate,d=c.clearImmediate,h=c.MessageChannel,m=0,v={},g="onreadystatechange",b=function(){var t=+this;if(v.hasOwnProperty(t)){var e=v[t];delete v[t],e()}},y=function(t){b.call(t.data)};f&&d||(f=function(t){for(var e=[],n=1;arguments.length>n;)e.push(arguments[n++]);return v[++m]=function(){s("function"==typeof t?t:Function(t),e)},a(m),m},d=function(t){delete v[t]},"process"==t(11)(l)?a=function(t){l.nextTick(o(b,t,1))}:h?(r=new h,i=r.port2,r.port1.onmessage=y,a=o(i.postMessage,i,1)):c.addEventListener&&"function"==typeof postMessage&&!c.importScripts?(a=function(t){c.postMessage(t+"","*")},c.addEventListener("message",y,!1)):a=g in p("script")?function(t){u.appendChild(p("script"))[g]=function(){u.removeChild(this),b.call(t)}}:function(t){setTimeout(o(b,t,1),0)}),e.exports={set:f,clear:d}},{11:11,17:17,20:20,29:29,32:32,33:33}],76:[function(t,e,n){var a=t(77),r=Math.max,i=Math.min;e.exports=function(t,e){return t=a(t),0>t?r(t+e,0):i(t,e)}},{77:77}],77:[function(t,e,n){var a=Math.ceil,r=Math.floor;e.exports=function(t){return isNaN(t=+t)?0:(t>0?r:a)(t)}},{}],78:[function(t,e,n){var a=t(34),r=t(18);e.exports=function(t){return a(r(t))}},{18:18,34:34}],79:[function(t,e,n){var a=t(77),r=Math.min;e.exports=function(t){return t>0?r(a(t),9007199254740991):0}},{77:77}],80:[function(t,e,n){var a=t(18);e.exports=function(t){return Object(a(t))}},{18:18}],81:[function(t,e,n){var a=t(38);e.exports=function(t,e){if(!a(t))return t;var n,r;if(e&&"function"==typeof(n=t.toString)&&!a(r=n.call(t)))return r;if("function"==typeof(n=t.valueOf)&&!a(r=n.call(t)))return r;if(!e&&"function"==typeof(n=t.toString)&&!a(r=n.call(t)))return r;throw TypeError("Can't convert object to primitive value")}},{38:38}],82:[function(t,e,n){var a=0,r=Math.random();e.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++a+r).toString(36))}},{}],83:[function(t,e,n){var a=t(67)("wks"),r=t(82),i=t(29).Symbol;e.exports=function(t){return a[t]||(a[t]=i&&i[t]||(i||r)("Symbol."+t))}},{29:29,67:67,82:82}],84:[function(t,e,n){var a=t(10),r=t(83)("iterator"),i=t(45);e.exports=t(16).getIteratorMethod=function(t){return void 0!=t?t[r]||t["@@iterator"]||i[a(t)]:void 0}},{10:10,16:16,45:45,83:83}],85:[function(t,e,n){"use strict";var a,r=t(46),i=t(22),o=t(19),s=t(59),u=t(32),p=t(20),c=t(30),l=t(11),f=t(33),d=t(24),h=t(4),m=t(2),v=t(38),g=t(80),b=t(78),y=t(77),_=t(76),x=t(79),w=t(34),k=t(82)("__proto__"),P=t(8),C=t(7)(!1),E=Object.prototype,S=Array.prototype,A=S.slice,O=S.join,T=r.setDesc,M=r.getDesc,R=r.setDescs,j={};o||(a=!d(function(){return 7!=T(p("div"),"a",{get:function(){return 7}}).a}),r.setDesc=function(t,e,n){if(a)try{return T(t,e,n)}catch(r){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(h(t)[e]=n.value),t},r.getDesc=function(t,e){if(a)try{return M(t,e)}catch(n){}return c(t,e)?s(!E.propertyIsEnumerable.call(t,e),t[e]):void 0},r.setDescs=R=function(t,e){h(t);for(var n,a=r.getKeys(e),i=a.length,o=0;i>o;)r.setDesc(t,n=a[o++],e[n]);return t}),i(i.S+i.F*!o,"Object",{getOwnPropertyDescriptor:r.getDesc,defineProperty:r.setDesc,defineProperties:R});var L="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(","),N=L.concat("length","prototype"),D=L.length,F=function(){var t,e=p("iframe"),n=D,a=">";for(e.style.display="none",u.appendChild(e),e.src="javascript:",t=e.contentWindow.document,t.open(),t.write("