From a84910f8694fda5495561f86cc406437788a9330 Mon Sep 17 00:00:00 2001 From: Mark Suckerberg Date: Fri, 22 Nov 2024 13:14:33 -0600 Subject: [PATCH 1/7] Subtle emote refactor --- code/__DEFINES/keybinding.dm | 1 + code/datums/emotes.dm | 7 +- code/datums/keybinding/communication.dm | 6 ++ code/modules/client/client_procs.dm | 2 + code/modules/client/verbs/looc.dm | 2 +- code/modules/mob/living/emote.dm | 6 ++ code/modules/mob/mob_say.dm | 12 +++ code/modules/mob/say_vr.dm | 105 ------------------------ shiptest.dme | 1 - 9 files changed, 32 insertions(+), 110 deletions(-) delete mode 100644 code/modules/mob/say_vr.dm diff --git a/code/__DEFINES/keybinding.dm b/code/__DEFINES/keybinding.dm index 50a16edc350a..2cf1558a3736 100644 --- a/code/__DEFINES/keybinding.dm +++ b/code/__DEFINES/keybinding.dm @@ -38,6 +38,7 @@ #define COMSIG_KB_CLIENT_LOOC_DOWN "keybinding_client_looc_down" #define COMSIG_KB_CLIENT_SAY_DOWN "keybinding_client_say_down" #define COMSIG_KB_CLIENT_ME_DOWN "keybinding_client_me_down" +#define COMSIG_KB_CLIENT_SUBTLE_DOWN "keybinding_client_subtle_down" #define COMSIG_KB_CLIENT_WHISPER_DOWN "keybinding_client_whisper_down" //Human diff --git a/code/datums/emotes.dm b/code/datums/emotes.dm index f8e05e9a38b6..4e4ec2af4ed3 100644 --- a/code/datums/emotes.dm +++ b/code/datums/emotes.dm @@ -26,7 +26,8 @@ var/vary = FALSE //used for the honk borg emote var/only_forced_audio = FALSE //can only code call this event instead of the player. var/cooldown = 0.8 SECONDS - var/static/regex/stop_bad_mime = regex(@"says|exclaims|yells|asks") + ///Range the emote can be seen or heard from + var/range = DEFAULT_MESSAGE_RANGE /datum/emote/New() if (ispath(mob_type_allowed_typecache)) @@ -76,9 +77,9 @@ M.show_message("[FOLLOW_LINK(M, user)] [dchatmsg]") if(emote_type == EMOTE_AUDIBLE) - user.audible_message(msg, deaf_message = "You see how [user] [msg]", audible_message_flags = EMOTE_MESSAGE) + user.audible_message(msg, deaf_message = "You see how [user] [msg]", hearing_distance = range, audible_message_flags = EMOTE_MESSAGE) else - user.visible_message(msg, blind_message = "You hear how [user] [msg]", visible_message_flags = EMOTE_MESSAGE) + user.visible_message(msg, blind_message = "You hear how [user] [msg]", vision_distance = range, visible_message_flags = EMOTE_MESSAGE) /// For handling emote cooldown, return true to allow the emote to happen /datum/emote/proc/check_cooldown(mob/user, intentional) diff --git a/code/datums/keybinding/communication.dm b/code/datums/keybinding/communication.dm index 3577a3440376..312629403855 100644 --- a/code/datums/keybinding/communication.dm +++ b/code/datums/keybinding/communication.dm @@ -25,6 +25,12 @@ full_name = "Custom Emote (/Me)" keybind_signal = COMSIG_KB_CLIENT_ME_DOWN +/datum/keybinding/client/communication/subtle + hotkey_keys = null + name = "Subtle" + full_name = "Subtle Custom Emote" + keybind_signal = COMSIG_KB_CLIENT_SUBTLE_DOWN + /datum/keybinding/client/communication/whisper hotkey_keys = list("Y") name = "Whisper" diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 87a33b0c989b..4a06f5e5fb3d 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -1032,6 +1032,8 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( winset(src, "default-[REF(key)]", "parent=default;name=[key];command=looc") if("Me") winset(src, "default-[REF(key)]", "parent=default;name=[key];command=me") + if("Subtle") + winset(src, "default-[REF(key)]", "parent=default;name=[key];command=subtle") if("Whisper") winset(src, "default-[REF(key)]", "parent=default;name=[key];command=whisper") diff --git a/code/modules/client/verbs/looc.dm b/code/modules/client/verbs/looc.dm index 1c66a077a065..701eef4390b6 100644 --- a/code/modules/client/verbs/looc.dm +++ b/code/modules/client/verbs/looc.dm @@ -59,7 +59,7 @@ GLOBAL_VAR_INIT(normal_looc_colour, "#6699CC") mob.log_talk(raw_msg, LOG_LOOC, tag = "(LOOC)") - var/list/heard = get_hearers_in_view(7, get_top_level_mob(mob)) + var/list/heard = get_hearers_in_view(7, mob) for(var/mob/hearer_mob in heard) var/client/hearer = hearer_mob.client diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm index f4042464f981..8a2f0ff69153 100644 --- a/code/modules/mob/living/emote.dm +++ b/code/modules/mob/living/emote.dm @@ -616,6 +616,12 @@ /datum/emote/living/custom/replace_pronoun(mob/user, message) return message +/datum/emote/living/custom/subtle + key = "subtle" + key_third_person = "subtle custom" + message = null + range = 1 + /datum/emote/living/help key = "help" diff --git a/code/modules/mob/mob_say.dm b/code/modules/mob/mob_say.dm index 9e03116f1ba3..49a191f22651 100644 --- a/code/modules/mob/mob_say.dm +++ b/code/modules/mob/mob_say.dm @@ -45,6 +45,18 @@ QUEUE_OR_CALL_VERB_FOR(VERB_CALLBACK(src, TYPE_PROC_REF(/mob, emote), "me", 1, message, TRUE), SSspeech_controller) +/mob/verb/subtle_verb(message as text) + set name = "Subtle" + set category = "IC" + + if(typing_indicator) + set_typing_indicator(FALSE) + if(GLOB.say_disabled) //This is here to try to identify lag problems + to_chat(usr, "Speech is currently admin-disabled.") + return + + QUEUE_OR_CALL_VERB_FOR(VERB_CALLBACK(src, TYPE_PROC_REF(/mob, emote), "subtle", 1, message, TRUE), SSspeech_controller) + ///Speak as a dead person (ghost etc) /mob/proc/say_dead(message) var/name = real_name diff --git a/code/modules/mob/say_vr.dm b/code/modules/mob/say_vr.dm deleted file mode 100644 index 01e3a4d33b1a..000000000000 --- a/code/modules/mob/say_vr.dm +++ /dev/null @@ -1,105 +0,0 @@ -////////////////////////////////////////////////////// -////////////////////SUBTLE COMMAND//////////////////// (ported from citadel, needed for LOOC and flavour text) -////////////////////////////////////////////////////// -/mob - var/flavor_text = "" //tired of fucking double checking this - -/mob/proc/update_flavor_text() - set name = "Update Flavor Text" - set category = "IC" - set src in usr - - if(usr != src) - to_chat(usr, span_warning("You can't set someone else's flavour text!")) - var/msg = input(usr, "A snippet of text shown when others examine you, describing what you may look like. This can also be used for OOC notes.", "Flavor Text", html_decode("flavor_text")) as message|null - - if(msg) - msg = copytext(msg, 1, MAX_MESSAGE_LEN) - msg = html_encode(msg) - - flavor_text = msg - -/mob/proc/print_flavor_text() - if(flavor_text && flavor_text != "") - var/msg = replacetext(flavor_text, "\n", " ") - if(length(msg) <= MAX_SHORTFLAVOR_LEN) - return "[msg]" - else - return "[copytext(msg, 1, MAX_SHORTFLAVOR_LEN)]... More..." - -/mob/proc/get_top_level_mob() - if(istype(src.loc,/mob)&&src.loc!=src) - var/mob/M=src.loc - return M.get_top_level_mob() - return src - -/proc/get_top_level_mob(mob/S) - if(istype(S.loc,/mob)&&S.loc!=S) - var/mob/M=S.loc - return M.get_top_level_mob() - return S - - -/* -SUBTLER -*/ - -/datum/emote/living/subtler - key = "subtler" - key_third_person = "subtler" - message = null - mob_type_blacklist_typecache = list(/mob/living/brain) - - -/datum/emote/living/subtler/proc/check_invalid(mob/user, input) - if(stop_bad_mime.Find(input, 1, 1)) - to_chat(user, "Invalid emote.") - return TRUE - return FALSE - -/datum/emote/living/subtler/run_emote(mob/user, params, type_override = null) - if(is_banned_from(user, "emote")) - to_chat(user, "You cannot send subtle emotes (banned).") - return FALSE - else if(user.client && user.client.prefs.muted & MUTE_IC) - to_chat(user, "You cannot send IC messages (muted).") - return FALSE - else if(!params) - var/subtle_emote = sanitize(input(user, "Choose an emote to display.", "Subtler")) - if(subtle_emote && !check_invalid(user, subtle_emote)) - var/type = input("Is this a visible or hearable emote?") as null|anything in list("Visible", "Hearable") - switch(type) - if("Visible") - emote_type = EMOTE_VISIBLE - if("Hearable") - emote_type = EMOTE_AUDIBLE - else - alert("Unable to use this emote, must be either hearable or visible.") - return - message = subtle_emote - else - return FALSE - else - message = params - if(type_override) - emote_type = type_override - . = TRUE - if(!can_run_emote(user)) - return FALSE - - user.log_message(message, LOG_SUBTLER) - message = "[user] " + "[message]" - - if(emote_type == EMOTE_AUDIBLE) - user.audible_message(message = message, hearing_distance = 1) - else - user.visible_message(message = message, self_message = message, vision_distance = 1) - -///////////////// VERB CODE -/mob/living/verb/subtler() - set name = "Subtler" - set category = "IC" - if(GLOB.say_disabled) //This is here to try to identify lag problems - to_chat(usr, "Speech is currently admin-disabled.") - return - usr.emote("subtler") diff --git a/shiptest.dme b/shiptest.dme index 66f2020bebbe..8c6a64fd535a 100644 --- a/shiptest.dme +++ b/shiptest.dme @@ -2463,7 +2463,6 @@ #include "code\modules\mob\mob_movement.dm" #include "code\modules\mob\mob_say.dm" #include "code\modules\mob\mob_transformation_simple.dm" -#include "code\modules\mob\say_vr.dm" #include "code\modules\mob\status_procs.dm" #include "code\modules\mob\transform_procs.dm" #include "code\modules\mob\update_icons.dm" From f8fa39aecfb148679386005eeb0d979218d37ce9 Mon Sep 17 00:00:00 2001 From: Mark Suckerberg Date: Sat, 23 Nov 2024 12:08:09 -0600 Subject: [PATCH 2/7] Basic markdown flavourtext --- code/__HELPERS/text.dm | 2 +- code/datums/dna.dm | 4 +-- code/modules/client/preferences.dm | 4 +-- code/modules/mob/living/carbon/carbon.dm | 27 +++++++++++++++++++ .../mob/living/carbon/carbon_defines.dm | 7 ++++- .../mob/living/carbon/human/examine.dm | 7 ++--- code/modules/mob/mob.dm | 6 ----- 7 files changed, 42 insertions(+), 15 deletions(-) diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index 1c605758a2cd..9e1df5ef8f9d 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -572,7 +572,7 @@ GLOBAL_LIST_INIT(binary, list("0","1")) t = replacetext(t, regex("\[^\\S\\r\\n \]", "g"), " ") - t = parsemarkdown_basic_step1(t) + t = parsemarkdown_basic_step1(t, limited) t = replacetext(t, regex("%s(?:ign)?(?=\\s|$)", "igm"), user ? "[user.real_name]" : "") t = replacetext(t, regex("%f(?:ield)?(?=\\s|$)", "igm"), "") diff --git a/code/datums/dna.dm b/code/datums/dna.dm index ccf91af252a9..8a815145a211 100644 --- a/code/datums/dna.dm +++ b/code/datums/dna.dm @@ -52,7 +52,7 @@ destination.dna.features = features.Copy() destination.dna.real_name = real_name destination.dna.temporary_mutations = temporary_mutations.Copy() - destination.flavor_text = destination.dna.features["flavor_text"] //Update the flavor_text to use new dna text + destination.set_flavor_text(destination.dna.features["flavor_text"]) //Update the flavor_text to use new dna text if(transfer_SE) destination.dna.mutation_index = mutation_index destination.dna.default_mutation_genes = default_mutation_genes @@ -371,7 +371,7 @@ //Do not use force_transfer_mutations for stuff like cloners without some precautions, otherwise some conditional mutations could break (timers, drill hat etc) if(newfeatures) dna.features = newfeatures - flavor_text = dna.features["flavor_text"] //Update the flavor_text to use new dna text + set_flavor_text(dna.features["flavor_text"]) //Update the flavor_text to use new dna text if(mrace) var/datum/species/newrace = new mrace.type diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 18c9a5374443..4c39e8db3673 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -1706,7 +1706,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) if("flavor_text") var/msg = stripped_multiline_input(usr, "A snippet of text shown when others examine you, describing what you may look like. This can also be used for OOC notes.", "Flavor Text", html_decode(features["flavor_text"]), MAX_FLAVOR_LEN, TRUE) - if(msg) //WS edit - "Cancel" does not clear flavor text + if(msg) features["flavor_text"] = msg if("hair") @@ -2485,7 +2485,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) character.fbp = fbp - character.flavor_text = features["flavor_text"] //Let's update their flavor_text at least initially + character.set_flavor_text(features["flavor_text"]) //Let's update their flavor_text at least initially if(loadout) //I have been told not to do this because it's too taxing on performance, but hey, I did it anyways! //I hate you old me //don't be mean for(var/gear in equipped_gear) diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index c5f9698682c5..8c79e40aac85 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -232,6 +232,12 @@ paper_note.show_through_camera(usr) + if(href_list["flavor_more"]) + var/datum/browser/popup = new(usr, "[name]'s flavor text", "[name]'s Flavor Text (expanded)", 500, 200) + popup.set_content(flavor_text_html) + popup.open() + return + /mob/living/carbon/on_fall() . = ..() loc?.handle_fall(src)//it's loc so it doesn't call the mob's handle_fall which does nothing @@ -1206,3 +1212,24 @@ set_lying_angle(pick(90, 270)) else set_lying_angle(new_lying_angle) + +/mob/living/carbon/proc/set_flavor_text(new_text) + if(!new_text) + return + if(new_text == flavor_text) + return + + flavor_text_html = parsemarkdown_basic(new_text) + flavor_text = new_text + + if(dna) + dna.features["flavor_text"] = flavor_text + +/mob/living/carbon/verb/change_flavor_text() + set name = "Change Flavor Text" + set category = "IC" + set src in usr + + var/new_text = stripped_multiline_input(usr, "A snippet of text shown when others examine you, describing what you may look like. This can also be used for OOC notes.", "Flavor Text", html_decode(flavor_text), MAX_FLAVOR_LEN, TRUE) + + set_flavor_text(new_text) diff --git a/code/modules/mob/living/carbon/carbon_defines.dm b/code/modules/mob/living/carbon/carbon_defines.dm index 8743fe33289e..cb5d248826f0 100644 --- a/code/modules/mob/living/carbon/carbon_defines.dm +++ b/code/modules/mob/living/carbon/carbon_defines.dm @@ -84,7 +84,7 @@ /// Timer id of any transformation var/transformation_timer - /// WS edit - moth dust when hugging + /// moth dust when hugging var/mothdust ///List of quirk cooldowns to track @@ -94,3 +94,8 @@ /// Can other carbons be shoved into this one to make it fall? var/can_be_shoved_into = FALSE + + /// A snippet of text shown when the mob is examined. + var/flavor_text = "" + /// Plaintext version of the flavor text, formatted with markdown. + var/flavor_text_html = "" diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index c462eb294f77..71f9b832fa21 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -389,9 +389,10 @@ if(invisible_man) . += "...?" else - var/flavor = print_flavor_text() - if(flavor) - . += flavor + var/text_to_add = flavor_text + if(length(text_to_add) > MAX_SHORTFLAVOR_LEN) + text_to_add = "[copytext(text_to_add, 1, MAX_SHORTFLAVOR_LEN)]... More..." + . += span_notice(text_to_add) . += "*---------*" SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, user, .) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index d56560a5acb5..8afa484d103c 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -829,12 +829,6 @@ unset_machine() src << browse(null, t1) - if(href_list["flavor_more"]) - var/datum/browser/popup = new(usr, "[name]'s flavor text", "[name]'s Flavor Text (expanded)", 500, 200) - popup.set_content(text("[][]", "[name]'s flavor text (expanded)", replacetext(flavor_text, "\n", "
"))) - popup.open() - return - if(user != src) if(href_list["item"] && user.canUseTopic(src, BE_CLOSE, NO_DEXTERITY)) var/slot = text2num(href_list["item"]) From 135365ecd82ff5fe738e78ed8a84abc644481650 Mon Sep 17 00:00:00 2001 From: Mark Suckerberg Date: Mon, 25 Nov 2024 11:56:56 -0600 Subject: [PATCH 3/7] attempt 1 --- code/datums/components/flavor_text.dm | 48 +++++++++++++++++ shiptest.dme | 1 + tgui/packages/tgui/interfaces/FlavorText.tsx | 52 +++++++++++++++++++ .../tgui/interfaces/common/Markdown.tsx | 48 +++++++++++++++++ .../tgui/styles/interfaces/FlavorText.scss | 9 ++++ 5 files changed, 158 insertions(+) create mode 100644 code/datums/components/flavor_text.dm create mode 100644 tgui/packages/tgui/interfaces/FlavorText.tsx create mode 100644 tgui/packages/tgui/interfaces/common/Markdown.tsx create mode 100644 tgui/packages/tgui/styles/interfaces/FlavorText.scss diff --git a/code/datums/components/flavor_text.dm b/code/datums/components/flavor_text.dm new file mode 100644 index 000000000000..3322417525ae --- /dev/null +++ b/code/datums/components/flavor_text.dm @@ -0,0 +1,48 @@ +/datum/component/flavor_text + dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS + /// The flavor text to display when the parent is examined. + var/flavor_text + var/portrait_url + var/portrait_source + + var/static/link_list = list("https://media.discordapp.net", "https://cdn.discordapp.com", "https://i.gyazo.com", "https://i.imgur.com") + var/static/end_regex = regex("^.jpg|.jpg|.png|.jpeg|.jpeg$") //Regex is terrible, don't touch the duplicate extensions + +/datum/component/flavor_text/Initialize(_flavor_text, _portrait_url, _portrait_source) + if(!isatom(parent)) + return COMPONENT_INCOMPATIBLE + + flavor_text = _flavor_text + + if(!length(_portrait_url) || !length(_portrait_source)) + return + + if(copytext_char(_portrait_url, 9) != "https://") + return + + for(var/link in link_list) + if(findtext(_portrait_url, link)) + break + + portrait_url = _portrait_url + portrait_source = _portrait_source + + + +/datum/component/flavor_text/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "FlavorText", "[user.real_name]'s Guestbook") + ui.set_autoupdate(FALSE) + ui.open() + +/datum/component/flavor_text/ui_state(mob/user) + return GLOB.always_state + +/datum/component/flavor_text/ui_data(mob/user) + var/list/data = list() + data["characterName"] = flavor_text + data["portraitUrl"] = portrait_url + data["portraitSource"] = portrait_source + data["flavorText"] = flavor_text + return data diff --git a/shiptest.dme b/shiptest.dme index 8c6a64fd535a..eddcb58c20d7 100644 --- a/shiptest.dme +++ b/shiptest.dme @@ -506,6 +506,7 @@ #include "code\datums\components\empprotection.dm" #include "code\datums\components\explodable.dm" #include "code\datums\components\fishing_spot.dm" +#include "code\datums\components\flavor_text.dm" #include "code\datums\components\footstep.dm" #include "code\datums\components\forensics.dm" #include "code\datums\components\fullauto.dm" diff --git a/tgui/packages/tgui/interfaces/FlavorText.tsx b/tgui/packages/tgui/interfaces/FlavorText.tsx new file mode 100644 index 000000000000..062a6ccececd --- /dev/null +++ b/tgui/packages/tgui/interfaces/FlavorText.tsx @@ -0,0 +1,52 @@ +import { marked } from 'marked'; +import { resolveAsset } from '../assets'; +import { useBackend } from '../backend'; +import { Box, Section, Stack } from '../components'; +import { Window } from '../layouts'; +import { walkTokens } from './common/Markdown'; + +type FlavorTextContext = { + characterName: string; + portraitUrl: string; + portraitSource: string; + flavorText: string; +}; + +export const FlavorText = (props, context) => { + const { data } = useBackend(context); + + marked.use({ + breaks: true, + gfm: true, + smartypants: true, + walkTokens: walkTokens, + // Once assets are fixed might need to change this for them + baseUrl: 'thisshouldbreakhttp', + }); + + return ( + + + + {data.portraitUrl && ( +
+ + + + + {data.portraitSource} + +
+ )} +
+ +
+
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/common/Markdown.tsx b/tgui/packages/tgui/interfaces/common/Markdown.tsx new file mode 100644 index 000000000000..5f4d34529ef7 --- /dev/null +++ b/tgui/packages/tgui/interfaces/common/Markdown.tsx @@ -0,0 +1,48 @@ +// Override function, any links and images should +// kill any other marked tokens we don't want here +export const walkTokens = (token) => { + switch (token.type) { + case 'url': + case 'autolink': + case 'reflink': + case 'link': + case 'image': + token.type = 'text'; + // Once asset system is up change to some default image + // or rewrite for icon images + token.href = ''; + break; + } +}; + +// This is an extension for marked defining a complete custom tokenizer. +// This tokenizer should run before the the non-custom ones, and gives us +// the ability to handle [_____] fields before the em/strong tokenizers +// mangle them, since underscores are used for italic/bold. +// This massively improves the order of operations, allowing us to run +// marked, THEN sanitise the output (much safer) and finally insert fields +// manually afterwards. +export const inputField = { + name: 'inputField', + level: 'inline', + + start(src: string) { + return src.match(/\[/)?.index; + }, + + tokenizer(src: string) { + const rule = /^\[_+\]/; + const match = src.match(rule); + if (match) { + const token = { + type: 'inputField', + raw: match[0], + }; + return token; + } + }, + + renderer(token: { type: string; raw: string }) { + return `${token.raw}`; + }, +}; diff --git a/tgui/packages/tgui/styles/interfaces/FlavorText.scss b/tgui/packages/tgui/styles/interfaces/FlavorText.scss new file mode 100644 index 000000000000..d9c2994f34b2 --- /dev/null +++ b/tgui/packages/tgui/styles/interfaces/FlavorText.scss @@ -0,0 +1,9 @@ +.FlavorText__Portrait { + height: 200px; + width: 200px; +} + +.FlavorText__Portrait img { + height: 100%; + width: 100%; +} From c6013b423e20264ddf5ec84b31e9b19c1869cb0d Mon Sep 17 00:00:00 2001 From: Mark Suckerberg Date: Tue, 3 Dec 2024 20:05:31 -0600 Subject: [PATCH 4/7] it works --- code/datums/components/flavor_text.dm | 36 +++++++++++++++++------- code/modules/mob/living/carbon/carbon.dm | 2 ++ 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/code/datums/components/flavor_text.dm b/code/datums/components/flavor_text.dm index 3322417525ae..2e1c81b59fb5 100644 --- a/code/datums/components/flavor_text.dm +++ b/code/datums/components/flavor_text.dm @@ -5,11 +5,10 @@ var/portrait_url var/portrait_source - var/static/link_list = list("https://media.discordapp.net", "https://cdn.discordapp.com", "https://i.gyazo.com", "https://i.imgur.com") - var/static/end_regex = regex("^.jpg|.jpg|.png|.jpeg|.jpeg$") //Regex is terrible, don't touch the duplicate extensions + var/static/flavortext_regex = regex(@"https://i\.imgur\.com/[0-9A-z]{7}\.(?:png|jpe?g)") /datum/component/flavor_text/Initialize(_flavor_text, _portrait_url, _portrait_source) - if(!isatom(parent)) + if(!ismob(parent)) return COMPONENT_INCOMPATIBLE flavor_text = _flavor_text @@ -17,27 +16,44 @@ if(!length(_portrait_url) || !length(_portrait_source)) return - if(copytext_char(_portrait_url, 9) != "https://") + if(!findtext(_portrait_url, flavortext_regex)) return - for(var/link in link_list) - if(findtext(_portrait_url, link)) - break - portrait_url = _portrait_url portrait_source = _portrait_source +/datum/component/flavor_text/RegisterWithParent() + RegisterSignal(parent, COMSIG_PARENT_EXAMINE_MORE, PROC_REF(handle_examine_more)) + +/datum/component/flavor_text/UnregisterFromParent() + UnregisterSignal(parent, COMSIG_PARENT_EXAMINE_MORE) + +/datum/component/flavor_text/InheritComponent(datum/component/flavor_text/new_comp, original, _flavor_text, _portrait_url, _portrait_source) + if(new_comp) + flavor_text = new_comp.flavor_text + portrait_url = new_comp.portrait_url + portrait_source = new_comp.portrait_source + else + flavor_text = _flavor_text + portrait_url = _portrait_url + portrait_source = _portrait_source + +/datum/component/flavor_text/proc/handle_examine_more(mob/user) + SIGNAL_HANDLER + if(!flavor_text) + return + INVOKE_ASYNC(src, PROC_REF(ui_interact), user) /datum/component/flavor_text/ui_interact(mob/user, datum/tgui/ui) ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, "FlavorText", "[user.real_name]'s Guestbook") + ui = new(user, src, "FlavorText", "[user.real_name]") ui.set_autoupdate(FALSE) ui.open() /datum/component/flavor_text/ui_state(mob/user) - return GLOB.always_state + return GLOB.z_state /datum/component/flavor_text/ui_data(mob/user) var/list/data = list() diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 8c79e40aac85..19d4f9dee90f 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -1219,6 +1219,8 @@ if(new_text == flavor_text) return + AddComponent(/datum/component/flavor_text, new_text, "https://i.imgur.com/weRxaGd.png", "honkbird") + flavor_text_html = parsemarkdown_basic(new_text) flavor_text = new_text From 19fdc4df0825dba7cdda81ad51e99613a9af19b0 Mon Sep 17 00:00:00 2001 From: Mark Suckerberg Date: Tue, 3 Dec 2024 22:05:18 -0600 Subject: [PATCH 5/7] actually integrates portraits --- code/__HELPERS/mobs.dm | 2 ++ code/datums/dna.dm | 6 ++-- code/modules/client/preferences.dm | 34 +++++++++++++++------ code/modules/client/preferences_savefile.dm | 8 +++++ code/modules/mob/living/carbon/carbon.dm | 13 +++----- 5 files changed, 43 insertions(+), 20 deletions(-) diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index 31ce8dceee49..3968563f383c 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -101,6 +101,8 @@ "elzu_horns" = pick(GLOB.elzu_horns_list), "ethcolor" = GLOB.color_list_ethereal[pick(GLOB.color_list_ethereal)], "flavor_text" = "", + "flavor_portrait" = "", + "flavor_portrait_source" = "", "frills" = pick(GLOB.frills_list), "horns" = pick(GLOB.horns_list), "ipc_antenna" = pick(GLOB.ipc_antennas_list), diff --git a/code/datums/dna.dm b/code/datums/dna.dm index 8a815145a211..6cf18cf814ed 100644 --- a/code/datums/dna.dm +++ b/code/datums/dna.dm @@ -52,7 +52,8 @@ destination.dna.features = features.Copy() destination.dna.real_name = real_name destination.dna.temporary_mutations = temporary_mutations.Copy() - destination.set_flavor_text(destination.dna.features["flavor_text"]) //Update the flavor_text to use new dna text + //Update the flavor_text to use new dna text + destination.set_flavor_text(destination.dna.features["flavor_text"], destination.dna.features["portrait_url"], destination.dna.features["portrait_source"]) if(transfer_SE) destination.dna.mutation_index = mutation_index destination.dna.default_mutation_genes = default_mutation_genes @@ -371,7 +372,8 @@ //Do not use force_transfer_mutations for stuff like cloners without some precautions, otherwise some conditional mutations could break (timers, drill hat etc) if(newfeatures) dna.features = newfeatures - set_flavor_text(dna.features["flavor_text"]) //Update the flavor_text to use new dna text + //Update the flavor_text to use new dna text + set_flavor_text(dna.features["flavor_text"], dna.features["portrait_url"], dna.features["portrait_source"]) if(mrace) var/datum/species/newrace = new mrace.type diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 4c39e8db3673..f524cb789faa 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -123,6 +123,8 @@ GLOBAL_LIST_EMPTY(preferences_datums) "elzu_horns" = "None", "elzu_tail" = "None", "flavor_text" = "", + "flavor_portrait" = "", + "flavor_portrait_source" = "", "body_size" = "Normal" ) var/list/randomise = list( @@ -325,14 +327,17 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "Always Random Age: [(randomise[RANDOM_AGE]) ? "Yes" : "No"]" dat += "When Antagonist: [(randomise[RANDOM_AGE_ANTAG]) ? "Yes" : "No"]" - dat += "
Set Flavor Text" - if(length(features["flavor_text"]) <= 40) - if(!length(features["flavor_text"])) - dat += "\[...\]" - else - dat += "[features["flavor_text"]]" - else - dat += "[copytext_char(features["flavor_text"], 1, 37)]...
" + dat += "
Flavor Text: " + var/flavortext = "\[...\]" + if(length(features["flavor_text"]) > 40) + flavortext = copytext_char(features["flavor_text"], 1, 37) + "..." + else if(length(features["flavor_text"])) + flavortext = features["flavor_text"] + dat += "[flavortext]" + + dat += "
Portrait:" + var/portrait_text = length(features["flavor_portrait"]) ? "\[Image by [features["flavor_portrait_source"]]\]" : "\[Unset\]" + dat += "[portrait_text]" dat += "

Special Names:
" var/old_group @@ -1709,6 +1714,17 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(msg) features["flavor_text"] = msg + if("flavor_portrait") + var/url = input(user, "A URL to an image that will be shown when others examine you.", "Flavor Portrait", features["flavor_portrait"]) as text|null + if(isnull(url)) + return + features["flavor_portrait"] = url + + var/source = input(user, "The name of the artist or other source for the specified image.", "Flavor Portrait", features["flavor_portrait_source"]) as text|null + if(isnull(source)) + return + features["flavor_portrait_source"] = source + if("hair") var/new_hair = input(user, "Choose your character's hair colour:", "Character Preference","#"+hair_color) as color|null if(new_hair) @@ -2485,7 +2501,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) character.fbp = fbp - character.set_flavor_text(features["flavor_text"]) //Let's update their flavor_text at least initially + character.set_flavor_text(features["flavor_text"], features["flavor_portrait"], features["flavor_portrait_source"]) //Let's update their flavor_text at least initially if(loadout) //I have been told not to do this because it's too taxing on performance, but hey, I did it anyways! //I hate you old me //don't be mean for(var/gear in equipped_gear) diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index 71d968d16130..c8348e10c258 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -479,6 +479,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car //Flavor Text S["feature_flavor_text"] >> features["flavor_text"] + S["feature_flavor_portrait"] >> features["flavor_portrait"] + S["feature_flavor_source"] >> features["flavor_source"] //try to fix any outdated data if necessary //preference updating will handle saving the updated data for us. @@ -562,7 +564,10 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car features["vox_neck_quills"] = sanitize_inlist(features["vox_neck_quills"], GLOB.vox_neck_quills_list, "None") features["elzu_horns"] = sanitize_inlist(features["elzu_horns"], GLOB.elzu_horns_list) features["tail_elzu"] = sanitize_inlist(features["tail_elzu"], GLOB.tails_list_elzu) + features["flavor_text"] = sanitize_text(features["flavor_text"], initial(features["flavor_text"])) + features["flavor_portrait"] = sanitize_text(features["flavor_portrait"], initial(features["flavor_portrait"])) + features["flavor_source"] = sanitize_text(features["flavor_source"], initial(features["flavor_source"])) all_quirks = SANITIZE_LIST(all_quirks) @@ -647,6 +652,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car //Flavor text WRITE_FILE(S["feature_flavor_text"] , features["flavor_text"]) + WRITE_FILE(S["feature_flavor_portrait"] , features["flavor_portrait"]) + WRITE_FILE(S["feature_flavor_source"] , features["flavor_source"]) + //Custom names for(var/custom_name_id in GLOB.preferences_custom_names) var/savefile_slot_name = custom_name_id + "_name" //TODO remove this diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 19d4f9dee90f..61823f7ee632 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -1159,11 +1159,6 @@ update_inv_gloves() . = TRUE -/mob/living/carbon/proc/update_flavor_text_feature(new_text) - if(!dna) - return - dna.features["flavor_text"] = new_text - /// Returns whether or not the carbon should be able to be shocked /mob/living/carbon/proc/should_electrocute(power_source) if (ismecha(loc)) @@ -1213,19 +1208,19 @@ else set_lying_angle(new_lying_angle) -/mob/living/carbon/proc/set_flavor_text(new_text) +/mob/living/carbon/proc/set_flavor_text(new_text, new_portrait, new_source) if(!new_text) return - if(new_text == flavor_text) - return - AddComponent(/datum/component/flavor_text, new_text, "https://i.imgur.com/weRxaGd.png", "honkbird") + AddComponent(/datum/component/flavor_text, new_text, new_portrait, new_source) flavor_text_html = parsemarkdown_basic(new_text) flavor_text = new_text if(dna) dna.features["flavor_text"] = flavor_text + dna.features["flavor_portrait"] = new_portrait + dna.features["flavor_source"] = new_source /mob/living/carbon/verb/change_flavor_text() set name = "Change Flavor Text" From dc010c4fe38c924441b7a7243844724e11090651 Mon Sep 17 00:00:00 2001 From: Mark Suckerberg Date: Tue, 17 Dec 2024 22:25:56 -0600 Subject: [PATCH 6/7] Adjustments --- code/datums/components/flavor_text.dm | 3 ++- code/game/atoms.dm | 15 +-------------- code/modules/mob/living/carbon/human/examine.dm | 12 ++++++++++++ tgui/packages/tgui/interfaces/FlavorText.tsx | 5 ++--- 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/code/datums/components/flavor_text.dm b/code/datums/components/flavor_text.dm index 2e1c81b59fb5..6f0d869adc16 100644 --- a/code/datums/components/flavor_text.dm +++ b/code/datums/components/flavor_text.dm @@ -8,6 +8,7 @@ var/static/flavortext_regex = regex(@"https://i\.imgur\.com/[0-9A-z]{7}\.(?:png|jpe?g)") /datum/component/flavor_text/Initialize(_flavor_text, _portrait_url, _portrait_source) + //You could technically use this on any atom, but... no. if(!ismob(parent)) return COMPONENT_INCOMPATIBLE @@ -48,7 +49,7 @@ /datum/component/flavor_text/ui_interact(mob/user, datum/tgui/ui) ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, "FlavorText", "[user.real_name]") + ui = new(user, src, "FlavorText", "[user.name]") ui.set_autoupdate(FALSE) ui.open() diff --git a/code/game/atoms.dm b/code/game/atoms.dm index acc2797b360a..934df9b074b5 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -1676,20 +1676,7 @@ /// Returns the atom name that should be used on screentip /atom/proc/get_screentip_name(client/hovering_client) - if(ishuman(src)) - var/mob/living/carbon/human/guy = src - var/mob/client_mob = hovering_client.mob - var/datum/guestbook/guestbook = client_mob.mind?.guestbook - if(guestbook) - var/known_name = guestbook.get_known_name(client_mob, guy) - if(known_name) - return known_name - else - return guy.get_visible_name() - else - return guy.real_name - else - return name + return name ///Called whenever a player is spawned on the same turf as this atom. /atom/proc/join_player_here(mob/M) diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index 71f9b832fa21..507868510e10 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -432,3 +432,15 @@ return if(get_age()) . += span_notice("[p_they(TRUE)] appear[p_s()] to be [get_age()].") + +/mob/living/carbon/human/get_screentip_name(client/hovering_client) + var/mob/client_mob = hovering_client.mob + var/datum/guestbook/guestbook = client_mob.mind?.guestbook + if(guestbook) + var/known_name = guestbook.get_known_name(client_mob, src) + if(known_name) + return known_name + else + return get_visible_name() + else + return real_name diff --git a/tgui/packages/tgui/interfaces/FlavorText.tsx b/tgui/packages/tgui/interfaces/FlavorText.tsx index 062a6ccececd..b325055bb8f3 100644 --- a/tgui/packages/tgui/interfaces/FlavorText.tsx +++ b/tgui/packages/tgui/interfaces/FlavorText.tsx @@ -1,5 +1,4 @@ import { marked } from 'marked'; -import { resolveAsset } from '../assets'; import { useBackend } from '../backend'; import { Box, Section, Stack } from '../components'; import { Window } from '../layouts'; @@ -27,11 +26,11 @@ export const FlavorText = (props, context) => { return ( - + {data.portraitUrl && (
- + {data.portraitSource} From 1f510f3b6ffe25c4f2a7eb73cd122cdb19c81f17 Mon Sep 17 00:00:00 2001 From: Mark Suckerberg Date: Wed, 18 Dec 2024 11:59:15 -0600 Subject: [PATCH 7/7] Aesthetic tweaks --- code/datums/components/flavor_text.dm | 5 +++ tgui/packages/tgui/interfaces/FlavorText.tsx | 36 +++++++++++--------- tgui/packages/tgui/styles/main.scss | 1 + 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/code/datums/components/flavor_text.dm b/code/datums/components/flavor_text.dm index 6f0d869adc16..c5f890a9c734 100644 --- a/code/datums/components/flavor_text.dm +++ b/code/datums/components/flavor_text.dm @@ -63,3 +63,8 @@ data["portraitSource"] = portrait_source data["flavorText"] = flavor_text return data + +/datum/component/flavor_text/vv_edit_var(var_name, var_value) + if(var_name == NAMEOF(src, portrait_url) && !findtext(var_value, flavortext_regex)) + return FALSE + return ..() diff --git a/tgui/packages/tgui/interfaces/FlavorText.tsx b/tgui/packages/tgui/interfaces/FlavorText.tsx index b325055bb8f3..1777460ea1fa 100644 --- a/tgui/packages/tgui/interfaces/FlavorText.tsx +++ b/tgui/packages/tgui/interfaces/FlavorText.tsx @@ -24,26 +24,30 @@ export const FlavorText = (props, context) => { }); return ( - + {data.portraitUrl && ( -
- - - - - {data.portraitSource} - -
+ +
+ + + + + {data.portraitSource} + +
+
)} -
- -
+ +
+ +
+
diff --git a/tgui/packages/tgui/styles/main.scss b/tgui/packages/tgui/styles/main.scss index d0fea4281710..dfc0bfc85b21 100644 --- a/tgui/packages/tgui/styles/main.scss +++ b/tgui/packages/tgui/styles/main.scss @@ -56,6 +56,7 @@ @include meta.load-css('./interfaces/CrewManifest.scss'); @include meta.load-css('./interfaces/ExperimentConfigure.scss'); @include meta.load-css('./interfaces/Fishing.scss'); +@include meta.load-css('./interfaces/FlavorText.scss'); @include meta.load-css('./interfaces/HellishRunes.scss'); @include meta.load-css('./interfaces/HotKeysHelp.scss'); @include meta.load-css('./interfaces/Hypertorus.scss');