diff --git a/SQL/players2.sql b/SQL/players2.sql
index dcd57b146ee..2ad03a96199 100644
--- a/SQL/players2.sql
+++ b/SQL/players2.sql
@@ -120,7 +120,8 @@ CREATE TABLE client (
hear_instruments INTEGER,
ambience_volume INTEGER,
credits_volume INTEGER,
- antag_objectives INTEGER
+ antag_objectives INTEGER,
+ typing_indicator INTEGER,
);
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 6d0da02bb2a..f4a050ddd8e 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -648,12 +648,44 @@
//Overlays
/atom/movable/overlay
var/atom/master = null
+ var/follow_proc = /atom/movable/overlay/proc/move_to_turf_or_null
+ var/master_moved_key
+ var/master_destroyed_key
anchored = 1
/atom/movable/overlay/New()
. = ..()
+ if(!loc)
+ CRASH("[type] created in nullspace.")
+ qdel(src)
+ return
+
+ master = loc
+ name = master.name
+ dir = master.dir
+
+ if(istype(master, /atom/movable))
+ var/atom/movable/AM = master
+ master_moved_key = AM.on_moved.Add(src, follow_proc)
+ SetInitLoc()
+
+ master_destroyed_key = master.on_destroyed.Add(src, .proc/qdel_self)
verbs.len = 0
+/atom/movable/overlay/proc/qdel_self()
+ qdel(src) // Rest in peace
+
+/atom/movable/overlay/Destroy()
+ if(istype(master, /atom/movable))
+ var/atom/movable/AM = master
+ AM.on_moved.Remove(master_moved_key)
+ master.on_destroyed.Remove(master_destroyed_key)
+ master = null
+ return ..()
+
+/atom/movable/overlay/proc/SetInitLoc()
+ forceMove(master.loc)
+
/atom/movable/overlay/blob_act()
return
@@ -672,6 +704,13 @@
return src.master.attack_hand(a, b, c)
return
+/atom/movable/overlay/proc/move_to_turf_or_null(var/list/event_args, var/mob/holder)
+ var/new_loc = event_args["loc"]
+ var/turf/T = get_turf(new_loc)
+ var/atom/movable/AM = master // the proc is only called if the master has a "on_moved" event.
+ if(T != loc)
+ forceMove(T, glide_size_override = DELAY2GLIDESIZE(AM.move_speed))
+
/atom/movable/proc/attempt_to_follow(var/atom/movable/A,var/turf/T)
if(anchored)
return 0
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index ea2fdf685ce..4f5f71bf934 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -142,6 +142,7 @@ var/const/MAX_SAVE_SLOTS = 16
var/credits_volume = 75
var/window_flashing = 1
var/antag_objectives = 0 //If set to 1, solo antag roles will get the standard objectives. If set to 0, will give them a freeform objective instead.
+ var/typing_indicator = 0
//Mob preview
var/icon/preview_icon = null
@@ -379,6 +380,8 @@ var/const/MAX_SAVE_SLOTS = 16
[(pulltoggle) ? "Toggle Pulling" : "Always Pull"]
Solo Antag Objectives:
[(antag_objectives) ? "Standard" : "Freeform"]
+ Say bubbles:
+ [(typing_indicator) ? "Active" : "Inactive"]
Randomized Character Slot:
@@ -1504,6 +1507,9 @@ NOTE: The change will take effect AFTER any current recruiting periods."}
if("antag_objectives")
antag_objectives = !antag_objectives
+ if("typing_indicator")
+ typing_indicator = !typing_indicator
+
if(user.client.holder)
switch(href_list["preference"])
if("hear_ahelp")
diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm
index e9e0807965f..de498b240f7 100644
--- a/code/modules/client/preferences_savefile.dm
+++ b/code/modules/client/preferences_savefile.dm
@@ -106,6 +106,7 @@
jingle = preference_list_client["jingle"]
window_flashing = text2num(preference_list_client["window_flashing"])
antag_objectives = text2num(preference_list_client["antag_objectives"])
+ typing_indicator = text2num(preference_list_client["typing_indicator"])
ooccolor = sanitize_hexcolor(ooccolor, initial(ooccolor))
lastchangelog = sanitize_text(lastchangelog, initial(lastchangelog))
@@ -135,6 +136,7 @@
credits_volume = sanitize_integer(credits_volume, 0, 100, initial(credits_volume))
window_flashing = sanitize_integer(window_flashing, 0, 1, initial(window_flashing))
antag_objectives = sanitize_integer(antag_objectives, 0, 1, initial(antag_objectives))
+ typing_indicator = sanitize_integer(typing_indicator, 0, 1, initial(typing_indicator))
initialize_preferences()
return 1
@@ -216,15 +218,15 @@
check.Add("SELECT ckey FROM client WHERE ckey = ?", ckey)
if(check.Execute(db))
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) 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)
+ 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 (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",\
+ 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)
if(!q.Execute(db))
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()]")
return 0
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=? 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, 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=? 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)
if(!q.Execute(db))
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()]")
@@ -273,6 +275,7 @@
S["credits_volume"]<< credits_volume
S["window_flashing"]<< window_flashing
S["antag_objectives"]<< antag_objectives
+ S["typing_indicator"]<< typing_indicator
return 1
//saving volume changes
diff --git a/code/modules/migrations/SS13_Prefs/022-say_bubbles.dm b/code/modules/migrations/SS13_Prefs/022-say_bubbles.dm
new file mode 100644
index 00000000000..1badaf990e6
--- /dev/null
+++ b/code/modules/migrations/SS13_Prefs/022-say_bubbles.dm
@@ -0,0 +1,13 @@
+/datum/migration/sqlite/ss13_prefs/_022
+ id = 22
+ name = "Toggle Typing Indicator"
+
+/datum/migration/sqlite/ss13_prefs/_022/up()
+ if(!hasColumn("client","typing_indicator"))
+ return execute("ALTER TABLE `client` ADD COLUMN typing_indicator INTEGER DEFAULT 0")
+ return TRUE
+
+/datum/migration/sqlite/ss13_prefs/_022/down()
+ if(hasColumn("client","typing_indicator"))
+ return execute("ALTER TABLE `client` DROP COLUMN typing_indicator")
+ return TRUE
diff --git a/code/modules/mob/typing_indicator.dm b/code/modules/mob/typing_indicator.dm
new file mode 100644
index 00000000000..a82fd063f75
--- /dev/null
+++ b/code/modules/mob/typing_indicator.dm
@@ -0,0 +1,61 @@
+/*Typing indicators, when a mob uses the F3/F4 keys to bring the say/emote input boxes up this little buddy is
+made and follows them around until they are done (or something bad happens), helps tell nearby people that 'hey!
+I IS TYPIN'!'
+*/
+
+// Ported from Baystation12 : https://github.com/Baystation12/Baystation12
+
+/mob
+ var/atom/movable/overlay/typing_indicator/typing_indicator = null
+
+/atom/movable/overlay/typing_indicator
+ follow_proc = /atom/movable/overlay/proc/move_to_turf_or_null
+ icon = 'icons/mob/talk.dmi'
+ icon_state = "h0"
+
+/atom/movable/overlay/typing_indicator/New()
+ . = ..()
+ if(!istype(master, /mob))
+ CRASH("Master of typing_indicator has invalid type: [master.type].")
+
+/atom/movable/overlay/typing_indicator/Destroy()
+ var/mob/M = master
+ M.typing_indicator = null
+ . = ..()
+
+/atom/movable/overlay/typing_indicator/SetInitLoc()
+ forceMove(get_turf(master))
+
+/mob/proc/create_typing_indicator()
+ if(client && !stat && client.prefs.typing_indicator && src.is_visible() && isturf(src.loc))
+ if(!typing_indicator)
+ typing_indicator = new(src)
+ typing_indicator.invisibility = 0
+
+/mob/proc/remove_typing_indicator() // A bit excessive, but goes with the creation of the indicator I suppose
+ if(typing_indicator)
+ typing_indicator.invisibility = INVISIBILITY_MAXIMUM
+
+/mob/Logout()
+ remove_typing_indicator()
+ . = ..()
+
+/mob/verb/say_wrapper()
+ set name = ".Say"
+ set hidden = 1
+
+ create_typing_indicator()
+ var/message = input("","say (text)") as text|null
+ remove_typing_indicator()
+ if(message)
+ say_verb(message)
+
+/mob/verb/me_wrapper()
+ set name = ".Me"
+ set hidden = 1
+
+ create_typing_indicator()
+ var/message = input("","me (text)") as text|null
+ remove_typing_indicator()
+ if(message)
+ me_verb(message)
diff --git a/interface/skin.dmf b/interface/skin.dmf
index 93570526ad6..0d942597345 100644
--- a/interface/skin.dmf
+++ b/interface/skin.dmf
@@ -139,10 +139,10 @@ macro "macro"
command = ".screenshot"
elem "F3"
name = "F3"
- command = "say"
+ command = ".Say"
elem "F4"
name = "F4"
- command = "me"
+ command = ".Me"
elem "F5"
name = "F5"
command = "Aghost"
@@ -294,7 +294,7 @@ macro "hotkeymode"
command = "a-intent right"
elem "M"
name = "M"
- command = "me"
+ command = ".Me"
elem "Q"
name = "Q"
command = ".northwest"
@@ -315,7 +315,7 @@ macro "hotkeymode"
command = "MoveKey 2 0"
elem "T"
name = "T"
- command = "say"
+ command = ".Say"
elem "W"
name = "W"
command = "MoveKey 1 1"
@@ -354,10 +354,10 @@ macro "hotkeymode"
command = ".screenshot"
elem "F3"
name = "F3"
- command = "say"
+ command = ".Say"
elem "F4"
name = "F4"
- command = "me"
+ command = ".Me"
elem "F5"
name = "F5"
command = "Aghost"
@@ -386,32 +386,32 @@ menu "menu"
name = "&Client"
command = ""
saved-params = "is-checked"
- elem
+ elem
name = "&Quick screenshot\tF2"
command = ".screenshot auto"
category = "&Client"
saved-params = "is-checked"
- elem
+ elem
name = "&Save screenshot as...\tShift+F2"
command = ".screenshot"
category = "&Client"
saved-params = "is-checked"
- elem
+ elem
name = ""
command = ""
category = "&Client"
saved-params = "is-checked"
- elem
+ elem
name = "Reconnect"
command = ".reconnect"
category = "&Client"
saved-params = "is-checked"
- elem
+ elem
name = "&Quit"
command = ".quit"
category = "&Client"
saved-params = "is-checked"
- elem
+ elem
name = "&Display"
command = ""
saved-params = "is-checked"
@@ -429,15 +429,15 @@ menu "menu"
is-checked = true
can-check = true
group = "scaling"
- saved-params = "is-checked"
+ saved-params = "is-checked"
elem "blur"
name = "&Blur"
command = ".winset \"mapwindow.map.zoom-mode=blur\""
category = "&Display"
group = "scaling"
can-check = true
- saved-params = "is-checked"
- elem
+ saved-params = "is-checked"
+ elem
name = ""
command = ""
category = "&Display"
@@ -449,8 +449,8 @@ menu "menu"
is-checked = true
can-check = true
group = "size"
- saved-params = "is-checked"
- elem
+ saved-params = "is-checked"
+ elem
name = ""
command = ""
category = "&Display"
@@ -490,7 +490,7 @@ menu "menu"
can-check = true
group = "size"
saved-params = "is-checked"
- elem
+ elem
name = ""
command = ""
category = "&Display"
@@ -512,16 +512,16 @@ menu "menu"
category = "&Display"
can-check = true
saved-params = "is-checked"
- elem
+ elem
name = "&Help"
command = ""
saved-params = "is-checked"
- elem
+ elem
name = "&Admin help\tF1"
command = "adminhelp"
category = "&Help"
saved-params = "is-checked"
- elem
+ elem
name = "&Hotkeys"
command = "hotkeys-help"
category = "&Help"
@@ -530,12 +530,12 @@ menu "menu"
name = "Settings"
command = ""
saved-params = "is-checked"
- elem
+ elem
name = "General Settings"
command = "modifypreferences 1"
category = "Settings"
saved-params = "is-checked"
- elem
+ elem
name = "Special Roles"
command = "modifypreferences 2"
category = "Settings"
@@ -545,12 +545,12 @@ menu "menu"
command = ""
is-disabled = true
saved-params = "is-checked"
- elem
+ elem
name = "Profiler"
command = ".profile"
category = "Server"
saved-params = "is-checked"
- elem
+ elem
name = ""
command = ""
category = "Server"
diff --git a/vgstation13.dme b/vgstation13.dme
index 97388a7e732..9ccbc5d3cb3 100644
--- a/vgstation13.dme
+++ b/vgstation13.dme
@@ -1608,6 +1608,7 @@
#include "code\modules\migrations\SS13_Prefs\019-add-credits_vol.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\022-say_bubbles.dm"
#include "code\modules\migrations\SS13_Prefs\_base.dm"
#include "code\modules\mining\abandonedcrates.dm"
#include "code\modules\mining\debug_shit.dm"
@@ -1650,6 +1651,7 @@
#include "code\modules\mob\spells.dm"
#include "code\modules\mob\thermoregulation.dm"
#include "code\modules\mob\transform_procs.dm"
+#include "code\modules\mob\typing_indicator.dm"
#include "code\modules\mob\update_icons.dm"
#include "code\modules\mob\camera\camera.dm"
#include "code\modules\mob\dead\death.dm"