From 6385d90efc2f8d4d38501f3cd6fd56be3e3c471a Mon Sep 17 00:00:00 2001 From: spookerton Date: Sun, 2 Oct 2022 15:17:50 +0100 Subject: [PATCH] added sstyping --- code/__defines/appearance.dm | 9 + code/controllers/subsystems/typing.dm | 234 ++++++++++++++++++ .../preference_setup/global/setting_datums.dm | 3 +- code/modules/mob/living/carbon/brain/brain.dm | 20 -- code/modules/mob/mob_defines.dm | 5 +- code/modules/mob/say.dm | 13 +- code/modules/mob/typing_indicator.dm | 59 ----- code/modules/multiz/zshadow.dm | 12 - polaris.dme | 2 +- 9 files changed, 251 insertions(+), 106 deletions(-) create mode 100644 code/controllers/subsystems/typing.dm delete mode 100644 code/modules/mob/typing_indicator.dm diff --git a/code/__defines/appearance.dm b/code/__defines/appearance.dm index faa97e8f06..87a05419a8 100644 --- a/code/__defines/appearance.dm +++ b/code/__defines/appearance.dm @@ -1,3 +1,12 @@ // Consider these images/atoms as part of the UI/HUD #define APPEARANCE_UI_IGNORE_ALPHA RESET_COLOR|RESET_TRANSFORM|NO_CLIENT_COLOR|RESET_ALPHA #define APPEARANCE_UI RESET_COLOR|RESET_TRANSFORM|NO_CLIENT_COLOR + +/// The atom is not clickable +#define XMOUSE_OPACITY_NEVER 0 + +/// The atom is clickable normally +#define XMOUSE_OPACITY_DEFAULT 1 + +/// The atom steals clicks from other clickables +#define XMOUSE_OPACITY_ALWAYS 2 diff --git a/code/controllers/subsystems/typing.dm b/code/controllers/subsystems/typing.dm new file mode 100644 index 0000000000..081a220963 --- /dev/null +++ b/code/controllers/subsystems/typing.dm @@ -0,0 +1,234 @@ +SUBSYSTEM_DEF(typing) + name = "Typing" + flags = SS_BACKGROUND | SS_NO_INIT + runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME + wait = 0.5 SECONDS + + /// The skin control to poll for TYPING_STATE_INPUT status. + var/const/INPUT_HANDLE = "mainwindow.input" + + var/const/INFLIGHT_TIMEOUT = 5 SECONDS + + /// The status entry index of the related client's typing indicator visibility preference. + var/const/INDEX_PREFERENCE = 1 + + /// The status entry index of the inflight state. + var/const/INDEX_INFLIGHT = 2 + + /// The status entry index of the timeout threshold. + var/const/INDEX_TIMEOUT = 3 + + /// The status entry index of the input bar typing state. + var/const/INDEX_INPUT_STATE = 4 + + /// The status entry index of the verb input typing state. + var/const/INDEX_VERB_STATE = 5 + + /// The highest index in a status entry. + var/const/MAX_INDEX = 5 + + /* + * A list of (ckey = list( + preference = 0|1, + inflight = 0|1, + timeout = num, + istyping_input = 0|1, + istyping_hotkey = 0|1 + ), ...) + See .proc/GetEntry for details. + */ + var/static/list/status = list() + + /// Matches input bar verbs that should set TYPING_STATE_INPUT. + var/static/regex/match_verbs = regex("^(Me|Say) +\"?\\w+") + + /// A list of clients waiting to be polled for input state. + var/static/list/client/queue = list() + + +/datum/controller/subsystem/typing/Recover() + status = list() + queue = list() + + +/datum/controller/subsystem/typing/fire(resumed, no_mc_tick) + if (!resumed) + queue = list() + for (var/client/client as anything in GLOB.clients) + queue += client + if (!length(queue)) + return + var/cut_until = 1 + var/list/entry + for (var/client/client as anything in queue) + ++cut_until + if (QDELETED(client)) + continue + entry = GetEntry(client) + if (!entry[INDEX_PREFERENCE]) + continue + if (!entry[INDEX_INFLIGHT]) + UpdateInputState(client, entry) + else if (world.time < entry[INDEX_TIMEOUT]) + entry[INDEX_INFLIGHT] = FALSE + if (no_mc_tick) + CHECK_TICK + else if (MC_TICK_CHECK) + queue.Cut(1, cut_until) + return + queue.Cut() + + +/// Return, generating if necessary, a ckey-indexed list holding typing status. +/datum/controller/subsystem/typing/proc/GetEntry(client/client) + PRIVATE_PROC(TRUE) + var/ckey + if (istext(client)) + ckey = client + else if (istype(client)) + ckey = client.ckey + else + return + var/list/entry = status[ckey] + if (!entry) + entry = new (MAX_INDEX) + entry[INDEX_PREFERENCE] = client.is_preference_enabled(/datum/client_preference/show_typing_indicator) + entry[INDEX_INFLIGHT] = FALSE + entry[INDEX_TIMEOUT] = world.time + entry[INDEX_INPUT_STATE] = FALSE + entry[INDEX_VERB_STATE] = FALSE + status[ckey] = entry + return entry + + +/// Updates client's preference bool for whether typing indicators should be shown. +/datum/controller/subsystem/typing/proc/UpdatePreference(client/client, preference) + var/list/entry = GetEntry(client) + entry[INDEX_PREFERENCE] = preference + UpdateIndicator(client, entry) + + +/// Updates client|ckey's verb typing state to new_state. +/datum/controller/subsystem/typing/proc/UpdateVerbState(client/client, state) + var/list/entry = GetEntry(client) + entry[INDEX_VERB_STATE] = state + UpdateIndicator(client, entry) + + +/// Request client's input bar state using winget and updating entry accordingly. +/datum/controller/subsystem/typing/proc/UpdateInputState(client/client, list/entry) + PRIVATE_PROC(TRUE) + set waitfor = FALSE + var/timeout = world.time + INFLIGHT_TIMEOUT + entry[INDEX_INFLIGHT] = TRUE + entry[INDEX_TIMEOUT] = timeout + var/content = winget(client, INPUT_HANDLE, "text") + if (timeout != entry[INDEX_TIMEOUT]) // We're stale. Touch nothing. + return + entry[INDEX_INFLIGHT] = FALSE + if (QDELETED(client) || !isliving(client.mob)) + return + entry[INDEX_INPUT_STATE] = match_verbs.Find(content) != 0 + UpdateIndicator(client, entry) + + +/// Attempt to update the mob's typing state and indicator according to new state. +/datum/controller/subsystem/typing/proc/UpdateIndicator(client/client, list/entry) + PRIVATE_PROC(TRUE) + var/mob/target = client.mob + var/display = target.stat == CONSCIOUS && entry[INDEX_PREFERENCE] && (entry[INDEX_INPUT_STATE] || entry[INDEX_VERB_STATE]) + if (display == target.is_typing) + return + if (display) + if (!target.typing_indicator) + target.typing_indicator = new (null, target) + target.typing_indicator.icon_state = "[target.speech_bubble_appearance()]_typing" + target.typing_indicator.pixel_y = target.icon_expected_height - 32 + target.vis_contents += target.typing_indicator + target.is_typing = TRUE + if (target.shadow) + target.vis_contents += target.typing_indicator + else + if (target.typing_indicator) + target.vis_contents -= target.typing_indicator + target.is_typing = FALSE + if (target.shadow) + target.vis_contents -= target.typing_indicator + + +/atom/movable/typing_indicator + icon = 'icons/mob/talk.dmi' + icon_state = "typing" + plane = PLANE_LIGHTING_ABOVE + mouse_opacity = XMOUSE_OPACITY_NEVER + simulated = FALSE + anchored = TRUE + + var/mob/owner + + +/atom/movable/typing_indicator/Destroy() + if (owner) + owner.vis_contents -= src + owner.typing_indicator = null + owner = null + return ..() + + +/atom/movable/typing_indicator/Initialize(mapload, mob/_owner) + . = ..() + if (!istype(_owner)) + return INITIALIZE_HINT_QDEL + owner = _owner + + +/// If this mob is or was piloted by a player with typing indicators enabled, an instance of one. +/mob/var/atom/movable/typing_indicator/typing_indicator + + +/// Whether this mob is currently typing, if piloted by a player. +/mob/var/is_typing + + +/mob/Destroy() + QDEL_NULL(typing_indicator) + return ..() + + +/mob/Logout() + if (typing_indicator) + vis_contents -= typing_indicator + is_typing = FALSE + ..() + + +/mob/verb/say_wrapper() + set name = ".Say" + set hidden = TRUE + SStyping.UpdateVerbState(client, TRUE) + var/message = input("","say (text)") as null | text + SStyping.UpdateVerbState(client, FALSE) + if (message) + say_verb(message) + + +/mob/verb/me_wrapper() + set name = ".Me" + set hidden = TRUE + SStyping.UpdateVerbState(client, TRUE) + var/message = input("","me (text)") as null | text + SStyping.UpdateVerbState(client, FALSE) + if (message) + me_verb(message) + + +// Retained for uses by non-player assets +/proc/generate_speech_bubble(atom/movable/loc, icon_state, layer = FLOAT_LAYER) + var/image/image = image('icons/mob/talk.dmi', loc, icon_state, layer) + image.appearance_flags |= KEEP_APART | RESET_COLOR | PIXEL_SCALE + if (ismovable(loc)) + var/x_scale = loc.get_icon_scale_x() + if (abs(x_scale) < 2) // reset transform on bubbles, except for the Very Large + image.pixel_z = (loc.icon_expected_height * (x_scale - 1)) + image.appearance_flags |= RESET_TRANSFORM + return image diff --git a/code/modules/client/preference_setup/global/setting_datums.dm b/code/modules/client/preference_setup/global/setting_datums.dm index 1e6f4ed11c..51893ccb83 100644 --- a/code/modules/client/preference_setup/global/setting_datums.dm +++ b/code/modules/client/preference_setup/global/setting_datums.dm @@ -178,8 +178,7 @@ var/global/list/_client_preferences_by_type disabled_description = "Hide" /datum/client_preference/show_typing_indicator/toggled(var/mob/preference_mob, var/enabled) - if(!enabled) - preference_mob.set_typing_indicator(FALSE) + SStyping.UpdatePreference(preference_mob.client, enabled) /datum/client_preference/show_ooc description ="OOC chat" diff --git a/code/modules/mob/living/carbon/brain/brain.dm b/code/modules/mob/living/carbon/brain/brain.dm index 78acfe4703..1482ab006c 100644 --- a/code/modules/mob/living/carbon/brain/brain.dm +++ b/code/modules/mob/living/carbon/brain/brain.dm @@ -43,23 +43,3 @@ /mob/living/carbon/brain/isSynthetic() return istype(loc, /obj/item/mmi) - -/mob/living/carbon/brain/set_typing_indicator(var/state) - if(isturf(loc)) - return ..() - - if(!is_preference_enabled(/datum/client_preference/show_typing_indicator)) - loc.cut_overlay(typing_indicator, TRUE) - return - - if(!typing_indicator) - init_typing_indicator("[speech_bubble_appearance()]_typing") - - if(state && !typing) - loc.add_overlay(typing_indicator, TRUE) - typing = TRUE - else if(typing) - loc.cut_overlay(typing_indicator, TRUE) - typing = FALSE - - return state diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index cf6aa54bc4..72f2c818c4 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -212,9 +212,6 @@ var/get_rig_stats = 0 //Moved from computer.dm - var/typing - var/obj/effect/decal/typing_indicator - var/low_priority = FALSE //Skip processing life() if there's just no players on this Z-level var/default_pixel_x = 0 //For offsetting mobs @@ -225,4 +222,4 @@ var/registered_z - var/in_enclosed_vehicle = 0 //For mechs and fighters ambiance. Can be used in other cases. \ No newline at end of file + var/in_enclosed_vehicle = 0 //For mechs and fighters ambiance. Can be used in other cases. diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index 20bf193bb4..fa7330cfc1 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -1,35 +1,32 @@ /mob/proc/say(var/message, var/datum/language/speaking = null, var/whispering = 0) return + /mob/verb/whisper(message as text) set name = "Whisper" set category = "IC" + usr.say(message, whispering = TRUE) - usr.say(message,whispering=1) /mob/verb/say_verb(message as text) set name = "Say" set category = "IC" - - set_typing_indicator(FALSE) usr.say(message) + /mob/verb/me_verb(message as message) set name = "Me" set category = "IC" - if(say_disabled) //This is here to try to identify lag problems - to_chat(usr, "Speech is currently admin-disabled.") + to_chat(usr, SPAN_WARNING("Speech is currently admin-disabled.")) return - message = sanitize(message) - - set_typing_indicator(FALSE) if(use_me) custom_emote(usr.emote_type, message) else usr.emote(message) + /mob/proc/say_dead(var/message) if(say_disabled) //This is here to try to identify lag problems to_chat(usr, "Speech is currently admin-disabled.") diff --git a/code/modules/mob/typing_indicator.dm b/code/modules/mob/typing_indicator.dm deleted file mode 100644 index 36a99a265b..0000000000 --- a/code/modules/mob/typing_indicator.dm +++ /dev/null @@ -1,59 +0,0 @@ -/proc/generate_speech_bubble(var/bubble_loc, var/speech_state, var/set_layer = FLOAT_LAYER) - var/image/I = image('icons/mob/talk.dmi', bubble_loc, speech_state, set_layer) - I.appearance_flags |= (KEEP_APART|RESET_COLOR|PIXEL_SCALE) - if(istype(bubble_loc, /atom/movable)) - var/atom/movable/AM = bubble_loc - var/x_scale = AM.get_icon_scale_x() - if(abs(x_scale) < 2) // reset transform on bubbles, except for the Very Large - I.pixel_z = (AM.icon_expected_height * (x_scale-1)) - I.appearance_flags |= RESET_TRANSFORM - return I - -/mob/proc/init_typing_indicator(var/set_state = "typing") - typing_indicator = new - typing_indicator.appearance = generate_speech_bubble(null, set_state) - typing_indicator.appearance_flags |= (KEEP_APART|RESET_COLOR|RESET_TRANSFORM|PIXEL_SCALE) - -/mob/proc/set_typing_indicator(var/state) //Leaving this here for mobs. - - if(!is_preference_enabled(/datum/client_preference/show_typing_indicator)) - if(typing_indicator) - cut_overlay(typing_indicator, TRUE) - return - - if(!typing_indicator) - init_typing_indicator("[speech_bubble_appearance()]_typing") - - if(state && !typing) - add_overlay(typing_indicator, TRUE) - typing = TRUE - else if(typing) - cut_overlay(typing_indicator, TRUE) - typing = FALSE - - if(shadow) //Multi-Z above-me shadows - shadow.set_typing_indicator(state) - - return state - -/mob/verb/say_wrapper() - set name = ".Say" - set hidden = 1 - - set_typing_indicator(TRUE) - var/message = input("","say (text)") as text - set_typing_indicator(FALSE) - - if(message) - say_verb(message) - -/mob/verb/me_wrapper() - set name = ".Me" - set hidden = 1 - - set_typing_indicator(TRUE) - var/message = input("","me (text)") as text - set_typing_indicator(FALSE) - - if(message) - me_verb(message) diff --git a/code/modules/multiz/zshadow.dm b/code/modules/multiz/zshadow.dm index ad86c06425..3bd9cae99a 100644 --- a/code/modules/multiz/zshadow.dm +++ b/code/modules/multiz/zshadow.dm @@ -113,15 +113,3 @@ INITIALIZE_IMMEDIATE(/mob/zshadow) . = ..() if(shadow) shadow.set_dir(new_dir) - -/mob/zshadow/set_typing_indicator(var/state) - if(!typing_indicator) - init_typing_indicator("typing") - if(state && !typing) - add_overlay(typing_indicator) - typing = 1 - else if(!state && typing) - cut_overlay(typing_indicator) - typing = 0 - if(shadow) - shadow.set_typing_indicator(state) diff --git a/polaris.dme b/polaris.dme index 15aad86337..001a5bcb26 100644 --- a/polaris.dme +++ b/polaris.dme @@ -248,6 +248,7 @@ #include "code\controllers\subsystems\ticker.dm" #include "code\controllers\subsystems\time_track.dm" #include "code\controllers\subsystems\timer.dm" +#include "code\controllers\subsystems\typing.dm" #include "code\controllers\subsystems\vote.dm" #include "code\controllers\subsystems\webhooks.dm" #include "code\controllers\subsystems\xenoarch.dm" @@ -2205,7 +2206,6 @@ #include "code\modules\mob\say.dm" #include "code\modules\mob\skillset.dm" #include "code\modules\mob\transform_procs.dm" -#include "code\modules\mob\typing_indicator.dm" #include "code\modules\mob\update_icons.dm" #include "code\modules\mob\_modifiers\aura.dm" #include "code\modules\mob\_modifiers\changeling.dm"