From 43d31857390eb04b1f855bd5aedf88df5258f912 Mon Sep 17 00:00:00 2001 From: Superlagg Date: Thu, 18 Jul 2024 14:36:41 -0700 Subject: [PATCH 01/19] chore: Refactor IS_IN_VIEWER_RECT macro parameter order no the fuck it doesnt --- code/controllers/subsystem/datumrentals.dm | 110 +++++++++++++++++++++ code/game/say.dm | 104 ++++++++++++++++--- code/modules/mob/living/say.dm | 20 ++-- 3 files changed, 211 insertions(+), 23 deletions(-) create mode 100644 code/controllers/subsystem/datumrentals.dm diff --git a/code/controllers/subsystem/datumrentals.dm b/code/controllers/subsystem/datumrentals.dm new file mode 100644 index 0000000000..6aa8e60daa --- /dev/null +++ b/code/controllers/subsystem/datumrentals.dm @@ -0,0 +1,110 @@ +/* + * File: datumrentals.dm + * Date: 13/13/2013 + * License: rent to own + * + * Purpose: A handy library system to hold all the wierd temporary data packets that would + * otherwise be sent as a dozen or so args and misc data lists + * As we all know, anything a frickhuge list or mass of args can do, a datum can do better. + * A proc can check out a datum, fill it with data, pass it around like the town bicycle, and then + * check it back in when it's done. + * They're generally intended to only be used for at most a second or two, otherwise you're not renting, you're buying. + * Which, fine, you can keep it, but really at that point you should just give it its own home. + * If we need a datum, but none are available, we can create a new one, but we should always check it back in. + * The checkin process is so that the datum can be recycled and reused, cus we're all about recycling here. + * Save the datums, nuke the whales. + * */ + +SUBSYSTEM_DEF(rentaldatums) + name = "RentalDatums" + flags = SS_TICKER + wait = 1 MINUTES + init_order = INIT_ORDER_RENTALS + + var/list/mommies = list() + + /// now for the rental mommies + var/list/chat_datums = list() + var/chat_uses_mommy = TRUE + + +/datum/controller/subsystem/rentaldatums/Initialize(start_timeofday) + init_datums() + . = ..() + +/datum/controller/subsystem/rentaldatums/proc/init_datums() + // + +/datum/controller/subsystem/rentaldatums/proc/CheckoutMommy(mom) + var/list/mymom = LAZYACCESS(vars, mom) + if(!mymom) + return null + for(var/datum/rental_mommy/mommy in mommies) + if(mommy.available) + mommy.checkout() + return mommy + var/datum/rental_mommy/mommy = LAZYACCESS(mymom, 1) // there will always be at least one mommy + var/datum/rental_mommy/mommy2 = new mommy.path() + mymom += mommy2 + mommy2.checkout() + return mommy2 + +/datum/rental_mommy // hey isnt that your mom? + /// Is your mom available? + var/available = TRUE + /// The number of tally marks sharpied on your mom's butt that denotes how many times she's been "rented" + var/uses = 0 + +/datum/rental_mommy/proc/checkout() + available = FALSE + uses += 1 + +/datum/rental_mommy/proc/checkin() + wipe() + available = TRUE + +/datum/rental_mommy/proc/wipe() + return + +/datum/rental_mommy/proc/copy_mommy(datum/rental_mommy/mommy) + if(mommy.type != type) + return + for(var/V in vars) + if(V == "vars") + continue + vars[V] = mommy.vars[V] + +/// Charlotte, chat's rental mommy +/// Holds a dynamic glob of chat data that can be easily manipulated and passed around +/// She loves it +/datum/rental_mommy/chat + var/original_message = "" + var/message = "" + var/original_speakername = "" + var/speakername = "" + var/atom/source + var/message_mode + var/message_key + var/list/spans = list() + var/sanitize + var/bubble_type = null + var/datum/language/language + var/language_key + var/datum/saymode/saymode + var/ignore_spam + var/forced + var/only_overhead + var/is_radio + var/radio_freq + var/close_message_range = 7 + var/far_message_range = 15 + /// Stuff to put to the left of the inner body of the message + var/list/msg_decor_left = list() + /// Stuff to put to the right of the inner body of the message + var/list/msg_decor_right = list() + /// for ventriloquist dolls to prevent the player from saying whatever it is they're saying, + /// and for the doll to say it instead + var/no_pass + /// SHOULD THE MESSAGE BE RENDERED IN ALL CAPS??????????????? + var/ALL_CAPS + diff --git a/code/game/say.dm b/code/game/say.dm index e1bf084124..2be63402b4 100644 --- a/code/game/say.dm +++ b/code/game/say.dm @@ -35,26 +35,59 @@ And the base of the send_speech() proc, which is the core of saycode. var/atom/movable/AM = _AM AM.Hear(rendered, src, message_language, message, , spans, message_mode, source, just_chat) -/atom/movable/proc/compose_message(atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode, face_name = FALSE, atom/movable/source) +/atom/movable/proc/compose_message( + atom/movable/speaker, + datum/language/message_language, + raw_message, + radio_freq, + list/spans= list(), + message_mode, + face_name = FALSE, + atom/movable/source, + list/data = list() +) // 9 ARGS! 15 BUTTS!!!! SECRET THIRD LEG!!!!!!!! if(!source) source = speaker + var/datum/rental_mommy/momchat = LAZYACCESS(data["momchat"], 1) + if(momchat) + momchat.original_message = raw_message + momchat.message = momchat.original_message + momchat.message_mode = message_mode + momchat.radio_freq = radio_freq + momchat.spans = spans.Copy() + momchat.sanitize = TRUE + momchat.source = source + momchat.language = message_language + momchat.face_name = face_name + momchat.data = data.Copy() //This proc uses text() because it is faster than appending strings. Thanks BYOND. //Basic span - var/spanpart1 = "" + var/outer_span = "[radio_freq ? get_radio_span(radio_freq) : "game say"]" + var/spanpart1 = "" + if(momchat) + momchat.outer_span_class = outer_span + momchat //Start name span. var/spanpart2 = "" + if(momchat) + momchat.name_span_class = "name" + momchat.name_span = spanpart2 //Radio freq/name display var/freqpart = radio_freq ? "\[[get_radio_name(radio_freq)]\] " : "" + if(momchat) + momchat.freqpart = freqpart //Speaker name var/namepart = "[speaker.GetVoice()][speaker.get_alt_name()]" if(face_name && ishuman(speaker)) var/mob/living/carbon/human/H = speaker namepart = "[H.get_face_name()]" //So "fake" speaking like in hallucinations does not give the speaker away if disguised + if(momchat) + momchat.speaker_name = namepart //End name span. var/endspanpart = "" //Message - var/messagepart = " [lang_treat(speaker, message_language, raw_message, spans, message_mode)]" + var/messagepart = " [lang_treat(speaker, message_language, raw_message, spans, message_mode, momchat)]" var/languageicon = "" var/datum/language/D = GLOB.language_datum_instances[message_language] @@ -88,18 +121,26 @@ And the base of the send_speech() proc, which is the core of saycode. . = verb_say return get_random_if_list(.) -/atom/movable/proc/say_quote(input, list/spans=list(speech_span), message_mode) +/atom/movable/proc/say_quote(input, list/spans=list(speech_span), message_mode, datum/rental_mommy/momchat) if(!input) input = "..." if(copytext_char(input, -2) == "!!") spans |= SPAN_YELL - var/reformatted = SSchat.emoticonify(src, input, message_mode, spans) + var/reformatted = SSchat.emoticonify(src, input, message_mode, spans, momchat) if(reformatted) return reformatted var/spanned = attach_spans(input, spans) - return "[say_mod(input, message_mode)][spanned ? ", \"[spanned]\"" : ""]" + if(momchat) + momchat.message_langtreated_spanned = spanned + momchat.message_langtreated_spanned_quotes = "[spanned]" + var/saymod = say_mod(input, message_mode) + if(momchat) + momchat.message_saymod = saymod + if(spanned) + momchat.message_saymod_comma = "[saymod] ," + return "[momchat][spanned ? ", \"[spanned]\"" : ""]" // Citadel edit [spanned ? ", \"[spanned]\"" : ""]" #define ENCODE_HTML_EPHASIS(input, char, html, varname) \ @@ -124,31 +165,64 @@ And the base of the send_speech() proc, which is the core of saycode. return /// Quirky citadel proc for our custom sayverbs to strip the verb out. Snowflakey as hell, say rewrite 3.0 when? -/atom/movable/proc/quoteless_say_quote(input, list/spans = list(speech_span), message_mode) +/atom/movable/proc/quoteless_say_quote(input, list/spans = list(speech_span), message_mode, list/bundle) if((input[1] == "!") && (length_char(input) > 1)) return "" - var/emoticontext = SSchat.emoticonify(src, input, message_mode, spans) + var/emoticontext = SSchat.emoticonify(src, input, message_mode, spans, bundle) if(emoticontext) return emoticontext var/pos = findtext(input, "*") - return pos? copytext(input, pos + 1) : input - -/atom/movable/proc/lang_treat(atom/movable/speaker, datum/language/language, raw_message, list/spans, message_mode, no_quote = FALSE) + var/message = pos? copytext(input, 1, pos - 1) : input + return message + +/// This proc is used to treat the language of a message. It will either scramble the message or leave it as is. +/// it will also do like 5 other things totally unrelated to language, because why not. +/atom/movable/proc/lang_treat( + atom/movable/speaker, + datum/language/language, + raw_message, + list/spans, + message_mode, + no_quote = FALSE, + datum/rental_mommy/momchat, +) if(has_language(language)) var/atom/movable/AM = speaker.GetSource() raw_message = say_emphasis(raw_message) + var/msg_out if(AM) //Basically means "if the speaker is virtual" - return no_quote ? AM.quoteless_say_quote(raw_message, spans, message_mode) : AM.say_quote(raw_message, spans, message_mode) + if(no_quote) + msg_out = AM.quoteless_say_quote(raw_message, spans, message_mode, momchat) + else // ^ V these used to be ternary operators that were so long they burst out of my sweatpants + msg_out = AM.say_quote(raw_message, spans, message_mode, momchat) + if(momchat) + momchat.message_langtreated_with_verb = msg_out + return msg_out else - return no_quote ? speaker.quoteless_say_quote(raw_message, spans, message_mode) : speaker.say_quote(raw_message, spans, message_mode) + if(no_quote) + msg_out = speaker.quoteless_say_quote(raw_message, spans, message_mode, momchat) + else + msg_out = speaker.say_quote(raw_message, spans, message_mode, momchat) + if(momchat) + momchat.message_langtreated_with_verb = msg_out else if(language) var/atom/movable/AM = speaker.GetSource() var/datum/language/D = GLOB.language_datum_instances[language] raw_message = D.scramble(raw_message) if(AM) - return no_quote ? AM.quoteless_say_quote(raw_message, spans, message_mode) : AM.say_quote(raw_message, spans, message_mode) + if(no_quote) + return AM.quoteless_say_quote(raw_message, spans, message_mode, momchat) + else + return AM.say_quote(raw_message, spans, message_mode, momchat) + if(momchat) + momchat.message_langtreated_with_verb = msg_out else - return no_quote ? speaker.quoteless_say_quote(raw_message, spans, message_mode) : speaker.say_quote(raw_message, spans, message_mode) + if(no_quote) + return speaker.quoteless_say_quote(raw_message, spans, message_mode, momchat) + else + return speaker.say_quote(raw_message, spans, message_mode, momchat) + if(momchat) + momchat.message_langtreated_with_verb = msg_out else return "makes a strange sound." diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index d24e8c6f12..6733c6e5a9 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -174,13 +174,13 @@ return 1 -/mob/living/compose_message(atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode, face_name = FALSE, atom/movable/source) +/mob/living/compose_message(atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode, face_name = FALSE, atom/movable/source, list/data = list()) . = ..() - if(isliving(speaker)) - var/turf/sourceturf = get_turf(source) - var/turf/T = get_turf(src) - if(sourceturf && T && !(sourceturf in get_hear(5, T))) - . = span_small("[.]") + // if(isliving(speaker)) + // var/turf/sourceturf = get_turf(source) + // var/turf/T = get_turf(src) + // if(sourceturf && T && !(sourceturf in get_hear(5, T))) + // . = span_small("[.]") /mob/living/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source, just_chat = FALSE, list/data = list()) SEND_SIGNAL(src, COMSIG_MOVABLE_HEAR, args) //parent calls can't overwrite the current proc args. @@ -292,7 +292,9 @@ eavesdropping = dots(message) eavesrendered = compose_message(src, message_language, eavesdropping, null, spans, message_mode, FALSE, source) - var/rendered = compose_message(src, message_language, message, null, spans, message_mode, FALSE, source) + var/datum/rental_mommy/momchat = SSrentaldatums.CheckoutMommy("chat_datums") + var/list/rental_data = list("mommy" = momchat) // mommy is very disappointed + var/rendered = compose_message(src, message_language, message, null, spans, message_mode, FALSE, source, rental_data) /// non-players for(var/_AM in listening) var/atom/movable/AM = _AM @@ -304,7 +306,9 @@ var/list/sblistening = list() /// players for(var/mob/mvc in visible_close) - mvc.Hear(rendered, src, message_language, message, null, spans, message_mode, source, just_chat) + var/list/data = list() + data["horny_stuff"] = composition_list + mvc.Hear(rendered, src, message_language, message, null, spans, message_mode, source, just_chat, data) sblistening |= mvc.client // for(var/mob/mvf in visible_far) // var/list/coolspans = spans From 3ffa75366e2339820ddb9bb1fa5c74f1233d82b8 Mon Sep 17 00:00:00 2001 From: Superlagg Date: Sun, 21 Jul 2024 15:18:23 -0700 Subject: [PATCH 02/19] Refactor IS_IN_VIEWER_RECT macro parameter order stop fucking saying that, its not what it does --- code/__DEFINES/subsystems.dm | 1 + code/__HELPERS/text.dm | 7 +- code/controllers/subsystem/chat.dm | 201 +++ code/controllers/subsystem/datumrentals.dm | 36 +- code/controllers/subsystem/dummies.dm | 4 +- code/controllers/subsystem/test.dm | 1098 +++++++++++++++++ code/game/say.dm | 37 +- code/modules/admin/admin_verbs.dm | 29 + code/modules/client/preferences.dm | 19 + code/modules/client/preferences_savefile.dm | 3 + code/modules/mob/living/say.dm | 177 ++- code/modules/mob/mob.dm | 60 +- code/modules/mob/mob_helpers.dm | 9 +- fortune13.dme | 1 + modular_coyote/code/modules/examine_images.dm | 37 +- testdoc.html | 377 ++---- 16 files changed, 1681 insertions(+), 415 deletions(-) create mode 100644 code/controllers/subsystem/test.dm diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index 4f27159d47..9f2eae7d43 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -127,6 +127,7 @@ #define INIT_ORDER_DEMO -99 // o avoid a bunch of changes related to initialization being written, do this last #define INIT_ORDER_CHAT -100 //Should be last to ensure chat remains smooth during init. #define INIT_ORDER_INTERACTIONS -150 +#define INIT_ORDER_RENTALS -160 // Subsystem fire priority, from lowest to highest priority // If the subsystem isn't listed here it's either DEFAULT or PROCESS (if it's a processing subsystem child) diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index 95c32af82c..7dd4a178c7 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -878,7 +878,10 @@ GLOBAL_LIST_INIT(hex_6toc, list("6","7","8","9","a","b","c")) return replacetext(text, keyword, span_color(keyword, color)) ///does both of above, for chat log coloring of player messages. -/proc/color_for_chatlog(text, color, name) +/proc/color_for_chatlog(text, color, name, datum/rental_mommy/chat/momchat) var/out = color_keyword(text, color, name) out = alternating_color_span(out, color, "\"", FALSE) - return out + . = out + if(momchat) // do it again, but this time with all our dumb vars + momchat.namepart = color_keyword(momchat.namepart, color, name) + momchat.message = alternating_color_span(momchat.message, color, "\"", FALSE) diff --git a/code/controllers/subsystem/chat.dm b/code/controllers/subsystem/chat.dm index d1ee2ecdcb..ce05d39fff 100644 --- a/code/controllers/subsystem/chat.dm +++ b/code/controllers/subsystem/chat.dm @@ -144,6 +144,207 @@ SUBSYSTEM_DEF(chat) return LAZYADD(payload_by_client[client], list(message)) +/datum/controller/subsystem/chat/proc/PreviewHornyFurryDatingSimMessage(mob/target, message_mode) + if(!istype(target)) + CRASH("PreviewHornyFurryDatingSimMessage called with invalid arguments! [speaker]! [target]!") + if(!message_mode) + CRASH("PreviewHornyFurryDatingSimMessage called with invalid arguments! [message_mode]!") + + var/mob/living/carbon/human/dummy/D = SSdummy.get_dummy() + var/datum/preferences/P = extract_prefs(target) + if(!P) + CRASH("PreviewHornyFurryDatingSimMessage called with invalid arguments! [P]!") + P.copy_to(D) + D.dummyckey = target.ckey + var/msg = "Hey there! How's it going? I was thinking we could go on a date sometime. I'm a vampire and" + switch(message_mode) + if(MODE_SAY) + msg = msg + if(MODE_WHISPER) + msg = "#[msg]" + if(MODE_SING) + msg = "%[msg]" + if(MODE_ASK) + msg = "[msg]?" + if(MODE_EXCLAIM) + msg = "[msg]!" + if(MODE_YELL) + msg = "$[msg]" + + var/datum/rental_mommy/chat/mommy = D.say(msg, message_mode) + mommy.prefs_override = P + var/mommess = BuildHornyFurryDatingSimMessage(mommy, TRUE) + mommy.checkin() + SSdummy.return_dummy(D) + to_chat(target, mommess) + +/datum/controller/subsystem/chat/proc/BuildHornyFurryDatingSimMessage(datum/rental_mommy/chat/mommy) + if(!istype(mommy)) + CRASH("BuildHornyFurryDatingSimMessage called with invalid arguments! [target]! [mommy]!") + if(!istype(mommy.recipiant)) + CRASH("BuildHornyFurryDatingSimMessage called with invalid arguments! [target]!") + if(!mommy.source) + CRASH("BuildHornyFurryDatingSimMessage called with invalid arguments! [mommy.source]!!!!") + var/datum/preferences/P = mommy.prefs_override || extract_prefs(mommy.source) + if(!P) + CRASH("BuildHornyFurryDatingSimMessage called with invalid arguments! [P]!") + /// SO. now we need a few things from the speaker (mommy) + /// - Name + /// - Spoken Verb + /// - Rendered message, with quotes + /// - The message mode, for reasons i'll get into later + /// - A list of profile images + /// - A link to their chardir profile + /// - A link to DM them + /// - A link to flirt with them + /// - A link to "interact" with them + /// - A color for the text background + /// - A color for the header background + /// and from this, we will make a furry dating sim style message that will be sent to the target *and* the speaker + var/m_name = mommy.namepart + var/m_verb = mommy.message_saymod_comma + var/m_rawmessage = mommy.original_message + var/m_message = mommy.message_langtreated_spanned_quotes + var/m_mode = mommy.message_mode + + /// look for something in m_rawmessage formatted as :exammple: and extract that to look up a custom image + /// We'll extract this, store it as a var, and use it as an override for the profile image + var/list/m_images = P ? P.profilePics.Copy() : test_pics + var/m_pfp = get_horny_pfp(m_rawmessage, m_images, m_mode) + + + /// now all the many many colors for everything! + // first the background gradients (and their angles) + var/tgc_1 = "#[P.mommychat_settings["top_gradient_color_1"]]" + var/tgc_2 = "#[P.mommychat_settings["top_gradient_color_2"]]" + var/tgangle = "[P.mommychat_settings["top_gradient_angle"]]" + var/bgc_1 = "#[P.mommychat_settings["bottom_gradient_color_1"]]" + var/bgc_2 = "#[P.mommychat_settings["bottom_gradient_color_2"]]" + var/bgangle = "[P.mommychat_settings["bottom_gradient_angle"]]" + var/bbc_1 = "#[P.mommychat_settings["button_background_color_1"]]" + var/bbc_2 = "#[P.mommychat_settings["button_background_color_2"]]" + var/bbangle = "[P.mommychat_settings["button_background_angle"]]" + // now the borders + var/obc = "#[P.mommychat_settings["outer_border_color"]]" + var/tbc = "#[P.mommychat_settings["top_border_color"]]" + var/bbc = "#[P.mommychat_settings["bottom_border_color"]]" + var/ibc = "#[P.mommychat_settings["image_border_color"]]" + var/ibs = "[P.mommychat_settings["image_border_size"]]" + var/ibt = "[P.mommychat_settings["image_border_style"]]" + // now the text colors + // most are defined by mommy, but some arent, so we'll need to get a color that contrasts with the average of the top and bottom gradient colors + var/tgc_to_num = hex2num(tgc_1) + hex2num(tgc_2) + var/bgc_to_num = hex2num(bgc_1) + hex2num(bgc_2) + var/avg_color = (tgc_to_num + bgc_to_num) / 4 + /// now we need to get the contrast color + var/contrast_color = num2hex(16777215 - avg_color) + var/dtc = "#[contrast_color]" + + /// Character Directory link + var/m_charlink = {"Profile"} + /// DM link + var/m_dmlink = {"DM"} + /// Flirt link + var/m_flirtlink = {"Flirt"} + /// Interact link + var/m_interactlink = {"Interact"} + + /// now we need to build the message + var/list/cum = list() + // First, the full body container + cum += "
" + // first the head + cum += "
" + // now the profile picture + cum += "
" + cum += "" + cum += "
" + // now the rest of the head + cum += "
" + cum += "[m_name]" // already formatted! + // now the button panel + cum += "
" // yiff is a joke, dont worry + cum += m_charlink + cum += m_dmlink + cum += m_flirtlink + cum += m_interactlink + cum += "
" + cum += "
" + cum += "
" + // now the body + cum += "
" + cum += "

[m_name] [m_verb]

" + cum += "

[m_message]

" + cum += "
" + cum += "
" + // now we need to send it to the target + return cum.Join() + + + + + + + // + //
+ // + //
+ // + //
+ // + //
+ // + // Foxxxy Vixen + //
+ // + // + // + // + //
+ //
+ //
+ // + //
+ //

Foxxxy Vixen asks,

+ //

Hey there! How's it going? I was thinking we could go on a date sometime. What do you say?

+ + +/datum/mob/proc/get_horny_pfp(m_rawmessage, list/m_images, m_mode) + var/image2use = "" + var/first_colon = findtext(m_rawmessage, ":") + if(first_colon) + var/list/splittify = splittext(m_rawmessage, ":") + for(var/splut in splittify) + var/testpart = ":[splut]:" + if(findtext(m_rawmessage, testpart)) + if(m_images[testpart]) + var/list/imgz = m_images[testpart] + if(imgz["link"] != "" && imgz["host"] != "") + image2use = PfpHostLink(imgz["link"], imgz["host"]) + /// then extract the message mode and see if they have a corresponting image + if(!SanitizePfpLink(image2use)) + var/list/testimages = m_images[m_mode] + if(testimages["link"] != "" && testimages["host"] != "") + image2use = PfpHostLink(testimages["link"], testimages["host"]) + // just grab their default one + if(!SanitizePfpLink(image2use)) + var/list/testimages = m_images[MODE_SAY] + if(testimages["link"] != "" && testimages["host"] != "") + image2use = PfpHostLink(testimages["link"], testimages["host"]) + // if we still dont have one, just use a placeholder + if(!SanitizePfpLink(image2use)) + image2use = "https://www.placehold.it/100x100.png" + return image2use + + /datum/controller/subsystem/chat/proc/build_flirt_datums() if(LAZYLEN(flirts)) QDEL_LIST_ASSOC_VAL(flirts) diff --git a/code/controllers/subsystem/datumrentals.dm b/code/controllers/subsystem/datumrentals.dm index 6aa8e60daa..13484d6cd0 100644 --- a/code/controllers/subsystem/datumrentals.dm +++ b/code/controllers/subsystem/datumrentals.dm @@ -17,7 +17,7 @@ SUBSYSTEM_DEF(rentaldatums) name = "RentalDatums" - flags = SS_TICKER + flags = SS_TICKER | SS_NO_FIRE wait = 1 MINUTES init_order = INIT_ORDER_RENTALS @@ -25,8 +25,7 @@ SUBSYSTEM_DEF(rentaldatums) /// now for the rental mommies var/list/chat_datums = list() - var/chat_uses_mommy = TRUE - + var/chat_uses_mommy = TRUE // my code my names /datum/controller/subsystem/rentaldatums/Initialize(start_timeofday) init_datums() @@ -39,12 +38,12 @@ SUBSYSTEM_DEF(rentaldatums) var/list/mymom = LAZYACCESS(vars, mom) if(!mymom) return null - for(var/datum/rental_mommy/mommy in mommies) + for(var/datum/rental_mommy/chat/mommy in mommies) if(mommy.available) mommy.checkout() return mommy - var/datum/rental_mommy/mommy = LAZYACCESS(mymom, 1) // there will always be at least one mommy - var/datum/rental_mommy/mommy2 = new mommy.path() + var/datum/rental_mommy/chat/mommy = LAZYACCESS(mymom, 1) // there will always be at least one mommy + var/datum/rental_mommy/chat/mommy2 = new mommy.type() mymom += mommy2 mommy2.checkout() return mommy2 @@ -66,7 +65,7 @@ SUBSYSTEM_DEF(rentaldatums) /datum/rental_mommy/proc/wipe() return -/datum/rental_mommy/proc/copy_mommy(datum/rental_mommy/mommy) +/datum/rental_mommy/proc/copy_mommy(datum/rental_mommy/chat/mommy) if(mommy.type != type) return for(var/V in vars) @@ -107,4 +106,27 @@ SUBSYSTEM_DEF(rentaldatums) var/no_pass /// SHOULD THE MESSAGE BE RENDERED IN ALL CAPS??????????????? var/ALL_CAPS + var/runechat_mode + var/mob/recipiant + var/display_turf + var/is_eavesdropping + var/chat_color_base + var/chat_color_sanitized + var/furry_dating_sim + var/namepart + var/face_name + var/data = list() + var/outer_span_class + var/outer_span + var/name_span_class + var/name_span + var/freqpart + var/speaker_name + var/message_langtreated_spanned + var/message_langtreated_spanned_quotes + var/message_saymod + var/message_saymod_comma + var/message_langtreated_quoteless + var/message_langtreated_with_verb + var/language_icon diff --git a/code/controllers/subsystem/dummies.dm b/code/controllers/subsystem/dummies.dm index f019c58e3b..413bd00473 100644 --- a/code/controllers/subsystem/dummies.dm +++ b/code/controllers/subsystem/dummies.dm @@ -2,7 +2,7 @@ #define DUMMY_RANDOM_DESIRED_SIZE 20 SUBSYSTEM_DEF(dummy) // who ya callin dummy, dummy? - name = "dummy" + name = "Dummy" flags = SS_BACKGROUND wait = 30 MINUTES @@ -315,6 +315,7 @@ SUBSYSTEM_DEF(dummy) // who ya callin dummy, dummy? status_flags = GODMODE|CANPUSH mouse_drag_pointer = MOUSE_INACTIVE_POINTER var/in_use = FALSE + var/dummyckey COOLDOWN_DECLARE(unuse_timer) INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy) @@ -336,6 +337,7 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy) dna.initialize_dna("B+", randomise = TRUE) icon_render_key = null transform = initial(transform) + dummyckey = null destroy_genitals() cut_overlays() diff --git a/code/controllers/subsystem/test.dm b/code/controllers/subsystem/test.dm new file mode 100644 index 0000000000..e3c26f2a19 --- /dev/null +++ b/code/controllers/subsystem/test.dm @@ -0,0 +1,1098 @@ +/* + * Filename: GropeKissMERP.dm + * Author: GropeKiss + * Date: Next Tuesday + * Purpose: To make hand items that can grope and kiss and do other things to very specific body parts and such + * + * these are singleton datums that hand items will ask to spit out some lovely text + * + * HELP - mostly rubbing or loving actions + * DISARM - mostly more playful teasing actions + * GRAB - things involving more assertive or gropey actions + * HARM - things that are more aggressive or painful + * + * */ + +GLOBAL_LIST_EMPTY(gropekissers) +///Welcome to GropeKiss +/datum/grope_kiss_MERP + /// accessible areas, by part, and by side + /* + for action tenses, format is: + I "suck" Tia's [toes], (present tense, user performing) + Dan "sucks" my [toes], (present tense, target recieving) + I continue "sucking" Tia's [toes] / Dan continues "sucking" my [toes], (continuous/gerundo) + Dan "sucked" Tia's [toes], (past tense, third person) + */ + var/list/head_front = list("head") + var/list/head_front_action_help = list( + "pat", + "pats", + "patting", + "patted", + ) + var/list/head_front_action_disarm = list( + "stroke", + "strokes", + "stroking", + "stroked", + ) + var/list/head_front_action_grab = list( + "grab", + "grabs", + "grabbing", + "grabbed", + ) + var/list/head_front_action_harm = list( + "slap", + "slaps", + "slapping", + "slapped", + ) + var/list/head_front_action_help_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/head_front_action_disarm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/head_front_action_grab_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/head_front_action_harm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + + var/list/head_side = list("ear") + var/list/head_side_action_help = list( + "caress", + "caresses", + "caressing", + "caressed", + ) + var/list/head_side_action_disarm = list( + "rub", + "rubs", + "rubbing", + "rubbed", + ) + var/list/head_side_action_grab = list( + "tug on", + "tugs on", + "tugging on", + "tugged on", + ) + var/list/head_side_action_harm = list( + "pinch", + "pinches", + "pinching", + "pinched", + ) + var/list/head_side_action_help_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/head_side_action_disarm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/head_side_action_grab_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/head_side_action_harm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + + var/list/head_back = list("neck") + var/list/head_back_action_help = list( + "scritch the back of", + "scritches the back of", + "scritching the back of", + "scritched the back of", + ) + var/list/head_back_action_disarm = list( + "massage", + "massages", + "massaging", + "massaged", + ) + var/list/head_back_action_grab = list( + "squeeze", + "squeezes", + "squeezing", + "squeezed", + ) + var/list/head_back_action_harm = list( + "choke", + "chokes", + "choking", + "choked", + ) + var/list/head_back_action_help_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/head_back_action_disarm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/head_back_action_grab_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/head_back_action_harm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + + /// eyes are special + var/list/eyes_front = list("eyes") + var/list/eyes_front_action_help = list( + "gaze into", + "gazes into", + "gazing into", + "gazed into", + ) + var/list/eyes_front_action_disarm = list( + "blink at", + "blinks at", + "blinking at", + "blinked at", + ) + var/list/eyes_front_action_grab = list( + "stare into", + "stares into", + "staring into", + "stared into", + ) + var/list/eyes_front_action_harm = list( + "poke", + "pokes", + "poking", + "poked", + ) + var/list/eyes_front_action_help_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/eyes_front_action_disarm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/eyes_front_action_grab_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/eyes_front_action_harm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + + var/list/eyes_side = list("eyebrow") + var/list/eyes_side_action_help = list( + "stroke", + "strokes", + "stroking", + "stroked", + ) + var/list/eyes_side_action_disarm = list( + "tickle", + "tickles", + "tickling", + "tickled", + ) + var/list/eyes_side_action_grab = list( + "furrow", + "furrows", + "furrowing", + "furrowed", + ) + var/list/eyes_side_action_harm = list( + "pluck", + "plucks", + "plucking", + "plucked", + ) + var/list/eyes_side_action_help_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/eyes_side_action_disarm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/eyes_side_action_grab_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/eyes_side_action_harm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + + var/list/eyes_back = list("earlobe") + var/list/eyes_back_action_help = list( + "dingle", + "dingles", + "dingling", + "dingled", + ) + var/list/eyes_back_action_disarm = list( + "rub", + "rubs", + "rubbing", + "rubbed", + ) + var/list/eyes_back_action_grab = list( + "tug on", + "tugs on", + "tugging on", + "tugged on", + ) + var/list/eyes_back_action_harm = list( + "flick", + "flicks", + "flicking", + "flicked", + ) + var/list/eyes_back_action_help_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/eyes_back_action_disarm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/eyes_back_action_grab_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/eyes_back_action_harm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + + /// mouth is also special + var/list/mouth_front = list("lips") + var/list/mouth_front_action_help = list( + "run fingers across", + "runs fingers across", + "running fingers across", + "ran fingers across", + ) + var/list/mouth_front_action_disarm = list( + "put finger to", + "puts finger to", + "putting finger to", + "put finger to", + ) + var/list/mouth_front_action_grab = list( + "slip fingers into", + "slips fingers into", + "slipping fingers into", + "slipped fingers into", + ) + var/list/mouth_front_action_harm = list( + "shove hand into", + "shoves hand into", + "shoving hand into", + "shoved hand into", + ) + var/list/mouth_front_action_help_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/mouth_front_action_disarm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/mouth_front_action_grab_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/mouth_front_action_harm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + + var/list/mouth_side = list("cheek") + var/list/mouth_side_action_help = list( + "caress", + "caresses", + "caressing", + "caressed", + ) + var/list/mouth_side_action_disarm = list( + "place hand on", + "places hand on", + "placing hand on", + "placed hand on", + ) + var/list/mouth_side_action_grab = list( + "grab", + "grabs", + "grabbing", + "grabbed", + ) + var/list/mouth_side_action_harm = list( + "slap", + "slaps", + "slapping", + "slapped", + ) + var/list/mouth_side_action_help_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/mouth_side_action_disarm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/mouth_side_action_grab_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/mouth_side_action_harm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + +//back of neck start// + var/list/mouth_back = list("the back of neck") // this is the thing that I am doing to Their Thing + var/list/mouth_back_action_help = list( + "stroke", + "strokes", + "stroking", + "stroked", + ) + var/list/mouth_back_action_disarm = list( + "dig fingers into", + "digs fingers into", + "digging fingers into", + "dug fingers into", + ) + var/list/mouth_back_action_grab = list( + "grab", //I [grab] the (back of Tia's neck) + "grabs", //Dan [grabs] (the back of my neck) + "grabbing", // Dan continues [grabbing] (the back of my neck) + "grabbed", // Dan [grabbed] (the back of Tia's neck) + ) +var/list/mouth_back_action_harm = list( + "squish", //I squish the back of Tia's neck + "squishes", //Dan grabs the back of my neck + "squishing", // Dan continues grabbing the back of my neck + "squished", // Dan grabbed the back of Tia's neck + ) + var/list/mouth_back_action_help_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/mouth_back_action_disarm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/mouth_back_action_grab_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/mouth_back_action_harm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') +//back of neck end// + + var/list/chest_front = list("chest") + var/list/chest_front_action_help = list( + "caress", + "caresses", + "caressing", + "caressed", + ) + var/list/chest_front_action_disarm = list( + "rub", + "rubs", + "rubbing", + "rubbed", + ) + var/list/chest_front_action_grab = list( + "grab", + "grabs", + "grabbing", + "grabbed", + ) + var/list/chest_front_action_harm = list( + "bap", + "baps", + "baping", + "bapped", + ) + var/list/chest_front_action_help_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/chest_front_action_disarm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/chest_front_action_grab_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/chest_front_action_harm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + + var/list/chest_side = list("underarm") + var/list/chest_side_action_help = list( + "feel up", + "feels up", + "feeling up", + "felt up", + ) + var/list/chest_side_action_disarm = list( + "tickle", + "tickles", + "tickling", + "tickled", + ) + var/list/chest_side_action_grab = list( + "grab", + "grabs", + "grabbing", + "grabbed", + ) + var/list/chest_side_action_harm = list( + "pinch", + "pinches", + "pinching", + "pinched", + ) + var/list/chest_side_action_help_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/chest_side_action_disarm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/chest_side_action_grab_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/chest_side_action_harm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + + var/list/chest_back = list("back") + var/list/chest_back_action_help = list( + "stroke", + "strokes", + "stroking", + "stroked", + ) + var/list/chest_back_action_disarm = list( + "massage", + "massages", + "massaging", + "massaged", + ) + var/list/chest_back_action_grab = list( + "scratch", + "scratches", + "scratching", + "scratched", + ) + var/list/chest_back_action_harm = list( + "slap", + "slaps", + "slapping", + "slapped", + ) + var/list/chest_back_action_help_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/chest_back_action_disarm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/chest_back_action_grab_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/chest_back_action_harm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + + var/list/l_arm_front = list("left arm") + var/list/l_arm_front_action_help = list( + "caress", + "caresses", + "caressing", + "caressed", + ) + var/list/l_arm_front_action_disarm = list( + "rub", + "rubs", + "rubbing", + "rubbed", + ) + var/list/l_arm_front_action_grab = list( + "grab", + "grabs", + "grabbing", + "grabbed", + ) + var/list/l_arm_front_action_harm = list( + "poke", + "pokes", + "poking", + "poked", + ) + var/list/l_arm_front_action_help_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/l_arm_front_action_disarm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/l_arm_front_action_grab_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/l_arm_front_action_harm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + + var/list/l_arm_side = list("left shoulder") + var/list/l_arm_side_action_help = list( + "caress", + "caresses", + "caressing", + "caressed", + ) + var/list/l_arm_side_action_disarm = list( + "rub", + "rubs", + "rubbing", + "rubbed", + ) + var/list/l_arm_side_action_grab = list( + "grip", + "grips", + "gripping", + "gripped", + ) + var/list/l_arm_side_action_harm = list( + "pinch", + "pinches", + "pinching", + "pinched", + ) + var/list/l_arm_side_action_help_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/l_arm_side_action_disarm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/l_arm_side_action_grab_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/l_arm_side_action_harm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + + var/list/l_arm_back = list("left upper back") + var/list/l_arm_back_action_help = list( + "rub", + "rubs", + "rubbing", + "rubbed", + ) + var/list/l_arm_back_action_disarm = list( + "massage", + "massages", + "massaging", + "massaged", + ) + var/list/l_arm_back_action_grab = list( + "scratch", + "scratches", + "scratching", + "scratched", + ) + var/list/l_arm_back_action_harm = list( + "slap", + "slaps", + "slapping", + "slapped", + ) + var/list/l_arm_back_action_help_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/l_arm_back_action_disarm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/l_arm_back_action_grab_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/l_arm_back_action_harm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + + + /// right arms are the same as left arms, but on the right side + var/list/r_arm_front = list("right arm") + var/list/r_arm_front_action_help = list( + "caress", + "caresses", + "caressing", + "caressed", + ) + var/list/r_arm_front_action_disarm = list( + "rub", + "rubs", + "rubbing", + "rubbed", + ) + var/list/r_arm_front_action_grab = list( + "grab", + "grabs", + "grabbing", + "grabbed", + ) + var/list/r_arm_front_action_harm = list( + "poke", + "pokes", + "poking", + "poked", + ) + var/list/r_arm_front_action_help_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/r_arm_front_action_disarm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/r_arm_front_action_grab_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/r_arm_front_action_harm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + + var/list/r_arm_side = list("right shoulder") + var/list/r_arm_side_action_help = list( + "caress", + "caresses", + "caressing", + "caressed", + ) + var/list/r_arm_side_action_disarm = list( + "rub", + "rubs", + "rubbing", + "rubbed", + ) + var/list/r_arm_side_action_grab = list( + "grip", + "grips", + "gripping", + "gripped", + ) + var/list/r_arm_side_action_harm = list( + "pinch", + "pinches", + "pinching", + "pinched", + ) + var/list/r_arm_side_action_help_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/r_arm_side_action_disarm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/r_arm_side_action_grab_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/r_arm_side_action_harm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + + var/list/r_arm_back = list("right upper back") + var/list/r_arm_back_action_help = list( + "rub", + "rubs", + "rubbing", + "rubbed", + ) + var/list/r_arm_back_action_disarm = list( + "massage", + "massages", + "massaging", + "massaged", + ) + var/list/r_arm_back_action_grab = list( + "scratch", + "scratches", + "scratching", + "scratched", + ) + var/list/r_arm_back_action_harm = list( + "slap", + "slaps", + "slapping", + "slapped", + ) + var/list/r_arm_back_action_help_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/r_arm_back_action_disarm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/r_arm_back_action_grab_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/r_arm_back_action_harm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + + /// legs very sensual and demand some "special" attention~ + var/list/l_leg_front = list("left thigh") // the inner thigh + var/list/l_leg_front_action_help = list( + "slide hand up against", + "slides hand up against", + "sliding hand up against", + "slid hand up against", + ) + var/list/l_leg_front_action_disarm = list( + "rub thumb along", + "rubs thumb along", + "rubbing thumb along", + "rubbed thumb along", + ) + var/list/l_leg_front_action_grab = list( + "squeeze fingers into", + "squeezes fingers into", + "squeezing fingers into", + "squeezed fingers into", + ) + var/list/l_leg_front_action_harm = list( + "pinch", + "pinches", + "pinching", + "pinched", + ) + var/list/l_leg_front_action_help_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/l_leg_front_action_disarm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/l_leg_front_action_grab_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/l_leg_front_action_harm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + + var/list/l_leg_front = list("left thigh") // the outer thigh + var/list/l_leg_front_action_help = list( + "rest hand on top of", + "rests hand on top of", + "resting hand on top of", + "rested hand on top of", + ) + var/list/l_leg_front_action_disarm = list( + "rub palm up and down", + "rubs palm up and down", + "rubbing palm up and down", + "rubbed palm up and down", + ) + var/list/l_leg_front_action_grab = list( + "grip fingers into", + "grips fingers into", + "gripping fingers into", + "gripped fingers into", + ) + var/list/l_leg_front_action_harm = list( + "slap", + "slaps", + "slapping", + "slapped", + ) + var/list/l_leg_front_action_help_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/l_leg_front_action_disarm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/l_leg_front_action_grab_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/l_leg_front_action_harm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + + var/list/l_leg_side = list("left hip") // the side of the hip, not quite the butt yet + var/list/l_leg_side_action_help = list( + "place hand on", + "places hand on", + "placing hand on", + "placed hand on", + ) + var/list/l_leg_side_action_disarm = list( + "press hand into", + "presses hand into", + "pressing hand into", + "pressed hand into", + ) + var/list/l_leg_side_action_grab = list( + "grab a handful of", + "grabs a handful of", + "grabbing a handful of", + "grabbed a handful of", + ) + var/list/l_leg_side_action_harm = list( + "smack", + "smacks", + "smacking", + "smacked", + ) + var/list/l_leg_side_action_help_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/l_leg_side_action_disarm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/l_leg_side_action_grab_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/l_leg_side_action_harm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + + var/list/l_leg_back = list("left buttcheek") // the butt, lots of fun here with big fat soft squishy cheeks + var/list/l_leg_back_action_help = list( + "slide hand up against", + "slides hand up against", + "sliding hand up against", + "slid hand up against", + ) + var/list/l_leg_back_action_disarm = list( + "squish fingers into", + "squishes fingers into", + "squishing fingers into", + "squished fingers into", + ) + var/list/l_leg_back_action_grab = list( + "grope a big fat handful of", + "gropes a big fat handful of", + "groping a big fat handful of", + "groped a big fat handful of", + ) + var/list/l_leg_back_action_harm = list( + "give a good hard smack to", + "gives a good hard smack to", + "giving a good hard smack to", + "give a good hard smack to", + ) + var/list/l_leg_back_action_help_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/l_leg_back_action_disarm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/l_leg_back_action_grab_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/l_leg_back_action_harm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + + var/list/r_leg_front = list("right thigh") + var/list/r_leg_front_action_help = list( + "slide hand up against", + "slides hand up against", + "sliding hand up against", + "slid hand up against", + ) + var/list/r_leg_front_action_disarm = list( + "rub thumb along", + "rubs thumb along", + "rubbing thumb along", + "rubbed thumb along", + ) + var/list/r_leg_front_action_grab = list( + "squeeze fingers into", + "squeezes fingers into", + "squeezing fingers into", + "squeezed fingers into", + ) + var/list/r_leg_front_action_harm = list( + "pinch", + "pinches", + "pinching", + "pinched", + ) + var/list/r_leg_front_action_help_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/r_leg_front_action_disarm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/r_leg_front_action_grab_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/r_leg_front_action_harm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + + var/list/r_leg_side = list("right hip") + var/list/r_leg_side_action_help = list( + "place hand on", + "places hand on", + "placing hand on", + "placed hand on", + ) + var/list/r_leg_side_action_disarm = list( + "press hand into", + "presses hand into", + "pressing hand into", + "pressed hand into", + ) + var/list/r_leg_side_action_grab = list( + "grab a handful of", + "grabs a handful of", + "grabbing a handful of", + "grabbed a handful of", + ) + var/list/r_leg_side_action_harm = list( + "smack", + "smacks", + "smacking", + "smacked", + ) + var/list/r_leg_side_action_help_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/r_leg_side_action_disarm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/r_leg_side_action_grab_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/r_leg_side_action_harm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + + var/list/r_leg_back = list("right buttcheek") + var/list/r_leg_back_action_help = list( + "slide hand up against", + "slides hand up against", + "sliding hand up against", + "slid hand up against", + ) + var/list/r_leg_back_action_disarm = list( + "squish fingers into", + "squishes fingers into", + "squishing fingers into", + "squished fingers into", + ) + var/list/r_leg_back_action_grab = list( + "grope a big fat handful of", + "gropes a big fat handful of", + "groping a big fat handful of", + "groped a big fat handful of", + ) + var/list/r_leg_back_action_harm = list( + "give a good hard smack to", + "gives a good hard smack to", + "giving a good hard smack to", + "give a good hard smack to", + ) + var/list/r_leg_back_action_help_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/r_leg_back_action_disarm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/r_leg_back_action_grab_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/r_leg_back_action_harm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + + var/list/groin_front = list("crotch") // very forward, very direct, gender neutral for now + var/list/groin_front_action_help = list( + "slick fingers across", + "slicks fingers across", + "slicking fingers across", + "slicked fingers across", + ) + var/list/groin_front_action_disarm = list( + "rub thumb along", + "rubs thumb along", + "rubbing thumb along", + "rubbed thumb along", + ) + var/list/groin_front_action_grab = list( + "squeeze fingers into", + "squeezes fingers into", + "squeezing fingers into", + "squeezed fingers into", + ) + var/list/groin_front_action_harm = list( + "sink fingers against", + "sinks fingers against", + "sinking fingers against", + "sunk fingers against", + ) + var/list/groin_front_action_help_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/groin_front_action_disarm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/groin_front_action_grab_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/groin_front_action_harm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + + var/list/groin_side = list("love handle") // more fat-focused play + var/list/groin_side_action_help = list( + "reach in and feel the softness of", + "reaches in and feels the softness of", + "reaching in and feeling the softness of", + "reached in and felt the softness of", + ) + var/list/groin_side_action_disarm = list( + "squish fingers into the fatty flesh of", + "squishes fingers into the fatty flesh of", + "squishing fingers into the fatty flesh of", + "squished fingers into the fatty flesh of", + ) + var/list/groin_side_action_grab = list( + "grope a big soft handful of", + "gropes a big soft handful of", + "groping a big soft handful of", + "groped a big soft handful of", + ) + var/list/groin_side_action_harm = list( + "pinch a big fat roll of", + "pinches a big fat roll of", + "pinching a big fat roll of", + "pinched a big fat roll of", + ) + var/list/groin_side_action_help_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/groin_side_action_disarm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/groin_side_action_grab_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/groin_side_action_harm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + + + + var/list/groin_back = list("butt") // the butt, lots of fun here with big fat soft squishy cheeks + var/list/groin_back_action_help = list( + "press hand against", + "presses hand against", + "pressing hand against", + "pressed hand against", + ) + var/list/groin_back_action_disarm = list( + "squish fingers into", + "squishes fingers into", + "squishing fingers into", + "squished fingers into", + ) + var/list/groin_back_action_grab = list( + "grope a big fat handful of", + "gropes a big fat handful of", + "groping a big fat handful of", + "groped a big fat handful of", + ) + var/list/groin_back_action_harm = list( + "give a good hard smack to", + "gives a good hard smack to", + "giving a good hard smack to", + "give a good hard smack to", + ) + var/list/groin_back_action_help_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/groin_back_action_disarm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/groin_back_action_grab_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + var/list/groin_back_action_harm_sound = list('sound/AAAAAAAAAAAAAAAAAAA.ogg') + + + var/list/l_hand_front = list("left hand") + + + var/list/l_hand_side = list("left wrist") + + + var/list/l_hand_back = list("left forearm") + + + var/list/r_hand_front = list("right hand") + + + var/list/r_hand_side = list("right wrist") + + + var/list/r_hand_back = list("right forearm") + + + var/list/l_foot_front = list("left foot") + + + var/list/l_foot_side = list("left ankle") + + + var/list/l_foot_back = list("left calf") + + + var/list/r_foot_front = list("right foot") + + + var/list/r_foot_side = list("right ankle") + + + var/list/r_foot_back = list("right calf") + + + +/datum/grope_kiss_MERP/proc/make_visible_message(mob/doer, mob/target, list/last_action = list()) + if(!doer || !target) + return + /// first get the direction of doer compared to target + var/direc = calc_dir(doer, target) + /// then, get the right location + var/intent = get_grope_intent(doer, target) + var/part = get_grope_list(part, direc, intent, FALSE, TRUE) + var/list/actions = get_grope_list(part, direc, intent) + var/tense = "present" + if(LAZYLEN(last_action)) + if(LAZYACCESS(last_action, 1) == direc) + if(LAZYACCESS(last_action, 2) == part) + if(LAZYACCESS(last_action, 3) == intent) + tense = "gerundo" + var/list/sounds = get_grope_list(part, direc, intent, TRUE) + var/snd = get_sound(doer, target, sounds) + var/watDsee = make_message_for_doer(doer, target, actions) + var/watTsee = make_message_for_target(doer, target, actions) + var/watEsee = make_message_for_everyone(doer, target, actions) + to_chat(doer, span_love(watDsee)) + to_chat(target, span_love(watTsee)) + doer.visible_message( + watEsee, + ignored_mobs = list(doer, target), + visible_distance = 2, + ) + return list(direc, part, intent) + +/datum/grope_kiss_MERP/proc/get_sound(mob/doer, mob/target, list/sounds) + if(LAZYLEN(sounds) != 4) + return + switch(doer.a_intent) + if(INTENT_HELP) + return LAZYACCESS(sounds, 1) + if(INTENT_DISARM) + return LAZYACCESS(sounds, 2) + if(INTENT_GRAB) + return LAZYACCESS(sounds, 3) + if(INTENT_HARM) + return LAZYACCESS(sounds, 4) + +/datum/grope_kiss_MERP/proc/make_message_for_doer(mob/doer, mob/target, list/actions, tense) + if( LAZYLEN(actions) != 4) + return + var/msg_out = "" + /// you slap Tia's cheek! + if(tense == "present") + var/act = actions[1] + msg_out = "I [act] [target.name]'s [part]!" + /// you continue slapping Tia's cheek! + if(tense == "gerundo") + var/act = actions[2] + msg_out = "I continue [act] [target.name]'s [part]!" + msg_out = replace(msg_out, "", "my") + msg_out = replace(msg_out, "", "[target.p_their()]") + return msg_out + +/datum/grope_kiss_MERP/proc/make_message_for_target(mob/doer, mob/target, list/actions, tense) + if( LAZYLEN(actions) != 4) + return + var/msg_out = "" + /// Tia slaps my cheek! + if(tense == "present") + var/act = actions[1] + msg_out = "[doer.name] [act] my [part]!" + var/act = replace(act, "", "[doer.p_their()]") + /// Tia continues slapping my cheek! + if(tense == "gerundo") + var/act = actions[2] + var/act = replace(act, "", "[doer.p_their()]") + msg_out = "[doer.name] continues [act] my [part]!" + msg_out = replace(msg_out, "", "[doer.p_their()]") + msg_out = replace(msg_out, "", "my") + return msg_out + +/datum/grope_kiss_MERP/proc/make_message_for_everyone(mob/doer, mob/target, list/actions, tense) + if( LAZYLEN(actions) != 4) + return + var/msg_out = "" + /// Dan slapped Tia's cheek! + if(tense == "present") + var/act = actions[1] + msg_out = "[doer.name] [act] [target.name]'s [part]!" + /// Dan continues to slap Tia's cheek! + if(tense == "gerundo") + var/act = actions[2] + msg_out = "[doer.name] continues to [act] [target.name]'s [part]!" + msg_out = replace(msg_out, "", "[doer.p_their()]") + msg_out = replace(msg_out, "", "[target.p_their()]") + return msg_out + +/datum/grope_kiss_MERP/proc/get_grope_target(mob/doer, mob/target, direc) + var/here = doer.zone_selected + var/port = vars["[here]_[direc]"] + return port + +/datum/grope_kiss_MERP/proc/get_grope_intent(mob/doer, mob/target) + switch(doer.a_intent) + if(INTENT_HELP) + return "help" + if(INTENT_DISARM) + return "disarm" + if(INTENT_GRAB) + return "grab" + if(INTENT_HARM) + return "harm" + +/datum/grope_kiss_MERP/proc/get_grope_list(partname, sidename, intentname, soundpls, just_part) + var/varname = "[partname]" + "_" + "[sidename]" + if(!just_part) + varname += "_action_" + "[intentname]" + if(soundpls) + varname += "_sound" + var/list/ret = list() + if(vars[varname]) + ret |= vars[varname] // datums re just massive lists that are easier to read + return ret + +// returns if the doer is in front of, behind, or to a side of the target +// The three tiles in front of the target are considered the front, the one tile behind the target is considered the back +// The two tiles to the left and right of the target are considered the sides +/datum/grope_kiss_MERP/proc/calc_dir(mob/doer, mob/target) + var/dirTarg = target.dir + var/turf/D = get_turf(doer) + var/turf/T = get_turf(target) + if(D == T) + // simple relative direction check + var/dirDoer = doer.dir + if(dirDoer == dirTarg) + return "front" + for(var/turnz in 1 to 3) // SPEEN + var/turnt = turn(dirDoer, 90*turnz) + if(turnz == 1 || turnz == 3) + if(turnt == dirTarg) + return "side" + else + if(turnt == dirTarg) + return "back" + return "front" + /// now, check a special ring of turfs around the target + var/list/turfz = list() + turfz.len = 8 + /// front + turfz[1] += get_step(T, dirTarg) + turfz[2] += get_step(T, turn(dirTarg, 45)) + turfz[3] += get_step(T, turn(dirTarg, -45)) + /// sides + turfz[4] += get_step(T, turn(dirTarg, 90)) + turfz[5] += get_step(T, turn(dirTarg, -90)) + turfz[6] += get_step(T, turn(dirTarg, 135)) + turfz[7] += get_step(T, turn(dirTarg, -135)) + /// back + turfz[8] += get_step(T, turn(dirTarg, 180)) + for(var/i in 1 to 8) + if(D == turfz[i]) + switch(i) + if(1, 2, 3) + return "front" + if(4, 5, 6, 7) + return "side" + if(8) + return "back" + return "front" + +/// a complex algorithm to check which triangular quadrant of the target the doer clicked + + + + + + + + + + + + + + + + + + + diff --git a/code/game/say.dm b/code/game/say.dm index 2be63402b4..736b24581c 100644 --- a/code/game/say.dm +++ b/code/game/say.dm @@ -48,7 +48,7 @@ And the base of the send_speech() proc, which is the core of saycode. ) // 9 ARGS! 15 BUTTS!!!! SECRET THIRD LEG!!!!!!!! if(!source) source = speaker - var/datum/rental_mommy/momchat = LAZYACCESS(data["momchat"], 1) + var/datum/rental_mommy/chat/momchat = LAZYACCESS(data["mommy"], 1) if(momchat) momchat.original_message = raw_message momchat.message = momchat.original_message @@ -59,6 +59,7 @@ And the base of the send_speech() proc, which is the core of saycode. momchat.source = source momchat.language = message_language momchat.face_name = face_name + data["mommy"] = null //Prevent infinite recursion momchat.data = data.Copy() //This proc uses text() because it is faster than appending strings. Thanks BYOND. //Basic span @@ -66,7 +67,7 @@ And the base of the send_speech() proc, which is the core of saycode. var/spanpart1 = "" if(momchat) momchat.outer_span_class = outer_span - momchat + momchat.outer_span = spanpart1 //Start name span. var/spanpart2 = "" if(momchat) @@ -82,7 +83,8 @@ And the base of the send_speech() proc, which is the core of saycode. var/mob/living/carbon/human/H = speaker namepart = "[H.get_face_name()]" //So "fake" speaking like in hallucinations does not give the speaker away if disguised if(momchat) - momchat.speaker_name = namepart + momchat.speakername = namepart + momchat.original_speakername = namepart //End name span. var/endspanpart = "" @@ -93,6 +95,8 @@ And the base of the send_speech() proc, which is the core of saycode. var/datum/language/D = GLOB.language_datum_instances[message_language] if(istype(D) && D.display_icon(src)) languageicon = "[D.get_icon()] " + if(momchat) + momchat.language_icon = languageicon return "[spanpart1][spanpart2][freqpart][languageicon][compose_track_href(speaker, namepart)][namepart][compose_job(speaker, message_language, raw_message, radio_freq)][endspanpart][messagepart]" @@ -121,16 +125,15 @@ And the base of the send_speech() proc, which is the core of saycode. . = verb_say return get_random_if_list(.) -/atom/movable/proc/say_quote(input, list/spans=list(speech_span), message_mode, datum/rental_mommy/momchat) +/atom/movable/proc/say_quote(input, list/spans=list(speech_span), message_mode, datum/rental_mommy/chat/momchat) if(!input) input = "..." if(copytext_char(input, -2) == "!!") spans |= SPAN_YELL + if(momchat) + momchat.spans |= SPAN_YELL - var/reformatted = SSchat.emoticonify(src, input, message_mode, spans, momchat) - if(reformatted) - return reformatted var/spanned = attach_spans(input, spans) if(momchat) momchat.message_langtreated_spanned = spanned @@ -165,14 +168,15 @@ And the base of the send_speech() proc, which is the core of saycode. return /// Quirky citadel proc for our custom sayverbs to strip the verb out. Snowflakey as hell, say rewrite 3.0 when? -/atom/movable/proc/quoteless_say_quote(input, list/spans = list(speech_span), message_mode, list/bundle) +/atom/movable/proc/quoteless_say_quote(input, list/spans = list(speech_span), message_mode, datum/rental_mommy/chat/momchat) if((input[1] == "!") && (length_char(input) > 1)) + if(momchat) + momchat.message_langtreated_quoteless = input return "" - var/emoticontext = SSchat.emoticonify(src, input, message_mode, spans, bundle) - if(emoticontext) - return emoticontext var/pos = findtext(input, "*") var/message = pos? copytext(input, 1, pos - 1) : input + if(momchat) + momchat.message_langtreated_quoteless = message return message /// This proc is used to treat the language of a message. It will either scramble the message or leave it as is. @@ -184,7 +188,7 @@ And the base of the send_speech() proc, which is the core of saycode. list/spans, message_mode, no_quote = FALSE, - datum/rental_mommy/momchat, + datum/rental_mommy/chat/momchat, ) if(has_language(language)) var/atom/movable/AM = speaker.GetSource() @@ -209,18 +213,19 @@ And the base of the send_speech() proc, which is the core of saycode. var/atom/movable/AM = speaker.GetSource() var/datum/language/D = GLOB.language_datum_instances[language] raw_message = D.scramble(raw_message) + var/msg_out if(AM) if(no_quote) - return AM.quoteless_say_quote(raw_message, spans, message_mode, momchat) + msg_out = AM.quoteless_say_quote(raw_message, spans, message_mode, momchat) else - return AM.say_quote(raw_message, spans, message_mode, momchat) + msg_out = AM.say_quote(raw_message, spans, message_mode, momchat) if(momchat) momchat.message_langtreated_with_verb = msg_out else if(no_quote) - return speaker.quoteless_say_quote(raw_message, spans, message_mode, momchat) + msg_out = speaker.quoteless_say_quote(raw_message, spans, message_mode, momchat) else - return speaker.say_quote(raw_message, spans, message_mode, momchat) + msg_out = speaker.say_quote(raw_message, spans, message_mode, momchat) if(momchat) momchat.message_langtreated_with_verb = msg_out else diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 0974394f1e..c4b0a03915 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -105,6 +105,7 @@ GLOBAL_PROTECT(admin_verbs_admin) /datum/admins/proc/admin_who, /datum/admins/proc/admin_who2, /datum/admins/proc/test_dailies, + /datum/admins/proc/make_cool_payload, ) GLOBAL_LIST_INIT(admin_verbs_ban, list(/client/proc/unban_panel, /client/proc/DB_ban_panel, /client/proc/stickybanpanel)) GLOBAL_PROTECT(admin_verbs_ban) @@ -1297,3 +1298,31 @@ GLOBAL_PROTECT(admin_verbs_hideable) [span_yellowteamradio("span_yellowteamradio")]\n\ ") +/datum/admins/proc/make_cool_payload() + set category = "Debug" + set name = "CoolText Pro" + set desc = "Opens a big textbox that you can type things in to send to yourself. Used for testing fancy chat message stuff." + + if(!check_rights(R_ADMIN)) + message_admins("[ADMIN_TPMONTY(usr)] tried to use mess with make_cool_payload() without admin perms.") + log_admin("INVALID ADMIN PROC ACCESS: [key_name(usr)] tried to use mess with make_cool_payload() without admin perms.") + return + GLOB.cooltext_pro.Open(usr) + +GLOBAL_DATUM_INIT(cooltext_pro, /datum/shrimpletext, new) + +/datum/shrimpletext + var/list/messages_by_ckey = list() + +/datum/shrimpletext/proc/Open(someone) + var/client/who = extract_mob(someone) + var/stored_text = messages_by_ckey[who.ckey] + var/text2send = input( + who, + "Enter the text you want to send to yourself. This is a big textbox, so you can type a lot of stuff.", + "CoolText Pro", + stored_text + ) as null|message + if(text2send) + messages_by_ckey[who.ckey] = text2send + to_chat(who, text2send) diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 680861628a..465142ef8b 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -150,6 +150,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/chat_on_map = TRUE var/max_chat_length = CHAT_MESSAGE_MAX_LENGTH var/see_chat_non_mob = TRUE + var/see_furry_dating_sim = TRUE ///Whether emotes will be displayed on runechat. Requires chat_on_map to have effect. Boolean. var/see_rc_emotes = TRUE ///Whether to apply mobs' runechat color to the chat log as well @@ -157,6 +158,24 @@ GLOBAL_LIST_EMPTY(preferences_datums) ///Keeping track of chat bg color var/chatbgcolor = "#131313" + var/list/mommychat_settings = list( + "top_gradient_color_1" = "333333", + "top_gradient_color_2" = "111111", + "top_gradient_angle" = 0, + "bottom_gradient_color_1" = "333333", + "bottom_gradient_color_2" = "111111", + "bottom_gradient_angle" = 0, + "button_background_color_1" = "FFFFFF", + "button_background_color_2" = "FFFFFF", + "button_background_angle" = 0, + "outer_border_color" = "000000", + "top_border_color" = "000000", + "bottom_border_color" = "000000", + "image_border_color" = "000000", + "image_border_size" = 0, + "image_border_style" = "solid", + ) + var/list/aghost_squelches = list() /// Custom Keybindings diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index 94af82a534..35b106f53e 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -275,6 +275,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car S["no_tetris_storage"] >> no_tetris_storage S["aghost_squelches"] >> aghost_squelches S["genital_whitelist"] >> genital_whitelist + S["see_furry_dating_sim"] >> see_furry_dating_sim S["lockouts"] >> lockouts // my bans! S["admin_wire_tap"] >> admin_wire_tap // my bans! @@ -341,6 +342,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car modless_key_bindings = sanitize_islist(modless_key_bindings, list()) aghost_squelches = sanitize_islist(aghost_squelches, list()) admin_wire_tap = sanitize_integer(admin_wire_tap, TRUE) + see_furry_dating_sim = sanitize_integer(see_furry_dating_sim, TRUE) verify_keybindings_valid() // one of these days this will runtime and you'll be glad that i put it in a different proc so no one gets their saves wiped @@ -444,6 +446,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car WRITE_FILE(S["pda_ringmessage"], pda_ringmessage) WRITE_FILE(S["key_bindings"], key_bindings) WRITE_FILE(S["modless_key_bindings"], modless_key_bindings) + WRITE_FILE(S["see_furry_dating_sim"], see_furry_dating_sim) //citadel code WRITE_FILE(S["screenshake"], screenshake) diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index 6733c6e5a9..64efbb1773 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -20,7 +20,7 @@ return new_msg -/mob/living/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, just_chat) +/mob/living/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, just_chat, mob/direct_to_mob = null) /* var/static/list/crit_allowed_modes = list( MODE_WHISPER = TRUE, MODE_CUSTOM_SAY = TRUE, @@ -33,7 +33,7 @@ var/static/list/unconscious_allowed_modes = list(MODE_CHANGELING = TRUE, MODE_ALIEN = TRUE) var/talk_key = get_key(message) - var/static/list/one_character_prefix = list(MODE_HEADSET = TRUE, MODE_ROBOT = TRUE, MODE_WHISPER = TRUE, MODE_SING = TRUE, "$" = TRUE, "#" = TRUE) + var/static/list/one_character_prefix = list(MODE_HEADSET = TRUE, MODE_ROBOT = TRUE, MODE_WHISPER = TRUE, MODE_SING = TRUE, MODE_YELL = TRUE, MODE_WHISPER = TRUE) var/ic_blocked = FALSE @@ -166,14 +166,12 @@ if(pressure < ONE_ATMOSPHERE*0.4) //Thin air, let's italicise the message spans |= SPAN_ITALICS */ - send_speech(message, message_range, src, bubble_type, spans, language, message_mode, just_chat) + return send_speech(message, message_range, src, bubble_type, spans, language, message_mode, just_chat, direct_to_mob) /* if(succumbed) succumb() to_chat(src, compose_message(src, language, message, null, spans, message_mode)) */ - return 1 - /mob/living/compose_message(atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode, face_name = FALSE, atom/movable/source, list/data = list()) . = ..() // if(isliving(speaker)) @@ -182,10 +180,22 @@ // if(sourceturf && T && !(sourceturf in get_hear(5, T))) // . = span_small("[.]") -/mob/living/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source, just_chat = FALSE, list/data = list()) +/mob/living/Hear( + message, + atom/movable/speaker, + datum/language/message_language, + raw_message, + radio_freq, + list/spans, + message_mode, + atom/movable/source, + just_chat = FALSE, + list/data = list(), +) // TEN ARGUMENTS !! FIVE BUTTS! SECRET THIRD LEG! SEND_SIGNAL(src, COMSIG_MOVABLE_HEAR, args) //parent calls can't overwrite the current proc args. if(!client) return + var/datum/rental_mommy/chat/momchat = data["mommy"] var/deaf_message var/deaf_type if(speaker != src) @@ -195,27 +205,50 @@ else deaf_message = span_notice("I can't hear yourself!") deaf_type = 2 // Since you should be able to hear yourself without looking - + // Create map text prior to modifying message for goonchat - if (client?.prefs?.chat_on_map && stat != UNCONSCIOUS && (client.prefs.see_chat_non_mob || ismob(speaker)) && can_hear()) - data["message_mode"] = message_mode - // make a second one, for in case we go from not seeing them to seeing them - if(data["is_eaves"] || data["is_far"] || data["display_turf"]) - var/list/cooldata = data.Copy() - cooldata["is_eaves"] = FALSE - cooldata["is_far"] = FALSE - cooldata["display_turf"] = null - create_chat_message(speaker, message_language, raw_message, spans, NONE, cooldata) - create_chat_message(speaker, message_language, raw_message, spans, NONE, data) + if (!isdummy(src) && client?.prefs?.chat_on_map && stat != UNCONSCIOUS && (client.prefs.see_chat_non_mob || ismob(speaker)) && can_hear()) + if(momchat) + if(momchat.runechat_mode == "hidden_pathable") + /// make one that's just normal, to display at the real source + var/datum/rental_mommy/chat/mom3 = SSrentaldatums.CheckoutMommy("chat_datums") + mom3.copy_mommy(momchat) + mom3.runechat_mode = "visible_close" + mom3.display_turf = null + mom3.is_eavesdropping = FALSE + create_chat_message(speaker, message_language, raw_message, spans, NONE, null, mom3) + create_chat_message(speaker, message_language, raw_message, spans, NONE, null, momchat) + else + data["message_mode"] = message_mode + // make a second one, for in case we go from not seeing them to seeing them + if(data["is_eaves"] || data["is_far"] || data["display_turf"]) + var/list/cooldata = data.Copy() + cooldata["is_eaves"] = FALSE + cooldata["is_far"] = FALSE + cooldata["display_turf"] = null + create_chat_message(speaker, message_language, raw_message, spans, NONE, cooldata) + create_chat_message(speaker, message_language, raw_message, spans, NONE, data) if(just_chat) return // Recompose message for AI hrefs, language incomprehension. - message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode, FALSE, source) + message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode, FALSE, source, data) + var/client/C = client + if(isdummy(src)) + var/mob/living/carbon/human/dummy/D = src + C = extract_client(D.dummyckey) if(client.prefs.color_chat_log) - var/sanitizedsaycolor = client.sanitize_chat_color(speaker.get_chat_color()) - message = color_for_chatlog(message, sanitizedsaycolor, speaker.name) - show_message(message, MSG_AUDIBLE, deaf_message, deaf_type) + var/base_chat_color = speaker.get_chat_color() + var/sanitizedsaycolor = client.sanitize_chat_color(base_chat_color) + if(momchat) + momchat.chat_color_base = base_chat_color + momchat.chat_color_sanitized = sanitizedsaycolor + message = color_for_chatlog(message, sanitizedsaycolor, speaker.name, momchat) + if(momchat && should_hornify()) + momchat.furry_dating_sim = TRUE + if(isdummy(src) && momchat) + return momchat + show_message(message, MSG_AUDIBLE, deaf_message, deaf_type, null, momchat, force) if(islist(data) && LAZYACCESS(data, "is_radio") && (data["ckey"] in GLOB.directory) && !SSchat.debug_block_radio_blurbles) if(CHECK_PREFS(src, RADIOPREF_HEAR_RADIO_STATIC)) playsound(src, RADIO_STATIC_SOUND, 20, FALSE, SOUND_DISTANCE(2), ignore_walls = TRUE) @@ -233,7 +266,8 @@ list/spans, datum/language/message_language=null, message_mode, - just_chat + just_chat, + mob/direct_to_mob = null, ) var/max_range = 15 var/static/list/eavesdropping_modes = list(MODE_WHISPER = TRUE, MODE_WHISPER_CRIT = TRUE) @@ -265,40 +299,51 @@ // var/list/hidden_inaccessible = CC.hidden_inaccessible.Copy() CC.putback() - var/list/the_dead = list() // var/list/yellareas //CIT CHANGE - adds the ability for yelling to penetrate walls and echo throughout areas // if(!eavesdrop_range && say_test(message) == "2") //CIT CHANGE - ditto // yellareas = get_areas_in_range(message_range*0.5, source) //CIT CHANGE - ditto - for(var/_M in GLOB.player_list) - var/mob/M = _M - if(QDELETED(M)) //Some times nulls and deleteds stay in this list. This is a workaround to prevent ic chat breaking for everyone when they do. - continue - if(M.stat != DEAD) //not dead, not important - continue - if(!M.client || !client) //client is so that ghosts don't have to listen to mice - continue - if(get_dist(M, source) > 7 || M.z != z) //they're out of range of normal hearing - if(eavesdropping_modes[message_mode] && !(M.client?.prefs.chat_toggles & CHAT_GHOSTWHISPER)) //they're whispering and we have hearing whispers at any range off + if(!direct_to_mob) + for(var/_M in GLOB.player_list) + var/mob/M = _M + if(QDELETED(M)) //Some times nulls and deleteds stay in this list. This is a workaround to prevent ic chat breaking for everyone when they do. + continue + if(M.stat != DEAD) //not dead, not important continue - if(!(M.client?.prefs.chat_toggles & CHAT_GHOSTEARS)) //they're talking normally and we have hearing at any range off + if(!M.client || !client) //client is so that ghosts don't have to listen to mice continue - CC.visible_close[M] = TRUE - the_dead[M] = TRUE + if(get_dist(M, source) > 7 || M.z != z) //they're out of range of normal hearing + if(eavesdropping_modes[message_mode] && !(M.client?.prefs.chat_toggles & CHAT_GHOSTWHISPER)) //they're whispering and we have hearing whispers at any range off + continue + if(!(M.client?.prefs.chat_toggles & CHAT_GHOSTEARS)) //they're talking normally and we have hearing at any range off + continue + CC.visible_close[M] = TRUE + the_dead[M] = TRUE - var/eavesdropping - var/eavesrendered - if(!quietness) - eavesdropping = dots(message) - eavesrendered = compose_message(src, message_language, eavesdropping, null, spans, message_mode, FALSE, source) - - var/datum/rental_mommy/momchat = SSrentaldatums.CheckoutMommy("chat_datums") + var/datum/rental_mommy/chat/momchat = SSrentaldatums.CheckoutMommy("chat_datums") + momchat.original_message = message + momchat.message = message + momchat.source = source + momchat.message_mode = message_mode + momchat.spans = spans + momchat.sanitize = TRUE + momchat.bubble_type = bubble_type + momchat.language = message_language + momchat.only_overhead = just_chat + momchat.source_quid = extract_quid(src) + momchat.source_ckey = ckey + if(direct_to_mob) + momchat.recipiant = direct_to_mob var/list/rental_data = list("mommy" = momchat) // mommy is very disappointed var/rendered = compose_message(src, message_language, message, null, spans, message_mode, FALSE, source, rental_data) + if(momchat.recipiant) + return momchat.recipiant.Hear(rendered, src, message_language, message, null, spans, message_mode, source, just_chat, list("mommy" = momchat)) /// non-players for(var/_AM in listening) var/atom/movable/AM = _AM - if(!quietness && get_dist(source, AM) > message_range && !(the_dead[AM])) + if(quietness && get_dist(source, AM) > message_range && !(the_dead[AM])) + var/eavesdropping = dots(message) + var/eavesrendered = compose_message(src, message_language, eavesdropping, null, spans, message_mode, FALSE, source, rental_data) AM.Hear(eavesrendered, src, message_language, eavesdropping, null, spans, message_mode, source, just_chat) else AM.Hear(rendered, src, message_language, message, null, spans, message_mode, source, just_chat) @@ -306,10 +351,14 @@ var/list/sblistening = list() /// players for(var/mob/mvc in visible_close) - var/list/data = list() - data["horny_stuff"] = composition_list - mvc.Hear(rendered, src, message_language, message, null, spans, message_mode, source, just_chat, data) + var/datum/rental_mommy/chat/mom2 = SSrentaldatums.CheckoutMommy("chat_datums") + mom2.copy_mommy(momchat) + mom2.runechat_mode = "visible_close" + mom2.recipiant = mvc + mvc.Hear(rendered, src, message_language, message, null, spans, message_mode, source, just_chat, list("mommy" = mom2)) sblistening |= mvc.client + if(!mom2.available) + mom2.checkin() // for(var/mob/mvf in visible_far) // var/list/coolspans = spans // coolspans += SPAN_SMALL @@ -320,12 +369,17 @@ // sblistening |= mvf.client for(var/mob/mhp in hidden_pathable) var/turf/hearfrom = hidden_pathable[mhp] - var/list/cooler_spans = spans - cooler_spans += SPAN_SMALL - var/list/data = list() - data["is_eaves"] = TRUE - data["display_turf"] = hearfrom - mhp.Hear(eavesrendered, src, message_language, eavesdropping, null, cooler_spans, message_mode, source, just_chat, data) + var/datum/rental_mommy/chat/mom3 = SSrentaldatums.CheckoutMommy("chat_datums") + mom3.copy_mommy(momchat) + mom3.spans |= SPAN_SMALL + mom3.is_eavesdropping = TRUE + mom3.display_turf = hearfrom + mom3.recipiant = mhp + var/msg_dotted = dots(message, distance = get_dist(get_turf(src), get_turf(mhp)), maxdistance = max_range) + var/msg_rerendered = compose_message(src, message_language, msg_dotted, null, spans, message_mode, FALSE, source, list("mommy" = mom3)) + mom3.message = msg_rerendered + mom3.runechat_mode = "hidden_pathable" + mhp.Hear(msg_rerendered, src, message_language, msg_rerendered, null, spans, message_mode, source, just_chat, list("mommy" = mom3)) sblistening |= mhp.client // for(var/mob/mhp in hidden_inaccessible) // var/turf/hearfrom = hidden_inaccessible[mhp] @@ -525,3 +579,20 @@ if(get_minds && mind) return mind.get_language_holder() . = ..() + +/mob/living/proc/should_hornify(datum/rental_mommy/chat/mommy) + if(!mommy) + return FALSE + if(!ishuman(mommy.source)) + return FALSE + var/mob/living/carbon/human/H = mommy.source + if(!H.client) + return FALSE + if(H.client.prefs.see_furry_dating_sim) + return FALSE + if(z != H.z) + return FALSE + if(get_dist(H, src) > 1) //If they're not right next to you, don't hornify them + return FALSE + return TRUE // lets get yiffy + diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index ca8c7f0f57..9a8b0efaae 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -86,39 +86,43 @@ /mob/proc/get_photo_description(obj/item/camera/camera) return "a ... thing?" -/mob/proc/show_message(msg, type, alt_msg, alt_type, pref_check)//Message, type of message (1 or 2), alternative message, alt message type (1 or 2) - if(audiovisual_redirect) - audiovisual_redirect.show_message(msg ? "[msg]" : null, type, alt_msg ? "[alt_msg]" : null, alt_type) +/mob/proc/show_message(msg, type, alt_msg, alt_type, pref_check, datum/rental_mommy/chat/momchat, force)//Message, type of message (1 or 2), alternative message, alt message type (1 or 2) + if(!force) + if(audiovisual_redirect) + audiovisual_redirect.show_message(msg ? "[msg]" : null, type, alt_msg ? "[alt_msg]" : null, alt_type) - if(!client) - return - - if(pref_check && !CHECK_PREFS(src, pref_check)) - return + if(!client) + return + + if(pref_check && !CHECK_PREFS(src, pref_check)) + return - msg = copytext_char(msg, 1, MAX_MESSAGE_LEN) + msg = copytext_char(msg, 1, MAX_MESSAGE_LEN) - if(type) - if(type & MSG_VISUAL && eye_blind )//Vision related - if(!alt_msg) - return - else - msg = alt_msg - type = alt_type + if(type) + if(type & MSG_VISUAL && eye_blind )//Vision related + if(!alt_msg) + return + else + msg = alt_msg + type = alt_type - if(type & MSG_AUDIBLE && !can_hear())//Hearing related - if(!alt_msg) - return - else - msg = alt_msg - type = alt_type - if(type & MSG_VISUAL && eye_blind) + if(type & MSG_AUDIBLE && !can_hear())//Hearing related + if(!alt_msg) return - // voice muffling - if(stat == UNCONSCIOUS) - if(type & MSG_AUDIBLE) //audio - to_chat(src, "... You can almost hear something ...") - return + else + msg = alt_msg + type = alt_type + if(type & MSG_VISUAL && eye_blind) + return + // voice muffling + if(stat == UNCONSCIOUS) + if(type & MSG_AUDIBLE) //audio + to_chat(src, "... You can almost hear something ...") + return + ///NOW HOLD ON THERE BUCKO, I think you're forgetting something~ + if(momchat && momchat.furry_dating_sim && (isdummy(momchat.source) || CHECK_PREFS(src, SHOW_ME_HORNY_FURRIES))) // its right here + msg = SSchat.BuildHornyFurryDatingSimMessage(src, momchat) // in my subsystem~ to_chat(src, msg) /** diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index e60ca3be80..e6b6ba0045 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -52,6 +52,7 @@ * * probability - probability any character gets changed * * This proc is dangerously laggy, avoid it or die + * no its not lol */ /proc/stars(phrase, probability = 25) if(probability <= 0) @@ -76,9 +77,15 @@ * * This proc is not laggy at all, and is better in every way =3 */ -/proc/dots(phrase, probability = 25) +/proc/dots(phrase, probability = 25, distance, maxdistance) if(probability <= 0) return phrase + if(distance && maxdistance) + /// throw out probability and calculate a new one based on how far away the message is + /// from the source of the message (distance) and the maximum distance the message can be + /// heard from (maxdistance) + probability = 100 - (distance / maxdistance) * 100 + probability = clamp(probability, 0, 90) phrase = html_decode(phrase) var/list/words = splittext(phrase, " ") . = "" diff --git a/fortune13.dme b/fortune13.dme index 30adc683ce..4b01afd06c 100644 --- a/fortune13.dme +++ b/fortune13.dme @@ -343,6 +343,7 @@ #include "code\controllers\subsystem\communications.dm" #include "code\controllers\subsystem\COOLBOOKss.dm" #include "code\controllers\subsystem\damage.dm" +#include "code\controllers\subsystem\datumrentals.dm" #include "code\controllers\subsystem\dbcore.dm" #include "code\controllers\subsystem\dcs.dm" #include "code\controllers\subsystem\disease.dm" diff --git a/modular_coyote/code/modules/examine_images.dm b/modular_coyote/code/modules/examine_images.dm index f46496c90e..28da644bfa 100644 --- a/modular_coyote/code/modules/examine_images.dm +++ b/modular_coyote/code/modules/examine_images.dm @@ -145,10 +145,45 @@ GLOBAL_LIST_INIT(pfp_filehost_safe_suffixes, list( client.prefs.save_character() // Preference code + saving! The rest of the code is located in preferences.dm where the UI is. -/datum/preferences +/datum/preferences // gdi jon, we dont have to modularize (also screw your pascal case) var/profilePicture = "" var/pfphost = "" + var/list/profilePics = list( + MODE_SING = list( + "host" = "", + "link" = "", + ), + MODE_SAY = list( + "host" = "", + "link" = "", + ), + MODE_ASK = list( + "host" = "", + "link" = "", + ), + MODE_EXCLAIM = list( + "host" = "", + "link" = "", + ), + MODE_YELL = list( + "host" = "", + "link" = "", + ), + MODE_WHISPER = list( + "host" = "", + "link" = "", + ), + MODE_CUSTOM_SAY = list( + "host" = "", + "link" = "", + ), + ":example:" = list( + "host" = "", + "link" = "", + ), + ) + // Moved this to preferences_savefile.dm as we're having issues with overriding the function I think. // My speculation is that us trying to open the save file multiple times with multiple users is causing a memory overflow on the server end and refusing to open it // Though surely it would alteast warn us?? IDK. There's no way to debug this live. That's atleast what I think is happening, as it's strange that it works locally, but not server side. diff --git a/testdoc.html b/testdoc.html index 90cd84b728..9440dd4553 100644 --- a/testdoc.html +++ b/testdoc.html @@ -1,315 +1,80 @@ - - - - - - - - - -
-
Tittle
-
-
- Character Settings - Character Appearance - Underlying Appearance - Loadout - Game Preferences - Content Preferences - Keybindings - -
- [name] -

- - - Layering -
- - Butt - - - Peen - - - belly - + - - - - ↓ - ↓ - Hidden by... - - - Penis - 1 - ↓ - - - ↓ - - - - - Clothes - - - - - Undies - - - - - Butt - 2 - - - ↑ - - - - - ↓ - - - + + + + Furry Dating Sim Message Block + + + +
+ +
+ +
+ +
+ Foxxxy Vixen +
+ + + + +
+
+
+ +
+

Foxxxy Vixen asks,

+

Hey there! How's it going? I was thinking we could go on a date sometime. What do you say?

+
+ - - Belly - 3 - - - ↑ - - - ↓ - - - When visible, layer... - - - Over Clothing - - - - - - - - - - -
-
-
- - - - From a47d5947fefe31518244dd7aa9c43bc8603180d5 Mon Sep 17 00:00:00 2001 From: Danny Kay Date: Sun, 21 Jul 2024 19:15:21 -0700 Subject: [PATCH 03/19] Refactor emoticonify proc to include momchat parameter --- code/controllers/subsystem/chat.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/controllers/subsystem/chat.dm b/code/controllers/subsystem/chat.dm index ce05d39fff..d29a6ec178 100644 --- a/code/controllers/subsystem/chat.dm +++ b/code/controllers/subsystem/chat.dm @@ -84,7 +84,7 @@ SUBSYSTEM_DEF(chat) var/datum/emoticon_bank/E = new(emo, emotilist) emoticon_cache[html_decode(emotie)] = E -/datum/controller/subsystem/chat/proc/emoticonify(atom/movable/sayer, message, messagemode, list/spans) +/datum/controller/subsystem/chat/proc/emoticonify(atom/movable/sayer, message, messagemode, list/spans, datum/rental_datum/momchat) if(!sayer) return if(istype(sayer, /mob)) From b6a252bc8b9f99c121231bd872507d05e81798df Mon Sep 17 00:00:00 2001 From: Danny Kay Date: Sun, 21 Jul 2024 19:18:53 -0700 Subject: [PATCH 04/19] Refactor IS_IN_VIEWER_RECT macro to correct parameter order --- code/controllers/subsystem/chat.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/controllers/subsystem/chat.dm b/code/controllers/subsystem/chat.dm index d29a6ec178..e40bbbc35d 100644 --- a/code/controllers/subsystem/chat.dm +++ b/code/controllers/subsystem/chat.dm @@ -88,7 +88,7 @@ SUBSYSTEM_DEF(chat) if(!sayer) return if(istype(sayer, /mob)) - var/mob/they = sayer + var/mob/they = sayer if(!they.client) return if(!(messagemode in list(MODE_SAY, MODE_WHISPER, MODE_SING, MODE_ASK, MODE_EXCLAIM, MODE_YELL))) From 381f926c84c4ce2e6c568d2f2303e5083d892a1f Mon Sep 17 00:00:00 2001 From: Superlagg Date: Tue, 23 Jul 2024 10:59:40 -0700 Subject: [PATCH 05/19] Refactor IS_IN_VIEWER_RECT macro to correct parameter order I swear to fuckin shit, copilot --- code/__DEFINES/voreconstants.dm | 1 + code/__HELPERS/text.dm | 2 +- code/controllers/subsystem/chat.dm | 107 +++++++++++++++------ code/controllers/subsystem/datumrentals.dm | 71 ++++++++++++-- code/controllers/subsystem/dummies.dm | 2 + code/controllers/subsystem/prefbreak.dm | 9 ++ code/game/say.dm | 9 +- code/modules/admin/admin_verbs.dm | 7 ++ code/modules/mob/living/say.dm | 19 ++-- code/modules/mob/mob.dm | 2 +- 10 files changed, 175 insertions(+), 54 deletions(-) diff --git a/code/__DEFINES/voreconstants.dm b/code/__DEFINES/voreconstants.dm index 80941031b5..9157e83abe 100644 --- a/code/__DEFINES/voreconstants.dm +++ b/code/__DEFINES/voreconstants.dm @@ -309,3 +309,4 @@ GLOBAL_LIST_INIT(prey_release_sounds, list( #define ADMIN_CHAT_FILTER_DMS "ADMIN_CHAT_FILTER_DMS" #define SEE_FANCY_OFF_SCREEN_RUNECHAT "SEE_FANCY_OFF_SCREEN_RUNECHAT" +#define SHOW_ME_HORNY_FURRIES "SHOW_ME_HORNY_FURRIES" diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index 7dd4a178c7..7ba9e40b54 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -884,4 +884,4 @@ GLOBAL_LIST_INIT(hex_6toc, list("6","7","8","9","a","b","c")) . = out if(momchat) // do it again, but this time with all our dumb vars momchat.namepart = color_keyword(momchat.namepart, color, name) - momchat.message = alternating_color_span(momchat.message, color, "\"", FALSE) + momchat.message = alternating_color_span(momchat.message_langtreated_spanned_quotes, color, "\"", FALSE) diff --git a/code/controllers/subsystem/chat.dm b/code/controllers/subsystem/chat.dm index e40bbbc35d..4b0b8f6791 100644 --- a/code/controllers/subsystem/chat.dm +++ b/code/controllers/subsystem/chat.dm @@ -56,6 +56,41 @@ SUBSYSTEM_DEF(chat) var/flirt_cooldown_time = 5 SECONDS var/debug_character_directory = 0 + var/list/test_pics = list( // warning: boobs + MODE_SAY = list( + "host" = "catbox.moe", + "link" = "4m71t6.jpg", + ), + MODE_ASK = list( + "host" = "catbox.moe", + "link" = "jm3f2c.jpg", + ), + MODE_SING = list( + "host" = "catbox.moe", + "link" = "em5bgb.jpg", + ), + MODE_EXCLAIM = list( + "host" = "catbox.moe", + "link" = "3ggp5e.jpg", + ), + MODE_YELL = list( + "host" = "catbox.moe", + "link" = "1dmlu5.jpg", + ), + MODE_WHISPER = list( + "host" = "catbox.moe", + "link" = "t040sg.jpg", + ), + MODE_CUSTOM_SAY = list( + "host" = "catbox.moe", + "link" = "fcm6yw.jpg", + ), + ":example:" = list( + "host" = "catbox.moe", + "link" = "fcm6yw.jpg", + ), + ) + var/debug_chud = FALSE /datum/controller/subsystem/chat/Initialize(start_timeofday) @@ -70,7 +105,7 @@ SUBSYSTEM_DEF(chat) /datum/controller/subsystem/chat/proc/setup_emoticon_cache() emoticon_cache.Cut() var/json_emoticons = file2text("strings/sausage_rolls.json") // am hungy - /// there was a comment here, but it was fucking enormous and made it hard to read + /// there was a comment here, but it was fukcing enormous and made it hard to read var/list/emoticons = safe_json_decode(json_emoticons) if(!LAZYLEN(emoticons)) return // :( @@ -84,11 +119,11 @@ SUBSYSTEM_DEF(chat) var/datum/emoticon_bank/E = new(emo, emotilist) emoticon_cache[html_decode(emotie)] = E -/datum/controller/subsystem/chat/proc/emoticonify(atom/movable/sayer, message, messagemode, list/spans, datum/rental_datum/momchat) +/datum/controller/subsystem/chat/proc/emoticonify(atom/movable/sayer, message, messagemode, list/spans, datum/rental_mommy/chat/momchat) if(!sayer) return if(istype(sayer, /mob)) - var/mob/they = sayer + var/mob/they = sayer if(!they.client) return if(!(messagemode in list(MODE_SAY, MODE_WHISPER, MODE_SING, MODE_ASK, MODE_EXCLAIM, MODE_YELL))) @@ -144,18 +179,26 @@ SUBSYSTEM_DEF(chat) return LAZYADD(payload_by_client[client], list(message)) +/datum/controller/subsystem/chat/proc/TestHorny() + var/mob/user = usr + to_chat(user, "Testing the horny") + for(var/mmode in test_pics) + PreviewHornyFurryDatingSimMessage(user, mmode) + to_chat(user, "Test complete") + /datum/controller/subsystem/chat/proc/PreviewHornyFurryDatingSimMessage(mob/target, message_mode) if(!istype(target)) - CRASH("PreviewHornyFurryDatingSimMessage called with invalid arguments! [speaker]! [target]!") + CRASH("PreviewHornyFurryDatingSimMessage called with invalid arguments! [target]!") if(!message_mode) CRASH("PreviewHornyFurryDatingSimMessage called with invalid arguments! [message_mode]!") - var/mob/living/carbon/human/dummy/D = SSdummy.get_dummy() + var/mob/living/carbon/human/dummy/D = SSdummy.get_a_dummy() var/datum/preferences/P = extract_prefs(target) if(!P) CRASH("PreviewHornyFurryDatingSimMessage called with invalid arguments! [P]!") P.copy_to(D) D.dummyckey = target.ckey + D.forceMove(get_turf(target)) var/msg = "Hey there! How's it going? I was thinking we could go on a date sometime. I'm a vampire and" switch(message_mode) if(MODE_SAY) @@ -171,7 +214,7 @@ SUBSYSTEM_DEF(chat) if(MODE_YELL) msg = "$[msg]" - var/datum/rental_mommy/chat/mommy = D.say(msg, message_mode) + var/datum/rental_mommy/chat/mommy = D.say(msg, message_mode, direct_to_mob = target) mommy.prefs_override = P var/mommess = BuildHornyFurryDatingSimMessage(mommy, TRUE) mommy.checkin() @@ -180,9 +223,10 @@ SUBSYSTEM_DEF(chat) /datum/controller/subsystem/chat/proc/BuildHornyFurryDatingSimMessage(datum/rental_mommy/chat/mommy) if(!istype(mommy)) - CRASH("BuildHornyFurryDatingSimMessage called with invalid arguments! [target]! [mommy]!") + CRASH("BuildHornyFurryDatingSimMessage called with invalid arguments! [mommy]!") + var/mob/living/target = mommy.recipiant if(!istype(mommy.recipiant)) - CRASH("BuildHornyFurryDatingSimMessage called with invalid arguments! [target]!") + CRASH("BuildHornyFurryDatingSimMessage called with invalid arguments! [target]!!") if(!mommy.source) CRASH("BuildHornyFurryDatingSimMessage called with invalid arguments! [mommy.source]!!!!") var/datum/preferences/P = mommy.prefs_override || extract_prefs(mommy.source) @@ -201,15 +245,15 @@ SUBSYSTEM_DEF(chat) /// - A color for the text background /// - A color for the header background /// and from this, we will make a furry dating sim style message that will be sent to the target *and* the speaker - var/m_name = mommy.namepart + var/m_name = mommy.speakername var/m_verb = mommy.message_saymod_comma var/m_rawmessage = mommy.original_message - var/m_message = mommy.message_langtreated_spanned_quotes - var/m_mode = mommy.message_mode + var/m_message = mommy.message + var/m_mode = mommy.message_mode || MODE_SAY /// look for something in m_rawmessage formatted as :exammple: and extract that to look up a custom image /// We'll extract this, store it as a var, and use it as an override for the profile image - var/list/m_images = P ? P.profilePics.Copy() : test_pics + var/list/m_images = /* P ? P.profilePics.Copy() : */test_pics var/m_pfp = get_horny_pfp(m_rawmessage, m_images, m_mode) @@ -233,29 +277,32 @@ SUBSYSTEM_DEF(chat) var/ibt = "[P.mommychat_settings["image_border_style"]]" // now the text colors // most are defined by mommy, but some arent, so we'll need to get a color that contrasts with the average of the top and bottom gradient colors - var/tgc_to_num = hex2num(tgc_1) + hex2num(tgc_2) - var/bgc_to_num = hex2num(bgc_1) + hex2num(bgc_2) + var/tgc_to_num = hex2num(replacetext(tgc_1,"#", "")) + hex2num(replacetext(tgc_2,"#", "")) + var/bgc_to_num = hex2num(replacetext(bgc_1,"#", "")) + hex2num(replacetext(bgc_2,"#", "")) var/avg_color = (tgc_to_num + bgc_to_num) / 4 /// now we need to get the contrast color var/contrast_color = num2hex(16777215 - avg_color) var/dtc = "#[contrast_color]" + var/senderquid = mommy.source_quid + var/senderckey = mommy.source_ckey + /// Character Directory link - var/m_charlink = {"Profile"} + var/m_charlink = {"Profile"} /// DM link - var/m_dmlink = {"DM"} + var/m_dmlink = {"DM"} /// Flirt link - var/m_flirtlink = {"Flirt"} + var/m_flirtlink = {"Flirt"} /// Interact link - var/m_interactlink = {"Interact"} + var/m_interactlink = {"Interact"} /// now we need to build the message var/list/cum = list() @@ -317,7 +364,7 @@ SUBSYSTEM_DEF(chat) //

Hey there! How's it going? I was thinking we could go on a date sometime. What do you say?

-/datum/mob/proc/get_horny_pfp(m_rawmessage, list/m_images, m_mode) +/datum/controller/subsystem/chat/proc/get_horny_pfp(m_rawmessage, list/m_images, m_mode) var/image2use = "" var/first_colon = findtext(m_rawmessage, ":") if(first_colon) @@ -330,17 +377,17 @@ SUBSYSTEM_DEF(chat) if(imgz["link"] != "" && imgz["host"] != "") image2use = PfpHostLink(imgz["link"], imgz["host"]) /// then extract the message mode and see if they have a corresponting image - if(!SanitizePfpLink(image2use)) + if(!image2use) var/list/testimages = m_images[m_mode] if(testimages["link"] != "" && testimages["host"] != "") image2use = PfpHostLink(testimages["link"], testimages["host"]) // just grab their default one - if(!SanitizePfpLink(image2use)) + if(!image2use) var/list/testimages = m_images[MODE_SAY] if(testimages["link"] != "" && testimages["host"] != "") image2use = PfpHostLink(testimages["link"], testimages["host"]) // if we still dont have one, just use a placeholder - if(!SanitizePfpLink(image2use)) + if(!image2use) image2use = "https://www.placehold.it/100x100.png" return image2use diff --git a/code/controllers/subsystem/datumrentals.dm b/code/controllers/subsystem/datumrentals.dm index 13484d6cd0..250db30c22 100644 --- a/code/controllers/subsystem/datumrentals.dm +++ b/code/controllers/subsystem/datumrentals.dm @@ -32,13 +32,15 @@ SUBSYSTEM_DEF(rentaldatums) . = ..() /datum/controller/subsystem/rentaldatums/proc/init_datums() - // + chat_datums = list() + chat_datums += new /datum/rental_mommy/chat() + /datum/controller/subsystem/rentaldatums/proc/CheckoutMommy(mom) var/list/mymom = LAZYACCESS(vars, mom) if(!mymom) return null - for(var/datum/rental_mommy/chat/mommy in mommies) + for(var/datum/rental_mommy/chat/mommy in mymom) if(mommy.available) mommy.checkout() return mommy @@ -68,10 +70,7 @@ SUBSYSTEM_DEF(rentaldatums) /datum/rental_mommy/proc/copy_mommy(datum/rental_mommy/chat/mommy) if(mommy.type != type) return - for(var/V in vars) - if(V == "vars") - continue - vars[V] = mommy.vars[V] + return TRUE /// Charlotte, chat's rental mommy /// Holds a dynamic glob of chat data that can be easily manipulated and passed around @@ -115,13 +114,12 @@ SUBSYSTEM_DEF(rentaldatums) var/furry_dating_sim var/namepart var/face_name - var/data = list() + var/list/data = list() var/outer_span_class var/outer_span var/name_span_class var/name_span var/freqpart - var/speaker_name var/message_langtreated_spanned var/message_langtreated_spanned_quotes var/message_saymod @@ -129,4 +127,61 @@ SUBSYSTEM_DEF(rentaldatums) var/message_langtreated_quoteless var/message_langtreated_with_verb var/language_icon + var/source_quid + var/source_ckey + var/datum/preferences/prefs_override + +/datum/rental_mommy/chat/copy_mommy(datum/rental_mommy/chat/mommy) + if(!..()) + CRASH("Tried to copy a mommy of a different type") + original_message = mommy.original_message + message = mommy.message + original_speakername = mommy.original_speakername + speakername = mommy.speakername + source = mommy.source + message_mode = mommy.message_mode + message_key = mommy.message_key + spans = mommy.spans + sanitize = mommy.sanitize + bubble_type = mommy.bubble_type + language = mommy.language + language_key = mommy.language_key + saymode = mommy.saymode + ignore_spam = mommy.ignore_spam + forced = mommy.forced + only_overhead = mommy.only_overhead + is_radio = mommy.is_radio + radio_freq = mommy.radio_freq + close_message_range = mommy.close_message_range + far_message_range = mommy.far_message_range + msg_decor_left = mommy.msg_decor_left + msg_decor_right = mommy.msg_decor_right + no_pass = mommy.no_pass + ALL_CAPS = mommy.ALL_CAPS + runechat_mode = mommy.runechat_mode + recipiant = mommy.recipiant + display_turf = mommy.display_turf + is_eavesdropping = mommy.is_eavesdropping + chat_color_base = mommy.chat_color_base + chat_color_sanitized = mommy.chat_color_sanitized + furry_dating_sim = mommy.furry_dating_sim + namepart = mommy.namepart + face_name = mommy.face_name + data = mommy.data + outer_span_class = mommy.outer_span_class + outer_span = mommy.outer_span + name_span_class = mommy.name_span_class + name_span = mommy.name_span + freqpart = mommy.freqpart + message_langtreated_spanned = mommy.message_langtreated_spanned + message_langtreated_spanned_quotes = mommy.message_langtreated_spanned_quotes + message_saymod = mommy.message_saymod + message_saymod_comma = mommy.message_saymod_comma + message_langtreated_quoteless = mommy.message_langtreated_quoteless + message_langtreated_with_verb = mommy.message_langtreated_with_verb + language_icon = mommy.language_icon + source_quid = mommy.source_quid + source_ckey = mommy.source_ckey + prefs_override = mommy.prefs_override + diff --git a/code/controllers/subsystem/dummies.dm b/code/controllers/subsystem/dummies.dm index 413bd00473..40e51fe258 100644 --- a/code/controllers/subsystem/dummies.dm +++ b/code/controllers/subsystem/dummies.dm @@ -340,6 +340,8 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy) dummyckey = null destroy_genitals() cut_overlays() + if(loc) + moveToNullspace() //Inefficient pooling/caching way. // GLOBAL_LIST_EMPTY(human_dummy_list) diff --git a/code/controllers/subsystem/prefbreak.dm b/code/controllers/subsystem/prefbreak.dm index 23cb7e78b8..e5f9c78835 100644 --- a/code/controllers/subsystem/prefbreak.dm +++ b/code/controllers/subsystem/prefbreak.dm @@ -261,6 +261,15 @@ SUBSYSTEM_DEF(prefbreak) // ALL ABOARD THE S.S. PREFBREAK OFF TO **** YOUR ***** // return consumer.see_fancy_offscreen_runechat // kinda vital here +/datum/prefcheck/see_horny_furry_stuff + index = SHOW_ME_HORNY_FURRIES + +/datum/prefcheck/see_horny_furry_stuff/allowed(datum/preferences/consumer) + PREFBROKEN + return TRUE + // return consumer.see_fancy_offscreen_runechat // kinda vital here + + diff --git a/code/game/say.dm b/code/game/say.dm index 736b24581c..74cc5f64e0 100644 --- a/code/game/say.dm +++ b/code/game/say.dm @@ -48,7 +48,7 @@ And the base of the send_speech() proc, which is the core of saycode. ) // 9 ARGS! 15 BUTTS!!!! SECRET THIRD LEG!!!!!!!! if(!source) source = speaker - var/datum/rental_mommy/chat/momchat = LAZYACCESS(data["mommy"], 1) + var/datum/rental_mommy/chat/momchat = LAZYACCESS(data, "mommy") if(momchat) momchat.original_message = raw_message momchat.message = momchat.original_message @@ -89,7 +89,7 @@ And the base of the send_speech() proc, which is the core of saycode. var/endspanpart = "" //Message - var/messagepart = " [lang_treat(speaker, message_language, raw_message, spans, message_mode, momchat)]" + var/messagepart = " [lang_treat(speaker, message_language, raw_message, spans, message_mode, null, momchat)]" var/languageicon = "" var/datum/language/D = GLOB.language_datum_instances[message_language] @@ -142,7 +142,7 @@ And the base of the send_speech() proc, which is the core of saycode. if(momchat) momchat.message_saymod = saymod if(spanned) - momchat.message_saymod_comma = "[saymod] ," + momchat.message_saymod_comma = "[saymod], " return "[momchat][spanned ? ", \"[spanned]\"" : ""]" // Citadel edit [spanned ? ", \"[spanned]\"" : ""]" @@ -209,6 +209,7 @@ And the base of the send_speech() proc, which is the core of saycode. msg_out = speaker.say_quote(raw_message, spans, message_mode, momchat) if(momchat) momchat.message_langtreated_with_verb = msg_out + return msg_out else if(language) var/atom/movable/AM = speaker.GetSource() var/datum/language/D = GLOB.language_datum_instances[language] @@ -221,6 +222,7 @@ And the base of the send_speech() proc, which is the core of saycode. msg_out = AM.say_quote(raw_message, spans, message_mode, momchat) if(momchat) momchat.message_langtreated_with_verb = msg_out + return msg_out else if(no_quote) msg_out = speaker.quoteless_say_quote(raw_message, spans, message_mode, momchat) @@ -228,6 +230,7 @@ And the base of the send_speech() proc, which is the core of saycode. msg_out = speaker.say_quote(raw_message, spans, message_mode, momchat) if(momchat) momchat.message_langtreated_with_verb = msg_out + return msg_out else return "makes a strange sound." diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index c4b0a03915..9240fc1496 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -106,6 +106,7 @@ GLOBAL_PROTECT(admin_verbs_admin) /datum/admins/proc/admin_who2, /datum/admins/proc/test_dailies, /datum/admins/proc/make_cool_payload, + /client/proc/test_horny_furries, ) GLOBAL_LIST_INIT(admin_verbs_ban, list(/client/proc/unban_panel, /client/proc/DB_ban_panel, /client/proc/stickybanpanel)) GLOBAL_PROTECT(admin_verbs_ban) @@ -436,6 +437,12 @@ GLOBAL_PROTECT(admin_verbs_hideable) mob.invisibility = INVISIBILITY_OBSERVER to_chat(mob, "Invisimin on. You are now as invisible as a ghost.") +/client/proc/test_horny_furries() + set name = "hornyfurry" + set category = "Debug" + set desc = "spams you with horny furries" + SSchat.TestHorny() + /client/proc/toggle_experimental_clickdrag_thing() set name = "Toggle Clickdrag Changes" set category = "Debug" diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index 64efbb1773..44bf2b0d0b 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -180,7 +180,7 @@ // if(sourceturf && T && !(sourceturf in get_hear(5, T))) // . = span_small("[.]") -/mob/living/Hear( +/mob/Hear( message, atom/movable/speaker, datum/language/message_language, @@ -207,7 +207,7 @@ deaf_type = 2 // Since you should be able to hear yourself without looking // Create map text prior to modifying message for goonchat - if (!isdummy(src) && client?.prefs?.chat_on_map && stat != UNCONSCIOUS && (client.prefs.see_chat_non_mob || ismob(speaker)) && can_hear()) + if (!isdummy(source) && client?.prefs?.chat_on_map && stat != UNCONSCIOUS && (client.prefs.see_chat_non_mob || ismob(speaker)) && can_hear()) if(momchat) if(momchat.runechat_mode == "hidden_pathable") /// make one that's just normal, to display at the real source @@ -234,21 +234,18 @@ // Recompose message for AI hrefs, language incomprehension. message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode, FALSE, source, data) var/client/C = client - if(isdummy(src)) - var/mob/living/carbon/human/dummy/D = src - C = extract_client(D.dummyckey) - if(client.prefs.color_chat_log) + if(C.prefs.color_chat_log) var/base_chat_color = speaker.get_chat_color() - var/sanitizedsaycolor = client.sanitize_chat_color(base_chat_color) + var/sanitizedsaycolor = C.sanitize_chat_color(base_chat_color) if(momchat) momchat.chat_color_base = base_chat_color momchat.chat_color_sanitized = sanitizedsaycolor message = color_for_chatlog(message, sanitizedsaycolor, speaker.name, momchat) - if(momchat && should_hornify()) + if(momchat && should_hornify(momchat)) momchat.furry_dating_sim = TRUE - if(isdummy(src) && momchat) + if(momchat && isdummy(momchat.source)) return momchat - show_message(message, MSG_AUDIBLE, deaf_message, deaf_type, null, momchat, force) + show_message(message, MSG_AUDIBLE, deaf_message, deaf_type, null, momchat) if(islist(data) && LAZYACCESS(data, "is_radio") && (data["ckey"] in GLOB.directory) && !SSchat.debug_block_radio_blurbles) if(CHECK_PREFS(src, RADIOPREF_HEAR_RADIO_STATIC)) playsound(src, RADIO_STATIC_SOUND, 20, FALSE, SOUND_DISTANCE(2), ignore_walls = TRUE) @@ -580,7 +577,7 @@ return mind.get_language_holder() . = ..() -/mob/living/proc/should_hornify(datum/rental_mommy/chat/mommy) +/mob/proc/should_hornify(datum/rental_mommy/chat/mommy) if(!mommy) return FALSE if(!ishuman(mommy.source)) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 9a8b0efaae..1c6c7702eb 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -122,7 +122,7 @@ return ///NOW HOLD ON THERE BUCKO, I think you're forgetting something~ if(momchat && momchat.furry_dating_sim && (isdummy(momchat.source) || CHECK_PREFS(src, SHOW_ME_HORNY_FURRIES))) // its right here - msg = SSchat.BuildHornyFurryDatingSimMessage(src, momchat) // in my subsystem~ + msg = SSchat.BuildHornyFurryDatingSimMessage(momchat) // in my subsystem~ to_chat(src, msg) /** From c52a351972c1215297bee4c3d2869155d94ae465 Mon Sep 17 00:00:00 2001 From: Superlagg Date: Fri, 26 Jul 2024 13:11:14 -0700 Subject: [PATCH 06/19] Refactor IS_IN_VIEWER_RECT macro to correct parameter order --- code/__HELPERS/text.dm | 2 +- code/controllers/subsystem/chat.dm | 58 +++++++++++++------ code/controllers/subsystem/datumrentals.dm | 51 ++++++++++++++++ code/game/atoms_movable.dm | 4 +- code/game/say.dm | 7 ++- code/modules/admin/admin_verbs.dm | 2 + .../admin/view_variables/modify_variables.dm | 2 +- code/modules/clothing/outfits/vv_outfit.dm | 2 +- code/modules/mob/living/say.dm | 1 + testdoc.html | 48 ++++++++++++--- 10 files changed, 146 insertions(+), 31 deletions(-) diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index 7ba9e40b54..ce92866a3d 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -883,5 +883,5 @@ GLOBAL_LIST_INIT(hex_6toc, list("6","7","8","9","a","b","c")) out = alternating_color_span(out, color, "\"", FALSE) . = out if(momchat) // do it again, but this time with all our dumb vars - momchat.namepart = color_keyword(momchat.namepart, color, name) + momchat.speakername = color_keyword(momchat.speakername, color, name) momchat.message = alternating_color_span(momchat.message_langtreated_spanned_quotes, color, "\"", FALSE) diff --git a/code/controllers/subsystem/chat.dm b/code/controllers/subsystem/chat.dm index 4b0b8f6791..4cdf0802c2 100644 --- a/code/controllers/subsystem/chat.dm +++ b/code/controllers/subsystem/chat.dm @@ -91,6 +91,8 @@ SUBSYSTEM_DEF(chat) ), ) + var/img_size = 75 + var/headspace = 4 var/debug_chud = FALSE /datum/controller/subsystem/chat/Initialize(start_timeofday) @@ -288,42 +290,64 @@ SUBSYSTEM_DEF(chat) var/senderckey = mommy.source_ckey /// Character Directory link - var/m_charlink = {"Profile"} + var/m_charlink = "" /// DM link - var/m_dmlink = {"DM"} + var/m_dmlink = "" /// Flirt link - var/m_flirtlink = {"Flirt"} + var/m_flirtlink = "" /// Interact link - var/m_interactlink = {"Interact"} + var/m_interactlink = "" + + +/* + + + + + */ /// now we need to build the message var/list/cum = list() // First, the full body container cum += "
" + + // first the head cum += "
" // now the profile picture - cum += "
" - cum += "" + cum += "
" + cum += "" cum += "
" // now the rest of the head - cum += "
" + cum += "
" cum += "[m_name]" // already formatted! // now the button panel - cum += "
" // yiff is a joke, dont worry + cum += "
-
Penis
-
-
-
Has one:
- - Yes - - -
Color:
- - Blue - - -
Shape:
- - cool - - -
Length:
- - 20 inch(es) - - -
-
-
Visibility Override:
- - Sorta - - -
Hidden by underwear:
- - Yes - - -
Hidden by clothing:
- - No - - -
-
" + cum += "" + cum += "" + cum += "" + cum += "" + cum += "" + cum += "" + cum += "" + cum += "" + cum += "
" cum += m_charlink + cum += "" cum += m_dmlink + cum += "
" cum += m_flirtlink + cum += "" cum += m_interactlink - cum += "" + cum += "
" cum += "
" cum += "
" // now the body diff --git a/code/controllers/subsystem/datumrentals.dm b/code/controllers/subsystem/datumrentals.dm index 250db30c22..846cabc2a9 100644 --- a/code/controllers/subsystem/datumrentals.dm +++ b/code/controllers/subsystem/datumrentals.dm @@ -184,4 +184,55 @@ SUBSYSTEM_DEF(rentaldatums) source_ckey = mommy.source_ckey prefs_override = mommy.prefs_override +/datum/rental_mommy/chat/wipe() + original_message = "" + message = "" + original_speakername = "" + speakername = "" + source = null + message_mode = null + message_key = null + spans = list() + sanitize = null + bubble_type = null + language = null + language_key = null + saymode = null + ignore_spam = null + forced = null + only_overhead = null + is_radio = null + radio_freq = null + close_message_range = 7 + far_message_range = 15 + msg_decor_left = list() + msg_decor_right = list() + no_pass = null + ALL_CAPS = null + runechat_mode = null + recipiant = null + display_turf = null + is_eavesdropping = null + chat_color_base = null + chat_color_sanitized = null + furry_dating_sim = null + namepart = null + face_name = null + data = list() + outer_span_class = null + outer_span = null + name_span_class = null + name_span = null + freqpart = null + message_langtreated_spanned = null + message_langtreated_spanned_quotes = null + message_saymod = null + message_saymod_comma = null + message_langtreated_quoteless = null + message_langtreated_with_verb = null + language_icon = null + source_quid = null + source_ckey = null + prefs_override = null + diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index d887ec60d2..79a64b2ce6 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -248,8 +248,8 @@ return T.zPassOut(src, direction, destination) && destination.zPassIn(src, direction, T) /atom/movable/vv_edit_var(var_name, var_value) - var/static/list/banned_edits = list("step_x", "step_y", "step_size") - var/static/list/careful_edits = list("bound_x", "bound_y", "bound_width", "bound_height") + var/static/list/banned_edits = list(/* "step_x", "step_y", "step_size" */) + var/static/list/careful_edits = list(/* "bound_x", "bound_y", "bound_width", "bound_height" */) if(var_name in banned_edits) return FALSE //PLEASE no. if((var_name in careful_edits) && (var_value % world.icon_size) != 0) diff --git a/code/game/say.dm b/code/game/say.dm index 74cc5f64e0..20dedff595 100644 --- a/code/game/say.dm +++ b/code/game/say.dm @@ -137,13 +137,14 @@ And the base of the send_speech() proc, which is the core of saycode. var/spanned = attach_spans(input, spans) if(momchat) momchat.message_langtreated_spanned = spanned - momchat.message_langtreated_spanned_quotes = "[spanned]" + momchat.message_langtreated_spanned_quotes = "\"[spanned]\"" var/saymod = say_mod(input, message_mode) if(momchat) momchat.message_saymod = saymod if(spanned) momchat.message_saymod_comma = "[saymod], " - return "[momchat][spanned ? ", \"[spanned]\"" : ""]" + momchat.message = momchat.message_langtreated_spanned_quotes + return "[saymod][spanned ? ", \"[spanned]\"" : ""]" // Citadel edit [spanned ? ", \"[spanned]\"" : ""]" #define ENCODE_HTML_EPHASIS(input, char, html, varname) \ @@ -172,11 +173,13 @@ And the base of the send_speech() proc, which is the core of saycode. if((input[1] == "!") && (length_char(input) > 1)) if(momchat) momchat.message_langtreated_quoteless = input + momchat.message = input return "" var/pos = findtext(input, "*") var/message = pos? copytext(input, 1, pos - 1) : input if(momchat) momchat.message_langtreated_quoteless = message + momchat.message = message return message /// This proc is used to treat the language of a message. It will either scramble the message or leave it as is. diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 9240fc1496..725d87a30f 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -1332,4 +1332,6 @@ GLOBAL_DATUM_INIT(cooltext_pro, /datum/shrimpletext, new) ) as null|message if(text2send) messages_by_ckey[who.ckey] = text2send + text2send = replacetext(text2send, "\n", "") + text2send = replacetext(text2send, " ", "") to_chat(who, text2send) diff --git a/code/modules/admin/view_variables/modify_variables.dm b/code/modules/admin/view_variables/modify_variables.dm index 58267f1318..fdbe0a51f1 100644 --- a/code/modules/admin/view_variables/modify_variables.dm +++ b/code/modules/admin/view_variables/modify_variables.dm @@ -4,7 +4,7 @@ GLOBAL_LIST_INIT(VVicon_edit_lock, list("icon", "icon_state", "overlays", "under GLOBAL_PROTECT(VVicon_edit_lock) GLOBAL_LIST_INIT(VVckey_edit, list("key", "ckey")) //Requires DEBUG or SPAWN GLOBAL_PROTECT(VVckey_edit) -GLOBAL_LIST_INIT(VVpixelmovement, list("bound_x", "bound_y", "step_x", "step_y", "step_size", "bound_height", "bound_width", "bounds")) //No editing ever. +GLOBAL_LIST_INIT(VVpixelmovement, list(/* "bound_x", "bound_y", "step_x", "step_y", "step_size", "bound_height", "bound_width", "bounds" */)) //No editing ever. GLOBAL_PROTECT(VVpixelmovement) /client/proc/vv_parse_text(O, new_var) diff --git a/code/modules/clothing/outfits/vv_outfit.dm b/code/modules/clothing/outfits/vv_outfit.dm index 3d91721357..db99ffb34a 100644 --- a/code/modules/clothing/outfits/vv_outfit.dm +++ b/code/modules/clothing/outfits/vv_outfit.dm @@ -44,7 +44,7 @@ /proc/collect_vv(obj/item/I) //Temporary/Internal stuff, do not copy these. - var/static/list/ignored_vars = list("vars","x","y","z","plane","layer","override","animate_movement","pixel_step_size","screen_loc","fingerprintslast","tip_timer") + var/static/list/ignored_vars = list(/* "vars","x","y","z","plane","layer","override","animate_movement","pixel_step_size","screen_loc","fingerprintslast","tip_timer" */) if(istype(I) && I.datum_flags & DF_VAR_EDITED) var/list/vedits = list() diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index 44bf2b0d0b..cb07723057 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -593,3 +593,4 @@ return FALSE return TRUE // lets get yiffy + diff --git a/testdoc.html b/testdoc.html index 9440dd4553..b6d8d29140 100644 --- a/testdoc.html +++ b/testdoc.html @@ -44,19 +44,16 @@ --> - +
-
-
Foxxxy Vixen
@@ -67,14 +64,51 @@
-

Foxxxy Vixen asks,

Hey there! How's it going? I was thinking we could go on a date sometime. What do you say?

- - + --> + + + + + +
+
+
+ +
+ + +
+ Ward Buttersworth the Cool Bunch Of Sourest Grapes Ever + + + + + + + + + +
+ + + +
+ + + +
+
+
+
+

Ward Buttersworth says,

+

Dup.

+
+
From 0d99123440461a0a1f2fd47cabad04b2d4d92cd4 Mon Sep 17 00:00:00 2001 From: Superlagg Date: Sun, 4 Aug 2024 22:09:13 -0700 Subject: [PATCH 07/19] Co-authored-by: Tk420634 --- code/__DEFINES/mommychat_image_packs.dm | 62 + code/controllers/subsystem/chat.dm | 911 +++++++++++++-- code/modules/admin/admin_verbs.dm | 9 + code/modules/client/preferences.dm | 57 +- code/modules/client/preferences_savefile.dm | 3 + code/modules/mob/living/say.dm | 2 +- modular_coyote/code/modules/examine_images.dm | 34 - tgui/packages/tgui/interfaces/HornyChat.js | 1017 +++++++++++++++++ 8 files changed, 1978 insertions(+), 117 deletions(-) create mode 100644 code/__DEFINES/mommychat_image_packs.dm create mode 100644 tgui/packages/tgui/interfaces/HornyChat.js diff --git a/code/__DEFINES/mommychat_image_packs.dm b/code/__DEFINES/mommychat_image_packs.dm new file mode 100644 index 0000000000..1a93fe1958 --- /dev/null +++ b/code/__DEFINES/mommychat_image_packs.dm @@ -0,0 +1,62 @@ + +/datum/horny_image_pack + var/packname = "Cute Coderbussies" + var/examine_host = "catbox.moe" + var/examine_url = "12345.png" + var/say_host + var/say_url + var/ask_host + var/ask_url + var/exclaim_host + var/exclaim_url + var/yell_host + var/yell_url + var/whisper_host + var/whisper_url + var/sing_host + var/sing_url + +/datum/horny_image_pack/proc/ListifyPack() + var/list/pak = list( + list( + "mode" = MODE_SAY, + "host" = say_host, + "url" = say_url, + ), + list( + "mode" = MODE_ASK, + "host" = ask_host, + "url" = ask_url, + ), + list( + "mode" = MODE_SING, + "host" = sing_host, + "url" = sing_url, + ), + list( + "mode" = MODE_EXCLAIM, + "host" = exclaim_host, + "url" = exclaim_url, + ), + list( + "mode" = MODE_YELL, + "host" = yell_host, + "url" = yell_url, + ), + list( + "mode" = MODE_WHISPER, + "host" = whisper_host, + "url" = whisper_url, + ), + list( + "mode" = ":flirty:", + "host" = "catbox.moe", + "url" = "12345.png", + ), + ) + return pak + + + + + diff --git a/code/controllers/subsystem/chat.dm b/code/controllers/subsystem/chat.dm index 4cdf0802c2..6d50809642 100644 --- a/code/controllers/subsystem/chat.dm +++ b/code/controllers/subsystem/chat.dm @@ -3,6 +3,11 @@ * SPDX-License-Identifier: MIT */ +GLOBAL_LIST_INIT(supported_img_hosts, list( + "https://files.catbox.moe", + "https://i.gyazo.com", +)) + SUBSYSTEM_DEF(chat) name = "Chat" flags = SS_TICKER @@ -41,6 +46,8 @@ SUBSYSTEM_DEF(chat) var/flirt_debug = TRUE var/debug_block_radio_blurbles = FALSE + var/list/horny_tguis = list() + /// Format: list("quid" = /datum/character_inspection) var/list/inspectors = list() @@ -56,53 +63,119 @@ SUBSYSTEM_DEF(chat) var/flirt_cooldown_time = 5 SECONDS var/debug_character_directory = 0 + var/list/stock_image_packs = list() // see: [code\__DEFINES\mommychat_image_packs.dm] + var/list/mandatory_modes = list(MODE_SAY, MODE_WHISPER, MODE_SING, MODE_ASK, MODE_EXCLAIM, MODE_YELL) + var/list/test_pics = list( // warning: boobs MODE_SAY = list( - "host" = "catbox.moe", - "link" = "4m71t6.jpg", + "Host" = "catbox.moe", + "URL" = "4m71t6.jpg", ), MODE_ASK = list( - "host" = "catbox.moe", - "link" = "jm3f2c.jpg", + "Host" = "catbox.moe", + "URL" = "jm3f2c.jpg", ), MODE_SING = list( - "host" = "catbox.moe", - "link" = "em5bgb.jpg", + "Host" = "catbox.moe", + "URL" = "em5bgb.jpg", ), MODE_EXCLAIM = list( - "host" = "catbox.moe", - "link" = "3ggp5e.jpg", + "Host" = "catbox.moe", + "URL" = "3ggp5e.jpg", ), MODE_YELL = list( - "host" = "catbox.moe", - "link" = "1dmlu5.jpg", + "Host" = "catbox.moe", + "URL" = "1dmlu5.jpg", ), MODE_WHISPER = list( - "host" = "catbox.moe", - "link" = "t040sg.jpg", + "Host" = "catbox.moe", + "URL" = "t040sg.jpg", ), MODE_CUSTOM_SAY = list( - "host" = "catbox.moe", - "link" = "fcm6yw.jpg", + "Host" = "catbox.moe", + "URL" = "fcm6yw.jpg", ), ":example:" = list( - "host" = "catbox.moe", - "link" = "fcm6yw.jpg", + "Host" = "catbox.moe", + "URL" = "fcm6yw.jpg", + ), + ) + + var/list/default_pfps = list( + MALE = list( + "Host" = "catbox.moe", + "URL" = "1i1zom.png", ), + FEMALE = list( + "Host" = "catbox.moe", + "URL" = "jgxtpe.png", + ) ) var/img_size = 75 var/headspace = 4 var/debug_chud = FALSE + var/list/colorable_keys = list( + "TopBoxGradient1", + "TopBoxGradient2", + "TopBoxBorderColor", + "BottomBoxGradient1", + "BottomBoxGradient2", + "BottomBoxBorderColor", + "ButtonGradient1", + "ButtonGradient2", + "ButtonBorderColor", + "ImageBorderBorderColor", + "OuterBoxBorderColor", + ) + var/list/numberable_keys = list( + "TopBoxGradientAngle", + "TopBoxBorderWidth", + "BottomBoxGradientAngle", + "BottomBoxBorderWidth", + "ButtonGradientAngle", + "ButtonBorderWidth", + "ImageBorderBorderWidth", + "OuterBoxBorderWidth", + ) + var/list/selectable_keys = list( + "TopBoxBorderStyle", + "BottomBoxBorderStyle", + "ButtonBorderStyle", + "ImageBorderBorderStyle", + "OuterBoxBorderStyle", + ) + var/list/borderstyles = list( + "solid", + "dotted", + "dashed", + "double", + "groove", + "ridge", + "inset", + "outset", + "none", + "hidden", + ) + var/numbermal_min = 0 + var/numbermal_max = 5 + var/numbermal_default = 1 /datum/controller/subsystem/chat/Initialize(start_timeofday) setup_emoticon_cache() build_flirt_datums() + // build_stock_image_packs() . = ..() spawn(5 SECONDS) to_chat(world, span_boldnotice("Initialized [LAZYLEN(emoticon_cache)] emoticons! ;D")) to_chat(world, span_boldnotice("Initialized [LAZYLEN(flirts)] flirty messages! <3")) + // to_chat(world, span_boldnotice("Initialized [LAZYLEN(stock_image_packs)] stock image packs! 'w'")) +// /datum/controller/subsystem/chat/proc/build_stock_image_packs() +// stock_image_packs.Cut() +// for(var/paq in subtypesof(/datum/horny_image_pack)) +// var/datum/horny_image_pack/P = new(paq) +// stock_image_packs += list(P.ListifyPack()) /datum/controller/subsystem/chat/proc/setup_emoticon_cache() emoticon_cache.Cut() @@ -181,6 +254,121 @@ SUBSYSTEM_DEF(chat) return LAZYADD(payload_by_client[client], list(message)) +/datum/controller/subsystem/chat/proc/SanitizeUserImages(someone) + if(!someone) + return + var/datum/preferences/P = extract_prefs(someone) + if(!P) + return + var/list/defaulturls = list( + default_pfps[MALE]["URL"], + default_pfps[FEMALE]["URL"], + ) + var/list/PP = P.ProfilePics // this is my PP + // testing stuff + PP.Cut() + for(var/emotimode in mandatory_modes) + var/list/tentry = list() + for(var/list/moud in test_pics) + if(moud["Mode"] == emotimode) + tentry = moud + break + if(tentry) // check if its a valid image thing, and if not, BALEET IT + var/valid = FALSE + // if the user's image is default, and it isnt appropriate to their gender, we'll update it (cus it defaults to male and not everyone is) + if(tentry["URL"] in defaulturls) + switch(P.gender) + if(MALE) + if(tentry["URL"] != default_pfps[MALE]["URL"]) + valid = FALSE + if(FEMALE) + if(tentry["URL"] != default_pfps[FEMALE]["URL"]) + valid = FALSE + else + for(var/validhost in GLOB.supported_img_hosts) + if(tentry["Host"] == validhost) + valid = TRUE + break + if(!valid) + PP -= tentry + tentry = null + if(!LAZYLEN(tentry)) + // none there, so we'll add a default + var/list/newPP = list( + "Mode" = emotimode, + ) + switch(P.gender) + if(MALE) + newPP["URL"] = default_pfps[MALE]["URL"] + newPP["Host"] = default_pfps[MALE]["Host"] + if(FEMALE) + newPP["URL"] = default_pfps[FEMALE]["URL"] + newPP["Host"] = default_pfps[FEMALE]["Host"] + else + if(prob(50)) + newPP["URL"] = default_pfps[MALE]["URL"] + newPP["Host"] = default_pfps[MALE]["Host"] + else + newPP["URL"] = default_pfps[FEMALE]["URL"] + newPP["Host"] = default_pfps[FEMALE]["Host"] + PP += list(newPP) + // and finally, sort them! + var/list/PPsorted = list() + for(var/emotimode in mandatory_modes) + for(var/list/PPentry in PP) // AKA, your mom + if(PPentry["Mode"] == emotimode) + PPsorted += list(PPentry) + // and add in the customs + for(var/list/PPentry in PP) + if(PPentry["Mode"] in mandatory_modes) + continue + PPsorted += list(PPentry) + P.ProfilePics = PPsorted.Copy() + // P.save_character() + +/// makes sure that the user has a properly filled out set of preferences lists for their hornychat +/datum/controller/subsystem/chat/proc/SanitizeUserPreferences(someone) + if(!someone) // You'll be able to set colors and such for each of the message modes! + return // So you could, say, have your I AM YELLING block look more YELLY + var/datum/preferences/P = extract_prefs(someone) // and, hm, maybe not today, probably tomorrow + if(!P) + return + var/list/HP = P.mommychat_settings // this is my PP + if(!islist(HP)) + HP = list() + /// P.mommychat_settings[mmode]["ImageBorderBorderWidth"] = "2px" // the settings look like this + for(var/mode in mandatory_modes) + if(!islist(HP[mode])) + HP[mode] = list() + // check if the entries have a valid value + for(var/key in colorable_keys) + var/vale = HP[mode][key] + if(!vale || (LAZYACCESS(vale, 1) != "#")) + HP[mode][key] = "#222222" + for(var/key in numberable_keys) + var/vale = HP[mode][key] + if(!isnum(vale) || (vale < numbermal_min) || (vale > numbermal_max)) + HP[mode][key] = numbermal_default + for(var/key in selectable_keys) + var/vale = HP[mode][key] + if(!vale || !(vale in borderstyles)) + HP[mode][key] = borderstyles[1] + P.mommychat_settings = HP + // P.save_character() + + +/datum/controller/subsystem/chat/proc/HornyPreferences(someone) + if(!someone) + return + var/client/C = extract_client(someone) + if(!C) + return + var/datum/horny_tgui_holder/HTH = LAZYACCESS(horny_tguis, C.ckey) + if(!HTH) + HTH = new(C.ckey) + horny_tguis[C.ckey] = HTH + HTH.OpenPrefsMenu() + /datum/controller/subsystem/chat/proc/TestHorny() var/mob/user = usr to_chat(user, "Testing the horny") @@ -188,12 +376,9 @@ SUBSYSTEM_DEF(chat) PreviewHornyFurryDatingSimMessage(user, mmode) to_chat(user, "Test complete") -/datum/controller/subsystem/chat/proc/PreviewHornyFurryDatingSimMessage(mob/target, message_mode) +/datum/controller/subsystem/chat/proc/PreviewHornyFurryDatingSimMessage(mob/target, message_mode, message, print) if(!istype(target)) CRASH("PreviewHornyFurryDatingSimMessage called with invalid arguments! [target]!") - if(!message_mode) - CRASH("PreviewHornyFurryDatingSimMessage called with invalid arguments! [message_mode]!") - var/mob/living/carbon/human/dummy/D = SSdummy.get_a_dummy() var/datum/preferences/P = extract_prefs(target) if(!P) @@ -201,27 +386,33 @@ SUBSYSTEM_DEF(chat) P.copy_to(D) D.dummyckey = target.ckey D.forceMove(get_turf(target)) - var/msg = "Hey there! How's it going? I was thinking we could go on a date sometime. I'm a vampire and" - switch(message_mode) - if(MODE_SAY) - msg = msg - if(MODE_WHISPER) - msg = "#[msg]" - if(MODE_SING) - msg = "%[msg]" - if(MODE_ASK) - msg = "[msg]?" - if(MODE_EXCLAIM) - msg = "[msg]!" - if(MODE_YELL) - msg = "$[msg]" - - var/datum/rental_mommy/chat/mommy = D.say(msg, message_mode, direct_to_mob = target) + var/msg = message ? message : "Hey there! How's it going? I was thinking we could go on a date sometime. I'm a vampire and" + if(message_mode) + switch(message_mode) // dan // I mean, it is, isnt it? + if(MODE_SAY) + msg = msg + if(MODE_WHISPER) + msg = "#[msg]" + if(MODE_SING) + msg = "%[msg]" + if(MODE_ASK) + msg = "[msg]?" + if(MODE_EXCLAIM) + msg = "[msg]!" + if(MODE_YELL) + msg = "$[msg]" + else + msg = "[msg][message_mode]" // to catch any custom modes + + var/datum/rental_mommy/chat/mommy = D.say(msg, direct_to_mob = target) mommy.prefs_override = P var/mommess = BuildHornyFurryDatingSimMessage(mommy, TRUE) mommy.checkin() SSdummy.return_dummy(D) - to_chat(target, mommess) + if(print) + to_chat(target, mommess) + else + return mommess /datum/controller/subsystem/chat/proc/BuildHornyFurryDatingSimMessage(datum/rental_mommy/chat/mommy) if(!istype(mommy)) @@ -255,28 +446,36 @@ SUBSYSTEM_DEF(chat) /// look for something in m_rawmessage formatted as :exammple: and extract that to look up a custom image /// We'll extract this, store it as a var, and use it as an override for the profile image - var/list/m_images = /* P ? P.profilePics.Copy() : */test_pics + var/list/m_images = /* P ? P.ProfilePics.Copy() : */test_pics var/m_pfp = get_horny_pfp(m_rawmessage, m_images, m_mode) /// now all the many many colors for everything! // first the background gradients (and their angles) - var/tgc_1 = "#[P.mommychat_settings["top_gradient_color_1"]]" - var/tgc_2 = "#[P.mommychat_settings["top_gradient_color_2"]]" - var/tgangle = "[P.mommychat_settings["top_gradient_angle"]]" - var/bgc_1 = "#[P.mommychat_settings["bottom_gradient_color_1"]]" - var/bgc_2 = "#[P.mommychat_settings["bottom_gradient_color_2"]]" - var/bgangle = "[P.mommychat_settings["bottom_gradient_angle"]]" - var/bbc_1 = "#[P.mommychat_settings["button_background_color_1"]]" - var/bbc_2 = "#[P.mommychat_settings["button_background_color_2"]]" - var/bbangle = "[P.mommychat_settings["button_background_angle"]]" + var/tgc_1 = "#[P.mommychat_settings["[m_mode]"]["TopGradientColor1"]]" + var/tgc_2 = "#[P.mommychat_settings["[m_mode]"]["TopGradientColor2"]]" + var/tgangle = "[P.mommychat_settings["[m_mode]"]["TopGradientAngle"]]" + var/tbc = "#[P.mommychat_settings["[m_mode]"]["TopBorderColor"]]" + var/tbs = "#[P.mommychat_settings["[m_mode]"]["TopBorderSize"]]" + var/tbt = "#[P.mommychat_settings["[m_mode]"]["TopBorderStyle"]]" + + var/bgc_1 = "#[P.mommychat_settings["[m_mode]"]["BottomGradientColor1"]]" + var/bgc_2 = "#[P.mommychat_settings["[m_mode]"]["BottomGradientColor2"]]" + var/bgangle = "[P.mommychat_settings["[m_mode]"]["BottomGradientAngle"]]" + var/bbc = "#[P.mommychat_settings["[m_mode]"]["BottomBorderColor"]]" + var/bbs = "#[P.mommychat_settings["[m_mode]"]["BottomBorderSize"]]" + var/bbt = "#[P.mommychat_settings["[m_mode]"]["BottomBorderStyle"]]" + + var/bbc_1 = "#[P.mommychat_settings["[m_mode]"]["ButtonBackgroundColor1"]]" + var/bbc_2 = "#[P.mommychat_settings["[m_mode]"]["ButtonBackgroundColor2"]]" + var/bbangle = "[P.mommychat_settings["[m_mode]"]["ButtonBackgroundAngle"]]" // now the borders - var/obc = "#[P.mommychat_settings["outer_border_color"]]" - var/tbc = "#[P.mommychat_settings["top_border_color"]]" - var/bbc = "#[P.mommychat_settings["bottom_border_color"]]" - var/ibc = "#[P.mommychat_settings["image_border_color"]]" - var/ibs = "[P.mommychat_settings["image_border_size"]]" - var/ibt = "[P.mommychat_settings["image_border_style"]]" + var/obc = "#[P.mommychat_settings["[m_mode]"]["OuterBorderColor"]]" + var/obs = "#[P.mommychat_settings["[m_mode]"]["OuterBorderSize"]]" + var/obt = "#[P.mommychat_settings["[m_mode]"]["OuterBorderStyle"]]" + var/ibc = "#[P.mommychat_settings["[m_mode]"]["ImageBorderColor"]]" + var/ibs = "[P.mommychat_settings["[m_mode]"]["ImageBorderSize"]]" + var/ibt = "[P.mommychat_settings["[m_mode]"]["ImageBorderStyle"]]" // now the text colors // most are defined by mommy, but some arent, so we'll need to get a color that contrasts with the average of the top and bottom gradient colors var/tgc_to_num = hex2num(replacetext(tgc_1,"#", "")) + hex2num(replacetext(tgc_2,"#", "")) @@ -317,11 +516,9 @@ SUBSYSTEM_DEF(chat) /// now we need to build the message var/list/cum = list() // First, the full body container - cum += "
" - - + cum += "
" // first the head - cum += "
" + cum += "
" // now the profile picture cum += "
" cum += "" @@ -351,7 +548,7 @@ SUBSYSTEM_DEF(chat) cum += "
" cum += "
" // now the body - cum += "
" + cum += "
" cum += "

[m_name] [m_verb]

" cum += "

[m_message]

" cum += "
" @@ -398,18 +595,18 @@ SUBSYSTEM_DEF(chat) if(findtext(m_rawmessage, testpart)) if(m_images[testpart]) var/list/imgz = m_images[testpart] - if(imgz["link"] != "" && imgz["host"] != "") - image2use = PfpHostLink(imgz["link"], imgz["host"]) + if(imgz["URL"] != "" && imgz["Host"] != "") + image2use = PfpHostLink(imgz["URL"], imgz["Host"]) /// then extract the message mode and see if they have a corresponting image if(!image2use) var/list/testimages = m_images[m_mode] - if(testimages["link"] != "" && testimages["host"] != "") - image2use = PfpHostLink(testimages["link"], testimages["host"]) + if(testimages["URL"] != "" && testimages["Host"] != "") + image2use = PfpHostLink(testimages["URL"], testimages["Host"]) // just grab their default one if(!image2use) var/list/testimages = m_images[MODE_SAY] - if(testimages["link"] != "" && testimages["host"] != "") - image2use = PfpHostLink(testimages["link"], testimages["host"]) + if(testimages["URL"] != "" && testimages["Host"] != "") + image2use = PfpHostLink(testimages["URL"], testimages["Host"]) // if we still dont have one, just use a placeholder if(!image2use) image2use = "https://www.placehold.it/100x100.png" @@ -1095,4 +1292,590 @@ SUBSYSTEM_DEF(chat) SSchat.start_page(src, A) +/// Hi! I'm the thing that holds the stuff for the horny mommychat setup thing +/datum/horny_tgui_holder + var/ownerkey + /// HornyChat supports copypasting! Rejoice! + var/list/clipboard = list() + // var/datum/horny_stock_image_tgui_holder/hsith // dewit + + +/datum/horny_tgui_holder/New(okey) + . = ..() + ownerkey = okey + // hsith = new(ownerkey) + +/datum/horny_tgui_holder/proc/OpenPrefsMenu() + var/mob/M = ckey2mob(ownerkey) + if(!M) + return + ui_interact(M) + +/datum/horny_tgui_holder/ui_state(mob/user) + return GLOB.always_state + +/datum/horny_tgui_holder/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "HornyChat") // yes Im calling it that + ui.open() + + +/datum/horny_tgui_holder/ui_static_data(mob/user) + var/list/data = list() + var/datum/preferences/P = extract_prefs(user) + /// first, the stock images + /// not using em lol + /// then, the user's images + SSchat.SanitizeUserImages(P) + SSchat.SanitizeUserPreferences(P) + data["UserImages"] = P.ProfilePics + /// Also all the previews + var/list/previewmsgs = list() + /// Anatomy of a msgmode entry in ProfilePics: + /// list( + /// "NameOfTheMode" = list( + /// "Host" = "www.catbox.moe", // or something + /// "URL" = "image.png", + /// ), + /// etc + /// ) + /// Anatomy of a preview message: + /// list( + /// list( + /// "[msgmode]" = "long html thing" + /// ), + /// etc + for(var/list/modus in P.ProfilePics) + var/msgmode = modus["Mode"] + var/message2say = "Hi." + switch(msgmode) + if(MODE_SAY) + message2say = "Hello! You are hearing me talk. I am saying words. I am saying words to you. \ + I am saying words to you in a friendly manner. I am not yelling, I am not whispering, I am not singing, \ + I am not asking, I am not exclaiming. I am saying words to you, and you are hearing them. And now I am done. Hi." + if(MODE_WHISPER) + message2say = "#Psst, hey! Wanna know a secret? I'm whispering to you. This is what its like for you to \ + whisper, and what other people will see when you whisper to them. Does it look like I'm whispering? \ + You can tell by my profile picture that whispering is taking place. And now I'm done. Hi." + if(MODE_SING) + message2say = "%Allow me to sing the song of my people: ACK ACAKCHA AKGH CKCKHHGN YIP YIP YAKCGH EE EI EEI \ + GEKKER GEKKER ACK GACK AKCH TCHOFF AHC IA IA TEKALI-LI RLYEA CTHULHU FTAGHN YIPYIP! And now I'm done. Hi." + if(MODE_ASK) + message2say = "Hello? Is this thing on? So tell me, how many licks does it take to get to the center of a vixen? \ + Can you help me find out? Wanna go on a date and help em find out how many licks it takes to get to the center of a vixen? \ + What if it's more than one? What if it's less than one? What if it's exactly one? And now I'm done? Hi?" + if(MODE_EXCLAIM) + message2say = "Wow! I'm so excited! I'm so excited to be here! I'm so excited to be talking to you! \ + This is so exciting! I'm so excited! I'm so excited! I'm so excited! I'm so excited! I'm so excited! \ + I'm so excited! I'm so excited! I'm so excited! I'm so excited! I'm so excited! I'm so excited! \ + And now I'm done! Hi!" + if(MODE_YELL) + message2say = "$AAAAAAAAAAAAAAAAAAAAAAAA AAAAAA AAAAAAAAAA A AAAAAAAAAA AAAAAAAAAAAAAAAAAAAA AAAAAAAAA \ + AAAAAAAAA A AAA AAAAAAAAAAA AAAAAAAAAA AAAAAAAAAA AAA AAAAAAA AAAAAA AAAA A A AA AAAAAAAA AAA AAAA AAAA \ + AA A A AA A AAA A A AAAAAAAAAAAAAA AAA A A AAA AAAAAAAAAAAA A AAAA A A A AA AAAAAAAAAAAAAAAAA AAAAAA \ + AND NOW IM DONE!! HI!!" + else + message2say = "Hi, this is a test of a custom message mode that has been set by you to be used to display \ + a custom message mode. The mode is set to [replacetext(msgmode, ":","")]. If the previous sentence was \ + cut off, please make a note of it. Cool huh? And now I'm done. Hi. [msgmode]" + var/msgmess = SSchat.PreviewHornyFurryDatingSimMessage(user, null, message2say, FALSE) + previewmsgs += list(list("Mode" = msgmode, "Message" = msgmess)) + data["NumbermalMin"] = SSchat.numbermal_min + data["NumbermalMax"] = SSchat.numbermal_max + data["PreviewHTMLs"] = previewmsgs + /// Then, the message appearance settings, its a mmouthful (just like your mom) + /// arranged by tab of course of course + data["TopBox"] = list() + data["BottomBox"] = list() + data["Buttons"] = list() + data["OuterBox"] = list() + data["ImageBox"] = list() + for(var/mmode in P.mommychat_settings) + // First, the top box settings + var/list/topfox = list() + topfox["Mode"] = mmode + topfox["Name"] = "Top Box" + topfox["Settings"] = list() + topfox["Settings"] += list( + list( + "Name" = "Gradient 1", + "Val" = P.mommychat_settings[mmode]["TopBoxGradient1"], + "Type" = "COLOR", + "Loc" = "L", + "PKey" = "TopBoxGradient1", + "Desc" = "The first color of the gradient for the top box.", + "Default" = "000000" + ), + list( + "Name" = "Gradient 2", + "Val" = P.mommychat_settings[mmode]["TopBoxGradient2"], + "Type" = "COLOR", + "Loc" = "L", + "PKey" = "TopBoxGradient2", + "Desc" = "The second color of the gradient for the top box.", + "Default" = "000000" + ), + list( + "Name" = "Gradient Angle", + "Val" = P.mommychat_settings[mmode]["TopBoxGradientAngle"], + "Type" = "NUMBER", + "Loc" = "L", + "PKey" = "TopBoxGradientAngle", + "Desc" = "The angle of the gradient for the top box.", + "Default" = "0" + ), + list( + "Name" = "Border Color", + "Val" = P.mommychat_settings[mmode]["TopBoxBorderColor"], + "Type" = "COLOR", + "Loc" = "R", + "PKey" = "TopBoxBorderColor", + "Desc" = "The color of the border for the top box.", + "Default" = "000000" + ), + list( + "Name" = "Border Width", + "Val" = P.mommychat_settings[mmode]["TopBoxBorderWidth"], + "Type" = "NUMBER", + "Loc" = "R", + "PKey" = "TopBoxBorderWidth", + "Desc" = "The width of the border for the top box.", + "Default" = "1" + ), + list( + "Name" = "Border Style", + "Val" = P.mommychat_settings[mmode]["TopBoxBorderStyle"], + "Type" = "SELECT", + "Loc" = "R", + "PKey" = "TopBoxBorderStyle", + "Desc" = "The style of the border for the top box.", + "Default" = "solid", + "Options" = SSchat.borderstyles + ), + ) + data["TopBox"] += list(topfox) + // Then, the bottom box settings + var/list/bottomfox = list() + bottomfox["Mode"] = mmode + bottomfox["Name"] = "Message Box" + bottomfox["Settings"] = list() + bottomfox["Settings"] += list( + list( + "Name" = "Gradient 1", + "Val" = P.mommychat_settings[mmode]["BottomBoxGradient1"], + "Type" = "COLOR", + "Loc" = "L", + "PKey" = "BottomBoxGradient1", + "Desc" = "The first color of the gradient for the bottom box.", + "Default" = "000000" + ), + list( + "Name" = "Gradient 2", + "Val" = P.mommychat_settings[mmode]["BottomBoxGradient2"], + "Type" = "COLOR", + "Loc" = "L", + "PKey" = "BottomBoxGradient2", + "Desc" = "The second color of the gradient for the bottom box.", + "Default" = "000000" + ), + list( + "Name" = "Gradient Angle", + "Val" = P.mommychat_settings[mmode]["BottomBoxGradientAngle"], + "Type" = "NUMBER", + "Loc" = "L", + "PKey" = "BottomBoxGradientAngle", + "Desc" = "The angle of the gradient for the bottom box.", + "Default" = "0" + ), + list( + "Name" = "Border Color", + "Val" = P.mommychat_settings[mmode]["BottomBoxBorderColor"], + "Type" = "COLOR", + "Loc" = "R", + "PKey" = "BottomBoxBorderColor", + "Desc" = "The color of the border for the bottom box.", + "Default" = "000000" + ), + list( + "Name" = "Border Width", + "Val" = P.mommychat_settings[mmode]["BottomBoxBorderWidth"], + "Type" = "NUMBER", + "Loc" = "R", + "PKey" = "BottomBoxBorderWidth", + "Desc" = "The width of the border for the bottom box.", + "Default" = "1" + ), + list( + "Name" = "Border Style", + "Val" = P.mommychat_settings[mmode]["BottomBoxBorderStyle"], + "Type" = "SELECT", + "Loc" = "R", + "PKey" = "BottomBoxBorderStyle", + "Desc" = "The style of the border for the bottom box.", + "Default" = "solid", + "Options" = SSchat.borderstyles + ), + ) + data["BottomBox"] += list(bottomfox) + // Then, the button settings + var/list/buttonfox = list() + buttonfox["Mode"] = mmode + buttonfox["Name"] = "Buttons" + buttonfox["Settings"] = list() + buttonfox["Settings"] += list( + list( + "Name" = "Gradient 1", + "Val" = P.mommychat_settings[mmode]["ButtonGradient1"], + "Type" = "COLOR", + "Loc" = "L", + "PKey" = "ButtonGradient1", + "Desc" = "The first color of the gradient for the buttons.", + "Default" = "000000" + ), + list( + "Name" = "Gradient 2", + "Val" = P.mommychat_settings[mmode]["ButtonGradient2"], + "Type" = "COLOR", + "Loc" = "L", + "PKey" = "ButtonGradient2", + "Desc" = "The second color of the gradient for the buttons.", + "Default" = "000000" + ), + list( + "Name" = "Gradient Angle", + "Val" = P.mommychat_settings[mmode]["ButtonGradientAngle"], + "Type" = "NUMBER", + "Loc" = "L", + "PKey" = "ButtonGradientAngle", + "Desc" = "The angle of the gradient for the buttons.", + "Default" = "0" + ), + list( + "Name" = "Border Color", + "Val" = P.mommychat_settings[mmode]["ButtonBorderColor"], + "Type" = "COLOR", + "Loc" = "R", + "PKey" = "ButtonBorderColor", + "Desc" = "The color of the border for the buttons.", + "Default" = "000000" + ), + list( + "Name" = "Border Width", + "Val" = P.mommychat_settings[mmode]["ButtonBorderWidth"], + "Type" = "NUMBER", + "Loc" = "R", + "PKey" = "ButtonBorderWidth", + "Desc" = "The width of the border for the buttons.", + "Default" = "1" + ), + list( + "Name" = "Border Style", + "Val" = P.mommychat_settings[mmode]["ButtonBorderStyle"], + "Type" = "SELECT", + "Loc" = "R", + "PKey" = "ButtonBorderStyle", + "Desc" = "The style of the border for the buttons.", + "Default" = "solid", + "Options" = SSchat.borderstyles + ), + ) + data["Buttons"] += list(buttonfox) + /// Then, the Image Border settings + var/list/imagefox = list() + imagefox["Mode"] = mmode + imagefox["Name"] = "Image Border" + imagefox["Settings"] = list() + imagefox["Settings"] += list( + list( + "Name" = "Border Color", + "Val" = P.mommychat_settings[mmode]["ImageBorderBorderColor"], + "Type" = "COLOR", + "Loc" = "L", + "PKey" = "ImageBorderBorderColor", + "Desc" = "The color of the border for the images.", + "Default" = "000000" + ), + list( + "Name" = "Border Width", + "Val" = P.mommychat_settings[mmode]["ImageBorderBorderWidth"], + "Type" = "NUMBER", + "Loc" = "L", + "PKey" = "ImageBorderBorderWidth", + "Desc" = "The width of the border for the images.", + "Default" = "1" + ), + list( + "Name" = "Border Style", + "Val" = P.mommychat_settings[mmode]["ImageBorderBorderStyle"], + "Type" = "SELECT", + "Loc" = "L", + "PKey" = "ImageBorderBorderStyle", + "Desc" = "The style of the border for the images.", + "Default" = "solid", + "Options" = SSchat.borderstyles + ), + ) + data["ImageBox"] += list(imagefox) + /// And finally the Outer Box settings + var/list/outerfox = list() + outerfox["Mode"] = mmode + outerfox["Name"] = "Outer Box" + outerfox["Settings"] = list() + outerfox["Settings"] += list( + list( + "Name" = "Border Color", + "Val" = P.mommychat_settings[mmode]["OuterBoxBorderColor"], + "Type" = "COLOR", + "Loc" = "L", + "PKey" = "OuterBoxBorderColor", + "Desc" = "The color of the border for the outer box.", + "Default" = "000000" + ), + list( + "Name" = "Border Width", + "Val" = P.mommychat_settings[mmode]["OuterBoxBorderWidth"], + "Type" = "NUMBER", + "Loc" = "L", + "PKey" = "OuterBoxBorderWidth", + "Desc" = "The width of the border for the outer box.", + "Default" = "1" + ), + list( + "Name" = "Border Style", + "Val" = P.mommychat_settings[mmode]["OuterBoxBorderStyle"], + "Type" = "SELECT", + "Loc" = "L", + "PKey" = "OuterBoxBorderStyle", + "Desc" = "The style of the border for the outer box.", + "Default" = "solid", + "Options" = SSchat.borderstyles + ), + ) + data["OuterBox"] += list(outerfox) + return data + +/* + * IMPORTANT NAMING CONVENTIONS + * Strings sent TO TGUI are in the format "ModeName" (e.g. "TopBoxGradient1") + * Strings sent FROM TGUI are in the format "ModeName" (e.g. "TopBoxGradient1") + * Clipboard VALUES STORED in the format "lowercase_name" (e.g. "profilepic") + * Clipboard are SENT TO TGUI in the format "ModeName" (e.g. "ProfilePic") + * Horny Images are stored as BYOND sees them in the code (e.g. "say" or "%") + * screw it, everything is in ModeName format + * + * */ + +/datum/horny_tgui_holder/ui_act(action, list/params) + . = ..() + if(!params["UserCkey"]) + return + var/mob/M = ckey2mob(params["UserCkey"]) + if(!M) + return + var/datum/preferences/P = extract_prefs(M) + if(!P) + return + switch(action) + if("ModifyProfileEntry") + var/mode = params["Mode"] + var/newmode = params["NewMode"] + var/host = params["Host"] + var/url = params["URL"] + var/list/ProfilePics = P.ProfilePics // operates directly on the referenced list, hopefully + var/list/fount + for(var/list/entry in ProfilePics) + if(entry["Mode"] == mode) + fount = entry + if(fount) + if(newmode) + fount["Mode"] = newmode + else + fount["Mode"] = mode + fount["Host"] = host + fount["URL"] = url + else + to_chat(M, span_alert("Unable to modify profile entry for [mode2string(mode)], entry not found! :c")) + return FALSE + to_chat(M, span_notice("Profile entry for [mode2string(mode)] has been updated!")) + . = TRUE + /// hey github copilot, what is your favorite color of fox? + /// "I like + /// the color of + /// the fox + /// that is + /// the color + /// of the + /// fox that + /// is the + /// color of + /// the fox // good talking to you, copilot + if("CopyImage") + var/mode = params["Mode"] + var/list/ProfilePics = P.ProfilePics + var/list/fount + for(var/list/entry in ProfilePics) + if(entry["Mode"] == mode) + fount = entry + if(fount) // + var/list/clip = list( + "Mode" = fount["Mode"], + "Host" = fount["Host"], + "URL" = fount["URL"], + ) + clipboard["ProfilePic"] = clip + to_chat(M, span_notice("Profile entry for [mode2string(mode)] has been copied to the clipboard!")) + . = TRUE + else + to_chat(M, span_alert("Unable to copy profile entry for [mode2string(mode)], entry not found! :c")) + return FALSE + if("PasteImage") + var/mode = params["Mode"] + var/list/ProfilePics = P.ProfilePics + var/list/clip = clipboard["ProfilePic"] + if(clip) + var/list/fount + for(var/list/entry in ProfilePics) + if(entry["Mode"] == mode) + fount = entry + if(fount) + fount["Mode"] = clip["Mode"] + fount["Host"] = clip["Host"] + fount["URL"] = clip["URL"] + to_chat(M, span_notice("Profile entry for [mode2string(mode)] has been pasted from the clipboard!")) + . = TRUE + else + to_chat(M, span_alert("Unable to paste profile entry for [mode2string(mode)], entry not found! :c")) + return FALSE + else + to_chat(M, span_alert("Unable to paste profile entry for [mode2string(mode)], clipboard is empty! :c")) + return FALSE + if("ClearProfileEntry") + var/mode = params["Mode"] + var/list/ProfilePics = P.ProfilePics + var/list/fount + for(var/list/entry in ProfilePics) + if(entry["Mode"] == mode) + fount = entry + if(fount) + if(mode in SSchat.mandatory_modes) + // just reset it + fount["Host"] = "www.catbox.moe" + switch(mode) + if(MODE_SAY) + fount["URL"] = "say.png" // replace these + if(MODE_WHISPER) + fount["URL"] = "whisper.png" // with the actual + if(MODE_SING) + fount["URL"] = "sing.png" // images + if(MODE_ASK) + fount["URL"] = "ask.png" // for the + if(MODE_EXCLAIM) + fount["URL"] = "exclaim.png" // modes + if(MODE_YELL) + fount["URL"] = "yell.png" // you yiff + else + fount["URL"] = "say.png" // you lose + to_chat(M, span_notice("Profile entry for [mode2string(mode)] has been reset!")) + else + ProfilePics -= fount + to_chat(M, span_notice("Profile entry for [mode2string(mode)] has been removed!")) + . = TRUE + else + to_chat(M, span_alert("Unable to clear profile entry for [mode2string(mode)], entry not found! :c")) + return FALSE + if("EditColor") // we'll handle this one + var/pisskey = params["PKey"] + var/mode = params["Mode"] + var/current = params["Current"] + if(!(pisskey in SSchat.colorable_keys)) + to_chat(M, span_alert("Unable to edit color for [mode2string(mode)], mode is not colorable! This is probably a bug :c")) + return FALSE + var/val = input( + M, + "Enter a new color for the [pisskey] of the [mode2string(mode)]!", + "Tripod like a real AngelFire", + current + ) as color|null + if(!istext(val)) + to_chat(M, span_alert("Nevermind!!")) + P.mommychat_settings[mode][pisskey] = val + to_chat(M, span_notice("Color for [mode2string(mode)] [pisskey] has been updated to #[val]!")) + . = TRUE + if("EditNumber") // TGUI handled this one + var/pisskey = params["PKey"] + var/mode = params["Mode"] + var/newval = params["Val"] + if(!(pisskey in SSchat.numberable_keys)) + to_chat(M, span_alert("Unable to edit number for [mode2string(mode)], mode is not numberable! This is probably a bug :c")) + return FALSE + P.mommychat_settings[mode][pisskey] = newval + to_chat(M, span_notice("Number for [mode2string(mode)] [pisskey] has been updated to [newval]!")) + . = TRUE + if("EditSelect") // TGUI handled this one + var/pisskey = params["PKey"] + var/mode = params["Mode"] + var/newval = params["Val"] + if(!(pisskey in SSchat.selectable_keys)) + to_chat(M, span_alert("Unable to edit select for [mode2string(mode)], mode is not selectable! This is probably a bug :c")) + return FALSE + P.mommychat_settings[mode][pisskey] = newval + to_chat(M, span_notice("Select for [mode2string(mode)] [pisskey] has been updated to [newval]!")) + . = TRUE + if("CopySetting") + var/pisskey = params["PKey"] + var/mode = params["Mode"] + var/list/horny_settings = P.mommychat_settings + var/list/fount + for(var/list/entry in horny_settings[mode]) + if(entry["PKey"] == pisskey) + fount = entry + if(fount) + var/list/clip = list( + "Mode" = fount["Mode"], + "PKey" = pisskey, + "Val" = fount[pisskey], + ) + clipboard["MsgSetting"] = clip + to_chat(M, span_notice("Setting for [mode2string(mode)] [pisskey] has been copied to the clipboard!")) + . = TRUE + else + to_chat(M, span_alert("Unable to copy setting for [mode2string(mode)] [pisskey], setting not found! :c")) + return FALSE + if("PasteSetting") + var/pisskey = params["PKey"] + var/mode = params["Mode"] + var/list/horny_settings = P.mommychat_settings + var/list/clip = clipboard["MsgSetting"] + if(clip) + // first check if this setting can accept the value + if((clip["PKey"] in SSchat.colorable_keys) && (pisskey in SSchat.colorable_keys)\ + || (clip["PKey"] in SSchat.numberable_keys) && (pisskey in SSchat.numberable_keys)\ + || (clip["PKey"] in SSchat.selectable_keys) && (pisskey in SSchat.selectable_keys)) + horny_settings[mode][pisskey] = clip["Val"] + to_chat(M, span_notice("Setting for [mode2string(mode)] [pisskey] has been pasted from the clipboard!")) + . = TRUE + else + to_chat(M, span_alert("Unable to paste setting for [mode2string(mode)] [pisskey], setting type mismatch! :c")) + return FALSE + else + to_chat(M, span_alert("Unable to paste setting for [mode2string(mode)] [pisskey], clipboard is empty! :c")) + return FALSE + +/datum/horny_tgui_holder/proc/mode2string(mode) + switch(mode) + if(MODE_SAY) + return "Say" + if(MODE_WHISPER) + return "Whisper" + if(MODE_SING) + return "Sing" + if(MODE_ASK) + return "Ask" + if(MODE_EXCLAIM) + return "Exclaim" + if(MODE_YELL) + return "Yell" + return "[mode]" + diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 725d87a30f..c1aa0d1196 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -107,6 +107,7 @@ GLOBAL_PROTECT(admin_verbs_admin) /datum/admins/proc/test_dailies, /datum/admins/proc/make_cool_payload, /client/proc/test_horny_furries, + /datum/admins/proc/test_hornychat_prefs, ) GLOBAL_LIST_INIT(admin_verbs_ban, list(/client/proc/unban_panel, /client/proc/DB_ban_panel, /client/proc/stickybanpanel)) GLOBAL_PROTECT(admin_verbs_ban) @@ -1316,6 +1317,14 @@ GLOBAL_PROTECT(admin_verbs_hideable) return GLOB.cooltext_pro.Open(usr) +/datum/admins/proc/test_hornychat_prefs() + set category = "Debug" + set name = "Access Hornychat" + set desc = "Opens the Hornychat preferences panel." + + SSchat.HornyPreferences(usr) + to_chat(usr, "Hornychat preferences opened! Hopefully!") + GLOBAL_DATUM_INIT(cooltext_pro, /datum/shrimpletext, new) /datum/shrimpletext diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 465142ef8b..c3db571502 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -158,24 +158,6 @@ GLOBAL_LIST_EMPTY(preferences_datums) ///Keeping track of chat bg color var/chatbgcolor = "#131313" - var/list/mommychat_settings = list( - "top_gradient_color_1" = "333333", - "top_gradient_color_2" = "111111", - "top_gradient_angle" = 0, - "bottom_gradient_color_1" = "333333", - "bottom_gradient_color_2" = "111111", - "bottom_gradient_angle" = 0, - "button_background_color_1" = "FFFFFF", - "button_background_color_2" = "FFFFFF", - "button_background_angle" = 0, - "outer_border_color" = "000000", - "top_border_color" = "000000", - "bottom_border_color" = "000000", - "image_border_color" = "000000", - "image_border_size" = 0, - "image_border_style" = "solid", - ) - var/list/aghost_squelches = list() /// Custom Keybindings @@ -317,6 +299,45 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/creature_body_size = 1 var/creature_fuzzy = FALSE + var/list/ProfilePics = list( + list( + "Mode" = MODE_SAY, + "Host" = "", + "URL" = "", + ), + list( + "Mode" = MODE_ASK, + "Host" = "", + "URL" = "", + ), + list( + "Mode" = MODE_SING, + "Host" = "", + "URL" = "", + ), + list( + "Mode" = MODE_EXCLAIM, + "Host" = "", + "URL" = "", + ), + list( + "Mode" = MODE_YELL, + "Host" = "", + "URL" = "", + ), + list( + "Mode" = MODE_WHISPER, + "Host" = "", + "URL" = "", + ), + list( + "Mode" = ":example:", + "Host" = "", + "URL" = "", + ), + ) + var/list/mommychat_settings = list() // will be set by SSchat (goodness me) + /// Quirk list /// okay lets compromise, we'll have type paths, but they're strings, happy? /// Format: list("/datum/quirk/aaa", "/datum/quirk/bbb", "/datum/quirk/ccc", etc) diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index 35b106f53e..aed7ab371c 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -1270,6 +1270,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car creature_profilepic = sanitize_text(creature_profilepic) creature_pfphost = sanitize_inlist(creature_pfphost, GLOB.pfp_filehosts, "") + SSchat.SanitizeUserImages(src) + SSchat.SanitizeUserPreferences(src) + features_override["grad_color"] = sanitize_hexcolor(features_override["grad_color"], 6, FALSE, default = COLOR_ALMOST_BLACK) features_override["grad_style"] = sanitize_inlist(features_override["grad_style"], GLOB.hair_gradients, "none") diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index cb07723057..8f2006d228 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -136,7 +136,7 @@ spans |= speech_span - if(language) + if(language && LAZYLEN(GLOB.language_datum_instances)) var/datum/language/L = GLOB.language_datum_instances[language] spans |= L.spans diff --git a/modular_coyote/code/modules/examine_images.dm b/modular_coyote/code/modules/examine_images.dm index 28da644bfa..ecaf94f604 100644 --- a/modular_coyote/code/modules/examine_images.dm +++ b/modular_coyote/code/modules/examine_images.dm @@ -149,40 +149,6 @@ GLOBAL_LIST_INIT(pfp_filehost_safe_suffixes, list( var/profilePicture = "" var/pfphost = "" - var/list/profilePics = list( - MODE_SING = list( - "host" = "", - "link" = "", - ), - MODE_SAY = list( - "host" = "", - "link" = "", - ), - MODE_ASK = list( - "host" = "", - "link" = "", - ), - MODE_EXCLAIM = list( - "host" = "", - "link" = "", - ), - MODE_YELL = list( - "host" = "", - "link" = "", - ), - MODE_WHISPER = list( - "host" = "", - "link" = "", - ), - MODE_CUSTOM_SAY = list( - "host" = "", - "link" = "", - ), - ":example:" = list( - "host" = "", - "link" = "", - ), - ) // Moved this to preferences_savefile.dm as we're having issues with overriding the function I think. // My speculation is that us trying to open the save file multiple times with multiple users is causing a memory overflow on the server end and refusing to open it diff --git a/tgui/packages/tgui/interfaces/HornyChat.js b/tgui/packages/tgui/interfaces/HornyChat.js new file mode 100644 index 0000000000..59c89c832a --- /dev/null +++ b/tgui/packages/tgui/interfaces/HornyChat.js @@ -0,0 +1,1017 @@ +/* eslint-disable react/no-danger */ +// 80 characters is not big enough for my yiff yiff +/* eslint-disable max-len */ +import { useBackend, useLocalState } from '../backend'; +import { toFixed } from 'common/math'; +import { multiline } from 'common/string'; +import { + AnimatedNumber, + Box, + Button, + Dropdown, + Input, + Tooltip, + Section, + LabeledList, + NoticeBox, + NumberInput, + Icon, + Stack, + Fragment, + Table, + ToggleBox, +} from '../components'; +import { formatMoney } from '../format'; +import { Window } from '../layouts'; +import { marked } from 'marked'; +import { sanitizeText } from '../sanitize'; + +/* + Welcome to HornyChat, how horny are you? + I yiffed a bowl of nails for breakfast, without any milk. + + This is the preferecnes page for HornyChat, you can set your preferences here. + There are three tabs, "Profile Pics", "Message Appearance", and "Preview". + + Profile Pics: + Sevreral lines for profile pics + Column names: + - Copy + - Message Mode + - Host + - Filename + - Preview + - Clear + Each line will have: + - A copy button + - Will add in a paste button for the rest of the entries + - The message mode that will trigger the profile pic + - A dropdown for supported hosts + - A text input for the image URL + - A preview of the image (smol) + - A clear button + At the bottom of the list panel, there will be an extra row, for stock pics + It'll have a button you can press, and it'll open a new window with a list of stock pics + We'll get into that later. + + Message Appearance: + This is where you set up the Geocities-style HTML gradient stuff. + You can set up a gradient for the background of each of the boxes, + as well as setup border colors, styles, and widths. + Web 2.0? Never heard of it. + Lots of tabs, lots of options. + there are two columns! + - Left column: + The message modes, arranged vertically + This will set a local state for the right column to display the options + for the selected message mode! + Each message mode will have a button to copy the settings to anothr mode + - Right column: + The rest of the damn owl + This will have a bunch of options for the selected message mode + as well as a preview of the message appearance + It will be arranged in rows: + - Which section of the message appearance to edit + - The options for that section + - A preview of the message appearance + + Preview: + A list of preview messages, one for each message mode + Each message will have a preview of the message appearance + as well as an example message (worning: will contain horny) + + */ + +const BackMsgMode2Front = { + "say": "Say", + "custom_say": "Custom Say", + "whisper": "Whisper", + "whispercrit": "Whisper", + "ask": "Ask", + "exclaim": "Exclaim", + "yell": "Yell", + "%": "Sing", // CUS COURSE ITS % + "emote": "Emote", + "me": "Me", +}; + +// dark pale violet: #5F9EA0 +// dark slate gray: #2F4F4F +// darker slate gray: #1F3A3A +// dark blue: #00008B +// dark olive green: #556B2F +// dark green: #006400 +// dark red: #8B0000 +// dark orange: #FF8C00 +// dark goldenrod: #B8860B +// dark khaki: #BDB76B +// dark orchid: #9932CC +// dark violet: #9400D3 +// dark magenta: #8B008B +// dark pink: #FF1493 +// dark salmon: #E9967A +// dark tomato: #D30000 +// dark coral: #FF7F50 +// dark sienna: #8B4513 +// dark brown: #5A2E2E +// dark chocolate: #D2691E +// dark purple: #800080 +// dark indigo: #4B0082 + +// dark cyan: #008B8B +// dark sky blue: #8CBED6 +// dark light blue: #8CBED6 +// dark light green: #90EE90 +// dark light red: #FF6347 +// dark light orange: #FFA500 +// dark light goldenrod: #DAA520 +// dark light khaki: #F0E68C +// dark light orchid: #9932CC +// dark light violet: #9400D3 +// dark light magenta: #EE82EE +// dark light pink: #FFB6C1 +// dark light salmon: #E9967A +// dark light tomato: #FF6347 +// dark light coral: #F08080 +// dark light sienna: #A0522D +// dark light brown: #A52A2A +// dark light chocolate: #D2691E +// dark light purple: #9370DB +// dark light indigo: #4B0082 + +// dark light cyan: #008B8B +// dark light sky blue: #8CBED6 +// dark light light blue: #8CBED6 +// dark light light green: #90EE90 +// dark light light red: #FF6347 +// dark light light orange: #FFA500 +// Thanks to https://www.colorhexa.com/ for the colors +// (I didnt actually use that site, I just like to thank random people) + +export const HornyChat = (props, context) => { + const { act, data } = useBackend(context); + return ( + + + + + + + {/* grow shrink my beloved, you have saved so many of my garbo layouts */} +
+ +
+
+ + + +
+
+
+ ); +}; + +// UpperRowTabBar is the top row of tabs in the HornyChat window yiff yiff +// UpperRowTab legend: +// 1: Profile Pics +// 2: Message Appearance +// 3: Preview +const UpperRowTabBar = (props, context) => { + const { act, data } = useBackend(context); + const [ + UpperRowTab, + setUpperRowTab, + ] = useLocalState(context, 'UpperRowTab', 1); + + return ( + + + + + + + + + + + + ); +}; + +// MainWindow is the main window of the HornyChat window yiff yiff +// Will route to the correct tab based on the UpperRowTab state +const MainWindow = (props, context) => { + const { act, data } = useBackend(context); + const [ + UpperRowTab, + setUpperRowTab, + ] = useLocalState(context, 'UpperRowTab', 1); + + switch (UpperRowTab) { + case 1: + return ( + // i will + ); + case 2: + return ( + // horny you + ); + case 3: + return ( + // into the dirt + ); + } +}; + +// LowerRowBar is the bottom row with two buttons: +// on the right, a "Save" button +// on the left, a "Get Profile Pics" button +// and a whole lot of nothing in between +const LowerRowBar = (props, context) => { + const { act, data } = useBackend(context); + const { + UserCkey, + } = data; + + return ( + + + {/* It just links to an AI furry image algovomitorihtym thing */} + {/* Probably the one on Perchance, that one's yiffy as heck */} + + + ))} {/* End of message mode butts */} + + + {/* Right column, the main meaty potatoe */} + + + + + {/* Top Right Row of Selector Butts */} + {Object.keys(ValidSettings).map((Setting, index) => ( + + + + ))} + + + + {/* Middle Right Row of Settings */} + + {/* Left column */} + + + {LeftColumn.map((Setting, index) => ( + + + + ))} + + + {/* Right column */} + + + {RightColumn.map((Setting, index) => ( + + + + ))} + + + + + + {/* Bottom Right Row, the Preview */} + {/* Surprise surprise, it mangles the formatting */} + {/* Eat a fukcing c0ck, Inferno */} +
+ + + +
+
+
+
+ {/* And the bookend so that the stack reaches the edges */} + {/* Does nothing but the format cant live without it, my spirit animal */} + + ); +}; + + +// SettingBlock is a single row in the settings list +// This is where you set up the message appearance for the selected message mode +const SettingBlock = (props, context) => { + const { act, data } = useBackend(context); + const { + SetBlock, + } = props; + + const { + NumbermalMin = 0, + NumbermalMax = 10, + Clipboard = {}, + } = data; + + const { + Name = "MySpace", + Val = "Tom", + Type = "COLOR", + PKey = "MySpaceTom", + Desc = "Tom shouldn't have sold MySpace", + Default = "Tom", + Options = [], + } = SetBlock; + + // ANATOMY OF SetBlock: + // { + // Name: 'Border Style', + // Val: 'solid', + // Type: 'SELECT', + // Loc: 'L', + // PKey: 'ImageBorderBorderStyle', + // Desc: 'The style of the border for the images.', + // Default: 'solid', + // Options: [ + // 'solid', 'dotted', + // 'dashed', 'double', + // 'groove', 'ridge', + // 'inset', 'outset', + // 'none', 'hidden' + // ] + // } + // Well not quite, cus of Type and Loc. + // Type is either "COLOR", "NUMBER", or "SELECT" + // - COLOR will just open BYOND's (the backend's) lovely color picker + // - NUMBER will be a fancy number draggy thing + // - SELECT will be a dropdown box + // Loc is either "L" or "R" + // The settings box is split into two columns, left and right + // Loc will determine which column the setting will be in + // This will be pre-sorted by us before we render it! yiff yiff + + // Check if we have a valid clipboard entry that's compatible with this setting + // first check if the clipboard entry is in fact an object, and not, say, a string + const HasValidClipboard = Clipboard + && Object.keys(Clipboard).length > 0 + && Clipboard.MsgSetting + && Clipboard.MsgSetting[Type] === Type + && (Type !== "SELECT" || Options.includes(Clipboard.MsgSetting[Val])) + && Clipboard.MsgSetting[Val] !== Val; // so we don't paste the same value + + const [ + SettingsModeSelected, + setSettingsModeSelected, + ] = useLocalState(context, 'SettingsModeSelected', "say"); + + // the copypaste and name slug + const ForePlay = () => { + return ( + + + " + var/m_charlink = "\ +
\ + Examine
" /// DM link - var/m_dmlink = "" + var/m_dmlink = "\ +
\ + DM
" /// Flirt link - var/m_flirtlink = "" + var/m_flirtlink = "\ +
\ + Flirt
" /// Interact link - var/m_interactlink = "" + var/m_interactlink = "\ +
\ + Interact
" /* @@ -547,8 +653,8 @@ SUBSYSTEM_DEF(chat) cum += "" cum += "
" cum += "
" - // now the body - cum += "
" + // now the body - the BottomBox + cum += "
" cum += "

[m_name] [m_verb]

" cum += "

[m_message]

" cum += "
" @@ -556,11 +662,6 @@ SUBSYSTEM_DEF(chat) // now we need to send it to the target return cum.Join() - - - - - // //
// @@ -586,32 +687,27 @@ SUBSYSTEM_DEF(chat) /datum/controller/subsystem/chat/proc/get_horny_pfp(m_rawmessage, list/m_images, m_mode) - var/image2use = "" - var/first_colon = findtext(m_rawmessage, ":") - if(first_colon) - var/list/splittify = splittext(m_rawmessage, ":") - for(var/splut in splittify) - var/testpart = ":[splut]:" - if(findtext(m_rawmessage, testpart)) - if(m_images[testpart]) - var/list/imgz = m_images[testpart] - if(imgz["URL"] != "" && imgz["Host"] != "") - image2use = PfpHostLink(imgz["URL"], imgz["Host"]) - /// then extract the message mode and see if they have a corresponting image - if(!image2use) - var/list/testimages = m_images[m_mode] - if(testimages["URL"] != "" && testimages["Host"] != "") - image2use = PfpHostLink(testimages["URL"], testimages["Host"]) - // just grab their default one - if(!image2use) - var/list/testimages = m_images[MODE_SAY] - if(testimages["URL"] != "" && testimages["Host"] != "") - image2use = PfpHostLink(testimages["URL"], testimages["Host"]) - // if we still dont have one, just use a placeholder - if(!image2use) - image2use = "https://www.placehold.it/100x100.png" - return image2use - + // two-stace bytch of a process: First extract any custom modes, then dive in for modes, then just dump something + var/modeimal = m_mode + var/list/splittify = splittext(m_rawmessage, ":") + if(LAZYLEN(splittify) > 1) + math: + for(var/splut in splittify) + var/testpart = ":[splut]:" + for(var/list/moud in m_images) + if(moud["Mode"] == testpart) + modeimal = testpart + break math // mathematical + // now we have the modeimal, we can get the image! + // they've already been sanitized, maybe + var/fallback_boy = "[default_pfps[MALE]["Host"]]/[default_pfps[MALE]["URL"]]" + for(var/list/maud in m_images) + if(maud["Mode"] == MODE_SAY) + fallback_boy = "[maud["Host"]]/[maud["URL"]]" + if(maud["Mode"] == modeimal) + return "[maud["Host"]]/[maud["URL"]]" // eat my shorts, Jon + // if we get here, we have an unsupported message mode, so just use Say + return fallback_boy // the boy is back in town /datum/controller/subsystem/chat/proc/build_flirt_datums() if(LAZYLEN(flirts)) @@ -834,9 +930,40 @@ SUBSYSTEM_DEF(chat) . = ..() if(href_list["DM"]) start_page(href_list["sender_quid"], href_list["reciever_quid"]) + if(href_list["FLIRT"]) + var/mob/living/flirter = ckey2mob(href_list["sender_quid"]) + if(!flirter) + return + var/mob/living/target = ckey2mob(href_list["reciever_quid"]) + if(!target) + return + if(!flirter.client || !target.client) + return + SSchat.add_flirt_target(flirter, target) + SSchat.ui_interact(flirter) + if(href_list["INTERACT"]) + var/mob/living/fricker = ckey2mob(href_list["sender_quid"]) + if(!fricker) + return + var/mob/living/frackee = ckey2mob(href_list["reciever_quid"]) + if(!frackee) + return + if(!fricker.client || !frackee.client) + return + SEND_SIGNAL(frackee, COMSIG_CLICK_CTRL_SHIFT, fricker) + if(href_list["CHARDIR"]) + var/mob/living/looker = ckey2mob(href_list["sender_quid"]) + if(!looker) + return + var/mob/living/lookee = ckey2mob(href_list["reciever_quid"]) + if(!lookee) + return + if(!looker.client || !lookee.client) + return + looker.true_examinate(lookee) // TRUE examinate EX /datum/controller/subsystem/chat/proc/is_blocked(mob/sender, mob/reciever) - return FALSE // todo: this + return FALSE // if youre enough of a pest to need this, you're probably gonna get banned anyway /datum/controller/subsystem/chat/proc/name_or_shark(mob/they) if(!istype(they)) @@ -854,7 +981,7 @@ SUBSYSTEM_DEF(chat) if(strings("data/super_special_ultra_instinct.json", "[ckey(they.real_name)]", TRUE, TRUE)) return test_name if(they.ckey == "aldrictavalin") // tired of this not working - return test_name + return test_name // good news it still doesnt work, fml return they.client.prefs.my_shark return test_name return safepick(GLOB.cow_names + GLOB.megacarp_first_names + GLOB.megacarp_last_names) @@ -1329,7 +1456,11 @@ SUBSYSTEM_DEF(chat) /// then, the user's images SSchat.SanitizeUserImages(P) SSchat.SanitizeUserPreferences(P) + data["SeeOthers"] = CHECK_PREFS(P, SHOW_ME_HORNY_FURRIES) data["UserImages"] = P.ProfilePics + data["UserCKEY"] = user.ckey + data["Clipboard"] = clipboard.Copy() + data["ValidHosts"] = GLOB.supported_img_hosts /// Also all the previews var/list/previewmsgs = list() /// Anatomy of a msgmode entry in ProfilePics: @@ -1383,6 +1514,8 @@ SUBSYSTEM_DEF(chat) previewmsgs += list(list("Mode" = msgmode, "Message" = msgmess)) data["NumbermalMin"] = SSchat.numbermal_min data["NumbermalMax"] = SSchat.numbermal_max + data["AnglemalMin"] = 0 + data["AnglemalMax"] = 360 data["PreviewHTMLs"] = previewmsgs /// Then, the message appearance settings, its a mmouthful (just like your mom) /// arranged by tab of course of course @@ -1419,7 +1552,7 @@ SUBSYSTEM_DEF(chat) list( "Name" = "Gradient Angle", "Val" = P.mommychat_settings[mmode]["TopBoxGradientAngle"], - "Type" = "NUMBER", + "Type" = "ANGLE", "Loc" = "L", "PKey" = "TopBoxGradientAngle", "Desc" = "The angle of the gradient for the top box.", @@ -1482,7 +1615,7 @@ SUBSYSTEM_DEF(chat) list( "Name" = "Gradient Angle", "Val" = P.mommychat_settings[mmode]["BottomBoxGradientAngle"], - "Type" = "NUMBER", + "Type" = "ANGLE", "Loc" = "L", "PKey" = "BottomBoxGradientAngle", "Desc" = "The angle of the gradient for the bottom box.", @@ -1545,7 +1678,7 @@ SUBSYSTEM_DEF(chat) list( "Name" = "Gradient Angle", "Val" = P.mommychat_settings[mmode]["ButtonGradientAngle"], - "Type" = "NUMBER", + "Type" = "ANGLE", "Loc" = "L", "PKey" = "ButtonGradientAngle", "Desc" = "The angle of the gradient for the buttons.", @@ -1591,7 +1724,7 @@ SUBSYSTEM_DEF(chat) "Name" = "Border Color", "Val" = P.mommychat_settings[mmode]["ImageBorderBorderColor"], "Type" = "COLOR", - "Loc" = "L", + "Loc" = "R", "PKey" = "ImageBorderBorderColor", "Desc" = "The color of the border for the images.", "Default" = "000000" @@ -1600,7 +1733,7 @@ SUBSYSTEM_DEF(chat) "Name" = "Border Width", "Val" = P.mommychat_settings[mmode]["ImageBorderBorderWidth"], "Type" = "NUMBER", - "Loc" = "L", + "Loc" = "R", "PKey" = "ImageBorderBorderWidth", "Desc" = "The width of the border for the images.", "Default" = "1" @@ -1609,7 +1742,7 @@ SUBSYSTEM_DEF(chat) "Name" = "Border Style", "Val" = P.mommychat_settings[mmode]["ImageBorderBorderStyle"], "Type" = "SELECT", - "Loc" = "L", + "Loc" = "R", "PKey" = "ImageBorderBorderStyle", "Desc" = "The style of the border for the images.", "Default" = "solid", @@ -1627,7 +1760,7 @@ SUBSYSTEM_DEF(chat) "Name" = "Border Color", "Val" = P.mommychat_settings[mmode]["OuterBoxBorderColor"], "Type" = "COLOR", - "Loc" = "L", + "Loc" = "R", "PKey" = "OuterBoxBorderColor", "Desc" = "The color of the border for the outer box.", "Default" = "000000" @@ -1636,7 +1769,7 @@ SUBSYSTEM_DEF(chat) "Name" = "Border Width", "Val" = P.mommychat_settings[mmode]["OuterBoxBorderWidth"], "Type" = "NUMBER", - "Loc" = "L", + "Loc" = "R", "PKey" = "OuterBoxBorderWidth", "Desc" = "The width of the border for the outer box.", "Default" = "1" @@ -1645,7 +1778,7 @@ SUBSYSTEM_DEF(chat) "Name" = "Border Style", "Val" = P.mommychat_settings[mmode]["OuterBoxBorderStyle"], "Type" = "SELECT", - "Loc" = "L", + "Loc" = "R", "PKey" = "OuterBoxBorderStyle", "Desc" = "The style of the border for the outer box.", "Default" = "solid", @@ -1665,7 +1798,9 @@ SUBSYSTEM_DEF(chat) * screw it, everything is in ModeName format * * */ - +#define CHANGED_SETTINGS 1 +#define CHANGED_IMAGES 2 +#define CHANGED_NOTHING 3 // COOL /datum/horny_tgui_holder/ui_act(action, list/params) . = ..() if(!params["UserCkey"]) @@ -1677,28 +1812,187 @@ SUBSYSTEM_DEF(chat) if(!P) return switch(action) - if("ModifyProfileEntry") + if("ToggleWhinyLittleBazingaMode") + TOGGLE_BITFIELD(P.chat_toggles, CHAT_SEE_COOLCHAT) + if(CHECK_BITFIELD(P.chat_toggles, CHAT_SEE_COOLCHAT)) + to_chat(M, span_notice("You will now see CoolChat messages!")) + else + to_chat(M, span_notice("You will now see boring normal chat messages!")) + if("OpenPerchance") + var/yiffme = alert( + M, + "This link will take you to an AI furry profile picture generator, hosted at https://perchance.org/furry-ai . \ + Be aware that the site will happily generate NSFW images, so be careful when using it. \ + Proceed?", + "Okay", + "Okay", + "Cancel", + ) + if(yiffme == "Okay") + M << link("https://perchance.org/furry-ai") + to_chat(M, span_notice("Opening Perchance...")) + to_chat(M, span_notice("If you're not redirected, please click here: Perchance")) + . = CHANGED_NOTHING + else + to_chat(M, span_alert("Never mind!!")) + . = CHANGED_NOTHING + if("OpenCatbox") + var/yiffme = alert( + M, + "This link will take you to Catbox.moe, an image hosting site that is popular with the furry community. \ + You can upload images here and use them as profile pictures. Do note that you'll need to write down the \ + URL of the image you upload, as Catbox won't be able to provide it for you after you close the tab. \ + Proceed?", + "Okay", + "Okay", + "Cancel" + ) + if(yiffme == "Okay") + M << link("https://catbox.moe") + to_chat(M, span_notice("Opening Catbox...")) + to_chat(M, span_notice("If you're not redirected, please click here: Catbox")) + . = CHANGED_NOTHING + else + to_chat(M, span_alert("Never mind!!")) + . = CHANGED_NOTHING + if("OpenGyazo") + var/yiffme = alert( + M, + "This link will take you to Gyazo, an image hosting site that both hosts images and also exists. \ + You can upload images here and use them as profile pictures. Proceed?", + "Okay", + "Okay", + "Cancel" + ) + if(yiffme == "Okay") + M << link("https://gyazo.com") + to_chat(M, span_notice("Opening Gyazo...")) + to_chat(M, span_notice("If you're not redirected, please click here: Gyazo")) + . = CHANGED_NOTHING + else + to_chat(M, span_alert("Never mind!!")) + . = CHANGED_NOTHING + if("SaveEverything") + to_chat(M, span_notice("Saving all changes...")) + // all the settings are autosaved, so this is just to make you feel better + if("ModifyHost") var/mode = params["Mode"] - var/newmode = params["NewMode"] - var/host = params["Host"] - var/url = params["URL"] - var/list/ProfilePics = P.ProfilePics // operates directly on the referenced list, hopefully - var/list/fount - for(var/list/entry in ProfilePics) - if(entry["Mode"] == mode) - fount = entry - if(fount) - if(newmode) - fount["Mode"] = newmode - else - fount["Mode"] = mode - fount["Host"] = host - fount["URL"] = url + var/newhost = params["NewHost"] + if(!(newhost in GLOB.supported_img_hosts)) + to_chat(M, span_alert("Unable to modify profile entry for [mode2string(mode)], host is not supported! :c")) + return FALSE + for(var/list/PPc in P.ProfilePics) + if(PPc["Mode"] != mode) + continue + PPc["Host"] = newhost + to_chat(M, span_notice("Host for [mode2string(mode)] has been updated to [newhost]!")) + . = CHANGED_IMAGES + break + if(!.) + to_chat(M, span_alert("Unable to modify profile entry for [mode2string(mode)], entry not found! :c")) + return FALSE else + to_chat(M, span_notice("Profile entry for [mode2string(mode)] has been updated!")) + if("ModifyURL") + var/mode = params["Mode"] + var/newurl = params["NewURL"] + /// rudimar, we need to check if the URL is valid + /// rudimar: we can do that by checking if the URL is a valid URL + if(!newurl || !istext(newurl) || !LAZYLEN(newurl)) + to_chat(M, span_alert("Unable to modify profile entry for [mode2string(mode)], URL cannot be empty! :c")) + return FALSE + var/valid = TRUE + var/list/splittest = splittext(newurl, ".") + if(LAZYLEN(splittest) != 2) + valid = FALSE + if(!(uppertext(splittest[2]) in GLOB.supported_img_exts)) + valid = FALSE + if(!valid) + to_chat(M, span_alert("Unable to modify profile entry for [mode2string(mode)], URL is not valid! :c")) + return FALSE + for(var/list/PPc in P.ProfilePics) + if(PPc["Mode"] != mode) + continue + PPc["URL"] = newurl + to_chat(M, span_notice("URL for [mode2string(mode)] has been updated to [newurl]!")) + . = CHANGED_IMAGES + break + if(!.) + to_chat(M, span_alert("Unable to modify profile entry for [mode2string(mode)], entry not found! :c")) + return FALSE + else + to_chat(M, span_notice("Profile entry for [mode2string(mode)] has been updated!")) + if("ModifyModename") + var/mode = params["Mode"] + var/newname = params["NewName"] + if(mode in SSchat.mandatory_modes) + to_chat(M, span_alert("Unable to modify profile entry for [mode2string(mode)], mode is not renamable! :c")) + return FALSE + newname = ckey(newname) + if(newname == "" || !istext(newname) || !LAZYLEN(newname)) + to_chat(M, span_alert("Unable to modify profile entry for [mode2string(mode)], mode name cannot be empty! :c")) + return FALSE + var/firstie = newname[1] // liek, zomg firstie^^; + var/lastie = newname[LAZYLEN(newname)] + if(firstie != ":") + newname = ":"+newname + if(lastie != ":") + newname = newname+":" + for(var/list/PPc in P.ProfilePics) + if(PPc["Mode"] != mode) + continue + PPc["Mode"] = newname + to_chat(M, span_notice("Mode name for [mode2string(mode)] has been updated to [newname]!")) + . = CHANGED_IMAGES + break + if(!.) to_chat(M, span_alert("Unable to modify profile entry for [mode2string(mode)], entry not found! :c")) return FALSE - to_chat(M, span_notice("Profile entry for [mode2string(mode)] has been updated!")) - . = TRUE + else + to_chat(M, span_notice("Profile entry for [mode2string(mode)] has been updated!")) + if("AddProfileEntry") + var/list/new_entry = list( + "Mode" = ":[safepick(GLOB.ing_verbs)][safepick(GLOB.adverbs)]:", + "Host" = params["Host"], + "URL" = params["URL"], + "Modifiable" = TRUE, + ) + P.ProfilePics += list(new_entry) + to_chat(M, span_notice("Profile entry for [mode2string(new_entry["Mode"])] has been added!")) + . = CHANGED_IMAGES + if("ClearProfileEntry") + var/mode = params["Mode"] + for(var/i in 1 to LAZYLEN(P.ProfilePics)) + var/list/PPc = P.ProfilePics[i] + if(PPc["Mode"] != mode) + continue + if(mode in SSchat.mandatory_modes) + // just reset it + switch(P.gender) + if(MALE) + PPc["Host"] = SSchat.default_pfps[MALE]["Host"] + PPc["URL"] = SSchat.default_pfps[MALE]["URL"] + if(FEMALE) + PPc["Host"] = SSchat.default_pfps[FEMALE]["Host"] + PPc["URL"] = SSchat.default_pfps[FEMALE]["URL"] + else + if(prob(50)) + PPc["Host"] = SSchat.default_pfps[MALE]["Host"] + PPc["URL"] = SSchat.default_pfps[MALE]["URL"] + else + PPc["Host"] = SSchat.default_pfps[FEMALE]["Host"] + PPc["URL"] = SSchat.default_pfps[FEMALE]["URL"] + to_chat(M, span_notice("Profile entry for [mode2string(mode)] has been reset!")) + . = CHANGED_IMAGES + break + else + P.ProfilePics.Cut(i, i+1) // surgical and sexy + to_chat(M, span_notice("Profile entry for [mode2string(mode)] has been removed!")) + . = CHANGED_IMAGES + break + if(!.) + to_chat(M, span_alert("Unable to clear profile entry for [mode2string(mode)], entry not found! :c")) + return FALSE /// hey github copilot, what is your favorite color of fox? /// "I like /// the color of @@ -1725,26 +2019,23 @@ SUBSYSTEM_DEF(chat) ) clipboard["ProfilePic"] = clip to_chat(M, span_notice("Profile entry for [mode2string(mode)] has been copied to the clipboard!")) - . = TRUE + . = CHANGED_IMAGES else to_chat(M, span_alert("Unable to copy profile entry for [mode2string(mode)], entry not found! :c")) return FALSE if("PasteImage") - var/mode = params["Mode"] - var/list/ProfilePics = P.ProfilePics + var/mode = params["Mode"] // the mode it will be pasted to var/list/clip = clipboard["ProfilePic"] if(clip) - var/list/fount - for(var/list/entry in ProfilePics) - if(entry["Mode"] == mode) - fount = entry - if(fount) - fount["Mode"] = clip["Mode"] - fount["Host"] = clip["Host"] - fount["URL"] = clip["URL"] + for(var/i in 1 to LAZYLEN(P.ProfilePics)) + var/list/entry = P.ProfilePics[i] + if(entry["Mode"] != mode) // find the entry to replace, and overwrite it + continue + P.ProfilePics[i]["Host"] = clip["Host"] + P.ProfilePics[i]["URL"] = clip["URL"] to_chat(M, span_notice("Profile entry for [mode2string(mode)] has been pasted from the clipboard!")) - . = TRUE - else + . = CHANGED_IMAGES + if(. != CHANGED_IMAGES) to_chat(M, span_alert("Unable to paste profile entry for [mode2string(mode)], entry not found! :c")) return FALSE else @@ -1780,7 +2071,7 @@ SUBSYSTEM_DEF(chat) else ProfilePics -= fount to_chat(M, span_notice("Profile entry for [mode2string(mode)] has been removed!")) - . = TRUE + . = CHANGED_IMAGES else to_chat(M, span_alert("Unable to clear profile entry for [mode2string(mode)], entry not found! :c")) return FALSE @@ -1791,27 +2082,48 @@ SUBSYSTEM_DEF(chat) if(!(pisskey in SSchat.colorable_keys)) to_chat(M, span_alert("Unable to edit color for [mode2string(mode)], mode is not colorable! This is probably a bug :c")) return FALSE + var/list/geosites = list( + "Tripod like a real AngelFire", + "Lycos like a real Tripod", + "Your free website from Geocities", + "MySpace up your webring", + "Your free trial of Dreamweaver 4 has expired", + "Navigate this Netscape in style", + "Your free trial of FrontPage 98 has expired", + "Web 2.0 like a real Web 1.0", + "Altavista like a real AskJeeves", + "Dear LiveJournal, I'm sorry I left you for Tumblr", + "Eudora like a real Outlook Express", + "Welcome to the World Wide Web", + "You're the 1,000,000th visitor to this site!", + "Website under construction", + "Guaranteed websafe colors", + "Best viewed in Internet Explorer 4", + "Best viewed in Netscape Navigator 4", + "VCL like a real Elfwood", + ) // Gradually, the web 1.0 sites are disappearing. But they're still here, in our hearts and yiffy posts var/val = input( M, "Enter a new color for the [pisskey] of the [mode2string(mode)]!", - "Tripod like a real AngelFire", + "[safepick(geosites)]", current ) as color|null if(!istext(val)) to_chat(M, span_alert("Nevermind!!")) + return FALSE P.mommychat_settings[mode][pisskey] = val - to_chat(M, span_notice("Color for [mode2string(mode)] [pisskey] has been updated to #[val]!")) - . = TRUE + to_chat(M, span_notice("Color for [mode2string(mode)] [pisskey] has been updated to [val]!")) + . = CHANGED_SETTINGS if("EditNumber") // TGUI handled this one var/pisskey = params["PKey"] var/mode = params["Mode"] var/newval = params["Val"] - if(!(pisskey in SSchat.numberable_keys)) + if(!(pisskey in SSchat.numberable_keys) && !(pisskey in SSchat.angleable_keys)) to_chat(M, span_alert("Unable to edit number for [mode2string(mode)], mode is not numberable! This is probably a bug :c")) return FALSE P.mommychat_settings[mode][pisskey] = newval to_chat(M, span_notice("Number for [mode2string(mode)] [pisskey] has been updated to [newval]!")) - . = TRUE + . = CHANGED_SETTINGS if("EditSelect") // TGUI handled this one var/pisskey = params["PKey"] var/mode = params["Mode"] @@ -1821,24 +2133,24 @@ SUBSYSTEM_DEF(chat) return FALSE P.mommychat_settings[mode][pisskey] = newval to_chat(M, span_notice("Select for [mode2string(mode)] [pisskey] has been updated to [newval]!")) - . = TRUE + . = CHANGED_SETTINGS if("CopySetting") - var/pisskey = params["PKey"] + var/pisskey = params["PKey"] // the setting key var/mode = params["Mode"] + var/typekind = params["Type"] var/list/horny_settings = P.mommychat_settings - var/list/fount - for(var/list/entry in horny_settings[mode]) - if(entry["PKey"] == pisskey) - fount = entry - if(fount) + var/list/hornier_settings = LAZYACCESS(horny_settings, mode) + var/found_value = LAZYACCESS(hornier_settings, pisskey) + if(!isnull(found_value)) var/list/clip = list( - "Mode" = fount["Mode"], - "PKey" = pisskey, - "Val" = fount[pisskey], + "Mode" = "[mode]", + "PKey" = "[pisskey]", + "Type" = "[typekind]", + "Val" = found_value, ) clipboard["MsgSetting"] = clip to_chat(M, span_notice("Setting for [mode2string(mode)] [pisskey] has been copied to the clipboard!")) - . = TRUE + . = CHANGED_SETTINGS else to_chat(M, span_alert("Unable to copy setting for [mode2string(mode)] [pisskey], setting not found! :c")) return FALSE @@ -1851,16 +2163,79 @@ SUBSYSTEM_DEF(chat) // first check if this setting can accept the value if((clip["PKey"] in SSchat.colorable_keys) && (pisskey in SSchat.colorable_keys)\ || (clip["PKey"] in SSchat.numberable_keys) && (pisskey in SSchat.numberable_keys)\ - || (clip["PKey"] in SSchat.selectable_keys) && (pisskey in SSchat.selectable_keys)) + || (clip["PKey"] in SSchat.selectable_keys) && (pisskey in SSchat.selectable_keys)\ + || (clip["PKey"] in SSchat.angleable_keys) && (pisskey in SSchat.angleable_keys)) horny_settings[mode][pisskey] = clip["Val"] to_chat(M, span_notice("Setting for [mode2string(mode)] [pisskey] has been pasted from the clipboard!")) - . = TRUE + . = CHANGED_SETTINGS else to_chat(M, span_alert("Unable to paste setting for [mode2string(mode)] [pisskey], setting type mismatch! :c")) return FALSE else to_chat(M, span_alert("Unable to paste setting for [mode2string(mode)] [pisskey], clipboard is empty! :c")) return FALSE + if("CopyModeSettings") + var/mode = params["Mode"] + var/list/horny_settings = P.mommychat_settings + var/list/hornier_settings = LAZYACCESS(horny_settings, mode) + if(LAZYLEN(hornier_settings)) // dont need to check too hard, just make sure its valid + clipboard["MessageAppearance"] = mode + to_chat(M, span_notice("Settings for [mode2string(mode)] have been copied to the clipboard!")) + . = CHANGED_SETTINGS + else + to_chat(M, span_alert("Unable to copy settings for [mode2string(mode)], mode not found! :c")) + return FALSE + if("PasteModeSettings") + var/mode = params["Mode"] // mode that will be pasted to + var/list/horny_settings = P.mommychat_settings + var/clipmode = clipboard["MessageAppearance"] + if(clipmode) + var/list/clipsettings = LAZYACCESS(horny_settings, clipmode) + if(LAZYLEN(clipsettings) == LAZYLEN(horny_settings[mode])) + horny_settings[mode] = clipsettings + to_chat(M, span_notice("Settings for [mode2string(mode)] have been pasted from the clipboard!")) + . = CHANGED_SETTINGS + else + to_chat(M, span_alert("Unable to paste settings for [mode2string(mode)], setting verification can mismatch! :c")) + return FALSE + else + to_chat(M, span_alert("Unable to paste settings for [mode2string(mode)], clipboard is empty! :c")) + return FALSE + if(.) + update_static_data(M) + if(. != CHANGED_NOTHING) + // we need to update the settings and pics to match + CoordinateSettingsAndPics(P, .) + P.save_character() + +/// GEE DAN, LETS MAKE PROFILE PICS AND SETTINGS BE TWO ENTIRELY SEPARATE LISTS +/// WHAT A GREAT IDEA, NO WAY THIS WOULD CAUSE DISCREPANCIES you fukcking dikc +/// This proc comapres mommychat_settings to ProfilePics and updates the two to match +/datum/horny_tgui_holder/proc/CoordinateSettingsAndPics(someone, whichchanged) + var/datum/preferences/P = extract_prefs(someone) + if(!P) + return + if(whichchanged != CHANGED_IMAGES && whichchanged != CHANGED_SETTINGS) + return + var/list/horny_settings = P.mommychat_settings + var/list/ProfilePics = P.ProfilePics + if(whichchanged == CHANGED_IMAGES) + // The images were changed, time to go through the settings and make sure it has this mode for(var/list/PPc in ProfilePics) + for(var/list/PPc in ProfilePics) + var/foundit = LAZYLEN(LAZYACCESS(P.mommychat_settings, PPc["Mode"])) + if(!foundit) + P.mommychat_settings[PPc["Mode"]] = GLOB.default_horny_settings.Copy() + P.mommychat_settings = horny_settings + + + + + + + + + + /datum/horny_tgui_holder/proc/mode2string(mode) switch(mode) @@ -1878,4 +2253,49 @@ SUBSYSTEM_DEF(chat) return "Yell" return "[mode]" +#undef CHANGED_SETTINGS +#undef CHANGED_IMAGES +#undef CHANGED_NOTHING + +/* todo: this +/// Say it with me, "Anything a frickhuge list can do, a datum can do better" +/datum/horny_setting + var/title // the title of the setting + var/tooltip // the description of the setting + var/group // the group of the setting + var/location // the location of the setting + var/setting_key // the key of the setting + var/setting_type // the type of the setting + var/setting_default // the default value of the setting + var/setting_options // the options for the setting + var/num_max // the maximum number for the setting + var/num_min // the minimum number for the setting + var/default // the default value of the setting + +/datum/horny_setting/New() + . = ..() + SSchat.horny_settings[group] = src + +/datum/horny_setting/proc/tgui_slug(list/mommy, mode) + if(!LAZYLEN(mommy) || !mode) + return + var/list/data = list() + var/list/setformode = LAZYACCESS(mommy, mode) + var/current = LAZYACCESS(setformode, setting_key) + data["Name"] = title + data["Tooltip"] = tooltip + data["Group"] = group + data["Loc"] = location + data["PKey"] = setting_key + data["Type"] = setting_type + data["Default"] = setting_default + data["Options"] = setting_options + data["NumMax"] = num_max + data["NumMin"] = num_min + data["Val"] = current + return data + */ + + + diff --git a/code/controllers/subsystem/prefbreak.dm b/code/controllers/subsystem/prefbreak.dm index e5f9c78835..333fcbb584 100644 --- a/code/controllers/subsystem/prefbreak.dm +++ b/code/controllers/subsystem/prefbreak.dm @@ -266,7 +266,7 @@ SUBSYSTEM_DEF(prefbreak) // ALL ABOARD THE S.S. PREFBREAK OFF TO **** YOUR ***** /datum/prefcheck/see_horny_furry_stuff/allowed(datum/preferences/consumer) PREFBROKEN - return TRUE + return CHECK_BITFIELD(consumer.chat_toggles, CHAT_SEE_COOLCHAT) // kinda vital here // return consumer.see_fancy_offscreen_runechat // kinda vital here diff --git a/code/game/say.dm b/code/game/say.dm index 20dedff595..c075aeeba9 100644 --- a/code/game/say.dm +++ b/code/game/say.dm @@ -106,21 +106,27 @@ And the base of the send_speech() proc, which is the core of saycode. /atom/movable/proc/compose_job(atom/movable/speaker, message_langs, raw_message, radio_freq) return "" -/atom/movable/proc/say_mod(input, message_mode) +/atom/movable/proc/say_mod(input, message_mode, datum/rental_mommy/chat/momchat) var/ending = copytext_char(input, -1) var/beginning = copytext_char(input, 1) if(message_mode == MODE_WHISPER || beginning == "#") . = verb_whisper + momchat?.message_mode = MODE_WHISPER else if(message_mode == MODE_SING) . = verb_sing + momchat?.message_mode = MODE_SING else if(copytext_char(input, -2) == "!!") . = verb_yell + momchat?.message_mode = MODE_YELL else if(ending == "?") . = verb_ask + momchat?.message_mode = MODE_ASK else if(ending == "!") . = verb_exclaim + momchat?.message_mode = MODE_EXCLAIM else if(beginning == "$") . = verb_yell + momchat?.message_mode = MODE_YELL else . = verb_say return get_random_if_list(.) @@ -138,7 +144,7 @@ And the base of the send_speech() proc, which is the core of saycode. if(momchat) momchat.message_langtreated_spanned = spanned momchat.message_langtreated_spanned_quotes = "\"[spanned]\"" - var/saymod = say_mod(input, message_mode) + var/saymod = say_mod(input, message_mode, momchat) if(momchat) momchat.message_saymod = saymod if(spanned) diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index c3db571502..146e383a58 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -247,9 +247,9 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/eye_type = DEFAULT_EYES_TYPE //Eye type var/split_eye_colors = FALSE var/tbs = TBS_DEFAULT // turner broadcasting system - var/kisser = KISS_DEFAULT // Kiss this / V \/ - /// which quester UID we're using | | | - var/quester_uid // (_______|_______) + var/kisser = KISS_DEFAULT // Kiss this / V \. + /// which quester UID we're using ( | ). + var/quester_uid // (__________) (__________) var/dm_open = TRUE var/needs_a_friend = FALSE // for the quest var/list/blocked_from_dms = list() // list of quids @@ -300,6 +300,11 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/creature_fuzzy = FALSE var/list/ProfilePics = list( + list( + "Mode" = MODE_PROFILE_PIC, + "Host" = "", + "URL" = "", + ), list( "Mode" = MODE_SAY, "Host" = "", @@ -578,6 +583,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(jobban_isbanned(user, "appearance")) dat += "You are banned from using custom names and appearances. You can continue to adjust your characters, but you will be randomised once you join the game.
" + dat += "Configure CoolChat / Profile Pictures!
" dat += "Name: " dat += "[real_name]
" @@ -604,8 +610,10 @@ GLOBAL_LIST_EMPTY(preferences_datums) */ //Right column dat +="" - dat += "

Profile Picture ([pfphost]):


" - dat += "Picture: [profilePicture ? "" : "Upload a picture!"]
" + dat += "Configure CoolChat / Profile Pictures!
" + // dat += "

Profile Picture ([pfphost]):


" + var/pfplink = SSchat.GetPicForMode(user, MODE_PROFILE_PIC) + dat += "Picture: [pfplink ? "" : "Upload a picture!"]
" dat += "" /* dat += "Special Names:
" @@ -1049,6 +1057,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += APPEARANCE_CATEGORY_COLUMN dat += "

Flavor Text

" dat += "Set Examine Text
" + dat += "Configure CoolChat / Profile Pictures!
" if(length(features["flavor_text"]) <= 40) if(!length(features["flavor_text"])) dat += "\[...\]" @@ -3733,6 +3742,9 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(new_toggle_color) hud_toggle_color = new_toggle_color + if("setup_hornychat") + SSchat.HornyPreferences(user) + if("gender") var/chosengender = input(user, "Select your character's gender.", "Gender Selection", gender) as null|anything in list(MALE,FEMALE,"nonbinary","object") if(!chosengender) diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index aed7ab371c..22698d8284 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -53,6 +53,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car return TRUE for(var/clog in missing_updates) switch(clog) + if(PMR_ADDED_COOLCHAT) // i broke it =3 + S["chat_toggles"] >> chat_toggles + chat_toggles |= CHAT_SEE_COOLCHAT + chat_toggles = sanitize_integer(chat_toggles, 0, INFINITY, TOGGLES_DEFAULT_CHAT) + current_revision |= PMR_ADDED_COOLCHAT if(PMR_ADDED_RADIO_BLURBLES) // i broke it =3 S["chat_toggles"] >> chat_toggles chat_toggles |= CHAT_HEAR_RADIOBLURBLES @@ -909,6 +914,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car // !! COYOTE SAVE FILE STUFF !! S["profilePicture"] >> profilePicture // Profile picklies S["pfphost"] >> pfphost + // !! DAN IS COOL SAVE FILE STUFF !! + var/list/pfp_list = safe_json_decode(S["ProfilePics"]) + ProfilePics = islist(pfp_list) ? pfp_list : list() + var/list/milfhub = safe_json_decode(S["mommychat_settings"]) + mommychat_settings = islist(milfhub) ? milfhub : list() S["gradient_color"] >> features_override["grad_color"] // Hair gradients! S["gradient_style"] >> features_override["grad_style"] // Hair gradients electric boogaloo 2!! @@ -1572,6 +1582,13 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car // !! COYOTE SAVEFILE STUFF !! WRITE_FILE(S["profilePicture"], profilePicture) WRITE_FILE(S["pfphost"], pfphost) + // !! DEER GETS EATEN BY COYOTE !! + var/pfpjson = safe_json_encode(ProfilePics) + if(pfpjson) + WRITE_FILE(S["ProfilePics"], pfpjson) + var/milfjson = safe_json_encode(mommychat_settings) + if(milfjson) + WRITE_FILE(S["mommychat_settings"], milfjson) WRITE_FILE(S["creature_profilepic"], creature_profilepic) WRITE_FILE(S["creature_pfphost"], creature_pfphost) diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm index 5421cf9680..1eac123b15 100644 --- a/code/modules/mob/dead/new_player/new_player.dm +++ b/code/modules/mob/dead/new_player/new_player.dm @@ -58,6 +58,8 @@ output += "

Click here to do something about that!

" else output += "

Configure Quirks!

" + output += "

Configure CoolChat!

" + output += "

Configure Profile Pics!

" if(SSticker.current_state <= GAME_STATE_PREGAME) output += "

Please be patient, the game is starting soon!

" @@ -65,7 +67,7 @@ output += "

(Fix Chat Window)

" output += "

(Fit Viewport)

" else - output += "

View Character Directory

" + // output += "

View Character Directory

" output += "

Join in!

" //output += "

[LINKIFY_READY("Observe", PLAYER_READY_TO_OBSERVE)]

" output += "

(Fix Chat Window)

" @@ -76,7 +78,7 @@ output += "
" - var/datum/browser/popup = new(src, "playersetup", "
Game Preferences
", 250, 400) + var/datum/browser/popup = new(src, "playersetup", "
Game Preferences
", 300, 600) popup.set_window_options("can_close=0") popup.set_content(output.Join()) popup.open(FALSE) @@ -175,6 +177,10 @@ client.prefs.ShowChoices(src) return 1 + if(href_list["show_hornychat"]) + SSchat.HornyPreferences(src) + return 1 + if(href_list["directory"]) client.show_character_directory() return 1 diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index 9e76d79405..2178eb3ccc 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -96,8 +96,9 @@ GLOBAL_LIST_INIT(personalitytrait2description, list( . = list("*---------*\nThis is [!obscure_name ? name : "Unknown"]!") - if (profilePicture) - . += "" + var/imagelink = SSchat.GetPicForMode(src, MODE_PROFILE_PIC) + if (imagelink) + . += "" var/vampDesc = ReturnVampExamine(user) // Vamps recognize the names of other vamps. diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 76f95e39eb..e943b69a1b 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -339,7 +339,7 @@ GLOBAL_VAR_INIT(crotch_call_cooldown, 0) // Gremling is just gonna do gremlin things and add this here > w> Cant be assed trying to fit this in somewhere else for now. if(href_list["enlargeImage"]) - var/dat = {""} + var/dat = {""} var/datum/browser/popup = new(usr, "enlargeImage", "Full Sized Picture!",1024,1024) popup.set_content(dat) popup.open() diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 1c6c7702eb..7c932f8e3b 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -685,7 +685,7 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0) usr << browse(text("[][]", name, replacetext(oocnotes, "\n", "
")), text("window=[];size=500x200", name)) onclose(usr, "[name]") if(href_list["enlargeImageCreature"]) - var/followers_clinic_full_of_big_strong_gay_dogs_in_it = PfpHostLink(profilePicture) + var/followers_clinic_full_of_big_strong_gay_dogs_in_it = SSchat.GetPicForMode(src, MODE_PROFILE_PIC) var/dat = {"
diff --git a/tgui/packages/tgui/interfaces/HornyChat.js b/tgui/packages/tgui/interfaces/HornyChat.js index 59c89c832a..bfcbe3869f 100644 --- a/tgui/packages/tgui/interfaces/HornyChat.js +++ b/tgui/packages/tgui/interfaces/HornyChat.js @@ -16,6 +16,7 @@ import { NoticeBox, NumberInput, Icon, + Knob, Stack, Fragment, Table, @@ -255,7 +256,8 @@ const MainWindow = (props, context) => { const LowerRowBar = (props, context) => { const { act, data } = useBackend(context); const { - UserCkey, + UserCKEY, + SeeOthers, } = data; return ( @@ -267,7 +269,9 @@ const LowerRowBar = (props, context) => { fluid icon="images" content="Get Profile Pics" - onClick={() => act('OpenStockPicker')} /> + onClick={() => act('OpenPerchance', { + UserCkey: UserCKEY, + })} /> {/* A link to catbox.moe, where you can upload your own images */} @@ -275,7 +279,9 @@ const LowerRowBar = (props, context) => { fluid icon="upload" content="Upload Images (to Catbox.moe)" - onClick={() => act('OpenCatbox')} /> + onClick={() => act('OpenCatbox', { + UserCkey: UserCKEY, + })} /> {/* A link to gyazo, another place to upload images */} @@ -283,7 +289,23 @@ const LowerRowBar = (props, context) => { fluid icon="upload" content="Upload Images (to Gyazo)" - onClick={() => act('OpenGyazo')} /> + onClick={() => act('OpenGyazo', { + UserCkey: UserCKEY, + })} /> + + + {/* A link to gyazo, another place to upload images */} +
" - cum += "
" + if(giv_head) + cum += "
" + // now the profile picture + cum += "
" + cum += "" + cum += "
" + // now the rest of the head + cum += "
" + cum += "[m_name]" // already formatted! + // now the button panel + cum += "" + cum += "" + cum += "" + cum += "" + cum += "" + cum += "" + cum += "" + cum += "" + cum += "" + cum += "
" + cum += m_charlink + cum += "" + cum += m_dmlink + cum += "
" + cum += m_flirtlink + cum += "" + cum += m_interactlink + cum += "
" + cum += "
" + cum += "
" // now the body - the BottomBox - cum += "
" - cum += "

[m_name] [m_verb]

" - cum += "

[m_message]

" - cum += "
" + if(giv_body) + cum += "
" + cum += "

[m_name] [m_verb]

" + cum += "

[m_message]

" + cum += "
" cum += "
" // now we need to send it to the target return cum.Join() @@ -1476,7 +1513,8 @@ SUBSYSTEM_DEF(chat) data["UserImages"] = P.ProfilePics data["UserCKEY"] = user.ckey data["Clipboard"] = clipboard.Copy() - data["ValidHosts"] = GLOB.supported_img_hosts + var/list/vhosts = assoc_list_strip_value(GLOB.supported_img_hosts) + data["ValidHosts"] = vhosts /// Also all the previews var/list/previewmsgs = list() /// Anatomy of a msgmode entry in ProfilePics: @@ -1920,17 +1958,30 @@ SUBSYSTEM_DEF(chat) return FALSE var/valid = TRUE var/list/splittest = splittext(newurl, ".") - if(LAZYLEN(splittest) != 2) - valid = FALSE - if(!(uppertext(splittest[2]) in GLOB.supported_img_exts)) + if(!(uppertext(LAZYACCESS(splittest,LAZYLEN(splittest))) in GLOB.supported_img_exts)) valid = FALSE if(!valid) to_chat(M, span_alert("Unable to modify profile entry for [mode2string(mode)], URL is not valid! :c")) return FALSE + /// rudimar, we need to check if the URL is a valid URL + /// actually we dont, but im just gonna filter out any domains that got pasted in + /// rudimar: oh, okay // also extract the domain from the URL, if it exists + var/newhost + spank: + for(var/hoste in GLOB.supported_img_hosts) + var/list/checkemup = list(hoste) + GLOB.supported_img_hosts[hoste] + for(var/checc in checkemup) + if(findtext(newurl, checc)) + var/list/splup = splittext(newurl, "/") + newurl = LAZYACCESS(splup, LAZYLEN(splup)) // get the last part of the URL + newhost = hoste + break spank // thats going in the spank break for(var/list/PPc in P.ProfilePics) if(PPc["Mode"] != mode) continue PPc["URL"] = newurl + if(!isnull(newhost)) + PPc["Host"] = newhost // UX SUPREME to_chat(M, span_notice("URL for [mode2string(mode)] has been updated to [newurl]!")) . = CHANGED_IMAGES break @@ -2252,16 +2303,6 @@ SUBSYSTEM_DEF(chat) P.mommychat_settings[PPc["Mode"]] = GLOB.default_horny_settings.Copy() P.mommychat_settings = horny_settings - - - - - - - - - - /datum/horny_tgui_holder/proc/mode2string(mode) switch(mode) if(MODE_SAY) diff --git a/code/controllers/subsystem/datumrentals.dm b/code/controllers/subsystem/datumrentals.dm index 846cabc2a9..6abd480e93 100644 --- a/code/controllers/subsystem/datumrentals.dm +++ b/code/controllers/subsystem/datumrentals.dm @@ -130,6 +130,7 @@ SUBSYSTEM_DEF(rentaldatums) var/source_quid var/source_ckey var/datum/preferences/prefs_override + var/dummy /datum/rental_mommy/chat/copy_mommy(datum/rental_mommy/chat/mommy) if(!..()) @@ -183,6 +184,7 @@ SUBSYSTEM_DEF(rentaldatums) source_quid = mommy.source_quid source_ckey = mommy.source_ckey prefs_override = mommy.prefs_override + dummy = mommy.dummy /datum/rental_mommy/chat/wipe() original_message = "" @@ -234,5 +236,5 @@ SUBSYSTEM_DEF(rentaldatums) source_quid = null source_ckey = null prefs_override = null - + dummy = null diff --git a/code/controllers/subsystem/dummies.dm b/code/controllers/subsystem/dummies.dm index 40e51fe258..bc5aa04971 100644 --- a/code/controllers/subsystem/dummies.dm +++ b/code/controllers/subsystem/dummies.dm @@ -314,6 +314,7 @@ SUBSYSTEM_DEF(dummy) // who ya callin dummy, dummy? real_name = "Test Dummy" status_flags = GODMODE|CANPUSH mouse_drag_pointer = MOUSE_INACTIVE_POINTER + put_away_them_grippers_hooh_you_got_them_grippers = TRUE var/in_use = FALSE var/dummyckey COOLDOWN_DECLARE(unuse_timer) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index b914336286..678fbfc1b7 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -7,6 +7,7 @@ GLOBAL_VAR_INIT(crotch_call_cooldown, 0) icon = 'icons/mob/human.dmi' icon_state = "caucasian_m" appearance_flags = KEEP_TOGETHER|TILE_BOUND|PIXEL_SCALE|LONG_GLIDE + var/put_away_them_grippers_hooh_you_got_them_grippers = 0 var/potato = FALSE /mob/living/carbon/human/twoman @@ -39,7 +40,8 @@ GLOBAL_VAR_INIT(crotch_call_cooldown, 0) physiology = new() AddComponent(/datum/component/personal_crafting) - AddComponent(/datum/component/footstep, FOOTSTEP_MOB_HUMAN, 0.3, 5) + if(!put_away_them_grippers_hooh_you_got_them_grippers) + AddComponent(/datum/component/footstep, FOOTSTEP_MOB_HUMAN, 0.3, 5) . = ..() if(CONFIG_GET(flag/disable_stambuffer)) diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index 8f2006d228..9fac09a828 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -57,11 +57,16 @@ var/original_message = message var/in_critical = InCritical() - if(one_character_prefix[message_mode]) - message = copytext_char(message, 2) - else if(message_mode || saymode) - message = copytext_char(message, 3) - message = trim_left(message) + /// a regex that will look for a word surrounded by colons, such as :bingus: or :wamfosm: . Spaces are not allowed in the word. + var/regex/colonizer_regex = regex(@":(\w+):", "g") + var/is_colone = colonizer_regex.Find(message) + + if(!is_colone) + if(one_character_prefix[message_mode]) + message = copytext_char(message, 2) + else if(message_mode || saymode) + message = copytext_char(message, 2) + message = trim(message) if(!message) return if(message_mode == MODE_ADMIN) @@ -578,6 +583,9 @@ . = ..() /mob/proc/should_hornify(datum/rental_mommy/chat/mommy) + return FALSE // quit seducing ghosts in the lobby, wacky + +/mob/living/should_hornify(datum/rental_mommy/chat/mommy) if(!mommy) return FALSE if(!ishuman(mommy.source)) @@ -589,7 +597,7 @@ return FALSE if(z != H.z) return FALSE - if(get_dist(H, src) > 1) //If they're not right next to you, don't hornify them + if(get_dist(H, src) > SSchat.max_horny_distance) //If they're not right next to you, don't hornify them return FALSE return TRUE // lets get yiffy diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index bfc7427ef6..c96b42aa98 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -218,3 +218,6 @@ /// if we did anything hostile, let us get attacked in crit, until we take this much more damage var/in_crit_HP_penalty = 0 + + /// FORMAT: list("quid" = list("last_heard" = time, "message_mode" = MODE_SAY), etc) + var/list/heard_data = list() diff --git a/tgui/packages/tgui/interfaces/HornyChat.js b/tgui/packages/tgui/interfaces/HornyChat.js index 534b6fd739..ff05ccba6e 100644 --- a/tgui/packages/tgui/interfaces/HornyChat.js +++ b/tgui/packages/tgui/interfaces/HornyChat.js @@ -446,27 +446,25 @@ const ProfilePicsTab = (props, context) => { // we need to calculate the longest message mode, and set the width to that // plus a bit of padding cus why not const LongestMode = UserImages.reduce((a, b) => a.Mode.length > b.Mode.length ? a : b); - const ModeWidth = `${(LongestMode.Mode.length) * 0.80}em`; + const ModeWidth = `${(LongestMode.Mode.length) * 0.55}em`; // same for the host column, it is *short*, too short for the hosts const LongestHost = ValidHosts.reduce((a, b) => a.length > b.length ? a : b); - const HostWidth = `${(LongestHost.length) * 0.75}rem`; + const HostWidth = `${(LongestHost.length) * 0.65}rem`; return ( - +
{/* Column names */} - - + Message Mode - Host + Image Link - + {/* Filename - - + */} + Preview @@ -478,26 +476,6 @@ const ProfilePicsTab = (props, context) => { key={index} {...CellStyle(PFPentry.Mode, index)}> {/* Copy */} - -
" - cum += "" - cum += "" - cum += "" - cum += "" - cum += "" - cum += "" - cum += "" - cum += "" - cum += "
" - cum += m_charlink - cum += "" - cum += m_dmlink - cum += "
" - cum += m_flirtlink - cum += "" - cum += m_interactlink - cum += "
" - cum += "
" + cum += "
" + cum += "x.x;" cum += "
" + // cum += "
" + // now the body - the BottomBox if(giv_body) - cum += "
" - cum += "

[m_name] [m_verb]

" - cum += "

[m_message]

" + cum += "
" + cum += "

[m_name] [m_verb]

" + if(!nomessage) + cum += "

[m_message]

" cum += "
" cum += "
" // now we need to send it to the target return cum.Join() - // - //
- // - //
- // - //
- // - //
- // - // Foxxxy Vixen - //
- // - // - // - // - //
- //
- //
- // - //
- //

Foxxxy Vixen asks,

- //

Hey there! How's it going? I was thinking we could go on a date sometime. What do you say?

- - -/datum/controller/subsystem/chat/proc/get_horny_pfp(m_rawmessage, list/m_images, m_mode) + +/datum/controller/subsystem/chat/proc/get_horny_pfp(m_rawmessage, list/m_images, m_mode, list/imglist) // two-stace bytch of a process: First extract any custom modes, then dive in for modes, then just dump something var/modeimal = m_mode // now we have the modeimal, we can get the image! @@ -748,6 +758,7 @@ SUBSYSTEM_DEF(chat) if(maud["Mode"] == MODE_SAY) fallback_boy = "[maud["Host"]]/[maud["URL"]]" if(maud["Mode"] == modeimal) + imglist |= maud return "[maud["Host"]]/[maud["URL"]]" // eat my shorts, Jon // if we get here, we have an unsupported message mode, so just use Say return fallback_boy // the boy is back in town @@ -1083,7 +1094,7 @@ SUBSYSTEM_DEF(chat) return TRUE /mob/verb/setup_coolchat() - set name = "Setup CoolChat" + set name = "Setup VisualChat" set category = "Preferences" SSchat.HornyPreferences(src) @@ -1513,6 +1524,8 @@ SUBSYSTEM_DEF(chat) data["UserImages"] = P.ProfilePics data["UserCKEY"] = user.ckey data["Clipboard"] = clipboard.Copy() + data["imgsize"] = SSchat.img_size + data["UserName"] = P.real_name var/list/vhosts = assoc_list_strip_value(GLOB.supported_img_hosts) data["ValidHosts"] = vhosts /// Also all the previews @@ -1534,6 +1547,7 @@ SUBSYSTEM_DEF(chat) for(var/list/modus in P.ProfilePics) var/msgmode = modus["Mode"] var/message2say = "Hi." + var/message2say2 switch(msgmode) if(MODE_SAY) message2say = "Hello! You are hearing me talk. I am saying words. I am saying words to you. \ @@ -1560,11 +1574,16 @@ SUBSYSTEM_DEF(chat) AAAAAAAAA A AAA AAAAAAAAAAA AAAAAAAAAA AAAAAAAAAA AAA AAAAAAA AAAAAA AAAA A A AA AAAAAAAA AAA AAAA AAAA \ AA A A AA A AAA A A AAAAAAAAAAAAAA AAA A A AAA AAAAAAAAAAAA A AAAA A A A AA AAAAAAAAAAAAAAAAA AAAAAA \ AND NOW IM DONE!! HI!!" + if(MODE_PROFILE_PIC) + continue // hi else message2say = "Hi, this is a test of a custom message mode that has been set by you to be used to display \ a custom message mode. The mode is set to [replacetext(msgmode, ":","")]. If the previous sentence was \ cut off, please make a note of it. Cool huh? And now I'm done. Hi. [msgmode]" + message2say2 = "[msgmode]" var/msgmess = SSchat.PreviewHornyFurryDatingSimMessage(user, null, message2say, FALSE) + if(message2say2) + msgmess += "

[SSchat.PreviewHornyFurryDatingSimMessage(user, null, message2say2, FALSE)]

" previewmsgs += list(list("Mode" = msgmode, "Message" = msgmess)) data["NumbermalMin"] = SSchat.numbermal_min data["NumbermalMax"] = SSchat.numbermal_max @@ -1579,6 +1598,8 @@ SUBSYSTEM_DEF(chat) data["OuterBox"] = list() data["ImageBox"] = list() for(var/mmode in P.mommychat_settings) + if(mmode == MODE_PROFILE_PIC) + continue // tchia uncle chuck // First, the top box settings var/list/topfox = list() topfox["Mode"] = mmode @@ -1586,57 +1607,57 @@ SUBSYSTEM_DEF(chat) topfox["Settings"] = list() topfox["Settings"] += list( list( - "Name" = "Gradient 1", + "Name" = "Image Box Top Gradient Color", "Val" = P.mommychat_settings[mmode]["TopBoxGradient1"], "Type" = "COLOR", "Loc" = "L", "PKey" = "TopBoxGradient1", - "Desc" = "The first color of the gradient for the top box.", + "Desc" = "The first color of the gradient for the section that contains the image for this message mode.", "Default" = "000000" ), list( - "Name" = "Gradient 2", + "Name" = "Image Box Bottom Gradient Color", "Val" = P.mommychat_settings[mmode]["TopBoxGradient2"], "Type" = "COLOR", "Loc" = "L", "PKey" = "TopBoxGradient2", - "Desc" = "The second color of the gradient for the top box.", + "Desc" = "The second color of the gradient for the section that contains the image for this message mode.", "Default" = "000000" ), list( - "Name" = "Gradient Angle", + "Name" = "Image Box Gradient Direction", "Val" = P.mommychat_settings[mmode]["TopBoxGradientAngle"], "Type" = "ANGLE", "Loc" = "L", "PKey" = "TopBoxGradientAngle", - "Desc" = "The angle of the gradient for the top box.", + "Desc" = "The angle of the gradient for the section that contains the image for this message mode.", "Default" = "0" ), list( - "Name" = "Border Color", + "Name" = "Image Box Outer Border Color", "Val" = P.mommychat_settings[mmode]["TopBoxBorderColor"], "Type" = "COLOR", "Loc" = "R", "PKey" = "TopBoxBorderColor", - "Desc" = "The color of the border for the top box.", + "Desc" = "The color of the border for the section that contains the image for this message mode.", "Default" = "000000" ), list( - "Name" = "Border Width", + "Name" = "Image Box Outer Border Width", "Val" = P.mommychat_settings[mmode]["TopBoxBorderWidth"], "Type" = "NUMBER", "Loc" = "R", "PKey" = "TopBoxBorderWidth", - "Desc" = "The width of the border for the top box.", + "Desc" = "The width of the border for the section that contains the image for this message mode. Set to 0 to disable.", "Default" = "1" ), list( - "Name" = "Border Style", + "Name" = "Image Box Outer Border Style", "Val" = P.mommychat_settings[mmode]["TopBoxBorderStyle"], "Type" = "SELECT", "Loc" = "R", "PKey" = "TopBoxBorderStyle", - "Desc" = "The style of the border for the top box.", + "Desc" = "The style of the border for the section that contains the image for this message mode.", "Default" = "solid", "Options" = SSchat.borderstyles ), @@ -1649,57 +1670,57 @@ SUBSYSTEM_DEF(chat) bottomfox["Settings"] = list() bottomfox["Settings"] += list( list( - "Name" = "Gradient 1", + "Name" = "Message Box Top Gradient Color", "Val" = P.mommychat_settings[mmode]["BottomBoxGradient1"], "Type" = "COLOR", "Loc" = "L", "PKey" = "BottomBoxGradient1", - "Desc" = "The first color of the gradient for the bottom box.", + "Desc" = "The first color of the gradient for the box that will contain your message.", "Default" = "000000" ), list( - "Name" = "Gradient 2", + "Name" = "Message Box Bottom Gradient Color", "Val" = P.mommychat_settings[mmode]["BottomBoxGradient2"], "Type" = "COLOR", "Loc" = "L", "PKey" = "BottomBoxGradient2", - "Desc" = "The second color of the gradient for the bottom box.", + "Desc" = "The second color of the gradient for the box that will contain your message.", "Default" = "000000" ), list( - "Name" = "Gradient Angle", + "Name" = "Message Box Gradient Direction", "Val" = P.mommychat_settings[mmode]["BottomBoxGradientAngle"], "Type" = "ANGLE", "Loc" = "L", "PKey" = "BottomBoxGradientAngle", - "Desc" = "The angle of the gradient for the bottom box.", + "Desc" = "The angle of the gradient for the box that will contain your message.", "Default" = "0" ), list( - "Name" = "Border Color", + "Name" = "Message Box Outer Border Color", "Val" = P.mommychat_settings[mmode]["BottomBoxBorderColor"], "Type" = "COLOR", "Loc" = "R", "PKey" = "BottomBoxBorderColor", - "Desc" = "The color of the border for the bottom box.", + "Desc" = "The color of the border for the box that will contain your message.", "Default" = "000000" ), list( - "Name" = "Border Width", + "Name" = "Message Box Outer Border Width", "Val" = P.mommychat_settings[mmode]["BottomBoxBorderWidth"], "Type" = "NUMBER", "Loc" = "R", "PKey" = "BottomBoxBorderWidth", - "Desc" = "The width of the border for the bottom box.", + "Desc" = "The width of the border for the box that will contain your message. Set to 0 to disable.", "Default" = "1" ), list( - "Name" = "Border Style", + "Name" = "Message Box Outer Border Style", "Val" = P.mommychat_settings[mmode]["BottomBoxBorderStyle"], "Type" = "SELECT", "Loc" = "R", "PKey" = "BottomBoxBorderStyle", - "Desc" = "The style of the border for the bottom box.", + "Desc" = "The style of the border for the box that will contain your message.", "Default" = "solid", "Options" = SSchat.borderstyles ), @@ -1767,7 +1788,7 @@ SUBSYSTEM_DEF(chat) "Options" = SSchat.borderstyles ), ) - data["Buttons"] += list(buttonfox) + data["Buttons"] += list(buttonfox) // unused /// Then, the Image Border settings var/list/imagefox = list() imagefox["Mode"] = mmode @@ -1803,7 +1824,7 @@ SUBSYSTEM_DEF(chat) "Options" = SSchat.borderstyles ), ) - data["ImageBox"] += list(imagefox) + data["ImageBox"] += list(imagefox) // unused /// And finally the Outer Box settings var/list/outerfox = list() outerfox["Mode"] = mmode @@ -1811,30 +1832,30 @@ SUBSYSTEM_DEF(chat) outerfox["Settings"] = list() outerfox["Settings"] += list( list( - "Name" = "Border Color", + "Name" = "Outer Box Border Color", "Val" = P.mommychat_settings[mmode]["OuterBoxBorderColor"], "Type" = "COLOR", "Loc" = "R", "PKey" = "OuterBoxBorderColor", - "Desc" = "The color of the border for the outer box.", + "Desc" = "The color of the border that will go around the entire message box.", "Default" = "000000" ), list( - "Name" = "Border Width", + "Name" = "Outer Box Border Width", "Val" = P.mommychat_settings[mmode]["OuterBoxBorderWidth"], "Type" = "NUMBER", "Loc" = "R", "PKey" = "OuterBoxBorderWidth", - "Desc" = "The width of the border for the outer box.", + "Desc" = "The width of the border that will go around the entire message box. Set to 0 to disable.", "Default" = "1" ), list( - "Name" = "Border Style", + "Name" = "Outer Box Border Style", "Val" = P.mommychat_settings[mmode]["OuterBoxBorderStyle"], "Type" = "SELECT", "Loc" = "R", "PKey" = "OuterBoxBorderStyle", - "Desc" = "The style of the border for the outer box.", + "Desc" = "The style of the border that will go around the entire message box.", "Default" = "solid", "Options" = SSchat.borderstyles ), @@ -1869,7 +1890,7 @@ SUBSYSTEM_DEF(chat) if("ToggleWhinyLittleBazingaMode") TOGGLE_BITFIELD(P.chat_toggles, CHAT_SEE_COOLCHAT) if(CHECK_BITFIELD(P.chat_toggles, CHAT_SEE_COOLCHAT)) - to_chat(M, span_notice("You will now see CoolChat messages!")) + to_chat(M, span_notice("You will now see VisualChat messages!")) else to_chat(M, span_notice("You will now see boring normal chat messages!")) . = CHANGED_NOTHING @@ -1982,6 +2003,8 @@ SUBSYSTEM_DEF(chat) PPc["URL"] = newurl if(!isnull(newhost)) PPc["Host"] = newhost // UX SUPREME + if(mode == MODE_PROFILE_PIC) + PPc["Suppress"] = TRUE to_chat(M, span_notice("URL for [mode2string(mode)] has been updated to [newurl]!")) . = CHANGED_IMAGES break @@ -2018,6 +2041,49 @@ SUBSYSTEM_DEF(chat) return FALSE else to_chat(M, span_notice("Profile entry for [mode2string(mode)] has been updated!")) + if("ModifyCustomBlankVerb") // if someone just types :bazinga:, show this message instead + var/mode = params["Mode"] + var/newmsg = params["NewMessage"] + // if(mode in SSchat.mandatory_modes) + // to_chat(M, span_alert("Unable to modify profile entry for [mode2string(mode)], mode is not modifiable! :c")) + // return FALSE + newmsg = replacetext(newmsg, @"\n", "") + if(!newmsg || !istext(newmsg) || !LAZYLEN(newmsg)) + to_chat(M, span_alert("Unable to modify profile entry for [mode2string(mode)], message cannot be empty! :c")) + return FALSE + for(var/list/PPc in P.ProfilePics) + if(PPc["Mode"] != mode) + continue + PPc["CustomBlankVerb"] = newmsg + to_chat(M, span_notice("Custom default message for [mode2string(mode)] has been updated!")) + . = CHANGED_IMAGES + break + if(!.) + to_chat(M, span_alert("Unable to modify profile entry for [mode2string(mode)], entry not found! :c")) + return FALSE + else + to_chat(M, span_notice("Profile entry for [mode2string(mode)] has been updated!")) + if("ModifyCustomMessageVerb") // if someone just types :bazinga:, show this verb as the message mode instead + var/mode = params["Mode"] + var/newverb = params["NewVerb"] + // if(mode in SSchat.mandatory_modes) // not that they can use it anyway + // to_chat(M, span_alert("Unable to modify profile entry for [mode2string(mode)], mode is not modifiable! :c")) + // return FALSE + if(!newverb || !istext(newverb) || !LAZYLEN(newverb)) + to_chat(M, span_alert("Unable to modify profile entry for [mode2string(mode)], verb cannot be empty! :c")) + return FALSE + for(var/list/PPc in P.ProfilePics) + if(PPc["Mode"] != mode) + continue + PPc["CustomMessageVerb"] = newverb + to_chat(M, span_notice("Custom default verb for [mode2string(mode)] has been updated!")) + . = CHANGED_IMAGES + break + if(!.) + to_chat(M, span_alert("Unable to modify profile entry for [mode2string(mode)], entry not found! :c")) + return FALSE + else + to_chat(M, span_notice("Profile entry for [mode2string(mode)] has been updated!")) if("AddProfileEntry") var/list/new_entry = list( "Mode" = ":[safepick(GLOB.ing_verbs)][safepick(GLOB.adverbs)]:", diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 146e383a58..d4e7c3bedd 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -583,7 +583,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(jobban_isbanned(user, "appearance")) dat += "You are banned from using custom names and appearances. You can continue to adjust your characters, but you will be randomised once you join the game.
" - dat += "Configure CoolChat / Profile Pictures!
" + dat += "Configure VisualChat / Profile Pictures!
" dat += "Name: " dat += "[real_name]
" @@ -610,7 +610,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) */ //Right column dat +="" - dat += "Configure CoolChat / Profile Pictures!
" + dat += "Configure VisualChat / Profile Pictures!
" // dat += "

Profile Picture ([pfphost]):


" var/pfplink = SSchat.GetPicForMode(user, MODE_PROFILE_PIC) dat += "Picture: [pfplink ? "" : "Upload a picture!"]
" @@ -1057,7 +1057,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += APPEARANCE_CATEGORY_COLUMN dat += "

Flavor Text

" dat += "Set Examine Text
" - dat += "Configure CoolChat / Profile Pictures!
" + dat += "Configure VisualChat / Profile Pictures!
" if(length(features["flavor_text"]) <= 40) if(!length(features["flavor_text"])) dat += "\[...\]" diff --git a/testdoc4.html b/testdoc4.html new file mode 100644 index 0000000000..dfde09a31b --- /dev/null +++ b/testdoc4.html @@ -0,0 +1,22 @@ +

Ward Buttersworth says,

"Hello! You are hearing me talk. I am saying words. I am saying words to you. I am saying words to you in a friendly manner. I am not yelling, I am not whispering, I am not singing, I am not asking, I am not exclaiming. I am saying words to you, and you are hearing them. And now I am done. Hi."

+ + +
+
+
+ +
+
+
+

+ Ward Buttersworth + says, +

+

+ "Hello! You are hearing me talk. I am saying words. I am saying words to you. I am saying words to you in a friendly manner. I am not yelling, I am not whispering, I am not singing, I am not asking, I am not exclaiming. I am saying words to you, and you are hearing them. And now I am done. Hi." +

+
+
+ + + diff --git a/tgui/packages/tgui/components/Button.js b/tgui/packages/tgui/components/Button.js index 80720736ed..8b527e5f27 100644 --- a/tgui/packages/tgui/components/Button.js +++ b/tgui/packages/tgui/components/Button.js @@ -11,6 +11,7 @@ import { createLogger } from '../logging'; import { Box } from './Box'; import { Icon } from './Icon'; import { Tooltip } from './Tooltip'; +import { Textarea } from '../components'; const logger = createLogger('Button'); @@ -36,6 +37,8 @@ export const Button = props => { children, onclick, onClick, + inputHeight, + inputWidth, ...rest } = props; const hasContent = !!(content || children); @@ -305,3 +308,115 @@ export class ButtonInput extends Component { } Button.Input = ButtonInput; + +export class ButtonTextArea extends Component { + constructor() { + super(); + this.inputRef = createRef(); + this.state = { + inInput: false, + }; + } + + setInInput(inInput) { + this.setState({ + inInput, + }); + if (this.inputRef) { + const input = this.inputRef.current; + if (inInput) { + input.value = this.props.currentValue || ""; + try { + input.focus(); + input.select(); + } + catch {} + } + } + } + + commitResult(e) { + if (this.inputRef) { + const input = this.inputRef.current; + const hasValue = (input.value !== ""); + if (hasValue) { + this.props.onCommit(e, input.value); + return; + } else { + if (!this.props.defaultValue) { + return; + } + this.props.onCommit(e, this.props.defaultValue); + } + } + } + + render() { + const { + fluid, + content, + icon, + iconRotation, + iconSpin, + tooltip, + tooltipPosition, + color = 'default', + placeholder, + maxLength, + ...rest + } = this.props; + + let buttonContent = ( + this.setInInput(true)}> + {icon && ( + + )} +
+ {content} +
+