SelectionXlat.cpp 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363
  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. // SelectionXlat.cpp
  24. // Message stream translator
  25. // Author: Michael S. Booth, January 2001
  26. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  27. #include "Common/ActionManager.h"
  28. #include "Common/GameAudio.h"
  29. #include "Common/GameEngine.h"
  30. #include "Common/MessageStream.h"
  31. #include "Common/MiscAudio.h"
  32. #include "Common/Player.h"
  33. #include "Common/PlayerList.h"
  34. #include "Common/ThingTemplate.h"
  35. #include "GameLogic/Damage.h"
  36. #include "GameLogic/GameLogic.h"
  37. #include "GameLogic/Object.h"
  38. #include "GameLogic/Squad.h"
  39. #include "GameLogic/Module/BodyModule.h"
  40. #include "GameLogic/Module/ContainModule.h"
  41. #include "GameLogic/Module/UpdateModule.h"
  42. #include "GameClient/ControlBar.h"
  43. #include "GameClient/Display.h"
  44. #include "GameClient/Drawable.h"
  45. #include "GameClient/GameClient.h"
  46. #include "GameClient/GameText.h"
  47. #include "GameClient/GameWindowManager.h"
  48. #include "GameClient/Keyboard.h"
  49. #include "GameClient/SelectionInfo.h"
  50. #include "GameClient/SelectionXlat.h"
  51. #include "GameClient/TerrainVisual.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. //-----------------------------------------------------------------------------
  59. //-----------------------------------------------------------------------------
  60. // Lorenzen changed this to a member of SelectionTranslator, providing external access
  61. // name ly in rebuildholeexposedie, where we decide whether to create GLA Holes when hand-of-Godding
  62. //#if defined(_DEBUG) || defined(_INTERNAL) || defined(_ALLOW_DEBUG_CHEATS_IN_RELEASE)
  63. //static Bool TheHandOfGodSelectionMode = false;
  64. //#endif
  65. #if defined(_DEBUG) || defined(_INTERNAL)
  66. static Bool TheHurtSelectionMode = false;
  67. static Bool TheDebugSelectionMode = false;
  68. #endif
  69. //-----------------------------------------------------------------------------
  70. static Bool currentlyLookingForSelection( )
  71. {
  72. // This needs to check if we are currently targetting for special weapons fire.
  73. return TheInGameUI->getGUICommand() == NULL;
  74. }
  75. //-----------------------------------------------------------------------------
  76. static Bool areAllSelected( const DrawableList& listToCheck )
  77. {
  78. DrawableListCIt it;
  79. for ( it = listToCheck.begin(); it != listToCheck.end(); ++it ) {
  80. if (!*it)
  81. continue;
  82. if (!(*it)->isSelected())
  83. return FALSE;
  84. }
  85. return TRUE;
  86. }
  87. //-----------------------------------------------------------------------------
  88. struct SFWRec
  89. {
  90. SelectionTranslator *translator;
  91. GameMessage *createTeamMsg;
  92. Bool dragSelecting;
  93. };
  94. //-----------------------------------------------------------------------------
  95. /*friend*/ Bool selectFriendsWrapper( Drawable *draw, void *userData )
  96. {
  97. SFWRec *info = (SFWRec *)userData;
  98. return info->translator->selectFriends(draw, info->createTeamMsg, info->dragSelecting) != 0;
  99. } // end selectFriendsWrapper
  100. /*friend*/ Bool killThemKillThemAllWrapper( Drawable *draw, void *userData )
  101. {
  102. SFWRec *info = (SFWRec *)userData;
  103. info->translator->killThemKillThemAll( draw, info->createTeamMsg );
  104. return true;
  105. }
  106. //-----------------------------------------------------------------------------
  107. /**
  108. * Returns true if the drawable can be selected under the current rules
  109. * of the system
  110. */
  111. Bool CanSelectDrawable( const Drawable *draw, Bool dragSelecting )
  112. {
  113. if(!draw || !draw->getObject())
  114. {
  115. return FALSE; // can't select
  116. }
  117. const Object *obj = draw->getObject();
  118. if( obj->isEffectivelyDead() && !obj->isKindOf(KINDOF_ALWAYS_SELECTABLE))
  119. {
  120. //Don't select dead/dying units.
  121. return FALSE;
  122. }
  123. //Added this to support attacking cargo planes without being able to select them.
  124. //I added the KINDOF_FORCEATTACKABLE to them, but unsure if it's possible to select
  125. //something without the KINDOF_SELECTABLE -- so doing a LATE code change. My gut
  126. //says we should simply have the KINDOF_SELECTABLE check only... but best to be safe.
  127. if( !obj->isKindOf( KINDOF_SELECTABLE ) && obj->isKindOf( KINDOF_FORCEATTACKABLE ) )
  128. {
  129. return FALSE;
  130. }
  131. // hidden objects cannot be selected
  132. if( draw->isDrawableEffectivelyHidden() )
  133. {
  134. return FALSE; // can't select
  135. }
  136. // ignore objects obscured by the GUI
  137. GameWindow *window = NULL;
  138. if (TheWindowManager)
  139. {
  140. const Coord3D *c = draw->getPosition();
  141. ICoord2D c2;
  142. TheTacticalView->worldToScreen(c, &c2);
  143. window = TheWindowManager->getWindowUnderCursor(c2.x, c2.y);
  144. }
  145. while (window)
  146. {
  147. // check to see if it or any of its parents are opaque. If so, we can't select anything.
  148. if (!BitTest( window->winGetStatus(), WIN_STATUS_SEE_THRU ))
  149. {
  150. return FALSE;
  151. }
  152. window = window->winGetParent();
  153. }
  154. //
  155. // structures cannot be selected by a drag select, you must individually pick them
  156. // NOTE that this is really a convenience for the multi select context sensitive UI,
  157. // later we might want to allow you to drag select buildings if only one building is
  158. // actually in the selection area, but don't forget complications like holding down
  159. // a key to "add" to an already existing selection list
  160. //
  161. // not allowing you to have multiple buildings selected drastically simplifies the
  162. // user interface ... including all those context sensitive commands that we
  163. // can just assume are for a single building selected.
  164. //
  165. if( dragSelecting && draw->isKindOf( KINDOF_STRUCTURE ) )
  166. {
  167. return FALSE;
  168. }
  169. // You cannot select something that has a logic override of unselectability or masked
  170. if( obj->getStatusBits().testForAny( MAKE_OBJECT_STATUS_MASK2( OBJECT_STATUS_UNSELECTABLE, OBJECT_STATUS_MASKED ) ) )
  171. {
  172. return FALSE;
  173. }
  174. if (!obj->isSelectable())
  175. {
  176. return false;
  177. }
  178. //Now allowing the selection of everything including enemies... but only if not drag selecting.
  179. //In fact the only way you can drag select is if the unit is on your team.
  180. if( dragSelecting && !obj->isLocallyControlled() )
  181. {
  182. return FALSE;
  183. }
  184. //Now we can select anything that is selectable.
  185. return TRUE;
  186. } // end canSelect
  187. //-----------------------------------------------------------------------------
  188. static Bool canSelectWrapper( Drawable *draw, void *userData )
  189. {
  190. Bool dragSelecting = *((Bool *)userData);
  191. return CanSelectDrawable( draw, dragSelecting );
  192. }
  193. //-----------------------------------------------------------------------------
  194. /**
  195. * Deselect all drawables, and emit a "TEAM_DESTROY" message, since
  196. * the "team" was the group of currently selected units.
  197. */
  198. static void deselectAll()
  199. {
  200. // deselect it all
  201. TheInGameUI->deselectAllDrawables();
  202. }
  203. //-----------------------------------------------------------------------------
  204. /**
  205. * Select the given drawable, without playing its sound.
  206. * Returns true.
  207. */
  208. static Bool selectSingleDrawableWithoutSound( Drawable *draw )
  209. {
  210. // since we are single selecting a drawable, unselect everything else
  211. deselectAll();
  212. // do the drawble selection
  213. TheInGameUI->selectDrawable( draw );
  214. Object *obj = draw->getObject();
  215. if (obj != NULL) {
  216. GameMessage *msg = TheMessageStream->appendMessage(GameMessage::MSG_CREATE_SELECTED_GROUP_NO_SOUND);
  217. msg->appendBooleanArgument(TRUE);
  218. msg->appendObjectIDArgument(obj->getID());
  219. }
  220. return true;
  221. }
  222. SelectionTranslator *TheSelectionTranslator = NULL;
  223. //-----------------------------------------------------------------------------
  224. //-----------------------------------------------------------------------------
  225. //-----------------------------------------------------------------------------
  226. //-----------------------------------------------------------------------------
  227. SelectionTranslator::SelectionTranslator()
  228. {
  229. m_leftMouseButtonIsDown = FALSE;
  230. m_dragSelecting = FALSE;
  231. m_lastGroupSelTime = 0;
  232. m_lastGroupSelGroup = -1;
  233. m_selectFeedbackAnchor.x = 0;
  234. m_selectFeedbackAnchor.y = 0;
  235. m_deselectFeedbackAnchor.x = 0;
  236. m_deselectFeedbackAnchor.y = 0;
  237. m_lastClick = 0;
  238. //Added By Sadullah Nader
  239. //Initializtion(s) inserted
  240. m_deselectDownCameraPosition.zero();
  241. m_displayedMaxWarning = FALSE;
  242. //
  243. m_selectCountMap.clear();
  244. TheSelectionTranslator = this;
  245. #if defined(_DEBUG) || defined(_INTERNAL) || defined(_ALLOW_DEBUG_CHEATS_IN_RELEASE)
  246. m_HandOfGodSelectionMode = FALSE;
  247. #endif
  248. }
  249. //-----------------------------------------------------------------------------
  250. SelectionTranslator::~SelectionTranslator()
  251. {
  252. }
  253. //-----------------------------------------------------------------------------
  254. /**
  255. * If this drawable is a 'friend' of mine, select it.
  256. */
  257. Bool SelectionTranslator::selectFriends( Drawable *draw, GameMessage *createTeamMsg,
  258. Bool dragSelecting )
  259. {
  260. if (CanSelectDrawable( draw, dragSelecting ))
  261. {
  262. // enforce an optional selection size limit
  263. if (TheInGameUI->getMaxSelectCount() > 0 && TheInGameUI->getSelectCount() >= TheInGameUI->getMaxSelectCount())
  264. {
  265. if (!m_displayedMaxWarning)
  266. {
  267. m_displayedMaxWarning = TRUE;
  268. UnicodeString msg;
  269. msg.format(TheGameText->fetch("GUI:MaxSelectionSize").str(), TheInGameUI->getMaxSelectCount());
  270. TheInGameUI->message(msg);
  271. }
  272. return false;
  273. }
  274. TheInGameUI->selectDrawable( draw );
  275. m_selectCountMap[draw->getTemplate()]++;
  276. // add to message's argument list if an object is present
  277. if( draw->getObject() && createTeamMsg )
  278. createTeamMsg->appendObjectIDArgument( draw->getObject()->getID() );
  279. return true; // selected
  280. } // end if
  281. return false; // not selected
  282. } // end selectFriends
  283. //-----------------------------------------------------------------------------
  284. Bool SelectionTranslator::killThemKillThemAll( Drawable *draw, GameMessage *killThemAllMsg )
  285. {
  286. if( draw )
  287. {
  288. Object *obj = draw->getObject();
  289. if( obj )
  290. {
  291. // enforce an optional selection size limit
  292. if (TheInGameUI->getMaxSelectCount() > 0 && TheInGameUI->getSelectCount() >= TheInGameUI->getMaxSelectCount())
  293. {
  294. if (!m_displayedMaxWarning)
  295. {
  296. m_displayedMaxWarning = TRUE;
  297. UnicodeString msg;
  298. msg.format(TheGameText->fetch("GUI:MaxSelectionSize").str(), TheInGameUI->getMaxSelectCount());
  299. TheInGameUI->message(msg);
  300. }
  301. return false;
  302. }
  303. // add to message's argument list if an object is present
  304. if( killThemAllMsg )
  305. {
  306. killThemAllMsg->appendObjectIDArgument( draw->getObject()->getID() );
  307. }
  308. return true; // selected
  309. }
  310. }
  311. return false;
  312. } // end selectFriends
  313. //-----------------------------------------------------------------------------
  314. /**
  315. * The SelectionTranslator is responsible for all selection semantics,
  316. * including click selection, area drag selection, right-click de-selection,
  317. * and CTRL-key group selection.
  318. * NOTE: This handler changes the event semantics for mouse buttons from
  319. * LEFT_DOWN -> LEFT_UP to LEFT_DOWN -> { LEFT_UP, AREA_SELECTION, or DRAWABLE_PICKED }
  320. */
  321. GameMessageDisposition SelectionTranslator::translateGameMessage(const GameMessage *msg)
  322. {
  323. GameMessageDisposition disp = KEEP_MESSAGE;
  324. if( !TheInGameUI->getInputEnabled() )
  325. {
  326. //Keep the message so the other translaters (WindowXlat) can handle.
  327. if( m_dragSelecting )
  328. {
  329. //Turn off drag select
  330. m_dragSelecting = FALSE;
  331. TheInGameUI->setSelecting( FALSE );
  332. TheInGameUI->endAreaSelectHint(NULL);
  333. TheTacticalView->setMouseLock( FALSE );
  334. }
  335. return KEEP_MESSAGE;
  336. }
  337. GameMessage::Type t = msg->getType();
  338. switch (t)
  339. {
  340. case GameMessage::MSG_META_BEGIN_FORCEATTACK:
  341. TheInGameUI->setForceAttackMode( true );
  342. break;
  343. case GameMessage::MSG_META_END_FORCEATTACK:
  344. TheInGameUI->setForceAttackMode( false );
  345. break;
  346. //-----------------------------------------------------------------------------
  347. case GameMessage::MSG_RAW_MOUSE_POSITION:
  348. {
  349. ICoord2D pixel;
  350. pixel = msg->getArgument( 0 )->pixel;
  351. // modifier appears to be unused, and the argument doesn't exist. jba.
  352. //Int modifier = msg->getArgument( 1 )->integer;
  353. if (m_leftMouseButtonIsDown)
  354. {
  355. ICoord2D delta;
  356. delta.x = abs(pixel.x - m_selectFeedbackAnchor.x);
  357. delta.y = abs(pixel.y - m_selectFeedbackAnchor.y);
  358. // if mouse has moved while left button is down, begin drag selection
  359. if (delta.x > TheMouse->m_dragTolerance || delta.y > TheMouse->m_dragTolerance)
  360. {
  361. if (m_dragSelecting == false)
  362. {
  363. m_dragSelecting = true;
  364. TheTacticalView->setMouseLock( TRUE );
  365. TheInGameUI->setSelecting( TRUE );
  366. }
  367. }
  368. // create "hint" messages defining selection region under construction
  369. if (m_dragSelecting)
  370. {
  371. // insert area selection "hint" message into stream
  372. GameMessage *hintMsg = TheMessageStream->appendMessage( GameMessage::MSG_AREA_SELECTION_HINT );
  373. // build rectangular region defined by the drag selection
  374. IRegion2D pixelRegion;
  375. buildRegion( &m_selectFeedbackAnchor, &pixel, &pixelRegion );
  376. hintMsg->appendPixelRegionArgument( pixelRegion );
  377. }
  378. }
  379. else //left button is not down (not drag select)
  380. {
  381. // insert Mouseover hint into stream for CommandTranslator and HintSpy to see.
  382. GameMessage *mouseoverMessage;
  383. //Kris: We want to show information such as the popup text on objects that are forceattackable even
  384. // when we're not in force attackable mode!
  385. UnsignedInt pickType = getPickTypesForContext( true /*TheInGameUI->isInForceAttackMode()*/ );
  386. Drawable *underCursor = TheTacticalView->pickDrawable( &pixel, TheInGameUI->isInForceAttackMode(), (PickType) pickType );
  387. Object *objUnderCursor = underCursor ? underCursor->getObject() : NULL;
  388. if( objUnderCursor && (!objUnderCursor->isEffectivelyDead() || objUnderCursor->isKindOf( KINDOF_ALWAYS_SELECTABLE )) )
  389. {
  390. mouseoverMessage = TheMessageStream->appendMessage( GameMessage::MSG_MOUSEOVER_DRAWABLE_HINT );
  391. mouseoverMessage->appendDrawableIDArgument( underCursor->getID() );
  392. }
  393. else// else this is a mouseover terrain
  394. {
  395. Coord3D position;
  396. TheTacticalView->screenToTerrain( &pixel, &position );
  397. mouseoverMessage = TheMessageStream->appendMessage( GameMessage::MSG_MOUSEOVER_LOCATION_HINT );
  398. mouseoverMessage->appendLocationArgument( position );
  399. }
  400. }
  401. break;
  402. }
  403. //-----------------------------------------------------------------------------
  404. case GameMessage::MSG_MOUSE_LEFT_DOUBLE_CLICK:
  405. {
  406. Int modifiers = msg->getArgument(1)->integer;
  407. // Pressing ctrl is disallowed for double clicking
  408. if (TheInGameUI->isInForceAttackMode())
  409. break;
  410. const IRegion2D& region = msg->getArgument(0)->pixelRegion;
  411. // Single point. If there's a unit in there, double click will select all of them.
  412. if (region.height() == 0 && region.width() == 0)
  413. {
  414. Bool selectAcrossMap = (BitTest(modifiers, KEY_STATE_ALT) ? TRUE : FALSE);
  415. // only allow things that are selectable. Also, we aren't allowed to
  416. Drawable *picked = TheTacticalView->pickDrawable( &region.lo, FALSE, PICK_TYPE_SELECTABLE);
  417. // If there wasn't anyone to pick, then we want to propagate this double click.
  418. if (picked == NULL)
  419. break;
  420. if (!picked->isMassSelectable())
  421. break;
  422. Object *pickedObj = picked->getObject();
  423. // We have to have an object in order to be able to do interesting double click stuff on
  424. // him. Also, if it is a structure, it is already selected, so don't select all the units
  425. // like him.
  426. if (pickedObj == NULL || !pickedObj->isLocallyControlled())
  427. break;
  428. // Ok. The logic is a little bit weird here. What we need to do is deselect everything
  429. // except for this one picked thing. Store off the old selection, pick the single clicked thing.
  430. // Then if
  431. DrawableList listOfSelectedDrawables;
  432. if (TheInGameUI->isInPreferSelectionMode()) {
  433. listOfSelectedDrawables = *TheInGameUI->getAllSelectedDrawables();
  434. }
  435. // Pick just that one guy.
  436. selectSingleDrawableWithoutSound(picked);
  437. // Yay. Either select across the screen or the world depending on selectAcrossMap
  438. if (selectAcrossMap)
  439. TheInGameUI->selectMatchingAcrossMap();
  440. else
  441. TheInGameUI->selectMatchingAcrossScreen();
  442. // emit "picked" message
  443. GameMessage *pickMsg = TheMessageStream->appendMessage( GameMessage::MSG_AREA_SELECTION );
  444. pickMsg->appendDrawableIDArgument( picked->getID() ); /// note we are putting in a drawable id
  445. if (TheInGameUI->isInPreferSelectionMode() && !listOfSelectedDrawables.empty()) {
  446. GameMessage *selectMore = TheMessageStream->appendMessage( GameMessage::MSG_CREATE_SELECTED_GROUP_NO_SOUND );
  447. selectMore->appendBooleanArgument(FALSE);
  448. for (DrawableListIt it = listOfSelectedDrawables.begin(); it != listOfSelectedDrawables.end(); ++it) {
  449. Drawable *draw = *it;
  450. if (draw && draw->isSelectable()) {
  451. TheInGameUI->selectDrawable(draw);
  452. selectMore->appendObjectIDArgument(draw->getObject()->getID());
  453. }
  454. }
  455. }
  456. disp = DESTROY_MESSAGE;
  457. }
  458. break;
  459. }
  460. //-----------------------------------------------------------------------------
  461. case GameMessage::MSG_MOUSEOVER_DRAWABLE_HINT:
  462. {
  463. if (TheInGameUI->isScrolling()) {
  464. // dont show this now.
  465. break;
  466. }
  467. DrawableID id = msg->getArgument(0)->drawableID;
  468. Drawable *draw = TheGameClient->findDrawableByID(id);
  469. if (!draw) {
  470. break;
  471. }
  472. GameMessage::Type msgType = TheGameClient->evaluateContextCommand(draw, draw->getPosition(), CommandTranslator::EVALUATE_ONLY);
  473. if( msgType == GameMessage::MSG_INVALID )
  474. {
  475. TheInGameUI->createMouseoverHint(msg); // this sets the cursor
  476. disp = DESTROY_MESSAGE;
  477. const CommandButton *command = TheInGameUI->getGUICommand();
  478. Bool ignoreCommand = FALSE;
  479. if( command )
  480. {
  481. if( command->getCommandType() == GUI_COMMAND_ATTACK_MOVE ||
  482. command->getCommandType() == GUI_COMMAND_GUARD ||
  483. command->getCommandType() == GUI_COMMAND_GUARD_WITHOUT_PURSUIT ||
  484. command->getCommandType() == GUI_COMMAND_GUARD_FLYING_UNITS_ONLY )
  485. {
  486. //These GUI commands can take care of themselves -- don't let
  487. //the selection translator meddle.
  488. ignoreCommand = TRUE;
  489. }
  490. }
  491. if( !ignoreCommand && !draw->getTemplate()->isKindOf( KINDOF_SHRUBBERY ) )
  492. {
  493. if( CanSelectDrawable( draw, FALSE ) )
  494. {
  495. TheMouse->setCursor(Mouse::SELECTING);
  496. }
  497. else
  498. {
  499. TheMouse->setCursor( Mouse::ARROW );
  500. }
  501. }
  502. }
  503. break;
  504. }
  505. //-----------------------------------------------------------------------------
  506. case GameMessage::MSG_MOUSE_LEFT_CLICK:
  507. {
  508. // If the quit menu is visible, we need to not process left clicks through the selection translator.
  509. if (TheInGameUI->isQuitMenuVisible())
  510. {
  511. disp = DESTROY_MESSAGE;
  512. break;
  513. }
  514. // Basically, we need to first determine if there are any drawables in the region of interest.
  515. // If there aren't then this click should move forward.
  516. IRegion2D selectionRegion = msg->getArgument(0)->pixelRegion;
  517. Bool isPoint = (selectionRegion.height() == 0 && selectionRegion.width() == 0);
  518. DrawableList drawablesThatWillSelect;
  519. PickDrawableStruct pds;
  520. pds.drawableListToFill = &drawablesThatWillSelect;
  521. TheTacticalView->iterateDrawablesInRegion(&selectionRegion, addDrawableToList, &pds);
  522. if (drawablesThatWillSelect.empty())
  523. {
  524. break;
  525. }
  526. // if there were drawables in the region, then we should determine if there is a context
  527. // sensitive command that should take place. If there is, then this isn't a selection thing
  528. const DrawableList *currentList = TheInGameUI->getAllSelectedDrawables();
  529. if (!currentlyLookingForSelection())
  530. {
  531. break;
  532. }
  533. SelectionInfo si;
  534. if (contextCommandForNewSelection(currentList, &drawablesThatWillSelect, &si, isPoint))
  535. {
  536. break;
  537. }
  538. // There isn't a context command, so this is a selection thing. Now, based on the keys,
  539. // determine whether or not we should create a new group, or append these guys to our existing
  540. // group.
  541. Bool addToGroup = TheInGameUI->isInPreferSelectionMode();
  542. if (si.currentCountEnemies > 0 ||
  543. si.currentCountCivilians > 0 ||
  544. si.currentCountFriends > 0 ||
  545. si.currentCountMineBuildings > 0)
  546. {
  547. // force a new group creation
  548. addToGroup = FALSE;
  549. }
  550. // If there are any of my units, then select those.
  551. if (si.newCountMine > 0)
  552. {
  553. si.selectMine = TRUE;
  554. // EXACTLY ONE CLICKED OR DRAGGED BUILDING
  555. if ( si.newCountMineBuildings == 1 && si.newCountMine == 1 )
  556. {
  557. addToGroup = FALSE;
  558. si.selectMineBuildings = TRUE;
  559. }
  560. else if ( si.newCountMineBuildings > 0 )////////////// SO SORRY, I KNOW THIS IS MICKEY MOUSE ///////////////////
  561. { // What we are after here is to allow the drag select to get the building,
  562. // if the other things in the list are going to be ignored anyway
  563. // so we find out whether the other things are not selectible
  564. // this came up with the new AmericaBuildingFireBase, which shows its contained
  565. // but does not let you select them. The selection is propagated to the container
  566. // in new code in SelectionInfo.cpp, in the static addDrawableToList();
  567. // -Mark Lorenzen, 6/12/03
  568. Bool onlyTheOneBuildingIsSelectableAnyway = TRUE;
  569. DrawableID buildingID = INVALID_DRAWABLE_ID;
  570. for (DrawableListIt it = drawablesThatWillSelect.begin(); it != drawablesThatWillSelect.end(); ++it)
  571. {
  572. const Drawable *d = *it;
  573. if ( d->isKindOf( KINDOF_STRUCTURE ) )
  574. {// make sure there is really only the one building in the list, as it may be multiply listed
  575. if ( buildingID == INVALID_DRAWABLE_ID ) // this is the first building
  576. buildingID = d->getID();
  577. else if ( buildingID != d->getID() )//oops, more than one building!
  578. onlyTheOneBuildingIsSelectableAnyway = FALSE;
  579. }
  580. else if ( d->isSelectable() )
  581. onlyTheOneBuildingIsSelectableAnyway = FALSE;
  582. if ( ! onlyTheOneBuildingIsSelectableAnyway )
  583. break;
  584. }
  585. if ( onlyTheOneBuildingIsSelectableAnyway )
  586. {
  587. addToGroup = FALSE;
  588. si.selectMineBuildings = TRUE;
  589. }
  590. }
  591. }
  592. else if (si.newCountEnemies > 0 && si.newCountCivilians > 0 && si.newCountFriends > 0)
  593. {
  594. // No go here
  595. break;
  596. }
  597. else if (si.newCountEnemies == 1)
  598. {
  599. addToGroup = FALSE;
  600. si.selectEnemies = TRUE;
  601. }
  602. else if (si.newCountCivilians == 1)
  603. {
  604. addToGroup = FALSE;
  605. si.selectCivilians = TRUE;
  606. }
  607. else if (si.newCountFriends == 1)
  608. {
  609. addToGroup = FALSE;
  610. si.selectFriends = TRUE;
  611. }
  612. // If we're not going to select anything, just bail now.
  613. if (!(si.selectMine || si.selectEnemies || si.selectCivilians || si.selectFriends))
  614. {
  615. break;
  616. }
  617. // If we've made it here, its time to do some selecting.
  618. disp = DESTROY_MESSAGE;
  619. // Whenever we manually select something, reset the last selected group.
  620. m_lastGroupSelGroup = -1;
  621. if (TheInGameUI->isInPreferSelectionMode() && isPoint && areAllSelected(drawablesThatWillSelect))
  622. {
  623. // If this was a point, shift was pressed and we already have that unit selected, then we
  624. // need to deselect those units.
  625. GameMessage *newMsg = TheMessageStream->appendMessage(GameMessage::MSG_REMOVE_FROM_SELECTED_GROUP);
  626. Drawable *draw = NULL;
  627. DrawableListIt it;
  628. for (it = drawablesThatWillSelect.begin(); it != drawablesThatWillSelect.end(); ++it)
  629. {
  630. draw = *it;
  631. if (!draw)
  632. {
  633. continue;
  634. }
  635. Object *objToDeselect = draw->getObject();
  636. if (!objToDeselect)
  637. {
  638. continue;
  639. }
  640. newMsg->appendObjectIDArgument(objToDeselect->getID());
  641. TheInGameUI->deselectDrawable(draw);
  642. }
  643. }
  644. else
  645. {
  646. if (!addToGroup)
  647. {
  648. deselectAll();
  649. }
  650. GameMessage *newMsg = TheMessageStream->appendMessage(GameMessage::MSG_CREATE_SELECTED_GROUP);
  651. newMsg->appendBooleanArgument(!addToGroup);
  652. Player *localPlayer = ThePlayerList->getLocalPlayer();
  653. Int newDrawablesSelected = 0;
  654. Drawable *draw = NULL;
  655. DrawableListIt it;
  656. for (it = drawablesThatWillSelect.begin(); it != drawablesThatWillSelect.end(); ++it)
  657. {
  658. draw = *it;
  659. if (!draw)
  660. {
  661. continue;
  662. }
  663. Object *obj = draw->getObject();
  664. if (!obj)
  665. {
  666. continue;
  667. }
  668. if (obj && obj->getContainedBy() != NULL)
  669. {
  670. // we're contained, and so we shouldn't be selectable.
  671. continue;
  672. }
  673. Drawable *drawToSelect = NULL;
  674. ObjectID objToAppend = INVALID_ID;
  675. if (si.selectMine && obj->isLocallyControlled())
  676. {
  677. if (!obj->isKindOf(KINDOF_STRUCTURE) || si.selectMineBuildings)
  678. {
  679. drawToSelect = draw;
  680. objToAppend = obj->getID();
  681. }
  682. }
  683. else
  684. {
  685. Relationship rel = localPlayer->getRelationship(obj->getTeam());
  686. if (si.selectEnemies && rel == ENEMIES)
  687. {
  688. drawToSelect = draw;
  689. objToAppend = obj->getID();
  690. }
  691. else if (si.selectCivilians && rel == NEUTRAL)
  692. {
  693. drawToSelect = draw;
  694. objToAppend = obj->getID();
  695. }
  696. else if (si.selectFriends && rel == ALLIES)
  697. {
  698. drawToSelect = draw;
  699. objToAppend = obj->getID();
  700. }
  701. }
  702. if (drawToSelect && objToAppend != INVALID_ID)
  703. {
  704. newMsg->appendObjectIDArgument(objToAppend);
  705. TheInGameUI->selectDrawable(drawToSelect);
  706. ++newDrawablesSelected;
  707. }
  708. }
  709. if( newDrawablesSelected > 1 )
  710. {
  711. localPlayer->getAcademyStats()->recordDragSelection();
  712. }
  713. if (newDrawablesSelected == 1 && draw)
  714. {
  715. #if defined(_DEBUG) || defined(_INTERNAL)
  716. if (m_HandOfGodSelectionMode && draw)
  717. {
  718. Object* obj = draw->getObject();
  719. if (obj)
  720. {
  721. TheAudio->addAudioEvent(&TheAudio->getMiscAudio()->m_noCanDoSound);
  722. GameMessage* msg = TheMessageStream->appendMessage( GameMessage::MSG_DEBUG_KILL_OBJECT );
  723. msg->appendObjectIDArgument(obj->getID());
  724. }
  725. disp = DESTROY_MESSAGE;
  726. break;
  727. }
  728. else
  729. if (TheHurtSelectionMode && draw)
  730. {
  731. Object* obj = draw->getObject();
  732. if (obj)
  733. {
  734. TheAudio->addAudioEvent(&TheAudio->getMiscAudio()->m_noCanDoSound);
  735. GameMessage* msg = TheMessageStream->appendMessage( GameMessage::MSG_DEBUG_HURT_OBJECT );
  736. msg->appendObjectIDArgument(obj->getID());
  737. }
  738. disp = DESTROY_MESSAGE;
  739. break;
  740. }
  741. #ifdef DEBUG_OBJECT_ID_EXISTS
  742. if (TheDebugSelectionMode && draw && draw->getObject())
  743. {
  744. if (TheObjectIDToDebug == 0)
  745. {
  746. TheObjectIDToDebug = draw->getObject()->getID();
  747. AsciiString msg;
  748. msg.format("Item %s %08x selected for debugging",draw->getTemplate()->getName().str(),TheObjectIDToDebug);
  749. UnicodeString msgu;
  750. msgu.translate(msg);
  751. TheInGameUI->message(msgu);
  752. disp = DESTROY_MESSAGE;
  753. break;
  754. }
  755. }
  756. #endif
  757. #endif
  758. #if defined(_ALLOW_DEBUG_CHEATS_IN_RELEASE)
  759. if (m_HandOfGodSelectionMode && draw)
  760. {
  761. Object* obj = draw->getObject();
  762. if (obj)
  763. {
  764. TheAudio->addAudioEvent(&TheAudio->getMiscAudio()->m_noCanDoSound);
  765. GameMessage* msg = TheMessageStream->appendMessage( GameMessage::MSG_DEBUG_KILL_OBJECT );
  766. msg->appendObjectIDArgument(obj->getID());
  767. }
  768. disp = DESTROY_MESSAGE;
  769. break;
  770. }
  771. #endif
  772. }
  773. }
  774. if (disp == DESTROY_MESSAGE)
  775. TheInGameUI->clearAttackMoveToMode();
  776. break;
  777. }
  778. //-----------------------------------------------------------------------------
  779. // Note that the raw left messages are only used to draw feedback now when
  780. // appropriate. All actual selection code takes place in
  781. // MSG_MOUSE_LEFT_CLICK & MSG_MOUSE_LEFT_DOUBLE_CLICK
  782. case GameMessage::MSG_RAW_MOUSE_LEFT_BUTTON_DOWN:
  783. {
  784. // cannot actually start area selection yet - have to wait for cursor to move a bit
  785. m_leftMouseButtonIsDown = true;
  786. m_selectFeedbackAnchor = msg->getArgument( 0 )->pixel;
  787. break;
  788. }
  789. //-----------------------------------------------------------------------------
  790. // Note that the raw left messages are only used to draw feedback now when
  791. // appropriate. All actual selection code takes place in
  792. // MSG_MOUSE_LEFT_CLICK & MSG_MOUSE_LEFT_DOUBLE_CLICK
  793. case GameMessage::MSG_RAW_MOUSE_LEFT_BUTTON_UP:
  794. {
  795. m_leftMouseButtonIsDown = FALSE;
  796. if (m_dragSelecting) {
  797. // Stop drag selecting now, thanks.
  798. m_dragSelecting = FALSE;
  799. TheTacticalView->setMouseLock( FALSE );
  800. TheInGameUI->setSelecting( FALSE );
  801. TheInGameUI->endAreaSelectHint(NULL);
  802. // insert area selection message into stream
  803. GameMessage *dragMsg = TheMessageStream->appendMessage( GameMessage::MSG_AREA_SELECTION );
  804. IRegion2D selectionRegion;
  805. buildRegion( &m_selectFeedbackAnchor, &msg->getArgument(0)->pixel, &selectionRegion );
  806. dragMsg->appendPixelRegionArgument( selectionRegion );
  807. }
  808. else
  809. {
  810. // left click behavior (not right drag)
  811. //Added support to cancel the GUI command without deselecting the unit(s) involved
  812. //when you right click.
  813. if( !TheInGameUI->getGUICommand() && !TheKeyboard->isShift() && !TheKeyboard->isCtrl() && !TheKeyboard->isAlt() )
  814. {
  815. //No GUI command mode, so deselect everyone if we're in alternate mouse mode.
  816. if( TheGlobalData->m_useAlternateMouse && TheInGameUI->getPendingPlaceSourceObjectID() == INVALID_ID )
  817. {
  818. if( !TheInGameUI->getPreventLeftClickDeselectionInAlternateMouseModeForOneClick() )
  819. {
  820. deselectAll();
  821. }
  822. else
  823. {
  824. //Prevent deselection of unit if it just issued some type of UI order such as attack move, guard,
  825. //initiating construction of a new structure.
  826. TheInGameUI->setPreventLeftClickDeselectionInAlternateMouseModeForOneClick( FALSE );
  827. }
  828. }
  829. }
  830. }
  831. break;
  832. }
  833. //-----------------------------------------------------------------------------
  834. case GameMessage::MSG_RAW_MOUSE_RIGHT_BUTTON_DOWN:
  835. {
  836. // There are three ways in which we can ignore this as a deselect:
  837. // 1) 2-D position on screen
  838. // 2) Time has exceeded the time which we allow for this to be a click.
  839. // 3) 3-D camera position has changed
  840. m_deselectFeedbackAnchor = msg->getArgument( 0 )->pixel;
  841. m_lastClick = (UnsignedInt) msg->getArgument( 2 )->integer;
  842. TheTacticalView->getPosition(&m_deselectDownCameraPosition);
  843. break;
  844. }
  845. //-----------------------------------------------------------------------------
  846. case GameMessage::MSG_RAW_MOUSE_RIGHT_BUTTON_UP:
  847. {
  848. ICoord2D delta, pixel;
  849. UnsignedInt currentTime;
  850. Coord3D cameraPos;
  851. TheTacticalView->getPosition(&cameraPos);
  852. cameraPos.sub(&m_deselectDownCameraPosition);
  853. pixel = msg->getArgument( 0 )->pixel;
  854. currentTime = (UnsignedInt) msg->getArgument( 2 )->integer;
  855. delta.x = m_deselectFeedbackAnchor.x - pixel.x;
  856. delta.y = m_deselectFeedbackAnchor.y - pixel.y;
  857. Bool isClick = TRUE;
  858. if (isClick &&
  859. abs(delta.x) > TheMouse->m_dragTolerance ||
  860. abs(delta.y) > TheMouse->m_dragTolerance)
  861. {
  862. isClick = FALSE;
  863. }
  864. if (isClick &&
  865. currentTime - m_lastClick > TheMouse->m_dragToleranceMS)
  866. {
  867. isClick = FALSE;
  868. }
  869. if (isClick &&
  870. cameraPos.length() > TheMouse->m_dragTolerance3D)
  871. {
  872. isClick = FALSE;
  873. }
  874. // right click behavior (not right drag)
  875. if (isClick)
  876. {
  877. //Added support to cancel the GUI command without deselecting the unit(s) involved
  878. //when you right click.
  879. if( TheInGameUI->getGUICommand() )
  880. {
  881. //Cancel GUI command mode... don't deselect units.
  882. TheInGameUI->setGUICommand( NULL );
  883. //With a GUI command cancel, we want no other behavior.
  884. disp = DESTROY_MESSAGE;
  885. TheInGameUI->setScrolling( FALSE );
  886. }
  887. else
  888. {
  889. //No GUI command mode, so deselect everyone if we're in regular mouse mode.
  890. //In alternate mouse mode, right click still cancels building placement.
  891. if (! TheGlobalData->m_useAlternateMouse || TheInGameUI->getPendingPlaceSourceObjectID() != INVALID_ID)
  892. {
  893. deselectAll();
  894. }
  895. }
  896. }
  897. break;
  898. }
  899. //-----------------------------------------------------------------------------
  900. case GameMessage::MSG_META_CREATE_TEAM0:
  901. case GameMessage::MSG_META_CREATE_TEAM1:
  902. case GameMessage::MSG_META_CREATE_TEAM2:
  903. case GameMessage::MSG_META_CREATE_TEAM3:
  904. case GameMessage::MSG_META_CREATE_TEAM4:
  905. case GameMessage::MSG_META_CREATE_TEAM5:
  906. case GameMessage::MSG_META_CREATE_TEAM6:
  907. case GameMessage::MSG_META_CREATE_TEAM7:
  908. case GameMessage::MSG_META_CREATE_TEAM8:
  909. case GameMessage::MSG_META_CREATE_TEAM9:
  910. {
  911. Int group = t - GameMessage::MSG_META_CREATE_TEAM0;
  912. if ( group >= 0 && group < 10 )
  913. {
  914. DEBUG_LOG(("META: create team %d\n",group));
  915. // Assign selected items to a group
  916. GameMessage *newmsg = TheMessageStream->appendMessage((GameMessage::Type)(GameMessage::MSG_CREATE_TEAM0 + group));
  917. Drawable *drawable = TheGameClient->getDrawableList();
  918. while (drawable != NULL)
  919. {
  920. if (drawable->isSelected() && drawable->getObject() && drawable->getObject()->isLocallyControlled())
  921. {
  922. newmsg->appendObjectIDArgument(drawable->getObject()->getID());
  923. }
  924. drawable = drawable->getNextDrawable();
  925. }
  926. }
  927. disp = DESTROY_MESSAGE;
  928. break;
  929. }
  930. //-----------------------------------------------------------------------------
  931. case GameMessage::MSG_META_SELECT_TEAM0:
  932. case GameMessage::MSG_META_SELECT_TEAM1:
  933. case GameMessage::MSG_META_SELECT_TEAM2:
  934. case GameMessage::MSG_META_SELECT_TEAM3:
  935. case GameMessage::MSG_META_SELECT_TEAM4:
  936. case GameMessage::MSG_META_SELECT_TEAM5:
  937. case GameMessage::MSG_META_SELECT_TEAM6:
  938. case GameMessage::MSG_META_SELECT_TEAM7:
  939. case GameMessage::MSG_META_SELECT_TEAM8:
  940. case GameMessage::MSG_META_SELECT_TEAM9:
  941. {
  942. Int group = t - GameMessage::MSG_META_SELECT_TEAM0;
  943. if ( group >= 0 && group < 10 )
  944. {
  945. DEBUG_LOG(("META: select team %d\n",group));
  946. UnsignedInt now = TheGameLogic->getFrame();
  947. if ( m_lastGroupSelTime == 0 )
  948. {
  949. m_lastGroupSelTime = now;
  950. }
  951. // check for double-press to jump view
  952. if ( now - m_lastGroupSelTime < 20 && group == m_lastGroupSelGroup )
  953. {
  954. DEBUG_LOG(("META: DOUBLETAP select team %d\n",group));
  955. Player *player = ThePlayerList->getLocalPlayer();
  956. if (player)
  957. {
  958. Squad *selectedSquad = player->getHotkeySquad(group);
  959. if (selectedSquad != NULL)
  960. {
  961. VecObjectPtr objlist = selectedSquad->getLiveObjects();
  962. Int numObjs = objlist.size();
  963. if (numObjs > 0)
  964. {
  965. // if theres someone in the group, center the camera on them.
  966. TheTacticalView->lookAt( objlist[numObjs-1]->getDrawable()->getPosition() );
  967. }
  968. }
  969. }
  970. }
  971. else
  972. {
  973. TheInGameUI->deselectAllDrawables( false ); //No need to post message because we're just creating a new group!
  974. // no need to send two messages for selecting the same group.
  975. TheMessageStream->appendMessage((GameMessage::Type)(GameMessage::MSG_SELECT_TEAM0 + group));
  976. Player *player = ThePlayerList->getLocalPlayer();
  977. if (player)
  978. {
  979. Squad *selectedSquad = player->getHotkeySquad(group);
  980. if (selectedSquad != NULL)
  981. {
  982. VecObjectPtr objlist = selectedSquad->getLiveObjects();
  983. Int numObjs = objlist.size();
  984. for (Int i = 0; i < numObjs; ++i)
  985. {
  986. if( objlist[i]->getControllingPlayer() == player )
  987. {
  988. TheInGameUI->selectDrawable(objlist[i]->getDrawable());
  989. }
  990. }
  991. }
  992. }
  993. }
  994. m_lastGroupSelTime = now;
  995. m_lastGroupSelGroup = group;
  996. }
  997. disp = DESTROY_MESSAGE;
  998. break;
  999. }
  1000. case GameMessage::MSG_META_ADD_TEAM0:
  1001. case GameMessage::MSG_META_ADD_TEAM1:
  1002. case GameMessage::MSG_META_ADD_TEAM2:
  1003. case GameMessage::MSG_META_ADD_TEAM3:
  1004. case GameMessage::MSG_META_ADD_TEAM4:
  1005. case GameMessage::MSG_META_ADD_TEAM5:
  1006. case GameMessage::MSG_META_ADD_TEAM6:
  1007. case GameMessage::MSG_META_ADD_TEAM7:
  1008. case GameMessage::MSG_META_ADD_TEAM8:
  1009. case GameMessage::MSG_META_ADD_TEAM9:
  1010. {
  1011. Int group = t - GameMessage::MSG_META_ADD_TEAM0;
  1012. if ( group >= 0 && group < 10 )
  1013. {
  1014. DEBUG_LOG(("META: select team %d\n",group));
  1015. UnsignedInt now = TheGameLogic->getFrame();
  1016. if ( m_lastGroupSelTime == 0 )
  1017. {
  1018. m_lastGroupSelTime = now;
  1019. }
  1020. // check for double-press to jump view
  1021. if ( now - m_lastGroupSelTime < 20 && group == m_lastGroupSelGroup )
  1022. {
  1023. DEBUG_LOG(("META: DOUBLETAP select team %d\n",group));
  1024. Player *player = ThePlayerList->getLocalPlayer();
  1025. if (player)
  1026. {
  1027. Squad *selectedSquad = player->getHotkeySquad(group);
  1028. if (selectedSquad != NULL)
  1029. {
  1030. VecObjectPtr objlist = selectedSquad->getLiveObjects();
  1031. Int numObjs = objlist.size();
  1032. if (numObjs > 0)
  1033. {
  1034. // if theres someone in the group, center the camera on them.
  1035. TheTacticalView->lookAt( objlist[numObjs-1]->getDrawable()->getPosition() );
  1036. }
  1037. }
  1038. }
  1039. }
  1040. else
  1041. {
  1042. Drawable *draw = TheInGameUI->getFirstSelectedDrawable();
  1043. if( draw && draw->isKindOf( KINDOF_STRUCTURE ) )
  1044. {
  1045. //Kris: Jan 12, 2005
  1046. //Can't select other units if you have a structure selected. So deselect the structure to prevent
  1047. //group force attack exploit.
  1048. TheInGameUI->deselectAllDrawables();
  1049. }
  1050. // no need to send two messages for selecting the same group.
  1051. TheMessageStream->appendMessage((GameMessage::Type)(GameMessage::MSG_ADD_TEAM0 + group));
  1052. Player *player = ThePlayerList->getLocalPlayer();
  1053. if (player)
  1054. {
  1055. Squad *selectedSquad = player->getHotkeySquad(group);
  1056. if (selectedSquad != NULL)
  1057. {
  1058. VecObjectPtr objlist = selectedSquad->getLiveObjects();
  1059. Int numObjs = objlist.size();
  1060. for (Int i = 0; i < numObjs; ++i)
  1061. {
  1062. TheInGameUI->selectDrawable(objlist[i]->getDrawable());
  1063. }
  1064. }
  1065. }
  1066. }
  1067. m_lastGroupSelTime = now;
  1068. m_lastGroupSelGroup = group;
  1069. }
  1070. disp = DESTROY_MESSAGE;
  1071. break;
  1072. }
  1073. //-----------------------------------------------------------------------------
  1074. case GameMessage::MSG_META_VIEW_TEAM0:
  1075. case GameMessage::MSG_META_VIEW_TEAM1:
  1076. case GameMessage::MSG_META_VIEW_TEAM2:
  1077. case GameMessage::MSG_META_VIEW_TEAM3:
  1078. case GameMessage::MSG_META_VIEW_TEAM4:
  1079. case GameMessage::MSG_META_VIEW_TEAM5:
  1080. case GameMessage::MSG_META_VIEW_TEAM6:
  1081. case GameMessage::MSG_META_VIEW_TEAM7:
  1082. case GameMessage::MSG_META_VIEW_TEAM8:
  1083. case GameMessage::MSG_META_VIEW_TEAM9:
  1084. {
  1085. Int group = t - GameMessage::MSG_META_VIEW_TEAM0;
  1086. if ( group >= 1 && group <= 10 )
  1087. {
  1088. DEBUG_LOG(("META: view team %d\n",group));
  1089. Player *player = ThePlayerList->getLocalPlayer();
  1090. if (player)
  1091. {
  1092. Squad *selectedSquad = player->getHotkeySquad(group);
  1093. if (selectedSquad != NULL)
  1094. {
  1095. VecObjectPtr objlist = selectedSquad->getLiveObjects();
  1096. Int numObjs = objlist.size();
  1097. if (numObjs > 0)
  1098. {
  1099. // if theres someone in the group, center the camera on them.
  1100. TheTacticalView->lookAt( objlist[ numObjs-1 ]->getDrawable()->getPosition() );
  1101. }
  1102. }
  1103. }
  1104. }
  1105. disp = DESTROY_MESSAGE;
  1106. break;
  1107. }
  1108. //-----------------------------------------------------------------------------------------
  1109. case GameMessage::MSG_META_OPTIONS:
  1110. {
  1111. // stop drawing selection feedback, as we're going to ignore the selection.
  1112. m_leftMouseButtonIsDown = FALSE;
  1113. // let this message drop through, the commandXLat will show the options screen itself.
  1114. break;
  1115. }
  1116. #if defined(_DEBUG) || defined(_INTERNAL)
  1117. //-----------------------------------------------------------------------------------------
  1118. case GameMessage::MSG_META_DEMO_TOGGLE_HAND_OF_GOD_MODE:
  1119. {
  1120. if ( !TheGameLogic->isInMultiplayerGame() )
  1121. {
  1122. m_HandOfGodSelectionMode = !m_HandOfGodSelectionMode;
  1123. TheInGameUI->message( UnicodeString( L"Meta Hand-Of-God Mode is %s" ), m_HandOfGodSelectionMode ? L"ON" : L"OFF" );
  1124. disp = DESTROY_MESSAGE;
  1125. }
  1126. break;
  1127. }
  1128. #endif
  1129. #if defined(_ALLOW_DEBUG_CHEATS_IN_RELEASE)
  1130. //-----------------------------------------------------------------------------------------
  1131. case GameMessage::MSG_CHEAT_TOGGLE_HAND_OF_GOD_MODE://NOTICE THE DIFFERENT NAME!!!!!!!!!!!!!!!!!!!!!!!!!!ML
  1132. {
  1133. if ( !TheGameLogic->isInMultiplayerGame() )
  1134. {
  1135. m_HandOfGodSelectionMode = !m_HandOfGodSelectionMode;
  1136. TheInGameUI->message( UnicodeString( L"Hand-Of-God Mode is %s" ), m_HandOfGodSelectionMode ? L"ON" : L"OFF" );
  1137. disp = DESTROY_MESSAGE;
  1138. }
  1139. break;
  1140. }
  1141. #endif
  1142. #if defined(_DEBUG) || defined(_INTERNAL)
  1143. //-----------------------------------------------------------------------------------------
  1144. case GameMessage::MSG_META_DEMO_TOGGLE_HURT_ME_MODE:
  1145. {
  1146. if ( !TheGameLogic->isInMultiplayerGame() )
  1147. {
  1148. TheHurtSelectionMode = !TheHurtSelectionMode;
  1149. TheInGameUI->message( UnicodeString( L"Hurt-Me Mode is %s" ), TheHurtSelectionMode ? L"ON" : L"OFF" );
  1150. disp = DESTROY_MESSAGE;
  1151. }
  1152. break;
  1153. }
  1154. #endif
  1155. #if defined(_DEBUG) || defined(_INTERNAL)
  1156. //-----------------------------------------------------------------------------------------
  1157. case GameMessage::MSG_META_DEMO_DEBUG_SELECTION:
  1158. {
  1159. TheDebugSelectionMode = !TheDebugSelectionMode;
  1160. TheInGameUI->message( UnicodeString( L"Debug-Selected-Item Mode is %s" ), TheDebugSelectionMode ? L"ON" : L"OFF" );
  1161. #ifdef DEBUG_OBJECT_ID_EXISTS
  1162. TheObjectIDToDebug = INVALID_ID;
  1163. #endif
  1164. disp = DESTROY_MESSAGE;
  1165. break;
  1166. }
  1167. #endif
  1168. }
  1169. return disp;
  1170. }
  1171. //Added By Sadullah Nader
  1172. //setDragSelecting(Bool dragSelect)
  1173. //Added to fix the drag selection problem in control bar
  1174. ////////////////////////////////////////////////////////////////////////
  1175. void SelectionTranslator::setDragSelecting(Bool dragSelect)
  1176. {
  1177. m_dragSelecting = dragSelect;
  1178. }
  1179. //setLeftMouseButton(Bool state)
  1180. //Added to turn of Left button down when left button goes up
  1181. ////////////////////////////////////////////////////////////////////////
  1182. void SelectionTranslator::setLeftMouseButton(Bool state)
  1183. {
  1184. m_leftMouseButtonIsDown = state;
  1185. }