From 0fbbede0145ea3d3c72da1fbb7bd296dbdfcec2e Mon Sep 17 00:00:00 2001 From: nevimer <77420409+nevimer@users.noreply.github.com> Date: Wed, 26 May 2021 07:15:08 -0400 Subject: [PATCH] atom_say, working runechat checkpoint a lot of work, ports from both vorestation and nebula --- code/__HELPERS/animations.dm | 49 +++++++++++++++++++ code/_macros.dm | 2 + code/game/atoms.dm | 11 ++++- code/modules/mob/floating_message.dm | 70 ++++++++++++++++++++++++++++ code/modules/mob/living/say.dm | 1 + code/modules/mob/mob_helpers.dm | 65 +++++++++++++++----------- code/modules/mob/say.dm | 12 +++++ code/modules/mob/typing_indicator.dm | 17 ++++--- vorestation.dme | 2 + 9 files changed, 192 insertions(+), 37 deletions(-) create mode 100644 code/__HELPERS/animations.dm create mode 100644 code/modules/mob/floating_message.dm diff --git a/code/__HELPERS/animations.dm b/code/__HELPERS/animations.dm new file mode 100644 index 00000000000..6a8cc78538d --- /dev/null +++ b/code/__HELPERS/animations.dm @@ -0,0 +1,49 @@ +/proc/remove_images_from_clients(image/I, list/show_to) + for(var/client/C in show_to) + C.images -= I + qdel(I) + +/proc/fade_out(image/I, list/show_to) + animate(I, alpha = 0, time = 0.5 SECONDS, easing = EASE_IN) + addtimer(CALLBACK(GLOBAL_PROC, .proc/remove_images_from_clients, I, show_to), 0.5 SECONDS) + +/proc/animate_speech_bubble(image/I, list/show_to, duration) + var/matrix/M = matrix() + M.Scale(0,0) + I.transform = M + I.alpha = 0 + for(var/client/C in show_to) + C.images += I + animate(I, transform = 0, alpha = 255, time = 0.2 SECONDS, easing = EASE_IN) + addtimer(CALLBACK(GLOBAL_PROC, .proc/fade_out, I, show_to), (duration - 0.5 SECONDS)) + +/proc/animate_receive_damage(atom/A) + var/pixel_x_diff = rand(-2,2) + var/pixel_y_diff = rand(-2,2) + animate(A, pixel_x = A.pixel_x + pixel_x_diff, pixel_y = A.pixel_y + pixel_y_diff, time = 2) + animate(pixel_x = initial(A.pixel_x), pixel_y = initial(A.pixel_y), time = 2) + +/proc/animate_throw(atom/A) + var/ipx = A.pixel_x + var/ipy = A.pixel_y + var/mpx = 0 + var/mpy = 0 + + if(A.dir & NORTH) + mpy += 3 + else if(A.dir & SOUTH) + mpy -= 3 + if(A.dir & EAST) + mpx += 3 + else if(A.dir & WEST) + mpx -= 3 + + var/x = mpx + ipx + var/y = mpy + ipy + + animate(A, pixel_x = x, pixel_y = y, time = 0.6, easing = EASE_OUT) + + var/matrix/M = matrix(A.transform) + animate(transform = turn(A.transform, (mpx - mpy) * 4), time = 0.6, easing = EASE_OUT) + animate(pixel_x = ipx, pixel_y = ipy, time = 0.6, easing = EASE_IN) + animate(transform = M, time = 0.6, easing = EASE_IN) diff --git a/code/_macros.dm b/code/_macros.dm index 776156746ef..f5dda34d90c 100644 --- a/code/_macros.dm +++ b/code/_macros.dm @@ -25,3 +25,5 @@ #define random_id(key,min_id,max_id) uniqueness_repository.Generate(/datum/uniqueness_generator/id_random, key, min_id, max_id) #define ARGS_DEBUG log_debug("[__FILE__] - [__LINE__]") ; for(var/arg in args) { log_debug("\t[log_info_line(arg)]") } + +#define JOINTEXT(X) jointext(X, null) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 378ee3c2f10..39693fe7e19 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -751,19 +751,26 @@ /atom/proc/is_incorporeal() return FALSE -/atom/proc/atom_say(message) +/atom/proc/atom_say(message, var/datum/language/speaking = null) if(!message) return + var/italics = 0 var/list/speech_bubble_hearers = list() + var/speech_bubble_test = say_test(message) + var/image/speech_bubble = image('icons/mob/talk_vr.dmi',src,"h[speech_bubble_test]") + speech_bubble.layer = layer + speech_bubble.plane = plane for(var/mob/M in get_mobs_in_view(7, src)) M.show_message("[src] [atom_say_verb], \"[message]\"", 2, null, 1) if(M.client) speech_bubble_hearers += M.client - if(length(speech_bubble_hearers)) var/image/I = generate_speech_bubble(src, "[bubble_icon][say_test(message)]", FLY_LAYER) I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA INVOKE_ASYNC(GLOBAL_PROC, /.proc/flick_overlay, I, speech_bubble_hearers, 30) + INVOKE_ASYNC(GLOBAL_PROC, .proc/animate_speech_bubble, speech_bubble, speech_bubble_hearers , 30) + INVOKE_ASYNC(src, /atom/movable/proc/animate_chat, message, speaking, italics, speech_bubble_hearers, 30) + /atom/proc/speech_bubble(bubble_state = "", bubble_loc = src, list/bubble_recipients = list()) return diff --git a/code/modules/mob/floating_message.dm b/code/modules/mob/floating_message.dm new file mode 100644 index 00000000000..fdb8bdb5f30 --- /dev/null +++ b/code/modules/mob/floating_message.dm @@ -0,0 +1,70 @@ +var/list/floating_chat_colors = list() + +/atom/movable + var/list/stored_chat_text + +/atom/movable/proc/animate_chat(message, var/datum/language/speaking = null, small, list/show_to, duration) + set waitfor = FALSE + + // 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") + message = replacetext(message, url_scheme, "") + + var/static/regex/html_metachars = new(@"&[A-Za-z]{1,7};", "g") + message = replacetext(message, html_metachars, "") + + var/style //additional style params for the message + var/fontsize = 6 + if(small) + fontsize = 4 + var/limit = 50 + if(copytext_char(message, length_char(message) - 1) == "!!") + fontsize = 8 + limit = 80 + style += "font-weight: bold;" + + if(length_char(message) > limit) + message = "[copytext_char(message, 1, limit)]..." + + if(!floating_chat_colors[name]) + floating_chat_colors[name] = get_random_colour(0,160,230) + style += "color: [floating_chat_colors[name]];" + + // create 2 messages, one that appears if you know the language, and one that appears when you don't know the language + var/image/understood = generate_floating_text(src, capitalize(message), style, fontsize, duration, show_to) + var/image/gibberish = speaking ? generate_floating_text(src, speaking.scramble(message), style, fontsize, duration, show_to) : understood + + for(var/client/C in show_to) + if(!C.mob.is_deaf()) + if(C.mob.say_understands(null, speaking)) + C.images += understood + else + C.images += gibberish + +/proc/generate_floating_text(atom/movable/holder, message, style, size, duration, show_to) + var/image/I = image(null, holder) + I.plane = PLANE_PLAYER_HUD + I.layer = PLANE_PLAYER_HUD_ITEMS + I.alpha = 0 + I.maptext_width = 80 + I.maptext_height = 64 + I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA + I.pixel_x = -round(I.maptext_width/2) + 16 + + //style = "font: 'Small Fonts'; -dm-text-outline: 1px black; font-size: [size]px; [style]" + + I.maptext = "