1 //Copyright Paul Reiche, Fred Ford. 1992-2002
2 
3 /*
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 
19 #include <stdlib.h>
20 
21 #include "comm.h"
22 #include "battle.h"
23 #include "fmv.h"
24 #include "gameev.h"
25 #include "types.h"
26 #include "globdata.h"
27 #include "resinst.h"
28 #include "restart.h"
29 #include "starbase.h"
30 #include "save.h"
31 #include "setup.h"
32 #include "master.h"
33 #include "controls.h"
34 #include "starcon.h"
35 #include "clock.h"
36 		// for GameClockTick()
37 #include "hyper.h"
38 		// for SeedUniverse()
39 #include "planets/planets.h"
40 		// for ExploreSolarSys()
41 #include "uqmdebug.h"
42 #include "libs/tasklib.h"
43 #include "libs/log.h"
44 #include "libs/gfxlib.h"
45 #include "libs/graphics/gfx_common.h"
46 #include "libs/graphics/tfb_draw.h"
47 #include "libs/misc.h"
48 
49 #include "uqmversion.h"
50 #include "options.h"
51 
52 volatile int MainExited = FALSE;
53 #ifdef DEBUG_SLEEP
54 uint32 mainThreadId;
55 extern uint32 SDL_ThreadID(void);
56 #endif
57 
58 // Open or close the periodically occuring QuasiSpace portal.
59 // It changes the appearant portal size when necessary.
60 static void
checkArilouGate(void)61 checkArilouGate (void)
62 {
63 	BYTE counter;
64 
65 	counter = GET_GAME_STATE (ARILOU_SPACE_COUNTER);
66 	if (GET_GAME_STATE (ARILOU_SPACE) == OPENING)
67 	{	// The portal is opening or fully open
68 		if (counter < 9)
69 			++counter;
70 	}
71 	else
72 	{	// The portal is closing or fully closed
73 		if (counter > 0)
74 			--counter;
75 	}
76 	SET_GAME_STATE (ARILOU_SPACE_COUNTER, counter);
77 }
78 
79 // Battle frame callback function.
80 static void
on_battle_frame(void)81 on_battle_frame (void)
82 {
83 	GameClockTick ();
84 	checkArilouGate ();
85 
86 	if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
87 		SeedUniverse ();
88 
89 	DrawAutoPilotMessage (FALSE);
90 }
91 
92 static void
BackgroundInitKernel(DWORD TimeOut)93 BackgroundInitKernel (DWORD TimeOut)
94 {
95 	LoadMasterShipList (TaskSwitch);
96 	TaskSwitch ();
97 	InitGameKernel ();
98 
99 	while ((GetTimeCounter () <= TimeOut) &&
100 	       !(GLOBAL (CurrentActivity) & CHECK_ABORT))
101 	{
102 		UpdateInputState ();
103 		TaskSwitch ();
104 	}
105 }
106 
107 // Executes on the main() thread
108 void
SignalStopMainThread(void)109 SignalStopMainThread (void)
110 {
111 	GamePaused = FALSE;
112 	GLOBAL (CurrentActivity) |= CHECK_ABORT;
113 	TaskSwitch ();
114 }
115 
116 // Executes on the main() thread
117 void
ProcessUtilityKeys(void)118 ProcessUtilityKeys (void)
119 {
120 	if (ImmediateInputState.menu[KEY_ABORT])
121 	{
122 		log_showBox (false, false);
123 		exit (EXIT_SUCCESS);
124 	}
125 
126 	if (ImmediateInputState.menu[KEY_FULLSCREEN])
127 	{
128 		int flags = GfxFlags ^ TFB_GFXFLAGS_FULLSCREEN;
129 		// clear ImmediateInputState so we don't repeat this next frame
130 		FlushInput ();
131 		TFB_DrawScreen_ReinitVideo (GraphicsDriver, flags, ScreenWidthActual,
132 				ScreenHeightActual);
133 	}
134 
135 #if defined(DEBUG) || defined(USE_DEBUG_KEY)
136 	{	// Only call the debug func on the rising edge of
137 		// ImmediateInputState[KEY_DEBUG] so it does not execute repeatedly.
138 		// This duplicates the PulsedInputState somewhat, but we cannot
139 		// use PulsedInputState here because it is meant for another thread.
140 		static int debugKeyState;
141 
142 		if (ImmediateInputState.menu[KEY_DEBUG] && debugKeyState == 0)
143 		{
144 			debugKeyPressed ();
145 		}
146 		debugKeyState = ImmediateInputState.menu[KEY_DEBUG];
147 	}
148 #endif  /* DEBUG */
149 }
150 
151 /* TODO: Remove these declarations once threading is gone. */
152 extern int snddriver, soundflags;
153 
154 int
Starcon2Main(void * threadArg)155 Starcon2Main (void *threadArg)
156 {
157 #ifdef DEBUG_SLEEP
158 	mainThreadId = SDL_ThreadID();
159 #endif
160 
161 #if CREATE_JOURNAL
162 {
163 int ac = argc;
164 char **av = argv;
165 
166 while (--ac > 0)
167 {
168 	++av;
169 	if ((*av)[0] == '-')
170 	{
171 		switch ((*av)[1])
172 		{
173 #if CREATE_JOURNAL
174 			case 'j':
175 				++create_journal;
176 				break;
177 #endif //CREATE_JOURNAL
178 		}
179 	}
180 }
181 }
182 #endif // CREATE_JOURNAL
183 
184 	{
185 		/* TODO: Put initAudio back in main where it belongs once threading
186 		 *       is gone.
187 		 */
188 		extern sint32 initAudio (sint32 driver, sint32 flags);
189 		initAudio (snddriver, soundflags);
190 	}
191 
192 	if (!LoadKernel (0,0))
193 	{
194 		log_add (log_Fatal, "\n  *** FATAL ERROR: Could not load basic content ***\n\nUQM requires at least the base content pack to run properly.");
195 		log_add (log_Fatal, "This file is typically called uqm-%d.%d.0-content.uqm.  UQM was expecting", UQM_MAJOR_VERSION, UQM_MINOR_VERSION);
196 		log_add (log_Fatal, "it in the %s/packages directory.", baseContentPath);
197 		log_add (log_Fatal, "Either your installation did not install the content pack at all, or it\ninstalled it in a different directory.\n\nFix your installation and rerun UQM.\n\n  *******************\n");
198 		log_showBox (true, true);
199 
200 		MainExited = TRUE;
201 		return EXIT_FAILURE;
202 	}
203 	log_add (log_Info, "We've loaded the Kernel");
204 
205 	GLOBAL (CurrentActivity) = 0;
206 	// show splash and init the kernel in the meantime
207 	SplashScreen (BackgroundInitKernel);
208 
209 //	OpenJournal ();
210 	while (StartGame ())
211 	{
212 		// Initialise a new game
213 		if (!SetPlayerInputAll ()) {
214 			log_add (log_Fatal, "Could not set player input.");
215 			explode ();  // Does not return;
216 		}
217 		InitGameStructures ();
218 		InitGameClock ();
219 		AddInitialGameEvents();
220 
221 		do
222 		{
223 #ifdef DEBUG
224 			if (debugHook != NULL)
225 			{
226 				void (*saveDebugHook) (void);
227 				saveDebugHook = debugHook;
228 				debugHook = NULL;
229 						// No further debugHook calls unless the called
230 						// function resets debugHook.
231 				(*saveDebugHook) ();
232 				continue;
233 			}
234 #endif
235 			SetStatusMessageMode (SMM_DEFAULT);
236 
237 			if (!((GLOBAL (CurrentActivity) | NextActivity) & CHECK_LOAD))
238 				ZeroVelocityComponents (&GLOBAL (velocity));
239 					// not going into talking pet conversation
240 			else if (GLOBAL (CurrentActivity) & CHECK_LOAD)
241 				GLOBAL (CurrentActivity) = NextActivity;
242 
243 			if ((GLOBAL (CurrentActivity) & START_ENCOUNTER)
244 					|| GET_GAME_STATE (CHMMR_BOMB_STATE) == 2)
245 			{
246 				if (GET_GAME_STATE (CHMMR_BOMB_STATE) == 2
247 						&& !GET_GAME_STATE (STARBASE_AVAILABLE))
248 				{	/* BGD mode */
249 					InstallBombAtEarth ();
250 				}
251 				else if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) == (BYTE)~0
252 						|| GET_GAME_STATE (CHMMR_BOMB_STATE) == 2)
253 				{
254 					GLOBAL (CurrentActivity) |= START_ENCOUNTER;
255 					VisitStarBase ();
256 				}
257 				else
258 				{
259 					GLOBAL (CurrentActivity) |= START_ENCOUNTER;
260 					RaceCommunication ();
261 				}
262 
263 				if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
264 				{
265 					GLOBAL (CurrentActivity) &= ~START_ENCOUNTER;
266 					if (LOBYTE (GLOBAL (CurrentActivity)) == IN_INTERPLANETARY)
267 						GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
268 				}
269 			}
270 			else if (GLOBAL (CurrentActivity) & START_INTERPLANETARY)
271 			{
272 				GLOBAL (CurrentActivity) = MAKE_WORD (IN_INTERPLANETARY, 0);
273 
274 				DrawAutoPilotMessage (TRUE);
275 				SetGameClockRate (INTERPLANETARY_CLOCK_RATE);
276 				ExploreSolarSys ();
277 			}
278 			else
279 			{
280 				// Entering HyperSpace or QuasiSpace.
281 				GLOBAL (CurrentActivity) = MAKE_WORD (IN_HYPERSPACE, 0);
282 
283 				DrawAutoPilotMessage (TRUE);
284 				SetGameClockRate (HYPERSPACE_CLOCK_RATE);
285 				Battle (&on_battle_frame);
286 			}
287 
288 			SetFlashRect (NULL);
289 
290 			LastActivity = GLOBAL (CurrentActivity);
291 
292 			if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD))
293 					&& (LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE
294 							// if died for some reason
295 					|| GLOBAL_SIS (CrewEnlisted) == (COUNT)~0))
296 			{
297 				if (GET_GAME_STATE (KOHR_AH_KILLED_ALL))
298 					InitCommunication (BLACKURQ_CONVERSATION);
299 						// surrendered to Ur-Quan
300 				else if (GLOBAL (CurrentActivity) & CHECK_RESTART)
301 					GLOBAL (CurrentActivity) &= ~CHECK_RESTART;
302 				break;
303 			}
304 		} while (!(GLOBAL (CurrentActivity) & CHECK_ABORT));
305 
306 		StopSound ();
307 		UninitGameClock ();
308 		UninitGameStructures ();
309 		ClearPlayerInputAll ();
310 	}
311 //	CloseJournal ();
312 
313 	UninitGameKernel ();
314 	FreeMasterShipList ();
315 	FreeKernel ();
316 
317 	log_showBox (false, false);
318 	MainExited = TRUE;
319 
320 	(void) threadArg;  /* Satisfying compiler (unused parameter) */
321 	return 0;
322 }
323 
324