4288 lines
116 KiB
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];
|
|
}
|