VESSEL.CPP (111373B)
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: /CounterStrike/VESSEL.CPP 1 3/03/97 10:26a 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 : VESSEL.CPP * 24 * * 25 * Programmer : Joe L. Bostic * 26 * * 27 * Start Date : 03/13/96 * 28 * * 29 * Last Update : July 31, 1996 [JLB] * 30 * * 31 *---------------------------------------------------------------------------------------------* 32 * Functions: * 33 * VesselClass::AI -- Handles the AI processing for vessel objects. * 34 * VesselClass::Assign_Destination -- Assign a destination for this vessel. * 35 * VesselClass::Can_Enter_Cell -- Determines if the vessel can enter the cell specified. * 36 * VesselClass::Can_Fire -- Determines if this vessel can fire its weapon. * 37 * VesselClass::Is_Allowed_To_Recloak -- Can the vessel recloak now? * 38 * VesselClass::Class_Of -- Fetches a reference to the vessel's class data. * 39 * VesselClass::Combat_AI -- Handles firing and target selection for the vessel. * 40 * VesselClass::Debug_Dump -- Dumps the vessel status information to the mono monitor. * 41 * VesselClass::Draw_It -- Draws the vessel. * 42 * VesselClass::Edge_Of_World_AI -- Determine if vessel is off the edge of the world. * 43 * VesselClass::Enter_Idle_Mode -- Causes the vessel to enter its default idle mode. * 44 * VesselClass::Fire_Coord -- Fetches the coordinate the firing originates from. * 45 * VesselClass::Greatest_Threat -- Determines the greatest threat (best target) for the vesse* 46 * VesselClass::Init -- Initialize the vessel heap system. * 47 * VesselClass::Mission_Retreat -- Perform the retreat mission. * 48 * VesselClass::Overlap_List -- Fetches the overlap list for this vessel object. * 49 * VesselClass::Per_Cell_Process -- Performs once-per-cell action. * 50 * VesselClass::Read_INI -- Read the vessel data from the INI database. * 51 * VesselClass::Repair_AI -- Process any self-repair required of this vessel. * 52 * VesselClass::Rotation_AI -- Handles turret and body rotation for this vessel. * 53 * VesselClass::Shape_Number -- Calculates the shape number for the ship body. * 54 * VesselClass::Start_Driver -- Starts the vessel by reserving the location it is moving to. * 55 * VesselClass::Take_Damage -- Assign damage to the vessel. * 56 * VesselClass::VesselClass -- Constructor for vessel class objects. * 57 * VesselClass::What_Action -- Determines action to perform on specified cell. * 58 * VesselClass::Write_INI -- Write all vessel scenario data to the INI database. * 59 * VesselClass::~VesselClass -- Destructor for vessel objects. * 60 * operator delete -- Deletes a vessel's memory block. * 61 * operator new -- Allocates a vessel object memory block. * 62 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 63 64 65 #include "function.h" 66 67 68 /*********************************************************************************************** 69 * VesselClass::VesselClass -- Constructor for vessel class objects. * 70 * * 71 * This is the normal constructor for vessel class objects. It will set up a vessel that * 72 * is valid excepting that it won't be placed on the map. * 73 * * 74 * INPUT: classid -- The type of vessel this will be. * 75 * * 76 * house -- The owner of this vessel. * 77 * * 78 * OUTPUT: none * 79 * * 80 * WARNINGS: none * 81 * * 82 * HISTORY: * 83 * 03/14/1996 JLB : Created. * 84 *=============================================================================================*/ 85 VesselClass::VesselClass(VesselType classid, HousesType house) : 86 DriveClass(RTTI_VESSEL, Vessels.ID(this), house), 87 Class(VesselTypes.Ptr((int)classid)), 88 IsToSelfRepair(false), 89 IsSelfRepairing(false), 90 DoorShutCountDown(0), 91 PulseCountDown(0), 92 SecondaryFacing(PrimaryFacing) 93 { 94 House->Tracking_Add(this); 95 96 /* 97 ** The ammo member is actually part of the techno class, but must be initialized 98 ** manually here because this is where we first have access to the class pointer. 99 */ 100 Ammo = Class->MaxAmmo; 101 102 /* 103 ** For two shooters, clear out the second shot flag -- it will be set the first time 104 ** the object fires. For non two shooters, set the flag since it will never be cleared 105 ** and the second shot flag tells the system that normal rearm times apply -- this is 106 ** what is desired for non two shooters. 107 */ 108 IsSecondShot = !Class->Is_Two_Shooter(); 109 Strength = Class->MaxStrength; 110 111 /* 112 ** The techno class cloakabilty flag is set according to the type 113 ** class cloakability flag. 114 */ 115 IsCloakable = Class->IsCloakable; 116 117 /* 118 ** Keep count of the number of units created. 119 */ 120 // if (Session.Type == GAME_INTERNET) { 121 // House->UnitTotals->Increment_Unit_Total((int)classid); 122 // } 123 } 124 125 126 /*********************************************************************************************** 127 * VesselClass::~VesselClass -- Destructor for vessel objects. * 128 * * 129 * The destructor will destroy the vessel and ensure that it is properly removed from the * 130 * game engine. * 131 * * 132 * INPUT: none * 133 * * 134 * OUTPUT: none * 135 * * 136 * WARNINGS: none * 137 * * 138 * HISTORY: * 139 * 03/14/1996 JLB : Created. * 140 *=============================================================================================*/ 141 VesselClass::~VesselClass(void) 142 { 143 if (GameActive && Class.Is_Valid()) { 144 145 /* 146 ** Remove this member from any team it may be associated with. This must occur at the 147 ** top most level of the inheritance hierarchy because it may call virtual functions. 148 */ 149 if (Team.Is_Valid()) { 150 Team->Remove(this); 151 Team = NULL; 152 } 153 154 House->Tracking_Remove(this); 155 156 /* 157 ** If there are any cargo members, delete them. 158 */ 159 while (Is_Something_Attached()) { 160 delete Detach_Object(); 161 } 162 163 Limbo(); 164 } 165 ID = -1; 166 } 167 168 169 /*********************************************************************************************** 170 * operator new -- Allocates a vessel object memory block. * 171 * * 172 * This routine is used to allocate a block of memory from the vessel heap. If there is * 173 * no more space in the heap, then this routine will return NULL. * 174 * * 175 * INPUT: none * 176 * * 177 * OUTPUT: Returns with the pointer to the allocated block of memory. * 178 * * 179 * WARNINGS: This routine could return NULL. * 180 * * 181 * HISTORY: * 182 * 03/14/1996 JLB : Created. * 183 *=============================================================================================*/ 184 void * VesselClass::operator new(size_t) 185 { 186 void * ptr = Vessels.Alloc(); 187 if (ptr != NULL) { 188 ((VesselClass *)ptr)->Set_Active(); 189 } 190 return(ptr); 191 } 192 193 194 /*********************************************************************************************** 195 * operator delete -- Deletes a vessel's memory block. * 196 * * 197 * This overloaded delete operator will return the vessel's memory back to the pool of * 198 * memory used for vessel allocation. * 199 * * 200 * INPUT: ptr -- Pointer to the vessel's memory block. * 201 * * 202 * OUTPUT: none * 203 * * 204 * WARNINGS: none * 205 * * 206 * HISTORY: * 207 * 03/14/1996 JLB : Created. * 208 *=============================================================================================*/ 209 void VesselClass::operator delete(void * ptr) 210 { 211 if (ptr != NULL) { 212 assert(((VesselClass *)ptr)->IsActive); 213 ((VesselClass *)ptr)->IsActive = false; 214 } 215 Vessels.Free((VesselClass *)ptr); 216 } 217 218 219 /*********************************************************************************************** 220 * VesselClass::Class_Of -- Fetches a reference to the vessel's class data. * 221 * * 222 * This routine will return with a reference to the static class data for this vessel. * 223 * * 224 * INPUT: none * 225 * * 226 * OUTPUT: Returns with a reference to the class data structure associated with this vessel. * 227 * * 228 * WARNINGS: none * 229 * * 230 * HISTORY: * 231 * 03/14/1996 JLB : Created. * 232 *=============================================================================================*/ 233 ObjectTypeClass const & VesselClass::Class_Of(void) const 234 { 235 assert(IsActive); 236 237 return(*Class); 238 } 239 240 241 /*********************************************************************************************** 242 * VesselClass::Can_Enter_Cell -- Determines if the vessel can enter the cell specified. * 243 * * 244 * This routine is used by find path and other movement logic to determine if this * 245 * vessel can enter the cell specified. * 246 * * 247 * INPUT: cell -- The cell to check this vessel against. * 248 * * 249 * OUTPUT: Returns with the movement restriction associated with movement into this object. * 250 * * 251 * WARNINGS: none * 252 * * 253 * HISTORY: * 254 * 03/14/1996 JLB : Created. * 255 *=============================================================================================*/ 256 MoveType VesselClass::Can_Enter_Cell(CELL cell, FacingType ) const 257 { 258 assert(Vessels.ID(this) == ID); 259 assert(IsActive); 260 261 if ((unsigned)cell >= MAP_CELL_TOTAL) return(MOVE_NO); 262 263 CellClass const * cellptr = &Map[cell]; 264 265 /* 266 ** Moving off the edge of the map is not allowed unless 267 ** this is a loaner vehicle. 268 */ 269 if (!ScenarioInit && !Map.In_Radar(cell) && !Is_Allowed_To_Leave_Map()) { 270 return(MOVE_NO); 271 } 272 273 MoveType retval = MOVE_OK; 274 275 /* 276 ** If there is blocking terrain (such as ice), then the vessel 277 ** can't move there. 278 */ 279 if (cellptr->Cell_Terrain() != NULL) { 280 return(MOVE_NO); 281 } 282 283 /* 284 ** If the cell is out and out impassable because of underlying terrain, then 285 ** return this immutable fact. 286 */ 287 if (Ground[cellptr->Land_Type()].Cost[Class->Speed] == 0) { 288 return(MOVE_NO); 289 } 290 291 /* 292 ** If some allied object has reserved the cell, then consider the cell 293 ** as blocked by a moving object. 294 */ 295 if (cellptr->Flag.Composite) { 296 297 if (cellptr->Flag.Occupy.Building) { 298 return(MOVE_NO); 299 } 300 301 TechnoClass * techno = cellptr->Cell_Techno(); 302 if (techno != NULL && techno->Is_Cloaked(this)) { 303 return(MOVE_CLOAK); 304 } 305 306 /* 307 ** If reserved by a vehicle, then consider this blocked terrain. 308 */ 309 if (cellptr->Flag.Occupy.Vehicle) { 310 retval = MOVE_MOVING_BLOCK; 311 } 312 } 313 314 /* 315 ** Return with the most severe reason why this cell would be impassable. 316 */ 317 return(retval); 318 } 319 320 321 /*********************************************************************************************** 322 * VesselClass::Shape_Number -- Calculates the shape number for the ship body. * 323 * * 324 * This routine will return with the shape number to use for the ship's body. * 325 * * 326 * INPUT: none * 327 * * 328 * OUTPUT: Returns with the shape number to use for the ship's body when drawing. * 329 * * 330 * WARNINGS: none * 331 * * 332 * HISTORY: * 333 * 07/31/1996 JLB : Created. * 334 *=============================================================================================*/ 335 int VesselClass::Shape_Number(void) const 336 { 337 /* 338 ** For eight facing units, adjust the facing number accordingly. 339 */ 340 FacingType facing = Dir_Facing(PrimaryFacing.Current()); 341 342 int shapenum = UnitClass::BodyShape[Dir_To_16(PrimaryFacing)*2]>>1; 343 344 /* 345 ** Special case code for transport. The north/south facing is in frame 346 ** 0. The east/west facing is in frame 3. 347 */ 348 if (*this == VESSEL_TRANSPORT) { 349 shapenum = 0; 350 } 351 #ifdef FIXIT_CARRIER // checked - ajw 9/28/98 352 if (*this == VESSEL_CARRIER) { 353 shapenum = 0; 354 } 355 #endif 356 /* 357 ** Door opening and closing animation stage check. 358 */ 359 if (!Is_Door_Closed()) { 360 shapenum = Door_Stage(); 361 } 362 363 return(shapenum); 364 } 365 366 367 /*********************************************************************************************** 368 * VesselClass::Draw_It -- Draws the vessel. * 369 * * 370 * Draws the vessel on the tactical display. This routine is called by the map rendering * 371 * process to display the vessel. * 372 * * 373 * INPUT: x,y -- The pixel coordinate to draw this vessel at. * 374 * * 375 * window-- The window to base clipping and coordinates upon when drawing. * 376 * * 377 * OUTPUT: none * 378 * * 379 * WARNINGS: none * 380 * * 381 * HISTORY: * 382 * 03/14/1996 JLB : Created. * 383 *=============================================================================================*/ 384 void VesselClass::Draw_It(int x, int y, WindowNumberType window) const 385 { 386 assert(Vessels.ID(this) == ID); 387 assert(IsActive); 388 389 /* 390 ** Verify the legality of the unit class. 391 */ 392 void const * shapefile = Get_Image_Data(); 393 if (shapefile == NULL) return; 394 395 /* 396 ** Need to know the shape name for the overlay now. ST - 8/19/2019 1:37PM 397 */ 398 const char *turret_shape_name = NULL; 399 400 /* 401 ** If drawing of this unit is not explicitly prohibited, then proceed 402 ** with the render process. 403 */ 404 const bool is_hidden = (Visual_Character() == VISUAL_HIDDEN) && (window != WINDOW_VIRTUAL); 405 if (!is_hidden) { 406 int facing = Dir_To_32(PrimaryFacing); 407 int tfacing = Dir_To_32(SecondaryFacing); 408 DirType rotation = DIR_N; 409 int scale = 0x0100; 410 411 /* 412 ** Actually perform the draw. Overlay an optional shimmer effect as necessary. 413 */ 414 Techno_Draw_Object(shapefile, Shape_Number(), x, y, window, rotation, scale); 415 416 /* 417 ** If there is a turret, then it must be rendered as well. This may include 418 ** firing animation if required. 419 */ 420 if (Class->IsTurretEquipped) { 421 int xx = x; 422 int yy = y; 423 424 /* 425 ** Determine which turret shape to use. This depends on if there 426 ** is any firing animation in progress. 427 */ 428 int shapenum = TechnoClass::BodyShape[tfacing]+32; 429 DirType turdir = DirType(Dir_To_16(PrimaryFacing)*16); 430 431 switch (Class->Type) { 432 case VESSEL_CA: 433 turret_shape_name = "TURR"; 434 shapefile = Class->TurretShapes; 435 shapenum = TechnoClass::BodyShape[Dir_To_32(SecondaryFacing)]; 436 Class->Turret_Adjust(turdir, xx, yy); 437 // Add shape file name forl new shape draw intercept. ST - 8/19/2019 1:42PM 438 //Techno_Draw_Object(shapefile, shapenum, xx, yy, window); 439 Techno_Draw_Object_Virtual(shapefile, shapenum, xx, yy, window, DIR_N, 0x0100, turret_shape_name); 440 xx = x; 441 yy = y; 442 turdir = DirType(Dir_To_16(PrimaryFacing+DIR_S)*16); 443 Class->Turret_Adjust(turdir, xx, yy); 444 break; 445 446 case VESSEL_DD: 447 turret_shape_name = "SSAM"; 448 shapefile = Class->SamShapes; 449 shapenum = TechnoClass::BodyShape[Dir_To_32(SecondaryFacing)]; 450 Class->Turret_Adjust(turdir, xx, yy); 451 break; 452 453 case VESSEL_PT: 454 turret_shape_name = "MGUN"; 455 shapefile = Class->MGunShapes; 456 shapenum = TechnoClass::BodyShape[Dir_To_32(SecondaryFacing)]; 457 Class->Turret_Adjust(turdir, xx, yy); 458 break; 459 460 default: 461 shapenum = TechnoClass::BodyShape[Dir_To_32(SecondaryFacing)]; 462 Class->Turret_Adjust(turdir, xx, yy); 463 break; 464 } 465 466 /* 467 ** Actually perform the draw. Overlay an optional shimmer effect as necessary. 468 */ 469 // Add shape file name forl new shape draw intercept. ST - 8/19/2019 1:42PM 470 if (turret_shape_name) { 471 Techno_Draw_Object_Virtual(shapefile, shapenum, xx, yy, window, DIR_N, 0x0100, turret_shape_name); 472 } else { 473 Techno_Draw_Object(shapefile, shapenum, xx, yy, window); 474 } 475 } 476 } 477 478 DriveClass::Draw_It(x, y, window); 479 480 /* 481 ** Patch so the transport will draw its passengers on top of itself. 482 */ 483 if (!Is_Door_Closed() && IsTethered && In_Radio_Contact() && !Contact_With_Whom()->IsInLimbo) { 484 TechnoClass * contact = Contact_With_Whom(); 485 assert(contact->IsActive); 486 487 int xxx = x + ((int)Lepton_To_Pixel((int)Coord_X(contact->Render_Coord())) - (int)Lepton_To_Pixel((int)Coord_X(Render_Coord()))); 488 int yyy = y + ((int)Lepton_To_Pixel((int)Coord_Y(contact->Render_Coord())) - (int)Lepton_To_Pixel((int)Coord_Y(Render_Coord()))); 489 contact->Draw_It(xxx, yyy, window); 490 contact->IsToDisplay = false; 491 } 492 } 493 494 495 #ifdef CHEAT_KEYS 496 /*********************************************************************************************** 497 * VesselClass::Debug_Dump -- Dumps the vessel status information to the mono monitor. * 498 * * 499 * This routine will display the vessel's status information. The information is dumped to * 500 * the monochrome monitor. * 501 * * 502 * INPUT: mono -- Pointer to the monochrome screen that the information will be displayed * 503 * to. * 504 * * 505 * OUTPUT: none * 506 * * 507 * WARNINGS: none * 508 * * 509 * HISTORY: * 510 * 03/20/1996 JLB : Created. * 511 *=============================================================================================*/ 512 void VesselClass::Debug_Dump(MonoClass * mono) const 513 { 514 assert(Vessels.ID(this) == ID); 515 assert(IsActive); 516 517 mono->Set_Cursor(0, 0); 518 mono->Print(Text_String(TXT_DEBUG_SHIP)); 519 mono->Set_Cursor(47, 5);mono->Printf("%02X:%02X", SecondaryFacing.Current(), SecondaryFacing.Desired()); 520 521 mono->Fill_Attrib(66, 13, 12, 1, IsSelfRepairing ? MonoClass::INVERSE : MonoClass::NORMAL); 522 mono->Fill_Attrib(66, 14, 12, 1, IsToSelfRepair ? MonoClass::INVERSE : MonoClass::NORMAL); 523 DriveClass::Debug_Dump(mono); 524 } 525 #endif 526 527 528 /*********************************************************************************************** 529 * VesselClass::Overlap_List -- Fetches the overlap list for this vessel object. * 530 * * 531 * This routine will fetch the overlap list for this vessel type. It takes into * 532 * consideration any movement the vessel may be doing. * 533 * * 534 * INPUT: none * 535 * * 536 * OUTPUT: Returns with a pointer to the overlap list for this vessel. * 537 * * 538 * WARNINGS: none * 539 * * 540 * HISTORY: * 541 * 03/20/1996 JLB : Created. * 542 *=============================================================================================*/ 543 short const * VesselClass::Overlap_List(bool redraw) const 544 { 545 assert(Vessels.ID(this) == ID); 546 assert(IsActive); 547 548 #ifdef PARTIAL 549 if (Height == 0 && redraw && Class->DimensionData != NULL) { 550 Rect rect; 551 int shapenum = Shape_Number(); 552 if (Class->DimensionData[shapenum].Is_Valid()) { 553 rect = Class->DimensionData[shapenum]; 554 } else { 555 rect = Class->DimensionData[shapenum] = Shape_Dimensions(Get_Image_Data(), shapenum); 556 } 557 558 if (IsSelected) { 559 rect = Union(rect, Rect(-32, 32, 64, 64)); 560 } 561 562 return(Coord_Spillage_List(Coord, rect, true)); 563 } 564 #else 565 redraw = redraw; 566 #endif 567 568 return(Coord_Spillage_List(Coord, 56)+1); 569 } 570 571 572 /*********************************************************************************************** 573 * VesselClass::AI -- Handles the AI processing for vessel objects. * 574 * * 575 * This routine is called once for each vessel object during each main game loop. All * 576 * normal AI processing is handled here. This includes dispatching and maintaining any * 577 * processing that is specific to vessel objects. * 578 * * 579 * INPUT: none * 580 * * 581 * OUTPUT: none * 582 * * 583 * WARNINGS: none * 584 * * 585 * HISTORY: * 586 * 03/20/1996 JLB : Created. * 587 * 07/16/1996 JLB : Prefers anti-sub weapons if firing on subs. * 588 *=============================================================================================*/ 589 void VesselClass::AI(void) 590 { 591 assert(Vessels.ID(this) == ID); 592 assert(IsActive); 593 594 if (Mission == MISSION_NONE && MissionQueue == MISSION_NONE) { 595 Enter_Idle_Mode(); 596 } 597 598 /* 599 ** HACK ALERT: 600 ** If the ship finds itself in a hunt order but it has no weapons, then tell it 601 ** to sail off the map instead. 602 */ 603 if (Mission == MISSION_HUNT && !Is_Weapon_Equipped()) { 604 Assign_Mission(MISSION_RETREAT); 605 } 606 607 /* 608 ** Act on new orders if the unit is at a good position to do so. 609 */ 610 if (!IsDriving && Is_Door_Closed() /*Mission != MISSION_UNLOAD*/) { 611 Commence(); 612 } 613 614 #ifndef CLIPDRAW 615 if (Map.In_View(Coord_Cell(Center_Coord()))) { 616 Mark(MARK_CHANGE); 617 } 618 #endif 619 620 #ifdef FIXIT_CARRIER // checked - ajw 9/28/98 621 // Re-stock the ammo of any on-board helicopters on an aircraft carrier. 622 if (*this == VESSEL_CARRIER && How_Many()) { 623 if (!MoebiusCountDown) { 624 MoebiusCountDown = Rule.ReloadRate * TICKS_PER_MINUTE; 625 ObjectClass *obj = Attached_Object(); 626 while (obj) { 627 long bogus; 628 ((AircraftClass *)obj)->Receive_Message(this,RADIO_RELOAD,bogus); 629 obj = (obj->Next); 630 } 631 } 632 } 633 #endif 634 /* 635 ** Process base class AI routine. If as a result of this, the vessel gets 636 ** destroyed, then detect this fact and bail early. 637 */ 638 DriveClass::AI(); 639 if (!IsActive) { 640 return; 641 } 642 643 /* 644 ** Handle body and turret rotation. 645 */ 646 Rotation_AI(); 647 648 /* 649 ** Handle any combat processing required. 650 */ 651 Combat_AI(); 652 653 /* 654 ** Delete this unit if it finds itself off the edge of the map and it is in 655 ** guard or other static mission mode. 656 */ 657 if (Edge_Of_World_AI()) { 658 return; 659 } 660 661 if (Class->Max_Passengers() > 0) { 662 663 /* 664 ** Double check that there is a passenger that is trying to load or unload. 665 ** If not, then close the door. 666 */ 667 if (!Is_Door_Closed() && Mission != MISSION_UNLOAD && Transmit_Message(RADIO_TRYING_TO_LOAD) != RADIO_ROGER && !(long)DoorShutCountDown) { 668 LST_Close_Door(); 669 } 670 } 671 672 /* 673 ** Don't start a new mission unless the vehicle is in the center of 674 ** a cell (not driving) and the door (if any) is closed. 675 */ 676 if (!IsDriving && Is_Door_Closed()/*&& Mission != MISSION_UNLOAD*/) { 677 Commence(); 678 } 679 680 /* 681 ** Do a step of repair here, if appropriate. 682 */ 683 Repair_AI(); 684 } 685 686 687 /*********************************************************************************************** 688 * VesselClass::Per_Cell_Process -- Performs once-per-cell action. * 689 * * 690 * This routine is called when the vessel travels one cell. It handles any processes that * 691 * must occur on a per-cell basis. * 692 * * 693 * INPUT: why -- Specifies the circumstances under which this routine was called. * 694 * * 695 * OUTPUT: none * 696 * * 697 * WARNINGS: none * 698 * * 699 * HISTORY: * 700 * 03/19/1996 JLB : Created. * 701 *=============================================================================================*/ 702 void VesselClass::Per_Cell_Process(PCPType why) 703 { 704 assert(Vessels.ID(this) == ID); 705 assert(IsActive); 706 707 BStart(BENCH_PCP); 708 709 if (why == PCP_END) { 710 CELL cell = Coord_Cell(Coord); 711 712 /* 713 ** The unit performs looking around at this time. If the 714 ** unit moved further than one square during the last track 715 ** move, don't do an incremental look. Do a full look around 716 ** instead. 717 */ 718 Look(!IsPlanningToLook); 719 IsPlanningToLook = false; 720 721 if (IsToSelfRepair) { 722 for (FacingType face = FACING_N; face < FACING_COUNT; face++) { 723 CELL cell = Coord_Cell(Adjacent_Cell(Center_Coord(), face)); 724 SmartPtr<BuildingClass> whom; 725 whom = Map[cell].Cell_Building(); 726 if (whom != NULL && ((*whom == STRUCT_SHIP_YARD) || (*whom == STRUCT_SUB_PEN)) ) { 727 728 // MBL 04.27.2020: Make only audible to the correct player 729 // if (IsOwnedByPlayer) Speak(VOX_REPAIRING); 730 if (IsOwnedByPlayer) Speak(VOX_REPAIRING, House); 731 732 IsSelfRepairing = true; 733 IsToSelfRepair = false; 734 break; 735 } 736 } 737 } 738 739 /* 740 ** If this is a loaner unit and is is off the edge of the 741 ** map, then it gets eliminated. 742 */ 743 if (Edge_Of_World_AI()) { 744 BEnd(BENCH_PCP); 745 return; 746 } 747 } 748 749 if (IsActive) { 750 DriveClass::Per_Cell_Process(why); 751 } 752 BEnd(BENCH_PCP); 753 } 754 755 756 /*********************************************************************************************** 757 * VesselClass::What_Action -- Determines what action would occur if clicked on object. * 758 * * 759 * Use this function to determine what action would likely occur if the specified object * 760 * were clicked on while this unit was selected as current. This function controls, not * 761 * only the action to perform, but indirectly controls the cursor shape to use as well. * 762 * * 763 * INPUT: object -- The object that to check for against "this" object. * 764 * * 765 * OUTPUT: Returns with the default action to perform. If no clear action can be determined, * 766 * then ACTION_NONE is returned. * 767 * * 768 * WARNINGS: none * 769 * * 770 * HISTORY: * 771 * 04/16/1996 BWG : Created. * 772 *=============================================================================================*/ 773 ActionType VesselClass::What_Action(ObjectClass const * object) const 774 { 775 assert(Vessels.ID(this) == ID); 776 assert(IsActive); 777 778 ActionType action = DriveClass::What_Action(object); 779 780 if (action == ACTION_SELF) { 781 if (Class->Max_Passengers() == 0 || !How_Many() ) { 782 action = ACTION_NONE; 783 } else { 784 // check to see if the transporter can unload. 785 bool found = 0; 786 #ifdef FIXIT_CARRIER // checked - ajw 9/28/98 787 if (*this != VESSEL_CARRIER) 788 #endif 789 for (FacingType face = FACING_N; face < FACING_COUNT && !found; face++) { 790 CELL cellnum = Adjacent_Cell(Coord_Cell(Coord), face); 791 CellClass * cell = &Map[cellnum]; 792 if (Map.In_Radar(cellnum)) { 793 if (Ground[cell->Land_Type()].Cost[SPEED_FOOT] == 0 || cell->Flag.Occupy.Building || cell->Flag.Occupy.Vehicle || cell->Flag.Occupy.Monolith || (cell->Flag.Composite & 0x01F) == 0x01F) { 794 continue; 795 } else { 796 found = true; 797 } 798 } 799 } 800 if (!found) { 801 action = ACTION_NONE; 802 } 803 } 804 } 805 806 /* 807 ** Special return to friendly repair factory action. 808 */ 809 if (House->IsPlayerControl && action == ACTION_SELECT && object->What_Am_I() == RTTI_BUILDING) { 810 BuildingClass * building = (BuildingClass *)object; 811 812 if (building->Class->ToBuild == RTTI_VESSELTYPE && building->House->Is_Ally(this)) { 813 action = ACTION_ENTER; 814 } 815 } 816 #ifdef FIXIT_CSII // checked - ajw 9/28/98 817 if (action == ACTION_ATTACK && object->What_Am_I() == RTTI_VESSEL && 818 (*this == VESSEL_MISSILESUB || *this == VESSEL_CA) ) { 819 action = ACTION_NOMOVE; 820 } 821 #endif 822 /* 823 ** If it doesn't know what to do with the object, then just 824 ** say it can't move there. 825 */ 826 if (action == ACTION_NONE) action = ACTION_NOMOVE; 827 828 return(action); 829 } 830 831 832 /*********************************************************************************************** 833 * VesselClass::Active_Click_With -- Intercepts the active click to see if deployment is possib* 834 * * 835 * This routine intercepts the active click operation. It check to see if this is a self * 836 * deployment request (MCV's have this ability). If it is, then the object is initiated * 837 * to self deploy. In the other cases, it passes the operation down to the lower * 838 * classes for processing. * 839 * * 840 * INPUT: action -- The action requested of the unit. * 841 * * 842 * object -- The object that the mouse pointer is over. * 843 * * 844 * OUTPUT: none * 845 * * 846 * WARNINGS: none * 847 * * 848 * HISTORY: * 849 * 04/16/1996 BWG : Created. * 850 *=============================================================================================*/ 851 void VesselClass::Active_Click_With(ActionType action, ObjectClass * object) 852 { 853 assert(Vessels.ID(this) == ID); 854 assert(IsActive); 855 856 // if (action != What_Action(object)) { 857 action = What_Action(object); 858 switch (action) { 859 case ACTION_ENTER: 860 action = ACTION_MOVE; 861 // BRR 10/18/96 IsToSelfRepair = true; 862 break; 863 864 default: 865 // action = ACTION_NONE; 866 break; 867 } 868 // } 869 // if (action == ACTION_ENTER) { 870 // BRR 10/18/96 IsToSelfRepair = true; 871 // action = ACTION_MOVE; 872 // } else { 873 // if (action != ACTION_NONE) { 874 // BRR 10/18/96 IsSelfRepairing = IsToSelfRepair = false; 875 // } 876 // } 877 878 DriveClass::Active_Click_With(action, object); 879 } 880 881 882 /*********************************************************************************************** 883 * VesselClass::Active_Click_With -- Performs specified action on specified cell. * 884 * * 885 * This routine is called when the mouse has been clicked over a cell and this unit must * 886 * now respond. Notice that this is merely a placeholder function that exists because there * 887 * is another function of the same name that needs to be overloaded. C++ has scoping * 888 * restrictions when there are two identically named functions that are overridden in * 889 * different classes -- it handles it badly, hence the existence of this routine. * 890 * * 891 * INPUT: action -- The action to perform on the cell specified. * 892 * * 893 * cell -- The cell that the action is to be performed on. * 894 * * 895 * OUTPUT: none * 896 * * 897 * WARNINGS: none * 898 * * 899 * HISTORY: * 900 * 04/16/1996 BWG : Created. * 901 *=============================================================================================*/ 902 void VesselClass::Active_Click_With(ActionType action, CELL cell) 903 { 904 assert(Vessels.ID(this) == ID); 905 assert(IsActive); 906 907 // BRR 10/18/96 IsToSelfRepair = false; 908 // if (action != ACTION_NONE) { 909 // BRR 10/18/96 IsSelfRepairing = false; 910 // } 911 DriveClass::Active_Click_With(action, cell); 912 } 913 914 915 /*********************************************************************************************** 916 * VesselClass::Take_Damage -- Assign damage to the vessel. * 917 * * 918 * This routine is called to apply damage to this vessel. The amount and type of damage * 919 * to apply is passed as parameters. This routine could end up destroying the vessel. * 920 * * 921 * INPUT: damage -- Reference to the amount of damage to apply to this vessel. The damage * 922 * value will be adjusted so that the actual damage applied will be * 923 * stored into this variable for possible subsequent examination. * 924 * * 925 * distance -- The distance from the center of the damage to the vessel itself. * 926 * * 927 * warhead -- The warhead type of damage to apply. * 928 * * 929 * source -- The perpetrator of this damage. Knowing who was responsible allows * 930 * retaliation logic. * 931 * * 932 * forced -- Is this damage forced upon the vessel by some supernatural means? * 933 * * 934 * OUTPUT: Returns with the result of the damage applied. This enumeration indicates the * 935 * general effect of the damage. Examine this return value to see if the vessel * 936 * has been destroyed. * 937 * * 938 * WARNINGS: The vessel could be destroyed by the call to this routine! * 939 * * 940 * HISTORY: * 941 * 05/13/1996 JLB : Created. * 942 *=============================================================================================*/ 943 ResultType VesselClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced) 944 { 945 assert(Vessels.ID(this) == ID); 946 assert(IsActive); 947 948 ResultType res = RESULT_NONE; 949 950 /* 951 ** In order for a this to be damaged, it must either be a unit 952 ** with a crew or a sandworm. 953 */ 954 res = FootClass::Take_Damage(damage, distance, warhead, source, forced); 955 956 if (res == RESULT_DESTROYED) { 957 Death_Announcement(source); 958 if (Class->Explosion != ANIM_NONE) { 959 AnimType anim = Class->Explosion; 960 961 new AnimClass(anim, Coord); 962 963 /* 964 ** Very strong units that have an explosion will also rock the 965 ** screen when they are destroyed. 966 */ 967 if (Class->MaxStrength > 400) { 968 int shakes = Class->MaxStrength / 150; 969 Shake_The_Screen(shakes, Owner()); 970 if (source && Owner() != source->Owner()) { 971 Shake_The_Screen(shakes, source->Owner()); 972 } 973 } 974 } 975 976 /* 977 ** Possibly have the crew member run away. 978 */ 979 Mark(MARK_UP); 980 while (Is_Something_Attached()) { 981 FootClass * object = Detach_Object(); 982 983 /* 984 ** Only infantry can run from a destroyed vehicle. Even then, it is not a sure 985 ** thing. 986 */ 987 object->Record_The_Kill(source); 988 delete object; 989 } 990 991 /* 992 ** Finally, delete the vehicle. 993 */ 994 delete this; 995 996 } else { 997 998 /* 999 ** When damaged and below half strength, start smoking if 1000 ** it isn't already smoking (and it's not a submarine). 1001 */ 1002 #ifdef FIXIT_CSII // checked - ajw 9/28/98 1003 if (Health_Ratio() <= Rule.ConditionYellow && !IsAnimAttached && (*this != VESSEL_SS && *this != VESSEL_MISSILESUB) ) { 1004 #else 1005 if (Health_Ratio() <= Rule.ConditionYellow && !IsAnimAttached && (*this != VESSEL_SS) ) { 1006 #endif 1007 AnimClass * anim = new AnimClass(ANIM_SMOKE_M, Coord_Add(Coord, XYP_Coord(0, -8))); 1008 if (anim != NULL) anim->Attach_To(this); 1009 } 1010 } 1011 return(res); 1012 } 1013 1014 1015 /*********************************************************************************************** 1016 * VesselClass::Can_Fire -- Determines if this vessel can fire its weapon. * 1017 * * 1018 * This routine is used to determine if this vessel can fire its weapon at the target * 1019 * specified. * 1020 * * 1021 * INPUT: target -- The target candidate to determine if firing upon is valid. * 1022 * * 1023 * which -- Which weapon to use when considering the candidate as a potential * 1024 * target. * 1025 * * 1026 * OUTPUT: Returns with the fire error type. This enum indicates if the vessel and fire. If * 1027 * it can't fire, then the enum indicates why. * 1028 * * 1029 * WARNINGS: none * 1030 * * 1031 * HISTORY: * 1032 * 05/13/1996 JLB : Created. * 1033 *=============================================================================================*/ 1034 FireErrorType VesselClass::Can_Fire(TARGET target, int which) const 1035 { 1036 assert(Vessels.ID(this) == ID); 1037 assert(IsActive); 1038 1039 DirType dir; // The facing to impart upon the projectile. 1040 int diff; 1041 1042 #ifdef FIXIT_CARRIER // checked - ajw 9/28/98 1043 if (*this == VESSEL_CARRIER) { 1044 if(!How_Many() || Arm) { 1045 return(FIRE_REARM); 1046 } else { 1047 return(FIRE_OK); 1048 } 1049 } 1050 #endif 1051 FireErrorType fire = DriveClass::Can_Fire(target, which); 1052 if(*this==VESSEL_DD) { 1053 Mono_Set_Cursor(0,0); 1054 } 1055 if (fire == FIRE_OK || fire == FIRE_CLOAKED) { 1056 WeaponTypeClass const * weapon = (which == 0) ? Class->PrimaryWeapon : Class->SecondaryWeapon; 1057 1058 /* 1059 ** Ensure that a torpedo will never be fired upon a non naval target. 1060 ** Unless that non-naval target is a naval building (sub pen/ship yard) 1061 */ 1062 bool isseatarget = Is_Target_Vessel(target); 1063 bool isbridgetarget = false; 1064 if (weapon->Bullet->IsSubSurface) { 1065 isbridgetarget = Is_Target_Cell(target); // enable shooting at bridges 1066 isseatarget |= isbridgetarget; 1067 } 1068 BuildingClass * bldg = ::As_Building(target); 1069 if (bldg != NULL && bldg->Class->Speed == SPEED_FLOAT) { 1070 isseatarget = true; 1071 } 1072 1073 dir = Direction(target); 1074 1075 if (weapon->Bullet->IsSubSurface) { 1076 if (!isseatarget && Is_Target_Object(target)) { 1077 return(FIRE_CANT); 1078 } 1079 1080 /* 1081 ** If it's a torpedo, let's check line-of-sight to make sure that 1082 ** there's only water squares between us and the target. 1083 */ 1084 ObjectClass * obj = As_Object(target); 1085 COORDINATE coord = Center_Coord(); 1086 if (obj != NULL) { 1087 int totaldist = ::Distance(coord, obj->Center_Coord()); 1088 while (totaldist > CELL_LEPTON_W) { 1089 coord = Coord_Move(coord, dir, CELL_LEPTON_W); 1090 if (Map[coord].Land_Type() != LAND_WATER) { 1091 if (!isbridgetarget) { 1092 return(FIRE_RANGE); 1093 } 1094 } 1095 1096 /* 1097 ** Check for friendly boats in the way. 1098 */ 1099 TechnoClass * tech = Map[coord].Cell_Techno(); 1100 if (tech != NULL && tech != this && House->Is_Ally(tech)) { 1101 return(FIRE_RANGE); 1102 } 1103 totaldist -= CELL_LEPTON_W; 1104 } 1105 } 1106 } 1107 1108 /* 1109 ** Depth charges are only good against submarines. 1110 */ 1111 if (weapon->Bullet->IsAntiSub) { 1112 if (!isseatarget) { 1113 return(FIRE_CANT); 1114 } else { 1115 #ifdef FIXIT_CSII // checked - ajw 9/28/98 1116 if (Is_Target_Vessel(target) && (*As_Vessel(target) != VESSEL_SS && *As_Vessel(target) != VESSEL_MISSILESUB) ) { 1117 #else 1118 if (Is_Target_Vessel(target) && *As_Vessel(target) != VESSEL_SS) { 1119 #endif 1120 if (!Is_Target_Vessel(target) || !weapon->Bullet->IsSubSurface) { 1121 return(FIRE_CANT); 1122 } 1123 } 1124 } 1125 } else { 1126 #ifdef FIXIT_CSII // checked - ajw 9/28/98 1127 if (Is_Target_Vessel(target) && (*As_Vessel(target) == VESSEL_SS || *As_Vessel(target) == VESSEL_MISSILESUB)) { 1128 #else 1129 if (Is_Target_Vessel(target) && *As_Vessel(target) == VESSEL_SS) { 1130 #endif 1131 return(FIRE_CANT); 1132 } 1133 } 1134 1135 /* 1136 ** If this unit cannot fire while moving, then bail. 1137 */ 1138 if (!Class->IsTurretEquipped && Target_Legal(NavCom)) { 1139 return(FIRE_MOVING); 1140 } 1141 1142 /* 1143 ** If the turret is rotating and the projectile isn't a homing type, then 1144 ** firing must be delayed until the rotation stops. 1145 */ 1146 if (!IsFiring && IsRotating && weapon->Bullet->ROT == 0) { 1147 return(FIRE_ROTATING); 1148 } 1149 1150 /* 1151 ** Determine if the turret facing isn't too far off of facing the target. 1152 */ 1153 if (Class->IsTurretEquipped) { 1154 diff = SecondaryFacing.Difference(dir); 1155 } else { 1156 diff = PrimaryFacing.Difference(dir); 1157 } 1158 diff = ABS(diff); 1159 1160 if (weapon->Bullet->ROT != 0) { 1161 diff >>= 2; 1162 } 1163 if (diff > 8) { 1164 return(FIRE_FACING); 1165 } 1166 } 1167 return(fire); 1168 } 1169 1170 1171 /*********************************************************************************************** 1172 * VesselClass::Fire_Coord -- Fetches the coordinate the firing originates from. * 1173 * * 1174 * This routine is called to determine the coordinate that a fired projectile will * 1175 * originate from. * 1176 * * 1177 * INPUT: which -- Which weapon is this query directed at? * 1178 * * 1179 * OUTPUT: Returns with the coordinate where a projectile would appear if it were fired. * 1180 * * 1181 * WARNINGS: none * 1182 * * 1183 * HISTORY: * 1184 * 05/13/1996 JLB : Created. * 1185 *=============================================================================================*/ 1186 FireDataType VesselClass::Fire_Data(int which) const 1187 { 1188 assert(Vessels.ID(this) == ID); 1189 assert(IsActive); 1190 1191 COORDINATE coord = Center_Coord(); 1192 1193 if (*this == VESSEL_CA) { 1194 if (IsSecondShot) { 1195 coord = Coord_Move(coord, PrimaryFacing + DIR_S, 0x0100); 1196 } else { 1197 coord = Coord_Move(coord, PrimaryFacing, 0x0100); 1198 } 1199 coord = Coord_Move(coord, DIR_N, 0x0030); 1200 return{coord,0x0040}; 1201 } 1202 1203 if (*this == VESSEL_PT) { 1204 coord = Coord_Move(coord, PrimaryFacing, 0x0080); 1205 coord = Coord_Move(coord, DIR_N, 0x0020); 1206 return{coord,0x0010}; 1207 } 1208 1209 return(DriveClass::Fire_Data(which)); 1210 } 1211 1212 COORDINATE VesselClass::Fire_Coord(int which) const 1213 { 1214 assert(Vessels.ID(this) == ID); 1215 assert(IsActive); 1216 1217 COORDINATE coord = Center_Coord(); 1218 1219 if (*this == VESSEL_CA) { 1220 if (IsSecondShot) { 1221 coord = Coord_Move(coord, PrimaryFacing + DIR_S, 0x0100); 1222 } else { 1223 coord = Coord_Move(coord, PrimaryFacing, 0x0100); 1224 } 1225 coord = Coord_Move(coord, DIR_N, 0x0030); 1226 coord = Coord_Move(coord, Turret_Facing(), 0x0040); 1227 return(coord); 1228 } 1229 1230 if (*this == VESSEL_PT) { 1231 coord = Coord_Move(coord, PrimaryFacing, 0x0080); 1232 coord = Coord_Move(coord, DIR_N, 0x0020); 1233 coord = Coord_Move(coord, Turret_Facing(), 0x0010); 1234 return(coord); 1235 } 1236 1237 return(DriveClass::Fire_Coord(which)); 1238 } 1239 1240 1241 /*********************************************************************************************** 1242 * VesselClass::Init -- Initialize the vessel heap system. * 1243 * * 1244 * This routine is used to clear out the vessel heap. It is called whenever a scenario is * 1245 * being initialized prior to scenario or saved game loading. * 1246 * * 1247 * INPUT: none * 1248 * * 1249 * OUTPUT: none * 1250 * * 1251 * WARNINGS: All vessel objects are invalid after this routine is called. * 1252 * * 1253 * HISTORY: * 1254 * 05/13/1996 JLB : Created. * 1255 *=============================================================================================*/ 1256 void VesselClass::Init(void) 1257 { 1258 Vessels.Free_All(); 1259 } 1260 1261 1262 /*********************************************************************************************** 1263 * VesselClass::Greatest_Threat -- Determines the greatest threat (best target) for the vessel * 1264 * * 1265 * This routine is used by ships to determine what target they should go after. * 1266 * * 1267 * INPUT: threat -- The threat type that this ship should go after (as determined by the * 1268 * team mission or general self defense principles). * 1269 * * 1270 * OUTPUT: Returns with the target that this ship should attack. * 1271 * * 1272 * WARNINGS: none * 1273 * * 1274 * HISTORY: * 1275 * 05/13/1996 JLB : Created. * 1276 *=============================================================================================*/ 1277 TARGET VesselClass::Greatest_Threat(ThreatType threat) const 1278 { 1279 if (*this == VESSEL_SS) { 1280 threat = threat & ThreatType(THREAT_RANGE|THREAT_AREA); 1281 threat = threat | THREAT_BOATS; 1282 1283 //BG: get subs to attack buildings also. 1284 threat = threat | THREAT_BUILDINGS; 1285 threat = threat | THREAT_FACTORIES; 1286 } else { 1287 if ((threat & (THREAT_GROUND|THREAT_POWER|THREAT_FACTORIES|THREAT_TIBERIUM|THREAT_BASE_DEFENSE|THREAT_BOATS)) == 0) { 1288 if (Class->PrimaryWeapon != NULL) { 1289 threat = threat | Class->PrimaryWeapon->Allowed_Threats(); 1290 } 1291 1292 if (Class->SecondaryWeapon != NULL) { 1293 threat = threat | Class->SecondaryWeapon->Allowed_Threats(); 1294 } 1295 1296 // threat = threat | THREAT_GROUND | THREAT_BOATS; 1297 } 1298 1299 // Cruisers can never hit infantry anyway, so take 'em out of the list 1300 // of possible targets. 1301 if (*this == VESSEL_CA) { 1302 threat = (ThreatType) (threat & (~THREAT_INFANTRY)); 1303 } 1304 } 1305 #ifdef FIXIT_CARRIER // checked - ajw 9/28/98 1306 if (*this == VESSEL_CARRIER) { 1307 return(TARGET_NONE); 1308 } 1309 #endif 1310 return(FootClass::Greatest_Threat(threat)); 1311 } 1312 1313 1314 /*********************************************************************************************** 1315 * VesselClass::Enter_Idle_Mode -- Causes the vessel to enter its default idle mode. * 1316 * * 1317 * This routine is called when the vessel is finished with what it is doing, but the next * 1318 * action is not known. This routine will determine what is the appropriate course of * 1319 * action for this vessel and then start it doing that. * 1320 * * 1321 * INPUT: none * 1322 * * 1323 * OUTPUT: none * 1324 * * 1325 * WARNINGS: none * 1326 * * 1327 * HISTORY: * 1328 * 07/09/1996 JLB : Created. * 1329 *=============================================================================================*/ 1330 void VesselClass::Enter_Idle_Mode(bool ) 1331 { 1332 assert(Vessels.ID(this) == ID); 1333 assert(IsActive); 1334 1335 MissionType order = MISSION_GUARD; 1336 1337 /* 1338 ** A movement mission without a NavCom would be pointless to have a radio contact since 1339 ** no radio coordination occurs on a just a simple movement mission. 1340 */ 1341 if (Mission == MISSION_MOVE && !Target_Legal(NavCom)) { 1342 Transmit_Message(RADIO_OVER_OUT); 1343 } 1344 1345 Handle_Navigation_List(); 1346 if (Target_Legal(NavCom)) { 1347 order = MISSION_MOVE; 1348 } else { 1349 1350 if (Class->PrimaryWeapon == NULL) { 1351 if (IsALoaner && Class->Max_Passengers() > 0 && Is_Something_Attached() && !Team) { 1352 order = MISSION_UNLOAD; 1353 } else { 1354 order = MISSION_GUARD; 1355 Assign_Target(TARGET_NONE); 1356 Assign_Destination(TARGET_NONE); 1357 } 1358 1359 } else { 1360 1361 if (Mission == MISSION_GUARD || Mission == MISSION_GUARD_AREA || MissionControl[Mission].IsParalyzed || MissionControl[Mission].IsZombie) { 1362 return; 1363 } 1364 1365 if (House->IsHuman || Team.Is_Valid()) { 1366 order = MISSION_GUARD; 1367 } else { 1368 if (House->IQ < Rule.IQGuardArea) { 1369 order = MISSION_GUARD; 1370 } else { 1371 order = MISSION_GUARD_AREA; 1372 } 1373 } 1374 } 1375 } 1376 Assign_Mission(order); 1377 } 1378 1379 1380 /*********************************************************************************************** 1381 * VesselClass::Receive_Message -- Handles receiving a radio message. * 1382 * * 1383 * This is the handler function for when a vessel receives a radio * 1384 * message. Typical use of this is when a unit unloads from a lst * 1385 * class so that clearing of the transport is successful. * 1386 * * 1387 * INPUT: from -- Pointer to the originator of the message. * 1388 * * 1389 * message -- The radio message received. * 1390 * * 1391 * param -- Reference to an optional parameter the might be needed to return * 1392 * information back to the originator of the message. * 1393 * * 1394 * OUTPUT: Returns with the radio message response. * 1395 * * 1396 * WARNINGS: none * 1397 * * 1398 * HISTORY: * 1399 * 05/31/1996 BWG : Created. * 1400 *=============================================================================================*/ 1401 RadioMessageType VesselClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) 1402 { 1403 assert(Vessels.ID(this) == ID); 1404 assert(IsActive); 1405 1406 switch (message) { 1407 1408 /* 1409 ** Asks if the passenger can load on this transport. 1410 */ 1411 case RADIO_CAN_LOAD: 1412 if (Class->Max_Passengers() == 0 || from == NULL || !House->Is_Ally(from->Owner())) return(RADIO_STATIC); 1413 if (How_Many() < Class->Max_Passengers()) { 1414 #ifdef FIXIT_CARRIER // checked - ajw 9/28/98 1415 if(*this == VESSEL_CARRIER && from->What_Am_I() == RTTI_AIRCRAFT) { 1416 return(RADIO_ROGER); 1417 } 1418 #endif 1419 /* 1420 ** Before saying "Sure, come on board", make sure we're adjacent to 1421 ** the shore. 1422 */ 1423 CELL cell; 1424 Desired_Load_Dir(from, cell); 1425 if(cell) { 1426 return(RADIO_ROGER); 1427 } 1428 } 1429 return(RADIO_NEGATIVE); 1430 1431 /* 1432 ** This message is sent by the passenger when it determines that it has 1433 ** entered the transport. 1434 */ 1435 case RADIO_IM_IN: 1436 #ifdef FIXIT_CARRIER // checked - ajw 9/28/98 1437 if(*this != VESSEL_CARRIER) { 1438 #endif 1439 if (How_Many() == Class->Max_Passengers()) { 1440 LST_Close_Door(); 1441 } else { 1442 DoorShutCountDown = TICKS_PER_SECOND; 1443 } 1444 #ifdef FIXIT_CARRIER // checked - ajw 9/28/98 1445 } 1446 #endif 1447 return(RADIO_ATTACH); 1448 1449 /* 1450 ** Docking maintenance message received. Check to see if new orders should be given 1451 ** to the impatient unit. 1452 */ 1453 case RADIO_DOCKING: 1454 1455 /* 1456 ** If this transport is moving, then always abort the docking request. 1457 */ 1458 if (IsDriving || Target_Legal(NavCom)) { 1459 return(RADIO_NEGATIVE); 1460 } 1461 1462 /* 1463 ** Check for the case of a docking message arriving from a unit that does not 1464 ** have formal radio contact established. This might be a unit that is standing 1465 ** by. If this transport is free to proceed with normal docking operation, then 1466 ** establish formal contact now. If the transport is completely full, then break 1467 ** off contact. In all other cases, just tell the pending unit to stand by. 1468 */ 1469 if (Contact_With_Whom() != from) { 1470 1471 /* 1472 ** Can't ever load up so tell the passenger to bug off. 1473 */ 1474 if (How_Many() >= Class->Max_Passengers()) { 1475 return(RADIO_NEGATIVE); 1476 } 1477 1478 /* 1479 ** Establish contact and let the loading process proceed normally. 1480 */ 1481 if (!In_Radio_Contact()) { 1482 param = TARGET_NONE; 1483 Transmit_Message(RADIO_HELLO, from); 1484 Transmit_Message(RADIO_MOVE_HERE, param); 1485 return(RADIO_ROGER); 1486 } else { 1487 1488 /* 1489 ** This causes the potential passenger to think that all is ok and to 1490 ** hold on for a bit. 1491 */ 1492 return(RADIO_ROGER); 1493 } 1494 } 1495 1496 /* 1497 ** 1498 */ 1499 if (Class->Max_Passengers() > 0 && *this == VESSEL_TRANSPORT && How_Many() < Class->Max_Passengers()) { 1500 DriveClass::Receive_Message(from, message, param); 1501 1502 if (!IsDriving && !IsRotating) { 1503 // if (!IsDriving && !IsRotating && !IsTethered) { 1504 1505 /* 1506 ** If the potential passenger needs someplace to go, then figure out a good 1507 ** spot and tell it to go. 1508 */ 1509 if (Transmit_Message(RADIO_NEED_TO_MOVE, from) == RADIO_ROGER) { 1510 1511 CELL cell; 1512 DirType dir = Desired_Load_Dir(from, cell); 1513 1514 /* 1515 ** If no adjacent free cells are detected, then passenger loading 1516 ** cannot occur. Break radio contact. 1517 */ 1518 if (cell == 0) { 1519 Transmit_Message(RADIO_OVER_OUT, from); 1520 } else { 1521 param = (long)::As_Target(cell); 1522 1523 /* 1524 ** If it is now facing the correct direction, then open the 1525 ** transport doors. Close the doors if the transport is full or needs 1526 ** to rotate. 1527 */ 1528 if (!Is_Door_Open()) { 1529 LST_Open_Door(); 1530 } 1531 1532 /* 1533 ** Tell the potential passenger where it should go. If the passenger is 1534 ** already at the staging location, then tell it to move onto the transport 1535 ** directly. 1536 */ 1537 if (Transmit_Message(RADIO_MOVE_HERE, param, from) == RADIO_YEA_NOW_WHAT) { 1538 if (Is_Door_Open()) { 1539 param = (long)As_Target(); 1540 Transmit_Message(RADIO_TETHER); 1541 if (Transmit_Message(RADIO_MOVE_HERE, param, from) != RADIO_ROGER) { 1542 Transmit_Message(RADIO_OVER_OUT, from); 1543 } else { 1544 Contact_With_Whom()->Unselect(); 1545 } 1546 } 1547 } 1548 } 1549 } 1550 } 1551 return(RADIO_ROGER); 1552 } 1553 #ifdef FIXIT_CARRIER // checked - ajw 9/28/98 1554 if (Class->Max_Passengers() > 0 && *this == VESSEL_CARRIER && How_Many() < Class->Max_Passengers()) { 1555 TechnoClass::Receive_Message(from, message, param); 1556 /* 1557 ** Establish contact with the object if this building isn't already in contact 1558 ** with another. 1559 */ 1560 if (!In_Radio_Contact()) { 1561 Transmit_Message(RADIO_HELLO, from); 1562 } 1563 if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { 1564 param = As_Target(); 1565 if (Transmit_Message(RADIO_MOVE_HERE, param) == RADIO_YEA_NOW_WHAT) { 1566 Transmit_Message(RADIO_TETHER); 1567 } 1568 } 1569 return(RADIO_ROGER); 1570 } 1571 #endif 1572 break; 1573 1574 /* 1575 ** When this message is received, it means that the other object 1576 ** has already turned its radio off. Turn this radio off as well. 1577 */ 1578 case RADIO_OVER_OUT: 1579 if (Mission == MISSION_RETURN) { 1580 Assign_Mission(MISSION_GUARD); 1581 } 1582 DriveClass::Receive_Message(from, message, param); 1583 return(RADIO_ROGER); 1584 1585 } 1586 return(DriveClass::Receive_Message(from, message, param)); 1587 } 1588 1589 1590 /*********************************************************************************************** 1591 * VesselClass::Desired_Load_Dir -- Determines the best cell and facing for loading. * 1592 * * 1593 * This routine examines the unit and adjacent cells in order to find the best facing * 1594 * for the transport and best staging cell for the potential passengers. This location is * 1595 * modified by adjacent cell passability and direction of the potential passenger. * 1596 * * 1597 * INPUT: passenger -- Pointer to the potential passenger. * 1598 * * 1599 * moveto -- Reference to the cell number that specifies where the potential * 1600 * passenger should move to first. * 1601 * * 1602 * OUTPUT: Returns with the direction the transport should face before opening the transport * 1603 * door. * 1604 * * 1605 * WARNINGS: none * 1606 * * 1607 * HISTORY: * 1608 * 06/01/1996 BWG : Created. * 1609 *=============================================================================================*/ 1610 DirType VesselClass::Desired_Load_Dir(ObjectClass * passenger, CELL & moveto) const 1611 { 1612 assert(Vessels.ID(this) == ID); 1613 assert(IsActive); 1614 1615 /* 1616 ** Determine the ideal facing that provides the least resistance. This would be the direction 1617 ** of the potential passenger or the current transport facing if it is going to unload. 1618 */ 1619 DirType faceto; 1620 if (passenger != NULL) { 1621 faceto = Direction(passenger); 1622 } else { 1623 faceto = PrimaryFacing.Current() + DIR_S; 1624 } 1625 1626 /* 1627 ** Sweep through the adjacent cells in order to find the best candidate. 1628 */ 1629 FacingType bestdir = FACING_N; 1630 int bestval = -1; 1631 for (FacingType face = FACING_N; face < FACING_COUNT; face++) { 1632 int value = 0; 1633 CELL cellnum = Adjacent_Cell(Coord_Cell(Coord), face); 1634 1635 /* 1636 ** Base the initial value of the potential cell according to whether the passenger is 1637 ** allowed to enter the cell. If it can't, then give such a negative value to the 1638 ** cell so that it is prevented from ever choosing that cell for load/unload. 1639 */ 1640 if (passenger != NULL) { 1641 value = (passenger->Can_Enter_Cell(cellnum) == MOVE_OK || Coord_Cell(passenger->Coord) == cellnum) ? 128 : -128; 1642 } else { 1643 CellClass * cell = &Map[cellnum]; 1644 if (Ground[cell->Land_Type()].Cost[SPEED_FOOT] == 0 || cell->Flag.Occupy.Building || cell->Flag.Occupy.Vehicle || cell->Flag.Occupy.Monolith || (cell->Flag.Composite & 0x01F) == 0x01F) { 1645 value = -128; 1646 } else { 1647 if (cell->Cell_Techno() && !House->Is_Ally(cell->Cell_Techno())) { 1648 value = -128; 1649 } else { 1650 value = 128; 1651 } 1652 } 1653 } 1654 1655 #if(0) 1656 /* 1657 ** Give more weight to the cells that require the least rotation of the transport or the 1658 ** least roundabout movement for the potential passenger. 1659 */ 1660 value -= (int)ABS((int)(signed char)Facing_Dir(face) - (int)(signed char)faceto); 1661 if (face == FACING_S) { 1662 value -= 100; 1663 } 1664 if (face == FACING_SW || face == FACING_SE) value += 64; 1665 #endif 1666 /* 1667 ** If the value for the potential cell is greater than the last recorded potential 1668 ** value, then record this cell as the best candidate. 1669 */ 1670 if (bestval == -1 || value > bestval) { 1671 bestval = value; 1672 bestdir = face; 1673 // } else { 1674 // ObjectClass * obj = Map[cellnum].Cell_Occupier(); 1675 // if (obj) obj->Scatter(Coord, true); 1676 } 1677 } 1678 1679 /* 1680 ** If a suitable direction was found, then return with the direction value. 1681 */ 1682 moveto = 0; 1683 if (bestval > 0) { 1684 static DirType _desired_to_actual[FACING_COUNT] = {DIR_S, DIR_SW, DIR_NW, DIR_NW, DIR_NE, DIR_NE, DIR_NE, DIR_SE}; 1685 1686 moveto = Adjacent_Cell(Coord_Cell(Coord), bestdir); 1687 return(_desired_to_actual[bestdir]); 1688 } 1689 return(DIR_N); 1690 } 1691 1692 1693 /*********************************************************************************************** 1694 * VesselClass::LST_Open_Door -- Opens a LST door. * 1695 * * 1696 * This routine will initiate opening of the doors on the LST. * 1697 * * 1698 * INPUT: none * 1699 * * 1700 * OUTPUT: none * 1701 * * 1702 * WARNINGS: none * 1703 * * 1704 * HISTORY: * 1705 * 06/01/1996 BWG : Created. * 1706 *=============================================================================================*/ 1707 void VesselClass::LST_Open_Door(void) 1708 { 1709 assert(Vessels.ID(this) == ID); 1710 assert(IsActive); 1711 1712 if (!IsDriving && !IsRotating) { 1713 Open_Door(5, 6); 1714 } 1715 } 1716 1717 1718 /*********************************************************************************************** 1719 * VesselClass::LST_Close_Door -- Closes a LST door. * 1720 * * 1721 * This routine will initiate closing of the LST door. * 1722 * * 1723 * INPUT: none * 1724 * * 1725 * OUTPUT: none * 1726 * * 1727 * WARNINGS: none * 1728 * * 1729 * HISTORY: * 1730 * 06/01/1996 BWG : Created. * 1731 *=============================================================================================*/ 1732 void VesselClass::LST_Close_Door(void) 1733 { 1734 assert(Vessels.ID(this) == ID); 1735 assert(IsActive); 1736 1737 Close_Door(5, 6); 1738 } 1739 1740 1741 /*********************************************************************************************** 1742 * VesselClass::Mission_Unload -- Handles unloading cargo. * 1743 * * 1744 * This is the AI control sequence for when a transport desires to unload its cargo. * 1745 * * 1746 * INPUT: none * 1747 * * 1748 * OUTPUT: Returns with the delay before calling this routine again. * 1749 * * 1750 * WARNINGS: none * 1751 * * 1752 * HISTORY: * 1753 * 06/01/1996 BWG : Created. * 1754 *=============================================================================================*/ 1755 int VesselClass::Mission_Unload(void) 1756 { 1757 assert(Vessels.ID(this) == ID); 1758 assert(IsActive); 1759 1760 enum { 1761 INITIAL_CHECK, 1762 MANEUVERING, 1763 OPENING_DOOR, 1764 UNLOADING, 1765 CLOSING_DOOR 1766 }; 1767 DirType dir; 1768 CELL cell; 1769 1770 switch (Class->Type) { 1771 case VESSEL_TRANSPORT: 1772 switch (Status) { 1773 case INITIAL_CHECK: 1774 dir = Desired_Load_Dir(NULL, cell); 1775 if (How_Many() > 0 && cell != 0) { 1776 Do_Turn(dir); 1777 Status = MANEUVERING; 1778 return(1); 1779 } else { 1780 if (!How_Many()) { // don't break out if still carrying passengers 1781 Assign_Mission(MISSION_GUARD); 1782 } 1783 } 1784 break; 1785 1786 case MANEUVERING: 1787 if (!IsRotating) { 1788 LST_Open_Door(); 1789 if (Is_Door_Opening()) { 1790 Status = OPENING_DOOR; 1791 return(1); 1792 } 1793 } 1794 break; 1795 1796 case OPENING_DOOR: 1797 if (Is_Door_Open()) { 1798 Status = UNLOADING; 1799 return(1); 1800 } else { 1801 if (!Is_Door_Opening()) { 1802 Status = INITIAL_CHECK; 1803 } 1804 } 1805 break; 1806 1807 case UNLOADING: 1808 if (How_Many()) { 1809 1810 /* 1811 ** Don't do anything if still in radio contact. 1812 */ 1813 if (In_Radio_Contact()) return(TICKS_PER_SECOND); 1814 1815 FootClass * passenger = Detach_Object(); 1816 1817 if (passenger != NULL) { 1818 DirType toface = DIR_S + PrimaryFacing; 1819 bool placed = false; 1820 1821 for (FacingType face = FACING_N; face < FACING_COUNT; face++) { 1822 DirType newface = toface + Facing_Dir(face); 1823 CELL newcell = Adjacent_Cell(Coord_Cell(Coord), newface); 1824 1825 if (passenger->Can_Enter_Cell(newcell) == MOVE_OK) { 1826 ScenarioInit++; 1827 passenger->Unlimbo(Coord_Move(Coord, newface, CELL_LEPTON_W/2), newface); 1828 ScenarioInit--; 1829 passenger->Assign_Mission(MISSION_MOVE); 1830 passenger->Assign_Destination(::As_Target(newcell)); 1831 passenger->Commence(); 1832 Transmit_Message(RADIO_HELLO, passenger); 1833 Transmit_Message(RADIO_TETHER, passenger); 1834 if (passenger->What_Am_I() == RTTI_UNIT) { 1835 ((UnitClass *)passenger)->IsToScatter = true; 1836 } 1837 placed = true; 1838 break; 1839 } 1840 } 1841 1842 /* 1843 ** If the attached unit could NOT be deployed, then re-attach 1844 ** it and then bail out of this deploy process. 1845 */ 1846 if (!placed) { 1847 Attach(passenger); 1848 1849 /* 1850 ** Tell everyone around the transport to scatter. 1851 */ 1852 for (FacingType face = FACING_N; face < FACING_COUNT; face++) { 1853 CellClass * cellptr = Map[Coord].Adjacent_Cell(face); 1854 if (cellptr && cellptr->Is_Clear_To_Move(SPEED_TRACK, true, true)) { 1855 cellptr->Incoming(0, true); 1856 } 1857 } 1858 1859 // Status = CLOSING_DOOR; 1860 } 1861 else { 1862 passenger->Look(false); 1863 } 1864 } 1865 } else { 1866 Status = CLOSING_DOOR; 1867 } 1868 break; 1869 1870 /* 1871 ** Close LST door in preparation for normal operation. 1872 */ 1873 case CLOSING_DOOR: 1874 if (Is_Door_Open()) { 1875 LST_Close_Door(); 1876 } 1877 if (Is_Door_Closed()) { 1878 if (IsALoaner) { 1879 Assign_Mission(MISSION_RETREAT); 1880 } else { 1881 Assign_Mission(MISSION_GUARD); 1882 } 1883 } 1884 break; 1885 } 1886 break; 1887 1888 default: 1889 break; 1890 } 1891 return(MissionControl[Mission].Normal_Delay()); 1892 } 1893 1894 1895 /*********************************************************************************************** 1896 * VesselClass::Assign_Destination -- Assign a destination for this vessel. * 1897 * * 1898 * This routine is called when a destination is to be assigned to this vessel. * 1899 * * 1900 * INPUT: target -- The destination to assign to this vessel. * 1901 * * 1902 * OUTPUT: none * 1903 * * 1904 * WARNINGS: none * 1905 * * 1906 * HISTORY: * 1907 * 07/09/1996 JLB : Created. * 1908 *=============================================================================================*/ 1909 void VesselClass::Assign_Destination(TARGET target) 1910 { 1911 assert(IsActive); 1912 1913 /* 1914 ** Abort early if there is anything wrong with the parameters 1915 ** or the unit already is assigned the specified destination. 1916 */ 1917 if (target == NavCom) return; 1918 1919 /* 1920 ** Transport vehicles must tell all passengers that are about to load, that they 1921 ** cannot proceed. This is accomplished with a radio message to this effect. 1922 */ 1923 if (In_Radio_Contact() && Class->Max_Passengers() > 0 && (Contact_With_Whom()->Is_Infantry() || Contact_With_Whom()->What_Am_I() == RTTI_UNIT)) { 1924 long param = TARGET_NONE; 1925 Transmit_Message(RADIO_MOVE_HERE, param); // should stop objects heading toward this transport. 1926 Transmit_Message(RADIO_OVER_OUT); 1927 if (!Is_Door_Closed()) { 1928 LST_Close_Door(); 1929 } 1930 } 1931 1932 if (!Is_Door_Closed()) { 1933 LST_Close_Door(); 1934 } 1935 1936 DriveClass::Assign_Destination(target); 1937 } 1938 1939 1940 /*********************************************************************************************** 1941 * VesselClass::Pip_Count -- Fetches the number of pips to display on vessel. * 1942 * * 1943 * This routine is used to fetch the number of "fullness" pips to display on the vessel. * 1944 * This will be the number of passengers on a transport. * 1945 * * 1946 * INPUT: none * 1947 * * 1948 * OUTPUT: Returns with the number of pips to draw on this unit. * 1949 * * 1950 * WARNINGS: none * 1951 * * 1952 * HISTORY: * 1953 * 06/25/1995 JLB : Created. * 1954 *=============================================================================================*/ 1955 int VesselClass::Pip_Count(void) const 1956 { 1957 if (Techno_Type_Class()->Max_Passengers() > 0) { 1958 int passengers = 0; 1959 ObjectClass const * object = Attached_Object(); 1960 for (int index = 0; index < Class_Of().Max_Pips(); index++) { 1961 if (object != NULL) { 1962 passengers++; 1963 object = object->Next; 1964 } 1965 } 1966 return passengers; 1967 } 1968 1969 return 0; 1970 } 1971 1972 1973 /*********************************************************************************************** 1974 * VesselClass::Mission_Retreat -- Perform the retreat mission. * 1975 * * 1976 * This will cause the vessel to run away from the battlefield. It searches for an escape * 1977 * map edge according to the reinforcement edge specified in the house. * 1978 * * 1979 * INPUT: none * 1980 * * 1981 * OUTPUT: Returns with the number of game frames to delay before this routine is called * 1982 * again. * 1983 * * 1984 * WARNINGS: none * 1985 * * 1986 * HISTORY: * 1987 * 07/09/1996 JLB : Created. * 1988 *=============================================================================================*/ 1989 int VesselClass::Mission_Retreat(void) 1990 { 1991 assert(Vessels.ID(this) == ID); 1992 assert(IsActive); 1993 1994 enum { 1995 PICK_RETREAT_POINT, 1996 TRAVEL 1997 }; 1998 switch (Status) { 1999 case PICK_RETREAT_POINT: 2000 IsALoaner = true; 2001 if (!Target_Legal(NavCom)) { 2002 // CELL cell = Map.Calculated_Cell(House->Control.Edge, (Team.Is_Valid()) ? Team->Class->Origin : -1, -1, Class->Speed); 2003 CELL cell = Map.Calculated_Cell(House->Control.Edge, (Team.Is_Valid()) ? Team->Class->Origin : -1, Coord_Cell(Center_Coord()), Class->Speed); 2004 if (Team.Is_Valid()) { 2005 Team->Remove(this); 2006 } 2007 Assign_Destination(::As_Target(cell)); 2008 } 2009 Status = TRAVEL; 2010 return(1); 2011 2012 case TRAVEL: 2013 if (!Target_Legal(NavCom)) { 2014 Status = PICK_RETREAT_POINT; 2015 } 2016 break; 2017 2018 default: 2019 break; 2020 } 2021 return(MissionControl[Mission].Normal_Delay()); 2022 } 2023 2024 2025 /*********************************************************************************************** 2026 * VesselClass::Is_Allowed_To_Recloak -- Can the vessel recloak now? * 2027 * * 2028 * Asking this question is part of the recloak process. If the answer is no, then * 2029 * recloaking is postponed. This facilitates keeping submarines visible for longer than * 2030 * they otherwise would be. * 2031 * * 2032 * INPUT: none * 2033 * * 2034 * OUTPUT: bool; Can this vessel recloak now? * 2035 * * 2036 * WARNINGS: none * 2037 * * 2038 * HISTORY: * 2039 * 07/09/1996 BWG : Created. * 2040 *=============================================================================================*/ 2041 bool VesselClass::Is_Allowed_To_Recloak(void) const 2042 { 2043 return(PulseCountDown == 0); 2044 } 2045 2046 2047 /*********************************************************************************************** 2048 * VesselClass::Read_INI -- Read the vessel data from the INI database. * 2049 * * 2050 * This will read and create all vessels specified in the INI database. This routine is * 2051 * called when the scenario starts. * 2052 * * 2053 * INPUT: ini -- Reference to the INI database to read the vessel data from. * 2054 * * 2055 * OUTPUT: none * 2056 * * 2057 * WARNINGS: Vessels will be created and placed on the map by this function. * 2058 * * 2059 * HISTORY: * 2060 * 07/09/1996 JLB : Created. * 2061 *=============================================================================================*/ 2062 void VesselClass::Read_INI(CCINIClass & ini) 2063 { 2064 VesselClass * vessel; // Working vessel pointer. 2065 HousesType inhouse; // Vessel house. 2066 VesselType classid; // Vessel class. 2067 char buf[128]; 2068 2069 int len = ini.Entry_Count(INI_Name()); 2070 for (int index = 0; index < len; index++) { 2071 char const * entry = ini.Get_Entry(INI_Name(), index); 2072 2073 ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf)); 2074 inhouse = HouseTypeClass::From_Name(strtok(buf, ",")); 2075 if (inhouse != HOUSE_NONE) { 2076 classid = VesselTypeClass::From_Name(strtok(NULL, ",")); 2077 2078 if (classid != VESSEL_NONE) { 2079 2080 if (HouseClass::As_Pointer(inhouse) != NULL) { 2081 vessel = new VesselClass(classid, inhouse); 2082 if (vessel != NULL) { 2083 2084 /* 2085 ** Read the raw data. 2086 */ 2087 int strength = atoi(strtok(NULL, ",\r\n")); 2088 2089 CELL cell = atoi(strtok(NULL, ",\r\n")); 2090 2091 COORDINATE coord = Cell_Coord(cell); 2092 2093 DirType dir = (DirType)atoi(strtok(NULL, ",\r\n")); 2094 MissionType mission = MissionClass::Mission_From_Name(strtok(NULL, ",\n\r")); 2095 2096 vessel->Trigger = NULL; 2097 TriggerTypeClass * tp = TriggerTypeClass::From_Name(strtok(NULL, ",\r\n")); 2098 if (tp != NULL) { 2099 TriggerClass * tt = Find_Or_Make(tp); 2100 if (tt != NULL) { 2101 tt->AttachCount++; 2102 vessel->Trigger = tt; 2103 } 2104 } 2105 2106 if (vessel->Unlimbo(coord, dir)) { 2107 vessel->Strength = (int)vessel->Class->MaxStrength * fixed(strength, 256); 2108 if (vessel->Strength > vessel->Class->MaxStrength-3) vessel->Strength = vessel->Class->MaxStrength; 2109 // vessel->Strength = Fixed_To_Cardinal(vessel->Class->MaxStrength, strength); 2110 if (Session.Type == GAME_NORMAL || vessel->House->IsHuman) { 2111 vessel->Assign_Mission(mission); 2112 vessel->Commence(); 2113 } else { 2114 vessel->Enter_Idle_Mode(); 2115 } 2116 2117 } else { 2118 2119 /* 2120 ** If the vessel could not be unlimboed, then this is a catastrophic error 2121 ** condition. Delete the vessel. 2122 */ 2123 delete vessel; 2124 } 2125 } 2126 } 2127 } 2128 } 2129 } 2130 } 2131 2132 2133 /*********************************************************************************************** 2134 * VesselClass::Write_INI -- Write all vessel scenario data to the INI database. * 2135 * * 2136 * This routine is used to add the vessel data (needed for scenario start) to the INI * 2137 * database specified. If there was any preexisting vessel data in the database, it will * 2138 * be cleared * 2139 * * 2140 * INPUT: ini -- Reference to the ini database to store the vessel data into. * 2141 * * 2142 * OUTPUT: none * 2143 * * 2144 * WARNINGS: none * 2145 * * 2146 * HISTORY: * 2147 * 07/09/1996 JLB : Created. * 2148 *=============================================================================================*/ 2149 void VesselClass::Write_INI(CCINIClass & ini) 2150 { 2151 /* 2152 ** First, clear out all existing vessel data from the ini file. 2153 */ 2154 ini.Clear(INI_Name()); 2155 2156 /* 2157 ** Write the vessel data out. 2158 */ 2159 for (int index = 0; index < Vessels.Count(); index++) { 2160 VesselClass * vessel = Vessels.Ptr(index); 2161 if (vessel != NULL && !vessel->IsInLimbo && vessel->IsActive) { 2162 char uname[10]; 2163 char buf[128]; 2164 2165 sprintf(uname, "%d", index); 2166 sprintf(buf, "%s,%s,%d,%u,%d,%s,%s", 2167 vessel->House->Class->IniName, 2168 vessel->Class->IniName, 2169 vessel->Health_Ratio()*256, 2170 Coord_Cell(vessel->Coord), 2171 vessel->PrimaryFacing.Current(), 2172 MissionClass::Mission_Name(vessel->Mission), 2173 vessel->Trigger.Is_Valid() ? vessel->Trigger->Class->IniName : "None" 2174 ); 2175 ini.Put_String(INI_Name(), uname, buf); 2176 } 2177 } 2178 } 2179 2180 2181 /*********************************************************************************************** 2182 * VesselClass::Start_Driver -- Starts the vessel by reserving the location it is moving to. * 2183 * * 2184 * This routine is called when the vessel starts moving. It will reserve the destination * 2185 * cell so that it won't be occupied by another vessel as this one is travelling. * 2186 * * 2187 * INPUT: headto -- The coordinate that will be headed to. * 2188 * * 2189 * OUTPUT: bool; Was the destination location successfully marked? * 2190 * * 2191 * WARNINGS: none * 2192 * * 2193 * HISTORY: * 2194 * 07/09/1996 JLB : Created. * 2195 *=============================================================================================*/ 2196 bool VesselClass::Start_Driver(COORDINATE & headto) 2197 { 2198 assert(Vessels.ID(this) == ID); 2199 assert(IsActive); 2200 2201 if (DriveClass::Start_Driver(headto) && IsActive) { //BG IsActive can be cleared by Start_Driver 2202 Mark_Track(headto, MARK_DOWN); 2203 return(true); 2204 } 2205 return(false); 2206 } 2207 2208 2209 /*********************************************************************************************** 2210 * VesselClass::What_Action -- Determines action to perform on specified cell. * 2211 * * 2212 * This routine will determine what action to perform if the mouse were clicked over the * 2213 * cell specified. * 2214 * * 2215 * INPUT: cell -- The cell that the mouse might be clicked on. * 2216 * * 2217 * OUTPUT: Returns with the action type that this unit will perform if the mouse were * 2218 * clicked of the cell specified. * 2219 * * 2220 * WARNINGS: none * 2221 * * 2222 * HISTORY: * 2223 * 07/11/1996 BWG : Created. * 2224 *=============================================================================================*/ 2225 ActionType VesselClass::What_Action(CELL cell) const 2226 { 2227 assert(Vessels.ID(this) == ID); 2228 assert(IsActive); 2229 2230 ActionType action = DriveClass::What_Action(cell); 2231 if (action == ACTION_NOMOVE && Map[cell].Land_Type() == LAND_BEACH) { 2232 return(ACTION_MOVE); 2233 } 2234 2235 if (action == ACTION_NOMOVE && Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->Bullet->IsSubSurface && Map[cell].Is_Bridge_Here()) { 2236 return(ACTION_ATTACK); 2237 } 2238 return(action); 2239 } 2240 2241 2242 /*********************************************************************************************** 2243 * VesselClass::Rotation_AI -- Handles turret and body rotation for this vessel. * 2244 * * 2245 * Any turret or body rotation for this vessel will be handled by this routine. * 2246 * * 2247 * INPUT: none * 2248 * * 2249 * OUTPUT: none * 2250 * * 2251 * WARNINGS: Only call this routine once per vessel per game logic loop. * 2252 * * 2253 * HISTORY: * 2254 * 07/29/1996 JLB : Created. * 2255 *=============================================================================================*/ 2256 void VesselClass::Rotation_AI(void) 2257 { 2258 if (Target_Legal(TarCom) && !IsRotating) { 2259 DirType dir = Direction(TarCom); 2260 2261 if (Class->IsTurretEquipped) { 2262 SecondaryFacing.Set_Desired(dir); 2263 } 2264 } 2265 2266 IsRotating = false; 2267 if (Class->IsTurretEquipped) { 2268 2269 if (SecondaryFacing.Is_Rotating()) { 2270 Mark(MARK_CHANGE_REDRAW); 2271 if (SecondaryFacing.Rotation_Adjust((Class->ROT * House->GroundspeedBias)+1)) { 2272 Mark(MARK_CHANGE_REDRAW); 2273 } 2274 2275 /* 2276 ** If no further rotation is necessary, flag that the rotation 2277 ** has stopped. 2278 */ 2279 IsRotating = SecondaryFacing.Is_Rotating(); 2280 } 2281 } 2282 } 2283 2284 2285 /*********************************************************************************************** 2286 * VesselClass::Combat_AI -- Handles firing and target selection for the vessel. * 2287 * * 2288 * This routine will process firing logic for the vessel. It includes searching for targets * 2289 * and performing any adjustments necessary to bring the target to bear. * 2290 * * 2291 * INPUT: none * 2292 * * 2293 * OUTPUT: none * 2294 * * 2295 * WARNINGS: Only call this routine once per vessel per game logic loop. * 2296 * * 2297 * HISTORY: * 2298 * 07/29/1996 JLB : Created. * 2299 *=============================================================================================*/ 2300 void VesselClass::Combat_AI(void) 2301 { 2302 if (Target_Legal(TarCom) && Is_Weapon_Equipped()) { 2303 2304 /* 2305 ** Determine which weapon can fire. First check for the primary weapon. If that weapon 2306 ** cannot fire, then check any secondary weapon. If neither weapon can fire, then the 2307 ** failure code returned is that from the primary weapon. 2308 */ 2309 int primary = What_Weapon_Should_I_Use(TarCom); 2310 FireErrorType ok = Can_Fire(TarCom, primary); 2311 2312 switch (ok) { 2313 case FIRE_OK: 2314 Fire_At(TarCom, primary); 2315 break; 2316 2317 case FIRE_FACING: 2318 if (Class->IsTurretEquipped) { 2319 SecondaryFacing.Set_Desired(Direction(TarCom)); 2320 } else { 2321 if (!PrimaryFacing.Is_Rotating()) { 2322 PrimaryFacing.Set_Desired(Direction(TarCom)); 2323 } 2324 } 2325 break; 2326 2327 case FIRE_CLOAKED: 2328 Mark(MARK_OVERLAP_UP); 2329 IsFiring = false; 2330 Mark(MARK_OVERLAP_DOWN); 2331 Do_Uncloak(); 2332 break; 2333 } 2334 } 2335 } 2336 2337 2338 /*********************************************************************************************** 2339 * VesselClass::Edge_Of_World_AI -- Determine if vessel is off the edge of the world. * 2340 * * 2341 * In addition to detecting the edge of world case, this routine will delete the vessel * 2342 * if it occurs. * 2343 * * 2344 * INPUT: none * 2345 * * 2346 * OUTPUT: bool; Was the vessel deleted by this routine? * 2347 * * 2348 * WARNINGS: Be sure to examine the return value and if true, abort any further processing * 2349 * for this vessel since it has been deleted. This routine should be called once * 2350 * per vessel per game logic loop. * 2351 * * 2352 * HISTORY: * 2353 * 07/29/1996 JLB : Created. * 2354 *=============================================================================================*/ 2355 bool VesselClass::Edge_Of_World_AI(void) 2356 { 2357 if (!IsDriving && !Map.In_Radar(Coord_Cell(Coord)) && IsLocked) { 2358 if (Team.Is_Valid()) Team->IsLeaveMap = true; 2359 Stun(); 2360 delete this; 2361 return(true); 2362 } 2363 return(false); 2364 } 2365 2366 2367 /*********************************************************************************************** 2368 * VesselClass::Repair_AI -- Process any self-repair required of this vessel. * 2369 * * 2370 * When a vessel repairs, it does so 'by itself' and not under direct control of another * 2371 * object. This self repair logic is processed here. Upon repair completion of money * 2372 * exhuastion, the repair process will terminate. * 2373 * * 2374 * INPUT: none * 2375 * * 2376 * OUTPUT: none * 2377 * * 2378 * WARNINGS: Call this routine only once per vessel per game logic loop. * 2379 * * 2380 * HISTORY: * 2381 * 07/29/1996 BWG : Created. * 2382 *=============================================================================================*/ 2383 void VesselClass::Repair_AI(void) 2384 { 2385 if (IsSelfRepairing) { 2386 if ((Frame % (TICKS_PER_MINUTE * Rule.RepairRate)) == 0) { 2387 Mark(MARK_CHANGE); 2388 int cost = Class->Repair_Cost(); 2389 int step = Class->Repair_Step(); 2390 2391 if (House->Available_Money() >= cost) { 2392 House->Spend_Money(cost); 2393 Strength += step; 2394 if (Strength >= Class->MaxStrength) { 2395 Strength = Class->MaxStrength; 2396 IsSelfRepairing = IsToSelfRepair = false; 2397 2398 // MBL 04.27.2020: Make only audible to the correct player 2399 // if (IsOwnedByPlayer) Speak(VOX_UNIT_REPAIRED); 2400 if (IsOwnedByPlayer) Speak(VOX_UNIT_REPAIRED, House); 2401 } 2402 } 2403 } 2404 } 2405 } 2406 2407 #ifdef FIXIT_CARRIER // checked - ajw 9/28/98 2408 /*********************************************************************************************** 2409 * VesselClass::Fire_At -- Try to fire upon the target specified. * 2410 * * 2411 * This routine is the auto-fire logic for the ship. It will check * 2412 * to see if we're an aircraft carrier, and if so, launch one of our * 2413 * aircraft. If we're not an aircraft carrier, it lets the higher-level * 2414 * Fire_At logic take over. * 2415 * * 2416 * INPUT: target -- The target to fire upon. * 2417 * * 2418 * which -- Which weapon to use when firing. 0=primary, 1=secondary. * 2419 * * 2420 * OUTPUT: bool; Did firing occur? * 2421 * * 2422 * WARNINGS: none * 2423 * * 2424 * HISTORY: * 2425 * 04/26/1994 JLB : Created. * 2426 *=============================================================================================*/ 2427 BulletClass * VesselClass::Fire_At(TARGET target, int which) 2428 { 2429 //PG assert(Units.ID(this) == ID); 2430 assert(IsActive); 2431 2432 if (*this == VESSEL_CARRIER) { 2433 Arm = CarrierLaunchDelay; 2434 FootClass * passenger = Detach_Object(); 2435 if (passenger != NULL) { 2436 ScenarioInit++; 2437 passenger->Unlimbo(Center_Coord()); 2438 ScenarioInit--; 2439 passenger->Assign_Mission(MISSION_ATTACK); 2440 passenger->Assign_Target(TarCom); 2441 passenger->Commence(); 2442 // If we've launched our last aircraft, discontinue attacking. 2443 if (!How_Many()) Assign_Target(TARGET_NONE); 2444 } 2445 } else { 2446 return DriveClass::Fire_At(target, which); 2447 } 2448 return (NULL); 2449 } 2450 2451 #endif