BULLET.CPP 51 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110
  1. //
  2. // Copyright 2020 Electronic Arts Inc.
  3. //
  4. // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free
  5. // software: you can redistribute it and/or modify it under the terms of
  6. // the GNU General Public License as published by the Free Software Foundation,
  7. // either version 3 of the License, or (at your option) any later version.
  8. // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed
  9. // in the hope that it will be useful, but with permitted additional restrictions
  10. // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
  11. // distributed with this program. You should have received a copy of the
  12. // GNU General Public License along with permitted additional restrictions
  13. // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
  14. /* $Header: /CounterStrike/BULLET.CPP 1 3/03/97 10:24a Joe_bostic $ */
  15. /***********************************************************************************************
  16. *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
  17. ***********************************************************************************************
  18. * *
  19. * Project Name : Command & Conquer *
  20. * *
  21. * File Name : BULLET.CPP *
  22. * *
  23. * Programmer : Joe L. Bostic *
  24. * *
  25. * Start Date : April 23, 1994 *
  26. * *
  27. * Last Update : October 10, 1996 [JLB] *
  28. * *
  29. *---------------------------------------------------------------------------------------------*
  30. * Functions: *
  31. * BulletClass::AI -- Logic processing for bullet. *
  32. * BulletClass::BulletClass -- Bullet constructor. *
  33. * BulletClass::Bullet_Explodes -- Performs bullet explosion logic. *
  34. * BulletClass::Detach -- Removes specified target from this bullet's targeting system. *
  35. * BulletClass::Draw_It -- Displays the bullet at location specified. *
  36. * BulletClass::In_Which_Layer -- Fetches the layer that the bullet resides in. *
  37. * BulletClass::Init -- Clears the bullets array for scenario preparation. *
  38. * BulletClass::Is_Forced_To_Explode -- Checks if bullet should explode NOW. *
  39. * BulletClass::Mark -- Performs related map refreshing under bullet. *
  40. * BulletClass::Occupy_List -- Determines the bullet occupation list. *
  41. * BulletClass::Shape_Number -- Fetches the shape number for the bullet object. *
  42. * BulletClass::Sort_Y -- Sort coordinate for bullet rendering. *
  43. * BulletClass::Target_Coord -- Fetches coordinate to use when firing on this object. *
  44. * BulletClass::Unlimbo -- Transitions a bullet object into the game render/logic system. *
  45. * BulletClass::delete -- Bullet memory delete. *
  46. * BulletClass::new -- Allocates memory for bullet object. *
  47. * BulletClass::~BulletClass -- Destructor for bullet objects. *
  48. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  49. #include "function.h"
  50. /***********************************************************************************************
  51. * BulletClass::BulletClass -- Bullet constructor. *
  52. * *
  53. * This is the constructor for the bullet class. It handles all *
  54. * initialization of the bullet and starting it in motion toward its *
  55. * target. *
  56. * *
  57. * INPUT: id -- The type of bullet this is (could be missile). *
  58. * *
  59. * OUTPUT: none *
  60. * *
  61. * WARNINGS: none *
  62. * *
  63. * HISTORY: *
  64. * 05/02/1994 JLB : Created. *
  65. * 06/20/1994 JLB : Firer is a base class pointer. *
  66. * 12/10/1994 JLB : Auto calculate range optional. *
  67. * 12/12/1994 JLB : Handles small arms as an instantaneous effect. *
  68. * 12/23/1994 JLB : Fixed scatter algorithm for non-homing projectiles. *
  69. * 12/31/1994 JLB : Removed range parameter (not needed). *
  70. *=============================================================================================*/
  71. BulletClass::BulletClass(BulletType id, TARGET target, TechnoClass * payback, int strength, WarheadType warhead, int speed) :
  72. ObjectClass(RTTI_BULLET, Bullets.ID(this)),
  73. Class(BulletTypes.Ptr((int)id)),
  74. Payback(payback),
  75. PrimaryFacing(DIR_N),
  76. IsInaccurate(false),
  77. IsToAnimate(false),
  78. IsLocked(true),
  79. TarCom(target),
  80. MaxSpeed(speed),
  81. Warhead(warhead)
  82. {
  83. Strength = strength;
  84. Height = FLIGHT_LEVEL;
  85. }
  86. /***********************************************************************************************
  87. * BulletClass::~BulletClass -- Destructor for bullet objects. *
  88. * *
  89. * The bullet destructor must detect if a dog has been attached to this bullet. If so, *
  90. * then the attached dog must be unlimboed back onto the map. This operation is necessary *
  91. * because, unlike other objects, the dog flies with the bullet it fires. *
  92. * *
  93. * INPUT: none *
  94. * *
  95. * OUTPUT: none *
  96. * *
  97. * WARNINGS: none *
  98. * *
  99. * HISTORY: *
  100. * 07/06/1996 JLB : Created. *
  101. *=============================================================================================*/
  102. BulletClass::~BulletClass(void)
  103. {
  104. if (GameActive) {
  105. /*
  106. ** SPECIAL CASE:
  107. ** The dog is attached to the dog bullet in a limbo state. When the bullet is
  108. ** destroyed, the dog must come back out of limbo at the closest location possible to
  109. ** the bullet.
  110. */
  111. if (Payback != NULL && Payback->What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)Payback)->Class->IsDog) {
  112. InfantryClass * dog = (InfantryClass *)Payback;
  113. if (dog) {
  114. bool unlimbo = false;
  115. DirType dogface = dog->PrimaryFacing;
  116. COORDINATE newcoord = Coord;
  117. /*
  118. ** Ensure that the coordinate, that the dog is to appear at, is legal. If not,
  119. ** then find a nearby legal location.
  120. */
  121. if (Can_Enter_Cell(newcoord) != MOVE_OK) {
  122. newcoord = Map.Nearby_Location(Coord_Cell(newcoord), dog->Class->Speed);
  123. }
  124. /*
  125. ** Try to put the dog down where the target impacted. If we can't
  126. ** put it in that cell, then scan through the adjacent cells,
  127. ** starting with our current heading, until we find a place where
  128. ** we can put him down. If all 8 adjacent cell checks fail, then
  129. ** just delete the dog.
  130. */
  131. for (int i = -1; i < 8; i++) {
  132. if (i != -1) {
  133. newcoord = Adjacent_Cell(Coord, FacingType(i));
  134. }
  135. ScenarioInit++;
  136. if (dog->Unlimbo(newcoord, dog->PrimaryFacing)) {
  137. dog->Mark(MARK_DOWN);
  138. dog->Do_Action(DO_DOG_MAUL, true);
  139. if (dog->WasSelected) {
  140. dog->Select();
  141. }
  142. ScenarioInit--;
  143. unlimbo = true;
  144. break;
  145. }
  146. ScenarioInit--;
  147. }
  148. Payback = 0;
  149. if (!unlimbo) {
  150. delete dog;
  151. }
  152. }
  153. }
  154. BulletClass::Limbo();
  155. }
  156. Class=0;
  157. Payback=0;
  158. }
  159. /***********************************************************************************************
  160. * BulletClass::new -- Allocates memory for bullet object. *
  161. * *
  162. * This function will "allocate" a block of memory for a bullet object. *
  163. * This memory block is merely lifted from a fixed pool of blocks. *
  164. * *
  165. * INPUT: size -- The size of the memory block needed. *
  166. * *
  167. * OUTPUT: Returns with a pointer to an available bullet object block. *
  168. * *
  169. * WARNINGS: none *
  170. * *
  171. * HISTORY: *
  172. * 05/02/1994 JLB : Created. *
  173. *=============================================================================================*/
  174. void * BulletClass::operator new(size_t )
  175. {
  176. void * ptr = Bullets.Allocate();
  177. if (ptr) {
  178. ((BulletClass *)ptr)->Set_Active();
  179. }
  180. return(ptr);
  181. }
  182. /***********************************************************************************************
  183. * BulletClass::delete -- Bullet memory delete. *
  184. * *
  185. * Since bullets memory is merely "allocated" out of a pool, it never *
  186. * actually gets deleted. *
  187. * *
  188. * INPUT: ptr -- Generic pointer to bullet object. *
  189. * *
  190. * OUTPUT: none *
  191. * *
  192. * WARNINGS: none *
  193. * *
  194. * HISTORY: *
  195. * 05/02/1994 JLB : Created. *
  196. *=============================================================================================*/
  197. void BulletClass::operator delete(void * ptr)
  198. {
  199. if (ptr) {
  200. ((BulletClass *)ptr)->IsActive = false;
  201. }
  202. Bullets.Free((BulletClass *)ptr);
  203. }
  204. /***********************************************************************************************
  205. * BulletClass::Occupy_List -- Determines the bullet occupation list. *
  206. * *
  207. * This function will determine the cell occupation list and return a pointer to it. Most *
  208. * bullets are small and the list is usually short, but on occasion, it can be a list that *
  209. * rivals the size of regular vehicles. *
  210. * *
  211. * INPUT: none *
  212. * *
  213. * OUTPUT: Returns with a pointer to the cell offset list that covers all the cells a bullet *
  214. * is over. *
  215. * *
  216. * WARNINGS: none *
  217. * *
  218. * HISTORY: *
  219. * 06/20/1994 JLB : Created. *
  220. * 01/05/1995 JLB : Handles projectiles with altitude. *
  221. *=============================================================================================*/
  222. short const * BulletClass::Occupy_List(bool) const
  223. {
  224. assert(Bullets.ID(this) == ID);
  225. assert(IsActive);
  226. /*
  227. ** Super-gigundo units use the >= 64 coord spillage list logic.
  228. */
  229. if (Class->IsGigundo) {
  230. static short _list[] = {
  231. -1, 0, 1,
  232. MAP_CELL_W*1-1, MAP_CELL_W*1, MAP_CELL_W*1+1,
  233. -MAP_CELL_W*1-1, -MAP_CELL_W*1, -MAP_CELL_W*1+1,
  234. MAP_CELL_W*2-1, MAP_CELL_W*2, MAP_CELL_W*2+1,
  235. -MAP_CELL_W*2-1, -MAP_CELL_W*2, -MAP_CELL_W*2+1,
  236. -MAP_CELL_W*3-1, -MAP_CELL_W*3, -MAP_CELL_W*3+1,
  237. REFRESH_EOL
  238. };
  239. return(_list);
  240. // return(Coord_Spillage_List(Coord, 64));
  241. }
  242. /*
  243. ** Flying units need a special adjustment to the spillage list to take into account
  244. ** that the bullet imagery and the shadow are widely separated.
  245. */
  246. if (Height > 0) {
  247. static short _list[25];
  248. const short * ptr = Coord_Spillage_List(Coord, 5);
  249. int index = 0;
  250. CELL cell1 = Coord_Cell(Coord);
  251. while (ptr[index] != REFRESH_EOL) {
  252. _list[index] = ptr[index];
  253. index++;
  254. }
  255. COORDINATE coord = Coord_Move(Coord, DIR_N, Height);
  256. CELL cell2 = Coord_Cell(coord);
  257. ptr = Coord_Spillage_List(coord, 5);
  258. while (*ptr != REFRESH_EOL) {
  259. _list[index++] = *ptr + (cell2 - cell1);
  260. ptr++;
  261. }
  262. _list[index] = REFRESH_EOL;
  263. return(_list);
  264. }
  265. return(Coord_Spillage_List(Coord, 10));
  266. }
  267. /***********************************************************************************************
  268. * BulletClass::Mark -- Performs related map refreshing under bullet. *
  269. * *
  270. * This routine marks the objects under the bullet so that they will *
  271. * be redrawn. This is necessary as the bullet moves -- objects under *
  272. * its path need to be restored. *
  273. * *
  274. * INPUT: none *
  275. * *
  276. * OUTPUT: none *
  277. * *
  278. * WARNINGS: none *
  279. * *
  280. * HISTORY: *
  281. * 05/02/1994 JLB : Created. *
  282. *=============================================================================================*/
  283. bool BulletClass::Mark(MarkType mark)
  284. {
  285. assert(Bullets.ID(this) == ID);
  286. assert(IsActive);
  287. if (ObjectClass::Mark(mark)) {
  288. if (!Class->IsInvisible) {
  289. Map.Refresh_Cells(Coord_Cell(Coord), Occupy_List());
  290. }
  291. return(true);
  292. }
  293. return(false);
  294. }
  295. /***********************************************************************************************
  296. * BulletClass::AI -- Logic processing for bullet. *
  297. * *
  298. * This routine will perform all logic (flight) logic on the bullet. *
  299. * Primarily this is motion, fuse tracking, and detonation logic. Call *
  300. * this routine no more than once per bullet per game tick. *
  301. * *
  302. * INPUT: none *
  303. * *
  304. * OUTPUT: none *
  305. * *
  306. * WARNINGS: none *
  307. * *
  308. * HISTORY: *
  309. * 05/02/1994 JLB : Created. *
  310. *=============================================================================================*/
  311. void BulletClass::AI(void)
  312. {
  313. assert(Bullets.ID(this) == ID);
  314. assert(IsActive);
  315. COORDINATE coord;
  316. ObjectClass::AI();
  317. if (!IsActive) return;
  318. /*
  319. ** Ballistic objects are handled here.
  320. */
  321. bool forced = false; // Forced explosion.
  322. if ((Class->IsArcing || Class->IsDropping) && !IsFalling) {
  323. forced = true;
  324. }
  325. /*
  326. ** Homing projectiles constantly change facing to face toward the target but
  327. ** they only do so every other game frame (improves game speed and makes
  328. ** missiles not so deadly).
  329. */
  330. if ((Frame & 0x01) && Class->ROT != 0 && Target_Legal(TarCom)) {
  331. PrimaryFacing.Set_Desired(Direction256(Coord, ::As_Coord(TarCom)));
  332. }
  333. /*
  334. ** Move the projectile forward according to its speed
  335. ** and direction.
  336. */
  337. coord = Coord;
  338. if (Class->IsFlameEquipped) {
  339. if (IsToAnimate) {
  340. if (stricmp(Class->GraphicName, "FB1") == 0) {
  341. new AnimClass(ANIM_FBALL_FADE, coord, 1);
  342. } else {
  343. new AnimClass(ANIM_SMOKE_PUFF, coord, 1);
  344. }
  345. }
  346. IsToAnimate = !IsToAnimate;
  347. }
  348. /*
  349. ** Handle any body rotation at this time. This process must
  350. ** occur every game fame in order to achieve smooth rotation.
  351. */
  352. if (PrimaryFacing.Is_Rotating()) {
  353. PrimaryFacing.Rotation_Adjust(Class->ROT);
  354. }
  355. switch (Physics(coord, PrimaryFacing)) {
  356. /*
  357. ** When a projectile reaches the edge of the world, it
  358. ** vanishes from existence -- presumed to explode off
  359. ** map.
  360. */
  361. case IMPACT_EDGE:
  362. Mark();
  363. if (Payback != NULL && Class->Type == BULLET_GPS_SATELLITE) {
  364. bool reveal = false;
  365. if (Session.Type != GAME_GLYPHX_MULTIPLAYER) {
  366. if (Payback->House == PlayerPtr) {
  367. reveal = true;
  368. }
  369. } else {
  370. if (Payback->House->IsHuman) {
  371. reveal = true;
  372. }
  373. }
  374. if (reveal) {
  375. if (!Map.Is_Radar_Active()) {
  376. Map.Radar_Activate(1);
  377. }
  378. for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) {
  379. Map.Map_Cell(cell, Payback->House);
  380. }
  381. Map.RadarClass::Flag_To_Redraw(true);
  382. }
  383. Payback->House->IsGPSActive = true;
  384. Payback->House->IsVisionary = true;
  385. }
  386. #ifdef OBSOLETE
  387. /*
  388. ** Hack: If it's the artificial nukes, don't let the bullets come down (as
  389. ** they're the only ones that blow up). We know it's artificial if you're
  390. ** at tech level 10 or below, because you can't build the nuclear silo until
  391. ** tech level 15 or so.
  392. */
  393. if (Payback != NULL && Class->Type == BULLET_NUKE_UP && Payback->House->Control.TechLevel <= 10) {
  394. BulletClass * bullet = new BulletClass(BULLET_NUKE_DOWN, ::As_Target(Payback->House->NukeDest), Payback, 200, WARHEAD_NUKE, MPH_VERY_FAST);
  395. if (bullet) {
  396. int celly = Cell_Y(Payback->House->NukeDest);
  397. celly -= 15;
  398. if (celly < 1) celly = 1;
  399. COORDINATE start = Cell_Coord(XY_Cell(Cell_X(Payback->House->NukeDest), celly));
  400. if (!bullet->Unlimbo(start, DIR_S)) {
  401. delete bullet;
  402. }
  403. }
  404. }
  405. #endif
  406. delete this;
  407. break;
  408. default:
  409. case IMPACT_NONE:
  410. /*
  411. ** The projectile has moved. Check its fuse. If detonation
  412. ** is signaled, then do so. Otherwise, just move.
  413. */
  414. case IMPACT_NORMAL:
  415. Mark();
  416. // if(Class->Type == BULLET_NUKE_DOWN) {
  417. // Render(true);
  418. // }
  419. if (Class->Type == BULLET_NUKE_UP) {
  420. if (Payback != NULL) {
  421. if (Distance(Payback->As_Target()) > 0x0C00) {
  422. delete this;
  423. return;
  424. }
  425. }
  426. }
  427. Coord = coord;
  428. /*
  429. ** See if the bullet should be forced to explode now in spite of what
  430. ** the fuse would otherwise indicate. Maybe the bullet hit a wall?
  431. */
  432. if (!forced) {
  433. forced = Is_Forced_To_Explode(Coord);
  434. }
  435. /*
  436. ** If the bullet is not to explode, then perform normal flight
  437. ** maintenance (usually nothing). Otherwise, explode and then
  438. ** delete the bullet.
  439. */
  440. if (!forced && (Class->IsDropping || !Fuse_Checkup(Coord))) {
  441. /*
  442. ** Certain projectiles lose strength when they travel.
  443. */
  444. if (Class->IsDegenerate && Strength > 5) {
  445. Strength--;
  446. }
  447. } else {
  448. Bullet_Explodes(forced);
  449. delete this;
  450. }
  451. break;
  452. }
  453. }
  454. /***********************************************************************************************
  455. * BulletClass::Shape_Number -- Fetches the shape number for the bullet object. *
  456. * *
  457. * Use this routine to fetch a shape number to use for this bullet object. *
  458. * *
  459. * INPUT: none *
  460. * *
  461. * OUTPUT: Returns with the shape number to use when drawing this bullet. *
  462. * *
  463. * WARNINGS: none *
  464. * *
  465. * HISTORY: *
  466. * 08/06/1996 JLB : Created. *
  467. *=============================================================================================*/
  468. int BulletClass::Shape_Number(void) const
  469. {
  470. int shapenum = 0;
  471. if (!Class->IsFaceless) {
  472. shapenum = UnitClass::BodyShape[Dir_To_32(PrimaryFacing)];
  473. }
  474. /*
  475. ** For tumbling projectiles, fetch offset stage.
  476. */
  477. if (Class->Tumble > 0) {
  478. shapenum += (long)Frame % Class->Tumble;
  479. }
  480. return(shapenum);
  481. }
  482. /***********************************************************************************************
  483. * BulletClass::Draw_It -- Displays the bullet at location specified. *
  484. * *
  485. * This routine displays the bullet visual at the location specified. *
  486. * *
  487. * INPUT: x,y -- The center coordinate to render the bullet at. *
  488. * *
  489. * window -- The window to clip to. *
  490. * *
  491. * OUTPUT: none *
  492. * *
  493. * WARNINGS: none *
  494. * *
  495. * HISTORY: *
  496. * 06/20/1994 JLB : Created. *
  497. * 06/27/1994 JLB : Takes a window clipping parameter. *
  498. * 01/08/1995 JLB : Handles translucent colors if necessary. *
  499. *=============================================================================================*/
  500. void BulletClass::Draw_It(int x, int y, WindowNumberType window) const
  501. {
  502. assert(Bullets.ID(this) == ID);
  503. assert(IsActive);
  504. /*
  505. ** Certain projectiles aren't visible. This includes small bullets (which are actually
  506. ** invisible) and flame thrower flames (which are rendered as an animation instead of a projectile).
  507. */
  508. if (Class->IsInvisible) return;
  509. /*
  510. ** If there is no shape loaded for this object, then
  511. ** it obviously can't be rendered -- just bail.
  512. */
  513. void const * shapeptr = Get_Image_Data();
  514. if (shapeptr == NULL) return;
  515. /*
  516. ** Get the basic shape number for this projectile.
  517. */
  518. int shapenum = Shape_Number();
  519. /*
  520. ** For flying projectiles, draw the shadow and adjust the actual projectile body
  521. ** render position.
  522. */
  523. if (Height > 0 && Class->IsShadow) {
  524. if (Class->IsParachuted) {
  525. // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019
  526. CC_Draw_Shape(this, AnimTypeClass::As_Reference(ANIM_PARA_BOMB).Get_Image_Data(), 1, x+Lepton_To_Pixel(Height/2), y+10, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, NULL, DisplayClass::UnitShadow);
  527. } else {
  528. // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019
  529. CC_Draw_Shape(this, shapeptr, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, NULL, DisplayClass::UnitShadow);
  530. }
  531. y -= Lepton_To_Pixel(Height);
  532. }
  533. /*
  534. ** Draw the main body of the projectile.
  535. */
  536. ShapeFlags_Type flags = SHAPE_NORMAL;
  537. if (Class->IsTranslucent) {
  538. flags = SHAPE_GHOST;
  539. }
  540. if (Class->IsSubSurface)
  541. {
  542. // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019
  543. CC_Draw_Shape(this, shapeptr, shapenum, x, y, window, flags|SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, NULL, DisplayClass::FadingShade);
  544. } else {
  545. // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019
  546. CC_Draw_Shape(this, shapeptr, shapenum, x, y, window, flags|SHAPE_CENTER|SHAPE_WIN_REL, NULL, DisplayClass::UnitShadow);
  547. }
  548. }
  549. /***********************************************************************************************
  550. * BulletClass::Init -- Clears the bullets array for scenario preparation. *
  551. * *
  552. * This routine will zero out the bullet tracking list and object array in preparation for *
  553. * the start of a new scenario. All bullets cease to exists after this function is *
  554. * called. *
  555. * *
  556. * INPUT: none *
  557. * *
  558. * OUTPUT: none *
  559. * *
  560. * WARNINGS: none *
  561. * *
  562. * HISTORY: *
  563. * 08/15/1994 JLB : Created. *
  564. *=============================================================================================*/
  565. void BulletClass::Init(void)
  566. {
  567. Bullets.Free_All();
  568. }
  569. /***********************************************************************************************
  570. * BulletClass::Detach -- Removes specified target from this bullet's targeting system. *
  571. * *
  572. * When an object is removed from the game system, it must be removed all targeting and *
  573. * tracking systems as well. This routine is used to remove the specified object from the *
  574. * bullet. If the object isn't part of this bullet's tracking system, then no action is *
  575. * performed. *
  576. * *
  577. * INPUT: target -- The target to remove from this tracking system. *
  578. * *
  579. * all -- Is the target going away for good as opposed to just cloaking/hiding? *
  580. * *
  581. * OUTPUT: none *
  582. * *
  583. * WARNINGS: none *
  584. * *
  585. * HISTORY: *
  586. * 09/24/1994 JLB : Created. *
  587. *=============================================================================================*/
  588. void BulletClass::Detach(TARGET target, bool all)
  589. {
  590. assert(Bullets.ID(this) == ID);
  591. assert(IsActive);
  592. ObjectClass * obj = As_Object(target);
  593. if (Payback != NULL && obj == Payback) {
  594. /*
  595. ** If we're being called as a result of the dog that fired us being put
  596. ** in limbo, then don't detach. If for any other reason, detach.
  597. */
  598. if (Payback->What_Am_I() != RTTI_INFANTRY || !((InfantryClass *)Payback)->Class->IsDog) {
  599. Payback = 0;
  600. }
  601. }
  602. if (all && target == TarCom) {
  603. TarCom = TARGET_NONE;
  604. }
  605. }
  606. /***********************************************************************************************
  607. * BulletClass::Unlimbo -- Transitions a bullet object into the game render/logic system. *
  608. * *
  609. * This routine is used to take a bullet object that is in limbo and transition it to the *
  610. * game system. A bullet object so transitioned, will be drawn and logic processing *
  611. * performed. In effect, it comes into existence. *
  612. * *
  613. * INPUT: coord -- The location where the bullet object is to appear. *
  614. * *
  615. * dir -- The initial facing for the bullet object. *
  616. * *
  617. * OUTPUT: bool; Was the unlimbo successful? *
  618. * *
  619. * WARNINGS: none *
  620. * *
  621. * HISTORY: *
  622. * 01/10/1995 JLB : Created. *
  623. *=============================================================================================*/
  624. bool BulletClass::Unlimbo(COORDINATE coord, DirType dir)
  625. {
  626. assert(Bullets.ID(this) == ID);
  627. assert(IsActive);
  628. /*
  629. ** Try to unlimbo the bullet as far as the base class is concerned. Use the already
  630. ** set direction and strength if the "punt" values were passed in. This allows a bullet
  631. ** to be setup prior to being launched.
  632. */
  633. if (!Class->IsHigh) {
  634. Height = 0;
  635. }
  636. if (ObjectClass::Unlimbo(coord)) {
  637. Map.Remove(this, In_Which_Layer());
  638. COORDINATE tcoord = As_Coord(TarCom);
  639. /*
  640. ** Homing projectiles (missiles) do NOT override facing. They just fire in the
  641. ** direction specified and let the chips fall where they may.
  642. */
  643. if (Class->ROT == 0 && !Class->IsDropping) {
  644. dir = Direction(tcoord);
  645. }
  646. /*
  647. ** Possibly adjust the target if this projectile is inaccurate. This occurs whenever
  648. ** certain weapons are trained upon targets they were never designed to attack. Example: when
  649. ** turrets or anti-tank missiles are fired at infantry. Indirect
  650. ** fire is inherently inaccurate.
  651. */
  652. if (IsInaccurate || Class->IsInaccurate ||
  653. ((Is_Target_Cell(TarCom) || Is_Target_Infantry(TarCom)) && (Warhead == WARHEAD_AP || Class->IsFueled))) {
  654. /*
  655. ** Inaccuracy for low velocity or homing projectiles manifests itself as a standard
  656. ** Circular Error of Probability (CEP) algorithm. High speed projectiles usually
  657. ** just overshoot the target by extending the straight line flight.
  658. */
  659. if (/*Class->ROT != 0 ||*/ Class->IsArcing) {
  660. int scatterdist = (::Distance(coord, tcoord)/16)-0x0040;
  661. scatterdist = min(scatterdist, Rule.HomingScatter);
  662. scatterdist = max(scatterdist, 0);
  663. dir = (DirType)((dir + (Random_Pick(0, 10)-5)) & 0x00FF);
  664. tcoord = Coord_Scatter(tcoord, Random_Pick(0, scatterdist));
  665. } else {
  666. int scatterdist = (::Distance(coord, tcoord)/16)-0x0040;
  667. scatterdist = min(scatterdist, Rule.BallisticScatter);
  668. scatterdist = max(scatterdist, 0);
  669. tcoord = Coord_Move(tcoord, dir, Random_Pick(0, scatterdist));
  670. }
  671. }
  672. /*
  673. ** For very fast and invisible projectiles, just make the projectile exist at the target
  674. ** location and dispense with the actual flight.
  675. */
  676. if (MaxSpeed == MPH_LIGHT_SPEED && Class->IsInvisible) {
  677. Coord = tcoord;
  678. }
  679. /*
  680. ** Set the range equal to either the class defined range or the calculated
  681. ** number of game frames it would take for the projectile to reach the target.
  682. */
  683. int range = 0xFF;
  684. if (!Class->IsDropping) {
  685. range = (::Distance(tcoord, Coord) / MaxSpeed) + 4;
  686. }
  687. /*
  688. ** Projectile speed is usually the default value for that projectile, but
  689. ** certain projectiles alter speed according to the distance to the
  690. ** target.
  691. */
  692. int speed = MaxSpeed;
  693. if (speed == MPH_LIGHT_SPEED) speed = MPH_IMMOBILE;
  694. if (Class->IsArcing) {
  695. speed = MaxSpeed + (Distance(tcoord) / 32);
  696. /*
  697. ** Set minimum speed (i.e., distance) for arcing projectiles.
  698. */
  699. speed = max(speed, 25);
  700. }
  701. if (!Class->IsDropping) {
  702. Fly_Speed(255, (MPHType)speed);
  703. }
  704. /*
  705. ** Arm the fuse.
  706. */
  707. Arm_Fuse(Coord, tcoord, range, ((As_Aircraft(TarCom)!=0) ? 0 : Class->Arming));
  708. /*
  709. ** Projectiles that make a ballistic flight to impact point must determine a
  710. ** vertical component for the projectile launch. This is crudely simulated
  711. ** by biasing ground speed according to target distance and then giving
  712. ** enough vertical velocity to keep the projectile airborne for the
  713. ** desired amount of time. The mathematically correct solution would be to
  714. ** calculate launch angle (given fixed projectile velocity) and then derive
  715. ** the vertical and horizontal components. That solution would require a
  716. ** square root and an arcsine lookup table. OUCH!
  717. */
  718. Riser = 0;
  719. if (Class->IsArcing) {
  720. IsFalling = true;
  721. Height = 1;
  722. Riser = ((Distance(tcoord)/2) / (speed+1)) * Rule.Gravity;
  723. Riser = max(Riser, 10);
  724. }
  725. if (Class->IsDropping) {
  726. IsFalling = true;
  727. Height = FLIGHT_LEVEL;
  728. // Height = Pixel_To_Lepton(24);
  729. Riser = 0;
  730. if (Class->IsParachuted) {
  731. AnimClass * anim = new AnimClass(ANIM_PARA_BOMB, Target_Coord());
  732. // AnimClass * anim = new AnimClass(ANIM_PARACHUTE, Target_Coord());
  733. if (anim) {
  734. anim->Attach_To(this);
  735. }
  736. }
  737. }
  738. Map.Submit(this, In_Which_Layer());
  739. PrimaryFacing = dir;
  740. return(true);
  741. }
  742. return(false);
  743. }
  744. /***********************************************************************************************
  745. * BulletClass::Target_Coord -- Fetches coordinate to use when firing on this object. *
  746. * *
  747. * *
  748. * INPUT: none *
  749. * *
  750. * OUTPUT: Returns with the coordinate that should be used when firing at the object. *
  751. * *
  752. * WARNINGS: none *
  753. * *
  754. * HISTORY: *
  755. * 09/21/1995 JLB : Created. *
  756. *=============================================================================================*/
  757. COORDINATE BulletClass::Target_Coord(void) const
  758. {
  759. assert(Bullets.ID(this) == ID);
  760. assert(IsActive);
  761. return(Coord_Add(XY_Coord(0, -Height), Coord));
  762. }
  763. /***********************************************************************************************
  764. * BulletClass::Sort_Y -- Sort coordinate for bullet rendering. *
  765. * *
  766. * This will return the coordinate to use when sorting this bullet in the display list. *
  767. * Typically, this only occurs for bullets in the ground layer. Since bullets are to be *
  768. * seen a bit more than the normal sorting order would otherwise imply, bias the sort *
  769. * value such that bullets will tend to be drawn on top of the objects. *
  770. * *
  771. * INPUT: none *
  772. * *
  773. * OUTPUT: Returns with the coordinate to use when sorting this bullet in the display list. *
  774. * *
  775. * WARNINGS: none *
  776. * *
  777. * HISTORY: *
  778. * 10/02/1996 JLB : Created. *
  779. *=============================================================================================*/
  780. COORDINATE BulletClass::Sort_Y(void) const
  781. {
  782. assert(this != 0);
  783. assert(IsActive);
  784. return(Coord_Move(Coord, DIR_S, CELL_LEPTON_H/2));
  785. }
  786. /***********************************************************************************************
  787. * BulletClass::In_Which_Layer -- Fetches the layer that the bullet resides in. *
  788. * *
  789. * This examines the bullet to determine what rendering layer it should be in. The *
  790. * normal logic applies unless this is a torpedo. A torpedo is always in the surface *
  791. * layer. *
  792. * *
  793. * INPUT: none *
  794. * *
  795. * OUTPUT: Returns with the render layer that this bullet should reside in. *
  796. * *
  797. * WARNINGS: none *
  798. * *
  799. * HISTORY: *
  800. * 10/10/1996 JLB : Created. *
  801. *=============================================================================================*/
  802. LayerType BulletClass::In_Which_Layer(void) const
  803. {
  804. if (Class->IsSubSurface) {
  805. return(LAYER_SURFACE);
  806. }
  807. return(ObjectClass::In_Which_Layer());
  808. }
  809. /***********************************************************************************************
  810. * BulletClass::Is_Forced_To_Explode -- Checks if bullet should explode NOW. *
  811. * *
  812. * This routine will examine the bullet and where it is travelling in order to determine *
  813. * if it should prematurely explode. Typical of this would be when a bullet hits a wall *
  814. * or a torpedo hits a ship -- regardless of where the projectile was originally aimed. *
  815. * *
  816. * INPUT: coord -- The new coordinate to place the bullet at presuming it is forced to *
  817. * explode and a modification of the bullet's coordinate is needed. *
  818. * Otherwise, the coordinate is not modified. *
  819. * *
  820. * OUTPUT: bool; Should the bullet explode now? *
  821. * *
  822. * WARNINGS: none *
  823. * *
  824. * HISTORY: *
  825. * 10/10/1996 JLB : Created. *
  826. *=============================================================================================*/
  827. bool BulletClass::Is_Forced_To_Explode(COORDINATE & coord) const
  828. {
  829. coord = Coord;
  830. CellClass const * cellptr = &Map[coord];
  831. /*
  832. ** Check for impact on a wall or other high obstacle.
  833. */
  834. if (!Class->IsHigh && cellptr->Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(cellptr->Overlay).IsHigh) {
  835. coord = Cell_Coord(Coord_Cell(coord));
  836. return(true);
  837. }
  838. /*
  839. ** Check to make sure that underwater projectiles (torpedoes) will not
  840. ** travel in anything but water.
  841. */
  842. if (Class->IsSubSurface) {
  843. int d = ::Distance(Coord_Fraction(coord), XY_Coord(CELL_LEPTON_W/2, CELL_LEPTON_W/2));
  844. if (cellptr->Land_Type() != LAND_WATER || (d < CELL_LEPTON_W/3 && cellptr->Cell_Techno() != NULL && cellptr->Cell_Techno() != Payback)) {
  845. /*
  846. ** Force explosion to be at center of techno object if one is present.
  847. */
  848. if (cellptr->Cell_Techno() != NULL) {
  849. coord = cellptr->Cell_Techno()->Target_Coord();
  850. }
  851. /*
  852. ** However, if the torpedo was blocked by a bridge, then force the
  853. ** torpedo to explode on top of that bridge cell.
  854. */
  855. if (cellptr->Is_Bridge_Here()) {
  856. coord = Coord_Snap(coord);
  857. }
  858. return(true);
  859. }
  860. }
  861. /*
  862. ** Bullets are generally more effective when they are fired at aircraft.
  863. */
  864. if (Class->IsAntiAircraft && As_Aircraft(TarCom) && Distance(TarCom) < 0x0080) {
  865. return(true);
  866. }
  867. /*
  868. ** No reason for forced explosion was detected, so return 'false' to
  869. ** indicate that no forced explosion is required.
  870. */
  871. return(false);
  872. }
  873. /***********************************************************************************************
  874. * BulletClass::Bullet_Explodes -- Performs bullet explosion logic. *
  875. * *
  876. * This handles the exploding bullet action. It will generate the animation and the *
  877. * damage as necessary. *
  878. * *
  879. * INPUT: none *
  880. * *
  881. * OUTPUT: none *
  882. * *
  883. * WARNINGS: The bullet should be deleted after this routine is called. *
  884. * *
  885. * HISTORY: *
  886. * 10/10/1996 JLB : Created. *
  887. *=============================================================================================*/
  888. void BulletClass::Bullet_Explodes(bool forced)
  889. {
  890. /*
  891. ** When the target is reached, explode and do the damage
  892. ** required of it. For homing objects, don't force the explosion to
  893. ** match the target position. Non-homing projectiles adjust position so
  894. ** that they hit the target. This compensates for the error in line of
  895. ** flight logic.
  896. */
  897. if ( (Payback != NULL && Payback->What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)Payback)->Class->IsDog) ||
  898. (!forced && !Class->IsArcing && Class->ROT == 0 && Fuse_Target())) {
  899. Coord = Fuse_Target();
  900. }
  901. /*
  902. ** Non-aircraft targets apply damage to the ground.
  903. */
  904. if (!Is_Target_Aircraft(TarCom) || As_Aircraft(TarCom)->In_Which_Layer() == LAYER_GROUND) {
  905. Explosion_Damage(Coord, Strength, Payback, Warhead);
  906. if (!IsActive) return;
  907. } else {
  908. /*
  909. ** Special damage apply for SAM missiles. This is the only way that missile
  910. ** damage affects the aircraft target.
  911. */
  912. if (Distance(TarCom) < 0x0080) {
  913. AircraftClass * object = As_Aircraft(TarCom);
  914. int str = Strength;
  915. if (object) object->Take_Damage(str, 0, Warhead, Payback);
  916. }
  917. }
  918. /*
  919. ** For projectiles that are invisible while travelling toward the target,
  920. ** allow scatter effect for the impact animation.
  921. */
  922. if (Class->IsInvisible) {
  923. Coord = Coord_Scatter(Coord, 0x0020);
  924. }
  925. /*
  926. ** Fetch the land type that the explosion will be upon. Special case for
  927. ** flying aircraft targets, their land type will be LAND_NONE.
  928. */
  929. CellClass const * cellptr = &Map[Coord];
  930. LandType land = cellptr->Land_Type();
  931. if (Is_Target_Aircraft(TarCom) && As_Aircraft(TarCom)->In_Which_Layer() == LAYER_TOP) {
  932. land = LAND_NONE;
  933. }
  934. AnimType anim = Combat_Anim(Strength, Warhead, land);
  935. /*
  936. ** If it's a water explosion that's going to play, don't play it
  937. ** if its cell is the same as the center cell of the target ship.
  938. */
  939. if (anim >= ANIM_WATER_EXP1 && anim <= ANIM_WATER_EXP3 && Is_Target_Vessel(TarCom)) {
  940. if (Coord_Cell(Coord) == Coord_Cell(As_Vessel(TarCom)->Center_Coord())) {
  941. anim = (AnimType) (ANIM_VEH_HIT1 + (anim - ANIM_WATER_EXP1));
  942. }
  943. }
  944. if (anim != ANIM_NONE) {
  945. AnimClass * aptr = new AnimClass(anim, Coord);
  946. if (aptr) {
  947. aptr->Sort_Above(TarCom);
  948. }
  949. /*
  950. ** Special case trap: if they're making the nuclear explosion,
  951. ** and no anim is available, force the nuclear damage anyway
  952. ** because nuke damage is done in the middle of the animation
  953. ** and if there's no animation, there won't be any damage.
  954. */
  955. if (!aptr && anim == ANIM_ATOM_BLAST) {
  956. GlyphX_Debug_Print("FAILED to create ANIM_ATOM_BLAST");
  957. HousesType house = HOUSE_NONE;
  958. if (Payback) {
  959. house = Payback->House->Class->House;
  960. }
  961. AnimClass::Do_Atom_Damage(house, Coord_Cell(Coord));
  962. }
  963. // MBL 05.20.2020
  964. // Fix for Nuke or Atom Bomb killing structures and units during animation sequence and not getting kills tracked
  965. // Per https://jaas.ea.com/browse/TDRA-6610
  966. //
  967. else if (aptr && anim == ANIM_ATOM_BLAST && aptr->OwnerHouse == HOUSE_NONE) {
  968. if (Payback && Payback->House && Payback->House->Class) {
  969. aptr->Set_Owner(Payback->House->Class->House);
  970. }
  971. }
  972. }
  973. // if (Payback && Payback->House == PlayerPtr && stricmp(Class->Name(), "GPSSATELLITE") == 0) {
  974. if (Payback && Class->Type == BULLET_GPS_SATELLITE) {
  975. bool reveal = false;
  976. if (Session.Type != GAME_GLYPHX_MULTIPLAYER) {
  977. if (Payback->House == PlayerPtr) {
  978. reveal = true;
  979. }
  980. } else {
  981. if (Payback->House->IsHuman) {
  982. reveal = true;
  983. }
  984. }
  985. if (reveal) {
  986. if (!Map.Is_Radar_Active()) {
  987. Map.Radar_Activate(1);
  988. }
  989. for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) {
  990. Map.Map_Cell(cell, Payback->House);
  991. }
  992. Map.RadarClass::Flag_To_Redraw(true);
  993. }
  994. // Sound_Effect(VOC_SATTACT2);
  995. Payback->House->IsGPSActive = true;
  996. Payback->House->IsVisionary = true;
  997. }
  998. }