ddnet/src/game/client/components/chat.cpp

437 lines
13 KiB
C++
Raw Normal View History

2010-11-20 10:37:14 +00:00
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
2008-08-27 19:41:02 +00:00
2011-02-27 16:56:03 +00:00
#include <engine/engine.h>
2010-05-29 07:25:38 +00:00
#include <engine/graphics.h>
#include <engine/textrender.h>
#include <engine/keys.h>
#include <engine/shared/config.h>
2010-05-29 07:25:38 +00:00
#include <game/generated/protocol.h>
#include <game/generated/client_data.h>
2010-05-29 07:25:38 +00:00
#include <game/client/gameclient.h>
2008-08-29 05:34:18 +00:00
#include <game/client/components/scoreboard.h>
2010-05-29 07:25:38 +00:00
#include <game/client/components/sounds.h>
#include <game/localization.h>
2010-05-29 07:25:38 +00:00
#include "chat.h"
CChat::CChat()
{
2010-05-29 07:25:38 +00:00
OnReset();
}
void CChat::OnReset()
{
for(int i = 0; i < MAX_LINES; i++)
2009-05-31 09:44:20 +00:00
{
2010-05-29 07:25:38 +00:00
m_aLines[i].m_Time = 0;
m_aLines[i].m_aText[0] = 0;
m_aLines[i].m_aName[0] = 0;
}
m_Show = false;
m_InputUpdate = false;
m_ChatStringOffset = 0;
m_CompletionChosen = -1;
m_aCompletionBuffer[0] = 0;
m_PlaceholderOffset = 0;
m_PlaceholderLength = 0;
2011-03-20 15:17:06 +00:00
m_pHistoryEntry = 0x0;
2010-05-29 07:25:38 +00:00
}
void CChat::OnRelease()
{
m_Show = false;
}
2010-05-29 07:25:38 +00:00
void CChat::OnStateChange(int NewState, int OldState)
{
if(OldState <= IClient::STATE_CONNECTING)
{
m_Mode = MODE_NONE;
2009-05-31 09:44:20 +00:00
for(int i = 0; i < MAX_LINES; i++)
2010-05-29 07:25:38 +00:00
m_aLines[i].m_Time = 0;
m_CurrentLine = 0;
2009-05-31 09:44:20 +00:00
}
}
2010-05-29 07:25:38 +00:00
void CChat::ConSay(IConsole::IResult *pResult, void *pUserData)
2008-08-27 19:41:02 +00:00
{
2010-05-29 07:25:38 +00:00
((CChat*)pUserData)->Say(0, pResult->GetString(0));
2008-08-27 19:41:02 +00:00
}
2010-05-29 07:25:38 +00:00
void CChat::ConSayTeam(IConsole::IResult *pResult, void *pUserData)
2008-08-27 19:41:02 +00:00
{
2010-05-29 07:25:38 +00:00
((CChat*)pUserData)->Say(1, pResult->GetString(0));
2008-08-27 19:41:02 +00:00
}
2010-05-29 07:25:38 +00:00
void CChat::ConChat(IConsole::IResult *pResult, void *pUserData)
2008-08-27 19:41:02 +00:00
{
2010-05-29 07:25:38 +00:00
const char *pMode = pResult->GetString(0);
if(str_comp(pMode, "all") == 0)
((CChat*)pUserData)->EnableMode(0);
else if(str_comp(pMode, "team") == 0)
((CChat*)pUserData)->EnableMode(1);
2008-08-27 19:41:02 +00:00
else
((CChat*)pUserData)->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "console", "expected all or team as mode");
2008-08-27 19:41:02 +00:00
}
void CChat::ConShowChat(IConsole::IResult *pResult, void *pUserData)
{
((CChat *)pUserData)->m_Show = pResult->GetInteger(0) != 0;
}
2010-05-29 07:25:38 +00:00
void CChat::OnConsoleInit()
2008-08-27 19:41:02 +00:00
{
2010-05-29 07:25:38 +00:00
Console()->Register("say", "r", CFGFLAG_CLIENT, ConSay, this, "Say in chat");
Console()->Register("say_team", "r", CFGFLAG_CLIENT, ConSayTeam, this, "Say in team chat");
Console()->Register("chat", "s", CFGFLAG_CLIENT, ConChat, this, "Enable chat with all/team mode");
Console()->Register("+show_chat", "", CFGFLAG_CLIENT, ConShowChat, this, "Show chat");
2008-08-27 19:41:02 +00:00
}
2011-03-20 15:36:10 +00:00
bool CChat::OnInput(IInput::CEvent Event)
{
2010-05-29 07:25:38 +00:00
if(m_Mode == MODE_NONE)
return false;
2011-03-20 15:36:10 +00:00
if(Event.m_Flags&IInput::FLAG_PRESS && Event.m_Key == KEY_ESCAPE)
{
2010-05-29 07:25:38 +00:00
m_Mode = MODE_NONE;
m_pClient->OnRelease();
}
2011-03-20 15:36:10 +00:00
else if(Event.m_Flags&IInput::FLAG_PRESS && (Event.m_Key == KEY_RETURN || Event.m_Key == KEY_KP_ENTER))
{
2010-05-29 07:25:38 +00:00
if(m_Input.GetString()[0])
2011-03-20 15:17:06 +00:00
{
2010-05-29 07:25:38 +00:00
Say(m_Mode == MODE_ALL ? 0 : 1, m_Input.GetString());
2011-03-20 15:17:06 +00:00
char *pEntry = m_History.Allocate(m_Input.GetLength()+1);
mem_copy(pEntry, m_Input.GetString(), m_Input.GetLength()+1);
}
m_pHistoryEntry = 0x0;
2010-05-29 07:25:38 +00:00
m_Mode = MODE_NONE;
m_pClient->OnRelease();
}
2011-03-20 15:36:10 +00:00
if(Event.m_Flags&IInput::FLAG_PRESS && Event.m_Key == KEY_TAB)
{
// fill the completion buffer
if(m_CompletionChosen < 0)
{
const char *pCursor = m_Input.GetString()+m_Input.GetCursorOffset();
for(int Count = 0; Count < m_Input.GetCursorOffset() && *(pCursor-1) != ' '; --pCursor, ++Count);
m_PlaceholderOffset = pCursor-m_Input.GetString();
for(m_PlaceholderLength = 0; *pCursor && *pCursor != ' '; ++pCursor)
++m_PlaceholderLength;
str_copy(m_aCompletionBuffer, m_Input.GetString()+m_PlaceholderOffset, min(static_cast<int>(sizeof(m_aCompletionBuffer)), m_PlaceholderLength+1));
}
// find next possible name
const char *pCompletionString = 0;
m_CompletionChosen = (m_CompletionChosen+1)%MAX_CLIENTS;
for(int i = 0; i < MAX_CLIENTS; ++i)
{
int Index = (m_CompletionChosen+i)%MAX_CLIENTS;
if(!m_pClient->m_Snap.m_paPlayerInfos[Index])
continue;
if(str_find_nocase(m_pClient->m_aClients[Index].m_aName, m_aCompletionBuffer))
{
pCompletionString = m_pClient->m_aClients[Index].m_aName;
m_CompletionChosen = Index;
break;
}
}
// insert the name
if(pCompletionString)
{
char aBuf[256];
str_copy(aBuf, m_Input.GetString(), min(static_cast<int>(sizeof(aBuf)), m_PlaceholderOffset+1));
str_append(aBuf, pCompletionString, sizeof(aBuf));
str_append(aBuf, m_Input.GetString()+m_PlaceholderOffset+m_PlaceholderLength, sizeof(aBuf));
m_PlaceholderLength = str_length(pCompletionString);
m_OldChatStringLength = m_Input.GetLength();
m_Input.Set(aBuf);
m_Input.SetCursorOffset(m_PlaceholderOffset+m_PlaceholderLength);
m_InputUpdate = true;
}
}
else
{
// reset name completion process
2011-03-20 15:36:10 +00:00
if(Event.m_Flags&IInput::FLAG_PRESS && Event.m_Key != KEY_TAB)
m_CompletionChosen = -1;
m_OldChatStringLength = m_Input.GetLength();
2011-03-20 15:36:10 +00:00
m_Input.ProcessInput(Event);
m_InputUpdate = true;
}
2011-03-20 15:36:10 +00:00
if(Event.m_Flags&IInput::FLAG_PRESS && Event.m_Key == KEY_UP)
2011-03-20 15:17:06 +00:00
{
if (m_pHistoryEntry)
{
char *pTest = m_History.Prev(m_pHistoryEntry);
if (pTest)
m_pHistoryEntry = pTest;
}
else
m_pHistoryEntry = m_History.Last();
if (m_pHistoryEntry)
{
unsigned int Len = str_length(m_pHistoryEntry);
if (Len < sizeof(m_Input) - 1) // TODO: WTF?
m_Input.Set(m_pHistoryEntry);
}
}
2011-03-20 15:36:10 +00:00
else if (Event.m_Flags&IInput::FLAG_PRESS && Event.m_Key == KEY_DOWN)
2011-03-20 15:17:06 +00:00
{
if (m_pHistoryEntry)
m_pHistoryEntry = m_History.Next(m_pHistoryEntry);
if (m_pHistoryEntry)
{
unsigned int Len = str_length(m_pHistoryEntry);
if (Len < sizeof(m_Input) - 1) // TODO: WTF?
m_Input.Set(m_pHistoryEntry);
}
else
m_Input.Clear();
}
return true;
}
2010-05-29 07:25:38 +00:00
void CChat::EnableMode(int Team)
{
if(Client()->State() == IClient::STATE_DEMOPLAYBACK)
return;
2010-05-29 07:25:38 +00:00
if(m_Mode == MODE_NONE)
{
2010-05-29 07:25:38 +00:00
if(Team)
m_Mode = MODE_TEAM;
else
2010-05-29 07:25:38 +00:00
m_Mode = MODE_ALL;
2010-05-29 07:25:38 +00:00
m_Input.Clear();
Input()->ClearEvents();
m_CompletionChosen = -1;
}
}
2010-05-29 07:25:38 +00:00
void CChat::OnMessage(int MsgType, void *pRawMsg)
{
2010-05-29 07:25:38 +00:00
if(MsgType == NETMSGTYPE_SV_CHAT)
{
2010-05-29 07:25:38 +00:00
CNetMsg_Sv_Chat *pMsg = (CNetMsg_Sv_Chat *)pRawMsg;
AddLine(pMsg->m_ClientID, pMsg->m_Team, pMsg->m_pMessage);
}
}
void CChat::AddLine(int ClientID, int Team, const char *pLine)
{
if(ClientID != -1 && m_pClient->m_aClients[ClientID].m_aName[0] == '\0' || // unknown client
m_pClient->m_aClients[ClientID].m_ChatIgnore)
return;
bool Highlighted = false;
2010-05-29 07:25:38 +00:00
char *p = const_cast<char*>(pLine);
while(*p)
{
2010-05-29 07:25:38 +00:00
pLine = p;
// find line seperator and strip multiline
while(*p)
{
if(*p++ == '\n')
{
*(p-1) = 0;
break;
}
}
2010-05-29 07:25:38 +00:00
m_CurrentLine = (m_CurrentLine+1)%MAX_LINES;
m_aLines[m_CurrentLine].m_Time = time_get();
2010-10-15 17:26:20 +00:00
m_aLines[m_CurrentLine].m_YOffset[0] = -1.0f;
m_aLines[m_CurrentLine].m_YOffset[1] = -1.0f;
m_aLines[m_CurrentLine].m_ClientID = ClientID;
2010-05-29 07:25:38 +00:00
m_aLines[m_CurrentLine].m_Team = Team;
m_aLines[m_CurrentLine].m_NameColor = -2;
m_aLines[m_CurrentLine].m_Highlighted = str_find_nocase(pLine, m_pClient->m_aClients[m_pClient->m_Snap.m_LocalClientID].m_aName) != 0;
if(m_aLines[m_CurrentLine].m_Highlighted)
Highlighted = true;
2010-05-29 07:25:38 +00:00
if(ClientID == -1) // server message
2010-05-29 07:25:38 +00:00
{
str_copy(m_aLines[m_CurrentLine].m_aName, "*** ", sizeof(m_aLines[m_CurrentLine].m_aName));
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)
2011-01-03 11:50:38 +00:00
m_aLines[m_CurrentLine].m_NameColor = TEAM_SPECTATORS;
2010-05-29 07:25:38 +00:00
2011-03-04 16:08:10 +00:00
if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_TEAMS)
2010-05-29 07:25:38 +00:00
{
if(m_pClient->m_aClients[ClientID].m_Team == TEAM_RED)
2011-01-03 11:50:38 +00:00
m_aLines[m_CurrentLine].m_NameColor = TEAM_RED;
else if(m_pClient->m_aClients[ClientID].m_Team == TEAM_BLUE)
2011-01-03 11:50:38 +00:00
m_aLines[m_CurrentLine].m_NameColor = TEAM_BLUE;
2010-05-29 07:25:38 +00:00
}
str_copy(m_aLines[m_CurrentLine].m_aName, m_pClient->m_aClients[ClientID].m_aName, sizeof(m_aLines[m_CurrentLine].m_aName));
2010-05-29 07:25:38 +00:00
str_format(m_aLines[m_CurrentLine].m_aText, sizeof(m_aLines[m_CurrentLine].m_aText), ": %s", pLine);
}
2010-05-29 07:25:38 +00:00
char aBuf[1024];
str_format(aBuf, sizeof(aBuf), "%s%s", m_aLines[m_CurrentLine].m_aName, m_aLines[m_CurrentLine].m_aText);
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "chat", aBuf);
}
2010-05-29 07:25:38 +00:00
// play sound
if(ClientID == -1)
m_pClient->m_pSounds->Play(CSounds::CHN_GUI, SOUND_CHAT_SERVER, 0, vec2(0,0));
else if(Highlighted)
m_pClient->m_pSounds->Play(CSounds::CHN_GUI, SOUND_CHAT_HIGHLIGHT, 0, vec2(0.0f, 0.0f));
else
m_pClient->m_pSounds->Play(CSounds::CHN_GUI, SOUND_CHAT_CLIENT, 0, vec2(0,0));
}
2010-05-29 07:25:38 +00:00
void CChat::OnRender()
{
2009-10-27 14:38:53 +00:00
Graphics()->MapScreen(0,0,300*Graphics()->ScreenAspect(),300);
float x = 5.0f;
2008-09-07 08:44:30 +00:00
float y = 300.0f-20.0f;
2010-05-29 07:25:38 +00:00
if(m_Mode != MODE_NONE)
{
// render chat input
2010-05-29 07:25:38 +00:00
CTextCursor Cursor;
TextRender()->SetCursor(&Cursor, x, y, 8.0f, TEXTFLAG_RENDER);
Cursor.m_LineWidth = 200.0f;
Cursor.m_MaxLines = 2;
2010-05-29 07:25:38 +00:00
if(m_Mode == MODE_ALL)
TextRender()->TextEx(&Cursor, Localize("All"), -1);
else if(m_Mode == MODE_TEAM)
TextRender()->TextEx(&Cursor, Localize("Team"), -1);
else
2010-05-29 07:25:38 +00:00
TextRender()->TextEx(&Cursor, Localize("Chat"), -1);
2009-06-15 07:34:25 +00:00
2010-05-29 07:25:38 +00:00
TextRender()->TextEx(&Cursor, ": ", -1);
// check if the visible text has to be moved
if(m_InputUpdate)
{
if(m_ChatStringOffset > 0 && m_Input.GetLength() < m_OldChatStringLength)
m_ChatStringOffset = max(0, m_ChatStringOffset-(m_OldChatStringLength-m_Input.GetLength()));
if(m_ChatStringOffset > m_Input.GetCursorOffset())
m_ChatStringOffset -= m_ChatStringOffset-m_Input.GetCursorOffset();
else
{
CTextCursor Temp = Cursor;
Temp.m_Flags = 0;
TextRender()->TextEx(&Temp, m_Input.GetString()+m_ChatStringOffset, m_Input.GetCursorOffset()-m_ChatStringOffset);
TextRender()->TextEx(&Temp, "|", -1);
while(Temp.m_LineCount > 2)
{
++m_ChatStringOffset;
Temp = Cursor;
Temp.m_Flags = 0;
TextRender()->TextEx(&Temp, m_Input.GetString()+m_ChatStringOffset, m_Input.GetCursorOffset()-m_ChatStringOffset);
TextRender()->TextEx(&Temp, "|", -1);
}
}
m_InputUpdate = false;
}
TextRender()->TextEx(&Cursor, m_Input.GetString()+m_ChatStringOffset, m_Input.GetCursorOffset()-m_ChatStringOffset);
2010-05-29 07:25:38 +00:00
CTextCursor Marker = Cursor;
TextRender()->TextEx(&Marker, "|", -1);
TextRender()->TextEx(&Cursor, m_Input.GetString()+m_Input.GetCursorOffset(), -1);
}
y -= 8.0f;
2010-05-29 07:25:38 +00:00
int64 Now = time_get();
float LineWidth = m_pClient->m_pScoreboard->Active() ? 90.0f : 200.0f;
float HeightLimit = m_pClient->m_pScoreboard->Active() ? 230.0f : m_Show ? 50.0f : 200.0f;
2010-10-15 17:26:20 +00:00
float Begin = x;
float FontSize = 6.0f;
CTextCursor Cursor;
int OffsetType = m_pClient->m_pScoreboard->Active() ? 1 : 0;
for(int i = 0; i < MAX_LINES; i++)
{
2010-05-29 07:25:38 +00:00
int r = ((m_CurrentLine-i)+MAX_LINES)%MAX_LINES;
if(Now > m_aLines[r].m_Time+16*time_freq() && !m_Show)
break;
2010-10-15 17:26:20 +00:00
// get the y offset (calculate it if we haven't done that yet)
if(m_aLines[r].m_YOffset[OffsetType] < 0.0f)
{
TextRender()->SetCursor(&Cursor, Begin, 0.0f, FontSize, 0);
Cursor.m_LineWidth = LineWidth;
TextRender()->TextEx(&Cursor, m_aLines[r].m_aName, -1);
TextRender()->TextEx(&Cursor, m_aLines[r].m_aText, -1);
m_aLines[r].m_YOffset[OffsetType] = Cursor.m_Y + Cursor.m_FontSize;
}
y -= m_aLines[r].m_YOffset[OffsetType];
2008-09-07 08:44:30 +00:00
// cut off if msgs waste too much space
if(y < HeightLimit)
2008-09-07 08:44:30 +00:00
break;
float Blend = Now > m_aLines[r].m_Time+14*time_freq() && !m_Show ? 1.0f-(Now-m_aLines[r].m_Time-14*time_freq())/(2.0f*time_freq()) : 1.0f;
// reset the cursor
2010-05-29 07:25:38 +00:00
TextRender()->SetCursor(&Cursor, Begin, y, FontSize, TEXTFLAG_RENDER);
Cursor.m_LineWidth = LineWidth;
// render name
if(m_aLines[r].m_ClientID == -1)
TextRender()->TextColor(1.0f, 1.0f, 0.5f, Blend); // system
2010-05-29 07:25:38 +00:00
else if(m_aLines[r].m_Team)
TextRender()->TextColor(0.45f, 0.9f, 0.45f, Blend); // team message
2011-01-03 11:50:38 +00:00
else if(m_aLines[r].m_NameColor == TEAM_RED)
TextRender()->TextColor(1.0f, 0.5f, 0.5f, Blend); // red
2011-01-03 11:50:38 +00:00
else if(m_aLines[r].m_NameColor == TEAM_BLUE)
TextRender()->TextColor(0.7f, 0.7f, 1.0f, Blend); // blue
2011-01-03 11:50:38 +00:00
else if(m_aLines[r].m_NameColor == TEAM_SPECTATORS)
TextRender()->TextColor(0.75f, 0.5f, 0.75f, Blend); // spectator
2010-10-15 17:26:20 +00:00
else
TextRender()->TextColor(0.8f, 0.8f, 0.8f, Blend);
2010-05-29 07:25:38 +00:00
TextRender()->TextEx(&Cursor, m_aLines[r].m_aName, -1);
// render line
if(m_aLines[r].m_ClientID == -1)
TextRender()->TextColor(1.0f, 1.0f, 0.5f, Blend); // system
else if(m_aLines[r].m_Highlighted)
TextRender()->TextColor(1.0f, 0.5f, 0.5f, Blend); // highlighted
2010-05-29 07:25:38 +00:00
else if(m_aLines[r].m_Team)
TextRender()->TextColor(0.65f, 1.0f, 0.65f, Blend); // team message
2010-10-15 17:26:20 +00:00
else
TextRender()->TextColor(1.0f, 1.0f, 1.0f, Blend);
2010-05-29 07:25:38 +00:00
TextRender()->TextEx(&Cursor, m_aLines[r].m_aText, -1);
}
2010-10-15 17:26:20 +00:00
TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f);
}
2010-05-29 07:25:38 +00:00
void CChat::Say(int Team, const char *pLine)
{
// send chat message
2010-05-29 07:25:38 +00:00
CNetMsg_Cl_Say Msg;
Msg.m_Team = Team;
Msg.m_pMessage = pLine;
Client()->SendPackMsg(&Msg, MSGFLAG_VITAL);
}