diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm index 88dca50becc6f..e9a88e2bea596 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 0000000000000..3f09f46817d5a --- /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 65dd14bf57b63..bd3752271ea5e 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 8ff099ef27eb1..5a6c1f3ab4364 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 5c2bd6a888780..640f6df10f9aa 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 e4fdb79bb8fac..91d5b62f64fee 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 e38d7050ef4d8..902aff9f6289c 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 1f004f31ac3ab..50541b9a60323 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 c26e54eae9678..abaafd386a949 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 e2bfe12874172..4791b7a77ee00 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 0879ec8f313c5..def88b7ed72c4 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 4f80adeab00de..f30c9e05aab00 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 6e0afd9bb7b4e..296a911770b51 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 60ee61e33da3b..d1fd0e46eeb65 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 db4cb5405805b..0f056e581db4f 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 616037c13e845..0c2871a26b025 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 db459b2aabdf7..03b293b13dc4d 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 3552211f73c4d..91c61b24f8391 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 e481731c7511c..84a097594b146 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 3ca0a042b52d4..fcb30a096da77 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 1c5a2dd5f8a61..505f068d36810 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 3538a8a557cb0..a5e7dee4cb970 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 5fbf329141cde..7f908756b9cc8 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 3cde9bcec7563..f424b225f04c5 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 3c597e7f325f4..000686fc5d8cf 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 851b47f3a2901..4542f7e610e98 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 ce80e490e10f1..73f5cc4c209fe 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 de94bbbc85add..af0a4940e70d0 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 17d849932d1e8..6a5a2da8c34f8 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 a583e579924e7..ee72082f1eac2 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 94d17a71812f1..566fe74f0f97a 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 1e04ee649f6b9..367a9896f01cd 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 12712211bb31e..877f80dbcb7ac 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 2039ead4c6828..f26e7cfe4d02d 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 0bb9f414ed38d..d7792ec314b7c 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 9ec72374407a0..b2fac45c4c835 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 51965b46a0509..85b1886e6c986 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 a5dfc478ae6d7..5d36d76de9718 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 74d3838a4dac4..08db2d3fab9b5 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 a2218658651ee..04c90fdf317a1 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 02dff1b445d10..0a4c310e75f6c 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 1366cf7435bfc..1cb400d694e98 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 ba146f90861c2..5230443c9ee12 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 6433f127aebc9..976678e443f0d 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 c7f221d85a0a8..ca558a9565ae5 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 f7c3cbf930188..d8c1ee69561e1 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 9c21c5b5a8e75..29e2455f206be 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 8dbd9bf0c887b..0b607e87e62e8 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 4516461cb51fb..a1a64f1f3bbe6 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 0000000000000..b6fb555729a78 --- /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 0000000000000..5128a35d524f7 --- /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 0000000000000..893ed13e93bdc --- /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 0000000000000..66bc9cb06ca39 --- /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 0000000000000..ea8597e5c213c --- /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 0000000000000..263d92b8265d0 --- /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 0000000000000..2319b40889260 --- /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 0000000000000..cc4c36e2ef71c --- /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 0000000000000..597f803775f5b --- /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 0000000000000..263c3d0a9e097 --- /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 ec71322d3c3dd..4d58f8207d46e 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 4cc3bf80c9f22..4aa19c9f1341a 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 c7ceff6111e70..7ae318c264a1a 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 100bec5cb9820..18372d5e3011b 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 089efe4444fb1..33c3d00d2ee80 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 483f907639008..6708f7c11e03c 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 632716e777685..5751c65b36468 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 3dd354050f974..43a909df807af 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 db366bfec9106..d41f949e22fee 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 7fe75dcd96cbb..1f9beb52bf7e0 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 45120f74fb0cf..4d5d1c26eb5bc 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 115a985710ddc..f392981ed4e18 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 156ac27da6676..920a0cade3b10 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 8b1e3b652981f..039b40b234135 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 abad6b4084e74..f808eb65ef14d 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 3caabec5ff915..3aa604649daba 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 35183bbe4a2ae..c6ff1ca9a03a8 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 41a097b6e02e6..4e97c5babd5a5 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 35bec8d669833..75d87d7323100 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 599eb71e5bb2f..60b8e3040ba78 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 59d901cb88bb4..b02b51e9696eb 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 6f6e449522f4a..5c50a87b4541a 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 5ea2dc2491e9d..2d8e36e7a616e 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 7b57090c1d178..d7633f4107c2d 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 de6bb7c827ec7..bc6d1d7fe04a3 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 9f746c0f5b550..e41d7f07e60de 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 9d8631e972aca..74c1f9621348e 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 abd9a7ec954d7..837258b5738d0 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 5a9c719d2343e..6fdb8f694ab5b 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 2ccd7a082650b..7468c8e21c089 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 afaf7718e0dae..3c45c5a8bda69 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 3585d989dc522..aec971fb37fcc 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 62600cc512f6c..b3e664c89d95f 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 25992568a2cd0..ef416c938fc58 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 8eb9382aaf08b..332f10d77572d 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 85e6e65f999d7..c6449c7b5a3fe 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 451977772f5e9..e50ef28fa6f63 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 9649e13029d1b..fbba77e9818a9 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 0e3daed388d9e..ac33e8c91323d 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 d039929d2fccf..19cd3b1cfd1af 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 4a7f30138b2b8..4c066919e84e0 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 0000000000000..e7dea65024a1b --- /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 c24aeccf5480b..3059ac422de2b 100644 Binary files a/icons/mob/actions.dmi and b/icons/mob/actions.dmi differ diff --git a/interface/stylesheet.dm b/interface/stylesheet.dm index de2fcf5fbe4c4..d3e518159f3c6 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 ad28e91fb1935..9320e6ce63a41 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 03651d3a6b61c..d52500b9d646f 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 498ce97ea6394..a6e5802281538 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("