Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
90c4405
I hate life
Jan 28, 2026
ec4300d
.
Feb 2, 2026
4e29768
idfk
Feb 2, 2026
366645d
Merge remote-tracking branch 'upstream/main' into wip/tgui_panel
Feb 4, 2026
422fbb8
effective copypasting
CheffieGithub Feb 6, 2026
4c9c410
nothing will go wrong
CheffieGithub Feb 6, 2026
d56671b
Building dependancies
CheffieGithub Feb 6, 2026
3902611
h
CheffieGithub Feb 6, 2026
37cf403
Merge pull request #1 from CheffieGithub/forest_tg
realforest2001 Feb 6, 2026
924b873
create panel
CheffieGithub Feb 6, 2026
b6d8dfc
xd
CheffieGithub Feb 6, 2026
072fae3
Merge pull request #2 from CheffieGithub/forest_tg
realforest2001 Feb 6, 2026
bd8ec48
remove stat()
CheffieGithub Feb 6, 2026
b41ba44
don't delete chat
CheffieGithub Feb 6, 2026
caae466
verb wrappers for panel updates
CheffieGithub Feb 6, 2026
b4b4ee5
bleh
CheffieGithub Feb 6, 2026
af4b9e4
Merge pull request #3 from CheffieGithub/forest_tg
realforest2001 Feb 6, 2026
dedc381
.
Feb 6, 2026
bcfec6e
Merge remote-tracking branch 'origin/wip/tgui_panel' into wip/tgui_panel
Feb 6, 2026
ac71e81
listed turf removal of unused
CheffieGithub Feb 6, 2026
cdce354
Fix CI?
CheffieGithub Feb 6, 2026
713af7c
Merge pull request #4 from CheffieGithub/forest_tg
CheffieGithub Feb 6, 2026
068218b
adds exec build files
Feb 6, 2026
259c0e0
update executable flags
CheffieGithub Feb 6, 2026
c60d91a
Merge pull request #5 from CheffieGithub/forest_tg
CheffieGithub Feb 6, 2026
5ab8a1e
AND THAT ONE
CheffieGithub Feb 6, 2026
8a99f30
FIx the other sh as well (#6)
CheffieGithub Feb 6, 2026
3d845f8
In tears
CheffieGithub Feb 6, 2026
d4cc8cf
Merge pull request #7 from CheffieGithub/forest_tg
CheffieGithub Feb 6, 2026
c78d1a2
FUCK
CheffieGithub Feb 6, 2026
5cb986e
Merge pull request #8 from CheffieGithub/forest_tg
CheffieGithub Feb 6, 2026
4198d9f
shitty styling
CheffieGithub Feb 6, 2026
7452774
Starting verb cats
Feb 6, 2026
35e7b24
Merge pull request #9 from CheffieGithub/forest_tg
realforest2001 Feb 6, 2026
c87c8b6
idiot
Feb 6, 2026
4800eb3
Merge remote-tracking branch 'origin/wip/tgui_panel' into wip/tgui_panel
Feb 6, 2026
031403d
.
Feb 6, 2026
d8062d4
Admin Tab primary categories
Feb 6, 2026
62396f8
Deletes Fun and Special tabs
Feb 6, 2026
f27d0ad
clean up scss and add tgui input
CheffieGithub Feb 7, 2026
9c9636a
scale
Feb 7, 2026
e4ad234
dsa
CheffieGithub Feb 7, 2026
da7ee26
Not using prettier yet
CheffieGithub Feb 7, 2026
5bd5106
two CM changes
CheffieGithub Feb 8, 2026
e06cd8a
more categories
Feb 8, 2026
5115f01
Death to Noises tab
Feb 8, 2026
12230db
doh
Feb 8, 2026
9b6dc14
more doh
Feb 8, 2026
48cf1d1
I like seeing
CheffieGithub Feb 8, 2026
5b51fbe
fix build all
CheffieGithub Feb 8, 2026
5f8081c
fix ui topic procs
CheffieGithub Feb 8, 2026
1d6fa94
update states and re-add assets
CheffieGithub Feb 8, 2026
05ba25a
ddd
CheffieGithub Feb 8, 2026
bc76223
Im tired
CheffieGithub Feb 8, 2026
e7872cd
for j
CheffieGithub Feb 8, 2026
2321570
fix asset cache
CheffieGithub Feb 9, 2026
440df46
Merge branch 'wip/tgui_panel' of https://github.com/realforest2001/Va…
CheffieGithub Feb 9, 2026
d262b59
Merge pull request #10 from CheffieGithub/forest_tg
CheffieGithub Feb 9, 2026
0fb062f
Merge branch 'wip/tgui_panel' of https://github.com/realforest2001/Va…
CheffieGithub Feb 9, 2026
5707e6b
Merge branch 'main' of https://github.com/Vanderlin-Tales-Of-Wine/Van…
CheffieGithub Feb 12, 2026
da870a4
skew
CheffieGithub Feb 12, 2026
938c41f
fix
CheffieGithub Feb 12, 2026
2a63b1b
b
CheffieGithub Feb 12, 2026
972620b
Merge branch 'main' of https://github.com/Vanderlin-Tales-Of-Wine/Van…
CheffieGithub Feb 13, 2026
44fe946
style
CheffieGithub Feb 14, 2026
586f7f5
Merge branch 'main' of https://github.com/Vanderlin-Tales-Of-Wine/Van…
CheffieGithub Feb 14, 2026
96f2c76
css
CheffieGithub Feb 14, 2026
3160eba
bleh
CheffieGithub Feb 14, 2026
9b1d165
h
CheffieGithub Feb 14, 2026
dc71710
Merge branch 'main' of https://github.com/Vanderlin-Tales-Of-Wine/Van…
CheffieGithub Feb 17, 2026
d7b8798
arrg
CheffieGithub Feb 17, 2026
fbb2eb9
xd
CheffieGithub Feb 17, 2026
cbe7008
brasd
CheffieGithub Feb 17, 2026
8f70246
ff
CheffieGithub Feb 17, 2026
2eff56b
everything but skin
CheffieGithub Feb 17, 2026
348c3a3
fuck
CheffieGithub Feb 17, 2026
7a215e8
skin
CheffieGithub Feb 17, 2026
4fc770a
fix
CheffieGithub Feb 18, 2026
e55ffc2
ffs
CheffieGithub Feb 18, 2026
9ab34ce
das
CheffieGithub Feb 18, 2026
e6b1335
I love fonts
CheffieGithub Feb 19, 2026
2727fc7
chat
CheffieGithub Feb 20, 2026
ba81e39
Merge branch 'main' of https://github.com/Vanderlin-Tales-Of-Wine/Van…
CheffieGithub Feb 21, 2026
427cd3f
topbar css
CheffieGithub Feb 21, 2026
60b6b61
unread changelog style
CheffieGithub Feb 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 50 additions & 25 deletions code/__DEFINES/chat.dm
Original file line number Diff line number Diff line change
@@ -1,31 +1,56 @@
#define CHAT_MESSAGE_SPAWN_TIME 0.2 SECONDS
#define CHAT_MESSAGE_LIFESPAN 5 SECONDS
#define CHAT_MESSAGE_EOL_FADE 0.8 SECONDS
#define CHAT_SPELLING_DELAY 0.02 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 CHAT_GLORF_LIST list(\
"-ah!!",\
"-GLORF!!",\
"-OW!!"\
)
/*!
* Copyright (c) 2020 Aleksej Komarov
* SPDX-License-Identifier: MIT
*/

#define CHAT_SPELLING_PUNCTUATION list(\
"," = 0.25 SECONDS,\
"." = 0.4 SECONDS,\
" " = 0.03 SECONDS,\
"-" = 0.2 SECONDS,\
"!" = 0.2 SECONDS,\
"?" = 0.15 SECONDS,\
)
/// How many chat payloads to keep in history
#define CHAT_RELIABILITY_HISTORY_SIZE 5
/// How many resends to allow before giving up
#define CHAT_RELIABILITY_MAX_RESENDS 3

#define MESSAGE_TYPE_SYSTEM "system"
#define MESSAGE_TYPE_LOCALCHAT "localchat"
#define MESSAGE_TYPE_RADIO "radio"
#define MESSAGE_TYPE_ENTERTAINMENT "entertainment"
#define MESSAGE_TYPE_INFO "info"
#define MESSAGE_TYPE_WARNING "warning"
#define MESSAGE_TYPE_DEADCHAT "deadchat"
#define MESSAGE_TYPE_OOC "ooc"
#define MESSAGE_TYPE_ADMINPM "adminpm"
#define MESSAGE_TYPE_COMBAT "combat"
#define MESSAGE_TYPE_ADMINCHAT "adminchat"
#define MESSAGE_TYPE_PRAYER "prayer"
#define MESSAGE_TYPE_MODCHAT "modchat"
#define MESSAGE_TYPE_EVENTCHAT "eventchat"
#define MESSAGE_TYPE_ADMINLOG "adminlog"
#define MESSAGE_TYPE_ATTACKLOG "attacklog"
#define MESSAGE_TYPE_DEBUG "debug"

#define CHAT_SPELLING_EXCEPTIONS list(\
"'",\
)
//debug printing macros (for development and testing)
/// Used for debug messages to the world
#define debug_world(msg) if (GLOB.Debug2) to_chat(world, \
type = MESSAGE_TYPE_DEBUG, \
text = "DEBUG: [msg]")
/// Used for debug messages to the player
#define debug_usr(msg) if (GLOB.Debug2 && usr) to_chat(usr, \
type = MESSAGE_TYPE_DEBUG, \
text = "DEBUG: [msg]")
/// Used for debug messages to the admins
#define debug_admins(msg) if (GLOB.Debug2) to_chat(GLOB.admins, \
type = MESSAGE_TYPE_DEBUG, \
text = "DEBUG: [msg]")
/// Used for debug messages to the server
#define debug_world_log(msg) if (GLOB.Debug2) log_world("DEBUG: [msg]")

/// Adds a generic box around whatever message you're sending in chat. Really makes things stand out.
#define boxed_message(str) ("<div class='boxed_message'>" + str + "</div>")
/// Adds a box around whatever message you're sending in chat. Can apply color and/or additional classes. Available colors: red, green, blue, purple. Use it like red_box

#define custom_boxed_message(classes, str) ("<div class='boxed_message " + classes + "'>" + str + "</div>")
/// Makes a fieldset with a neaty styled name. Can apply additional classes.
#define fieldset_block(title, content, classes) ("<fieldset class='fieldset " + classes + "'><legend class='fieldset_legend'>" + title + "</legend>" + content + "</fieldset>")
/// Makes a horizontal line with text in the middle
#define separator_hr(str) ("<div class='separator'>" + str + "</div>")

/// Emboldens runechat messages
#define RUNECHAT_BOLD(str) "+[str]+"
29 changes: 29 additions & 0 deletions code/__DEFINES/chat_message.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#define CHAT_MESSAGE_SPAWN_TIME 0.2 SECONDS
#define CHAT_MESSAGE_LIFESPAN 5 SECONDS
#define CHAT_MESSAGE_EOL_FADE 0.8 SECONDS
#define CHAT_SPELLING_DELAY 0.02 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 CHAT_GLORF_LIST list(\
"-ah!!",\
"-GLORF!!",\
"-OW!!"\
)

#define CHAT_SPELLING_PUNCTUATION list(\
"," = 0.25 SECONDS,\
"." = 0.4 SECONDS,\
" " = 0.03 SECONDS,\
"-" = 0.2 SECONDS,\
"!" = 0.2 SECONDS,\
"?" = 0.15 SECONDS,\
)


#define CHAT_SPELLING_EXCEPTIONS list(\
"'",\
)
6 changes: 0 additions & 6 deletions code/__DEFINES/misc.dm
Original file line number Diff line number Diff line change
Expand Up @@ -286,12 +286,6 @@ GLOBAL_LIST_INIT(pda_styles, sortList(list(MONO, VT, ORBITRON, SHARE)))
#define SHELTER_DEPLOY_BAD_AREA "bad area"
#define SHELTER_DEPLOY_ANCHORED_OBJECTS "anchored objects"

//debug printing macros
#define debug_world(msg) if (GLOB.Debug2) to_chat(world, "DEBUG: [msg]")
#define debug_usr(msg) if (GLOB.Debug2&&usr) to_chat(usr, "DEBUG: [msg]")
#define debug_admins(msg) if (GLOB.Debug2) to_chat(GLOB.admins, "DEBUG: [msg]")
#define debug_world_log(msg) if (GLOB.Debug2) log_world("DEBUG: [msg]")

#define INCREMENT_TALLY(L, stat) if(L[stat]){L[stat]++}else{L[stat] = 1}

//TODO Move to a pref
Expand Down
20 changes: 0 additions & 20 deletions code/controllers/configuration/configuration.dm
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@
loadmaplist(CONFIG_MAPS_FILE)
LoadMOTD()
LoadPolicy()
LoadChatFilter()
LoadRelays()

if(Master)
Expand Down Expand Up @@ -307,25 +306,6 @@ Example config:
else
log_config("Unknown command in map vote config: '[command]'")

/datum/controller/configuration/proc/LoadChatFilter()
var/list/in_character_filter = list()

if(!fexists("[directory]/in_character_filter.txt"))
return

log_config("Loading config file in_character_filter.txt...")

for(var/line in file2list("[directory]/in_character_filter.txt"))
if(!line)
continue
if(findtextEx(line,"#",1,2))
continue
in_character_filter += REGEX_QUOTE(line)

ic_filter_regex = in_character_filter.len ? regex("\\b([jointext(in_character_filter, "|")])\\b", "i") : null

syncChatRegexes()

//Message admins when you can.
/datum/controller/configuration/proc/DelayedMessageAdmins(text)
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(message_admins), text), 0)
Expand Down
198 changes: 78 additions & 120 deletions code/controllers/subsystem/chat.dm
Original file line number Diff line number Diff line change
@@ -1,139 +1,97 @@
/*!
* Copyright (c) 2020 Aleksej Komarov
* SPDX-License-Identifier: MIT
*/

SUBSYSTEM_DEF(chat)
name = "Chat"
flags = SS_TICKER
flags = SS_TICKER|SS_NO_INIT
wait = 1
priority = FIRE_PRIORITY_CHAT
init_order = INIT_ORDER_CHAT

var/list/payload = list()

/datum/controller/subsystem/chat/fire()
for(var/client/C as anything in payload)
C << output(payload[C], "browseroutput:output")
payload -= C

if(MC_TICK_CHECK)
return

/datum/controller/subsystem/chat/proc/queue(target, message, handle_whitespace = TRUE)
if(!target || !message)
return

if(!istext(message))
stack_trace("to_chat called with invalid input type")
return
/// Assosciates a ckey with a list of messages to send to them.
var/list/list/datum/chat_payload/client_to_payloads = list()

if(target == world)
target = GLOB.clients
/// Associates a ckey with an assosciative list of their last CHAT_RELIABILITY_HISTORY_SIZE messages.
var/list/list/datum/chat_payload/client_to_reliability_history = list()

//Some macros remain in the string even after parsing and fuck up the eventual output
var/original_message = message
message = replacetext(message, "\improper", "")
message = replacetext(message, "\proper", "")
if(handle_whitespace)
message = replacetext(message, "\n", "<br>")
message = replacetext(message, "\t", "[FOURSPACES][FOURSPACES]")
message += "<br>"
/// Assosciates a ckey with their next sequence number.
var/list/client_to_sequence_number = list()

/datum/controller/subsystem/chat/proc/generate_payload(client/target, message_data)
var/sequence = client_to_sequence_number[target.ckey]
client_to_sequence_number[target.ckey] += 1

//url_encode it TWICE, this way any UTF-8 characters are able to be decoded by the Javascript.
//Do the double-encoding here to save nanoseconds
var/twiceEncoded = url_encode(url_encode(message))
var/datum/chat_payload/payload = new
payload.sequence = sequence
payload.content = message_data

if(islist(target))
for(var/I in target)
var/client/C = CLIENT_FROM_VAR(I) //Grab us a client if possible
if(!(target.ckey in client_to_reliability_history))
client_to_reliability_history[target.ckey] = list()
var/list/client_history = client_to_reliability_history[target.ckey]
client_history["[sequence]"] = payload

if(!C)
return
if(length(client_history) > CHAT_RELIABILITY_HISTORY_SIZE)
var/oldest = text2num(client_history[1])
for(var/index in 2 to length(client_history))
var/test = text2num(client_history[index])
if(test < oldest)
oldest = test
client_history -= "[oldest]"
return payload

//Send it to the old style output window.
SEND_TEXT(C, original_message)
/datum/controller/subsystem/chat/proc/send_payload_to_client(client/target, datum/chat_payload/payload)
target.tgui_panel.window.send_message("chat/message", payload.into_message())
SEND_TEXT(target, payload.get_content_as_html())

if(!C?.chatOutput || C.chatOutput.broken) //A player who hasn't updated his skin file.
continue

if(!C.chatOutput.loaded) //Client still loading, put their messages in a queue
C.chatOutput.messageQueue += message
continue

payload[C] += twiceEncoded

else
var/client/C = CLIENT_FROM_VAR(target) //Grab us a client if possible

if(!C)
return

//Send it to the old style output window.
SEND_TEXT(C, original_message)
/datum/controller/subsystem/chat/fire()
for(var/ckey in client_to_payloads)
var/client/target = GLOB.directory[ckey]
if(isnull(target)) // verify client still exists
LAZYREMOVE(client_to_payloads, ckey)
continue

if(!C?.chatOutput || C.chatOutput.broken) //A player who hasn't updated his skin file.
return
for(var/datum/chat_payload/payload as anything in client_to_payloads[ckey])
send_payload_to_client(target, payload)
LAZYREMOVE(client_to_payloads, ckey)

if(!C.chatOutput.loaded) //Client still loading, put their messages in a queue
C.chatOutput.messageQueue += message
if(MC_TICK_CHECK)
return

payload[C] += twiceEncoded


//Global chat procs
/proc/to_chat_immediate(target, message, handle_whitespace = TRUE)
if(!target || !message)
/datum/controller/subsystem/chat/proc/queue(queue_target, list/message_data)
var/list/targets = islist(queue_target) ? queue_target : list(queue_target)
for(var/target in targets)
var/client/client = CLIENT_FROM_VAR(target)
if(isnull(client))
continue
LAZYADDASSOCLIST(client_to_payloads, client.ckey, generate_payload(client, message_data))

/datum/controller/subsystem/chat/proc/send_immediate(send_target, list/message_data)
var/list/targets = islist(send_target) ? send_target : list(send_target)
for(var/target in targets)
var/client/client = CLIENT_FROM_VAR(target)
if(isnull(client))
continue
send_payload_to_client(client, generate_payload(client, message_data))

/datum/controller/subsystem/chat/proc/handle_resend(client/client, sequence)
var/list/client_history = client_to_reliability_history[client.ckey]
sequence = "[sequence]"
if(isnull(client_history) || !(sequence in client_history))
return

if(target == world)
target = GLOB.clients

var/original_message = message
if(handle_whitespace)
message = replacetext(message, "\n", "<br>")
message = replacetext(message, "\t", "[FOURSPACES][FOURSPACES]") //EIGHT SPACES IN TOTAL!!

if(islist(target))
// Do the double-encoding outside the loop to save nanoseconds
var/twiceEncoded = url_encode(url_encode(message))
for(var/I in target)
var/client/C = CLIENT_FROM_VAR(I) //Grab us a client if possible

if (!C)
continue

//Send it to the old style output window.
SEND_TEXT(C, original_message)

if(!C.chatOutput || C.chatOutput.broken) // A player who hasn't updated his skin file.
continue

if(!C.chatOutput.loaded)
//Client still loading, put their messages in a queue
C.chatOutput.messageQueue += message
continue

C << output(twiceEncoded, "browseroutput:output")
else
var/client/C = CLIENT_FROM_VAR(target) //Grab us a client if possible

if (!C)
return

//Send it to the old style output window.
SEND_TEXT(C, original_message)

if(!C.chatOutput || C.chatOutput.broken) // A player who hasn't updated his skin file.
return

if(!C.chatOutput.loaded)
//Client still loading, put their messages in a queue
C.chatOutput.messageQueue += message
return

// url_encode it TWICE, this way any UTF-8 characters are able to be decoded by the Javascript.
C << output(url_encode(url_encode(message)), "browseroutput:output")

/proc/to_chat(target, message, handle_whitespace = TRUE)
if(Master.current_runlevel == RUNLEVEL_INIT || !SSchat?.initialized)
to_chat_immediate(target, message, handle_whitespace)
return
SSchat.queue(target, message, handle_whitespace)
var/datum/chat_payload/payload = client_history[sequence]
if(payload.resends > CHAT_RELIABILITY_MAX_RESENDS)
return // we tried but byond said no

payload.resends += 1
send_payload_to_client(client, client_history[sequence])
SSblackbox.record_feedback(
"nested tally",
"chat_resend_byond_version",
1,
list(
"[client.byond_version]",
"[client.byond_build]",
),
)
1 change: 1 addition & 0 deletions code/controllers/subsystem/events.dm
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ SUBSYSTEM_DEF(events)
/client/proc/forceGamemode()
set name = "Open Gamemode Panel"
set category = "GameMaster.Fun"

if(!holder ||!check_rights(R_FUN))
return
holder.forceGamemode(usr)
Expand Down
Loading
Loading