1 #include "StrategicMap.h"
2 
3 #include "AI.h"
4 #include "Ambient_Control.h"
5 #include "Animated_ProgressBar.h"
6 #include "Animation_Control.h"
7 #include "Assignments.h"
8 #include "Auto_Resolve.h"
9 #include "Boxing.h"
10 #include "Bullets.h"
11 #include "Campaign_Types.h"
12 #include "ContentManager.h"
13 #include "ContentMusic.h"
14 #include "Creature_Spreading.h"
15 #include "Cursor_Control.h"
16 #include "Cursors.h"
17 #include "Debug.h"
18 #include "Dialogue_Control.h"
19 #include "Directories.h"
20 #include "Enemy_Soldier_Save.h"
21 #include "Environment.h"
22 #include "Event_Pump.h"
23 #include "Exit_Grids.h"
24 #include "Explosion_Control.h"
25 #include "Faces.h"
26 #include "Fade_Screen.h"
27 #include "FileMan.h"
28 #include "Font.h"
29 #include "Font_Control.h"
30 #include "GameInstance.h"
31 #include "GameLoop.h"
32 #include "GameScreen.h"
33 #include "GameSettings.h"
34 #include "Game_Clock.h"
35 #include "Game_Events.h"
36 #include "HImage.h"
37 #include "Handle_UI.h"
38 #include "History.h"
39 #include "Interface.h"
40 #include "Interface_Dialogue.h"
41 #include "Interface_Panels.h"
42 #include "Isometric_Utils.h"
43 #include "Items.h"
44 #include "JAScreens.h"
45 #include "Keys.h"
46 #include "LoadSaveSectorInfo.h"
47 #include "LoadSaveStrategicMapElement.h"
48 #include "Loading_Screen.h"
49 #include "Local.h"
50 #include "Logger.h"
51 #include "MapScreen.h"
52 #include "Map_Edgepoints.h"
53 #include "Map_Information.h"
54 #include "Map_Screen_Helicopter.h"
55 #include "Meanwhile.h"
56 #include "Merc_Contract.h"
57 #include "Merc_Entering.h"
58 #include "Merc_Hiring.h"
59 #include "Message.h"
60 #include "MessageBoxScreen.h"
61 #include "Militia_Control.h"
62 #include "MineModel.h"
63 #include "Music_Control.h"
64 #include "NPC.h"
65 #include "OppList.h"
66 #include "Overhead.h"
67 #include "PathAI.h"
68 #include "Physics.h"
69 #include "Player_Command.h"
70 #include "Points.h"
71 #include "PreBattle_Interface.h"
72 #include "Queen_Command.h"
73 #include "Quests.h"
74 #include "Radar_Screen.h"
75 #include "Random.h"
76 #include "RenderWorld.h"
77 #include "Render_Dirty.h"
78 #include "SamSiteModel.h"
79 #include "SaveLoadMap.h"
80 #include "Scheduling.h"
81 #include "ScreenIDs.h"
82 #include "Soldier_Add.h"
83 #include "Soldier_Control.h"
84 #include "Soldier_Create.h"
85 #include "Soldier_Init_List.h"
86 #include "Soldier_Macros.h"
87 #include "Sound_Control.h"
88 #include "Squads.h"
89 #include "Strategic.h"
90 #include "StrategicMap_Secrets.h"
91 #include "StrategicMapSecretModel.h"
92 #include "Strategic_Event_Handler.h"
93 #include "Strategic_Mines.h"
94 #include "Strategic_Movement.h"
95 #include "Strategic_Pathing.h"
96 #include "Strategic_Town_Loyalty.h"
97 #include "Strategic_Turns.h"
98 #include "SysUtil.h"
99 #include "Sys_Globals.h"
100 #include "Tactical_Placement_GUI.h"
101 #include "Tactical_Save.h"
102 #include "Tactical_Turns.h"
103 #include "Text.h"
104 #include "Timer.h"
105 #include "Timer_Control.h"
106 #include "TownModel.h"
107 #include "Town_Militia.h"
108 #include "Types.h"
109 #include "UILayout.h"
110 #include "VObject.h"
111 #include "VSurface.h"
112 #include "Video.h"
113 #include "WorldDat.h"
114 #include "WorldDef.h"
115 #include "WorldMan.h"
116 
117 #include <string_theory/format>
118 #include <string_theory/string>
119 
120 #include <algorithm>
121 #include <iterator>
122 #include <map>
123 #include <stdexcept>
124 
125 //Used by PickGridNoToWalkIn
126 #define MAX_ATTEMPTS 200
127 
128 #define QUEST_CHECK_EVENT_TIME ( 8 * 60 )
129 #define BOBBYRAY_UPDATE_TIME   ( 9 * 60 )
130 #define INSURANCE_UPDATE_TIME  0
131 #define EARLY_MORNING_TIME     ( 4 * 60 )
132 #define ENRICO_MAIL_TIME       ( 7 * 60 )
133 
134 
135 extern INT16 gsRobotGridNo;
136 
137 BOOLEAN gfGettingNameFromSaveLoadScreen;
138 
139 INT16 gWorldSectorX = 0;
140 INT16 gWorldSectorY = 0;
141 INT8  gbWorldSectorZ = -1;
142 
143 static INT16  gsAdjacentSectorX;
144 static INT16  gsAdjacentSectorY;
145 static INT8   gbAdjacentSectorZ;
146 static GROUP* gpAdjacentGroup = 0;
147 static UINT8  gubAdjacentJumpCode;
148 static UINT32 guiAdjacentTraverseTime;
149 UINT8			gubTacticalDirection;
150 static INT16  gsAdditionalData;
151 
152 static BOOLEAN fUsingEdgePointsForStrategicEntry = FALSE;
153 BOOLEAN		gfInvalidTraversal = FALSE;
154 BOOLEAN		gfLoneEPCAttemptingTraversal = FALSE;
155 BOOLEAN		gfRobotWithoutControllerAttemptingTraversal = FALSE;
156 BOOLEAN   gubLoneMercAttemptingToAbandonEPCs = 0;
157 const SOLDIERTYPE* gPotentiallyAbandonedEPC = NULL;
158 
159 INT8 gbGreenToElitePromotions = 0;
160 INT8 gbGreenToRegPromotions = 0;
161 INT8 gbRegToElitePromotions = 0;
162 INT8 gbMilitiaPromotions = 0;
163 
164 
165 BOOLEAN		gfUseAlternateMap = FALSE;
166 
167 static INT16 const DirXIncrementer[8] =
168 {
169 	0,        //N
170 	1,        //NE
171 	1,        //E
172 	1,        //SE
173 	0,         //S
174 	-1,       //SW
175 	-1,       //W
176 	-1       //NW
177 };
178 
179 static INT16 const DirYIncrementer[8] =
180 {
181 	-1,        //N
182 	-1,        //NE
183 	0,        //E
184 	1,        //SE
185 	1,         //S
186 	1,       //SW
187 	0,       //W
188 	-1       //NW
189 };
190 
191 Observable<> BeforePrepareSector = {};
192 
193 extern BOOLEAN gfOverrideSector;
194 
195 
196 StrategicMapElement StrategicMap[MAP_WORLD_X * MAP_WORLD_Y];
197 
198 
199 //temp timer stuff -- to measure the time it takes to load a map.
200 
UndergroundTacticalTraversalTime(INT8 const exit_direction)201 static UINT32 UndergroundTacticalTraversalTime(INT8 const exit_direction)
202 { /* We are attempting to traverse in an underground environment. We need to use
203 	 * a complete different method.  When underground, all sectors are instantly
204 	 * adjacent. */
205 	GridNo gridno;
206 	switch (exit_direction)
207 	{
208 		case NORTH_STRATEGIC_MOVE: gridno = gMapInformation.sNorthGridNo; break;
209 		case EAST_STRATEGIC_MOVE:  gridno = gMapInformation.sEastGridNo;  break;
210 		case SOUTH_STRATEGIC_MOVE: gridno = gMapInformation.sSouthGridNo; break;
211 		case WEST_STRATEGIC_MOVE:  gridno = gMapInformation.sWestGridNo;  break;
212 		default: throw std::logic_error("invalid exit direction");
213 	}
214 	return gridno != -1 ? 0 : TRAVERSE_TIME_IMPOSSIBLE;
215 }
216 
217 
BeginLoadScreen()218 void BeginLoadScreen( )
219 {
220 	UINT32 uiStartTime, uiCurrTime;
221 	INT32 iPercentage, iFactor;
222 	UINT32 uiTimeRange;
223 
224 	SetCurrentCursorFromDatabase( VIDEO_NO_CURSOR );
225 
226 	if( guiCurrentScreen == MAP_SCREEN && !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME) && !AreInMeanwhile() )
227 	{
228 		SGPBox const DstRect = { 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT };
229 		uiTimeRange = 2000;
230 		iPercentage = 0;
231 		uiStartTime = GetClock();
232 		BltVideoSurface(guiSAVEBUFFER, FRAME_BUFFER, 0, 0, NULL);
233 		PlayJA2SampleFromFile(SOUNDSDIR "/final psionic blast 01 (16-44).wav", HIGHVOLUME, 1, MIDDLEPAN);
234 		while( iPercentage < 100  )
235 		{
236 			uiCurrTime = GetClock();
237 			iPercentage = (uiCurrTime-uiStartTime) * 100 / uiTimeRange;
238 			iPercentage = MIN( iPercentage, 100 );
239 
240 			//Factor the percentage so that it is modified by a gravity falling acceleration effect.
241 			iFactor = (iPercentage - 50) * 2;
242 			if( iPercentage < 50 )
243 				iPercentage = (UINT32)(iPercentage + iPercentage * iFactor * 0.01 + 0.5);
244 			else
245 				iPercentage = (UINT32)(iPercentage + (100-iPercentage) * iFactor * 0.01 + 0.05);
246 
247 			if( iPercentage > 50 )
248 			{
249 				guiSAVEBUFFER->ShadowRectUsingLowPercentTable(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
250 			}
251 
252 			SGPBox const SrcRect =
253 			{
254 				(UINT16) (536 * iPercentage / 100),
255 				(UINT16) (367 * iPercentage / 100),
256 				(UINT16) (SCREEN_WIDTH  - 541 * iPercentage / 100),
257 				(UINT16) (SCREEN_HEIGHT - 406 * iPercentage / 100)
258 			};
259 			BltStretchVideoSurface(FRAME_BUFFER, guiSAVEBUFFER, &SrcRect, &DstRect);
260 			InvalidateScreen();
261 			RefreshScreen();
262 		}
263 	}
264 	FRAME_BUFFER->Fill(Get16BPPColor(FROMRGB(0, 0, 0)));
265 	InvalidateScreen( );
266 	RefreshScreen();
267 
268 	//If we are loading a saved game, use the Loading screen we saved into the SavedGameHeader file
269 	// ( which gets reloaded into gubLastLoadingScreenID )
270 	if( !gfGotoSectorTransition )
271 	{
272 		UINT8 const id = gTacticalStatus.uiFlags & LOADING_SAVED_GAME ?
273 			gubLastLoadingScreenID :
274 			GetLoadScreenID(gWorldSectorX, gWorldSectorY, gbWorldSectorZ);
275 		DisplayLoadScreenWithID(id);
276 	}
277 }
278 
279 static void InitializeMapStructure(void);
280 
281 static void HandleAirspaceControlUpdated();
282 
InitStrategicEngine()283 void InitStrategicEngine()
284 {
285 	// this runs every time we start the application, so don't put anything in here that's only supposed to run when a new
286 	// *game* is started!  Those belong in InitStrategicLayer() instead.
287 
288 	InitializeMapStructure();
289 
290 	// set up town stuff
291 	BuildListOfTownSectors( );
292 
293 	OnAirspaceControlUpdated.addListener("default", HandleAirspaceControlUpdated);
294 }
295 
296 
GetTownIdForSector(UINT8 const sector)297 UINT8 GetTownIdForSector(UINT8 const sector)
298 {
299 	// return the name value of the town in this sector
300 	return StrategicMap[SECTOR_INFO_TO_STRATEGIC_INDEX(sector)].bNameId;
301 }
302 
303 
GetTownSectorSize(INT8 const town_id)304 UINT8 GetTownSectorSize(INT8 const town_id)
305 {
306 	UINT8 n = 0;
307 	FOR_EACH_SECTOR_IN_TOWN(i, town_id) ++n;
308 	return n;
309 }
310 
311 
GetTownSectorsUnderControl(INT8 const town_id)312 UINT8 GetTownSectorsUnderControl(INT8 const town_id)
313 {
314 	UINT8 n = 0;
315 	FOR_EACH_SECTOR_IN_TOWN(i, town_id)
316 	{
317 		UINT32 const x = SECTORX(i->sector);
318 		UINT32 const y = SECTORY(i->sector);
319 		if (StrategicMap[CALCULATE_STRATEGIC_INDEX(x, y)].fEnemyControlled) continue;
320 		if (NumEnemiesInSector(x, y) != 0)                                  continue;
321 		++n;
322 	}
323 	return n;
324 }
325 
326 
327 static void InitializeStrategicMapSectorTownNames(void);
328 
329 
InitializeMapStructure(void)330 static void InitializeMapStructure(void)
331 {
332 	std::fill(std::begin(StrategicMap), std::end(StrategicMap), StrategicMapElement{});
333 
334 	InitializeStrategicMapSectorTownNames( );
335 }
336 
337 // get short sector name without town name
GetShortSectorString(INT16 sMapX,INT16 sMapY)338 ST::string GetShortSectorString(INT16 sMapX, INT16 sMapY)
339 {
340 	// OK, build string id like J11
341 	return ST::format("{c}{}", 'A' - 1 + sMapY, sMapX);
342 }
343 
344 
GetMapFileName(INT16 const x,INT16 const y,INT8 const z,char * const buf,BOOLEAN const add_alternate_map_letter)345 void GetMapFileName(INT16 const x, INT16 const y, INT8 const z, char* const buf, BOOLEAN const add_alternate_map_letter)
346 {
347 	size_t n = sprintf(buf, "%c%d", 'A' - 1 + y, x);
348 
349 	if (z != 0) n += sprintf(buf + n, "_b%d", z);
350 
351 	/* The gfUseAlternateMap flag is set while loading saved games. When starting
352 	 * a new game the underground sector info has not been initialized, so we need
353 	 * the flag to load an alternate sector. */
354 	if (GetSectorFlagStatus(x, y, z, SF_USE_ALTERNATE_MAP) || gfUseAlternateMap)
355 	{
356 		gfUseAlternateMap = FALSE;
357 		if (add_alternate_map_letter) n += sprintf(buf + n, "_a");
358 	}
359 
360 	if (AreInMeanwhile() && x == 3 && y == 16 && z == 0)
361 	{
362 		if (add_alternate_map_letter) n += sprintf(buf + n, "_m");
363 	}
364 
365 	sprintf(buf + n, ".dat");
366 }
367 
368 
HandleRPCDescriptionOfSector(INT16 const x,INT16 const y,INT16 const z)369 static void HandleRPCDescriptionOfSector(INT16 const x, INT16 const y, INT16 const z)
370 {
371 	struct SectorDescriptionInfo
372 	{
373 		UINT8 y;
374 		UINT8 x;
375 		UINT8 quote;
376 	};
377 
378 	SectorDescriptionInfo const sector_description[] =
379 	{
380 		{  2, 13,  0 }, // B13 Drassen
381 		{  3, 13,  1 }, // C13 Drassen
382 		{  4, 13,  2 }, // D13 Drassen
383 		{  8, 13,  3 }, // H13 Alma
384 		{  8, 14,  4 }, // H14 Alma
385 		{  9, 13,  5 }, // I13 Alma (extra quote 6 if Sci-fi)
386 		{  9, 14,  7 }, // I14 Alma
387 		{  6,  8,  8 }, // F8  Cambria
388 		{  6,  9,  9 }, // F9  Cambria
389 		{  7,  8, 10 }, // G8  Cambria
390 
391 		{  7,  9, 11 }, // G9  Cambria
392 		{  3,  6, 12 }, // C6  San Mona
393 		{  3,  5, 13 }, // C5  San Mona
394 		{  4,  5, 14 }, // D5  San Mona
395 		{  2,  2, 15 }, // B2  Chitzena
396 		{  1,  2, 16 }, // A2  Chitzena
397 		{  7,  1, 17 }, // G1  Grumm
398 		{  8,  1, 18 }, // H1  Grumm
399 		{  7,  2, 19 }, // G2  Grumm
400 		{  8,  2, 20 }, // H2  Grumm
401 
402 		{  9,  6, 21 }, // I6  Estoni
403 		{ 11,  4, 22 }, // K4  Orta
404 		{ 12, 11, 23 }, // L11 Balime
405 		{ 12, 12, 24 }, // L12 Balime
406 		{ 15,  3, 25 }, // O3  Meduna
407 		{ 16,  3, 26 }, // P3  Meduna
408 		{ 14,  4, 27 }, // N4  Meduna
409 		{ 14,  3, 28 }, // N3  Meduna
410 		{ 15,  4, 30 }, // O4  Meduna
411 		{ 10,  9, 31 }, // J9  Tixa
412 
413 		{  4, 15, 32 }, // D15 NE SAM
414 		{  4,  2, 33 }, // D2  NW SAM
415 		{  9,  8, 34 }  // I8  CENTRAL SAM
416 	};
417 
418 	TacticalStatusType& ts = gTacticalStatus;
419 	// Default to false
420 	ts.fCountingDownForGuideDescription = FALSE;
421 
422 	if (GetSectorFlagStatus(x, y, z, SF_HAVE_USED_GUIDE_QUOTE)) return;
423 	if (z != 0) return;
424 
425 	// Check if we are in a good sector
426 	FOR_EACH(SectorDescriptionInfo const, i, sector_description)
427 	{
428 		if (x != i->x || y != i->y) continue;
429 
430 		// If we're not scifi, skip some
431 		if (i == &sector_description[3] && !gGameOptions.fSciFi) continue;
432 
433 		SetSectorFlag(x, y, z, SF_HAVE_USED_GUIDE_QUOTE);
434 
435 		ts.fCountingDownForGuideDescription = TRUE;
436 		ts.bGuideDescriptionCountDown       = 4 + Random(5); // 4 to 8 tactical turns
437 		ts.ubGuideDescriptionToUse          = i->quote;
438 		ts.bGuideDescriptionSectorX         = x;
439 		ts.bGuideDescriptionSectorY         = y;
440 
441 		// Handle guide description (will be needed if a SAM one)
442 		HandleRPCDescription();
443 		break;
444 	}
445 }
446 
447 
448 static void EnterSector(INT16 x, INT16 y, INT8 z);
449 enum
450 {
451 	ABOUT_TO_LOAD_NEW_MAP,
452 	ABOUT_TO_TRASH_WORLD,
453 };
454 static void HandleDefiniteUnloadingOfWorld(UINT8 ubUnloadCode);
455 
456 
SetCurrentWorldSector(INT16 const x,INT16 const y,INT8 const z)457 void SetCurrentWorldSector(INT16 const x, INT16 const y, INT8 const z)
458 {
459 	SyncStrategicTurnTimes();
460 
461 	// is the sector already loaded?
462 	if (gWorldSectorX == x && y == gWorldSectorY && z == gbWorldSectorZ)
463 	{
464 		/* Insert the enemies into the newly loaded map based on the strategic
465 		 * information. Note, the flag will return TRUE only if enemies were added.
466 		 * The game may wish to do something else in a case where no enemies are
467 		 * present. */
468 
469 		SetPendingNewScreen(GAME_SCREEN);
470 		if (NumEnemyInSector() == 0)
471 		{
472 			PrepareEnemyForSectorBattle();
473 		}
474 		if (gubNumCreaturesAttackingTown != 0 &&
475 				z                            == 0 &&
476 				gubSectorIDOfCreatureAttack  == SECTOR(x, y))
477 		{
478 			PrepareCreaturesForBattle();
479 		}
480 
481 		if (gfGotoSectorTransition)
482 		{
483 			BeginLoadScreen();
484 			gfGotoSectorTransition = FALSE;
485 		}
486 
487 		HandleHelicopterOnGround(true);
488 
489 		ResetMilitia();
490 		AllTeamsLookForAll(TRUE);
491 		return;
492 	}
493 
494 	if (gWorldSectorX != 0 && gWorldSectorY != 0 && gbWorldSectorZ != -1)
495 	{
496 		HandleDefiniteUnloadingOfWorld(ABOUT_TO_LOAD_NEW_MAP);
497 	}
498 
499 	// make this the currently loaded sector
500 	gWorldSectorX  = x;
501 	gWorldSectorY  = y;
502 	gbWorldSectorZ = z;
503 
504 	// update currently selected map sector to match
505 	ChangeSelectedMapSector(x, y, z);
506 
507 	bool const loading_savegame = gTacticalStatus.uiFlags & LOADING_SAVED_GAME;
508 	if (loading_savegame)
509 	{
510 		SetMusicMode(MUSIC_MAIN_MENU);
511 	}
512 	else
513 	{
514 		StopAnyCurrentlyTalkingSpeech();
515 
516 		/* Check to see if the sector we are loading is the cave sector under Tixa.
517 		 * If so then we will set up the meanwhile scene to start the creature
518 		 * quest. */
519 		if (x == 9 && y == 10 && z == 2)
520 		{
521 			InitCreatureQuest(); // Ignored if already active.
522 		}
523 
524 		gTacticalStatus.uiTimeSinceLastInTactical = GetWorldTotalMin();
525 		InitializeTacticalStatusAtBattleStart();
526 		HandleHelicopterOnGround(false);
527 	}
528 
529 	EnterSector(x, y, z);
530 
531 	if (!loading_savegame)
532 	{
533 		InitAI();
534 		ExamineDoorsOnEnteringSector();
535 	}
536 
537 	/* Update all the doors in the sector according to the temp file previously
538 	 * loaded, and any changes made by the schedules */
539 	UpdateDoorGraphicsFromStatus();
540 
541 	// Set the fact we have visited the  sector
542 	SetSectorFlag(x, y, z, SF_ALREADY_LOADED);
543 
544 	// Check for helicopter being on the ground in this sector
545 	HandleHelicopterOnGround(true);
546 
547 	if (!loading_savegame)
548 	{
549 		if (gubMusicMode == MUSIC_TACTICAL_ENEMYPRESENT ?
550 					NumHostilesInSector(x, y, z) == 0 :
551 					gubMusicMode != MUSIC_TACTICAL_BATTLE)
552 		{
553 			// ATE: Fade FAST
554 			SetMusicFadeSpeed(5);
555 			SetMusicMode(MUSIC_TACTICAL_NOTHING);
556 		}
557 
558 		// ATE: Check what sector we are in, to show description if we have an RPC
559 		HandleRPCDescriptionOfSector(x, y, z);
560 
561 		// ATE: Set Flag for being visited
562 		SetSectorFlag(x, y, z, SF_HAS_ENTERED_TACTICAL);
563 
564 		ResetMultiSelection();
565 
566 		gTacticalStatus.fHasEnteredCombatModeSinceEntering = FALSE;
567 		gTacticalStatus.fDontAddNewCrows                   = FALSE;
568 
569 		// Adjust delay for tense quote
570 		gTacticalStatus.sCreatureTenseQuoteDelay = 10 + Random(20);
571 
572 		INT16 sWarpWorldX;
573 		INT16 sWarpWorldY;
574 		INT8  bWarpWorldZ;
575 		INT16 sWarpGridNo;
576 		if (z >= 2 && GetWarpOutOfMineCodes(&sWarpWorldX, &sWarpWorldY, &bWarpWorldZ, &sWarpGridNo))
577 		{
578 			gTacticalStatus.uiFlags |= IN_CREATURE_LAIR;
579 		}
580 		else
581 		{
582 			gTacticalStatus.uiFlags &= ~IN_CREATURE_LAIR;
583 		}
584 
585 		gTacticalStatus.ubNumCrowsPossible = 5 + Random(5);
586 	}
587 }
588 
589 
RemoveMercsInSector()590 void RemoveMercsInSector( )
591 {
592 	// ATE: only for OUR guys.. the rest is taken care of in TrashWorld() when a new sector is added...
593 	FOR_EACH_IN_TEAM(i, OUR_TEAM)
594 	{
595 		RemoveSoldierFromGridNo(*i);
596 	}
597 }
598 
599 
PrepareLoadedSector()600 void PrepareLoadedSector()
601 {
602 	BOOLEAN fAddCivs = TRUE;
603 	INT8 bMineIndex = -1;
604 
605 	BeforePrepareSector();
606 
607 	if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
608 	{
609 		UpdateMercsInSector();
610 	}
611 
612 	// Reset ambients!
613 	HandleNewSectorAmbience( gTilesets[ giCurrentTilesetID ].ubAmbientID );
614 
615 	//if we are loading a 'pristine' map ( ie, not loading a saved game )
616 	if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ))
617 	{
618 		if ( !AreReloadingFromMeanwhile( ) )
619 		{
620 			SetPendingNewScreen(GAME_SCREEN);
621 
622 			// Make interface the team panel always...
623 			SetCurrentInterfacePanel(TEAM_PANEL);
624 		}
625 
626 
627 		//Check to see if civilians should be added.  Always add civs to maps unless they are
628 		//in a mine that is shutdown.
629 		if( gbWorldSectorZ )
630 		{
631 			bMineIndex = GetIdOfMineForSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ );
632 			if( bMineIndex != -1 )
633 			{
634 				if( !AreThereMinersInsideThisMine( (UINT8)bMineIndex ) )
635 				{
636 					fAddCivs = FALSE;
637 				}
638 			}
639 		}
640 		if( fAddCivs )
641 		{
642 			AddSoldierInitListTeamToWorld(CIV_TEAM);
643 		}
644 
645 		AddSoldierInitListTeamToWorld(MILITIA_TEAM);
646 		AddSoldierInitListBloodcats();
647 		//Creatures are only added if there are actually some of them.  It has to go through some
648 		//additional checking.
649 
650 		PrepareCreaturesForBattle();
651 
652 		PrepareMilitiaForTactical();
653 
654 		// OK, set varibles for entring this new sector...
655 		gTacticalStatus.fVirginSector = TRUE;
656 
657 		AddProfilesNotUsingProfileInsertionData();
658 
659 		if( !AreInMeanwhile() || GetMeanwhileID() == INTERROGATION )
660 		{ // Insert the enemies into the newly loaded map based on the strategic information.
661 			PrepareEnemyForSectorBattle();
662 		}
663 
664 
665 		//Regardless whether or not this was set, clear it now.
666 		gfRestoringEnemySoldiersFromTempFile = FALSE;
667 
668 		//@@@Evaluate
669 		//Add profiles to world using strategic info, not editor placements.
670 		AddProfilesUsingProfileInsertionData();
671 
672 		PostSchedules();
673 	}
674 
675 	CallAvailableTeamEnemiesToAmbush(gMapInformation.sCenterGridNo);
676 
677 	if( !( gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
678 	{
679 		// unpause game
680 		UnPauseGame( );
681 	}
682 
683 	gpBattleGroup = NULL;
684 
685 	if( gfTacticalTraversal )
686 	{
687 		CalculateNonPersistantPBIInfo();
688 	}
689 
690 	SLOGD("Current Time is: %d", GetWorldTotalMin() );
691 
692 	AllTeamsLookForAll( TRUE );
693 }
694 
695 #define RANDOM_HEAD_MINERS 4
HandleQuestCodeOnSectorEntry(INT16 sNewSectorX,INT16 sNewSectorY,INT8 bNewSectorZ)696 void HandleQuestCodeOnSectorEntry( INT16 sNewSectorX, INT16 sNewSectorY, INT8 bNewSectorZ )
697 {
698 	UINT8		ubRandomMiner[RANDOM_HEAD_MINERS] = { 106, 156, 157, 158 };
699 	UINT8		ubMiner, ubMinersPlaced;
700 
701 	if ( CheckFact( FACT_ALL_TERRORISTS_KILLED, 0 ) )
702 	{
703 		// end terrorist quest
704 		EndQuest( QUEST_KILL_TERRORISTS, gMercProfiles[ CARMEN ].sSectorX, gMercProfiles[ CARMEN ].sSectorY );
705 		// remove Carmen
706 		gMercProfiles[ CARMEN ].sSectorX = 0;
707 		gMercProfiles[ CARMEN ].sSectorY = 0;
708 		gMercProfiles[ CARMEN ].bSectorZ = 0;
709 	}
710 
711 	UINT8 const sector = SECTOR(sNewSectorX, sNewSectorY);
712 	// are we in a mine sector, on the surface?
713 	if (bNewSectorZ == 0)
714 	{
715 		INT8 const ubThisMine = GetMineIndexForSector(sector);
716 		if (ubThisMine != -1 && !CheckFact(FACT_MINERS_PLACED, 0))
717 		{
718 			auto thisMine = GCM->getMine(ubThisMine);
719 			// SET HEAD MINER LOCATIONS, unless mine is abandoned
720 			if (!thisMine->isAbandoned())
721 			{
722 				ubMinersPlaced = 0;
723 
724 				if (!thisMine->headMinerAssigned)
725 				{
726 					// Fred Morris is always in the first mine sector we enter, unless head miner here has been pre-determined (then he's randomized, too)
727 					MERCPROFILESTRUCT& fred = GetProfile(FRED);
728 					fred.sSectorX = sNewSectorX;
729 					fred.sSectorY = sNewSectorY;
730 					fred.bSectorZ = 0;
731 					fred.bTown    = thisMine->associatedTownId;
732 
733 					// mark miner as placed
734 					ubRandomMiner[ 0 ] = 0;
735 					ubMinersPlaced++;
736 				}
737 
738 				// assign the remaining (3) miners randomly
739 				for (const MineModel* ubMine : GCM->getMines())
740 				{
741 					if ( ubMine->mineId == ubThisMine || ubMine->headMinerAssigned || ubMine->isAbandoned() )
742 					{
743 						// Alma always has Matt as a miner, and we have assigned Fred to the current mine
744 						// and San Mona is abandoned
745 						continue;
746 					}
747 
748 					do
749 					{
750 						ubMiner = (UINT8) Random( RANDOM_HEAD_MINERS );
751 					}
752 					while( ubRandomMiner[ ubMiner ] == 0 );
753 
754 					MERCPROFILESTRUCT& p      = GetProfile(ubRandomMiner[ubMiner]);
755 					UINT8 const        sector = ubMine->entranceSector;
756 					p.sSectorX = SECTORX(sector);
757 					p.sSectorY = SECTORY(sector);
758 					p.bSectorZ = 0;
759 					p.bTown = ubMine->associatedTownId;
760 
761 					// mark miner as placed
762 					ubRandomMiner[ ubMiner ] = 0;
763 					ubMinersPlaced++;
764 
765 					if (ubMinersPlaced == RANDOM_HEAD_MINERS)
766 					{
767 						break;
768 					}
769 				}
770 
771 				SetFactTrue( FACT_MINERS_PLACED );
772 			}
773 		}
774 	}
775 
776 	if (!CheckFact(FACT_ROBOT_RECRUITED_AND_MOVED, 0))
777 	{
778 		const SOLDIERTYPE* const pRobot = FindSoldierByProfileIDOnPlayerTeam(ROBOT);
779 		if (pRobot)
780 		{
781 			// robot is on our team and we have changed sectors, so we can
782 			// replace the robot-under-construction in Madlab's sector
783 			RemoveGraphicFromTempFile( gsRobotGridNo, SEVENTHISTRUCT1, gMercProfiles[MADLAB].sSectorX, gMercProfiles[MADLAB].sSectorY, gMercProfiles[MADLAB].bSectorZ );
784 			SetFactTrue( FACT_ROBOT_RECRUITED_AND_MOVED );
785 		}
786 	}
787 
788 	// Check to see if any player merc has the Chalice; if so,
789 	// note it as stolen
790 	CFOR_EACH_IN_TEAM(s, OUR_TEAM)
791 	{
792 		if (FindObj(s, CHALICE) != ITEM_NOT_FOUND)
793 		{
794 			SetFactTrue(FACT_CHALICE_STOLEN);
795 		}
796 	}
797 
798 	if (gubQuest[QUEST_KINGPIN_MONEY] == QUESTINPROGRESS &&
799 			CheckFact(FACT_KINGPIN_CAN_SEND_ASSASSINS, 0)    &&
800 			GetTownIdForSector(sector) != BLANK_SECTOR       &&
801 			Random(10 + GetNumberOfMilitiaInSector(sNewSectorX, sNewSectorY, bNewSectorZ)) < 3)
802 	{
803 		DecideOnAssassin();
804 	}
805 
806 /*
807 	if (sector == SEC_C5)
808 	{
809 		// reset Madame Layla counters
810 		gMercProfiles[ MADAME ].bNPCData = 0;
811 		gMercProfiles[ MADAME ].bNPCData2 = 0;
812 	}
813 	*/
814 
815 	if (sector == SEC_C6 && gubQuest[QUEST_RESCUE_MARIA] == QUESTDONE)
816 	{
817 		// make sure Maria and Angel are gone
818 		gMercProfiles[ MARIA ].sSectorX = 0;
819 		gMercProfiles[ MARIA ].sSectorY = 0;
820 		gMercProfiles[ ANGEL ].sSectorX = 0;
821 		gMercProfiles[ ANGEL ].sSectorY = 0;
822 	}
823 
824 	if (sector == SEC_D5)
825 	{
826 		gBoxer[0] = NULL;
827 		gBoxer[1] = NULL;
828 		gBoxer[2] = NULL;
829 	}
830 
831 	if (sector == SEC_P3)
832 	{
833 		// heal up Elliot if he's been hurt
834 		if ( gMercProfiles[ ELLIOT ].bMercStatus != MERC_IS_DEAD )
835 		{
836 			gMercProfiles[ ELLIOT ].bLife = gMercProfiles[ ELLIOT ].bLifeMax;
837 		}
838 	}
839 
840 	ResetOncePerConvoRecordsForAllNPCsInLoadedSector();
841 }
842 
843 
HandleQuestCodeOnSectorExit(INT16 sOldSectorX,INT16 sOldSectorY,INT8 bOldSectorZ)844 static void HandleQuestCodeOnSectorExit(INT16 sOldSectorX, INT16 sOldSectorY, INT8 bOldSectorZ)
845 {
846 	if ( sOldSectorX == KINGPIN_MONEY_SECTOR_X && sOldSectorY == KINGPIN_MONEY_SECTOR_Y && bOldSectorZ == KINGPIN_MONEY_SECTOR_Z )
847 	{
848 		CheckForKingpinsMoneyMissing( TRUE );
849 	}
850 
851 	if ( sOldSectorX == 13 && sOldSectorY == MAP_ROW_H && bOldSectorZ == 0 && CheckFact( FACT_CONRAD_SHOULD_GO, 0 ) )
852 	{
853 		// remove Conrad from the map
854 		gMercProfiles[ CONRAD ].sSectorX = 0;
855 		gMercProfiles[ CONRAD ].sSectorY = 0;
856 	}
857 
858 	if ( sOldSectorX == HOSPITAL_SECTOR_X && sOldSectorY == HOSPITAL_SECTOR_Y && bOldSectorZ == HOSPITAL_SECTOR_Z )
859 	{
860 		CheckForMissingHospitalSupplies();
861 	}
862 
863 	// reset the state of the museum alarm for Eldin's quotes
864 	SetFactFalse( FACT_MUSEUM_ALARM_WENT_OFF );
865 }
866 
867 
SetupProfileInsertionDataForCivilians(void)868 static void SetupProfileInsertionDataForCivilians(void)
869 {
870 	FOR_EACH_IN_TEAM(s, CIV_TEAM)
871 	{
872 		if (s->bInSector) SetupProfileInsertionDataForSoldier(s);
873 	}
874 }
875 
876 
EnterSector(INT16 const x,INT16 const y,INT8 const z)877 static void EnterSector(INT16 const x, INT16 const y, INT8 const z)
878 {
879 	PauseGame();
880 	// Stop time for this frame
881 	InterruptTime();
882 
883 	/* Setup the tactical existance of RPCs and CIVs in the last sector before
884 	 * moving on to a new sector. */
885 	//@@@Evaluate
886 	if (gfWorldLoaded) SetupProfileInsertionDataForCivilians();
887 
888 	if (!(gTacticalStatus.uiFlags & LOADING_SAVED_GAME))
889 	{
890 		// Handle NPC stuff related to changing sectors
891 		HandleQuestCodeOnSectorEntry(x, y, z);
892 	}
893 
894 	BeginLoadScreen();
895 
896 	/* This has to be done before loadworld, as it will remmove old gridnos if
897 	 * present */
898 	RemoveMercsInSector();
899 
900 	if (!AreInMeanwhile())
901 	{
902 		SetSectorFlag(x, y, z, SF_ALREADY_VISITED);
903 	}
904 
905 	CreateLoadingScreenProgressBar();
906 
907 	char filename[50];
908 	GetMapFileName(x, y, z, filename, TRUE);
909 	LoadWorld(filename);
910 	LoadRadarScreenBitmap(filename);
911 	// We have to add the helicopter after the sector is fully loaded
912 	// to prevent that the pathfinding doenst consider its collission-grids
913 	HandleHelicopterOnGround(true);
914 
915 	/* ATE: Moved this form above, so that we can have the benefit of changing the
916 	 * world BEFORE adding guys to it. */
917 	if (!(gTacticalStatus.uiFlags & LOADING_SAVED_GAME))
918 	{
919 		try
920 		{ // Load the current sectors Information From the temporary files
921 			LoadCurrentSectorsInformationFromTempItemsFile();
922 		}
923 		catch (...)
924 		{ /* The integrity of the temp files have been compromised.  Boot out of the
925 			 * game after warning message. */
926 			InitExitGameDialogBecauseFileHackDetected();
927 			return;
928 		}
929 	}
930 
931 	RemoveLoadingScreenProgressBar();
932 
933 	if (gfEnterTacticalPlacementGUI)
934 	{
935 		SetPendingNewScreen(GAME_SCREEN);
936 		InitTacticalPlacementGUI();
937 	}
938 	else
939 	{
940 		EndMapScreen(FALSE);
941 		PrepareLoadedSector();
942 	}
943 
944 	/* This function will either hide or display the tree tops, depending on the
945 	 * game setting */
946 	SetTreeTopStateForMap();
947 }
948 
949 
UpdateMercsInSector()950 void UpdateMercsInSector()
951 {
952 	// Remove from interface slot
953 	RemoveAllPlayersFromSlot();
954 
955 	// Remove tactical interface stuff
956 	guiPendingOverrideEvent = I_CHANGE_TO_IDLE;
957 
958 	//If we are in this function during the loading of a sector
959 	if (!(gTacticalStatus.uiFlags & LOADING_SAVED_GAME))
960 	{
961 		//DONT set these values
962 		SetSelectedMan(NULL);
963 		gfGameScreenLocateToSoldier = TRUE;
964 	}
965 
966 	SetAllAutoFacesInactive();
967 
968 	if (fUsingEdgePointsForStrategicEntry)
969 	{
970 		BeginMapEdgepointSearch();
971 	}
972 
973 	UINT8       pow_squad   = NO_CURRENT_SQUAD;
974 	UINT8 const first_enemy = gTacticalStatus.Team[ENEMY_TEAM].bFirstID;
975 	UINT8 const last_enemy  = gTacticalStatus.Team[CREATURE_TEAM].bLastID;
976 	INT16 const sSectorX    = gWorldSectorX;
977 	INT16 const sSectorY    = gWorldSectorY;
978 	INT8  const bSectorZ    = gbWorldSectorZ;
979 	for (INT32 i = 0; i != MAX_NUM_SOLDIERS; ++i)
980 	{
981 		if (gfRestoringEnemySoldiersFromTempFile &&
982 				first_enemy <= i && i <= last_enemy)
983 		{ /* Don't update enemies/creatures (consec. teams) if they were just
984 			 * restored via the temp map files */
985 			continue;
986 		}
987 
988 		SOLDIERTYPE& s = GetMan(i);
989 		RemoveMercSlot(&s);
990 
991 		s.bInSector = FALSE;
992 
993 		if (!s.bActive)             continue;
994 		if (s.sSectorX != sSectorX) continue;
995 		if (s.sSectorY != sSectorY) continue;
996 		if (s.bSectorZ != bSectorZ) continue;
997 		if (s.fBetweenSectors)      continue;
998 
999 		if (!(gTacticalStatus.uiFlags & LOADING_SAVED_GAME))
1000 		{
1001 			if (gMapInformation.sCenterGridNo != -1 &&
1002 					gfBlitBattleSectorLocator           &&
1003 					s.bTeam != CIV_TEAM                 &&
1004 					(
1005 						gubEnemyEncounterCode == ENEMY_AMBUSH_CODE ||
1006 						gubEnemyEncounterCode == BLOODCAT_AMBUSH_CODE
1007 					))
1008 			{
1009 				s.ubStrategicInsertionCode = INSERTION_CODE_GRIDNO;
1010 				s.usStrategicInsertionData = gMapInformation.sCenterGridNo;
1011 			}
1012 			else if (gfOverrideInsertionWithExitGrid)
1013 			{
1014 				s.ubStrategicInsertionCode = INSERTION_CODE_GRIDNO;
1015 				s.usStrategicInsertionData = gExitGrid.usGridNo;
1016 			}
1017 		}
1018 
1019 		UpdateMercInSector(s, sSectorX, sSectorY, bSectorZ);
1020 
1021 		if (gTacticalStatus.uiFlags & LOADING_SAVED_GAME) continue;
1022 		if (s.bAssignment != ASSIGNMENT_POW)              continue;
1023 
1024 		if (pow_squad == NO_CURRENT_SQUAD)
1025 		{
1026 			// ATE: If we are in i13 - pop up message!
1027 			if (sSectorY == MAP_ROW_I && sSectorX == 13)
1028 			{
1029 				DoMessageBox(MSG_BOX_BASIC_STYLE, TacticalStr[POW_MERCS_ARE_HERE], GAME_SCREEN, MSG_BOX_FLAG_OK, NULL, NULL);
1030 			}
1031 			else
1032 			{
1033 				AddCharacterToUniqueSquad(&s);
1034 				pow_squad  = s.bAssignment;
1035 				s.bNeutral = FALSE;
1036 			}
1037 		}
1038 		else
1039 		{
1040 			if (sSectorY != MAP_ROW_I && sSectorX != 13)
1041 			{
1042 				AddCharacterToSquad(&s, pow_squad);
1043 			}
1044 		}
1045 
1046 		// ATE: Call actions based on what POW we are on...
1047 		if (gubQuest[QUEST_HELD_IN_ALMA] == QUESTINPROGRESS)
1048 		{
1049 			EndQuest(QUEST_HELD_IN_ALMA, sSectorX, sSectorY);
1050 			HandleNPCDoAction(0, NPC_ACTION_GRANT_EXPERIENCE_3, 0);
1051 		}
1052 	}
1053 
1054 	if (fUsingEdgePointsForStrategicEntry)
1055 	{
1056 		EndMapEdgepointSearch();
1057 		fUsingEdgePointsForStrategicEntry = FALSE;
1058 	}
1059 }
1060 
1061 
1062 static ST::string GetLoadedSectorString();
1063 
1064 
UpdateMercInSector(SOLDIERTYPE & s,INT16 const x,INT16 const y,INT8 const z)1065 void UpdateMercInSector(SOLDIERTYPE& s, INT16 const x, INT16 const y, INT8 const z)
1066 {
1067 	// Determine entrance direction and get sweetspot
1068 	// Some checks here must be fleshed out
1069 
1070 	if (!s.bActive) return;
1071 
1072 	if (s.bAssignment == IN_TRANSIT) return;
1073 
1074 	if (s.ubProfile != NO_PROFILE && GetProfile(s.ubProfile).ubMiscFlags3 & PROFILE_MISC_FLAG3_PERMANENT_INSERTION_CODE)
1075 	{ // Override orders
1076 		s.bOrders = STATIONARY;
1077 	}
1078 
1079 	if (s.ubStrategicInsertionCode == INSERTION_CODE_PRIMARY_EDGEINDEX ||
1080 			s.ubStrategicInsertionCode == INSERTION_CODE_SECONDARY_EDGEINDEX)
1081 	{
1082 		if (!fUsingEdgePointsForStrategicEntry)
1083 		{ // If we are not supposed to use this now, pick something better
1084 			s.ubStrategicInsertionCode = (UINT8)s.usStrategicInsertionData;
1085 		}
1086 	}
1087 
1088 MAPEDGEPOINT_SEARCH_FAILED:
1089 	// Use insertion direction from loaded map
1090 	GridNo gridno;
1091 	switch (s.ubStrategicInsertionCode)
1092 	{
1093 		case INSERTION_CODE_NORTH:  gridno = gMapInformation.sNorthGridNo;  goto check_entry;
1094 		case INSERTION_CODE_SOUTH:  gridno = gMapInformation.sSouthGridNo;  goto check_entry;
1095 		case INSERTION_CODE_EAST:   gridno = gMapInformation.sEastGridNo;   goto check_entry;
1096 		case INSERTION_CODE_WEST:   gridno = gMapInformation.sWestGridNo;   goto check_entry;
1097 		case INSERTION_CODE_CENTER: gridno = gMapInformation.sCenterGridNo; goto check_entry;
1098 check_entry:
1099 			if (gridno == -1 && !gfEditMode)
1100 			{ /* Strategic insertion failed because it expected to find an entry
1101 				 * point. This is likely a missing part of the map or possible fault in
1102 				 * strategic movement costs, traversal logic, etc. */
1103 				ST::string sector = GetLoadedSectorString();
1104 
1105 				ST::string entry;
1106 				if (gMapInformation.sNorthGridNo != -1)
1107 				{
1108 					entry  = "north";
1109 					gridno = gMapInformation.sNorthGridNo;
1110 				}
1111 				else if (gMapInformation.sEastGridNo != -1)
1112 				{
1113 					entry  = "east";
1114 					gridno = gMapInformation.sEastGridNo;
1115 				}
1116 				else if (gMapInformation.sSouthGridNo != -1)
1117 				{
1118 					entry  = "south";
1119 					gridno = gMapInformation.sSouthGridNo;
1120 				}
1121 				else if (gMapInformation.sWestGridNo != -1)
1122 				{
1123 					entry  = "west";
1124 					gridno = gMapInformation.sWestGridNo;
1125 				}
1126 				else if (gMapInformation.sCenterGridNo != -1)
1127 				{
1128 					entry  = "center";
1129 					gridno = gMapInformation.sCenterGridNo;
1130 				}
1131 				else
1132 				{
1133 					SLOGD("Sector %s has NO entrypoints -- using precise center of map for %s.", sector.c_str(), s.name.c_str());
1134 					goto place_in_center;
1135 				}
1136 				ST::string no_entry;
1137 				switch (s.ubStrategicInsertionCode)
1138 				{
1139 					case INSERTION_CODE_NORTH:  no_entry = "north";  break;
1140 					case INSERTION_CODE_EAST:   no_entry = "east";   break;
1141 					case INSERTION_CODE_SOUTH:  no_entry = "south";  break;
1142 					case INSERTION_CODE_WEST:   no_entry = "west";   break;
1143 					case INSERTION_CODE_CENTER: no_entry = "center"; break;
1144 				}
1145 				if (!no_entry.empty())
1146 				{
1147 					SLOGD("Sector %s doesn't have a %s entrypoint -- substituting %s entrypoint for %s.", sector.c_str(), no_entry.c_str(), entry.c_str(), s.name.c_str());
1148 				}
1149 			}
1150 			break;
1151 
1152 		case INSERTION_CODE_GRIDNO:
1153 			gridno = s.usStrategicInsertionData;
1154 			break;
1155 
1156 		case INSERTION_CODE_PRIMARY_EDGEINDEX:
1157 		{
1158 			gridno = SearchForClosestPrimaryMapEdgepoint(s.sPendingActionData2, (UINT8)s.usStrategicInsertionData);
1159 			SLOGD("%s's primary insertion gridno is %d using %d as initial search gridno and %d insertion code.",
1160 						s.name.c_str(), gridno, s.sPendingActionData2, s.usStrategicInsertionData);
1161 			if (gridno == NOWHERE)
1162 			{
1163 				SLOGE("Main edgepoint search failed for %s -- substituting entrypoint.", s.name.c_str());
1164 				s.ubStrategicInsertionCode = (UINT8)s.usStrategicInsertionData;
1165 				goto MAPEDGEPOINT_SEARCH_FAILED;
1166 			}
1167 			break;
1168 		}
1169 
1170 		case INSERTION_CODE_SECONDARY_EDGEINDEX:
1171 		{
1172 			gridno = SearchForClosestSecondaryMapEdgepoint(s.sPendingActionData2, (UINT8)s.usStrategicInsertionData);
1173 			SLOGD("%s's isolated insertion gridno is %d using %d as initial search gridno and %d insertion code.",
1174 						s.name.c_str(), gridno, s.sPendingActionData2, s.usStrategicInsertionData);
1175 			if (gridno == NOWHERE)
1176 			{
1177 				SLOGE("Isolated edgepoint search failed for %s -- substituting entrypoint.", s.name.c_str());
1178 				s.ubStrategicInsertionCode = (UINT8)s.usStrategicInsertionData;
1179 				goto MAPEDGEPOINT_SEARCH_FAILED;
1180 			}
1181 			break;
1182 		}
1183 
1184 		case INSERTION_CODE_ARRIVING_GAME:
1185 			// Are we in the start sector?
1186 			if (SECTOR(x,             y)             == START_SECTOR && z              == 0 &&
1187 					SECTOR(gWorldSectorX, gWorldSectorY) == START_SECTOR && gbWorldSectorZ == 0)
1188 			{ // Try another location and walk into map
1189 				gridno = 4379;
1190 			}
1191 			else
1192 			{
1193 				s.ubStrategicInsertionCode = INSERTION_CODE_NORTH;
1194 				gridno                     = gMapInformation.sNorthGridNo;
1195 			}
1196 			break;
1197 
1198 		case INSERTION_CODE_CHOPPER:
1199 			AddMercToHeli(&s);
1200 			return;
1201 
1202 		default:
1203 			SLOGD("Improper insertion code %d given to UpdateMercsInSector", s.ubStrategicInsertionCode);
1204 			goto place_in_center;
1205 	}
1206 
1207 	// If no insertion direction exists, this is bad!
1208 	if (gridno == -1)
1209 	{
1210 		SLOGW("Insertion gridno for direction %d not added to map sector %d %d", s.ubStrategicInsertionCode, x, y);
1211 place_in_center:
1212 		gridno = WORLD_ROWS / 2 * WORLD_COLS + WORLD_COLS / 2;
1213 	}
1214 
1215 	s.sInsertionGridNo = gridno;
1216 	AddSoldierToSector(&s);
1217 }
1218 
1219 
InitializeStrategicMapSectorTownNames(void)1220 static void InitializeStrategicMapSectorTownNames(void)
1221 {
1222 	for (auto& element: GCM->getTowns())
1223 	{
1224 		auto town = element.second;
1225 		for (auto sector : town->sectorIDs)
1226 		{
1227 			StrategicMap[ SECTOR_INFO_TO_STRATEGIC_INDEX(sector) ].bNameId = town->townId;
1228 		}
1229 	}
1230 }
1231 
GetSectorLandTypeString(UINT8 const ubSectorID,UINT8 const ubSectorZ,bool const fDetailed)1232 ST::string GetSectorLandTypeString(UINT8 const ubSectorID, UINT8 const ubSectorZ, bool const fDetailed)
1233 {
1234 	// first consider map secrets and SAM sites
1235 	auto secret = GetMapSecretBySectorID(ubSectorID);
1236 	if (ubSectorZ == 0 && secret)
1237 	{
1238 		UINT8 ubLandType = secret->getLandType(IsSecretFoundAt(ubSectorID));
1239 		if (ubLandType != TOWN) // we will handle town sectors separately
1240 		{
1241 			return (secret->isSAMSite && !fDetailed)
1242 					? GCM->getLandTypeString(SAM_SITE)
1243 					: GCM->getLandTypeString(ubLandType);
1244 		}
1245 	}
1246 
1247 	// special facilities
1248 	if (ubSectorZ > 0 || fDetailed)
1249 	{
1250 		int16_t landType = GCM->getSectorLandType(ubSectorID, ubSectorZ);
1251 		if (landType >= 0)
1252 		{
1253 			return GCM->getLandTypeString(landType);
1254 		}
1255 	}
1256 
1257 	INT8 const town_name_id = StrategicMap[SECTOR_INFO_TO_STRATEGIC_INDEX(ubSectorID)].bNameId;
1258 	if (town_name_id != BLANK_SECTOR)
1259 	{	// show town name
1260 		return GCM->getTownName(town_name_id);
1261 	}
1262 
1263 	if (ubSectorZ > 0)
1264 	{	// any other underground sectors (not facility, not part of a mine) are creature lair
1265 		return GCM->getLandTypeString(CREATURE_LAIR);
1266 	}
1267 
1268 	// finally consider the sector traversibility
1269 	UINT8 ubTraversibility = SectorInfo[ubSectorID].ubTraversability[THROUGH_STRATEGIC_MOVE];
1270 	return GCM->getLandTypeString(ubTraversibility);
1271 }
1272 
GetSectorIDString(INT16 x,INT16 y,INT8 z,BOOLEAN detailed)1273 ST::string GetSectorIDString(INT16 x, INT16 y, INT8 z, BOOLEAN detailed)
1274 {
1275 	if (x <= 0 || y <= 0 || z < 0) /* Empty? */
1276 	{
1277 		return ST::null;
1278 	}
1279 
1280 	if (z != 0)
1281 	{
1282 		UNDERGROUND_SECTORINFO const* const u = FindUnderGroundSector(x, y, z);
1283 		if (!u || (!(u->uiFlags & SF_ALREADY_VISITED) && !gfGettingNameFromSaveLoadScreen))
1284 		{ // Display nothing
1285 			return ST::null;
1286 		}
1287 	}
1288 
1289 	INT8    const  mine_index = GetIdOfMineForSector(x, y, z);
1290 	ST::string add;
1291 	if (mine_index != -1)
1292 	{
1293 		add = GCM->getTownName(GetTownAssociatedWithMine(mine_index));
1294 		if (detailed && mine_index != -1)
1295 		{	// Append "Mine"
1296 			add += ST::format(" {}", pwMineStrings[0]);
1297 		}
1298 	}
1299 
1300 	if (add.empty())
1301 	{
1302 		UINT8 const sector_id = SECTOR(x, y);
1303 		add = GetSectorLandTypeString(sector_id, z, detailed);
1304 	}
1305 
1306 	return ST::format("{c}{}: {}", 'A' + y - 1, x, add);
1307 }
1308 
1309 
SetInsertionDataFromAdjacentMoveDirection(SOLDIERTYPE & s,UINT8 const tactical_direction,INT16 const additional_data)1310 static void SetInsertionDataFromAdjacentMoveDirection(SOLDIERTYPE& s, UINT8 const tactical_direction, INT16 const additional_data)
1311 {
1312 	// Set insertion code
1313 	switch (tactical_direction)
1314 	{
1315 		case 255:
1316 			// We are using an exit grid, set insertion values
1317 			EXITGRID ExitGrid;
1318 			if (!GetExitGrid(additional_data, &ExitGrid))
1319 			{
1320 				SLOGA("No valid Exit grid can be found when one was expected: SetInsertionDataFromAdjacentMoveDirection.");
1321 			}
1322 			s.ubStrategicInsertionCode        = INSERTION_CODE_GRIDNO;
1323 			s.usStrategicInsertionData        = ExitGrid.usGridNo;
1324 			s.bUseExitGridForReentryDirection = TRUE;
1325 			break;
1326 
1327 		case NORTH: s.ubStrategicInsertionCode = INSERTION_CODE_SOUTH; break;
1328 		case SOUTH: s.ubStrategicInsertionCode = INSERTION_CODE_NORTH; break;
1329 		case EAST:  s.ubStrategicInsertionCode = INSERTION_CODE_WEST;  break;
1330 		case WEST:  s.ubStrategicInsertionCode = INSERTION_CODE_EAST;  break;
1331 
1332 		default: // Wrong direction given
1333 			SLOGD("Improper insertion direction %d given to SetInsertionDataFromAdjacentMoveDirection", tactical_direction);
1334 			s.ubStrategicInsertionCode = INSERTION_CODE_WEST;
1335 			break;
1336 	}
1337 }
1338 
1339 
GetInsertionDataFromAdjacentMoveDirection(UINT8 ubTacticalDirection,INT16 sAdditionalData)1340 static UINT8 GetInsertionDataFromAdjacentMoveDirection(UINT8 ubTacticalDirection, INT16 sAdditionalData)
1341 {
1342 	UINT8				ubDirection;
1343 
1344 
1345 	// Set insertion code
1346 	switch( ubTacticalDirection )
1347 	{
1348 		// OK, we are using an exit grid - set insertion values...
1349 
1350 		case 255:
1351 
1352 			ubDirection = 255;
1353 			break;
1354 
1355 		case NORTH:
1356 			ubDirection = NORTH_STRATEGIC_MOVE;
1357 			break;
1358 		case SOUTH:
1359 			ubDirection = SOUTH_STRATEGIC_MOVE;
1360 			break;
1361 		case EAST:
1362 			ubDirection = EAST_STRATEGIC_MOVE;
1363 			break;
1364 		case WEST:
1365 			ubDirection = WEST_STRATEGIC_MOVE;
1366 			break;
1367 		default:
1368 			// Wrong direction given!
1369 			SLOGD("Improper insertion direction %d given to GetInsertionDataFromAdjacentMoveDirection", ubTacticalDirection);
1370 			ubDirection = EAST_STRATEGIC_MOVE;
1371 	}
1372 
1373 	return( ubDirection );
1374 
1375 }
1376 
1377 
GetStrategicInsertionDataFromAdjacentMoveDirection(UINT8 ubTacticalDirection,INT16 sAdditionalData)1378 static UINT8 GetStrategicInsertionDataFromAdjacentMoveDirection(UINT8 ubTacticalDirection, INT16 sAdditionalData)
1379 {
1380 	UINT8				ubDirection;
1381 
1382 
1383 	// Set insertion code
1384 	switch( ubTacticalDirection )
1385 	{
1386 		// OK, we are using an exit grid - set insertion values...
1387 
1388 		case 255:
1389 
1390 			ubDirection = 255;
1391 			break;
1392 
1393 		case NORTH:
1394 			ubDirection = INSERTION_CODE_SOUTH;
1395 			break;
1396 		case SOUTH:
1397 			ubDirection = INSERTION_CODE_NORTH;
1398 			break;
1399 		case EAST:
1400 			ubDirection = INSERTION_CODE_WEST;
1401 			break;
1402 		case WEST:
1403 			ubDirection = INSERTION_CODE_EAST;
1404 			break;
1405 		default:
1406 			// Wrong direction given!
1407 			SLOGD("Improper insertion direction %d given to GetStrategicInsertionDataFromAdjacentMoveDirection", ubTacticalDirection);
1408 			ubDirection = EAST_STRATEGIC_MOVE;
1409 	}
1410 	return( ubDirection );
1411 }
1412 
1413 
1414 static INT16 PickGridNoNearestEdge(SOLDIERTYPE* pSoldier, UINT8 ubTacticalDirection);
1415 
1416 
JumpIntoAdjacentSector(UINT8 ubTacticalDirection,UINT8 ubJumpCode,INT16 sAdditionalData)1417 void JumpIntoAdjacentSector( UINT8 ubTacticalDirection, UINT8 ubJumpCode, INT16 sAdditionalData )
1418 {
1419 	SOLDIERTYPE *pValidSoldier = NULL;
1420 	UINT32 uiTraverseTime=0;
1421 	UINT8 ubDirection = (UINT8)-1; // XXX HACK000E
1422 	EXITGRID ExitGrid;
1423 
1424 	// Set initial selected
1425 	// ATE: moved this towards top...
1426 	SOLDIERTYPE* const sel = GetSelectedMan();
1427 	gPreferredInitialSelectedGuy = sel;
1428 
1429 	if ( ubJumpCode == JUMP_ALL_LOAD_NEW || ubJumpCode == JUMP_ALL_NO_LOAD )
1430 	{
1431 		// TODO: Check flags to see if we can jump!
1432 		// Move controllable mercs!
1433 		FOR_EACH_IN_TEAM(s, OUR_TEAM)
1434 		{
1435 			// If we are controllable
1436 			if (OkControllableMerc(s) && s->bAssignment == CurrentSquad())
1437 			{
1438 				pValidSoldier = s;
1439 				//This now gets handled by strategic movement.  It is possible that the
1440 				//group won't move instantaneously.
1441 				//s->sSectorX = sNewX;
1442 				//s->sSectorY = sNewY;
1443 
1444 				ubDirection = GetInsertionDataFromAdjacentMoveDirection( ubTacticalDirection, sAdditionalData );
1445 				break;
1446 			}
1447 		}
1448 	}
1449 	else if ( ( ubJumpCode == JUMP_SINGLE_LOAD_NEW || ubJumpCode == JUMP_SINGLE_NO_LOAD ) )
1450 	{
1451 		// Use selected soldier...
1452 		// This guy should always be 1 ) selected and 2 ) close enough to exit sector to leave
1453 		if (sel != NULL)
1454 		{
1455 			pValidSoldier = sel;
1456 			ubDirection = GetInsertionDataFromAdjacentMoveDirection( ubTacticalDirection, sAdditionalData );
1457 		}
1458 
1459 		if( ubJumpCode == JUMP_SINGLE_NO_LOAD )
1460 		{ // handle soldier moving by themselves
1461 			HandleSoldierLeavingSectorByThemSelf( pValidSoldier );
1462 		}
1463 		else
1464 		{ // now add char to a squad all their own
1465 			AddCharacterToUniqueSquad( pValidSoldier );
1466 		}
1467 	}
1468 	else
1469 	{
1470 		// OK, no jump code here given...
1471 		SLOGD("Improper jump code %d given to JumpIntoAdjacentSector", ubJumpCode);
1472 	}
1473 
1474 	Assert( pValidSoldier );
1475 
1476 	//Now, determine the traversal time.
1477 	GROUP* const pGroup = GetGroup(pValidSoldier->ubGroupID);
1478 	AssertMsg( pGroup, String( "%s is not in a valid group (pSoldier->ubGroupID is %d)", pValidSoldier->name.c_str(), pValidSoldier->ubGroupID) );
1479 
1480 	// If we are going through an exit grid, don't get traversal direction!
1481 	if ( ubTacticalDirection != 255 )
1482 	{
1483 		if( !gbWorldSectorZ )
1484 		{
1485 			uiTraverseTime = GetSectorMvtTimeForGroup( (UINT8)SECTOR( pGroup->ubSectorX, pGroup->ubSectorY ), ubDirection, pGroup );
1486 		}
1487 		else if( gbWorldSectorZ > 0 )
1488 		{ //We are attempting to traverse in an underground environment.  We need to use a complete different
1489 			//method.  When underground, all sectors are instantly adjacent.
1490 			uiTraverseTime = UndergroundTacticalTraversalTime( ubDirection );
1491 		}
1492 		AssertMsg(uiTraverseTime != TRAVERSE_TIME_IMPOSSIBLE, "Attempting to tactically traverse to adjacent sector, despite being unable to do so.");
1493 	}
1494 
1495 	// Alrighty, we want to do whatever our omnipotent player asked us to do
1496 	// this is what the ubJumpCode is for.
1497 	// Regardless of that we were asked to do, we MUST walk OFF ( Ian loves this... )
1498 	// So..... let's setup our people to walk off...
1499 	// We deal with a pGroup here... if an all move or a group...
1500 
1501 	// Setup some globals so our callback that deals when guys go off screen is handled....
1502 	// Look in the handler function AllMercsHaveWalkedOffSector() below...
1503 	gpAdjacentGroup				= pGroup;
1504 	gubAdjacentJumpCode		= ubJumpCode;
1505 	guiAdjacentTraverseTime	= uiTraverseTime;
1506 	gubTacticalDirection  = ubTacticalDirection;
1507 	gsAdditionalData			= sAdditionalData;
1508 
1509 	// If normal direction, use it!
1510 	if ( ubTacticalDirection != 255 )
1511 	{
1512 		gsAdjacentSectorX				= (INT16)(gWorldSectorX + DirXIncrementer[ ubTacticalDirection ]);
1513 		gsAdjacentSectorY				= (INT16)(gWorldSectorY + DirYIncrementer[ ubTacticalDirection ]);
1514 		gbAdjacentSectorZ				= pValidSoldier->bSectorZ;
1515 	}
1516 	else
1517 	{
1518 		// Take directions from exit grid info!
1519 		if ( !GetExitGrid( sAdditionalData, &ExitGrid ) )
1520 		{
1521 			SLOGA("Told to use exit grid at %d but one does not exist", sAdditionalData);
1522 		}
1523 
1524 		gsAdjacentSectorX				= ExitGrid.ubGotoSectorX;
1525 		gsAdjacentSectorY				= ExitGrid.ubGotoSectorY;
1526 		gbAdjacentSectorZ				= ExitGrid.ubGotoSectorZ;
1527 	}
1528 
1529 	// Give guy(s) orders to walk off sector...
1530 	if( pGroup->fPlayer )
1531 	{	//For player groups, update the soldier information
1532 		UINT8				ubNum = 0;
1533 
1534 		CFOR_EACH_PLAYER_IN_GROUP(curr, pGroup)
1535 		{
1536 			if ( OK_CONTROLLABLE_MERC( curr->pSoldier) )
1537 			{
1538 				if ( ubTacticalDirection != 255 )
1539 				{
1540 					const INT16 sGridNo = PickGridNoNearestEdge(curr->pSoldier, ubTacticalDirection);
1541 
1542 					curr->pSoldier->sPreTraversalGridNo = curr->pSoldier->sGridNo;
1543 
1544 					if ( sGridNo != NOWHERE )
1545 					{
1546 						// Save wait code - this will make buddy walk off screen into oblivion
1547 						curr->pSoldier->ubWaitActionToDo = 2;
1548 						// This will set the direction so we know now to move into oblivion
1549 						curr->pSoldier->uiPendingActionData1 = ubTacticalDirection;
1550 					}
1551 					else
1552 					{
1553 						SLOGA("Failed to get good exit location for adjacentmove");
1554 					}
1555 
1556 					EVENT_GetNewSoldierPath( curr->pSoldier, sGridNo, WALKING );
1557 
1558 				}
1559 				else
1560 				{
1561 					// Here, get closest location for exit grid....
1562 					const INT16 sGridNo = FindGridNoFromSweetSpotCloseToExitGrid(curr->pSoldier, sAdditionalData, 10);
1563 
1564 					//curr->pSoldier->
1565 					if ( sGridNo != NOWHERE )
1566 					{
1567 						// Save wait code - this will make buddy walk off screen into oblivion
1568 						//curr->pSoldier->ubWaitActionToDo = 2;
1569 					}
1570 					else
1571 					{
1572 						SLOGA("Failed to get good exit location for adjacentmove");
1573 					}
1574 
1575 					// Don't worry about walk off screen, just stay at gridno...
1576 					curr->pSoldier->ubWaitActionToDo = 1;
1577 
1578 					// Set buddy go!
1579 					gfPlotPathToExitGrid = TRUE;
1580 					EVENT_GetNewSoldierPath( curr->pSoldier, sGridNo, WALKING );
1581 					gfPlotPathToExitGrid = FALSE;
1582 
1583 				}
1584 				ubNum++;
1585 			}
1586 			else
1587 			{
1588 				// We will remove them later....
1589 			}
1590 		}
1591 
1592 		// ATE: Do another round, removing guys from group that can't go on...
1593 BEGINNING_LOOP:
1594 		CFOR_EACH_PLAYER_IN_GROUP(curr, pGroup)
1595 		{
1596 			if ( !OK_CONTROLLABLE_MERC( curr->pSoldier ) )
1597 			{
1598 				RemoveCharacterFromSquads( curr->pSoldier );
1599 				goto BEGINNING_LOOP;
1600 			}
1601 		}
1602 
1603 		// OK, setup TacticalOverhead polling system that will notify us once everybody
1604 		// has made it to our destination.
1605 		const UINT8 action = (ubTacticalDirection == 255 ?
1606 			WAIT_FOR_MERCS_TO_WALK_TO_GRIDNO : WAIT_FOR_MERCS_TO_WALKOFF_SCREEN);
1607 		SetActionToDoOnceMercsGetToLocation(action, ubNum);
1608 
1609 		// Lock UI!
1610 		guiPendingOverrideEvent = LU_BEGINUILOCK;
1611 		HandleTacticalUI( );
1612 	}
1613 }
1614 
1615 
HandleSoldierLeavingSectorByThemSelf(SOLDIERTYPE * pSoldier)1616 void HandleSoldierLeavingSectorByThemSelf( SOLDIERTYPE *pSoldier )
1617 {
1618 	// soldier leaving thier squad behind, will rejoin later
1619 	// if soldier in a squad, set the fact they want to return here
1620 
1621 	if( pSoldier->bAssignment < ON_DUTY )
1622 	{
1623 			RemoveCharacterFromSquads( pSoldier ); // REDUNDANT AddCharacterToUniqueSquad()
1624 
1625 		// are they in a group?..remove from group
1626 		if( pSoldier->ubGroupID != 0 )
1627 		{
1628 			// remove from group
1629 			RemovePlayerFromGroup(*pSoldier);
1630 			pSoldier->ubGroupID = 0;
1631 		}
1632 	}
1633 	else
1634 	{
1635 		// otherwise, they are on thier own, not in a squad, simply remove mvt group
1636 		if( pSoldier->ubGroupID && pSoldier->bAssignment != VEHICLE )
1637 		{ //Can only remove groups if they aren't persistant (not in a squad or vehicle)
1638 			// delete group
1639 			RemoveGroup(*GetGroup(pSoldier->ubGroupID));
1640 			pSoldier->ubGroupID = 0;
1641 		}
1642 	}
1643 
1644 	// set to guard
1645 	AddCharacterToUniqueSquad( pSoldier );
1646 
1647 	if( pSoldier->ubGroupID == 0 )
1648 	{
1649 		// create independant group
1650 		GROUP& g = *CreateNewPlayerGroupDepartingFromSector(pSoldier->sSectorX, pSoldier->sSectorY);
1651 		AddPlayerToGroup(g, *pSoldier);
1652 	}
1653 }
1654 
1655 
1656 static void DoneFadeOutExitGridSector(void);
1657 static void HandlePotentialMoraleHitForSkimmingSectors(GROUP* pGroup);
1658 
1659 
AllMercsWalkedToExitGrid()1660 void AllMercsWalkedToExitGrid()
1661 {
1662 	BOOLEAN fDone;
1663 
1664 	HandlePotentialMoraleHitForSkimmingSectors( gpAdjacentGroup );
1665 
1666 	if( gubAdjacentJumpCode == JUMP_ALL_NO_LOAD || gubAdjacentJumpCode == JUMP_SINGLE_NO_LOAD )
1667 	{
1668 		Assert( gpAdjacentGroup );
1669 		CFOR_EACH_PLAYER_IN_GROUP(pPlayer, gpAdjacentGroup)
1670 		{
1671 			SOLDIERTYPE& s = *pPlayer->pSoldier;
1672 			SetInsertionDataFromAdjacentMoveDirection(s, gubTacticalDirection, gsAdditionalData);
1673 			RemoveSoldierFromTacticalSector(s);
1674 		}
1675 
1676 		SetGroupSectorValue(gsAdjacentSectorX, gsAdjacentSectorY, gbAdjacentSectorZ, *gpAdjacentGroup);
1677 
1678 		SetDefaultSquadOnSectorEntry( TRUE );
1679 
1680 	}
1681 	else
1682 	{
1683 		//Because we are actually loading the new map, and we are physically traversing, we don't want
1684 		//to bring up the prebattle interface when we arrive if there are enemies there.  This flag
1685 		//ignores the initialization of the prebattle interface and clears the flag.
1686 		gfTacticalTraversal = TRUE;
1687 		gpTacticalTraversalGroup = gpAdjacentGroup;
1688 
1689 		//Check for any unconcious and/or dead merc and remove them from the current squad, so that they
1690 		//don't get moved to the new sector.
1691 		fDone = FALSE;
1692 		while( !fDone )
1693 		{
1694 			fDone = FALSE;
1695 			const PLAYERGROUP* pPlayer = gpAdjacentGroup->pPlayerList;
1696 			while( pPlayer )
1697 			{
1698 				if( pPlayer->pSoldier->bLife < OKLIFE )
1699 				{
1700 					AddCharacterToUniqueSquad( pPlayer->pSoldier );
1701 					break;
1702 				}
1703 				pPlayer = pPlayer->next;
1704 			}
1705 			if( !pPlayer )
1706 			{
1707 				fDone = TRUE;
1708 			}
1709 		}
1710 
1711 		// OK, Set insertion direction for all these guys....
1712 		Assert( gpAdjacentGroup );
1713 		CFOR_EACH_PLAYER_IN_GROUP(pPlayer, gpAdjacentGroup)
1714 		{
1715 			SetInsertionDataFromAdjacentMoveDirection(*pPlayer->pSoldier, gubTacticalDirection, gsAdditionalData);
1716 		}
1717 		SetGroupSectorValue(gsAdjacentSectorX, gsAdjacentSectorY, gbAdjacentSectorZ, *gpAdjacentGroup);
1718 
1719 		gFadeOutDoneCallback = DoneFadeOutExitGridSector;
1720 		FadeOutGameScreen( );
1721 	}
1722 	if( !PlayerMercsInSector( (UINT8)gsAdjacentSectorX, (UINT8)gsAdjacentSectorY, (UINT8)gbAdjacentSectorZ ) )
1723 	{
1724 		HandleLoyaltyImplicationsOfMercRetreat( RETREAT_TACTICAL_TRAVERSAL, gsAdjacentSectorX, gsAdjacentSectorY, gbAdjacentSectorZ );
1725 	}
1726 	if( gubAdjacentJumpCode == JUMP_ALL_NO_LOAD || gubAdjacentJumpCode == JUMP_SINGLE_NO_LOAD )
1727 	{
1728 		gfTacticalTraversal = FALSE;
1729 		gpTacticalTraversalGroup = NULL;
1730 		gpTacticalTraversalChosenSoldier = NULL;
1731 	}
1732 }
1733 
1734 
SetupTacticalTraversalInformation(void)1735 static void SetupTacticalTraversalInformation(void)
1736 {
1737 	INT16 sScreenX, sScreenY;
1738 
1739 	Assert( gpAdjacentGroup );
1740 	CFOR_EACH_PLAYER_IN_GROUP(pPlayer, gpAdjacentGroup)
1741 	{
1742 		SOLDIERTYPE& s = *pPlayer->pSoldier;
1743 
1744 		SetInsertionDataFromAdjacentMoveDirection(s, gubTacticalDirection, gsAdditionalData);
1745 
1746 		// pass flag that this is a tactical traversal, the path built MUST go in the traversed direction even if longer!
1747 		PlotPathForCharacter(s, gsAdjacentSectorX, gsAdjacentSectorY, true);
1748 
1749 		if( guiAdjacentTraverseTime <= 5 )
1750 		{
1751 			// Determine 'mirror' gridno...
1752 			// Convert to absolute xy
1753 			GetAbsoluteScreenXYFromMapPos(GETWORLDINDEXFROMWORLDCOORDS(s.sY, s.sX), &sScreenX, &sScreenY);
1754 
1755 			// Get 'mirror', depending on what direction...
1756 			switch( gubTacticalDirection )
1757 			{
1758 				case NORTH:			sScreenY = 1520;				break;
1759 				case SOUTH:			sScreenY = 0;						break;
1760 				case EAST:			sScreenX = 0;						break;
1761 				case WEST:			sScreenX = 3160;				break;
1762 			}
1763 
1764 			// Convert into a gridno again.....
1765 			const GridNo sNewGridNo = GetMapPosFromAbsoluteScreenXY(sScreenX, sScreenY);
1766 
1767 			// Save this gridNo....
1768 			s.sPendingActionData2      = sNewGridNo;
1769 			// Copy CODe computed earlier into data
1770 			s.usStrategicInsertionData = s.ubStrategicInsertionCode;
1771 			// Now use NEW code....
1772 
1773 			s.ubStrategicInsertionCode = CalcMapEdgepointClassInsertionCode(s.sPreTraversalGridNo);
1774 
1775 			if( gubAdjacentJumpCode == JUMP_SINGLE_LOAD_NEW || gubAdjacentJumpCode == JUMP_ALL_LOAD_NEW )
1776 			{
1777 				fUsingEdgePointsForStrategicEntry = TRUE;
1778 			}
1779 		}
1780 	}
1781 	if( gubAdjacentJumpCode == JUMP_ALL_NO_LOAD || gubAdjacentJumpCode == JUMP_SINGLE_NO_LOAD )
1782 	{
1783 		gfTacticalTraversal = FALSE;
1784 		gpTacticalTraversalGroup = NULL;
1785 		gpTacticalTraversalChosenSoldier = NULL;
1786 	}
1787 }
1788 
1789 
1790 static void DoneFadeOutAdjacentSector(void);
1791 
1792 
AllMercsHaveWalkedOffSector()1793 void AllMercsHaveWalkedOffSector( )
1794 {
1795 	BOOLEAN fEnemiesInLoadedSector = FALSE;
1796 
1797 	if( NumEnemiesInAnySector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ ) )
1798 	{
1799 		fEnemiesInLoadedSector = TRUE;
1800 	}
1801 
1802 	if (fEnemiesInLoadedSector)
1803 	{
1804 		HandleLoyaltyImplicationsOfMercRetreat( RETREAT_TACTICAL_TRAVERSAL, gWorldSectorX, gWorldSectorY, gbWorldSectorZ );
1805 	}
1806 
1807 	//Setup strategic traversal information
1808 	if( guiAdjacentTraverseTime <= 5 )
1809 	{
1810 		gfTacticalTraversal = TRUE;
1811 		gpTacticalTraversalGroup = gpAdjacentGroup;
1812 
1813 		if( gbAdjacentSectorZ > 0 && guiAdjacentTraverseTime <= 5 )
1814 		{	//Nasty strategic movement logic doesn't like underground sectors!
1815 			gfUndergroundTacticalTraversal = TRUE;
1816 		}
1817 	}
1818 	ClearMercPathsAndWaypointsForAllInGroup(*gpAdjacentGroup);
1819 	AddWaypointToPGroup( gpAdjacentGroup, (UINT8)gsAdjacentSectorX, (UINT8)gsAdjacentSectorY );
1820 	if( gbAdjacentSectorZ > 0 && guiAdjacentTraverseTime <= 5 )
1821 	{	//Nasty strategic movement logic doesn't like underground sectors!
1822 		gfUndergroundTacticalTraversal = TRUE;
1823 	}
1824 
1825 	SetupTacticalTraversalInformation();
1826 
1827 	// ATE: Added here: donot load another screen if we were told not to....
1828 	if( ( gubAdjacentJumpCode == JUMP_ALL_NO_LOAD || gubAdjacentJumpCode == JUMP_SINGLE_NO_LOAD ) )
1829 	{ //Case 1:  Group is leaving sector, but there are other mercs in sector and player wants to stay, or
1830 		//         there are other mercs in sector while a battle is in progress.
1831 		CFOR_EACH_PLAYER_IN_GROUP(pPlayer, gpAdjacentGroup)
1832 		{
1833 			RemoveSoldierFromTacticalSector(*pPlayer->pSoldier);
1834 		}
1835 		SetDefaultSquadOnSectorEntry( TRUE );
1836 	}
1837 	else
1838 	{
1839 		if( fEnemiesInLoadedSector )
1840 		{ //We are retreating from a sector with enemies in it and there are no mercs left  so
1841 			//warp the game time by 5 minutes to simulate the actual retreat.  This restricts the
1842 			//player from immediately coming back to the same sector they left to perhaps take advantage
1843 			//of the tactical placement gui to get into better position.  Additionally, if there are any
1844 			//enemies in this sector that are part of a movement group, reset that movement group so that they
1845 			//are "in" the sector rather than 75% of the way to the next sector if that is the case.
1846 			ResetMovementForEnemyGroupsInLocation( (UINT8)gWorldSectorX, (UINT8)gWorldSectorY );
1847 
1848 			if( guiAdjacentTraverseTime > 5 )
1849 			{
1850 				//Because this final group is retreating, simulate extra time to retreat, so they can't immediately come back.
1851 				WarpGameTime( 300, WARPTIME_NO_PROCESSING_OF_EVENTS );
1852 			}
1853 		}
1854 		if( guiAdjacentTraverseTime <= 5 )
1855 		{
1856 			//Case 2:  Immediatly loading the next sector
1857 			if( !gbAdjacentSectorZ )
1858 			{
1859 				UINT32 uiWarpTime;
1860 				uiWarpTime = (GetWorldTotalMin() + 5) * 60 - GetWorldTotalSeconds();
1861 				WarpGameTime( uiWarpTime, WARPTIME_PROCESS_TARGET_TIME_FIRST );
1862 			}
1863 			else if( gbAdjacentSectorZ > 0 )
1864 			{
1865 				UINT32 uiWarpTime;
1866 				uiWarpTime = (GetWorldTotalMin() + 1) * 60 - GetWorldTotalSeconds();
1867 				WarpGameTime( uiWarpTime, WARPTIME_PROCESS_TARGET_TIME_FIRST );
1868 			}
1869 
1870 			//Because we are actually loading the new map, and we are physically traversing, we don't want
1871 			//to bring up the prebattle interface when we arrive if there are enemies there.  This flag
1872 			//ignores the initialization of the prebattle interface and clears the flag.
1873 			gFadeOutDoneCallback = DoneFadeOutAdjacentSector;
1874 			FadeOutGameScreen( );
1875 		}
1876 		else
1877 		{ //Case 3:  Going directly to mapscreen
1878 
1879 			//Lock game into mapscreen mode, but after the fade is done.
1880 			gfEnteringMapScreen = TRUE;
1881 
1882 			// ATE; Fade FAST....
1883 			SetMusicFadeSpeed( 5 );
1884 			SetMusicMode( MUSIC_TACTICAL_NOTHING );
1885 		}
1886 	}
1887 }
1888 
1889 
DoneFadeOutExitGridSector(void)1890 static void DoneFadeOutExitGridSector(void)
1891 {
1892 	SetCurrentWorldSector( gsAdjacentSectorX, gsAdjacentSectorY, gbAdjacentSectorZ );
1893 	if( gfTacticalTraversal && gpTacticalTraversalGroup && gpTacticalTraversalChosenSoldier )
1894 	{
1895 		if( gTacticalStatus.fEnemyInSector )
1896 		{
1897 			TacticalCharacterDialogue(gpTacticalTraversalChosenSoldier, QUOTE_ENEMY_PRESENCE);
1898 		}
1899 	}
1900 	gfTacticalTraversal = FALSE;
1901 	gpTacticalTraversalGroup = NULL;
1902 	gpTacticalTraversalChosenSoldier = NULL;
1903 	FadeInGameScreen( );
1904 }
1905 
1906 
1907 static INT16 PickGridNoToWalkIn(SOLDIERTYPE* pSoldier, UINT8 ubInsertionDirection, UINT32* puiNumAttempts);
1908 
1909 
DoneFadeOutAdjacentSector(void)1910 static void DoneFadeOutAdjacentSector(void)
1911 {
1912 	UINT8 ubDirection;
1913 	SetCurrentWorldSector( gsAdjacentSectorX, gsAdjacentSectorY, gbAdjacentSectorZ );
1914 
1915 	ubDirection = GetStrategicInsertionDataFromAdjacentMoveDirection( gubTacticalDirection, gsAdditionalData );
1916 	if( gfTacticalTraversal && gpTacticalTraversalGroup && gpTacticalTraversalChosenSoldier )
1917 	{
1918 		if( gTacticalStatus.fEnemyInSector )
1919 		{
1920 			TacticalCharacterDialogue(gpTacticalTraversalChosenSoldier, QUOTE_ENEMY_PRESENCE);
1921 		}
1922 	}
1923 	gfTacticalTraversal = FALSE;
1924 	gpTacticalTraversalGroup = NULL;
1925 	gpTacticalTraversalChosenSoldier = NULL;
1926 
1927 	if ( gfCaves )
1928 	{
1929 		// ATE; Set tactical status flag...
1930 		gTacticalStatus.uiFlags |= IGNORE_ALL_OBSTACLES;
1931 		// Set pathing flag to path through anything....
1932 		gfPathAroundObstacles = FALSE;
1933 	}
1934 
1935 	// OK, give our guys new orders...
1936 	if( gpAdjacentGroup->fPlayer )
1937 	{
1938 		//For player groups, update the soldier information
1939 		UINT32 uiAttempts;
1940 		INT16				sGridNo, sOldGridNo;
1941 		UINT8				ubNum = 0;
1942 		CFOR_EACH_PLAYER_IN_GROUP(curr, gpAdjacentGroup)
1943 		{
1944 			if (curr->pSoldier->sGridNo != NOWHERE)
1945 			{
1946 				sGridNo = PickGridNoToWalkIn(curr->pSoldier, ubDirection, &uiAttempts);
1947 
1948 				//If the search algorithm failed due to too many attempts, simply reset the
1949 				//the gridno as the destination is a reserved gridno and we will place the
1950 				//merc there without walking into the sector.
1951 				if (sGridNo == NOWHERE && uiAttempts == MAX_ATTEMPTS)
1952 				{
1953 					sGridNo = curr->pSoldier->sGridNo;
1954 				}
1955 
1956 				if (sGridNo != NOWHERE)
1957 				{
1958 					curr->pSoldier->ubWaitActionToDo = 1;
1959 					// OK, here we have been given a position, a gridno has been given to use as well....
1960 					sOldGridNo = curr->pSoldier->sGridNo;
1961 					EVENT_SetSoldierPosition(curr->pSoldier, sGridNo, SSP_NONE);
1962 					if (sGridNo != sOldGridNo)
1963 					{
1964 						EVENT_GetNewSoldierPath(curr->pSoldier, sOldGridNo, WALKING);
1965 					}
1966 					ubNum++;
1967 				}
1968 			}
1969 			else
1970 			{
1971 				SLOGW("%s's gridno is NOWHERE, and is attempting to walk into sector.", curr->pSoldier->name.c_str());
1972 			}
1973 		}
1974 		SetActionToDoOnceMercsGetToLocation(WAIT_FOR_MERCS_TO_WALKON_SCREEN, ubNum);
1975 		guiPendingOverrideEvent = LU_BEGINUILOCK;
1976 		HandleTacticalUI( );
1977 
1978 		// Unset flag here.....
1979 		gfPathAroundObstacles = TRUE;
1980 
1981 	}
1982 	FadeInGameScreen( );
1983 }
1984 
1985 
SoldierOKForSectorExit(SOLDIERTYPE * pSoldier,INT8 bExitDirection,UINT16 usAdditionalData)1986 static BOOLEAN SoldierOKForSectorExit(SOLDIERTYPE* pSoldier, INT8 bExitDirection, UINT16 usAdditionalData)
1987 {
1988 	INT16 sWorldX;
1989 	INT16 sWorldY;
1990 
1991 	// if the soldiers gridno is not NOWHERE
1992 	if( pSoldier->sGridNo == NOWHERE )
1993 		return( FALSE );
1994 
1995 	// OK, anyone on roofs cannot!
1996 	if ( pSoldier->bLevel > 0 )
1997 		return( FALSE );
1998 
1999 	// Get screen coordinates for current position of soldier
2000 	GetAbsoluteScreenXYFromMapPos(pSoldier->sGridNo, &sWorldX, &sWorldY);
2001 
2002 	// Check direction
2003 	switch( bExitDirection )
2004 	{
2005 		case EAST_STRATEGIC_MOVE:
2006 
2007 			if ( sWorldX < ( ( gsRightX - gsLeftX ) - CHECK_DIR_X_DELTA ) )
2008 			{
2009 				// NOT OK, return FALSE
2010 				return( FALSE );
2011 			}
2012 			break;
2013 
2014 		case WEST_STRATEGIC_MOVE:
2015 
2016 			if ( sWorldX > CHECK_DIR_X_DELTA )
2017 			{
2018 				// NOT OK, return FALSE
2019 				return( FALSE );
2020 			}
2021 			break;
2022 
2023 		case SOUTH_STRATEGIC_MOVE:
2024 
2025 			if ( sWorldY < ( ( gsBottomY - gsTopY ) - CHECK_DIR_Y_DELTA ) )
2026 			{
2027 				// NOT OK, return FALSE
2028 				return( FALSE );
2029 			}
2030 			break;
2031 
2032 		case NORTH_STRATEGIC_MOVE:
2033 
2034 			if ( sWorldY > CHECK_DIR_Y_DELTA )
2035 			{
2036 				// NOT OK, return FALSE
2037 				return( FALSE );
2038 			}
2039 			break;
2040 
2041 			// This case is for an exit grid....
2042 			// check if we are close enough.....
2043 
2044 		case -1:
2045 
2046 
2047 			// FOR REALTIME - DO MOVEMENT BASED ON STANCE!
2048 			if (!(gTacticalStatus.uiFlags & INCOMBAT))
2049 			{
2050 				pSoldier->usUIMovementMode =  GetMoveStateBasedOnStance( pSoldier, gAnimControl[ pSoldier->usAnimState ].ubEndHeight );
2051 			}
2052 
2053 			const INT16 sGridNo = FindGridNoFromSweetSpotCloseToExitGrid(pSoldier, usAdditionalData, 10);
2054 			if ( sGridNo == NOWHERE )
2055 			{
2056 				return( FALSE );
2057 			}
2058 
2059 			// ATE: if we are in combat, get cost to move here....
2060 			if ( gTacticalStatus.uiFlags & INCOMBAT )
2061 			{
2062 				// Turn off at end of function...
2063 				const INT16 sAPs = PlotPath(pSoldier, sGridNo, NO_COPYROUTE, NO_PLOT, pSoldier->usUIMovementMode, pSoldier->bActionPoints);
2064 				if ( !EnoughPoints( pSoldier, sAPs, 0, FALSE ) )
2065 				{
2066 					return( FALSE );
2067 				}
2068 			}
2069 			break;
2070 
2071 	}
2072 	return( TRUE );
2073 }
2074 
2075 //ATE: Returns FALSE if NOBODY is close enough, 1 if ONLY selected guy is and 2 if all on squad are...
OKForSectorExit(INT8 bExitDirection,UINT16 usAdditionalData,UINT32 * puiTraverseTimeInMinutes)2076 BOOLEAN OKForSectorExit( INT8 bExitDirection, UINT16 usAdditionalData, UINT32 *puiTraverseTimeInMinutes )
2077 {
2078 	BOOLEAN     fAtLeastOneMercControllable = FALSE;
2079 	BOOLEAN     fOnlySelectedGuy = FALSE;
2080 	SOLDIERTYPE *pValidSoldier = NULL;
2081 	UINT8       ubReturnVal = FALSE;
2082 	UINT8       ubNumControllableMercs = 0;
2083 	UINT8       ubNumMercs = 0, ubNumEPCs = 0;
2084 	UINT8       ubPlayerControllableMercsInSquad = 0;
2085 
2086 	const SOLDIERTYPE* const sel = GetSelectedMan();
2087 	// must have a selected soldier to be allowed to tactically traverse.
2088 	if (sel == NULL) return FALSE;
2089 
2090 	/*
2091 	//Exception code for the two sectors in San Mona that are separated by a cliff.  We want to allow strategic
2092 	//traversal, but NOT tactical traversal.  The only way to tactically go from D4 to D5 (or viceversa) is to enter
2093 	//the cave entrance.
2094 	if( gWorldSectorX == 4 && gWorldSectorY == 4 && !gbWorldSectorZ && bExitDirection == EAST_STRATEGIC_MOVE )
2095 	{
2096 		gfInvalidTraversal = TRUE;
2097 		return FALSE;
2098 	}
2099 	if( gWorldSectorX == 5 && gWorldSectorY == 4 && !gbWorldSectorZ && bExitDirection == WEST_STRATEGIC_MOVE )
2100 	{
2101 		gfInvalidTraversal = TRUE;
2102 		return FALSE;
2103 	}
2104 	*/
2105 
2106 	gfInvalidTraversal = FALSE;
2107 	gfLoneEPCAttemptingTraversal = FALSE;
2108 	gubLoneMercAttemptingToAbandonEPCs = 0;
2109 	gPotentiallyAbandonedEPC = NULL;
2110 
2111 	// Look through all mercs and check if they are within range of east end....
2112 	FOR_EACH_IN_TEAM(pSoldier, OUR_TEAM)
2113 	{
2114 		// If we are controllable
2115 		if (OkControllableMerc(pSoldier) && pSoldier->bAssignment == CurrentSquad())
2116 		{
2117 			//Need to keep a copy of a good soldier, so we can access it later, and
2118 			//not more than once.
2119 			pValidSoldier = pSoldier;
2120 
2121 			ubNumControllableMercs++;
2122 
2123 			//We need to keep track of the number of EPCs and mercs in this squad.  If we have
2124 			//only one merc and one or more EPCs, then we can't allow the merc to tactically traverse,
2125 			//if he is the only merc near enough to traverse.
2126 			if( AM_AN_EPC( pSoldier ) )
2127 			{
2128 				ubNumEPCs++;
2129 				//Also record the EPC's slot ID incase we later build a string using the EPC's name.
2130 				gPotentiallyAbandonedEPC = pSoldier;
2131 				if( AM_A_ROBOT( pSoldier ) && !CanRobotBeControlled( pSoldier ) )
2132 				{
2133 					gfRobotWithoutControllerAttemptingTraversal = TRUE;
2134 					ubNumControllableMercs--;
2135 					continue;
2136 				}
2137 			}
2138 			else
2139 			{
2140 				ubNumMercs++;
2141 			}
2142 
2143 			if ( SoldierOKForSectorExit( pSoldier, bExitDirection, usAdditionalData ) )
2144 			{
2145 				fAtLeastOneMercControllable++;
2146 
2147 				if (pSoldier == sel) fOnlySelectedGuy = TRUE;
2148 			}
2149 			else
2150 			{
2151 				GROUP *pGroup;
2152 
2153 				// ATE: Dont's assume exit grids here...
2154 				if ( bExitDirection != -1 )
2155 				{
2156 					//Now, determine if this is a valid path.
2157 					pGroup = GetGroup( pValidSoldier->ubGroupID );
2158 					AssertMsg( pGroup, String( "%s is not in a valid group (pSoldier->ubGroupID is %d)", pValidSoldier->name.c_str(), pValidSoldier->ubGroupID) );
2159 					UINT32 traverse_time = TRAVERSE_TIME_IMPOSSIBLE;
2160 					if( !gbWorldSectorZ )
2161 					{
2162 						traverse_time = GetSectorMvtTimeForGroup((UINT8)SECTOR(pGroup->ubSectorX, pGroup->ubSectorY), bExitDirection, pGroup);
2163 					}
2164 					else if( gbWorldSectorZ > 1 )
2165 					{ //We are attempting to traverse in an underground environment.  We need to use a complete different
2166 						//method.  When underground, all sectors are instantly adjacent.
2167 						traverse_time = UndergroundTacticalTraversalTime(bExitDirection);
2168 					}
2169 					if (puiTraverseTimeInMinutes) *puiTraverseTimeInMinutes = traverse_time;
2170 					if (traverse_time == TRAVERSE_TIME_IMPOSSIBLE)
2171 					{
2172 						gfInvalidTraversal = TRUE;
2173 						return FALSE;
2174 					}
2175 				}
2176 				else
2177 				{
2178 					// Exit grid travel is instantaneous
2179 					if (puiTraverseTimeInMinutes) *puiTraverseTimeInMinutes = 0;
2180 				}
2181 			}
2182 		}
2183 	}
2184 
2185 	// If we are here, at least one guy is controllable in this sector, at least he can go!
2186 	if( fAtLeastOneMercControllable )
2187 	{
2188 		ubPlayerControllableMercsInSquad = (UINT8)NumberOfPlayerControllableMercsInSquad(sel->bAssignment);
2189 		if( fAtLeastOneMercControllable <= ubPlayerControllableMercsInSquad )
2190 		{ //if the selected merc is an EPC and we can only leave with that merc, then prevent it
2191 			//as EPCs aren't allowed to leave by themselves.  Instead of restricting this in the
2192 			//exiting sector gui, we restrict it by explaining it with a message box.
2193 			if (AM_AN_EPC(sel))
2194 			{
2195 				if (fAtLeastOneMercControllable < ubPlayerControllableMercsInSquad || fAtLeastOneMercControllable == 1)
2196 				{
2197 					gfLoneEPCAttemptingTraversal = TRUE;
2198 					return FALSE;
2199 				}
2200 			}
2201 			else
2202 			{	//We previously counted the number of EPCs and mercs, and if the selected merc is not an EPC and there are no
2203 				//other mercs in the squad able to escort the EPCs, we will prohibit this merc from tactically traversing.
2204 				if( ubNumEPCs && ubNumMercs == 1 && fAtLeastOneMercControllable < ubPlayerControllableMercsInSquad )
2205 				{
2206 					gubLoneMercAttemptingToAbandonEPCs = ubNumEPCs;
2207 					return FALSE;
2208 				}
2209 			}
2210 		}
2211 		if ( bExitDirection != -1 )
2212 		{
2213 			GROUP *pGroup;
2214 			//Now, determine if this is a valid path.
2215 			pGroup = GetGroup( pValidSoldier->ubGroupID );
2216 			AssertMsg( pGroup, String( "%s is not in a valid group (pSoldier->ubGroupID is %d)", pValidSoldier->name.c_str(), pValidSoldier->ubGroupID) );
2217 			UINT32 traverse_time = TRAVERSE_TIME_IMPOSSIBLE; // -XXX Wmaybe-uninitialized : valid default?
2218 			if( !gbWorldSectorZ )
2219 			{
2220 				traverse_time = GetSectorMvtTimeForGroup((UINT8)SECTOR(pGroup->ubSectorX, pGroup->ubSectorY), bExitDirection, pGroup);
2221 			}
2222 			else if( gbWorldSectorZ > 0 )
2223 			{ //We are attempting to traverse in an underground environment.  We need to use a complete different
2224 				//method.  When underground, all sectors are instantly adjacent.
2225 				traverse_time = UndergroundTacticalTraversalTime(bExitDirection);
2226 			}
2227 			if (puiTraverseTimeInMinutes) *puiTraverseTimeInMinutes = traverse_time;
2228 			if (traverse_time == TRAVERSE_TIME_IMPOSSIBLE)
2229 			{
2230 				gfInvalidTraversal = TRUE;
2231 				ubReturnVal = FALSE;
2232 			}
2233 			else
2234 			{
2235 				ubReturnVal = TRUE;
2236 			}
2237 		}
2238 		else
2239 		{
2240 			ubReturnVal = TRUE;
2241 			// Exit grid travel is instantaneous
2242 			if (puiTraverseTimeInMinutes) *puiTraverseTimeInMinutes = 0;
2243 		}
2244 	}
2245 
2246 	if ( ubReturnVal )
2247 	{
2248 		// Default to FALSE again, until we see that we have
2249 		ubReturnVal = FALSE;
2250 
2251 		if ( fAtLeastOneMercControllable )
2252 		{
2253 			// Do we contain the selected guy?
2254 			if ( fOnlySelectedGuy )
2255 			{
2256 				ubReturnVal = 1;
2257 			}
2258 			// Is the whole squad able to go here?
2259 			if ( fAtLeastOneMercControllable == ubPlayerControllableMercsInSquad )
2260 			{
2261 				ubReturnVal = 2;
2262 			}
2263 		}
2264 	}
2265 
2266 	return( ubReturnVal );
2267 }
2268 
2269 
SetupNewStrategicGame()2270 void SetupNewStrategicGame()
2271 {
2272 	// Set all sectors as enemy controlled.
2273 	FOR_EACH(StrategicMapElement, i, StrategicMap)
2274 	{
2275 		i->fEnemyControlled = TRUE;
2276 	}
2277 
2278 	InitNewGameClock();
2279 	DeleteAllStrategicEvents();
2280 
2281 	// Set up all events that get processed daily.
2282 	BuildDayLightLevels();
2283 	// Check for quests each morning.
2284 	AddEveryDayStrategicEvent(EVENT_CHECKFORQUESTS,                   QUEST_CHECK_EVENT_TIME, 0);
2285 	// Some things get updated in the very early morning.
2286 	AddEveryDayStrategicEvent(EVENT_DAILY_EARLY_MORNING_EVENTS,       EARLY_MORNING_TIME,     0);
2287 	// Daily update BobbyRay Inventory.
2288 	AddEveryDayStrategicEvent(EVENT_DAILY_UPDATE_BOBBY_RAY_INVENTORY, BOBBYRAY_UPDATE_TIME,   0);
2289 	// Daily update of the M.E.R.C. site..
2290 	AddEveryDayStrategicEvent(EVENT_DAILY_UPDATE_OF_MERC_SITE,        0,                      0);
2291 	// Daily update of insured mercs.
2292 	AddEveryDayStrategicEvent(EVENT_HANDLE_INSURED_MERCS,             INSURANCE_UPDATE_TIME,  0);
2293 	// Daily update of mercs.
2294 	AddEveryDayStrategicEvent(EVENT_MERC_DAILY_UPDATE,                0,                      0);
2295 	// Daily mine production processing events.
2296 	AddEveryDayStrategicEvent(EVENT_SETUP_MINE_INCOME,                0,                      0);
2297 	// Daily checks for E-mail from Enrico.
2298 	AddEveryDayStrategicEvent(EVENT_ENRICO_MAIL,                      ENRICO_MAIL_TIME,       0);
2299 
2300 	// Hourly update of all sorts of things
2301 	AddPeriodStrategicEvent(EVENT_HOURLY_UPDATE,       60, 0);
2302 	AddPeriodStrategicEvent(EVENT_QUARTER_HOUR_UPDATE, 15, 0);
2303 
2304 	// Clear any possible battle locator.
2305 	gfBlitBattleSectorLocator = FALSE;
2306 
2307 	StrategicTurnsNewGame();
2308 
2309 	// Move the landing zone over to the start sector.
2310 	g_merc_arrive_sector = START_SECTOR;
2311 }
2312 
2313 
CanGoToTacticalInSector(INT16 const x,INT16 const y,UINT8 const z)2314 bool CanGoToTacticalInSector(INT16 const x, INT16 const y, UINT8 const z)
2315 {
2316 	// If not a valid sector
2317 	if (x < 1 || 16 < x) return false;
2318 	if (y < 1 || 16 < x) return false;
2319 	if (          3 < z) return false;
2320 
2321 	/* Look for all living, fighting mercs on player's team. Robot and EPCs
2322 	 * qualify! */
2323 	CFOR_EACH_IN_TEAM(i, OUR_TEAM)
2324 	{
2325 		SOLDIERTYPE const& s = *i;
2326 		/* ARM: Now allows loading of sector with all mercs below OKLIFE as long as
2327 		 * they're alive */
2328 		if (s.bLife == 0)                      continue;
2329 		if (s.uiStatusFlags & SOLDIER_VEHICLE) continue;
2330 		if (s.bAssignment == IN_TRANSIT)       continue;
2331 		if (s.bAssignment == ASSIGNMENT_POW)   continue;
2332 		if (s.bAssignment == ASSIGNMENT_DEAD)  continue;
2333 		if (SoldierAboardAirborneHeli(s))      continue;
2334 		if (s.fBetweenSectors)                 continue;
2335 		if (s.sSectorX != x)                   continue;
2336 		if (s.sSectorY != y)                   continue;
2337 		if (s.bSectorZ != z)                   continue;
2338 		return true;
2339 	}
2340 
2341 	return false;
2342 }
2343 
HandleAirspaceControlUpdated()2344 static void HandleAirspaceControlUpdated()
2345 {
2346 	// check if currently selected arrival sector still has secure airspace
2347 
2348 	// if it's not enemy air controlled
2349 	if (StrategicMap[SECTOR_INFO_TO_STRATEGIC_INDEX(g_merc_arrive_sector)].fEnemyAirControlled)
2350 	{
2351 		// get the name of the old sector
2352 		ST::string sMsgSubString1 = GetSectorIDString(SECTORX(g_merc_arrive_sector), SECTORY(g_merc_arrive_sector), 0, FALSE);
2353 
2354 		// Move the landing zone over to the start sector.
2355 		g_merc_arrive_sector = START_SECTOR;
2356 
2357 		// get the name of the new sector
2358 		ST::string sMsgSubString2 = GetSectorIDString(SECTORX(g_merc_arrive_sector), SECTORY(g_merc_arrive_sector), 0, FALSE);
2359 
2360 		// now build the string
2361 		ST::string sMsgString = st_format_printf(pBullseyeStrings[ 4 ], sMsgSubString1, sMsgSubString2);
2362 
2363 		// confirm the change with overlay message
2364 		DoScreenIndependantMessageBox(sMsgString, MSG_BOX_FLAG_OK, NULL);
2365 
2366 		// update position of bullseye
2367 		fMapPanelDirty = TRUE;
2368 
2369 		// update destination column for any mercs in transit
2370 		fTeamPanelDirty = TRUE;
2371 	}
2372 
2373 
2374 	// ARM: airspace control now affects refueling site availability, so update that too with every change!
2375 	UpdateRefuelSiteAvailability();
2376 }
2377 
SaveStrategicInfoToSavedFile(HWFILE const f)2378 void SaveStrategicInfoToSavedFile(HWFILE const f)
2379 {
2380 	// Save the strategic map information
2381 	FOR_EACH(StrategicMapElement const, i, StrategicMap)
2382 	{
2383 		InjectStrategicMapElementIntoFile(f, *i);
2384 	}
2385 
2386 	// Save the Sector Info
2387 	FOR_EACH(SECTORINFO const, i, SectorInfo)
2388 	{
2389 		InjectSectorInfoIntoFile(f, *i);
2390 	}
2391 
2392 	// Skip the SAM controlled sector information
2393 	FileSeek(f, MAP_WORLD_X * MAP_WORLD_Y, FILE_SEEK_FROM_CURRENT);
2394 
2395 	// Save the state of the 2nd map secret (fFoundOrta)
2396 	BOOLEAN fFound = GetMapSecretStateForSave(1);
2397 	FileWrite(f, &fFound, sizeof(BOOLEAN));
2398 }
2399 
2400 
LoadStrategicInfoFromSavedFile(HWFILE const f)2401 void LoadStrategicInfoFromSavedFile(HWFILE const f)
2402 {
2403 	// Load the strategic map information
2404 	FOR_EACH (StrategicMapElement, i, StrategicMap)
2405 	{
2406 		ExtractStrategicMapElementFromFile(f, *i);
2407 	}
2408 
2409 	// Load the Sector Info
2410 	FOR_EACH(SECTORINFO, i, SectorInfo)
2411 	{
2412 		ExtractSectorInfoFromFile(f, *i);
2413 	}
2414 
2415 	// Skip the SAM controlled sector information
2416 	FileSeek(f, MAP_WORLD_X * MAP_WORLD_Y, FILE_SEEK_FROM_CURRENT);
2417 
2418 	// Load state of the 2nd map secret (fFoundOrta)
2419 	BOOLEAN fFound;
2420 	FileRead(f, &fFound, sizeof(BOOLEAN));
2421 	SetMapSecretStateFromSave(1, fFound);
2422 }
2423 
2424 
PickGridNoNearestEdge(SOLDIERTYPE * pSoldier,UINT8 ubTacticalDirection)2425 static INT16 PickGridNoNearestEdge(SOLDIERTYPE* pSoldier, UINT8 ubTacticalDirection)
2426 {
2427 	INT16  sGridNo, sStartGridNo, sOldGridNo;
2428 	INT8   bOdd = 1, bOdd2 = 1;
2429 	UINT8  bAdjustedDist = 0;
2430 	UINT32 cnt;
2431 
2432 	switch( ubTacticalDirection )
2433 	{
2434 
2435 		case EAST:
2436 
2437 			sGridNo      = pSoldier->sGridNo;
2438 			sStartGridNo = pSoldier->sGridNo;
2439 			sOldGridNo   = pSoldier->sGridNo;
2440 
2441 			// Move directly to the right!
2442 			while( GridNoOnVisibleWorldTile( sGridNo ) )
2443 			{
2444 				sOldGridNo = sGridNo;
2445 
2446 				if ( bOdd )
2447 				{
2448 					sGridNo -= WORLD_COLS;
2449 				}
2450 				else
2451 				{
2452 					sGridNo++;
2453 				}
2454 
2455 				bOdd = (INT8)!bOdd;
2456 			}
2457 
2458 			sGridNo      = sOldGridNo;
2459 			sStartGridNo = sOldGridNo;
2460 
2461 			do
2462 			{
2463 				// OK, here we go back one, check for OK destination...
2464 				if ( NewOKDestination( pSoldier, sGridNo, TRUE, pSoldier->bLevel ) && FindBestPath( pSoldier, sGridNo, pSoldier->bLevel, WALKING, NO_COPYROUTE, PATH_THROUGH_PEOPLE ) )
2465 				{
2466 					return( sGridNo );
2467 				}
2468 
2469 				// If here, try another place!
2470 				// ( alternate up/down )
2471 				if ( bOdd2 )
2472 				{
2473 					bAdjustedDist++;
2474 
2475 					sGridNo = sStartGridNo;
2476 
2477 					for ( cnt = 0; cnt < bAdjustedDist; cnt++ )
2478 					{
2479 						sGridNo = (INT16)(sGridNo - WORLD_COLS - 1);
2480 					}
2481 				}
2482 				else
2483 				{
2484 					sGridNo = sStartGridNo;
2485 
2486 					for ( cnt = 0; cnt < bAdjustedDist; cnt++ )
2487 					{
2488 						sGridNo = (INT16)(sGridNo + WORLD_COLS + 1);
2489 					}
2490 				}
2491 
2492 				bOdd2 = (INT8)(!bOdd2);
2493 
2494 			} while( TRUE );
2495 
2496 		case WEST:
2497 
2498 			sGridNo      = pSoldier->sGridNo;
2499 			sStartGridNo = pSoldier->sGridNo;
2500 			sOldGridNo   = pSoldier->sGridNo;
2501 
2502 			// Move directly to the left!
2503 			while( GridNoOnVisibleWorldTile( sGridNo ) )
2504 			{
2505 				sOldGridNo = sGridNo;
2506 
2507 				if ( bOdd )
2508 				{
2509 					sGridNo += WORLD_COLS;
2510 				}
2511 				else
2512 				{
2513 					sGridNo--;
2514 				}
2515 
2516 				bOdd = (INT8)!bOdd;
2517 			}
2518 
2519 			sGridNo      = sOldGridNo;
2520 			sStartGridNo = sOldGridNo;
2521 
2522 			do
2523 			{
2524 				// OK, here we go back one, check for OK destination...
2525 				if ( NewOKDestination( pSoldier, sGridNo, TRUE, pSoldier->bLevel ) && FindBestPath( pSoldier, sGridNo, pSoldier->bLevel, WALKING, NO_COPYROUTE, PATH_THROUGH_PEOPLE ) )
2526 				{
2527 					return( sGridNo );
2528 				}
2529 
2530 				// If here, try another place!
2531 				// ( alternate up/down )
2532 				if ( bOdd2 )
2533 				{
2534 					bAdjustedDist++;
2535 
2536 					sGridNo = sStartGridNo;
2537 
2538 					for ( cnt = 0; cnt < bAdjustedDist; cnt++ )
2539 					{
2540 						sGridNo = (INT16)(sGridNo - WORLD_COLS - 1);
2541 					}
2542 				}
2543 				else
2544 				{
2545 					sGridNo = sStartGridNo;
2546 
2547 					for ( cnt = 0; cnt < bAdjustedDist; cnt++ )
2548 					{
2549 						sGridNo = (INT16)(sGridNo + WORLD_COLS + 1);
2550 					}
2551 				}
2552 
2553 				bOdd2 = (INT8)(!bOdd2);
2554 
2555 			} while( TRUE );
2556 
2557 		case NORTH:
2558 
2559 			sGridNo      = pSoldier->sGridNo;
2560 			sStartGridNo = pSoldier->sGridNo;
2561 			sOldGridNo   = pSoldier->sGridNo;
2562 
2563 			// Move directly to the left!
2564 			while( GridNoOnVisibleWorldTile( sGridNo ) )
2565 			{
2566 				sOldGridNo = sGridNo;
2567 
2568 				if ( bOdd )
2569 				{
2570 					sGridNo -= WORLD_COLS;
2571 				}
2572 				else
2573 				{
2574 					sGridNo--;
2575 				}
2576 
2577 				bOdd = (INT8)(!bOdd);
2578 			}
2579 
2580 			sGridNo      = sOldGridNo;
2581 			sStartGridNo = sOldGridNo;
2582 
2583 			do
2584 			{
2585 				// OK, here we go back one, check for OK destination...
2586 				if ( NewOKDestination( pSoldier, sGridNo, TRUE, pSoldier->bLevel ) && FindBestPath( pSoldier, sGridNo, pSoldier->bLevel, WALKING, NO_COPYROUTE, PATH_THROUGH_PEOPLE ) )
2587 				{
2588 					return( sGridNo );
2589 				}
2590 
2591 				// If here, try another place!
2592 				// ( alternate left/right )
2593 				if ( bOdd2 )
2594 				{
2595 					bAdjustedDist++;
2596 
2597 					sGridNo = sStartGridNo;
2598 
2599 					for ( cnt = 0; cnt < bAdjustedDist; cnt++ )
2600 					{
2601 						sGridNo = (INT16)(sGridNo + WORLD_COLS - 1);
2602 					}
2603 				}
2604 				else
2605 				{
2606 					sGridNo = sStartGridNo;
2607 
2608 					for ( cnt = 0; cnt < bAdjustedDist; cnt++ )
2609 					{
2610 						sGridNo = (INT16)(sGridNo - WORLD_COLS + 1);
2611 					}
2612 				}
2613 
2614 				bOdd2 = (INT8)(!bOdd2);
2615 
2616 			} while( TRUE );
2617 
2618 		case SOUTH:
2619 
2620 			sGridNo      = pSoldier->sGridNo;
2621 			sStartGridNo = pSoldier->sGridNo;
2622 			sOldGridNo   = pSoldier->sGridNo;
2623 
2624 			// Move directly to the left!
2625 			while( GridNoOnVisibleWorldTile( sGridNo ) )
2626 			{
2627 				sOldGridNo = sGridNo;
2628 
2629 				if ( bOdd )
2630 				{
2631 					sGridNo += WORLD_COLS;
2632 				}
2633 				else
2634 				{
2635 					sGridNo++;
2636 				}
2637 
2638 				bOdd = (INT8)(!bOdd);
2639 			}
2640 
2641 			sGridNo      = sOldGridNo;
2642 			sStartGridNo = sOldGridNo;
2643 
2644 			do
2645 			{
2646 				// OK, here we go back one, check for OK destination...
2647 				if ( NewOKDestination( pSoldier, sGridNo, TRUE, pSoldier->bLevel ) && FindBestPath( pSoldier, sGridNo, pSoldier->bLevel, WALKING, NO_COPYROUTE, PATH_THROUGH_PEOPLE ) )
2648 				{
2649 					return( sGridNo );
2650 				}
2651 
2652 				// If here, try another place!
2653 				// ( alternate left/right )
2654 				if ( bOdd2 )
2655 				{
2656 					bAdjustedDist++;
2657 
2658 					sGridNo = sStartGridNo;
2659 
2660 					for ( cnt = 0; cnt < bAdjustedDist; cnt++ )
2661 					{
2662 						sGridNo = (INT16)(sGridNo + WORLD_COLS - 1);
2663 					}
2664 				}
2665 				else
2666 				{
2667 					sGridNo = sStartGridNo;
2668 
2669 					for ( cnt = 0; cnt < bAdjustedDist; cnt++ )
2670 					{
2671 						sGridNo = (INT16)(sGridNo - WORLD_COLS + 1);
2672 					}
2673 				}
2674 
2675 				bOdd2 = (INT8)(!bOdd2);
2676 
2677 			} while( TRUE );
2678 	}
2679 
2680 	return( NOWHERE );
2681 }
2682 
2683 
AdjustSoldierPathToGoOffEdge(SOLDIERTYPE * pSoldier,INT16 sEndGridNo,UINT8 ubTacticalDirection)2684 void AdjustSoldierPathToGoOffEdge( SOLDIERTYPE *pSoldier, INT16 sEndGridNo, UINT8 ubTacticalDirection )
2685 {
2686 	INT16 sNewGridNo, sTempGridNo;
2687 	INT32 iLoop;
2688 
2689 	// will this path segment actually take us to our desired destination in the first place?
2690 	if (pSoldier->ubPathDataSize + 2 > MAX_PATH_LIST_SIZE)
2691 	{
2692 
2693 		sTempGridNo = pSoldier->sGridNo;
2694 
2695 		for (iLoop = 0; iLoop < pSoldier->ubPathDataSize; iLoop++)
2696 		{
2697 			sTempGridNo += DirectionInc( pSoldier->ubPathingData[ iLoop ] );
2698 		}
2699 
2700 		if (sTempGridNo == sEndGridNo)
2701 		{
2702 			// we can make it, but there isn't enough path room for the two steps required.
2703 			// truncate our path so there's guaranteed the merc will have to generate another
2704 			// path later on...
2705 			pSoldier->ubPathDataSize -= 4;
2706 			return;
2707 		}
2708 		else
2709 		{
2710 			// can't even make it there with these 30 tiles of path, abort...
2711 			return;
2712 		}
2713 	}
2714 
2715 	switch( ubTacticalDirection )
2716 	{
2717 		case EAST:
2718 
2719 			sNewGridNo = NewGridNo( (UINT16)sEndGridNo, DirectionInc( NORTHEAST ) );
2720 
2721 			if ( OutOfBounds( sEndGridNo, sNewGridNo ) )
2722 			{
2723 				return;
2724 			}
2725 
2726 			pSoldier->ubPathingData[ pSoldier->ubPathDataSize ] = NORTHEAST;
2727 			pSoldier->ubPathDataSize++;
2728 			pSoldier->sFinalDestination = sNewGridNo;
2729 			pSoldier->usActionData = sNewGridNo;
2730 
2731 			sTempGridNo = NewGridNo( (UINT16)sNewGridNo, DirectionInc( NORTHEAST ) );
2732 
2733 			if ( OutOfBounds( sNewGridNo, sTempGridNo ) )
2734 			{
2735 				return;
2736 			}
2737 			sNewGridNo = sTempGridNo;
2738 
2739 			pSoldier->ubPathingData[ pSoldier->ubPathDataSize ] = NORTHEAST;
2740 			pSoldier->ubPathDataSize++;
2741 			pSoldier->sFinalDestination = sNewGridNo;
2742 			pSoldier->usActionData = sNewGridNo;
2743 
2744 			break;
2745 
2746 		case WEST:
2747 
2748 			sNewGridNo = NewGridNo( (UINT16)sEndGridNo, DirectionInc( SOUTHWEST ) );
2749 
2750 			if ( OutOfBounds( sEndGridNo, sNewGridNo ) )
2751 			{
2752 				return;
2753 			}
2754 
2755 			pSoldier->ubPathingData[ pSoldier->ubPathDataSize ] = SOUTHWEST;
2756 			pSoldier->ubPathDataSize++;
2757 			pSoldier->sFinalDestination = sNewGridNo;
2758 			pSoldier->usActionData = sNewGridNo;
2759 
2760 			sTempGridNo = NewGridNo( (UINT16)sNewGridNo, DirectionInc( SOUTHWEST ) );
2761 
2762 			if ( OutOfBounds( sNewGridNo, sTempGridNo ) )
2763 			{
2764 				return;
2765 			}
2766 			sNewGridNo = sTempGridNo;
2767 
2768 			pSoldier->ubPathingData[ pSoldier->ubPathDataSize ] = SOUTHWEST;
2769 			pSoldier->ubPathDataSize++;
2770 			pSoldier->sFinalDestination = sNewGridNo;
2771 			pSoldier->usActionData = sNewGridNo;
2772 			break;
2773 
2774 		case NORTH:
2775 
2776 			sNewGridNo = NewGridNo( (UINT16)sEndGridNo, (UINT16)DirectionInc( (UINT8)NORTHWEST ) );
2777 
2778 			if ( OutOfBounds( sEndGridNo, sNewGridNo ) )
2779 			{
2780 				return;
2781 			}
2782 
2783 			pSoldier->ubPathingData[ pSoldier->ubPathDataSize ] = NORTHWEST;
2784 			pSoldier->ubPathDataSize++;
2785 			pSoldier->sFinalDestination = sNewGridNo;
2786 			pSoldier->usActionData = sNewGridNo;
2787 
2788 			sTempGridNo = NewGridNo( (UINT16)sNewGridNo, DirectionInc( NORTHWEST ) );
2789 
2790 			if ( OutOfBounds( sNewGridNo, sTempGridNo ) )
2791 			{
2792 				return;
2793 			}
2794 			sNewGridNo = sTempGridNo;
2795 
2796 			pSoldier->ubPathingData[ pSoldier->ubPathDataSize ] = NORTHWEST;
2797 			pSoldier->ubPathDataSize++;
2798 			pSoldier->sFinalDestination = sNewGridNo;
2799 			pSoldier->usActionData = sNewGridNo;
2800 
2801 			break;
2802 
2803 		case SOUTH:
2804 
2805 			sNewGridNo = NewGridNo( (UINT16)sEndGridNo, DirectionInc( SOUTHEAST ) );
2806 
2807 			if ( OutOfBounds( sEndGridNo, sNewGridNo ) )
2808 			{
2809 				return;
2810 			}
2811 
2812 			pSoldier->ubPathingData[ pSoldier->ubPathDataSize ] = SOUTHEAST;
2813 			pSoldier->ubPathDataSize++;
2814 			pSoldier->sFinalDestination = sNewGridNo;
2815 			pSoldier->usActionData = sNewGridNo;
2816 
2817 			sTempGridNo = NewGridNo( (UINT16)sNewGridNo, DirectionInc( SOUTHEAST ) );
2818 
2819 			if ( OutOfBounds( sNewGridNo, sTempGridNo ) )
2820 			{
2821 				return;
2822 			}
2823 			sNewGridNo = sTempGridNo;
2824 
2825 			pSoldier->ubPathingData[ pSoldier->ubPathDataSize ] = SOUTHEAST;
2826 			pSoldier->ubPathDataSize++;
2827 			pSoldier->sFinalDestination = sNewGridNo;
2828 			pSoldier->usActionData = sNewGridNo;
2829 			break;
2830 
2831 	}
2832 }
2833 
2834 
PickGridNoToWalkIn(SOLDIERTYPE * pSoldier,UINT8 ubInsertionDirection,UINT32 * puiNumAttempts)2835 static INT16 PickGridNoToWalkIn(SOLDIERTYPE* pSoldier, UINT8 ubInsertionDirection, UINT32* puiNumAttempts)
2836 {
2837 	INT16  sGridNo, sStartGridNo, sOldGridNo;
2838 	INT8   bOdd = 1, bOdd2 = 1;
2839 	UINT8  bAdjustedDist = 0;
2840 	UINT32 cnt;
2841 
2842 	*puiNumAttempts = 0;
2843 
2844 	switch( ubInsertionDirection )
2845 	{
2846 		// OK, we're given a direction on visible map, let's look for the first oone
2847 		// we find that is just on the start of visible map...
2848 		case INSERTION_CODE_WEST:
2849 
2850 			sGridNo      = (INT16)pSoldier->sGridNo;
2851 			sStartGridNo = (INT16)pSoldier->sGridNo;
2852 			sOldGridNo   = (INT16)pSoldier->sGridNo;
2853 
2854 			// Move directly to the left!
2855 			while( GridNoOnVisibleWorldTile( sGridNo ) )
2856 			{
2857 				sOldGridNo = sGridNo;
2858 
2859 				if ( bOdd )
2860 				{
2861 					sGridNo += WORLD_COLS;
2862 				}
2863 				else
2864 				{
2865 					sGridNo--;
2866 				}
2867 
2868 				bOdd = (INT8)(!bOdd);
2869 			}
2870 
2871 			sGridNo      = sOldGridNo;
2872 			sStartGridNo = sOldGridNo;
2873 
2874 			while( *puiNumAttempts < MAX_ATTEMPTS )
2875 			{
2876 				(*puiNumAttempts)++;
2877 				// OK, here we go back one, check for OK destination...
2878 				if ( ( gTacticalStatus.uiFlags & IGNORE_ALL_OBSTACLES ) || ( NewOKDestination( pSoldier, sGridNo, TRUE, pSoldier->bLevel ) && FindBestPath( pSoldier, sGridNo, pSoldier->bLevel, WALKING, NO_COPYROUTE, PATH_THROUGH_PEOPLE ) ) )
2879 				{
2880 					return( sGridNo );
2881 				}
2882 
2883 				// If here, try another place!
2884 				// ( alternate up/down )
2885 				if ( bOdd2 )
2886 				{
2887 					bAdjustedDist++;
2888 
2889 					sGridNo = sStartGridNo;
2890 
2891 					for ( cnt = 0; cnt < bAdjustedDist; cnt++ )
2892 					{
2893 						sGridNo = (INT16)(sGridNo - WORLD_COLS - 1);
2894 					}
2895 				}
2896 				else
2897 				{
2898 					sGridNo = sStartGridNo;
2899 
2900 					for ( cnt = 0; cnt < bAdjustedDist; cnt++ )
2901 					{
2902 						sGridNo = (INT16)(sGridNo + WORLD_COLS + 1);
2903 					}
2904 				}
2905 
2906 				bOdd2 = (INT8)(!bOdd2);
2907 
2908 			}
2909 			return NOWHERE;
2910 
2911 		case INSERTION_CODE_EAST:
2912 
2913 			sGridNo      = (INT16)pSoldier->sGridNo;
2914 			sStartGridNo = (INT16)pSoldier->sGridNo;
2915 			sOldGridNo   = (INT16)pSoldier->sGridNo;
2916 
2917 			// Move directly to the right!
2918 			while( GridNoOnVisibleWorldTile( sGridNo ) )
2919 			{
2920 				sOldGridNo = sGridNo;
2921 
2922 				if ( bOdd )
2923 				{
2924 					sGridNo -= WORLD_COLS;
2925 				}
2926 				else
2927 				{
2928 					sGridNo++;
2929 				}
2930 
2931 				bOdd = (INT8)(!bOdd);
2932 			}
2933 
2934 			sGridNo      = sOldGridNo;
2935 			sStartGridNo = sOldGridNo;
2936 
2937 			while( *puiNumAttempts < MAX_ATTEMPTS )
2938 			{
2939 				(*puiNumAttempts)++;
2940 				// OK, here we go back one, check for OK destination...
2941 				if ( ( gTacticalStatus.uiFlags & IGNORE_ALL_OBSTACLES ) || ( NewOKDestination( pSoldier, sGridNo, TRUE, pSoldier->bLevel ) && FindBestPath( pSoldier, sGridNo, pSoldier->bLevel, WALKING, NO_COPYROUTE, PATH_THROUGH_PEOPLE ) ) )
2942 				{
2943 					return( sGridNo );
2944 				}
2945 
2946 				// If here, try another place!
2947 				// ( alternate up/down )
2948 				if ( bOdd2 )
2949 				{
2950 					bAdjustedDist++;
2951 
2952 					sGridNo = sStartGridNo;
2953 
2954 					for ( cnt = 0; cnt < bAdjustedDist; cnt++ )
2955 					{
2956 						sGridNo = (INT16)(sGridNo - WORLD_COLS - 1);
2957 					}
2958 				}
2959 				else
2960 				{
2961 					sGridNo = sStartGridNo;
2962 
2963 					for ( cnt = 0; cnt < bAdjustedDist; cnt++ )
2964 					{
2965 						sGridNo = (INT16)(sGridNo + WORLD_COLS + 1);
2966 					}
2967 				}
2968 
2969 				bOdd2 = (INT8)(!bOdd2);
2970 
2971 			}
2972 			return NOWHERE;
2973 
2974 		case INSERTION_CODE_NORTH:
2975 
2976 			sGridNo        = (INT16)pSoldier->sGridNo;
2977 			sStartGridNo   = (INT16)pSoldier->sGridNo;
2978 			sOldGridNo     = (INT16)pSoldier->sGridNo;
2979 
2980 			// Move directly to the up!
2981 			while( GridNoOnVisibleWorldTile( sGridNo ) )
2982 			{
2983 				sOldGridNo = sGridNo;
2984 
2985 				if ( bOdd )
2986 				{
2987 					sGridNo -= WORLD_COLS;
2988 				}
2989 				else
2990 				{
2991 					sGridNo--;
2992 				}
2993 
2994 				bOdd = (INT8)(!bOdd);
2995 			}
2996 
2997 			sGridNo      = sOldGridNo;
2998 			sStartGridNo = sOldGridNo;
2999 
3000 			while( *puiNumAttempts < MAX_ATTEMPTS )
3001 			{
3002 				(*puiNumAttempts)++;
3003 				// OK, here we go back one, check for OK destination...
3004 				if ( ( gTacticalStatus.uiFlags & IGNORE_ALL_OBSTACLES ) || ( NewOKDestination( pSoldier, sGridNo, TRUE, pSoldier->bLevel ) && FindBestPath( pSoldier, sGridNo, pSoldier->bLevel, WALKING, NO_COPYROUTE, PATH_THROUGH_PEOPLE ) ) )
3005 				{
3006 					return( sGridNo );
3007 				}
3008 
3009 				// If here, try another place!
3010 				// ( alternate left/right )
3011 				if ( bOdd2 )
3012 				{
3013 					bAdjustedDist++;
3014 
3015 					sGridNo = sStartGridNo;
3016 
3017 					for ( cnt = 0; cnt < bAdjustedDist; cnt++ )
3018 					{
3019 						sGridNo = (INT16)(sGridNo - WORLD_COLS + 1);
3020 					}
3021 				}
3022 				else
3023 				{
3024 					sGridNo = sStartGridNo;
3025 
3026 					for ( cnt = 0; cnt < bAdjustedDist; cnt++ )
3027 					{
3028 						sGridNo = (INT16)(sGridNo + WORLD_COLS - 1);
3029 					}
3030 				}
3031 
3032 				bOdd2 = (INT8)(!bOdd2);
3033 
3034 			}
3035 			return NOWHERE;
3036 
3037 		case INSERTION_CODE_SOUTH:
3038 
3039 			sGridNo       = (INT16)pSoldier->sGridNo;
3040 			sStartGridNo  = (INT16)pSoldier->sGridNo;
3041 			sOldGridNo    = (INT16)pSoldier->sGridNo;
3042 
3043 			// Move directly to the down!
3044 			while( GridNoOnVisibleWorldTile( sGridNo ) )
3045 			{
3046 				sOldGridNo = sGridNo;
3047 
3048 				if ( bOdd )
3049 				{
3050 					sGridNo += WORLD_COLS;
3051 				}
3052 				else
3053 				{
3054 					sGridNo++;
3055 				}
3056 
3057 				bOdd = (INT8)(!bOdd);
3058 			}
3059 
3060 			sGridNo      = sOldGridNo;
3061 			sStartGridNo = sOldGridNo;
3062 
3063 			while( *puiNumAttempts < MAX_ATTEMPTS )
3064 			{
3065 				(*puiNumAttempts)++;
3066 				// OK, here we go back one, check for OK destination...
3067 				if ( ( gTacticalStatus.uiFlags & IGNORE_ALL_OBSTACLES ) || ( NewOKDestination( pSoldier, sGridNo, TRUE, pSoldier->bLevel ) && FindBestPath( pSoldier, sGridNo, pSoldier->bLevel, WALKING, NO_COPYROUTE, PATH_THROUGH_PEOPLE ) ) )
3068 				{
3069 					return( sGridNo );
3070 				}
3071 
3072 				// If here, try another place!
3073 				// ( alternate left/right )
3074 				if ( bOdd2 )
3075 				{
3076 					bAdjustedDist++;
3077 
3078 					sGridNo = sStartGridNo;
3079 
3080 					for ( cnt = 0; cnt < bAdjustedDist; cnt++ )
3081 					{
3082 						sGridNo = (INT16)(sGridNo - WORLD_COLS + 1);
3083 					}
3084 				}
3085 				else
3086 				{
3087 					sGridNo = sStartGridNo;
3088 
3089 					for ( cnt = 0; cnt < bAdjustedDist; cnt++ )
3090 					{
3091 						sGridNo = (INT16)(sGridNo + WORLD_COLS - 1);
3092 					}
3093 				}
3094 
3095 				bOdd2 = (INT8)(!bOdd2);
3096 
3097 			}
3098 			return NOWHERE;
3099 
3100 	}
3101 
3102 	//Unhandled exit
3103 	*puiNumAttempts = 0;
3104 
3105 	return( NOWHERE );
3106 }
3107 
3108 
3109 //NEW!
3110 //Calculates the name of the sector based on the loaded sector values.
3111 //Examples:		A9
3112 //						A10_B1
3113 //						J9_B2_A ( >= BETAVERSION ) else J9_B2 (release equivalent)
GetLoadedSectorString()3114 static ST::string GetLoadedSectorString()
3115 {
3116 	if (!gfWorldLoaded)
3117 	{
3118 		return ST::null;
3119 	}
3120 	else if (gbWorldSectorZ == 0)
3121 	{
3122 		return ST::format("{c}{}", gWorldSectorY + 'A' - 1, gWorldSectorX);
3123 	}
3124 	else
3125 	{
3126 		return ST::format("{c}{}_b{}", gWorldSectorY + 'A' - 1, gWorldSectorX, gbWorldSectorZ);
3127 	}
3128 }
3129 
3130 
HandleSlayDailyEvent(void)3131 void HandleSlayDailyEvent( void )
3132 {
3133 	SOLDIERTYPE* const pSoldier = FindSoldierByProfileIDOnPlayerTeam(SLAY);
3134 	if( pSoldier == NULL )
3135 	{
3136 		return;
3137 	}
3138 
3139 	// valid soldier?
3140 	if (pSoldier->bLife == 0 || pSoldier->bAssignment == IN_TRANSIT || pSoldier->bAssignment == ASSIGNMENT_POW)
3141 	{
3142 		// no
3143 		return;
3144 	}
3145 
3146 	// ATE: This function is used to check for the ultimate last day SLAY can stay for
3147 	// he may decide to leave randomly while asleep...
3148 	//if the user hasnt renewed yet, and is still leaving today
3149 	if( ( pSoldier->iEndofContractTime /1440 ) <= (INT32)GetWorldDay( ) )
3150 	{
3151 		pSoldier->ubLeaveHistoryCode = HISTORY_SLAY_MYSTERIOUSLY_LEFT;
3152 		MakeCharacterDialogueEventContractEndingNoAskEquip(*pSoldier);
3153 	}
3154 }
3155 
3156 
IsSectorDesert(INT16 const x,INT16 const y)3157 bool IsSectorDesert(INT16 const x, INT16 const y)
3158 {
3159 	return SectorInfo[SECTOR(x, y)].ubTraversability[THROUGH_STRATEGIC_MOVE] == SAND;
3160 }
3161 
3162 
HandleDefiniteUnloadingOfWorld(UINT8 const ubUnloadCode)3163 static void HandleDefiniteUnloadingOfWorld(UINT8 const ubUnloadCode)
3164 {
3165 	// clear tactical queue
3166 	ClearEventQueue();
3167 
3168 	// ATE: End all bullets....
3169 	DeleteAllBullets();
3170 
3171 	// End all physics objects...
3172 	RemoveAllPhysicsObjects();
3173 
3174 	RemoveAllActiveTimedBombs();
3175 
3176 	// handle any quest stuff here so world items can be affected
3177 	HandleQuestCodeOnSectorExit( gWorldSectorX, gWorldSectorY, gbWorldSectorZ );
3178 
3179 	//if we arent loading a saved game
3180 	if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
3181 	{
3182 		//Clear any potential battle flags.  They will be set if necessary.
3183 		gTacticalStatus.fEnemyInSector = FALSE;
3184 		gTacticalStatus.uiFlags &= ~INCOMBAT;
3185 	}
3186 
3187 	if ( ubUnloadCode == ABOUT_TO_LOAD_NEW_MAP )
3188 	{
3189 		//if we arent loading a saved game
3190 		if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
3191 		{
3192 
3193 			// Save the current sectors Item list to a temporary file, if its not the first time in
3194 			SaveCurrentSectorsInformationToTempItemFile();
3195 
3196 			// Update any mercs currently in sector, their profile info...
3197 			UpdateSoldierPointerDataIntoProfile();
3198 		}
3199 	}
3200 	else if( ubUnloadCode == ABOUT_TO_TRASH_WORLD )
3201 	{
3202 		//Save the current sectors open temp files to the disk
3203 		SaveCurrentSectorsInformationToTempItemFile();
3204 
3205 		//Setup the tactical existance of the current soldier.
3206 		//@@@Evaluate
3207 		SetupProfileInsertionDataForCivilians();
3208 
3209 		gfBlitBattleSectorLocator = FALSE;
3210 	}
3211 
3212 	//Handle cases for both types of unloading
3213 	HandleMilitiaStatusInCurrentMapBeforeLoadingNewMap();
3214 }
3215 
3216 
HandlePotentialBringUpAutoresolveToFinishBattle()3217 BOOLEAN HandlePotentialBringUpAutoresolveToFinishBattle( )
3218 {
3219 	INT32 i;
3220 
3221 	//We don't have mercs in the sector.  Now, we check to see if there are BOTH enemies and militia.  If both
3222 	//co-exist in the sector, then make them fight for control of the sector via autoresolve.
3223 	for( i = gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID; i <= gTacticalStatus.Team[ CREATURE_TEAM ].bLastID; i++ )
3224 	{
3225 		SOLDIERTYPE const& creature = GetMan(i);
3226 		if (creature.bActive &&
3227 				creature.bLife != 0 &&
3228 				creature.sSectorX == gWorldSectorX &&
3229 				creature.sSectorY == gWorldSectorY &&
3230 				creature.bSectorZ == gbWorldSectorZ)
3231 		{ //We have enemies, now look for militia!
3232 			for( i = gTacticalStatus.Team[ MILITIA_TEAM ].bFirstID; i <= gTacticalStatus.Team[ MILITIA_TEAM ].bLastID; i++ )
3233 			{
3234 				SOLDIERTYPE const& milita = GetMan(i);
3235 				if (milita.bActive &&
3236 						milita.bLife != 0 &&
3237 						milita.bSide    == OUR_TEAM &&
3238 						milita.sSectorX == gWorldSectorX &&
3239 						milita.sSectorY == gWorldSectorY &&
3240 						milita.bSectorZ == gbWorldSectorZ)
3241 				{ //We have militia and enemies and no mercs!  Let's finish this battle in autoresolve.
3242 					gfEnteringMapScreen = TRUE;
3243 					gfEnteringMapScreenToEnterPreBattleInterface = TRUE;
3244 					gfAutomaticallyStartAutoResolve = TRUE;
3245 					gfUsePersistantPBI = FALSE;
3246 					gubPBSectorX = (UINT8)gWorldSectorX;
3247 					gubPBSectorY = (UINT8)gWorldSectorY;
3248 					gubPBSectorZ = (UINT8)gbWorldSectorZ;
3249 					gfBlitBattleSectorLocator = TRUE;
3250 					gfTransferTacticalOppositionToAutoResolve = TRUE;
3251 					if( gubEnemyEncounterCode != CREATURE_ATTACK_CODE )
3252 					{
3253 						gubEnemyEncounterCode = ENEMY_INVASION_CODE; //has to be, if militia are here.
3254 					}
3255 					else
3256 					{
3257 						//DoScreenIndependantMessageBox(gzLateLocalizedString[STR_LATE_39], MSG_BOX_FLAG_OK, MapScreenDefaultOkBoxCallback);
3258 					}
3259 
3260 					return( TRUE );
3261 				}
3262 			}
3263 		}
3264 	}
3265 
3266 	return( FALSE );
3267 }
3268 
3269 
CheckAndHandleUnloadingOfCurrentWorld()3270 BOOLEAN CheckAndHandleUnloadingOfCurrentWorld()
3271 try
3272 {
3273 	INT16 sBattleSectorX, sBattleSectorY, sBattleSectorZ;
3274 
3275 	//Don't bother checking this if we don't have a world loaded.
3276 	if( !gfWorldLoaded )
3277 	{
3278 		return FALSE;
3279 	}
3280 
3281 	if (DidGameJustStart() && SECTOR(gWorldSectorX, gWorldSectorY) == START_SECTOR && gbWorldSectorZ == 0)
3282 	{
3283 		return FALSE;
3284 	}
3285 
3286 	GetCurrentBattleSectorXYZ( &sBattleSectorX, &sBattleSectorY, &sBattleSectorZ );
3287 
3288 	if( guiCurrentScreen == AUTORESOLVE_SCREEN )
3289 	{ //The user has decided to let the game autoresolve the current battle.
3290 		if( gWorldSectorX == sBattleSectorX && gWorldSectorY == sBattleSectorY && gbWorldSectorZ == sBattleSectorZ )
3291 		{
3292 			FOR_EACH_IN_TEAM(i, OUR_TEAM)
3293 			{ //If we have a live and valid soldier
3294 				SOLDIERTYPE& s = *i;
3295 				if (s.bLife == 0)                 continue;
3296 				if (s.fBetweenSectors)            continue;
3297 				if (IsMechanical(s))              continue;
3298 				if (AM_AN_EPC(&s))                continue;
3299 				if (s.sSectorX != gWorldSectorX)  continue;
3300 				if (s.sSectorY != gWorldSectorY)  continue;
3301 				if (s.bSectorZ != gbWorldSectorZ) continue;
3302 				RemoveSoldierFromGridNo(s);
3303 				InitSoldierOppList(s);
3304 			}
3305 		}
3306 	}
3307 	else
3308 	{	//Check and see if we have any live mercs in the sector.
3309 		CFOR_EACH_IN_TEAM(s, OUR_TEAM)
3310 		{ //If we have a live and valid soldier
3311 			if (s->bLife != 0 &&
3312 					!s->fBetweenSectors &&
3313 					!IsMechanical(*s) &&
3314 					!AM_AN_EPC(s) &&
3315 					s->sSectorX == gWorldSectorX &&
3316 					s->sSectorY == gWorldSectorY &&
3317 					s->bSectorZ == gbWorldSectorZ)
3318 			{
3319 				return FALSE;
3320 			}
3321 		}
3322 		//KM : August 6, 1999 Patch fix
3323 		//     Added logic to prevent a crash when player mercs would retreat from a battle involving militia and enemies.
3324 		//     Without the return here, it would proceed to trash the world, and then when autoresolve would come up to
3325 		//     finish the tactical battle, it would fail to find the existing soldier information (because it was trashed).
3326 		if( HandlePotentialBringUpAutoresolveToFinishBattle( ) )
3327 		{
3328 			return FALSE;
3329 		}
3330 		//end
3331 
3332 		//HandlePotentialBringUpAutoresolveToFinishBattle( ); //prior patch logic
3333 	}
3334 
3335 
3336 	CheckForEndOfCombatMode( FALSE );
3337 	EndTacticalBattleForEnemy();
3338 
3339 	// ATE: Change cursor to wait cursor for duration of frame.....
3340 	// save old cursor ID....
3341 	SetCurrentCursorFromDatabase( CURSOR_WAIT_NODELAY );
3342 	RefreshScreen();
3343 
3344 	// JA2Gold: Leaving sector, so get rid of ambients!
3345 	DeleteAllAmbients();
3346 
3347 	if( guiCurrentScreen == GAME_SCREEN )
3348 	{
3349 		if( !gfTacticalTraversal )
3350 		{ //if we are in tactical and don't intend on going to another sector immediately, then
3351 			gfEnteringMapScreen = TRUE;
3352 		}
3353 		else
3354 		{ //The trashing of the world will be handled automatically.
3355 			return FALSE;
3356 		}
3357 	}
3358 
3359 	//We have passed all the checks and can Trash the world.
3360 	HandleDefiniteUnloadingOfWorld(ABOUT_TO_TRASH_WORLD);
3361 
3362 	if( guiCurrentScreen == AUTORESOLVE_SCREEN )
3363 	{
3364 		if( gWorldSectorX == sBattleSectorX && gWorldSectorY == sBattleSectorY && gbWorldSectorZ == sBattleSectorZ )
3365 		{
3366 			/* Yes, this is and looks like a hack.  The conditions of this if
3367 			 * statement doesn't work inside TrashWorld() or more specifically,
3368 			 * TacticalRemoveSoldier() from within TrashWorld().  Because we are in
3369 			 * the autoresolve screen, soldiers are internally created different (from
3370 			 * pointers instead of Menptr[]).  It keys on the fact that we are in the
3371 			 * autoresolve screen.  So, by switching the screen, it'll delete the
3372 			 * soldiers in the loaded world properly, then later on, once autoresolve
3373 			 * is complete, it'll delete the autoresolve soldiers properly.  As you
3374 			 * can now see, the above if conditions don't change throughout this whole
3375 			 * process which makes it necessary to do it this way. */
3376 			guiCurrentScreen = MAP_SCREEN;
3377 			TrashWorld();
3378 			guiCurrentScreen = AUTORESOLVE_SCREEN;
3379 		}
3380 	}
3381 	else
3382 	{
3383 		TrashWorld();
3384 	}
3385 
3386 	//Clear all combat related flags.
3387 	gTacticalStatus.fEnemyInSector = FALSE;
3388 	gTacticalStatus.uiFlags &= ~INCOMBAT;
3389 	EndTopMessage( );
3390 
3391 
3392 	//Clear the world sector values.
3393 	SetWorldSectorInvalid();
3394 
3395 	//Clear the flags regarding.
3396 	gfCaves = FALSE;
3397 	gfBasement = FALSE;
3398 
3399 	return TRUE;
3400 }
3401 catch (...) { return FALSE; }
3402 
3403 
3404 /* This is called just before the world is unloaded to preserve location
3405  * information for RPCs and NPCs either in the sector or strategically in the
3406  * sector (such as firing an NPC in a sector that isn't yet loaded.)  When
3407  * loading that sector, the RPC would be added. */
3408 //@@@Evaluate
SetupProfileInsertionDataForSoldier(const SOLDIERTYPE * const s)3409 void SetupProfileInsertionDataForSoldier(const SOLDIERTYPE* const s)
3410 {
3411 	if (s->ubProfile == NO_PROFILE) return;
3412 	MERCPROFILESTRUCT& p = GetProfile(s->ubProfile);
3413 
3414 	// can't be changed?
3415 	if (p.ubMiscFlags3 & PROFILE_MISC_FLAG3_PERMANENT_INSERTION_CODE) return;
3416 
3417 	if (gfWorldLoaded && s->bActive && s->bInSector)
3418 	{
3419 		// This soldier is currently in the sector
3420 
3421 		//@@@Evaluate -- insert code here
3422 		//SAMPLE CODE:  There are multiple situations that I didn't code.  The gridno should be the final destination
3423 		//or reset???
3424 
3425 		if (s->ubQuoteRecord && s->ubQuoteActionID)
3426 		{
3427 			// if moving to traverse
3428 			if (s->ubQuoteActionID >= QUOTE_ACTION_ID_TRAVERSE_EAST && s->ubQuoteActionID <= QUOTE_ACTION_ID_TRAVERSE_NORTH)
3429 			{
3430 				// Handle traversal.  This NPC's sector will NOT already be set correctly, so we have to call for that too
3431 				HandleNPCChangesForTacticalTraversal(s);
3432 				p.fUseProfileInsertionInfo = FALSE;
3433 				if (s->ubProfile != NO_PROFILE && NPCHasUnusedRecordWithGivenApproach(s->ubProfile, APPROACH_DONE_TRAVERSAL))
3434 				{
3435 					p.ubMiscFlags3 |= PROFILE_MISC_FLAG3_HANDLE_DONE_TRAVERSAL;
3436 				}
3437 			}
3438 			else
3439 			{
3440 				if (s->sFinalDestination == s->sGridNo)
3441 				{
3442 					p.usStrategicInsertionData = s->sGridNo;
3443 				}
3444 				else if (s->sAbsoluteFinalDestination != NOWHERE)
3445 				{
3446 					p.usStrategicInsertionData = s->sAbsoluteFinalDestination;
3447 				}
3448 				else
3449 				{
3450 					p.usStrategicInsertionData = s->sFinalDestination;
3451 				}
3452 
3453 				p.fUseProfileInsertionInfo = TRUE;
3454 				p.ubStrategicInsertionCode = INSERTION_CODE_GRIDNO;
3455 				p.ubQuoteActionID          = s->ubQuoteActionID;
3456 				p.ubQuoteRecord            = s->ubQuoteActionID;
3457 			}
3458 		}
3459 		else
3460 		{
3461 			p.fUseProfileInsertionInfo = FALSE;
3462 		}
3463 	}
3464 	else
3465 	{
3466 		//use strategic information
3467 		/* It appears to set the soldier's strategic insertion code everytime a
3468 		 * group arrives in a new sector.  The insertion data isn't needed for these
3469 		 * cases as the code is a direction only. */
3470 		p.ubStrategicInsertionCode = s->ubStrategicInsertionCode;
3471 		p.usStrategicInsertionData = 0;
3472 
3473 		//Strategic system should now work.
3474 		p.fUseProfileInsertionInfo = TRUE;
3475 	}
3476 }
3477 
3478 
HandlePotentialMoraleHitForSkimmingSectors(GROUP * pGroup)3479 static void HandlePotentialMoraleHitForSkimmingSectors(GROUP* pGroup)
3480 {
3481 	if ( !gTacticalStatus.fHasEnteredCombatModeSinceEntering && gTacticalStatus.fEnemyInSector )
3482 	{
3483 		//Flag is set so if "wilderness" enemies are in the adjacent sector of this group, the group has
3484 		//a 90% chance of ambush.  Because this typically doesn't happen very often, the chance is high.
3485 		//This reflects the enemies radioing ahead to other enemies of the group's arrival, so they have
3486 		//time to setup a good ambush!
3487 		pGroup->uiFlags |= GROUPFLAG_HIGH_POTENTIAL_FOR_AMBUSH;
3488 
3489 		CFOR_EACH_PLAYER_IN_GROUP(pPlayer, pGroup)
3490 		{
3491 			// Do morale hit...
3492 			// CC look here!
3493 			// pPlayer->pSoldier
3494 		}
3495 	}
3496 }
3497 
3498 
GetWorldSector()3499 UINT GetWorldSector()
3500 {
3501 	if (gWorldSectorX == 0 || gWorldSectorY == 0) return NO_SECTOR;
3502 	return SECTOR(gWorldSectorX, gWorldSectorY);
3503 }
3504 
3505