ControlBarMultiSelect.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: ControlBarMultiSelect.cpp ////////////////////////////////////////////////////////////////
  24. // Author: Colin Day, March 2002
  25. // Desc: Context sensitive GUI for when you select mutiple objects. What we do is show
  26. // the commands that you can use between them all
  27. ///////////////////////////////////////////////////////////////////////////////////////////////////
  28. // USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
  29. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  30. #include "Common/ThingTemplate.h"
  31. #include "GameClient/ControlBar.h"
  32. #include "GameClient/Drawable.h"
  33. #include "GameClient/GameClient.h"
  34. #include "GameClient/GadgetPushButton.h"
  35. #include "GameClient/GameWindow.h"
  36. #include "GameClient/InGameUI.h"
  37. #include "GameLogic/Object.h"
  38. #ifdef _INTERNAL
  39. // for occasional debugging...
  40. //#pragma optimize("", off)
  41. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  42. #endif
  43. //-------------------------------------------------------------------------------------------------
  44. /** Reset the common command data */
  45. //-------------------------------------------------------------------------------------------------
  46. void ControlBar::resetCommonCommandData( void )
  47. {
  48. Int i;
  49. for( i = 0; i < MAX_COMMANDS_PER_SET; i++ )
  50. {
  51. m_commonCommands[ i ] = NULL;
  52. //Clear out any remnant overlays.
  53. GadgetButtonDrawOverlayImage( m_commandWindows[ i ], NULL );
  54. }
  55. } // end resetCommonCommandData
  56. //-------------------------------------------------------------------------------------------------
  57. /** add the common commands of this drawable to the common command set */
  58. //-------------------------------------------------------------------------------------------------
  59. void ControlBar::addCommonCommands( Drawable *draw, Bool firstDrawable )
  60. {
  61. Int i;
  62. const CommandButton *command;
  63. // sanity
  64. if( draw == NULL )
  65. return;
  66. Object* obj = draw->getObject();
  67. if (!obj)
  68. return;
  69. if (obj->isKindOf(KINDOF_IGNORED_IN_GUI)) // ignore these guys
  70. return;
  71. // get the command set of this drawable
  72. const CommandSet *commandSet = findCommandSet( obj->getCommandSetString() );
  73. if( commandSet == NULL )
  74. {
  75. //
  76. // if there is no command set for this drawable, none of the selected drawables
  77. // can possibly have matching commands so we'll get rid of them all
  78. //
  79. for( i = 0; i < MAX_COMMANDS_PER_SET; i++ )
  80. {
  81. m_commonCommands[ i ] = NULL;
  82. if (m_commandWindows[ i ])
  83. {
  84. m_commandWindows[ i ]->winHide( TRUE );
  85. }
  86. // After Every change to the m_commandWIndows, we need to show fill in the missing blanks with the images
  87. // removed from multiplayer branch
  88. //showCommandMarkers();
  89. } // end for i
  90. return;
  91. } // end if
  92. //
  93. // easy case, if we're adding the first drawable we simply just add any of the commands
  94. // in its set that can be multi-select commands to the common command set
  95. //
  96. if( firstDrawable == TRUE )
  97. {
  98. // just add each command that is classified as a common command
  99. for( i = 0; i < MAX_COMMANDS_PER_SET; i++ )
  100. {
  101. // our implementation doesn't necessarily make use of the max possible command buttons
  102. if (! m_commandWindows[ i ]) continue;
  103. // get command
  104. command = commandSet->getCommandButton(i);
  105. // add if present and can be used in a multi select
  106. if( command && BitTest( command->getOptions(), OK_FOR_MULTI_SELECT ) == TRUE )
  107. {
  108. // put it in the common command set
  109. m_commonCommands[ i ] = command;
  110. // show and enable this control
  111. m_commandWindows[ i ]->winHide( FALSE );
  112. m_commandWindows[ i ]->winEnable( TRUE );
  113. // set the command into the control
  114. setControlCommand( m_commandWindows[ i ], command );
  115. } // end if
  116. } // end for i
  117. } // end if
  118. else
  119. {
  120. // go through each command one by one
  121. for( i = 0; i < MAX_COMMANDS_PER_SET; i++ )
  122. {
  123. // our implementation doesn't necessarily make use of the max possible command buttons
  124. if (! m_commandWindows[ i ]) continue;
  125. // get the command
  126. command = commandSet->getCommandButton(i);
  127. Bool attackMove = (command && command->getCommandType() == GUI_COMMAND_ATTACK_MOVE) ||
  128. (m_commonCommands[ i ] && m_commonCommands[ i ]->getCommandType() == GUI_COMMAND_ATTACK_MOVE);
  129. // Kris: When any units have attack move, they all get it. This is to allow
  130. // combat units to be selected with the odd dozer or pilot and still retain that ability.
  131. if( attackMove && !m_commonCommands[ i ] )
  132. {
  133. // put it in the common command set
  134. m_commonCommands[ i ] = command;
  135. // show and enable this control
  136. m_commandWindows[ i ]->winHide( FALSE );
  137. m_commandWindows[ i ]->winEnable( TRUE );
  138. // set the command into the control
  139. setControlCommand( m_commandWindows[ i ], command );
  140. }
  141. else if( command != m_commonCommands[ i ] && !attackMove )
  142. {
  143. //
  144. // if this command does not match the command that is in the common command set then
  145. // *neither* this command OR the command in the common command set are really common
  146. // commands, so we will remove the one that has been stored in the common set
  147. //
  148. // remove the common command
  149. m_commonCommands[ i ] = NULL;
  150. //
  151. // hide the window control cause it should have been made visible from a command
  152. // that was placed in this common 'slot' earlier
  153. //
  154. m_commandWindows[ i ]->winHide( TRUE );
  155. }
  156. } // end if
  157. } // end else
  158. // After Every change to the m_commandWIndows, we need to show fill in the missing blanks with the images
  159. // removed from multiplayer branch
  160. //showCommandMarkers();
  161. } // end addCommonCommands
  162. //-------------------------------------------------------------------------------------------------
  163. /** Populate the visible command bar with commands that are common to all the objects
  164. * that are selected in the UI */
  165. //-------------------------------------------------------------------------------------------------
  166. void ControlBar::populateMultiSelect( void )
  167. {
  168. Drawable *draw;
  169. Bool firstDrawable = TRUE;
  170. Bool portraitSet = FALSE;
  171. const Image *portrait = NULL;
  172. Object *portraitObj = NULL;
  173. // first reset the common command data
  174. resetCommonCommandData();
  175. // by default, hide all the controls in the command section
  176. for( Int i = 0; i < MAX_COMMANDS_PER_SET; i++ )
  177. {
  178. if (m_commandWindows[ i ])
  179. {
  180. m_commandWindows[ i ]->winHide( TRUE );
  181. }
  182. }
  183. // sanity
  184. DEBUG_ASSERTCRASH( TheInGameUI->getSelectCount() > 1,
  185. ("populateMultiSelect: Can't populate multiselect context cause there are only '%d' things selected\n",
  186. TheInGameUI->getSelectCount()) );
  187. // get the list of drawable IDs from the in game UI
  188. const DrawableList *selectedDrawables = TheInGameUI->getAllSelectedDrawables();
  189. // sanity
  190. DEBUG_ASSERTCRASH( selectedDrawables->empty() == FALSE, ("populateMultiSelect: Drawable list is empty\n") );
  191. // loop through all the selected drawables
  192. for( DrawableListCIt it = selectedDrawables->begin();
  193. it != selectedDrawables->end(); ++it )
  194. {
  195. // get the drawable
  196. draw = *it;
  197. if (draw->getObject()->isKindOf(KINDOF_IGNORED_IN_GUI)) // ignore these guys
  198. continue;
  199. //
  200. // add command for this drawable, note that we also sanity check to make sure the
  201. // drawable has an object as all interesting drawables that we can select should
  202. // actually have an object underneath it so that we can do interesting things with
  203. // it ... otherwise we should have never selected it.
  204. // NOTE that we're not considering objects that are currently in the process of
  205. // being sold as those objects can't be issued anymore commands
  206. //
  207. if( draw && draw->getObject() &&
  208. !draw->getObject()->getStatusBits().test( OBJECT_STATUS_SOLD ) )
  209. {
  210. // add the common commands of this drawable to the common command set
  211. addCommonCommands( draw, firstDrawable );
  212. // not adding the first drawble anymore
  213. firstDrawable = FALSE;
  214. //
  215. // keep track of the portrait images, if all units selected have the same portrait
  216. // we will display it in the right HUD, otherwise we won't
  217. //
  218. if( portraitSet == FALSE )
  219. {
  220. portrait = draw->getTemplate()->getSelectedPortraitImage();
  221. portraitObj = draw->getObject();
  222. portraitSet = TRUE;
  223. } // end if
  224. else if( draw->getTemplate()->getSelectedPortraitImage() != portrait )
  225. portrait = NULL;
  226. } // end if
  227. } // end for, drawble id iterator
  228. // set the portrait image
  229. setPortraitByObject( portraitObj );
  230. } // end populateMultiSelect
  231. //-------------------------------------------------------------------------------------------------
  232. /** Update logic for the multi select context sensitive GUI */
  233. //-------------------------------------------------------------------------------------------------
  234. void ControlBar::updateContextMultiSelect( void )
  235. {
  236. Drawable *draw;
  237. Object *obj;
  238. const CommandButton *command;
  239. GameWindow *win;
  240. Int objectsThatCanDoCommand[ MAX_COMMANDS_PER_SET ];
  241. Int i;
  242. // zero the array that counts how many objects can do each command
  243. memset( objectsThatCanDoCommand, 0, sizeof( objectsThatCanDoCommand ) );
  244. // santiy
  245. DEBUG_ASSERTCRASH( TheInGameUI->getSelectCount() > 1,
  246. ("updateContextMultiSelect: TheInGameUI only has '%d' things selected\n",
  247. TheInGameUI->getSelectCount()) );
  248. // get the list of drawable IDs from the in game UI
  249. const DrawableList *selectedDrawables = TheInGameUI->getAllSelectedDrawables();
  250. // sanity
  251. DEBUG_ASSERTCRASH( selectedDrawables->empty() == FALSE, ("populateMultiSelect: Drawable list is empty\n") );
  252. // loop through all the selected drawable IDs
  253. for( DrawableListCIt it = selectedDrawables->begin();
  254. it != selectedDrawables->end(); ++it )
  255. {
  256. // get the drawable from the ID
  257. draw = *it;
  258. if (draw->getObject()->isKindOf(KINDOF_IGNORED_IN_GUI)) // ignore these guys
  259. continue;
  260. // get the object
  261. obj = draw->getObject();
  262. // sanity
  263. if( obj == NULL )
  264. continue;
  265. // for each of the visible command windows make sure the object can execute the command
  266. for( i = 0; i < MAX_COMMANDS_PER_SET; i++ )
  267. {
  268. // get the control window
  269. win = m_commandWindows[ i ];
  270. // our implementation doesn't necessarily make use of the max possible command buttons
  271. if (!win) continue;
  272. // don't consider hidden windows
  273. if( win->winIsHidden() == TRUE )
  274. continue;
  275. // get the command
  276. command = (const CommandButton *)GadgetButtonGetData(win);
  277. if( command == NULL )
  278. continue;
  279. // can we do the command
  280. CommandAvailability availability = getCommandAvailability( command, obj, win );
  281. win->winClearStatus( WIN_STATUS_NOT_READY );
  282. win->winClearStatus( WIN_STATUS_ALWAYS_COLOR );
  283. // enable/disable the window control
  284. switch( availability )
  285. {
  286. case COMMAND_HIDDEN:
  287. win->winHide( TRUE );
  288. break;
  289. case COMMAND_RESTRICTED:
  290. win->winEnable( FALSE );
  291. break;
  292. case COMMAND_NOT_READY:
  293. win->winEnable( FALSE );
  294. win->winSetStatus( WIN_STATUS_NOT_READY );
  295. break;
  296. case COMMAND_CANT_AFFORD:
  297. win->winEnable( FALSE );
  298. win->winSetStatus( WIN_STATUS_ALWAYS_COLOR );
  299. break;
  300. default:
  301. win->winEnable( TRUE );
  302. break;
  303. }
  304. //If button is a CHECK_LIKE, then update it's status now.
  305. if( BitTest( command->getOptions(), CHECK_LIKE ) )
  306. {
  307. GadgetCheckLikeButtonSetVisualCheck( win, availability == COMMAND_ACTIVE );
  308. }
  309. if( availability == COMMAND_AVAILABLE || availability == COMMAND_ACTIVE )
  310. objectsThatCanDoCommand[ i ]++;
  311. } // end for i
  312. } // end for, selected drawables
  313. //
  314. // for each command, if any objects can do the command we enable the window, otherwise
  315. // we disable it
  316. //
  317. for( i = 0; i < MAX_COMMANDS_PER_SET; i++ )
  318. {
  319. // our implementation doesn't necessarily make use of the max possible command buttons
  320. if (! m_commandWindows[ i ]) continue;
  321. // don't consider hidden commands
  322. if( m_commandWindows[ i ]->winIsHidden() == TRUE )
  323. continue;
  324. // don't consider slots that don't have commands
  325. if( m_commonCommands[ i ] == NULL )
  326. continue;
  327. // check the count of objects that can do the command and enable/disable the control,
  328. if( objectsThatCanDoCommand[ i ] > 0 )
  329. m_commandWindows[ i ]->winEnable( TRUE );
  330. else
  331. m_commandWindows[ i ]->winEnable( FALSE );
  332. } // end for i
  333. // After Every change to the m_commandWIndows, we need to show fill in the missing blanks with the images
  334. // removed from multiplayer branch
  335. //showCommandMarkers();
  336. } // end updateContextMultiSelect