Garry's Mod Wiki

Revision Difference

Advanced_Chatbox#517622

<cat>Dev.Lua</cat> # Benefits over [basic chatbox](/gmod/Basic_Chatbox)? # Benefits over <page text="basic chatbox">Basic_Chatbox</page>? Making a chat box using this method is slightly harder and requires a little more attention to detail but it gives you full control over the chatbox as opposed to just simply reskinning it. You can add buttons, image panels, and more by basically coding the chatbox from the bottom up. # Getting started ## Setting up Same set up as the basic chatbox... DFrame, DTextEntry, RichText and a table. ``` myChat = {} myChat.dFrame = ... myChat.dTextEntry = ... myChat.dRichText = ... ``` Here is a basic run down of how this type of chat box works by Facepunch user [PortalGod](http://facepunch.com/member.php?u=235325) * Disable "messagemode" in <page>GM:PlayerBindPress</page>, open vgui chatbox and focus its text entry instead. * In textentry:OnEnter (or OnKeyCodePressed and check for enter), RunConsoleCommand("say", textentry:GetText()) and hide the chatbox. * Hijack <page>chat.AddText</page> and parse the arguments yourself (see RichText), call the original chat.AddText inside it though for colored console text ## Adding hooks The first thing you need to do is override function called when the player's chat binds are pressed, you do this in a PlayerBindPress hook. This will prevent the default chat box from appearing when the player presses their chat keys. In this function is where you would open up your chat box and I would highly recommend you add a boolean argument to it for team chat. It would be bad if your players tried to talk to their team and it went global ``` hook.Add("PlayerBindPress", "overrideChatbind", function( ply, bind, pressed ) local bTeam = false if bind == "messagemode" then print("global chat") elseif bind == "messagemode2" then print("team chat") bTeam = true else return end myChat.openChatbox( bTeam ) return true -- Doesn't allow any functions to be called for this bind end) ``` Now you want to display messages for stuff like sv_cheats being changed or a player leaving the game, that kind of stuff is important. ``` hook.Add("ChatText", "serverNotifications", function( index, name, text, type ) if type == "joinleave" or type == "none" then myChat.dRichText:AppendText( text.."\n" ) end end) ``` Lastly you should also hide the default chat messages since you will add your own to your chat box ``` hook.Add("HUDShouldDraw", "noMoreDefault", function( name ) if name == "CHudChat" then return false end end) ``` ## chat.AddText If you do not override chat.AddText, all the calls to that function will appear in the default chatbox which you have hidden. We want the player to see this so we will override the original function with one of our own. The source code for this function is not available, it likely isn't in Lua, so we will save the old function and just call that. You might notice that the arguments are ellipses (...), this is a deprecated Lua functionality that gLua has called varargs that basically means you can pass pretty much any number of arguments to that function. ``` local oldAddText = chat.AddText function chat.AddText(...) local args = {...} -- Create a table of varargs for _, obj in pairs( args ) do if type(obj) == "table" then -- We were passed a color object myChat.dRichText:InsertColorChange( obj.r, obj.g, obj.b, 255 ) elseif type(obj) == "string" then -- This is just a string myChat.dRichText:AppendText( obj ) elseif obj:IsPlayer() then local col = GAMEMODE:GetTeamColor( obj ) -- Get the player's team color myChat.dRichText:InsertColorChange( col.r, col.g, col.b, 255 ) -- Make their name that color myChat.dRichText:AppendText( obj:Nick() ) end end -- Gotta end our line for this message myChat.dRichText:AppendText( "\n" ) -- Call the original function oldAddText (...) end ``` ## Restoring functionality Now that we have overridden the default chat box with one of our own, the hooks that are called by the default chat box won't be called anymore. We need to fix this. In your chat box's text entry, you need to tell the player to say the text so the proper gamemode hooks are called for that. It would also be a good idea to make sure that the Escape key closes your chat box so we account for that too ``` myChat.dTextEntry.OnKeyCodeTyped = function( self, code ) if code == KEY_ESCAPE then -- Work around to hide the chatbox when the client presses escape myChat.closeChatbox() gui.HideGameUI() elseif code == KEY_ENTER then -- Replicate the client pressing enter if string.Trim( self:GetText() ) != "" then LocalPlayer():ConCommand("say "..self:GetText()) end myChat.closeChatbox() end end ``` When opening the chat box you are going to want to call the function for that even and then make sure your DTextEntry is available to be typed in ``` function myChat.openChatbox() -- Stuff -- MakePopup calls the input functions so we don't need to call those myChat.dFrame:MakePopup() myChat.dTextEntry:RequestFocus() gamemode.Call("StartChat") -- More stuff end ``` When the player closes your chat box you need to make sure they can move around again and that once again the proper gamemode function are called. ``` function myChat.closeChatbox() -- Stuff -- Give the player control again myChat.dFrame:SetMouseInputEnabled( false ) myChat.dFrame:SetKeyboardInputEnabled( false ) gui.EnableScreenClicker( false ) -- We are done chatting gamemode.Call("FinishChat") -- Clear the text entry myChat.dTextEntry:SetText( "" ) gamemode.Call( "ChatTextChanged", "" ) -- More stuff end ``` # Finishing ## Summary Although this is quick and covers very little code wise, it should hopefully be a useful tool for anyone looking to code their own chatbox and add their own spin to it. It contains the basics of what is used to create a chatbox of this scale without any of the visual code, a backend of sorts. This doesn't do anything fancy by default like having embeddable images or any of that, but it is possible using this as a base ## Examples Exho's Chatbox - [https://github.com/Exho1/eChat](https://github.com/Exho1/eChat)