1 /*----------------Patrick 15/11/96-------------------
2 Source for NPC generator behavior
3 ----------------------------------------------------*/
4 #include "3dc.h"
5 #include "inline.h"
6 #include "module.h"
7 #include "stratdef.h"
8 #include "gamedef.h"
9 #include "bh_types.h"
10
11 #include "bh_alien.h"
12 #include "bh_marin.h"
13 #include "pfarlocs.h"
14 #include "bh_gener.h"
15 #include "pvisible.h"
16 #include "pheromon.h"
17 #include "bh_far.h"
18 #include "pldghost.h"
19
20 #include "load_shp.h"
21 #define UseLocalAssert Yes
22 #include "ourasert.h"
23
24 #include "huddefs.h"
25 #include "showcmds.h"
26
27 /* externs for this file */
28 extern int NormalFrameTime;
29 extern char *ModuleCurrVisArray;
30
31 extern void CreateMarineDynamic(STRATEGYBLOCK* Generator,MARINE_NPC_WEAPONS weapon_for_marine);
32
33 /* globals for this file */
34 HIVE_DATA NPCHive;
35
36 //generator details that are initialised with in setup_generators in the rif load
37 HIVELEVELPARAMS LoadedHiveData;
38 static int TypeOfNPCGenerated;
39
40 /* prototypes for this file */
41 static void ResetGeneratorTimer(GENERATOR_BLOCK *genBlock);
42 static void ResetHiveStateTime(void);
43 int NumNPCsFromThisGenerator(STRATEGYBLOCK* gen_sbptr);
44
45 int SlackTotal;
46 int SlackSize;
47 int ShowSlack=0;
48
49 int NearAliens;
50 int Alt_NearAliens;
51 int FarAliens;
52 int Alt_FarAliens;
53 int ShowHiveState=0;
54
55 /* for testing */
56 #define logGenData 0
57 #if logGenData
58 FILE *logFile;
59 #endif
60
61
62 /*
63 Stuff for adjusting difficulty level according to player's performance
64 */
65 static void GeneratorBalance_Init();
66 static void GeneratorBalance_PerFrameMaintenance();
67 static int GeneratorBalance_GlobalLimit();
68 static int GeneratorBalance_LocalLimit(int normal_limit);
69
70 BOOL UseGeneratorBalance = FALSE;
71 struct
72 {
73
74 int Timer;
75
76 int AIScore;
77 int PlayerScore;
78
79 int PlayerValue;
80
81 int RateMultiplier;
82 int MaxAIShift;
83
84 int Counter;
85
86 int MaxOwnSettingNpc;
87 }GeneratorBalance;
88
ZapSlack(void)89 void ZapSlack(void) {
90
91 SlackTotal=0;
92 SlackSize=0;
93
94 }
95
96 /*----------------Patrick 15/11/96-------------------
97 Initialise a generator.
98 ----------------------------------------------------*/
InitGenerator(void * bhdata,STRATEGYBLOCK * sbPtr)99 void InitGenerator(void *bhdata, STRATEGYBLOCK *sbPtr)
100 {
101
102 GENERATOR_BLOCK *toolsData = (GENERATOR_BLOCK *)bhdata;
103 GENERATOR_BLOCK *genBlock = (GENERATOR_BLOCK *)AllocateMem(sizeof(GENERATOR_BLOCK));
104 if (!genBlock)
105 {
106 memoryInitialisationFailure = 1;
107 return;
108 }
109
110 *genBlock=*toolsData;
111
112 genBlock->Timer = 0;
113 genBlock->RateIncreaseTimer=60*ONE_FIXED;
114 if(genBlock->GenerationRate<=0)
115 genBlock->GenerationRate=1;
116
117 sbPtr->SBdataptr = (void *)genBlock;
118 sbPtr->maintainVisibility = 0;
119 sbPtr->containingModule = NULL;
120
121
122 sbPtr->shapeIndex=0; //shape index not relevant when using hierarchical models
123
124 if(UseGeneratorBalance && genBlock->use_own_max_npc)
125 {
126 GeneratorBalance.MaxOwnSettingNpc+=genBlock->MaxGenNPCs;
127 }
128 }
129
130
131 /*----------------Patrick 22/1/97-------------------
132 Generator Behaviour function.
133 ----------------------------------------------------*/
GeneratorBehaviour(STRATEGYBLOCK * sbPtr)134 void GeneratorBehaviour(STRATEGYBLOCK *sbPtr)
135 {
136 GENERATOR_BLOCK *genBlock = (GENERATOR_BLOCK *)sbPtr->SBdataptr;
137 LOCALASSERT(genBlock);
138
139 /* don't do this for a net game */
140 // textprint("GenBeh\n");
141 if(AvP.Network != I_No_Network && AvP.NetworkAIServer==0) return;
142 // textprint("GenBeh ok\n");
143
144 /* if our number of generators/minute is not > 0, the generator system is
145 effectively turned off... */
146 if(!(NPCHive.generatorNPCsPerMinute>0)) return;
147
148 /*see whether this generator is active*/
149 if(!genBlock->Active)return;
150
151 /*if generator is using its own rate values , check for rate increase */
152 if(genBlock->use_own_rate_values)
153 {
154 genBlock->RateIncreaseTimer-=NormalFrameTime;
155 if(genBlock->RateIncreaseTimer<0)
156 {
157 genBlock->RateIncreaseTimer=ONE_FIXED*60;
158 genBlock->GenerationRate+=genBlock->GenerationRateIncrease;
159 genBlock->GenerationRate=min(genBlock->GenerationRate,GENSPERMINUTE_MAX*100);
160 genBlock->GenerationRate=max(genBlock->GenerationRate,GENSPERMINUTE_MIN*100);
161 }
162 }
163
164 /* check the timer */
165 if(UseGeneratorBalance && AvP.Network != I_No_Network)
166 {
167 genBlock->Timer -= MUL_FIXED(NormalFrameTime,GeneratorBalance.RateMultiplier);
168 }
169 else
170 {
171 genBlock->Timer -= NormalFrameTime;
172 }
173 if(genBlock->Timer > 0) return;
174
175 /* reset the timer */
176 ResetGeneratorTimer(genBlock);
177
178 /* at this point, we have reset the timer, and are set to
179 generate a new npc: however, some things can still
180 prevent this: eg if the module is visible, or there are too
181 many generator npcs in the environment */
182
183 /* if generator is visible, do not create a new NPC */
184 if(ModuleCurrVisArray[(sbPtr->containingModule->m_index)])
185 {
186 #if logGenData
187 {
188 logFile = fopen("D:/PATRICK/GENLOG.TXT","a");
189 fprintf(logFile, "generator: I am visible \n \n");
190 fclose(logFile);
191 }
192 #endif
193 return;
194 }
195
196 /*If in a network game , must also make sure the module isn't visible by other players*/
197 if(AvP.Network!=I_No_Network)
198 {
199 /* go through the strategy blocks looking for players*/
200 int sbIndex;
201 for(sbIndex=0;sbIndex<NumActiveStBlocks;sbIndex++)
202 {
203 STRATEGYBLOCK *playerSbPtr = ActiveStBlockList[sbIndex];
204 NETGHOSTDATABLOCK *ghostData;
205 if(playerSbPtr->I_SBtype!=I_BehaviourNetGhost) continue;
206 ghostData = (NETGHOSTDATABLOCK *)playerSbPtr->SBdataptr;
207
208 if(ghostData->type==I_BehaviourAlienPlayer ||
209 ghostData->type==I_BehaviourMarinePlayer ||
210 ghostData->type==I_BehaviourPredatorPlayer)
211 {
212 /*this is another player*/
213 if(playerSbPtr->containingModule)
214 {
215 if(IsModuleVisibleFromModule(playerSbPtr->containingModule,sbPtr->containingModule))
216 {
217 /*Another player can see this generator's module*/
218 return;
219 }
220 }
221 }
222 }
223 }
224
225 /* if there are too many NPCs in the module, do not create a new one */
226 if(PherAi_Buf[(sbPtr->containingModule->m_aimodule->m_index)] >= MAX_GENERATORNPCSPERMODULE)
227 {
228 #if logGenData
229 {
230 logFile = fopen("D:/PATRICK/GENLOG.TXT","a");
231 fprintf(logFile, "generator: too many aliens in my module \n \n");
232 fclose(logFile);
233 }
234 #endif
235 return;
236 }
237 /* if there are too many npcs in the env, do not create a new one */
238 if(UseGeneratorBalance && AvP.Network != I_No_Network)
239 {
240 if(genBlock->use_own_max_npc)
241 {
242 //check npcs from this generator
243 if (NumNPCsFromThisGenerator(sbPtr) >= GeneratorBalance_LocalLimit(genBlock->MaxGenNPCs))
244 return;
245
246 }
247 else
248 {
249 //check global npc limit
250 if (NumGeneratorNPCsInEnv() >= GeneratorBalance_GlobalLimit())
251 return;
252 }
253 }
254 else
255 {
256 if(genBlock->use_own_max_npc)
257 {
258 //check npcs from this generator
259 if (NumNPCsFromThisGenerator(sbPtr) >= genBlock->MaxGenNPCs)
260 return;
261
262 }
263 else
264 {
265 //check global npc limit
266 if (NumGeneratorNPCsInEnv() >= NPCHive.maxGeneratorNPCs)
267 return;
268 }
269 }
270 /* ok... create an NPC, then */
271 GLOBALASSERT(genBlock->WeightingTotal);
272 {
273 int random=FastRandom()%genBlock->WeightingTotal;
274 if(random<0) random=-random;
275
276 //pulse rifle marine
277 if(random<genBlock->PulseMarine_Wt)
278 {
279 CreateMarineDynamic(sbPtr,MNPCW_PulseRifle);
280 return;
281 }
282 random-=genBlock->PulseMarine_Wt;
283
284 //pistol marine
285 if(random<genBlock->PistolMarine_Wt)
286 {
287 CreateMarineDynamic(sbPtr,MNPCW_PistolMarine);
288 return;
289 }
290 random-=genBlock->PistolMarine_Wt;
291
292 //flamer marine
293 if(random<genBlock->FlameMarine_Wt)
294 {
295 CreateMarineDynamic(sbPtr,MNPCW_Flamethrower);
296 return;
297 }
298 random-=genBlock->FlameMarine_Wt;
299
300 //smartgun marine
301 if(random<genBlock->SmartMarine_Wt)
302 {
303 CreateMarineDynamic(sbPtr,MNPCW_Smartgun);
304 return;
305 }
306 random-=genBlock->SmartMarine_Wt;
307
308 //sadar marine
309 if(random<genBlock->SadarMarine_Wt)
310 {
311 CreateMarineDynamic(sbPtr,MNPCW_SADAR);
312 return;
313 }
314 random-=genBlock->SadarMarine_Wt;
315
316 //grenade marine
317 if(random<genBlock->GrenadeMarine_Wt)
318 {
319 CreateMarineDynamic(sbPtr,MNPCW_GrenadeLauncher);
320 return;
321 }
322 random-=genBlock->GrenadeMarine_Wt;
323
324
325 //minigun marine
326 if(random<genBlock->MinigunMarine_Wt)
327 {
328 CreateMarineDynamic(sbPtr,MNPCW_Minigun);
329 return;
330 }
331 random-=genBlock->MinigunMarine_Wt;
332
333 //shotgun civilian
334 if(random<genBlock->ShotgunCiv_Wt)
335 {
336 CreateMarineDynamic(sbPtr,MNPCW_MShotgun);
337 return;
338 }
339 random-=genBlock->ShotgunCiv_Wt;
340
341 //pistol civilian
342 if(random<genBlock->PistolCiv_Wt)
343 {
344 CreateMarineDynamic(sbPtr,MNPCW_MPistol);
345 return;
346 }
347 random-=genBlock->PistolCiv_Wt;
348
349 //flamer civilian
350 if(random<genBlock->FlameCiv_Wt)
351 {
352 CreateMarineDynamic(sbPtr,MNPCW_MFlamer);
353 return;
354 }
355 random-=genBlock->FlameCiv_Wt;
356
357 //unarmed civilian
358 if(random<genBlock->UnarmedCiv_Wt)
359 {
360 CreateMarineDynamic(sbPtr,MNPCW_MUnarmed);
361 return;
362 }
363 random-=genBlock->UnarmedCiv_Wt;
364
365 //molotov civilian
366 if(random<genBlock->MolotovCiv_Wt)
367 {
368 CreateMarineDynamic(sbPtr,MNPCW_MMolotov);
369 return;
370 }
371 random-=genBlock->MolotovCiv_Wt;
372
373 //alien
374 if(random<genBlock->Alien_Wt)
375 {
376 CreateAlienDynamic(sbPtr,AT_Standard);
377 return;
378 }
379 random-=genBlock->Alien_Wt;
380
381 //predator alien
382 if(random<genBlock->PredAlien_Wt)
383 {
384 CreateAlienDynamic(sbPtr,AT_Predalien);
385 return;
386 }
387 random-=genBlock->PredAlien_Wt;
388
389 //praetorian
390 if(random<genBlock->Praetorian_Wt)
391 {
392 CreateAlienDynamic(sbPtr,AT_Praetorian);
393 return;
394 }
395 random-=genBlock->Praetorian_Wt;
396
397 GLOBALASSERT(0=="Failed to select generator badguy");
398
399 }
400
401 }
402
403
404 /*----------------Patrick 21/1/97-------------------
405 Initialise the hive
406 ----------------------------------------------------*/
InitHive(void)407 void InitHive(void)
408 {
409 extern int NumActiveStBlocks;
410 extern STRATEGYBLOCK *ActiveStBlockList[];
411 int sbIndex = 0;
412 STRATEGYBLOCK *sbPtr;
413
414 GeneratorBalance_Init();
415
416 /* initialise the hive data */
417 NPCHive.currentState = HS_Attack;
418 NPCHive.numGenerators = 0;
419
420 NPCHive.hiveStateTimer = 0;
421 NPCHive.genRateTimer = 0;
422 NPCHive.maxGeneratorNPCs = 0;
423 NPCHive.generatorNPCsPerMinute = 0;
424 NPCHive.deltaGeneratorNPCsPerMinute = 0;
425
426 NPCHive.AliensCanBeGenerated = FALSE;
427 NPCHive.PredAliensCanBeGenerated = FALSE;
428 NPCHive.PraetoriansCanBeGenerated = FALSE;
429
430 SlackTotal=0;
431 SlackSize=0;
432
433 NearAliens=0;
434 Alt_NearAliens=0;
435 FarAliens=0;
436 Alt_FarAliens=0;
437
438 /* don't do any more for a net game */
439 /* actually, do - Richard */
440 // if(AvP.Network != I_No_Network && AvP.NetworkAIServer==0) return;
441 // if(AvP.Network != I_No_Network) return;
442
443
444
445
446 /* set the level parameters */
447 // NPCHive.maxGeneratorNPCs = hiveLevelData[AvP.CurrentEnv].maxGeneratorNPCs;
448 // NPCHive.generatorNPCsPerMinute = hiveLevelData[AvP.CurrentEnv].generatorNPCsPerMinute;
449 // NPCHive.deltaGeneratorNPCsPerMinute = hiveLevelData[AvP.CurrentEnv].deltaGeneratorNPCsPerMinute;
450 // NPCHive.genRateTimer = (ONE_FIXED*60);
451 //
452 // /* validate these parameters */
453 // if(NPCHive.maxGeneratorNPCs > MAXGENNPCS_MAX)
454 // NPCHive.maxGeneratorNPCs = MAXGENNPCS_MAX;
455 // if(NPCHive.maxGeneratorNPCs < MAXGENNPCS_MIN)
456 // NPCHive.maxGeneratorNPCs = MAXGENNPCS_MIN;
457 // if(NPCHive.generatorNPCsPerMinute > GENSPERMINUTE_MAX)
458 // NPCHive.generatorNPCsPerMinute = GENSPERMINUTE_MAX;
459 // if(NPCHive.generatorNPCsPerMinute < GENSPERMINUTE_MIN)
460 // NPCHive.generatorNPCsPerMinute = GENSPERMINUTE_MIN;
461 // if(NPCHive.deltaGeneratorNPCsPerMinute > INCREASEGENSPERMINUTE_MAX)
462 // NPCHive.deltaGeneratorNPCsPerMinute = INCREASEGENSPERMINUTE_MAX;
463 // if(NPCHive.deltaGeneratorNPCsPerMinute < INCREASEGENSPERMINUTE_MIN)
464 // NPCHive.deltaGeneratorNPCsPerMinute = INCREASEGENSPERMINUTE_MIN;
465 //
466 // /* init the hive timer */
467 // ResetHiveStateTime();
468
469 /* Now in ActivateHive. */
470
471 /* Some futher generator initialisations: work out what modules the generators are
472 in, and how many generators there are.
473 */
474 sbIndex = 0;
475 while(sbIndex < NumActiveStBlocks)
476 {
477 sbPtr = ActiveStBlockList[sbIndex++];
478 if(sbPtr->I_SBtype == I_BehaviourGenerator)
479 {
480 GENERATOR_BLOCK *genBlock = (GENERATOR_BLOCK *)sbPtr->SBdataptr;
481 sbPtr->containingModule = ModuleFromPosition(&genBlock->Position, (MODULE *)0);
482 LOCALASSERT(sbPtr->containingModule);
483 NPCHive.numGenerators++;
484 /* init generator times to something quite small...
485 so that we get some npcs in the env quickly */
486 genBlock->Timer = ONE_FIXED;
487
488 //work out which types of alien can be generated on this level (for multiplayer)
489 if(genBlock->Alien_Wt) NPCHive.AliensCanBeGenerated = TRUE;
490 if(genBlock->PredAlien_Wt) NPCHive.PredAliensCanBeGenerated = TRUE;
491 if(genBlock->Praetorian_Wt) NPCHive.PraetoriansCanBeGenerated = TRUE;
492 }
493 }
494
495 #if logGenData
496 {
497 logFile = fopen("D:/PATRICK/GENLOG.TXT","w");
498 fprintf(logFile, "GENERATOR/HIVE DATA LOG \n \n");
499 fprintf(logFile, "num Geners: %d \n",NPCHive.numGenerators);
500 fprintf(logFile, "hive timer: %d \n",NPCHive.hiveStateTimer);
501 fprintf(logFile, "max npcs: %d \n",NPCHive.maxGeneratorNPCs);
502 fprintf(logFile, "npcs per min: %d \n",NPCHive.generatorNPCsPerMinute);
503 fprintf(logFile, "change in npcs per min: %d \n \n",NPCHive.deltaGeneratorNPCsPerMinute);
504 fclose(logFile);
505 }
506 #endif
507
508 ActivateHive();
509
510 }
511
ActivateHive(void)512 void ActivateHive(void) {
513
514 /* Placed in for Jules's level... CDF 1/12/97, Deadline day! */
515
516 NPCHive.maxGeneratorNPCs=LoadedHiveData.maxGeneratorNPCs;
517 NPCHive.generatorNPCsPerMinute=LoadedHiveData.generatorNPCsPerMinute;
518 NPCHive.deltaGeneratorNPCsPerMinute=LoadedHiveData.deltaGeneratorNPCsPerMinute;
519 NPCHive.genRateTimer=60*ONE_FIXED;
520
521 /* validate these parameters */
522 if(NPCHive.maxGeneratorNPCs > MAXGENNPCS_MAX)
523 NPCHive.maxGeneratorNPCs = MAXGENNPCS_MAX;
524 if(NPCHive.maxGeneratorNPCs < MAXGENNPCS_MIN)
525 NPCHive.maxGeneratorNPCs = MAXGENNPCS_MIN;
526 if(NPCHive.generatorNPCsPerMinute > GENSPERMINUTE_MAX)
527 NPCHive.generatorNPCsPerMinute = GENSPERMINUTE_MAX;
528 if(NPCHive.generatorNPCsPerMinute < GENSPERMINUTE_MIN)
529 NPCHive.generatorNPCsPerMinute = GENSPERMINUTE_MIN;
530 if(NPCHive.deltaGeneratorNPCsPerMinute > INCREASEGENSPERMINUTE_MAX)
531 NPCHive.deltaGeneratorNPCsPerMinute = INCREASEGENSPERMINUTE_MAX;
532 if(NPCHive.deltaGeneratorNPCsPerMinute < INCREASEGENSPERMINUTE_MIN)
533 NPCHive.deltaGeneratorNPCsPerMinute = INCREASEGENSPERMINUTE_MIN;
534
535 /* init the hive timer */
536 ResetHiveStateTime();
537
538 }
539
DeActivateHive(void)540 void DeActivateHive(void) {
541
542 NPCHive.genRateTimer = 0;
543 NPCHive.maxGeneratorNPCs = 0;
544 NPCHive.generatorNPCsPerMinute = 0;
545 NPCHive.deltaGeneratorNPCsPerMinute = 0;
546
547 /* validate these parameters */
548 if(NPCHive.maxGeneratorNPCs > MAXGENNPCS_MAX)
549 NPCHive.maxGeneratorNPCs = MAXGENNPCS_MAX;
550 if(NPCHive.maxGeneratorNPCs < MAXGENNPCS_MIN)
551 NPCHive.maxGeneratorNPCs = MAXGENNPCS_MIN;
552 if(NPCHive.generatorNPCsPerMinute > GENSPERMINUTE_MAX)
553 NPCHive.generatorNPCsPerMinute = GENSPERMINUTE_MAX;
554 if(NPCHive.generatorNPCsPerMinute < GENSPERMINUTE_MIN)
555 NPCHive.generatorNPCsPerMinute = GENSPERMINUTE_MIN;
556 if(NPCHive.deltaGeneratorNPCsPerMinute > INCREASEGENSPERMINUTE_MAX)
557 NPCHive.deltaGeneratorNPCsPerMinute = INCREASEGENSPERMINUTE_MAX;
558 if(NPCHive.deltaGeneratorNPCsPerMinute < INCREASEGENSPERMINUTE_MIN)
559 NPCHive.deltaGeneratorNPCsPerMinute = INCREASEGENSPERMINUTE_MIN;
560
561 /* init the hive timer */
562 ResetHiveStateTime();
563
564 }
565
566 /*---------------------Patrick 21/1/97------------------------
567 Do hive management:
568 generator time is decreased at the start of an attack phase
569 ------------------------------------------------------------*/
DoHive(void)570 void DoHive(void)
571 {
572 /* don't do this for a net game */
573 if(AvP.Network != I_No_Network && AvP.NetworkAIServer==0) return;
574 // if(AvP.Network != I_No_Network) return;
575
576 if(AvP.Network != I_No_Network)
577 {
578 GeneratorBalance_PerFrameMaintenance();
579 }
580
581 NearAliens=Alt_NearAliens;
582 Alt_NearAliens=0;
583 FarAliens=Alt_FarAliens;
584 Alt_FarAliens=0;
585
586 /* chack hive state timer */
587 NPCHive.hiveStateTimer -= NormalFrameTime;
588 if(NPCHive.hiveStateTimer <= 0)
589 {
590 /* state change */
591 if(NPCHive.currentState == HS_Attack)
592 {
593 #if ULTRAVIOLENCE
594 /* Hackery. An experiment. CDF 2/12/97. Ha. */
595 NPCHive.currentState = HS_Attack;
596 #else
597 /* switch to regroup */
598 NPCHive.currentState = HS_Regroup;
599 #endif
600 }
601 else
602 {
603 /* switch to attack */
604 #if ULTRAVIOLENCE
605 #else
606 LOCALASSERT(NPCHive.currentState == HS_Regroup);
607 #endif
608 NPCHive.currentState = HS_Attack;
609 }
610 ResetHiveStateTime();
611 }
612
613 /* check gen rate timer */
614 NPCHive.genRateTimer -= NormalFrameTime;
615 if(NPCHive.genRateTimer <= 0)
616 {
617 /* increase frequency */
618 NPCHive.generatorNPCsPerMinute += NPCHive.deltaGeneratorNPCsPerMinute;
619 /* validate this value */
620 if(NPCHive.generatorNPCsPerMinute > GENSPERMINUTE_MAX)
621 NPCHive.generatorNPCsPerMinute = GENSPERMINUTE_MAX;
622 if(NPCHive.generatorNPCsPerMinute < GENSPERMINUTE_MIN)
623 NPCHive.generatorNPCsPerMinute = GENSPERMINUTE_MIN;
624
625 NPCHive.genRateTimer = (ONE_FIXED*60);
626 }
627
628 /* Print hive state. */
629
630 if(NPCHive.currentState == HS_Attack) {
631 if (ShowHiveState) {
632 PrintDebuggingText("Hive Attacking %d...\n",NPCHive.hiveStateTimer);
633 }
634 } else {
635 if (ShowHiveState) {
636 PrintDebuggingText("Hive Retreating %d...\n",NPCHive.hiveStateTimer);
637 }
638 }
639
640 if (ShowHiveState) {
641 PrintDebuggingText("Near Aliens = %d\nFar Aliens = %d\n",NearAliens,FarAliens);
642 }
643
644 if ((SlackSize)&&(ShowSlack)) {
645 int Slack;
646
647 Slack=(SlackTotal/SlackSize);
648 PrintDebuggingText("Average Slack %d\n",Slack);
649 }
650 }
651
652
653 /*----------------Patrick 22/1/97-------------------
654 Timer functions
655 ----------------------------------------------------*/
ResetGeneratorTimer(GENERATOR_BLOCK * genBlock)656 static void ResetGeneratorTimer(GENERATOR_BLOCK *genBlock)
657 {
658 LOCALASSERT(genBlock);
659
660 /* shouldn't be doing this for a net game */
661 // LOCALASSERT(AvP.Network == I_No_Network);
662
663 if(genBlock->use_own_rate_values)
664 {
665 LOCALASSERT(genBlock->GenerationRate);
666 genBlock->Timer = (60 * ONE_FIXED *100)/genBlock->GenerationRate;
667 }
668 else
669 {
670 /* if we get here, there must be at least one generator, and some kind of generator rate */
671 LOCALASSERT(NPCHive.numGenerators>0);
672 LOCALASSERT(NPCHive.generatorNPCsPerMinute>0);
673
674 /* set the timer */
675 genBlock->Timer = (((60 * ONE_FIXED)/(NPCHive.generatorNPCsPerMinute))*NPCHive.numGenerators);
676 }
677 /* randomise +- an eighth */
678 {
679 int baseTime = genBlock->Timer;
680 genBlock->Timer = ((baseTime*7)/8) + (FastRandom()%(baseTime/4));
681 }
682 /* clamp */
683 if(genBlock->Timer>GENERATORTIME_MAX) genBlock->Timer=GENERATORTIME_MAX;
684 if(genBlock->Timer<GENERATORTIME_MIN) genBlock->Timer=GENERATORTIME_MIN;
685
686 #if logGenData
687 {
688 logFile = fopen("D:/PATRICK/GENLOG.TXT","a");
689 fprintf(logFile, "Reset Gen Timer \n");
690 fprintf(logFile, "gen timer to: %d seconds \n \n",genBlock->Timer);
691 fclose(logFile);
692 }
693 #endif
694 }
695
ResetHiveStateTime(void)696 static void ResetHiveStateTime(void)
697 {
698 int baseTime;
699
700 /* shouldn't be doing this for a net game */
701 // LOCALASSERT(AvP.Network == I_No_Network);
702
703 /* set the timer, +- an eighth */
704 baseTime = LoadedHiveData.hiveStateBaseTime;
705 NPCHive.hiveStateTimer = ((baseTime*7)/8) + (FastRandom()%(baseTime/4));
706
707 #if logGenData
708 {
709 logFile = fopen("D:/PATRICK/GENLOG.TXT","a");
710 fprintf(logFile, "Reset Hive Timer \n");
711 fprintf(logFile, "hive timer to: %d seconds \n \n",NPCHive.hiveStateTimer);
712 fclose(logFile);
713 }
714 #endif
715 }
716
717
718 /* Patrick 11/8/97 ---------------------------------------------------
719 A couple of useful functions
720 -------------------------------------------------------------------*/
NumGeneratorNPCsInEnv(void)721 int NumGeneratorNPCsInEnv(void)
722 {
723 extern int NumActiveStBlocks;
724 extern STRATEGYBLOCK *ActiveStBlockList[];
725 int sbIndex = 0;
726 STRATEGYBLOCK *sbPtr;
727 int numOfNPCs = 0;
728
729 while(sbIndex < NumActiveStBlocks)
730 {
731 sbPtr = ActiveStBlockList[sbIndex++];
732 if((sbPtr->I_SBtype == I_BehaviourAlien)||(sbPtr->I_SBtype == I_BehaviourMarine))
733 {
734 //All placed bad guys will have the last character of the sbname as 0
735 //generated badguys shoud have a non-zero last character.
736 if(sbPtr->SBname[SB_NAME_LENGTH-1])
737 {
738 numOfNPCs++;
739 }
740 }
741 }
742
743 #if logGenData
744 {
745 logFile = fopen("D:/PATRICK/GENLOG.TXT","a");
746 fprintf(logFile, "current num gener npcs: %d \n \n",numOfNPCs);
747 fclose(logFile);
748 }
749 #endif
750 return numOfNPCs;
751 }
NumNPCsFromThisGenerator(STRATEGYBLOCK * gen_sbptr)752 int NumNPCsFromThisGenerator(STRATEGYBLOCK* gen_sbptr)
753 {
754 extern int NumActiveStBlocks;
755 extern STRATEGYBLOCK *ActiveStBlockList[];
756 int sbIndex = 0;
757 STRATEGYBLOCK *sbPtr;
758 int numOfNPCs = 0;
759
760 while(sbIndex < NumActiveStBlocks)
761 {
762 sbPtr = ActiveStBlockList[sbIndex++];
763 switch(sbPtr->I_SBtype)
764 {
765 case I_BehaviourAlien :
766 {
767 ALIEN_STATUS_BLOCK* status_block=(ALIEN_STATUS_BLOCK*)sbPtr->SBdataptr;
768 GLOBALASSERT(status_block);
769
770 if(status_block->generator_sbptr==gen_sbptr)
771 {
772 //this alien was produced by this generator
773 numOfNPCs++;
774 }
775 }
776 break;
777
778 case I_BehaviourMarine :
779 {
780 MARINE_STATUS_BLOCK* status_block=(MARINE_STATUS_BLOCK*)sbPtr->SBdataptr;
781 GLOBALASSERT(status_block);
782
783 if(status_block->generator_sbptr==gen_sbptr)
784 {
785 //this marine was produced by this generator
786 numOfNPCs++;
787 }
788 }
789 break;
790
791 default: ; /* do nothing */
792 }
793
794 }
795
796 return numOfNPCs;
797 }
798
799
NumGeneratorNPCsVisible(void)800 int NumGeneratorNPCsVisible(void)
801 {
802 extern int NumActiveStBlocks;
803 extern STRATEGYBLOCK *ActiveStBlockList[];
804 int sbIndex = 0;
805 STRATEGYBLOCK *sbPtr;
806 int numOfVisNPCs = 0;
807
808 while(sbIndex < NumActiveStBlocks)
809 {
810 sbPtr = ActiveStBlockList[sbIndex++];
811 if((sbPtr->I_SBtype == I_BehaviourAlien)||(sbPtr->I_SBtype == I_BehaviourMarine))
812 {
813 if(sbPtr->SBdptr)numOfVisNPCs++;
814 }
815 }
816 #if logGenData
817 {
818 logFile = fopen("D:/PATRICK/GENLOG.TXT","a");
819 fprintf(logFile, "current num visible npcs: %d \n \n",numOfVisNPCs);
820 fclose(logFile);
821 }
822 #endif
823 return numOfVisNPCs;
824 }
825
ForceAGenerator_Shell(void)826 void ForceAGenerator_Shell(void) {
827
828 NewOnScreenMessage("FORCING...\n");
829 ForceAGenerator();
830 }
831
ForceAGenerator(void)832 void ForceAGenerator(void)
833 {
834 extern int NumActiveStBlocks;
835 extern STRATEGYBLOCK *ActiveStBlockList[];
836 int sbIndex = 0;
837 STRATEGYBLOCK *sbPtr;
838 #if logGenData
839 {
840 logFile = fopen("D:/PATRICK/GENLOG.TXT","a");
841 fprintf(logFile, "forcing a generator... \n");
842 fclose(logFile);
843 }
844 #endif
845
846 while(sbIndex < NumActiveStBlocks)
847 {
848 sbPtr = ActiveStBlockList[sbIndex++];
849 if(sbPtr->I_SBtype == I_BehaviourGenerator)
850 {
851 GENERATOR_BLOCK *genBlock = (GENERATOR_BLOCK *)sbPtr->SBdataptr;
852 LOCALASSERT(genBlock);
853
854 if(genBlock->Timer>0)
855 {
856 /* found a generator with timer>0, so set it to zero and return */
857 genBlock->Timer = 0;
858 #if logGenData
859 {
860 logFile = fopen("D:/PATRICK/GENLOG.TXT","a");
861 fprintf(logFile, "... forced generator ref %d \n \n",sbIndex);
862 fclose(logFile);
863 }
864 #endif
865 return;
866 }
867 }
868 }
869 #if logGenData
870 {
871 logFile = fopen("D:/PATRICK/GENLOG.TXT","a");
872 fprintf(logFile, "... didn't find one to force \n \n");
873 fclose(logFile);
874 }
875 #endif
876
877 }
878
879
SetHiveParamaters(int enemytype,int max,int genpermin,int deltagenpermin,int time)880 void SetHiveParamaters(int enemytype,int max,int genpermin,int deltagenpermin,int time)
881 {
882 LoadedHiveData.maxGeneratorNPCs=max;
883 LoadedHiveData.generatorNPCsPerMinute=genpermin;
884 LoadedHiveData.deltaGeneratorNPCsPerMinute=deltagenpermin;
885 LoadedHiveData.hiveStateBaseTime=time;
886
887 TypeOfNPCGenerated=enemytype;
888 }
889
890
891 /*--------------------**
892 ** Loading and Saving **
893 **--------------------*/
894 #include "savegame.h"
895 typedef struct generator_save_block
896 {
897 SAVE_BLOCK_STRATEGY_HEADER header;
898
899 int Timer;
900 int Active;
901
902 int GenerationRate; //scaled up by 100
903 int RateIncreaseTimer;
904 }GENERATOR_SAVE_BLOCK;
905
906 //defines for load/save macros
907 #define SAVELOAD_BLOCK block
908 #define SAVELOAD_BEHAV genBlock
909
910
LoadStrategy_Generator(SAVE_BLOCK_STRATEGY_HEADER * header)911 void LoadStrategy_Generator(SAVE_BLOCK_STRATEGY_HEADER* header)
912 {
913 STRATEGYBLOCK* sbPtr;
914 GENERATOR_BLOCK *genBlock;
915 GENERATOR_SAVE_BLOCK* block = (GENERATOR_SAVE_BLOCK*) header;
916
917 //check the size of the save block
918 if(header->size!=sizeof(*block)) return;
919
920 //find the existing strategy block
921 sbPtr = FindSBWithName(header->SBname);
922 if(!sbPtr) return;
923
924 //make sure the strategy found is of the right type
925 if(sbPtr->I_SBtype != I_BehaviourGenerator) return;
926
927 genBlock = (GENERATOR_BLOCK*)sbPtr->SBdataptr;
928
929 //start copying stuff
930
931 COPYELEMENT_LOAD(Timer)
932 COPYELEMENT_LOAD(Active)
933 COPYELEMENT_LOAD(GenerationRate)
934 COPYELEMENT_LOAD(RateIncreaseTimer)
935
936 }
937
SaveStrategy_Generator(STRATEGYBLOCK * sbPtr)938 void SaveStrategy_Generator(STRATEGYBLOCK* sbPtr)
939 {
940 GENERATOR_SAVE_BLOCK *block;
941 GENERATOR_BLOCK *genBlock;
942 genBlock = (GENERATOR_BLOCK*)sbPtr->SBdataptr;
943
944
945 GET_STRATEGY_SAVE_BLOCK(block,sbPtr);
946
947 //start copying stuff
948
949 COPYELEMENT_SAVE(Timer)
950 COPYELEMENT_SAVE(Active)
951 COPYELEMENT_SAVE(GenerationRate)
952 COPYELEMENT_SAVE(RateIncreaseTimer)
953
954 }
955
956
957 /*----------------------------------**
958 ** And now the global hive settings **
959 **----------------------------------*/
960
961
962 typedef struct hive_save_block
963 {
964 SAVE_BLOCK_HEADER header;
965
966 HIVE_STATE currentState;
967 int hiveStateTimer;
968 int genRateTimer;
969 int generatorNPCsPerMinute;
970
971 }HIVE_SAVE_BLOCK;
972
973 #undef SAVELOAD_BLOCK
974 #undef SAVELOAD_BEHAV
975 //defines for load/save macros
976 #define SAVELOAD_BLOCK block
977 #define SAVELOAD_BEHAV (&NPCHive)
978
LoadHiveSettings(SAVE_BLOCK_HEADER * header)979 void LoadHiveSettings(SAVE_BLOCK_HEADER* header)
980 {
981 HIVE_SAVE_BLOCK* block = (HIVE_SAVE_BLOCK*) header;
982
983 //check the size of the save block
984 if(header->size!=sizeof(*block)) return;
985
986 COPYELEMENT_LOAD(currentState)
987 COPYELEMENT_LOAD(hiveStateTimer)
988 COPYELEMENT_LOAD(genRateTimer)
989 COPYELEMENT_LOAD(generatorNPCsPerMinute)
990
991 }
992
SaveHiveSettings()993 void SaveHiveSettings()
994 {
995 HIVE_SAVE_BLOCK* block;
996 GET_SAVE_BLOCK_POINTER(block);
997
998 //fill in the header
999 block->header.type = SaveBlock_GlobalHive;
1000 block->header.size = sizeof(*block);
1001
1002
1003 COPYELEMENT_SAVE(currentState)
1004 COPYELEMENT_SAVE(hiveStateTimer)
1005 COPYELEMENT_SAVE(genRateTimer)
1006 COPYELEMENT_SAVE(generatorNPCsPerMinute)
1007 }
1008
1009
1010
1011
1012
1013
1014
1015
1016 int GeneratorBalance_PlayerScoreValue = 0;
1017
1018 #define GENERATOR_BALANCE_DECAY (ONE_FIXED * .01)
1019 #define GENERATOR_BALANCE_THRESHHOLD (100000)
1020
GeneratorBalance_Init()1021 static void GeneratorBalance_Init()
1022 {
1023 GeneratorBalance.PlayerValue = GeneratorBalance_PlayerScoreValue*100;
1024
1025 GeneratorBalance.Timer = 0;
1026 GeneratorBalance.AIScore = 0;
1027 GeneratorBalance.PlayerScore = 0;
1028
1029 GeneratorBalance.RateMultiplier = ONE_FIXED;
1030 GeneratorBalance.MaxAIShift = 0;
1031 GeneratorBalance.Counter = GENERATOR_BALANCE_THRESHHOLD/2;
1032
1033 GeneratorBalance.MaxOwnSettingNpc = 0;
1034
1035 }
1036
GeneratorBalance_NotePlayerDeath()1037 void GeneratorBalance_NotePlayerDeath()
1038 {
1039 if(!UseGeneratorBalance) return;
1040
1041 GeneratorBalance.PlayerScore += GeneratorBalance.PlayerValue;
1042 }
1043
GeneratorBalance_NoteAIDeath()1044 void GeneratorBalance_NoteAIDeath()
1045 {
1046 if(!UseGeneratorBalance) return;
1047
1048 GeneratorBalance.AIScore+=100;
1049 }
1050
GeneratorBalance_PerFrameMaintenance()1051 static void GeneratorBalance_PerFrameMaintenance()
1052 {
1053 if(GeneratorBalance.PlayerValue!=GeneratorBalance_PlayerScoreValue*100)
1054 {
1055 GeneratorBalance.PlayerValue=GeneratorBalance_PlayerScoreValue*100;
1056 UseGeneratorBalance = (GeneratorBalance.PlayerValue>0);
1057 }
1058
1059 if(!UseGeneratorBalance) return;
1060
1061
1062 // PrintDebuggingText("\n\n\n\n\nAI : %d\n",GeneratorBalance.AIScore);
1063 // PrintDebuggingText("Player : %d\n",GeneratorBalance.PlayerScore);
1064 // PrintDebuggingText("Counter : %d\n",GeneratorBalance.Counter);
1065 PrintDebuggingText("\n\n\nAi Limit Shift : %d\n",GeneratorBalance.MaxAIShift);
1066
1067
1068 GeneratorBalance.Timer += NormalFrameTime;
1069
1070 if(GeneratorBalance.Timer > 10 * ONE_FIXED)
1071 {
1072 GeneratorBalance.Timer-= 10 * ONE_FIXED;
1073
1074 if(GeneratorBalance.PlayerScore>0 || GeneratorBalance.AIScore>0)
1075 {
1076
1077 if(GeneratorBalance.PlayerScore>GeneratorBalance.AIScore)
1078 {
1079 //make things easier
1080 int ratio = DIV_FIXED(GeneratorBalance.PlayerScore+GeneratorBalance.PlayerValue,GeneratorBalance.AIScore+GeneratorBalance.PlayerValue);
1081
1082 /*
1083 if(ratio > (ONE_FIXED*1.1))
1084 {
1085 GeneratorBalance.RateMultiplier = DIV_FIXED(GeneratorBalance.RateMultiplier,ONE_FIXED *1.1);
1086 }
1087 */
1088 {
1089 int decrement = ratio - ONE_FIXED;
1090 if(GeneratorBalance.MaxAIShift < 0)
1091 {
1092 decrement /= (-GeneratorBalance.MaxAIShift)+1;
1093 }
1094
1095 GeneratorBalance.Counter-=decrement;
1096 if(GeneratorBalance.Counter<0)
1097 {
1098 GeneratorBalance.Counter = GENERATOR_BALANCE_THRESHHOLD/2;
1099 GeneratorBalance.MaxAIShift--;
1100 GeneratorBalance.PlayerScore = GeneratorBalance.AIScore;
1101
1102 }
1103
1104 }
1105 }
1106 else
1107 {
1108 //make things harder
1109 int ratio = DIV_FIXED(GeneratorBalance.AIScore+GeneratorBalance.PlayerValue,GeneratorBalance.PlayerScore+GeneratorBalance.PlayerValue);
1110 /*
1111 if(ratio > (ONE_FIXED*1.1))
1112 {
1113 GeneratorBalance.RateMultiplier = MUL_FIXED(GeneratorBalance.RateMultiplier,ONE_FIXED *1.1);
1114 }
1115 */
1116
1117
1118 {
1119 int increment = ratio - ONE_FIXED;
1120 if(GeneratorBalance.MaxAIShift > 0)
1121 {
1122 increment /= GeneratorBalance.MaxAIShift+1;
1123 }
1124
1125 GeneratorBalance.Counter+=increment;
1126 if(GeneratorBalance.Counter>GENERATOR_BALANCE_THRESHHOLD)
1127 {
1128 GeneratorBalance.Counter = GENERATOR_BALANCE_THRESHHOLD/2;
1129 GeneratorBalance.MaxAIShift++;
1130 GeneratorBalance.AIScore = GeneratorBalance.PlayerScore;
1131
1132 }
1133
1134 }
1135 }
1136 GeneratorBalance.AIScore -= MUL_FIXED(GENERATOR_BALANCE_DECAY,GeneratorBalance.AIScore);
1137 GeneratorBalance.PlayerScore -= MUL_FIXED(GENERATOR_BALANCE_DECAY,GeneratorBalance.PlayerScore);
1138 }
1139
1140 if(GeneratorBalance.RateMultiplier > 4*ONE_FIXED) GeneratorBalance.RateMultiplier = 4*ONE_FIXED;
1141 if(GeneratorBalance.RateMultiplier < ONE_FIXED/4) GeneratorBalance.RateMultiplier = ONE_FIXED/4;
1142
1143
1144
1145
1146 }
1147 }
1148
1149
GeneratorBalance_GlobalLimit()1150 static int GeneratorBalance_GlobalLimit()
1151 {
1152 int limit = NPCHive.maxGeneratorNPCs + GeneratorBalance.MaxAIShift;
1153
1154 if(limit < 2) limit = 2;
1155 if(limit > NPCHive.maxGeneratorNPCs +4) limit = NPCHive.maxGeneratorNPCs +4;
1156 return(limit);
1157 }
1158
GeneratorBalance_LocalLimit(int normal_limit)1159 static int GeneratorBalance_LocalLimit(int normal_limit)
1160 {
1161 if(GeneratorBalance.MaxAIShift == 0) return(normal_limit);
1162
1163 if(GeneratorBalance.MaxAIShift < 0)
1164 {
1165 int limit = GeneratorBalance.MaxOwnSettingNpc + GeneratorBalance.MaxAIShift;
1166
1167 if(limit < 2) limit = 2;
1168
1169 if(NumGeneratorNPCsInEnv() >= limit) return(0);
1170 return(normal_limit);
1171 }
1172 else
1173 {
1174 int shift = min(GeneratorBalance.MaxAIShift,4);
1175 int limit = GeneratorBalance.MaxOwnSettingNpc + shift;
1176 int alien_shortfall = limit - NumGeneratorNPCsInEnv();
1177
1178 return(normal_limit + min(alien_shortfall,shift));
1179
1180 }
1181 }
1182