Logo Search packages:      
Sourcecode: warzone2100 version File versions

action.c

Go to the documentation of this file.
/*
      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
*/
/**
 * @file 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. */
00060 #define  ACTION_DESTRUCT_TIME 2000

/** Droids heavier than this rotate and pitch more slowly. */
00063 #define HEAVY_WEAPON_WEIGHT     50000

#define ACTION_TURRET_ROTATION_RATE 45
#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. */
00070 #define KEEP_TRYING_REPAIR    10000

/** How far away the repair droid can be from the damaged droid to function. */
00073 #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)

const char* getDroidActionName(DROID_ACTION action)
{
      static const char* name[] =
      {
            "DACTION_NONE",                           // not doing anything
            "DACTION_MOVE",                           // 1 moving to a location
            "DACTION_BUILD",                          // building a structure
            "DACTION_BUILD_FOUNDATION",         // 3 building a foundation for a structure
            "DACTION_DEMOLISH",                       // demolishing a structure
            "DACTION_REPAIR",                         // 5 repairing a structure
            "DACTION_ATTACK",                         // attacking something
            "DACTION_OBSERVE",                        // 7 observing something
            "DACTION_FIRESUPPORT",              // attacking something visible by a sensor droid
            "DACTION_SULK",                           // 9 refuse to do anything aggresive for a fixed time
            "DACTION_DESTRUCT",                       // self destruct
            "DACTION_TRANSPORTOUT",             // 11 move transporter offworld
            "DACTION_TRANSPORTWAITTOFLYIN",     // wait for timer to move reinforcements in
            "DACTION_TRANSPORTIN",              // 13 move transporter onworld
            "DACTION_DROIDREPAIR",              // repairing a droid
            "DACTION_RESTORE",                        // 15 restore resistance points of a structure
            "DACTION_CLEARWRECK",                     // clearing building wreckage
            "DACTION_MOVEFIRE",                       // 17
            "DACTION_MOVETOBUILD",              // moving to a new building location
            "DACTION_MOVETODEMOLISH",                 // 19 moving to a new demolition location
            "DACTION_MOVETOREPAIR",             // moving to a new repair location
            "DACTION_BUILDWANDER",              // 21 moving around while building
            "DACTION_FOUNDATION_WANDER",        // moving around while building the foundation
            "DACTION_MOVETOATTACK",             // 23 moving to a target to attack
            "DACTION_ROTATETOATTACK",                 // rotating to a target to attack
            "DACTION_MOVETOOBSERVE",                  // 25 moving to be able to see a target
            "DACTION_WAITFORREPAIR",                  // waiting to be repaired by a facility
            "DACTION_MOVETOREPAIRPOINT",        // 27 move to repair facility repair point
            "DACTION_WAITDURINGREPAIR",         // waiting to be repaired by a facility
            "DACTION_MOVETODROIDREPAIR",        // 29 moving to a new location next to droid to be repaired
            "DACTION_MOVETORESTORE",                  // moving to a low resistance structure
            "DACTION_MOVETOCLEAR",              // 31 moving to a building wreck location
            "DACTION_MOVETOREARM",              // (32)moving to a rearming pad - VTOLS
            "DACTION_WAITFORREARM",             // (33)waiting for rearm - VTOLS
            "DACTION_MOVETOREARMPOINT",         // (34)move to rearm point - VTOLS - this actually moves them onto the pad
            "DACTION_WAITDURINGREARM",          // (35)waiting during rearm process- VTOLS
            "DACTION_VTOLATTACK",                     // (36) a VTOL droid doing attack runs
            "DACTION_CLEARREARMPAD",                  // (37) a VTOL droid being told to get off a rearm pad
            "DACTION_RETURNTOPOS",              // (38) used by scout/patrol order when returning to route
            "DACTION_FIRESUPPORT_RETREAT",      // (39) used by firesupport order when sensor retreats
            "ACTION UNKNOWN",
            "DACTION_CIRCLE"                    // (41) circling while engaging
      };

      ASSERT(action < sizeof(name) / sizeof(name[0]), "DROID_ACTION out of range: %u", action);

      return name[action];
}

/* 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;
      UWORD                   nearest = 0;
      UWORD                   tRot;
      UWORD                   tPitch;

      //Watermelon:default turret rotation 0
      tRot = 0;

      //get the maximum rotation this frame
      rotation = timeAdjustedIncrement(ACTION_TURRET_ROTATION_RATE, true);
      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;
      }
}

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

      // these are constants now and can be set up at the start of the function
      rotRate = ACTION_TURRET_ROTATION_RATE * 4;
      pitchRate = ACTION_TURRET_ROTATION_RATE * 2;

      if (psWeapStats)
      {
            // extra heavy weapons on some structures need to rotate and pitch more slowly
            if (psWeapStats->weight > HEAVY_WEAPON_WEIGHT)
            {
                  UDWORD excess = 100 * (psWeapStats->weight - HEAVY_WEAPON_WEIGHT) / psWeapStats->weight;

                  rotRate = ACTION_TURRET_ROTATION_RATE * 2 - excess;
                  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 = timeAdjustedIncrement(rotRate, true);
      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 = timeAdjustedIncrement(pitchRate, true);
      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) &&
            isVtolDroid((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);
      ASSERT(psTarget != NULL, "actionVisibleTarget: Target is NULL");
      if (!psTarget)
      {
            return false;
      }

      if (psDroid->numWeaps == 0)
      {
            if ( visibleObject((BASE_OBJECT*)psDroid, psTarget) )
            {
                  return true;
            }
      }

      if (isVtolDroid(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
// MAY return an invalid (0, 0) position on MP maps!
static 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 (isVtolDroid(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 (TileHasWall(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             *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" );

      /* 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 (isVtolDroid(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 (isVtolDroid(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, 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 (!isVtolDroid(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 (isVtolDroid(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))) )
                                    {
                                          BASE_OBJECT* psActionTarget;

                                          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 (isVtolDroid(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, 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)))
            {
                  for (i = 0;i < psDroid->numWeaps;i++)
                  {
                        setDroidActionTarget(psDroid, NULL, i);
                  }
                  psDroid->action = DACTION_NONE;
                  break;
            }

            bHasTarget = false;
            for(i = 0;i < psDroid->numWeaps;i++)
            {
                  BASE_OBJECT* psActionTarget;

                  //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))
                   || (!isVtolDroid(psDroid)
                    && (psTarget = orderStateObj(psDroid, DORDER_FIRESUPPORT))
                    && 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) )
                  {
                        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, psDroid);
                  psDroid->sMove.psFormation = NULL;
            }

            // send vtols back to rearm
            if (isVtolDroid(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 (!isVtolDroid(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, 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 (TileHasStructure(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
                         && (TileHasStructure(psTile)
                          || TileHasFeature(psTile)))
                        {
                              if (TileHasStructure(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);
                        if (pbx == 0 || pby == 0)
                        {
                              debug(LOG_NEVER, "DACTION_MOVETOBUILD: No HQ, cannot move in that direction.");
                              psDroid->action = DACTION_NONE;
                              break;
                        }
                        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, 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->orderX, psDroid->orderY);
            }
            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, 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);
                        if (pbx == 0 || pby == 0)
                        {
                              debug(LOG_NEVER, "No HQ - cannot move in that direction.");
                              psDroid->action = DACTION_NONE;
                              break;
                        }
                        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, 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) &&
                        (TileHasStructure(psTile) ||
                        TileHasFeature(psTile)))
                  {
                        if (TileHasStructure(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;
                  rangeSq = droidSensorRange(psDroid);
                  rangeSq = rangeSq * rangeSq;
                  if (!visibleObject((BASE_OBJECT *)psDroid, psDroid->psActionTarget[0]) ||
                        xdiff*xdiff + ydiff*ydiff >= rangeSq)
                  {
                        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;
                  rangeSq = droidSensorRange(psDroid);
                  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)
            {
                  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" );
                  //don't move VTOL's
                  // also don't move closer to sensor towers
                  if (!isVtolDroid(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;
                        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
                  {
                        debug(LOG_DEATH, "actionUpdateDroid: Droid %d destructed", (int)psDroid->id);
                        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
                  //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;

      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))
               || (!isVtolDroid(psDroid)
                && (psTarget = orderStateObj(psDroid, DORDER_FIRESUPPORT))
                && 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" );
            ASSERT(psAction->x > 0 && psAction->y > 0, "actionUnitBase: Bad build order position");
            psDroid->action = DACTION_MOVETOBUILD;
            psDroid->actionX = psAction->x;
            psDroid->actionY = psAction->y;
            if (actionDroidOnBuildPos(psDroid, psDroid->orderX, psDroid->orderY, psDroid->psTarStats))
            {
                  actionHomeBasePos(psDroid->player, &pbx,&pby);
                  if (pbx == 0 || pby == 0)
                  {
                        debug(LOG_NEVER, "DACTION_BUILD: No HQ, cannot move in that direction.");
                        psDroid->action = DACTION_NONE;
                        break;
                  }
                  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(!isVtolDroid(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 (!isVtolDroid(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) ||
            (TileIsOccupied(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;

}

Generated by  Doxygen 1.6.0   Back to index