mirror of
https://github.com/yogstation13/Yogstation.git
synced 2025-02-26 09:04:50 +00:00
Ports Runescape Chat bubbles from TG-station (#8293)
* Working * 512 * Hacky AF 512 workaround, revert if 513 update * 512 fix * Update layers.dm * PLANE GO CRASH * Revert "512" This reverts commit024f7adfe3. * Revert "512 fix" This reverts commit0efdc42688. * Revert "Hacky AF 512 workaround, revert if 513 update" This reverts commit569987d66f. * f * -f * Revert "-f" This reverts commitaadad7b75d. * Revert "f" This reverts commit0e4368ecee. * reverts darkness Co-authored-by: Bobbahbrown <bobbahbrown@gmail.com>
This commit is contained in:
@@ -78,6 +78,8 @@
|
|||||||
#define MASSIVE_OBJ_LAYER 11
|
#define MASSIVE_OBJ_LAYER 11
|
||||||
#define POINT_LAYER 12
|
#define POINT_LAYER 12
|
||||||
|
|
||||||
|
#define CHAT_LAYER 12.5
|
||||||
|
|
||||||
#define LIGHTING_PLANE 15
|
#define LIGHTING_PLANE 15
|
||||||
#define LIGHTING_LAYER 15
|
#define LIGHTING_LAYER 15
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
#define LAZYCLEARLIST(L) if(L) L.Cut()
|
#define LAZYCLEARLIST(L) if(L) L.Cut()
|
||||||
#define SANITIZE_LIST(L) ( islist(L) ? L : list() )
|
#define SANITIZE_LIST(L) ( islist(L) ? L : list() )
|
||||||
#define reverseList(L) reverseRange(L.Copy())
|
#define reverseList(L) reverseRange(L.Copy())
|
||||||
|
#define LAZYADDASSOC(L, K, V) if(!L) { L = list(); } L[K] += list(V);
|
||||||
|
#define LAZYREMOVEASSOC(L, K, V) if(L) { if(L[K]) { L[K] -= V; if(!length(L[K])) L -= K; } if(!length(L)) L = null; }
|
||||||
|
|
||||||
// binary search sorted insert
|
// binary search sorted insert
|
||||||
// IN: Object to be inserted
|
// IN: Object to be inserted
|
||||||
|
|||||||
@@ -147,6 +147,8 @@
|
|||||||
friend_talk(message)
|
friend_talk(message)
|
||||||
|
|
||||||
/mob/camera/imaginary_friend/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode)
|
/mob/camera/imaginary_friend/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode)
|
||||||
|
if (client?.prefs.chat_on_map && (client.prefs.see_chat_non_mob || ismob(speaker)))
|
||||||
|
create_chat_message(speaker, message_language, raw_message, spans, message_mode)
|
||||||
to_chat(src, compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode))
|
to_chat(src, compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode))
|
||||||
|
|
||||||
/mob/camera/imaginary_friend/proc/friend_talk(message)
|
/mob/camera/imaginary_friend/proc/friend_talk(message)
|
||||||
|
|||||||
236
code/datums/chatmessage.dm
Normal file
236
code/datums/chatmessage.dm
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
#define CHAT_MESSAGE_SPAWN_TIME 0.2 SECONDS
|
||||||
|
#define CHAT_MESSAGE_LIFESPAN 5 SECONDS
|
||||||
|
#define CHAT_MESSAGE_EOL_FADE 0.7 SECONDS
|
||||||
|
#define CHAT_MESSAGE_EXP_DECAY 0.7 // Messages decay at pow(factor, idx in stack)
|
||||||
|
#define CHAT_MESSAGE_HEIGHT_DECAY 0.9 // Increase message decay based on the height of the message
|
||||||
|
#define CHAT_MESSAGE_APPROX_LHEIGHT 11 // Approximate height in pixels of an 'average' line, used for height decay
|
||||||
|
#define CHAT_MESSAGE_WIDTH 96 // pixels
|
||||||
|
#define CHAT_MESSAGE_MAX_LENGTH 110 // characters
|
||||||
|
#define WXH_TO_HEIGHT(x) text2num(copytext((x), findtextEx((x), "x") + 1)) // thanks lummox
|
||||||
|
|
||||||
|
/**
|
||||||
|
* # Chat Message Overlay
|
||||||
|
*
|
||||||
|
* Datum for generating a message overlay on the map
|
||||||
|
*/
|
||||||
|
/datum/chatmessage
|
||||||
|
/// The visual element of the chat messsage
|
||||||
|
var/image/message
|
||||||
|
/// The location in which the message is appearing
|
||||||
|
var/atom/message_loc
|
||||||
|
/// The client who heard this message
|
||||||
|
var/client/owned_by
|
||||||
|
/// Contains the scheduled destruction time
|
||||||
|
var/scheduled_destruction
|
||||||
|
/// Contains the approximate amount of lines for height decay
|
||||||
|
var/approx_lines
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a chat message overlay
|
||||||
|
*
|
||||||
|
* Arguments:
|
||||||
|
* * text - The text content of the overlay
|
||||||
|
* * target - The target atom to display the overlay at
|
||||||
|
* * owner - The mob that owns this overlay, only this mob will be able to view it
|
||||||
|
* * extra_classes - Extra classes to apply to the span that holds the text
|
||||||
|
* * lifespan - The lifespan of the message in deciseconds
|
||||||
|
*/
|
||||||
|
/datum/chatmessage/New(text, atom/target, mob/owner, list/extra_classes = null, lifespan = CHAT_MESSAGE_LIFESPAN)
|
||||||
|
. = ..()
|
||||||
|
if (!istype(target))
|
||||||
|
CRASH("Invalid target given for chatmessage")
|
||||||
|
if(QDELETED(owner) || !istype(owner) || !owner.client)
|
||||||
|
stack_trace("/datum/chatmessage created with [isnull(owner) ? "null" : "invalid"] mob owner")
|
||||||
|
qdel(src)
|
||||||
|
return
|
||||||
|
INVOKE_ASYNC(src, .proc/generate_image, text, target, owner, extra_classes, lifespan)
|
||||||
|
|
||||||
|
/datum/chatmessage/Destroy()
|
||||||
|
if (owned_by)
|
||||||
|
if (owned_by.seen_messages)
|
||||||
|
LAZYREMOVEASSOC(owned_by.seen_messages, message_loc, src)
|
||||||
|
owned_by.images.Remove(message)
|
||||||
|
owned_by = null
|
||||||
|
message_loc = null
|
||||||
|
message = null
|
||||||
|
return ..()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a chat message image representation
|
||||||
|
*
|
||||||
|
* Arguments:
|
||||||
|
* * text - The text content of the overlay
|
||||||
|
* * target - The target atom to display the overlay at
|
||||||
|
* * owner - The mob that owns this overlay, only this mob will be able to view it
|
||||||
|
* * extra_classes - Extra classes to apply to the span that holds the text
|
||||||
|
* * lifespan - The lifespan of the message in deciseconds
|
||||||
|
*/
|
||||||
|
/datum/chatmessage/proc/generate_image(text, atom/target, mob/owner, list/extra_classes, lifespan)
|
||||||
|
// Register client who owns this message
|
||||||
|
owned_by = owner.client
|
||||||
|
RegisterSignal(owned_by, COMSIG_PARENT_QDELETING, .proc/qdel, src)
|
||||||
|
|
||||||
|
// Clip message
|
||||||
|
var/maxlen = owned_by.prefs.max_chat_length
|
||||||
|
if (length_char(text) > maxlen)
|
||||||
|
text = copytext_char(text, 1, maxlen + 1) + "..." // BYOND index moment
|
||||||
|
|
||||||
|
// Calculate target color if not already present
|
||||||
|
if (!target.chat_color || target.chat_color_name != target.name)
|
||||||
|
target.chat_color = colorize_string(target.name)
|
||||||
|
target.chat_color_darkened = colorize_string(target.name, 0.85, 0.85)
|
||||||
|
target.chat_color_name = target.name
|
||||||
|
|
||||||
|
// Get rid of any URL schemes that might cause BYOND to automatically wrap something in an anchor tag
|
||||||
|
var/static/regex/url_scheme = new(@"[A-Za-z][A-Za-z0-9+-\.]*:\/\/", "g")
|
||||||
|
text = replacetext(text, url_scheme, "")
|
||||||
|
|
||||||
|
// Reject whitespace
|
||||||
|
var/static/regex/whitespace = new(@"^\s*$")
|
||||||
|
if (whitespace.Find(text))
|
||||||
|
qdel(src)
|
||||||
|
return
|
||||||
|
|
||||||
|
// Non mobs speakers can be small
|
||||||
|
if (!ismob(target))
|
||||||
|
extra_classes |= "small"
|
||||||
|
|
||||||
|
// Append radio icon if from a virtual speaker
|
||||||
|
if (extra_classes.Find("virtual-speaker"))
|
||||||
|
var/image/r_icon = image('icons/UI_Icons/chat/chat_icons.dmi', icon_state = "radio")
|
||||||
|
text = "\icon[r_icon] " + text
|
||||||
|
|
||||||
|
// We dim italicized text to make it more distinguishable from regular text
|
||||||
|
var/tgt_color = extra_classes.Find("italics") ? target.chat_color_darkened : target.chat_color
|
||||||
|
|
||||||
|
// Approximate text height
|
||||||
|
// Note we have to replace HTML encoded metacharacters otherwise MeasureText will return a zero height
|
||||||
|
// BYOND Bug #2563917
|
||||||
|
// Construct text
|
||||||
|
var/static/regex/html_metachars = new(@"&[A-Za-z]{1,7};", "g")
|
||||||
|
var/complete_text = "<span class='center maptext [extra_classes != null ? extra_classes.Join(" ") : ""]' style='color: [tgt_color]'>[text]</span>"
|
||||||
|
var/mheight = WXH_TO_HEIGHT(owned_by.MeasureText(replacetext(complete_text, html_metachars, "m"), null, CHAT_MESSAGE_WIDTH))
|
||||||
|
approx_lines = max(1, mheight / CHAT_MESSAGE_APPROX_LHEIGHT)
|
||||||
|
|
||||||
|
// Translate any existing messages upwards, apply exponential decay factors to timers
|
||||||
|
message_loc = target
|
||||||
|
if (owned_by.seen_messages)
|
||||||
|
var/idx = 1
|
||||||
|
var/combined_height = approx_lines
|
||||||
|
for(var/msg in owned_by.seen_messages[message_loc])
|
||||||
|
var/datum/chatmessage/m = msg
|
||||||
|
animate(m.message, pixel_y = m.message.pixel_y + mheight, time = CHAT_MESSAGE_SPAWN_TIME)
|
||||||
|
combined_height += m.approx_lines
|
||||||
|
var/sched_remaining = m.scheduled_destruction - world.time
|
||||||
|
if (sched_remaining > CHAT_MESSAGE_SPAWN_TIME)
|
||||||
|
var/remaining_time = (sched_remaining) * (CHAT_MESSAGE_EXP_DECAY ** idx++) * (CHAT_MESSAGE_HEIGHT_DECAY ** combined_height)
|
||||||
|
m.scheduled_destruction = world.time + remaining_time
|
||||||
|
addtimer(CALLBACK(m, .proc/end_of_life), remaining_time, TIMER_UNIQUE|TIMER_OVERRIDE)
|
||||||
|
|
||||||
|
// Build message image
|
||||||
|
message = image(loc = message_loc, layer = CHAT_LAYER)
|
||||||
|
message.plane = GAME_PLANE
|
||||||
|
message.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA | KEEP_APART
|
||||||
|
message.alpha = 0
|
||||||
|
message.pixel_y = owner.bound_height * 0.95
|
||||||
|
message.maptext_width = CHAT_MESSAGE_WIDTH
|
||||||
|
message.maptext_height = mheight
|
||||||
|
message.maptext_x = (CHAT_MESSAGE_WIDTH - owner.bound_width) * -0.5
|
||||||
|
message.maptext = complete_text
|
||||||
|
|
||||||
|
// View the message
|
||||||
|
LAZYADDASSOC(owned_by.seen_messages, message_loc, src)
|
||||||
|
owned_by.images |= message
|
||||||
|
animate(message, alpha = 255, time = CHAT_MESSAGE_SPAWN_TIME)
|
||||||
|
|
||||||
|
// Prepare for destruction
|
||||||
|
scheduled_destruction = world.time + (lifespan - CHAT_MESSAGE_EOL_FADE)
|
||||||
|
addtimer(CALLBACK(src, .proc/end_of_life), lifespan - CHAT_MESSAGE_EOL_FADE, TIMER_UNIQUE|TIMER_OVERRIDE)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies final animations to overlay CHAT_MESSAGE_EOL_FADE deciseconds prior to message deletion
|
||||||
|
*/
|
||||||
|
/datum/chatmessage/proc/end_of_life(fadetime = CHAT_MESSAGE_EOL_FADE)
|
||||||
|
animate(message, alpha = 0, time = fadetime, flags = ANIMATION_PARALLEL)
|
||||||
|
QDEL_IN(src, fadetime)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a message overlay at a defined location for a given speaker
|
||||||
|
*
|
||||||
|
* Arguments:
|
||||||
|
* * speaker - The atom who is saying this message
|
||||||
|
* * message_language - The language that the message is said in
|
||||||
|
* * raw_message - The text content of the message
|
||||||
|
* * spans - Additional classes to be added to the message
|
||||||
|
* * message_mode - Bitflags relating to the mode of the message
|
||||||
|
*/
|
||||||
|
/mob/proc/create_chat_message(atom/movable/speaker, datum/language/message_language, raw_message, list/spans, message_mode)
|
||||||
|
// Ensure the list we are using, if present, is a copy so we don't modify the list provided to us
|
||||||
|
spans = spans?.Copy()
|
||||||
|
|
||||||
|
// Check for virtual speakers (aka hearing a message through a radio)
|
||||||
|
var/atom/movable/originalSpeaker = speaker
|
||||||
|
if (istype(speaker, /atom/movable/virtualspeaker))
|
||||||
|
var/atom/movable/virtualspeaker/v = speaker
|
||||||
|
speaker = v.source
|
||||||
|
spans |= "virtual-speaker"
|
||||||
|
|
||||||
|
// Ignore virtual speaker (most often radio messages) from ourself
|
||||||
|
if (originalSpeaker != src && speaker == src)
|
||||||
|
return
|
||||||
|
|
||||||
|
// Display visual above source
|
||||||
|
new /datum/chatmessage(lang_treat(speaker, message_language, raw_message, spans, null, TRUE), speaker, src, spans)
|
||||||
|
|
||||||
|
|
||||||
|
// Tweak these defines to change the available color ranges
|
||||||
|
#define CM_COLOR_SAT_MIN 0.6
|
||||||
|
#define CM_COLOR_SAT_MAX 0.7
|
||||||
|
#define CM_COLOR_LUM_MIN 0.65
|
||||||
|
#define CM_COLOR_LUM_MAX 0.75
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a color for a name, will return the same color for a given string consistently within a round.atom
|
||||||
|
*
|
||||||
|
* Note that this proc aims to produce pastel-ish colors using the HSL colorspace. These seem to be favorable for displaying on the map.
|
||||||
|
*
|
||||||
|
* Arguments:
|
||||||
|
* * name - The name to generate a color for
|
||||||
|
* * sat_shift - A value between 0 and 1 that will be multiplied against the saturation
|
||||||
|
* * lum_shift - A value between 0 and 1 that will be multiplied against the luminescence
|
||||||
|
*/
|
||||||
|
/datum/chatmessage/proc/colorize_string(name, sat_shift = 1, lum_shift = 1)
|
||||||
|
// seed to help randomness
|
||||||
|
var/static/rseed = rand(1,26)
|
||||||
|
|
||||||
|
// get hsl using the selected 6 characters of the md5 hash
|
||||||
|
var/hash = copytext(md5(name + GLOB.round_id), rseed, rseed + 6)
|
||||||
|
var/h = hex2num(copytext(hash, 1, 3)) * (360 / 255)
|
||||||
|
var/s = (hex2num(copytext(hash, 3, 5)) >> 2) * ((CM_COLOR_SAT_MAX - CM_COLOR_SAT_MIN) / 63) + CM_COLOR_SAT_MIN
|
||||||
|
var/l = (hex2num(copytext(hash, 5, 7)) >> 2) * ((CM_COLOR_LUM_MAX - CM_COLOR_LUM_MIN) / 63) + CM_COLOR_LUM_MIN
|
||||||
|
|
||||||
|
// adjust for shifts
|
||||||
|
s *= clamp(sat_shift, 0, 1)
|
||||||
|
l *= clamp(lum_shift, 0, 1)
|
||||||
|
|
||||||
|
// convert to rgb
|
||||||
|
var/h_int = round(h/60) // mapping each section of H to 60 degree sections
|
||||||
|
var/c = (1 - abs(2 * l - 1)) * s
|
||||||
|
var/x = c * (1 - abs((h / 60) % 2 - 1))
|
||||||
|
var/m = l - c * 0.5
|
||||||
|
x = (x + m) * 255
|
||||||
|
c = (c + m) * 255
|
||||||
|
m *= 255
|
||||||
|
switch(h_int)
|
||||||
|
if(0)
|
||||||
|
return "#[num2hex(c, 2)][num2hex(x, 2)][num2hex(m, 2)]"
|
||||||
|
if(1)
|
||||||
|
return "#[num2hex(x, 2)][num2hex(c, 2)][num2hex(m, 2)]"
|
||||||
|
if(2)
|
||||||
|
return "#[num2hex(m, 2)][num2hex(c, 2)][num2hex(x, 2)]"
|
||||||
|
if(3)
|
||||||
|
return "#[num2hex(m, 2)][num2hex(x, 2)][num2hex(c, 2)]"
|
||||||
|
if(4)
|
||||||
|
return "#[num2hex(x, 2)][num2hex(m, 2)][num2hex(c, 2)]"
|
||||||
|
if(5)
|
||||||
|
return "#[num2hex(c, 2)][num2hex(m, 2)][num2hex(x, 2)]"
|
||||||
@@ -39,6 +39,13 @@
|
|||||||
var/rad_flags = NONE // Will move to flags_1 when i can be arsed to
|
var/rad_flags = NONE // Will move to flags_1 when i can be arsed to
|
||||||
var/rad_insulation = RAD_NO_INSULATION
|
var/rad_insulation = RAD_NO_INSULATION
|
||||||
|
|
||||||
|
var/chat_color_name // Last name used to calculate a color for the chatmessage overlays
|
||||||
|
|
||||||
|
var/chat_color // Last color calculated for the the chatmessage overlays
|
||||||
|
|
||||||
|
var/chat_color_darkened // A luminescence-shifted value of the last color calculated for chatmessage overlays
|
||||||
|
|
||||||
|
|
||||||
/atom/New(loc, ...)
|
/atom/New(loc, ...)
|
||||||
//atom creation method that preloads variables at creation
|
//atom creation method that preloads variables at creation
|
||||||
if(GLOB.use_preloader && (src.type == GLOB._preloader.target_path))//in case the instanciated atom is creating other atoms in New()
|
if(GLOB.use_preloader && (src.type == GLOB._preloader.target_path))//in case the instanciated atom is creating other atoms in New()
|
||||||
|
|||||||
@@ -98,21 +98,21 @@ GLOBAL_LIST_INIT(freqtospan, list(
|
|||||||
var/spanned = attach_spans(input, spans)
|
var/spanned = attach_spans(input, spans)
|
||||||
return "[say_mod(input, message_mode)], \"[spanned]\""
|
return "[say_mod(input, message_mode)], \"[spanned]\""
|
||||||
|
|
||||||
/atom/movable/proc/lang_treat(atom/movable/speaker, datum/language/language, raw_message, list/spans, message_mode)
|
/atom/movable/proc/lang_treat(atom/movable/speaker, datum/language/language, raw_message, list/spans, message_mode, no_quote = FALSE)
|
||||||
if(has_language(language))
|
if(has_language(language))
|
||||||
var/atom/movable/AM = speaker.GetSource()
|
var/atom/movable/AM = speaker.GetSource()
|
||||||
if(AM) //Basically means "if the speaker is virtual"
|
if(AM) //Basically means "if the speaker is virtual"
|
||||||
return AM.say_quote(raw_message, spans, message_mode)
|
return no_quote ? raw_message : AM.say_quote(raw_message, spans, message_mode)
|
||||||
else
|
else
|
||||||
return speaker.say_quote(raw_message, spans, message_mode)
|
return no_quote ? raw_message : speaker.say_quote(raw_message, spans, message_mode)
|
||||||
else if(language)
|
else if(language)
|
||||||
var/atom/movable/AM = speaker.GetSource()
|
var/atom/movable/AM = speaker.GetSource()
|
||||||
var/datum/language/D = GLOB.language_datum_instances[language]
|
var/datum/language/D = GLOB.language_datum_instances[language]
|
||||||
raw_message = D.scramble(raw_message)
|
raw_message = D.scramble(raw_message)
|
||||||
if(AM)
|
if(AM)
|
||||||
return AM.say_quote(raw_message, spans, message_mode)
|
return no_quote ? raw_message : AM.say_quote(raw_message, spans, message_mode)
|
||||||
else
|
else
|
||||||
return speaker.say_quote(raw_message, spans, message_mode)
|
return no_quote ? raw_message : speaker.say_quote(raw_message, spans, message_mode)
|
||||||
else
|
else
|
||||||
return "makes a strange sound."
|
return "makes a strange sound."
|
||||||
|
|
||||||
|
|||||||
@@ -127,6 +127,9 @@ the new instance inside the host to be updated to the template's stats.
|
|||||||
link = FOLLOW_LINK(src, to_follow)
|
link = FOLLOW_LINK(src, to_follow)
|
||||||
else
|
else
|
||||||
link = ""
|
link = ""
|
||||||
|
// Create map text prior to modifying message for goonchat
|
||||||
|
if (client?.prefs.chat_on_map && (client.prefs.see_chat_non_mob || ismob(speaker)))
|
||||||
|
create_chat_message(speaker, message_language, raw_message, spans, message_mode)
|
||||||
// Recompose the message, because it's scrambled by default
|
// Recompose the message, because it's scrambled by default
|
||||||
message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode)
|
message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode)
|
||||||
to_chat(src, "[link] [message]")
|
to_chat(src, "[link] [message]")
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
/client
|
/client
|
||||||
//////////////////////
|
//////////////////////
|
||||||
//BLACK MAGIC THINGS//
|
//BLACK MAGIC THINGS//
|
||||||
@@ -80,3 +79,5 @@
|
|||||||
var/list/char_render_holders //Should only be a key-value list of north/south/east/west = obj/screen.
|
var/list/char_render_holders //Should only be a key-value list of north/south/east/west = obj/screen.
|
||||||
|
|
||||||
var/encoding = "1252" // yogs - LibVG
|
var/encoding = "1252" // yogs - LibVG
|
||||||
|
|
||||||
|
var/list/seen_messages // Messages currently seen by this client
|
||||||
@@ -504,6 +504,7 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
|
|||||||
if(movingmob != null)
|
if(movingmob != null)
|
||||||
movingmob.client_mobs_in_contents -= mob
|
movingmob.client_mobs_in_contents -= mob
|
||||||
UNSETEMPTY(movingmob.client_mobs_in_contents)
|
UNSETEMPTY(movingmob.client_mobs_in_contents)
|
||||||
|
seen_messages = null
|
||||||
Master.UpdateTickRate()
|
Master.UpdateTickRate()
|
||||||
sync_logout_with_db(connection_number) // yogs - logout logging
|
sync_logout_with_db(connection_number) // yogs - logout logging
|
||||||
return ..()
|
return ..()
|
||||||
|
|||||||
@@ -103,6 +103,10 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
|||||||
|
|
||||||
var/action_buttons_screen_locs = list()
|
var/action_buttons_screen_locs = list()
|
||||||
|
|
||||||
|
var/chat_on_map = TRUE
|
||||||
|
var/max_chat_length = CHAT_MESSAGE_MAX_LENGTH
|
||||||
|
var/see_chat_non_mob = TRUE
|
||||||
|
|
||||||
/datum/preferences/New(client/C)
|
/datum/preferences/New(client/C)
|
||||||
parent = C
|
parent = C
|
||||||
|
|
||||||
@@ -464,6 +468,10 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
|||||||
dat += "<b>UI Style:</b> <a href='?_src_=prefs;task=input;preference=ui'>[UI_style]</a><br>"
|
dat += "<b>UI Style:</b> <a href='?_src_=prefs;task=input;preference=ui'>[UI_style]</a><br>"
|
||||||
dat += "<b>tgui Monitors:</b> <a href='?_src_=prefs;preference=tgui_lock'>[(tgui_lock) ? "Primary" : "All"]</a><br>"
|
dat += "<b>tgui Monitors:</b> <a href='?_src_=prefs;preference=tgui_lock'>[(tgui_lock) ? "Primary" : "All"]</a><br>"
|
||||||
dat += "<b>tgui Style:</b> <a href='?_src_=prefs;preference=tgui_fancy'>[(tgui_fancy) ? "Fancy" : "No Frills"]</a><br>"
|
dat += "<b>tgui Style:</b> <a href='?_src_=prefs;preference=tgui_fancy'>[(tgui_fancy) ? "Fancy" : "No Frills"]</a><br>"
|
||||||
|
dat += "<b>Show Runechat Chat Bubbles:</b> <a href='?_src_=prefs;preference=chat_on_map'>[chat_on_map ? "Enabled" : "Disabled"]</a><br>"
|
||||||
|
dat += "<b>Runechat message char limit:</b> <a href='?_src_=prefs;preference=max_chat_length;task=input'>[max_chat_length]</a><br>"
|
||||||
|
dat += "<b>See Runechat for non-mobs:</b> <a href='?_src_=prefs;preference=see_chat_non_mob'>[see_chat_non_mob ? "Enabled" : "Disabled"]</a><br>"
|
||||||
|
|
||||||
dat += "<br>"
|
dat += "<br>"
|
||||||
dat += "<b>Action Buttons:</b> <a href='?_src_=prefs;preference=action_buttons'>[(buttons_locked) ? "Locked In Place" : "Unlocked"]</a><br>"
|
dat += "<b>Action Buttons:</b> <a href='?_src_=prefs;preference=action_buttons'>[(buttons_locked) ? "Locked In Place" : "Unlocked"]</a><br>"
|
||||||
//dat += "<b>Keybindings:</b> <a href='?_src_=prefs;preference=hotkeys'>[(hotkeys) ? "Hotkeys" : "Default"]</a><br>" // yogs - Custom keybindings
|
//dat += "<b>Keybindings:</b> <a href='?_src_=prefs;preference=hotkeys'>[(hotkeys) ? "Hotkeys" : "Default"]</a><br>" // yogs - Custom keybindings
|
||||||
@@ -1489,6 +1497,11 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
|||||||
var/pickedPDAColor = input(user, "Choose your PDA Interface color.", "Character Preference",pda_color) as color|null
|
var/pickedPDAColor = input(user, "Choose your PDA Interface color.", "Character Preference",pda_color) as color|null
|
||||||
if(pickedPDAColor)
|
if(pickedPDAColor)
|
||||||
pda_color = pickedPDAColor
|
pda_color = pickedPDAColor
|
||||||
|
|
||||||
|
if ("max_chat_length")
|
||||||
|
var/desiredlength = input(user, "Choose the max character length of shown Runechat messages. Valid range is 1 to [CHAT_MESSAGE_MAX_LENGTH] (default: [initial(max_chat_length)]))", "Character Preference", max_chat_length) as null|num
|
||||||
|
if (!isnull(desiredlength))
|
||||||
|
max_chat_length = clamp(desiredlength, 1, CHAT_MESSAGE_MAX_LENGTH)
|
||||||
// yogs start - Custom keybindings
|
// yogs start - Custom keybindings
|
||||||
if(href_list["keybinding"])
|
if(href_list["keybinding"])
|
||||||
update_keybindings(user, href_list["keybinding"], href_list["dir"])
|
update_keybindings(user, href_list["keybinding"], href_list["dir"])
|
||||||
@@ -1516,6 +1529,11 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
|||||||
else
|
else
|
||||||
bindings.unbind_movement() // yogs - Rebindable keys
|
bindings.unbind_movement() // yogs - Rebindable keys
|
||||||
winset(user, null, "input.focus=true input.background-color=[COLOR_INPUT_ENABLED] mainwindow.macro=old_default")
|
winset(user, null, "input.focus=true input.background-color=[COLOR_INPUT_ENABLED] mainwindow.macro=old_default")
|
||||||
|
if("chat_on_map")
|
||||||
|
chat_on_map = !chat_on_map
|
||||||
|
if("see_chat_non_mob")
|
||||||
|
see_chat_non_mob = !see_chat_non_mob
|
||||||
|
|
||||||
if("action_buttons")
|
if("action_buttons")
|
||||||
buttons_locked = !buttons_locked
|
buttons_locked = !buttons_locked
|
||||||
if("tgui_fancy")
|
if("tgui_fancy")
|
||||||
|
|||||||
@@ -136,6 +136,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
|||||||
S["lastchangelog"] >> lastchangelog
|
S["lastchangelog"] >> lastchangelog
|
||||||
S["UI_style"] >> UI_style
|
S["UI_style"] >> UI_style
|
||||||
S["hotkeys"] >> hotkeys
|
S["hotkeys"] >> hotkeys
|
||||||
|
S["chat_on_map"] >> chat_on_map
|
||||||
|
S["max_chat_length"] >> max_chat_length
|
||||||
|
S["see_chat_non_mob"] >> see_chat_non_mob
|
||||||
S["tgui_fancy"] >> tgui_fancy
|
S["tgui_fancy"] >> tgui_fancy
|
||||||
S["tgui_lock"] >> tgui_lock
|
S["tgui_lock"] >> tgui_lock
|
||||||
S["buttons_locked"] >> buttons_locked
|
S["buttons_locked"] >> buttons_locked
|
||||||
@@ -186,6 +189,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
|||||||
lastchangelog = sanitize_text(lastchangelog, initial(lastchangelog))
|
lastchangelog = sanitize_text(lastchangelog, initial(lastchangelog))
|
||||||
UI_style = sanitize_inlist(UI_style, GLOB.available_ui_styles, GLOB.available_ui_styles[1])
|
UI_style = sanitize_inlist(UI_style, GLOB.available_ui_styles, GLOB.available_ui_styles[1])
|
||||||
hotkeys = sanitize_integer(hotkeys, 0, 1, initial(hotkeys))
|
hotkeys = sanitize_integer(hotkeys, 0, 1, initial(hotkeys))
|
||||||
|
chat_on_map = sanitize_integer(chat_on_map, 0, 1, initial(chat_on_map))
|
||||||
|
max_chat_length = sanitize_integer(max_chat_length, 1, CHAT_MESSAGE_MAX_LENGTH, initial(max_chat_length))
|
||||||
|
see_chat_non_mob = sanitize_integer(see_chat_non_mob, 0, 1, initial(see_chat_non_mob))
|
||||||
tgui_fancy = sanitize_integer(tgui_fancy, 0, 1, initial(tgui_fancy))
|
tgui_fancy = sanitize_integer(tgui_fancy, 0, 1, initial(tgui_fancy))
|
||||||
tgui_lock = sanitize_integer(tgui_lock, 0, 1, initial(tgui_lock))
|
tgui_lock = sanitize_integer(tgui_lock, 0, 1, initial(tgui_lock))
|
||||||
buttons_locked = sanitize_integer(buttons_locked, 0, 1, initial(buttons_locked))
|
buttons_locked = sanitize_integer(buttons_locked, 0, 1, initial(buttons_locked))
|
||||||
@@ -236,6 +242,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
|||||||
WRITE_FILE(S["lastchangelog"], lastchangelog)
|
WRITE_FILE(S["lastchangelog"], lastchangelog)
|
||||||
WRITE_FILE(S["UI_style"], UI_style)
|
WRITE_FILE(S["UI_style"], UI_style)
|
||||||
WRITE_FILE(S["hotkeys"], hotkeys)
|
WRITE_FILE(S["hotkeys"], hotkeys)
|
||||||
|
WRITE_FILE(S["chat_on_map"], chat_on_map)
|
||||||
|
WRITE_FILE(S["max_chat_length"], max_chat_length)
|
||||||
|
WRITE_FILE(S["see_chat_non_mob"], see_chat_non_mob)
|
||||||
WRITE_FILE(S["tgui_fancy"], tgui_fancy)
|
WRITE_FILE(S["tgui_fancy"], tgui_fancy)
|
||||||
WRITE_FILE(S["tgui_lock"], tgui_lock)
|
WRITE_FILE(S["tgui_lock"], tgui_lock)
|
||||||
WRITE_FILE(S["buttons_locked"], buttons_locked)
|
WRITE_FILE(S["buttons_locked"], buttons_locked)
|
||||||
|
|||||||
@@ -689,30 +689,28 @@ GLOBAL_LIST_INIT(hallucination_list, list(
|
|||||||
else
|
else
|
||||||
if(get_dist(target,H)<get_dist(target,person))
|
if(get_dist(target,H)<get_dist(target,person))
|
||||||
person = H
|
person = H
|
||||||
if(person && !force_radio) //Basic talk
|
|
||||||
var/chosen = specific_message
|
// Get person to affect if radio hallucination
|
||||||
if(!chosen)
|
var/is_radio = !person || force_radio
|
||||||
chosen = capitalize(pick(speak_messages))
|
if (is_radio)
|
||||||
chosen = replacetext(chosen, "%TARGETNAME%", target_name)
|
|
||||||
var/image/speech_overlay = image('icons/mob/talk.dmi', person, "default0", layer = ABOVE_MOB_LAYER)
|
|
||||||
var/message = target.compose_message(person,understood_language,chosen,null,list(person.speech_span),face_name = TRUE)
|
|
||||||
feedback_details += "Type: Talk, Source: [person.real_name], Message: [message]"
|
|
||||||
to_chat(target, message)
|
|
||||||
if(target.client)
|
|
||||||
target.client.images |= speech_overlay
|
|
||||||
sleep(30)
|
|
||||||
target.client.images.Remove(speech_overlay)
|
|
||||||
else // Radio talk
|
|
||||||
var/chosen = specific_message
|
|
||||||
if(!chosen)
|
|
||||||
chosen = capitalize(pick(radio_messages))
|
|
||||||
chosen = replacetext(chosen, "%TARGETNAME%", target_name)
|
|
||||||
var/list/humans = list()
|
var/list/humans = list()
|
||||||
for(var/mob/living/carbon/human/H in GLOB.alive_mob_list)
|
for(var/mob/living/carbon/human/H in GLOB.alive_mob_list)
|
||||||
humans += H
|
humans += H
|
||||||
person = pick(humans)
|
person = pick(humans)
|
||||||
var/message = target.compose_message(person,understood_language,chosen,"[FREQ_COMMON]",list(person.speech_span),face_name = TRUE)
|
|
||||||
feedback_details += "Type: Radio, Source: [person.real_name], Message: [message]"
|
// Generate message
|
||||||
|
var/spans = list(person.speech_span)
|
||||||
|
var/chosen = !specific_message ? capitalize(pick(is_radio ? speak_messages : radio_messages)) : specific_message
|
||||||
|
chosen = replacetext(chosen, "%TARGETNAME%", target_name)
|
||||||
|
var/message = target.compose_message(person, understood_language, chosen, is_radio ? "[FREQ_COMMON]" : null, spans, face_name = TRUE)
|
||||||
|
feedback_details += "Type: [is_radio ? "Radio" : "Talk"], Source: [person.real_name], Message: [message]"
|
||||||
|
|
||||||
|
// Display message
|
||||||
|
if (!is_radio && !target.client?.prefs.chat_on_map)
|
||||||
|
var/image/speech_overlay = image('icons/mob/talk.dmi', person, "default0", layer = ABOVE_MOB_LAYER)
|
||||||
|
INVOKE_ASYNC(GLOBAL_PROC, /proc/flick_overlay, speech_overlay, list(target.client), 30)
|
||||||
|
if (target.client?.prefs.chat_on_map)
|
||||||
|
target.create_chat_message(person, understood_language, chosen, spans, 0)
|
||||||
to_chat(target, message)
|
to_chat(target, message)
|
||||||
qdel(src)
|
qdel(src)
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,9 @@
|
|||||||
else
|
else
|
||||||
to_follow = V.source
|
to_follow = V.source
|
||||||
var/link = FOLLOW_LINK(src, to_follow)
|
var/link = FOLLOW_LINK(src, to_follow)
|
||||||
|
// Create map text prior to modifying message for goonchat
|
||||||
|
if (client?.prefs.chat_on_map && (client.prefs.see_chat_non_mob || ismob(speaker)))
|
||||||
|
create_chat_message(speaker, message_language, raw_message, spans, message_mode)
|
||||||
// Recompose the message, because it's scrambled by default
|
// Recompose the message, because it's scrambled by default
|
||||||
message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode)
|
message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode)
|
||||||
to_chat(src, "[link] [message]")
|
to_chat(src, "[link] [message]")
|
||||||
|
|
||||||
|
|||||||
@@ -225,6 +225,10 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
|
|||||||
else
|
else
|
||||||
deaf_message = "<span class='notice'>You can't hear yourself!</span>"
|
deaf_message = "<span class='notice'>You can't hear yourself!</span>"
|
||||||
deaf_type = 2 // Since you should be able to hear yourself without looking
|
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())
|
||||||
|
create_chat_message(speaker, message_language, raw_message, spans, message_mode)
|
||||||
|
|
||||||
|
|
||||||
// Recompose message for AI hrefs, language incomprehension.
|
// Recompose message for AI hrefs, language incomprehension.
|
||||||
message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode)
|
message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode)
|
||||||
|
|||||||
@@ -861,11 +861,11 @@
|
|||||||
return get_dist(src, A) <= max(viewscale[1]*0.5,viewscale[2]*0.5)
|
return get_dist(src, A) <= max(viewscale[1]*0.5,viewscale[2]*0.5)
|
||||||
|
|
||||||
/mob/living/silicon/ai/proc/relay_speech(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode)
|
/mob/living/silicon/ai/proc/relay_speech(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode)
|
||||||
raw_message = lang_treat(speaker, message_language, raw_message, spans, message_mode)
|
var/treated_message = lang_treat(speaker, message_language, raw_message, spans, message_mode)
|
||||||
var/start = "Relayed Speech: "
|
var/start = "Relayed Speech: "
|
||||||
var/namepart = "[speaker.GetVoice()][speaker.get_alt_name()]"
|
var/namepart = "[speaker.GetVoice()][speaker.get_alt_name()]"
|
||||||
var/hrefpart = "<a href='?src=[REF(src)];track=[html_encode(namepart)]'>"
|
var/hrefpart = "<a href='?src=[REF(src)];track=[html_encode(namepart)]'>"
|
||||||
var/jobpart
|
var/jobpart = "Unknown"
|
||||||
|
|
||||||
if (iscarbon(speaker))
|
if (iscarbon(speaker))
|
||||||
var/mob/living/carbon/S = speaker
|
var/mob/living/carbon/S = speaker
|
||||||
@@ -874,7 +874,10 @@
|
|||||||
else
|
else
|
||||||
jobpart = "Unknown"
|
jobpart = "Unknown"
|
||||||
|
|
||||||
var/rendered = "<i><span class='game say'>[start]<span class='name'>[hrefpart][namepart] ([jobpart])</a> </span><span class='message'>[raw_message]</span></span></i>"
|
var/rendered = "<i><span class='game say'>[start]<span class='name'>[hrefpart][namepart] ([jobpart])</a> </span><span class='message'>[treated_message]</span></span></i>"
|
||||||
|
|
||||||
|
if (client?.prefs.chat_on_map && (client.prefs.see_chat_non_mob || ismob(speaker)))
|
||||||
|
create_chat_message(speaker, message_language, raw_message, spans, message_mode)
|
||||||
|
|
||||||
show_message(rendered, 2)
|
show_message(rendered, 2)
|
||||||
|
|
||||||
|
|||||||
BIN
icons/UI_Icons/chat/chat_icons.dmi
Normal file
BIN
icons/UI_Icons/chat/chat_icons.dmi
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 237 B |
@@ -146,6 +146,7 @@ window "mapwindow"
|
|||||||
is-default = true
|
is-default = true
|
||||||
saved-params = "icon-size"
|
saved-params = "icon-size"
|
||||||
zoom-mode = distort
|
zoom-mode = distort
|
||||||
|
style=".center { text-align: center; } .maptext { font-family: 'Small Fonts'; font-size: 7px; -dm-text-outline: 1px black; color: white; line-height: 1.1; } .command_headset { font-weight: bold; font-size: 8px; } .small { font-size: 6px; } .big { font-size: 8px; } .reallybig { font-size: 8px; } .extremelybig { font-size: 8px; } .greentext { color: #00FF00; font-size: 7px; } .redtext { color: #FF0000; font-size: 7px; } .clown { color: #FF69Bf; font-size: 7px; font-weight: bold; } .his_grace { color: #15D512; } .hypnophrase { color: #0d0d0d; font-weight: bold; } .yell { font-weight: bold; } .italics { font-size: 6px; }"
|
||||||
|
|
||||||
window "infowindow"
|
window "infowindow"
|
||||||
elem "infowindow"
|
elem "infowindow"
|
||||||
@@ -406,4 +407,3 @@ window "Telecomms IDE"
|
|||||||
command = "cancel"
|
command = "cancel"
|
||||||
multi-line = true
|
multi-line = true
|
||||||
no-command = true
|
no-command = true
|
||||||
|
|
||||||
|
|||||||
@@ -316,6 +316,7 @@
|
|||||||
#include "code\datums\beam.dm"
|
#include "code\datums\beam.dm"
|
||||||
#include "code\datums\browser.dm"
|
#include "code\datums\browser.dm"
|
||||||
#include "code\datums\callback.dm"
|
#include "code\datums\callback.dm"
|
||||||
|
#include "code\datums\chatmessage.dm"
|
||||||
#include "code\datums\cinematic.dm"
|
#include "code\datums\cinematic.dm"
|
||||||
#include "code\datums\dash_weapon.dm"
|
#include "code\datums\dash_weapon.dm"
|
||||||
#include "code\datums\datacore.dm"
|
#include "code\datums\datacore.dm"
|
||||||
|
|||||||
Reference in New Issue
Block a user