Radar.cpp 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546
  1. /*
  2. ** Command & Conquer Generals(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: Radar.cpp ////////////////////////////////////////////////////////////////////////////////
  24. // Author: Colin Day, January 2002
  25. // Desc: Radar logic implementation
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  28. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  29. #include "Common/GameAudio.h"
  30. #include "Common/GameState.h"
  31. #include "Common/MiscAudio.h"
  32. #include "Common/Radar.h"
  33. #include "Common/Player.h"
  34. #include "Common/PlayerList.h"
  35. #include "Common/ThingTemplate.h"
  36. #include "Common/GlobalData.h"
  37. #include "Common/Xfer.h"
  38. #include "GameClient/Drawable.h"
  39. #include "GameClient/Eva.h"
  40. #include "GameClient/GameWindowManager.h"
  41. #include "GameClient/InGameUI.h"
  42. #include "GameClient/ControlBar.h"
  43. #include "GameLogic/GameLogic.h"
  44. #include "GameLogic/Object.h"
  45. #include "GameLogic/PartitionManager.h"
  46. #include "GameLogic/TerrainLogic.h"
  47. #include "GameLogic/Module/ContainModule.h"
  48. #include "GameLogic/Module/StealthUpdate.h"
  49. #ifdef _INTERNAL
  50. // for occasional debugging...
  51. //#pragma optimize("", off)
  52. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  53. #endif
  54. // GLOBALS ////////////////////////////////////////////////////////////////////////////////////////
  55. Radar *TheRadar = NULL; ///< the radar global singleton
  56. // PRIVATE ////////////////////////////////////////////////////////////////////////////////////////
  57. #define RADAR_QUEUE_TERRAIN_REFRESH_DELAY (LOGICFRAMES_PER_SECOND * 3.0f)
  58. //-------------------------------------------------------------------------------------------------
  59. /** Delete list resources used by the radar and return them to the memory pools */
  60. //-------------------------------------------------------------------------------------------------
  61. void Radar::deleteListResources( void )
  62. {
  63. RadarObject *nextObject;
  64. // delete entries from the local object list
  65. while( m_localObjectList )
  66. {
  67. // get next object
  68. nextObject = m_localObjectList->friend_getNext();
  69. // remove radar data from object
  70. m_localObjectList->friend_getObject()->friend_setRadarData( NULL );
  71. // delete the head of the list
  72. m_localObjectList->deleteInstance();
  73. // set head of the list to the next object
  74. m_localObjectList = nextObject;
  75. } // end while
  76. // delete entries from the regular object list
  77. while( m_objectList )
  78. {
  79. // get next object
  80. nextObject = m_objectList->friend_getNext();
  81. // remove radar data from object
  82. m_objectList->friend_getObject()->friend_setRadarData( NULL );
  83. // delete the head of the list
  84. m_objectList->deleteInstance();
  85. // set head of the list to the next object
  86. m_objectList = nextObject;
  87. } // end while
  88. Object *obj;
  89. for( obj = TheGameLogic->getFirstObject(); obj; obj = obj->getNextObject() )
  90. {
  91. DEBUG_ASSERTCRASH( obj->friend_getRadarData() == NULL, ("oops") );
  92. }
  93. } // end deleteListResources
  94. // PUBLIC METHODS /////////////////////////////////////////////////////////////////////////////////
  95. //-------------------------------------------------------------------------------------------------
  96. //-------------------------------------------------------------------------------------------------
  97. RadarObject::RadarObject( void )
  98. {
  99. m_object = NULL;
  100. m_next = NULL;
  101. m_color = GameMakeColor( 255, 255, 255, 255 );
  102. }
  103. //-------------------------------------------------------------------------------------------------
  104. //-------------------------------------------------------------------------------------------------
  105. RadarObject::~RadarObject( void )
  106. {
  107. }
  108. //-------------------------------------------------------------------------------------------------
  109. //-------------------------------------------------------------------------------------------------
  110. Bool RadarObject::isTemporarilyHidden() const
  111. {
  112. Drawable* draw = m_object->getDrawable();
  113. if (draw->getStealthLook() == STEALTHLOOK_INVISIBLE || draw->isDrawableEffectivelyHidden())
  114. return true;
  115. return false;
  116. }
  117. // ------------------------------------------------------------------------------------------------
  118. /** CRC */
  119. // ------------------------------------------------------------------------------------------------
  120. void RadarObject::crc( Xfer *xfer )
  121. {
  122. } // end crc
  123. // ------------------------------------------------------------------------------------------------
  124. /** Xfer method
  125. * Version Info:
  126. * 1: Initial version */
  127. // ------------------------------------------------------------------------------------------------
  128. void RadarObject::xfer( Xfer *xfer )
  129. {
  130. // version
  131. XferVersion currentVersion = 1;
  132. XferVersion version = currentVersion;
  133. xfer->xferVersion( &version, currentVersion );
  134. // object id
  135. ObjectID objectID = m_object ? m_object->getID() : INVALID_ID;
  136. xfer->xferObjectID( &objectID );
  137. if( xfer->getXferMode() == XFER_LOAD )
  138. {
  139. // find the object and save
  140. m_object = TheGameLogic->findObjectByID( objectID );
  141. if( m_object == NULL )
  142. {
  143. DEBUG_CRASH(( "RadarObject::xfer - Unable to find object for radar data\n" ));
  144. throw SC_INVALID_DATA;
  145. } // end if
  146. // tell the object we now have some radar data
  147. m_object->friend_setRadarData( this );
  148. } // end if
  149. // color
  150. xfer->xferColor( &m_color );
  151. } // end xfer
  152. // ------------------------------------------------------------------------------------------------
  153. /** Load post process */
  154. // ------------------------------------------------------------------------------------------------
  155. void RadarObject::loadPostProcess( void )
  156. {
  157. } // end loadPostProcess
  158. //-------------------------------------------------------------------------------------------------
  159. //-------------------------------------------------------------------------------------------------
  160. Radar::Radar( void )
  161. {
  162. m_radarWindow = NULL;
  163. m_objectList = NULL;
  164. m_localObjectList = NULL;
  165. m_radarHidden = false;
  166. m_radarForceOn = false;
  167. m_terrainAverageZ = 0.0f;
  168. m_waterAverageZ = 0.0f;
  169. m_xSample = 0.0f;
  170. m_ySample = 0.0f;
  171. m_mapExtent.lo.x = 0.0f;
  172. m_mapExtent.lo.y = 0.0f;
  173. m_mapExtent.lo.z = 0.0f;
  174. m_mapExtent.hi.x = 0.0f;
  175. m_mapExtent.hi.y = 0.0f;
  176. m_mapExtent.hi.z = 0.0f;
  177. m_queueTerrainRefreshFrame = 0;
  178. // clear the radar events
  179. clearAllEvents();
  180. } // end Radar
  181. //-------------------------------------------------------------------------------------------------
  182. //-------------------------------------------------------------------------------------------------
  183. Radar::~Radar( void )
  184. {
  185. // delete list resources
  186. deleteListResources();
  187. } // end ~Radar
  188. //-------------------------------------------------------------------------------------------------
  189. /** Clear all radar events */
  190. //-------------------------------------------------------------------------------------------------
  191. void Radar::clearAllEvents( void )
  192. {
  193. // set next free index to the first one
  194. m_nextFreeRadarEvent = 0;
  195. m_lastRadarEvent = -1;
  196. // zero out all data
  197. for( Int i = 0; i < MAX_RADAR_EVENTS; ++i )
  198. {
  199. m_event[ i ].type = RADAR_EVENT_INVALID;
  200. m_event[ i ].active = FALSE;
  201. m_event[ i ].createFrame = 0;
  202. m_event[ i ].dieFrame = 0;
  203. m_event[ i ].fadeFrame = 0;
  204. m_event[ i ].color1.red = 0;
  205. m_event[ i ].color1.green = 0;
  206. m_event[ i ].color1.blue = 0;
  207. m_event[ i ].color2.red = 0;
  208. m_event[ i ].color2.green = 0;
  209. m_event[ i ].color2.blue = 0;
  210. m_event[ i ].worldLoc.x = 0.0f;
  211. m_event[ i ].worldLoc.y = 0.0f;
  212. m_event[ i ].worldLoc.z = 0.0f;
  213. m_event[ i ].radarLoc.x = 0;
  214. m_event[ i ].radarLoc.y = 0;
  215. m_event[ i ].soundPlayed = FALSE;
  216. } // end for i
  217. } // end clearAllEvents
  218. //-------------------------------------------------------------------------------------------------
  219. /** Reset radar data */
  220. //-------------------------------------------------------------------------------------------------
  221. void Radar::reset( void )
  222. {
  223. // delete list resources
  224. deleteListResources();
  225. // clear all events
  226. clearAllEvents();
  227. // stop forcing the radar on
  228. m_radarForceOn = false;
  229. } // end reset
  230. //-------------------------------------------------------------------------------------------------
  231. /** Radar per frame update */
  232. //-------------------------------------------------------------------------------------------------
  233. void Radar::update( void )
  234. {
  235. Int i;
  236. UnsignedInt thisFrame = TheGameLogic->getFrame();
  237. //
  238. // traverse the radar event list, if an event has a creationFrame it means that it
  239. // exists ... check to see if it's time for that event to die and if so, just clear
  240. // out the active status
  241. //
  242. for( i = 0; i < MAX_RADAR_EVENTS; i++ )
  243. {
  244. if( m_event[ i ].active == TRUE && m_event[ i ].createFrame &&
  245. thisFrame > m_event[ i ].dieFrame )
  246. m_event[ i ].active = FALSE;
  247. } // end for i
  248. // see if we should refresh the terrain
  249. if( m_queueTerrainRefreshFrame != 0 &&
  250. TheGameLogic->getFrame() - m_queueTerrainRefreshFrame > RADAR_QUEUE_TERRAIN_REFRESH_DELAY )
  251. {
  252. // refresh the terrain
  253. refreshTerrain( TheTerrainLogic );
  254. } // end if
  255. } // end update
  256. //-------------------------------------------------------------------------------------------------
  257. /** Reset the radar for the new map data being given to it */
  258. //-------------------------------------------------------------------------------------------------
  259. void Radar::newMap( TerrainLogic *terrain )
  260. {
  261. // keep a pointer for our radar window
  262. Int id = NAMEKEY( "ControlBar.wnd:LeftHUD" );
  263. m_radarWindow = TheWindowManager->winGetWindowFromId( NULL, id );
  264. DEBUG_ASSERTCRASH( m_radarWindow, ("Radar::newMap - Unable to find radar game window\n") );
  265. // reset all the data in the radar
  266. reset();
  267. // get the extents of the new map
  268. terrain->getExtent( &m_mapExtent );
  269. // we will sample at these intervals across the map
  270. m_xSample = m_mapExtent.width() / RADAR_CELL_WIDTH;
  271. m_ySample = m_mapExtent.height() / RADAR_CELL_HEIGHT;
  272. // find the "middle" height for the terrain (most used value) and water table
  273. Int x, y, z;
  274. Int terrainSamples = 0, waterSamples = 0;
  275. m_terrainAverageZ = 0.0f;
  276. m_waterAverageZ = 0.0f;
  277. ICoord2D radarPoint;
  278. Coord3D worldPoint;
  279. for( y = 0; y < RADAR_CELL_HEIGHT; y++ )
  280. for( x = 0; x < RADAR_CELL_WIDTH; x++ )
  281. {
  282. radarPoint.x = x;
  283. radarPoint.y = y;
  284. radarToWorld( &radarPoint, &worldPoint );
  285. z = terrain->getGroundHeight( worldPoint.x, worldPoint.y );
  286. Real waterZ;
  287. if( terrain->isUnderwater( worldPoint.x, worldPoint.y, &waterZ ) )
  288. {
  289. m_waterAverageZ += z;
  290. waterSamples++;
  291. }
  292. else
  293. {
  294. m_terrainAverageZ += z;
  295. terrainSamples++;
  296. }
  297. } // end for x
  298. // avoid divide by zeros
  299. if( terrainSamples == 0 )
  300. terrainSamples = 1;
  301. if( waterSamples == 0 )
  302. waterSamples = 1;
  303. // compute averages
  304. m_terrainAverageZ = m_terrainAverageZ / INT_TO_REAL( terrainSamples );
  305. m_waterAverageZ = m_waterAverageZ / INT_TO_REAL( waterSamples );
  306. } // end newMap
  307. //-------------------------------------------------------------------------------------------------
  308. /** Add an object to the radar list. The object will be sorted in the list to be grouped
  309. * using it's radar priority */
  310. //-------------------------------------------------------------------------------------------------
  311. void Radar::addObject( Object *obj )
  312. {
  313. // get the radar priority for this object
  314. RadarPriorityType newPriority = obj->getRadarPriority();
  315. if( isPriorityVisible( newPriority ) == FALSE )
  316. return;
  317. // if this object is on the radar, remove it in favor of the new add
  318. RadarObject **list;
  319. RadarObject *newObj;
  320. // sanity
  321. DEBUG_ASSERTCRASH( obj->friend_getRadarData() == NULL,
  322. ("Radar: addObject - non NULL radar data for '%s'\n",
  323. obj->getTemplate()->getName().str()) );
  324. // allocate a new object
  325. newObj = newInstance(RadarObject);
  326. // set the object data
  327. newObj->friend_setObject( obj );
  328. // set color for this object on the radar
  329. const Player *player = obj->getControllingPlayer();
  330. Bool useIndicatorColor = true;
  331. if( obj->isKindOf( KINDOF_DISGUISER ) )
  332. {
  333. //Because we have support for disguised units pretending to be units from another
  334. //team, we need to intercept it here and make sure it's rendered appropriately
  335. //based on which client is rendering it.
  336. static NameKeyType key_StealthUpdate = NAMEKEY( "StealthUpdate" );
  337. StealthUpdate *update = (StealthUpdate*)obj->findUpdateModule( key_StealthUpdate );
  338. if( update )
  339. {
  340. if( update->isDisguised() )
  341. {
  342. Player *clientPlayer = ThePlayerList->getLocalPlayer();
  343. Player *disguisedPlayer = ThePlayerList->getNthPlayer( update->getDisguisedPlayerIndex() );
  344. if( player->getRelationship( clientPlayer->getDefaultTeam() ) != ALLIES && clientPlayer->isPlayerActive() )
  345. {
  346. //Neutrals and enemies will see this disguised unit as the team it's disguised as.
  347. player = disguisedPlayer;
  348. if( player )
  349. useIndicatorColor = false;
  350. }
  351. //Otherwise, the color will show up as the team it really belongs to (already set above).
  352. }
  353. }
  354. }
  355. if( obj->getContain() )
  356. {
  357. // To handle Stealth garrison, ask containers what color they are drawing with to the local player.
  358. // Local is okay because radar display is not synced.
  359. player = obj->getContain()->getApparentControllingPlayer( ThePlayerList->getLocalPlayer() );
  360. if( player )
  361. useIndicatorColor = false;
  362. }
  363. if( useIndicatorColor || (player == NULL) )
  364. {
  365. newObj->setColor( obj->getIndicatorColor() );
  366. }
  367. else
  368. {
  369. newObj->setColor( player->getPlayerColor() );
  370. }
  371. // set a chunk of radar data in the object
  372. obj->friend_setRadarData( newObj );
  373. //
  374. // we will put this on either the local object list for objects that belong to the
  375. // local player, or on the regular object list for all other objects
  376. //
  377. if( obj->isLocallyControlled() )
  378. list = &m_localObjectList;
  379. else
  380. list = &m_objectList;
  381. // link object to master list at the head of it's priority section
  382. if( *list == NULL )
  383. *list = newObj; // trivial case, an empty list
  384. else
  385. {
  386. RadarPriorityType prevPriority, currPriority;
  387. RadarObject *currObject, *prevObject, *nextObject;
  388. prevObject = NULL;
  389. prevPriority = RADAR_PRIORITY_INVALID;
  390. for( currObject = *list; currObject; currObject = nextObject )
  391. {
  392. // get the next object
  393. nextObject = currObject->friend_getNext();
  394. // get the priority of this entry in the list (currPriority)
  395. currPriority = currObject->friend_getObject()->getRadarPriority();
  396. //
  397. // if there is no previous object, or the previous priority is less than the
  398. // our new priority, and the current object in the list has a priority
  399. // higher than our equal to our own we need to be inserted here
  400. //
  401. if( (prevObject == NULL || prevPriority < newPriority ) &&
  402. (currPriority >= newPriority) )
  403. {
  404. // insert into the list just ahead of currObject
  405. if( prevObject )
  406. {
  407. // the new entry next points to what the previous one used to point to
  408. newObj->friend_setNext( prevObject->friend_getNext() );
  409. // the previous one next now points to the new entry
  410. prevObject->friend_setNext( newObj );
  411. } // end if
  412. else
  413. {
  414. // the new object next points to the current object
  415. newObj->friend_setNext( currObject );
  416. // new list head is now newObj
  417. *list = newObj;
  418. } // end else
  419. break; // exit for, stop the insert
  420. } // end if
  421. else if( nextObject == NULL )
  422. {
  423. // at the end of the list, put object here
  424. currObject->friend_setNext( newObj );
  425. } // end else if
  426. // our current object is now the previous object
  427. prevObject = currObject;
  428. prevPriority = currPriority;
  429. } // end if
  430. } // end else
  431. } // end addObject
  432. //-------------------------------------------------------------------------------------------------
  433. /** Try to delete an object from a specific list */
  434. //-------------------------------------------------------------------------------------------------
  435. Bool Radar::deleteFromList( Object *obj, RadarObject **list )
  436. {
  437. RadarObject *radarObject, *prevObject = NULL;
  438. // find the object in list
  439. for( radarObject = *list; radarObject; radarObject = radarObject->friend_getNext() )
  440. {
  441. if( radarObject->friend_getObject() == obj )
  442. {
  443. // unlink the object from list
  444. if( prevObject == NULL )
  445. *list = radarObject->friend_getNext(); // removing head of list
  446. else
  447. prevObject->friend_setNext( radarObject->friend_getNext() );
  448. // set the object radar data to NULL
  449. obj->friend_setRadarData( NULL );
  450. // delete the object instance
  451. radarObject->deleteInstance();
  452. // all done, object found and deleted
  453. return TRUE;
  454. } // end if
  455. // save this object as previous one encountered in the list
  456. prevObject = radarObject;
  457. } // end for, radarObject
  458. // object was not found in this list
  459. return FALSE;
  460. } // end deleteFromList
  461. //-------------------------------------------------------------------------------------------------
  462. /** Remove an object from the radar, the object may reside in any list */
  463. //-------------------------------------------------------------------------------------------------
  464. void Radar::removeObject( Object *obj )
  465. {
  466. // sanity
  467. if( obj->friend_getRadarData() == NULL )
  468. return;
  469. if( deleteFromList( obj, &m_localObjectList ) == TRUE )
  470. return;
  471. else if( deleteFromList( obj, &m_objectList ) == TRUE )
  472. return;
  473. else
  474. {
  475. // sanity
  476. DEBUG_ASSERTCRASH( 0, ("Radar: Tried to remove object '%s' which was not found\n",
  477. obj->getTemplate()->getName().str()) );
  478. } // end else
  479. } // end removeObject
  480. //-------------------------------------------------------------------------------------------------
  481. /** Translate a 2D spot on the radar (from (0,0) to (RADAR_CELL_WIDTH,RADAR_CELL_HEIGHT)
  482. * to a 3D spot in the world on the terrain
  483. * Return TRUE if the radar points translates to a valid world position
  484. * Return FALSE if the radar point is not a valid world position */
  485. //-------------------------------------------------------------------------------------------------
  486. Bool Radar::radarToWorld( const ICoord2D *radar, Coord3D *world )
  487. {
  488. Int x, y;
  489. // sanity
  490. if( radar == NULL || world == NULL )
  491. return FALSE;
  492. // get the coords
  493. x = radar->x;
  494. y = radar->y;
  495. // more sanity
  496. if( x < 0 )
  497. x = 0;
  498. if( x >= RADAR_CELL_WIDTH )
  499. x = RADAR_CELL_WIDTH - 1;
  500. if( y < 0 )
  501. y = 0;
  502. if( y >= RADAR_CELL_HEIGHT )
  503. y = RADAR_CELL_HEIGHT - 1;
  504. // translate to world
  505. world->x = x * m_xSample;
  506. world->y = y * m_ySample;
  507. // find the terrain height here
  508. world->z = TheTerrainLogic->getGroundHeight( world->x, world->y );
  509. return TRUE; // valid translation
  510. } // end radarToWorld
  511. //-------------------------------------------------------------------------------------------------
  512. /** Translate a point in the world to the 2D radar (x,y)
  513. * Return TRUE if the world point successfully translates to a radar point
  514. * Return FALSE if world point is a bogus off the map position */
  515. //-------------------------------------------------------------------------------------------------
  516. Bool Radar::worldToRadar( const Coord3D *world, ICoord2D *radar )
  517. {
  518. // sanity
  519. if( world == NULL || radar == NULL )
  520. return FALSE;
  521. // sanity check the world position
  522. // if( world->x < m_mapExtent.lo.x || world->x > m_mapExtent.hi.x ||
  523. // world->y < m_mapExtent.lo.y || world->y > m_mapExtent.hi.y )
  524. // return FALSE;
  525. // This is actually an insanity check. Nobody uses the return value, so this just leaves garbage in the
  526. // return pointer. The reason the question gets asked is there are 60 partition cells to 128 radar cells
  527. // (for example), and the radar wants to draw a horizontal line. This line ends up three pixels long
  528. // at the right side, so the radar gives up and doesn't draw the middle one.
  529. // We bind to on radar anyway, and we only ask if we are on radar, so don't intentionally add edge weirdness.
  530. // convert
  531. radar->x = world->x / m_xSample;
  532. radar->y = world->y / m_ySample;
  533. // keep it in bounds
  534. if( radar->x < 0 )
  535. radar->x = 0;
  536. if( radar->x >= RADAR_CELL_WIDTH )
  537. radar->x = RADAR_CELL_WIDTH - 1;
  538. if( radar->y < 0 )
  539. radar->y = 0;
  540. if( radar->y >= RADAR_CELL_HEIGHT )
  541. radar->y = RADAR_CELL_HEIGHT - 1;
  542. return TRUE; // valid translation
  543. } // end worldToRadar
  544. // ------------------------------------------------------------------------------------------------
  545. /** Translate an actual pixel location (relative pixel with (0,0) being the top left of
  546. * the radar area) to the "logical" radar coords that would cover the entire area of display
  547. * on the screen. This is needed because some maps are "long" or "tall" and need a translation
  548. * to any radar image that has been scaled to preserve the map aspect ratio */
  549. // ------------------------------------------------------------------------------------------------
  550. Bool Radar::localPixelToRadar( const ICoord2D *pixel, ICoord2D *radar )
  551. {
  552. // sanity
  553. if( pixel == NULL || radar == NULL )
  554. return FALSE;
  555. // get window size of the radar
  556. ICoord2D size;
  557. m_radarWindow->winGetSize( &size.x, &size.y );
  558. //
  559. // act like we're going to draw and find the aspect ratio adjusted points of the
  560. // terrain radar positions
  561. //
  562. ICoord2D start = { 0, 0 };
  563. ICoord2D ul, lr;
  564. findDrawPositions( start.x, start.y, size.x, size.y, &ul, &lr );
  565. // get the scaled width and height
  566. Int scaledWidth = lr.x - ul.x;
  567. Int scaledHeight = lr.y - ul.y;
  568. // if the pixel is outsize of the adjusted radar area there are no logical coords
  569. if( pixel->x < ul.x || pixel->x > lr.x ||
  570. pixel->y < ul.y || pixel->y > lr.y )
  571. return FALSE;
  572. if( scaledWidth >= scaledHeight )
  573. {
  574. // just normal conversion from full stretched to radar cells
  575. radar->x = (pixel->x - ul.x)* RADAR_CELL_WIDTH / scaledWidth;
  576. // conversion for scaled Y direction in map
  577. radar->y = REAL_TO_INT( ((pixel->y - ul.y) / INT_TO_REAL( scaledHeight )) * size.y );
  578. //
  579. // radar->y now refers to a point that was "as if" the map was square, translate to radar
  580. // note that y is inverted to have the radar align with the world (+x = right, -y = down)
  581. //
  582. radar->y = (size.y - radar->y) * RADAR_CELL_HEIGHT / size.y;
  583. } // end if
  584. else
  585. {
  586. // conversion for scaled Y direction in map
  587. radar->x = REAL_TO_INT( ((pixel->x - ul.x) / INT_TO_REAL( scaledWidth )) * size.x );
  588. // radar->x now refers to a point that was "as if" the map was square, translate to radar
  589. radar->x = radar->x * RADAR_CELL_WIDTH / size.x;
  590. //
  591. // just normal conversion from full stretched to radar cells, note that y is inverted
  592. // to have the radar align with the world (+x = right, -y = down)
  593. //
  594. radar->y = (size.y - pixel->y) * RADAR_CELL_HEIGHT / size.y;
  595. } // end else
  596. return TRUE;
  597. } // end localPixelToRadar
  598. // ------------------------------------------------------------------------------------------------
  599. /** Translate a screen mouse position to world coords if the screen position is within
  600. * the radar window and that spot in the radar corresponds to a point in the world */
  601. // ------------------------------------------------------------------------------------------------
  602. Bool Radar::screenPixelToWorld( const ICoord2D *pixel, Coord3D *world )
  603. {
  604. // sanity
  605. if( pixel == NULL || world == NULL )
  606. return FALSE;
  607. // if we have no radar window can't do the conversion
  608. if( m_radarWindow == NULL )
  609. return FALSE;
  610. // translate pixel coords to local pixel coords relative to the radar window
  611. ICoord2D localPixel;
  612. ICoord2D screenPos;
  613. m_radarWindow->winGetScreenPosition( &screenPos.x, &screenPos.y );
  614. localPixel.x = pixel->x - screenPos.x;
  615. localPixel.y = pixel->y - screenPos.y;
  616. // translate local pixel to radar
  617. ICoord2D radar;
  618. if( localPixelToRadar( &localPixel, &radar ) == FALSE )
  619. return FALSE;
  620. // translate radar to world
  621. return radarToWorld( &radar, world );
  622. } // end screenPixelToWorld
  623. // ------------------------------------------------------------------------------------------------
  624. /** Given the pixel coordinates, see if there is an object that is exactly in this
  625. * spot represented on the radar */
  626. // ------------------------------------------------------------------------------------------------
  627. Object *Radar::objectUnderRadarPixel( const ICoord2D *pixel )
  628. {
  629. // sanity
  630. if( pixel == NULL )
  631. return NULL;
  632. // convert pixel location to radar logical radar location
  633. ICoord2D radar;
  634. if( localPixelToRadar( pixel, &radar ) == FALSE )
  635. return NULL;
  636. // object we will return
  637. Object *obj = NULL;
  638. //
  639. // scan the objects on the radar list and return any object that maps its world location
  640. // to the radar location
  641. //
  642. // search the local object list
  643. obj = searchListForRadarLocationMatch( m_localObjectList, &radar );
  644. // search all other objects if not found
  645. if( obj == NULL )
  646. obj = searchListForRadarLocationMatch( m_objectList, &radar );
  647. // return the object found (if any)
  648. return obj;
  649. } // end objectUnderRadarPixel
  650. // ------------------------------------------------------------------------------------------------
  651. /** Search the object list for an object that maps to the given logical radar coords */
  652. // ------------------------------------------------------------------------------------------------
  653. Object *Radar::searchListForRadarLocationMatch( RadarObject *listHead, ICoord2D *radarMatch )
  654. {
  655. // sanity
  656. if( listHead == NULL || radarMatch == NULL )
  657. return NULL;
  658. // scan the list
  659. RadarObject *radarObject;
  660. ICoord2D radar;
  661. for( radarObject = listHead; radarObject; radarObject = radarObject->friend_getNext() )
  662. {
  663. // get object
  664. Object *obj = radarObject->friend_getObject();
  665. // sanity
  666. if( obj == NULL )
  667. {
  668. DEBUG_CRASH(( "Radar::searchListForRadarLocationMatch - NULL object encountered in list\n" ));
  669. continue;
  670. } // end if
  671. // convert object position to logical radar
  672. worldToRadar( obj->getPosition(), &radar );
  673. // see if this matches our match radar location
  674. if( radar.x >= radarMatch->x - 1 &&
  675. radar.x <= radarMatch->x + 1 &&
  676. radar.y >= radarMatch->y - 1 &&
  677. radar.y <= radarMatch->y + 1 )
  678. return obj;
  679. } // end for, radarObject
  680. // no match found
  681. return NULL;
  682. } // end searchListForRadarLocationMatch
  683. // ------------------------------------------------------------------------------------------------
  684. /** Given the RELATIVE SCREEN start X and Y, the width and height of the area to draw the whole
  685. * radar in, compute what the upper left (ul) and lower right (lr) local coordinates are
  686. * that represent the actual terrain image part of the radar that will preserve the
  687. * aspect ratio of the map */
  688. // ------------------------------------------------------------------------------------------------
  689. void Radar::findDrawPositions( Int startX, Int startY, Int width, Int height,
  690. ICoord2D *ul, ICoord2D *lr )
  691. {
  692. Real ratioWidth;
  693. Real ratioHeight;
  694. Coord2D radar;
  695. ratioWidth = m_mapExtent.width()/(width * 1.0f);
  696. ratioHeight = m_mapExtent.height()/(height* 1.0f);
  697. if( ratioWidth >= ratioHeight)
  698. {
  699. radar.x = m_mapExtent.width() / ratioWidth;
  700. radar.y = m_mapExtent.height()/ ratioWidth;
  701. ul->x = 0;
  702. ul->y = (height - radar.y) / 2.0f;
  703. lr->x = radar.x;
  704. lr->y = height - ul->y;
  705. }
  706. else
  707. {
  708. radar.x = m_mapExtent.width() / ratioHeight;
  709. radar.y = m_mapExtent.height()/ ratioHeight;
  710. ul->x = (width - radar.x ) / 2.0f;
  711. ul->y = 0;
  712. lr->x = width - ul->x;
  713. lr->y = radar.y;
  714. }
  715. /*
  716. if( m_mapExtent.width() > m_mapExtent.height() )
  717. {
  718. //
  719. // +---------------+
  720. // | |
  721. // | |
  722. // +---------------+
  723. // | map area |
  724. // +---------------+
  725. // | |
  726. // | |
  727. // +---------------+
  728. //
  729. ul->x = 0;
  730. ul->y = (height - (m_mapExtent.height() / m_mapExtent.width() * height)) / 2.0f;
  731. lr->x = width;
  732. lr->y = height - ul->y;
  733. } // end if
  734. else if( m_mapExtent.height() > m_mapExtent.width() )
  735. {
  736. // +-----+---+-----+
  737. // | | m | |
  738. // | | a | |
  739. // | | p | |
  740. // | | | |
  741. // | | a | |
  742. // | | r | |
  743. // | | e | |
  744. // | | a | |
  745. // +-----+---+-----+
  746. //
  747. ul->x = (width - (m_mapExtent.width() / m_mapExtent.height() * width)) / 2.0f;
  748. ul->y = 0;
  749. lr->x = width - ul->x;
  750. lr->y = height;
  751. } // end else
  752. else
  753. {
  754. ul->x = 0;
  755. ul->y = 0;
  756. lr->x = width;
  757. lr->y = height;
  758. } // end else
  759. */
  760. // make them pixel positions
  761. ul->x += startX;
  762. ul->y += startY;
  763. lr->x += startX;
  764. lr->y += startY;
  765. } // end findDrawPositions
  766. //-------------------------------------------------------------------------------------------------
  767. /** Radar color lookup table */
  768. //-------------------------------------------------------------------------------------------------
  769. struct RadarColorLookup
  770. {
  771. RadarEventType event;
  772. RGBAColorInt color1;
  773. RGBAColorInt color2;
  774. };
  775. static RadarColorLookup radarColorLookupTable[] =
  776. {
  777. /* Radar Event Color 1 Color 2 */
  778. { RADAR_EVENT_CONSTRUCTION, { 128, 128, 255, 255 }, { 128, 255, 255, 255 } },
  779. { RADAR_EVENT_UPGRADE, { 128, 0, 64, 255 }, { 255, 185, 220, 255 } },
  780. { RADAR_EVENT_UNDER_ATTACK, { 255, 0, 0, 255 }, { 255, 128, 128, 255 } },
  781. { RADAR_EVENT_INFORMATION, { 255, 255, 0, 255 }, { 255, 255, 128, 255 } },
  782. { RADAR_EVENT_BEACON_PULSE, { 255, 255, 0, 255 }, { 255, 255, 128, 255 } },
  783. { RADAR_EVENT_INFILTRATION, { 0, 255, 255, 255 }, { 128, 255, 255, 255 } },
  784. { RADAR_EVENT_BATTLE_PLAN, { 255, 255, 255, 255 }, { 255, 255, 255, 255 } },
  785. { RADAR_EVENT_STEALTH_DISCOVERED, { 0, 255, 0, 255 }, { 0, 128, 0, 255 } },
  786. { RADAR_EVENT_STEALTH_NEUTRALIZED, { 0, 255, 0, 255 }, { 0, 128, 0, 255 } },
  787. { RADAR_EVENT_FAKE, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } },
  788. { RADAR_EVENT_INVALID, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } }
  789. };
  790. //-------------------------------------------------------------------------------------------------
  791. /** Create a new radar event */
  792. //-------------------------------------------------------------------------------------------------
  793. void Radar::createEvent( const Coord3D *world, RadarEventType type, Real secondsToLive )
  794. {
  795. // sanity
  796. if( world == NULL )
  797. return;
  798. // lookup the colors we are to used based on the event
  799. RGBAColorInt color[ 2 ];
  800. for( Int i = 0; radarColorLookupTable[ i ].event != RADAR_EVENT_INVALID; ++i )
  801. {
  802. if( radarColorLookupTable[ i ].event == type )
  803. {
  804. color[ 0 ] = radarColorLookupTable[ i ].color1;
  805. color[ 1 ] = radarColorLookupTable[ i ].color2;
  806. break;
  807. } // end if
  808. } // end while
  809. // check for no match found in color table
  810. if( radarColorLookupTable[ i ].event == RADAR_EVENT_INVALID )
  811. {
  812. static RGBAColorInt color1 = { 255, 255, 255, 255 };
  813. static RGBAColorInt color2 = { 255, 255, 255, 255 };
  814. DEBUG_CRASH(( "Radar::createEvent - Event not found in color table, using default colors\n" ));
  815. color[ 0 ] = color1;
  816. color[ 1 ] = color2;
  817. } // end if
  818. // call the internal method to create the event with these colors
  819. internalCreateEvent( world, type, secondsToLive, &color[ 0 ], &color[ 1 ] );
  820. } // end createEvent
  821. // ------------------------------------------------------------------------------------------------
  822. /** Create radar event using a specific colors from the player */
  823. // ------------------------------------------------------------------------------------------------
  824. void Radar::createPlayerEvent( Player *player, const Coord3D *world,
  825. RadarEventType type, Real secondsToLive )
  826. {
  827. // sanity
  828. if( player == NULL || world == NULL )
  829. return;
  830. // figure out the two colors we should use
  831. Color c;
  832. UnsignedByte r, g, b, a;
  833. RGBAColorInt color[ 2 ];
  834. // color 1
  835. c = player->getPlayerColor();
  836. GameGetColorComponents( c, &r, &g, &b, &a );
  837. color[ 0 ].red = r;
  838. color[ 0 ].green = g;
  839. color[ 0 ].blue = b;
  840. color[ 0 ].alpha = a;
  841. // the second will be a darker color 1
  842. Real darkScale = 0.75f;
  843. color[ 1 ] = color[ 0 ];
  844. color[ 1 ].red -= REAL_TO_INT( color[ 0 ].red * darkScale );
  845. if( color[ 1 ].red < 0 )
  846. color[ 1 ].red = 0;
  847. color[ 1 ].green -= REAL_TO_INT( color[ 0 ].green * darkScale );
  848. if( color[ 1 ].green < 0 )
  849. color[ 1 ].green = 0;
  850. color[ 1 ].blue -= REAL_TO_INT( color[ 0 ].blue * darkScale );
  851. if( color[ 1 ].blue < 0 )
  852. color[ 1 ].blue = 0;
  853. // create the events using these colors
  854. internalCreateEvent( world, type, secondsToLive, &color[ 0 ], &color[ 1 ] );
  855. } // end createPlayerEvent
  856. //-------------------------------------------------------------------------------------------------
  857. /** Create a new radar event */
  858. //-------------------------------------------------------------------------------------------------
  859. void Radar::internalCreateEvent( const Coord3D *world, RadarEventType type, Real secondsToLive,
  860. const RGBAColorInt *color1, const RGBAColorInt *color2 )
  861. {
  862. static Real secondsBeforeDieToFade = 0.5f; ///< this many seconds before we hit the die frame we start to fade away
  863. // sanity
  864. if( world == NULL || color1 == NULL || color2 == NULL )
  865. return;
  866. // translate the world coord to radar coords
  867. ICoord2D radar;
  868. worldToRadar( world, &radar );
  869. // add to the list of radar events
  870. m_event[ m_nextFreeRadarEvent ].type = type;
  871. m_event[ m_nextFreeRadarEvent ].active = TRUE;
  872. m_event[ m_nextFreeRadarEvent ].createFrame = TheGameLogic->getFrame();
  873. m_event[ m_nextFreeRadarEvent ].dieFrame = TheGameLogic->getFrame() + LOGICFRAMES_PER_SECOND * secondsToLive;
  874. m_event[ m_nextFreeRadarEvent ].fadeFrame = m_event[ m_nextFreeRadarEvent ].dieFrame - LOGICFRAMES_PER_SECOND * secondsBeforeDieToFade;
  875. m_event[ m_nextFreeRadarEvent ].color1 = *color1;
  876. m_event[ m_nextFreeRadarEvent ].color2 = *color2;
  877. m_event[ m_nextFreeRadarEvent ].worldLoc = *world;
  878. m_event[ m_nextFreeRadarEvent ].radarLoc = radar;
  879. m_event[ m_nextFreeRadarEvent ].soundPlayed = FALSE;
  880. // record the index of this, our "last" radar event.
  881. if ( type != RADAR_EVENT_BEACON_PULSE )
  882. m_lastRadarEvent = m_nextFreeRadarEvent;
  883. //
  884. // increment the next radar event index, wrapping to the beginning. If we ever have so many
  885. // events that they fill up the buffer the oldest ones will just drop off, eh ... should be fine.
  886. //
  887. m_nextFreeRadarEvent++;
  888. if( m_nextFreeRadarEvent >= MAX_RADAR_EVENTS )
  889. m_nextFreeRadarEvent = 0;
  890. } // end createEvent
  891. //-------------------------------------------------------------------------------------------------
  892. /** Get the last event position, if any.
  893. * Returns TRUE if event was found
  894. * Returns FALSE if there is no "last event" */
  895. //-------------------------------------------------------------------------------------------------
  896. Bool Radar::getLastEventLoc( Coord3D *eventPos )
  897. {
  898. // if we have an index for the last event, one was present
  899. if( m_lastRadarEvent != -1 )
  900. {
  901. if( eventPos )
  902. *eventPos = m_event[ m_lastRadarEvent ].worldLoc;
  903. return TRUE;
  904. } // end if
  905. return FALSE; // no last event
  906. } // end getLastEventLoc
  907. // ------------------------------------------------------------------------------------------------
  908. /** Try to create a radar event for "we're under attack". This will be called every time
  909. * actual damage is dealt to an object that the player owns that shows up on the radar.
  910. * We don't want to create under attack events every time we are damaged and we also want
  911. * to limit them based on time and local area of other recent attack events */
  912. // ------------------------------------------------------------------------------------------------
  913. void Radar::tryUnderAttackEvent( const Object *obj )
  914. {
  915. // sanity
  916. if( obj == NULL )
  917. return;
  918. // try to create the event
  919. Bool eventCreated = tryEvent( RADAR_EVENT_UNDER_ATTACK, obj->getPosition() );
  920. // if event created, do some more feedback
  921. if( eventCreated )
  922. {
  923. TheControlBar->triggerRadarAttackGlow();
  924. //
  925. ///@todo Should make an INI data driven table for radar event strings, and audio events
  926. //
  927. // UI feedback for being under attack (note that we display these messages and audio
  928. // queues even if we don't have a radar)
  929. //
  930. Player *player = ThePlayerList->getLocalPlayer();
  931. // create a message for the attack event
  932. if( obj->isKindOf( KINDOF_INFANTRY ) || obj->isKindOf( KINDOF_VEHICLE ) )
  933. {
  934. AudioEventRTS unitAttackSound;
  935. if( obj->isKindOf(KINDOF_HARVESTER) )
  936. {
  937. // display special message
  938. TheInGameUI->message( "RADAR:HarvesterUnderAttack" );
  939. // play special audio event
  940. unitAttackSound = TheAudio->getMiscAudio()->m_radarHarvesterUnderAttackSound;
  941. }
  942. else
  943. {
  944. // display message
  945. TheInGameUI->message( "RADAR:UnitUnderAttack" );
  946. // play audio event
  947. unitAttackSound = TheAudio->getMiscAudio()->m_radarStructureUnderAttackSound;
  948. }
  949. unitAttackSound.setPlayerIndex(player->getPlayerIndex());
  950. TheAudio->addAudioEvent( &unitAttackSound );
  951. } // end if
  952. else if( obj->isKindOf( KINDOF_STRUCTURE ) && obj->isKindOf( KINDOF_MP_COUNT_FOR_VICTORY ) )
  953. {
  954. // play EVA. If its our object, play Base under attack.
  955. if (obj->getControllingPlayer()->isLocalPlayer())
  956. TheEva->setShouldPlay(EVA_BaseUnderAttack);
  957. else if (ThePlayerList->getLocalPlayer()->getRelationship(obj->getTeam()) == ALLIES)
  958. TheEva->setShouldPlay(EVA_AllyUnderAttack);
  959. // display message
  960. TheInGameUI->message( "RADAR:StructureUnderAttack" );
  961. // play audio event
  962. static AudioEventRTS structureAttackSound = TheAudio->getMiscAudio()->m_radarStructureUnderAttackSound;
  963. structureAttackSound.setPlayerIndex(player->getPlayerIndex());
  964. TheAudio->addAudioEvent( &structureAttackSound );
  965. } // end else if
  966. else
  967. {
  968. // display message
  969. TheInGameUI->message( "RADAR:UnderAttack" );
  970. // play audio event
  971. static AudioEventRTS underAttackSound = TheAudio->getMiscAudio()->m_radarStructureUnderAttackSound;
  972. underAttackSound.setPlayerIndex(player->getPlayerIndex());
  973. TheAudio->addAudioEvent( &underAttackSound );
  974. } // end else
  975. } // end if
  976. } // end tryUnderAttackEvent
  977. // ------------------------------------------------------------------------------------------------
  978. /** Try to create a radar event for "infiltration".
  979. This happens whenever a unit is hijacked, defected, converted to carbomb, hacked, or
  980. otherwise snuck into */
  981. // ------------------------------------------------------------------------------------------------
  982. void Radar::tryInfiltrationEvent( const Object *obj )
  983. {
  984. // We should only be warned against infiltrations that are taking place against us.
  985. if (obj->getControllingPlayer() != ThePlayerList->getLocalPlayer())
  986. return;
  987. // create the radar event
  988. createEvent( obj->getPosition(), RADAR_EVENT_INFILTRATION );
  989. //
  990. ///@todo Should make an INI data driven table for radar event strings, and audio events
  991. //
  992. // UI feedback for being under attack (note that we display these messages and audio
  993. // queues even if we don't have a radar)
  994. //
  995. Player *player = ThePlayerList->getLocalPlayer();
  996. // display message
  997. TheInGameUI->message( "RADAR:Infiltration" );
  998. // play audio event
  999. static AudioEventRTS infiltrationWarningSound = TheAudio->getMiscAudio()->m_radarInfiltrationSound;
  1000. infiltrationWarningSound.setPlayerIndex(player->getPlayerIndex());
  1001. TheAudio->addAudioEvent( &infiltrationWarningSound );
  1002. } // end tryInfiltrationEvent
  1003. // ------------------------------------------------------------------------------------------------
  1004. // ------------------------------------------------------------------------------------------------
  1005. Bool Radar::tryEvent( RadarEventType event, const Coord3D *pos )
  1006. {
  1007. // sanity
  1008. if( event <= RADAR_EVENT_INVALID || event >= RADAR_EVENT_NUM_EVENTS || pos == NULL )
  1009. return FALSE;
  1010. // see if there was an event of this type within the given range within the given time
  1011. UnsignedInt currentFrame = TheGameLogic->getFrame();
  1012. const Real closeEnoughDistanceSq = 250.0f * 250.0f;
  1013. const UnsignedInt framesBetweenEvents = LOGICFRAMES_PER_SECOND * 10;
  1014. //
  1015. // see if there was any matching radar event within range of this one
  1016. // that wasn't too long ago, if there was we won't create another and get outta here
  1017. //
  1018. for( Int i = 0; i < MAX_RADAR_EVENTS; ++i )
  1019. {
  1020. // only pay attention to under attack events
  1021. if( m_event[ i ].type == event )
  1022. {
  1023. // get distance from our new event location to this event location in 2D
  1024. Real distSquared = m_event[ i ].worldLoc.x - pos->x * m_event[ i ].worldLoc.x - pos->x +
  1025. m_event[ i ].worldLoc.y - pos->y * m_event[ i ].worldLoc.y - pos->y;
  1026. if( distSquared <= closeEnoughDistanceSq )
  1027. {
  1028. // finally only reject making a new event of this existing one is "recent enough"
  1029. if( currentFrame - m_event[ i ].createFrame < framesBetweenEvents )
  1030. return FALSE; // reject it
  1031. } // end if
  1032. } // end if
  1033. } // end for i
  1034. // if we got here then we want to create a new event
  1035. createEvent( pos, event );
  1036. // return TRUE for successfully created event
  1037. return TRUE;
  1038. } // end tryEvent
  1039. // ------------------------------------------------------------------------------------------------
  1040. // ------------------------------------------------------------------------------------------------
  1041. void Radar::refreshTerrain( TerrainLogic *terrain )
  1042. {
  1043. // no future queue is valid now
  1044. m_queueTerrainRefreshFrame = 0;
  1045. } // end refreshTerrain
  1046. // ------------------------------------------------------------------------------------------------
  1047. /** Queue a refresh of the radar terrain, we have this so that if there is code that
  1048. * rapidly needs to refresh the radar, it should use this so we aren't continually
  1049. * rebuilding the radar graphic because that process is slow. If you need to update
  1050. * the terrain on the radar immediately use refreshTerrain() */
  1051. // ------------------------------------------------------------------------------------------------
  1052. void Radar::queueTerrainRefresh( void )
  1053. {
  1054. //
  1055. // we just simply overwrite the frame we have recorded for a radar refresh. If there was
  1056. // already one there, it's simply just forgotten and whatever changes we wanted to see
  1057. // with that refresh will have to wait until enough time has passed to show these
  1058. // changes as well. why you ask ... well, because if we're calling this in close enough
  1059. // proximity for us to overwrite something, we're changing the terrain features
  1060. // quite often and can't afford the expense of rebuilding the radar visual
  1061. //
  1062. m_queueTerrainRefreshFrame = TheGameLogic->getFrame();
  1063. } // end queueTerrainRefresh
  1064. // ------------------------------------------------------------------------------------------------
  1065. /** CRC */
  1066. // ------------------------------------------------------------------------------------------------
  1067. void Radar::crc( Xfer *xfer )
  1068. {
  1069. } // end crc
  1070. // ------------------------------------------------------------------------------------------------
  1071. /** Xfer a radar object list given the head pointer as a parameter
  1072. * Version Info;
  1073. * 1: Initial version */
  1074. // ------------------------------------------------------------------------------------------------
  1075. static void xferRadarObjectList( Xfer *xfer, RadarObject **head )
  1076. {
  1077. RadarObject *radarObject;
  1078. // sanity
  1079. DEBUG_ASSERTCRASH( head != NULL, ("xferRadarObjectList - Invalid parameters\n" ));
  1080. // version
  1081. XferVersion currentVersion = 1;
  1082. XferVersion version = currentVersion;
  1083. xfer->xferVersion( &version, currentVersion );
  1084. // write could of objects in list
  1085. UnsignedShort count = 0;
  1086. for( radarObject = *head; radarObject; radarObject = radarObject->friend_getNext() )
  1087. count++;
  1088. xfer->xferUnsignedShort( &count );
  1089. // xfer the list data
  1090. if( xfer->getXferMode() == XFER_SAVE )
  1091. {
  1092. // save each object
  1093. for( radarObject = *head; radarObject; radarObject = radarObject->friend_getNext() )
  1094. {
  1095. // save this object
  1096. xfer->xferSnapshot( radarObject );
  1097. } // end for, radarObject
  1098. } // end if, save
  1099. else
  1100. {
  1101. // the list should be empty at this point as we are loading it as a whole
  1102. if( *head != NULL )
  1103. {
  1104. #if 1
  1105. // srj sez: yeah, it SHOULD be, but a few rogue things come into existence (via their ctor) preloaded
  1106. // with stuff (eg, "CabooseFullOfTerrorists"). we immediately destroy 'em, but they don't go away just yet.
  1107. // so just ignore 'em if possible.
  1108. for( radarObject = *head; radarObject; radarObject = radarObject->friend_getNext() )
  1109. {
  1110. if (!radarObject->friend_getObject()->isDestroyed())
  1111. {
  1112. DEBUG_CRASH(( "xferRadarObjectList - List head should be NULL, or contain only destroyed objects\n" ));
  1113. throw SC_INVALID_DATA;
  1114. }
  1115. }
  1116. #else
  1117. DEBUG_CRASH(( "xferRadarObjectList - List head should be NULL, but isn't\n" ));
  1118. throw SC_INVALID_DATA;
  1119. #endif
  1120. } // end if
  1121. // read each element
  1122. for( UnsignedShort i = 0; i < count; ++i )
  1123. {
  1124. // alloate a new radar object
  1125. radarObject = newInstance(RadarObject);
  1126. // link to the end of the list
  1127. if( *head == NULL )
  1128. *head = radarObject;
  1129. else
  1130. {
  1131. RadarObject *other;
  1132. for( other = *head; other->friend_getNext() != NULL; other = other->friend_getNext() )
  1133. {
  1134. } // end for, other
  1135. // set the end of the list to point to the new object
  1136. other->friend_setNext( radarObject );
  1137. } // end else
  1138. // load the data
  1139. xfer->xferSnapshot( radarObject );
  1140. } // end for i
  1141. } // end else, load
  1142. } // end xferRadarObjectList
  1143. // ------------------------------------------------------------------------------------------------
  1144. /** Xfer Method
  1145. * Version Info:
  1146. * 1: Initial version */
  1147. // ------------------------------------------------------------------------------------------------
  1148. void Radar::xfer( Xfer *xfer )
  1149. {
  1150. // version
  1151. XferVersion currentVersion = 1;
  1152. XferVersion version = currentVersion;
  1153. xfer->xferVersion( &version, currentVersion );
  1154. // radar hidden
  1155. xfer->xferBool( &m_radarHidden );
  1156. // radar force on
  1157. xfer->xferBool( &m_radarForceOn );
  1158. // save our local object list
  1159. xferRadarObjectList( xfer, &m_localObjectList );
  1160. // save the regular object list
  1161. xferRadarObjectList( xfer, &m_objectList );
  1162. // save the radar event count and data
  1163. UnsignedShort eventCountVerify = MAX_RADAR_EVENTS;
  1164. UnsignedShort eventCount = eventCountVerify;
  1165. xfer->xferUnsignedShort( &eventCount );
  1166. if( eventCount != eventCountVerify )
  1167. {
  1168. DEBUG_CRASH(( "Radar::xfer - size of MAX_RADAR_EVENTS has changed, you must version this xfer method to accomodate the new array size. Was '%d' and is now '%d'\n",
  1169. eventCount, eventCountVerify ));
  1170. throw SC_INVALID_DATA;
  1171. } // end if
  1172. for( UnsignedShort i = 0; i < eventCount; ++i )
  1173. {
  1174. // xfer event data
  1175. xfer->xferUser( &m_event[ i ].type, sizeof( RadarEventType ) );
  1176. xfer->xferBool( &m_event[ i ].active );
  1177. xfer->xferUnsignedInt( &m_event[ i ].createFrame );
  1178. xfer->xferUnsignedInt( &m_event[ i ].dieFrame );
  1179. xfer->xferUnsignedInt( &m_event[ i ].fadeFrame );
  1180. xfer->xferRGBAColorInt( &m_event[ i ].color1 );
  1181. xfer->xferRGBAColorInt( &m_event[ i ].color2 );
  1182. xfer->xferCoord3D( &m_event[ i ].worldLoc );
  1183. xfer->xferICoord2D( &m_event[ i ].radarLoc );
  1184. xfer->xferBool( &m_event[ i ].soundPlayed );
  1185. } // end for i
  1186. // next event index
  1187. xfer->xferInt( &m_nextFreeRadarEvent );
  1188. // last event index
  1189. xfer->xferInt( &m_lastRadarEvent );
  1190. } // end xfer
  1191. // ------------------------------------------------------------------------------------------------
  1192. /** Load post process */
  1193. // ------------------------------------------------------------------------------------------------
  1194. void Radar::loadPostProcess( void )
  1195. {
  1196. //
  1197. // refresh the radar texture now that all the objects (specifically bridges) have
  1198. // been loaded with their correct damage states from save game file
  1199. //
  1200. refreshTerrain( TheTerrainLogic );
  1201. } // end loadPostProcess
  1202. // ------------------------------------------------------------------------------------------------
  1203. /** Is the priority type passed in a "visible" one that can show up on the radar */
  1204. // ------------------------------------------------------------------------------------------------
  1205. Bool Radar::isPriorityVisible( RadarPriorityType priority ) const
  1206. {
  1207. switch( priority )
  1208. {
  1209. case RADAR_PRIORITY_INVALID:
  1210. case RADAR_PRIORITY_NOT_ON_RADAR:
  1211. return FALSE;
  1212. default:
  1213. return TRUE;
  1214. } // end switch
  1215. } // end isPriorityVisible