~ubuntu-branches/ubuntu/lucid/warzone2100/lucid

« back to all changes in this revision

Viewing changes to src/ai.c

  • Committer: Bazaar Package Importer
  • Author(s): Christoph Egger, Paul Wise, Christoph Egger
  • Date: 2009-06-29 17:12:52 UTC
  • mfrom: (1.1.11 upstream) (2.1.7 squeeze)
  • Revision ID: james.westby@ubuntu.com-20090629171252-5ddnlfg3zfchrega
Tags: 2.2.1+dfsg1-1
[ Paul Wise ]
* New upstream release (Closes: #534962)
* Adjust the flex build-depends to take account of the conflict
  with all the versions of flex 2.5.34 (LP: #372872)
* Make the -music Recommends more strict, 2.1 music doesn't work
  with 2.2.
* Upstream moved the downloads to sourceforge, update the watch file
* Bump Standards-Version, no changes needed
* Drop use of dh_desktop since it no longer does anything
* Recommend the new warzone2100-video package, version 2.2 or similar
* Mention the warzone2100 crash reports in the -dbg package description

[ Christoph Egger ]
* Replace CC-2.0 graphic from cybersphinx, create a new tarball
* Add myself to uploaders

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
2
        This file is part of Warzone 2100.
3
3
        Copyright (C) 1999-2004  Eidos Interactive
4
 
        Copyright (C) 2005-2007  Warzone Resurrection Project
 
4
        Copyright (C) 2005-2009  Warzone Resurrection Project
5
5
 
6
6
        Warzone 2100 is free software; you can redistribute it and/or modify
7
7
        it under the terms of the GNU General Public License as published by
43
43
// alliances
44
44
UBYTE   alliances[MAX_PLAYERS][MAX_PLAYERS];
45
45
 
 
46
 
 
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)
 
49
{
 
50
        WEAPON_STATS            *psWStats;
 
51
        SDWORD                          xdiff,ydiff, longRange;
 
52
 
 
53
        if (psStruct->numWeaps == 0 || psStruct->asWeaps[0].nStat == 0)
 
54
        {
 
55
                // Can't attack without a weapon
 
56
                return false;
 
57
        }
 
58
 
 
59
        psWStats = psStruct->asWeaps[weapon_slot].nStat + asWeaponStats;
 
60
 
 
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)
 
65
        {
 
66
                // in range
 
67
                return true;
 
68
 
 
69
        }
 
70
 
 
71
        return false;
 
72
}
 
73
 
 
74
static BOOL aiDroidHasRange(DROID *psDroid, BASE_OBJECT *psTarget, int weapon_slot)
 
75
{
 
76
        WEAPON_STATS            *psWStats;
 
77
        SDWORD                  xdiff, ydiff, longRange;
 
78
 
 
79
        if (psDroid->numWeaps == 0 || psDroid->asWeaps[0].nStat == 0)
 
80
        {
 
81
                // Can't attack without a weapon
 
82
                return false;
 
83
        }
 
84
 
 
85
        psWStats = psDroid->asWeaps[weapon_slot].nStat + asWeaponStats;
 
86
 
 
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)
 
91
        {
 
92
                // in range
 
93
                return true;
 
94
 
 
95
        }
 
96
 
 
97
        return false;
 
98
}
 
99
 
46
100
/* alliance code for ai. return true if an alliance has formed. */
47
101
BOOL aiCheckAlliances(UDWORD s1,UDWORD s2)
48
102
{
147
201
                                                        if(friendlyDroid->order != DORDER_ATTACK)
148
202
                                                        {
149
203
                                                                // make sure target is near enough
150
 
                                                                if (dirtySqrt(psDroid->pos.x, psDroid->pos.y, tempTarget->pos.x, tempTarget->pos.y)
151
 
                                                                    < droidSensorRange(psDroid))
 
204
                                                                if (aiDroidHasRange(psDroid, tempTarget, weapon_slot))
152
205
                                                                {
153
206
                                                                        targetInQuestion = tempTarget;          //consider this target
154
207
                                                                }
159
212
                                else if(friendlyObj->type == OBJ_STRUCTURE)
160
213
                                {
161
214
                                        tempTarget = ((STRUCTURE*)friendlyObj)->psTarget[0];
162
 
                                        if (tempTarget && !tempTarget->died)
 
215
                                        if (tempTarget && !tempTarget->died && aiDroidHasRange(psDroid, tempTarget, weapon_slot))
163
216
                                        {
164
217
                                                targetInQuestion = tempTarget;
165
218
                                        }
167
220
                        }
168
221
                }
169
222
 
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))
177
231
                {
178
 
 
179
 
                        if ( !validTarget((BASE_OBJECT *)psDroid, targetInQuestion, weapon_slot) )
180
 
                        {
181
 
                                continue;
182
 
                        }
183
 
                        else if (targetInQuestion->type == OBJ_DROID)
 
232
                        if (targetInQuestion->type == OBJ_DROID)
184
233
                        {
185
234
                                // in multiPlayer - don't attack Transporters with EW
186
235
                                if (bMultiPlayer)
201
250
                        }
202
251
                        else if (targetInQuestion->type == OBJ_STRUCTURE)
203
252
                        {
 
253
                                STRUCTURE *psStruct = (STRUCTURE *)targetInQuestion;
 
254
 
204
255
                                if (electronic)
205
256
                                {
206
257
                                        /* don't want to target structures with resistance of zero if using electronic warfare */
209
260
                                                psTarget = targetInQuestion;
210
261
                                        }
211
262
                                }
212
 
                                else if (((STRUCTURE *)targetInQuestion)->asWeaps[weapon_slot].nStat > 0)
 
263
                                else if (psStruct->asWeaps[weapon_slot].nStat > 0)
213
264
                                {
214
265
                                        // structure with weapons - go for this
215
266
                                        psTarget = targetInQuestion;
216
267
                                }
217
 
                                else if ( (  ((STRUCTURE *)targetInQuestion)->pStructureType->type != REF_WALL
218
 
                                                   &&((STRUCTURE *)targetInQuestion)->pStructureType->type != REF_WALLCORNER
219
 
                                                  )
220
 
                                                 || driveModeActive()
221
 
                                                 || (bMultiPlayer && game.type == SKIRMISH && !isHumanPlayer(psDroid->player))
222
 
                                                )
 
268
                                else if ((psStruct->pStructureType->type != REF_WALL && psStruct->pStructureType->type != REF_WALLCORNER)
 
269
                                         || driveModeActive() || (bMultiPlayer && !isHumanPlayer(psDroid->player)))
223
270
                                {
224
271
                                        psTarget = targetInQuestion;
225
272
                                }
245
292
        if (bestTarget)
246
293
        {
247
294
                ASSERT(!bestTarget->died, "aiBestNearestTarget: AI gave us a target that is already dead.");
 
295
                targetStructure = visGetBlockingWall((BASE_OBJECT *)psDroid, bestTarget);
 
296
 
248
297
                /* See if target is blocked by a wall; only affects direct weapons */
249
298
                if (proj_Direct(asWeaponStats + psDroid->asWeaps[weapon_slot].nStat)
250
 
                 && (targetStructure = visGetBlockingWall((BASE_OBJECT *)psDroid, bestTarget)))
 
299
                 && targetStructure)
251
300
                {
252
301
                        //are we any good against walls?
253
302
                        if(asStructStrengthModifier[weaponEffect][targetStructure->pStructureType->strength] >= 100)            //can attack atleast with default strength
403
452
 
404
453
                /* Now calculate the overall weight */
405
454
                attackWeight = asWeaponModifier[weaponEffect][(asPropulsionStats + targetDroid->asBits[COMP_PROPULSION].nStat)->propulsionType] // Our weapon's effect against target
406
 
                                - WEIGHT_DIST_TILE_DROID * map_coord(dirtySqrt(psAttacker->pos.x, psAttacker->pos.y, targetDroid->pos.x, targetDroid->pos.y)) // farer droids are less attractive
 
455
                                - WEIGHT_DIST_TILE_DROID * map_coord(dirtyHypot(psAttacker->pos.x - targetDroid->pos.x, psAttacker->pos.y - targetDroid->pos.y)) // farer droids are less attractive
407
456
                                + WEIGHT_HEALTH_DROID * damageRatio // we prefer damaged droids
408
457
                                + targetTypeBonus; // some droid types have higher priority
409
458
 
444
493
 
445
494
                /* Now calculate the overall weight */
446
495
                attackWeight = asStructStrengthModifier[weaponEffect][targetStructure->pStructureType->strength] // Our weapon's effect against target
447
 
                                - WEIGHT_DIST_TILE_STRUCT * map_coord(dirtySqrt(psAttacker->pos.x, psAttacker->pos.y, targetStructure->pos.x, targetStructure->pos.y)) // farer structs are less attractive
 
496
                                - WEIGHT_DIST_TILE_STRUCT * map_coord(dirtyHypot(psAttacker->pos.x - targetStructure->pos.x, psAttacker->pos.y - targetStructure->pos.y)) // farer structs are less attractive
448
497
                                + WEIGHT_HEALTH_STRUCT * damageRatio // we prefer damaged structures
449
498
                                + targetTypeBonus; // some structure types have higher priority
450
499
 
467
516
        }
468
517
 
469
518
        /* We prefer objects we can see and can attack immediately */
470
 
        if(!visibleObjWallBlock((BASE_OBJECT *)psAttacker, psTarget))
 
519
        if(!visibleObject((BASE_OBJECT *)psAttacker, psTarget, true))
471
520
        {
472
521
                attackWeight /= WEIGHT_NOT_VISIBLE_F;
473
522
        }
504
553
}
505
554
 
506
555
 
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)
509
 
{
510
 
        WEAPON_STATS            *psWStats;
511
 
        SDWORD                          xdiff,ydiff, longRange;
512
 
 
513
 
        if (psStruct->numWeaps == 0 || psStruct->asWeaps[0].nStat == 0)
514
 
        {
515
 
                // Can't attack without a weapon
516
 
                return false;
517
 
        }
518
 
 
519
 
        psWStats = psStruct->asWeaps[weapon_slot].nStat + asWeaponStats;
520
 
 
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)
525
 
        {
526
 
                // in range
527
 
                return true;
528
 
 
529
 
        }
530
 
 
531
 
        return false;
532
 
}
533
 
 
534
 
 
535
556
// see if an object is a wall
536
557
static BOOL aiObjIsWall(BASE_OBJECT *psObj)
537
558
{
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)
555
576
{
556
 
        UDWORD  radSquared;
557
577
        BASE_OBJECT             *psTarget = NULL;
558
 
        SDWORD                  xdiff,ydiff, distSq, tarDist, minDist;//, longRange;
 
578
        SDWORD                  xdiff,ydiff, distSq, tarDist, minDist;
559
579
        BOOL                    bCBTower;
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;
566
584
 
567
585
        /* Get the sensor range */
568
586
        switch (psObj->type)
579
597
                        // Can't attack without a weapon
580
598
                        return false;
581
599
                }
582
 
                radSquared = sensorRange * sensorRange;
583
600
                break;
584
601
        case OBJ_STRUCTURE:
585
602
                if (((STRUCTURE *)psObj)->numWeaps == 0 || ((STRUCTURE *)psObj)->asWeaps[0].nStat == 0)
587
604
                        // Can't attack without a weapon
588
605
                        return false;
589
606
                }
590
 
 
591
 
                // increase the sensor range for AA sites
592
 
                // AA sites are defensive structures that can only shoot in the air
593
 
                if ( (((STRUCTURE *)psObj)->pStructureType->type == REF_DEFENSE) &&
594
 
                         (asWeaponStats[((STRUCTURE *)psObj)->asWeaps[weapon_slot].nStat].surfaceToAir == SHOOT_IN_AIR) )
595
 
                {
596
 
                        sensorRange = 3 * sensorRange / 2;
597
 
                }
598
 
 
599
 
                radSquared = sensorRange*sensorRange;
600
607
                break;
601
608
        default:
602
 
                radSquared = 0;
603
609
                break;
604
610
        }
605
611
 
609
615
                BASE_OBJECT *psCurrTarget = ((DROID *)psObj)->psActionTarget[0];
610
616
 
611
617
                /* find a new target */
612
 
                newTargetWeight = aiBestNearestTarget((DROID *)psObj, &psTarget, weapon_slot);
 
618
                int newTargetWeight = aiBestNearestTarget((DROID *)psObj, &psTarget, weapon_slot);
613
619
 
614
620
                /* Calculate weight of the current target if updating; but take care not to target
615
621
                 * ourselves... */
618
624
                        curTargetWeight = targetAttackWeight(psCurrTarget, psObj, weapon_slot);
619
625
                }
620
626
 
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
625
 
                        ))
 
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)))
626
633
                {
627
 
                        /*check its a valid target*/
628
 
                        if (validTarget(psObj, psTarget, weapon_slot))
629
 
                        {
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
636
 
                                {
637
 
                                        ASSERT(!isDead(psTarget), "aiChooseTarget: Droid found a dead target!");
638
 
                                    *ppsTarget = psTarget;
639
 
                                    return true;
640
 
                                }
641
 
                        }
 
634
                        ASSERT(!isDead(psTarget), "aiChooseTarget: Droid found a dead target!");
 
635
                        *ppsTarget = psTarget;
 
636
                        return true;
642
637
                }
643
638
        }
644
639
        else if (psObj->type == OBJ_STRUCTURE)
645
640
        {
646
 
                WEAPON_STATS    *psWStats;
 
641
                WEAPON_STATS    *psWStats = NULL;
 
642
                int     longRange = 0;
 
643
                BOOL    bCommanderBlock = false;
647
644
 
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");
650
646
 
651
647
                psWStats = ((STRUCTURE *)psObj)->asWeaps[weapon_slot].nStat + asWeaponStats;
 
648
                longRange = proj_GetLongRange(psWStats);
652
649
 
653
650
                // see if there is a target from the command droids
654
651
                psTarget = NULL;
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))
659
655
                {
680
676
                }
681
677
 
682
678
                // indirect fire structures use sensor towers first
683
 
                tarDist = SDWORD_MAX;
 
679
                tarDist = longRange * longRange;
684
680
                minDist = psWStats->minRange * psWStats->minRange;
685
681
                bCBTower = false;
686
 
                if (psTarget == NULL &&
687
 
                        !bCommanderBlock &&
688
 
                        !proj_Direct(psWStats))
 
682
                if (psTarget == NULL && !bCommanderBlock && !proj_Direct(psWStats))
689
683
                {
690
684
                        for(psCStruct=apsStructLists[psObj->player]; psCStruct; psCStruct=psCStruct->psNext)
691
685
                        {
700
694
                                    && psCStruct->psTarget[0] != NULL
701
695
                                    && !psCStruct->psTarget[0]->died)
702
696
                                {
703
 
                                        /*check its a valid target*/
 
697
                                        /* Check it is a valid target */
704
698
                                        //Watermelon:Greater than 1 for now
705
699
                                        if ( validTarget(psObj, psCStruct->psTarget[0], 0) &&
706
700
                                                aiStructHasRange((STRUCTURE *)psObj, psCStruct->psTarget[0], weapon_slot))
716
710
                                            }
717
711
                                        }
718
712
                                }
719
 
                                else if (structCBSensor(psCStruct)
 
713
                                else if ((structCBSensor(psCStruct) || objRadarDetector((BASE_OBJECT *)psCStruct))
720
714
                                         && psCStruct->psTarget[0] != NULL
721
715
                                         && !psCStruct->psTarget[0]->died)
722
716
                                {
723
 
                                        /*check its a valid target*/
 
717
                                        /* Check it is a valid target */
724
718
                                        if ( validTarget(psObj, psCStruct->psTarget[0], 0) &&
725
719
                                                aiStructHasRange((STRUCTURE *)psObj, psCStruct->psTarget[0], weapon_slot))
726
720
                                        {
739
733
                        }
740
734
                }
741
735
 
742
 
                if ((psTarget == NULL) &&
743
 
                        !bCommanderBlock)
 
736
                if (psTarget == NULL && !bCommanderBlock)
744
737
                {
745
738
                        BASE_OBJECT *psCurr;
746
739
 
748
741
                        psCurr = gridIterate();
749
742
                        while (psCurr != NULL)
750
743
                        {
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])
753
748
                                {
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;
 
753
                                        if (distSq < tarDist
 
754
                                            || (psTarget && psTarget->type == OBJ_STRUCTURE && ((STRUCTURE *)psTarget)->status != SS_BUILT)
 
755
                                            || (psTarget && aiObjIsWall(psTarget) && !aiObjIsWall(psCurr)))
756
756
                                        {
757
 
                                                /*check its a valid target*/
758
 
                                                //Watermelon:Greater than 1 for now
759
 
                                                if ( validTarget(psObj, psCurr, weapon_slot) &&
760
 
                                                        !aiObjIsWall(psCurr))
761
 
                                                {
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] &&
768
 
                                                            distSq < tarDist)
769
 
                                                    {
770
 
                                                            psTarget = psCurr;
771
 
                                                            tarDist = distSq;
772
 
                                                    }
773
 
                                                }
 
757
                                                psTarget = psCurr;
 
758
                                                tarDist = distSq;
774
759
                                        }
775
760
                                }
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)
794
779
{
795
 
        SDWORD  sensorRange = objSensorRange(psObj);
796
 
        UDWORD  radSquared;
797
 
        BASE_OBJECT             *psCurr,*psTemp = NULL;
798
 
        BASE_OBJECT             *psTarget = NULL;
799
 
        SDWORD  xdiff,ydiff, distSq, tarDist;
800
 
 
801
 
        /* Get the sensor range */
802
 
        switch (psObj->type)
803
 
        {
804
 
        case OBJ_DROID:
805
 
                if (asSensorStats[((DROID *)psObj)->asBits[COMP_SENSOR].nStat].
806
 
                        location != LOC_TURRET)
807
 
                {
808
 
                        // to be used for Turret Sensors only
809
 
                        return false;
810
 
                }
811
 
                radSquared = sensorRange * sensorRange;
812
 
                break;
813
 
        case OBJ_STRUCTURE:
814
 
                if (!(structStandardSensor((STRUCTURE *)psObj) ||
815
 
                        structVTOLSensor((STRUCTURE *)psObj)))
816
 
                {
817
 
                        // to be used for Standard and VTOL intercept Turret Sensors only
818
 
                        return false;
819
 
                }
820
 
                radSquared = sensorRange * sensorRange;
821
 
                break;
822
 
        default:
823
 
                radSquared = 0;
824
 
                break;
825
 
        }
826
 
 
827
 
        /* See if there is a something in range */
828
 
        if (psObj->type == OBJ_DROID && CAN_UPDATE_NAYBORS( (DROID *)psObj ))
829
 
        {
 
780
        int             sensorRange = objSensorRange(psObj);
 
781
        unsigned int    radSquared = sensorRange * sensorRange;
 
782
        bool            radarDetector = objRadarDetector(psObj);
 
783
 
 
784
        if (!objActiveRadar(psObj) && !radarDetector)
 
785
        {
 
786
                ASSERT(false, "Only to be used for sensor turrets!");
 
787
                return false;
 
788
        }
 
789
 
 
790
        /* See if there is something in range */
 
791
        if (radarDetector)
 
792
        {
 
793
                BASE_OBJECT     *psCurr, *psTemp = NULL;
 
794
                int             tarDist = SDWORD_MAX;
 
795
 
 
796
                gridStartIterate((SDWORD)psObj->pos.x, (SDWORD)psObj->pos.y);
 
797
                psCurr = gridIterate();
 
798
                while (psCurr != NULL)
 
799
                {
 
800
                        if (psCurr->type == OBJ_STRUCTURE || psCurr->type == OBJ_DROID)
 
801
                        {
 
802
                                if (psObj->player != psCurr->player
 
803
                                    && !aiCheckAlliances(psCurr->player,psObj->player)
 
804
                                    && objActiveRadar(psCurr))
 
805
                                {
 
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;
 
812
 
 
813
                                        if (distSq < sensorSquared && distSq < tarDist)
 
814
                                        {
 
815
                                                psTemp = psCurr;
 
816
                                                tarDist = distSq;
 
817
                                        }
 
818
                                }
 
819
                        }
 
820
                        psCurr = gridIterate();
 
821
                }
 
822
 
 
823
                if (psTemp)
 
824
                {
 
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");
 
828
                        *ppsTarget = psTemp;
 
829
                        return true;
 
830
                }
 
831
        }
 
832
        else if (psObj->type == OBJ_DROID && CAN_UPDATE_NAYBORS((DROID *)psObj))
 
833
        {
 
834
                BASE_OBJECT     *psTarget = NULL;
 
835
 
830
836
                if (aiBestNearestTarget((DROID *)psObj, &psTarget, 0) >= 0)
831
837
                {
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;
 
842
 
 
843
                        if (distSq < radSquared)
836
844
                        {
837
845
                                *ppsTarget = psTarget;
838
846
                                return true;
839
847
                        }
840
848
                }
841
849
        }
842
 
        else
 
850
        else    // structure
843
851
        {
844
 
                tarDist = SDWORD_MAX;
 
852
                BASE_OBJECT     *psCurr, *psTemp = NULL;
 
853
                int             tarDist = SDWORD_MAX;
 
854
 
845
855
                gridStartIterate((SDWORD)psObj->pos.x, (SDWORD)psObj->pos.y);
846
856
                psCurr = gridIterate();
847
857
                while (psCurr != NULL)
848
858
                {
849
 
                            //don't target features
850
 
                            if (psCurr->type != OBJ_FEATURE && !psCurr->died)
851
 
                            {
852
 
                                    if (psObj->player != psCurr->player &&
853
 
                                            !aiCheckAlliances(psCurr->player,psObj->player) &&
854
 
                                            !aiObjIsWall(psCurr))
855
 
                                    {
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] &&
862
 
                                                    distSq < tarDist)
863
 
                                            {
864
 
                                                    psTemp = psCurr;
865
 
                                                    tarDist = distSq;
866
 
                                            }
867
 
                                    }
868
 
                            }
869
 
                            psCurr = gridIterate();
 
859
                        // Don't target features or dead objects
 
860
                        if (psCurr->type != OBJ_FEATURE && !psCurr->died)
 
861
                        {
 
862
                                if (psObj->player != psCurr->player &&
 
863
                                    !aiCheckAlliances(psCurr->player,psObj->player) &&
 
864
                                    !aiObjIsWall(psCurr))
 
865
                                {
 
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;
 
870
 
 
871
                                        if (distSq < radSquared && psCurr->visible[psObj->player] && distSq < tarDist)
 
872
                                        {
 
873
                                                psTemp = psCurr;
 
874
                                                tarDist = distSq;
 
875
                                        }
 
876
                                }
 
877
                        }
 
878
                        psCurr = gridIterate();
870
879
                }
871
880
 
872
881
                if (psTemp)
896
905
        // HACK: we always want to update orders when NOT running a MP game,
897
906
        // and we don't want to update when the droid belongs to another human player
898
907
        if (!myResponsibility(psDroid->player) && bMultiPlayer
899
 
                && isHumanPlayer(psDroid->player))
 
908
                  && isHumanPlayer(psDroid->player))
900
909
        {
901
910
                return;         // we should not order this droid around
902
911
        }
1014
1023
                        if((psDroid->id % TARGET_UPD_SKIP_FRAMES) ==
1015
1024
                                (frameGetFrameNumber() % TARGET_UPD_SKIP_FRAMES))
1016
1025
                        {
1017
 
                                int i;
 
1026
                                unsigned int i;
1018
1027
 
1019
1028
                                (void)updateAttackTarget((BASE_OBJECT*)psDroid, 0); // this function always has to be called on weapon-slot 0 (even if ->numWeaps == 0)
1020
1029
 
1033
1042
        {
1034
1043
                if (psDroid->droidType == DROID_SENSOR)
1035
1044
                {
1036
 
                        //Watermelon:only 1 target for sensor droid
1037
 
                        if ( aiChooseTarget((BASE_OBJECT *)psDroid, &psTarget, 0, true) )
 
1045
                        if (aiChooseSensorTarget((BASE_OBJECT *)psDroid, &psTarget))
1038
1046
                        {
1039
1047
                                orderDroidObj(psDroid, DORDER_OBSERVE, psTarget);
1040
1048
                        }
1049
1057
        }
1050
1058
}
1051
1059
 
1052
 
/*set of rules which determine whether the weapon associated with the object
1053
 
can fire on the propulsion type of the target*/
1054
 
//Watermelon:added weapon_slot
 
1060
/* Set of rules which determine whether the weapon associated with the object can fire on the propulsion type of the target. */
1055
1061
BOOL validTarget(BASE_OBJECT *psObject, BASE_OBJECT *psTarget, int weapon_slot)
1056
1062
{
1057
1063
        BOOL    bTargetInAir, bValidTarget = false;
1062
1068
                return false;
1063
1069
        }
1064
1070
 
1065
 
        //need to check propulsion type of target
 
1071
        // Need to check propulsion type of target
1066
1072
        switch (psTarget->type)
1067
1073
        {
1068
1074
        case OBJ_DROID:
1069
 
        if (asPropulsionTypes[asPropulsionStats[((DROID *)psTarget)->asBits[
1070
 
            COMP_PROPULSION].nStat].propulsionType].travel == AIR)
1071
 
        {
 
1075
                if (asPropulsionTypes[asPropulsionStats[((DROID *)psTarget)->asBits[
 
1076
                                                            COMP_PROPULSION].nStat].propulsionType].travel == AIR)
 
1077
                {
1072
1078
                        if (((DROID *)psTarget)->sMove.Status != MOVEINACTIVE)
1073
1079
                        {
1074
 
                    bTargetInAir = true;
 
1080
                                bTargetInAir = true;
1075
1081
                        }
1076
1082
                        else
1077
1083
                        {
1078
 
                    bTargetInAir = false;
 
1084
                                bTargetInAir = false;
1079
1085
                        }
1080
 
        }
1081
 
        else
1082
 
        {
1083
 
            bTargetInAir = false;
1084
 
        }
1085
 
        break;
1086
 
    case OBJ_STRUCTURE:
 
1086
                }
 
1087
                else
 
1088
                {
 
1089
                        bTargetInAir = false;
 
1090
                }
 
1091
                break;
 
1092
        case OBJ_STRUCTURE:
1087
1093
        default:
1088
 
        //lets hope so!
1089
 
        bTargetInAir = false;
1090
 
        break;
1091
 
    }
 
1094
                //lets hope so!
 
1095
                bTargetInAir = false;
 
1096
                break;
 
1097
        }
1092
1098
 
1093
1099
        //need what can shoot at
1094
1100
        switch (psObject->type)
1095
1101
        {
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)
1101
1107
                {
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))
1104
1110
                        {
1105
1111
                                return true;
1106
1112
                        }
1110
1116
                        return false;
1111
1117
                }
1112
1118
                /*
1113
 
        if (((DROID *)psObject)->asWeaps[0].nStat != 0 && ((DROID *)psObject)->numWeaps > 0)
 
1119
                if (((DROID *)psObject)->asWeaps[0].nStat != 0 && ((DROID *)psObject)->numWeaps > 0)
1114
1120
                {
1115
 
            surfaceToAir = asWeaponStats[((DROID *)psObject)->asWeaps[0].nStat].surfaceToAir;
1116
 
        }
 
1121
                    surfaceToAir = asWeaponStats[((DROID *)psObject)->asWeaps[0].nStat].surfaceToAir;
 
1122
                }
1117
1123
                else
1118
1124
                {
1119
1125
                         surfaceToAir = 0;
1123
1129
        case OBJ_STRUCTURE:
1124
1130
                // Can't attack without a weapon
1125
1131
                //Watermelon:re-enabled if (((DROID *)psObject)->numWeaps != 0) to prevent crash
1126
 
                if ( ((STRUCTURE *)psObject)->numWeaps != 0 &&
1127
 
                        ((STRUCTURE *)psObject)->asWeaps[weapon_slot].nStat != 0 )
 
1132
                if (((STRUCTURE *)psObject)->numWeaps != 0 &&
 
1133
                        ((STRUCTURE *)psObject)->asWeaps[weapon_slot].nStat != 0)
1128
1134
                {
1129
1135
                        surfaceToAir = asWeaponStats[((STRUCTURE *)psObject)->asWeaps[weapon_slot].nStat].surfaceToAir;
1130
1136
                }
1133
1139
                        surfaceToAir = 0;
1134
1140
                }
1135
1141
 
1136
 
                if ( ((surfaceToAir & SHOOT_IN_AIR) && bTargetInAir) || ((surfaceToAir & SHOOT_ON_GROUND) && !bTargetInAir) )
 
1142
                if (((surfaceToAir & SHOOT_IN_AIR) && bTargetInAir) || ((surfaceToAir & SHOOT_ON_GROUND) && !bTargetInAir))
1137
1143
                {
1138
1144
                        return true;
1139
1145
                }
1140
 
                        break;
1141
 
                default:
1142
 
                        surfaceToAir = 0;
1143
 
                        break;
1144
 
                }
1145
 
 
1146
 
    //if target is in the air and you can shoot in the air - OK
1147
 
    if (bTargetInAir && (surfaceToAir & SHOOT_IN_AIR))
1148
 
    {
1149
 
        bValidTarget = true;
1150
 
    }
1151
 
 
1152
 
    //if target is on the ground and can shoot at it - OK
1153
 
    if (!bTargetInAir && (surfaceToAir & SHOOT_ON_GROUND))
1154
 
    {
1155
 
        bValidTarget = true;
1156
 
    }
1157
 
 
1158
 
    return bValidTarget;
 
1146
                break;
 
1147
        default:
 
1148
                surfaceToAir = 0;
 
1149
                break;
 
1150
        }
 
1151
 
 
1152
        //if target is in the air and you can shoot in the air - OK
 
1153
        if (bTargetInAir && (surfaceToAir & SHOOT_IN_AIR))
 
1154
        {
 
1155
                bValidTarget = true;
 
1156
        }
 
1157
 
 
1158
        //if target is on the ground and can shoot at it - OK
 
1159
        if (!bTargetInAir && (surfaceToAir & SHOOT_ON_GROUND))
 
1160
        {
 
1161
                bValidTarget = true;
 
1162
        }
 
1163
 
 
1164
        return bValidTarget;
1159
1165
}
1160
1166
 
1161
1167
/* Make droid/structure look for a better target */