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