From 0990a8ddcf7c2567fd3506bced806427e95d2fe0 Mon Sep 17 00:00:00 2001 From: Mothblocks <35135081+Mothblocks@users.noreply.github.com> Date: Sun, 29 Aug 2021 17:11:38 -0700 Subject: [PATCH] Give admins the ability to put a popup notice for players in tickets (#61010) Admins can now give players a popup if they are not responding to tickets. Popup is cleared when player replies or ticket is resolved/closed. As more and more of the chat screen is made irrelevant, new players read it less and less. This means that a lot of new players are ignoring ahelps, which is something I've encountered myself. --- code/__DEFINES/dcs/signals.dm | 8 ++ code/__DEFINES/layers.dm | 2 + .../configuration/entries/general.dm | 3 +- code/datums/components/admin_popup.dm | 125 ++++++++++++++++++ code/modules/admin/topic.dm | 16 +++ code/modules/admin/verbs/adminhelp.dm | 5 + code/modules/admin/verbs/adminpm.dm | 17 +-- config/config.txt | 6 +- tgstation.dme | 1 + 9 files changed, 164 insertions(+), 19 deletions(-) create mode 100644 code/datums/components/admin_popup.dm diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 21f863ec406..9522134a3a6 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -877,6 +877,14 @@ ///stops the bible chain from continuing. When all of the effects of the bible smacking have been moved to a signal we can kill this #define COMSIG_END_BIBLE_CHAIN (1<<0) +/// Admin helps +/// From /datum/admin_help/RemoveActive(). +/// Fired when an adminhelp is made inactive either due to closing or resolving. +#define COMSIG_ADMIN_HELP_MADE_INACTIVE "admin_help_made_inactive" + +/// Called when the player replies. From /client/proc/cmd_admin_pm(). +#define COMSIG_ADMIN_HELP_REPLIED "admin_help_replied" + ///Closets ///From base of [/obj/structure/closet/proc/insert]: (atom/movable/inserted) #define COMSIG_CLOSET_INSERT "closet_insert" diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm index 77b3ac86ddc..88f9536bc9a 100644 --- a/code/__DEFINES/layers.dm +++ b/code/__DEFINES/layers.dm @@ -183,6 +183,8 @@ ///1000 is an unimportant number, it's just to normalize copied layers #define RADIAL_CONTENT_LAYER 1000 +#define ADMIN_POPUP_LAYER 1 + ///Plane of the "splash" icon used that shows on the lobby screen. Nothing should ever be above this. #define SPLASHSCREEN_PLANE 9999 #define SPLASHSCREEN_RENDER_TARGET "SPLASHSCREEN_PLANE" diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm index 583660220d5..80bade82bc2 100644 --- a/code/controllers/configuration/entries/general.dm +++ b/code/controllers/configuration/entries/general.dm @@ -125,7 +125,8 @@ /datum/config_entry/flag/no_dead_vote // dead people can't vote -/datum/config_entry/flag/popup_admin_pm // adminPMs to non-admins show in a pop-up 'reply' window when set +/// Gives the ability to send players a maptext popup. +/datum/config_entry/flag/popup_admin_pm /datum/config_entry/number/fps default = 20 diff --git a/code/datums/components/admin_popup.dm b/code/datums/components/admin_popup.dm new file mode 100644 index 00000000000..65b97e09b1a --- /dev/null +++ b/code/datums/components/admin_popup.dm @@ -0,0 +1,125 @@ +/// Applied to clients when they receive an admin popup, alerting them to +/// their ticket. +/datum/component/admin_popup + /// The user's most active ticket. If this is resolved, closed, or replied to, + /// then the component will delete itself. + var/datum/admin_help/ticket + + var/atom/movable/screen/admin_popup/admin_popup + +/datum/component/admin_popup/Initialize(datum/admin_help/ticket) + if (!istype(parent, /client)) + return COMPONENT_INCOMPATIBLE + + if (!istype(ticket)) + return COMPONENT_INCOMPATIBLE + + create_notice() + + RegisterSignal( + ticket, + list( + COMSIG_ADMIN_HELP_MADE_INACTIVE, + COMSIG_ADMIN_HELP_REPLIED, + COMSIG_PARENT_QDELETING, + ), + .proc/delete_self, + ) + +/datum/component/admin_popup/Destroy(force, silent) + var/client/parent_client = parent + + parent_client?.screen -= admin_popup + QDEL_NULL(admin_popup) + + if (!QDELETED(ticket)) + UnregisterSignal(ticket, list( + COMSIG_ADMIN_HELP_MADE_INACTIVE, + COMSIG_ADMIN_HELP_REPLIED, + COMSIG_PARENT_QDELETING, + )) + + ticket = null + + return ..() + +/datum/component/admin_popup/proc/create_notice() + if(admin_popup) + qdel(admin_popup) + admin_popup = new + + var/client/parent_client = parent + admin_popup.maptext_width = getviewsize(parent_client.view_size.getView())[1] * world.icon_size + parent_client.screen += admin_popup + +/datum/component/admin_popup/proc/delete_self() + SIGNAL_HANDLER + qdel(src) + +/// The UI element for admin popups +/atom/movable/screen/admin_popup + icon = null + icon_state = null + plane = ABOVE_HUD_PLANE + layer = ADMIN_POPUP_LAYER + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + screen_loc = "TOP-5,LEFT" + maptext_height = 480 + maptext_width = 480 + maptext = "" + + var/static/list/colors = list( + COLOR_RED, + COLOR_ORANGE, + COLOR_YELLOW, + COLOR_LIME, + COLOR_CYAN, + COLOR_PURPLE, + ) + + /// The last color chosen in the animation, sourced from the static list colors. + var/last_color_index = 0 + + /// The `world.time` when the last color update occurred. + var/last_update_time = 0 + +/atom/movable/screen/admin_popup/New(loc, ...) + . = ..() + + START_PROCESSING(SSobj, src) + update_text() + +/atom/movable/screen/admin_popup/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/atom/movable/screen/admin_popup/process(delta_time) + update_text() + +/atom/movable/screen/admin_popup/proc/update_text() + // Even if processing time changes, we want this to remain slow. + // We want to pester them into reading their ticket, not give them a seizure! + if (world.time - last_update_time < 2 SECONDS) + return + + last_color_index = (last_color_index % colors.len) + 1 + + var/message = "" + message += "HEY! An admin is trying to talk to you!
Check your chat window, and click their name to respond!" + message += "
" + + maptext = MAPTEXT(message) + last_update_time = world.time + +/// Tries to give the target an admin popup. +/// If it fails, will send the error to the passed admin. +/proc/give_admin_popup(client/target, client/admin, message) + log_admin("[key_name(admin)] sent an admin popup to [key_name(target)].") + + var/datum/admin_help/current_ticket = target.current_ticket + if (!current_ticket) + to_chat(admin, span_warning("[key_name(target)] had no active ahelp, aborting.")) + return + + admin.cmd_admin_pm(target, message) + target.AddComponent(/datum/component/admin_popup, current_ticket) diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index a73208f839c..f78b4ac53c5 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1055,6 +1055,22 @@ to_chat(H, span_adminnotice("Your prayers have been answered!! You received the best [new_item.name]!"), confidential = TRUE) SEND_SOUND(H, sound('sound/effects/pray_chaplain.ogg')) + else if (href_list["adminpopup"]) + if (!check_rights(R_ADMIN)) + return + + var/message = input(owner, "As well as a popup, they'll also be sent a message to reply to. What do you want that to be?", "Message") as text|null + if (!message) + to_chat(owner, span_notice("Popup cancelled.")) + return + + var/client/target = locate(href_list["adminpopup"]) + if (!istype(target)) + to_chat(owner, span_notice("The mob doesn't exist anymore!")) + return + + give_admin_popup(target, owner, message) + else if(href_list["adminsmite"]) if(!check_rights(R_ADMIN|R_FUN)) return diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm index 9c5217f33d1..803924b99c7 100644 --- a/code/modules/admin/verbs/adminhelp.dm +++ b/code/modules/admin/verbs/adminhelp.dm @@ -254,6 +254,9 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new) if(state == AHELP_ACTIVE) . += ClosureLinks(ref_src) + if (CONFIG_GET(flag/popup_admin_pm)) + . += " (POPUP)" + //private /datum/admin_help/proc/ClosureLinks(ref_src) if(!ref_src) @@ -345,6 +348,8 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new) if(initiator && initiator.current_ticket == src) initiator.current_ticket = null + SEND_SIGNAL(src, COMSIG_ADMIN_HELP_MADE_INACTIVE) + //Mark open ticket as closed/meme /datum/admin_help/proc/Close(key_name = key_name_admin(usr), silent = FALSE) if(state != AHELP_ACTIVE) diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm index aaf5c54ea14..67cee2e72eb 100644 --- a/code/modules/admin/verbs/adminpm.dm +++ b/code/modules/admin/verbs/adminpm.dm @@ -212,6 +212,8 @@ if(holder && recipient.holder && !current_ticket) //Both are admins, and this is not a reply to our own ticket. badmin = TRUE if(recipient.holder && !badmin) + SEND_SIGNAL(current_ticket, COMSIG_ADMIN_HELP_REPLIED) + if(holder) to_chat(recipient, type = MESSAGE_TYPE_ADMINPM, @@ -243,7 +245,6 @@ //play the receiving admin the adminhelp sound (if they have them enabled) if(recipient.prefs.toggles & SOUND_ADMINHELP) SEND_SOUND(recipient, sound('sound/effects/adminhelp.ogg')) - else if(holder) //sender is an admin but recipient is not. Do BIG RED TEXT var/already_logged = FALSE @@ -277,10 +278,6 @@ //always play non-admin recipients the adminhelp sound SEND_SOUND(recipient, sound('sound/effects/adminhelp.ogg')) - //AdminPM popup for ApocStation and anybody else who wants to use it. Set it with POPUP_ADMIN_PM in config.txt ~Carn - if(CONFIG_GET(flag/popup_admin_pm)) - INVOKE_ASYNC(src, .proc/popup_admin_pm, recipient, msg) - else //neither are admins if(!current_ticket) to_chat(src, @@ -312,16 +309,6 @@ html = span_notice("PM: [key_name(src, X, 0)]->[key_name(recipient, X, 0)]: [keywordparsedmsg]") , confidential = TRUE) -/client/proc/popup_admin_pm(client/recipient, msg) - var/sender = src - var/sendername = key - var/reply = input(recipient, msg,"Admin PM from-[sendername]", "") as message|null //show message and await a reply - if(recipient && reply) - if(sender) - recipient.cmd_admin_pm(sender,reply) //sender is still about, let's reply to them - else - adminhelp(reply) //sender has left, adminhelp instead - #define TGS_AHELP_USAGE "Usage: ticket " /proc/TgsPm(target,msg,sender) target = ckey(target) diff --git a/config/config.txt b/config/config.txt index b109a26bd71..3578a57fb71 100644 --- a/config/config.txt +++ b/config/config.txt @@ -286,9 +286,9 @@ CHECK_RANDOMIZER ## Uncomment this to forbid admins from possessing the singularity. #FORBID_SINGULO_POSSESSION -## Uncomment to show a popup 'reply to' window to every non-admin that receives an adminPM. -## The intention is to make adminPMs more visible. (although I fnd popups annoying so this defaults to off) -#POPUP_ADMIN_PM +## Uncomment to give admins the ability to send a maptext popup to players. +## Only fires when an admin requests it, not every ahelp. +POPUP_ADMIN_PM ## Uncomment to allow special 'Easter-egg' events on special holidays such as seasonal holidays and stuff like 'Talk Like a Pirate Day' :3 YAARRR ALLOW_HOLIDAYS diff --git a/tgstation.dme b/tgstation.dme index 47a215efa80..6eda4fa316c 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -508,6 +508,7 @@ #include "code\datums\changelog\changelog.dm" #include "code\datums\components\_component.dm" #include "code\datums\components\acid.dm" +#include "code\datums\components\admin_popup.dm" #include "code\datums\components\anti_magic.dm" #include "code\datums\components\aquarium.dm" #include "code\datums\components\area_sound_manager.dm"