BULLET.CPP 50 KB

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