Merge pull request #2042 from Dune-jr/feature-chat-commands

Chat commands
This commit is contained in:
oy 2019-03-24 11:32:09 +01:00 committed by GitHub
commit cf8212a663
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 560 additions and 59 deletions

View file

@ -1913,6 +1913,38 @@ void str_clean_whitespaces(char *str_in)
}
}
/* removes leading and trailing spaces */
void str_clean_whitespaces_simple(char *str_in)
{
char *read = str_in;
char *write = str_in;
/* skip initial whitespace */
while(*read == ' ')
read++;
/* end of read string is detected in the loop */
while(1)
{
/* skip whitespace */
int found_whitespace = 0;
for(; *read == ' ' && !found_whitespace; read++)
found_whitespace = 1;
/* if not at the end of the string, put a found whitespace here */
if(*read)
{
if(found_whitespace)
*write++ = ' ';
*write++ = *read++;
}
else
{
*write = 0;
break;
}
}
}
char *str_skip_to_whitespace(char *str)
{
while(*str && (*str != ' ' && *str != '\t' && *str != '\n'))
@ -1920,6 +1952,13 @@ char *str_skip_to_whitespace(char *str)
return str;
}
const char *str_skip_to_whitespace_const(const char *str)
{
while(*str && (*str != ' ' && *str != '\t' && *str != '\n'))
str++;
return str;
}
char *str_skip_whitespaces(char *str)
{
while(*str && (*str == ' ' || *str == '\t' || *str == '\n' || *str == '\r'))

View file

@ -912,6 +912,18 @@ int str_check_pathname(const char* str);
*/
void str_clean_whitespaces(char *str);
/*
Function: str_clean_whitespaces_simple
Removes leading and trailing spaces
Parameters:
str - String to clean up
Remarks:
- The strings are treated as zero-terminated strings.
*/
void str_clean_whitespaces_simple(char *str);
/*
Function: str_skip_to_whitespace
Skips leading non-whitespace characters(all but ' ', '\t', '\n', '\r').
@ -928,6 +940,12 @@ void str_clean_whitespaces(char *str);
*/
char *str_skip_to_whitespace(char *str);
/*
Function: str_skip_to_whitespace_const
See str_skip_to_whitespace.
*/
const char *str_skip_to_whitespace_const(const char *str);
/*
Function: str_skip_whitespaces
Skips leading whitespace characters(' ', '\t', '\n', '\r').

View file

@ -22,9 +22,28 @@
CChat::CChat()
{
// init chat commands (must be in alphabetical order)
static CChatCommand s_aapCommands[] = {
{"/all", " - Switch to all chat", &Com_All},
{"/friend", " <player name>", &Com_Befriend},
{"/m", " <player name>", &Com_Mute},
{"/mute", " <player name>", &Com_Mute},
{"/r", " - Reply to a whisper", &Com_Reply},
{"/team", " - Switch to team chat", &Com_Team},
{"/w", " <player name>", &Com_Whisper},
{"/whisper", " <player name>", &Com_Whisper},
};
const int CommandsCount = sizeof(s_aapCommands) / sizeof(CChatCommand);
m_pCommands = new CChatCommands(s_aapCommands, CommandsCount);
OnReset();
}
CChat::~CChat()
{
delete m_pCommands;
}
void CChat::OnReset()
{
for(int i = 0; i < MAX_LINES; i++)
@ -35,6 +54,8 @@ void CChat::OnReset()
}
m_Mode = CHAT_NONE;
// m_WhisperTarget = -1;
m_LastWhisperFrom = -1;
m_ReverseCompletion = false;
m_Show = false;
m_InputUpdate = false;
@ -47,6 +68,8 @@ void CChat::OnReset()
m_pHistoryEntry = 0x0;
m_PendingChatCounter = 0;
m_LastChatSend = 0;
m_IgnoreCommand = false;
m_pCommands->Reset();
for(int i = 0; i < CHAT_NUM; ++i)
m_aLastSoundPlayed[i] = 0;
@ -159,11 +182,24 @@ bool CChat::OnInput(IInput::CEvent Event)
return false;
if(Event.m_Flags&IInput::FLAG_PRESS && Event.m_Key == KEY_ESCAPE)
{
if(IsTypingCommand())
{
m_IgnoreCommand = true;
}
else
{
m_Mode = CHAT_NONE;
m_pClient->OnRelease();
}
}
else if(Event.m_Flags&IInput::FLAG_PRESS && (Event.m_Key == KEY_RETURN || Event.m_Key == KEY_KP_ENTER))
{
if(IsTypingCommand() && ExecuteCommand())
{
// everything is handled within
}
else
{
if(m_Input.GetString()[0])
{
@ -191,6 +227,7 @@ bool CChat::OnInput(IInput::CEvent Event)
m_Mode = CHAT_NONE;
m_pClient->OnRelease();
}
}
if(Event.m_Flags&IInput::FLAG_PRESS && Event.m_Key == KEY_TAB)
{
if (m_Mode == CHAT_WHISPER)
@ -323,6 +360,12 @@ bool CChat::OnInput(IInput::CEvent Event)
m_ReverseCompletion = false;
if(Event.m_Flags&IInput::FLAG_PRESS && Event.m_Key == KEY_UP)
{
if(IsTypingCommand())
{
m_pCommands->SelectPreviousCommand();
}
else
{
if(m_pHistoryEntry)
{
@ -337,7 +380,14 @@ bool CChat::OnInput(IInput::CEvent Event)
if(m_pHistoryEntry)
m_Input.Set(m_pHistoryEntry->m_aText);
}
}
else if (Event.m_Flags&IInput::FLAG_PRESS && Event.m_Key == KEY_DOWN)
{
if(IsTypingCommand())
{
m_pCommands->SelectNextCommand();
}
else
{
if(m_pHistoryEntry)
m_pHistoryEntry = m_History.Next(m_pHistoryEntry);
@ -347,23 +397,36 @@ bool CChat::OnInput(IInput::CEvent Event)
else
m_Input.Clear();
}
}
return true;
}
void CChat::EnableMode(int Mode)
void CChat::EnableMode(int Mode, const char* pText)
{
if(Client()->State() == IClient::STATE_DEMOPLAYBACK)
return;
if(m_Mode == CHAT_NONE)
{
m_Mode = Mode;
ClearInput();
if(pText) // optional text to initalize with
{
m_Input.Set(pText);
m_Input.SetCursorOffset(str_length(pText));
m_InputUpdate = true;
}
}
void CChat::ClearInput()
{
m_Input.Clear();
Input()->Clear();
m_CompletionChosen = -1;
}
m_IgnoreCommand = false;
m_pCommands->Reset();
}
void CChat::OnMessage(int MsgType, void *pRawMsg)
@ -377,7 +440,7 @@ void CChat::OnMessage(int MsgType, void *pRawMsg)
void CChat::AddLine(int ClientID, int Mode, const char *pLine, int TargetID)
{
if(*pLine == 0 || (ClientID != -1 && (!g_Config.m_ClShowsocial || !m_pClient->m_aClients[ClientID].m_Active || // unknown client
if(*pLine == 0 || (ClientID >= 0 && (!g_Config.m_ClShowsocial || !m_pClient->m_aClients[ClientID].m_Active || // unknown client
m_pClient->m_aClients[ClientID].m_ChatIgnore ||
g_Config.m_ClFilterchat == 2 ||
(m_pClient->m_LocalClientID != ClientID && g_Config.m_ClFilterchat == 1 && !m_pClient->m_aClients[ClientID].m_Friend))))
@ -385,7 +448,7 @@ void CChat::AddLine(int ClientID, int Mode, const char *pLine, int TargetID)
if(Mode == CHAT_WHISPER)
{
// unknown client
if(ClientID == -1 || !m_pClient->m_aClients[ClientID].m_Active || TargetID == -1 || !m_pClient->m_aClients[TargetID].m_Active)
if(ClientID < 0 || !m_pClient->m_aClients[ClientID].m_Active || TargetID < 0 || !m_pClient->m_aClients[TargetID].m_Active)
return;
// should be sender or receiver
if(ClientID != m_pClient->m_LocalClientID && TargetID != m_pClient->m_LocalClientID)
@ -451,7 +514,7 @@ void CChat::AddLine(int ClientID, int Mode, const char *pLine, int TargetID)
// check for highlighted name
Highlighted = false;
// do not highlight our own messages, whispers and system messages
if(Mode != CHAT_WHISPER && ClientID != -1 && ClientID != m_pClient->m_LocalClientID)
if(Mode != CHAT_WHISPER && ClientID >= 0 && ClientID != m_pClient->m_LocalClientID)
{
const char *pHL = str_find_nocase(pLine, m_pClient->m_aClients[m_pClient->m_LocalClientID].m_aName);
if(pHL)
@ -474,11 +537,16 @@ void CChat::AddLine(int ClientID, int Mode, const char *pLine, int TargetID)
if(Mode == CHAT_WHISPER && ClientID == m_pClient->m_LocalClientID && TargetID >= 0)
NameCID = TargetID;
if(ClientID == -1) // server message
if(ClientID == SERVER_MSG)
{
m_aLines[m_CurrentLine].m_aName[0] = 0;
str_format(m_aLines[m_CurrentLine].m_aText, sizeof(m_aLines[m_CurrentLine].m_aText), "*** %s", pLine);
}
else if(ClientID == CLIENT_MSG)
{
m_aLines[m_CurrentLine].m_aName[0] = 0;
str_format(m_aLines[m_CurrentLine].m_aText, sizeof(m_aLines[m_CurrentLine].m_aText), "— %s", pLine);
}
else
{
if(m_pClient->m_aClients[ClientID].m_Team == TEAM_SPECTATORS)
@ -509,9 +577,12 @@ void CChat::AddLine(int ClientID, int Mode, const char *pLine, int TargetID)
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, aBufMode, aBuf, Highlighted || Mode == CHAT_WHISPER);
}
if(Mode == CHAT_WHISPER && m_pClient->m_LocalClientID != ClientID)
m_LastWhisperFrom = ClientID; // we received a a whisper
// play sound
int64 Now = time_get();
if(ClientID == -1)
if(ClientID < 0)
{
if(Now-m_aLastSoundPlayed[CHAT_SERVER] >= time_freq()*3/10)
{
@ -566,6 +637,8 @@ void CChat::OnRender()
const int LocalCID = m_pClient->m_LocalClientID;
const CGameClient::CClientData& LocalClient = m_pClient->m_aClients[LocalCID];
const int LocalTteam = LocalClient.m_Team;
// bool showCommands;
float CategoryWidth = 0;
if(m_Mode == CHAT_WHISPER && !m_pClient->m_aClients[m_WhisperTarget].m_Active)
m_Mode = CHAT_NONE;
@ -573,8 +646,8 @@ void CChat::OnRender()
{
// calculate category text size
// TODO: rework TextRender. Writing the same code twice to calculate a simple thing as width is ridiculus
float CategoryWidth = 0;
float CategoryHeight;
const float IconOffsetX = m_Mode == CHAT_WHISPER ? 6.0f : 0.0f;
const float CategoryFontSize = 8.0f;
const float InputFontSize = 8.0f;
char aCatText[48];
@ -617,8 +690,6 @@ void CChat::OnRender()
else if(m_Mode == CHAT_WHISPER)
CatRectColor = CRCWhisper;
const float IconOffsetX = m_Mode == CHAT_WHISPER ? 6.0f : 0.0f;
CUIRect CatRect;
CatRect.x = 0;
CatRect.y = y;
@ -758,7 +829,7 @@ void CChat::OnRender()
else if(Line.m_Mode == CHAT_WHISPER)
str_format(aBuf, sizeof(aBuf), "[%s] ", Localize("Whisper"));
if(Line.m_ClientID != -1)
if(Line.m_ClientID >= 0)
{
Cursor.m_X += RenderTools()->GetClientIdRectSize(Cursor.m_FontSize);
str_append(aBuf, Line.m_aName, sizeof(aBuf));
@ -908,7 +979,7 @@ void CChat::OnRender()
}
// render name
if(Line.m_ClientID == -1)
if(Line.m_ClientID < 0)
TextColor = ColorSystem;
else if(Line.m_Mode == CHAT_WHISPER)
TextColor = ColorWhisper;
@ -923,7 +994,7 @@ void CChat::OnRender()
else
TextColor = ColorAllPre;
if(Line.m_ClientID != -1)
if(Line.m_ClientID >= 0)
{
int NameCID = Line.m_ClientID;
if(Line.m_Mode == CHAT_WHISPER && Line.m_ClientID == m_pClient->m_LocalClientID && Line.m_TargetID >= 0)
@ -938,7 +1009,7 @@ void CChat::OnRender()
}
// render line
if(Line.m_ClientID == -1)
if(Line.m_ClientID < 0)
TextColor = ColorSystem;
else if(Line.m_Mode == CHAT_WHISPER)
TextColor = ColorWhisper;
@ -964,6 +1035,8 @@ void CChat::OnRender()
TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f);
TextRender()->TextOutlineColor(0.0f, 0.0f, 0.0f, 0.3f);
HandleCommands(x+CategoryWidth, Height - 24.f, 200.0f-CategoryWidth);
}
void CChat::Say(int Mode, const char *pLine)
@ -977,3 +1050,319 @@ void CChat::Say(int Mode, const char *pLine)
Msg.m_pMessage = pLine;
Client()->SendPackMsg(&Msg, MSGFLAG_VITAL);
}
bool CChat::IsTypingCommand() const
{
return m_Input.GetString()[0] == '/' && !m_IgnoreCommand;
}
// chat commands handlers
void CChat::HandleCommands(float x, float y, float w)
{
// render commands menu
if(m_Mode != CHAT_NONE && IsTypingCommand())
{
const float Alpha = 0.90f;
const float LineWidth = w;
const float LineHeight = 8.0f;
m_pCommands->Filter(m_Input.GetString()); // flag active commands, update selected command
const int ActiveCount = m_pCommands->CountActiveCommands();
if(ActiveCount > 0) // at least one command to display
{
CUIRect Rect = {x, y-(ActiveCount+1)*LineHeight, LineWidth, (ActiveCount+1)*LineHeight};
RenderTools()->DrawUIRect(&Rect, vec4(0.125f, 0.125f, 0.125f, Alpha), CUI::CORNER_ALL, 3.0f);
// render notification
{
y -= LineHeight;
CTextCursor Cursor;
TextRender()->SetCursor(&Cursor, Rect.x + 5.0f, y, 5.0f, TEXTFLAG_RENDER);
TextRender()->TextColor(0.5f, 0.5f, 0.5f, 1.0f);
TextRender()->TextEx(&Cursor, Localize("Press Enter to confirm or Esc to cancel"), -1);
}
// render commands
for(int i = ActiveCount - 1; i >= 0; i--)
{
y -= LineHeight;
CUIRect HighlightRect = {Rect.x, y, LineWidth, LineHeight-1};
// retrieve command
const CChatCommand* pCommand = m_pCommands->GetCommand(i);
// draw selection box
if(pCommand == m_pCommands->GetSelectedCommand())
RenderTools()->DrawUIRect(&HighlightRect, vec4(0.25f, 0.25f, 0.6f, Alpha), CUI::CORNER_ALL, 2.0f);
// print command
CTextCursor Cursor;
TextRender()->SetCursor(&Cursor, Rect.x + 5.0f, y, 5.0f, TEXTFLAG_RENDER);
TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f);
TextRender()->TextEx(&Cursor, pCommand->m_pCommandText, -1);
TextRender()->TextColor(0.5f, 0.5f, 0.5f, 1.0f);
TextRender()->TextEx(&Cursor, pCommand->m_pHelpText, -1);
TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f);
}
}
}
}
bool CChat::ExecuteCommand()
{
if(m_pCommands->CountActiveCommands() == 0)
return false;
const char* pCommandStr = m_Input.GetString();
const CChatCommand* pCommand = m_pCommands->GetSelectedCommand();
dbg_assert(pCommand != 0, "selected command does not exist");
bool IsFullMatch = str_find(pCommandStr, pCommand->m_pCommandText); // if the command text is fully inside pCommandStr (aka, not a shortcut)
if(IsFullMatch)
{
// execute command
if(pCommand->m_pfnFunc != 0)
pCommand->m_pfnFunc(this, pCommandStr);
}
else
{
// autocomplete command
char aBuf[128];
str_copy(aBuf, pCommand->m_pCommandText, sizeof(aBuf));
str_append(aBuf, " ", sizeof(aBuf));
m_Input.Set(aBuf);
m_Input.SetCursorOffset(str_length(aBuf));
m_InputUpdate = true;
}
return true;
}
// returns -1 if not found or duplicate
int CChat::IdentifyNameParameter(const char* pCommand) const
{
// retrieve name parameter
const char* pParameter = str_skip_to_whitespace_const(pCommand);
if(!pParameter)
return -1;
// do not count leading and trailing whitespaces
char aName[MAX_NAME_LENGTH];
str_copy(aName, pParameter+1, sizeof(aName));
str_clean_whitespaces_simple(aName);
int TargetID = -1;
for(int i = 0; i < MAX_CLIENTS; ++i)
{
if(!m_pClient->m_aClients[i].m_Active || i == m_pClient->m_LocalClientID) // skip local user
continue;
if(str_length(m_pClient->m_aClients[i].m_aName) == str_length(aName) && str_comp(m_pClient->m_aClients[i].m_aName, aName) == 0)
{
// name strictly matches
if(TargetID != -1)
{
// duplicate; be conservative
dbg_msg("chat", "name duplicate found, aborting whisper command");
return -1;
}
TargetID = i;
}
}
return TargetID;
}
// callback functions for commands
void CChat::Com_All(CChat *pChatData, const char* pCommand)
{
const char* pParameter = str_skip_to_whitespace_const(pCommand);
char *pBuf = 0x0;
if(pParameter++ && *pParameter) // skip the first space
{
// save the parameter in a buffer before EnableMode clears it
pBuf = (char*)mem_alloc(str_length(pParameter) + 1, 1);
str_copy(pBuf, pParameter, str_length(pParameter) + 1);
}
pChatData->EnableMode(CHAT_ALL, pBuf);
mem_free(pBuf);
}
void CChat::Com_Team(CChat *pChatData, const char* pCommand)
{
const char* pParameter = str_skip_to_whitespace_const(pCommand);
char *pBuf = 0x0;
if(pParameter++ && *pParameter) // skip the first space
{
// save the parameter in a buffer before EnableMode clears it
pBuf = (char*)mem_alloc(str_length(pParameter) + 1, 1);
str_copy(pBuf, pParameter, str_length(pParameter) + 1);
}
pChatData->EnableMode(CHAT_TEAM, pBuf);
mem_free(pBuf);
}
void CChat::Com_Reply(CChat *pChatData, const char* pCommand)
{
if(pChatData->m_LastWhisperFrom == -1)
pChatData->ClearInput(); // just reset the chat
else
{
pChatData->m_WhisperTarget = pChatData->m_LastWhisperFrom;
const char* pParameter = str_skip_to_whitespace_const(pCommand);
char *pBuf = 0x0;
if(pParameter++ && *pParameter) // skip the first space
{
// save the parameter in a buffer before EnableMode clears it
pBuf = (char*)mem_alloc(str_length(pParameter) + 1, 1);
str_copy(pBuf, pParameter, sizeof(pBuf));
}
pChatData->EnableMode(CHAT_WHISPER, pBuf);
mem_free(pBuf);
}
}
void CChat::Com_Whisper(CChat *pChatData, const char* pCommand)
{
int TargetID = pChatData->IdentifyNameParameter(pCommand);
if(TargetID != -1)
{
pChatData->m_WhisperTarget = TargetID;
pChatData->EnableMode(CHAT_WHISPER);
}
}
void CChat::Com_Mute(CChat *pChatData, const char* pCommand)
{
int TargetID = pChatData->IdentifyNameParameter(pCommand);
if(TargetID != -1)
{
pChatData->m_pClient->m_aClients[TargetID].m_ChatIgnore ^= 1;
pChatData->ClearInput();
char aMsg[128];
str_format(aMsg, sizeof(aMsg), pChatData->m_pClient->m_aClients[TargetID].m_ChatIgnore ? Localize("'%s' was muted") : Localize("'%s' was unmuted"), pChatData->m_pClient->m_aClients[TargetID].m_aName);
pChatData->AddLine(CLIENT_MSG, CHAT_ALL, aMsg, -1);
}
}
void CChat::Com_Befriend(CChat *pChatData, const char* pCommand)
{
int TargetID = pChatData->IdentifyNameParameter(pCommand);
if(TargetID != -1)
{
bool isFriend = pChatData->m_pClient->m_aClients[TargetID].m_Friend;
if(isFriend)
pChatData->m_pClient->Friends()->RemoveFriend(pChatData->m_pClient->m_aClients[TargetID].m_aName, pChatData->m_pClient->m_aClients[TargetID].m_aClan);
else
pChatData->m_pClient->Friends()->AddFriend(pChatData->m_pClient->m_aClients[TargetID].m_aName, pChatData->m_pClient->m_aClients[TargetID].m_aClan);
pChatData->m_pClient->m_aClients[TargetID].m_Friend ^= 1;
pChatData->ClearInput();
char aMsg[128];
str_format(aMsg, sizeof(aMsg), !isFriend ? Localize("'%s' was added as a friend") : Localize("'%s' was removed as a friend"), pChatData->m_pClient->m_aClients[TargetID].m_aName);
pChatData->AddLine(CLIENT_MSG, CHAT_ALL, aMsg, -1);
}
}
// CChatCommands methods
CChat::CChatCommands::CChatCommands(CChatCommand apCommands[], int Count) : m_apCommands(apCommands), m_Count(Count), m_pSelectedCommand(0x0) { }
CChat::CChatCommands::~CChatCommands() { }
// selection
void CChat::CChatCommands::Reset()
{
m_pSelectedCommand = 0x0;
}
// Example: /whisper command will match "/whi", "/whisper" and "/whisper tee"
void CChat::CChatCommands::Filter(const char* pLine)
{
char aCommandStr[64];
str_copy(aCommandStr, pLine, sizeof(aCommandStr));
// truncate the string at the first whitespace to get the command
char* pFirstWhitespace = str_skip_to_whitespace(aCommandStr);
if(pFirstWhitespace)
*pFirstWhitespace = 0;
for(int i = 0; i < m_Count; i++)
m_apCommands[i].m_aFiltered = (str_find_nocase(m_apCommands[i].m_pCommandText, aCommandStr) != m_apCommands[i].m_pCommandText);
// also update selected command
if(!GetSelectedCommand() || GetSelectedCommand()->m_aFiltered)
{
if(CountActiveCommands() > 0)
m_pSelectedCommand = &m_apCommands[GetActiveIndex(0)]; // default to first command
else
m_pSelectedCommand = 0x0;
}
}
// this will not return a correct value if we are not writing a command (m_Input.GetString()[0] == '/')
int CChat::CChatCommands::CountActiveCommands() const
{
int n = m_Count;
for(int i = 0; i < m_Count; i++)
n -= m_apCommands[i].m_aFiltered;
return n;
}
const CChat::CChatCommand* CChat::CChatCommands::GetCommand(int index) const
{
return &m_apCommands[GetActiveIndex(index)];
}
const CChat::CChatCommand* CChat::CChatCommands::GetSelectedCommand() const
{
return m_pSelectedCommand;
}
void CChat::CChatCommands::SelectPreviousCommand()
{
CChatCommand* LastCommand = 0x0;
for(int i = 0; i < m_Count; i++)
{
if(m_apCommands[i].m_aFiltered)
continue;
if(&m_apCommands[i] == m_pSelectedCommand)
{
m_pSelectedCommand = LastCommand;
return;
}
LastCommand = &m_apCommands[i];
}
}
void CChat::CChatCommands::SelectNextCommand()
{
bool FoundSelected = false;
for(int i = 0; i < m_Count; i++)
{
if(m_apCommands[i].m_aFiltered)
continue;
if(FoundSelected)
{
m_pSelectedCommand = &m_apCommands[i];
return;
}
if(&m_apCommands[i] == m_pSelectedCommand)
FoundSelected = true;
}
}
int CChat::CChatCommands::GetActiveIndex(int index) const
{
for(int i = 0; i < m_Count; i++)
{
if(m_apCommands[i].m_aFiltered)
index++;
if(i == index)
return i;
}
dbg_break();
return -1;
}

View file

@ -2,6 +2,7 @@
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#ifndef GAME_CLIENT_COMPONENTS_CHAT_H
#define GAME_CLIENT_COMPONENTS_CHAT_H
#include <base/system.h>
#include <engine/shared/ringbuffer.h>
#include <game/client/component.h>
#include <game/client/lineinput.h>
@ -28,10 +29,17 @@ class CChat : public CComponent
bool m_Highlighted;
};
// client IDs for special messages
enum
{
CLIENT_MSG = -2,
SERVER_MSG = -1,
};
CLine m_aLines[MAX_LINES];
int m_CurrentLine;
// chat
// chat sounds
enum
{
CHAT_SERVER=0,
@ -42,6 +50,7 @@ class CChat : public CComponent
int m_Mode;
int m_WhisperTarget;
int m_LastWhisperFrom;
bool m_Show;
bool m_InputUpdate;
int m_ChatStringOffset;
@ -64,6 +73,51 @@ class CChat : public CComponent
int64 m_LastChatSend;
int64 m_aLastSoundPlayed[CHAT_NUM];
// chat commands
struct CChatCommand
{
const char* m_pCommandText;
const char* m_pHelpText;
void (*m_pfnFunc)(CChat *pChatData, const char* pCommand);
bool m_aFiltered; // 0 = shown, 1 = hidden
};
class CChatCommands
{
CChatCommand *m_apCommands;
int m_Count;
CChatCommand *m_pSelectedCommand;
private:
int GetActiveIndex(int index) const;
public:
CChatCommands(CChatCommand apCommands[], int Count);
~CChatCommands();
void Reset();
void Filter(const char* pLine);
int CountActiveCommands() const;
const CChatCommand* GetCommand(int index) const;
const CChatCommand* GetSelectedCommand() const;
void SelectPreviousCommand();
void SelectNextCommand();
};
CChatCommands *m_pCommands;
bool m_IgnoreCommand;
bool IsTypingCommand() const;
void HandleCommands(float x, float y, float w);
bool ExecuteCommand();
int IdentifyNameParameter(const char* pCommand) const;
static void Com_All(CChat *pChatData, const char* pCommand);
static void Com_Team(CChat *pChatData, const char* pCommand);
static void Com_Reply(CChat *pChatData, const char* pCommand);
static void Com_Whisper(CChat *pChatData, const char* pCommand);
static void Com_Mute(CChat *pChatData, const char* pCommand);
static void Com_Befriend(CChat *pChatData, const char* pCommand);
void ClearInput();
static void ConSay(IConsole::IResult *pResult, void *pUserData);
static void ConSayTeam(IConsole::IResult *pResult, void *pUserData);
static void ConWhisper(IConsole::IResult *pResult, void *pUserData);
@ -72,12 +126,13 @@ class CChat : public CComponent
public:
CChat();
~CChat();
bool IsActive() const { return m_Mode != CHAT_NONE; }
void AddLine(int ClientID, int Team, const char *pLine, int TargetID = -1);
void EnableMode(int Team);
void EnableMode(int Team, const char* pText = NULL);
void Say(int Team, const char *pLine);