VictoryConditions.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. /*
  2. ** Command & Conquer Generals(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 "GameLogic/GameLogic.h"
  43. #include "GameLogic/PartitionManager.h"
  44. #include "GameLogic/ScriptActions.h"
  45. #include "GameLogic/VictoryConditions.h"
  46. #include "GameNetwork/GameInfo.h"
  47. #include "GameNetwork/NetworkDefs.h"
  48. #ifdef _INTERNAL
  49. // for occasional debugging...
  50. //#pragma optimize("", off)
  51. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  52. #endif
  53. //-------------------------------------------------------------------------------------------------
  54. #define ISSET(x) (m_victoryConditions & VICTORY_##x)
  55. //-------------------------------------------------------------------------------------------------
  56. VictoryConditionsInterface *TheVictoryConditions = NULL;
  57. //-------------------------------------------------------------------------------------------------
  58. inline static Bool areAllies(const Player *p1, const Player *p2)
  59. {
  60. if (p1 != p2 &&
  61. p1->getRelationship(p2->getDefaultTeam()) == ALLIES &&
  62. p2->getRelationship(p1->getDefaultTeam()) == ALLIES)
  63. return true;
  64. return false;
  65. }
  66. //-------------------------------------------------------------------------------------------------
  67. class VictoryConditions : public VictoryConditionsInterface
  68. {
  69. public:
  70. VictoryConditions();
  71. void init( void );
  72. void reset( void );
  73. void update( void );
  74. Bool hasAchievedVictory(Player *player); ///< has a specific player and his allies won?
  75. Bool hasBeenDefeated(Player *player); ///< has a specific player and his allies lost?
  76. Bool hasSinglePlayerBeenDefeated(Player *player); ///< has a specific player lost?
  77. void cachePlayerPtrs( void ); ///< players have been created - cache the ones of interest
  78. Bool isLocalAlliedVictory( void ); ///< convenience function
  79. Bool isLocalAlliedDefeat( void ); ///< convenience function
  80. Bool isLocalDefeat( void ); ///< convenience function
  81. Bool amIObserver( void ) { return m_isObserver;} ///< Am I an observer?( need this for scripts )
  82. virtual UnsignedInt getEndFrame( void ) { return m_endFrame; } ///< on which frame was the game effectively over?
  83. private:
  84. Player* m_players[MAX_PLAYER_COUNT];
  85. Int m_localSlotNum;
  86. UnsignedInt m_endFrame;
  87. Bool m_isDefeated[MAX_PLAYER_COUNT];
  88. Bool m_localPlayerDefeated; ///< prevents condition from being signaled each frame
  89. Bool m_singleAllianceRemaining; ///< prevents condition from being signaled each frame
  90. Bool m_isObserver;
  91. };
  92. //-------------------------------------------------------------------------------------------------
  93. VictoryConditionsInterface * createVictoryConditions( void )
  94. {
  95. // only one created, so no MemoryPool usage
  96. return NEW VictoryConditions;
  97. }
  98. //-------------------------------------------------------------------------------------------------
  99. VictoryConditions::VictoryConditions()
  100. {
  101. reset();
  102. }
  103. //-------------------------------------------------------------------------------------------------
  104. void VictoryConditions::init( void )
  105. {
  106. reset();
  107. }
  108. //-------------------------------------------------------------------------------------------------
  109. void VictoryConditions::reset( void )
  110. {
  111. for (Int i=0; i<MAX_PLAYER_COUNT; ++i)
  112. {
  113. m_players[i] = NULL;
  114. m_isDefeated[i] = false;
  115. }
  116. m_localSlotNum = -1;
  117. m_localPlayerDefeated = false;
  118. m_singleAllianceRemaining = false;
  119. m_isObserver = false;
  120. m_endFrame = 0;
  121. m_victoryConditions = VICTORY_NOBUILDINGS | VICTORY_NOUNITS;
  122. }
  123. //-------------------------------------------------------------------------------------------------
  124. void VictoryConditions::update( void )
  125. {
  126. if (!TheRecorder->isMultiplayer() || (m_localSlotNum == -1 && !m_isObserver))
  127. return;
  128. // Check for a single winning alliance
  129. if (!m_singleAllianceRemaining)
  130. {
  131. Bool multipleAlliances = false;
  132. Player *alive = NULL;
  133. Player *player;
  134. for (Int i=0; i<MAX_PLAYER_COUNT; ++i)
  135. {
  136. player = m_players[i];
  137. if (player && !hasSinglePlayerBeenDefeated(player))
  138. {
  139. if (alive)
  140. {
  141. // check to verify they are on the same team
  142. if (!areAllies(alive, player))
  143. {
  144. multipleAlliances = true;
  145. break;
  146. }
  147. }
  148. else
  149. {
  150. alive = player; // save this pointer to check against
  151. }
  152. }
  153. }
  154. if (!multipleAlliances)
  155. {
  156. m_singleAllianceRemaining = true; // don't check again
  157. m_endFrame = TheGameLogic->getFrame();
  158. }
  159. }
  160. // check for player eliminations
  161. for (Int i=0; i<MAX_PLAYER_COUNT; ++i)
  162. {
  163. Player *p = m_players[i];
  164. if (p && !m_isDefeated[i] && hasSinglePlayerBeenDefeated(p))
  165. {
  166. m_isDefeated[i] = true;
  167. if (TheGameLogic->getFrame() > 1)
  168. {
  169. ThePartitionManager->revealMapForPlayerPermanently( p->getPlayerIndex() );
  170. TheInGameUI->message("GUI:PlayerHasBeenDefeated", p->getPlayerDisplayName().str() );
  171. // People are boneheads. Also play a sound
  172. static AudioEventRTS leftGameSound("GUIMessageReceived");
  173. TheAudio->addAudioEvent(&leftGameSound);
  174. }
  175. for (Int idx = 0; idx < MAX_SLOTS; ++idx)
  176. {
  177. AsciiString pName;
  178. pName.format("player%d", idx);
  179. if (p->getPlayerNameKey() == NAMEKEY(pName))
  180. {
  181. GameSlot *slot = (TheGameInfo)?TheGameInfo->getSlot(idx):NULL;
  182. if (slot && slot->isAI())
  183. {
  184. DEBUG_LOG(("Marking AI player %s as defeated\n", pName.str()));
  185. slot->setLastFrameInGame(TheGameLogic->getFrame());
  186. }
  187. }
  188. }
  189. // destroy any remaining units (infantry if its a short game, for example)
  190. p->killPlayer();
  191. PopulateInGameDiplomacyPopup();
  192. }
  193. }
  194. // Check if the local player has been eliminated
  195. if (!m_localPlayerDefeated && !m_isObserver)
  196. {
  197. Player *localPlayer = m_players[m_localSlotNum];
  198. if (hasSinglePlayerBeenDefeated(localPlayer))
  199. {
  200. if (!m_singleAllianceRemaining)
  201. {
  202. //MessageBoxOk(TheGameText->fetch("GUI:Defeat"), TheGameText->fetch("GUI:LocalDefeat"), NULL);
  203. }
  204. m_localPlayerDefeated = true; // don't check again
  205. TheRadar->forceOn(TRUE);
  206. SetInGameChatType( INGAME_CHAT_EVERYONE ); // can't chat to allies after death. Only to other observers.
  207. }
  208. }
  209. }
  210. //-------------------------------------------------------------------------------------------------
  211. Bool VictoryConditions::hasAchievedVictory(Player *player)
  212. {
  213. if (!player)
  214. return false;
  215. if (m_singleAllianceRemaining)
  216. {
  217. for (Int i=0; i<MAX_PLAYER_COUNT; ++i)
  218. {
  219. if ( m_players[i] && !hasSinglePlayerBeenDefeated(m_players[i]) &&
  220. (player == m_players[i] || areAllies(m_players[i], player)) )
  221. return true;
  222. }
  223. }
  224. return false;
  225. }
  226. //-------------------------------------------------------------------------------------------------
  227. Bool VictoryConditions::hasBeenDefeated(Player *player)
  228. {
  229. if (!player)
  230. return false;
  231. if (m_singleAllianceRemaining && !hasAchievedVictory(player))
  232. return true;
  233. return false;
  234. }
  235. //-------------------------------------------------------------------------------------------------
  236. Bool VictoryConditions::hasSinglePlayerBeenDefeated(Player *player)
  237. {
  238. if (!player)
  239. return false;
  240. KindOfMaskType mask;
  241. mask.set(KINDOF_MP_COUNT_FOR_VICTORY);
  242. if ( ISSET(NOUNITS) && ISSET(NOBUILDINGS) )
  243. {
  244. if ( !player->hasAnyObjects() )
  245. {
  246. return true;
  247. }
  248. }
  249. else if ( ISSET(NOUNITS) )
  250. {
  251. if ( !player->hasAnyUnits() )
  252. {
  253. return true;
  254. }
  255. }
  256. else if ( ISSET(NOBUILDINGS) )
  257. {
  258. if ( !player->hasAnyBuildings(mask) )
  259. {
  260. return true;
  261. }
  262. }
  263. return false;
  264. }
  265. //-------------------------------------------------------------------------------------------------
  266. void VictoryConditions::cachePlayerPtrs( void )
  267. {
  268. if (!TheRecorder->isMultiplayer())
  269. return;
  270. Int playerCount = 0;
  271. const PlayerTemplate *civTemplate = ThePlayerTemplateStore->findPlayerTemplate( NAMEKEY("FactionCivilian") );
  272. for (Int i=0; i<MAX_PLAYER_COUNT; ++i)
  273. {
  274. Player *player = ThePlayerList->getNthPlayer(i);
  275. 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>"));
  276. if (player && player != ThePlayerList->getNeutralPlayer() && player->getPlayerTemplate() && player->getPlayerTemplate() != civTemplate && !player->isPlayerObserver())
  277. {
  278. DEBUG_LOG(("Caching player\n"));
  279. m_players[playerCount] = player;
  280. if (m_players[playerCount]->isLocalPlayer())
  281. m_localSlotNum = playerCount;
  282. ++playerCount;
  283. }
  284. }
  285. while (playerCount < MAX_PLAYER_COUNT)
  286. {
  287. m_players[playerCount++] = NULL;
  288. }
  289. if (m_localSlotNum < 0)
  290. {
  291. m_localPlayerDefeated = true; // if we have no local player, don't check for defeat
  292. DEBUG_ASSERTCRASH(TheRadar, ("No Radar!"));
  293. TheRadar->forceOn(TRUE);
  294. m_isObserver = true;
  295. }
  296. }
  297. //-------------------------------------------------------------------------------------------------
  298. Bool VictoryConditions::isLocalAlliedVictory( void )
  299. {
  300. if (m_isObserver)
  301. return false;
  302. return (hasAchievedVictory(m_players[m_localSlotNum]));
  303. }
  304. //-------------------------------------------------------------------------------------------------
  305. Bool VictoryConditions::isLocalAlliedDefeat( void )
  306. {
  307. if (m_isObserver)
  308. return m_singleAllianceRemaining;
  309. return (hasBeenDefeated(m_players[m_localSlotNum]));
  310. }
  311. //-------------------------------------------------------------------------------------------------
  312. Bool VictoryConditions::isLocalDefeat( void )
  313. {
  314. if (m_isObserver)
  315. return FALSE;
  316. return (m_localPlayerDefeated);
  317. }