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