From 1038df69833dada68ba5114e8c614357a645d4ed Mon Sep 17 00:00:00 2001 From: James Date: Sat, 19 Feb 2022 17:42:17 +0000 Subject: [PATCH 01/15] First page of panel basically done --- code/modules/admin/admin.dm | 2 - code/modules/admin/admin_verbs.dm | 3 +- code/modules/admin/player_panel2.dm | 198 +++++ code/modules/admin/topic.dm | 15 +- code/modules/admin/verbs/randomverbs.dm | 85 +- code/modules/mob/mob.dm | 8 + code/modules/mob/mob_defines.dm | 3 + code/modules/wiremod/core/admin_panel.dm | 3 +- config/entries/jexp.txt | 2 +- .../code/modules/mob/living/living.dm | 27 + .../code/modules/mob/living/living_defines.dm | 4 + tgstation.dme | 3 + tgui/packages/tgui/interfaces/PlayerPanel2.js | 793 ++++++++++++++++++ 13 files changed, 1085 insertions(+), 61 deletions(-) create mode 100644 code/modules/admin/player_panel2.dm create mode 100644 modular_splurt/code/modules/mob/living/living.dm create mode 100644 modular_splurt/code/modules/mob/living/living_defines.dm create mode 100644 tgui/packages/tgui/interfaces/PlayerPanel2.js diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index 6de1d3257c..d6c66f109b 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -75,8 +75,6 @@ body += "BP - " body += "PM - " body += "SM - " - if (ishuman(M) && M.mind) - body += "HM - " body += "FLW - " //Default to client logs if available var/source = LOGSRC_MOB diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 9d92d08385..ff41e68188 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -29,6 +29,7 @@ GLOBAL_PROTECT(admin_verbs_admin) /client/proc/invisimin, /*allows our mob to go invisible/visible*/ // /datum/admins/proc/show_traitor_panel, /*interface which shows a mob's mind*/ -Removed due to rare practical use. Moved to debug verbs ~Errorage /datum/admins/proc/show_player_panel, /*shows an interface for individual players, with various links (links require additional flags*/ + /datum/admins/proc/show_player_panel2, /*shows an interface for individual players, with various links (links require additional flags*/ /datum/verbs/menu/Admin/verb/playerpanel, /client/proc/game_panel, /*game panel, allows to change game-mode etc*/ /client/proc/check_ai_laws, /*shows AI and borg laws*/ @@ -46,7 +47,6 @@ GLOBAL_PROTECT(admin_verbs_admin) /client/proc/getserverlogs, /*for accessing server logs*/ /client/proc/getcurrentlogs, /*for accessing server logs for the current round*/ /client/proc/cmd_admin_subtle_message, /*send a message to somebody as a 'voice in their head'*/ - /client/proc/cmd_admin_headset_message, /*send a message to somebody through their headset as CentCom*/ /client/proc/cmd_admin_delete, /*delete an instance/object/mob/etc*/ /client/proc/cmd_admin_check_contents, /*displays the contents of an instance*/ /client/proc/centcom_podlauncher,/*Open a window to launch a Supplypod and configure it or it's contents*/ @@ -230,7 +230,6 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list( /client/proc/admin_ghost, /client/proc/toggle_view_range, /client/proc/cmd_admin_subtle_message, - /client/proc/cmd_admin_headset_message, /client/proc/cmd_admin_check_contents, /datum/admins/proc/access_news_network, /client/proc/admin_end_shift, diff --git a/code/modules/admin/player_panel2.dm b/code/modules/admin/player_panel2.dm new file mode 100644 index 0000000000..7dcc02e89b --- /dev/null +++ b/code/modules/admin/player_panel2.dm @@ -0,0 +1,198 @@ +GLOBAL_LIST_INIT(mute_bits, list( + list(name = "IC", bitflag = MUTE_IC), + list(name = "OOC", bitflag = MUTE_OOC), + list(name = "Pray", bitflag = MUTE_PRAY), + list(name = "Adminhelp", bitflag = MUTE_ADMINHELP), + list(name = "Deadchat", bitflag = MUTE_DEADCHAT) +)) + +/datum/admins/proc/show_player_panel2(mob/M) + if(!M) + to_chat(owner, "You seem to be selecting a mob that doesn't exist anymore.") + return + + // this is stupid, thanks byond + if(istype(src, /client)) + var/client/C = src + src = C.holder + + if(!check_rights()) + to_chat(owner, "Error: you are not an admin!") + return + + if(!M.mob_panel) + M.create_player_panel() + + M.mob_panel.ui_interact(owner.mob) + +/datum/player_panel + var/mob/targetMob + +/datum/player_panel/New(mob/target) + . = ..() + targetMob = target + +/datum/player_panel/Destroy(force, ...) + targetMob = null + + SStgui.close_uis(src) + return ..() + +/datum/player_panel/ui_interact(mob/user, datum/tgui/ui) + if(!targetMob) + return + + ui = SStgui.try_update_ui(user, src, ui) + if (!ui) + ui = new(user, src, "PlayerPanel2", "[targetMob.name] Player Panel") + ui.open() + +/datum/player_panel/ui_state(mob/user) + return GLOB.admin_state + +/datum/player_panel/ui_data(mob/user) + . = list() + .["mob_name"] = targetMob.name + .["current_permissions"] = user.client?.holder?.rank.rights + + .["mob_type"] = targetMob.type + + .["glob_mute_bits"] = GLOB.mute_bits + + var/mob/living/L = targetMob + if (istype(L)) + .["is_type_mob_living"] = TRUE + .["is_frozen"] = L.admin_frozen + .["is_slept"] = L.admin_sleeping + + if(targetMob.client) + var/client/targetClient = targetMob.client + .["client_key"] = targetClient.key + .["client_ckey"] = targetClient.ckey + .["client_muted"] = targetClient.prefs.muted + .["client_rank"] = targetClient.holder ? targetClient.holder.rank : "Player" + else + .["client_key"] = null + .["client_ckey"] = null + .["client_muted"] = null + .["client_rank"] = null + +/datum/player_panel/ui_static_data() + . = list() + + if(CONFIG_GET(flag/use_exp_tracking)) + .["playtimes_enabled"] = TRUE + +/datum/player_panel/ui_act(action, params, datum/tgui/ui) + if(..()) + return + + var/client/admin = ui.user.client + + to_chat(admin, "Easdasdasdadmin!") + + if (!check_rights(R_ADMIN)) + message_admins("WARNING: NON-ADMIN [ADMIN_LOOKUPFLW(user)] ACCESSING ADMIN PANEL. WARN Casper#3044.") + to_chat(admin, "Error: you are not an admin!") + return + + switch(action) + if ("edit_rank") + if (!targetMob.client?.ckey) + priority_announce("failed client ckey check") + return + + var/list/context = list() + + context["key"] = targetMob.client.ckey + + if (GLOB.admin_datums[targetMob.client.ckey] || GLOB.deadmins[targetMob.client.ckey]) + context["editrights"] = "rank" + else + context["editrights"] = "add" + + priority_announce("sending...") + admin.holder.edit_rights_topic(context) + + if ("access_variables") + admin.debug_variables(targetMob) + + if ("access_playtimes") + if (targetMob.client) + admin.holder.cmd_show_exp_panel(targetMob.client) + + if ("private_message") + admin.cmd_admin_pm_context(targetMob) + + if ("subtle_message") + admin.cmd_admin_subtle_message(targetMob) + + if ("set_name") + targetMob.vv_auto_rename(params["name"]) + + if ("heal") + admin.cmd_admin_rejuvenate(targetMob) + + if ("ghost") + if(targetMob.client) + log_admin("[key_name(admin)] ejected [key_name(targetMob)] from their body.") + message_admins("[key_name_admin(admin)] ejected [key_name_admin(targetMob)] from their body.") + to_chat(targetMob, "An admin has ejected you from your body.") + targetMob.ghostize(FALSE) + + if ("smite") + admin.smite(targetMob) + + if ("bring") + admin.Getmob(targetMob) + + if ("orbit") + if(!isobserver(admin.mob)) + admin.admin_ghost() + var/mob/dead/observer/O = admin.mob + O.ManualFollow(targetMob) + + if ("jump_to") + admin.jumptomob(targetMob) + + if ("freeze") + var/mob/living/L = targetMob + if (istype(L)) + L.toggle_admin_freeze(admin) + + if ("sleep") + var/mob/living/L = targetMob + if (istype(L)) + L.toggle_admin_sleep(admin) + + if ("lobby") + if(!isobserver(targetMob)) + to_chat(usr, "You can only send ghost players back to the Lobby.") + return + + if(!targetMob.client) + to_chat(usr, "[targetMob] doesn't seem to have an active client.") + return + + log_admin("[key_name(usr)] has sent [key_name(targetMob)] back to the Lobby.") + message_admins("[key_name(usr)] has sent [key_name(targetMob)] back to the Lobby.") + + var/mob/dead/new_player/NP = new() + NP.ckey = targetMob.ckey + qdel(targetMob) + if(GLOB.preferences_datums[NP.ckey]) + var/datum/preferences/P = GLOB.preferences_datums[NP.ckey] + P.respawn_restrictions_active = FALSE + + if ("force_say") + targetMob.say(params["to_say"], forced="admin") + + if ("force_emote") + targetMob.emote("me", EMOTE_VISIBLE, params["to_emote"]) + + if ("mute") + if(!targetMob.client) + return + + targetMob.client.prefs.muted = text2num(params["mute_flag"]) + log_admin("[key_name(admin)] set the mute flags for [key_name(targetMob)] to [targetMob.client.prefs.muted].") diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 0b52d0a7b8..d004c6ef36 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1798,7 +1798,8 @@ else if(href_list["adminplayeropts"]) var/mob/M = locate(href_list["adminplayeropts"]) - show_player_panel(M) + // show_player_panel(M) + show_player_panel2(M) else if(href_list["adminplayerobservefollow"]) if(!isobserver(usr) && !check_rights(R_ADMIN)) @@ -2025,21 +2026,22 @@ return var/mob/M = locate(href_list["CentComReply"]) - usr.client.admin_headset_message(M, RADIO_CHANNEL_CENTCOM) + usr.client.cmd_admin_subtle_message(M, RADIO_CHANNEL_CENTCOM) else if(href_list["SyndicateReply"]) if(!check_rights(R_ADMIN)) return var/mob/M = locate(href_list["SyndicateReply"]) - usr.client.admin_headset_message(M, RADIO_CHANNEL_SYNDICATE) + usr.client.cmd_admin_subtle_message(M, RADIO_CHANNEL_SYNDICATE) else if(href_list["HeadsetMessage"]) if(!check_rights(R_ADMIN)) return var/mob/M = locate(href_list["HeadsetMessage"]) - usr.client.admin_headset_message(M) + usr.client.cmd_admin_subtle_message(M) + //ambition start else if(href_list["ObjectiveRequest"]) if(!check_rights(R_ADMIN)) @@ -2099,6 +2101,8 @@ if(!check_rights(R_ADMIN)) return + priority_announce("what is this") + var/mob/M = locate(href_list["subtlemessage"]) usr.client.cmd_admin_subtle_message(M) @@ -2730,7 +2734,8 @@ var/mob/M = locate(href_list["mob"]) in GLOB.mob_list var/client/C = M.client usr.client.cmd_admin_mod_antag_rep(C, href_list["modantagrep"]) - show_player_panel(M) + // show_player_panel(M) + show_player_panel2(M) else if(href_list["slowquery"]) if(!check_rights(R_ADMIN)) diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index cacbb4f627..96cbfc6c21 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -19,67 +19,51 @@ admin_ticket_log(M, msg) SSblackbox.record_feedback("tally", "admin_verb", 1, "Drop Everything") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! -/client/proc/cmd_admin_subtle_message(mob/M in GLOB.mob_list) - set category = "Admin.Events" +/client/proc/cmd_admin_subtle_message(mob/M in GLOB.mob_list, sender = null) + set category = null set name = "Subtle Message" if(!ismob(M)) + priority_announce("no mob!") return if(!check_rights(R_ADMIN)) - return - - message_admins("[key_name_admin(src)] has started answering [ADMIN_LOOKUPFLW(M)]'s prayer.") - var/msg = input("Message:", text("Subtle PM to [M.key]")) as text|null - - if (!msg) - message_admins("[key_name_admin(src)] decided not to answer [ADMIN_LOOKUPFLW(M)]'s prayer") - return - if(usr) - if (usr.client) - if(usr.client.holder) - to_chat(M, "You hear a voice in your head... [msg]") - - log_admin("SubtlePM: [key_name(usr)] -> [key_name(M)] : [msg]") - msg = " SubtleMessage: [key_name_admin(usr)] -> [key_name_admin(M)] : [msg]" - message_admins(msg) - admin_ticket_log(M, msg) - SSblackbox.record_feedback("tally", "admin_verb", 1, "Subtle Message") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_headset_message(mob/M in GLOB.mob_list) - set category = "Admin.Events" - set name = "Headset Message" - - admin_headset_message(M) - -/client/proc/admin_headset_message(mob/M in GLOB.mob_list, sender = null) - var/mob/living/carbon/human/H = M - - if(!check_rights(R_ADMIN)) - return - - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - if(!istype(H.ears, /obj/item/radio/headset)) - to_chat(usr, "The person you are trying to contact is not wearing a headset.") + priority_announce("no admin!") return if (!sender) - sender = input("Who is the message from?", "Sender") as null|anything in list(RADIO_CHANNEL_CENTCOM,RADIO_CHANNEL_SYNDICATE) - if(!sender) + var/list/subtle_message_options = list("Voice in head", RADIO_CHANNEL_CENTCOM, RADIO_CHANNEL_SYNDICATE) + sender = tgui_input_list(usr, "Choose the method of subtle messaging", "Subtle Message", subtle_message_options) + if (!sender) + priority_announce("no sender!") return - message_admins("[key_name_admin(src)] has started answering [key_name_admin(H)]'s [sender] request.") - var/input = input("Please enter a message to reply to [key_name(H)] via their headset.","Outgoing message from [sender]", "") as text|null - if(!input) - message_admins("[key_name_admin(src)] decided not to answer [key_name_admin(H)]'s [sender] request.") + message_admins("[key_name_admin(src)] has started answering [ADMIN_LOOKUPFLW(M)]'s prayer / faction PM.") + var/msg = input("Contents of the message", text("Subtle PM to [M.key]")) as text + if (!msg) + message_admins("[key_name_admin(src)] decided not to answer [ADMIN_LOOKUPFLW(M)]'s prayer / faction PM.") return - log_directed_talk(mob, H, input, LOG_ADMIN, "reply") - message_admins("[key_name_admin(src)] replied to [key_name_admin(H)]'s [sender] message with: \"[input]\"") - to_chat(H, "You hear something crackle in your ears for a moment before a voice speaks. \"Please stand by for a message from [sender == "Syndicate" ? "your benefactor" : "Central Command"]. Message as follows[sender == "Syndicate" ? ", agent." : ":"] [input]. Message ends.\"") + if (sender == "Voice in head") + to_chat(M, "You hear a voice in your head... [msg]") + else + var/mob/living/carbon/human/H = M - SSblackbox.record_feedback("tally", "admin_verb", 1, "Headset Message") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + if(!istype(H)) + to_chat(usr, "The person you are trying to contact is not human. Unsent message: [msg]") + return + + if(!istype(H.ears, /obj/item/radio/headset)) + to_chat(usr, "The person you are trying to contact is not wearing a headset. Unsent message: [msg]") + return + + to_chat(H, "You hear something crackle in your ears for a moment before a voice speaks. \"Please stand by for a message from [sender == RADIO_CHANNEL_SYNDICATE ? "your benefactor" : "Central Command"]. Message as follows[sender == RADIO_CHANNEL_SYNDICATE ? ", agent." : ":"] [msg]. Message ends.\"") + + + log_admin("SubtlePM ([sender]): [key_name(usr)] -> [key_name(M)] : [msg]") + msg = " SubtleMessage ([sender]): [key_name_admin(usr)] -> [key_name_admin(M)] : [msg]" + message_admins(msg) + admin_ticket_log(M, msg) + SSblackbox.record_feedback("tally", "admin_verb", 1, "Subtle Message") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /client/proc/cmd_admin_mod_antag_rep(client/C in GLOB.clients, var/operation) set category = "Special Verbs" @@ -1336,7 +1320,8 @@ Traitors and the like can also be revived with the previous role mostly intact. if(!check_rights(R_ADMIN) || !check_rights(R_FUN)) return - var/list/punishment_list = list(ADMIN_PUNISHMENT_PIE, + var/list/punishment_list = list( + ADMIN_PUNISHMENT_PIE, ADMIN_PUNISHMENT_CUSTOM_PIE, ADMIN_PUNISHMENT_FIREBALL, ADMIN_PUNISHMENT_LIGHTNING, @@ -1362,7 +1347,7 @@ Traitors and the like can also be revived with the previous role mostly intact. ADMIN_PUNISHMENT_BOOKIFY, ADMIN_PUNISHMENT_BONK) - var/punishment = input("Choose a punishment", "DIVINE SMITING") as null|anything in punishment_list + var/punishment = tgui_input_list(usr, "Choose a punishment", "Divine Smiting", punishment_list) if(QDELETED(target) || !punishment) return diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 9418998f16..7ea631e2a0 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -19,6 +19,7 @@ initialize_actionspeed() init_rendering() hook_vr("mob_new",list(src)) + create_player_panel() /mob/Destroy()//This makes sure that mobs with clients/keys are not just deleted from the game. remove_from_mob_list() @@ -36,6 +37,7 @@ observe.reset_perspective(null) dispose_rendering() qdel(hud_used) + QDEL_NULL(mob_panel) for(var/cc in client_colours) qdel(cc) client_colours = null @@ -58,6 +60,11 @@ I.appearance_flags = RESET_COLOR|RESET_TRANSFORM hud_list[hud] = I +/mob/proc/create_player_panel() + QDEL_NULL(mob_panel) + + mob_panel = new(src) + /mob/proc/Cell() set category = "Admin" set hidden = 1 @@ -959,6 +966,7 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0) if(!check_rights(NONE)) return usr.client.holder.show_player_panel(src) + // usr.client.holder.show_player_panel2(src) if(href_list[VV_HK_GODMODE]) if(!check_rights(R_ADMIN)) return diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index c04b9f29a8..320ee86456 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -20,6 +20,9 @@ /// Fullscreen objects var/list/fullscreens = list() + // Admin player panel for this mob + var/datum/player_panel/mob_panel + /// What receives our keyboard input. src by default. var/datum/focus diff --git a/code/modules/wiremod/core/admin_panel.dm b/code/modules/wiremod/core/admin_panel.dm index 33af02f514..39c013edd5 100644 --- a/code/modules/wiremod/core/admin_panel.dm +++ b/code/modules/wiremod/core/admin_panel.dm @@ -61,7 +61,8 @@ circuit.ui_interact(usr) if ("open_player_panel") var/datum/mind/inserter = circuit.inserter_mind?.resolve() - usr.client?.holder?.show_player_panel(inserter?.current) + // usr.client?.holder?.show_player_panel(inserter?.current) + usr.client?.holder?.show_player_panel2(inserter?.current) return TRUE diff --git a/config/entries/jexp.txt b/config/entries/jexp.txt index e46d743a66..e854b53b98 100644 --- a/config/entries/jexp.txt +++ b/config/entries/jexp.txt @@ -9,7 +9,7 @@ #USE_ACCOUNT_AGE_FOR_JOBS ## Unhash this to track player playtime in the database. Requires database to be enabled. -#USE_EXP_TRACKING +USE_EXP_TRACKING ## Unhash this to enable playtime requirements for head jobs. #USE_EXP_RESTRICTIONS_HEADS ## Unhash this to override head jobs' playtime requirements with this number of hours. diff --git a/modular_splurt/code/modules/mob/living/living.dm b/modular_splurt/code/modules/mob/living/living.dm new file mode 100644 index 0000000000..2ba0fb7f36 --- /dev/null +++ b/modular_splurt/code/modules/mob/living/living.dm @@ -0,0 +1,27 @@ +/// Toggle admin frozen +/mob/living/proc/toggle_admin_freeze(client/admin) + admin_frozen = !admin_frozen + + if(admin_frozen) + SetStun(INFINITY, ignore_canstun = TRUE) + else + SetStun(0, ignore_canstun = TRUE) + + if(client && admin) + to_chat(src, "An admin has [!admin_frozen ? "un" : ""]frozen you.") + log_admin("[key_name(admin)] toggled admin-freeze on [key_name(src)].") + message_admins("[key_name_admin(admin)] toggled admin-freeze on [key_name_admin(src)].") + +/// Toggle admin sleeping +/mob/living/proc/toggle_admin_sleep(client/admin) + admin_sleeping = !admin_sleeping + + if(admin_sleeping) + SetSleeping(INFINITY, ignore_canstun = TRUE) + else + SetSleeping(0, ignore_canstun = TRUE) + + if(client && admin) + to_chat(src, "An admin has [!admin_sleeping ? "un": ""]slept you.") + log_admin("[key_name(admin)] toggled admin-sleep on [key_name(src)].") + message_admins("[key_name_admin(admin)] toggled admin-sleep on [key_name_admin(src)].") diff --git a/modular_splurt/code/modules/mob/living/living_defines.dm b/modular_splurt/code/modules/mob/living/living_defines.dm new file mode 100644 index 0000000000..bfc7719041 --- /dev/null +++ b/modular_splurt/code/modules/mob/living/living_defines.dm @@ -0,0 +1,4 @@ +/mob/living + // Admin CC + var/admin_frozen = FALSE + var/admin_sleeping = FALSE diff --git a/tgstation.dme b/tgstation.dme index b5ebc931a0..ce6f8dd57b 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1497,6 +1497,7 @@ #include "code\modules\admin\outfits.dm" #include "code\modules\admin\permissionedit.dm" #include "code\modules\admin\player_panel.dm" +#include "code\modules\admin\player_panel2.dm" #include "code\modules\admin\sound_emitter.dm" #include "code\modules\admin\sql_message_system.dm" #include "code\modules\admin\stickyban.dm" @@ -4261,6 +4262,8 @@ #include "modular_splurt\code\game\object\structures\barricades.dm" #include "modular_splurt\code\game\object\structures\bathroom.dm" #include "modular_splurt\code\game\object\structures\dresser.dm" +#include "modular_splurt\code\modules\mob\living\living.dm" +#include "modular_splurt\code\modules\mob\living\living_defines.dm" #include "modular_splurt\code\modules\mob\living\silicon\robot\robot.dm" #include "modular_splurt\code\game\object\structures\false_walls.dm" #include "modular_splurt\code\game\object\structures\flora.dm" diff --git a/tgui/packages/tgui/interfaces/PlayerPanel2.js b/tgui/packages/tgui/interfaces/PlayerPanel2.js new file mode 100644 index 0000000000..9d908c9073 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PlayerPanel2.js @@ -0,0 +1,793 @@ +import { Fragment } from "inferno"; +import { useBackend, useLocalState } from '../backend'; +import { Input, Button, Flex, Section, Tabs, Box, Dropdown, Slider, Tooltip } from '../components'; +import { Window } from '../layouts'; + +const PAGES = [ + { + title: 'General', + component: () => GeneralActions, + color: "green", + icon: "tools", + }, + { + title: 'Punish', + component: () => PunishmentActions, + color: "red", + icon: "gavel", + canAccess: data => { + return data.client_ckey; + }, + }, + // { + // title: 'Physical', + // component: () => PhysicalActions, + // color: "red", + // icon: "bolt", + // canAccess: data => { + // return !!data.is_human || !!data.is_xeno; + // }, + // }, + // { + // title: 'Transform', + // component: () => TransformActions, + // color: "orange", + // icon: "exchange-alt", + // canAccess: data => { + // return hasPermission(data, "mob_transform"); + // }, + // }, + // { + // title: 'Fun', + // component: () => FunActions, + // color: "blue", + // icon: "laugh", + // canAccess: data => { + // return hasPermission(data, "fun"); + // }, + // }, + // { + // title: 'Antag', + // component: () => AntagActions, + // color: "blue", + // icon: "crosshairs", + // canAccess: data => { + // return (data.is_human || data.is_xeno); + // }, + // }, +]; + +export const PlayerPanel2 = (props, context) => { + const { act, data } = useBackend(context); + const [pageIndex, setPageIndex] = useLocalState(context, 'pageIndex', 0); + const [canModifyCkey, setModifyCkey] = useLocalState(context, 'canModifyCkey', false); + const PageComponent = PAGES[pageIndex].component(); + const { mob_name, mob_type, client_key, client_ckey, client_rank } = data; + + return ( + + +
+ + Name: + + act("set_name", { name: value })} /> + + {!!client_ckey && ( + + Rank: + +
+ + +
+ + {PAGES.map((page, i) => { + if (page.canAccess && !page.canAccess(data)) { + return; + } + + return ( + setPageIndex(i)}> + {page.title} + + ); + })} + +
+
+ + + +
+
+
+ ); +}; + +const GeneralActions = (props, context) => { + const { act, data } = useBackend(context); + + const { is_type_mob_living, is_frozen, is_slept, client_key } = data; + return ( +
+
+ +
+ +
+ + act("bring")} + /> +
+ +
+ + act("freeze")} + /> + act("sleep")} + /> + act("lobby")} + /> + + + Force Say: + + act("force_say", { to_say: value })} + /> + + + + Force Emote: + + act("force_emote", { to_emote: value })} + /> + + +
+
+ ); +}; + +const PunishmentActions = (props, context) => { + const { act, data } = useBackend(context); + const { glob_mute_bits, client_muted } = data; + return ( +
+
+ + act("mob_ban")} + /> + act("mob_eorg_ban")} + /> +
+ +
+ +
+ +
+ + {glob_mute_bits.map((bit, i) => { + const isMuted = (client_muted + && (client_muted & bit.bitflag)); + return ( act("mute", { "mute_flag": !isMuted? client_muted | bit.bitflag : client_muted & ~bit.bitflag })} + />); + }) } + +
+ +
+ +
+
+ ); +}; + +// const TransformActions = (props, context) => { +// const { act, data } = useBackend(context); +// const { glob_pp_transformables } = data; +// return ( +//
+// {Object.keys(glob_pp_transformables).map((element, i) => ( +//
+// +// {glob_pp_transformables[element].map((option, optionIndex) => ( +// act("mob_transform", { key: option.key })} +// /> +// ))} +// +//
+// ))} +//
+// ); +// }; + +// const FunActions = (props, context) => { +// const { act, data } = useBackend(context); +// const { glob_span } = data; +// const [getSpanSetting, setSpanSetting] = useLocalState(context, "span_setting", glob_span[0].span); + +// const [lockExplode, setLockExplode] = useLocalState(context, "explode_lock_toggle", true); +// const [expPower, setExpPower] = useLocalState(context, "exp_power", 50); +// const [falloff, setFalloff] = useLocalState(context, "falloff", 75); +// return ( +//
+// {hasPermission(data, "mob_narrate") && ( +//
+// +// {glob_span.map((spanData, i) => ( +// setSpanSetting(spanData.span)} +// height="100%" +// /> +// ))} +// +// +// Narrate: +// +// act("mob_narrate", +// { +// to_narrate: +// `${value}`, +// } +// )} +// /> +// +// +//
+// )} + +// {hasPermission(data, "mob_explode") && ( +//
setLockExplode(!lockExplode)} +// color={lockExplode? "good" : "bad"} +// /> +// )}> +// +// +// +// +// +// setExpPower(value)} +// ranges={{ +// good: [0, 200], +// average: [200, 400], +// bad: [400, 500], +// }} +// minValue={0} +// maxValue={500} +// /> +// setFalloff(value)} +// ranges={{ +// bad: [0, 10], +// average: [10, 75], +// good: [75, 200], +// }} +// minValue={1} +// maxValue={200} +// mt={1} +// stepPixelSize={2} +// /> +// +// +//
+// )} +//
+// ); +// }; + +// const AntagActions = (props, context) => { +// const { act, data } = useBackend(context); +// const { glob_hives, is_xeno, is_human } = data; + +// const [selectedHivenumber, setHivenumber] = useLocalState(context, "selected_hivenumber", Object.keys(glob_hives)[0]); +// return ( +//
+// {!!is_human && ( +//
+// +//
+// )} +//
setHivenumber(value)} +// /> +// }> +// +// {!!is_human && ( +// +//
+//
+// ); +// }; + +// const PhysicalActions = (props, context) => { +// const { act, data } = useBackend(context); +// const { +// is_human, glob_limbs, mob_speed, +// mob_status_flags, glob_status_flags, +// mob_feels_pain, +// } = data; + +// const limbs = Object.keys(glob_limbs); +// const limb_flags = limbs.map((_, i) => (1< +//
+// {Object.keys(glob_status_flags).map((val, i) => ( +// act("set_status_flags", +// { +// status_flags: +// (mob_status_flags & glob_status_flags[val]) +// ? mob_status_flags & ~glob_status_flags[val] +// : mob_status_flags|glob_status_flags[val], +// } +// )} +// /> +// ))} +// act("set_pain", { feels_pain: !mob_feels_pain })} +// /> +//
+// {!!is_human && ( +//
+// {limbs.map((val, index) => ( +// setDelimbOption( +// (delimbOption & limb_flags[index]) +// ? delimbOption & ~limb_flags[index] +// : delimbOption|limb_flags[index] +// )} +// /> +// ))} +// +// )}> +// +// act("mob_delimb", { +// limbs: limb_flags.map((val, index) => +// !!(delimbOption & val) && glob_limbs[limbs[index]] +// ), +// })} +// /> +// act("mob_relimb", { +// limbs: limb_flags.map((val, index) => +// !!(delimbOption & val) && glob_limbs[limbs[index]] +// ), +// })} +// /> +// +//
+// )} +//
+// +// {!!is_human && ( +// act("cryo_human")} +// > +// +// +// )} +// act("strip_equipment", { drop_items: true })} +// /> +// +// +// {hasPermission(data, "set_speed") && ( +// act("set_speed", { speed: -value })} +// unit="Mob Speed" +// /> +// )} +// +//
+//
+// +//
+// +// ); +// }; From ef610eb21c471294b1572dc61081418bb4c481eb Mon Sep 17 00:00:00 2001 From: James Date: Sun, 20 Feb 2022 18:40:00 +0000 Subject: [PATCH 02/15] WIP jobban panel --- code/modules/admin/player_panel2.dm | 29 +++- code/modules/admin/topic.dm | 4 +- code/modules/client/client_procs.dm | 1 + modular_splurt/code/modules/admin/JobBan.dm | 162 ++++++++++++++++++ modular_splurt/code/modules/admin/Kick.dm | 23 +++ modular_splurt/code/modules/admin/NewBan.dm | 70 ++++++++ .../code/modules/client/client_defines.dm | 3 + .../code/modules/client/client_procs.dm | 5 + .../code/modules/mob/living/living.dm | 4 +- tgstation.dme | 4 + tgui/packages/tgui/interfaces/JobbanPanel.js | 110 ++++++++++++ tgui/packages/tgui/interfaces/PlayerPanel2.js | 151 +++++++++------- 12 files changed, 501 insertions(+), 65 deletions(-) create mode 100644 modular_splurt/code/modules/admin/JobBan.dm create mode 100644 modular_splurt/code/modules/admin/Kick.dm create mode 100644 modular_splurt/code/modules/admin/NewBan.dm create mode 100644 modular_splurt/code/modules/client/client_defines.dm create mode 100644 tgui/packages/tgui/interfaces/JobbanPanel.js diff --git a/code/modules/admin/player_panel2.dm b/code/modules/admin/player_panel2.dm index 7dcc02e89b..7dcb911952 100644 --- a/code/modules/admin/player_panel2.dm +++ b/code/modules/admin/player_panel2.dm @@ -80,8 +80,10 @@ GLOBAL_LIST_INIT(mute_bits, list( /datum/player_panel/ui_static_data() . = list() - if(CONFIG_GET(flag/use_exp_tracking)) - .["playtimes_enabled"] = TRUE + if(targetMob.client) + if(CONFIG_GET(flag/use_exp_tracking)) + .["playtimes_enabled"] = TRUE + .["playtime"] = targetMob.client.get_exp_living() /datum/player_panel/ui_act(action, params, datum/tgui/ui) if(..()) @@ -92,7 +94,7 @@ GLOBAL_LIST_INIT(mute_bits, list( to_chat(admin, "Easdasdasdadmin!") if (!check_rights(R_ADMIN)) - message_admins("WARNING: NON-ADMIN [ADMIN_LOOKUPFLW(user)] ACCESSING ADMIN PANEL. WARN Casper#3044.") + message_admins("WARNING: NON-ADMIN [ADMIN_LOOKUPFLW(admin)] ACCESSING ADMIN PANEL. WARN Casper#3044.") to_chat(admin, "Error: you are not an admin!") return @@ -190,6 +192,27 @@ GLOBAL_LIST_INIT(mute_bits, list( if ("force_emote") targetMob.emote("me", EMOTE_VISIBLE, params["to_emote"]) + if ("prison") + if(isAI(targetMob)) + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai.") + return + + targetMob.forceMove(pick(GLOB.prisonwarp)) + to_chat(targetMob, "You have been sent to Prison!") + + log_admin("[key_name(admin)] has sent [key_name(targetMob)] to Prison!") + message_admins("[key_name_admin(admin)] has sent [key_name_admin(targetMob)] to Prison!") + + if ("kick") + // Kick(targetMob) + + if ("ban") + // NewBan(targetMob) + + if ("job_ban") + if(targetMob.client) + admin.holder.show_jobban_panel(targetMob.client) + if ("mute") if(!targetMob.client) return diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index d004c6ef36..25cb5bd5cb 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -933,13 +933,15 @@ dat += "Bloodsucker" else dat += "Bloodsucker" + + dat += "" //So things dont get squished. + //Slave Trader if(jobban_isbanned(M, ROLE_SLAVER) || isbanned_dept) dat += "Slaver" else dat += "Slaver" - //Other Roles (black) dat += "" dat += "" diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index dc1aefd746..7d42d8367e 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -519,6 +519,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( log_access("Logout: [key_name(src)]") GLOB.ahelp_tickets.ClientLogout(src) SSserver_maint.UpdateHubStatus() + QDEL_NULL(jobban_panel) if(credits) QDEL_LIST(credits) if(holder) diff --git a/modular_splurt/code/modules/admin/JobBan.dm b/modular_splurt/code/modules/admin/JobBan.dm new file mode 100644 index 0000000000..6eb2cc4d01 --- /dev/null +++ b/modular_splurt/code/modules/admin/JobBan.dm @@ -0,0 +1,162 @@ +/datum/admins/proc/show_jobban_panel(client/C) + if(!istype(C, /client)) + to_chat(owner, "This client is no longer in the game.") + return + + // this is stupid, thanks byond + if(istype(src, /client)) + var/client/Cl = src + src = Cl.holder + + if(!check_rights()) + to_chat(owner, "Error: you are not an admin!") + return + + if(!C.jobban_panel) + C.create_jobban_panel() + + C.jobban_panel.ui_interact(owner.mob) + +/datum/jobban_panel + var/client/targetClient + var/list/role_status // A list of each role and whether they are banned or not for this player. + +/datum/jobban_panel/New(client/target) + . = ..() + targetClient = target + +/datum/jobban_panel/Destroy(force, ...) + targetClient = null + + SStgui.close_uis(src) + return ..() + +/datum/jobban_panel/ui_interact(mob/user, datum/tgui/ui) + if(!targetClient) + return + + ui = SStgui.try_update_ui(user, src, ui) + if (!ui) + ui = new(user, src, "JobbanPanel", "[targetClient.ckey] Jobban Panel") + ui.open() + +/datum/jobban_panel/ui_state(mob/user) + return GLOB.admin_state + +/datum/jobban_panel/ui_data(mob/user) + . = list() + .["client_ckey"] = targetClient.ckey + + if (role_status) + .["roles"] = role_status + +/datum/jobban_panel/ui_static_data() + . = list() + updateJobbanStatus() + .["roles"] = role_status + +/datum/jobban_panel/ui_act(action, params, datum/tgui/ui) + if(..()) + return + + if (!check_rights(R_BAN)) + to_chat(usr, "Error: You do not have sufficient admin rights to ban players.") + return + + if(!SSjob) + to_chat(usr, "Jobs subsystem not initialized yet!") + return + + var/query = params["selected_role"] // Which role was clicked on the UI, .e.g. "Assistant" or "engineeringdept" + var/is_category = params["is_category"] + var/list/jobs_to_set = list() // All the roles relating to the selected role above + + to_chat(usr, "Query: " + query) + + if (is_category) + for(var/list/role_category in role_status) // For every department / antag category + if (role_category["category_name"] == query) // If this is the selected category + to_chat(usr, "Category found: " + role_category["category_name"]) + + for(var/list/role in role_category["category_roles"]) + jobs_to_set += role["name"] + + break + else + to_chat(usr, "NOT IT") + + else + jobs_to_set += query + + to_chat(usr, "Total jobs to process:") + for(var/role in jobs_to_set) + to_chat(usr, role) + + + + + + +// Updates the jobban status of this client's jobban panel. +/datum/jobban_panel/proc/updateJobbanStatus() + var/list/roles_to_check = list() + var/list/roles = list() + var/mob/M = targetClient.mob + + roles_to_check += list(list( + "name" = "Command", + "color" = "yellow", + "roles" = GLOB.command_positions + )) + roles_to_check += list(list( + "name" = "Security", + "color" = "red", + "roles" = GLOB.security_positions + )) + roles_to_check += list(list( + "name" = "Engineering", + "color" = "orange", + "roles" = GLOB.engineering_positions + )) + roles_to_check += list(list( + "name" = "Medical", + "color" = "blue", + "roles" = GLOB.medical_positions + )) + roles_to_check += list(list( + "name" = "Science", + "color" = "violet", + "roles" = GLOB.science_positions + )) + roles_to_check += list(list( + "name" = "Supply", + "color" = "brown", + "roles" = GLOB.supply_positions + )) + roles_to_check += list(list( + "name" = "Service", + "color" = "green", + "roles" = GLOB.civilian_positions + )) + roles_to_check += list(list( + "name" = "Silicon", + "color" = "purple", + "roles" = GLOB.nonhuman_positions + )) + + for(var/list/role_category in roles_to_check) // For every department / antag category + var/list/category_roles = list() + category_roles["category_name"] = role_category["name"] + category_roles["category_roles"] = list() + + for(var/role in role_category["roles"]) // For every job / antag + var/list/roles_instance = list() + roles_instance["name"] = role + if(jobban_isbanned(M, role)) + roles_instance["is_banned"] = TRUE + + category_roles["category_roles"] += list(roles_instance) + + roles += list(category_roles) + + role_status = roles diff --git a/modular_splurt/code/modules/admin/Kick.dm b/modular_splurt/code/modules/admin/Kick.dm new file mode 100644 index 0000000000..5a11bcc0f0 --- /dev/null +++ b/modular_splurt/code/modules/admin/Kick.dm @@ -0,0 +1,23 @@ +/proc/kick(mob/M) + to_chat(usr, "debug thing.") + + + + + + if(ismob(M)) + if(!check_if_greater_rights_than(M.client)) + to_chat(usr, "Error: They have more rights than you do.") + return + if(alert(usr, "Kick [key_name(M)]?", "Confirm", "Yes", "No") != "Yes") + return + if(!M) + to_chat(usr, "Error: [M] no longer exists!") + return + if(!M.client) + to_chat(usr, "Error: [M] no longer has a client!") + return + to_chat(M, "You have been kicked from the server by [usr.client.holder.fakekey ? "an Administrator" : "[usr.client.key]"].") + log_admin("[key_name(usr)] kicked [key_name(M)].") + message_admins("[key_name_admin(usr)] kicked [key_name_admin(M)].") + qdel(M.client) diff --git a/modular_splurt/code/modules/admin/NewBan.dm b/modular_splurt/code/modules/admin/NewBan.dm new file mode 100644 index 0000000000..8c34a0adcf --- /dev/null +++ b/modular_splurt/code/modules/admin/NewBan.dm @@ -0,0 +1,70 @@ +// /proc/NewBan(mob/M) +// if(!check_rights(R_BAN)) +// return + +// // var/mob/M = mob_to_ban +// if(!ismob(M)) +// return + +// // if(M.client && M.client.holder) +// // return //admins cannot be banned. Even if they could, the ban doesn't affect them anyway + +// switch(alert("Temporary Ban for [M.key]?",,"Yes","No", "Cancel")) +// if("Yes") +// var/mins = input(usr,"How long (in minutes)?","Ban time",1440) as num|null +// if(mins <= 0) +// to_chat(usr, "[mins] is not a valid duration.") +// return +// var/reason = input(usr,"Please State Reason For Banning [M.key].","Reason") as message|null +// if(!reason) +// return +// if(!DB_ban_record(BANTYPE_TEMP, M, mins, reason)) +// to_chat(usr, "Failed to apply ban.") +// return +// AddBan(M.ckey, M.computer_id, reason, usr.ckey, 1, mins) +// ban_unban_log_save("[key_name(usr)] has banned [key_name(M)]. - Reason: [reason] - This will be removed in [mins] minutes.") +// to_chat(M, "You have been banned by [usr.client.key].\nReason: [reason]") +// to_chat(M, "This is a temporary ban, it will be removed in [mins] minutes. The round ID is [GLOB.round_id].") +// var/bran = CONFIG_GET(string/banappeals) +// if(bran) +// to_chat(M, "To try to resolve this matter head to [bran]") +// else +// to_chat(M, "No ban appeals URL has been set.") +// log_admin_private("[key_name(usr)] has banned [key_name(M)].\nReason: [key_name(M)]\nThis will be removed in [mins] minutes.") +// var/msg = "[key_name_admin(usr)] has banned [key_name_admin(M)].\nReason: [reason]\nThis will be removed in [mins] minutes." +// message_admins(msg) +// var/datum/admin_help/AH = M.client ? M.client.current_ticket : null +// if(AH) +// AH.Resolve() +// qdel(M.client) +// if("No") +// var/reason = input(usr,"Please State Reason For Banning [M.key].","Reason") as message|null +// if(!reason) +// return +// switch(alert(usr,"IP ban?",,"Yes","No","Cancel")) +// if("Cancel") +// return +// if("Yes") +// AddBan(M.ckey, M.computer_id, reason, usr.ckey, 0, 0, M.lastKnownIP) +// if("No") +// AddBan(M.ckey, M.computer_id, reason, usr.ckey, 0, 0) +// to_chat(M, "You have been banned by [usr.client.key].\nReason: [reason]") +// to_chat(M, "This is a permanent ban. The round ID is [GLOB.round_id].") +// var/bran = CONFIG_GET(string/banappeals) +// if(bran) +// to_chat(M, "To try to resolve this matter head to [bran]") +// else +// to_chat(M, "No ban appeals URL has been set.") +// if(!DB_ban_record(BANTYPE_PERMA, M, -1, reason)) +// to_chat(usr, "Failed to apply ban.") +// return +// ban_unban_log_save("[key_name(usr)] has permabanned [key_name(M)]. - Reason: [reason] - This is a permanent ban.") +// log_admin_private("[key_name(usr)] has banned [key_name(M)].\nReason: [reason]\nThis is a permanent ban.") +// var/msg = "[key_name_admin(usr)] has banned [key_name_admin(M)].\nReason: [reason]\nThis is a permanent ban." +// message_admins(msg) +// var/datum/admin_help/AH = M.client ? M.client.current_ticket : null +// if(AH) +// AH.Resolve() +// qdel(M.client) +// if("Cancel") +// return diff --git a/modular_splurt/code/modules/client/client_defines.dm b/modular_splurt/code/modules/client/client_defines.dm new file mode 100644 index 0000000000..3111c09871 --- /dev/null +++ b/modular_splurt/code/modules/client/client_defines.dm @@ -0,0 +1,3 @@ +/client + // Jobban panel for this client + var/datum/jobban_panel/jobban_panel = null diff --git a/modular_splurt/code/modules/client/client_procs.dm b/modular_splurt/code/modules/client/client_procs.dm index d62111d09d..6b97b608b3 100644 --- a/modular_splurt/code/modules/client/client_procs.dm +++ b/modular_splurt/code/modules/client/client_procs.dm @@ -2,3 +2,8 @@ . = ..() if(ip_intel != initial(ip_intel) && ip_intel >= CONFIG_GET(number/ipintel_rating_bad)) uses_vpn = TRUE + +/client/proc/create_jobban_panel() + QDEL_NULL(jobban_panel) + + jobban_panel = new(src) diff --git a/modular_splurt/code/modules/mob/living/living.dm b/modular_splurt/code/modules/mob/living/living.dm index 2ba0fb7f36..e83dd0fa35 100644 --- a/modular_splurt/code/modules/mob/living/living.dm +++ b/modular_splurt/code/modules/mob/living/living.dm @@ -8,7 +8,7 @@ SetStun(0, ignore_canstun = TRUE) if(client && admin) - to_chat(src, "An admin has [!admin_frozen ? "un" : ""]frozen you.") + to_chat(src, "An admin has [!admin_frozen ? "un" : ""]frozen you.") log_admin("[key_name(admin)] toggled admin-freeze on [key_name(src)].") message_admins("[key_name_admin(admin)] toggled admin-freeze on [key_name_admin(src)].") @@ -22,6 +22,6 @@ SetSleeping(0, ignore_canstun = TRUE) if(client && admin) - to_chat(src, "An admin has [!admin_sleeping ? "un": ""]slept you.") + to_chat(src, "An admin has [!admin_sleeping ? "un": ""]slept you.") log_admin("[key_name(admin)] toggled admin-sleep on [key_name(src)].") message_admins("[key_name_admin(admin)] toggled admin-sleep on [key_name_admin(src)].") diff --git a/tgstation.dme b/tgstation.dme index ce6f8dd57b..fdb1ebe1b1 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -2030,6 +2030,7 @@ #include "code\modules\chatter\chatter.dm" #include "code\modules\client\client_colour.dm" #include "code\modules\client\client_defines.dm" +#include "modular_splurt\code\modules\client\client_defines.dm" #include "code\modules\client\client_procs.dm" #include "code\modules\client\client_vpn_detect.dm" #include "code\modules\client\message.dm" @@ -4226,6 +4227,9 @@ #include "modular_splurt\code\datums\traits\good.dm" #include "modular_splurt\code\datums\traits\negative.dm" #include "modular_splurt\code\datums\traits\neutral.dm" +#include "modular_splurt\code\modules\admin\NewBan.dm" +#include "modular_splurt\code\modules\admin\Kick.dm" +#include "modular_splurt\code\modules\admin\JobBan.dm" #include "modular_splurt\code\game\atoms.dm" #include "modular_splurt\code\game\data_huds.dm" #include "modular_splurt\code\game\area\areas\centcom.dm" diff --git a/tgui/packages/tgui/interfaces/JobbanPanel.js b/tgui/packages/tgui/interfaces/JobbanPanel.js new file mode 100644 index 0000000000..475519c415 --- /dev/null +++ b/tgui/packages/tgui/interfaces/JobbanPanel.js @@ -0,0 +1,110 @@ +import { Fragment } from "inferno"; +import { useBackend, useLocalState } from '../backend'; +import { Input, Button, Flex, Section, Tabs, Box, Dropdown, Slider, Tooltip } from '../components'; +import { Window } from '../layouts'; + +export const JobbanPanel = (props, context) => { + const { act, data } = useBackend(context); + const [tab, setTab] = useLocalState(context, 'tab', 0); + // const PageComponent = PAGES[pageIndex].component(); + const { roles, client_ckey } = data; + + return ( + + + + +
+ + {roles.map((role_category, i) => { return ( + setTab(i)}> + {role_category.category_name} + + ); })} + +
+
+ + + +
+
+
+ ); +}; + +const GeneralActions = (props, context) => { + const { act, data } = useBackend(context); + const [tab] = useLocalState(context, 'tab', 0); + const { roles, client_key } = data; + return ( +
+
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/PlayerPanel2.js b/tgui/packages/tgui/interfaces/PlayerPanel2.js index 9d908c9073..baa0beda6e 100644 --- a/tgui/packages/tgui/interfaces/PlayerPanel2.js +++ b/tgui/packages/tgui/interfaces/PlayerPanel2.js @@ -62,7 +62,8 @@ export const PlayerPanel2 = (props, context) => { const [pageIndex, setPageIndex] = useLocalState(context, 'pageIndex', 0); const [canModifyCkey, setModifyCkey] = useLocalState(context, 'canModifyCkey', false); const PageComponent = PAGES[pageIndex].component(); - const { mob_name, mob_type, client_key, client_ckey, client_rank } = data; + const { mob_name, mob_type, client_key, client_ckey, client_rank, + playtimes_enabled, playtime } = data; return ( { minWidth="11rem" textAlign="center" ml=".5rem" icon="window-restore" - content="View Playtimes" + content={playtimes_enabled ? playtime : "Playtimes"} + disabled={!playtimes_enabled} onClick={() => act("access_playtimes")} /> @@ -178,7 +180,7 @@ export const PlayerPanel2 = (props, context) => { const GeneralActions = (props, context) => { const { act, data } = useBackend(context); - const { is_type_mob_living, is_frozen, is_slept, client_key } = data; + const { client_key, is_type_mob_living } = data; return (
@@ -191,14 +193,16 @@ const GeneralActions = (props, context) => { icon="heart" color="green" content="Rejuvenate" + disabled={!is_type_mob_living} onClick={() => act("heal")} />
+ +
+ +
@@ -366,36 +422,13 @@ const PunishmentActions = (props, context) => { width="100%" height="100%" checked={isMuted} - color={isMuted? "good" : "bad"} + color={isMuted? "bad" : ""} content={bit.name} onClick={() => act("mute", { "mute_flag": !isMuted? client_muted | bit.bitflag : client_muted & ~bit.bitflag })} />); }) }
- -
- -
); }; From 77d2faa8dabc93304cc4574ce08cb26e92f14844 Mon Sep 17 00:00:00 2001 From: James Date: Mon, 21 Feb 2022 02:43:14 +0000 Subject: [PATCH 03/15] About to merge player / jobban panel --- modular_splurt/code/modules/admin/JobBan.dm | 264 +++++++++++++----- tgui/packages/tgui/interfaces/JobbanPanel.js | 73 +++-- tgui/packages/tgui/interfaces/PlayerPanel2.js | 132 ++++++++- 3 files changed, 379 insertions(+), 90 deletions(-) diff --git a/modular_splurt/code/modules/admin/JobBan.dm b/modular_splurt/code/modules/admin/JobBan.dm index 6eb2cc4d01..cf9292f519 100644 --- a/modular_splurt/code/modules/admin/JobBan.dm +++ b/modular_splurt/code/modules/admin/JobBan.dm @@ -1,3 +1,86 @@ +GLOBAL_LIST_INIT(jobban_panel_data, list( + list( + "name" = "Command", + "color" = "yellow", + "roles" = GLOB.command_positions + ), + list( + "name" = "Security", + "color" = "red", + "roles" = GLOB.security_positions + ), + list( + "name" = "Engineering", + "color" = "orange", + "roles" = GLOB.engineering_positions + ), + list( + "name" = "Medical", + "color" = "blue", + "roles" = GLOB.medical_positions + ), + list( + "name" = "Science", + "color" = "violet", + "roles" = GLOB.science_positions + ), + list( + "name" = "Supply", + "color" = "brown", + "roles" = GLOB.supply_positions + ), + list( + "name" = "Service", + "color" = "green", + "roles" = GLOB.civilian_positions + ), + list( + "name" = "Silicon", + "color" = "purple", + "roles" = GLOB.nonhuman_positions + ), + list( + "name" = "Ghost Roles", + "color" = "teal", + "roles" = list( + ROLE_PAI, + ROLE_POSIBRAIN, + ROLE_DRONE, + ROLE_DEATHSQUAD, + ROLE_LAVALAND, + ROLE_GHOSTCAFE, + ROLE_SENTIENCE, + ROLE_MIND_TRANSFER + ) + ), + list( + "name" = "Other", + "color" = "grey", + "roles" = list( + ROLE_RESPAWN + ) + ), + list( + "name" = "Antagonists", + "color" = "red", + "roles" = list( + ROLE_TRAITOR, + ROLE_CHANGELING, + ROLE_HERETIC, + ROLE_OPERATIVE, + ROLE_REV, + ROLE_CULTIST, + ROLE_SERVANT_OF_RATVAR, + ROLE_WIZARD, + ROLE_ABDUCTOR, + ROLE_ALIEN, + ROLE_FAMILIES, + ROLE_BLOODSUCKER, + ROLE_SLAVER + ) + ) +)) + /datum/admins/proc/show_jobban_panel(client/C) if(!istype(C, /client)) to_chat(owner, "This client is no longer in the game.") @@ -46,15 +129,15 @@ /datum/jobban_panel/ui_data(mob/user) . = list() .["client_ckey"] = targetClient.ckey + .["is_antag_banned"] = jobban_isbanned(targetClient.mob, ROLE_SYNDICATE) - if (role_status) - .["roles"] = role_status - -/datum/jobban_panel/ui_static_data() - . = list() - updateJobbanStatus() + if (!role_status) + updateJobbanStatus() .["roles"] = role_status +// /datum/jobban_panel/ui_static_data() +// . = list() + /datum/jobban_panel/ui_act(action, params, datum/tgui/ui) if(..()) return @@ -67,96 +150,147 @@ to_chat(usr, "Jobs subsystem not initialized yet!") return - var/query = params["selected_role"] // Which role was clicked on the UI, .e.g. "Assistant" or "engineeringdept" - var/is_category = params["is_category"] - var/list/jobs_to_set = list() // All the roles relating to the selected role above + var/mob/M = targetClient.mob + var/list/jobs_to_set = list() // All the roles relating to the clicked button - to_chat(usr, "Query: " + query) - - if (is_category) + if (params["is_category"]) for(var/list/role_category in role_status) // For every department / antag category - if (role_category["category_name"] == query) // If this is the selected category - to_chat(usr, "Category found: " + role_category["category_name"]) + if (role_category["category_name"] == params["selected_role"]) // If this is the selected category for(var/list/role in role_category["category_roles"]) jobs_to_set += role["name"] - break - else - to_chat(usr, "NOT IT") else - jobs_to_set += query + jobs_to_set += params["selected_role"] - to_chat(usr, "Total jobs to process:") + var/mode = params["want_to_ban"] + + var/list/jobs_to_set_trimmed = list() // The roles from jobs_to_set that aren't already banned / unbanned for(var/role in jobs_to_set) - to_chat(usr, role) - + // If we are in ban mode and this role is unbanned OR if we are in unban mode and this role is banned + // (We don't want to ban / unban roles that are already banned / unbanned) + if ((mode && !jobban_isbanned(M, role)) || (!mode && jobban_isbanned(M, role))) + jobs_to_set_trimmed += role + for(var/role in jobs_to_set_trimmed) + if (jobs_to_set_trimmed.len) // At least one role to get banned / unbanned + if (mode) + usr.client.holder.Jobban(targetClient.mob, jobs_to_set_trimmed) + else + usr.client.holder.UnJobban(targetClient.mob, jobs_to_set_trimmed) + updateJobbanStatus() // Update TGUI data to reflect new ban statuses // Updates the jobban status of this client's jobban panel. /datum/jobban_panel/proc/updateJobbanStatus() - var/list/roles_to_check = list() var/list/roles = list() var/mob/M = targetClient.mob - roles_to_check += list(list( - "name" = "Command", - "color" = "yellow", - "roles" = GLOB.command_positions - )) - roles_to_check += list(list( - "name" = "Security", - "color" = "red", - "roles" = GLOB.security_positions - )) - roles_to_check += list(list( - "name" = "Engineering", - "color" = "orange", - "roles" = GLOB.engineering_positions - )) - roles_to_check += list(list( - "name" = "Medical", - "color" = "blue", - "roles" = GLOB.medical_positions - )) - roles_to_check += list(list( - "name" = "Science", - "color" = "violet", - "roles" = GLOB.science_positions - )) - roles_to_check += list(list( - "name" = "Supply", - "color" = "brown", - "roles" = GLOB.supply_positions - )) - roles_to_check += list(list( - "name" = "Service", - "color" = "green", - "roles" = GLOB.civilian_positions - )) - roles_to_check += list(list( - "name" = "Silicon", - "color" = "purple", - "roles" = GLOB.nonhuman_positions - )) - - for(var/list/role_category in roles_to_check) // For every department / antag category + for(var/list/role_category in GLOB.jobban_panel_data) // For every department / antag category var/list/category_roles = list() category_roles["category_name"] = role_category["name"] + category_roles["category_color"] = role_category["color"] category_roles["category_roles"] = list() for(var/role in role_category["roles"]) // For every job / antag var/list/roles_instance = list() roles_instance["name"] = role - if(jobban_isbanned(M, role)) + var/reason = jobban_isbanned(M, role) + if(reason) roles_instance["is_banned"] = TRUE + roles_instance["ban_reason"] = reason category_roles["category_roles"] += list(roles_instance) roles += list(category_roles) role_status = roles + +// notbannedlist is just a list of strings of the job titles you want to ban. +/datum/admins/proc/Jobban(mob/M, list/notbannedlist) + var/severity = null + var/reason = null + + switch(tgui_alert(usr, "Job ban type", buttons = list("Temporary", "Permanent", "Cancel"))) + if("Temporary") + var/mins = input(usr,"How long (in minutes)?","Ban time",1440) as num|null + if(mins <= 0) + to_chat(usr, "[mins] is not a valid duration.") + return + reason = input(usr,"Please State Reason For Banning [M.key].","Reason") as message|null + if(!reason) + return + severity = tgui_alert(usr, "Set the severity of the note/ban", buttons = list("High", "Medium", "Minor", "None")) + if(!severity) + return + var/msg + for(var/job in notbannedlist) + if(!DB_ban_record(BANTYPE_JOB_TEMP, M, mins, reason, job)) + to_chat(usr, "Failed to apply ban.") + return + if(M.client) + jobban_buildcache(M.client) + ban_unban_log_save("[key_name(usr)] temp-jobbanned [key_name(M)] from [job] for [mins] minutes. reason: [reason]") + log_admin_private("[key_name(usr)] temp-jobbanned [key_name(M)] from [job] for [mins] minutes.") + if(!msg) + msg = job + else + msg += ", [job]" + create_message("note", M.key, null, "Banned from [msg] - [reason]", null, null, 0, 0, null, 0, severity) + message_admins("[key_name_admin(usr)] banned [key_name_admin(M)] from [msg] for [mins] minutes.") + to_chat(M, "You have been [((msg == "ooc") || (msg == "appearance") || (msg == "pacifist")) ? "banned" : "jobbanned"] by [usr.client.key] from: [msg == "pacifist" ? "using violence" : msg].") + to_chat(M, "The reason is: [reason]") + to_chat(M, "This jobban will be lifted in [mins] minutes.") + + if("Permanent") + reason = input(usr,"Please State Reason For Banning [M.key].","Reason") as message|null + if (!reason) + return + severity = tgui_alert(usr, "Please State Reason For Banning", buttons = list("High", "Medium", "Minor", "None")) + if (!severity) + return + + var/msg + for(var/job in notbannedlist) + if(!DB_ban_record(BANTYPE_JOB_PERMA, M, -1, reason, job)) + to_chat(usr, "Failed to apply ban.") + return + if(M.client) + jobban_buildcache(M.client) + ban_unban_log_save("[key_name(usr)] perma-jobbanned [key_name(M)] from [job]. reason: [reason]") + log_admin_private("[key_name(usr)] perma-banned [key_name(M)] from [job]") + if(!msg) + msg = job + else + msg += ", [job]" + create_message("note", M.key, null, "Banned from [msg] - [reason]", null, null, 0, 0, null, 0, severity) + message_admins("[key_name_admin(usr)] banned [key_name_admin(M)] from [msg].") + to_chat(M, "You have been [((msg == "ooc") || (msg == "appearance") || (msg == "pacifist")) ? "banned" : "jobbanned"] by [usr.client.key] from: [msg == "pacifist" ? "using violence" : msg].") + to_chat(M, "The reason is: [reason]") + to_chat(M, "This jobban can be lifted only upon request.") + +// notbannedlist is just a list of strings of the job titles you want to unban. +/datum/admins/proc/UnJobban(mob/M, list/bannedlist) + var/msg + for(var/job in bannedlist) + var/reason = jobban_isbanned(M, job) + if (tgui_alert(usr, "Job: '[job]' Reason: '[reason]'", "Un-Jobban This Player?", list("Yes", "No")) == "Yes") + ban_unban_log_save("[key_name(usr)] unjobbanned [key_name(M)] from [job]") + log_admin_private("[key_name(usr)] unbanned [key_name(M)] from [job]") + DB_ban_unban(M.ckey, BANTYPE_ANY_JOB, job) + if(M.client) + jobban_buildcache(M.client) + if(!msg) + msg = job + else + msg += ", [job]" + if(msg) + message_admins("[key_name_admin(usr)] unbanned [key_name_admin(M)] from [msg].") + to_chat(M, "You have been un-jobbanned by [usr.client.key] from [msg].") + + + + diff --git a/tgui/packages/tgui/interfaces/JobbanPanel.js b/tgui/packages/tgui/interfaces/JobbanPanel.js index 475519c415..a096563e00 100644 --- a/tgui/packages/tgui/interfaces/JobbanPanel.js +++ b/tgui/packages/tgui/interfaces/JobbanPanel.js @@ -1,6 +1,6 @@ import { Fragment } from "inferno"; import { useBackend, useLocalState } from '../backend'; -import { Input, Button, Flex, Section, Tabs, Box, Dropdown, Slider, Tooltip } from '../components'; +import { Input, Collapsible, Button, Flex, Section, Tabs, Box, Dropdown, Slider, Tooltip, NoticeBox, Blink } from '../components'; import { Window } from '../layouts'; export const JobbanPanel = (props, context) => { @@ -12,18 +12,18 @@ export const JobbanPanel = (props, context) => { return ( - +
{roles.map((role_category, i) => { return ( setTab(i)}> @@ -45,11 +45,10 @@ export const JobbanPanel = (props, context) => { const GeneralActions = (props, context) => { const { act, data } = useBackend(context); const [tab] = useLocalState(context, 'tab', 0); - const { roles, client_key } = data; + const { roles, is_antag_banned } = data; return (
@@ -59,9 +58,10 @@ const GeneralActions = (props, context) => { icon="lock" minWidth="8rem" textAlign="center" - onClick={() => act('add_ban', { + onClick={() => act(null, { selected_role: roles[tab].category_name, is_category: true, + want_to_ban: true, })} />
+
+ ); +}; + const GeneralActions = (props, context) => { const { act, data } = useBackend(context); From 6717ae7dd234a84c29781cf2037dfa12e41d3f89 Mon Sep 17 00:00:00 2001 From: James Date: Mon, 21 Feb 2022 19:21:13 +0000 Subject: [PATCH 04/15] Jobban panel merged with player panel --- code/modules/admin/player_panel2.dm | 97 +++++++- code/modules/client/client_procs.dm | 1 - modular_splurt/code/modules/admin/JobBan.dm | 149 +------------ .../code/modules/client/client_defines.dm | 2 - .../code/modules/client/client_procs.dm | 5 - tgui/packages/tgui/interfaces/PlayerPanel2.js | 210 +++++++++--------- 6 files changed, 207 insertions(+), 257 deletions(-) diff --git a/code/modules/admin/player_panel2.dm b/code/modules/admin/player_panel2.dm index 7dcb911952..599d1a0391 100644 --- a/code/modules/admin/player_panel2.dm +++ b/code/modules/admin/player_panel2.dm @@ -27,6 +27,9 @@ GLOBAL_LIST_INIT(mute_bits, list( /datum/player_panel var/mob/targetMob + var/client/targetClient + var/list/roleStatus // A list of each role and whether they are banned or not for this player. + var/isAntagBanned /datum/player_panel/New(mob/target) . = ..() @@ -34,6 +37,9 @@ GLOBAL_LIST_INIT(mute_bits, list( /datum/player_panel/Destroy(force, ...) targetMob = null + targetClient = null + roleStatus = null + isAntagBanned = null SStgui.close_uis(src) return ..() @@ -66,16 +72,22 @@ GLOBAL_LIST_INIT(mute_bits, list( .["is_slept"] = L.admin_sleeping if(targetMob.client) - var/client/targetClient = targetMob.client - .["client_key"] = targetClient.key + targetClient = targetMob.client .["client_ckey"] = targetClient.ckey .["client_muted"] = targetClient.prefs.muted .["client_rank"] = targetClient.holder ? targetClient.holder.rank : "Player" + + + if (!roleStatus) + updateJobbanStatus() + .["roles"] = roleStatus + .["is_antag_banned"] = isAntagBanned + else - .["client_key"] = null + targetClient = null + roleStatus = null + isAntagBanned = null .["client_ckey"] = null - .["client_muted"] = null - .["client_rank"] = null /datum/player_panel/ui_static_data() . = list() @@ -91,8 +103,6 @@ GLOBAL_LIST_INIT(mute_bits, list( var/client/admin = ui.user.client - to_chat(admin, "Easdasdasdadmin!") - if (!check_rights(R_ADMIN)) message_admins("WARNING: NON-ADMIN [ADMIN_LOOKUPFLW(admin)] ACCESSING ADMIN PANEL. WARN Casper#3044.") to_chat(admin, "Error: you are not an admin!") @@ -198,7 +208,7 @@ GLOBAL_LIST_INIT(mute_bits, list( return targetMob.forceMove(pick(GLOB.prisonwarp)) - to_chat(targetMob, "You have been sent to Prison!") + to_chat(targetMob, "You have been sent to Prison!") log_admin("[key_name(admin)] has sent [key_name(targetMob)] to Prison!") message_admins("[key_name_admin(admin)] has sent [key_name_admin(targetMob)] to Prison!") @@ -211,7 +221,7 @@ GLOBAL_LIST_INIT(mute_bits, list( if ("job_ban") if(targetMob.client) - admin.holder.show_jobban_panel(targetMob.client) + process_banlist(params["selected_role"], params["is_category"], params["want_to_ban"]) if ("mute") if(!targetMob.client) @@ -219,3 +229,72 @@ GLOBAL_LIST_INIT(mute_bits, list( targetMob.client.prefs.muted = text2num(params["mute_flag"]) log_admin("[key_name(admin)] set the mute flags for [key_name(targetMob)] to [targetMob.client.prefs.muted].") + +// process_banlist: Gets all jobs in a job category +// Input: +// query (string): The name of the role / department you want to jobban. +// is_category (boolean): Is the query a department / role category? e.g. query "Engineering" needs TRUE +// want_to_ban (boolean): Should we ban or should we unban the job we just supplied. +// +// Output:A list of strings with the names of each role the INPUT covers. +/datum/player_panel/proc/process_banlist(query, is_category, want_to_ban) + if(!SSjob) + to_chat(usr, "Jobs subsystem not initialized yet!") + return + + var/mob/M = targetClient.mob + var/list/jobs_to_set = list() // All the roles relating to the clicked button + + if (is_category) + for(var/list/role_category in roleStatus) // For every department / antag category + if (role_category["category_name"] == query) // If this is the selected category + + for(var/list/role in role_category["category_roles"]) + jobs_to_set += role["name"] + break + + else + jobs_to_set += query + + var/list/jobs_to_set_trimmed = list() // The roles from jobs_to_set that aren't already banned / unbanned + for(var/role in jobs_to_set) + + // If we are in ban mode and this role is unbanned OR if we are in unban mode and this role is banned + // (We don't want to ban / unban roles that are already banned / unbanned) + if ((want_to_ban && !jobban_isbanned(M, role)) || (!want_to_ban && jobban_isbanned(M, role))) + jobs_to_set_trimmed += role + + for(var/role in jobs_to_set_trimmed) + + if (jobs_to_set_trimmed.len) // At least one role to get banned / unbanned + if (want_to_ban) + usr.client.holder.Jobban(targetClient.mob, jobs_to_set_trimmed) + else + usr.client.holder.UnJobban(targetClient.mob, jobs_to_set_trimmed) + + updateJobbanStatus() // Update TGUI data to reflect new ban statuses + +// Updates the jobban status of this client's jobban panel. +/datum/player_panel/proc/updateJobbanStatus() + var/list/roles = list() + + for(var/list/role_category in GLOB.jobban_panel_data) // For every department / antag category + var/list/category_roles = list() + category_roles["category_name"] = role_category["name"] + category_roles["category_color"] = role_category["color"] + category_roles["category_roles"] = list() + + for(var/role in role_category["roles"]) // For every job / antag + var/list/roles_instance = list() + roles_instance["name"] = role + var/reason = jobban_isbanned(targetMob, role) + if(reason) + roles_instance["ban_reason"] = reason + + category_roles["category_roles"] += list(roles_instance) + + roles += list(category_roles) + + roleStatus = roles + + isAntagBanned = jobban_isbanned(targetMob, ROLE_SYNDICATE) diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 7d42d8367e..dc1aefd746 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -519,7 +519,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( log_access("Logout: [key_name(src)]") GLOB.ahelp_tickets.ClientLogout(src) SSserver_maint.UpdateHubStatus() - QDEL_NULL(jobban_panel) if(credits) QDEL_LIST(credits) if(holder) diff --git a/modular_splurt/code/modules/admin/JobBan.dm b/modular_splurt/code/modules/admin/JobBan.dm index cf9292f519..8cb14fec1f 100644 --- a/modular_splurt/code/modules/admin/JobBan.dm +++ b/modular_splurt/code/modules/admin/JobBan.dm @@ -1,3 +1,5 @@ +// Stuff that helps the TGUI player panel jobban section to work + GLOBAL_LIST_INIT(jobban_panel_data, list( list( "name" = "Command", @@ -50,13 +52,7 @@ GLOBAL_LIST_INIT(jobban_panel_data, list( ROLE_LAVALAND, ROLE_GHOSTCAFE, ROLE_SENTIENCE, - ROLE_MIND_TRANSFER - ) - ), - list( - "name" = "Other", - "color" = "grey", - "roles" = list( + ROLE_MIND_TRANSFER, ROLE_RESPAWN ) ), @@ -81,136 +77,13 @@ GLOBAL_LIST_INIT(jobban_panel_data, list( ) )) -/datum/admins/proc/show_jobban_panel(client/C) - if(!istype(C, /client)) - to_chat(owner, "This client is no longer in the game.") - return - - // this is stupid, thanks byond - if(istype(src, /client)) - var/client/Cl = src - src = Cl.holder - - if(!check_rights()) - to_chat(owner, "Error: you are not an admin!") - return - - if(!C.jobban_panel) - C.create_jobban_panel() - - C.jobban_panel.ui_interact(owner.mob) - -/datum/jobban_panel - var/client/targetClient - var/list/role_status // A list of each role and whether they are banned or not for this player. - -/datum/jobban_panel/New(client/target) - . = ..() - targetClient = target - -/datum/jobban_panel/Destroy(force, ...) - targetClient = null - - SStgui.close_uis(src) - return ..() - -/datum/jobban_panel/ui_interact(mob/user, datum/tgui/ui) - if(!targetClient) - return - - ui = SStgui.try_update_ui(user, src, ui) - if (!ui) - ui = new(user, src, "JobbanPanel", "[targetClient.ckey] Jobban Panel") - ui.open() - -/datum/jobban_panel/ui_state(mob/user) - return GLOB.admin_state - -/datum/jobban_panel/ui_data(mob/user) - . = list() - .["client_ckey"] = targetClient.ckey - .["is_antag_banned"] = jobban_isbanned(targetClient.mob, ROLE_SYNDICATE) - - if (!role_status) - updateJobbanStatus() - .["roles"] = role_status - -// /datum/jobban_panel/ui_static_data() -// . = list() - -/datum/jobban_panel/ui_act(action, params, datum/tgui/ui) - if(..()) - return - +// notbannedlist is just a list of strings of the job titles you want to ban. +/datum/admins/proc/Jobban(mob/M, list/notbannedlist) + to_chat(M, "ban start") if (!check_rights(R_BAN)) to_chat(usr, "Error: You do not have sufficient admin rights to ban players.") return - if(!SSjob) - to_chat(usr, "Jobs subsystem not initialized yet!") - return - - var/mob/M = targetClient.mob - var/list/jobs_to_set = list() // All the roles relating to the clicked button - - if (params["is_category"]) - for(var/list/role_category in role_status) // For every department / antag category - if (role_category["category_name"] == params["selected_role"]) // If this is the selected category - - for(var/list/role in role_category["category_roles"]) - jobs_to_set += role["name"] - break - - else - jobs_to_set += params["selected_role"] - - var/mode = params["want_to_ban"] - - var/list/jobs_to_set_trimmed = list() // The roles from jobs_to_set that aren't already banned / unbanned - for(var/role in jobs_to_set) - - // If we are in ban mode and this role is unbanned OR if we are in unban mode and this role is banned - // (We don't want to ban / unban roles that are already banned / unbanned) - if ((mode && !jobban_isbanned(M, role)) || (!mode && jobban_isbanned(M, role))) - jobs_to_set_trimmed += role - - for(var/role in jobs_to_set_trimmed) - - if (jobs_to_set_trimmed.len) // At least one role to get banned / unbanned - if (mode) - usr.client.holder.Jobban(targetClient.mob, jobs_to_set_trimmed) - else - usr.client.holder.UnJobban(targetClient.mob, jobs_to_set_trimmed) - - updateJobbanStatus() // Update TGUI data to reflect new ban statuses - -// Updates the jobban status of this client's jobban panel. -/datum/jobban_panel/proc/updateJobbanStatus() - var/list/roles = list() - var/mob/M = targetClient.mob - - for(var/list/role_category in GLOB.jobban_panel_data) // For every department / antag category - var/list/category_roles = list() - category_roles["category_name"] = role_category["name"] - category_roles["category_color"] = role_category["color"] - category_roles["category_roles"] = list() - - for(var/role in role_category["roles"]) // For every job / antag - var/list/roles_instance = list() - roles_instance["name"] = role - var/reason = jobban_isbanned(M, role) - if(reason) - roles_instance["is_banned"] = TRUE - roles_instance["ban_reason"] = reason - - category_roles["category_roles"] += list(roles_instance) - - roles += list(category_roles) - - role_status = roles - -// notbannedlist is just a list of strings of the job titles you want to ban. -/datum/admins/proc/Jobban(mob/M, list/notbannedlist) var/severity = null var/reason = null @@ -271,9 +144,15 @@ GLOBAL_LIST_INIT(jobban_panel_data, list( to_chat(M, "You have been [((msg == "ooc") || (msg == "appearance") || (msg == "pacifist")) ? "banned" : "jobbanned"] by [usr.client.key] from: [msg == "pacifist" ? "using violence" : msg].") to_chat(M, "The reason is: [reason]") to_chat(M, "This jobban can be lifted only upon request.") + to_chat(M, "ban end") // notbannedlist is just a list of strings of the job titles you want to unban. /datum/admins/proc/UnJobban(mob/M, list/bannedlist) + + if (!check_rights(R_BAN)) + to_chat(usr, "Error: You do not have sufficient admin rights to unban players.") + return + var/msg for(var/job in bannedlist) var/reason = jobban_isbanned(M, job) @@ -290,7 +169,3 @@ GLOBAL_LIST_INIT(jobban_panel_data, list( if(msg) message_admins("[key_name_admin(usr)] unbanned [key_name_admin(M)] from [msg].") to_chat(M, "You have been un-jobbanned by [usr.client.key] from [msg].") - - - - diff --git a/modular_splurt/code/modules/client/client_defines.dm b/modular_splurt/code/modules/client/client_defines.dm index 3111c09871..7149778f20 100644 --- a/modular_splurt/code/modules/client/client_defines.dm +++ b/modular_splurt/code/modules/client/client_defines.dm @@ -1,3 +1 @@ /client - // Jobban panel for this client - var/datum/jobban_panel/jobban_panel = null diff --git a/modular_splurt/code/modules/client/client_procs.dm b/modular_splurt/code/modules/client/client_procs.dm index 6b97b608b3..d62111d09d 100644 --- a/modular_splurt/code/modules/client/client_procs.dm +++ b/modular_splurt/code/modules/client/client_procs.dm @@ -2,8 +2,3 @@ . = ..() if(ip_intel != initial(ip_intel) && ip_intel >= CONFIG_GET(number/ipintel_rating_bad)) uses_vpn = TRUE - -/client/proc/create_jobban_panel() - QDEL_NULL(jobban_panel) - - jobban_panel = new(src) diff --git a/tgui/packages/tgui/interfaces/PlayerPanel2.js b/tgui/packages/tgui/interfaces/PlayerPanel2.js index bfdab70a3e..7d62ba0144 100644 --- a/tgui/packages/tgui/interfaces/PlayerPanel2.js +++ b/tgui/packages/tgui/interfaces/PlayerPanel2.js @@ -1,6 +1,6 @@ import { Fragment } from "inferno"; import { useBackend, useLocalState } from '../backend'; -import { Input, Button, Flex, Section, Tabs, Box, Dropdown, Slider, Tooltip } from '../components'; +import { Input, Button, Flex, Section, Tabs, Box, NoticeBox, Collapsible, Dropdown, Slider, Tooltip } from '../components'; import { Window } from '../layouts'; const PAGES = [ @@ -19,6 +19,15 @@ const PAGES = [ return data.client_ckey; }, }, + { + title: 'Job Bans', + component: () => Jobbans, + color: "red", + icon: "gavel", + canAccess: data => { + return data.client_ckey; + }, + }, // { // title: 'Physical', // component: () => PhysicalActions, @@ -60,9 +69,8 @@ const PAGES = [ export const PlayerPanel2 = (props, context) => { const { act, data } = useBackend(context); const [pageIndex, setPageIndex] = useLocalState(context, 'pageIndex', 0); - const [canModifyCkey, setModifyCkey] = useLocalState(context, 'canModifyCkey', false); - // const PageComponent = PAGES[pageIndex].component(); - const [tab, setTab] = useLocalState(context, 'tab', 0); + const PageComponent = PAGES[pageIndex].component(); + const { mob_name, mob_type, client_key, client_ckey, client_rank, playtimes_enabled, playtime } = data; @@ -145,7 +153,7 @@ export const PlayerPanel2 = (props, context) => {
- {/* {PAGES.map((page, i) => { + {PAGES.map((page, i) => { if (page.canAccess && !page.canAccess(data)) { return; } @@ -160,51 +168,13 @@ export const PlayerPanel2 = (props, context) => { {page.title} ); - })} */} - setTab(0)}> - Tab title - - setTab(1)}> - Tab title - - setTab(2)}> - Tab title - + })} -
- - - {tab === 0 && ( - - )} - {tab === 1 && ( - - )} - {tab === 2 && ( - - )} - {/* */} + + @@ -214,34 +184,23 @@ export const PlayerPanel2 = (props, context) => { const Jobbans = (props, context) => { const { act, data } = useBackend(context); - const [tab2, setTab2] = useLocalState(context, 'tab2', 0); - const { client_key, is_type_mob_living } = data; + const [jobbanTab, setJobbanTab] = useLocalState(context, 'jobbanTab', 0); + const { client_key, is_type_mob_living, roles } = data; return (
- setTab2(0)}> - Tab title - - setTab2(1)}> - Tab title - - setTab2(2)}> - Tab title - + {roles.map((role_category, i) => { return ( + setJobbanTab(i)}> + {role_category.category_name} + + ); })}
@@ -255,42 +214,87 @@ const Jobbans = (props, context) => { const Joban2 = (props, context) => { const { act, data } = useBackend(context); - - const { client_key, is_type_mob_living } = data; + const [jobbanTab] = useLocalState(context, 'jobbanTab', 0); + const { roles, is_antag_banned } = data; return (
-
- -
From 31b5fade1933ec9c39084419a3b19f8919c3cd12 Mon Sep 17 00:00:00 2001 From: James Date: Fri, 25 Feb 2022 05:12:50 +0000 Subject: [PATCH 05/15] Added more stuff to PP and misc --- _maps/map_files/generic/CentCom.dmm | 3 + code/game/machinery/cryopod.dm | 350 +++--- code/modules/admin/admin_verbs.dm | 5 +- code/modules/admin/player_panel2.dm | 196 ++- code/modules/mob/living/carbon/carbon.dm | 18 +- code/modules/mob/living/carbon/human/human.dm | 37 +- modular_splurt/code/modules/admin/JobBan.dm | 18 +- modular_splurt/code/modules/admin/Kick.dm | 32 +- modular_splurt/code/modules/admin/NewBan.dm | 134 +-- .../code/modules/admin/Transform.dm | 131 ++ .../code/modules/client/client_procs.dm | 97 ++ modular_splurt/code/modules/mob/mob.dm | 9 + tgstation.dme | 1 + tgui/packages/tgui/interfaces/PlayerPanel2.js | 1056 +++++++++-------- 14 files changed, 1278 insertions(+), 809 deletions(-) create mode 100644 modular_splurt/code/modules/admin/Transform.dm diff --git a/_maps/map_files/generic/CentCom.dmm b/_maps/map_files/generic/CentCom.dmm index a203cd1034..5cd77e9c82 100644 --- a/_maps/map_files/generic/CentCom.dmm +++ b/_maps/map_files/generic/CentCom.dmm @@ -19355,6 +19355,9 @@ /area/centcom/holding) "Ud" = ( /obj/effect/turf_decal/stripes/corner, +/obj/machinery/computer/cryopod{ + pixel_x = 32 + }, /turf/open/floor/plasteel, /area/slavers) "Ug" = ( diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index 3386229fc8..ecd9f1a96a 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -8,9 +8,9 @@ * ~ Zuhayr */ GLOBAL_LIST_EMPTY(cryopod_computers) +GLOBAL_VAR_INIT(cryo_next_admin_warning, 0) // The last time we told admins about a mapping / coding error because no cryo computer was found //Main cryopod console. - /obj/machinery/computer/cryopod name = "general oversight console" desc = "An interface between crew and the cryogenic storage/teleporter storage oversight systems." @@ -143,7 +143,6 @@ GLOBAL_LIST_EMPTY(cryopod_computers) ///Weakref to our controller var/datum/weakref/control_computer_weakref - COOLDOWN_DECLARE(last_no_computer_message) /obj/machinery/cryopod/Initialize() ..() @@ -151,32 +150,16 @@ GLOBAL_LIST_EMPTY(cryopod_computers) /obj/machinery/cryopod/LateInitialize() update_icon() - find_control_computer() + control_computer_weakref = cryo_find_control_computer(src) // This is not a good situation /obj/machinery/cryopod/Destroy() control_computer_weakref = null return ..() -/obj/machinery/cryopod/proc/find_control_computer(urgent = FALSE) - for(var/cryo_console as anything in GLOB.cryopod_computers) - var/obj/machinery/computer/cryopod/console = cryo_console - if(get_area(console) == get_area(src)) - control_computer_weakref = WEAKREF(console) - break - - // Don't send messages unless we *need* the computer, and less than five minutes have passed since last time we messaged - if(!control_computer_weakref && urgent && COOLDOWN_FINISHED(src, last_no_computer_message)) - COOLDOWN_START(src, last_no_computer_message, 5 MINUTES) - log_admin("\The [src] in [get_area(src)] could not find control computer!") - message_admins("\The [src] in [get_area(src)] could not find control computer!") - last_no_computer_message = world.time - - return control_computer_weakref != null - /obj/machinery/cryopod/close_machine(atom/movable/target) if(!control_computer_weakref) - find_control_computer(TRUE) + control_computer_weakref = cryo_find_control_computer(src, TRUE) if((isnull(target) || isliving(target)) && state_open && !panel_open) ..(target) var/mob/living/mob_occupant = occupant @@ -214,12 +197,11 @@ GLOBAL_LIST_EMPTY(cryopod_computers) if(!mob_occupant.client && COOLDOWN_FINISHED(src, despawn_world_time)) if(!control_computer_weakref) - find_control_computer(urgent = TRUE) + control_computer_weakref = cryo_find_control_computer(src, urgent = TRUE) despawn_occupant() -/obj/machinery/cryopod/proc/handle_objectives() - var/mob/living/mob_occupant = occupant +/proc/cryo_handle_objectives(mob/living/mob_occupant) // Update any existing objectives involving this mob. for(var/datum/objective/objective in GLOB.objectives) // We don't want revs to get objectives that aren't for heads of staff. Letting @@ -276,147 +258,6 @@ GLOBAL_LIST_EMPTY(cryopod_computers) return TRUE return FALSE -// This function can not be undone; do not call this unless you are sure -/obj/machinery/cryopod/proc/despawn_occupant() - var/mob/living/mob_occupant = occupant - var/list/crew_member = list() - - crew_member["name"] = mob_occupant.real_name - - if(mob_occupant.mind) - // Handle job slot/tater cleanup. - var/job = mob_occupant.mind.assigned_role - crew_member["job"] = job - SSjob.FreeRole(job) - // if(LAZYLEN(mob_occupant.mind.objectives)) - // mob_occupant.mind.objectives.Cut() - mob_occupant.mind.special_role = null - else - crew_member["job"] = "N/A" - - // Delete them from datacore. - var/announce_rank = null - for(var/datum/data/record/medical_record as anything in GLOB.data_core.medical) - if(medical_record.fields["name"] == mob_occupant.real_name) - qdel(medical_record) - for(var/datum/data/record/security_record as anything in GLOB.data_core.security) - if(security_record.fields["name"] == mob_occupant.real_name) - qdel(security_record) - for(var/datum/data/record/general_record as anything in GLOB.data_core.general) - if(general_record.fields["name"] == mob_occupant.real_name) - announce_rank = general_record.fields["rank"] - qdel(general_record) - - var/obj/machinery/computer/cryopod/control_computer = control_computer_weakref?.resolve() - if(!control_computer) - control_computer_weakref = null - else - control_computer.frozen_crew += list(crew_member) - - // Make an announcement and log the person entering storage. - if(GLOB.announcement_systems.len) - var/obj/machinery/announcement_system/announcer = pick(GLOB.announcement_systems) - announcer.announce(tele ? "CRYOSTORAGE_TELE" : "CRYOSTORAGE", mob_occupant.real_name, announce_rank, list()) - - visible_message(span_notice("\The [src] hums and hisses as it [tele ? "teleports" : "moves"] [mob_occupant.real_name] [tele ? "to centcom" : "into storage"].")) - if(tele) - do_fake_sparks(2, TRUE, src) // Oh yeah plasmafire time. - playsound(src, 'sound/weapons/emitter2.ogg', 25, 1, extrarange = 3, falloff_exponent = 5) - - /* ============================= */ - var/list/obj/item/storing = list() - var/list/obj/item/destroying = list() - var/list/obj/item/destroy_later = list() - var/drop_to_ground = !istype(control_computer, /obj/machinery/computer/cryopod) || !control_computer.allow_items - var/mind_identity = mob_occupant.mind?.name - var/occupant_identity = mob_occupant.real_name - - if(iscyborg(mob_occupant)) - var/mob/living/silicon/robot/R = mob_occupant - if(R.mmi?.brain) - destroy_later += R.mmi - destroy_later += R.mmi.brain - for(var/i in R.module) - if(!isitem(i)) - destroying += i - continue - var/obj/item/I = i - // let's be honest we only care about the trash bag don't beat around the bush - if(SEND_SIGNAL(I, COMSIG_CONTAINS_STORAGE)) - storing += I.contents - for(var/atom/movable/AM in I.contents) - AM.forceMove(src) - R.module.remove_module(I, TRUE) - else - if(ishuman(mob_occupant)) - var/mob/living/carbon/human/H = mob_occupant - if(H.mind && H.client && H.client.prefs && H == H.mind.original_character) - H.SaveTCGCards() - - var/list/gear = list() - if(iscarbon(mob_occupant)) // sorry simp-le-mobs deserve no mercy - var/mob/living/carbon/C = mob_occupant - gear = C.get_all_gear() - - for(var/obj/item/item_content as anything in gear) - if(!istype(item_content) || HAS_TRAIT(item_content, TRAIT_NODROP)) - destroying += item_content - continue - if(item_content.item_flags & (DROPDEL | ABSTRACT)) - destroying += item_content - continue - - // destroying stays in mob for a bit - item_content.forceMove(src) - - // WEE WOO SNOWFLAKE TIME - if(istype(item_content, /obj/item/pda)) - var/obj/item/pda/P = item_content - if((P.owner == mind_identity) || (P.owner == occupant_identity)) - destroying += P - else - storing += P - else if(istype(item_content, /obj/item/card/id)) - var/obj/item/card/id/idcard = item_content - if((idcard.registered_name == mind_identity) || (idcard.registered_name == occupant_identity)) - destroying += idcard - else - storing += idcard - else - storing += item_content - - // get rid of mobs - for(var/mob/living/L in mob_occupant.GetAllContents() - mob_occupant) - L.forceMove(drop_location()) - - if(storing.len) - var/obj/item/storage/box/blue/cryostorage_items/O = new /obj/item/storage/box/blue/cryostorage_items - O.name = "[tele ? "early leave" : "cryogenic"] retrieval package: [mob_occupant.real_name]" - O.real_name = mob_occupant.real_name - for(var/i in storing) - var/obj/item/I = i - I.forceMove(O) - O.forceMove(drop_to_ground ? control_computer.drop_location() : control_computer) - if((control_computer == control_computer) && !drop_to_ground) - control_computer.stored_packages += O - /* ============================= */ - - // Ghost and delete the mob. - // they already did ghost verb - var/mob/dead/observer/G = mob_occupant.get_ghost(TRUE) - if(G) - G.voluntary_ghosted = TRUE - // they did not ghost verb - else - mob_occupant.ghostize(FALSE, penalize = TRUE, voluntary = TRUE, cryo = TRUE) - - QDEL_LIST(destroying) - handle_objectives() - QDEL_NULL(occupant) - QDEL_LIST(destroy_later) - open_machine() - name = initial(name) - /obj/machinery/cryopod/MouseDrop_T(mob/living/target, mob/user) if(!istype(target) || !can_interact(user) || !target.Adjacent(user) || !ismob(target) || isanimal(target) || !istype(user.loc, /turf) || target.buckled) return @@ -502,3 +343,184 @@ GLOBAL_LIST_EMPTY(cryopod_computers) /obj/item/storage/box/blue/cryostorage_items w_class = WEIGHT_CLASS_HUGE var/real_name = "fire coderbus" + occupant + +// This function can not be undone; do not call this unless you are sure +/obj/machinery/cryopod/proc/despawn_occupant() + cryoMob(occupant, control_computer_weakref, src, tele, initial(name)) + +/proc/cryoMob(mob/living/mob_occupant, datum/weakref/control_computer_weakref, obj/machinery/cryopod/pod, is_teleporter, initial_name, effects = FALSE) + var/list/crew_member = list() + + // No computer passed in, use admin-cryo instead + if (!control_computer_weakref) + control_computer_weakref = cryo_find_control_computer(urgent = TRUE) + + if (effects) + // Fancy effect for admin-cryo + playsound(get_turf(mob_occupant.loc), 'sound/magic/Repulse.ogg', 100, 1) + var/datum/effect_system/spark_spread/quantum/sparks = new + sparks.set_up(10, 1, mob_occupant) + sparks.attach(mob_occupant.loc) + sparks.start() + + crew_member["name"] = mob_occupant.real_name + + if(mob_occupant.mind) + // Handle job slot/tater cleanup. + var/job = mob_occupant.mind.assigned_role + crew_member["job"] = job + SSjob.FreeRole(job) + // if(LAZYLEN(mob_occupant.mind.objectives)) + // mob_occupant.mind.objectives.Cut() + mob_occupant.mind.special_role = null + else + crew_member["job"] = "N/A" + + // Delete them from datacore. + var/announce_rank = null + for(var/datum/data/record/medical_record as anything in GLOB.data_core.medical) + if(medical_record.fields["name"] == mob_occupant.real_name) + qdel(medical_record) + for(var/datum/data/record/security_record as anything in GLOB.data_core.security) + if(security_record.fields["name"] == mob_occupant.real_name) + qdel(security_record) + for(var/datum/data/record/general_record as anything in GLOB.data_core.general) + if(general_record.fields["name"] == mob_occupant.real_name) + announce_rank = general_record.fields["rank"] + qdel(general_record) + + var/obj/machinery/computer/cryopod/control_computer = control_computer_weakref?.resolve() + if(!control_computer) + control_computer_weakref = null + else + control_computer.frozen_crew += list(crew_member) + + // Make an announcement and log the person entering storage. + if(GLOB.announcement_systems.len) + var/obj/machinery/announcement_system/announcer = pick(GLOB.announcement_systems) + announcer.announce(is_teleporter ? "CRYOSTORAGE_TELE" : "CRYOSTORAGE", mob_occupant.real_name, announce_rank, list()) + + if (pod) + pod.visible_message(span_notice("\The [pod] hums and hisses as it [is_teleporter ? "teleports" : "moves"] [mob_occupant.real_name] [is_teleporter ? "to centcom" : "into storage"].")) + if(is_teleporter) + do_fake_sparks(2, TRUE, pod) // Oh yeah plasmafire time. + playsound(pod, 'sound/weapons/emitter2.ogg', 25, 1, extrarange = 3, falloff_exponent = 5) + + /* ============================= */ + var/list/obj/item/storing = list() + var/list/obj/item/destroying = list() + var/list/obj/item/destroy_later = list() + var/drop_to_ground = !istype(control_computer, /obj/machinery/computer/cryopod) || !control_computer.allow_items + var/mind_identity = mob_occupant.mind?.name + var/occupant_identity = mob_occupant.real_name + + if(iscyborg(mob_occupant)) + var/mob/living/silicon/robot/R = mob_occupant + if(R.mmi?.brain) + destroy_later += R.mmi + destroy_later += R.mmi.brain + for(var/i in R.module) + if(!isitem(i)) + destroying += i + continue + var/obj/item/I = i + // let's be honest we only care about the trash bag don't beat around the bush + if(SEND_SIGNAL(I, COMSIG_CONTAINS_STORAGE)) + storing += I.contents + for(var/atom/movable/AM in I.contents) + if(pod) + AM.forceMove(pod) + R.module.remove_module(I, TRUE) + else + if(ishuman(mob_occupant)) + var/mob/living/carbon/human/H = mob_occupant + if(H.mind && H.client && H.client.prefs && H == H.mind.original_character) + H.SaveTCGCards() + + var/list/gear = list() + if(iscarbon(mob_occupant)) // sorry simp-le-mobs deserve no mercy + var/mob/living/carbon/C = mob_occupant + gear = C.get_all_gear() + + for(var/obj/item/item_content as anything in gear) + if(!istype(item_content) || HAS_TRAIT(item_content, TRAIT_NODROP)) + destroying += item_content + continue + if(item_content.item_flags & (DROPDEL | ABSTRACT)) + destroying += item_content + continue + + // destroying stays in mob for a bit + if(pod) + item_content.forceMove(pod) + + // WEE WOO SNOWFLAKE TIME + if(istype(item_content, /obj/item/pda)) + var/obj/item/pda/P = item_content + if((P.owner == mind_identity) || (P.owner == occupant_identity)) + destroying += P + else + storing += P + else if(istype(item_content, /obj/item/card/id)) + var/obj/item/card/id/idcard = item_content + if((idcard.registered_name == mind_identity) || (idcard.registered_name == occupant_identity)) + destroying += idcard + else + storing += idcard + else + storing += item_content + + // get rid of mobs + if (pod) + for(var/mob/living/L in mob_occupant.GetAllContents() - mob_occupant) + L.forceMove(pod.drop_location()) + + if(storing.len) + var/obj/item/storage/box/blue/cryostorage_items/O = new /obj/item/storage/box/blue/cryostorage_items + O.name = "[is_teleporter ? "early leave" : "cryogenic"] retrieval package: [mob_occupant.real_name]" + O.real_name = mob_occupant.real_name + for(var/i in storing) + var/obj/item/I = i + I.forceMove(O) + O.forceMove(drop_to_ground ? control_computer.drop_location() : control_computer) + if((control_computer == control_computer) && !drop_to_ground) + control_computer.stored_packages += O + /* ============================= */ + + // Ghost and delete the mob. + // they already did ghost verb + var/mob/dead/observer/G = mob_occupant.get_ghost(TRUE) + if(G) + G.voluntary_ghosted = TRUE + // they did not ghost verb + else + mob_occupant.ghostize(FALSE, penalize = TRUE, voluntary = TRUE, cryo = TRUE) + + QDEL_LIST(destroying) + cryo_handle_objectives() + QDEL_NULL(mob_occupant) + QDEL_LIST(destroy_later) + + if (pod) + pod.open_machine() + pod.name = initial_name + +/proc/cryo_find_control_computer(obj/machinery/cryopod/pod = null, urgent = FALSE) + + for(var/cryo_console as anything in GLOB.cryopod_computers) + var/obj/machinery/computer/cryopod/console = cryo_console + + if(!pod) // No cryopod passed in, we will try to find the standard crew cryo computer instead. + var/turf/console_turf = get_turf(console) + if((console_turf.z == 2) && (!istype(get_area(console), /area/security/prison))) // If station Z-level and NOT the prison cryo computer. Should get the standard crew cryo computer. + return WEAKREF(console) + else + if(get_area(console) == get_area(pod)) + return WEAKREF(console) + + // Don't send messages unless we *need* the computer, and less than five minutes have passed since last time we messaged + if(urgent && (world.time > GLOB.cryo_next_admin_warning)) + log_admin("\The [pod] in [get_area(pod)] could not find control computer!") + message_admins("\The [pod] in [get_area(pod)] could not find control computer!") + GLOB.cryo_next_admin_warning = world.time + 5 MINUTES diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index ff41e68188..a408643826 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -712,7 +712,8 @@ GLOBAL_PROTECT(admin_verbs_hideable) var/type_length = length_char("/obj/effect/proc_holder/spell") + 2 for(var/A in GLOB.spells) spell_list[copytext_char("[A]", type_length)] = A - var/obj/effect/proc_holder/spell/S = input("Choose the spell to give to that guy", "ABRAKADABRA") as null|anything in sortList(spell_list) + var/obj/effect/proc_holder/spell/S = tgui_input_list(usr, "Choose a spell to give", "ABRAKADABRA", sortList(spell_list)) //input("Choose the spell to give to that guy", "ABRAKADABRA") as null|anything in sortList(spell_list) + if(!S) return @@ -733,7 +734,7 @@ GLOBAL_PROTECT(admin_verbs_hideable) set desc = "Remove a spell from the selected mob." if(T?.mind) - var/obj/effect/proc_holder/spell/S = input("Choose the spell to remove", "NO ABRAKADABRA") as null|anything in sortList(T.mind.spell_list) + var/obj/effect/proc_holder/spell/S = tgui_input_list(usr, "Choose the spell to remove", "NO ABRAKADABRA", sortList(T.mind.spell_list)) // input("Choose the spell to remove", "NO ABRAKADABRA") as null|anything in sortList(T.mind.spell_list) if(S) T.mind.RemoveSpell(S) log_admin("[key_name(usr)] removed the spell [S] from [key_name(T)].") diff --git a/code/modules/admin/player_panel2.dm b/code/modules/admin/player_panel2.dm index 599d1a0391..a96350eaad 100644 --- a/code/modules/admin/player_panel2.dm +++ b/code/modules/admin/player_panel2.dm @@ -2,10 +2,18 @@ GLOBAL_LIST_INIT(mute_bits, list( list(name = "IC", bitflag = MUTE_IC), list(name = "OOC", bitflag = MUTE_OOC), list(name = "Pray", bitflag = MUTE_PRAY), - list(name = "Adminhelp", bitflag = MUTE_ADMINHELP), + list(name = "Ahelp", bitflag = MUTE_ADMINHELP), list(name = "Deadchat", bitflag = MUTE_DEADCHAT) )) +GLOBAL_LIST_INIT(pp_limbs, list( + "Head" = BODY_ZONE_HEAD, + "Left leg" = BODY_ZONE_L_LEG, + "Right leg" = BODY_ZONE_R_LEG, + "Left arm" = BODY_ZONE_L_ARM, + "Right arm" = BODY_ZONE_R_ARM +)) + /datum/admins/proc/show_player_panel2(mob/M) if(!M) to_chat(owner, "You seem to be selecting a mob that doesn't exist anymore.") @@ -29,17 +37,25 @@ GLOBAL_LIST_INIT(mute_bits, list( var/mob/targetMob var/client/targetClient var/list/roleStatus // A list of each role and whether they are banned or not for this player. - var/isAntagBanned + var/antagBanReason + var/activeRoleBans + var/mobSize // Because aparently there is no variable that tracks this on the mob?? /datum/player_panel/New(mob/target) . = ..() targetMob = target + var/mob/living/L = targetMob + if (istype(L)) + mobSize = L.mob_size + /datum/player_panel/Destroy(force, ...) targetMob = null targetClient = null roleStatus = null - isAntagBanned = null + antagBanReason = null + activeRoleBans = null + mobSize = null SStgui.close_uis(src) return ..() @@ -58,18 +74,18 @@ GLOBAL_LIST_INIT(mute_bits, list( /datum/player_panel/ui_data(mob/user) . = list() - .["mob_name"] = targetMob.name + .["mob_name"] = targetMob.real_name .["current_permissions"] = user.client?.holder?.rank.rights - .["mob_type"] = targetMob.type - - .["glob_mute_bits"] = GLOB.mute_bits + .["godmode"] = targetMob.status_flags & GODMODE var/mob/living/L = targetMob if (istype(L)) .["is_type_mob_living"] = TRUE .["is_frozen"] = L.admin_frozen .["is_slept"] = L.admin_sleeping + .["is_type_mob_human"] = ishuman(L) + .["mob_scale"] = mobSize if(targetMob.client) targetClient = targetMob.client @@ -77,22 +93,36 @@ GLOBAL_LIST_INIT(mute_bits, list( .["client_muted"] = targetClient.prefs.muted .["client_rank"] = targetClient.holder ? targetClient.holder.rank : "Player" - if (!roleStatus) updateJobbanStatus() .["roles"] = roleStatus - .["is_antag_banned"] = isAntagBanned + .["antag_ban_reason"] = antagBanReason + .["active_role_ban_count"] = activeRoleBans else targetClient = null roleStatus = null - isAntagBanned = null + antagBanReason = null .["client_ckey"] = null /datum/player_panel/ui_static_data() . = list() + .["transformables"] = GLOB.pp_transformables + .["glob_limbs"] = GLOB.pp_limbs + .["glob_mute_bits"] = GLOB.mute_bits + .["current_time"] = time2text(world.timeofday, "YYYY-MM-DD hh:mm:ss") + if(targetMob.client) + var/byond_version = "Unknown" + if(targetClient.byond_version) + byond_version = "[targetClient.byond_version].[targetClient.byond_build ? targetClient.byond_build : "xxx"]" + .["data_byond_version"] = byond_version + .["data_player_join_date"] = targetClient.player_join_date + .["data_account_join_date"] = targetClient.account_join_date + .["data_related_cid"] = targetClient.related_accounts_cid + .["data_related_ip"] = targetClient.related_accounts_ip + if(CONFIG_GET(flag/use_exp_tracking)) .["playtimes_enabled"] = TRUE .["playtime"] = targetMob.client.get_exp_living() @@ -101,7 +131,7 @@ GLOBAL_LIST_INIT(mute_bits, list( if(..()) return - var/client/admin = ui.user.client + var/client/admin = usr.client if (!check_rights(R_ADMIN)) message_admins("WARNING: NON-ADMIN [ADMIN_LOOKUPFLW(admin)] ACCESSING ADMIN PANEL. WARN Casper#3044.") @@ -151,6 +181,7 @@ GLOBAL_LIST_INIT(mute_bits, list( message_admins("[key_name_admin(admin)] ejected [key_name_admin(targetMob)] from their body.") to_chat(targetMob, "An admin has ejected you from your body.") targetMob.ghostize(FALSE) + targetMob.mind.key = null if ("smite") admin.smite(targetMob) @@ -196,6 +227,16 @@ GLOBAL_LIST_INIT(mute_bits, list( var/datum/preferences/P = GLOB.preferences_datums[NP.ckey] P.respawn_restrictions_active = FALSE + if ("select_equipment") + admin.cmd_select_equipment(targetMob) + + if ("strip") + for(var/obj/item/I in targetMob) + targetMob.dropItemToGround(I, TRUE) //The TRUE forces all items to drop, since this is an admin undress. + + if ("cryo") + cryoMob(targetMob, effects = TRUE) + if ("force_say") targetMob.say(params["to_say"], forced="admin") @@ -214,10 +255,20 @@ GLOBAL_LIST_INIT(mute_bits, list( message_admins("[key_name_admin(admin)] has sent [key_name_admin(targetMob)] to Prison!") if ("kick") - // Kick(targetMob) + admin.holder.kick(targetMob) if ("ban") - // NewBan(targetMob) + admin.holder.newBan(targetMob) + + if ("sticky_ban") + var/list/ban_settings = list() + if(targetMob.client) + ban_settings["ckey"] = targetMob.ckey + admin.holder.stickyban("add", ban_settings) + + if ("notes") + if (targetMob.client) + browse_messages(target_ckey = targetMob.ckey) if ("job_ban") if(targetMob.client) @@ -230,6 +281,117 @@ GLOBAL_LIST_INIT(mute_bits, list( targetMob.client.prefs.muted = text2num(params["mute_flag"]) log_admin("[key_name(admin)] set the mute flags for [key_name(targetMob)] to [targetMob.client.prefs.muted].") + if ("mute_all") + if(!targetMob.client) + return + + for(var/bit in GLOB.mute_bits) + targetMob.client.prefs.muted |= bit["bitflag"] + + log_admin("[key_name(admin)] mass-muted [key_name(targetMob)].") + + if ("unmute_all") + if(!targetMob.client) + return + + for(var/bit in GLOB.mute_bits) + targetMob.client.prefs.muted &= ~bit["bitflag"] + + log_admin("[key_name(admin)] mass-unmuted [key_name(targetMob)].") + + if ("related_accounts") + if(targetMob.client) + var/related_accounts + if (params["related_thing"] == "CID") + related_accounts = targetMob.client.related_accounts_cid + else + related_accounts = targetMob.client.related_accounts_ip + + related_accounts = splittext(related_accounts, ", ") + + var/list/dat = list("Related accounts by [params["related_thing"]]:") + dat += related_accounts + usr << browse(dat.Join("
"), "window=related_[targetMob.client];size=420x300") + + if ("transform") + var/choice = params["newType"] + if (choice == "/mob/living") + choice = tgui_input_list(usr, "What should this mob transform into", "Mob Transform", subtypesof(choice)) + if (!choice) + return + + admin.holder.transformMob(targetMob, admin.mob, choice, params["newTypeName"]) + + if ("toggle_godmode") + admin.cmd_admin_godmode(targetMob) + + if ("spell") + admin.toggle_spell(targetMob) + + if ("martial_art") + admin.teach_martial_art(targetMob) + + if ("quirk") + admin.toggle_quirk(targetMob) + + if ("species") + admin.set_species(targetMob) + + if ("limb") + if(!params["limbs"] || !ishuman(targetMob)) + return + + var/mob/living/carbon/human/H = targetMob + + for(var/limb in params["limbs"]) + to_chat(usr, limb) + if (!limb) + continue + + to_chat(usr, "passed") + if (params["delimb_mode"]) + var/obj/item/bodypart/L = H.get_bodypart(limb) + if (!L) + continue + L.dismember(harmless = TRUE) + playsound(H, 'modular_splurt/sound/effects/cartoon_pop.ogg', 70) + else + H.regenerate_limb(limb) + + if ("scale") + var/mob/living/L = targetMob + if(!isnull(params["new_scale"]) && istype(L)) + L.vv_edit_var("resize", params["new_scale"]) + mobSize = params["new_scale"] + + if ("explode") + var/power = text2num(params["power"]) + + var/turf/T = get_turf(usr) + message_admins("[ADMIN_LOOKUPFLW(usr)] created an admin explosion at [ADMIN_VERBOSEJMP(T)].") + log_admin("[key_name(usr)] created an admin explosion at [usr.loc].") + + explosion(usr, power / 3, power / 2, power, power, ignorecap = TRUE) + + if ("narrate") + var/list/stylesRaw = params["classes"] + + var/styles = "" + for(var/style in stylesRaw) + styles += "[style]:[stylesRaw[style]];" + + if (params["mode_global"]) + to_chat(world, "[params["message"]]") + log_admin("GlobalNarrate: [key_name(usr)] : [params["message"]]") + message_admins("[key_name_admin(usr)] Sent a global narrate") + else + for(var/mob/M in view(params["range"], usr)) + to_chat(M, "[params["message"]]") + + log_admin("LocalNarrate: [key_name(usr)] at [AREACOORD(usr)]: [params["message"]]") + message_admins(" LocalNarrate: [key_name_admin(usr)] at [ADMIN_VERBOSEJMP(usr)]: [params["message"]]
") + + // process_banlist: Gets all jobs in a job category // Input: // query (string): The name of the role / department you want to jobban. @@ -272,11 +434,12 @@ GLOBAL_LIST_INIT(mute_bits, list( else usr.client.holder.UnJobban(targetClient.mob, jobs_to_set_trimmed) - updateJobbanStatus() // Update TGUI data to reflect new ban statuses + updateJobbanStatus() // Update TGUI data to reflect new ban statuses // Updates the jobban status of this client's jobban panel. /datum/player_panel/proc/updateJobbanStatus() var/list/roles = list() + var/active_role_bans = 0 for(var/list/role_category in GLOB.jobban_panel_data) // For every department / antag category var/list/category_roles = list() @@ -290,11 +453,12 @@ GLOBAL_LIST_INIT(mute_bits, list( var/reason = jobban_isbanned(targetMob, role) if(reason) roles_instance["ban_reason"] = reason + active_role_bans++ category_roles["category_roles"] += list(roles_instance) roles += list(category_roles) roleStatus = roles - - isAntagBanned = jobban_isbanned(targetMob, ROLE_SYNDICATE) + antagBanReason = jobban_isbanned(targetMob, ROLE_SYNDICATE) + activeRoleBans = active_role_bans diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 06dd7ad7c3..b0d1808782 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -1037,23 +1037,7 @@ if(href_list[VV_HK_MARTIAL_ART]) if(!check_rights(NONE)) return - var/list/artpaths = subtypesof(/datum/martial_art) - var/list/artnames = list() - for(var/i in artpaths) - var/datum/martial_art/M = i - artnames[initial(M.name)] = M - var/result = input(usr, "Choose the martial art to teach","JUDO CHOP") as null|anything in artnames - if(!usr) - return - if(QDELETED(src)) - to_chat(usr, "Mob doesn't exist anymore") - return - if(result) - var/chosenart = artnames[result] - var/datum/martial_art/MA = new chosenart - MA.teach(src) - log_admin("[key_name(usr)] has taught [MA] to [key_name(src)].") - message_admins("[key_name_admin(usr)] has taught [MA] to [key_name_admin(src)].") + usr.client.teach_martial_art(src) if(href_list[VV_HK_GIVE_TRAUMA]) if(!check_rights(NONE)) return diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 3c4fdd68cb..bdeb1ef452 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -721,26 +721,27 @@ return copy_outfit() if(href_list[VV_HK_MOD_QUIRKS]) - if(!check_rights(R_SPAWN)) - return + usr.client.toggle_quirk(src) + // if(!check_rights(R_SPAWN)) + // return - var/list/options = list("Clear"="Clear") - for(var/x in subtypesof(/datum/quirk)) - var/datum/quirk/T = x - var/qname = initial(T.name) - options[has_quirk(T) ? "[qname] (Remove)" : "[qname] (Add)"] = T + // var/list/options = list("Clear"="Clear") + // for(var/x in subtypesof(/datum/quirk)) + // var/datum/quirk/T = x + // var/qname = initial(T.name) + // options[has_quirk(T) ? "[qname] (Remove)" : "[qname] (Add)"] = T - var/result = input(usr, "Choose quirk to add/remove","Quirk Mod") as null|anything in options - if(result) - if(result == "Clear") - for(var/datum/quirk/q in roundstart_quirks) - remove_quirk(q.type) - else - var/T = options[result] - if(has_quirk(T)) - remove_quirk(T) - else - add_quirk(T,TRUE) + // var/result = input(usr, "Choose quirk to add/remove","Quirk Mod") as null|anything in options + // if(result) + // if(result == "Clear") + // for(var/datum/quirk/q in roundstart_quirks) + // remove_quirk(q.type) + // else + // var/T = options[result] + // if(has_quirk(T)) + // remove_quirk(T) + // else + // add_quirk(T,TRUE) if(href_list[VV_HK_MAKE_MONKEY]) if(!check_rights(R_SPAWN)) return diff --git a/modular_splurt/code/modules/admin/JobBan.dm b/modular_splurt/code/modules/admin/JobBan.dm index 8cb14fec1f..98b2fe27d2 100644 --- a/modular_splurt/code/modules/admin/JobBan.dm +++ b/modular_splurt/code/modules/admin/JobBan.dm @@ -52,8 +52,7 @@ GLOBAL_LIST_INIT(jobban_panel_data, list( ROLE_LAVALAND, ROLE_GHOSTCAFE, ROLE_SENTIENCE, - ROLE_MIND_TRANSFER, - ROLE_RESPAWN + ROLE_MIND_TRANSFER ) ), list( @@ -74,12 +73,22 @@ GLOBAL_LIST_INIT(jobban_panel_data, list( ROLE_BLOODSUCKER, ROLE_SLAVER ) + ), + list( + "name" = "Other", + "color" = "red", + "roles" = list( + "pacifist", + "appearance", + "emote", + "OOC", + ROLE_RESPAWN + ) ) )) // notbannedlist is just a list of strings of the job titles you want to ban. /datum/admins/proc/Jobban(mob/M, list/notbannedlist) - to_chat(M, "ban start") if (!check_rights(R_BAN)) to_chat(usr, "Error: You do not have sufficient admin rights to ban players.") return @@ -144,7 +153,6 @@ GLOBAL_LIST_INIT(jobban_panel_data, list( to_chat(M, "You have been [((msg == "ooc") || (msg == "appearance") || (msg == "pacifist")) ? "banned" : "jobbanned"] by [usr.client.key] from: [msg == "pacifist" ? "using violence" : msg].") to_chat(M, "The reason is: [reason]") to_chat(M, "This jobban can be lifted only upon request.") - to_chat(M, "ban end") // notbannedlist is just a list of strings of the job titles you want to unban. /datum/admins/proc/UnJobban(mob/M, list/bannedlist) @@ -156,7 +164,7 @@ GLOBAL_LIST_INIT(jobban_panel_data, list( var/msg for(var/job in bannedlist) var/reason = jobban_isbanned(M, job) - if (tgui_alert(usr, "Job: '[job]' Reason: '[reason]'", "Un-Jobban This Player?", list("Yes", "No")) == "Yes") + if (tgui_alert(usr, "Job: '[job]' Ban reason: '[reason]'", "Un-Jobban This Player?", list("Yes", "No")) == "Yes") ban_unban_log_save("[key_name(usr)] unjobbanned [key_name(M)] from [job]") log_admin_private("[key_name(usr)] unbanned [key_name(M)] from [job]") DB_ban_unban(M.ckey, BANTYPE_ANY_JOB, job) diff --git a/modular_splurt/code/modules/admin/Kick.dm b/modular_splurt/code/modules/admin/Kick.dm index 5a11bcc0f0..19c0f77de2 100644 --- a/modular_splurt/code/modules/admin/Kick.dm +++ b/modular_splurt/code/modules/admin/Kick.dm @@ -1,23 +1,13 @@ -/proc/kick(mob/M) - to_chat(usr, "debug thing.") +/datum/admins/proc/kick(mob/M) + if (!ismob(M) || !M.client) + to_chat(usr, "Error: [M] has no client!") + return + if(!check_if_greater_rights_than(M.client)) + to_chat(usr, "Error: They have more rights than you do.") + return - - - - if(ismob(M)) - if(!check_if_greater_rights_than(M.client)) - to_chat(usr, "Error: They have more rights than you do.") - return - if(alert(usr, "Kick [key_name(M)]?", "Confirm", "Yes", "No") != "Yes") - return - if(!M) - to_chat(usr, "Error: [M] no longer exists!") - return - if(!M.client) - to_chat(usr, "Error: [M] no longer has a client!") - return - to_chat(M, "You have been kicked from the server by [usr.client.holder.fakekey ? "an Administrator" : "[usr.client.key]"].") - log_admin("[key_name(usr)] kicked [key_name(M)].") - message_admins("[key_name_admin(usr)] kicked [key_name_admin(M)].") - qdel(M.client) + to_chat(M, "You have been kicked from the server by [usr.client.holder.fakekey ? "an Administrator" : "[usr.client.key]"].") + log_admin("[key_name(usr)] kicked [key_name(M)].") + message_admins("[key_name_admin(usr)] kicked [key_name_admin(M)].") + qdel(M.client) diff --git a/modular_splurt/code/modules/admin/NewBan.dm b/modular_splurt/code/modules/admin/NewBan.dm index 8c34a0adcf..9a42461c34 100644 --- a/modular_splurt/code/modules/admin/NewBan.dm +++ b/modular_splurt/code/modules/admin/NewBan.dm @@ -1,70 +1,70 @@ -// /proc/NewBan(mob/M) -// if(!check_rights(R_BAN)) -// return +/datum/admins/proc/newBan(mob/M) + if(!check_rights(R_BAN)) + return -// // var/mob/M = mob_to_ban -// if(!ismob(M)) -// return + // var/mob/M = mob_to_ban + if(!ismob(M) || !M.client) + to_chat(usr, "Error: [M] has no client!") + return -// // if(M.client && M.client.holder) -// // return //admins cannot be banned. Even if they could, the ban doesn't affect them anyway + if(M.client && M.client.holder) + to_chat(usr, "Error: You cannot ban admins!") + return //admins cannot be banned. Even if they could, the ban doesn't affect them anyway -// switch(alert("Temporary Ban for [M.key]?",,"Yes","No", "Cancel")) -// if("Yes") -// var/mins = input(usr,"How long (in minutes)?","Ban time",1440) as num|null -// if(mins <= 0) -// to_chat(usr, "[mins] is not a valid duration.") -// return -// var/reason = input(usr,"Please State Reason For Banning [M.key].","Reason") as message|null -// if(!reason) -// return -// if(!DB_ban_record(BANTYPE_TEMP, M, mins, reason)) -// to_chat(usr, "Failed to apply ban.") -// return -// AddBan(M.ckey, M.computer_id, reason, usr.ckey, 1, mins) -// ban_unban_log_save("[key_name(usr)] has banned [key_name(M)]. - Reason: [reason] - This will be removed in [mins] minutes.") -// to_chat(M, "You have been banned by [usr.client.key].\nReason: [reason]") -// to_chat(M, "This is a temporary ban, it will be removed in [mins] minutes. The round ID is [GLOB.round_id].") -// var/bran = CONFIG_GET(string/banappeals) -// if(bran) -// to_chat(M, "To try to resolve this matter head to [bran]") -// else -// to_chat(M, "No ban appeals URL has been set.") -// log_admin_private("[key_name(usr)] has banned [key_name(M)].\nReason: [key_name(M)]\nThis will be removed in [mins] minutes.") -// var/msg = "[key_name_admin(usr)] has banned [key_name_admin(M)].\nReason: [reason]\nThis will be removed in [mins] minutes." -// message_admins(msg) -// var/datum/admin_help/AH = M.client ? M.client.current_ticket : null -// if(AH) -// AH.Resolve() -// qdel(M.client) -// if("No") -// var/reason = input(usr,"Please State Reason For Banning [M.key].","Reason") as message|null -// if(!reason) -// return -// switch(alert(usr,"IP ban?",,"Yes","No","Cancel")) -// if("Cancel") -// return -// if("Yes") -// AddBan(M.ckey, M.computer_id, reason, usr.ckey, 0, 0, M.lastKnownIP) -// if("No") -// AddBan(M.ckey, M.computer_id, reason, usr.ckey, 0, 0) -// to_chat(M, "You have been banned by [usr.client.key].\nReason: [reason]") -// to_chat(M, "This is a permanent ban. The round ID is [GLOB.round_id].") -// var/bran = CONFIG_GET(string/banappeals) -// if(bran) -// to_chat(M, "To try to resolve this matter head to [bran]") -// else -// to_chat(M, "No ban appeals URL has been set.") -// if(!DB_ban_record(BANTYPE_PERMA, M, -1, reason)) -// to_chat(usr, "Failed to apply ban.") -// return -// ban_unban_log_save("[key_name(usr)] has permabanned [key_name(M)]. - Reason: [reason] - This is a permanent ban.") -// log_admin_private("[key_name(usr)] has banned [key_name(M)].\nReason: [reason]\nThis is a permanent ban.") -// var/msg = "[key_name_admin(usr)] has banned [key_name_admin(M)].\nReason: [reason]\nThis is a permanent ban." -// message_admins(msg) -// var/datum/admin_help/AH = M.client ? M.client.current_ticket : null -// if(AH) -// AH.Resolve() -// qdel(M.client) -// if("Cancel") -// return + switch(tgui_alert(usr, "Ban type", buttons = list("Temporary", "Permanent", "Cancel"))) + if("Temporary") + var/mins = input(usr,"How long (in minutes)?","Ban time",1440) as num|null + if(mins <= 0) + to_chat(usr, "[mins] is not a valid duration.") + return + var/reason = input(usr,"Please State Reason For Banning [M.key].","Reason") as message|null + if(!reason) + return + if(!DB_ban_record(BANTYPE_TEMP, M, mins, reason)) + to_chat(usr, "Failed to apply ban.") + return + AddBan(M.ckey, M.computer_id, reason, usr.ckey, 1, mins) + ban_unban_log_save("[key_name(usr)] has banned [key_name(M)]. - Reason: [reason] - This will be removed in [mins] minutes.") + to_chat(M, "You have been banned by [usr.client.key].\nReason: [reason]") + to_chat(M, "This is a temporary ban, it will be removed in [mins] minutes. The round ID is [GLOB.round_id].") + var/bran = CONFIG_GET(string/banappeals) + if(bran) + to_chat(M, "To try to resolve this matter head to [bran]") + else + to_chat(M, "No ban appeals URL has been set.") + log_admin_private("[key_name(usr)] has banned [key_name(M)].\nReason: [key_name(M)]\nThis will be removed in [mins] minutes.") + var/msg = "[key_name_admin(usr)] has banned [key_name_admin(M)].\nReason: [reason]\nThis will be removed in [mins] minutes." + message_admins(msg) + var/datum/admin_help/AH = M.client ? M.client.current_ticket : null + if(AH) + AH.Resolve() + qdel(M.client) + if("Permanent") + var/reason = input(usr,"Please State Reason For Banning [M.key].","Reason") as message|null + if(!reason) + return + switch(alert(usr,"IP ban?",,"Yes","No","Cancel")) + if("Cancel") + return + if("Yes") + AddBan(M.ckey, M.computer_id, reason, usr.ckey, 0, 0, M.lastKnownIP) + if("No") + AddBan(M.ckey, M.computer_id, reason, usr.ckey, 0, 0) + to_chat(M, "You have been banned by [usr.client.key].\nReason: [reason]") + to_chat(M, "This is a permanent ban. The round ID is [GLOB.round_id].") + var/bran = CONFIG_GET(string/banappeals) + if(bran) + to_chat(M, "To try to resolve this matter head to [bran]") + else + to_chat(M, "No ban appeals URL has been set.") + if(!DB_ban_record(BANTYPE_PERMA, M, -1, reason)) + to_chat(usr, "Failed to apply ban.") + return + ban_unban_log_save("[key_name(usr)] has permabanned [key_name(M)]. - Reason: [reason] - This is a permanent ban.") + log_admin_private("[key_name(usr)] has banned [key_name(M)].\nReason: [reason]\nThis is a permanent ban.") + var/msg = "[key_name_admin(usr)] has banned [key_name_admin(M)].\nReason: [reason]\nThis is a permanent ban." + message_admins(msg) + var/datum/admin_help/AH = M.client ? M.client.current_ticket : null + if(AH) + AH.Resolve() + qdel(M.client) diff --git a/modular_splurt/code/modules/admin/Transform.dm b/modular_splurt/code/modules/admin/Transform.dm new file mode 100644 index 0000000000..755dbe1132 --- /dev/null +++ b/modular_splurt/code/modules/admin/Transform.dm @@ -0,0 +1,131 @@ +// Stuff that helps the TGUI player panel transform section to work + +GLOBAL_LIST_INIT(pp_transformables, list( + list( + name = "Common", + color = "", + types = list( + list( + name = "Human", + key = /mob/living/carbon/human + ), + list( + name = "Monkey", + key = /mob/living/carbon/monkey + ), + list( + name = "Cyborg", + key = /mob/living/silicon/robot + ) + ) + ), + list( + name = "Xenomorph", + color = "purple", + types = list( + list( + name = "Larva", + key = /mob/living/carbon/alien/larva + ), + list( + name = "Drone", + key = /mob/living/carbon/alien/humanoid/drone + ), + list( + name = "Hunter", + key = /mob/living/carbon/alien/humanoid/hunter + ), + list( + name = "Sentinel", + key = /mob/living/carbon/alien/humanoid/sentinel + ), + list( + name = "Praetorian", + key = /mob/living/carbon/alien/humanoid/royal/praetorian + ), + list( + name = "Queen", + key = /mob/living/carbon/alien/humanoid/royal/queen + ) + ) + ), + list( + name = "Lavaland", + color = "orange", + types = list( + list( + name = "Goliath", + key = /mob/living/simple_animal/hostile/asteroid/goliath/beast + ), + list( + name = "Legion", + key = /mob/living/simple_animal/hostile/asteroid/elite/legionnaire + ), + list( + name = "Blood-Drunk Miner", + key = /mob/living/simple_animal/hostile/megafauna/blood_drunk_miner + ), + list( + name = "Gladiator", + key = /mob/living/simple_animal/hostile/megafauna/gladiator + ), + list( + name = "Rough Process", + key = /mob/living/simple_animal/hostile/megafauna/rogueprocess + ), + list( + name = "Dragon", + key = /mob/living/simple_animal/hostile/megafauna/dragon + ), + list( + name = "Legion Hivelord", + key = /mob/living/simple_animal/hostile/asteroid/hivelord/legion + ) + ) + ), + list( + name = "Cultist", + color = "violet", + types = list( + list( + name = "Shade", + key = /mob/living/simple_animal/hostile/construct/shade + ), + list( + name = "Artificer", + key = /mob/living/simple_animal/hostile/construct/builder + ), + list( + name = "Wraith", + key = /mob/living/simple_animal/hostile/construct/wraith + ), + list( + name = "Juggernaut", + key = /mob/living/simple_animal/hostile/construct/armored + ) + ) + ) +)) + +// M: Mob to change +// newType: Path of new type e.g: /mob/living/carbon/alien/humanoid/drone +// newTypeName (optional): Name of the new type (used in logging): e.g: "Drone" +/datum/admins/proc/transformMob(mob/M, mob/adminMob, newType, newTypeName) + if(!check_rights(R_SPAWN)) + return + + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob.") + return + + if (!newTypeName) + newTypeName = newType + + log_admin("[key_name(usr)] transformed [key_name(M)] into a [newTypeName].") + message_admins("[key_name_admin(usr)] transformed [key_name_admin(M)] into a [newTypeName].") + + var/mob/newMob = M.change_mob_type(newType, delete_old_mob = TRUE) + + if (M == adminMob) + adminMob = newMob + addtimer(CALLBACK(newMob.mob_panel, /datum.proc/ui_interact, adminMob), 0.1 SECONDS) diff --git a/modular_splurt/code/modules/client/client_procs.dm b/modular_splurt/code/modules/client/client_procs.dm index d62111d09d..fb2e9a1fdb 100644 --- a/modular_splurt/code/modules/client/client_procs.dm +++ b/modular_splurt/code/modules/client/client_procs.dm @@ -2,3 +2,100 @@ . = ..() if(ip_intel != initial(ip_intel) && ip_intel >= CONFIG_GET(number/ipintel_rating_bad)) uses_vpn = TRUE + +/client/proc/toggle_quirk(mob/living/carbon/human/H) + if (!istype(H)) + to_chat(usr, "This can only be used on /mob/living/carbon/human.") + return + + var/list/options = list("Clear"="Clear") + for(var/x in subtypesof(/datum/quirk)) + var/datum/quirk/T = x + var/qname = initial(T.name) + options[H.has_quirk(T) ? "[qname] (Remove)" : "[qname] (Add)"] = T + + var/result = tgui_input_list(usr, "Choose quirk to add/remove", "Mob Quirks", options) // input(usr, "Choose quirk to add/remove","Quirk Mod") as null|anything in options + + if(QDELETED(H)) + to_chat(usr, "Mob doesn't exist anymore") + return + + if(result) + if(result == "Clear") + for(var/datum/quirk/q in H.roundstart_quirks) + H.remove_quirk(q.type) + else + var/T = options[result] + if(H.has_quirk(T)) + H.remove_quirk(T) + else + H.add_quirk(T,TRUE) + +/client/proc/toggle_spell(mob/M) + var/list/options = list("Clear"="Clear") + for(var/x in GLOB.spells) + var/obj/effect/proc_holder/spell/spell = x + var/spell_name = initial(spell.name) + options[M.has_spell(spell) ? "[spell_name] (Remove)" : "[spell_name] (Add)"] = spell + + to_chat(usr, "List length: [options.len]") + + var/spell_to_modify = tgui_input_list(usr, "Choose spell to add/remove", "Mob Spells", options) // input(usr, "Choose quirk to add/remove","Quirk Mod") as null|anything in options + + if(QDELETED(M)) + to_chat(usr, "Mob doesn't exist anymore") + return + + to_chat(usr, "Option selected to modify: [spell_to_modify]") + if(spell_to_modify) + if(spell_to_modify == "Clear") + if (!M.mind) + return + for(var/obj/effect/proc_holder/spell/S in M.mind.spell_list) + M.mind.RemoveSpell(S.type) + else + var/T = options[spell_to_modify] + if(M.has_spell(T)) + to_chat(usr, "Attempted to remove...") + M.mind.RemoveSpell(T) + else + to_chat(usr, "Attempted to add...") + if (M.mind) + M.mind.AddSpell(new T) + else + M.AddSpell(new T) + message_admins("Spells given to mindless mobs will not be transferred in mindswap or cloning!") + + to_chat(usr, "END") + +/client/proc/teach_martial_art(mob/living/carbon/C) + if (!istype(C)) + to_chat(usr, "This can only be used on /mob/living/carbon.") + return + + var/list/artpaths = subtypesof(/datum/martial_art) + var/list/artnames = list() + for(var/i in artpaths) + var/datum/martial_art/M = i + artnames[initial(M.name)] = M + var/result = tgui_input_list(usr, "Choose the martial art to teach", "JUDO CHOP", artnames) // input(usr, "Choose the martial art to teach","JUDO CHOP") as null|anything in artnames + + if(QDELETED(C)) + to_chat(usr, "Mob doesn't exist anymore") + return + if(result) + var/chosenart = artnames[result] + var/datum/martial_art/MA = new chosenart + MA.teach(C) + log_admin("[key_name(usr)] has taught [MA] to [key_name(C)].") + message_admins("[key_name_admin(usr)] has taught [MA] to [key_name_admin(C)].") + +/client/proc/set_species(mob/living/carbon/human/H) + if (istype(H)) + var/result = tgui_input_list(usr, "Choose a new species","Species", GLOB.species_list) + if(QDELETED(H)) + to_chat(usr, "Mob doesn't exist anymore") + return + if(result) + admin_ticket_log("[key_name_admin(usr)] has modified the bodyparts of [H] to [result]") + H.set_species(GLOB.species_list[result]) diff --git a/modular_splurt/code/modules/mob/mob.dm b/modular_splurt/code/modules/mob/mob.dm index 8831d46d22..9b5926076b 100644 --- a/modular_splurt/code/modules/mob/mob.dm +++ b/modular_splurt/code/modules/mob/mob.dm @@ -11,3 +11,12 @@ return FALSE transform = transform.Turn(1) is_tilted++ + +/mob/proc/has_spell(spelltype) + if (!mind) + return FALSE + + for(var/obj/effect/proc_holder/spell/S in mind.spell_list) + if(S.type == spelltype) + return TRUE + return FALSE diff --git a/tgstation.dme b/tgstation.dme index fdb1ebe1b1..9f6a142747 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -4230,6 +4230,7 @@ #include "modular_splurt\code\modules\admin\NewBan.dm" #include "modular_splurt\code\modules\admin\Kick.dm" #include "modular_splurt\code\modules\admin\JobBan.dm" +#include "modular_splurt\code\modules\admin\Transform.dm" #include "modular_splurt\code\game\atoms.dm" #include "modular_splurt\code\game\data_huds.dm" #include "modular_splurt\code\game\area\areas\centcom.dm" diff --git a/tgui/packages/tgui/interfaces/PlayerPanel2.js b/tgui/packages/tgui/interfaces/PlayerPanel2.js index 7d62ba0144..666e707a70 100644 --- a/tgui/packages/tgui/interfaces/PlayerPanel2.js +++ b/tgui/packages/tgui/interfaces/PlayerPanel2.js @@ -1,6 +1,6 @@ import { Fragment } from "inferno"; import { useBackend, useLocalState } from '../backend'; -import { Input, Button, Flex, Section, Tabs, Box, NoticeBox, Collapsible, Dropdown, Slider, Tooltip } from '../components'; +import { Input, Button, Flex, Section, Tabs, Box, NoticeBox, NumberInput, Collapsible, LabeledList, Dropdown, Slider, Tooltip } from '../components'; import { Window } from '../layouts'; const PAGES = [ @@ -10,6 +10,21 @@ const PAGES = [ color: "green", icon: "tools", }, + { + title: 'Mob', + component: () => PhysicalActions, + color: "yellow", + icon: "bolt", + canAccess: data => { + return !!data.mob_type.includes("/mob/living"); + }, + }, + { + title: 'Transform', + component: () => TransformActions, + color: "orange", + icon: "exchange-alt", + }, { title: 'Punish', component: () => PunishmentActions, @@ -20,48 +35,33 @@ const PAGES = [ }, }, { - title: 'Job Bans', - component: () => Jobbans, + title: 'Feature Bans', + component: () => FeatureBanTabs, color: "red", icon: "gavel", canAccess: data => { return data.client_ckey; }, }, - // { - // title: 'Physical', - // component: () => PhysicalActions, - // color: "red", - // icon: "bolt", - // canAccess: data => { - // return !!data.is_human || !!data.is_xeno; - // }, - // }, - // { - // title: 'Transform', - // component: () => TransformActions, - // color: "orange", - // icon: "exchange-alt", - // canAccess: data => { - // return hasPermission(data, "mob_transform"); - // }, - // }, - // { - // title: 'Fun', - // component: () => FunActions, - // color: "blue", - // icon: "laugh", - // canAccess: data => { - // return hasPermission(data, "fun"); - // }, - // }, + { + title: 'Fun', + component: () => FunActions, + color: "blue", + icon: "laugh", + }, + { + title: 'aaaaaa', + component: () => FunActions, + color: "blue", + icon: "laugh", + }, // { // title: 'Antag', // component: () => AntagActions, // color: "blue", // icon: "crosshairs", // canAccess: data => { - // return (data.is_human || data.is_xeno); + // return (data.is_type_mob_human || data.is_xeno); // }, // }, ]; @@ -71,8 +71,8 @@ export const PlayerPanel2 = (props, context) => { const [pageIndex, setPageIndex] = useLocalState(context, 'pageIndex', 0); const PageComponent = PAGES[pageIndex].component(); - const { mob_name, mob_type, client_key, client_ckey, client_rank, - playtimes_enabled, playtime } = data; + const { mob_name, mob_type, client_ckey, client_rank, playtimes_enabled, + playtime } = data; return ( { {!!client_ckey && ( Client: - {client_key} + {client_ckey} @@ -182,10 +182,149 @@ export const PlayerPanel2 = (props, context) => { ); }; -const Jobbans = (props, context) => { +const PhysicalActions = (props, context) => { const { act, data } = useBackend(context); + const [mobScale, setMobScale] = useLocalState(context, 'mobScale', 1); + const { glob_limbs, godmode, mob_type } = data; + const limbs = Object.keys(glob_limbs); + const limb_flags = limbs.map((_, i) => (1< +
act("toggle_godmode")} + /> + }> + +
+
+ {limbs.map((val, index) => ( + setDelimbOption( + (delimbOption & limb_flags[index]) + ? delimbOption & ~limb_flags[index] + : delimbOption|limb_flags[index] + )} + /> + ))} + + )}> + + act("limb", { + limbs: limb_flags.map((val, index) => + !!(delimbOption & val) && glob_limbs[limbs[index]] + ), + delimb_mode: true, + })} + /> + act("limb", { + limbs: limb_flags.map((val, index) => + !!(delimbOption & val) && glob_limbs[limbs[index]] + ), + })} + /> + +
+
+ + { + setMobScale(value); // Update slider value + act("scale", { new_scale: value }); // Update mob's value + }} + unit="x" + /> + +
+
+ + Force Say: + + act("force_say", { to_say: value })} + /> + + + + Force Emote: + + act("force_emote", { to_emote: value })} + /> + + +
+
+ ); +}; + + +const FeatureBanTabs = (props, context) => { + const { data } = useBackend(context); const [jobbanTab, setJobbanTab] = useLocalState(context, 'jobbanTab', 0); - const { client_key, is_type_mob_living, roles } = data; + const { roles } = data; return ( @@ -206,22 +345,32 @@ const Jobbans = (props, context) => { - + ); }; -const Joban2 = (props, context) => { +const FeatureBans = (props, context) => { const { act, data } = useBackend(context); const [jobbanTab] = useLocalState(context, 'jobbanTab', 0); - const { roles, is_antag_banned } = data; + const { roles, antag_ban_reason } = data; return (
+
-
- +
+ {
-
- +
+ +
); @@ -417,55 +546,60 @@ const GeneralActions = (props, context) => { const PunishmentActions = (props, context) => { const { act, data } = useBackend(context); - const { client_key, is_type_mob_living, is_frozen, is_slept, - glob_mute_bits, client_muted } = data; + const { is_type_mob_living, is_frozen, is_slept, glob_mute_bits, + client_muted, data_related_cid, data_related_ip, data_byond_version, + data_player_join_date, data_account_join_date, active_role_ban_count, + current_time } = data; return ( -
-
- - +
+ +
+ act("kick")} /> - -
- -
-
-
- +
- -
- +
+
+ + Related accounts by: + +
); }; -// const TransformActions = (props, context) => { -// const { act, data } = useBackend(context); -// const { glob_pp_transformables } = data; -// return ( -//
-// {Object.keys(glob_pp_transformables).map((element, i) => ( -//
-// -// {glob_pp_transformables[element].map((option, optionIndex) => ( -// act("mob_transform", { key: option.key })} -// /> -// ))} -// -//
-// ))} -//
-// ); -// }; +const TransformActions = (props, context) => { + const { act, data } = useBackend(context); + const { transformables, mob_type } = data; + return ( +
-// const FunActions = (props, context) => { -// const { act, data } = useBackend(context); -// const { glob_span } = data; -// const [getSpanSetting, setSpanSetting] = useLocalState(context, "span_setting", glob_span[0].span); + -// -// -// setExpPower(value)} -// ranges={{ -// good: [0, 200], -// average: [200, 400], -// bad: [400, 500], -// }} -// minValue={0} -// maxValue={500} -// /> -// setFalloff(value)} -// ranges={{ -// bad: [0, 10], -// average: [10, 75], -// good: [75, 200], -// }} -// minValue={1} -// maxValue={200} -// mt={1} -// stepPixelSize={2} -// /> -// -// -//
-// )} -//
-// ); -// }; +const FunActions = (props, context) => { + const { act, data } = useBackend(context); + const { glob_span } = data; + const [lockExplode, setLockExplode] = useLocalState(context, "explode_lock_toggle", true); + const [expPower, setExpPower] = useLocalState(context, "exp_power", 8); + const [narrateSize, setNarrateSize] = useLocalState(context, "narrateSize", 1); + const [narrateMessage, setNarrateMessage] = useLocalState(context, "narrateMessage", ""); + const [narrateColour, setNarrateColour] = useLocalState(context, "narrateColour", "White"); + const [narrateFont, setNarrateFont] = useLocalState(context, "narrateFont", "Verdana"); + const [narrateBold, setNarrateBold] = useLocalState(context, "narrateBold", false); + const [narrateItalic, setNarrateItalic] = useLocalState(context, "narrateItalic", false); + const [narrateGlobal, setNarrateGlobal] = useLocalState(context, "narrateGlobal", false); + const [narrateRange, setNarrateRange] = useLocalState(context, "narrateRange", 7); + + const narrateStyles = { + 'color': narrateColour, + 'font-size': narrateSize + 'rem', + 'font-weight': (narrateBold ? 'bold' : ''), + 'font-family': narrateFont, + 'font-style': (narrateItalic ? 'italic' : ''), + }; + + return ( +
+ + These features are centred on YOUR viewport + + +
setLockExplode(!lockExplode)} + color={lockExplode? "good" : "bad"} + /> + )}> + + + + + + setExpPower(value)} + ranges={{ + good: [0, 8], + average: [8, 15], + bad: [15, 30], + }} + minValue={1} + maxValue={30} + height="100%" + /> + + +
+
setNarrateGlobal(!narrateGlobal)} + /> + }> + + + + + + setNarrateColour(value)} /> + + + setNarrateFont(value)} /> + + + + + + + setNarrateBold(!narrateBold)} + /> + + + setNarrateItalic(!narrateItalic)} + /> + + + + + + + setNarrateSize(value)} /> + + {!narrateGlobal && ( + + setNarrateRange(value)} /> + + )} + + + + + + + + setNarrateMessage(value)} + /> + + +
+
+ ); +}; // const AntagActions = (props, context) => { // const { act, data } = useBackend(context); -// const { glob_hives, is_xeno, is_human } = data; +// const { glob_hives, is_xeno, is_type_mob_human } = data; // const [selectedHivenumber, setHivenumber] = useLocalState(context, "selected_hivenumber", Object.keys(glob_hives)[0]); // return ( //
-// {!!is_human && ( +// {!!is_type_mob_human && ( //
// { // grow={1} // mt={1} // > -// {!!is_human && ( +// {!!is_type_mob_human && ( // //
// ); // }; - -// const PhysicalActions = (props, context) => { -// const { act, data } = useBackend(context); -// const { -// is_human, glob_limbs, mob_speed, -// mob_status_flags, glob_status_flags, -// mob_feels_pain, -// } = data; - -// const limbs = Object.keys(glob_limbs); -// const limb_flags = limbs.map((_, i) => (1< -//
-// {Object.keys(glob_status_flags).map((val, i) => ( -// act("set_status_flags", -// { -// status_flags: -// (mob_status_flags & glob_status_flags[val]) -// ? mob_status_flags & ~glob_status_flags[val] -// : mob_status_flags|glob_status_flags[val], -// } -// )} -// /> -// ))} -// act("set_pain", { feels_pain: !mob_feels_pain })} -// /> -//
-// {!!is_human && ( -//
-// {limbs.map((val, index) => ( -// setDelimbOption( -// (delimbOption & limb_flags[index]) -// ? delimbOption & ~limb_flags[index] -// : delimbOption|limb_flags[index] -// )} -// /> -// ))} -// -// )}> -// -// act("mob_delimb", { -// limbs: limb_flags.map((val, index) => -// !!(delimbOption & val) && glob_limbs[limbs[index]] -// ), -// })} -// /> -// act("mob_relimb", { -// limbs: limb_flags.map((val, index) => -// !!(delimbOption & val) && glob_limbs[limbs[index]] -// ), -// })} -// /> -// -//
-// )} -//
-// -// {!!is_human && ( -// act("cryo_human")} -// > -// -// -// )} -// act("strip_equipment", { drop_items: true })} -// /> -// -// -// {hasPermission(data, "set_speed") && ( -// act("set_speed", { speed: -value })} -// unit="Mob Speed" -// /> -// )} -// -//
-//
-// -//
-//
-// ); -// }; From d55f509282a124feb8798f5dae2346cb22eb5638 Mon Sep 17 00:00:00 2001 From: James Date: Sat, 26 Feb 2022 06:42:19 +0000 Subject: [PATCH 06/15] Cleanup --- code/modules/admin/admin_verbs.dm | 2 +- code/modules/admin/player_panel2.dm | 55 +++- code/modules/admin/topic.dm | 2 +- code/modules/admin/verbs/randomverbs.dm | 9 +- html/font-awesome/css/all.min.css | 2 +- .../code/modules/admin/Transform.dm | 8 +- .../code/modules/client/client_defines.dm | 1 - tgstation.dme | 1 - tgui/packages/tgui/interfaces/JobbanPanel.js | 145 ----------- tgui/packages/tgui/interfaces/PlayerPanel2.js | 245 ++++++++---------- 10 files changed, 169 insertions(+), 301 deletions(-) delete mode 100644 modular_splurt/code/modules/client/client_defines.dm delete mode 100644 tgui/packages/tgui/interfaces/JobbanPanel.js diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index a408643826..eb0522202c 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -229,7 +229,7 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list( /datum/admins/proc/set_admin_notice, /client/proc/admin_ghost, /client/proc/toggle_view_range, - /client/proc/cmd_admin_subtle_message, + // /client/proc/cmd_admin_subtle_message, /client/proc/cmd_admin_check_contents, /datum/admins/proc/access_news_network, /client/proc/admin_end_shift, diff --git a/code/modules/admin/player_panel2.dm b/code/modules/admin/player_panel2.dm index a96350eaad..d2b8b97cb0 100644 --- a/code/modules/admin/player_panel2.dm +++ b/code/modules/admin/player_panel2.dm @@ -75,16 +75,15 @@ GLOBAL_LIST_INIT(pp_limbs, list( /datum/player_panel/ui_data(mob/user) . = list() .["mob_name"] = targetMob.real_name - .["current_permissions"] = user.client?.holder?.rank.rights + // .["current_permissions"] = user.client?.holder?.rank.rights .["mob_type"] = targetMob.type + .["admin_mob_type"] = user.client?.mob.type .["godmode"] = targetMob.status_flags & GODMODE var/mob/living/L = targetMob if (istype(L)) - .["is_type_mob_living"] = TRUE .["is_frozen"] = L.admin_frozen .["is_slept"] = L.admin_sleeping - .["is_type_mob_human"] = ishuman(L) .["mob_scale"] = mobSize if(targetMob.client) @@ -113,7 +112,9 @@ GLOBAL_LIST_INIT(pp_limbs, list( .["glob_mute_bits"] = GLOB.mute_bits .["current_time"] = time2text(world.timeofday, "YYYY-MM-DD hh:mm:ss") - if(targetMob.client) + .["initial_scale"] = 1 + + if(targetClient) var/byond_version = "Unknown" if(targetClient.byond_version) byond_version = "[targetClient.byond_version].[targetClient.byond_build ? targetClient.byond_build : "xxx"]" @@ -123,6 +124,8 @@ GLOBAL_LIST_INIT(pp_limbs, list( .["data_related_cid"] = targetClient.related_accounts_cid .["data_related_ip"] = targetClient.related_accounts_ip + .["initial_scale"] = targetClient.prefs.features["body_size"] + if(CONFIG_GET(flag/use_exp_tracking)) .["playtimes_enabled"] = TRUE .["playtime"] = targetMob.client.get_exp_living() @@ -180,9 +183,24 @@ GLOBAL_LIST_INIT(pp_limbs, list( log_admin("[key_name(admin)] ejected [key_name(targetMob)] from their body.") message_admins("[key_name_admin(admin)] ejected [key_name_admin(targetMob)] from their body.") to_chat(targetMob, "An admin has ejected you from your body.") - targetMob.ghostize(FALSE) + targetMob.ghostize(FALSE, penalize = TRUE, voluntary = TRUE) targetMob.mind.key = null + if ("offer_control") + offer_control(targetMob) + + if ("take_control") + if(targetMob.ckey) + var/mob/dead/observer/ghost = new/mob/dead/observer(get_turf(targetMob), targetMob) + ghost.ckey = targetMob.ckey + + message_admins("[key_name_admin(usr)] assumed direct control of [targetMob].") + log_admin("[key_name(usr)] assumed direct control of [targetMob].") + var/mob/adminmob = admin.mob + adminmob.transfer_ckey(targetMob, send_signal = FALSE) + if(isobserver(adminmob)) + qdel(adminmob) + if ("smite") admin.smite(targetMob) @@ -366,12 +384,17 @@ GLOBAL_LIST_INIT(pp_limbs, list( if ("explode") var/power = text2num(params["power"]) + var/empMode = text2num(params["emp_mode"]) + var/turf/T = get_turf(usr) - message_admins("[ADMIN_LOOKUPFLW(usr)] created an admin explosion at [ADMIN_VERBOSEJMP(T)].") - log_admin("[key_name(usr)] created an admin explosion at [usr.loc].") + message_admins("[ADMIN_LOOKUPFLW(usr)] created an admin [empMode ? "EMP" : "explosion"] at [ADMIN_VERBOSEJMP(T)].") + log_admin("[key_name(usr)] created an admin [empMode ? "EMP" : "explosion"] at [usr.loc].") - explosion(usr, power / 3, power / 2, power, power, ignorecap = TRUE) + if (empMode) + empulse_using_range(usr, power, TRUE) + else + explosion(usr, power / 3, power / 2, power, power, ignorecap = TRUE) if ("narrate") var/list/stylesRaw = params["classes"] @@ -391,6 +414,22 @@ GLOBAL_LIST_INIT(pp_limbs, list( log_admin("LocalNarrate: [key_name(usr)] at [AREACOORD(usr)]: [params["message"]]") message_admins(" LocalNarrate: [key_name_admin(usr)] at [ADMIN_VERBOSEJMP(usr)]: [params["message"]]
") + if ("languages") + var/datum/language_holder/H = targetMob.get_language_holder() + H.open_language_menu(usr) + + if ("ambitions") + // if (!targetMob.client) + // return + + var/datum/mind/requesting_mind = targetMob.mind + if(!istype(requesting_mind) || QDELETED(requesting_mind)) + to_chat(usr, "This mind reference is no longer valid. It has probably since been destroyed.") + return + requesting_mind.do_edit_objectives_ambitions() + + if ("traitor_panel") + admin.holder.show_traitor_panel(targetMob) // process_banlist: Gets all jobs in a job category // Input: diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 25cb5bd5cb..df1fbc9244 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -2103,7 +2103,7 @@ if(!check_rights(R_ADMIN)) return - priority_announce("what is this") + priority_announce("DIGDUG - OLD PP SM VERB") var/mob/M = locate(href_list["subtlemessage"]) usr.client.cmd_admin_subtle_message(M) diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 96cbfc6c21..20325f74dc 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -19,10 +19,14 @@ admin_ticket_log(M, msg) SSblackbox.record_feedback("tally", "admin_verb", 1, "Drop Everything") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! -/client/proc/cmd_admin_subtle_message(mob/M in GLOB.mob_list, sender = null) - set category = null +/client/proc/cmd_admin_subtle_message(mob/M in GLOB.mob_list) //, sender = null) + set category = "Admin.Events" set name = "Subtle Message" + var/sender = null + + priority_announce("AAAAAAAAAAAAAAAAAAAAAAA") + if(!ismob(M)) priority_announce("no mob!") return @@ -31,6 +35,7 @@ return if (!sender) + priority_announce("lol") var/list/subtle_message_options = list("Voice in head", RADIO_CHANNEL_CENTCOM, RADIO_CHANNEL_SYNDICATE) sender = tgui_input_list(usr, "Choose the method of subtle messaging", "Subtle Message", subtle_message_options) if (!sender) diff --git a/html/font-awesome/css/all.min.css b/html/font-awesome/css/all.min.css index e7cdfffe75..a3fb0572e8 100644 --- a/html/font-awesome/css/all.min.css +++ b/html/font-awesome/css/all.min.css @@ -2,4 +2,4 @@ * Font Awesome Free 5.9.0 by @fontawesome - https://fontawesome.com * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) */ -.fa,.fab,.fal,.far,.fas{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;line-height:1}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-.0667em}.fa-xs{font-size:.75em}.fa-sm{font-size:.875em}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.fa-ul>li{position:relative}.fa-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}.fa-border{border:.08em solid #eee;border-radius:.1em;padding:.2em .25em .15em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.fab.fa-pull-left,.fal.fa-pull-left,.far.fa-pull-left,.fas.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.fab.fa-pull-right,.fal.fa-pull-right,.far.fa-pull-right,.fas.fa-pull-right{margin-left:.3em}.fa-spin{animation:fa-spin 2s infinite linear}.fa-pulse{animation:fa-spin 1s infinite steps(8)}@keyframes fa-spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";transform:scaleX(-1)}.fa-flip-vertical{transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical,.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{transform:scale(-1)}:root .fa-flip-both,:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{filter:none}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-500px:before{content:"\f26e"}.fa-accessible-icon:before{content:"\f368"}.fa-accusoft:before{content:"\f369"}.fa-acquisitions-incorporated:before{content:"\f6af"}.fa-ad:before{content:"\f641"}.fa-address-book:before{content:"\f2b9"}.fa-address-card:before{content:"\f2bb"}.fa-adjust:before{content:"\f042"}.fa-adn:before{content:"\f170"}.fa-adobe:before{content:"\f778"}.fa-adversal:before{content:"\f36a"}.fa-affiliatetheme:before{content:"\f36b"}.fa-air-freshener:before{content:"\f5d0"}.fa-airbnb:before{content:"\f834"}.fa-algolia:before{content:"\f36c"}.fa-align-center:before{content:"\f037"}.fa-align-justify:before{content:"\f039"}.fa-align-left:before{content:"\f036"}.fa-align-right:before{content:"\f038"}.fa-alipay:before{content:"\f642"}.fa-allergies:before{content:"\f461"}.fa-amazon:before{content:"\f270"}.fa-amazon-pay:before{content:"\f42c"}.fa-ambulance:before{content:"\f0f9"}.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-amilia:before{content:"\f36d"}.fa-anchor:before{content:"\f13d"}.fa-android:before{content:"\f17b"}.fa-angellist:before{content:"\f209"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-down:before{content:"\f107"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angry:before{content:"\f556"}.fa-angrycreative:before{content:"\f36e"}.fa-angular:before{content:"\f420"}.fa-ankh:before{content:"\f644"}.fa-app-store:before{content:"\f36f"}.fa-app-store-ios:before{content:"\f370"}.fa-apper:before{content:"\f371"}.fa-apple:before{content:"\f179"}.fa-apple-alt:before{content:"\f5d1"}.fa-apple-pay:before{content:"\f415"}.fa-archive:before{content:"\f187"}.fa-archway:before{content:"\f557"}.fa-arrow-alt-circle-down:before{content:"\f358"}.fa-arrow-alt-circle-left:before{content:"\f359"}.fa-arrow-alt-circle-right:before{content:"\f35a"}.fa-arrow-alt-circle-up:before{content:"\f35b"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-down:before{content:"\f063"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrows-alt:before{content:"\f0b2"}.fa-arrows-alt-h:before{content:"\f337"}.fa-arrows-alt-v:before{content:"\f338"}.fa-artstation:before{content:"\f77a"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asterisk:before{content:"\f069"}.fa-asymmetrik:before{content:"\f372"}.fa-at:before{content:"\f1fa"}.fa-atlas:before{content:"\f558"}.fa-atlassian:before{content:"\f77b"}.fa-atom:before{content:"\f5d2"}.fa-audible:before{content:"\f373"}.fa-audio-description:before{content:"\f29e"}.fa-autoprefixer:before{content:"\f41c"}.fa-avianex:before{content:"\f374"}.fa-aviato:before{content:"\f421"}.fa-award:before{content:"\f559"}.fa-aws:before{content:"\f375"}.fa-baby:before{content:"\f77c"}.fa-baby-carriage:before{content:"\f77d"}.fa-backspace:before{content:"\f55a"}.fa-backward:before{content:"\f04a"}.fa-bacon:before{content:"\f7e5"}.fa-balance-scale:before{content:"\f24e"}.fa-balance-scale-left:before{content:"\f515"}.fa-balance-scale-right:before{content:"\f516"}.fa-ban:before{content:"\f05e"}.fa-band-aid:before{content:"\f462"}.fa-bandcamp:before{content:"\f2d5"}.fa-barcode:before{content:"\f02a"}.fa-bars:before{content:"\f0c9"}.fa-baseball-ball:before{content:"\f433"}.fa-basketball-ball:before{content:"\f434"}.fa-bath:before{content:"\f2cd"}.fa-battery-empty:before{content:"\f244"}.fa-battery-full:before{content:"\f240"}.fa-battery-half:before{content:"\f242"}.fa-battery-quarter:before{content:"\f243"}.fa-battery-three-quarters:before{content:"\f241"}.fa-battle-net:before{content:"\f835"}.fa-bed:before{content:"\f236"}.fa-beer:before{content:"\f0fc"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-bell:before{content:"\f0f3"}.fa-bell-slash:before{content:"\f1f6"}.fa-bezier-curve:before{content:"\f55b"}.fa-bible:before{content:"\f647"}.fa-bicycle:before{content:"\f206"}.fa-biking:before{content:"\f84a"}.fa-bimobject:before{content:"\f378"}.fa-binoculars:before{content:"\f1e5"}.fa-biohazard:before{content:"\f780"}.fa-birthday-cake:before{content:"\f1fd"}.fa-bitbucket:before{content:"\f171"}.fa-bitcoin:before{content:"\f379"}.fa-bity:before{content:"\f37a"}.fa-black-tie:before{content:"\f27e"}.fa-blackberry:before{content:"\f37b"}.fa-blender:before{content:"\f517"}.fa-blender-phone:before{content:"\f6b6"}.fa-blind:before{content:"\f29d"}.fa-blog:before{content:"\f781"}.fa-blogger:before{content:"\f37c"}.fa-blogger-b:before{content:"\f37d"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-bold:before{content:"\f032"}.fa-bolt:before{content:"\f0e7"}.fa-bomb:before{content:"\f1e2"}.fa-bone:before{content:"\f5d7"}.fa-bong:before{content:"\f55c"}.fa-book:before{content:"\f02d"}.fa-book-dead:before{content:"\f6b7"}.fa-book-medical:before{content:"\f7e6"}.fa-book-open:before{content:"\f518"}.fa-book-reader:before{content:"\f5da"}.fa-bookmark:before{content:"\f02e"}.fa-bootstrap:before{content:"\f836"}.fa-border-all:before{content:"\f84c"}.fa-border-none:before{content:"\f850"}.fa-border-style:before{content:"\f853"}.fa-bowling-ball:before{content:"\f436"}.fa-box:before{content:"\f466"}.fa-box-open:before{content:"\f49e"}.fa-boxes:before{content:"\f468"}.fa-braille:before{content:"\f2a1"}.fa-brain:before{content:"\f5dc"}.fa-bread-slice:before{content:"\f7ec"}.fa-briefcase:before{content:"\f0b1"}.fa-briefcase-medical:before{content:"\f469"}.fa-broadcast-tower:before{content:"\f519"}.fa-broom:before{content:"\f51a"}.fa-brush:before{content:"\f55d"}.fa-btc:before{content:"\f15a"}.fa-buffer:before{content:"\f837"}.fa-bug:before{content:"\f188"}.fa-building:before{content:"\f1ad"}.fa-bullhorn:before{content:"\f0a1"}.fa-bullseye:before{content:"\f140"}.fa-burn:before{content:"\f46a"}.fa-buromobelexperte:before{content:"\f37f"}.fa-bus:before{content:"\f207"}.fa-bus-alt:before{content:"\f55e"}.fa-business-time:before{content:"\f64a"}.fa-buysellads:before{content:"\f20d"}.fa-calculator:before{content:"\f1ec"}.fa-calendar:before{content:"\f133"}.fa-calendar-alt:before{content:"\f073"}.fa-calendar-check:before{content:"\f274"}.fa-calendar-day:before{content:"\f783"}.fa-calendar-minus:before{content:"\f272"}.fa-calendar-plus:before{content:"\f271"}.fa-calendar-times:before{content:"\f273"}.fa-calendar-week:before{content:"\f784"}.fa-camera:before{content:"\f030"}.fa-camera-retro:before{content:"\f083"}.fa-campground:before{content:"\f6bb"}.fa-canadian-maple-leaf:before{content:"\f785"}.fa-candy-cane:before{content:"\f786"}.fa-cannabis:before{content:"\f55f"}.fa-capsules:before{content:"\f46b"}.fa-car:before{content:"\f1b9"}.fa-car-alt:before{content:"\f5de"}.fa-car-battery:before{content:"\f5df"}.fa-car-crash:before{content:"\f5e1"}.fa-car-side:before{content:"\f5e4"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-caret-square-down:before{content:"\f150"}.fa-caret-square-left:before{content:"\f191"}.fa-caret-square-right:before{content:"\f152"}.fa-caret-square-up:before{content:"\f151"}.fa-caret-up:before{content:"\f0d8"}.fa-carrot:before{content:"\f787"}.fa-cart-arrow-down:before{content:"\f218"}.fa-cart-plus:before{content:"\f217"}.fa-cash-register:before{content:"\f788"}.fa-cat:before{content:"\f6be"}.fa-cc-amazon-pay:before{content:"\f42d"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-apple-pay:before{content:"\f416"}.fa-cc-diners-club:before{content:"\f24c"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-cc-visa:before{content:"\f1f0"}.fa-centercode:before{content:"\f380"}.fa-centos:before{content:"\f789"}.fa-certificate:before{content:"\f0a3"}.fa-chair:before{content:"\f6c0"}.fa-chalkboard:before{content:"\f51b"}.fa-chalkboard-teacher:before{content:"\f51c"}.fa-charging-station:before{content:"\f5e7"}.fa-chart-area:before{content:"\f1fe"}.fa-chart-bar:before{content:"\f080"}.fa-chart-line:before{content:"\f201"}.fa-chart-pie:before{content:"\f200"}.fa-check:before{content:"\f00c"}.fa-check-circle:before{content:"\f058"}.fa-check-double:before{content:"\f560"}.fa-check-square:before{content:"\f14a"}.fa-cheese:before{content:"\f7ef"}.fa-chess:before{content:"\f439"}.fa-chess-bishop:before{content:"\f43a"}.fa-chess-board:before{content:"\f43c"}.fa-chess-king:before{content:"\f43f"}.fa-chess-knight:before{content:"\f441"}.fa-chess-pawn:before{content:"\f443"}.fa-chess-queen:before{content:"\f445"}.fa-chess-rook:before{content:"\f447"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-down:before{content:"\f078"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-chevron-up:before{content:"\f077"}.fa-child:before{content:"\f1ae"}.fa-chrome:before{content:"\f268"}.fa-chromecast:before{content:"\f838"}.fa-church:before{content:"\f51d"}.fa-circle:before{content:"\f111"}.fa-circle-notch:before{content:"\f1ce"}.fa-city:before{content:"\f64f"}.fa-clinic-medical:before{content:"\f7f2"}.fa-clipboard:before{content:"\f328"}.fa-clipboard-check:before{content:"\f46c"}.fa-clipboard-list:before{content:"\f46d"}.fa-clock:before{content:"\f017"}.fa-clone:before{content:"\f24d"}.fa-closed-captioning:before{content:"\f20a"}.fa-cloud:before{content:"\f0c2"}.fa-cloud-download-alt:before{content:"\f381"}.fa-cloud-meatball:before{content:"\f73b"}.fa-cloud-moon:before{content:"\f6c3"}.fa-cloud-moon-rain:before{content:"\f73c"}.fa-cloud-rain:before{content:"\f73d"}.fa-cloud-showers-heavy:before{content:"\f740"}.fa-cloud-sun:before{content:"\f6c4"}.fa-cloud-sun-rain:before{content:"\f743"}.fa-cloud-upload-alt:before{content:"\f382"}.fa-cloudscale:before{content:"\f383"}.fa-cloudsmith:before{content:"\f384"}.fa-cloudversify:before{content:"\f385"}.fa-cocktail:before{content:"\f561"}.fa-code:before{content:"\f121"}.fa-code-branch:before{content:"\f126"}.fa-codepen:before{content:"\f1cb"}.fa-codiepie:before{content:"\f284"}.fa-coffee:before{content:"\f0f4"}.fa-cog:before{content:"\f013"}.fa-cogs:before{content:"\f085"}.fa-coins:before{content:"\f51e"}.fa-columns:before{content:"\f0db"}.fa-comment:before{content:"\f075"}.fa-comment-alt:before{content:"\f27a"}.fa-comment-dollar:before{content:"\f651"}.fa-comment-dots:before{content:"\f4ad"}.fa-comment-medical:before{content:"\f7f5"}.fa-comment-slash:before{content:"\f4b3"}.fa-comments:before{content:"\f086"}.fa-comments-dollar:before{content:"\f653"}.fa-compact-disc:before{content:"\f51f"}.fa-compass:before{content:"\f14e"}.fa-compress:before{content:"\f066"}.fa-compress-arrows-alt:before{content:"\f78c"}.fa-concierge-bell:before{content:"\f562"}.fa-confluence:before{content:"\f78d"}.fa-connectdevelop:before{content:"\f20e"}.fa-contao:before{content:"\f26d"}.fa-cookie:before{content:"\f563"}.fa-cookie-bite:before{content:"\f564"}.fa-copy:before{content:"\f0c5"}.fa-copyright:before{content:"\f1f9"}.fa-couch:before{content:"\f4b8"}.fa-cpanel:before{content:"\f388"}.fa-creative-commons:before{content:"\f25e"}.fa-creative-commons-by:before{content:"\f4e7"}.fa-creative-commons-nc:before{content:"\f4e8"}.fa-creative-commons-nc-eu:before{content:"\f4e9"}.fa-creative-commons-nc-jp:before{content:"\f4ea"}.fa-creative-commons-nd:before{content:"\f4eb"}.fa-creative-commons-pd:before{content:"\f4ec"}.fa-creative-commons-pd-alt:before{content:"\f4ed"}.fa-creative-commons-remix:before{content:"\f4ee"}.fa-creative-commons-sa:before{content:"\f4ef"}.fa-creative-commons-sampling:before{content:"\f4f0"}.fa-creative-commons-sampling-plus:before{content:"\f4f1"}.fa-creative-commons-share:before{content:"\f4f2"}.fa-creative-commons-zero:before{content:"\f4f3"}.fa-credit-card:before{content:"\f09d"}.fa-critical-role:before{content:"\f6c9"}.fa-crop:before{content:"\f125"}.fa-crop-alt:before{content:"\f565"}.fa-cross:before{content:"\f654"}.fa-crosshairs:before{content:"\f05b"}.fa-crow:before{content:"\f520"}.fa-crown:before{content:"\f521"}.fa-crutch:before{content:"\f7f7"}.fa-css3:before{content:"\f13c"}.fa-css3-alt:before{content:"\f38b"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-cut:before{content:"\f0c4"}.fa-cuttlefish:before{content:"\f38c"}.fa-d-and-d:before{content:"\f38d"}.fa-d-and-d-beyond:before{content:"\f6ca"}.fa-dashcube:before{content:"\f210"}.fa-database:before{content:"\f1c0"}.fa-deaf:before{content:"\f2a4"}.fa-delicious:before{content:"\f1a5"}.fa-democrat:before{content:"\f747"}.fa-deploydog:before{content:"\f38e"}.fa-deskpro:before{content:"\f38f"}.fa-desktop:before{content:"\f108"}.fa-dev:before{content:"\f6cc"}.fa-deviantart:before{content:"\f1bd"}.fa-dharmachakra:before{content:"\f655"}.fa-dhl:before{content:"\f790"}.fa-diagnoses:before{content:"\f470"}.fa-diaspora:before{content:"\f791"}.fa-dice:before{content:"\f522"}.fa-dice-d20:before{content:"\f6cf"}.fa-dice-d6:before{content:"\f6d1"}.fa-dice-five:before{content:"\f523"}.fa-dice-four:before{content:"\f524"}.fa-dice-one:before{content:"\f525"}.fa-dice-six:before{content:"\f526"}.fa-dice-three:before{content:"\f527"}.fa-dice-two:before{content:"\f528"}.fa-digg:before{content:"\f1a6"}.fa-digital-ocean:before{content:"\f391"}.fa-digital-tachograph:before{content:"\f566"}.fa-directions:before{content:"\f5eb"}.fa-discord:before{content:"\f392"}.fa-discourse:before{content:"\f393"}.fa-divide:before{content:"\f529"}.fa-dizzy:before{content:"\f567"}.fa-dna:before{content:"\f471"}.fa-dochub:before{content:"\f394"}.fa-docker:before{content:"\f395"}.fa-dog:before{content:"\f6d3"}.fa-dollar-sign:before{content:"\f155"}.fa-dolly:before{content:"\f472"}.fa-dolly-flatbed:before{content:"\f474"}.fa-donate:before{content:"\f4b9"}.fa-door-closed:before{content:"\f52a"}.fa-door-open:before{content:"\f52b"}.fa-dot-circle:before{content:"\f192"}.fa-dove:before{content:"\f4ba"}.fa-download:before{content:"\f019"}.fa-draft2digital:before{content:"\f396"}.fa-drafting-compass:before{content:"\f568"}.fa-dragon:before{content:"\f6d5"}.fa-draw-polygon:before{content:"\f5ee"}.fa-dribbble:before{content:"\f17d"}.fa-dribbble-square:before{content:"\f397"}.fa-dropbox:before{content:"\f16b"}.fa-drum:before{content:"\f569"}.fa-drum-steelpan:before{content:"\f56a"}.fa-drumstick-bite:before{content:"\f6d7"}.fa-drupal:before{content:"\f1a9"}.fa-dumbbell:before{content:"\f44b"}.fa-dumpster:before{content:"\f793"}.fa-dumpster-fire:before{content:"\f794"}.fa-dungeon:before{content:"\f6d9"}.fa-dyalog:before{content:"\f399"}.fa-earlybirds:before{content:"\f39a"}.fa-ebay:before{content:"\f4f4"}.fa-edge:before{content:"\f282"}.fa-edit:before{content:"\f044"}.fa-egg:before{content:"\f7fb"}.fa-eject:before{content:"\f052"}.fa-elementor:before{content:"\f430"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-ello:before{content:"\f5f1"}.fa-ember:before{content:"\f423"}.fa-empire:before{content:"\f1d1"}.fa-envelope:before{content:"\f0e0"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-text:before{content:"\f658"}.fa-envelope-square:before{content:"\f199"}.fa-envira:before{content:"\f299"}.fa-equals:before{content:"\f52c"}.fa-eraser:before{content:"\f12d"}.fa-erlang:before{content:"\f39d"}.fa-ethereum:before{content:"\f42e"}.fa-ethernet:before{content:"\f796"}.fa-etsy:before{content:"\f2d7"}.fa-euro-sign:before{content:"\f153"}.fa-evernote:before{content:"\f839"}.fa-exchange-alt:before{content:"\f362"}.fa-exclamation:before{content:"\f12a"}.fa-exclamation-circle:before{content:"\f06a"}.fa-exclamation-triangle:before{content:"\f071"}.fa-expand:before{content:"\f065"}.fa-expand-arrows-alt:before{content:"\f31e"}.fa-expeditedssl:before{content:"\f23e"}.fa-external-link-alt:before{content:"\f35d"}.fa-external-link-square-alt:before{content:"\f360"}.fa-eye:before{content:"\f06e"}.fa-eye-dropper:before{content:"\f1fb"}.fa-eye-slash:before{content:"\f070"}.fa-facebook:before{content:"\f09a"}.fa-facebook-f:before{content:"\f39e"}.fa-facebook-messenger:before{content:"\f39f"}.fa-facebook-square:before{content:"\f082"}.fa-fan:before{content:"\f863"}.fa-fantasy-flight-games:before{content:"\f6dc"}.fa-fast-backward:before{content:"\f049"}.fa-fast-forward:before{content:"\f050"}.fa-fax:before{content:"\f1ac"}.fa-feather:before{content:"\f52d"}.fa-feather-alt:before{content:"\f56b"}.fa-fedex:before{content:"\f797"}.fa-fedora:before{content:"\f798"}.fa-female:before{content:"\f182"}.fa-fighter-jet:before{content:"\f0fb"}.fa-figma:before{content:"\f799"}.fa-file:before{content:"\f15b"}.fa-file-alt:before{content:"\f15c"}.fa-file-archive:before{content:"\f1c6"}.fa-file-audio:before{content:"\f1c7"}.fa-file-code:before{content:"\f1c9"}.fa-file-contract:before{content:"\f56c"}.fa-file-csv:before{content:"\f6dd"}.fa-file-download:before{content:"\f56d"}.fa-file-excel:before{content:"\f1c3"}.fa-file-export:before{content:"\f56e"}.fa-file-image:before{content:"\f1c5"}.fa-file-import:before{content:"\f56f"}.fa-file-invoice:before{content:"\f570"}.fa-file-invoice-dollar:before{content:"\f571"}.fa-file-medical:before{content:"\f477"}.fa-file-medical-alt:before{content:"\f478"}.fa-file-pdf:before{content:"\f1c1"}.fa-file-powerpoint:before{content:"\f1c4"}.fa-file-prescription:before{content:"\f572"}.fa-file-signature:before{content:"\f573"}.fa-file-upload:before{content:"\f574"}.fa-file-video:before{content:"\f1c8"}.fa-file-word:before{content:"\f1c2"}.fa-fill:before{content:"\f575"}.fa-fill-drip:before{content:"\f576"}.fa-film:before{content:"\f008"}.fa-filter:before{content:"\f0b0"}.fa-fingerprint:before{content:"\f577"}.fa-fire:before{content:"\f06d"}.fa-fire-alt:before{content:"\f7e4"}.fa-fire-extinguisher:before{content:"\f134"}.fa-firefox:before{content:"\f269"}.fa-first-aid:before{content:"\f479"}.fa-first-order:before{content:"\f2b0"}.fa-first-order-alt:before{content:"\f50a"}.fa-firstdraft:before{content:"\f3a1"}.fa-fish:before{content:"\f578"}.fa-fist-raised:before{content:"\f6de"}.fa-flag:before{content:"\f024"}.fa-flag-checkered:before{content:"\f11e"}.fa-flag-usa:before{content:"\f74d"}.fa-flask:before{content:"\f0c3"}.fa-flickr:before{content:"\f16e"}.fa-flipboard:before{content:"\f44d"}.fa-flushed:before{content:"\f579"}.fa-fly:before{content:"\f417"}.fa-folder:before{content:"\f07b"}.fa-folder-minus:before{content:"\f65d"}.fa-folder-open:before{content:"\f07c"}.fa-folder-plus:before{content:"\f65e"}.fa-font:before{content:"\f031"}.fa-font-awesome:before{content:"\f2b4"}.fa-font-awesome-alt:before{content:"\f35c"}.fa-font-awesome-flag:before{content:"\f425"}.fa-font-awesome-logo-full:before{content:"\f4e6"}.fa-fonticons:before{content:"\f280"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-football-ball:before{content:"\f44e"}.fa-fort-awesome:before{content:"\f286"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-forumbee:before{content:"\f211"}.fa-forward:before{content:"\f04e"}.fa-foursquare:before{content:"\f180"}.fa-free-code-camp:before{content:"\f2c5"}.fa-freebsd:before{content:"\f3a4"}.fa-frog:before{content:"\f52e"}.fa-frown:before{content:"\f119"}.fa-frown-open:before{content:"\f57a"}.fa-fulcrum:before{content:"\f50b"}.fa-funnel-dollar:before{content:"\f662"}.fa-futbol:before{content:"\f1e3"}.fa-galactic-republic:before{content:"\f50c"}.fa-galactic-senate:before{content:"\f50d"}.fa-gamepad:before{content:"\f11b"}.fa-gas-pump:before{content:"\f52f"}.fa-gavel:before{content:"\f0e3"}.fa-gem:before{content:"\f3a5"}.fa-genderless:before{content:"\f22d"}.fa-get-pocket:before{content:"\f265"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-ghost:before{content:"\f6e2"}.fa-gift:before{content:"\f06b"}.fa-gifts:before{content:"\f79c"}.fa-git:before{content:"\f1d3"}.fa-git-alt:before{content:"\f841"}.fa-git-square:before{content:"\f1d2"}.fa-github:before{content:"\f09b"}.fa-github-alt:before{content:"\f113"}.fa-github-square:before{content:"\f092"}.fa-gitkraken:before{content:"\f3a6"}.fa-gitlab:before{content:"\f296"}.fa-gitter:before{content:"\f426"}.fa-glass-cheers:before{content:"\f79f"}.fa-glass-martini:before{content:"\f000"}.fa-glass-martini-alt:before{content:"\f57b"}.fa-glass-whiskey:before{content:"\f7a0"}.fa-glasses:before{content:"\f530"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-globe:before{content:"\f0ac"}.fa-globe-africa:before{content:"\f57c"}.fa-globe-americas:before{content:"\f57d"}.fa-globe-asia:before{content:"\f57e"}.fa-globe-europe:before{content:"\f7a2"}.fa-gofore:before{content:"\f3a7"}.fa-golf-ball:before{content:"\f450"}.fa-goodreads:before{content:"\f3a8"}.fa-goodreads-g:before{content:"\f3a9"}.fa-google:before{content:"\f1a0"}.fa-google-drive:before{content:"\f3aa"}.fa-google-play:before{content:"\f3ab"}.fa-google-plus:before{content:"\f2b3"}.fa-google-plus-g:before{content:"\f0d5"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-wallet:before{content:"\f1ee"}.fa-gopuram:before{content:"\f664"}.fa-graduation-cap:before{content:"\f19d"}.fa-gratipay:before{content:"\f184"}.fa-grav:before{content:"\f2d6"}.fa-greater-than:before{content:"\f531"}.fa-greater-than-equal:before{content:"\f532"}.fa-grimace:before{content:"\f57f"}.fa-grin:before{content:"\f580"}.fa-grin-alt:before{content:"\f581"}.fa-grin-beam:before{content:"\f582"}.fa-grin-beam-sweat:before{content:"\f583"}.fa-grin-hearts:before{content:"\f584"}.fa-grin-squint:before{content:"\f585"}.fa-grin-squint-tears:before{content:"\f586"}.fa-grin-stars:before{content:"\f587"}.fa-grin-tears:before{content:"\f588"}.fa-grin-tongue:before{content:"\f589"}.fa-grin-tongue-squint:before{content:"\f58a"}.fa-grin-tongue-wink:before{content:"\f58b"}.fa-grin-wink:before{content:"\f58c"}.fa-grip-horizontal:before{content:"\f58d"}.fa-grip-lines:before{content:"\f7a4"}.fa-grip-lines-vertical:before{content:"\f7a5"}.fa-grip-vertical:before{content:"\f58e"}.fa-gripfire:before{content:"\f3ac"}.fa-grunt:before{content:"\f3ad"}.fa-guitar:before{content:"\f7a6"}.fa-gulp:before{content:"\f3ae"}.fa-h-square:before{content:"\f0fd"}.fa-hacker-news:before{content:"\f1d4"}.fa-hacker-news-square:before{content:"\f3af"}.fa-hackerrank:before{content:"\f5f7"}.fa-hamburger:before{content:"\f805"}.fa-hammer:before{content:"\f6e3"}.fa-hamsa:before{content:"\f665"}.fa-hand-holding:before{content:"\f4bd"}.fa-hand-holding-heart:before{content:"\f4be"}.fa-hand-holding-usd:before{content:"\f4c0"}.fa-hand-lizard:before{content:"\f258"}.fa-hand-middle-finger:before{content:"\f806"}.fa-hand-paper:before{content:"\f256"}.fa-hand-peace:before{content:"\f25b"}.fa-hand-point-down:before{content:"\f0a7"}.fa-hand-point-left:before{content:"\f0a5"}.fa-hand-point-right:before{content:"\f0a4"}.fa-hand-point-up:before{content:"\f0a6"}.fa-hand-pointer:before{content:"\f25a"}.fa-hand-rock:before{content:"\f255"}.fa-hand-scissors:before{content:"\f257"}.fa-hand-spock:before{content:"\f259"}.fa-hands:before{content:"\f4c2"}.fa-hands-helping:before{content:"\f4c4"}.fa-handshake:before{content:"\f2b5"}.fa-hanukiah:before{content:"\f6e6"}.fa-hard-hat:before{content:"\f807"}.fa-hashtag:before{content:"\f292"}.fa-hat-wizard:before{content:"\f6e8"}.fa-haykal:before{content:"\f666"}.fa-hdd:before{content:"\f0a0"}.fa-heading:before{content:"\f1dc"}.fa-headphones:before{content:"\f025"}.fa-headphones-alt:before{content:"\f58f"}.fa-headset:before{content:"\f590"}.fa-heart:before{content:"\f004"}.fa-heart-broken:before{content:"\f7a9"}.fa-heartbeat:before{content:"\f21e"}.fa-helicopter:before{content:"\f533"}.fa-highlighter:before{content:"\f591"}.fa-hiking:before{content:"\f6ec"}.fa-hippo:before{content:"\f6ed"}.fa-hips:before{content:"\f452"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-history:before{content:"\f1da"}.fa-hockey-puck:before{content:"\f453"}.fa-holly-berry:before{content:"\f7aa"}.fa-home:before{content:"\f015"}.fa-hooli:before{content:"\f427"}.fa-hornbill:before{content:"\f592"}.fa-horse:before{content:"\f6f0"}.fa-horse-head:before{content:"\f7ab"}.fa-hospital:before{content:"\f0f8"}.fa-hospital-alt:before{content:"\f47d"}.fa-hospital-symbol:before{content:"\f47e"}.fa-hot-tub:before{content:"\f593"}.fa-hotdog:before{content:"\f80f"}.fa-hotel:before{content:"\f594"}.fa-hotjar:before{content:"\f3b1"}.fa-hourglass:before{content:"\f254"}.fa-hourglass-end:before{content:"\f253"}.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-start:before{content:"\f251"}.fa-house-damage:before{content:"\f6f1"}.fa-houzz:before{content:"\f27c"}.fa-hryvnia:before{content:"\f6f2"}.fa-html5:before{content:"\f13b"}.fa-hubspot:before{content:"\f3b2"}.fa-i-cursor:before{content:"\f246"}.fa-ice-cream:before{content:"\f810"}.fa-icicles:before{content:"\f7ad"}.fa-icons:before{content:"\f86d"}.fa-id-badge:before{content:"\f2c1"}.fa-id-card:before{content:"\f2c2"}.fa-id-card-alt:before{content:"\f47f"}.fa-igloo:before{content:"\f7ae"}.fa-image:before{content:"\f03e"}.fa-images:before{content:"\f302"}.fa-imdb:before{content:"\f2d8"}.fa-inbox:before{content:"\f01c"}.fa-indent:before{content:"\f03c"}.fa-industry:before{content:"\f275"}.fa-infinity:before{content:"\f534"}.fa-info:before{content:"\f129"}.fa-info-circle:before{content:"\f05a"}.fa-instagram:before{content:"\f16d"}.fa-intercom:before{content:"\f7af"}.fa-internet-explorer:before{content:"\f26b"}.fa-invision:before{content:"\f7b0"}.fa-ioxhost:before{content:"\f208"}.fa-italic:before{content:"\f033"}.fa-itch-io:before{content:"\f83a"}.fa-itunes:before{content:"\f3b4"}.fa-itunes-note:before{content:"\f3b5"}.fa-java:before{content:"\f4e4"}.fa-jedi:before{content:"\f669"}.fa-jedi-order:before{content:"\f50e"}.fa-jenkins:before{content:"\f3b6"}.fa-jira:before{content:"\f7b1"}.fa-joget:before{content:"\f3b7"}.fa-joint:before{content:"\f595"}.fa-joomla:before{content:"\f1aa"}.fa-journal-whills:before{content:"\f66a"}.fa-js:before{content:"\f3b8"}.fa-js-square:before{content:"\f3b9"}.fa-jsfiddle:before{content:"\f1cc"}.fa-kaaba:before{content:"\f66b"}.fa-kaggle:before{content:"\f5fa"}.fa-key:before{content:"\f084"}.fa-keybase:before{content:"\f4f5"}.fa-keyboard:before{content:"\f11c"}.fa-keycdn:before{content:"\f3ba"}.fa-khanda:before{content:"\f66d"}.fa-kickstarter:before{content:"\f3bb"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-kiss:before{content:"\f596"}.fa-kiss-beam:before{content:"\f597"}.fa-kiss-wink-heart:before{content:"\f598"}.fa-kiwi-bird:before{content:"\f535"}.fa-korvue:before{content:"\f42f"}.fa-landmark:before{content:"\f66f"}.fa-language:before{content:"\f1ab"}.fa-laptop:before{content:"\f109"}.fa-laptop-code:before{content:"\f5fc"}.fa-laptop-medical:before{content:"\f812"}.fa-laravel:before{content:"\f3bd"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-laugh:before{content:"\f599"}.fa-laugh-beam:before{content:"\f59a"}.fa-laugh-squint:before{content:"\f59b"}.fa-laugh-wink:before{content:"\f59c"}.fa-layer-group:before{content:"\f5fd"}.fa-leaf:before{content:"\f06c"}.fa-leanpub:before{content:"\f212"}.fa-lemon:before{content:"\f094"}.fa-less:before{content:"\f41d"}.fa-less-than:before{content:"\f536"}.fa-less-than-equal:before{content:"\f537"}.fa-level-down-alt:before{content:"\f3be"}.fa-level-up-alt:before{content:"\f3bf"}.fa-life-ring:before{content:"\f1cd"}.fa-lightbulb:before{content:"\f0eb"}.fa-line:before{content:"\f3c0"}.fa-link:before{content:"\f0c1"}.fa-linkedin:before{content:"\f08c"}.fa-linkedin-in:before{content:"\f0e1"}.fa-linode:before{content:"\f2b8"}.fa-linux:before{content:"\f17c"}.fa-lira-sign:before{content:"\f195"}.fa-list:before{content:"\f03a"}.fa-list-alt:before{content:"\f022"}.fa-list-ol:before{content:"\f0cb"}.fa-list-ul:before{content:"\f0ca"}.fa-location-arrow:before{content:"\f124"}.fa-lock:before{content:"\f023"}.fa-lock-open:before{content:"\f3c1"}.fa-long-arrow-alt-down:before{content:"\f309"}.fa-long-arrow-alt-left:before{content:"\f30a"}.fa-long-arrow-alt-right:before{content:"\f30b"}.fa-long-arrow-alt-up:before{content:"\f30c"}.fa-low-vision:before{content:"\f2a8"}.fa-luggage-cart:before{content:"\f59d"}.fa-lyft:before{content:"\f3c3"}.fa-magento:before{content:"\f3c4"}.fa-magic:before{content:"\f0d0"}.fa-magnet:before{content:"\f076"}.fa-mail-bulk:before{content:"\f674"}.fa-mailchimp:before{content:"\f59e"}.fa-male:before{content:"\f183"}.fa-mandalorian:before{content:"\f50f"}.fa-map:before{content:"\f279"}.fa-map-marked:before{content:"\f59f"}.fa-map-marked-alt:before{content:"\f5a0"}.fa-map-marker:before{content:"\f041"}.fa-map-marker-alt:before{content:"\f3c5"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-markdown:before{content:"\f60f"}.fa-marker:before{content:"\f5a1"}.fa-mars:before{content:"\f222"}.fa-mars-double:before{content:"\f227"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mask:before{content:"\f6fa"}.fa-mastodon:before{content:"\f4f6"}.fa-maxcdn:before{content:"\f136"}.fa-medal:before{content:"\f5a2"}.fa-medapps:before{content:"\f3c6"}.fa-medium:before{content:"\f23a"}.fa-medium-m:before{content:"\f3c7"}.fa-medkit:before{content:"\f0fa"}.fa-medrt:before{content:"\f3c8"}.fa-meetup:before{content:"\f2e0"}.fa-megaport:before{content:"\f5a3"}.fa-meh:before{content:"\f11a"}.fa-meh-blank:before{content:"\f5a4"}.fa-meh-rolling-eyes:before{content:"\f5a5"}.fa-memory:before{content:"\f538"}.fa-mendeley:before{content:"\f7b3"}.fa-menorah:before{content:"\f676"}.fa-mercury:before{content:"\f223"}.fa-meteor:before{content:"\f753"}.fa-microchip:before{content:"\f2db"}.fa-microphone:before{content:"\f130"}.fa-microphone-alt:before{content:"\f3c9"}.fa-microphone-alt-slash:before{content:"\f539"}.fa-microphone-slash:before{content:"\f131"}.fa-microscope:before{content:"\f610"}.fa-microsoft:before{content:"\f3ca"}.fa-minus:before{content:"\f068"}.fa-minus-circle:before{content:"\f056"}.fa-minus-square:before{content:"\f146"}.fa-mitten:before{content:"\f7b5"}.fa-mix:before{content:"\f3cb"}.fa-mixcloud:before{content:"\f289"}.fa-mizuni:before{content:"\f3cc"}.fa-mobile:before{content:"\f10b"}.fa-mobile-alt:before{content:"\f3cd"}.fa-modx:before{content:"\f285"}.fa-monero:before{content:"\f3d0"}.fa-money-bill:before{content:"\f0d6"}.fa-money-bill-alt:before{content:"\f3d1"}.fa-money-bill-wave:before{content:"\f53a"}.fa-money-bill-wave-alt:before{content:"\f53b"}.fa-money-check:before{content:"\f53c"}.fa-money-check-alt:before{content:"\f53d"}.fa-monument:before{content:"\f5a6"}.fa-moon:before{content:"\f186"}.fa-mortar-pestle:before{content:"\f5a7"}.fa-mosque:before{content:"\f678"}.fa-motorcycle:before{content:"\f21c"}.fa-mountain:before{content:"\f6fc"}.fa-mouse-pointer:before{content:"\f245"}.fa-mug-hot:before{content:"\f7b6"}.fa-music:before{content:"\f001"}.fa-napster:before{content:"\f3d2"}.fa-neos:before{content:"\f612"}.fa-network-wired:before{content:"\f6ff"}.fa-neuter:before{content:"\f22c"}.fa-newspaper:before{content:"\f1ea"}.fa-nimblr:before{content:"\f5a8"}.fa-node:before{content:"\f419"}.fa-node-js:before{content:"\f3d3"}.fa-not-equal:before{content:"\f53e"}.fa-notes-medical:before{content:"\f481"}.fa-npm:before{content:"\f3d4"}.fa-ns8:before{content:"\f3d5"}.fa-nutritionix:before{content:"\f3d6"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-oil-can:before{content:"\f613"}.fa-old-republic:before{content:"\f510"}.fa-om:before{content:"\f679"}.fa-opencart:before{content:"\f23d"}.fa-openid:before{content:"\f19b"}.fa-opera:before{content:"\f26a"}.fa-optin-monster:before{content:"\f23c"}.fa-osi:before{content:"\f41a"}.fa-otter:before{content:"\f700"}.fa-outdent:before{content:"\f03b"}.fa-page4:before{content:"\f3d7"}.fa-pagelines:before{content:"\f18c"}.fa-pager:before{content:"\f815"}.fa-paint-brush:before{content:"\f1fc"}.fa-paint-roller:before{content:"\f5aa"}.fa-palette:before{content:"\f53f"}.fa-palfed:before{content:"\f3d8"}.fa-pallet:before{content:"\f482"}.fa-paper-plane:before{content:"\f1d8"}.fa-paperclip:before{content:"\f0c6"}.fa-parachute-box:before{content:"\f4cd"}.fa-paragraph:before{content:"\f1dd"}.fa-parking:before{content:"\f540"}.fa-passport:before{content:"\f5ab"}.fa-pastafarianism:before{content:"\f67b"}.fa-paste:before{content:"\f0ea"}.fa-patreon:before{content:"\f3d9"}.fa-pause:before{content:"\f04c"}.fa-pause-circle:before{content:"\f28b"}.fa-paw:before{content:"\f1b0"}.fa-paypal:before{content:"\f1ed"}.fa-peace:before{content:"\f67c"}.fa-pen:before{content:"\f304"}.fa-pen-alt:before{content:"\f305"}.fa-pen-fancy:before{content:"\f5ac"}.fa-pen-nib:before{content:"\f5ad"}.fa-pen-square:before{content:"\f14b"}.fa-pencil-alt:before{content:"\f303"}.fa-pencil-ruler:before{content:"\f5ae"}.fa-penny-arcade:before{content:"\f704"}.fa-people-carry:before{content:"\f4ce"}.fa-pepper-hot:before{content:"\f816"}.fa-percent:before{content:"\f295"}.fa-percentage:before{content:"\f541"}.fa-periscope:before{content:"\f3da"}.fa-person-booth:before{content:"\f756"}.fa-phabricator:before{content:"\f3db"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-phoenix-squadron:before{content:"\f511"}.fa-phone:before{content:"\f095"}.fa-phone-alt:before{content:"\f879"}.fa-phone-slash:before{content:"\f3dd"}.fa-phone-square:before{content:"\f098"}.fa-phone-square-alt:before{content:"\f87b"}.fa-phone-volume:before{content:"\f2a0"}.fa-photo-video:before{content:"\f87c"}.fa-php:before{content:"\f457"}.fa-pied-piper:before{content:"\f2ae"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-pied-piper-hat:before{content:"\f4e5"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-piggy-bank:before{content:"\f4d3"}.fa-pills:before{content:"\f484"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-p:before{content:"\f231"}.fa-pinterest-square:before{content:"\f0d3"}.fa-pizza-slice:before{content:"\f818"}.fa-place-of-worship:before{content:"\f67f"}.fa-plane:before{content:"\f072"}.fa-plane-arrival:before{content:"\f5af"}.fa-plane-departure:before{content:"\f5b0"}.fa-play:before{content:"\f04b"}.fa-play-circle:before{content:"\f144"}.fa-playstation:before{content:"\f3df"}.fa-plug:before{content:"\f1e6"}.fa-plus:before{content:"\f067"}.fa-plus-circle:before{content:"\f055"}.fa-plus-square:before{content:"\f0fe"}.fa-podcast:before{content:"\f2ce"}.fa-poll:before{content:"\f681"}.fa-poll-h:before{content:"\f682"}.fa-poo:before{content:"\f2fe"}.fa-poo-storm:before{content:"\f75a"}.fa-poop:before{content:"\f619"}.fa-portrait:before{content:"\f3e0"}.fa-pound-sign:before{content:"\f154"}.fa-power-off:before{content:"\f011"}.fa-pray:before{content:"\f683"}.fa-praying-hands:before{content:"\f684"}.fa-prescription:before{content:"\f5b1"}.fa-prescription-bottle:before{content:"\f485"}.fa-prescription-bottle-alt:before{content:"\f486"}.fa-print:before{content:"\f02f"}.fa-procedures:before{content:"\f487"}.fa-product-hunt:before{content:"\f288"}.fa-project-diagram:before{content:"\f542"}.fa-pushed:before{content:"\f3e1"}.fa-puzzle-piece:before{content:"\f12e"}.fa-python:before{content:"\f3e2"}.fa-qq:before{content:"\f1d6"}.fa-qrcode:before{content:"\f029"}.fa-question:before{content:"\f128"}.fa-question-circle:before{content:"\f059"}.fa-quidditch:before{content:"\f458"}.fa-quinscape:before{content:"\f459"}.fa-quora:before{content:"\f2c4"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-quran:before{content:"\f687"}.fa-r-project:before{content:"\f4f7"}.fa-radiation:before{content:"\f7b9"}.fa-radiation-alt:before{content:"\f7ba"}.fa-rainbow:before{content:"\f75b"}.fa-random:before{content:"\f074"}.fa-raspberry-pi:before{content:"\f7bb"}.fa-ravelry:before{content:"\f2d9"}.fa-react:before{content:"\f41b"}.fa-reacteurope:before{content:"\f75d"}.fa-readme:before{content:"\f4d5"}.fa-rebel:before{content:"\f1d0"}.fa-receipt:before{content:"\f543"}.fa-recycle:before{content:"\f1b8"}.fa-red-river:before{content:"\f3e3"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-alien:before{content:"\f281"}.fa-reddit-square:before{content:"\f1a2"}.fa-redhat:before{content:"\f7bc"}.fa-redo:before{content:"\f01e"}.fa-redo-alt:before{content:"\f2f9"}.fa-registered:before{content:"\f25d"}.fa-remove-format:before{content:"\f87d"}.fa-renren:before{content:"\f18b"}.fa-reply:before{content:"\f3e5"}.fa-reply-all:before{content:"\f122"}.fa-replyd:before{content:"\f3e6"}.fa-republican:before{content:"\f75e"}.fa-researchgate:before{content:"\f4f8"}.fa-resolving:before{content:"\f3e7"}.fa-restroom:before{content:"\f7bd"}.fa-retweet:before{content:"\f079"}.fa-rev:before{content:"\f5b2"}.fa-ribbon:before{content:"\f4d6"}.fa-ring:before{content:"\f70b"}.fa-road:before{content:"\f018"}.fa-robot:before{content:"\f544"}.fa-rocket:before{content:"\f135"}.fa-rocketchat:before{content:"\f3e8"}.fa-rockrms:before{content:"\f3e9"}.fa-route:before{content:"\f4d7"}.fa-rss:before{content:"\f09e"}.fa-rss-square:before{content:"\f143"}.fa-ruble-sign:before{content:"\f158"}.fa-ruler:before{content:"\f545"}.fa-ruler-combined:before{content:"\f546"}.fa-ruler-horizontal:before{content:"\f547"}.fa-ruler-vertical:before{content:"\f548"}.fa-running:before{content:"\f70c"}.fa-rupee-sign:before{content:"\f156"}.fa-sad-cry:before{content:"\f5b3"}.fa-sad-tear:before{content:"\f5b4"}.fa-safari:before{content:"\f267"}.fa-salesforce:before{content:"\f83b"}.fa-sass:before{content:"\f41e"}.fa-satellite:before{content:"\f7bf"}.fa-satellite-dish:before{content:"\f7c0"}.fa-save:before{content:"\f0c7"}.fa-schlix:before{content:"\f3ea"}.fa-school:before{content:"\f549"}.fa-screwdriver:before{content:"\f54a"}.fa-scribd:before{content:"\f28a"}.fa-scroll:before{content:"\f70e"}.fa-sd-card:before{content:"\f7c2"}.fa-search:before{content:"\f002"}.fa-search-dollar:before{content:"\f688"}.fa-search-location:before{content:"\f689"}.fa-search-minus:before{content:"\f010"}.fa-search-plus:before{content:"\f00e"}.fa-searchengin:before{content:"\f3eb"}.fa-seedling:before{content:"\f4d8"}.fa-sellcast:before{content:"\f2da"}.fa-sellsy:before{content:"\f213"}.fa-server:before{content:"\f233"}.fa-servicestack:before{content:"\f3ec"}.fa-shapes:before{content:"\f61f"}.fa-share:before{content:"\f064"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-share-square:before{content:"\f14d"}.fa-shekel-sign:before{content:"\f20b"}.fa-shield-alt:before{content:"\f3ed"}.fa-ship:before{content:"\f21a"}.fa-shipping-fast:before{content:"\f48b"}.fa-shirtsinbulk:before{content:"\f214"}.fa-shoe-prints:before{content:"\f54b"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-shopping-cart:before{content:"\f07a"}.fa-shopware:before{content:"\f5b5"}.fa-shower:before{content:"\f2cc"}.fa-shuttle-van:before{content:"\f5b6"}.fa-sign:before{content:"\f4d9"}.fa-sign-in-alt:before{content:"\f2f6"}.fa-sign-language:before{content:"\f2a7"}.fa-sign-out-alt:before{content:"\f2f5"}.fa-signal:before{content:"\f012"}.fa-signature:before{content:"\f5b7"}.fa-sim-card:before{content:"\f7c4"}.fa-simplybuilt:before{content:"\f215"}.fa-sistrix:before{content:"\f3ee"}.fa-sitemap:before{content:"\f0e8"}.fa-sith:before{content:"\f512"}.fa-skating:before{content:"\f7c5"}.fa-sketch:before{content:"\f7c6"}.fa-skiing:before{content:"\f7c9"}.fa-skiing-nordic:before{content:"\f7ca"}.fa-skull:before{content:"\f54c"}.fa-skull-crossbones:before{content:"\f714"}.fa-skyatlas:before{content:"\f216"}.fa-skype:before{content:"\f17e"}.fa-slack:before{content:"\f198"}.fa-slack-hash:before{content:"\f3ef"}.fa-slash:before{content:"\f715"}.fa-sleigh:before{content:"\f7cc"}.fa-sliders-h:before{content:"\f1de"}.fa-slideshare:before{content:"\f1e7"}.fa-smile:before{content:"\f118"}.fa-smile-beam:before{content:"\f5b8"}.fa-smile-wink:before{content:"\f4da"}.fa-smog:before{content:"\f75f"}.fa-smoking:before{content:"\f48d"}.fa-smoking-ban:before{content:"\f54d"}.fa-sms:before{content:"\f7cd"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-snowboarding:before{content:"\f7ce"}.fa-snowflake:before{content:"\f2dc"}.fa-snowman:before{content:"\f7d0"}.fa-snowplow:before{content:"\f7d2"}.fa-socks:before{content:"\f696"}.fa-solar-panel:before{content:"\f5ba"}.fa-sort:before{content:"\f0dc"}.fa-sort-alpha-down:before{content:"\f15d"}.fa-sort-alpha-down-alt:before{content:"\f881"}.fa-sort-alpha-up:before{content:"\f15e"}.fa-sort-alpha-up-alt:before{content:"\f882"}.fa-sort-amount-down:before{content:"\f160"}.fa-sort-amount-down-alt:before{content:"\f884"}.fa-sort-amount-up:before{content:"\f161"}.fa-sort-amount-up-alt:before{content:"\f885"}.fa-sort-down:before{content:"\f0dd"}.fa-sort-numeric-down:before{content:"\f162"}.fa-sort-numeric-down-alt:before{content:"\f886"}.fa-sort-numeric-up:before{content:"\f163"}.fa-sort-numeric-up-alt:before{content:"\f887"}.fa-sort-up:before{content:"\f0de"}.fa-soundcloud:before{content:"\f1be"}.fa-sourcetree:before{content:"\f7d3"}.fa-spa:before{content:"\f5bb"}.fa-space-shuttle:before{content:"\f197"}.fa-speakap:before{content:"\f3f3"}.fa-speaker-deck:before{content:"\f83c"}.fa-spell-check:before{content:"\f891"}.fa-spider:before{content:"\f717"}.fa-spinner:before{content:"\f110"}.fa-splotch:before{content:"\f5bc"}.fa-spotify:before{content:"\f1bc"}.fa-spray-can:before{content:"\f5bd"}.fa-square:before{content:"\f0c8"}.fa-square-full:before{content:"\f45c"}.fa-square-root-alt:before{content:"\f698"}.fa-squarespace:before{content:"\f5be"}.fa-stack-exchange:before{content:"\f18d"}.fa-stack-overflow:before{content:"\f16c"}.fa-stackpath:before{content:"\f842"}.fa-stamp:before{content:"\f5bf"}.fa-star:before{content:"\f005"}.fa-star-and-crescent:before{content:"\f699"}.fa-star-half:before{content:"\f089"}.fa-star-half-alt:before{content:"\f5c0"}.fa-star-of-david:before{content:"\f69a"}.fa-star-of-life:before{content:"\f621"}.fa-staylinked:before{content:"\f3f5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-steam-symbol:before{content:"\f3f6"}.fa-step-backward:before{content:"\f048"}.fa-step-forward:before{content:"\f051"}.fa-stethoscope:before{content:"\f0f1"}.fa-sticker-mule:before{content:"\f3f7"}.fa-sticky-note:before{content:"\f249"}.fa-stop:before{content:"\f04d"}.fa-stop-circle:before{content:"\f28d"}.fa-stopwatch:before{content:"\f2f2"}.fa-store:before{content:"\f54e"}.fa-store-alt:before{content:"\f54f"}.fa-strava:before{content:"\f428"}.fa-stream:before{content:"\f550"}.fa-street-view:before{content:"\f21d"}.fa-strikethrough:before{content:"\f0cc"}.fa-stripe:before{content:"\f429"}.fa-stripe-s:before{content:"\f42a"}.fa-stroopwafel:before{content:"\f551"}.fa-studiovinari:before{content:"\f3f8"}.fa-stumbleupon:before{content:"\f1a4"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-subscript:before{content:"\f12c"}.fa-subway:before{content:"\f239"}.fa-suitcase:before{content:"\f0f2"}.fa-suitcase-rolling:before{content:"\f5c1"}.fa-sun:before{content:"\f185"}.fa-superpowers:before{content:"\f2dd"}.fa-superscript:before{content:"\f12b"}.fa-supple:before{content:"\f3f9"}.fa-surprise:before{content:"\f5c2"}.fa-suse:before{content:"\f7d6"}.fa-swatchbook:before{content:"\f5c3"}.fa-swimmer:before{content:"\f5c4"}.fa-swimming-pool:before{content:"\f5c5"}.fa-symfony:before{content:"\f83d"}.fa-synagogue:before{content:"\f69b"}.fa-sync:before{content:"\f021"}.fa-sync-alt:before{content:"\f2f1"}.fa-syringe:before{content:"\f48e"}.fa-table:before{content:"\f0ce"}.fa-table-tennis:before{content:"\f45d"}.fa-tablet:before{content:"\f10a"}.fa-tablet-alt:before{content:"\f3fa"}.fa-tablets:before{content:"\f490"}.fa-tachometer-alt:before{content:"\f3fd"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-tape:before{content:"\f4db"}.fa-tasks:before{content:"\f0ae"}.fa-taxi:before{content:"\f1ba"}.fa-teamspeak:before{content:"\f4f9"}.fa-teeth:before{content:"\f62e"}.fa-teeth-open:before{content:"\f62f"}.fa-telegram:before{content:"\f2c6"}.fa-telegram-plane:before{content:"\f3fe"}.fa-temperature-high:before{content:"\f769"}.fa-temperature-low:before{content:"\f76b"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-tenge:before{content:"\f7d7"}.fa-terminal:before{content:"\f120"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-th:before{content:"\f00a"}.fa-th-large:before{content:"\f009"}.fa-th-list:before{content:"\f00b"}.fa-the-red-yeti:before{content:"\f69d"}.fa-theater-masks:before{content:"\f630"}.fa-themeco:before{content:"\f5c6"}.fa-themeisle:before{content:"\f2b2"}.fa-thermometer:before{content:"\f491"}.fa-thermometer-empty:before{content:"\f2cb"}.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-think-peaks:before{content:"\f731"}.fa-thumbs-down:before{content:"\f165"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbtack:before{content:"\f08d"}.fa-ticket-alt:before{content:"\f3ff"}.fa-times:before{content:"\f00d"}.fa-times-circle:before{content:"\f057"}.fa-tint:before{content:"\f043"}.fa-tint-slash:before{content:"\f5c7"}.fa-tired:before{content:"\f5c8"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-toilet:before{content:"\f7d8"}.fa-toilet-paper:before{content:"\f71e"}.fa-toolbox:before{content:"\f552"}.fa-tools:before{content:"\f7d9"}.fa-tooth:before{content:"\f5c9"}.fa-torah:before{content:"\f6a0"}.fa-torii-gate:before{content:"\f6a1"}.fa-tractor:before{content:"\f722"}.fa-trade-federation:before{content:"\f513"}.fa-trademark:before{content:"\f25c"}.fa-traffic-light:before{content:"\f637"}.fa-train:before{content:"\f238"}.fa-tram:before{content:"\f7da"}.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-trash:before{content:"\f1f8"}.fa-trash-alt:before{content:"\f2ed"}.fa-trash-restore:before{content:"\f829"}.fa-trash-restore-alt:before{content:"\f82a"}.fa-tree:before{content:"\f1bb"}.fa-trello:before{content:"\f181"}.fa-tripadvisor:before{content:"\f262"}.fa-trophy:before{content:"\f091"}.fa-truck:before{content:"\f0d1"}.fa-truck-loading:before{content:"\f4de"}.fa-truck-monster:before{content:"\f63b"}.fa-truck-moving:before{content:"\f4df"}.fa-truck-pickup:before{content:"\f63c"}.fa-tshirt:before{content:"\f553"}.fa-tty:before{content:"\f1e4"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-tv:before{content:"\f26c"}.fa-twitch:before{content:"\f1e8"}.fa-twitter:before{content:"\f099"}.fa-twitter-square:before{content:"\f081"}.fa-typo3:before{content:"\f42b"}.fa-uber:before{content:"\f402"}.fa-ubuntu:before{content:"\f7df"}.fa-uikit:before{content:"\f403"}.fa-umbrella:before{content:"\f0e9"}.fa-umbrella-beach:before{content:"\f5ca"}.fa-underline:before{content:"\f0cd"}.fa-undo:before{content:"\f0e2"}.fa-undo-alt:before{content:"\f2ea"}.fa-uniregistry:before{content:"\f404"}.fa-universal-access:before{content:"\f29a"}.fa-university:before{content:"\f19c"}.fa-unlink:before{content:"\f127"}.fa-unlock:before{content:"\f09c"}.fa-unlock-alt:before{content:"\f13e"}.fa-untappd:before{content:"\f405"}.fa-upload:before{content:"\f093"}.fa-ups:before{content:"\f7e0"}.fa-usb:before{content:"\f287"}.fa-user:before{content:"\f007"}.fa-user-alt:before{content:"\f406"}.fa-user-alt-slash:before{content:"\f4fa"}.fa-user-astronaut:before{content:"\f4fb"}.fa-user-check:before{content:"\f4fc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-clock:before{content:"\f4fd"}.fa-user-cog:before{content:"\f4fe"}.fa-user-edit:before{content:"\f4ff"}.fa-user-friends:before{content:"\f500"}.fa-user-graduate:before{content:"\f501"}.fa-user-injured:before{content:"\f728"}.fa-user-lock:before{content:"\f502"}.fa-user-md:before{content:"\f0f0"}.fa-user-minus:before{content:"\f503"}.fa-user-ninja:before{content:"\f504"}.fa-user-nurse:before{content:"\f82f"}.fa-user-plus:before{content:"\f234"}.fa-user-secret:before{content:"\f21b"}.fa-user-shield:before{content:"\f505"}.fa-user-slash:before{content:"\f506"}.fa-user-tag:before{content:"\f507"}.fa-user-tie:before{content:"\f508"}.fa-user-times:before{content:"\f235"}.fa-users:before{content:"\f0c0"}.fa-users-cog:before{content:"\f509"}.fa-usps:before{content:"\f7e1"}.fa-ussunnah:before{content:"\f407"}.fa-utensil-spoon:before{content:"\f2e5"}.fa-utensils:before{content:"\f2e7"}.fa-vaadin:before{content:"\f408"}.fa-vector-square:before{content:"\f5cb"}.fa-venus:before{content:"\f221"}.fa-venus-double:before{content:"\f226"}.fa-venus-mars:before{content:"\f228"}.fa-viacoin:before{content:"\f237"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-vial:before{content:"\f492"}.fa-vials:before{content:"\f493"}.fa-viber:before{content:"\f409"}.fa-video:before{content:"\f03d"}.fa-video-slash:before{content:"\f4e2"}.fa-vihara:before{content:"\f6a7"}.fa-vimeo:before{content:"\f40a"}.fa-vimeo-square:before{content:"\f194"}.fa-vimeo-v:before{content:"\f27d"}.fa-vine:before{content:"\f1ca"}.fa-vk:before{content:"\f189"}.fa-vnv:before{content:"\f40b"}.fa-voicemail:before{content:"\f897"}.fa-volleyball-ball:before{content:"\f45f"}.fa-volume-down:before{content:"\f027"}.fa-volume-mute:before{content:"\f6a9"}.fa-volume-off:before{content:"\f026"}.fa-volume-up:before{content:"\f028"}.fa-vote-yea:before{content:"\f772"}.fa-vr-cardboard:before{content:"\f729"}.fa-vuejs:before{content:"\f41f"}.fa-walking:before{content:"\f554"}.fa-wallet:before{content:"\f555"}.fa-warehouse:before{content:"\f494"}.fa-water:before{content:"\f773"}.fa-wave-square:before{content:"\f83e"}.fa-waze:before{content:"\f83f"}.fa-weebly:before{content:"\f5cc"}.fa-weibo:before{content:"\f18a"}.fa-weight:before{content:"\f496"}.fa-weight-hanging:before{content:"\f5cd"}.fa-weixin:before{content:"\f1d7"}.fa-whatsapp:before{content:"\f232"}.fa-whatsapp-square:before{content:"\f40c"}.fa-wheelchair:before{content:"\f193"}.fa-whmcs:before{content:"\f40d"}.fa-wifi:before{content:"\f1eb"}.fa-wikipedia-w:before{content:"\f266"}.fa-wind:before{content:"\f72e"}.fa-window-close:before{content:"\f410"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-windows:before{content:"\f17a"}.fa-wine-bottle:before{content:"\f72f"}.fa-wine-glass:before{content:"\f4e3"}.fa-wine-glass-alt:before{content:"\f5ce"}.fa-wix:before{content:"\f5cf"}.fa-wizards-of-the-coast:before{content:"\f730"}.fa-wolf-pack-battalion:before{content:"\f514"}.fa-won-sign:before{content:"\f159"}.fa-wordpress:before{content:"\f19a"}.fa-wordpress-simple:before{content:"\f411"}.fa-wpbeginner:before{content:"\f297"}.fa-wpexplorer:before{content:"\f2de"}.fa-wpforms:before{content:"\f298"}.fa-wpressr:before{content:"\f3e4"}.fa-wrench:before{content:"\f0ad"}.fa-x-ray:before{content:"\f497"}.fa-xbox:before{content:"\f412"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-y-combinator:before{content:"\f23b"}.fa-yahoo:before{content:"\f19e"}.fa-yammer:before{content:"\f840"}.fa-yandex:before{content:"\f413"}.fa-yandex-international:before{content:"\f414"}.fa-yarn:before{content:"\f7e3"}.fa-yelp:before{content:"\f1e9"}.fa-yen-sign:before{content:"\f157"}.fa-yin-yang:before{content:"\f6ad"}.fa-yoast:before{content:"\f2b1"}.fa-youtube:before{content:"\f167"}.fa-youtube-square:before{content:"\f431"}.fa-zhihu:before{content:"\f63f"}.sr-only{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sr-only-focusable:active,.sr-only-focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:400;font-display:auto;src:url(fa-regular-400.eot);src:url(fa-regular-400.eot?#iefix) format("embedded-opentype"),url(fa-regular-400.woff) format("woff")}.far{font-weight:400}@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:900;font-display:auto;src:url(fa-solid-900.eot);src:url(fa-solid-900.eot?#iefix) format("embedded-opentype"),url(fa-solid-900.woff) format("woff")}.fa,.far,.fas{font-family:"Font Awesome 5 Free"}.fa,.fas{font-weight:900} \ No newline at end of file +.fa,.fab,.fal,.far,.fas{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;line-height:1}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-.0667em}.fa-xs{font-size:.75em}.fa-sm{font-size:.875em}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.fa-ul>li{position:relative}.fa-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}.fa-border{border:.08em solid #eee;border-radius:.1em;padding:.2em .25em .15em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.fab.fa-pull-left,.fal.fa-pull-left,.far.fa-pull-left,.fas.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.fab.fa-pull-right,.fal.fa-pull-right,.far.fa-pull-right,.fas.fa-pull-right{margin-left:.3em}.fa-spin{animation:fa-spin 2s infinite linear}.fa-pulse{animation:fa-spin 1s infinite steps(8)}@keyframes fa-spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";transform:scaleX(-1)}.fa-flip-vertical{transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical,.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{transform:scale(-1)}:root .fa-flip-both,:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{filter:none}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-500px:before{content:"\f26e"}.fa-accessible-icon:before{content:"\f368"}.fa-accusoft:before{content:"\f369"}.fa-acquisitions-incorporated:before{content:"\f6af"}.fa-ad:before{content:"\f641"}.fa-address-book:before{content:"\f2b9"}.fa-address-card:before{content:"\f2bb"}.fa-adjust:before{content:"\f042"}.fa-adn:before{content:"\f170"}.fa-adobe:before{content:"\f778"}.fa-adversal:before{content:"\f36a"}.fa-affiliatetheme:before{content:"\f36b"}.fa-air-freshener:before{content:"\f5d0"}.fa-airbnb:before{content:"\f834"}.fa-algolia:before{content:"\f36c"}.fa-align-center:before{content:"\f037"}.fa-align-justify:before{content:"\f039"}.fa-align-left:before{content:"\f036"}.fa-align-right:before{content:"\f038"}.fa-alipay:before{content:"\f642"}.fa-allergies:before{content:"\f461"}.fa-amazon:before{content:"\f270"}.fa-amazon-pay:before{content:"\f42c"}.fa-ambulance:before{content:"\f0f9"}.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-amilia:before{content:"\f36d"}.fa-anchor:before{content:"\f13d"}.fa-android:before{content:"\f17b"}.fa-angellist:before{content:"\f209"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-down:before{content:"\f107"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angry:before{content:"\f556"}.fa-angrycreative:before{content:"\f36e"}.fa-angular:before{content:"\f420"}.fa-ankh:before{content:"\f644"}.fa-app-store:before{content:"\f36f"}.fa-app-store-ios:before{content:"\f370"}.fa-apper:before{content:"\f371"}.fa-apple:before{content:"\f179"}.fa-apple-alt:before{content:"\f5d1"}.fa-apple-pay:before{content:"\f415"}.fa-archive:before{content:"\f187"}.fa-archway:before{content:"\f557"}.fa-arrow-alt-circle-down:before{content:"\f358"}.fa-arrow-alt-circle-left:before{content:"\f359"}.fa-arrow-alt-circle-right:before{content:"\f35a"}.fa-arrow-alt-circle-up:before{content:"\f35b"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-down:before{content:"\f063"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrows-alt:before{content:"\f0b2"}.fa-arrows-alt-h:before{content:"\f337"}.fa-arrows-alt-v:before{content:"\f338"}.fa-artstation:before{content:"\f77a"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asterisk:before{content:"\f069"}.fa-asymmetrik:before{content:"\f372"}.fa-at:before{content:"\f1fa"}.fa-atlas:before{content:"\f558"}.fa-atlassian:before{content:"\f77b"}.fa-atom:before{content:"\f5d2"}.fa-audible:before{content:"\f373"}.fa-audio-description:before{content:"\f29e"}.fa-autoprefixer:before{content:"\f41c"}.fa-avianex:before{content:"\f374"}.fa-aviato:before{content:"\f421"}.fa-award:before{content:"\f559"}.fa-aws:before{content:"\f375"}.fa-baby:before{content:"\f77c"}.fa-baby-carriage:before{content:"\f77d"}.fa-backspace:before{content:"\f55a"}.fa-backward:before{content:"\f04a"}.fa-bacon:before{content:"\f7e5"}.fa-balance-scale:before{content:"\f24e"}.fa-balance-scale-left:before{content:"\f515"}.fa-balance-scale-right:before{content:"\f516"}.fa-ban:before{content:"\f05e"}.fa-band-aid:before{content:"\f462"}.fa-bandcamp:before{content:"\f2d5"}.fa-barcode:before{content:"\f02a"}.fa-bars:before{content:"\f0c9"}.fa-baseball-ball:before{content:"\f433"}.fa-basketball-ball:before{content:"\f434"}.fa-bath:before{content:"\f2cd"}.fa-battery-empty:before{content:"\f244"}.fa-battery-full:before{content:"\f240"}.fa-battery-half:before{content:"\f242"}.fa-battery-quarter:before{content:"\f243"}.fa-battery-three-quarters:before{content:"\f241"}.fa-battle-net:before{content:"\f835"}.fa-bed:before{content:"\f236"}.fa-beer:before{content:"\f0fc"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-bell:before{content:"\f0f3"}.fa-bell-slash:before{content:"\f1f6"}.fa-bezier-curve:before{content:"\f55b"}.fa-bible:before{content:"\f647"}.fa-bicycle:before{content:"\f206"}.fa-biking:before{content:"\f84a"}.fa-bimobject:before{content:"\f378"}.fa-binoculars:before{content:"\f1e5"}.fa-biohazard:before{content:"\f780"}.fa-birthday-cake:before{content:"\f1fd"}.fa-bitbucket:before{content:"\f171"}.fa-bitcoin:before{content:"\f379"}.fa-bity:before{content:"\f37a"}.fa-black-tie:before{content:"\f27e"}.fa-blackberry:before{content:"\f37b"}.fa-blender:before{content:"\f517"}.fa-blender-phone:before{content:"\f6b6"}.fa-blind:before{content:"\f29d"}.fa-blog:before{content:"\f781"}.fa-blogger:before{content:"\f37c"}.fa-blogger-b:before{content:"\f37d"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-bold:before{content:"\f032"}.fa-bolt:before{content:"\f0e7"}.fa-bomb:before{content:"\f1e2"}.fa-bone:before{content:"\f5d7"}.fa-bong:before{content:"\f55c"}.fa-book:before{content:"\f02d"}.fa-book-dead:before{content:"\f6b7"}.fa-book-medical:before{content:"\f7e6"}.fa-book-open:before{content:"\f518"}.fa-book-reader:before{content:"\f5da"}.fa-bookmark:before{content:"\f02e"}.fa-bootstrap:before{content:"\f836"}.fa-border-all:before{content:"\f84c"}.fa-border-none:before{content:"\f850"}.fa-border-style:before{content:"\f853"}.fa-bowling-ball:before{content:"\f436"}.fa-box:before{content:"\f466"}.fa-box-open:before{content:"\f49e"}.fa-boxes:before{content:"\f468"}.fa-braille:before{content:"\f2a1"}.fa-brain:before{content:"\f5dc"}.fa-bread-slice:before{content:"\f7ec"}.fa-briefcase:before{content:"\f0b1"}.fa-briefcase-medical:before{content:"\f469"}.fa-broadcast-tower:before{content:"\f519"}.fa-broom:before{content:"\f51a"}.fa-brush:before{content:"\f55d"}.fa-btc:before{content:"\f15a"}.fa-buffer:before{content:"\f837"}.fa-bug:before{content:"\f188"}.fa-building:before{content:"\f1ad"}.fa-bullhorn:before{content:"\f0a1"}.fa-bullseye:before{content:"\f140"}.fa-burn:before{content:"\f46a"}.fa-buromobelexperte:before{content:"\f37f"}.fa-bus:before{content:"\f207"}.fa-bus-alt:before{content:"\f55e"}.fa-business-time:before{content:"\f64a"}.fa-buysellads:before{content:"\f20d"}.fa-calculator:before{content:"\f1ec"}.fa-calendar:before{content:"\f133"}.fa-calendar-alt:before{content:"\f073"}.fa-calendar-check:before{content:"\f274"}.fa-calendar-day:before{content:"\f783"}.fa-calendar-minus:before{content:"\f272"}.fa-calendar-plus:before{content:"\f271"}.fa-calendar-times:before{content:"\f273"}.fa-calendar-week:before{content:"\f784"}.fa-camera:before{content:"\f030"}.fa-camera-retro:before{content:"\f083"}.fa-campground:before{content:"\f6bb"}.fa-canadian-maple-leaf:before{content:"\f785"}.fa-candy-cane:before{content:"\f786"}.fa-cannabis:before{content:"\f55f"}.fa-capsules:before{content:"\f46b"}.fa-car:before{content:"\f1b9"}.fa-car-alt:before{content:"\f5de"}.fa-car-battery:before{content:"\f5df"}.fa-car-crash:before{content:"\f5e1"}.fa-car-side:before{content:"\f5e4"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-caret-square-down:before{content:"\f150"}.fa-caret-square-left:before{content:"\f191"}.fa-caret-square-right:before{content:"\f152"}.fa-caret-square-up:before{content:"\f151"}.fa-caret-up:before{content:"\f0d8"}.fa-carrot:before{content:"\f787"}.fa-cart-arrow-down:before{content:"\f218"}.fa-cart-plus:before{content:"\f217"}.fa-cash-register:before{content:"\f788"}.fa-cat:before{content:"\f6be"}.fa-cc-amazon-pay:before{content:"\f42d"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-apple-pay:before{content:"\f416"}.fa-cc-diners-club:before{content:"\f24c"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-cc-visa:before{content:"\f1f0"}.fa-centercode:before{content:"\f380"}.fa-centos:before{content:"\f789"}.fa-certificate:before{content:"\f0a3"}.fa-chair:before{content:"\f6c0"}.fa-chalkboard:before{content:"\f51b"}.fa-chalkboard-teacher:before{content:"\f51c"}.fa-charging-station:before{content:"\f5e7"}.fa-chart-area:before{content:"\f1fe"}.fa-chart-bar:before{content:"\f080"}.fa-chart-line:before{content:"\f201"}.fa-chart-pie:before{content:"\f200"}.fa-check:before{content:"\f00c"}.fa-check-circle:before{content:"\f058"}.fa-check-double:before{content:"\f560"}.fa-check-square:before{content:"\f14a"}.fa-cheese:before{content:"\f7ef"}.fa-chess:before{content:"\f439"}.fa-chess-bishop:before{content:"\f43a"}.fa-chess-board:before{content:"\f43c"}.fa-chess-king:before{content:"\f43f"}.fa-chess-knight:before{content:"\f441"}.fa-chess-pawn:before{content:"\f443"}.fa-chess-queen:before{content:"\f445"}.fa-chess-rook:before{content:"\f447"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-down:before{content:"\f078"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-chevron-up:before{content:"\f077"}.fa-child:before{content:"\f1ae"}.fa-chrome:before{content:"\f268"}.fa-chromecast:before{content:"\f838"}.fa-church:before{content:"\f51d"}.fa-circle:before{content:"\f111"}.fa-circle-notch:before{content:"\f1ce"}.fa-city:before{content:"\f64f"}.fa-clinic-medical:before{content:"\f7f2"}.fa-clipboard:before{content:"\f328"}.fa-clipboard-check:before{content:"\f46c"}.fa-clipboard-list:before{content:"\f46d"}.fa-clock:before{content:"\f017"}.fa-clone:before{content:"\f24d"}.fa-closed-captioning:before{content:"\f20a"}.fa-cloud:before{content:"\f0c2"}.fa-cloud-download-alt:before{content:"\f381"}.fa-cloud-meatball:before{content:"\f73b"}.fa-cloud-moon:before{content:"\f6c3"}.fa-cloud-moon-rain:before{content:"\f73c"}.fa-cloud-rain:before{content:"\f73d"}.fa-cloud-showers-heavy:before{content:"\f740"}.fa-cloud-sun:before{content:"\f6c4"}.fa-cloud-sun-rain:before{content:"\f743"}.fa-cloud-upload-alt:before{content:"\f382"}.fa-cloudscale:before{content:"\f383"}.fa-cloudsmith:before{content:"\f384"}.fa-cloudversify:before{content:"\f385"}.fa-cocktail:before{content:"\f561"}.fa-code:before{content:"\f121"}.fa-code-branch:before{content:"\f126"}.fa-codepen:before{content:"\f1cb"}.fa-codiepie:before{content:"\f284"}.fa-coffee:before{content:"\f0f4"}.fa-cog:before{content:"\f013"}.fa-cogs:before{content:"\f085"}.fa-coins:before{content:"\f51e"}.fa-columns:before{content:"\f0db"}.fa-comment:before{content:"\f075"}.fa-comment-alt:before{content:"\f27a"}.fa-comment-dollar:before{content:"\f651"}.fa-comment-dots:before{content:"\f4ad"}.fa-comment-medical:before{content:"\f7f5"}.fa-comment-slash:before{content:"\f4b3"}.fa-comments:before{content:"\f086"}.fa-comments-dollar:before{content:"\f653"}.fa-compact-disc:before{content:"\f51f"}.fa-compass:before{content:"\f14e"}.fa-compress:before{content:"\f066"}.fa-compress-arrows-alt:before{content:"\f78c"}.fa-concierge-bell:before{content:"\f562"}.fa-confluence:before{content:"\f78d"}.fa-connectdevelop:before{content:"\f20e"}.fa-contao:before{content:"\f26d"}.fa-cookie:before{content:"\f563"}.fa-cookie-bite:before{content:"\f564"}.fa-copy:before{content:"\f0c5"}.fa-copyright:before{content:"\f1f9"}.fa-couch:before{content:"\f4b8"}.fa-cpanel:before{content:"\f388"}.fa-creative-commons:before{content:"\f25e"}.fa-creative-commons-by:before{content:"\f4e7"}.fa-creative-commons-nc:before{content:"\f4e8"}.fa-creative-commons-nc-eu:before{content:"\f4e9"}.fa-creative-commons-nc-jp:before{content:"\f4ea"}.fa-creative-commons-nd:before{content:"\f4eb"}.fa-creative-commons-pd:before{content:"\f4ec"}.fa-creative-commons-pd-alt:before{content:"\f4ed"}.fa-creative-commons-remix:before{content:"\f4ee"}.fa-creative-commons-sa:before{content:"\f4ef"}.fa-creative-commons-sampling:before{content:"\f4f0"}.fa-creative-commons-sampling-plus:before{content:"\f4f1"}.fa-creative-commons-share:before{content:"\f4f2"}.fa-creative-commons-zero:before{content:"\f4f3"}.fa-credit-card:before{content:"\f09d"}.fa-critical-role:before{content:"\f6c9"}.fa-crop:before{content:"\f125"}.fa-crop-alt:before{content:"\f565"}.fa-cross:before{content:"\f654"}.fa-crosshairs:before{content:"\f05b"}.fa-crow:before{content:"\f520"}.fa-crown:before{content:"\f521"}.fa-crutch:before{content:"\f7f7"}.fa-css3:before{content:"\f13c"}.fa-css3-alt:before{content:"\f38b"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-cut:before{content:"\f0c4"}.fa-cuttlefish:before{content:"\f38c"}.fa-d-and-d:before{content:"\f38d"}.fa-d-and-d-beyond:before{content:"\f6ca"}.fa-dashcube:before{content:"\f210"}.fa-database:before{content:"\f1c0"}.fa-deaf:before{content:"\f2a4"}.fa-delicious:before{content:"\f1a5"}.fa-democrat:before{content:"\f747"}.fa-deploydog:before{content:"\f38e"}.fa-deskpro:before{content:"\f38f"}.fa-desktop:before{content:"\f108"}.fa-dev:before{content:"\f6cc"}.fa-deviantart:before{content:"\f1bd"}.fa-dharmachakra:before{content:"\f655"}.fa-dhl:before{content:"\f790"}.fa-diagnoses:before{content:"\f470"}.fa-diaspora:before{content:"\f791"}.fa-dice:before{content:"\f522"}.fa-dice-d20:before{content:"\f6cf"}.fa-dice-d6:before{content:"\f6d1"}.fa-dice-five:before{content:"\f523"}.fa-dice-four:before{content:"\f524"}.fa-dice-one:before{content:"\f525"}.fa-dice-six:before{content:"\f526"}.fa-dice-three:before{content:"\f527"}.fa-dice-two:before{content:"\f528"}.fa-digg:before{content:"\f1a6"}.fa-digital-ocean:before{content:"\f391"}.fa-digital-tachograph:before{content:"\f566"}.fa-directions:before{content:"\f5eb"}.fa-discord:before{content:"\f392"}.fa-discourse:before{content:"\f393"}.fa-divide:before{content:"\f529"}.fa-dizzy:before{content:"\f567"}.fa-dna:before{content:"\f471"}.fa-dochub:before{content:"\f394"}.fa-docker:before{content:"\f395"}.fa-dog:before{content:"\f6d3"}.fa-dollar-sign:before{content:"\f155"}.fa-dolly:before{content:"\f472"}.fa-dolly-flatbed:before{content:"\f474"}.fa-donate:before{content:"\f4b9"}.fa-door-closed:before{content:"\f52a"}.fa-door-open:before{content:"\f52b"}.fa-dot-circle:before{content:"\f192"}.fa-dove:before{content:"\f4ba"}.fa-download:before{content:"\f019"}.fa-draft2digital:before{content:"\f396"}.fa-drafting-compass:before{content:"\f568"}.fa-dragon:before{content:"\f6d5"}.fa-draw-polygon:before{content:"\f5ee"}.fa-dribbble:before{content:"\f17d"}.fa-dribbble-square:before{content:"\f397"}.fa-dropbox:before{content:"\f16b"}.fa-drum:before{content:"\f569"}.fa-drum-steelpan:before{content:"\f56a"}.fa-drumstick-bite:before{content:"\f6d7"}.fa-drupal:before{content:"\f1a9"}.fa-dumbbell:before{content:"\f44b"}.fa-dumpster:before{content:"\f793"}.fa-dumpster-fire:before{content:"\f794"}.fa-dungeon:before{content:"\f6d9"}.fa-dyalog:before{content:"\f399"}.fa-earlybirds:before{content:"\f39a"}.fa-ebay:before{content:"\f4f4"}.fa-edge:before{content:"\f282"}.fa-edit:before{content:"\f044"}.fa-egg:before{content:"\f7fb"}.fa-eject:before{content:"\f052"}.fa-elementor:before{content:"\f430"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-ello:before{content:"\f5f1"}.fa-ember:before{content:"\f423"}.fa-empire:before{content:"\f1d1"}.fa-envelope:before{content:"\f0e0"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-text:before{content:"\f658"}.fa-envelope-square:before{content:"\f199"}.fa-envira:before{content:"\f299"}.fa-equals:before{content:"\f52c"}.fa-eraser:before{content:"\f12d"}.fa-erlang:before{content:"\f39d"}.fa-ethereum:before{content:"\f42e"}.fa-ethernet:before{content:"\f796"}.fa-etsy:before{content:"\f2d7"}.fa-euro-sign:before{content:"\f153"}.fa-evernote:before{content:"\f839"}.fa-exchange-alt:before{content:"\f362"}.fa-exclamation:before{content:"\f12a"}.fa-exclamation-circle:before{content:"\f06a"}.fa-exclamation-triangle:before{content:"\f071"}.fa-expand:before{content:"\f065"}.fa-expand-arrows-alt:before{content:"\f31e"}.fa-expeditedssl:before{content:"\f23e"}.fa-external-link-alt:before{content:"\f35d"}.fa-external-link-square-alt:before{content:"\f360"}.fa-eye:before{content:"\f06e"}.fa-eye-dropper:before{content:"\f1fb"}.fa-eye-slash:before{content:"\f070"}.fa-facebook:before{content:"\f09a"}.fa-facebook-f:before{content:"\f39e"}.fa-facebook-messenger:before{content:"\f39f"}.fa-facebook-square:before{content:"\f082"}.fa-fan:before{content:"\f863"}.fa-fantasy-flight-games:before{content:"\f6dc"}.fa-fast-backward:before{content:"\f049"}.fa-fast-forward:before{content:"\f050"}.fa-fax:before{content:"\f1ac"}.fa-feather:before{content:"\f52d"}.fa-feather-alt:before{content:"\f56b"}.fa-fedex:before{content:"\f797"}.fa-fedora:before{content:"\f798"}.fa-female:before{content:"\f182"}.fa-fighter-jet:before{content:"\f0fb"}.fa-figma:before{content:"\f799"}.fa-file:before{content:"\f15b"}.fa-file-alt:before{content:"\f15c"}.fa-file-archive:before{content:"\f1c6"}.fa-file-audio:before{content:"\f1c7"}.fa-file-code:before{content:"\f1c9"}.fa-file-contract:before{content:"\f56c"}.fa-file-csv:before{content:"\f6dd"}.fa-file-download:before{content:"\f56d"}.fa-file-excel:before{content:"\f1c3"}.fa-file-export:before{content:"\f56e"}.fa-file-image:before{content:"\f1c5"}.fa-file-import:before{content:"\f56f"}.fa-file-invoice:before{content:"\f570"}.fa-file-invoice-dollar:before{content:"\f571"}.fa-file-medical:before{content:"\f477"}.fa-file-medical-alt:before{content:"\f478"}.fa-file-pdf:before{content:"\f1c1"}.fa-file-powerpoint:before{content:"\f1c4"}.fa-file-prescription:before{content:"\f572"}.fa-file-signature:before{content:"\f573"}.fa-file-upload:before{content:"\f574"}.fa-file-video:before{content:"\f1c8"}.fa-file-word:before{content:"\f1c2"}.fa-fill:before{content:"\f575"}.fa-fill-drip:before{content:"\f576"}.fa-film:before{content:"\f008"}.fa-filter:before{content:"\f0b0"}.fa-fingerprint:before{content:"\f577"}.fa-fire:before{content:"\f06d"}.fa-fire-alt:before{content:"\f7e4"}.fa-fire-extinguisher:before{content:"\f134"}.fa-firefox:before{content:"\f269"}.fa-first-aid:before{content:"\f479"}.fa-first-order:before{content:"\f2b0"}.fa-first-order-alt:before{content:"\f50a"}.fa-firstdraft:before{content:"\f3a1"}.fa-fish:before{content:"\f578"}.fa-fist-raised:before{content:"\f6de"}.fa-flag:before{content:"\f024"}.fa-flag-checkered:before{content:"\f11e"}.fa-flag-usa:before{content:"\f74d"}.fa-flask:before{content:"\f0c3"}.fa-flickr:before{content:"\f16e"}.fa-flipboard:before{content:"\f44d"}.fa-flushed:before{content:"\f579"}.fa-fly:before{content:"\f417"}.fa-folder:before{content:"\f07b"}.fa-folder-minus:before{content:"\f65d"}.fa-folder-open:before{content:"\f07c"}.fa-folder-plus:before{content:"\f65e"}.fa-font:before{content:"\f031"}.fa-font-awesome:before{content:"\f2b4"}.fa-font-awesome-alt:before{content:"\f35c"}.fa-font-awesome-flag:before{content:"\f425"}.fa-font-awesome-logo-full:before{content:"\f4e6"}.fa-fonticons:before{content:"\f280"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-football-ball:before{content:"\f44e"}.fa-fort-awesome:before{content:"\f286"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-forumbee:before{content:"\f211"}.fa-forward:before{content:"\f04e"}.fa-foursquare:before{content:"\f180"}.fa-free-code-camp:before{content:"\f2c5"}.fa-freebsd:before{content:"\f3a4"}.fa-frog:before{content:"\f52e"}.fa-frown:before{content:"\f119"}.fa-frown-open:before{content:"\f57a"}.fa-fulcrum:before{content:"\f50b"}.fa-funnel-dollar:before{content:"\f662"}.fa-futbol:before{content:"\f1e3"}.fa-galactic-republic:before{content:"\f50c"}.fa-galactic-senate:before{content:"\f50d"}.fa-gamepad:before{content:"\f11b"}.fa-gas-pump:before{content:"\f52f"}.fa-gavel:before{content:"\f0e3"}.fa-gem:before{content:"\f3a5"}.fa-genderless:before{content:"\f22d"}.fa-get-pocket:before{content:"\f265"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-ghost:before{content:"\f6e2"}.fa-gift:before{content:"\f06b"}.fa-gifts:before{content:"\f79c"}.fa-git:before{content:"\f1d3"}.fa-git-alt:before{content:"\f841"}.fa-git-square:before{content:"\f1d2"}.fa-github:before{content:"\f09b"}.fa-github-alt:before{content:"\f113"}.fa-github-square:before{content:"\f092"}.fa-gitkraken:before{content:"\f3a6"}.fa-gitlab:before{content:"\f296"}.fa-gitter:before{content:"\f426"}.fa-glass-cheers:before{content:"\f79f"}.fa-glass-martini:before{content:"\f000"}.fa-glass-martini-alt:before{content:"\f57b"}.fa-glass-whiskey:before{content:"\f7a0"}.fa-glasses:before{content:"\f530"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-globe:before{content:"\f0ac"}.fa-globe-africa:before{content:"\f57c"}.fa-globe-americas:before{content:"\f57d"}.fa-globe-asia:before{content:"\f57e"}.fa-globe-europe:before{content:"\f7a2"}.fa-gofore:before{content:"\f3a7"}.fa-golf-ball:before{content:"\f450"}.fa-goodreads:before{content:"\f3a8"}.fa-goodreads-g:before{content:"\f3a9"}.fa-google:before{content:"\f1a0"}.fa-google-drive:before{content:"\f3aa"}.fa-google-play:before{content:"\f3ab"}.fa-google-plus:before{content:"\f2b3"}.fa-google-plus-g:before{content:"\f0d5"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-wallet:before{content:"\f1ee"}.fa-gopuram:before{content:"\f664"}.fa-graduation-cap:before{content:"\f19d"}.fa-gratipay:before{content:"\f184"}.fa-grav:before{content:"\f2d6"}.fa-greater-than:before{content:"\f531"}.fa-greater-than-equal:before{content:"\f532"}.fa-grimace:before{content:"\f57f"}.fa-grin:before{content:"\f580"}.fa-grin-alt:before{content:"\f581"}.fa-grin-beam:before{content:"\f582"}.fa-grin-beam-sweat:before{content:"\f583"}.fa-grin-hearts:before{content:"\f584"}.fa-grin-squint:before{content:"\f585"}.fa-grin-squint-tears:before{content:"\f586"}.fa-grin-stars:before{content:"\f587"}.fa-grin-tears:before{content:"\f588"}.fa-grin-tongue:before{content:"\f589"}.fa-grin-tongue-squint:before{content:"\f58a"}.fa-grin-tongue-wink:before{content:"\f58b"}.fa-grin-wink:before{content:"\f58c"}.fa-grip-horizontal:before{content:"\f58d"}.fa-grip-lines:before{content:"\f7a4"}.fa-grip-lines-vertical:before{content:"\f7a5"}.fa-grip-vertical:before{content:"\f58e"}.fa-gripfire:before{content:"\f3ac"}.fa-grunt:before{content:"\f3ad"}.fa-guitar:before{content:"\f7a6"}.fa-gulp:before{content:"\f3ae"}.fa-h-square:before{content:"\f0fd"}.fa-hacker-news:before{content:"\f1d4"}.fa-hacker-news-square:before{content:"\f3af"}.fa-hackerrank:before{content:"\f5f7"}.fa-hamburger:before{content:"\f805"}.fa-hammer:before{content:"\f6e3"}.fa-hamsa:before{content:"\f665"}.fa-hand-holding:before{content:"\f4bd"}.fa-hand-holding-heart:before{content:"\f4be"}.fa-hand-holding-usd:before{content:"\f4c0"}.fa-hand-lizard:before{content:"\f258"}.fa-hand-middle-finger:before{content:"\f806"}.fa-hand-paper:before{content:"\f256"}.fa-hand-peace:before{content:"\f25b"}.fa-hand-point-down:before{content:"\f0a7"}.fa-hand-point-left:before{content:"\f0a5"}.fa-hand-point-right:before{content:"\f0a4"}.fa-hand-point-up:before{content:"\f0a6"}.fa-hand-pointer:before{content:"\f25a"}.fa-hand-rock:before{content:"\f255"}.fa-hand-scissors:before{content:"\f257"}.fa-hand-spock:before{content:"\f259"}.fa-hands:before{content:"\f4c2"}.fa-hands-helping:before{content:"\f4c4"}.fa-handshake:before{content:"\f2b5"}.fa-hanukiah:before{content:"\f6e6"}.fa-hard-hat:before{content:"\f807"}.fa-hashtag:before{content:"\f292"}.fa-hat-wizard:before{content:"\f6e8"}.fa-haykal:before{content:"\f666"}.fa-hdd:before{content:"\f0a0"}.fa-heading:before{content:"\f1dc"}.fa-headphones:before{content:"\f025"}.fa-headphones-alt:before{content:"\f58f"}.fa-headset:before{content:"\f590"}.fa-heart:before{content:"\f004"}.fa-heart-broken:before{content:"\f7a9"}.fa-heartbeat:before{content:"\f21e"}.fa-helicopter:before{content:"\f533"}.fa-highlighter:before{content:"\f591"}.fa-hiking:before{content:"\f6ec"}.fa-hippo:before{content:"\f6ed"}.fa-hips:before{content:"\f452"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-history:before{content:"\f1da"}.fa-hockey-puck:before{content:"\f453"}.fa-holly-berry:before{content:"\f7aa"}.fa-home:before{content:"\f015"}.fa-hooli:before{content:"\f427"}.fa-hornbill:before{content:"\f592"}.fa-horse:before{content:"\f6f0"}.fa-horse-head:before{content:"\f7ab"}.fa-hospital:before{content:"\f0f8"}.fa-hospital-alt:before{content:"\f47d"}.fa-hospital-symbol:before{content:"\f47e"}.fa-hot-tub:before{content:"\f593"}.fa-hotdog:before{content:"\f80f"}.fa-hotel:before{content:"\f594"}.fa-hotjar:before{content:"\f3b1"}.fa-hourglass:before{content:"\f254"}.fa-hourglass-end:before{content:"\f253"}.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-start:before{content:"\f251"}.fa-house-damage:before{content:"\f6f1"}.fa-houzz:before{content:"\f27c"}.fa-hryvnia:before{content:"\f6f2"}.fa-html5:before{content:"\f13b"}.fa-hubspot:before{content:"\f3b2"}.fa-i-cursor:before{content:"\f246"}.fa-ice-cream:before{content:"\f810"}.fa-icicles:before{content:"\f7ad"}.fa-icons:before{content:"\f86d"}.fa-id-badge:before{content:"\f2c1"}.fa-id-card:before{content:"\f2c2"}.fa-id-card-alt:before{content:"\f47f"}.fa-igloo:before{content:"\f7ae"}.fa-image:before{content:"\f03e"}.fa-images:before{content:"\f302"}.fa-imdb:before{content:"\f2d8"}.fa-inbox:before{content:"\f01c"}.fa-indent:before{content:"\f03c"}.fa-industry:before{content:"\f275"}.fa-infinity:before{content:"\f534"}.fa-info:before{content:"\f129"}.fa-info-circle:before{content:"\f05a"}.fa-instagram:before{content:"\f16d"}.fa-intercom:before{content:"\f7af"}.fa-internet-explorer:before{content:"\f26b"}.fa-invision:before{content:"\f7b0"}.fa-ioxhost:before{content:"\f208"}.fa-italic:before{content:"\f033"}.fa-itch-io:before{content:"\f83a"}.fa-itunes:before{content:"\f3b4"}.fa-itunes-note:before{content:"\f3b5"}.fa-java:before{content:"\f4e4"}.fa-jedi:before{content:"\f669"}.fa-jedi-order:before{content:"\f50e"}.fa-jenkins:before{content:"\f3b6"}.fa-jira:before{content:"\f7b1"}.fa-joget:before{content:"\f3b7"}.fa-joint:before{content:"\f595"}.fa-joomla:before{content:"\f1aa"}.fa-journal-whills:before{content:"\f66a"}.fa-js:before{content:"\f3b8"}.fa-js-square:before{content:"\f3b9"}.fa-jsfiddle:before{content:"\f1cc"}.fa-kaaba:before{content:"\f66b"}.fa-kaggle:before{content:"\f5fa"}.fa-key:before{content:"\f084"}.fa-keybase:before{content:"\f4f5"}.fa-keyboard:before{content:"\f11c"}.fa-keycdn:before{content:"\f3ba"}.fa-khanda:before{content:"\f66d"}.fa-kickstarter:before{content:"\f3bb"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-kiss:before{content:"\f596"}.fa-kiss-beam:before{content:"\f597"}.fa-kiss-wink-heart:before{content:"\f598"}.fa-kiwi-bird:before{content:"\f535"}.fa-korvue:before{content:"\f42f"}.fa-landmark:before{content:"\f66f"}.fa-language:before{content:"\f1ab"}.fa-laptop:before{content:"\f109"}.fa-laptop-code:before{content:"\f5fc"}.fa-laptop-medical:before{content:"\f812"}.fa-laravel:before{content:"\f3bd"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-laugh:before{content:"\f599"}.fa-laugh-beam:before{content:"\f59a"}.fa-laugh-squint:before{content:"\f59b"}.fa-laugh-wink:before{content:"\f59c"}.fa-layer-group:before{content:"\f5fd"}.fa-leaf:before{content:"\f06c"}.fa-leanpub:before{content:"\f212"}.fa-lemon:before{content:"\f094"}.fa-less:before{content:"\f41d"}.fa-less-than:before{content:"\f536"}.fa-less-than-equal:before{content:"\f537"}.fa-level-down-alt:before{content:"\f3be"}.fa-level-up-alt:before{content:"\f3bf"}.fa-life-ring:before{content:"\f1cd"}.fa-lightbulb:before{content:"\f0eb"}.fa-line:before{content:"\f3c0"}.fa-link:before{content:"\f0c1"}.fa-linkedin:before{content:"\f08c"}.fa-linkedin-in:before{content:"\f0e1"}.fa-linode:before{content:"\f2b8"}.fa-linux:before{content:"\f17c"}.fa-lira-sign:before{content:"\f195"}.fa-list:before{content:"\f03a"}.fa-list-alt:before{content:"\f022"}.fa-list-ol:before{content:"\f0cb"}.fa-list-ul:before{content:"\f0ca"}.fa-location-arrow:before{content:"\f124"}.fa-lock:before{content:"\f023"}.fa-lock-open:before{content:"\f3c1"}.fa-long-arrow-alt-down:before{content:"\f309"}.fa-long-arrow-alt-left:before{content:"\f30a"}.fa-long-arrow-alt-right:before{content:"\f30b"}.fa-long-arrow-alt-up:before{content:"\f30c"}.fa-low-vision:before{content:"\f2a8"}.fa-luggage-cart:before{content:"\f59d"}.fa-lyft:before{content:"\f3c3"}.fa-magento:before{content:"\f3c4"}.fa-magic:before{content:"\f0d0"}.fa-magnet:before{content:"\f076"}.fa-mail-bulk:before{content:"\f674"}.fa-mailchimp:before{content:"\f59e"}.fa-male:before{content:"\f183"}.fa-mandalorian:before{content:"\f50f"}.fa-map:before{content:"\f279"}.fa-map-marked:before{content:"\f59f"}.fa-map-marked-alt:before{content:"\f5a0"}.fa-map-marker:before{content:"\f041"}.fa-map-marker-alt:before{content:"\f3c5"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-markdown:before{content:"\f60f"}.fa-marker:before{content:"\f5a1"}.fa-mars:before{content:"\f222"}.fa-mars-double:before{content:"\f227"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mask:before{content:"\f6fa"}.fa-mastodon:before{content:"\f4f6"}.fa-maxcdn:before{content:"\f136"}.fa-medal:before{content:"\f5a2"}.fa-medapps:before{content:"\f3c6"}.fa-medium:before{content:"\f23a"}.fa-medium-m:before{content:"\f3c7"}.fa-medkit:before{content:"\f0fa"}.fa-medrt:before{content:"\f3c8"}.fa-meetup:before{content:"\f2e0"}.fa-megaport:before{content:"\f5a3"}.fa-meh:before{content:"\f11a"}.fa-meh-blank:before{content:"\f5a4"}.fa-meh-rolling-eyes:before{content:"\f5a5"}.fa-memory:before{content:"\f538"}.fa-mendeley:before{content:"\f7b3"}.fa-menorah:before{content:"\f676"}.fa-mercury:before{content:"\f223"}.fa-meteor:before{content:"\f753"}.fa-microchip:before{content:"\f2db"}.fa-microphone:before{content:"\f130"}.fa-microphone-alt:before{content:"\f3c9"}.fa-microphone-alt-slash:before{content:"\f539"}.fa-microphone-slash:before{content:"\f131"}.fa-microscope:before{content:"\f610"}.fa-microsoft:before{content:"\f3ca"}.fa-minus:before{content:"\f068"}.fa-minus-circle:before{content:"\f056"}.fa-minus-square:before{content:"\f146"}.fa-mitten:before{content:"\f7b5"}.fa-mix:before{content:"\f3cb"}.fa-mixcloud:before{content:"\f289"}.fa-mizuni:before{content:"\f3cc"}.fa-mobile:before{content:"\f10b"}.fa-mobile-alt:before{content:"\f3cd"}.fa-modx:before{content:"\f285"}.fa-monero:before{content:"\f3d0"}.fa-money-bill:before{content:"\f0d6"}.fa-money-bill-alt:before{content:"\f3d1"}.fa-money-bill-wave:before{content:"\f53a"}.fa-money-bill-wave-alt:before{content:"\f53b"}.fa-money-check:before{content:"\f53c"}.fa-money-check-alt:before{content:"\f53d"}.fa-monument:before{content:"\f5a6"}.fa-moon:before{content:"\f186"}.fa-mortar-pestle:before{content:"\f5a7"}.fa-mosque:before{content:"\f678"}.fa-motorcycle:before{content:"\f21c"}.fa-mountain:before{content:"\f6fc"}.fa-mouse-pointer:before{content:"\f245"}.fa-mug-hot:before{content:"\f7b6"}.fa-music:before{content:"\f001"}.fa-napster:before{content:"\f3d2"}.fa-neos:before{content:"\f612"}.fa-network-wired:before{content:"\f6ff"}.fa-neuter:before{content:"\f22c"}.fa-newspaper:before{content:"\f1ea"}.fa-nimblr:before{content:"\f5a8"}.fa-node:before{content:"\f419"}.fa-node-js:before{content:"\f3d3"}.fa-not-equal:before{content:"\f53e"}.fa-notes-medical:before{content:"\f481"}.fa-npm:before{content:"\f3d4"}.fa-ns8:before{content:"\f3d5"}.fa-nutritionix:before{content:"\f3d6"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-oil-can:before{content:"\f613"}.fa-old-republic:before{content:"\f510"}.fa-om:before{content:"\f679"}.fa-opencart:before{content:"\f23d"}.fa-openid:before{content:"\f19b"}.fa-opera:before{content:"\f26a"}.fa-optin-monster:before{content:"\f23c"}.fa-osi:before{content:"\f41a"}.fa-otter:before{content:"\f700"}.fa-outdent:before{content:"\f03b"}.fa-page4:before{content:"\f3d7"}.fa-pagelines:before{content:"\f18c"}.fa-pager:before{content:"\f815"}.fa-paint-brush:before{content:"\f1fc"}.fa-paint-roller:before{content:"\f5aa"}.fa-palette:before{content:"\f53f"}.fa-palfed:before{content:"\f3d8"}.fa-pallet:before{content:"\f482"}.fa-paper-plane:before{content:"\f1d8"}.fa-paperclip:before{content:"\f0c6"}.fa-parachute-box:before{content:"\f4cd"}.fa-paragraph:before{content:"\f1dd"}.fa-parking:before{content:"\f540"}.fa-passport:before{content:"\f5ab"}.fa-pastafarianism:before{content:"\f67b"}.fa-paste:before{content:"\f0ea"}.fa-patreon:before{content:"\f3d9"}.fa-pause:before{content:"\f04c"}.fa-pause-circle:before{content:"\f28b"}.fa-paw:before{content:"\f1b0"}.fa-paypal:before{content:"\f1ed"}.fa-peace:before{content:"\f67c"}.fa-pen:before{content:"\f304"}.fa-pen-alt:before{content:"\f305"}.fa-pen-fancy:before{content:"\f5ac"}.fa-pen-nib:before{content:"\f5ad"}.fa-pen-square:before{content:"\f14b"}.fa-pencil-alt:before{content:"\f303"}.fa-pencil-ruler:before{content:"\f5ae"}.fa-penny-arcade:before{content:"\f704"}.fa-people-carry:before{content:"\f4ce"}.fa-pepper-hot:before{content:"\f816"}.fa-percent:before{content:"\f295"}.fa-percentage:before{content:"\f541"}.fa-periscope:before{content:"\f3da"}.fa-person-booth:before{content:"\f756"}.fa-phabricator:before{content:"\f3db"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-phoenix-squadron:before{content:"\f511"}.fa-phone:before{content:"\f095"}.fa-phone-alt:before{content:"\f879"}.fa-phone-slash:before{content:"\f3dd"}.fa-phone-square:before{content:"\f098"}.fa-phone-square-alt:before{content:"\f87b"}.fa-phone-volume:before{content:"\f2a0"}.fa-photo-video:before{content:"\f87c"}.fa-php:before{content:"\f457"}.fa-pied-piper:before{content:"\f2ae"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-pied-piper-hat:before{content:"\f4e5"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-piggy-bank:before{content:"\f4d3"}.fa-pills:before{content:"\f484"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-p:before{content:"\f231"}.fa-pinterest-square:before{content:"\f0d3"}.fa-pizza-slice:before{content:"\f818"}.fa-place-of-worship:before{content:"\f67f"}.fa-plane:before{content:"\f072"}.fa-plane-arrival:before{content:"\f5af"}.fa-plane-departure:before{content:"\f5b0"}.fa-play:before{content:"\f04b"}.fa-play-circle:before{content:"\f144"}.fa-playstation:before{content:"\f3df"}.fa-plug:before{content:"\f1e6"}.fa-plus:before{content:"\f067"}.fa-plus-circle:before{content:"\f055"}.fa-plus-square:before{content:"\f0fe"}.fa-podcast:before{content:"\f2ce"}.fa-poll:before{content:"\f681"}.fa-poll-h:before{content:"\f682"}.fa-poo:before{content:"\f2fe"}.fa-poo-storm:before{content:"\f75a"}.fa-poop:before{content:"\f619"}.fa-portrait:before{content:"\f3e0"}.fa-pound-sign:before{content:"\f154"}.fa-power-off:before{content:"\f011"}.fa-pray:before{content:"\f683"}.fa-praying-hands:before{content:"\f684"}.fa-prescription:before{content:"\f5b1"}.fa-prescription-bottle:before{content:"\f485"}.fa-prescription-bottle-alt:before{content:"\f486"}.fa-print:before{content:"\f02f"}.fa-procedures:before{content:"\f487"}.fa-product-hunt:before{content:"\f288"}.fa-project-diagram:before{content:"\f542"}.fa-pushed:before{content:"\f3e1"}.fa-puzzle-piece:before{content:"\f12e"}.fa-python:before{content:"\f3e2"}.fa-qq:before{content:"\f1d6"}.fa-qrcode:before{content:"\f029"}.fa-question:before{content:"\f128"}.fa-question-circle:before{content:"\f059"}.fa-quidditch:before{content:"\f458"}.fa-quinscape:before{content:"\f459"}.fa-quora:before{content:"\f2c4"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-quran:before{content:"\f687"}.fa-r-project:before{content:"\f4f7"}.fa-radiation:before{content:"\f7b9"}.fa-radiation-alt:before{content:"\f7ba"}.fa-rainbow:before{content:"\f75b"}.fa-random:before{content:"\f074"}.fa-raspberry-pi:before{content:"\f7bb"}.fa-ravelry:before{content:"\f2d9"}.fa-react:before{content:"\f41b"}.fa-reacteurope:before{content:"\f75d"}.fa-readme:before{content:"\f4d5"}.fa-rebel:before{content:"\f1d0"}.fa-receipt:before{content:"\f543"}.fa-recycle:before{content:"\f1b8"}.fa-red-river:before{content:"\f3e3"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-alien:before{content:"\f281"}.fa-reddit-square:before{content:"\f1a2"}.fa-redhat:before{content:"\f7bc"}.fa-redo:before{content:"\f01e"}.fa-redo-alt:before{content:"\f2f9"}.fa-registered:before{content:"\f25d"}.fa-remove-format:before{content:"\f87d"}.fa-renren:before{content:"\f18b"}.fa-reply:before{content:"\f3e5"}.fa-reply-all:before{content:"\f122"}.fa-replyd:before{content:"\f3e6"}.fa-republican:before{content:"\f75e"}.fa-researchgate:before{content:"\f4f8"}.fa-resolving:before{content:"\f3e7"}.fa-restroom:before{content:"\f7bd"}.fa-retweet:before{content:"\f079"}.fa-rev:before{content:"\f5b2"}.fa-ribbon:before{content:"\f4d6"}.fa-ring:before{content:"\f70b"}.fa-road:before{content:"\f018"}.fa-robot:before{content:"\f544"}.fa-rocket:before{content:"\f135"}.fa-rocketchat:before{content:"\f3e8"}.fa-rockrms:before{content:"\f3e9"}.fa-route:before{content:"\f4d7"}.fa-rss:before{content:"\f09e"}.fa-rss-square:before{content:"\f143"}.fa-ruble-sign:before{content:"\f158"}.fa-ruler:before{content:"\f545"}.fa-ruler-combined:before{content:"\f546"}.fa-ruler-horizontal:before{content:"\f547"}.fa-ruler-vertical:before{content:"\f548"}.fa-running:before{content:"\f70c"}.fa-rupee-sign:before{content:"\f156"}.fa-sad-cry:before{content:"\f5b3"}.fa-sad-tear:before{content:"\f5b4"}.fa-safari:before{content:"\f267"}.fa-salesforce:before{content:"\f83b"}.fa-sass:before{content:"\f41e"}.fa-satellite:before{content:"\f7bf"}.fa-satellite-dish:before{content:"\f7c0"}.fa-save:before{content:"\f0c7"}.fa-schlix:before{content:"\f3ea"}.fa-school:before{content:"\f549"}.fa-screwdriver:before{content:"\f54a"}.fa-scribd:before{content:"\f28a"}.fa-scroll:before{content:"\f70e"}.fa-sd-card:before{content:"\f7c2"}.fa-search:before{content:"\f002"}.fa-search-dollar:before{content:"\f688"}.fa-search-location:before{content:"\f689"}.fa-search-minus:before{content:"\f010"}.fa-search-plus:before{content:"\f00e"}.fa-searchengin:before{content:"\f3eb"}.fa-seedling:before{content:"\f4d8"}.fa-sellcast:before{content:"\f2da"}.fa-sellsy:before{content:"\f213"}.fa-server:before{content:"\f233"}.fa-servicestack:before{content:"\f3ec"}.fa-shapes:before{content:"\f61f"}.fa-share:before{content:"\f064"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-share-square:before{content:"\f14d"}.fa-shekel-sign:before{content:"\f20b"}.fa-shield-alt:before{content:"\f3ed"}.fa-ship:before{content:"\f21a"}.fa-shipping-fast:before{content:"\f48b"}.fa-shirtsinbulk:before{content:"\f214"}.fa-shoe-prints:before{content:"\f54b"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-shopping-cart:before{content:"\f07a"}.fa-shopware:before{content:"\f5b5"}.fa-shower:before{content:"\f2cc"}.fa-shuttle-van:before{content:"\f5b6"}.fa-sign:before{content:"\f4d9"}.fa-sign-in-alt:before{content:"\f2f6"}.fa-sign-language:before{content:"\f2a7"}.fa-sign-out-alt:before{content:"\f2f5"}.fa-signal:before{content:"\f012"}.fa-signature:before{content:"\f5b7"}.fa-sim-card:before{content:"\f7c4"}.fa-simplybuilt:before{content:"\f215"}.fa-sistrix:before{content:"\f3ee"}.fa-sitemap:before{content:"\f0e8"}.fa-sith:before{content:"\f512"}.fa-skating:before{content:"\f7c5"}.fa-sketch:before{content:"\f7c6"}.fa-skiing:before{content:"\f7c9"}.fa-skiing-nordic:before{content:"\f7ca"}.fa-skull:before{content:"\f54c"}.fa-skull-crossbones:before{content:"\f714"}.fa-skyatlas:before{content:"\f216"}.fa-skype:before{content:"\f17e"}.fa-slack:before{content:"\f198"}.fa-slack-hash:before{content:"\f3ef"}.fa-slash:before{content:"\f715"}.fa-sleigh:before{content:"\f7cc"}.fa-sliders-h:before{content:"\f1de"}.fa-slideshare:before{content:"\f1e7"}.fa-smile:before{content:"\f118"}.fa-smile-beam:before{content:"\f5b8"}.fa-smile-wink:before{content:"\f4da"}.fa-smog:before{content:"\f75f"}.fa-smoking:before{content:"\f48d"}.fa-smoking-ban:before{content:"\f54d"}.fa-sms:before{content:"\f7cd"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-snowboarding:before{content:"\f7ce"}.fa-snowflake:before{content:"\f2dc"}.fa-snowman:before{content:"\f7d0"}.fa-snowplow:before{content:"\f7d2"}.fa-socks:before{content:"\f696"}.fa-solar-panel:before{content:"\f5ba"}.fa-sort:before{content:"\f0dc"}.fa-sort-alpha-down:before{content:"\f15d"}.fa-sort-alpha-down-alt:before{content:"\f881"}.fa-sort-alpha-up:before{content:"\f15e"}.fa-sort-alpha-up-alt:before{content:"\f882"}.fa-sort-amount-down:before{content:"\f160"}.fa-sort-amount-down-alt:before{content:"\f884"}.fa-sort-amount-up:before{content:"\f161"}.fa-sort-amount-up-alt:before{content:"\f885"}.fa-sort-down:before{content:"\f0dd"}.fa-sort-numeric-down:before{content:"\f162"}.fa-sort-numeric-down-alt:before{content:"\f886"}.fa-sort-numeric-up:before{content:"\f163"}.fa-sort-numeric-up-alt:before{content:"\f887"}.fa-sort-up:before{content:"\f0de"}.fa-soundcloud:before{content:"\f1be"}.fa-sourcetree:before{content:"\f7d3"}.fa-spa:before{content:"\f5bb"}.fa-space-shuttle:before{content:"\f197"}.fa-speakap:before{content:"\f3f3"}.fa-speaker-deck:before{content:"\f83c"}.fa-spell-check:before{content:"\f891"}.fa-spider:before{content:"\f717"}.fa-spinner:before{content:"\f110"}.fa-splotch:before{content:"\f5bc"}.fa-spotify:before{content:"\f1bc"}.fa-spray-can:before{content:"\f5bd"}.fa-square:before{content:"\f0c8"}.fa-square-full:before{content:"\f45c"}.fa-square-root-alt:before{content:"\f698"}.fa-squarespace:before{content:"\f5be"}.fa-stack-exchange:before{content:"\f18d"}.fa-stack-overflow:before{content:"\f16c"}.fa-stackpath:before{content:"\f842"}.fa-stamp:before{content:"\f5bf"}.fa-star:before{content:"\f005"}.fa-star-and-crescent:before{content:"\f699"}.fa-star-half:before{content:"\f089"}.fa-star-half-alt:before{content:"\f5c0"}.fa-star-of-david:before{content:"\f69a"}.fa-star-of-life:before{content:"\f621"}.fa-staylinked:before{content:"\f3f5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-steam-symbol:before{content:"\f3f6"}.fa-step-backward:before{content:"\f048"}.fa-step-forward:before{content:"\f051"}.fa-stethoscope:before{content:"\f0f1"}.fa-sticker-mule:before{content:"\f3f7"}.fa-sticky-note:before{content:"\f249"}.fa-stop:before{content:"\f04d"}.fa-stop-circle:before{content:"\f28d"}.fa-stopwatch:before{content:"\f2f2"}.fa-store:before{content:"\f54e"}.fa-store-alt:before{content:"\f54f"}.fa-strava:before{content:"\f428"}.fa-stream:before{content:"\f550"}.fa-street-view:before{content:"\f21d"}.fa-strikethrough:before{content:"\f0cc"}.fa-stripe:before{content:"\f429"}.fa-stripe-s:before{content:"\f42a"}.fa-stroopwafel:before{content:"\f551"}.fa-studiovinari:before{content:"\f3f8"}.fa-stumbleupon:before{content:"\f1a4"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-subscript:before{content:"\f12c"}.fa-subway:before{content:"\f239"}.fa-suitcase:before{content:"\f0f2"}.fa-suitcase-rolling:before{content:"\f5c1"}.fa-sun:before{content:"\f185"}.fa-superpowers:before{content:"\f2dd"}.fa-superscript:before{content:"\f12b"}.fa-supple:before{content:"\f3f9"}.fa-surprise:before{content:"\f5c2"}.fa-suse:before{content:"\f7d6"}.fa-swatchbook:before{content:"\f5c3"}.fa-swimmer:before{content:"\f5c4"}.fa-swimming-pool:before{content:"\f5c5"}.fa-symfony:before{content:"\f83d"}.fa-synagogue:before{content:"\f69b"}.fa-sync:before{content:"\f021"}.fa-sync-alt:before{content:"\f2f1"}.fa-syringe:before{content:"\f48e"}.fa-table:before{content:"\f0ce"}.fa-table-tennis:before{content:"\f45d"}.fa-tablet:before{content:"\f10a"}.fa-tablet-alt:before{content:"\f3fa"}.fa-tablets:before{content:"\f490"}.fa-tachometer-alt:before{content:"\f3fd"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-tape:before{content:"\f4db"}.fa-tasks:before{content:"\f0ae"}.fa-taxi:before{content:"\f1ba"}.fa-teamspeak:before{content:"\f4f9"}.fa-teeth:before{content:"\f62e"}.fa-teeth-open:before{content:"\f62f"}.fa-telegram:before{content:"\f2c6"}.fa-telegram-plane:before{content:"\f3fe"}.fa-temperature-high:before{content:"\f769"}.fa-temperature-low:before{content:"\f76b"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-tenge:before{content:"\f7d7"}.fa-terminal:before{content:"\f120"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-th:before{content:"\f00a"}.fa-th-large:before{content:"\f009"}.fa-th-list:before{content:"\f00b"}.fa-the-red-yeti:before{content:"\f69d"}.fa-theater-masks:before{content:"\f630"}.fa-themeco:before{content:"\f5c6"}.fa-themeisle:before{content:"\f2b2"}.fa-thermometer:before{content:"\f491"}.fa-thermometer-empty:before{content:"\f2cb"}.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-think-peaks:before{content:"\f731"}.fa-thumbs-down:before{content:"\f165"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbtack:before{content:"\f08d"}.fa-ticket-alt:before{content:"\f3ff"}.fa-times:before{content:"\f00d"}.fa-times-circle:before{content:"\f057"}.fa-tint:before{content:"\f043"}.fa-tint-slash:before{content:"\f5c7"}.fa-tired:before{content:"\f5c8"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-toilet:before{content:"\f7d8"}.fa-toilet-paper:before{content:"\f71e"}.fa-toolbox:before{content:"\f552"}.fa-tools:before{content:"\f7d9"}.fa-tooth:before{content:"\f5c9"}.fa-torah:before{content:"\f6a0"}.fa-torii-gate:before{content:"\f6a1"}.fa-tractor:before{content:"\f722"}.fa-trade-federation:before{content:"\f513"}.fa-trademark:before{content:"\f25c"}.fa-traffic-light:before{content:"\f637"}.fa-train:before{content:"\f238"}.fa-tram:before{content:"\f7da"}.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-trash:before{content:"\f1f8"}.fa-trash-alt:before{content:"\f2ed"}.fa-trash-restore:before{content:"\f829"}.fa-trash-restore-alt:before{content:"\f82a"}.fa-tree:before{content:"\f1bb"}.fa-trello:before{content:"\f181"}.fa-tripadvisor:before{content:"\f262"}.fa-trophy:before{content:"\f091"}.fa-truck:before{content:"\f0d1"}.fa-truck-loading:before{content:"\f4de"}.fa-truck-monster:before{content:"\f63b"}.fa-truck-moving:before{content:"\f4df"}.fa-truck-pickup:before{content:"\f63c"}.fa-tshirt:before{content:"\f553"}.fa-tty:before{content:"\f1e4"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-tv:before{content:"\f26c"}.fa-twitch:before{content:"\f1e8"}.fa-twitter:before{content:"\f099"}.fa-twitter-square:before{content:"\f081"}.fa-typo3:before{content:"\f42b"}.fa-uber:before{content:"\f402"}.fa-ubuntu:before{content:"\f7df"}.fa-uikit:before{content:"\f403"}.fa-umbrella:before{content:"\f0e9"}.fa-umbrella-beach:before{content:"\f5ca"}.fa-underline:before{content:"\f0cd"}.fa-undo:before{content:"\f0e2"}.fa-undo-alt:before{content:"\f2ea"}.fa-uniregistry:before{content:"\f404"}.fa-universal-access:before{content:"\f29a"}.fa-university:before{content:"\f19c"}.fa-unlink:before{content:"\f127"}.fa-unlock:before{content:"\f09c"}.fa-unlock-alt:before{content:"\f13e"}.fa-untappd:before{content:"\f405"}.fa-upload:before{content:"\f093"}.fa-ups:before{content:"\f7e0"}.fa-usb:before{content:"\f287"}.fa-user:before{content:"\f007"}.fa-user-alt:before{content:"\f406"}.fa-user-alt-slash:before{content:"\f4fa"}.fa-user-astronaut:before{content:"\f4fb"}.fa-user-check:before{content:"\f4fc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-clock:before{content:"\f4fd"}.fa-user-cog:before{content:"\f4fe"}.fa-user-edit:before{content:"\f4ff"}.fa-user-friends:before{content:"\f500"}.fa-user-graduate:before{content:"\f501"}.fa-user-injured:before{content:"\f728"}.fa-user-lock:before{content:"\f502"}.fa-user-md:before{content:"\f0f0"}.fa-user-minus:before{content:"\f503"}.fa-user-ninja:before{content:"\f504"}.fa-user-nurse:before{content:"\f82f"}.fa-user-plus:before{content:"\f234"}.fa-user-secret:before{content:"\f21b"}.fa-user-shield:before{content:"\f505"}.fa-user-slash:before{content:"\f506"}.fa-user-tag:before{content:"\f507"}.fa-user-tie:before{content:"\f508"}.fa-user-times:before{content:"\f235"}.fa-users:before{content:"\f0c0"}.fa-users-cog:before{content:"\f509"}.fa-usps:before{content:"\f7e1"}.fa-ussunnah:before{content:"\f407"}.fa-utensil-spoon:before{content:"\f2e5"}.fa-utensils:before{content:"\f2e7"}.fa-vaadin:before{content:"\f408"}.fa-vector-square:before{content:"\f5cb"}.fa-venus:before{content:"\f221"}.fa-venus-double:before{content:"\f226"}.fa-venus-mars:before{content:"\f228"}.fa-viacoin:before{content:"\f237"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-vial:before{content:"\f492"}.fa-vials:before{content:"\f493"}.fa-viber:before{content:"\f409"}.fa-video:before{content:"\f03d"}.fa-video-slash:before{content:"\f4e2"}.fa-vihara:before{content:"\f6a7"}.fa-vimeo:before{content:"\f40a"}.fa-vimeo-square:before{content:"\f194"}.fa-vimeo-v:before{content:"\f27d"}.fa-vine:before{content:"\f1ca"}.fa-vk:before{content:"\f189"}.fa-vnv:before{content:"\f40b"}.fa-voicemail:before{content:"\f897"}.fa-volleyball-ball:before{content:"\f45f"}.fa-volume-down:before{content:"\f027"}.fa-volume-mute:before{content:"\f6a9"}.fa-volume-off:before{content:"\f026"}.fa-volume-up:before{content:"\f028"}.fa-vote-yea:before{content:"\f772"}.fa-vr-cardboard:before{content:"\f729"}.fa-vuejs:before{content:"\f41f"}.fa-walking:before{content:"\f554"}.fa-wallet:before{content:"\f555"}.fa-warehouse:before{content:"\f494"}.fa-water:before{content:"\f773"}.fa-wave-square:before{content:"\f83e"}.fa-waze:before{content:"\f83f"}.fa-weebly:before{content:"\f5cc"}.fa-weibo:before{content:"\f18a"}.fa-weight:before{content:"\f496"}.fa-weight-hanging:before{content:"\f5cd"}.fa-weixin:before{content:"\f1d7"}.fa-whatsapp:before{content:"\f232"}.fa-whatsapp-square:before{content:"\f40c"}.fa-wheelchair:before{content:"\f193"}.fa-whmcs:before{content:"\f40d"}.fa-wifi:before{content:"\f1eb"}.fa-wikipedia-w:before{content:"\f266"}.fa-wind:before{content:"\f72e"}.fa-window-close:before{content:"\f410"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-windows:before{content:"\f17a"}.fa-wine-bottle:before{content:"\f72f"}.fa-wine-glass:before{content:"\f4e3"}.fa-wine-glass-alt:before{content:"\f5ce"}.fa-wix:before{content:"\f5cf"}.fa-wizards-of-the-coast:before{content:"\f730"}.fa-wolf-pack-battalion:before{content:"\f514"}.fa-won-sign:before{content:"\f159"}.fa-wordpress:before{content:"\f19a"}.fa-wordpress-simple:before{content:"\f411"}.fa-wpbeginner:before{content:"\f297"}.fa-wpexplorer:before{content:"\f2de"}.fa-wpforms:before{content:"\f298"}.fa-wpressr:before{content:"\f3e4"}.fa-wrench:before{content:"\f0ad"}.fa-x-ray:before{content:"\f497"}.fa-xbox:before{content:"\f412"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-y-combinator:before{content:"\f23b"}.fa-yahoo:before{content:"\f19e"}.fa-yammer:before{content:"\f840"}.fa-yandex:before{content:"\f413"}.fa-yandex-international:before{content:"\f414"}.fa-yarn:before{content:"\f7e3"}.fa-yelp:before{content:"\f1e9"}.fa-yen-sign:before{content:"\f157"}.fa-yin-yang:before{content:"\f6ad"}.fa-yoast:before{content:"\f2b1"}.fa-youtube:before{content:"\f167"}.fa-youtube-square:before{content:"\f431"}.fa-zhihu:before{content:"\f63f"}.sr-only{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sr-only-focusable:active,.sr-only-focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:400;font-display:auto;src:url(fa-regular-400.eot);src:url(fa-regular-400.eot?#iefix) format("embedded-opentype"),url(fa-regular-400.woff) format("woff")}.far{font-weight:400}@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:900;font-display:auto;src:url(fa-solid-900.eot);src:url(fa-solid-900.eot?#iefix) format("embedded-opentype"),url(fa-solid-900.woff) format("woff")}.fa,.far,.fas{font-family:"Font Awesome 5 Free"}.fa,.fas{font-weight:900} diff --git a/modular_splurt/code/modules/admin/Transform.dm b/modular_splurt/code/modules/admin/Transform.dm index 755dbe1132..5fbc48be22 100644 --- a/modular_splurt/code/modules/admin/Transform.dm +++ b/modular_splurt/code/modules/admin/Transform.dm @@ -59,7 +59,7 @@ GLOBAL_LIST_INIT(pp_transformables, list( ), list( name = "Legion", - key = /mob/living/simple_animal/hostile/asteroid/elite/legionnaire + key = /mob/living/simple_animal/hostile/asteroid/hivelord/legion ), list( name = "Blood-Drunk Miner", @@ -69,17 +69,13 @@ GLOBAL_LIST_INIT(pp_transformables, list( name = "Gladiator", key = /mob/living/simple_animal/hostile/megafauna/gladiator ), - list( - name = "Rough Process", - key = /mob/living/simple_animal/hostile/megafauna/rogueprocess - ), list( name = "Dragon", key = /mob/living/simple_animal/hostile/megafauna/dragon ), list( name = "Legion Hivelord", - key = /mob/living/simple_animal/hostile/asteroid/hivelord/legion + key = /mob/living/simple_animal/hostile/asteroid/elite/legionnaire ) ) ), diff --git a/modular_splurt/code/modules/client/client_defines.dm b/modular_splurt/code/modules/client/client_defines.dm deleted file mode 100644 index 7149778f20..0000000000 --- a/modular_splurt/code/modules/client/client_defines.dm +++ /dev/null @@ -1 +0,0 @@ -/client diff --git a/tgstation.dme b/tgstation.dme index 9f6a142747..936f94560b 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -2030,7 +2030,6 @@ #include "code\modules\chatter\chatter.dm" #include "code\modules\client\client_colour.dm" #include "code\modules\client\client_defines.dm" -#include "modular_splurt\code\modules\client\client_defines.dm" #include "code\modules\client\client_procs.dm" #include "code\modules\client\client_vpn_detect.dm" #include "code\modules\client\message.dm" diff --git a/tgui/packages/tgui/interfaces/JobbanPanel.js b/tgui/packages/tgui/interfaces/JobbanPanel.js deleted file mode 100644 index a096563e00..0000000000 --- a/tgui/packages/tgui/interfaces/JobbanPanel.js +++ /dev/null @@ -1,145 +0,0 @@ -import { Fragment } from "inferno"; -import { useBackend, useLocalState } from '../backend'; -import { Input, Collapsible, Button, Flex, Section, Tabs, Box, Dropdown, Slider, Tooltip, NoticeBox, Blink } from '../components'; -import { Window } from '../layouts'; - -export const JobbanPanel = (props, context) => { - const { act, data } = useBackend(context); - const [tab, setTab] = useLocalState(context, 'tab', 0); - // const PageComponent = PAGES[pageIndex].component(); - const { roles, client_ckey } = data; - - return ( - - - - -
- - {roles.map((role_category, i) => { return ( - setTab(i)}> - {role_category.category_name} - - ); })} - -
-
- - - -
-
-
- ); -}; - -const GeneralActions = (props, context) => { - const { act, data } = useBackend(context); - const [tab] = useLocalState(context, 'tab', 0); - const { roles, is_antag_banned } = data; - return ( -
-
-
-
- ); -}; diff --git a/tgui/packages/tgui/interfaces/PlayerPanel2.js b/tgui/packages/tgui/interfaces/PlayerPanel2.js index 666e707a70..4db967d76c 100644 --- a/tgui/packages/tgui/interfaces/PlayerPanel2.js +++ b/tgui/packages/tgui/interfaces/PlayerPanel2.js @@ -50,20 +50,11 @@ const PAGES = [ icon: "laugh", }, { - title: 'aaaaaa', - component: () => FunActions, + title: 'Other', + component: () => AntagActions, color: "blue", - icon: "laugh", + icon: "crosshairs", }, - // { - // title: 'Antag', - // component: () => AntagActions, - // color: "blue", - // icon: "crosshairs", - // canAccess: data => { - // return (data.is_type_mob_human || data.is_xeno); - // }, - // }, ]; export const PlayerPanel2 = (props, context) => { @@ -130,7 +121,6 @@ export const PlayerPanel2 = (props, context) => { Client: {client_ckey} -
-
+
{ + setMobScale(initial_scale); + act("scale", { new_scale: initial_scale }); + }} + /> + }> @@ -443,7 +442,7 @@ const FeatureBans = (props, context) => { const GeneralActions = (props, context) => { const { act, data } = useBackend(context); - const { client_ckey, is_type_mob_living, is_type_mob_human, mob_type } = data; + const { client_ckey, mob_type, admin_mob_type } = data; return (
@@ -453,28 +452,19 @@ const GeneralActions = (props, context) => { icon="heart" color="green" content="Rejuvenate" - disabled={!is_type_mob_living} + disabled={!mob_type.includes("/mob/living")} onClick={() => act("heal")} />
@@ -507,7 +497,7 @@ const GeneralActions = (props, context) => { width="100%" content="Select Equipment" icon="user-tie" - disabled={!is_type_mob_human} + disabled={!mob_type.includes("/mob/living/carbon/human")} onClick={() => act("select_equipment")} /> { icon="trash-alt" width="100%" height="100%" - disabled={!is_type_mob_human} + disabled={!mob_type.includes("/mob/living/carbon/human")} onClick={() => act("strip")} /> @@ -525,7 +515,7 @@ const GeneralActions = (props, context) => { icon="snowflake" width="100%" color="orange" - disabled={!is_type_mob_human} + disabled={!mob_type.includes("/mob/living/carbon/human")} onClick={() => act("cryo")} /> { content="Send To Lobby" color="orange" icon="undo" - disabled={mob_type !== "/mob/dead/observer"} + disabled={!mob_type.includes("/mob/dead/observer")} tooltip={mob_type !== "/mob/dead/observer" ? "Can only be used on ghosts" : ""} onClick={() => act("lobby")} />
+
+ + act("ghost")} + /> + act("take_control")} + /> + act("offer_control")} + /> + +
); }; const PunishmentActions = (props, context) => { const { act, data } = useBackend(context); - const { is_type_mob_living, is_frozen, is_slept, glob_mute_bits, + const { mob_type, is_frozen, is_slept, glob_mute_bits, client_muted, data_related_cid, data_related_ip, data_byond_version, data_player_join_date, data_account_join_date, active_role_ban_count, current_time } = data; @@ -568,7 +586,7 @@ const PunishmentActions = (props, context) => { content="Freeze" color={is_frozen ? "orange" : ""} icon={is_frozen ? 'check-square-o' : 'square-o'} - disabled={!is_type_mob_living} + disabled={!mob_type.includes("/mob/living")} onClick={() => act("freeze")} /> @@ -795,11 +820,11 @@ const FunActions = (props, context) => { unit="Range" value={expPower} stepPixelSize={15} - onChange={(e, value) => setExpPower(value)} + onDrag={(e, value) => setExpPower(value)} ranges={{ - good: [0, 8], - average: [8, 15], - bad: [15, 30], + green: [0, 8], + orange: [8, 15], + red: [15, 30], }} minValue={1} maxValue={30} @@ -921,88 +946,38 @@ const FunActions = (props, context) => { ); }; -// const AntagActions = (props, context) => { -// const { act, data } = useBackend(context); -// const { glob_hives, is_xeno, is_type_mob_human } = data; +const AntagActions = (props, context) => { + const { act, data } = useBackend(context); + const { mob_type } = data; -// const [selectedHivenumber, setHivenumber] = useLocalState(context, "selected_hivenumber", Object.keys(glob_hives)[0]); -// return ( -//
-// {!!is_type_mob_human && ( -//
-// -//
-// )} -//
setHivenumber(value)} -// /> -// }> -// -// {!!is_type_mob_human && ( -// -//
-//
-// ); -// }; + return ( +
+
+
+
+ ); +}; From a5d37e701fdae42c5046c035e6be9e30d863c195 Mon Sep 17 00:00:00 2001 From: James Date: Sat, 26 Feb 2022 09:07:58 +0000 Subject: [PATCH 07/15] More cleanup --- code/modules/admin/admin.dm | 5 +++ code/modules/admin/admin_verbs.dm | 3 +- code/modules/admin/player_panel2.dm | 31 ++++++++-------- code/modules/admin/topic.dm | 8 ++--- code/modules/admin/verbs/randomverbs.dm | 10 +----- code/modules/client/preferences.dm | 6 +++- code/modules/client/preferences_savefile.dm | 8 +++-- code/modules/mob/living/carbon/human/human.dm | 20 ----------- code/modules/mob/mob.dm | 2 -- code/modules/wiremod/core/admin_panel.dm | 3 +- .../code/modules/client/client_procs.dm | 7 ---- tgui/packages/tgui/interfaces/PlayerPanel2.js | 36 ++++++++++++++----- 12 files changed, 64 insertions(+), 75 deletions(-) diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index d6c66f109b..972b9e6977 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -16,6 +16,11 @@ set name = "Show Player Panel" set desc="Edit player (respawn, ban, heal, etc)" + // Based on client setting, use modern TGUI player panel instead + if(usr.client.prefs.use_new_playerpanel) + usr.client.holder.show_player_panel2(M) + return + if(!check_rights()) return diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index eb0522202c..e7f0dff2ae 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -29,7 +29,6 @@ GLOBAL_PROTECT(admin_verbs_admin) /client/proc/invisimin, /*allows our mob to go invisible/visible*/ // /datum/admins/proc/show_traitor_panel, /*interface which shows a mob's mind*/ -Removed due to rare practical use. Moved to debug verbs ~Errorage /datum/admins/proc/show_player_panel, /*shows an interface for individual players, with various links (links require additional flags*/ - /datum/admins/proc/show_player_panel2, /*shows an interface for individual players, with various links (links require additional flags*/ /datum/verbs/menu/Admin/verb/playerpanel, /client/proc/game_panel, /*game panel, allows to change game-mode etc*/ /client/proc/check_ai_laws, /*shows AI and borg laws*/ @@ -229,7 +228,7 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list( /datum/admins/proc/set_admin_notice, /client/proc/admin_ghost, /client/proc/toggle_view_range, - // /client/proc/cmd_admin_subtle_message, + /client/proc/cmd_admin_subtle_message, /client/proc/cmd_admin_check_contents, /datum/admins/proc/access_news_network, /client/proc/admin_end_shift, diff --git a/code/modules/admin/player_panel2.dm b/code/modules/admin/player_panel2.dm index d2b8b97cb0..52c9fdc6da 100644 --- a/code/modules/admin/player_panel2.dm +++ b/code/modules/admin/player_panel2.dm @@ -144,7 +144,6 @@ GLOBAL_LIST_INIT(pp_limbs, list( switch(action) if ("edit_rank") if (!targetMob.client?.ckey) - priority_announce("failed client ckey check") return var/list/context = list() @@ -156,7 +155,6 @@ GLOBAL_LIST_INIT(pp_limbs, list( else context["editrights"] = "add" - priority_announce("sending...") admin.holder.edit_rights_topic(context) if ("access_variables") @@ -183,23 +181,28 @@ GLOBAL_LIST_INIT(pp_limbs, list( log_admin("[key_name(admin)] ejected [key_name(targetMob)] from their body.") message_admins("[key_name_admin(admin)] ejected [key_name_admin(targetMob)] from their body.") to_chat(targetMob, "An admin has ejected you from your body.") - targetMob.ghostize(FALSE, penalize = TRUE, voluntary = TRUE) - targetMob.mind.key = null + targetMob.ghostize(FALSE) if ("offer_control") offer_control(targetMob) if ("take_control") - if(targetMob.ckey) - var/mob/dead/observer/ghost = new/mob/dead/observer(get_turf(targetMob), targetMob) - ghost.ckey = targetMob.ckey + var/mob/adminMob = admin.mob - message_admins("[key_name_admin(usr)] assumed direct control of [targetMob].") - log_admin("[key_name(usr)] assumed direct control of [targetMob].") - var/mob/adminmob = admin.mob - adminmob.transfer_ckey(targetMob, send_signal = FALSE) - if(isobserver(adminmob)) - qdel(adminmob) + // Disassociates observer mind from the body mind + if(targetMob.client) + targetMob.ghostize(FALSE) + else + for(var/mob/dead/observer/ghost in GLOB.dead_mob_list) + if(targetMob.mind == ghost.mind) + ghost.mind = null + + targetMob.ckey = adminMob.ckey + qdel(adminMob) + + message_admins("[key_name_admin(usr)] took control of [targetMob].") + log_admin("[key_name(usr)] took control of [targetMob].") + addtimer(CALLBACK(targetMob.mob_panel, /datum.proc/ui_interact, targetMob), 0.1 SECONDS) if ("smite") admin.smite(targetMob) @@ -362,11 +365,9 @@ GLOBAL_LIST_INIT(pp_limbs, list( var/mob/living/carbon/human/H = targetMob for(var/limb in params["limbs"]) - to_chat(usr, limb) if (!limb) continue - to_chat(usr, "passed") if (params["delimb_mode"]) var/obj/item/bodypart/L = H.get_bodypart(limb) if (!L) diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index df1fbc9244..324264bd30 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1800,8 +1800,7 @@ else if(href_list["adminplayeropts"]) var/mob/M = locate(href_list["adminplayeropts"]) - // show_player_panel(M) - show_player_panel2(M) + show_player_panel(M) else if(href_list["adminplayerobservefollow"]) if(!isobserver(usr) && !check_rights(R_ADMIN)) @@ -2103,8 +2102,6 @@ if(!check_rights(R_ADMIN)) return - priority_announce("DIGDUG - OLD PP SM VERB") - var/mob/M = locate(href_list["subtlemessage"]) usr.client.cmd_admin_subtle_message(M) @@ -2736,8 +2733,7 @@ var/mob/M = locate(href_list["mob"]) in GLOB.mob_list var/client/C = M.client usr.client.cmd_admin_mod_antag_rep(C, href_list["modantagrep"]) - // show_player_panel(M) - show_player_panel2(M) + show_player_panel(M) else if(href_list["slowquery"]) if(!check_rights(R_ADMIN)) diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 20325f74dc..4a73ec1032 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -19,27 +19,19 @@ admin_ticket_log(M, msg) SSblackbox.record_feedback("tally", "admin_verb", 1, "Drop Everything") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! -/client/proc/cmd_admin_subtle_message(mob/M in GLOB.mob_list) //, sender = null) +/client/proc/cmd_admin_subtle_message(mob/M in GLOB.mob_list, sender = null) set category = "Admin.Events" set name = "Subtle Message" - var/sender = null - - priority_announce("AAAAAAAAAAAAAAAAAAAAAAA") - if(!ismob(M)) - priority_announce("no mob!") return if(!check_rights(R_ADMIN)) - priority_announce("no admin!") return if (!sender) - priority_announce("lol") var/list/subtle_message_options = list("Voice in head", RADIO_CHANNEL_CENTCOM, RADIO_CHANNEL_SYNDICATE) sender = tgui_input_list(usr, "Choose the method of subtle messaging", "Subtle Message", subtle_message_options) if (!sender) - priority_announce("no sender!") return message_admins("[key_name_admin(src)] has started answering [ADMIN_LOOKUPFLW(M)]'s prayer / faction PM.") diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 7fd41197de..c821897746 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -95,6 +95,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/preferred_map = null var/preferred_chaos = null var/be_victim = null + var/use_new_playerpanel = FALSE var/disable_combat_cursor = FALSE var/pda_style = MONO var/pda_color = "#808000" @@ -1033,7 +1034,8 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "Adminhelp Sounds: [(toggles & SOUND_ADMINHELP)?"Enabled":"Disabled"]
" dat += "Announce Login: [(toggles & ANNOUNCE_LOGIN)?"Enabled":"Disabled"]
" dat += "
" - dat += "Combo HUD Lighting: [(toggles & COMBOHUD_LIGHTING)?"Full-bright":"No Change"]
" // sandstorm end - moves admins prefs to the right + dat += "Combo HUD Lighting: [(toggles & COMBOHUD_LIGHTING)?"Full-bright":"No Change"]
" + dat += "Use Modern Player Panel: [use_new_playerpanel ? "Yes" : "No"]
" // sandstorm end - moves admins prefs to the right dat += "

Citadel Preferences

" //Because fuck me if preferences can't be fucking modularized and expected to update in a reasonable timeframe. dat += "Widescreen: [widescreenpref ? "Enabled ([CONFIG_GET(string/default_view)])" : "Disabled (15x15)"]
" @@ -3169,6 +3171,8 @@ GLOBAL_LIST_EMPTY(preferences_datums) toggles ^= ANNOUNCE_LOGIN if("combohud_lighting") toggles ^= COMBOHUD_LIGHTING + if("use_new_playerpanel") + use_new_playerpanel = !use_new_playerpanel if("be_special") var/be_special_type = href_list["be_special_type"] diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index f497279770..24d57237ac 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -443,10 +443,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car S["no_tetris_storage"] >> no_tetris_storage // Splurt - S["be_victim"] >> be_victim - S["disable_combat_cursor"] >> disable_combat_cursor + S["be_victim"] >> be_victim + S["disable_combat_cursor"] >> disable_combat_cursor + S["use_new_playerpanel"] >> use_new_playerpanel //favorite outfits - S["favorite_outfits"] >> favorite_outfits + S["favorite_outfits"] >> favorite_outfits var/list/parsed_favs = list() for(var/typetext in favorite_outfits) @@ -629,6 +630,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car // Splurt WRITE_FILE(S["be_victim"], be_victim) WRITE_FILE(S["disable_combat_cursor"], disable_combat_cursor) + WRITE_FILE(S["use_new_playerpanel"], use_new_playerpanel) var/mob/living/carbon/human/H = parent.mob if(istype(H)) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index bdeb1ef452..d33541625c 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -722,26 +722,6 @@ copy_outfit() if(href_list[VV_HK_MOD_QUIRKS]) usr.client.toggle_quirk(src) - // if(!check_rights(R_SPAWN)) - // return - - // var/list/options = list("Clear"="Clear") - // for(var/x in subtypesof(/datum/quirk)) - // var/datum/quirk/T = x - // var/qname = initial(T.name) - // options[has_quirk(T) ? "[qname] (Remove)" : "[qname] (Add)"] = T - - // var/result = input(usr, "Choose quirk to add/remove","Quirk Mod") as null|anything in options - // if(result) - // if(result == "Clear") - // for(var/datum/quirk/q in roundstart_quirks) - // remove_quirk(q.type) - // else - // var/T = options[result] - // if(has_quirk(T)) - // remove_quirk(T) - // else - // add_quirk(T,TRUE) if(href_list[VV_HK_MAKE_MONKEY]) if(!check_rights(R_SPAWN)) return diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 7ea631e2a0..e890623957 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -62,7 +62,6 @@ /mob/proc/create_player_panel() QDEL_NULL(mob_panel) - mob_panel = new(src) /mob/proc/Cell() @@ -966,7 +965,6 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0) if(!check_rights(NONE)) return usr.client.holder.show_player_panel(src) - // usr.client.holder.show_player_panel2(src) if(href_list[VV_HK_GODMODE]) if(!check_rights(R_ADMIN)) return diff --git a/code/modules/wiremod/core/admin_panel.dm b/code/modules/wiremod/core/admin_panel.dm index 39c013edd5..33af02f514 100644 --- a/code/modules/wiremod/core/admin_panel.dm +++ b/code/modules/wiremod/core/admin_panel.dm @@ -61,8 +61,7 @@ circuit.ui_interact(usr) if ("open_player_panel") var/datum/mind/inserter = circuit.inserter_mind?.resolve() - // usr.client?.holder?.show_player_panel(inserter?.current) - usr.client?.holder?.show_player_panel2(inserter?.current) + usr.client?.holder?.show_player_panel(inserter?.current) return TRUE diff --git a/modular_splurt/code/modules/client/client_procs.dm b/modular_splurt/code/modules/client/client_procs.dm index fb2e9a1fdb..0da99d96de 100644 --- a/modular_splurt/code/modules/client/client_procs.dm +++ b/modular_splurt/code/modules/client/client_procs.dm @@ -38,15 +38,12 @@ var/spell_name = initial(spell.name) options[M.has_spell(spell) ? "[spell_name] (Remove)" : "[spell_name] (Add)"] = spell - to_chat(usr, "List length: [options.len]") - var/spell_to_modify = tgui_input_list(usr, "Choose spell to add/remove", "Mob Spells", options) // input(usr, "Choose quirk to add/remove","Quirk Mod") as null|anything in options if(QDELETED(M)) to_chat(usr, "Mob doesn't exist anymore") return - to_chat(usr, "Option selected to modify: [spell_to_modify]") if(spell_to_modify) if(spell_to_modify == "Clear") if (!M.mind) @@ -56,18 +53,14 @@ else var/T = options[spell_to_modify] if(M.has_spell(T)) - to_chat(usr, "Attempted to remove...") M.mind.RemoveSpell(T) else - to_chat(usr, "Attempted to add...") if (M.mind) M.mind.AddSpell(new T) else M.AddSpell(new T) message_admins("Spells given to mindless mobs will not be transferred in mindswap or cloning!") - to_chat(usr, "END") - /client/proc/teach_martial_art(mob/living/carbon/C) if (!istype(C)) to_chat(usr, "This can only be used on /mob/living/carbon.") diff --git a/tgui/packages/tgui/interfaces/PlayerPanel2.js b/tgui/packages/tgui/interfaces/PlayerPanel2.js index 4db967d76c..614295f749 100644 --- a/tgui/packages/tgui/interfaces/PlayerPanel2.js +++ b/tgui/packages/tgui/interfaces/PlayerPanel2.js @@ -51,7 +51,7 @@ const PAGES = [ }, { title: 'Other', - component: () => AntagActions, + component: () => OtherActions, color: "blue", icon: "crosshairs", }, @@ -544,7 +544,7 @@ const GeneralActions = (props, context) => { width="100%" content="Take Control" confirmColor="bad" - disabled={mob_type.includes("/mob/dead/observer") && admin_mob_type.includes("/mob/dead/observer")} + disabled={mob_type.includes("/mob/dead/observer") || !admin_mob_type.includes("/mob/dead/observer")} onClick={() => act("take_control")} /> { const FunActions = (props, context) => { const { act } = useBackend(context); + + const colours = { + 'White': '#a4bad6', + 'Dark': '#42474D', + 'Red': '#c51e1e', + 'Red Bright': '#FF0000', + 'Velvet': '#660015', + 'Green': '#059223', + 'Blue': '#6685f5', + 'Purple': '#800080', + 'Purple Dark': '#5000A0', + 'Narsie': '#973e3b', + 'Ratvar': '#BE8700', + }; + const [lockExplode, setLockExplode] = useLocalState(context, "explode_lock_toggle", true); const [empMode, setEmpMode] = useLocalState(context, "empMode", false); const [expPower, setExpPower] = useLocalState(context, "exp_power", 8); const [narrateSize, setNarrateSize] = useLocalState(context, "narrateSize", 1); const [narrateMessage, setNarrateMessage] = useLocalState(context, "narrateMessage", ""); - const [narrateColour, setNarrateColour] = useLocalState(context, "narrateColour", "White"); + const [narrateColour, setNarrateColour] = useLocalState(context, "narrateColour", Object.keys(colours)[0]); const [narrateFont, setNarrateFont] = useLocalState(context, "narrateFont", "Verdana"); const [narrateBold, setNarrateBold] = useLocalState(context, "narrateBold", false); const [narrateItalic, setNarrateItalic] = useLocalState(context, "narrateItalic", false); const [narrateGlobal, setNarrateGlobal] = useLocalState(context, "narrateGlobal", false); const [narrateRange, setNarrateRange] = useLocalState(context, "narrateRange", 7); + + const narrateStyles = { - 'color': narrateColour, + 'color': colours[narrateColour], 'font-size': narrateSize + 'rem', 'font-weight': (narrateBold ? 'bold' : ''), 'font-family': narrateFont, @@ -851,8 +868,9 @@ const FunActions = (props, context) => { setNarrateColour(value)} /> + options={Object.keys(colours)} + onSelected={(value) => setNarrateColour(value)} + /> { ); }; -const AntagActions = (props, context) => { +const OtherActions = (props, context) => { const { act, data } = useBackend(context); - const { mob_type } = data; + const { mob_type, client_ckey } = data; return (
@@ -959,6 +977,7 @@ const AntagActions = (props, context) => { p=".5rem" mb=".5rem" textAlign="center" + disabled={!client_ckey} onClick={(e) => act("traitor_panel")} />
From 7932cd7b076abb927529bdf945b6fa5c4e5e80e4 Mon Sep 17 00:00:00 2001 From: James Date: Sat, 26 Feb 2022 09:59:54 +0000 Subject: [PATCH 08/15] Cleanup 3 --- code/modules/admin/admin_verbs.dm | 1 - code/modules/admin/player_panel2.dm | 5 +---- code/modules/admin/topic.dm | 13 +++---------- code/modules/admin/verbs/randomverbs.dm | 5 ++++- code/modules/mob/mob.dm | 4 ---- code/modules/mob/mob_defines.dm | 3 --- config/entries/jexp.txt | 2 +- modular_splurt/code/modules/mob/mob.dm | 4 ++++ modular_splurt/code/modules/mob/mob_defines.dm | 3 +++ tgui/packages/tgui/interfaces/PlayerPanel2.js | 2 +- 10 files changed, 17 insertions(+), 25 deletions(-) diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index e7f0dff2ae..154df05356 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -228,7 +228,6 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list( /datum/admins/proc/set_admin_notice, /client/proc/admin_ghost, /client/proc/toggle_view_range, - /client/proc/cmd_admin_subtle_message, /client/proc/cmd_admin_check_contents, /datum/admins/proc/access_news_network, /client/proc/admin_end_shift, diff --git a/code/modules/admin/player_panel2.dm b/code/modules/admin/player_panel2.dm index 52c9fdc6da..5ad5f06946 100644 --- a/code/modules/admin/player_panel2.dm +++ b/code/modules/admin/player_panel2.dm @@ -168,7 +168,7 @@ GLOBAL_LIST_INIT(pp_limbs, list( admin.cmd_admin_pm_context(targetMob) if ("subtle_message") - admin.cmd_admin_subtle_message(targetMob) + admin.cmd_admin_subtle_headset_message(targetMob) if ("set_name") targetMob.vv_auto_rename(params["name"]) @@ -420,9 +420,6 @@ GLOBAL_LIST_INIT(pp_limbs, list( H.open_language_menu(usr) if ("ambitions") - // if (!targetMob.client) - // return - var/datum/mind/requesting_mind = targetMob.mind if(!istype(requesting_mind) || QDELETED(requesting_mind)) to_chat(usr, "This mind reference is no longer valid. It has probably since been destroyed.") diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 324264bd30..8d56133083 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -2027,21 +2027,14 @@ return var/mob/M = locate(href_list["CentComReply"]) - usr.client.cmd_admin_subtle_message(M, RADIO_CHANNEL_CENTCOM) + usr.client.cmd_admin_subtle_headset_message(M, RADIO_CHANNEL_CENTCOM) else if(href_list["SyndicateReply"]) if(!check_rights(R_ADMIN)) return var/mob/M = locate(href_list["SyndicateReply"]) - usr.client.cmd_admin_subtle_message(M, RADIO_CHANNEL_SYNDICATE) - - else if(href_list["HeadsetMessage"]) - if(!check_rights(R_ADMIN)) - return - - var/mob/M = locate(href_list["HeadsetMessage"]) - usr.client.cmd_admin_subtle_message(M) + usr.client.cmd_admin_subtle_headset_message(M, RADIO_CHANNEL_SYNDICATE) //ambition start else if(href_list["ObjectiveRequest"]) @@ -2103,7 +2096,7 @@ return var/mob/M = locate(href_list["subtlemessage"]) - usr.client.cmd_admin_subtle_message(M) + usr.client.cmd_admin_subtle_headset_message(M) else if(href_list["individuallog"]) if(!check_rights(R_ADMIN)) diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 4a73ec1032..4413560072 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -19,10 +19,13 @@ admin_ticket_log(M, msg) SSblackbox.record_feedback("tally", "admin_verb", 1, "Drop Everything") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! -/client/proc/cmd_admin_subtle_message(mob/M in GLOB.mob_list, sender = null) +/client/proc/cmd_admin_subtle_message(mob/M in GLOB.mob_list) set category = "Admin.Events" set name = "Subtle Message" + cmd_admin_subtle_headset_message(M) + +/client/proc/cmd_admin_subtle_headset_message(mob/M, sender = null) if(!ismob(M)) return if(!check_rights(R_ADMIN)) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index e890623957..2172b6b9a9 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -60,10 +60,6 @@ I.appearance_flags = RESET_COLOR|RESET_TRANSFORM hud_list[hud] = I -/mob/proc/create_player_panel() - QDEL_NULL(mob_panel) - mob_panel = new(src) - /mob/proc/Cell() set category = "Admin" set hidden = 1 diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 320ee86456..c04b9f29a8 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -20,9 +20,6 @@ /// Fullscreen objects var/list/fullscreens = list() - // Admin player panel for this mob - var/datum/player_panel/mob_panel - /// What receives our keyboard input. src by default. var/datum/focus diff --git a/config/entries/jexp.txt b/config/entries/jexp.txt index e854b53b98..e46d743a66 100644 --- a/config/entries/jexp.txt +++ b/config/entries/jexp.txt @@ -9,7 +9,7 @@ #USE_ACCOUNT_AGE_FOR_JOBS ## Unhash this to track player playtime in the database. Requires database to be enabled. -USE_EXP_TRACKING +#USE_EXP_TRACKING ## Unhash this to enable playtime requirements for head jobs. #USE_EXP_RESTRICTIONS_HEADS ## Unhash this to override head jobs' playtime requirements with this number of hours. diff --git a/modular_splurt/code/modules/mob/mob.dm b/modular_splurt/code/modules/mob/mob.dm index 9b5926076b..4f87cd79d2 100644 --- a/modular_splurt/code/modules/mob/mob.dm +++ b/modular_splurt/code/modules/mob/mob.dm @@ -20,3 +20,7 @@ if(S.type == spelltype) return TRUE return FALSE + +/mob/proc/create_player_panel() + QDEL_NULL(mob_panel) + mob_panel = new(src) diff --git a/modular_splurt/code/modules/mob/mob_defines.dm b/modular_splurt/code/modules/mob/mob_defines.dm index ca952d2e09..b8f14819a2 100644 --- a/modular_splurt/code/modules/mob/mob_defines.dm +++ b/modular_splurt/code/modules/mob/mob_defines.dm @@ -1,2 +1,5 @@ /mob var/is_tilted + + // Admin player panel for this mob + var/datum/player_panel/mob_panel diff --git a/tgui/packages/tgui/interfaces/PlayerPanel2.js b/tgui/packages/tgui/interfaces/PlayerPanel2.js index 614295f749..d6a0bbbd25 100644 --- a/tgui/packages/tgui/interfaces/PlayerPanel2.js +++ b/tgui/packages/tgui/interfaces/PlayerPanel2.js @@ -535,7 +535,7 @@ const GeneralActions = (props, context) => { act("ghost")} From 73ab88ddf2a602edf268078f1a60f4c91281fc41 Mon Sep 17 00:00:00 2001 From: Crowbar764 <59719612+Crowbar764@users.noreply.github.com> Date: Sat, 26 Feb 2022 10:44:48 +0000 Subject: [PATCH 09/15] Trying to fix conflict --- tgstation.dme | 1 - 1 file changed, 1 deletion(-) diff --git a/tgstation.dme b/tgstation.dme index 5e9e133c60..0b975ffc49 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -4270,7 +4270,6 @@ #include "modular_splurt\code\game\object\structures\dresser.dm" #include "modular_splurt\code\modules\mob\living\living.dm" #include "modular_splurt\code\modules\mob\living\living_defines.dm" -#include "modular_splurt\code\modules\mob\living\silicon\robot\robot.dm" #include "modular_splurt\code\modules\clothing\gloves.dm" #include "modular_splurt\code\modules\clothing\suits\heavy.dm" #include "modular_splurt\code\game\object\structures\false_walls.dm" From e1ec4b66fdca2b28fde6e76738826dd4f39b51e2 Mon Sep 17 00:00:00 2001 From: James Date: Sun, 27 Feb 2022 16:02:26 +0000 Subject: [PATCH 10/15] Some modularisation --- code/game/machinery/cryopod.dm | 20 --------- code/modules/admin/verbs/randomverbs.dm | 41 +------------------ code/modules/mob/mob.dm | 2 - modular_splurt/code/game/machinery/cryopod.dm | 20 +++++++++ .../code/modules/admin/verbs/randomverbs.dm | 40 ++++++++++++++++++ modular_splurt/code/modules/mob/mob.dm | 8 ++++ tgstation.dme | 1 + 7 files changed, 70 insertions(+), 62 deletions(-) create mode 100644 modular_splurt/code/game/machinery/cryopod.dm diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index ecd9f1a96a..b11d17c49c 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -8,7 +8,6 @@ * ~ Zuhayr */ GLOBAL_LIST_EMPTY(cryopod_computers) -GLOBAL_VAR_INIT(cryo_next_admin_warning, 0) // The last time we told admins about a mapping / coding error because no cryo computer was found //Main cryopod console. /obj/machinery/computer/cryopod @@ -505,22 +504,3 @@ GLOBAL_VAR_INIT(cryo_next_admin_warning, 0) // The last time we told admins abou if (pod) pod.open_machine() pod.name = initial_name - -/proc/cryo_find_control_computer(obj/machinery/cryopod/pod = null, urgent = FALSE) - - for(var/cryo_console as anything in GLOB.cryopod_computers) - var/obj/machinery/computer/cryopod/console = cryo_console - - if(!pod) // No cryopod passed in, we will try to find the standard crew cryo computer instead. - var/turf/console_turf = get_turf(console) - if((console_turf.z == 2) && (!istype(get_area(console), /area/security/prison))) // If station Z-level and NOT the prison cryo computer. Should get the standard crew cryo computer. - return WEAKREF(console) - else - if(get_area(console) == get_area(pod)) - return WEAKREF(console) - - // Don't send messages unless we *need* the computer, and less than five minutes have passed since last time we messaged - if(urgent && (world.time > GLOB.cryo_next_admin_warning)) - log_admin("\The [pod] in [get_area(pod)] could not find control computer!") - message_admins("\The [pod] in [get_area(pod)] could not find control computer!") - GLOB.cryo_next_admin_warning = world.time + 5 MINUTES diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 4413560072..e5e5fca448 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -19,52 +19,13 @@ admin_ticket_log(M, msg) SSblackbox.record_feedback("tally", "admin_verb", 1, "Drop Everything") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +// Unless I am dent-skulled, there is no way to do (mob/M in GLOB.mob_list, someothervar) with context-menu buttons, hence the instant proc call. /client/proc/cmd_admin_subtle_message(mob/M in GLOB.mob_list) set category = "Admin.Events" set name = "Subtle Message" cmd_admin_subtle_headset_message(M) -/client/proc/cmd_admin_subtle_headset_message(mob/M, sender = null) - if(!ismob(M)) - return - if(!check_rights(R_ADMIN)) - return - - if (!sender) - var/list/subtle_message_options = list("Voice in head", RADIO_CHANNEL_CENTCOM, RADIO_CHANNEL_SYNDICATE) - sender = tgui_input_list(usr, "Choose the method of subtle messaging", "Subtle Message", subtle_message_options) - if (!sender) - return - - message_admins("[key_name_admin(src)] has started answering [ADMIN_LOOKUPFLW(M)]'s prayer / faction PM.") - var/msg = input("Contents of the message", text("Subtle PM to [M.key]")) as text - if (!msg) - message_admins("[key_name_admin(src)] decided not to answer [ADMIN_LOOKUPFLW(M)]'s prayer / faction PM.") - return - - if (sender == "Voice in head") - to_chat(M, "You hear a voice in your head... [msg]") - else - var/mob/living/carbon/human/H = M - - if(!istype(H)) - to_chat(usr, "The person you are trying to contact is not human. Unsent message: [msg]") - return - - if(!istype(H.ears, /obj/item/radio/headset)) - to_chat(usr, "The person you are trying to contact is not wearing a headset. Unsent message: [msg]") - return - - to_chat(H, "You hear something crackle in your ears for a moment before a voice speaks. \"Please stand by for a message from [sender == RADIO_CHANNEL_SYNDICATE ? "your benefactor" : "Central Command"]. Message as follows[sender == RADIO_CHANNEL_SYNDICATE ? ", agent." : ":"] [msg]. Message ends.\"") - - - log_admin("SubtlePM ([sender]): [key_name(usr)] -> [key_name(M)] : [msg]") - msg = " SubtleMessage ([sender]): [key_name_admin(usr)] -> [key_name_admin(M)] : [msg]" - message_admins(msg) - admin_ticket_log(M, msg) - SSblackbox.record_feedback("tally", "admin_verb", 1, "Subtle Message") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - /client/proc/cmd_admin_mod_antag_rep(client/C in GLOB.clients, var/operation) set category = "Special Verbs" set name = "Modify Antagonist Reputation" diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 2172b6b9a9..9418998f16 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -19,7 +19,6 @@ initialize_actionspeed() init_rendering() hook_vr("mob_new",list(src)) - create_player_panel() /mob/Destroy()//This makes sure that mobs with clients/keys are not just deleted from the game. remove_from_mob_list() @@ -37,7 +36,6 @@ observe.reset_perspective(null) dispose_rendering() qdel(hud_used) - QDEL_NULL(mob_panel) for(var/cc in client_colours) qdel(cc) client_colours = null diff --git a/modular_splurt/code/game/machinery/cryopod.dm b/modular_splurt/code/game/machinery/cryopod.dm new file mode 100644 index 0000000000..0e9bd8497f --- /dev/null +++ b/modular_splurt/code/game/machinery/cryopod.dm @@ -0,0 +1,20 @@ +GLOBAL_VAR_INIT(cryo_next_admin_warning, 0) // The last time we told admins about a mapping / coding error because no cryo computer was found + +/proc/cryo_find_control_computer(obj/machinery/cryopod/pod = null, urgent = FALSE) + + for(var/cryo_console as anything in GLOB.cryopod_computers) + var/obj/machinery/computer/cryopod/console = cryo_console + + if(!pod) // No cryopod passed in, we will try to find the standard crew cryo computer instead. + var/turf/console_turf = get_turf(console) + if((console_turf.z == 2) && (!istype(get_area(console), /area/security/prison))) // If station Z-level and NOT the prison cryo computer. Should get the standard crew cryo computer. + return WEAKREF(console) + else + if(get_area(console) == get_area(pod)) + return WEAKREF(console) + + // Don't send messages unless we *need* the computer, and less than five minutes have passed since last time we messaged + if(urgent && (world.time > GLOB.cryo_next_admin_warning)) + log_admin("\The [pod] in [get_area(pod)] could not find control computer!") + message_admins("\The [pod] in [get_area(pod)] could not find control computer!") + GLOB.cryo_next_admin_warning = world.time + 5 MINUTES diff --git a/modular_splurt/code/modules/admin/verbs/randomverbs.dm b/modular_splurt/code/modules/admin/verbs/randomverbs.dm index ba7c712761..00c97dddcf 100644 --- a/modular_splurt/code/modules/admin/verbs/randomverbs.dm +++ b/modular_splurt/code/modules/admin/verbs/randomverbs.dm @@ -6,3 +6,43 @@ var/obj/item/reagent_containers/food/snacks/store/book/funnyBook = new(get_turf(target)) target.forceMove(funnyBook) funnyBook.name = "Book of " + target.name + +/client/proc/cmd_admin_subtle_headset_message(mob/M, sender = null) + if(!ismob(M)) + return + if(!check_rights(R_ADMIN)) + return + + if (!sender) + var/list/subtle_message_options = list("Voice in head", RADIO_CHANNEL_CENTCOM, RADIO_CHANNEL_SYNDICATE) + sender = tgui_input_list(usr, "Choose the method of subtle messaging", "Subtle Message", subtle_message_options) + if (!sender) + return + + message_admins("[key_name_admin(src)] has started answering [ADMIN_LOOKUPFLW(M)]'s prayer / faction PM.") + var/msg = input("Contents of the message", text("Subtle PM to [M.key]")) as text + if (!msg) + message_admins("[key_name_admin(src)] decided not to answer [ADMIN_LOOKUPFLW(M)]'s prayer / faction PM.") + return + + if (sender == "Voice in head") + to_chat(M, "You hear a voice in your head... [msg]") + else + var/mob/living/carbon/human/H = M + + if(!istype(H)) + to_chat(usr, "The person you are trying to contact is not human. Unsent message: [msg]") + return + + if(!istype(H.ears, /obj/item/radio/headset)) + to_chat(usr, "The person you are trying to contact is not wearing a headset. Unsent message: [msg]") + return + + to_chat(H, "You hear something crackle in your ears for a moment before a voice speaks. \"Please stand by for a message from [sender == RADIO_CHANNEL_SYNDICATE ? "your benefactor" : "Central Command"]. Message as follows[sender == RADIO_CHANNEL_SYNDICATE ? ", agent." : ":"] [msg]. Message ends.\"") + + + log_admin("SubtlePM ([sender]): [key_name(usr)] -> [key_name(M)] : [msg]") + msg = " SubtleMessage ([sender]): [key_name_admin(usr)] -> [key_name_admin(M)] : [msg]" + message_admins(msg) + admin_ticket_log(M, msg) + SSblackbox.record_feedback("tally", "admin_verb", 1, "Subtle Message") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/modular_splurt/code/modules/mob/mob.dm b/modular_splurt/code/modules/mob/mob.dm index 4f87cd79d2..5413b496fb 100644 --- a/modular_splurt/code/modules/mob/mob.dm +++ b/modular_splurt/code/modules/mob/mob.dm @@ -1,3 +1,11 @@ +/mob/Initialize() + . = ..() + create_player_panel() + +/mob/Destroy() + QDEL_NULL(mob_panel) + . = ..() + /mob/verb/tilt_left() set hidden = TRUE if(!canface() || is_tilted < -45) diff --git a/tgstation.dme b/tgstation.dme index 0b975ffc49..678b38b29d 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -4228,6 +4228,7 @@ #include "modular_splurt\code\datums\traits\good.dm" #include "modular_splurt\code\datums\traits\negative.dm" #include "modular_splurt\code\datums\traits\neutral.dm" +#include "modular_splurt\code\game\machinery\cryopod.dm" #include "modular_splurt\code\modules\admin\NewBan.dm" #include "modular_splurt\code\modules\admin\Kick.dm" #include "modular_splurt\code\modules\admin\JobBan.dm" From 41bb2e875c2e59543ffd9b5bcd2b16318cf3d565 Mon Sep 17 00:00:00 2001 From: James Date: Sun, 27 Feb 2022 17:48:19 +0000 Subject: [PATCH 11/15] Slaver stuff --- .../code/game/machinery/computer/slavery.dm | 2 ++ .../game/object/items/devices/radio/electropack.dm | 5 ++++- .../code/game/object/items/devices/radio/headset.dm | 5 +++++ .../structures/crates_lockers/closets/slaver.dm | 4 ++-- .../code/modules/antagonists/_common/antag_spawner.dm | 8 ++++++++ .../antagonists/slaver/equipment/orderable_gear.dm | 11 +++++++++-- .../antagonists/slaver/equipment/slaver_outfits.dm | 6 +----- tgstation.dme | 1 + tgui/packages/tgui/interfaces/SlaveConsole.js | 9 +++++++-- 9 files changed, 39 insertions(+), 12 deletions(-) create mode 100644 modular_splurt/code/game/object/items/devices/radio/headset.dm diff --git a/modular_splurt/code/game/machinery/computer/slavery.dm b/modular_splurt/code/game/machinery/computer/slavery.dm index 8276f62302..8e05044231 100644 --- a/modular_splurt/code/game/machinery/computer/slavery.dm +++ b/modular_splurt/code/game/machinery/computer/slavery.dm @@ -1,4 +1,5 @@ #define ANNOUNCEMENT_COOLDOWN 6 MINUTES +#define SLAVER_RANSOM_SCALE_VALUE 1000000 /obj/machinery/computer/slavery name = "\improper slave management console" @@ -49,6 +50,7 @@ var/turf/curr = get_turf(src) data["currentCoords"] = "[curr.x], [curr.y], [curr.z]" data["value_table"] = GLOB.slavers_ransom_values + data["ransom_scale_value"] = SLAVER_RANSOM_SCALE_VALUE data["categories"] = list() for(var/category in possible_gear) var/list/cat = list( diff --git a/modular_splurt/code/game/object/items/devices/radio/electropack.dm b/modular_splurt/code/game/object/items/devices/radio/electropack.dm index 85821e5bd3..826c6d018b 100644 --- a/modular_splurt/code/game/object/items/devices/radio/electropack.dm +++ b/modular_splurt/code/game/object/items/devices/radio/electropack.dm @@ -43,8 +43,11 @@ var/automatic_ransom_value = GLOB.slavers_ransom_values[M.job] if (automatic_ransom_value) station_rank = M.job - setPrice(automatic_ransom_value) + var/datum/bank_account/bank = SSeconomy.get_dep_account(ACCOUNT_CAR) + automatic_ransom_value += automatic_ransom_value * (bank.account_balance / SLAVER_RANSOM_SCALE_VALUE) // Slave price scales with station credit balance (+100% per 1,000,000 credits in the cargo budget) + + setPrice(automatic_ransom_value) /obj/item/electropack/shockcollar/slave/proc/setPrice(newPrice) var/mob/living/M = loc diff --git a/modular_splurt/code/game/object/items/devices/radio/headset.dm b/modular_splurt/code/game/object/items/devices/radio/headset.dm new file mode 100644 index 0000000000..f3dbd8c603 --- /dev/null +++ b/modular_splurt/code/game/object/items/devices/radio/headset.dm @@ -0,0 +1,5 @@ +/obj/item/radio/headset/syndicate/slaver/Initialize() + . = ..() + + set_frequency(FREQ_SYNDICATE) + freqlock = TRUE diff --git a/modular_splurt/code/game/object/structures/crates_lockers/closets/slaver.dm b/modular_splurt/code/game/object/structures/crates_lockers/closets/slaver.dm index b47aa92475..a1414adfef 100644 --- a/modular_splurt/code/game/object/structures/crates_lockers/closets/slaver.dm +++ b/modular_splurt/code/game/object/structures/crates_lockers/closets/slaver.dm @@ -10,7 +10,7 @@ new /obj/item/clothing/under/syndicate(src) new /obj/item/clothing/shoes/workboots(src) new /obj/item/storage/belt/military(src) - new /obj/item/radio/headset/syndicate(src) + new /obj/item/radio/headset/syndicate/slaver(src) new /obj/item/crowbar/red(src) new /obj/item/restraints/handcuffs(src) new /obj/item/restraints/handcuffs(src) @@ -31,7 +31,7 @@ new /obj/item/clothing/under/syndicate(src) new /obj/item/clothing/shoes/workboots(src) new /obj/item/storage/belt/military(src) - new /obj/item/radio/headset/syndicate(src) + new /obj/item/radio/headset/syndicate/slaver(src) new /obj/item/crowbar/red(src) new /obj/item/restraints/handcuffs(src) new /obj/item/restraints/handcuffs(src) diff --git a/modular_splurt/code/modules/antagonists/_common/antag_spawner.dm b/modular_splurt/code/modules/antagonists/_common/antag_spawner.dm index f5c2ba304f..ebb077ff11 100644 --- a/modular_splurt/code/modules/antagonists/_common/antag_spawner.dm +++ b/modular_splurt/code/modules/antagonists/_common/antag_spawner.dm @@ -59,6 +59,8 @@ else R = new /mob/living/silicon/robot/modules/slaver(T) + R.radio = new /obj/item/radio/borg/syndicate(R) + var/brainopsname = C.prefs.real_name R.mmi.name = "Man-Machine Interface: [brainopsname]" @@ -73,3 +75,9 @@ new_borg.send_to_spawnpoint = FALSE R.mind.add_antag_datum(new_borg,creator_slaver.slaver_team) R.mind.special_role = "Slaver Cyborg" + R.grant_language(/datum/language/codespeak, TRUE, TRUE) + + var/newName = sanitize_name(stripped_input(R, "Enter new robot name", "Cyborg Reclassification", "Cyborg", MAX_NAME_LEN)) + if (newName) + R.custom_name = newName + R.updatename() diff --git a/modular_splurt/code/modules/antagonists/slaver/equipment/orderable_gear.dm b/modular_splurt/code/modules/antagonists/slaver/equipment/orderable_gear.dm index c460c46503..6e9033a0b4 100644 --- a/modular_splurt/code/modules/antagonists/slaver/equipment/orderable_gear.dm +++ b/modular_splurt/code/modules/antagonists/slaver/equipment/orderable_gear.dm @@ -90,6 +90,13 @@ GLOBAL_LIST_INIT(slaver_gear, subtypesof(/datum/slaver_gear)) category = "Advanced" cost = 10000 +/datum/slaver_gear/jetpack + name = "Jetpack" + description = "Back-mounted jetpack for use in zero-gravity environments." + build_path = /obj/item/tank/jetpack/oxygen/harness + category = "Advanced" + cost = 3000 + /datum/slaver_gear/riot name = "Riot Kit" description = "Toy L6 SAW with riot darts." @@ -153,7 +160,7 @@ GLOBAL_LIST_INIT(slaver_gear, subtypesof(/datum/slaver_gear)) description = ".50 cal sniper rifle with sleep-inducing rounds." build_path = /obj/item/storage/backpack/duffelbag/syndie/slaver_marksman category = "Firearms" - cost = 40000 + cost = 50000 /datum/slaver_gear/freedom name = "Freedom Implant" @@ -171,7 +178,7 @@ GLOBAL_LIST_INIT(slaver_gear, subtypesof(/datum/slaver_gear)) /datum/slaver_gear/implant_teleport name = "Emergency Teleport Implants" - description = "Emergency teleport implant x 2." + description = "Emergency teleport implant x 2. Once activated, user needs to stand still for 5 seconds, after which they will be teleported from any location back to the slaver hideout. Has only one use." build_path = /obj/item/storage/box/slaver_teleport category = "Implants" cost = 15000 diff --git a/modular_splurt/code/modules/antagonists/slaver/equipment/slaver_outfits.dm b/modular_splurt/code/modules/antagonists/slaver/equipment/slaver_outfits.dm index c123e2ca5e..c97a6f18c8 100644 --- a/modular_splurt/code/modules/antagonists/slaver/equipment/slaver_outfits.dm +++ b/modular_splurt/code/modules/antagonists/slaver/equipment/slaver_outfits.dm @@ -5,7 +5,7 @@ shoes = /obj/item/clothing/shoes/workboots gloves = /obj/item/clothing/gloves/color/black back = /obj/item/storage/backpack - ears = /obj/item/radio/headset/syndicate + ears = /obj/item/radio/headset/syndicate/slaver id = /obj/item/card/id/syndicate/slaver backpack_contents = list(/obj/item/storage/box/survival,\ /obj/item/kitchen/knife/combat/survival) @@ -17,9 +17,5 @@ gloves = /obj/item/clothing/gloves/krav_maga/combatglovesplus /datum/outfit/slaver/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE, client/preference_source) - var/obj/item/radio/R = H.ears - R.set_frequency(FREQ_SYNDICATE) - R.freqlock = TRUE - H.faction |= ROLE_SLAVER H.update_icons() diff --git a/tgstation.dme b/tgstation.dme index a6cb7c0d62..e2ba4298e8 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -4251,6 +4251,7 @@ #include "modular_splurt\code\game\object\items\circuitboards\computer_circuitboards.dm" #include "modular_splurt\code\game\object\items\circuitboards\machine_circuitboards.dm" #include "modular_splurt\code\game\object\items\devices\radio\electropack.dm" +#include "modular_splurt\code\game\object\items\devices\radio\headset.dm" #include "modular_splurt\code\game\object\items\devices\radio\radio.dm" #include "modular_splurt\code\game\object\items\implants\implant_slaver.dm" #include "modular_splurt\code\game\object\items\lewd_items\lewd.dm" diff --git a/tgui/packages/tgui/interfaces/SlaveConsole.js b/tgui/packages/tgui/interfaces/SlaveConsole.js index 2cb5545ec5..d133b4363b 100644 --- a/tgui/packages/tgui/interfaces/SlaveConsole.js +++ b/tgui/packages/tgui/interfaces/SlaveConsole.js @@ -108,7 +108,7 @@ export const SlaveConsole = (props, context) => { const SlavePanel = (props, context) => { const { slaves } = props; - const { act, data } = useBackend(context); + const { act } = useBackend(context); if (!slaves.length) { return ( @@ -239,7 +239,9 @@ const SupplyPanel = (props, context) => { }; const RansomPanel = (props, context) => { + const { data } = useBackend(context); const value_table = props.value_table; + const { cargo_credits, ransom_scale_value } = data; let value_table_converted = []; @@ -271,7 +273,10 @@ const RansomPanel = (props, context) => { {rank_value.rank} - {formatMoney(rank_value.value, null, true)}cr + {formatMoney( + rank_value.value + (rank_value.value + * (cargo_credits / ransom_scale_value)) + , null, true)}cr ); From 78f58b887ab07328e5847c89e2be679bc7504f05 Mon Sep 17 00:00:00 2001 From: BongaTheProto <93835010+BongaTheProto@users.noreply.github.com> Date: Sun, 27 Feb 2022 16:55:54 -0500 Subject: [PATCH 12/15] Update tgstation.dme it did this on its own. I wish I knew how to manually make it order the file :( --- tgstation.dme | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tgstation.dme b/tgstation.dme index 50a5945ed0..918b6effd6 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -4229,11 +4229,6 @@ #include "modular_splurt\code\datums\traits\good.dm" #include "modular_splurt\code\datums\traits\negative.dm" #include "modular_splurt\code\datums\traits\neutral.dm" -#include "modular_splurt\code\game\machinery\cryopod.dm" -#include "modular_splurt\code\modules\admin\NewBan.dm" -#include "modular_splurt\code\modules\admin\Kick.dm" -#include "modular_splurt\code\modules\admin\JobBan.dm" -#include "modular_splurt\code\modules\admin\Transform.dm" #include "modular_splurt\code\game\atoms.dm" #include "modular_splurt\code\game\data_huds.dm" #include "modular_splurt\code\game\area\areas\centcom.dm" @@ -4242,6 +4237,7 @@ #include "modular_splurt\code\game\gamemodes\objectives.dm" #include "modular_splurt\code\game\machinery\brigdoors.dm" #include "modular_splurt\code\game\machinery\chem_alert.dm" +#include "modular_splurt\code\game\machinery\cryopod.dm" #include "modular_splurt\code\game\machinery\research_table.dm" #include "modular_splurt\code\game\machinery\computer\slavery.dm" #include "modular_splurt\code\game\machinery\porta_turret\portable_turret.dm" @@ -4270,8 +4266,6 @@ #include "modular_splurt\code\game\object\structures\barricades.dm" #include "modular_splurt\code\game\object\structures\bathroom.dm" #include "modular_splurt\code\game\object\structures\dresser.dm" -#include "modular_splurt\code\modules\mob\living\living.dm" -#include "modular_splurt\code\modules\mob\living\living_defines.dm" #include "modular_splurt\code\game\object\structures\false_walls.dm" #include "modular_splurt\code\game\object\structures\flora.dm" #include "modular_splurt\code\game\object\structures\ghost_role_spawners.dm" @@ -4291,7 +4285,11 @@ #include "modular_splurt\code\game\turfs\simulated\floor\plating\asteroid.dm" #include "modular_splurt\code\game\turfs\simulated\wall\mineral_walls.dm" #include "modular_splurt\code\modules\admin\chat_commands.dm" +#include "modular_splurt\code\modules\admin\JobBan.dm" +#include "modular_splurt\code\modules\admin\Kick.dm" +#include "modular_splurt\code\modules\admin\NewBan.dm" #include "modular_splurt\code\modules\admin\playtimes.dm" +#include "modular_splurt\code\modules\admin\Transform.dm" #include "modular_splurt\code\modules\admin\verbs\discord_subsystem.dm" #include "modular_splurt\code\modules\admin\verbs\discordbunker.dm" #include "modular_splurt\code\modules\admin\verbs\one_click_antag.dm" @@ -4390,6 +4388,8 @@ #include "modular_splurt\code\modules\mob\dead\new_player\sprite_accesories\snouts.dm" #include "modular_splurt\code\modules\mob\dead\new_player\sprite_accesories\synthliz.dm" #include "modular_splurt\code\modules\mob\dead\new_player\sprite_accesories\tails.dm" +#include "modular_splurt\code\modules\mob\living\living.dm" +#include "modular_splurt\code\modules\mob\living\living_defines.dm" #include "modular_splurt\code\modules\mob\living\carbon\human\human.dm" #include "modular_splurt\code\modules\mob\living\carbon\human\human_defines.dm" #include "modular_splurt\code\modules\mob\living\carbon\human\simple_animal\hostile\deathclaw\deathclaw.dm" From b3fc91a1f42d7cb5300a8d6936af4db7d24777a9 Mon Sep 17 00:00:00 2001 From: James Date: Mon, 28 Feb 2022 05:46:01 +0000 Subject: [PATCH 13/15] Adds logs button --- code/modules/admin/player_panel2.dm | 7 +++ tgui/packages/tgui/interfaces/PlayerPanel2.js | 43 +++++++++++++------ 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/code/modules/admin/player_panel2.dm b/code/modules/admin/player_panel2.dm index 5ad5f06946..72139127d7 100644 --- a/code/modules/admin/player_panel2.dm +++ b/code/modules/admin/player_panel2.dm @@ -291,6 +291,13 @@ GLOBAL_LIST_INIT(pp_limbs, list( if (targetMob.client) browse_messages(target_ckey = targetMob.ckey) + if ("logs") + var/source = LOGSRC_MOB + if (targetMob.client) + source = LOGSRC_CLIENT + + show_individual_logging_panel(targetMob, source) + if ("job_ban") if(targetMob.client) process_banlist(params["selected_role"], params["is_category"], params["want_to_ban"]) diff --git a/tgui/packages/tgui/interfaces/PlayerPanel2.js b/tgui/packages/tgui/interfaces/PlayerPanel2.js index d6a0bbbd25..b3178d959a 100644 --- a/tgui/packages/tgui/interfaces/PlayerPanel2.js +++ b/tgui/packages/tgui/interfaces/PlayerPanel2.js @@ -30,9 +30,6 @@ const PAGES = [ component: () => PunishmentActions, color: "red", icon: "gavel", - canAccess: data => { - return data.client_ckey; - }, }, { title: 'Feature Bans', @@ -564,21 +561,34 @@ const GeneralActions = (props, context) => { const PunishmentActions = (props, context) => { const { act, data } = useBackend(context); - const { mob_type, is_frozen, is_slept, glob_mute_bits, + const { client_ckey, mob_type, is_frozen, is_slept, glob_mute_bits, client_muted, data_related_cid, data_related_ip, data_byond_version, data_player_join_date, data_account_join_date, active_role_ban_count, current_time } = data; return (
-
Other Roles