/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see .
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: InGameChat.cpp ///////////////////////////////////////////////////////////////////////
// Author: Matthew D. Campbell - June 2002
// Desc: GUI callbacks for the in-game chat entry
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/Player.h"
#include "Common/PlayerList.h"
#include "GameClient/DisconnectMenu.h"
#include "GameClient/GameWindow.h"
#include "GameClient/Gadget.h"
#include "GameClient/GadgetTextEntry.h"
#include "GameClient/GadgetStaticText.h"
#include "GameClient/GameClient.h"
#include "GameClient/GameText.h"
#include "GameClient/GUICallbacks.h"
#include "GameClient/InGameUI.h"
#include "GameClient/LanguageFilter.h"
#include "GameLogic/GameLogic.h"
#include "GameNetwork/GameInfo.h"
#include "GameNetwork/NetworkInterface.h"
static GameWindow *chatWindow = NULL;
static GameWindow *chatTextEntry = NULL;
static GameWindow *chatTypeStaticText = NULL;
static UnicodeString s_savedChat;
static InGameChatType inGameChatType;
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void ShowInGameChat( Bool immediate )
{
#if !defined(_PLAYTEST)
if (TheGameLogic->isInReplayGame())
return;
#endif
if (TheInGameUI->isQuitMenuVisible())
return;
if (TheDisconnectMenu && TheDisconnectMenu->isScreenVisible())
return;
if (chatWindow)
{
chatWindow->winHide(FALSE);
chatWindow->winEnable(TRUE);
chatTextEntry->winHide(FALSE);
chatTextEntry->winEnable(TRUE);
GadgetTextEntrySetText( chatTextEntry, s_savedChat );
s_savedChat.clear();
}
else
{
chatWindow = TheWindowManager->winCreateFromScript( AsciiString("InGameChat.wnd") );
static NameKeyType textEntryChatID = TheNameKeyGenerator->nameToKey( "InGameChat.wnd:TextEntryChat" );
chatTextEntry = TheWindowManager->winGetWindowFromId( NULL, textEntryChatID );
GadgetTextEntrySetText( chatTextEntry, UnicodeString::TheEmptyString );
static NameKeyType chatTypeStaticTextID = TheNameKeyGenerator->nameToKey( "InGameChat.wnd:StaticTextChatType" );
chatTypeStaticText = TheWindowManager->winGetWindowFromId( NULL, chatTypeStaticTextID );
}
TheWindowManager->winSetFocus( chatTextEntry );
SetInGameChatType( INGAME_CHAT_EVERYONE );
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void ResetInGameChat( void )
{
if(chatWindow)
TheWindowManager->winDestroy( chatWindow );
chatWindow = NULL;
chatTextEntry = NULL;
chatTypeStaticText = NULL;
s_savedChat.clear();
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void HideInGameChat( Bool immediate )
{
if (chatWindow)
{
s_savedChat = GadgetTextEntryGetText( chatTextEntry );
chatWindow->winHide(TRUE);
chatWindow->winEnable(FALSE);
chatTextEntry->winHide(TRUE);
chatTextEntry->winEnable(FALSE);
TheWindowManager->winSetFocus( NULL );
}
TheWindowManager->winSetFocus( NULL );
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void SetInGameChatType( InGameChatType chatType )
{
inGameChatType = chatType;
if (chatTypeStaticText)
{
switch (inGameChatType)
{
case INGAME_CHAT_EVERYONE:
if (ThePlayerList->getLocalPlayer()->isPlayerActive())
GadgetStaticTextSetText( chatTypeStaticText, TheGameText->fetch("Chat:Everyone") );
else
GadgetStaticTextSetText( chatTypeStaticText, TheGameText->fetch("Chat:Observers") );
break;
case INGAME_CHAT_ALLIES:
GadgetStaticTextSetText( chatTypeStaticText, TheGameText->fetch("Chat:Allies") );
break;
case INGAME_CHAT_PLAYERS:
GadgetStaticTextSetText( chatTypeStaticText, TheGameText->fetch("Chat:Players") );
break;
}
}
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
Bool IsInGameChatActive() {
if (chatWindow != NULL) {
if (chatWindow->winIsHidden() == FALSE) {
return TRUE;
}
}
return FALSE;
}
// Slash commands -------------------------------------------------------------------------
extern "C" {
int getQR2HostingStatus(void);
}
extern int isThreadHosting;
Bool handleInGameSlashCommands(UnicodeString uText)
{
AsciiString message;
message.translate(uText);
if (message.getCharAt(0) != '/')
{
return FALSE; // not a slash command
}
AsciiString remainder = message.str() + 1;
AsciiString token;
remainder.nextToken(&token);
token.toLower();
if (token == "host")
{
UnicodeString s;
s.format(L"Hosting qr2:%d thread:%d", getQR2HostingStatus(), isThreadHosting);
TheInGameUI->message(s);
return TRUE; // was a slash command
}
return FALSE; // not a slash command
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void ToggleInGameChat( Bool immediate )
{
static Bool justHid = false;
if (justHid)
{
justHid = false;
return;
}
#if !defined(_PLAYTEST)
if (TheGameLogic->isInReplayGame())
return;
#endif
if (!TheGameInfo->isMultiPlayer() && TheGlobalData->m_netMinPlayers)
return;
if (chatWindow)
{
Bool show = chatWindow->winIsHidden();
if (show)
ShowInGameChat( immediate );
else
{
if (chatTextEntry)
{
// Send what is there, clear it out, and hide the window
UnicodeString msg = GadgetTextEntryGetText( chatTextEntry );
msg.trim();
if (!msg.isEmpty() && !handleInGameSlashCommands(msg))
{
const Player *localPlayer = ThePlayerList->getLocalPlayer();
AsciiString playerName;
Int playerMask = 0;
for (Int i=0; ifindPlayerWithNameKey( TheNameKeyGenerator->nameToKey( playerName ) );
if (player && localPlayer)
{
switch (inGameChatType)
{
case INGAME_CHAT_EVERYONE:
if (!TheGameInfo->getConstSlot(i)->isMuted())
playerMask |= (1<getRelationship(localPlayer->getDefaultTeam()) == ALLIES &&
localPlayer->getRelationship(player->getDefaultTeam()) == ALLIES) || player==localPlayer )
playerMask |= (1<filterLine(msg);
TheNetwork->sendChat(msg, playerMask);
}
GadgetTextEntrySetText( chatTextEntry, UnicodeString::TheEmptyString );
HideInGameChat( immediate );
justHid = true;
}
}
}
else
{
ShowInGameChat( immediate );
}
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
WindowMsgHandledType InGameChatInput( GameWindow *window, UnsignedInt msg,
WindowMsgData mData1, WindowMsgData mData2 )
{
switch( msg )
{
// --------------------------------------------------------------------------------------------
case GWM_CHAR:
{
UnsignedByte key = mData1;
// UnsignedByte state = mData2;
switch( key )
{
// ----------------------------------------------------------------------------------------
case KEY_ESC:
{
HideInGameChat();
return MSG_HANDLED;
//return MSG_IGNORED;
} // end escape
} // end switch( key )
return MSG_HANDLED;
} // end char
}
return MSG_IGNORED;
} // end InGameChatInput
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
WindowMsgHandledType InGameChatSystem( GameWindow *window, UnsignedInt msg,
WindowMsgData mData1, WindowMsgData mData2 )
{
switch( msg )
{
//---------------------------------------------------------------------------------------------
case GGM_FOCUS_CHANGE:
{
// Bool focus = (Bool) mData1;
//if (focus)
//TheWindowManager->winSetGrabWindow( chatTextEntry );
break;
} // end focus change
//---------------------------------------------------------------------------------------------
case GWM_INPUT_FOCUS:
{
// if we're givin the opportunity to take the keyboard focus we must say we want it
if( mData1 == TRUE )
*(Bool *)mData2 = TRUE;
return MSG_HANDLED;
}//case GWM_INPUT_FOCUS:
//---------------------------------------------------------------------------------------------
case GEM_EDIT_DONE:
{
ToggleInGameChat();
//HideInGameChat();
break;
} // end button selected
//---------------------------------------------------------------------------------------------
case GBM_SELECTED:
{
GameWindow *control = (GameWindow *)mData1;
static NameKeyType buttonClearID = TheNameKeyGenerator->nameToKey( AsciiString( "InGameChat.wnd:ButtonClear" ) );
if (control && control->winGetWindowId() == buttonClearID)
{
if (chatTextEntry)
GadgetTextEntrySetText( chatTextEntry, UnicodeString::TheEmptyString );
s_savedChat.clear();
}
break;
} // end button selected
//---------------------------------------------------------------------------------------------
default:
return MSG_IGNORED;
} // end switch( msg )
return MSG_HANDLED;
} // end InGameChatSystem