warzone2100/src/order.c

4288 lines
116 KiB
C

/*
This file is part of Warzone 2100.
Copyright (C) 1999-2004 Eidos Interactive
Copyright (C) 2005-2007 Warzone Resurrection Project
Warzone 2100 is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Warzone 2100 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Warzone 2100; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* Order.c
*
* Functions for setting the orders of a droid or group of droids
*
*/
#include <string.h>
#include "lib/framework/frame.h"
#include "lib/framework/input.h"
#include "lib/framework/math-help.h"
#include "objects.h"
#include "order.h"
#include "action.h"
#include "map.h"
#include "formationdef.h"
#include "formation.h"
#include "geometry.h"
#include "projectile.h"
#include "effects.h" // for waypoint display
#include "lib/gamelib/gtime.h"
#include "intorder.h"
#include "orderdef.h"
#include "transporter.h"
#include "group.h"
#include "cmddroid.h"
#include "lib/script/script.h"
#include "scripttabs.h"
#include "scriptcb.h"
#include "multiplay.h" //ajl
#include "mission.h"
#include "hci.h"
#include "visibility.h"
#include "display.h"
#include "ai.h"
#include "warcam.h"
#include "lib/sound/audio_id.h"
#include "lib/sound/audio.h"
#include "fpath.h"
#include "display3d.h"
#include "combat.h"
// how long to run for
#define RUN_TIME 8000
#define RUN_BURN_TIME 10000
// how far to move away from something that is being defended
#define DEFEND_MAXDIST (TILE_UNITS * 3) //5)
#define DEFEND_BASEDIST (TILE_UNITS * 3)
#define DEFEND_CMD_MAXDIST (TILE_UNITS * 8)
#define DEFEND_CMD_BASEDIST (TILE_UNITS * 5)
// how big an area for a repair droid to cover
#define REPAIR_MAXDIST (TILE_UNITS * 5)
// how big an area for a constructor droid to cover
#define CONSTRUCT_MAXDIST (TILE_UNITS * 8)
// how close to the target a droid has to be to be for DORDER_SCOUT to end
#define SCOUT_DIST (TILE_UNITS * 8)
// how far a droid can wander when attacking during a DORDER_SCOUT
#define SCOUT_ATTACK_DIST (TILE_UNITS * 5)
// retreat positions for the players
RUN_DATA asRunData[MAX_PLAYERS];
// deal with a droid receiving a primary order
BOOL secondaryGotPrimaryOrder(DROID *psDroid, DROID_ORDER order);
// check all the orders in the list for died objects
void orderCheckList(DROID *psDroid);
// clear all the orders from the list
void orderClearDroidList(DROID *psDroid);
void orderDroidStatsTwoLocAdd(DROID *psDroid, DROID_ORDER order,
BASE_STATS *psStats, UDWORD x1, UDWORD y1, UDWORD x2, UDWORD y2);
//Watermelon:add a timestamp to order circle
static UDWORD orderStarted;
// whether an order effect has been displayed
static BOOL bOrderEffectDisplayed = false;
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
//call this *AFTER* every mission so it gets reset
void initRunData(void)
{
UBYTE i;
for (i = 0; i < MAX_PLAYERS; i++)
{
memset(&asRunData[i], 0, sizeof(RUN_DATA));
}
}
//FIXME: unit doesn't shoot while returning to the guard position
// check whether a droid has to move back to the thing it is guarding
static void orderCheckGuardPosition(DROID *psDroid, SDWORD range)
{
SDWORD xdiff, ydiff;
UDWORD x,y;
if (psDroid->psTarget != NULL)
{
// repair droids always follow behind - don't want them jumping into the line of fire
if ((!(psDroid->droidType == DROID_REPAIR || psDroid->droidType == DROID_CYBORG_REPAIR))
&& psDroid->psTarget->type == OBJ_DROID && orderStateLoc((DROID *)psDroid->psTarget, DORDER_MOVE, &x,&y))
{
// got a moving droid - check against where the unit is going
psDroid->orderX = (UWORD)x;
psDroid->orderY = (UWORD)y;
}
else
{
psDroid->orderX = psDroid->psTarget->pos.x;
psDroid->orderY = psDroid->psTarget->pos.y;
}
}
xdiff = (SDWORD)psDroid->pos.x - (SDWORD)psDroid->orderX;
ydiff = (SDWORD)psDroid->pos.y - (SDWORD)psDroid->orderY;
if (xdiff*xdiff + ydiff*ydiff > range*range)
{
if ((psDroid->sMove.Status != MOVEINACTIVE) &&
((psDroid->action == DACTION_MOVE) ||
(psDroid->action == DACTION_MOVEFIRE)))
{
xdiff = (SDWORD)psDroid->sMove.DestinationX - (SDWORD)psDroid->orderX;
ydiff = (SDWORD)psDroid->sMove.DestinationY - (SDWORD)psDroid->orderY;
if (xdiff*xdiff + ydiff*ydiff > range*range)
{
actionDroidLoc(psDroid, DACTION_MOVE, psDroid->orderX, psDroid->orderY);
}
}
else
{
actionDroidLoc(psDroid, DACTION_MOVE, psDroid->orderX, psDroid->orderY);
}
}
}
/*For a given repair droid, check if there are any damaged droids within
a defined range*/
BASE_OBJECT * checkForRepairRange(DROID *psDroid,DROID *psTarget)
{
DROID *psCurr;
SDWORD xdiff, ydiff;
ASSERT( psDroid->droidType == DROID_REPAIR || psDroid->droidType ==
DROID_CYBORG_REPAIR, "checkForRepairRange:Invalid droid type" );
if(psTarget != NULL
&& psTarget->died)
{
psTarget = NULL;
}
// if guarding a unit - always check that first
psCurr = (DROID*)orderStateObj(psDroid, DORDER_GUARD);
if (psCurr != NULL
&& psCurr->type == OBJ_DROID
&& droidIsDamaged(psCurr))
{
return (BASE_OBJECT *)psCurr;
}
if ((psTarget != NULL) &&
(psTarget->type == OBJ_DROID) &&
(psTarget->player == psDroid->player))
{
psCurr = psTarget->psNext;
}
else
{
psCurr = apsDroidLists[psDroid->player];
}
for (; psCurr != NULL; psCurr = psCurr->psNext)
{
//check for damage
if (droidIsDamaged(psCurr) &&
visibleObject((BASE_OBJECT *)psDroid, (BASE_OBJECT *)psCurr, false))
{
//check for within range
xdiff = (SDWORD)psDroid->pos.x - (SDWORD)psCurr->pos.x;
ydiff = (SDWORD)psDroid->pos.y - (SDWORD)psCurr->pos.y;
if ( (xdiff*xdiff) + (ydiff*ydiff) < REPAIR_MAXDIST*REPAIR_MAXDIST)
{
return (BASE_OBJECT *)psCurr;
}
}
}
return NULL;
}
/*For a given constructor droid, check if there are any damaged buildings within
a defined range*/
BASE_OBJECT * checkForDamagedStruct(DROID *psDroid, STRUCTURE *psTarget)
{
STRUCTURE *psCurr;
SDWORD xdiff, ydiff;
ASSERT(psDroid->droidType == DROID_CONSTRUCT || psDroid->droidType == DROID_CYBORG_CONSTRUCT, "Invalid unit type");
if(psTarget != NULL
&& psTarget->died)
{
psTarget = NULL;
}
if ((psTarget != NULL) &&
(psTarget->type == OBJ_STRUCTURE) &&
(psTarget->player == psDroid->player))
{
psCurr = psTarget->psNext;
}
else
{
psCurr = apsStructLists[psDroid->player];
}
for (; psCurr != NULL; psCurr = psCurr->psNext)
{
//check for damage
if (structIsDamaged(psCurr) &&
visibleObject((BASE_OBJECT *)psDroid, (BASE_OBJECT *)psCurr, false))
{
//check for within range
xdiff = (SDWORD)psDroid->pos.x - (SDWORD)psCurr->pos.x;
ydiff = (SDWORD)psDroid->pos.y - (SDWORD)psCurr->pos.y;
//check for repair distance and not construct_dist - this allows for structures being up to 3 tiles across
if ((xdiff*xdiff) + (ydiff*ydiff) < REPAIR_MAXDIST*REPAIR_MAXDIST)
{
return (BASE_OBJECT *)psCurr;
}
}
}
return NULL;
}
/* Update a droids order state */
void orderUpdateDroid(DROID *psDroid)
{
BASE_OBJECT *psObj;
STRUCTURE *psStruct, *psWall;
REPAIR_FACILITY *psRepairFac;
SDWORD xdiff,ydiff, temp;
SECONDARY_STATE state;
BOOL bAttack;
//Watermelon:int i
UBYTE i;
float radToAction;
SDWORD xoffset,yoffset;
// clear the target if it has died
if (psDroid->psTarget && psDroid->psTarget->died)
{
setDroidTarget(psDroid, NULL);
}
//clear its base struct if its died
if (psDroid->psBaseStruct && psDroid->psBaseStruct->died)
{
setDroidBase(psDroid, NULL);
}
// check for died objects in the list
orderCheckList(psDroid);
switch (psDroid->order)
{
case DORDER_NONE:
psObj = NULL;
// see if there are any orders queued up
if (orderDroidList(psDroid))
{
// started a new order, quit
break;
}
else if (!myResponsibility(psDroid->player))
{
return;
}
// if you are in a command group, default to guarding the commander
else if (hasCommander(psDroid) &&
(psDroid->psTarStats != (BASE_STATS *) structGetDemolishStat())) // stop the constructor auto repairing when it is about to demolish
{
orderDroidObj(psDroid, DORDER_GUARD, (BASE_OBJECT *)psDroid->psGroup->psCommander);
}
else if (psDroid->droidType == DROID_TRANSPORTER)
{
//check transporter isn't sitting there waiting to be filled when nothing exists!
if (psDroid->player == selectedPlayer && getDroidsToSafetyFlag()
&& !missionDroidsRemaining(selectedPlayer))
{
// check that nothing is on the transporter (transporter counts as first in group)
if (psDroid->psGroup && psDroid->psGroup->refCount < 2)
{
// the script can call startMission for this callback for offworld missions
eventFireCallbackTrigger((TRIGGER_TYPE)CALL_START_NEXT_LEVEL);
}
}
}
// default to guarding if the correct secondary order is set
else if ((psDroid->player == selectedPlayer) &&
(psDroid->psTarStats != (BASE_STATS *) structGetDemolishStat()) && // stop the constructor auto repairing when it is about to demolish
secondaryGetState(psDroid, DSO_HALTTYPE, &state) &&
!isVtolDroid(psDroid) &&
state == DSS_HALT_GUARD)
{
UDWORD actionX = psDroid->pos.x;
UDWORD actionY = psDroid->pos.y;
orderDroidLoc(psDroid, DORDER_GUARD, actionX,actionY);
}
//repair droids default to repairing droids within a given range
else if ((psDroid->droidType == DROID_REPAIR || psDroid->droidType == DROID_CYBORG_REPAIR) && !orderState(psDroid, DORDER_GUARD))
{
psObj = checkForRepairRange(psDroid,NULL);
if (psObj && (!bMultiPlayer || myResponsibility(psDroid->player)))
{
orderDroidObj(psDroid, DORDER_DROIDREPAIR, psObj);
}
}
//constructor droids default to repairing structures within a given range
else if ((psDroid->droidType == DROID_CONSTRUCT || psDroid->droidType == DROID_CYBORG_CONSTRUCT) &&
!orderState(psDroid, DORDER_GUARD) &&
(psDroid->psTarStats != (BASE_STATS *) structGetDemolishStat()))
{
psObj = checkForDamagedStruct(psDroid,NULL);
if (psObj && (!bMultiPlayer || myResponsibility(psDroid->player)))
{
orderDroidObj(psDroid, DORDER_REPAIR, psObj);
}
}
break;
case DORDER_TRANSPORTRETURN:
if (psDroid->action == DACTION_NONE)
{
missionMoveTransporterOffWorld( psDroid );
/* clear order */
psDroid->order = DORDER_NONE;
setDroidTarget(psDroid, NULL);
psDroid->psTarStats = NULL;
}
break;
case DORDER_TRANSPORTOUT:
if (psDroid->action == DACTION_NONE)
{
//if moving droids to safety and still got some droids left don't do callback
if (psDroid->player == selectedPlayer && getDroidsToSafetyFlag() &&
missionDroidsRemaining(selectedPlayer))
{
//move droids in Transporter into holding list
moveDroidsToSafety(psDroid);
//we need the transporter to just sit off world for a while...
orderDroid( psDroid, DORDER_TRANSPORTIN );
/* set action transporter waits for timer */
actionDroid( psDroid, DACTION_TRANSPORTWAITTOFLYIN );
missionSetReinforcementTime( gameTime );
//don't do this until waited for the required time
//fly Transporter back to get some more droids
//orderDroidLoc( psDroid, DORDER_TRANSPORTIN,
// getLandingX(selectedPlayer), getLandingY(selectedPlayer));
}
else
{
//the script can call startMission for this callback for offworld missions
eventFireCallbackTrigger((TRIGGER_TYPE)CALL_START_NEXT_LEVEL);
/* clear order */
psDroid->order = DORDER_NONE;
setDroidTarget(psDroid, NULL);
psDroid->psTarStats = NULL;
}
}
break;
case DORDER_TRANSPORTIN:
if ( (psDroid->action == DACTION_NONE) &&
(psDroid->sMove.Status == MOVEINACTIVE) )
{
/* clear order */
psDroid->order = DORDER_NONE;
setDroidTarget(psDroid, NULL);
psDroid->psTarStats = NULL;
//FFS! You only wan't to do this if the droid being tracked IS the transporter! Not all the time!
// What about if your happily playing the game and tracking a droid, and re-enforcements come in!
// And suddenly BLAM!!!! It drops you out of camera mode for no apparent reason! TOTALY CONFUSING
// THE PLAYER!
//
// Just had to get that off my chest....end of rant.....
//
if( psDroid == getTrackingDroid() ) { // Thats better...
/* deselect transporter if have been tracking */
if ( getWarCamStatus() )
{
camToggleStatus();
}
}
DeSelectDroid(psDroid);
/*don't try the unload if moving droids to safety and still got some
droids left - wait until full and then launch again*/
if (psDroid->player == selectedPlayer && getDroidsToSafetyFlag() &&
missionDroidsRemaining(selectedPlayer))
{
resetTransporter(psDroid);
}
else
{
unloadTransporter( psDroid, psDroid->pos.x, psDroid->pos.y, false );
}
}
break;
case DORDER_MOVE:
case DORDER_RETREAT:
case DORDER_DESTRUCT:
// Just wait for the action to finish then clear the order
if (psDroid->action == DACTION_NONE)
{
psDroid->order = DORDER_NONE;
setDroidTarget(psDroid, NULL);
psDroid->psTarStats = NULL;
}
break;
case DORDER_RECOVER:
if (psDroid->psTarget == NULL)
{
psDroid->order = DORDER_NONE;
}
else if (psDroid->action == DACTION_NONE)
{
// stoped moving, but still havn't got the artifact
actionDroidLoc(psDroid, DACTION_MOVE, psDroid->psTarget->pos.x,psDroid->psTarget->pos.y);
}
break;
case DORDER_MOVE_ATTACKWALL:
case DORDER_SCOUT_ATTACKWALL:
//Watermelon:check against all weapons now
for(i = 0;i <psDroid->numWeaps;i++)
{
if (psDroid->psTarget == NULL)
{
if (psDroid->order == DORDER_MOVE_ATTACKWALL)
{
psDroid->order = DORDER_MOVE;
}
else
{
psDroid->order = DORDER_SCOUT;
}
actionDroidLoc(psDroid, DACTION_MOVE, psDroid->orderX,psDroid->orderY);
}
else if ((((psDroid->action != DACTION_ATTACK) &&
(psDroid->action != DACTION_MOVETOATTACK) &&
(psDroid->action != DACTION_ROTATETOATTACK)) ||
(psDroid->psActionTarget[0] != psDroid->psTarget)) &&
actionInRange(psDroid, psDroid->psTarget, 0) )
{
actionDroidObj(psDroid, DACTION_ATTACK, psDroid->psTarget);
}
else if (psDroid->action == DACTION_NONE)
{
if (psDroid->order == DORDER_SCOUT_ATTACKWALL)
{
psDroid->order = DORDER_SCOUT;
}
actionDroidLoc(psDroid, DACTION_MOVE, psDroid->psTarget->pos.x, psDroid->psTarget->pos.y);
}
}
break;
case DORDER_SCOUT:
case DORDER_PATROL:
// if there is an enemy around, attack it
if ( (psDroid->action == DACTION_MOVE) && CAN_UPDATE_NAYBORS(psDroid) &&
(secondaryGetState(psDroid, DSO_ATTACK_LEVEL, &state) && (state == DSS_ALEV_ALWAYS)) &&
(aiBestNearestTarget(psDroid, &psObj, 0) >= 0) )
{
switch (psDroid->droidType)
{
case DROID_WEAPON:
case DROID_CYBORG:
case DROID_CYBORG_SUPER:
case DROID_PERSON:
case DROID_COMMAND:
actionDroidObj(psDroid, DACTION_ATTACK, psObj);
break;
case DROID_SENSOR:
actionDroidObj(psDroid, DACTION_OBSERVE, psObj);
break;
default:
actionDroid(psDroid, DACTION_NONE);
break;
}
}
else if (psDroid->action == DACTION_NONE)
{
xdiff = (SDWORD)psDroid->pos.x - (SDWORD)psDroid->orderX;
ydiff = (SDWORD)psDroid->pos.y - (SDWORD)psDroid->orderY;
if (xdiff*xdiff + ydiff*ydiff < SCOUT_DIST*SCOUT_DIST)
{
if (psDroid->order == DORDER_PATROL)
{
// head back to the other point
temp = psDroid->orderX;
psDroid->orderX = psDroid->orderX2;
psDroid->orderX2 = (UWORD)temp;
temp = psDroid->orderY;
psDroid->orderY = psDroid->orderY2;
psDroid->orderY2 = (UWORD)temp;
actionDroidLoc(psDroid, DACTION_MOVE, psDroid->orderX,psDroid->orderY);
}
else
{
psDroid->order = DORDER_NONE;
}
}
else
{
actionDroidLoc(psDroid, DACTION_MOVE, psDroid->orderX,psDroid->orderY);
}
}
else if ((psDroid->action == DACTION_ATTACK) ||
(psDroid->action == DACTION_MOVETOATTACK) ||
(psDroid->action == DACTION_ROTATETOATTACK) ||
(psDroid->action == DACTION_OBSERVE) ||
(psDroid->action == DACTION_MOVETOOBSERVE))
{
// attacking something - see if the droid has gone too far
xdiff = (SDWORD)psDroid->pos.x - (SDWORD)psDroid->actionX;
ydiff = (SDWORD)psDroid->pos.y - (SDWORD)psDroid->actionY;
if (xdiff*xdiff + ydiff*ydiff > SCOUT_ATTACK_DIST*SCOUT_ATTACK_DIST)
{
actionDroidLoc(psDroid, DACTION_RETURNTOPOS, psDroid->actionX,psDroid->actionY);
}
}
break;
case DORDER_CIRCLE:
// if there is an enemy around, attack it
if ( (psDroid->action == DACTION_MOVE) && CAN_UPDATE_NAYBORS(psDroid) &&
(secondaryGetState(psDroid, DSO_ATTACK_LEVEL, &state) && (state == DSS_ALEV_ALWAYS)) &&
(aiBestNearestTarget(psDroid, &psObj, 0) >= 0) )
{
switch (psDroid->droidType)
{
case DROID_WEAPON:
case DROID_CYBORG:
case DROID_CYBORG_SUPER:
case DROID_PERSON:
case DROID_COMMAND:
actionDroidObj(psDroid, DACTION_ATTACK, psObj);
break;
case DROID_SENSOR:
actionDroidObj(psDroid, DACTION_OBSERVE, psObj);
break;
default:
actionDroid(psDroid, DACTION_NONE);
break;
}
}
else if (psDroid->action == DACTION_NONE || psDroid->action == DACTION_MOVE)
{
if (psDroid->action == DACTION_MOVE)
{
if ( orderStarted && ((orderStarted + 500) > gameTime) )
{
break;
}
orderStarted = gameTime;
}
psDroid->action = DACTION_NONE;
xdiff = (SDWORD)psDroid->pos.x - (SDWORD)psDroid->orderX;
ydiff = (SDWORD)psDroid->pos.y - (SDWORD)psDroid->orderY;
if (xdiff*xdiff + ydiff*ydiff <= 2000 * 2000)
{
if (psDroid->order == DORDER_CIRCLE)
{
//Watermelon:use orderX,orderY as local space origin and calculate droid direction in local space
radToAction = atan2f((float)xdiff, (float)ydiff);
xoffset = sinf(radToAction) * 1500;
yoffset = cosf(radToAction) * 1500;
xdiff = (SDWORD)psDroid->pos.x - (SDWORD)(psDroid->orderX + xoffset);
ydiff = (SDWORD)psDroid->pos.y - (SDWORD)(psDroid->orderY + yoffset);
if (xdiff*xdiff + ydiff*ydiff < TILE_UNITS * TILE_UNITS)
{
//Watermelon:conter-clockwise 30 degree's per action
radToAction -= M_PI * 30 / 180;
xoffset = sinf(radToAction) * 1500;
yoffset = cosf(radToAction) * 1500;
actionDroidLoc(psDroid, DACTION_MOVE, (psDroid->orderX + xoffset),(psDroid->orderY + yoffset));
}
else
{
actionDroidLoc(psDroid, DACTION_MOVE, (psDroid->orderX + xoffset),(psDroid->orderY + yoffset));
}
}
else
{
psDroid->order = DORDER_NONE;
}
}
}
else if ((psDroid->action == DACTION_ATTACK) ||
(psDroid->action == DACTION_MOVETOATTACK) ||
(psDroid->action == DACTION_ROTATETOATTACK) ||
(psDroid->action == DACTION_OBSERVE) ||
(psDroid->action == DACTION_MOVETOOBSERVE))
{
// attacking something - see if the droid has gone too far
xdiff = (SDWORD)psDroid->pos.x - (SDWORD)psDroid->actionX;
ydiff = (SDWORD)psDroid->pos.y - (SDWORD)psDroid->actionY;
//if (xdiff*xdiff + ydiff*ydiff > psDroid->sMove.iGuardRadius * psDroid->sMove.iGuardRadius)
if (xdiff*xdiff + ydiff*ydiff > 2000 * 2000)
{
// head back to the target location
actionDroidLoc(psDroid, DACTION_RETURNTOPOS, psDroid->orderX,psDroid->orderY);
}
}
break;
case DORDER_HELPBUILD:
case DORDER_DEMOLISH:
case DORDER_OBSERVE:
case DORDER_REPAIR:
case DORDER_DROIDREPAIR:
case DORDER_RESTORE:
case DORDER_CLEARWRECK:
if (psDroid->action == DACTION_NONE ||
psDroid->psTarget == NULL)
{
psDroid->order = DORDER_NONE;
actionDroid(psDroid, DACTION_NONE);
if (psDroid->player == selectedPlayer)
{
intRefreshScreen();
}
}
/* if(psDroid->droidType == DROID_REPAIR
&& psDroid->action == DACTION_SULK)
{
psObj = checkForRepairRange(psDroid,(DROID *)psDroid->psTarget);
if (psObj)
{
orderDroidObj(psDroid, DORDER_DROIDREPAIR, psObj);
break;
}
}*/
break;
case DORDER_REARM:
if ((psDroid->psTarget == NULL) ||
(psDroid->psActionTarget[0] == NULL) )
{
// arm pad destroyed find another
psDroid->order = DORDER_NONE;
moveToRearm(psDroid);
}
else if (psDroid->action == DACTION_NONE)
{
psDroid->order = DORDER_NONE;
}
break;
case DORDER_ATTACK:
case DORDER_ATTACKTARGET:
if (psDroid->psTarget == NULL)
{
// if vtol then return to rearm pad as long as there are no other
// orders queued up
if (isVtolDroid(psDroid))
{
if (!orderDroidList(psDroid))
{
psDroid->order = DORDER_NONE;
moveToRearm(psDroid);
}
}
else
{
psDroid->order = DORDER_NONE;
actionDroid(psDroid, DACTION_NONE);
}
}
else if ( ((psDroid->action == DACTION_MOVE) ||
(psDroid->action == DACTION_MOVEFIRE)) &&
actionVisibleTarget(psDroid, psDroid->psTarget, 0) && !isVtolDroid(psDroid))
{
// moved near enough to attack change to attack action
actionDroidObj(psDroid, DACTION_ATTACK, psDroid->psTarget);
}
else if ( (psDroid->action == DACTION_MOVETOATTACK) &&
!isVtolDroid(psDroid) &&
!actionVisibleTarget(psDroid, psDroid->psTarget, 0) )
{
// lost sight of the target while chasing it - change to a move action so
// that the unit will fire on other things while moving
actionDroidLoc(psDroid, DACTION_MOVE, psDroid->psTarget->pos.x, psDroid->psTarget->pos.y);
}
else if (!isVtolDroid(psDroid)
&& psDroid->psTarget == psDroid->psActionTarget[0]
&& actionInRange(psDroid, psDroid->psTarget, 0)
&& (psWall = visGetBlockingWall((BASE_OBJECT *)psDroid, psDroid->psTarget))
&& psWall->player != psDroid->player)
{
// there is a wall in the way - attack that
actionDroidObj(psDroid, DACTION_ATTACK, (BASE_OBJECT *)psWall);
}
else if ((psDroid->action == DACTION_NONE) ||
(psDroid->action == DACTION_CLEARREARMPAD))
{
if ((psDroid->order == DORDER_ATTACKTARGET) &&
secondaryGetState(psDroid, DSO_HALTTYPE, &state) && (state == DSS_HALT_HOLD) &&
!actionInRange(psDroid, psDroid->psTarget, 0) )
{
// on hold orders give up
psDroid->order = DORDER_NONE;
setDroidTarget(psDroid, NULL);
}
else if (!isVtolDroid(psDroid) ||
allVtolsRearmed(psDroid))
{
actionDroidObj(psDroid, DACTION_ATTACK, psDroid->psTarget);
}
}
break;
case DORDER_BUILD:
if (psDroid->action == DACTION_BUILD &&
psDroid->psTarget == NULL)
{
psDroid->order = DORDER_NONE;
actionDroid(psDroid, DACTION_NONE);
}
else if (psDroid->action == DACTION_NONE)
{
psDroid->order = DORDER_NONE;
}
break;
case DORDER_EMBARK:
// only place it can be trapped - in multiPlayer can only put cyborgs onto a Transporter
if (bMultiPlayer && !cyborgDroid(psDroid))
{
psDroid->order = DORDER_NONE;
actionDroid(psDroid, DACTION_NONE);
}
else
{
// don't want the droids to go into a formation for this order
if (psDroid->sMove.psFormation != NULL)
{
formationLeave(psDroid->sMove.psFormation, psDroid);
psDroid->sMove.psFormation = NULL;
}
// Wait for the action to finish then assign to Transporter (if not already flying)
if (psDroid->psTarget == NULL || transporterFlying((DROID *)psDroid->psTarget))
{
psDroid->order = DORDER_NONE;
actionDroid(psDroid, DACTION_NONE);
}
else if (abs((SDWORD)psDroid->pos.x - (SDWORD)psDroid->psTarget->pos.x) < TILE_UNITS
&& abs((SDWORD)psDroid->pos.y - (SDWORD)psDroid->psTarget->pos.y) < TILE_UNITS)
{
// if in multiPlayer, only want to process if this player's droid
if (!bMultiPlayer || psDroid->player == selectedPlayer)
{
// save the target of current droid (the transporter)
DROID * transporter = (DROID *)psDroid->psTarget;
// order the droid to stop so moveUpdateDroid does not process this unit
orderDroid(psDroid, DORDER_STOP);
setDroidTarget(psDroid, NULL);
psDroid->psTarStats = NULL;
secondarySetState(psDroid, DSO_RETURN_TO_LOC, DSS_NONE);
// process orders *before* adding unit to transporter
// since we remove it from the list!
transporterAddDroid(transporter, psDroid);
}
}
else if (psDroid->action == DACTION_NONE)
{
actionDroidLoc(psDroid, DACTION_MOVE, psDroid->psTarget->pos.x,psDroid->psTarget->pos.y);
}
}
// Do we need to clear the secondary order "DSO_EMBARK" here? (PD)
break;
case DORDER_DISEMBARK:
//only valid in multiPlayer mode
if (bMultiPlayer)
{
//this order can only be given to Transporter droids
if (psDroid->droidType == DROID_TRANSPORTER)
{
/*once the Transporter has reached its destination (and landed),
get all the units to disembark*/
if (psDroid->action == DACTION_NONE && psDroid->sMove.Status ==
MOVEINACTIVE && psDroid->sMove.iVertSpeed == 0)
{
//only need to unload if this player's droid
if (psDroid->player == selectedPlayer)
{
unloadTransporter(psDroid, psDroid->pos.x, psDroid->pos.y, false);
}
//reset the transporter's order
psDroid->order = DORDER_NONE;
}
}
}
break;
case DORDER_RTB:
// Just wait for the action to finish then clear the order
if (psDroid->action == DACTION_NONE)
{
psDroid->order = DORDER_NONE;
secondarySetState(psDroid, DSO_RETURN_TO_LOC, DSS_NONE);
}
break;
case DORDER_LEAVEMAP:
if ((psDroid->pos.x < TILE_UNITS*2) ||
(psDroid->pos.x > (mapWidth-2)*TILE_UNITS) ||
(psDroid->pos.y < TILE_UNITS*2) ||
(psDroid->pos.y > (mapHeight-2)*TILE_UNITS) ||
(psDroid->action == DACTION_NONE))
{
psDroid->order = DORDER_NONE;
psScrCBVtolOffMap = psDroid;
eventFireCallbackTrigger((TRIGGER_TYPE)CALL_VTOL_OFF_MAP);
}
break;
case DORDER_RTR:
case DORDER_RTR_SPECIFIED:
if (psDroid->psTarget == NULL)
{
psDroid->order = DORDER_NONE;
actionDroid(psDroid, DACTION_NONE);
}
else if (psDroid->action == DACTION_NONE)
{
/* get repair facility pointer */
psStruct = (STRUCTURE *) psDroid->psTarget;
ASSERT( psStruct != NULL,
"orderUpdateUnit: invalid structure pointer" );
psRepairFac = (REPAIR_FACILITY *) psStruct->pFunctionality;
ASSERT( psRepairFac != NULL,
"orderUpdateUnit: invalid repair facility pointer" );
xdiff = (SDWORD)psDroid->pos.x - (SDWORD)psDroid->psTarget->pos.x;
ydiff = (SDWORD)psDroid->pos.y - (SDWORD)psDroid->psTarget->pos.y;
if (xdiff*xdiff + ydiff*ydiff < (TILE_UNITS*8)*(TILE_UNITS*8))
{
/* action droid to wait */
actionDroid(psDroid, DACTION_WAITFORREPAIR);
}
else
{
// move the droid closer to the repair facility
actionDroidLoc(psDroid, DACTION_MOVE, psDroid->psTarget->pos.x, psDroid->psTarget->pos.y);
}
}
else if (psDroid->order == DORDER_RTR &&
(psDroid->action == DACTION_MOVE ||
psDroid->action == DACTION_MOVEFIRE) &&
((psDroid->id % 50) == (frameGetFrameNumber() % 50)))
{
// see if there is a repair facility nearer than the one currently selected
orderDroid(psDroid, DORDER_RTR);
}
break;
case DORDER_RUNBURN:
if (psDroid->actionStarted + RUN_BURN_TIME < gameTime)
{
debug(LOG_DEATH, "orderUpdateDroid: Droid %d burned to death", psDroid->id); // why is this an order?
destroyDroid( psDroid );
}
break;
case DORDER_RUN:
if (psDroid->action == DACTION_NONE)
{
// got there so stop running
psDroid->order = DORDER_NONE;
}
if (psDroid->actionStarted + RUN_TIME < gameTime)
{
// been running long enough
actionDroid(psDroid, DACTION_NONE);
psDroid->order = DORDER_NONE;
}
break;
case DORDER_LINEBUILD:
if (psDroid->action == DACTION_NONE ||
(psDroid->action == DACTION_BUILD && psDroid->psTarget == NULL))
{
// finished building the current structure
if (map_coord(psDroid->orderX) == map_coord(psDroid->orderX2)
&& map_coord(psDroid->orderY) == map_coord(psDroid->orderY2))
{
// finished all the structures - done
psDroid->order = DORDER_NONE;
setDroidTarget(psDroid, NULL);
psDroid->psTarStats = NULL;
break;
}
// update the position for another structure
if (map_coord(psDroid->orderX) == map_coord(psDroid->orderX2))
{
// still got building to do - working vertically
if (psDroid->orderY < psDroid->orderY2)
{
psDroid->orderY += TILE_UNITS;
}
else
{
psDroid->orderY -= TILE_UNITS;
}
}
else if (map_coord(psDroid->orderY) == map_coord(psDroid->orderY2))
{
// still got building to do - working horizontally
if (psDroid->orderX < psDroid->orderX2)
{
psDroid->orderX += TILE_UNITS;
}
else
{
psDroid->orderX -= TILE_UNITS;
}
}
else
{
ASSERT( false, "orderUpdateUnit: LINEBUILD order on diagonal line" );
break;
}
// build another structure
setDroidTarget(psDroid, NULL);
actionDroidLoc(psDroid, DACTION_BUILD, psDroid->orderX,psDroid->orderY);
//intRefreshScreen();
}
break;
case DORDER_FIRESUPPORT:
if (psDroid->psTarget == NULL)
{
psDroid->order = DORDER_NONE;
if (isVtolDroid(psDroid))
{
moveToRearm(psDroid);
}
else
{
actionDroid(psDroid, DACTION_NONE);
}
}
//before targetting - check VTOL's are fully armed
else if (vtolEmpty(psDroid))
{
moveToRearm(psDroid);
}
//indirect weapon droid attached to (standard)sensor droid
else
{
BASE_OBJECT *psFireTarget = NULL;
if (psDroid->psTarget->type == OBJ_DROID)
{
DROID *psSpotter = (DROID *)psDroid->psTarget;
// psFireTarget = orderStateObj((DROID *)psDroid->psTarget, DORDER_OBSERVE);
if (psSpotter->action == DACTION_OBSERVE
|| (psSpotter->droidType == DROID_COMMAND && psSpotter->action == DACTION_ATTACK))
{
psFireTarget = psSpotter->psActionTarget[0];
}
}
else if (psDroid->psTarget->type == OBJ_STRUCTURE)
{
STRUCTURE *psSpotter = (STRUCTURE *)psDroid->psTarget;
psFireTarget = psSpotter->psTarget[0];
}
if (psFireTarget && !psFireTarget->died)
{
bAttack = false;
if (isVtolDroid(psDroid))
{
if (!vtolEmpty(psDroid) &&
((psDroid->action == DACTION_MOVETOREARM) ||
(psDroid->action == DACTION_WAITFORREARM)) &&
(psDroid->sMove.Status != MOVEINACTIVE) )
{
// catch vtols that were attacking another target which was destroyed
// get them to attack the new target rather than returning to rearm
bAttack = true;
}
else if (allVtolsRearmed(psDroid))
{
bAttack = true;
}
}
else
{
bAttack = true;
}
//if not currently attacking or target has changed
if ( bAttack &&
(!droidAttacking(psDroid) ||
psFireTarget != psDroid->psActionTarget[0]))
{
//get the droid to attack
actionDroidObj(psDroid, DACTION_ATTACK, psFireTarget);
}
}
else if (isVtolDroid(psDroid) &&
(psDroid->action != DACTION_NONE) &&
(psDroid->action != DACTION_FIRESUPPORT))
{
moveToRearm(psDroid);
}
else if ((psDroid->action != DACTION_FIRESUPPORT) &&
(psDroid->action != DACTION_FIRESUPPORT_RETREAT))
{
actionDroidObj(psDroid, DACTION_FIRESUPPORT, psDroid->psTarget);
}
}
break;
case DORDER_RECYCLE:
// don't bother with formations for this order
if (psDroid->sMove.psFormation)
{
formationLeave(psDroid->sMove.psFormation, psDroid);
psDroid->sMove.psFormation = NULL;
}
if (psDroid->psTarget == NULL)
{
psDroid->order = DORDER_NONE;
actionDroid(psDroid, DACTION_NONE);
}
else if (actionReachedBuildPos(psDroid, psDroid->psTarget->pos.x, psDroid->psTarget->pos.y,
(BASE_STATS *)((STRUCTURE *)psDroid->psTarget)->pStructureType))
{
recycleDroid(psDroid);
}
else if (psDroid->action == DACTION_NONE)
{
actionDroidLoc(psDroid, DACTION_MOVE, psDroid->psTarget->pos.x,psDroid->psTarget->pos.y);
}
break;
case DORDER_GUARD:
if (orderDroidList(psDroid))
{
// started a queued order - quit
break;
}
else if (!myResponsibility(psDroid->player))
{
return;
}
else if ((psDroid->action == DACTION_NONE) ||
(psDroid->action == DACTION_MOVE) ||
(psDroid->action == DACTION_MOVEFIRE))
{
// not doing anything, make sure the droid is close enough
// to the thing it is defending
//if ((psDroid->droidType != DROID_REPAIR) &&
if ((!(psDroid->droidType == DROID_REPAIR || psDroid->droidType ==
DROID_CYBORG_REPAIR)) && psDroid->psTarget != NULL &&
psDroid->psTarget->type == OBJ_DROID &&
((DROID *)psDroid->psTarget)->droidType == DROID_COMMAND)
{
// guarding a commander, allow more space
orderCheckGuardPosition(psDroid, DEFEND_CMD_BASEDIST);
}
else
{
orderCheckGuardPosition(psDroid, DEFEND_BASEDIST);
}
}
else if (psDroid->droidType == DROID_REPAIR ||
psDroid->droidType == DROID_CYBORG_REPAIR)
{
// repairing something, make sure the droid doesn't go too far
orderCheckGuardPosition(psDroid, REPAIR_MAXDIST);
}
else if (psDroid->droidType == DROID_CONSTRUCT ||
psDroid->droidType == DROID_CYBORG_CONSTRUCT)
{
// repairing something, make sure the droid doesn't go too far
orderCheckGuardPosition(psDroid, CONSTRUCT_MAXDIST);
}
else if (psDroid->droidType == DROID_TRANSPORTER)
{
//check transporter isn't sitting there waiting to be filled when nothing exists!
if (psDroid->player == selectedPlayer && getDroidsToSafetyFlag() &&
!missionDroidsRemaining(selectedPlayer))
{
//check that nothing is on the transporter (transporter counts as first in group)
if (psDroid->psGroup && psDroid->psGroup->refCount < 2)
{
//the script can call startMission for this callback for offworld missions
eventFireCallbackTrigger((TRIGGER_TYPE)CALL_START_NEXT_LEVEL);
}
}
}
else
{
//let vtols return to rearm
if (!vtolRearming(psDroid))
{
// attacking something, make sure the droid doesn't go too far
if (psDroid->psTarget != NULL && psDroid->psTarget->type == OBJ_DROID &&
((DROID *)psDroid->psTarget)->droidType == DROID_COMMAND)
{
// guarding a commander, allow more space
orderCheckGuardPosition(psDroid, DEFEND_CMD_MAXDIST);
}
else
{
orderCheckGuardPosition(psDroid, DEFEND_MAXDIST);
}
}
}
// get combat units in a command group to attack the commanders target
if ( hasCommander(psDroid) && (psDroid->numWeaps > 0) )
{
if ((psDroid->psGroup->psCommander->action == DACTION_ATTACK) &&
(psDroid->psGroup->psCommander->psActionTarget[0] != NULL) &&
(!psDroid->psGroup->psCommander->psActionTarget[0]->died))
{
psObj = psDroid->psGroup->psCommander->psActionTarget[0];
if (psDroid->action == DACTION_ATTACK ||
psDroid->action == DACTION_MOVETOATTACK)
{
if (psDroid->psActionTarget[0] != psObj)
{
actionDroidObj(psDroid, DACTION_ATTACK, psObj);
}
}
else if (psDroid->action != DACTION_MOVE)
{
actionDroidObj(psDroid, DACTION_ATTACK, psObj);
}
}
// make sure units in a command group are actually guarding the commander
psObj = orderStateObj(psDroid, DORDER_GUARD); // find out who is being guarded by the droid
if (psObj == NULL
|| psObj != (BASE_OBJECT *)psDroid->psGroup->psCommander)
{
orderDroidObj(psDroid, DORDER_GUARD, (BASE_OBJECT *)psDroid->psGroup->psCommander);
}
}
//repair droids default to repairing droids within a given range
psObj = NULL;
if ((psDroid->droidType == DROID_REPAIR ||
psDroid->droidType == DROID_CYBORG_REPAIR) &&
secondaryGetState(psDroid, DSO_HALTTYPE, &state) &&
(state != DSS_HALT_HOLD))
{
if (psDroid->action == DACTION_NONE)
{
psObj = checkForRepairRange(psDroid,NULL);
}
else if (psDroid->action == DACTION_SULK)
{
psObj = checkForRepairRange(psDroid,(DROID *)psDroid->psActionTarget[0]);
}
if (psObj)
{
actionDroidObj(psDroid, DACTION_DROIDREPAIR, (BASE_OBJECT *)psObj);
}
}
//construct droids default to repairing structures within a given range
psObj = NULL;
if ((psDroid->droidType == DROID_CONSTRUCT ||
psDroid->droidType == DROID_CYBORG_CONSTRUCT) &&
secondaryGetState(psDroid, DSO_HALTTYPE, &state) &&
(state != DSS_HALT_HOLD))
{
if (psDroid->action == DACTION_NONE)
{
psObj = checkForDamagedStruct(psDroid,NULL);
}
else if (psDroid->action == DACTION_SULK)
{
psObj = checkForDamagedStruct(psDroid,(STRUCTURE *)psDroid->psActionTarget);
}
if (psObj)
{
actionDroidObj(psDroid, DACTION_REPAIR, psObj);
}
}
break;
default:
ASSERT( false, "orderUpdateUnit: unknown order" );
}
// catch any vtol that is rearming but has finished his order
if (psDroid->order == DORDER_NONE && vtolRearming(psDroid)
&& (psDroid->psActionTarget[0] == NULL || !psDroid->psActionTarget[0]->died))
{
psDroid->order = DORDER_REARM;
setDroidTarget(psDroid, psDroid->psActionTarget[0]);
}
}
/* Give a command group an order */
static void orderCmdGroupBase(DROID_GROUP *psGroup, DROID_ORDER_DATA *psData)
{
DROID *psCurr, *psChosen;
SDWORD xdiff,ydiff, currdist, mindist;
ASSERT( psGroup != NULL,
"cmdUnitOrderGroupBase: invalid unit group" );
if (psData->order == DORDER_RECOVER)
{
// picking up an artifact - only need to send one unit
psChosen = NULL;
mindist = SDWORD_MAX;
for(psCurr = psGroup->psList; psCurr; psCurr=psCurr->psGrpNext)
{
xdiff = (SDWORD)psCurr->pos.x - (SDWORD)psData->psObj->pos.x;
ydiff = (SDWORD)psCurr->pos.y - (SDWORD)psData->psObj->pos.y;
currdist = xdiff*xdiff + ydiff*ydiff;
if (currdist < mindist)
{
psChosen = psCurr;
mindist = currdist;
}
}
if (psChosen != NULL)
{
orderDroidBase(psChosen, psData);
}
}
else
{
for (psCurr = psGroup->psList; psCurr; psCurr=psCurr->psGrpNext)
{
if (!orderState(psCurr, DORDER_RTR)) // if you change this, youll need to change sendcmdgroup()
{
orderDroidBase(psCurr, psData);
}
}
}
turnOffMultiMsg(false);
}
// check the position of units giving fire support to this unit and tell
// them to pull back if the sensor is going to move through them
WZ_DECL_UNUSED static void orderCheckFireSupportPos(DROID *psSensor, DROID_ORDER_DATA *psOrder)
{
SDWORD fsx,fsy, fsnum, sensorVX,sensorVY, fsVX,fsVY;
float sensorAngle, fsAngle, adiff;
SDWORD xdiff,ydiff;
SECONDARY_STATE state;
DROID *psCurr;
BASE_OBJECT *psTarget;
BOOL bRetreat;
// find the middle of and droids doing firesupport
fsx = fsy = fsnum = 0;
for(psCurr=apsDroidLists[psSensor->player]; psCurr; psCurr=psCurr->psNext)
{
if (!isVtolDroid(psCurr)
&& (psTarget = orderStateObj(psCurr, DORDER_FIRESUPPORT))
&& psTarget == (BASE_OBJECT *)psSensor
&& secondaryGetState(psCurr, DSO_HALTTYPE, &state)
&& state != DSS_HALT_HOLD)
{
// got a unit doing fire support
fsnum += 1;
fsx += (SDWORD)psCurr->pos.x;
fsy += (SDWORD)psCurr->pos.y;
}
}
bRetreat = false;
if (fsnum != 0)
{
// there are some units to check the position of
fsx /= fsnum;
fsy /= fsnum;
// don't do it if too near to the firesupport units
xdiff = fsx - (SDWORD)psSensor->pos.x;
ydiff = fsy - (SDWORD)psSensor->pos.y;
if (xdiff*xdiff + ydiff*ydiff < (TILE_UNITS*5)*(TILE_UNITS*5))
{
goto done;
}
sensorVX = (SDWORD)psOrder->x - (SDWORD)psSensor->pos.x;
sensorVY = (SDWORD)psOrder->y - (SDWORD)psSensor->pos.y;
fsVX = fsx - (SDWORD)psSensor->pos.x;
fsVY = fsy - (SDWORD)psSensor->pos.y;
// now check if the move position is further away than the firesupport units
if (sensorVX*sensorVX + sensorVY*sensorVY < fsVX*fsVX + fsVY*fsVY)
{
goto done;
}
// now get the angle between the firesupport units and the sensor move
sensorAngle = (float)atan2f(sensorVY, sensorVX);
fsAngle = (float)atan2f(fsVY, fsVX);
adiff = fsAngle - sensorAngle;
if (adiff < 0)
{
adiff += (float)(M_PI * 2);
}
if (adiff > M_PI)
{
adiff -= (float)(M_PI);
}
// if the angle between the firesupport units and the sensor move is bigger
// than 45 degrees don't retreat
if (adiff > M_PI / 4)
{
goto done;
}
bRetreat = true;
}
done:
// made a decision whether to retreat
// now move the firesupport units
for(psCurr=apsDroidLists[psSensor->player]; psCurr; psCurr=psCurr->psNext)
{
if (!isVtolDroid(psCurr)
&& (psTarget = orderStateObj(psCurr, DORDER_FIRESUPPORT))
&& psTarget == (BASE_OBJECT *)psSensor
&& secondaryGetState(psCurr, DSO_HALTTYPE, &state)
&& state != DSS_HALT_HOLD)
{
if (bRetreat)
{
actionDroidLoc(psCurr, DACTION_FIRESUPPORT_RETREAT, psOrder->x, psOrder->y);
}
else if (psCurr->action == DACTION_FIRESUPPORT_RETREAT)
{
actionDroid(psCurr, DACTION_NONE);
}
}
}
}
#define AUDIO_DELAY_FIRESUPPORT (3*GAME_TICKS_PER_SEC)
static void orderPlayFireSupportAudio( BASE_OBJECT *psObj )
{
DROID *psDroid = NULL;
STRUCTURE *psStruct = NULL;
SDWORD iAudioID = NO_SOUND;
/* play appropriate speech */
switch ( psObj->type )
{
case OBJ_DROID:
psDroid = (DROID *) psObj;
ASSERT( psObj != NULL,
"orderPlayFireSupportAudio: invalid droid pointer" );
if ( psDroid->droidType == DROID_COMMAND )
{
iAudioID = ID_SOUND_ASSIGNED_TO_COMMANDER;
}
else if ( psDroid->droidType == DROID_SENSOR )
{
iAudioID = ID_SOUND_ASSIGNED_TO_SENSOR;
}
break;
case OBJ_STRUCTURE:
ASSERT( psObj != NULL,
"orderPlayFireSupportAudio: invalid structure pointer" );
psStruct = (STRUCTURE *) psObj;
//check for non-CB first
if ( structStandardSensor(psStruct) || structVTOLSensor(psStruct) )
{
iAudioID = ID_SOUND_ASSIGNED_TO_SENSOR;
}
else if ( structCBSensor(psStruct) || structVTOLCBSensor(psStruct) )
{
iAudioID = ID_SOUND_ASSIGNED_TO_COUNTER_RADAR;
}
break;
default:
break;
}
if ( iAudioID != NO_SOUND )
{
audio_QueueTrackMinDelay( iAudioID, AUDIO_DELAY_FIRESUPPORT );
}
}
/* The base order function */
void orderDroidBase(DROID *psDroid, DROID_ORDER_DATA *psOrder)
{
UDWORD iRepairFacDistSq, iStructDistSq, iFactoryDistSq;
STRUCTURE *psStruct, *psRepairFac, *psFactory;
SDWORD iDX, iDY;
SECONDARY_STATE state;
UDWORD droidX,droidY;
// deal with a droid receiving a primary order
if (secondaryGotPrimaryOrder(psDroid, psOrder->order))
{
psOrder->order = DORDER_NONE;
}
// if this is a command droid - all it's units do the same thing
if ((psDroid->droidType == DROID_COMMAND) &&
(psDroid->psGroup != NULL) &&
(psDroid->psGroup->type == GT_COMMAND) &&
(psOrder->order != DORDER_GUARD) && //(psOrder->psObj == NULL)) &&
(psOrder->order != DORDER_RTR) &&
(psOrder->order != DORDER_RECYCLE) &&
(psOrder->order != DORDER_MOVE))
{
if (psOrder->order == DORDER_ATTACK)
{
// change to attacktarget so that the group members
// guard order does not get canceled
psOrder->order = DORDER_ATTACKTARGET;
orderCmdGroupBase(psDroid->psGroup, psOrder);
psOrder->order = DORDER_ATTACK;
}
else
{
orderCmdGroupBase(psDroid->psGroup, psOrder);
}
// the commander doesn't have to pick up artifacts, one
// of his units will do it for him (if there are any in his group).
if ((psOrder->order == DORDER_RECOVER) &&
(psDroid->psGroup->psList != NULL))
{
psOrder->order = DORDER_NONE;
}
}
switch (psOrder->order)
{
case DORDER_NONE:
// used when choose order cannot assign an order
break;
case DORDER_STOP:
// get the droid to stop doing whatever it is doing
actionDroid(psDroid, DACTION_NONE);
psDroid->order = DORDER_NONE;
setDroidTarget(psDroid, NULL);
psDroid->psTarStats = NULL;
psDroid->orderX = 0;
psDroid->orderY = 0;
psDroid->orderX2 = 0;
psDroid->orderY2 = 0;
break;
case DORDER_MOVE:
case DORDER_SCOUT:
// can't move vtols to blocking tiles
if (isVtolDroid(psDroid)
&& fpathBlockingTile(map_coord(psOrder->x), map_coord(psOrder->y), getPropulsionStats(psDroid)->propulsionType))
{
break;
}
//in multiPlayer, cannot move Transporter to blocking tile either
if (game.maxPlayers > 0
&& psDroid->droidType == DROID_TRANSPORTER
&& fpathBlockingTile(map_coord(psOrder->x), map_coord(psOrder->y), getPropulsionStats(psDroid)->propulsionType))
{
break;
}
// move a droid to a location
psDroid->order = psOrder->order;
psDroid->orderX = psOrder->x;
psDroid->orderY = psOrder->y;
actionDroidLoc(psDroid, DACTION_MOVE, psOrder->x,psOrder->y);
break;
case DORDER_PATROL:
psDroid->order = psOrder->order;
psDroid->orderX = psOrder->x;
psDroid->orderY = psOrder->y;
psDroid->orderX2 = psDroid->pos.x;
psDroid->orderY2 = psDroid->pos.y;
actionDroidLoc(psDroid, DACTION_MOVE, psOrder->x,psOrder->y);
break;
case DORDER_RECOVER:
psDroid->order = DORDER_RECOVER;
setDroidTarget(psDroid, psOrder->psObj);
actionDroidLoc(psDroid, DACTION_MOVE, psOrder->psObj->pos.x,psOrder->psObj->pos.y);
break;
case DORDER_TRANSPORTOUT:
// tell a (transporter) droid to leave home base for the offworld mission
psDroid->order = DORDER_TRANSPORTOUT;
psDroid->orderX = psOrder->x;
psDroid->orderY = psOrder->y;
actionDroidLoc(psDroid, DACTION_TRANSPORTOUT, psOrder->x,psOrder->y);
break;
case DORDER_TRANSPORTRETURN:
// tell a (transporter) droid to return after unloading
psDroid->order = DORDER_TRANSPORTRETURN;
psDroid->orderX = psOrder->x;
psDroid->orderY = psOrder->y;
actionDroidLoc(psDroid, DACTION_TRANSPORTOUT, psOrder->x,psOrder->y);
break;
case DORDER_TRANSPORTIN:
// tell a (transporter) droid to fly onworld
psDroid->order = DORDER_TRANSPORTIN;
psDroid->orderX = psOrder->x;
psDroid->orderY = psOrder->y;
actionDroidLoc(psDroid, DACTION_TRANSPORTIN, psOrder->x,psOrder->y);
break;
case DORDER_ATTACK:
case DORDER_ATTACKTARGET:
if (psDroid->numWeaps == 0
|| psDroid->asWeaps[0].nStat == 0
|| psDroid->droidType == DROID_TRANSPORTER)
{
break;
}
else if (psDroid->order == DORDER_GUARD && psOrder->order == DORDER_ATTACKTARGET)
{
// attacking something while guarding, don't change the order
actionDroidObj(psDroid, DACTION_ATTACK, (BASE_OBJECT *)psOrder->psObj);
}
else if (!psOrder->psObj->died)
{
//cannot attack a Transporter with EW in multiPlayer
if (game.maxPlayers > 0 && electronicDroid(psDroid)
&& psOrder->psObj->type == OBJ_DROID && ((DROID *)psOrder->psObj)->droidType == DROID_TRANSPORTER)
{
break;
}
setDroidTarget(psDroid, psOrder->psObj);
psDroid->order = psOrder->order;
if (isVtolDroid(psDroid)
|| actionInsideMinRange(psDroid, psOrder->psObj, 0)
|| (psOrder->order == DORDER_ATTACKTARGET
&& secondaryGetState(psDroid, DSO_HALTTYPE, &state) && state == DSS_HALT_HOLD))
{
actionDroidObj(psDroid, DACTION_ATTACK, psOrder->psObj);
}
else
{
actionDroidLoc(psDroid, DACTION_MOVE, psOrder->psObj->pos.x, psOrder->psObj->pos.y);
}
}
break;
case DORDER_BUILD:
// build a new structure
if (!(psDroid->droidType == DROID_CONSTRUCT || psDroid->droidType == DROID_CYBORG_CONSTRUCT))
{
break;
}
ASSERT( psOrder->psStats != NULL,
"orderUnitBase: invalid structure stats pointer" );
psDroid->order = DORDER_BUILD;
psDroid->orderX = psOrder->x;
psDroid->orderY = psOrder->y;
setDroidTarget(psDroid, NULL);
psDroid->psTarStats = psOrder->psStats;
actionDroidLoc(psDroid, DACTION_BUILD, psOrder->x,psOrder->y);
break;
case DORDER_BUILDMODULE:
//build a module onto the structure
if (!(psDroid->droidType == DROID_CONSTRUCT || psDroid->droidType == DROID_CYBORG_CONSTRUCT))
{
break;
}
psDroid->order = DORDER_BUILD;
psDroid->orderX = psOrder->psObj->pos.x;
psDroid->orderY = psOrder->psObj->pos.y;
setDroidTarget(psDroid, NULL);
psDroid->psTarStats = (BASE_STATS *)getModuleStat((STRUCTURE *)psOrder->psObj);
ASSERT(psDroid->psTarStats != NULL, "orderUnitBase: should have found a module stats");
actionDroidLoc(psDroid, DACTION_BUILD, psOrder->psObj->pos.x,psOrder->psObj->pos.y);
break;
case DORDER_LINEBUILD:
// build a line of structures
if (!(psDroid->droidType == DROID_CONSTRUCT || psDroid->droidType == DROID_CYBORG_CONSTRUCT))
{
break;
}
ASSERT(psOrder->psStats != NULL, "Invalid structure stats pointer");
psDroid->order = DORDER_LINEBUILD;
psDroid->orderX = psOrder->x;
psDroid->orderY = psOrder->y;
psDroid->orderX2 = psOrder->x2;
psDroid->orderY2 = psOrder->y2;
setDroidTarget(psDroid, NULL);
psDroid->psTarStats = psOrder->psStats;
actionDroidLoc(psDroid, DACTION_BUILD, psOrder->x,psOrder->y);
break;
case DORDER_HELPBUILD:
// help to build a structure that is starting to be built
if (!(psDroid->droidType == DROID_CONSTRUCT || psDroid->droidType == DROID_CYBORG_CONSTRUCT))
{
break;
}
psDroid->order = DORDER_HELPBUILD;
psDroid->orderX = psOrder->psObj->pos.x;
psDroid->orderY = psOrder->psObj->pos.y;
setDroidTarget(psDroid, psOrder->psObj);
psDroid->psTarStats = (BASE_STATS *)((STRUCTURE *)psOrder->psObj)->pStructureType;
actionDroidLoc(psDroid, DACTION_BUILD, psDroid->orderX, psDroid->orderY);
break;
case DORDER_DEMOLISH:
if (!(psDroid->droidType == DROID_CONSTRUCT || psDroid->droidType == DROID_CYBORG_CONSTRUCT))
{
break;
}
psDroid->order = DORDER_DEMOLISH;
psDroid->orderX = psOrder->psObj->pos.x;
psDroid->orderY = psOrder->psObj->pos.y;
setDroidTarget(psDroid, psOrder->psObj);
actionDroidObj(psDroid, DACTION_DEMOLISH, (BASE_OBJECT *)psOrder->psObj);
break;
case DORDER_REPAIR:
if (!(psDroid->droidType == DROID_CONSTRUCT || psDroid->droidType == DROID_CYBORG_CONSTRUCT))
{
break;
}
psDroid->order = DORDER_REPAIR;
psDroid->orderX = psOrder->psObj->pos.x;
psDroid->orderY = psOrder->psObj->pos.y;
setDroidTarget(psDroid, psOrder->psObj);
actionDroidObj(psDroid, DACTION_REPAIR, (BASE_OBJECT *)psOrder->psObj);
break;
case DORDER_DROIDREPAIR:
if (!(psDroid->droidType == DROID_REPAIR || psDroid->droidType == DROID_CYBORG_REPAIR))
{
break;
}
psDroid->order = DORDER_DROIDREPAIR;
setDroidTarget(psDroid, psOrder->psObj);
actionDroidObj(psDroid, DACTION_DROIDREPAIR, (BASE_OBJECT *)psOrder->psObj);
break;
case DORDER_OBSERVE:
// keep an object within sensor view
psDroid->order = DORDER_OBSERVE;
setDroidTarget(psDroid, psOrder->psObj);
actionDroidObj(psDroid, DACTION_OBSERVE, (BASE_OBJECT *)psOrder->psObj);
break;
case DORDER_FIRESUPPORT:
if (psDroid->asWeaps[0].nStat == 0)
{
break;
}
psDroid->order = DORDER_FIRESUPPORT;
setDroidTarget(psDroid, psOrder->psObj);
// let the order update deal with vtol droids
if (!isVtolDroid(psDroid))
{
actionDroidObj(psDroid, DACTION_FIRESUPPORT, (BASE_OBJECT *)psOrder->psObj);
}
if ( psDroid->player == selectedPlayer )
{
orderPlayFireSupportAudio( psOrder->psObj );
}
break;
case DORDER_RETREAT:
case DORDER_RUNBURN:
case DORDER_RUN:
psDroid->order = psOrder->order;
if ((psOrder->order == DORDER_RUN) &&
((psOrder->x != 0) || (psOrder->y != 0)))
{
psDroid->orderX = psOrder->x;
psDroid->orderY = psOrder->y;
}
else
{
psDroid->orderX = (UWORD)asRunData[psDroid->player].sPos.x;
psDroid->orderY = (UWORD)asRunData[psDroid->player].sPos.y;
}
actionDroidLoc(psDroid, DACTION_MOVE, psDroid->orderX,psDroid->orderY);
break;
case DORDER_DESTRUCT:
psDroid->order = DORDER_DESTRUCT;
actionDroid(psDroid, DACTION_DESTRUCT);
break;
case DORDER_RTB:
// send vtols back to their return pos
if (isVtolDroid(psDroid) && !bMultiPlayer && psDroid->player != selectedPlayer)
{
iDX = asVTOLReturnPos[psDroid->player].x;
iDY = asVTOLReturnPos[psDroid->player].y;
if (iDX && iDY)
{
psDroid->order = DORDER_LEAVEMAP;
actionDroidLoc(psDroid, DACTION_MOVE, iDX, iDY);
if (psDroid->sMove.psFormation)
{
formationLeave(psDroid->sMove.psFormation, psDroid);
psDroid->sMove.psFormation = NULL;
}
break;
}
}
for(psStruct=apsStructLists[psDroid->player]; psStruct; psStruct = psStruct->psNext)
{
if (psStruct->pStructureType->type == REF_HQ)
{
psDroid->order = DORDER_RTB;
droidX = psStruct->pos.x;
droidY = psStruct->pos.y;
// Find a place to land for vtols. And Transporters in a multiPlay game.
if (isVtolDroid(psDroid) || ((game.maxPlayers > 0) && (psDroid->droidType == DROID_TRANSPORTER)))
{
actionVTOLLandingPos(psDroid, &droidX,&droidY);
}
actionDroidLoc(psDroid, DACTION_MOVE, droidX,droidY);
break;
}
}
// no HQ go to the landing zone
if (psDroid->order != DORDER_RTB)
{
// see if the LZ has been set up
iDX = getLandingX(psDroid->player);
iDY = getLandingY(psDroid->player);
if (iDX && iDY)
{
psDroid->order = DORDER_RTB;
//actionDroidLoc(psDroid, DACTION_MOVE, getLandingX(psDroid->player),
// getLandingY(psDroid->player));
actionDroidLoc(psDroid, DACTION_MOVE, iDX,iDY);
}
else
{
// haven't got an LZ set up so don't do anything
psDroid->order = DORDER_NONE;
}
}
break;
case DORDER_RTR:
case DORDER_RTR_SPECIFIED:
if (isVtolDroid(psDroid))
{
moveToRearm(psDroid);
break;
}
if (psOrder->psObj == NULL)
{
psRepairFac = NULL;
iRepairFacDistSq = 0;
for(psStruct=apsStructLists[psDroid->player]; psStruct; psStruct = psStruct->psNext)
{
if ((psStruct->pStructureType->type == REF_REPAIR_FACILITY) ||
((psStruct->pStructureType->type == REF_HQ) && (psRepairFac == NULL)))
{
/* get droid->facility distance squared */
iDX = (SDWORD)psDroid->pos.x - (SDWORD)psStruct->pos.x;
iDY = (SDWORD)psDroid->pos.y - (SDWORD)psStruct->pos.y;
iStructDistSq = iDX*iDX + iDY*iDY;
/* choose current structure if first repair facility found or
* nearer than previously chosen facility
*/
if ( psRepairFac == NULL || (psRepairFac->pStructureType->type == REF_HQ) ||
(iRepairFacDistSq > iStructDistSq) )
{
/* first facility found */
psRepairFac = psStruct;
iRepairFacDistSq = iStructDistSq;
}
}
}
}
else
{
psRepairFac = (STRUCTURE *)psOrder->psObj;
}
// droids doing a DORDER_RTR periodically give themselves a DORDER_RTR so that
// they always go to the nearest repair facility
// this stops the unit doing anything more if the same repair facility gets chosen
if (psDroid->order == DORDER_RTR &&
psDroid->psTarget == (BASE_OBJECT *)psRepairFac)
{
break;
}
/* give repair order if repair facility found */
if ( psRepairFac != NULL )
{
if (psRepairFac->pStructureType->type == REF_REPAIR_FACILITY)
{
/* move to front of structure */
psDroid->order = psOrder->order;
psDroid->orderX = psRepairFac->pos.x;
psDroid->orderY = psRepairFac->pos.y;
setDroidTarget(psDroid, (BASE_OBJECT *) psRepairFac);
/*if in multiPlayer, and the Transporter has been sent to be
repaired, need to find a suitable location to drop down*/
if (game.maxPlayers > 0 && psDroid->droidType == DROID_TRANSPORTER)
{
UDWORD droidX, droidY;
droidX = psDroid->orderX;
droidY = psDroid->orderY;
actionVTOLLandingPos(psDroid, &droidX,&droidY);
actionDroidLoc(psDroid, DACTION_MOVE, droidX,droidY);
}
else
{
actionDroidObjLoc( psDroid, DACTION_MOVE, (BASE_OBJECT *) psRepairFac,
psDroid->orderX, psDroid->orderY);
}
}
else
{
orderDroid(psDroid, DORDER_RTB);
}
}
else
{
// no repair facility or HQ go to the landing zone
if (!bMultiPlayer && selectedPlayer == 0)
{
orderDroid(psDroid, DORDER_RTB);
}
}
break;
case DORDER_EMBARK:
// move the droid to the transporter location
psDroid->order = DORDER_EMBARK;
psDroid->orderX = psOrder->psObj->pos.x;
psDroid->orderY = psOrder->psObj->pos.y;
setDroidTarget(psDroid, psOrder->psObj);
actionDroidLoc(psDroid, DACTION_MOVE, psOrder->psObj->pos.x, psOrder->psObj->pos.y);
break;
case DORDER_DISEMBARK:
//only valid in multiPlayer mode - cannot use the check on bMultiPlayer since it has been
//set to false before this function call
if (game.maxPlayers > 0)
{
//this order can only be given to Transporter droids
if (psDroid->droidType == DROID_TRANSPORTER)
{
psDroid->order = DORDER_DISEMBARK;
psDroid->orderX = psOrder->x;
psDroid->orderY = psOrder->y;
//move the Transporter to the requested location
actionDroidLoc(psDroid, DACTION_MOVE, psOrder->x,psOrder->y);
//close the Transporter interface - if up
if (widgGetFromID(psWScreen,IDTRANS_FORM) != NULL)
{
intRemoveTrans();
}
}
}
break;
case DORDER_RECYCLE:
psFactory = NULL;
iFactoryDistSq = 0;
for(psStruct=apsStructLists[psDroid->player]; psStruct; psStruct = psStruct->psNext)
{
//look for nearest factory or repair facility
if (psStruct->pStructureType->type == REF_FACTORY ||
psStruct->pStructureType->type == REF_CYBORG_FACTORY ||
psStruct->pStructureType->type == REF_VTOL_FACTORY ||
psStruct->pStructureType->type == REF_REPAIR_FACILITY)
{
/* get droid->facility distance squared */
iDX = (SDWORD)psDroid->pos.x - (SDWORD)psStruct->pos.x;
iDY = (SDWORD)psDroid->pos.y - (SDWORD)psStruct->pos.y;
iStructDistSq = iDX*iDX + iDY*iDY;
/* choose current structure if first facility found or
* nearer than previously chosen facility
*/
if ( psFactory == NULL || (iFactoryDistSq > iStructDistSq) )
{
/* first facility found */
psFactory = psStruct;
iFactoryDistSq = iStructDistSq;
}
}
}
/* give recycle order if facility found */
if ( psFactory != NULL )
{
/* move to front of structure */
psDroid->order = DORDER_RECYCLE;
psDroid->orderX = psFactory->pos.x;
psDroid->orderY = (UWORD)(psFactory->pos.y +
world_coord(psFactory->pStructureType->baseBreadth) / 2 +
TILE_UNITS / 2);
setDroidTarget(psDroid, (BASE_OBJECT *) psFactory);
actionDroidObjLoc( psDroid, DACTION_MOVE, (BASE_OBJECT *) psFactory,
psDroid->orderX, psDroid->orderY);
}
break;
case DORDER_GUARD:
psDroid->order = DORDER_GUARD;
setDroidTarget(psDroid, psOrder->psObj);
if (psOrder->psObj != NULL)
{
psDroid->orderX = psOrder->psObj->pos.x;
psDroid->orderY = psOrder->psObj->pos.y;
}
else
{
psDroid->orderX = psOrder->x;
psDroid->orderY = psOrder->y;
}
actionDroid( psDroid, DACTION_NONE );
break;
case DORDER_RESTORE:
if (!electronicDroid(psDroid))
{
break;
}
if (psOrder->psObj->type != OBJ_STRUCTURE)
{
ASSERT( false, "orderDroidBase: invalid object type for Restore order" );
break;
}
psDroid->order = DORDER_RESTORE;
psDroid->orderX = psOrder->psObj->pos.x;
psDroid->orderY = psOrder->psObj->pos.y;
setDroidTarget(psDroid, psOrder->psObj);
actionDroidObj(psDroid, DACTION_RESTORE, (BASE_OBJECT *)psOrder->psObj);
break;
case DORDER_CLEARWRECK:
if (!(psDroid->droidType == DROID_CONSTRUCT || psDroid->droidType == DROID_CYBORG_CONSTRUCT))
{
break;
}
psDroid->order = DORDER_CLEARWRECK;
psDroid->orderX = psOrder->psObj->pos.x;
psDroid->orderY = psOrder->psObj->pos.y;
setDroidTarget(psDroid, psOrder->psObj);
actionDroidObj(psDroid, DACTION_CLEARWRECK, (BASE_OBJECT *)psOrder->psObj);
break;
case DORDER_REARM:
// didn't get executed before
if (!isVtolDroid(psDroid))
{
break;
}
psDroid->order = DORDER_REARM;
setDroidTarget(psDroid, psOrder->psObj);
actionDroidObj(psDroid,DACTION_MOVETOREARM, (BASE_OBJECT *)psOrder->psObj);
assignVTOLPad(psDroid, (STRUCTURE *)psOrder->psObj);
break;
case DORDER_CIRCLE:
if (!isVtolDroid(psDroid))
{
break;
}
psDroid->order = psOrder->order;
psDroid->orderX = psOrder->x;
psDroid->orderY = psOrder->y;
actionDroidLoc(psDroid, DACTION_MOVE, psOrder->x,psOrder->y);
break;
default:
ASSERT( false, "orderUnitBase: unknown order" );
break;
}
}
/* Give a droid an order */
void orderDroid(DROID *psDroid, DROID_ORDER order)
{
DROID_ORDER_DATA sOrder;
ASSERT( psDroid != NULL,
"orderUnit: Invalid unit pointer" );
ASSERT( order == DORDER_NONE ||
order == DORDER_RETREAT ||
order == DORDER_DESTRUCT ||
order == DORDER_RTR ||
order == DORDER_RTB ||
order == DORDER_RECYCLE ||
order == DORDER_RUN ||
order == DORDER_RUNBURN ||
order == DORDER_TRANSPORTIN ||
order == DORDER_STOP, // Added this PD.
"orderUnit: Invalid order" );
memset(&sOrder,0,sizeof(DROID_ORDER_DATA));
sOrder.order = order;
orderDroidBase(psDroid, &sOrder);
if(bMultiPlayer)
{
SendDroidInfo(psDroid, order, 0, 0, NULL);
}
}
/* Check the order state of a droid */
BOOL orderState(DROID *psDroid, DROID_ORDER order)
{
if (order == DORDER_RTR)
{
return psDroid->order == DORDER_RTR || psDroid->order == DORDER_RTR_SPECIFIED;
}
return psDroid->order == order;
}
BOOL validOrderForLoc(DROID_ORDER order)
{
return (order == DORDER_NONE || order == DORDER_MOVE || order == DORDER_GUARD ||
order == DORDER_SCOUT || order == DORDER_RUN || order == DORDER_PATROL ||
order == DORDER_TRANSPORTOUT || order == DORDER_TRANSPORTIN ||
order == DORDER_TRANSPORTRETURN || order == DORDER_DISEMBARK ||
order == DORDER_CIRCLE);
}
/* Give a droid an order with a location target */
void orderDroidLoc(DROID *psDroid, DROID_ORDER order, UDWORD x, UDWORD y)
{
DROID_ORDER_DATA sOrder;
ASSERT(psDroid != NULL, "Invalid unit pointer");
ASSERT(validOrderForLoc(order), "Invalid order for location");
orderClearDroidList(psDroid);
if(bMultiPlayer) //ajl
{
SendDroidInfo(psDroid, order, x, y, NULL);
turnOffMultiMsg(true); // msgs off.
}
memset(&sOrder,0,sizeof(DROID_ORDER_DATA));
sOrder.order = order;
sOrder.x = (UWORD)x;
sOrder.y = (UWORD)y;
orderDroidBase(psDroid, &sOrder);
turnOffMultiMsg(false); //msgs back on..
}
/* Get the state of a droid order with it's location */
BOOL orderStateLoc(DROID *psDroid, DROID_ORDER order, UDWORD *pX, UDWORD *pY)
{
if (order != psDroid->order)
{
return false;
}
// check the order is one with a location
switch (psDroid->order)
{
default:
// not a location order - return false
break;
case DORDER_MOVE:
case DORDER_RETREAT:
*pX = psDroid->orderX;
*pY = psDroid->orderY;
return true;
break;
}
return false;
}
BOOL validOrderForObj(DROID_ORDER order)
{
return (order == DORDER_NONE || order == DORDER_HELPBUILD || order == DORDER_DEMOLISH ||
order == DORDER_REPAIR || order == DORDER_ATTACK || order == DORDER_FIRESUPPORT ||
order == DORDER_OBSERVE || order == DORDER_ATTACKTARGET || order == DORDER_RTR ||
order == DORDER_RTR_SPECIFIED || order == DORDER_EMBARK || order == DORDER_GUARD ||
order == DORDER_DROIDREPAIR || order == DORDER_RESTORE || order == DORDER_BUILDMODULE ||
order == DORDER_REARM || order == DORDER_CLEARWRECK || order == DORDER_RECOVER);
}
/* Give a droid an order with an object target */
void orderDroidObj(DROID *psDroid, DROID_ORDER order, BASE_OBJECT *psObj)
{
DROID_ORDER_DATA sOrder;
ASSERT(psDroid != NULL, "Invalid unit pointer");
ASSERT(validOrderForObj(order), "Invalid order for object");
orderClearDroidList(psDroid);
if(bMultiPlayer) //ajl
{
SendDroidInfo(psDroid, order, 0, 0, psObj);
}
memset(&sOrder,0,sizeof(DROID_ORDER_DATA));
sOrder.order = order;
sOrder.psObj = psObj;
orderDroidBase(psDroid, &sOrder);
}
/* Get the state of a droid order with an object */
BASE_OBJECT* orderStateObj(DROID *psDroid, DROID_ORDER order)
{
BOOL match = false;
switch (order)
{
case DORDER_BUILD:
case DORDER_LINEBUILD:
case DORDER_HELPBUILD:
if (psDroid->order == DORDER_BUILD ||
psDroid->order == DORDER_HELPBUILD ||
psDroid->order == DORDER_LINEBUILD)
{
match = true;
}
break;
case DORDER_ATTACK:
case DORDER_FIRESUPPORT:
case DORDER_OBSERVE:
case DORDER_DEMOLISH:
case DORDER_DROIDREPAIR:
case DORDER_REARM:
case DORDER_GUARD:
if (psDroid->order == order)
{
match = true;
}
break;
case DORDER_RTR:
if (psDroid->order == DORDER_RTR ||
psDroid->order == DORDER_RTR_SPECIFIED)
{
match = true;
}
default:
break;
}
if (!match)
{
return NULL;
}
// check the order is one with an object
switch (psDroid->order)
{
default:
// not an object order - return false
return NULL;
break;
case DORDER_BUILD:
case DORDER_LINEBUILD:
if (psDroid->action == DACTION_BUILD ||
psDroid->action == DACTION_BUILDWANDER)
{
return psDroid->psTarget;
}
break;
case DORDER_HELPBUILD:
if (psDroid->action == DACTION_BUILD ||
psDroid->action == DACTION_BUILDWANDER ||
psDroid->action == DACTION_MOVETOBUILD)
{
return psDroid->psTarget;
}
break;
//case DORDER_HELPBUILD:
case DORDER_ATTACK:
case DORDER_FIRESUPPORT:
case DORDER_OBSERVE:
case DORDER_DEMOLISH:
case DORDER_RTR:
case DORDER_RTR_SPECIFIED:
case DORDER_DROIDREPAIR:
case DORDER_REARM:
case DORDER_GUARD:
return psDroid->psTarget;
break;
}
return NULL;
}
/* Give a droid an order with a location and a stat */
void orderDroidStatsLoc(DROID *psDroid, DROID_ORDER order, BASE_STATS *psStats, UDWORD x, UDWORD y)
{
DROID_ORDER_DATA sOrder;
ASSERT(psDroid != NULL, "Invalid unit pointer");
ASSERT(order == DORDER_BUILD, "Invalid order for location");
memset(&sOrder,0,sizeof(DROID_ORDER_DATA));
sOrder.order = order;
sOrder.x = (UWORD)x;
sOrder.y = (UWORD)y;
sOrder.psStats = psStats;
orderDroidBase(psDroid, &sOrder);
}
/* add an order with a location and a stat to the droids order list*/
void orderDroidStatsLocAdd(DROID *psDroid, DROID_ORDER order, BASE_STATS *psStats, UDWORD x, UDWORD y)
{
DROID_ORDER_DATA sOrder;
ASSERT(psDroid != NULL, "Invalid unit pointer");
// can only queue build orders with this function
if (order != DORDER_BUILD)
{
return;
}
memset(&sOrder,0,sizeof(DROID_ORDER_DATA));
sOrder.order = order;
sOrder.x = (UWORD)x;
sOrder.y = (UWORD)y;
sOrder.psStats = psStats;
orderDroidAdd(psDroid, &sOrder);
}
/* Give a droid an order with a location and a stat */
void orderDroidStatsTwoLoc(DROID *psDroid, DROID_ORDER order, BASE_STATS *psStats, UDWORD x1, UDWORD y1, UDWORD x2, UDWORD y2)
{
DROID_ORDER_DATA sOrder;
ASSERT(psDroid != NULL, "Invalid unit pointer");
ASSERT(order == DORDER_LINEBUILD, "Invalid order for location");
ASSERT(x1 == x2 || y1 == y2, "Invalid locations for LINEBUILD");
memset(&sOrder,0,sizeof(DROID_ORDER_DATA));
sOrder.order = order;
sOrder.x = (UWORD)x1;
sOrder.y = (UWORD)y1;
sOrder.x2 = (UWORD)x2;
sOrder.y2 = (UWORD)y2;
sOrder.psStats = psStats;
orderDroidBase(psDroid, &sOrder);
}
/* Add an order with a location and a stat */
void orderDroidStatsTwoLocAdd(DROID *psDroid, DROID_ORDER order,
BASE_STATS *psStats, UDWORD x1, UDWORD y1, UDWORD x2, UDWORD y2)
{
DROID_ORDER_DATA sOrder;
ASSERT(psDroid != NULL, "Invalid unit pointer");
ASSERT(order == DORDER_LINEBUILD, "Invalid order for location");
ASSERT(x1 == x2 || y1 == y2, "Invalid locations for LINEBUILD");
memset(&sOrder,0,sizeof(DROID_ORDER_DATA));
sOrder.order = order;
sOrder.x = (UWORD)x1;
sOrder.y = (UWORD)y1;
sOrder.x2 = (UWORD)x2;
sOrder.y2 = (UWORD)y2;
sOrder.psStats = psStats;
orderDroidAdd(psDroid, &sOrder);
}
/* Get the state of a droid order with a location and a stat */
BOOL orderStateStatsLoc(DROID *psDroid, DROID_ORDER order, BASE_STATS **ppsStats, UDWORD *pX, UDWORD *pY)
{
BOOL match = false;
switch (order)
{
case DORDER_BUILD:
case DORDER_LINEBUILD:
if (psDroid->order == DORDER_BUILD ||
psDroid->order == DORDER_LINEBUILD)
{
match = true;
}
break;
default:
break;
}
if (!match)
{
return false;
}
// check the order is one with stats and a location
switch (psDroid->order)
{
default:
// not a stats/location order - return false
return false;
break;
case DORDER_BUILD:
case DORDER_LINEBUILD:
if (psDroid->action == DACTION_MOVETOBUILD ||
psDroid->action == DACTION_BUILD_FOUNDATION ||
psDroid->action == DACTION_FOUNDATION_WANDER)
{
*ppsStats = psDroid->psTarStats;
*pX = psDroid->orderX;
*pY = psDroid->orderY;
return true;
}
break;
}
return false;
}
// add an order to a droids order list
void orderDroidAdd(DROID *psDroid, DROID_ORDER_DATA *psOrder)
{
Vector3i position;
ASSERT(psDroid != NULL, "Invalid unit pointer");
if (psDroid->listSize >= ORDER_LIST_MAX)
{
// no room to store the order, quit
return;
}
// if not doing anything - do it immediately
if (psDroid->listSize == 0 &&
(psDroid->order == DORDER_NONE ||
psDroid->order == DORDER_GUARD))
{
orderDroidBase(psDroid, psOrder);
}
else
{
psDroid->asOrderList[psDroid->listSize].order = psOrder->order;
//psDroid->asOrderList[psDroid->listSize].psObj = psOrder->psObj;
if (psOrder->order == DORDER_BUILD || psOrder->order == DORDER_LINEBUILD)
{
setDroidOrderTarget(psDroid, psOrder->psStats, psDroid->listSize);
}
else
{
setDroidOrderTarget(psDroid, psOrder->psObj, psDroid->listSize);
}
psDroid->asOrderList[psDroid->listSize].x = (UWORD)psOrder->x;
psDroid->asOrderList[psDroid->listSize].y = (UWORD)psOrder->y;
psDroid->asOrderList[psDroid->listSize].x2 = (UWORD)psOrder->x2;
psDroid->asOrderList[psDroid->listSize].y2 = (UWORD)psOrder->y2;
psDroid->listSize += 1;
}
//don't display the arrow-effects with build orders since unnecessary
if (!bOrderEffectDisplayed && (psOrder->order != DORDER_BUILD ||
psOrder->order != DORDER_LINEBUILD || psOrder->order !=
DORDER_BUILDMODULE || psOrder->order != DORDER_HELPBUILD))
{
position.x = psOrder->x;
position.z = psOrder->y;
position.y = map_Height(position.x, position.z) + 32;
if ((psOrder->psObj != NULL) &&
(psOrder->psObj->sDisplay.imd != NULL))
{
position.y += psOrder->psObj->sDisplay.imd->max.y;
}
addEffect(&position,EFFECT_WAYPOINT,WAYPOINT_TYPE,false,NULL,0);
bOrderEffectDisplayed = true;
}
}
// do the next order from a droids order list
BOOL orderDroidList(DROID *psDroid)
{
DROID_ORDER_DATA sOrder;
if (psDroid->listSize > 0)
{
// there are some orders to give
memset(&sOrder, 0, sizeof(DROID_ORDER_DATA));
sOrder.order = psDroid->asOrderList[0].order;
//sOrder.psObj = psDroid->asOrderList[0].psObj;
switch (psDroid->asOrderList[0].order)
{
case DORDER_MOVE:
sOrder.psObj = NULL;
break;
case DORDER_ATTACK:
case DORDER_REPAIR:
case DORDER_OBSERVE:
case DORDER_DROIDREPAIR:
case DORDER_FIRESUPPORT:
case DORDER_CLEARWRECK:
case DORDER_DEMOLISH:
case DORDER_HELPBUILD:
case DORDER_BUILDMODULE:
sOrder.psObj = (BASE_OBJECT *)psDroid->asOrderList[0].psOrderTarget;
break;
case DORDER_BUILD:
case DORDER_LINEBUILD:
sOrder.psObj = NULL;
sOrder.psStats = (BASE_STATS *)psDroid->asOrderList[0].psOrderTarget;
break;
default:
ASSERT( false, "orderDroidList: Invalid order" );
return false;
}
sOrder.x = psDroid->asOrderList[0].x;
sOrder.y = psDroid->asOrderList[0].y;
sOrder.x2 = psDroid->asOrderList[0].x2;
sOrder.y2 = psDroid->asOrderList[0].y2;
psDroid->listSize -= 1;
// move the rest of the list down
memmove(psDroid->asOrderList, psDroid->asOrderList + 1, psDroid->listSize * sizeof(ORDER_LIST));
memset(psDroid->asOrderList + psDroid->listSize, 0, sizeof(ORDER_LIST));
orderDroidBase(psDroid, &sOrder);
//don't send BUILD orders in multiplayer
if(bMultiPlayer && !(sOrder.order == DORDER_BUILD || sOrder.order == DORDER_LINEBUILD))
{
SendDroidInfo(psDroid, sOrder.order , sOrder.x, sOrder.y,sOrder.psObj);
}
return true;
}
return false;
}
// clear all the orders from the list
void orderClearDroidList(DROID *psDroid)
{
psDroid->listSize = 0;
memset(psDroid->asOrderList, 0, sizeof(ORDER_LIST)*ORDER_LIST_MAX);
}
// check all the orders in the list for died objects
void orderCheckList(DROID *psDroid)
{
SDWORD i;
i=0;
while (i<psDroid->listSize)
{
//if (psDroid->asOrderList[i].psObj &&
// (psDroid->asOrderList[i].psObj)->died)
//if order requires an object
if (psDroid->asOrderList[i].order == DORDER_ATTACK ||
psDroid->asOrderList[i].order == DORDER_REPAIR ||
psDroid->asOrderList[i].order == DORDER_OBSERVE ||
psDroid->asOrderList[i].order == DORDER_DROIDREPAIR ||
psDroid->asOrderList[i].order == DORDER_FIRESUPPORT ||
psDroid->asOrderList[i].order == DORDER_CLEARWRECK ||
psDroid->asOrderList[i].order == DORDER_DEMOLISH ||
psDroid->asOrderList[i].order == DORDER_HELPBUILD ||
psDroid->asOrderList[i].order == DORDER_BUILDMODULE)
{
if ((BASE_OBJECT *)psDroid->asOrderList[i].psOrderTarget &&
((BASE_OBJECT *)psDroid->asOrderList[i].psOrderTarget)->died)
{
// copy any other orders down the stack
psDroid->listSize -= 1;
memmove(psDroid->asOrderList + i, psDroid->asOrderList + i + 1,
(psDroid->listSize - i) * sizeof(ORDER_LIST));
memset(psDroid->asOrderList + psDroid->listSize, 0, sizeof(ORDER_LIST));
}
else
{
i++;
}
}
else
{
i ++;
}
}
}
// add a location order to a droids order list
static BOOL orderDroidLocAdd(DROID *psDroid, DROID_ORDER order, UDWORD x, UDWORD y)
{
DROID_ORDER_DATA sOrder;
// can only queue move orders
if (order != DORDER_MOVE)
{
return false;
}
memset(&sOrder, 0, sizeof(DROID_ORDER_DATA));
sOrder.order = order;
sOrder.x = (UWORD)x;
sOrder.y = (UWORD)y;
orderDroidAdd(psDroid, &sOrder);
return true;
}
// add an object order to a droids order list
static BOOL orderDroidObjAdd(DROID *psDroid, DROID_ORDER order, BASE_OBJECT *psObj[DROID_MAXWEAPS])
{
DROID_ORDER_DATA sOrder;
// check can queue the order
if (order != DORDER_ATTACK &&
order != DORDER_REPAIR &&
order != DORDER_OBSERVE &&
order != DORDER_DROIDREPAIR &&
order != DORDER_FIRESUPPORT &&
order != DORDER_CLEARWRECK &&
order != DORDER_DEMOLISH &&
order != DORDER_HELPBUILD &&
order != DORDER_BUILDMODULE)
{
return false;
}
memset(&sOrder, 0, sizeof(DROID_ORDER_DATA));
sOrder.order = order;
sOrder.psObj = psObj[0];
sOrder.x = (UWORD)psObj[0]->pos.x;
sOrder.y = (UWORD)psObj[0]->pos.y;
orderDroidAdd(psDroid, &sOrder);
return true;
}
/* Choose an order for a droid from a location */
DROID_ORDER chooseOrderLoc(DROID *psDroid, UDWORD x,UDWORD y)
{
DROID_ORDER order = DORDER_NONE;
SECONDARY_STATE state;
PROPULSION_TYPE propulsion = getPropulsionStats(psDroid)->propulsionType;
// default to move; however, we can only end up on a tile
// where can stay, ie VTOLs must be able to land as well
if (isVtolDroid(psDroid))
{
propulsion = PROPULSION_TYPE_WHEELED;
}
if (!fpathBlockingTile(map_coord(x), map_coord(y), propulsion))
{
order = DORDER_MOVE;
}
// scout if shift was pressed
if(keyDown(KEY_LSHIFT) || keyDown(KEY_RSHIFT))
{
order = DORDER_SCOUT;
}
// and now we want Transporters to fly! - in multiPlayer!!
if (psDroid->droidType == DROID_TRANSPORTER && game.maxPlayers != 0)
{
/* in MultiPlayer - if ALT-key is pressed then need to get the Transporter
* to fly to location and all units disembark */
if (keyDown(KEY_LALT) || keyDown(KEY_RALT))
{
order = DORDER_DISEMBARK;
}
}
else if (secondaryGetState(psDroid, DSO_CIRCLE, &state) &&
state == DSS_CIRCLE_SET)
{
order = DORDER_CIRCLE;
secondarySetState(psDroid, DSO_CIRCLE, DSS_NONE);
}
else if (secondaryGetState(psDroid, DSO_PATROL, &state) &&
state == DSS_PATROL_SET)
{
order = DORDER_PATROL;
secondarySetState(psDroid, DSO_PATROL, DSS_NONE);
}
return order;
}
/* Give selected droids an order from a location target or
move selected Delivery Point to new location
If add is true then the order is queued in the droid
*/
void orderSelectedLocAdd(UDWORD player, UDWORD x, UDWORD y, BOOL add)
{
DROID *psCurr;
DROID_ORDER order;
//if were in build select mode ignore all other clicking
if (intBuildSelectMode())
{
return;
}
if (!add && bMultiPlayer && SendGroupOrderSelected((UBYTE)player,x,y,NULL) )
{ // turn off multiplay messages,since we've send a group one instead.
turnOffMultiMsg(true);
}
// remove any units from their command group
for(psCurr = apsDroidLists[player]; psCurr; psCurr=psCurr->psNext)
{
if (psCurr->selected && hasCommander(psCurr))
{
grpLeave(psCurr->psGroup, psCurr);
}
}
// note that an order list graphic needs to be displayed
bOrderEffectDisplayed = false;
for(psCurr = apsDroidLists[player]; psCurr; psCurr=psCurr->psNext)
{
if (psCurr->selected)
{
order = chooseOrderLoc(psCurr, x, y);
// see if the order can be added to the list
if (order != DORDER_NONE && !(add && orderDroidLocAdd(psCurr, order, x, y)))
{
// if not just do it straight off
orderDroidLoc(psCurr, order, x,y);
}
}
}
turnOffMultiMsg(false); // msgs back on...
}
void orderSelectedLoc(UDWORD player, UDWORD x, UDWORD y)
{
orderSelectedLocAdd(player, x,y, false);
}
/* Choose an order for a droid from an object */
DROID_ORDER chooseOrderObj(DROID *psDroid, BASE_OBJECT *psObj)
{
DROID_ORDER order;
STRUCTURE *psStruct;
FEATURE *psFeature;
if(psDroid->droidType == DROID_TRANSPORTER)
{
//in multiPlayer, need to be able to get Transporter repaired
if (bMultiPlayer)
{
//default to no order
order = DORDER_NONE;
if (psObj->player == psDroid->player &&
psObj->type == OBJ_STRUCTURE)
{
psStruct = (STRUCTURE *) psObj;
ASSERT( psObj != NULL,
"chooseOrderObj: invalid structure pointer" );
if ( psStruct->pStructureType->type == REF_REPAIR_FACILITY &&
psStruct->status == SS_BUILT)
{
order = DORDER_RTR_SPECIFIED;
}
}
return (order);
}
else
{
return(DORDER_NONE);
}
}
//check for transporters first
if (psObj->type == OBJ_DROID && ((DROID *)psObj)->droidType == DROID_TRANSPORTER
&& psObj->player == psDroid->player)
{
order = DORDER_EMBARK;
//Cannot test this here since bMultiPlayer will have been set to false
/* //in multiPlayer can only put cyborgs onto a Transporter
if (bMultiPlayer && psDroid->droidType != DROID_CYBORG)
{
order = DORDER_NONE;
}
*/
}
// go to recover an artifact/oil drum - don't allow VTOL's to get this order
else if (psObj->type == OBJ_FEATURE &&
(((FEATURE *)psObj)->psStats->subType == FEAT_GEN_ARTE ||
((FEATURE *)psObj)->psStats->subType == FEAT_OIL_DRUM) )
{
if (isVtolDroid(psDroid))
{
order = DORDER_NONE;
}
else
{
order = DORDER_RECOVER;
}
}
// else default to attack if the droid has a weapon
else if (psDroid->numWeaps > 0
&& psObj->player != psDroid->player
&& !aiCheckAlliances(psObj->player , psDroid->player) )
{
//check valid weapon/prop combination
if (!validTarget((BASE_OBJECT *)psDroid, psObj, 0))
{
order = DORDER_NONE;
}
else
{
order = DORDER_ATTACK;
}
}
else if (psDroid->droidType == DROID_SENSOR
&& psObj->player != psDroid->player
&& !aiCheckAlliances(psObj->player , psDroid->player) )
{
//check for standard sensor or VTOL intercept sensor
if (asSensorStats[psDroid->asBits[COMP_SENSOR].nStat].type == STANDARD_SENSOR ||
asSensorStats[psDroid->asBits[COMP_SENSOR].nStat].type == VTOL_INTERCEPT_SENSOR ||
asSensorStats[psDroid->asBits[COMP_SENSOR].nStat].type == SUPER_SENSOR)
{
// a sensor droid observing an object
order = DORDER_OBSERVE;
}
else
{
order = DORDER_NONE;
}
}
else if (droidSensorDroidWeapon(psObj, psDroid))
{
// got an indirect weapon droid or vtol doing fire support
order = DORDER_FIRESUPPORT;
setSensorAssigned();
}
else if ( psObj->player == psDroid->player &&
psObj->type == OBJ_DROID &&
((DROID *)psObj)->droidType == DROID_COMMAND &&
psDroid->droidType != DROID_COMMAND &&
psDroid->droidType != DROID_CONSTRUCT &&
psDroid->droidType != DROID_CYBORG_CONSTRUCT)
{
// if (!isVtolDroid(psDroid))
{
// get a droid to join a command droids group
cmdDroidAddDroid((DROID *) psObj, psDroid);
DeSelectDroid(psDroid);
order = DORDER_NONE;
}
/* else
{
order = DORDER_FIRESUPPORT;
}*/
}
//repair droid
else if (psObj->player == psDroid->player &&
psObj->type == OBJ_DROID &&
//psDroid->droidType == DROID_REPAIR &&
(psDroid->droidType == DROID_REPAIR ||
psDroid->droidType == DROID_CYBORG_REPAIR) &&
droidIsDamaged((DROID *)psObj))
{
order = DORDER_DROIDREPAIR;
}
// guarding constructor droids
else if (psObj->player == psDroid->player &&
psObj->type == OBJ_DROID &&
(((DROID *)psObj)->droidType == DROID_CONSTRUCT ||
((DROID *)psObj)->droidType == DROID_CYBORG_CONSTRUCT ||
((DROID *)psObj)->droidType == DROID_SENSOR) &&
(psDroid->droidType == DROID_WEAPON ||
psDroid->droidType == DROID_CYBORG) &&
proj_Direct(asWeaponStats + psDroid->asWeaps[0].nStat))
{
order = DORDER_GUARD;
assignSensorTarget(psObj);
psDroid->selected = false;
}
else if ( psObj->player == psDroid->player &&
psObj->type == OBJ_STRUCTURE )
{
psStruct = (STRUCTURE *) psObj;
ASSERT( psObj != NULL,
"chooseOrderObj: invalid structure pointer" );
/* check whether construction droid */
order = DORDER_NONE;
if ( psDroid->droidType == DROID_CONSTRUCT ||
psDroid->droidType == DROID_CYBORG_CONSTRUCT)
{
//Re-written to allow demolish order to be added to the queuing system
if (intDemolishSelectMode())
{
//check to see if anything is currently trying to build the structure
//can't build and demolish at the same time!
if (psStruct->status != SS_BUILT &&
checkDroidsBuilding(psStruct))
{
order = DORDER_NONE;
psDroid->psTarStats = NULL;
}
else
{
order = DORDER_DEMOLISH;
}
}
//check for non complete structures
else if (psStruct->status != SS_BUILT)
{
//if something else is demolishing, then help demolish
if (checkDroidsDemolishing(psStruct))
{
psDroid->psTarStats = (BASE_STATS *) structGetDemolishStat();
order = DORDER_DEMOLISH;
}
//else help build
else
{
order = DORDER_HELPBUILD;
}
}
//check for half built structure
/*else if ( psStruct->status == SS_BEING_BUILT)
{
// got a construction droid building a structure
order = DORDER_HELPBUILD;
}*/
//else if ( psStruct->body < psStruct->baseBodyPoints )
else if ( psStruct->body < structureBody(psStruct))
{
order = DORDER_REPAIR;
}
//check if can build a module
else if (buildModule(psStruct))
{
order = DORDER_BUILDMODULE;
}
else
{
order = DORDER_NONE;
}
}
if (order == DORDER_NONE)
{
/* check repair facility and in need of repair */
if ( psStruct->pStructureType->type == REF_REPAIR_FACILITY &&
psStruct->status == SS_BUILT)
// ((SDWORD)(PERCENT(psDroid->body,psDroid->originalBody)) < 100) )
{
order = DORDER_RTR_SPECIFIED;
}
else if (electronicDroid(psDroid) &&
//psStruct->resistance < (SDWORD)(psStruct->pStructureType->resistance))
psStruct->resistance < (SDWORD)structureResistance(psStruct->
pStructureType, psStruct->player))
{
order = DORDER_RESTORE;
}
//check for counter battery assignment
else if (structSensorDroidWeapon(psStruct, psDroid))
{
// secondarySetState(psDroid, DSO_HALTTYPE, DSS_HALT_HOLD);
order = DORDER_FIRESUPPORT;
//inform display system
setSensorAssigned();
//deselect droid
// psDroid->selected = false;
DeSelectDroid(psDroid);
}
//REARM VTOLS
else if (isVtolDroid(psDroid))
{
//default to no order
order = DORDER_NONE;
//check if rearm pad
if (psStruct->pStructureType->type == REF_REARM_PAD)
{
//don't bother checking cos we want it to go there if directed
//check if need to be rearmed/repaired
//if (!vtolHappy(psDroid))
{
order = DORDER_REARM;
}
}
}
else
{
order = DORDER_GUARD;
}
}
}
//check for constructor droid clearing up wrecked buildings
else if ( (psDroid->droidType == DROID_CONSTRUCT ||
psDroid->droidType == DROID_CYBORG_CONSTRUCT) &&
psObj->type == OBJ_FEATURE )
{
psFeature = (FEATURE *) psObj;
ASSERT( psObj != NULL,
"chooseOrderObj: invalid feature pointer" );
if (psFeature->psStats->subType == FEAT_BUILD_WRECK)
{
order = DORDER_CLEARWRECK;
}
else
{
order = DORDER_NONE;
}
}
else
{
order = DORDER_NONE;
}
return order;
}
static void orderPlayOrderObjAudio( UDWORD player, BASE_OBJECT *psObj )
{
DROID *psDroid;
/* loop over selected droids */
for( psDroid = apsDroidLists[player]; psDroid; psDroid=psDroid->psNext )
{
if ( psDroid->selected )
{
/* currently only looks for VTOL */
if ( isVtolDroid( psDroid ) )
{
switch ( psDroid->order )
{
case DORDER_ATTACK:
audio_QueueTrack( ID_SOUND_ON_OUR_WAY2 );
break;
}
}
/* only play audio once */
break;
}
}
}
/* Give selected droids an order from an object target
* If add is true the order is queued with the droid
*/
void orderSelectedObjAdd(UDWORD player, BASE_OBJECT *psObj, BOOL add)
{
DROID *psCurr, *psDemolish;
DROID_ORDER order;
if (!add && bMultiPlayer && SendGroupOrderSelected((UBYTE)player,0,0,psObj) )
{ // turn off multiplay messages,since we've send a group one instead.
turnOffMultiMsg(true);
}
// remove any units from their command group
for(psCurr = apsDroidLists[player]; psCurr; psCurr=psCurr->psNext)
{
if (psCurr->selected && hasCommander(psCurr))
{
grpLeave(psCurr->psGroup, psCurr);
}
}
// note that an order list graphic needs to be displayed
bOrderEffectDisplayed = false;
psDemolish = NULL;
for(psCurr = apsDroidLists[player]; psCurr; psCurr=psCurr->psNext)
{
if (psCurr->selected)
{
order = chooseOrderObj(psCurr, psObj);
if (order == DORDER_DEMOLISH && player == selectedPlayer)
{
psDemolish = psCurr;
}
// see if the order can be added to the list
if (!(add && orderDroidObjAdd(psCurr, order, &psObj)))
{
// if not just do it straight off
orderDroidObj(psCurr, order, psObj);
}
}
}
orderPlayOrderObjAudio( player, psObj );
turnOffMultiMsg(false); //msgs back on.
//This feels like the wrong place but it has to be done once the order has been received...
//demolish queuing...need to bring the interface back up
if (psDemolish)
{
/*this will stop the constructor being able to demolish any other
buildings until the demolish button is re-selected*/
intDemolishCancel();
//turn off the build queue availability until desired release date!
#ifndef DISABLE_BUILD_QUEUE
//re-add the stat (side) interface to allow a new selection
if (ctrlShiftDown())
{
intConstructorSelected(psDemolish);
}
#endif
}
}
void orderSelectedObj(UDWORD player, BASE_OBJECT *psObj)
{
orderSelectedObjAdd(player, psObj, false);
}
/* order all selected droids with a location and a stat */
void orderSelectedStatsLoc(UDWORD player, DROID_ORDER order,
BASE_STATS *psStats, UDWORD x, UDWORD y, BOOL add)
{
DROID *psCurr;
//turn off the build queue availability until desired release date!
#ifdef DISABLE_BUILD_QUEUE
add = false;
#endif
for(psCurr = apsDroidLists[player]; psCurr; psCurr=psCurr->psNext)
{
if (psCurr->selected)
{
if (add)
{
orderDroidStatsLocAdd(psCurr, order, psStats, x,y);
}
else
{
orderDroidStatsLoc(psCurr, order, psStats, x,y);
}
}
}
}
/* order all selected droids with two a locations and a stat */
void orderSelectedStatsTwoLoc(UDWORD player, DROID_ORDER order,
BASE_STATS *psStats, UDWORD x1, UDWORD y1, UDWORD x2, UDWORD y2, BOOL add)
{
DROID *psCurr;
//turn off the build queue availability until desired release date!
#ifdef DISABLE_BUILD_QUEUE
add = false;
#endif
for(psCurr = apsDroidLists[player]; psCurr; psCurr=psCurr->psNext)
{
if (psCurr->selected)
{
if (add)
{
orderDroidStatsTwoLocAdd(psCurr, order, psStats, x1,y1, x2,y2);
}
else
{
orderDroidStatsTwoLoc(psCurr, order, psStats, x1,y1, x2,y2);
}
}
}
}
// See if the player has access to a transporter in this map.
//
DROID *FindATransporter(void)
{
DROID *psDroid;
for(psDroid = apsDroidLists[selectedPlayer]; (psDroid != NULL); psDroid = psDroid->psNext) {
if( psDroid->droidType == DROID_TRANSPORTER ) {
return psDroid;
}
}
return NULL;
}
// See if the player has access to a factory in this map.
//
static STRUCTURE *FindAFactory(UDWORD player, UDWORD factoryType)
{
STRUCTURE *psStruct;
ASSERT( player < MAX_PLAYERS,
"FindAFactory: invalid player number" );
for(psStruct = apsStructLists[player]; psStruct != NULL; psStruct = psStruct->psNext)
{
if(psStruct->pStructureType->type == factoryType)
{
return psStruct;
}
}
return NULL;
}
// See if the player has access to a repair facility in this map.
//
static STRUCTURE *FindARepairFacility(void)
{
STRUCTURE *psStruct;
for(psStruct = apsStructLists[selectedPlayer]; (psStruct != NULL); psStruct = psStruct->psNext) {
if(psStruct->pStructureType->type == REF_REPAIR_FACILITY) {
return psStruct;
}
}
return NULL;
}
// see if a droid supports a secondary order
BOOL secondarySupported(DROID *psDroid, SECONDARY_ORDER sec)
{
BOOL supported;
supported = true; // Default to supported.
switch (sec)
{
case DSO_ASSIGN_PRODUCTION:
case DSO_ASSIGN_CYBORG_PRODUCTION:
case DSO_ASSIGN_VTOL_PRODUCTION:
case DSO_CLEAR_PRODUCTION: // remove production from a command droid
case DSO_FIRE_DESIGNATOR:
if (psDroid->droidType != DROID_COMMAND)
{
supported = false;
}
if ((sec == DSO_ASSIGN_PRODUCTION && FindAFactory(psDroid->player, REF_FACTORY) == NULL) ||
(sec == DSO_ASSIGN_CYBORG_PRODUCTION && FindAFactory(psDroid->player, REF_CYBORG_FACTORY) == NULL) ||
(sec == DSO_ASSIGN_VTOL_PRODUCTION && FindAFactory(psDroid->player, REF_VTOL_FACTORY) == NULL))
{
supported = false;
}
//don't allow factories to be assigned to commanders during a Limbo Expand mission
if ((sec == DSO_ASSIGN_PRODUCTION || sec == DSO_ASSIGN_CYBORG_PRODUCTION ||
sec == DSO_ASSIGN_VTOL_PRODUCTION) && missionLimboExpand())
{
supported = false;
}
break;
case DSO_ATTACK_RANGE:
case DSO_ATTACK_LEVEL:
if (psDroid->droidType == DROID_REPAIR ||
psDroid->droidType == DROID_CYBORG_REPAIR)
{
supported = false;
}
if (psDroid->droidType == DROID_CONSTRUCT ||
psDroid->droidType == DROID_CYBORG_CONSTRUCT)
{
supported = false;
}
break;
case DSO_CIRCLE:
if (!isVtolDroid(psDroid))
{
supported = false;
}
break;
case DSO_REPAIR_LEVEL:
case DSO_PATROL:
case DSO_HALTTYPE:
case DSO_RETURN_TO_LOC:
break;
/* case DSO_RETURN_TO_REPAIR: // Only if player has got a repair facility.
if(FindARepairFacility() == NULL) {
supported = false;
}
break;*/
case DSO_RECYCLE: // Only if player has got a factory.
if ((FindAFactory(psDroid->player, REF_FACTORY) == NULL) &&
(FindAFactory(psDroid->player, REF_CYBORG_FACTORY) == NULL) &&
(FindAFactory(psDroid->player, REF_VTOL_FACTORY) == NULL) &&
(FindARepairFacility() == NULL))
{
supported = false;
}
break;
/* case DSO_EMBARK: // Only if player has got a transporter.
if(FindATransporter() == NULL) {
supported = false;
}
break;*/
default:
supported = false;
break;
}
return supported;
}
// get the state of a secondary order, return false if unsupported
BOOL secondaryGetState(DROID *psDroid, SECONDARY_ORDER sec, SECONDARY_STATE *pState)
{
SECONDARY_STATE state;
state = psDroid->secondaryOrder;
switch (sec)
{
case DSO_ATTACK_RANGE:
*pState = (SECONDARY_STATE)(state & DSS_ARANGE_MASK);
break;
case DSO_REPAIR_LEVEL:
*pState = (SECONDARY_STATE)(state & DSS_REPLEV_MASK);
break;
case DSO_ATTACK_LEVEL:
*pState = (SECONDARY_STATE)(state & DSS_ALEV_MASK);
break;
case DSO_ASSIGN_PRODUCTION:
case DSO_ASSIGN_CYBORG_PRODUCTION:
case DSO_ASSIGN_VTOL_PRODUCTION:
*pState = (SECONDARY_STATE)(state & DSS_ASSPROD_MASK);
break;
case DSO_RECYCLE:
*pState = (SECONDARY_STATE)(state & DSS_RECYCLE_MASK);
break;
case DSO_PATROL:
*pState = (SECONDARY_STATE)(state & DSS_PATROL_MASK);
break;
case DSO_CIRCLE:
*pState = (SECONDARY_STATE)(state & DSS_CIRCLE_MASK);
break;
case DSO_HALTTYPE:
*pState = (SECONDARY_STATE)(state & DSS_HALT_MASK);
break;
case DSO_RETURN_TO_LOC:
*pState = (SECONDARY_STATE)(state & DSS_RTL_MASK);
break;
case DSO_FIRE_DESIGNATOR:
// *pState = state & DSS_FIREDES_MASK;
if (cmdDroidGetDesignator(psDroid->player) == psDroid)
{
*pState = DSS_FIREDES_SET;
}
else
{
*pState = DSS_NONE;
}
break;
default:
*pState = DSS_NONE;
break;
}
return true;
}
#ifdef DEBUG
static char *secondaryPrintFactories(UDWORD state)
{
SDWORD i;
static char aBuff[255];
memset(aBuff, 0, sizeof(aBuff));
for(i=0; i<5; i++)
{
if (state & (1 << (i + DSS_ASSPROD_SHIFT)))
{
aBuff[i] = (char)('0' + i);
}
else
{
aBuff[i] = ' ';
}
if (state & (1 << (i + DSS_ASSPROD_CYBORG_SHIFT)))
{
aBuff[i*2 + 5] = 'c';
aBuff[i*2 + 6] = (char)('0' + i);
}
else
{
aBuff[i*2 + 5] = ' ';
aBuff[i*2 + 6] = ' ';
}
}
return aBuff;
}
#else
#define secondaryPrintFactories(x)
#endif
// check the damage level of a droid against it's secondary state
void secondaryCheckDamageLevel(DROID *psDroid)
{
SECONDARY_STATE State;
unsigned int repairLevel;
if( secondaryGetState(psDroid, DSO_REPAIR_LEVEL, &State) )
{
if (State == DSS_REPLEV_LOW)
{
repairLevel = REPAIRLEV_HIGH; //repair often
}
else if(State == DSS_REPLEV_HIGH)
{
repairLevel = REPAIRLEV_LOW; // don't repair often.
}
else
{
repairLevel = 0; //never repair
}
//don't bother checking if 'do or die'
if( repairLevel && PERCENT(psDroid->body,psDroid->originalBody) <= repairLevel)
{
if (psDroid->selected)
{
DeSelectDroid(psDroid);
}
if (!isVtolDroid(psDroid))
{
psDroid->group = UBYTE_MAX;
}
/* set return to repair if not on hold */
if ( psDroid->order != DORDER_RTR &&
psDroid->order != DORDER_RTB &&
!vtolRearming(psDroid))
{
if (isVtolDroid(psDroid))
{
moveToRearm(psDroid);
}
else
{
orderDroid(psDroid, DORDER_RTR);
}
}
}
}
}
// set the state of a secondary order, return false if failed.
BOOL secondarySetState(DROID *psDroid, SECONDARY_ORDER sec, SECONDARY_STATE State)
{
UDWORD CurrState, factType, prodType;
STRUCTURE *psStruct;
SDWORD factoryInc, order;
BOOL retVal, bMultiPlayGame = false;
DROID *psTransport, *psCurr, *psNext;
if(bMultiPlayer)
{
//store the value before overwriting
bMultiPlayGame = bMultiPlayer;
sendDroidSecondary(psDroid,sec,State);
turnOffMultiMsg(true); // msgs off.
}
// set the state for any droids in the command group
if ((sec != DSO_RECYCLE) &&
psDroid->droidType == DROID_COMMAND &&
psDroid->psGroup != NULL &&
psDroid->psGroup->type == GT_COMMAND)
{
grpSetSecondary(psDroid->psGroup, sec, State);
}
CurrState = psDroid->secondaryOrder;
retVal = true;
switch (sec) {
case DSO_ATTACK_RANGE:
CurrState = (CurrState & ~DSS_ARANGE_MASK) | State;
break;
case DSO_REPAIR_LEVEL:
CurrState = (CurrState & ~DSS_REPLEV_MASK) | State;
psDroid->secondaryOrder = CurrState;
secondaryCheckDamageLevel(psDroid);
break;
case DSO_ATTACK_LEVEL:
CurrState = (CurrState & ~DSS_ALEV_MASK) | State;
if (State == DSS_ALEV_NEVER)
{
if ( orderState(psDroid, DORDER_ATTACK) )// ||
// orderState(psDroid, DORDER_FIRESUPPORT) )
{
// just kill these orders
orderDroid(psDroid, DORDER_STOP);
if (isVtolDroid(psDroid))
{
moveToRearm(psDroid);
}
}
else if ( orderState(psDroid, DORDER_GUARD) &&
droidAttacking(psDroid))
{
// send the unit back to the guard position
actionDroid(psDroid, DACTION_NONE);
}
else if ( orderState(psDroid, DORDER_PATROL) )
{
// send the unit back to the patrol
actionDroidLoc(psDroid, DACTION_RETURNTOPOS, psDroid->actionX, psDroid->actionY);
}
}
break;
case DSO_ASSIGN_PRODUCTION:
case DSO_ASSIGN_CYBORG_PRODUCTION:
case DSO_ASSIGN_VTOL_PRODUCTION:
#ifdef DEBUG
debug( LOG_NEVER, "order factories %s\n", secondaryPrintFactories(State));
#endif
if ( sec == DSO_ASSIGN_PRODUCTION)
{
prodType = REF_FACTORY;
}
else if ( sec == DSO_ASSIGN_CYBORG_PRODUCTION)
{
prodType = REF_CYBORG_FACTORY;
}
else
{
prodType = REF_VTOL_FACTORY;
}
if (psDroid->droidType == DROID_COMMAND)
{
// look for the factories
for (psStruct = apsStructLists[psDroid->player]; psStruct;
psStruct=psStruct->psNext)
{
factType = psStruct->pStructureType->type;
if ( factType == REF_FACTORY ||
factType == REF_VTOL_FACTORY ||
factType == REF_CYBORG_FACTORY )
{
factoryInc = ((FACTORY*)psStruct->pFunctionality)->psAssemblyPoint->factoryInc;
if (factType == REF_FACTORY)
{
factoryInc += DSS_ASSPROD_SHIFT;
}
else if ( factType == REF_CYBORG_FACTORY )
{
factoryInc += DSS_ASSPROD_CYBORG_SHIFT;
}
else
{
factoryInc += DSS_ASSPROD_VTOL_SHIFT;
}
if ( !( CurrState & ( 1 << factoryInc) ) &&
( State & ( 1 << factoryInc) ) )
{
assignFactoryCommandDroid(psStruct, psDroid);// assign this factory to the command droid
}
else if ( ( prodType == factType ) &&
( CurrState & ( 1 << factoryInc) ) &&
!( State & ( 1 << factoryInc) ) )
{
// remove this factory from the command droid
assignFactoryCommandDroid(psStruct, NULL);
}
}
}
if (prodType == REF_FACTORY)
{
CurrState &= ~DSS_ASSPROD_FACT_MASK;
}
else if (prodType == REF_CYBORG_FACTORY)
{
CurrState &= ~DSS_ASSPROD_CYB_MASK;
}
else
{
CurrState &= ~DSS_ASSPROD_VTOL_MASK;
}
CurrState |= (State & DSS_ASSPROD_MASK);
#ifdef DEBUG
debug( LOG_NEVER, "final factories %s\n", secondaryPrintFactories(CurrState));
#endif
}
break;
case DSO_CLEAR_PRODUCTION:
if (psDroid->droidType == DROID_COMMAND)
{
// simply clear the flag - all the factory stuff is done in assignFactoryCommandDroid
CurrState &= ~ (State & DSS_ASSPROD_MASK);
}
break;
case DSO_RECYCLE:
if(State & DSS_RECYCLE_MASK)
{
if (!orderState(psDroid, DORDER_RECYCLE))
{
orderDroid(psDroid, DORDER_RECYCLE);
}
// CurrState &= ~(DSS_HOLD_SET|DSS_RTB_SET|DSS_RTR_SET|DSS_RECYCLE_SET);
CurrState &= ~(DSS_RTL_MASK|DSS_RECYCLE_MASK|DSS_HALT_MASK);
CurrState |= DSS_RECYCLE_SET|DSS_HALT_GUARD;
psDroid->group = UBYTE_MAX;
if (psDroid->psGroup != NULL)
{
if (psDroid->droidType == DROID_COMMAND)
{
// remove all the units from the commanders group
for (psCurr = psDroid->psGroup->psList; psCurr; psCurr=psNext)
{
psNext = psCurr->psGrpNext;
grpLeave(psCurr->psGroup, psCurr);
orderDroid(psCurr, DORDER_STOP);
}
}
else if (psDroid->psGroup->type == GT_COMMAND)
{
grpLeave(psDroid->psGroup, psDroid);
}
}
}
else
{
if (orderState(psDroid, DORDER_RECYCLE))
{
orderDroid(psDroid, DORDER_STOP);
}
CurrState &= ~DSS_RECYCLE_MASK;
}
break;
case DSO_CIRCLE:
if (State & DSS_CIRCLE_SET)
{
CurrState |= DSS_CIRCLE_SET;
}
else
{
CurrState &= ~DSS_CIRCLE_MASK;
}
break;
case DSO_PATROL:
if (State & DSS_PATROL_SET)
{
CurrState |= DSS_PATROL_SET;
}
else
{
CurrState &= ~DSS_PATROL_MASK;
}
break;
case DSO_HALTTYPE:
switch (State & DSS_HALT_MASK)
{
case DSS_HALT_PURSUE:
CurrState &= ~ DSS_HALT_MASK;
CurrState |= DSS_HALT_PURSUE;
if (orderState(psDroid, DORDER_GUARD))
{
orderDroid(psDroid, DORDER_STOP);
}
break;
case DSS_HALT_GUARD:
CurrState &= ~ DSS_HALT_MASK;
CurrState |= DSS_HALT_GUARD;
orderDroidLoc(psDroid, DORDER_GUARD, psDroid->pos.x,psDroid->pos.y);
break;
case DSS_HALT_HOLD:
CurrState &= ~ DSS_HALT_MASK;
CurrState |= DSS_HALT_HOLD;
if (!orderState(psDroid, DORDER_FIRESUPPORT))
{
orderDroid(psDroid, DORDER_STOP);
}
break;
}
break;
case DSO_RETURN_TO_LOC:
if ((State & DSS_RTL_MASK) == 0)
{
if (orderState(psDroid, DORDER_RTR) ||
orderState(psDroid, DORDER_RTB) ||
orderState(psDroid, DORDER_EMBARK))
{
orderDroid(psDroid, DORDER_STOP);
}
CurrState &= ~DSS_RTL_MASK;
}
else
{
order = DORDER_NONE;
CurrState &= ~DSS_RTL_MASK;
if ((CurrState & DSS_HALT_MASK) == DSS_HALT_HOLD)
{
CurrState &= ~DSS_HALT_MASK;
CurrState |= DSS_HALT_GUARD;
}
switch (State & DSS_RTL_MASK)
{
case DSS_RTL_REPAIR:
// if (FindARepairFacility() != NULL)
{
order = DORDER_RTR;
CurrState |= DSS_RTL_REPAIR;
// can't clear the selection here cos it breaks
// the secondary order screen
// psDroid->selected = false;
// psDroid->group = UBYTE_MAX;
}
// else
// {
// retVal = false;
// }
break;
case DSS_RTL_BASE:
order = DORDER_RTB;
CurrState |= DSS_RTL_BASE;
break;
case DSS_RTL_TRANSPORT:
psTransport = FindATransporter();
if (psTransport != NULL)
{
//in multiPlayer can only put cyborgs onto a Transporter
if (bMultiPlayGame && !cyborgDroid(psDroid))
{
retVal = false;
}
else
{
order = DORDER_EMBARK;
CurrState |= DSS_RTL_TRANSPORT;
if (!orderState(psDroid, DORDER_EMBARK))
{
orderDroidObj(psDroid, DORDER_EMBARK, (BASE_OBJECT *)psTransport);
}
}
}
else
{
retVal = false;
}
break;
default:
order = DORDER_NONE;
break;
}
if (!orderState(psDroid, order))
{
orderDroid(psDroid, order);
}
}
break;
case DSO_FIRE_DESIGNATOR:
// don't actually set any secondary flags - the cmdDroid array is
// always used to determine which commander is the designator
if (State & DSS_FIREDES_SET)
{
cmdDroidSetDesignator(psDroid);
}
else if (cmdDroidGetDesignator(psDroid->player) == psDroid)
{
cmdDroidClearDesignator(psDroid->player);
}
break;
default:
break;
}
psDroid->secondaryOrder = CurrState;
turnOffMultiMsg(false);
return retVal;
}
// deal with a droid receiving a primary order
BOOL secondaryGotPrimaryOrder(DROID *psDroid, DROID_ORDER order)
{
UDWORD oldState;
if (psDroid->droidType == DROID_TRANSPORTER)
{
return false;
}
if (order != DORDER_NONE &&
order != DORDER_STOP &&
order != DORDER_DESTRUCT &&
order != DORDER_GUARD)
{
//reset 2ndary order
oldState = psDroid->secondaryOrder;
psDroid->secondaryOrder &= ~ (DSS_RTL_MASK|DSS_RECYCLE_MASK|DSS_PATROL_MASK);
if((oldState != psDroid->secondaryOrder) &&
(psDroid->player == selectedPlayer))
{
intRefreshScreen();
}
}
return false;
}
// set the state of a numeric group
static void secondarySetGroupState(UDWORD player, UDWORD group, SECONDARY_ORDER sec, SECONDARY_STATE state)
{
DROID *psCurr;
SECONDARY_STATE currState;
for(psCurr = apsDroidLists[player]; psCurr; psCurr=psCurr->psNext)
{
if (psCurr->group == group &&
secondaryGetState(psCurr, sec, &currState) && (currState != state))
{
secondarySetState(psCurr, sec, state);
}
}
}
// get the average secondary state of a numeric group
static SECONDARY_STATE secondaryGetAverageGroupState(UDWORD player, UDWORD group, UDWORD mask)
{
#define MAX_STATES 5
struct { UDWORD state, num; } aStateCount[MAX_STATES];
SDWORD i, numStates, max;
DROID *psCurr;
// count the number of units for each state
numStates = 0;
memset(aStateCount, 0, sizeof(aStateCount));
for(psCurr=apsDroidLists[player]; psCurr; psCurr=psCurr->psNext)
{
if (psCurr->group == group)
{
for (i=0; i<numStates; i++)
{
if (aStateCount[i].state == (psCurr->secondaryOrder & mask))
{
aStateCount[i].num += 1;
break;
}
}
if (i == numStates)
{
aStateCount[numStates].state = psCurr->secondaryOrder & mask;
aStateCount[numStates].num = 1;
numStates += 1;
}
}
}
max = 0;
for (i=0; i<numStates; i++)
{
if (aStateCount[i].num > aStateCount[max].num)
{
max = i;
}
}
return aStateCount[max].state;
}
// make all the members of a numeric group have the same secondary states
void secondarySetAverageGroupState(UDWORD player, UDWORD group)
{
// lookup table for orders and masks
#define MAX_ORDERS 4
struct { UDWORD order, mask; } aOrders[MAX_ORDERS] =
{
{ DSO_ATTACK_RANGE, DSS_ARANGE_MASK },
{ DSO_REPAIR_LEVEL, DSS_REPLEV_MASK },
{ DSO_ATTACK_LEVEL, DSS_ALEV_MASK },
{ DSO_HALTTYPE, DSS_HALT_MASK }
};
SDWORD i, state;
for(i=0; i<MAX_ORDERS; i++)
{
state = secondaryGetAverageGroupState(player, group, aOrders[i].mask);
secondarySetGroupState(player, group, aOrders[i].order, state);
}
}
// do a moral check for a player
void orderMoralCheck(UDWORD player)
{
DROID *psCurr;
SDWORD units, numVehicles, leadership, personLShip, check;
// count the number of vehicles and units on the side
units=0;
numVehicles = 0;
for(psCurr=apsDroidLists[player]; psCurr; psCurr=psCurr->psNext)
{
units += 1;
if (psCurr->droidType != DROID_PERSON)
{
numVehicles += 1;
}
}
if (units > asRunData[player].forceLevel)
{
// too many units, don't run
return;
}
// debug( LOG_NEVER, "moral check for player %d\n", player );
// calculate the overall leadership
leadership = asRunData[player].leadership + 10;
personLShip = asRunData[player].leadership + numVehicles * 3;
// do the moral check for each droid
for(psCurr = apsDroidLists[player]; psCurr; psCurr=psCurr->psNext)
{
if (orderState(psCurr, DORDER_RUN) ||
orderState(psCurr, DORDER_RUNBURN) ||
orderState(psCurr, DORDER_RETREAT) ||
orderState(psCurr, DORDER_RTB) ||
orderState(psCurr, DORDER_RTR) ||
orderState(psCurr, DORDER_DESTRUCT))
{
// already running - ignore
continue;
}
check = rand() % 100;
if (psCurr->droidType == DROID_PERSON)
{
if (check > personLShip)
{
// debug( LOG_NEVER, " DORDER_RUN: droid %d\n", psCurr->id );
orderDroid(psCurr, DORDER_RUN);
}
}
else
{
if (check > leadership)
{
// debug( LOG_NEVER, " DORDER_RUN: droid %d\n", psCurr->id );
orderDroid(psCurr, DORDER_RUN);
}
}
}
}
// do a moral check for a group
void orderGroupMoralCheck(DROID_GROUP *psGroup)
{
DROID *psCurr;
SDWORD units, numVehicles, leadership, personLShip, check;
RUN_DATA *psRunData;
// count the number of vehicles and units on the side
units=0;
numVehicles = 0;
for(psCurr=psGroup->psList; psCurr; psCurr=psCurr->psGrpNext)
{
units += 1;
if (psCurr->droidType != DROID_PERSON)
{
numVehicles += 1;
}
}
psRunData = &psGroup->sRunData;
if (units > psRunData->forceLevel)
{
// too many units, don't run
return;
}
// calculate the overall leadership
leadership = psRunData->leadership + 10;
personLShip = psRunData->leadership + numVehicles * 3;
// do the moral check for each droid
for(psCurr = psGroup->psList; psCurr; psCurr=psCurr->psGrpNext)
{
if (orderState(psCurr, DORDER_RUN) ||
orderState(psCurr, DORDER_RUNBURN) ||
orderState(psCurr, DORDER_RETREAT) ||
orderState(psCurr, DORDER_RTB) ||
orderState(psCurr, DORDER_RTR) ||
orderState(psCurr, DORDER_DESTRUCT))
{
// already running - ignore
continue;
}
check = rand() % 100;
if (psCurr->droidType == DROID_PERSON)
{
if (check > personLShip)
{
// debug( LOG_NEVER, " DORDER_RUN: droid %d\n", psCurr->id );
orderDroidLoc(psCurr, DORDER_RUN, psRunData->sPos.x, psRunData->sPos.y);
}
}
else
{
if (check > leadership)
{
// debug( LOG_NEVER, " DORDER_RUN: droid %d\n", psCurr->id );
orderDroidLoc(psCurr, DORDER_RUN, psRunData->sPos.x, psRunData->sPos.y);
}
}
}
}
// do a health check for a droid
void orderHealthCheck(DROID *psDroid)
{
DROID *psCurr;
SBYTE healthLevel = 0;
UDWORD retreatX = 0, retreatY = 0;
if (psDroid->droidType == DROID_TRANSPORTER)
{
return;
}
//get the health value to compare with
if (psDroid->psGroup)
{
healthLevel = psDroid->psGroup->sRunData.healthLevel;
retreatX = psDroid->psGroup->sRunData.sPos.x;
retreatY = psDroid->psGroup->sRunData.sPos.y;
}
//if health has not been set for the group - use players'
if (!healthLevel)
{
healthLevel = asRunData[psDroid->player].healthLevel;
}
//if not got a health level set then ignore
if (!healthLevel)
{
return;
}
//if pos has not been set for the group - use players'
if (retreatX == 0 && retreatY == 0)
{
retreatX = asRunData[psDroid->player].sPos.x;
retreatY = asRunData[psDroid->player].sPos.y;
}
if (PERCENT(psDroid->body, psDroid->originalBody) < healthLevel)
{
//order this droid to turn and run - // if already running - ignore
if (!(orderState(psDroid, DORDER_RUN) ||
orderState(psDroid, DORDER_RUNBURN) ||
orderState(psDroid, DORDER_RETREAT) ||
orderState(psDroid, DORDER_RTB) ||
orderState(psDroid, DORDER_RTR) ||
orderState(psDroid, DORDER_DESTRUCT)))
{
// debug( LOG_NEVER, " DORDER_RUN: droid %d\n", psDroid->id );
orderDroidLoc(psDroid, DORDER_RUN, retreatX, retreatY);
}
// order each unit in the same group to run
if (psDroid->psGroup)
{
for(psCurr = psDroid->psGroup->psList; psCurr; psCurr=psCurr->psGrpNext)
{
if (orderState(psCurr, DORDER_RUN) ||
orderState(psCurr, DORDER_RUNBURN) ||
orderState(psCurr, DORDER_RETREAT) ||
orderState(psCurr, DORDER_RTB) ||
orderState(psCurr, DORDER_RTR) ||
orderState(psCurr, DORDER_DESTRUCT))
{
// already running - ignore
continue;
}
// debug( LOG_NEVER, " DORDER_RUN: droid %d\n", psCurr->id );
orderDroidLoc(psCurr, DORDER_RUN, retreatX, retreatY);
}
}
}
}
// set the state of a secondary order for a Factory, return false if failed.
BOOL setFactoryState(STRUCTURE *psStruct, SECONDARY_ORDER sec, SECONDARY_STATE State)
{
UDWORD CurrState;
BOOL retVal;
FACTORY *psFactory;
if (!StructIsFactory(psStruct))
{
ASSERT( false, "setFactoryState: structure is not a factory" );
return false;
}
psFactory = (FACTORY *)psStruct->pFunctionality;
CurrState = psFactory->secondaryOrder;
retVal = true;
switch (sec) {
case DSO_ATTACK_RANGE:
CurrState = (CurrState & ~DSS_ARANGE_MASK) | State;
break;
case DSO_REPAIR_LEVEL:
CurrState = (CurrState & ~DSS_REPLEV_MASK) | State;
break;
case DSO_ATTACK_LEVEL:
CurrState = (CurrState & ~DSS_ALEV_MASK) | State;
break;
case DSO_PATROL:
if (State & DSS_PATROL_SET)
{
CurrState |= DSS_PATROL_SET;
}
else
{
CurrState &= ~DSS_PATROL_MASK;
}
break;
case DSO_HALTTYPE:
switch (State & DSS_HALT_MASK)
{
case DSS_HALT_PURSUE:
CurrState &= ~ DSS_HALT_MASK;
CurrState |= DSS_HALT_PURSUE;
break;
case DSS_HALT_GUARD:
CurrState &= ~ DSS_HALT_MASK;
CurrState |= DSS_HALT_GUARD;
break;
case DSS_HALT_HOLD:
CurrState &= ~ DSS_HALT_MASK;
CurrState |= DSS_HALT_HOLD;
break;
}
break;
default:
break;
}
psFactory->secondaryOrder = CurrState;
return retVal;
}
// get the state of a secondary order for a Factory, return false if unsupported
BOOL getFactoryState(STRUCTURE *psStruct, SECONDARY_ORDER sec, SECONDARY_STATE *pState)
{
UDWORD state;
if (!StructIsFactory(psStruct))
{
ASSERT( false, "getFactoryState: structure is not a factory" );
return false;
}
state = ((FACTORY *)psStruct->pFunctionality)->secondaryOrder;
switch (sec)
{
case DSO_ATTACK_RANGE:
*pState = (SECONDARY_STATE)(state & DSS_ARANGE_MASK);
break;
case DSO_REPAIR_LEVEL:
*pState = (SECONDARY_STATE)(state & DSS_REPLEV_MASK);
break;
case DSO_ATTACK_LEVEL:
*pState = (SECONDARY_STATE)(state & DSS_ALEV_MASK);
break;
case DSO_PATROL:
*pState = (SECONDARY_STATE)(state & DSS_PATROL_MASK);
break;
case DSO_HALTTYPE:
*pState = (SECONDARY_STATE)(state & DSS_HALT_MASK);
break;
default:
*pState = 0;
break;
}
return true;
}
//lasSat structure can select a target
void orderStructureObj(UDWORD player, BASE_OBJECT *psObj)
{
STRUCTURE *psStruct;
for (psStruct = apsStructLists[player]; psStruct; psStruct = psStruct->psNext)
{
if (lasSatStructSelected(psStruct))
{
// FIXME HACK Needed since we got those ugly Vector3uw floating around in BASE_OBJECT...
Vector3i pos = {psObj->pos.x, psObj->pos.y, psObj->pos.z};
// Lassats have just one weapon
unsigned int firePause = weaponFirePause(&asWeaponStats[psStruct->asWeaps[0].nStat], (UBYTE)player);
unsigned int damLevel = PERCENT(psStruct->body, structureBody(psStruct));
if (damLevel < HEAVY_DAMAGE_LEVEL)
{
firePause += firePause;
}
if (isHumanPlayer(player)
&& (gameTime - psStruct->asWeaps[0].lastFired <= firePause) )
{
/* Too soon to fire again */
break;
}
//ok to fire - so fire away
proj_SendProjectile(&psStruct->asWeaps[0], NULL, player, pos, psObj, true, 0);
//set up last fires time
psStruct->asWeaps[0].lastFired = gameTime;
//play 5 second countdown message
audio_QueueTrackPos( ID_SOUND_LAS_SAT_COUNTDOWN,
psObj->pos.x, psObj->pos.y, psObj->pos.z );
// send the weapon fire
if(bMultiPlayer)
{
sendLasSat(player,psStruct,psObj);
}
break;
}
}
}
const char* getDroidOrderName(DROID_ORDER order)
{
static const char* name[] =
{
"DORDER_NONE", // no order set
"DORDER_STOP", // stop the current order
"DORDER_MOVE", // 2 - move to a location
"DORDER_ATTACK", // attack an enemy
"DORDER_BUILD", // 4 - build a structure
"DORDER_HELPBUILD", // help to build a structure
"DORDER_LINEBUILD", // 6 - build a number of structures in a row (walls + bridges)
"DORDER_DEMOLISH", // demolish a structure
"DORDER_REPAIR", // 8 - repair a structure
"DORDER_OBSERVE", // keep a target in sensor view
"DORDER_FIRESUPPORT", // 10 - attack whatever the linked sensor droid attacks
"DORDER_RETREAT", // return to the players retreat position
"DORDER_DESTRUCT", // 12 - self destruct
"DORDER_RTB", // return to base
"DORDER_RTR", // 14 - return to repair at any repair facility
"DORDER_RUN", // run away after moral failure
"DORDER_EMBARK", // 16 - board a transporter
"DORDER_DISEMBARK", // get off a transporter
"DORDER_ATTACKTARGET", // 18 - a suggestion to attack something
// i.e. the target was chosen because the droid could see it
"DORDER_COMMAND", // a command droid issuing orders to it's group
"DORDER_BUILDMODULE", // 20 - build a module (power, research or factory)
"DORDER_RECYCLE", // return to factory to be recycled
"DORDER_TRANSPORTOUT", // 22 - offworld transporter order
"DORDER_TRANSPORTIN", // onworld transporter order
"DORDER_TRANSPORTRETURN", // 24 - transporter return after unloading
"DORDER_GUARD", // guard a structure
"DORDER_DROIDREPAIR", // 26 - repair a droid
"DORDER_RESTORE", // restore resistance points for a structure
"DORDER_SCOUT", // 28 - same as move, but stop if an enemy is seen
"DORDER_RUNBURN", // run away on fire
"DORDER_CLEARWRECK", // 30 - constructor droid to clear up building wreckage
"DORDER_PATROL", // move between two way points
"DORDER_REARM", // 32 - order a vtol to rearming pad
"DORDER_MOVE_ATTACKWALL", // move to a location taking out a blocking wall on the way
"DORDER_SCOUT_ATTACKWALL", // 34 - scout to a location taking out a blocking wall on the way
"DORDER_RECOVER", // pick up an artifact
"DORDER_LEAVEMAP", // 36 - vtol flying off the map
"DORDER_RTR_SPECIFIED", // return to repair at a specified repair center
"DORDER_UNDEFINED",
"DORDER_UNDEFINED2",
"DORDER_CIRCLE" // circles target location and engage
};
ASSERT(order < sizeof(name) / sizeof(name[0]), "DROID_ORDER out of range: %u", order);
return name[order];
}