warzone2100/src/action.c

3057 lines
83 KiB
C

/*
This file is part of Warzone 2100.
Copyright (C) 1999-2004 Eidos Interactive
Copyright (C) 2005-2007 Warzone Resurrection Project
Warzone 2100 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 2 of the License, or
(at your option) any later version.
Warzone 2100 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Warzone 2100; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* Action.c
*
* Functions for setting the action of a droid
*
*/
#include <string.h>
#include "lib/framework/frame.h"
#include "lib/gamelib/gtime.h"
#include "lib/script/script.h"
#include "action.h"
#include "lib/sound/audio_id.h"
#include "lib/sound/audio.h"
#include "combat.h"
#include "formation.h"
#include "geometry.h"
#include "hci.h"
#include "intdisplay.h"
#include "mission.h"
#include "multiplay.h"
#include "projectile.h"
#include "research.h"
#include "scriptcb.h"
#include "scripttabs.h"
#include "transporter.h"
#include "visibility.h"
/* attack run distance */
#define VTOL_ATTACK_LENGTH 1000
#define VTOL_ATTACK_WIDTH 200
#define VTOL_ATTACK_TARDIST 400
#define VTOL_ATTACK_RETURNDIST 700
// turret rotation limits
#define VTOL_TURRET_RLIMIT 315
#define VTOL_TURRET_LLIMIT 45
// time to pause before a droid blows up
#define ACTION_DESTRUCT_TIME 2000
//#define PITCH_UPPER_LIMIT 60
//#define PITCH_LOWER_LIMIT -15
#define ACTION_TURRET_ROTATION_RATE 180
#define REPAIR_PITCH_LOWER 30
#define REPAIR_PITCH_UPPER -15
//how long to follow a damaged droid around before giving up if don't get near
#define KEEP_TRYING_REPAIR 10000
//how far away the repair droid can be from the damaged droid to function
#define REPAIR_RANGE (TILE_UNITS * TILE_UNITS * 4)
//how many tiles to pull back
#define PULL_BACK_DIST 10
// data required for any action
typedef struct _droid_action_data
{
DROID_ACTION action;
UDWORD x,y;
//Watermelon:multiple action target info
BASE_OBJECT *psObj;
BASE_STATS *psStats;
} DROID_ACTION_DATA;
// Check if a droid has stoped moving
#define DROID_STOPPED(psDroid) \
(psDroid->sMove.Status == MOVEINACTIVE || psDroid->sMove.Status == MOVEHOVER || \
psDroid->sMove.Status == MOVESHUFFLE)
/* Check if a target is at correct range to attack */
BOOL actionInAttackRange(DROID *psDroid, BASE_OBJECT *psObj, int weapon_slot)
{
SDWORD dx, dy, dz, radSq, rangeSq, longRange;
SECONDARY_STATE state;
WEAPON_STATS *psStats;
CHECK_DROID(psDroid);
if (psDroid->asWeaps[0].nStat == 0)
{
return FALSE;
}
dx = (SDWORD)psDroid->pos.x - (SDWORD)psObj->pos.x;
dy = (SDWORD)psDroid->pos.y - (SDWORD)psObj->pos.y;
dz = (SDWORD)psDroid->pos.z - (SDWORD)psObj->pos.z;
radSq = dx*dx + dy*dy;
psStats = asWeaponStats + psDroid->asWeaps[weapon_slot].nStat;
if (psDroid->order == DORDER_ATTACKTARGET
&& secondaryGetState(psDroid, DSO_HALTTYPE, &state)
&& state == DSS_HALT_HOLD)
{
longRange = proj_GetLongRange(psStats);
rangeSq = longRange * longRange;
}
else
{
switch (psDroid->secondaryOrder & DSS_ARANGE_MASK)
{
case DSS_ARANGE_DEFAULT:
//if (psStats->shortHit > psStats->longHit)
if (weaponShortHit(psStats, psDroid->player) > weaponLongHit(psStats, psDroid->player))
{
rangeSq = psStats->shortRange * psStats->shortRange;
}
else
{
longRange = proj_GetLongRange(psStats);
rangeSq = longRange * longRange;
}
break;
case DSS_ARANGE_SHORT:
rangeSq = psStats->shortRange * psStats->shortRange;
break;
case DSS_ARANGE_LONG:
longRange = proj_GetLongRange(psStats);
rangeSq = longRange * longRange;
break;
default:
ASSERT(!"unknown attackrange order", "actionInAttackRange: unknown attack range order");
longRange = proj_GetLongRange(psStats);
rangeSq = longRange * longRange;
break;
}
}
/* check max range */
if ( radSq <= rangeSq )
{
/* check min range */
rangeSq = psStats->minRange * psStats->minRange;
if ( radSq >= rangeSq || !proj_Direct( psStats ) )
{
return TRUE;
}
}
return FALSE;
}
// check if a target is within weapon range
BOOL actionInRange(DROID *psDroid, BASE_OBJECT *psObj, int weapon_slot)
{
SDWORD dx, dy, dz, radSq, rangeSq, longRange;
WEAPON_STATS *psStats;
CHECK_DROID(psDroid);
if (psDroid->asWeaps[0].nStat == 0)
{
return FALSE;
}
psStats = asWeaponStats + psDroid->asWeaps[weapon_slot].nStat;
dx = (SDWORD)psDroid->pos.x - (SDWORD)psObj->pos.x;
dy = (SDWORD)psDroid->pos.y - (SDWORD)psObj->pos.y;
dz = (SDWORD)psDroid->pos.z - (SDWORD)psObj->pos.z;
radSq = dx*dx + dy*dy;
longRange = proj_GetLongRange(psStats);
rangeSq = longRange * longRange;
/* check max range */
if ( radSq <= rangeSq )
{
/* check min range */
rangeSq = psStats->minRange * psStats->minRange;
if ( radSq >= rangeSq || !proj_Direct( psStats ) )
{
return TRUE;
}
}
return FALSE;
}
// check if a target is inside minimum weapon range
BOOL actionInsideMinRange(DROID *psDroid, BASE_OBJECT *psObj, int weapon_slot)
{
SDWORD dx, dy, dz, radSq, rangeSq, minRange;
WEAPON_STATS *psStats;
CHECK_DROID(psDroid);
/* Watermelon:if I am a multi-turret droid */
if (psDroid->asWeaps[0].nStat == 0)
{
return FALSE;
}
psStats = asWeaponStats + psDroid->asWeaps[weapon_slot].nStat;
dx = (SDWORD)psDroid->pos.x - (SDWORD)psObj->pos.x;
dy = (SDWORD)psDroid->pos.y - (SDWORD)psObj->pos.y;
dz = (SDWORD)psDroid->pos.z - (SDWORD)psObj->pos.z;
radSq = dx*dx + dy*dy;
minRange = (SDWORD)psStats->minRange;
rangeSq = minRange * minRange;
// check min range
if ( radSq <= rangeSq )
{
return TRUE;
}
return FALSE;
}
// Realign turret
void actionAlignTurret(BASE_OBJECT *psObj, int weapon_slot)
{
UDWORD rotation;
//Watermelon:multiple temp tRot, tPitch
UWORD nearest = 0;
UWORD tRot;
UWORD tPitch;
//Watermelon:default turret rotation 0
tRot = 0;
//get the maximum rotation this frame
//rotation = (psDroid->turretRotRate * frameTime) / (4 * GAME_TICKS_PER_SEC);
rotation = (ACTION_TURRET_ROTATION_RATE * frameTime) / (4 * GAME_TICKS_PER_SEC);
if (rotation == 0)
{
rotation = 1;
}
switch (psObj->type)
{
case OBJ_DROID:
tRot = ((DROID *)psObj)->turretRotation[weapon_slot];
tPitch = ((DROID *)psObj)->turretPitch[weapon_slot];
break;
case OBJ_STRUCTURE:
tRot = ((STRUCTURE *)psObj)->turretRotation[weapon_slot];
tPitch = ((STRUCTURE *)psObj)->turretPitch[weapon_slot];
// now find the nearest 90 degree angle
nearest = (UWORD)(((tRot + 45) / 90) * 90);
tRot = (UWORD)(((tRot + 360) - nearest) % 360);
break;
default:
ASSERT(!"invalid object type", "actionAlignTurret: invalid object type");
return;
break;
}
if (rotation > 180)//crop to 180 degrees, no point in turning more than all the way round
{
rotation = 180;
}
if (tRot < 180)// +ve angle 0 - 179 degrees
{
if (tRot > rotation)
{
tRot = (UWORD)(tRot - rotation);
}
else
{
tRot = 0;
}
}
else //angle greater than 180 rotate in opposite direction
{
if (tRot < (360 - rotation))
{
tRot = (UWORD)(tRot + rotation);
}
else
{
tRot = 0;
}
}
tRot %= 360;
// align the turret pitch
if (tPitch < 180)// +ve angle 0 - 179 degrees
{
if (tPitch > rotation/2)
{
tPitch = (UWORD)(tPitch - rotation/2);
}
else
{
tPitch = 0;
}
}
else // -ve angle rotate in opposite direction
{
if (tPitch < (360 -(SDWORD)rotation/2))
{
tPitch = (UWORD)(tPitch + rotation/2);
}
else
{
tPitch = 0;
}
}
tPitch %= 360;
switch (psObj->type)
{
case OBJ_DROID:
((DROID *)psObj)->turretRotation[weapon_slot] = tRot;
((DROID *)psObj)->turretPitch[weapon_slot] = tPitch;
break;
case OBJ_STRUCTURE:
// now adjust back to the nearest 90 degree angle
tRot = (UWORD)((tRot + nearest) % 360);
((STRUCTURE *)psObj)->turretRotation[weapon_slot] = tRot;
((STRUCTURE *)psObj)->turretPitch[weapon_slot] = tPitch;
break;
default:
ASSERT(!"invalid object type", "actionAlignTurret: invalid object type");
return;
break;
}
}
#define HEAVY_WEAPON_WEIGHT 50000
/* returns true if on target */
BOOL actionTargetTurret(BASE_OBJECT *psAttacker, BASE_OBJECT *psTarget, UWORD *pRotation,
UWORD *pPitch, WEAPON_STATS *psWeapStats, BOOL bInvert, int weapon_slot)
{
SWORD tRotation, tPitch, rotRate, pitchRate;
SDWORD targetRotation,targetPitch;
SDWORD pitchError;
SDWORD rotationError, dx, dy, dz;
BOOL onTarget = FALSE;
float fR;
SDWORD pitchLowerLimit, pitchUpperLimit;
DROID *psDroid = NULL;
// Vector3i muzzle;
//these are constants now and can be set up at the start of the function
rotRate = ACTION_TURRET_ROTATION_RATE;
pitchRate = (SWORD)(ACTION_TURRET_ROTATION_RATE/2);
//added for 22/07/99 upgrade - AB
if (psWeapStats)
{
//extra heavy weapons on some structures need to rotate and pitch more slowly
if (psWeapStats->weight > HEAVY_WEAPON_WEIGHT)
{
rotRate = (SWORD)(ACTION_TURRET_ROTATION_RATE/2 - (100 *
(psWeapStats->weight - HEAVY_WEAPON_WEIGHT) / psWeapStats->weight));
pitchRate = (SWORD) (rotRate / 2);
}
}
tRotation = *pRotation;
tPitch = *pPitch;
//set the pitch limits based on the weapon stats of the attacker
pitchLowerLimit = pitchUpperLimit = 0;
if (psAttacker->type == OBJ_STRUCTURE)
{
pitchLowerLimit = asWeaponStats[((STRUCTURE *)psAttacker)->asWeaps[weapon_slot].
nStat].minElevation;
pitchUpperLimit = asWeaponStats[((STRUCTURE *)psAttacker)->asWeaps[weapon_slot].
nStat].maxElevation;
}
else if (psAttacker->type == OBJ_DROID)
{
psDroid = (DROID *) psAttacker;
if ( (psDroid->droidType == DROID_WEAPON) ||
(psDroid->droidType == DROID_TRANSPORTER) ||
(psDroid->droidType == DROID_COMMAND) ||
(psDroid->droidType == DROID_CYBORG) ||
(psDroid->droidType == DROID_CYBORG_SUPER) )
{
pitchLowerLimit = asWeaponStats[psDroid->asWeaps[weapon_slot].
nStat].minElevation;
pitchUpperLimit = asWeaponStats[psDroid->asWeaps[weapon_slot].
nStat].maxElevation;
}
else if ( psDroid->droidType == DROID_REPAIR )
{
pitchLowerLimit = REPAIR_PITCH_LOWER;
pitchUpperLimit = REPAIR_PITCH_UPPER;
}
}
//get the maximum rotation this frame
rotRate = (SWORD)(rotRate * frameTime / GAME_TICKS_PER_SEC);
if (rotRate > 180)//crop to 180 degrees, no point in turning more than all the way round
{
rotRate = 180;
}
if (rotRate <= 0)
{
rotRate = 1;
}
pitchRate = (SWORD)(pitchRate * frameTime / GAME_TICKS_PER_SEC);
if (pitchRate > 180)//crop to 180 degrees, no point in turning more than all the way round
{
pitchRate = 180;
}
if (pitchRate <= 0)
{
pitchRate = 1;
}
/* if ( (psAttacker->type == OBJ_STRUCTURE) &&
(((STRUCTURE *)psAttacker)->pStructureType->type == REF_DEFENSE) &&
(asWeaponStats[((STRUCTURE *)psAttacker)->asWeaps[0].nStat].surfaceToAir == SHOOT_IN_AIR) )
{
rotRate = 180;
pitchRate = 180;
}*/
//and point the turret at target
targetRotation = calcDirection(psAttacker->pos.x, psAttacker->pos.y, psTarget->pos.x, psTarget->pos.y);
rotationError = targetRotation - (tRotation + psAttacker->direction);
//restrict rotationerror to =/- 180 degrees
while (rotationError > 180)
{
rotationError -= 360;
}
while (rotationError < -180)
{
rotationError += 360;
}
if (-rotationError > (SDWORD)rotRate)
{
// subtract rotation
if (tRotation < rotRate)
{
tRotation = (SWORD)(tRotation + 360 - rotRate);
}
else
{
tRotation = (SWORD)(tRotation - rotRate);
}
}
else if (rotationError > (SDWORD)rotRate)
{
// add rotation
tRotation = (SWORD)(tRotation + rotRate);
tRotation %= 360;
}
else //roughly there so lock on and fire
{
if ( (SDWORD)psAttacker->direction > targetRotation )
{
tRotation = (SWORD)(targetRotation + 360 - psAttacker->direction);
}
else
{
tRotation = (SWORD)(targetRotation - psAttacker->direction);
}
// debug( LOG_NEVER, "locked on target...\n");
onTarget = TRUE;
}
tRotation %= 360;
if ((psAttacker->type == OBJ_DROID) &&
vtolDroid((DROID *)psAttacker))
{
// limit the rotation for vtols
if ((tRotation <= 180) && (tRotation > VTOL_TURRET_LLIMIT))
{
tRotation = VTOL_TURRET_LLIMIT;
}
else if ((tRotation > 180) && (tRotation < VTOL_TURRET_RLIMIT))
{
tRotation = VTOL_TURRET_RLIMIT;
}
}
/* set muzzle pitch if direct fire */
// if ( asWeaponStats[psAttacker->asWeaps->nStat].direct == TRUE )
if ( psWeapStats != NULL &&
( proj_Direct( psWeapStats ) ||
( (psAttacker->type == OBJ_DROID) &&
!proj_Direct( psWeapStats ) &&
actionInsideMinRange(psDroid, psTarget, weapon_slot) ) ) )
{
// difference between muzzle position and droid origin is unlikely to affect aiming
// particularly as target origin is used
// calcDroidMuzzleLocation( psAttacker, &muzzle);
dx = psTarget->pos.x - psAttacker->pos.x;//muzzle.x;
dy = psTarget->pos.y - psAttacker->pos.y;//muzzle.y;
// dz = map_Height(psTarget->pos.x, psTarget->pos.y) - psAttacker->pos.z;//muzzle.z;
dz = psTarget->pos.z - psAttacker->pos.z;//muzzle.z;
/* get target distance */
fR = trigIntSqrt( dx*dx + dy*dy );
targetPitch = (SDWORD)( RAD_TO_DEG(atan2(dz, fR)));
//tPitch = tPitch;
if (tPitch > 180)
{
tPitch -=360;
}
/* invert calculations for bottom-mounted weapons (i.e. for vtols) */
if ( bInvert )
{
tPitch = (SWORD)(-tPitch);
targetPitch = -targetPitch;
}
pitchError = targetPitch - tPitch;
if (pitchError < -pitchRate)
{
// move down
tPitch = (SWORD)(tPitch - pitchRate);
onTarget = FALSE;
}
else if (pitchError > pitchRate)
{
// add rotation
tPitch = (SWORD)(tPitch + pitchRate);
onTarget = FALSE;
}
else //roughly there so lock on and fire
{
tPitch = (SWORD)targetPitch;
}
/* re-invert result for bottom-mounted weapons (i.e. for vtols) */
if ( bInvert )
{
tPitch = (SWORD)-tPitch;
}
if (tPitch < pitchLowerLimit)
{
// move down
tPitch = (SWORD)pitchLowerLimit;
onTarget = FALSE;
}
else if (tPitch > pitchUpperLimit)
{
// add rotation
tPitch = (SWORD)pitchUpperLimit;
onTarget = FALSE;
}
if (tPitch < 0)
{
tPitch += 360;
}
}
*pRotation = tRotation;
*pPitch = tPitch;
// debug( LOG_NEVER, "endrot=%d\n",tRotation);
return onTarget;
}
// return whether a droid can see a target to fire on it
BOOL actionVisibleTarget(DROID *psDroid, BASE_OBJECT *psTarget, int weapon_slot)
{
WEAPON_STATS *psStats;
CHECK_DROID(psDroid);
if (psDroid->numWeaps == 0)
{
if ( visibleObject((BASE_OBJECT*)psDroid, psTarget) )
{
return TRUE;
}
}
//if (psDroid->numWeaps == 0)
if (vtolDroid(psDroid))
{
if ( visibleObject((BASE_OBJECT*)psDroid, psTarget) )
{
return TRUE;
}
return FALSE;
}
psStats = asWeaponStats + psDroid->asWeaps[weapon_slot].nStat;
if (proj_Direct(psStats))
{
if (visibleObjWallBlock((BASE_OBJECT*)psDroid, psTarget))
{
return TRUE;
}
}
else
{
// indirect can only attack things they can see unless attacking
// through a sensor droid - see DORDER_FIRESUPPORT
if (orderState(psDroid, DORDER_FIRESUPPORT))
{
if (psTarget->visible[psDroid->player])
{
return TRUE;
}
}
else
{
if (visibleObject((BASE_OBJECT*)psDroid, psTarget))
{
return TRUE;
}
}
}
return FALSE;
}
static void actionAddVtolAttackRun( DROID *psDroid )
{
double fA;
SDWORD deltaX, deltaY, iA, iX, iY;
BASE_OBJECT *psTarget;
#if 0
SDWORD iVx, iVy;
#endif
CHECK_DROID(psDroid);
if ( psDroid->psActionTarget[0] != NULL )
{
psTarget = psDroid->psActionTarget[0];
}
else if ( psDroid->psTarget != NULL )
{
psTarget = psDroid->psTarget;
}
else
{
return;
}
/* get normal vector from droid to target */
deltaX = psTarget->pos.x - psDroid->pos.x;
deltaY = psTarget->pos.y - psDroid->pos.y;
/* get magnitude of normal vector (Pythagorean theorem) */
fA = trigIntSqrt( deltaX*deltaX + deltaY*deltaY );
iA = fA;
#if 0
/* get left perpendicular to normal vector:
* swap normal vector elements and negate y:
* scale to attack ellipse width
*/
iVx = deltaY * VTOL_ATTACK_WIDTH / iA;
iVy = -deltaX * VTOL_ATTACK_WIDTH / iA;
/* add waypoint left perpendicular to target*/
iX = psTarget->pos.x + iVx;
iY = psTarget->pos.y + iVy;
#endif
/* add waypoint behind target attack length away*/
if (iA != 0)
{
iX = psTarget->pos.x + (deltaX * VTOL_ATTACK_LENGTH / iA);
iY = psTarget->pos.y + (deltaY * VTOL_ATTACK_LENGTH / iA);
}
else
{
// We should only ever get here if both deltaX and deltaY
// are zero (look at the above pythagoeran theorem).
// This code is here to prevent a divide by zero error
//
// The next values are valid because if both deltas are zero
// then iA will be zero as well resulting in:
// (deltaXY * VTOL_ATTACK_LENGTH / iA) = (0 * VTOL_ATTACK_LENGTH / 0) = (0 / 0) = 0
iX = psTarget->pos.x;
iY = psTarget->pos.y;
}
if (iX <= 0
|| iY<=0
|| iX > world_coord(GetWidthOfMap())
|| iY > world_coord(GetHeightOfMap()))
{
debug( LOG_NEVER, "*** actionAddVtolAttackRun: run off map! ***\n" );
}
else
{
moveDroidToDirect( psDroid, iX, iY );
}
}
static void actionUpdateVtolAttack( DROID *psDroid )
{
WEAPON_STATS *psWeapStats[DROID_MAXWEAPS];
UBYTE i;
CHECK_DROID(psDroid);
/* don't do attack runs whilst returning to base */
if ( psDroid->order == DORDER_RTB )
{
return;
}
/* Watermelon:if I am a multi-turret droid */
if (psDroid->numWeaps > 1)
{
for(i = 0;i < psDroid->numWeaps;i++)
{
if (psDroid->asWeaps[i].nStat != 0)
{
psWeapStats[i] = asWeaponStats + psDroid->asWeaps[i].nStat;
ASSERT( psWeapStats != NULL,
"actionUpdateVtolAttack: invalid weapon stats pointer" );
break;
}
}
}
else
{
if (psDroid->asWeaps[0].nStat > 0)
{
psWeapStats[0] = asWeaponStats + psDroid->asWeaps[0].nStat;
ASSERT( psWeapStats != NULL,
"actionUpdateVtolAttack: invalid weapon stats pointer" );
}
}
/* order back to base after fixed number of attack runs */
if ( psWeapStats[0] != NULL )
{
if (vtolEmpty(psDroid))
{
moveToRearm(psDroid);
return;
}
}
/* circle around target if hovering and not cyborg */
if (psDroid->sMove.Status == MOVEHOVER && !cyborgDroid(psDroid))
{
actionAddVtolAttackRun( psDroid );
}
}
static void actionUpdateTransporter( DROID *psDroid )
{
CHECK_DROID(psDroid);
//check if transporter has arrived
if (updateTransporter(psDroid))
{
// Got to destination
psDroid->action = DACTION_NONE;
return;
}
}
// calculate a position for units to pull back to if they
// need to increase the range between them and a target
static void actionCalcPullBackPoint(BASE_OBJECT *psObj, BASE_OBJECT *psTarget, SDWORD *px, SDWORD *py)
{
SDWORD xdiff,ydiff, len;
// get the vector from the target to the object
xdiff = (SDWORD)psObj->pos.x - (SDWORD)psTarget->pos.x;
ydiff = (SDWORD)psObj->pos.y - (SDWORD)psTarget->pos.y;
len = (SDWORD)sqrtf(xdiff*xdiff + ydiff*ydiff);
if (len == 0)
{
xdiff = TILE_UNITS;
ydiff = TILE_UNITS;
}
else
{
xdiff = (xdiff * TILE_UNITS) / len;
ydiff = (ydiff * TILE_UNITS) / len;
}
// create the position
*px = (SDWORD)psObj->pos.x + xdiff * PULL_BACK_DIST;
*py = (SDWORD)psObj->pos.y + ydiff * PULL_BACK_DIST;
// make sure coordinates stay inside of the map
clip_world_offmap(px, py);
}
// check whether a droid is in the neighboring tile to a build position
BOOL actionReachedBuildPos(DROID *psDroid, SDWORD x, SDWORD y, BASE_STATS *psStats)
{
SDWORD width, breadth, tx,ty, dx,dy;
CHECK_DROID(psDroid);
// do all calculations in half tile units so that
// the droid moves to within half a tile of the target
// NOT ANY MORE - JOHN
dx = map_coord(psDroid->pos.x);
dy = map_coord(psDroid->pos.y);
if (StatIsStructure(psStats))
{
width = ((STRUCTURE_STATS *)psStats)->baseWidth;
breadth = ((STRUCTURE_STATS *)psStats)->baseBreadth;
}
else
{
width = ((FEATURE_STATS *)psStats)->baseWidth;
breadth = ((FEATURE_STATS *)psStats)->baseBreadth;
}
tx = map_coord(x);
ty = map_coord(y);
// move the x,y to the top left of the structure
tx -= width/2;
ty -= breadth/2;
if ( (dx == (tx -1)) || (dx == (tx + width)) )
{
// droid could be at either the left or the right
if ( (dy >= (ty -1)) && (dy <= (ty + breadth)) )
{
return TRUE;
}
}
else if ( (dy == (ty -1)) || (dy == (ty + breadth)) )
{
// droid could be at either the top or the bottom
if ( (dx >= (tx -1)) && (dx <= (tx + width)) )
{
return TRUE;
}
}
return FALSE;
}
// check if a droid is on the foundations of a new building
BOOL actionDroidOnBuildPos(DROID *psDroid, SDWORD x, SDWORD y, BASE_STATS *psStats)
{
SDWORD width, breadth, tx,ty, dx,dy;
CHECK_DROID(psDroid);
dx = map_coord(psDroid->pos.x);
dy = map_coord(psDroid->pos.y);
if (StatIsStructure(psStats))
{
width = ((STRUCTURE_STATS *)psStats)->baseWidth;
breadth = ((STRUCTURE_STATS *)psStats)->baseBreadth;
}
else
{
width = ((FEATURE_STATS *)psStats)->baseWidth;
breadth = ((FEATURE_STATS *)psStats)->baseBreadth;
}
tx = map_coord(x) - (width / 2);
ty = map_coord(y) - (breadth / 2);
if (dx >= tx
&& dx < tx + width
&& dy >= ty
&& dy < ty + breadth)
{
return TRUE;
}
return FALSE;
}
// return the position of a players home base
void actionHomeBasePos(SDWORD player, SDWORD *px, SDWORD *py)
{
STRUCTURE *psStruct;
ASSERT( player >= 0 && player < MAX_PLAYERS,
"actionHomeBasePos: invalide player number" );
for(psStruct = apsStructLists[player]; psStruct; psStruct=psStruct->psNext)
{
if (psStruct->pStructureType->type == REF_HQ)
{
*px = (SDWORD)psStruct->pos.x;
*py = (SDWORD)psStruct->pos.y;
return;
}
}
*px = getLandingX(player);
*py = getLandingY(player);
}
// tell the action system of a potential location for walls blocking routing
BOOL actionRouteBlockingPos(DROID *psDroid, SDWORD tx, SDWORD ty)
{
SDWORD i,j;
MAPTILE *psTile;
STRUCTURE *psWall;
CHECK_DROID(psDroid);
if (vtolDroid(psDroid) ||
((psDroid->order != DORDER_MOVE) &&
(psDroid->order != DORDER_SCOUT)))
{
return FALSE;
}
// see if there is a wall to attack around the location
psWall = NULL;
for(i= tx -1; i <= tx + 1; i++)
{
for(j= ty -1; j <= ty + 1; j++)
{
if (tileOnMap(i,j))
{
psTile = mapTile(i,j);
if (TILE_HAS_WALL(psTile))
{
psWall = getTileStructure((UDWORD)i,(UDWORD)j);
//Watermelon:fixes AI try to destroy ally's wall bug
if (psWall->player != psDroid->player &&
!aiCheckAlliances(psWall->player, psDroid->player))
{
goto done;
}
else
{
psWall = NULL;
}
}
}
}
}
done:
if (psWall != NULL)
{
if (psDroid->order == DORDER_MOVE)
{
psDroid->order = DORDER_MOVE_ATTACKWALL;
}
else if (psDroid->order == DORDER_SCOUT)
{
psDroid->order = DORDER_SCOUT_ATTACKWALL;
}
setDroidTarget(psDroid, (BASE_OBJECT *)psWall);
return TRUE;
}
return FALSE;
}
#define VTOL_ATTACK_AUDIO_DELAY (3*GAME_TICKS_PER_SEC)
// Update the action state for a droid
void actionUpdateDroid(DROID *psDroid)
{
UDWORD droidX,droidY;
UDWORD tlx,tly;
STRUCTURE *psStruct;
STRUCTURE_STATS *psStructStats;
BASE_OBJECT *psTarget;
WEAPON_STATS *psWeapStats;
SDWORD targetDir, dirDiff, pbx,pby;
SDWORD xdiff,ydiff, rangeSq;
SECONDARY_STATE state;
PROPULSION_STATS *psPropStats;
BOOL bChaseBloke, bInvert;
FEATURE *psNextWreck;
BOOL (*actionUpdateFunc)(DROID *psDroid) = NULL;
SDWORD moveAction;
BOOL bDoHelpBuild;
MAPTILE *psTile;
UBYTE i = 0;
//this is a bit field
UBYTE num_weapons = 0;
BASE_OBJECT *psActionTarget;
BASE_OBJECT *psTargets[DROID_MAXWEAPS];
UBYTE j,iVisible = 1;
BOOL bHasTarget;
CHECK_DROID(psDroid);
psPropStats = asPropulsionStats + psDroid->asBits[COMP_PROPULSION].nStat;
ASSERT( psPropStats != NULL,
"actionUpdateUnit: invalid propulsion stats pointer" );
ASSERT( psDroid->turretRotation[i] < 360, "turretRotation out of range: %d", psDroid->turretRotation[i]);
ASSERT( psDroid->direction < 360, "unit direction out of range: %f", psDroid->direction);
/* check whether turret inverted for actionTargetTurret */
//if ( psDroid->droidType != DROID_CYBORG &&
if ( !cyborgDroid(psDroid) &&
psPropStats->propulsionType == LIFT )
{
bInvert = TRUE;
}
else
{
bInvert = FALSE;
}
// clear the target if it has died
// weapon droid
if (psDroid->numWeaps > 0)
{
// clear targets
for (i = 0;i < psDroid->numWeaps;i++)
{
if (psDroid->psActionTarget[i] && psDroid->psActionTarget[i]->died)
{
setDroidActionTarget(psDroid, NULL, i);
if (i == 0)
{
if ( (psDroid->action != DACTION_MOVEFIRE) &&
(psDroid->action != DACTION_TRANSPORTIN) &&
(psDroid->action != DACTION_TRANSPORTOUT) )
{
psDroid->action = DACTION_NONE;
//if Vtol - return to rearm pad
if (vtolDroid(psDroid))
{
moveToRearm(psDroid);
}
}
}
}
}
}
//utility droid
else
{
if (psDroid->psActionTarget[0] && psDroid->psActionTarget[0]->died)
{
setDroidActionTarget(psDroid, NULL, 0);
if ( (psDroid->action != DACTION_MOVEFIRE) &&
(psDroid->action != DACTION_TRANSPORTIN) &&
(psDroid->action != DACTION_TRANSPORTOUT) )
{
psDroid->action = DACTION_NONE;
//if Vtol - return to rearm pad
if (vtolDroid(psDroid))
{
moveToRearm(psDroid);
}
}
}
}
//if the droid has been attacked by an EMP weapon, it is temporarily disabled
if (psDroid->lastHitWeapon == WSC_EMP)
{
if (gameTime - psDroid->timeLastHit > EMP_DISABLE_TIME)
{
//the actionStarted time needs to be adjusted
psDroid->actionStarted += (gameTime - psDroid->timeLastHit);
//reset the lastHit parameters
psDroid->timeLastHit = 0;
psDroid->lastHitWeapon = UDWORD_MAX;
}
else
{
//get out without updating
return;
}
}
//if (psDroid->numWeaps == 0)
/* Watermelon:if I am a multi-turret droid */
if (psDroid->numWeaps > 1)
{
for(i = 0;i < psDroid->numWeaps;i++)
{
if (psDroid->asWeaps[i].nStat > 0)
{
num_weapons += (1 << (i+1));
}
}
}
else
{
num_weapons = 2;
}
psTarget = psDroid->psTarget;
//Watermelon:safety check
//if (psDroid->numWeaps > 0)
if (psDroid->asWeaps[0].nStat > 0)
{
psWeapStats = asWeaponStats + psDroid->asWeaps[0].nStat;
}
else
{
psWeapStats = NULL;
}
switch (psDroid->action)
{
case DACTION_NONE:
case DACTION_WAITFORREPAIR:
// doing nothing
//since not doing anything, see if need to self repair
if (selfRepairEnabled(psDroid->player))
{
//wait for 1 second to give the repair facility a chance to do the repair work
if (gameTime - psDroid->actionStarted > GAME_TICKS_PER_SEC)
{
droidSelfRepair(psDroid);
}
}
break;
case DACTION_WAITDURINGREPAIR:
// don't want to be in a formation for this move
if (psDroid->sMove.psFormation != NULL)
{
formationLeave(psDroid->sMove.psFormation, (BASE_OBJECT *)psDroid);
psDroid->sMove.psFormation = NULL;
}
// move back to the repair facility if necessary
if (DROID_STOPPED(psDroid) &&
!actionReachedBuildPos(psDroid,
(SDWORD)psDroid->psTarget->pos.x,(SDWORD)psDroid->psTarget->pos.y,
(BASE_STATS *)((STRUCTURE*)psDroid->psTarget)->pStructureType ) )
{
moveDroidToNoFormation(psDroid, psDroid->psTarget->pos.x, psDroid->psTarget->pos.y);
}
break;
case DACTION_TRANSPORTWAITTOFLYIN:
//if we're moving droids to safety and currently waiting to fly back in, see if time is up
if (psDroid->player == selectedPlayer && getDroidsToSafetyFlag())
{
if ((SDWORD)(mission.ETA - (gameTime - missionGetReinforcementTime())) <= 0)
{
if (!droidRemove(psDroid, mission.apsDroidLists))
{
ASSERT( FALSE, "actionUpdate: Unable to remove transporter from mission list" );
}
addDroid(psDroid, apsDroidLists);
//set the x/y up since they were set to INVALID_XY when moved offWorld
missionGetTransporterExit(selectedPlayer, &droidX, &droidY);
psDroid->pos.x = droidX;
psDroid->pos.y = droidY;
//fly Transporter back to get some more droids
orderDroidLoc( psDroid, DORDER_TRANSPORTIN,
getLandingX(selectedPlayer), getLandingY(selectedPlayer));
}
else
{
/*if we're currently moving units to safety and waiting to fly
back in - check there is something to fly back for!*/
if (!missionDroidsRemaining(selectedPlayer))
{
//the script can call startMission for this callback for offworld missions
eventFireCallbackTrigger((TRIGGER_TYPE)CALL_START_NEXT_LEVEL);
}
}
}
break;
case DACTION_MOVE:
// moving to a location
if (DROID_STOPPED(psDroid))
{
// Got to destination
psDroid->action = DACTION_NONE;
/* notify scripts we have reached the destination
* also triggers when patrolling and reached a waypoint
*/
psScrCBOrder = psDroid->order;
psScrCBOrderDroid = psDroid;
eventFireCallbackTrigger((TRIGGER_TYPE)CALL_DROID_REACH_LOCATION);
psScrCBOrderDroid = NULL;
psScrCBOrder = DORDER_NONE;
}
//Watermelon:added multiple weapon check
else if (psDroid->numWeaps > 0)
{
for(i = 0;i < psDroid->numWeaps;i++)
{
if ( (num_weapons & (1 << (i+1))) )
{
BASE_OBJECT *psTemp = NULL;
//Watermelon:I moved psWeapStats flag update there
psWeapStats = asWeaponStats + psDroid->asWeaps[i].nStat;
if (!vtolDroid(psDroid) &&
psDroid->asWeaps[i].nStat > 0 &&
psWeapStats->rotate &&
psWeapStats->fireOnMove != FOM_NO &&
CAN_UPDATE_NAYBORS(psDroid) &&
(aiBestNearestTarget(psDroid, &psTemp, i) >= 0))
{
if (secondaryGetState(psDroid, DSO_ATTACK_LEVEL, &state))
{
if (state == DSS_ALEV_ALWAYS)
{
psDroid->action = DACTION_MOVEFIRE;
}
}
else
{
psDroid->action = DACTION_MOVEFIRE;
}
}
setDroidActionTarget(psDroid, psTemp, 0);
}
}
}
break;
case DACTION_RETURNTOPOS:
case DACTION_FIRESUPPORT_RETREAT:
if (DROID_STOPPED(psDroid))
{
// Got to destination
psDroid->action = DACTION_NONE;
}
break;
case DACTION_TRANSPORTIN:
case DACTION_TRANSPORTOUT:
actionUpdateTransporter( psDroid );
break;
case DACTION_MOVEFIRE:
//check if vtol that its armed
if (vtolEmpty(psDroid))
{
moveToRearm(psDroid);
}
bHasTarget = FALSE;
//loop through weapons and look for target for each weapon
for (i = 0;i < psDroid->numWeaps;i++)
{
if (psDroid->psActionTarget[i] == NULL)
{
BASE_OBJECT *psTemp;
if (aiBestNearestTarget(psDroid, &psTemp, i) >= 0)
{
bHasTarget = TRUE;
setDroidActionTarget(psDroid, psTemp, i);
}
}
if (psDroid->psActionTarget[i] &&
visibleObject((BASE_OBJECT*)psDroid, psDroid->psActionTarget[i]))
{
iVisible += (1 << (i+1));
}
}
for (j = 0;j < psDroid->numWeaps;j++)
{
//Watermelon:vtResult uses psActionTarget[0] for now since it's the first target
if (psDroid->psActionTarget[j] != NULL &&
validTarget((BASE_OBJECT *)psDroid, psDroid->psActionTarget[j], j))
{
// firing on something while moving
if (DROID_STOPPED(psDroid))
{
// Got to desitination
psDroid->action = DACTION_NONE;
break;
}
else if ((psDroid->psActionTarget[j] == NULL) ||
!validTarget((BASE_OBJECT *)psDroid, psDroid->psActionTarget[j], j) ||
(secondaryGetState(psDroid, DSO_ATTACK_LEVEL, &state) && (state != DSS_ALEV_ALWAYS)))
{
if (j == (psDroid->numWeaps - 1) && !bHasTarget)
{
// Target lost
psDroid->action = DACTION_MOVE;
}
else
{
continue;
}
//if Vtol - return to rearm pad
/*if (vtolDroid(psDroid))
{
moveToRearm(psDroid);
}*/
}
//check the target hasn't become one the same player ID - eg Electronic Warfare
else if (electronicDroid(psDroid) &&
(psDroid->player == psDroid->psActionTarget[j]->player))
{
setDroidActionTarget(psDroid, NULL, i);
psDroid->action = DACTION_NONE;
}
else
{
if (iVisible == 1 && !bHasTarget && j == (psDroid->numWeaps - 1))
{
// lost the target
psDroid->action = DACTION_MOVE;
for (i = 0;i < psDroid->numWeaps;i++)
{
setDroidActionTarget(psDroid, NULL, i);
}
}
if (iVisible & (1 << (j+1)))
{
bHasTarget = TRUE;
//Watermelon:I moved psWeapStats flag update there
psWeapStats = asWeaponStats + psDroid->asWeaps[j].nStat;
//Watermelon:to fix a AA-weapon attack ground unit exploit
if ( (num_weapons & (1 << (j+1))) )
{
if (psDroid->psActionTarget[j])
{
psActionTarget = psDroid->psActionTarget[j];
}
else
{
psActionTarget = psDroid->psActionTarget[0];
}
if (validTarget((BASE_OBJECT *)psDroid, psActionTarget, j))
{
if (actionTargetTurret((BASE_OBJECT*)psDroid, psActionTarget,
&(psDroid->turretRotation[j]), &(psDroid->turretPitch[j]),
&asWeaponStats[psDroid->asWeaps[j].nStat],
bInvert,j))
{
// In range - fire !!!
combFire(&psDroid->asWeaps[j], (BASE_OBJECT *)psDroid,
psActionTarget, j);
}
}
else
{
if ((iVisible & 2) && validTarget((BASE_OBJECT *)psDroid, psDroid->psActionTarget[0], j))
{
if (actionTargetTurret((BASE_OBJECT*)psDroid, psDroid->psActionTarget[0],
&(psDroid->turretRotation[j]), &(psDroid->turretPitch[j]),
&asWeaponStats[psDroid->asWeaps[j].nStat],
bInvert,j))
{
// In range - fire !!!
combFire(&psDroid->asWeaps[j], (BASE_OBJECT *)psDroid,
psDroid->psActionTarget[0], j);
}
}
}
}
}
}
}
}
//check its a VTOL unit since adding Transporter's into multiPlayer
/* check vtol attack runs */
if (vtolDroid(psDroid))
{
actionUpdateVtolAttack( psDroid );
}
break;
case DACTION_ATTACK:
ASSERT( psDroid->psActionTarget[0] != NULL,
"actionUpdateUnit: target is NULL while attacking" );
// don't wan't formations for this one
if (psDroid->sMove.psFormation)
{
formationLeave(psDroid->sMove.psFormation, (BASE_OBJECT *)psDroid);
psDroid->sMove.psFormation = NULL;
}
//check the target hasn't become one the same player ID - Electronic Warfare
if ((electronicDroid(psDroid) && (psDroid->player == psDroid->psActionTarget[0]->player)))// ||
// (secondaryGetState(psDroid, DSO_ATTACK_LEVEL, &state) && (state != DSS_ALEV_ALWAYS)))
{
for (i = 0;i < psDroid->numWeaps;i++)
{
setDroidActionTarget(psDroid, NULL, i);
}
psDroid->action = DACTION_NONE;
}
bHasTarget = FALSE;
for(i = 0;i < psDroid->numWeaps;i++)
{
//Skip main turret target changes
if (i > 0 &&
psDroid->psActionTarget[i] == NULL &&
aiChooseTarget((BASE_OBJECT*)psDroid, &psTargets[i], i, FALSE))
{
setDroidActionTarget(psDroid, psTargets[i], i);
}
if (psDroid->psActionTarget[i])
{
psActionTarget = psDroid->psActionTarget[i];
}
else
{
psActionTarget = psDroid->psActionTarget[0];
}
if ( (num_weapons & (1 << (i+1))) &&
actionVisibleTarget(psDroid, psActionTarget, i) &&
actionInRange(psDroid, psActionTarget, i))
{
bHasTarget = TRUE;
psWeapStats = asWeaponStats + psDroid->asWeaps[i].nStat;
if (validTarget((BASE_OBJECT *)psDroid, psActionTarget, i))
{
if (!psWeapStats->rotate)
{
// no rotating turret - need to check aligned with target
targetDir = (SDWORD)calcDirection(
psDroid->pos.x,psDroid->pos.y,
psActionTarget->pos.x, psActionTarget->pos.y);
dirDiff = labs(targetDir - (SDWORD)psDroid->direction);
}
else
{
dirDiff = 0;
}
if (dirDiff > FIXED_TURRET_DIR)
{
if (psDroid->sMove.Status != MOVESHUFFLE)
{
psDroid->action = DACTION_ROTATETOATTACK;
moveTurnDroid(psDroid, psActionTarget->pos.x, psActionTarget->pos.y);
}
}
else if (!psWeapStats->rotate ||
actionTargetTurret((BASE_OBJECT*)psDroid, psActionTarget,
&(psDroid->turretRotation[i]), &(psDroid->turretPitch[i]),
&asWeaponStats[psDroid->asWeaps[i].nStat],
bInvert,i))
{
/* In range - fire !!! */
combFire(&psDroid->asWeaps[i], (BASE_OBJECT *)psDroid, psActionTarget, i);
}
}
}
}
if (!bHasTarget)
{
if (((psDroid->order == DORDER_ATTACKTARGET || psDroid->order == DORDER_FIRESUPPORT) &&
secondaryGetState(psDroid, DSO_HALTTYPE, &state) && (state == DSS_HALT_HOLD) ) ||
( !vtolDroid(psDroid) &&
orderStateObj(psDroid, DORDER_FIRESUPPORT, &psTarget) && (psTarget->type == OBJ_STRUCTURE) ) )
{
// don't move if on hold or firesupport for a sensor tower
psDroid->action = DACTION_NONE; // holding, cancel the order.
}
else
{
psDroid->action = DACTION_MOVETOATTACK; // out of range - chase it
}
}
break;
case DACTION_VTOLATTACK:
//Watermelon:uses vtResult
if (psDroid->psActionTarget[0] != NULL &&
validTarget((BASE_OBJECT *)psDroid, psDroid->psActionTarget[0], 0))
{
//check if vtol that its armed
if ( (vtolEmpty(psDroid)) ||
(psDroid->psActionTarget[0] == NULL) ||
//check the target hasn't become one the same player ID - Electronic Warfare
(electronicDroid(psDroid) && (psDroid->player == psDroid->psActionTarget[0]->player)) ||
!validTarget((BASE_OBJECT *)psDroid, psDroid->psActionTarget[0], 0) )// ||
// (secondaryGetState(psDroid, DSO_ATTACK_LEVEL, &state) && (state != DSS_ALEV_ALWAYS)))
{
moveToRearm(psDroid);
break;
}
for(i = 0;i <psDroid->numWeaps;i++)
{
if ( (num_weapons & (1 << (i+1))) &&
validTarget((BASE_OBJECT *)psDroid, psDroid->psActionTarget[0], i) )
{
//Watermelon:I moved psWeapStats flag update there
psWeapStats = asWeaponStats + psDroid->asWeaps[i].nStat;
if (actionVisibleTarget(psDroid, psDroid->psActionTarget[0], i))
{
if ( actionInRange(psDroid, psDroid->psActionTarget[0], i) )
{
if ( psDroid->player == selectedPlayer )
{
audio_QueueTrackMinDelay( ID_SOUND_COMMENCING_ATTACK_RUN2,
VTOL_ATTACK_AUDIO_DELAY );
}
if (actionTargetTurret((BASE_OBJECT*)psDroid, psDroid->psActionTarget[0],
&(psDroid->turretRotation[i]), &(psDroid->turretPitch[i]),
&asWeaponStats[psDroid->asWeaps[i].nStat],
bInvert,i))
{
// In range - fire !!!
combFire(&psDroid->asWeaps[i], (BASE_OBJECT *)psDroid,
psDroid->psActionTarget[0], i);
}
}
else
{
actionTargetTurret((BASE_OBJECT*)psDroid, psDroid->psActionTarget[0],
&(psDroid->turretRotation[i]), &(psDroid->turretPitch[i]),
&asWeaponStats[psDroid->asWeaps[i].nStat],
bInvert,i);
}
}
}
}
}
/* else
{
// lost the target
psDroid->action = DACTION_MOVETOATTACK;
moveDroidTo(psDroid, psDroid->psActionTarget->pos.x, psDroid->psActionTarget->pos.y);
}*/
/* check vtol attack runs */
// actionUpdateVtolAttack( psDroid );
/* circle around target if hovering and not cyborg */
if (DROID_STOPPED(psDroid))
{
actionAddVtolAttackRun( psDroid );
}
else
{
// if the vtol is close to the target, go around again
xdiff = (SDWORD)psDroid->pos.x - (SDWORD)psDroid->psActionTarget[0]->pos.x;
ydiff = (SDWORD)psDroid->pos.y - (SDWORD)psDroid->psActionTarget[0]->pos.y;
rangeSq = xdiff*xdiff + ydiff*ydiff;
if (rangeSq < (VTOL_ATTACK_TARDIST*VTOL_ATTACK_TARDIST))
{
// don't do another attack run if already moving away from the target
xdiff = (SDWORD)psDroid->sMove.DestinationX - (SDWORD)psDroid->psActionTarget[0]->pos.x;
ydiff = (SDWORD)psDroid->sMove.DestinationY - (SDWORD)psDroid->psActionTarget[0]->pos.y;
if ((xdiff*xdiff + ydiff*ydiff) < (VTOL_ATTACK_TARDIST*VTOL_ATTACK_TARDIST))
{
actionAddVtolAttackRun( psDroid );
}
}
// if the vtol is far enough away head for the target again
// else if (rangeSq > (VTOL_ATTACK_RETURNDIST*VTOL_ATTACK_RETURNDIST))
else if (rangeSq > (SDWORD)(psWeapStats->longRange*psWeapStats->longRange))
{
// don't do another attack run if already heading for the target
xdiff = (SDWORD)psDroid->sMove.DestinationX - (SDWORD)psDroid->psActionTarget[0]->pos.x;
ydiff = (SDWORD)psDroid->sMove.DestinationY - (SDWORD)psDroid->psActionTarget[0]->pos.y;
if ((xdiff*xdiff + ydiff*ydiff) > (VTOL_ATTACK_TARDIST*VTOL_ATTACK_TARDIST))
{
moveDroidToDirect(psDroid, psDroid->psActionTarget[0]->pos.x,psDroid->psActionTarget[0]->pos.y);
}
}
}
break;
case DACTION_MOVETOATTACK:
// don't wan't formations for this one
if (psDroid->sMove.psFormation)
{
formationLeave(psDroid->sMove.psFormation, (BASE_OBJECT *)psDroid);
psDroid->sMove.psFormation = NULL;
}
// send vtols back to rearm
if (vtolDroid(psDroid) &&
vtolEmpty(psDroid))
{
moveToRearm(psDroid);
break;
}
ASSERT(psDroid->psActionTarget[0] != NULL, "action update move to attack target is NULL");
//check the target hasn't become one the same player ID - Electronic Warfare
if ((electronicDroid(psDroid) && (psDroid->player == psDroid->psActionTarget[0]->player)) ||
!validTarget((BASE_OBJECT *)psDroid, psDroid->psActionTarget[0], 0) )
{
for (i = 0;i < psDroid->numWeaps;i++)
{
setDroidActionTarget(psDroid, NULL, i);
}
psDroid->action = DACTION_NONE;
}
else
{
if (actionVisibleTarget(psDroid, psDroid->psActionTarget[0], 0))
{
for(i = 0;i < psDroid->numWeaps;i++)
{
if( (num_weapons & (1 << (i+1))) &&
validTarget((BASE_OBJECT *)psDroid, psDroid->psActionTarget[0], i) &&
actionVisibleTarget(psDroid, psDroid->psActionTarget[0], i) )
{
psWeapStats = asWeaponStats + psDroid->asWeaps[i].nStat;
if (psWeapStats->rotate)
{
actionTargetTurret((BASE_OBJECT*)psDroid, psDroid->psActionTarget[0],
&(psDroid->turretRotation[i]), &(psDroid->turretPitch[i]),
&asWeaponStats[psDroid->asWeaps[i].nStat],
bInvert,i);
}
bChaseBloke = FALSE;
if (!vtolDroid(psDroid) &&
psDroid->psActionTarget[0]->type == OBJ_DROID &&
((DROID *)psDroid->psActionTarget[0])->droidType == DROID_PERSON &&
psWeapStats->fireOnMove != FOM_NO)
{
bChaseBloke = TRUE;
}
if ( (actionInAttackRange(psDroid, psDroid->psActionTarget[0], i)) &&
!bChaseBloke)
{
/* Stop the droid moving any closer */
// ASSERT( psDroid->pos.x != 0 && psDroid->pos.y != 0,
// "moveUpdateUnit: Unit at (0,0)" );
/* init vtol attack runs count if necessary */
if ( psPropStats->propulsionType == LIFT )
{
psDroid->action = DACTION_VTOLATTACK;
//actionAddVtolAttackRun( psDroid );
//actionUpdateVtolAttack( psDroid );
}
else
{
moveStopDroid(psDroid);
if (psWeapStats->rotate)
{
psDroid->action = DACTION_ATTACK;
}
else
{
psDroid->action = DACTION_ROTATETOATTACK;
moveTurnDroid(psDroid, psDroid->psActionTarget[0]->pos.x,psDroid->psActionTarget[0]->pos.y);
}
}
}
else if ( actionInRange(psDroid, psDroid->psActionTarget[0], i) )
{
// fire while closing range
combFire(&psDroid->asWeaps[i], (BASE_OBJECT *)psDroid, psDroid->psActionTarget[0], i);
}
}
}
}
else
{
for(i = 0;i < psDroid->numWeaps;i++)
{
if ((psDroid->turretRotation[i] != 0) ||
(psDroid->turretPitch[i] != 0))
{
actionAlignTurret((BASE_OBJECT *)psDroid, i);
}
}
}
if (DROID_STOPPED(psDroid) && psDroid->action != DACTION_ATTACK)
{
/* Stopped moving but haven't reached the target - possibly move again */
if (psDroid->order == DORDER_ATTACKTARGET && secondaryGetState(psDroid, DSO_HALTTYPE, &state) && (state == DSS_HALT_HOLD))
{
psDroid->action = DACTION_NONE; // on hold, give up.
}
else if ( actionInsideMinRange(psDroid, psDroid->psActionTarget[0], 0) )
{
//Watermelon:'hack' to make the droid to check the primary turrent instead of all
psWeapStats = asWeaponStats + psDroid->asWeaps[0].nStat;
if ( proj_Direct( psWeapStats ) )
{
// try and extend the range
actionCalcPullBackPoint((BASE_OBJECT *)psDroid, psDroid->psActionTarget[0], &pbx,&pby);
moveDroidTo(psDroid, (UDWORD)pbx, (UDWORD)pby);
}
else
{
if (psWeapStats->rotate)
{
psDroid->action = DACTION_ATTACK;
}
else
{
psDroid->action = DACTION_ROTATETOATTACK;
moveTurnDroid( psDroid, psDroid->psActionTarget[0]->pos.x,
psDroid->psActionTarget[0]->pos.y);
}
}
}
else
{
// try to close the range
moveDroidTo(psDroid, psDroid->psActionTarget[0]->pos.x, psDroid->psActionTarget[0]->pos.y);
}
}
}
break;
case DACTION_SULK:
// unable to route to target ... don't do anything aggressive until time is up
// we need to do something defensive at this point ???
//hmmm, hope this doesn't cause any problems!
if (gameTime > psDroid->actionStarted)
{
// debug( LOG_NEVER, "sulk over.. %p\n", psDroid );
psDroid->action = DACTION_NONE; // Sulking is over lets get back to the action ... is this all I need to do to get it back into the action?
}
break;
case DACTION_ROTATETOATTACK:
// if (DROID_STOPPED(psDroid))
if (psDroid->sMove.Status != MOVETURNTOTARGET)
{
psDroid->action = DACTION_ATTACK;
}
break;
case DACTION_MOVETOBUILD:
// The droid cannot be in a formation
if (psDroid->sMove.psFormation != NULL)
{
formationLeave(psDroid->sMove.psFormation, (BASE_OBJECT *)psDroid);
psDroid->sMove.psFormation = NULL;
}
// moving to a location to build a structure
if (actionReachedBuildPos(psDroid,
(SDWORD)psDroid->orderX,(SDWORD)psDroid->orderY, psDroid->psTarStats) &&
!actionDroidOnBuildPos(psDroid,
(SDWORD)psDroid->orderX,(SDWORD)psDroid->orderY, psDroid->psTarStats))
{
moveStopDroid(psDroid);
bDoHelpBuild = FALSE;
// Got to destination - start building
psStructStats = (STRUCTURE_STATS*)psDroid->psTarStats;
if (psDroid->order == DORDER_BUILD && psDroid->psTarget == NULL)
{
// Starting a new structure
// calculate the top left of the structure
tlx = (SDWORD)psDroid->orderX - (SDWORD)(psStructStats->baseWidth * TILE_UNITS)/2;
tly = (SDWORD)psDroid->orderY - (SDWORD)(psStructStats->baseBreadth * TILE_UNITS)/2;
//need to check if something has already started building here?
//unless its a module!
if (IsStatExpansionModule(psStructStats))
{
debug( LOG_NEVER, "DACTION_MOVETOBUILD: setUpBuildModule\n");
setUpBuildModule(psDroid);
}
else if (TILE_HAS_STRUCTURE(mapTile(map_coord(psDroid->orderX), map_coord(psDroid->orderY))))
{
// structure on the build location - see if it is the same type
psStruct = getTileStructure(map_coord(psDroid->orderX), map_coord(psDroid->orderY));
if (psStruct->pStructureType == (STRUCTURE_STATS *)psDroid->psTarStats)
{
// same type - do a help build
setDroidTarget(psDroid, (BASE_OBJECT *)psStruct);
bDoHelpBuild = TRUE;
}
else if ((psStruct->pStructureType->type == REF_WALL ||
psStruct->pStructureType->type == REF_WALLCORNER) &&
((STRUCTURE_STATS *)psDroid->psTarStats)->type == REF_DEFENSE)
{
// building a gun tower over a wall - OK
if (droidStartBuild(psDroid))
{
debug( LOG_NEVER, "DACTION_MOVETOBUILD: start foundation\n");
psDroid->action = DACTION_BUILD;
}
else
{
psDroid->action = DACTION_NONE;
}
}
else
{
psDroid->action = DACTION_NONE;
}
}
else if (!validLocation((BASE_STATS*)psDroid->psTarStats,
map_coord(tlx),
map_coord(tly),
psDroid->player,
FALSE))
{
debug( LOG_NEVER, "DACTION_MOVETOBUILD: !validLocation\n");
psDroid->action = DACTION_NONE;
}
else
{
psDroid->action = DACTION_BUILD_FOUNDATION;
psDroid->actionStarted = gameTime;
psDroid->actionPoints = 0;
}
}
else if( (psDroid->order == DORDER_LINEBUILD||psDroid->order==DORDER_BUILD)
&&(psStructStats->type == REF_WALL || psStructStats->type == REF_WALLCORNER ||
psStructStats->type == REF_DEFENSE))
{
// building a wall.
psTile = mapTile(map_coord(psDroid->orderX), map_coord(psDroid->orderY));
if (psDroid->psTarget == NULL
&& (TILE_HAS_STRUCTURE(psTile)
|| TILE_HAS_FEATURE(psTile)))
{
if (TILE_HAS_STRUCTURE(psTile))
{
// structure on the build location - see if it is the same type
psStruct = getTileStructure(map_coord(psDroid->orderX), map_coord(psDroid->orderY));
if (psStruct->pStructureType == (STRUCTURE_STATS *)psDroid->psTarStats)
{
// same type - do a help build
setDroidTarget(psDroid, (BASE_OBJECT *)psStruct);
bDoHelpBuild = TRUE;
}
else if ((psStruct->pStructureType->type == REF_WALL || psStruct->pStructureType->type == REF_WALLCORNER) &&
((STRUCTURE_STATS *)psDroid->psTarStats)->type == REF_DEFENSE)
{
// building a gun tower over a wall - OK
if (droidStartBuild(psDroid))
{
debug( LOG_NEVER, "DACTION_MOVETOBUILD: start foundation\n");
psDroid->action = DACTION_BUILD;
}
else
{
psDroid->action = DACTION_NONE;
}
}
else
{
psDroid->action = DACTION_NONE;
}
}
else
{
psDroid->action = DACTION_NONE;
}
}
else if (droidStartBuild(psDroid))
{
psDroid->action = DACTION_BUILD;
intBuildStarted(psDroid);
}
else
{
psDroid->action = DACTION_NONE;
}
}
else
{
bDoHelpBuild = TRUE;
}
if (bDoHelpBuild)
{
// continuing a partially built structure (order = helpBuild)
if (droidStartBuild(psDroid))
{
debug( LOG_NEVER, "DACTION_MOVETOBUILD: starting help build\n");
psDroid->action = DACTION_BUILD;
intBuildStarted(psDroid);
}
else
{
debug( LOG_NEVER, "DACTION_MOVETOBUILD: giving up (3)\n");
psDroid->action = DACTION_NONE;
}
}
}
else if (DROID_STOPPED(psDroid))
{
if (actionDroidOnBuildPos(psDroid,
(SDWORD)psDroid->orderX,(SDWORD)psDroid->orderY, psDroid->psTarStats))
{
actionHomeBasePos(psDroid->player, &pbx,&pby);
moveDroidToNoFormation(psDroid, (UDWORD)pbx,(UDWORD)pby);
}
else
{
moveDroidToNoFormation(psDroid, psDroid->actionX,psDroid->actionY);
}
}
break;
case DACTION_BUILD:
// The droid cannot be in a formation
if (psDroid->sMove.psFormation != NULL)
{
formationLeave(psDroid->sMove.psFormation, (BASE_OBJECT *)psDroid);
psDroid->sMove.psFormation = NULL;
}
if (DROID_STOPPED(psDroid) &&
!actionReachedBuildPos(psDroid,
(SDWORD)psDroid->orderX,(SDWORD)psDroid->orderY, psDroid->psTarStats))
{
// psDroid->action = DACTION_MOVETOBUILD;
moveDroidToNoFormation(psDroid, psDroid->actionX,psDroid->actionY);
}
else if (!DROID_STOPPED(psDroid) &&
psDroid->sMove.Status != MOVETURNTOTARGET &&
psDroid->sMove.Status != MOVESHUFFLE &&
actionReachedBuildPos(psDroid,
(SDWORD)psDroid->orderX,(SDWORD)psDroid->orderY, psDroid->psTarStats))
{
moveStopDroid(psDroid);
}
if (droidUpdateBuild(psDroid))
{
/* if ( (psDroid->psTarget) && (psDroid->sMove.Status != MOVESHUFFLE) )
{
moveTurnDroid(psDroid,psDroid->psTarget->pos.x,psDroid->psTarget->pos.y);
}*/
// Watermelon:make construct droid to use turretRotation[0]
actionTargetTurret((BASE_OBJECT*)psDroid, psDroid->psActionTarget[0],
&(psDroid->turretRotation[0]), &(psDroid->turretPitch[0]),
NULL,FALSE,0);
}
break;
case DACTION_MOVETODEMOLISH:
case DACTION_MOVETOREPAIR:
case DACTION_MOVETOCLEAR:
case DACTION_MOVETORESTORE:
// The droid cannot be in a formation
if (psDroid->sMove.psFormation != NULL)
{
formationLeave(psDroid->sMove.psFormation, (BASE_OBJECT *)psDroid);
psDroid->sMove.psFormation = NULL;
}
// see if the droid is at the edge of what it is moving to
if (actionReachedBuildPos(psDroid,
(SDWORD)psDroid->actionX,(SDWORD)psDroid->actionY, psDroid->psTarStats) &&
!actionDroidOnBuildPos(psDroid,
(SDWORD)psDroid->actionX,(SDWORD)psDroid->actionY, psDroid->psTarStats))
{
moveStopDroid(psDroid);
// got to the edge - start doing whatever it was meant to do
switch (psDroid->action)
{
case DACTION_MOVETODEMOLISH:
psDroid->action = droidStartDemolishing(psDroid) ? DACTION_DEMOLISH : DACTION_NONE;
break;
case DACTION_MOVETOREPAIR:
psDroid->action = droidStartRepair(psDroid) ? DACTION_REPAIR : DACTION_NONE;
break;
case DACTION_MOVETOCLEAR:
psDroid->action = droidStartClearing(psDroid) ? DACTION_CLEARWRECK : DACTION_NONE;
break;
case DACTION_MOVETORESTORE:
psDroid->action = droidStartRestore(psDroid) ? DACTION_RESTORE : DACTION_NONE;
break;
default:
break;
}
}
else if (DROID_STOPPED(psDroid))
{
if (actionDroidOnBuildPos(psDroid,
(SDWORD)psDroid->actionX,(SDWORD)psDroid->actionY, psDroid->psTarStats))
{
actionHomeBasePos(psDroid->player, &pbx,&pby);
moveDroidToNoFormation(psDroid, (UDWORD)pbx,(UDWORD)pby);
}
else
{
moveDroidToNoFormation(psDroid, psDroid->actionX,psDroid->actionY);
}
}
break;
case DACTION_DEMOLISH:
case DACTION_REPAIR:
case DACTION_CLEARWRECK:
case DACTION_RESTORE:
// set up for the specific action
switch (psDroid->action)
{
case DACTION_DEMOLISH:
moveAction = DACTION_MOVETODEMOLISH;
actionUpdateFunc = droidUpdateDemolishing;
break;
case DACTION_REPAIR:
moveAction = DACTION_MOVETOREPAIR;
actionUpdateFunc = droidUpdateRepair;
break;
case DACTION_CLEARWRECK:
moveAction = DACTION_MOVETOCLEAR;
actionUpdateFunc = droidUpdateClearing;
break;
case DACTION_RESTORE:
moveAction = DACTION_MOVETORESTORE;
actionUpdateFunc = droidUpdateRestore;
break;
default:
break;
}
// now do the action update
if (DROID_STOPPED(psDroid) &&
!actionReachedBuildPos(psDroid,
(SDWORD)psDroid->actionX,(SDWORD)psDroid->actionY, psDroid->psTarStats))
{
moveDroidToNoFormation(psDroid, psDroid->actionX,psDroid->actionY);
}
else if (!DROID_STOPPED(psDroid) &&
psDroid->sMove.Status != MOVETURNTOTARGET &&
psDroid->sMove.Status != MOVESHUFFLE &&
actionReachedBuildPos(psDroid,
(SDWORD)psDroid->actionX,(SDWORD)psDroid->actionY, psDroid->psTarStats))
{
moveStopDroid(psDroid);
}
else if ( actionUpdateFunc(psDroid) )
{
//Watermelon:use 0 for non-combat(only 1 'weapon')
actionTargetTurret((BASE_OBJECT*)psDroid, psDroid->psActionTarget[0],
&(psDroid->turretRotation[0]), &(psDroid->turretPitch[0]),
NULL,FALSE,0);
}
else if (psDroid->action == DACTION_CLEARWRECK)
{
//see if there is any other wreckage in the area
psDroid->action = DACTION_NONE;
psNextWreck = checkForWreckage(psDroid);
if (psNextWreck)
{
orderDroidObj(psDroid, DORDER_CLEARWRECK, (BASE_OBJECT *)psNextWreck);
}
}
else
{
psDroid->action = DACTION_NONE;
}
break;
case DACTION_MOVETOREARMPOINT:
/* moving to rearm pad */
if (DROID_STOPPED(psDroid))
{
psDroid->action = DACTION_WAITDURINGREARM;
}
break;
case DACTION_MOVETOREPAIRPOINT:
// don't want to be in a formation for this move
if (psDroid->sMove.psFormation != NULL)
{
formationLeave(psDroid->sMove.psFormation, (BASE_OBJECT *)psDroid);
psDroid->sMove.psFormation = NULL;
}
/* moving from front to rear of repair facility or rearm pad */
/* xdiff = (SDWORD)psDroid->pos.x - ((SDWORD)psDroid->psActionTarget->pos.x + TILE_UNITS);
ydiff = (SDWORD)psDroid->pos.y - (SDWORD)psDroid->psActionTarget->pos.y;
if (xdiff*xdiff + ydiff*ydiff < (TILE_UNITS/2)*(TILE_UNITS/2))*/
if (actionReachedBuildPos(psDroid, psDroid->psActionTarget[0]->pos.x,psDroid->psActionTarget[0]->pos.y,
(BASE_STATS *)((STRUCTURE *)psDroid->psActionTarget[0])->pStructureType))
{
moveStopDroid(psDroid);
psDroid->action = DACTION_WAITDURINGREPAIR;
}
else if (DROID_STOPPED(psDroid))
{
moveDroidToNoFormation(psDroid, psDroid->psActionTarget[0]->pos.x,
psDroid->psActionTarget[0]->pos.y);
}
break;
case DACTION_BUILD_FOUNDATION:
//building a structure's foundation - flattening the ground for now
{
psTile = mapTile(map_coord(psDroid->orderX), map_coord(psDroid->orderY));
psStructStats = (STRUCTURE_STATS*)psDroid->psTarStats;
tlx = (SDWORD)psDroid->orderX - (SDWORD)(psStructStats->baseWidth * TILE_UNITS)/2;
tly = (SDWORD)psDroid->orderY - (SDWORD)(psStructStats->baseBreadth * TILE_UNITS)/2;
if ((psDroid->psTarget == NULL) &&
(TILE_HAS_STRUCTURE(psTile) ||
TILE_HAS_FEATURE(psTile)))
{
if (TILE_HAS_STRUCTURE(psTile))
{
// structure on the build location - see if it is the same type
psStruct = getTileStructure(map_coord(psDroid->orderX), map_coord(psDroid->orderY));
if (psStruct->pStructureType == (STRUCTURE_STATS *)psDroid->psTarStats)
{
// same type - do a help build
setDroidTarget(psDroid, (BASE_OBJECT *)psStruct);
}
else
{
psDroid->action = DACTION_NONE;
}
}
else if (!validLocation((BASE_STATS*)psDroid->psTarStats,
map_coord(tlx),
map_coord(tly),
psDroid->player,
FALSE))
{
psDroid->action = DACTION_NONE;
}
}
//ready to start building the structure
if ( psDroid->action != DACTION_NONE &&
droidStartBuild(psDroid))
{
debug( LOG_NEVER, "DACTION_BUILD_FOUNDATION: start build\n");
psDroid->action = DACTION_BUILD;
}
else
{
debug( LOG_NEVER, "DACTION_BUILD_FOUNDATION: giving up\n");
psDroid->action = DACTION_NONE;
}
}
break;
case DACTION_OBSERVE:
// align the turret
actionTargetTurret((BASE_OBJECT*)psDroid, psDroid->psActionTarget[0],
&(psDroid->turretRotation[0]), &(psDroid->turretPitch[0]),
NULL,FALSE,0);
if (cbSensorDroid(psDroid))
{
// don't move to the target, just make sure it is visible
// Anyone commenting this out will get a knee capping from John.
// You have been warned!!
psDroid->psActionTarget[0]->visible[psDroid->player] = UBYTE_MAX;
}
else
{
// make sure the target is within sensor range
xdiff = (SDWORD)psDroid->pos.x - (SDWORD)psDroid->psActionTarget[0]->pos.x;
ydiff = (SDWORD)psDroid->pos.y - (SDWORD)psDroid->psActionTarget[0]->pos.y;
//if change this back - change in MOVETOOBSERVE as well
//rangeSq = 2 * (SDWORD)psDroid->sensorRange / 3;
rangeSq = (SDWORD)psDroid->sensorRange;
rangeSq = rangeSq * rangeSq;
if (!visibleObject((BASE_OBJECT *)psDroid, psDroid->psActionTarget[0]) ||
xdiff*xdiff + ydiff*ydiff >= rangeSq)
{
/* if (secondaryGetState(psDroid, DSO_HALTTYPE, &state) && (state == DSS_HALT_HOLD))
{
psDroid->action = DACTION_NONE; // holding, don't move.
}
else*/
{
psDroid->action = DACTION_MOVETOOBSERVE;
moveDroidTo(psDroid, psDroid->psActionTarget[0]->pos.x,psDroid->psActionTarget[0]->pos.y);
}
}
}
break;
case DACTION_MOVETOOBSERVE:
// align the turret
actionTargetTurret((BASE_OBJECT*)psDroid, psDroid->psActionTarget[0],
&(psDroid->turretRotation[0]), &(psDroid->turretPitch[0]),
NULL,FALSE,0);
if (visibleObject((BASE_OBJECT *)psDroid, psDroid->psActionTarget[0]))
{
// make sure the target is within sensor range
xdiff = (SDWORD)psDroid->pos.x - (SDWORD)psDroid->psActionTarget[0]->pos.x;
ydiff = (SDWORD)psDroid->pos.y - (SDWORD)psDroid->psActionTarget[0]->pos.y;
//if change this back - change in OBSERVE as well
//rangeSq = 2 * (SDWORD)psDroid->sensorRange / 3;
rangeSq = (SDWORD)psDroid->sensorRange;
rangeSq = rangeSq * rangeSq;
if ((xdiff*xdiff + ydiff*ydiff < rangeSq) &&
!DROID_STOPPED(psDroid))
{
psDroid->action = DACTION_OBSERVE;
moveStopDroid(psDroid);
}
}
if (DROID_STOPPED(psDroid) && psDroid->action == DACTION_MOVETOOBSERVE)
{
/* if (secondaryGetState(psDroid, DSO_HALTTYPE, &state) && (state == DSS_HALT_HOLD))
{
psDroid->action = DACTION_NONE; // on hold, don't go any further.
}
else*/
{
moveDroidTo(psDroid, psDroid->psActionTarget[0]->pos.x,psDroid->psActionTarget[0]->pos.y);
}
}
break;
case DACTION_FIRESUPPORT:
//can be either a droid or a structure now - AB 7/10/98
ASSERT( (psDroid->psTarget->type == OBJ_DROID ||
psDroid->psTarget->type == OBJ_STRUCTURE) &&
(psDroid->psTarget->player == psDroid->player),
"DACTION_FIRESUPPORT: incorrect target type" );
/* if (orderState(((DROID *)psDroid->psTarget), DORDER_OBSERVE))
{
// move to attack
psDroid->action = DACTION_MOVETOFSUPP_ATTACK;
setDroidActionTarget(psDroid, psDroid->psTarget->psTarget, 0);
moveDroidTo(psDroid, psDroid->psActionTarget->pos.x, psDroid->psActionTarget->pos.y);
}
else
{*/
//Move droids attached to structures and droids now...AB 13/10/98
//move (indirect weapon)droids attached to a sensor
//if (psDroid->psTarget->type == OBJ_DROID)
//{
//don't move VTOL's
// also don't move closer to sensor towers
if (!vtolDroid(psDroid) &&
(psDroid->psTarget->type != OBJ_STRUCTURE))
{
//move droids to within short range of the sensor now!!!!
xdiff = (SDWORD)psDroid->pos.x - (SDWORD)psDroid->psTarget->pos.x;
ydiff = (SDWORD)psDroid->pos.y - (SDWORD)psDroid->psTarget->pos.y;
// make sure the weapon droid is within 2/3 weapon range of the sensor
//rangeSq = 2 * proj_GetLongRange(asWeaponStats + psDroid->asWeaps[0].nStat) / 3;
rangeSq = asWeaponStats[psDroid->asWeaps[0].nStat].shortRange;
rangeSq = rangeSq * rangeSq;
if (xdiff*xdiff + ydiff*ydiff < rangeSq)
{
if (!DROID_STOPPED(psDroid))
{
moveStopDroid(psDroid);
}
}
else
{
if (!DROID_STOPPED(psDroid))
{
xdiff = (SDWORD)psDroid->psTarget->pos.x - (SDWORD)psDroid->sMove.DestinationX;
ydiff = (SDWORD)psDroid->psTarget->pos.y - (SDWORD)psDroid->sMove.DestinationY;
}
if (DROID_STOPPED(psDroid) ||
(xdiff*xdiff + ydiff*ydiff > rangeSq))
{
if (secondaryGetState(psDroid, DSO_HALTTYPE, &state) && (
state == DSS_HALT_HOLD))
{
// droid on hold, don't allow moves.
psDroid->action = DACTION_NONE;
}
else
{
// move in range
moveDroidTo(psDroid, psDroid->psTarget->pos.x,psDroid->psTarget->pos.y);
}
}
}
}
//}
break;
case DACTION_DESTRUCT:
if ((psDroid->actionStarted + ACTION_DESTRUCT_TIME) < gameTime)
{
if ( psDroid->droidType == DROID_PERSON )
{
droidBurn(psDroid);
}
else
{
destroyDroid(psDroid);
}
}
break;
case DACTION_MOVETODROIDREPAIR:
// moving to repair a droid
xdiff = (SDWORD)psDroid->pos.x - (SDWORD)psDroid->psActionTarget[0]->pos.x;
ydiff = (SDWORD)psDroid->pos.y - (SDWORD)psDroid->psActionTarget[0]->pos.y;
if ( xdiff*xdiff + ydiff*ydiff < REPAIR_RANGE )
{
// Got to destination - start repair
//rotate turret to point at droid being repaired
/*if (actionTargetTurret((BASE_OBJECT*)psDroid,
psDroid->psActionTarget, &(psDroid->turretRotation),
&(psDroid->turretPitch), psDroid->turretRotRate,
(SWORD)(psDroid->turretRotRate/2), FALSE, FALSE))*/
//Watermelon:use 0 for repair droid
if (actionTargetTurret((BASE_OBJECT*)psDroid,
psDroid->psActionTarget[0], &(psDroid->turretRotation[0]),
&(psDroid->turretPitch[0]), NULL, FALSE, 0))
{
if (droidStartDroidRepair(psDroid))
{
psDroid->action = DACTION_DROIDREPAIR;
}
else
{
psDroid->action = DACTION_NONE;
}
}
}
if (DROID_STOPPED(psDroid))
{
// Couldn't reach destination - try and find a new one
psDroid->actionX = psDroid->psActionTarget[0]->pos.x;
psDroid->actionY = psDroid->psActionTarget[0]->pos.y;
moveDroidTo(psDroid, psDroid->actionX, psDroid->actionY);
}
break;
case DACTION_DROIDREPAIR:
//if not doing self-repair
//Watermelon:use 0 for repair droid
if (psDroid->psActionTarget[0] != (BASE_OBJECT *)psDroid)
{
actionTargetTurret((BASE_OBJECT*)psDroid,
psDroid->psActionTarget[0], &(psDroid->turretRotation[0]),
&(psDroid->turretPitch[0]), NULL, FALSE, 0);
}
//check still next to the damaged droid
xdiff = (SDWORD)psDroid->pos.x - (SDWORD)psDroid->psActionTarget[0]->pos.x;
ydiff = (SDWORD)psDroid->pos.y - (SDWORD)psDroid->psActionTarget[0]->pos.y;
if ( xdiff*xdiff + ydiff*ydiff > REPAIR_RANGE )
{
/*once started - don't allow the Repair droid to follow the
damaged droid for too long*/
/*if (psDroid->actionPoints)
{
if (gameTime - psDroid->actionStarted > KEEP_TRYING_REPAIR)
{
addConsoleMessage("Repair Droid has given up!",DEFAULT_JUSTIFY);
psDroid->action = DACTION_NONE;
break;
}
}*/
// damaged droid has moved off - follow!
psDroid->actionX = psDroid->psActionTarget[0]->pos.x;
psDroid->actionY = psDroid->psActionTarget[0]->pos.y;
psDroid->action = DACTION_MOVETODROIDREPAIR;
moveDroidTo(psDroid, psDroid->actionX, psDroid->actionY);
}
else
{
if (!droidUpdateDroidRepair(psDroid))
{
psDroid->action = DACTION_NONE;
//if the order is RTR then resubmit order so that the unit will go to repair facility point
if (orderState(psDroid,DORDER_RTR))
{
orderDroid(psDroid, DORDER_RTR);
}
}
else
{
// don't let the target for a repair shuffle
if (((DROID *)psDroid->psActionTarget[0])->sMove.Status == MOVESHUFFLE)
{
moveStopDroid((DROID *)psDroid->psActionTarget[0]);
}
}
}
break;
case DACTION_WAITFORREARM:
// wait here for the rearm pad to ask the vtol to move
if (psDroid->psActionTarget[0] == NULL)
{
// rearm pad destroyed - move to another
moveToRearm(psDroid);
break;
}
if (DROID_STOPPED(psDroid) && vtolHappy(psDroid))
{
// don't actually need to rearm so just sit next to the rearm pad
psDroid->action = DACTION_NONE;
}
break;
case DACTION_CLEARREARMPAD:
if (DROID_STOPPED(psDroid))
{
psDroid->action = DACTION_NONE;
}
break;
case DACTION_WAITDURINGREARM:
// this gets cleared by the rearm pad
break;
case DACTION_MOVETOREARM:
if (psDroid->psActionTarget[0] == NULL)
{
// base destroyed - find another
moveToRearm(psDroid);
break;
}
if (visibleObject((BASE_OBJECT *)psDroid, psDroid->psActionTarget[0]))
{
// got close to the rearm pad - now find a clear one
debug( LOG_NEVER, "Unit %d: seen rearm pad\n", psDroid->id );
psStruct = findNearestReArmPad(psDroid, (STRUCTURE *)psDroid->psActionTarget[0], TRUE);
if (psStruct != NULL)
{
// found a clear landing pad - go for it
debug( LOG_NEVER, "Found clear rearm pad\n" );
setDroidActionTarget(psDroid, (BASE_OBJECT *)psStruct, 0);
}
psDroid->action = DACTION_WAITFORREARM;
}
if (DROID_STOPPED(psDroid) ||
(psDroid->action == DACTION_WAITFORREARM))
{
droidX = psDroid->psActionTarget[0]->pos.x;
droidY = psDroid->psActionTarget[0]->pos.y;
if (!actionVTOLLandingPos(psDroid, &droidX, &droidY))
{
// totally bunged up - give up
debug( LOG_NEVER, "DACTION_MOVETOREARM: couldn't find a clear tile near rearm pad - RTB\n" );
orderDroid(psDroid, DORDER_RTB);
break;
}
moveDroidToDirect(psDroid, droidX,droidY);
}
break;
default:
ASSERT(!"unknown action", "actionUpdateUnit: unknown action");
break;
}
if (psDroid->action != DACTION_MOVEFIRE &&
psDroid->action != DACTION_ATTACK &&
psDroid->action != DACTION_MOVETOATTACK &&
psDroid->action != DACTION_MOVETODROIDREPAIR &&
psDroid->action != DACTION_DROIDREPAIR &&
psDroid->action != DACTION_BUILD &&
psDroid->action != DACTION_OBSERVE &&
psDroid->action != DACTION_MOVETOOBSERVE)
{
//Watermelon:use 0 for all non-combat droid types
if (psDroid->numWeaps == 0)
{
if ((psDroid->turretRotation[0] != 0) ||
(psDroid->turretPitch[0] != 0))
{
actionAlignTurret((BASE_OBJECT *)psDroid, 0);
}
}
else
{
for (i = 0;i < psDroid->numWeaps;i++)
{
if ((psDroid->turretRotation[i] != 0) ||
(psDroid->turretPitch[i] != 0))
{
actionAlignTurret((BASE_OBJECT *)psDroid, i);
}
}
}
}
CHECK_DROID(psDroid);
}
/* Overall action function that is called by the specific action functions */
static void actionDroidBase(DROID *psDroid, DROID_ACTION_DATA *psAction)
{
SECONDARY_STATE state;
SDWORD pbx,pby;
WEAPON_STATS *psWeapStats;
UDWORD droidX,droidY;
BASE_OBJECT *psTarget;
//Watermelon:added MinRangeResult;
UBYTE i;
CHECK_DROID(psDroid);
switch (psAction->action)
{
case DACTION_NONE:
// Clear up what ever the droid was doing before if necessary
if (!DROID_STOPPED(psDroid))
{
moveStopDroid(psDroid);
}
psDroid->action = DACTION_NONE;
psDroid->actionX = 0;
psDroid->actionY = 0;
psDroid->actionStarted = 0;
psDroid->actionPoints = 0;
psDroid->powerAccrued = 0;
if (psDroid->numWeaps > 0)
{
for (i = 0;i < psDroid->numWeaps;i++)
{
setDroidActionTarget(psDroid, NULL, i);
}
}
else
{
setDroidActionTarget(psDroid, NULL, 0);
}
break;
case DACTION_TRANSPORTWAITTOFLYIN:
psDroid->action = DACTION_TRANSPORTWAITTOFLYIN;
break;
case DACTION_ATTACK:
// can't attack without a weapon
// or yourself
if ((psDroid->asWeaps[0].nStat == 0) ||
(psDroid->droidType == DROID_TRANSPORTER) ||
(psAction->psObj == (BASE_OBJECT *)psDroid))
{
return;
}
//check electronic droids only attack structures - not so anymore!
if (electronicDroid(psDroid))
{
//check for low or zero resistance - just zero resistance!
if (psAction->psObj->type == OBJ_STRUCTURE
&& !validStructResistance((STRUCTURE *)psAction->psObj))
{
//structure is low resistance already so don't attack
psDroid->action = DACTION_NONE;
break;
}
//in multiPlayer cannot electronically attack a tranporter
if (bMultiPlayer
&& psAction->psObj->type == OBJ_DROID
&& ((DROID *)psAction->psObj)->droidType == DROID_TRANSPORTER)
{
psDroid->action = DACTION_NONE;
break;
}
}
// note the droid's current pos so that scout & patrol orders know how far the
// droid has gone during an attack
// slightly strange place to store this I know, but I didn't want to add any more to the droid
psDroid->actionX = psDroid->pos.x;
psDroid->actionY = psDroid->pos.y;
setDroidActionTarget(psDroid, psAction->psObj, 0);
if ( ( (psDroid->order == DORDER_ATTACKTARGET || psDroid->order == DORDER_FIRESUPPORT) &&
secondaryGetState(psDroid, DSO_HALTTYPE, &state) && (state == DSS_HALT_HOLD)) ||
( !vtolDroid(psDroid) &&
orderStateObj(psDroid, DORDER_FIRESUPPORT, &psTarget) && (psTarget->type == OBJ_STRUCTURE) ) )
{
psDroid->action = DACTION_ATTACK; // holding, try attack straightaway
}
else if (actionInsideMinRange(psDroid, psAction->psObj, 0))
{
psWeapStats = &asWeaponStats[psDroid->asWeaps[0].nStat];
if ( !proj_Direct( psWeapStats ) )
{
if (psWeapStats->rotate)
{
psDroid->action = DACTION_ATTACK;
}
else
{
psDroid->action = DACTION_ROTATETOATTACK;
moveTurnDroid(psDroid, psDroid->psActionTarget[0]->pos.x, psDroid->psActionTarget[0]->pos.y);
}
}
else
{
/* direct fire - try and extend the range */
psDroid->action = DACTION_MOVETOATTACK;
actionCalcPullBackPoint((BASE_OBJECT *)psDroid, psAction->psObj, &pbx,&pby);
turnOffMultiMsg(TRUE);
moveDroidTo(psDroid, (UDWORD)pbx, (UDWORD)pby);
turnOffMultiMsg(FALSE);
}
}
else
{
psDroid->action = DACTION_MOVETOATTACK;
turnOffMultiMsg(TRUE);
moveDroidTo(psDroid, psAction->psObj->pos.x, psAction->psObj->pos.y);
turnOffMultiMsg(FALSE);
}
break;
case DACTION_MOVETOREARM:
psDroid->action = DACTION_MOVETOREARM;
psDroid->actionX = psAction->psObj->pos.x;
psDroid->actionY = psAction->psObj->pos.y;
psDroid->actionStarted = gameTime;
setDroidActionTarget(psDroid, psAction->psObj, 0);
droidX = psDroid->psActionTarget[0]->pos.x;
droidY = psDroid->psActionTarget[0]->pos.y;
if (!actionVTOLLandingPos(psDroid, &droidX, &droidY))
{
// totally bunged up - give up
orderDroid(psDroid, DORDER_RTB);
break;
}
moveDroidToDirect(psDroid, droidX, droidY);
break;
case DACTION_CLEARREARMPAD:
debug( LOG_NEVER, "Unit %d clearing rearm pad\n", psDroid->id);
psDroid->action = DACTION_CLEARREARMPAD;
setDroidActionTarget(psDroid, psAction->psObj, 0);
droidX = psDroid->psActionTarget[0]->pos.x;
droidY = psDroid->psActionTarget[0]->pos.y;
if (!actionVTOLLandingPos(psDroid, &droidX, &droidY))
{
// totally bunged up - give up
orderDroid(psDroid, DORDER_RTB);
break;
}
moveDroidToDirect(psDroid, droidX, droidY);
break;
case DACTION_MOVE:
case DACTION_TRANSPORTIN:
case DACTION_TRANSPORTOUT:
case DACTION_RETURNTOPOS:
case DACTION_FIRESUPPORT_RETREAT:
psDroid->action = psAction->action;
psDroid->actionX = psAction->x;
psDroid->actionY = psAction->y;
psDroid->actionStarted = gameTime;
setDroidActionTarget(psDroid, psAction->psObj, 0);
moveDroidTo(psDroid, psAction->x, psAction->y);
break;
case DACTION_BUILD:
ASSERT( psDroid->order == DORDER_BUILD || psDroid->order == DORDER_HELPBUILD ||
psDroid->order == DORDER_LINEBUILD,
"actionUnitBase: cannot start build action without a build order" );
psDroid->action = DACTION_MOVETOBUILD;
psDroid->actionX = psAction->x;
psDroid->actionY = psAction->y;
if (actionDroidOnBuildPos(psDroid,
(SDWORD)psDroid->orderX,(SDWORD)psDroid->orderY, psDroid->psTarStats))
{
actionHomeBasePos(psDroid->player, &pbx,&pby);
moveDroidToNoFormation(psDroid, (UDWORD)pbx,(UDWORD)pby);
}
else
{
moveDroidToNoFormation(psDroid, psDroid->actionX,psDroid->actionY);
}
break;
case DACTION_DEMOLISH:
ASSERT( psDroid->order == DORDER_DEMOLISH,
"actionUnitBase: cannot start demolish action without a demolish order" );
psDroid->action = DACTION_MOVETODEMOLISH;
psDroid->actionX = psAction->x;
psDroid->actionY = psAction->y;
ASSERT( (psDroid->psTarget != NULL) && (psDroid->psTarget->type == OBJ_STRUCTURE),
"actionUnitBase: invalid target for demolish order" );
psDroid->psTarStats = (BASE_STATS *)((STRUCTURE *)psDroid->psTarget)->pStructureType;
setDroidActionTarget(psDroid, psAction->psObj, 0);
moveDroidTo(psDroid, psAction->x, psAction->y);
break;
case DACTION_REPAIR:
//ASSERT( psDroid->order == DORDER_REPAIR,
// "actionDroidBase: cannot start repair action without a repair order" );
psDroid->action = DACTION_MOVETOREPAIR;
psDroid->actionX = psAction->x;
psDroid->actionY = psAction->y;
//this needs setting so that automatic repair works
setDroidActionTarget(psDroid, psAction->psObj, 0);
ASSERT( (psDroid->psActionTarget[0] != NULL) && (psDroid->psActionTarget[0]->type == OBJ_STRUCTURE),
"actionUnitBase: invalid target for demolish order" );
psDroid->psTarStats = (BASE_STATS *)((STRUCTURE *)psDroid->psActionTarget[0])->pStructureType;
moveDroidTo(psDroid, psAction->x, psAction->y);
break;
case DACTION_OBSERVE:
setDroidActionTarget(psDroid, psAction->psObj, 0);
psDroid->actionX = psDroid->pos.x;
psDroid->actionY = psDroid->pos.y;
if (//(secondaryGetState(psDroid, DSO_HALTTYPE, &state) && (state == DSS_HALT_HOLD)) ||
cbSensorDroid(psDroid))
{
psDroid->action = DACTION_OBSERVE;
}
else
{
psDroid->action = DACTION_MOVETOOBSERVE;
moveDroidTo(psDroid, psDroid->psActionTarget[0]->pos.x, psDroid->psActionTarget[0]->pos.y);
}
break;
case DACTION_FIRESUPPORT:
psDroid->action = DACTION_FIRESUPPORT;
if(!vtolDroid(psDroid) &&
!(secondaryGetState(psDroid, DSO_HALTTYPE, &state) && (state == DSS_HALT_HOLD)) && // check hold
(psDroid->psTarget->type != OBJ_STRUCTURE))
{
moveDroidTo(psDroid, psDroid->psTarget->pos.x, psDroid->psTarget->pos.y); // movetotarget.
}
break;
case DACTION_SULK:
// debug( LOG_NEVER, "Go with sulk ... %p\n", psDroid );
psDroid->action = DACTION_SULK;
// hmmm, hope this doesn't cause any problems!
psDroid->actionStarted = gameTime+MIN_SULK_TIME+(rand()%(
MAX_SULK_TIME-MIN_SULK_TIME));
break;
case DACTION_DESTRUCT:
psDroid->action = DACTION_DESTRUCT;
psDroid->actionStarted = gameTime;
break;
case DACTION_WAITFORREPAIR:
psDroid->action = DACTION_WAITFORREPAIR;
//set the time so we can tell whether the start the self repair or not
psDroid->actionStarted = gameTime;
break;
case DACTION_MOVETOREPAIRPOINT:
psDroid->action = psAction->action;
psDroid->actionX = psAction->x;
psDroid->actionY = psAction->y;
psDroid->actionStarted = gameTime;
setDroidActionTarget(psDroid, psAction->psObj, 0);
moveDroidToNoFormation( psDroid, psAction->x, psAction->y );
break;
case DACTION_WAITDURINGREPAIR:
psDroid->action = DACTION_WAITDURINGREPAIR;
break;
case DACTION_MOVETOREARMPOINT:
debug( LOG_NEVER, "Unit %d moving to rearm point\n", psDroid->id);
psDroid->action = psAction->action;
psDroid->actionX = psAction->x;
psDroid->actionY = psAction->y;
psDroid->actionStarted = gameTime;
setDroidActionTarget(psDroid, psAction->psObj, 0);
moveDroidToDirect( psDroid, psAction->x, psAction->y );
// make sure there arn't any other VTOLs on the rearm pad
ensureRearmPadClear((STRUCTURE *)psAction->psObj, psDroid);
break;
case DACTION_DROIDREPAIR:
// ASSERT( psDroid->order == DORDER_DROIDREPAIR,
// "actionDroidBase: cannot start droid repair action without a repair order" );
psDroid->action = DACTION_MOVETODROIDREPAIR;
psDroid->actionX = psAction->x;
psDroid->actionY = psAction->y;
setDroidActionTarget(psDroid, psAction->psObj, 0);
//initialise the action points
psDroid->actionPoints = 0;
moveDroidTo(psDroid, psAction->x, psAction->y);
break;
case DACTION_RESTORE:
ASSERT( psDroid->order == DORDER_RESTORE,
"actionUnitBase: cannot start restore action without a restore order" );
psDroid->action = DACTION_MOVETORESTORE;
psDroid->actionX = psAction->x;
psDroid->actionY = psAction->y;
ASSERT( (psDroid->psTarget != NULL) && (psDroid->psTarget->type == OBJ_STRUCTURE),
"actionUnitBase: invalid target for restore order" );
psDroid->psTarStats = (BASE_STATS *)((STRUCTURE *)psDroid->psTarget)->pStructureType;
setDroidActionTarget(psDroid, psAction->psObj, 0);
moveDroidTo(psDroid, psAction->x, psAction->y);
break;
case DACTION_CLEARWRECK:
ASSERT( psDroid->order == DORDER_CLEARWRECK,
"actionUnitBase: cannot start clear action without a clear order" );
psDroid->action = DACTION_MOVETOCLEAR;
psDroid->actionX = psAction->x;
psDroid->actionY = psAction->y;
ASSERT( (psDroid->psTarget != NULL) && (psDroid->psTarget->type == OBJ_FEATURE),
"actionUnitBase: invalid target for demolish order" );
psDroid->psTarStats = (BASE_STATS *)((FEATURE *)psDroid->psTarget)->psStats;
setDroidActionTarget(psDroid, psDroid->psTarget, 0);
moveDroidTo(psDroid, psAction->x, psAction->y);
break;
default:
ASSERT(!"unknown action", "actionUnitBase: unknown action");
break;
}
CHECK_DROID(psDroid);
}
/* Give a droid an action */
void actionDroid(DROID *psDroid, DROID_ACTION action)
{
DROID_ACTION_DATA sAction;
memset(&sAction, 0, sizeof(DROID_ACTION_DATA));
sAction.action = action;
actionDroidBase(psDroid, &sAction);
}
/* Give a droid an action with a location target */
void actionDroidLoc(DROID *psDroid, DROID_ACTION action, UDWORD x, UDWORD y)
{
DROID_ACTION_DATA sAction;
memset(&sAction, 0, sizeof(DROID_ACTION_DATA));
sAction.action = action;
sAction.x = x;
sAction.y = y;
actionDroidBase(psDroid, &sAction);
}
/* Give a droid an action with an object target */
void actionDroidObj(DROID *psDroid, DROID_ACTION action, BASE_OBJECT *psObj)
{
DROID_ACTION_DATA sAction;
memset(&sAction, 0, sizeof(DROID_ACTION_DATA));
sAction.action = action;
sAction.psObj = psObj;
sAction.x = psObj->pos.x;
sAction.y = psObj->pos.y;
actionDroidBase(psDroid, &sAction);
}
/* Give a droid an action with an object target and a location */
void actionDroidObjLoc(DROID *psDroid, DROID_ACTION action,
BASE_OBJECT *psObj, UDWORD x, UDWORD y)
{
DROID_ACTION_DATA sAction;
memset(&sAction, 0, sizeof(DROID_ACTION_DATA));
sAction.action = action;
sAction.psObj = psObj;
sAction.x = x;
sAction.y = y;
actionDroidBase(psDroid, &sAction);
}
/*send the vtol droid back to the nearest rearming pad - if one otherwise
return to base*/
// IF YOU CHANGE THE ORDER/ACTION RESULTS TELL ALEXL!!!! && recvvtolrearm
void moveToRearm(DROID *psDroid)
{
STRUCTURE *psStruct;
UBYTE chosen=0;
CHECK_DROID(psDroid);
if (!vtolDroid(psDroid))
{
return;
}
//if droid is already returning - ignore
if (vtolRearming(psDroid))
{
return;
}
//get the droid to fly back to a ReArming Pad
// don't worry about finding a clear one for the minute
psStruct = findNearestReArmPad(psDroid, psDroid->psBaseStruct, FALSE);
if (psStruct)
{
// note a base rearm pad if the vtol doesn't have one
if (psDroid->psBaseStruct == NULL)
{
setDroidBase(psDroid, psStruct);
}
//return to re-arming pad
if (psDroid->order == DORDER_NONE)
{
// no order set - use the rearm order to ensure the unit goes back
// to the landing pad
orderDroidObj(psDroid, DORDER_REARM, (BASE_OBJECT *)psStruct);
chosen=1;
}
else
{
actionDroidObj(psDroid, DACTION_MOVETOREARM, (BASE_OBJECT *)psStruct);
chosen=2;
}
}
else
{
//return to base un-armed
orderDroid( psDroid, DORDER_RTB );
chosen =3;
}
}
// whether a tile is suitable for a vtol to land on
static BOOL vtolLandingTile(SDWORD x, SDWORD y)
{
MAPTILE *psTile;
if (x < 0 || x >= (SDWORD)mapWidth ||
y < 0 || y >= (SDWORD)mapHeight)
{
return FALSE;
}
psTile = mapTile(x,y);
if ((psTile->tileInfoBits & BITS_FPATHBLOCK) ||
(TILE_OCCUPIED(psTile)) ||
(terrainType(psTile) == TER_CLIFFFACE) ||
(terrainType(psTile) == TER_WATER))
{
return FALSE;
}
return TRUE;
}
// choose a landing position for a VTOL when it goes to rearm
BOOL actionVTOLLandingPos(DROID *psDroid, UDWORD *px, UDWORD *py)
{
SDWORD i,j;
SDWORD startX,endX,startY,endY, tx,ty;
UDWORD passes;
DROID *psCurr;
BOOL result;
CHECK_DROID(psDroid);
/* Initial box dimensions and set iteration count to zero */
startX = endX = map_coord(*px);
startY = endY = map_coord(*py);
passes = 0;
// set blocking flags for all the other droids
for(psCurr=apsDroidLists[psDroid->player]; psCurr; psCurr = psCurr->psNext)
{
if (DROID_STOPPED(psCurr))
{
tx = map_coord(psCurr->pos.x);
ty = map_coord(psCurr->pos.y);
}
else
{
tx = map_coord(psCurr->sMove.DestinationX);
ty = map_coord(psCurr->sMove.DestinationY);
}
if (psCurr != psDroid)
{
if (tileOnMap(tx, ty))
{
mapTile(tx,ty)->tileInfoBits |= BITS_FPATHBLOCK;
}
}
}
/* Keep going until we get a tile or we exceed distance */
result = FALSE;
while(passes<20)
{
/* Process whole box */
for(i = startX; i <= endX; i++)
{
for(j = startY; j<= endY; j++)
{
/* Test only perimeter as internal tested previous iteration */
if(i==startX || i==endX || j==startY || j==endY)
{
/* Good enough? */
if(vtolLandingTile(i,j))
{
/* Set exit conditions and get out NOW */
debug( LOG_NEVER, "Unit %d landing pos (%d,%d)\n",psDroid->id, i,j);
*px = world_coord(i) + TILE_UNITS / 2;
*py = world_coord(j) + TILE_UNITS / 2;
result = TRUE;
goto exit;
}
}
}
}
/* Expand the box out in all directions - off map handled by tileAcceptable */
startX--; startY--; endX++; endY++; passes++;
}
exit:
// clear blocking flags for all the other droids
for(psCurr=apsDroidLists[psDroid->player]; psCurr; psCurr = psCurr->psNext)
{
if (DROID_STOPPED(psCurr))
{
tx = map_coord(psCurr->pos.x);
ty = map_coord(psCurr->pos.y);
}
else
{
tx = map_coord(psCurr->sMove.DestinationX);
ty = map_coord(psCurr->sMove.DestinationY);
}
if (tileOnMap(tx, ty))
{
mapTile(tx,ty)->tileInfoBits &= ~BITS_FPATHBLOCK;
}
}
return result;
}