/*
** 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: Diplomacy.cpp ///////////////////////////////////////////////////////////////////////
// Author: Matthew D. Campbell - August 2002
// Desc: GUI callbacks for the diplomacy menu
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/GlobalData.h"
#include "Common/MultiplayerSettings.h"
#include "Common/Player.h"
#include "Common/PlayerList.h"
#include "Common/PlayerTemplate.h"
#include "Common/Recorder.h"
#include "GameClient/AnimateWindowManager.h"
#include "GameClient/Diplomacy.h"
#include "GameClient/DisconnectMenu.h"
#include "GameClient/GameWindow.h"
#include "GameClient/Gadget.h"
#include "GameClient/GadgetCheckBox.h"
#include "GameClient/GadgetListBox.h"
#include "GameClient/GadgetTextEntry.h"
#include "GameClient/GadgetStaticText.h"
#include "GameClient/GadgetRadioButton.h"
#include "GameClient/GameClient.h"
#include "GameClient/GameText.h"
#include "GameClient/GUICallbacks.h"
#include "GameClient/InGameUI.h"
#include "GameLogic/GameLogic.h"
#include "GameLogic/VictoryConditions.h"
#include "GameNetwork/GameInfo.h"
#include "GameNetwork/NetworkInterface.h"
#include "GameNetwork/GameSpy/BuddyDefs.h"
#include "GameNetwork/GameSpy/peerDefs.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
//-------------------------------------------------------------------------------------------------
static NameKeyType staticTextPlayerID[MAX_SLOTS];
static NameKeyType staticTextSideID[MAX_SLOTS];
static NameKeyType staticTextTeamID[MAX_SLOTS];
static NameKeyType staticTextStatusID[MAX_SLOTS];
static NameKeyType buttonMuteID[MAX_SLOTS];
static NameKeyType buttonUnMuteID[MAX_SLOTS];
static NameKeyType radioButtonInGameID = NAMEKEY_INVALID;
static NameKeyType radioButtonBuddiesID = NAMEKEY_INVALID;
static GameWindow *radioButtonInGame = NULL;
static GameWindow *radioButtonBuddies = NULL;
static NameKeyType winInGameID = NAMEKEY_INVALID;
static NameKeyType winBuddiesID = NAMEKEY_INVALID;
static NameKeyType winSoloID = NAMEKEY_INVALID;
static GameWindow *winInGame = NULL;
static GameWindow *winBuddies = NULL;
static GameWindow *winSolo = NULL;
static GameWindow *staticTextPlayer[MAX_SLOTS] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
static GameWindow *staticTextSide[MAX_SLOTS] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
static GameWindow *staticTextTeam[MAX_SLOTS] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
static GameWindow *staticTextStatus[MAX_SLOTS] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
static GameWindow *buttonMute[MAX_SLOTS] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
static GameWindow *buttonUnMute[MAX_SLOTS] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
static Int slotNumInRow[MAX_SLOTS];
//-------------------------------------------------------------------------------------------------
static WindowLayout *theLayout = NULL;
static GameWindow *theWindow = NULL;
static AnimateWindowManager *theAnimateWindowManager = NULL;
WindowMsgHandledType BuddyControlSystem( GameWindow *window, UnsignedInt msg,
WindowMsgData mData1, WindowMsgData mData2);
void InitBuddyControls(Int type);
void updateBuddyInfo( void );
static void grabWindowPointers( void )
{
for (Int i=0; iwinGetWindowFromId(theWindow, staticTextPlayerID[i]);
staticTextSide[i] = TheWindowManager->winGetWindowFromId(theWindow, staticTextSideID[i]);
staticTextTeam[i] = TheWindowManager->winGetWindowFromId(theWindow, staticTextTeamID[i]);
staticTextStatus[i] = TheWindowManager->winGetWindowFromId(theWindow, staticTextStatusID[i]);
buttonMute[i] = TheWindowManager->winGetWindowFromId(theWindow, buttonMuteID[i]);
buttonUnMute[i] = TheWindowManager->winGetWindowFromId(theWindow, buttonUnMuteID[i]);
slotNumInRow[i] = -1;
}
}
static void releaseWindowPointers( void )
{
for (Int i=0; im_animateWindows)
{
Bool wasFinished = theAnimateWindowManager->isFinished();
theAnimateWindowManager->update();
if (theAnimateWindowManager->isFinished() && !wasFinished && theAnimateWindowManager->isReversed())
theWindow->winHide( TRUE );
}
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
static BriefingList theBriefingList;
//-------------------------------------------------------------------------------------------------
BriefingList* GetBriefingTextList(void)
{
return &theBriefingList;
}
//-------------------------------------------------------------------------------------------------
void UpdateDiplomacyBriefingText(AsciiString newText, Bool clear)
{
GameWindow *listboxSolo = TheWindowManager->winGetWindowFromId(theWindow, NAMEKEY("Diplomacy.wnd:ListboxSolo"));
if (clear)
{
theBriefingList.clear();
if (listboxSolo)
GadgetListBoxReset(listboxSolo);
}
if (newText.isEmpty())
return;
if (std::find(theBriefingList.begin(), theBriefingList.end(), newText) != theBriefingList.end())
return;
theBriefingList.push_back(newText);
if (!listboxSolo)
return;
UnicodeString translated = TheGameText->fetch(newText);
Int numEntries = GadgetListBoxGetNumEntries(listboxSolo);
GadgetListBoxAddEntryText(listboxSolo, translated, TheInGameUI->getMessageColor(numEntries%2), -1);
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void ShowDiplomacy( Bool immediate )
{
if (!TheInGameUI->getInputEnabled() || TheGameLogic->isIntroMoviePlaying() ||
TheGameLogic->isLoadingGame())
return;
if (TheInGameUI->isQuitMenuVisible())
return;
if (TheDisconnectMenu && TheDisconnectMenu->isScreenVisible())
return;
if (theWindow)
{
theWindow->winHide(FALSE);
theWindow->winEnable(TRUE);
}
else
{
theLayout = TheWindowManager->winCreateLayout( "Diplomacy.wnd" );
theWindow = theLayout->getFirstWindow();
theLayout->setUpdate(updateFunc);
theAnimateWindowManager = NEW AnimateWindowManager;
radioButtonInGameID = TheNameKeyGenerator->nameToKey("Diplomacy.wnd:RadioButtonInGame");
radioButtonBuddiesID = TheNameKeyGenerator->nameToKey("Diplomacy.wnd:RadioButtonBuddies");
radioButtonInGame = TheWindowManager->winGetWindowFromId(NULL, radioButtonInGameID);
radioButtonBuddies = TheWindowManager->winGetWindowFromId(NULL, radioButtonBuddiesID);
winInGameID = TheNameKeyGenerator->nameToKey("Diplomacy.wnd:InGameParent");
winBuddiesID = TheNameKeyGenerator->nameToKey("Diplomacy.wnd:BuddiesParent");
winSoloID = TheNameKeyGenerator->nameToKey("Diplomacy.wnd:SoloParent");
winInGame = TheWindowManager->winGetWindowFromId(NULL, winInGameID);
winBuddies = TheWindowManager->winGetWindowFromId(NULL, winBuddiesID);
winSolo = TheWindowManager->winGetWindowFromId(NULL, winSoloID);
if (!TheRecorder->isMultiplayer())
{
GameWindow *listboxSolo = TheWindowManager->winGetWindowFromId(theWindow, NAMEKEY("Diplomacy.wnd:ListboxSolo"));
if (listboxSolo)
{
for (BriefingList::iterator it = theBriefingList.begin(); it != theBriefingList.end(); ++it)
{
UnicodeString translated = TheGameText->fetch(*it);
Int numEntries = GadgetListBoxGetNumEntries(listboxSolo);
GadgetListBoxAddEntryText(listboxSolo, translated, TheInGameUI->getMessageColor(numEntries%2), -1);
}
}
}
}
theLayout->hide(FALSE);
radioButtonInGame->winHide(TRUE);
radioButtonBuddies->winHide(TRUE);
GadgetRadioSetSelection(radioButtonInGame, FALSE);
if (TheRecorder->isMultiplayer())
{
winInGame->winHide(FALSE);
winBuddies->winHide(TRUE);
winSolo->winHide(TRUE);
}
else
{
winInGame->winHide(TRUE);
winBuddies->winHide(TRUE);
winSolo->winHide(FALSE);
}
theAnimateWindowManager->reset();
if (!immediate && TheGlobalData->m_animateWindows)
theAnimateWindowManager->registerGameWindow( theWindow, WIN_ANIMATION_SLIDE_TOP, TRUE, 200 );
TheInGameUI->registerWindowLayout(theLayout);
grabWindowPointers();
PopulateInGameDiplomacyPopup();
if(TheGameSpyInfo && TheGameSpyInfo->getLocalProfileID() != 0)
{
radioButtonInGame->winHide(FALSE);
radioButtonBuddies->winHide(FALSE);
InitBuddyControls(1);
PopulateOldBuddyMessages();
updateBuddyInfo();
}
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void ResetDiplomacy( void )
{
if(theLayout)
{
TheInGameUI->unregisterWindowLayout(theLayout);
theLayout->destroyWindows();
theLayout->deleteInstance();
InitBuddyControls(-1);
}
theLayout = NULL;
theWindow = NULL;
if (theAnimateWindowManager)
delete theAnimateWindowManager;
theAnimateWindowManager = NULL;
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void HideDiplomacy( Bool immediate )
{
releaseWindowPointers();
if (theWindow)
{
if (immediate || !TheGlobalData->m_animateWindows)
{
theWindow->winHide(TRUE);
theWindow->winEnable(FALSE);
}
else
{
if (theAnimateWindowManager->isFinished())
theAnimateWindowManager->reverseAnimateWindow();
}
}
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void ToggleDiplomacy( Bool immediate )
{
// If we bring this up, let's hide the quit menu
HideQuitMenu();
if (theWindow)
{
Bool show = theWindow->winIsHidden();
if (show)
ShowDiplomacy( immediate );
else
HideDiplomacy( immediate );
}
else
{
ShowDiplomacy( immediate );
}
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
WindowMsgHandledType DiplomacyInput( GameWindow *window, UnsignedInt msg,
WindowMsgData mData1, WindowMsgData mData2 )
{
switch( msg )
{
// --------------------------------------------------------------------------------------------
case GWM_CHAR:
{
UnsignedByte key = mData1;
// UnsignedByte state = mData2;
switch( key )
{
// ----------------------------------------------------------------------------------------
case KEY_ESC:
{
HideDiplomacy();
return MSG_HANDLED;
//return MSG_IGNORED;
} // end escape
} // end switch( key )
return MSG_HANDLED;
} // end char
}
return MSG_IGNORED;
} // end DiplomacyInput
//-------------------------------------------------------------------------------------------------
WindowMsgHandledType DiplomacySystem( GameWindow *window, UnsignedInt msg,
WindowMsgData mData1, WindowMsgData mData2 )
{
if(BuddyControlSystem(window, msg, mData1, mData2) == MSG_HANDLED)
{
return MSG_HANDLED;
}
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 given the opportunity to take the keyboard focus we must say we don't want it
if( mData1 == TRUE )
*(Bool *)mData2 = FALSE;
return MSG_HANDLED;
}//case GWM_INPUT_FOCUS:
//---------------------------------------------------------------------------------------------
case GBM_SELECTED:
{
GameWindow *control = (GameWindow *)mData1;
NameKeyType controlID = (NameKeyType)control->winGetWindowId();
static NameKeyType buttonHideID = NAMEKEY( "Diplomacy.wnd:ButtonHide" );
if (controlID == buttonHideID)
{
HideDiplomacy( FALSE );
}
else if( controlID == radioButtonInGameID)
{
winInGame->winHide(FALSE);
winBuddies->winHide(TRUE);
}
else if( controlID == radioButtonBuddiesID)
{
winInGame->winHide(TRUE);
winBuddies->winHide(FALSE);
}
for (Int i=0; i= 0)
{
TheGameInfo->getSlot(slotNumInRow[i])->mute(TRUE);
PopulateInGameDiplomacyPopup();
break;
}
if (controlID == buttonUnMuteID[i] && slotNumInRow[i] >= 0)
{
TheGameInfo->getSlot(slotNumInRow[i])->mute(FALSE);
PopulateInGameDiplomacyPopup();
break;
}
}
break;
} // end button selected
//---------------------------------------------------------------------------------------------
default:
return MSG_IGNORED;
} // end switch( msg )
return MSG_HANDLED;
} // end DiplomacySystem
void PopulateInGameDiplomacyPopup( void )
{
if (!TheGameInfo)
return;
Int rowNum = 0;
for (Int slotNum=0; slotNumgetConstSlot(slotNum);
if (slot && slot->isOccupied())
{
Bool isInGame = false;
// Note - for skirmish, TheNetwork == NULL. jba.
if (TheNetwork && TheNetwork->isPlayerConnected(slotNum)) {
isInGame = true;
} else if ((TheNetwork == NULL) && slot->isHuman()) {
// this is a skirmish game and it is the human player.
isInGame = true;
}
if (slot->isAI())
isInGame = true;
AsciiString playerName;
playerName.format("player%d", slotNum);
Player *player = ThePlayerList->findPlayerWithNameKey(NAMEKEY(playerName));
Bool isAlive = !TheVictoryConditions->hasSinglePlayerBeenDefeated(player);
Bool isObserver = player->isPlayerObserver();
if (slot->isHuman() && TheGameInfo->getLocalSlotNum() != slotNum && isInGame)
{
// show mute button
if (buttonMute[rowNum])
{
buttonMute[rowNum]->winHide(slot->isMuted());
}
if (buttonUnMute[rowNum])
{
buttonUnMute[rowNum]->winHide(!slot->isMuted());
}
}
else
{
// can't mute self, AI players, or MIA humans
if (buttonMute[rowNum])
buttonMute[rowNum]->winHide(TRUE);
if (buttonUnMute[rowNum])
buttonUnMute[rowNum]->winHide(TRUE);
}
Color playerColor = TheMultiplayerSettings->getColor(slot->getApparentColor())->getColor();
Color backColor = GameMakeColor(0, 0, 0, 255);
Color aliveColor = GameMakeColor(0, 255, 0, 255);
Color deadColor = GameMakeColor(255, 0, 0, 255);
Color observerInGameColor = GameMakeColor(255, 255, 255, 255);
Color goneColor = GameMakeColor(196, 0, 0, 255);
Color observerGoneColor = GameMakeColor(196, 196, 196, 255);
if (staticTextPlayer[rowNum])
{
staticTextPlayer[rowNum]->winSetEnabledTextColors( playerColor, backColor );
GadgetStaticTextSetText(staticTextPlayer[rowNum], slot->getName());
}
if (staticTextSide[rowNum])
{
staticTextSide[rowNum]->winSetEnabledTextColors( playerColor, backColor );
GadgetStaticTextSetText(staticTextSide[rowNum], slot->getApparentPlayerTemplateDisplayName() );
}
if (staticTextTeam[rowNum])
{
staticTextTeam[rowNum]->winSetEnabledTextColors( playerColor, backColor );
AsciiString teamStr;
teamStr.format("Team:%d", slot->getTeamNumber() + 1);
if (slot->isAI() && slot->getTeamNumber() == -1)
teamStr = "Team:AI";
GadgetStaticTextSetText(staticTextTeam[rowNum], TheGameText->fetch(teamStr) );
}
if (staticTextStatus[rowNum])
{
staticTextStatus[rowNum]->winHide(FALSE);
if (isInGame)
{
if (isAlive)
{
staticTextStatus[rowNum]->winSetEnabledTextColors( aliveColor, backColor );
GadgetStaticTextSetText(staticTextStatus[rowNum], TheGameText->fetch("GUI:PlayerAlive"));
}
else
{
if (isObserver)
{
staticTextStatus[rowNum]->winSetEnabledTextColors( observerInGameColor, backColor );
GadgetStaticTextSetText(staticTextStatus[rowNum], TheGameText->fetch("GUI:PlayerObserver"));
}
else
{
staticTextStatus[rowNum]->winSetEnabledTextColors( deadColor, backColor );
GadgetStaticTextSetText(staticTextStatus[rowNum], TheGameText->fetch("GUI:PlayerDead"));
}
}
}
else
{
// not in game
if (isObserver)
{
staticTextStatus[rowNum]->winSetEnabledTextColors( observerGoneColor, backColor );
GadgetStaticTextSetText(staticTextStatus[rowNum], TheGameText->fetch("GUI:PlayerObserverGone"));
}
else
{
staticTextStatus[rowNum]->winSetEnabledTextColors( goneColor, backColor );
GadgetStaticTextSetText(staticTextStatus[rowNum], TheGameText->fetch("GUI:PlayerGone"));
}
}
}
slotNumInRow[rowNum++] = slotNum;
}
}
while (rowNum < MAX_SLOTS)
{
slotNumInRow[rowNum] = -1;
if (staticTextPlayer[rowNum])
staticTextPlayer[rowNum]->winHide(TRUE);
if (staticTextSide[rowNum])
staticTextSide[rowNum]->winHide(TRUE);
if (staticTextTeam[rowNum])
staticTextTeam[rowNum]->winHide(TRUE);
if (staticTextStatus[rowNum])
staticTextStatus[rowNum]->winHide(TRUE);
if (buttonMute[rowNum])
buttonMute[rowNum]->winHide(TRUE);
if (buttonUnMute[rowNum])
buttonUnMute[rowNum]->winHide(TRUE);
++rowNum;
}
}