1 #include "Meanwhile.h"
2 
3 #include "Assignments.h"
4 #include "Campaign_Types.h"
5 #include "ContentManager.h"
6 #include "ContentMusic.h"
7 #include "Dialogue_Control.h"
8 #include "Fade_Screen.h"
9 #include "GameInstance.h"
10 #include "GameSettings.h"
11 #include "Game_Clock.h"
12 #include "Game_Event_Hook.h"
13 #include "Game_Events.h"
14 #include "Interface.h"
15 #include "Interface_Control.h"
16 #include "Interface_Dialogue.h"
17 #include "Interface_Items.h"
18 #include "JAScreens.h"
19 #include "MapScreen.h"
20 #include "Map_Information.h"
21 #include "Map_Screen_Helicopter.h"
22 #include "Map_Screen_Interface.h"
23 #include "Map_Screen_Interface_Map.h"
24 #include "MessageBoxScreen.h"
25 #include "Music_Control.h"
26 #include "NPC.h"
27 #include "NpcPlacementModel.h"
28 #include "Overhead.h"
29 #include "PreBattle_Interface.h"
30 #include "Quests.h"
31 #include "Random.h"
32 #include "SamSiteModel.h"
33 #include "ScreenIDs.h"
34 #include "Soldier_Profile.h"
35 #include "Squads.h"
36 #include "StrategicMap.h"
37 #include "Strategic_AI.h"
38 #include "Tactical_Save.h"
39 #include "Text.h"
40 
41 #include <string_theory/format>
42 #include <string_theory/string>
43 
44 
45 #define MAX_MEANWHILE_PROFILES	10
46 
47 // the snap to grid nos for meanwhile scenes
48 static const UINT16 gusMeanWhileGridNo[] =
49 {
50 	12248,
51 	12248,
52 	12248,
53 	12248,
54 	12248,
55 	12248,
56 	12248,
57 	12248,
58 	12248,
59 	12248,
60 	12248,
61 	8075,
62 	12248,
63 	12248,
64 	12248,
65 	12248,
66 	12248,
67 };
68 
69 
70 struct NPC_SAVE_INFO
71 {
72 	UINT8 ubProfile;
73 	INT16 sX;
74 	INT16 sY;
75 	INT16 sZ;
76 	INT16 sGridNo;
77 };
78 
79 
80 // BEGIN SERALIZATION
81 MEANWHILE_DEFINITION gCurrentMeanwhileDef;
82 MEANWHILE_DEFINITION gMeanwhileDef[NUM_MEANWHILES];
83 BOOLEAN              gfMeanwhileTryingToStart = FALSE;
84 BOOLEAN              gfInMeanwhile = FALSE;
85 // END SERIALIZATION
86 static INT16 gsOldSectorX;
87 static INT16 gsOldSectorY;
88 static INT16 gsOldSectorZ;
89 static INT16 gsOldSelectedSectorX;
90 static INT16 gsOldSelectedSectorY;
91 static INT16 gsOldSelectedSectorZ;
92 
93 static UINT32        guiOldScreen;
94 static NPC_SAVE_INFO gNPCSaveData[MAX_MEANWHILE_PROFILES];
95 static UINT32        guiNumNPCSaves = 0;
96 static BOOLEAN       gfReloadingScreenFromMeanwhile = FALSE;
97 static BOOLEAN       gfWorldWasLoaded = FALSE;
98 static UINT8         ubCurrentMeanWhileId = 0;
99 
100 UINT32 uiMeanWhileFlags = 0;
101 
102 // meanwhile flag defines
103 #define END_OF_PLAYERS_FIRST_BATTLE_FLAG	0x00000001
104 #define DRASSEN_LIBERATED_FLAG			0x00000002
105 #define CAMBRIA_LIBERATED_FLAG			0x00000004
106 #define ALMA_LIBERATED_FLAG			0x00000008
107 #define GRUMM_LIBERATED_FLAG			0x00000010
108 #define CHITZENA_LIBERATED_FLAG			0x00000020
109 #define NW_SAM_FLAG				0x00000040
110 #define NE_SAM_FLAG				0x00000080
111 #define CENTRAL_SAM_FLAG			0x00000100
112 #define FLOWERS_FLAG				0x00000200
113 #define LOST_TOWN_FLAG				0x00000400
114 #define CREATURES_FLAG				0x00000800
115 #define KILL_CHOPPER_FLAG			0x00001000
116 #define AWOL_SCIENTIST_FLAG			0x00002000
117 #define OUTSKIRTS_MEDUNA_FLAG			0x00004000
118 #define INTERROGATION_FLAG			0x00008000
119 #define BALIME_LIBERATED_FLAG			0x00010000
120 
121 
MeanwhileIDToFlag(UINT8 const meanwhile_id)122 static UINT32 MeanwhileIDToFlag(UINT8 const meanwhile_id)
123 {
124 	switch (meanwhile_id)
125 	{
126 		case END_OF_PLAYERS_FIRST_BATTLE: return END_OF_PLAYERS_FIRST_BATTLE_FLAG;
127 		case DRASSEN_LIBERATED:           return DRASSEN_LIBERATED_FLAG;
128 		case CAMBRIA_LIBERATED:           return CAMBRIA_LIBERATED_FLAG;
129 		case ALMA_LIBERATED:              return ALMA_LIBERATED_FLAG;
130 		case GRUMM_LIBERATED:             return GRUMM_LIBERATED_FLAG;
131 		case CHITZENA_LIBERATED:          return CHITZENA_LIBERATED_FLAG;
132 		case BALIME_LIBERATED:            return BALIME_LIBERATED_FLAG;
133 		case NW_SAM:                      return NW_SAM_FLAG;
134 		case NE_SAM:                      return NE_SAM_FLAG;
135 		case CENTRAL_SAM:                 return CENTRAL_SAM_FLAG;
136 		case FLOWERS:                     return FLOWERS_FLAG;
137 		case LOST_TOWN:                   return LOST_TOWN_FLAG;
138 		case CREATURES:                   return CREATURES_FLAG;
139 		case KILL_CHOPPER:                return KILL_CHOPPER_FLAG;
140 		case AWOL_SCIENTIST:              return AWOL_SCIENTIST_FLAG;
141 		case OUTSKIRTS_MEDUNA:            return OUTSKIRTS_MEDUNA_FLAG;
142 		case INTERROGATION:               return INTERROGATION_FLAG;
143 		default:                          return 0;
144 	}
145 }
146 
147 
148 // set flag for this event
SetMeanWhileFlag(UINT8 const meanwhile_id)149 static void SetMeanWhileFlag(UINT8 const meanwhile_id)
150 {
151 	uiMeanWhileFlags |= MeanwhileIDToFlag(meanwhile_id);
152 }
153 
154 
155 // is this flag set?
GetMeanWhileFlag(UINT8 const meanwhile_id)156 static bool GetMeanWhileFlag(UINT8 const meanwhile_id)
157 {
158 	return uiMeanWhileFlags & MeanwhileIDToFlag(meanwhile_id);
159 }
160 
161 
GetFreeNPCSave(void)162 static NPC_SAVE_INFO* GetFreeNPCSave(void)
163 {
164 	for (NPC_SAVE_INFO* si = gNPCSaveData; si != gNPCSaveData + guiNumNPCSaves; ++si)
165 	{
166 		if (si->ubProfile == NO_PROFILE) return si;
167 	}
168 	if (guiNumNPCSaves < MAX_MEANWHILE_PROFILES)
169 	{
170 		return &gNPCSaveData[guiNumNPCSaves++];
171 	}
172 	return NULL;
173 }
174 
175 
ScheduleMeanwhileEvent(INT16 const x,INT16 const y,UINT16 const trigger_event,UINT8 const meanwhile_id,UINT8 const npc,UINT32 const time)176 void ScheduleMeanwhileEvent(INT16 const x, INT16 const y, UINT16 const trigger_event, UINT8 const meanwhile_id, UINT8 const npc, UINT32 const time)
177 {
178 	// event scheduled to happen before, ignore
179 	if (GetMeanWhileFlag(meanwhile_id)) return;
180 
181 	// set the meanwhile flag for this event
182 	SetMeanWhileFlag(meanwhile_id);
183 
184 	// set the id value
185 	ubCurrentMeanWhileId = meanwhile_id;
186 
187 	// Copy definiaiotn structure into position in global array....
188 	MEANWHILE_DEFINITION& m = gMeanwhileDef[meanwhile_id];
189 	m.sSectorX       = x;
190 	m.sSectorY       = y;
191 	m.usTriggerEvent = trigger_event;
192 	m.ubMeanwhileID  = meanwhile_id;
193 	m.ubNPCNumber    = npc;
194 
195 	// A meanwhile.. poor elliot!
196 	// increment his slapped count...
197 
198 	// We need to do it here 'cause they may skip it...
199 	if ( gMercProfiles[ ELLIOT ].bNPCData != 17 )
200 	{
201 		gMercProfiles[ ELLIOT ].bNPCData++;
202 	}
203 
204 	AddStrategicEvent(EVENT_MEANWHILE, time, meanwhile_id);
205 }
206 
207 
BeginMeanwhile(UINT8 ubMeanwhileID)208 void BeginMeanwhile(UINT8 ubMeanwhileID)
209 {
210 	INT32 cnt;
211 
212 	// Save if we are in Dead is Dead Mode
213 	DoDeadIsDeadSaveIfNecessary();
214 
215 	// copy meanwhile data from array to structure for current
216 	gCurrentMeanwhileDef = gMeanwhileDef[ubMeanwhileID];
217 
218 	gfMeanwhileTryingToStart = TRUE;
219 	PauseGame();
220 	// prevent anyone from messing with the pause!
221 	LockPauseState(LOCK_PAUSE_MEANWHILE);
222 
223 	// Set NO_PROFILE info....
224 	for ( cnt = 0; cnt < MAX_MEANWHILE_PROFILES; cnt++ )
225 	{
226 		gNPCSaveData[ cnt ].ubProfile = NO_PROFILE;
227 	}
228 }
229 
230 
231 static void BeginMeanwhileCallBack(MessageBoxReturnValue);
232 
233 
BringupMeanwhileBox(void)234 static void BringupMeanwhileBox(void)
235 {
236 	ST::string zStr = ST::format("{}.....", pMessageStrings[ MSG_MEANWHILE ]);
237 	MessageBoxFlags const flags =
238 		gCurrentMeanwhileDef.ubMeanwhileID != INTERROGATION
239 		&& MeanwhileSceneSeen(gCurrentMeanwhileDef.ubMeanwhileID)
240 		? MSG_BOX_FLAG_OKSKIP : MSG_BOX_FLAG_OK;
241 	DoMessageBox(MSG_BOX_BASIC_STYLE, zStr, guiCurrentScreen, flags, BeginMeanwhileCallBack, NULL);
242 }
243 
CheckForMeanwhileOKStart()244 void CheckForMeanwhileOKStart( )
245 {
246 	if ( gfMeanwhileTryingToStart )
247 	{
248 		// Are we in prebattle interface?
249 		if ( gfPreBattleInterfaceActive )
250 		{
251 			return;
252 		}
253 
254 		if ( !InterfaceOKForMeanwhilePopup() )
255 		{
256 			return;
257 		}
258 
259 		if (!DialogueQueueIsEmptyAndNobodyIsTalking())
260 		{
261 			return;
262 		}
263 
264 		gfMeanwhileTryingToStart = FALSE;
265 
266 		guiOldScreen = guiCurrentScreen;
267 
268 		if ( guiCurrentScreen == GAME_SCREEN )
269 		{
270 			LeaveTacticalScreen( GAME_SCREEN );
271 		}
272 
273 
274 
275 		// We need to make sure we have no item - at least in tactical
276 		// In mapscreen, time is paused when manipulating items...
277 		CancelItemPointer( );
278 
279 		BringupMeanwhileBox( );
280 	}
281 }
282 
283 
SetNPCMeanwhile(const ProfileID pid,const INT16 sector_x,const INT16 sector_y)284 static void SetNPCMeanwhile(const ProfileID pid, const INT16 sector_x, const INT16 sector_y)
285 {
286 	NPC_SAVE_INFO* const si = GetFreeNPCSave();
287 	if (si == NULL) return;
288 
289 	MERCPROFILESTRUCT& p = GetProfile(pid);
290 	si->ubProfile = pid;
291 	si->sX        = p.sSectorX;
292 	si->sY        = p.sSectorY;
293 	si->sZ        = p.bSectorZ;
294 	si->sGridNo   = p.sGridNo;
295 
296 	ReloadQuoteFile(pid);
297 	ChangeNpcToDifferentSector(p, sector_x, sector_y, 0);
298 }
299 
300 
301 static void DoneFadeOutMeanwhile(void);
302 
303 
StartMeanwhile(void)304 static void StartMeanwhile(void)
305 {
306 	// OK, save old position...
307 	if ( gfWorldLoaded )
308 	{
309 		gsOldSectorX = gWorldSectorX;
310 		gsOldSectorY = gWorldSectorY;
311 		gsOldSectorZ = gbWorldSectorZ;
312 	}
313 
314 	gsOldSelectedSectorX = sSelMapX;
315 	gsOldSelectedSectorY = sSelMapY;
316 	gsOldSelectedSectorZ = (INT16) iCurrentMapSectorZ;
317 
318 	gfInMeanwhile = TRUE;
319 
320 	// ATE: Change music before load
321 	SetMusicMode( MUSIC_MAIN_MENU );
322 
323 
324 	gfWorldWasLoaded = gfWorldLoaded;
325 
326 	// Setup NPC locations, depending on meanwhile type...
327 	switch( gCurrentMeanwhileDef.ubMeanwhileID )
328 	{
329 		case	END_OF_PLAYERS_FIRST_BATTLE:
330 		case	DRASSEN_LIBERATED:
331 		case	CAMBRIA_LIBERATED:
332 		case	ALMA_LIBERATED:
333 		case	GRUMM_LIBERATED:
334 		case	CHITZENA_LIBERATED:
335 		case  BALIME_LIBERATED:
336 		case	NW_SAM:
337 		case	NE_SAM:
338 		case	CENTRAL_SAM:
339 		case	FLOWERS:
340 		case	LOST_TOWN:
341 		case	CREATURES:
342 		case	KILL_CHOPPER:
343 		case	AWOL_SCIENTIST:
344 		case	OUTSKIRTS_MEDUNA:
345 			SetNPCMeanwhile(QUEEN,  3, 16);
346 			SetNPCMeanwhile(ELLIOT, 3, 16);
347 			if (gCurrentMeanwhileDef.ubMeanwhileID == OUTSKIRTS_MEDUNA)
348 			{
349 				SetNPCMeanwhile(JOE, 3, 16);
350 			}
351 			break;
352 
353 		case	INTERROGATION:
354 			SetNPCMeanwhile(QUEEN,  7, 14);
355 			SetNPCMeanwhile(ELLIOT, 7, 14);
356 			SetNPCMeanwhile(JOE,    7, 14);
357 			break;
358 	}
359 
360 	// fade out old screen....
361 	FadeOutNextFrame( );
362 
363 	// Load new map....
364 	gFadeOutDoneCallback = DoneFadeOutMeanwhile;
365 
366 
367 }
368 
369 
370 static void DoneFadeInMeanwhile(void);
371 static void LocateMeanWhileGrid(void);
372 
373 
DoneFadeOutMeanwhile(void)374 static void DoneFadeOutMeanwhile(void)
375 {
376 	// OK, insertion data found, enter sector!
377 
378 	SetCurrentWorldSector( gCurrentMeanwhileDef.sSectorX, gCurrentMeanwhileDef.sSectorY, 0 );
379 
380 	//LocateToMeanwhileCharacter( );
381 	LocateMeanWhileGrid( );
382 
383 	gFadeInDoneCallback = DoneFadeInMeanwhile;
384 
385 	FadeInNextFrame( );
386 }
387 
388 
DoneFadeInMeanwhile(void)389 static void DoneFadeInMeanwhile(void)
390 {
391 	// ATE: double check that we are in meanwhile
392 	// this is if we cancel right away.....
393 	if ( gfInMeanwhile )
394 	{
395 		giNPCReferenceCount = 1;
396 
397 		if ( gCurrentMeanwhileDef.ubMeanwhileID != INTERROGATION )
398 		{
399 			gTacticalStatus.uiFlags |= SHOW_ALL_MERCS;
400 		}
401 
402 		TriggerNPCRecordImmediately( gCurrentMeanwhileDef.ubNPCNumber, (UINT8)gCurrentMeanwhileDef.usTriggerEvent );
403 	}
404 }
405 
406 
407 static void ProcessImplicationsOfMeanwhile(void);
408 
409 
BeginMeanwhileCallBack(MessageBoxReturnValue const bExitValue)410 static void BeginMeanwhileCallBack(MessageBoxReturnValue const bExitValue)
411 {
412 	if ( bExitValue == MSG_BOX_RETURN_OK || bExitValue == MSG_BOX_RETURN_YES )
413 	{
414 		gTacticalStatus.uiFlags |= ENGAGED_IN_CONV;
415 		// Increment reference count...
416 		giNPCReferenceCount = 1;
417 
418 		StartMeanwhile( );
419 	}
420 	else
421 	{
422 		// skipped scene!
423 		gfInMeanwhile = FALSE;
424 		ProcessImplicationsOfMeanwhile();
425 		UnLockPauseState();
426 		UnPauseGame();
427 	}
428 }
429 
430 
AreInMeanwhile()431 bool AreInMeanwhile()
432 {
433 	/* KM: April 6, 1999
434 	 * Tactical traversal needs to take precedence over meanwhile events. When
435 	 * tactically traversing, we expect to make it to the other side without
436 	 * interruption. */
437 	if (gfTacticalTraversal) return false;
438 
439 	if (gfInMeanwhile) return true;
440 
441 	/* Check to make sure a meanwhile scene isn't in the event list occurring at
442 	 * the exact same time as this call. Meanwhile scenes have precedence over a
443 	 * new battle if they occur in the same second. */
444 	for (STRATEGICEVENT const* i = gpEventList; i; i = i->next)
445 	{
446 		if (i->uiTimeStamp != GetWorldTotalSeconds()) return false;
447 		if (i->ubCallbackID == EVENT_MEANWHILE)       return true;
448 	}
449 
450 	return false;
451 }
452 
453 
ProcessImplicationsOfMeanwhile()454 static void ProcessImplicationsOfMeanwhile()
455 {
456 	auto samList = GCM->getSamSites();
457 	switch( gCurrentMeanwhileDef.ubMeanwhileID )
458 	{
459 		case END_OF_PLAYERS_FIRST_BATTLE:
460 			if( gGameOptions.ubDifficultyLevel == DIF_LEVEL_HARD )
461 			{ //Wake up the queen earlier to punish the good players!
462 				ExecuteStrategicAIAction( STRATEGIC_AI_ACTION_WAKE_QUEEN, 0, 0 );
463 			}
464 			HandleNPCDoAction( QUEEN, NPC_ACTION_SEND_SOLDIERS_TO_BATTLE_LOCATION, 0 );
465 			break;
466 		case CAMBRIA_LIBERATED:
467 		case ALMA_LIBERATED:
468 		case GRUMM_LIBERATED:
469 		case CHITZENA_LIBERATED:
470 		case BALIME_LIBERATED:
471 			ExecuteStrategicAIAction( STRATEGIC_AI_ACTION_WAKE_QUEEN, 0, 0 );
472 			break;
473 		case DRASSEN_LIBERATED:
474 			ExecuteStrategicAIAction( STRATEGIC_AI_ACTION_WAKE_QUEEN, 0, 0 );
475 			HandleNPCDoAction( QUEEN, NPC_ACTION_SEND_SOLDIERS_TO_DRASSEN, 0 );
476 			break;
477 		case CREATURES:
478 			// add Rat
479 			HandleNPCDoAction( QUEEN, NPC_ACTION_ADD_RAT, 0 );
480 			break;
481 		case AWOL_SCIENTIST:
482 			{
483 				INT16	sSectorX = 0, sSectorY = 0;
484 
485 				StartQuest( QUEST_FIND_SCIENTIST, -1, -1 );
486 				// place Madlab and robot!
487 				auto placement = GCM->getNpcPlacement(MADLAB);
488 				for (UINT8 s : placement->sectorIds)
489 				{
490 					// the sector was already determined at game init, find it...
491 					if (SectorInfo[s].uiFlags & SF_USE_ALTERNATE_MAP)
492 					{
493 						sSectorX = SECTORX(s);
494 						sSectorY = SECTORY(s);
495 					}
496 				}
497 
498 				if (!sSectorX && !sSectorY)
499 				{
500 					SLOGE("unable to find Madlab's sector");
501 				}
502 				gMercProfiles[ MADLAB ].sSectorX = sSectorX;
503 				gMercProfiles[ MADLAB ].sSectorY = sSectorY;
504 				gMercProfiles[ MADLAB ].bSectorZ = 0;
505 
506 				gMercProfiles[ ROBOT ].sSectorX = sSectorX;
507 				gMercProfiles[ ROBOT ].sSectorY = sSectorY;
508 				gMercProfiles[ ROBOT ].bSectorZ = 0;
509 			}
510 			break;
511 
512 		{
513 			UINT8 sector;
514 		case NW_SAM:      sector = samList[SAM_SITE_ONE]->sectorId; goto send_troops_to_sam;
515 		case NE_SAM:      sector = samList[SAM_SITE_TWO]->sectorId; goto send_troops_to_sam;
516 		case CENTRAL_SAM: sector = samList[SAM_SITE_THREE]->sectorId; goto send_troops_to_sam;
517 send_troops_to_sam:
518 			ExecuteStrategicAIAction(NPC_ACTION_SEND_TROOPS_TO_SAM, SECTORX(sector), SECTORY(sector));
519 			break;
520 		}
521 
522 		default:
523 			break;
524 	}
525 }
526 
527 
RestoreNPCMeanwhile(void)528 static void RestoreNPCMeanwhile(void)
529 {
530 	// ATE: Restore people to saved positions...
531 	// OK, restore NPC save info...
532 	for (const NPC_SAVE_INFO* si = gNPCSaveData, *const end = gNPCSaveData + guiNumNPCSaves; si != end; ++si)
533 	{
534 		const ProfileID pid = si->ubProfile;
535 		if (pid == NO_PROFILE) continue;
536 
537 		MERCPROFILESTRUCT& p = GetProfile(pid);
538 		p.sSectorX = si->sX;
539 		p.sSectorY = si->sY;
540 		p.bSectorZ = (INT8)si->sZ;
541 		p.sGridNo  = (INT8)si->sGridNo;
542 
543 		// Ensure NPC files loaded...
544 		ReloadQuoteFile(pid);
545 	}
546 }
547 
548 
549 static void DoneFadeOutMeanwhileOnceDone(void);
550 
551 
EndMeanwhile()552 void EndMeanwhile( )
553 {
554 	EmptyDialogueQueue();
555 	ProcessImplicationsOfMeanwhile();
556 	SetMeanwhileSceneSeen( gCurrentMeanwhileDef.ubMeanwhileID );
557 
558 	gfInMeanwhile = FALSE;
559 	giNPCReferenceCount = 0;
560 
561 	gTacticalStatus.uiFlags &= (~ENGAGED_IN_CONV );
562 
563 	UnLockPauseState();
564 	UnPauseGame();
565 
566 	// ATE: Make sure!
567 	TurnOffSectorLocator();
568 
569 	if ( gCurrentMeanwhileDef.ubMeanwhileID != INTERROGATION )
570 	{
571 		gTacticalStatus.uiFlags &= (~SHOW_ALL_MERCS);
572 
573 		// OK, load old sector again.....
574 		FadeOutNextFrame( );
575 
576 		// Load new map....
577 		gFadeOutDoneCallback = DoneFadeOutMeanwhileOnceDone;
578 	}
579 	else
580 	{
581 		// We leave this sector open for our POWs to escape!
582 		// Set music mode to enemy present!
583 		SetMusicMode( MUSIC_TACTICAL_ENEMYPRESENT );
584 
585 		RestoreNPCMeanwhile();
586 	}
587 }
588 
589 
590 static void DoneFadeInMeanwhileOnceDone(void);
591 
592 
DoneFadeOutMeanwhileOnceDone(void)593 static void DoneFadeOutMeanwhileOnceDone(void)
594 {
595 	// OK, insertion data found, enter sector!
596 	gfReloadingScreenFromMeanwhile = TRUE;
597 
598 
599 	if( gfWorldWasLoaded )
600 	{
601 		SetCurrentWorldSector( gsOldSectorX, gsOldSectorY, (INT8)gsOldSectorZ );
602 
603 		ExamineCurrentSquadLights( );
604 	}
605 	else
606 	{
607 		TrashWorld( );
608 		// NB no world is loaded!
609 		SetWorldSectorInvalid();
610 	}
611 
612 	ChangeSelectedMapSector( gsOldSelectedSectorX, gsOldSelectedSectorY, (INT8) gsOldSelectedSectorZ );
613 
614 	gfReloadingScreenFromMeanwhile = FALSE;
615 
616 	RestoreNPCMeanwhile();
617 
618 	gFadeInDoneCallback = DoneFadeInMeanwhileOnceDone;
619 
620 	// OK, based on screen we were in....
621 	switch( guiOldScreen )
622 	{
623 		case MAP_SCREEN:
624 			InternalLeaveTacticalScreen( MAP_SCREEN );
625 			//gfEnteringMapScreen = TRUE;
626 			break;
627 
628 		case GAME_SCREEN:
629 			// restore old interface panel flag
630 			SetCurrentInterfacePanel(TEAM_PANEL);
631 			break;
632 	}
633 
634 	FadeInNextFrame( );
635 
636 }
637 
638 
DoneFadeInMeanwhileOnceDone(void)639 static void DoneFadeInMeanwhileOnceDone(void)
640 {
641 
642 }
643 
644 
LocateMeanWhileGrid(void)645 static void LocateMeanWhileGrid(void)
646 {
647 	INT16 sGridNo = 0;
648 
649 	// go to the approp. gridno
650 	sGridNo = gusMeanWhileGridNo[ ubCurrentMeanWhileId ];
651 
652 	InternalLocateGridNo( sGridNo, TRUE );
653 }
654 
LocateToMeanwhileCharacter()655 void LocateToMeanwhileCharacter( )
656 {
657 	if ( gfInMeanwhile )
658 	{
659 		SOLDIERTYPE* const pSoldier = FindSoldierByProfileID(gCurrentMeanwhileDef.ubNPCNumber);
660 		if (pSoldier != NULL) LocateSoldier(pSoldier, FALSE);
661 	}
662 }
663 
664 
AreReloadingFromMeanwhile()665 BOOLEAN AreReloadingFromMeanwhile( )
666 {
667 	return( gfReloadingScreenFromMeanwhile );
668 }
669 
GetMeanwhileID()670 UINT8 GetMeanwhileID( )
671 {
672 	return( gCurrentMeanwhileDef.ubMeanwhileID );
673 }
674 
675 
HandleCreatureRelease(void)676 void HandleCreatureRelease( void )
677 {
678 	UINT32 const uiTime = GetWorldTotalMin() + 5;
679 	ScheduleMeanwhileEvent(3, 16, 0, CREATURES, QUEEN, uiTime);
680 }
681 
682 
HandleMeanWhileEventPostingForTownLiberation(UINT8 bTownId)683 void HandleMeanWhileEventPostingForTownLiberation( UINT8 bTownId )
684 {
685 	UINT32 const uiTime = GetWorldTotalMin() + 5;
686 
687 	UINT8 ubId;
688 	switch (bTownId) // which town liberated?
689 	{
690 		case DRASSEN:  ubId = DRASSEN_LIBERATED;  break;
691 		case CAMBRIA:  ubId = CAMBRIA_LIBERATED;  break;
692 		case ALMA:     ubId = ALMA_LIBERATED;     break;
693 		case GRUMM:    ubId = GRUMM_LIBERATED;    break;
694 		case CHITZENA: ubId = CHITZENA_LIBERATED; break;
695 		case BALIME:   ubId = BALIME_LIBERATED;   break;
696 		default: return;
697 	}
698 	ScheduleMeanwhileEvent(3, 16, 0, ubId, QUEEN, uiTime);
699 }
700 
701 
HandleMeanWhileEventPostingForTownLoss()702 void HandleMeanWhileEventPostingForTownLoss()
703 {
704 	UINT32 const uiTime = GetWorldTotalMin() + 5;
705 	ScheduleMeanwhileEvent(3, 16, 0, LOST_TOWN, QUEEN, uiTime);
706 }
707 
708 
HandleMeanWhileEventPostingForSAMLiberation(INT8 bSamId)709 void HandleMeanWhileEventPostingForSAMLiberation( INT8 bSamId )
710 {
711 	UINT8 ubId;
712 	switch (bSamId) // which SAM liberated?
713 	{
714 		case 0:  ubId = NW_SAM;      break;
715 		case 1:  ubId = NE_SAM;      break;
716 		case 2:  ubId = CENTRAL_SAM; break;
717 		case 3:  return; // no meanwhile scene for this SAM site
718 		default: return; // invalid parameter
719 	}
720 	UINT32 const uiTime = GetWorldTotalMin() + 5;
721 	ScheduleMeanwhileEvent(3, 16, 0, ubId, QUEEN, uiTime);
722 }
723 
724 
HandleFlowersMeanwhileScene(INT8 bTimeCode)725 void HandleFlowersMeanwhileScene( INT8 bTimeCode )
726 {
727 	UINT32 uiTime = 0;
728 
729 	// make sure scene hasn't been used before
730 	if ( GetMeanWhileFlag( FLOWERS ) )
731 	{
732 		return;
733 	}
734 
735 	// time delay should be based on time code, 0 next day, 1 seeral days (random)
736 	if ( bTimeCode == 0 )
737 	{
738 		// 20-24 hours later
739 		uiTime = GetWorldTotalMin() + 60 * ( 20 + Random( 5 ) );
740 	}
741 	else
742 	{
743 		// 2-4 days later
744 		uiTime = GetWorldTotalMin() + 60 * ( 24 + Random( 48 ) );
745 	}
746 
747 	ScheduleMeanwhileEvent(3, 16, 0, FLOWERS, QUEEN, uiTime);
748 }
749 
750 
HandleOutskirtsOfMedunaMeanwhileScene(void)751 void HandleOutskirtsOfMedunaMeanwhileScene( void )
752 {
753 	UINT32 const uiTime = GetWorldTotalMin() + 5;
754 	ScheduleMeanwhileEvent(3, 16, 0, OUTSKIRTS_MEDUNA, QUEEN, uiTime);
755 }
756 
757 
HandleKillChopperMeanwhileScene(void)758 void HandleKillChopperMeanwhileScene( void )
759 {
760 	// make sure scene hasn't been used before
761 	if ( GetMeanWhileFlag( KILL_CHOPPER ) )
762 	{
763 		return;
764 	}
765 
766 	UINT32 const uiTime = GetWorldTotalMin() + 55 + Random(10);
767 	ScheduleMeanwhileEvent(3, 16, 0, KILL_CHOPPER, QUEEN, uiTime);
768 }
769 
770 
HandleScientistAWOLMeanwhileScene(void)771 void HandleScientistAWOLMeanwhileScene( void )
772 {
773 	UINT32 const uiTime = GetWorldTotalMin() + 5;
774 	ScheduleMeanwhileEvent(3, 16, 0, AWOL_SCIENTIST, QUEEN, uiTime);
775 }
776 
777 
HandleFirstBattleVictory(void)778 static void HandleFirstBattleVictory(void)
779 {
780 	UINT32 const uiTime = GetWorldTotalMin() + 5;
781 	ScheduleMeanwhileEvent(3, 16, 0, END_OF_PLAYERS_FIRST_BATTLE, QUEEN, uiTime);
782 }
783 
784 
HandleDelayedFirstBattleVictory(void)785 static void HandleDelayedFirstBattleVictory(void)
786 {
787 	/*
788 	//It is theoretically impossible to liberate a town within 60 minutes of the first battle (which is supposed to
789 	//occur outside of a town in this scenario).  The delay is attributed to the info taking longer to reach the queen.
790 	UINT32 const uiTime = GetWorldTotalMin() + 60;
791 	*/
792 	UINT32 const uiTime = GetWorldTotalMin() + 5;
793 	ScheduleMeanwhileEvent(3, 16, 0, END_OF_PLAYERS_FIRST_BATTLE, QUEEN, uiTime);
794 }
795 
796 
HandleFirstBattleEndingWhileInTown(INT16 sSectorX,INT16 sSectorY,INT16 bSectorZ,BOOLEAN fFromAutoResolve)797 void HandleFirstBattleEndingWhileInTown( INT16 sSectorX, INT16 sSectorY, INT16 bSectorZ, BOOLEAN fFromAutoResolve )
798 {
799 	INT8 bTownId = 0;
800 	INT16 sSector = 0;
801 
802 	if ( GetMeanWhileFlag( END_OF_PLAYERS_FIRST_BATTLE ) )
803 	{
804 		return;
805 	}
806 
807 	// if this is in fact a town and it is the first battle, then set gfFirstBattleMeanwhileScenePending true
808 	// if  is true then this is the end of the second battle, post the first meanwhile OR, on call to trash world, that
809 	// means player is leaving sector
810 
811 	// grab sector value
812 	sSector = sSectorX + sSectorY * MAP_WORLD_X;
813 
814 	// get town name id
815 	bTownId = StrategicMap[ sSector ].bNameId;
816 
817 	if ( bTownId == BLANK_SECTOR )
818 	{
819 		// invalid town
820 		HandleDelayedFirstBattleVictory( );
821 		gfFirstBattleMeanwhileScenePending = FALSE;
822 	}
823 	else if ( gfFirstBattleMeanwhileScenePending || fFromAutoResolve )
824 	{
825 		HandleFirstBattleVictory( );
826 		gfFirstBattleMeanwhileScenePending = FALSE;
827 	}
828 	else
829 	{
830 		gfFirstBattleMeanwhileScenePending = TRUE;
831 	}
832 }
833 
834 
HandleFirstMeanWhileSetUpWithTrashWorld(void)835 void HandleFirstMeanWhileSetUpWithTrashWorld( void )
836 {
837 
838 	// exiting sector after first battle fought
839 	if( gfFirstBattleMeanwhileScenePending )
840 	{
841 		HandleFirstBattleVictory( );
842 		gfFirstBattleMeanwhileScenePending = FALSE;
843 	}
844 
845 }
846 
847 
848 #ifdef WITH_UNITTESTS
849 #include "gtest/gtest.h"
850 
TEST(Meanwhile,asserts)851 TEST(Meanwhile, asserts)
852 {
853 	EXPECT_EQ(sizeof(MEANWHILE_DEFINITION), 8u);
854 }
855 
856 #endif
857