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(&centreToEdgeVector);
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=&current_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