diff --git a/lua/cbox/cl_chatbox.lua b/lua/cbox/cl_chatbox.lua index 1324e77..3ab8bcb 100644 --- a/lua/cbox/cl_chatbox.lua +++ b/lua/cbox/cl_chatbox.lua @@ -35,11 +35,14 @@ local surface_SetMaterial = surface.SetMaterial cbox.chatbox = cbox.chatbox or {} cbox.chatbox.tabs = cbox.chatbox.tabs or {} cbox.chatbox.panels = cbox.chatbox.panels or {} +cbox.chatbox.modes = cbox.chatbox.modes or {} local CHATBOX_COLOR = CreateClientConVar("cbox_chatbox_color", "160 160 160", true, false, "Chatbox background color") local CHATBOX_ALPHA = CreateClientConVar("cbox_chatbox_alpha", "128", true, false, "Chatbox background alpha") local CHATBOX_BLUR = CreateClientConVar("cbox_chatbox_blur", "0", true, false, "Chatbox background is blurred") local CHATBOX_FADE = CreateClientConVar("cbox_chatbox_fade", "1", true, false, "Chatbox fades in and out") +local CHATBOX_MODE1 = CreateClientConVar("cbox_chatbox_mode1", "say", true, false, "Default mode when pressing messagemode") +local CHATBOX_MODE2 = CreateClientConVar("cbox_chatbox_mode2", "say_team", true, false, "Default mode when pressing messagemode2") ---@param height number ---@return number @@ -69,6 +72,17 @@ function cbox.chatbox.AddTab(key, name, icon, callback) } end +---Add a new chatbox send mode +---@param key string Internal unique name +---@param name string Display name +---@param callback fun(text) What to do when enter is pressed on this mode +function cbox.chatbox.AddMode(key, name, callback) + cbox.chatbox.modes[key] = { + name = name, + callback = callback, + } +end + local MATERIAL_BLUR = Material("pp/blurscreen") local function add_rounded_poly(poly, x, y, rad, seg, offset) @@ -85,9 +99,11 @@ local function add_rounded_poly(poly, x, y, rad, seg, offset) end end -local function RoundedBoxPoly(x, y, w, h, rad, seg) +local function RoundedBoxPoly(w, h, rad, seg) local poly = {} + local x, y = 0, 0 + add_rounded_poly(poly, x + rad, y + rad, rad, seg, seg) poly[#poly + 1] = { @@ -117,9 +133,11 @@ local function RoundedBoxPoly(x, y, w, h, rad, seg) add_rounded_poly(poly, x + rad, y + (h - rad), rad, seg) - surface_DrawPoly(poly) + return poly end +local PREV_W, PREV_H, BACKGROUND_POLY + local function CreateChatbox() if IsValid(cbox.chatbox.panels.frame) then cbox.chatbox.panels.frame:Remove() @@ -136,11 +154,17 @@ local function CreateChatbox() -- TODO: make this configurable frame:SetMinWidth(dw) - frame:SetMinWidth(dh) + frame:SetMinHeight(dh) - -- TODO: save resizing/moving - frame:SetPos(dx, dy) - frame:SetSize(dw, dh) + local x = frame:GetCookie("pos_x", dx) + local y = frame:GetCookie("pos_y", dy) + local w = frame:GetCookie("width", dw) + local h = frame:GetCookie("height", dh) + frame:SetPos(x, y) + frame:SetSize(w, h) + PREV_W = w + PREV_H = h + BACKGROUND_POLY = RoundedBoxPoly(w, h, 8, 24) frame:SetVisible(false) @@ -180,7 +204,12 @@ local function CreateChatbox() self.animFade:Run() - -- TODO: save position and size + local w, h = self:GetSize() + if w ~= PREV_W or h ~= PREV_H then + PREV_W = w + PREV_H = h + BACKGROUND_POLY = RoundedBoxPoly(w, h, 8, 24) + end end function frame:Paint(w, h) @@ -203,7 +232,7 @@ local function CreateChatbox() draw_NoTexture() surface_SetDrawColor(255, 255, 255) - RoundedBoxPoly(0, 0, w, h, 8, 24) + surface_DrawPoly(BACKGROUND_POLY) -- Only draw things that are in the stencil buffer render_SetStencilCompareFunction( STENCIL_EQUAL ) @@ -231,6 +260,15 @@ local function CreateChatbox() render_SetStencilEnable(false) end + function frame:OnClose() + local x, y = frame:GetPos() + local w, h = frame:GetSize() + frame:SetCookie("pos_x", x) + frame:SetCookie("pos_y", y) + frame:SetCookie("width", w) + frame:SetCookie("height", h) + end + cbox.chatbox.panels.frame = frame local tabs = vgui.Create("DPropertySheet", frame, "cbox.chatbox.tabs") @@ -291,6 +329,8 @@ local function CreateChatbox() end local function Init() + include("cbox/cl_modes.lua") + local tab_files = file.Find("cbox/tabs/*", "LUA") for _, name in ipairs(tab_files) do cbox.utils.RealmPrint("Loading chatbox tab:", name) @@ -309,6 +349,8 @@ local function Init() return true end) + + hook.Run("cbox.chatbox.Initialize") end ---Opens the chatbox @@ -350,6 +392,14 @@ function cbox.chatbox.Open(alt) cbox.chatbox.panels.input:RequestFocus() + if IsValid(cbox.chatbox.panels.mode_switch) then + if alt then + cbox.chatbox.panels.mode_switch:UpdateMode(CHATBOX_MODE2:GetString()) + else + cbox.chatbox.panels.mode_switch:UpdateMode(CHATBOX_MODE1:GetString()) + end + end + hook.Run("StartChat") end diff --git a/lua/cbox/cl_hooks.lua b/lua/cbox/cl_hooks.lua index b83fbd1..2b936b3 100644 --- a/lua/cbox/cl_hooks.lua +++ b/lua/cbox/cl_hooks.lua @@ -1,5 +1,9 @@ local CHATPRINT_COLOR = CreateClientConVar("cbox_chatprint_color", "151 211 255", true, false, "Color for system messages") local JOINLEAVE_COLOR = CreateClientConVar("cbox_joinleave_color", "161 255 161", true, false, "Color for join/leave messages") +local TEAM_COLOR = CreateClientConVar("cbox_team_color", "24 161 35", true, false, "Color for the team tag") +local DEAD_COLOR = CreateClientConVar("cbox_dead_color", "255 24 35", true, false, "Color for the dead tag") + +local color_white = Color(255, 255, 255) local hookTable = {} if cbox.hooks then @@ -16,6 +20,7 @@ end ---@alias ValidHooks ---| '"PreChatAddText"' # Runs before the true chat.AddText is called, allows modification to arguments +---| '"PrePlayerChat"' # Runs before player chat calls chat.AddText ---Adds a hook ---@param name ValidHooks @@ -34,7 +39,6 @@ function cbox.hooks.Remove(name, identifier) hookTable[name][identifier] = nil end - cbox.detours = cbox.detours or {} cbox.detours.chat_AddText = cbox.detours.chat_AddText or chat.AddText @@ -77,3 +81,67 @@ hook.Add("ChatText", "cbox.chattext", function(index, name, text, type) return true end) + +local function OverrideOnPlayerChat() + cbox.detours.OnPlayerChat = cbox.detours.OnPlayerChat or GAMEMODE.OnPlayerChat + + function GAMEMODE:OnPlayerChat(ply, text, is_team, is_dead, ...) + local oldChatAddText = chat.AddText + local args = {} + chat.AddText = function(...) + args = {...} + end + cbox.detours.OnPlayerChat(self, ply, text, is_team, is_dead, ...) + chat.AddText = oldChatAddText + + local new_args = {} + + for i, arg in ipairs(args) do + new_args[i] = arg + + if isstring(arg) then + if arg == "*DEAD* " then + new_args[i - 1] = cbox.utils.ParseColorStringToColor(DEAD_COLOR:GetString()) + elseif arg == "(TEAM) " then + new_args[i - 1] = cbox.utils.ParseColorStringToColor(TEAM_COLOR:GetString()) + elseif arg == "Console" then + table.insert(new_args, i, Color(160, 160, 160)) + elseif arg:StartsWith(": ") and #arg > 2 then + local text = arg:sub(3) + new_args[i] = ": " + table.insert(new_args, i + 1, text) + end + end + end + + local hooks = hookTable.PrePlayerChat or {} + for id, callback in next, hooks do + local function catch(err) + cbox.utils.RealmError(("Failed to run callback for PrePlayerChat hook %q:"):format(id), err) + end + + local ok, ret = xpcall(callback, catch, new_args) + if not ok then continue end + + if ret ~= nil then + if not istable(ret) then + cbox.utils.RealmError(("Got return for PrePlayerChat hook %q, but it was not a table."):format(id)) + + continue + end + new_args = ret + end + end + + chat.AddText(unpack(new_args)) + + return true + end +end + +hook.Add("Initialize", "cbox.onplayerchat", function() + OverrideOnPlayerChat() +end) +if GAMEMODE then + OverrideOnPlayerChat() +end diff --git a/lua/cbox/cl_modes.lua b/lua/cbox/cl_modes.lua new file mode 100644 index 0000000..fbc417b --- /dev/null +++ b/lua/cbox/cl_modes.lua @@ -0,0 +1,15 @@ +cbox.chatbox.AddMode("say", "Say", function(text) + RunConsoleCommand("say", text) +end) + +cbox.chatbox.AddMode("say_team", "Team", function(text) + RunConsoleCommand("say_team", text) +end) + +cbox.chatbox.AddMode("cmd", "Console", function(text) + local function catch(err) + LocalPlayer():ChatPrint("Failed to run command: " .. err) + end + + xpcall(function() LocalPlayer():ConCommand(text) end, catch) +end) diff --git a/lua/cbox/modules/cl_greentext.lua b/lua/cbox/modules/cl_greentext.lua index 502189b..a87af73 100644 --- a/lua/cbox/modules/cl_greentext.lua +++ b/lua/cbox/modules/cl_greentext.lua @@ -7,11 +7,9 @@ cbox.hooks.Add("PreChatAddText", "cbox.greentext", function(args) if not ENABLED:GetBool() then return end for i, arg in ipairs(args) do - if isstring(arg) and arg:StartsWith(": >") then - args[i] = arg:sub(3) + local prevArg = args[i - 1] + if isstring(prevArg) and prevArg == ": " and isstring(arg) and arg:StartsWith(">") then table.insert(args, i, cbox.utils.ParseColorStringToColor(COLOR:GetString())) - table.insert(args, i, ": ") - table.insert(args, i, color_white) break end end diff --git a/lua/cbox/modules/cl_timestamps.lua b/lua/cbox/modules/cl_timestamps.lua index 9c28d1f..6dbc76c 100644 --- a/lua/cbox/modules/cl_timestamps.lua +++ b/lua/cbox/modules/cl_timestamps.lua @@ -8,10 +8,7 @@ local ALL_MESSAGES = CreateClientConVar("cbox_timestamps_all", "1", true, false, local color_white = Color(255, 255, 255) -cbox.hooks.Add("PreChatAddText", "cbox.timestamps", function(args) - if not ENABLED:GetBool() then return end - if not ALL_MESSAGES:GetBool() then return end - +local function MakeTimestamp(args) local use24 = TRUETIME:GetBool() local stamp = "" @@ -53,4 +50,18 @@ cbox.hooks.Add("PreChatAddText", "cbox.timestamps", function(args) end return new_args +end + +cbox.hooks.Add("PreChatAddText", "cbox.timestamps", function(args) + if not ENABLED:GetBool() then return end + if not ALL_MESSAGES:GetBool() then return end + + return MakeTimestamp(args) +end) + +cbox.hooks.Add("PrePlayerChat", "cbox.timestamps", function(args) + if not ENABLED:GetBool() then return end + if ALL_MESSAGES:GetBool() then return end + + return MakeTimestamp(args) end) diff --git a/lua/cbox/sh_init.lua b/lua/cbox/sh_init.lua index e4bbd48..9495d9e 100644 --- a/lua/cbox/sh_init.lua +++ b/lua/cbox/sh_init.lua @@ -7,6 +7,7 @@ include("cbox/sh_utils.lua") if SERVER then AddCSLuaFile("cbox/cl_hooks.lua") AddCSLuaFile("cbox/cl_chatbox.lua") + AddCSLuaFile("cbox/cl_modes.lua") local tab_files = file.Find("cbox/tabs/*", "LUA") for _, name in ipairs(tab_files) do diff --git a/lua/cbox/tabs/chat.lua b/lua/cbox/tabs/chat.lua index 6ca1b36..a89b5b4 100644 --- a/lua/cbox/tabs/chat.lua +++ b/lua/cbox/tabs/chat.lua @@ -45,11 +45,20 @@ cbox.chatbox.AddTab("\1chat", "Chat", "icon16/comments.png", function() local mode_switch = vgui.Create("DButton", input_wrapper) mode_switch:SetTextColor(INPUT_TEXT_COLOR) - mode_switch:SetText("Say") - mode_switch:SizeToContents() mode_switch:Dock(LEFT) cbox.chatbox.panels.mode_switch = mode_switch + function mode_switch:UpdateMode(mode) + local mode_data = cbox.chatbox.modes[mode] + if not mode_data then + self.current_mode = "say" + mode_data = cbox.chatbox.modes.say + end + + self.current_mode = mode + self:SetText(mode_data.name) + end + function mode_switch:Paint(w, h) surface_SetDrawColor(0, 0, 0, 128) surface_DrawRect(0, 0, w, h) @@ -66,13 +75,31 @@ cbox.chatbox.AddTab("\1chat", "Chat", "icon16/comments.png", function() DLabel.ApplySchemeSettings(self) end mode_switch:SetFont("ChatFont") + mode_switch:UpdateMode("say") + function mode_switch:NextMode() + local keys = table.GetKeys(cbox.chatbox.modes) + table.sort(keys) + + local _, next_mode = next(keys, table.KeyFromValue(keys, self.current_mode)) + if not next_mode then next_mode = keys[1] end + + self:UpdateMode(next_mode) + end function mode_switch:DoClick() - -- TODO + self:NextMode() end function mode_switch:DoRightClick() - -- TODO + local menu = DermaMenu() + + for mode, data in SortedPairs(cbox.chatbox.modes) do + menu:AddOption(data.name, function() + self:UpdateMode(mode) + end) + end + + menu:Open() end function input:OnKeyCodeTyped(key) @@ -84,16 +111,17 @@ cbox.chatbox.AddTab("\1chat", "Chat", "icon16/comments.png", function() elseif key == KEY_ENTER then local text = self:GetText():Trim() if text ~= "" then - RunConsoleCommand("say", text) + cbox.chatbox.modes[mode_switch.current_mode].callback(text) end cbox.chatbox.Close() elseif key == KEY_TAB then if #self:GetText() == 0 then - -- TODO: mode switch + mode_switch:NextMode() else -- TODO: autocomplete end + timer.Simple(0, function() self:RequestFocus() end) else hook.Run("ChatTextChanged", self:GetText()) end @@ -115,8 +143,7 @@ hook.Add("OnChatAddText", "cbox.chatbox.history", function(args) history:InsertColorChange(col.r, col.g, col.b, 255) history:AppendText(arg:Name()) else - local col = cbox.utils.colors.BRAND - history:InsertColorChange(col.r, col.g, col.b, 255) + history:InsertColorChange(160, 160, 160, 255) history:AppendText("???") end end