mirror of
https://github.com/vgstation-coders/vgstation13.git
synced 2025-12-10 18:32:03 +00:00
Runescape-y chat on the game world (#26652)
* Runechat WIP * Runtimes + whispers + speech * Off by default * Fixes * Toggle outlines on the fly * Actually fix travis + toggle on * Background colour * Small fix. Don't merge yet, it runtimes * Fix runtimes and togglecreep * ... * Wrong proc * rgb * Fixes
This commit is contained in:
@@ -34,3 +34,9 @@
|
|||||||
#define SECURITY_POSITIONS list("Head of Security", "Warden", "Detective", "Security Officer")
|
#define SECURITY_POSITIONS list("Head of Security", "Warden", "Detective", "Security Officer")
|
||||||
|
|
||||||
#define ALWAYSTRUE 2
|
#define ALWAYSTRUE 2
|
||||||
|
|
||||||
|
#define APPEARANCE_UI_IGNORE_ALPHA (RESET_COLOR|RESET_TRANSFORM|NO_CLIENT_COLOR|RESET_ALPHA|PIXEL_SCALE)
|
||||||
|
|
||||||
|
#define SPEECH_MODE_SAY 1
|
||||||
|
#define SPEECH_MODE_WHISPER 2
|
||||||
|
#define SPEECH_MODE_FINAL 3
|
||||||
|
|||||||
@@ -195,6 +195,7 @@ What is the naming convention for planes or layers?
|
|||||||
#define ABOVE_SINGULO_LAYER 7
|
#define ABOVE_SINGULO_LAYER 7
|
||||||
#define GRAVITYGRID_LAYER 8
|
#define GRAVITYGRID_LAYER 8
|
||||||
#define SNOW_OVERLAY_LAYER 9
|
#define SNOW_OVERLAY_LAYER 9
|
||||||
|
#define CHAT_LAYER 12.1
|
||||||
|
|
||||||
#define GHOST_PLANE 19 // Ghosts show up under lighting, HUD etc.
|
#define GHOST_PLANE 19 // Ghosts show up under lighting, HUD etc.
|
||||||
|
|
||||||
|
|||||||
258
code/datums/chat_message.dm
Normal file
258
code/datums/chat_message.dm
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
#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.6 // 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 68 // 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
|
||||||
|
* Ported from TGStation; https://github.com/tgstation/tgstation/pull/50608/, author: bobbahbrown
|
||||||
|
*/
|
||||||
|
/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(!istype(owner) || owner.gcDestroyed || !owner.client)
|
||||||
|
stack_trace("/datum/chatmessage created with [isnull(owner) ? "null" : "invalid"] mob owner")
|
||||||
|
qdel(src)
|
||||||
|
return
|
||||||
|
generate_image(text, target, owner, extra_classes, lifespan)
|
||||||
|
|
||||||
|
/datum/chatmessage/Destroy()
|
||||||
|
if (owned_by)
|
||||||
|
owned_by.seen_messages.Remove(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
|
||||||
|
owner.on_destroyed.Add(src, .proc/qdel_self)
|
||||||
|
|
||||||
|
// 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 comes from a radio
|
||||||
|
if (extra_classes.Find("spoken_into_radio"))
|
||||||
|
var/image/r_icon = image('icons/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 = "<div class='runechatdiv'><span class='center maptext [extra_classes != null ? extra_classes.Join(" ") : ""]' style='color: [tgt_color];'>[text]</span></div>"
|
||||||
|
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)
|
||||||
|
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
|
||||||
|
spawn(remaining_time)
|
||||||
|
m.end_of_life()
|
||||||
|
|
||||||
|
// Build message image
|
||||||
|
message = image(loc = message_loc, layer = CHAT_LAYER)
|
||||||
|
message.plane = MOB_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
|
||||||
|
owned_by.seen_messages.Add(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)
|
||||||
|
spawn(lifespan - CHAT_MESSAGE_EOL_FADE)
|
||||||
|
end_of_life()
|
||||||
|
|
||||||
|
/datum/chatmessage/proc/qdel_self()
|
||||||
|
qdel(src)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
if (gcDestroyed)
|
||||||
|
return
|
||||||
|
animate(message, alpha = 0, time = fadetime, flags = ANIMATION_PARALLEL)
|
||||||
|
spawn(fadetime)
|
||||||
|
qdel(src)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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, mode, list/existing_extra_classes)
|
||||||
|
// Check for virtual speakers (aka hearing a message through a radio)
|
||||||
|
if (existing_extra_classes.Find("radio"))
|
||||||
|
return
|
||||||
|
|
||||||
|
var/list/extra_classes = list()
|
||||||
|
extra_classes += existing_extra_classes
|
||||||
|
|
||||||
|
if (mode == SPEECH_MODE_WHISPER)
|
||||||
|
extra_classes |= "small"
|
||||||
|
|
||||||
|
if (client.toggle_runechat_outlines)
|
||||||
|
extra_classes |= "black_outline"
|
||||||
|
|
||||||
|
var/dist = get_dist(src, speaker)
|
||||||
|
switch (dist)
|
||||||
|
if (4 to 5)
|
||||||
|
extra_classes |= "small"
|
||||||
|
if (5 to 16)
|
||||||
|
extra_classes |= "very_small"
|
||||||
|
|
||||||
|
if (!say_understands(speaker, message_language))
|
||||||
|
raw_message = message_language.scramble(raw_message)
|
||||||
|
|
||||||
|
// Display visual above source
|
||||||
|
new /datum/chatmessage(raw_message, speaker, src, extra_classes)
|
||||||
|
|
||||||
|
// 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 + "[world_startup_time]"), 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 rgba
|
||||||
|
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 "[rgb(c,x,m)]C8"
|
||||||
|
if(1)
|
||||||
|
return "[rgb(x,c,m)]C8"
|
||||||
|
if(2)
|
||||||
|
return "[rgb(m,c,x)]C8"
|
||||||
|
if(3)
|
||||||
|
return "[rgb(m,x,c)]C8"
|
||||||
|
if(4)
|
||||||
|
return "[rgb(x,m,c)]C8"
|
||||||
|
if(5)
|
||||||
|
return "[rgb(c,m,x)]C8"
|
||||||
|
|
||||||
|
/client/verb/toggle_runechat_outline()
|
||||||
|
set category = "OOC"
|
||||||
|
set name = "Toggle Runechat Outlines"
|
||||||
|
toggle_runechat_outlines = !toggle_runechat_outlines
|
||||||
|
to_chat(mob, "<span class='notice'>Runechat outlines are now [toggle_runechat_outlines ? "enabled" : "disabled"].</span>")
|
||||||
@@ -1,7 +1,3 @@
|
|||||||
#define SPEECH_MODE_SAY 1
|
|
||||||
#define SPEECH_MODE_WHISPER 2
|
|
||||||
#define SPEECH_MODE_FINAL 3
|
|
||||||
|
|
||||||
/datum/speech
|
/datum/speech
|
||||||
var/name = "" // Displayed name
|
var/name = "" // Displayed name
|
||||||
var/as_name = "" // (as [as_name])
|
var/as_name = "" // (as [as_name])
|
||||||
@@ -168,5 +164,7 @@
|
|||||||
if(data)
|
if(data)
|
||||||
wrapper_classes=data.Copy()
|
wrapper_classes=data.Copy()
|
||||||
|
|
||||||
|
wrapper_classes.Add("radio")
|
||||||
|
|
||||||
/datum/speech/proc/set_language(var/lang_id)
|
/datum/speech/proc/set_language(var/lang_id)
|
||||||
language = all_languages[lang_id]
|
language = all_languages[lang_id]
|
||||||
|
|||||||
@@ -48,6 +48,14 @@ var/global/list/ghdel_profiling = list()
|
|||||||
appearance_flags = TILE_BOUND|LONG_GLIDE
|
appearance_flags = TILE_BOUND|LONG_GLIDE
|
||||||
|
|
||||||
var/slowdown_modifier //modified on how fast a person can move over the tile we are on, see turf.dm for more info
|
var/slowdown_modifier //modified on how fast a person can move over the tile we are on, see turf.dm for more info
|
||||||
|
/// Last name used to calculate a color for the chatmessage overlays
|
||||||
|
var/chat_color_name
|
||||||
|
/// Last color calculated for the the chatmessage overlays
|
||||||
|
var/chat_color
|
||||||
|
/// A luminescence-shifted value of the last color calculated for chatmessage overlays
|
||||||
|
var/chat_color_darkened
|
||||||
|
/// The chat color var, without alpha.
|
||||||
|
var/chat_color_hover
|
||||||
|
|
||||||
/atom/proc/beam_connect(var/obj/effect/beam/B)
|
/atom/proc/beam_connect(var/obj/effect/beam/B)
|
||||||
if(!last_beamchecks)
|
if(!last_beamchecks)
|
||||||
|
|||||||
@@ -91,6 +91,9 @@
|
|||||||
var/received_credits = FALSE
|
var/received_credits = FALSE
|
||||||
var/received_roundend_audio = FALSE
|
var/received_roundend_audio = FALSE
|
||||||
|
|
||||||
|
// Runechat messages
|
||||||
|
var/list/seen_messages = list()
|
||||||
|
var/toggle_runechat_outlines = TRUE
|
||||||
|
|
||||||
var/list/person_animation_viewers = list()
|
var/list/person_animation_viewers = list()
|
||||||
var/list/item_animation_viewers = list()
|
var/list/item_animation_viewers = list()
|
||||||
|
|||||||
@@ -215,6 +215,12 @@ var/const/MAX_SAVE_SLOTS = 16
|
|||||||
var/credits = CREDITS_ALWAYS
|
var/credits = CREDITS_ALWAYS
|
||||||
var/jingle = JINGLE_CLASSIC
|
var/jingle = JINGLE_CLASSIC
|
||||||
|
|
||||||
|
// Runscape-like chat
|
||||||
|
var/mob_chat_on_map = FALSE
|
||||||
|
var/max_chat_length = CHAT_MESSAGE_MAX_LENGTH
|
||||||
|
var/obj_chat_on_map = FALSE
|
||||||
|
var/no_goonchat_for_obj = FALSE
|
||||||
|
|
||||||
var/client/client
|
var/client/client
|
||||||
var/saveloaded = 0
|
var/saveloaded = 0
|
||||||
|
|
||||||
@@ -417,6 +423,15 @@ var/const/MAX_SAVE_SLOTS = 16
|
|||||||
<a href='?_src_=prefs;preference=credits_volume'><b>[credits_volume]</b></a><br>
|
<a href='?_src_=prefs;preference=credits_volume'><b>[credits_volume]</b></a><br>
|
||||||
<b>Window Flashing</b>
|
<b>Window Flashing</b>
|
||||||
<a href='?_src_=prefs;preference=window_flashing'><b>[(window_flashing) ? "Yes":"No"]</b></a><br>
|
<a href='?_src_=prefs;preference=window_flashing'><b>[(window_flashing) ? "Yes":"No"]</b></a><br>
|
||||||
|
<center>Runechat prefererences</center>
|
||||||
|
<b>Chat on map for mobs:</b>
|
||||||
|
<a href='?_src_=prefs;preference=mob_chat_on_map'>[mob_chat_on_map ? "Enabled" : "Disabled"]</a><br>
|
||||||
|
<b>Chat on map for objects:</b>
|
||||||
|
<a href='?_src_=prefs;preference=obj_chat_on_map'>[obj_chat_on_map ? "Enabled" : "Disabled"]</a><br>
|
||||||
|
<b>No goonchat messages for objects:</b>
|
||||||
|
<a href='?_src_=prefs;preference=no_goonchat_for_obj'>[no_goonchat_for_obj ? "Enabled" : "Disabled"]</a><br>
|
||||||
|
<b>Runechat message char limit:</b>
|
||||||
|
<a href='?_src_=prefs;preference=max_chat_length;task=input'>[max_chat_length]</a><br>
|
||||||
</div>
|
</div>
|
||||||
</div>"}
|
</div>"}
|
||||||
|
|
||||||
@@ -1517,6 +1532,19 @@ NOTE: The change will take effect AFTER any current recruiting periods."}
|
|||||||
if("typing_indicator")
|
if("typing_indicator")
|
||||||
typing_indicator = !typing_indicator
|
typing_indicator = !typing_indicator
|
||||||
|
|
||||||
|
if ("mob_chat_on_map")
|
||||||
|
mob_chat_on_map = !mob_chat_on_map
|
||||||
|
|
||||||
|
if ("obj_chat_on_map")
|
||||||
|
obj_chat_on_map = !obj_chat_on_map
|
||||||
|
|
||||||
|
if ("max_chat_length")
|
||||||
|
max_chat_length = 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 ("no_goonchat_for_obj")
|
||||||
|
no_goonchat_for_obj = !no_goonchat_for_obj
|
||||||
|
|
||||||
|
|
||||||
if(user.client.holder)
|
if(user.client.holder)
|
||||||
switch(href_list["preference"])
|
switch(href_list["preference"])
|
||||||
if("hear_ahelp")
|
if("hear_ahelp")
|
||||||
|
|||||||
@@ -137,6 +137,10 @@
|
|||||||
window_flashing = sanitize_integer(window_flashing, 0, 1, initial(window_flashing))
|
window_flashing = sanitize_integer(window_flashing, 0, 1, initial(window_flashing))
|
||||||
antag_objectives = sanitize_integer(antag_objectives, 0, 1, initial(antag_objectives))
|
antag_objectives = sanitize_integer(antag_objectives, 0, 1, initial(antag_objectives))
|
||||||
typing_indicator = sanitize_integer(typing_indicator, 0, 1, initial(typing_indicator))
|
typing_indicator = sanitize_integer(typing_indicator, 0, 1, initial(typing_indicator))
|
||||||
|
mob_chat_on_map = sanitize_integer(mob_chat_on_map, 0, 1, initial(mob_chat_on_map))
|
||||||
|
max_chat_length = sanitize_integer(max_chat_length, 0, CHAT_MESSAGE_MAX_LENGTH, initial(typing_indicator))
|
||||||
|
obj_chat_on_map = sanitize_integer(obj_chat_on_map, 0, 1, initial(obj_chat_on_map))
|
||||||
|
no_goonchat_for_obj = sanitize_integer(no_goonchat_for_obj, 0, 1, initial(no_goonchat_for_obj))
|
||||||
initialize_preferences()
|
initialize_preferences()
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
@@ -218,15 +222,15 @@
|
|||||||
check.Add("SELECT ckey FROM client WHERE ckey = ?", ckey)
|
check.Add("SELECT ckey FROM client WHERE ckey = ?", ckey)
|
||||||
if(check.Execute(db))
|
if(check.Execute(db))
|
||||||
if(!check.NextRow())
|
if(!check.NextRow())
|
||||||
q.Add("INSERT into client (ckey, ooc_color, lastchangelog, UI_style, default_slot, toggles, UI_style_color, UI_style_alpha, warns, warnbans, randomslot, volume, usewmp, special, usenanoui, tooltips, progress_bars, space_parallax, space_dust, parallax_speed, stumble, attack_animation, pulltoggle, credits, jingle, hear_voicesound, hear_instruments, ambience_volume, credits_volume, window_flashing, antag_objectives, typing_indicator) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",\
|
q.Add("INSERT into client (ckey, ooc_color, lastchangelog, UI_style, default_slot, toggles, UI_style_color, UI_style_alpha, warns, warnbans, randomslot, volume, usewmp, special, usenanoui, tooltips, progress_bars, space_parallax, space_dust, parallax_speed, stumble, attack_animation, pulltoggle, credits, jingle, hear_voicesound, hear_instruments, ambience_volume, credits_volume, window_flashing, antag_objectives, typing_indicator, mob_chat_on_map, max_chat_length, obj_chat_on_map, no_goonchat_for_obj) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?, ?)",\
|
||||||
ckey, ooccolor, lastchangelog, UI_style, default_slot, toggles, UI_style_color, UI_style_alpha, warns, warnbans, randomslot, volume, usewmp, special_popup, usenanoui, tooltips, progress_bars, space_parallax, space_dust, parallax_speed, stumble, attack_animation, pulltoggle, credits, jingle, hear_voicesound, hear_instruments, ambience_volume, credits_volume, window_flashing, antag_objectives, typing_indicator)
|
ckey, ooccolor, lastchangelog, UI_style, default_slot, toggles, UI_style_color, UI_style_alpha, warns, warnbans, randomslot, volume, usewmp, special_popup, usenanoui, tooltips, progress_bars, space_parallax, space_dust, parallax_speed, stumble, attack_animation, pulltoggle, credits, jingle, hear_voicesound, hear_instruments, ambience_volume, credits_volume, window_flashing, antag_objectives, typing_indicator, mob_chat_on_map, max_chat_length, obj_chat_on_map, no_goonchat_for_obj)
|
||||||
if(!q.Execute(db))
|
if(!q.Execute(db))
|
||||||
message_admins("Error in save_preferences_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]")
|
message_admins("Error in save_preferences_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]")
|
||||||
WARNING("Error in save_preferences_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
|
WARNING("Error in save_preferences_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
q.Add("UPDATE client SET ooc_color=?,lastchangelog=?,UI_style=?,default_slot=?,toggles=?,UI_style_color=?,UI_style_alpha=?,warns=?,warnbans=?,randomslot=?,volume=?,usewmp=?,special=?,usenanoui=?,tooltips=?,progress_bars=?,space_parallax=?,space_dust=?,parallax_speed=?, stumble=?, attack_animation=?, pulltoggle=?, credits=?, jingle=?, hear_voicesound=?, hear_instruments=?, ambience_volume=?, credits_volume=?, window_flashing=?, antag_objectives=? , typing_indicator=? WHERE ckey = ?",\
|
q.Add("UPDATE client SET ooc_color=?,lastchangelog=?,UI_style=?,default_slot=?,toggles=?,UI_style_color=?,UI_style_alpha=?,warns=?,warnbans=?,randomslot=?,volume=?,usewmp=?,special=?,usenanoui=?,tooltips=?,progress_bars=?,space_parallax=?,space_dust=?,parallax_speed=?, stumble=?, attack_animation=?, pulltoggle=?, credits=?, jingle=?, hear_voicesound=?, hear_instruments=?, ambience_volume=?, credits_volume=?, window_flashing=?, antag_objectives=? , typing_indicator=? , mob_chat_on_map=? , max_chat_length=?, obj_chat_on_map=?, no_goonchat_for_obj=?, WHERE ckey = ?",\
|
||||||
ooccolor, lastchangelog, UI_style, default_slot, toggles, UI_style_color, UI_style_alpha, warns, warnbans, randomslot, volume, usewmp, special_popup, usenanoui, tooltips, progress_bars, space_parallax, space_dust, parallax_speed, stumble, attack_animation, pulltoggle, credits, jingle, hear_voicesound, hear_instruments, ambience_volume, credits_volume, window_flashing, antag_objectives, typing_indicator, ckey)
|
ooccolor, lastchangelog, UI_style, default_slot, toggles, UI_style_color, UI_style_alpha, warns, warnbans, randomslot, volume, usewmp, special_popup, usenanoui, tooltips, progress_bars, space_parallax, space_dust, parallax_speed, stumble, attack_animation, pulltoggle, credits, jingle, hear_voicesound, hear_instruments, ambience_volume, credits_volume, window_flashing, antag_objectives, typing_indicator, mob_chat_on_map, max_chat_length, obj_chat_on_map,no_goonchat_for_obj, ckey)
|
||||||
if(!q.Execute(db))
|
if(!q.Execute(db))
|
||||||
message_admins("Error in save_preferences_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]")
|
message_admins("Error in save_preferences_sqlite [__FILE__] ln:[__LINE__] #: [q.Error()] - [q.ErrorMsg()]")
|
||||||
WARNING("Error in save_preferences_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
|
WARNING("Error in save_preferences_sqlite [__FILE__] ln:[__LINE__] #:[q.Error()] - [q.ErrorMsg()]")
|
||||||
@@ -276,6 +280,10 @@
|
|||||||
S["window_flashing"]<< window_flashing
|
S["window_flashing"]<< window_flashing
|
||||||
S["antag_objectives"]<< antag_objectives
|
S["antag_objectives"]<< antag_objectives
|
||||||
S["typing_indicator"]<< typing_indicator
|
S["typing_indicator"]<< typing_indicator
|
||||||
|
S["mob_chat_on_map"] << mob_chat_on_map
|
||||||
|
S["max_chat_length"] << max_chat_length
|
||||||
|
S["obj_chat_on_map"] << obj_chat_on_map
|
||||||
|
S["no_goonchat_for_obj"] << no_goonchat_for_obj
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
//saving volume changes
|
//saving volume changes
|
||||||
|
|||||||
33
code/modules/migrations/SS13_Prefs/023-runechat.dm
Normal file
33
code/modules/migrations/SS13_Prefs/023-runechat.dm
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/datum/migration/sqlite/ss13_prefs/_023
|
||||||
|
id = 23
|
||||||
|
name = "Runechat"
|
||||||
|
|
||||||
|
/datum/migration/sqlite/ss13_prefs/_023/up()
|
||||||
|
var/mig1
|
||||||
|
var/mig2
|
||||||
|
var/mig3
|
||||||
|
var/mig4
|
||||||
|
if(!hasColumn("client","mob_chat_on_map"))
|
||||||
|
mig1 = execute("ALTER TABLE `client` ADD COLUMN mob_chat_on_map INTEGER DEFAULT 0")
|
||||||
|
if(!hasColumn("client","max_chat_length"))
|
||||||
|
mig2 = execute("ALTER TABLE `client` ADD COLUMN max_chat_length INTEGER DEFAULT [CHAT_MESSAGE_MAX_LENGTH]")
|
||||||
|
if(!hasColumn("client","obj_chat_on_map"))
|
||||||
|
mig3 = execute("ALTER TABLE `client` ADD COLUMN obj_chat_on_map INTEGER DEFAULT 0")
|
||||||
|
if(!hasColumn("client","no_goonchat_for_obj"))
|
||||||
|
mig4 = execute("ALTER TABLE `client` ADD COLUMN no_goonchat_for_obj INTEGER DEFAULT 0")
|
||||||
|
return mig1 && mig2 && mig3 && mig4
|
||||||
|
|
||||||
|
/datum/migration/sqlite/ss13_prefs/_023/down()
|
||||||
|
var/mig1
|
||||||
|
var/mig2
|
||||||
|
var/mig3
|
||||||
|
var/mig4
|
||||||
|
if(hasColumn("client","mob_chat_on_map"))
|
||||||
|
mig1 = execute("ALTER TABLE `client` DROP COLUMN mob_chat_on_map")
|
||||||
|
if(hasColumn("client","obj_chat_on_map"))
|
||||||
|
mig2 = execute("ALTER TABLE `client` DROP COLUMN obj_chat_on_map")
|
||||||
|
if(hasColumn("client","max_chat_length"))
|
||||||
|
mig3 = execute("ALTER TABLE `client` DROP COLUMN max_chat_length")
|
||||||
|
if(hasColumn("client","no_goonchat_for_obj"))
|
||||||
|
mig4 = execute("ALTER TABLE `client` DROP COLUMN no_goonchat_for_obj")
|
||||||
|
return mig1 && mig2 && mig3 && mig4
|
||||||
@@ -45,5 +45,7 @@
|
|||||||
if ((client.prefs.toggles & CHAT_GHOSTRADIO) != CHAT_GHOSTRADIO)
|
if ((client.prefs.toggles & CHAT_GHOSTRADIO) != CHAT_GHOSTRADIO)
|
||||||
say_testing(src, "/mob/dead/observer/Hear(): CHAT_GHOSTRADIO is disabled, blocking. ([client.prefs.toggles] & [CHAT_GHOSTRADIO]) = [client.prefs.toggles & CHAT_GHOSTRADIO]")
|
say_testing(src, "/mob/dead/observer/Hear(): CHAT_GHOSTRADIO is disabled, blocking. ([client.prefs.toggles] & [CHAT_GHOSTRADIO]) = [client.prefs.toggles & CHAT_GHOSTRADIO]")
|
||||||
return
|
return
|
||||||
|
if (client?.prefs.mob_chat_on_map && (client.prefs.obj_chat_on_map || ismob(speech.speaker)))
|
||||||
|
create_chat_message(speech.speaker, speech.language, speech.message, speech.mode, speech.wrapper_classes)
|
||||||
|
|
||||||
to_chat(src, "<a href='?src=\ref[src];follow=\ref[source]'>(Follow)</a> [rendered_speech]")
|
to_chat(src, "<a href='?src=\ref[src];follow=\ref[source]'>(Follow)</a> [rendered_speech]")
|
||||||
|
|||||||
@@ -74,6 +74,19 @@ var/list/department_radio_keys = list(
|
|||||||
":&" = "borerchat", "#&" = "borerchat", ".&" = "borerchat",
|
":&" = "borerchat", "#&" = "borerchat", ".&" = "borerchat",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var/list/headset_modes = list(
|
||||||
|
"Response Team",
|
||||||
|
"Command",
|
||||||
|
"Service",
|
||||||
|
"Engineering",
|
||||||
|
"Security",
|
||||||
|
"Syndicate",
|
||||||
|
"Supply",
|
||||||
|
"Medical",
|
||||||
|
"Science",
|
||||||
|
"department",
|
||||||
|
)
|
||||||
|
|
||||||
/mob/living/proc/get_default_language()
|
/mob/living/proc/get_default_language()
|
||||||
if(!default_language)
|
if(!default_language)
|
||||||
if(languages && languages.len)
|
if(languages && languages.len)
|
||||||
@@ -181,6 +194,8 @@ var/list/department_radio_keys = list(
|
|||||||
treat_speech(speech)
|
treat_speech(speech)
|
||||||
|
|
||||||
var/radio_return = get_speech_flags(message_mode)
|
var/radio_return = get_speech_flags(message_mode)
|
||||||
|
if (speech_was_spoken_into_radio(message_mode))
|
||||||
|
speech.wrapper_classes.Add("spoken_into_radio")
|
||||||
if(radio_return & NOPASS) //There's a whisper() message_mode, no need to continue the proc if that is called
|
if(radio_return & NOPASS) //There's a whisper() message_mode, no need to continue the proc if that is called
|
||||||
whisper(speech.message, speech.language)
|
whisper(speech.message, speech.language)
|
||||||
returnToPool(speech)
|
returnToPool(speech)
|
||||||
@@ -261,7 +276,16 @@ var/list/department_radio_keys = list(
|
|||||||
rendered_message = replacetextEx(rendered_message, "AI", "<i style='color: blue;'>AI</i>")
|
rendered_message = replacetextEx(rendered_message, "AI", "<i style='color: blue;'>AI</i>")
|
||||||
rendered_message = replacetext(rendered_message, ai.real_name, "<i style='color: blue;'>[ai.real_name]</i>")
|
rendered_message = replacetext(rendered_message, ai.real_name, "<i style='color: blue;'>[ai.real_name]</i>")
|
||||||
|
|
||||||
|
// Runechat messages
|
||||||
|
if (ismob(speech.speaker) && client?.prefs.mob_chat_on_map && stat != UNCONSCIOUS && !is_deaf())
|
||||||
|
create_chat_message(speech.speaker, speech.language, speech.message, speech.mode, speech.wrapper_classes)
|
||||||
|
else if (client?.prefs.obj_chat_on_map && stat != UNCONSCIOUS && !is_deaf())
|
||||||
|
create_chat_message(speech.speaker, speech.language, speech.message, speech.mode, speech.wrapper_classes)
|
||||||
|
if (ismob(speech.speaker))
|
||||||
show_message(rendered_message, type, deaf_message, deaf_type, src)
|
show_message(rendered_message, type, deaf_message, deaf_type, src)
|
||||||
|
else if (!client.prefs.no_goonchat_for_obj || length_char(speech.message) > client?.prefs.max_chat_length) // Objects : only display if no goonchat on map or if the runemessage is too small.
|
||||||
|
show_message(rendered_message, type, deaf_message, deaf_type, src)
|
||||||
|
|
||||||
return rendered_message
|
return rendered_message
|
||||||
|
|
||||||
/mob/living/proc/hear_radio_only()
|
/mob/living/proc/hear_radio_only()
|
||||||
@@ -454,6 +478,14 @@ var/list/department_radio_keys = list(
|
|||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
/mob/living/proc/speech_was_spoken_into_radio(var/message_mode)
|
||||||
|
if (message_mode in headset_modes)
|
||||||
|
return TRUE
|
||||||
|
switch (message_mode)
|
||||||
|
if(MODE_HEADSET, MODE_SECURE_HEADSET, MODE_R_HAND, MODE_L_HAND, MODE_INTERCOM, MODE_BINARY)
|
||||||
|
return TRUE
|
||||||
|
return FALSE
|
||||||
|
|
||||||
/mob/living/proc/radio(var/datum/speech/speech, var/message_mode)
|
/mob/living/proc/radio(var/datum/speech/speech, var/message_mode)
|
||||||
switch(message_mode)
|
switch(message_mode)
|
||||||
if(MODE_R_HAND)
|
if(MODE_R_HAND)
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
#define WORLD_ICON_SIZE 32
|
#define WORLD_ICON_SIZE 32
|
||||||
#define PIXEL_MULTIPLIER WORLD_ICON_SIZE/32
|
#define PIXEL_MULTIPLIER WORLD_ICON_SIZE/32
|
||||||
|
|
||||||
|
var/world_startup_time
|
||||||
|
|
||||||
/world
|
/world
|
||||||
mob = /mob/new_player
|
mob = /mob/new_player
|
||||||
turf = /turf/space
|
turf = /turf/space
|
||||||
@@ -7,11 +10,13 @@
|
|||||||
cache_lifespan = 0 //stops player uploaded stuff from being kept in the rsc past the current session
|
cache_lifespan = 0 //stops player uploaded stuff from being kept in the rsc past the current session
|
||||||
//loop_checks = 0
|
//loop_checks = 0
|
||||||
icon_size = WORLD_ICON_SIZE
|
icon_size = WORLD_ICON_SIZE
|
||||||
#define RECOMMENDED_VERSION 512
|
|
||||||
|
#define RECOMMENDED_VERSION 513
|
||||||
|
|
||||||
|
|
||||||
var/savefile/panicfile
|
var/savefile/panicfile
|
||||||
/world/New()
|
/world/New()
|
||||||
|
world_startup_time = world.timeofday
|
||||||
var/extools_path = system_type == MS_WINDOWS ? "byond-extools.dll" : "libbyond-extools.so"
|
var/extools_path = system_type == MS_WINDOWS ? "byond-extools.dll" : "libbyond-extools.so"
|
||||||
if(fexists(extools_path))
|
if(fexists(extools_path))
|
||||||
call(extools_path, "maptick_initialize")()
|
call(extools_path, "maptick_initialize")()
|
||||||
@@ -68,7 +73,7 @@ var/savefile/panicfile
|
|||||||
*/
|
*/
|
||||||
#ifdef BORDER_USE_TURF_EXIT
|
#ifdef BORDER_USE_TURF_EXIT
|
||||||
if(byond_version < RECOMMENDED_VERSION)
|
if(byond_version < RECOMMENDED_VERSION)
|
||||||
warning("Your server's byond version does not meet the recommended requirements for this code. Please update BYOND to atleast 512.1426")
|
warning("Your server's byond version does not meet the recommended requirements for this code. Please update BYOND to atleast 513.")
|
||||||
#endif
|
#endif
|
||||||
make_datum_references_lists() //initialises global lists for referencing frequently used datums (so that we only ever do it once)
|
make_datum_references_lists() //initialises global lists for referencing frequently used datums (so that we only ever do it once)
|
||||||
|
|
||||||
|
|||||||
BIN
icons/chat_icons.dmi
Normal file
BIN
icons/chat_icons.dmi
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 237 B |
@@ -782,6 +782,7 @@ window "mapwindow"
|
|||||||
text-color = none
|
text-color = none
|
||||||
is-default = true
|
is-default = true
|
||||||
saved-params = "icon-size"
|
saved-params = "icon-size"
|
||||||
|
style=".center { text-align: center; } .runechatdiv {background-color: #20202070} .black_outline { -dm-text-outline: 1px black } .maptext { font-family: 'Small Fonts'; font-size: 7px; color: white; line-height: 1.1; } .command_headset { font-weight: bold; font-size: 8px; } .small { font-size: 6px; } .very_small { font-size: 5px;} .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; }"
|
||||||
elem "credits"
|
elem "credits"
|
||||||
type = BROWSER
|
type = BROWSER
|
||||||
pos = 0,0
|
pos = 0,0
|
||||||
|
|||||||
@@ -764,6 +764,7 @@ window "mapwindow"
|
|||||||
text-color = none
|
text-color = none
|
||||||
is-default = true
|
is-default = true
|
||||||
saved-params = "icon-size"
|
saved-params = "icon-size"
|
||||||
|
style=".center { text-align: center; } .runechatdiv {background-color: #20202070} .black_outline { -dm-text-outline: 1px black } .maptext { font-family: 'Small Fonts'; font-size: 7px; color: white; line-height: 1.1; } .command_headset { font-weight: bold; font-size: 8px; } .small { font-size: 6px; } .very_small { font-size: 5px;} .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; }"
|
||||||
elem "credits"
|
elem "credits"
|
||||||
type = BROWSER
|
type = BROWSER
|
||||||
pos = 0,0
|
pos = 0,0
|
||||||
|
|||||||
@@ -243,6 +243,7 @@
|
|||||||
#include "code\datums\ai_laws.dm"
|
#include "code\datums\ai_laws.dm"
|
||||||
#include "code\datums\alt_control.dm"
|
#include "code\datums\alt_control.dm"
|
||||||
#include "code\datums\browser.dm"
|
#include "code\datums\browser.dm"
|
||||||
|
#include "code\datums\chat_message.dm"
|
||||||
#include "code\datums\circuits.dm"
|
#include "code\datums\circuits.dm"
|
||||||
#include "code\datums\climate.dm"
|
#include "code\datums\climate.dm"
|
||||||
#include "code\datums\computerfiles.dm"
|
#include "code\datums\computerfiles.dm"
|
||||||
@@ -1629,6 +1630,7 @@
|
|||||||
#include "code\modules\migrations\SS13_Prefs\020-window_flashing.dm"
|
#include "code\modules\migrations\SS13_Prefs\020-window_flashing.dm"
|
||||||
#include "code\modules\migrations\SS13_Prefs\021-antag_objectives.dm"
|
#include "code\modules\migrations\SS13_Prefs\021-antag_objectives.dm"
|
||||||
#include "code\modules\migrations\SS13_Prefs\022-say_bubbles.dm"
|
#include "code\modules\migrations\SS13_Prefs\022-say_bubbles.dm"
|
||||||
|
#include "code\modules\migrations\SS13_Prefs\023-runechat.dm"
|
||||||
#include "code\modules\migrations\SS13_Prefs\_base.dm"
|
#include "code\modules\migrations\SS13_Prefs\_base.dm"
|
||||||
#include "code\modules\mining\abandonedcrates.dm"
|
#include "code\modules\mining\abandonedcrates.dm"
|
||||||
#include "code\modules\mining\debug_shit.dm"
|
#include "code\modules\mining\debug_shit.dm"
|
||||||
|
|||||||
Reference in New Issue
Block a user