LookAtXlat.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681
  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. // LookAtXlat.cpp
  24. // Translate raw input events into camera movement commands
  25. // Author: Michael S. Booth, April 2001
  26. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  27. #include "windows.h"
  28. #include "Common/GameType.h"
  29. #include "Common/MessageStream.h"
  30. #include "Common/Player.h"
  31. #include "Common/PlayerList.h"
  32. #include "Common/Recorder.h"
  33. #include "Common/StatsCollector.h"
  34. #include "GameLogic/Object.h"
  35. #include "GameLogic/PartitionManager.h"
  36. #include "GameClient/Display.h"
  37. #include "GameClient/GameText.h"
  38. #include "GameClient/Mouse.h"
  39. #include "GameClient/Shell.h"
  40. #include "GameClient/GameClient.h"
  41. #include "GameClient/KeyDefs.h"
  42. #include "GameClient/View.h"
  43. #include "GameClient/Drawable.h"
  44. #include "GameClient/LookAtXlat.h"
  45. #include "GameLogic/Module/UpdateModule.h"
  46. #include "GameLogic/GameLogic.h"
  47. #include "Common/GlobalData.h" // for camera pitch angle only
  48. LookAtTranslator *TheLookAtTranslator = NULL;
  49. static enum
  50. {
  51. DIR_UP = 0,
  52. DIR_DOWN,
  53. DIR_LEFT,
  54. DIR_RIGHT
  55. };
  56. static Bool scrollDir[4] = { false, false, false, false };
  57. Int SCROLL_AMT = 100;
  58. static const Int edgeScrollSize = 3;
  59. static Mouse::MouseCursor prevCursor = Mouse::ARROW;
  60. //-----------------------------------------------------------------------------
  61. void LookAtTranslator::setScrolling(Int x)
  62. {
  63. if (!TheInGameUI->getInputEnabled())
  64. return;
  65. prevCursor = TheMouse->getMouseCursor();
  66. m_isScrolling = true;
  67. TheInGameUI->setScrolling( TRUE );
  68. TheTacticalView->setMouseLock( TRUE );
  69. m_scrollType = x;
  70. if(TheStatsCollector)
  71. TheStatsCollector->startScrollTime();
  72. }
  73. //-----------------------------------------------------------------------------
  74. void LookAtTranslator::stopScrolling( void )
  75. {
  76. m_isScrolling = false;
  77. TheInGameUI->setScrolling( FALSE );
  78. TheTacticalView->setMouseLock( FALSE );
  79. TheMouse->setCursor(prevCursor);
  80. m_scrollType = SCROLL_NONE;
  81. // if we have a stats collectore increment the stats
  82. if(TheStatsCollector)
  83. TheStatsCollector->endScrollTime();
  84. }
  85. //-----------------------------------------------------------------------------
  86. LookAtTranslator::LookAtTranslator() :
  87. m_isScrolling(false),
  88. m_isRotating(false),
  89. m_isPitching(false),
  90. m_isChangingFOV(false),
  91. m_timestamp(0),
  92. m_lastPlaneID(INVALID_DRAWABLE_ID),
  93. m_lastMouseMoveFrame(0),
  94. m_scrollType(SCROLL_NONE)
  95. {
  96. //Added By Sadullah Nader
  97. //Initializations misssing and needed
  98. m_anchor.x = m_anchor.y = 0;
  99. m_currentPos.x = m_currentPos.y = 0;
  100. m_originalAnchor.x = m_originalAnchor.y = 0;
  101. //
  102. DEBUG_ASSERTCRASH(!TheLookAtTranslator, ("Already have a LookAtTranslator - why do you need two?"));
  103. TheLookAtTranslator = this;
  104. }
  105. //-----------------------------------------------------------------------------
  106. LookAtTranslator::~LookAtTranslator()
  107. {
  108. if (TheLookAtTranslator == this)
  109. TheLookAtTranslator = NULL;
  110. }
  111. const ICoord2D* LookAtTranslator::getRMBScrollAnchor(void)
  112. {
  113. if (m_isScrolling && m_scrollType == SCROLL_RMB)
  114. {
  115. return &m_anchor;
  116. }
  117. return NULL;
  118. }
  119. Bool LookAtTranslator::hasMouseMovedRecently( void )
  120. {
  121. if (m_lastMouseMoveFrame > TheGameLogic->getFrame())
  122. m_lastMouseMoveFrame = 0; // reset for new game
  123. if (m_lastMouseMoveFrame + LOGICFRAMES_PER_SECOND < TheGameLogic->getFrame())
  124. return false;
  125. return true;
  126. }
  127. void LookAtTranslator::setCurrentPos( const ICoord2D& pos )
  128. {
  129. m_currentPos = pos;
  130. }
  131. //-----------------------------------------------------------------------------
  132. /**
  133. * The LookAt Translator is responsible for camera movements. It is directly responsible for
  134. * right mouse button scrolling, and CTRL-<F key> bookmarking. It also responds to certain
  135. * LOOKAT message on the message stream.
  136. */
  137. GameMessageDisposition LookAtTranslator::translateGameMessage(const GameMessage *msg)
  138. {
  139. GameMessageDisposition disp = KEEP_MESSAGE;
  140. GameMessage::Type t = msg->getType();
  141. switch (t)
  142. {
  143. //-----------------------------------------------------------------------------
  144. case GameMessage::MSG_RAW_KEY_DOWN:
  145. case GameMessage::MSG_RAW_KEY_UP:
  146. {
  147. // get key and state from args
  148. UnsignedByte key = msg->getArgument( 0 )->integer;
  149. UnsignedByte state = msg->getArgument( 1 )->integer;
  150. Bool isPressed = !(BitTest( state, KEY_STATE_UP ));
  151. if (TheShell && TheShell->isShellActive())
  152. break;
  153. switch (key)
  154. {
  155. case KEY_UP:
  156. scrollDir[DIR_UP] = isPressed;
  157. break;
  158. case KEY_DOWN:
  159. scrollDir[DIR_DOWN] = isPressed;
  160. break;
  161. case KEY_LEFT:
  162. scrollDir[DIR_LEFT] = isPressed;
  163. break;
  164. case KEY_RIGHT:
  165. scrollDir[DIR_RIGHT] = isPressed;
  166. break;
  167. }
  168. if (TheInGameUI->isSelecting() || (m_isScrolling && m_scrollType != SCROLL_KEY))
  169. break;
  170. // see if we need to start/stop scrolling
  171. Int numDirs = 0;
  172. for (Int i=0; i<4; ++i)
  173. {
  174. if (scrollDir[i])
  175. numDirs++;
  176. }
  177. if (numDirs && !m_isScrolling)
  178. {
  179. setScrolling( SCROLL_KEY );
  180. }
  181. else if (!numDirs && m_isScrolling)
  182. {
  183. stopScrolling();
  184. }
  185. break;
  186. }
  187. //-----------------------------------------------------------------------------
  188. case GameMessage::MSG_RAW_MOUSE_RIGHT_BUTTON_DOWN:
  189. {
  190. m_lastMouseMoveFrame = TheGameLogic->getFrame();
  191. m_anchor = msg->getArgument( 0 )->pixel;
  192. m_currentPos = msg->getArgument( 0 )->pixel;
  193. // disable mouse scrolling in alternate mouse mode, per Harvard 7/15/03
  194. if (!TheGlobalData->m_useAlternateMouse && !TheInGameUI->isSelecting() && !m_isScrolling)
  195. {
  196. setScrolling(SCROLL_RMB);
  197. }
  198. break;
  199. }
  200. //-----------------------------------------------------------------------------
  201. case GameMessage::MSG_RAW_MOUSE_RIGHT_BUTTON_UP:
  202. {
  203. m_lastMouseMoveFrame = TheGameLogic->getFrame();
  204. if (m_scrollType == SCROLL_RMB)
  205. {
  206. stopScrolling();
  207. }
  208. break;
  209. }
  210. //-----------------------------------------------------------------------------
  211. case GameMessage::MSG_RAW_MOUSE_MIDDLE_BUTTON_DOWN:
  212. {
  213. m_lastMouseMoveFrame = TheGameLogic->getFrame();
  214. m_isRotating = true;
  215. m_anchor = msg->getArgument( 0 )->pixel;
  216. m_originalAnchor = msg->getArgument( 0 )->pixel;
  217. m_currentPos = msg->getArgument( 0 )->pixel;
  218. m_timestamp = TheGameClient->getFrame();
  219. break;
  220. }
  221. //-----------------------------------------------------------------------------
  222. case GameMessage::MSG_RAW_MOUSE_MIDDLE_BUTTON_UP:
  223. {
  224. m_lastMouseMoveFrame = TheGameLogic->getFrame();
  225. const UnsignedInt CLICK_DURATION = 5;
  226. const UnsignedInt PIXEL_OFFSET = 5;
  227. m_isRotating = false;
  228. Int dx = m_currentPos.x-m_originalAnchor.x;
  229. if (dx<0) dx = -dx;
  230. Int dy = m_currentPos.y-m_originalAnchor.y;
  231. Bool didMove = dx>PIXEL_OFFSET || dy>PIXEL_OFFSET;
  232. // if middle button is "clicked", reset to "home" orientation
  233. if (!didMove && TheGameClient->getFrame() - m_timestamp < CLICK_DURATION)
  234. {
  235. TheTacticalView->setAngleAndPitchToDefault();
  236. TheTacticalView->setZoomToDefault();
  237. }
  238. break;
  239. }
  240. //-----------------------------------------------------------------------------
  241. case GameMessage::MSG_RAW_MOUSE_POSITION:
  242. {
  243. if (m_currentPos.x != msg->getArgument( 0 )->pixel.x || m_currentPos.y != msg->getArgument( 0 )->pixel.y)
  244. m_lastMouseMoveFrame = TheGameLogic->getFrame();
  245. m_currentPos = msg->getArgument( 0 )->pixel;
  246. UnsignedInt height = TheDisplay->getHeight();
  247. UnsignedInt width = TheDisplay->getWidth();
  248. if (TheInGameUI->getInputEnabled() == FALSE) {
  249. // We don't care how we're scrolling, just stop.
  250. if (m_isScrolling)
  251. stopScrolling();
  252. break;
  253. }
  254. if (!TheGlobalData->m_windowed)
  255. {
  256. if (m_isScrolling)
  257. {
  258. if ( m_scrollType == SCROLL_SCREENEDGE && (m_currentPos.x >= edgeScrollSize && m_currentPos.y >= edgeScrollSize && m_currentPos.y < height-edgeScrollSize && m_currentPos.x < width-edgeScrollSize) )
  259. {
  260. stopScrolling();
  261. }
  262. }
  263. else
  264. {
  265. if ( m_currentPos.x < edgeScrollSize || m_currentPos.y < edgeScrollSize || m_currentPos.y >= height-edgeScrollSize || m_currentPos.x >= width-edgeScrollSize )
  266. {
  267. setScrolling(SCROLL_SCREENEDGE);
  268. }
  269. }
  270. }
  271. // rotate the view
  272. if (m_isRotating)
  273. {
  274. const Real FACTOR = 0.01f;
  275. Real angle = FACTOR * (m_currentPos.x - m_anchor.x);
  276. TheTacticalView->setAngle( TheTacticalView->getAngle() + angle );
  277. m_anchor = msg->getArgument( 0 )->pixel;
  278. }
  279. // rotate the view up/down
  280. if (m_isPitching)
  281. {
  282. const Real FACTOR = 0.01f;
  283. Real angle = FACTOR * (m_currentPos.y - m_anchor.y);
  284. TheTacticalView->setPitch( TheTacticalView->getPitch() + angle );
  285. m_anchor = msg->getArgument( 0 )->pixel;
  286. }
  287. #if defined(_DEBUG) || defined(_INTERNAL)
  288. // adjust the field of view
  289. if (m_isChangingFOV)
  290. {
  291. const Real FACTOR = 0.01f;
  292. Real angle = FACTOR * (m_currentPos.y - m_anchor.y);
  293. TheTacticalView->setFieldOfView( TheTacticalView->getFieldOfView() + angle );
  294. m_anchor = msg->getArgument( 0 )->pixel;
  295. }
  296. #endif
  297. break;
  298. }
  299. //-----------------------------------------------------------------------------
  300. case GameMessage::MSG_RAW_MOUSE_WHEEL:
  301. {
  302. m_lastMouseMoveFrame = TheGameLogic->getFrame();
  303. Int spin = msg->getArgument( 1 )->integer;
  304. if (spin > 0)
  305. {
  306. for ( ; spin > 0; spin--)
  307. TheTacticalView->zoomIn();
  308. }
  309. else
  310. {
  311. for ( ;spin < 0; spin++ )
  312. TheTacticalView->zoomOut();
  313. }
  314. }
  315. //-----------------------------------------------------------------------------
  316. case GameMessage::MSG_META_OPTIONS:
  317. {
  318. // stop the scrolling
  319. stopScrolling();
  320. // let the message drop through, cause we need to process this message for
  321. // selection as well.
  322. break;
  323. }
  324. //-----------------------------------------------------------------------------
  325. case GameMessage::MSG_FRAME_TICK:
  326. {
  327. Coord2D offset = {0, 0};
  328. // If we've been forced to stop scrolling (script action?) then stop
  329. if (m_isScrolling && !TheInGameUI->isScrolling())
  330. {
  331. TheInGameUI->setScrollAmount(offset);
  332. stopScrolling();
  333. }
  334. else
  335. // scroll the view
  336. if (m_isScrolling)
  337. {
  338. switch (m_scrollType)
  339. {
  340. case SCROLL_RMB:
  341. {
  342. if (TheInGameUI->shouldMoveRMBScrollAnchor())
  343. {
  344. Int maxX = TheDisplay->getWidth()/2;
  345. Int maxY = TheDisplay->getHeight()/2;
  346. if (m_currentPos.x + maxX < m_anchor.x)
  347. m_anchor.x = m_currentPos.x + maxX;
  348. else if (m_currentPos.x - maxX > m_anchor.x)
  349. m_anchor.x = m_currentPos.x - maxX;
  350. if (m_currentPos.y + maxY < m_anchor.y)
  351. m_anchor.y = m_currentPos.y + maxY;
  352. else if (m_currentPos.y - maxY > m_anchor.y)
  353. m_anchor.y = m_currentPos.y - maxY;
  354. }
  355. offset.x = TheGlobalData->m_horizontalScrollSpeedFactor * (m_currentPos.x - m_anchor.x);
  356. offset.y = TheGlobalData->m_verticalScrollSpeedFactor * (m_currentPos.y - m_anchor.y);
  357. Coord2D vec;
  358. vec.x = offset.x;
  359. vec.y = offset.y;
  360. vec.normalize();
  361. // Add in the window scroll amount as the minimum.
  362. offset.x += TheGlobalData->m_horizontalScrollSpeedFactor * vec.x * sqr(TheGlobalData->m_keyboardScrollFactor);
  363. offset.y += TheGlobalData->m_verticalScrollSpeedFactor * vec.y * sqr(TheGlobalData->m_keyboardScrollFactor);
  364. }
  365. break;
  366. case SCROLL_KEY:
  367. {
  368. if (scrollDir[DIR_UP])
  369. {
  370. offset.y -= TheGlobalData->m_verticalScrollSpeedFactor * SCROLL_AMT * TheGlobalData->m_keyboardScrollFactor;
  371. }
  372. if (scrollDir[DIR_DOWN])
  373. {
  374. offset.y += TheGlobalData->m_verticalScrollSpeedFactor * SCROLL_AMT * TheGlobalData->m_keyboardScrollFactor;
  375. }
  376. if (scrollDir[DIR_LEFT])
  377. {
  378. offset.x -= TheGlobalData->m_horizontalScrollSpeedFactor * SCROLL_AMT * TheGlobalData->m_keyboardScrollFactor;
  379. }
  380. if (scrollDir[DIR_RIGHT])
  381. {
  382. offset.x += TheGlobalData->m_horizontalScrollSpeedFactor * SCROLL_AMT * TheGlobalData->m_keyboardScrollFactor;
  383. }
  384. }
  385. break;
  386. case SCROLL_SCREENEDGE:
  387. {
  388. UnsignedInt height = TheDisplay->getHeight();
  389. UnsignedInt width = TheDisplay->getWidth();
  390. if (m_currentPos.y < edgeScrollSize)
  391. {
  392. offset.y -= TheGlobalData->m_verticalScrollSpeedFactor * SCROLL_AMT * TheGlobalData->m_keyboardScrollFactor;
  393. }
  394. if (m_currentPos.y >= height-edgeScrollSize)
  395. {
  396. offset.y += TheGlobalData->m_verticalScrollSpeedFactor * SCROLL_AMT * TheGlobalData->m_keyboardScrollFactor;
  397. }
  398. if (m_currentPos.x < edgeScrollSize)
  399. {
  400. offset.x -= TheGlobalData->m_horizontalScrollSpeedFactor * SCROLL_AMT * TheGlobalData->m_keyboardScrollFactor;
  401. }
  402. if (m_currentPos.x >= width-edgeScrollSize)
  403. {
  404. offset.x += TheGlobalData->m_horizontalScrollSpeedFactor * SCROLL_AMT * TheGlobalData->m_keyboardScrollFactor;
  405. }
  406. }
  407. break;
  408. }
  409. TheInGameUI->setScrollAmount(offset);
  410. TheTacticalView->scrollBy( &offset );
  411. }
  412. else //not scrolling so reset amount
  413. TheInGameUI->setScrollAmount(offset);
  414. #if !defined(_PLAYTEST)
  415. //if (TheGlobalData->m_saveCameraInReplay /*&& TheRecorder->getMode() != RECORDERMODETYPE_PLAYBACK *//**/&& (TheGameLogic->isInSinglePlayerGame() || TheGameLogic->isInSkirmishGame())/**/)
  416. //if (TheGlobalData->m_saveCameraInReplay && (TheGameLogic->isInMultiplayerGame() || TheGameLogic->isInSinglePlayerGame() || TheGameLogic->isInSkirmishGame()))
  417. if (TheGlobalData->m_saveCameraInReplay && (TheGameLogic->isInSinglePlayerGame() || TheGameLogic->isInSkirmishGame()))
  418. {
  419. ViewLocation currentView;
  420. TheTacticalView->getLocation(&currentView);
  421. GameMessage *msg = TheMessageStream->appendMessage( GameMessage::MSG_SET_REPLAY_CAMERA );
  422. msg->appendLocationArgument( currentView.m_pos );
  423. msg->appendRealArgument( currentView.m_angle );
  424. msg->appendRealArgument( currentView.m_pitch );
  425. msg->appendRealArgument( currentView.m_zoom );
  426. msg->appendIntegerArgument( (Int)TheMouse->getMouseCursor() );
  427. msg->appendPixelArgument( m_currentPos );
  428. }
  429. #endif
  430. break;
  431. }
  432. // ------------------------------------------------------------------------
  433. #if defined(_DEBUG) || defined(_INTERNAL)
  434. case GameMessage::MSG_META_DEMO_BEGIN_ADJUST_PITCH:
  435. {
  436. DEBUG_ASSERTCRASH(!m_isPitching, ("hmm, mismatched m_isPitching"));
  437. m_isPitching = true;
  438. disp = DESTROY_MESSAGE;
  439. break;
  440. }
  441. #endif // #if defined(_DEBUG) || defined(_INTERNAL)
  442. // ------------------------------------------------------------------------
  443. #if defined(_DEBUG) || defined(_INTERNAL)
  444. case GameMessage::MSG_META_DEMO_END_ADJUST_PITCH:
  445. {
  446. DEBUG_ASSERTCRASH(m_isPitching, ("hmm, mismatched m_isPitching"));
  447. m_isPitching = false;
  448. disp = DESTROY_MESSAGE;
  449. break;
  450. }
  451. #endif // #if defined(_DEBUG) || defined(_INTERNAL)
  452. // ------------------------------------------------------------------------
  453. #if defined(_DEBUG) || defined(_INTERNAL)
  454. case GameMessage::MSG_META_DEMO_DESHROUD:
  455. {
  456. ThePartitionManager->revealMapForPlayerPermanently( ThePlayerList->getLocalPlayer()->getPlayerIndex() );
  457. break;
  458. }
  459. #endif // #if defined(_DEBUG) || defined(_INTERNAL)
  460. // ------------------------------------------------------------------------
  461. #if defined(_DEBUG) || defined(_INTERNAL)
  462. case GameMessage::MSG_META_DEMO_ENSHROUD:
  463. {
  464. // Need to first undo the permanent Look laid down by DEMO_DESHROUD, then blast a shroud dollop.
  465. ThePartitionManager->undoRevealMapForPlayerPermanently( ThePlayerList->getLocalPlayer()->getPlayerIndex() );
  466. ThePartitionManager->shroudMapForPlayer( ThePlayerList->getLocalPlayer()->getPlayerIndex() );
  467. break;
  468. }
  469. #endif // #if defined(_DEBUG) || defined(_INTERNAL)
  470. // ------------------------------------------------------------------------
  471. #if defined(_DEBUG) || defined(_INTERNAL)
  472. case GameMessage::MSG_META_DEMO_BEGIN_ADJUST_FOV:
  473. {
  474. DEBUG_ASSERTCRASH(!m_isChangingFOV, ("hmm, mismatched m_isChangingFOV"));
  475. m_isChangingFOV = true;
  476. m_anchor = m_currentPos;
  477. break;
  478. }
  479. #endif // #if defined(_DEBUG) || defined(_INTERNAL)
  480. // ------------------------------------------------------------------------
  481. #if defined(_DEBUG) || defined(_INTERNAL)
  482. case GameMessage::MSG_META_DEMO_END_ADJUST_FOV:
  483. {
  484. DEBUG_ASSERTCRASH(m_isChangingFOV, ("hmm, mismatched m_isChangingFOV"));
  485. m_isChangingFOV = false;
  486. break;
  487. }
  488. #endif // #if defined(_DEBUG) || defined(_INTERNAL)
  489. //-----------------------------------------------------------------------------------------
  490. case GameMessage::MSG_META_SAVE_VIEW1:
  491. case GameMessage::MSG_META_SAVE_VIEW2:
  492. case GameMessage::MSG_META_SAVE_VIEW3:
  493. case GameMessage::MSG_META_SAVE_VIEW4:
  494. case GameMessage::MSG_META_SAVE_VIEW5:
  495. case GameMessage::MSG_META_SAVE_VIEW6:
  496. case GameMessage::MSG_META_SAVE_VIEW7:
  497. case GameMessage::MSG_META_SAVE_VIEW8:
  498. {
  499. Int slot = t - GameMessage::MSG_META_SAVE_VIEW1 + 1;
  500. if ( slot > 0 && slot <= MAX_VIEW_LOCS )
  501. {
  502. TheTacticalView->getLocation( &m_viewLocation[slot-1] );
  503. UnicodeString msg;
  504. msg.format( TheGameText->fetch( "GUI:BookmarkXSet" ), slot );
  505. TheInGameUI->message( msg );
  506. }
  507. disp = DESTROY_MESSAGE;
  508. break;
  509. }
  510. //-----------------------------------------------------------------------------------------
  511. case GameMessage::MSG_META_VIEW_VIEW1:
  512. case GameMessage::MSG_META_VIEW_VIEW2:
  513. case GameMessage::MSG_META_VIEW_VIEW3:
  514. case GameMessage::MSG_META_VIEW_VIEW4:
  515. case GameMessage::MSG_META_VIEW_VIEW5:
  516. case GameMessage::MSG_META_VIEW_VIEW6:
  517. case GameMessage::MSG_META_VIEW_VIEW7:
  518. case GameMessage::MSG_META_VIEW_VIEW8:
  519. {
  520. Int slot = t - GameMessage::MSG_META_VIEW_VIEW1 + 1;
  521. if ( slot > 0 && slot <= MAX_VIEW_LOCS )
  522. {
  523. TheTacticalView->setLocation( &m_viewLocation[slot-1] );
  524. }
  525. disp = DESTROY_MESSAGE;
  526. break;
  527. }
  528. //-----------------------------------------------------------------------------
  529. #if defined(_DEBUG) || defined(_INTERNAL)
  530. case GameMessage::MSG_META_DEMO_LOCK_CAMERA_TO_PLANES:
  531. {
  532. Drawable *first = NULL;
  533. if (m_lastPlaneID)
  534. first = TheGameClient->findDrawableByID( m_lastPlaneID );
  535. if (first == NULL)
  536. first = TheGameClient->firstDrawable();
  537. if (first)
  538. {
  539. Drawable *d = first;
  540. Bool done = false;
  541. while(!done)
  542. {
  543. // get next Drawable, wrapping around to head of list if necessary
  544. d = d->getNextDrawable();
  545. if (d == NULL)
  546. d = TheGameClient->firstDrawable();
  547. // if we've found an airborne object, lock onto it
  548. // "isAboveTerrain" only indicates that we are currently in the air, but that
  549. // could be the case if we are a buggy jumping a hill, or a unit being paradropped.
  550. // the right thing would be to look at the locomotors.
  551. // so this isn't really right, but will suffice for demo purposes.
  552. if (d->getObject() && d->getObject()->isAboveTerrain() )
  553. {
  554. Bool doLock = true;
  555. // but don't lock onto projectiles
  556. ProjectileUpdateInterface* pui = NULL;
  557. for (BehaviorModule** u = d->getObject()->getBehaviorModules(); *u; ++u)
  558. {
  559. if ((pui = (*u)->getProjectileUpdateInterface()) != NULL)
  560. {
  561. doLock = false;
  562. break;
  563. }
  564. }
  565. if (doLock)
  566. {
  567. TheTacticalView->setCameraLock( d->getObject()->getID() );
  568. m_lastPlaneID = d->getID();
  569. done = true;
  570. break;
  571. }
  572. } // if airborne found
  573. // if we're back to the first, quit
  574. if (d == first)
  575. break;
  576. } // while
  577. } // end plane lock
  578. disp = DESTROY_MESSAGE;
  579. break;
  580. }
  581. #endif // #if defined(_DEBUG) || defined(_INTERNAL)
  582. } // end switch
  583. return disp;
  584. } // end LookAtTranslator
  585. void LookAtTranslator::resetModes()
  586. {
  587. m_isScrolling = FALSE;
  588. m_isRotating = FALSE;
  589. m_isPitching = FALSE;
  590. m_isChangingFOV = FALSE;
  591. }