ControlBarMultiSelect.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  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: 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. m_commandWindows[ i ]->winHide( TRUE );
  83. // After Every change to the m_commandWIndows, we need to show fill in the missing blanks with the images
  84. // removed from multiplayer branch
  85. //showCommandMarkers();
  86. } // end for i
  87. return;
  88. } // end if
  89. //
  90. // easy case, if we're adding the first drawable we simply just add any of the commands
  91. // in its set that can be multi-select commands to the common command set
  92. //
  93. if( firstDrawable == TRUE )
  94. {
  95. // just add each command that is classified as a common command
  96. for( i = 0; i < MAX_COMMANDS_PER_SET; i++ )
  97. {
  98. // get command
  99. command = commandSet->getCommandButton(i);
  100. // add if present and can be used in a multi select
  101. if( command && BitTest( command->getOptions(), OK_FOR_MULTI_SELECT ) == TRUE )
  102. {
  103. // put it in the common command set
  104. m_commonCommands[ i ] = command;
  105. // show and enable this control
  106. m_commandWindows[ i ]->winHide( FALSE );
  107. m_commandWindows[ i ]->winEnable( TRUE );
  108. // set the command into the control
  109. setControlCommand( m_commandWindows[ i ], command );
  110. } // end if
  111. } // end for i
  112. } // end if
  113. else
  114. {
  115. // go through each command one by one
  116. for( i = 0; i < MAX_COMMANDS_PER_SET; i++ )
  117. {
  118. // get the command
  119. command = commandSet->getCommandButton(i);
  120. Bool attackMove = (command && command->getCommandType() == GUI_COMMAND_ATTACK_MOVE) ||
  121. (m_commonCommands[ i ] && m_commonCommands[ i ]->getCommandType() == GUI_COMMAND_ATTACK_MOVE);
  122. // Kris: When any units have attack move, they all get it. This is to allow
  123. // combat units to be selected with the odd dozer or pilot and still retain that ability.
  124. if( attackMove && !m_commonCommands[ i ] )
  125. {
  126. // put it in the common command set
  127. m_commonCommands[ i ] = command;
  128. // show and enable this control
  129. m_commandWindows[ i ]->winHide( FALSE );
  130. m_commandWindows[ i ]->winEnable( TRUE );
  131. // set the command into the control
  132. setControlCommand( m_commandWindows[ i ], command );
  133. }
  134. else if( command != m_commonCommands[ i ] && !attackMove )
  135. {
  136. //
  137. // if this command does not match the command that is in the common command set then
  138. // *neither* this command OR the command in the common command set are really common
  139. // commands, so we will remove the one that has been stored in the common set
  140. //
  141. // remove the common command
  142. m_commonCommands[ i ] = NULL;
  143. //
  144. // hide the window control cause it should have been made visible from a command
  145. // that was placed in this common 'slot' earlier
  146. //
  147. m_commandWindows[ i ]->winHide( TRUE );
  148. }
  149. } // end if
  150. } // end else
  151. // After Every change to the m_commandWIndows, we need to show fill in the missing blanks with the images
  152. // removed from multiplayer branch
  153. //showCommandMarkers();
  154. } // end addCommonCommands
  155. //-------------------------------------------------------------------------------------------------
  156. /** Populate the visible command bar with commands that are common to all the objects
  157. * that are selected in the UI */
  158. //-------------------------------------------------------------------------------------------------
  159. void ControlBar::populateMultiSelect( void )
  160. {
  161. Drawable *draw;
  162. Bool firstDrawable = TRUE;
  163. Bool portraitSet = FALSE;
  164. const Image *portrait = NULL;
  165. Object *portraitObj = NULL;
  166. // first reset the common command data
  167. resetCommonCommandData();
  168. // by default, hide all the controls in the command section
  169. for( Int i = 0; i < MAX_COMMANDS_PER_SET; i++ )
  170. m_commandWindows[ i ]->winHide( TRUE );
  171. // sanity
  172. DEBUG_ASSERTCRASH( TheInGameUI->getSelectCount() > 1,
  173. ("populateMultiSelect: Can't populate multiselect context cause there are only '%d' things selected\n",
  174. TheInGameUI->getSelectCount()) );
  175. // get the list of drawable IDs from the in game UI
  176. const DrawableList *selectedDrawables = TheInGameUI->getAllSelectedDrawables();
  177. // sanity
  178. DEBUG_ASSERTCRASH( selectedDrawables->empty() == FALSE, ("populateMultiSelect: Drawable list is empty\n") );
  179. // loop through all the selected drawables
  180. for( DrawableListCIt it = selectedDrawables->begin();
  181. it != selectedDrawables->end(); ++it )
  182. {
  183. // get the drawable
  184. draw = *it;
  185. if (draw->getObject()->isKindOf(KINDOF_IGNORED_IN_GUI)) // ignore these guys
  186. continue;
  187. //
  188. // add command for this drawable, note that we also sanity check to make sure the
  189. // drawable has an object as all interesting drawables that we can select should
  190. // actually have an object underneath it so that we can do interesting things with
  191. // it ... otherwise we should have never selected it.
  192. // NOTE that we're not considering objects that are currently in the process of
  193. // being sold as those objects can't be issued anymore commands
  194. //
  195. if( draw && draw->getObject() &&
  196. BitTest( draw->getObject()->getStatusBits(), OBJECT_STATUS_SOLD ) == FALSE )
  197. {
  198. // add the common commands of this drawable to the common command set
  199. addCommonCommands( draw, firstDrawable );
  200. // not adding the first drawble anymore
  201. firstDrawable = FALSE;
  202. //
  203. // keep track of the portrait images, if all units selected have the same portrait
  204. // we will display it in the right HUD, otherwise we won't
  205. //
  206. if( portraitSet == FALSE )
  207. {
  208. portrait = draw->getTemplate()->getSelectedPortraitImage();
  209. portraitObj = draw->getObject();
  210. portraitSet = TRUE;
  211. } // end if
  212. else if( draw->getTemplate()->getSelectedPortraitImage() != portrait )
  213. portrait = NULL;
  214. } // end if
  215. } // end for, drawble id iterator
  216. // set the portrait image
  217. setPortraitByObject( portraitObj );
  218. } // end populateMultiSelect
  219. //-------------------------------------------------------------------------------------------------
  220. /** Update logic for the multi select context sensitive GUI */
  221. //-------------------------------------------------------------------------------------------------
  222. void ControlBar::updateContextMultiSelect( void )
  223. {
  224. Drawable *draw;
  225. Object *obj;
  226. const CommandButton *command;
  227. GameWindow *win;
  228. Int objectsThatCanDoCommand[ MAX_COMMANDS_PER_SET ];
  229. Int i;
  230. // zero the array that counts how many objects can do each command
  231. memset( objectsThatCanDoCommand, 0, sizeof( objectsThatCanDoCommand ) );
  232. // santiy
  233. DEBUG_ASSERTCRASH( TheInGameUI->getSelectCount() > 1,
  234. ("updateContextMultiSelect: TheInGameUI only has '%d' things selected\n",
  235. TheInGameUI->getSelectCount()) );
  236. // get the list of drawable IDs from the in game UI
  237. const DrawableList *selectedDrawables = TheInGameUI->getAllSelectedDrawables();
  238. // sanity
  239. DEBUG_ASSERTCRASH( selectedDrawables->empty() == FALSE, ("populateMultiSelect: Drawable list is empty\n") );
  240. // loop through all the selected drawable IDs
  241. for( DrawableListCIt it = selectedDrawables->begin();
  242. it != selectedDrawables->end(); ++it )
  243. {
  244. // get the drawable from the ID
  245. draw = *it;
  246. if (draw->getObject()->isKindOf(KINDOF_IGNORED_IN_GUI)) // ignore these guys
  247. continue;
  248. // get the object
  249. obj = draw->getObject();
  250. // sanity
  251. if( obj == NULL )
  252. continue;
  253. // for each of the visible command windows make sure the object can execute the command
  254. for( i = 0; i < MAX_COMMANDS_PER_SET; i++ )
  255. {
  256. // get the control window
  257. win = m_commandWindows[ i ];
  258. // don't consider hidden windows
  259. if( win->winIsHidden() == TRUE )
  260. continue;
  261. // get the command
  262. command = (const CommandButton *)GadgetButtonGetData(win);
  263. if( command == NULL )
  264. continue;
  265. // can we do the command
  266. CommandAvailability availability = getCommandAvailability( command, obj, win );
  267. win->winClearStatus( WIN_STATUS_NOT_READY );
  268. win->winClearStatus( WIN_STATUS_ALWAYS_COLOR );
  269. // enable/disable the window control
  270. switch( availability )
  271. {
  272. case COMMAND_HIDDEN:
  273. win->winHide( TRUE );
  274. break;
  275. case COMMAND_RESTRICTED:
  276. win->winEnable( FALSE );
  277. break;
  278. case COMMAND_NOT_READY:
  279. win->winEnable( FALSE );
  280. win->winSetStatus( WIN_STATUS_NOT_READY );
  281. break;
  282. case COMMAND_CANT_AFFORD:
  283. win->winEnable( FALSE );
  284. win->winSetStatus( WIN_STATUS_ALWAYS_COLOR );
  285. break;
  286. default:
  287. win->winEnable( TRUE );
  288. break;
  289. }
  290. //If button is a CHECK_LIKE, then update it's status now.
  291. if( BitTest( command->getOptions(), CHECK_LIKE ) )
  292. {
  293. GadgetCheckLikeButtonSetVisualCheck( win, availability == COMMAND_ACTIVE );
  294. }
  295. if( availability == COMMAND_AVAILABLE || availability == COMMAND_ACTIVE )
  296. objectsThatCanDoCommand[ i ]++;
  297. } // end for i
  298. } // end for, selected drawables
  299. //
  300. // for each command, if any objects can do the command we enable the window, otherwise
  301. // we disable it
  302. //
  303. for( i = 0; i < MAX_COMMANDS_PER_SET; i++ )
  304. {
  305. // don't consider hidden commands
  306. if( m_commandWindows[ i ]->winIsHidden() == TRUE )
  307. continue;
  308. // don't consider slots that don't have commands
  309. if( m_commonCommands[ i ] == NULL )
  310. continue;
  311. // check the count of objects that can do the command and enable/disable the control,
  312. if( objectsThatCanDoCommand[ i ] > 0 )
  313. m_commandWindows[ i ]->winEnable( TRUE );
  314. else
  315. m_commandWindows[ i ]->winEnable( FALSE );
  316. } // end for i
  317. // After Every change to the m_commandWIndows, we need to show fill in the missing blanks with the images
  318. // removed from multiplayer branch
  319. //showCommandMarkers();
  320. } // end updateContextMultiSelect