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
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);
2021-01-11 21:54:08 +00:00
# include <game/ddracechat.h>
2022-05-23 18:03:07 +00:00
std : : sort ( m_Commands . begin ( ) , m_Commands . end ( ) ) ;
2019-03-19 09:26:30 +00:00
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 )
{
2022-05-23 18:03:07 +00:00
m_Commands . push_back ( CCommand { pName , pParams } ) ;
2019-03-19 09:26:30 +00:00
}
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 ;
2021-09-14 11:15:09 +00:00
m_CompletionUsed = false ;
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 ( ) ;
2021-06-23 05:05:49 +00:00
for ( int64_t & LastSoundPlayed : m_aLastSoundPlayed )
2020-10-26 14:14:07 +00:00
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-12-22 18:24:48 +00:00
void CChat : : ConchainChatOld ( IConsole : : IResult * pResult , void * pUserData , IConsole : : FCommandCallback pfnCallback , void * pCallbackUserData )
2020-10-13 20:08:52 +00:00
{
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-12-22 18:24:48 +00:00
Console ( ) - > Chain ( " cl_chat_old " , ConchainChatOld , 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 ;
2021-12-18 11:23:20 +00:00
if ( Input ( ) - > ModifierIsPressed ( ) & & 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
2021-09-13 09:47:47 +00:00
char aLine [ 256 ] ;
2015-08-26 14:40:59 +00:00
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 ' )
{
2021-09-13 09:47:47 +00:00
int max = minimum ( i - Begin + 1 , ( int ) sizeof ( aLine ) ) ;
2021-12-20 01:34:02 +00:00
str_copy ( aLine , Text + Begin , max ) ;
2020-09-22 16:02:03 +00:00
Begin = i + 1 ;
2021-09-13 09:47:47 +00:00
SayChat ( aLine ) ;
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
}
2021-09-13 09:47:47 +00:00
int max = minimum ( i - Begin + 1 , ( int ) sizeof ( aLine ) ) ;
2021-12-20 01:34:02 +00:00
str_copy ( aLine , Text + Begin , max ) ;
2021-11-24 23:03:35 +00:00
m_Input . Append ( aLine ) ;
2015-08-25 12:24:46 +00:00
}
}
2021-12-18 11:23:20 +00:00
if ( Input ( ) - > ModifierIsPressed ( ) & & Input ( ) - > KeyPress ( KEY_C ) )
2015-08-25 12:24:46 +00:00
{
Input ( ) - > SetClipboardText ( m_Input . GetString ( ) ) ;
}
2021-12-18 11:23:20 +00:00
if ( Input ( ) - > ModifierIsPressed ( ) ) // 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
2021-09-14 11:15:09 +00:00
if ( ! m_CompletionUsed )
2010-12-16 01:31:12 +00:00
{
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
}
2022-03-14 23:56:10 +00:00
if ( ! m_CompletionUsed & & m_aCompletionBuffer [ 0 ] ! = ' / ' )
{
// Create the completion list of player names through which the player can iterate
const char * PlayerName , * FoundInput ;
m_PlayerCompletionListLength = 0 ;
for ( auto & PlayerInfo : m_pClient - > m_Snap . m_paInfoByName )
{
if ( PlayerInfo )
{
PlayerName = m_pClient - > m_aClients [ PlayerInfo - > m_ClientID ] . m_aName ;
FoundInput = str_utf8_find_nocase ( PlayerName , m_aCompletionBuffer ) ;
if ( FoundInput ! = 0 )
{
m_aPlayerCompletionList [ m_PlayerCompletionListLength ] . ClientID = PlayerInfo - > m_ClientID ;
// The score for suggesting a player name is determined by the distance of the search input to the beginning of the player name
m_aPlayerCompletionList [ m_PlayerCompletionListLength ] . Score = ( int ) ( FoundInput - PlayerName ) ;
m_PlayerCompletionListLength + + ;
}
}
}
std : : stable_sort ( m_aPlayerCompletionList , m_aPlayerCompletionList + m_PlayerCompletionListLength ,
[ ] ( const CRateablePlayer & p1 , const CRateablePlayer & p2 ) - > bool {
return p1 . Score < p2 . Score ;
} ) ;
}
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
2021-09-14 11:15:09 +00:00
if ( m_ReverseTAB & & m_CompletionUsed )
m_CompletionChosen - - ;
else if ( ! m_ReverseTAB )
m_CompletionChosen + + ;
m_CompletionChosen = ( m_CompletionChosen + 2 * NumCommands ) % ( 2 * NumCommands ) ;
m_CompletionUsed = true ;
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 ] ;
2022-03-13 10:42:45 +00:00
if ( str_startswith ( Command . pName , pCommandStart ) )
2019-03-19 09:26:30 +00:00
{
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 ;
2022-03-14 23:56:10 +00:00
if ( m_PlayerCompletionListLength > 0 )
2014-10-06 11:02:55 +00:00
{
2022-03-14 23:56:10 +00:00
// We do this in a loop, if a player left the game during the repeated pressing of Tab, they are skipped
CGameClient : : CClientData * CompletionClientData ;
for ( int i = 0 ; i < m_PlayerCompletionListLength ; + + i )
2019-03-19 09:26:30 +00:00
{
2022-03-14 23:56:10 +00:00
if ( m_ReverseTAB & & m_CompletionUsed )
{
m_CompletionChosen - - ;
}
else if ( ! m_ReverseTAB )
{
m_CompletionChosen + + ;
}
if ( m_CompletionChosen < 0 )
{
m_CompletionChosen + = m_PlayerCompletionListLength ;
}
m_CompletionChosen % = m_PlayerCompletionListLength ;
m_CompletionUsed = true ;
CompletionClientData = & m_pClient - > m_aClients [ m_aPlayerCompletionList [ m_CompletionChosen ] . ClientID ] ;
if ( ! CompletionClientData - > m_Active )
{
continue ;
}
pCompletionString = CompletionClientData - > m_aName ;
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 )
2021-09-14 11:15:09 +00:00
{
2014-10-06 11:02:55 +00:00
if ( Event . m_Key ! = KEY_LSHIFT )
2021-09-14 11:15:09 +00:00
{
2014-10-06 11:02:55 +00:00
m_CompletionChosen = - 1 ;
2021-09-14 11:15:09 +00:00
m_CompletionUsed = false ;
}
}
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 ;
2021-09-14 11:15:09 +00:00
m_CompletionUsed = false ;
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 )
{
2021-06-28 20:51:14 +00:00
const char * pHL = str_utf8_find_nocase ( pLine , pName ) ;
2015-07-22 13:37:00 +00:00
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
2021-01-28 14:36:30 +00:00
if ( Length > 0 & & ( pLine = = pHL | | pHL [ - 1 ] = = ' ' ) & & ( pHL [ Length ] = = 0 | | pHL [ Length ] = = ' ' | | pHL [ Length ] = = ' . ' | | pHL [ Length ] = = ' ! ' | | pHL [ Length ] = = ' , ' | | pHL [ Length ] = = ' ? ' | | pHL [ Length ] = = ' : ' ) )
2015-07-22 13:37:00 +00:00
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 ;
2022-03-20 11:57:50 +00:00
auto & & FChatMsgCheckAndPrint = [ this ] ( CLine * pLine_ ) {
if ( pLine_ - > m_ClientID < 0 ) // server or client message
2021-03-01 17:35:06 +00:00
{
if ( Client ( ) - > State ( ) ! = IClient : : STATE_DEMOPLAYBACK )
2022-03-20 11:57:50 +00:00
StoreSave ( pLine_ - > m_aText ) ;
2021-03-01 17:35:06 +00:00
}
char aBuf [ 1024 ] ;
2022-03-20 11:57:50 +00:00
str_format ( aBuf , sizeof ( aBuf ) , " %s%s%s " , pLine_ - > m_aName , pLine_ - > m_ClientID > = 0 ? " : " : " " , pLine_ - > m_aText ) ;
2021-03-08 00:08:38 +00:00
ColorRGBA ChatLogColor { 1 , 1 , 1 , 1 } ;
2022-03-20 11:57:50 +00:00
if ( pLine_ - > m_Highlighted )
2021-03-08 00:08:38 +00:00
{
ChatLogColor = color_cast < ColorRGBA > ( ColorHSLA ( g_Config . m_ClMessageHighlightColor ) ) ;
}
else
{
2022-03-20 11:57:50 +00:00
if ( pLine_ - > m_Friend & & g_Config . m_ClMessageFriend )
2021-03-08 00:08:38 +00:00
ChatLogColor = color_cast < ColorRGBA > ( ColorHSLA ( g_Config . m_ClMessageFriendColor ) ) ;
2022-03-20 11:57:50 +00:00
else if ( pLine_ - > m_Team )
2021-03-08 00:08:38 +00:00
ChatLogColor = color_cast < ColorRGBA > ( ColorHSLA ( g_Config . m_ClMessageTeamColor ) ) ;
2022-03-20 11:57:50 +00:00
else if ( pLine_ - > m_ClientID = = - 1 ) // system
2021-03-08 00:08:38 +00:00
ChatLogColor = color_cast < ColorRGBA > ( ColorHSLA ( g_Config . m_ClMessageSystemColor ) ) ;
2022-03-20 11:57:50 +00:00
else if ( pLine_ - > m_ClientID = = - 2 ) // client
2021-03-08 00:08:38 +00:00
ChatLogColor = color_cast < ColorRGBA > ( ColorHSLA ( g_Config . m_ClMessageClientColor ) ) ;
else // regular message
ChatLogColor = color_cast < ColorRGBA > ( ColorHSLA ( g_Config . m_ClMessageColor ) ) ;
}
2022-03-20 11:57:50 +00:00
Console ( ) - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , pLine_ - > m_Whisper ? " whisper " : ( pLine_ - > m_Team ? " teamchat " : " chat " ) , aBuf , ChatLogColor ) ;
2021-03-01 17:35:06 +00:00
} ;
2021-01-28 10:32:38 +00:00
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 ] ;
2022-03-29 20:39:16 +00:00
// Team Number:
2022-03-29 20:35:50 +00:00
// 0 = global; 1 = team; 2 = sending whisper; 3 = receiving whisper
2020-09-20 15:25:27 +00:00
// If it's a client message, m_aText will have ": " prepended so we have to work around it.
2022-03-29 20:35:50 +00:00
if ( pCurrentLine - > m_TeamNumber = = 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 ;
2021-03-01 17:35:06 +00:00
FChatMsgCheckAndPrint ( pCurrentLine ) ;
return ;
2020-09-20 15:25:27 +00:00
}
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 ;
2022-03-29 20:35:50 +00:00
pCurrentLine - > m_TeamNumber = Team ;
pCurrentLine - > m_Team = Team = = 1 ;
pCurrentLine - > m_Whisper = Team > = 2 ;
2020-09-20 15:25:27 +00:00
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
{
2021-01-28 10:21:55 +00:00
if ( ClientID > = 0 & & 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
2021-03-01 17:35:06 +00:00
if ( pCurrentLine - > m_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 ) ;
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 ;
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 ;
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 ' )
{
2020-11-05 12:13:05 +00:00
if ( ! g_Config . m_ClChatOld )
2020-10-13 20:08:52 +00:00
{
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 ;
2020-11-08 05:39:16 +00:00
pCurrentLine - > m_RenderSkinMetrics = m_pClient - > m_aClients [ pCurrentLine - > m_ClientID ] . m_RenderInfo . m_SkinMetrics ;
2020-10-13 20:08:52 +00:00
pCurrentLine - > m_HasRenderTee = true ;
}
}
2021-03-01 17:35:06 +00:00
FChatMsgCheckAndPrint ( pCurrentLine ) ;
2021-01-28 10:32:38 +00:00
}
2009-06-15 13:01:04 +00:00
// play sound
2021-06-23 05:05:49 +00:00
int64_t Now = time ( ) ;
2016-08-30 23:39:59 +00:00
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 )
{
2021-07-12 09:43:56 +00:00
m_pClient - > m_Sounds . Play ( CSounds : : CHN_GUI , SOUND_CHAT_SERVER , 0 ) ;
2014-10-25 00:52:08 +00:00
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 )
{
2021-07-12 09:43:56 +00:00
m_pClient - > m_Sounds . Play ( CSounds : : CHN_GUI , SOUND_CHAT_HIGHLIGHT , 0 ) ;
2014-10-25 00:52:08 +00:00
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
{
2021-11-05 16:00:39 +00:00
bool PlaySound = m_aLines [ m_CurrentLine ] . m_Team ? g_Config . m_SndTeamChat : g_Config . m_SndChat ;
2021-08-03 13:13:54 +00:00
# if defined(CONF_VIDEORECORDER)
if ( IVideo : : Current ( ) )
{
2022-03-01 19:23:29 +00:00
PlaySound & = ( bool ) g_Config . m_ClVideoShowChat ;
2021-08-03 13:13:54 +00:00
}
# endif
if ( PlaySound )
2014-05-04 16:35:37 +00:00
{
2021-07-12 09:43:56 +00:00
m_pClient - > m_Sounds . Play ( CSounds : : CHN_GUI , SOUND_CHAT_CLIENT , 0 ) ;
2014-05-04 16:35:37 +00:00
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 ( )
{
2021-02-15 14:15:21 +00:00
for ( auto & Line : m_aLines )
2020-10-13 20:08:52 +00:00
{
2021-02-15 14:15:21 +00:00
if ( Line . m_HasRenderTee )
2020-10-13 20:08:52 +00:00
{
2021-07-12 09:43:56 +00:00
const CSkin * pSkin = m_pClient - > m_Skins . Get ( m_pClient - > m_Skins . Find ( Line . m_aSkinName ) ) ;
2021-02-15 14:15:21 +00:00
if ( Line . m_CustomColoredSkin )
Line . m_RenderSkin = pSkin - > m_ColorableSkin ;
2020-10-13 20:08:52 +00:00
else
2021-02-15 14:15:21 +00:00
Line . m_RenderSkin = pSkin - > m_OriginalSkin ;
2020-11-08 05:39:16 +00:00
2021-02-15 14:15:21 +00:00
Line . m_RenderSkinMetrics = pSkin - > m_Metrics ;
2020-10-13 20:08:52 +00:00
}
}
}
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-07 18:05:23 +00:00
float ScreenRatio = Graphics ( ) - > ScreenAspect ( ) ;
2021-07-12 09:43:56 +00:00
bool IsScoreBoardOpen = m_pClient - > m_Scoreboard . Active ( ) & & ( ScreenRatio > 1.7f ) ; // only assume scoreboard when screen ratio is widescreen(something around 16:9)
2020-11-01 03:52:07 +00:00
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 ;
2020-11-05 12:13:05 +00:00
if ( g_Config . m_ClChatOld )
2020-10-13 20:08:52 +00:00
{
RealMsgPaddingX = 0 ;
2020-11-05 12:13:05 +00:00
RealMsgPaddingY = 0 ;
2020-10-13 20:08:52 +00:00
RealMsgPaddingTee = 0 ;
2020-11-05 12:13:05 +00:00
}
2020-10-13 20:08:52 +00:00
2021-06-23 05:05:49 +00:00
int64_t Now = time ( ) ;
2020-11-07 18:05:23 +00:00
float LineWidth = ( IsScoreBoardOpen ? 85.0f : 200.0f ) - ( RealMsgPaddingX * 1.5f ) - 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 )
2020-12-27 00:19:36 +00:00
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-11-05 12:13:05 +00:00
if ( g_Config . m_ClChatOld )
2020-10-13 20:08:52 +00:00
{
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
2021-10-14 14:49:11 +00:00
if ( ! IsScoreBoardOpen & & ! g_Config . m_ClChatOld )
2020-11-01 03:52:07 +00:00
{
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 ;
2021-10-14 14:49:11 +00:00
if ( ! IsScoreBoardOpen & & ! g_Config . m_ClChatOld )
2020-11-01 03:52:07 +00:00
{
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 ) ;
2020-11-05 12:13:05 +00:00
if ( ! g_Config . m_ClChatOld & & ( m_aLines [ r ] . m_aText [ 0 ] ! = ' \0 ' | | m_aLines [ r ] . m_aName [ 0 ] ! = ' \0 ' ) )
2020-10-13 20:08:52 +00:00
{
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 ) ;
2021-07-12 09:43:56 +00:00
if ( m_pClient - > m_GameConsole . IsClosed ( ) )
2020-09-22 16:01:13 +00:00
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 ( ) ;
2020-11-07 18:05:23 +00:00
float ScreenRatio = Graphics ( ) - > ScreenAspect ( ) ;
2021-07-12 09:43:56 +00:00
bool IsScoreBoardOpen = m_pClient - > m_Scoreboard . Active ( ) & & ( ScreenRatio > 1.7f ) ; // only assume scoreboard when screen ratio is widescreen(something around 16:9)
2020-11-07 18:05:23 +00:00
2021-06-23 05:05:49 +00:00
int64_t Now = time ( ) ;
2020-11-07 18:05:23 +00:00
float HeightLimit = IsScoreBoardOpen ? 180.0f : m_PrevShowChat ? 50.0f : 200.0f ;
int OffsetType = IsScoreBoardOpen ? 1 : 0 ;
2020-10-13 20:08:52 +00:00
float RealMsgPaddingX = MESSAGE_PADDING_X ;
float RealMsgPaddingY = MESSAGE_PADDING_Y ;
2020-11-05 12:13:05 +00:00
if ( g_Config . m_ClChatOld )
2020-10-13 20:08:52 +00:00
{
RealMsgPaddingX = 0 ;
2020-11-05 12:13:05 +00:00
RealMsgPaddingY = 0 ;
2020-10-13 20:08:52 +00:00
}
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
2020-11-05 12:13:05 +00:00
if ( ! g_Config . m_ClChatOld )
2020-10-13 20:08:52 +00:00
{
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-11-05 12:13:05 +00:00
if ( ! g_Config . m_ClChatOld & & m_aLines [ r ] . m_HasRenderTee )
2020-10-13 20:08:52 +00:00
{
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 ;
2020-11-08 05:39:16 +00:00
RenderInfo . m_SkinMetrics = m_aLines [ r ] . m_RenderSkinMetrics ;
2020-10-13 20:08:52 +00:00
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 ;
CAnimState * pIdleState = CAnimState : : GetIdle ( ) ;
2020-11-08 05:39:16 +00:00
vec2 OffsetToMid ;
RenderTools ( ) - > GetRenderTeeOffsetToRenderedTee ( pIdleState , & RenderInfo , OffsetToMid ) ;
vec2 TeeRenderPos ( x + ( RealMsgPaddingX + MESSAGE_TEE_SIZE ) / 2.0f , y + OffsetTeeY + FullHeightMinusTee / 2.0f + OffsetToMid . y ) ;
RenderTools ( ) - > RenderTee ( pIdleState , & RenderInfo , EMOTE_NORMAL , vec2 ( 1 , 0.1f ) , TeeRenderPos , Blend ) ;
2020-10-13 20:08:52 +00:00
}
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 ;
2022-01-21 00:54:14 +00:00
Client ( ) - > SendPackMsgActive ( & 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 ) ) ;
}
}