SelectionInfo.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  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. #include "PreRTS.h"
  24. #include "GameClient/SelectionInfo.h"
  25. #include "Common/ActionManager.h"
  26. #include "GameLogic/Damage.h"
  27. #include "Common/Player.h"
  28. #include "Common/PlayerList.h"
  29. #include "Common/ThingTemplate.h"
  30. #include "GameClient/CommandXlat.h"
  31. #include "GameClient/ControlBar.h"
  32. #include "GameClient/Drawable.h"
  33. #include "GameClient/GameClient.h"
  34. #include "GameClient/KeyDefs.h"
  35. #ifdef _INTERNAL
  36. // for occasional debugging...
  37. //#pragma optimize("", off)
  38. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  39. #endif
  40. //-------------------------------------------------------------------------------------------------
  41. SelectionInfo::SelectionInfo() :
  42. currentCountEnemies(0),
  43. currentCountCivilians(0),
  44. currentCountMine(0),
  45. currentCountMineInfantry(0),
  46. currentCountMineBuildings(0),
  47. currentCountFriends(0),
  48. newCountEnemies(0),
  49. newCountCivilians(0),
  50. newCountCrates(0),
  51. newCountMine(0),
  52. newCountMineBuildings(0),
  53. newCountFriends(0),
  54. newCountGarrisonableBuildings(0),
  55. selectEnemies(FALSE),
  56. selectCivilians(FALSE),
  57. selectMine(FALSE),
  58. selectMineBuildings(FALSE),
  59. selectFriends(FALSE)
  60. { }
  61. //-------------------------------------------------------------------------------------------------
  62. PickDrawableStruct::PickDrawableStruct() : drawableListToFill(NULL)
  63. {
  64. //Added By Sadullah Nader
  65. //Initializations inserted
  66. drawableListToFill = FALSE;
  67. //
  68. forceAttackMode = TheInGameUI->isInForceAttackMode();
  69. UnsignedInt pickType = getPickTypesForContext(forceAttackMode);
  70. translatePickTypesToKindof(pickType, kindofsToMatch);
  71. if (!forceAttackMode)
  72. {
  73. kindofsToMatch.set(KINDOF_ALWAYS_SELECTABLE);
  74. }
  75. }
  76. //-------------------------------------------------------------------------------------------------
  77. /**
  78. * Given a list of currently selected things and a list of things that are currently under
  79. * the selection (pointer or drag), generate some useful information about each.
  80. */
  81. extern Bool contextCommandForNewSelection(const DrawableList *currentlySelectedDrawables,
  82. const DrawableList *newlySelectedDrawables,
  83. SelectionInfo *outSelectionInfo,
  84. Bool selectionIsPoint)
  85. {
  86. if (!(currentlySelectedDrawables && newlySelectedDrawables && outSelectionInfo))
  87. return FALSE;
  88. Bool forceFire = TheInGameUI->isInForceAttackMode();
  89. Bool forceMove = TheInGameUI->isInForceMoveToMode();
  90. if (forceFire || forceMove) {
  91. return FALSE;
  92. }
  93. Player *localPlayer = ThePlayerList->getLocalPlayer();
  94. DrawableListCIt it;
  95. for (it = currentlySelectedDrawables->begin(); it != currentlySelectedDrawables->end(); ++it) {
  96. if (!(*it)) {
  97. continue;
  98. }
  99. Object *obj = (*it)->getObject();
  100. if (!obj) {
  101. continue;
  102. }
  103. if (obj->isLocallyControlled()) {
  104. ++outSelectionInfo->currentCountMine;
  105. if (obj->isKindOf(KINDOF_INFANTRY)) {
  106. ++outSelectionInfo->currentCountMineInfantry;
  107. } else if (obj->isKindOf(KINDOF_STRUCTURE)) {
  108. ++outSelectionInfo->currentCountMineBuildings;
  109. }
  110. } else {
  111. Relationship rel = localPlayer->getRelationship(obj->getTeam());
  112. if (rel == ALLIES) {
  113. ++outSelectionInfo->currentCountFriends;
  114. } else if (rel == ENEMIES) {
  115. ++outSelectionInfo->currentCountEnemies;
  116. } else if (rel == NEUTRAL) {
  117. ++outSelectionInfo->currentCountCivilians;
  118. }
  119. }
  120. }
  121. Drawable *newMine = NULL;
  122. Drawable *newFriendly = NULL;
  123. Drawable *newEnemy = NULL;
  124. Drawable *newCivilian = NULL;
  125. for (it = newlySelectedDrawables->begin(); it != newlySelectedDrawables->end(); ++it) {
  126. if (!(*it)) {
  127. continue;
  128. }
  129. Object *obj = (*it)->getObject();
  130. if (!obj) {
  131. continue;
  132. }
  133. if (TheActionManager->canPlayerGarrison(localPlayer, obj, CMD_FROM_PLAYER)) {
  134. ++outSelectionInfo->newCountGarrisonableBuildings;
  135. }
  136. if (obj->isKindOf(KINDOF_CRATE)) {
  137. ++outSelectionInfo->newCountCrates;
  138. }
  139. if (obj->isLocallyControlled()) {
  140. ++outSelectionInfo->newCountMine;
  141. newMine = *it;
  142. if (obj->isKindOf(KINDOF_STRUCTURE)) {
  143. ++outSelectionInfo->newCountMineBuildings;
  144. }
  145. } else {
  146. Relationship rel = localPlayer->getRelationship(obj->getTeam());
  147. if (rel == ALLIES) {
  148. newFriendly = *it;
  149. ++outSelectionInfo->newCountFriends;
  150. } else if (rel == ENEMIES) {
  151. newEnemy = *it;
  152. ++outSelectionInfo->newCountEnemies;
  153. } else if (rel == NEUTRAL) {
  154. newCivilian = *it;
  155. ++outSelectionInfo->newCountCivilians;
  156. }
  157. }
  158. }
  159. DEBUG_ASSERTCRASH(outSelectionInfo->currentCountEnemies <= 1, ("Selection bug. jkmcd"));
  160. DEBUG_ASSERTCRASH(outSelectionInfo->currentCountFriends <= 1, ("Selection bug. jkmcd"));
  161. DEBUG_ASSERTCRASH(outSelectionInfo->currentCountCivilians <= 1, ("Selection bug. jkmcd"));
  162. if (outSelectionInfo->currentCountEnemies > 0) {
  163. // If we have an enemy selected, there are no context sensitive commands
  164. return FALSE;
  165. }
  166. if (outSelectionInfo->currentCountFriends > 0) {
  167. return FALSE;
  168. }
  169. if (outSelectionInfo->currentCountCivilians > 0) {
  170. return FALSE;
  171. }
  172. if (TheGlobalData->m_useAlternateMouse) {
  173. // context sensitive commands never apply when selecting in alternate mouse mode
  174. return FALSE;
  175. }
  176. if (outSelectionInfo->currentCountMine > 0) {
  177. if (outSelectionInfo->newCountEnemies > 0) {
  178. if (outSelectionInfo->newCountEnemies == 1 && selectionIsPoint) {
  179. return TheGameClient->evaluateContextCommand(newEnemy, newEnemy->getPosition(), CommandTranslator::EVALUATE_ONLY) != GameMessage::MSG_INVALID;
  180. }
  181. return selectionIsPoint;
  182. }
  183. if (outSelectionInfo->newCountMine > 0) {
  184. if (outSelectionInfo->newCountMine == 1 && selectionIsPoint && !TheInGameUI->isInPreferSelectionMode()) {
  185. return TheGameClient->evaluateContextCommand(newMine, newMine->getPosition(), CommandTranslator::EVALUATE_ONLY) != GameMessage::MSG_INVALID;
  186. }
  187. return FALSE;
  188. }
  189. if (outSelectionInfo->newCountFriends > 0) {
  190. if (outSelectionInfo->newCountFriends == 1 && selectionIsPoint) {
  191. return TheGameClient->evaluateContextCommand(newFriendly, newFriendly->getPosition(), CommandTranslator::EVALUATE_ONLY) != GameMessage::MSG_INVALID;
  192. }
  193. return FALSE;
  194. }
  195. if (outSelectionInfo->currentCountMineInfantry > 0 && outSelectionInfo->newCountGarrisonableBuildings == 1) {
  196. return TRUE;
  197. }
  198. if (outSelectionInfo->newCountCivilians > 0) {
  199. if (outSelectionInfo->newCountCivilians == 1 && selectionIsPoint) {
  200. return TheGameClient->evaluateContextCommand(newCivilian, newCivilian->getPosition(), CommandTranslator::EVALUATE_ONLY) != GameMessage::MSG_INVALID;
  201. }
  202. return FALSE;
  203. }
  204. if (outSelectionInfo->newCountCrates > 0) {
  205. return (outSelectionInfo->newCountCrates == 1 && selectionIsPoint);
  206. }
  207. }
  208. if (outSelectionInfo->currentCountMine == 0) {
  209. return FALSE;
  210. }
  211. return selectionIsPoint;
  212. }
  213. //-------------------------------------------------------------------------------------------------
  214. UnsignedInt getPickTypesForContext( Bool forceAttackMode )
  215. {
  216. UnsignedInt types = PICK_TYPE_SELECTABLE;
  217. if (forceAttackMode)
  218. types |= PICK_TYPE_FORCEATTACKABLE;
  219. //
  220. // if we have a gui context command that allows for a shrubbery target then we want to
  221. // pick that type too (generally shrubbery aren't pickable cause it would get in
  222. // the way with movement and general selection)
  223. //
  224. const CommandButton *command = TheInGameUI->getGUICommand();
  225. if (command != NULL) {
  226. if (BitTest( command->getOptions(), ALLOW_MINE_TARGET)) {
  227. types |= PICK_TYPE_MINES;
  228. }
  229. if (BitTest( command->getOptions(), ALLOW_SHRUBBERY_TARGET ) ) {
  230. types |= PICK_TYPE_SHRUBBERY;
  231. }
  232. } else {
  233. types |= getPickTypesForCurrentSelection(forceAttackMode);
  234. }
  235. return types;
  236. } // end getPickTypesForContext
  237. //-------------------------------------------------------------------------------------------------
  238. UnsignedInt getPickTypesForCurrentSelection( Bool forceAttackMode )
  239. {
  240. UnsignedInt retVal = 0;
  241. if (!TheInGameUI->areSelectedObjectsControllable()) {
  242. return retVal;
  243. }
  244. const DrawableList *allSelectedDrawables = TheInGameUI->getAllSelectedDrawables();
  245. for (DrawableListCIt cit = allSelectedDrawables->begin(); cit != allSelectedDrawables->end(); ++cit) {
  246. Drawable *draw = *cit;
  247. if (!draw) {
  248. continue;
  249. }
  250. Object *obj = draw->getObject();
  251. if (!obj) {
  252. continue;
  253. }
  254. // srj sez: thanks to new, area-effect disarming, we NO LONGER want to do this...
  255. // if (obj->hasWeaponToDealDamageType(DAMAGE_DISARM)) {
  256. // retVal |= PICK_TYPE_MINES;
  257. // }
  258. if (obj->hasWeaponToDealDamageType(DAMAGE_FLAME) && forceAttackMode ) {
  259. retVal |= PICK_TYPE_SHRUBBERY;
  260. }
  261. // For efficiency.
  262. if (BitTest(retVal, PICK_TYPE_MINES | PICK_TYPE_SHRUBBERY)) {
  263. break;
  264. }
  265. }
  266. return retVal;
  267. }
  268. //-------------------------------------------------------------------------------------------------
  269. void translatePickTypesToKindof(UnsignedInt pickTypes, KindOfMaskType& outMask)
  270. {
  271. if (BitTest(pickTypes, PICK_TYPE_SELECTABLE)) {
  272. outMask.set(KINDOF_SELECTABLE);
  273. }
  274. if (BitTest(pickTypes, PICK_TYPE_SHRUBBERY)) {
  275. outMask.set(KINDOF_SHRUBBERY);
  276. }
  277. if (BitTest(pickTypes, PICK_TYPE_MINES)) {
  278. outMask.set(KINDOF_MINE);
  279. }
  280. if (BitTest(pickTypes, PICK_TYPE_FORCEATTACKABLE)) {
  281. outMask.set(KINDOF_FORCEATTACKABLE);
  282. }
  283. }
  284. //-------------------------------------------------------------------------------------------------
  285. // Given a drawable, add it to an stl list specified by userData.
  286. // Useful for iterateDrawablesInRegion.
  287. Bool addDrawableToList( Drawable *draw, void *userData )
  288. {
  289. PickDrawableStruct *pds = (PickDrawableStruct *) userData;
  290. #if defined(_DEBUG) || defined(_INTERNAL)
  291. if (TheGlobalData->m_allowUnselectableSelection) {
  292. pds->drawableListToFill->push_back(draw);
  293. return TRUE;
  294. }
  295. #endif
  296. if (!pds->drawableListToFill)
  297. return FALSE;
  298. if (!draw->getTemplate()->isAnyKindOf(pds->kindofsToMatch))
  299. return FALSE;
  300. if (!draw->isSelectable())
  301. return FALSE;
  302. pds->drawableListToFill->push_back(draw);
  303. return TRUE;
  304. }