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