ControlBarCommandProcessing.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733
  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: ControlBarCommandProcessing.cpp //////////////////////////////////////////////////////////
  24. // Author: Colin Day, March 2002
  25. // Desc: This file contain just the method responsible for processing the actual command
  26. // clicks from the window controls in the UI
  27. ///////////////////////////////////////////////////////////////////////////////////////////////////
  28. // USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
  29. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  30. #include "Common/BuildAssistant.h"
  31. #include "Common/Money.h"
  32. #include "Common/Player.h"
  33. #include "Common/PlayerList.h"
  34. #include "Common/Science.h"
  35. #include "Common/SpecialPower.h"
  36. #include "Common/ThingTemplate.h"
  37. #include "Common/Upgrade.h"
  38. #include "Common/PlayerTemplate.h"
  39. #include "GameClient/CommandXlat.h"
  40. #include "GameClient/ControlBar.h"
  41. #include "GameClient/Drawable.h"
  42. #include "GameClient/Eva.h"
  43. #include "GameClient/GameClient.h"
  44. #include "GameClient/GadgetPushButton.h"
  45. #include "GameClient/GameWindow.h"
  46. #include "GameClient/GameWindowManager.h"
  47. #include "GameClient/InGameUI.h"
  48. #include "GameClient/AnimateWindowManager.h"
  49. #include "GameLogic/GameLogic.h"
  50. #include "GameLogic/Object.h"
  51. #include "GameLogic/Module/ProductionUpdate.h"
  52. #ifdef _INTERNAL
  53. // for occasional debugging...
  54. //#pragma optimize("", off)
  55. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  56. #endif
  57. //-------------------------------------------------------------------------------------------------
  58. /** Process a button transition message from the window system that should be for one of
  59. * our GUI commands */
  60. //-------------------------------------------------------------------------------------------------
  61. CBCommandStatus ControlBar::processCommandTransitionUI( GameWindow *control, GadgetGameMessage gadgetMessage )
  62. {
  63. // sanity, we won't process messages if we have no source object
  64. if( m_currContext != CB_CONTEXT_MULTI_SELECT &&
  65. (m_currentSelectedDrawable == NULL ||
  66. m_currentSelectedDrawable->getObject() == NULL) )
  67. {
  68. if( m_currContext != CB_CONTEXT_NONE &&
  69. m_currContext != CB_CONTEXT_OBSERVER_INFO &&
  70. m_currContext != CB_CONTEXT_OBSERVER_LIST)
  71. switchToContext( CB_CONTEXT_NONE, NULL );
  72. return CBC_COMMAND_NOT_USED;
  73. } // end if
  74. return CBC_COMMAND_USED;
  75. }
  76. //-------------------------------------------------------------------------------------------------
  77. /** Process a button selected message from the window system that should be for one of
  78. * our GUI commands */
  79. //-------------------------------------------------------------------------------------------------
  80. CBCommandStatus ControlBar::processCommandUI( GameWindow *control,
  81. GadgetGameMessage gadgetMessage )
  82. {
  83. // get the command pointer from the control user data we put in the button
  84. const CommandButton *commandButton = (const CommandButton *)GadgetButtonGetData(control);
  85. // sanity, we won't process messages if we have no source object,
  86. // unless we're CB_CONTEXT_PURCHASE_SCIENCE or GUI_COMMAND_SPECIAL_POWER_FROM_COMMAND_CENTER
  87. if( m_currContext != CB_CONTEXT_MULTI_SELECT &&
  88. commandButton->getCommandType() != GUI_COMMAND_PURCHASE_SCIENCE &&
  89. commandButton->getCommandType() != GUI_COMMAND_SPECIAL_POWER_FROM_COMMAND_CENTER &&
  90. (m_currentSelectedDrawable == NULL ||
  91. m_currentSelectedDrawable->getObject() == NULL) )
  92. {
  93. if( m_currContext != CB_CONTEXT_NONE )
  94. switchToContext( CB_CONTEXT_NONE, NULL );
  95. return CBC_COMMAND_NOT_USED;
  96. } // end if
  97. // sanity
  98. if( control == NULL )
  99. return CBC_COMMAND_NOT_USED;
  100. // the context sensitive gui only is only made of buttons ... sanity
  101. if( control->winGetInputFunc() != GadgetPushButtonInput )
  102. return CBC_COMMAND_NOT_USED;
  103. if( commandButton == NULL )
  104. return CBC_COMMAND_NOT_USED;
  105. // if the button is flashing, tell it to stop flashing
  106. commandButton->setFlashCount(0);
  107. TheControlBar->setFlash( FALSE );
  108. if( commandButton->getCommandType() != GUI_COMMAND_EXIT_CONTAINER )
  109. {
  110. GadgetButtonSetEnabledImage( control, commandButton->getButtonImage() );
  111. }
  112. //
  113. // get the object that is driving the context sensitive UI if we're not in a multi
  114. // select context
  115. //
  116. Object *obj = NULL;
  117. if( m_currContext != CB_CONTEXT_MULTI_SELECT &&
  118. commandButton->getCommandType() != GUI_COMMAND_PURCHASE_SCIENCE &&
  119. commandButton->getCommandType() != GUI_COMMAND_SPECIAL_POWER_FROM_COMMAND_CENTER)
  120. obj = m_currentSelectedDrawable->getObject();
  121. //@todo Kris -- Special case code so convoy trucks can detonate nuke trucks -- if other things need this,
  122. //rethink it.
  123. if( obj && BitTest( commandButton->getOptions(), SINGLE_USE_COMMAND ) )
  124. {
  125. /** @todo Added obj check because Single Use and Multi Select crash when used together, but with this check
  126. * they just won't work. When the "rethinking" occurs, this can get fixed. Right now it is unused.
  127. * Convoy Truck needs Multi Select so Single Use is turned off, and noone else has it.
  128. */
  129. //Make sure the command button is marked as used if it's a single use command. That way
  130. //we can never press the button again. This was added specifically for nuke convoy trucks.
  131. //When you click to detonate the nuke, it takes a few seconds to detonate in order to play
  132. //a sound. But we want to disable the button after the first click.
  133. obj->markSingleUseCommandUsed(); //Yeah, an object can only use one single use command...
  134. }
  135. TheInGameUI->placeBuildAvailable( NULL, NULL );
  136. //Play any available unit specific sound for button
  137. Player *player = ThePlayerList->getLocalPlayer();
  138. if( player )
  139. {
  140. AudioEventRTS sound = *commandButton->getUnitSpecificSound();
  141. sound.setPlayerIndex( player->getPlayerIndex() );
  142. TheAudio->addAudioEvent( &sound );
  143. }
  144. if( BitTest( commandButton->getOptions(), COMMAND_OPTION_NEED_TARGET ) )
  145. {
  146. if (commandButton->getOptions() & USES_MINE_CLEARING_WEAPONSET)
  147. {
  148. TheMessageStream->appendMessage( GameMessage::MSG_SET_MINE_CLEARING_DETAIL );
  149. }
  150. //June 06, 2002 -- Major change
  151. //I've added support for specific context sensitive commands which need targets just like
  152. //other options may need. When we need a target, the user must move the cursor to a position
  153. //where he wants the GUI command to take place. Older commands such as napalm strikes or daisy
  154. //cutter drops simply needed the user to click anywhere he desired.
  155. //
  156. //Now, we have new commands that will only work when the user clicks on valid targets to interact
  157. //with. For example, the terrorist can jack a car and convert it into a carbomb, but he has to
  158. //click on a valid car. In this case the doCommandOrHint code will determine if the mode is valid
  159. //or not and the cursor modes will be set appropriately.
  160. TheInGameUI->setGUICommand( commandButton );
  161. }
  162. else switch( commandButton->getCommandType() )
  163. {
  164. //---------------------------------------------------------------------------------------------
  165. case GUI_COMMAND_DOZER_CONSTRUCT:
  166. {
  167. // sanity
  168. if( m_currentSelectedDrawable == NULL )
  169. break;
  170. //Kris: September 27, 2002
  171. //Make sure we have enough CASH to build it WHEN we click the button to build it,
  172. //before actually previewing the purchase, otherwise, cancel altogether.
  173. const ThingTemplate *whatToBuild = commandButton->getThingTemplate();
  174. CanMakeType cmt = TheBuildAssistant->canMakeUnit( obj, whatToBuild );
  175. if (cmt == CANMAKE_NO_MONEY)
  176. {
  177. TheEva->setShouldPlay(EVA_InsufficientFunds);
  178. TheInGameUI->message( "GUI:NotEnoughMoneyToBuild" );
  179. break;
  180. }
  181. else if (cmt == CANMAKE_QUEUE_FULL)
  182. {
  183. TheInGameUI->message( "GUI:ProductionQueueFull" );
  184. break;
  185. }
  186. else if (cmt == CANMAKE_PARKING_PLACES_FULL)
  187. {
  188. TheInGameUI->message( "GUI:ParkingPlacesFull" );
  189. break;
  190. }
  191. else if( cmt == CANMAKE_MAXED_OUT_FOR_PLAYER )
  192. {
  193. TheInGameUI->message( "GUI:UnitMaxedOut" );
  194. break;
  195. }
  196. // tell the UI that we want to build something so we get a building at the cursor
  197. TheInGameUI->placeBuildAvailable( commandButton->getThingTemplate(), m_currentSelectedDrawable );
  198. break;
  199. } // end dozer construct
  200. //---------------------------------------------------------------------------------------------
  201. case GUI_COMMAND_DOZER_CONSTRUCT_CANCEL:
  202. {
  203. // get the object we have selected
  204. Object *building = obj;
  205. if( building == NULL )
  206. break;
  207. // sanity check, the building must be under our control to cancel construction
  208. if( building->getControllingPlayer() != ThePlayerList->getLocalPlayer() )
  209. break;
  210. // do the message
  211. TheMessageStream->appendMessage( GameMessage::MSG_DOZER_CANCEL_CONSTRUCT );
  212. break;
  213. } // end cancel dozer construction
  214. //---------------------------------------------------------------------------------------------
  215. case GUI_COMMAND_UNIT_BUILD:
  216. {
  217. const ThingTemplate *whatToBuild = commandButton->getThingTemplate();
  218. // get the "factory" object that is going to make the thing
  219. Object *factory = obj;
  220. if( factory == NULL )
  221. break;
  222. // sanity, we must have something to build
  223. DEBUG_ASSERTCRASH( whatToBuild, ("Undefined BUILD command for object '%s'\n",
  224. commandButton->getThingTemplate()->getName().str()) );
  225. CanMakeType cmt = TheBuildAssistant->canMakeUnit(factory, whatToBuild);
  226. if (cmt == CANMAKE_NO_MONEY)
  227. {
  228. TheEva->setShouldPlay(EVA_InsufficientFunds);
  229. TheInGameUI->message( "GUI:NotEnoughMoneyToBuild" );
  230. break;
  231. }
  232. else if (cmt == CANMAKE_QUEUE_FULL)
  233. {
  234. TheInGameUI->message( "GUI:ProductionQueueFull" );
  235. break;
  236. }
  237. else if (cmt == CANMAKE_PARKING_PLACES_FULL)
  238. {
  239. TheInGameUI->message( "GUI:ParkingPlacesFull" );
  240. break;
  241. }
  242. else if( cmt == CANMAKE_MAXED_OUT_FOR_PLAYER )
  243. {
  244. TheInGameUI->message( "GUI:UnitMaxedOut" );
  245. break;
  246. }
  247. else if (cmt != CANMAKE_OK)
  248. {
  249. DEBUG_ASSERTCRASH( 0, ("Cannot create '%s' because the factory object '%s' returns false for canMakeUnit\n",
  250. whatToBuild->getName().str(),
  251. factory->getTemplate()->getName().str()) );
  252. break;
  253. }
  254. // get the production interface from the factory object
  255. ProductionUpdateInterface *pu = factory->getProductionUpdateInterface();
  256. // sanity, we can't build things if we can't produce units
  257. if( pu == NULL )
  258. {
  259. DEBUG_ASSERTCRASH( 0, ("Cannot create '%s' because the factory object '%s' is not capable of producting units\n",
  260. whatToBuild->getName().str(),
  261. factory->getTemplate()->getName().str()) );
  262. break;
  263. } // end if
  264. // get a new production id to assign to this
  265. ProductionID productionID = pu->requestUniqueUnitID();
  266. // create a message to build this thing
  267. GameMessage *msg = TheMessageStream->appendMessage( GameMessage::MSG_QUEUE_UNIT_CREATE );
  268. msg->appendIntegerArgument( whatToBuild->getTemplateID() );
  269. msg->appendIntegerArgument( productionID );
  270. break;
  271. } // end build unit
  272. //---------------------------------------------------------------------------------------------
  273. case GUI_COMMAND_CANCEL_UNIT_BUILD:
  274. {
  275. Int i;
  276. // find out which index (i) in the queue represents the button clicked
  277. for( i = 0; i < MAX_BUILD_QUEUE_BUTTONS; i++ )
  278. if( m_queueData[ i ].control == control )
  279. break;
  280. // sanity, control not found
  281. if( i == MAX_BUILD_QUEUE_BUTTONS )
  282. {
  283. DEBUG_ASSERTCRASH( 0, ("Control not found in build queue data\n") );
  284. break;
  285. } // end if
  286. // sanity
  287. if( m_queueData[ i ].type != PRODUCTION_UNIT )
  288. break;
  289. // the the production ID to cancel
  290. ProductionID productionIDToCancel = m_queueData[ i ].productionID;
  291. // get the object that is the producer
  292. Object *producer = obj;
  293. if( producer == NULL )
  294. break;
  295. // sanity, we must control the producer ... if this isn't true they might be hacking the game
  296. if( producer->getControllingPlayer() != ThePlayerList->getLocalPlayer() )
  297. break;
  298. // send a message to cancel that particular production entry
  299. GameMessage *msg = TheMessageStream->appendMessage( GameMessage::MSG_CANCEL_UNIT_CREATE );
  300. msg->appendIntegerArgument( productionIDToCancel );
  301. break;
  302. } // end cancel unit build
  303. //---------------------------------------------------------------------------------------------
  304. case GUI_COMMAND_PLAYER_UPGRADE:
  305. {
  306. const UpgradeTemplate *upgradeT = commandButton->getUpgradeTemplate();
  307. DEBUG_ASSERTCRASH( upgradeT, ("Undefined upgrade '%s' in player upgrade command\n", "UNKNOWN") );
  308. // sanity
  309. if( obj == NULL || upgradeT == NULL )
  310. break;
  311. // make sure the player can really make this
  312. if( TheUpgradeCenter->canAffordUpgrade( ThePlayerList->getLocalPlayer(), upgradeT, TRUE ) == FALSE )
  313. {
  314. break;
  315. }
  316. ProductionUpdateInterface* pu = obj ? obj->getProductionUpdateInterface() : NULL;
  317. if (pu != NULL)
  318. {
  319. CanMakeType cmt = pu->canQueueUpgrade(upgradeT);
  320. if (cmt == CANMAKE_QUEUE_FULL)
  321. {
  322. TheInGameUI->message( "GUI:ProductionQueueFull" );
  323. break;
  324. }
  325. }
  326. // send the message
  327. GameMessage *msg = TheMessageStream->appendMessage( GameMessage::MSG_QUEUE_UPGRADE );
  328. msg->appendObjectIDArgument( obj->getID() );
  329. msg->appendIntegerArgument( upgradeT->getUpgradeNameKey() );
  330. break;
  331. } // command player upgrade
  332. //---------------------------------------------------------------------------------------------
  333. case GUI_COMMAND_OBJECT_UPGRADE:
  334. {
  335. const UpgradeTemplate *upgradeT = commandButton->getUpgradeTemplate();
  336. DEBUG_ASSERTCRASH( upgradeT, ("Undefined upgrade '%s' in object upgrade command\n", "UNKNOWN") );
  337. // sanity
  338. if( upgradeT == NULL )
  339. break;
  340. //Make sure the player can really make this
  341. if( TheUpgradeCenter->canAffordUpgrade( ThePlayerList->getLocalPlayer(), upgradeT, TRUE ) == FALSE )
  342. {
  343. //Kris: Disabled because we can get a valid reason for not being able to afford the upgrade!
  344. //TheInGameUI->message( "upgrade unsupported in commandprocessing." );
  345. break;
  346. }
  347. ProductionUpdateInterface* pu = obj ? obj->getProductionUpdateInterface() : NULL;
  348. if (pu != NULL)
  349. {
  350. CanMakeType cmt = pu->canQueueUpgrade(upgradeT);
  351. if (cmt == CANMAKE_QUEUE_FULL)
  352. {
  353. TheInGameUI->message( "GUI:ProductionQueueFull" );
  354. break;
  355. }
  356. }
  357. ObjectID objID = INVALID_ID;
  358. if (obj)
  359. objID = obj->getID();
  360. // make sure that the this object can actually build the upgrade
  361. if( obj && (obj->hasUpgrade( upgradeT ) == TRUE || obj->affectedByUpgrade( upgradeT ) == FALSE) )
  362. break;
  363. // send the message
  364. GameMessage *msg = TheMessageStream->appendMessage( GameMessage::MSG_QUEUE_UPGRADE );
  365. msg->appendObjectIDArgument( objID );
  366. msg->appendIntegerArgument( upgradeT->getUpgradeNameKey() );
  367. break;
  368. } // end object upgrade
  369. //---------------------------------------------------------------------------------------------
  370. case GUI_COMMAND_CANCEL_UPGRADE:
  371. {
  372. Int i;
  373. // find out which index (i) in the queue represents the button clicked
  374. for( i = 0; i < MAX_BUILD_QUEUE_BUTTONS; i++ )
  375. if( m_queueData[ i ].control == control )
  376. break;
  377. // sanity, control not found
  378. if( i == MAX_BUILD_QUEUE_BUTTONS )
  379. {
  380. DEBUG_ASSERTCRASH( 0, ("Control not found in build queue data\n") );
  381. break;
  382. } // end if
  383. // sanity
  384. if( m_queueData[ i ].type != PRODUCTION_UPGRADE )
  385. break;
  386. // get the upgrade to cancel
  387. const UpgradeTemplate *upgradeT = m_queueData[ i ].upgradeToResearch;
  388. // get producer object (the thing driving our UI)
  389. Object *producer = obj;
  390. // sanity
  391. if( upgradeT == NULL || producer == NULL )
  392. break;
  393. // send the message
  394. GameMessage *msg = TheMessageStream->appendMessage( GameMessage::MSG_CANCEL_UPGRADE );
  395. msg->appendIntegerArgument( upgradeT->getUpgradeNameKey() );
  396. break;
  397. } // end cancel upgrade
  398. //---------------------------------------------------------------------------------------------
  399. case GUI_COMMAND_ATTACK_MOVE:
  400. TheMessageStream->appendMessage(GameMessage::MSG_META_TOGGLE_ATTACKMOVE);
  401. break;
  402. //---------------------------------------------------------------------------------------------
  403. case GUI_COMMAND_STOP:
  404. {
  405. // This message always works on the currently selected team
  406. TheMessageStream->appendMessage(GameMessage::MSG_DO_STOP);
  407. break;
  408. }
  409. //---------------------------------------------------------------------------------------------
  410. case GUI_COMMAND_WAYPOINTS:
  411. break;
  412. //-------------------------------------------------------------------------------------------------
  413. case GUI_COMMAND_EXIT_CONTAINER:
  414. {
  415. Int i;
  416. ObjectID objID;
  417. //
  418. // find the object ID that wants to exit by scanning through the transport data and looking
  419. // for the matching control button
  420. //
  421. for( i = 0; i < MAX_COMMANDS_PER_SET; i++ )
  422. if( m_containData[ i ].control == control )
  423. objID = m_containData[ i ].objectID;
  424. // get the actual object
  425. Object *objWantingExit = TheGameLogic->findObjectByID( objID );
  426. // if object is not found remove inventory entry and exit
  427. if( objWantingExit == NULL )
  428. {
  429. //
  430. // remove from inventory data to avoid future matches ... the inventory update
  431. // cycle of the UI will repopulate any buttons as the contents of objects
  432. // change so this is only an edge case that will be visually corrected next frame
  433. //
  434. m_containData[ i ].control = NULL;
  435. m_containData[ i ].objectID = INVALID_ID;
  436. break; // exit case
  437. } // end if
  438. // send message to exit
  439. GameMessage *exitMsg = TheMessageStream->appendMessage( GameMessage::MSG_EXIT );
  440. exitMsg->appendObjectIDArgument( objWantingExit->getID() ); // 0 is the thing inside coming out
  441. break;
  442. } // end transport exit
  443. //---------------------------------------------------------------------------------------------
  444. case GUI_COMMAND_EVACUATE:
  445. {
  446. // Cancel GUI command mode.
  447. TheInGameUI->setGUICommand( NULL );
  448. if (BitTest(commandButton->getOptions(), NEED_TARGET_POS) == FALSE) {
  449. pickAndPlayUnitVoiceResponse( TheInGameUI->getAllSelectedDrawables(), GameMessage::MSG_EVACUATE );
  450. TheMessageStream->appendMessage( GameMessage::MSG_EVACUATE );
  451. }
  452. break;
  453. } // end evacuate
  454. // --------------------------------------------------------------------------------------------
  455. case GUI_COMMAND_EXECUTE_RAILED_TRANSPORT:
  456. {
  457. TheMessageStream->appendMessage( GameMessage::MSG_EXECUTE_RAILED_TRANSPORT );
  458. break;
  459. } // end execute railed transport
  460. // --------------------------------------------------------------------------------------------
  461. case GUI_COMMAND_HACK_INTERNET:
  462. {
  463. pickAndPlayUnitVoiceResponse( TheInGameUI->getAllSelectedDrawables(), GameMessage::MSG_INTERNET_HACK );
  464. TheMessageStream->appendMessage( GameMessage::MSG_INTERNET_HACK );
  465. break;
  466. }
  467. //---------------------------------------------------------------------------------------------
  468. case GUI_COMMAND_SET_RALLY_POINT:
  469. {
  470. break;
  471. } // end set rally point
  472. //---------------------------------------------------------------------------------------------
  473. case GUI_COMMAND_SELL:
  474. {
  475. // command needs no additional data, send the message
  476. TheMessageStream->appendMessage( GameMessage::MSG_SELL );
  477. break;
  478. } // end sell
  479. // --------------------------------------------------------------------------------------------
  480. case GUI_COMMAND_TOGGLE_OVERCHARGE:
  481. {
  482. TheMessageStream->appendMessage( GameMessage::MSG_TOGGLE_OVERCHARGE );
  483. break;
  484. } // end overcharge
  485. #ifdef ALLOW_SURRENDER
  486. // ------------------------------------------------------------------------------------------------
  487. case GUI_COMMAND_POW_RETURN_TO_PRISON:
  488. {
  489. TheMessageStream->appendMessage( GameMessage::MSG_RETURN_TO_PRISON );
  490. break;
  491. } // end return to prison
  492. #endif
  493. //---------------------------------------------------------------------------------------------
  494. case GUI_COMMAND_BEACON_DELETE:
  495. {
  496. break;
  497. } // end delete beacon
  498. //---------------------------------------------------------------------------------------------
  499. case GUI_COMMAND_GUARD:
  500. case GUI_COMMAND_GUARD_WITHOUT_PURSUIT:
  501. case GUI_COMMAND_GUARD_FLYING_UNITS_ONLY:
  502. case GUI_COMMAND_COMBATDROP:
  503. {
  504. DEBUG_CRASH(("hmm, should never occur"));
  505. }
  506. break;
  507. //---------------------------------------------------------------------------------------------
  508. case GUI_COMMAND_SWITCH_WEAPON:
  509. {
  510. // command needs no additional data, send the message
  511. GameMessage *msg = TheMessageStream->appendMessage( GameMessage::MSG_SWITCH_WEAPONS );
  512. //Play mode change acknowledgement
  513. PickAndPlayInfo info;
  514. WeaponSlotType slot = commandButton->getWeaponSlot();
  515. info.m_weaponSlot = &slot;
  516. pickAndPlayUnitVoiceResponse( TheInGameUI->getAllSelectedDrawables(), GameMessage::MSG_SWITCH_WEAPONS, &info );
  517. msg->appendIntegerArgument( commandButton->getWeaponSlot() );
  518. break;
  519. }
  520. //---------------------------------------------------------------------------------------------
  521. case GUI_COMMAND_FIRE_WEAPON:
  522. {
  523. // command needs no additional data, send the message
  524. GameMessage *msg = TheMessageStream->appendMessage( GameMessage::MSG_DO_WEAPON );
  525. msg->appendIntegerArgument( commandButton->getWeaponSlot() );
  526. msg->appendIntegerArgument( commandButton->getMaxShotsToFire() );
  527. break;
  528. } // end fire weapon
  529. //---------------------------------------------------------------------------------------------
  530. case GUI_COMMAND_SPECIAL_POWER_FROM_COMMAND_CENTER:
  531. {
  532. Object* cmdCenter = ThePlayerList->getLocalPlayer()->findNaturalCommandCenter();
  533. if (cmdCenter == NULL)
  534. break;
  535. // command needs no additional data, send the message
  536. GameMessage *msg = TheMessageStream->appendMessage( GameMessage::MSG_DO_SPECIAL_POWER );
  537. msg->appendIntegerArgument( commandButton->getSpecialPowerTemplate()->getID() );
  538. msg->appendIntegerArgument( commandButton->getOptions() );
  539. msg->appendObjectIDArgument( cmdCenter->getID() );
  540. break;
  541. } // end special weapon
  542. case GUI_COMMAND_SPECIAL_POWER:
  543. {
  544. // command needs no additional data, send the message
  545. GameMessage *msg = TheMessageStream->appendMessage( GameMessage::MSG_DO_SPECIAL_POWER );
  546. msg->appendIntegerArgument( commandButton->getSpecialPowerTemplate()->getID() );
  547. msg->appendIntegerArgument( commandButton->getOptions() );
  548. msg->appendObjectIDArgument( INVALID_ID ); // no specific source
  549. break;
  550. } // end special weapon
  551. //---------------------------------------------------------------------------------------------
  552. case GUI_COMMAND_PURCHASE_SCIENCE:
  553. {
  554. // loop through all the sciences on the button and select the one we don't have
  555. ScienceType st = SCIENCE_INVALID;
  556. Player *player = ThePlayerList->getLocalPlayer();
  557. for(Int i = 0; i < commandButton->getScienceVec().size(); ++i)
  558. {
  559. st = commandButton->getScienceVec()[ i ];
  560. if(!player->hasScience(st) && TheScienceStore->playerHasPrereqsForScience(player, st) && TheScienceStore->getSciencePurchaseCost(st) <= player->getSciencePurchasePoints())
  561. {
  562. break;
  563. }
  564. }
  565. if( st == SCIENCE_INVALID)
  566. {
  567. switchToContext( CB_CONTEXT_NONE, NULL );
  568. break;
  569. }
  570. GameMessage *msg = TheMessageStream->appendMessage( GameMessage::MSG_PURCHASE_SCIENCE );
  571. msg->appendIntegerArgument( st );
  572. markUIDirty();
  573. break;
  574. } // end pick specialized science
  575. //---------------------------------------------------------------------------------------------
  576. default:
  577. DEBUG_ASSERTCRASH( 0, ("Unknown command '%d'\n", commandButton->getCommandType()) );
  578. return CBC_COMMAND_NOT_USED;
  579. } // end switch
  580. return CBC_COMMAND_USED;
  581. } // end processCommandUI