1 #include "GameLoop.h"
2 #include "GameVersion.h"
3 #include "Local.h"
4 #include "SGP.h"
5 #include "Screens.h"
6 #include "ShopKeeper_Interface.h"
7 #include "Tactical_Placement_GUI.h"
8 #include "Cursors.h"
9 #include "Init.h"
10 #include "Music_Control.h"
11 #include "Sys_Globals.h"
12 #include "Laptop.h"
13 #include "MapScreen.h"
14 #include "Game_Clock.h"
15 #include "Overhead.h"
16 #include "Map_Screen_Interface.h"
17 #include "Tactical_Save.h"
18 #include "Interface.h"
19 #include "GameSettings.h"
20 #include "Text.h"
21 #include "HelpScreen.h"
22 #include "SaveLoadGame.h"
23 #include "Finances.h"
24 #include "Options_Screen.h"
25 #include "Debug.h"
26 #include "Video.h"
27 #include "MemMan.h"
28 #include "Button_System.h"
29 #include "Font_Control.h"
30 #include "UILayout.h"
31 #include "GameState.h"
32 #include "sgp/FileMan.h"
33 #include "Logger.h"
34
35 #include <string_theory/format>
36 #include <string_theory/string>
37
38 #include <stdexcept>
39
40
41 ScreenID guiCurrentScreen = ERROR_SCREEN; // XXX TODO001A had no explicit initialisation
42 ScreenID guiPendingScreen = NO_PENDING_SCREEN;
43
44 static BOOLEAN gfCheckForFreeSpaceOnHardDrive = FALSE;
45
46 // The InitializeGame function is responsible for setting up all data and Gaming Engine
47 // tasks which will run the game
48
InitializeGame(void)49 void InitializeGame(void)
50 {
51 UINT32 uiIndex;
52
53 // Initlaize mouse subsystems
54 MSYS_Init( );
55 InitButtonSystem();
56 InitCursors( );
57
58 // Init Fonts
59 InitializeFonts();
60
61 InitTacticalSave();
62
63 SLOGI("Version Label: %s", g_version_label);
64 SLOGI("Version #: %s", g_version_number);
65
66 // Initialize Game Screens.
67 for (uiIndex = 0; uiIndex < MAX_SCREENS; uiIndex++)
68 {
69 void (*const init)(void) = GameScreens[uiIndex].InitializeScreen;
70 if (init) init();
71 }
72
73 //Init the help screen system
74 InitHelpScreenSystem();
75
76 //Loads the saved (if any) general JA2 game settings
77 LoadGameSettings();
78
79 //Initialize the Game options ( Gun nut, scifi and dif. levels
80 InitGameOptions();
81
82 // preload mapscreen graphics
83 HandlePreloadOfMapGraphics( );
84
85 guiCurrentScreen = INIT_SCREEN;
86 }
87
88
89 // The ShutdownGame function will free up/undo all things that were started in InitializeGame()
90 // It will also be responsible to making sure that all Gaming Engine tasks exit properly
91
ShutdownGame(void)92 void ShutdownGame(void)
93 {
94 // handle shutdown of game with respect to preloaded mapscreen graphics
95 HandleRemovalOfPreLoadedMapGraphics( );
96
97 ShutdownJA2( );
98
99 //Save the general save game settings to disk
100 SaveGameSettings();
101
102 InitTacticalSave();
103 }
104
105
106 static void HandleNewScreenChange(UINT32 uiNewScreen, UINT32 uiOldScreen);
107
108
109 // This is the main Gameloop. This should eventually by one big switch statement which represents
110 // the state of the game (i.e. Main Menu, PC Generation, Combat loop, etc....)
111 // This function exits constantly and reenters constantly
GameLoop(void)112 void GameLoop(void)
113 try
114 {
115 InputAtom InputEvent;
116 ScreenID uiOldScreen = guiCurrentScreen;
117
118 SGPPoint MousePos;
119 GetMousePos(&MousePos);
120 // Hook into mouse stuff for MOVEMENT MESSAGES
121 MouseSystemHook(MOUSE_POS, MousePos.iX, MousePos.iY);
122 MusicPoll();
123
124 while (DequeueSpecificEvent(&InputEvent, MOUSE_EVENTS))
125 {
126 MouseSystemHook(InputEvent.usEvent, MousePos.iX, MousePos.iY);
127 }
128
129
130 if ( gfGlobalError )
131 {
132 guiCurrentScreen = ERROR_SCREEN;
133 }
134
135
136 //if we are to check for free space on the hard drive
137 if( gfCheckForFreeSpaceOnHardDrive )
138 {
139 //only if we are in a screen that can get this check
140 if( guiCurrentScreen == MAP_SCREEN || guiCurrentScreen == GAME_SCREEN || guiCurrentScreen == SAVE_LOAD_SCREEN )
141 {
142 // Make sure the user has enough hard drive space
143 uint64_t uiSpaceOnDrive = GetFreeSpaceOnHardDriveWhereGameIsRunningFrom();
144 if( uiSpaceOnDrive < REQUIRED_FREE_SPACE )
145 {
146 ST::string zSpaceOnDrive = ST::format("{.2f}", uiSpaceOnDrive / (FLOAT)BYTESINMEGABYTE);
147
148 ST::string zText = st_format_printf(pMessageStrings[ MSG_LOWDISKSPACE_WARNING ], zSpaceOnDrive, "20");
149
150 if( guiPreviousOptionScreen == MAP_SCREEN )
151 DoMapMessageBox( MSG_BOX_BASIC_STYLE, zText, MAP_SCREEN, MSG_BOX_FLAG_OK, NULL );
152 else
153 DoMessageBox( MSG_BOX_BASIC_STYLE, zText, GAME_SCREEN, MSG_BOX_FLAG_OK, NULL, NULL );
154 }
155 gfCheckForFreeSpaceOnHardDrive = FALSE;
156 }
157 }
158
159 // ATE: Force to be in message box screen!
160 if ( gfInMsgBox )
161 {
162 guiPendingScreen = MSG_BOX_SCREEN;
163 }
164
165 if ( guiPendingScreen != NO_PENDING_SCREEN )
166 {
167 // Based on active screen, deinit!
168 if( guiPendingScreen != guiCurrentScreen )
169 {
170 switch( guiCurrentScreen )
171 {
172 case MAP_SCREEN:
173 if( guiPendingScreen != MSG_BOX_SCREEN )
174 {
175 EndMapScreen( FALSE );
176 }
177 break;
178 case LAPTOP_SCREEN:
179 ExitLaptop();
180 break;
181 default:
182 break;
183 }
184 }
185
186 // if the screen has chnaged
187 if( uiOldScreen != guiPendingScreen )
188 {
189 // Set the fact that the screen has changed
190 uiOldScreen = guiPendingScreen;
191
192 HandleNewScreenChange( guiPendingScreen, guiCurrentScreen );
193 }
194 guiCurrentScreen = guiPendingScreen;
195 guiPendingScreen = NO_PENDING_SCREEN;
196
197 }
198
199
200
201 uiOldScreen = (*(GameScreens[guiCurrentScreen].HandleScreen))();
202
203 // if the screen has chnaged
204 if( uiOldScreen != guiCurrentScreen )
205 {
206 HandleNewScreenChange( uiOldScreen, guiCurrentScreen );
207 guiCurrentScreen = uiOldScreen;
208 }
209
210 RefreshScreen();
211
212 guiGameCycleCounter++;
213
214 UpdateClock();
215
216 }
217 catch (std::exception const& e)
218 {
219 guiPreviousOptionScreen = guiCurrentScreen;
220 char const* what;
221 char const* success = "failed";
222 char const* attach = "";
223
224 if (gfEditMode && GameState::getInstance()->isEditorMode())
225 {
226 what = "map";
227 if (SaveWorld("error.dat"))
228 {
229 success = "succeeded (error.dat)";
230 attach = " Do not forget to attach the map.";
231 }
232 }
233 else
234 {
235 what = "savegame";
236 if (SaveGame(SAVE__ERROR_NUM, "error savegame"))
237 {
238 success = "succeeded (error.sav)";
239 attach = " Do not forget to attach the savegame.";
240 }
241 }
242 char msg[2048];
243 snprintf(msg, lengthof(msg),
244 "%s\n"
245 "Creating an emergency %s %s.\n"
246 "Please report this error with a description of the circumstances.%s",
247 e.what(), what, success, attach
248 );
249 throw std::runtime_error(msg);
250 }
251
252
SetPendingNewScreen(ScreenID const uiNewScreen)253 void SetPendingNewScreen(ScreenID const uiNewScreen)
254 {
255 guiPendingScreen = uiNewScreen;
256 }
257
258
259 // Gets called when the screen changes, place any needed in code in here
HandleNewScreenChange(UINT32 uiNewScreen,UINT32 uiOldScreen)260 static void HandleNewScreenChange(UINT32 uiNewScreen, UINT32 uiOldScreen)
261 {
262 //if we are not going into the message box screen, and we didnt just come from it
263 if( ( uiNewScreen != MSG_BOX_SCREEN && uiOldScreen != MSG_BOX_SCREEN ) )
264 {
265 //reset the help screen
266 NewScreenSoResetHelpScreen( );
267 }
268 }
269
270
HandleShortCutExitState()271 void HandleShortCutExitState()
272 {
273 SLOGI("User pressed ESCape, TERMINATING");
274
275 // Use YES/NO pop up box, setup for particular screen
276 switch (guiCurrentScreen)
277 {
278 case DEBUG_SCREEN:
279 case EDIT_SCREEN:
280 case ERROR_SCREEN:
281 // Do not prompt if error or editor
282 requestGameExit();
283 break;
284
285 case LAPTOP_SCREEN:
286 DoLapTopSystemMessageBox(pMessageStrings[MSG_EXITGAME], LAPTOP_SCREEN, MSG_BOX_FLAG_YESNO, EndGameMessageBoxCallBack);
287 break;
288
289 case MAP_SCREEN:
290 DoMapMessageBox(MSG_BOX_BASIC_STYLE, pMessageStrings[MSG_EXITGAME], MAP_SCREEN, MSG_BOX_FLAG_YESNO, EndGameMessageBoxCallBack);
291 return;
292
293 case SHOPKEEPER_SCREEN:
294 DoSkiMessageBox(pMessageStrings[MSG_EXITGAME], SHOPKEEPER_SCREEN, MSG_BOX_FLAG_YESNO, EndGameMessageBoxCallBack);
295 break;
296
297 default:
298 { // set up for all otherscreens
299 SGPBox const pCenteringRect = { 0, 0, SCREEN_WIDTH, INV_INTERFACE_START_Y };
300 DoMessageBox(MSG_BOX_BASIC_STYLE, pMessageStrings[MSG_EXITGAME], guiCurrentScreen, MSG_BOX_FLAG_YESNO, EndGameMessageBoxCallBack, &pCenteringRect);
301 break;
302 }
303 }
304 }
305
306
EndGameMessageBoxCallBack(MessageBoxReturnValue const bExitValue)307 void EndGameMessageBoxCallBack(MessageBoxReturnValue const bExitValue)
308 {
309 // yes, so start over, else stay here and do nothing for now
310 if( bExitValue == MSG_BOX_RETURN_YES )
311 {
312 requestGameExit();
313 }
314
315 //If we are in the tactical placement gui, we need this flag set so the interface is updated.
316 if( gfTacticalPlacementGUIActive )
317 {
318 gfTacticalPlacementGUIDirty = TRUE;
319 gfValidLocationsChanged = TRUE;
320 }
321 }
322
323
NextLoopCheckForEnoughFreeHardDriveSpace()324 void NextLoopCheckForEnoughFreeHardDriveSpace()
325 {
326 gfCheckForFreeSpaceOnHardDrive = TRUE;
327 }
328