44
44
UBYTE alliances[MAX_PLAYERS][MAX_PLAYERS];
47
// see if a structure has the range to fire on a target
48
static BOOL aiStructHasRange(STRUCTURE *psStruct, BASE_OBJECT *psTarget, int weapon_slot)
50
WEAPON_STATS *psWStats;
51
SDWORD xdiff,ydiff, longRange;
53
if (psStruct->numWeaps == 0 || psStruct->asWeaps[0].nStat == 0)
55
// Can't attack without a weapon
59
psWStats = psStruct->asWeaps[weapon_slot].nStat + asWeaponStats;
61
xdiff = (SDWORD)psStruct->pos.x - (SDWORD)psTarget->pos.x;
62
ydiff = (SDWORD)psStruct->pos.y - (SDWORD)psTarget->pos.y;
63
longRange = proj_GetLongRange(psWStats);
64
if (xdiff*xdiff + ydiff*ydiff < longRange*longRange)
74
static BOOL aiDroidHasRange(DROID *psDroid, BASE_OBJECT *psTarget, int weapon_slot)
76
WEAPON_STATS *psWStats;
77
SDWORD xdiff, ydiff, longRange;
79
if (psDroid->numWeaps == 0 || psDroid->asWeaps[0].nStat == 0)
81
// Can't attack without a weapon
85
psWStats = psDroid->asWeaps[weapon_slot].nStat + asWeaponStats;
87
xdiff = (SDWORD)psDroid->pos.x - (SDWORD)psTarget->pos.x;
88
ydiff = (SDWORD)psDroid->pos.y - (SDWORD)psTarget->pos.y;
89
longRange = proj_GetLongRange(psWStats);
90
if (xdiff*xdiff + ydiff*ydiff < longRange*longRange)
46
100
/* alliance code for ai. return true if an alliance has formed. */
47
101
BOOL aiCheckAlliances(UDWORD s1,UDWORD s2)
170
if (targetInQuestion != NULL &&
171
targetInQuestion != (BASE_OBJECT *)psDroid && //in case friendly unit had me as target
172
(targetInQuestion->type == OBJ_DROID ||
173
targetInQuestion->type == OBJ_STRUCTURE) &&
174
targetInQuestion->visible[psDroid->player] &&
175
targetInQuestion->player != psDroid->player &&
176
!aiCheckAlliances(targetInQuestion->player,psDroid->player))
223
if (targetInQuestion != NULL
224
&& targetInQuestion != (BASE_OBJECT *)psDroid // in case friendly unit had me as target
225
&& (targetInQuestion->type == OBJ_DROID || targetInQuestion->type == OBJ_STRUCTURE)
226
&& targetInQuestion->visible[psDroid->player]
227
&& targetInQuestion->player != psDroid->player
228
&& !aiCheckAlliances(targetInQuestion->player,psDroid->player)
229
&& validTarget((BASE_OBJECT *)psDroid, targetInQuestion, weapon_slot)
230
&& aiDroidHasRange(psDroid, targetInQuestion, weapon_slot))
179
if ( !validTarget((BASE_OBJECT *)psDroid, targetInQuestion, weapon_slot) )
183
else if (targetInQuestion->type == OBJ_DROID)
232
if (targetInQuestion->type == OBJ_DROID)
185
234
// in multiPlayer - don't attack Transporters with EW
186
235
if (bMultiPlayer)
507
// see if a structure has the range to fire on a target
508
static BOOL aiStructHasRange(STRUCTURE *psStruct, BASE_OBJECT *psTarget, int weapon_slot)
510
WEAPON_STATS *psWStats;
511
SDWORD xdiff,ydiff, longRange;
513
if (psStruct->numWeaps == 0 || psStruct->asWeaps[0].nStat == 0)
515
// Can't attack without a weapon
519
psWStats = psStruct->asWeaps[weapon_slot].nStat + asWeaponStats;
521
xdiff = (SDWORD)psStruct->pos.x - (SDWORD)psTarget->pos.x;
522
ydiff = (SDWORD)psStruct->pos.y - (SDWORD)psTarget->pos.y;
523
longRange = proj_GetLongRange(psWStats);
524
if (xdiff*xdiff + ydiff*ydiff < longRange*longRange)
535
556
// see if an object is a wall
536
557
static BOOL aiObjIsWall(BASE_OBJECT *psObj)
553
574
/* See if there is a target in range */
554
575
BOOL aiChooseTarget(BASE_OBJECT *psObj, BASE_OBJECT **ppsTarget, int weapon_slot, BOOL bUpdateTarget)
557
577
BASE_OBJECT *psTarget = NULL;
558
SDWORD xdiff,ydiff, distSq, tarDist, minDist;//, longRange;
578
SDWORD xdiff,ydiff, distSq, tarDist, minDist;
560
580
STRUCTURE *psCStruct;
561
581
DROID *psCommander;
562
BOOL bCommanderBlock;
563
UDWORD sensorRange = objSensorRange(psObj);
564
SECONDARY_STATE state;
565
SDWORD curTargetWeight=-1,newTargetWeight;
582
SECONDARY_STATE state;
583
SDWORD curTargetWeight=-1;
567
585
/* Get the sensor range */
568
586
switch (psObj->type)
618
624
curTargetWeight = targetAttackWeight(psCurrTarget, psObj, weapon_slot);
621
if (newTargetWeight >= 0 && //found a new target
622
(!bUpdateTarget || //choosing a new target, don't care if current one is better
623
(curTargetWeight <= 0) || //attacker had no valid target, use new one
624
(newTargetWeight > (curTargetWeight + OLD_TARGET_THRESHOLD)) //updating and new target is better
627
if (newTargetWeight >= 0 // found a new target
628
&& (!bUpdateTarget // choosing a new target, don't care if current one is better
629
|| curTargetWeight <= 0 // attacker had no valid target, use new one
630
|| newTargetWeight > curTargetWeight + OLD_TARGET_THRESHOLD) // updating and new target is better
631
&& validTarget(psObj, psTarget, weapon_slot)
632
&& (aiDroidHasRange((DROID *)psObj, psTarget, weapon_slot) || (secondaryGetState((DROID *)psObj, DSO_HALTTYPE, &state) && state != DSS_HALT_HOLD)))
627
/*check its a valid target*/
628
if (validTarget(psObj, psTarget, weapon_slot))
630
/* See if in sensor range */
631
xdiff = psTarget->pos.x - psObj->pos.x;
632
ydiff = psTarget->pos.y - psObj->pos.y;
633
if ((xdiff*xdiff + ydiff*ydiff < (SDWORD)radSquared) || //target is within our sensor range
634
(secondaryGetState((DROID *)psObj, DSO_HALTTYPE, &state) && //in case we got this target from a friendly unit see if can pursue it
635
(state != DSS_HALT_HOLD))) //make sure it's guard or pursue
637
ASSERT(!isDead(psTarget), "aiChooseTarget: Droid found a dead target!");
638
*ppsTarget = psTarget;
634
ASSERT(!isDead(psTarget), "aiChooseTarget: Droid found a dead target!");
635
*ppsTarget = psTarget;
644
639
else if (psObj->type == OBJ_STRUCTURE)
646
WEAPON_STATS *psWStats;
641
WEAPON_STATS *psWStats = NULL;
643
BOOL bCommanderBlock = false;
648
ASSERT( ((STRUCTURE *)psObj)->asWeaps[weapon_slot].nStat > 0,
649
"aiChooseTarget: no weapons on structure" );
645
ASSERT(((STRUCTURE *)psObj)->asWeaps[weapon_slot].nStat > 0, "no weapons on structure");
651
647
psWStats = ((STRUCTURE *)psObj)->asWeaps[weapon_slot].nStat + asWeaponStats;
648
longRange = proj_GetLongRange(psWStats);
653
650
// see if there is a target from the command droids
655
652
psCommander = cmdDroidGetDesignator(psObj->player);
656
bCommanderBlock = false;
657
653
if (!proj_Direct(psWStats) && (psCommander != NULL) &&
658
654
aiStructHasRange((STRUCTURE *)psObj, (BASE_OBJECT *)psCommander, weapon_slot))
748
741
psCurr = gridIterate();
749
742
while (psCurr != NULL)
751
//don't target features
752
if (psCurr->type != OBJ_FEATURE && !psCurr->died)
744
/* Check that it is a valid target */
745
if (psCurr->type != OBJ_FEATURE && !psCurr->died && aiStructHasRange((STRUCTURE *)psObj, psCurr, weapon_slot)
746
&& psObj->player != psCurr->player && !aiCheckAlliances(psCurr->player, psObj->player)
747
&& validTarget(psObj, psCurr, weapon_slot) && psCurr->visible[psObj->player])
754
if (psObj->player != psCurr->player &&
755
!aiCheckAlliances(psCurr->player,psObj->player))
749
// See if in sensor range and visible
750
xdiff = psCurr->pos.x - psObj->pos.x;
751
ydiff = psCurr->pos.y - psObj->pos.y;
752
distSq = xdiff * xdiff + ydiff * ydiff;
754
|| (psTarget && psTarget->type == OBJ_STRUCTURE && ((STRUCTURE *)psTarget)->status != SS_BUILT)
755
|| (psTarget && aiObjIsWall(psTarget) && !aiObjIsWall(psCurr)))
757
/*check its a valid target*/
758
//Watermelon:Greater than 1 for now
759
if ( validTarget(psObj, psCurr, weapon_slot) &&
760
!aiObjIsWall(psCurr))
762
// See if in sensor range and visible
763
xdiff = psCurr->pos.x - psObj->pos.x;
764
ydiff = psCurr->pos.y - psObj->pos.y;
765
distSq = xdiff*xdiff + ydiff*ydiff;
766
if (distSq < (SDWORD)radSquared &&
767
psCurr->visible[psObj->player] &&
776
761
psCurr = gridIterate();
792
777
/* See if there is a target in range for Sensor objects*/
793
778
BOOL aiChooseSensorTarget(BASE_OBJECT *psObj, BASE_OBJECT **ppsTarget)
795
SDWORD sensorRange = objSensorRange(psObj);
797
BASE_OBJECT *psCurr,*psTemp = NULL;
798
BASE_OBJECT *psTarget = NULL;
799
SDWORD xdiff,ydiff, distSq, tarDist;
801
/* Get the sensor range */
805
if (asSensorStats[((DROID *)psObj)->asBits[COMP_SENSOR].nStat].
806
location != LOC_TURRET)
808
// to be used for Turret Sensors only
811
radSquared = sensorRange * sensorRange;
814
if (!(structStandardSensor((STRUCTURE *)psObj) ||
815
structVTOLSensor((STRUCTURE *)psObj)))
817
// to be used for Standard and VTOL intercept Turret Sensors only
820
radSquared = sensorRange * sensorRange;
827
/* See if there is a something in range */
828
if (psObj->type == OBJ_DROID && CAN_UPDATE_NAYBORS( (DROID *)psObj ))
780
int sensorRange = objSensorRange(psObj);
781
unsigned int radSquared = sensorRange * sensorRange;
782
bool radarDetector = objRadarDetector(psObj);
784
if (!objActiveRadar(psObj) && !radarDetector)
786
ASSERT(false, "Only to be used for sensor turrets!");
790
/* See if there is something in range */
793
BASE_OBJECT *psCurr, *psTemp = NULL;
794
int tarDist = SDWORD_MAX;
796
gridStartIterate((SDWORD)psObj->pos.x, (SDWORD)psObj->pos.y);
797
psCurr = gridIterate();
798
while (psCurr != NULL)
800
if (psCurr->type == OBJ_STRUCTURE || psCurr->type == OBJ_DROID)
802
if (psObj->player != psCurr->player
803
&& !aiCheckAlliances(psCurr->player,psObj->player)
804
&& objActiveRadar(psCurr))
806
// See if in twice *their* sensor range
807
const int xdiff = psCurr->pos.x - psObj->pos.x;
808
const int ydiff = psCurr->pos.y - psObj->pos.y;
809
const unsigned int distSq = xdiff * xdiff + ydiff * ydiff;
810
const int targetSensor = objSensorRange(psCurr) * 2;
811
const unsigned int sensorSquared = targetSensor * targetSensor;
813
if (distSq < sensorSquared && distSq < tarDist)
820
psCurr = gridIterate();
825
objTrace(psTemp->id, "Targetted by radar detector %d", (int)psObj->id);
826
objTrace(psObj->id, "Targetting radar %d", (int)psTemp->id);
827
ASSERT(!psTemp->died, "aiChooseSensorTarget gave us a dead target");
832
else if (psObj->type == OBJ_DROID && CAN_UPDATE_NAYBORS((DROID *)psObj))
834
BASE_OBJECT *psTarget = NULL;
830
836
if (aiBestNearestTarget((DROID *)psObj, &psTarget, 0) >= 0)
832
838
/* See if in sensor range */
833
xdiff = psTarget->pos.x - psObj->pos.x;
834
ydiff = psTarget->pos.y - psObj->pos.y;
835
if (xdiff*xdiff + ydiff*ydiff < (SDWORD)radSquared)
839
const int xdiff = psTarget->pos.x - psObj->pos.x;
840
const int ydiff = psTarget->pos.y - psObj->pos.y;
841
const unsigned int distSq = xdiff * xdiff + ydiff * ydiff;
843
if (distSq < radSquared)
837
845
*ppsTarget = psTarget;
844
tarDist = SDWORD_MAX;
852
BASE_OBJECT *psCurr, *psTemp = NULL;
853
int tarDist = SDWORD_MAX;
845
855
gridStartIterate((SDWORD)psObj->pos.x, (SDWORD)psObj->pos.y);
846
856
psCurr = gridIterate();
847
857
while (psCurr != NULL)
849
//don't target features
850
if (psCurr->type != OBJ_FEATURE && !psCurr->died)
852
if (psObj->player != psCurr->player &&
853
!aiCheckAlliances(psCurr->player,psObj->player) &&
854
!aiObjIsWall(psCurr))
856
// See if in sensor range and visible
857
xdiff = psCurr->pos.x - psObj->pos.x;
858
ydiff = psCurr->pos.y - psObj->pos.y;
859
distSq = xdiff*xdiff + ydiff*ydiff;
860
if (distSq < (SDWORD)radSquared &&
861
psCurr->visible[psObj->player] &&
869
psCurr = gridIterate();
859
// Don't target features or dead objects
860
if (psCurr->type != OBJ_FEATURE && !psCurr->died)
862
if (psObj->player != psCurr->player &&
863
!aiCheckAlliances(psCurr->player,psObj->player) &&
864
!aiObjIsWall(psCurr))
866
// See if in sensor range and visible
867
const int xdiff = psCurr->pos.x - psObj->pos.x;
868
const int ydiff = psCurr->pos.y - psObj->pos.y;
869
const unsigned int distSq = xdiff * xdiff + ydiff * ydiff;
871
if (distSq < radSquared && psCurr->visible[psObj->player] && distSq < tarDist)
878
psCurr = gridIterate();
1065
//need to check propulsion type of target
1071
// Need to check propulsion type of target
1066
1072
switch (psTarget->type)
1068
1074
case OBJ_DROID:
1069
if (asPropulsionTypes[asPropulsionStats[((DROID *)psTarget)->asBits[
1070
COMP_PROPULSION].nStat].propulsionType].travel == AIR)
1075
if (asPropulsionTypes[asPropulsionStats[((DROID *)psTarget)->asBits[
1076
COMP_PROPULSION].nStat].propulsionType].travel == AIR)
1072
1078
if (((DROID *)psTarget)->sMove.Status != MOVEINACTIVE)
1074
bTargetInAir = true;
1080
bTargetInAir = true;
1078
bTargetInAir = false;
1084
bTargetInAir = false;
1083
bTargetInAir = false;
1089
bTargetInAir = false;
1089
bTargetInAir = false;
1095
bTargetInAir = false;
1093
1099
//need what can shoot at
1094
1100
switch (psObject->type)
1096
1102
case OBJ_DROID:
1097
// Can't attack without a weapon
1103
// Can't attack without a weapon
1098
1104
//Watermelon:re-enabled if (((DROID *)psObject)->numWeaps != 0) to prevent crash
1099
if ( ((DROID *)psObject)->numWeaps != 0 &&
1100
((DROID *)psObject)->asWeaps[weapon_slot].nStat != 0 )
1105
if (((DROID *)psObject)->numWeaps != 0 &&
1106
((DROID *)psObject)->asWeaps[weapon_slot].nStat != 0)
1102
1108
surfaceToAir = asWeaponStats[((DROID *)psObject)->asWeaps[weapon_slot].nStat].surfaceToAir;
1103
if ( ((surfaceToAir & SHOOT_IN_AIR) && bTargetInAir) || ((surfaceToAir & SHOOT_ON_GROUND) && !bTargetInAir) )
1109
if (((surfaceToAir & SHOOT_IN_AIR) && bTargetInAir) || ((surfaceToAir & SHOOT_ON_GROUND) && !bTargetInAir))