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-08-31 11:56:04 +00:00
|
|
|
#include <base/tl/string.h>
|
|
|
|
|
2019-04-05 23:15:02 +00:00
|
|
|
#include <engine/editor.h>
|
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/keys.h>
|
|
|
|
#include <engine/shared/config.h>
|
2020-06-22 20:31:41 +00:00
|
|
|
#include <engine/shared/csv.h>
|
2020-09-22 16:02:03 +00:00
|
|
|
#include <engine/textrender.h>
|
2008-08-27 15:48:50 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
#include <game/generated/client_data.h>
|
2020-09-22 16:02:03 +00:00
|
|
|
#include <game/generated/protocol.h>
|
2008-08-27 15:48:50 +00:00
|
|
|
|
2020-10-13 20:08:52 +00:00
|
|
|
#include <game/client/animstate.h>
|
2010-05-29 07:25:38 +00:00
|
|
|
#include <game/client/gameclient.h>
|
2008-08-29 05:34:18 +00:00
|
|
|
|
2020-09-22 16:01:13 +00:00
|
|
|
#include <game/client/components/console.h>
|
2010-08-18 01:57:35 +00:00
|
|
|
#include <game/client/components/scoreboard.h>
|
2020-10-13 20:08:52 +00:00
|
|
|
#include <game/client/components/skins.h>
|
2010-05-29 07:25:38 +00:00
|
|
|
#include <game/client/components/sounds.h>
|
|
|
|
#include <game/localization.h>
|
2008-08-27 15:48:50 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
#include "chat.h"
|
|
|
|
|
|
|
|
CChat::CChat()
|
2008-08-27 15:48:50 +00:00
|
|
|
{
|
2020-10-26 14:14:07 +00:00
|
|
|
for(auto &Line : m_aLines)
|
2018-03-13 20:50:49 +00:00
|
|
|
{
|
|
|
|
// reset the container indices, so the text containers can be deleted on reset
|
2020-10-26 14:14:07 +00:00
|
|
|
Line.m_TextContainerIndex = -1;
|
|
|
|
Line.m_QuadContainerIndex = -1;
|
2018-03-13 20:50:49 +00:00
|
|
|
}
|
2019-03-19 09:26:30 +00:00
|
|
|
|
2020-09-22 16:02:03 +00:00
|
|
|
#define CHAT_COMMAND(name, params, flags, callback, userdata, help) RegisterCommand(name, params, flags, help);
|
|
|
|
#include <game/server/ddracechat.h>
|
2019-03-19 09:26:30 +00:00
|
|
|
m_Commands.sort_range();
|
|
|
|
|
2020-09-22 16:01:13 +00:00
|
|
|
m_Mode = MODE_NONE;
|
2020-07-07 09:57:57 +00:00
|
|
|
Reset();
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2019-03-19 09:26:30 +00:00
|
|
|
void CChat::RegisterCommand(const char *pName, const char *pParams, int flags, const char *pHelp)
|
|
|
|
{
|
|
|
|
m_Commands.add_unsorted(CCommand{pName, pParams});
|
|
|
|
}
|
|
|
|
|
2020-10-13 20:08:52 +00:00
|
|
|
void CChat::RebuildChat()
|
2018-03-21 14:53:29 +00:00
|
|
|
{
|
2020-10-26 14:14:07 +00:00
|
|
|
for(auto &Line : m_aLines)
|
2018-03-21 14:53:29 +00:00
|
|
|
{
|
2020-10-26 14:14:07 +00:00
|
|
|
if(Line.m_TextContainerIndex != -1)
|
|
|
|
TextRender()->DeleteTextContainer(Line.m_TextContainerIndex);
|
|
|
|
Line.m_TextContainerIndex = -1;
|
|
|
|
if(Line.m_QuadContainerIndex != -1)
|
|
|
|
Graphics()->DeleteQuadContainer(Line.m_QuadContainerIndex);
|
|
|
|
Line.m_QuadContainerIndex = -1;
|
2020-09-28 16:30:27 +00:00
|
|
|
// recalculate sizes
|
2020-10-26 14:14:07 +00:00
|
|
|
Line.m_YOffset[0] = -1.f;
|
|
|
|
Line.m_YOffset[1] = -1.f;
|
2018-03-21 14:53:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-13 20:08:52 +00:00
|
|
|
void CChat::OnWindowResize()
|
|
|
|
{
|
|
|
|
RebuildChat();
|
|
|
|
}
|
|
|
|
|
2020-07-07 09:57:57 +00:00
|
|
|
void CChat::Reset()
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2020-10-26 14:14:07 +00:00
|
|
|
for(auto &Line : m_aLines)
|
2009-05-31 09:44:20 +00:00
|
|
|
{
|
2020-10-26 14:14:07 +00:00
|
|
|
if(Line.m_TextContainerIndex != -1)
|
|
|
|
TextRender()->DeleteTextContainer(Line.m_TextContainerIndex);
|
|
|
|
if(Line.m_QuadContainerIndex != -1)
|
|
|
|
Graphics()->DeleteQuadContainer(Line.m_QuadContainerIndex);
|
|
|
|
Line.m_Time = 0;
|
|
|
|
Line.m_aText[0] = 0;
|
|
|
|
Line.m_aName[0] = 0;
|
|
|
|
Line.m_Friend = false;
|
|
|
|
Line.m_TextContainerIndex = -1;
|
|
|
|
Line.m_QuadContainerIndex = -1;
|
|
|
|
Line.m_TimesRepeated = 0;
|
|
|
|
Line.m_HasRenderTee = false;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2020-11-01 03:48:10 +00:00
|
|
|
m_PrevScoreBoardShowed = false;
|
2018-03-13 20:50:49 +00:00
|
|
|
m_PrevShowChat = false;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2014-10-06 11:02:55 +00:00
|
|
|
m_ReverseTAB = false;
|
2010-05-30 12:01:11 +00:00
|
|
|
m_Show = false;
|
2010-10-11 00:29:30 +00:00
|
|
|
m_InputUpdate = false;
|
|
|
|
m_ChatStringOffset = 0;
|
2010-12-16 01:31:12 +00:00
|
|
|
m_CompletionChosen = -1;
|
|
|
|
m_aCompletionBuffer[0] = 0;
|
|
|
|
m_PlaceholderOffset = 0;
|
|
|
|
m_PlaceholderLength = 0;
|
2011-03-20 15:17:06 +00:00
|
|
|
m_pHistoryEntry = 0x0;
|
2012-01-09 22:13:51 +00:00
|
|
|
m_PendingChatCounter = 0;
|
|
|
|
m_LastChatSend = 0;
|
2020-07-07 09:57:57 +00:00
|
|
|
m_CurrentLine = 0;
|
2020-09-22 16:01:13 +00:00
|
|
|
DisableMode();
|
|
|
|
|
2020-10-26 14:14:07 +00:00
|
|
|
for(long long &LastSoundPlayed : m_aLastSoundPlayed)
|
|
|
|
LastSoundPlayed = 0;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2010-09-12 10:43:03 +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)
|
2020-07-07 09:57:57 +00:00
|
|
|
Reset();
|
2008-08-27 15:48:50 +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
|
|
|
{
|
2020-09-22 16:02:03 +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
|
|
|
{
|
2020-09-22 16:02:03 +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)
|
2020-09-22 16:02:03 +00:00
|
|
|
((CChat *)pUserData)->EnableMode(0);
|
2010-05-29 07:25:38 +00:00
|
|
|
else if(str_comp(pMode, "team") == 0)
|
2020-09-22 16:02:03 +00:00
|
|
|
((CChat *)pUserData)->EnableMode(1);
|
2008-08-27 19:41:02 +00:00
|
|
|
else
|
2020-09-22 16:02:03 +00:00
|
|
|
((CChat *)pUserData)->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "console", "expected all or team as mode");
|
2013-10-13 23:40:42 +00:00
|
|
|
|
2015-08-12 15:03:27 +00:00
|
|
|
if(pResult->GetString(1)[0] || g_Config.m_ClChatReset)
|
2020-09-22 16:02:03 +00:00
|
|
|
((CChat *)pUserData)->m_Input.Set(pResult->GetString(1));
|
2008-08-27 19:41:02 +00:00
|
|
|
}
|
|
|
|
|
2010-05-30 12:01:11 +00:00
|
|
|
void CChat::ConShowChat(IConsole::IResult *pResult, void *pUserData)
|
|
|
|
{
|
|
|
|
((CChat *)pUserData)->m_Show = pResult->GetInteger(0) != 0;
|
|
|
|
}
|
|
|
|
|
2017-01-05 23:17:41 +00:00
|
|
|
void CChat::ConEcho(IConsole::IResult *pResult, void *pUserData)
|
|
|
|
{
|
2019-06-05 17:17:55 +00:00
|
|
|
((CChat *)pUserData)->Echo(pResult->GetString(0));
|
|
|
|
}
|
|
|
|
|
2020-10-13 20:08:52 +00:00
|
|
|
void CChat::ConchainChatTee(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
|
|
|
|
{
|
|
|
|
pfnCallback(pResult, pCallbackUserData);
|
|
|
|
((CChat *)pUserData)->RebuildChat();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CChat::ConchainChatBackground(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
|
|
|
|
{
|
|
|
|
pfnCallback(pResult, pCallbackUserData);
|
|
|
|
((CChat *)pUserData)->RebuildChat();
|
|
|
|
}
|
|
|
|
|
2019-06-05 17:17:55 +00:00
|
|
|
void CChat::Echo(const char *pString)
|
|
|
|
{
|
|
|
|
AddLine(-2, 0, pString);
|
2017-01-05 23:17:41 +00:00
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
void CChat::OnConsoleInit()
|
2008-08-27 19:41:02 +00:00
|
|
|
{
|
2015-12-28 15:14:52 +00:00
|
|
|
Console()->Register("say", "r[message]", CFGFLAG_CLIENT, ConSay, this, "Say in chat");
|
|
|
|
Console()->Register("say_team", "r[message]", CFGFLAG_CLIENT, ConSayTeam, this, "Say in team chat");
|
|
|
|
Console()->Register("chat", "s['team'|'all'] ?r[message]", CFGFLAG_CLIENT, ConChat, this, "Enable chat with all/team mode");
|
2010-05-30 12:01:11 +00:00
|
|
|
Console()->Register("+show_chat", "", CFGFLAG_CLIENT, ConShowChat, this, "Show chat");
|
2017-01-05 23:17:41 +00:00
|
|
|
Console()->Register("echo", "r[message]", CFGFLAG_CLIENT, ConEcho, this, "Echo the text in chat window");
|
2020-10-13 20:08:52 +00:00
|
|
|
Console()->Chain("cl_chat_tee", ConchainChatTee, this);
|
|
|
|
Console()->Chain("cl_chat_background", ConchainChatBackground, this);
|
2008-08-27 19:41:02 +00:00
|
|
|
}
|
|
|
|
|
2011-03-20 15:36:10 +00:00
|
|
|
bool CChat::OnInput(IInput::CEvent Event)
|
2008-08-27 15:48:50 +00:00
|
|
|
{
|
2010-05-29 07:25:38 +00:00
|
|
|
if(m_Mode == MODE_NONE)
|
2008-08-27 15:48:50 +00:00
|
|
|
return false;
|
|
|
|
|
2016-04-30 18:11:26 +00:00
|
|
|
if(Input()->KeyIsPressed(KEY_LCTRL) && Input()->KeyPress(KEY_V))
|
2015-08-25 12:24:46 +00:00
|
|
|
{
|
2015-08-26 14:40:59 +00:00
|
|
|
const char *Text = Input()->GetClipboardText();
|
|
|
|
if(Text)
|
2015-08-25 12:24:46 +00:00
|
|
|
{
|
2015-08-26 14:40:59 +00:00
|
|
|
// if the text has more than one line, we send all lines except the last one
|
|
|
|
// the last one is set as in the text field
|
|
|
|
char Line[256];
|
|
|
|
int i, Begin = 0;
|
|
|
|
for(i = 0; i < str_length(Text); i++)
|
2015-08-25 12:24:46 +00:00
|
|
|
{
|
2015-08-26 14:40:59 +00:00
|
|
|
if(Text[i] == '\n')
|
|
|
|
{
|
2019-04-26 19:36:49 +00:00
|
|
|
int max = minimum(i - Begin + 1, (int)sizeof(Line));
|
2020-09-06 19:54:57 +00:00
|
|
|
str_utf8_copy(Line, Text + Begin, max);
|
2020-09-22 16:02:03 +00:00
|
|
|
Begin = i + 1;
|
2015-08-26 14:40:59 +00:00
|
|
|
SayChat(Line);
|
2020-09-22 16:02:03 +00:00
|
|
|
while(Text[i] == '\n')
|
|
|
|
i++;
|
2015-08-26 14:40:59 +00:00
|
|
|
}
|
2015-08-25 12:24:46 +00:00
|
|
|
}
|
2019-04-26 19:36:49 +00:00
|
|
|
int max = minimum(i - Begin + 1, (int)sizeof(Line));
|
2020-09-06 19:54:57 +00:00
|
|
|
str_utf8_copy(Line, Text + Begin, max);
|
2015-08-26 14:40:59 +00:00
|
|
|
m_Input.Add(Line);
|
2015-08-25 12:24:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-30 18:11:26 +00:00
|
|
|
if(Input()->KeyIsPressed(KEY_LCTRL) && Input()->KeyPress(KEY_C))
|
2015-08-25 12:24:46 +00:00
|
|
|
{
|
|
|
|
Input()->SetClipboardText(m_Input.GetString());
|
|
|
|
}
|
|
|
|
|
2016-05-06 17:48:36 +00:00
|
|
|
if(Input()->KeyIsPressed(KEY_LCTRL)) // jump to spaces and special ASCII characters
|
2016-05-06 15:16:31 +00:00
|
|
|
{
|
|
|
|
int SearchDirection = 0;
|
2017-02-14 16:00:50 +00:00
|
|
|
if(Input()->KeyPress(KEY_LEFT) || Input()->KeyPress(KEY_BACKSPACE))
|
2016-05-06 15:16:31 +00:00
|
|
|
SearchDirection = -1;
|
2018-02-22 19:09:24 +00:00
|
|
|
else if(Input()->KeyPress(KEY_RIGHT) || Input()->KeyPress(KEY_DELETE))
|
2016-05-06 15:16:31 +00:00
|
|
|
SearchDirection = 1;
|
|
|
|
|
|
|
|
if(SearchDirection != 0)
|
|
|
|
{
|
2018-02-22 19:46:02 +00:00
|
|
|
int OldOffset = m_Input.GetCursorOffset();
|
|
|
|
|
2017-02-14 16:00:50 +00:00
|
|
|
int FoundAt = SearchDirection > 0 ? m_Input.GetLength() - 1 : 0;
|
|
|
|
for(int i = m_Input.GetCursorOffset() + SearchDirection; SearchDirection > 0 ? i < m_Input.GetLength() - 1 : i > 0; i += SearchDirection)
|
2016-05-06 15:16:31 +00:00
|
|
|
{
|
2017-02-18 08:02:29 +00:00
|
|
|
int Next = i + SearchDirection;
|
2020-09-22 16:02:03 +00:00
|
|
|
if((m_Input.GetString()[Next] == ' ') ||
|
2017-02-18 08:02:29 +00:00
|
|
|
(m_Input.GetString()[Next] >= 32 && m_Input.GetString()[Next] <= 47) ||
|
|
|
|
(m_Input.GetString()[Next] >= 58 && m_Input.GetString()[Next] <= 64) ||
|
|
|
|
(m_Input.GetString()[Next] >= 91 && m_Input.GetString()[Next] <= 96))
|
2016-05-06 15:16:31 +00:00
|
|
|
{
|
|
|
|
FoundAt = i;
|
2018-03-13 20:50:49 +00:00
|
|
|
if(SearchDirection < 0)
|
2016-05-06 23:01:56 +00:00
|
|
|
FoundAt++;
|
2016-05-06 15:16:31 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-02-22 19:46:02 +00:00
|
|
|
|
2017-02-14 16:00:50 +00:00
|
|
|
if(Input()->KeyPress(KEY_BACKSPACE))
|
|
|
|
{
|
|
|
|
if(m_Input.GetCursorOffset() != 0)
|
|
|
|
{
|
2017-02-18 08:02:29 +00:00
|
|
|
char aText[512];
|
|
|
|
str_copy(aText, m_Input.GetString(), FoundAt + 1);
|
2017-02-14 16:06:51 +00:00
|
|
|
|
2017-02-14 16:00:50 +00:00
|
|
|
if(m_Input.GetCursorOffset() != str_length(m_Input.GetString()))
|
2017-02-18 08:02:29 +00:00
|
|
|
str_append(aText, m_Input.GetString() + m_Input.GetCursorOffset(), str_length(m_Input.GetString()));
|
2017-02-14 16:06:51 +00:00
|
|
|
|
2017-02-18 08:02:29 +00:00
|
|
|
m_Input.Set(aText);
|
2017-02-14 16:00:50 +00:00
|
|
|
}
|
|
|
|
}
|
2018-02-22 19:09:24 +00:00
|
|
|
else if(Input()->KeyPress(KEY_DELETE))
|
|
|
|
{
|
|
|
|
if(m_Input.GetCursorOffset() != m_Input.GetLength())
|
|
|
|
{
|
|
|
|
char aText[512];
|
|
|
|
aText[0] = '\0';
|
|
|
|
|
|
|
|
str_copy(aText, m_Input.GetString(), m_Input.GetCursorOffset() + 1);
|
|
|
|
|
|
|
|
if(FoundAt != m_Input.GetLength())
|
|
|
|
str_append(aText, m_Input.GetString() + FoundAt, sizeof(aText));
|
2019-04-05 23:15:02 +00:00
|
|
|
|
2018-02-22 19:09:24 +00:00
|
|
|
m_Input.Set(aText);
|
2018-02-22 19:46:02 +00:00
|
|
|
FoundAt = OldOffset;
|
2018-02-22 19:09:24 +00:00
|
|
|
}
|
|
|
|
}
|
2018-02-22 19:46:02 +00:00
|
|
|
m_Input.SetCursorOffset(FoundAt);
|
2016-05-06 15:16:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-22 16:02:03 +00:00
|
|
|
if(Event.m_Flags & IInput::FLAG_PRESS && Event.m_Key == KEY_ESCAPE)
|
2010-09-12 10:43:03 +00:00
|
|
|
{
|
2020-09-22 16:01:13 +00:00
|
|
|
DisableMode();
|
2010-09-12 10:43:03 +00:00
|
|
|
m_pClient->OnRelease();
|
2015-08-12 15:03:27 +00:00
|
|
|
if(g_Config.m_ClChatReset)
|
|
|
|
m_Input.Clear();
|
2010-09-12 10:43:03 +00:00
|
|
|
}
|
2020-09-22 16:02:03 +00:00
|
|
|
else if(Event.m_Flags & IInput::FLAG_PRESS && (Event.m_Key == KEY_RETURN || Event.m_Key == KEY_KP_ENTER))
|
2008-08-27 15:48:50 +00:00
|
|
|
{
|
2010-05-29 07:25:38 +00:00
|
|
|
if(m_Input.GetString()[0])
|
2011-03-20 15:17:06 +00:00
|
|
|
{
|
2012-04-19 23:09:49 +00:00
|
|
|
bool AddEntry = false;
|
|
|
|
|
2020-09-22 16:02:03 +00:00
|
|
|
if(m_LastChatSend + time_freq() < time())
|
2012-04-19 23:09:49 +00:00
|
|
|
{
|
2012-01-09 22:13:51 +00:00
|
|
|
Say(m_Mode == MODE_ALL ? 0 : 1, m_Input.GetString());
|
2012-04-19 23:09:49 +00:00
|
|
|
AddEntry = true;
|
|
|
|
}
|
|
|
|
else if(m_PendingChatCounter < 3)
|
|
|
|
{
|
2012-01-09 22:13:51 +00:00
|
|
|
++m_PendingChatCounter;
|
2012-04-19 23:09:49 +00:00
|
|
|
AddEntry = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(AddEntry)
|
|
|
|
{
|
2020-09-22 16:02:03 +00:00
|
|
|
CHistoryEntry *pEntry = m_History.Allocate(sizeof(CHistoryEntry) + m_Input.GetLength());
|
2012-04-19 23:09:49 +00:00
|
|
|
pEntry->m_Team = m_Mode == MODE_ALL ? 0 : 1;
|
2020-09-22 16:02:03 +00:00
|
|
|
mem_copy(pEntry->m_aText, m_Input.GetString(), m_Input.GetLength() + 1);
|
2012-04-19 23:09:49 +00:00
|
|
|
}
|
2011-03-20 15:17:06 +00:00
|
|
|
}
|
|
|
|
m_pHistoryEntry = 0x0;
|
2020-09-22 16:01:13 +00:00
|
|
|
DisableMode();
|
2010-09-12 10:43:03 +00:00
|
|
|
m_pClient->OnRelease();
|
2015-08-12 15:03:27 +00:00
|
|
|
m_Input.Clear();
|
2008-08-27 15:48:50 +00:00
|
|
|
}
|
2020-09-22 16:02:03 +00:00
|
|
|
if(Event.m_Flags & IInput::FLAG_PRESS && Event.m_Key == KEY_TAB)
|
2010-12-16 01:31:12 +00:00
|
|
|
{
|
|
|
|
// fill the completion buffer
|
|
|
|
if(m_CompletionChosen < 0)
|
|
|
|
{
|
2020-09-22 16:02:03 +00:00
|
|
|
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();
|
2010-12-16 01:31:12 +00:00
|
|
|
|
|
|
|
for(m_PlaceholderLength = 0; *pCursor && *pCursor != ' '; ++pCursor)
|
|
|
|
++m_PlaceholderLength;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2020-10-26 11:56:21 +00:00
|
|
|
str_truncate(m_aCompletionBuffer, sizeof(m_aCompletionBuffer), m_Input.GetString() + m_PlaceholderOffset, m_PlaceholderLength);
|
2010-12-16 01:31:12 +00:00
|
|
|
}
|
|
|
|
|
2019-03-19 09:26:30 +00:00
|
|
|
if(m_aCompletionBuffer[0] == '/')
|
|
|
|
{
|
|
|
|
CCommand *pCompletionCommand = 0;
|
|
|
|
|
|
|
|
const size_t NumCommands = m_Commands.size();
|
2014-10-06 11:02:55 +00:00
|
|
|
|
|
|
|
if(m_ReverseTAB)
|
2020-09-22 16:02:03 +00:00
|
|
|
m_CompletionChosen = (m_CompletionChosen - 1 + 2 * NumCommands) % (2 * NumCommands);
|
2014-10-06 11:02:55 +00:00
|
|
|
else
|
2020-09-22 16:02:03 +00:00
|
|
|
m_CompletionChosen = (m_CompletionChosen + 1) % (2 * NumCommands);
|
2014-10-06 11:02:55 +00:00
|
|
|
|
2020-09-22 16:02:03 +00:00
|
|
|
const char *pCommandStart = m_aCompletionBuffer + 1;
|
|
|
|
for(size_t i = 0; i < 2 * NumCommands; ++i)
|
2019-03-19 09:26:30 +00:00
|
|
|
{
|
|
|
|
int SearchType;
|
|
|
|
int Index;
|
2014-10-06 11:02:55 +00:00
|
|
|
|
2019-03-19 09:26:30 +00:00
|
|
|
if(m_ReverseTAB)
|
|
|
|
{
|
2020-09-22 16:02:03 +00:00
|
|
|
SearchType = ((m_CompletionChosen - i + 2 * NumCommands) % (2 * NumCommands)) / NumCommands;
|
|
|
|
Index = (m_CompletionChosen - i + NumCommands) % NumCommands;
|
2019-03-19 09:26:30 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-09-22 16:02:03 +00:00
|
|
|
SearchType = ((m_CompletionChosen + i) % (2 * NumCommands)) / NumCommands;
|
|
|
|
Index = (m_CompletionChosen + i) % NumCommands;
|
2019-03-19 09:26:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
auto &Command = m_Commands[Index];
|
|
|
|
|
|
|
|
if(str_comp_nocase_num(Command.pName, pCommandStart, str_length(pCommandStart)) == 0)
|
|
|
|
{
|
|
|
|
pCompletionCommand = &Command;
|
2020-09-22 16:02:03 +00:00
|
|
|
m_CompletionChosen = Index + SearchType * NumCommands;
|
2019-03-19 09:26:30 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// insert the command
|
|
|
|
if(pCompletionCommand)
|
2014-10-06 11:02:55 +00:00
|
|
|
{
|
2019-03-19 09:26:30 +00:00
|
|
|
char aBuf[256];
|
|
|
|
// add part before the name
|
2020-10-26 11:56:21 +00:00
|
|
|
str_truncate(aBuf, sizeof(aBuf), m_Input.GetString(), m_PlaceholderOffset);
|
2019-03-19 09:26:30 +00:00
|
|
|
|
|
|
|
// add the command
|
|
|
|
str_append(aBuf, "/", sizeof(aBuf));
|
|
|
|
str_append(aBuf, pCompletionCommand->pName, sizeof(aBuf));
|
|
|
|
|
|
|
|
// add separator
|
|
|
|
const char *pSeparator = pCompletionCommand->pParams[0] == '\0' ? "" : " ";
|
|
|
|
str_append(aBuf, pSeparator, sizeof(aBuf));
|
|
|
|
if(*pSeparator)
|
|
|
|
str_append(aBuf, pSeparator, sizeof(aBuf));
|
|
|
|
|
|
|
|
// add part after the name
|
2020-09-22 16:02:03 +00:00
|
|
|
str_append(aBuf, m_Input.GetString() + m_PlaceholderOffset + m_PlaceholderLength, sizeof(aBuf));
|
2019-03-19 09:26:30 +00:00
|
|
|
|
2020-09-22 16:02:03 +00:00
|
|
|
m_PlaceholderLength = str_length(pSeparator) + str_length(pCompletionCommand->pName) + 1;
|
2019-03-19 09:26:30 +00:00
|
|
|
m_OldChatStringLength = m_Input.GetLength();
|
|
|
|
m_Input.Set(aBuf); // TODO: Use Add instead
|
2020-09-22 16:02:03 +00:00
|
|
|
m_Input.SetCursorOffset(m_PlaceholderOffset + m_PlaceholderLength);
|
2019-03-19 09:26:30 +00:00
|
|
|
m_InputUpdate = true;
|
2014-10-06 11:02:55 +00:00
|
|
|
}
|
2019-03-19 09:26:30 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// find next possible name
|
|
|
|
const char *pCompletionString = 0;
|
|
|
|
|
|
|
|
if(m_ReverseTAB)
|
2020-09-22 16:02:03 +00:00
|
|
|
m_CompletionChosen = (m_CompletionChosen - 1 + 2 * MAX_CLIENTS) % (2 * MAX_CLIENTS);
|
2014-10-06 11:02:55 +00:00
|
|
|
else
|
2020-09-22 16:02:03 +00:00
|
|
|
m_CompletionChosen = (m_CompletionChosen + 1) % (2 * MAX_CLIENTS);
|
2019-03-19 09:26:30 +00:00
|
|
|
|
2020-09-22 16:02:03 +00:00
|
|
|
for(int i = 0; i < 2 * MAX_CLIENTS; ++i)
|
2014-10-06 11:02:55 +00:00
|
|
|
{
|
2019-03-19 09:26:30 +00:00
|
|
|
int SearchType;
|
|
|
|
int Index;
|
2014-10-06 11:02:55 +00:00
|
|
|
|
2019-03-19 09:26:30 +00:00
|
|
|
if(m_ReverseTAB)
|
|
|
|
{
|
2020-09-22 16:02:03 +00:00
|
|
|
SearchType = ((m_CompletionChosen - i + 2 * MAX_CLIENTS) % (2 * MAX_CLIENTS)) / MAX_CLIENTS;
|
|
|
|
Index = (m_CompletionChosen - i + MAX_CLIENTS) % MAX_CLIENTS;
|
2019-03-19 09:26:30 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-09-22 16:02:03 +00:00
|
|
|
SearchType = ((m_CompletionChosen + i) % (2 * MAX_CLIENTS)) / MAX_CLIENTS;
|
|
|
|
Index = (m_CompletionChosen + i) % MAX_CLIENTS;
|
2019-03-19 09:26:30 +00:00
|
|
|
}
|
2014-10-06 11:02:55 +00:00
|
|
|
|
2019-03-19 09:26:30 +00:00
|
|
|
if(!m_pClient->m_Snap.m_paInfoByName[Index])
|
|
|
|
continue;
|
2014-10-06 11:06:35 +00:00
|
|
|
|
2019-03-19 09:26:30 +00:00
|
|
|
int Index2 = m_pClient->m_Snap.m_paInfoByName[Index]->m_ClientID;
|
|
|
|
|
|
|
|
bool Found = false;
|
|
|
|
if(SearchType == 1)
|
|
|
|
{
|
|
|
|
if(str_utf8_comp_nocase_num(m_pClient->m_aClients[Index2].m_aName, m_aCompletionBuffer, str_length(m_aCompletionBuffer)) &&
|
|
|
|
str_utf8_find_nocase(m_pClient->m_aClients[Index2].m_aName, m_aCompletionBuffer))
|
|
|
|
Found = true;
|
|
|
|
}
|
|
|
|
else if(!str_utf8_comp_nocase_num(m_pClient->m_aClients[Index2].m_aName, m_aCompletionBuffer, str_length(m_aCompletionBuffer)))
|
2011-06-09 21:28:20 +00:00
|
|
|
Found = true;
|
2019-03-19 09:26:30 +00:00
|
|
|
|
|
|
|
if(Found)
|
|
|
|
{
|
|
|
|
pCompletionString = m_pClient->m_aClients[Index2].m_aName;
|
2020-09-22 16:02:03 +00:00
|
|
|
m_CompletionChosen = Index + SearchType * MAX_CLIENTS;
|
2019-03-19 09:26:30 +00:00
|
|
|
break;
|
|
|
|
}
|
2011-06-09 21:28:20 +00:00
|
|
|
}
|
|
|
|
|
2019-03-19 09:26:30 +00:00
|
|
|
// insert the name
|
|
|
|
if(pCompletionString)
|
2010-12-16 01:31:12 +00:00
|
|
|
{
|
2019-03-19 09:26:30 +00:00
|
|
|
char aBuf[256];
|
|
|
|
// add part before the name
|
2020-10-26 11:56:21 +00:00
|
|
|
str_truncate(aBuf, sizeof(aBuf), m_Input.GetString(), m_PlaceholderOffset);
|
2019-03-19 09:26:30 +00:00
|
|
|
|
|
|
|
// add the name
|
|
|
|
str_append(aBuf, pCompletionString, sizeof(aBuf));
|
|
|
|
|
|
|
|
// add separator
|
|
|
|
const char *pSeparator = "";
|
2020-09-22 16:02:03 +00:00
|
|
|
if(*(m_Input.GetString() + m_PlaceholderOffset + m_PlaceholderLength) != ' ')
|
2019-03-19 09:26:30 +00:00
|
|
|
pSeparator = m_PlaceholderOffset == 0 ? ": " : " ";
|
|
|
|
else if(m_PlaceholderOffset == 0)
|
|
|
|
pSeparator = ":";
|
|
|
|
if(*pSeparator)
|
|
|
|
str_append(aBuf, pSeparator, sizeof(aBuf));
|
|
|
|
|
|
|
|
// add part after the name
|
2020-09-22 16:02:03 +00:00
|
|
|
str_append(aBuf, m_Input.GetString() + m_PlaceholderOffset + m_PlaceholderLength, sizeof(aBuf));
|
2019-03-19 09:26:30 +00:00
|
|
|
|
2020-09-22 16:02:03 +00:00
|
|
|
m_PlaceholderLength = str_length(pSeparator) + str_length(pCompletionString);
|
2019-03-19 09:26:30 +00:00
|
|
|
m_OldChatStringLength = m_Input.GetLength();
|
|
|
|
m_Input.Set(aBuf); // TODO: Use Add instead
|
2020-09-22 16:02:03 +00:00
|
|
|
m_Input.SetCursorOffset(m_PlaceholderOffset + m_PlaceholderLength);
|
2019-03-19 09:26:30 +00:00
|
|
|
m_InputUpdate = true;
|
2010-12-16 01:31:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-08-27 15:48:50 +00:00
|
|
|
else
|
2010-10-11 00:29:30 +00:00
|
|
|
{
|
2010-12-16 01:31:12 +00:00
|
|
|
// reset name completion process
|
2020-09-22 16:02:03 +00:00
|
|
|
if(Event.m_Flags & IInput::FLAG_PRESS && Event.m_Key != KEY_TAB)
|
2014-10-06 11:02:55 +00:00
|
|
|
if(Event.m_Key != KEY_LSHIFT)
|
|
|
|
m_CompletionChosen = -1;
|
2010-12-16 01:31:12 +00:00
|
|
|
|
2010-10-11 10:31:45 +00:00
|
|
|
m_OldChatStringLength = m_Input.GetLength();
|
2011-03-20 15:36:10 +00:00
|
|
|
m_Input.ProcessInput(Event);
|
2010-10-11 00:29:30 +00:00
|
|
|
m_InputUpdate = true;
|
|
|
|
}
|
2020-09-22 16:02:03 +00:00
|
|
|
if(Event.m_Flags & IInput::FLAG_PRESS && Event.m_Key == KEY_LSHIFT)
|
2014-10-06 11:02:55 +00:00
|
|
|
{
|
|
|
|
m_ReverseTAB = true;
|
|
|
|
}
|
2020-09-22 16:02:03 +00:00
|
|
|
else if(Event.m_Flags & IInput::FLAG_RELEASE && Event.m_Key == KEY_LSHIFT)
|
2014-10-06 11:02:55 +00:00
|
|
|
{
|
|
|
|
m_ReverseTAB = false;
|
|
|
|
}
|
2020-09-22 16:02:03 +00:00
|
|
|
if(Event.m_Flags & IInput::FLAG_PRESS && Event.m_Key == KEY_UP)
|
2011-03-20 15:17:06 +00:00
|
|
|
{
|
2012-01-09 22:13:51 +00:00
|
|
|
if(m_pHistoryEntry)
|
2011-03-20 15:17:06 +00:00
|
|
|
{
|
2012-01-09 22:13:51 +00:00
|
|
|
CHistoryEntry *pTest = m_History.Prev(m_pHistoryEntry);
|
2011-03-20 15:17:06 +00:00
|
|
|
|
2012-01-09 22:13:51 +00:00
|
|
|
if(pTest)
|
2011-03-20 15:17:06 +00:00
|
|
|
m_pHistoryEntry = pTest;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
m_pHistoryEntry = m_History.Last();
|
|
|
|
|
2012-01-09 22:13:51 +00:00
|
|
|
if(m_pHistoryEntry)
|
|
|
|
m_Input.Set(m_pHistoryEntry->m_aText);
|
2011-03-20 15:17:06 +00:00
|
|
|
}
|
2020-09-22 16:02:03 +00:00
|
|
|
else if(Event.m_Flags & IInput::FLAG_PRESS && Event.m_Key == KEY_DOWN)
|
2011-03-20 15:17:06 +00:00
|
|
|
{
|
2012-01-09 22:13:51 +00:00
|
|
|
if(m_pHistoryEntry)
|
2011-03-20 15:17:06 +00:00
|
|
|
m_pHistoryEntry = m_History.Next(m_pHistoryEntry);
|
|
|
|
|
2018-03-13 20:50:49 +00:00
|
|
|
if(m_pHistoryEntry)
|
2012-01-09 22:13:51 +00:00
|
|
|
m_Input.Set(m_pHistoryEntry->m_aText);
|
2011-03-20 15:17:06 +00:00
|
|
|
else
|
|
|
|
m_Input.Clear();
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2008-08-27 15:48:50 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
void CChat::EnableMode(int Team)
|
2008-08-27 15:48:50 +00:00
|
|
|
{
|
2010-09-07 18:01:51 +00:00
|
|
|
if(Client()->State() == IClient::STATE_DEMOPLAYBACK)
|
|
|
|
return;
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
if(m_Mode == MODE_NONE)
|
2008-08-27 15:48:50 +00:00
|
|
|
{
|
2010-05-29 07:25:38 +00:00
|
|
|
if(Team)
|
|
|
|
m_Mode = MODE_TEAM;
|
2008-08-27 15:48:50 +00:00
|
|
|
else
|
2010-05-29 07:25:38 +00:00
|
|
|
m_Mode = MODE_ALL;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2016-09-01 13:12:13 +00:00
|
|
|
Input()->SetIMEState(true);
|
2016-04-30 18:11:26 +00:00
|
|
|
Input()->Clear();
|
2010-12-16 01:31:12 +00:00
|
|
|
m_CompletionChosen = -1;
|
2008-08-27 15:48:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-22 16:01:13 +00:00
|
|
|
void CChat::DisableMode()
|
|
|
|
{
|
|
|
|
if(m_Mode != MODE_NONE)
|
|
|
|
{
|
|
|
|
Input()->SetIMEState(false);
|
|
|
|
m_Mode = MODE_NONE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
void CChat::OnMessage(int MsgType, void *pRawMsg)
|
2008-08-27 15:48:50 +00:00
|
|
|
{
|
2010-05-29 07:25:38 +00:00
|
|
|
if(MsgType == NETMSGTYPE_SV_CHAT)
|
2008-08-27 15:48:50 +00:00
|
|
|
{
|
2010-05-29 07:25:38 +00:00
|
|
|
CNetMsg_Sv_Chat *pMsg = (CNetMsg_Sv_Chat *)pRawMsg;
|
2011-02-12 10:40:36 +00:00
|
|
|
AddLine(pMsg->m_ClientID, pMsg->m_Team, pMsg->m_pMessage);
|
2008-08-27 15:48:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-22 13:37:00 +00:00
|
|
|
bool CChat::LineShouldHighlight(const char *pLine, const char *pName)
|
|
|
|
{
|
|
|
|
const char *pHL = str_find_nocase(pLine, pName);
|
|
|
|
|
2018-03-13 20:50:49 +00:00
|
|
|
if(pHL)
|
2015-07-22 13:37:00 +00:00
|
|
|
{
|
2015-08-17 19:35:12 +00:00
|
|
|
int Length = str_length(pName);
|
2015-07-22 13:37:00 +00:00
|
|
|
|
|
|
|
if((pLine == pHL || pHL[-1] == ' ') && (pHL[Length] == 0 || pHL[Length] == ' ' || pHL[Length] == '.' || pHL[Length] == '!' || pHL[Length] == ',' || pHL[Length] == '?' || pHL[Length] == ':'))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-06-20 13:55:52 +00:00
|
|
|
#define SAVES_FILE "ddnet-saves.txt"
|
2020-06-22 20:31:41 +00:00
|
|
|
const char *SAVES_HEADER[] = {
|
|
|
|
"Time",
|
|
|
|
"Player",
|
|
|
|
"Map",
|
|
|
|
"Code",
|
|
|
|
};
|
2020-06-20 13:55:52 +00:00
|
|
|
|
|
|
|
void CChat::StoreSave(const char *pText)
|
|
|
|
{
|
|
|
|
const char *pStart = str_find(pText, "Team successfully saved by ");
|
|
|
|
const char *pMid = str_find(pText, ". Use '/load ");
|
2020-06-25 20:30:51 +00:00
|
|
|
const char *pOn = str_find(pText, "' on ");
|
|
|
|
const char *pEnd = str_find(pText, pOn ? " to continue" : "' to continue");
|
2020-06-20 13:55:52 +00:00
|
|
|
|
2020-06-25 20:30:51 +00:00
|
|
|
if(!pStart || !pMid || !pEnd || pMid < pStart || pEnd < pMid || (pOn && (pOn < pMid || pEnd < pOn)))
|
2020-06-20 13:55:52 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
char aName[16];
|
2020-10-26 11:56:21 +00:00
|
|
|
str_truncate(aName, sizeof(aName), pStart + 27, pMid - pStart - 27);
|
2020-06-20 13:55:52 +00:00
|
|
|
|
|
|
|
char aSaveCode[64];
|
2020-06-25 20:30:51 +00:00
|
|
|
|
2020-10-26 11:56:21 +00:00
|
|
|
str_truncate(aSaveCode, sizeof(aSaveCode), pMid + 13, (pOn ? pOn : pEnd) - pMid - 13);
|
2020-06-20 13:55:52 +00:00
|
|
|
|
|
|
|
char aTimestamp[20];
|
|
|
|
str_timestamp_format(aTimestamp, sizeof(aTimestamp), FORMAT_SPACE);
|
|
|
|
|
|
|
|
// TODO: Find a simple way to get the names of team members. This doesn't
|
|
|
|
// work since team is killed first, then save message gets sent:
|
|
|
|
/*
|
|
|
|
for(int i = 0; i < MAX_CLIENTS; i++)
|
|
|
|
{
|
|
|
|
const CNetObj_PlayerInfo *pInfo = GameClient()->m_Snap.m_paInfoByDDTeam[i];
|
|
|
|
if(!pInfo)
|
|
|
|
continue;
|
|
|
|
pInfo->m_Team // All 0
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
2020-06-22 20:31:41 +00:00
|
|
|
IOHANDLE File = Storage()->OpenFile(SAVES_FILE, IOFLAG_APPEND, IStorage::TYPE_SAVE);
|
2020-06-20 13:55:52 +00:00
|
|
|
if(!File)
|
|
|
|
return;
|
|
|
|
|
2020-06-22 20:31:41 +00:00
|
|
|
const char *apColumns[4] = {
|
|
|
|
aTimestamp,
|
|
|
|
aName,
|
|
|
|
Client()->GetCurrentMap(),
|
|
|
|
aSaveCode,
|
|
|
|
};
|
|
|
|
|
|
|
|
if(io_tell(File) == 0)
|
2020-06-20 13:55:52 +00:00
|
|
|
{
|
2020-06-22 20:31:41 +00:00
|
|
|
CsvWrite(File, 4, SAVES_HEADER);
|
2020-06-20 13:55:52 +00:00
|
|
|
}
|
2020-06-22 20:31:41 +00:00
|
|
|
CsvWrite(File, 4, apColumns);
|
2020-06-20 13:55:52 +00:00
|
|
|
io_close(File);
|
|
|
|
}
|
|
|
|
|
2011-02-12 10:40:36 +00:00
|
|
|
void CChat::AddLine(int ClientID, int Team, const char *pLine)
|
2008-08-27 15:48:50 +00:00
|
|
|
{
|
2018-01-05 19:10:39 +00:00
|
|
|
if(*pLine == 0 ||
|
|
|
|
(ClientID == -1 && !g_Config.m_ClShowChatSystem) ||
|
|
|
|
(ClientID >= 0 && (m_pClient->m_aClients[ClientID].m_aName[0] == '\0' || // unknown client
|
2020-09-22 16:02:03 +00:00
|
|
|
m_pClient->m_aClients[ClientID].m_ChatIgnore ||
|
|
|
|
(m_pClient->m_Snap.m_LocalClientID != ClientID && g_Config.m_ClShowChatFriends && !m_pClient->m_aClients[ClientID].m_Friend) ||
|
|
|
|
(m_pClient->m_Snap.m_LocalClientID != ClientID && m_pClient->m_aClients[ClientID].m_Foe))))
|
2010-09-06 10:32:41 +00:00
|
|
|
return;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2013-12-27 03:09:45 +00:00
|
|
|
// trim right and set maximum length to 256 utf8-characters
|
2013-04-01 18:30:58 +00:00
|
|
|
int Length = 0;
|
|
|
|
const char *pStr = pLine;
|
|
|
|
const char *pEnd = 0;
|
|
|
|
while(*pStr)
|
2015-07-09 00:08:14 +00:00
|
|
|
{
|
2013-04-01 18:30:58 +00:00
|
|
|
const char *pStrOld = pStr;
|
|
|
|
int Code = str_utf8_decode(&pStr);
|
|
|
|
|
|
|
|
// check if unicode is not empty
|
2018-03-14 01:27:15 +00:00
|
|
|
if(!str_utf8_isspace(Code))
|
2013-04-01 18:30:58 +00:00
|
|
|
{
|
|
|
|
pEnd = 0;
|
|
|
|
}
|
|
|
|
else if(pEnd == 0)
|
|
|
|
pEnd = pStrOld;
|
|
|
|
|
2013-12-27 03:09:45 +00:00
|
|
|
if(++Length >= 256)
|
2013-04-01 18:30:58 +00:00
|
|
|
{
|
|
|
|
*(const_cast<char *>(pStr)) = 0;
|
|
|
|
break;
|
|
|
|
}
|
2015-07-09 00:08:14 +00:00
|
|
|
}
|
2013-04-01 18:30:58 +00:00
|
|
|
if(pEnd != 0)
|
|
|
|
*(const_cast<char *>(pEnd)) = 0;
|
|
|
|
|
2011-03-20 14:58:59 +00:00
|
|
|
bool Highlighted = false;
|
2020-09-22 16:02:03 +00:00
|
|
|
char *p = const_cast<char *>(pLine);
|
2020-05-06 06:13:29 +00:00
|
|
|
|
|
|
|
// Only empty string left
|
|
|
|
if(*p == 0)
|
|
|
|
return;
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
while(*p)
|
2008-08-27 15:48:50 +00:00
|
|
|
{
|
2011-06-09 21:49:06 +00:00
|
|
|
Highlighted = false;
|
2010-05-29 07:25:38 +00:00
|
|
|
pLine = p;
|
2018-07-10 09:29:02 +00:00
|
|
|
// find line separator and strip multiline
|
2010-05-29 07:25:38 +00:00
|
|
|
while(*p)
|
|
|
|
{
|
|
|
|
if(*p++ == '\n')
|
|
|
|
{
|
2020-09-22 16:02:03 +00:00
|
|
|
*(p - 1) = 0;
|
2010-05-29 07:25:38 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2008-08-27 15:48:50 +00:00
|
|
|
|
2020-09-20 15:25:27 +00:00
|
|
|
CLine *pCurrentLine = &m_aLines[m_CurrentLine];
|
|
|
|
|
|
|
|
// If it's a client message, m_aText will have ": " prepended so we have to work around it.
|
2020-10-13 20:08:52 +00:00
|
|
|
if(pCurrentLine->m_Team == Team && pCurrentLine->m_ClientID == ClientID && str_comp(pCurrentLine->m_aText, pLine) == 0)
|
2020-09-20 15:25:27 +00:00
|
|
|
{
|
|
|
|
pCurrentLine->m_TimesRepeated++;
|
|
|
|
if(pCurrentLine->m_TextContainerIndex != -1)
|
|
|
|
TextRender()->DeleteTextContainer(pCurrentLine->m_TextContainerIndex);
|
|
|
|
pCurrentLine->m_TextContainerIndex = -1;
|
2020-10-13 20:08:52 +00:00
|
|
|
|
|
|
|
if(pCurrentLine->m_QuadContainerIndex != -1)
|
|
|
|
Graphics()->DeleteQuadContainer(pCurrentLine->m_QuadContainerIndex);
|
|
|
|
pCurrentLine->m_QuadContainerIndex = -1;
|
2020-09-20 15:25:27 +00:00
|
|
|
pCurrentLine->m_Time = time();
|
2020-11-01 03:48:10 +00:00
|
|
|
pCurrentLine->m_YOffset[0] = -1.f;
|
|
|
|
pCurrentLine->m_YOffset[1] = -1.f;
|
2020-09-20 15:25:27 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-09-22 16:02:03 +00:00
|
|
|
m_CurrentLine = (m_CurrentLine + 1) % MAX_LINES;
|
2011-08-11 08:59:14 +00:00
|
|
|
|
2020-09-20 15:25:27 +00:00
|
|
|
pCurrentLine = &m_aLines[m_CurrentLine];
|
2020-09-29 15:01:39 +00:00
|
|
|
pCurrentLine->m_TimesRepeated = 0;
|
2020-09-20 15:25:27 +00:00
|
|
|
pCurrentLine->m_Time = time();
|
2020-11-01 03:48:10 +00:00
|
|
|
pCurrentLine->m_YOffset[0] = -1.0f;
|
|
|
|
pCurrentLine->m_YOffset[1] = -1.0f;
|
2020-09-20 15:25:27 +00:00
|
|
|
pCurrentLine->m_ClientID = ClientID;
|
|
|
|
pCurrentLine->m_Team = Team;
|
|
|
|
pCurrentLine->m_NameColor = -2;
|
|
|
|
|
|
|
|
if(pCurrentLine->m_TextContainerIndex != -1)
|
|
|
|
TextRender()->DeleteTextContainer(pCurrentLine->m_TextContainerIndex);
|
|
|
|
pCurrentLine->m_TextContainerIndex = -1;
|
2018-03-13 20:50:49 +00:00
|
|
|
|
2020-10-13 20:08:52 +00:00
|
|
|
if(pCurrentLine->m_QuadContainerIndex != -1)
|
|
|
|
Graphics()->DeleteQuadContainer(pCurrentLine->m_QuadContainerIndex);
|
|
|
|
pCurrentLine->m_QuadContainerIndex = -1;
|
|
|
|
|
2011-06-09 21:49:06 +00:00
|
|
|
// check for highlighted name
|
2018-03-13 20:50:49 +00:00
|
|
|
if(Client()->State() != IClient::STATE_DEMOPLAYBACK)
|
2011-06-09 21:49:06 +00:00
|
|
|
{
|
2017-02-23 13:13:14 +00:00
|
|
|
if(ClientID != m_pClient->m_LocalIDs[0])
|
2015-08-20 14:01:34 +00:00
|
|
|
{
|
|
|
|
// main character
|
2018-03-13 20:50:49 +00:00
|
|
|
if(LineShouldHighlight(pLine, m_pClient->m_aClients[m_pClient->m_LocalIDs[0]].m_aName))
|
2015-08-20 14:01:34 +00:00
|
|
|
Highlighted = true;
|
|
|
|
// dummy
|
2017-02-23 13:13:14 +00:00
|
|
|
if(m_pClient->Client()->DummyConnected() && LineShouldHighlight(pLine, m_pClient->m_aClients[m_pClient->m_LocalIDs[1]].m_aName))
|
2015-08-20 14:01:34 +00:00
|
|
|
Highlighted = true;
|
|
|
|
}
|
2011-06-09 21:49:06 +00:00
|
|
|
}
|
2015-07-22 13:37:00 +00:00
|
|
|
else
|
2014-05-17 20:45:44 +00:00
|
|
|
{
|
2015-07-22 13:37:00 +00:00
|
|
|
// on demo playback use local id from snap directly,
|
|
|
|
// since m_LocalIDs isn't valid there
|
2018-03-13 20:50:49 +00:00
|
|
|
if(LineShouldHighlight(pLine, m_pClient->m_aClients[m_pClient->m_Snap.m_LocalClientID].m_aName))
|
2015-07-22 13:37:00 +00:00
|
|
|
Highlighted = true;
|
2014-05-17 20:45:44 +00:00
|
|
|
}
|
|
|
|
|
2020-09-20 15:25:27 +00:00
|
|
|
pCurrentLine->m_Highlighted = Highlighted;
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2017-01-05 23:17:41 +00:00
|
|
|
if(ClientID < 0) // server or client message
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2020-09-20 15:25:27 +00:00
|
|
|
str_copy(pCurrentLine->m_aName, "*** ", sizeof(pCurrentLine->m_aName));
|
|
|
|
str_format(pCurrentLine->m_aText, sizeof(pCurrentLine->m_aText), "%s", pLine);
|
2020-06-20 13:55:52 +00:00
|
|
|
|
|
|
|
if(Client()->State() != IClient::STATE_DEMOPLAYBACK)
|
2020-09-20 15:25:27 +00:00
|
|
|
StoreSave(pCurrentLine->m_aText);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
else
|
2008-08-27 15:48:50 +00:00
|
|
|
{
|
2011-02-12 10:40:36 +00:00
|
|
|
if(m_pClient->m_aClients[ClientID].m_Team == TEAM_SPECTATORS)
|
2020-09-20 15:25:27 +00:00
|
|
|
pCurrentLine->m_NameColor = TEAM_SPECTATORS;
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2020-09-22 16:02:03 +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
|
|
|
{
|
2011-02-12 10:40:36 +00:00
|
|
|
if(m_pClient->m_aClients[ClientID].m_Team == TEAM_RED)
|
2020-09-20 15:25:27 +00:00
|
|
|
pCurrentLine->m_NameColor = TEAM_RED;
|
2011-02-12 10:40:36 +00:00
|
|
|
else if(m_pClient->m_aClients[ClientID].m_Team == TEAM_BLUE)
|
2020-09-20 15:25:27 +00:00
|
|
|
pCurrentLine->m_NameColor = TEAM_BLUE;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2018-03-13 20:50:49 +00:00
|
|
|
if(Team == 2) // whisper send
|
2014-01-30 15:49:15 +00:00
|
|
|
{
|
2020-09-20 15:25:27 +00:00
|
|
|
str_format(pCurrentLine->m_aName, sizeof(pCurrentLine->m_aName), "→ %s", m_pClient->m_aClients[ClientID].m_aName);
|
|
|
|
pCurrentLine->m_NameColor = TEAM_BLUE;
|
|
|
|
pCurrentLine->m_Highlighted = false;
|
|
|
|
pCurrentLine->m_Team = 0;
|
2016-06-03 20:47:38 +00:00
|
|
|
Highlighted = false;
|
2014-01-30 15:49:15 +00:00
|
|
|
}
|
2018-03-13 20:50:49 +00:00
|
|
|
else if(Team == 3) // whisper recv
|
2014-01-30 15:49:15 +00:00
|
|
|
{
|
2020-09-20 15:25:27 +00:00
|
|
|
str_format(pCurrentLine->m_aName, sizeof(pCurrentLine->m_aName), "← %s", m_pClient->m_aClients[ClientID].m_aName);
|
|
|
|
pCurrentLine->m_NameColor = TEAM_RED;
|
|
|
|
pCurrentLine->m_Highlighted = true;
|
|
|
|
pCurrentLine->m_Team = 0;
|
2014-01-30 15:49:15 +00:00
|
|
|
Highlighted = true;
|
|
|
|
}
|
|
|
|
else
|
2020-10-13 20:08:52 +00:00
|
|
|
str_format(pCurrentLine->m_aName, sizeof(pCurrentLine->m_aName), "%s", m_pClient->m_aClients[ClientID].m_aName);
|
2014-01-30 15:49:15 +00:00
|
|
|
|
2020-10-13 20:08:52 +00:00
|
|
|
str_format(pCurrentLine->m_aText, sizeof(pCurrentLine->m_aText), "%s", pLine);
|
2020-09-20 15:25:27 +00:00
|
|
|
pCurrentLine->m_Friend = m_pClient->m_aClients[ClientID].m_Friend;
|
2008-08-27 15:48:50 +00:00
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2020-10-13 20:08:52 +00:00
|
|
|
pCurrentLine->m_HasRenderTee = false;
|
|
|
|
|
2020-09-20 15:25:27 +00:00
|
|
|
pCurrentLine->m_Friend = ClientID >= 0 ? m_pClient->m_aClients[ClientID].m_Friend : false;
|
2017-03-10 17:49:39 +00:00
|
|
|
|
2020-10-13 20:08:52 +00:00
|
|
|
if(pCurrentLine->m_ClientID >= 0 && pCurrentLine->m_aName[0] != '\0')
|
|
|
|
{
|
|
|
|
if(g_Config.m_ClChatTee)
|
|
|
|
{
|
|
|
|
pCurrentLine->m_CustomColoredSkin = m_pClient->m_aClients[pCurrentLine->m_ClientID].m_RenderInfo.m_CustomColoredSkin;
|
|
|
|
if(pCurrentLine->m_CustomColoredSkin)
|
|
|
|
pCurrentLine->m_RenderSkin = m_pClient->m_aClients[pCurrentLine->m_ClientID].m_RenderInfo.m_ColorableRenderSkin;
|
|
|
|
else
|
|
|
|
pCurrentLine->m_RenderSkin = m_pClient->m_aClients[pCurrentLine->m_ClientID].m_RenderInfo.m_OriginalRenderSkin;
|
|
|
|
|
|
|
|
str_copy(pCurrentLine->m_aSkinName, m_pClient->m_aClients[pCurrentLine->m_ClientID].m_aSkinName, sizeof(pCurrentLine->m_aSkinName));
|
|
|
|
pCurrentLine->m_ColorBody = m_pClient->m_aClients[pCurrentLine->m_ClientID].m_RenderInfo.m_ColorBody;
|
|
|
|
pCurrentLine->m_ColorFeet = m_pClient->m_aClients[pCurrentLine->m_ClientID].m_RenderInfo.m_ColorFeet;
|
|
|
|
pCurrentLine->m_HasRenderTee = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
char aBuf[1024];
|
2020-10-13 20:08:52 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "%s: %s", pCurrentLine->m_aName, pCurrentLine->m_aText);
|
2020-09-20 15:25:27 +00:00
|
|
|
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, Team >= 2 ? "whisper" : (pCurrentLine->m_Team ? "teamchat" : "chat"), aBuf, Highlighted);
|
2008-08-27 15:48:50 +00:00
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2009-06-15 13:01:04 +00:00
|
|
|
// play sound
|
2016-08-30 23:39:59 +00:00
|
|
|
int64 Now = time();
|
|
|
|
if(ClientID == -1)
|
2012-01-06 18:47:49 +00:00
|
|
|
{
|
2020-09-22 16:02:03 +00:00
|
|
|
if(Now - m_aLastSoundPlayed[CHAT_SERVER] >= time_freq() * 3 / 10)
|
2012-01-06 18:47:49 +00:00
|
|
|
{
|
2014-10-25 00:52:08 +00:00
|
|
|
if(g_Config.m_SndServerMessage)
|
|
|
|
{
|
|
|
|
m_pClient->m_pSounds->Play(CSounds::CHN_GUI, SOUND_CHAT_SERVER, 0);
|
|
|
|
m_aLastSoundPlayed[CHAT_SERVER] = Now;
|
|
|
|
}
|
2012-01-06 18:47:49 +00:00
|
|
|
}
|
|
|
|
}
|
2017-01-05 23:17:41 +00:00
|
|
|
else if(ClientID == -2) // Client message
|
|
|
|
{
|
|
|
|
// No sound yet
|
|
|
|
}
|
2019-09-24 09:00:30 +00:00
|
|
|
else if(Highlighted && Client()->State() != IClient::STATE_DEMOPLAYBACK)
|
2012-01-06 18:47:49 +00:00
|
|
|
{
|
2020-09-22 16:02:03 +00:00
|
|
|
if(Now - m_aLastSoundPlayed[CHAT_HIGHLIGHT] >= time_freq() * 3 / 10)
|
2012-01-06 18:47:49 +00:00
|
|
|
{
|
2015-08-11 01:10:05 +00:00
|
|
|
char aBuf[1024];
|
2020-10-13 20:08:52 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "%s: %s", m_aLines[m_CurrentLine].m_aName, m_aLines[m_CurrentLine].m_aText);
|
2020-04-14 15:53:53 +00:00
|
|
|
Client()->Notify("DDNet Chat", aBuf);
|
2014-10-25 00:52:08 +00:00
|
|
|
if(g_Config.m_SndHighlight)
|
|
|
|
{
|
|
|
|
m_pClient->m_pSounds->Play(CSounds::CHN_GUI, SOUND_CHAT_HIGHLIGHT, 0);
|
|
|
|
m_aLastSoundPlayed[CHAT_HIGHLIGHT] = Now;
|
|
|
|
}
|
2019-04-05 23:15:02 +00:00
|
|
|
|
|
|
|
if(g_Config.m_ClEditor)
|
|
|
|
{
|
|
|
|
GameClient()->Editor()->UpdateMentions();
|
|
|
|
}
|
2012-01-06 18:47:49 +00:00
|
|
|
}
|
|
|
|
}
|
2014-02-06 17:17:29 +00:00
|
|
|
else if(Team != 2)
|
2012-01-06 18:47:49 +00:00
|
|
|
{
|
2020-09-22 16:02:03 +00:00
|
|
|
if(Now - m_aLastSoundPlayed[CHAT_CLIENT] >= time_freq() * 3 / 10)
|
2012-01-06 18:47:49 +00:00
|
|
|
{
|
2020-09-22 16:02:03 +00:00
|
|
|
if((g_Config.m_SndTeamChat || !m_aLines[m_CurrentLine].m_Team) && (g_Config.m_SndChat || m_aLines[m_CurrentLine].m_Team))
|
2014-05-04 16:35:37 +00:00
|
|
|
{
|
|
|
|
m_pClient->m_pSounds->Play(CSounds::CHN_GUI, SOUND_CHAT_CLIENT, 0);
|
|
|
|
m_aLastSoundPlayed[CHAT_CLIENT] = Now;
|
|
|
|
}
|
2012-01-06 18:47:49 +00:00
|
|
|
}
|
|
|
|
}
|
2008-08-27 15:48:50 +00:00
|
|
|
}
|
|
|
|
|
2020-10-13 20:08:52 +00:00
|
|
|
void CChat::RefindSkins()
|
|
|
|
{
|
|
|
|
for(int i = 0; i < MAX_LINES; i++)
|
|
|
|
{
|
|
|
|
int r = ((m_CurrentLine - i) + MAX_LINES) % MAX_LINES;
|
|
|
|
if(m_aLines[r].m_TextContainerIndex == -1)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if(m_aLines[r].m_HasRenderTee)
|
|
|
|
{
|
|
|
|
const CSkin *pSkin = m_pClient->m_pSkins->Get(m_pClient->m_pSkins->Find(m_aLines[r].m_aSkinName));
|
|
|
|
if(m_aLines[r].m_CustomColoredSkin)
|
|
|
|
m_aLines[r].m_RenderSkin = pSkin->m_ColorableSkin;
|
|
|
|
else
|
|
|
|
m_aLines[r].m_RenderSkin = pSkin->m_OriginalSkin;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-13 20:50:49 +00:00
|
|
|
void CChat::OnPrepareLines()
|
|
|
|
{
|
|
|
|
float x = 5.0f;
|
|
|
|
float y = 300.0f - 28.0f;
|
2020-10-13 20:08:52 +00:00
|
|
|
float FontSize = FONT_SIZE;
|
2018-03-13 20:50:49 +00:00
|
|
|
|
2020-11-01 03:52:07 +00:00
|
|
|
bool IsScoreBoardOpen = m_pClient->m_pScoreboard->Active();
|
|
|
|
|
|
|
|
bool ForceRecreate = IsScoreBoardOpen != m_PrevScoreBoardShowed;
|
2020-04-28 20:35:54 +00:00
|
|
|
bool ShowLargeArea = m_Show || g_Config.m_ClShowChat == 2;
|
2020-10-13 20:08:52 +00:00
|
|
|
|
2020-11-01 03:48:10 +00:00
|
|
|
ForceRecreate |= ShowLargeArea != m_PrevShowChat;
|
|
|
|
|
2020-11-01 03:52:07 +00:00
|
|
|
m_PrevScoreBoardShowed = IsScoreBoardOpen;
|
2020-04-28 20:35:54 +00:00
|
|
|
m_PrevShowChat = ShowLargeArea;
|
2018-03-13 20:50:49 +00:00
|
|
|
|
2020-10-13 20:08:52 +00:00
|
|
|
float RealMsgPaddingX = MESSAGE_PADDING_X;
|
|
|
|
float RealMsgPaddingY = MESSAGE_PADDING_Y;
|
|
|
|
float RealMsgPaddingTee = MESSAGE_TEE_SIZE + MESSAGE_TEE_PADDING_RIGHT;
|
|
|
|
|
|
|
|
if(!g_Config.m_ClChatBackground)
|
|
|
|
{
|
|
|
|
RealMsgPaddingX = 0;
|
|
|
|
RealMsgPaddingY = (g_Config.m_ClChatTee ? (MESSAGE_TEE_SIZE - FONT_SIZE) : 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!g_Config.m_ClChatTee)
|
|
|
|
RealMsgPaddingTee = 0;
|
|
|
|
|
2019-10-13 11:57:24 +00:00
|
|
|
int64 Now = time();
|
2020-11-01 03:52:07 +00:00
|
|
|
float LineWidth = (IsScoreBoardOpen ? 90.0f : 200.0f) - RealMsgPaddingX - RealMsgPaddingTee;
|
2020-10-13 20:08:52 +00:00
|
|
|
|
2020-11-01 03:52:07 +00:00
|
|
|
float HeightLimit = IsScoreBoardOpen ? 180.0f : m_PrevShowChat ? 50.0f : 200.0f;
|
2018-03-13 20:50:49 +00:00
|
|
|
float Begin = x;
|
2020-10-13 20:08:52 +00:00
|
|
|
float TextBegin = Begin + RealMsgPaddingX / 2.0f;
|
2018-03-13 20:50:49 +00:00
|
|
|
CTextCursor Cursor;
|
2020-11-01 03:52:07 +00:00
|
|
|
int OffsetType = IsScoreBoardOpen ? 1 : 0;
|
2020-10-13 20:08:52 +00:00
|
|
|
|
2018-03-13 20:50:49 +00:00
|
|
|
for(int i = 0; i < MAX_LINES; i++)
|
|
|
|
{
|
|
|
|
int r = ((m_CurrentLine - i) + MAX_LINES) % MAX_LINES;
|
2020-10-13 20:08:52 +00:00
|
|
|
|
2020-04-28 20:35:54 +00:00
|
|
|
if(Now > m_aLines[r].m_Time + 16 * time_freq() && !m_PrevShowChat)
|
2018-03-13 20:50:49 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
if(m_aLines[r].m_TextContainerIndex != -1 && !ForceRecreate)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if(m_aLines[r].m_TextContainerIndex != -1)
|
|
|
|
TextRender()->DeleteTextContainer(m_aLines[r].m_TextContainerIndex);
|
|
|
|
|
|
|
|
m_aLines[r].m_TextContainerIndex = -1;
|
|
|
|
|
2020-10-13 20:08:52 +00:00
|
|
|
if(m_aLines[r].m_QuadContainerIndex != -1)
|
|
|
|
Graphics()->DeleteQuadContainer(m_aLines[r].m_QuadContainerIndex);
|
|
|
|
|
|
|
|
m_aLines[r].m_QuadContainerIndex = -1;
|
|
|
|
|
|
|
|
char aName[64 + 12] = "";
|
2020-09-20 15:25:27 +00:00
|
|
|
|
2019-05-10 22:26:15 +00:00
|
|
|
if(g_Config.m_ClShowIDs && m_aLines[r].m_ClientID >= 0 && m_aLines[r].m_aName[0] != '\0')
|
2018-03-13 20:50:49 +00:00
|
|
|
{
|
2020-10-13 20:08:52 +00:00
|
|
|
if(m_aLines[r].m_ClientID < 10)
|
|
|
|
str_format(aName, sizeof(aName), " %d: ", m_aLines[r].m_ClientID);
|
2018-03-13 20:50:49 +00:00
|
|
|
else
|
2020-10-13 20:08:52 +00:00
|
|
|
str_format(aName, sizeof(aName), "%d: ", m_aLines[r].m_ClientID);
|
2018-03-13 20:50:49 +00:00
|
|
|
}
|
2020-09-20 15:25:27 +00:00
|
|
|
|
|
|
|
str_append(aName, m_aLines[r].m_aName, sizeof(aName));
|
|
|
|
|
2020-09-27 11:28:50 +00:00
|
|
|
char aCount[12];
|
|
|
|
if(m_aLines[r].m_ClientID < 0)
|
|
|
|
str_format(aCount, sizeof(aCount), "[%d] ", m_aLines[r].m_TimesRepeated + 1);
|
|
|
|
else
|
|
|
|
str_format(aCount, sizeof(aCount), " [%d]", m_aLines[r].m_TimesRepeated + 1);
|
2018-03-13 20:50:49 +00:00
|
|
|
|
2020-10-13 20:08:52 +00:00
|
|
|
if(!g_Config.m_ClChatTee)
|
|
|
|
{
|
|
|
|
m_aLines[r].m_HasRenderTee = false;
|
|
|
|
}
|
|
|
|
|
2018-03-13 20:50:49 +00:00
|
|
|
// get the y offset (calculate it if we haven't done that yet)
|
2020-11-01 03:48:10 +00:00
|
|
|
if(m_aLines[r].m_YOffset[OffsetType] < 0.0f)
|
2018-03-13 20:50:49 +00:00
|
|
|
{
|
2020-10-13 20:08:52 +00:00
|
|
|
TextRender()->SetCursor(&Cursor, TextBegin, 0.0f, FontSize, 0);
|
2018-03-13 20:50:49 +00:00
|
|
|
Cursor.m_LineWidth = LineWidth;
|
2020-09-20 15:25:27 +00:00
|
|
|
|
2020-10-22 20:21:19 +00:00
|
|
|
if(m_aLines[r].m_ClientID >= 0 && m_aLines[r].m_aName[0] != '\0')
|
2020-10-13 20:08:52 +00:00
|
|
|
{
|
2020-10-22 20:21:19 +00:00
|
|
|
Cursor.m_X += RealMsgPaddingTee;
|
|
|
|
|
|
|
|
if(m_aLines[r].m_Friend && g_Config.m_ClMessageFriend)
|
|
|
|
{
|
|
|
|
TextRender()->TextEx(&Cursor, "♥ ", -1);
|
|
|
|
}
|
2020-10-13 20:08:52 +00:00
|
|
|
}
|
|
|
|
|
2018-03-13 20:50:49 +00:00
|
|
|
TextRender()->TextEx(&Cursor, aName, -1);
|
2020-09-27 11:28:50 +00:00
|
|
|
if(m_aLines[r].m_TimesRepeated > 0)
|
|
|
|
TextRender()->TextEx(&Cursor, aCount, -1);
|
2020-10-13 20:08:52 +00:00
|
|
|
|
2020-10-22 20:21:19 +00:00
|
|
|
if(m_aLines[r].m_ClientID >= 0 && m_aLines[r].m_aName[0] != '\0')
|
|
|
|
{
|
|
|
|
TextRender()->TextEx(&Cursor, ": ", -1);
|
|
|
|
}
|
2020-10-13 20:08:52 +00:00
|
|
|
|
|
|
|
CTextCursor AppendCursor = Cursor;
|
2020-11-01 03:52:07 +00:00
|
|
|
|
|
|
|
if(!IsScoreBoardOpen)
|
|
|
|
{
|
|
|
|
AppendCursor.m_StartX = Cursor.m_X;
|
|
|
|
AppendCursor.m_LineWidth -= (Cursor.m_LongestLineWidth - Cursor.m_StartX);
|
|
|
|
}
|
2020-10-13 20:08:52 +00:00
|
|
|
|
|
|
|
TextRender()->TextEx(&AppendCursor, m_aLines[r].m_aText, -1);
|
|
|
|
|
2020-11-01 03:48:10 +00:00
|
|
|
m_aLines[r].m_YOffset[OffsetType] = AppendCursor.m_Y + AppendCursor.m_FontSize + RealMsgPaddingY;
|
2018-03-13 20:50:49 +00:00
|
|
|
}
|
2020-10-13 20:08:52 +00:00
|
|
|
|
2020-11-01 03:48:10 +00:00
|
|
|
y -= m_aLines[r].m_YOffset[OffsetType];
|
2018-03-13 20:50:49 +00:00
|
|
|
|
|
|
|
// cut off if msgs waste too much space
|
|
|
|
if(y < HeightLimit)
|
|
|
|
break;
|
|
|
|
|
|
|
|
// the position the text was created
|
2020-10-13 20:08:52 +00:00
|
|
|
m_aLines[r].m_TextYOffset = y + RealMsgPaddingY / 2.f;
|
2018-03-13 20:50:49 +00:00
|
|
|
|
2020-10-22 20:21:19 +00:00
|
|
|
int CurRenderFlags = TextRender()->GetRenderFlags();
|
|
|
|
TextRender()->SetRenderFlags(CurRenderFlags | ETextRenderFlags::TEXT_RENDER_FLAG_NO_AUTOMATIC_QUAD_UPLOAD);
|
|
|
|
|
2018-03-13 20:50:49 +00:00
|
|
|
// reset the cursor
|
2020-10-13 20:08:52 +00:00
|
|
|
TextRender()->SetCursor(&Cursor, TextBegin, m_aLines[r].m_TextYOffset, FontSize, TEXTFLAG_RENDER);
|
2018-03-13 20:50:49 +00:00
|
|
|
Cursor.m_LineWidth = LineWidth;
|
|
|
|
|
2020-10-13 20:08:52 +00:00
|
|
|
// Message is from valid player
|
|
|
|
if(m_aLines[r].m_ClientID >= 0 && m_aLines[r].m_aName[0] != '\0')
|
2018-03-13 20:50:49 +00:00
|
|
|
{
|
2020-10-13 20:08:52 +00:00
|
|
|
Cursor.m_X += RealMsgPaddingTee;
|
|
|
|
|
|
|
|
if(m_aLines[r].m_Friend && g_Config.m_ClMessageFriend)
|
|
|
|
{
|
|
|
|
const char *pHeartStr = "♥ ";
|
|
|
|
ColorRGBA rgb = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClMessageFriendColor));
|
|
|
|
TextRender()->TextColor(rgb.WithAlpha(1.f));
|
|
|
|
if(m_aLines[r].m_TextContainerIndex == -1)
|
|
|
|
m_aLines[r].m_TextContainerIndex = TextRender()->CreateTextContainer(&Cursor, pHeartStr);
|
|
|
|
else
|
|
|
|
TextRender()->AppendTextContainer(&Cursor, m_aLines[r].m_TextContainerIndex, pHeartStr);
|
|
|
|
}
|
2018-03-13 20:50:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// render name
|
2020-10-13 20:08:52 +00:00
|
|
|
ColorRGBA NameColor;
|
2018-03-13 20:50:49 +00:00
|
|
|
if(m_aLines[r].m_ClientID == -1) // system
|
|
|
|
{
|
2020-10-13 20:08:52 +00:00
|
|
|
NameColor = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClMessageSystemColor));
|
2018-03-13 20:50:49 +00:00
|
|
|
}
|
|
|
|
else if(m_aLines[r].m_ClientID == -2) // client
|
|
|
|
{
|
2020-10-13 20:08:52 +00:00
|
|
|
NameColor = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClMessageClientColor));
|
2018-03-13 20:50:49 +00:00
|
|
|
}
|
|
|
|
else if(m_aLines[r].m_Team)
|
|
|
|
{
|
2020-10-13 20:08:52 +00:00
|
|
|
NameColor = CalculateNameColor(ColorHSLA(g_Config.m_ClMessageTeamColor));
|
2018-03-13 20:50:49 +00:00
|
|
|
}
|
|
|
|
else if(m_aLines[r].m_NameColor == TEAM_RED)
|
2020-10-13 20:08:52 +00:00
|
|
|
NameColor = ColorRGBA(1.0f, 0.5f, 0.5f, 1.f); // red
|
2018-03-13 20:50:49 +00:00
|
|
|
else if(m_aLines[r].m_NameColor == TEAM_BLUE)
|
2020-10-13 20:08:52 +00:00
|
|
|
NameColor = ColorRGBA(0.7f, 0.7f, 1.0f, 1.f); // blue
|
2018-03-13 20:50:49 +00:00
|
|
|
else if(m_aLines[r].m_NameColor == TEAM_SPECTATORS)
|
2020-10-13 20:08:52 +00:00
|
|
|
NameColor = ColorRGBA(0.75f, 0.5f, 0.75f, 1.f); // spectator
|
2018-03-13 20:50:49 +00:00
|
|
|
else if(m_aLines[r].m_ClientID >= 0 && g_Config.m_ClChatTeamColors && m_pClient->m_Teams.Team(m_aLines[r].m_ClientID))
|
|
|
|
{
|
2020-10-13 20:08:52 +00:00
|
|
|
NameColor = color_cast<ColorRGBA>(ColorHSLA(m_pClient->m_Teams.Team(m_aLines[r].m_ClientID) / 64.0f, 1.0f, 0.75f));
|
2018-03-13 20:50:49 +00:00
|
|
|
}
|
|
|
|
else
|
2020-10-13 20:08:52 +00:00
|
|
|
NameColor = ColorRGBA(0.8f, 0.8f, 0.8f, 1.f);
|
|
|
|
|
|
|
|
TextRender()->TextColor(NameColor);
|
2018-03-13 20:50:49 +00:00
|
|
|
|
|
|
|
if(m_aLines[r].m_TextContainerIndex == -1)
|
|
|
|
m_aLines[r].m_TextContainerIndex = TextRender()->CreateTextContainer(&Cursor, aName);
|
|
|
|
else
|
|
|
|
TextRender()->AppendTextContainer(&Cursor, m_aLines[r].m_TextContainerIndex, aName);
|
|
|
|
|
2020-09-27 11:28:50 +00:00
|
|
|
if(m_aLines[r].m_TimesRepeated > 0)
|
2018-03-13 20:50:49 +00:00
|
|
|
{
|
2020-09-27 11:28:50 +00:00
|
|
|
TextRender()->TextColor(1.0f, 1.0f, 1.0f, 0.3f);
|
|
|
|
if(m_aLines[r].m_TextContainerIndex == -1)
|
|
|
|
m_aLines[r].m_TextContainerIndex = TextRender()->CreateTextContainer(&Cursor, aCount);
|
|
|
|
else
|
|
|
|
TextRender()->AppendTextContainer(&Cursor, m_aLines[r].m_TextContainerIndex, aCount);
|
2018-03-13 20:50:49 +00:00
|
|
|
}
|
2020-09-27 11:28:50 +00:00
|
|
|
|
2020-10-13 20:08:52 +00:00
|
|
|
if(m_aLines[r].m_ClientID >= 0 && m_aLines[r].m_aName[0] != '\0')
|
|
|
|
{
|
|
|
|
TextRender()->TextColor(NameColor);
|
|
|
|
if(m_aLines[r].m_TextContainerIndex == -1)
|
|
|
|
m_aLines[r].m_TextContainerIndex = TextRender()->CreateTextContainer(&Cursor, ": ");
|
|
|
|
else
|
|
|
|
TextRender()->AppendTextContainer(&Cursor, m_aLines[r].m_TextContainerIndex, ": ");
|
|
|
|
}
|
|
|
|
|
2020-09-27 11:28:50 +00:00
|
|
|
// render line
|
|
|
|
ColorRGBA Color;
|
|
|
|
if(m_aLines[r].m_ClientID == -1) // system
|
|
|
|
Color = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClMessageSystemColor));
|
2018-03-13 20:50:49 +00:00
|
|
|
else if(m_aLines[r].m_ClientID == -2) // client
|
2020-09-27 11:28:50 +00:00
|
|
|
Color = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClMessageClientColor));
|
2018-03-13 20:50:49 +00:00
|
|
|
else if(m_aLines[r].m_Highlighted) // highlighted
|
2020-09-27 11:28:50 +00:00
|
|
|
Color = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClMessageHighlightColor));
|
2018-03-13 20:50:49 +00:00
|
|
|
else if(m_aLines[r].m_Team) // team message
|
2020-09-27 11:28:50 +00:00
|
|
|
Color = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClMessageTeamColor));
|
2018-03-13 20:50:49 +00:00
|
|
|
else // regular message
|
2020-09-27 11:28:50 +00:00
|
|
|
Color = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClMessageColor));
|
|
|
|
|
|
|
|
TextRender()->TextColor(Color);
|
2018-03-13 20:50:49 +00:00
|
|
|
|
2020-10-13 20:08:52 +00:00
|
|
|
CTextCursor AppendCursor = Cursor;
|
2020-11-01 03:52:07 +00:00
|
|
|
if(!IsScoreBoardOpen)
|
|
|
|
{
|
|
|
|
AppendCursor.m_LineWidth -= (Cursor.m_LongestLineWidth - Cursor.m_StartX);
|
|
|
|
AppendCursor.m_StartX = Cursor.m_X;
|
|
|
|
}
|
2020-10-13 20:08:52 +00:00
|
|
|
|
2018-03-13 20:50:49 +00:00
|
|
|
if(m_aLines[r].m_TextContainerIndex == -1)
|
2020-10-13 20:08:52 +00:00
|
|
|
m_aLines[r].m_TextContainerIndex = TextRender()->CreateTextContainer(&AppendCursor, m_aLines[r].m_aText);
|
2018-03-13 20:50:49 +00:00
|
|
|
else
|
2020-10-13 20:08:52 +00:00
|
|
|
TextRender()->AppendTextContainer(&AppendCursor, m_aLines[r].m_TextContainerIndex, m_aLines[r].m_aText);
|
|
|
|
|
|
|
|
if(g_Config.m_ClChatBackground && (m_aLines[r].m_aText[0] != '\0' || m_aLines[r].m_aName[0] != '\0'))
|
|
|
|
{
|
2020-11-01 03:48:10 +00:00
|
|
|
float Height = m_aLines[r].m_YOffset[OffsetType];
|
2020-10-13 20:08:52 +00:00
|
|
|
Graphics()->SetColor(1, 1, 1, 1);
|
2020-10-22 20:21:19 +00:00
|
|
|
m_aLines[r].m_QuadContainerIndex = RenderTools()->CreateRoundRectQuadContainer(Begin, y, (AppendCursor.m_LongestLineWidth - TextBegin) + RealMsgPaddingX * 1.5f, Height, MESSAGE_ROUNDING, CUI::CORNER_ALL);
|
2020-10-13 20:08:52 +00:00
|
|
|
}
|
2020-10-22 20:21:19 +00:00
|
|
|
|
|
|
|
TextRender()->SetRenderFlags(CurRenderFlags);
|
|
|
|
if(m_aLines[r].m_TextContainerIndex != -1)
|
|
|
|
TextRender()->UploadTextContainer(m_aLines[r].m_TextContainerIndex);
|
2018-03-13 20:50:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f);
|
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
void CChat::OnRender()
|
2008-08-27 15:48:50 +00:00
|
|
|
{
|
2012-01-09 22:13:51 +00:00
|
|
|
// send pending chat messages
|
2020-09-22 16:02:03 +00:00
|
|
|
if(m_PendingChatCounter > 0 && m_LastChatSend + time_freq() < time())
|
2012-01-09 22:13:51 +00:00
|
|
|
{
|
|
|
|
CHistoryEntry *pEntry = m_History.Last();
|
2020-09-22 16:02:03 +00:00
|
|
|
for(int i = m_PendingChatCounter - 1; pEntry; --i, pEntry = m_History.Prev(pEntry))
|
2012-01-09 22:13:51 +00:00
|
|
|
{
|
|
|
|
if(i == 0)
|
|
|
|
{
|
|
|
|
Say(pEntry->m_Team, pEntry->m_aText);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
--m_PendingChatCounter;
|
|
|
|
}
|
|
|
|
|
2020-09-22 16:02:03 +00:00
|
|
|
float Width = 300.0f * Graphics()->ScreenAspect();
|
2011-03-27 21:43:45 +00:00
|
|
|
Graphics()->MapScreen(0.0f, 0.0f, Width, 300.0f);
|
2011-03-19 17:28:47 +00:00
|
|
|
float x = 5.0f;
|
2020-09-22 16:02:03 +00:00
|
|
|
float y = 300.0f - 20.0f;
|
2010-05-29 07:25:38 +00:00
|
|
|
if(m_Mode != MODE_NONE)
|
2008-08-27 15:48:50 +00:00
|
|
|
{
|
|
|
|
// render chat input
|
2010-05-29 07:25:38 +00:00
|
|
|
CTextCursor Cursor;
|
|
|
|
TextRender()->SetCursor(&Cursor, x, y, 8.0f, TEXTFLAG_RENDER);
|
2020-09-22 16:02:03 +00:00
|
|
|
Cursor.m_LineWidth = Width - 190.0f;
|
2010-10-11 00:29:30 +00:00
|
|
|
Cursor.m_MaxLines = 2;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
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);
|
2008-08-27 15:48:50 +00:00
|
|
|
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);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2016-08-15 05:16:06 +00:00
|
|
|
// IME candidate editing
|
|
|
|
bool Editing = false;
|
|
|
|
int EditingCursor = Input()->GetEditingCursor();
|
2018-03-13 20:50:49 +00:00
|
|
|
if(Input()->GetIMEState())
|
2016-08-15 05:16:06 +00:00
|
|
|
{
|
2020-10-25 15:48:12 +00:00
|
|
|
if(str_length(Input()->GetIMEEditingText()))
|
2016-08-15 05:16:06 +00:00
|
|
|
{
|
2020-10-25 15:48:12 +00:00
|
|
|
m_Input.Editing(Input()->GetIMEEditingText(), EditingCursor);
|
2016-08-15 05:16:06 +00:00
|
|
|
Editing = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-10-11 00:29:30 +00:00
|
|
|
// check if the visible text has to be moved
|
|
|
|
if(m_InputUpdate)
|
|
|
|
{
|
2016-08-15 05:16:06 +00:00
|
|
|
if(m_ChatStringOffset > 0 && m_Input.GetLength(Editing) < m_OldChatStringLength)
|
2020-09-22 16:02:03 +00:00
|
|
|
m_ChatStringOffset = maximum(0, m_ChatStringOffset - (m_OldChatStringLength - m_Input.GetLength(Editing)));
|
2010-10-11 10:31:45 +00:00
|
|
|
|
2016-08-15 05:16:06 +00:00
|
|
|
if(m_ChatStringOffset > m_Input.GetCursorOffset(Editing))
|
2020-09-22 16:02:03 +00:00
|
|
|
m_ChatStringOffset -= m_ChatStringOffset - m_Input.GetCursorOffset(Editing);
|
2010-10-11 00:29:30 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
CTextCursor Temp = Cursor;
|
|
|
|
Temp.m_Flags = 0;
|
2020-09-22 16:02:03 +00:00
|
|
|
TextRender()->TextEx(&Temp, m_Input.GetString(Editing) + m_ChatStringOffset, m_Input.GetCursorOffset(Editing) - m_ChatStringOffset);
|
2010-10-11 00:29:30 +00:00
|
|
|
TextRender()->TextEx(&Temp, "|", -1);
|
2010-10-11 10:31:45 +00:00
|
|
|
while(Temp.m_LineCount > 2)
|
|
|
|
{
|
2010-10-11 00:29:30 +00:00
|
|
|
++m_ChatStringOffset;
|
2010-10-11 10:31:45 +00:00
|
|
|
Temp = Cursor;
|
|
|
|
Temp.m_Flags = 0;
|
2020-09-22 16:02:03 +00:00
|
|
|
TextRender()->TextEx(&Temp, m_Input.GetString(Editing) + m_ChatStringOffset, m_Input.GetCursorOffset(Editing) - m_ChatStringOffset);
|
2010-10-11 10:31:45 +00:00
|
|
|
TextRender()->TextEx(&Temp, "|", -1);
|
|
|
|
}
|
2010-10-11 00:29:30 +00:00
|
|
|
}
|
2010-10-11 10:31:45 +00:00
|
|
|
m_InputUpdate = false;
|
2010-10-11 00:29:30 +00:00
|
|
|
}
|
|
|
|
|
2020-09-22 16:02:03 +00:00
|
|
|
TextRender()->TextEx(&Cursor, m_Input.GetString(Editing) + m_ChatStringOffset, m_Input.GetCursorOffset(Editing) - m_ChatStringOffset);
|
|
|
|
static float MarkerOffset = TextRender()->TextWidth(0, 8.0f, "|", -1, -1.0f) / 3;
|
2010-05-29 07:25:38 +00:00
|
|
|
CTextCursor Marker = Cursor;
|
2011-06-09 21:54:11 +00:00
|
|
|
Marker.m_X -= MarkerOffset;
|
2010-05-29 07:25:38 +00:00
|
|
|
TextRender()->TextEx(&Marker, "|", -1);
|
2020-09-22 16:02:03 +00:00
|
|
|
TextRender()->TextEx(&Cursor, m_Input.GetString(Editing) + m_Input.GetCursorOffset(Editing), -1);
|
2020-09-22 16:01:13 +00:00
|
|
|
if(m_pClient->m_pGameConsole->IsClosed())
|
|
|
|
Input()->SetEditingPosition(Marker.m_X, Marker.m_Y + Marker.m_FontSize);
|
2008-08-27 15:48:50 +00:00
|
|
|
}
|
|
|
|
|
2019-10-13 12:29:55 +00:00
|
|
|
#if defined(CONF_VIDEORECORDER)
|
|
|
|
if(!((g_Config.m_ClShowChat && !IVideo::Current()) || (g_Config.m_ClVideoShowChat && IVideo::Current())))
|
|
|
|
#else
|
2018-03-13 20:50:49 +00:00
|
|
|
if(!g_Config.m_ClShowChat)
|
2019-10-13 12:29:55 +00:00
|
|
|
#endif
|
2018-01-05 19:11:05 +00:00
|
|
|
return;
|
|
|
|
|
2010-08-16 00:21:18 +00:00
|
|
|
y -= 8.0f;
|
2008-08-27 15:48:50 +00:00
|
|
|
|
2018-03-13 20:50:49 +00:00
|
|
|
OnPrepareLines();
|
|
|
|
|
2016-08-30 23:39:59 +00:00
|
|
|
int64 Now = time();
|
2020-11-01 03:48:10 +00:00
|
|
|
float HeightLimit = m_pClient->m_pScoreboard->Active() ? 180.0f : m_PrevShowChat ? 50.0f : 200.0f;
|
|
|
|
int OffsetType = m_pClient->m_pScoreboard->Active() ? 1 : 0;
|
2020-10-13 20:08:52 +00:00
|
|
|
|
|
|
|
float RealMsgPaddingX = MESSAGE_PADDING_X;
|
|
|
|
float RealMsgPaddingY = MESSAGE_PADDING_Y;
|
|
|
|
|
|
|
|
if(!g_Config.m_ClChatBackground)
|
|
|
|
{
|
|
|
|
RealMsgPaddingX = 0;
|
|
|
|
RealMsgPaddingY = (g_Config.m_ClChatTee ? (MESSAGE_TEE_SIZE - FONT_SIZE) : 0);
|
|
|
|
}
|
|
|
|
|
2010-08-18 01:57:35 +00:00
|
|
|
for(int i = 0; i < MAX_LINES; i++)
|
2008-08-27 15:48:50 +00:00
|
|
|
{
|
2020-09-22 16:02:03 +00:00
|
|
|
int r = ((m_CurrentLine - i) + MAX_LINES) % MAX_LINES;
|
|
|
|
if(Now > m_aLines[r].m_Time + 16 * time_freq() && !m_PrevShowChat)
|
2008-08-27 15:48:50 +00:00
|
|
|
break;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2020-11-01 03:48:10 +00:00
|
|
|
y -= m_aLines[r].m_YOffset[OffsetType];
|
2008-08-27 15:48:50 +00:00
|
|
|
|
2008-09-07 08:44:30 +00:00
|
|
|
// cut off if msgs waste too much space
|
2010-05-30 12:01:11 +00:00
|
|
|
if(y < HeightLimit)
|
2008-09-07 08:44:30 +00:00
|
|
|
break;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2020-09-22 16:02:03 +00:00
|
|
|
float Blend = Now > m_aLines[r].m_Time + 14 * time_freq() && !m_PrevShowChat ? 1.0f - (Now - m_aLines[r].m_Time - 14 * time_freq()) / (2.0f * time_freq()) : 1.0f;
|
2010-12-16 03:20:50 +00:00
|
|
|
|
2020-10-13 20:08:52 +00:00
|
|
|
// Draw backgrounds for messages in one batch
|
|
|
|
if(g_Config.m_ClChatBackground)
|
|
|
|
{
|
|
|
|
Graphics()->TextureClear();
|
|
|
|
if(m_aLines[r].m_QuadContainerIndex != -1)
|
|
|
|
{
|
|
|
|
Graphics()->SetColor(0, 0, 0, 0.12f * Blend);
|
|
|
|
Graphics()->RenderQuadContainerEx(m_aLines[r].m_QuadContainerIndex, 0, -1, 0, ((y + RealMsgPaddingY / 2.0f) - m_aLines[r].m_TextYOffset));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-13 20:50:49 +00:00
|
|
|
if(m_aLines[r].m_TextContainerIndex != -1)
|
2017-03-10 17:49:39 +00:00
|
|
|
{
|
2020-10-13 20:08:52 +00:00
|
|
|
if(g_Config.m_ClChatTee && m_aLines[r].m_HasRenderTee)
|
|
|
|
{
|
|
|
|
CTeeRenderInfo RenderInfo;
|
|
|
|
RenderInfo.m_CustomColoredSkin = m_aLines[r].m_CustomColoredSkin;
|
|
|
|
if(m_aLines[r].m_CustomColoredSkin)
|
|
|
|
RenderInfo.m_ColorableRenderSkin = m_aLines[r].m_RenderSkin;
|
|
|
|
else
|
|
|
|
RenderInfo.m_OriginalRenderSkin = m_aLines[r].m_RenderSkin;
|
|
|
|
|
|
|
|
RenderInfo.m_ColorBody = m_aLines[r].m_ColorBody;
|
|
|
|
RenderInfo.m_ColorFeet = m_aLines[r].m_ColorFeet;
|
|
|
|
RenderInfo.m_Size = MESSAGE_TEE_SIZE;
|
|
|
|
|
|
|
|
float RowHeight = FONT_SIZE + RealMsgPaddingY;
|
|
|
|
float OffsetTeeY = MESSAGE_TEE_SIZE / 2.0f;
|
|
|
|
float FullHeightMinusTee = RowHeight - MESSAGE_TEE_SIZE;
|
2020-11-01 03:48:10 +00:00
|
|
|
float TWSkinUnreliableOffset = 1.0f; // teeworlds skins were always a bit in the ground
|
2020-10-13 20:08:52 +00:00
|
|
|
|
|
|
|
CAnimState *pIdleState = CAnimState::GetIdle();
|
|
|
|
RenderTools()->RenderTee(pIdleState, &RenderInfo, EMOTE_NORMAL, vec2(1, 0.1f), vec2(x + (RealMsgPaddingX + MESSAGE_TEE_SIZE) / 2.0f, y + OffsetTeeY + FullHeightMinusTee / 2.0f + TWSkinUnreliableOffset), Blend);
|
|
|
|
}
|
|
|
|
|
2018-03-13 20:50:49 +00:00
|
|
|
STextRenderColor TextOutline(0.f, 0.f, 0.f, 0.3f * Blend);
|
|
|
|
STextRenderColor Text(1.f, 1.f, 1.f, Blend);
|
2020-10-13 20:08:52 +00:00
|
|
|
TextRender()->RenderTextContainer(m_aLines[r].m_TextContainerIndex, &Text, &TextOutline, 0, (y + RealMsgPaddingY / 2.0f) - m_aLines[r].m_TextYOffset);
|
2014-07-07 13:47:11 +00:00
|
|
|
}
|
2008-08-27 15:48:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
void CChat::Say(int Team, const char *pLine)
|
2008-08-27 15:48:50 +00:00
|
|
|
{
|
2016-08-30 23:39:59 +00:00
|
|
|
m_LastChatSend = time();
|
2012-01-09 22:13:51 +00:00
|
|
|
|
2008-08-27 15:48:50 +00:00
|
|
|
// 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);
|
2008-08-27 15:48:50 +00:00
|
|
|
}
|
2015-08-25 12:24:46 +00:00
|
|
|
|
|
|
|
void CChat::SayChat(const char *pLine)
|
|
|
|
{
|
|
|
|
if(!pLine || str_length(pLine) < 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
bool AddEntry = false;
|
|
|
|
|
2020-09-22 16:02:03 +00:00
|
|
|
if(m_LastChatSend + time_freq() < time())
|
2015-08-25 12:24:46 +00:00
|
|
|
{
|
|
|
|
Say(m_Mode == MODE_ALL ? 0 : 1, pLine);
|
|
|
|
AddEntry = true;
|
|
|
|
}
|
|
|
|
else if(m_PendingChatCounter < 3)
|
|
|
|
{
|
|
|
|
++m_PendingChatCounter;
|
|
|
|
AddEntry = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(AddEntry)
|
|
|
|
{
|
2020-09-22 16:02:03 +00:00
|
|
|
CHistoryEntry *pEntry = m_History.Allocate(sizeof(CHistoryEntry) + str_length(pLine) - 1);
|
2015-08-25 12:24:46 +00:00
|
|
|
pEntry->m_Team = m_Mode == MODE_ALL ? 0 : 1;
|
|
|
|
mem_copy(pEntry->m_aText, pLine, str_length(pLine));
|
|
|
|
}
|
|
|
|
}
|