CnC_Remastered_Collection/REDALERT/DRIVE.CPP
PG-SteveT fc5cd5a775 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 09:43:21 -07:00

2339 lines
91 KiB
C++

//
// 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/DRIVE.CPP 1 3/03/97 10:24a 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 : DRIVE.CPP *
* *
* Programmer : Joe L. Bostic *
* *
* Start Date : April 22, 1994 *
* *
* Last Update : October 31, 1996 [JLB] *
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* DriveClass::AI -- Processes unit movement and rotation. *
* DriveClass::Approach_Target -- Handles approaching the target in order to attack it. *
* DriveClass::Assign_Destination -- Set the unit's NavCom. *
* DriveClass::Class_Of -- Fetches a reference to the class type for this object. *
* DriveClass::Debug_Dump -- Displays status information to monochrome screen. *
* DriveClass::Do_Turn -- Tries to turn the vehicle to the specified direction. *
* DriveClass::DriveClass -- Constructor for drive class object. *
* DriveClass::Fixup_Path -- Adds smooth start path to normal movement path. *
* DriveClass::Force_Track -- Forces the unit to use the indicated track. *
* DriveClass::Lay_Track -- Handles track laying logic for the unit. *
* DriveClass::Limbo -- Prepares vehicle and then limbos it. *
* DriveClass::Mark_Track -- Marks the midpoint of the track as occupied. *
* DriveClass::Ok_To_Move -- Checks to see if this object can begin moving. *
* DriveClass::Per_Cell_Process -- Handles when unit finishes movement into a cell. *
* DriveClass::Response_Attack -- Voice feedback when ordering the unit to attack a target. *
* DriveClass::Response_Move -- Voice feedback when ordering the unit to move. *
* DriveClass::Response_Select -- Voice feedback when selecting the unit. *
* DriveClass::Scatter -- Causes the unit to travel to a nearby safe cell. *
* DriveClass::Smooth_Turn -- Handles the low level coord calc for smooth turn logic. *
* DriveClass::Start_Of_Move -- Tries to get a unit to advance toward cell. *
* DriveClass::Stop_Driver -- Handles removing occupation bits when driving stops. *
* DriveClass::Teleport_To -- Teleport object to specified location. *
* DriveClass::While_Moving -- Processes unit movement. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "function.h"
#ifdef NEVER
void test(void)
{
enum nums {one, two, three};
nums x;
nums *ptr;
ptr = &x;
}
#endif
/***********************************************************************************************
* DriveClass::Response_Select -- Voice feedback when selecting the unit. *
* *
* This is the voice to play when the unit is selected. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 12/30/1994 JLB : Created. *
*=============================================================================================*/
void DriveClass::Response_Select(void)
{
assert(IsActive);
static VocType _response[] = {
VOC_VEHIC,
VOC_REPORT,
VOC_YESSIR,
VOC_YESSIR,
VOC_YESSIR,
VOC_AWAIT
};
VocType response = _response[Sim_Random_Pick(0, ARRAY_SIZE(_response)-1)];
if (AllowVoice) {
Sound_Effect(response, fixed(1), -(ID+1));
}
}
/***********************************************************************************************
* DriveClass::Response_Move -- Voice feedback when ordering the unit to move. *
* *
* This plays the audio feedback when ordering this unit to move to a new destination. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 12/30/1994 JLB : Created. *
*=============================================================================================*/
void DriveClass::Response_Move(void)
{
assert(IsActive);
static VocType _response[] = {
VOC_ACKNOWL,
VOC_AFFIRM,
};
VocType response = _response[Sim_Random_Pick(0, ARRAY_SIZE(_response)-1)];
if (AllowVoice) {
Sound_Effect(response, fixed(1), -(ID+1));
}
}
/***********************************************************************************************
* DriveClass::Response_Attack -- Voice feedback when ordering the unit to attack a target. *
* *
* This plays the audio feedback when ordering this unit to attack. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 12/30/1994 JLB : Created. *
*=============================================================================================*/
void DriveClass::Response_Attack(void)
{
assert(IsActive);
static VocType _response[] = {
VOC_AFFIRM,
VOC_ACKNOWL
};
VocType response = _response[Sim_Random_Pick(0, ARRAY_SIZE(_response)-1)];
if (AllowVoice) {
Sound_Effect(response, fixed(1), -(ID+1));
}
}
/***********************************************************************************************
* DriveClass::Scatter -- Causes the unit to travel to a nearby safe cell. *
* *
* This routine is called when the unit discovers that it should get out of the "hot seat" *
* and move to an adjacent cell. Since the safety of the adjacent cell is not determined *
* before the move begins, it will appear that the unit is just scattering (which it *
* should). *
* *
* INPUT: threat -- The coordinate of the source of the threat. The unit will try to move *
* roughly away from the threat. *
* *
* forced -- The threat is real and a serious effort to scatter should be made. *
* *
* nokidding-- The scatter should affect the player's infantry even if it otherwise *
* wouldn't have. *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 09/25/1994 JLB : Created. *
* 09/27/1995 JLB : Revised to never scatter if already moving. *
* 07/09/1996 JLB : Moved to DriveClass so that ships will scatter too. *
* 08/02/1996 JLB : Added the "nokidding" parameter. *
*=============================================================================================*/
void DriveClass::Scatter(COORDINATE threat, bool forced, bool nokidding)
{
assert(IsActive);
/*
** Certain missions prevent scattering regardless of whether it would be
** a good idea or not.
*/
if (MissionControl[Mission].IsParalyzed) return;
if ((What_Am_I() != RTTI_UNIT || !((UnitClass *)this)->IsDumping) && (!Target_Legal(NavCom) || (nokidding && !IsRotating))) {
if (!Target_Legal(TarCom) || forced || Random_Pick(1, 4) == 1) {
FacingType toface;
FacingType newface;
CELL newcell;
if (threat != 0) {
toface = Dir_Facing(Direction8(threat, Coord));
toface = toface + FacingType(Random_Pick(0, 2)-1);
} else {
toface = Dir_Facing(PrimaryFacing.Current());
toface = toface + FacingType(Random_Pick(0, 2)-1);
}
for (FacingType face = FACING_N; face < FACING_COUNT; face++) {
newface = toface + face;
newcell = Adjacent_Cell(Coord_Cell(Coord), newface);
if (Map.In_Radar(newcell) && Can_Enter_Cell(newcell) == MOVE_OK) {
Assign_Destination(::As_Target(newcell));
}
}
}
}
}
/***********************************************************************************************
* DriveClass::Limbo -- Prepares vehicle and then limbos it. *
* *
* This routine removes the occupation bits for the vehicle and also handles cleaning up *
* any vehicle reservation bits. After this, it then proceeds with limboing the unit. *
* *
* INPUT: none *
* *
* OUTPUT: bool; Was the vehicle limboed? *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 12/22/1994 JLB : Created. *
*=============================================================================================*/
bool DriveClass::Limbo(void)
{
if (!IsInLimbo) {
Stop_Driver();
TrackNumber = -1;
}
return(FootClass::Limbo());
}
/***********************************************************************************************
* DriveClass::Stop_Driver -- Handles removing occupation bits when driving stops. *
* *
* This routine will remove the "reservation" flag (if present) when the vehicle is *
* required to stop movement. *
* *
* INPUT: none *
* *
* OUTPUT: bool; Was the vehicle stopped? *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 12/22/1994 JLB : Created. *
*=============================================================================================*/
bool DriveClass::Stop_Driver(void)
{
assert(IsActive);
/*
** We only need to do something if the vehicle is actually going
** somewhere.
*/
if (Head_To_Coord()) {
/*
** Safe off whether the vehicle is down or not so we know whether
** we have to put it back down.
*/
int temp = IsDown;
/*
** If the vehicle is down, pick it up so it doesn't interfere with
** our flags.
*/
if (temp) {
Mark(MARK_UP);
}
/*
** Call the drive class function which will let us release the
** reserved track.
*/
Mark_Track(Head_To_Coord(), MARK_UP);
/*
** If it was down it should be down when we are done.
*/
if (temp) {
Mark(MARK_DOWN);
}
}
return(FootClass::Stop_Driver());
}
/***********************************************************************************************
* DriveClass::Do_Turn -- Tries to turn the vehicle to the specified direction. *
* *
* This routine will set the vehicle to rotate to the direction specified. For tracked *
* vehicles, it is just a simple rotation. For wheeled vehicles, it performs a series *
* of short drives (three point turn) to face the desired direction. *
* *
* INPUT: dir -- The direction that this vehicle should face. *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 05/29/1995 JLB : Created. *
*=============================================================================================*/
void DriveClass::Do_Turn(DirType dir)
{
assert(IsActive);
if (dir != PrimaryFacing) {
#ifdef TOFIX
/*
** Special rotation track is needed for units that
** cannot rotate in place.
*/
if (Special.IsThreePoint && TrackNumber == -1 && Techno_Type_Class()->Speed == SPEED_WHEEL) {
int facediff; // Signed difference between current and desired facing.
FacingType face; // Current facing (ordinal value).
facediff = PrimaryFacing.Difference(dir) >> 5;
facediff = Bound(facediff, -2, 2);
if (facediff) {
face = Dir_Facing(PrimaryFacing);
IsOnShortTrack = true;
Force_Track(face*FACING_COUNT + (face + facediff), Coord);
Path[0] = FACING_NONE;
Set_Speed(0xFF); // Full speed.
}
} else {
PrimaryFacing.Set_Desired(dir);
}
#else
PrimaryFacing.Set_Desired(dir);
// IsRotating = true;
#endif
}
}
/***********************************************************************************************
* DriveClass::Teleport_To -- Teleport object to specified location. *
* *
* This will teleport the object to the specified location or as close as possible to it *
* if the destination is blocked. *
* *
* INPUT: cell -- The desired destination cell to teleport to. *
* *
* OUTPUT: bool; Was the teleport successful? *
* *
* WARNINGS: All current activity of this object will be terminated by the teleport. It will *
* arrive at the destination in static guard mode. *
* *
* HISTORY: *
* 10/21/1996 JLB : Created. *
* 10/31/1996 JLB : Handles flag teleport case. *
*=============================================================================================*/
bool DriveClass::Teleport_To(CELL cell)
{
/*
** All cargo gets destroyed.
*/
if (Rule.IsChronoKill) {
Kill_Cargo(NULL);
}
Stop_Driver();
Force_Track(-1, 0);
PrimaryFacing.Set_Current(PrimaryFacing.Desired());
Transmit_Message(RADIO_OVER_OUT);
Assign_Destination(TARGET_NONE);
Assign_Target(TARGET_NONE);
Assign_Mission(MISSION_NONE);
Commence();
Mark(MARK_UP);
/*
** A teleported unit will drop the flag right where it's at.
*/
if (What_Am_I() == RTTI_UNIT && ((UnitClass *)this)->Flagged != HOUSE_NONE) {
HouseClass::As_Pointer(((UnitClass *)this)->Flagged)->Flag_Attach(Coord_Cell(Coord));
}
if (Can_Enter_Cell(cell) != MOVE_OK) {
cell = Map.Nearby_Location(cell, Techno_Type_Class()->Speed);
}
Coord = Cell_Coord(cell);
Mark(MARK_DOWN);
Look(false);
Per_Cell_Process(PCP_END);
return(true);
}
/***********************************************************************************************
* DriveClass::Force_Track -- Forces the unit to use the indicated track. *
* *
* This override (nuclear bomb) style routine is to be used when a unit needs to start *
* on a movement track but is outside the normal movement system. This occurs when a *
* harvester starts driving off of a refinery. *
* *
* INPUT: track -- The track number to start on. *
* *
* coord -- The coordinate that the unit will end up at when the movement track *
* is completed. *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 03/17/1995 JLB : Created. *
*=============================================================================================*/
void DriveClass::Force_Track(int track, COORDINATE coord)
{
assert(IsActive);
TrackNumber = track;
TrackIndex = 0;
if (coord != 0) {
Start_Driver(coord);
}
}
/***********************************************************************************************
* DriveClass::DriveClass -- Constructor for drive class object. *
* *
* This will initialize the drive class to its default state. It is called as a result *
* of creating a unit. *
* *
* INPUT: classid -- The unit's ID class. It is passed on to the foot class constructor. *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 07/13/1994 JLB : Created. *
*=============================================================================================*/
DriveClass::DriveClass(RTTIType rtti, int id, HousesType house) :
FootClass(rtti, id, house),
IsMoebius(false),
IsHarvesting(false),
IsTurretLockedDown(false),
IsOnShortTrack(false),
SpeedAccum(0),
MoebiusCountDown(0),
MoebiusCell(0),
TrackNumber(-1),
TrackIndex(0)
{
}
#ifdef CHEAT_KEYS
/***********************************************************************************************
* DriveClass::Debug_Dump -- Displays status information to monochrome screen. *
* *
* This debug utility function will display the status of the drive class to the mono *
* screen. It is through this information that bugs can be tracked down. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 05/31/1994 JLB : Created. *
*=============================================================================================*/
void DriveClass::Debug_Dump(MonoClass * mono) const
{
assert(IsActive);
mono->Fill_Attrib(66, 14, 12, 1, IsMoebius ? MonoClass::INVERSE : MonoClass::NORMAL);
FootClass::Debug_Dump(mono);
}
#endif
/***********************************************************************************************
* DriveClass::Smooth_Turn -- Handles the low level coord calc for smooth turn logic. *
* *
* This routine calculates the new coordinate value needed for the *
* smooth turn logic. The adjustment and flag values must be *
* determined prior to entering this routine. *
* *
* INPUT: adj -- The adjustment coordinate as lifted from the *
* correct smooth turn table. *
* *
* dir -- Pointer to dir for possible modification *
* according to the flag bits. *
* *
* OUTPUT: Returns with the coordinate the unit should positioned to. *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 03/14/1994 JLB : Created. *
* 07/13/1994 JLB : Converted to member function. *
*=============================================================================================*/
COORDINATE DriveClass::Smooth_Turn(COORDINATE adj, DirType & dir)
{
assert(IsActive);
DirType workdir = dir;
int x,y;
int temp;
TrackControlType flags = TrackControl[TrackNumber].Flag;
x = Coord_X(adj);
y = Coord_Y(adj);
if (flags & F_T) {
temp = x;
x = y;
y = temp;
workdir = (DirType)(DIR_W - workdir);
}
if (flags & F_X) {
x = -x;
workdir = (DirType)-workdir;
}
if (flags & F_Y) {
y = -y;
workdir = (DirType)(DIR_S - workdir);
}
dir = workdir;
return(XY_Coord( (LEPTON)(Coord_X(Head_To_Coord()) + x), (LEPTON)(Coord_Y(Head_To_Coord()) + y)));
}
/***********************************************************************************************
* DriveClass::Assign_Destination -- Set the unit's NavCom. *
* *
* This routine is used to set the unit's navigation computer to the *
* specified target. Once the navigation computer is set, the unit *
* will start planning and moving toward the destination. *
* *
* INPUT: target -- The destination target for the unit to head to. *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 09/07/1992 JLB : Created. *
* 04/15/1994 JLB : Converted to member function. *
*=============================================================================================*/
void DriveClass::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;
/*
** For harvesting type vehicles, it might go into a dock and unload procedure
** when the harvester is full and an empty refinery is selected as a target.
*/
BuildingClass * b = As_Building(target);
/*
** If the player clicked on refinery but it is not busy, then assign
** it to unload at the refinery.
*/
if (b != NULL && *b == STRUCT_REFINERY && What_Am_I() == RTTI_UNIT && ((UnitTypeClass *)Techno_Type_Class())->IsToHarvest) {
if (Contact_With_Whom() != b && !b->In_Radio_Contact()) {
/*
** Establish radio contact protocol. If the facility responds correctly,
** then remain in radio contact and proceed toward the desired destination.
*/
if (Transmit_Message(RADIO_HELLO, b) == RADIO_ROGER) {
if (Mission != MISSION_ENTER && Mission != MISSION_HARVEST) {
Assign_Mission(MISSION_ENTER);
target = TARGET_NONE;
} else {
// target = TARGET_NONE;
}
} else {
// target = TARGET_NONE;
}
} else {
// target = TARGET_NONE;
}
}
/*
** Set the unit's navigation computer.
*/
FootClass::Assign_Destination(target);
Path[0] = FACING_NONE; // Force recalculation of path.
if (!IsDriving && Mission != MISSION_UNLOAD) {
Start_Of_Move();
}
}
/***********************************************************************************************
* DriveClass::While_Moving -- Processes unit movement. *
* *
* This routine is used to process movement for the units as they move. *
* It is called many times for each cell's worth of movement. This *
* routine only applies after the next cell HeadTo has been determined. *
* *
* INPUT: none *
* *
* OUTPUT: true/false; Should this routine be called again? *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 02/02/1992 JLB : Created. *
* 04/15/1994 JLB : Converted to member function. *
*=============================================================================================*/
bool DriveClass::While_Moving(void)
{
assert(IsActive);
/*
** Perform quick legality checks.
*/
if (!IsDriving || TrackNumber == -1 || (IsRotating && !Techno_Type_Class()->IsTurretEquipped)) {
SpeedAccum = 0; // Kludge? No speed should accumulate if movement is on hold.
return(false);
}
/*
** If enough movement has accumulated so that the unit can
** visibly move on the map, then process accordingly.
** Slow the unit down if he's carrying a flag.
*/
MPHType maxspeed = MPHType(min(Techno_Type_Class()->MaxSpeed * SpeedBias * House->GroundspeedBias, (int)MPH_LIGHT_SPEED));
if (IsFormationMove) maxspeed = FormationMaxSpeed;
int actual; // Working movement addition value.
if (((UnitClass *)this)->Flagged != HOUSE_NONE) {
actual = SpeedAccum + ((int)maxspeed/2) * fixed(Speed, 256);
} else {
actual = SpeedAccum + maxspeed * fixed(Speed, 256);
}
if (actual > PIXEL_LEPTON_W) {
TurnTrackType const * track; // Track control pointer.
TrackType const * ptr; // Pointer to coord offset values.
int tracknum; // The track number being processed.
FacingType nextface; // Next facing queued in path.
bool adj; // Is a turn coming up?
track = &TrackControl[TrackNumber];
if (IsOnShortTrack) {
tracknum = track->StartTrack;
} else {
tracknum = track->Track;
}
ptr = RawTracks[tracknum-1].Track;
nextface = Path[0];
/*
** Determine if there is a turn coming up. If there is
** a turn, then track jumping might occur.
*/
adj = false;
if (nextface != FACING_NONE && Dir_Facing(track->Facing) != nextface) {
adj = true;
}
/*
** Skip ahead the number of track steps required (limited only
** by track length). Set the unit to the new position and
** flag the unit accordingly.
*/
Mark(MARK_UP);
while (actual > PIXEL_LEPTON_W) {
COORDINATE offset;
DirType dir;
actual -= PIXEL_LEPTON_W;
offset = ptr[TrackIndex].Offset;
if (offset || !TrackIndex) {
dir = ptr[TrackIndex].Facing;
Coord = Smooth_Turn(offset, dir);
PrimaryFacing.Set(dir);
/*
** See if "per cell" processing is necessary.
*/
if (TrackIndex && RawTracks[tracknum-1].Cell == TrackIndex) {
Mark(MARK_DOWN);
Per_Cell_Process(PCP_DURING);
if (!IsActive) {
return(false);
}
Mark(MARK_UP);
}
/*
** The unit could "jump tracks". Check to see if the unit should
** do so.
*/
if (/**this != UNIT_GUNBOAT &&*/ nextface != FACING_NONE && adj && RawTracks[tracknum-1].Jump == TrackIndex && TrackIndex) {
TurnTrackType const * newtrack; // Proposed jump-to track.
int tnum;
tnum = (int)(Dir_Facing(track->Facing) * FACING_COUNT) + (int)nextface;
newtrack = &TrackControl[tnum];
if (newtrack->Track && RawTracks[newtrack->Track-1].Entry) {
COORDINATE c = Head_To_Coord();
int oldspeed = Speed;
c = Adjacent_Cell(c, nextface);
switch (Can_Enter_Cell(Coord_Cell(c), nextface)) {
case MOVE_OK:
IsOnShortTrack = false; // Shouldn't be necessary, but...
TrackNumber = tnum;
track = newtrack;
tracknum = track->Track;
TrackIndex = RawTracks[tracknum-1].Entry-1; // Anticipate increment.
ptr = RawTracks[tracknum-1].Track;
adj = false;
Stop_Driver();
IsDriving = true;
Per_Cell_Process(PCP_END);
IsDriving = false;
if (!IsActive) return(false);
if (Start_Driver(c)) {
Set_Speed(oldspeed);
memcpy((char*)&Path[0], (char*)&Path[1], CONQUER_PATH_MAX-1);
Path[CONQUER_PATH_MAX-1] = FACING_NONE;
} else {
Path[0] = FACING_NONE;
TrackNumber = -1;
actual = 0;
}
break;
case MOVE_CLOAK:
Map[c].Shimmer();
break;
case MOVE_TEMP:
#ifdef TOFIX
if (*this == UNIT_HARVESTER || !House->IsHuman) {
#else
if (!House->IsHuman) {
#endif
Map[c].Incoming(0, true, true);
}
break;
}
}
}
TrackIndex++;
} else {
actual = 0;
Coord = Head_To_Coord();
Stop_Driver();
TrackNumber = -1;
TrackIndex = NULL;
/*
** Perform "per cell" activities.
*/
Mark(MARK_DOWN);
Per_Cell_Process(PCP_END);
if (!IsActive) return(false);
Mark(MARK_UP);
break;
}
}
if (IsActive) {
Mark(MARK_DOWN);
}
}
/*
** Replace any remainder back into the unit's movement
** accumulator to be processed next pass.
*/
SpeedAccum = actual;
return(true);
}
/***********************************************************************************************
* DriveClass::Per_Cell_Process -- Handles when unit finishes movement into a cell. *
* *
* This routine is called when a unit has mostly or completely *
* entered a cell. The unit might be in the middle of a movement track *
* when this routine is called. It's primary purpose is to perform *
* sighting and other "per cell" activities. *
* *
* INPUT: why -- Specifies the circumstances under which this routine was called. *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 11/03/1993 JLB : Created. *
* 03/30/1994 JLB : Revamped for track system. *
* 04/15/1994 JLB : Converted to member function. *
* 06/18/1994 JLB : Converted to virtual function. *
* 06/18/1994 JLB : Distinguishes between center and near-center conditions. *
*=============================================================================================*/
void DriveClass::Per_Cell_Process(PCPType why)
{
assert(IsActive);
if (why == PCP_END) {
CELL cell = Coord_Cell(Coord);
/*
** Check to see if it has reached its destination. If so, then clear the NavCom
** regardless of the remaining path list.
*/
if (As_Cell(NavCom) == cell) {
IsTurretLockedDown = false;
NavCom = TARGET_NONE;
Path[0] = FACING_NONE;
}
Lay_Track();
}
FootClass::Per_Cell_Process(why);
}
/***********************************************************************************************
* DriveClass::Start_Of_Move -- Tries to get a unit to advance toward cell. *
* *
* This will try to start a unit advancing toward the cell it is *
* facing. It will check for and handle legality and reserving of the *
* necessary cell. *
* *
* INPUT: none *
* *
* OUTPUT: true/false; Should this routine be called again because *
* initial start operation is temporarily delayed? *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 02/02/1992 JLB : Created. *
* 10/18/1993 JLB : This should be called repeatedly until HeadTo is not NULL. *
* 03/16/1994 JLB : Revamped for track logic. *
* 04/15/1994 JLB : Converted to member function. *
* 06/19/1995 JLB : Fixed so that it won't fire on ground unnecessarily. *
* 07/13/1995 JLB : Handles bumping into cloaked objects. *
* 09/22/1995 JLB : Breaks out of hopeless hunt mode. *
* 07/10/1996 JLB : Sets scan limit if necessary. *
*=============================================================================================*/
bool DriveClass::Start_Of_Move(void)
{
assert(IsActive);
FacingType facing; // Direction movement will commence.
DirType dir; // Desired actual facing toward destination.
int facediff; // Difference between current and desired facing.
int speed; // Speed of unit.
CELL destcell; // Cell of destination.
LandType ground; // Ground unit is entering.
COORDINATE dest; // Destination coordinate.
facing = Path[0];
if (!Target_Legal(NavCom) && facing == FACING_NONE) {
IsTurretLockedDown = false;
Stop_Driver();
if (Mission == MISSION_MOVE) {
Enter_Idle_Mode();
}
return(false); // Why is it calling this routine!?!
}
/*
** Reduce the path length if the target is a unit and the
** range to the unit is less than the precalculated path steps.
*/
if (facing != FACING_NONE) {
int dist;
if (Is_Target_Vessel(NavCom) || Is_Target_Unit(NavCom) || Is_Target_Infantry(NavCom)) {
dist = Lepton_To_Cell((LEPTON)Distance(NavCom));
if (dist < ARRAY_SIZE(Path)) {
Path[dist] = FACING_NONE;
facing = Path[0]; // Maybe needed.
}
}
}
/*
** If the path is invalid at this point, then generate one. If
** generating a new path fails, then abort NavCom.
*/
if (facing == FACING_NONE) {
/*
** If after a path search, there is still no valid path, then set the
** NavCom to null and let the script take care of assigning a new
** navigation target.
*/
if (PathDelay != 0) {
return(false);
}
if (!Basic_Path()) {
/*
** If the unit is close enough to the target then just stop
** driving now. This prevents the fidgeting that would occur
** if they mindlessly kept trying to get to the exact location
** desired. This is quite necessary since it is typical to move
** several units with the same mouse click.
*/
if (!Is_On_Priority_Mission() && Distance(NavCom) < Rule.CloseEnoughDistance && (Mission == MISSION_MOVE || Mission == MISSION_GUARD_AREA)) {
Assign_Destination(TARGET_NONE);
if (!IsActive) return(false);
} else {
/*
** If a basic path could not be found, but the immediate move destination is
** blocked by a friendly temporary blockage, then cause that blockage
** to scatter.
*/
CELL cell = Adjacent_Cell(Coord_Cell(Center_Coord()), PrimaryFacing.Current());
if (Map.In_Radar(cell)) {
MoveType ok = Can_Enter_Cell(cell);
if (ok == MOVE_TEMP) {
CellClass * cellptr = &Map[cell];
TechnoClass * blockage = cellptr->Cell_Techno();
if (blockage && House->Is_Ally(blockage)) {
/*
** If the target can be told to get out of the way, only bother
** to do so if we aren't very close to the target and this
** object can just say "good enough" and stop here.
*/
if (Distance(NavCom) < Rule.CloseEnoughDistance && !In_Radio_Contact()) {
Assign_Destination(TARGET_NONE);
return(false);
} else {
cellptr->Incoming(0, true, false);
// cellptr->Incoming(0, true, true);
}
}
}
}
if (TryTryAgain > 0) {
TryTryAgain--;
} else {
Assign_Destination(TARGET_NONE);
if (!IsActive) return(false);
if (IsNewNavCom) Sound_Effect(VOC_SCOLD);
IsNewNavCom = false;
}
}
/*
** Since the path was blocked, check to make sure that it was completely
** blocked. If so and it has a valid TarCom and it is out of range of the
** TarCom, then give this unit a range limit so that it might not pick
** a "can't reach" target again.
*/
if (!Target_Legal(NavCom) && Target_Legal(TarCom) && !In_Range(TarCom)) {
IsScanLimited = true;
if (Team.Is_Valid()) Team->Scan_Limit();
Assign_Target(TARGET_NONE);
}
/*
** Stop the movement, for now, and let the subsequent logic in later game
** frames resume movement as appropriate.
*/
Stop_Driver();
TrackNumber = -1;
IsTurretLockedDown = false;
return(false);
}
/*
** If a basic path could be found, but the immediate move destination is
** blocked by a friendly temporary blockage, then cause that blockage
** to scatter.
*/
CELL cell = Adjacent_Cell(Coord_Cell(Center_Coord()), Path[0]);
if (Map.In_Radar(cell)) {
MoveType ok = Can_Enter_Cell(cell);
if (ok == MOVE_TEMP) {
CellClass * cellptr = &Map[cell];
TechnoClass * blockage = cellptr->Cell_Techno();
if (blockage && House->Is_Ally(blockage)) {
/*
** If the target can be told to get out of the way, only bother
** to do so if we aren't very close to the target and this
** object can just say "good enough" and stop here.
*/
if (Distance(NavCom) < Rule.CloseEnoughDistance && !In_Radio_Contact()) {
Assign_Destination(TARGET_NONE);
return(false);
} else {
cellptr->Incoming(0, true, false);
// cellptr->Incoming(0, true, true);
}
}
}
}
TryTryAgain = PATH_RETRY;
facing = Path[0];
}
/*
** Determine the coordinate of the next cell to move into.
*/
dest = Adjacent_Cell(Coord, facing);
dir = Facing_Dir(facing);
/*
** Set the facing correctly if it isn't already correct. This
** means starting a rotation track if necessary.
*/
facediff = PrimaryFacing.Difference(dir);
if (facediff) {
/*
** Request a change of facing.
*/
Do_Turn(dir);
return(true);
} else {
/* NOTE: Beyond this point, actual track assignment can begin.
**
** If the cell to move into is impassable (probably for some unexpected
** reason), then abort the path list and set the speed to zero. The
** next time this routine is called, a new path will be generated.
*/
destcell = Coord_Cell(dest);
Mark(MARK_UP);
MoveType cando = Can_Enter_Cell(destcell, facing);
Mark(MARK_DOWN);
if (cando != MOVE_OK) {
if (Mission == MISSION_MOVE /*KO&& House->IsHuman */&& Distance(NavCom) < Rule.CloseEnoughDistance) {
Assign_Destination(TARGET_NONE);
if (!IsActive) return(false);//BG
}
/*
** If a temporary friendly object is blocking the path, then cause it to
** get out of the way.
*/
if (cando == MOVE_TEMP) {
Map[destcell].Incoming(0, true, true);
}
/*
** If a cloaked object is blocking, then shimmer the cell.
*/
if (cando == MOVE_CLOAK) {
Map[destcell].Shimmer();
}
Stop_Driver();
if (cando != MOVE_MOVING_BLOCK) {
Path[0] = FACING_NONE; // Path is blocked!
}
/*
** If blocked by a moving block then just exit start of move and
** try again next tick.
*/
if (cando == MOVE_DESTROYABLE) {
if (Map[destcell].Cell_Object()) {
if (!House->Is_Ally(Map[destcell].Cell_Object())) {
Override_Mission(MISSION_ATTACK, Map[destcell].Cell_Object()->As_Target(), TARGET_NONE);
}
} else {
if (Map[destcell].Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Map[destcell].Overlay).IsWall) {
Override_Mission(MISSION_ATTACK, ::As_Target(destcell), TARGET_NONE);
}
}
} else {
if (IsNewNavCom) Sound_Effect(VOC_SCOLD);
}
IsNewNavCom = false;
TrackNumber = -1;
return(true);
}
/*
** Determine the speed that the unit can travel to the desired square.
*/
ground = Map[destcell].Land_Type();
speed = Ground[ground].Cost[Techno_Type_Class()->Speed] * 255;
/* change speed if it's related to a team move */
if (IsFormationMove) speed = Ground[ground].Cost[FormationSpeed] * 255;
if (!speed) speed = 128;
#ifdef NEVER
/*
** Set the jiggle flag if the terrain would cause the unit
** to jiggle when travelled over.
*/
BaseF &= ~BASEF_JIGGLE;
if (Ground[ground].Jiggle) {
BaseF |= BASEF_JIGGLE;
}
#endif
/*
** A damaged unit has a reduced speed.
*/
if (Health_Ratio() <= Rule.ConditionYellow /*(Techno_Type_Class()->MaxStrength>>1) > Strength*/) {
speed -= (speed/4); // Three quarters speed.
}
if ((speed != Speed)/* || !SpeedAdd*/) {
Set_Speed(speed); // Full speed.
}
/*
** Reserve the destination cell so that it won't become
** occupied AS this unit is moving into it.
*/
if (cando != MOVE_OK) {
Path[0] = FACING_NONE; // Path is blocked!
TrackNumber = -1;
dest = NULL;
} else {
Overrun_Square(Coord_Cell(dest), true);
/*
** Determine which track to use (based on recorded path).
*/
FacingType nextface = Path[1];
if (nextface == FACING_NONE) nextface = facing;
IsOnShortTrack = false;
TrackNumber = facing * FACING_COUNT + (int)nextface;
if (TrackControl[TrackNumber].Track == 0) {
Path[0] = FACING_NONE;
TrackNumber = -1;
return(true);
} else {
if (TrackControl[TrackNumber].Flag & F_D) {
/*
** If the middle cell of a two cell track contains a crate,
** the check for goodies before movement starts.
*/
if (!Map[destcell].Goodie_Check(this)) {
cando = MOVE_NO;
if (!IsActive) return(false);
} else {
if (!IsActive) return(false);
dest = Adjacent_Cell(dest, nextface);
destcell = Coord_Cell(dest);
cando = Can_Enter_Cell(destcell);
}
if (!IsActive) return(false);
if (cando != MOVE_OK) {
/*
** If a temporary friendly object is blocking the path, then cause it to
** get out of the way.
*/
if (cando == MOVE_TEMP) {
Map[destcell].Incoming(0, true, true);
}
/*
** If a cloaked object is blocking, then shimmer the cell.
*/
if (cando == MOVE_CLOAK) {
Map[destcell].Shimmer();
}
Path[0] = FACING_NONE; // Path is blocked!
TrackNumber = -1;
dest = NULL;
if (cando == MOVE_DESTROYABLE) {
if (Map[destcell].Cell_Object()) {
if (!House->Is_Ally(Map[destcell].Cell_Object())) {
Override_Mission(MISSION_ATTACK, Map[destcell].Cell_Object()->As_Target(), TARGET_NONE);
}
} else {
if (Map[destcell].Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Map[destcell].Overlay).IsWall) {
Override_Mission(MISSION_ATTACK, ::As_Target(destcell), TARGET_NONE);
}
}
IsNewNavCom = false;
TrackIndex = 0;
return(true);
}
} else {
memcpy((char*)&Path[0], (char*)&Path[2], CONQUER_PATH_MAX-2);
Path[CONQUER_PATH_MAX-2] = FACING_NONE;
IsPlanningToLook = true;
}
} else {
memcpy((char*)&Path[0], (char*)&Path[1], CONQUER_PATH_MAX-1);
}
Path[CONQUER_PATH_MAX-1] = FACING_NONE;
}
}
IsNewNavCom = false;
TrackIndex = 0;
if (!Start_Driver(dest)) {
TrackNumber = -1;
Path[0] = FACING_NONE;
Set_Speed(0);
}
}
return(false);
}
/***********************************************************************************************
* DriveClass::AI -- Processes unit movement and rotation. *
* *
* This routine is used to process unit movement and rotation. It *
* functions autonomously from the script system. Thus, once a unit *
* is give rotation command or movement path, it will follow this *
* until specifically instructed to stop. The advantage of this *
* method is that it allows smooth movement of units, faster game *
* execution, and reduced script complexity (since actual movement *
* dynamics need not be controlled directly by the scripts). *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: This routine relies on the process control bits for the *
* specified unit (for speed reasons). Thus, only setting *
* movement, rotation, or path list will the unit perform *
* any physics. *
* *
* HISTORY: *
* 09/26/1993 JLB : Created. *
* 04/15/1994 JLB : Converted to member function. *
*=============================================================================================*/
void DriveClass::AI(void)
{
assert(IsActive);
FootClass::AI();
if (!IsActive || Height > 0) return;
/*
** Is this a unit that's been teleported using the chronosphere, and if so,
** has his timer expired such that he needs to teleport back?
*/
if (IsMoebius) {
#ifdef FIXIT_CSII // checked - ajw 9/28/98
if (What_Am_I() != RTTI_UNIT || ((UnitClass *)this)->Class->Type != UNIT_CHRONOTANK) {
#endif
if (MoebiusCountDown == 0) {
IsMoebius = false;
Teleport_To(MoebiusCell);
MoebiusCell = 0;
}
#ifdef FIXIT_CSII // checked - ajw 9/28/98
}
#endif
}
/*
** If the unit is following a track, then continue
** to do so -- mindlessly.
*/
if (TrackNumber != -1) {
/*
** Perform the movement accumulation.
*/
While_Moving();
if (!IsActive) return;
if (TrackNumber == -1 && (Target_Legal(NavCom) || Path[0] != FACING_NONE) && (What_Am_I() != RTTI_UNIT || !((UnitClass*)this)->IsDumping)) {
Start_Of_Move();
if (!IsActive) return;
While_Moving();
if (!IsActive) return;
}
} else {
/*
** For tracked units that are rotating in place, perform the rotation now.
*/
#ifdef TOFIX
if ((Class->Speed == SPEED_FLOAT || Class->Speed == SPEED_HOVER || Class->Speed == SPEED_TRACK || (Class->Speed == SPEED_WHEEL && !Special.IsThreePoint)) && PrimaryFacing.Is_Rotating()) {
if (PrimaryFacing.Rotation_Adjust(Class->ROT)) {
Mark(MARK_CHANGE);
}
#else
if (PrimaryFacing.Is_Rotating()) {
Mark(MARK_CHANGE_REDRAW);
if (PrimaryFacing.Rotation_Adjust(Techno_Type_Class()->ROT * House->GroundspeedBias)) {
Mark(MARK_CHANGE_REDRAW);
}
#endif
if (!IsRotating) {
Per_Cell_Process(PCP_ROTATION);
if (!IsActive) return;
}
} else {
/*
** The unit has no track to follow, but if there
** is a navigation target or a remaining path,
** then start on a new track.
*/
if ((Mission != MISSION_GUARD || Target_Legal(NavCom)) && Mission != MISSION_UNLOAD) {
if (Target_Legal(NavCom) || Path[0] != FACING_NONE) {
/*
** Double check to make sure that the movement destination is
** in a zone that this unit can travel to. If not, then abort
** the navigation target. Exception is to allow units to leave
** impassable cells regardless of zone checks.
*/
LandType land = LAND_NONE;
if (What_Am_I() == RTTI_INFANTRY || What_Am_I() == RTTI_UNIT) {
land = Map[Center_Coord()].Land_Type();
}
if (IsLocked && Mission != MISSION_ENTER && Target_Legal(NavCom) && !Is_In_Same_Zone(As_Cell(NavCom)) &&
land != LAND_ROCK && land != LAND_WATER && land != LAND_RIVER && !Team) {
Stop_Driver();
Assign_Destination(TARGET_NONE);
} else {
Start_Of_Move();
if (!IsActive) return;
While_Moving();
if (!IsActive) return;
}
} else {
Stop_Driver();
}
}
}
}
}
/***********************************************************************************************
* DriveClass::Fixup_Path -- Adds smooth start path to normal movement path. *
* *
* This routine modifies the path of the specified unit so that it *
* will not start out with a rotation. This is necessary for those *
* vehicles that have difficulty with rotating in place. Typically, *
* this includes wheeled vehicles. *
* *
* INPUT: unit -- Pointer to the unit to adjust. *
* *
* path -- Pointer to path structure. *
* *
* OUTPUT: none *
* *
* WARNINGS: Only units that require a fixup get modified. The *
* modification only occurs, if there is a legal path to *
* do so. *
* *
* HISTORY: *
* 04/03/1994 JLB : Created. *
* 04/06/1994 JLB : Uses path structure. *
* 04/10/1994 JLB : Diagonal smooth turn added. *
* 04/15/1994 JLB : Converted to member function. *
*=============================================================================================*/
void DriveClass::Fixup_Path(PathType * path)
{
assert(IsActive);
FacingType stage[6]={FACING_N,FACING_N,FACING_N,FACING_N,FACING_N,FACING_N}; // Prefix path elements.
int facediff; // The facing difference value (0..4 | 0..-4).
static FacingType _path[4][6] = {
{(FacingType)2,(FacingType)0,(FacingType)2,(FacingType)0,(FacingType)0,(FacingType)0},
{(FacingType)3,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)0,(FacingType)0},
{(FacingType)4,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)0,(FacingType)0},
{(FacingType)4,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)1,(FacingType)0}
};
static FacingType _dpath[4][6] = {
{(FacingType)0,(FacingType)0,(FacingType)0,(FacingType)0,(FacingType)0,(FacingType)0},
{(FacingType)3,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)0,(FacingType)0},
{(FacingType)4,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)1,(FacingType)0},
{(FacingType)5,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)1,(FacingType)0}
};
int index;
int counter; // Path addition
FacingType * ptr; // Path list pointer.
FacingType * ptr2; // Copy of new path list pointer.
FacingType nextpath; // Next path value.
CELL cell; // Working cell value.
bool ok;
/*
** Verify that the unit is valid and there is a path problem to resolve.
*/
if (!path || path->Command[0] == FACING_NONE) {
return;
}
/*
** Only wheeled vehicles need a path fixup -- to avoid 3 point turns.
*/
#ifdef TOFIX
if (!Special.IsThreePoint || Class->Speed != SPEED_WHEEL) {
#else
if (What_Am_I() == RTTI_UNIT || What_Am_I() == RTTI_VESSEL) {
// if (What_Am_I() == RTTI_UNIT) {
#endif
return;
}
/*
** If the original path starts in the same direction as the unit, then
** there is no problem to resolve -- abort.
*/
facediff = PrimaryFacing.Difference((DirType)(path->Command[0]<<5)) >> 5;
if (!facediff) return;
if (Dir_Facing(PrimaryFacing) & FACING_NE) {
ptr = &_dpath[(FacingType)ABS((int)facediff)-FACING_NE][1]; // Pointer to path adjust list.
counter = (int)_dpath[(FacingType)ABS((int)facediff)-FACING_NE][0]; // Number of path adjusts.
} else {
ptr = &_path[(FacingType)ABS((int)facediff)-FACING_NE][1]; // Pointer to path adjust list.
counter = (int)_path[(FacingType)ABS((int)facediff)-FACING_NE][0]; // Number of path adjusts.
}
ptr2 = ptr;
ok = true; // Presume adjustment is all ok.
cell = Coord_Cell(Coord); // Starting cell.
nextpath = Dir_Facing(PrimaryFacing); // Starting path.
for (index = 0; index < counter; index++) {
/*
** Determine next path element and add it to the
** working path list.
*/
if (facediff > 0) {
nextpath = nextpath + *ptr++;
} else {
nextpath = nextpath - *ptr++;
}
stage[index] = nextpath;
cell = Adjacent_Cell(cell, nextpath);
//cell = Coord_Cell(Adjacent_Cell(Cell_Coord(cell), nextpath));
/*
** If it can't enter this cell, then abort the path
** building operation without adjusting the unit's
** path.
*/
if (Can_Enter_Cell(cell, nextpath) != MOVE_OK) {
ok = false;
break;
}
}
/*
** If veering to the left was not successful, then try veering
** to the right. This only makes sense if the vehicle is trying
** to turn 180 degrees.
*/
if (!ok && ABS(facediff) == 4) {
ptr = ptr2; // Pointer to path adjust list.
facediff = -facediff;
ok = true; // Presume adjustment is all ok.
cell = Coord_Cell(Coord); // Starting cell.
nextpath = Dir_Facing(PrimaryFacing); // Starting path.
for (index = 0; index < counter; index++) {
/*
** Determine next path element and add it to the
** working path list.
*/
if (facediff > 0) {
nextpath = nextpath + *ptr++;
} else {
nextpath = nextpath - *ptr++;
}
stage[index] = nextpath;
cell = Coord_Cell(Adjacent_Cell(Cell_Coord(cell), nextpath));
/*
** If it can't enter this cell, then abort the path
** building operation without adjusting the unit's
** path.
*/
if (Can_Enter_Cell(cell, nextpath) != MOVE_OK) {
ok = false;
break;
}
}
}
/*
** If a legal path addition was created, then install it in place
** of the first path value. The initial path entry is to be replaced
** with a sequence of path entries that create smooth turning.
*/
if (ok) {
if (path->Length <= 1) {
memmove((char *)&stage[0], (char*)path->Command, max(counter, 1));
path->Length = counter;
} else {
/*
** Optimize the transition path step from the smooth turn
** first part as it joins with the rest of the normal
** path. The normal prefix path steps are NOT to be optimized.
*/
if (counter) {
counter--;
path->Command[0] = stage[counter];
Optimize_Moves(path, MOVE_OK);
}
/*
** If there is more than one prefix path element, then
** insert the rest now.
*/
if (counter) {
memmove((char*)&path->Command[0], (char*)&path->Command[counter], 40-counter);
memmove((char*)&stage[0], (char*)&path->Command[0], counter);
path->Length += counter;
}
}
path->Command[path->Length] = FACING_NONE;
}
}
/***********************************************************************************************
* DriveClass::Lay_Track -- Handles track laying logic for the unit. *
* *
* This routine handles the track laying for the unit. This entails examining the unit's *
* current location as well as the direction and whether this unit is allowed to lay *
* tracks in the first place. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 05/28/1994 JLB : Created. *
*=============================================================================================*/
void DriveClass::Lay_Track(void)
{
assert(IsActive);
#ifdef NEVER
static IconCommandType * _trackdirs[8] = {
TrackN_S,
TrackNE_SW,
TrackE_W,
TrackNW_SE,
TrackN_S,
TrackNE_SW,
TrackE_W,
TrackNW_SE
};
if (!(ClassF & CLASSF_TRACKS)) return;
Icon_Install(Coord_Cell(Coord), _trackdirs[Facing_To_8(BodyFacing)]);
#endif
}
/***********************************************************************************************
* DriveClass::Mark_Track -- Marks the midpoint of the track as occupied. *
* *
* This routine will ensure that the midpoint (if any) of the track that the unit is *
* following, will be marked according to the mark type specified. *
* *
* INPUT: headto -- The head to coordinate. *
* *
* type -- The type of marking to perform. *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 07/30/1995 JLB : Created. *
*=============================================================================================*/
void DriveClass::Mark_Track(COORDINATE headto, MarkType type)
{
assert(IsActive);
int value;
if (type == MARK_UP) {
value = false;
} else {
value = true;
}
if (headto) {
if (!IsOnShortTrack && TrackNumber != -1) {
/*
** If we have not passed the per cell process point we need
** to deal with it.
*/
int tracknum = TrackControl[TrackNumber].Track;
if (tracknum) {
TrackType const * ptr = RawTracks[tracknum - 1].Track;
int cellidx = RawTracks[tracknum - 1].Cell;
if (cellidx > -1) {
DirType dir = ptr[cellidx].Facing;
if (TrackIndex < cellidx && cellidx != -1) {
COORDINATE offset = Smooth_Turn(ptr[cellidx].Offset, dir);
Map[offset].Flag.Occupy.Vehicle = value;
}
}
}
}
Map[headto].Flag.Occupy.Vehicle = value;
}
}
/***********************************************************************************************
* DriveClass::Ok_To_Move -- Checks to see if this object can begin moving. *
* *
* This routine is used to verify that this object is allowed to move. Some objects can *
* be temporarily occupied and thus cannot move until the situation permits. *
* *
* INPUT: direction -- The direction that movement would be desired. *
* *
* OUTPUT: Can the unit move in the direction specified? *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 07/29/1995 JLB : Created. *
*=============================================================================================*/
bool DriveClass::Ok_To_Move(DirType ) const
{
assert(IsActive);
return true;
}
/***************************************************************************
** Smooth turn track tables. These are coordinate offsets from the center
** of the destination cell. These are the raw tracks that are modified
** by negating the X and Y portions as necessary. Also for reverse travelling
** direction, the track list can be processed backward.
**
** Track 1 = N
** Track 2 = NE
** Track 3 = N->NE 45 deg (double path consumption)
** Track 4 = N->E 90 deg (double path consumption)
** Track 5 = NE->SE 90 deg (double path consumption)
** Track 6 = NE->N 45 deg (double path consumption)
** Track 7 = N->NE (facing change only)
** Track 8 = NE->E (facing change only)
** Track 9 = N->E (facing change only)
** Track 10= NE->SE (facing change only)
** Track 11= back up into refinery
** Track 12= drive out of refinery
*/
//#pragma warn -ias
DriveClass::TrackType const DriveClass::Track1[24] = {
{0x00F50000L,(DirType)0},
{0x00EA0000L,(DirType)0},
{0x00DF0000L,(DirType)0},
{0x00D40000L,(DirType)0},
{0x00C90000L,(DirType)0},
{0x00BE0000L,(DirType)0},
{0x00B30000L,(DirType)0},
{0x00A80000L,(DirType)0},
{0x009D0000L,(DirType)0},
{0x00920000L,(DirType)0},
{0x00870000L,(DirType)0},
{0x007C0000L,(DirType)0}, // Track jump check here.
{0x00710000L,(DirType)0},
{0x00660000L,(DirType)0},
{0x005B0000L,(DirType)0},
{0x00500000L,(DirType)0},
{0x00450000L,(DirType)0},
{0x003A0000L,(DirType)0},
{0x002F0000L,(DirType)0},
{0x00240000L,(DirType)0},
{0x00190000L,(DirType)0},
{0x000E0000L,(DirType)0},
{0x00030000L,(DirType)0},
{0x00000000L,(DirType)0}
};
DriveClass::TrackType const DriveClass::Track2[] = {
{0x00F8FF08L,(DirType)32},
{0x00F0FF10L,(DirType)32},
{0x00E8FF18L,(DirType)32},
{0x00E0FF20L,(DirType)32},
{0x00D8FF28L,(DirType)32},
{0x00D0FF30L,(DirType)32},
{0x00C8FF38L,(DirType)32},
{0x00C0FF40L,(DirType)32},
{0x00B8FF48L,(DirType)32},
{0x00B0FF50L,(DirType)32},
{0x00A8FF58L,(DirType)32},
{0x00A0FF60L,(DirType)32},
{0x0098FF68L,(DirType)32},
{0x0090FF70L,(DirType)32},
{0x0088FF78L,(DirType)32},
{0x0080FF80L,(DirType)32}, // Track jump check here.
{0x0078FF88L,(DirType)32},
{0x0070FF90L,(DirType)32},
{0x0068FF98L,(DirType)32},
{0x0060FFA0L,(DirType)32},
{0x0058FFA8L,(DirType)32},
{0x0050FFB0L,(DirType)32},
{0x0048FFB8L,(DirType)32},
{0x0040FFC0L,(DirType)32},
{0x0038FFC8L,(DirType)32},
{0x0030FFD0L,(DirType)32},
{0x0028FFD8L,(DirType)32},
{0x0020FFE0L,(DirType)32},
{0x0018FFE8L,(DirType)32},
{0x0010FFF0L,(DirType)32},
{0x0008FFF8L,(DirType)32},
{0x00000000L,(DirType)32}
};
DriveClass::TrackType const DriveClass::Track3[] = {
{0x01F5FF00L,(DirType)0},
{0x01EAFF00L,(DirType)0},
{0x01DFFF00L,(DirType)0},
{0x01D4FF00L,(DirType)0},
{0x01C9FF00L,(DirType)0},
{0x01BEFF00L,(DirType)0},
{0x01B3FF00L,(DirType)0},
{0x01A8FF00L,(DirType)0},
{0x019DFF00L,(DirType)0},
{0x0192FF00L,(DirType)0},
{0x0187FF00L,(DirType)0},
{0x0180FF00L,(DirType)0},
{0x0175FF00L,(DirType)0}, // Jump entry point here.
{0x016BFF00L,(DirType)0},
{0x0160FF02L,(DirType)1},
{0x0155FF04L,(DirType)3},
{0x014CFF06L,(DirType)4},
{0x0141FF08L,(DirType)5},
{0x0137FF0BL,(DirType)7},
{0x012EFF0FL,(DirType)8},
{0x0124FF13L,(DirType)9},
{0x011AFF17L,(DirType)11},
{0x0110FF1BL,(DirType)12},
{0x0107FF1FL,(DirType)13}, // Center cell processing here.
{0x00FCFF24L,(DirType)15},
{0x00F3FF28L,(DirType)16},
{0x00ECFF2CL,(DirType)17},
{0x00E0FF32L,(DirType)19},
{0x00D7FF36L,(DirType)20},
{0x00CFFF3DL,(DirType)21},
{0x00C6FF42L,(DirType)23},
{0x00BAFF49L,(DirType)24},
{0x00B0FF4DL,(DirType)25},
{0x00A8FF58L,(DirType)27},
{0x00A0FF60L,(DirType)28},
{0x0098FF68L,(DirType)29},
{0x0090FF70L,(DirType)31},
{0x0088FF78L,(DirType)32},
{0x0080FF80L,(DirType)32}, // Track jump check here.
{0x0078FF88L,(DirType)32},
{0x0070FF90L,(DirType)32},
{0x0068FF98L,(DirType)32},
{0x0060FFA0L,(DirType)32},
{0x0058FFA8L,(DirType)32},
{0x0050FFB0L,(DirType)32},
{0x0048FFB8L,(DirType)32},
{0x0040FFC0L,(DirType)32},
{0x0038FFC8L,(DirType)32},
{0x0030FFD0L,(DirType)32},
{0x0028FFD8L,(DirType)32},
{0x0020FFE0L,(DirType)32},
{0x0018FFE8L,(DirType)32},
{0x0010FFF0L,(DirType)32},
{0x0008FFF8L,(DirType)32},
{0x00000000L,(DirType)32}
};
DriveClass::TrackType const DriveClass::Track4[] = {
{0x00F5FF00L,(DirType)0},
{0x00EBFF00L,(DirType)0},
{0x00E0FF00L,(DirType)0},
{0x00D5FF00L,(DirType)0},
{0x00CBFF01L,(DirType)0},
{0x00C0FF03L,(DirType)0},
{0x00B5FF05L,(DirType)1},
{0x00ABFF07L,(DirType)1},
{0x00A0FF0AL,(DirType)2},
{0x0095FF0DL,(DirType)3},
{0x008BFF10L,(DirType)4},
{0x0080FF14L,(DirType)5}, // Track entry here.
{0x0075FF18L,(DirType)8},
{0x006DFF1CL,(DirType)12},
{0x0063FF22L,(DirType)16},
{0x005AFF25L,(DirType)20},
{0x0052FF2BL,(DirType)23},
{0x0048FF32L,(DirType)27},
{0x0040FF37L,(DirType)32},
{0x0038FF3DL,(DirType)36},
{0x0030FF46L,(DirType)39},
{0x002BFF4FL,(DirType)43},
{0x0024FF58L,(DirType)47},
{0x0020FF60L,(DirType)51},
{0x001BFF6DL,(DirType)54},
{0x0017FF79L,(DirType)57},
{0x0014FF82L,(DirType)60}, // Track jump here.
{0x0011FF8FL,(DirType)62},
{0x000DFF98L,(DirType)63},
{0x0009FFA2L,(DirType)64},
{0x0006FFACL,(DirType)64},
{0x0004FFB5L,(DirType)66},
{0x0003FFC0L,(DirType)64},
{0x0002FFCBL,(DirType)64},
{0x0001FFD5L,(DirType)64},
{0x0000FFE0L,(DirType)64},
{0x0000FFEBL,(DirType)64},
{0x0000FFF5L,(DirType)64},
{0x00000000L,(DirType)64}
};
DriveClass::TrackType const DriveClass::Track5[] = {
{0xFFF8FE08L,(DirType)32},
{0xFFF0FE10L,(DirType)32},
{0xFFE8FE18L,(DirType)32},
{0xFFE0FE20L,(DirType)32},
{0xFFD8FE28L,(DirType)32},
{0xFFD0FE30L,(DirType)32},
{0xFFC8FE38L,(DirType)32},
{0xFFC0FE40L,(DirType)32},
{0xFFB8FE48L,(DirType)32},
{0xFFB0FE50L,(DirType)32},
{0xFFA8FE58L,(DirType)32},
{0xFFA0FE60L,(DirType)32},
{0xFF98FE68L,(DirType)32},
{0xFF90FE70L,(DirType)32},
{0xFF88FE78L,(DirType)32},
{0xFF80FE80L,(DirType)32}, // Track entry here.
{0xFF78FE88L,(DirType)32},
{0xFF71FE90L,(DirType)32},
{0xFF6AFE97L,(DirType)32},
{0xFF62FE9FL,(DirType)32},
{0xFF5AFEA8L,(DirType)32},
{0xFF53FEB0L,(DirType)35},
{0xFF4BFEB7L,(DirType)38},
{0xFF44FEBEL,(DirType)41},
{0xFF3EFEC4L,(DirType)44},
{0xFF39FECEL,(DirType)47},
{0xFF34FED8L,(DirType)50},
{0xFF30FEE0L,(DirType)53},
{0xFF2DFEEBL,(DirType)56},
{0xFF2CFEF5L,(DirType)59},
{0xFF2BFF00L,(DirType)62},
{0xFF2CFF0BL,(DirType)66},
{0xFF2DFF15L,(DirType)69},
{0xFF30FF1FL,(DirType)72},
{0xFF34FF28L,(DirType)75},
{0xFF39FF30L,(DirType)78},
{0xFF3EFF3AL,(DirType)81},
{0xFF44FF44L,(DirType)84},
{0xFF4BFF4BL,(DirType)87},
{0xFF53FF50L,(DirType)90},
{0xFF5AFF58L,(DirType)93},
{0xFF62FF60L,(DirType)96},
{0xFF6AFF68L,(DirType)96},
{0xFF71FF70L,(DirType)96},
{0xFF78FF78L,(DirType)96},
{0xFF80FF80L,(DirType)96}, // Track jump check here.
{0xFF88FF88L,(DirType)96},
{0xFF90FF90L,(DirType)96},
{0xFF98FF98L,(DirType)96},
{0xFFA0FFA0L,(DirType)96},
{0xFFA8FFA8L,(DirType)96},
{0xFFB0FFB0L,(DirType)96},
{0xFFB8FFB8L,(DirType)96},
{0xFFC0FFC0L,(DirType)96},
{0xFFC8FFC8L,(DirType)96},
{0xFFD0FFD0L,(DirType)96},
{0xFFD8FFD8L,(DirType)96},
{0xFFE0FFE0L,(DirType)96},
{0xFFE8FFE8L,(DirType)96},
{0xFFF0FFF0L,(DirType)96},
{0xFFF8FFF8L,(DirType)96},
{0x00000000L,(DirType)96}
};
DriveClass::TrackType const DriveClass::Track6[] = {
{0x0100FE00L,(DirType)32},
{0x00F8FE08L,(DirType)32},
{0x00F0FE10L,(DirType)32},
{0x00E8FE18L,(DirType)32},
{0x00E0FE20L,(DirType)32},
{0x00D8FE28L,(DirType)32},
{0x00D0FE30L,(DirType)32},
{0x00C8FE38L,(DirType)32},
{0x00C0FE40L,(DirType)32},
{0x00B8FE48L,(DirType)32},
{0x00B0FE50L,(DirType)32},
{0x00A8FE58L,(DirType)32},
{0x00A0FE60L,(DirType)32},
{0x0098FE68L,(DirType)32},
{0x0090FE70L,(DirType)32},
{0x0088FE78L,(DirType)32},
{0x0080FE80L,(DirType)32}, // Jump entry point here.
{0x0078FE88L,(DirType)32},
{0x0070FE90L,(DirType)32},
{0x0068FE98L,(DirType)32},
{0x0060FEA0L,(DirType)32},
{0x0058FEA8L,(DirType)32},
{0x0055FEAEL,(DirType)32},
{0x004EFEB8L,(DirType)35},
{0x0048FEC0L,(DirType)37},
{0x0042FEC9L,(DirType)40},
{0x003BFED2L,(DirType)43},
{0x0037FEDAL,(DirType)45},
{0x0032FEE3L,(DirType)48},
{0x002BFEEBL,(DirType)51},
{0x0026FEF5L,(DirType)53},
{0x0022FEFEL,(DirType)56},
{0x001CFF08L,(DirType)59},
{0x0019FF12L,(DirType)61},
{0x0015FF1BL,(DirType)64},
{0x0011FF26L,(DirType)64},
{0x000EFF30L,(DirType)64},
{0x000BFF39L,(DirType)64},
{0x0009FF43L,(DirType)64},
{0x0007FF4EL,(DirType)64},
{0x0005FF57L,(DirType)64},
{0x0003FF62L,(DirType)64},
{0x0001FF6DL,(DirType)64},
{0x0000FF77L,(DirType)64},
{0x0000FF80L,(DirType)64}, // Track jump check here.
{0x0000FF8BL,(DirType)64},
{0x0000FF95L,(DirType)64},
{0x0000FFA0L,(DirType)64},
{0x0000FFABL,(DirType)64},
{0x0000FFB5L,(DirType)64},
{0x0000FFC0L,(DirType)64},
{0x0000FFCBL,(DirType)64},
{0x0000FFD5L,(DirType)64},
{0x0000FFE0L,(DirType)64},
{0x0000FFEBL,(DirType)64},
{0x0000FFF5L,(DirType)64},
{0x00000000L,(DirType)64}
};
DriveClass::TrackType const DriveClass::Track7[] = {
{0x0006FFFFL,(DirType)0},
{0x000CFFFEL,(DirType)4},
{0x0011FFFCL,(DirType)8},
{0x0018FFFAL,(DirType)12},
{0x001FFFF6L,(DirType)16},
{0x0024FFF3L,(DirType)19},
{0x002BFFF0L,(DirType)22},
{0x0030FFFDL,(DirType)23},
{0x0035FFEBL,(DirType)24},
{0x0038FFE8L,(DirType)25},
{0x003CFFE6L,(DirType)26},
{0x0040FFE3L,(DirType)27},
{0x0043FFE0L,(DirType)28},
{0x0046FFDDL,(DirType)29},
{0x0043FFDFL,(DirType)30},
{0x0040FFE1L,(DirType)30},
{0x003CFFE3L,(DirType)30},
{0x0038FFE5L,(DirType)30},
{0x0035FFE7L,(DirType)31},
{0x0030FFE9L,(DirType)31},
{0x002BFFEBL,(DirType)31},
{0x0024FFEDL,(DirType)31},
{0x001FFFF1L,(DirType)31},
{0x0018FFF4L,(DirType)32},
{0x0011FFF7L,(DirType)32},
{0x000CFFFAL,(DirType)32},
{0x0006FFFDL,(DirType)32},
{0x00000000L,(DirType)32}
};
DriveClass::TrackType const DriveClass::Track8[] = {
{0x0003FFFCL,(DirType)32},
{0x0006FFF7L,(DirType)36},
{0x000AFFF1L,(DirType)40},
{0x000CFFEBL,(DirType)44},
{0x000DFFE4L,(DirType)46},
{0x000EFFDCL,(DirType)48},
{0x000FFFD5L,(DirType)50},
{0x0010FFD0L,(DirType)52},
{0x0011FFC9L,(DirType)54},
{0x0012FFC2L,(DirType)56},
{0x0011FFC0L,(DirType)58},
{0x0010FFC2L,(DirType)60},
{0x000EFFC9L,(DirType)62},
{0x000CFFCFL,(DirType)64},
{0x000AFFD5L,(DirType)64},
{0x0008FFDAL,(DirType)64},
{0x0006FFE2L,(DirType)64},
{0x0004FFE9L,(DirType)64},
{0x0002FFEFL,(DirType)64},
{0x0001FFF5L,(DirType)64},
{0x0000FFF9L,(DirType)64},
{0x00000000L,(DirType)64}
};
DriveClass::TrackType const DriveClass::Track9[] = {
{0xFFF50002L,(DirType)0},
{0xFFEB0004L,(DirType)2},
{0xFFE00006L,(DirType)4},
{0xFFD50009L,(DirType)6},
{0xFFCE000CL,(DirType)9},
{0xFFC8000FL,(DirType)11},
{0xFFC00012L,(DirType)13},
{0xFFB80015L,(DirType)16},
{0xFFC00012L,(DirType)18},
{0xFFC8000EL,(DirType)20},
{0xFFCE000AL,(DirType)22},
{0xFFD50004L,(DirType)24},
{0xFFDE0000L,(DirType)26},
{0xFFE9FFF8L,(DirType)28},
{0xFFEEFFF2L,(DirType)30},
{0xFFF5FFEBL,(DirType)32},
{0xFFFDFFE1L,(DirType)34},
{0x0002FFD8L,(DirType)36},
{0x0007FFD2L,(DirType)39},
{0x000BFFCBL,(DirType)41},
{0x0010FFC5L,(DirType)43},
{0x0013FFBEL,(DirType)45},
{0x0015FFB7L,(DirType)48},
{0x0013FFBEL,(DirType)50},
{0x0011FFC5L,(DirType)52},
{0x000BFFCCL,(DirType)54},
{0x0008FFD4L,(DirType)56},
{0x0005FFDFL,(DirType)58},
{0x0003FFEBL,(DirType)62},
{0x0001FFF5L,(DirType)64},
{0x00000000L,(DirType)64}
};
DriveClass::TrackType const DriveClass::Track10[] = {
{0xFFF6000BL,(DirType)32},
{0xFFF00015L,(DirType)37},
{0xFFEB0020L,(DirType)42},
{0xFFE9002BL,(DirType)47},
{0xFFE50032L,(DirType)52},
{0xFFE30038L,(DirType)57},
{0xFFE00040L,(DirType)60},
{0xFFE20038L,(DirType)62},
{0xFFE40032L,(DirType)64},
{0xFFE5002AL,(DirType)68},
{0xFFE6001EL,(DirType)70},
{0xFFE70015L,(DirType)72},
{0xFFE8000BL,(DirType)74},
{0xFFE90000L,(DirType)76},
{0xFFE8FFF5L,(DirType)78},
{0xFFE7FFEBL,(DirType)80},
{0xFFE6FFE0L,(DirType)82},
{0xFFE5FFD5L,(DirType)84},
{0xFFE4FFCEL,(DirType)86},
{0xFFE2FFC5L,(DirType)88},
{0xFFE0FFC0L,(DirType)90},
{0xFFE3FFC5L,(DirType)92},
{0xFFE5FFCEL,(DirType)94},
{0xFFE9FFD5L,(DirType)95},
{0xFFEBFFE0L,(DirType)96},
{0xFFF0FFEBL,(DirType)96},
{0xFFF6FFF5L,(DirType)96},
{0x00000000L,(DirType)96}
};
DriveClass::TrackType const DriveClass::Track11[] = {
{0x01000000L,DIR_SW},
{0x00F30008L,DIR_SW},
{0x00E50010L,DIR_SW_X1},
{0x00D60018L,DIR_SW_X1},
{0x00C80020L,DIR_SW_X1},
{0x00B90028L,DIR_SW_X1},
{0x00AB0030L,DIR_SW_X2},
{0x009C0038L,DIR_SW_X2},
{0x008D0040L,DIR_SW_X2},
{0x007F0048L,DIR_SW_X2},
{0x00710050L,DIR_SW_X2},
{0x00640058L,DIR_SW_X2},
{0x00550060L,DIR_SW_X2},
{0x00000000L,DIR_SW_X2}
};
DriveClass::TrackType const DriveClass::Track12[] = {
{0xFF550060L,DIR_SW_X2},
{0xFF640058L,DIR_SW_X2},
{0xFF710050L,DIR_SW_X2},
{0xFF7F0048L,DIR_SW_X2},
{0xFF8D0040L,DIR_SW_X2},
{0xFF9C0038L,DIR_SW_X2},
{0xFFAB0030L,DIR_SW_X2},
{0xFFB90028L,DIR_SW_X1},
{0xFFC80020L,DIR_SW_X1},
{0xFFD60018L,DIR_SW_X1},
{0xFFE50010L,DIR_SW_X1},
{0xFFF30008L,DIR_SW},
{0x00000000L,DIR_SW}
};
#if(1)
/*
** Drive out of weapon's factory.
*/
DriveClass::TrackType const DriveClass::Track13[] = {
{XYP_COORD(0,-35),DIR_S},
{XYP_COORD(0,-34),DIR_S},
{XYP_COORD(0,-33),DIR_S},
{XYP_COORD(0,-32),DIR_S},
{XYP_COORD(0,-31),DIR_S},
{XYP_COORD(0,-30),DIR_S},
{XYP_COORD(0,-29),DIR_S},
{XYP_COORD(0,-28),DIR_S},
{XYP_COORD(0,-27),DIR_S},
{XYP_COORD(0,-26),DIR_S},
{XYP_COORD(0,-25),DIR_S},
{XYP_COORD(0,-24),DIR_S},
{XYP_COORD(0,-23),DIR_S},
{XYP_COORD(0,-22),DIR_S},
{XYP_COORD(0,-21),DIR_S},
{XYP_COORD(0,-20),DIR_S},
{XYP_COORD(0,-19),DIR_S},
{XYP_COORD(0,-18),DIR_S},
{XYP_COORD(0,-17),DIR_S},
{XYP_COORD(0,-16),DIR_S},
{XYP_COORD(0,-15),DIR_S},
{XYP_COORD(0,-14),DIR_S},
{XYP_COORD(0,-13),DIR_S},
{XYP_COORD(0,-12),DIR_S},
{XYP_COORD(0,-11),DIR_S},
{XYP_COORD(0,-10),DIR_S},
{XYP_COORD(0,-9),DIR_S},
{XYP_COORD(0,-8),DIR_S},
{XYP_COORD(0,-7),DIR_S},
{XYP_COORD(0,-6),DIR_S},
{XYP_COORD(0,-5),DIR_S},
{XYP_COORD(0,-4),DIR_S},
{XYP_COORD(0,-3),DIR_S},
{XYP_COORD(0,-2),DIR_S},
{XYP_COORD(0,-1),DIR_S},
{0x00000000L,DIR_S}
};
#else
/*
** Drive out of weapon's factory.
*/
DriveClass::TrackType const DriveClass::Track13[] = {
{XYP_COORD(10,-21),(DirType)(DIR_SW-10)},
{XYP_COORD(10,-21),(DirType)(DIR_SW-10)},
{XYP_COORD(10,-20),(DirType)(DIR_SW-10)},
{XYP_COORD(10,-20),(DirType)(DIR_SW-10)},
{XYP_COORD(9,-18),(DirType)(DIR_SW-10)},
{XYP_COORD(9,-18),(DirType)(DIR_SW-10)},
{XYP_COORD(9,-17),(DirType)(DIR_SW-10)},
{XYP_COORD(8,-16),(DirType)(DIR_SW-10)},
{XYP_COORD(8,-15),(DirType)(DIR_SW-10)},
{XYP_COORD(7,-14),(DirType)(DIR_SW-10)},
{XYP_COORD(7,-13),(DirType)(DIR_SW-10)},
{XYP_COORD(6,-12),(DirType)(DIR_SW-10)},
{XYP_COORD(6,-11),(DirType)(DIR_SW-10)},
{XYP_COORD(5,-10),(DirType)(DIR_SW-10)},
{XYP_COORD(5,-9),(DirType)(DIR_SW-10)},
{XYP_COORD(4,-8),(DirType)(DIR_SW-10)},
{XYP_COORD(4,-7),(DirType)(DIR_SW-10)},
{XYP_COORD(3,-6),(DirType)(DIR_SW-10)},
{XYP_COORD(3,-5),(DirType)(DIR_SW-9)},
{XYP_COORD(2,-4),(DirType)(DIR_SW-7)},
{XYP_COORD(2,-3),(DirType)(DIR_SW-5)},
{XYP_COORD(1,-2),(DirType)(DIR_SW-3)},
{XYP_COORD(1,-1),(DirType)(DIR_SW-1)},
{0x00000000L,DIR_SW}
};
#endif
/*
** There are a limited basic number of tracks that a vehicle can follow. These
** are they. Each track can be interpreted differently but this is controlled
** by the TrackControl structure elaborated elsewhere.
*/
DriveClass::RawTrackType const DriveClass::RawTracks[13] = {
{Track1, -1, 0, -1},
{Track2, -1, 0, -1},
{Track3, 37, 12, 22},
{Track4, 26, 11, 19},
{Track5, 45, 15, 31},
{Track6, 44, 16, 27},
{Track7, -1, 0, -1},
{Track8, -1, 0, -1},
{Track9, -1, 0, -1},
{Track10, -1, 0, -1},
{Track11, -1, 0, -1},
{Track12, -1, 0, -1},
{Track13, -1, 0, -1}
};
/***************************************************************************
** Smooth turning control table. Given two directions in a path list, this
** table determines which track to use and what modifying operations need
** be performed on the track data.
*/
DriveClass::TurnTrackType const DriveClass::TrackControl[67] = {
{1, 0, DIR_N, F_}, // 0-0
{3, 7, DIR_NE, F_D}, // 0-1 (raw chart)
{4, 9, DIR_E, F_D}, // 0-2 (raw chart)
{0, 0, DIR_SE, F_}, // 0-3 !
{0, 0, DIR_S, F_}, // 0-4 !
{0, 0, DIR_SW, F_}, // 0-5 !
{4, 9, DIR_W, (DriveClass::TrackControlType)(F_X|F_D)}, // 0-6
{3, 7, DIR_NW, (DriveClass::TrackControlType)(F_X|F_D)}, // 0-7
{6, 8, DIR_N, (DriveClass::TrackControlType)(F_T|F_X|F_Y|F_D)}, // 1-0
{2, 0, DIR_NE, F_}, // 1-1 (raw chart)
{6, 8, DIR_E, F_D}, // 1-2 (raw chart)
{5, 10, DIR_SE, F_D}, // 1-3 (raw chart)
{0, 0, DIR_S, F_}, // 1-4 !
{0, 0, DIR_SW, F_}, // 1-5 !
{0, 0, DIR_W, F_}, // 1-6 !
{5, 10, DIR_NW, (DriveClass::TrackControlType)(F_T|F_X|F_Y|F_D)}, // 1-7
{4, 9, DIR_N, (DriveClass::TrackControlType)(F_T|F_X|F_Y|F_D)}, // 2-0
{3, 7, DIR_NE, (DriveClass::TrackControlType)(F_T|F_X|F_Y|F_D)}, // 2-1
{1, 0, DIR_E, (DriveClass::TrackControlType)(F_T|F_X)}, // 2-2
{3, 7, DIR_SE, (DriveClass::TrackControlType)(F_T|F_X|F_D)}, // 2-3
{4, 9, DIR_S, (DriveClass::TrackControlType)(F_T|F_X|F_D)}, // 2-4
{0, 0, DIR_SW, F_}, // 2-5 !
{0, 0, DIR_W, F_}, // 2-6 !
{0, 0, DIR_NW, F_}, // 2-7 !
{0, 0, DIR_N, F_}, // 3-0 !
{5, 10, DIR_NE, (DriveClass::TrackControlType)(F_Y|F_D)}, // 3-1
{6, 8, DIR_E, (DriveClass::TrackControlType)(F_Y|F_D)}, // 3-2
{2, 0, DIR_SE, F_Y}, // 3-3
{6, 8, DIR_S, (DriveClass::TrackControlType)(F_T|F_X|F_D)}, // 3-4
{5, 10, DIR_SW, (DriveClass::TrackControlType)(F_T|F_X|F_D)}, // 3-5
{0, 0, DIR_W, F_}, // 3-6 !
{0, 0, DIR_NW, F_}, // 3-7 !
{0, 0, DIR_N, F_}, // 4-0 !
{0, 0, DIR_NE, F_}, // 4-1 !
{4, 9, DIR_E, (DriveClass::TrackControlType)(F_Y|F_D)}, // 4-2
{3, 7, DIR_SE, (DriveClass::TrackControlType)(F_Y|F_D)}, // 4-3
{1, 0, DIR_S, F_Y}, // 4-4
{3, 7, DIR_SW, (DriveClass::TrackControlType)(F_X|F_Y|F_D)}, // 4-5
{4, 9, DIR_W, (DriveClass::TrackControlType)(F_X|F_Y|F_D)}, // 4-6
{0, 0, DIR_NW, F_}, // 4-7 !
{0, 0, DIR_N, F_}, // 5-0 !
{0, 0, DIR_NE, F_}, // 5-1 !
{0, 0, DIR_E, F_}, // 5-2 !
{5, 10, DIR_SE, (DriveClass::TrackControlType)(F_T|F_D)}, // 5-3
{6, 8, DIR_S, (DriveClass::TrackControlType)(F_T|F_D)}, // 5-4
{2, 0, DIR_SW, F_T}, // 5-5
{6, 8, DIR_W, (DriveClass::TrackControlType)(F_X|F_Y|F_D)}, // 5-6
{5, 10, DIR_NW, (DriveClass::TrackControlType)(F_X|F_Y|F_D)}, // 5-7
{4, 9, DIR_N, (DriveClass::TrackControlType)(F_T|F_Y|F_D)}, // 6-0
{0, 0, DIR_NE, F_}, // 6-1 !
{0, 0, DIR_E, F_}, // 6-2 !
{0, 0, DIR_SE, F_}, // 6-3 !
{4, 9, DIR_S, (DriveClass::TrackControlType)(F_T|F_D)}, // 6-4
{3, 7, DIR_SW, (DriveClass::TrackControlType)(F_T|F_D)}, // 6-5
{1, 0, DIR_W, F_T}, // 6-6
{3, 7, DIR_NW, (DriveClass::TrackControlType)(F_T|F_Y|F_D)}, // 6-7
{6, 8, DIR_N, (DriveClass::TrackControlType)(F_T|F_Y|F_D)}, // 7-0
{5, 10, DIR_NE, (DriveClass::TrackControlType)(F_T|F_Y|F_D)}, // 7-1
{0, 0, DIR_E, F_}, // 7-2 !
{0, 0, DIR_SE, F_}, // 7-3 !
{0, 0, DIR_S, F_}, // 7-4 !
{5, 10, DIR_SW, (DriveClass::TrackControlType)(F_X|F_D)}, // 7-5
{6, 8, DIR_W, (DriveClass::TrackControlType)(F_X|F_D)}, // 7-6
{2, 0, DIR_NW, F_X}, // 7-7
{11, 11, DIR_SW, F_}, // Backup harvester into refinery.
{12, 12, DIR_SW_X2, F_}, // Drive back into refinery.
{13, 13, DIR_SW, F_} // Drive out of weapons factory.
};