TURRET.CPP 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  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: F:\projects\c&c\vcs\code\turret.cpv 2.18 16 Oct 1995 16:50:38 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 : TURRET.CPP *
  22. * *
  23. * Programmer : Joe L. Bostic *
  24. * *
  25. * Start Date : April 25, 1994 *
  26. * *
  27. * Last Update : August 13, 1995 [JLB] *
  28. * *
  29. *---------------------------------------------------------------------------------------------*
  30. * Functions: *
  31. * TurretClass::AI -- Handles the reloading of the turret weapon. *
  32. * TurretClass::Can_Fire -- Determines if turret can fire upon target. *
  33. * TurretClass::Debug_Dump -- Debug printing of turret values. *
  34. * TurretClass::Fire_At -- Try to fire upon the target specified. *
  35. * TurretClass::Fire_Coord -- Determines the coorindate that projectile would appear. *
  36. * TurretClass::Fire_Direction -- Determines the directinon of firing. *
  37. * TurretClass::Ok_To_Move -- Queries whether the vehicle can move. *
  38. * TurretClass::TurretClass -- Normal constructor for the turret class. *
  39. * TurretClass::TurretClass -- The default constructor for turret class objects. *
  40. * TurretClass::Unlimbo -- Unlimboes turret object. *
  41. * TurretClass::~TurretClass -- Default destructor for turret class objects. *
  42. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  43. #include "function.h"
  44. #include "turret.h"
  45. /***********************************************************************************************
  46. * TurretClass::~TurretClass -- Default destructor for turret class objects. *
  47. * *
  48. * This is the default destructor for turret class objects. It does nothing. *
  49. * *
  50. * INPUT: none *
  51. * *
  52. * OUTPUT: none *
  53. * *
  54. * WARNINGS: none *
  55. * *
  56. * HISTORY: *
  57. * 08/13/1995 JLB : Created. *
  58. *=============================================================================================*/
  59. TurretClass::~TurretClass(void)
  60. {
  61. }
  62. /***********************************************************************************************
  63. * TurretClass::TurretClass -- The default constructor for turret class objects. *
  64. * *
  65. * This is the default constructor for turret class objects. It does nothing. *
  66. * *
  67. * INPUT: none *
  68. * *
  69. * OUTPUT: none *
  70. * *
  71. * WARNINGS: none *
  72. * *
  73. * HISTORY: *
  74. * 08/13/1995 JLB : Created. *
  75. *=============================================================================================*/
  76. TurretClass::TurretClass(void)
  77. {
  78. }
  79. /***********************************************************************************************
  80. * TurretClass::TurretClass -- Normal constructor for the turret class. *
  81. * *
  82. * This is the normal constructor for the turret class. It merely sets the turret up to *
  83. * face north. *
  84. * *
  85. * INPUT: classid -- The type id for this particular unit. *
  86. * *
  87. * house -- The house that this unit will belong to. *
  88. * *
  89. * OUTPUT: none *
  90. * *
  91. * WARNINGS: none *
  92. * *
  93. * HISTORY: *
  94. * 02/02/1995 JLB : Created. *
  95. *=============================================================================================*/
  96. TurretClass::TurretClass(UnitType classid, HousesType house) :
  97. DriveClass(classid, house)
  98. {
  99. Reload = 0;
  100. }
  101. #ifdef CHEAT_KEYS
  102. /***********************************************************************************************
  103. * TurretClass::Debug_Dump -- Debug printing of turret values. *
  104. * *
  105. * This routine is used to display the current values of this turret *
  106. * class instance. It is primarily used in the debug output screen. *
  107. * *
  108. * INPUT: x,y -- Monochrome screen coordinates to display data. *
  109. * *
  110. * OUTPUT: none *
  111. * *
  112. * WARNINGS: none *
  113. * *
  114. * HISTORY: *
  115. * 05/12/1994 JLB : Created. *
  116. *=============================================================================================*/
  117. void TurretClass::Debug_Dump(MonoClass *mono) const
  118. {
  119. mono->Set_Cursor(36, 3);mono->Printf("%02X:%02X", SecondaryFacing.Current(), SecondaryFacing.Desired());
  120. mono->Set_Cursor(28, 7);mono->Printf("%2d", Arm);
  121. DriveClass::Debug_Dump(mono);
  122. }
  123. #endif
  124. /***********************************************************************************************
  125. * TurretClass::Ok_To_Move -- Queries whether the vehicle can move. *
  126. * *
  127. * This virtual routine is used to determine if the vehicle is allowed *
  128. * to start moving. It is typically called when the vehicle desires *
  129. * to move but needs confirmation from the turret logic before *
  130. * proceeding. This happens when dealing with a vehicle that must have *
  131. * its turret face the same direction as the body before the vehicle *
  132. * may begin movement. *
  133. * *
  134. * INPUT: dir -- The facing the unit wants to travel in. *
  135. * *
  136. * OUTPUT: bool; Can the unit begin forward movement now? *
  137. * *
  138. * WARNINGS: none *
  139. * *
  140. * HISTORY: *
  141. * 05/12/1994 JLB : Created. *
  142. *=============================================================================================*/
  143. bool TurretClass::Ok_To_Move(DirType dir)
  144. {
  145. if (Class->IsLockTurret) {
  146. if (IsRotating) {
  147. return(false);
  148. } else {
  149. if (SecondaryFacing.Difference(dir)) {
  150. SecondaryFacing.Set_Desired(dir);
  151. return(false);
  152. }
  153. }
  154. }
  155. return(true);
  156. }
  157. /***********************************************************************************************
  158. * TurretClass::AI -- Handles the reloading of the turret weapon. *
  159. * *
  160. * This processes the reloading of the turret. It does this by decrementing the arming *
  161. * countdown timer and when it reaches zero, the turret may fire. *
  162. * *
  163. * INPUT: none *
  164. * *
  165. * OUTPUT: none *
  166. * *
  167. * WARNINGS: none *
  168. * *
  169. * HISTORY: *
  170. * 06/21/1994 JLB : Created. *
  171. *=============================================================================================*/
  172. void TurretClass::AI(void)
  173. {
  174. DriveClass::AI();
  175. /*
  176. ** A unit with a constant rotating radar dish is handled here.
  177. */
  178. if (Class->IsRadarEquipped) {
  179. SecondaryFacing.Set((DirType)(SecondaryFacing.Current() + 8));
  180. Mark(MARK_CHANGE);
  181. } else {
  182. IsRotating = false;
  183. if (Class->IsTurretEquipped) {
  184. if (IsTurretLockedDown) {
  185. SecondaryFacing.Set_Desired(PrimaryFacing.Current());
  186. }
  187. if (SecondaryFacing.Is_Rotating()) {
  188. if (SecondaryFacing.Rotation_Adjust(Class->ROT+1)) {
  189. Mark(MARK_CHANGE);
  190. }
  191. /*
  192. ** If no further rotation is necessary, flag that the rotation
  193. ** has stopped.
  194. */
  195. IsRotating = SecondaryFacing.Is_Rotating();
  196. } else {
  197. if (!IsTurretLockedDown && !Target_Legal(TarCom)) {
  198. if (!Target_Legal(NavCom)) {
  199. SecondaryFacing.Set_Desired(PrimaryFacing.Current());
  200. } else {
  201. SecondaryFacing.Set_Desired(Direction(NavCom));
  202. }
  203. }
  204. }
  205. }
  206. }
  207. }
  208. /***********************************************************************************************
  209. * TurretClass::Fire_At -- Try to fire upon the target specified. *
  210. * *
  211. * This routine is the auto-fire logic for the turret. It will check *
  212. * to see if firing is technically legal given the specified target. *
  213. * If it is legal to fire, it does so. It is safe to call this routine *
  214. * every game tick. *
  215. * *
  216. * INPUT: target -- The target to fire upon. *
  217. * *
  218. * which -- Which weapon to use when firing. 0=primary, 1=secondary. *
  219. * *
  220. * OUTPUT: bool; Did firing occur? *
  221. * *
  222. * WARNINGS: none *
  223. * *
  224. * HISTORY: *
  225. * 04/26/1994 JLB : Created. *
  226. *=============================================================================================*/
  227. BulletClass * TurretClass::Fire_At(TARGET target, int which)
  228. {
  229. BulletClass * bullet = NULL;
  230. WeaponTypeClass const * weapon = (which == 0) ? &Weapons[Class->Primary] : &Weapons[Class->Secondary];
  231. if (Can_Fire(target, which) == FIRE_OK) {
  232. bullet = DriveClass::Fire_At(target, which);
  233. if (bullet) {
  234. /*
  235. ** Possible reload timer set.
  236. */
  237. if (*this == UNIT_MSAM && Reload == 0) {
  238. Reload = TICKS_PER_SECOND * 30;
  239. }
  240. }
  241. }
  242. return(bullet);
  243. }
  244. /***********************************************************************************************
  245. * TurretClass::Can_Fire -- Determines if turret can fire upon target. *
  246. * *
  247. * This routine determines if the turret can fire upon the target *
  248. * specified. *
  249. * *
  250. * INPUT: target -- The target to fire upon. *
  251. * *
  252. * which -- Which weapon to use to determine legality to fire. 0=primary, *
  253. * 1=secondary. *
  254. * *
  255. * OUTPUT: Returns the fire status type that indicates if firing is allowed and if not, why. *
  256. * *
  257. * WARNINGS: none *
  258. * *
  259. * HISTORY: *
  260. * 04/26/1994 JLB : Created. *
  261. * 06/01/1994 JLB : Returns reason why it can't fire. *
  262. *=============================================================================================*/
  263. FireErrorType TurretClass::Can_Fire(TARGET target, int which) const
  264. {
  265. DirType dir; // The facing to impart upon the projectile.
  266. int diff;
  267. FireErrorType fire = DriveClass::Can_Fire(target, which);
  268. if (fire == FIRE_OK) {
  269. WeaponTypeClass const * weapon = (which == 0) ? &Weapons[Class->Primary] : &Weapons[Class->Secondary];
  270. /*
  271. ** If this unit cannot fire while moving, then bail.
  272. */
  273. if ((!Class->IsTurretEquipped || Class->IsLockTurret) && Target_Legal(NavCom)) {
  274. return(FIRE_MOVING);
  275. }
  276. /*
  277. ** If the turret is rotating and the projectile isn't a homing type, then
  278. ** firing must be delayed until the rotation stops.
  279. */
  280. if (!IsFiring && IsRotating && !BulletTypeClass::As_Reference(weapon->Fires).IsHoming) {
  281. return(FIRE_ROTATING);
  282. }
  283. dir = Direction(target);
  284. /*
  285. ** Determine if the turret facing isn't too far off of facing the target.
  286. */
  287. if (Class->IsTurretEquipped) {
  288. diff = SecondaryFacing.Difference(dir);
  289. } else {
  290. diff = PrimaryFacing.Difference(dir);
  291. }
  292. diff = ABS(diff);
  293. /*
  294. ** Special flame tank logic.
  295. */
  296. if (weapon->Fires == BULLET_FLAME) {
  297. if (Dir_Facing(dir) == Dir_Facing(PrimaryFacing)) {
  298. diff = 0;
  299. }
  300. }
  301. if (BulletTypeClass::As_Reference(weapon->Fires).IsHoming) {
  302. diff >>= 2;
  303. }
  304. if (diff < 8) {
  305. return(DriveClass::Can_Fire(target, which));
  306. }
  307. return(FIRE_FACING);
  308. }
  309. return(fire);
  310. }
  311. /***********************************************************************************************
  312. * TurretClass::Fire_Coord -- Determines the coorindate that projectile would appear. *
  313. * *
  314. * Use this routine to determine the exact coordinate that a projectile would appear if it *
  315. * were fired from this unit. For units with turrets, typically, this would be at the end *
  316. * of the barrel. *
  317. * *
  318. * INPUT: none *
  319. * *
  320. * OUTPUT: Returns with coordinate of where a projectile should appear if this unit were *
  321. * to fire one. *
  322. * *
  323. * WARNINGS: none *
  324. * *
  325. * HISTORY: *
  326. * 12/28/1994 JLB : Created. *
  327. *=============================================================================================*/
  328. FireDataType TurretClass::Fire_Data(int which) const
  329. {
  330. COORDINATE coord = Center_Coord();
  331. int dist = 0;
  332. switch (Class->Type) {
  333. case UNIT_GUNBOAT:
  334. coord = Coord_Move(coord, PrimaryFacing.Current(), Pixel2Lepton[Class->TurretOffset]);
  335. dist = 0x0060;
  336. break;
  337. case UNIT_ARTY:
  338. coord = Coord_Move(coord, DIR_N, 0x0040);
  339. dist = 0x0060;
  340. break;
  341. case UNIT_FTANK:
  342. dist = 0x30;
  343. break;
  344. case UNIT_HTANK:
  345. coord = Coord_Move(coord, DIR_N, 0x0040);
  346. if (which == 0) {
  347. dist = 0x00C0;
  348. } else {
  349. dist = 0x0008;
  350. }
  351. break;
  352. case UNIT_LTANK:
  353. coord = Coord_Move(coord, DIR_N, 0x0020);
  354. dist = 0x00C0;
  355. break;
  356. case UNIT_MTANK:
  357. coord = Coord_Move(coord, DIR_N, 0x0030);
  358. dist = 0x00C0;
  359. break;
  360. case UNIT_APC:
  361. case UNIT_JEEP:
  362. case UNIT_BUGGY:
  363. coord = Coord_Move(coord, DIR_N, 0x0030);
  364. dist = 0x0030;
  365. break;
  366. #ifdef PETROGLYPH_EXAMPLE_MOD
  367. case UNIT_NUKE_TANK:
  368. coord = Coord_Move(coord, DIR_N, 0x00A0);
  369. dist = 0x00A0;
  370. break;
  371. #endif //PETROGLYPH_EXAMPLE_MOD
  372. }
  373. return {coord,dist};
  374. }
  375. COORDINATE TurretClass::Fire_Coord(int which) const
  376. {
  377. COORDINATE coord = Center_Coord();
  378. int dist = 0;
  379. int lateral = 0;
  380. DirType dir = PrimaryFacing.Current();
  381. if (Class->IsTurretEquipped) {
  382. dir = SecondaryFacing.Current();
  383. }
  384. switch (Class->Type) {
  385. case UNIT_GUNBOAT:
  386. coord = Coord_Move(coord, PrimaryFacing.Current(), Pixel2Lepton[Class->TurretOffset]);
  387. dist = 0x0060;
  388. break;
  389. case UNIT_ARTY:
  390. coord = Coord_Move(coord, DIR_N, 0x0040);
  391. dist = 0x0060;
  392. break;
  393. case UNIT_FTANK:
  394. dist = 0x30;
  395. if (IsSecondShot) {
  396. coord = Coord_Move(coord, (DirType)(dir+DIR_E), 0x20);
  397. } else {
  398. coord = Coord_Move(coord, (DirType)(dir+DIR_W), 0x20);
  399. }
  400. break;
  401. case UNIT_HTANK:
  402. coord = Coord_Move(coord, DIR_N, 0x0040);
  403. if (which == 0) {
  404. dist = 0x00C0;
  405. lateral = 0x0028;
  406. } else {
  407. dist = 0x0008;
  408. lateral = 0x0040;
  409. }
  410. if (IsSecondShot) {
  411. coord = Coord_Move(coord, (DirType)(dir+DIR_E), lateral);
  412. } else {
  413. coord = Coord_Move(coord, (DirType)(dir+DIR_W), lateral);
  414. }
  415. break;
  416. case UNIT_LTANK:
  417. coord = Coord_Move(coord, DIR_N, 0x0020);
  418. dist = 0x00C0;
  419. break;
  420. case UNIT_MTANK:
  421. coord = Coord_Move(coord, DIR_N, 0x0030);
  422. dist = 0x00C0;
  423. break;
  424. case UNIT_APC:
  425. case UNIT_JEEP:
  426. case UNIT_BUGGY:
  427. coord = Coord_Move(coord, DIR_N, 0x0030);
  428. dist = 0x0030;
  429. break;
  430. #ifdef PETROGLYPH_EXAMPLE_MOD
  431. case UNIT_NUKE_TANK:
  432. coord = Coord_Move(coord, DIR_N, 0x00A0);
  433. dist = 0x00A0;
  434. break;
  435. #endif //PETROGLYPH_EXAMPLE_MOD
  436. }
  437. if (dist) {
  438. coord = Coord_Move(coord, dir, dist);
  439. }
  440. return(coord);
  441. }
  442. /***********************************************************************************************
  443. * TurretClass::Unlimbo -- Unlimboes turret object. *
  444. * *
  445. * This routine is called when a turret equipped unit unlimboes. It sets the turret to *
  446. * face the same direction as the body. *
  447. * *
  448. * INPUT: coord -- The coordinate where the unit is unlimboing. *
  449. * *
  450. * dir -- The desired body and turret facing to use. *
  451. * *
  452. * OUTPUT: Was the unit unlimboed successfully? *
  453. * *
  454. * WARNINGS: none *
  455. * *
  456. * HISTORY: *
  457. * 06/25/1995 JLB : Created. *
  458. *=============================================================================================*/
  459. bool TurretClass::Unlimbo(COORDINATE coord, DirType dir)
  460. {
  461. if (DriveClass::Unlimbo(coord, dir)) {
  462. SecondaryFacing = dir;
  463. return(true);
  464. }
  465. return(false);
  466. }
  467. /***********************************************************************************************
  468. * TurretClass::Fire_Direction -- Determines the directinon of firing. *
  469. * *
  470. * This routine will return with the facing that a projectile will travel if it was *
  471. * fired at this instant. The facing should match the turret facing for those units *
  472. * equipped with a turret. If the unit doesn't have a turret, then it will be the facing *
  473. * of the body. *
  474. * *
  475. * INPUT: none *
  476. * *
  477. * OUTPUT: Returns with the default firing direction for a projectile. *
  478. * *
  479. * WARNINGS: none *
  480. * *
  481. * HISTORY: *
  482. * 06/25/1995 JLB : Created. *
  483. *=============================================================================================*/
  484. DirType TurretClass::Fire_Direction(void) const
  485. {
  486. if (Class->IsTurretEquipped) {
  487. if (*this == UNIT_MSAM) {
  488. int diff1 = SecondaryFacing.Difference(DIR_E);
  489. int diff2 = SecondaryFacing.Difference(DIR_W);
  490. diff1 = ABS(diff1);
  491. diff2 = ABS(diff2);
  492. int diff = MIN(diff1, diff2);
  493. int adj = Fixed_To_Cardinal(ABS(SecondaryFacing.Difference(DIR_N)), 64-diff);
  494. if (SecondaryFacing.Difference(DIR_N) < 0) {
  495. return(DirType)(SecondaryFacing - (DirType)adj);
  496. } else {
  497. return(DirType)(SecondaryFacing + (DirType)adj);
  498. }
  499. }
  500. return(SecondaryFacing.Current());
  501. }
  502. return(PrimaryFacing.Current());
  503. }