Advanced Chatbox

From Garry's Mod
Jump to: navigation, search

Contents

Benefits over basic chatbox?

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

  • Disable "messagemode" in GM:PlayerBindPress, 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 chat.AddText 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

Personal tools
Navigation