4b825dc642cb6eb9a060e54bf8d69288fbee490494a6641b73026d662261604d7d192beae70b11dc
 
 
1
#include "system.h"
 
 
2
#include "prototyp.h"
 
 
3
#include "stratdef.h"
 
 
4
#include "dynamics.h"
 
 
5
#include "pfarlocs.h"
 
 
6
#include "pheromon.h"
 
 
7
#include "bh_types.h"
 
 
8
#include "pvisible.h"
 
 
9
#include "npc_marine.h"
 
 
10
#include "npc_alien.h"
 
 
11
#include "npc_predator.h"
 
 
12
#include "npc_xenoborg.h"
 
 
13
#include "npc_sentrygun.h"
 
 
14
#include "weapons.h"
 
 
15
#include "weaponbehaviour.h"
 
 
16
#include "los.h"
 
 
17
#include "targeting.h"
 
 
18
#include <assert.h>
 
 
19
#include <stdio.h>
 
 
20
#include "bh_light.h"
 
 
21
 
 
 
22
extern int GlobalFrameCounter;
 
 
23
extern int RouteFinder_CallsThisFrame;
 
 
24
extern DEATH_DATA Alien_Deaths[];
 
 
25
extern DEATH_DATA Marine_Deaths[];
 
 
26
extern DEATH_DATA Predator_Deaths[];
 
 
27
extern DEATH_DATA Xenoborg_Deaths[];
 
 
28
extern ATTACK_DATA Alien_Attacks[];
 
 
29
extern ATTACK_DATA Wristblade_Attacks[];
 
 
30
extern ATTACK_DATA PredStaff_Attacks[];
 
 
31
 
 
 
32
extern void QNormalise(QUAT *q);
 
 
33
extern int IsAIModuleVisibleFromAIModule(AIMODULE *source,AIMODULE *target);
 
 
34
 
 
 
35
typedef enum paq_far_bhstate
 
 
36
{
 
 
37
    PAQFS_Wait,
 
 
38
    PAQFS_Hunt,
 
 
39
    PAQFS_Wander,
 
 
40
    PAQFS_Dying
 
 
41
 
 
 
42
} PAQ_FAR_BHSTATE;
 
 
43
 
 
 
44
typedef enum paq_near_bhstate
 
 
45
{
 
 
46
    PAQNS_Wait,
 
 
47
    PAQNS_Approach,
 
 
48
    PAQNS_Attack,
 
 
49
    PAQNS_Wander,
 
 
50
    PAQNS_Avoidance,
 
 
51
    PAQNS_Dying
 
 
52
 
 
 
53
} PAQ_NEAR_BHSTATE;
 
 
54
 
 
 
55
typedef struct paqStatusBlock
 
 
56
{
 
 
57
    signed int health;
 
 
58
    signed int nearSpeed;
 
 
59
    signed int damageInflicted;
 
 
60
    PAQ_FAR_BHSTATE FarBehaviourState;
 
 
61
    PAQ_NEAR_BHSTATE NearBehaviourState;
 
 
62
    int stateTimer;
 
 
63
    HMODELCONTROLLER HModelController;
 
 
64
    NPC_MOVEMENTDATA moveData;
 
 
65
    NPC_WANDERDATA wanderData;
 
 
66
 
 
 
67
} PAQ_STATUS_BLOCK;
 
 
68
 
 
 
69
int CheckAdjacencyValidity(AIMODULE *target, AIMODULE *source, int alien) 
 
 
70
{
 
 
71
    FARENTRYPOINT *thisEp = GetAIModuleEP(target, source);
 
 
72
 
 
 
73
    return (thisEp) ? !(!alien && thisEp->alien_only) : 0;
 
 
74
}
 
 
75
 
 
 
76
/* First number is max speed, in mm/s. */
 
 
77
/* Second number is acceleration, in mm/s^2. */
 
 
78
/* Individual marines vary this by +/- 12.5%. */
 
 
79
/* Predators and xenoborgs don't at the moment. */
 
 
80
 
 
 
81
static const MOVEMENT_DATA Movement_Stats[] = {
 
 
82
    {
 
 
83
        MDI_Marine_Mooch_Bored,
 
 
84
        1500,
 
 
85
        1500,
 
 
86
    },
 
 
87
    {
 
 
88
        MDI_Marine_Mooch_Alert,
 
 
89
        1500,
 
 
90
        1500,
 
 
91
    },
 
 
92
    {
 
 
93
        MDI_Marine_Combat,
 
 
94
        4000,
 
 
95
        4000,
 
 
96
    },
 
 
97
    {
 
 
98
        MDI_Marine_Sprint,
 
 
99
        10000,
 
 
100
        10000,
 
 
101
    },
 
 
102
    {
 
 
103
        MDI_Civilian_Mooch_Bored,
 
 
104
        1500,
 
 
105
        1500,
 
 
106
    },
 
 
107
    {
 
 
108
        MDI_Civilian_Mooch_Alert,
 
 
109
        1500,
 
 
110
        1500,
 
 
111
    },
 
 
112
    {
 
 
113
        MDI_Civilian_Combat,
 
 
114
        4000,
 
 
115
        4000,
 
 
116
    },
 
 
117
    {
 
 
118
        MDI_Civilian_Sprint,
 
 
119
        10000,
 
 
120
        10000,
 
 
121
    },
 
 
122
    {
 
 
123
        MDI_Predator,
 
 
124
        8000,
 
 
125
        10000,
 
 
126
    },
 
 
127
    {
 
 
128
        MDI_Casual_Predator,
 
 
129
        3000,
 
 
130
        3000,
 
 
131
    },
 
 
132
    {
 
 
133
        MDI_Xenoborg,
 
 
134
        1000,
 
 
135
        1000,
 
 
136
    },
 
 
137
    {
 
 
138
        MDI_End,
 
 
139
        0,
 
 
140
        0,
 
 
141
    },
 
 
142
};
 
 
143
 
 
 
144
const MOVEMENT_DATA *GetThisMovementData(MOVEMENT_DATA_INDEX index)
 
 
145
{
 
 
146
    return &Movement_Stats[index];
 
 
147
}
 
 
148
 
 
 
149
int NPC_IsDead(STRATEGYBLOCK *sbPtr)
 
 
150
{
 
 
151
    if(sbPtr->please_destroy_me) 
 
 
152
        return 1;
 
 
153
 
 
 
154
    switch(sbPtr->type)
 
 
155
    {
 
 
156
        case I_BehaviourMarine:
 
 
157
        case I_BehaviourAlien:
 
 
158
            return 0;
 
 
159
        case I_BehaviourAlienPlayer:
 
 
160
        case I_BehaviourPredatorPlayer:
 
 
161
        case I_BehaviourMarinePlayer:
 
 
162
            return !PlayerStatus.Alive || Observer;
 
 
163
        case I_BehaviourPredator:
 
 
164
        {
 
 
165
            PREDATOR_STATUS_BLOCK *predatorStatusPointer = (PREDATOR_STATUS_BLOCK *)(sbPtr->dataptr);
 
 
166
            assert(predatorStatusPointer);
 
 
167
 
 
 
168
            return (predatorStatusPointer->behaviourState == PBS_Dying);
 
 
169
        }
 
 
170
        break;
 
 
171
        case I_BehaviourQueenAlien:
 
 
172
        {
 
 
173
            PAQ_STATUS_BLOCK *paqStatusPointer = (PAQ_STATUS_BLOCK *)(sbPtr->dataptr);
 
 
174
            assert(paqStatusPointer);
 
 
175
 
 
 
176
            return (paqStatusPointer->NearBehaviourState == PAQNS_Dying);
 
 
177
        }
 
 
178
        break;
 
 
179
        case I_BehaviourHierarchicalFragment:
 
 
180
        case I_BehaviourCorpse:
 
 
181
            /* Corpses are always dead :-) */
 
 
182
            return 1;
 
 
183
        case I_BehaviourAutoGun:
 
 
184
        {
 
 
185
            AUTOGUN_STATUS_BLOCK *agunStatusPointer = (AUTOGUN_STATUS_BLOCK *)(sbPtr->dataptr);    
 
 
186
            assert(agunStatusPointer);
 
 
187
 
 
 
188
            return (agunStatusPointer->behaviourState == I_disabled);
 
 
189
        }
 
 
190
        case I_BehaviourPlacedLight:
 
 
191
        {
 
 
192
            if(!sbPtr->maintainVisibility || sbPtr->DamageBlock.Indestructable)
 
 
193
            {
 
 
194
                return 1;
 
 
195
            }
 
 
196
            else
 
 
197
            {
 
 
198
                PLACED_LIGHT_BEHAV_BLOCK* pl_bhv = (PLACED_LIGHT_BEHAV_BLOCK*)sbPtr->dataptr;
 
 
199
 
 
 
200
                return ((Light_State_Broken == pl_bhv->state) || (Light_OnOff_Off == pl_bhv->on_off_state));
 
 
201
            }
 
 
202
        }
 
 
203
        case I_BehaviourFlare:
 
 
204
        {
 
 
205
            FLARE_BEHAV_BLOCK *bbPtr = (FLARE_BEHAV_BLOCK * ) sbPtr->dataptr;
 
 
206
            return bbPtr->LifeTimeRemaining < 0;
 
 
207
        }
 
 
208
        case I_BehaviourXenoborg:
 
 
209
        case I_BehaviourFaceHugger:
 
 
210
            return 0;
 
 
211
        default:
 
 
212
            return 0;
 
 
213
    }
 
 
214
}
 
 
215
 
 
 
216
void NPC_InitMovementData(NPC_MOVEMENTDATA *moveData)
 
 
217
{
 
 
218
    assert(moveData);
 
 
219
 
 
 
220
    moveData->numObstructiveCollisions = 0;
 
 
221
    moveData->avoidanceDirn.vx = moveData->avoidanceDirn.vy =  moveData->avoidanceDirn.vz = 0;
 
 
222
    moveData->lastTarget.vx =  moveData->lastTarget.vy =  moveData->lastTarget.vz = 0;
 
 
223
    moveData->lastVelocity.vx =  moveData->lastVelocity.vy =  moveData->lastVelocity.vz = 0;
 
 
224
    moveData->numReverses = 0;
 
 
225
    moveData->lastModule = NULL;
 
 
226
}
 
 
227
 
 
 
228
void NPC_IsObstructed(STRATEGYBLOCK *sbPtr, NPC_MOVEMENTDATA *moveData, NPC_OBSTRUCTIONREPORT *details, STRATEGYBLOCK **destructableObject)
 
 
229
{
 
 
230
    assert(destructableObject);
 
 
231
    assert(details);
 
 
232
    assert(moveData);
 
 
233
    assert(sbPtr);
 
 
234
    DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr;
 
 
235
    assert(dynPtr);
 
 
236
    struct collisionreport *nextReport = dynPtr->CollisionReportPtr;
 
 
237
 
 
 
238
    /* init destructable object pointer, etc... */
 
 
239
    *destructableObject = NULL;
 
 
240
    details->environment = 0;
 
 
241
    details->destructableObject = 0;
 
 
242
    details->otherCharacter = 0;
 
 
243
    details->anySingleObstruction = 0;
 
 
244
 
 
 
245
    /* check our velocity: if we haven't got one, we can't be obstructed, so just return */
 
 
246
    if(!sbPtr->DynPtr->LinVelocity.vx && !sbPtr->DynPtr->LinVelocity.vy && !sbPtr->DynPtr->LinVelocity.vz)
 
 
247
    {
 
 
248
        moveData->numObstructiveCollisions = 0;  
 
 
249
        return;
 
 
250
    }
 
 
251
 
 
 
252
    /* get my velocity and behaviour type */
 
 
253
    VECTORCH velDirn = dynPtr->LinVelocity;
 
 
254
    Normalise(&velDirn);
 
 
255
 
 
 
256
    /* walk the collision report list, looking for collisions that obstruct our movement...
 
 
257
    excluding objects of our own type and the player */
 
 
258
 
 
 
259
    while(nextReport)
 
 
260
    {
 
 
261
        int IsCharacterOrPlayer = 0;
 
 
262
        /* int dotWithGravity; */
 
 
263
        int normalDotWithVelocity;
 
 
264
 
 
 
265
        if(nextReport->ObstacleSBPtr)
 
 
266
        {
 
 
267
            if(nextReport->ObstacleSBPtr == PlayerStatus.sbptr) 
 
 
268
                IsCharacterOrPlayer = 1;
 
 
269
 
 
 
270
            if(nextReport->ObstacleSBPtr->type == sbPtr->type)
 
 
271
            {
 
 
272
                IsCharacterOrPlayer = 1;
 
 
273
                details->otherCharacter = 1;
 
 
274
                details->anySingleObstruction = 1;
 
 
275
            } 
 
 
276
        }
 
 
277
 
 
 
278
        {
 
 
279
            VECTORCH normVelocity = sbPtr->DynPtr->LinVelocity;
 
 
280
            Normalise(&normVelocity);
 
 
281
            normalDotWithVelocity = DotProduct(&nextReport->ObstacleNormal, &normVelocity);
 
 
282
        }
 
 
283
 
 
 
284
    //              if((normalDotWithVelocity < -46341) && !IsCharacterOrPlayer)
 
 
285
        /* So aliens can break through windows, 19/5/98 CDF */
 
 
286
 
 
 
287
        if(!IsCharacterOrPlayer && ((normalDotWithVelocity < -46341) || nextReport->ObstacleSBPtr))
 
 
288
        {
 
 
289
            /* aha... got one....*/
 
 
290
            moveData->numObstructiveCollisions++;
 
 
291
 
 
 
292
            if(moveData->numObstructiveCollisions > NPC_IMPEDING_COL_THRESHOLD)
 
 
293
            {
 
 
294
                moveData->numObstructiveCollisions = 0;
 
 
295
                details->anySingleObstruction = 1;
 
 
296
                details->environment = 1;
 
 
297
 
 
 
298
                if(nextReport->ObstacleSBPtr)
 
 
299
                {
 
 
300
                    if(nextReport->ObstacleSBPtr->type == I_BehaviourInanimateObject)
 
 
301
                    {
 
 
302
                        if(!nextReport->ObstacleSBPtr->DamageBlock.Indestructable)
 
 
303
                        {
 
 
304
                            INANIMATEOBJECT_STATUSBLOCK* objectstatusptr = nextReport->ObstacleSBPtr->dataptr;
 
 
305
 
 
 
306
                            if(objectstatusptr)
 
 
307
                            {
 
 
308
                                /* aha: an object which the npc can destroy... */
 
 
309
                                *destructableObject = nextReport->ObstacleSBPtr;
 
 
310
                                details->destructableObject = 1;
 
 
311
                                details->environment = 0;
 
 
312
                            }
 
 
313
                        }
 
 
314
                    }
 
 
315
                }
 
 
316
            }
 
 
317
            /* if we have an obstructive collision, then return at this point
 
 
318
            to avoid resetting numObstructiveCollisions. NB we only want to
 
 
319
            record one per frame anyway... */
 
 
320
            return; 
 
 
321
        }
 
 
322
 
 
 
323
        nextReport = nextReport->NextCollisionReportPtr;
 
 
324
    }
 
 
325
 
 
 
326
    /* no obstructions this frame, then ... */
 
 
327
    moveData->numObstructiveCollisions = 0;  
 
 
328
}
 
 
329
 
 
 
330
int NPC_CannotReachTarget(NPC_MOVEMENTDATA *moveData, VECTORCH* thisTarget, VECTORCH* thisVelocity)
 
 
331
{
 
 
332
    assert(moveData);
 
 
333
    assert(thisTarget);
 
 
334
    assert(thisVelocity);
 
 
335
 
 
 
336
    /* if movement data has zero velocity, update and return */
 
 
337
    if(!moveData->lastVelocity.vx && !moveData->lastVelocity.vy && !moveData->lastVelocity.vz)
 
 
338
    {
 
 
339
        moveData->lastVelocity = *thisVelocity;
 
 
340
        moveData->lastTarget = *thisTarget;
 
 
341
        moveData->numReverses = 0;
 
 
342
        return 0;
 
 
343
    }
 
 
344
 
 
 
345
    /* if new velocity is zero, update and return */
 
 
346
    if(!thisVelocity->vx && !thisVelocity->vy && !thisVelocity->vz)
 
 
347
    {
 
 
348
        moveData->lastVelocity = *thisVelocity;
 
 
349
        moveData->lastTarget = *thisTarget;
 
 
350
        moveData->numReverses = 0;
 
 
351
        return 0;
 
 
352
    }
 
 
353
 
 
 
354
    /* if move data target and this target are different, update and return*/
 
 
355
    if((thisTarget->vx != moveData->lastTarget.vx)||
 
 
356
       (thisTarget->vy != moveData->lastTarget.vy)||
 
 
357
       (thisTarget->vz != moveData->lastTarget.vz))
 
 
358
    {
 
 
359
        moveData->lastVelocity = *thisVelocity;
 
 
360
        moveData->lastTarget = *thisTarget;
 
 
361
        moveData->numReverses = 0;
 
 
362
        return 0;
 
 
363
    }
 
 
364
 
 
 
365
    /* at this point we have a previous velocity, a new velocity, 
 
 
366
    and the previous target is the same as the current target... 
 
 
367
    so compare previous and new velocities... */
 
 
368
 
 
 
369
    if(DotProduct(&moveData->lastVelocity, thisVelocity) < (-56000)) /* 30 degrees */
 
 
370
    {
 
 
371
        moveData->lastVelocity = *thisVelocity;
 
 
372
        moveData->lastTarget = *thisTarget;
 
 
373
        moveData->numReverses++;
 
 
374
 
 
 
375
        if(moveData->numReverses > 1)
 
 
376
        {
 
 
377
            moveData->numReverses = 0;
 
 
378
            return 1;
 
 
379
        }
 
 
380
        else 
 
 
381
            return 0;
 
 
382
    }
 
 
383
 
 
 
384
    /* just update */
 
 
385
    moveData->lastVelocity = *thisVelocity;
 
 
386
    moveData->lastTarget = *thisTarget;
 
 
387
    moveData->numReverses = 0;
 
 
388
 
 
 
389
return 0;
 
 
390
}
 
 
391
 
 
 
392
/*------------------------------Patrick 14/2/97-----------------------------------
 
 
393
  Returns direction of movement to avoid obstructive collision in current
 
 
394
  direction of movement...
 
 
395
  -------------------------------------------------------------------------------*/
 
 
396
 
 
 
397
/* in this new version, the direction is taken from the npc's current direction (so
 
 
398
that it will work in 3d, for aliens)... and is returned in velocityDirection */
 
 
399
 
 
 
400
void NPCGetAvoidanceDirection(STRATEGYBLOCK *sbPtr, VECTORCH *velocityDirection, NPC_OBSTRUCTIONREPORT *details)
 
 
401
{
 
 
402
    assert(sbPtr);
 
 
403
    assert(sbPtr->DynPtr);
 
 
404
    assert(velocityDirection);
 
 
405
 
 
 
406
    /* init velcity direction */
 
 
407
    velocityDirection->vx = velocityDirection->vy = velocityDirection->vz = 0;
 
 
408
 
 
 
409
    /* just in case */
 
 
410
    if(!sbPtr->containingModule)
 
 
411
        return; 
 
 
412
 
 
 
413
    if(details->environment || details->destructableObject || details->otherCharacter || details->anySingleObstruction)
 
 
414
    {
 
 
415
        VECTORCH newDirection1;
 
 
416
        VECTORCH newDirection2;
 
 
417
        int dir1dist = 0;
 
 
418
        int dir2dist = 0;
 
 
419
        /* going for a 90 degree turn + back a bit */
 
 
420
 
 
 
421
        /* construct the direction(s)... 
 
 
422
        start with object's local x unit vector (from local coo-ord system in world space) */
 
 
423
        newDirection1.vx = sbPtr->DynPtr->OrientMat.mat11;
 
 
424
        newDirection1.vy = sbPtr->DynPtr->OrientMat.mat12;
 
 
425
        newDirection1.vz = sbPtr->DynPtr->OrientMat.mat13;
 
 
426
        newDirection2.vx = -newDirection1.vx;
 
 
427
        newDirection2.vy = -newDirection1.vy;
 
 
428
        newDirection2.vz = -newDirection1.vz;
 
 
429
        /* ...and add on 1/4 of the -z direction...*/
 
 
430
        newDirection1.vx -= (sbPtr->DynPtr->OrientMat.mat31/4);
 
 
431
        newDirection1.vy -= (sbPtr->DynPtr->OrientMat.mat32/4);
 
 
432
        newDirection1.vz -= (sbPtr->DynPtr->OrientMat.mat33/4);
 
 
433
        newDirection2.vx -= (sbPtr->DynPtr->OrientMat.mat31/4);
 
 
434
        newDirection2.vy -= (sbPtr->DynPtr->OrientMat.mat32/4);
 
 
435
        newDirection2.vz -= (sbPtr->DynPtr->OrientMat.mat33/4);
 
 
436
        Normalise(&newDirection1);
 
 
437
        Normalise(&newDirection2);
 
 
438
 
 
 
439
        /* test how far we could go in each direction... */
 
 
440
        {
 
 
441
            VECTORCH startingPosition = sbPtr->DynPtr->Position;
 
 
442
            VECTORCH testDirn = newDirection1;
 
 
443
 
 
 
444
            CheckForVectorIntersectionWith3dObject(sbPtr->containingModule->m_dptr, &startingPosition, &testDirn);
 
 
445
 
 
 
446
            dir1dist = LOS_ObjectHitPtr ? LOS_Lambda : NPC_MAX_VIEWRANGE;
 
 
447
 
 
 
448
            startingPosition = sbPtr->DynPtr->Position;
 
 
449
            testDirn = newDirection2;
 
 
450
 
 
 
451
            CheckForVectorIntersectionWith3dObject(sbPtr->containingModule->m_dptr, &startingPosition, &testDirn);
 
 
452
 
 
 
453
            dir2dist = LOS_ObjectHitPtr ? LOS_Lambda : NPC_MAX_VIEWRANGE;
 
 
454
        }
 
 
455
 
 
 
456
        *velocityDirection = (dir1dist > dir2dist) ? newDirection1 : newDirection2;
 
 
457
    }
 
 
458
}
 
 
459
 
 
 
460
/*------------------------Patrick 3/2/97---------------------------------
 
 
461
  Sets an NPC's orientation so that it's z axis aligns to a given vector
 
 
462
  (modified from kevin's alien align to velocity routine)
 
 
463
  -----------------------------------------------------------------------*/
 
 
464
 
 
 
465
int NPCOrientateToVector(STRATEGYBLOCK *sbPtr, VECTORCH *zAxisVector, int turnspeed)
 
 
466
{
 
 
467
    int turnThisFrame;
 
 
468
    int orientatedOk = 0;
 
 
469
 
 
 
470
    assert(sbPtr);
 
 
471
    assert(sbPtr->DynPtr);
 
 
472
    assert(zAxisVector);
 
 
473
 
 
 
474
    /* zero vector: nothing to do */
 
 
475
    if(!zAxisVector->vx && !zAxisVector->vy && !zAxisVector->vz)
 
 
476
        return 1;
 
 
477
 
 
 
478
    VECTORCH localZAxisVector = *zAxisVector;
 
 
479
 
 
 
480
    /* rotate world zAxisVector into local space */
 
 
481
    MATRIXCH toLocal = sbPtr->DynPtr->OrientMat;
 
 
482
 
 
 
483
    TransposeMatrixCH(&toLocal);
 
 
484
    RotateVector(&localZAxisVector, &toLocal);
 
 
485
    Normalise(&localZAxisVector);
 
 
486
 
 
 
487
    int maxTurnThisFrame = MUL_FIXED(NormalFrameTime, turnspeed);
 
 
488
    int localZVecEulerY = ArcTan(localZAxisVector.vx, localZAxisVector.vz);
 
 
489
    assert((localZVecEulerY >= 0) && (localZVecEulerY <= 4096));
 
 
490
 
 
 
491
    /* if euler-y is 0 we are already aligned: nothing to do */
 
 
492
    if(!localZVecEulerY || localZVecEulerY == 4096) 
 
 
493
        return 1; 
 
 
494
 
 
 
495
    if(localZVecEulerY > 2048)
 
 
496
    {
 
 
497
        if(localZVecEulerY > (4096 - maxTurnThisFrame))
 
 
498
        {
 
 
499
            turnThisFrame = localZVecEulerY;
 
 
500
            orientatedOk = 1;
 
 
501
        }
 
 
502
        else
 
 
503
        {
 
 
504
            turnThisFrame = (4096 - maxTurnThisFrame);
 
 
505
            orientatedOk = 0;
 
 
506
        }
 
 
507
    }
 
 
508
    else
 
 
509
    {
 
 
510
        if(localZVecEulerY > maxTurnThisFrame)
 
 
511
        {
 
 
512
            turnThisFrame = maxTurnThisFrame;
 
 
513
            orientatedOk = 0;
 
 
514
        }
 
 
515
        else
 
 
516
        {
 
 
517
            turnThisFrame = localZVecEulerY;
 
 
518
            orientatedOk = 1;
 
 
519
        }
 
 
520
    }
 
 
521
 
 
 
522
    /* now convert into a matrix & multiply existing orientation by it ...  */
 
 
523
    {
 
 
524
        MATRIXCH mat;
 
 
525
        int cos = GetCos(turnThisFrame);
 
 
526
        int sin = GetSin(turnThisFrame);
 
 
527
        mat.mat11 = cos;                 
 
 
528
        mat.mat12 = 0;
 
 
529
        mat.mat13 = -sin;
 
 
530
        mat.mat21 = 0;          
 
 
531
        mat.mat22 = 65536;              
 
 
532
        mat.mat23 = 0;          
 
 
533
        mat.mat31 = sin;                
 
 
534
        mat.mat32 = 0;          
 
 
535
        mat.mat33 = cos;                
 
 
536
 
 
 
537
        MatrixMultiply(&sbPtr->DynPtr->OrientMat, &mat, &sbPtr->DynPtr->OrientMat);
 
 
538
        MatrixToEuler(&sbPtr->DynPtr->OrientMat, &sbPtr->DynPtr->OrientEuler);
 
 
539
    }
 
 
540
 
 
 
541
return orientatedOk;
 
 
542
}
 
 
543
 
 
 
544
int NPCOrientateToVector2(STRATEGYBLOCK *sbPtr, VECTORCH *zAxisVector, int turnspeed)
 
 
545
{
 
 
546
    /* zero vector: nothing to do */
 
 
547
    if(!zAxisVector->vx && !zAxisVector->vy && !zAxisVector->vz)
 
 
548
        return 1;
 
 
549
 
 
 
550
    VECTORCH localZAxisVector = *zAxisVector;
 
 
551
 
 
 
552
    /* rotate world zAxisVector into local space */
 
 
553
    MATRIXCH toLocal = sbPtr->DynPtr->OrientMat;
 
 
554
 
 
 
555
    TransposeMatrixCH(&toLocal);
 
 
556
    RotateVector(&localZAxisVector, &toLocal);
 
 
557
    Normalise(&localZAxisVector);
 
 
558
 
 
 
559
    int maxTurnThisFrame = MUL_FIXED(NormalFrameTime, turnspeed);
 
 
560
    int localZVecEulerY = ArcTan(localZAxisVector.vx, localZAxisVector.vz);
 
 
561
    assert((localZVecEulerY >= 0) && (localZVecEulerY <= 4096));
 
 
562
 
 
 
563
    /* if euler-y is 0 we are already aligned: nothing to do */
 
 
564
    if(!localZVecEulerY || localZVecEulerY == 4096) 
 
 
565
        return 1; 
 
 
566
 
 
 
567
    if(localZVecEulerY > 2048)
 
 
568
        return (localZVecEulerY > (4096 - maxTurnThisFrame));
 
 
569
    else
 
 
570
        return !(localZVecEulerY > maxTurnThisFrame);
 
 
571
}
 
 
572
 
 
 
573
/* this function returns a target point for firing a projectile at the player*/
 
 
574
void NPCGetTargetPosition(VECTORCH *targetPoint, STRATEGYBLOCK *target)
 
 
575
{
 
 
576
    assert(target);
 
 
577
    assert(targetPoint);
 
 
578
 
 
 
579
    if (target->DisplayBlock) 
 
 
580
    {
 
 
581
        GetTargetingPointOfObject(target->DisplayBlock, targetPoint);
 
 
582
    }
 
 
583
    else
 
 
584
    {
 
 
585
        *targetPoint = target->DynPtr->Position;
 
 
586
        //targetPoint->vy -= 500; /* Just to be on the safe side. */
 
 
587
    }
 
 
588
}
 
 
589
 
 
 
590
/*------------------------Patrick 31/1/97-----------------------------
 
 
591
  Returns 2d approach velocity and time for given NPC, target, and speed
 
 
592
  targetDirn must be a normalised vector direction.
 
 
593
  --------------------------------------------------------------------*/
 
 
594
 
 
 
595
int NPCSetVelocity(STRATEGYBLOCK *sbPtr, VECTORCH* targetDirn, int in_speed)
 
 
596
{
 
 
597
    int orientated;
 
 
598
    /* Set up speed as local, so we can tamper with it. */
 
 
599
    int accelerationThisFrame;
 
 
600
    int speed = in_speed;
 
 
601
 
 
 
602
    assert(sbPtr);
 
 
603
    assert(sbPtr->DynPtr);
 
 
604
    assert(targetDirn);
 
 
605
 
 
 
606
    /*
 
 
607
    set targetDirn.vy to 0 just in case: if NPCGetTargetDirn is used get the target
 
 
608
    direction, the y component should always be 0, but other target directions may be
 
 
609
    passed...
 
 
610
    */
 
 
611
 
 
 
612
    // Okay, that was old.  But maybe we need to do that unless UseStandardGravity is unset.
 
 
613
 
 
 
614
    switch(sbPtr->type)
 
 
615
    {
 
 
616
        case I_BehaviourMarine:
 
 
617
        {
 
 
618
            MARINE_STATUS_BLOCK *marineStatusPointer = (MARINE_STATUS_BLOCK *)(sbPtr->dataptr);    
 
 
619
            assert(marineStatusPointer);
 
 
620
            accelerationThisFrame = marineStatusPointer->acceleration;
 
 
621
        }
 
 
622
        break;
 
 
623
        case I_BehaviourPredator:
 
 
624
        {
 
 
625
            /* May need to change this based on state at some point? */
 
 
626
            PREDATOR_STATUS_BLOCK *predatorStatusPointer = (PREDATOR_STATUS_BLOCK *)(sbPtr->dataptr);    
 
 
627
            assert(predatorStatusPointer);
 
 
628
 
 
 
629
            const MOVEMENT_DATA *movementData;
 
 
630
            switch(predatorStatusPointer->behaviourState)
 
 
631
            {
 
 
632
                case PBS_Avoidance:
 
 
633
                {
 
 
634
                    if (predatorStatusPointer->lastState != PBS_Wandering)
 
 
635
                    {
 
 
636
                        movementData = GetThisMovementData(MDI_Predator);
 
 
637
                        break;
 
 
638
                    }
 
 
639
                } // no break for you
 
 
640
                case PBS_Wandering:
 
 
641
                {
 
 
642
                    movementData = GetThisMovementData(MDI_Casual_Predator);
 
 
643
                    /* Fix reduced max speed. */
 
 
644
                    speed = movementData->maxSpeed;
 
 
645
                }
 
 
646
                break;
 
 
647
                default:
 
 
648
                    movementData = GetThisMovementData(MDI_Predator);
 
 
649
            }
 
 
650
 
 
 
651
            accelerationThisFrame = movementData->acceleration;
 
 
652
        }
 
 
653
        break;
 
 
654
        case I_BehaviourXenoborg:
 
 
655
        {
 
 
656
            const MOVEMENT_DATA *movementData = GetThisMovementData(MDI_Xenoborg);
 
 
657
            accelerationThisFrame = movementData->acceleration;
 
 
658
        }
 
 
659
        break;
 
 
660
        default:
 
 
661
        {
 
 
662
            if (sbPtr->DynPtr->UseStandardGravity)
 
 
663
            {
 
 
664
                targetDirn->vy = 0;
 
 
665
 
 
 
666
                /* first check for zero direction vector */
 
 
667
                if(!targetDirn->vx && !targetDirn->vz)
 
 
668
                {
 
 
669
                    sbPtr->DynPtr->LinVelocity.vx =  sbPtr->DynPtr->LinVelocity.vy = sbPtr->DynPtr->LinVelocity.vz = 0;
 
 
670
                return 1;
 
 
671
                }
 
 
672
            }
 
 
673
 
 
 
674
            orientated = NPCOrientateToVector(sbPtr, targetDirn, NPC_TURNRATE);   
 
 
675
 
 
 
676
            {
 
 
677
                VECTORCH velocity;
 
 
678
                VECTORCH yDirection;
 
 
679
 
 
 
680
                yDirection.vx = sbPtr->DynPtr->OrientMat.mat21;         
 
 
681
                yDirection.vy = sbPtr->DynPtr->OrientMat.mat22;         
 
 
682
                yDirection.vz = sbPtr->DynPtr->OrientMat.mat23;
 
 
683
 
 
 
684
                int dotProduct = DotProduct(&yDirection,targetDirn);
 
 
685
 
 
 
686
                velocity.vx = targetDirn->vx - MUL_FIXED(yDirection.vx,dotProduct);
 
 
687
                velocity.vy = targetDirn->vy - MUL_FIXED(yDirection.vy,dotProduct);
 
 
688
                velocity.vz = targetDirn->vz - MUL_FIXED(yDirection.vz,dotProduct);
 
 
689
 
 
 
690
                if ( !velocity.vx && !velocity.vy && !velocity.vz)
 
 
691
                {
 
 
692
                    sbPtr->DynPtr->LinVelocity.vx = sbPtr->DynPtr->LinVelocity.vy = sbPtr->DynPtr->LinVelocity.vz = 0;
 
 
693
                    return orientated;
 
 
694
                }
 
 
695
 
 
 
696
                Normalise(&velocity);
 
 
697
 
 
 
698
                sbPtr->DynPtr->LinVelocity.vx = MUL_FIXED(velocity.vx,speed);           
 
 
699
                sbPtr->DynPtr->LinVelocity.vy = MUL_FIXED(velocity.vy,speed);           
 
 
700
                sbPtr->DynPtr->LinVelocity.vz = MUL_FIXED(velocity.vz,speed);                   
 
 
701
            }
 
 
702
        return orientated;
 
 
703
        }
 
 
704
    }
 
 
705
 
 
 
706
    {
 
 
707
        VECTORCH deltaV,targetV,yDirection,movementOffset;
 
 
708
 
 
 
709
        /* Mode 2, for marines 'n' predators.  And xenoborgs. */
 
 
710
 
 
 
711
        /* I'll still believe in 'Speed'... but look up acceleration. */
 
 
712
        /* I'll get that from outside in a minute, okay? */
 
 
713
        /* Assume you're ground-based. */
 
 
714
 
 
 
715
        targetDirn->vy = 0;
 
 
716
 
 
 
717
        /* first check for zero direction vector */
 
 
718
 
 
 
719
        if(!targetDirn->vx && !targetDirn->vz)
 
 
720
        {
 
 
721
            /* For the moment, you can still stop on a dime. */
 
 
722
            sbPtr->DynPtr->LinVelocity.vx = sbPtr->DynPtr->LinVelocity.vy = sbPtr->DynPtr->LinVelocity.vz = 0;
 
 
723
            return 1;
 
 
724
        }
 
 
725
 
 
 
726
        accelerationThisFrame = MUL_FIXED(accelerationThisFrame, NormalFrameTime);
 
 
727
        /* u+at, and all that, wot? */
 
 
728
 
 
 
729
        yDirection.vx = sbPtr->DynPtr->OrientMat.mat21;
 
 
730
        yDirection.vy = sbPtr->DynPtr->OrientMat.mat22;
 
 
731
        yDirection.vz = sbPtr->DynPtr->OrientMat.mat23;
 
 
732
 
 
 
733
        int dotProduct = DotProduct(&yDirection,targetDirn);
 
 
734
 
 
 
735
        targetV.vx = targetDirn->vx - MUL_FIXED(yDirection.vx,dotProduct);
 
 
736
        targetV.vy = targetDirn->vy - MUL_FIXED(yDirection.vy,dotProduct);
 
 
737
        targetV.vz = targetDirn->vz - MUL_FIXED(yDirection.vz,dotProduct);
 
 
738
 
 
 
739
        if ( !targetV.vx && !targetV.vy && !targetV.vz)
 
 
740
        {
 
 
741
            sbPtr->DynPtr->LinVelocity.vx = sbPtr->DynPtr->LinVelocity.vy = sbPtr->DynPtr->LinVelocity.vz = 0;
 
 
742
        return 1;
 
 
743
        }
 
 
744
 
 
 
745
        Normalise(&targetV);
 
 
746
 
 
 
747
        targetV.vx = MUL_FIXED(targetV.vx,speed);
 
 
748
        targetV.vy = MUL_FIXED(targetV.vy,speed);
 
 
749
        targetV.vz = MUL_FIXED(targetV.vz,speed);
 
 
750
 
 
 
751
        deltaV.vx = targetV.vx-sbPtr->DynPtr->LinVelocity.vx;
 
 
752
        deltaV.vy = targetV.vy-sbPtr->DynPtr->LinVelocity.vy;
 
 
753
        deltaV.vz = targetV.vz-sbPtr->DynPtr->LinVelocity.vz;
 
 
754
 
 
 
755
        /* Now, deltaV is what we need to match. */
 
 
756
 
 
 
757
        int deltaVMag = Approximate3dMagnitude(&deltaV);
 
 
758
 
 
 
759
        if (deltaVMag <= accelerationThisFrame)
 
 
760
        {
 
 
761
            int dotP,magV;
 
 
762
            VECTORCH normVelocity;
 
 
763
 
 
 
764
            sbPtr->DynPtr->LinVelocity = targetV;
 
 
765
 
 
 
766
            movementOffset.vx = sbPtr->DynPtr->Position.vx - sbPtr->DynPtr->PrevPosition.vx;
 
 
767
            movementOffset.vy = sbPtr->DynPtr->Position.vy - sbPtr->DynPtr->PrevPosition.vy;
 
 
768
            movementOffset.vz = sbPtr->DynPtr->Position.vz - sbPtr->DynPtr->PrevPosition.vz;
 
 
769
 
 
 
770
            normVelocity = sbPtr->DynPtr->LinVelocity;
 
 
771
            magV = Approximate3dMagnitude(&sbPtr->DynPtr->LinVelocity);
 
 
772
 
 
 
773
            Normalise(&movementOffset);
 
 
774
            Normalise(&normVelocity);
 
 
775
            dotP = DotProduct(&normVelocity,&movementOffset);
 
 
776
 
 
 
777
            /* To get a reasonable value out... */
 
 
778
            if (magV < (speed >> 2))
 
 
779
            {
 
 
780
                orientated = NPCOrientateToVector(sbPtr, &targetV, NPC_TURNRATE);
 
 
781
            }
 
 
782
            else if ((dotP > 40000) || (!movementOffset.vx && !movementOffset.vy && !movementOffset.vz))
 
 
783
            {
 
 
784
                orientated = NPCOrientateToVector(sbPtr, &normVelocity, NPC_TURNRATE);
 
 
785
            }
 
 
786
            else
 
 
787
            {
 
 
788
                orientated = NPCOrientateToVector(sbPtr, &movementOffset, NPC_TURNRATE);
 
 
789
            }
 
 
790
 
 
 
791
        return orientated;
 
 
792
        }
 
 
793
 
 
 
794
        /* If we're here, we can't make the target velocity yet. */
 
 
795
        Normalise(&deltaV);
 
 
796
        deltaV.vx = MUL_FIXED(deltaV.vx,accelerationThisFrame);
 
 
797
        deltaV.vy = MUL_FIXED(deltaV.vy,accelerationThisFrame);
 
 
798
        deltaV.vz = MUL_FIXED(deltaV.vz,accelerationThisFrame);
 
 
799
 
 
 
800
        sbPtr->DynPtr->LinVelocity.vx += deltaV.vx;
 
 
801
        sbPtr->DynPtr->LinVelocity.vy += deltaV.vy;
 
 
802
        sbPtr->DynPtr->LinVelocity.vz += deltaV.vz;
 
 
803
 
 
 
804
        movementOffset.vx = sbPtr->DynPtr->Position.vx - sbPtr->DynPtr->PrevPosition.vx;
 
 
805
        movementOffset.vy = sbPtr->DynPtr->Position.vy - sbPtr->DynPtr->PrevPosition.vy;
 
 
806
        movementOffset.vz = sbPtr->DynPtr->Position.vz - sbPtr->DynPtr->PrevPosition.vz;
 
 
807
 
 
 
808
        VECTORCH normVelocity = sbPtr->DynPtr->LinVelocity;
 
 
809
        int magV = Approximate3dMagnitude(&sbPtr->DynPtr->LinVelocity);
 
 
810
 
 
 
811
        Normalise(&movementOffset);
 
 
812
        Normalise(&normVelocity);
 
 
813
        int dotP = DotProduct(&normVelocity,&movementOffset);
 
 
814
 
 
 
815
        /* To get a reasonable value out... */
 
 
816
        if (magV < (speed >> 2))
 
 
817
        {
 
 
818
            orientated = NPCOrientateToVector(sbPtr, &targetV, NPC_TURNRATE);
 
 
819
        }
 
 
820
        else if ((dotP > 40000) || ((movementOffset.vx == 0) && (movementOffset.vy == 0) && (movementOffset.vz == 0)))
 
 
821
        {
 
 
822
            orientated = NPCOrientateToVector(sbPtr, &normVelocity, NPC_TURNRATE);
 
 
823
        }
 
 
824
        else
 
 
825
        {
 
 
826
            orientated = NPCOrientateToVector(sbPtr, &movementOffset, NPC_TURNRATE);
 
 
827
        }
 
 
828
    }
 
 
829
 
 
 
830
return orientated;
 
 
831
}
 
 
832
 
 
 
833
int AIModuleIsVisible(AIMODULE *aimodule)
 
 
834
{
 
 
835
    /* D'oh! */
 
 
836
    MODULE **module_list = aimodule->m_module_ptrs;
 
 
837
 
 
 
838
    while (*module_list) 
 
 
839
    {
 
 
840
        if (ModuleCurrVisArray[(*module_list)->m_index]) 
 
 
841
            return 1;
 
 
842
 
 
 
843
        module_list++;
 
 
844
    }
 
 
845
 
 
 
846
return 0;
 
 
847
}
 
 
848
 
 
 
849
/*------------------------Patrick 1/2/97-----------------------------
 
 
850
  Tries to find an ep in an adjacent module which can be used as a 
 
 
851
  movement target for NPC. 
 
 
852
  --------------------------------------------------------------------*/
 
 
853
 
 
 
854
static int NPCFindTargetEP(STRATEGYBLOCK *sbPtr, VECTORCH *targetPosn, AIMODULE **targetModule, int alien)
 
 
855
{
 
 
856
    int aTargetExists = 0;
 
 
857
 
 
 
858
    AIMODULE **AdjModuleRefPtr = sbPtr->containingModule->m_aimodule->m_link_ptrs;
 
 
859
 
 
 
860
    /* check if there is a module adjacency list */ 
 
 
861
    if(AdjModuleRefPtr)
 
 
862
    {
 
 
863
        FARENTRYPOINT *bestEp;
 
 
864
        AIMODULE *bestModule = NULL;
 
 
865
        unsigned int bestSmell = 0;
 
 
866
 
 
 
867
        do
 
 
868
        {
 
 
869
            AIMODULE *nextAdjModule = *AdjModuleRefPtr++;                             
 
 
870
 
 
 
871
            if (AIModuleIsVisible(nextAdjModule))
 
 
872
            {
 
 
873
                /* it is adjacent & visible ... */
 
 
874
                FARENTRYPOINT *thisEp = GetAIModuleEP(nextAdjModule, sbPtr->containingModule->m_aimodule);
 
 
875
 
 
 
876
                if(thisEp && !(!alien && thisEp->alien_only))
 
 
877
                {
 
 
878
                    /* ... and has an ep, so test it's pheromone level */
 
 
879
                    if(PherPl_ReadBuf[(nextAdjModule->m_index)] > bestSmell)
 
 
880
                    {
 
 
881
                        bestSmell = PherPl_ReadBuf[(nextAdjModule->m_index)];
 
 
882
                        bestEp = thisEp;
 
 
883
                        bestModule = nextAdjModule;
 
 
884
                        aTargetExists = 1;
 
 
885
                    }
 
 
886
                }
 
 
887
            }
 
 
888
 
 
 
889
        } while(*AdjModuleRefPtr != 0);
 
 
890
 
 
 
891
        if(aTargetExists)
 
 
892
        {
 
 
893
            *targetPosn = bestEp->position;
 
 
894
            *targetModule = bestModule;
 
 
895
            targetPosn->vx += bestModule->m_world.vx;
 
 
896
            targetPosn->vy += bestModule->m_world.vy;
 
 
897
            targetPosn->vz += bestModule->m_world.vz;                       
 
 
898
            assert(bestEp->donorIndex == sbPtr->containingModule->m_aimodule->m_index);                
 
 
899
        }
 
 
900
    }
 
 
901
 
 
 
902
return aTargetExists;
 
 
903
}
 
 
904
 
 
 
905
/*------------------------Patrick 11/2/97-----------------------------
 
 
906
  If we're not in the same module as the player, find an ep for a
 
 
907
  suitable target module, and return this.  If cannot find an ep,
 
 
908
  or if the player is in our module, return the player's position.
 
 
909
  --------------------------------------------------------------------*/
 
 
910
 
 
 
911
void NPCGetMovementTarget(STRATEGYBLOCK *sbPtr, STRATEGYBLOCK *target, VECTORCH *targetPosition,int *targetIsAirduct,int alien)
 
 
912
{       
 
 
913
    assert(sbPtr);
 
 
914
    assert(targetPosition);
 
 
915
    assert(targetIsAirduct);
 
 
916
    assert(target);
 
 
917
 
 
 
918
    if (target == PlayerStatus.sbptr)
 
 
919
    {
 
 
920
        if(sbPtr->containingModule->m_aimodule != PlayerStatus.sbptr->containingModule->m_aimodule)
 
 
921
        {
 
 
922
            VECTORCH epPosn;
 
 
923
            AIMODULE *epModule;
 
 
924
 
 
 
925
            if(NPCFindTargetEP(sbPtr, &epPosn, &epModule, alien))
 
 
926
            {
 
 
927
                *targetPosition = epPosn;
 
 
928
                *targetIsAirduct = ((*epModule->m_module_ptrs)->m_flags & MODULEFLAG_AIRDUCT);
 
 
929
            return;
 
 
930
            }
 
 
931
        }
 
 
932
 
 
 
933
        /* in same module as player, or can't find an entry point */
 
 
934
        //*targetPosition = PlayerStatus.DisplayBlock->ObWorld;
 
 
935
        GetTargetingPointOfObject(PlayerStatus.DisplayBlock, targetPosition);
 
 
936
        *targetIsAirduct = 0;
 
 
937
    }
 
 
938
    else
 
 
939
    {
 
 
940
        /* Improve this presently */
 
 
941
        //*targetPosition = target->DynPtr->Position;
 
 
942
        GetTargetingPointOfObject_Far(target, targetPosition);
 
 
943
        *targetIsAirduct = 0;
 
 
944
    }
 
 
945
}
 
 
946
 
 
 
947
/*------------------------------Patrick 24/3/97-----------------------------------
 
 
948
    Calculates best direction of movement for NPC, from our main target direction:
 
 
949
    1. If on same poly as player, move towards him/her;
 
 
950
    2. Otherwise, find best adjacent floor polygon to move towards our target.
 
 
951
 
 
 
952
    NB works in 2d...
 
 
953
 -------------------------------------------------------------------------------*/
 
 
954
extern int SetupPolygonAccessFromShapeIndex(int shapeIndex);
 
 
955
 
 
 
956
static VECTORCH GMD_myPolyPoints[4];
 
 
957
static int GMD_myPolyNumPoints;
 
 
958
static VECTORCH myPolyEdgePoints[4];
 
 
959
static VECTORCH myPolyEdgeDirections[4];
 
 
960
static VECTORCH myPolyEdgeNormals[4];
 
 
961
static VECTORCH myPolyMidPoint;
 
 
962
static int myPolyEdgeMoveDistances[4];
 
 
963
int ShowPredoStats = 0;
 
 
964
 
 
 
965
static int IsMyPolyRidiculous()
 
 
966
{
 
 
967
    int a = 0;
 
 
968
 
 
 
969
    /* Please make sure the globals are sensible! */
 
 
970
 
 
 
971
    for (; a < GMD_myPolyNumPoints; a++)
 
 
972
    {
 
 
973
        VECTORCH side;
 
 
974
        int sideend;
 
 
975
 
 
 
976
        if (a >= (GMD_myPolyNumPoints-1)) 
 
 
977
            sideend = 0;
 
 
978
        else 
 
 
979
            sideend = a + 1;
 
 
980
 
 
 
981
        side.vx = GMD_myPolyPoints[a].vx-GMD_myPolyPoints[sideend].vx;
 
 
982
        side.vy = GMD_myPolyPoints[a].vy-GMD_myPolyPoints[sideend].vy;
 
 
983
        side.vz = GMD_myPolyPoints[a].vz-GMD_myPolyPoints[sideend].vz;
 
 
984
 
 
 
985
            /* Stoopid! */
 
 
986
        if (Approximate3dMagnitude(&side) < 1000)
 
 
987
            return 1;
 
 
988
    }
 
 
989
 
 
 
990
return 0;
 
 
991
}
 
 
992
 
 
 
993
/* Patrick 12/6/97: This function is an auxilary function to NPCGetMovementDirection(), and
 
 
994
adds a curve to the edge point direction, weighted towards the centre of the current poly */
 
 
995
 
 
 
996
static void NPCFindCurveToEdgePoint(STRATEGYBLOCK *sbPtr, int edgeIndex, VECTORCH *velocityDirection)
 
 
997
{
 
 
998
    VECTORCH curvedPath;
 
 
999
    VECTORCH dirnToCentre;
 
 
1000
 
 
 
1001
    assert(sbPtr);
 
 
1002
    assert(velocityDirection);
 
 
1003
 
 
 
1004
    /* default direction- just in case something goes wrong */
 
 
1005
    *velocityDirection = myPolyEdgeDirections[edgeIndex];
 
 
1006
 
 
 
1007
    /* find dirn to centre of poly */
 
 
1008
    dirnToCentre.vx = myPolyMidPoint.vx - sbPtr->DynPtr->Position.vx;
 
 
1009
    dirnToCentre.vz = myPolyMidPoint.vz - sbPtr->DynPtr->Position.vz;
 
 
1010
    dirnToCentre.vy = 0;
 
 
1011
 
 
 
1012
    if(!dirnToCentre.vx && !dirnToCentre.vz)
 
 
1013
        return;
 
 
1014
 
 
 
1015
    Normalise(&dirnToCentre);
 
 
1016
 
 
 
1017
    /* calculated weighted vector to centre */
 
 
1018
    int weighting = DotProduct(&myPolyEdgeDirections[edgeIndex],&dirnToCentre);
 
 
1019
 
 
 
1020
    if(!weighting)
 
 
1021
        return;
 
 
1022
 
 
 
1023
    dirnToCentre.vx = WideMulNarrowDiv(dirnToCentre.vx,weighting,ONE_FIXED);
 
 
1024
    dirnToCentre.vz = WideMulNarrowDiv(dirnToCentre.vz,weighting,ONE_FIXED);
 
 
1025
 
 
 
1026
    /* add them to find curved direction path */
 
 
1027
    curvedPath.vx = myPolyEdgeDirections[edgeIndex].vx + dirnToCentre.vx;
 
 
1028
    curvedPath.vz = myPolyEdgeDirections[edgeIndex].vz + dirnToCentre.vz;
 
 
1029
    curvedPath.vy = 0;
 
 
1030
    Normalise(&curvedPath);
 
 
1031
    *velocityDirection = curvedPath;        
 
 
1032
}
 
 
1033
 
 
 
1034
/*------------------------Patrick 24/3/97-----------------------------
 
 
1035
  This function is used to determine an intersection between 2 line
 
 
1036
  segments.  The passed segment end points have been transformed into
 
 
1037
  a space where the second segment is aligned to the z axis, and extends
 
 
1038
  from 0 to the passed extent parameter. This makes the intersection
 
 
1039
  test easy...
 
 
1040
  --------------------------------------------------------------------*/
 
 
1041
 
 
 
1042
static int VectorIntersects2dZVector(VECTORCH *vecStart,VECTORCH *vecEnd, int zExtent)
 
 
1043
{
 
 
1044
    int vecIntercept;
 
 
1045
    assert(vecStart);
 
 
1046
    assert(vecEnd);
 
 
1047
 
 
 
1048
    if((vecStart->vx < 0) && (vecEnd->vx < 0))
 
 
1049
        return 0;
 
 
1050
 
 
 
1051
    if((vecStart->vx > 0) && (vecEnd->vx > 0))
 
 
1052
        return 0;
 
 
1053
 
 
 
1054
    if((vecStart->vz < 0) && (vecEnd->vz < 0))
 
 
1055
        return 0;
 
 
1056
 
 
 
1057
    if((vecStart->vz > zExtent)&&(vecEnd->vz > zExtent))
 
 
1058
        return 0;
 
 
1059
 
 
 
1060
    vecIntercept = vecStart->vz + WideMulNarrowDiv((vecEnd->vz - vecStart->vz),vecStart->vx,(vecEnd->vx - vecStart->vx));
 
 
1061
 
 
 
1062
    if((vecIntercept > 0) && (vecIntercept < zExtent))
 
 
1063
        return 1; /* (deliberately ignoring endpoints) */      
 
 
1064
return 0;
 
 
1065
}
 
 
1066
 
 
 
1067
/*------------------------Patrick 28/1/99-----------------------------
 
 
1068
  Tries to find a floor polygon for a given world space location in
 
 
1069
  a given module. Early exit if the location isn't even in the module...
 
 
1070
  --------------------------------------------------------------------*/
 
 
1071
 
 
 
1072
static int CheckMyFloorPoly(VECTORCH* currentPosition, MODULE* currentModule)
 
 
1073
{
 
 
1074
    struct ColPolyTag polygonData;
 
 
1075
    int positionPoints[2];
 
 
1076
    VECTORCH localPosition;
 
 
1077
    int numPolys;
 
 
1078
    int     polyCounter;
 
 
1079
    int polyFound = 0;
 
 
1080
    int polyFoundIndex = 0;
 
 
1081
 
 
 
1082
    assert(currentPosition);
 
 
1083
    assert(currentModule);
 
 
1084
 
 
 
1085
    /* first, get the local position */
 
 
1086
    localPosition.vx = currentPosition->vx - currentModule->m_world.vx;
 
 
1087
    localPosition.vy = currentPosition->vy - currentModule->m_world.vy;
 
 
1088
    localPosition.vz = currentPosition->vz - currentModule->m_world.vz;
 
 
1089
 
 
 
1090
    if(!PointIsInModule(currentModule, &localPosition) )
 
 
1091
    {
 
 
1092
        // Whoops ! I'm looking in the wrong module. Better just forget it.
 
 
1093
        return NPC_GMD_NOPOLY;
 
 
1094
    }
 
 
1095
 
 
 
1096
    /* set up position points for object*/
 
 
1097
    positionPoints[0] = localPosition.vx;
 
 
1098
    positionPoints[1] = localPosition.vz;
 
 
1099
 
 
 
1100
    numPolys = SetupPolygonAccessFromShapeIndex(currentModule->m_mapptr->MapShape);
 
 
1101
    polyCounter = numPolys;
 
 
1102
 
 
 
1103
    /* loop through the item list, then ... */
 
 
1104
    while((polyCounter > 0) && !polyFound)
 
 
1105
    {
 
 
1106
        {
 
 
1107
            extern int **ItemArrayPtr;
 
 
1108
            extern POLYHEADER *PolyheaderPtr;
 
 
1109
            int *itemPtr = *(ItemArrayPtr++);
 
 
1110
            PolyheaderPtr = (POLYHEADER *) itemPtr;
 
 
1111
        }
 
 
1112
 
 
 
1113
        GetPolygonVertices(&polygonData);
 
 
1114
        GetPolygonNormal(&polygonData);
 
 
1115
 
 
 
1116
        /* first of all, reject any that don't have an up normal */
 
 
1117
        if(polygonData.PolyNormal.vy < 0)
 
 
1118
        {
 
 
1119
            /* set up poly points for containment test */
 
 
1120
            int polyPoints[10];
 
 
1121
            int numPtsInPoly = polygonData.NumberOfVertices;
 
 
1122
            int i = 0;
 
 
1123
 
 
 
1124
            for(; i < numPtsInPoly; i++)
 
 
1125
            {
 
 
1126
                polyPoints[(i*2)] = polygonData.PolyPoint[i].vx;
 
 
1127
                polyPoints[((i*2)+1)] = polygonData.PolyPoint[i].vz;
 
 
1128
            }
 
 
1129
 
 
 
1130
            if (PointInPolygon(&positionPoints[0], &polyPoints[0], numPtsInPoly,2))
 
 
1131
            {       
 
 
1132
                polyFound = 1;
 
 
1133
                polyFoundIndex = numPolys - polyCounter;
 
 
1134
            }
 
 
1135
        }
 
 
1136
 
 
 
1137
        polyCounter--;
 
 
1138
    }
 
 
1139
 
 
 
1140
    /* Init some globals */         
 
 
1141
    {
 
 
1142
        int i=0;
 
 
1143
        GMD_myPolyNumPoints = 0;
 
 
1144
 
 
 
1145
        for(; i < 4; i++)
 
 
1146
            GMD_myPolyPoints[i].vx = GMD_myPolyPoints[i].vy = GMD_myPolyPoints[i].vz = -1;
 
 
1147
    }               
 
 
1148
 
 
 
1149
    /* if we haven't found a poly, return NPC_GMD_NOPOLY.  Otherwise, return the index and fill
 
 
1150
    out some globals... */
 
 
1151
 
 
 
1152
    if(!polyFound)
 
 
1153
        return NPC_GMD_NOPOLY;
 
 
1154
 
 
 
1155
    assert(polyFoundIndex >= 0);
 
 
1156
    assert(polyFound < numPolys);
 
 
1157
 
 
 
1158
    GMD_myPolyNumPoints = polygonData.NumberOfVertices;     
 
 
1159
    GMD_myPolyPoints[0] = polygonData.PolyPoint[0];
 
 
1160
    GMD_myPolyPoints[1] = polygonData.PolyPoint[1];
 
 
1161
    GMD_myPolyPoints[2] = polygonData.PolyPoint[2];             
 
 
1162
 
 
 
1163
    if(GMD_myPolyNumPoints > 3) 
 
 
1164
        GMD_myPolyPoints[3] = polygonData.PolyPoint[3];
 
 
1165
 
 
 
1166
return polyFoundIndex;
 
 
1167
}
 
 
1168
 
 
 
1169
void NPCGetMovementDirection(STRATEGYBLOCK *sbPtr, VECTORCH *velocityDirection, VECTORCH *targetPosition, WAYPOINT_MANAGER *waypointManager)
 
 
1170
{
 
 
1171
    VECTORCH targetDirection;
 
 
1172
    int i;
 
 
1173
    int playerPoly = NPC_GMD_NOPOLY;
 
 
1174
    int ourPolyThisFrame = NPC_GMD_NOPOLY;
 
 
1175
 
 
 
1176
    assert(sbPtr);
 
 
1177
    assert(sbPtr->DynPtr);
 
 
1178
    assert(velocityDirection);
 
 
1179
    assert(targetPosition);
 
 
1180
 
 
 
1181
    if (sbPtr->containingModule == NULL) 
 
 
1182
    {
 
 
1183
        /* Oops. */
 
 
1184
        targetDirection = *targetPosition;
 
 
1185
        targetDirection.vx -= sbPtr->DynPtr->Position.vx;
 
 
1186
        targetDirection.vz -= sbPtr->DynPtr->Position.vz;
 
 
1187
        targetDirection.vy = 0;
 
 
1188
        Normalise(&targetDirection);
 
 
1189
        *velocityDirection = targetDirection;
 
 
1190
    return;
 
 
1191
    }
 
 
1192
    else if ((sbPtr->containingModule->m_aimodule->m_waypoints != NULL) && waypointManager) 
 
 
1193
    {
 
 
1194
        if (NPCGetWaypointDirection(sbPtr->containingModule->m_aimodule->m_waypoints, sbPtr, velocityDirection, targetPosition, waypointManager)) 
 
 
1195
            return; /* Success! */
 
 
1196
    }
 
 
1197
 
 
 
1198
    /* First get the (2d) direction of the main target */
 
 
1199
    targetDirection = *targetPosition;
 
 
1200
    targetDirection.vx -= sbPtr->DynPtr->Position.vx;
 
 
1201
    targetDirection.vz -= sbPtr->DynPtr->Position.vz;
 
 
1202
    /* If we got here, we *should* not be a crawling alien... */
 
 
1203
 
 
 
1204
    if ((sbPtr->type == I_BehaviourAlien) && !sbPtr->DynPtr->UseStandardGravity)
 
 
1205
    {
 
 
1206
        targetDirection.vy -= sbPtr->DynPtr->Position.vy;
 
 
1207
    }
 
 
1208
    else
 
 
1209
    {
 
 
1210
        /*
 
 
1211
            // Non-planar adjacency warnings?
 
 
1212
            targetDirection.vy -= sbPtr->DynPtr->Position.vy;
 
 
1213
            Normalise(&targetDirection);
 
 
1214
 
 
 
1215
            if (targetDirection.vy < -46000) 
 
 
1216
                printf("Non-planar adjacency!\n");
 
 
1217
        */
 
 
1218
 
 
 
1219
        targetDirection.vy = 0;
 
 
1220
    }
 
 
1221
 
 
 
1222
    Normalise(&targetDirection);
 
 
1223
 
 
 
1224
    /* first- a hack to cope with stairs and al those little polygons:
 
 
1225
    if we're in stairs just return the target direction. This is okay aslong as we don't 
 
 
1226
    have curved stairs*/
 
 
1227
 
 
 
1228
    if(sbPtr->containingModule->m_flags & MODULEFLAG_STAIRS)
 
 
1229
    {
 
 
1230
        *velocityDirection = targetDirection;
 
 
1231
        return;
 
 
1232
    }
 
 
1233
 
 
 
1234
    playerPoly = CheckMyFloorPoly(&PlayerStatus.DisplayBlock->ObWorld, PlayerStatus.sbptr->containingModule);
 
 
1235
    ourPolyThisFrame = CheckMyFloorPoly(&(sbPtr->DynPtr->Position), sbPtr->containingModule);
 
 
1236
 
 
 
1237
    /* Check for not having a current poly: this seems to happen occasionally
 
 
1238
    around module boundaries- npc just needs a jolt... */
 
 
1239
 
 
 
1240
    if((ourPolyThisFrame == NPC_GMD_NOPOLY) || IsMyPolyRidiculous())
 
 
1241
    {
 
 
1242
        *velocityDirection = targetDirection;
 
 
1243
        return;
 
 
1244
    }
 
 
1245
 
 
 
1246
    /* Now check for player on our poly NB only do this is we are not the player: as this function is used in player demo */    
 
 
1247
 
 
 
1248
    if((sbPtr != PlayerStatus.sbptr) && (sbPtr->containingModule == PlayerStatus.sbptr->containingModule) && (playerPoly !=
NPC_GMD_NOPOLY))
 
 
1249
    {
 
 
1250
        if(playerPoly == ourPolyThisFrame)
 
 
1251
        {
 
 
1252
            /* cripes- we're on the same poly */
 
 
1253
            *velocityDirection = targetDirection;
 
 
1254
            return;
 
 
1255
        }
 
 
1256
    }
 
 
1257
 
 
 
1258
    /* Now get all the data we need:
 
 
1259
    1. World space coords of poly edge mid points
 
 
1260
    2. Directions from npc to those points
 
 
1261
    3. Midpoint of our polygon.
 
 
1262
    3a Some error checking.
 
 
1263
    4. Distances we could move from poly midpoint to edge midpoint before hitting something
 
 
1264
    */ 
 
 
1265
 
 
 
1266
    for(i=0; i < GMD_myPolyNumPoints; i++)
 
 
1267
    {
 
 
1268
        int point1 = i; 
 
 
1269
        int point2 = i+1;               
 
 
1270
 
 
 
1271
        if(point2 >= GMD_myPolyNumPoints)
 
 
1272
            point2 = 0;
 
 
1273
 
 
 
1274
        {
 
 
1275
            /* find the edge out normal - NB this won't work for clockwise polygons (2d) */                 
 
 
1276
            VECTORCH upNormal = {0,-65536,0};
 
 
1277
            VECTORCH edgeVector;
 
 
1278
 
 
 
1279
            edgeVector.vx = GMD_myPolyPoints[point2].vx - GMD_myPolyPoints[point1].vx;
 
 
1280
            edgeVector.vy = 0;
 
 
1281
            edgeVector.vz = GMD_myPolyPoints[point2].vz - GMD_myPolyPoints[point1].vz;
 
 
1282
 
 
 
1283
            CrossProduct(&edgeVector,&upNormal,&myPolyEdgeNormals[i]);
 
 
1284
            Normalise(&myPolyEdgeNormals[i]);
 
 
1285
            assert(myPolyEdgeNormals[i].vy == 0);
 
 
1286
        }
 
 
1287
 
 
 
1288
        /* 1 : Calculate edge midpoint (2d) */
 
 
1289
        myPolyEdgePoints[i].vx = ((GMD_myPolyPoints[point1].vx + GMD_myPolyPoints[point2].vx)/2)+MUL_FIXED(myPolyEdgeNormals[i].vx,50);
 
 
1290
        myPolyEdgePoints[i].vy = 0;     
 
 
1291
        myPolyEdgePoints[i].vz = ((GMD_myPolyPoints[point1].vz + GMD_myPolyPoints[point2].vz)/2)+MUL_FIXED(myPolyEdgeNormals[i].vz,50);         
 
 
1292
        /* Into world space*/
 
 
1293
        myPolyEdgePoints[i].vx += sbPtr->containingModule->m_world.vx;
 
 
1294
        myPolyEdgePoints[i].vz += sbPtr->containingModule->m_world.vz;
 
 
1295
 
 
 
1296
        /* 2 : Directions to those points */
 
 
1297
        myPolyEdgeDirections[i].vx = myPolyEdgePoints[i].vx - sbPtr->DynPtr->Position.vx;
 
 
1298
        myPolyEdgeDirections[i].vy = 0;
 
 
1299
        myPolyEdgeDirections[i].vz = myPolyEdgePoints[i].vz - sbPtr->DynPtr->Position.vz;               
 
 
1300
        Normalise(&myPolyEdgeDirections[i]);
 
 
1301
    }
 
 
1302
 
 
 
1303
    /* 3 : Poly midpoint- actually just an approximation, but doesn't matter 
 
 
1304
       as long as its inside the poly (in world space):
 
 
1305
       NB
 
 
1306
       Quads are done by finding the midpoint of a diagonal.
 
 
1307
       Triangles bisect a side then take the midpoint of that and the third point, else
 
 
1308
       they can end up with a midpoint on one of their sides which buggers things up.*/
 
 
1309
 
 
 
1310
    if(GMD_myPolyNumPoints == 3)
 
 
1311
    {
 
 
1312
        VECTORCH bisect;
 
 
1313
        bisect.vy = ((GMD_myPolyPoints[1].vy + GMD_myPolyPoints[2].vy)/2);
 
 
1314
        bisect.vx = ((GMD_myPolyPoints[1].vx + GMD_myPolyPoints[2].vx)/2);
 
 
1315
        bisect.vz = ((GMD_myPolyPoints[1].vz + GMD_myPolyPoints[2].vz)/2);
 
 
1316
        myPolyMidPoint.vy = ((GMD_myPolyPoints[0].vy + bisect.vy)/2)+sbPtr->containingModule->m_world.vy;
 
 
1317
        myPolyMidPoint.vx = ((GMD_myPolyPoints[0].vx + bisect.vx)/2)+sbPtr->containingModule->m_world.vx;
 
 
1318
        myPolyMidPoint.vz = ((GMD_myPolyPoints[0].vz + bisect.vz)/2)+sbPtr->containingModule->m_world.vz;
 
 
1319
    }
 
 
1320
    else
 
 
1321
    {
 
 
1322
        myPolyMidPoint.vy = ((GMD_myPolyPoints[0].vy + GMD_myPolyPoints[2].vy)/2)+sbPtr->containingModule->m_world.vy;
 
 
1323
        myPolyMidPoint.vx = ((GMD_myPolyPoints[0].vx + GMD_myPolyPoints[2].vx)/2)+sbPtr->containingModule->m_world.vx;
 
 
1324
        myPolyMidPoint.vz = ((GMD_myPolyPoints[0].vz + GMD_myPolyPoints[2].vz)/2)+sbPtr->containingModule->m_world.vz;
 
 
1325
    }
 
 
1326
 
 
 
1327
    /* Error trapping:
 
 
1328
    1. if midpoint is not in our polygon, just move to target
 
 
1329
    2. If main target is in our poly, just move to target
 
 
1330
    3. If any edge points are in our poly, just move to the target also
 
 
1331
    */
 
 
1332
    {
 
 
1333
        int edgePoint[2];
 
 
1334
        int polyPoints[10];
 
 
1335
        int j;
 
 
1336
 
 
 
1337
        for(j=0; j < GMD_myPolyNumPoints; j++)
 
 
1338
        {
 
 
1339
            polyPoints[(j*2)] = GMD_myPolyPoints[j].vx;
 
 
1340
            polyPoints[(j*2)+1] = GMD_myPolyPoints[j].vz;
 
 
1341
        }
 
 
1342
 
 
 
1343
        /* Edge points (in poly local space) */
 
 
1344
        for(j=0; j < GMD_myPolyNumPoints; j++)
 
 
1345
        {
 
 
1346
            edgePoint[0] = myPolyEdgePoints[j].vx - sbPtr->containingModule->m_world.vx;
 
 
1347
            edgePoint[1] = myPolyEdgePoints[j].vz - sbPtr->containingModule->m_world.vz;
 
 
1348
 
 
 
1349
            if(PointInPolygon(&edgePoint[0], &polyPoints[0], GMD_myPolyNumPoints, 2))
 
 
1350
            {
 
 
1351
                /* one of the edge points is inside our poly */
 
 
1352
                *velocityDirection = targetDirection;
 
 
1353
                return;
 
 
1354
            }
 
 
1355
        }
 
 
1356
 
 
 
1357
        /* Mid point (in poly local space) */
 
 
1358
        edgePoint[0] = myPolyMidPoint.vx - sbPtr->containingModule->m_world.vx;
 
 
1359
        edgePoint[1] = myPolyMidPoint.vz - sbPtr->containingModule->m_world.vz;
 
 
1360
 
 
 
1361
        if(!PointInPolygon(&edgePoint[0], &polyPoints[0], GMD_myPolyNumPoints,2))
 
 
1362
        {
 
 
1363
            /* the midpoint is inside our poly */
 
 
1364
            *velocityDirection = targetDirection;
 
 
1365
        return;
 
 
1366
        }
 
 
1367
 
 
 
1368
        /* Target point (in poly local space)*/
 
 
1369
        edgePoint[0] = targetPosition->vx - sbPtr->containingModule->m_world.vx;
 
 
1370
        edgePoint[1] = targetPosition->vz - sbPtr->containingModule->m_world.vz;                
 
 
1371
 
 
 
1372
        if(PointInPolygon(&edgePoint[0], &polyPoints[0], GMD_myPolyNumPoints,2))
 
 
1373
        {
 
 
1374
            /* the main target point is inside our poly:- this shouldn't happen,
 
 
1375
            as we have already tested for the player earlier on */
 
 
1376
            *velocityDirection = targetDirection;
 
 
1377
        return;
 
 
1378
        }
 
 
1379
    }
 
 
1380
 
 
 
1381
    /* 4 : Finally, the distances that we can move from the poly midpoint beyond
 
 
1382
    each edge midpoint before we hit something... 
 
 
1383
    */
 
 
1384
    {
 
 
1385
        for(i=0; i < GMD_myPolyNumPoints; i++)
 
 
1386
        {
 
 
1387
            VECTORCH centreToEdgeVector;
 
 
1388
            int centreToEdgeDistance;
 
 
1389
 
 
 
1390
            centreToEdgeVector.vx = myPolyEdgePoints[i].vx - myPolyMidPoint.vx;
 
 
1391
            centreToEdgeVector.vz = myPolyEdgePoints[i].vz - myPolyMidPoint.vz;
 
 
1392
            centreToEdgeVector.vy = 0;
 
 
1393
            centreToEdgeDistance = Magnitude(&centreToEdgeVector);
 
 
1394
 
 
 
1395
            /* how far we can move along the centre to edge direction */
 
 
1396
            {
 
 
1397
                VECTORCH startingPosition = myPolyMidPoint;
 
 
1398
                VECTORCH testDirn = centreToEdgeVector;
 
 
1399
 
 
 
1400
                /* poly mid point is in 3d world space: we need to do this
 
 
1401
                test at the height of the npc*/
 
 
1402
                startingPosition.vy = myPolyMidPoint.vy - 1500;
 
 
1403
                /*startingPosition.vy = sbPtr->DynPtr->Position.vy;*/
 
 
1404
                /* Normalise the test direction */
 
 
1405
                Normalise(&testDirn);
 
 
1406
 
 
 
1407
                CheckForVectorIntersectionWith3dObject(sbPtr->containingModule->m_dptr, &startingPosition, &testDirn);
 
 
1408
 
 
 
1409
                myPolyEdgeMoveDistances[i] = LOS_ObjectHitPtr ? LOS_Lambda : NPC_MAX_VIEWRANGE;
 
 
1410
            }
 
 
1411
 
 
 
1412
            /* how far we can move beyond the poly edge */
 
 
1413
            myPolyEdgeMoveDistances[i] -= centreToEdgeDistance;
 
 
1414
 
 
 
1415
            /* quick check to eliminate Saturn type dodgy triangle points:
 
 
1416
            by setting move distance to zero, we should never select this edge as our
 
 
1417
            target edge... */
 
 
1418
 
 
 
1419
            {
 
 
1420
                int point1 = i; 
 
 
1421
                int point2 = i+1;               
 
 
1422
 
 
 
1423
                if(point2 >= GMD_myPolyNumPoints)
 
 
1424
                    point2 = 0;           
 
 
1425
 
 
 
1426
                if(     (GMD_myPolyPoints[point1].vx == GMD_myPolyPoints[point2].vx) && 
 
 
1427
                    (GMD_myPolyPoints[point1].vy == GMD_myPolyPoints[point2].vy) && 
 
 
1428
                    (GMD_myPolyPoints[point1].vz == GMD_myPolyPoints[point2].vz))
 
 
1429
                    {
 
 
1430
                        assert(1==0);
 
 
1431
 
 
 
1432
                        myPolyEdgeMoveDistances[i] = 0;
 
 
1433
                    }                       
 
 
1434
            }
 
 
1435
        }
 
 
1436
    }
 
 
1437
 
 
 
1438
    /* Now the crucial bit:- try to find an edge in our polygon that we can move towards
 
 
1439
       and which is intersected by the vector from the mid point to the main target. If
 
 
1440
       we find one, this is our edge! */ 
 
 
1441
 
 
 
1442
    for(i=0; i < GMD_myPolyNumPoints; i++)
 
 
1443
    {
 
 
1444
        if(myPolyEdgeMoveDistances[i] > NPC_MIN_MOVEFROMPOLYDIST)
 
 
1445
        {
 
 
1446
            int vecExtent;
 
 
1447
            int ePoint1, ePoint2;
 
 
1448
            VECTORCH endPoint1, endPoint2;
 
 
1449
            VECTORCH edgeVector;
 
 
1450
            MATRIXCH edgeMatrix;
 
 
1451
 
 
 
1452
            ePoint1 = i;
 
 
1453
            ePoint2 = i+1;
 
 
1454
 
 
 
1455
            if(ePoint2>=GMD_myPolyNumPoints)
 
 
1456
                ePoint2 = 0;
 
 
1457
 
 
 
1458
            edgeVector.vx = GMD_myPolyPoints[ePoint2].vx - GMD_myPolyPoints[ePoint1].vx;
 
 
1459
            edgeVector.vy = 0;
 
 
1460
            edgeVector.vz = GMD_myPolyPoints[ePoint2].vz - GMD_myPolyPoints[ePoint1].vz;
 
 
1461
 
 
 
1462
            Normalise(&edgeVector);
 
 
1463
            vecExtent = Magnitude(&edgeVector);
 
 
1464
 
 
 
1465
            /* This IS the right way around */
 
 
1466
            edgeMatrix.mat11 = myPolyEdgeNormals[i].vx;
 
 
1467
            edgeMatrix.mat21 = 0;
 
 
1468
            edgeMatrix.mat31 = myPolyEdgeNormals[i].vz;
 
 
1469
            edgeMatrix.mat12 = 0;
 
 
1470
            edgeMatrix.mat22 = 65536;
 
 
1471
            edgeMatrix.mat32 = 0;
 
 
1472
            edgeMatrix.mat13 = edgeVector.vx;
 
 
1473
            edgeMatrix.mat23 = 0;
 
 
1474
            edgeMatrix.mat33 = edgeVector.vz;
 
 
1475
 
 
 
1476
            /* set up the test vector */
 
 
1477
            endPoint1 = myPolyMidPoint;
 
 
1478
            endPoint1.vx = endPoint1.vx - sbPtr->containingModule->m_world.vx - GMD_myPolyPoints[ePoint1].vx;
 
 
1479
            endPoint1.vy = 0;
 
 
1480
            endPoint1.vz = endPoint1.vz - sbPtr->containingModule->m_world.vz - GMD_myPolyPoints[ePoint1].vz;
 
 
1481
            RotateVector(&endPoint1, &edgeMatrix);
 
 
1482
 
 
 
1483
            endPoint2 = *targetPosition;
 
 
1484
            endPoint2.vx = endPoint2.vx - sbPtr->containingModule->m_world.vx - GMD_myPolyPoints[ePoint1].vx;
 
 
1485
            endPoint2.vy = 0;
 
 
1486
            endPoint2.vz = endPoint2.vz - sbPtr->containingModule->m_world.vz - GMD_myPolyPoints[ePoint1].vz;
 
 
1487
            RotateVector(&endPoint2, &edgeMatrix);
 
 
1488
 
 
 
1489
            if(VectorIntersects2dZVector(&endPoint1,&endPoint2,vecExtent))
 
 
1490
            {
 
 
1491
                /* that'll do nicely */
 
 
1492
 
 
 
1493
                /* test */
 
 
1494
               //         if(sbPtr == PlayerStatus.sbptr)
 
 
1495
             //               printf("intersection test\n");
 
 
1496
 
 
 
1497
                NPCFindCurveToEdgePoint(sbPtr,i,velocityDirection);
 
 
1498
 
 
 
1499
                return;
 
 
1500
            }
 
 
1501
        }
 
 
1502
    }
 
 
1503
 
 
 
1504
    /* test */
 
 
1505
    //        if(sbPtr == PlayerStatus.sbptr)
 
 
1506
    //              printf("nearest edge test\n");
 
 
1507
 
 
 
1508
    /* Didn't find an intersection edge, so just pick the nearest 
 
 
1509
      edge point that we can traverse */
 
 
1510
    {
 
 
1511
        int directionFound = 0;
 
 
1512
        VECTORCH bestDirection;
 
 
1513
        int closestDistance = 1000000; /* something very big */
 
 
1514
 
 
 
1515
        for(i=0; i < GMD_myPolyNumPoints; i++)
 
 
1516
        {
 
 
1517
            if(myPolyEdgeMoveDistances[i] > NPC_MIN_MOVEFROMPOLYDIST)
 
 
1518
            {
 
 
1519
                int myDist = (VectorDistance(&myPolyEdgePoints[i],targetPosition));
 
 
1520
 
 
 
1521
                if(myDist < closestDistance)
 
 
1522
                {
 
 
1523
                    bestDirection = myPolyEdgeDirections[i];
 
 
1524
                    closestDistance = myDist;
 
 
1525
                    directionFound = 1;
 
 
1526
                }
 
 
1527
            }
 
 
1528
        }
 
 
1529
 
 
 
1530
        /* return best direction, if we have one */
 
 
1531
        if(directionFound)
 
 
1532
        {
 
 
1533
            assert(bestDirection.vy == 0);
 
 
1534
            *velocityDirection = bestDirection;
 
 
1535
            return;
 
 
1536
        }
 
 
1537
    }
 
 
1538
 
 
 
1539
    /* We have utterly failed to find a suitable direction */
 
 
1540
    *velocityDirection = targetDirection;
 
 
1541
}
 
 
1542
 
 
 
1543
/* Patrick 23/8/97 -----------------------------------------------------
 
 
1544
A couple of functions for wandering
 
 
1545
-----------------------------------------------------------------------*/
 
 
1546
void NPC_InitWanderData(NPC_WANDERDATA *wanderData)
 
 
1547
{
 
 
1548
    assert(wanderData);
 
 
1549
    wanderData->currentModule = NPC_NOWANDERMODULE;
 
 
1550
    wanderData->worldPosition.vx = wanderData->worldPosition.vy = wanderData->worldPosition.vz = 0;
 
 
1551
}
 
 
1552
 
 
 
1553
/* Patrick: 26/8/97
 
 
1554
Finding a suitable target module- look thro' all the visible non-airduct modules 
 
 
1555
connected to the npc's current module. pick one, and use it's ep as a 
 
 
1556
target.  
 
 
1557
We take a random adjacent ep as our target, but reject the most 'backward' one
 
 
1558
(compared to our last velocity, as recorded in npc_movedata) as our last choice
 
 
1559
*/
 
 
1560
 
 
 
1561
void NPC_FindAIWanderTarget(STRATEGYBLOCK *sbPtr, NPC_WANDERDATA *wanderData, NPC_MOVEMENTDATA *moveData, int alien)
 
 
1562
{
 
 
1563
    AIMODULE* chosenModule = NULL;
 
 
1564
    VECTORCH chosenEpWorld;
 
 
1565
    int numFound = 0;
 
 
1566
 
 
 
1567
    AIMODULE* worstModule = NULL;
 
 
1568
    VECTORCH worstEpWorld;
 
 
1569
    int worstEpDot;
 
 
1570
 
 
 
1571
    AIMODULE **AdjModuleRefPtr;
 
 
1572
    VECTORCH lastVelocityDirection;
 
 
1573
    int gotLastVelocityDirection = 0;
 
 
1574
 
 
 
1575
    assert(sbPtr);
 
 
1576
    assert(sbPtr->DynPtr);
 
 
1577
    assert(wanderData);
 
 
1578
    assert(moveData);
 
 
1579
 
 
 
1580
    /* init the wander data block now, and we only have to fill in the correct
 
 
1581
    values if we get them... */
 
 
1582
    NPC_InitWanderData(wanderData);
 
 
1583
 
 
 
1584
    /* do we have a current module? */
 
 
1585
    if(!sbPtr->containingModule->m_aimodule)
 
 
1586
        return; /* no containing module */
 
 
1587
 
 
 
1588
    AdjModuleRefPtr = sbPtr->containingModule->m_aimodule->m_link_ptrs;     
 
 
1589
    /* check if there is a module adjacency list */ 
 
 
1590
 
 
 
1591
    if(!AdjModuleRefPtr)
 
 
1592
        return;
 
 
1593
 
 
 
1594
    /* try to get our last velocity direction */
 
 
1595
    if(moveData->lastVelocity.vx || moveData->lastVelocity.vz || moveData->lastVelocity.vy)
 
 
1596
    {
 
 
1597
        lastVelocityDirection = moveData->lastVelocity;
 
 
1598
        Normalise(&lastVelocityDirection);
 
 
1599
        gotLastVelocityDirection = 1;
 
 
1600
    }
 
 
1601
    else
 
 
1602
    {
 
 
1603
        gotLastVelocityDirection = 0;
 
 
1604
    }
 
 
1605
 
 
 
1606
    /* if we've got a previous velocity, go through each adjacent module, 
 
 
1607
    and try to find the worst one */                        
 
 
1608
 
 
 
1609
    while(*AdjModuleRefPtr != 0)
 
 
1610
    {
 
 
1611
        AIMODULE *nextAdjModule = *AdjModuleRefPtr;                             
 
 
1612
 
 
 
1613
        if (AIModuleIsVisible(nextAdjModule) && !((*(nextAdjModule->m_module_ptrs))->m_flags & MODULEFLAG_AIRDUCT) && (nextAdjModule
!= moveData->lastModule))
 
 
1614
        {
 
 
1615
            /* it is adjacent & visible & not an airduct:
 
 
1616
            try to find the ep position from this module... */
 
 
1617
            FARENTRYPOINT *thisEp = GetAIModuleEP(nextAdjModule, sbPtr->containingModule->m_aimodule);
 
 
1618
 
 
 
1619
            if(thisEp)
 
 
1620
            {
 
 
1621
                if (!(!alien && thisEp->alien_only))
 
 
1622
                {
 
 
1623
                    /* aha. an ep!... */ 
 
 
1624
                    VECTORCH thisEpWorld = thisEp->position;
 
 
1625
 
 
 
1626
                    thisEpWorld.vx += nextAdjModule->m_world.vx;
 
 
1627
                    thisEpWorld.vy += nextAdjModule->m_world.vy;
 
 
1628
                    thisEpWorld.vz += nextAdjModule->m_world.vz;                    
 
 
1629
 
 
 
1630
                    if(gotLastVelocityDirection)
 
 
1631
                    {
 
 
1632
                        VECTORCH thisEpDirection;
 
 
1633
                        int thisEpDot;
 
 
1634
 
 
 
1635
                        thisEpDirection = thisEpWorld;
 
 
1636
                        thisEpDirection.vx -= sbPtr->DynPtr->Position.vx;
 
 
1637
                        thisEpDirection.vy -= sbPtr->DynPtr->Position.vy;
 
 
1638
                        thisEpDirection.vz -= sbPtr->DynPtr->Position.vz;
 
 
1639
                        Normalise(&thisEpDirection);
 
 
1640
                        thisEpDot = DotProduct(&thisEpDirection,&lastVelocityDirection);
 
 
1641
 
 
 
1642
                        if(!worstModule)
 
 
1643
                        {
 
 
1644
                            worstModule = nextAdjModule;
 
 
1645
                            worstEpWorld = thisEpWorld;
 
 
1646
                            worstEpDot = thisEpDot;                                                 
 
 
1647
                        }
 
 
1648
                        else
 
 
1649
                        {
 
 
1650
                            numFound++;
 
 
1651
 
 
 
1652
                            if(thisEpDot<worstEpDot)
 
 
1653
                            {
 
 
1654
                                /* worse than our worst, so meld current worst with current,
 
 
1655
                                and set new worst */
 
 
1656
 
 
 
1657
                                if(FastRandom()%numFound == 0)
 
 
1658
                                {
 
 
1659
                                    /* take this one */
 
 
1660
                                    chosenModule = worstModule;
 
 
1661
                                    chosenEpWorld = worstEpWorld;
 
 
1662
                                }
 
 
1663
 
 
 
1664
                                worstModule = nextAdjModule;
 
 
1665
                                worstEpWorld = thisEpWorld;
 
 
1666
                                worstEpDot = thisEpDot;                                                 
 
 
1667
                            }
 
 
1668
                            else
 
 
1669
                            {
 
 
1670
                                /* better than our worst... */
 
 
1671
                                if(FastRandom()%numFound == 0)
 
 
1672
                                {
 
 
1673
                                    /* take this one */
 
 
1674
                                    chosenModule = nextAdjModule;
 
 
1675
                                    chosenEpWorld = thisEpWorld;
 
 
1676
                                }
 
 
1677
                            }
 
 
1678
                        }
 
 
1679
                    }
 
 
1680
                    else
 
 
1681
                    {
 
 
1682
                        /* don't bother with worst... */
 
 
1683
                        numFound++;
 
 
1684
 
 
 
1685
                        if(FastRandom() % numFound == 0)
 
 
1686
                        {
 
 
1687
                            /* take this one */
 
 
1688
                            chosenModule = nextAdjModule;
 
 
1689
                            chosenEpWorld = thisEpWorld;
 
 
1690
                        }
 
 
1691
                    }
 
 
1692
                }
 
 
1693
            }
 
 
1694
        }
 
 
1695
 
 
 
1696
        AdjModuleRefPtr++;
 
 
1697
    }
 
 
1698
 
 
 
1699
    if(chosenModule)
 
 
1700
    {
 
 
1701
        assert(numFound >= 1);
 
 
1702
        wanderData->currentModule = sbPtr->containingModule->m_aimodule->m_index;
 
 
1703
        wanderData->worldPosition = chosenEpWorld;
 
 
1704
    }
 
 
1705
    else if(worstModule)
 
 
1706
    {
 
 
1707
        wanderData->currentModule = sbPtr->containingModule->m_aimodule->m_index;
 
 
1708
        wanderData->worldPosition = worstEpWorld;
 
 
1709
    }
 
 
1710
}
 
 
1711
 
 
 
1712
#define NEARLINK_QUEUE_LENGTH 100
 
 
1713
 
 
 
1714
typedef struct nl_route_queue
 
 
1715
{
 
 
1716
    int depth;
 
 
1717
    AIMODULE *aimodule;
 
 
1718
    AIMODULE *first_step;
 
 
1719
 
 
 
1720
} NL_ROUTE_QUEUE;
 
 
1721
 
 
 
1722
NL_ROUTE_QUEUE NearLink_Route_Queue[NEARLINK_QUEUE_LENGTH];
 
 
1723
 
 
 
1724
int NL_Queue_End,NL_Queue_Exec;
 
 
1725
 
 
 
1726
AIMODULE *GetNextModuleForLink_Core(AIMODULE *source, AIMODULE *target, int max_depth, int visibility_check, int alien)
 
 
1727
{
 
 
1728
    /* Recursively search AIModule tree, trying to connect source and target. *
 
 
1729
     * Return NULL on failure. */
 
 
1730
 
 
 
1731
    if (source == target)
 
 
1732
        return(source);
 
 
1733
 
 
 
1734
    /* Clear the start. */
 
 
1735
 
 
 
1736
    NearLink_Route_Queue[0].depth = 0;
 
 
1737
    NearLink_Route_Queue[0].aimodule = source;
 
 
1738
    NearLink_Route_Queue[0].first_step = NULL;
 
 
1739
    NearLink_Route_Queue[1].aimodule = NULL; /* To set a standard. */
 
 
1740
 
 
 
1741
    NL_Queue_End = 1;
 
 
1742
    NL_Queue_Exec = 0;
 
 
1743
 
 
 
1744
    RouteFinder_CallsThisFrame++;
 
 
1745
 
 
 
1746
    while (NearLink_Route_Queue[NL_Queue_Exec].aimodule != NULL)
 
 
1747
    {
 
 
1748
        AIMODULE *thisModule = NearLink_Route_Queue[NL_Queue_Exec].aimodule;
 
 
1749
        AIMODULE **AdjModuleRefPtr = thisModule->m_link_ptrs;
 
 
1750
 
 
 
1751
        if(AdjModuleRefPtr)     /* check that there is a list of adjacent modules */
 
 
1752
        {
 
 
1753
            while(*AdjModuleRefPtr != 0)
 
 
1754
            {
 
 
1755
                /* Probably want some validity test for the link. */
 
 
1756
                if (AIModuleIsPhysical(*AdjModuleRefPtr)
 
 
1757
                    && AIModuleAdmitsPheromones(*AdjModuleRefPtr)
 
 
1758
                    && CheckAdjacencyValidity((*AdjModuleRefPtr), thisModule, alien)
 
 
1759
                    && (!visibility_check || IsAIModuleVisibleFromAIModule(source, *AdjModuleRefPtr)))
 
 
1760
                    {
 
 
1761
 
 
 
1762
                    /* Is this the target? */
 
 
1763
                    if (*AdjModuleRefPtr == target)
 
 
1764
                    {
 
 
1765
                        /* Yes!!! */
 
 
1766
                        return (NearLink_Route_Queue[NL_Queue_Exec].first_step) ? (NearLink_Route_Queue[NL_Queue_Exec].first_step) : target;
 
 
1767
                    }
 
 
1768
                    else if ( (NearLink_Route_Queue[NL_Queue_Exec].depth < max_depth)
 
 
1769
                        &&( /* Test for 'used this time round' */
 
 
1770
                            ((*AdjModuleRefPtr)->RouteFinder_FrameStamp != GlobalFrameCounter)
 
 
1771
                            ||((*AdjModuleRefPtr)->RouteFinder_IterationNumber != RouteFinder_CallsThisFrame)))
 
 
1772
                    {
 
 
1773
                        /* Add to queue. */
 
 
1774
                        NearLink_Route_Queue[NL_Queue_End].aimodule = (*AdjModuleRefPtr);
 
 
1775
                        NearLink_Route_Queue[NL_Queue_End].depth = NearLink_Route_Queue[NL_Queue_Exec].depth+1;
 
 
1776
 
 
 
1777
                        /* Remember first step. */
 
 
1778
                        if (NearLink_Route_Queue[NL_Queue_Exec].first_step == NULL)
 
 
1779
                            NearLink_Route_Queue[NL_Queue_End].first_step = (*AdjModuleRefPtr);
 
 
1780
                        else
 
 
1781
                            NearLink_Route_Queue[NL_Queue_End].first_step = NearLink_Route_Queue[NL_Queue_Exec].first_step;
 
 
1782
 
 
 
1783
                        /* Stamp as used. */
 
 
1784
                        (*AdjModuleRefPtr)->RouteFinder_FrameStamp = GlobalFrameCounter;
 
 
1785
                        (*AdjModuleRefPtr)->RouteFinder_IterationNumber = RouteFinder_CallsThisFrame;
 
 
1786
                        NL_Queue_End++;
 
 
1787
 
 
 
1788
                        if (NL_Queue_End >= NEARLINK_QUEUE_LENGTH)
 
 
1789
                        {
 
 
1790
                            NL_Queue_End = 0;
 
 
1791
                            //printf("Wrapping Nearlink Queue!\n");
 
 
1792
                        }
 
 
1793
 
 
 
1794
                        NearLink_Route_Queue[NL_Queue_End].aimodule = NULL;
 
 
1795
 
 
 
1796
                        if (NL_Queue_End == NL_Queue_Exec)
 
 
1797
                        {
 
 
1798
                            printf("Oh, no.  NearLinkQueue screwed.  NL_Queue_End=%d, depth =
%d\n",NL_Queue_End,NearLink_Route_Queue[NL_Queue_Exec].depth);
 
 
1799
                            assert(NL_Queue_End!=NL_Queue_Exec); //if this happens the queue probably needs to be longer
 
 
1800
                        }
 
 
1801
                    }
 
 
1802
                }
 
 
1803
 
 
 
1804
                /* next adjacent module reference pointer */
 
 
1805
                AdjModuleRefPtr++;
 
 
1806
            }
 
 
1807
        }
 
 
1808
 
 
 
1809
        /* Done all the links. */
 
 
1810
 
 
 
1811
        NL_Queue_Exec++;
 
 
1812
        if (NL_Queue_Exec >= NEARLINK_QUEUE_LENGTH)
 
 
1813
            NL_Queue_Exec = 0;
 
 
1814
    }
 
 
1815
 
 
 
1816
/* Still here?  Must have hit the end, then. */
 
 
1817
return NULL;
 
 
1818
}
 
 
1819
 
 
 
1820
AIMODULE *GetNextModuleForLink(AIMODULE *source,AIMODULE *target,int max_depth,int alien) 
 
 
1821
{
 
 
1822
    return GetNextModuleForLink_Core(source, target, max_depth, 0, alien);
 
 
1823
}
 
 
1824
 
 
 
1825
int PathArraySize;
 
 
1826
PATHHEADER* PathArray;
 
 
1827
 
 
 
1828
int GetNextModuleInPath(int current_module, int path) 
 
 
1829
{
 
 
1830
    assert(path>=0 && path < PathArraySize);
 
 
1831
    assert(PathArray);
 
 
1832
    assert(PathArray[path].path_length);
 
 
1833
 
 
 
1834
return (current_module + 1) % PathArray[path].path_length;
 
 
1835
}
 
 
1836
 
 
 
1837
AIMODULE *TranslatePathIndex(int current_module, int path) 
 
 
1838
{
 
 
1839
    assert(path >= 0 && path < PathArraySize);
 
 
1840
    assert(PathArray);
 
 
1841
    assert(current_module < PathArray[path].path_length);
 
 
1842
 
 
 
1843
return (PathArray[path].modules_in_path[current_module]);
 
 
1844
}
 
 
1845
 
 
 
1846
/* Death Shell */
 
 
1847
 
 
 
1848
static int CheckDeathValidity(HMODELCONTROLLER *controller, const SECTION *TemplateRoot, DEATH_DATA *ThisDeath, int wound_flags, int priority_wounds,
 
 
1849
    int hurtiness, HIT_FACING *facing, int burning, int crouching, int electrical) 
 
 
1850
{
 
 
1851
    if (crouching)
 
 
1852
    {
 
 
1853
        if (!ThisDeath->Crouching)
 
 
1854
            return 0;
 
 
1855
    }
 
 
1856
    else
 
 
1857
    {
 
 
1858
        if (ThisDeath->Crouching)
 
 
1859
            return 0;
 
 
1860
    }
 
 
1861
 
 
 
1862
    if (burning) 
 
 
1863
    {
 
 
1864
        if (!ThisDeath->Burning)
 
 
1865
            return 0;
 
 
1866
    } 
 
 
1867
    else 
 
 
1868
    {
 
 
1869
        if (ThisDeath->Burning)
 
 
1870
            return 0;
 
 
1871
    }
 
 
1872
 
 
 
1873
    if (electrical)
 
 
1874
    {
 
 
1875
        if (!ThisDeath->Electrical)
 
 
1876
            return 0;
 
 
1877
    }
 
 
1878
    else 
 
 
1879
    {
 
 
1880
        if (ThisDeath->Electrical)
 
 
1881
            return 0;
 
 
1882
    }
 
 
1883
 
 
 
1884
    switch(hurtiness)
 
 
1885
    {
 
 
1886
        case 0:
 
 
1887
        default:
 
 
1888
            if (ThisDeath->MinorBoom) 
 
 
1889
                return 0;
 
 
1890
 
 
 
1891
            if (ThisDeath->MajorBoom) 
 
 
1892
                return 0;
 
 
1893
            break;
 
 
1894
        case 1:
 
 
1895
            if (!ThisDeath->MinorBoom) 
 
 
1896
                return 0;
 
 
1897
            break;
 
 
1898
        case 2:
 
 
1899
            if (!ThisDeath->MajorBoom)
 
 
1900
                return 0;
 
 
1901
    }
 
 
1902
 
 
 
1903
    /* Facing.  Complex one... */
 
 
1904
    /* Input facing must contain at least all the flags in the death. */
 
 
1905
    if (facing)
 
 
1906
    {
 
 
1907
        if (ThisDeath->Facing.Front && !facing->Front) 
 
 
1908
            return 0;
 
 
1909
 
 
 
1910
        if (ThisDeath->Facing.Back && !facing->Back)
 
 
1911
            return 0;
 
 
1912
 
 
 
1913
        if (ThisDeath->Facing.Left && !facing->Left)
 
 
1914
            return 0;
 
 
1915
 
 
 
1916
        if (ThisDeath->Facing.Right && !facing->Right)
 
 
1917
            return 0;
 
 
1918
    }
 
 
1919
    else
 
 
1920
    {
 
 
1921
        if ( ThisDeath->Facing.Front || ThisDeath->Facing.Back || ThisDeath->Facing.Left || ThisDeath->Facing.Right ) 
 
 
1922
            return 0;
 
 
1923
    }
 
 
1924
 
 
 
1925
    /* Wound flags.  Also quite odd. */
 
 
1926
    /* If wound_flags are specified in the death, the input must contain them. */
 
 
1927
    if (ThisDeath->wound_flags) 
 
 
1928
    {
 
 
1929
        if ((ThisDeath->wound_flags & wound_flags) != ThisDeath->wound_flags)
 
 
1930
            return 0;
 
 
1931
    }
 
 
1932
 
 
 
1933
    /* Priority wound flags.  As above, but backwards. */
 
 
1934
    /* If the input has priority wounds, the death must contain them. */
 
 
1935
    if (priority_wounds) 
 
 
1936
    {
 
 
1937
        if ((ThisDeath->priority_wounds & priority_wounds) != priority_wounds)
 
 
1938
            return 0;
 
 
1939
    }
 
 
1940
 
 
 
1941
    /* Finally, sequence validity. */
 
 
1942
    if (ThisDeath->Template) 
 
 
1943
    {
 
 
1944
        if (!HModelSequence_Exists_FromRoot(TemplateRoot, ThisDeath->Sequence_Type, ThisDeath->Sub_Sequence))
 
 
1945
            return 0;
 
 
1946
    }
 
 
1947
    else
 
 
1948
    {
 
 
1949
        if (!HModelSequence_Exists(controller, ThisDeath->Sequence_Type, ThisDeath->Sub_Sequence))
 
 
1950
            return 0;
 
 
1951
    }
 
 
1952
 
 
 
1953
return 1;
 
 
1954
}
 
 
1955
 
 
 
1956
static int CountValidDeaths(HMODELCONTROLLER *controller, const SECTION *TemplateRoot,DEATH_DATA *FirstDeath,int wound_flags,int priority_wounds,
 
 
1957
    int hurtiness,HIT_FACING *facing,int burning,int crouching, int electrical) 
 
 
1958
{
 
 
1959
    int number_of_candidates = 0;
 
 
1960
    DEATH_DATA *this_death = FirstDeath;
 
 
1961
 
 
 
1962
    while (this_death->Sequence_Type >= 0) 
 
 
1963
    {
 
 
1964
        if (CheckDeathValidity(controller, TemplateRoot, this_death ,wound_flags, priority_wounds, hurtiness, facing, burning, crouching, electrical))
 
 
1965
            number_of_candidates++;
 
 
1966
 
 
 
1967
        this_death++;
 
 
1968
    }
 
 
1969
 
 
 
1970
return number_of_candidates;
 
 
1971
}
 
 
1972
 
 
 
1973
DEATH_DATA *GetThisDeath(HMODELCONTROLLER *controller, const SECTION *TemplateRoot,DEATH_DATA *FirstDeath,int wound_flags,int priority_wounds,
 
 
1974
    int hurtiness,HIT_FACING *facing,int burning,int crouching, int electrical, int index) 
 
 
1975
{
 
 
1976
    /* Extract 'index' from the valid deaths. */
 
 
1977
    DEATH_DATA *this_death = FirstDeath;
 
 
1978
    int number = 0;
 
 
1979
 
 
 
1980
    while (this_death->Sequence_Type >= 0) 
 
 
1981
    {
 
 
1982
        if (CheckDeathValidity(controller, TemplateRoot, this_death, wound_flags, priority_wounds, hurtiness, facing, burning, crouching, electrical)) 
 
 
1983
        {
 
 
1984
            if (number == index) 
 
 
1985
                return this_death;
 
 
1986
            else
 
 
1987
                number++;
 
 
1988
        }
 
 
1989
 
 
 
1990
        this_death++;
 
 
1991
    }
 
 
1992
 
 
 
1993
return NULL;
 
 
1994
}
 
 
1995
 
 
 
1996
DEATH_DATA *GetThisDeath_FromCode(DEATH_DATA *FirstDeath,int code) 
 
 
1997
{
 
 
1998
    /* Extract 'code' from the valid deaths. */
 
 
1999
    DEATH_DATA *this_death = FirstDeath;
 
 
2000
 
 
 
2001
    while (this_death->Sequence_Type >= 0) 
 
 
2002
    {
 
 
2003
        if (this_death->Multiplayer_Code == code) 
 
 
2004
            return this_death;
 
 
2005
 
 
 
2006
        this_death++;
 
 
2007
    }
 
 
2008
 
 
 
2009
return NULL;
 
 
2010
}
 
 
2011
 
 
 
2012
DEATH_DATA *GetThisDeath_FromUniqueCode(int code) 
 
 
2013
{
 
 
2014
    extern DEATH_DATA Predator_Special_SelfDestruct_Death;
 
 
2015
 
 
 
2016
    DEATH_DATA* this_death = NULL;
 
 
2017
 
 
 
2018
    switch (code >> 16)
 
 
2019
    {
 
 
2020
        case 0:
 
 
2021
            this_death = &Alien_Deaths[0];
 
 
2022
        break;
 
 
2023
        case 1:
 
 
2024
            this_death = &Marine_Deaths[0];
 
 
2025
        break;
 
 
2026
        case 2:
 
 
2027
            return &Predator_Special_SelfDestruct_Death;
 
 
2028
        case 3:
 
 
2029
            this_death = &Predator_Deaths[0];
 
 
2030
        break;
 
 
2031
        case 4:
 
 
2032
            this_death = &Xenoborg_Deaths[0];
 
 
2033
        break;
 
 
2034
        default:
 
 
2035
            return 0;
 
 
2036
    }
 
 
2037
 
 
 
2038
    while (this_death->Sequence_Type >= 0) 
 
 
2039
    {
 
 
2040
        if (this_death->Unique_Code == code) 
 
 
2041
               return this_death;
 
 
2042
 
 
 
2043
        this_death++;
 
 
2044
    }
 
 
2045
 
 
 
2046
return 0;
 
 
2047
}
 
 
2048
 
 
 
2049
static DEATH_DATA *GetDeathSequence(HMODELCONTROLLER *controller, const SECTION *TemplateRoot, DEATH_DATA *FirstDeath, int wound_flags, int priority_wounds,
 
 
2050
    int hurtiness, HIT_FACING *facing, int burning, int crouching, int electrical) 
 
 
2051
{
 
 
2052
    int index;
 
 
2053
    int number_of_candidates = 0;
 
 
2054
    int use_priority_wounds = priority_wounds;
 
 
2055
    int use_wound_flags = wound_flags;
 
 
2056
    int use_hurtiness = hurtiness;
 
 
2057
    HIT_FACING *use_facing = facing;
 
 
2058
    int use_burning = burning;
 
 
2059
    int use_crouching = crouching;
 
 
2060
    int use_electrical = electrical;
 
 
2061
 
 
 
2062
    while (!number_of_candidates) 
 
 
2063
    {
 
 
2064
        /* Iterate, making simplifications, until there are valid deaths. */
 
 
2065
 
 
 
2066
number_of_candidates = CountValidDeaths(controller, TemplateRoot, FirstDeath, use_wound_flags, use_priority_wounds, use_hurtiness, use_facing, use_burning,
use_crouching, use_electrical);
 
 
2067
 
 
 
2068
        if (!number_of_candidates)
 
 
2069
        {
 
 
2070
            /* Right.  Make a change.  Priority wounds first. */
 
 
2071
            if (use_priority_wounds)
 
 
2072
            {
 
 
2073
                use_priority_wounds = 0;
 
 
2074
                continue;
 
 
2075
            }
 
 
2076
            /* Wound flags next. */
 
 
2077
            if (use_wound_flags != 0xffffffff) 
 
 
2078
            {
 
 
2079
                use_wound_flags = 0xffffffff;
 
 
2080
                continue;
 
 
2081
            }
 
 
2082
            /* Now facing. */
 
 
2083
            if (use_facing)
 
 
2084
            {
 
 
2085
                use_facing = NULL;
 
 
2086
                continue;
 
 
2087
            }
 
 
2088
            /* Now hurtiness. */
 
 
2089
            if (use_hurtiness)
 
 
2090
            {
 
 
2091
                use_hurtiness--;
 
 
2092
                continue;
 
 
2093
            }
 
 
2094
            /* Now electrical. */
 
 
2095
            if (use_electrical)
 
 
2096
            {
 
 
2097
                use_electrical = 0;
 
 
2098
                continue;
 
 
2099
            }
 
 
2100
            /* Finally, burning. */
 
 
2101
            if (use_burning)
 
 
2102
            {
 
 
2103
                use_burning = 0;
 
 
2104
                continue;
 
 
2105
            }
 
 
2106
            /* Only crouch is left! */
 
 
2107
            //puts("DEATH SELECTION FAILURE!\n");
 
 
2108
            if (use_crouching)
 
 
2109
            {
 
 
2110
                use_crouching = 0;
 
 
2111
                continue;
 
 
2112
            }
 
 
2113
            //puts("I REALLY MEAN IT!\n");
 
 
2114
            /* Here goes nothing. */
 
 
2115
        return FirstDeath;
 
 
2116
        }
 
 
2117
    }
 
 
2118
 
 
 
2119
    /* Right, by now we should have a number of candidates. */
 
 
2120
    assert(number_of_candidates);
 
 
2121
 
 
 
2122
    index = FastRandom() % number_of_candidates;
 
 
2123
 
 
 
2124
return GetThisDeath(controller, TemplateRoot, FirstDeath, use_wound_flags, use_priority_wounds, use_hurtiness, use_facing, use_burning, use_crouching,
use_electrical, index);
 
 
2125
}
 
 
2126
 
 
 
2127
DEATH_DATA *GetAlienDeathSequence(HMODELCONTROLLER *controller, const SECTION *TemplateRoot,int wound_flags,int priority_wounds,
 
 
2128
int hurtiness,HIT_FACING *facing,int burning,int crouching, int electrical) 
 
 
2129
{
 
 
2130
    return GetDeathSequence(controller, TemplateRoot, Alien_Deaths, wound_flags, priority_wounds, hurtiness, facing, burning, crouching, electrical);
 
 
2131
}
 
 
2132
 
 
 
2133
DEATH_DATA *GetMarineDeathSequence(HMODELCONTROLLER *controller, const SECTION *TemplateRoot,int wound_flags,int priority_wounds,
 
 
2134
int hurtiness,HIT_FACING *facing,int burning,int crouching, int electrical) 
 
 
2135
{
 
 
2136
    return GetDeathSequence(controller, TemplateRoot, Marine_Deaths, wound_flags, priority_wounds, hurtiness, facing, burning, crouching, electrical);
 
 
2137
}
 
 
2138
 
 
 
2139
DEATH_DATA *GetPredatorDeathSequence(HMODELCONTROLLER *controller, const SECTION *TemplateRoot,int wound_flags,int priority_wounds,
 
 
2140
int hurtiness,HIT_FACING *facing,int burning,int crouching,int electrical) 
 
 
2141
{
 
 
2142
    return GetDeathSequence(controller, TemplateRoot, Predator_Deaths, wound_flags, priority_wounds, hurtiness, facing, burning, crouching, electrical);
 
 
2143
}
 
 
2144
 
 
 
2145
DEATH_DATA *GetXenoborgDeathSequence(HMODELCONTROLLER *controller, const SECTION *TemplateRoot,int wound_flags,int priority_wounds,
 
 
2146
int hurtiness,HIT_FACING *facing,int burning,int crouching,int electrical) 
 
 
2147
{
 
 
2148
    return GetDeathSequence(controller, TemplateRoot, Xenoborg_Deaths, wound_flags, priority_wounds, hurtiness, facing, burning, crouching, electrical);
 
 
2149
}
 
 
2150
 
 
 
2151
static int CheckAttackValidity(HMODELCONTROLLER *controller,ATTACK_DATA *ThisAttack,int wound_flags, int crouching,int pouncing) 
 
 
2152
{
 
 
2153
    if (crouching)
 
 
2154
    {
 
 
2155
        if (!ThisAttack->Crouching)
 
 
2156
            return 0;
 
 
2157
    }
 
 
2158
    else 
 
 
2159
    {
 
 
2160
        if (ThisAttack->Crouching)
 
 
2161
            return 0;
 
 
2162
    }
 
 
2163
 
 
 
2164
    if (pouncing) 
 
 
2165
    {
 
 
2166
        if (!ThisAttack->Pouncing)
 
 
2167
            return 0;
 
 
2168
    }
 
 
2169
    else 
 
 
2170
    {
 
 
2171
        if (ThisAttack->Pouncing)
 
 
2172
            return 0;
 
 
2173
    }
 
 
2174
 
 
 
2175
    /* Wound flags.  Quite odd, and different to deaths. */
 
 
2176
    /* If wound_flags are specified in the death, the input must NOT contain them. */
 
 
2177
 
 
 
2178
    if (ThisAttack->wound_flags && (ThisAttack->wound_flags & wound_flags))
 
 
2179
            return 0;
 
 
2180
 
 
 
2181
    /* Finally, sequence validity. */
 
 
2182
    if (!HModelSequence_Exists(controller,ThisAttack->Sequence_Type,ThisAttack->Sub_Sequence)) 
 
 
2183
        return 0;
 
 
2184
 
 
 
2185
return 1;
 
 
2186
}
 
 
2187
 
 
 
2188
static int CountValidAttacks(HMODELCONTROLLER *controller,ATTACK_DATA *FirstAttack,int wound_flags, int crouching, int pouncing) 
 
 
2189
{
 
 
2190
    ATTACK_DATA *this_attack = FirstAttack;
 
 
2191
    int number_of_candidates = 0;
 
 
2192
 
 
 
2193
    while (this_attack->Sequence_Type >= 0) 
 
 
2194
    {
 
 
2195
        if (CheckAttackValidity(controller,this_attack,wound_flags,crouching,pouncing))
 
 
2196
            number_of_candidates++;
 
 
2197
 
 
 
2198
        this_attack++;
 
 
2199
    }
 
 
2200
 
 
 
2201
return number_of_candidates;
 
 
2202
}
 
 
2203
 
 
 
2204
ATTACK_DATA *GetThisAttack(HMODELCONTROLLER *controller,ATTACK_DATA *FirstAttack,int wound_flags, int crouching, int pouncing, int index) 
 
 
2205
{
 
 
2206
    /* Extract 'index' from the valid attacks. */
 
 
2207
    ATTACK_DATA *this_attack = FirstAttack;
 
 
2208
    int number = 0;
 
 
2209
 
 
 
2210
    while (this_attack->Sequence_Type >= 0) 
 
 
2211
    {
 
 
2212
        if (CheckAttackValidity(controller,this_attack,wound_flags,crouching,pouncing)) 
 
 
2213
        {
 
 
2214
            if (number == index)
 
 
2215
                return this_attack;
 
 
2216
            else
 
 
2217
                number++;
 
 
2218
        }
 
 
2219
 
 
 
2220
        this_attack++;
 
 
2221
    }
 
 
2222
 
 
 
2223
return NULL;
 
 
2224
}
 
 
2225
 
 
 
2226
ATTACK_DATA *GetThisAttack_FromUniqueCode(int code)
 
 
2227
{
 
 
2228
    extern ATTACK_DATA Alien_Special_Gripping_Attack;
 
 
2229
    //search for an attack using a code that should be unique across all attacks
 
 
2230
    //(used for loading)
 
 
2231
 
 
 
2232
    if(code < 0)
 
 
2233
        return NULL;
 
 
2234
 
 
 
2235
    ///try the alien attacks
 
 
2236
    ATTACK_DATA *this_attack = &Alien_Attacks[0];
 
 
2237
 
 
 
2238
    while (this_attack->Sequence_Type >= 0) 
 
 
2239
    {
 
 
2240
        if (this_attack->Unique_Code == code)
 
 
2241
        {
 
 
2242
            return this_attack;
 
 
2243
            break;
 
 
2244
        }
 
 
2245
 
 
 
2246
        this_attack++;
 
 
2247
    }
 
 
2248
 
 
 
2249
    //try the wristblade attacks
 
 
2250
    this_attack = &Wristblade_Attacks[0];
 
 
2251
 
 
 
2252
    while (this_attack->Sequence_Type >= 0) 
 
 
2253
    {
 
 
2254
        if (this_attack->Unique_Code == code) 
 
 
2255
        {
 
 
2256
            return this_attack;
 
 
2257
            break;
 
 
2258
        }
 
 
2259
 
 
 
2260
        this_attack++;
 
 
2261
    }
 
 
2262
 
 
 
2263
    //try the staff attacks
 
 
2264
    this_attack = &PredStaff_Attacks[0];
 
 
2265
 
 
 
2266
    while (this_attack->Sequence_Type >= 0) 
 
 
2267
    {
 
 
2268
        if (this_attack->Unique_Code == code) 
 
 
2269
        {
 
 
2270
            return this_attack;
 
 
2271
            break;
 
 
2272
        }
 
 
2273
 
 
 
2274
        this_attack++;
 
 
2275
    }
 
 
2276
 
 
 
2277
    //try gripping attack
 
 
2278
    if(Alien_Special_Gripping_Attack.Unique_Code == code)
 
 
2279
        return &Alien_Special_Gripping_Attack;
 
 
2280
 
 
 
2281
//no such attack
 
 
2282
return NULL;
 
 
2283
}
 
 
2284
 
 
 
2285
static ATTACK_DATA *GetAttackSequence(HMODELCONTROLLER *controller,ATTACK_DATA *FirstAttack,int wound_flags,int crouching, int pouncing) 
 
 
2286
{
 
 
2287
    int number_of_candidates = 0;
 
 
2288
    int use_crouching = crouching;
 
 
2289
    int use_wound_flags = wound_flags;
 
 
2290
 
 
 
2291
    while (!number_of_candidates) 
 
 
2292
    {
 
 
2293
        /* Iterate, making simplifications, until there are valid deaths. */
 
 
2294
        number_of_candidates = CountValidAttacks(controller,FirstAttack,use_wound_flags,use_crouching,pouncing);
 
 
2295
 
 
 
2296
        if (!number_of_candidates) 
 
 
2297
        {
 
 
2298
            /* Wound flags first. */
 
 
2299
            if (use_wound_flags != 0)
 
 
2300
            {
 
 
2301
                use_wound_flags = 0;
 
 
2302
                continue;
 
 
2303
            }
 
 
2304
            /* Only crouch is left! */
 
 
2305
            if (use_crouching)
 
 
2306
            {
 
 
2307
                use_crouching = 0;
 
 
2308
                continue;
 
 
2309
            }
 
 
2310
            /* Now, pounce is absolutely inviolate. */
 
 
2311
            if (pouncing)
 
 
2312
            {
 
 
2313
                /* If we're looking for a pounce, and there is none, return NULL. */
 
 
2314
                return NULL;
 
 
2315
            }
 
 
2316
           //GADGET_NewOnScreenMessage("ATTACK SELECTION FAILURE!\n");
 
 
2317
           /* Here goes nothing. */
 
 
2318
        return FirstAttack;
 
 
2319
        }
 
 
2320
    }
 
 
2321
 
 
 
2322
    /* Right, by now we should have a number of candidates. */
 
 
2323
    assert(number_of_candidates);
 
 
2324
 
 
 
2325
return GetThisAttack(controller, FirstAttack, use_wound_flags, use_crouching, pouncing, FastRandom() % number_of_candidates);
 
 
2326
}
 
 
2327
 
 
 
2328
ATTACK_DATA *GetAlienAttackSequence(HMODELCONTROLLER *controller,int wound_flags,int crouching) 
 
 
2329
{
 
 
2330
        return GetAttackSequence(controller, Alien_Attacks, wound_flags, crouching, 0);
 
 
2331
}
 
 
2332
 
 
 
2333
ATTACK_DATA *GetAlienPounceAttack(HMODELCONTROLLER *controller,int wound_flags,int crouching) 
 
 
2334
{
 
 
2335
        return GetAttackSequence(controller, Alien_Attacks, wound_flags, crouching, 1);
 
 
2336
}
 
 
2337
 
 
 
2338
ATTACK_DATA *GetWristbladeAttackSequence(HMODELCONTROLLER *controller,int wound_flags,int crouching) 
 
 
2339
{
 
 
2340
        return GetAttackSequence(controller, Wristblade_Attacks, wound_flags, crouching, 0);
 
 
2341
}
 
 
2342
 
 
 
2343
ATTACK_DATA *GetPredStaffAttackSequence(HMODELCONTROLLER *controller,int wound_flags,int crouching) 
 
 
2344
{
 
 
2345
        return GetAttackSequence(controller, PredStaff_Attacks, wound_flags, crouching, 0);
 
 
2346
}
 
 
2347
 
 
 
2348
AIMODULE *NearNPC_GetTargetAIModuleForRetreat(STRATEGYBLOCK *sbPtr, NPC_MOVEMENTDATA *moveData)
 
 
2349
{
 
 
2350
    extern unsigned int PlayerSmell;
 
 
2351
 
 
 
2352
    AIMODULE* targetModule = NULL;
 
 
2353
    unsigned int targetSmell = PlayerSmell + 1;     /* should be higher than any smell anywhere this frame */
 
 
2354
    unsigned int targetNumAdj = 0;
 
 
2355
    int targetEpDot = -ONE_FIXED;
 
 
2356
    VECTORCH lastVelocityDirection;
 
 
2357
    int gotLastVelocityDirection = 0;
 
 
2358
 
 
 
2359
    assert(sbPtr);
 
 
2360
 
 
 
2361
    if(sbPtr->containingModule == NULL)
 
 
2362
        return targetModule;  
 
 
2363
 
 
 
2364
    AIMODULE **AdjModuleRefPtr = sbPtr->containingModule->m_aimodule->m_link_ptrs;
 
 
2365
 
 
 
2366
    /* try to get our last velocity direction */
 
 
2367
    if(moveData->lastVelocity.vx || moveData->lastVelocity.vz || moveData->lastVelocity.vy)
 
 
2368
    {
 
 
2369
        lastVelocityDirection = moveData->lastVelocity;
 
 
2370
        Normalise(&lastVelocityDirection);
 
 
2371
        gotLastVelocityDirection = 1;
 
 
2372
    }
 
 
2373
 
 
 
2374
    /* check that there is a list of adjacent modules, and that it is not empty (ie points to zero) */
 
 
2375
 
 
 
2376
    if(AdjModuleRefPtr)     
 
 
2377
    {
 
 
2378
        while(*AdjModuleRefPtr != 0)
 
 
2379
        {
 
 
2380
            /* get the index */
 
 
2381
            int AdjModuleIndex = (*AdjModuleRefPtr)->m_index;
 
 
2382
            unsigned int AdjModuleSmell = PherPl_ReadBuf[AdjModuleIndex];
 
 
2383
            FARENTRYPOINT *thisEp = GetAIModuleEP((*AdjModuleRefPtr), sbPtr->containingModule->m_aimodule);
 
 
2384
            int thisEpDot = -ONE_FIXED;
 
 
2385
            int chooseThisOne = 0;
 
 
2386
 
 
 
2387
            if(thisEp)
 
 
2388
            {
 
 
2389
                /* aha. an ep!... */ 
 
 
2390
                VECTORCH thisEpWorld = thisEp->position;
 
 
2391
 
 
 
2392
                thisEpWorld.vx += (*AdjModuleRefPtr)->m_world.vx;
 
 
2393
                thisEpWorld.vy += (*AdjModuleRefPtr)->m_world.vy;
 
 
2394
                thisEpWorld.vz += (*AdjModuleRefPtr)->m_world.vz;                       
 
 
2395
 
 
 
2396
                if(gotLastVelocityDirection)
 
 
2397
                {
 
 
2398
                    VECTORCH thisEpDirection;
 
 
2399
 
 
 
2400
                    thisEpDirection = thisEpWorld;
 
 
2401
                    thisEpDirection.vx -= sbPtr->DynPtr->Position.vx;
 
 
2402
                    thisEpDirection.vy -= sbPtr->DynPtr->Position.vy;
 
 
2403
                    thisEpDirection.vz -= sbPtr->DynPtr->Position.vz;
 
 
2404
                    Normalise(&thisEpDirection);
 
 
2405
                    thisEpDot = DotProduct(&thisEpDirection,&lastVelocityDirection);
 
 
2406
                }
 
 
2407
            }
 
 
2408
 
 
 
2409
            /* if this adjacent module's smell value is lower than
 
 
2410
            the current 'highest smell' record the new module as the
 
 
2411
            target.  
 
 
2412
            Tie break on best direction. */
 
 
2413
 
 
 
2414
            if (!targetModule) 
 
 
2415
            {
 
 
2416
                chooseThisOne = 1;
 
 
2417
            }
 
 
2418
            else 
 
 
2419
            {
 
 
2420
                if (AdjModuleSmell < targetSmell) 
 
 
2421
                {
 
 
2422
                    chooseThisOne = 1;
 
 
2423
                }
 
 
2424
                else if (AdjModuleSmell == targetSmell) 
 
 
2425
                {
 
 
2426
                    if (thisEpDot > targetEpDot) 
 
 
2427
                        chooseThisOne = 1;
 
 
2428
                }
 
 
2429
            }
 
 
2430
 
 
 
2431
            if (chooseThisOne)
 
 
2432
            {
 
 
2433
                targetSmell = PherPl_ReadBuf[AdjModuleIndex];
 
 
2434
                targetModule = *AdjModuleRefPtr;
 
 
2435
                targetNumAdj = NumAdjacentModules(*AdjModuleRefPtr);                                                    
 
 
2436
                targetEpDot = thisEpDot;
 
 
2437
            }
 
 
2438
            /* next adjacent module reference pointer */
 
 
2439
            AdjModuleRefPtr++;
 
 
2440
        }
 
 
2441
    }
 
 
2442
 
 
 
2443
return targetModule;
 
 
2444
}
 
 
2445
 
 
 
2446
static AIMODULE *General_GetRetreatModule_Core(AIMODULE *source,int max_depth) 
 
 
2447
{
 
 
2448
    AIMODULE **AdjModuleRefPtr;
 
 
2449
    AIMODULE *deepest_target = NULL;
 
 
2450
    NL_ROUTE_QUEUE deepest_route;
 
 
2451
 
 
 
2452
    /* Note this DOES NOT set the CallsThisFrame variable.  That MUST be set before the call. */
 
 
2453
 
 
 
2454
    /* Clear the start. */
 
 
2455
 
 
 
2456
    deepest_route.depth = 0;
 
 
2457
    deepest_route.aimodule = NULL;
 
 
2458
    deepest_route.first_step = NULL;
 
 
2459
 
 
 
2460
    NearLink_Route_Queue[0].depth = 0;
 
 
2461
    NearLink_Route_Queue[0].aimodule = source;
 
 
2462
    NearLink_Route_Queue[0].first_step = NULL;
 
 
2463
    NearLink_Route_Queue[1].aimodule = NULL; /* To set a standard. */
 
 
2464
 
 
 
2465
    NL_Queue_End = 1;
 
 
2466
    NL_Queue_Exec = 0;
 
 
2467
 
 
 
2468
    while (NearLink_Route_Queue[NL_Queue_Exec].aimodule!=NULL) 
 
 
2469
    {
 
 
2470
        AIMODULE *thisModule = NearLink_Route_Queue[NL_Queue_Exec].aimodule;
 
 
2471
 
 
 
2472
        AdjModuleRefPtr = thisModule->m_link_ptrs;
 
 
2473
 
 
 
2474
        if(AdjModuleRefPtr)     /* check that there is a list of adjacent modules */
 
 
2475
        {
 
 
2476
            while(*AdjModuleRefPtr != 0)
 
 
2477
            {
 
 
2478
                /* Probably want some validity test for the link. */
 
 
2479
                if (AIModuleIsPhysical(*AdjModuleRefPtr) && AIModuleAdmitsPheromones(*AdjModuleRefPtr))
 
 
2480
                {
 
 
2481
                    /* No visibility check? */
 
 
2482
                    /* Consider depth? */
 
 
2483
 
 
 
2484
                    if (NearLink_Route_Queue[NL_Queue_Exec].depth < deepest_route.depth) 
 
 
2485
                    {
 
 
2486
                        deepest_route= NearLink_Route_Queue[NL_Queue_Exec];
 
 
2487
                        deepest_target = (*AdjModuleRefPtr);
 
 
2488
                    }
 
 
2489
 
 
 
2490
                    /* Process link. */
 
 
2491
                    if ((NearLink_Route_Queue[NL_Queue_Exec].depth < max_depth)
 
 
2492
                        &&( /* Test for 'used this time round' */
 
 
2493
                            ((*AdjModuleRefPtr)->RouteFinder_FrameStamp!=GlobalFrameCounter)
 
 
2494
                            ||((*AdjModuleRefPtr)->RouteFinder_IterationNumber!=RouteFinder_CallsThisFrame)
 
 
2495
                        ))
 
 
2496
                    {
 
 
2497
                        /* Add to queue. */
 
 
2498
                        NearLink_Route_Queue[NL_Queue_End].aimodule = (*AdjModuleRefPtr);
 
 
2499
                        NearLink_Route_Queue[NL_Queue_End].depth = NearLink_Route_Queue[NL_Queue_Exec].depth+1;
 
 
2500
                        /* Remember first step. */
 
 
2501
 
 
 
2502
                        if (NearLink_Route_Queue[NL_Queue_Exec].first_step == NULL) 
 
 
2503
                            NearLink_Route_Queue[NL_Queue_End].first_step = (*AdjModuleRefPtr);
 
 
2504
                        else 
 
 
2505
                            NearLink_Route_Queue[NL_Queue_End].first_step = NearLink_Route_Queue[NL_Queue_Exec].first_step;
 
 
2506
 
 
 
2507
                        /* Stamp as used. */
 
 
2508
                        (*AdjModuleRefPtr)->RouteFinder_FrameStamp = GlobalFrameCounter;
 
 
2509
                        (*AdjModuleRefPtr)->RouteFinder_IterationNumber = RouteFinder_CallsThisFrame;
 
 
2510
                        NL_Queue_End++;
 
 
2511
 
 
 
2512
                        if (NL_Queue_End >= NEARLINK_QUEUE_LENGTH) 
 
 
2513
                        {
 
 
2514
                            NL_Queue_End = 0;
 
 
2515
                            //printf("Wrapping Nearlink Queue!\n");
 
 
2516
                        }
 
 
2517
 
 
 
2518
                        NearLink_Route_Queue[NL_Queue_End].aimodule=NULL;
 
 
2519
 
 
 
2520
                        if (NL_Queue_End == NL_Queue_Exec) 
 
 
2521
                        {
 
 
2522
    printf("Oh, no.  NearLinkQueue screwed.  NL_Queue_End=%d, depth = %d\n",NL_Queue_End,NearLink_Route_Queue[NL_Queue_Exec].depth);
 
 
2523
    assert(NL_Queue_End!=NL_Queue_Exec); //if this happens the queue probably needs to be longer
 
 
2524
                        }
 
 
2525
                    }
 
 
2526
                    else if (NearLink_Route_Queue[NL_Queue_Exec].depth >= max_depth) 
 
 
2527
                    {
 
 
2528
                        /* That's well deep.  Let's return. */
 
 
2529
                        return(*AdjModuleRefPtr);
 
 
2530
                    }
 
 
2531
                }
 
 
2532
                /* next adjacent module reference pointer */
 
 
2533
                AdjModuleRefPtr++;
 
 
2534
            }
 
 
2535
        }
 
 
2536
 
 
 
2537
        /* Done all the links. */
 
 
2538
        NL_Queue_Exec++;
 
 
2539
        if (NL_Queue_Exec >= NEARLINK_QUEUE_LENGTH)
 
 
2540
            NL_Queue_Exec = 0;
 
 
2541
    }
 
 
2542
 
 
 
2543
    /* Split up for easier debugging... */
 
 
2544
    if (deepest_target) 
 
 
2545
        return deepest_target;
 
 
2546
    else
 
 
2547
        return NULL; /* There's nowhere to retreat to! */
 
 
2548
}
 
 
2549
 
 
 
2550
AIMODULE *General_GetAIModuleForRetreat(STRATEGYBLOCK *sbPtr,AIMODULE *fearModule,int max_depth) 
 
 
2551
{
 
 
2552
    AIMODULE **AdjModuleRefPtr;
 
 
2553
    int success = 0;
 
 
2554
 
 
 
2555
    assert(sbPtr->containingModule);
 
 
2556
    AIMODULE *my_module = sbPtr->containingModule->m_aimodule;
 
 
2557
    assert(my_module);
 
 
2558
 
 
 
2559
    if (fearModule == NULL) 
 
 
2560
        return NULL;
 
 
2561
 
 
 
2562
    /* Step one: search down till we get to my_module, checking off modules. */
 
 
2563
 
 
 
2564
    if (my_module == fearModule) 
 
 
2565
    {
 
 
2566
        /* Cripes! */
 
 
2567
        AIMODULE *targetModule;
 
 
2568
        RouteFinder_CallsThisFrame++;
 
 
2569
        targetModule = General_GetRetreatModule_Core(my_module, max_depth);
 
 
2570
    return targetModule;
 
 
2571
    }
 
 
2572
 
 
 
2573
    /* Clear the start. */
 
 
2574
 
 
 
2575
    NearLink_Route_Queue[0].depth = 0;
 
 
2576
    NearLink_Route_Queue[0].aimodule = fearModule;
 
 
2577
    NearLink_Route_Queue[0].first_step = NULL;
 
 
2578
    NearLink_Route_Queue[1].aimodule = NULL; /* To set a standard. */
 
 
2579
 
 
 
2580
    NL_Queue_End = 1;
 
 
2581
    NL_Queue_Exec = 0;
 
 
2582
 
 
 
2583
    /* Hijack RouteFinder. */       
 
 
2584
    RouteFinder_CallsThisFrame++;
 
 
2585
 
 
 
2586
    while (NearLink_Route_Queue[NL_Queue_Exec].aimodule != NULL)
 
 
2587
    {
 
 
2588
        AIMODULE *thisModule = NearLink_Route_Queue[NL_Queue_Exec].aimodule;
 
 
2589
 
 
 
2590
        AdjModuleRefPtr = thisModule->m_link_ptrs;
 
 
2591
 
 
 
2592
        if(AdjModuleRefPtr)     /* check that there is a list of adjacent modules */
 
 
2593
        {
 
 
2594
            while(*AdjModuleRefPtr != 0)
 
 
2595
            {
 
 
2596
                /* Probably want some validity test for the link. */
 
 
2597
                if ((AIModuleIsPhysical(*AdjModuleRefPtr)) &&(AIModuleAdmitsPheromones(*AdjModuleRefPtr)))
 
 
2598
                    /* No visibility check. */
 
 
2599
                {
 
 
2600
                    /* Is this my_module? */
 
 
2601
                    if ( (*AdjModuleRefPtr) == my_module)
 
 
2602
                    {
 
 
2603
                        /* Yes!!! Break out. */
 
 
2604
                        success = 1;
 
 
2605
                        break;
 
 
2606
                    }
 
 
2607
                    else if ( (NearLink_Route_Queue[NL_Queue_Exec].depth<max_depth)
 
 
2608
                        &&( /* Test for 'used this time round' */
 
 
2609
                            ((*AdjModuleRefPtr)->RouteFinder_FrameStamp!=GlobalFrameCounter)
 
 
2610
                            ||((*AdjModuleRefPtr)->RouteFinder_IterationNumber!=RouteFinder_CallsThisFrame)))
 
 
2611
                    {
 
 
2612
                        success = 0;
 
 
2613
                        /* Add to queue. */
 
 
2614
                        NearLink_Route_Queue[NL_Queue_End].aimodule = (*AdjModuleRefPtr);
 
 
2615
                        NearLink_Route_Queue[NL_Queue_End].depth = NearLink_Route_Queue[NL_Queue_Exec].depth+1;
 
 
2616
                        /* Remember first step. */
 
 
2617
 
 
 
2618
                        if (NearLink_Route_Queue[NL_Queue_Exec].first_step == NULL)
 
 
2619
                            NearLink_Route_Queue[NL_Queue_End].first_step = (*AdjModuleRefPtr);
 
 
2620
                        else
 
 
2621
                            NearLink_Route_Queue[NL_Queue_End].first_step = NearLink_Route_Queue[NL_Queue_Exec].first_step;
 
 
2622
 
 
 
2623
                        /* Stamp as used. */
 
 
2624
                        (*AdjModuleRefPtr)->RouteFinder_FrameStamp = GlobalFrameCounter;
 
 
2625
                        (*AdjModuleRefPtr)->RouteFinder_IterationNumber = RouteFinder_CallsThisFrame;
 
 
2626
                        NL_Queue_End++;
 
 
2627
 
 
 
2628
                        if (NL_Queue_End >= NEARLINK_QUEUE_LENGTH)
 
 
2629
                        {
 
 
2630
                            NL_Queue_End = 0;
 
 
2631
                            //printf("Wrapping Nearlink Queue!\n");
 
 
2632
                        }
 
 
2633
 
 
 
2634
                        NearLink_Route_Queue[NL_Queue_End].aimodule = NULL;
 
 
2635
 
 
 
2636
                        #if DEBUG
 
 
2637
                        if (NL_Queue_End == NL_Queue_Exec)
 
 
2638
                        {
 
 
2639
                            printf("Oh, no.  NearLinkQueue screwed.  NL_Queue_End=%d, depth =
%d\n",NL_Queue_End,NearLink_Route_Queue[NL_Queue_Exec].depth);
 
 
2640
                            assert(NL_Queue_End!=NL_Queue_Exec); //if this happens the queue probably needs to be longer
 
 
2641
                        }
 
 
2642
                        #endif
 
 
2643
                    }
 
 
2644
                }
 
 
2645
                /* next adjacent module reference pointer */
 
 
2646
                AdjModuleRefPtr++;
 
 
2647
            }
 
 
2648
        }
 
 
2649
 
 
 
2650
        /* Done all the links. */
 
 
2651
 
 
 
2652
        if (success) // Continue break out.
 
 
2653
            break;
 
 
2654
 
 
 
2655
        NL_Queue_Exec++;
 
 
2656
 
 
 
2657
        if (NL_Queue_Exec >= NEARLINK_QUEUE_LENGTH)
 
 
2658
            NL_Queue_Exec = 0;
 
 
2659
    }
 
 
2660
 
 
 
2661
    /* By now, we should have broken out... or maxed out the range. */
 
 
2662
    if (success)
 
 
2663
        return General_GetRetreatModule_Core(my_module,max_depth);
 
 
2664
    else
 
 
2665
        return NULL;
 
 
2666
}
 
 
2667
 
 
 
2668
static void ClearThirdAvoidance(NPC_AVOIDANCEMANAGER *manager) 
 
 
2669
{
 
 
2670
    manager->baseVector.vx = manager->baseVector.vy = manager->baseVector.vz = 0;
 
 
2671
    manager->currentVector = manager->baseVector;
 
 
2672
    manager->avoidanceDirection.vx = manager->avoidanceDirection.vy = manager->avoidanceDirection.vz = 0;
 
 
2673
    manager->bestVector.vx = manager->bestVector.vy = manager->bestVector.vz = 0;
 
 
2674
    manager->basePoint = manager->baseVector;
 
 
2675
    manager->stage = 0;
 
 
2676
    manager->bestDistance = 0;
 
 
2677
    manager->bestStage = 0;
 
 
2678
    /* Er... ignore the rotmat for now... */
 
 
2679
}
 
 
2680
 
 
 
2681
void Initialise_AvoidanceManager(NPC_AVOIDANCEMANAGER *manager)
 
 
2682
{
 
 
2683
    assert(manager);
 
 
2684
 
 
 
2685
    ClearThirdAvoidance(manager);
 
 
2686
 
 
 
2687
    manager->avoidanceDirection.vx = manager->avoidanceDirection.vy = manager->avoidanceDirection.vz = 0;
 
 
2688
    manager->incidenceDirection.vx = manager->incidenceDirection.vy = manager->incidenceDirection.vz = 0;
 
 
2689
    manager->incidentPoint.vx = manager->incidentPoint.vy = manager->incidentPoint.vz = 0;
 
 
2690
    manager->aggregateNormal.vx = manager->aggregateNormal.vy = manager->aggregateNormal.vz = 0;
 
 
2691
 
 
 
2692
    manager->recommendedDistance = 0;
 
 
2693
    manager->timer = 0;
 
 
2694
    manager->primaryCollision = NULL; 
 
 
2695
    manager->substate = AvSS_FreeMovement;
 
 
2696
 
 
 
2697
    /* Allows destruction of explosive objects. */
 
 
2698
    manager->ClearanceDamage = AMMO_NPC_OBSTACLE_CLEAR;
 
 
2699
}
 
 
2700
 
 
 
2701
static void New_GetAvoidanceDirection(STRATEGYBLOCK *sbPtr, NPC_AVOIDANCEMANAGER *manager, VECTORCH *aggregateNormal) 
 
 
2702
{
 
 
2703
    VECTORCH spaceNormal,transverse;
 
 
2704
    VECTORCH direction[4];
 
 
2705
 
 
 
2706
    assert(manager);
 
 
2707
    assert(sbPtr);
 
 
2708
    DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr;
 
 
2709
    assert(dynPtr);
 
 
2710
 
 
 
2711
    /* aggregateNormal should be normalised, and should point away from the collisions. */
 
 
2712
    /* First dot it with gravity... */
 
 
2713
 
 
 
2714
    if (dynPtr->UseStandardGravity) 
 
 
2715
    {
 
 
2716
        dynPtr->GravityDirection.vx = 0;
 
 
2717
        dynPtr->GravityDirection.vy = 65536;
 
 
2718
        dynPtr->GravityDirection.vz = 0;
 
 
2719
    }
 
 
2720
 
 
 
2721
    int dot = -(DotProduct(&dynPtr->GravityDirection,aggregateNormal));
 
 
2722
 
 
 
2723
    /* Hold that thought. */
 
 
2724
    spaceNormal.vx = (aggregateNormal->vx + MUL_FIXED(dot,dynPtr->GravityDirection.vx));
 
 
2725
    spaceNormal.vy = (aggregateNormal->vy + MUL_FIXED(dot,dynPtr->GravityDirection.vy));
 
 
2726
    spaceNormal.vz = (aggregateNormal->vz + MUL_FIXED(dot,dynPtr->GravityDirection.vz));
 
 
2727
 
 
 
2728
    Normalise(&spaceNormal);
 
 
2729
    /* Now, spaceNormal should be in the plane we want to consider. */
 
 
2730
    CrossProduct(&spaceNormal,&dynPtr->GravityDirection,&transverse);
 
 
2731
    Normalise(&transverse);
 
 
2732
    /* ...And 'transverse' should be at 90degs to it. */
 
 
2733
 
 
 
2734
    /* For now, emulate the old avoidance code... */
 
 
2735
 
 
 
2736
    direction[0] = transverse;
 
 
2737
    direction[1].vx = -transverse.vx;
 
 
2738
    direction[1].vy = -transverse.vy;
 
 
2739
    direction[1].vz = -transverse.vz;
 
 
2740
 
 
 
2741
    // Added by Alex - see if we can get a better direction this way.
 
 
2742
    direction[2] = spaceNormal;
 
 
2743
    direction[3].vx = -direction[2].vx;
 
 
2744
    direction[3].vy = -direction[2].vy;
 
 
2745
    direction[3].vz = -direction[2].vz;
 
 
2746
 
 
 
2747
    direction[0].vx += (spaceNormal.vx/4);
 
 
2748
    direction[0].vy += (spaceNormal.vy/4);
 
 
2749
    direction[0].vz += (spaceNormal.vz/4);
 
 
2750
    direction[1].vx += (spaceNormal.vx/4);
 
 
2751
    direction[1].vy += (spaceNormal.vy/4);
 
 
2752
    direction[1].vz += (spaceNormal.vz/4);
 
 
2753
 
 
 
2754
    direction[2].vx -= (transverse.vx/4);
 
 
2755
    direction[2].vy -= (transverse.vy/4);
 
 
2756
    direction[2].vz -= (transverse.vz/4);
 
 
2757
    direction[3].vx -= (transverse.vx/4);
 
 
2758
    direction[3].vy -= (transverse.vy/4);
 
 
2759
    direction[3].vz -= (transverse.vz/4);
 
 
2760
 
 
 
2761
    Normalise(&direction[0]);
 
 
2762
    Normalise(&direction[1]);
 
 
2763
    Normalise(&direction[2]);
 
 
2764
    Normalise(&direction[3]);
 
 
2765
 
 
 
2766
    {
 
 
2767
        int this_distance, i;
 
 
2768
        int best_distance_so_far = 0;
 
 
2769
        int best_direction_so_far = 0;
 
 
2770
 
 
 
2771
        /* test how far we could go in each direction... */
 
 
2772
        for( i=0; i < 4; i++ )
 
 
2773
        {
 
 
2774
            VECTORCH startingPosition = sbPtr->DynPtr->Position;
 
 
2775
            VECTORCH testDirn = direction[i];
 
 
2776
 
 
 
2777
            CheckForVectorIntersectionWith3dObject(sbPtr->containingModule->m_dptr, &startingPosition,&testDirn);
 
 
2778
 
 
 
2779
            this_distance = LOS_ObjectHitPtr ? LOS_Lambda : NPC_MAX_VIEWRANGE;
 
 
2780
 
 
 
2781
            if( this_distance > best_distance_so_far )
 
 
2782
            {
 
 
2783
                // What follows is an attempt to make sure we don't jump off any cliffs...
 
 
2784
                VECTORCH test_location;
 
 
2785
                testDirn.vx *= this_distance;
 
 
2786
                testDirn.vy *= this_distance;
 
 
2787
                testDirn.vz *= this_distance;
 
 
2788
                test_location.vx = startingPosition.vx + testDirn.vx;
 
 
2789
                test_location.vy = startingPosition.vy + testDirn.vy;
 
 
2790
                test_location.vz = startingPosition.vz + testDirn.vz;
 
 
2791
 
 
 
2792
                if( CheckMyFloorPoly(&test_location, sbPtr->containingModule) != NPC_GMD_NOPOLY)
 
 
2793
                {
 
 
2794
                    best_direction_so_far = i;
 
 
2795
                    best_distance_so_far = this_distance;
 
 
2796
                }
 
 
2797
            }
 
 
2798
        }
 
 
2799
 
 
 
2800
        manager->avoidanceDirection = direction[best_direction_so_far];
 
 
2801
    }
 
 
2802
}
 
 
2803
 
 
 
2804
static void New_GetSecondAvoidanceDirection(STRATEGYBLOCK *sbPtr, NPC_AVOIDANCEMANAGER *manager, VECTORCH *aggregateNormal) 
 
 
2805
{
 
 
2806
    VECTORCH spaceNormal,transverse;
 
 
2807
    VECTORCH direction1,direction2;
 
 
2808
    /* Yeesh. */
 
 
2809
 
 
 
2810
    assert(manager);
 
 
2811
    assert(sbPtr);
 
 
2812
    DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr;
 
 
2813
    assert(dynPtr);
 
 
2814
 
 
 
2815
    /* aggregateNormal should be normalised, and should point away from the collisions. */
 
 
2816
    /* First dot it with gravity... */
 
 
2817
 
 
 
2818
    int dot = -(DotProduct(&dynPtr->GravityDirection,aggregateNormal));
 
 
2819
    /* Hold that thought. */
 
 
2820
    spaceNormal.vx = (aggregateNormal->vx + MUL_FIXED(dot,dynPtr->GravityDirection.vx));
 
 
2821
    spaceNormal.vy = (aggregateNormal->vy + MUL_FIXED(dot,dynPtr->GravityDirection.vy));
 
 
2822
    spaceNormal.vz = (aggregateNormal->vz + MUL_FIXED(dot,dynPtr->GravityDirection.vz));
 
 
2823
 
 
 
2824
    Normalise(&spaceNormal);
 
 
2825
    /* Now, spaceNormal should be in the plane we want to consider. */
 
 
2826
    CrossProduct(&spaceNormal,&dynPtr->GravityDirection,&transverse);
 
 
2827
    Normalise(&transverse);
 
 
2828
    /* ...And 'transverse' should be at 90degs to it. */
 
 
2829
 
 
 
2830
    /* For now, emulate the old avoidance code... */
 
 
2831
 
 
 
2832
    direction1 = transverse;
 
 
2833
    direction2.vx = -transverse.vx;
 
 
2834
    direction2.vy = -transverse.vy;
 
 
2835
    direction2.vz = -transverse.vz;
 
 
2836
 
 
 
2837
    if ((FastRandom() & 65535) < 32767) 
 
 
2838
    {
 
 
2839
        direction1.vx += (spaceNormal.vx/2);
 
 
2840
        direction1.vy += (spaceNormal.vy/2);
 
 
2841
        direction1.vz += (spaceNormal.vz/2);
 
 
2842
        direction2.vx += (spaceNormal.vx/2);
 
 
2843
        direction2.vy += (spaceNormal.vy/2);
 
 
2844
        direction2.vz += (spaceNormal.vz/2);
 
 
2845
    }
 
 
2846
    else
 
 
2847
    {
 
 
2848
        direction1.vx += (spaceNormal.vx);
 
 
2849
        direction1.vy += (spaceNormal.vy);
 
 
2850
        direction1.vz += (spaceNormal.vz);
 
 
2851
        direction2.vx += (spaceNormal.vx);
 
 
2852
        direction2.vy += (spaceNormal.vy);
 
 
2853
        direction2.vz += (spaceNormal.vz);
 
 
2854
    }
 
 
2855
 
 
 
2856
    Normalise(&direction1);
 
 
2857
    Normalise(&direction2);
 
 
2858
 
 
 
2859
    {
 
 
2860
        int dir1dist,dir2dist;
 
 
2861
        /* test how far we could go in each direction... */
 
 
2862
        {
 
 
2863
            VECTORCH startingPosition = sbPtr->DynPtr->Position;
 
 
2864
            VECTORCH testDirn = direction1;
 
 
2865
 
 
 
2866
            CheckForVectorIntersectionWith3dObject(sbPtr->containingModule->m_dptr, &startingPosition, &testDirn);
 
 
2867
 
 
 
2868
            dir1dist = LOS_ObjectHitPtr ? LOS_Lambda : NPC_MAX_VIEWRANGE;
 
 
2869
 
 
 
2870
            startingPosition = sbPtr->DynPtr->Position;
 
 
2871
            testDirn = direction2;
 
 
2872
 
 
 
2873
            CheckForVectorIntersectionWith3dObject(sbPtr->containingModule->m_dptr, &startingPosition, &testDirn);
 
 
2874
 
 
 
2875
            dir2dist = LOS_ObjectHitPtr ? LOS_Lambda : NPC_MAX_VIEWRANGE;
 
 
2876
        }
 
 
2877
 
 
 
2878
        manager->avoidanceDirection = (dir1dist > dir2dist) ? direction1 : direction2;
 
 
2879
    }
 
 
2880
}
 
 
2881
 
 
 
2882
void AlignVelocityToGravity(STRATEGYBLOCK *sbPtr,VECTORCH *velocity) 
 
 
2883
{
 
 
2884
        assert(sbPtr);
 
 
2885
        DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr;
 
 
2886
        assert(dynPtr);
 
 
2887
 
 
 
2888
        if (dynPtr->UseStandardGravity) 
 
 
2889
    {
 
 
2890
                dynPtr->GravityDirection.vx = 0;
 
 
2891
                dynPtr->GravityDirection.vy = 65536;
 
 
2892
                dynPtr->GravityDirection.vz = 0;
 
 
2893
        }
 
 
2894
 
 
 
2895
    int dot = -(DotProduct(&dynPtr->GravityDirection,velocity));
 
 
2896
        /* Hold that thought. */
 
 
2897
        velocity->vx = (velocity->vx + MUL_FIXED(dot,dynPtr->GravityDirection.vx));
 
 
2898
        velocity->vy = (velocity->vy + MUL_FIXED(dot,dynPtr->GravityDirection.vy));
 
 
2899
        velocity->vz = (velocity->vz + MUL_FIXED(dot,dynPtr->GravityDirection.vz));
 
 
2900
 
 
 
2901
    if (!velocity->vx && !velocity->vy && !velocity->vz) 
 
 
2902
    {
 
 
2903
        /* That can't be good.  Can it? */
 
 
2904
        //velocity->vx = ONE_FIXED;
 
 
2905
        return;
 
 
2906
    }
 
 
2907
 
 
 
2908
        Normalise(velocity);
 
 
2909
}
 
 
2910
 
 
 
2911
static void InitialiseThirdAvoidance(STRATEGYBLOCK *sbPtr,NPC_AVOIDANCEMANAGER *manager) 
 
 
2912
{
 
 
2913
    assert(manager);
 
 
2914
    assert(sbPtr);
 
 
2915
    DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr;
 
 
2916
    assert(dynPtr);
 
 
2917
 
 
 
2918
    if (dynPtr->UseStandardGravity) 
 
 
2919
    {
 
 
2920
        dynPtr->GravityDirection.vx = 0;
 
 
2921
        dynPtr->GravityDirection.vy = 65536;
 
 
2922
        dynPtr->GravityDirection.vz = 0;
 
 
2923
    }
 
 
2924
 
 
 
2925
    /* Setup base vector. */
 
 
2926
    {
 
 
2927
        /* Try using positive... z. */
 
 
2928
        manager->baseVector.vx = sbPtr->DynPtr->OrientMat.mat31;
 
 
2929
        manager->baseVector.vy = sbPtr->DynPtr->OrientMat.mat32;
 
 
2930
        manager->baseVector.vz = sbPtr->DynPtr->OrientMat.mat33;
 
 
2931
 
 
 
2932
        int dot = (DotProduct(&dynPtr->GravityDirection,&manager->baseVector));
 
 
2933
 
 
 
2934
        if (!((dot < 65000) && (dot > -65000))) 
 
 
2935
        {
 
 
2936
            /* Too close.  Let's use x. */
 
 
2937
            manager->baseVector.vx = sbPtr->DynPtr->OrientMat.mat11;
 
 
2938
            manager->baseVector.vy = sbPtr->DynPtr->OrientMat.mat12;
 
 
2939
            manager->baseVector.vz = sbPtr->DynPtr->OrientMat.mat13;
 
 
2940
        }
 
 
2941
 
 
 
2942
        AlignVelocityToGravity(sbPtr,&manager->baseVector);
 
 
2943
    }
 
 
2944
 
 
 
2945
    manager->currentVector = manager->baseVector;
 
 
2946
 
 
 
2947
    /* Keep still! */
 
 
2948
    manager->avoidanceDirection.vx = manager->avoidanceDirection.vy = manager->avoidanceDirection.vz = 0;
 
 
2949
    manager->bestVector.vx = manager->bestVector.vy =  manager->bestVector.vz = 0;
 
 
2950
 
 
 
2951
    manager->basePoint = dynPtr->Position;
 
 
2952
    manager->stage = 0;
 
 
2953
    manager->bestDistance = 0;
 
 
2954
    manager->bestStage = 0;
 
 
2955
 
 
 
2956
    /* Finally, generate a matrix to rotate 22.5degs about GravityDirection. */
 
 
2957
 
 
 
2958
    {
 
 
2959
        QUAT deltaRotQ;
 
 
2960
        /* Angle is 256, halfangle is 128. */   
 
 
2961
        int cosHalfAngle = GetCos(128);
 
 
2962
        int sinHalfAngle = GetSin(128);
 
 
2963
 
 
 
2964
        deltaRotQ.quatw = cosHalfAngle;
 
 
2965
        deltaRotQ.quatx = MUL_FIXED(sinHalfAngle,dynPtr->GravityDirection.vx);
 
 
2966
        deltaRotQ.quaty = MUL_FIXED(sinHalfAngle,dynPtr->GravityDirection.vy);
 
 
2967
        deltaRotQ.quatz = MUL_FIXED(sinHalfAngle,dynPtr->GravityDirection.vz);
 
 
2968
 
 
 
2969
        QNormalise(&deltaRotQ);
 
 
2970
        QuatToMat(&deltaRotQ,&manager->rotationMatrix);
 
 
2971
    }
 
 
2972
 
 
 
2973
    /* Say we're in Third Avoidance. */
 
 
2974
    manager->substate = AvSS_ThirdAvoidance;
 
 
2975
}
 
 
2976
 
 
 
2977
static int SimpleEdgeDetectionTest(STRATEGYBLOCK *sbPtr, COLLISIONREPORT *vcr) 
 
 
2978
{
 
 
2979
    VECTORCH alpha,beta,tvec;
 
 
2980
 
 
 
2981
    GetTargetingPointOfObject_Far(sbPtr,&alpha);
 
 
2982
    /* Now add half a metre in positive z. */
 
 
2983
 
 
 
2984
    tvec.vx = sbPtr->DynPtr->OrientMat.mat31;
 
 
2985
    tvec.vy = sbPtr->DynPtr->OrientMat.mat32;
 
 
2986
    tvec.vz = sbPtr->DynPtr->OrientMat.mat33;
 
 
2987
 
 
 
2988
    tvec.vx = MUL_FIXED(tvec.vx,1000);
 
 
2989
    tvec.vy = MUL_FIXED(tvec.vy,1000);
 
 
2990
    tvec.vz = MUL_FIXED(tvec.vz,1000);
 
 
2991
 
 
 
2992
    alpha.vx += tvec.vx;
 
 
2993
    alpha.vy += tvec.vy;
 
 
2994
    alpha.vz += tvec.vz;
 
 
2995
 
 
 
2996
    beta.vx = sbPtr->DynPtr->OrientMat.mat21;
 
 
2997
    beta.vy = sbPtr->DynPtr->OrientMat.mat22;
 
 
2998
    beta.vz = sbPtr->DynPtr->OrientMat.mat23;
 
 
2999
    Normalise(&beta);
 
 
3000
 
 
 
3001
    /* Now do an LOS test. */
 
 
3002
    FindPolygonInLineOfSight(&beta, &alpha, 0, sbPtr->DisplayBlock);
 
 
3003
 
 
 
3004
    /* Pass the test if the test hit something within a metre (y) of dynPtr->Position. */
 
 
3005
 
 
 
3006
    if (LOS_ObjectHitPtr) 
 
 
3007
    {
 
 
3008
        VECTORCH offset;
 
 
3009
        int dot;
 
 
3010
        /* Examine LOS_Point. */
 
 
3011
        offset.vx = LOS_Point.vx - sbPtr->DynPtr->Position.vx;
 
 
3012
        offset.vy = LOS_Point.vy - sbPtr->DynPtr->Position.vy;
 
 
3013
        offset.vz = LOS_Point.vz - sbPtr->DynPtr->Position.vz;
 
 
3014
 
 
 
3015
        dot = DotProduct(&offset, &beta);
 
 
3016
 
 
 
3017
        //printf("Dot %d\n",dot);
 
 
3018
 
 
 
3019
        if ((dot > -1000) && (dot < 1000)) 
 
 
3020
            return 0;
 
 
3021
    }
 
 
3022
 
 
 
3023
    /* Must be about to hit a rail or an edge? */
 
 
3024
    vcr->ObstacleSBPtr = NULL;
 
 
3025
    vcr->ObstacleNormal.vx = -sbPtr->DynPtr->OrientMat.mat31;
 
 
3026
    vcr->ObstacleNormal.vy = -sbPtr->DynPtr->OrientMat.mat32;
 
 
3027
    vcr->ObstacleNormal.vz = -sbPtr->DynPtr->OrientMat.mat33;
 
 
3028
    vcr->ObstaclePoint = LOS_Point;
 
 
3029
    vcr->NextCollisionReportPtr = NULL;
 
 
3030
 
 
 
3031
return 1;
 
 
3032
}
 
 
3033
 
 
 
3034
static int SBIsEnvironment(STRATEGYBLOCK *sbPtr)
 
 
3035
{
 
 
3036
    return (sbPtr == NULL) ? 1 : (sbPtr->DisplayBlock && sbPtr->DisplayBlock->Module);
 
 
3037
}
 
 
3038
 
 
 
3039
int New_NPC_IsObstructed(STRATEGYBLOCK *sbPtr, NPC_AVOIDANCEMANAGER *manager)
 
 
3040
{
 
 
3041
    VECTORCH myVelocityDirection;
 
 
3042
    VECTORCH aggregateNormal = { 0,0,0 };
 
 
3043
    int numObstructiveCollisions = 0;
 
 
3044
    STRATEGYBLOCK *highestPriorityCollision = (STRATEGYBLOCK *)-1;
 
 
3045
 
 
 
3046
    assert(manager);
 
 
3047
    assert(sbPtr);
 
 
3048
    DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr;
 
 
3049
    assert(dynPtr);
 
 
3050
    struct collisionreport *nextReport = dynPtr->CollisionReportPtr;
 
 
3051
 
 
 
3052
    /* check our velocity: if we haven't got one, we can't be obstructed, so just return */
 
 
3053
    if(!sbPtr->DynPtr->LinVelocity.vx && !sbPtr->DynPtr->LinVelocity.vy && !sbPtr->DynPtr->LinVelocity.vz)
 
 
3054
            return 0;
 
 
3055
 
 
 
3056
    if (sbPtr->type == I_BehaviourMarine) 
 
 
3057
    {
 
 
3058
        COLLISIONREPORT vcr;
 
 
3059
 
 
 
3060
        if (SimpleEdgeDetectionTest(sbPtr, &vcr)) 
 
 
3061
        {
 
 
3062
            vcr.NextCollisionReportPtr = nextReport;
 
 
3063
            nextReport = &vcr;
 
 
3064
        }
 
 
3065
    }
 
 
3066
 
 
 
3067
    /* Trivial reject to save time. */
 
 
3068
    if (nextReport == NULL)
 
 
3069
        return 0;
 
 
3070
 
 
 
3071
    /* get my velocity direction, normalised... */
 
 
3072
    myVelocityDirection = dynPtr->LinVelocity;
 
 
3073
    Normalise(&myVelocityDirection);
 
 
3074
 
 
 
3075
    if (manager->substate == AvSS_FreeMovement) 
 
 
3076
    Initialise_AvoidanceManager(manager);
 
 
3077
 
 
 
3078
    /* Walk the collision report list. */
 
 
3079
    while(nextReport)
 
 
3080
    {               
 
 
3081
        int normalDotWithVelocity = DotProduct(&nextReport->ObstacleNormal, &myVelocityDirection);
 
 
3082
 
 
 
3083
        if (normalDotWithVelocity < -32768)
 
 
3084
        {
 
 
3085
            /* If we're in FirstAvoidance already, might want to disregard the same collision again. */
 
 
3086
            if (manager->substate == AvSS_FirstAvoidance) 
 
 
3087
            {
 
 
3088
                if (!SBIsEnvironment(manager->primaryCollision)) 
 
 
3089
                {
 
 
3090
                    if (manager->primaryCollision == nextReport->ObstacleSBPtr) 
 
 
3091
                    {
 
 
3092
                        /* Advance and continue. */
 
 
3093
                        nextReport = nextReport->NextCollisionReportPtr;
 
 
3094
                        continue;
 
 
3095
                    }
 
 
3096
                }
 
 
3097
            }
 
 
3098
 
 
 
3099
            /* We have detected a collision with a strategy, or an obstructive environment bit. */
 
 
3100
            numObstructiveCollisions++;
 
 
3101
            aggregateNormal.vx += nextReport->ObstacleNormal.vx;
 
 
3102
            aggregateNormal.vy += nextReport->ObstacleNormal.vy;
 
 
3103
            aggregateNormal.vz += nextReport->ObstacleNormal.vz;
 
 
3104
 
 
 
3105
            /* Sort out highest priority collision. */
 
 
3106
            if (highestPriorityCollision == (STRATEGYBLOCK *)-1) 
 
 
3107
            {
 
 
3108
                highestPriorityCollision = nextReport->ObstacleSBPtr;
 
 
3109
            }
 
 
3110
            else
 
 
3111
            {
 
 
3112
                /* If this collision is with the environment, and the older one was not, replace it. */
 
 
3113
 
 
 
3114
                if (SBIsEnvironment(nextReport->ObstacleSBPtr) && !SBIsEnvironment(highestPriorityCollision)) 
 
 
3115
                    highestPriorityCollision = nextReport->ObstacleSBPtr;
 
 
3116
            }
 
 
3117
 
 
 
3118
            if(nextReport->ObstacleSBPtr)
 
 
3119
            {
 
 
3120
                if(nextReport->ObstacleSBPtr->type == I_BehaviourInanimateObject)
 
 
3121
                {
 
 
3122
                    if(!nextReport->ObstacleSBPtr->DamageBlock.Indestructable)
 
 
3123
                    {
 
 
3124
                        INANIMATEOBJECT_STATUSBLOCK* objectstatusptr = nextReport->ObstacleSBPtr->dataptr;
 
 
3125
 
 
 
3126
                        if (objectstatusptr)
 
 
3127
                        {
 
 
3128
                            /* Consider explosive objects as obstructions to most things. */
 
 
3129
 
 
 
3130
                            if (!objectstatusptr->explosionType || (manager->ClearanceDamage != AMMO_NPC_OBSTACLE_CLEAR))
 
 
3131
                            {
 
 
3132
                                /* aha: an object which the npc can destroy... damage it, and return zero. */
 
 
3133
                                CauseDamageToObject(nextReport->ObstacleSBPtr, &TemplateAmmo[manager->ClearanceDamage].MaxDamage, ONE_FIXED,NULL);
 
 
3134
                                return 0;
 
 
3135
                                /* After a few frames of that, there'll just be real obstructions. */
 
 
3136
                            }
 
 
3137
                        }
 
 
3138
                    }
 
 
3139
                }
 
 
3140
            }
 
 
3141
        }
 
 
3142
 
 
 
3143
        nextReport = nextReport->NextCollisionReportPtr;
 
 
3144
    }
 
 
3145
 
 
 
3146
    if (!numObstructiveCollisions)
 
 
3147
    return 0; /* No collisions!  Woohoo!  But don't reset the substate... */
 
 
3148
 
 
 
3149
    switch (manager->substate)
 
 
3150
    {
 
 
3151
        case AvSS_FreeMovement:
 
 
3152
        default:
 
 
3153
        {
 
 
3154
            /* Right, we've run into something all right. */
 
 
3155
            assert(highestPriorityCollision != (STRATEGYBLOCK *)-1);
 
 
3156
 
 
 
3157
            manager->primaryCollision = highestPriorityCollision;
 
 
3158
            manager->incidenceDirection = myVelocityDirection;
 
 
3159
            manager->incidentPoint = dynPtr->Position;
 
 
3160
            /* Decide on a distance... */
 
 
3161
 
 
 
3162
            if (SBIsEnvironment(manager->primaryCollision)) 
 
 
3163
                manager->recommendedDistance = 3000;
 
 
3164
            else
 
 
3165
                manager->recommendedDistance = 2000;
 
 
3166
 
 
 
3167
            manager->timer = STANDARD_AVOIDANCE_TIME;
 
 
3168
 
 
 
3169
            /* ...and a direction. */
 
 
3170
            Normalise(&aggregateNormal);
 
 
3171
            manager->aggregateNormal = aggregateNormal;
 
 
3172
 
 
 
3173
            New_GetAvoidanceDirection(sbPtr,manager,&aggregateNormal);
 
 
3174
 
 
 
3175
            manager->substate = AvSS_FirstAvoidance;
 
 
3176
        }
 
 
3177
        return 1;
 
 
3178
        case AvSS_FirstAvoidance:
 
 
3179
        {
 
 
3180
            /* Right, we've run into something again. */
 
 
3181
            /* Retain point, direction and distance. */
 
 
3182
            /* ...but get a new direction. */
 
 
3183
 
 
 
3184
            aggregateNormal.vx += manager->aggregateNormal.vx;
 
 
3185
            aggregateNormal.vy += manager->aggregateNormal.vy;
 
 
3186
            aggregateNormal.vz += manager->aggregateNormal.vz;
 
 
3187
 
 
 
3188
            /* Add the new aggregatenormal to the old one, and normalise... */
 
 
3189
 
 
 
3190
            Normalise(&aggregateNormal);
 
 
3191
 
 
 
3192
            /* Then pass that number into the second direction system. */
 
 
3193
            New_GetSecondAvoidanceDirection(sbPtr,manager,&aggregateNormal);
 
 
3194
 
 
 
3195
            manager->substate = AvSS_SecondAvoidance;
 
 
3196
            manager->timer = STANDARD_AVOIDANCE_TIME;
 
 
3197
        }
 
 
3198
        return 1;
 
 
3199
        case AvSS_SecondAvoidance:
 
 
3200
        case AvSS_ThirdAvoidance:
 
 
3201
        {
 
 
3202
            /* Right, we've run into something again again. (Again.) */
 
 
3203
 
 
 
3204
            /* Retain point, direction and distance, and go directly to third avoidance. */
 
 
3205
 
 
 
3206
            manager->timer = STANDARD_AVOIDANCE_TIME;
 
 
3207
            InitialiseThirdAvoidance(sbPtr,manager);
 
 
3208
 
 
 
3209
            return 1;
 
 
3210
        }
 
 
3211
    }
 
 
3212
}
 
 
3213
 
 
 
3214
static int ExecuteThirdAvoidance(STRATEGYBLOCK *sbPtr,NPC_AVOIDANCEMANAGER *manager) 
 
 
3215
{
 
 
3216
    assert(manager);
 
 
3217
    assert(sbPtr);
 
 
3218
    DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr;
 
 
3219
    assert(dynPtr);
 
 
3220
 
 
 
3221
    if (manager->stage < 9) 
 
 
3222
    {
 
 
3223
        /* Still in the spin.  Increment stage NOW! */
 
 
3224
        manager->stage++;
 
 
3225
        /* Now raycast. */
 
 
3226
        {
 
 
3227
            VECTORCH testDirn = manager->currentVector;
 
 
3228
 
 
 
3229
            CheckForVectorIntersectionWith3dObject(sbPtr->containingModule->m_dptr, &manager->basePoint, &testDirn);
 
 
3230
 
 
 
3231
            if (LOS_ObjectHitPtr) 
 
 
3232
            {
 
 
3233
                /* Hit environment! */
 
 
3234
                if (LOS_Lambda>manager->bestDistance) 
 
 
3235
                {
 
 
3236
                    /* Register this as best. */
 
 
3237
                    manager->bestDistance = LOS_Lambda;
 
 
3238
                    manager->bestStage = manager->stage;
 
 
3239
                    manager->bestVector = testDirn;
 
 
3240
                }
 
 
3241
            }
 
 
3242
 
 
 
3243
            testDirn.vx = -manager->currentVector.vx;
 
 
3244
            testDirn.vy = -manager->currentVector.vy;
 
 
3245
            testDirn.vz = -manager->currentVector.vz;
 
 
3246
 
 
 
3247
            CheckForVectorIntersectionWith3dObject(sbPtr->containingModule->m_dptr, &manager->basePoint, &testDirn);
 
 
3248
 
 
 
3249
            if (LOS_ObjectHitPtr) 
 
 
3250
            {
 
 
3251
                /* Hit environment! */
 
 
3252
                if (LOS_Lambda>manager->bestDistance) 
 
 
3253
                {
 
 
3254
                    /* Register this as best. */
 
 
3255
                    manager->bestDistance = LOS_Lambda;
 
 
3256
                    manager->bestStage = -manager->stage;
 
 
3257
                    manager->bestVector = testDirn;
 
 
3258
                }
 
 
3259
            }
 
 
3260
        }
 
 
3261
 
 
 
3262
        /* Now, rotate vector round. */
 
 
3263
        RotateVector(&manager->currentVector,&manager->rotationMatrix);
 
 
3264
    }
 
 
3265
    else if (manager->stage == 9) 
 
 
3266
    {
 
 
3267
        /* Now, we must have checked all 16 directions. */
 
 
3268
        if (manager->bestStage == 0)
 
 
3269
        {
 
 
3270
            /* That's a bit of a mystery.  Return fubared. */
 
 
3271
            return -1;
 
 
3272
        }
 
 
3273
 
 
 
3274
        /* Now, my beautiful assistant, open the envelope! */
 
 
3275
        if (manager->bestDistance < THIRD_AVOIDANCE_MINDIST) 
 
 
3276
        {
 
 
3277
            /* Whatta loada junk. */
 
 
3278
            return -1;
 
 
3279
        }
 
 
3280
        else
 
 
3281
        {
 
 
3282
            /* Go, my child, in the direction of destiny! */
 
 
3283
            manager->avoidanceDirection = manager->bestVector;
 
 
3284
            Normalise(&manager->avoidanceDirection);
 
 
3285
            manager->stage++;
 
 
3286
            return 0;
 
 
3287
        }
 
 
3288
    }
 
 
3289
    else
 
 
3290
    {
 
 
3291
        /* In stage 10, we're going that way and trying to escape. */
 
 
3292
        if (New_NPC_IsObstructed(sbPtr,manager) == 1) 
 
 
3293
        {
 
 
3294
            VECTORCH offset;
 
 
3295
            /* We're obstructed AGAIN!  Gordon Bennet... restart? */
 
 
3296
            offset.vx = dynPtr->Position.vx-manager->basePoint.vx;
 
 
3297
            offset.vy = dynPtr->Position.vy-manager->basePoint.vy;
 
 
3298
            offset.vz = dynPtr->Position.vz-manager->basePoint.vz;
 
 
3299
 
 
 
3300
            if (Approximate3dMagnitude(&offset) < 300)
 
 
3301
            {
 
 
3302
                /* I suspect restarting will get us nowhere, *
 
 
3303
                 * something's in the primary path.  Fail!   */
 
 
3304
                return -1;
 
 
3305
            }
 
 
3306
 
 
 
3307
        InitialiseThirdAvoidance(sbPtr,manager);
 
 
3308
        return 0;
 
 
3309
        }
 
 
3310
 
 
 
3311
        /* Are we far enough away? */
 
 
3312
        {
 
 
3313
            VECTORCH offset;
 
 
3314
 
 
 
3315
            offset.vx = dynPtr->Position.vx-manager->incidentPoint.vx;
 
 
3316
            offset.vy = dynPtr->Position.vy-manager->incidentPoint.vy;
 
 
3317
            offset.vz = dynPtr->Position.vz-manager->incidentPoint.vz;
 
 
3318
 
 
 
3319
            if (Approximate3dMagnitude(&offset) > manager->recommendedDistance) 
 
 
3320
            {
 
 
3321
                /* Now let's check against third avoidance. */
 
 
3322
                offset.vx = dynPtr->Position.vx-manager->basePoint.vx;
 
 
3323
                offset.vy = dynPtr->Position.vy-manager->basePoint.vy;
 
 
3324
                offset.vz = dynPtr->Position.vz-manager->basePoint.vz;
 
 
3325
 
 
 
3326
                if (Approximate3dMagnitude(&offset) > (THIRD_AVOIDANCE_MINDIST >> 1)) 
 
 
3327
                {
 
 
3328
                    /* Exit with success!  Glory be! */
 
 
3329
                    return 1;
 
 
3330
                }
 
 
3331
            }
 
 
3332
            else
 
 
3333
            {
 
 
3334
                /* Still check, to stop repeated third avoidance bouncing. */
 
 
3335
                offset.vx = dynPtr->Position.vx-manager->basePoint.vx;
 
 
3336
                offset.vy = dynPtr->Position.vy-manager->basePoint.vy;
 
 
3337
                offset.vz = dynPtr->Position.vz-manager->basePoint.vz;
 
 
3338
 
 
 
3339
                if (Approximate3dMagnitude(&offset) > ((manager->bestDistance) >> 1)) 
 
 
3340
                    return 1; /* Exit with success, before we look stupid! */
 
 
3341
            }
 
 
3342
        }
 
 
3343
    /* Still here, hmm? */
 
 
3344
    return 0;
 
 
3345
    }
 
 
3346
 
 
 
3347
return 0;
 
 
3348
}
 
 
3349
 
 
 
3350
AVOIDANCE_RETURN_CONDITION AllNewAvoidanceKernel(STRATEGYBLOCK *sbPtr,NPC_AVOIDANCEMANAGER *manager) 
 
 
3351
{
 
 
3352
    /* Velocity must be set deliberately... even if it's null. */
 
 
3353
 
 
 
3354
    assert(manager);
 
 
3355
    assert(sbPtr);
 
 
3356
    DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr;
 
 
3357
    assert(dynPtr);
 
 
3358
 
 
 
3359
    /* Want to split here based on substate. */
 
 
3360
 
 
 
3361
    switch (manager->substate) 
 
 
3362
    {
 
 
3363
        case AvSS_FreeMovement:
 
 
3364
        {
 
 
3365
            /* Really shouldn't be here.  Go'way. */
 
 
3366
            Initialise_AvoidanceManager(manager);
 
 
3367
        }
 
 
3368
        return AvRC_Clear;
 
 
3369
        case AvSS_FirstAvoidance:
 
 
3370
        {
 
 
3371
            if (New_NPC_IsObstructed(sbPtr,manager) == 1) 
 
 
3372
                return AvRC_Avoidance; /* We're obstructed AGAIN! */
 
 
3373
 
 
 
3374
            /* Are we far enough away? */
 
 
3375
            {
 
 
3376
                VECTORCH offset;
 
 
3377
 
 
 
3378
                offset.vx = dynPtr->Position.vx-manager->incidentPoint.vx;
 
 
3379
                offset.vy = dynPtr->Position.vy-manager->incidentPoint.vy;
 
 
3380
                offset.vz = dynPtr->Position.vz-manager->incidentPoint.vz;
 
 
3381
 
 
 
3382
                if (Approximate3dMagnitude(&offset) > manager->recommendedDistance) 
 
 
3383
                {
 
 
3384
                    /* Exit! */
 
 
3385
                    Initialise_AvoidanceManager(manager);
 
 
3386
                return AvRC_Clear;
 
 
3387
                }
 
 
3388
            }
 
 
3389
 
 
 
3390
            manager->timer -= NormalFrameTime;
 
 
3391
 
 
 
3392
            if (manager->timer <= 0) 
 
 
3393
            {
 
 
3394
                /* Ooh, we're in a fix here... */
 
 
3395
                /* Probably need Avoidance 3 for this. */
 
 
3396
                InitialiseThirdAvoidance(sbPtr,manager);
 
 
3397
            return AvRC_Avoidance;
 
 
3398
            }
 
 
3399
        }
 
 
3400
        break;
 
 
3401
        case AvSS_SecondAvoidance:
 
 
3402
        {
 
 
3403
            if (New_NPC_IsObstructed(sbPtr,manager) == 1)
 
 
3404
                return(AvRC_Avoidance); /* Should be in Third Avoidance here. */
 
 
3405
 
 
 
3406
            /* Are we far enough away? */
 
 
3407
            {
 
 
3408
                VECTORCH offset;
 
 
3409
 
 
 
3410
                offset.vx = dynPtr->Position.vx-manager->incidentPoint.vx;
 
 
3411
                offset.vy = dynPtr->Position.vy-manager->incidentPoint.vy;
 
 
3412
                offset.vz = dynPtr->Position.vz-manager->incidentPoint.vz;
 
 
3413
 
 
 
3414
                if (Approximate3dMagnitude(&offset) > manager->recommendedDistance) 
 
 
3415
                {
 
 
3416
                    /* Exit! */
 
 
3417
                    Initialise_AvoidanceManager(manager);
 
 
3418
                    return AvRC_Clear;
 
 
3419
                }
 
 
3420
            }
 
 
3421
 
 
 
3422
            manager->timer -= NormalFrameTime;
 
 
3423
 
 
 
3424
            if (manager->timer <= 0)
 
 
3425
            {
 
 
3426
                /* Ooh, we're in a fix here... */
 
 
3427
                /* Probably need Avoidance 3 for this. */
 
 
3428
                InitialiseThirdAvoidance(sbPtr,manager);
 
 
3429
                return AvRC_Avoidance;
 
 
3430
            }
 
 
3431
        }
 
 
3432
        break;
 
 
3433
        case AvSS_ThirdAvoidance:
 
 
3434
        {
 
 
3435
            /* Let's have a whole function here! */
 
 
3436
            int result = ExecuteThirdAvoidance(sbPtr,manager);
 
 
3437
 
 
 
3438
            if (result == -1) 
 
 
3439
            {
 
 
3440
                /* Totally fubared.  Return failure. */
 
 
3441
                Initialise_AvoidanceManager(manager);
 
 
3442
                return AvRC_Failure;
 
 
3443
            }
 
 
3444
            else if (result == 1) 
 
 
3445
            {
 
 
3446
                /* Success!  Return clear. */
 
 
3447
                Initialise_AvoidanceManager(manager);
 
 
3448
                return AvRC_Clear;
 
 
3449
            }
 
 
3450
            else
 
 
3451
            {
 
 
3452
                /* Still going, return avoidance. */
 
 
3453
                return AvRC_Avoidance;
 
 
3454
            }
 
 
3455
            break;
 
 
3456
        }
 
 
3457
        default:
 
 
3458
            assert(0);
 
 
3459
            break;
 
 
3460
    }
 
 
3461
 
 
 
3462
return AvRC_Avoidance;
 
 
3463
}
 
 
3464
 
 
 
3465
int GetAvoidanceDirection(STRATEGYBLOCK *sbPtr, NPC_AVOIDANCEMANAGER *manager)
 
 
3466
{
 
 
3467
    VECTORCH newDirection1, newDirection2;
 
 
3468
    int dir1dist, dir2dist;
 
 
3469
    MATRIXCH matrix;
 
 
3470
    EULER euler;
 
 
3471
 
 
 
3472
        assert(manager);
 
 
3473
        assert(sbPtr);
 
 
3474
 
 
 
3475
        /* just in case */
 
 
3476
    if(!sbPtr->containingModule)
 
 
3477
        return 0; 
 
 
3478
 
 
 
3479
        /* going for a 90 degree turn + back a bit */
 
 
3480
    newDirection1 = manager->incidenceDirection;
 
 
3481
    newDirection2 = newDirection1;
 
 
3482
    euler.EulerX = 0;
 
 
3483
    euler.EulerY = 1024;
 
 
3484
    euler.EulerZ = 0;
 
 
3485
    CreateEulerMatrix( &euler, &matrix );
 
 
3486
    RotateVector( &newDirection1, &matrix );
 
 
3487
    euler.EulerY = 3072;
 
 
3488
    CreateEulerMatrix( &euler, &matrix );
 
 
3489
    RotateVector( &newDirection2, &matrix );
 
 
3490
 
 
 
3491
        Normalise(&newDirection1);
 
 
3492
        Normalise(&newDirection2);
 
 
3493
 
 
 
3494
        /* test how far we could go in each direction... */
 
 
3495
        {
 
 
3496
               VECTORCH startingPosition = sbPtr->DynPtr->Position;
 
 
3497
               VECTORCH testDirn = newDirection1;
 
 
3498
 
 
 
3499
               CheckForVectorIntersectionWith3dObject(sbPtr->containingModule->m_dptr, &startingPosition, &testDirn);
 
 
3500
 
 
 
3501
           dir1dist = LOS_ObjectHitPtr ? LOS_Lambda : NPC_MAX_VIEWRANGE;
 
 
3502
 
 
 
3503
               startingPosition = sbPtr->DynPtr->Position;
 
 
3504
               testDirn = newDirection2;
 
 
3505
 
 
 
3506
               CheckForVectorIntersectionWith3dObject(sbPtr->containingModule->m_dptr, &startingPosition, &testDirn);
 
 
3507
 
 
 
3508
           dir2dist = LOS_ObjectHitPtr ? LOS_Lambda : NPC_MAX_VIEWRANGE;
 
 
3509
        }
 
 
3510
 
 
 
3511
    manager->avoidanceDirection = (dir1dist > dir2dist) ? newDirection1 : newDirection2;
 
 
3512
 
 
 
3513
return 1;
 
 
3514
}
 
 
3515
 
 
 
3516
static int FrisbeeSight_FrustrumReject(VECTORCH *localOffset)
 
 
3517
{
 
 
3518
    //printf("Local Offset: %d %d %d\n",localOffset->vx,localOffset->vy,localOffset->vz);
 
 
3519
 
 
 
3520
    VECTORCH fixed_offset = *localOffset;
 
 
3521
 
 
 
3522
    if (((fixed_offset.vx < 0) && ( (fixed_offset.vy < -fixed_offset.vx) && (fixed_offset.vy >= 0)))
 
 
3523
         || (((fixed_offset.vy < 0) && (-fixed_offset.vy < -fixed_offset.vx )) && ( fixed_offset.vz > 0 ) ))
 
 
3524
    {
 
 
3525
        /* 90 horizontal, 90 vertical? */
 
 
3526
        return 1;
 
 
3527
    }
 
 
3528
 
 
 
3529
return 0;
 
 
3530
}
 
 
3531
 
 
 
3532
int NPCCanSeeTarget(STRATEGYBLOCK *sbPtr, STRATEGYBLOCK *target)
 
 
3533
{
 
 
3534
    int frustrum_test;
 
 
3535
 
 
 
3536
    if ((target->containingModule == NULL) || (sbPtr->containingModule == NULL))
 
 
3537
            return 0;
 
 
3538
 
 
 
3539
    if ((target->DisplayBlock == NULL) || (sbPtr->DisplayBlock == NULL))
 
 
3540
        return IsModuleVisibleFromModule(target->containingModule, sbPtr->containingModule);
 
 
3541
 
 
 
3542
    switch (sbPtr->type)
 
 
3543
    {
 
 
3544
        case I_BehaviourMarine:
 
 
3545
        {
 
 
3546
            MATRIXCH WtoL;
 
 
3547
            VECTORCH offset, sourcepos, targetpos;
 
 
3548
 
 
 
3549
            MARINE_STATUS_BLOCK *marineStatusPointer = (MARINE_STATUS_BLOCK *)(sbPtr->dataptr);    
 
 
3550
            assert(marineStatusPointer);                      
 
 
3551
            /* Arc reject. */
 
 
3552
 
 
 
3553
            SECTION_DATA *head_sec = GetThisSectionData(marineStatusPointer->HModelController.section_data, "head");
 
 
3554
 
 
 
3555
            if (head_sec)
 
 
3556
            {
 
 
3557
                WtoL = head_sec->SecMat;
 
 
3558
                sourcepos = head_sec->World_Offset;
 
 
3559
            }
 
 
3560
            else
 
 
3561
            {
 
 
3562
                WtoL = sbPtr->DynPtr->OrientMat;
 
 
3563
                GetTargetingPointOfObject_Far(sbPtr,&sourcepos);
 
 
3564
            }
 
 
3565
 
 
 
3566
            GetTargetingPointOfObject_Far(target,&targetpos);
 
 
3567
 
 
 
3568
            offset.vx = sourcepos.vx - targetpos.vx;
 
 
3569
            offset.vy = sourcepos.vy - targetpos.vy;
 
 
3570
            offset.vz = sourcepos.vz - targetpos.vz;
 
 
3571
 
 
 
3572
            TransposeMatrixCH(&WtoL);
 
 
3573
            RotateVector(&offset,&WtoL);
 
 
3574
 
 
 
3575
            frustrum_test = MarineSight_FrustrumReject(sbPtr,&offset,target);
 
 
3576
        }
 
 
3577
        break;
 
 
3578
        case I_BehaviourAutoGun:
 
 
3579
        {
 
 
3580
            /* Less pretentious, based on the SB. */
 
 
3581
            MATRIXCH WtoL;
 
 
3582
            VECTORCH offset, sourcepos, targetpos;
 
 
3583
            /* Arc reject. */
 
 
3584
 
 
 
3585
            WtoL = sbPtr->DynPtr->OrientMat;
 
 
3586
            GetTargetingPointOfObject_Far(sbPtr,&sourcepos);
 
 
3587
            GetTargetingPointOfObject_Far(target,&targetpos);
 
 
3588
 
 
 
3589
            offset.vx = sourcepos.vx-targetpos.vx;
 
 
3590
            offset.vy = sourcepos.vy-targetpos.vy;
 
 
3591
            offset.vz = sourcepos.vz-targetpos.vz;
 
 
3592
 
 
 
3593
            TransposeMatrixCH(&WtoL);
 
 
3594
            RotateVector(&offset,&WtoL);
 
 
3595
 
 
 
3596
            frustrum_test = AGunSight_FrustrumReject(&offset);
 
 
3597
        }
 
 
3598
        break;
 
 
3599
        case I_BehaviourFrisbee:
 
 
3600
        {
 
 
3601
            MATRIXCH WtoL;
 
 
3602
            VECTORCH offset, sourcepos, targetpos;
 
 
3603
 
 
 
3604
            FRISBEE_BEHAV_BLOCK *frisbeeStatusPointer = (FRISBEE_BEHAV_BLOCK *)(sbPtr->dataptr);    
 
 
3605
            assert(frisbeeStatusPointer);                      
 
 
3606
            /* Arc reject. */
 
 
3607
 
 
 
3608
            SECTION_DATA *disc_sec = GetThisSectionData(frisbeeStatusPointer->HModelController.section_data, "Mdisk");
 
 
3609
 
 
 
3610
            if (disc_sec)
 
 
3611
            {
 
 
3612
                WtoL = disc_sec->SecMat;
 
 
3613
                sourcepos = disc_sec->World_Offset;
 
 
3614
            }
 
 
3615
            else
 
 
3616
            {
 
 
3617
                WtoL = sbPtr->DynPtr->OrientMat;
 
 
3618
                GetTargetingPointOfObject_Far(sbPtr,&sourcepos);
 
 
3619
            }
 
 
3620
 
 
 
3621
            GetTargetingPointOfObject_Far(target,&targetpos);
 
 
3622
 
 
 
3623
            offset.vx = sourcepos.vx-targetpos.vx;
 
 
3624
            offset.vy = sourcepos.vy-targetpos.vy;
 
 
3625
            offset.vz = sourcepos.vz-targetpos.vz;
 
 
3626
 
 
 
3627
            TransposeMatrixCH(&WtoL);
 
 
3628
            RotateVector(&offset,&WtoL);
 
 
3629
 
 
 
3630
            frustrum_test = FrisbeeSight_FrustrumReject(&offset);
 
 
3631
        }
 
 
3632
        break;
 
 
3633
        case I_BehaviourXenoborg:
 
 
3634
        {
 
 
3635
            MATRIXCH WtoL;
 
 
3636
            VECTORCH offset, sourcepos, targetpos;
 
 
3637
 
 
 
3638
            XENO_STATUS_BLOCK *xenoStatusPointer = (XENO_STATUS_BLOCK *)(sbPtr->dataptr);    
 
 
3639
            assert(xenoStatusPointer);
 
 
3640
            /* Arc reject. */
 
 
3641
 
 
 
3642
            SECTION_DATA *head_sec = GetThisSectionData(xenoStatusPointer->HModelController.section_data, "head");
 
 
3643
 
 
 
3644
            if (head_sec)
 
 
3645
            {
 
 
3646
                WtoL = head_sec->SecMat;
 
 
3647
                sourcepos = head_sec->World_Offset;
 
 
3648
            }
 
 
3649
            else
 
 
3650
            {
 
 
3651
                WtoL = sbPtr->DynPtr->OrientMat;
 
 
3652
                GetTargetingPointOfObject_Far(sbPtr,&sourcepos);
 
 
3653
            }
 
 
3654
 
 
 
3655
            GetTargetingPointOfObject_Far(target,&targetpos);
 
 
3656
 
 
 
3657
            offset.vx = sourcepos.vx-targetpos.vx;
 
 
3658
            offset.vy = sourcepos.vy-targetpos.vy;
 
 
3659
            offset.vz = sourcepos.vz-targetpos.vz;
 
 
3660
 
 
 
3661
            TransposeMatrixCH(&WtoL);
 
 
3662
            RotateVector(&offset,&WtoL);
 
 
3663
 
 
 
3664
            frustrum_test = XenoSight_FrustrumReject(sbPtr, &offset);
 
 
3665
        }
 
 
3666
        break;
 
 
3667
        default:
 
 
3668
            frustrum_test = 1;
 
 
3669
    }
 
 
3670
 
 
 
3671
    if (frustrum_test)
 
 
3672
    {
 
 
3673
        /* connect eyeposition to head */
 
 
3674
        VECTORCH eyePosition = {0, -1500, 0};
 
 
3675
        RotateVector(&eyePosition, &sbPtr->DynPtr->OrientMat);
 
 
3676
 
 
 
3677
        eyePosition.vx += sbPtr->DynPtr->Position.vx;
 
 
3678
        eyePosition.vy += sbPtr->DynPtr->Position.vy;
 
 
3679
        eyePosition.vz += sbPtr->DynPtr->Position.vz;
 
 
3680
 
 
 
3681
        return IsThisObjectVisibleFromThisPosition_WithIgnore(target->DisplayBlock, sbPtr->DisplayBlock, &eyePosition);
 
 
3682
    }
 
 
3683
 
 
 
3684
return 0;
 
 
3685
}