Skip to content

Commit

Permalink
Add bindchat
Browse files Browse the repository at this point in the history
  • Loading branch information
SollyBunny authored Dec 22, 2024
1 parent 0ad19fb commit 462fba0
Show file tree
Hide file tree
Showing 12 changed files with 495 additions and 98 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2405,6 +2405,8 @@ if(CLIENT)
components/spectator.h
components/statboard.cpp
components/statboard.h
components/tclient/bindchat.cpp
components/tclient/bindchat.h
components/tclient/bindwheel.cpp
components/tclient/bindwheel.h
components/tclient/menus_tclient.cpp
Expand Down
11 changes: 9 additions & 2 deletions src/game/client/components/chat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,10 @@ bool CChat::OnInput(const IInput::CEvent &Event)
m_CommandsNeedSorting = false;
}

SendChatQueued(m_Input.GetString());
if (m_pClient->m_Bindchat.ChatDoBinds(m_Input.GetString()))
; // Do nothing as bindchat was executed
else
SendChatQueued(m_Input.GetString());
m_pHistoryEntry = nullptr;
DisableMode();
m_pClient->OnRelease();
Expand Down Expand Up @@ -312,7 +315,11 @@ bool CChat::OnInput(const IInput::CEvent &Event)
});
}

if(m_aCompletionBuffer[0] == '/' && !m_vCommands.empty())
if (m_pClient->m_Bindchat.ChatDoAutocomplete(ShiftPressed))
{

}
else if(m_aCompletionBuffer[0] == '/' && !m_vCommands.empty())
{
CCommand *pCompletionCommand = 0;

Expand Down
2 changes: 2 additions & 0 deletions src/game/client/components/chat.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ class CChat : public CComponent
bool LineShouldHighlight(const char *pLine, const char *pName);
void StoreSave(const char *pText);

friend class CBindchat;

public:
CChat();
int Sizeof() const override { return sizeof(*this); }
Expand Down
1 change: 1 addition & 0 deletions src/game/client/components/menus.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class CMenus : public CComponent
int DoButton_CheckBox_Number(const void *pId, const char *pText, int Checked, const CUIRect *pRect);

bool DoSliderWithScaledValue(const void *pId, int *pOption, const CUIRect *pRect, const char *pStr, int Min, int Max, int Scale, const IScrollbarScale *pScale, unsigned Flags = 0u, const char *pSuffix = "");
bool DoEditBoxWithLabel(CLineInput *LineInput, const CUIRect *pRect, const char *pLabel, const char *pDefault, char *pBuf, size_t BufSize);

ColorHSLA DoLine_ColorPicker(CButtonContainer *pResetId, float LineSize, float LabelSize, float BottomMargin, CUIRect *pMainRect, const char *pText, unsigned int *pColorValue, ColorRGBA DefaultColor, bool CheckBoxSpacing = true, int *pCheckBoxValue = nullptr, bool Alpha = false);
ColorHSLA DoButton_ColorPicker(const CUIRect *pRect, unsigned int *pHslaColor, bool Alpha);
Expand Down
256 changes: 256 additions & 0 deletions src/game/client/components/tclient/bindchat.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
#include <engine/shared/config.h>
#include <game/client/gameclient.h>

#include "../chat.h"
#include "../emoticon.h"

#include "bindchat.h"

CBindchat::CBindchat()
{
OnReset();
}

void CBindchat::ConAddBindchat(IConsole::IResult *pResult, void *pUserData)
{
const char *aName = pResult->GetString(0);
const char *aCommand = pResult->GetString(1);

CBindchat *pThis = static_cast<CBindchat *>(pUserData);
pThis->AddBind(aName, aCommand);
}

void CBindchat::ConBindchats(IConsole::IResult *pResult, void *pUserData)
{
CBindchat *pThis = static_cast<CBindchat *>(pUserData);
char aBuf[BINDCHAT_MAX_NAME + BINDCHAT_MAX_CMD + 32];
if(pResult->NumArguments() == 1)
{
const char *pName = pResult->GetString(0);
for(const CBind &Bind : pThis->m_vBinds)
{
if(str_comp_nocase(Bind.m_aName, pName) == 0)
{
str_format(aBuf, sizeof(aBuf), "%s = %s", Bind.m_aName, Bind.m_aCommand);
pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "bindchat", aBuf);
return;
}
}
str_format(aBuf, sizeof(aBuf), "%s is not bound", pName);
pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "bindchat", aBuf);
}
else
{
for(const CBind &Bind : pThis->m_vBinds)
{
str_format(aBuf, sizeof(aBuf), "%s = %s", Bind.m_aName, Bind.m_aCommand);
pThis->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "bindchat", aBuf);
}
}
}

void CBindchat::ConRemoveBindchat(IConsole::IResult *pResult, void *pUserData)
{
const char *aName = pResult->GetString(0);
CBindchat *pThis = static_cast<CBindchat *>(pUserData);
pThis->RemoveBind(aName);
}


void CBindchat::ConRemoveBindchatAll(IConsole::IResult *pResult, void *pUserData)
{
CBindchat *pThis = static_cast<CBindchat *>(pUserData);
pThis->RemoveAllBinds();
}

void CBindchat::ConBindchatDefaults(IConsole::IResult *pResult, void *pUserData)
{
CBindchat *pThis = static_cast<CBindchat *>(pUserData);
pThis->AddBind("!shrug", "say ¯\\_(ツ)_/¯");
pThis->AddBind("!flip", "say (╯°□°)╯︵ ┻━┻");
pThis->AddBind("!unflip", "say ┬─┬ノ( º _ ºノ)");
}

void CBindchat::AddBind(const char *pName, const char *pCommand)
{
if((pName[0] == '\0' && pCommand[0] == '\0') || m_vBinds.size() >= BINDCHAT_MAX_BINDS)
return;

RemoveBind(pName); // Prevent duplicates

CBind Bind;
str_copy(Bind.m_aName, pName);
str_copy(Bind.m_aCommand, pCommand);
m_vBinds.push_back(Bind);
}

void CBindchat::RemoveBind(const char *pName)
{
if(pName[0] == '\0')
return;
for (auto It = m_vBinds.begin(); It != m_vBinds.end(); ++It)
{
if(str_comp(It->m_aName, pName) == 0)
{
m_vBinds.erase(It);
return;
}
}
}

void CBindchat::RemoveBind(int Index)
{
if(Index >= static_cast<int>(m_vBinds.size()) || Index < 0)
return;
auto It = m_vBinds.begin() + Index;
m_vBinds.erase(It);
}

void CBindchat::RemoveAllBinds()
{
m_vBinds.clear();
}

int CBindchat::GetBind(const char *pCommand)
{
if(pCommand[0] == '\0')
return -1;
for(auto It = m_vBinds.begin(); It != m_vBinds.end(); ++It)
{
if(str_comp_nocase(It->m_aCommand, pCommand) == 0)
return &*It - m_vBinds.data();
}
return -1;
}

CBindchat::CBind *CBindchat::Get(int Index)
{
if(Index < 0 || Index >= (int)m_vBinds.size())
return nullptr;
return &m_vBinds[Index];
}

void CBindchat::OnConsoleInit()
{
IConfigManager *pConfigManager = Kernel()->RequestInterface<IConfigManager>();
if(pConfigManager)
pConfigManager->RegisterTCallback(ConfigSaveCallback, this);

Console()->Register("bindchat", "s[name] r[command]", CFGFLAG_CLIENT, ConAddBindchat, this, "Add a chat bind");
Console()->Register("bindchats", "?s[name]", CFGFLAG_CLIENT, ConBindchats, this, "Print command executed by this name or all chat binds");
Console()->Register("unbindchat", "s[name] r[command]", CFGFLAG_CLIENT, ConRemoveBindchat, this, "Remove a chat bind");
Console()->Register("unbindchatall", "", CFGFLAG_CLIENT, ConRemoveBindchatAll, this, "Removes all chat binds");
Console()->Register("bindchatdefaults", "", CFGFLAG_CLIENT, ConBindchatDefaults, this, "Adds default chat binds");
}

void CBindchat::ExecuteBind(int Bind, const char *pArgs)
{
char aBuf[BINDCHAT_MAX_CMD] = "";
str_append(aBuf, m_vBinds[Bind].m_aCommand);
if(pArgs)
{
str_append(aBuf, " ");
str_append(aBuf, pArgs);
}
Console()->ExecuteLine(aBuf);
}

bool CBindchat::ChatDoBinds(const char *pText)
{
CChat &Chat = GameClient()->m_Chat;
const char *pSpace = str_find(pText, " ");
size_t SpaceIndex = pSpace ? pSpace - pText : strlen(pText);
for(const CBind &Bind : m_vBinds)
{
if(str_comp_nocase_num(pText, Bind.m_aName, SpaceIndex) == 0)
{
ExecuteBind(&Bind - m_vBinds.data(), pSpace ? pSpace + 1 : nullptr);
// Add to history (see CChat::SendChatQueued)
const int Length = str_length(pText);
CChat::CHistoryEntry *pEntry = Chat.m_History.Allocate(sizeof(CChat::CHistoryEntry) + Length);
pEntry->m_Team = 0; // All
str_copy(pEntry->m_aText, pText, Length + 1);
return true;
}
}
return false;
}

bool CBindchat::ChatDoAutocomplete(bool ShiftPressed) {
CChat &Chat = GameClient()->m_Chat;

if(m_vBinds.size() == 0)
return false;
if(*Chat.m_aCompletionBuffer == '\0')
return false;

const CBind *pCompletionBind = nullptr;

if(ShiftPressed && Chat.m_CompletionUsed)
Chat.m_CompletionChosen--;
else if(!ShiftPressed)
Chat.m_CompletionChosen++;
Chat.m_CompletionChosen = (Chat.m_CompletionChosen + m_vBinds.size()) % m_vBinds.size(); // size != 0

Chat.m_CompletionUsed = true;
for(const CBind &Bind : m_vBinds)
{
if(str_startswith_nocase(Bind.m_aName, Chat.m_aCompletionBuffer))
{
pCompletionBind = &Bind;
Chat.m_CompletionChosen = &Bind - m_vBinds.data();
break;
}
}

// insert the command
if(pCompletionBind)
{
char aBuf[CChat::MAX_LINE_LENGTH];
// add part before the name
str_truncate(aBuf, sizeof(aBuf), Chat.m_Input.GetString(), Chat.m_PlaceholderOffset);

// add the command
str_append(aBuf, pCompletionBind->m_aName);

// add separator
// TODO: figure out if the command would accept an extra param
// char commandBuf[128];
// str_next_token(pCompletionBind->m_aCommand, " ", commandBuf, sizeof(commandBuf));
// CCommandInfo *pInfo = m_pClient->Console()->GetCommandInfo(commandBuf, CFGFLAG_CLIENT, false);
// if(pInfo && pInfo->m_pParams != '\0')
const char pSeperator[] = " ";
str_append(aBuf, pSeperator);

// add part after the name
str_append(aBuf, Chat.m_Input.GetString() + Chat.m_PlaceholderOffset + Chat.m_PlaceholderLength);

Chat.m_PlaceholderLength = sizeof(pSeperator) + str_length(pCompletionBind->m_aName) + 1;
Chat.m_Input.Set(aBuf);
Chat.m_Input.SetCursorOffset(Chat.m_PlaceholderOffset + Chat.m_PlaceholderLength);
}

return pCompletionBind != nullptr;
}

void CBindchat::ConfigSaveCallback(IConfigManager *pConfigManager, void *pUserData)
{
CBindchat *pThis = (CBindchat *)pUserData;

for(CBind &Bind : pThis->m_vBinds)
{
char aBuf[BINDCHAT_MAX_CMD * 2] = "";
char *pEnd = aBuf + sizeof(aBuf);
char *pDst;
str_append(aBuf, "bindchat \"");
// Escape name
pDst = aBuf + str_length(aBuf);
str_escape(&pDst, Bind.m_aName, pEnd);
str_append(aBuf, "\" \"");
// Escape command
pDst = aBuf + str_length(aBuf);
str_escape(&pDst, Bind.m_aCommand, pEnd);
str_append(aBuf, "\"");
pConfigManager->WriteLine(aBuf);
}
}
58 changes: 58 additions & 0 deletions src/game/client/components/tclient/bindchat.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@

#ifndef GAME_CLIENT_COMPONENTS_BINDCHAT_H
#define GAME_CLIENT_COMPONENTS_BINDCHAT_H
#include <game/client/component.h>
class IConfigManager;

enum
{
BINDCHAT_MAX_NAME = 64,
BINDCHAT_MAX_CMD = 1024,
BINDCHAT_MAX_BINDS = 256,
};

class CBindchat : public CComponent
{
static void ConAddBindchat(IConsole::IResult *pResult, void *pUserData);
static void ConBindchats(IConsole::IResult *pResult, void *pUserData);
static void ConRemoveBindchat(IConsole::IResult *pResult, void *pUserData);
static void ConRemoveBindchatAll(IConsole::IResult *pResult, void *pUserData);
static void ConBindchatDefaults(IConsole::IResult *pResult, void *pUserData);

static void ConfigSaveCallback(IConfigManager *pConfigManager, void *pUserData);

void ExecuteBind(int Bind, const char *pArgs);

public:
class CBind
{
public:
char m_aName[BINDCHAT_MAX_NAME];
char m_aCommand[BINDCHAT_MAX_CMD];

bool operator==(const CBind &Other) const
{
return str_comp(m_aName, Other.m_aName) == 0 && str_comp(m_aCommand, Other.m_aCommand) == 0;
}
};

std::vector<CBind> m_vBinds;

CBindchat();
virtual int Sizeof() const override { return sizeof(*this); }

virtual void OnConsoleInit() override;

void AddBind(const char *pName, const char *pCommand);
void RemoveBind(const char *pName);
void RemoveBind(int Index);
void RemoveAllBinds();

int GetBind(const char *pCommand);
CBind *Get(int Index);

bool ChatDoBinds(const char *pText);
bool ChatDoAutocomplete(bool ShiftPressed);
};

#endif
Loading

0 comments on commit 462fba0

Please sign in to comment.