1 #include "Campaign_Init.h"
2 #include "Overhead.h"
3 #include "FileMan.h"
4 #include "Creature_Spreading.h"
5 #include "Campaign_Types.h"
6 #include "Queen_Command.h"
7 #include "Strategic_Movement.h"
8 #include "Game_Event_Hook.h"
9 #include "GameSettings.h"
10 #include "Random.h"
11 #include "Message.h"
12 #include "Font_Control.h"
13 #include "Soldier_Init_List.h"
14 #include "Lighting.h"
15 #include "StrategicMap.h"
16 #include "Game_Clock.h"
17 #include "Strategic_Mines.h"
18 #include "Music_Control.h"
19 #include "ContentMusic.h"
20 #include "Strategic.h"
21 #include "JAScreens.h"
22 #include "Town_Militia.h"
23 #include "Strategic_Town_Loyalty.h"
24 #include "PreBattle_Interface.h"
25 #include "Map_Edgepoints.h"
26 #include "Animation_Data.h"
27 #include "OppList.h"
28 #include "Meanwhile.h"
29 #include "Strategic_AI.h"
30 #include "Map_Information.h"
31 #include "MemMan.h"
32 #include "Debug.h"
33 #include "ScreenIDs.h"
34 #include "GameInstance.h"
35 #include "ContentManager.h"
36 #include "MineModel.h"
37 #include "CreatureLairModel.h"
38 #include "GameInstance.h"
39 #include "ContentManager.h"
40 #include "MineModel.h"
41 
42 #include <algorithm>
43 #include <stdexcept>
44 #include <vector>
45 
46 //GAME BALANCING DEFINITIONS FOR CREATURE SPREADING
47 //Hopefully, adjusting these following definitions will ease the balancing of the
48 //creature spreading.
49 //The one note here is that for any definitions that have a XXX_BONUS at the end of a definition,
50 //it gets added on to it's counterpart via:
51 //								XXX_VALUE + Random( 1 + XXX_BONUS )
52 
53 //This is how often the creatures spread, once the quest begins.  The smaller the gap,
54 //the faster the creatures will advance.  This is also directly related to the reproduction
55 //rates which are applied each time the creatures spread.
56 #define EASY_SPREAD_TIME_IN_MINUTES		510	//easy spreads every 8.5 hours
57 #define NORMAL_SPREAD_TIME_IN_MINUTES		450	//normal spreads every 7.5 hours
58 #define HARD_SPREAD_TIME_IN_MINUTES		390	//hard spreads every 6.5 hours
59 
60 //Once the queen is added to the game, we can instantly let her spread x number of times
61 //to give her a head start.  This can also be a useful tool for having slow reproduction rates
62 //but quicker head start to compensate to make the creatures less aggressive overall.
63 #define EASY_QUEEN_INIT_BONUS_SPREADS		1
64 #define NORMAL_QUEEN_INIT_BONUS_SPREADS		2
65 #define HARD_QUEEN_INIT_BONUS_SPREADS		3
66 
67 //This value modifies the chance to populate a given sector.  This is different from the previous definition.
68 //This value gets applied to a potentially complicated formula, using the creature habitat to modify
69 //chance to populate, along with factoring in the relative distance to the hive range (to promote deeper lair
70 //population increases), etc.  I would recommend not tweaking the value too much in either direction from
71 //zero due to the fact that this can greatly effect spread times and maximum populations.  Basically, if the
72 //creatures are spreading too quickly, increase the value, otherwise decrease it to a negative value
73 #define EASY_POPULATION_MODIFIER		0
74 #define NORMAL_POPULATION_MODIFIER		0
75 #define HARD_POPULATION_MODIFIER		0
76 
77 //Augments the chance that the creatures will attack a town.  The conditions for attacking a town
78 //are based strictly on the occupation of the creatures in each of the four mine exits.  For each creature
79 //there is a base chance of 10% that the creatures will feed sometime during the night.
80 #define EASY_CREATURE_TOWN_AGGRESSIVENESS	-10
81 #define NORMAL_CREATURE_TOWN_AGGRESSIVENESS	0
82 #define HARD_CREATURE_TOWN_AGGRESSIVENESS	10
83 
84 
85 //This is how many creatures the queen produces for each cycle of spreading.  The higher
86 //the numbers the faster the creatures will advance.
87 #define EASY_QUEEN_REPRODUCTION_BASE		6 //6-7
88 #define EASY_QUEEN_REPRODUCTION_BONUS		1
89 #define NORMAL_QUEEN_REPRODUCTION_BASE		7 //7-9
90 #define NORMAL_QUEEN_REPRODUCTION_BONUS		2
91 #define HARD_QUEEN_REPRODUCTION_BASE		9 //9-12
92 #define HARD_QUEEN_REPRODUCTION_BONUS		3
93 
94 //When either in a cave level with blue lights or there is a creature presence, then
95 //we override the normal music with the creature music.  The conditions are maintained
96 //inside the function PrepareCreaturesForBattle() in this module.
97 BOOLEAN gfUseCreatureMusic = FALSE;
98 BOOLEAN gfCreatureMeanwhileScenePlayed = FALSE;
99 
100 struct CREATURE_DIRECTIVE
101 {
102 	CREATURE_DIRECTIVE*     next;
103 	UNDERGROUND_SECTORINFO *pLevel;
104 };
105 
106 CREATURE_DIRECTIVE *gLair;
107 const CreatureLairModel* gLairModel;
108 
109 INT32 giHabitatedDistance = 0;
110 INT32 giPopulationModifier = 0;
111 INT32 giLairID = 0;
112 INT32 giDestroyedLairID = 0;
113 
114 //various information required for keeping track of the battle sector involved for
115 //prebattle interface, autoresolve, etc.
116 INT16 gsCreatureInsertionCode = 0;
117 INT16 gsCreatureInsertionGridNo = 0;
118 UINT8 gubNumCreaturesAttackingTown = 0;
119 UINT8 gubYoungMalesAttackingTown = 0;
120 UINT8 gubYoungFemalesAttackingTown = 0;
121 UINT8 gubAdultMalesAttackingTown = 0;
122 UINT8 gubAdultFemalesAttackingTown = 0;
123 UINT8 gubCreatureBattleCode = CREATURE_BATTLE_CODE_NONE;
124 UINT8 gubSectorIDOfCreatureAttack = 0;
125 
126 
NewDirective(UINT8 ubSectorID,UINT8 ubSectorZ,UINT8 ubCreatureHabitat)127 static CREATURE_DIRECTIVE* NewDirective(UINT8 ubSectorID, UINT8 ubSectorZ, UINT8 ubCreatureHabitat)
128 {
129 	UINT8 ubSectorX, ubSectorY;
130 	CREATURE_DIRECTIVE* const curr = new CREATURE_DIRECTIVE{};
131 	ubSectorX = (UINT8)((ubSectorID % 16) + 1);
132 	ubSectorY = (UINT8)((ubSectorID / 16) + 1);
133 	curr->pLevel = FindUnderGroundSector( ubSectorX, ubSectorY, ubSectorZ );
134 	if( !curr->pLevel )
135 	{
136 		SLOGA("Could not find underground sector node (%c%db_%d) that should exist.",
137 			ubSectorY + 'A' - 1, ubSectorX, ubSectorZ);
138 		delete curr;
139 		return 0;
140 	}
141 
142 	curr->pLevel->ubCreatureHabitat = ubCreatureHabitat;
143 	curr->next = NULL;
144 	return curr;
145 }
146 
InitCreatureLair(const CreatureLairModel * lairModel)147 static void InitCreatureLair(const CreatureLairModel* lairModel)
148 {
149 	gLairModel = lairModel;
150 
151 	CREATURE_DIRECTIVE* curr = NULL;
152 	giLairID = lairModel->lairId;
153 
154 	//initialize the linked list of lairs
155 	for (auto sec : lairModel->lairSectors)
156 	{
157 		auto next = NewDirective(sec.sectorId, sec.sectorLevel, sec.habitatType);
158 		if (sec.habitatType == QUEEN_LAIR && !next->pLevel->ubNumCreatures)
159 		{
160 			next->pLevel->ubNumCreatures = 1;	//for the queen.
161 		}
162 
163 		if (curr == NULL)
164 		{ // first node, set gLair to the start of list
165 			gLair = next;
166 		}
167 		else
168 		{ // append to list
169 			curr->next = next;
170 		}
171 		curr = next;
172 	}
173 }
174 
IsMineInfestible(const MineModel * mine)175 static bool IsMineInfestible(const MineModel* mine)
176 { // If neither head miner was attacked, ore will/has run out nor enemy controlled
177 	UINT8 id = mine->mineId;
178 	MINE_STATUS_TYPE const& m = gMineStatus[id];
179 	return  !m.fAttackedHeadMiner       &&
180 		m.uiOreRunningOutPoint == 0 &&
181 		!StrategicMap[SECTOR_INFO_TO_STRATEGIC_INDEX(mine->entranceSector)].fEnemyControlled;
182 }
183 
184 
InitCreatureQuest()185 void InitCreatureQuest()
186 {
187 	INT32 i=-1;
188 	UINT8 ubChosenMineId;
189 	INT32 iRandom;
190 	INT32 iNumMinesInfestible;
191 	std::vector<UINT8> ubMinesInfestible;
192 
193 	if( giLairID )
194 	{
195 		return; //already active!
196 	}
197 
198 	if( !gfCreatureMeanwhileScenePlayed )
199 	{
200 		//Start the meanwhile scene for the queen ordering the release of the creatures.
201 		HandleCreatureRelease();
202 		gfCreatureMeanwhileScenePlayed = TRUE;
203 	}
204 
205 	giHabitatedDistance = 0;
206 	switch( gGameOptions.ubDifficultyLevel )
207 	{
208 		case DIF_LEVEL_EASY:
209 			giPopulationModifier = EASY_POPULATION_MODIFIER;
210 			break;
211 		case DIF_LEVEL_MEDIUM:
212 			giPopulationModifier = NORMAL_POPULATION_MODIFIER;
213 			break;
214 		case DIF_LEVEL_HARD:
215 			giPopulationModifier = HARD_POPULATION_MODIFIER;
216 			break;
217 	}
218 
219 	/* Determine which of the four mines are infectible by creatures. Infectible
220 	 * mines are those that are player controlled and unlimited. We don't want the
221 	 * creatures to infect the mine that runs out. */
222 	for (auto lair : GCM->getCreatureLairs())
223 	{
224 		auto mine = GCM->getMine(lair->associatedMineId);
225 		if (IsMineInfestible(mine))
226 		{
227 			ubMinesInfestible.push_back(mine->mineId);
228 		}
229 	}
230 
231 	iNumMinesInfestible = ubMinesInfestible.size();
232 	if( !iNumMinesInfestible )
233 	{
234 		return;
235 	}
236 
237 	//Choose one of the infectible mines randomly
238 	iRandom = Random( iNumMinesInfestible );
239 	ubChosenMineId = ubMinesInfestible[iRandom];
240 
241 	//Now, choose a start location for the queen.
242 	auto lairModel = GCM->getCreatureLairByMineId(ubChosenMineId);
243 	InitCreatureLair(lairModel);
244 
245 	// enable the lair entrance
246 	UINT8 entranceSector = lairModel->entranceSector;
247 	UNDERGROUND_SECTORINFO* lairEntrance = FindUnderGroundSector(SECTORX(entranceSector), SECTORY(entranceSector), lairModel->entranceSectorLevel);
248 	if (lairEntrance == NULL)
249 	{
250 		throw std::runtime_error("Lair entrance sector is not defined as an underground sector");
251 	}
252 	lairEntrance->uiFlags |= SF_PENDING_ALTERNATE_MAP;
253 
254 	//Now determine how often we will spread the creatures.
255 	switch( gGameOptions.ubDifficultyLevel )
256 	{
257 		case DIF_LEVEL_EASY:
258 			i = EASY_QUEEN_INIT_BONUS_SPREADS;
259 			AddPeriodStrategicEvent( EVENT_CREATURE_SPREAD, EASY_SPREAD_TIME_IN_MINUTES, 0 );
260 			break;
261 		case DIF_LEVEL_MEDIUM:
262 			i = NORMAL_QUEEN_INIT_BONUS_SPREADS;
263 			AddPeriodStrategicEvent( EVENT_CREATURE_SPREAD, NORMAL_SPREAD_TIME_IN_MINUTES, 0 );
264 			break;
265 		case DIF_LEVEL_HARD:
266 			i = HARD_QUEEN_INIT_BONUS_SPREADS;
267 			AddPeriodStrategicEvent( EVENT_CREATURE_SPREAD, HARD_SPREAD_TIME_IN_MINUTES, 0 );
268 			break;
269 	}
270 
271 	//Set things up so that the creatures can plan attacks on helpless miners and civilians while
272 	//they are sleeping.  They do their planning at 10PM every day, and decide to attack sometime
273 	//during the night.
274 	AddEveryDayStrategicEvent( EVENT_CREATURE_NIGHT_PLANNING, 1320, 0 );
275 
276 	//Got to give the queen some early protection, so do some creature spreading.
277 	while( i-- )
278 	{ //# times spread is based on difficulty, and the values in the defines.
279 		SpreadCreatures();
280 	}
281 }
282 
283 
AddCreatureToNode(CREATURE_DIRECTIVE * node)284 static void AddCreatureToNode(CREATURE_DIRECTIVE* node)
285 {
286 	node->pLevel->ubNumCreatures++;
287 
288 	if( node->pLevel->uiFlags & SF_PENDING_ALTERNATE_MAP )
289 	{ //there is an alternate map meaning that there is a dynamic opening.  From now on
290 		//we substitute this map.
291 		node->pLevel->uiFlags &= ~SF_PENDING_ALTERNATE_MAP;
292 		node->pLevel->uiFlags |= SF_USE_ALTERNATE_MAP;
293 	}
294 }
295 
296 
PlaceNewCreature(CREATURE_DIRECTIVE * node,INT32 iDistance)297 static BOOLEAN PlaceNewCreature(CREATURE_DIRECTIVE* node, INT32 iDistance)
298 {
299 	if( !node )
300 		return FALSE;
301 	//check to see if the creatures are permitted to spread into certain areas.  There are 4 mines (human perspective), and
302 	//creatures won't spread to them until the player controls them.  Additionally, if the player has recently cleared the
303 	//mine, then temporarily prevent the spreading of creatures.
304 
305 	if( giHabitatedDistance == iDistance )
306 	{	//FRONT-LINE CONDITIONS -- consider expansion or frontline fortification.  The formulae used
307 		//in this sector are geared towards outer expansion.
308 		//we have reached the distance limitation for the spreading.  We will determine if
309 		//the area is populated enough to spread further.  The minimum population must be 4 before
310 		//spreading is even considered.
311 		if( node->pLevel->ubNumCreatures*10 - 10 <= (INT32)Random( 60 ) )
312 		{
313 			// x<=1 100%
314 			// x==2  83%
315 			// x==3  67%
316 			// x==4  50%
317 			// x==5  33%
318 			// x==6  17%
319 			// x>=7   0%
320 			AddCreatureToNode( node );
321 			return TRUE;
322 		}
323 	}
324 	else if( giHabitatedDistance > iDistance )
325 	{ //we are within the "safe" habitated area of the creature's area of influence.  The chance of
326 		//increasing the population inside this sector depends on how deep we are within the sector.
327 		if( node->pLevel->ubNumCreatures < MAX_STRATEGIC_TEAM_SIZE ||
328 			(node->pLevel->ubNumCreatures < 32 && node->pLevel->ubCreatureHabitat == QUEEN_LAIR) )
329 		{ //there is ALWAYS a chance to habitate an interior sector, though the chances are slim for
330 			//highly occupied sectors.  This chance is modified by the type of area we are in.
331 			INT32 iAbsoluteMaxPopulation;
332 			INT32 iMaxPopulation=-1;
333 			INT32 iChanceToPopulate;
334 			switch( node->pLevel->ubCreatureHabitat )
335 			{
336 				case QUEEN_LAIR: //Defend the queen bonus
337 					iAbsoluteMaxPopulation = 32;
338 					break;
339 				case LAIR: //Smaller defend the queen bonus
340 					iAbsoluteMaxPopulation = 18;
341 					break;
342 				case LAIR_ENTRANCE: //Smallest defend the queen bonus
343 					iAbsoluteMaxPopulation = 15;
344 					break;
345 				case INNER_MINE: //neg bonus -- actually promotes expansion over population, and decrease max pop here.
346 					iAbsoluteMaxPopulation = 12;
347 					break;
348 				case OUTER_MINE: //neg bonus -- actually promotes expansion over population, and decrease max pop here.
349 					iAbsoluteMaxPopulation = 10;
350 					break;
351 				case FEEDING_GROUNDS: //get free food bonus!  yummy humans :)
352 					iAbsoluteMaxPopulation = 15;
353 					break;
354 				case MINE_EXIT:	//close access to humans (don't want to overwhelm them)
355 					iAbsoluteMaxPopulation = 10;
356 					break;
357 				default:
358 					SLOGA("PlaceNewCreature: invalid habitat type");
359 					return FALSE;
360 			}
361 
362 			switch( gGameOptions.ubDifficultyLevel )
363 			{
364 				case DIF_LEVEL_EASY: //50%
365 					iAbsoluteMaxPopulation /= 2; //Half
366 					break;
367 				case DIF_LEVEL_MEDIUM: //80%
368 					iAbsoluteMaxPopulation = iAbsoluteMaxPopulation * 4 / 5;
369 					break;
370 				case DIF_LEVEL_HARD: //100%
371 					break;
372 			}
373 
374 			//Calculate the desired max population percentage based purely on current distant to creature range.
375 			//The closer we are to the lair, the closer this value will be to 100.
376 			iMaxPopulation = 100 - iDistance * 100 / giHabitatedDistance;
377 			iMaxPopulation = MAX( iMaxPopulation, 25 );
378 			//Now, convert the previous value into a numeric population.
379 			iMaxPopulation = iAbsoluteMaxPopulation * iMaxPopulation / 100;
380 			iMaxPopulation = MAX( iMaxPopulation, 4 );
381 
382 
383 			//The chance to populate a sector is higher for lower populations.  This is calculated on
384 			//the ratio of current population to the max population.
385 			iChanceToPopulate = 100 - node->pLevel->ubNumCreatures * 100 / iMaxPopulation;
386 
387 			if( !node->pLevel->ubNumCreatures || (iChanceToPopulate > (INT32)Random( 100 )
388 					&& iMaxPopulation > node->pLevel->ubNumCreatures) )
389 			{
390 				AddCreatureToNode( node );
391 				return TRUE;
392 			}
393 		}
394 	}
395 	else
396 	{ //we are in a new area, so we will populate it
397 		AddCreatureToNode( node );
398 		giHabitatedDistance++;
399 		return TRUE;
400 	}
401 	if( PlaceNewCreature( node->next, iDistance + 1 ) )
402 		return TRUE;
403 	return FALSE;
404 }
405 
SpreadCreatures()406 void SpreadCreatures()
407 {
408 	UINT16 usNewCreatures=0;
409 
410 	if (giLairID == -1) return;
411 
412 	//queen just produced a litter of creature larvae.  Let's do some spreading now.
413 	switch( gGameOptions.ubDifficultyLevel )
414 	{
415 		case DIF_LEVEL_EASY:
416 			usNewCreatures = (UINT16)(EASY_QUEEN_REPRODUCTION_BASE + Random( 1 + EASY_QUEEN_REPRODUCTION_BONUS ));
417 			break;
418 		case DIF_LEVEL_MEDIUM:
419 			usNewCreatures = (UINT16)(NORMAL_QUEEN_REPRODUCTION_BASE + Random( 1 + NORMAL_QUEEN_REPRODUCTION_BONUS ));
420 			break;
421 		case DIF_LEVEL_HARD:
422 			usNewCreatures = (UINT16)(HARD_QUEEN_REPRODUCTION_BASE + Random( 1 + HARD_QUEEN_REPRODUCTION_BONUS ));
423 			break;
424 	}
425 
426 	while( usNewCreatures-- )
427 	{
428 		//Note, this function can and will fail if the population gets dense.  This is a necessary
429 		//feature.  Otherwise, the queen would fill all the cave levels with MAX_STRATEGIC_TEAM_SIZE monsters, and that would
430 		//be bad.
431 		PlaceNewCreature( gLair, 0 );
432 	}
433 }
434 
435 
AddCreaturesToBattle(UINT8 n_young_males,UINT8 n_young_females,UINT8 n_adult_males,UINT8 n_adult_females)436 static void AddCreaturesToBattle(UINT8 n_young_males, UINT8 n_young_females, UINT8 n_adult_males, UINT8 n_adult_females)
437 {
438 	INT16 const insertion_code = gsCreatureInsertionCode;
439 	UINT8       desired_direction;
440 	switch (insertion_code)
441 	{
442 		case INSERTION_CODE_NORTH:  desired_direction = SOUTHEAST; break;
443 		case INSERTION_CODE_EAST:   desired_direction = SOUTHWEST; break;
444 		case INSERTION_CODE_SOUTH:  desired_direction = NORTHWEST; break;
445 		case INSERTION_CODE_WEST:   desired_direction = NORTHEAST; break;
446 		case INSERTION_CODE_GRIDNO: desired_direction = 0;         break;
447 		default: throw std::logic_error("Invalid direction passed to AddCreaturesToBattle()");
448 	}
449 
450 	MAPEDGEPOINTINFO edgepoint_info;
451 	if (insertion_code != INSERTION_CODE_GRIDNO)
452 	{
453 		ChooseMapEdgepoints(&edgepoint_info, insertion_code, n_young_males + n_young_females + n_adult_males + n_adult_females);
454 	}
455 
456 	std::vector<SoldierBodyType> bodies;
457 	bodies.insert(bodies.end(), n_young_males, YAM_MONSTER);
458 	bodies.insert(bodies.end(), n_young_females, YAF_MONSTER);
459 	bodies.insert(bodies.end(), n_adult_males, AM_MONSTER);
460 	bodies.insert(bodies.end(), n_adult_females, ADULTFEMALEMONSTER);
461 	std::shuffle(bodies.begin(), bodies.end(), gRandomEngine);
462 
463 	UINT8 slot = 0;
464 	for (SoldierBodyType const body : bodies)
465 	{
466 		SOLDIERTYPE* const s = TacticalCreateCreature(body);
467 		s->bHunting                 = TRUE;
468 		s->ubInsertionDirection     = desired_direction;
469 		s->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO;
470 		if (insertion_code == INSERTION_CODE_GRIDNO)
471 		{
472 			s->usStrategicInsertionData = gsCreatureInsertionGridNo;
473 		}
474 		else if (slot < edgepoint_info.ubNumPoints)
475 		{ // Use an edgepoint
476 			s->usStrategicInsertionData = edgepoint_info.sGridNo[slot++];
477 		}
478 		else
479 		{ // No edgepoints left, so put him at the entrypoint
480 			s->ubStrategicInsertionCode = insertion_code;
481 		}
482 		UpdateMercInSector(*s, gWorldSectorX, gWorldSectorY, 0);
483 	}
484 
485 	gsCreatureInsertionCode      = 0;
486 	gsCreatureInsertionGridNo    = 0;
487 	gubNumCreaturesAttackingTown = 0;
488 	gubYoungMalesAttackingTown   = 0;
489 	gubYoungFemalesAttackingTown = 0;
490 	gubAdultMalesAttackingTown   = 0;
491 	gubAdultFemalesAttackingTown = 0;
492 	gubCreatureBattleCode        = CREATURE_BATTLE_CODE_NONE;
493 	gubSectorIDOfCreatureAttack  = 0;
494 	AllTeamsLookForAll(FALSE);
495 }
496 
497 
ChooseTownSectorToAttack(UINT8 ubSectorID,BOOLEAN fSpecificSector)498 static void ChooseTownSectorToAttack(UINT8 ubSectorID, BOOLEAN fSpecificSector)
499 {
500 	const CreatureAttackSector* attackDetails = NULL;
501 	if (gLairModel == NULL)
502 	{
503 		SLOGA("gLairModel is NULL. Something wrong!");
504 		return;
505 	}
506 
507 	// determine town sector to attack
508 	attackDetails = (fSpecificSector) ?
509 		gLairModel->getTownAttackDetails(ubSectorID) : // attack the given sector
510 		gLairModel->chooseTownSectorToAttack()         // pick a sector to attack
511 	;
512 	if (!attackDetails)
513 	{
514 		SLOGA("ChooseTownSectorToAttack: invalid SectorID");
515 		return;
516 	}
517 
518 	// determine how the enemies enter the sector
519 	gubSectorIDOfCreatureAttack = attackDetails->sectorId;
520 	gsCreatureInsertionCode = attackDetails->insertionCode;
521 	if (gsCreatureInsertionCode == INSERTION_CODE_GRIDNO)
522 	{
523 		gsCreatureInsertionGridNo = attackDetails->insertionGridNo;
524 	}
525 }
526 
CreatureAttackTown(UINT8 ubSectorID,BOOLEAN fSpecificSector)527 void CreatureAttackTown(UINT8 ubSectorID, BOOLEAN fSpecificSector)
528 { //This is the launching point of the creature attack.
529 	UNDERGROUND_SECTORINFO *pSector;
530 	UINT8 ubSectorX, ubSectorY;
531 
532 	if( gfWorldLoaded && gTacticalStatus.fEnemyInSector )
533 	{ //Battle currently in progress, repost the event
534 		AddStrategicEvent( EVENT_CREATURE_ATTACK, GetWorldTotalMin() + Random( 10 ), ubSectorID );
535 		return;
536 	}
537 
538 	gubCreatureBattleCode = CREATURE_BATTLE_CODE_NONE;
539 
540 	ubSectorX = SECTORX(ubSectorID);
541 	ubSectorY = SECTORY(ubSectorID);
542 
543 	if (!fSpecificSector)
544 	{
545 		//Record the number of creatures in the sector.
546 		pSector = FindUnderGroundSector( ubSectorX, ubSectorY, 1 );
547 		if( !pSector )
548 		{
549 			CreatureAttackTown(ubSectorID, TRUE);
550 			return;
551 		}
552 		gubNumCreaturesAttackingTown = pSector->ubNumCreatures;
553 		if( !gubNumCreaturesAttackingTown )
554 		{
555 			CreatureAttackTown(ubSectorID, TRUE);
556 			return;
557 		}
558 
559 		pSector->ubNumCreatures = 0;
560 
561 		//Choose one of the town sectors to attack.  Sectors closer to
562 		//the mine entrance have a greater chance of being chosen.
563 		ChooseTownSectorToAttack( ubSectorID, FALSE );
564 		ubSectorX = SECTORX(gubSectorIDOfCreatureAttack);
565 		ubSectorY = SECTORY(gubSectorIDOfCreatureAttack);
566 	}
567 	else
568 	{
569 		ChooseTownSectorToAttack(ubSectorID, TRUE);
570 		gubNumCreaturesAttackingTown = 5;
571 	}
572 
573 	//Now that the sector has been chosen, attack it!
574 	if( PlayerGroupsInSector( ubSectorX, ubSectorY, 0 ) )
575 	{ //we have players in the sector
576 		if( ubSectorX == gWorldSectorX && ubSectorY == gWorldSectorY && !gbWorldSectorZ )
577 		{ //This is the currently loaded sector.  All we have to do is change the music and insert
578 			//the creatures tactically.
579 			if( guiCurrentScreen == GAME_SCREEN )
580 			{
581 				gubCreatureBattleCode = CREATURE_BATTLE_CODE_TACTICALLYADD;
582 			}
583 			else
584 			{
585 				gubCreatureBattleCode = CREATURE_BATTLE_CODE_PREBATTLEINTERFACE;
586 			}
587 		}
588 		else
589 		{
590 			gubCreatureBattleCode = CREATURE_BATTLE_CODE_PREBATTLEINTERFACE;
591 		}
592 	}
593 	else if( CountAllMilitiaInSector( ubSectorX, ubSectorY ) )
594 	{ //we have militia in the sector
595 		gubCreatureBattleCode = CREATURE_BATTLE_CODE_AUTORESOLVE;
596 	}
597 	else if( !StrategicMap[ ubSectorX + MAP_WORLD_X * ubSectorY ].fEnemyControlled )
598 	{ //player controlled sector -- eat some civilians
599 		AdjustLoyaltyForCivsEatenByMonsters( ubSectorX, ubSectorY, gubNumCreaturesAttackingTown );
600 		SectorInfo[ ubSectorID ].ubDayOfLastCreatureAttack = (UINT8)GetWorldDay();
601 		return;
602 	}
603 	else
604 	{ //enemy controlled sectors don't get attacked.
605 		return;
606 	}
607 
608 	SectorInfo[ ubSectorID ].ubDayOfLastCreatureAttack = (UINT8)GetWorldDay();
609 	switch( gubCreatureBattleCode )
610 	{
611 		case CREATURE_BATTLE_CODE_AUTORESOLVE:
612 			gfAutomaticallyStartAutoResolve = TRUE;
613 			/* FALLTHROUGH */
614 		case CREATURE_BATTLE_CODE_PREBATTLEINTERFACE:
615 			InitPreBattleInterface(0, true);
616 			break;
617 		case CREATURE_BATTLE_CODE_TACTICALLYADD:
618 			PrepareCreaturesForBattle();
619 			break;
620 	}
621 	InterruptTime();
622 	PauseGame();
623 	LockPauseState(LOCK_PAUSE_CREATURE_ATTACK);
624 }
625 
626 
DeleteDirectiveNode(CREATURE_DIRECTIVE ** node)627 static void DeleteDirectiveNode(CREATURE_DIRECTIVE** node)
628 {
629 	if( (*node)->next )
630 		DeleteDirectiveNode( &((*node)->next) );
631 	delete *node;
632 	*node = NULL;
633 }
634 
635 //Recursively delete all nodes (from the top down).
DeleteCreatureDirectives()636 void DeleteCreatureDirectives()
637 {
638 	if( gLair )
639 		DeleteDirectiveNode( &gLair );
640 	giLairID = 0;
641 }
642 
EndCreatureQuest()643 void EndCreatureQuest()
644 {
645 	CREATURE_DIRECTIVE *curr;
646 	UNDERGROUND_SECTORINFO *pSector;
647 	INT32 i;
648 
649 	//By setting the lairID to -1, when it comes time to spread creatures,
650 	//They will get subtracted instead.
651 	giDestroyedLairID = giLairID;
652 	giLairID = -1;
653 
654 	//Also nuke all of the creatures in all of the other mine sectors.  This
655 	//is keyed on the fact that the queen monster is killed.
656 	curr = gLair;
657 	if( curr )
658 	{ //skip first node (there could be other creatures around.
659 		curr = curr->next;
660 	}
661 	while( curr )
662 	{
663 		curr->pLevel->ubNumCreatures = 0;
664 		curr = curr->next;
665 	}
666 
667 	//Remove the creatures that are trapped underneath Tixa
668 	pSector = FindUnderGroundSector( 9, 10, 2 );
669 	if( pSector )
670 	{
671 		pSector->ubNumCreatures = 0;
672 	}
673 
674 	//Also find and nuke all creatures on any surface levels!!!
675 	//KM: Sept 3, 1999 patch
676 	for( i = 0; i < 255; i++ )
677 	{
678 		SectorInfo[ i ].ubNumCreatures = 0;
679 		SectorInfo[ i ].ubCreaturesInBattle = 0;
680 	}
681 }
682 
683 
CreaturesInUndergroundSector(UINT8 ubSectorID,UINT8 ubSectorZ)684 static UINT8 CreaturesInUndergroundSector(UINT8 ubSectorID, UINT8 ubSectorZ)
685 {
686 	UNDERGROUND_SECTORINFO *pSector;
687 	UINT8 ubSectorX, ubSectorY;
688 	ubSectorX = (UINT8)SECTORX( ubSectorID );
689 	ubSectorY = (UINT8)SECTORY( ubSectorID );
690 	pSector = FindUnderGroundSector( ubSectorX, ubSectorY, ubSectorZ );
691 	if( pSector )
692 		return pSector->ubNumCreatures;
693 	return 0;
694 }
695 
MineClearOfMonsters(UINT8 ubMineIndex)696 BOOLEAN MineClearOfMonsters( UINT8 ubMineIndex )
697 {
698 	Assert(ubMineIndex < MAX_NUMBER_OF_MINES);
699 
700 	if( !gMineStatus[ ubMineIndex ].fPrevInvadedByMonsters )
701 	{
702 		auto mine = GCM->getMine(ubMineIndex);
703 		if (mine == NULL)
704 		{
705 			SLOGE("Attempting to check if mine is clear but mine index is invalid (%d).", ubMineIndex);
706 			return true;
707 		}
708 		for (auto sector : mine->mineSectors)
709 		{
710 			if (CreaturesInUndergroundSector(sector[0], sector[1]))
711 			{
712 				return false;
713 			}
714 		}
715 	}
716 	else
717 	{ //mine was previously invaded by creatures.  Don't allow mine production until queen is dead.
718 		if( giLairID != -1 )
719 		{
720 			return FALSE;
721 		}
722 	}
723 	return TRUE;
724 }
725 
DetermineCreatureTownComposition(UINT8 ubNumCreatures,UINT8 * pubNumYoungMales,UINT8 * pubNumYoungFemales,UINT8 * pubNumAdultMales,UINT8 * pubNumAdultFemales)726 void DetermineCreatureTownComposition(UINT8 ubNumCreatures,
727 					UINT8 *pubNumYoungMales, UINT8 *pubNumYoungFemales,
728 					UINT8 *pubNumAdultMales, UINT8 *pubNumAdultFemales)
729 {
730 	INT32 i, iRandom;
731 	UINT8 ubYoungMalePercentage = 10;
732 	UINT8 ubYoungFemalePercentage = 65;
733 	UINT8 ubAdultMalePercentage = 5;
734 	UINT8 ubAdultFemalePercentage = 20;
735 
736 	//First step is to convert the percentages into the numbers we will use.
737 	ubYoungFemalePercentage += ubYoungMalePercentage;
738 	ubAdultMalePercentage += ubYoungFemalePercentage;
739 	ubAdultFemalePercentage += ubAdultMalePercentage;
740 	if( ubAdultFemalePercentage != 100 )
741 	{
742 		SLOGA("Percentage for adding creatures don't add up to 100." );
743 	}
744 	//Second step is to determine the breakdown of the creatures randomly.
745 	i = ubNumCreatures;
746 	while( i-- )
747 	{
748 		iRandom = Random( 100 );
749 		if( iRandom < ubYoungMalePercentage )
750 			(*pubNumYoungMales)++;
751 		else if( iRandom < ubYoungFemalePercentage )
752 			(*pubNumYoungFemales)++;
753 		else if( iRandom < ubAdultMalePercentage )
754 			(*pubNumAdultMales)++;
755 		else
756 			(*pubNumAdultFemales)++;
757 	}
758 }
759 
DetermineCreatureTownCompositionBasedOnTacticalInformation(UINT8 * pubNumCreatures,UINT8 * pubNumYoungMales,UINT8 * pubNumYoungFemales,UINT8 * pubNumAdultMales,UINT8 * pubNumAdultFemales)760 void DetermineCreatureTownCompositionBasedOnTacticalInformation(UINT8 *pubNumCreatures,
761 								UINT8 *pubNumYoungMales, UINT8 *pubNumYoungFemales,
762 								UINT8 *pubNumAdultMales, UINT8 *pubNumAdultFemales)
763 {
764 	SECTORINFO *pSector;
765 
766 	pSector = &SectorInfo[ SECTOR( gWorldSectorX, gWorldSectorY ) ];
767 	*pubNumCreatures = 0;
768 	pSector->ubNumCreatures = 0;
769 	pSector->ubCreaturesInBattle = 0;
770 	CFOR_EACH_IN_TEAM(s, CREATURE_TEAM)
771 	{
772 		if (s->bInSector && s->bLife)
773 		{
774 			switch (s->ubBodyType)
775 			{
776 				case ADULTFEMALEMONSTER:
777 					(*pubNumCreatures)++;
778 					(*pubNumAdultFemales)++;
779 					break;
780 				case AM_MONSTER:
781 					(*pubNumCreatures)++;
782 					(*pubNumAdultMales)++;
783 					break;
784 				case YAF_MONSTER:
785 					(*pubNumCreatures)++;
786 					(*pubNumYoungFemales)++;
787 					break;
788 				case YAM_MONSTER:
789 					(*pubNumCreatures)++;
790 					(*pubNumYoungMales)++;
791 					break;
792 			}
793 		}
794 	}
795 }
796 
797 
798 
PrepareCreaturesForBattle()799 BOOLEAN PrepareCreaturesForBattle()
800 {
801 	UNDERGROUND_SECTORINFO *pSector;
802 	INT32 i, iRandom;
803 	BOOLEAN fQueen;
804 	UINT8 ubLarvaePercentage;
805 	UINT8 ubInfantPercentage;
806 	UINT8 ubYoungMalePercentage;
807 	UINT8 ubYoungFemalePercentage;
808 	UINT8 ubAdultMalePercentage;
809 	UINT8 ubAdultFemalePercentage;
810 	UINT8 ubCreatureHabitat;
811 	UINT8 ubNumLarvae = 0;
812 	UINT8 ubNumInfants = 0;
813 	UINT8 ubNumYoungMales = 0;
814 	UINT8 ubNumYoungFemales = 0;
815 	UINT8 ubNumAdultMales = 0;
816 	UINT8 ubNumAdultFemales = 0;
817 	UINT8 ubNumCreatures;
818 
819 	if( !gubCreatureBattleCode )
820 	{
821 		//By default, we only play creature music in the cave levels (the creature levels all consistently
822 		//have blue lights while human occupied mines have red lights.  We always play creature music
823 		//when creatures are in the level.
824 		gfUseCreatureMusic = LightGetColor()->b != 0;
825 
826 		if( !gbWorldSectorZ )
827 			return FALSE;  //Creatures don't attack overworld with this battle code.
828 		pSector = FindUnderGroundSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ );
829 		if( !pSector )
830 		{
831 			return FALSE;
832 		}
833 		if( !pSector->ubNumCreatures )
834 		{
835 			return FALSE;
836 		}
837 		gfUseCreatureMusic = TRUE; //creatures are here, so play creature music
838 		ubCreatureHabitat = pSector->ubCreatureHabitat;
839 		ubNumCreatures = pSector->ubNumCreatures;
840 	}
841 	else
842 	{ //creatures are attacking a town sector
843 		gfUseCreatureMusic = TRUE;
844 		SetMusicMode( MUSIC_TACTICAL_NOTHING );
845 		ubCreatureHabitat = MINE_EXIT;
846 		ubNumCreatures = gubNumCreaturesAttackingTown;
847 	}
848 
849 	switch( ubCreatureHabitat )
850 	{
851 		case QUEEN_LAIR:
852 			fQueen = TRUE;
853 			ubLarvaePercentage = 20;
854 			ubInfantPercentage = 40;
855 			ubYoungMalePercentage = 0;
856 			ubYoungFemalePercentage = 0;
857 			ubAdultMalePercentage = 30;
858 			ubAdultFemalePercentage = 10;
859 			break;
860 		case LAIR:
861 			fQueen = FALSE;
862 			ubLarvaePercentage = 15;
863 			ubInfantPercentage = 35;
864 			ubYoungMalePercentage = 10;
865 			ubYoungFemalePercentage = 5;
866 			ubAdultMalePercentage = 25;
867 			ubAdultFemalePercentage = 10;
868 			break;
869 		case LAIR_ENTRANCE:
870 			fQueen = FALSE;
871 			ubLarvaePercentage = 0;
872 			ubInfantPercentage = 15;
873 			ubYoungMalePercentage = 30;
874 			ubYoungFemalePercentage = 10;
875 			ubAdultMalePercentage = 35;
876 			ubAdultFemalePercentage = 10;
877 			break;
878 		case INNER_MINE:
879 			fQueen = FALSE;
880 			ubLarvaePercentage = 0;
881 			ubInfantPercentage = 0;
882 			ubYoungMalePercentage = 20;
883 			ubYoungFemalePercentage = 40;
884 			ubAdultMalePercentage = 10;
885 			ubAdultFemalePercentage = 30;
886 			break;
887 		case OUTER_MINE:
888 		case MINE_EXIT:
889 			fQueen = FALSE;
890 			ubLarvaePercentage = 0;
891 			ubInfantPercentage = 0;
892 			ubYoungMalePercentage = 10;
893 			ubYoungFemalePercentage = 65;
894 			ubAdultMalePercentage = 5;
895 			ubAdultFemalePercentage = 20;
896 			break;
897 		default:
898 			SLOGE("Invalid creature habitat ID of %d for PrepareCreaturesForBattle.  Ignoring...", ubCreatureHabitat );
899 			return FALSE;
900 	}
901 
902 	//First step is to convert the percentages into the numbers we will use.
903 	if( fQueen )
904 	{
905 		ubNumCreatures--;
906 	}
907 	ubInfantPercentage += ubLarvaePercentage;
908 	ubYoungMalePercentage += ubInfantPercentage;
909 	ubYoungFemalePercentage += ubYoungMalePercentage;
910 	ubAdultMalePercentage += ubYoungFemalePercentage;
911 	ubAdultFemalePercentage += ubAdultMalePercentage;
912 	if( ubAdultFemalePercentage != 100 )
913 	{
914 		SLOGA("Percentage for adding creatures don't add up to 100." );
915 	}
916 	//Second step is to determine the breakdown of the creatures randomly.
917 	i = ubNumCreatures;
918 	while( i-- )
919 	{
920 		iRandom = Random( 100 );
921 		if( iRandom < ubLarvaePercentage )
922 			ubNumLarvae++;
923 		else if( iRandom < ubInfantPercentage )
924 			ubNumInfants++;
925 		else if( iRandom < ubYoungMalePercentage )
926 			ubNumYoungMales++;
927 		else if( iRandom < ubYoungFemalePercentage )
928 			ubNumYoungFemales++;
929 		else if( iRandom < ubAdultMalePercentage )
930 			ubNumAdultMales++;
931 		else
932 			ubNumAdultFemales++;
933 	}
934 
935 	if( gbWorldSectorZ )
936 	{
937 		UNDERGROUND_SECTORINFO *pUndergroundSector;
938 		pUndergroundSector = FindUnderGroundSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ );
939 		if( !pUndergroundSector )
940 		{ //No info?!!!!!
941 			SLOGA("Please report underground sector you are in or going to and send save if possible." );
942 			return FALSE;
943 		}
944 		pUndergroundSector->ubCreaturesInBattle = pUndergroundSector->ubNumCreatures;
945 	}
946 	else
947 	{
948 		SECTORINFO *pSector;
949 		pSector = &SectorInfo[ SECTOR( gWorldSectorX, gWorldSectorY ) ];
950 		pSector->ubNumCreatures = ubNumCreatures;
951 		pSector->ubCreaturesInBattle = ubNumCreatures;
952 	}
953 
954 	switch( gubCreatureBattleCode )
955 	{
956 		case CREATURE_BATTLE_CODE_NONE: //in the mines
957 			AddSoldierInitListCreatures( fQueen, ubNumLarvae, ubNumInfants,
958 				ubNumYoungMales, ubNumYoungFemales, ubNumAdultMales, ubNumAdultFemales );
959 			break;
960 		case CREATURE_BATTLE_CODE_TACTICALLYADD: //creature attacking a town sector
961 		case CREATURE_BATTLE_CODE_PREBATTLEINTERFACE:
962 			AddCreaturesToBattle( ubNumYoungMales, ubNumYoungFemales, ubNumAdultMales, ubNumAdultFemales );
963 			break;
964 		case CREATURE_BATTLE_CODE_AUTORESOLVE:
965 			return FALSE;
966 	}
967 	return TRUE;
968 }
969 
CreatureNightPlanning()970 void CreatureNightPlanning()
971 { //Check the populations of the mine exits, and factor a chance for them to attack at night.
972 	for (auto lair: GCM->getCreatureLairs())
973 	{
974 		auto mine = GCM->getMine(lair->associatedMineId);
975 		UINT8 ubNumCreatures = CreaturesInUndergroundSector(mine->entranceSector, 1);
976 		if (ubNumCreatures > 1 && ubNumCreatures * 10 > (INT32)PreRandom(100))
977 		{ //10% chance for each creature to decide it's time to attack.
978 			AddStrategicEvent(EVENT_CREATURE_ATTACK, GetWorldTotalMin() + 1 + PreRandom(429), mine->entranceSector);
979 		}
980 	}
981 }
982 
983 
CheckConditionsForTriggeringCreatureQuest(INT16 sSectorX,INT16 sSectorY,INT8 bSectorZ)984 void CheckConditionsForTriggeringCreatureQuest( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ )
985 {
986 	UINT8 ubValidMines = 0;
987 	if( !gGameOptions.fSciFi )
988 		return; //No scifi, no creatures...
989 	if( giLairID )
990 		return;	//Creature quest already begun
991 
992 	//Count the number of "infectible mines" the player occupies
993 	for (auto lair : GCM->getCreatureLairs())
994 	{
995 		auto mine = GCM->getMine(lair->associatedMineId);
996 		auto sectorIndex = SECTOR_INFO_TO_STRATEGIC_INDEX(mine->entranceSector);
997 		if (!StrategicMap[sectorIndex].fEnemyControlled)
998 		{
999 			ubValidMines++;
1000 		}
1001 	}
1002 
1003 	if( ubValidMines >= 3 )
1004 	{
1005 		InitCreatureQuest();
1006 	}
1007 }
1008 
1009 
SaveCreatureDirectives(HWFILE const hFile)1010 void SaveCreatureDirectives(HWFILE const hFile)
1011 {
1012 	FileWrite(hFile, &giHabitatedDistance,  4);
1013 	FileWrite(hFile, &giPopulationModifier, 4);
1014 	FileWrite(hFile, &giLairID,             4);
1015 	FileWrite(hFile, &gfUseCreatureMusic,   1);
1016 	FileWrite(hFile, &giDestroyedLairID,    4);
1017 }
1018 
1019 
LoadCreatureDirectives(HWFILE const hFile,UINT32 const uiSavedGameVersion)1020 void LoadCreatureDirectives(HWFILE const hFile, UINT32 const uiSavedGameVersion)
1021 {
1022 	FileRead(hFile, &giHabitatedDistance,  4);
1023 	FileRead(hFile, &giPopulationModifier, 4);
1024 	FileRead(hFile, &giLairID,             4);
1025 	FileRead(hFile, &gfUseCreatureMusic,   1);
1026 
1027 	if( uiSavedGameVersion >= 82 )
1028 	{
1029 		FileRead(hFile, &giDestroyedLairID, 4);
1030 	}
1031 	else
1032 	{
1033 		giDestroyedLairID = 0;
1034 	}
1035 
1036 	switch( giLairID )
1037 	{
1038 	case -1: //creature quest finished -- it's okay
1039 	case 0:  //lair doesn't exist yet -- it's okay
1040 		break;
1041 	default:
1042 		auto lair = GCM->getCreatureLair(giLairID);
1043 		if (!lair)
1044 		{
1045 			STLOGE("Invalid restoration of creature lair ID of {}.  Save game potentially hosed.", giLairID);
1046 			break;
1047 		}
1048 		InitCreatureLair(lair);
1049 		break;
1050 	}
1051 }
1052 
1053 
PlayerGroupIsInACreatureInfestedMine()1054 BOOLEAN PlayerGroupIsInACreatureInfestedMine()
1055 {
1056 	CREATURE_DIRECTIVE *curr;
1057 	INT16 sSectorX, sSectorY;
1058 	INT8 bSectorZ;
1059 
1060 	if( giLairID <= 0 )
1061 	{ //Creature quest inactive
1062 		return FALSE;
1063 	}
1064 
1065 	//Lair is active, so look for live soldier in any creature level
1066 	curr = gLair;
1067 	while( curr )
1068 	{
1069 		sSectorX = curr->pLevel->ubSectorX;
1070 		sSectorY = curr->pLevel->ubSectorY;
1071 		bSectorZ = (INT8)curr->pLevel->ubSectorZ;
1072 		//Loop through all the creature directives (mine sectors that are infectible) and
1073 		//see if players are there.
1074 		CFOR_EACH_IN_TEAM(pSoldier, OUR_TEAM)
1075 		{
1076 			if (pSoldier->bLife    != 0 &&
1077 					pSoldier->sSectorX == sSectorX &&
1078 					pSoldier->sSectorY == sSectorY &&
1079 					pSoldier->bSectorZ == bSectorZ &&
1080 					!pSoldier->fBetweenSectors )
1081 			{
1082 				return TRUE;
1083 			}
1084 		}
1085 		curr = curr->next;
1086 	}
1087 
1088 	//Lair is active, but no mercs are in these sectors
1089 	return FALSE;
1090 }
1091 
1092 
GetWarpOutOfMineCodes(INT16 * const sector_x,INT16 * const sector_y,INT8 * const sector_z,INT16 * const insertion_grid_no)1093 bool GetWarpOutOfMineCodes(INT16* const sector_x, INT16* const sector_y, INT8* const sector_z, INT16* const insertion_grid_no)
1094 {
1095 	if (!gfWorldLoaded)      return false;
1096 	if (gbWorldSectorZ == 0) return false;
1097 
1098 	auto lair = gLairModel;
1099 	if (lair == NULL && giLairID == -1)
1100 	{
1101 		// Quest is finished
1102 		lair = GCM->getCreatureLair(giDestroyedLairID);
1103 	}
1104 
1105 	// Now make sure the mercs are in the previously infested mine
1106 	if (lair != NULL && lair->isSectorInLair(gWorldSectorX, gWorldSectorY, gbWorldSectorZ))
1107 	{
1108 		*sector_x = SECTORX(lair->warpExitSector);
1109 		*sector_y = SECTORY(lair->warpExitSector);
1110 		*sector_z = 0;
1111 		*insertion_grid_no = lair->warpExitGridNo;
1112 
1113 		return true;
1114 	}
1115 	return false;
1116 }
1117