2020-05-27 20:16:20 +01:00
|
|
|
//
|
|
|
|
// Copyright 2020 Electronic Arts Inc.
|
|
|
|
//
|
|
|
|
// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free
|
|
|
|
// software: you can redistribute it and/or modify it under the terms of
|
|
|
|
// the GNU General Public License as published by the Free Software Foundation,
|
|
|
|
// either version 3 of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed
|
|
|
|
// in the hope that it will be useful, but with permitted additional restrictions
|
|
|
|
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
|
|
|
|
// distributed with this program. You should have received a copy of the
|
|
|
|
// GNU General Public License along with permitted additional restrictions
|
|
|
|
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
|
|
|
|
|
|
|
|
/* $Header: /CounterStrike/VESSEL.CPP 1 3/03/97 10:26a Joe_bostic $ */
|
|
|
|
/***********************************************************************************************
|
|
|
|
*** 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 ***
|
|
|
|
***********************************************************************************************
|
|
|
|
* *
|
|
|
|
* Project Name : Command & Conquer *
|
|
|
|
* *
|
|
|
|
* File Name : VESSEL.CPP *
|
|
|
|
* *
|
|
|
|
* Programmer : Joe L. Bostic *
|
|
|
|
* *
|
|
|
|
* Start Date : 03/13/96 *
|
|
|
|
* *
|
|
|
|
* Last Update : July 31, 1996 [JLB] *
|
|
|
|
* *
|
|
|
|
*---------------------------------------------------------------------------------------------*
|
|
|
|
* Functions: *
|
|
|
|
* VesselClass::AI -- Handles the AI processing for vessel objects. *
|
|
|
|
* VesselClass::Assign_Destination -- Assign a destination for this vessel. *
|
|
|
|
* VesselClass::Can_Enter_Cell -- Determines if the vessel can enter the cell specified. *
|
|
|
|
* VesselClass::Can_Fire -- Determines if this vessel can fire its weapon. *
|
|
|
|
* VesselClass::Is_Allowed_To_Recloak -- Can the vessel recloak now? *
|
|
|
|
* VesselClass::Class_Of -- Fetches a reference to the vessel's class data. *
|
|
|
|
* VesselClass::Combat_AI -- Handles firing and target selection for the vessel. *
|
|
|
|
* VesselClass::Debug_Dump -- Dumps the vessel status information to the mono monitor. *
|
|
|
|
* VesselClass::Draw_It -- Draws the vessel. *
|
|
|
|
* VesselClass::Edge_Of_World_AI -- Determine if vessel is off the edge of the world. *
|
|
|
|
* VesselClass::Enter_Idle_Mode -- Causes the vessel to enter its default idle mode. *
|
|
|
|
* VesselClass::Fire_Coord -- Fetches the coordinate the firing originates from. *
|
|
|
|
* VesselClass::Greatest_Threat -- Determines the greatest threat (best target) for the vesse*
|
|
|
|
* VesselClass::Init -- Initialize the vessel heap system. *
|
|
|
|
* VesselClass::Mission_Retreat -- Perform the retreat mission. *
|
|
|
|
* VesselClass::Overlap_List -- Fetches the overlap list for this vessel object. *
|
|
|
|
* VesselClass::Per_Cell_Process -- Performs once-per-cell action. *
|
|
|
|
* VesselClass::Read_INI -- Read the vessel data from the INI database. *
|
|
|
|
* VesselClass::Repair_AI -- Process any self-repair required of this vessel. *
|
|
|
|
* VesselClass::Rotation_AI -- Handles turret and body rotation for this vessel. *
|
|
|
|
* VesselClass::Shape_Number -- Calculates the shape number for the ship body. *
|
|
|
|
* VesselClass::Start_Driver -- Starts the vessel by reserving the location it is moving to. *
|
|
|
|
* VesselClass::Take_Damage -- Assign damage to the vessel. *
|
|
|
|
* VesselClass::VesselClass -- Constructor for vessel class objects. *
|
|
|
|
* VesselClass::What_Action -- Determines action to perform on specified cell. *
|
|
|
|
* VesselClass::Write_INI -- Write all vessel scenario data to the INI database. *
|
|
|
|
* VesselClass::~VesselClass -- Destructor for vessel objects. *
|
|
|
|
* operator delete -- Deletes a vessel's memory block. *
|
|
|
|
* operator new -- Allocates a vessel object memory block. *
|
|
|
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
|
|
|
|
|
|
|
|
#include "function.h"
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::VesselClass -- Constructor for vessel class objects. *
|
|
|
|
* *
|
|
|
|
* This is the normal constructor for vessel class objects. It will set up a vessel that *
|
|
|
|
* is valid excepting that it won't be placed on the map. *
|
|
|
|
* *
|
|
|
|
* INPUT: classid -- The type of vessel this will be. *
|
|
|
|
* *
|
|
|
|
* house -- The owner of this vessel. *
|
|
|
|
* *
|
|
|
|
* OUTPUT: none *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 03/14/1996 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
VesselClass::VesselClass(VesselType classid, HousesType house) :
|
|
|
|
DriveClass(RTTI_VESSEL, Vessels.ID(this), house),
|
|
|
|
Class(VesselTypes.Ptr((int)classid)),
|
|
|
|
IsToSelfRepair(false),
|
|
|
|
IsSelfRepairing(false),
|
|
|
|
DoorShutCountDown(0),
|
|
|
|
PulseCountDown(0),
|
|
|
|
SecondaryFacing(PrimaryFacing)
|
|
|
|
{
|
|
|
|
House->Tracking_Add(this);
|
|
|
|
|
|
|
|
/*
|
|
|
|
** The ammo member is actually part of the techno class, but must be initialized
|
|
|
|
** manually here because this is where we first have access to the class pointer.
|
|
|
|
*/
|
|
|
|
Ammo = Class->MaxAmmo;
|
|
|
|
|
|
|
|
/*
|
|
|
|
** For two shooters, clear out the second shot flag -- it will be set the first time
|
|
|
|
** the object fires. For non two shooters, set the flag since it will never be cleared
|
|
|
|
** and the second shot flag tells the system that normal rearm times apply -- this is
|
|
|
|
** what is desired for non two shooters.
|
|
|
|
*/
|
|
|
|
IsSecondShot = !Class->Is_Two_Shooter();
|
|
|
|
Strength = Class->MaxStrength;
|
|
|
|
|
|
|
|
/*
|
|
|
|
** The techno class cloakabilty flag is set according to the type
|
|
|
|
** class cloakability flag.
|
|
|
|
*/
|
|
|
|
IsCloakable = Class->IsCloakable;
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Keep count of the number of units created.
|
|
|
|
*/
|
|
|
|
// if (Session.Type == GAME_INTERNET) {
|
|
|
|
// House->UnitTotals->Increment_Unit_Total((int)classid);
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::~VesselClass -- Destructor for vessel objects. *
|
|
|
|
* *
|
|
|
|
* The destructor will destroy the vessel and ensure that it is properly removed from the *
|
|
|
|
* game engine. *
|
|
|
|
* *
|
|
|
|
* INPUT: none *
|
|
|
|
* *
|
|
|
|
* OUTPUT: none *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 03/14/1996 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
VesselClass::~VesselClass(void)
|
|
|
|
{
|
|
|
|
if (GameActive && Class.Is_Valid()) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Remove this member from any team it may be associated with. This must occur at the
|
|
|
|
** top most level of the inheritance hierarchy because it may call virtual functions.
|
|
|
|
*/
|
|
|
|
if (Team.Is_Valid()) {
|
|
|
|
Team->Remove(this);
|
|
|
|
Team = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
House->Tracking_Remove(this);
|
|
|
|
|
|
|
|
/*
|
|
|
|
** If there are any cargo members, delete them.
|
|
|
|
*/
|
|
|
|
while (Is_Something_Attached()) {
|
|
|
|
delete Detach_Object();
|
|
|
|
}
|
|
|
|
|
|
|
|
Limbo();
|
|
|
|
}
|
|
|
|
ID = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* operator new -- Allocates a vessel object memory block. *
|
|
|
|
* *
|
|
|
|
* This routine is used to allocate a block of memory from the vessel heap. If there is *
|
|
|
|
* no more space in the heap, then this routine will return NULL. *
|
|
|
|
* *
|
|
|
|
* INPUT: none *
|
|
|
|
* *
|
|
|
|
* OUTPUT: Returns with the pointer to the allocated block of memory. *
|
|
|
|
* *
|
|
|
|
* WARNINGS: This routine could return NULL. *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 03/14/1996 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
void * VesselClass::operator new(size_t)
|
|
|
|
{
|
|
|
|
void * ptr = Vessels.Alloc();
|
|
|
|
if (ptr != NULL) {
|
|
|
|
((VesselClass *)ptr)->Set_Active();
|
|
|
|
}
|
|
|
|
return(ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* operator delete -- Deletes a vessel's memory block. *
|
|
|
|
* *
|
|
|
|
* This overloaded delete operator will return the vessel's memory back to the pool of *
|
|
|
|
* memory used for vessel allocation. *
|
|
|
|
* *
|
|
|
|
* INPUT: ptr -- Pointer to the vessel's memory block. *
|
|
|
|
* *
|
|
|
|
* OUTPUT: none *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 03/14/1996 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
void VesselClass::operator delete(void * ptr)
|
|
|
|
{
|
|
|
|
if (ptr != NULL) {
|
|
|
|
assert(((VesselClass *)ptr)->IsActive);
|
|
|
|
((VesselClass *)ptr)->IsActive = false;
|
|
|
|
}
|
|
|
|
Vessels.Free((VesselClass *)ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::Class_Of -- Fetches a reference to the vessel's class data. *
|
|
|
|
* *
|
|
|
|
* This routine will return with a reference to the static class data for this vessel. *
|
|
|
|
* *
|
|
|
|
* INPUT: none *
|
|
|
|
* *
|
|
|
|
* OUTPUT: Returns with a reference to the class data structure associated with this vessel. *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 03/14/1996 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
ObjectTypeClass const & VesselClass::Class_Of(void) const
|
|
|
|
{
|
|
|
|
assert(IsActive);
|
|
|
|
|
|
|
|
return(*Class);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::Can_Enter_Cell -- Determines if the vessel can enter the cell specified. *
|
|
|
|
* *
|
|
|
|
* This routine is used by find path and other movement logic to determine if this *
|
|
|
|
* vessel can enter the cell specified. *
|
|
|
|
* *
|
|
|
|
* INPUT: cell -- The cell to check this vessel against. *
|
|
|
|
* *
|
|
|
|
* OUTPUT: Returns with the movement restriction associated with movement into this object. *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 03/14/1996 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
MoveType VesselClass::Can_Enter_Cell(CELL cell, FacingType ) const
|
|
|
|
{
|
|
|
|
assert(Vessels.ID(this) == ID);
|
|
|
|
assert(IsActive);
|
|
|
|
|
|
|
|
if ((unsigned)cell >= MAP_CELL_TOTAL) return(MOVE_NO);
|
|
|
|
|
|
|
|
CellClass const * cellptr = &Map[cell];
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Moving off the edge of the map is not allowed unless
|
|
|
|
** this is a loaner vehicle.
|
|
|
|
*/
|
|
|
|
if (!ScenarioInit && !Map.In_Radar(cell) && !Is_Allowed_To_Leave_Map()) {
|
|
|
|
return(MOVE_NO);
|
|
|
|
}
|
|
|
|
|
|
|
|
MoveType retval = MOVE_OK;
|
|
|
|
|
|
|
|
/*
|
|
|
|
** If there is blocking terrain (such as ice), then the vessel
|
|
|
|
** can't move there.
|
|
|
|
*/
|
|
|
|
if (cellptr->Cell_Terrain() != NULL) {
|
|
|
|
return(MOVE_NO);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** If the cell is out and out impassable because of underlying terrain, then
|
|
|
|
** return this immutable fact.
|
|
|
|
*/
|
|
|
|
if (Ground[cellptr->Land_Type()].Cost[Class->Speed] == 0) {
|
|
|
|
return(MOVE_NO);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** If some allied object has reserved the cell, then consider the cell
|
|
|
|
** as blocked by a moving object.
|
|
|
|
*/
|
|
|
|
if (cellptr->Flag.Composite) {
|
|
|
|
|
|
|
|
if (cellptr->Flag.Occupy.Building) {
|
|
|
|
return(MOVE_NO);
|
|
|
|
}
|
|
|
|
|
|
|
|
TechnoClass * techno = cellptr->Cell_Techno();
|
2020-08-06 17:44:54 +01:00
|
|
|
if (techno != NULL && techno->Is_Cloaked(this)) {
|
2020-05-27 20:16:20 +01:00
|
|
|
return(MOVE_CLOAK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** If reserved by a vehicle, then consider this blocked terrain.
|
|
|
|
*/
|
|
|
|
if (cellptr->Flag.Occupy.Vehicle) {
|
|
|
|
retval = MOVE_MOVING_BLOCK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Return with the most severe reason why this cell would be impassable.
|
|
|
|
*/
|
|
|
|
return(retval);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::Shape_Number -- Calculates the shape number for the ship body. *
|
|
|
|
* *
|
|
|
|
* This routine will return with the shape number to use for the ship's body. *
|
|
|
|
* *
|
|
|
|
* INPUT: none *
|
|
|
|
* *
|
|
|
|
* OUTPUT: Returns with the shape number to use for the ship's body when drawing. *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 07/31/1996 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
int VesselClass::Shape_Number(void) const
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
** For eight facing units, adjust the facing number accordingly.
|
|
|
|
*/
|
|
|
|
FacingType facing = Dir_Facing(PrimaryFacing.Current());
|
|
|
|
|
|
|
|
int shapenum = UnitClass::BodyShape[Dir_To_16(PrimaryFacing)*2]>>1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Special case code for transport. The north/south facing is in frame
|
|
|
|
** 0. The east/west facing is in frame 3.
|
|
|
|
*/
|
|
|
|
if (*this == VESSEL_TRANSPORT) {
|
|
|
|
shapenum = 0;
|
|
|
|
}
|
|
|
|
#ifdef FIXIT_CARRIER // checked - ajw 9/28/98
|
|
|
|
if (*this == VESSEL_CARRIER) {
|
|
|
|
shapenum = 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
|
|
** Door opening and closing animation stage check.
|
|
|
|
*/
|
|
|
|
if (!Is_Door_Closed()) {
|
|
|
|
shapenum = Door_Stage();
|
|
|
|
}
|
|
|
|
|
|
|
|
return(shapenum);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::Draw_It -- Draws the vessel. *
|
|
|
|
* *
|
|
|
|
* Draws the vessel on the tactical display. This routine is called by the map rendering *
|
|
|
|
* process to display the vessel. *
|
|
|
|
* *
|
|
|
|
* INPUT: x,y -- The pixel coordinate to draw this vessel at. *
|
|
|
|
* *
|
|
|
|
* window-- The window to base clipping and coordinates upon when drawing. *
|
|
|
|
* *
|
|
|
|
* OUTPUT: none *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 03/14/1996 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
void VesselClass::Draw_It(int x, int y, WindowNumberType window) const
|
|
|
|
{
|
|
|
|
assert(Vessels.ID(this) == ID);
|
|
|
|
assert(IsActive);
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Verify the legality of the unit class.
|
|
|
|
*/
|
|
|
|
void const * shapefile = Get_Image_Data();
|
|
|
|
if (shapefile == NULL) return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Need to know the shape name for the overlay now. ST - 8/19/2019 1:37PM
|
|
|
|
*/
|
|
|
|
const char *turret_shape_name = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
** If drawing of this unit is not explicitly prohibited, then proceed
|
|
|
|
** with the render process.
|
|
|
|
*/
|
|
|
|
const bool is_hidden = (Visual_Character() == VISUAL_HIDDEN) && (window != WINDOW_VIRTUAL);
|
|
|
|
if (!is_hidden) {
|
|
|
|
int facing = Dir_To_32(PrimaryFacing);
|
|
|
|
int tfacing = Dir_To_32(SecondaryFacing);
|
|
|
|
DirType rotation = DIR_N;
|
|
|
|
int scale = 0x0100;
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Actually perform the draw. Overlay an optional shimmer effect as necessary.
|
|
|
|
*/
|
|
|
|
Techno_Draw_Object(shapefile, Shape_Number(), x, y, window, rotation, scale);
|
|
|
|
|
|
|
|
/*
|
|
|
|
** If there is a turret, then it must be rendered as well. This may include
|
|
|
|
** firing animation if required.
|
|
|
|
*/
|
|
|
|
if (Class->IsTurretEquipped) {
|
|
|
|
int xx = x;
|
|
|
|
int yy = y;
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Determine which turret shape to use. This depends on if there
|
|
|
|
** is any firing animation in progress.
|
|
|
|
*/
|
|
|
|
int shapenum = TechnoClass::BodyShape[tfacing]+32;
|
|
|
|
DirType turdir = DirType(Dir_To_16(PrimaryFacing)*16);
|
|
|
|
|
|
|
|
switch (Class->Type) {
|
|
|
|
case VESSEL_CA:
|
|
|
|
turret_shape_name = "TURR";
|
|
|
|
shapefile = Class->TurretShapes;
|
|
|
|
shapenum = TechnoClass::BodyShape[Dir_To_32(SecondaryFacing)];
|
|
|
|
Class->Turret_Adjust(turdir, xx, yy);
|
|
|
|
// Add shape file name forl new shape draw intercept. ST - 8/19/2019 1:42PM
|
|
|
|
//Techno_Draw_Object(shapefile, shapenum, xx, yy, window);
|
|
|
|
Techno_Draw_Object_Virtual(shapefile, shapenum, xx, yy, window, DIR_N, 0x0100, turret_shape_name);
|
|
|
|
xx = x;
|
|
|
|
yy = y;
|
|
|
|
turdir = DirType(Dir_To_16(PrimaryFacing+DIR_S)*16);
|
|
|
|
Class->Turret_Adjust(turdir, xx, yy);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VESSEL_DD:
|
|
|
|
turret_shape_name = "SSAM";
|
|
|
|
shapefile = Class->SamShapes;
|
|
|
|
shapenum = TechnoClass::BodyShape[Dir_To_32(SecondaryFacing)];
|
|
|
|
Class->Turret_Adjust(turdir, xx, yy);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VESSEL_PT:
|
|
|
|
turret_shape_name = "MGUN";
|
|
|
|
shapefile = Class->MGunShapes;
|
|
|
|
shapenum = TechnoClass::BodyShape[Dir_To_32(SecondaryFacing)];
|
|
|
|
Class->Turret_Adjust(turdir, xx, yy);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
shapenum = TechnoClass::BodyShape[Dir_To_32(SecondaryFacing)];
|
|
|
|
Class->Turret_Adjust(turdir, xx, yy);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Actually perform the draw. Overlay an optional shimmer effect as necessary.
|
|
|
|
*/
|
|
|
|
// Add shape file name forl new shape draw intercept. ST - 8/19/2019 1:42PM
|
|
|
|
if (turret_shape_name) {
|
|
|
|
Techno_Draw_Object_Virtual(shapefile, shapenum, xx, yy, window, DIR_N, 0x0100, turret_shape_name);
|
|
|
|
} else {
|
|
|
|
Techno_Draw_Object(shapefile, shapenum, xx, yy, window);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DriveClass::Draw_It(x, y, window);
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Patch so the transport will draw its passengers on top of itself.
|
|
|
|
*/
|
|
|
|
if (!Is_Door_Closed() && IsTethered && In_Radio_Contact() && !Contact_With_Whom()->IsInLimbo) {
|
|
|
|
TechnoClass * contact = Contact_With_Whom();
|
|
|
|
assert(contact->IsActive);
|
|
|
|
|
|
|
|
int xxx = x + ((int)Lepton_To_Pixel((int)Coord_X(contact->Render_Coord())) - (int)Lepton_To_Pixel((int)Coord_X(Render_Coord())));
|
|
|
|
int yyy = y + ((int)Lepton_To_Pixel((int)Coord_Y(contact->Render_Coord())) - (int)Lepton_To_Pixel((int)Coord_Y(Render_Coord())));
|
|
|
|
contact->Draw_It(xxx, yyy, window);
|
|
|
|
contact->IsToDisplay = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef CHEAT_KEYS
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::Debug_Dump -- Dumps the vessel status information to the mono monitor. *
|
|
|
|
* *
|
|
|
|
* This routine will display the vessel's status information. The information is dumped to *
|
|
|
|
* the monochrome monitor. *
|
|
|
|
* *
|
|
|
|
* INPUT: mono -- Pointer to the monochrome screen that the information will be displayed *
|
|
|
|
* to. *
|
|
|
|
* *
|
|
|
|
* OUTPUT: none *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 03/20/1996 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
void VesselClass::Debug_Dump(MonoClass * mono) const
|
|
|
|
{
|
|
|
|
assert(Vessels.ID(this) == ID);
|
|
|
|
assert(IsActive);
|
|
|
|
|
|
|
|
mono->Set_Cursor(0, 0);
|
|
|
|
mono->Print(Text_String(TXT_DEBUG_SHIP));
|
|
|
|
mono->Set_Cursor(47, 5);mono->Printf("%02X:%02X", SecondaryFacing.Current(), SecondaryFacing.Desired());
|
|
|
|
|
|
|
|
mono->Fill_Attrib(66, 13, 12, 1, IsSelfRepairing ? MonoClass::INVERSE : MonoClass::NORMAL);
|
|
|
|
mono->Fill_Attrib(66, 14, 12, 1, IsToSelfRepair ? MonoClass::INVERSE : MonoClass::NORMAL);
|
|
|
|
DriveClass::Debug_Dump(mono);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::Overlap_List -- Fetches the overlap list for this vessel object. *
|
|
|
|
* *
|
|
|
|
* This routine will fetch the overlap list for this vessel type. It takes into *
|
|
|
|
* consideration any movement the vessel may be doing. *
|
|
|
|
* *
|
|
|
|
* INPUT: none *
|
|
|
|
* *
|
|
|
|
* OUTPUT: Returns with a pointer to the overlap list for this vessel. *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 03/20/1996 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
short const * VesselClass::Overlap_List(bool redraw) const
|
|
|
|
{
|
|
|
|
assert(Vessels.ID(this) == ID);
|
|
|
|
assert(IsActive);
|
|
|
|
|
|
|
|
#ifdef PARTIAL
|
|
|
|
if (Height == 0 && redraw && Class->DimensionData != NULL) {
|
|
|
|
Rect rect;
|
|
|
|
int shapenum = Shape_Number();
|
|
|
|
if (Class->DimensionData[shapenum].Is_Valid()) {
|
|
|
|
rect = Class->DimensionData[shapenum];
|
|
|
|
} else {
|
|
|
|
rect = Class->DimensionData[shapenum] = Shape_Dimensions(Get_Image_Data(), shapenum);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IsSelected) {
|
|
|
|
rect = Union(rect, Rect(-32, 32, 64, 64));
|
|
|
|
}
|
|
|
|
|
|
|
|
return(Coord_Spillage_List(Coord, rect, true));
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
redraw = redraw;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return(Coord_Spillage_List(Coord, 56)+1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::AI -- Handles the AI processing for vessel objects. *
|
|
|
|
* *
|
|
|
|
* This routine is called once for each vessel object during each main game loop. All *
|
|
|
|
* normal AI processing is handled here. This includes dispatching and maintaining any *
|
|
|
|
* processing that is specific to vessel objects. *
|
|
|
|
* *
|
|
|
|
* INPUT: none *
|
|
|
|
* *
|
|
|
|
* OUTPUT: none *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 03/20/1996 JLB : Created. *
|
|
|
|
* 07/16/1996 JLB : Prefers anti-sub weapons if firing on subs. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
void VesselClass::AI(void)
|
|
|
|
{
|
|
|
|
assert(Vessels.ID(this) == ID);
|
|
|
|
assert(IsActive);
|
|
|
|
|
|
|
|
if (Mission == MISSION_NONE && MissionQueue == MISSION_NONE) {
|
|
|
|
Enter_Idle_Mode();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** HACK ALERT:
|
|
|
|
** If the ship finds itself in a hunt order but it has no weapons, then tell it
|
|
|
|
** to sail off the map instead.
|
|
|
|
*/
|
|
|
|
if (Mission == MISSION_HUNT && !Is_Weapon_Equipped()) {
|
|
|
|
Assign_Mission(MISSION_RETREAT);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Act on new orders if the unit is at a good position to do so.
|
|
|
|
*/
|
|
|
|
if (!IsDriving && Is_Door_Closed() /*Mission != MISSION_UNLOAD*/) {
|
|
|
|
Commence();
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef CLIPDRAW
|
|
|
|
if (Map.In_View(Coord_Cell(Center_Coord()))) {
|
|
|
|
Mark(MARK_CHANGE);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef FIXIT_CARRIER // checked - ajw 9/28/98
|
|
|
|
// Re-stock the ammo of any on-board helicopters on an aircraft carrier.
|
|
|
|
if (*this == VESSEL_CARRIER && How_Many()) {
|
|
|
|
if (!MoebiusCountDown) {
|
|
|
|
MoebiusCountDown = Rule.ReloadRate * TICKS_PER_MINUTE;
|
|
|
|
ObjectClass *obj = Attached_Object();
|
|
|
|
while (obj) {
|
|
|
|
long bogus;
|
|
|
|
((AircraftClass *)obj)->Receive_Message(this,RADIO_RELOAD,bogus);
|
|
|
|
obj = (obj->Next);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
|
|
** Process base class AI routine. If as a result of this, the vessel gets
|
|
|
|
** destroyed, then detect this fact and bail early.
|
|
|
|
*/
|
|
|
|
DriveClass::AI();
|
|
|
|
if (!IsActive) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Handle body and turret rotation.
|
|
|
|
*/
|
|
|
|
Rotation_AI();
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Handle any combat processing required.
|
|
|
|
*/
|
|
|
|
Combat_AI();
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Delete this unit if it finds itself off the edge of the map and it is in
|
|
|
|
** guard or other static mission mode.
|
|
|
|
*/
|
|
|
|
if (Edge_Of_World_AI()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Class->Max_Passengers() > 0) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Double check that there is a passenger that is trying to load or unload.
|
|
|
|
** If not, then close the door.
|
|
|
|
*/
|
|
|
|
if (!Is_Door_Closed() && Mission != MISSION_UNLOAD && Transmit_Message(RADIO_TRYING_TO_LOAD) != RADIO_ROGER && !(long)DoorShutCountDown) {
|
|
|
|
LST_Close_Door();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Don't start a new mission unless the vehicle is in the center of
|
|
|
|
** a cell (not driving) and the door (if any) is closed.
|
|
|
|
*/
|
|
|
|
if (!IsDriving && Is_Door_Closed()/*&& Mission != MISSION_UNLOAD*/) {
|
|
|
|
Commence();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Do a step of repair here, if appropriate.
|
|
|
|
*/
|
|
|
|
Repair_AI();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::Per_Cell_Process -- Performs once-per-cell action. *
|
|
|
|
* *
|
|
|
|
* This routine is called when the vessel travels one cell. It handles any processes that *
|
|
|
|
* must occur on a per-cell basis. *
|
|
|
|
* *
|
|
|
|
* INPUT: why -- Specifies the circumstances under which this routine was called. *
|
|
|
|
* *
|
|
|
|
* OUTPUT: none *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 03/19/1996 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
void VesselClass::Per_Cell_Process(PCPType why)
|
|
|
|
{
|
|
|
|
assert(Vessels.ID(this) == ID);
|
|
|
|
assert(IsActive);
|
|
|
|
|
|
|
|
BStart(BENCH_PCP);
|
|
|
|
|
|
|
|
if (why == PCP_END) {
|
|
|
|
CELL cell = Coord_Cell(Coord);
|
|
|
|
|
|
|
|
/*
|
|
|
|
** The unit performs looking around at this time. If the
|
|
|
|
** unit moved further than one square during the last track
|
|
|
|
** move, don't do an incremental look. Do a full look around
|
|
|
|
** instead.
|
|
|
|
*/
|
|
|
|
Look(!IsPlanningToLook);
|
|
|
|
IsPlanningToLook = false;
|
|
|
|
|
|
|
|
if (IsToSelfRepair) {
|
|
|
|
for (FacingType face = FACING_N; face < FACING_COUNT; face++) {
|
|
|
|
CELL cell = Coord_Cell(Adjacent_Cell(Center_Coord(), face));
|
|
|
|
SmartPtr<BuildingClass> whom;
|
|
|
|
whom = Map[cell].Cell_Building();
|
|
|
|
if (whom != NULL && ((*whom == STRUCT_SHIP_YARD) || (*whom == STRUCT_SUB_PEN)) ) {
|
|
|
|
|
|
|
|
// MBL 04.27.2020: Make only audible to the correct player
|
|
|
|
// if (IsOwnedByPlayer) Speak(VOX_REPAIRING);
|
|
|
|
if (IsOwnedByPlayer) Speak(VOX_REPAIRING, House);
|
|
|
|
|
|
|
|
IsSelfRepairing = true;
|
|
|
|
IsToSelfRepair = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** If this is a loaner unit and is is off the edge of the
|
|
|
|
** map, then it gets eliminated.
|
|
|
|
*/
|
|
|
|
if (Edge_Of_World_AI()) {
|
|
|
|
BEnd(BENCH_PCP);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IsActive) {
|
|
|
|
DriveClass::Per_Cell_Process(why);
|
|
|
|
}
|
|
|
|
BEnd(BENCH_PCP);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::What_Action -- Determines what action would occur if clicked on object. *
|
|
|
|
* *
|
|
|
|
* Use this function to determine what action would likely occur if the specified object *
|
|
|
|
* were clicked on while this unit was selected as current. This function controls, not *
|
|
|
|
* only the action to perform, but indirectly controls the cursor shape to use as well. *
|
|
|
|
* *
|
|
|
|
* INPUT: object -- The object that to check for against "this" object. *
|
|
|
|
* *
|
|
|
|
* OUTPUT: Returns with the default action to perform. If no clear action can be determined, *
|
|
|
|
* then ACTION_NONE is returned. *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 04/16/1996 BWG : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
ActionType VesselClass::What_Action(ObjectClass const * object) const
|
|
|
|
{
|
|
|
|
assert(Vessels.ID(this) == ID);
|
|
|
|
assert(IsActive);
|
|
|
|
|
|
|
|
ActionType action = DriveClass::What_Action(object);
|
|
|
|
|
|
|
|
if (action == ACTION_SELF) {
|
|
|
|
if (Class->Max_Passengers() == 0 || !How_Many() ) {
|
|
|
|
action = ACTION_NONE;
|
|
|
|
} else {
|
|
|
|
// check to see if the transporter can unload.
|
|
|
|
bool found = 0;
|
|
|
|
#ifdef FIXIT_CARRIER // checked - ajw 9/28/98
|
|
|
|
if (*this != VESSEL_CARRIER)
|
|
|
|
#endif
|
|
|
|
for (FacingType face = FACING_N; face < FACING_COUNT && !found; face++) {
|
|
|
|
CELL cellnum = Adjacent_Cell(Coord_Cell(Coord), face);
|
|
|
|
CellClass * cell = &Map[cellnum];
|
|
|
|
if (Map.In_Radar(cellnum)) {
|
|
|
|
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) {
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!found) {
|
|
|
|
action = ACTION_NONE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Special return to friendly repair factory action.
|
|
|
|
*/
|
|
|
|
if (House->IsPlayerControl && action == ACTION_SELECT && object->What_Am_I() == RTTI_BUILDING) {
|
|
|
|
BuildingClass * building = (BuildingClass *)object;
|
|
|
|
|
|
|
|
if (building->Class->ToBuild == RTTI_VESSELTYPE && building->House->Is_Ally(this)) {
|
|
|
|
action = ACTION_ENTER;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef FIXIT_CSII // checked - ajw 9/28/98
|
|
|
|
if (action == ACTION_ATTACK && object->What_Am_I() == RTTI_VESSEL &&
|
|
|
|
(*this == VESSEL_MISSILESUB || *this == VESSEL_CA) ) {
|
|
|
|
action = ACTION_NOMOVE;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
|
|
** If it doesn't know what to do with the object, then just
|
|
|
|
** say it can't move there.
|
|
|
|
*/
|
|
|
|
if (action == ACTION_NONE) action = ACTION_NOMOVE;
|
|
|
|
|
|
|
|
return(action);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::Active_Click_With -- Intercepts the active click to see if deployment is possib*
|
|
|
|
* *
|
|
|
|
* This routine intercepts the active click operation. It check to see if this is a self *
|
|
|
|
* deployment request (MCV's have this ability). If it is, then the object is initiated *
|
|
|
|
* to self deploy. In the other cases, it passes the operation down to the lower *
|
|
|
|
* classes for processing. *
|
|
|
|
* *
|
|
|
|
* INPUT: action -- The action requested of the unit. *
|
|
|
|
* *
|
|
|
|
* object -- The object that the mouse pointer is over. *
|
|
|
|
* *
|
|
|
|
* OUTPUT: none *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 04/16/1996 BWG : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
void VesselClass::Active_Click_With(ActionType action, ObjectClass * object)
|
|
|
|
{
|
|
|
|
assert(Vessels.ID(this) == ID);
|
|
|
|
assert(IsActive);
|
|
|
|
|
|
|
|
// if (action != What_Action(object)) {
|
|
|
|
action = What_Action(object);
|
|
|
|
switch (action) {
|
|
|
|
case ACTION_ENTER:
|
|
|
|
action = ACTION_MOVE;
|
|
|
|
// BRR 10/18/96 IsToSelfRepair = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
// action = ACTION_NONE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// }
|
|
|
|
// if (action == ACTION_ENTER) {
|
|
|
|
// BRR 10/18/96 IsToSelfRepair = true;
|
|
|
|
// action = ACTION_MOVE;
|
|
|
|
// } else {
|
|
|
|
// if (action != ACTION_NONE) {
|
|
|
|
// BRR 10/18/96 IsSelfRepairing = IsToSelfRepair = false;
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
DriveClass::Active_Click_With(action, object);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::Active_Click_With -- Performs specified action on specified cell. *
|
|
|
|
* *
|
|
|
|
* This routine is called when the mouse has been clicked over a cell and this unit must *
|
|
|
|
* now respond. Notice that this is merely a placeholder function that exists because there *
|
|
|
|
* is another function of the same name that needs to be overloaded. C++ has scoping *
|
|
|
|
* restrictions when there are two identically named functions that are overridden in *
|
|
|
|
* different classes -- it handles it badly, hence the existence of this routine. *
|
|
|
|
* *
|
|
|
|
* INPUT: action -- The action to perform on the cell specified. *
|
|
|
|
* *
|
|
|
|
* cell -- The cell that the action is to be performed on. *
|
|
|
|
* *
|
|
|
|
* OUTPUT: none *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 04/16/1996 BWG : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
void VesselClass::Active_Click_With(ActionType action, CELL cell)
|
|
|
|
{
|
|
|
|
assert(Vessels.ID(this) == ID);
|
|
|
|
assert(IsActive);
|
|
|
|
|
|
|
|
// BRR 10/18/96 IsToSelfRepair = false;
|
|
|
|
// if (action != ACTION_NONE) {
|
|
|
|
// BRR 10/18/96 IsSelfRepairing = false;
|
|
|
|
// }
|
|
|
|
DriveClass::Active_Click_With(action, cell);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::Take_Damage -- Assign damage to the vessel. *
|
|
|
|
* *
|
|
|
|
* This routine is called to apply damage to this vessel. The amount and type of damage *
|
|
|
|
* to apply is passed as parameters. This routine could end up destroying the vessel. *
|
|
|
|
* *
|
|
|
|
* INPUT: damage -- Reference to the amount of damage to apply to this vessel. The damage *
|
|
|
|
* value will be adjusted so that the actual damage applied will be *
|
|
|
|
* stored into this variable for possible subsequent examination. *
|
|
|
|
* *
|
|
|
|
* distance -- The distance from the center of the damage to the vessel itself. *
|
|
|
|
* *
|
|
|
|
* warhead -- The warhead type of damage to apply. *
|
|
|
|
* *
|
|
|
|
* source -- The perpetrator of this damage. Knowing who was responsible allows *
|
|
|
|
* retaliation logic. *
|
|
|
|
* *
|
|
|
|
* forced -- Is this damage forced upon the vessel by some supernatural means? *
|
|
|
|
* *
|
|
|
|
* OUTPUT: Returns with the result of the damage applied. This enumeration indicates the *
|
|
|
|
* general effect of the damage. Examine this return value to see if the vessel *
|
|
|
|
* has been destroyed. *
|
|
|
|
* *
|
|
|
|
* WARNINGS: The vessel could be destroyed by the call to this routine! *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 05/13/1996 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
ResultType VesselClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced)
|
|
|
|
{
|
|
|
|
assert(Vessels.ID(this) == ID);
|
|
|
|
assert(IsActive);
|
|
|
|
|
|
|
|
ResultType res = RESULT_NONE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
** In order for a this to be damaged, it must either be a unit
|
|
|
|
** with a crew or a sandworm.
|
|
|
|
*/
|
|
|
|
res = FootClass::Take_Damage(damage, distance, warhead, source, forced);
|
|
|
|
|
|
|
|
if (res == RESULT_DESTROYED) {
|
|
|
|
Death_Announcement(source);
|
|
|
|
if (Class->Explosion != ANIM_NONE) {
|
|
|
|
AnimType anim = Class->Explosion;
|
|
|
|
|
|
|
|
new AnimClass(anim, Coord);
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Very strong units that have an explosion will also rock the
|
|
|
|
** screen when they are destroyed.
|
|
|
|
*/
|
|
|
|
if (Class->MaxStrength > 400) {
|
|
|
|
int shakes = Class->MaxStrength / 150;
|
|
|
|
Shake_The_Screen(shakes, Owner());
|
|
|
|
if (source && Owner() != source->Owner()) {
|
|
|
|
Shake_The_Screen(shakes, source->Owner());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Possibly have the crew member run away.
|
|
|
|
*/
|
|
|
|
Mark(MARK_UP);
|
|
|
|
while (Is_Something_Attached()) {
|
|
|
|
FootClass * object = Detach_Object();
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Only infantry can run from a destroyed vehicle. Even then, it is not a sure
|
|
|
|
** thing.
|
|
|
|
*/
|
|
|
|
object->Record_The_Kill(source);
|
|
|
|
delete object;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Finally, delete the vehicle.
|
|
|
|
*/
|
|
|
|
delete this;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
/*
|
|
|
|
** When damaged and below half strength, start smoking if
|
|
|
|
** it isn't already smoking (and it's not a submarine).
|
|
|
|
*/
|
|
|
|
#ifdef FIXIT_CSII // checked - ajw 9/28/98
|
|
|
|
if (Health_Ratio() <= Rule.ConditionYellow && !IsAnimAttached && (*this != VESSEL_SS && *this != VESSEL_MISSILESUB) ) {
|
|
|
|
#else
|
|
|
|
if (Health_Ratio() <= Rule.ConditionYellow && !IsAnimAttached && (*this != VESSEL_SS) ) {
|
|
|
|
#endif
|
|
|
|
AnimClass * anim = new AnimClass(ANIM_SMOKE_M, Coord_Add(Coord, XYP_Coord(0, -8)));
|
|
|
|
if (anim != NULL) anim->Attach_To(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return(res);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::Can_Fire -- Determines if this vessel can fire its weapon. *
|
|
|
|
* *
|
|
|
|
* This routine is used to determine if this vessel can fire its weapon at the target *
|
|
|
|
* specified. *
|
|
|
|
* *
|
|
|
|
* INPUT: target -- The target candidate to determine if firing upon is valid. *
|
|
|
|
* *
|
|
|
|
* which -- Which weapon to use when considering the candidate as a potential *
|
|
|
|
* target. *
|
|
|
|
* *
|
|
|
|
* OUTPUT: Returns with the fire error type. This enum indicates if the vessel and fire. If *
|
|
|
|
* it can't fire, then the enum indicates why. *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 05/13/1996 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
FireErrorType VesselClass::Can_Fire(TARGET target, int which) const
|
|
|
|
{
|
|
|
|
assert(Vessels.ID(this) == ID);
|
|
|
|
assert(IsActive);
|
|
|
|
|
|
|
|
DirType dir; // The facing to impart upon the projectile.
|
|
|
|
int diff;
|
|
|
|
|
|
|
|
#ifdef FIXIT_CARRIER // checked - ajw 9/28/98
|
|
|
|
if (*this == VESSEL_CARRIER) {
|
|
|
|
if(!How_Many() || Arm) {
|
|
|
|
return(FIRE_REARM);
|
|
|
|
} else {
|
|
|
|
return(FIRE_OK);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
FireErrorType fire = DriveClass::Can_Fire(target, which);
|
|
|
|
if(*this==VESSEL_DD) {
|
|
|
|
Mono_Set_Cursor(0,0);
|
|
|
|
}
|
|
|
|
if (fire == FIRE_OK || fire == FIRE_CLOAKED) {
|
|
|
|
WeaponTypeClass const * weapon = (which == 0) ? Class->PrimaryWeapon : Class->SecondaryWeapon;
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Ensure that a torpedo will never be fired upon a non naval target.
|
|
|
|
** Unless that non-naval target is a naval building (sub pen/ship yard)
|
|
|
|
*/
|
|
|
|
bool isseatarget = Is_Target_Vessel(target);
|
|
|
|
bool isbridgetarget = false;
|
|
|
|
if (weapon->Bullet->IsSubSurface) {
|
|
|
|
isbridgetarget = Is_Target_Cell(target); // enable shooting at bridges
|
|
|
|
isseatarget |= isbridgetarget;
|
|
|
|
}
|
|
|
|
BuildingClass * bldg = ::As_Building(target);
|
|
|
|
if (bldg != NULL && bldg->Class->Speed == SPEED_FLOAT) {
|
|
|
|
isseatarget = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
dir = Direction(target);
|
|
|
|
|
|
|
|
if (weapon->Bullet->IsSubSurface) {
|
|
|
|
if (!isseatarget && Is_Target_Object(target)) {
|
|
|
|
return(FIRE_CANT);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** If it's a torpedo, let's check line-of-sight to make sure that
|
|
|
|
** there's only water squares between us and the target.
|
|
|
|
*/
|
|
|
|
ObjectClass * obj = As_Object(target);
|
|
|
|
COORDINATE coord = Center_Coord();
|
|
|
|
if (obj != NULL) {
|
|
|
|
int totaldist = ::Distance(coord, obj->Center_Coord());
|
|
|
|
while (totaldist > CELL_LEPTON_W) {
|
|
|
|
coord = Coord_Move(coord, dir, CELL_LEPTON_W);
|
|
|
|
if (Map[coord].Land_Type() != LAND_WATER) {
|
|
|
|
if (!isbridgetarget) {
|
|
|
|
return(FIRE_RANGE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Check for friendly boats in the way.
|
|
|
|
*/
|
|
|
|
TechnoClass * tech = Map[coord].Cell_Techno();
|
|
|
|
if (tech != NULL && tech != this && House->Is_Ally(tech)) {
|
|
|
|
return(FIRE_RANGE);
|
|
|
|
}
|
|
|
|
totaldist -= CELL_LEPTON_W;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Depth charges are only good against submarines.
|
|
|
|
*/
|
|
|
|
if (weapon->Bullet->IsAntiSub) {
|
|
|
|
if (!isseatarget) {
|
|
|
|
return(FIRE_CANT);
|
|
|
|
} else {
|
|
|
|
#ifdef FIXIT_CSII // checked - ajw 9/28/98
|
|
|
|
if (Is_Target_Vessel(target) && (*As_Vessel(target) != VESSEL_SS && *As_Vessel(target) != VESSEL_MISSILESUB) ) {
|
|
|
|
#else
|
|
|
|
if (Is_Target_Vessel(target) && *As_Vessel(target) != VESSEL_SS) {
|
|
|
|
#endif
|
|
|
|
if (!Is_Target_Vessel(target) || !weapon->Bullet->IsSubSurface) {
|
|
|
|
return(FIRE_CANT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
#ifdef FIXIT_CSII // checked - ajw 9/28/98
|
|
|
|
if (Is_Target_Vessel(target) && (*As_Vessel(target) == VESSEL_SS || *As_Vessel(target) == VESSEL_MISSILESUB)) {
|
|
|
|
#else
|
|
|
|
if (Is_Target_Vessel(target) && *As_Vessel(target) == VESSEL_SS) {
|
|
|
|
#endif
|
|
|
|
return(FIRE_CANT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** If this unit cannot fire while moving, then bail.
|
|
|
|
*/
|
|
|
|
if (!Class->IsTurretEquipped && Target_Legal(NavCom)) {
|
|
|
|
return(FIRE_MOVING);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** If the turret is rotating and the projectile isn't a homing type, then
|
|
|
|
** firing must be delayed until the rotation stops.
|
|
|
|
*/
|
|
|
|
if (!IsFiring && IsRotating && weapon->Bullet->ROT == 0) {
|
|
|
|
return(FIRE_ROTATING);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Determine if the turret facing isn't too far off of facing the target.
|
|
|
|
*/
|
|
|
|
if (Class->IsTurretEquipped) {
|
|
|
|
diff = SecondaryFacing.Difference(dir);
|
|
|
|
} else {
|
|
|
|
diff = PrimaryFacing.Difference(dir);
|
|
|
|
}
|
|
|
|
diff = ABS(diff);
|
|
|
|
|
|
|
|
if (weapon->Bullet->ROT != 0) {
|
|
|
|
diff >>= 2;
|
|
|
|
}
|
|
|
|
if (diff > 8) {
|
|
|
|
return(FIRE_FACING);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return(fire);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::Fire_Coord -- Fetches the coordinate the firing originates from. *
|
|
|
|
* *
|
|
|
|
* This routine is called to determine the coordinate that a fired projectile will *
|
|
|
|
* originate from. *
|
|
|
|
* *
|
|
|
|
* INPUT: which -- Which weapon is this query directed at? *
|
|
|
|
* *
|
|
|
|
* OUTPUT: Returns with the coordinate where a projectile would appear if it were fired. *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 05/13/1996 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
2020-08-06 17:44:54 +01:00
|
|
|
FireDataType VesselClass::Fire_Data(int which) const
|
|
|
|
{
|
|
|
|
assert(Vessels.ID(this) == ID);
|
|
|
|
assert(IsActive);
|
|
|
|
|
|
|
|
COORDINATE coord = Center_Coord();
|
|
|
|
|
|
|
|
if (*this == VESSEL_CA) {
|
|
|
|
if (IsSecondShot) {
|
|
|
|
coord = Coord_Move(coord, PrimaryFacing + DIR_S, 0x0100);
|
|
|
|
} else {
|
|
|
|
coord = Coord_Move(coord, PrimaryFacing, 0x0100);
|
|
|
|
}
|
|
|
|
coord = Coord_Move(coord, DIR_N, 0x0030);
|
|
|
|
return{coord,0x0040};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*this == VESSEL_PT) {
|
|
|
|
coord = Coord_Move(coord, PrimaryFacing, 0x0080);
|
|
|
|
coord = Coord_Move(coord, DIR_N, 0x0020);
|
|
|
|
return{coord,0x0010};
|
|
|
|
}
|
|
|
|
|
|
|
|
return(DriveClass::Fire_Data(which));
|
|
|
|
}
|
|
|
|
|
2020-05-27 20:16:20 +01:00
|
|
|
COORDINATE VesselClass::Fire_Coord(int which) const
|
|
|
|
{
|
|
|
|
assert(Vessels.ID(this) == ID);
|
|
|
|
assert(IsActive);
|
|
|
|
|
|
|
|
COORDINATE coord = Center_Coord();
|
|
|
|
|
|
|
|
if (*this == VESSEL_CA) {
|
|
|
|
if (IsSecondShot) {
|
|
|
|
coord = Coord_Move(coord, PrimaryFacing + DIR_S, 0x0100);
|
|
|
|
} else {
|
|
|
|
coord = Coord_Move(coord, PrimaryFacing, 0x0100);
|
|
|
|
}
|
|
|
|
coord = Coord_Move(coord, DIR_N, 0x0030);
|
|
|
|
coord = Coord_Move(coord, Turret_Facing(), 0x0040);
|
|
|
|
return(coord);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*this == VESSEL_PT) {
|
|
|
|
coord = Coord_Move(coord, PrimaryFacing, 0x0080);
|
|
|
|
coord = Coord_Move(coord, DIR_N, 0x0020);
|
|
|
|
coord = Coord_Move(coord, Turret_Facing(), 0x0010);
|
|
|
|
return(coord);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(DriveClass::Fire_Coord(which));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::Init -- Initialize the vessel heap system. *
|
|
|
|
* *
|
|
|
|
* This routine is used to clear out the vessel heap. It is called whenever a scenario is *
|
|
|
|
* being initialized prior to scenario or saved game loading. *
|
|
|
|
* *
|
|
|
|
* INPUT: none *
|
|
|
|
* *
|
|
|
|
* OUTPUT: none *
|
|
|
|
* *
|
|
|
|
* WARNINGS: All vessel objects are invalid after this routine is called. *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 05/13/1996 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
void VesselClass::Init(void)
|
|
|
|
{
|
|
|
|
Vessels.Free_All();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::Greatest_Threat -- Determines the greatest threat (best target) for the vessel *
|
|
|
|
* *
|
|
|
|
* This routine is used by ships to determine what target they should go after. *
|
|
|
|
* *
|
|
|
|
* INPUT: threat -- The threat type that this ship should go after (as determined by the *
|
|
|
|
* team mission or general self defense principles). *
|
|
|
|
* *
|
|
|
|
* OUTPUT: Returns with the target that this ship should attack. *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 05/13/1996 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
TARGET VesselClass::Greatest_Threat(ThreatType threat) const
|
|
|
|
{
|
|
|
|
if (*this == VESSEL_SS) {
|
|
|
|
threat = threat & ThreatType(THREAT_RANGE|THREAT_AREA);
|
|
|
|
threat = threat | THREAT_BOATS;
|
|
|
|
|
|
|
|
//BG: get subs to attack buildings also.
|
|
|
|
threat = threat | THREAT_BUILDINGS;
|
|
|
|
threat = threat | THREAT_FACTORIES;
|
|
|
|
} else {
|
|
|
|
if ((threat & (THREAT_GROUND|THREAT_POWER|THREAT_FACTORIES|THREAT_TIBERIUM|THREAT_BASE_DEFENSE|THREAT_BOATS)) == 0) {
|
|
|
|
if (Class->PrimaryWeapon != NULL) {
|
|
|
|
threat = threat | Class->PrimaryWeapon->Allowed_Threats();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Class->SecondaryWeapon != NULL) {
|
|
|
|
threat = threat | Class->SecondaryWeapon->Allowed_Threats();
|
|
|
|
}
|
|
|
|
|
|
|
|
// threat = threat | THREAT_GROUND | THREAT_BOATS;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cruisers can never hit infantry anyway, so take 'em out of the list
|
|
|
|
// of possible targets.
|
|
|
|
if (*this == VESSEL_CA) {
|
|
|
|
threat = (ThreatType) (threat & (~THREAT_INFANTRY));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef FIXIT_CARRIER // checked - ajw 9/28/98
|
|
|
|
if (*this == VESSEL_CARRIER) {
|
|
|
|
return(TARGET_NONE);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return(FootClass::Greatest_Threat(threat));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::Enter_Idle_Mode -- Causes the vessel to enter its default idle mode. *
|
|
|
|
* *
|
|
|
|
* This routine is called when the vessel is finished with what it is doing, but the next *
|
|
|
|
* action is not known. This routine will determine what is the appropriate course of *
|
|
|
|
* action for this vessel and then start it doing that. *
|
|
|
|
* *
|
|
|
|
* INPUT: none *
|
|
|
|
* *
|
|
|
|
* OUTPUT: none *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 07/09/1996 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
void VesselClass::Enter_Idle_Mode(bool )
|
|
|
|
{
|
|
|
|
assert(Vessels.ID(this) == ID);
|
|
|
|
assert(IsActive);
|
|
|
|
|
|
|
|
MissionType order = MISSION_GUARD;
|
|
|
|
|
|
|
|
/*
|
|
|
|
** A movement mission without a NavCom would be pointless to have a radio contact since
|
|
|
|
** no radio coordination occurs on a just a simple movement mission.
|
|
|
|
*/
|
|
|
|
if (Mission == MISSION_MOVE && !Target_Legal(NavCom)) {
|
|
|
|
Transmit_Message(RADIO_OVER_OUT);
|
|
|
|
}
|
|
|
|
|
|
|
|
Handle_Navigation_List();
|
|
|
|
if (Target_Legal(NavCom)) {
|
|
|
|
order = MISSION_MOVE;
|
|
|
|
} else {
|
|
|
|
|
|
|
|
if (Class->PrimaryWeapon == NULL) {
|
|
|
|
if (IsALoaner && Class->Max_Passengers() > 0 && Is_Something_Attached() && !Team) {
|
|
|
|
order = MISSION_UNLOAD;
|
|
|
|
} else {
|
|
|
|
order = MISSION_GUARD;
|
|
|
|
Assign_Target(TARGET_NONE);
|
|
|
|
Assign_Destination(TARGET_NONE);
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
if (Mission == MISSION_GUARD || Mission == MISSION_GUARD_AREA || MissionControl[Mission].IsParalyzed || MissionControl[Mission].IsZombie) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (House->IsHuman || Team.Is_Valid()) {
|
|
|
|
order = MISSION_GUARD;
|
|
|
|
} else {
|
|
|
|
if (House->IQ < Rule.IQGuardArea) {
|
|
|
|
order = MISSION_GUARD;
|
|
|
|
} else {
|
|
|
|
order = MISSION_GUARD_AREA;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Assign_Mission(order);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::Receive_Message -- Handles receiving a radio message. *
|
|
|
|
* *
|
|
|
|
* This is the handler function for when a vessel receives a radio *
|
|
|
|
* message. Typical use of this is when a unit unloads from a lst *
|
|
|
|
* class so that clearing of the transport is successful. *
|
|
|
|
* *
|
|
|
|
* INPUT: from -- Pointer to the originator of the message. *
|
|
|
|
* *
|
|
|
|
* message -- The radio message received. *
|
|
|
|
* *
|
|
|
|
* param -- Reference to an optional parameter the might be needed to return *
|
|
|
|
* information back to the originator of the message. *
|
|
|
|
* *
|
|
|
|
* OUTPUT: Returns with the radio message response. *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 05/31/1996 BWG : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
RadioMessageType VesselClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param)
|
|
|
|
{
|
|
|
|
assert(Vessels.ID(this) == ID);
|
|
|
|
assert(IsActive);
|
|
|
|
|
|
|
|
switch (message) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Asks if the passenger can load on this transport.
|
|
|
|
*/
|
|
|
|
case RADIO_CAN_LOAD:
|
|
|
|
if (Class->Max_Passengers() == 0 || from == NULL || !House->Is_Ally(from->Owner())) return(RADIO_STATIC);
|
|
|
|
if (How_Many() < Class->Max_Passengers()) {
|
|
|
|
#ifdef FIXIT_CARRIER // checked - ajw 9/28/98
|
|
|
|
if(*this == VESSEL_CARRIER && from->What_Am_I() == RTTI_AIRCRAFT) {
|
|
|
|
return(RADIO_ROGER);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
|
|
** Before saying "Sure, come on board", make sure we're adjacent to
|
|
|
|
** the shore.
|
|
|
|
*/
|
|
|
|
CELL cell;
|
|
|
|
Desired_Load_Dir(from, cell);
|
|
|
|
if(cell) {
|
|
|
|
return(RADIO_ROGER);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return(RADIO_NEGATIVE);
|
|
|
|
|
|
|
|
/*
|
|
|
|
** This message is sent by the passenger when it determines that it has
|
|
|
|
** entered the transport.
|
|
|
|
*/
|
|
|
|
case RADIO_IM_IN:
|
|
|
|
#ifdef FIXIT_CARRIER // checked - ajw 9/28/98
|
|
|
|
if(*this != VESSEL_CARRIER) {
|
|
|
|
#endif
|
|
|
|
if (How_Many() == Class->Max_Passengers()) {
|
|
|
|
LST_Close_Door();
|
|
|
|
} else {
|
|
|
|
DoorShutCountDown = TICKS_PER_SECOND;
|
|
|
|
}
|
|
|
|
#ifdef FIXIT_CARRIER // checked - ajw 9/28/98
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return(RADIO_ATTACH);
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Docking maintenance message received. Check to see if new orders should be given
|
|
|
|
** to the impatient unit.
|
|
|
|
*/
|
|
|
|
case RADIO_DOCKING:
|
|
|
|
|
|
|
|
/*
|
|
|
|
** If this transport is moving, then always abort the docking request.
|
|
|
|
*/
|
|
|
|
if (IsDriving || Target_Legal(NavCom)) {
|
|
|
|
return(RADIO_NEGATIVE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Check for the case of a docking message arriving from a unit that does not
|
|
|
|
** have formal radio contact established. This might be a unit that is standing
|
|
|
|
** by. If this transport is free to proceed with normal docking operation, then
|
|
|
|
** establish formal contact now. If the transport is completely full, then break
|
|
|
|
** off contact. In all other cases, just tell the pending unit to stand by.
|
|
|
|
*/
|
|
|
|
if (Contact_With_Whom() != from) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Can't ever load up so tell the passenger to bug off.
|
|
|
|
*/
|
|
|
|
if (How_Many() >= Class->Max_Passengers()) {
|
|
|
|
return(RADIO_NEGATIVE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Establish contact and let the loading process proceed normally.
|
|
|
|
*/
|
|
|
|
if (!In_Radio_Contact()) {
|
|
|
|
param = TARGET_NONE;
|
|
|
|
Transmit_Message(RADIO_HELLO, from);
|
|
|
|
Transmit_Message(RADIO_MOVE_HERE, param);
|
|
|
|
return(RADIO_ROGER);
|
|
|
|
} else {
|
|
|
|
|
|
|
|
/*
|
|
|
|
** This causes the potential passenger to think that all is ok and to
|
|
|
|
** hold on for a bit.
|
|
|
|
*/
|
|
|
|
return(RADIO_ROGER);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
**
|
|
|
|
*/
|
|
|
|
if (Class->Max_Passengers() > 0 && *this == VESSEL_TRANSPORT && How_Many() < Class->Max_Passengers()) {
|
|
|
|
DriveClass::Receive_Message(from, message, param);
|
|
|
|
|
|
|
|
if (!IsDriving && !IsRotating) {
|
|
|
|
// if (!IsDriving && !IsRotating && !IsTethered) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
** If the potential passenger needs someplace to go, then figure out a good
|
|
|
|
** spot and tell it to go.
|
|
|
|
*/
|
|
|
|
if (Transmit_Message(RADIO_NEED_TO_MOVE, from) == RADIO_ROGER) {
|
|
|
|
|
|
|
|
CELL cell;
|
|
|
|
DirType dir = Desired_Load_Dir(from, cell);
|
|
|
|
|
|
|
|
/*
|
|
|
|
** If no adjacent free cells are detected, then passenger loading
|
|
|
|
** cannot occur. Break radio contact.
|
|
|
|
*/
|
|
|
|
if (cell == 0) {
|
|
|
|
Transmit_Message(RADIO_OVER_OUT, from);
|
|
|
|
} else {
|
|
|
|
param = (long)::As_Target(cell);
|
|
|
|
|
|
|
|
/*
|
|
|
|
** If it is now facing the correct direction, then open the
|
|
|
|
** transport doors. Close the doors if the transport is full or needs
|
|
|
|
** to rotate.
|
|
|
|
*/
|
|
|
|
if (!Is_Door_Open()) {
|
|
|
|
LST_Open_Door();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Tell the potential passenger where it should go. If the passenger is
|
|
|
|
** already at the staging location, then tell it to move onto the transport
|
|
|
|
** directly.
|
|
|
|
*/
|
|
|
|
if (Transmit_Message(RADIO_MOVE_HERE, param, from) == RADIO_YEA_NOW_WHAT) {
|
|
|
|
if (Is_Door_Open()) {
|
|
|
|
param = (long)As_Target();
|
|
|
|
Transmit_Message(RADIO_TETHER);
|
|
|
|
if (Transmit_Message(RADIO_MOVE_HERE, param, from) != RADIO_ROGER) {
|
|
|
|
Transmit_Message(RADIO_OVER_OUT, from);
|
|
|
|
} else {
|
|
|
|
Contact_With_Whom()->Unselect();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return(RADIO_ROGER);
|
|
|
|
}
|
|
|
|
#ifdef FIXIT_CARRIER // checked - ajw 9/28/98
|
|
|
|
if (Class->Max_Passengers() > 0 && *this == VESSEL_CARRIER && How_Many() < Class->Max_Passengers()) {
|
|
|
|
TechnoClass::Receive_Message(from, message, param);
|
|
|
|
/*
|
|
|
|
** Establish contact with the object if this building isn't already in contact
|
|
|
|
** with another.
|
|
|
|
*/
|
|
|
|
if (!In_Radio_Contact()) {
|
|
|
|
Transmit_Message(RADIO_HELLO, from);
|
|
|
|
}
|
|
|
|
if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) {
|
|
|
|
param = As_Target();
|
|
|
|
if (Transmit_Message(RADIO_MOVE_HERE, param) == RADIO_YEA_NOW_WHAT) {
|
|
|
|
Transmit_Message(RADIO_TETHER);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return(RADIO_ROGER);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
** When this message is received, it means that the other object
|
|
|
|
** has already turned its radio off. Turn this radio off as well.
|
|
|
|
*/
|
|
|
|
case RADIO_OVER_OUT:
|
|
|
|
if (Mission == MISSION_RETURN) {
|
|
|
|
Assign_Mission(MISSION_GUARD);
|
|
|
|
}
|
|
|
|
DriveClass::Receive_Message(from, message, param);
|
|
|
|
return(RADIO_ROGER);
|
|
|
|
|
|
|
|
}
|
|
|
|
return(DriveClass::Receive_Message(from, message, param));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::Desired_Load_Dir -- Determines the best cell and facing for loading. *
|
|
|
|
* *
|
|
|
|
* This routine examines the unit and adjacent cells in order to find the best facing *
|
|
|
|
* for the transport and best staging cell for the potential passengers. This location is *
|
|
|
|
* modified by adjacent cell passability and direction of the potential passenger. *
|
|
|
|
* *
|
|
|
|
* INPUT: passenger -- Pointer to the potential passenger. *
|
|
|
|
* *
|
|
|
|
* moveto -- Reference to the cell number that specifies where the potential *
|
|
|
|
* passenger should move to first. *
|
|
|
|
* *
|
|
|
|
* OUTPUT: Returns with the direction the transport should face before opening the transport *
|
|
|
|
* door. *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 06/01/1996 BWG : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
DirType VesselClass::Desired_Load_Dir(ObjectClass * passenger, CELL & moveto) const
|
|
|
|
{
|
|
|
|
assert(Vessels.ID(this) == ID);
|
|
|
|
assert(IsActive);
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Determine the ideal facing that provides the least resistance. This would be the direction
|
|
|
|
** of the potential passenger or the current transport facing if it is going to unload.
|
|
|
|
*/
|
|
|
|
DirType faceto;
|
|
|
|
if (passenger != NULL) {
|
|
|
|
faceto = Direction(passenger);
|
|
|
|
} else {
|
|
|
|
faceto = PrimaryFacing.Current() + DIR_S;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Sweep through the adjacent cells in order to find the best candidate.
|
|
|
|
*/
|
|
|
|
FacingType bestdir = FACING_N;
|
|
|
|
int bestval = -1;
|
|
|
|
for (FacingType face = FACING_N; face < FACING_COUNT; face++) {
|
|
|
|
int value = 0;
|
|
|
|
CELL cellnum = Adjacent_Cell(Coord_Cell(Coord), face);
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Base the initial value of the potential cell according to whether the passenger is
|
|
|
|
** allowed to enter the cell. If it can't, then give such a negative value to the
|
|
|
|
** cell so that it is prevented from ever choosing that cell for load/unload.
|
|
|
|
*/
|
|
|
|
if (passenger != NULL) {
|
|
|
|
value = (passenger->Can_Enter_Cell(cellnum) == MOVE_OK || Coord_Cell(passenger->Coord) == cellnum) ? 128 : -128;
|
|
|
|
} else {
|
|
|
|
CellClass * cell = &Map[cellnum];
|
|
|
|
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) {
|
|
|
|
value = -128;
|
|
|
|
} else {
|
|
|
|
if (cell->Cell_Techno() && !House->Is_Ally(cell->Cell_Techno())) {
|
|
|
|
value = -128;
|
|
|
|
} else {
|
|
|
|
value = 128;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#if(0)
|
|
|
|
/*
|
|
|
|
** Give more weight to the cells that require the least rotation of the transport or the
|
|
|
|
** least roundabout movement for the potential passenger.
|
|
|
|
*/
|
|
|
|
value -= (int)ABS((int)(signed char)Facing_Dir(face) - (int)(signed char)faceto);
|
|
|
|
if (face == FACING_S) {
|
|
|
|
value -= 100;
|
|
|
|
}
|
|
|
|
if (face == FACING_SW || face == FACING_SE) value += 64;
|
|
|
|
#endif
|
|
|
|
/*
|
|
|
|
** If the value for the potential cell is greater than the last recorded potential
|
|
|
|
** value, then record this cell as the best candidate.
|
|
|
|
*/
|
|
|
|
if (bestval == -1 || value > bestval) {
|
|
|
|
bestval = value;
|
|
|
|
bestdir = face;
|
|
|
|
// } else {
|
|
|
|
// ObjectClass * obj = Map[cellnum].Cell_Occupier();
|
|
|
|
// if (obj) obj->Scatter(Coord, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** If a suitable direction was found, then return with the direction value.
|
|
|
|
*/
|
|
|
|
moveto = 0;
|
|
|
|
if (bestval > 0) {
|
|
|
|
static DirType _desired_to_actual[FACING_COUNT] = {DIR_S, DIR_SW, DIR_NW, DIR_NW, DIR_NE, DIR_NE, DIR_NE, DIR_SE};
|
|
|
|
|
|
|
|
moveto = Adjacent_Cell(Coord_Cell(Coord), bestdir);
|
|
|
|
return(_desired_to_actual[bestdir]);
|
|
|
|
}
|
|
|
|
return(DIR_N);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::LST_Open_Door -- Opens a LST door. *
|
|
|
|
* *
|
|
|
|
* This routine will initiate opening of the doors on the LST. *
|
|
|
|
* *
|
|
|
|
* INPUT: none *
|
|
|
|
* *
|
|
|
|
* OUTPUT: none *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 06/01/1996 BWG : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
void VesselClass::LST_Open_Door(void)
|
|
|
|
{
|
|
|
|
assert(Vessels.ID(this) == ID);
|
|
|
|
assert(IsActive);
|
|
|
|
|
|
|
|
if (!IsDriving && !IsRotating) {
|
|
|
|
Open_Door(5, 6);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::LST_Close_Door -- Closes a LST door. *
|
|
|
|
* *
|
|
|
|
* This routine will initiate closing of the LST door. *
|
|
|
|
* *
|
|
|
|
* INPUT: none *
|
|
|
|
* *
|
|
|
|
* OUTPUT: none *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 06/01/1996 BWG : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
void VesselClass::LST_Close_Door(void)
|
|
|
|
{
|
|
|
|
assert(Vessels.ID(this) == ID);
|
|
|
|
assert(IsActive);
|
|
|
|
|
|
|
|
Close_Door(5, 6);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::Mission_Unload -- Handles unloading cargo. *
|
|
|
|
* *
|
|
|
|
* This is the AI control sequence for when a transport desires to unload its cargo. *
|
|
|
|
* *
|
|
|
|
* INPUT: none *
|
|
|
|
* *
|
|
|
|
* OUTPUT: Returns with the delay before calling this routine again. *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 06/01/1996 BWG : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
int VesselClass::Mission_Unload(void)
|
|
|
|
{
|
|
|
|
assert(Vessels.ID(this) == ID);
|
|
|
|
assert(IsActive);
|
|
|
|
|
|
|
|
enum {
|
|
|
|
INITIAL_CHECK,
|
|
|
|
MANEUVERING,
|
|
|
|
OPENING_DOOR,
|
|
|
|
UNLOADING,
|
|
|
|
CLOSING_DOOR
|
|
|
|
};
|
|
|
|
DirType dir;
|
|
|
|
CELL cell;
|
|
|
|
|
|
|
|
switch (Class->Type) {
|
|
|
|
case VESSEL_TRANSPORT:
|
|
|
|
switch (Status) {
|
|
|
|
case INITIAL_CHECK:
|
|
|
|
dir = Desired_Load_Dir(NULL, cell);
|
|
|
|
if (How_Many() > 0 && cell != 0) {
|
|
|
|
Do_Turn(dir);
|
|
|
|
Status = MANEUVERING;
|
|
|
|
return(1);
|
|
|
|
} else {
|
|
|
|
if (!How_Many()) { // don't break out if still carrying passengers
|
|
|
|
Assign_Mission(MISSION_GUARD);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MANEUVERING:
|
|
|
|
if (!IsRotating) {
|
|
|
|
LST_Open_Door();
|
|
|
|
if (Is_Door_Opening()) {
|
|
|
|
Status = OPENING_DOOR;
|
|
|
|
return(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OPENING_DOOR:
|
|
|
|
if (Is_Door_Open()) {
|
|
|
|
Status = UNLOADING;
|
|
|
|
return(1);
|
|
|
|
} else {
|
|
|
|
if (!Is_Door_Opening()) {
|
|
|
|
Status = INITIAL_CHECK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case UNLOADING:
|
|
|
|
if (How_Many()) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Don't do anything if still in radio contact.
|
|
|
|
*/
|
|
|
|
if (In_Radio_Contact()) return(TICKS_PER_SECOND);
|
|
|
|
|
|
|
|
FootClass * passenger = Detach_Object();
|
|
|
|
|
|
|
|
if (passenger != NULL) {
|
|
|
|
DirType toface = DIR_S + PrimaryFacing;
|
|
|
|
bool placed = false;
|
|
|
|
|
|
|
|
for (FacingType face = FACING_N; face < FACING_COUNT; face++) {
|
|
|
|
DirType newface = toface + Facing_Dir(face);
|
|
|
|
CELL newcell = Adjacent_Cell(Coord_Cell(Coord), newface);
|
|
|
|
|
|
|
|
if (passenger->Can_Enter_Cell(newcell) == MOVE_OK) {
|
|
|
|
ScenarioInit++;
|
|
|
|
passenger->Unlimbo(Coord_Move(Coord, newface, CELL_LEPTON_W/2), newface);
|
|
|
|
ScenarioInit--;
|
|
|
|
passenger->Assign_Mission(MISSION_MOVE);
|
|
|
|
passenger->Assign_Destination(::As_Target(newcell));
|
|
|
|
passenger->Commence();
|
|
|
|
Transmit_Message(RADIO_HELLO, passenger);
|
|
|
|
Transmit_Message(RADIO_TETHER, passenger);
|
|
|
|
if (passenger->What_Am_I() == RTTI_UNIT) {
|
|
|
|
((UnitClass *)passenger)->IsToScatter = true;
|
|
|
|
}
|
|
|
|
placed = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** If the attached unit could NOT be deployed, then re-attach
|
|
|
|
** it and then bail out of this deploy process.
|
|
|
|
*/
|
|
|
|
if (!placed) {
|
|
|
|
Attach(passenger);
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Tell everyone around the transport to scatter.
|
|
|
|
*/
|
|
|
|
for (FacingType face = FACING_N; face < FACING_COUNT; face++) {
|
2020-06-22 17:43:21 +01:00
|
|
|
CellClass * cellptr = Map[Coord].Adjacent_Cell(face);
|
|
|
|
if (cellptr && cellptr->Is_Clear_To_Move(SPEED_TRACK, true, true)) {
|
2020-05-27 20:16:20 +01:00
|
|
|
cellptr->Incoming(0, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Status = CLOSING_DOOR;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
passenger->Look(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Status = CLOSING_DOOR;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Close LST door in preparation for normal operation.
|
|
|
|
*/
|
|
|
|
case CLOSING_DOOR:
|
|
|
|
if (Is_Door_Open()) {
|
|
|
|
LST_Close_Door();
|
|
|
|
}
|
|
|
|
if (Is_Door_Closed()) {
|
|
|
|
if (IsALoaner) {
|
|
|
|
Assign_Mission(MISSION_RETREAT);
|
|
|
|
} else {
|
|
|
|
Assign_Mission(MISSION_GUARD);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return(MissionControl[Mission].Normal_Delay());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::Assign_Destination -- Assign a destination for this vessel. *
|
|
|
|
* *
|
|
|
|
* This routine is called when a destination is to be assigned to this vessel. *
|
|
|
|
* *
|
|
|
|
* INPUT: target -- The destination to assign to this vessel. *
|
|
|
|
* *
|
|
|
|
* OUTPUT: none *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 07/09/1996 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
void VesselClass::Assign_Destination(TARGET target)
|
|
|
|
{
|
|
|
|
assert(IsActive);
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Abort early if there is anything wrong with the parameters
|
|
|
|
** or the unit already is assigned the specified destination.
|
|
|
|
*/
|
|
|
|
if (target == NavCom) return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Transport vehicles must tell all passengers that are about to load, that they
|
|
|
|
** cannot proceed. This is accomplished with a radio message to this effect.
|
|
|
|
*/
|
|
|
|
if (In_Radio_Contact() && Class->Max_Passengers() > 0 && (Contact_With_Whom()->Is_Infantry() || Contact_With_Whom()->What_Am_I() == RTTI_UNIT)) {
|
|
|
|
long param = TARGET_NONE;
|
|
|
|
Transmit_Message(RADIO_MOVE_HERE, param); // should stop objects heading toward this transport.
|
|
|
|
Transmit_Message(RADIO_OVER_OUT);
|
|
|
|
if (!Is_Door_Closed()) {
|
|
|
|
LST_Close_Door();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!Is_Door_Closed()) {
|
|
|
|
LST_Close_Door();
|
|
|
|
}
|
|
|
|
|
|
|
|
DriveClass::Assign_Destination(target);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::Pip_Count -- Fetches the number of pips to display on vessel. *
|
|
|
|
* *
|
|
|
|
* This routine is used to fetch the number of "fullness" pips to display on the vessel. *
|
|
|
|
* This will be the number of passengers on a transport. *
|
|
|
|
* *
|
|
|
|
* INPUT: none *
|
|
|
|
* *
|
|
|
|
* OUTPUT: Returns with the number of pips to draw on this unit. *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 06/25/1995 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
int VesselClass::Pip_Count(void) const
|
|
|
|
{
|
|
|
|
if (Techno_Type_Class()->Max_Passengers() > 0) {
|
|
|
|
int passengers = 0;
|
|
|
|
ObjectClass const * object = Attached_Object();
|
|
|
|
for (int index = 0; index < Class_Of().Max_Pips(); index++) {
|
|
|
|
if (object != NULL) {
|
|
|
|
passengers++;
|
|
|
|
object = object->Next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return passengers;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::Mission_Retreat -- Perform the retreat mission. *
|
|
|
|
* *
|
|
|
|
* This will cause the vessel to run away from the battlefield. It searches for an escape *
|
|
|
|
* map edge according to the reinforcement edge specified in the house. *
|
|
|
|
* *
|
|
|
|
* INPUT: none *
|
|
|
|
* *
|
|
|
|
* OUTPUT: Returns with the number of game frames to delay before this routine is called *
|
|
|
|
* again. *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 07/09/1996 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
int VesselClass::Mission_Retreat(void)
|
|
|
|
{
|
|
|
|
assert(Vessels.ID(this) == ID);
|
|
|
|
assert(IsActive);
|
|
|
|
|
|
|
|
enum {
|
|
|
|
PICK_RETREAT_POINT,
|
|
|
|
TRAVEL
|
|
|
|
};
|
|
|
|
switch (Status) {
|
|
|
|
case PICK_RETREAT_POINT:
|
|
|
|
IsALoaner = true;
|
|
|
|
if (!Target_Legal(NavCom)) {
|
|
|
|
// CELL cell = Map.Calculated_Cell(House->Control.Edge, (Team.Is_Valid()) ? Team->Class->Origin : -1, -1, Class->Speed);
|
|
|
|
CELL cell = Map.Calculated_Cell(House->Control.Edge, (Team.Is_Valid()) ? Team->Class->Origin : -1, Coord_Cell(Center_Coord()), Class->Speed);
|
|
|
|
if (Team.Is_Valid()) {
|
|
|
|
Team->Remove(this);
|
|
|
|
}
|
|
|
|
Assign_Destination(::As_Target(cell));
|
|
|
|
}
|
|
|
|
Status = TRAVEL;
|
|
|
|
return(1);
|
|
|
|
|
|
|
|
case TRAVEL:
|
|
|
|
if (!Target_Legal(NavCom)) {
|
|
|
|
Status = PICK_RETREAT_POINT;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return(MissionControl[Mission].Normal_Delay());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::Is_Allowed_To_Recloak -- Can the vessel recloak now? *
|
|
|
|
* *
|
|
|
|
* Asking this question is part of the recloak process. If the answer is no, then *
|
|
|
|
* recloaking is postponed. This facilitates keeping submarines visible for longer than *
|
|
|
|
* they otherwise would be. *
|
|
|
|
* *
|
|
|
|
* INPUT: none *
|
|
|
|
* *
|
|
|
|
* OUTPUT: bool; Can this vessel recloak now? *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 07/09/1996 BWG : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
bool VesselClass::Is_Allowed_To_Recloak(void) const
|
|
|
|
{
|
|
|
|
return(PulseCountDown == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::Read_INI -- Read the vessel data from the INI database. *
|
|
|
|
* *
|
|
|
|
* This will read and create all vessels specified in the INI database. This routine is *
|
|
|
|
* called when the scenario starts. *
|
|
|
|
* *
|
|
|
|
* INPUT: ini -- Reference to the INI database to read the vessel data from. *
|
|
|
|
* *
|
|
|
|
* OUTPUT: none *
|
|
|
|
* *
|
|
|
|
* WARNINGS: Vessels will be created and placed on the map by this function. *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 07/09/1996 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
void VesselClass::Read_INI(CCINIClass & ini)
|
|
|
|
{
|
|
|
|
VesselClass * vessel; // Working vessel pointer.
|
|
|
|
HousesType inhouse; // Vessel house.
|
|
|
|
VesselType classid; // Vessel class.
|
|
|
|
char buf[128];
|
|
|
|
|
|
|
|
int len = ini.Entry_Count(INI_Name());
|
|
|
|
for (int index = 0; index < len; index++) {
|
|
|
|
char const * entry = ini.Get_Entry(INI_Name(), index);
|
|
|
|
|
|
|
|
ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf));
|
|
|
|
inhouse = HouseTypeClass::From_Name(strtok(buf, ","));
|
|
|
|
if (inhouse != HOUSE_NONE) {
|
|
|
|
classid = VesselTypeClass::From_Name(strtok(NULL, ","));
|
|
|
|
|
|
|
|
if (classid != VESSEL_NONE) {
|
|
|
|
|
|
|
|
if (HouseClass::As_Pointer(inhouse) != NULL) {
|
|
|
|
vessel = new VesselClass(classid, inhouse);
|
|
|
|
if (vessel != NULL) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Read the raw data.
|
|
|
|
*/
|
|
|
|
int strength = atoi(strtok(NULL, ",\r\n"));
|
|
|
|
|
|
|
|
CELL cell = atoi(strtok(NULL, ",\r\n"));
|
|
|
|
|
|
|
|
COORDINATE coord = Cell_Coord(cell);
|
|
|
|
|
|
|
|
DirType dir = (DirType)atoi(strtok(NULL, ",\r\n"));
|
|
|
|
MissionType mission = MissionClass::Mission_From_Name(strtok(NULL, ",\n\r"));
|
|
|
|
|
|
|
|
vessel->Trigger = NULL;
|
|
|
|
TriggerTypeClass * tp = TriggerTypeClass::From_Name(strtok(NULL, ",\r\n"));
|
|
|
|
if (tp != NULL) {
|
|
|
|
TriggerClass * tt = Find_Or_Make(tp);
|
|
|
|
if (tt != NULL) {
|
|
|
|
tt->AttachCount++;
|
|
|
|
vessel->Trigger = tt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vessel->Unlimbo(coord, dir)) {
|
|
|
|
vessel->Strength = (int)vessel->Class->MaxStrength * fixed(strength, 256);
|
|
|
|
if (vessel->Strength > vessel->Class->MaxStrength-3) vessel->Strength = vessel->Class->MaxStrength;
|
|
|
|
// vessel->Strength = Fixed_To_Cardinal(vessel->Class->MaxStrength, strength);
|
|
|
|
if (Session.Type == GAME_NORMAL || vessel->House->IsHuman) {
|
|
|
|
vessel->Assign_Mission(mission);
|
|
|
|
vessel->Commence();
|
|
|
|
} else {
|
|
|
|
vessel->Enter_Idle_Mode();
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
/*
|
|
|
|
** If the vessel could not be unlimboed, then this is a catastrophic error
|
|
|
|
** condition. Delete the vessel.
|
|
|
|
*/
|
|
|
|
delete vessel;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::Write_INI -- Write all vessel scenario data to the INI database. *
|
|
|
|
* *
|
|
|
|
* This routine is used to add the vessel data (needed for scenario start) to the INI *
|
|
|
|
* database specified. If there was any preexisting vessel data in the database, it will *
|
|
|
|
* be cleared *
|
|
|
|
* *
|
|
|
|
* INPUT: ini -- Reference to the ini database to store the vessel data into. *
|
|
|
|
* *
|
|
|
|
* OUTPUT: none *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 07/09/1996 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
void VesselClass::Write_INI(CCINIClass & ini)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
** First, clear out all existing vessel data from the ini file.
|
|
|
|
*/
|
|
|
|
ini.Clear(INI_Name());
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Write the vessel data out.
|
|
|
|
*/
|
|
|
|
for (int index = 0; index < Vessels.Count(); index++) {
|
|
|
|
VesselClass * vessel = Vessels.Ptr(index);
|
|
|
|
if (vessel != NULL && !vessel->IsInLimbo && vessel->IsActive) {
|
|
|
|
char uname[10];
|
|
|
|
char buf[128];
|
|
|
|
|
|
|
|
sprintf(uname, "%d", index);
|
|
|
|
sprintf(buf, "%s,%s,%d,%u,%d,%s,%s",
|
|
|
|
vessel->House->Class->IniName,
|
|
|
|
vessel->Class->IniName,
|
|
|
|
vessel->Health_Ratio()*256,
|
|
|
|
Coord_Cell(vessel->Coord),
|
|
|
|
vessel->PrimaryFacing.Current(),
|
|
|
|
MissionClass::Mission_Name(vessel->Mission),
|
|
|
|
vessel->Trigger.Is_Valid() ? vessel->Trigger->Class->IniName : "None"
|
|
|
|
);
|
|
|
|
ini.Put_String(INI_Name(), uname, buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::Start_Driver -- Starts the vessel by reserving the location it is moving to. *
|
|
|
|
* *
|
|
|
|
* This routine is called when the vessel starts moving. It will reserve the destination *
|
|
|
|
* cell so that it won't be occupied by another vessel as this one is travelling. *
|
|
|
|
* *
|
|
|
|
* INPUT: headto -- The coordinate that will be headed to. *
|
|
|
|
* *
|
|
|
|
* OUTPUT: bool; Was the destination location successfully marked? *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 07/09/1996 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
bool VesselClass::Start_Driver(COORDINATE & headto)
|
|
|
|
{
|
|
|
|
assert(Vessels.ID(this) == ID);
|
|
|
|
assert(IsActive);
|
|
|
|
|
|
|
|
if (DriveClass::Start_Driver(headto) && IsActive) { //BG IsActive can be cleared by Start_Driver
|
|
|
|
Mark_Track(headto, MARK_DOWN);
|
|
|
|
return(true);
|
|
|
|
}
|
|
|
|
return(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::What_Action -- Determines action to perform on specified cell. *
|
|
|
|
* *
|
|
|
|
* This routine will determine what action to perform if the mouse were clicked over the *
|
|
|
|
* cell specified. *
|
|
|
|
* *
|
|
|
|
* INPUT: cell -- The cell that the mouse might be clicked on. *
|
|
|
|
* *
|
|
|
|
* OUTPUT: Returns with the action type that this unit will perform if the mouse were *
|
|
|
|
* clicked of the cell specified. *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 07/11/1996 BWG : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
ActionType VesselClass::What_Action(CELL cell) const
|
|
|
|
{
|
|
|
|
assert(Vessels.ID(this) == ID);
|
|
|
|
assert(IsActive);
|
|
|
|
|
|
|
|
ActionType action = DriveClass::What_Action(cell);
|
|
|
|
if (action == ACTION_NOMOVE && Map[cell].Land_Type() == LAND_BEACH) {
|
|
|
|
return(ACTION_MOVE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (action == ACTION_NOMOVE && Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->Bullet->IsSubSurface && Map[cell].Is_Bridge_Here()) {
|
|
|
|
return(ACTION_ATTACK);
|
|
|
|
}
|
|
|
|
return(action);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::Rotation_AI -- Handles turret and body rotation for this vessel. *
|
|
|
|
* *
|
|
|
|
* Any turret or body rotation for this vessel will be handled by this routine. *
|
|
|
|
* *
|
|
|
|
* INPUT: none *
|
|
|
|
* *
|
|
|
|
* OUTPUT: none *
|
|
|
|
* *
|
|
|
|
* WARNINGS: Only call this routine once per vessel per game logic loop. *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 07/29/1996 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
void VesselClass::Rotation_AI(void)
|
|
|
|
{
|
|
|
|
if (Target_Legal(TarCom) && !IsRotating) {
|
|
|
|
DirType dir = Direction(TarCom);
|
|
|
|
|
|
|
|
if (Class->IsTurretEquipped) {
|
|
|
|
SecondaryFacing.Set_Desired(dir);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
IsRotating = false;
|
|
|
|
if (Class->IsTurretEquipped) {
|
|
|
|
|
|
|
|
if (SecondaryFacing.Is_Rotating()) {
|
|
|
|
Mark(MARK_CHANGE_REDRAW);
|
|
|
|
if (SecondaryFacing.Rotation_Adjust((Class->ROT * House->GroundspeedBias)+1)) {
|
|
|
|
Mark(MARK_CHANGE_REDRAW);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** If no further rotation is necessary, flag that the rotation
|
|
|
|
** has stopped.
|
|
|
|
*/
|
|
|
|
IsRotating = SecondaryFacing.Is_Rotating();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::Combat_AI -- Handles firing and target selection for the vessel. *
|
|
|
|
* *
|
|
|
|
* This routine will process firing logic for the vessel. It includes searching for targets *
|
|
|
|
* and performing any adjustments necessary to bring the target to bear. *
|
|
|
|
* *
|
|
|
|
* INPUT: none *
|
|
|
|
* *
|
|
|
|
* OUTPUT: none *
|
|
|
|
* *
|
|
|
|
* WARNINGS: Only call this routine once per vessel per game logic loop. *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 07/29/1996 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
void VesselClass::Combat_AI(void)
|
|
|
|
{
|
|
|
|
if (Target_Legal(TarCom) && Is_Weapon_Equipped()) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Determine which weapon can fire. First check for the primary weapon. If that weapon
|
|
|
|
** cannot fire, then check any secondary weapon. If neither weapon can fire, then the
|
|
|
|
** failure code returned is that from the primary weapon.
|
|
|
|
*/
|
|
|
|
int primary = What_Weapon_Should_I_Use(TarCom);
|
|
|
|
FireErrorType ok = Can_Fire(TarCom, primary);
|
|
|
|
|
|
|
|
switch (ok) {
|
|
|
|
case FIRE_OK:
|
|
|
|
Fire_At(TarCom, primary);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FIRE_FACING:
|
|
|
|
if (Class->IsTurretEquipped) {
|
|
|
|
SecondaryFacing.Set_Desired(Direction(TarCom));
|
|
|
|
} else {
|
|
|
|
if (!PrimaryFacing.Is_Rotating()) {
|
|
|
|
PrimaryFacing.Set_Desired(Direction(TarCom));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FIRE_CLOAKED:
|
|
|
|
Mark(MARK_OVERLAP_UP);
|
|
|
|
IsFiring = false;
|
|
|
|
Mark(MARK_OVERLAP_DOWN);
|
|
|
|
Do_Uncloak();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::Edge_Of_World_AI -- Determine if vessel is off the edge of the world. *
|
|
|
|
* *
|
|
|
|
* In addition to detecting the edge of world case, this routine will delete the vessel *
|
|
|
|
* if it occurs. *
|
|
|
|
* *
|
|
|
|
* INPUT: none *
|
|
|
|
* *
|
|
|
|
* OUTPUT: bool; Was the vessel deleted by this routine? *
|
|
|
|
* *
|
|
|
|
* WARNINGS: Be sure to examine the return value and if true, abort any further processing *
|
|
|
|
* for this vessel since it has been deleted. This routine should be called once *
|
|
|
|
* per vessel per game logic loop. *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 07/29/1996 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
bool VesselClass::Edge_Of_World_AI(void)
|
|
|
|
{
|
|
|
|
if (!IsDriving && !Map.In_Radar(Coord_Cell(Coord)) && IsLocked) {
|
|
|
|
if (Team.Is_Valid()) Team->IsLeaveMap = true;
|
|
|
|
Stun();
|
|
|
|
delete this;
|
|
|
|
return(true);
|
|
|
|
}
|
|
|
|
return(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::Repair_AI -- Process any self-repair required of this vessel. *
|
|
|
|
* *
|
|
|
|
* When a vessel repairs, it does so 'by itself' and not under direct control of another *
|
|
|
|
* object. This self repair logic is processed here. Upon repair completion of money *
|
|
|
|
* exhuastion, the repair process will terminate. *
|
|
|
|
* *
|
|
|
|
* INPUT: none *
|
|
|
|
* *
|
|
|
|
* OUTPUT: none *
|
|
|
|
* *
|
|
|
|
* WARNINGS: Call this routine only once per vessel per game logic loop. *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 07/29/1996 BWG : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
void VesselClass::Repair_AI(void)
|
|
|
|
{
|
|
|
|
if (IsSelfRepairing) {
|
|
|
|
if ((Frame % (TICKS_PER_MINUTE * Rule.RepairRate)) == 0) {
|
|
|
|
Mark(MARK_CHANGE);
|
|
|
|
int cost = Class->Repair_Cost();
|
|
|
|
int step = Class->Repair_Step();
|
|
|
|
|
|
|
|
if (House->Available_Money() >= cost) {
|
|
|
|
House->Spend_Money(cost);
|
|
|
|
Strength += step;
|
|
|
|
if (Strength >= Class->MaxStrength) {
|
|
|
|
Strength = Class->MaxStrength;
|
|
|
|
IsSelfRepairing = IsToSelfRepair = false;
|
|
|
|
|
|
|
|
// MBL 04.27.2020: Make only audible to the correct player
|
|
|
|
// if (IsOwnedByPlayer) Speak(VOX_UNIT_REPAIRED);
|
|
|
|
if (IsOwnedByPlayer) Speak(VOX_UNIT_REPAIRED, House);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef FIXIT_CARRIER // checked - ajw 9/28/98
|
|
|
|
/***********************************************************************************************
|
|
|
|
* VesselClass::Fire_At -- Try to fire upon the target specified. *
|
|
|
|
* *
|
|
|
|
* This routine is the auto-fire logic for the ship. It will check *
|
|
|
|
* to see if we're an aircraft carrier, and if so, launch one of our *
|
|
|
|
* aircraft. If we're not an aircraft carrier, it lets the higher-level *
|
|
|
|
* Fire_At logic take over. *
|
|
|
|
* *
|
|
|
|
* INPUT: target -- The target to fire upon. *
|
|
|
|
* *
|
|
|
|
* which -- Which weapon to use when firing. 0=primary, 1=secondary. *
|
|
|
|
* *
|
|
|
|
* OUTPUT: bool; Did firing occur? *
|
|
|
|
* *
|
|
|
|
* WARNINGS: none *
|
|
|
|
* *
|
|
|
|
* HISTORY: *
|
|
|
|
* 04/26/1994 JLB : Created. *
|
|
|
|
*=============================================================================================*/
|
|
|
|
BulletClass * VesselClass::Fire_At(TARGET target, int which)
|
|
|
|
{
|
|
|
|
//PG assert(Units.ID(this) == ID);
|
|
|
|
assert(IsActive);
|
|
|
|
|
|
|
|
if (*this == VESSEL_CARRIER) {
|
|
|
|
Arm = CarrierLaunchDelay;
|
|
|
|
FootClass * passenger = Detach_Object();
|
|
|
|
if (passenger != NULL) {
|
|
|
|
ScenarioInit++;
|
|
|
|
passenger->Unlimbo(Center_Coord());
|
|
|
|
ScenarioInit--;
|
|
|
|
passenger->Assign_Mission(MISSION_ATTACK);
|
|
|
|
passenger->Assign_Target(TarCom);
|
|
|
|
passenger->Commence();
|
|
|
|
// If we've launched our last aircraft, discontinue attacking.
|
|
|
|
if (!How_Many()) Assign_Target(TARGET_NONE);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return DriveClass::Fire_At(target, which);
|
|
|
|
}
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|