CnC_Remastered_Collection/REDALERT/VESSEL.CPP

2451 lines
109 KiB
C++
Raw Permalink Normal View History

//
// 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();
if (techno != NULL && techno->Is_Cloaked(this)) {
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. *
*=============================================================================================*/
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));
}
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++) {
Command & Conquer Remastered post-launch patch Improvements to harvester resource finding logic. Don't allow the Advanced Comm Center to be capturable in skirmish or multiplayer. Increased failed pathfinding fudge factor. Buildings accept the Guard command if they can attack. Don't allow force capturing of ally structures. Fixes for laser Orcas in S3cr3t M1ss10n. Properly restore them after save. Reset Orcas after loads. Fixed flag animation rendering in CTF. Potentially fix a crash if aircraft are destroyed outside the map bounds. Fixed legacy Obelisk line rendering. Fix out-of-bounds crash in TD; issue was already fixed in RA. Disable capture flag on Commandos. Drop the flag when entering the limbo state. Fixed end game race condition, winning team ID is always sent before individual player win/lose messages. Fixed Chan spawn on SCB10EA. Don't show enter cursor for enemy units on refineries and repair pads. Changing right-click support for first put building on hold, and then subsequenct right-clicks to decrement that queue count for 1x or 5x; Then, 1x or 5x Left click will resume from hold. Don't debug reveal legacy rendering when a player is defeated. Fixed crash when loading saves of custom campaign maps. Reset harvester archived target when given a direct harvest order. Prevent NOD cargo planes from being force attacked. Fixed unit selection on load. Migrated queued repair pad functionality from RA to TD. Randomly animate infantry in area guard mode. Fixed crash accessing inactive objects. Added some walls in SCG08EB to prevent civilians from killing themselves. TD + RA: Audio: Overiding "Our base is under attack" cooldown timing from legacy from 2 minutes to 30 seconds, so it will be heard 4x as often. Fixed adjacent cell out-of-bounds crash issues. Kill player on disconnect Fixed and improved build time calculations to be consistent between TD and RA. Don't show health bars for cloaked Stealth Tanks in the legacy renderer. Fix selection of individual control groups after mixed selection. More adjustments to SCG08EB; switch C7 to C5, and add civilian AI to avoid Tiberium. Extra safety checks for units that have no weapons and aircraft that can't hunt. Fix loading of multiple infantry onto an APC. Additional safety checks for invalid coordinates. Prevent units from being instantly repaired. Fix map passability. Fail Allied mission 5B if the spy re-boards the starting transport (matches 5A and 5C behavior). Fixed multiplayer formation move causing units to move at light speed. Ignore movement destination checks if a unit is part of a mission-driven team. Fix buffer overrun crash. Ignore mines when determining win conditions. Fixed river passability in Blue Lakes.
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)) {
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