Radar.cpp 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568
  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. // 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;
  274. Int terrainSamples = 0, waterSamples = 0;
  275. m_terrainAverageZ = 0.0f;
  276. m_waterAverageZ = 0.0f;
  277. Coord3D worldPoint;
  278. // since we're averaging let's skip every second sample...
  279. worldPoint.y=0;
  280. for( y = 0; y < RADAR_CELL_HEIGHT; y+=2, worldPoint.y+=2.0*m_ySample )
  281. {
  282. worldPoint.x=0;
  283. for( x = 0; x < RADAR_CELL_WIDTH; x+=2, worldPoint.x+=2.0*m_xSample )
  284. {
  285. // don't use this, we don't really need the
  286. // Z position by this function... radarToWorld( &radarPoint, &worldPoint );
  287. // and this is done by isUnderwater anyway: z = terrain->getGroundHeight( worldPoint.x, worldPoint.y );
  288. Real z,waterZ;
  289. if( terrain->isUnderwater( worldPoint.x, worldPoint.y, &waterZ, &z ) )
  290. {
  291. m_waterAverageZ += z;
  292. waterSamples++;
  293. }
  294. else
  295. {
  296. m_terrainAverageZ += z;
  297. terrainSamples++;
  298. }
  299. } // end for x
  300. }
  301. // avoid divide by zeros
  302. if( terrainSamples == 0 )
  303. terrainSamples = 1;
  304. if( waterSamples == 0 )
  305. waterSamples = 1;
  306. // compute averages
  307. m_terrainAverageZ = m_terrainAverageZ / INT_TO_REAL( terrainSamples );
  308. m_waterAverageZ = m_waterAverageZ / INT_TO_REAL( waterSamples );
  309. } // end newMap
  310. //-------------------------------------------------------------------------------------------------
  311. /** Add an object to the radar list. The object will be sorted in the list to be grouped
  312. * using it's radar priority */
  313. //-------------------------------------------------------------------------------------------------
  314. void Radar::addObject( Object *obj )
  315. {
  316. // get the radar priority for this object
  317. RadarPriorityType newPriority = obj->getRadarPriority();
  318. if( isPriorityVisible( newPriority ) == FALSE )
  319. return;
  320. // if this object is on the radar, remove it in favor of the new add
  321. RadarObject **list;
  322. RadarObject *newObj;
  323. // sanity
  324. DEBUG_ASSERTCRASH( obj->friend_getRadarData() == NULL,
  325. ("Radar: addObject - non NULL radar data for '%s'\n",
  326. obj->getTemplate()->getName().str()) );
  327. // allocate a new object
  328. newObj = newInstance(RadarObject);
  329. // set the object data
  330. newObj->friend_setObject( obj );
  331. // set color for this object on the radar
  332. const Player *player = obj->getControllingPlayer();
  333. Bool useIndicatorColor = true;
  334. if( obj->isKindOf( KINDOF_DISGUISER ) )
  335. {
  336. //Because we have support for disguised units pretending to be units from another
  337. //team, we need to intercept it here and make sure it's rendered appropriately
  338. //based on which client is rendering it.
  339. StealthUpdate *update = obj->getStealth();
  340. if( update )
  341. {
  342. if( update->isDisguised() )
  343. {
  344. Player *clientPlayer = ThePlayerList->getLocalPlayer();
  345. Player *disguisedPlayer = ThePlayerList->getNthPlayer( update->getDisguisedPlayerIndex() );
  346. if( player->getRelationship( clientPlayer->getDefaultTeam() ) != ALLIES && clientPlayer->isPlayerActive() )
  347. {
  348. //Neutrals and enemies will see this disguised unit as the team it's disguised as.
  349. player = disguisedPlayer;
  350. if( player )
  351. useIndicatorColor = false;
  352. }
  353. //Otherwise, the color will show up as the team it really belongs to (already set above).
  354. }
  355. }
  356. }
  357. if( obj->getContain() )
  358. {
  359. // To handle Stealth garrison, ask containers what color they are drawing with to the local player.
  360. // Local is okay because radar display is not synced.
  361. player = obj->getContain()->getApparentControllingPlayer( ThePlayerList->getLocalPlayer() );
  362. if( player )
  363. useIndicatorColor = false;
  364. }
  365. if( useIndicatorColor || (player == NULL) )
  366. {
  367. newObj->setColor( obj->getIndicatorColor() );
  368. }
  369. else
  370. {
  371. newObj->setColor( player->getPlayerColor() );
  372. }
  373. // set a chunk of radar data in the object
  374. obj->friend_setRadarData( newObj );
  375. //
  376. // we will put this on either the local object list for objects that belong to the
  377. // local player, or on the regular object list for all other objects
  378. //
  379. if( obj->isLocallyControlled() )
  380. list = &m_localObjectList;
  381. else
  382. list = &m_objectList;
  383. // link object to master list at the head of it's priority section
  384. if( *list == NULL )
  385. *list = newObj; // trivial case, an empty list
  386. else
  387. {
  388. RadarPriorityType prevPriority, currPriority;
  389. RadarObject *currObject, *prevObject, *nextObject;
  390. prevObject = NULL;
  391. prevPriority = RADAR_PRIORITY_INVALID;
  392. for( currObject = *list; currObject; currObject = nextObject )
  393. {
  394. // get the next object
  395. nextObject = currObject->friend_getNext();
  396. // get the priority of this entry in the list (currPriority)
  397. currPriority = currObject->friend_getObject()->getRadarPriority();
  398. //
  399. // if there is no previous object, or the previous priority is less than the
  400. // our new priority, and the current object in the list has a priority
  401. // higher than our equal to our own we need to be inserted here
  402. //
  403. if( (prevObject == NULL || prevPriority < newPriority ) &&
  404. (currPriority >= newPriority) )
  405. {
  406. // insert into the list just ahead of currObject
  407. if( prevObject )
  408. {
  409. // the new entry next points to what the previous one used to point to
  410. newObj->friend_setNext( prevObject->friend_getNext() );
  411. // the previous one next now points to the new entry
  412. prevObject->friend_setNext( newObj );
  413. } // end if
  414. else
  415. {
  416. // the new object next points to the current object
  417. newObj->friend_setNext( currObject );
  418. // new list head is now newObj
  419. *list = newObj;
  420. } // end else
  421. break; // exit for, stop the insert
  422. } // end if
  423. else if( nextObject == NULL )
  424. {
  425. // at the end of the list, put object here
  426. currObject->friend_setNext( newObj );
  427. } // end else if
  428. // our current object is now the previous object
  429. prevObject = currObject;
  430. prevPriority = currPriority;
  431. } // end if
  432. } // end else
  433. } // end addObject
  434. //-------------------------------------------------------------------------------------------------
  435. /** Try to delete an object from a specific list */
  436. //-------------------------------------------------------------------------------------------------
  437. Bool Radar::deleteFromList( Object *obj, RadarObject **list )
  438. {
  439. RadarObject *radarObject, *prevObject = NULL;
  440. // find the object in list
  441. for( radarObject = *list; radarObject; radarObject = radarObject->friend_getNext() )
  442. {
  443. if( radarObject->friend_getObject() == obj )
  444. {
  445. // unlink the object from list
  446. if( prevObject == NULL )
  447. *list = radarObject->friend_getNext(); // removing head of list
  448. else
  449. prevObject->friend_setNext( radarObject->friend_getNext() );
  450. // set the object radar data to NULL
  451. obj->friend_setRadarData( NULL );
  452. // delete the object instance
  453. radarObject->deleteInstance();
  454. // all done, object found and deleted
  455. return TRUE;
  456. } // end if
  457. // save this object as previous one encountered in the list
  458. prevObject = radarObject;
  459. } // end for, radarObject
  460. // object was not found in this list
  461. return FALSE;
  462. } // end deleteFromList
  463. //-------------------------------------------------------------------------------------------------
  464. /** Remove an object from the radar, the object may reside in any list */
  465. //-------------------------------------------------------------------------------------------------
  466. void Radar::removeObject( Object *obj )
  467. {
  468. // sanity
  469. if( obj->friend_getRadarData() == NULL )
  470. return;
  471. if( deleteFromList( obj, &m_localObjectList ) == TRUE )
  472. return;
  473. else if( deleteFromList( obj, &m_objectList ) == TRUE )
  474. return;
  475. else
  476. {
  477. // sanity
  478. DEBUG_ASSERTCRASH( 0, ("Radar: Tried to remove object '%s' which was not found\n",
  479. obj->getTemplate()->getName().str()) );
  480. } // end else
  481. } // end removeObject
  482. //-------------------------------------------------------------------------------------------------
  483. /** Translate a 2D spot on the radar (from (0,0) to (RADAR_CELL_WIDTH,RADAR_CELL_HEIGHT)
  484. * to a 3D spot in the world. Does not determine Z value!
  485. * Return TRUE if the radar points translates to a valid world position
  486. * Return FALSE if the radar point is not a valid world position */
  487. //-------------------------------------------------------------------------------------------------
  488. Bool Radar::radarToWorld2D( const ICoord2D *radar, Coord3D *world )
  489. {
  490. Int x, y;
  491. // sanity
  492. if( radar == NULL || world == NULL )
  493. return FALSE;
  494. // get the coords
  495. x = radar->x;
  496. y = radar->y;
  497. // more sanity
  498. if( x < 0 )
  499. x = 0;
  500. if( x >= RADAR_CELL_WIDTH )
  501. x = RADAR_CELL_WIDTH - 1;
  502. if( y < 0 )
  503. y = 0;
  504. if( y >= RADAR_CELL_HEIGHT )
  505. y = RADAR_CELL_HEIGHT - 1;
  506. // translate to world
  507. world->x = x * m_xSample;
  508. world->y = y * m_ySample;
  509. return TRUE;
  510. }
  511. //-------------------------------------------------------------------------------------------------
  512. /** Translate a 2D spot on the radar (from (0,0) to (RADAR_CELL_WIDTH,RADAR_CELL_HEIGHT)
  513. * to a 3D spot in the world on the terrain
  514. * Return TRUE if the radar points translates to a valid world position
  515. * Return FALSE if the radar point is not a valid world position */
  516. //-------------------------------------------------------------------------------------------------
  517. Bool Radar::radarToWorld( const ICoord2D *radar, Coord3D *world )
  518. {
  519. if (!radarToWorld2D(radar,world))
  520. return FALSE;
  521. // find the terrain height here
  522. world->z = TheTerrainLogic->getGroundHeight( world->x, world->y );
  523. return TRUE; // valid translation
  524. } // end radarToWorld
  525. //-------------------------------------------------------------------------------------------------
  526. /** Translate a point in the world to the 2D radar (x,y)
  527. * Return TRUE if the world point successfully translates to a radar point
  528. * Return FALSE if world point is a bogus off the map position */
  529. //-------------------------------------------------------------------------------------------------
  530. Bool Radar::worldToRadar( const Coord3D *world, ICoord2D *radar )
  531. {
  532. // sanity
  533. if( world == NULL || radar == NULL )
  534. return FALSE;
  535. // sanity check the world position
  536. // if( world->x < m_mapExtent.lo.x || world->x > m_mapExtent.hi.x ||
  537. // world->y < m_mapExtent.lo.y || world->y > m_mapExtent.hi.y )
  538. // return FALSE;
  539. // This is actually an insanity check. Nobody uses the return value, so this just leaves garbage in the
  540. // return pointer. The reason the question gets asked is there are 60 partition cells to 128 radar cells
  541. // (for example), and the radar wants to draw a horizontal line. This line ends up three pixels long
  542. // at the right side, so the radar gives up and doesn't draw the middle one.
  543. // We bind to on radar anyway, and we only ask if we are on radar, so don't intentionally add edge weirdness.
  544. // convert
  545. radar->x = world->x / m_xSample;
  546. radar->y = world->y / m_ySample;
  547. // keep it in bounds
  548. if( radar->x < 0 )
  549. radar->x = 0;
  550. if( radar->x >= RADAR_CELL_WIDTH )
  551. radar->x = RADAR_CELL_WIDTH - 1;
  552. if( radar->y < 0 )
  553. radar->y = 0;
  554. if( radar->y >= RADAR_CELL_HEIGHT )
  555. radar->y = RADAR_CELL_HEIGHT - 1;
  556. return TRUE; // valid translation
  557. } // end worldToRadar
  558. // ------------------------------------------------------------------------------------------------
  559. /** Translate an actual pixel location (relative pixel with (0,0) being the top left of
  560. * the radar area) to the "logical" radar coords that would cover the entire area of display
  561. * on the screen. This is needed because some maps are "long" or "tall" and need a translation
  562. * to any radar image that has been scaled to preserve the map aspect ratio */
  563. // ------------------------------------------------------------------------------------------------
  564. Bool Radar::localPixelToRadar( const ICoord2D *pixel, ICoord2D *radar )
  565. {
  566. // sanity
  567. if( pixel == NULL || radar == NULL )
  568. return FALSE;
  569. // get window size of the radar
  570. ICoord2D size;
  571. m_radarWindow->winGetSize( &size.x, &size.y );
  572. //
  573. // act like we're going to draw and find the aspect ratio adjusted points of the
  574. // terrain radar positions
  575. //
  576. ICoord2D start = { 0, 0 };
  577. ICoord2D ul, lr;
  578. findDrawPositions( start.x, start.y, size.x, size.y, &ul, &lr );
  579. // get the scaled width and height
  580. Int scaledWidth = lr.x - ul.x;
  581. Int scaledHeight = lr.y - ul.y;
  582. // if the pixel is outsize of the adjusted radar area there are no logical coords
  583. if( pixel->x < ul.x || pixel->x > lr.x ||
  584. pixel->y < ul.y || pixel->y > lr.y )
  585. return FALSE;
  586. if( scaledWidth >= scaledHeight )
  587. {
  588. // just normal conversion from full stretched to radar cells
  589. radar->x = (pixel->x - ul.x)* RADAR_CELL_WIDTH / scaledWidth;
  590. // conversion for scaled Y direction in map
  591. radar->y = REAL_TO_INT( ((pixel->y - ul.y) / INT_TO_REAL( scaledHeight )) * size.y );
  592. //
  593. // radar->y now refers to a point that was "as if" the map was square, translate to radar
  594. // note that y is inverted to have the radar align with the world (+x = right, -y = down)
  595. //
  596. radar->y = (size.y - radar->y) * RADAR_CELL_HEIGHT / size.y;
  597. } // end if
  598. else
  599. {
  600. // conversion for scaled Y direction in map
  601. radar->x = REAL_TO_INT( ((pixel->x - ul.x) / INT_TO_REAL( scaledWidth )) * size.x );
  602. // radar->x now refers to a point that was "as if" the map was square, translate to radar
  603. radar->x = radar->x * RADAR_CELL_WIDTH / size.x;
  604. //
  605. // just normal conversion from full stretched to radar cells, note that y is inverted
  606. // to have the radar align with the world (+x = right, -y = down)
  607. //
  608. radar->y = (size.y - pixel->y) * RADAR_CELL_HEIGHT / size.y;
  609. } // end else
  610. return TRUE;
  611. } // end localPixelToRadar
  612. // ------------------------------------------------------------------------------------------------
  613. /** Translate a screen mouse position to world coords if the screen position is within
  614. * the radar window and that spot in the radar corresponds to a point in the world */
  615. // ------------------------------------------------------------------------------------------------
  616. Bool Radar::screenPixelToWorld( const ICoord2D *pixel, Coord3D *world )
  617. {
  618. // sanity
  619. if( pixel == NULL || world == NULL )
  620. return FALSE;
  621. // if we have no radar window can't do the conversion
  622. if( m_radarWindow == NULL )
  623. return FALSE;
  624. // translate pixel coords to local pixel coords relative to the radar window
  625. ICoord2D localPixel;
  626. ICoord2D screenPos;
  627. m_radarWindow->winGetScreenPosition( &screenPos.x, &screenPos.y );
  628. localPixel.x = pixel->x - screenPos.x;
  629. localPixel.y = pixel->y - screenPos.y;
  630. // translate local pixel to radar
  631. ICoord2D radar;
  632. if( localPixelToRadar( &localPixel, &radar ) == FALSE )
  633. return FALSE;
  634. // translate radar to world
  635. return radarToWorld( &radar, world );
  636. } // end screenPixelToWorld
  637. // ------------------------------------------------------------------------------------------------
  638. /** Given the pixel coordinates, see if there is an object that is exactly in this
  639. * spot represented on the radar */
  640. // ------------------------------------------------------------------------------------------------
  641. Object *Radar::objectUnderRadarPixel( const ICoord2D *pixel )
  642. {
  643. // sanity
  644. if( pixel == NULL )
  645. return NULL;
  646. // convert pixel location to radar logical radar location
  647. ICoord2D radar;
  648. if( localPixelToRadar( pixel, &radar ) == FALSE )
  649. return NULL;
  650. // object we will return
  651. Object *obj = NULL;
  652. //
  653. // scan the objects on the radar list and return any object that maps its world location
  654. // to the radar location
  655. //
  656. // search the local object list
  657. obj = searchListForRadarLocationMatch( m_localObjectList, &radar );
  658. // search all other objects if not found
  659. if( obj == NULL )
  660. obj = searchListForRadarLocationMatch( m_objectList, &radar );
  661. // return the object found (if any)
  662. return obj;
  663. } // end objectUnderRadarPixel
  664. // ------------------------------------------------------------------------------------------------
  665. /** Search the object list for an object that maps to the given logical radar coords */
  666. // ------------------------------------------------------------------------------------------------
  667. Object *Radar::searchListForRadarLocationMatch( RadarObject *listHead, ICoord2D *radarMatch )
  668. {
  669. // sanity
  670. if( listHead == NULL || radarMatch == NULL )
  671. return NULL;
  672. // scan the list
  673. RadarObject *radarObject;
  674. ICoord2D radar;
  675. for( radarObject = listHead; radarObject; radarObject = radarObject->friend_getNext() )
  676. {
  677. // get object
  678. Object *obj = radarObject->friend_getObject();
  679. // sanity
  680. if( obj == NULL )
  681. {
  682. DEBUG_CRASH(( "Radar::searchListForRadarLocationMatch - NULL object encountered in list\n" ));
  683. continue;
  684. } // end if
  685. // convert object position to logical radar
  686. worldToRadar( obj->getPosition(), &radar );
  687. // see if this matches our match radar location
  688. if( radar.x >= radarMatch->x - 1 &&
  689. radar.x <= radarMatch->x + 1 &&
  690. radar.y >= radarMatch->y - 1 &&
  691. radar.y <= radarMatch->y + 1 )
  692. return obj;
  693. } // end for, radarObject
  694. // no match found
  695. return NULL;
  696. } // end searchListForRadarLocationMatch
  697. // ------------------------------------------------------------------------------------------------
  698. /** Given the RELATIVE SCREEN start X and Y, the width and height of the area to draw the whole
  699. * radar in, compute what the upper left (ul) and lower right (lr) local coordinates are
  700. * that represent the actual terrain image part of the radar that will preserve the
  701. * aspect ratio of the map */
  702. // ------------------------------------------------------------------------------------------------
  703. void Radar::findDrawPositions( Int startX, Int startY, Int width, Int height,
  704. ICoord2D *ul, ICoord2D *lr )
  705. {
  706. Real ratioWidth;
  707. Real ratioHeight;
  708. Coord2D radar;
  709. ratioWidth = m_mapExtent.width()/(width * 1.0f);
  710. ratioHeight = m_mapExtent.height()/(height* 1.0f);
  711. if( ratioWidth >= ratioHeight)
  712. {
  713. radar.x = m_mapExtent.width() / ratioWidth;
  714. radar.y = m_mapExtent.height()/ ratioWidth;
  715. ul->x = 0;
  716. ul->y = (height - radar.y) / 2.0f;
  717. lr->x = radar.x;
  718. lr->y = height - ul->y;
  719. }
  720. else
  721. {
  722. radar.x = m_mapExtent.width() / ratioHeight;
  723. radar.y = m_mapExtent.height()/ ratioHeight;
  724. ul->x = (width - radar.x ) / 2.0f;
  725. ul->y = 0;
  726. lr->x = width - ul->x;
  727. lr->y = radar.y;
  728. }
  729. /*
  730. if( m_mapExtent.width() > m_mapExtent.height() )
  731. {
  732. //
  733. // +---------------+
  734. // | |
  735. // | |
  736. // +---------------+
  737. // | map area |
  738. // +---------------+
  739. // | |
  740. // | |
  741. // +---------------+
  742. //
  743. ul->x = 0;
  744. ul->y = (height - (m_mapExtent.height() / m_mapExtent.width() * height)) / 2.0f;
  745. lr->x = width;
  746. lr->y = height - ul->y;
  747. } // end if
  748. else if( m_mapExtent.height() > m_mapExtent.width() )
  749. {
  750. // +-----+---+-----+
  751. // | | m | |
  752. // | | a | |
  753. // | | p | |
  754. // | | | |
  755. // | | a | |
  756. // | | r | |
  757. // | | e | |
  758. // | | a | |
  759. // +-----+---+-----+
  760. //
  761. ul->x = (width - (m_mapExtent.width() / m_mapExtent.height() * width)) / 2.0f;
  762. ul->y = 0;
  763. lr->x = width - ul->x;
  764. lr->y = height;
  765. } // end else
  766. else
  767. {
  768. ul->x = 0;
  769. ul->y = 0;
  770. lr->x = width;
  771. lr->y = height;
  772. } // end else
  773. */
  774. // make them pixel positions
  775. ul->x += startX;
  776. ul->y += startY;
  777. lr->x += startX;
  778. lr->y += startY;
  779. } // end findDrawPositions
  780. //-------------------------------------------------------------------------------------------------
  781. /** Radar color lookup table */
  782. //-------------------------------------------------------------------------------------------------
  783. struct RadarColorLookup
  784. {
  785. RadarEventType event;
  786. RGBAColorInt color1;
  787. RGBAColorInt color2;
  788. };
  789. static RadarColorLookup radarColorLookupTable[] =
  790. {
  791. /* Radar Event Color 1 Color 2 */
  792. { RADAR_EVENT_CONSTRUCTION, { 128, 128, 255, 255 }, { 128, 255, 255, 255 } },
  793. { RADAR_EVENT_UPGRADE, { 128, 0, 64, 255 }, { 255, 185, 220, 255 } },
  794. { RADAR_EVENT_UNDER_ATTACK, { 255, 0, 0, 255 }, { 255, 128, 128, 255 } },
  795. { RADAR_EVENT_INFORMATION, { 255, 255, 0, 255 }, { 255, 255, 128, 255 } },
  796. { RADAR_EVENT_BEACON_PULSE, { 255, 255, 0, 255 }, { 255, 255, 128, 255 } },
  797. { RADAR_EVENT_INFILTRATION, { 0, 255, 255, 255 }, { 128, 255, 255, 255 } },
  798. { RADAR_EVENT_BATTLE_PLAN, { 255, 255, 255, 255 }, { 255, 255, 255, 255 } },
  799. { RADAR_EVENT_STEALTH_DISCOVERED, { 0, 255, 0, 255 }, { 0, 128, 0, 255 } },
  800. { RADAR_EVENT_STEALTH_NEUTRALIZED, { 0, 255, 0, 255 }, { 0, 128, 0, 255 } },
  801. { RADAR_EVENT_FAKE, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } },
  802. { RADAR_EVENT_INVALID, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } }
  803. };
  804. //-------------------------------------------------------------------------------------------------
  805. /** Create a new radar event */
  806. //-------------------------------------------------------------------------------------------------
  807. void Radar::createEvent( const Coord3D *world, RadarEventType type, Real secondsToLive )
  808. {
  809. // sanity
  810. if( world == NULL )
  811. return;
  812. // lookup the colors we are to used based on the event
  813. RGBAColorInt color[ 2 ];
  814. for( Int i = 0; radarColorLookupTable[ i ].event != RADAR_EVENT_INVALID; ++i )
  815. {
  816. if( radarColorLookupTable[ i ].event == type )
  817. {
  818. color[ 0 ] = radarColorLookupTable[ i ].color1;
  819. color[ 1 ] = radarColorLookupTable[ i ].color2;
  820. break;
  821. } // end if
  822. } // end while
  823. // check for no match found in color table
  824. if( radarColorLookupTable[ i ].event == RADAR_EVENT_INVALID )
  825. {
  826. static RGBAColorInt color1 = { 255, 255, 255, 255 };
  827. static RGBAColorInt color2 = { 255, 255, 255, 255 };
  828. DEBUG_CRASH(( "Radar::createEvent - Event not found in color table, using default colors\n" ));
  829. color[ 0 ] = color1;
  830. color[ 1 ] = color2;
  831. } // end if
  832. // call the internal method to create the event with these colors
  833. internalCreateEvent( world, type, secondsToLive, &color[ 0 ], &color[ 1 ] );
  834. } // end createEvent
  835. // ------------------------------------------------------------------------------------------------
  836. /** Create radar event using a specific colors from the player */
  837. // ------------------------------------------------------------------------------------------------
  838. void Radar::createPlayerEvent( Player *player, const Coord3D *world,
  839. RadarEventType type, Real secondsToLive )
  840. {
  841. // sanity
  842. if( player == NULL || world == NULL )
  843. return;
  844. // figure out the two colors we should use
  845. Color c;
  846. UnsignedByte r, g, b, a;
  847. RGBAColorInt color[ 2 ];
  848. // color 1
  849. c = player->getPlayerColor();
  850. GameGetColorComponents( c, &r, &g, &b, &a );
  851. color[ 0 ].red = r;
  852. color[ 0 ].green = g;
  853. color[ 0 ].blue = b;
  854. color[ 0 ].alpha = a;
  855. // the second will be a darker color 1
  856. Real darkScale = 0.75f;
  857. color[ 1 ] = color[ 0 ];
  858. color[ 1 ].red -= REAL_TO_INT( color[ 0 ].red * darkScale );
  859. if( color[ 1 ].red < 0 )
  860. color[ 1 ].red = 0;
  861. color[ 1 ].green -= REAL_TO_INT( color[ 0 ].green * darkScale );
  862. if( color[ 1 ].green < 0 )
  863. color[ 1 ].green = 0;
  864. color[ 1 ].blue -= REAL_TO_INT( color[ 0 ].blue * darkScale );
  865. if( color[ 1 ].blue < 0 )
  866. color[ 1 ].blue = 0;
  867. // create the events using these colors
  868. internalCreateEvent( world, type, secondsToLive, &color[ 0 ], &color[ 1 ] );
  869. } // end createPlayerEvent
  870. //-------------------------------------------------------------------------------------------------
  871. /** Create a new radar event */
  872. //-------------------------------------------------------------------------------------------------
  873. void Radar::internalCreateEvent( const Coord3D *world, RadarEventType type, Real secondsToLive,
  874. const RGBAColorInt *color1, const RGBAColorInt *color2 )
  875. {
  876. static Real secondsBeforeDieToFade = 0.5f; ///< this many seconds before we hit the die frame we start to fade away
  877. // sanity
  878. if( world == NULL || color1 == NULL || color2 == NULL )
  879. return;
  880. // translate the world coord to radar coords
  881. ICoord2D radar;
  882. worldToRadar( world, &radar );
  883. // add to the list of radar events
  884. m_event[ m_nextFreeRadarEvent ].type = type;
  885. m_event[ m_nextFreeRadarEvent ].active = TRUE;
  886. m_event[ m_nextFreeRadarEvent ].createFrame = TheGameLogic->getFrame();
  887. m_event[ m_nextFreeRadarEvent ].dieFrame = TheGameLogic->getFrame() + LOGICFRAMES_PER_SECOND * secondsToLive;
  888. m_event[ m_nextFreeRadarEvent ].fadeFrame = m_event[ m_nextFreeRadarEvent ].dieFrame - LOGICFRAMES_PER_SECOND * secondsBeforeDieToFade;
  889. m_event[ m_nextFreeRadarEvent ].color1 = *color1;
  890. m_event[ m_nextFreeRadarEvent ].color2 = *color2;
  891. m_event[ m_nextFreeRadarEvent ].worldLoc = *world;
  892. m_event[ m_nextFreeRadarEvent ].radarLoc = radar;
  893. m_event[ m_nextFreeRadarEvent ].soundPlayed = FALSE;
  894. // record the index of this, our "last" radar event.
  895. if ( type != RADAR_EVENT_BEACON_PULSE )
  896. m_lastRadarEvent = m_nextFreeRadarEvent;
  897. //
  898. // increment the next radar event index, wrapping to the beginning. If we ever have so many
  899. // events that they fill up the buffer the oldest ones will just drop off, eh ... should be fine.
  900. //
  901. m_nextFreeRadarEvent++;
  902. if( m_nextFreeRadarEvent >= MAX_RADAR_EVENTS )
  903. m_nextFreeRadarEvent = 0;
  904. } // end createEvent
  905. //-------------------------------------------------------------------------------------------------
  906. /** Get the last event position, if any.
  907. * Returns TRUE if event was found
  908. * Returns FALSE if there is no "last event" */
  909. //-------------------------------------------------------------------------------------------------
  910. Bool Radar::getLastEventLoc( Coord3D *eventPos )
  911. {
  912. // if we have an index for the last event, one was present
  913. if( m_lastRadarEvent != -1 )
  914. {
  915. if( eventPos )
  916. *eventPos = m_event[ m_lastRadarEvent ].worldLoc;
  917. return TRUE;
  918. } // end if
  919. return FALSE; // no last event
  920. } // end getLastEventLoc
  921. // ------------------------------------------------------------------------------------------------
  922. /** Try to create a radar event for "we're under attack". This will be called every time
  923. * actual damage is dealt to an object that the player owns that shows up on the radar.
  924. * We don't want to create under attack events every time we are damaged and we also want
  925. * to limit them based on time and local area of other recent attack events */
  926. // ------------------------------------------------------------------------------------------------
  927. void Radar::tryUnderAttackEvent( const Object *obj )
  928. {
  929. // sanity
  930. if( obj == NULL )
  931. return;
  932. // try to create the event
  933. Bool eventCreated = tryEvent( RADAR_EVENT_UNDER_ATTACK, obj->getPosition() );
  934. // if event created, do some more feedback
  935. if( eventCreated )
  936. {
  937. TheControlBar->triggerRadarAttackGlow();
  938. //
  939. ///@todo Should make an INI data driven table for radar event strings, and audio events
  940. //
  941. // UI feedback for being under attack (note that we display these messages and audio
  942. // queues even if we don't have a radar)
  943. //
  944. Player *player = ThePlayerList->getLocalPlayer();
  945. // create a message for the attack event
  946. if( obj->isKindOf( KINDOF_INFANTRY ) || obj->isKindOf( KINDOF_VEHICLE ) )
  947. {
  948. AudioEventRTS unitAttackSound;
  949. if( obj->isKindOf(KINDOF_HARVESTER) )
  950. {
  951. // display special message
  952. TheInGameUI->message( "RADAR:HarvesterUnderAttack" );
  953. // play special audio event
  954. unitAttackSound = TheAudio->getMiscAudio()->m_radarHarvesterUnderAttackSound;
  955. }
  956. else
  957. {
  958. // display message
  959. TheInGameUI->message( "RADAR:UnitUnderAttack" );
  960. // play audio event
  961. unitAttackSound = TheAudio->getMiscAudio()->m_radarStructureUnderAttackSound;
  962. }
  963. unitAttackSound.setPlayerIndex(player->getPlayerIndex());
  964. TheAudio->addAudioEvent( &unitAttackSound );
  965. } // end if
  966. else if( obj->isKindOf( KINDOF_STRUCTURE ) && obj->isKindOf( KINDOF_MP_COUNT_FOR_VICTORY ) )
  967. {
  968. // play EVA. If its our object, play Base under attack.
  969. if (obj->getControllingPlayer()->isLocalPlayer())
  970. TheEva->setShouldPlay(EVA_BaseUnderAttack);
  971. else if (ThePlayerList->getLocalPlayer()->getRelationship(obj->getTeam()) == ALLIES)
  972. TheEva->setShouldPlay(EVA_AllyUnderAttack);
  973. // display message
  974. TheInGameUI->message( "RADAR:StructureUnderAttack" );
  975. // play audio event
  976. static AudioEventRTS structureAttackSound = TheAudio->getMiscAudio()->m_radarStructureUnderAttackSound;
  977. structureAttackSound.setPlayerIndex(player->getPlayerIndex());
  978. TheAudio->addAudioEvent( &structureAttackSound );
  979. } // end else if
  980. else
  981. {
  982. // display message
  983. TheInGameUI->message( "RADAR:UnderAttack" );
  984. // play audio event
  985. static AudioEventRTS underAttackSound = TheAudio->getMiscAudio()->m_radarStructureUnderAttackSound;
  986. underAttackSound.setPlayerIndex(player->getPlayerIndex());
  987. TheAudio->addAudioEvent( &underAttackSound );
  988. } // end else
  989. } // end if
  990. } // end tryUnderAttackEvent
  991. // ------------------------------------------------------------------------------------------------
  992. /** Try to create a radar event for "infiltration".
  993. This happens whenever a unit is hijacked, defected, converted to carbomb, hacked, or
  994. otherwise snuck into */
  995. // ------------------------------------------------------------------------------------------------
  996. void Radar::tryInfiltrationEvent( const Object *obj )
  997. {
  998. //Sanity!
  999. if( !obj )
  1000. {
  1001. return;
  1002. }
  1003. // We should only be warned against infiltrations that are taking place against us.
  1004. if( obj->getControllingPlayer() != ThePlayerList->getLocalPlayer() )
  1005. return;
  1006. // create the radar event
  1007. createEvent( obj->getPosition(), RADAR_EVENT_INFILTRATION );
  1008. //
  1009. ///@todo Should make an INI data driven table for radar event strings, and audio events
  1010. //
  1011. // UI feedback for being under attack (note that we display these messages and audio
  1012. // queues even if we don't have a radar)
  1013. //
  1014. Player *player = ThePlayerList->getLocalPlayer();
  1015. // display message
  1016. TheInGameUI->message( "RADAR:Infiltration" );
  1017. // play audio event
  1018. static AudioEventRTS infiltrationWarningSound = TheAudio->getMiscAudio()->m_radarInfiltrationSound;
  1019. infiltrationWarningSound.setPlayerIndex(player->getPlayerIndex());
  1020. TheAudio->addAudioEvent( &infiltrationWarningSound );
  1021. } // end tryInfiltrationEvent
  1022. // ------------------------------------------------------------------------------------------------
  1023. // ------------------------------------------------------------------------------------------------
  1024. Bool Radar::tryEvent( RadarEventType event, const Coord3D *pos )
  1025. {
  1026. // sanity
  1027. if( event <= RADAR_EVENT_INVALID || event >= RADAR_EVENT_NUM_EVENTS || pos == NULL )
  1028. return FALSE;
  1029. // see if there was an event of this type within the given range within the given time
  1030. UnsignedInt currentFrame = TheGameLogic->getFrame();
  1031. const Real closeEnoughDistanceSq = 250.0f * 250.0f;
  1032. const UnsignedInt framesBetweenEvents = LOGICFRAMES_PER_SECOND * 10;
  1033. //
  1034. // see if there was any matching radar event within range of this one
  1035. // that wasn't too long ago, if there was we won't create another and get outta here
  1036. //
  1037. for( Int i = 0; i < MAX_RADAR_EVENTS; ++i )
  1038. {
  1039. // only pay attention to under attack events
  1040. if( m_event[ i ].type == event )
  1041. {
  1042. // get distance from our new event location to this event location in 2D
  1043. Real distSquared = m_event[ i ].worldLoc.x - pos->x * m_event[ i ].worldLoc.x - pos->x +
  1044. m_event[ i ].worldLoc.y - pos->y * m_event[ i ].worldLoc.y - pos->y;
  1045. if( distSquared <= closeEnoughDistanceSq )
  1046. {
  1047. // finally only reject making a new event of this existing one is "recent enough"
  1048. if( currentFrame - m_event[ i ].createFrame < framesBetweenEvents )
  1049. return FALSE; // reject it
  1050. } // end if
  1051. } // end if
  1052. } // end for i
  1053. // if we got here then we want to create a new event
  1054. createEvent( pos, event );
  1055. // return TRUE for successfully created event
  1056. return TRUE;
  1057. } // end tryEvent
  1058. // ------------------------------------------------------------------------------------------------
  1059. // ------------------------------------------------------------------------------------------------
  1060. void Radar::refreshTerrain( TerrainLogic *terrain )
  1061. {
  1062. // no future queue is valid now
  1063. m_queueTerrainRefreshFrame = 0;
  1064. } // end refreshTerrain
  1065. // ------------------------------------------------------------------------------------------------
  1066. /** Queue a refresh of the radar terrain, we have this so that if there is code that
  1067. * rapidly needs to refresh the radar, it should use this so we aren't continually
  1068. * rebuilding the radar graphic because that process is slow. If you need to update
  1069. * the terrain on the radar immediately use refreshTerrain() */
  1070. // ------------------------------------------------------------------------------------------------
  1071. void Radar::queueTerrainRefresh( void )
  1072. {
  1073. //
  1074. // we just simply overwrite the frame we have recorded for a radar refresh. If there was
  1075. // already one there, it's simply just forgotten and whatever changes we wanted to see
  1076. // with that refresh will have to wait until enough time has passed to show these
  1077. // changes as well. why you ask ... well, because if we're calling this in close enough
  1078. // proximity for us to overwrite something, we're changing the terrain features
  1079. // quite often and can't afford the expense of rebuilding the radar visual
  1080. //
  1081. m_queueTerrainRefreshFrame = TheGameLogic->getFrame();
  1082. } // end queueTerrainRefresh
  1083. // ------------------------------------------------------------------------------------------------
  1084. /** CRC */
  1085. // ------------------------------------------------------------------------------------------------
  1086. void Radar::crc( Xfer *xfer )
  1087. {
  1088. } // end crc
  1089. // ------------------------------------------------------------------------------------------------
  1090. /** Xfer a radar object list given the head pointer as a parameter
  1091. * Version Info;
  1092. * 1: Initial version */
  1093. // ------------------------------------------------------------------------------------------------
  1094. static void xferRadarObjectList( Xfer *xfer, RadarObject **head )
  1095. {
  1096. RadarObject *radarObject;
  1097. // sanity
  1098. DEBUG_ASSERTCRASH( head != NULL, ("xferRadarObjectList - Invalid parameters\n" ));
  1099. // version
  1100. XferVersion currentVersion = 1;
  1101. XferVersion version = currentVersion;
  1102. xfer->xferVersion( &version, currentVersion );
  1103. // write could of objects in list
  1104. UnsignedShort count = 0;
  1105. for( radarObject = *head; radarObject; radarObject = radarObject->friend_getNext() )
  1106. count++;
  1107. xfer->xferUnsignedShort( &count );
  1108. // xfer the list data
  1109. if( xfer->getXferMode() == XFER_SAVE )
  1110. {
  1111. // save each object
  1112. for( radarObject = *head; radarObject; radarObject = radarObject->friend_getNext() )
  1113. {
  1114. // save this object
  1115. xfer->xferSnapshot( radarObject );
  1116. } // end for, radarObject
  1117. } // end if, save
  1118. else
  1119. {
  1120. // the list should be empty at this point as we are loading it as a whole
  1121. if( *head != NULL )
  1122. {
  1123. #if 1
  1124. // srj sez: yeah, it SHOULD be, but a few rogue things come into existence (via their ctor) preloaded
  1125. // with stuff (eg, "CabooseFullOfTerrorists"). we immediately destroy 'em, but they don't go away just yet.
  1126. // so just ignore 'em if possible.
  1127. for( radarObject = *head; radarObject; radarObject = radarObject->friend_getNext() )
  1128. {
  1129. if (!radarObject->friend_getObject()->isDestroyed())
  1130. {
  1131. DEBUG_CRASH(( "xferRadarObjectList - List head should be NULL, or contain only destroyed objects\n" ));
  1132. throw SC_INVALID_DATA;
  1133. }
  1134. }
  1135. #else
  1136. DEBUG_CRASH(( "xferRadarObjectList - List head should be NULL, but isn't\n" ));
  1137. throw SC_INVALID_DATA;
  1138. #endif
  1139. } // end if
  1140. // read each element
  1141. for( UnsignedShort i = 0; i < count; ++i )
  1142. {
  1143. // alloate a new radar object
  1144. radarObject = newInstance(RadarObject);
  1145. // link to the end of the list
  1146. if( *head == NULL )
  1147. *head = radarObject;
  1148. else
  1149. {
  1150. RadarObject *other;
  1151. for( other = *head; other->friend_getNext() != NULL; other = other->friend_getNext() )
  1152. {
  1153. } // end for, other
  1154. // set the end of the list to point to the new object
  1155. other->friend_setNext( radarObject );
  1156. } // end else
  1157. // load the data
  1158. xfer->xferSnapshot( radarObject );
  1159. } // end for i
  1160. } // end else, load
  1161. } // end xferRadarObjectList
  1162. // ------------------------------------------------------------------------------------------------
  1163. /** Xfer Method
  1164. * Version Info:
  1165. * 1: Initial version */
  1166. // ------------------------------------------------------------------------------------------------
  1167. void Radar::xfer( Xfer *xfer )
  1168. {
  1169. // version
  1170. XferVersion currentVersion = 1;
  1171. XferVersion version = currentVersion;
  1172. xfer->xferVersion( &version, currentVersion );
  1173. // radar hidden
  1174. xfer->xferBool( &m_radarHidden );
  1175. // radar force on
  1176. xfer->xferBool( &m_radarForceOn );
  1177. // save our local object list
  1178. xferRadarObjectList( xfer, &m_localObjectList );
  1179. // save the regular object list
  1180. xferRadarObjectList( xfer, &m_objectList );
  1181. // save the radar event count and data
  1182. UnsignedShort eventCountVerify = MAX_RADAR_EVENTS;
  1183. UnsignedShort eventCount = eventCountVerify;
  1184. xfer->xferUnsignedShort( &eventCount );
  1185. if( eventCount != eventCountVerify )
  1186. {
  1187. 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",
  1188. eventCount, eventCountVerify ));
  1189. throw SC_INVALID_DATA;
  1190. } // end if
  1191. for( UnsignedShort i = 0; i < eventCount; ++i )
  1192. {
  1193. // xfer event data
  1194. xfer->xferUser( &m_event[ i ].type, sizeof( RadarEventType ) );
  1195. xfer->xferBool( &m_event[ i ].active );
  1196. xfer->xferUnsignedInt( &m_event[ i ].createFrame );
  1197. xfer->xferUnsignedInt( &m_event[ i ].dieFrame );
  1198. xfer->xferUnsignedInt( &m_event[ i ].fadeFrame );
  1199. xfer->xferRGBAColorInt( &m_event[ i ].color1 );
  1200. xfer->xferRGBAColorInt( &m_event[ i ].color2 );
  1201. xfer->xferCoord3D( &m_event[ i ].worldLoc );
  1202. xfer->xferICoord2D( &m_event[ i ].radarLoc );
  1203. xfer->xferBool( &m_event[ i ].soundPlayed );
  1204. } // end for i
  1205. // next event index
  1206. xfer->xferInt( &m_nextFreeRadarEvent );
  1207. // last event index
  1208. xfer->xferInt( &m_lastRadarEvent );
  1209. } // end xfer
  1210. // ------------------------------------------------------------------------------------------------
  1211. /** Load post process */
  1212. // ------------------------------------------------------------------------------------------------
  1213. void Radar::loadPostProcess( void )
  1214. {
  1215. //
  1216. // refresh the radar texture now that all the objects (specifically bridges) have
  1217. // been loaded with their correct damage states from save game file
  1218. //
  1219. refreshTerrain( TheTerrainLogic );
  1220. } // end loadPostProcess
  1221. // ------------------------------------------------------------------------------------------------
  1222. /** Is the priority type passed in a "visible" one that can show up on the radar */
  1223. // ------------------------------------------------------------------------------------------------
  1224. Bool Radar::isPriorityVisible( RadarPriorityType priority ) const
  1225. {
  1226. switch( priority )
  1227. {
  1228. case RADAR_PRIORITY_INVALID:
  1229. case RADAR_PRIORITY_NOT_ON_RADAR:
  1230. return FALSE;
  1231. default:
  1232. return TRUE;
  1233. } // end switch
  1234. } // end isPriorityVisible