VictoryConditions.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: VictoryConditions.cpp //////////////////////////////////////////////////////
  24. // Generals multiplayer victory condition specifications
  25. // Author: Matthew D. Campbell, February 2002
  26. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  27. #include "Common/AudioEventRTS.h"
  28. #include "Common/GameAudio.h"
  29. #include "Common/GameCommon.h"
  30. #include "Common/GameEngine.h"
  31. #include "Common/KindOf.h"
  32. #include "Common/PlayerList.h"
  33. #include "Common/Player.h"
  34. #include "Common/PlayerTemplate.h"
  35. #include "Common/Radar.h"
  36. #include "Common/Recorder.h"
  37. #include "GameClient/InGameUI.h"
  38. #include "GameClient/Diplomacy.h"
  39. #include "GameClient/GameText.h"
  40. #include "GameClient/GUICallbacks.h"
  41. #include "GameClient/MessageBox.h"
  42. #include "GameClient/GameClient.h"
  43. #include "GameLogic/GameLogic.h"
  44. #include "GameLogic/PartitionManager.h"
  45. #include "GameLogic/ScriptActions.h"
  46. #include "GameLogic/VictoryConditions.h"
  47. #include "GameNetwork/GameInfo.h"
  48. #include "GameNetwork/NetworkDefs.h"
  49. #ifdef _INTERNAL
  50. // for occasional debugging...
  51. //#pragma optimize("", off)
  52. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  53. #endif
  54. //-------------------------------------------------------------------------------------------------
  55. #define ISSET(x) (m_victoryConditions & VICTORY_##x)
  56. //-------------------------------------------------------------------------------------------------
  57. VictoryConditionsInterface *TheVictoryConditions = NULL;
  58. //-------------------------------------------------------------------------------------------------
  59. inline static Bool areAllies(const Player *p1, const Player *p2)
  60. {
  61. if (p1 != p2 &&
  62. p1->getRelationship(p2->getDefaultTeam()) == ALLIES &&
  63. p2->getRelationship(p1->getDefaultTeam()) == ALLIES)
  64. return true;
  65. return false;
  66. }
  67. //-------------------------------------------------------------------------------------------------
  68. class VictoryConditions : public VictoryConditionsInterface
  69. {
  70. public:
  71. VictoryConditions();
  72. void init( void );
  73. void reset( void );
  74. void update( void );
  75. Bool hasAchievedVictory(Player *player); ///< has a specific player and his allies won?
  76. Bool hasBeenDefeated(Player *player); ///< has a specific player and his allies lost?
  77. Bool hasSinglePlayerBeenDefeated(Player *player); ///< has a specific player lost?
  78. void cachePlayerPtrs( void ); ///< players have been created - cache the ones of interest
  79. Bool isLocalAlliedVictory( void ); ///< convenience function
  80. Bool isLocalAlliedDefeat( void ); ///< convenience function
  81. Bool isLocalDefeat( void ); ///< convenience function
  82. Bool amIObserver( void ) { return m_isObserver;} ///< Am I an observer?( need this for scripts )
  83. virtual UnsignedInt getEndFrame( void ) { return m_endFrame; } ///< on which frame was the game effectively over?
  84. private:
  85. Player* m_players[MAX_PLAYER_COUNT];
  86. Int m_localSlotNum;
  87. UnsignedInt m_endFrame;
  88. Bool m_isDefeated[MAX_PLAYER_COUNT];
  89. Bool m_localPlayerDefeated; ///< prevents condition from being signaled each frame
  90. Bool m_singleAllianceRemaining; ///< prevents condition from being signaled each frame
  91. Bool m_isObserver;
  92. };
  93. //-------------------------------------------------------------------------------------------------
  94. VictoryConditionsInterface * createVictoryConditions( void )
  95. {
  96. // only one created, so no MemoryPool usage
  97. return NEW VictoryConditions;
  98. }
  99. //-------------------------------------------------------------------------------------------------
  100. VictoryConditions::VictoryConditions()
  101. {
  102. reset();
  103. }
  104. //-------------------------------------------------------------------------------------------------
  105. void VictoryConditions::init( void )
  106. {
  107. reset();
  108. }
  109. //-------------------------------------------------------------------------------------------------
  110. void VictoryConditions::reset( void )
  111. {
  112. for (Int i=0; i<MAX_PLAYER_COUNT; ++i)
  113. {
  114. m_players[i] = NULL;
  115. m_isDefeated[i] = false;
  116. }
  117. m_localSlotNum = -1;
  118. m_localPlayerDefeated = false;
  119. m_singleAllianceRemaining = false;
  120. m_isObserver = false;
  121. m_endFrame = 0;
  122. m_victoryConditions = VICTORY_NOBUILDINGS | VICTORY_NOUNITS;
  123. }
  124. //-------------------------------------------------------------------------------------------------
  125. void VictoryConditions::update( void )
  126. {
  127. if (!TheRecorder->isMultiplayer() || (m_localSlotNum == -1 && !m_isObserver))
  128. return;
  129. // Check for a single winning alliance
  130. if (!m_singleAllianceRemaining)
  131. {
  132. Bool multipleAlliances = false;
  133. Player *alive = NULL;
  134. Player *player;
  135. for (Int i=0; i<MAX_PLAYER_COUNT; ++i)
  136. {
  137. player = m_players[i];
  138. if (player && !hasSinglePlayerBeenDefeated(player))
  139. {
  140. if (alive)
  141. {
  142. // check to verify they are on the same team
  143. if (!areAllies(alive, player))
  144. {
  145. multipleAlliances = true;
  146. break;
  147. }
  148. }
  149. else
  150. {
  151. alive = player; // save this pointer to check against
  152. }
  153. }
  154. }
  155. if (!multipleAlliances)
  156. {
  157. m_singleAllianceRemaining = true; // don't check again
  158. m_endFrame = TheGameLogic->getFrame();
  159. }
  160. }
  161. // check for player eliminations
  162. for (Int i=0; i<MAX_PLAYER_COUNT; ++i)
  163. {
  164. Player *p = m_players[i];
  165. if (p && !m_isDefeated[i] && hasSinglePlayerBeenDefeated(p))
  166. {
  167. m_isDefeated[i] = true;
  168. if (TheGameLogic->getFrame() > 1)
  169. {
  170. ThePartitionManager->revealMapForPlayerPermanently( p->getPlayerIndex() );
  171. TheGameClient->updateFakeDrawables();
  172. TheInGameUI->message("GUI:PlayerHasBeenDefeated", p->getPlayerDisplayName().str() );
  173. // People are boneheads. Also play a sound
  174. static AudioEventRTS leftGameSound("GUIMessageReceived");
  175. TheAudio->addAudioEvent(&leftGameSound);
  176. }
  177. for (Int idx = 0; idx < MAX_SLOTS; ++idx)
  178. {
  179. AsciiString pName;
  180. pName.format("player%d", idx);
  181. if (p->getPlayerNameKey() == NAMEKEY(pName))
  182. {
  183. GameSlot *slot = (TheGameInfo)?TheGameInfo->getSlot(idx):NULL;
  184. if (slot && slot->isAI())
  185. {
  186. DEBUG_LOG(("Marking AI player %s as defeated\n", pName.str()));
  187. slot->setLastFrameInGame(TheGameLogic->getFrame());
  188. }
  189. }
  190. }
  191. // destroy any remaining units (infantry if its a short game, for example)
  192. p->killPlayer();
  193. PopulateInGameDiplomacyPopup();
  194. }
  195. }
  196. // Check if the local player has been eliminated
  197. if (!m_localPlayerDefeated && !m_isObserver)
  198. {
  199. Player *localPlayer = m_players[m_localSlotNum];
  200. if (hasSinglePlayerBeenDefeated(localPlayer))
  201. {
  202. if (!m_singleAllianceRemaining)
  203. {
  204. //MessageBoxOk(TheGameText->fetch("GUI:Defeat"), TheGameText->fetch("GUI:LocalDefeat"), NULL);
  205. }
  206. m_localPlayerDefeated = true; // don't check again
  207. TheRadar->forceOn(TRUE);
  208. SetInGameChatType( INGAME_CHAT_EVERYONE ); // can't chat to allies after death. Only to other observers.
  209. }
  210. }
  211. }
  212. //-------------------------------------------------------------------------------------------------
  213. Bool VictoryConditions::hasAchievedVictory(Player *player)
  214. {
  215. if (!player)
  216. return false;
  217. if (m_singleAllianceRemaining)
  218. {
  219. for (Int i=0; i<MAX_PLAYER_COUNT; ++i)
  220. {
  221. if ( m_players[i] && !hasSinglePlayerBeenDefeated(m_players[i]) &&
  222. (player == m_players[i] || areAllies(m_players[i], player)) )
  223. return true;
  224. }
  225. }
  226. return false;
  227. }
  228. //-------------------------------------------------------------------------------------------------
  229. Bool VictoryConditions::hasBeenDefeated(Player *player)
  230. {
  231. if (!player)
  232. return false;
  233. if (m_singleAllianceRemaining && !hasAchievedVictory(player))
  234. return true;
  235. return false;
  236. }
  237. //-------------------------------------------------------------------------------------------------
  238. Bool VictoryConditions::hasSinglePlayerBeenDefeated(Player *player)
  239. {
  240. if (!player)
  241. return false;
  242. KindOfMaskType mask;
  243. mask.set(KINDOF_MP_COUNT_FOR_VICTORY);
  244. if ( ISSET(NOUNITS) && ISSET(NOBUILDINGS) )
  245. {
  246. if ( !player->hasAnyObjects() )
  247. {
  248. return true;
  249. }
  250. }
  251. else if ( ISSET(NOUNITS) )
  252. {
  253. if ( !player->hasAnyUnits() )
  254. {
  255. return true;
  256. }
  257. }
  258. else if ( ISSET(NOBUILDINGS) )
  259. {
  260. if ( !player->hasAnyBuildings(mask) )
  261. {
  262. return true;
  263. }
  264. }
  265. return false;
  266. }
  267. //-------------------------------------------------------------------------------------------------
  268. void VictoryConditions::cachePlayerPtrs( void )
  269. {
  270. if (!TheRecorder->isMultiplayer())
  271. return;
  272. Int playerCount = 0;
  273. const PlayerTemplate *civTemplate = ThePlayerTemplateStore->findPlayerTemplate( NAMEKEY("FactionCivilian") );
  274. for (Int i=0; i<MAX_PLAYER_COUNT; ++i)
  275. {
  276. Player *player = ThePlayerList->getNthPlayer(i);
  277. DEBUG_LOG(("Checking whether to cache player %d - [%ls], house [%ls]\n", i, player?player->getPlayerDisplayName().str():L"<NOBODY>", (player&&player->getPlayerTemplate())?player->getPlayerTemplate()->getDisplayName().str():L"<NONE>"));
  278. if (player && player != ThePlayerList->getNeutralPlayer() && player->getPlayerTemplate() && player->getPlayerTemplate() != civTemplate && !player->isPlayerObserver())
  279. {
  280. DEBUG_LOG(("Caching player\n"));
  281. m_players[playerCount] = player;
  282. if (m_players[playerCount]->isLocalPlayer())
  283. m_localSlotNum = playerCount;
  284. ++playerCount;
  285. }
  286. }
  287. while (playerCount < MAX_PLAYER_COUNT)
  288. {
  289. m_players[playerCount++] = NULL;
  290. }
  291. if (m_localSlotNum < 0)
  292. {
  293. m_localPlayerDefeated = true; // if we have no local player, don't check for defeat
  294. DEBUG_ASSERTCRASH(TheRadar, ("No Radar!"));
  295. TheRadar->forceOn(TRUE);
  296. m_isObserver = true;
  297. }
  298. }
  299. //-------------------------------------------------------------------------------------------------
  300. Bool VictoryConditions::isLocalAlliedVictory( void )
  301. {
  302. if (m_isObserver)
  303. return false;
  304. return (hasAchievedVictory(m_players[m_localSlotNum]));
  305. }
  306. //-------------------------------------------------------------------------------------------------
  307. Bool VictoryConditions::isLocalAlliedDefeat( void )
  308. {
  309. if (m_isObserver)
  310. return m_singleAllianceRemaining;
  311. return (hasBeenDefeated(m_players[m_localSlotNum]));
  312. }
  313. //-------------------------------------------------------------------------------------------------
  314. Bool VictoryConditions::isLocalDefeat( void )
  315. {
  316. if (m_isObserver)
  317. return FALSE;
  318. return (m_localPlayerDefeated);
  319. }