//
// 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/UNIT.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 : UNIT.CPP                                                     *
 *                                                                                             *
 *                   Programmer : Joe L. Bostic                                                *
 *                                                                                             *
 *                   Start Date : September 10, 1993                                           *
 *                                                                                             *
 *                  Last Update : November 3, 1996 [JLB]                                       *
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 *   Recoil_Adjust -- Adjust pixel values in direction specified.                              *
 *   UnitClass::AI -- AI processing for the unit.                                              *
 *   UnitClass::APC_Close_Door -- Closes an APC door.                                          *
 *   UnitClass::APC_Open_Door -- Opens an APC door.                                            *
 *   UnitClass::Active_Click_With -- Intercepts the active click to see if deployment is possib*
 *   UnitClass::Active_Click_With -- Performs specified action on specified cell.              *
 *   UnitClass::Approach_Target -- Handles approaching the target in order to attack it.       *
 *   UnitClass::Assign_Destination -- Assign a destination to a unit.                          *
 *   UnitClass::Blocking_Object -- Determines how a object blocks a unit                       *
 *   UnitClass::Can_Enter_Cell -- Determines cell entry legality.                              *
 *   UnitClass::Can_Fire -- Determines if turret can fire upon target.                         *
 *   UnitClass::Click_With -- Handles player map clicking while this unit is selected.         *
 *   UnitClass::Credit_Load -- Fetch the full credit value of cargo carried.                   *
 *   UnitClass::Crew_Type -- Fetches the kind of crew that this object produces.               *
 *   UnitClass::Debug_Dump -- Displays the status of the unit to the mono monitor.             *
 *   UnitClass::Desired_Load_Dir -- Determines the best cell and facing for loading.           *
 *   UnitClass::Draw_It -- Draws a unit object.                                                *
 *   UnitClass::Edge_Of_World_AI -- Check for falling off the edge of the world.               *
 *   UnitClass::Enter_Idle_Mode -- Unit enters idle mode state.                                *
 *   UnitClass::Fire_Direction -- Determines the direction of firing.                          *
 *   UnitClass::Firing_AI -- Handle firing logic for this unit.                                *
 *   UnitClass::Flag_Attach -- Attaches a house flag to this unit.                             *
 *   UnitClass::Flag_Remove -- Removes the house flag from this unit.                          *
 *   UnitClass::Goto_Clear_Spot -- Finds a clear spot to deploy.                               *
 *   UnitClass::Goto_Tiberium -- Search for and head toward nearest available Tiberium patch.  *
 *   UnitClass::Greatest_Threat -- Fetches the greatest threat for this unit.                  *
 *   UnitClass::Harvesting -- Harvests tiberium at the current location.                       *
 *   UnitClass::Init -- Clears all units for scenario preparation.                             *
 *   UnitClass::Limbo -- Limbo this unit.                                                      *
 *   UnitClass::Mission_Guard -- Special guard mission override processor.                     *
 *   UnitClass::Mission_Guard_Area -- Guard area logic for units.                              *
 *   UnitClass::Mission_Harvest -- Handles the harvesting process used by harvesters.          *
 *   UnitClass::Mission_Hunt -- This is the AI process for aggressive enemy units.             *
 *   UnitClass::Mission_Move -- Handles special move mission overrides.                        *
 *   UnitClass::Mission_Repair -- Handles finding and proceeding on a repair mission.          *
 *   UnitClass::Mission_Unload -- Handles unloading cargo.                                     *
 *   UnitClass::Offload_Tiberium_Bail -- Offloads one Tiberium quantum from the object.        *
 *   UnitClass::Ok_To_Move -- Queries whether the vehicle can move.                            *
 *   UnitClass::Overlap_List -- Determines overlap list for units.                             *
 *   UnitClass::Overrun_Square -- Handles vehicle overrun of a cell.                           *
 *   UnitClass::Per_Cell_Process -- Performs operations necessary on a per cell basis.         *
 *   UnitClass::Pip_Count -- Fetches the number of pips to display on unit.                    *
 *   UnitClass::Random_Animate -- Handles random idle animation for the unit.                  *
 *   UnitClass::Read_INI -- Reads units from scenario INI file.                                *
 *   UnitClass::Receive_Message -- Handles receiving a radio message.                          *
 *   UnitClass::Reload_AI -- Perform reload logic for this unit.                               *
 *   UnitClass::Rotation_AI -- Process any turret or body rotation.                            *
 *   UnitClass::Scatter -- Causes the unit to scatter to a nearby location.                    *
 *   UnitClass::Set_Speed -- Initiate unit movement physics.                                   *
 *   UnitClass::Shape_Number -- Fetch the shape number to use for this unit.                   *
 *   UnitClass::Should_Crush_It -- Determines if this unit should crush an object.             *
 *   UnitClass::Sort_Y -- Give Y coordinate sort value for unit.                               *
 *   UnitClass::Start_Driver -- Starts driving and reserves destination cell.                  *
 *   UnitClass::Take_Damage -- Inflicts damage points on a unit.                               *
 *   UnitClass::Tiberium_Check -- Search for and head toward nearest available Tiberium patch. *
 *   UnitClass::Tiberium_Load -- Determine the Tiberium load as a percentage.                  *
 *   UnitClass::Try_To_Deploy -- The unit attempts to "deploy" at current location.            *
 *   UnitClass::UnitClass -- Constructor for units.                                            *
 *   UnitClass::Unlimbo -- Removes unit from stasis.                                           *
 *   UnitClass::What_Action -- Determines action to perform on specified cell.                 *
 *   UnitClass::What_Action -- Determines what action would occur if clicked on object.        *
 *   UnitClass::Write_INI -- Store the units to the INI database.                              *
 *   UnitClass::delete -- Deletion operator for units.                                         *
 *   UnitClass::new -- Allocate a unit slot and adjust access arrays.                          *
 *   UnitClass::~UnitClass -- Destructor for unit objects.                                     *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

#include	"function.h"
#include "COORDA.h"


extern void Logic_Switch_Player_Context(ObjectClass *object);
extern void Logic_Switch_Player_Context(HouseClass *object);
extern void On_Special_Weapon_Targetting(const HouseClass* player_ptr, SpecialWeaponType weapon_type);
extern bool Is_Legacy_Render_Enabled(void);


static int _GapShroudXTable[]={
		-1, 0, 1,
	-2,-1, 0, 1, 2,
	-2,-1, 0, 1, 2,
	-2,-1, 0, 1, 2,
	-2,-1, 0, 1, 2,
	-2,-1, 0, 1, 2,
		-1, 0, 1
};
static int _GapShroudYTable[]={
		-3,-3,-3,
	-2,-2,-2,-2,-2,
	-1,-1,-1,-1,-1,
	 0, 0, 0, 0, 0,
	 1, 1, 1, 1, 1,
	 2, 2, 2, 2, 2,
		 3, 3, 3
};

/***********************************************************************************************
 * Recoil_Adjust -- Adjust pixel values in direction specified.                                *
 *                                                                                             *
 *    This is a helper routine that modifies the pixel coordinates provided according to the   *
 *    direction specified. The effect is the simulate recoil effects by moving an object 'back'*
 *    one pixel. Since the pixels moved depend on facing, this routine handles the pixel       *
 *    adjustment quickly.                                                                      *
 *                                                                                             *
 * INPUT:   dir   -- The direction to base the recoil on.                                      *
 *                                                                                             *
 *          x,y   -- References to the pixel coordinates that will be adjusted.                *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   05/08/1995 JLB : Created.                                                                 *
 *=============================================================================================*/
void Recoil_Adjust(DirType dir, int &x, int &y)
{
	static struct {
		signed char X,Y;
	} _adjust[32] = {
		{0,	1},	// N
		{0,	1},
		{0,	1},
		{-1,	1},
		{-1,	1},	// NE
		{-1,	1},
		{-1,	0},
		{-1,	0},
		{-1,	0},	// E
		{-1,	0},
		{-1,	-1},
		{-1,	-1},
		{-1,	-1},	// SE
		{-1,	-1},
		{-1,	-1},
		{0,	-1},
		{0,	-1},	// S
		{0,	-1},
		{0,	-1},
		{1,	-1},
		{1,	-1},	// SW
		{1,	-1},
		{1,	0},
		{1,	0},
		{1,	0},	// W
		{1,	0},
		{1,	1},
		{1,	1},
		{1,	1},	// NW
		{1,	1},
		{0,	1},
		{0,	1}
	};

	int index = Dir_To_32(dir);
	x += _adjust[index].X;
	y += _adjust[index].Y;
}


/***********************************************************************************************
 * UnitClass::new -- Allocate a unit slot and adjust access arrays.                            *
 *                                                                                             *
 *    This routine will allocate a unit from the available unit pool and                       *
 *    fixup all the access lists to match. It will allocate a unit slot                        *
 *    from within the range allowed for the specified unit type. If no                         *
 *    slot was found, then it will fail.                                                       *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  Returns with a pointer to the allocated unit.                                      *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   04/11/1994 JLB : Created.                                                                 *
 *   04/21/1994 JLB : Converted to operator new.                                               *
 *=============================================================================================*/
void * UnitClass::operator new(size_t)
{
	void * ptr = Units.Alloc();
	if (ptr != NULL) {
		((UnitClass *)ptr)->Set_Active();
	}
	return(ptr);
}


/***********************************************************************************************
 * UnitClass::delete -- Deletion operator for units.                                           *
 *                                                                                             *
 *    This removes the unit from the local allocation system. Since this                       *
 *    is a fixed block of memory, not much has to be done to delete the                        *
 *    unit. Merely marking it as inactive is enough.                                           *
 *                                                                                             *
 * INPUT:   ptr   -- Pointer to the unit to delete.                                            *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   04/21/1994 JLB : Created.                                                                 *
 *=============================================================================================*/
void UnitClass::operator delete(void * ptr)
{
	if (ptr != NULL) {
		((UnitClass *)ptr)->IsActive = false;
	}
	Units.Free((UnitClass *)ptr);
}


/***********************************************************************************************
 * UnitClass::~UnitClass -- Destructor for unit objects.                                       *
 *                                                                                             *
 *    This destructor will lower the unit count for the owning house as well as inform any     *
 *    other units in communication, that this unit is about to leave reality.                  *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/15/1994 JLB : Created.                                                                 *
 *=============================================================================================*/
UnitClass::~UnitClass(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;
}


/***********************************************************************************************
 * UnitClass::UnitClass -- Constructor for units.                                              *
 *                                                                                             *
 *    This constructor for units will initialize the unit into the game                        *
 *    system. It will be placed in all necessary tracking lists. The initial condition will    *
 *    be in a state of limbo.                                                                  *
 *                                                                                             *
 * INPUT:   classid  -- The type of unit to create.                                            *
 *                                                                                             *
 *          house -- The house owner of this unit.                                             *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   04/21/1994 JLB : Created.                                                                 *
 *=============================================================================================*/
UnitClass::UnitClass(UnitType classid, HousesType house) :
	DriveClass(RTTI_UNIT, Units.ID(this), house),
	Class(UnitTypes.Ptr((int)classid)),
	Flagged(HOUSE_NONE),
	IsDumping(false),
	Gems(0),
	Gold(0),
	Tiberium(0),
	IsToScatter(false),
	ShroudBits(0xFFFFFFFFUL),
	ShroudCenter(0),
	Reload(0),
	SecondaryFacing(PrimaryFacing),
	TiberiumUnloadRefinery(TARGET_NONE)
{
	Reload = 0;
	House->Tracking_Add(this);
	Ammo = Class->MaxAmmo;
	IsCloakable = Class->IsCloakable;
	if (Class->IsAnimating) Set_Rate(Options.Normalize_Delay(3));

	/*
	**	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;

	/*
	** Keep count of the number of units created.
	*/
//	if (Session.Type == GAME_INTERNET) {
//		House->UnitTotals->Increment_Unit_Total((int)classid);
//	}
}


#ifdef CHEAT_KEYS
/***********************************************************************************************
 * UnitClass::Debug_Dump -- Displays the status of the unit to the mono monitor.               *
 *                                                                                             *
 *    This displays the current status of the unit class to the mono monitor. By this display  *
 *    bugs may be tracked down or prevented.                                                   *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   06/02/1994 JLB : Created.                                                                 *
 *=============================================================================================*/
void UnitClass::Debug_Dump(MonoClass * mono) const
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	mono->Set_Cursor(0, 0);
	mono->Print(Text_String(TXT_DEBUG_VEHICLE));
	mono->Set_Cursor(47, 5);mono->Printf("%02X:%02X", SecondaryFacing.Current(), SecondaryFacing.Desired());

	mono->Set_Cursor(1, 11);mono->Printf("%03", Gems);
	mono->Set_Cursor(7, 11);mono->Printf("%03", Gold);

	mono->Fill_Attrib(66, 13, 12, 1, IsDumping ? MonoClass::INVERSE : MonoClass::NORMAL);

	DriveClass::Debug_Dump(mono);
}
#endif


/***********************************************************************************************
 * UnitClass::Sort_Y -- Give Y coordinate sort value for unit.                                 *
 *                                                                                             *
 *    This routine is used by the rendering system in order to sort the                        *
 *    game objects in a back to front order. This is now the correct                           *
 *    overlap effect is achieved.                                                              *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  Returns with a coordinate value that can be used for sorting.                      *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   05/17/1994 JLB : Created.                                                                 *
 *=============================================================================================*/
COORDINATE UnitClass::Sort_Y(void) const
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	return(Coord_Add(Coord, 0x00800000L));
}


/***********************************************************************************************
 * UnitClass::AI -- AI processing for the unit.                                                *
 *                                                                                             *
 *    This routine will perform the AI processing necessary for the unit. These are non-       *
 *    graphic related operations.                                                              *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   05/31/1994 JLB : Created.                                                                 *
 *=============================================================================================*/
void UnitClass::AI(void)
{
	assert(Units.ID(this) == ID);
	assert(IsActive);
	/*
	**	Act on new orders if the unit is at a good position to do so.
	*/
	if (Height == 0 && !IsDumping && !IsDriving && Is_Door_Closed() /*&& Mission != MISSION_UNLOAD*/) {
//		if (MissionQueue == MISSION_NONE) Enter_Idle_Mode();
		Commence();
	}
	DriveClass::AI();
	if (!IsActive || Height > 0) {
		return;
	}

	/*
	**	Hack check to ensure that a harvester won't harvest if it is not harvesting.
	*/
	if (Mission != MISSION_HARVEST) {
		IsHarvesting = false;
	}

	/*
	**	Handle combat logic for this unit. It will determine if it has a target and
	**	if so, if conditions are favorable for firing. When conditions permit, the
	**	unit will fire upon its target.
	*/
	Firing_AI();
#ifdef FIXIT_CSII	//	checked - ajw 9/28/98
	if (!IsActive) {
		return;
	}
#endif
	/*
	**	Turret rotation processing. Handles rotating radar dish
	**	as well as conventional turrets if present. If no turret present, but
	**	it decides that the body should face its target, then body rotation
	**	would occur by this process as well.
	*/
	Rotation_AI();

	/*
	**	Scatter units off buildings in guard modes.
	*/
	if (!IsTethered && !IsFiring && !IsDriving && !IsRotating && (Mission == MISSION_GUARD || Mission == MISSION_GUARD_AREA) && MissionQueue == MISSION_NONE && Map[Coord].Cell_Building() != NULL) {
		Scatter(0, true, true);
	}

	/*
	**	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;
	}

	/*
	**	Units will reload every so often if they are under the burden of
	**	being required to reload between shots.
	*/
	Reload_AI();

	/*
	**	Transporters require special logic handled here since there isn't a MISSION_WAIT_FOR_PASSENGERS
	**	mission that they can follow. Passenger loading is merely a part of their normal operation.
	*/
	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) {
			APC_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 (!IsDumping && !IsDriving && Is_Door_Closed()/*&& Mission != MISSION_UNLOAD*/) {
		Commence();
	}

	/*
	**	A cloaked object that is carrying the flag will always shimmer.
	*/
	if (Cloak == CLOAKED && Flagged != HOUSE_NONE) {
		Do_Shimmer();
	}

	/*
	**	Mobile gap generators regenerate their gap every so often (just in case).
	*/
	if (Class->IsGapper && !IsDriving && (Frame % TICKS_PER_SECOND) == 0) {
		Shroud_Regen();
	}
}


/***********************************************************************************************
 * UnitClass::Rotation_AI -- Process any turret or body rotation.                              *
 *                                                                                             *
 *    This routine will handle the rotation logic for the unit's turret (if it has one) as     *
 *    well as its normal body shape.                                                           *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   07/30/1996 JLB : Created.                                                                 *
 *=============================================================================================*/
void UnitClass::Rotation_AI(void)
{
	if (Target_Legal(TarCom) && !IsRotating) {
		DirType dir = Direction(TarCom);

		if (Class->IsTurretEquipped) {
			SecondaryFacing.Set_Desired(dir);
		} else {

			/*
			**	Non turret equipped vehicles will rotate their body to face the target only
			**	if the vehicle isn't currently moving or facing the correct direction. This
			**	applies only to tracked vehicles. Wheeled vehicles never rotate to face the
			**	target, since they aren't maneuverable enough.
			*/
			if ((Class->Speed == SPEED_TRACK /* || *this == UNIT_BIKE */ ) && !Target_Legal(NavCom) && !IsDriving && PrimaryFacing.Difference(dir)) {
				PrimaryFacing.Set_Desired(dir);
			}
		}
	}

	if (Class->IsRadarEquipped) {
		Mark(MARK_CHANGE_REDRAW);
		SecondaryFacing.Set((DirType)(SecondaryFacing.Current() + 8));
		Mark(MARK_CHANGE_REDRAW);
	} else {

		IsRotating = false;
		if (Class->IsTurretEquipped) {
			if (IsTurretLockedDown) {
				SecondaryFacing.Set_Desired(PrimaryFacing.Current());
			}

			if (SecondaryFacing.Is_Rotating()) {
				Mark(MARK_CHANGE_REDRAW);
				if (SecondaryFacing.Rotation_Adjust(Class->ROT+1)) {
					Mark(MARK_CHANGE_REDRAW);
				}

				/*
				**	If no further rotation is necessary, flag that the rotation
				**	has stopped.
				*/
				if (!Class->IsRadarEquipped) {
					IsRotating = SecondaryFacing.Is_Rotating();
				}
			} else {
				if (!IsTurretLockedDown && !Target_Legal(TarCom)) {
					if (!Target_Legal(NavCom)) {
						SecondaryFacing.Set_Desired(PrimaryFacing.Current());
					} else {
						SecondaryFacing.Set_Desired(Direction(NavCom));
					}
				}
			}
		}
	}
}


/***********************************************************************************************
 * UnitClass::Edge_Of_World_AI -- Check for falling off the edge of the world.                 *
 *                                                                                             *
 *    When a unit leaves the map it will be eliminated. This routine checks for this case      *
 *    and eliminates the unit accordingly.                                                     *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  bool; Was the unit eliminated by this routine?                                     *
 *                                                                                             *
 * WARNINGS:   Be sure to check for the return value and if 'true' abort any further processing*
 *             of the unit since it is dead. Only call this routine once per unit per          *
 *             game logic loop.                                                                *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   07/30/1996 JLB : Created.                                                                 *
 *=============================================================================================*/
bool UnitClass::Edge_Of_World_AI(void)
{
	if (Mission == MISSION_GUARD && !Map.In_Radar(Coord_Cell(Coord)) && IsLocked) {
		if (Team.Is_Valid()) Team->IsLeaveMap = true;
		Stun();
		delete this;
		return(true);
	}
	return(false);
}


/***********************************************************************************************
 * UnitClass::Reload_AI -- Perform reload logic for this unit.                                 *
 *                                                                                             *
 *    Some units require special reload logic. The V2 rocket launcher in particular. Perform   *
 *    this reload logic with this routine.                                                     *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   Only call this routine once per unit per game logic loop.                       *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   07/30/1996 JLB : Created.                                                                 *
 *=============================================================================================*/
void UnitClass::Reload_AI(void)
{
	if (*this == UNIT_V2_LAUNCHER && Ammo < Class->MaxAmmo) {
		if (IsDriving) {
			Reload = Reload + 1;
		} else {
			if (Reload == 0) {
				Ammo++;
				if (Ammo < Class->MaxAmmo) {
					Reload = TICKS_PER_SECOND*30;
				}
				Mark(MARK_CHANGE);
			}
		}
	}
}


/***********************************************************************************************
 * UnitClass::Firing_AI -- Handle firing logic for this unit.                                  *
 *                                                                                             *
 *    This routine wil check for and perform any firing logic required of this unit.           *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   This should be called only once per unit per game logic loop.                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   07/30/1996 JLB : Created.                                                                 *
 *=============================================================================================*/
void UnitClass::Firing_AI(void)
{
	if (Target_Legal(TarCom) && Class->PrimaryWeapon != NULL) {

		/*
		**	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:
				if (!((UnitClass *)this)->Class->IsFireAnim) {
					Mark(MARK_OVERLAP_UP);
					IsFiring = false;
					Mark(MARK_OVERLAP_DOWN);
				}

				Fire_At(TarCom, primary);
				break;

			case FIRE_FACING:
#ifdef FIXIT_CSII	//	checked - ajw 9/28/98
				if (Class->IsLockTurret || Class->Type == UNIT_DEMOTRUCK) {
#else
				if (Class->IsLockTurret) {
#endif
					if (!Target_Legal(NavCom) && !IsDriving) {
						PrimaryFacing.Set_Desired(Direction(TarCom));
						SecondaryFacing.Set_Desired(PrimaryFacing.Desired());
					}
				} else {
					SecondaryFacing.Set_Desired(Direction(TarCom));
				}
				break;

			case FIRE_CLOAKED:
				Mark(MARK_OVERLAP_UP);
				IsFiring = false;
				Mark(MARK_OVERLAP_DOWN);
				Do_Uncloak();
				break;
		}
	}
}


/***********************************************************************************************
 * UnitClass::Receive_Message -- Handles receiving a radio message.                            *
 *                                                                                             *
 *    This is the handler function for when a unit receives a radio                            *
 *    message. Typical use of this is when a unit unloads from a hover                         *
 *    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/22/1994 JLB : Created.                                                                 *
 *=============================================================================================*/
RadioMessageType UnitClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param)
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	switch (message) {
		/*
		**	Checks to see if this object is in need of service depot processing.
		*/
		case RADIO_NEED_REPAIR:
			if (!IsDriving && !Target_Legal(NavCom) && (Health_Ratio() >= 1 && (*this != UNIT_MINELAYER || Ammo >= Class->MaxAmmo))) return(RADIO_NEGATIVE);
			break;
//			return(RADIO_ROGER);

		/*
		**	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()) {
				return(RADIO_ROGER);
			}
			return(RADIO_NEGATIVE);

		/*
		**	The refinery has told this harvester that it should begin the backup procedure
		**	so that proper unloading may take place.
		*/
		case RADIO_BACKUP_NOW:
			DriveClass::Receive_Message(from, message, param);
			if (!IsRotating && PrimaryFacing != DIR_W) {
				Do_Turn(DIR_W);
			} else {
				if (!IsDriving) {
					TechnoClass	* whom = Contact_With_Whom();
					if (IsTethered && whom != NULL) {
						if (whom->What_Am_I() == RTTI_BUILDING && Mission == MISSION_ENTER) {
							if (Transmit_Message(RADIO_IM_IN, whom) == RADIO_ROGER) {
								Transmit_Message(RADIO_UNLOADED, whom);
							}
						}
					}
				}
			}
			return(RADIO_ROGER);

		/*
		**	This message is sent by the passenger when it determines that it has
		**	entered the transport.
		*/
		case RADIO_IM_IN:
			if (How_Many() == Class->Max_Passengers()) {
				APC_Close_Door();
			}
			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()) {
					Transmit_Message(RADIO_HELLO, from);
				} 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 && How_Many() < Class->Max_Passengers()) {
				DriveClass::Receive_Message(from, message, param);

				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);
							Do_Turn(dir);

							/*
							**	If it is now facing the correct direction, then open the
							**	transport doors. Close the doors if the transport is or needs
							**	to rotate.
							*/
#ifdef FIXIT_PHASETRANSPORT	//	checked - ajw 9/28/98
							if (*this == UNIT_APC || *this == UNIT_PHASE) {
#else
							if (*this == UNIT_APC) {
#endif
								if (IsRotating) {
									if (!Is_Door_Closed()) {
										APC_Close_Door();
									}
								} else {
									if (!Is_Door_Open()) {
										APC_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) {
#ifdef FIXIT_PHASETRANSPORT	//	checked - ajw 9/28/98
								if ( (*this != UNIT_APC && *this != UNIT_PHASE) || Is_Door_Open()) {
#else
								if (*this != UNIT_APC || Is_Door_Open()) {
#endif
									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);
			}
			break;

		/*
		**	Something bad has happened to the object in contact with. Abort any coordinated
		**	activity with this object. Basically, ... run away! Run away!
		*/
		case RADIO_RUN_AWAY:
			if (Class->IsToHarvest && In_Radio_Contact() && Mission == MISSION_ENTER) {
				TechnoClass * contact = Contact_With_Whom();
				if (contact->What_Am_I() == RTTI_BUILDING && *((BuildingClass*)contact) == STRUCT_REFINERY) {
					// Slight hack; set a target so the harvest mission knows to skip to finding home state
					Assign_Mission(MISSION_HARVEST);
					TarCom = As_Target();
					return(RADIO_ROGER);
				}
			}
			return(DriveClass::Receive_Message(from, message, param));

		/*
		**	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));
}


/***********************************************************************************************
 * UnitClass::Unlimbo -- Removes unit from stasis.                                             *
 *                                                                                             *
 *    This routine will place a unit into the game and out of its limbo                        *
 *    state. This occurs whenever a unit is unloaded from a transport.                         *
 *                                                                                             *
 * INPUT:   coord    -- The coordinate to make the unit appear.                                *
 *                                                                                             *
 *          dir      -- The initial facing to impart upon the unit.                            *
 *                                                                                             *
 * OUTPUT:  bool; Was the unit unlimboed successfully?  If the desired                         *
 *                coordinate is illegal, then this might very well return                      *
 *                false.                                                                       *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   05/22/1994 JLB : Created.                                                                 *
 *=============================================================================================*/
bool UnitClass::Unlimbo(COORDINATE coord, DirType dir)
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	/*
	**	All units must start out facing one of the 8 major directions.
	*/
	dir = Facing_Dir(Dir_Facing(dir));
	if (DriveClass::Unlimbo(coord, dir)) {

		SecondaryFacing = dir;
		/*
		**	Ensure that the owning house knows about the
		**	new object.
		*/
		House->UScan |= (1L << Class->Type);
		House->ActiveUScan |= (1L << Class->Type);

		/*
		**	If it starts off the edge of the map, then it already starts cloaked.
		*/
		if (IsCloakable && !IsLocked) Cloak = CLOAKED;

		/*
		**	Units default to no special animation.
		*/
		Set_Rate(0);
		Set_Stage(0);

		return(true);
	}
	return(false);
}


/***********************************************************************************************
 * UnitClass::Take_Damage -- Inflicts damage points on a unit.                                 *
 *                                                                                             *
 *    This routine will inflict the specified number of damage points on                       *
 *    the given unit. If the unit is destroyed, then this routine will                         *
 *    remove the unit cleanly from the game. The return value indicates                        *
 *    whether the unit was destroyed. This will allow appropriate death                        *
 *    animation or whatever.                                                                   *
 *                                                                                             *
 * INPUT:   damage-- The number of damage points to inflict.                                   *
 *                                                                                             *
 *          distance -- The distance from the damage center point to the object's center point.*
 *                                                                                             *
 *          warhead--The type of damage to inflict.                                            *
 *                                                                                             *
 *          source   -- Who is responsible for this damage?                                    *
 *                                                                                             *
 * OUTPUT:  Returns the result of the damage process. This can range from RESULT_NONE up to    *
 *          RESULT_DESTROYED.                                                                  *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   05/30/1991 JLB : Created.                                                                 *
 *   07/12/1991 JLB : Script initiated by unit destruction.                                    *
 *   04/15/1994 JLB : Converted to member function.                                            *
 *   04/16/1994 JLB : Warhead modifier.                                                        *
 *   06/03/1994 JLB : Added the source of the damage target value.                             *
 *   06/20/1994 JLB : Source is a base class pointer.                                          *
 *   11/22/1994 JLB : Shares base damage handler for techno objects.                           *
 *   06/30/1995 JLB : Lasers do maximum damage against gunboat.                                *
 *   08/16/1995 JLB : Harvester crushing doesn't occur on early missions.                      *
 *=============================================================================================*/
ResultType UnitClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced)
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	ResultType res = RESULT_NONE;

	/*
	**	Remember if this object was selected. If it was and it gets destroyed and it has
	**	passengers that pop out, then the passengers will inherit the select state.
	*/
	//bool select = (IsSelected && House->IsPlayerControl);
	bool select = (Is_Selected_By_Player() );//&& House->IsPlayerControl);

	/*
	**	In order for a this to be damaged, it must either be a unit
	**	with a crew or a sandworm.
	*/
	res = DriveClass::Take_Damage(damage, distance, warhead, source, forced);

	if (res == RESULT_DESTROYED) {
		Death_Announcement(source);
		Shroud_Regen();	// remove the shroud if it's a gap generator
		if (Class->Explosion != ANIM_NONE) {
			AnimType anim = Class->Explosion;

			/*
			**	SSM launchers will really explode big if they are carrying
			**	missiles at the time of the explosion.
			*/
			if (*this == UNIT_V2_LAUNCHER && Ammo) {
				anim = ANIM_NAPALM3;
			}

			new AnimClass(anim, Coord);

			/*
			**	Harvesters explode with a force equal to the amount of
			**	Tiberium they are carrying.
			*/
			if (Tiberium > 0 && Rule.IsExplosiveHarvester) {
				Wide_Area_Damage(Coord, CELL_LEPTON_W + CELL_LEPTON_W/2, Credit_Load()+Class->MaxStrength, this, WARHEAD_HE);
			}

			/*
			**	Very strong units that have an explosion will also rock the
			**	screen when they are destroyed.
			*/
			if (Class->MaxStrength > 400) {
				Shake_The_Screen(3, Owner());
				if (source && Owner() != source->Owner()) {
					Shake_The_Screen(3, source->Owner());
				}
			}
		}

		/*
		**	Possibly have the crew member run away.
		*/
		CELL cell = Coord_Cell(Center_Coord());
		Mark(MARK_UP);

		if (Class->IsCrew && Class->Max_Passengers() == 0) {
			if (Percent_Chance(50)) {
				InfantryClass * i = 0;

				if (Class->PrimaryWeapon == NULL) {
					i = new InfantryClass(INFANTRY_C1, House->Class->House);
					if (i != NULL) i->IsTechnician = true;
				} else {
					i = new InfantryClass(INFANTRY_E1, House->Class->House);
				}
				if (i != NULL) {
					if (i->Unlimbo(Coord, DIR_N)) {
						i->Strength = Random_Pick(5, (int)i->Class->MaxStrength/2);
						i->Scatter(0, true);
						if (!House->IsHuman) {
							i->Assign_Mission(MISSION_HUNT);
						} else {
							i->Assign_Mission(MISSION_GUARD);
						}
						if (select) i->Select();
					} else {
						delete i;
					}
				}
			}
		} else {
			while (Is_Something_Attached()) {
				FootClass * object = Detach_Object();

				if (object == NULL) break;		// How can this happen?

				/*
				**	Only infantry can run from a destroyed vehicle. Even then, it is not a sure
				**	thing.
				*/
				if (object->Is_Infantry() && object->Unlimbo(Coord, DIR_N)) {
					object->Look(false);
					object->Scatter(0, true);
					if (select) object->Select();
				} else {
					object->Record_The_Kill(source);
					delete object;
				}
			}
		}

		/*
		**	If this is a truck, there is a possibility that a crate will drop out
		**	if the scenario so indicates and there is room.
		*/
		if (Scen.IsTruckCrate && *this == UNIT_TRUCK) {
			cell = Nearby_Location();
			if (cell != 0) {
				new OverlayClass(OVERLAY_WOOD_CRATE, cell);
			}
		}

		if (*this == UNIT_MCV) {
			if (House) {
				House->Check_Pertinent_Structures();
			}
		}

		/*
		**	Finally, delete the vehicle.
		*/
		delete this;

	} else {

		/*
		**	When damaged and below half strength, start smoking if
		**	it isn't already smoking.
		*/
		if (Health_Ratio() <= Rule.ConditionYellow && !IsAnimAttached) {
#ifdef FIXIT_ANTS
			if (*this != UNIT_ANT1 && *this != UNIT_ANT2 && *this != UNIT_ANT3)  {
#endif
				AnimClass * anim = new AnimClass(ANIM_SMOKE_M, Coord_Add(Coord, XYP_Coord(0, -8)));
				if (anim) anim->Attach_To(this);
#ifdef FIXIT_ANTS
			}
#endif
		   }

		/*
		**	Try to crush anyone that fires on this unit if possible. The harvester
		**	typically is the only one that will qualify here.
		*/
		if (!Team.Is_Valid() && source != NULL && !IsTethered && !House->Is_Ally(source) && (!House->IsHuman || Rule.IsAutoCrush)) {

			/*
			**	Try to crush the attacker if it can be crushed by this unit and this unit is
			**	not equipped with a flame type weapon. If this unit has a weapon and the target
			**	is not very close, then fire on it instead. In easy mode, they never run over the
			**	player. In hard mode, they always do. In normal mode, they only overrun past
			**	mission #8.
			*/
			if (Should_Crush_It(source)) {
				Assign_Destination(source->As_Target());
				Assign_Mission(MISSION_MOVE);
			} else {

				/*
				**	Try to return to base if possible.
				*/
				if (*this == UNIT_HARVESTER && Pip_Count() && Health_Ratio() <= Rule.ConditionYellow) {

					/*
					**	Find nearby refinery and head to it?
					*/
					BuildingClass * building = Find_Docking_Bay(STRUCT_REFINERY, false);

					/*
					**	Since the refinery said it was ok to load, establish radio
					**	contact with the refinery and then await docking orders.
					*/
					if (building != NULL && Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER) {
						Assign_Mission(MISSION_ENTER);
					}
				}
			}
		}

		/*
		**	Computer controlled harvester will radio for help if they are attacked.
		*/
		if (*this == UNIT_HARVESTER && !House->IsHuman && source) {
			Base_Is_Attacked(source);
		}
	}
	return(res);
}


/***********************************************************************************************
 * UnitClass::Active_Click_With -- Intercepts the active click to see if deployment is possible*
 *                                                                                             *
 *    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:                                                                                    *
 *   03/10/1995 JLB : Created.                                                                 *
 *=============================================================================================*/
void UnitClass::Active_Click_With(ActionType action, ObjectClass * object)
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	if (action != What_Action(object)) {
		action = What_Action(object);
		switch (action) {
			case ACTION_SABOTAGE:
			case ACTION_CAPTURE:
				action = ACTION_ATTACK;
				break;

			case ACTION_ENTER:
				action = ACTION_MOVE;
				break;

			default:
				break;
		}
	}

	/*
	**	Short circuit out if trying to tell a unit to "nomove" to itself. This bypass of the
	**	normal active click with logic prevents any disturbance to the vehicle's state. Without
	**	this bypass, a unit on a repair bay would stop repairing because it would break radio
	**	contact.
	*/
	if (object == this && action == ACTION_NOMOVE) {
		return;
	}
#ifdef FIXIT_CSII	//	checked - ajw 9/28/98
	if (*this == UNIT_MAD && (IsDumping || Gold)) {
	} else {
		DriveClass::Active_Click_With(action, object);
	}
#else
	DriveClass::Active_Click_With(action, object);
#endif
}


/***********************************************************************************************
 * UnitClass::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:                                                                                    *
 *   09/21/1995 JLB : Created.                                                                 *
 *=============================================================================================*/
void UnitClass::Active_Click_With(ActionType action, CELL cell)
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

#ifdef FIXIT_CSII	//	checked - ajw 9/28/98
	if (*this == UNIT_MAD && (IsDumping || Gold)) {
	} else {
		DriveClass::Active_Click_With(action, cell);
	}
#else
	DriveClass::Active_Click_With(action, cell);
#endif
}


void UnitClass::Player_Assign_Mission(MissionType mission, TARGET target, TARGET destination)
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	if (mission == MISSION_HARVEST) {
		ArchiveTarget = TARGET_NONE;
	} else if (mission == MISSION_ENTER) {
		BuildingClass* building = As_Building(destination);
		if (building != NULL && *building == STRUCT_REFINERY && building->In_Radio_Contact()) {
			building->Transmit_Message(RADIO_OVER_OUT);
		}
	}
	DriveClass::Player_Assign_Mission(mission, target, destination);
}


/***********************************************************************************************
 * UnitClass::Enter_Idle_Mode -- Unit enters idle mode state.                                  *
 *                                                                                             *
 *    This routine is called when the unit completes one mission but does not have a clear     *
 *    follow up mission to perform. In such a case, the unit should enter a default idle       *
 *    state. This idle state varies depending on what the current internal computer            *
 *    settings of the unit is as well as what kind of unit it is.                              *
 *                                                                                             *
 * INPUT:   initial  -- Is this called when the unit just leaves a factory or is initially     *
 *                      or is initially placed on the map?                                     *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   05/31/1994 JLB : Created.                                                                 *
 *   06/03/1994 JLB : Fixed to handle non-combat vehicles.                                     *
 *   06/18/1995 JLB : Allows a harvester to stop harvesting.                                   *
 *=============================================================================================*/
void UnitClass::Enter_Idle_Mode(bool initial)
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	MissionType	order = MISSION_GUARD;

	if (IsToScatter) {
		IsToScatter = false;
		Scatter(0, true);
	}

	/*
	**	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 (!Is_Weapon_Equipped()) {
			if (Class->IsToHarvest) {
				if (!In_Radio_Contact() && Mission != MISSION_HARVEST && MissionQueue != MISSION_HARVEST) {
					if (initial || !House->IsHuman || Map[Coord].Land_Type() == LAND_TIBERIUM) {
						order = MISSION_HARVEST;
					} else {
						order = MISSION_GUARD;
					}
					Assign_Target(TARGET_NONE);
					Assign_Destination(TARGET_NONE);
				} else {
					return;
				}
			} else {
				if (IsALoaner && Class->Max_Passengers() > 0 && Is_Something_Attached() && !Team.Is_Valid()) {
					order = MISSION_UNLOAD;
				} else {
#ifdef FIXIT_CSII	//	checked - ajw 9/28/98
	if(*this == UNIT_MAD && Mission == MISSION_UNLOAD) {
		order = MISSION_UNLOAD;
	} else {
#endif
					order = MISSION_GUARD;
					Assign_Target(TARGET_NONE);
					Assign_Destination(TARGET_NONE);
				}
			}
#ifdef FIXIT_CSII	//	checked - ajw 9/28/98
	}
#endif
		} else {

			if (Mission == MISSION_GUARD || Mission == MISSION_GUARD_AREA || MissionControl[Mission].IsParalyzed || MissionControl[Mission].IsZombie) {
				return;
			}

			if (House->IQ < Rule.IQGuardArea || Team.Is_Valid()) {
				order = MISSION_GUARD;
			} else {
				order = MISSION_GUARD_AREA;
			}
		}
	}
	Assign_Mission(order);
}


/***********************************************************************************************
 * UnitClass::Goto_Clear_Spot -- Finds a clear spot to deploy.                                 *
 *                                                                                             *
 *    This routine is used by the MCV to find a clear spot to deploy. If a clear spot          *
 *    is found, then the MCV will assign that location to its navigation computer. This only   *
 *    occurs if the MCV isn't already heading toward a spot.                                   *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  bool;  Is the located at a spot where it can deploy?                               *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   06/27/1994 JLB : Created.                                                                 *
 *=============================================================================================*/
bool UnitClass::Goto_Clear_Spot(void)
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	Mark(MARK_UP);
	if (!Target_Legal(NavCom) && BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(Adjacent_Cell(Coord_Cell(Center_Coord()), FACING_NW))) {
		Mark(MARK_DOWN);
		return(true);
	}

	if (!Target_Legal(NavCom)) {
		/*
		**	This scan table is skewed to north scanning only. This should
		**	probably be converted to a more flexible method.
		*/
		static int _offsets[] = {
			-MAP_CELL_W*1,
			-MAP_CELL_W*2,
			-(MAP_CELL_W*2)+1,
			-(MAP_CELL_W*2)-1,
			-MAP_CELL_W*3,
			-(MAP_CELL_W*3)+1,
			-(MAP_CELL_W*3)-1,
			-(MAP_CELL_W*3)+2,
			-(MAP_CELL_W*3)-2,
			-MAP_CELL_W*4,
			-(MAP_CELL_W*4)+1,
			-(MAP_CELL_W*4)-1,
			-(MAP_CELL_W*4)+2,
			-(MAP_CELL_W*4)-2,
//BG: Added south scanning
			MAP_CELL_W*1,
			MAP_CELL_W*2,
			(MAP_CELL_W*2)+1,
			(MAP_CELL_W*2)-1,
			MAP_CELL_W*3,
			(MAP_CELL_W*3)+1,
			(MAP_CELL_W*3)-1,
			(MAP_CELL_W*3)+2,
			(MAP_CELL_W*3)-2,
			MAP_CELL_W*4,
			(MAP_CELL_W*4)+1,
			(MAP_CELL_W*4)-1,
			(MAP_CELL_W*4)+2,
			(MAP_CELL_W*4)-2,

//BG: Added some token east/west scanning
			-1,-2,-3,-4,

			 1, 2, 3, 4,
			0
		};
		int * ptr;

		ptr = &_offsets[0];
		while (*ptr) {
			CELL	cell = Coord_Cell(Coord)+*ptr++;
			CELL	check_cell = Adjacent_Cell(cell, FACING_NW);
			if (BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(check_cell)) {
				Assign_Destination(::As_Target(cell));
				break;
			}
		}
	}
	Mark(MARK_DOWN);

	/*
	** If we couldn't find a destination to go to, let's try random movement
	** to see if that brings us to a better spot.
	*/
	if(!Target_Legal(NavCom) && !House->IsHuman) {
		Scatter(0);
	}

	return(false);
}


/***********************************************************************************************
 * UnitClass::Try_To_Deploy -- The unit attempts to "deploy" at current location.              *
 *                                                                                             *
 *    Certain units have the ability to deploy into a building. When this routine is called    *
 *    for one of those units, it will attempt to deploy at its current location. If the unit   *
 *    is in motion to a destination or it isn't one of the special units that can deploy or    *
 *    it isn't allowed to deploy at this location for some reason it won't deploy. In all      *
 *    other cases, it will begin to deploy and once it begins only a player abort action will  *
 *    stop it.                                                                                 *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  bool; Was deployment begun?                                                        *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   06/18/1994 JLB : Created.                                                                 *
 *=============================================================================================*/
bool UnitClass::Try_To_Deploy(void)
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	if (!Target_Legal(NavCom) && !IsRotating) {
		if (*this == UNIT_MCV) {

			/*
			**	Determine if it is legal to deploy at this location. If not, tell the
			**	player.
			*/
			Mark(MARK_UP);
			CELL cell = Coord_Cell(Adjacent_Cell(Center_Coord(), FACING_NW));
			if (!BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(cell)) {
				if (PlayerPtr == House) {
					Speak(VOX_DEPLOY);
				}
				if (!House->IsHuman) {
					BuildingTypeClass::As_Reference(STRUCT_CONST).Flush_For_Placement(cell, House);
				}
				Mark(MARK_DOWN);
				IsDeploying = false;
				return(false);
			}
			Mark(MARK_DOWN);

			/*
			**	If the unit is not facing the correct direction, then start it rotating
			**	toward the right facing, but still flag it as if it had deployed. This is
			**	because it will deploy as soon as it reaches the correct facing.
			*/
			if (PrimaryFacing.Current() != DIR_SW) {
				Do_Turn(DIR_SW);
//				PrimaryFacing.Set_Desired(DIR_SW);
				IsDeploying = true;
				return(true);
			}

			/*
			**	Since the unit is already facing the correct direction, actually do the
			**	deploy logic. If for some reason this cannot occur, then don't delete the
			**	unit, just mark it as not deploying.
			*/
			Mark(MARK_UP);
			BuildingClass * building = new BuildingClass(STRUCT_CONST, House->Class->House);
			if (building != NULL) {
				if (building->Unlimbo(Adjacent_Cell(Coord, FACING_NW))) {

					/*
					**	Play the buildup sound for the player if this is the players
					**	MCV.
					*/
					if (building->House == PlayerPtr) {
						Sound_Effect(VOC_PLACE_BUILDING_DOWN, Center_Coord());
					} else {
						building->IsToRebuild = true;
						building->IsToRepair = true;
					}

					/*
					**	Always reveal the construction yard to the player that owned the
					**	mobile construction vehicle.
					*/
					building->Revealed(House);

					/*
					**	When the MCV deploys, always consider production to have started
					**	for the owning house. This ensures that in multiplay, computer
					**	opponents will begin construction as soon as they start their
					**	base.
					*/
					House->IsStarted = true;

					/*
					**	Force the newly placed construction yard to be in the same strength
					**	ratio as the MCV that deployed into it.
					*/
					building->Strength = Health_Ratio() * (int)building->Class->MaxStrength;

					/*
					** Force the MCV to drop any flag it was carrying.  This will also set
					** the owner house's flag home cell (since the house's FlagHome is
					** presumably 0 at this point).
					*/
					Stun();

					/*
					** If this MCV was teleported here, clear the gray flag so
					** the screen will go back to color.
					*/
					if (IsMoebius && !Scen.IsFadingColor) {
						Scen.IsFadingBW = false;
						Scen.IsFadingColor = true;
						Scen.FadeTimer = GRAYFADETIME;
					}
					delete this;
					return(true);
				} else {

					/*
					**	Could not deploy the construction yard at this location! Just revert
					**	back to normal "just sitting there" mode and await further instructions.
					*/
					delete building;
				}
			}
			Mark(MARK_DOWN);
			IsDeploying = false;
		}
	}
	return(false);
}


/***********************************************************************************************
 * UnitClass::Per_Cell_Process -- Performs operations necessary on a per cell basis.           *
 *                                                                                             *
 *    This routine will perform the operations necessary that occur when a unit is at the      *
 *    center of a cell. These operations could entail deploying into a construction yard,      *
 *    radioing a transport unit, and looking around for the enemy.                             *
 *                                                                                             *
 * INPUT:   why   -- Specifies the circumstances under which this routine was called.          *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   06/18/1994 JLB : Created.                                                                 *
 *   06/17/1995 JLB : Handles case when building says "NO!"                                    *
 *   06/30/1995 JLB : Gunboats head back and forth now.                                        *
 *=============================================================================================*/
void UnitClass::Per_Cell_Process(PCPType why)
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	CELL	cell = Coord_Cell(Coord);
	HousesType house;

	if (why == PCP_END || why == PCP_ROTATION) {
		/*
		**	Check to see if this is merely the end of a rotation for the MCV as it is
		**	preparing to deploy. In this case, it should begin its deploy process.
		*/
		if (IsDeploying) {
			Try_To_Deploy();
			if (!IsActive) return;			// Unit no longer exists -- bail.
		}
	}

	BStart(BENCH_PCP);
	if (why == PCP_END) {
		/*
		**	If this is a unit that is driving onto a building then the unit must enter
		**	the building as the final step.
		*/
		TechnoClass	* whom = Contact_With_Whom();
		if (IsTethered && whom != NULL) {
			if (whom->What_Am_I() == RTTI_BUILDING && Mission == MISSION_ENTER) {
				if (whom == Map[CELL(cell-MAP_CELL_W)].Cell_Building()) {
					switch (Transmit_Message(RADIO_IM_IN, whom)) {
						case RADIO_ROGER:
							break;

						case RADIO_ATTACH:
							break;

						default:
							Scatter(0, true);
							break;
					}
				}
			}
		}

		/*
		**	Unit entering a transport vehicle will break radio contact
		**	and attach itself to the transporter.
		*/
		TechnoClass * techno = Contact_With_Whom();
		if (Mission == MISSION_ENTER && techno && Coord_Cell(Coord) == Coord_Cell(techno->Coord) && techno == As_Techno(NavCom)) {
			if (Transmit_Message(RADIO_IM_IN) == RADIO_ATTACH) {
				Limbo();
				techno->Attach(this);
			}
			BEnd(BENCH_PCP);
			return;
		}

		/*
		**	When breaking away from a transport object or building, possibly
		**	scatter or otherwise begin normal unit operations.
		*/
		if (IsTethered && (Mission != MISSION_ENTER ||
				(As_Techno(NavCom) != NULL && Contact_With_Whom() != As_Techno(NavCom))
				) &&
				Mission != MISSION_UNLOAD) {

			/*
			**	Special hack check to make sure that even though it has moved one
			**	cell, if it is still on the building (e.g., service depot), have
			**	it scatter again.
			*/
			if	(Map[Coord].Cell_Building() != NULL && !Target_Legal(NavCom)) {
				Scatter(0, true, true);
			} else {
				TechnoClass * contact = Contact_With_Whom();
				if (Transmit_Message(RADIO_UNLOADED) == RADIO_RUN_AWAY) {
					if (*this == UNIT_HARVESTER && contact && contact->What_Am_I() == RTTI_BUILDING && *((BuildingClass*)contact) != STRUCT_REPAIR) {
						Assign_Mission(MISSION_HARVEST);
					} else if (!Target_Legal(NavCom)) {
						Scatter(0, true);
					} else {

						/*
						**	Special case hack to allow automatic transition to loading
						**	onto a transport (or other situation) if the destination
						**	so indicates.
						*/
						TechnoClass * techno = As_Techno(NavCom);
						if (techno != NULL) {
							Transmit_Message(RADIO_DOCKING, techno);
						}
					}
				} else {
					if (*this == UNIT_HARVESTER) {
						if (Target_Legal(ArchiveTarget)) {
							Assign_Mission(MISSION_HARVEST);
							Assign_Destination(ArchiveTarget);
							ArchiveTarget = TARGET_NONE;
						} else {

							/*
							**	Since there is no place to go, move away to clear
							**	the pad for another harvester.
							*/
							if (!Target_Legal(NavCom)) {
								Scatter(0, true);
							}
						}
					}
				}
			}
		}

		/*
		**	If this is a loaner unit and is is off the edge of the
		**	map, then it gets eliminated. That is, unless it is carrying cargo. This means that
		**	it is probably carrying an incoming reinforcement and it should not be eliminated.
		*/
		if (Edge_Of_World_AI()) {
			BEnd(BENCH_PCP);
			return;
		}

		/*
		**	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.
		*/
		if (IsPlanningToLook) {
			IsPlanningToLook = false;
			Look(false);
		} else {
			Look(true);
		}

		/*
		** If this is a mobile gap generator, restore the shroud where appropriate
		** and re-shroud around us.
		*/
		if (Class->IsGapper && !House->IsPlayerControl) {
			Shroud_Regen();
		}

		/*
		**	Act on new orders if the unit is at a good position to do so.
		*/
		if (!IsDumping) {
			Commence();
		}

		/*
		**	Certain units require some setup time after they come to a halt.
		*/
		if (!Target_Legal(NavCom) && Path[0] == FACING_NONE) {
			if (Class->IsNoFireWhileMoving) {
				Arm = Rearm_Delay(true)/4;
			}
		}

		/*
		**	If there is a house flag here, then this unit just might pick it up.
		*/
		if (Flagged == HOUSE_NONE) {

			if (Map[cell].IsFlagged && !House->Is_Ally(Map[cell].Owner)) {
				HouseClass::As_Pointer(Map[cell].Owner)->Flag_Attach(this);
			}
		}

		/*
		**	If this is the unit's own flag-home-cell and the unit is carrying
		** a flag, destroy the house of the flag the unit is carrying.
		*/
		if (Flagged != HOUSE_NONE) {

			/*
			**	If this vehicle is carrying your flag, then it will reveal the
			**	map for you as well as itself. This gives you and opportunity to
			**	attack the unit.
			*/
			if (!IsOwnedByPlayer && Flagged == PlayerPtr->Class->House) {
				Map.Sight_From(Coord_Cell(Coord), Class->SightRange, House, true);
			}

			/*
			**	If the flag reaches the home cell for the player, then the flag's
			**	owner will be destroyed.
			*/
			if (cell == HouseClass::As_Pointer(Owner())->FlagHome) {
				house = Flagged; // Flag_Remove will clear 'Flagged', so save it
				HouseClass::As_Pointer(house)->Flag_Remove(As_Target(), true);
				HouseClass::As_Pointer(house)->Flag_To_Die();
			}
		}

		/*
		** If entering a cell with a land mine in it, blow up the mine.
		*/
		BuildingClass * bldng = Map[cell].Cell_Building();
		if (bldng != NULL && (*bldng == STRUCT_AVMINE || *bldng == STRUCT_APMINE) && !bldng->House->Is_Ally(this)) {

			/*
			** Special case: if it's a land mine deployer, and it ran over the
			** type of mine it deploys (only possible if it just dropped it
			** down) then ignore the mine.
			*/
			if (*this != UNIT_MINELAYER || bldng->House != House) {

				COORDINATE blcoord = bldng->Center_Coord();

				new AnimClass(ANIM_MINE_EXP1, blcoord);
//				new AnimClass(Combat_Anim(Rule.AVMineDamage, WARHEAD_HE, Map[cell].Land_Type()), blcoord);

				/*
				** Vehicles blow up both mines, but they only take significant damage from AV mines.
				*/
				if (*bldng == STRUCT_AVMINE) {
					int damage = Rule.AVMineDamage;
					Take_Damage(damage, 0, WARHEAD_HE);
				} else {
					int damage = 10;
					Take_Damage(damage, 0, WARHEAD_HE);
				}
				delete bldng;
				if (!IsActive) {
					BEnd(BENCH_PCP);
					return;
				}
			}
		}

		/*
		**	If after all is said and done, the unit finishes its move on an impassable cell, then
		**	it must presume that it is in the case of a unit driving onto a bridge that blows up
		**	before the unit completes it's move. In such a case the unit should have been destroyed
		**	anyway, so blow it up now.
		*/
		LandType land = Map[Coord].Land_Type();
		if (!IsDriving && IsMovingOntoBridge && (land == LAND_ROCK || land == LAND_WATER || land == LAND_RIVER)) {
			new AnimClass(Combat_Anim(Strength, WARHEAD_AP, land), Coord);
			int damage = Strength;
			Take_Damage(damage, 0, WARHEAD_AP, NULL, true);
			return;
		}
	}

	/*
	**	Destroy any crushable wall that is driven over by a tracked vehicle.
	*/
	CellClass * cellptr = &Map[cell];
	if (Class->IsCrusher && cellptr->Overlay != OVERLAY_NONE) {
//	if (Class->Speed == SPEED_TRACK && cellptr->Overlay != OVERLAY_NONE) {
		OverlayTypeClass const * optr = &OverlayTypeClass::As_Reference(cellptr->Overlay);

		if (optr->IsCrushable) {
			if (optr->Type == OVERLAY_SANDBAG_WALL) {
				Sound_Effect(VOC_SANDBAG, Center_Coord());
			} else {
				Sound_Effect(VOC_WALLKILL2, Center_Coord());
			}
			cellptr->Reduce_Wall(-1);
		}
	}

	/*
	**	Check to see if crushing of any unfortunate infantry is warranted.
	*/
	Overrun_Square(Coord_Cell(Coord), false);

	if (!IsActive) {
		BEnd(BENCH_PCP);
		return;
	}
	DriveClass::Per_Cell_Process(why);
	BEnd(BENCH_PCP);
}


/***********************************************************************************************
 * UnitClass::Shape_Number -- Fetch the shape number to use for this unit.                     *
 *                                                                                             *
 *    This routine will calculate the shape number for this unit. The shape number is used     *
 *    for the body of the unit.                                                                *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  Returns with the shape number to be used for this unit.                            *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   07/29/1996 JLB : Created.                                                                 *
 *=============================================================================================*/
int UnitClass::Shape_Number(void) const
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	int			shapenum;		// Working shape number.
	int			facing = Dir_To_32(PrimaryFacing);
	int			tfacing = Dir_To_32(SecondaryFacing);
	DirType		rotation = DIR_N;

#ifdef FIXIT_ANTS
	/*
	**	This handles the ant case.
	*/
	if (Class->Rotation == 8)  {

		/*
		**	The starting frame is based on the facing of the unit.
		*/
		shapenum = ((UnitClass::BodyShape[facing]+2)/4) & 0x07;

		/*
		**	If the unit is driving, then it has an animation adjustment to the frame number.
		*/
		if (IsDriving)  {
			shapenum = 8 + (shapenum * 8) + ((::Frame+ID)/2) % 8;
		} else {

			/*
			**	If in combat, then do combat anims.
			*/
			if (Arm > 0)  {
				shapenum = 8 + 64 + (shapenum * 4) + ((::Frame+ID)/2) % 4;
			}
		}
	} else {
#endif

	/*
	**	Fetch the harvesting animation stage as appropriate.
	*/
	if (IsHarvesting && !PrimaryFacing.Is_Rotating() && !NavCom && !IsDriving) {
//			static char _hstage[] = {0, 1, 2, 3, 4, 5, 6, 7, 0};
		unsigned stage = Fetch_Stage();
		if (stage >= ARRAY_SIZE(Class->Harvester_Load_List)) stage = ARRAY_SIZE(Class->Harvester_Load_List)-1;
		shapenum = 32 + (((UnitClass::BodyShape[facing]+2)/4)*Class->Harvester_Load_Count)+Class->Harvester_Load_List[stage];
	} else {
		/*
		** If the harvester's dumping a load of ore, show that animation
		*/
		if (IsDumping) {
			unsigned stage = Fetch_Stage();
#ifdef FIXIT_CSII	//	checked - ajw 9/28/98
			if (*this == UNIT_MAD) {
				if (stage >= 8) {
					stage = 7;
				}
				shapenum = 32 + stage + (UnitClass::BodyShape[facing]/4)*8;
			} else {
				if (stage >= ARRAY_SIZE(Class->Harvester_Dump_List)) stage = ARRAY_SIZE(Class->Harvester_Dump_List)-1;
				shapenum = Class->Harvester_Dump_List[stage]+96;
			}
#else
			if (stage >= ARRAY_SIZE(Class->Harvester_Dump_List)) stage = ARRAY_SIZE(Class->Harvester_Dump_List)-1;
			shapenum = Class->Harvester_Dump_List[stage]+96;
#endif
		} else {
			shapenum = UnitClass::BodyShape[facing];

			if (Class->IsAnimating) {
				shapenum = Fetch_Stage();
			}

			/*
			**	Door opening and closing animation must be handled carefully. There are only
			**	certain directions where this door animation will work.
			*/
			if (!Is_Door_Closed() && (PrimaryFacing == DIR_NW || PrimaryFacing == DIR_NE)) {
				if (PrimaryFacing == DIR_NE) {
					shapenum = 32;
				} else {
					if (PrimaryFacing == DIR_NW) {
						shapenum = 35;
					}
				}
				shapenum += Door_Stage();
			}
		}
	}

#ifdef FIXIT_ANTS
	}
#endif

	/*
	**	The body of the V2 launcher indicates whether it is loaded with a missile
	**	or not.
	*/
	if (*this == UNIT_V2_LAUNCHER) {
		if (Ammo == 0) shapenum += 32;
	}

	return(shapenum);
}


/***********************************************************************************************
 * UnitClass::Draw_It -- Draws a unit object.                                                  *
 *                                                                                             *
 *    This routine is the one that actually draws a unit object. It displays the unit          *
 *    according to its current state flags and centered at the location specified.             *
 *                                                                                             *
 * INPUT:   x,y   -- The X and Y coordinate of where to draw the unit.                         *
 *                                                                                             *
 *          window   -- The clipping window to use.                                            *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   06/20/1994 JLB : Created.                                                                 *
 *   06/27/1994 JLB : Takes a window parameter.                                                *
 *   08/15/1994 JLB : Removed infantry support.                                                *
 *   01/07/1995 JLB : Harvester animation support.                                             *
 *   07/08/1995 JLB : Uses general purpose draw routine.                                       *
 *=============================================================================================*/
void UnitClass::Draw_It(int x, int y, WindowNumberType window) const
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	int			shapenum;		// Working shape number.
	void const	* shapefile;		// Working shape file pointer.
	int			facing = Dir_To_32(PrimaryFacing);
	int			tfacing = Dir_To_32(SecondaryFacing);
	DirType		rotation = DIR_N;
	int			scale = 0x0100;

	/*
	**	Verify the legality of the unit class.
	*/
	shapefile = Get_Image_Data();
	if (shapefile == NULL) return;

	/*
	**	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) {
		shapenum = Shape_Number();

		/*
		**	The artillery unit should have its entire body recoil when it fires.
		*/
		if (*this == UNIT_ARTY && IsInRecoilState) {
			Recoil_Adjust(PrimaryFacing.Current(), x, y);
		}

		/*
		**	Actually perform the draw. Overlay an optional shimmer effect as necessary.
		*/
		Techno_Draw_Object(shapefile, shapenum, x, y, window, rotation, scale);

		/*
		**	If there is a rotating radar dish, draw it now.
		*/
		if (Class->IsRadarEquipped) {
			if (*this == UNIT_MGG) {
				int x2 = x, y2 = y;
				shapenum = 32 + (Frame & 7);
				Class->Turret_Adjust(PrimaryFacing, x2, y2);
				Techno_Draw_Object(shapefile, shapenum, x2, y2, window);
			} else {
//#ifdef FIXIT_PHASETRANSPORT	//	checked - ajw 9/28/98
//				if (*this == UNIT_PHASE) {
//					shapenum = 38 + (Frame & 7);
//				} else {
//					shapenum = 32 + (Frame % 32);
//				}
//#else
				shapenum = 32 + (Frame % 32);
//#endif
#ifdef FIXIT_CSII	//	checked - ajw 9/28/98
				if (*this == UNIT_TESLATANK) {
					Techno_Draw_Object(shapefile, shapenum, x, y, window);
				} else {
					Techno_Draw_Object(shapefile, shapenum, x, y-5, window);
				}
#else
				Techno_Draw_Object(shapefile, shapenum, x, y-5, window);
#endif
			}
		}

		/*
		**	If there is a turret, then it must be rendered as well. This may include
		**	firing animation if required.
		*/
		if (/*!Class->IsChunkyShape &&*/ 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.
			*/
			shapenum = TechnoClass::BodyShape[tfacing]+32;
#ifdef FIXIT_PHASETRANSPORT	//	checked - ajw 9/28/98
			if (*this == UNIT_PHASE) {
				shapenum += 6;
			}
#endif
			/*
			**	A recoiling turret moves "backward" one pixel.
			*/
			if (IsInRecoilState) {
				Recoil_Adjust(SecondaryFacing, xx, yy);
			}

			Class->Turret_Adjust(PrimaryFacing, xx, yy);

			/*
			**	Actually perform the draw. Overlay an optional shimmer effect as necessary.
			*/
			Techno_Draw_Object(shapefile, shapenum, xx, yy, window);
		}
	}

	/*
	**	If this unit is carrying the flag, then draw that on top of everything else.
	*/
	if (Flagged != HOUSE_NONE) {
		shapefile = MFCD::Retrieve("FLAGFLY.SHP");
		int flag_x = x + (ICON_PIXEL_W / 2) - 2;
		int flag_y = y + (3 * ICON_PIXEL_H / 4) - Get_Build_Frame_Height(shapefile);
		CC_Draw_Shape(this, "FLAGFLY", shapefile, Frame % 14, flag_x, flag_y, window, SHAPE_CENTER|SHAPE_FADING|SHAPE_GHOST, HouseClass::As_Pointer(Flagged)->Remap_Table(false, Class->Remap), Map.UnitShadow, DIR_N, 0x0100, Flagged);
	}

	DriveClass::Draw_It(x, y, window);
}


/***********************************************************************************************
 * UnitClass::Tiberium_Check -- Search for and head toward nearest available Tiberium patch.   *
 *                                                                                             *
 *    This routine is used to move a harvester to a place where it can load up with            *
 *    Tiberium. It will return true only if it can start harvesting. Otherwise, it sets        *
 *    the navigation computer toward the nearest Tiberium and lets the unit head there         *
 *    automatically.                                                                           *
 *                                                                                             *
 * INPUT:   center   -- Reference to the center of the radius scan.                            *
 *                                                                                             *
 *          x,y      -- Relative offset from the center cell to perform the check upon.        *
 *                                                                                             *
 * OUTPUT:  int; Amount of Tiberium at this location.                                          *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   07/18/1994 JLB : Created.                                                                 *
 *=============================================================================================*/
int UnitClass::Tiberium_Check(CELL & center, int x, int y)
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	/*
	**	If the specified offset from the origin will cause it
	**	to spill past the map edge, then abort this cell check.
	*/
	if (Cell_X(center)+x < Map.MapCellX) return(0);
	if (Cell_X(center)+x >= Map.MapCellX+Map.MapCellWidth) return(0);
	if (Cell_Y(center)+y < Map.MapCellY) return(0);
	if (Cell_Y(center)+y >= Map.MapCellY+Map.MapCellHeight) return(0);

	center = XY_Cell(Cell_X(center)+x, Cell_Y(center)+y);

	if ((Session.Type != GAME_NORMAL || (!IsOwnedByPlayer || Map[center].Is_Mapped(PlayerPtr)))) {
		if (Map[Coord].Zones[Class->MZone] != Map[center].Zones[Class->MZone]) return(0);
		if (!Map[center].Cell_Techno() && Map[center].Land_Type() == LAND_TIBERIUM) {
			int value = 0;
			switch (Map[center].Overlay) {
				case OVERLAY_GOLD1:
				case OVERLAY_GOLD2:
				case OVERLAY_GOLD3:
				case OVERLAY_GOLD4:
					value = Rule.GoldValue;
					break;
				case OVERLAY_GEMS1:
				case OVERLAY_GEMS2:
				case OVERLAY_GEMS3:
				case OVERLAY_GEMS4:
					value = Rule.GemValue*4;
					break;
			}
			return((Map[center].OverlayData+1)*value);
		}
	}
	return(0);
}


/***********************************************************************************************
 * UnitClass::Goto_Tiberium -- Searches for and heads toward tiberium.                         *
 *                                                                                             *
 *    This routine will cause the unit to search for and head toward nearby Tiberium. When     *
 *    the Tiberium is reached, then this routine should not be called again until such time    *
 *    as additional harvesting is required. When this routine returns false, then it should    *
 *    be called again until such time as it returns true.                                      *
 *                                                                                             *
 * INPUT:   rad = size of ring to search                                                       *
 *                                                                                             *
 * OUTPUT:  Has the unit reached Tiberium and harvesting should begin?                         *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   09/22/1995 JLB : Created.                                                                 *
 *=============================================================================================*/
bool UnitClass::Goto_Tiberium(int rad)
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	if (!Target_Legal(NavCom)) {
		CELL center = Coord_Cell(Center_Coord());
		if (Map[center].Land_Type() == LAND_TIBERIUM) {
			return(true);
		} else {

			/*
			**	Perform a ring search outward from the center.
			*/
			for (int radius = 1; radius < rad; radius++) {
				CELL cell = center;
				CELL bestcell = 0;
				int tiberium = 0;
				int besttiberium = 0;
				for (int x = -radius; x <= radius; x++) {

					/*
					**	Randomize the corners.
					*/
					int corner[2];
					int corners[4][2] = {
						{x, -radius},
						{x, +radius},
						{-radius, x},
						{+radius, x}
					};
					for (int i = 0; i < 3; i++) {
						int j = i + rand() / (RAND_MAX / (4 - i) + 1);
						memcpy(&corner, &corners[j], sizeof(corner));
						memcpy(&corners[j], &corners[i], sizeof(corner));
						memcpy(&corners[i], corner, sizeof(corner));
					}

					cell = center;
					tiberium = Tiberium_Check(cell, corners[0][0], corners[0][1]);
					if (tiberium > besttiberium) {
						bestcell = cell;
						besttiberium = tiberium;
					}

					cell = center;
					tiberium = Tiberium_Check(cell, corners[1][0], corners[1][1]);
					if (tiberium > besttiberium) {
						bestcell = cell;
						besttiberium = tiberium;
					}

					cell = center;
					tiberium = Tiberium_Check(cell, corners[2][0], corners[2][1]);
					if (tiberium > besttiberium) {
						bestcell = cell;
						besttiberium = tiberium;
					}

					cell = center;
					tiberium = Tiberium_Check(cell, corners[3][0], corners[3][1]);
					if (tiberium > besttiberium) {
						bestcell = cell;
						besttiberium = tiberium;
					}
				}
				if (bestcell) {
					Assign_Destination(::As_Target(bestcell));
					return(false);
				}
			}
		}
	}

	return(false);
}


/***********************************************************************************************
 * UnitClass::Harvesting -- Harvests tiberium at the current location.                         *
 *                                                                                             *
 *    This routine is used to by the harvester to harvest Tiberium at the current location.    *
 *    When harvesting is complete, this routine will return true.                              *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  bool; Is harvesting complete?                                                      *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   07/18/1994 JLB : Created.                                                                 *
 *=============================================================================================*/
bool UnitClass::Harvesting(void)
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	CELL	cell = Coord_Cell(Coord);
	CellClass * ptr = &Map[cell];

	/*
	**	Keep waiting if still heading toward a spot to harvest.
	*/
	if (Target_Legal(NavCom)) return(true);

	if (Tiberium_Load() < 1 && ptr->Land_Type() == LAND_TIBERIUM) {

		/*
		**	Lift some Tiberium from the ground. Try to lift a complete
		**	"level" of Tiberium. A level happens to be 6 steps. If there
		**	is a partial level, then lift that instead. Never lift more
		**	than the harvester can carry.
		*/
//		int reducer = (ptr->OverlayData % 6) + 1;
		int reducer = 1;
		OverlayType overlay = ptr->Overlay;
		reducer = ptr->Reduce_Tiberium(min(reducer, Rule.BailCount-Tiberium));
		Tiberium += reducer;
		switch (overlay) {
			case OVERLAY_GOLD1:
			case OVERLAY_GOLD2:
			case OVERLAY_GOLD3:
			case OVERLAY_GOLD4:
				Gold += reducer;
				break;

			case OVERLAY_GEMS1:
			case OVERLAY_GEMS2:
			case OVERLAY_GEMS3:
			case OVERLAY_GEMS4:
				Gems += reducer;
				if (Rule.BailCount > Tiberium) {Gems++;Tiberium++;}
				if (Rule.BailCount > Tiberium) {Gems++;Tiberium++;}
				if (Rule.BailCount > Tiberium) {Gems++;Tiberium++;}
				break;

			default:
				break;
		}
		Set_Stage(0);
		Set_Rate(Rule.OreDumpRate);

	} else {

		/*
		**	If the harvester is stopped on a non Tiberium field and the harvester
		**	isn't loaded with Tiberium, then no further action can be performed
		**	by this logic routine. Bail with a failure and thus cause a branch to
		**	a better suited logic processor.
		*/
		Set_Stage(0);
		Set_Rate(0);
		return(false);
	}
	return(true);
}


/***********************************************************************************************
 * UnitClass::Mission_Unload -- Handles unloading cargo.                                       *
 *                                                                                             *
 *    This is the AI control sequence for when a transport desires to unload its cargo and     *
 *    then exit the map.                                                                       *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  Returns with the delay before calling this routine again.                          *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   07/18/1994 JLB : Created.                                                                 *
 *=============================================================================================*/
int UnitClass::Mission_Unload(void)
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	enum {
		INITIAL_CHECK,
		MANEUVERING,
		OPENING_DOOR,
		UNLOADING,
		CLOSING_DOOR
	};
	DirType	dir;
	CELL		cell;

	switch (Class->Type) {
		case UNIT_HARVESTER:
			if (PrimaryFacing != DIR_W) {
				if (!IsRotating) {
					Do_Turn(DIR_W);
				}
				return(5);
			}

			if (!IsDumping) {
				IsDumping = true;
				Set_Stage(0);
				Set_Rate(Rule.OreDumpRate);
				break;
			}
			if (Fetch_Stage() < ARRAY_SIZE(Class->Harvester_Dump_List)-1) break;

			IsDumping = false;
			if (Tiberium) {
				Tiberium = 0;
				int credits = Credit_Load();
				House->Harvested(credits);
				Tiberium = Gold = Gems = 0;
			}
			Transmit_Message(RADIO_OVER_OUT);

			Assign_Mission(MISSION_HARVEST);
			break;

		case UNIT_TRUCK:
			switch (Status) {
				case INITIAL_CHECK:
					dir = Desired_Load_Dir(NULL, cell);
					if (How_Many() && cell != 0) {
						Do_Turn(dir);
						Status = MANEUVERING;
						return(1);
					} else {
						Assign_Mission(MISSION_GUARD);
					}
					break;

				case MANEUVERING:
					if (!IsRotating) {
						Status = UNLOADING;
						return(1);
					}
					break;

				case UNLOADING:
					if (How_Many()) {
						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, 0x0080), newface);
									ScenarioInit--;
									passenger->Assign_Mission(MISSION_MOVE);
									passenger->Assign_Destination(::As_Target(newcell));
									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);
								Status = CLOSING_DOOR;
							}
							else {
								passenger->Look(false);
							}
						}
					} else {
						Status = CLOSING_DOOR;
					}
					break;

				/*
				**	Close APC door in preparation for normal operation.
				*/
				case CLOSING_DOOR:
					Assign_Mission(MISSION_GUARD);
					break;
			}
			break;

		case UNIT_APC:
#ifdef FIXIT_PHASETRANSPORT	//	checked - ajw 9/28/98
		case UNIT_PHASE:
#endif
			switch (Status) {
				case INITIAL_CHECK:
					dir = Desired_Load_Dir(NULL, cell);
					if (How_Many() && cell != 0) {
						Do_Turn(dir);
						Status = MANEUVERING;
						return(1);
					} else {
						Assign_Mission(MISSION_GUARD);
					}
					break;

				case MANEUVERING:
					if (!IsRotating) {
						APC_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()) {
						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, 0x0080), newface);
									ScenarioInit--;
									passenger->Assign_Mission(MISSION_MOVE);
									passenger->Assign_Destination(::As_Target(newcell));
									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);
								Status = CLOSING_DOOR;
							}
							else {
								passenger->Look(false);
							}
						}
					} else {
						Status = CLOSING_DOOR;
					}
					break;

				/*
				**	Close APC door in preparation for normal operation.
				*/
				case CLOSING_DOOR:
					if (Is_Door_Open()) {
						APC_Close_Door();
					}
					if (Is_Door_Closed()) {
						Assign_Mission(MISSION_GUARD);
					}
					break;
			}
			break;

		case UNIT_MCV:
			switch (Status) {
				case 0:
					Path[0] = FACING_NONE;
					Status = 1;
					break;

				case 1:
					if (!IsDriving) {
						Try_To_Deploy();
						if (IsActive) {
							if (IsDeploying) {
								Status = 2;
							} else {
								if (!House->IsHuman && Session.Type != GAME_NORMAL) {
									Assign_Mission(MISSION_HUNT);
								} else {
									Assign_Mission(MISSION_GUARD);
								}
							}
						}
					}
					break;

				case 2:
					if (!IsDeploying) {
						Assign_Mission(MISSION_GUARD);
					}
					break;
			}
			return(1);

		case UNIT_MINELAYER:
			switch (Status) {
				case INITIAL_CHECK:
					dir = DIR_NE;
					if (Ammo > 0) {
						Do_Turn(dir);
						Status = MANEUVERING;
						return(1);
					} else {
						Assign_Mission(MISSION_GUARD);
					}
					break;

				case MANEUVERING:
					if (!IsRotating) {
						APC_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 (Ammo > 0) {
						if (!Map[Center_Coord()].Cell_Building()) {
							Mark(MARK_UP);
							BuildingClass * building = new BuildingClass((House->ActLike == HOUSE_USSR || House->ActLike == HOUSE_UKRAINE || House->ActLike == HOUSE_BAD) ? STRUCT_APMINE : STRUCT_AVMINE, House->Class->House);
							if (building != NULL) {
								ScenarioInit = true;
								if (building->Unlimbo(Coord)) {
									Sound_Effect(VOC_MINELAY1, Coord);
									ScenarioInit = false;
									building->Revealed(House);
									Ammo--;
								}
								ScenarioInit = false;
							}
							Status = CLOSING_DOOR;
							Mark(MARK_DOWN);
						} else {
							Status = CLOSING_DOOR;
						}
					} else {
						Status = CLOSING_DOOR;
					}
					break;

				/*
				**	Close APC door in preparation for normal operation.
				*/
				case CLOSING_DOOR:
					if (Is_Door_Open()) {
						APC_Close_Door();
					}
					if (Is_Door_Closed()) {
						Assign_Mission(MISSION_GUARD);
					}
					break;
			}
			break;

#ifdef FIXIT_CSII	//	checked - ajw 9/28/98
		case UNIT_MAD:
			if (!Gems && !IsDumping) {
				Gems = 1;
				Gold = 0;
				Arm = QuakeDelay * House->ROFBias;
#ifdef ENGLISH
				Speak(VOX_MADTANK_DEPLOYED);	// this voice only exists in English
#else
				Sound_Effect(VOC_BUZZY1,Center_Coord());
#endif
				Set_Stage(0);
				Set_Rate(Rule.OreDumpRate*2);
				IsDumping = true;

#if 1
				InfantryClass *crew = new InfantryClass(INFANTRY_C1, House->Class->House);
				if (crew != NULL) crew->IsTechnician = true;

				if (crew != NULL) {
					DirType toface = DIR_S + PrimaryFacing;

					for (FacingType face = FACING_N; face < FACING_COUNT; face++) {
						DirType newface = toface + Facing_Dir(face);
						CELL newcell = Adjacent_Cell(Coord_Cell(Coord), newface);
						if (crew->Can_Enter_Cell(newcell) == MOVE_OK) {
							ScenarioInit++;
							crew->Unlimbo(Coord_Move(Coord, newface, 0x0080), newface);
							ScenarioInit--;
							crew->Assign_Mission(MISSION_MOVE);
							crew->Assign_Destination(::As_Target(newcell));
							break;
						}
					}
				}
#endif
			}

			if ( (Arm && !Gold) || IronCurtainCountDown) {
				Set_Stage(Fetch_Stage() & 1);
				return(1);
			}

			if (!Gold) {
				Sound_Effect(VOC_MAD_CHARGE, Center_Coord());
				Set_Stage(0);
				Gold = 1;
				return(1);
			}

			if (Fetch_Stage() < 7) {
				return(1);
			}

			IsDumping = false;

			Sound_Effect(VOC_MAD_EXPLODE, Center_Coord());

			Strength = 1;			// assure destruction
			PendingTimeQuake = true;		// trigger a time quake
			TimeQuakeCenter = ::As_Target(Center_Coord());
			break;

		case UNIT_CHRONOTANK:
			if (IsOwnedByPlayer) {
				Map.IsTargettingMode = SPC_CHRONO2;
				HouseClass* old_player_ptr = PlayerPtr;
				Logic_Switch_Player_Context(this);
				Unselect_All();
				On_Special_Weapon_Targetting(PlayerPtr, Map.IsTargettingMode);
				Logic_Switch_Player_Context(old_player_ptr);
			}
			House->UnitToTeleport = As_Target();

			Assign_Mission(MISSION_GUARD);
			break;
#endif
		default:
			break;
	}
	return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2));
}


/***********************************************************************************************
 * UnitClass::Mission_Harvest -- Handles the harvesting process used by harvesters.            *
 *                                                                                             *
 *    This is the AI process used by harvesters when they are doing their harvesting action.   *
 *    This entails searching for nearby Tiberium, heading there, harvesting, and then          *
 *    returning to a refinery for unloading.                                                   *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  Returns with the delay before calling this routine again.                          *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   07/18/1994 JLB : Created.                                                                 *
 *   06/21/1995 JLB : Force guard mode if no Tiberium found.                                   *
 *   09/28/1995 JLB : Aborts harvesting if there are no more refineries.                       *
 *=============================================================================================*/
int UnitClass::Mission_Harvest(void)
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	enum {
		LOOKING,
		HARVESTING,
		FINDHOME,
		HEADINGHOME,
		GOINGTOIDLE,
	};

	/*
	**	A non-harvesting type unit will just sit still if it is given the harvest mission. This
	**	allows combat units to act "brain dead".
	*/
	if (!Class->IsToHarvest) return(TICKS_PER_SECOND*30);

	/*
	**	If there are no more refineries, then drop into guard mode.
	*/
	if (!(House->ActiveBScan & STRUCTF_REFINERY)) {
		Assign_Mission(MISSION_GUARD);
		return(1);
	}

	switch (Status) {

		/*
		**	Go and find a Tiberium field to harvest.
		*/
		case LOOKING:
			/*
			**	Slightly hacky; if TarCom is set then skip to finding home state.
			*/
			if (Target_Legal(TarCom)) {
				Assign_Target(TARGET_NONE);
				Status = FINDHOME;
				return(1);
			}

			/*
			** Look for ore where we last found some - mine the same patch
			*/
			if (Target_Legal(ArchiveTarget)) {
				Assign_Destination(ArchiveTarget);
				ArchiveTarget = 0;
			}
			IsHarvesting = false;
			if (Goto_Tiberium(Rule.TiberiumLongScan / CELL_LEPTON_W)) {
				IsHarvesting = true;
				Set_Rate(2);
				Set_Stage(0);
				Status = HARVESTING;
				ArchiveTarget = ::As_Target(Coord_Cell(Coord));
				return(1);
			} else {

				/*
				**	If the harvester isn't on Tiberium and it is not heading toward Tiberium, then
				**	force it to go into guard mode. This will prevent the harvester from repeatedly
				**	searching for Tiberium.
				*/
				if (!Target_Legal(NavCom)) {

					/*
					**	If the archive target is legal, then head there since it is presumed
					**	that the archive target points to the last place it harvested at. This might
					**	solve the case where the harvester gets stuck and can't find Tiberium just because
					**	it is greater than 32 squares away.
					*/
					if (Target_Legal(ArchiveTarget)) {
						Assign_Destination(ArchiveTarget);
					} else {
						Status = GOINGTOIDLE;
						IsUseless = true;
						House->IsTiberiumShort = true;
						return(TICKS_PER_SECOND*7);
					}
				} else {
					IsUseless = false;
				}
			}
			break;

		/*
		**	Harvest at current location until full or Tiberium exhausted.
		*/
		case HARVESTING:
//			if (Fetch_Stage() > ARRAY_SIZE(Class->Harvester_Load_List)) {
//				Set_Stage(0);
//			}
			if (Fetch_Rate() == 0) {
				Set_Stage(0);
				Set_Rate(Rule.OreDumpRate);
			}

			if (Fetch_Stage() < ARRAY_SIZE(Class->Harvester_Load_List)) return(1);
			if (!Harvesting()) {
				IsHarvesting = false;
				if (Tiberium_Load() == 1) {
					Status = FINDHOME;
				} else {
					if (!Goto_Tiberium(Rule.TiberiumShortScan / CELL_LEPTON_W) && !Target_Legal(NavCom))	{
					  	ArchiveTarget = TARGET_NONE;
						Status = FINDHOME;
					} else {
						Status = HARVESTING;
						IsHarvesting = true;
					}
				}
				return(1);
			} else if (!Target_Legal(NavCom) && ArchiveTarget == TARGET_NONE) {
				ArchiveTarget = ::As_Target(Coord_Cell(Coord));
			}
			return(1);
//			return(TICKS_PER_SECOND*Rule.OreDumpRate);

		/*
		**	Find and head to refinery.
		*/
		case FINDHOME:
			if (!Target_Legal(NavCom)) {

				/*
				**	Find best refinery.
				*/
				BuildingClass * nearest = Find_Best_Refinery();

				/*
				**	Since the refinery said it was ok to load, establish radio
				**	contact with the refinery and then await docking orders.
				*/
				if (nearest != NULL && Transmit_Message(RADIO_HELLO, nearest) == RADIO_ROGER) {
					Status = HEADINGHOME;
					if (nearest->House == PlayerPtr && (PlayerPtr->Capacity - PlayerPtr->Tiberium) < 300 && PlayerPtr->Capacity > 500 && (PlayerPtr->ActiveBScan & (STRUCTF_REFINERY | STRUCTF_CONST))) {
						Speak(VOX_NEED_MO_CAPACITY);
					}
				} else {
					ScenarioInit++;
					nearest = Find_Best_Refinery();
					ScenarioInit--;
					if (nearest != NULL) {
						Assign_Destination(::As_Target(Nearby_Location(nearest)));
					}
				}
			}
			break;

		/*
		**	In communication with refinery so that it will successfully dock and
		**	unload. If, for some reason, radio contact was lost, then hunt for
		**	another refinery to unload at.
		*/
		case HEADINGHOME:
			Assign_Mission(MISSION_ENTER);
			return(1);

		/*
		**	The harvester has nothing to do. There is no Tiberium nearby and
		**	no where to go.
		*/
		case GOINGTOIDLE:
			if (IsUseless) {
				if (House->ActiveBScan & STRUCTF_REPAIR) {
					Assign_Mission(MISSION_REPAIR);
				} else {
					Assign_Mission(MISSION_HUNT);
				}
			}
			Assign_Mission(MISSION_GUARD);
			break;

	}
	return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2));
}


/***********************************************************************************************
 * UnitClass::Mission_Hunt -- This is the AI process for aggressive enemy units.               *
 *                                                                                             *
 *    Computer controlled units must be intelligent enough to find enemies as well as to       *
 *    attack them. This AI process will handle both the simple attack process as well as the   *
 *    scanning for enemy units to attack.                                                      *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  Returns with the delay before calling this routine again.                          *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   07/18/1994 JLB : Created.                                                                 *
 *=============================================================================================*/
int UnitClass::Mission_Hunt(void)
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	if (*this == UNIT_MCV) {
		enum {
			FIND_SPOT,
			WAITING
		};

		switch (Status) {

			/*
			**	This stage handles locating a convenient spot, rotating to face the correct
			**	direction and then commencing the deployment operation.
			*/
			case FIND_SPOT:
				if (Goto_Clear_Spot()) {
					if (Try_To_Deploy()) {
						Status = WAITING;
					}
				}
				break;

			/*
			**	This stage watchdogs the deployment operation and if for some reason, the deployment
			**	is aborted (the IsDeploying flag becomes false), then it reverts back to hunting for
			**	a convenient spot to deploy.
			*/
			case WAITING:
				if (!IsDeploying) {
					Status = FIND_SPOT;
				}
				break;
		}
	} else {

		return(DriveClass::Mission_Hunt());
	}
	return(MissionControl[Mission].Normal_Delay()+Random_Pick(0, 2));
}


/***********************************************************************************************
 * UnitClass::Overlap_List -- Determines overlap list for units.                               *
 *                                                                                             *
 *    The unit overlap list is used to keep track of which cells are to                        *
 *    be marked for redraw. This is critical in order to keep the units                        *
 *    displayed correctly.                                                                     *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  Returns with the overlap list pointer for the unit at its                          *
 *          present position.                                                                  *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   05/26/1994 JLB : Created.                                                                 *
 *   06/19/1994 JLB : Uses Coord_Spillable_List function.                                      *
 *=============================================================================================*/
short const * UnitClass::Overlap_List(bool redraw) const
{
	assert(Units.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 (Is_Selected_By_Player()) {
			rect = Union(rect, Rect(-15, -15, 30, 30));
		}

		if (Class->IsTurretEquipped || Class->IsRadarEquipped) {
			rect = Union(rect, Rect(-15, -15, 30, 30));
		}

		return(Coord_Spillage_List(Coord, rect, true));
	}
#else
	redraw = redraw;
#endif

	int size = ICON_PIXEL_W;

	if (redraw && (Is_Selected_By_Player() || IsFiring)) {
		size += 24;
	}
	if (Class->IsGigundo || IsAnimAttached) {
		size = ICON_PIXEL_W*2;
	}

	return(Coord_Spillage_List(Coord, size)+1);
}


/***********************************************************************************************
 * UnitClass::Can_Enter_Cell -- Determines cell entry legality.                                *
 *                                                                                             *
 *    Use this routine to determine if the unit can enter the cell                             *
 *    specified and given the direction of entry specified. Typically,                         *
 *    this is used when determining unit travel path.                                          *
 *                                                                                             *
 * INPUT:   cell     -- The cell to examine.                                                   *
 *                                                                                             *
 *          facing   -- The facing that the unit would enter the specified                     *
 *                      cell. If this value is -1, then don't consider                         *
 *                      facing when performing the check.                                      *
 *                                                                                             *
 * OUTPUT:  Returns the reason why it couldn't enter the cell or MOVE_OK if movement is        *
 *          allowed.                                                                           *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   09/07/1992 JLB : Created.                                                                 *
 *   04/16/1994 JLB : Converted to member function.                                            *
 *   07/04/1995 JLB : Allowed to drive on building trying to enter it.                         *
 *=============================================================================================*/
MoveType UnitClass::Can_Enter_Cell(CELL cell, FacingType ) const
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	bool cancrush = false;

	CellClass const * cellptr = &Map[cell];

	if ((unsigned)cell >= MAP_CELL_TOTAL) return(MOVE_NO);

	/*
	**	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() && IsLocked) {
		return(MOVE_NO);
	}

	MoveType retval = MOVE_OK;

	/*
	**	Certain vehicles can drive over walls. Check for this case and
	**	and return the appropriate flag. Other units treat walls as impassable.
	*/
	if (cellptr->Overlay != OVERLAY_NONE) {
		OverlayTypeClass const * optr = &OverlayTypeClass::As_Reference(cellptr->Overlay);

		if (optr->IsCrate && !((Session.Type == GAME_NORMAL) ? House->IsPlayerControl : House->IsHuman) && Session.Type == GAME_NORMAL) {
			return(MOVE_NO);
		}

		if (optr->IsWall) {

			/*
			**	If the blocking wall is crushable (and not owned by this player or one of this players
			**	allies, then record that it is crushable and let the normal logic take over. The end
			**	result should cause this unit to consider the cell passable.
			*/
			if (optr->IsCrushable && Class->IsCrusher) {
				cancrush = !House->Is_Ally(cellptr->Owner);
			}

			if (!cancrush && Is_Weapon_Equipped()) {
				WarheadTypeClass const * whead = Class->PrimaryWeapon->WarheadPtr;

				if (whead->IsWallDestroyer || (whead->IsWoodDestroyer && optr->IsWooden)) {
//					if (!House->IsHuman && !House->Is_Ally(cellptr->Owner)) {
						if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE;
//					} else {
//						return(MOVE_NO);
//					}
				} else {
					return(MOVE_NO);
				}
			}
		}
	}

	/*
	** Loop through all of the objects in the square setting a bit
	** for how they affect movement.
	*/
	bool crushable = false;
	ObjectClass * obj = cellptr->Cell_Occupier();
	while (obj != NULL) {

		if (obj != this) {

			/*
			** If object is a land mine, allow movement if possible.
			*/
			if (obj->What_Am_I() == RTTI_BUILDING && (!Rule.IsMineAware || !((BuildingClass *)obj)->House->Is_Ally(House))) {
				if ((*(BuildingClass *)obj) == STRUCT_APMINE) return(MOVE_OK);
				if ((*(BuildingClass *)obj) == STRUCT_AVMINE) return(MOVE_OK);
			}

			/*
			**	Always allow entry if trying to move on a cell with
			**	authorization from the occupier.
			*/
			if (obj == Contact_With_Whom() && (IsTethered || (obj->What_Am_I() ==  RTTI_BUILDING && *((BuildingClass *)obj) == STRUCT_REPAIR))) {
				return(MOVE_OK);
			}

			/*
			**	Special check to allow entry into the sea transport this vehicle
			**	is trying to enter.
			*/
			if (Mission == MISSION_ENTER && obj->As_Target() == NavCom && IsTethered) {
				return(MOVE_OK);
			}

			/*
			**	Guard area should not allow the guarding unit to enter the cell with the
			**	guarded unit.
			*/
			if (Mission == MISSION_GUARD_AREA && ArchiveTarget == obj->As_Target()) {
				return(MOVE_NO);
			}

			bool is_moving = obj->Is_Foot() &&
						(Target_Legal(((FootClass *)obj)->NavCom) || ((FootClass *)obj)->PrimaryFacing.Is_Rotating() || ((FootClass *)obj)->IsDriving);
//						(((FootClass *)obj)->PrimaryFacing.Is_Rotating() || ((FootClass *)obj)->IsDriving);
//						(((FootClass *)obj)->IsRotating || ((FootClass *)obj)->IsDriving);
//						(Target_Legal(((FootClass *)obj)->NavCom) || ((FootClass *)obj)->IsDriving);

			if (House->Is_Ally(obj)) {
				if (is_moving) {
					int face 		= Dir_Facing(PrimaryFacing);
					int techface	= Dir_Facing(((FootClass const *)obj)->PrimaryFacing) ^4;
					if (face == techface && Distance((AbstractClass const *)obj) <= 0x1FF) {
						return(MOVE_NO);
					}
					if (retval < MOVE_MOVING_BLOCK) retval = MOVE_MOVING_BLOCK;
				} else {
					if (obj->What_Am_I() == RTTI_BUILDING) return(MOVE_NO);

					/*
					**	If the blocking object is not in the same zone, then it certainly
					**	isn't a temporary block, it is a permanent one.
					*/
					if (Map[Coord].Zones[Class->MZone] != cellptr->Zones[Class->MZone]) return(MOVE_NO);

					if (retval < MOVE_TEMP) retval = MOVE_TEMP;
				}
			} else {

				/*
				**	Cloaked enemy objects are not considered if this is a Find_Path()
				**	call.
				*/
				if (!obj->Is_Techno() || !((TechnoClass *)obj)->Is_Cloaked(this)) {

					/*
					**	If this unit can crush infantry, and there is an enemy infantry in the
					**	cell, don't consider the cell impassible. This is true even if the unit
					**	doesn't contain a legitimate weapon.
					*/
					bool crusher = Class->IsCrusher;
					if (!crusher || !obj->Class_Of().IsCrushable) {

						/*
						**	Any non-allied blockage is considered impassable if the unit
						**	is not equipped with a weapon.
						*/
						if (Class->PrimaryWeapon == NULL) return(MOVE_NO);

						/*
						**	Some kinds of terrain are considered destroyable if the unit is equipped
						**	with the weapon that can destroy it. Otherwise, the terrain is considered
						**	impassable.
						*/
						switch (obj->What_Am_I()) {
							case RTTI_TERRAIN:

#ifdef TOFIX
								if (((TerrainClass *)obj)->Class->Armor == ARMOR_WOOD &&
										Class->PrimaryWeapon->WarheadPtr->IsWoodDestroyer) {

									if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE;
								} else {
									return(MOVE_NO);
								}
								break;
#else
								return(MOVE_NO);
#endif

							default:
								if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE;
								break;
						}
					} else {
						crushable = true;
					}
				} else {
					if (retval < MOVE_CLOAK) retval = MOVE_CLOAK;
				}
			}
		}

		/*
		**	Move to next object in chain.
		*/
		obj = obj->Next;
	}

	/*
	**	If the cell is out and out impassable because of underlying terrain, then
	**	return this immutable fact.
	*/
	if (!cancrush && retval != MOVE_DESTROYABLE && 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 (retval == MOVE_OK && !crushable && (cellptr->Flag.Composite & 0x3F) != 0) {

		/*
		**	If reserved by a vehicle, then consider this blocked terrain.
		*/
		if (cellptr->Flag.Occupy.Vehicle) {
			retval = MOVE_MOVING_BLOCK;
		} else {
			if (cellptr->InfType != HOUSE_NONE && House->Is_Ally(cellptr->InfType)) {
				retval = MOVE_MOVING_BLOCK;
			} else {

				/*
				**	Enemy infantry have reserved the cell. If this unit can crush
				**	infantry, consider the cell passable. If not, then consider the
				**	cell destroyable if it has a weapon. If neither case applies, then
				**	this vehicle should avoid the cell altogether.
				*/
				if (!Class->IsCrusher) {
					if (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->Bullet->IsAntiGround) {
						retval = MOVE_DESTROYABLE;
					} else {
						return(MOVE_NO);
					}
				}
			}
		}
	}

	/*
	** If its ok to move into the cell because we can crush whats in the cell, then
	** make sure no one else is already moving into the cell to crush something.
	*/
	if (retval == MOVE_OK && crushable && cellptr->Flag.Occupy.Vehicle) {

		/*
		**	However, if the cell is occupied by a crushable vehicle, then we can
		**	never be sure if some other friendly vehicle is also trying to crush
		**	the cell at the same time. In the case of a crushable vehicle in the
		**	cell, then allow entry.
		*/
		if (!cellptr->Cell_Unit() || !cellptr->Cell_Unit()->Class->IsCrushable) {
			return(MOVE_MOVING_BLOCK);
		}
	}

	/*
	**	Return with the most severe reason why this cell would be impassable.
	*/
	return(retval);
}


/***********************************************************************************************
 * UnitClass::Init -- Clears all units for scenario preparation.                               *
 *                                                                                             *
 *    This routine will zero out the unit list and unit objects. This routine is typically     *
 *    used in preparation for a new scenario load. All units are guaranteed to be eliminated   *
 *    by this routine.                                                                         *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/15/1994 JLB : Created.                                                                 *
 *=============================================================================================*/
void UnitClass::Init(void)
{
	Units.Free_All();
}


/***********************************************************************************************
 * UnitClass::Start_Driver -- Starts driving and reserves destination cell.                    *
 *                                                                                             *
 *    This routine will start the vehicle moving by marking the destination cell as            *
 *    reserved. Cells must be reserved in this fashion or else they might become occupied as   *
 *    the vehicle is proceeding toward it.                                                     *
 *                                                                                             *
 * INPUT:   headto   -- The location where the vehicle will be heading.                        *
 *                                                                                             *
 * OUTPUT:  bool; Was the vehicle started to move? Failure could be the result of the cell     *
 *                being occupied.                                                              *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   12/22/1994 JLB : Created.                                                                 *
 *=============================================================================================*/
bool UnitClass::Start_Driver(COORDINATE & headto)
{
	assert(Units.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);
}


/***********************************************************************************************
 * UnitClass::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:                                                                                    *
 *   01/11/1995 JLB : Created.                                                                 *
 *=============================================================================================*/
ActionType UnitClass::What_Action(ObjectClass const * object) const
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	ActionType action = DriveClass::What_Action(object);

	/*
	** Allow units to move onto land mines.
	*/
	if (action == ACTION_NONE && object->What_Am_I() == RTTI_BUILDING) {
		StructType blah = *((BuildingClass *)object);
		if (blah == STRUCT_AVMINE || blah == STRUCT_APMINE) return(ACTION_MOVE);
	}

	/*
	**	If the unit doesn't have a weapon, but can crush the object, then consider
	**	the object as a movable location.
	*/
	if (action == ACTION_ATTACK && !Can_Player_Fire()) {
		if (Class->IsCrusher && object->Class_Of().IsCrushable) {
			action = ACTION_MOVE;
		} else {
			action = ACTION_SELECT;
		}
	}

	/*
	**	Don't allow special deploy action unless there is something to deploy.
	*/
	if (action == ACTION_SELF) {
		if (*this == UNIT_MCV) {

			/*
			**	The MCV will get the no-deploy cursor if it couldn't
			**	deploy at its current location.
			*/
			((ObjectClass &)(*this)).Mark(MARK_UP);
			if (!BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(Coord_Cell(Adjacent_Cell(Center_Coord(), FACING_NW)))) {
				action = ACTION_NO_DEPLOY;
			}
			((ObjectClass &)(*this)).Mark(MARK_DOWN);

		} else {

			/*
			**	The mine layer can "deploy" its mines if it currently isn't
			**	sitting on top of a mine and it still has mines available.
			*/
			if (*this == UNIT_MINELAYER) {
				if (!Ammo || Map[Center_Coord()].Cell_Building() || (Map[Center_Coord()].Smudge != SMUDGE_NONE && SmudgeTypeClass::As_Reference(Map[Center_Coord()].Smudge).IsBib)) {
					action = ACTION_NO_DEPLOY;
				}
			} else {
#ifdef FIXIT_CSII	//	checked - ajw 9/28/98
				if (*this == UNIT_CHRONOTANK || *this == UNIT_MAD) {
					if (*this == UNIT_CHRONOTANK) {
// If the chrono tank's counter is still charging up, don't allow deploy.  Or,
// if it's a player-controlled chrono tank, and the player's currently trying
// to teleport a different unit, don't allow teleporting this unit.
						if(MoebiusCountDown || (IsOwnedByPlayer && House->UnitToTeleport && Map.IsTargettingMode == SPC_CHRONO2)) {
							action = ACTION_NO_DEPLOY;
						}
					}
				} else {
#endif
				/*
				**	All other units can "deploy" their passengers if they in-fact have
				**	passengers and are a transport vehicle. Otherwise, they cannot
				**	perform any self action.
				*/
				if (Class->Max_Passengers() > 0) {
					if (How_Many() == 0) {
						action = ACTION_NO_DEPLOY;
					}
				} else {
					action = ACTION_NONE;
				}
#ifdef FIXIT_CSII	//	checked - ajw 9/28/98
				}
#endif
			}
		}
	}

	/*
	**	Special return to friendly refinery action.
	*/
	bool is_player_controlled = (Session.Type == GAME_NORMAL)
		? (House->IsPlayerControl && object->Owner() != HOUSE_NONE && HouseClass::As_Pointer(object->Owner())->IsPlayerControl)
		: (Is_Owned_By_Player() && House->Class->House == object->Owner());
	if (is_player_controlled && object->What_Am_I() == RTTI_BUILDING && ((UnitClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)object) == RADIO_ROGER) {
		action = ACTION_ENTER;
	}

	/*
	**	Special return to friendly repair factory action.
	*/
	if (is_player_controlled && action == ACTION_SELECT && object->What_Am_I() == RTTI_BUILDING) {
		BuildingClass * building = (BuildingClass *)object;
		if (building->Class->Type == STRUCT_REPAIR && ((UnitClass *)this)->Transmit_Message(RADIO_CAN_LOAD, building) == RADIO_ROGER && !building->In_Radio_Contact() && !building->Is_Something_Attached()) {
			action = ACTION_MOVE;
		}
	}

	/*
	**	Check to see if it can enter a transporter.
	*/
	if (
		House->Is_Ally(object) &&
		House->IsPlayerControl && object->Is_Techno() && object->What_Am_I() == RTTI_VESSEL) {
#ifdef FIXIT_CARRIER	//	checked - ajw 9/28/98
 if( *(VesselClass *)object != VESSEL_CARRIER) {
#endif
		switch (((UnitClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)object)) {
			case RADIO_ROGER:
				action = ACTION_ENTER;
				break;

			case RADIO_NEGATIVE:
				action = ACTION_NO_ENTER;
				break;

			default:
				action = ACTION_NONE;
				break;
		}
#ifdef FIXIT_CARRIER	//	checked - ajw 9/28/98
 }
#endif
	}

#ifdef FIXIT_CSII	//	checked - ajw 9/28/98
	if (*this == UNIT_MAD && (IsDumping || Gold)) {
		action = ACTION_NONE;
	}
#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);
}


/***********************************************************************************************
 * UnitClass::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. At the unit level, only the harvester is checked for. The lower          *
 *    classes determine the regular action response.                                           *
 *                                                                                             *
 * 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:                                                                                    *
 *   09/21/1995 JLB : Created.                                                                 *
 *=============================================================================================*/
ActionType UnitClass::What_Action(CELL cell) const
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	ActionType action = DriveClass::What_Action(cell);
	if (action == ACTION_MOVE && Map[cell].Land_Type() == LAND_TIBERIUM && Class->IsToHarvest) {
		return(ACTION_HARVEST);
	}
#ifdef FIXIT_CSII	//	checked - ajw 9/28/98
	if (*this == UNIT_MAD && (IsDumping || Gold)) {
		action = ACTION_NOMOVE;
	}
#endif
	return(action);
}


/***********************************************************************************************
 * UnitClass::Exit_Repair -- Drive the unit off the repair facility.                           *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   04/03/1995 BWG : Created.                                                                 *
 *=============================================================================================*/
#define XYCELL(x, y)	(y*MAP_CELL_W+x)
void UnitClass::Exit_Repair(void)
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	int	i;
	CELL	cell;
	bool	found = false;
	static short const ExitRepair[] = {
		XYCELL(0,	-2),
		XYCELL(1,	-1),
		XYCELL(2,	0),
		XYCELL(1,	1),
		XYCELL(0,	2),
		XYCELL(-1,	1),
		XYCELL(-2,	0),
		XYCELL(-1,	-1)
	};

	cell = Coord_Cell(Coord) + ExitRepair[Dir_Facing(PrimaryFacing.Current())];
	if (Can_Enter_Cell(cell) == MOVE_OK) found = true;

	if (!found) for (i=0; i<8; i++) {
		cell = Coord_Cell(Coord) + ExitRepair[i];
		if (Can_Enter_Cell(cell) == MOVE_OK) {
			found = true;
			break;
		}
	}
	if (found) {
//		DirType	dir = Direction(cell);

		Assign_Mission(MISSION_MOVE);
		Assign_Destination(::As_Target(cell));
	}
}


/***********************************************************************************************
 * UnitClass::Mission_Guard -- Special guard mission override processor.                       *
 *                                                                                             *
 *    Handles the guard mission for the unit. If the IQ is high enough and the unit is         *
 *    a harvester, it will begin to harvest automatically. An MCV might autodeploy.            *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  Returns the time delay before this command is executed again.                      *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   05/08/1995 JLB : Created.                                                                 *
 *   05/08/1995 JLB : Fixes gunboat problems.                                                  *
 *=============================================================================================*/
int UnitClass::Mission_Guard(void)
{
	assert(Units.ID(this) == ID);
	assert(IsActive);
	if (/*House->IsBaseBuilding &&*/ !House->IsHuman && Class->IsToHarvest && House->Get_Quantity(STRUCT_REFINERY) > 0 && !House->IsTiberiumShort) {
		Assign_Mission(MISSION_HARVEST);
		return(1);
//		return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2));
	}

	if (*this == UNIT_MCV && House->IsBaseBuilding) {
		Assign_Mission(MISSION_UNLOAD);
		return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2));
	}
	return(DriveClass::Mission_Guard());
}


/***********************************************************************************************
 * UnitClass::Mission_Move -- Handles special move mission overrides.                          *
 *                                                                                             *
 *    This routine intercepts the normal move mission and if a gunboat is being processed,     *
 *    changes its mission to hunt. This is an attempt to keep the gunboat on the hunt mission  *
 *    regardless of what the player did.                                                       *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  Returns the number of ticks before this routine should be called again.            *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   05/09/1995 JLB : Created.                                                                 *
 *   09/28/1995 JLB : Harvester stick in guard mode if no more refineries.                     *
 *=============================================================================================*/
int UnitClass::Mission_Move(void)
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	IsHarvesting = false;

	/*
	**	Always make sure that that transport door is closed if the vehicle is moving.
	*/
	if (!Is_Door_Closed()) {
		APC_Close_Door();
	}

	return(DriveClass::Mission_Move());
}


int UnitClass::Mission_Enter(void)
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	if (Class->IsToHarvest) {
		TechnoClass * contact = Contact_With_Whom();
		if (contact == NULL) {
			contact = As_Techno(ArchiveTarget);
		}
		if (contact != NULL &&
			contact->What_Am_I() == RTTI_BUILDING &&
			*((BuildingClass*)contact) == STRUCT_REFINERY) {
			TiberiumUnloadRefinery = contact->As_Target();
		}
	}

	return(DriveClass::Mission_Enter());
}


/***********************************************************************************************
 * UnitClass::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:                                                                                    *
 *   05/23/1995 JLB : Created.                                                                 *
 *=============================================================================================*/
DirType UnitClass::Desired_Load_Dir(ObjectClass * passenger, CELL & moveto) const
{
	assert(Units.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;
				}
			}
		}

		/*
		**	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;

		/*
		**	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;
		}
	}

	/*
	**	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_S);
}


/***********************************************************************************************
 * UnitClass::Flag_Attach -- Attaches a house flag to this unit.                               *
 *                                                                                             *
 *    This routine will attach a house flag to this unit.                                      *
 *                                                                                             *
 * INPUT:   house -- The house that is having its flag attached to it.                         *
 *                                                                                             *
 * OUTPUT:  Was the house flag successfully attached to this unit?                             *
 *                                                                                             *
 * WARNINGS:   A unit can only carry one flag at a time. This might be a reason for failure    *
 *             of this routine.                                                                *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   05/23/1995 JLB : Created.                                                                 *
 *=============================================================================================*/
bool UnitClass::Flag_Attach(HousesType house)
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	if (house != HOUSE_NONE && Flagged == HOUSE_NONE)	{
		Flagged = house;
		Mark(MARK_CHANGE);
		return(true);
	}
	return(false);
}


/***********************************************************************************************
 * UnitClass::Flag_Remove -- Removes the house flag from this unit.                            *
 *                                                                                             *
 *    This routine will remove the house flag that is attached to this unit.                   *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  Was the flag successfully removed?                                                 *
 *                                                                                             *
 * WARNINGS:   This routine doesn't put the flag into a new location. That operation must      *
 *             be performed or else the house flag will cease to exist.                        *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   05/23/1995 JLB : Created.                                                                 *
 *=============================================================================================*/
bool UnitClass::Flag_Remove(void)
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	if (Flagged != HOUSE_NONE) {
		Flagged = HOUSE_NONE;
		Mark(MARK_CHANGE);
		return(true);
	}
	return(false);
}


/***********************************************************************************************
 * UnitClass::Pip_Count -- Fetches the number of pips to display on unit.                      *
 *                                                                                             *
 *    This routine is used to fetch the number of "fullness" pips to display on the unit.      *
 *    This will either be the number of passengers or the percentage full (in 1/5ths) of       *
 *    a harvester.                                                                             *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  Returns with the number of pips to draw on this unit.                              *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   06/25/1995 JLB : Created.                                                                 *
 *=============================================================================================*/
int UnitClass::Pip_Count(void) const
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	if (Class->Max_Passengers() > 0) {
		return(How_Many());
	}

	if (*this == UNIT_MINELAYER) {
		int retval = 0;
		if (Ammo > 0) {
			retval = Class->Max_Pips() * fixed(Ammo, Class->MaxAmmo);
			if (!retval) retval = 1;
		}
		return(retval);
	}

	if (*this == UNIT_HARVESTER) {
		return((Gold + Gems) / 4);
	}

#ifdef FIXIT_CSII	//	checked - ajw 9/28/98
	if (*this == UNIT_CHRONOTANK) {
		int fulldur = ChronoTankDuration * TICKS_PER_MINUTE;
		return( (fulldur - MoebiusCountDown) / (fulldur / 5));
	}
#endif
	return(0);
}


/***********************************************************************************************
 * UnitClass::APC_Close_Door -- Closes an APC door.                                            *
 *                                                                                             *
 *    This routine will initiate closing of the APC door.                                      *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   06/25/1995 JLB : Created.                                                                 *
 *=============================================================================================*/
void UnitClass::APC_Close_Door(void)
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	Close_Door(10, 2);
}


/***********************************************************************************************
 * UnitClass::APC_Open_Door -- Opens an APC door.                                              *
 *                                                                                             *
 *    This routine will initiate opening of the APC door.                                      *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   06/25/1995 JLB : Created.                                                                 *
 *=============================================================================================*/
void UnitClass::APC_Open_Door(void)
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	if (!IsDriving && !IsRotating) {
		if (PrimaryFacing == DIR_NW || PrimaryFacing == DIR_NE) {
			Open_Door(10, 2);
		} else {
			Open_Door(1, 2);
		}
	}
}


/***********************************************************************************************
 * UnitClass::Crew_Type -- Fetches the kind of crew that this object produces.                 *
 *                                                                                             *
 *    When a unit is destroyed, a crew member might be generated. This routine will return     *
 *    with the infantry type to produce for this unit. This routine will be called for every   *
 *    survivor that is generated.                                                              *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  Returns with a suggested infantry type to generate as a survivor from this unit.   *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   08/13/1995 JLB : Created.                                                                 *
 *=============================================================================================*/
InfantryType UnitClass::Crew_Type(void) const
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	if (Class->PrimaryWeapon == NULL) {
		if (Percent_Chance(50)) {
			return(INFANTRY_C1);
		} else {
			return(INFANTRY_C7);
		}
	}
	return(DriveClass::Crew_Type());
}


/***********************************************************************************************
 * UnitClass::Mission_Repair -- Handles finding and proceeding on a repair mission.            *
 *                                                                                             *
 *    This mission handler will look for a repair facility. If one is found then contact       *
 *    is established and then the normal Mission_Enter logic is performed. The repair facility *
 *    will take over the actual repair coordination process.                                   *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  Returns the number of game frames to delay before calling this routine again.      *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   10/02/1995 JLB : Created.                                                                 *
 *=============================================================================================*/
int UnitClass::Mission_Repair(void)
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	BuildingClass * nearest = Find_Docking_Bay(STRUCT_REFINERY, true);

	IsHarvesting = false;

	/*
	**	If there is no available repair facility, then check to see if there
	**	are any repair facilities at all. If not, then enter this unit
	**	into idle state.
	*/
	if (nearest == NULL) {
		if (!(House->ActiveBScan & STRUCTF_REFINERY)) {
			Enter_Idle_Mode();
		}
	} else {

		/*
		**	Try to establish radio contact with the repair facility. If contact
		**	was established, then proceed with normal enter mission, which handles
		**	the repair process.
		*/
		if (Transmit_Message(RADIO_HELLO, nearest) == RADIO_ROGER) {
			Assign_Mission(MISSION_ENTER);
			return(1);
		}
	}

	/*
	**	If no action could be performed at this time, then wait
	**	around for a bit before trying again.
	*/
	return(MissionControl[Mission].Normal_Delay());
}


/***********************************************************************************************
 * UnitClass::Fire_Direction -- Determines the direction of firing.                            *
 *                                                                                             *
 *    This routine will return with the facing that a projectile will travel if it was         *
 *    fired at this instant. The facing should match the turret facing for those units         *
 *    equipped with a turret. If the unit doesn't have a turret, then it will be the facing    *
 *    of the body.                                                                             *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  Returns with the default firing direction for a projectile.                        *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   06/25/1995 JLB : Created.                                                                 *
 *=============================================================================================*/
DirType UnitClass::Fire_Direction(void) const
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	if (Class->IsTurretEquipped) {
		if (*this == UNIT_V2_LAUNCHER) {
			int diff1 = SecondaryFacing.Difference(DIR_E);
			int diff2 = SecondaryFacing.Difference(DIR_W);
			diff1 = ABS(diff1);
			diff2 = ABS(diff2);
			int diff = min(diff1, diff2);
			int adj = Fixed_To_Cardinal(ABS(SecondaryFacing.Difference(DIR_N)), 64-diff);
			if (SecondaryFacing.Difference(DIR_N) < 0) {
				return(DirType)(SecondaryFacing - (DirType)adj);
			} else {
				return(DirType)(SecondaryFacing + (DirType)adj);
			}
		}
		return(SecondaryFacing.Current());
	}

	return(DriveClass::Fire_Direction());
}


/***********************************************************************************************
 * UnitClass::Ok_To_Move -- Queries whether the vehicle can move.                              *
 *                                                                                             *
 *    This virtual routine is used to determine if the vehicle is allowed                      *
 *    to start moving. It is typically called when the vehicle desires                         *
 *    to move but needs confirmation from the turret logic before                              *
 *    proceeding. This happens when dealing with a vehicle that must have                      *
 *    its turret face the same direction as the body before the vehicle                        *
 *    may begin movement.                                                                      *
 *                                                                                             *
 * INPUT:   dir      -- The facing the unit wants to travel in.                                *
 *                                                                                             *
 * OUTPUT:  bool; Can the unit begin forward movement now?                                     *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   05/12/1994 JLB : Created.                                                                 *
 *=============================================================================================*/
bool UnitClass::Ok_To_Move(DirType dir) const
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	if (Class->IsLockTurret) {
		if (IsRotating) {
			return(false);
		} else {
			if (SecondaryFacing.Difference(dir)) {
				((UnitClass *)this)->SecondaryFacing.Set_Desired(dir);
				return(false);
			}
		}
	}
	return(true);
}


/***********************************************************************************************
 * UnitClass::Can_Fire -- Determines if turret can fire upon target.                           *
 *                                                                                             *
 *    This routine determines if the turret can fire upon the target                           *
 *    specified.                                                                               *
 *                                                                                             *
 * INPUT:   target   -- The target to fire upon.                                               *
 *                                                                                             *
 *          which    -- Which weapon to use to determine legality to fire. 0=primary,          *
 *                      1=secondary.                                                           *
 *                                                                                             *
 * OUTPUT:  Returns the fire status type that indicates if firing is allowed and if not, why.  *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   04/26/1994 JLB : Created.                                                                 *
 *   06/01/1994 JLB : Returns reason why it can't fire.                                        *
 *=============================================================================================*/
FireErrorType UnitClass::Can_Fire(TARGET target, int which) const
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	DirType			dir;					// The facing to impart upon the projectile.
	int				diff;
	FireErrorType	fire = DriveClass::Can_Fire(target, which);

	if (fire == FIRE_OK) {
		WeaponTypeClass const * weapon = (which == 0) ? Class->PrimaryWeapon : Class->SecondaryWeapon;

		/*
		**	If this unit cannot fire while moving, then bail.
		*/
		if ((Class->IsNoFireWhileMoving /*!Class->IsTurretEquipped || Class->IsLockTurret*/) && 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);
		}

		dir = Direction(target);

		/*
		**	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(DriveClass::Can_Fire(target, which));
		}
		return(FIRE_FACING);
	}
	return(fire);
}


/***********************************************************************************************
 * UnitClass::Fire_At -- Try to fire upon the target specified.                                *
 *                                                                                             *
 *    This routine is the auto-fire logic for the turret. It will check                        *
 *    to see if firing is technically legal given the specified target.                        *
 *    If it is legal to fire, it does so. It is safe to call this routine                      *
 *    every game tick.                                                                         *
 *                                                                                             *
 * 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 * UnitClass::Fire_At(TARGET target, int which)
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	BulletClass * bullet = NULL;
	WeaponTypeClass const * weap = (which == 0) ? Class->PrimaryWeapon : Class->SecondaryWeapon;
	if (weap == NULL) return(NULL);

	if (Can_Fire(target, which) == FIRE_OK) {
		bullet = DriveClass::Fire_At(target, which);

		if (bullet != NULL) {
#ifdef FIXIT_CSII	//	checked - ajw 9/28/98
if(Class->Type == UNIT_DEMOTRUCK && IsActive) delete this;
#endif
			/*
			**	Possible reload timer set.
			*/
			if ((*this == UNIT_V2_LAUNCHER) && Reload == 0) {
				Reload = TICKS_PER_SECOND * 30;
			}
		}
	}

	return(bullet);
}


/***********************************************************************************************
 * UnitClass::Class_Of -- Fetches a reference to the class type for this object.               *
 *                                                                                             *
 *    This routine will fetch a reference to the TypeClass of this object.                     *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  Returns with reference to the type class of this object.                           *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   07/29/1995 JLB : Created.                                                                 *
 *=============================================================================================*/
ObjectTypeClass const & UnitClass::Class_Of(void) const
{
	assert(Units.ID(this) == ID);
	assert(IsActive);

	return(*Class);
}


/***********************************************************************************************
 * UnitClass::Tiberium_Load -- Determine the Tiberium load as a percentage.                    *
 *                                                                                             *
 *    Use this routine to determine what the Tiberium load is (as a fixed point percentage).   *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  Returns with the current "fullness" rating for the object.                         *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   03/17/1995 JLB : Created.                                                                 *
 *=============================================================================================*/
fixed UnitClass::Tiberium_Load(void) const
{
	assert(IsActive);

	if (*this == UNIT_HARVESTER) {
		return(fixed(Tiberium, Rule.BailCount));
	}
	return(0);
}


BuildingClass* UnitClass::Find_Best_Refinery(void) const
{
	/*
	**	Remember our last refinery and prefer that one, if still available and valid.
	*/
	if (Target_Legal(TiberiumUnloadRefinery)) {
		BuildingClass * refinery = As_Building(TiberiumUnloadRefinery);
		if (refinery != NULL &&
			refinery->House == House &&
			!refinery->IsInLimbo &&
			refinery->Mission != MISSION_DECONSTRUCTION &&
			*refinery == STRUCT_REFINERY &&
			Map[refinery->Center_Coord()].Zones[Techno_Type_Class()->MZone] == Map[Center_Coord()].Zones[Techno_Type_Class()->MZone]) {
			return refinery;
		} else {
			TiberiumUnloadRefinery = TARGET_NONE;
		}
	}

	/*
	**	Find nearby refinery and head to it?
	*/
	return Find_Docking_Bay(STRUCT_REFINERY, false);
}


/***********************************************************************************************
 * UnitClass::Offload_Tiberium_Bail -- Offloads one Tiberium quantum from the object.          *
 *                                                                                             *
 *    This routine will offload one Tiberium packet/quantum/bail from the object. Multiple     *
 *    calls to this routine are needed in order to fully offload all Tiberium.                 *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  Returns with the number of credits offloaded for the one call. If zero is returned,*
 *          then this indicates that all Tiberium has been offloaded.                          *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   07/19/1995 JLB : Created.                                                                 *
 *=============================================================================================*/
int UnitClass::Offload_Tiberium_Bail(void)
{
	assert(IsActive);

	if (Tiberium) {
		Tiberium--;

// MBL 05.15.2020: Note, if the below code is ever reeanbled for some ready, make sure to see fix in 
// Tiberian Dawn's DriveClass::Offload_Tiberium_Bail() for AI players

#ifdef TOFIX
		if (House->IsHuman) {
			return(UnitTypeClass::FULL_LOAD_CREDITS/UnitTypeClass::STEP_COUNT);
		}
		return(UnitTypeClass::FULL_LOAD_CREDITS+(UnitTypeClass::FULL_LOAD_CREDITS/3)/UnitTypeClass::STEP_COUNT);
#endif
	}
	return(0);
}


/***********************************************************************************************
 * UnitClass::Approach_Target -- Handles approaching the target in order to attack it.         *
 *                                                                                             *
 *    This routine will check to see if the target is infantry and it can be overrun. It will  *
 *    try to overrun the infantry rather than attack it. This only applies to computer         *
 *    controlled vehicles. If it isn't the infantry overrun case, then it falls into the       *
 *    base class for normal (complex) approach algorithm.                                      *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   03/17/1995 JLB : Created.                                                                 *
 *   07/12/1995 JLB : Flamethrower tanks don't overrun -- their weapon is better.              *
 *=============================================================================================*/
void UnitClass::Approach_Target(void)
{
	assert(IsActive);

	/*
	**	Only if there is a legal target should the approach check occur.
	*/
	if (!House->IsHuman && Target_Legal(TarCom) && !Target_Legal(NavCom)) {

		/*
		**	Special case:
		**	If this is for a unit that can crush infantry, and the target is
		**	infantry, AND the infantry is pretty darn close, then just try
		**	to drive over the infantry instead of firing on it.
		*/
		TechnoClass * target = As_Techno(TarCom);
		if (Class->IsCrusher && Distance(TarCom) < Rule.CrushDistance && target && ((TechnoTypeClass const &)(target->Class_Of())).IsCrushable) {
			Assign_Destination(TarCom);
			return;
		}
	}

	/*
	**	In the other cases, uses the more complex "get to just within weapon range"
	**	algorithm.
	*/
	DriveClass::Approach_Target();
}


/***********************************************************************************************
 * DriveClass::Overrun_Square -- Handles vehicle overrun of a cell.                            *
 *                                                                                             *
 *    This routine is called when a vehicle enters a square or when it is about to enter a     *
 *    square (controlled by parameter). When a vehicle that can crush infantry enters a        *
 *    cell that contains infantry, then the infantry will be destroyed (regardless of          *
 *    affiliation). When a vehicle threatens to overrun a square, all occupying infantry       *
 *    will attempt to get out of the way.                                                      *
 *                                                                                             *
 * INPUT:   cell     -- The cell that is, or soon will be, entered by a vehicle.               *
 *                                                                                             *
 *          threaten -- Don't kill, but just threaten to enter the cell.                       *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   01/19/1995 JLB : Created.                                                                 *
 *=============================================================================================*/
void UnitClass::Overrun_Square(CELL cell, bool threaten)
{
	assert(IsActive);

	CellClass * cellptr = &Map[cell];

	if (Class->IsCrusher) {
		if (threaten) {

			/*
			**	If the cell contains infantry, then they will panic when a vehicle tries
			**	drive over them. Have the infantry run away instead.
			*/
			if (cellptr->Flag.Composite & 0x1F) {

				/*
				**	Scattering is controlled by the game difficulty level.
				*/
				cellptr->Incoming(0, true);
			}
		} else {
			ObjectClass * object = cellptr->Cell_Occupier();
			int crushed = false;
			while (object != NULL) {
				if (object->Class_Of().IsCrushable && !House->Is_Ally(object) && Distance(object->Center_Coord()) < CELL_LEPTON_W/2) {

#ifdef OBSOLETE
					/*
					** If we're running over infantry, let's see if the infantry we're
					** squashing is a thief trying to capture us.  If so, let him succeed.
					*/
					if (object->What_Am_I() == RTTI_INFANTRY && *((InfantryClass *)object) == INFANTRY_THIEF && ((InfantryClass *)object)->NavCom == As_Target()) {
						ObjectClass * next = object->Next;
						IsOwnedByPlayer = ((InfantryClass *)object)->IsOwnedByPlayer;
						House = ((InfantryClass *)object)->House;
						delete object;
						object = next;
					} else {
#endif
						ObjectClass * next = object->Next;
						crushed = true;

						/*
						** Record credit for the kill(s)
						*/
						Sound_Effect(VOC_SQUISH, Coord);
						if (object->Height == 0) {
							AnimClass* anim = new AnimClass(ANIM_CORPSE1, object->Center_Coord());
							if (anim != NULL) {
								anim->Set_Owner(object->Owner());
							}
						}
						object->Record_The_Kill(this);
						object->Mark(MARK_UP);
						object->Limbo();
						delete object;
						//new OverlayClass(OVERLAY_SQUISH, Coord_Cell(Coord));

						object = next;
#ifdef OBSOLETE
					}
#endif
				} else {
					object = object->Next;
				}
			}
			if (crushed) Do_Uncloak();
		}
	}
}


/***********************************************************************************************
 * UnitClass::Assign_Destination -- Assign a destination to a unit.                            *
 *                                                                                             *
 *    This will assign the specified destination to the unit. It is presumed that doing is     *
 *    is all that is needed in order to cause the unit to move to the specified destination.   *
 *                                                                                             *
 * INPUT:   target   -- The target (location) to move to.                                      *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   07/09/1996 JLB : Created.                                                                 *
 *=============================================================================================*/
void UnitClass::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()) {
		Transmit_Message(RADIO_OVER_OUT);
	}

	BuildingClass * b = As_Building(target);

	/*
	**	Handle entry logic here.
	*/
	if (Mission == MISSION_ENTER || MissionQueue == MISSION_ENTER) {

		/*
		**	If not already in radio contact (presumed with the transport), then
		**	either try to establish contact if allowed, or just move close and
		**	wait until radio contact can be established.
		*/
		if (!In_Radio_Contact()) {
			if (b != NULL) {

				/*
				**	Determine if the transport is already in radio contact. If so, then just move
				**	toward the transport and try to establish contact at a later time.
				*/
				if (b->In_Radio_Contact()) {
// TCTCTC -- call for an update from the transport to get a good rendezvous position.
					ArchiveTarget = target;

/*
**	HACK ALERT: The repair bay is counting on the assignment of the NavCom by this routine.
**	The refinery must NOT have the navcom assigned by this routine.
*/
if (*b != STRUCT_REPAIR) {
	target = TARGET_NONE;
}
				} else {
					if (Transmit_Message(RADIO_DOCKING, b) != RADIO_ROGER) {
						Transmit_Message(RADIO_OVER_OUT);
						if (*b == STRUCT_REPAIR) {
							ArchiveTarget = target;
						}
					}
if (*b != STRUCT_REPAIR) {
	ArchiveTarget = target;
	target = TARGET_NONE;
}
				}
			} else {
				TechnoClass * techno = As_Techno(target);
				if (techno != NULL) {

					/*
					**	Determine if the transport is already in radio contact. If so, then just move
					**	toward the transport and try to establish contact at a later time.
					*/
					if (techno->In_Radio_Contact()) {
	// TCTCTC -- call for an update from the transport to get a good rendezvous position.

						ArchiveTarget = target;
					} else {
						if (Transmit_Message(RADIO_HELLO, techno) == RADIO_ROGER) {
							if (Transmit_Message(RADIO_DOCKING) != RADIO_ROGER) {
								Transmit_Message(RADIO_OVER_OUT);
							} else {
								//BG: keep retransmitted navcom from radio-move-here.
								return;
							}
						}
					}
				}

			}
		} else {
			Path[0] = FACING_NONE;
		}
	} else {
		Path[0] = FACING_NONE;
	}

	/*
	**	If the player clicked on a friendly repair facility and the repair
	**	facility is currently not involved with some other unit (radio or unloading).
	*/
	if (b != NULL && *b == STRUCT_REPAIR) {
		if (b->In_Radio_Contact() && (b->Contact_With_Whom() != this) ) {
//			if (target != NULL) {
				ArchiveTarget = target;
//			}
//			target = TARGET_NONE;
		} else {

			/*
			**	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) {

				/*
				**	Last check to make sure that the loading square is free from permanent
				**	occupation (such as a building).
				*/
				CELL cell = (CELL)(Coord_Cell(b->Center_Coord()) + (MAP_CELL_W-1));
				if (Ground[Map[cell].Land_Type()].Cost[Techno_Type_Class()->Speed] > 0) {
					if (Transmit_Message(RADIO_DOCKING) == RADIO_ROGER) {
						FootClass::Assign_Destination(target);
						Path[0] = FACING_NONE;
						return;
					}

					/*
					**	Failure to establish a docking relationship with the refinery.
					**	Bail & await further instructions.
					*/
					Transmit_Message(RADIO_OVER_OUT);
				}
			}
		}
	}

	DriveClass::Assign_Destination(target);
}


/***********************************************************************************************
 * UnitClass::Greatest_Threat -- Fetches the greatest threat for this unit.                    *
 *                                                                                             *
 *    This routine will search the map looking for a good target to attack. It takes into      *
 *    consideration the type of weapon it is equipped with.                                    *
 *                                                                                             *
 * INPUT:   threat   -- The threat type to search for.                                         *
 *                                                                                             *
 * OUTPUT:  Returns with a target value of the target that this unit should pursue. If there   *
 *          is no suitable target, then TARGET_NONE is returned.                               *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   07/09/1996 JLB : Created.                                                                 *
 *=============================================================================================*/
TARGET UnitClass::Greatest_Threat(ThreatType threat) const
{
	assert(IsActive);
	if (Class->PrimaryWeapon != NULL) {
		threat = threat | Class->PrimaryWeapon->Allowed_Threats();
	}
	if (Class->SecondaryWeapon != NULL) {
		threat = threat | Class->SecondaryWeapon->Allowed_Threats();
	}

#ifdef OBSOLETE
	if (House->IsHuman) {
		threat = threat & ~THREAT_BUILDINGS;
	}
#endif

	return(FootClass::Greatest_Threat(threat));
}


/***********************************************************************************************
 * UnitClass::Read_INI -- Reads units from scenario INI file.                                  *
 *                                                                                             *
 *    This routine is used to read all the starting units from the                             *
 *    scenario control INI file. The units are created and placed on the                       *
 *    map by this routine.                                                                     *
 *                                                                                             *
 *    INI entry format:                                                                        *
 *      Housename, Typename, Strength, Coord, Facingnum, Missionname, Triggername              *
 *                                                                                             *
 * INPUT:   buffer   -- Pointer to the loaded scenario INI file.                               *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   05/24/1994 JLB : Created.                                                                 *
 *=============================================================================================*/
void UnitClass::Read_INI(CCINIClass & ini)
{
	UnitClass	* unit;			// Working unit pointer.
	HousesType	inhouse;			// Unit house.
	UnitType		classid;			// Unit 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 = UnitTypeClass::From_Name(strtok(NULL, ","));

			if (classid != UNIT_NONE) {

				if (HouseClass::As_Pointer(inhouse) != NULL) {
					unit = new UnitClass(classid, inhouse);
					if (unit != 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"));

						unit->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++;
								unit->Trigger = tt;
							}
						}

						if (unit->Unlimbo(coord, dir)) {
							unit->Strength = (int)unit->Class->MaxStrength * fixed(strength, 256);
							if (unit->Strength > unit->Class->MaxStrength-3) unit->Strength = unit->Class->MaxStrength;
							if (Session.Type == GAME_NORMAL || unit->House->IsHuman) {
								unit->Assign_Mission(mission);
								unit->Commence();
							} else {
								unit->Enter_Idle_Mode();
							}

						} else {

							/*
							**	If the unit could not be unlimboed, then this is a catastrophic error
							**	condition. Delete the unit.
							*/
							delete unit;
						}
					}
				}
			}
		}
	}
}


/***********************************************************************************************
 * UnitClass::Write_INI -- Store the units to the INI database.                                *
 *                                                                                             *
 *    This routine will store all the unit data to the INI database.                           *
 *                                                                                             *
 * INPUT:   ini   -- Reference to the INI database object to store to.                         *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   07/03/1996 JLB : Created.                                                                 *
 *=============================================================================================*/
void UnitClass::Write_INI(CCINIClass & ini)
{
	/*
	**	First, clear out all existing unit data from the ini file.
	*/
	ini.Clear(INI_Name());

	/*
	**	Write the unit data out.
	*/
	for (int index = 0; index < Units.Count(); index++) {
		UnitClass * unit = Units.Ptr(index);
		if (unit != NULL && !unit->IsInLimbo && unit->IsActive) {
			char	uname[10];
			char	buf[128];

			sprintf(uname, "%d", index);
			sprintf(buf, "%s,%s,%d,%u,%d,%s,%s",
				unit->House->Class->IniName,
				unit->Class->IniName,
				unit->Health_Ratio()*256,
				Coord_Cell(unit->Coord),
				unit->PrimaryFacing.Current(),
				MissionClass::Mission_Name(unit->Mission),
				unit->Trigger.Is_Valid() ? unit->Trigger->Class->IniName : "None"
				);
			ini.Put_String(INI_Name(), uname, buf);
		}
	}
}


/***********************************************************************************************
 * UnitClass::Credit_Load -- Fetch the full credit value of cargo carried.                     *
 *                                                                                             *
 *    This will determine the value of the cargo carried (limited to considering only gold     *
 *    and gems) and return that value. Use this to determine how 'valuable' a harvester is.    *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  Returns with the credit value of the cargo load of this unit (harvester).          *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   07/29/1996 JLB : Created.                                                                 *
 *=============================================================================================*/
int UnitClass::Credit_Load(void) const
{
	return((Gold * Rule.GoldValue) + (Gems * Rule.GemValue));
}


/***********************************************************************************************
 * UnitClass::Should_Crush_It -- Determines if this unit should crush an object.               *
 *                                                                                             *
 *    Call this routine to determine if this unit should crush the object specified. The       *
 *    test for crushable action depends on proximity and ability of the unit. If a unit        *
 *    should crush the object, then it should be given a movement order to enter the cell      *
 *    where the object is located.                                                             *
 *                                                                                             *
 * INPUT:   it -- The object to see if it should be crushed.                                   *
 *                                                                                             *
 * OUTPUT:  bool; Should "it" be crushed by this unit?                                         *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   07/29/1996 JLB : Created.                                                                 *
 *=============================================================================================*/
bool UnitClass::Should_Crush_It(TechnoClass const * it) const
{
	assert(IsActive);

	/*
	**	If this unit cannot crush anything or the candidate object cannot be crushed,
	**	then it obviously should not try to crush it -- return negative answer.
	*/
	if (!Class->IsCrusher || it == NULL || !it->Techno_Type_Class()->IsCrushable) return(false);

	/*
	**	Objects that are far away should really be fired upon rather than crushed.
	*/
	if (Distance(it) > Rule.CrushDistance) return(false);

	/*
	**	Human controlled units don't automatically crush. Neither do computer controlled ones
	**	if they are at difficult setting.
	*/
	if (House->IsHuman || House->Difficulty == DIFF_HARD) return(false);

	/*
	**	If the weapon this unit is equipped with is very good against crushable objects then
	**	fire the weapon instead. It is presumed that a wood destroying weapon is good against
	**	most crushable object types (infantry).
	*/
	if (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->WarheadPtr->IsWoodDestroyer) return(false);

	/*
	**	If the house IQ indicates that crushing should not be allowed, then don't
	**	suggest that crushing be done.
	*/
	if (House->IQ < Rule.IQCrush) return(false);

	/*
	** Don't allow crushing of spies by computer-controlled vehicles.
	*/
	if (it->What_Am_I() == RTTI_INFANTRY && *(InfantryClass *)it == INFANTRY_SPY) {
		return(false);
	}

	return(true);
}


/***********************************************************************************************
 * UnitClass::Scatter -- Causes the unit to scatter to a nearby location.                      *
 *                                                                                             *
 *    This scatter logic will actually look for a nearby location rather than an adjacent      *
 *    free location. This is necessary because sometimes a unit is required to scatter more    *
 *    than one cell. A vehicle on a service depot is a prime example.                          *
 *                                                                                             *
 * INPUT:   threat   -- The coordinate that a potential threat resides. If this is a non       *
 *                      threat related scatter, then this parameter will be zero.              *
 *                                                                                             *
 *          forced   -- Should the scatter be performed even if it would be otherwise          *
 *                      inconvenient?                                                          *
 *                                                                                             *
 *          nokidding-- Should the scatter be performed even if it would otherwise be          *
 *                      illegal?                                                               *
 *                                                                                             *
 * OUTPUT:  none                                                                               *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   10/02/1996 JLB : Created.                                                                 *
 *=============================================================================================*/
void UnitClass::Scatter(COORDINATE threat, bool forced, bool nokidding)
{
	assert(IsActive);

	if (Mission == MISSION_SLEEP || Mission == MISSION_STICKY || Mission == MISSION_UNLOAD) return;

	/*
	**	Certain missions prevent scattering regardless of whether it would be
	**	a good idea or not.
	*/
	if (!MissionControl[Mission].IsScatter && !forced) return;

	if (PrimaryFacing.Is_Rotating()) return;
//	if (IsRotating) return;

	if (Target_Legal(NavCom) && !nokidding) return;

	if (threat == 0) {
		Assign_Destination(::As_Target(Map.Nearby_Location(Coord_Cell(Coord), Class->Speed)));
	} else {
		DriveClass::Scatter(threat, forced, nokidding);
	}
}


/***********************************************************************************************
 * UnitClass::Limbo -- Limbo this unit.                                                        *
 *                                                                                             *
 *    This will cause the unit to go into a limbo state. If it was carrying a flag, then       *
 *    the flag will be dropped where the unit is at.                                           *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  bool; Was this unit limboed?                                                       *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   10/08/1996 JLB : Created.                                                                 *
 *=============================================================================================*/
bool UnitClass::Limbo(void)
{
	if (DriveClass::Limbo()) {
		if (Flagged != HOUSE_NONE) {
			HouseClass::As_Pointer(Flagged)->Flag_Attach(Coord_Cell(Coord));
			Flagged = HOUSE_NONE;
		}
		return(true);
	}
	return(false);
}



/***********************************************************************************************
 * UnitClass::Apply_Temporary_Jamming_Shroud -- Apply a temporary gap generator shroud effect  *
 *                                                                                             *
 *    This is intended as a temporary effect that is active only during export of the          *
 *    shroud data                                                                              *
 *                                                                                             *
 * INPUT:   House to apply effect for                                                          *
 *                                                                                             *
 * OUTPUT:  Bitmask of cells that effect was applied to                                        *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   8/19/2020 12:13PM ST : Created.                                                           *
 *=============================================================================================*/
unsigned int UnitClass::Apply_Temporary_Jamming_Shroud(HouseClass *house_to_apply_for)
{
	unsigned int shroud_bits_applied = 0;

	if (!IsActive || !Strength) {
		return shroud_bits_applied;
	}

	if (!Class->IsGapper) {
		return shroud_bits_applied;
	}

	CELL shroud_center = Coord_Cell(Center_Coord());
	int centerx = Cell_X(shroud_center);
	int centery = Cell_Y(shroud_center);
	CELL trycell;

	for (int index = 0; index < 31; index++) {
		shroud_bits_applied <<= 1;
		trycell = XY_Cell(centerx + _GapShroudXTable[index], centery + _GapShroudYTable[index]);
		if (Map[trycell].Is_Mapped(house_to_apply_for)) {
			Map.Jam_Cell(trycell, House);
			shroud_bits_applied |= 1;
		}
	}

	if (shroud_bits_applied) {
		Map.Constrained_Look(Coord, 5 * CELL_LEPTON_W, house_to_apply_for);
	}
	
	return shroud_bits_applied;
}	
	
	
			  
/***********************************************************************************************
 * UnitClass::Unapply_Temporary_Jamming_Shroud -- Remove temporary gap generator shroud effect *
 *                                                                                             *
 *    Remove gap effect added by Apply_Temporary_Jamming_Shroud                                *
 *                                                                                             *
 * INPUT:   House to unapply effect for                                                        *
 *          Bitmask of cells that effect was applied to                                        *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   8/19/2020 12:16PM ST : Created.                                                           *
 *=============================================================================================*/
void UnitClass::Unapply_Temporary_Jamming_Shroud(HouseClass *house_to_unapply_for, unsigned int shroud_bits_applied)
{
	if (!IsActive || !Strength) {
		return;
	}

	if (!Class->IsGapper) {
		return;
	}

	CELL shroud_center = Coord_Cell(Center_Coord());
	int centerx = Cell_X(shroud_center);
	int centery = Cell_Y(shroud_center);
	CELL trycell;

	for (int index = 30; index >= 0 && shroud_bits_applied; index--) {
		if (shroud_bits_applied & 1) {
			trycell = XY_Cell(centerx + _GapShroudXTable[index], centery + _GapShroudYTable[index]);
			Map.UnJam_Cell(trycell, House);
			Map.Map_Cell(trycell, house_to_unapply_for);
		}
		shroud_bits_applied >>= 1;
	}
}	



/*
** Updated for client/server multiplayer - ST 8/12/2019 11:46AM
*/
void UnitClass::Shroud_Regen(void)
{
	if (Class->IsGapper/*KO && !House->IsPlayerControl*/) {
		
		int index;
		int centerx, centery;
		CELL trycell;

		if (Session.Type != GAME_GLYPHX_MULTIPLAYER || Is_Legacy_Render_Enabled()) {
			// Only restore under the shroud if it's a valid field.
			if (ShroudBits != (unsigned)-1L) {
				centerx = Cell_X(ShroudCenter);
				centery = Cell_Y(ShroudCenter);
				for (index = 30; index >= 0 && ShroudBits; index--) {
					if (ShroudBits & 1) {
						trycell = XY_Cell(centerx + _GapShroudXTable[index], centery + _GapShroudYTable[index]);
	#if(0)
						Map.Map_Cell(trycell, PlayerPtr);
	#else
					Map.UnJam_Cell(trycell, House);
					Map.Map_Cell(trycell, House);
	#endif
					}
					ShroudBits >>= 1;
				}
			}

			if(IsActive && Strength) {
				// Now shroud around the new center
				ShroudBits = 0L;
				ShroudCenter = Coord_Cell(Center_Coord());
				centerx = Cell_X(ShroudCenter);
				centery = Cell_Y(ShroudCenter);
				for (index = 0; index < 31; index++) {
					ShroudBits <<= 1;
					trycell = XY_Cell(centerx + _GapShroudXTable[index], centery + _GapShroudYTable[index]);
					if (Map[trycell].Is_Mapped(House)) {
						Map.Jam_Cell(trycell, House);
						ShroudBits |= 1;
					}
				}
			}
		}

		/*
		** Updated for client/server multiplayer. ST - 8/12/2019 3:25PM
		*/
		if (Session.Type != GAME_GLYPHX_MULTIPLAYER) {
			if (House->IsPlayerControl) {
				Map.Constrained_Look(Coord, 5 * CELL_LEPTON_W, PlayerPtr);
			}
		
		} else {
		
			if (Is_Legacy_Render_Enabled()) {
				for (int i = 0; i < Session.Players.Count(); i++) {
					HouseClass *player_house = HouseClass::As_Pointer(Session.Players[i]->Player.ID);
					if (player_house->IsHuman && player_house != House) {
						Map.Constrained_Look(Coord, 5 * CELL_LEPTON_W, player_house);
					}
				}
			}
		}
	}
}


/***********************************************************************************************
 * UnitClass::Mission_Guard_Area -- Guard area logic for units.                                *
 *                                                                                             *
 *    This logic is similar to normal guard area except that APCs owned by the computer will   *
 *    try to load up with nearby infantry. This will give the computer some fake intelligence  *
 *    when playing in skirmish mode.                                                           *
 *                                                                                             *
 * INPUT:   none                                                                               *
 *                                                                                             *
 * OUTPUT:  Returns with the delay to use before calling this routine again.                   *
 *                                                                                             *
 * WARNINGS:   none                                                                            *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   11/03/1996 JLB : Created.                                                                 *
 *=============================================================================================*/
int UnitClass::Mission_Guard_Area(void)
{
	assert(IsActive);

	/*
	**	Check to see if this is an APC that is largely empty and not otherwise doing anything.
	**	Such an APC should load up with infantry.
	*/
	if (Session.Type != GAME_NORMAL &&
#ifdef FIXIT_PHASETRANSPORT	//	checked - ajw 9/28/98
			(*this == UNIT_APC || *this == UNIT_PHASE ) &&
#else
			*this == UNIT_APC &&
#endif
			!Target_Legal(TarCom) &&
			!In_Radio_Contact() &&
			House->Which_Zone(this) != ZONE_NONE &&
			!House->IsHuman) {


		int needed = Class->Max_Passengers() - How_Many();
		for (int index = 0; index < Infantry.Count(); index++) {
			if (needed == 0) break;

			InfantryClass * infantry = Infantry.Ptr(index);

			if (infantry != NULL &&
					infantry->IsActive &&
					!infantry->IsInLimbo &&
					infantry->Strength > 0 &&
					infantry->House == House &&
					!Target_Legal(infantry->TarCom) &&
					!Target_Legal(infantry->NavCom) &&
					Distance(infantry) < 7 * CELL_LEPTON_W &&
					(infantry->Mission == MISSION_GUARD || infantry->Mission == MISSION_GUARD_AREA)) {

				infantry->Assign_Mission(MISSION_ENTER);
				infantry->ArchiveTarget = As_Target();
				needed--;
			}
		}
	}
	return(DriveClass::Mission_Guard_Area());
}