GameLogicDispatch.cpp 63 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988
  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: GameLogicDispatch.cpp ////////////////////////////////////////////////////////////////////
  24. // Author: Mike Booth, Colin Day
  25. // Description: Message logic to drive the game play
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  28. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  29. #include "Common/CRCDebug.h"
  30. #include "Common/GameAudio.h"
  31. #include "Common/GameEngine.h"
  32. #include "Common/GlobalData.h"
  33. #include "Common/NameKeyGenerator.h"
  34. #include "Common/ThingFactory.h"
  35. #include "Common/Player.h"
  36. #include "Common/PlayerList.h"
  37. #include "Common/PlayerTemplate.h"
  38. #include "Common/MessageStream.h"
  39. #include "Common/MultiplayerSettings.h"
  40. #include "Common/Recorder.h"
  41. #include "Common/BuildAssistant.h"
  42. #include "Common/SpecialPower.h"
  43. #include "Common/ThingTemplate.h"
  44. #include "Common/Upgrade.h"
  45. #include "Common/StatsCollector.h"
  46. #include "Common/Radar.h"
  47. #include "GameLogic/AIPathfind.h"
  48. #include "GameLogic/GameLogic.h"
  49. #include "GameLogic/Locomotor.h"
  50. #include "GameLogic/Object.h"
  51. #include "GameLogic/ObjectCreationList.h"
  52. #include "GameLogic/ObjectIter.h"
  53. //#include "GameLogic/PartitionManager.h"
  54. #include "GameLogic/AI.h"
  55. #include "GameLogic/Module/AIUpdate.h"
  56. #include "GameLogic/Module/BodyModule.h"
  57. #include "GameLogic/Module/OpenContain.h"
  58. #include "GameLogic/Module/ProductionUpdate.h"
  59. #include "GameLogic/Module/SpecialPowerModule.h"
  60. #include "GameLogic/ScriptActions.h"
  61. #include "GameLogic/ScriptEngine.h"
  62. #include "GameLogic/VictoryConditions.h"
  63. #include "GameLogic/Weapon.h"
  64. #include "GameClient/CommandXlat.h"
  65. #include "GameClient/ControlBar.h"
  66. #include "GameClient/Drawable.h"
  67. #include "GameClient/Eva.h"
  68. #include "GameClient/GameText.h"
  69. #include "GameClient/GameWindowManager.h"
  70. #include "GameClient/GuiCallbacks.h"
  71. #include "GameClient/InGameUI.h"
  72. #include "GameClient/KeyDefs.h"
  73. #include "GameClient/Mouse.h"
  74. #include "GameClient/ParticleSys.h"
  75. #include "GameClient/Shell.h"
  76. #include "GameClient/Module/BeaconClientUpdate.h"
  77. #include "GameClient/LookAtXlat.h"
  78. #include "GameNetwork/NetworkInterface.h"
  79. #ifdef _INTERNAL
  80. // for occasional debugging...
  81. //#pragma optimize("", off)
  82. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  83. #endif
  84. #define MAX_PATH_SUBJECTS 64
  85. static Bool theBuildPlan = false;
  86. static Object *thePlanSubject[ MAX_PATH_SUBJECTS ];
  87. static int thePlanSubjectCount = 0;
  88. //static WindowLayout *background = NULL;
  89. // ------------------------------------------------------------------------------------------------
  90. /** Issue the movement command to the object */
  91. // ------------------------------------------------------------------------------------------------
  92. static void doMoveTo( Object *obj, const Coord3D *pos )
  93. {
  94. AIUpdateInterface *ai = obj->getAIUpdateInterface();
  95. DEBUG_ASSERTCRASH(ai, ("Attemped doMoveTo() on an Object with no AI\n"));
  96. if (ai)
  97. {
  98. if (theBuildPlan)
  99. {
  100. int i;
  101. // if this object isn't in the buildPlan set, add it
  102. for( i=0; i<thePlanSubjectCount; i++ )
  103. if (thePlanSubject[i] == obj)
  104. break;
  105. if (i == thePlanSubjectCount)
  106. thePlanSubject[ thePlanSubjectCount++ ] = obj;
  107. ai->queueWaypoint( pos );
  108. }
  109. else
  110. {
  111. ai->clearWaypointQueue();
  112. obj->leaveGroup();
  113. obj->releaseWeaponLock(LOCKED_TEMPORARILY); // release any temporary locks.
  114. ai->aiMoveToPosition( pos, CMD_FROM_PLAYER );
  115. }
  116. }
  117. }
  118. // ------------------------------------------------------------------------------------------------
  119. // ------------------------------------------------------------------------------------------------
  120. static void doSetRallyPoint( Object *obj, const Coord3D& pos )
  121. {
  122. Bool isLocalPlayer = obj->isLocallyControlled();
  123. //
  124. // we must be able to find a path from the object to the point they have chosen, cause setting
  125. // a rally point at a invalid location would suck. To be super nice, we have to make sure
  126. // that every type of object that can be created from the thing setting the rally point
  127. // can actually find a path from the thing to the point
  128. //
  129. // to see the never-finished code to check all locomotor sets, see past revs of GUICommandTranslator.cpp -MDC
  130. //
  131. // for now, just use the basic human locomotor ... and enable the above code when Steven
  132. // tells me how to get the locomotor sets based on a thing template (CBD)
  133. //
  134. NameKeyType key = NAMEKEY( "BasicHumanLocomotor" );
  135. LocomotorSet locomotorSet;
  136. locomotorSet.addLocomotor( TheLocomotorStore->findLocomotorTemplate( key ) );
  137. if( TheAI->pathfinder()->quickDoesPathExist( locomotorSet, obj->getPosition(), &pos ) == FALSE )
  138. {
  139. // user feedback
  140. if( isLocalPlayer )
  141. {
  142. // display error message to user
  143. TheInGameUI->message( TheGameText->fetch( "GUI:RallyPointNoPath" ) );
  144. // play the no can do sound
  145. static AudioEventRTS rallyNotSet("UnableToSetRallyPoint");
  146. rallyNotSet.setPosition(&pos);
  147. TheAudio->addAudioEvent(&rallyNotSet);
  148. } // end if
  149. return;
  150. } // end if
  151. // feedback to the player
  152. if( isLocalPlayer )
  153. {
  154. // print a message to the user
  155. UnicodeString info;
  156. info.format( TheGameText->fetch( "GUI:RallyPointSet" ),
  157. obj->getTemplate()->getDisplayName().str() );
  158. TheInGameUI->message( info );
  159. // play a sound for setting the rally point
  160. static AudioEventRTS rallyPointSet("RallyPointSet");
  161. rallyPointSet.setPosition(&pos);
  162. rallyPointSet.setPlayerIndex(obj->getControllingPlayer()->getPlayerIndex());
  163. TheAudio->addAudioEvent(&rallyPointSet);
  164. // mark the UI as dirty so that we re-evaluate the selection and show the rally point
  165. Drawable *draw = obj->getDrawable();
  166. if( draw && draw->isSelected() )
  167. TheControlBar->markUIDirty();
  168. } // end if
  169. // if this object has a ProductionExitUpdate interface, we are setting a rally point
  170. ExitInterface *exitInterface = obj->getObjectExitInterface();
  171. if( exitInterface )
  172. {
  173. // set the rally point
  174. exitInterface->setRallyPoint( &pos );
  175. }
  176. }
  177. static Object * getSingleObjectFromSelection(const AIGroup *currentlySelectedGroup)
  178. {
  179. if( currentlySelectedGroup )
  180. {
  181. const VecObjectID& selectedObjects = currentlySelectedGroup->getAllIDs();
  182. DEBUG_ASSERTCRASH(selectedObjects.size() == 1, ("Trying to get single object from multiple selection!"));
  183. VecObjectID::const_iterator it = selectedObjects.begin();
  184. return TheGameLogic->findObjectByID(*it);
  185. }
  186. return NULL;
  187. }
  188. // ------------------------------------------------------------------------------------------------
  189. // ------------------------------------------------------------------------------------------------
  190. void GameLogic::closeWindows( void )
  191. {
  192. HideDiplomacy();
  193. ResetDiplomacy();
  194. HideInGameChat();
  195. ResetInGameChat();
  196. TheControlBar->hidePurchaseScience();
  197. TheControlBar->hideSpecialPowerShortcut();
  198. HideQuitMenu();
  199. // hide the options menu
  200. NameKeyType buttonID = TheNameKeyGenerator->nameToKey( "OptionsMenu.wnd:ButtonBack" );
  201. GameWindow *button = TheWindowManager->winGetWindowFromId( NULL, buttonID );
  202. GameWindow *window = TheWindowManager->winGetWindowFromId( NULL, TheNameKeyGenerator->nameToKey("OptionsMenu.wnd:OptionsMenuParent") );
  203. if(window)
  204. TheWindowManager->winSendSystemMsg( window, GBM_SELECTED,
  205. (WindowMsgData)button, buttonID );
  206. }
  207. // ------------------------------------------------------------------------------------------------
  208. // ------------------------------------------------------------------------------------------------
  209. void GameLogic::clearGameData( Bool showScoreScreen )
  210. {
  211. if( !isInGame() )
  212. {
  213. DEBUG_CRASH(("We tried to clear the game data when we weren't in a game"));
  214. return;
  215. }
  216. // m_background = TheWindowManager->winCreateLayout("Menus/BlankWindow.wnd");
  217. // DEBUG_ASSERTCRASH(m_background,("We Couldn't Load Menus/BlankWindow.wnd"));
  218. // m_background->hide(FALSE);
  219. // m_background->bringForward();
  220. // reset the game engine to accept data for a new game
  221. if(TheStatsCollector)
  222. TheStatsCollector->writeFileEnd();
  223. TheScriptActions->closeWindows(FALSE); // Close victory or defeat windows.
  224. Bool shellGame = FALSE;
  225. if ((!isInShellGame() || !isInGame()) && showScoreScreen)
  226. {
  227. shellGame = TRUE;
  228. TheShell->push("Menus/ScoreScreen.wnd");
  229. TheShell->showShell(FALSE); // by passing in false, we don't want to run the Init on the shell screen we just pushed on
  230. void FixupScoreScreenMovieWindow( void );
  231. FixupScoreScreenMovieWindow();
  232. }
  233. TheGameEngine->reset();
  234. setGameMode(GAME_NONE);
  235. // m_background->bringForward();
  236. // if(shellGame)
  237. if (TheGlobalData->m_initialFile.isEmpty() == FALSE)
  238. {
  239. TheGameEngine->setQuitting(TRUE);
  240. }
  241. HideControlBar();
  242. closeWindows();
  243. TheMouse->setVisibility(TRUE);
  244. if(m_background)
  245. {
  246. m_background->destroyWindows();
  247. m_background->deleteInstance();
  248. m_background = NULL;
  249. }
  250. }
  251. // ------------------------------------------------------------------------------------------------
  252. /** Prepare for a new game */
  253. // ------------------------------------------------------------------------------------------------
  254. void GameLogic::prepareNewGame( Int gameMode, GameDifficulty diff, Int rankPoints )
  255. {
  256. //Added By Sadullah Nader
  257. //Fix for loading game scene
  258. setGameLoading(TRUE);
  259. //
  260. TheScriptEngine->setGlobalDifficulty(diff);
  261. if(!m_background)
  262. {
  263. m_background = TheWindowManager->winCreateLayout("Menus/BlankWindow.wnd");
  264. DEBUG_ASSERTCRASH(m_background,("We Couldn't Load Menus/BlankWindow.wnd"));
  265. m_background->hide(FALSE);
  266. m_background->bringForward();
  267. }
  268. m_background->getFirstWindow()->winClearStatus(WIN_STATUS_IMAGE);
  269. TheGameLogic->setGameMode( gameMode );
  270. if (!TheGlobalData->m_pendingFile.isEmpty())
  271. {
  272. TheWritableGlobalData->m_mapName = TheGlobalData->m_pendingFile;
  273. TheWritableGlobalData->m_pendingFile.clear();
  274. }
  275. m_rankPointsToAddAtGameStart = rankPoints;
  276. DEBUG_LOG(("GameLogic::prepareNewGame() - m_rankPointsToAddAtGameStart = %d\n", m_rankPointsToAddAtGameStart));
  277. // If we're about to start a game, hide the shell.
  278. if(!TheGameLogic->isInShellGame())
  279. TheShell->hideShell();
  280. m_startNewGame = FALSE;
  281. } // end prepareNewGame
  282. //-------------------------------------------------------------------------------------------------
  283. /** This message handles dispatches object command messages to the
  284. * appropriate objects.
  285. * @todo Rename this to "CommandProcessor", or similiar. */
  286. //-------------------------------------------------------------------------------------------------
  287. void GameLogic::logicMessageDispatcher( GameMessage *msg, void *userData )
  288. {
  289. #ifdef _DEBUG
  290. DEBUG_ASSERTCRASH(msg != NULL && msg != (GameMessage*)0xdeadbeef, ("bad msg"));
  291. #endif
  292. Player *thisPlayer = ThePlayerList->getNthPlayer( msg->getPlayerIndex() );
  293. DEBUG_ASSERTCRASH( thisPlayer, ("logicMessageDispatcher: Processing message from unknown player (player index '%d')\n",
  294. msg->getPlayerIndex()) );
  295. AIGroup *currentlySelectedGroup = NULL;
  296. if (isInGame())
  297. {
  298. if (msg->getType() >= GameMessage::MSG_BEGIN_NETWORK_MESSAGES && msg->getType() <= GameMessage::MSG_END_NETWORK_MESSAGES)
  299. {
  300. if (msg->getType() != GameMessage::MSG_LOGIC_CRC && msg->getType() != GameMessage::MSG_SET_REPLAY_CAMERA)
  301. {
  302. currentlySelectedGroup = TheAI->createGroup(); // can't do this outside a game - it'll cause sync errors galore.
  303. CRCGEN_LOG(( "Creating AIGroup %d in GameLogic::logicMessageDispatcher()\n", (currentlySelectedGroup)?currentlySelectedGroup->getID():0 ));
  304. thisPlayer->getCurrentSelectionAsAIGroup(currentlySelectedGroup);
  305. // We can't issue commands to groups that contain units that don't belong the issuing player, so pretend like
  306. // there's nothing selected. Also, if currentlySelectedGroup is empty, go ahead and delete it, so that we can skip
  307. // any processing on it.
  308. if (currentlySelectedGroup->isEmpty())
  309. {
  310. TheAI->destroyGroup(currentlySelectedGroup);
  311. currentlySelectedGroup = NULL;
  312. }
  313. // If there are any units that the player doesn't own, then remove them from the "currentlySelectedGroup"
  314. if (currentlySelectedGroup)
  315. if (currentlySelectedGroup->removeAnyObjectsNotOwnedByPlayer(thisPlayer))
  316. currentlySelectedGroup = NULL;
  317. if(TheStatsCollector)
  318. TheStatsCollector->collectMsgStats(msg);
  319. }
  320. }
  321. }
  322. #ifdef DEBUG_LOGGING
  323. AsciiString commandName;
  324. commandName = msg->getCommandAsAsciiString();
  325. if (msg->getType() < GameMessage::MSG_BEGIN_NETWORK_MESSAGES || msg->getType() > GameMessage::MSG_END_NETWORK_MESSAGES)
  326. {
  327. commandName.concat(" (NON-LOGIC-MESSAGE!!!)");
  328. }
  329. else if (msg->getType() == GameMessage::MSG_BEGIN_NETWORK_MESSAGES)
  330. {
  331. commandName = " (CRC message!)";
  332. }
  333. #if 0
  334. if (commandName.isNotEmpty() /*&& msg->getType() != GameMessage::MSG_FRAME_TICK*/)
  335. {
  336. DEBUG_LOG(("Frame %d: GameLogic::logicMessageDispatcher() saw a %s from player %d (%ls)\n", getFrame(), commandName.str(),
  337. msg->getPlayerIndex(), thisPlayer->getPlayerDisplayName().str()));
  338. }
  339. #endif
  340. #endif // DEBUG_LOGGING
  341. // process the message
  342. GameMessage::Type msgType = msg->getType();
  343. switch( msgType )
  344. {
  345. //---------------------------------------------------------------------------------------------
  346. case GameMessage::MSG_NEW_GAME:
  347. {
  348. //DEBUG_ASSERTCRASH(msg->getArgumentCount() == 1 || msg->getArgumentCount() == 2, ("%d arguments to MSG_NEW_GAME", msg->getArgumentCount()));
  349. Int gameMode = msg->getArgument( 0 )->integer;
  350. Int rankPoints = 0;
  351. GameDifficulty diff = DIFFICULTY_NORMAL;
  352. if ( msg->getArgumentCount() >= 2 )
  353. diff = (GameDifficulty)msg->getArgument( 1 )->integer;
  354. if ( msg->getArgumentCount() >= 3 )
  355. rankPoints = msg->getArgument( 2 )->integer;
  356. if ( msg->getArgumentCount() >= 4 )
  357. {
  358. Int maxFPS = msg->getArgument( 3 )->integer;
  359. if (maxFPS < 1 || maxFPS > 1000)
  360. maxFPS = TheGlobalData->m_framesPerSecondLimit;
  361. DEBUG_LOG(("Setting max FPS limit to %d FPS\n", maxFPS));
  362. TheGameEngine->setFramesPerSecondLimit(maxFPS);
  363. TheWritableGlobalData->m_useFpsLimit = true;
  364. }
  365. // prepare for new game
  366. prepareNewGame( gameMode, diff, rankPoints );
  367. // start new game
  368. startNewGame( FALSE );
  369. break;
  370. } // end new game
  371. //---------------------------------------------------------------------------------------------
  372. case GameMessage::MSG_CLEAR_GAME_DATA:
  373. {
  374. #if defined(_DEBUG) || defined(_INTERNAL)
  375. if (TheDisplay && TheGlobalData->m_dumpAssetUsage)
  376. TheDisplay->dumpAssetUsage(TheGlobalData->m_mapName.str());
  377. #endif
  378. if (currentlySelectedGroup)
  379. TheAI->destroyGroup(currentlySelectedGroup);
  380. currentlySelectedGroup = NULL;
  381. TheGameLogic->clearGameData();
  382. break;
  383. } // end clear game data
  384. //---------------------------------------------------------------------------------------------
  385. case GameMessage::MSG_META_BEGIN_PATH_BUILD:
  386. {
  387. DEBUG_LOG(("META: begin path build\n"));
  388. DEBUG_ASSERTCRASH(!theBuildPlan, ("mismatched theBuildPlan"));
  389. if (theBuildPlan == false)
  390. {
  391. theBuildPlan = true;
  392. thePlanSubjectCount = 0;
  393. }
  394. break;
  395. }
  396. //---------------------------------------------------------------------------------------------
  397. case GameMessage::MSG_META_END_PATH_BUILD:
  398. {
  399. DEBUG_LOG(("META: end path build\n"));
  400. DEBUG_ASSERTCRASH(theBuildPlan, ("mismatched theBuildPlan"));
  401. // tell everyone who participated in the plan to move
  402. for( int i=0; i<thePlanSubjectCount; i++ )
  403. {
  404. AIUpdateInterface *ai = thePlanSubject[i]->getAIUpdateInterface();
  405. if (ai)
  406. ai->executeWaypointQueue();
  407. }
  408. theBuildPlan = false;
  409. thePlanSubjectCount = 0;
  410. break;
  411. }
  412. //---------------------------------------------------------------------------------------------
  413. case GameMessage::MSG_SET_RALLY_POINT:
  414. {
  415. Object *obj = TheGameLogic->findObjectByID( msg->getArgument( 0 )->objectID );
  416. Coord3D dest = msg->getArgument( 1 )->location;
  417. if (obj)
  418. {
  419. doSetRallyPoint( obj, dest );
  420. }
  421. break;
  422. }
  423. //---------------------------------------------------------------------------------------------
  424. case GameMessage::MSG_DO_WEAPON:
  425. {
  426. WeaponSlotType weaponSlot = (WeaponSlotType)msg->getArgument( 0 )->integer;
  427. Int maxShotsToFire = msg->getArgument( 1 )->integer;
  428. // lock it just till the weapon is empty or the attack is "done"
  429. if( currentlySelectedGroup && currentlySelectedGroup->setWeaponLockForGroup( weaponSlot, LOCKED_TEMPORARILY ))
  430. {
  431. currentlySelectedGroup->groupAttackPosition( NULL, maxShotsToFire, CMD_FROM_PLAYER );
  432. }
  433. break;
  434. }
  435. //---------------------------------------------------------------------------------------------
  436. case GameMessage::MSG_COMBATDROP_AT_OBJECT:
  437. {
  438. Object *targetObject = TheGameLogic->findObjectByID( msg->getArgument( 0 )->objectID );
  439. // issue command for either single object or for selected group
  440. if( currentlySelectedGroup )
  441. currentlySelectedGroup->groupCombatDrop( targetObject,
  442. *targetObject->getPosition(),
  443. CMD_FROM_PLAYER );
  444. /*
  445. if( sourceObject && targetObject )
  446. {
  447. AIUpdateInterface* sourceAI = sourceObject->getAIUpdateInterface();
  448. if (sourceAI)
  449. {
  450. sourceAI->aiCombatDrop( targetObject, *targetObject->getPosition(), CMD_FROM_PLAYER );
  451. }
  452. }
  453. */
  454. break;
  455. } // end GameMessage::MSG_COMBATDROP_AT_OBJECT
  456. //---------------------------------------------------------------------------------------------
  457. case GameMessage::MSG_COMBATDROP_AT_LOCATION:
  458. {
  459. Coord3D targetLoc = msg->getArgument( 0 )->location;
  460. if( currentlySelectedGroup )
  461. currentlySelectedGroup->groupCombatDrop( NULL, targetLoc, CMD_FROM_PLAYER );
  462. /*
  463. if( sourceObject )
  464. {
  465. AIUpdateInterface* sourceAI = sourceObject->getAIUpdateInterface();
  466. if (sourceAI)
  467. {
  468. sourceAI->aiCombatDrop( NULL, targetLoc, CMD_FROM_PLAYER );
  469. }
  470. }
  471. */
  472. break;
  473. } // end GameMessage::MSG_COMBATDROP_AT_LOCATION
  474. //---------------------------------------------------------------------------------------------
  475. case GameMessage::MSG_DO_WEAPON_AT_OBJECT:
  476. {
  477. // Lock the weapon choice to the right weapon, then give an attack command
  478. WeaponSlotType weaponSlot = (WeaponSlotType)msg->getArgument( 0 )->integer;
  479. Object *targetObject = TheGameLogic->findObjectByID( msg->getArgument( 1 )->objectID );
  480. Int maxShotsToFire = msg->getArgument( 2 )->integer;
  481. // sanity
  482. if( targetObject == NULL )
  483. break;
  484. // issue command for either single object or for selected group
  485. if( currentlySelectedGroup )
  486. {
  487. // lock it just till the weapon is empty or the attack is "done"
  488. if (currentlySelectedGroup->setWeaponLockForGroup( weaponSlot, LOCKED_TEMPORARILY ))
  489. currentlySelectedGroup->groupAttackObject( targetObject, maxShotsToFire, CMD_FROM_PLAYER );
  490. } // end if, command for group
  491. break;
  492. } // end do weapon at object
  493. //---------------------------------------------------------------------------------------------
  494. case GameMessage::MSG_SWITCH_WEAPONS:
  495. {
  496. // use the selected group
  497. WeaponSlotType weaponSlot = (WeaponSlotType)msg->getArgument( 0 )->integer;
  498. // lock until un-switched, or switched to something else.
  499. if( currentlySelectedGroup )
  500. currentlySelectedGroup->setWeaponLockForGroup( weaponSlot, LOCKED_PERMANENTLY );
  501. break;
  502. }
  503. //---------------------------------------------------------------------------------------------
  504. case GameMessage::MSG_SET_MINE_CLEARING_DETAIL:
  505. {
  506. if( currentlySelectedGroup )
  507. {
  508. currentlySelectedGroup->setMineClearingDetail(true);
  509. }
  510. break;
  511. }
  512. //---------------------------------------------------------------------------------------------
  513. case GameMessage::MSG_DO_WEAPON_AT_LOCATION:
  514. {
  515. WeaponSlotType weaponSlot = (WeaponSlotType)msg->getArgument( 0 )->integer;
  516. Coord3D targetLoc = msg->getArgument( 1 )->location;
  517. Int maxShotsToFire = msg->getArgument( 2 )->integer;
  518. // issue command for either single object or for selected group
  519. if( currentlySelectedGroup )
  520. {
  521. // lock it just till the weapon is empty or the attack is "done"
  522. if (currentlySelectedGroup->setWeaponLockForGroup( weaponSlot, LOCKED_TEMPORARILY ))
  523. currentlySelectedGroup->groupAttackPosition( &targetLoc, maxShotsToFire, CMD_FROM_PLAYER );
  524. } // end if, command for group
  525. break;
  526. } //end do weapon at location
  527. //---------------------------------------------------------------------------------------------
  528. case GameMessage::MSG_DO_SPECIAL_POWER:
  529. {
  530. // first argument is the special power ID
  531. UnsignedInt specialPowerID = msg->getArgument( 0 )->integer;
  532. // Command button options -- special power may care about variance options
  533. UnsignedInt options = msg->getArgument( 1 )->integer;
  534. // check for possible specific source, ignoring selection.
  535. ObjectID sourceID = msg->getArgument(2)->objectID;
  536. Object* source = TheGameLogic->findObjectByID(sourceID);
  537. if (source != NULL)
  538. {
  539. AIGroup* theGroup = TheAI->createGroup();
  540. theGroup->add(source);
  541. theGroup->groupDoSpecialPower( specialPowerID, options );
  542. TheAI->destroyGroup(theGroup);
  543. }
  544. else
  545. {
  546. //Use the selected group!
  547. if( currentlySelectedGroup )
  548. {
  549. currentlySelectedGroup->groupDoSpecialPower( specialPowerID, options );
  550. }
  551. }
  552. break;
  553. } // end do special
  554. //---------------------------------------------------------------------------------------------
  555. case GameMessage::MSG_DO_SPECIAL_POWER_AT_LOCATION:
  556. {
  557. // first argument is the special power ID
  558. UnsignedInt specialPowerID = msg->getArgument( 0 )->integer;
  559. // Location argument 2 is destination
  560. Coord3D targetCoord = msg->getArgument(1)->location;
  561. // Object in way -- if applicable (some specials care, others don't)
  562. ObjectID objectID = msg->getArgument( 2 )->objectID;
  563. Object *objectInWay = TheGameLogic->findObjectByID( objectID );
  564. // Command button options -- special power may care about variance options
  565. UnsignedInt options = msg->getArgument( 3 )->integer;
  566. // check for possible specific source, ignoring selection.
  567. ObjectID sourceID = msg->getArgument(4)->objectID;
  568. Object* source = TheGameLogic->findObjectByID(sourceID);
  569. if (source != NULL)
  570. {
  571. AIGroup* theGroup = TheAI->createGroup();
  572. theGroup->add(source);
  573. theGroup->groupDoSpecialPowerAtLocation( specialPowerID, &targetCoord, objectInWay, options );
  574. TheAI->destroyGroup(theGroup);
  575. }
  576. else
  577. {
  578. //Use the selected group!
  579. if( currentlySelectedGroup )
  580. {
  581. currentlySelectedGroup->groupDoSpecialPowerAtLocation( specialPowerID, &targetCoord, objectInWay, options );
  582. }
  583. }
  584. break;
  585. } // end do special at location
  586. //---------------------------------------------------------------------------------------------
  587. case GameMessage::MSG_DO_SPECIAL_POWER_AT_OBJECT:
  588. {
  589. // first argument is the special power ID
  590. UnsignedInt specialPowerID = msg->getArgument( 0 )->integer;
  591. // argument 2 is target object
  592. ObjectID targetID = msg->getArgument(1)->objectID;
  593. Object *target = TheGameLogic->findObjectByID( targetID );
  594. if( !target )
  595. {
  596. break;
  597. }
  598. // Command button options -- special power may care about variance options
  599. UnsignedInt options = msg->getArgument( 2 )->integer;
  600. // check for possible specific source, ignoring selection.
  601. ObjectID sourceID = msg->getArgument(3)->objectID;
  602. Object* source = TheGameLogic->findObjectByID(sourceID);
  603. if (source != NULL)
  604. {
  605. AIGroup* theGroup = TheAI->createGroup();
  606. theGroup->add(source);
  607. theGroup->groupDoSpecialPowerAtObject( specialPowerID, target, options );
  608. TheAI->destroyGroup(theGroup);
  609. }
  610. else
  611. {
  612. if( currentlySelectedGroup )
  613. {
  614. currentlySelectedGroup->groupDoSpecialPowerAtObject( specialPowerID, target, options );
  615. }
  616. }
  617. break;
  618. } // end do special at object
  619. //---------------------------------------------------------------------------------------------
  620. case GameMessage::MSG_DO_ATTACKMOVETO:
  621. {
  622. Coord3D dest = msg->getArgument( 0 )->location;
  623. if (currentlySelectedGroup)
  624. {
  625. currentlySelectedGroup->releaseWeaponLockForGroup(LOCKED_TEMPORARILY); // release any temporary locks.
  626. currentlySelectedGroup->groupAttackMoveToPosition( &dest, NO_MAX_SHOTS_LIMIT, CMD_FROM_PLAYER );
  627. }
  628. break;
  629. }
  630. //---------------------------------------------------------------------------------------------
  631. case GameMessage::MSG_DO_FORCEMOVETO:
  632. {
  633. Coord3D dest = msg->getArgument( 0 )->location;
  634. if (currentlySelectedGroup)
  635. {
  636. currentlySelectedGroup->releaseWeaponLockForGroup(LOCKED_TEMPORARILY); // release any temporary locks.
  637. currentlySelectedGroup->groupMoveToPosition( &dest, false, CMD_FROM_PLAYER );
  638. }
  639. break;
  640. }
  641. //---------------------------------------------------------------------------------------------
  642. // MSG_DO_SALVAGE is intentionally set up to mimic the moveto.
  643. case GameMessage::MSG_DO_SALVAGE:
  644. case GameMessage::MSG_DO_MOVETO:
  645. {
  646. Coord3D dest = msg->getArgument( 0 )->location;
  647. if( currentlySelectedGroup )
  648. {
  649. //DEBUG_LOG(("GameLogicDispatch - got a MSG_DO_MOVETO command\n"));
  650. currentlySelectedGroup->releaseWeaponLockForGroup(LOCKED_TEMPORARILY); // release any temporary locks.
  651. currentlySelectedGroup->groupMoveToPosition( &dest, false, CMD_FROM_PLAYER );
  652. }
  653. break;
  654. }
  655. //---------------------------------------------------------------------------------------------
  656. case GameMessage::MSG_ADD_WAYPOINT:
  657. {
  658. Coord3D dest = msg->getArgument( 0 )->location;
  659. if( currentlySelectedGroup )
  660. {
  661. //DEBUG_LOG(("GameLogicDispatch - got a MSG_DO_MOVETO command\n"));
  662. currentlySelectedGroup->releaseWeaponLockForGroup(LOCKED_TEMPORARILY); // release any temporary locks.
  663. currentlySelectedGroup->groupMoveToPosition( &dest, true, CMD_FROM_PLAYER );
  664. }
  665. break;
  666. }
  667. //---------------------------------------------------------------------------------------------
  668. case GameMessage::MSG_DO_GUARD_POSITION:
  669. {
  670. Coord3D loc = msg->getArgument( 0 )->location;
  671. GuardMode gm = (GuardMode)msg->getArgument( 1 )->integer;
  672. if (currentlySelectedGroup)
  673. {
  674. currentlySelectedGroup->groupGuardPosition(&loc, gm, CMD_FROM_PLAYER);
  675. }
  676. break;
  677. }
  678. //---------------------------------------------------------------------------------------------
  679. case GameMessage::MSG_DO_GUARD_OBJECT:
  680. {
  681. Object* obj = TheGameLogic->findObjectByID( msg->getArgument( 0 )->objectID );
  682. if (!obj)
  683. break;
  684. GuardMode gm = (GuardMode)msg->getArgument( 1 )->integer;
  685. if (currentlySelectedGroup)
  686. {
  687. currentlySelectedGroup->groupGuardObject(obj, gm, CMD_FROM_PLAYER);
  688. }
  689. break;
  690. }
  691. //---------------------------------------------------------------------------------------------
  692. case GameMessage::MSG_DO_STOP:
  693. {
  694. if (currentlySelectedGroup)
  695. {
  696. currentlySelectedGroup->groupIdle(CMD_FROM_PLAYER);
  697. }
  698. break;
  699. }
  700. //---------------------------------------------------------------------------------------------
  701. case GameMessage::MSG_DO_SCATTER:
  702. {
  703. if (currentlySelectedGroup)
  704. {
  705. currentlySelectedGroup->groupScatter(CMD_FROM_PLAYER);
  706. }
  707. break;
  708. }
  709. //---------------------------------------------------------------------------------------------
  710. case GameMessage::MSG_CREATE_FORMATION:
  711. {
  712. if (currentlySelectedGroup)
  713. {
  714. currentlySelectedGroup->groupCreateFormation(CMD_FROM_PLAYER);
  715. }
  716. break;
  717. }
  718. //---------------------------------------------------------------------------------------------
  719. case GameMessage::MSG_CLEAR_INGAME_POPUP_MESSAGE:
  720. {
  721. if( TheInGameUI )
  722. {
  723. TheInGameUI->clearPopupMessageData();
  724. }
  725. break;
  726. }
  727. //---------------------------------------------------------------------------------------------
  728. case GameMessage::MSG_DO_CHEER:
  729. {
  730. //All selected units play cheer animation.
  731. if( currentlySelectedGroup )
  732. {
  733. currentlySelectedGroup->groupCheer( CMD_FROM_PLAYER );
  734. }
  735. break;
  736. }
  737. #if defined(_DEBUG) || defined(_INTERNAL)
  738. //---------------------------------------------------------------------------------------------
  739. case GameMessage::MSG_DEBUG_KILL_SELECTION:
  740. {
  741. //All selected units die
  742. if( currentlySelectedGroup )
  743. {
  744. const VecObjectID& selectedObjects = currentlySelectedGroup->getAllIDs();
  745. for (VecObjectID::const_iterator it = selectedObjects.begin(); it != selectedObjects.end(); ++it)
  746. {
  747. Object *obj = findObjectByID(*it);
  748. if (obj)
  749. {
  750. obj->kill();
  751. }
  752. }
  753. }
  754. break;
  755. }
  756. //---------------------------------------------------------------------------------------------
  757. case GameMessage::MSG_DEBUG_HURT_OBJECT:
  758. {
  759. Object* objToHurt = TheGameLogic->findObjectByID( msg->getArgument( 0 )->objectID );
  760. if (objToHurt)
  761. {
  762. DamageInfo damageInfo;
  763. damageInfo.in.m_damageType = DAMAGE_UNRESISTABLE;
  764. damageInfo.in.m_deathType = DEATH_NORMAL;
  765. damageInfo.in.m_sourceID = INVALID_ID;
  766. damageInfo.in.m_amount = objToHurt->getBodyModule()->getMaxHealth() / 10.0f;
  767. objToHurt->attemptDamage( &damageInfo );
  768. }
  769. break;
  770. }
  771. //---------------------------------------------------------------------------------------------
  772. case GameMessage::MSG_DEBUG_KILL_OBJECT:
  773. {
  774. Object* objToHurt = TheGameLogic->findObjectByID( msg->getArgument( 0 )->objectID );
  775. if (objToHurt)
  776. {
  777. objToHurt->kill();
  778. }
  779. break;
  780. }
  781. #endif
  782. #ifdef ALLOW_SURRENDER
  783. //---------------------------------------------------------------------------------------------
  784. case GameMessage::MSG_DO_SURRENDER:
  785. {
  786. //All selected units surrender
  787. if( currentlySelectedGroup )
  788. {
  789. Object* objWeSurrenderedTo = TheGameLogic->findObjectByID( msg->getArgument( 0 )->objectID );
  790. Bool surrender = msg->getArgument( 1 )->boolean;
  791. currentlySelectedGroup->groupSurrender( objWeSurrenderedTo, surrender, CMD_FROM_PLAYER );
  792. }
  793. break;
  794. }
  795. #endif
  796. //---------------------------------------------------------------------------------------------
  797. case GameMessage::MSG_ENTER:
  798. {
  799. Object *enter = TheGameLogic->findObjectByID( msg->getArgument( 1 )->objectID );
  800. // sanity
  801. if( enter == NULL )
  802. break;
  803. if( currentlySelectedGroup )
  804. {
  805. currentlySelectedGroup->releaseWeaponLockForGroup(LOCKED_TEMPORARILY); // release any temporary locks.
  806. currentlySelectedGroup->groupEnter( enter, CMD_FROM_PLAYER );
  807. }
  808. break;
  809. } // end GameMessage::MSG_ENTER
  810. //---------------------------------------------------------------------------------------------
  811. case GameMessage::MSG_EXIT:
  812. {
  813. Object *objectWantingToExit = TheGameLogic->findObjectByID( msg->getArgument( 0 )->objectID );
  814. Object *objectContainingExiter = getSingleObjectFromSelection(currentlySelectedGroup);
  815. // sanity
  816. if( objectWantingToExit == NULL )
  817. break;
  818. if( objectContainingExiter == NULL )
  819. break;
  820. // sanity, the player must actually control this object
  821. if( objectWantingToExit->getControllingPlayer() != thisPlayer )
  822. break;
  823. objectWantingToExit->releaseWeaponLock(LOCKED_TEMPORARILY); // release any temporary locks.
  824. // exit whatever objectWantingToExit is INSIDE of
  825. AIUpdateInterface *ai = objectWantingToExit->getAIUpdateInterface();
  826. if( ai )
  827. ai->aiExit( objectContainingExiter, CMD_FROM_PLAYER );
  828. // Just like Enter, Exit needs to know the thing to exit. This can no longer be assumed because of the Tunnel system.
  829. // If you do not specify the thing to Exit, it will Exit the thing it thinks it is in. For a tunnel network,
  830. // that will be the specific Tunnel it entered. (Scripts can talk directly to the guy to say Get Out Regardless)
  831. break;
  832. } // end GameMessage::MSG_EXIT
  833. //---------------------------------------------------------------------------------------------
  834. case GameMessage::MSG_EVACUATE:
  835. {
  836. // issue command for either single object or for selected group
  837. // AIGroup *group = TheAI->findGroup( *selectedGroupID );
  838. if( currentlySelectedGroup )
  839. {
  840. //Coord3D pos;
  841. //Bool hasArgs = FALSE;
  842. //hasArgs = (msg->getArgumentCount() > 0);
  843. //if (hasArgs)
  844. // pos = msg->getArgument(0)->location;
  845. currentlySelectedGroup->releaseWeaponLockForGroup(LOCKED_TEMPORARILY); // release any temporary locks.
  846. // evacuate message is for the selected group
  847. //if (hasArgs)
  848. // currentlySelectedGroup->groupMoveToAndEvacuate( &pos, CMD_FROM_PLAYER );
  849. //else
  850. currentlySelectedGroup->groupEvacuate( CMD_FROM_PLAYER );
  851. // no, this is bad, don't do here, do when POSTING message
  852. // pickAndPlayUnitVoiceResponse( TheInGameUI->getAllSelectedDrawables(), GameMessage::MSG_EVACUATE );
  853. } // end if, command for group
  854. break;
  855. } // end GameMessage::MSG_EVACUATE
  856. // --------------------------------------------------------------------------------------------
  857. case GameMessage::MSG_EXECUTE_RAILED_TRANSPORT:
  858. {
  859. // issue command to currently selected objects
  860. if( currentlySelectedGroup )
  861. currentlySelectedGroup->groupExecuteRailedTransport( CMD_FROM_PLAYER );
  862. break;
  863. } // end GameMessage::MSG_EXECUTE_RAILED_TRANSPORT
  864. //---------------------------------------------------------------------------------------------
  865. case GameMessage::MSG_INTERNET_HACK:
  866. {
  867. // ObjectID sourceID = msg->getArgument( 0 )->objectID;
  868. if( currentlySelectedGroup )
  869. {
  870. currentlySelectedGroup->releaseWeaponLockForGroup(LOCKED_TEMPORARILY); // release any temporary locks.
  871. currentlySelectedGroup->groupHackInternet( CMD_FROM_PLAYER );
  872. }
  873. break;
  874. }
  875. // --------------------------------------------------------------------------------------------
  876. case GameMessage::MSG_GET_REPAIRED:
  877. {
  878. Object *repairDepot = TheGameLogic->findObjectByID( msg->getArgument( 0 )->objectID );
  879. // sanity
  880. if( repairDepot == NULL )
  881. break;
  882. // tell the currently selected group to go get repaired
  883. if( currentlySelectedGroup )
  884. currentlySelectedGroup->groupGetRepaired( repairDepot, CMD_FROM_PLAYER );
  885. break;
  886. } // end get repaired
  887. // --------------------------------------------------------------------------------------------
  888. case GameMessage::MSG_DOCK:
  889. {
  890. Object *dockBuilding = TheGameLogic->findObjectByID( msg->getArgument( 0 )->objectID );
  891. // sanity
  892. if( dockBuilding == NULL )
  893. break;
  894. // tell the currently selected group to go get repaired
  895. if( currentlySelectedGroup )
  896. currentlySelectedGroup->groupDock( dockBuilding, CMD_FROM_PLAYER );
  897. break;
  898. } // end get repaired
  899. // --------------------------------------------------------------------------------------------
  900. case GameMessage::MSG_GET_HEALED:
  901. {
  902. Object *healDest = TheGameLogic->findObjectByID( msg->getArgument( 0 )->objectID );
  903. // sanity
  904. if( healDest == NULL )
  905. break;
  906. // tell the currently selected group to enter the building for healing
  907. if( currentlySelectedGroup )
  908. currentlySelectedGroup->groupGetHealed( healDest, CMD_FROM_PLAYER );
  909. break;
  910. } // end get repaired
  911. // --------------------------------------------------------------------------------------------
  912. case GameMessage::MSG_DO_REPAIR:
  913. {
  914. Object *repairTarget = TheGameLogic->findObjectByID( msg->getArgument( 0 )->objectID );
  915. // sanity
  916. if( repairTarget == NULL )
  917. break;
  918. //
  919. // tell the currently selected group of objects to go repair the target object, note
  920. // that only one of them will actually go ahead and do the repair
  921. //
  922. if( currentlySelectedGroup )
  923. currentlySelectedGroup->groupRepair( repairTarget, CMD_FROM_PLAYER );
  924. break;
  925. } // end get repaired
  926. // --------------------------------------------------------------------------------------------
  927. case GameMessage::MSG_RESUME_CONSTRUCTION:
  928. {
  929. Object *constructTarget = TheGameLogic->findObjectByID( msg->getArgument( 0 )->objectID );
  930. // sanity
  931. if( constructTarget == NULL )
  932. break;
  933. //
  934. // tell the currently selected group of objects to resume construction on
  935. // the target object, note that only one of them will go off and resume construction
  936. // on the target
  937. //
  938. if( currentlySelectedGroup )
  939. currentlySelectedGroup->groupResumeConstruction( constructTarget, CMD_FROM_PLAYER );
  940. // no, this is bad, don't do here, do when POSTING message
  941. // pickAndPlayUnitVoiceResponse( TheInGameUI->getAllSelectedDrawables(), msg->getType() );
  942. break;
  943. } // end resume construction
  944. // --------------------------------------------------------------------------------------------
  945. case GameMessage::MSG_DO_SPECIAL_POWER_OVERRIDE_DESTINATION:
  946. {
  947. const Coord3D *loc = &msg->getArgument( 0 )->location;
  948. SpecialPowerType spType = (SpecialPowerType)msg->getArgument( 1 )->integer;
  949. ObjectID sourceID = msg->getArgument(2)->objectID;
  950. Object* source = TheGameLogic->findObjectByID(sourceID);
  951. if (source != NULL)
  952. {
  953. AIGroup* theGroup = TheAI->createGroup();
  954. theGroup->add(source);
  955. theGroup->groupOverrideSpecialPowerDestination( spType, loc, CMD_FROM_PLAYER );
  956. TheAI->destroyGroup(theGroup);
  957. }
  958. else
  959. {
  960. if( currentlySelectedGroup )
  961. {
  962. currentlySelectedGroup->groupOverrideSpecialPowerDestination( spType, loc, CMD_FROM_PLAYER );
  963. }
  964. }
  965. }
  966. //---------------------------------------------------------------------------------------------
  967. case GameMessage::MSG_DO_ATTACK_OBJECT:
  968. {
  969. Object *enemy = TheGameLogic->findObjectByID( msg->getArgument( 0 )->objectID );
  970. // Check enemy, as it is possible that he died this frame.
  971. if (enemy)
  972. {
  973. if (currentlySelectedGroup)
  974. {
  975. currentlySelectedGroup->releaseWeaponLockForGroup(LOCKED_TEMPORARILY); // release any temporary locks.
  976. currentlySelectedGroup->groupAttackObject( enemy, NO_MAX_SHOTS_LIMIT, CMD_FROM_PLAYER );
  977. }
  978. }
  979. break;
  980. } // end GameMessage::MSG_DO_ATTACK_GROUND_OBJECT
  981. //---------------------------------------------------------------------------------------------
  982. case GameMessage::MSG_DO_FORCE_ATTACK_OBJECT:
  983. {
  984. Object *enemy = TheGameLogic->findObjectByID( msg->getArgument( 0 )->objectID );
  985. // Check enemy, as it is possible that he died this frame.
  986. if (enemy)
  987. {
  988. if (currentlySelectedGroup)
  989. {
  990. currentlySelectedGroup->releaseWeaponLockForGroup(LOCKED_TEMPORARILY); // release any temporary locks.
  991. currentlySelectedGroup->groupForceAttackObject( enemy, NO_MAX_SHOTS_LIMIT, CMD_FROM_PLAYER );
  992. }
  993. }
  994. break;
  995. } // end GameMessage::MSG_DO_ATTACK_GROUND_OBJECT
  996. //---------------------------------------------------------------------------------------------
  997. case GameMessage::MSG_DO_FORCE_ATTACK_GROUND:
  998. {
  999. const Coord3D *pos = &msg->getArgument( 0 )->location;
  1000. if (currentlySelectedGroup)
  1001. {
  1002. /////////////////////////////////////////////////////////////////////
  1003. //Lorenzen sez: unclear, yet how to solve this for all cases
  1004. //Kris: This code was added to allow the toxin tractor to force attack
  1005. // while contaminating. When this change was made, it was causing
  1006. // rangers and scud launchers to reset to primary weapon mode whenever
  1007. // force attacking while not idle. I fixed this by enforcing the
  1008. // temporary and permanent modes that are already set when attempting
  1009. // the new lock. In this case, the temp lock attempt will fail whenever
  1010. // a permanent lock is in effect, thus fixing the ranger and scud and
  1011. // allowing the tox tractor to work as well.
  1012. Bool forceAttackRequiresPrimaryWeapon = !currentlySelectedGroup->isIdle();
  1013. if ( forceAttackRequiresPrimaryWeapon )
  1014. {
  1015. currentlySelectedGroup->setWeaponLockForGroup( PRIMARY_WEAPON, LOCKED_TEMPORARILY );
  1016. currentlySelectedGroup->groupAttackPosition( pos, NO_MAX_SHOTS_LIMIT, CMD_FROM_PLAYER );
  1017. currentlySelectedGroup->releaseWeaponLockForGroup(LOCKED_TEMPORARILY);
  1018. }
  1019. else
  1020. ///////////////////////////////////////////////////////////////////
  1021. {
  1022. currentlySelectedGroup->releaseWeaponLockForGroup(LOCKED_TEMPORARILY);
  1023. currentlySelectedGroup->groupAttackPosition( pos, NO_MAX_SHOTS_LIMIT, CMD_FROM_PLAYER );
  1024. }
  1025. }
  1026. break;
  1027. } // end GameMessage::MSG_DO_ATTACK_GROUND_OBJECT
  1028. //---------------------------------------------------------------------------------------------
  1029. case GameMessage::MSG_QUEUE_UPGRADE:
  1030. {
  1031. const UpgradeTemplate *upgradeT = TheUpgradeCenter->findUpgradeByKey( (NameKeyType)(msg->getArgument( 1 )->integer) );
  1032. if (!upgradeT) // sanity
  1033. break;
  1034. if (currentlySelectedGroup)
  1035. currentlySelectedGroup->queueUpgrade( upgradeT );
  1036. break;
  1037. } // end queue upgrade
  1038. //---------------------------------------------------------------------------------------------
  1039. case GameMessage::MSG_CANCEL_UPGRADE:
  1040. {
  1041. Object *producer = getSingleObjectFromSelection(currentlySelectedGroup);
  1042. const UpgradeTemplate *upgradeT = TheUpgradeCenter->findUpgradeByKey( (NameKeyType)(msg->getArgument( 0 )->integer) );
  1043. // sanity
  1044. if( producer == NULL || upgradeT == NULL )
  1045. break;
  1046. // the player must actually control the producer object
  1047. if( producer->getControllingPlayer() != thisPlayer )
  1048. break;
  1049. // producer must have a production update
  1050. ProductionUpdateInterface *pu = producer->getProductionUpdateInterface();
  1051. if( pu == NULL )
  1052. break;
  1053. // cancel the upgrade
  1054. pu->cancelUpgrade( upgradeT );
  1055. break;
  1056. } // end cancel upgrade
  1057. //---------------------------------------------------------------------------------------------
  1058. case GameMessage::MSG_QUEUE_UNIT_CREATE:
  1059. {
  1060. Object *producer = getSingleObjectFromSelection(currentlySelectedGroup);
  1061. const ThingTemplate *whatToCreate;
  1062. ProductionID productionID;
  1063. // get data from the message
  1064. whatToCreate = TheThingFactory->findByTemplateID( msg->getArgument( 0 )->integer );
  1065. productionID = (ProductionID)msg->getArgument( 1 )->integer;
  1066. // sanity
  1067. if ( producer == NULL || whatToCreate == NULL )
  1068. break;
  1069. // get the production interface for the producer
  1070. ProductionUpdateInterface *pu = producer->getProductionUpdateInterface();
  1071. if( pu == NULL )
  1072. {
  1073. DEBUG_ASSERTCRASH( 0, ("MSG_QUEUE_UNIT_CREATE: Producer '%s' doesn't have a unit production interface\n",
  1074. producer->getTemplate()->getName().str()) );
  1075. break;
  1076. } // end if
  1077. // queue the build
  1078. pu->queueCreateUnit( whatToCreate, productionID );
  1079. break;
  1080. } // end GameMessage::MSG_QUEUE_UNIT_CREATE
  1081. //-------------------------------------------------------------------------------------------------
  1082. case GameMessage::MSG_CANCEL_UNIT_CREATE:
  1083. {
  1084. Object *producer = getSingleObjectFromSelection(currentlySelectedGroup);
  1085. ProductionID productionID = (ProductionID)msg->getArgument( 0 )->integer;
  1086. // sanity
  1087. if( producer == NULL )
  1088. break;
  1089. // sanity, the player must control the producer
  1090. if( producer->getControllingPlayer() != thisPlayer )
  1091. break;
  1092. // get the unit production interface
  1093. ProductionUpdateInterface *pu = producer->getProductionUpdateInterface();
  1094. if( pu == NULL )
  1095. return;
  1096. // cancel the production
  1097. pu->cancelUnitCreate( productionID );
  1098. break;
  1099. } // end GameMessage::MSG_CANCEL_UNIT_CREATE
  1100. //---------------------------------------------------------------------------------------------
  1101. case GameMessage::MSG_DOZER_CONSTRUCT:
  1102. case GameMessage::MSG_DOZER_CONSTRUCT_LINE:
  1103. {
  1104. const ThingTemplate *place;
  1105. Coord3D loc;
  1106. Real angle;
  1107. // get player, what to place, and location
  1108. Object *constructorObject = getSingleObjectFromSelection(currentlySelectedGroup);
  1109. place = TheThingFactory->findByTemplateID( msg->getArgument( 0 )->integer );
  1110. loc = msg->getArgument( 1 )->location;
  1111. angle = msg->getArgument( 2 )->real;
  1112. if( place == NULL || constructorObject == NULL )
  1113. break; //These are not crashes, as the object may have died before this message came in
  1114. if( msg->getType() == GameMessage::MSG_DOZER_CONSTRUCT )
  1115. {
  1116. TheBuildAssistant->buildObjectNow( constructorObject, place, &loc, angle,
  1117. constructorObject->getControllingPlayer() );
  1118. } // end if
  1119. else
  1120. {
  1121. Coord3D locEnd;
  1122. // get the end of the line location in the world
  1123. locEnd = msg->getArgument( 3 )->location;
  1124. // place the line of structures, the end location being present will make it happen
  1125. TheBuildAssistant->buildObjectLineNow( constructorObject, place, &loc, &locEnd, angle,
  1126. constructorObject->getControllingPlayer() );
  1127. } // end else
  1128. // place the sound for putting a building down
  1129. static AudioEventRTS placeBuilding(AsciiString("PlaceBuilding"));
  1130. placeBuilding.setObjectID(constructorObject->getID());
  1131. TheAudio->addAudioEvent( &placeBuilding );
  1132. // no, this is bad, don't do here, do when POSTING message
  1133. // pickAndPlayUnitVoiceResponse( TheInGameUI->getAllSelectedDrawables(), msg->getType() );
  1134. break;
  1135. } // end build start
  1136. //---------------------------------------------------------------------------------------------
  1137. case GameMessage::MSG_DOZER_CANCEL_CONSTRUCT:
  1138. {
  1139. // get the building to cancel construction on
  1140. Object *building = getSingleObjectFromSelection(currentlySelectedGroup);
  1141. if( building == NULL )
  1142. break;
  1143. // the player sending this message must actually control this building
  1144. if( building->getControllingPlayer() != thisPlayer )
  1145. break;
  1146. // Check to make sure it is actually under construction
  1147. if( !building->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION) )
  1148. break;
  1149. // OK, refund the money to the player, unless it is a rebuilding Hole.
  1150. if( !building->testStatus(OBJECT_STATUS_RECONSTRUCTING))
  1151. {
  1152. Money *money = thisPlayer->getMoney();
  1153. UnsignedInt amount = building->getTemplate()->calcCostToBuild( thisPlayer );
  1154. money->deposit( amount );
  1155. }
  1156. //
  1157. // Destroy the building ... killing the
  1158. // building will automatically cause the dozer also cancel its own building
  1159. // behavior and it will go on its merry way doing other assigned tasks
  1160. //
  1161. building->kill();
  1162. break;
  1163. } // end cancel dozer construction
  1164. // --------------------------------------------------------------------------------------------
  1165. case GameMessage::MSG_SELL:
  1166. {
  1167. // use the selected group
  1168. if( currentlySelectedGroup )
  1169. currentlySelectedGroup->groupSell( CMD_FROM_PLAYER );
  1170. break;
  1171. } // end sell
  1172. // --------------------------------------------------------------------------------------------
  1173. case GameMessage::MSG_TOGGLE_OVERCHARGE:
  1174. {
  1175. // use the selected group
  1176. if( currentlySelectedGroup )
  1177. currentlySelectedGroup->groupToggleOvercharge( CMD_FROM_PLAYER );
  1178. break;
  1179. } // end toggle overcharge
  1180. #ifdef ALLOW_SURRENDER
  1181. // --------------------------------------------------------------------------------------------
  1182. case GameMessage::MSG_PICK_UP_PRISONER:
  1183. {
  1184. Object *prisoner = TheGameLogic->findObjectByID( msg->getArgument( 0 )->objectID );
  1185. if( prisoner )
  1186. {
  1187. // use selected group
  1188. if( currentlySelectedGroup )
  1189. currentlySelectedGroup->groupPickUpPrisoner( prisoner, CMD_FROM_PLAYER );
  1190. } // end if
  1191. break;
  1192. } // end pick up prisoner
  1193. #endif
  1194. #ifdef ALLOW_SURRENDER
  1195. // --------------------------------------------------------------------------------------------
  1196. case GameMessage::MSG_RETURN_TO_PRISON:
  1197. {
  1198. // use selected group
  1199. if( currentlySelectedGroup )
  1200. currentlySelectedGroup->groupReturnToPrison( NULL, CMD_FROM_PLAYER );
  1201. break;
  1202. } // end return to prison
  1203. #endif
  1204. //---------------------------------------------------------------------------------------------
  1205. // No sound does exactly the same logical processing as the usual message. Just double them up.
  1206. case GameMessage::MSG_CREATE_SELECTED_GROUP_NO_SOUND:
  1207. case GameMessage::MSG_CREATE_SELECTED_GROUP:
  1208. {
  1209. Bool createNewGroup = msg->getArgument( 0 )->boolean;
  1210. Player *player = ThePlayerList->getNthPlayer(msg->getPlayerIndex());
  1211. if (player == NULL) {
  1212. DEBUG_CRASH(("GameLogicDispatch - MSG_CREATE_SELECTED_GROUP had an invalid player nubmer"));
  1213. break;
  1214. }
  1215. Bool firstObject = TRUE;
  1216. for (Int i = 1; i < msg->getArgumentCount(); ++i) {
  1217. Object *obj = TheGameLogic->findObjectByID( msg->getArgument( i )->objectID );
  1218. if (!obj) {
  1219. continue;
  1220. }
  1221. TheGameLogic->selectObject(obj, createNewGroup && firstObject, player->getPlayerMask());
  1222. firstObject = FALSE;
  1223. }
  1224. break;
  1225. } // end build start
  1226. //---------------------------------------------------------------------------------------------
  1227. case GameMessage::MSG_REMOVE_FROM_SELECTED_GROUP:
  1228. {
  1229. Player *player = ThePlayerList->getNthPlayer(msg->getPlayerIndex());
  1230. if (player == NULL) {
  1231. DEBUG_CRASH(("GameLogicDispatch - MSG_CREATE_SELECTED_GROUP had an invalid player nubmer"));
  1232. break;
  1233. }
  1234. for (Int i = 0; i < msg->getArgumentCount(); ++i) {
  1235. ObjectID objID = msg->getArgument(i)->objectID;
  1236. Object *objToRemove = TheGameLogic->findObjectByID(objID);
  1237. if (!objToRemove) {
  1238. continue;
  1239. }
  1240. TheGameLogic->deselectObject(objToRemove, player->getPlayerMask());
  1241. }
  1242. break;
  1243. } // end MSG_REMOVE_FROM_SELECTED_GROUP
  1244. //---------------------------------------------------------------------------------------------
  1245. case GameMessage::MSG_DESTROY_SELECTED_GROUP:
  1246. {
  1247. Player *player = ThePlayerList->getNthPlayer(msg->getPlayerIndex());
  1248. if (player != NULL)
  1249. {
  1250. player->setCurrentlySelectedAIGroup(NULL);
  1251. }
  1252. break;
  1253. } // end build start
  1254. // --------------------------------------------------------------------------------------------
  1255. case GameMessage::MSG_SELECTED_GROUP_COMMAND:
  1256. {
  1257. break;
  1258. } // end selected group command
  1259. // --------------------------------------------------------------------------------------------
  1260. case GameMessage::MSG_PLACE_BEACON:
  1261. {
  1262. // how many does this player have active?
  1263. Coord3D pos = msg->getArgument( 0 )->location;
  1264. Region3D r;
  1265. TheTerrainLogic->getExtent(&r);
  1266. if (!r.isInRegionNoZ(&pos))
  1267. pos = TheTerrainLogic->findClosestEdgePoint(&pos);
  1268. const ThingTemplate *thing = TheThingFactory->findTemplate( thisPlayer->getPlayerTemplate()->getBeaconTemplate() );
  1269. if (thing && !TheVictoryConditions->hasSinglePlayerBeenDefeated(thisPlayer))
  1270. {
  1271. Int count;
  1272. thisPlayer->countObjectsByThingTemplate( 1, &thing, false, &count );
  1273. DEBUG_LOG(("Player already has %d beacons active\n", count));
  1274. if (count >= TheMultiplayerSettings->getMaxBeaconsPerPlayer())
  1275. {
  1276. if (thisPlayer == ThePlayerList->getLocalPlayer())
  1277. {
  1278. // tell the user
  1279. TheInGameUI->message( TheGameText->fetch("GUI:TooManyBeacons") );
  1280. // play a sound
  1281. static AudioEventRTS aSound("BeaconPlacementFailed");
  1282. aSound.setPosition(&pos);
  1283. aSound.setPlayerIndex(thisPlayer->getPlayerIndex());
  1284. TheAudio->addAudioEvent(&aSound);
  1285. }
  1286. break;
  1287. }
  1288. Object *object = TheThingFactory->newObject( thing, thisPlayer->getDefaultTeam() );
  1289. object->setPosition( &pos );
  1290. object->setProducer(NULL);
  1291. if (thisPlayer->getRelationship( ThePlayerList->getLocalPlayer()->getDefaultTeam() ) == ALLIES || ThePlayerList->getLocalPlayer()->isPlayerObserver())
  1292. {
  1293. // tell the user
  1294. UnicodeString s;
  1295. s.format(TheGameText->fetch("GUI:BeaconPlaced"), thisPlayer->getPlayerDisplayName().str());
  1296. TheInGameUI->message( s );
  1297. // play a sound
  1298. static AudioEventRTS aSound("BeaconPlaced");
  1299. aSound.setPlayerIndex(thisPlayer->getPlayerIndex());
  1300. aSound.setPosition(&pos);
  1301. TheAudio->addAudioEvent(&aSound);
  1302. // beacons are a rare event; play a nifty radar event thingie
  1303. TheRadar->createEvent( object->getPosition(), RADAR_EVENT_INFORMATION );
  1304. if (ThePlayerList->getLocalPlayer()->getRelationship(thisPlayer->getDefaultTeam()) == ALLIES)
  1305. TheEva->setShouldPlay(EVA_BeaconDetected);
  1306. TheControlBar->markUIDirty(); // check if we should grey out the button
  1307. }
  1308. else
  1309. {
  1310. Int updateCount = 0;
  1311. static NameKeyType nameKeyClientUpdate = NAMEKEY("BeaconClientUpdate");
  1312. ClientUpdateModule ** clientModules = object->getDrawable()->getClientUpdateModules();
  1313. if (clientModules)
  1314. {
  1315. while (*clientModules)
  1316. {
  1317. if ((*clientModules)->getModuleNameKey() == nameKeyClientUpdate)
  1318. {
  1319. (*(BeaconClientUpdate **)clientModules)->hideBeacon();
  1320. ++updateCount;
  1321. }
  1322. ++clientModules;
  1323. }
  1324. }
  1325. DEBUG_ASSERTCRASH(updateCount == 1, ("Saw %d update modules for the beacon!", updateCount));
  1326. }
  1327. }
  1328. else
  1329. {
  1330. // tell the user
  1331. TheInGameUI->message( TheGameText->fetch("GUI:BeaconPlacementFailed") );
  1332. // play a sound
  1333. static AudioEventRTS aSound("BeaconPlacementFailed");
  1334. aSound.setPosition(&pos);
  1335. aSound.setPlayerIndex(thisPlayer->getPlayerIndex());
  1336. TheAudio->addAudioEvent(&aSound);
  1337. }
  1338. break;
  1339. } // end beacon placement
  1340. // --------------------------------------------------------------------------------------------
  1341. case GameMessage::MSG_REMOVE_BEACON:
  1342. {
  1343. AIGroup *allSelectedObjects = NULL;
  1344. allSelectedObjects = TheAI->createGroup();
  1345. thisPlayer->getCurrentSelectionAsAIGroup(allSelectedObjects); // need to act on all objects, so we can hide teammates' beacons.
  1346. if( allSelectedObjects )
  1347. {
  1348. const VecObjectID& selectedObjects = allSelectedObjects->getAllIDs();
  1349. for (VecObjectID::const_iterator it = selectedObjects.begin(); it != selectedObjects.end(); ++it)
  1350. {
  1351. Object *beacon = findObjectByID(*it);
  1352. if (beacon)
  1353. {
  1354. const ThingTemplate *thing = TheThingFactory->findTemplate( beacon->getControllingPlayer()->getPlayerTemplate()->getBeaconTemplate() );
  1355. if (thing->isEquivalentTo(beacon->getTemplate()))
  1356. {
  1357. if (beacon->getControllingPlayer() == thisPlayer)
  1358. {
  1359. TheGameLogic->destroyObject(beacon); // the owner is telling it to go away. such is life.
  1360. TheControlBar->markUIDirty(); // check if we should un-grey out the button
  1361. }
  1362. else if (thisPlayer == ThePlayerList->getLocalPlayer())
  1363. {
  1364. Drawable *beaconDrawable = beacon->getDrawable();
  1365. if (beaconDrawable)
  1366. {
  1367. static NameKeyType nameKeyClientUpdate = NAMEKEY("BeaconClientUpdate");
  1368. ClientUpdateModule ** clientModules = beaconDrawable->getClientUpdateModules();
  1369. if (clientModules)
  1370. {
  1371. while (*clientModules)
  1372. {
  1373. if ((*clientModules)->getModuleNameKey() == nameKeyClientUpdate)
  1374. (*(BeaconClientUpdate **)clientModules)->hideBeacon();
  1375. ++clientModules;
  1376. }
  1377. }
  1378. }
  1379. }
  1380. }
  1381. }
  1382. }
  1383. if (allSelectedObjects->isEmpty())
  1384. {
  1385. TheAI->destroyGroup(allSelectedObjects);
  1386. allSelectedObjects = NULL;
  1387. }
  1388. }
  1389. break;
  1390. } // end beacon removal
  1391. // --------------------------------------------------------------------------------------------
  1392. case GameMessage::MSG_SET_BEACON_TEXT:
  1393. {
  1394. if( currentlySelectedGroup )
  1395. {
  1396. const VecObjectID& selectedObjects = currentlySelectedGroup->getAllIDs();
  1397. for (VecObjectID::const_iterator it = selectedObjects.begin(); it != selectedObjects.end(); ++it)
  1398. {
  1399. Object *beacon = findObjectByID(*it);
  1400. if (beacon)
  1401. {
  1402. Drawable *beaconDrawable = beacon->getDrawable();
  1403. if (beaconDrawable)
  1404. {
  1405. UnicodeString s;
  1406. for( int i=0; i<msg->getArgumentCount(); i++ )
  1407. {
  1408. s.concat( msg->getArgument(i)->wChar );
  1409. }
  1410. if (s.isEmpty())
  1411. beaconDrawable->clearCaptionText();
  1412. else
  1413. beaconDrawable->setCaptionText(s);
  1414. }
  1415. }
  1416. }
  1417. }
  1418. break;
  1419. } // end beacon text
  1420. // --------------------------------------------------------------------------------------------
  1421. case GameMessage::MSG_SELF_DESTRUCT:
  1422. {
  1423. if (msg->getArgument(0)->boolean)
  1424. {
  1425. // transfer control to any living ally
  1426. for (Int i=0; i<ThePlayerList->getPlayerCount(); ++i)
  1427. {
  1428. if (i != msg->getPlayerIndex())
  1429. {
  1430. Player *otherPlayer = ThePlayerList->getNthPlayer(i);
  1431. if (thisPlayer->getRelationship(otherPlayer->getDefaultTeam()) == ALLIES &&
  1432. otherPlayer->getRelationship(thisPlayer->getDefaultTeam()) == ALLIES)
  1433. {
  1434. if (TheVictoryConditions->hasSinglePlayerBeenDefeated(otherPlayer))
  1435. continue;
  1436. // a living ally! hooray!
  1437. otherPlayer->transferAssetsFromThat(thisPlayer);
  1438. thisPlayer->killPlayer(); // just to be safe (and to kill beacons etc that don't transfer)
  1439. break;
  1440. }
  1441. }
  1442. }
  1443. if (i == ThePlayerList->getPlayerCount())
  1444. {
  1445. // didn't find any allies. die, loner!
  1446. thisPlayer->killPlayer();
  1447. }
  1448. }
  1449. else
  1450. {
  1451. thisPlayer->killPlayer();
  1452. }
  1453. // There is no reason to do any notification here, it now takes place in the victory conditions.
  1454. // bonehead.
  1455. break;
  1456. }
  1457. // --------------------------------------------------------------------------------------------
  1458. case GameMessage::MSG_SET_REPLAY_CAMERA:
  1459. {
  1460. if (TheRecorder->getMode() == RECORDERMODETYPE_PLAYBACK && TheGlobalData->m_useCameraInReplay && TheControlBar->getObserverLookAtPlayer() == thisPlayer)
  1461. {
  1462. if (TheTacticalView->isCameraMovementFinished())
  1463. {
  1464. ViewLocation loc;
  1465. Coord3D pos;
  1466. Real pitch, angle, zoom;
  1467. pos = msg->getArgument( 0 )->location;
  1468. angle = msg->getArgument( 1 )->real;
  1469. pitch = msg->getArgument( 2 )->real;
  1470. zoom = msg->getArgument( 3 )->real;
  1471. loc.init(pos.x, pos.y, pos.z, angle, pitch, zoom);
  1472. TheTacticalView->setLocation( &loc );
  1473. if (!TheLookAtTranslator->hasMouseMovedRecently())
  1474. {
  1475. TheMouse->setCursor( (Mouse::MouseCursor)(msg->getArgument( 4 )->integer) );
  1476. ICoord2D mousePos = msg->getArgument( 5 )->pixel;
  1477. TheMouse->setPosition( mousePos.x, mousePos.y );
  1478. TheLookAtTranslator->setCurrentPos( mousePos );
  1479. }
  1480. }
  1481. }
  1482. break;
  1483. } // end beacon text
  1484. //---------------------------------------------------------------------------------------------
  1485. case GameMessage::MSG_CREATE_TEAM0:
  1486. case GameMessage::MSG_CREATE_TEAM1:
  1487. case GameMessage::MSG_CREATE_TEAM2:
  1488. case GameMessage::MSG_CREATE_TEAM3:
  1489. case GameMessage::MSG_CREATE_TEAM4:
  1490. case GameMessage::MSG_CREATE_TEAM5:
  1491. case GameMessage::MSG_CREATE_TEAM6:
  1492. case GameMessage::MSG_CREATE_TEAM7:
  1493. case GameMessage::MSG_CREATE_TEAM8:
  1494. case GameMessage::MSG_CREATE_TEAM9:
  1495. {
  1496. Int playerIndex = msg->getPlayerIndex();
  1497. Player *player = ThePlayerList->getNthPlayer(playerIndex);
  1498. DEBUG_ASSERTCRASH(player != NULL, ("Could not find player for create team message"));
  1499. if (player == NULL)
  1500. {
  1501. break;
  1502. }
  1503. player->processCreateTeamGameMessage(msg->getType() - GameMessage::MSG_CREATE_TEAM0, msg);
  1504. break;
  1505. } // end create team command
  1506. case GameMessage::MSG_SELECT_TEAM0:
  1507. case GameMessage::MSG_SELECT_TEAM1:
  1508. case GameMessage::MSG_SELECT_TEAM2:
  1509. case GameMessage::MSG_SELECT_TEAM3:
  1510. case GameMessage::MSG_SELECT_TEAM4:
  1511. case GameMessage::MSG_SELECT_TEAM5:
  1512. case GameMessage::MSG_SELECT_TEAM6:
  1513. case GameMessage::MSG_SELECT_TEAM7:
  1514. case GameMessage::MSG_SELECT_TEAM8:
  1515. case GameMessage::MSG_SELECT_TEAM9:
  1516. {
  1517. Int playerIndex = msg->getPlayerIndex();
  1518. Player *player = ThePlayerList->getNthPlayer(playerIndex);
  1519. DEBUG_ASSERTCRASH(player != NULL, ("Could not find player for select team message"));
  1520. if (player == NULL)
  1521. {
  1522. break;
  1523. }
  1524. player->processSelectTeamGameMessage(msg->getType() - GameMessage::MSG_SELECT_TEAM0, msg);
  1525. break;
  1526. }
  1527. case GameMessage::MSG_ADD_TEAM0:
  1528. case GameMessage::MSG_ADD_TEAM1:
  1529. case GameMessage::MSG_ADD_TEAM2:
  1530. case GameMessage::MSG_ADD_TEAM3:
  1531. case GameMessage::MSG_ADD_TEAM4:
  1532. case GameMessage::MSG_ADD_TEAM5:
  1533. case GameMessage::MSG_ADD_TEAM6:
  1534. case GameMessage::MSG_ADD_TEAM7:
  1535. case GameMessage::MSG_ADD_TEAM8:
  1536. case GameMessage::MSG_ADD_TEAM9:
  1537. {
  1538. Int playerIndex = msg->getPlayerIndex();
  1539. Player *player = ThePlayerList->getNthPlayer(playerIndex);
  1540. DEBUG_ASSERTCRASH(player != NULL, ("Could not find player for add team message"));
  1541. if (player == NULL)
  1542. {
  1543. break;
  1544. }
  1545. player->processAddTeamGameMessage(msg->getType() - GameMessage::MSG_ADD_TEAM0, msg);
  1546. break;
  1547. }
  1548. //---------------------------------------------------------------------------------------------
  1549. case GameMessage::MSG_LOGIC_CRC:
  1550. {
  1551. if (TheNetwork)
  1552. {
  1553. Int slotIndex = -1;
  1554. for (Int i=0; i<MAX_SLOTS; ++i)
  1555. {
  1556. if (thisPlayer->getPlayerType() == PLAYER_HUMAN && TheNetwork->getPlayerName(i) == thisPlayer->getPlayerDisplayName())
  1557. {
  1558. slotIndex = i;
  1559. break;
  1560. }
  1561. }
  1562. if (slotIndex < 0 || !TheNetwork->isPlayerConnected(slotIndex))
  1563. break;
  1564. if (thisPlayer->isLocalPlayer())
  1565. {
  1566. #if defined(_DEBUG) || defined(_INTERNAL)
  1567. // don't even put this in release, cause someone might hack it.
  1568. if (!TheDebugIgnoreSyncErrors)
  1569. {
  1570. #endif
  1571. m_shouldValidateCRCs = TRUE;
  1572. #if defined(_DEBUG) || defined(_INTERNAL)
  1573. }
  1574. #endif
  1575. }
  1576. //UnsignedInt oldCRC = m_cachedCRCs[msg->getPlayerIndex()];
  1577. UnsignedInt newCRC = msg->getArgument(0)->integer;
  1578. //DEBUG_LOG(("Recieved CRC of %8.8X from %ls on frame %d\n", newCRC,
  1579. //thisPlayer->getPlayerDisplayName().str(), m_frame));
  1580. m_cachedCRCs[msg->getPlayerIndex()] = newCRC; // to mask problem: = (oldCRC < newCRC)?newCRC:oldCRC;
  1581. }
  1582. else if (TheRecorder && TheRecorder->getMode() == RECORDERMODETYPE_PLAYBACK)
  1583. {
  1584. UnsignedInt newCRC = msg->getArgument(0)->integer;
  1585. //DEBUG_LOG(("Saw CRC of %X from player %d. Our CRC is %X. Arg count is %d\n",
  1586. //newCRC, thisPlayer->getPlayerIndex(), getCRC(), msg->getArgumentCount()));
  1587. TheRecorder->handleCRCMessage(newCRC, thisPlayer->getPlayerIndex(), (msg->getArgument(1)->boolean));
  1588. }
  1589. break;
  1590. } // end CRC message
  1591. //---------------------------------------------------------------------------------------------
  1592. case GameMessage::MSG_PURCHASE_SCIENCE:
  1593. {
  1594. ScienceType science = (ScienceType)msg->getArgument( 0 )->integer;
  1595. // sanity
  1596. if( science == SCIENCE_INVALID || thisPlayer == NULL )
  1597. break;
  1598. thisPlayer->attemptToPurchaseScience(science);
  1599. break;
  1600. } // end pick specialized science
  1601. } // end switch
  1602. /**/ /// @todo: multiplayer semantics
  1603. if (currentlySelectedGroup && TheRecorder->getMode() == RECORDERMODETYPE_PLAYBACK && TheGlobalData->m_useCameraInReplay && TheControlBar->getObserverLookAtPlayer() == thisPlayer /*&& !TheRecorder->isMultiplayer()*/)
  1604. {
  1605. const VecObjectID& selectedObjects = currentlySelectedGroup->getAllIDs();
  1606. TheInGameUI->deselectAllDrawables();
  1607. for (VecObjectID::const_iterator it = selectedObjects.begin(); it != selectedObjects.end(); ++it)
  1608. {
  1609. const Object *obj = findObjectByID(*it);
  1610. if (obj)
  1611. {
  1612. Drawable *draw = obj->getDrawable();
  1613. if (draw)
  1614. TheInGameUI->selectDrawable(draw);
  1615. }
  1616. }
  1617. }
  1618. /**/
  1619. if( currentlySelectedGroup != NULL )
  1620. {
  1621. TheAI->destroyGroup(currentlySelectedGroup);
  1622. }
  1623. } // end logicMessageDispatches