LookAtXlat.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690
  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. // 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. if (!TheInGameUI->isSelecting() && !m_isScrolling)
  194. {
  195. setScrolling(SCROLL_RMB);
  196. }
  197. break;
  198. }
  199. //-----------------------------------------------------------------------------
  200. case GameMessage::MSG_RAW_MOUSE_RIGHT_BUTTON_UP:
  201. {
  202. m_lastMouseMoveFrame = TheGameLogic->getFrame();
  203. if (m_scrollType == SCROLL_RMB)
  204. {
  205. stopScrolling();
  206. }
  207. break;
  208. }
  209. //-----------------------------------------------------------------------------
  210. case GameMessage::MSG_RAW_MOUSE_MIDDLE_BUTTON_DOWN:
  211. {
  212. m_lastMouseMoveFrame = TheGameLogic->getFrame();
  213. m_isRotating = true;
  214. m_anchor = msg->getArgument( 0 )->pixel;
  215. m_originalAnchor = msg->getArgument( 0 )->pixel;
  216. m_currentPos = msg->getArgument( 0 )->pixel;
  217. m_timestamp = TheGameClient->getFrame();
  218. break;
  219. }
  220. //-----------------------------------------------------------------------------
  221. case GameMessage::MSG_RAW_MOUSE_MIDDLE_BUTTON_UP:
  222. {
  223. m_lastMouseMoveFrame = TheGameLogic->getFrame();
  224. const UnsignedInt CLICK_DURATION = 5;
  225. const UnsignedInt PIXEL_OFFSET = 5;
  226. m_isRotating = false;
  227. Int dx = m_currentPos.x-m_originalAnchor.x;
  228. if (dx<0) dx = -dx;
  229. Int dy = m_currentPos.y-m_originalAnchor.y;
  230. Bool didMove = dx>PIXEL_OFFSET || dy>PIXEL_OFFSET;
  231. // if middle button is "clicked", reset to "home" orientation
  232. if (!didMove && TheGameClient->getFrame() - m_timestamp < CLICK_DURATION)
  233. {
  234. TheTacticalView->setAngleAndPitchToDefault();
  235. TheTacticalView->setZoomToDefault();
  236. }
  237. break;
  238. }
  239. //-----------------------------------------------------------------------------
  240. case GameMessage::MSG_RAW_MOUSE_POSITION:
  241. {
  242. if (m_currentPos.x != msg->getArgument( 0 )->pixel.x || m_currentPos.y != msg->getArgument( 0 )->pixel.y)
  243. m_lastMouseMoveFrame = TheGameLogic->getFrame();
  244. m_currentPos = msg->getArgument( 0 )->pixel;
  245. UnsignedInt height = TheDisplay->getHeight();
  246. UnsignedInt width = TheDisplay->getWidth();
  247. if (TheInGameUI->getInputEnabled() == FALSE) {
  248. // We don't care how we're scrolling, just stop.
  249. if (m_isScrolling)
  250. stopScrolling();
  251. break;
  252. }
  253. if (!TheGlobalData->m_windowed)
  254. {
  255. if (m_isScrolling)
  256. {
  257. if ( m_scrollType == SCROLL_SCREENEDGE && (m_currentPos.x >= edgeScrollSize && m_currentPos.y >= edgeScrollSize && m_currentPos.y < height-edgeScrollSize && m_currentPos.x < width-edgeScrollSize) )
  258. {
  259. stopScrolling();
  260. }
  261. }
  262. else
  263. {
  264. if ( m_currentPos.x < edgeScrollSize || m_currentPos.y < edgeScrollSize || m_currentPos.y >= height-edgeScrollSize || m_currentPos.x >= width-edgeScrollSize )
  265. {
  266. setScrolling(SCROLL_SCREENEDGE);
  267. }
  268. }
  269. }
  270. // rotate the view
  271. if (m_isRotating)
  272. {
  273. const Real FACTOR = 0.01f;
  274. Real angle = FACTOR * (m_currentPos.x - m_anchor.x);
  275. TheTacticalView->setAngle( TheTacticalView->getAngle() + angle );
  276. m_anchor = msg->getArgument( 0 )->pixel;
  277. }
  278. // rotate the view up/down
  279. if (m_isPitching)
  280. {
  281. const Real FACTOR = 0.01f;
  282. Real angle = FACTOR * (m_currentPos.y - m_anchor.y);
  283. TheTacticalView->setPitch( TheTacticalView->getPitch() + angle );
  284. m_anchor = msg->getArgument( 0 )->pixel;
  285. }
  286. #if defined(_DEBUG) || defined(_INTERNAL)
  287. // adjust the field of view
  288. if (m_isChangingFOV)
  289. {
  290. const Real FACTOR = 0.01f;
  291. Real angle = FACTOR * (m_currentPos.y - m_anchor.y);
  292. TheTacticalView->setFieldOfView( TheTacticalView->getFieldOfView() + angle );
  293. m_anchor = msg->getArgument( 0 )->pixel;
  294. }
  295. #endif
  296. break;
  297. }
  298. //-----------------------------------------------------------------------------
  299. case GameMessage::MSG_RAW_MOUSE_WHEEL:
  300. {
  301. m_lastMouseMoveFrame = TheGameLogic->getFrame();
  302. Int spin = msg->getArgument( 1 )->integer;
  303. if (spin > 0)
  304. {
  305. for ( ; spin > 0; spin--)
  306. TheTacticalView->zoomIn();
  307. }
  308. else
  309. {
  310. for ( ;spin < 0; spin++ )
  311. TheTacticalView->zoomOut();
  312. }
  313. }
  314. //-----------------------------------------------------------------------------
  315. case GameMessage::MSG_META_OPTIONS:
  316. {
  317. // stop the scrolling
  318. stopScrolling();
  319. // let the message drop through, cause we need to process this message for
  320. // selection as well.
  321. break;
  322. }
  323. //-----------------------------------------------------------------------------
  324. case GameMessage::MSG_FRAME_TICK:
  325. {
  326. Coord2D offset = {0, 0};
  327. // If we've been forced to stop scrolling (script action?) then stop
  328. if (m_isScrolling && !TheInGameUI->isScrolling())
  329. {
  330. TheInGameUI->setScrollAmount(offset);
  331. stopScrolling();
  332. }
  333. else
  334. // scroll the view
  335. if (m_isScrolling)
  336. {
  337. switch (m_scrollType)
  338. {
  339. case SCROLL_RMB:
  340. {
  341. if (TheInGameUI->shouldMoveRMBScrollAnchor())
  342. {
  343. Int maxX = TheDisplay->getWidth()/2;
  344. Int maxY = TheDisplay->getHeight()/2;
  345. if (m_currentPos.x + maxX < m_anchor.x)
  346. m_anchor.x = m_currentPos.x + maxX;
  347. else if (m_currentPos.x - maxX > m_anchor.x)
  348. m_anchor.x = m_currentPos.x - maxX;
  349. if (m_currentPos.y + maxY < m_anchor.y)
  350. m_anchor.y = m_currentPos.y + maxY;
  351. else if (m_currentPos.y - maxY > m_anchor.y)
  352. m_anchor.y = m_currentPos.y - maxY;
  353. }
  354. offset.x = TheGlobalData->m_horizontalScrollSpeedFactor * (m_currentPos.x - m_anchor.x);
  355. offset.y = TheGlobalData->m_verticalScrollSpeedFactor * (m_currentPos.y - m_anchor.y);
  356. Coord2D vec;
  357. vec.x = offset.x;
  358. vec.y = offset.y;
  359. vec.normalize();
  360. // Add in the window scroll amount as the minimum.
  361. offset.x += TheGlobalData->m_horizontalScrollSpeedFactor * vec.x * sqr(TheGlobalData->m_keyboardScrollFactor);
  362. offset.y += TheGlobalData->m_verticalScrollSpeedFactor * vec.y * sqr(TheGlobalData->m_keyboardScrollFactor);
  363. }
  364. break;
  365. case SCROLL_KEY:
  366. {
  367. if (scrollDir[DIR_UP])
  368. {
  369. offset.y -= TheGlobalData->m_verticalScrollSpeedFactor * SCROLL_AMT * TheGlobalData->m_keyboardScrollFactor;
  370. }
  371. if (scrollDir[DIR_DOWN])
  372. {
  373. offset.y += TheGlobalData->m_verticalScrollSpeedFactor * SCROLL_AMT * TheGlobalData->m_keyboardScrollFactor;
  374. }
  375. if (scrollDir[DIR_LEFT])
  376. {
  377. offset.x -= TheGlobalData->m_horizontalScrollSpeedFactor * SCROLL_AMT * TheGlobalData->m_keyboardScrollFactor;
  378. }
  379. if (scrollDir[DIR_RIGHT])
  380. {
  381. offset.x += TheGlobalData->m_horizontalScrollSpeedFactor * SCROLL_AMT * TheGlobalData->m_keyboardScrollFactor;
  382. }
  383. }
  384. break;
  385. case SCROLL_SCREENEDGE:
  386. {
  387. UnsignedInt height = TheDisplay->getHeight();
  388. UnsignedInt width = TheDisplay->getWidth();
  389. if (m_currentPos.y < edgeScrollSize)
  390. {
  391. offset.y -= TheGlobalData->m_verticalScrollSpeedFactor * SCROLL_AMT * TheGlobalData->m_keyboardScrollFactor;
  392. }
  393. if (m_currentPos.y >= height-edgeScrollSize)
  394. {
  395. offset.y += TheGlobalData->m_verticalScrollSpeedFactor * SCROLL_AMT * TheGlobalData->m_keyboardScrollFactor;
  396. }
  397. if (m_currentPos.x < edgeScrollSize)
  398. {
  399. offset.x -= TheGlobalData->m_horizontalScrollSpeedFactor * SCROLL_AMT * TheGlobalData->m_keyboardScrollFactor;
  400. }
  401. if (m_currentPos.x >= width-edgeScrollSize)
  402. {
  403. offset.x += TheGlobalData->m_horizontalScrollSpeedFactor * SCROLL_AMT * TheGlobalData->m_keyboardScrollFactor;
  404. }
  405. }
  406. break;
  407. }
  408. TheInGameUI->setScrollAmount(offset);
  409. TheTacticalView->scrollBy( &offset );
  410. }
  411. else //not scrolling so reset amount
  412. TheInGameUI->setScrollAmount(offset);
  413. //if (TheGlobalData->m_saveCameraInReplay /*&& TheRecorder->getMode() != RECORDERMODETYPE_PLAYBACK *//**/&& (TheGameLogic->isInSinglePlayerGame() || TheGameLogic->isInSkirmishGame())/**/)
  414. //if (TheGlobalData->m_saveCameraInReplay && (TheGameLogic->isInMultiplayerGame() || TheGameLogic->isInSinglePlayerGame() || TheGameLogic->isInSkirmishGame()))
  415. if (TheGlobalData->m_saveCameraInReplay && (TheGameLogic->isInSinglePlayerGame() || TheGameLogic->isInSkirmishGame()))
  416. {
  417. ViewLocation currentView;
  418. TheTacticalView->getLocation(&currentView);
  419. GameMessage *msg = TheMessageStream->appendMessage( GameMessage::MSG_SET_REPLAY_CAMERA );
  420. msg->appendLocationArgument( currentView.m_pos );
  421. msg->appendRealArgument( currentView.m_angle );
  422. msg->appendRealArgument( currentView.m_pitch );
  423. msg->appendRealArgument( currentView.m_zoom );
  424. msg->appendIntegerArgument( (Int)TheMouse->getMouseCursor() );
  425. msg->appendPixelArgument( m_currentPos );
  426. }
  427. break;
  428. }
  429. // ------------------------------------------------------------------------
  430. #if defined(_DEBUG) || defined(_INTERNAL)
  431. case GameMessage::MSG_META_DEMO_BEGIN_ADJUST_PITCH:
  432. {
  433. DEBUG_ASSERTCRASH(!m_isPitching, ("hmm, mismatched m_isPitching"));
  434. m_isPitching = true;
  435. disp = DESTROY_MESSAGE;
  436. break;
  437. }
  438. #endif // #if defined(_DEBUG) || defined(_INTERNAL)
  439. // ------------------------------------------------------------------------
  440. #if defined(_DEBUG) || defined(_INTERNAL)
  441. case GameMessage::MSG_META_DEMO_END_ADJUST_PITCH:
  442. {
  443. DEBUG_ASSERTCRASH(m_isPitching, ("hmm, mismatched m_isPitching"));
  444. m_isPitching = false;
  445. disp = DESTROY_MESSAGE;
  446. break;
  447. }
  448. #endif // #if defined(_DEBUG) || defined(_INTERNAL)
  449. // ------------------------------------------------------------------------
  450. #if defined(_DEBUG) || defined(_INTERNAL)
  451. case GameMessage::MSG_META_DEMO_DESHROUD:
  452. {
  453. ThePartitionManager->revealMapForPlayerPermanently( ThePlayerList->getLocalPlayer()->getPlayerIndex() );
  454. break;
  455. }
  456. #endif // #if defined(_DEBUG) || defined(_INTERNAL)
  457. // ------------------------------------------------------------------------
  458. #if defined(_ALLOW_DEBUG_CHEATS_IN_RELEASE)
  459. case GameMessage::MSG_CHEAT_DESHROUD:
  460. {
  461. if (!TheGameLogic->isInMultiplayerGame())
  462. {
  463. ThePartitionManager->revealMapForPlayerPermanently( ThePlayerList->getLocalPlayer()->getPlayerIndex() );
  464. }
  465. break;
  466. }
  467. #endif // #if defined(_ALLOW_DEBUG_CHEATS_IN_RELEASE)
  468. // ------------------------------------------------------------------------
  469. #if defined(_DEBUG) || defined(_INTERNAL)
  470. case GameMessage::MSG_META_DEMO_ENSHROUD:
  471. {
  472. // Need to first undo the permanent Look laid down by DEMO_DESHROUD, then blast a shroud dollop.
  473. ThePartitionManager->undoRevealMapForPlayerPermanently( ThePlayerList->getLocalPlayer()->getPlayerIndex() );
  474. ThePartitionManager->shroudMapForPlayer( ThePlayerList->getLocalPlayer()->getPlayerIndex() );
  475. break;
  476. }
  477. #endif // #if defined(_DEBUG) || defined(_INTERNAL)
  478. // ------------------------------------------------------------------------
  479. #if defined(_DEBUG) || defined(_INTERNAL)
  480. case GameMessage::MSG_META_DEMO_BEGIN_ADJUST_FOV:
  481. {
  482. //DEBUG_ASSERTCRASH(!m_isChangingFOV, ("hmm, mismatched m_isChangingFOV"));
  483. m_isChangingFOV = true;
  484. m_anchor = m_currentPos;
  485. break;
  486. }
  487. #endif // #if defined(_DEBUG) || defined(_INTERNAL)
  488. // ------------------------------------------------------------------------
  489. #if defined(_DEBUG) || defined(_INTERNAL)
  490. case GameMessage::MSG_META_DEMO_END_ADJUST_FOV:
  491. {
  492. // DEBUG_ASSERTCRASH(m_isChangingFOV, ("hmm, mismatched m_isChangingFOV"));
  493. m_isChangingFOV = false;
  494. break;
  495. }
  496. #endif // #if defined(_DEBUG) || defined(_INTERNAL)
  497. //-----------------------------------------------------------------------------------------
  498. case GameMessage::MSG_META_SAVE_VIEW1:
  499. case GameMessage::MSG_META_SAVE_VIEW2:
  500. case GameMessage::MSG_META_SAVE_VIEW3:
  501. case GameMessage::MSG_META_SAVE_VIEW4:
  502. case GameMessage::MSG_META_SAVE_VIEW5:
  503. case GameMessage::MSG_META_SAVE_VIEW6:
  504. case GameMessage::MSG_META_SAVE_VIEW7:
  505. case GameMessage::MSG_META_SAVE_VIEW8:
  506. {
  507. Int slot = t - GameMessage::MSG_META_SAVE_VIEW1 + 1;
  508. if ( slot > 0 && slot <= MAX_VIEW_LOCS )
  509. {
  510. TheTacticalView->getLocation( &m_viewLocation[slot-1] );
  511. UnicodeString msg;
  512. msg.format( TheGameText->fetch( "GUI:BookmarkXSet" ), slot );
  513. TheInGameUI->message( msg );
  514. }
  515. disp = DESTROY_MESSAGE;
  516. break;
  517. }
  518. //-----------------------------------------------------------------------------------------
  519. case GameMessage::MSG_META_VIEW_VIEW1:
  520. case GameMessage::MSG_META_VIEW_VIEW2:
  521. case GameMessage::MSG_META_VIEW_VIEW3:
  522. case GameMessage::MSG_META_VIEW_VIEW4:
  523. case GameMessage::MSG_META_VIEW_VIEW5:
  524. case GameMessage::MSG_META_VIEW_VIEW6:
  525. case GameMessage::MSG_META_VIEW_VIEW7:
  526. case GameMessage::MSG_META_VIEW_VIEW8:
  527. {
  528. Int slot = t - GameMessage::MSG_META_VIEW_VIEW1 + 1;
  529. if ( slot > 0 && slot <= MAX_VIEW_LOCS )
  530. {
  531. TheTacticalView->setLocation( &m_viewLocation[slot-1] );
  532. }
  533. disp = DESTROY_MESSAGE;
  534. break;
  535. }
  536. //-----------------------------------------------------------------------------
  537. #if defined(_DEBUG) || defined(_INTERNAL)
  538. case GameMessage::MSG_META_DEMO_LOCK_CAMERA_TO_PLANES:
  539. {
  540. Drawable *first = NULL;
  541. if (m_lastPlaneID)
  542. first = TheGameClient->findDrawableByID( m_lastPlaneID );
  543. if (first == NULL)
  544. first = TheGameClient->firstDrawable();
  545. if (first)
  546. {
  547. Drawable *d = first;
  548. Bool done = false;
  549. while(!done)
  550. {
  551. // get next Drawable, wrapping around to head of list if necessary
  552. d = d->getNextDrawable();
  553. if (d == NULL)
  554. d = TheGameClient->firstDrawable();
  555. // if we've found an airborne object, lock onto it
  556. // "isAboveTerrain" only indicates that we are currently in the air, but that
  557. // could be the case if we are a buggy jumping a hill, or a unit being paradropped.
  558. // the right thing would be to look at the locomotors.
  559. // so this isn't really right, but will suffice for demo purposes.
  560. if (d->getObject() && d->getObject()->isAboveTerrain() )
  561. {
  562. Bool doLock = true;
  563. // but don't lock onto projectiles
  564. ProjectileUpdateInterface* pui = NULL;
  565. for (BehaviorModule** u = d->getObject()->getBehaviorModules(); *u; ++u)
  566. {
  567. if ((pui = (*u)->getProjectileUpdateInterface()) != NULL)
  568. {
  569. doLock = false;
  570. break;
  571. }
  572. }
  573. if (doLock)
  574. {
  575. TheTacticalView->setCameraLock( d->getObject()->getID() );
  576. m_lastPlaneID = d->getID();
  577. done = true;
  578. break;
  579. }
  580. } // if airborne found
  581. // if we're back to the first, quit
  582. if (d == first)
  583. break;
  584. } // while
  585. } // end plane lock
  586. disp = DESTROY_MESSAGE;
  587. break;
  588. }
  589. #endif // #if defined(_DEBUG) || defined(_INTERNAL)
  590. } // end switch
  591. return disp;
  592. } // end LookAtTranslator
  593. void LookAtTranslator::resetModes()
  594. {
  595. m_isScrolling = FALSE;
  596. m_isRotating = FALSE;
  597. m_isPitching = FALSE;
  598. m_isChangingFOV = FALSE;
  599. }