-replaced some numbers scattered across the code with defines

-when looking for a new target droids/structures will now take into consideration targets of the friendly droids/structures nearby
-droids/structures are now constantly looking for the best target - will no longer lock on decoys/walls and other crappy targets forever
-ALT+Space now opens a special debug window - set window text with setDebugMenuEntry()
-minor cleanups

git-svn-id: svn+ssh://svn.gna.org/svn/warzone/trunk@905 4a71c877-e1ca-e34f-864e-861f7616d084
master
Roman C 2006-12-22 15:13:04 +00:00
parent 1cb603a445
commit bd2dd39e56
16 changed files with 435 additions and 217 deletions

View File

@ -1218,9 +1218,9 @@ static void actionUpdateTransporter( DROID *psDroid )
{
if (psDroid->asWeaps[i].nStat > 0)
{
if (psDroid->psActionTarget[0] == NULL)
if (psDroid->psActionTarget[0] == NULL && CAN_UPDATE_NAYBORS(psDroid))
{
aiBestNearestTarget(psDroid, &psDroid->psActionTarget[0], i);
(void)aiBestNearestTarget(psDroid, &psDroid->psActionTarget[0], i);
}
if ( psDroid->psActionTarget[0] != NULL )
@ -1461,7 +1461,7 @@ void actionUpdateDroid(DROID *psDroid)
UDWORD tlx,tly;
STRUCTURE *psStruct;
STRUCTURE_STATS *psStructStats;
BASE_OBJECT *psTarget;//, *psObj;
BASE_OBJECT *psTarget;
WEAPON_STATS *psWeapStats;
SDWORD targetDir, dirDiff, pbx,pby;
SDWORD xdiff,ydiff, rangeSq;
@ -1704,7 +1704,8 @@ void actionUpdateDroid(DROID *psDroid)
psDroid->asWeaps[i].nStat > 0 &&
psWeapStats->rotate &&
psWeapStats->fireOnMove != FOM_NO &&
aiBestNearestTarget(psDroid, &psDroid->psActionTarget[0], 0))
CAN_UPDATE_NAYBORS(psDroid) &&
(aiBestNearestTarget(psDroid, &psDroid->psActionTarget[0], i) >= 0))
{
if (secondaryGetState(psDroid, DSO_ATTACK_LEVEL, &state))
{
@ -1755,7 +1756,7 @@ void actionUpdateDroid(DROID *psDroid)
}
else
{
vtResult = 1;
vtResult = INVALID_TARGET;
}
// firing on something while moving
@ -1765,7 +1766,7 @@ void actionUpdateDroid(DROID *psDroid)
psDroid->action = DACTION_NONE;
}
else if ((psDroid->psActionTarget[0] == NULL) ||
(vtResult == 1) ||
(vtResult == INVALID_TARGET) ||
(secondaryGetState(psDroid, DSO_ATTACK_LEVEL, &state) && (state != DSS_ALEV_ALWAYS)))
{
// Target lost
@ -1788,6 +1789,7 @@ void actionUpdateDroid(DROID *psDroid)
}
else
{
/* Deal with target */
if (visibleObject((BASE_OBJECT*)psDroid, psDroid->psActionTarget[0]))
{
for(i = 0;i < psDroid->numWeaps;i++)
@ -1797,12 +1799,6 @@ void actionUpdateDroid(DROID *psDroid)
//Watermelon:to fix a AA-weapon attack ground unit exploit
if ( (num_weapons & (1 << (i+1))) && (vtResult & (1 << (i+1))) )
{
/*if (actionTargetTurret((BASE_OBJECT*)psDroid, psDroid->psActionTarget,
&(psDroid->turretRotation), &(psDroid->turretPitch),
psDroid->turretRotRate, (SWORD)(psDroid->turretRotRate/2),
//asWeaponStats[psDroid->asWeaps->nStat].direct))
proj_Direct(&asWeaponStats[psDroid->asWeaps->nStat]),
bInvert))*/
if (actionTargetTurret((BASE_OBJECT*)psDroid, psDroid->psActionTarget[0],
&(psDroid->turretRotation[i]), &(psDroid->turretPitch[i]),
&asWeaponStats[psDroid->asWeaps[i].nStat],
@ -1852,7 +1848,7 @@ void actionUpdateDroid(DROID *psDroid)
//check the target hasn't become one the same player ID - Electronic Warfare
if ((electronicDroid(psDroid) && (psDroid->player == psDroid->psActionTarget[0]->player)) ||
(vtResult == 1) )// ||
(vtResult == INVALID_TARGET) )// ||
// (secondaryGetState(psDroid, DSO_ATTACK_LEVEL, &state) && (state != DSS_ALEV_ALWAYS)))
{
psDroid->psActionTarget[0] = NULL;
@ -2048,7 +2044,7 @@ void actionUpdateDroid(DROID *psDroid)
}
else
{
vtResult = 1;
vtResult = INVALID_TARGET;
}
avtResult = actionVisibleTarget(psDroid, psDroid->psActionTarget[0]);
@ -2058,7 +2054,7 @@ void actionUpdateDroid(DROID *psDroid)
(psDroid->psActionTarget[0] == NULL) ||
//check the target hasn't become one the same player ID - Electronic Warfare
(electronicDroid(psDroid) && (psDroid->player == psDroid->psActionTarget[0]->player)) ||
(vtResult == 1) )// ||
(vtResult == INVALID_TARGET) )// ||
// (secondaryGetState(psDroid, DSO_ATTACK_LEVEL, &state) && (state != DSS_ALEV_ALWAYS)))
{
moveToRearm(psDroid);
@ -2172,7 +2168,7 @@ void actionUpdateDroid(DROID *psDroid)
//check the target hasn't become one the same player ID - Electronic Warfare
if ((electronicDroid(psDroid) && (psDroid->player == psDroid->psActionTarget[0]->player)) ||
(vtResult == 1) )// ||
(vtResult == INVALID_TARGET) )// ||
// (secondaryGetState(psDroid, DSO_ATTACK_LEVEL, &state) && (state != DSS_ALEV_ALWAYS)))
{
for (i = 0;i < psDroid->numWeaps;i++)

View File

@ -130,6 +130,21 @@ extern BOOL actionRouteBlockingPos(DROID *psDroid, SDWORD x, SDWORD y);
// choose a landing position for a VTOL when it goes to rearm
extern BOOL actionVTOLLandingPos(DROID *psDroid, UDWORD *px, UDWORD *py);
// Try to find a better target
extern BOOL updateAttackTarget(BASE_OBJECT * psAttacker, SDWORD weapon_slot);
//How many frames to skip before updating naybors
#define NAYBOR_SKIP_FRAMES 8
/* How many frames to skip before looking for a better target */
#define TARGET_UPD_SKIP_FRAMES 1000
#define INVALID_TARGET 1
/* Macro to check if it's time to update naybor list */
#define CAN_UPDATE_NAYBORS(object)\
(((object)->id % NAYBOR_SKIP_FRAMES) == (frameGetFrameNumber() % NAYBOR_SKIP_FRAMES))
#endif

467
src/ai.c
View File

@ -19,6 +19,9 @@
#include "projectile.h"
#include "visibility.h"
/* Calculates attack priority for a certain target */
SDWORD targetAttackWeight(BASE_OBJECT *psTarget, BASE_OBJECT *psAttacker, SDWORD weapon_slot);
// alliances
UBYTE alliances[MAX_PLAYERS][MAX_PLAYERS];
@ -69,36 +72,31 @@ BOOL aiShutdown(void)
}
// Find the best nearest target for a droid
BOOL aiBestNearestTarget(DROID *psDroid, BASE_OBJECT **ppsObj, int weapon_slot)
// Returns integer representing target priority, -1 if failed
SDWORD aiBestNearestTarget(DROID *psDroid, BASE_OBJECT **ppsObj, int weapon_slot)
{
UDWORD i;
SDWORD bestMod,newMod,damage,targetTypeBonus;
BASE_OBJECT *psTarget, *psObj, *bestTarget;
BOOL electronic = FALSE;
WEAPON_EFFECT weaponEffect;
UDWORD i;
SDWORD bestMod,newMod,failure=-1;
BASE_OBJECT *psTarget,*friendlyObj,*bestTarget,*targetInQuestion,*tempTarget;
BOOL electronic = FALSE;
STRUCTURE *targetStructure;
DROID *targetDroid;
WEAPON_EFFECT weaponEffect;
DROID *friendlyDroid;
if ((psDroid->id % 8) == (frameGetFrameNumber() % 8))
{
droidGetNaybors(psDroid);
}
else
{
return FALSE;
}
//don't bother looking if empty vtol droid
if (vtolEmpty(psDroid))
{
return FALSE;
return failure;
}
/* Return if have no weapons */
// Watermelon:added a protection against no weapon droid 'numWeaps'
// The ai orders a non-combat droid to patrol = crash without it...
if(psDroid->asWeaps[0].nStat == 0 || psDroid->numWeaps == 0)
return FALSE;
return failure;
droidGetNaybors(psDroid);
//weaponMod = asWeaponModifier[weaponEffect][(asPropulsionStats + ((DROID*)psObj)->asBits[COMP_PROPULSION].nStat)->propulsionType];
weaponEffect = ((WEAPON_STATS *)(asWeaponStats + psDroid->asWeaps[weapon_slot].nStat))->weaponEffect;
@ -110,161 +108,132 @@ BOOL aiBestNearestTarget(DROID *psDroid, BASE_OBJECT **ppsObj, int weapon_slot)
bestMod = 0;
for (i=0; i< numNaybors; i++)
{
if (asDroidNaybors[i].psObj->player != psDroid->player &&
(asDroidNaybors[i].psObj->type == OBJ_DROID ||
asDroidNaybors[i].psObj->type == OBJ_STRUCTURE) &&
asDroidNaybors[i].psObj->visible[psDroid->player] &&
!aiCheckAlliances(asDroidNaybors[i].psObj->player,psDroid->player))
friendlyObj = NULL;
targetInQuestion = asDroidNaybors[i].psObj;
/* This is a friendly unit, check if we can reuse its target */
if(targetInQuestion->player == psDroid->player ||
aiCheckAlliances(targetInQuestion->player,psDroid->player))
{
psObj = asDroidNaybors[i].psObj;
if ( validTarget((BASE_OBJECT *)psDroid, psObj) == 1)
friendlyObj = targetInQuestion;
targetInQuestion = NULL;
/* Can we see what it is doing? */
if(friendlyObj->visible[psDroid->player])
{
if(friendlyObj->type == OBJ_DROID)
{
friendlyDroid = ((DROID *)friendlyObj);
/* See if friendly droid has a target */
tempTarget = friendlyDroid->psActionTarget[0];
if(tempTarget != NULL)
{
//make sure a weapon droid is targeting it
if(friendlyDroid->numWeaps > 0)
{
// make sure this target wasn't assigned explicitly to this droid
if(friendlyDroid->order != DORDER_ATTACK)
{
//(WEAPON_STATS *)(asWeaponStats + ((DROID *)friendlyObj)->asWeaps[0].nStat)->;
// make sure target is near enough
if(dirtySqrt(psDroid->x,psDroid->y,tempTarget->x,tempTarget->y)
< (psDroid->sensorRange * 2))
{
targetInQuestion = tempTarget; //consider this target
}
}
}
}
}
else if(friendlyObj->type == OBJ_STRUCTURE)
{
targetInQuestion = ((STRUCTURE *)friendlyObj)->psTarget[0];
}
}
}
if (targetInQuestion != NULL &&
targetInQuestion != (BASE_OBJECT *)psDroid && //in case friendly unit had me as target
(targetInQuestion->type == OBJ_DROID ||
targetInQuestion->type == OBJ_STRUCTURE) &&
targetInQuestion->visible[psDroid->player] &&
targetInQuestion->player != psDroid->player &&
!aiCheckAlliances(targetInQuestion->player,psDroid->player))
{
if ( validTarget((BASE_OBJECT *)psDroid, targetInQuestion) == INVALID_TARGET)
{
continue;
}
else if (psObj->type == OBJ_DROID)
else if (targetInQuestion->type == OBJ_DROID)
{
//in multiPlayer - don't attack Transporters with EW
if (bMultiPlayer)
{
//if not electronic then valid target
if (!electronic OR (electronic AND ((DROID *)psObj)->
if (!electronic OR (electronic AND ((DROID *)targetInQuestion)->
droidType != DROID_TRANSPORTER))
{
//only a valid target if NOT a transporter
psTarget = psObj;
// break;
psTarget = targetInQuestion;
}
}
else
{
psTarget = psObj;
//break;
psTarget = targetInQuestion;
}
}
else if (psObj->type == OBJ_STRUCTURE)
else if (targetInQuestion->type == OBJ_STRUCTURE)
{
if (electronic)
{
/*don't want to target structures with resistance of zero if
using electronic warfare*/
// if (((STRUCTURE *)psObj)->pStructureType->resistance != 0)// AND
//((STRUCTURE *)psObj)->resistance >= (SDWORD)(((STRUCTURE *)
//psObj)->pStructureType->resistance))
//((STRUCTURE *)psObj)->resistance >= (SDWORD)
//structureResistance(((STRUCTURE *)psObj)->pStructureType,
//psObj->player))
if (validStructResistance((STRUCTURE *)psObj))
// if (((STRUCTURE *)targetInQuestion)->pStructureType->resistance != 0)// AND
//((STRUCTURE *)targetInQuestion)->resistance >= (SDWORD)(((STRUCTURE *)
//targetInQuestion)->pStructureType->resistance))
//((STRUCTURE *)targetInQuestion)->resistance >= (SDWORD)
//structureResistance(((STRUCTURE *)targetInQuestion)->pStructureType,
//targetInQuestion->player))
if (validStructResistance((STRUCTURE *)targetInQuestion))
{
psTarget = psObj;
//break;
psTarget = targetInQuestion;
}
}
//else if (((STRUCTURE *)psObj)->numWeaps > 0)
else if (((STRUCTURE *)psObj)->asWeaps[weapon_slot].nStat > 0)
//else if (((STRUCTURE *)targetInQuestion)->numWeaps > 0)
else if (((STRUCTURE *)targetInQuestion)->asWeaps[weapon_slot].nStat > 0)
{
// structure with weapons - go for this
psTarget = psObj;
//break;
psTarget = targetInQuestion;
}
else if ( ( ((STRUCTURE *)psObj)->pStructureType->type != REF_WALL
&&((STRUCTURE *)psObj)->pStructureType->type != REF_WALLCORNER
else if ( ( ((STRUCTURE *)targetInQuestion)->pStructureType->type != REF_WALL
&&((STRUCTURE *)targetInQuestion)->pStructureType->type != REF_WALLCORNER
)
|| driveModeActive()
|| (bMultiPlayer && game.type == SKIRMISH && !isHumanPlayer(psDroid->player))
)
{
psTarget = psObj;
psTarget = targetInQuestion;
}
}
/* Check if our weapon is most effective against this object */
if(psTarget != NULL && psTarget == psObj) //was assigned?
if(psTarget != NULL && psTarget == targetInQuestion) //was assigned?
{
targetTypeBonus = 0; //Sensors/ecm droids, non-military structures get lower priority
if(psTarget->type == OBJ_DROID)
{
targetDroid = (DROID *)psTarget;
/* Calculate damage this target suffered */
damage = targetDroid->originalBody - targetDroid->body;
/* See if this type of a droid should be prioritized */
switch (targetDroid->droidType)
{
case DROID_CYBORG:
case DROID_WEAPON:
case DROID_CYBORG_SUPER:
case DROID_COMMAND: //or should it get more priority?
targetTypeBonus = WEIGHT_WEAPON_DROIDS;
break;
case DROID_CONSTRUCT:
case DROID_REPAIR:
case DROID_CYBORG_CONSTRUCT:
case DROID_CYBORG_REPAIR:
targetTypeBonus = WEIGHT_SERVICE_DROIDS;
break;
}
/* Now calculate the overall weight */
newMod = asWeaponModifier[weaponEffect][(asPropulsionStats + targetDroid->asBits[COMP_PROPULSION].nStat)->propulsionType] //Weapon effect
- (WEIGHT_DIST_TILE_DROID * (dirtySqrt(psDroid->x, psDroid->y,targetDroid->x,targetDroid->y) >> TILE_SHIFT) ) //substract WEIGHT_DIST_TILE_DROID per tile, 128 world units in a tile
+ (damage * 10 / targetDroid->originalBody ) * WEIGHT_HEALTH_DROID //we prefer damaged droids
+ targetTypeBonus; //some droid types have higher priority
}
else if (psTarget->type == OBJ_STRUCTURE)
{
targetStructure = (STRUCTURE *)psTarget;
/* Calculate damage this target suffered */
damage = structureBody(targetStructure) - targetStructure->body;
/* See if this type of a structure should be prioritized */
switch(targetStructure->pStructureType->type)
{
case REF_DEFENSE:
targetTypeBonus = WEIGHT_WEAPON_STRUCT;
break;
case REF_FACTORY:
case REF_CYBORG_FACTORY:
case REF_REPAIR_FACILITY:
targetTypeBonus = WEIGHT_MILITARY_STRUCT;
break;
}
/* Now calculate the overall weight */
newMod = asStructStrengthModifier[weaponEffect][targetStructure->pStructureType->strength] //Weapon effect
- (WEIGHT_DIST_TILE_STRUCT * (dirtySqrt(psDroid->x, psDroid->y,targetStructure->x,targetStructure->y) >> TILE_SHIFT) ) //substract WEIGHT_DIST_TILE_STRUCT per tile, 128 world units in a tile
+ (damage * 10 / structureBody(targetStructure) ) * WEIGHT_HEALTH_STRUCT //we prefer damaged structures
+ targetTypeBonus; //some structure types have higher priority
/* Go for unfinished structures only if nothing else found (same for non-visible structures) */
if(targetStructure->status != SS_BUILT) //a decoy?
newMod /= WEIGHT_STRUCT_NOTBUILT_F;
}
else
{
continue;
}
/* We prefer objects we can see and can attack immediately */
if(!visibleObjWallBlock((BASE_OBJECT *)psDroid, psTarget))
{
newMod /= WEIGHT_NOT_VISIBLE_F;
}
newMod = targetAttackWeight(psTarget, (BASE_OBJECT *)psDroid, weapon_slot);
/* Remember this one if it's our best target so far */
if( newMod > bestMod || bestTarget == NULL)
if( newMod >= 0 && (newMod > bestMod || bestTarget == NULL))
{
bestMod = newMod;
bestTarget = psObj;
bestTarget = psTarget;
}
}
}
}
if (bestTarget)
@ -280,10 +249,118 @@ BOOL aiBestNearestTarget(DROID *psDroid, BASE_OBJECT **ppsObj, int weapon_slot)
}
*ppsObj = bestTarget;
return TRUE;
return bestMod;
}
return FALSE;
return failure;
}
/* Calculates attack priority for a certain target */
SDWORD targetAttackWeight(BASE_OBJECT *psTarget, BASE_OBJECT *psAttacker, SDWORD weapon_slot)
{
SDWORD targetTypeBonus,damage,attackWeight=0,noTarget=-1;
DROID *targetDroid;
STRUCTURE *targetStructure;
WEAPON_EFFECT weaponEffect;
if(psTarget == NULL || psAttacker == NULL){
return noTarget;
}
targetTypeBonus = 0; //Sensors/ecm droids, non-military structures get lower priority
/* Get attacker weapon effect */
if(psAttacker->type == OBJ_DROID){
weaponEffect = ((WEAPON_STATS *)(asWeaponStats + ((DROID *)psAttacker)->asWeaps[weapon_slot].nStat))->weaponEffect;
}else if(psAttacker->type == OBJ_STRUCTURE){
weaponEffect = ((WEAPON_STATS *)(asWeaponStats + ((STRUCTURE *)psAttacker)->asWeaps[weapon_slot].nStat))->weaponEffect;
}
else /* feature */
{
ASSERT(FALSE, "targetAttackWeight: Invalid attacker object type");
return noTarget;
}
/* Calculate attack weight */
if(psTarget->type == OBJ_DROID)
{
targetDroid = (DROID *)psTarget;
/* Calculate damage this target suffered */
damage = targetDroid->originalBody - targetDroid->body;
/* See if this type of a droid should be prioritized */
switch (targetDroid->droidType)
{
case DROID_CYBORG:
case DROID_WEAPON:
case DROID_CYBORG_SUPER:
case DROID_COMMAND: //or should it get more priority?
targetTypeBonus = WEIGHT_WEAPON_DROIDS;
break;
case DROID_CONSTRUCT:
case DROID_REPAIR:
case DROID_CYBORG_CONSTRUCT:
case DROID_CYBORG_REPAIR:
targetTypeBonus = WEIGHT_SERVICE_DROIDS;
break;
}
/* Now calculate the overall weight */
attackWeight = asWeaponModifier[weaponEffect][(asPropulsionStats + targetDroid->asBits[COMP_PROPULSION].nStat)->propulsionType] //Weapon effect
- (WEIGHT_DIST_TILE_DROID * (dirtySqrt(psAttacker->x, psAttacker->y,targetDroid->x,targetDroid->y) >> TILE_SHIFT) ) //substract WEIGHT_DIST_TILE_DROID per tile, 128 world units in a tile
+ (damage * 10 / targetDroid->originalBody ) * WEIGHT_HEALTH_DROID //we prefer damaged droids
+ targetTypeBonus; //some droid types have higher priority
}
else if(psTarget->type == OBJ_STRUCTURE)
{
targetStructure = (STRUCTURE *)psTarget;
/* Calculate damage this target suffered */
damage = structureBody(targetStructure) - targetStructure->body;
/* See if this type of a structure should be prioritized */
switch(targetStructure->pStructureType->type)
{
case REF_DEFENSE:
targetTypeBonus = WEIGHT_WEAPON_STRUCT;
break;
case REF_RESOURCE_EXTRACTOR:
targetTypeBonus = WEIGHT_DERRICK_STRUCT;
break;
case REF_FACTORY:
case REF_CYBORG_FACTORY:
case REF_REPAIR_FACILITY:
targetTypeBonus = WEIGHT_MILITARY_STRUCT;
break;
}
/* Now calculate the overall weight */
attackWeight = asStructStrengthModifier[weaponEffect][targetStructure->pStructureType->strength] //Weapon effect
- (WEIGHT_DIST_TILE_STRUCT * (dirtySqrt(psAttacker->x, psAttacker->y,targetStructure->x,targetStructure->y) >> TILE_SHIFT) ) //substract WEIGHT_DIST_TILE_STRUCT per tile, 128 world units in a tile
+ (damage * 10 / structureBody(targetStructure) ) * WEIGHT_HEALTH_STRUCT //we prefer damaged structures
+ targetTypeBonus; //some structure types have higher priority
/* Go for unfinished structures only if nothing else found (same for non-visible structures) */
if(targetStructure->status != SS_BUILT) //a decoy?
attackWeight /= WEIGHT_STRUCT_NOTBUILT_F;
}
else //a feature
{
return noTarget;
}
/* We prefer objects we can see and can attack immediately */
if(!visibleObjWallBlock((BASE_OBJECT *)psAttacker, psTarget))
{
attackWeight /= WEIGHT_NOT_VISIBLE_F;
}
return attackWeight;
}
@ -336,7 +413,7 @@ static BOOL aiObjIsWall(BASE_OBJECT *psObj)
/* See if there is a target in range */
// Watermelon:to accept another int value weapon_slot
BOOL aiChooseTarget(BASE_OBJECT *psObj,
BASE_OBJECT **ppsTarget,int weapon_slot)
BASE_OBJECT **ppsTarget,int weapon_slot, BOOL bUpdateTarget)
{
UDWORD radSquared;
// UDWORD player;
@ -348,6 +425,8 @@ BOOL aiChooseTarget(BASE_OBJECT *psObj,
DROID *psCommander;
BOOL bCommanderBlock;
UDWORD sensorRange;
SECONDARY_STATE state;
SDWORD curTargetWeight=-1,newTargetWeight;
//Watermelon:weapon_slot to store which turrent is InAttackRange
/* Get the sensor range */
@ -398,7 +477,19 @@ BOOL aiChooseTarget(BASE_OBJECT *psObj,
/* See if there is a something in range */
if (psObj->type == OBJ_DROID)
{
if (aiBestNearestTarget((DROID *)psObj, &psTarget, weapon_slot))
/* find a new target */
newTargetWeight = aiBestNearestTarget((DROID *)psObj, &psTarget, weapon_slot);
/* Calculate weight of the current target of updating */
if(bUpdateTarget){
curTargetWeight = targetAttackWeight(((DROID *)psObj)->psActionTarget[0], psObj, weapon_slot);
}
if (newTargetWeight >= 0 && //found a new target
(!bUpdateTarget || //choosing a new target, don't care if current one is better
(curTargetWeight <= 0) || //attacker had no valid target, use new one
(newTargetWeight > (curTargetWeight + OLD_TARGET_THRESHOLD)) //updating and new target is better
))
{
/*check its a valid target*/
//Watermelon:Greater than 1 for now
@ -407,7 +498,9 @@ BOOL aiChooseTarget(BASE_OBJECT *psObj,
/* See if in sensor range */
xdiff = psTarget->x - psObj->x;
ydiff = psTarget->y - psObj->y;
if (xdiff*xdiff + ydiff*ydiff < (SDWORD)radSquared)
if ((xdiff*xdiff + ydiff*ydiff < (SDWORD)radSquared) || //target is within our sensor range
(secondaryGetState((DROID *)psObj, DSO_HALTTYPE, &state) && //in case we got this target from a friendly unit see if can pursue it
(state != DSS_HALT_HOLD))) //make sure it's guard or pursue
{
*ppsTarget = psTarget;
return TRUE;
@ -473,7 +566,7 @@ BOOL aiChooseTarget(BASE_OBJECT *psObj,
{
/*check its a valid target*/
//Watermelon:Greater than 1 for now
if ( (validTarget(psObj, psCStruct->psTarget[0]) > 1) &&
if ( (validTarget(psObj, psCStruct->psTarget[0]) > INVALID_TARGET) &&
aiStructHasRange((STRUCTURE *)psObj, psCStruct->psTarget[0], weapon_slot))
{
xdiff = (SDWORD)psCStruct->psTarget[0]->x - (SDWORD)psObj->x;
@ -491,7 +584,7 @@ BOOL aiChooseTarget(BASE_OBJECT *psObj,
psCStruct->psTarget[0] != NULL)
{
/*check its a valid target*/
if ( (validTarget(psObj, psCStruct->psTarget[0]) > 1) &&
if ( (validTarget(psObj, psCStruct->psTarget[0]) > INVALID_TARGET) &&
aiStructHasRange((STRUCTURE *)psObj, psCStruct->psTarget[0], weapon_slot))
{
xdiff = (SDWORD)psCStruct->psTarget[0]->x - (SDWORD)psObj->x;
@ -524,7 +617,7 @@ BOOL aiChooseTarget(BASE_OBJECT *psObj,
{
/*check its a valid target*/
//Watermelon:Greater than 1 for now
if ( (validTarget(psObj, psCurr) > 1) &&
if ( (validTarget(psObj, psCurr) > INVALID_TARGET) &&
!aiObjIsWall(psCurr))
{
// See if in sensor range and visible
@ -650,9 +743,9 @@ BOOL aiChooseSensorTarget(BASE_OBJECT *psObj, BASE_OBJECT **ppsTarget)
}
/* See if there is a something in range */
if (psObj->type == OBJ_DROID)
if (psObj->type == OBJ_DROID && CAN_UPDATE_NAYBORS( (DROID *)psObj ))
{
if (aiBestNearestTarget((DROID *)psObj, &psTarget, 0))
if (aiBestNearestTarget((DROID *)psObj, &psTarget, 0) >= 0)
{
/* See if in sensor range */
xdiff = psTarget->x - psObj->x;
@ -761,17 +854,20 @@ void aiUpdateDroid(DROID *psDroid)
//Watermelon:psTarget Array
BASE_OBJECT *psTarget[DROID_MAXWEAPS];
SECONDARY_STATE state;
BOOL lookForTarget;
BOOL lookForTarget,updateTarget;
// BOOL bTemp;
ASSERT( PTRVALID(psDroid, sizeof(DROID)),
"updateUnitAI: invalid Unit pointer" );
lookForTarget = TRUE;
updateTarget = TRUE;
// don't look for a target if sulking
if (psDroid->action == DACTION_SULK)
{
lookForTarget = FALSE;
updateTarget = FALSE;
}
// don't look for a target if doing something else
if (!orderState(psDroid, DORDER_NONE) &&
@ -779,11 +875,32 @@ void aiUpdateDroid(DROID *psDroid)
{
lookForTarget = FALSE;
}
/* Only try to update target if already have some target */
if (psDroid->action != DACTION_ATTACK &&
psDroid->action != DACTION_ATTACK_M &&
psDroid->action != DACTION_MOVEFIRE &&
psDroid->action != DACTION_MOVETOATTACK &&
psDroid->action != DACTION_ROTATETOATTACK)
{
updateTarget = FALSE;
}
/* Don't update target if we are sent to attack and reached
attack destination (attacking our target) */
if((orderState(psDroid, DORDER_ATTACK) || orderState(psDroid, DORDER_ATTACK_M))
&& (psDroid->psActionTarget[0] == psDroid->psTarget[0]))
{
updateTarget = FALSE;
}
// don't look for a target if there are any queued orders
if (psDroid->listSize > 0)
{
lookForTarget = FALSE;
updateTarget = FALSE;
}
// horrible check to stop droids looking for a target if
// they would switch to the guard order in the order update loop
if ((psDroid->order == DORDER_NONE) &&
@ -793,6 +910,7 @@ void aiUpdateDroid(DROID *psDroid)
(state == DSS_HALT_GUARD))
{
lookForTarget = FALSE;
updateTarget = FALSE;
}
// don't allow units to start attacking if they will switch to guarding the commander
@ -800,14 +918,15 @@ void aiUpdateDroid(DROID *psDroid)
psDroid->psGroup->type == GT_COMMAND)
{
lookForTarget = FALSE;
updateTarget = FALSE;
}
if(bMultiPlayer && vtolDroid(psDroid) && isHumanPlayer(psDroid->player))
{
lookForTarget = FALSE;
updateTarget = FALSE;
}
// do not choose another target if doing anything while guarding
if (orderState(psDroid, DORDER_GUARD) &&
(psDroid->action != DACTION_NONE))
@ -823,14 +942,16 @@ void aiUpdateDroid(DROID *psDroid)
// do not look for a target if droid is currently under direct control.
if(driveModeActive() && (psDroid == driveGetDriven())) {
lookForTarget = FALSE;
updateTarget = FALSE;
}
// only computer senosr droids in the single player game aquire targets
// only computer sensor droids in the single player game aquire targets
if ((psDroid->droidType == DROID_SENSOR && psDroid->player == selectedPlayer)
&& !bMultiPlayer
)
{
lookForTarget = FALSE;
updateTarget = FALSE;
}
// do not attack if the attack level is wrong
@ -842,10 +963,32 @@ void aiUpdateDroid(DROID *psDroid)
}
}
/* Null target - see if there is an enemy to attack */
if (lookForTarget &&
aiChooseTarget((BASE_OBJECT *)psDroid, &psTarget[0], 0))
/* Don't rebuild 'Naybor' list too often */
if(!CAN_UPDATE_NAYBORS(psDroid))
{
lookForTarget = FALSE;
}
/* For commanders and non-assigned non-commanders:
look for a better target once in a while */
if(!lookForTarget && updateTarget)
{
if((psDroid->numWeaps > 0) && ((psDroid->droidType == DROID_COMMAND) ||
!(psDroid->psGroup && (psDroid->psGroup->type == GT_COMMAND)))) //not assigned to commander
{
if((psDroid->id % TARGET_UPD_SKIP_FRAMES) ==
(frameGetFrameNumber() % TARGET_UPD_SKIP_FRAMES))
{
(void)updateAttackTarget((BASE_OBJECT*)psDroid, 0);
}
}
}
/* Null target - see if there is an enemy to attack */
if (lookForTarget && !updateTarget &&
aiChooseTarget((BASE_OBJECT *)psDroid, &psTarget[0], 0, FALSE))
{
//console("Choosing first-time target");
// my_error("",0,"","Droid(%s) attacking : %d\n",
// psDroid->pName, psTarget->id );
@ -871,7 +1014,7 @@ can fire on the propulsion type of the target*/
int validTarget(BASE_OBJECT *psObject, BASE_OBJECT *psTarget)
{
//Watermelon:return value int
int ValidTarget = 1;
int ValidTarget = INVALID_TARGET;
int i;
BOOL bTargetInAir;
//BOOL bTargetInAir, bValidTarget;
@ -975,3 +1118,43 @@ int validTarget(BASE_OBJECT *psObject, BASE_OBJECT *psTarget)
return ValidTarget;
}
/* Make droid/structure look for a better target */
BOOL updateAttackTarget(BASE_OBJECT * psAttacker, SDWORD weapon_slot)
{
BASE_OBJECT *psBetterTarget=NULL;
DROID *psDroid;
psBetterTarget = NULL;
if(aiChooseTarget(psAttacker, &psBetterTarget, weapon_slot, TRUE)) //update target
{
//if(psAttacker->player == selectedPlayer)
// console("GOT A NEW TARGET");
if(psAttacker->type == OBJ_DROID)
{
psDroid = (DROID *)psAttacker;
if( orderState(psDroid, DORDER_NONE) ||
orderState(psDroid, DORDER_GUARD) ||
orderState(psDroid, DORDER_ATTACKTARGET))
{
orderDroidObj((DROID *)psAttacker, DORDER_ATTACKTARGET, psBetterTarget);
}
else //can't override current order
{
//psDroid->action = DACTION_MOVEFIRE;
psDroid->psActionTarget[0] = psBetterTarget;
}
}
else if (psAttacker->type == OBJ_STRUCTURE)
{
((STRUCTURE *)psAttacker)->psTarget[weapon_slot] = psBetterTarget;
}
return TRUE;
}
return FALSE;
}

View File

@ -20,22 +20,27 @@
#define ALLIANCES_TEAMS 2 //locked teams
//#define GROUP_WINS 2
/* Weights used for target selection code */
#define WEIGHT_DIST_TILE 11 //In points used in weaponmodifier.txt and structuremodifier.txt
#define WEIGHT_DIST_TILE_DROID WEIGHT_DIST_TILE //How much weight a distance of 1 tile (128 world units) has when looking for the best nearest target
#define WEIGHT_DIST_TILE_STRUCT WEIGHT_DIST_TILE
#define WEIGHT_HEALTH_DROID WEIGHT_DIST_TILE //How much weight unit damage has (per 10% of damage, ie for each 10% of damage we add WEIGHT_HEALTH_DROID)
//~100% damage should be ~8 tiles (max sensor range)
#define WEIGHT_HEALTH_STRUCT WEIGHT_DIST_TILE
/* Weights used for target selection code,
* target distance is used as 'common currency'
*/
#define WEIGHT_DIST_TILE 11 //In points used in weaponmodifier.txt and structuremodifier.txt
#define WEIGHT_DIST_TILE_DROID WEIGHT_DIST_TILE //How much weight a distance of 1 tile (128 world units) has when looking for the best nearest target
#define WEIGHT_DIST_TILE_STRUCT WEIGHT_DIST_TILE
#define WEIGHT_HEALTH_DROID WEIGHT_DIST_TILE //How much weight unit damage has (per 10% of damage, ie for each 10% of damage we add WEIGHT_HEALTH_DROID)
//~100% damage should be ~8 tiles (max sensor range)
#define WEIGHT_HEALTH_STRUCT WEIGHT_DIST_TILE
#define WEIGHT_NOT_VISIBLE_F 10 //We really don't like objects we can't see
#define WEIGHT_NOT_VISIBLE_F 10 //We really don't like objects we can't see
#define WEIGHT_SERVICE_DROIDS (WEIGHT_DIST_TILE_DROID * 5) //We don't want them to be repairing droids or structures while we are after them
#define WEIGHT_WEAPON_DROIDS (WEIGHT_DIST_TILE_DROID * 3) //We prefer to go after anything that has a gun and can hurt us
#define WEIGHT_MILITARY_STRUCT WEIGHT_DIST_TILE_STRUCT //Droid/cyborg factories, repair facility; shouldn't have too much weight
#define WEIGHT_WEAPON_STRUCT WEIGHT_WEAPON_DROIDS //Same as weapon droids (?)
#define WEIGHT_SERVICE_DROIDS (WEIGHT_DIST_TILE_DROID * 5) //We don't want them to be repairing droids or structures while we are after them
#define WEIGHT_WEAPON_DROIDS (WEIGHT_DIST_TILE_DROID * 3) //We prefer to go after anything that has a gun and can hurt us
#define WEIGHT_MILITARY_STRUCT WEIGHT_DIST_TILE_STRUCT //Droid/cyborg factories, repair facility; shouldn't have too much weight
#define WEIGHT_WEAPON_STRUCT WEIGHT_WEAPON_DROIDS //Same as weapon droids (?)
#define WEIGHT_DERRICK_STRUCT (WEIGHT_WEAPON_STRUCT + WEIGHT_DIST_TILE_STRUCT * 4) //Even if it's 4 tiles further away than defenses we still choose it
#define WEIGHT_STRUCT_NOTBUILT_F 8 //Humans won't fool us anymore!
#define WEIGHT_STRUCT_NOTBUILT_F 8 //Humans won't fool us anymore!
#define OLD_TARGET_THRESHOLD (WEIGHT_DIST_TILE * 4) //it only makes sense to switch target if new one is 4+ tiles closer
// alliances
extern UBYTE alliances[MAX_PLAYERS][MAX_PLAYERS];
@ -55,12 +60,13 @@ extern BOOL aiInitDroid(DROID *psDroid);
/* Do the AI for a droid */
extern void aiUpdateDroid(DROID *psDroid);
// Find the nearest target to a droid added int weapon_slot
extern BOOL aiBestNearestTarget(DROID *psDroid, BASE_OBJECT **ppsObj, int weapon_slot);
// Find the nearest best target for a droid
// returns integer representing quality of choice, -1 if failed
extern SDWORD aiBestNearestTarget(DROID *psDroid, BASE_OBJECT **ppsObj, int weapon_slot);
/* See if there is a target in range added int weapon_slot*/
extern BOOL aiChooseTarget(BASE_OBJECT *psObj,
BASE_OBJECT **ppsTarget, int weapon_slot);
BASE_OBJECT **ppsTarget, int weapon_slot, BOOL bUpdateTarget);
/*set the droid to attack if wihin range otherwise move to target*/
extern void attackTarget(DROID *psDroid, BASE_OBJECT *psTarget);

View File

@ -129,7 +129,7 @@ void combFire(WEAPON *psWeap, BASE_OBJECT *psAttacker, BASE_OBJECT *psTarget, in
psStats = asWeaponStats + psWeap->nStat;
//check valid weapon/prop combination
if (validTarget(psAttacker, psTarget) == 1)
if (validTarget(psAttacker, psTarget) == INVALID_TARGET)
{
return;
}

View File

@ -208,18 +208,13 @@ BOOL droidDamage(DROID *psDroid, UDWORD damage, UDWORD weaponClass, UDWORD weapo
//TOP
if (angle == HIT_ANGLE_TOP)
{
impact_side = HIT_SIDE_TOP; //4
impact_side = HIT_SIDE_TOP; //4
}
//BOTTOM
else if (angle == HIT_ANGLE_BOTTOM)
{
impact_side = HIT_SIDE_BOTTOM; //5
}
//FRONT
else if (angle <= 45 || angle >= 315)
{
impact_side = HIT_SIDE_FRONT; //0
}
//RIGHT
else if (angle > 45 && angle < 135)
{
@ -235,6 +230,11 @@ BOOL droidDamage(DROID *psDroid, UDWORD damage, UDWORD weaponClass, UDWORD weapo
{
impact_side = HIT_SIDE_LEFT; //2
}
//FRONT - default
else //if (angle <= 45 || angle >= 315)
{
impact_side = HIT_SIDE_FRONT; //0
}
debug( LOG_ATTACK, "unitDamage(%d): body %d armour %d damage: %d\n",
psDroid->id, psDroid->body, psDroid->armour[impact_side][WC_KINETIC], damage);

View File

@ -31,7 +31,7 @@ extern DROID_TEMPLATE *apsDroidTemplates[MAX_PLAYERS];
/* The range for neighbouring objects */
#define NAYBOR_RANGE (TILE_UNITS*8)
#define NAYBOR_RANGE (TILE_UNITS*9) //range of lancer, BB, TK etc
//used to stop structures being built too near the edge and droids being placed down - pickATile
#define TOO_NEAR_EDGE 3
@ -52,7 +52,7 @@ typedef enum
} PICKTILE;
/* Store for the objects near the droid currently being updated */
#define MAX_NAYBORS 100
#define MAX_NAYBORS 120
extern NAYBOR_INFO asDroidNaybors[MAX_NAYBORS];
extern UDWORD numNaybors;

View File

@ -632,6 +632,12 @@ void kf_ToggleCamera( void )
camToggleStatus();
}
/* Toggle 'watch' window on/off */
void kf_ToggleWatchWindow( void )
{
(void)addDebugMenu(!DebugMenuUp);
}
// --------------------------------------------------------------------------
/* Simulates a close down */

View File

@ -207,6 +207,8 @@ extern void kf_NormalSpeed( void );
void kf_ToggleRadarTerrain( void ); //radar terrain
void kf_ToggleRadarAllyEnemy( void ); //enemy/ally color toggle
extern void kf_ToggleWatchWindow( void );
extern int fogCol;
#endif

View File

@ -158,7 +158,6 @@ _keymapsave keyMapSaveTable[] =
kf_SelectAllUnits,
kf_SelectAllVTOLs,
kf_SelectAllWheeled,
kf_FinishResearch,
kf_FrameRate,
kf_SelectAllSameType,
kf_SelectNextFactory,
@ -224,6 +223,7 @@ _keymapsave keyMapSaveTable[] =
kf_DownDroidScale,
kf_RaiseGamma,
kf_LowerGamma,
kf_ToggleWatchWindow,
NULL // last function!
};
@ -424,6 +424,7 @@ void keyInitMappings( BOOL bForceDefaults )
keyAddMapping(KEYMAP__DEBUG,KEY_LCTRL,KEY_G,KEYMAP_PRESSED,kf_ToggleGodMode, "Toggle god Mode Status");
keyAddMapping(KEYMAP__DEBUG,KEY_LCTRL,KEY_O,KEYMAP_PRESSED,kf_ChooseOptions, "Display Options Screen");
keyAddMapping(KEYMAP__DEBUG,KEY_LCTRL,KEY_X,KEYMAP_PRESSED,kf_FinishResearch, "Complete current research");
keyAddMapping(KEYMAP__DEBUG,KEY_LALT,KEY_SPACE,KEYMAP_PRESSED,kf_ToggleWatchWindow, "Toggle watch window");
saveKeyMap(); // save out the default key mappings.

View File

@ -1120,9 +1120,31 @@ void addMultiPlayer(UDWORD player,UDWORD pos)
}
}
/* Output some text to the debug menu */
void setDebugMenuEntry(char *entry, SDWORD index)
{
BOOL bAddingNew = FALSE;
/* New one? */
if(!strcmp(debugMenuEntry[index],""))
{
bAddingNew = TRUE;
}
/* Set */
strcpy(debugMenuEntry[index], entry);
/* Re-open it if already open to recalculate height */
if(DebugMenuUp && bAddingNew)
{
intCloseDebugMenuNoAnim();
(void)addDebugMenu(TRUE);
}
}
void intCloseDebugMenuNoAnim(void)
{
widgDelete(psWScreen, DEBUGMENU_CLOSE);
//widgDelete(psWScreen, DEBUGMENU_CLOSE);
widgDelete(psWScreen, DEBUGMENU);
DebugMenuUp = FALSE;
//intMode = INT_NORMAL;
@ -1192,7 +1214,7 @@ BOOL addDebugMenu(BOOL bAdd)
}
// Add the close button.
memset(&sButInit, 0, sizeof(W_BUTINIT));
/* memset(&sButInit, 0, sizeof(W_BUTINIT));
sButInit.formID = DEBUGMENU;
sButInit.id = DEBUGMENU_CLOSE;
sButInit.style = WBUT_PLAIN;
@ -1207,7 +1229,7 @@ BOOL addDebugMenu(BOOL bAdd)
if (!widgAddButton(psWScreen, &sButInit))
{
return FALSE;
}
} */
DebugMenuUp = TRUE;

View File

@ -23,6 +23,7 @@ extern BOOL intAddMultiMenu (void);
extern BOOL addDebugMenu (BOOL bAdd);
extern void intCloseDebugMenuNoAnim (void);
extern void setDebugMenuEntry(char *entry, SDWORD index);
extern BOOL MultiMenuUp;
extern BOOL ClosingMultiMenu;
@ -31,13 +32,13 @@ extern BOOL DebugMenuUp;
//extern void intDisplayMiniMultiMenu (void);
#define MULTIMENU 10600
#define MULTIMENU_FORM MULTIMENU
#define MULTIMENU 10600
#define MULTIMENU_FORM MULTIMENU
#define DEBUGMENU 106000
#define DEBUGMENU_CLOSE (DEBUGMENU+1)
#define DEBUGMENU_CLOSE (DEBUGMENU+1)
#define DEBUGMENU_MAX_ENTRIES 10
#define DEBUGMENU_BUTTON (DEBUGMENU_CLOSE + DEBUGMENU_MAX_ENTRIES)
#define DEBUGMENU_BUTTON (DEBUGMENU_CLOSE + DEBUGMENU_MAX_ENTRIES)
extern char debugMenuEntry[DEBUGMENU_MAX_ENTRIES][MAX_STR_LENGTH];

View File

@ -101,6 +101,7 @@ void initRunData(void)
}
}
//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
void orderCheckGuardPosition(DROID *psDroid, SDWORD range)
{
@ -580,9 +581,9 @@ if(!bMultiPlayer || myResponsibility(psDroid->player))
case DORDER_SCOUT:
case DORDER_PATROL:
// if there is an enemy around, attack it
if ( (psDroid->action == DACTION_MOVE) &&
(secondaryGetState(psDroid, DSO_ATTACK_LEVEL, &state) && (state == DSS_ALEV_ALWAYS)) &&
aiBestNearestTarget(psDroid, &psObj, 0) )
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)
{
@ -643,11 +644,11 @@ if(!bMultiPlayer || myResponsibility(psDroid->player))
}
}
break;
case DORDER_CIRCLE:
case DORDER_CIRCLE:
// if there is an enemy around, attack it
if ( (psDroid->action == DACTION_MOVE) &&
(secondaryGetState(psDroid, DSO_ATTACK_LEVEL, &state) && (state == DSS_ALEV_ALWAYS)) &&
aiBestNearestTarget(psDroid, &psObj, 0) )
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)
{
@ -3249,7 +3250,7 @@ DROID_ORDER chooseOrderObj(DROID *psDroid, BASE_OBJECT *psObj)
&& !aiCheckAlliances(psObj->player , psDroid->player) )
{
//check valid weapon/prop combination
if (validTarget((BASE_OBJECT *)psDroid, psObj) == 1)
if (validTarget((BASE_OBJECT *)psDroid, psObj) == INVALID_TARGET)
{
order = DORDER_NONE;
}

View File

@ -1690,7 +1690,7 @@ BOOL scrSkVtolEnableCheck(void)
// ********************************************************************************************
BOOL scrSkGetFactoryCapacity(void)
{
SDWORD count=0,structure;
SDWORD count=0;
STRUCTURE *psStructure;
if (!stackPopParams(1,ST_STRUCTURE, &psStructure))

View File

@ -10590,7 +10590,6 @@ BOOL scrDebugMenu(void)
BOOL scrSetDebugMenuEntry(void)
{
SDWORD index;
BOOL bAddingNew = FALSE;
if (!stackPopParams(2, VAL_STRING, &strParam1, VAL_INT, &index))
{
@ -10598,21 +10597,7 @@ BOOL scrSetDebugMenuEntry(void)
return FALSE;
}
/* New one? */
if(!strcmp(debugMenuEntry[index],""))
{
bAddingNew = TRUE;
}
/* Set */
strcpy(debugMenuEntry[index], strParam1);
/* Re-open it if already open to recalculate height */
if(DebugMenuUp && bAddingNew)
{
intCloseDebugMenuNoAnim();
(void)addDebugMenu(TRUE);
}
setDebugMenuEntry(strParam1, index);
return TRUE;
}

View File

@ -3995,7 +3995,7 @@ static void aiUpdateStructure(STRUCTURE *psStructure)
{
if ((psStructure->id % 20) == (frameGetFrameNumber() % 20))
{
if (aiChooseTarget((BASE_OBJECT *)psStructure, &psChosenObj, i))
if (aiChooseTarget((BASE_OBJECT *)psStructure, &psChosenObj, i, TRUE)) //structures always update their targets
{
debug( LOG_ATTACK, "Struct(%d) attacking : %d\n",
psStructure->id, psChosenObj->id );