1 /*------------------------Patrick 26/11/96-----------------------------
2   Source file for FAR AI alien behaviour etc....
3   NB some of the functions in this file are re-used for other NPC AI.
4   --------------------------------------------------------------------*/
5 #include "3dc.h"
6 #include "inline.h"
7 #include "module.h"
8 #include "stratdef.h"
9 #include "gamedef.h"
10 #include "bh_types.h"
11 #include "comp_shp.h"
12 
13 #include "dynblock.h"
14 #include "dynamics.h"
15 
16 #include "pheromon.h"
17 #include "bh_pred.h"
18 #include "bh_alien.h"
19 #include "bh_far.h"
20 #include "pfarlocs.h"
21 #include "bh_gener.h"
22 #include "pvisible.h"
23 #include "bh_marin.h"
24 #include "weapons.h"
25 #include "showcmds.h"
26 #include "pldnet.h"
27 
28 #define UseLocalAssert Yes
29 #include "ourasert.h"
30 #include "dxlog.h"
31 
32 /* prototypes for this file */
33 static void Execute_AFS_Hunt(STRATEGYBLOCK *sbPtr);
34 static void Execute_AFS_Wait(STRATEGYBLOCK *sbPtr);
35 static void Execute_AFS_Retreat(STRATEGYBLOCK *sbPtr);
36 static void Execute_AFS_Wander(STRATEGYBLOCK *sbPtr);
37 static void Execute_AFS_Approach(STRATEGYBLOCK *sbPtr);
38 static void Execute_AFS_Attack(STRATEGYBLOCK *sbPtr);
39 static void Execute_AFS_Avoidance(STRATEGYBLOCK *sbPtr);
40 static int ProcessFarAlienTargetModule(STRATEGYBLOCK *sbPtr, AIMODULE* targetModule);
41 extern void AlienNearState_Dormant(STRATEGYBLOCK *sbPtr);
42 extern void AlienNearState_Awakening(STRATEGYBLOCK *sbPtr);
43 extern void AlienNearState_Taunting(STRATEGYBLOCK *sbPtr);
44 
45 /* external global variables used in this file */
46 extern int NormalFrameTime;
47 extern int ModuleArraySize;
48 extern char *ModuleCurrVisArray;
49 static int entryPointFailures = 0;
50 extern int ShowHiveState;
51 
52 extern SCENE Global_Scene;
53 extern SCENEMODULE **Global_ModulePtr;
54 static MODULE **Global_ModuleArrayPtr;
55 
56 extern void Execute_Alien_Dying(STRATEGYBLOCK *sbPtr);
57 
58 /*--------------------Patrick 9/12/96-----------------------
59   Far Alien behaviour execution shell.
60   Behaviour is defined by a set of states: the AFS_....
61   enumeration defined in bh_alien.h.
62   In addition, the far alien state is defined by a timer: ie
63   any state may 'timeout', possibly forcing a state change.
64   ----------------------------------------------------------*/
FarAlienBehaviour(STRATEGYBLOCK * sbPtr)65 void FarAlienBehaviour(STRATEGYBLOCK *sbPtr)
66 {
67 	ALIEN_STATUS_BLOCK *alienStatusPointer;
68 	char *descriptor;
69 
70 	LOCALASSERT(sbPtr);
71  	/* a precondition: there should be no display block */
72  	LOCALASSERT(!(sbPtr->SBdptr));
73 
74  	/* get the alien's status block */
75 	alienStatusPointer=(ALIEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
76 	LOCALASSERT(alienStatusPointer);
77 
78 	/* execute far behaviour state... */
79 	switch(alienStatusPointer->BehaviourState)
80 	{
81    		case(ABS_Wait):
82 		{
83 			Execute_AFS_Wait(sbPtr);
84    			descriptor="Waiting";
85    			break;
86    		}
87 		case(ABS_Approach):
88 		case(ABS_Jump):
89 		{
90 			Execute_AFS_Approach(sbPtr);
91 			descriptor="Approaching";
92 			break;
93 		}
94 		case(ABS_Hunt):
95 		{
96 			Execute_AFS_Hunt(sbPtr);
97    			descriptor="Hunting";
98    			break;
99    		}
100    		case(ABS_Retreat):
101 		{
102 			Execute_AFS_Retreat(sbPtr);
103    			descriptor="Retreating";
104    			break;
105    		}
106    		case(ABS_Wander):
107 		{
108 			Execute_AFS_Wander(sbPtr);
109    			descriptor="Wandering";
110    			break;
111    		}
112 		case(ABS_Attack):
113 		case(ABS_Pounce):
114 		{
115 			Execute_AFS_Attack(sbPtr);
116 			descriptor="Attacking";
117 			break;
118 		}
119 		case(ABS_Avoidance):
120 		{
121 			Execute_AFS_Avoidance(sbPtr);
122 			descriptor="Avoiding";
123 			break;
124 		}
125 		case(ABS_Dying):
126 		{
127    			descriptor="Dying";
128 			Execute_Alien_Dying(sbPtr);
129 			break;
130 		}
131 		case ABS_Dormant:
132 		{
133 			AlienNearState_Dormant(sbPtr);
134 			descriptor="Dormant";
135 			break;
136 		}
137 		case ABS_Awakening:
138 		{
139 			AlienNearState_Awakening(sbPtr);
140 			descriptor="Awakening";
141 			break;
142 		}
143 		case ABS_Taunting:
144 		{
145 			AlienNearState_Taunting(sbPtr);
146 			descriptor="Taunting";
147 			break;
148 		}
149 		default:
150 		{
151 			descriptor=NULL;
152 			LOCALASSERT(1==0); /* should never get here */
153 		}
154 	}
155 
156 	/* check here to see if the alien is in a doorway....
157 	If so, and it is a proximity door, make sure it is open. */
158 	{
159 		MODULEDOORTYPE doorType = ModuleIsADoor(sbPtr->containingModule);
160 
161 		if(doorType == MDT_ProxDoor) {
162 			((PROXDOOR_BEHAV_BLOCK *)sbPtr->containingModule->m_sbptr->SBdataptr)->alienTrigger = 1;
163 		}
164 	}
165 
166 	if (ShowHiveState) {
167 		/* Alien position print. */
168 
169 		MODULE *thisModule = sbPtr->containingModule;
170 
171 		LOCALASSERT(thisModule);
172 
173 		PrintDebuggingText("This FAR %s ALIEN is in module %d, %s\n",descriptor,thisModule->m_index,thisModule->name);
174 
175 	}
176 
177 	/* make sure that we are inside a module:
178 	if this fires it means that a far alien is not inside
179 	the module it is supposed to be in */
180 	#if UseLocalAssert
181 	{
182 		VECTORCH localCoords;
183 		MODULE *thisModule = sbPtr->containingModule;
184 
185 		LOCALASSERT(thisModule);
186 
187 		localCoords = sbPtr->DynPtr->Position;
188 		localCoords.vx -= thisModule->m_world.vx;
189 		localCoords.vy -= thisModule->m_world.vy;
190 		localCoords.vz -= thisModule->m_world.vz;
191 
192 		if(PointIsInModule(thisModule, &localCoords)==0)
193 		{
194 			textprint("FAR ALIEN MODULE CONTAINMENT FAILURE \n");
195 
196 			LOGDXFMT(("Alien containment failure: %s alien is in %s, position is %d,%d,%d:\nModule extents are: %d:%d, %d:%d, %d:%d",
197 				descriptor,
198 				thisModule->name,localCoords.vx,localCoords.vy,localCoords.vz,
199 				thisModule->m_maxx,thisModule->m_minx,thisModule->m_maxy,thisModule->m_miny,
200 				thisModule->m_maxz,thisModule->m_minz));
201 
202 			LOCALASSERT(1==0);
203 		}
204 	}
205 	#endif
206 
207 	/* textprint("NO ENTRY POINT COUNT %d \n", entryPointFailures);	*/
208 
209 
210 }
211 
212 /*--------------------Patrick 9/12/96-----------------------
213   Execute far alien hunting behaviour....
214   This is basically the default alien behaviour, following
215   the player's pheromone trail.
216 
217   On hunting behaviour, the alien moves between modules
218   using the pre-computed module locations list.  After
219   being relocated to a new module, the alien waits for 'x'
220   seconds (having a good sniff around) then decides which
221   module to move into next.
222 
223   NB only passable modules are updated with player smell,
224   so aliens won't get stuck behind non-automatic doors.
225   ----------------------------------------------------------*/
Execute_AFS_Hunt(STRATEGYBLOCK * sbPtr)226 static void Execute_AFS_Hunt(STRATEGYBLOCK *sbPtr)
227 {
228 	ALIEN_STATUS_BLOCK *alienStatusPointer;
229 	AIMODULE *targetModule = 0;
230 
231  	/* get the alien's status block */
232 	LOCALASSERT(sbPtr);
233 	alienStatusPointer=(ALIEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
234 	LOCALASSERT(alienStatusPointer);
235 
236 	/* Decrement the Far state timer */
237 	alienStatusPointer->FarStateTimer -= NormalFrameTime;
238 
239 	/* check if far state timer has timed-out. If so, it is time
240 	to do something. Otherwise just return. */
241 	if(alienStatusPointer->FarStateTimer>0) return;
242 
243 	/* check the alien hive, to see itf we've switched to regroup:
244 	if so, reset the alien far behaviour state to retreat, with the alien far
245 	state timer set to 0, forcing a retreating movement next frame... */
246 	if(NPCHive.currentState == HS_Regroup)
247 	{
248 		alienStatusPointer->BehaviourState = ABS_Retreat;
249 		alienStatusPointer->FarStateTimer = 0; /* forces execution of new state next frame*/
250 		return;
251 	}
252 
253 	/* check to see if the player is invisible, etc... */
254 //	if ((!AlienIsAwareOfTarget(sbPtr))
255 //		||(alienStatusPointer->Target!=Player->ObStrategyBlock))
256 	if ((!AlienIsAwareOfTarget(sbPtr))&&(alienStatusPointer->Target==Player->ObStrategyBlock)
257 		&&(NPC_IsDead(Player->ObStrategyBlock)))
258 	{
259 		alienStatusPointer->BehaviourState = ABS_Wander;
260 		alienStatusPointer->FarStateTimer = 0; /* forces execution of new state next frame*/
261 		return;
262 	}
263 
264 	/* get the target */
265 	targetModule = FarNPC_GetTargetAIModuleForHunt(sbPtr,1);
266 
267 	/* if there is no target module, it means that the alien is trapped in an
268 	unlinked module. In this case, reset the timer and return. */
269 	if(!targetModule)
270 	{
271 		alienStatusPointer->FarStateTimer = ALIEN_FAR_MOVE_TIME;
272 
273 		#if 0
274 		/* Better have a handler for this. */
275 		alienStatusPointer->BehaviourState = ABS_Dormant;
276 		alienStatusPointer->CurveTimeOut = 0;
277 		if (HModelSequence_Exists(&alienStatusPointer->HModelController,HMSQT_AlienStand,ASSS_Dormant)) {
278 			SetAlienShapeAnimSequence_Core(sbPtr,HMSQT_AlienStand,ASSS_Dormant,-1,ONE_FIXED);
279 		} else {
280 			SetAlienShapeAnimSequence_Core(sbPtr,HMSQT_AlienStand,ASSS_Standard,ONE_FIXED,(ONE_FIXED>>2));
281 		}
282 		#else
283 		alienStatusPointer->BehaviourState = ABS_Wander;
284 		alienStatusPointer->CurveTimeOut = 0;
285 		#endif
286 		return;
287 	}
288 
289 	/* Examine target, and decide what to do */
290 	GLOBALASSERT(AIModuleIsPhysical(targetModule));
291 
292 	#if 0
293 	ProcessFarAlienTargetModule(sbPtr, targetModule);
294 	/* reset the timer */
295 	alienStatusPointer->FarStateTimer = ALIEN_FAR_MOVE_TIME;
296 	#else
297 	alienStatusPointer->FarStateTimer = ProcessFarAlienTargetModule(sbPtr, targetModule);
298 	#endif
299 }
300 
301 
302 /*-----------------------Patrick 9/12/96--------------------------
303   Far alien waiting behaviour functions:
304   Do nothing: when it becomes visible, it will switch to attack, or
305   near wait and then wander...
306   ----------------------------------------------------------------*/
Execute_AFS_Wait(STRATEGYBLOCK * sbPtr)307 static void Execute_AFS_Wait(STRATEGYBLOCK *sbPtr)
308 {
309 	/* do nothing */
310 
311 	//#if ULTRAVIOLENCE
312 	/* Look, now there might be no enemies. */
313 	#if 0
314 	/* ...I think not. */
315 
316 	ALIEN_STATUS_BLOCK *alienStatusPointer;
317 
318 	alienStatusPointer=(ALIEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
319 	LOCALASSERT(alienStatusPointer);
320 
321 	alienStatusPointer->BehaviourState = ABS_Hunt;
322 	alienStatusPointer->FarStateTimer = 0; /* forces execution of new state next frame*/
323 
324 	#endif
325 }
326 
Execute_AFS_Approach(STRATEGYBLOCK * sbPtr)327 static void Execute_AFS_Approach(STRATEGYBLOCK *sbPtr) {
328 
329 	ALIEN_STATUS_BLOCK *alienStatusPointer;
330 
331 	alienStatusPointer=(ALIEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
332 	LOCALASSERT(alienStatusPointer);
333 
334 	/* For the moment, switch to attack or to hunt. */
335 
336 	if (Validate_Target(alienStatusPointer->Target,alienStatusPointer->Target_SBname)==0) {
337 		/* Whoops, no target. */
338 		//GLOBALASSERT(0);
339 		/* Go back to hunt. */
340 		alienStatusPointer->BehaviourState = ABS_Hunt;
341 		alienStatusPointer->FarStateTimer = 0; /* forces execution of new state next frame*/
342 		return;
343 		/* Should check for this in AlienBehaviour. */
344 	}
345 
346 	/* See if we're in the same module. */
347 
348 	if (alienStatusPointer->Target->containingModule->m_aimodule!=sbPtr->containingModule->m_aimodule) {
349 		/* Go back to hunt. */
350 		alienStatusPointer->BehaviourState = ABS_Hunt;
351 		alienStatusPointer->FarStateTimer = 0; /* forces execution of new state next frame*/
352 	} else {
353 		/* Go to attack. */
354 		alienStatusPointer->BehaviourState = ABS_Attack;
355 		alienStatusPointer->FarStateTimer = 0; /* forces execution of new state next frame*/
356 	}
357 
358 }
359 
Execute_AFS_Attack(STRATEGYBLOCK * sbPtr)360 static void Execute_AFS_Attack(STRATEGYBLOCK *sbPtr) {
361 
362 	ALIEN_STATUS_BLOCK *alienStatusPointer;
363 
364 	alienStatusPointer=(ALIEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
365 	LOCALASSERT(alienStatusPointer);
366 
367 	/* For the moment, switch to hunt, or smite the target. */
368 
369 	if (Validate_Target(alienStatusPointer->Target,alienStatusPointer->Target_SBname)==0) {
370 		/* Whoops, no target. */
371 		GLOBALASSERT(0);
372 		/* Should check for this in AlienBehaviour. */
373 	}
374 
375 	/* See if we're in the same module. */
376 
377 	if (alienStatusPointer->Target->containingModule->m_aimodule!=sbPtr->containingModule->m_aimodule) {
378 		/* Go back to hunt. */
379 		alienStatusPointer->BehaviourState = ABS_Hunt;
380 		alienStatusPointer->FarStateTimer = 0; /* forces execution of new state next frame*/
381 	} else {
382 		/* Decrement the Far state timer */
383 		alienStatusPointer->FarStateTimer -= NormalFrameTime;
384 
385 		/* check if far state timer has timed-out. If so, it is time
386 		to do something. Otherwise just return. */
387 		if(alienStatusPointer->FarStateTimer>0) return;
388 
389 		#if 0
390 		CauseDamageToObject(alienStatusPointer->Target,&TemplateAmmo[AMMO_NPC_ALIEN_CLAW].MaxDamage[AvP.Difficulty], ONE_FIXED,NULL);
391 		#endif
392 		/* Kersplat. */
393 		alienStatusPointer->FarStateTimer=ALIEN_ATTACKTIME;
394 		/* Cunning, eh? */
395 
396 	}
397 
398 }
399 
Execute_AFS_Avoidance(STRATEGYBLOCK * sbPtr)400 static void Execute_AFS_Avoidance(STRATEGYBLOCK *sbPtr) {
401 
402 	/* No obstacles in far behaviour. */
403 	ALIEN_STATUS_BLOCK *alienStatusPointer;
404 
405 	alienStatusPointer=(ALIEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
406 	LOCALASSERT(alienStatusPointer);
407 
408 	Initialise_AvoidanceManager(sbPtr,&alienStatusPointer->avoidanceManager);
409 
410 	/* Go directly to hunt.  Do not pass GO. */
411 	alienStatusPointer->BehaviourState = ABS_Hunt;
412 	alienStatusPointer->FarStateTimer = 0; /* forces execution of new state next frame*/
413 
414 }
415 
Execute_AFS_Retreat(STRATEGYBLOCK * sbPtr)416 static void Execute_AFS_Retreat(STRATEGYBLOCK *sbPtr)
417 {
418 	ALIEN_STATUS_BLOCK *alienStatusPointer;
419 	AIMODULE *targetModule = 0;
420 
421  	/* get the alien's status block */
422 	alienStatusPointer=(ALIEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
423 	LOCALASSERT(alienStatusPointer);
424 
425 	/* Decrement the Far state timer */
426 	alienStatusPointer->FarStateTimer -= NormalFrameTime;
427 
428 	/* check if far state timer has timed-out. If so, it is time
429 	to do something. Otherwise just return. */
430 	if(alienStatusPointer->FarStateTimer>0) return;
431 
432 	/* check the alien hive, to see if we've switched to attack:
433 	if so, reset the alien far behaviour state to attack, with the alien far
434 	state timer set to 0, forcing a movement next frame...
435 	NB if we can't attack the player, hunting function will automatically
436 	switch to wander */
437 	if(NPCHive.currentState == HS_Attack)
438 	{
439 		alienStatusPointer->BehaviourState = ABS_Hunt;
440 		alienStatusPointer->FarStateTimer = 0; /* forces execution of new state next frame*/
441 		return;
442 	}
443 
444 	/* get the target module... */
445 	targetModule = FarNPC_GetTargetAIModuleForRetreat(sbPtr);
446 
447 	/* if there is no target module, reset the timer and return. */
448 	if(!targetModule)
449 	{
450 		alienStatusPointer->FarStateTimer = ALIEN_FAR_MOVE_TIME;
451 		return;
452 	}
453 
454 	/* Examine target, and decide what to do */
455 	GLOBALASSERT(AIModuleIsPhysical(targetModule));
456 
457 	#if 0
458 	ProcessFarAlienTargetModule(sbPtr, targetModule);
459 	/* reset the timer */
460 	alienStatusPointer->FarStateTimer = ALIEN_FAR_MOVE_TIME;
461 	#else
462 	alienStatusPointer->FarStateTimer = ProcessFarAlienTargetModule(sbPtr, targetModule);
463 	#endif
464 }
465 
Execute_AFS_Wander(STRATEGYBLOCK * sbPtr)466 static void Execute_AFS_Wander(STRATEGYBLOCK *sbPtr)
467 {
468 	ALIEN_STATUS_BLOCK *alienStatusPointer;
469 	AIMODULE *targetModule = 0;
470 
471  	/* get the alien's status block */
472 	alienStatusPointer=(ALIEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
473 	LOCALASSERT(alienStatusPointer);
474 
475 	/* Decrement the Far state timer */
476 	alienStatusPointer->FarStateTimer -= NormalFrameTime;
477 
478 	/* check if far state timer has timed-out. If so, it is time
479 	to do something. Otherwise just return. */
480 	if(alienStatusPointer->FarStateTimer>0) return;
481 
482 	/* check for state changes:
483 	if hive says retreat, then retreat, regardless of whether or not we can see the player
484 	otherwise, if we can see the player, go to hunt */
485 	if(NPCHive.currentState == HS_Regroup)
486 	{
487 		alienStatusPointer->BehaviourState = ABS_Retreat;
488 		alienStatusPointer->FarStateTimer = 0; /* forces execution of new state next frame*/
489 		return;
490 	}
491 
492 	/* see if we want to switch to attack */
493 	if(AlienIsAwareOfTarget(sbPtr))
494 	{
495 		alienStatusPointer->BehaviourState = ABS_Hunt;
496 		alienStatusPointer->FarStateTimer = 0; /* forces execution of new state next frame*/
497 		return;
498 	}
499 	/* That used to be 100% for ULTRAVIOLENCE. But,
500 	now, there might concievably be NO targets, -> wander. */
501 
502 	/* get the target module... */
503 	targetModule = FarNPC_GetTargetAIModuleForWander(sbPtr,NULL,1);
504 
505 	/* if there is no target module, reset the timer and return. */
506 	if(!targetModule)
507 	{
508 		alienStatusPointer->FarStateTimer = ALIEN_FAR_MOVE_TIME;
509 		return;
510 	}
511 
512 	/* Examine target, and decide what to do */
513 	GLOBALASSERT(AIModuleIsPhysical(targetModule));
514 
515 	#if 0
516 	ProcessFarAlienTargetModule(sbPtr, targetModule);
517 	/* reset the timer */
518 	alienStatusPointer->FarStateTimer = ALIEN_FAR_MOVE_TIME;
519 	#else
520 	alienStatusPointer->FarStateTimer = ProcessFarAlienTargetModule(sbPtr, targetModule);
521 	#endif
522 }
523 
524 
525 
526 
527 /*--------------------Patrick 27/1/97----------------------
528   This function is used by the various far alien behaviour
529   functions to move an alien NPC: it decides whether and how
530   to move an alien into a given target.
531 
532   2/7/97 extra bit:
533   If a location fails, we flip the y-orientation of the npc,
534   so that wandering behaviour can find a new path;
535   ----------------------------------------------------------*/
ProcessFarAlienTargetModule(STRATEGYBLOCK * sbPtr,AIMODULE * targetModule)536 static int ProcessFarAlienTargetModule(STRATEGYBLOCK *sbPtr, AIMODULE* targetModule)
537 {
538 	NPC_TARGETMODULESTATUS targetStatus;
539 	ALIEN_STATUS_BLOCK *alienStatusPointer;
540 	VECTORCH oldPos;
541 
542 	LOCALASSERT(sbPtr);
543 	LOCALASSERT(targetModule);
544 	LOCALASSERT(sbPtr->I_SBtype == I_BehaviourAlien);
545 
546  	/* get the alien's status block */
547 	alienStatusPointer=(ALIEN_STATUS_BLOCK *)(sbPtr->SBdataptr);
548 	LOCALASSERT(alienStatusPointer);
549 
550 	oldPos=sbPtr->DynPtr->Position;
551 
552 	/* get the target module's status, and decide what to do */
553 	targetStatus = GetTargetAIModuleStatus(sbPtr, targetModule,1);
554 	switch(targetStatus)
555 	{
556 		case(NPCTM_NoEntryPoint):
557 		{
558 			/* do nothing: can't get in. */
559 			FarNpc_FlipAround(sbPtr);
560 			entryPointFailures++;
561 			break;
562 		}
563 		case(NPCTM_NormalRoom):
564 		{
565 			/* locate to target	*/
566 			LocateFarNPCInAIModule(sbPtr, targetModule);
567 			break;
568 		}
569 		case(NPCTM_AirDuct):
570 		{
571 			/* locate to target	*/
572 			LocateFarNPCInAIModule(sbPtr, targetModule);
573 			break;
574 		}
575 		case(NPCTM_LiftTeleport):
576 		{
577 			/* do nothing: aliens can't use lifts */
578 			FarNpc_FlipAround(sbPtr);
579 			break;
580 		}
581 		case(NPCTM_ProxDoorOpen):
582 		{
583 			/* locate to target	*/
584 			LocateFarNPCInAIModule(sbPtr, targetModule);
585 			break;
586 		}
587 		case(NPCTM_ProxDoorNotOpen):
588 		{
589 			MODULE *renderModule;
590 			renderModule=*(targetModule->m_module_ptrs);
591 			/* trigger the door, and set timer to quick so we can catch the door when it's open */
592 			((PROXDOOR_BEHAV_BLOCK *)renderModule->m_sbptr->SBdataptr)->alienTrigger = 1;
593 			break;
594 		}
595 		case(NPCTM_LiftDoorOpen):
596 		{
597 			#if 0
598 			/* do nothing: don't really want to go into a lift, or we'll get trapped */
599 			FarNpc_FlipAround(sbPtr);
600 			#else
601 			/* Another pre-written screw up! */
602 			LocateFarNPCInAIModule(sbPtr, targetModule);
603 			#endif
604 			break;
605 		}
606 		case(NPCTM_LiftDoorNotOpen):
607 		{
608 		   /*  do nothing - well, there's nothing we can do, really*/
609 			FarNpc_FlipAround(sbPtr);
610 		   	break;
611 		}
612 		case(NPCTM_SecurityDoorOpen):
613 		{
614 			/* locate to target	*/
615 			LocateFarNPCInAIModule(sbPtr, targetModule);
616 			break;
617 		}
618 		case(NPCTM_SecurityDoorNotOpen):
619 		{
620 		   /*  do nothing - well, there's nothing we can do, really*/
621 			FarNpc_FlipAround(sbPtr);
622 		   	break;
623 		}
624 		default:
625 		{
626 			LOCALASSERT(1==0);
627 		}
628 	}
629 	/* Now, deduce how far it's moved... */
630 	oldPos.vx-=sbPtr->DynPtr->Position.vx;
631 	oldPos.vy-=sbPtr->DynPtr->Position.vy;
632 	oldPos.vz-=sbPtr->DynPtr->Position.vz;
633 	{
634 		int distance;
635 
636 		distance=Approximate3dMagnitude(&oldPos);
637 
638 		if (distance==0) {
639 			return(ALIEN_FAR_MOVE_TIME);
640 		} else {
641 			/* How long? */
642 			return(DIV_FIXED(distance,(ALIEN_FORWARDVELOCITY>>1)));
643 		}
644 	}
645 	return(ALIEN_FAR_MOVE_TIME);
646 }
647 
648 
649 /*-----------------------------------------------------------------------
650 
651 	FUNCTIONS USED BY ALL NPC FAR BEHAVIOUR FUNCTIONS
652 
653 -------------------------------------------------------------------------*/
654 
655 
656 /*--------------------Patrick 27/1/97----------------------
657   This function relocates an NPC into a target module.
658   If the module is visible it uses the entry point. If not
659   it uses an auxilary location, or the entry point if there
660   aren't any.
661 
662   2/7/97: added a bit to update the npc orientation when
663   moving. This orientation is used for wandering behaviour
664   ----------------------------------------------------------*/
LocateFarNPCInModule(STRATEGYBLOCK * sbPtr,MODULE * targetModule)665 void LocateFarNPCInModule(STRATEGYBLOCK *sbPtr, MODULE *targetModule)
666 {
667 	int noOfAuxLocs;
668 	VECTORCH *auxLocsList;
669 	int noOfEntryPoints;
670 	FARENTRYPOINT *entryPointsList;
671 	FARENTRYPOINT *targetEntryPoint;
672 	VECTORCH newPosition;
673 
674 	/* a pre-condition... */
675 	GLOBALASSERT(ModuleIsPhysical(targetModule));
676 
677 	/* now: a few tests for npc's that are generated... (aliens and marines) */
678 	if((sbPtr->I_SBtype==I_BehaviourAlien)||(sbPtr->I_SBtype==I_BehaviourMarine))
679 	{
680 		if((PherAi_Buf[(targetModule->m_index)]) >= MAX_GENERATORNPCSPERMODULE)
681 		{
682 			/* do nothing (since there are only a few auxilary locs per module) */
683 			return;
684 		}
685 
686 		if(ModuleCurrVisArray[(targetModule->m_index)])
687 		{
688 			/* the target is visible... */
689 			if(NumGeneratorNPCsVisible() >= MAX_VISIBLEGENERATORNPCS)
690 			{
691 				/* do nothing: there are already enough visible npcs */
692 				return;
693 			}
694 		}
695 	}
696 
697 	/* now move the npc to it's target... */
698 	noOfAuxLocs = FALLP_AuxLocs[(targetModule->m_index)].numLocations;
699 	auxLocsList = FALLP_AuxLocs[(targetModule->m_index)].locationsList;
700 	noOfEntryPoints = FALLP_EntryPoints[(targetModule->m_index)].numEntryPoints;
701 	entryPointsList = FALLP_EntryPoints[(targetModule->m_index)].entryPointsList;
702 
703 	/* find the entry point for the target */
704 	LOCALASSERT(sbPtr->containingModule);
705 	targetEntryPoint = GetModuleEP(targetModule,(sbPtr->containingModule));
706 	LOCALASSERT(targetEntryPoint);
707 
708 	/* if it's visible, use the entry point.
709 	if it's not visible, use an auxilary location. If there aren't any auxilary
710 	locations, use the entry point. */
711 
712 	if(ModuleCurrVisArray[(targetModule->m_index)])
713 	{
714 		newPosition = targetEntryPoint->position;
715    	}
716 	else
717 	{
718    		/* pick an auxilary location: if there aren't any, use the entry point */
719 		if(noOfAuxLocs)
720 		{
721 			int targetLocInx;
722    			int npcHeight;
723    			targetLocInx = FastRandom() % noOfAuxLocs;
724    			newPosition = auxLocsList[targetLocInx];
725    			/* move up 1/2 npc height, plus a bit more(100). this only applies
726    			to auxilary locations, not eps */
727 			npcHeight = (mainshapelist[sbPtr->shapeIndex]->shapemaxy
728    				- mainshapelist[sbPtr->shapeIndex]->shapeminy)/2;
729    			if(npcHeight>1000) npcHeight = 1000;
730    			newPosition.vy -=(npcHeight + 100);
731    		}
732 		else newPosition = targetEntryPoint->position;
733    	}
734 
735    	/* now set the alien's new position and current module.
736 	   NB this is world position + alien height in y + a little extra in y to make sure */
737 	{
738 		DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr;
739 		LOCALASSERT(dynPtr);
740 
741 		dynPtr->Position = newPosition;
742 		dynPtr->Position.vx += targetModule->m_world.vx;
743 		dynPtr->Position.vy += targetModule->m_world.vy;
744 		dynPtr->Position.vz += targetModule->m_world.vz;
745 		dynPtr->PrevPosition = dynPtr->Position;
746 
747 	   	dynPtr->OrientEuler.EulerX = 0;
748 	   	dynPtr->OrientEuler.EulerZ = 0;
749 	   	{
750 			VECTORCH vec;
751 			vec.vx = targetModule->m_world.vx - sbPtr->containingModule->m_world.vx;
752 			vec.vz = targetModule->m_world.vz - sbPtr->containingModule->m_world.vz;
753 			vec.vy = 0;
754 			Normalise(&vec);
755 	   		dynPtr->OrientEuler.EulerY = ArcTan(vec.vx, vec.vz);
756 		}
757 	}
758 	/* finally, update the alien's module */
759 	sbPtr->containingModule = targetModule;
760 }
761 
LocateFarNPCInAIModule(STRATEGYBLOCK * sbPtr,AIMODULE * targetModule)762 void LocateFarNPCInAIModule(STRATEGYBLOCK *sbPtr, AIMODULE *targetModule)
763 {
764 	int noOfAuxLocs;
765 	VECTORCH *auxLocsList;
766 	int noOfEntryPoints;
767 	FARENTRYPOINT *entryPointsList;
768 	FARENTRYPOINT *targetEntryPoint;
769 	VECTORCH newPosition;
770 	MODULE *renderModule;
771 	int targetLocInx;
772 
773 	SCENEMODULE *smptr;
774 
775 	smptr = Global_ModulePtr[Global_Scene];
776 	Global_ModuleArrayPtr = smptr->sm_marray;
777 
778 	/* now: a few tests for npc's that are generated... (aliens and marines) */
779 	if((sbPtr->I_SBtype==I_BehaviourAlien)||(sbPtr->I_SBtype==I_BehaviourMarine))
780 	{
781 		if((PherAi_Buf[(targetModule->m_index)]) >= MAX_GENERATORNPCSPERMODULE)
782 		{
783 			/* do nothing (since there are only a few auxilary locs per module) */
784 			return;
785 		}
786 
787 		//if(ModuleCurrVisArray[(*(targetModule->m_module_ptrs))->m_index])
788 		if (AIModuleIsVisible(targetModule))
789 		{
790 			/* the target is visible... */
791 			if(NumGeneratorNPCsVisible() >= MAX_VISIBLEGENERATORNPCS)
792 			{
793 				/* do nothing: there are already enough visible npcs */
794 				return;
795 			}
796 		}
797 	}
798 
799 	/* now move the npc to it's target... */
800 	noOfAuxLocs = FALLP_AuxLocs[(targetModule->m_index)].numLocations;
801 	auxLocsList = FALLP_AuxLocs[(targetModule->m_index)].locationsList;
802 	noOfEntryPoints = FALLP_EntryPoints[(targetModule->m_index)].numEntryPoints;
803 	entryPointsList = FALLP_EntryPoints[(targetModule->m_index)].entryPointsList;
804 
805 	/* find the entry point for the target */
806 	LOCALASSERT(sbPtr->containingModule);
807 	targetEntryPoint = GetAIModuleEP(targetModule,(sbPtr->containingModule->m_aimodule));
808 	LOCALASSERT(targetEntryPoint);
809 
810 	/* if it's visible, use the entry point.
811 	if it's not visible, use an auxilary location. If there aren't any auxilary
812 	locations, use the entry point. */
813 
814 	//if(ModuleCurrVisArray[(*(targetModule->m_module_ptrs))->m_index])
815 	if (AIModuleIsVisible(targetModule))
816 	{
817 		newPosition = targetEntryPoint->position;
818 		targetLocInx=-1;
819    	}
820 	else
821 	{
822    		/* pick an auxilary location: if there aren't any, use the entry point */
823 		if(noOfAuxLocs)
824 		{
825 			#if 0
826    			int npcHeight;
827 			#endif
828    			targetLocInx = FastRandom() % noOfAuxLocs;
829    			newPosition = auxLocsList[targetLocInx];
830    			/* move up 1/2 npc height, plus a bit more(100). this only applies
831    			to auxilary locations, not eps */
832 			#if 0
833 			npcHeight = (mainshapelist[sbPtr->shapeIndex]->shapemaxy
834    				- mainshapelist[sbPtr->shapeIndex]->shapeminy)/2;
835    			if(npcHeight>1000) npcHeight = 1000;
836    			newPosition.vy -=(npcHeight + 100);
837 			#endif
838 
839 			if(AvP.Network != I_No_Network)
840 			{
841 				//Multiplayer game
842 				//send this new position to the other players
843 				AddNetMsg_FarAlienPosition(sbPtr,targetModule->m_index,targetLocInx,FALSE);
844 			}
845    		}
846 		else {
847 			newPosition = targetEntryPoint->position;
848 			if(AvP.Network != I_No_Network)
849 			{
850 				//Multiplayer game
851 				//send this new position to the other players
852 				AddNetMsg_FarAlienPosition(sbPtr,targetModule->m_index,sbPtr->containingModule->m_aimodule->m_index,TRUE);
853 			}
854 		}
855    	}
856 
857 	{
858 		VECTORCH temp_Pos;
859 
860 		temp_Pos.vx=newPosition.vx+targetModule->m_world.vx;
861 		temp_Pos.vy=newPosition.vy+targetModule->m_world.vy;
862 		temp_Pos.vz=newPosition.vz+targetModule->m_world.vz;
863 
864 		renderModule=ModuleFromPosition(&temp_Pos,sbPtr->containingModule);
865 		if (renderModule==NULL) {
866 			#if 0
867 			LOGDXFMT(("Right, here comes the assert.\nNoOfAuxLocs %d.\nTargetLocInx %d\n"
868 			,noOfAuxLocs,targetLocInx));
869 			if (*(targetModule->m_module_ptrs)) {
870 				LOGDXFMT(("TargetModule %s.\nContainingModule %s.\n",(*(targetModule->m_module_ptrs))->name,sbPtr->containingModule->name));
871 			} else {
872 				LOGDXFMT(("TargetModule not found.\nContainingModule %s.\n",sbPtr->containingModule->name));
873 			}
874 			LOGDXFMT(("I really should crash out here.\n"));
875 
876 			//GLOBALASSERT(renderModule);
877 			NewOnScreenMessage("DODGED A BULLET.\n");
878 			#endif
879 			return;
880 		}
881 	}
882 
883    	/* now set the alien's new position and current module.
884 	   NB this is world position + alien height in y + a little extra in y to make sure */
885 	{
886 		DYNAMICSBLOCK *dynPtr = sbPtr->DynPtr;
887 		LOCALASSERT(dynPtr);
888 
889 		dynPtr->Position = newPosition;
890 		dynPtr->Position.vx += targetModule->m_world.vx;
891 		dynPtr->Position.vy += targetModule->m_world.vy;
892 		dynPtr->Position.vz += targetModule->m_world.vz;
893 		dynPtr->PrevPosition = dynPtr->Position;
894 
895 	   	dynPtr->OrientEuler.EulerX = 0;
896 	   	dynPtr->OrientEuler.EulerZ = 0;
897 	   	{
898 			VECTORCH vec;
899 			vec.vx = targetModule->m_world.vx - sbPtr->containingModule->m_world.vx;
900 			vec.vz = targetModule->m_world.vz - sbPtr->containingModule->m_world.vz;
901 			vec.vy = 0;
902 			Normalise(&vec);
903 	   		dynPtr->OrientEuler.EulerY = ArcTan(vec.vx, vec.vz);
904 		}
905 	}
906 	/* finally, update the alien's module */
907 	sbPtr->containingModule = renderModule;
908 
909 	#if UseLocalAssert
910 	{
911 		VECTORCH localCoords;
912 		MODULE *thisModule = sbPtr->containingModule;
913 
914 		LOCALASSERT(thisModule);
915 
916 		localCoords = sbPtr->DynPtr->Position;
917 		localCoords.vx -= thisModule->m_world.vx;
918 		localCoords.vy -= thisModule->m_world.vy;
919 		localCoords.vz -= thisModule->m_world.vz;
920 
921 		if(PointIsInModule(thisModule, &localCoords)==0)
922 		{
923 			textprint("FAR ALIEN MODULE CONTAINMENT FAILURE \n");
924 
925 			LOGDXFMT(("Alien containment failure: alien is in %s, position is %d,%d,%d:\nModule extents are: %d:%d, %d:%d, %d:%d",
926 				thisModule->name,localCoords.vx,localCoords.vy,localCoords.vz,
927 				thisModule->m_maxx,thisModule->m_minx,thisModule->m_maxy,thisModule->m_miny,
928 				thisModule->m_maxz,thisModule->m_minz));
929 
930 			LOCALASSERT(1==0);
931 		}
932 	}
933 	#endif
934 }
935 
936 
937 /*--------------------Patrick 10/12/96----------------------
938   This function returns the status of a passed target
939   module that an NPC might want to move to.
940   ----------------------------------------------------------*/
941 
GetTargetAIModuleStatus(STRATEGYBLOCK * sbPtr,AIMODULE * targetModule,int alien)942 NPC_TARGETMODULESTATUS GetTargetAIModuleStatus(STRATEGYBLOCK *sbPtr, AIMODULE *targetModule, int alien)
943 {
944 	MODULEDOORTYPE doorStatus;
945 	MODULE *renderModule;
946 
947 	/* first check for entry point from current module */
948 	{
949 		FARENTRYPOINT *targetEntryPoint;
950 		targetEntryPoint = GetAIModuleEP(targetModule,(sbPtr->containingModule->m_aimodule));
951 
952 		if(targetEntryPoint == (FARENTRYPOINT *)0) return NPCTM_NoEntryPoint;
953 
954 		if (!alien) {
955 			if (targetEntryPoint->alien_only) {
956 				return NPCTM_NoEntryPoint;
957 			}
958 		}
959 	}
960 
961 	renderModule=*(targetModule->m_module_ptrs);
962 
963 	doorStatus = (ModuleIsADoor(renderModule));
964 
965 	switch(doorStatus)
966 	{
967 		case(MDT_ProxDoor):
968 		{
969  			if(GetState(renderModule->m_sbptr))
970  				return NPCTM_ProxDoorOpen;
971 			else
972 				return NPCTM_ProxDoorNotOpen;
973 
974 			break;
975 		}
976 
977 		case(MDT_LiftDoor):
978 		{
979  			if(GetState(renderModule->m_sbptr))
980  				return NPCTM_LiftDoorOpen;
981 			else
982 				return NPCTM_LiftDoorNotOpen;
983 
984 			break;
985 		}
986 
987 		case(MDT_SecurityDoor):
988 		{
989  			if(GetState(renderModule->m_sbptr))
990  				return NPCTM_SecurityDoorOpen;
991 			else
992 				return NPCTM_SecurityDoorNotOpen;
993 
994 			break;
995 		}
996 
997 		default:
998 		{
999 			LOCALASSERT(doorStatus==MDT_NotADoor);
1000 		}
1001 
1002 	}
1003 
1004 	/* now check for lift */
1005 	if(sbPtr->I_SBtype == I_BehaviourLift) return NPCTM_LiftTeleport;
1006 
1007 	/* check for air duct */
1008 	if(renderModule->m_flags & MODULEFLAG_AIRDUCT) return NPCTM_AirDuct;
1009 
1010 	/* at this point, we know it's a room (or stairs) ... */
1011 	return NPCTM_NormalRoom;
1012 }
1013 
1014 /* Patrick 1/7/97-----------------------------------------
1015   A suit of functions for general far NPC use which return a
1016   target module for hunting, wandering, and retreating
1017   ----------------------------------------------------------*/
1018 
FarNPC_GetTargetAIModuleForHunt(STRATEGYBLOCK * sbPtr,int alien)1019 AIMODULE *FarNPC_GetTargetAIModuleForHunt(STRATEGYBLOCK *sbPtr, int alien)
1020 {
1021 	AIMODULE **AdjModuleRefPtr;
1022 	int AdjModuleIndex;
1023 	unsigned int highestSmell = 0;
1024 	AIMODULE* targetModule = (AIMODULE *)0;
1025 
1026 	LOCALASSERT(sbPtr);
1027 	if(sbPtr->containingModule==NULL) return targetModule;
1028 	AdjModuleRefPtr = sbPtr->containingModule->m_aimodule->m_link_ptrs;
1029 
1030 	/* check that there is a list of adjacent modules, and that it is not
1031 	empty (ie points to zero) */
1032 	if(AdjModuleRefPtr)
1033 	{
1034 		while(*AdjModuleRefPtr != 0)
1035 		{
1036 			/* get the index */
1037 			AdjModuleIndex = (*AdjModuleRefPtr)->m_index;
1038 
1039 			if (CheckAdjacencyValidity((*AdjModuleRefPtr), sbPtr->containingModule->m_aimodule,alien)) {
1040 				/* if this adjacent module's smell value is higher than
1041 				the current 'highest smell' record the new module as the
1042 				target. */
1043 				if(PherPl_ReadBuf[AdjModuleIndex] > highestSmell)
1044 				{
1045 					highestSmell = PherPl_ReadBuf[AdjModuleIndex];
1046 					targetModule = *AdjModuleRefPtr;
1047 				}
1048 			}
1049 			/* next adjacent module reference pointer */
1050 			AdjModuleRefPtr++;
1051 		}
1052 	}
1053 	/* Consider my module being the target. */
1054 	if (PherPl_ReadBuf[sbPtr->containingModule->m_aimodule->m_index] > highestSmell) {
1055 		targetModule=sbPtr->containingModule->m_aimodule;
1056 	}
1057 
1058 	return targetModule;
1059 }
1060 
1061 /* Patrick 2/7/96: this function returns a module for wandering to */
FarNPC_GetTargetAIModuleForWander(STRATEGYBLOCK * sbPtr,AIMODULE * exception,int alien)1062 AIMODULE *FarNPC_GetTargetAIModuleForWander(STRATEGYBLOCK *sbPtr, AIMODULE *exception, int alien)
1063 {
1064 	AIMODULE **AdjModuleRefPtr;
1065 	DYNAMICSBLOCK *dynPtr;
1066 	AIMODULE* targetModule = (AIMODULE *)0;
1067 	int bestDirn = -100000;	/* lower than the lowest */
1068 	VECTORCH npcDirn;
1069 
1070 	/* some checks */
1071 	if(!sbPtr) return targetModule;
1072 	if(!sbPtr) return targetModule;
1073 	dynPtr = sbPtr->DynPtr;
1074 	if(!dynPtr) return targetModule;
1075 
1076 	/* get npc 2d directional vector */
1077 	npcDirn.vx = GetSin(dynPtr->OrientEuler.EulerY);
1078 	npcDirn.vz = GetCos(dynPtr->OrientEuler.EulerY);
1079 	npcDirn.vy = 0;
1080 	Normalise(&npcDirn);
1081 
1082 	/* init adjacent module pointer */
1083 	AdjModuleRefPtr = sbPtr->containingModule->m_aimodule->m_link_ptrs;
1084 
1085 	/* check that there is a list of adjacent modules, and that it is not
1086 	empty (ie points to zero) */
1087 	if(AdjModuleRefPtr)
1088 	{
1089 		while(*AdjModuleRefPtr != 0)
1090 		{
1091 			AIMODULE *nextAdjModule = *AdjModuleRefPtr;
1092 			VECTORCH moduleDirn;
1093 			int thisDirn;
1094 
1095 			if (CheckAdjacencyValidity((*AdjModuleRefPtr), sbPtr->containingModule->m_aimodule,alien)) {
1096 				moduleDirn.vx = nextAdjModule->m_world.vx - sbPtr->containingModule->m_world.vx;
1097 				moduleDirn.vz = nextAdjModule->m_world.vz - sbPtr->containingModule->m_world.vz;
1098 				moduleDirn.vy = 0;
1099 				Normalise(&moduleDirn);
1100 
1101 				thisDirn = DotProduct(&npcDirn,&moduleDirn);
1102 				if( (thisDirn>bestDirn) && (exception!=nextAdjModule))
1103 				{
1104 					targetModule = nextAdjModule;
1105 					bestDirn = thisDirn;
1106 				}
1107 			}
1108 			AdjModuleRefPtr++;
1109 		}
1110 	}
1111 	return targetModule;
1112 }
1113 
FarNPC_GetTargetAIModuleForRetreat(STRATEGYBLOCK * sbPtr)1114 AIMODULE *FarNPC_GetTargetAIModuleForRetreat(STRATEGYBLOCK *sbPtr)
1115 {
1116 	extern unsigned int PlayerSmell;
1117 
1118 	AIMODULE **AdjModuleRefPtr;
1119 	AIMODULE* targetModule = (AIMODULE *)0;
1120 	unsigned int targetSmell = PlayerSmell + 1;	/* should be higher than any smell anywhere this frame */
1121 	unsigned int targetNumAdj = 0;
1122 
1123 	LOCALASSERT(sbPtr);
1124 	if(sbPtr->containingModule==NULL) return targetModule;
1125 	AdjModuleRefPtr = sbPtr->containingModule->m_aimodule->m_link_ptrs;
1126 
1127 	/* check that there is a list of adjacent modules, and that it is not
1128 	empty (ie points to zero) */
1129 	if(AdjModuleRefPtr)
1130 	{
1131 		while(*AdjModuleRefPtr != 0)
1132 		{
1133 
1134 			/* get the index */
1135 			int AdjModuleIndex = (*AdjModuleRefPtr)->m_index;
1136 			int AdjModuleSmell = PherPl_ReadBuf[AdjModuleIndex];
1137 			int AdjModuleNumAdjacencies	= NumAdjacentModules((*AdjModuleRefPtr));
1138 
1139 			/* if this adjacent module's smell value is lower than
1140 			the current 'highest smell' record the new module as the
1141 			target.  If they're equal, tie-break on number of adjacencies*/
1142 			if( (!targetModule) ||
1143 				(AdjModuleSmell < targetSmell)||
1144 				((AdjModuleSmell == targetSmell) && (AdjModuleNumAdjacencies > targetNumAdj))
1145 			  )
1146 			{
1147 				targetSmell = PherPl_ReadBuf[AdjModuleIndex];
1148 				targetModule = *AdjModuleRefPtr;
1149 				targetNumAdj = NumAdjacentModules((*AdjModuleRefPtr));
1150 			}
1151 			/* next adjacent module reference pointer */
1152 			AdjModuleRefPtr++;
1153 		}
1154 	}
1155 	return targetModule;
1156 }
1157 
FarNPC_GetTargetAIModuleForGlobalHunt(STRATEGYBLOCK * sbPtr)1158 AIMODULE *FarNPC_GetTargetAIModuleForGlobalHunt(STRATEGYBLOCK *sbPtr)
1159 {
1160 	AIMODULE **AdjModuleRefPtr;
1161 	int AdjModuleIndex;
1162 	unsigned int highestSmell = 0;
1163 	AIMODULE* targetModule = (AIMODULE *)0;
1164 
1165 	LOCALASSERT(sbPtr);
1166 	if(sbPtr->containingModule==NULL) {
1167 		return targetModule;
1168 	}
1169 	AdjModuleRefPtr = sbPtr->containingModule->m_aimodule->m_link_ptrs;
1170 
1171 	/* check that there is a list of adjacent modules, and that it is not
1172 	empty (ie points to zero) */
1173 	if(AdjModuleRefPtr)
1174 	{
1175 		while(*AdjModuleRefPtr != 0)
1176 		{
1177 			/* get the index */
1178 			AdjModuleIndex = (*AdjModuleRefPtr)->m_index;
1179 
1180 			/* if this adjacent module's smell value is higher than
1181 			the current 'highest smell' record the new module as the
1182 			target. */
1183 			if(PherAls_ReadBuf[AdjModuleIndex] > highestSmell)
1184 			{
1185 				highestSmell = PherAls_ReadBuf[AdjModuleIndex];
1186 				targetModule = *AdjModuleRefPtr;
1187 			}
1188 			/* next adjacent module reference pointer */
1189 			AdjModuleRefPtr++;
1190 		}
1191 	}
1192 
1193 	if (highestSmell<PherAls_ReadBuf[sbPtr->containingModule->m_aimodule->m_index]) {
1194 		return (sbPtr->containingModule->m_aimodule);
1195 	} else {
1196 		return (targetModule);
1197 	}
1198 }
1199 
1200 /* CDF 28/5/98 */
FarNPC_GetTargetAIModuleForMarineRespond(STRATEGYBLOCK * sbPtr)1201 AIMODULE *FarNPC_GetTargetAIModuleForMarineRespond(STRATEGYBLOCK *sbPtr)
1202 {
1203 	AIMODULE **AdjModuleRefPtr;
1204 	int AdjModuleIndex;
1205 	unsigned int highestSmell = 0;
1206 	AIMODULE* targetModule = (AIMODULE *)0;
1207 
1208 	LOCALASSERT(sbPtr);
1209 	if(sbPtr->containingModule==NULL) return targetModule;
1210 	AdjModuleRefPtr = sbPtr->containingModule->m_aimodule->m_link_ptrs;
1211 
1212 	/* check that there is a list of adjacent modules, and that it is not
1213 	empty (ie points to zero) */
1214 	if(AdjModuleRefPtr)
1215 	{
1216 		while(*AdjModuleRefPtr != 0)
1217 		{
1218 			/* get the index */
1219 			AdjModuleIndex = (*AdjModuleRefPtr)->m_index;
1220 
1221 			if (CheckAdjacencyValidity((*AdjModuleRefPtr), sbPtr->containingModule->m_aimodule,0)) {
1222 				/* if this adjacent module's smell value is higher than
1223 				the current 'highest smell' record the new module as the
1224 				target. */
1225 				if(PherMars_ReadBuf[AdjModuleIndex] > highestSmell)
1226 				{
1227 					highestSmell = PherMars_ReadBuf[AdjModuleIndex];
1228 					targetModule = *AdjModuleRefPtr;
1229 				}
1230 			}
1231 			/* next adjacent module reference pointer */
1232 			AdjModuleRefPtr++;
1233 		}
1234 	}
1235 	return targetModule;
1236 }
1237 
1238 
1239 /* Patrick 2/7/97:
1240 This function turns the npc around (in y) a random amount: this is used to
1241 turn the npc around if it reaches an impasse in the environment, and the
1242 orientation is used to select a target module for wandering behaviour */
FarNpc_FlipAround(STRATEGYBLOCK * sbPtr)1243 void FarNpc_FlipAround(STRATEGYBLOCK *sbPtr)
1244 {
1245 	DYNAMICSBLOCK *dynPtr;
1246 
1247 	LOCALASSERT(sbPtr);
1248  	/* get the dynamics block */
1249 	dynPtr = sbPtr->DynPtr;
1250 	LOCALASSERT(dynPtr);
1251 
1252 	dynPtr->OrientEuler.EulerY += (1024 + FastRandom()%1024);
1253 	dynPtr->OrientEuler.EulerY &= wrap360;
1254 }
1255