artlry.cpp 53 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835
  1. //***************************************************************************
  2. //
  3. // artlry.cpp -- File contains the Artillery Strike Object code
  4. //
  5. // MechCommander 2
  6. //
  7. //---------------------------------------------------------------------------//
  8. // Copyright (C) Microsoft Corporation. All rights reserved. //
  9. //===========================================================================//
  10. #ifndef MCLIB_H
  11. #include "mclib.h"
  12. #endif
  13. #ifndef ARTLRY_H
  14. #include "artlry.h"
  15. #endif
  16. #ifndef TEAM_H
  17. #include "team.h"
  18. #endif
  19. #ifndef OBJMGR_H
  20. #include "objmgr.h"
  21. #endif
  22. #ifndef MOVE_H
  23. #include "move.h"
  24. #endif
  25. #ifndef CARNAGE_H
  26. #include "carnage.h"
  27. #endif
  28. #ifndef COLLSN_H
  29. #include "collsn.h"
  30. #endif
  31. #ifndef TURRET_H
  32. #include "turret.h"
  33. #endif
  34. #ifndef TERRAIN_H
  35. #include "terrain.h"
  36. #endif
  37. #ifndef COMNDR_H
  38. #include "comndr.h"
  39. #endif
  40. #ifndef MISSION_H
  41. #include "mission.h"
  42. #endif
  43. #ifndef GATE_H
  44. #include "gate.h"
  45. #endif
  46. #ifndef GAMESOUND_H
  47. #include "gamesound.h"
  48. #endif
  49. #ifndef MULTPLYR_H
  50. #include "multplyr.h"
  51. #endif
  52. //---------------------------------------------------------------------------
  53. //extern float worldUnitsPerMeter;
  54. //extern ObjectMapPtr GameObjectMap;
  55. //long NumCameraDrones = 0;
  56. #define ARTILLERYCHUNK_COMMANDERID_BITS 3
  57. #define ARTILLERYCHUNK_STRIKETYPE_BITS 3
  58. #define ARTILLERYCHUNK_CELLPOS_BITS 10
  59. #define ARTILLERYCHUNK_SECONDS_BITS 6
  60. #define ARTILLERYCHUNK_COMMANDERID_MASK 0x00000007
  61. #define ARTILLERYCHUNK_STRIKETYPE_MASK 0x00000007
  62. #define ARTILLERYCHUNK_CELLPOS_MASK 0x000003FF
  63. #define ARTILLERYCHUNK_SECONDS_MASK 0x0000003F
  64. extern MidLevelRenderer::MLRClipper * theClipper;
  65. extern bool useShadows;
  66. #define AIRSTRIKE_NAME "airstrikemarker"
  67. #define SENSOR_NAME "sensormarker"
  68. #define GV_LEFT_DUST_ID 0
  69. #define GV_RIGHT_DUST_ID 1
  70. extern bool MLRVertexLimitReached;
  71. //***************************************************************************
  72. // MISC
  73. //***************************************************************************
  74. void CallArtillery (long commanderID, long strikeType, Stuff::Vector3D strikeLoc, long secondsToImpact, bool randomOff)
  75. {
  76. //-------------------------------------------------------------------
  77. // If this commander already has 10 strikes going and I'm the server,
  78. // then no more! If I'm a client, trust the server...
  79. long numArtillery = 0;
  80. for (long i = 0; i < ObjectManager->getNumArtillery(); i++)
  81. {
  82. ArtilleryPtr artillery = ObjectManager->getArtillery(i);
  83. if (artillery && artillery->getExists() && (artillery->getCommanderId() == commanderID))
  84. numArtillery++;
  85. }
  86. if (numArtillery >= 10)
  87. {
  88. if (MPlayer && MPlayer->isServer())
  89. return;
  90. }
  91. ArtilleryPtr artilleryStrike = ObjectManager->createArtillery(strikeType,strikeLoc);
  92. if (artilleryStrike)
  93. {
  94. artilleryStrike->iFacePosition = strikeLoc;
  95. artilleryStrike->setPosition( strikeLoc );
  96. //-----------------------------------------------------------
  97. // alignment only really matters for sensor & camera strikes.
  98. artilleryStrike->setTeamId(Commander::commanders[commanderID]->getTeam()->getId(), true);
  99. artilleryStrike->setCommanderId(commanderID);
  100. //-----------------------------------------------------------
  101. // Set Time to Impact here. Only if timeToImpact is != -1.0
  102. if (secondsToImpact != -1)
  103. artilleryStrike->info.strike.timeToImpact = (float)secondsToImpact;
  104. // Of course, then our friends the designers set
  105. // secondsToImpact to 0. Well Done. No warning. Looks like
  106. // a bug don't it?
  107. // MINIMUM time to impact is now four!!!!!
  108. if (secondsToImpact < 2)
  109. artilleryStrike->info.strike.timeToImpact = -1; //FULL Duration.
  110. //------------------------------------
  111. // We must be server if we got here...
  112. if (MPlayer) {
  113. if ((strikeType == ARTILLERY_SMALL) || (strikeType == ARTILLERY_LARGE))
  114. MPlayer->numAirStrikesUsed[commanderID]++;
  115. else
  116. MPlayer->numSensorProbesUsed[commanderID]++;
  117. if (MPlayer->isServer())
  118. MPlayer->addArtilleryChunk(commanderID, strikeType, strikeLoc, secondsToImpact);
  119. }
  120. artilleryStrike->update(); // call this so if in pause mode, there is something to draw
  121. }
  122. }
  123. //---------------------------------------------------------------------------
  124. void IfaceCallStrike (long strikeID,
  125. Stuff::Vector3D* strikeLoc,
  126. GameObjectPtr strikeTarget,
  127. bool playerStrike,
  128. bool clanStrike,
  129. float timeToImpact)
  130. {
  131. if ((strikeID != ARTILLERY_LARGE) && (strikeID != ARTILLERY_SMALL) && (strikeID != ARTILLERY_SENSOR))
  132. return;
  133. if (!strikeLoc && !strikeTarget)
  134. return;
  135. Stuff::Vector3D strikeLocation;
  136. if (strikeLoc)
  137. strikeLocation = *strikeLoc;
  138. else
  139. strikeLocation = strikeTarget->getPosition();
  140. bool bRandom = 0;
  141. long commanderID = -1;
  142. if (playerStrike)
  143. {
  144. commanderID = Commander::home->getId();
  145. bRandom = Team::home->teamLineOfSight(*strikeLoc,0.0f) ? 0 : 1;
  146. }
  147. else if (clanStrike)
  148. {
  149. if (MPlayer)
  150. Fatal(0, " Iface.CallStrike: Need more info than clanStrike in MPlayer ");
  151. //------------------------------------------------------------
  152. // In campaign game, clans can strike into unrevealed terrain.
  153. bRandom = Team::teams[1]->teamLineOfSight(*strikeLoc,0.0f) ? 0 : 1;
  154. commanderID = 1;
  155. }
  156. switch (strikeID)
  157. {
  158. case ARTILLERY_LARGE:
  159. case ARTILLERY_SMALL:
  160. if (playerStrike && Team::home->teamLineOfSight(strikeLocation,0.0f))
  161. {
  162. soundSystem->playDigitalSample(MAPBUTTONS_GUI);
  163. }
  164. break;
  165. }
  166. long secondsToImpact = (long)timeToImpact;
  167. if (strikeID == ARTILLERY_SMALL)
  168. strikeID = ARTILLERY_LARGE;
  169. if (strikeID == ARTILLERY_LARGE && bRandom)
  170. strikeID = ARTILLERY_SMALL;
  171. if (MPlayer)
  172. {
  173. if (MPlayer->isServer())
  174. {
  175. CallArtillery(commanderID, strikeID, strikeLocation, secondsToImpact, bRandom);
  176. }
  177. else
  178. {
  179. MPlayer->sendPlayerArtillery(strikeID, strikeLocation, secondsToImpact);
  180. }
  181. }
  182. else
  183. {
  184. CallArtillery(commanderID, strikeID, strikeLocation, secondsToImpact, bRandom);
  185. }
  186. }
  187. //***************************************************************************
  188. // ARTILLERY CHUNK
  189. //***************************************************************************
  190. void* ArtilleryChunk::operator new (size_t ourSize) {
  191. void* result;
  192. result = systemHeap->Malloc(ourSize);
  193. return(result);
  194. }
  195. //---------------------------------------------------------------------------
  196. void ArtilleryChunk::operator delete (void* us) {
  197. systemHeap->Free(us);
  198. }
  199. //---------------------------------------------------------------------------
  200. void ArtilleryChunk::build (long _commanderId,
  201. long _strikeType,
  202. Stuff::Vector3D location,
  203. long _seconds) {
  204. commanderId = _commanderId;
  205. strikeType = _strikeType;
  206. land->worldToCell(location, cellRC[0], cellRC[1]);
  207. secondsToImpact = _seconds;
  208. data = 0;
  209. }
  210. //---------------------------------------------------------------------------
  211. void ArtilleryChunk::pack (void) {
  212. data = 0;
  213. data |= (secondsToImpact + 1);
  214. data <<= ARTILLERYCHUNK_CELLPOS_BITS;
  215. data |= cellRC[0];
  216. data <<= ARTILLERYCHUNK_CELLPOS_BITS;
  217. data |= cellRC[1];
  218. data <<= ARTILLERYCHUNK_STRIKETYPE_BITS;
  219. data |= strikeType;
  220. data <<= ARTILLERYCHUNK_COMMANDERID_BITS;
  221. data |= commanderId;
  222. }
  223. //---------------------------------------------------------------------------
  224. void ArtilleryChunk::unpack (void) {
  225. unsigned long tempData = data;
  226. commanderId = (tempData & ARTILLERYCHUNK_COMMANDERID_MASK);
  227. tempData >>= ARTILLERYCHUNK_COMMANDERID_BITS;
  228. strikeType = (tempData & ARTILLERYCHUNK_STRIKETYPE_MASK);
  229. tempData >>= ARTILLERYCHUNK_STRIKETYPE_BITS;
  230. cellRC[1] = (tempData & ARTILLERYCHUNK_CELLPOS_MASK);
  231. tempData >>= ARTILLERYCHUNK_CELLPOS_BITS;
  232. cellRC[0] = (tempData & ARTILLERYCHUNK_CELLPOS_MASK);
  233. tempData >>= ARTILLERYCHUNK_CELLPOS_BITS;
  234. secondsToImpact = (tempData & ARTILLERYCHUNK_SECONDS_MASK) - 1;
  235. }
  236. //---------------------------------------------------------------------------
  237. bool ArtilleryChunk::equalTo (ArtilleryChunkPtr chunk) {
  238. if (commanderId != chunk->commanderId)
  239. return(false);
  240. if (strikeType != chunk->strikeType)
  241. return(false);
  242. if (cellRC[0] != chunk->cellRC[0])
  243. return(false);
  244. if (cellRC[1] != chunk->cellRC[1])
  245. return(false);
  246. if (secondsToImpact != chunk->secondsToImpact)
  247. return(false);
  248. return(true);
  249. }
  250. //***************************************************************************
  251. // class ArtilleryType
  252. //***************************************************************************
  253. GameObjectPtr ArtilleryType::createInstance (void) {
  254. ArtilleryPtr newArtillery = new Artillery;
  255. if (!newArtillery)
  256. return(NULL);
  257. newArtillery->init(true, this);
  258. return(newArtillery);
  259. }
  260. //---------------------------------------------------------------------------
  261. void ArtilleryType::init (void) {
  262. ObjectType::init();
  263. objectTypeClass = ARTILLERY_TYPE;
  264. objectClass = ARTILLERY;
  265. }
  266. //---------------------------------------------------------------------------
  267. void ArtilleryType::destroy (void)
  268. {
  269. if (explosionOffsetX)
  270. {
  271. systemHeap->Free(explosionOffsetX);
  272. explosionOffsetX = NULL;
  273. }
  274. if (explosionOffsetY)
  275. {
  276. systemHeap->Free(explosionOffsetY);
  277. explosionOffsetY = NULL;
  278. }
  279. if (explosionDelay)
  280. {
  281. systemHeap->Free(explosionDelay);
  282. explosionDelay = NULL;
  283. }
  284. ObjectType::destroy();
  285. }
  286. //---------------------------------------------------------------------------
  287. long ArtilleryType::init (FilePtr objFile, unsigned long fileSize) {
  288. long result = 0;
  289. FitIniFile miFile;
  290. result = miFile.open(objFile,fileSize);
  291. if (result != NO_ERR)
  292. return(result);
  293. //---------------------------------------------------------------
  294. // Load up the artillery data.
  295. result = miFile.seekBlock("Artillery");
  296. if (result != NO_ERR)
  297. return(result);
  298. char artillerySpriteName[80];
  299. result = miFile.readIdString("ArtillerySpriteName",artillerySpriteName,79);
  300. if (result != NO_ERR)
  301. return(result);
  302. result = miFile.readIdULong("FrameCount",frameCount);
  303. if (result != NO_ERR)
  304. return(result);
  305. result = miFile.readIdULong("StartFrame",startFrame);
  306. if (result != NO_ERR)
  307. return(result);
  308. result = miFile.readIdFloat("FrameRate",frameRate);
  309. if (result != NO_ERR)
  310. return(result);
  311. result = miFile.readIdFloat("NominalTimeToImpact",nominalTimeToImpact);
  312. if (result != NO_ERR)
  313. return(result);
  314. result = miFile.readIdFloat("NominalTimeToLaunch",nominalTimeToLaunch);
  315. if (result != NO_ERR)
  316. nominalTimeToLaunch = nominalTimeToImpact - 10;
  317. result = miFile.readIdFloat("NominalDamage",nominalDamage);
  318. if (result != NO_ERR)
  319. return(result);
  320. result = miFile.readIdFloat("NominalMajorRange",nominalMajorRange);
  321. if (result != NO_ERR)
  322. return(result);
  323. result = miFile.readIdFloat("NominalMajorHits",nominalMajorHits);
  324. if (result != NO_ERR)
  325. return(result);
  326. result = miFile.readIdFloat("NominalMinorRange",nominalMinorRange);
  327. if (result != NO_ERR)
  328. return(result);
  329. result = miFile.readIdFloat("NominalMinorHits",nominalMinorHits);
  330. if (result != NO_ERR)
  331. return(result);
  332. result = miFile.readIdFloat("NominalSensorTime",nominalSensorTime);
  333. if (result != NO_ERR)
  334. return(result);
  335. result = miFile.readIdFloat("NominalSensorRange",nominalSensorRange);
  336. if (result != NO_ERR)
  337. return(result);
  338. result = miFile.readIdFloat("fontScale",fontScale);
  339. if (result != NO_ERR)
  340. return(result);
  341. result = miFile.readIdFloat("fontXOffset",fontXOffset);
  342. if (result != NO_ERR)
  343. return(result);
  344. result = miFile.readIdFloat("fontYOffset",fontYOffset);
  345. if (result != NO_ERR)
  346. return(result);
  347. result = miFile.readIdULong("fontColor",fontColor);
  348. if (result != NO_ERR)
  349. return(result);
  350. if (nominalDamage)
  351. {
  352. result = miFile.readIdLong("NumExplosions",numExplosions);
  353. if (result != NO_ERR)
  354. return(result);
  355. explosionOffsetX = (float *)systemHeap->Malloc(sizeof(float)*numExplosions);
  356. gosASSERT(explosionOffsetX != NULL);
  357. explosionOffsetY = (float *)systemHeap->Malloc(sizeof(float)*numExplosions);
  358. gosASSERT(explosionOffsetY != NULL);
  359. explosionDelay = (float *)systemHeap->Malloc(sizeof(float)*numExplosions);
  360. gosASSERT(explosionDelay != NULL);
  361. for (long i=0;i<numExplosions;i++)
  362. {
  363. char explosionId[50];
  364. sprintf(explosionId,"ExplosionDelay%d",i);
  365. result = miFile.readIdFloat(explosionId,explosionDelay[i]);
  366. if (result != NO_ERR)
  367. return(result);
  368. sprintf(explosionId,"ExplosionOffsetX%d",i);
  369. result = miFile.readIdFloat(explosionId,explosionOffsetX[i]);
  370. if (result != NO_ERR)
  371. return(result);
  372. sprintf(explosionId,"ExplosionOffsetY%d",i);
  373. result = miFile.readIdFloat(explosionId,explosionOffsetY[i]);
  374. if (result != NO_ERR)
  375. return(result);
  376. }
  377. result = miFile.readIdLong("ExplosionsPerExplosion",numExplosionsPerExplosion);
  378. if (result != NO_ERR)
  379. return(result);
  380. result = miFile.readIdLong("ExplosionRandomOffsetX",explosionRandomX);
  381. if (result != NO_ERR)
  382. return(result);
  383. result = miFile.readIdLong("ExplosionRandomOffsetY",explosionRandomY);
  384. if (result != NO_ERR)
  385. return(result);
  386. result = miFile.readIdLong("MinArtilleryHeadRange",minArtilleryHeadRange);
  387. if (result != NO_ERR)
  388. minArtilleryHeadRange = 5.0;
  389. }
  390. else
  391. {
  392. explosionOffsetX = explosionOffsetY = explosionDelay = NULL;
  393. }
  394. //-------------------------------------------------------
  395. // Initialize the base object Type from the current file.
  396. result = ObjectType::init(&miFile);
  397. return(result);
  398. }
  399. //---------------------------------------------------------------------------
  400. bool ArtilleryType::handleCollision (GameObjectPtr collidee, GameObjectPtr collider)
  401. {
  402. if (MPlayer && !MPlayer->isServer())
  403. return(false);
  404. //----------------------------------------------------------
  405. // Artillery counts as damage IF AND ONLY IF the artillery
  406. // strike arrives during the collision time. At all other
  407. // times, the artillery doesn't matter.
  408. ArtilleryPtr artillery = (ArtilleryPtr)collidee;
  409. ArtilleryTypePtr artilleryType = (ArtilleryTypePtr)artillery->getObjectType();
  410. if (artillery->getFlag(OBJECT_FLAG_BOOM))
  411. {
  412. //---------------------
  413. // Get Range to target.
  414. Stuff::Vector3D distance = collider->getPosition();
  415. distance -= collidee->getPosition();
  416. distance.z = 0.0; //Do NOT use elevation!!! Will make it look like its not WORKING!!!!
  417. float range = distance.GetLength() * metersPerWorldUnit;
  418. if ((collider->getObjectClass() == GATE) || (collider->getObjectClass() == TURRET))
  419. {
  420. switch (collider->getObjectClass())
  421. {
  422. case GATE:
  423. {
  424. //---------------------------------------------------
  425. // An explosion may NOT damage a gate or turret unless
  426. // is is within the "little" extent radius.
  427. float littleExtent = ((GatePtr)collider)->getLittleExtent() * metersPerWorldUnit;
  428. if (littleExtent < range)
  429. {
  430. float realRange = range - littleExtent;
  431. if (realRange > ((ArtilleryTypePtr)artillery->getObjectType())->nominalMajorRange)
  432. return FALSE;
  433. }
  434. }
  435. break;
  436. case TURRET:
  437. {
  438. //---------------------------------------------------
  439. // An explosion may NOT damage a gate or turret unless
  440. // is is within the "little" extent radius.
  441. float littleExtent = ((TurretPtr)collider)->getLittleExtent() * metersPerWorldUnit;
  442. if (littleExtent < range)
  443. {
  444. float realRange = range - littleExtent;
  445. if (realRange > artilleryType->nominalMajorRange)
  446. return(false);
  447. }
  448. }
  449. break;
  450. }
  451. }
  452. else if ((collider->getObjectClass() == BATTLEMECH) &&
  453. (((MoverPtr)collider)->getMoveType() == MOVETYPE_AIR) &&
  454. (collider->getStatus() != OBJECT_STATUS_SHUTDOWN))
  455. {
  456. //DO Nothing. Helicopters are immune.
  457. return false;
  458. }
  459. bool isMajorHit = (range <= artilleryType->nominalMajorRange);
  460. long numHits = artilleryType->nominalMajorHits;
  461. if (!isMajorHit)
  462. numHits = artilleryType->nominalMinorHits;
  463. for (long i = 0; i < numHits; i++)
  464. {
  465. WeaponShotInfo shot;
  466. shot.init(collidee->getWatchID(), -3, artilleryType->nominalDamage, 0, 0);
  467. if (collider->isMover())
  468. {
  469. if (range <= minArtilleryHeadRange)
  470. shot.hitLocation = collider->calcHitLocation(collidee, -1, ATTACKSOURCE_DFA, 0);
  471. else
  472. shot.hitLocation = collider->calcHitLocation(collidee, -1, ATTACKSOURCE_ARTILLERY, 0);
  473. shot.setEntryAngle(collider->relFacingTo(collidee->getPosition()));
  474. }
  475. collider->handleWeaponHit(&shot, (MPlayer != NULL));
  476. }
  477. }
  478. return(false);
  479. }
  480. //---------------------------------------------------------------------------
  481. bool ArtilleryType::handleDestruction (GameObjectPtr collidee, GameObjectPtr collider)
  482. {
  483. //-------------------------------------------------------
  484. // Artillery goes away when it hits. Nothing happens as
  485. // a result of a collision.
  486. return(false);
  487. }
  488. //***************************************************************************
  489. // class Artillery
  490. //***************************************************************************
  491. void Artillery::init (bool create)
  492. {
  493. artilleryType = ARTILLERY_SMALL;
  494. initFlags();
  495. setFlag(OBJECT_FLAG_JUSTCREATED, true);
  496. info.strike.timeToImpact = -1.0;
  497. info.strike.timeToLaunch = -1.0;
  498. info.strike.sensorRange = 0.0;
  499. info.strike.contactUpdate = scenarioTime;
  500. info.strike.sensorSystemIndex = -1;
  501. info.strike.timeToBlind = 0.0;
  502. hitEffect = NULL;
  503. leftContrail = NULL;
  504. rightContrail = NULL;
  505. bomber = NULL;
  506. setFlag(OBJECT_FLAG_RANDOM_OFFSET, 0);
  507. }
  508. //---------------------------------------------------------------------------
  509. void Artillery::init (bool create, long _artilleryType)
  510. {
  511. init(create);
  512. artilleryType = _artilleryType;
  513. if (isStrike())
  514. {
  515. }
  516. else if (isSensor())
  517. {
  518. }
  519. }
  520. //---------------------------------------------------------------------------
  521. void Artillery::handleStaticCollision (void)
  522. {
  523. if (getFlag(OBJECT_FLAG_TANGIBLE))
  524. {
  525. //-----------------------------------------------------
  526. // What is our block and vertex number?
  527. long blockNumber = 0;
  528. long vertexNumber = 0;
  529. getBlockAndVertexNumber(blockNumber,vertexNumber);
  530. long CellRow, CellCol;
  531. land->worldToCell(getPosition(), CellRow, CellCol);
  532. //-------------------------------------------------------
  533. // MUST figure out radius to set off mines in CELLS now.
  534. // -fs
  535. long startCellRow = CellRow - 4;
  536. long startCellCol = CellCol - 4;
  537. long i=0;
  538. for (i = startCellRow; i < startCellRow + 9; i++)
  539. {
  540. for (long j = startCellCol; j < startCellCol + 9; j++)
  541. {
  542. if (GameMap->inBounds(i,j))
  543. {
  544. long mineResult = 0;
  545. mineResult = GameMap->getMine(i,j);
  546. if (mineResult == 1)
  547. {
  548. Stuff::Vector3D minePosition;
  549. land->cellToWorld(i,j,minePosition);
  550. GameMap->setMine(i,j,2);
  551. if (MPlayer)
  552. {
  553. MPlayer->addMineChunk(i,
  554. j,
  555. 1,
  556. 2,
  557. 2);
  558. }
  559. ObjectManager->createExplosion(MINE_EXPLOSION_ID, NULL, minePosition, MineSplashDamage, MineSplashRange * worldUnitsPerMeter);
  560. }
  561. }
  562. }
  563. }
  564. //-------------------------------------------------------------------------
  565. // We must now move out into other tiles for the artillery strike to work.
  566. // Remember, Its pretty big!
  567. // Just grab the nine vertices around this one. Problems arise when on Block border. Handle it.
  568. blockNumber = 0;
  569. vertexNumber = 0;
  570. getBlockAndVertexNumber(blockNumber,vertexNumber);
  571. //-------------------------------------------------------------------------
  572. // We must now move out into other tiles for the artillery strike to work.
  573. // Remember, Its pretty big!
  574. // Just grab the nine vertices around this one. Problems arise when on Block border. Handle it.
  575. long topLeftBlockNumber = blockNumber - Terrain::blocksMapSide - 1;
  576. long currentBlockNumber = topLeftBlockNumber;
  577. long totalBlocks = Terrain::blocksMapSide * Terrain::blocksMapSide;
  578. for (i = 0; i < 3; i++)
  579. {
  580. for (long j = 0; j < 3; j++)
  581. {
  582. if ((currentBlockNumber >= 0) && (currentBlockNumber < totalBlocks))
  583. {
  584. long numObjectsInBlock = ObjectManager->getObjBlockNumObjects(currentBlockNumber);
  585. for (long objIndex = 0; objIndex < numObjectsInBlock; objIndex++)
  586. {
  587. GameObjectPtr obj = ObjectManager->getObjBlockObject(currentBlockNumber, objIndex);
  588. if (!obj)
  589. STOP(("Object Number %d in terrain Block %d was NULL!",objIndex,currentBlockNumber));
  590. if (obj->getExists())
  591. ObjectManager->detectStaticCollision(this, obj);
  592. }
  593. }
  594. currentBlockNumber++;
  595. }
  596. currentBlockNumber = topLeftBlockNumber + (Terrain::blocksMapSide * (i + 1));
  597. }
  598. }
  599. }
  600. //---------------------------------------------------------------------------
  601. void Artillery::setJustCreated (void)
  602. {
  603. if (getFlag(OBJECT_FLAG_JUSTCREATED))
  604. {
  605. ArtilleryTypePtr type = (ArtilleryTypePtr)getObjectType();
  606. setFlag(OBJECT_FLAG_JUSTCREATED, false);
  607. if (info.strike.timeToImpact == -1.0)
  608. info.strike.timeToImpact = type->nominalTimeToImpact;
  609. info.strike.timeToLaunch = type->nominalTimeToLaunch;
  610. info.strike.sensorRange = type->nominalSensorRange;
  611. info.strike.timeToBlind = 0.0;
  612. setFlag(OBJECT_FLAG_TANGIBLE, false);
  613. setFlag(OBJECT_FLAG_SENSORS_GOING, false);
  614. if (info.strike.sensorRange)
  615. {
  616. long cellR, cellC;
  617. land->worldToCell(position,cellR, cellC);
  618. if (GameMap->getDeepWater(cellR, cellC) || GameMap->getShallowWater(cellR, cellC))
  619. effectId--;
  620. }
  621. //Create the GOSFX here.
  622. if (strcmp(weaponEffects->GetEffectName(effectId),"NONE") != 0)
  623. {
  624. //--------------------------------------------
  625. // Yes, load it on up.
  626. unsigned flags = gosFX::Effect::ExecuteFlag;
  627. Check_Object(gosFX::EffectLibrary::Instance);
  628. gosFX::Effect::Specification* gosEffectSpec = gosFX::EffectLibrary::Instance->Find(weaponEffects->GetEffectName(effectId));
  629. if (gosEffectSpec)
  630. {
  631. hitEffect = gosFX::EffectLibrary::Instance->MakeEffect(gosEffectSpec->m_effectID, flags);
  632. gosASSERT(hitEffect != NULL);
  633. MidLevelRenderer::MLRTexturePool::Instance->LoadImages();
  634. }
  635. }
  636. if (strcmp(weaponEffects->GetEffectName(JET_CONTRAIL_ID),"NONE") != 0)
  637. {
  638. //--------------------------------------------
  639. // Yes, load it on up.
  640. unsigned flags = gosFX::Effect::ExecuteFlag;
  641. Check_Object(gosFX::EffectLibrary::Instance);
  642. gosFX::Effect::Specification* gosEffectSpec = gosFX::EffectLibrary::Instance->Find(weaponEffects->GetEffectName(JET_CONTRAIL_ID));
  643. if (gosEffectSpec)
  644. {
  645. //leftContrail = gosFX::EffectLibrary::Instance->MakeEffect(gosEffectSpec->m_effectID, flags);
  646. //gosASSERT(leftContrail != NULL);
  647. rightContrail = gosFX::EffectLibrary::Instance->MakeEffect(gosEffectSpec->m_effectID, flags);
  648. gosASSERT(rightContrail != NULL);
  649. MidLevelRenderer::MLRTexturePool::Instance->LoadImages();
  650. }
  651. }
  652. if (info.strike.sensorRange)
  653. {
  654. SensorSystemPtr sensor = SensorManager->newSensor();
  655. info.strike.sensorSystemIndex = sensor->id;
  656. setSensorData(getTeam());
  657. if ((info.strike.timeToBlind == 0.0f) && !getFlag(OBJECT_FLAG_SENSORS_GOING))
  658. {
  659. if (hitEffect)
  660. {
  661. Stuff::LinearMatrix4D shapeOrigin;
  662. Stuff::Point3D actualPosition;
  663. actualPosition.x = -position.x;
  664. actualPosition.y = position.z;
  665. actualPosition.z = position.y;
  666. shapeOrigin.BuildRotation(Stuff::EulerAngles(0.0f,0.0f,0.0f));
  667. shapeOrigin.BuildTranslation(actualPosition);
  668. gosFX::Effect::ExecuteInfo info((Stuff::Time)scenarioTime,&shapeOrigin,NULL);
  669. hitEffect->Start(&info);
  670. }
  671. }
  672. }
  673. }
  674. }
  675. //---------------------------------------------------------------------------
  676. long Artillery::update (void)
  677. {
  678. if (getFlag(OBJECT_FLAG_JUSTCREATED))
  679. {
  680. setJustCreated();
  681. }
  682. else
  683. {
  684. info.strike.timeToImpact -= frameLength;
  685. info.strike.timeToLaunch -= frameLength;
  686. }
  687. ArtilleryTypePtr type = (ArtilleryTypePtr)getObjectType();
  688. recalcBounds(eye); //Are we even visible?
  689. if (inView)
  690. {
  691. windowsVisible = turn;
  692. }
  693. //-------------------------------------------------------------------------------
  694. // Special Cases here. If we are an explosive round, let everyone know we went.
  695. if (getFlag(OBJECT_FLAG_BOOM) && (type->nominalDamage > 0.0))
  696. {
  697. setFlag(OBJECT_FLAG_TANGIBLE, false);
  698. //------------------------------------------------
  699. // Update GOSFX
  700. if (hitEffect && hitEffect->IsExecuted())
  701. {
  702. Stuff::Point3D actualPosition;
  703. actualPosition.x = -position.x;
  704. actualPosition.y = position.z;
  705. actualPosition.z = position.y;
  706. Stuff::LinearMatrix4D shapeOrigin;
  707. shapeOrigin.BuildRotation(Stuff::EulerAngles(0.0f,0.0f,0.0f));
  708. shapeOrigin.BuildTranslation(actualPosition);
  709. Stuff::OBB boundingBox;
  710. gosFX::Effect::ExecuteInfo info((Stuff::Time)scenarioTime,&shapeOrigin,&boundingBox);
  711. bool result = hitEffect->Execute(&info);
  712. if (!result)
  713. {
  714. hitEffect->Kill(); //Effect is over. Otherwise, wait until hit!
  715. delete hitEffect;
  716. hitEffect = NULL;
  717. if (bomber)
  718. {
  719. delete bomber;
  720. bomber = NULL;
  721. }
  722. if (leftContrail)
  723. {
  724. leftContrail->Kill();
  725. delete leftContrail;
  726. leftContrail = NULL;
  727. }
  728. if (rightContrail)
  729. {
  730. rightContrail->Kill();
  731. delete rightContrail;
  732. rightContrail = NULL;
  733. }
  734. return false; //Strike is also over
  735. }
  736. }
  737. }
  738. if ((info.strike.timeToImpact > 0.0f) || (type->nominalDamage == 0.0))
  739. {
  740. bool oldShadows = useShadows;
  741. useShadows = false;
  742. appearance->setObjectParameters(iFacePosition,((ObjectAppearance*)appearance)->rotation,false,getTeamId(),Team::getRelation(getTeamId(), Team::home->getId()));
  743. appearance->recalcBounds();
  744. appearance->update();
  745. useShadows = oldShadows;
  746. }
  747. if (bomber && (info.strike.timeToImpact <= 2.5f) && !bombRunStarted)
  748. {
  749. bomber->setObjectParameters(position,0.0f,false,getTeamId(),Team::getRelation(getTeamId(), Team::home->getId()));
  750. bomber->setMoverParameters(0.0f);
  751. bomber->setGesture(0);
  752. bomber->setObjStatus(OBJECT_STATUS_DESTROYED);
  753. bomber->recalcBounds();
  754. bomber->setVisibility(true,true);
  755. bomber->update(); //Force it to try and draw or stuff will not work!
  756. bombRunStarted = true;
  757. if (rightContrail && rightContrail->IsExecuted())
  758. {
  759. Stuff::LinearMatrix4D shapeOrigin;
  760. Stuff::Vector3D lPosition = bomber->getNodeNamePosition("activity_node");
  761. Stuff::Point3D actualPosition;
  762. actualPosition.x = -lPosition.x;
  763. actualPosition.y = lPosition.z;
  764. actualPosition.z = lPosition.y;
  765. shapeOrigin.BuildRotation(Stuff::EulerAngles(0.0f,0.0f,0.0f));
  766. shapeOrigin.BuildTranslation(actualPosition);
  767. gosFX::Effect::ExecuteInfo info((Stuff::Time)scenarioTime,&shapeOrigin,NULL);
  768. rightContrail->Start(&info);
  769. }
  770. if (leftContrail && leftContrail->IsExecuted())
  771. {
  772. Stuff::LinearMatrix4D shapeOrigin;
  773. Stuff::Vector3D lPosition = bomber->getDustNodePosition(GV_LEFT_DUST_ID);
  774. Stuff::Point3D actualPosition;
  775. actualPosition.x = -lPosition.x;
  776. actualPosition.y = lPosition.z;
  777. actualPosition.z = lPosition.y;
  778. shapeOrigin.BuildRotation(Stuff::EulerAngles(0.0f,0.0f,0.0f));
  779. shapeOrigin.BuildTranslation(actualPosition);
  780. gosFX::Effect::ExecuteInfo info((Stuff::Time)scenarioTime,&shapeOrigin,NULL);
  781. leftContrail->Start(&info);
  782. }
  783. }
  784. if (bomber && bombRunStarted)
  785. {
  786. bomber->setObjectParameters(position,0.0f,false,getTeamId(),Team::getRelation(getTeamId(), Team::home->getId()));
  787. bomber->recalcBounds();
  788. bomber->setVisibility(true,true);
  789. bomber->update();
  790. if (bomber && leftContrail && leftContrail->IsExecuted())
  791. {
  792. Stuff::Vector3D lPosition = bomber->getDustNodePosition(GV_LEFT_DUST_ID);
  793. Stuff::Point3D actualPosition;
  794. actualPosition.x = -lPosition.x;
  795. actualPosition.y = lPosition.z;
  796. actualPosition.z = lPosition.y;
  797. Stuff::LinearMatrix4D shapeOrigin;
  798. shapeOrigin.BuildRotation(Stuff::EulerAngles(0.0f,0.0f,0.0f));
  799. shapeOrigin.BuildTranslation(actualPosition);
  800. Stuff::OBB boundingBox;
  801. gosFX::Effect::ExecuteInfo info((Stuff::Time)scenarioTime,&shapeOrigin,&boundingBox);
  802. leftContrail->Execute(&info);
  803. }
  804. if (bomber && rightContrail && rightContrail->IsExecuted())
  805. {
  806. Stuff::Vector3D lPosition = bomber->getNodeNamePosition("activity_node");
  807. Stuff::Point3D actualPosition;
  808. actualPosition.x = -lPosition.x;
  809. actualPosition.y = lPosition.z;
  810. actualPosition.z = lPosition.y;
  811. Stuff::LinearMatrix4D shapeOrigin;
  812. shapeOrigin.BuildRotation(Stuff::EulerAngles(0.0f,0.0f,0.0f));
  813. shapeOrigin.BuildTranslation(actualPosition);
  814. Stuff::OBB boundingBox;
  815. gosFX::Effect::ExecuteInfo info((Stuff::Time)scenarioTime,&shapeOrigin,&boundingBox);
  816. rightContrail->Execute(&info);
  817. }
  818. }
  819. if ((info.strike.timeToImpact <= 5.5) && !getFlag(OBJECT_FLAG_WHOOSH))
  820. {
  821. if (soundSystem && (type->nominalDamage > 0.0))
  822. {
  823. setFlag(OBJECT_FLAG_WHOOSH, true);
  824. soundSystem->playDigitalSample(INCOMING_AIRSTRIKE,position,true);
  825. }
  826. }
  827. if (!getFlag(OBJECT_FLAG_BOOM) && (info.strike.timeToImpact <= 0.0) && (type->nominalDamage > 0.0))
  828. {
  829. Stuff::Vector3D actualPosition = position;
  830. //-----------------------------------
  831. // Create the giant artillery blast.
  832. // GOS Fx now.
  833. if (hitEffect && hitEffect->IsExecuted())
  834. {
  835. Stuff::LinearMatrix4D shapeOrigin;
  836. Stuff::Point3D actualPosition;
  837. actualPosition.x = -position.x;
  838. actualPosition.y = position.z;
  839. actualPosition.z = position.y;
  840. shapeOrigin.BuildRotation(Stuff::EulerAngles(0.0f,0.0f,0.0f));
  841. shapeOrigin.BuildTranslation(actualPosition);
  842. gosFX::Effect::ExecuteInfo info((Stuff::Time)scenarioTime,&shapeOrigin,NULL);
  843. hitEffect->Start(&info);
  844. }
  845. //------------------------------------------------
  846. // Update GOSFX -- One free update here.
  847. if (hitEffect && hitEffect->IsExecuted())
  848. {
  849. Stuff::Point3D actualPosition;
  850. actualPosition.x = -position.x;
  851. actualPosition.y = position.z;
  852. actualPosition.z = position.y;
  853. Stuff::LinearMatrix4D shapeOrigin;
  854. shapeOrigin.BuildRotation(Stuff::EulerAngles(0.0f,0.0f,0.0f));
  855. shapeOrigin.BuildTranslation(actualPosition);
  856. Stuff::OBB boundingBox;
  857. gosFX::Effect::ExecuteInfo info((Stuff::Time)scenarioTime,&shapeOrigin,&boundingBox);
  858. bool result = hitEffect->Execute(&info);
  859. if (!result)
  860. {
  861. hitEffect->Kill(); //Effect is over. Otherwise, wait until hit!
  862. delete hitEffect;
  863. hitEffect = NULL;
  864. }
  865. }
  866. setFlag(OBJECT_FLAG_BOOM, true);
  867. setFlag(OBJECT_FLAG_TANGIBLE, true);
  868. }
  869. //----------------------------------------------------------------------------
  870. // Sensor round is ticking now. Update Everything.
  871. if ((info.strike.timeToBlind > 0.0) && getFlag(OBJECT_FLAG_SENSORS_GOING))
  872. {
  873. info.strike.timeToBlind -= frameLength;
  874. info.strike.sensorRange = info.strike.timeToBlind / type->nominalSensorTime;
  875. info.strike.sensorRange *= type->nominalSensorRange;
  876. info.strike.sensorRange *= worldUnitsPerMeter;
  877. SensorSystemPtr sensor = SensorManager->getSensor(info.strike.sensorSystemIndex);
  878. Assert(sensor != NULL, 0, " Artillery.update: NULL sensor ");
  879. sensor->setRange(info.strike.sensorRange * metersPerWorldUnit);
  880. //--------------------------------------
  881. // Actual scan is done in team update...
  882. }
  883. else if ((info.strike.timeToBlind <= 0.0) && getFlag(OBJECT_FLAG_SENSORS_GOING))
  884. {
  885. //---------------------------------------------------
  886. // All done, return FALSE;
  887. SensorSystemPtr sensor = SensorManager->getSensor(info.strike.sensorSystemIndex);
  888. SensorManager->removeTeamSensor(getTeam()->getId(), sensor);
  889. SensorManager->freeSensor(sensor);
  890. return(0);
  891. }
  892. //-------------------------------------------------------------------------------------------
  893. // If we are a sensor round, start the sensor countdown. ALWAYS on, even before effect hits.
  894. // time to blind is being reset everytime without this else
  895. else if (type->nominalSensorTime > 0)
  896. {
  897. info.strike.timeToBlind = type->nominalSensorTime;
  898. setFlag(OBJECT_FLAG_SENSORS_GOING, true);
  899. }
  900. if (type->nominalDamage == 0)
  901. {
  902. //-------------------------------------
  903. // Create the Sensor Probe Landing now
  904. // GOS Fx now.
  905. if (hitEffect)
  906. {
  907. Stuff::Point3D actualPosition;
  908. actualPosition.x = -position.x;
  909. actualPosition.y = position.z;
  910. actualPosition.z = position.y;
  911. Stuff::LinearMatrix4D shapeOrigin;
  912. shapeOrigin.BuildRotation(Stuff::EulerAngles(0.0f,0.0f,0.0f));
  913. shapeOrigin.BuildTranslation(actualPosition);
  914. Stuff::OBB boundingBox;
  915. gosFX::Effect::ExecuteInfo xinfo((Stuff::Time)scenarioTime,&shapeOrigin,&boundingBox);
  916. bool result = hitEffect->Execute(&xinfo);
  917. if (!result)
  918. {
  919. hitEffect->Kill(); //Effect is over. Otherwise, wait until hit!
  920. delete hitEffect;
  921. hitEffect = NULL;
  922. }
  923. }
  924. }
  925. return(1);
  926. }
  927. //---------------------------------------------------------------------------
  928. bool Artillery::recalcBounds (CameraPtr myEye)
  929. {
  930. if (myEye)
  931. {
  932. //--------------------------------------------------
  933. // First, if we are using perspective, figure out
  934. // if object too far from camera. Far Clip Plane.
  935. if (eye->usePerspective)
  936. {
  937. Stuff::Point3D Distance;
  938. Stuff::Point3D objPosition(position);
  939. Stuff::Point3D eyePosition(eye->getPosition());
  940. Distance.Subtract(objPosition,eyePosition);
  941. float eyeDistance = Distance.GetApproximateLength();
  942. if (eyeDistance > Camera::MaxClipDistance)
  943. {
  944. inView = false;
  945. }
  946. else
  947. {
  948. inView = true;
  949. }
  950. //-----------------------------------------------------------------
  951. // If inside farClip plane, check if behind camera.
  952. // Find angle between lookVector of Camera and vector from camPos
  953. // to Target. If angle is less then halfFOV, object is visible.
  954. if (inView)
  955. {
  956. Stuff::Vector3D Distance;
  957. Stuff::Point3D objPosition;
  958. Stuff::Point3D eyePosition(eye->getCameraOrigin());
  959. objPosition.x = -position.x;
  960. objPosition.y = position.z;
  961. objPosition.z = position.y;
  962. Distance.Subtract(objPosition,eyePosition);
  963. Distance.Normalize(Distance);
  964. float cosine = Distance * eye->getLookVector();
  965. if (cosine > eye->cosHalfFOV)
  966. inView = true;
  967. else
  968. inView = false;
  969. }
  970. }
  971. else
  972. {
  973. inView = true;
  974. }
  975. if (inView)
  976. {
  977. eye->projectZ(position,screenPos);
  978. Stuff::Vector4D iFaceScreen;
  979. eye->projectZ(iFacePosition, iFaceScreen );
  980. if ((screenPos.x >= 0) && (screenPos.y >= 0) &&
  981. (screenPos.x <= eye->getScreenResX()) &&
  982. (screenPos.y <= eye->getScreenResY())
  983. || ((iFaceScreen.x >= 0) && (iFaceScreen.y >= 0) &&
  984. (iFaceScreen.x <= eye->getScreenResX()) &&
  985. (iFaceScreen.y <= eye->getScreenResY())))
  986. {
  987. inView = true;
  988. }
  989. else
  990. {
  991. inView = false;
  992. }
  993. }
  994. }
  995. return (inView);
  996. }
  997. //---------------------------------------------------------------------------
  998. TeamPtr Artillery::getTeam (void)
  999. {
  1000. if (teamId == -1)
  1001. return(NULL);
  1002. return(Team::teams[teamId]);
  1003. }
  1004. //---------------------------------------------------------------------------
  1005. void Artillery::setCommanderId (long _commanderId)
  1006. {
  1007. commanderId = _commanderId;
  1008. }
  1009. //---------------------------------------------------------------------------
  1010. long Artillery::setTeamId (long _teamId, bool setup)
  1011. {
  1012. teamId = _teamId;
  1013. Assert(teamId > -1, teamId, " Mover.setTeamId: bad teamId ");
  1014. return(NO_ERR);
  1015. }
  1016. //---------------------------------------------------------------------------
  1017. bool Artillery::isFriendly (TeamPtr team) {
  1018. if (teamId > -1)
  1019. return(Team::relations[teamId][team->getId()] == RELATION_FRIENDLY);
  1020. return(false);
  1021. }
  1022. //---------------------------------------------------------------------------
  1023. bool Artillery::isEnemy (TeamPtr team) {
  1024. if (teamId > -1)
  1025. return(Team::relations[teamId][team->getId()] == RELATION_ENEMY);
  1026. return(false);
  1027. }
  1028. //---------------------------------------------------------------------------
  1029. bool Artillery::isNeutral (TeamPtr team) {
  1030. if (teamId > -1)
  1031. return(Team::relations[teamId][team->getId()] == RELATION_NEUTRAL);
  1032. return(true);
  1033. }
  1034. //---------------------------------------------------------------------------
  1035. void Artillery::setSensorRange (float range)
  1036. {
  1037. SensorSystemPtr sensor = SensorManager->getSensor(info.strike.sensorSystemIndex);
  1038. if (sensor)
  1039. sensor->setRange(range);
  1040. }
  1041. //---------------------------------------------------------------------------
  1042. void Artillery::setSensorData (TeamPtr team, float sensorTime, float range)
  1043. {
  1044. if (sensorTime != -1.0)
  1045. info.strike.timeToBlind = sensorTime;
  1046. if (range != -1.0)
  1047. info.strike.sensorRange = range;
  1048. SensorSystemPtr sensor = SensorManager->getSensor(info.strike.sensorSystemIndex);
  1049. Assert(sensor != NULL, info.strike.sensorSystemIndex, " Artillery.setSensorData: NULL sensor ");
  1050. sensor->setOwner(this);
  1051. SensorManager->addTeamSensor(team->getId(), sensor);
  1052. sensor->setRange(range);
  1053. sensor->setLOSCapability(false);
  1054. }
  1055. //---------------------------------------------------------------------------
  1056. void Artillery::drawSelectBox (unsigned char color)
  1057. {
  1058. }
  1059. //---------------------------------------------------------------------------
  1060. void Artillery::render (void)
  1061. {
  1062. if (inView)
  1063. {
  1064. if (hitEffect )
  1065. {
  1066. gosFX::Effect::DrawInfo drawInfo;
  1067. drawInfo.m_clipper = theClipper;
  1068. MidLevelRenderer::MLRState mlrState;
  1069. //mlrState.SetBackFaceOn();
  1070. mlrState.SetDitherOn();
  1071. mlrState.SetTextureCorrectionOn();
  1072. mlrState.SetZBufferCompareOn();
  1073. mlrState.SetZBufferWriteOn();
  1074. //mlrState.SetFilterMode(MidLevelRenderer::MLRState::BiLinearFilterMode);
  1075. //mlrState.SetAlphaMode(MidLevelRenderer::MLRState::AlphaInvAlphaMode);
  1076. drawInfo.m_state = mlrState;
  1077. drawInfo.m_clippingFlags = 0x0;
  1078. Stuff::Point3D actualPosition;
  1079. actualPosition.x = -position.x;
  1080. actualPosition.y = position.z;
  1081. actualPosition.z = position.y;
  1082. Stuff::LinearMatrix4D shapeOrigin;
  1083. shapeOrigin.BuildRotation(Stuff::EulerAngles(0.0f,0.0f,0.0f));
  1084. shapeOrigin.BuildTranslation(actualPosition);
  1085. drawInfo.m_parentToWorld = &shapeOrigin;
  1086. if (!MLRVertexLimitReached)
  1087. hitEffect->Draw(&drawInfo);
  1088. }
  1089. if ( (!MPlayer && (info.strike.timeToImpact > 0.f)) ||
  1090. (MPlayer && info.strike.timeToImpact > 0.f
  1091. && ((teamId == Team::home->getId()) || info.strike.timeToImpact < 3.0f)))
  1092. {
  1093. // We are drawing the timer.
  1094. // Draw it.
  1095. char text[256];
  1096. switch (artilleryType)
  1097. {
  1098. case ARTILLERY_LARGE:
  1099. sprintf(text,"%02.2f",info.strike.timeToImpact);
  1100. break;
  1101. case ARTILLERY_SMALL:
  1102. sprintf(text,"%02.2f",info.strike.timeToImpact);
  1103. break;
  1104. case ARTILLERY_SENSOR:
  1105. text[0] = 0; // players are confused by sensor count down
  1106. break;
  1107. }
  1108. DWORD width, height;
  1109. Stuff::Vector4D moveHere;
  1110. eye->projectZ( iFacePosition, moveHere );
  1111. gos_TextSetAttributes (gosFontHandle, 0, gosFontScale, false, false, false, false);
  1112. gos_TextStringLength(&width,&height,text);
  1113. moveHere.y += 10.0f;
  1114. moveHere.x -= width / 2;
  1115. moveHere.z = width;
  1116. moveHere.w = height;
  1117. globalFloatHelp->setFloatHelp(text,moveHere,SD_GREEN,0x0,gosFontScale,false,false,false,false);
  1118. if (appearance->canBeSeen())
  1119. {
  1120. windowsVisible = turn;
  1121. appearance->setVisibility(true,true);
  1122. appearance->render(-1);
  1123. }
  1124. }
  1125. }
  1126. if (bomber && bombRunStarted)
  1127. {
  1128. bomber->setVisibility(true,true);
  1129. bomber->render();
  1130. bomber->renderShadows();
  1131. gosFX::Effect::DrawInfo drawInfo;
  1132. drawInfo.m_clipper = theClipper;
  1133. MidLevelRenderer::MLRState mlrState;
  1134. //mlrState.SetBackFaceOn();
  1135. mlrState.SetDitherOn();
  1136. mlrState.SetTextureCorrectionOn();
  1137. mlrState.SetZBufferCompareOn();
  1138. mlrState.SetZBufferWriteOn();
  1139. //mlrState.SetFilterMode(MidLevelRenderer::MLRState::BiLinearFilterMode);
  1140. //mlrState.SetAlphaMode(MidLevelRenderer::MLRState::AlphaInvAlphaMode);
  1141. drawInfo.m_state = mlrState;
  1142. drawInfo.m_clippingFlags = 0x0;
  1143. if (leftContrail)
  1144. {
  1145. Stuff::Vector3D lPosition = bomber->getDustNodePosition(GV_LEFT_DUST_ID);
  1146. Stuff::Point3D actualPosition;
  1147. actualPosition.x = -lPosition.x;
  1148. actualPosition.y = lPosition.z;
  1149. actualPosition.z = lPosition.y;
  1150. Stuff::LinearMatrix4D shapeOrigin;
  1151. shapeOrigin.BuildRotation(Stuff::EulerAngles(0.0f,0.0f,0.0f));
  1152. shapeOrigin.BuildTranslation(actualPosition);
  1153. drawInfo.m_parentToWorld = &shapeOrigin;
  1154. if (!MLRVertexLimitReached)
  1155. leftContrail->Draw(&drawInfo);
  1156. }
  1157. if (rightContrail)
  1158. {
  1159. Stuff::Vector3D lPosition = bomber->getNodeNamePosition("activity_node");
  1160. Stuff::Point3D actualPosition;
  1161. actualPosition.x = -lPosition.x;
  1162. actualPosition.y = lPosition.z;
  1163. actualPosition.z = lPosition.y;
  1164. Stuff::LinearMatrix4D shapeOrigin;
  1165. shapeOrigin.BuildRotation(Stuff::EulerAngles(0.0f,0.0f,0.0f));
  1166. shapeOrigin.BuildTranslation(actualPosition);
  1167. drawInfo.m_parentToWorld = &shapeOrigin;
  1168. if (!MLRVertexLimitReached)
  1169. rightContrail->Draw(&drawInfo);
  1170. }
  1171. }
  1172. }
  1173. //---------------------------------------------------------------------------
  1174. long Artillery::handleWeaponHit (WeaponShotInfoPtr shotInfo, bool addMultiplayChunk)
  1175. {
  1176. return(NO_ERR);
  1177. }
  1178. //---------------------------------------------------------------------------
  1179. void Artillery::destroy (void)
  1180. {
  1181. //Must delete here too in case effect was NOT over when mission ended!
  1182. if (hitEffect)
  1183. {
  1184. hitEffect->Kill();
  1185. delete hitEffect;
  1186. hitEffect = NULL;
  1187. }
  1188. if (leftContrail)
  1189. {
  1190. leftContrail->Kill();
  1191. delete leftContrail;
  1192. leftContrail = NULL;
  1193. }
  1194. if (rightContrail)
  1195. {
  1196. rightContrail->Kill();
  1197. delete rightContrail;
  1198. rightContrail = NULL;
  1199. }
  1200. if (bomber)
  1201. {
  1202. delete bomber;
  1203. bomber = NULL;
  1204. }
  1205. }
  1206. //---------------------------------------------------------------------------
  1207. void Artillery::init (bool create, ObjectTypePtr _type)
  1208. {
  1209. GameObject::init(create, _type);
  1210. initFlags();
  1211. switch (_type->getObjectTypeClass())
  1212. {
  1213. case ARTILLERY_TYPE:
  1214. objectClass = ARTILLERY;
  1215. setFlag(OBJECT_FLAG_EXISTS, true);
  1216. setFlag(OBJECT_FLAG_JUSTCREATED, true);
  1217. setFlag(OBJECT_FLAG_BOOM, false);
  1218. info.strike.timeToImpact = -1.0;
  1219. info.strike.explosionOffFlags = 0;
  1220. break;
  1221. }
  1222. effectId = -1;
  1223. switch (_type->whatAmI())
  1224. {
  1225. case BIG_ARTLRY:
  1226. {
  1227. effectId = LARGE_AIRSTRIKE_ID;
  1228. init(ARTILLERY_LARGE);
  1229. //--------------------------------------------
  1230. //Load up the strike appearance before hit.
  1231. char appearName[1024];
  1232. strcpy(appearName,AIRSTRIKE_NAME);
  1233. //--------------------------------------------------------------
  1234. // New code!!!
  1235. // We need to append the sprite type to the appearance num now.
  1236. // The MechEdit tool does not assume a sprite type, nor should it.
  1237. // MechCmdr2 features much simpler objects which only use 1 type of sprite!
  1238. long appearanceType = (BLDG_TYPE << 24);
  1239. AppearanceTypePtr buildingAppearanceType = NULL;
  1240. if (!appearName)
  1241. {
  1242. //------------------------------------------------------
  1243. // LOAD a dummy appearance until real ones are available
  1244. // for this building!
  1245. appearanceType = (BLDG_TYPE << 24);
  1246. buildingAppearanceType = appearanceTypeList->getAppearance(appearanceType,"TESTBLDG");
  1247. }
  1248. else
  1249. {
  1250. buildingAppearanceType = appearanceTypeList->getAppearance(appearanceType,appearName);
  1251. if (!buildingAppearanceType)
  1252. {
  1253. char msg[1024];
  1254. sprintf(msg,"No Building Appearance Named %s",appearName);
  1255. Fatal(0,msg);
  1256. }
  1257. }
  1258. appearance = new BldgAppearance;
  1259. gosASSERT(appearance != NULL);
  1260. //--------------------------------------------------------------
  1261. // The only appearance type for buildings is MLR_APPEARANCE.
  1262. gosASSERT(buildingAppearanceType->getAppearanceClass() == BLDG_TYPE);
  1263. appearance->init((BldgAppearanceType*)buildingAppearanceType, (GameObjectPtr)this);
  1264. //--------------------------------------------------------------
  1265. appearanceType = (GV_TYPE << 24);
  1266. AppearanceTypePtr bomberAppearanceType = appearanceTypeList->getAppearance(appearanceType,"Shilone");
  1267. if (!bomberAppearanceType)
  1268. {
  1269. STOP(("Unable to create Bomber for air strike Shilone"));
  1270. }
  1271. if ((appearanceType>>24) == GV_TYPE)
  1272. {
  1273. bomber = new GVAppearance;
  1274. if (!bomber)
  1275. STOP((" Artillery.init: unable to create appearance for Shilone"));
  1276. //-----------------------------------------------------------------
  1277. bomber->init((GVAppearanceType*)bomberAppearanceType, (GameObjectPtr)this);
  1278. }
  1279. bombRunStarted = false;
  1280. }
  1281. break;
  1282. case SMALL_ARTLRY:
  1283. {
  1284. effectId = SMALL_AIRSTRIKE_ID;
  1285. init(ARTILLERY_SMALL);
  1286. //--------------------------------------------
  1287. //Load up the strike appearance before hit.
  1288. char appearName[1024];
  1289. strcpy(appearName,AIRSTRIKE_NAME);
  1290. //--------------------------------------------------------------
  1291. // New code!!!
  1292. // We need to append the sprite type to the appearance num now.
  1293. // The MechEdit tool does not assume a sprite type, nor should it.
  1294. // MechCmdr2 features much simpler objects which only use 1 type of sprite!
  1295. long appearanceType = (BLDG_TYPE << 24);
  1296. AppearanceTypePtr buildingAppearanceType = NULL;
  1297. if (!appearName)
  1298. {
  1299. //------------------------------------------------------
  1300. // LOAD a dummy appearance until real ones are available
  1301. // for this building!
  1302. appearanceType = (BLDG_TYPE << 24);
  1303. buildingAppearanceType = appearanceTypeList->getAppearance(appearanceType,"TESTBLDG");
  1304. }
  1305. else
  1306. {
  1307. buildingAppearanceType = appearanceTypeList->getAppearance(appearanceType,appearName);
  1308. if (!buildingAppearanceType)
  1309. {
  1310. char msg[1024];
  1311. sprintf(msg,"No Building Appearance Named %s",appearName);
  1312. Fatal(0,msg);
  1313. }
  1314. }
  1315. appearance = new BldgAppearance;
  1316. gosASSERT(appearance != NULL);
  1317. //--------------------------------------------------------------
  1318. // The only appearance type for buildings is MLR_APPEARANCE.
  1319. gosASSERT(buildingAppearanceType->getAppearanceClass() == BLDG_TYPE);
  1320. appearance->init((BldgAppearanceType*)buildingAppearanceType, (GameObjectPtr)this);
  1321. //--------------------------------------------------------------
  1322. appearanceType = (GV_TYPE << 24);
  1323. AppearanceTypePtr bomberAppearanceType = appearanceTypeList->getAppearance(appearanceType,"shilone");
  1324. if (!bomberAppearanceType)
  1325. {
  1326. STOP(("Unable to create Bomber for air strike Shilone"));
  1327. }
  1328. if ((appearanceType>>24) == GV_TYPE)
  1329. {
  1330. bomber = new GVAppearance;
  1331. if (!bomber)
  1332. STOP((" Artillery.init: unable to create appearance for Shilone"));
  1333. //-----------------------------------------------------------------
  1334. bomber->init((GVAppearanceType*)bomberAppearanceType, (GameObjectPtr)this);
  1335. }
  1336. bombRunStarted = false;
  1337. }
  1338. break;
  1339. case SENSOR_ARTLRY:
  1340. {
  1341. effectId = SENSOR_AIRSTRIKE_ID;
  1342. init(create, ARTILLERY_SENSOR);
  1343. //--------------------------------------------
  1344. //Load up the strike appearance before hit.
  1345. char appearName[1024];
  1346. strcpy(appearName,SENSOR_NAME);
  1347. //--------------------------------------------------------------
  1348. // New code!!!
  1349. // We need to append the sprite type to the appearance num now.
  1350. // The MechEdit tool does not assume a sprite type, nor should it.
  1351. // MechCmdr2 features much simpler objects which only use 1 type of sprite!
  1352. long appearanceType = (BLDG_TYPE << 24);
  1353. AppearanceTypePtr buildingAppearanceType = NULL;
  1354. if (!appearName)
  1355. {
  1356. //------------------------------------------------------
  1357. // LOAD a dummy appearance until real ones are available
  1358. // for this building!
  1359. appearanceType = (BLDG_TYPE << 24);
  1360. buildingAppearanceType = appearanceTypeList->getAppearance(appearanceType,"TESTBLDG");
  1361. }
  1362. else
  1363. {
  1364. buildingAppearanceType = appearanceTypeList->getAppearance(appearanceType,appearName);
  1365. if (!buildingAppearanceType)
  1366. {
  1367. char msg[1024];
  1368. sprintf(msg,"No Building Appearance Named %s",appearName);
  1369. Fatal(0,msg);
  1370. }
  1371. }
  1372. appearance = new BldgAppearance;
  1373. gosASSERT(appearance != NULL);
  1374. //--------------------------------------------------------------
  1375. // The only appearance type for buildings is MLR_APPEARANCE.
  1376. gosASSERT(buildingAppearanceType->getAppearanceClass() == BLDG_TYPE);
  1377. appearance->init((BldgAppearanceType*)buildingAppearanceType, (GameObjectPtr)this);
  1378. }
  1379. break;
  1380. }
  1381. }
  1382. //---------------------------------------------------------------------------
  1383. void Artillery::Save (PacketFilePtr file, long packetNum)
  1384. {
  1385. ArtilleryData data;
  1386. CopyTo(&data);
  1387. //PacketNum incremented in ObjectManager!!
  1388. file->writePacket(packetNum,(MemoryPtr)&data,sizeof(ArtilleryData),STORAGE_TYPE_ZLIB);
  1389. }
  1390. //---------------------------------------------------------------------------
  1391. void Artillery::CopyTo (ArtilleryData *data)
  1392. {
  1393. data->artilleryType = artilleryType;
  1394. data->teamId = teamId;
  1395. data->commanderId = commanderId;
  1396. data->info = info;
  1397. data->effectId = effectId;
  1398. data->bombRunStarted = bombRunStarted;
  1399. data->inView = inView;
  1400. data->iFacePosition = iFacePosition;
  1401. GameObject::CopyTo(dynamic_cast<GameObjectData *>(data));
  1402. }
  1403. //---------------------------------------------------------------------------
  1404. void Artillery::Load (ArtilleryData *data)
  1405. {
  1406. GameObject::Load(dynamic_cast<GameObjectData *>(data));
  1407. artilleryType = data->artilleryType;
  1408. commanderId = data->commanderId;
  1409. teamId = data->teamId;
  1410. info = data->info;
  1411. effectId = data->effectId;
  1412. bombRunStarted = data->bombRunStarted;
  1413. inView = data->inView;
  1414. iFacePosition = data->iFacePosition;
  1415. // if we're not a true artillery round, we're a sensor probe.
  1416. // This checks to see if we've hit yet.
  1417. // If not, create the impact effect and move on.
  1418. if ((data->info.strike.sensorRange != 0.0f) && (data->info.strike.timeToImpact <= 0.0) && !getFlag(OBJECT_FLAG_SENSORS_GOING))
  1419. {
  1420. long cellR, cellC;
  1421. land->worldToCell(position,cellR, cellC);
  1422. if (GameMap->getDeepWater(cellR, cellC) || GameMap->getShallowWater(cellR, cellC))
  1423. effectId--;
  1424. //Create the GOSFX here.
  1425. if (strcmp(weaponEffects->GetEffectName(effectId),"NONE") != 0)
  1426. {
  1427. //--------------------------------------------
  1428. // Yes, load it on up.
  1429. unsigned flags = gosFX::Effect::ExecuteFlag;
  1430. Check_Object(gosFX::EffectLibrary::Instance);
  1431. gosFX::Effect::Specification* gosEffectSpec = gosFX::EffectLibrary::Instance->Find(weaponEffects->GetEffectName(effectId));
  1432. if (gosEffectSpec)
  1433. {
  1434. hitEffect = gosFX::EffectLibrary::Instance->MakeEffect(gosEffectSpec->m_effectID, flags);
  1435. gosASSERT(hitEffect != NULL);
  1436. MidLevelRenderer::MLRTexturePool::Instance->LoadImages();
  1437. }
  1438. }
  1439. if (hitEffect)
  1440. {
  1441. Stuff::LinearMatrix4D shapeOrigin;
  1442. Stuff::Point3D actualPosition;
  1443. actualPosition.x = -position.x;
  1444. actualPosition.y = position.z;
  1445. actualPosition.z = position.y;
  1446. shapeOrigin.BuildRotation(Stuff::EulerAngles(0.0f,0.0f,0.0f));
  1447. shapeOrigin.BuildTranslation(actualPosition);
  1448. gosFX::Effect::ExecuteInfo info((Stuff::Time)scenarioTime,&shapeOrigin,NULL);
  1449. hitEffect->Start(&info);
  1450. }
  1451. }
  1452. if (!getFlag(OBJECT_FLAG_BOOM) && (data->info.strike.timeToImpact > 0.0) && (data->info.strike.sensorRange == 0.0f))
  1453. {
  1454. //Create the GOSFX here.
  1455. if (strcmp(weaponEffects->GetEffectName(effectId),"NONE") != 0)
  1456. {
  1457. //--------------------------------------------
  1458. // Yes, load it on up.
  1459. unsigned flags = gosFX::Effect::ExecuteFlag;
  1460. Check_Object(gosFX::EffectLibrary::Instance);
  1461. gosFX::Effect::Specification* gosEffectSpec = gosFX::EffectLibrary::Instance->Find(weaponEffects->GetEffectName(effectId));
  1462. if (gosEffectSpec)
  1463. {
  1464. hitEffect = gosFX::EffectLibrary::Instance->MakeEffect(gosEffectSpec->m_effectID, flags);
  1465. gosASSERT(hitEffect != NULL);
  1466. MidLevelRenderer::MLRTexturePool::Instance->LoadImages();
  1467. }
  1468. }
  1469. }
  1470. if (data->info.strike.sensorSystemIndex != -1)
  1471. {
  1472. SensorSystemPtr sensor = SensorManager->newSensor();
  1473. info.strike.sensorSystemIndex = sensor->id;
  1474. setSensorData(getTeam());
  1475. }
  1476. }
  1477. //---------------------------------------------------------------------------