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