1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell
5  * or otherwise commercially exploit the source or things you created based on the
6  * source.
7  *
8 */
9 
10 
11 
12 
13 #ifdef _WIN32
14  #include <direct.h>
15  #include <io.h>
16  #include <windows.h>
17 #ifndef _MINGW
18  #include <crtdbg.h>
19 #endif // !_MINGW
20 #else
21  #include <unistd.h>
22  #include <sys/stat.h>
23 #endif
24 
25 #include "anim/animplay.h"
26 #include "asteroid/asteroid.h"
27 #include "autopilot/autopilot.h"
28 #include "bmpman/bmpman.h"
29 #include "camera/camera.h"
30 #include "cfile/cfile.h"
31 #include "cmdline/cmdline.h"
32 #include "cmeasure/cmeasure.h"
33 #include "cutscene/cutscenes.h"
34 #include "cutscene/movie.h"
35 #include "debris/debris.h"
36 #include "exceptionhandler/exceptionhandler.h"
37 #include "external_dll/trackirpublic.h" // header file for the TrackIR routines (Swifty)
38 #include "fireball/fireballs.h"
39 #include "freespace2/freespace.h"
40 #include "freespace2/freespaceresource.h"
41 #include "freespace2/levelpaging.h"
42 #include "gamehelp/contexthelp.h"
43 #include "gamehelp/gameplayhelp.h"
44 #include "gamesequence/gamesequence.h"
45 #include "gamesnd/eventmusic.h"
46 #include "gamesnd/gamesnd.h"
47 #include "globalincs/alphacolors.h"
48 #include "globalincs/version.h"
49 #include "globalincs/mspdb_callstack.h"
50 #include "graphics/font.h"
51 #include "hud/hud.h"
52 #include "hud/hudconfig.h"
53 #include "hud/hudescort.h"
54 #include "hud/hudlock.h"
55 #include "hud/hudmessage.h"
56 #include "hud/hudshield.h"
57 #include "hud/hudtargetbox.h"
58 #include "hud/hudparse.h"
59 #include "hud/hudsquadmsg.h"
60 #include "iff_defs/iff_defs.h"
61 #include "io/joy.h"
62 #include "io/joy_ff.h"
63 #include "io/key.h"
64 #include "io/mouse.h"
65 #include "io/timer.h"
66 #include "jumpnode/jumpnode.h"
67 #include "lab/lab.h"
68 #include "lab/wmcgui.h"	//So that GUI_System can be initialized
69 #include "lighting/lighting.h"
70 #include "localization/localize.h"
71 #include "math/staticrand.h"
72 #include "menuui/barracks.h"
73 #include "menuui/credits.h"
74 #include "menuui/mainhallmenu.h"
75 #include "menuui/optionsmenu.h"
76 #include "menuui/playermenu.h"
77 #include "menuui/readyroom.h"
78 #include "menuui/snazzyui.h"
79 #include "menuui/techmenu.h"
80 #include "menuui/trainingmenu.h"
81 #include "mission/missionbriefcommon.h"
82 #include "mission/missioncampaign.h"
83 #include "mission/missiongoals.h"
84 #include "mission/missionhotkey.h"
85 #include "mission/missionload.h"
86 #include "mission/missionlog.h"
87 #include "mission/missionmessage.h"
88 #include "mission/missionparse.h"
89 #include "mission/missiontraining.h"
90 #include "missionui/fictionviewer.h"
91 #include "missionui/missionbrief.h"
92 #include "missionui/missioncmdbrief.h"
93 #include "missionui/missiondebrief.h"
94 #include "missionui/missionloopbrief.h"
95 #include "missionui/missionpause.h"
96 #include "missionui/missionscreencommon.h"
97 #include "missionui/missionshipchoice.h"
98 #include "missionui/missionweaponchoice.h"
99 #include "missionui/redalert.h"
100 #include "mod_table/mod_table.h"
101 #include "nebula/neb.h"
102 #include "nebula/neblightning.h"
103 #include "network/multi.h"
104 #include "network/multi_dogfight.h"
105 #include "network/multi_endgame.h"
106 #include "network/multi_ingame.h"
107 #include "network/multi_log.h"
108 #include "network/multi_pause.h"
109 #include "network/multi_pxo.h"
110 #include "network/multi_rate.h"
111 #include "network/multi_respawn.h"
112 #include "network/multi_voice.h"
113 #include "network/multimsgs.h"
114 #include "network/multiteamselect.h"
115 #include "network/multiui.h"
116 #include "network/multiutil.h"
117 #include "network/stand_gui.h"
118 #include "object/objcollide.h"
119 #include "object/objectsnd.h"
120 #include "object/waypoint.h"
121 #include "observer/observer.h"
122 #include "osapi/osapi.h"
123 #include "osapi/osregistry.h"
124 #include "parse/encrypt.h"
125 #include "parse/generic_log.h"
126 #include "parse/lua.h"
127 #include "parse/parselo.h"
128 #include "parse/scripting.h"
129 #include "parse/sexp.h"
130 #include "particle/particle.h"
131 #include "playerman/managepilot.h"
132 #include "playerman/player.h"
133 #include "popup/popup.h"
134 #include "popup/popupdead.h"
135 #include "radar/radar.h"
136 #include "radar/radarsetup.h"
137 #include "render/3d.h"
138 #include "ship/afterburner.h"
139 #include "ship/awacs.h"
140 #include "ship/ship.h"
141 #include "ship/shipcontrails.h"
142 #include "ship/shipfx.h"
143 #include "ship/shiphit.h"
144 #include "sound/audiostr.h"
145 #include "sound/ds.h"
146 #include "sound/fsspeech.h"
147 #include "sound/sound.h"
148 #include "sound/voicerec.h"
149 #include "starfield/starfield.h"
150 #include "starfield/supernova.h"
151 #include "stats/medals.h"
152 #include "stats/stats.h"
153 #include "weapon/beam.h"
154 #include "weapon/emp.h"
155 #include "weapon/flak.h"
156 #include "weapon/muzzleflash.h"
157 #include "weapon/shockwave.h"
158 #include "weapon/weapon.h"
159 #include "fs2netd/fs2netd_client.h"
160 #include "pilotfile/pilotfile.h"
161 
162 #include "globalincs/pstypes.h"
163 
164 #include <stdexcept>
165 
166 extern int Om_tracker_flag; // needed for FS2OpenPXO config
167 
168 
169 
170 #ifdef NDEBUG
171 #ifdef FRED
172 #error macro FRED is defined when trying to build release FreeSpace.  Please undefine FRED macro in build settings
173 #endif
174 #endif
175 
176 
177 //	Revision history.
178 //	Full version:
179 //    1.00.04	5/26/98	MWA -- going final (12 pm)
180 //    1.00.03	5/26/98	MWA -- going final (3 am)
181 //    1.00.02	5/25/98	MWA -- going final
182 //    1.00.01	5/25/98	MWA -- going final
183 //		0.90		5/21/98	MWA -- getting ready for final.
184 //		0.10		4/9/98.  Set by MK.
185 //
186 //	OEM version:
187 //		1.00		5/28/98	AL.	First release to Interplay QA.
188 
189 
190 //  This function is defined in code\network\multiutil.cpp so will be linked from multiutil.obj
191 //  it's required fro the -missioncrcs command line option - Kazan
192 void multi_spew_pxo_checksums(int max_files, char *outfile);
193 void fs2netd_spew_table_checksums(char *outfile);
194 
195 extern bool frame_rate_display;
196 
197 bool Env_cubemap_drawn = false;
198 
199 void game_reset_view_clip();
200 void game_reset_shade_frame();
201 void game_post_level_init();
202 void game_do_frame();
203 void game_update_missiontime();	// called from game_do_frame() and navmap_do_frame()
204 void game_reset_time();
205 void game_show_framerate();			// draws framerate in lower right corner
206 
207 int Game_no_clear = 0;
208 
209 typedef struct big_expl_flash {
210 	float max_flash_intensity;	// max intensity
211 	float cur_flash_intensity;	// cur intensity
212 	int	flash_start;		// start time
213 } big_expl_flash;
214 
215 #define FRAME_FILTER 16
216 
217 #define DEFAULT_SKILL_LEVEL	1
218 int	Game_skill_level = DEFAULT_SKILL_LEVEL;
219 
220 #define EXE_FNAME			("fs2.exe")
221 
222 #define LAUNCHER_FNAME	("Launcher.exe")
223 
224 // JAS: Code for warphole camera.
225 // Needs to be cleaned up.
226 float Warpout_time = 0.0f;
227 int Warpout_forced = 0;		// Set if this is a forced warpout that cannot be cancelled.
228 int Warpout_sound = -1;
229 int Use_joy_mouse = 0;
230 int Use_palette_flash = 1;
231 #ifndef NDEBUG
232 int Use_fullscreen_at_startup = 0;
233 #endif
234 int Show_area_effect = 0;
235 object	*Last_view_target = NULL;
236 
237 int dogfight_blown = 0;
238 
239 int	frame_int = -1;
240 float frametimes[FRAME_FILTER];
241 float frametotal = 0.0f;
242 float flRealframetime;
243 float flFrametime;
244 fix FrametimeOverall = 0;
245 
246 #ifndef NDEBUG
247 	int	Show_framerate = 1;
248 	int	Show_mem = 1;
249 #else
250 	int	Show_framerate = 0;
251 	int	Show_mem = 0;
252 #endif
253 
254 int	Framerate_cap = 120;
255 
256 // to determine if networking should be disabled, needs to be done first thing
257 int Networking_disabled = 0;
258 
259 // for the model page in system
260 extern void model_page_in_start();
261 
262 int	Show_cpu = 0;
263 int	Show_target_debug_info = 0;
264 int	Show_target_weapons = 0;
265 int	Game_font = -1;
266 #ifndef NDEBUG
267 static int Show_player_pos = 0;		// debug console command to show player world pos on HUD
268 #endif
269 
270 int Debug_octant = -1;
271 
272 fix Game_time_compression = F1_0;
273 fix Desired_time_compression = Game_time_compression;
274 fix Time_compression_change_rate = 0;
275 bool Time_compression_locked = false; //Can the user change time with shift- controls?
276 
277 // auto-lang stuff
278 int detect_lang();
279 
280 // table checksums that will be used for pilot files
281 uint Weapon_tbl_checksum = 0;
282 uint Ships_tbl_checksum = 0;
283 
284 // if the ships.tbl the player has is valid
285 int Game_ships_tbl_valid = 0;
286 
287 // if the weapons.tbl the player has is valid
288 int Game_weapons_tbl_valid = 0;
289 
290 int Test_begin = 0;
291 extern int	Player_attacking_enabled;
292 int Show_net_stats;
293 
294 int Pre_player_entry;
295 
296 int	Fred_running = 0;
297 
298 // required for hudtarget... kinda dumb, but meh
299 char Fred_alt_names[MAX_SHIPS][NAME_LENGTH+1];
300 char Fred_callsigns[MAX_SHIPS][NAME_LENGTH+1];
301 
302 char Game_current_mission_filename[MAX_FILENAME_LEN];
303 int game_single_step = 0;
304 int last_single_step=0;
305 
306 int game_zbuffer = 1;
307 static int Game_paused;
308 
309 int Game_level_seed;
310 
311 #define EXPIRE_BAD_CHECKSUM			1
312 #define EXPIRE_BAD_TIME					2
313 
314 extern void ssm_init();
315 extern void ssm_level_init();
316 extern void ssm_process();
317 
318 // static variable to contain the time this version was built
319 // commented out for now until
320 // I figure out how to get the username into the file
321 //LOCAL char freespace_build_time[] = "Compiled on:"__DATE__" "__TIME__" by "__USER__;
322 
323 // defines and variables used for dumping frame for making trailers.
324 #ifndef NDEBUG
325 int Debug_dump_frames = 0;			// Set to 0 to not dump frames, else equal hz to dump. (15 or 30 probably)
326 int Debug_dump_trigger = 0;
327 int Debug_dump_frame_count;
328 int Debug_dump_frame_num = 0;
329 #define DUMP_BUFFER_NUM_FRAMES	1			// store every 15 frames
330 #endif
331 
332 // amount of time to wait after the player has died before we display the death died popup
333 #define PLAYER_DIED_POPUP_WAIT		2500
334 int Player_died_popup_wait = -1;
335 
336 int Multi_ping_timestamp = -1;
337 
338 int Default_env_map = -1;
339 
340 // builtin mission list stuff
341 int Game_builtin_mission_count = 92;
342 fs_builtin_mission Game_builtin_mission_list[MAX_BUILTIN_MISSIONS] = {
343 	// single player campaign
344 	{ "freespace2.fc2",				(FSB_FROM_VOLITION | FSB_CAMPAIGN_FILE),					"" },
345 
346 	// act 1
347 	{ "sm1-01.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_2	},
348 	{ "sm1-02.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_2	},
349 	{ "sm1-03.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_2	},
350 	{ "sm1-04.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_2	},
351 	{ "sm1-05.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_2	},
352 	{ "sm1-06.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_2	},
353 	{ "sm1-07.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_2	},
354 	{ "sm1-08.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_2	},
355 	{ "sm1-09.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_2	},
356 	{ "sm1-10.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_2	},
357 	{ "loop1-1.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_2	},
358 	{ "loop1-2.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_2	},
359 	{ "loop1-3.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_2	},
360 	{ "training-1.fs2",				(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_2	},
361 	{ "training-2.fs2",				(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_2	},
362 	{ "training-3.fs2",				(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_2	},
363 	{ "tsm-104.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_2	},
364 	{ "tsm-105.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_2	},
365 	{ "tsm-106.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_2	},
366 
367 	// act 2
368 	{ "sm2-01.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_3	},
369 	{ "sm2-02.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_3	},
370 	{ "sm2-03.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_3	},
371 	{ "sm2-04.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_3	},
372 	{ "sm2-05.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_3	},
373 	{ "sm2-06.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_3	},
374 	{ "sm2-07.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_3	},
375 	{ "sm2-08.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_3	},
376 	{ "sm2-09.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_3	},
377 	{ "sm2-10.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_3	},
378 
379 	// act 3
380 	{ "sm3-01.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_3	},
381 	{ "sm3-02.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_3	},
382 	{ "sm3-03.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_3	},
383 	{ "sm3-04.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_3	},
384 	{ "sm3-05.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_3	},
385 	{ "sm3-06.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_3	},
386 	{ "sm3-07.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_3	},
387 	{ "sm3-08.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_3	},
388 	{ "sm3-09.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_3	},
389 	{ "sm3-10.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_3	},
390 	{ "loop2-1.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_3	},
391 	{ "loop2-2.fs2",					(FSB_FROM_VOLITION | FSB_CAMPAIGN),							FS_CDROM_VOLUME_3	},
392 
393 	// multiplayer missions
394 
395 	// gauntlet
396 	{ "g-shi.fs2",						(FSB_FROM_VOLITION | FSB_MULTI),								""						},
397 	{ "g-ter.fs2",						(FSB_FROM_VOLITION | FSB_MULTI),								""						},
398 	{ "g-vas.fs2",						(FSB_FROM_VOLITION | FSB_MULTI),								""						},
399 
400 	// coop
401 	{ "m-01.fs2",						(FSB_FROM_VOLITION | FSB_MULTI),								""						},
402 	{ "m-02.fs2",						(FSB_FROM_VOLITION | FSB_MULTI),								""						},
403 	{ "m-03.fs2",						(FSB_FROM_VOLITION | FSB_MULTI),								""						},
404 	{ "m-04.fs2",						(FSB_FROM_VOLITION | FSB_MULTI),								""						},
405 
406 	// dogfight
407 	{ "mdh-01.fs2",					(FSB_FROM_VOLITION | FSB_MULTI),								""						},
408 	{ "mdh-02.fs2",					(FSB_FROM_VOLITION | FSB_MULTI),								""						},
409 	{ "mdh-03.fs2",					(FSB_FROM_VOLITION | FSB_MULTI),								""						},
410 	{ "mdh-04.fs2",					(FSB_FROM_VOLITION | FSB_MULTI),								""						},
411 	{ "mdh-05.fs2",					(FSB_FROM_VOLITION | FSB_MULTI),								""						},
412 	{ "mdh-06.fs2",					(FSB_FROM_VOLITION | FSB_MULTI),								""						},
413 	{ "mdh-07.fs2",					(FSB_FROM_VOLITION | FSB_MULTI),								""						},
414 	{ "mdh-08.fs2",					(FSB_FROM_VOLITION | FSB_MULTI),								""						},
415 	{ "mdh-09.fs2",					(FSB_FROM_VOLITION | FSB_MULTI),								""						},
416 	{ "mdl-01.fs2",					(FSB_FROM_VOLITION | FSB_MULTI),								""						},
417 	{ "mdl-02.fs2",					(FSB_FROM_VOLITION | FSB_MULTI),								""						},
418 	{ "mdl-03.fs2",					(FSB_FROM_VOLITION | FSB_MULTI),								""						},
419 	{ "mdl-04.fs2",					(FSB_FROM_VOLITION | FSB_MULTI),								""						},
420 	{ "mdl-05.fs2",					(FSB_FROM_VOLITION | FSB_MULTI),								""						},
421 	{ "mdl-06.fs2",					(FSB_FROM_VOLITION | FSB_MULTI),								""						},
422 	{ "mdl-07.fs2",					(FSB_FROM_VOLITION | FSB_MULTI),								""						},
423 	{ "mdl-08.fs2",					(FSB_FROM_VOLITION | FSB_MULTI),								""						},
424 	{ "mdl-09.fs2",					(FSB_FROM_VOLITION | FSB_MULTI),								""						},
425 	{ "mdm-01.fs2",					(FSB_FROM_VOLITION | FSB_MULTI),								""						},
426 	{ "mdm-02.fs2",					(FSB_FROM_VOLITION | FSB_MULTI),								""						},
427 	{ "mdm-03.fs2",					(FSB_FROM_VOLITION | FSB_MULTI),								""						},
428 	{ "mdm-04.fs2",					(FSB_FROM_VOLITION | FSB_MULTI),								""						},
429 	{ "mdm-05.fs2",					(FSB_FROM_VOLITION | FSB_MULTI),								""						},
430 	{ "mdm-06.fs2",					(FSB_FROM_VOLITION | FSB_MULTI),								""						},
431 	{ "mdm-07.fs2",					(FSB_FROM_VOLITION | FSB_MULTI),								""						},
432 	{ "mdm-08.fs2",					(FSB_FROM_VOLITION | FSB_MULTI),								""						},
433 	{ "mdm-09.fs2",					(FSB_FROM_VOLITION | FSB_MULTI),								""						},
434 	{ "osdog.fs2",						(FSB_FROM_VOLITION | FSB_MULTI),								""						},
435 
436 	// TvT
437 	{ "mt-01.fs2",						(FSB_FROM_VOLITION | FSB_MULTI),								""						},
438 	{ "mt-02.fs2",						(FSB_FROM_VOLITION | FSB_MULTI),								""						},
439 	{ "mt-03.fs2",						(FSB_FROM_VOLITION | FSB_MULTI),								""						},
440 	{ "mt-04.fs2",						(FSB_FROM_VOLITION | FSB_MULTI),								""						},
441 	{ "mt-05.fs2",						(FSB_FROM_VOLITION | FSB_MULTI),								""						},
442 	{ "mt-06.fs2",						(FSB_FROM_VOLITION | FSB_MULTI),								""						},
443 	{ "mt-07.fs2",						(FSB_FROM_VOLITION | FSB_MULTI),								""						},
444 	{ "mt-08.fs2",						(FSB_FROM_VOLITION | FSB_MULTI),								""						},
445 	{ "mt-09.fs2",						(FSB_FROM_VOLITION | FSB_MULTI),								""						},
446 	{ "mt-10.fs2",						(FSB_FROM_VOLITION | FSB_MULTI),								""						},
447 
448 	// campaign
449 	{ "templar.fc2",				(FSB_FROM_VOLITION | FSB_MULTI | FSB_CAMPAIGN_FILE),					"" },
450 	{ "templar-01.fs2",				(FSB_FROM_VOLITION | FSB_MULTI | FSB_CAMPAIGN),			""						},
451 	{ "templar-02.fs2",				(FSB_FROM_VOLITION | FSB_MULTI | FSB_CAMPAIGN),			""						},
452 	{ "templar-03.fs2",				(FSB_FROM_VOLITION | FSB_MULTI | FSB_CAMPAIGN),			""						},
453 	{ "templar-04.fs2",				(FSB_FROM_VOLITION | FSB_MULTI | FSB_CAMPAIGN),			""						},
454 };
455 
456 
457 // Internal function prototypes
458 void game_maybe_draw_mouse(float frametime);
459 void init_animating_pointer();
460 void load_animating_pointer(char *filename, int dx, int dy);
461 void unload_animating_pointer();
462 void game_do_training_checks();
463 void game_shutdown(void);
464 void game_show_event_debug(float frametime);
465 void game_event_debug_init();
466 void game_frame(bool paused = false);
467 void game_start_subspace_ambient_sound();
468 void game_stop_subspace_ambient_sound();
469 void verify_ships_tbl();
470 void verify_weapons_tbl();
471 void game_title_screen_display();
472 void game_title_screen_close();
473 
474 // loading background filenames
475 static char *Game_loading_bground_fname[GR_NUM_RESOLUTIONS] = {
476 	"LoadingBG",		// GR_640
477 	"2_LoadingBG"		// GR_1024
478 };
479 
480 
481 static char *Game_loading_ani_fname[GR_NUM_RESOLUTIONS] = {
482 	"Loading",		// GR_640
483 	"2_Loading"		// GR_1024
484 };
485 
486 static char *Game_title_screen_fname[GR_NUM_RESOLUTIONS] = {
487 	"PreLoad",
488 	"2_PreLoad"
489 };
490 
491 static char *Game_logo_screen_fname[GR_NUM_RESOLUTIONS] = {
492 	"PreLoadLogo",
493 	"2_PreLoadLogo"
494 };
495 
496 // for title screens
497 static int Game_title_bitmap = -1;
498 static int Game_title_logo = -1;
499 
500 // cdrom stuff
501 char Game_CDROM_dir[MAX_PATH_LEN];
502 int init_cdrom();
503 
504 // How much RAM is on this machine. Set in WinMain
505 uint FreeSpace_total_ram = 0;
506 
507 // game flash stuff
508 float Game_flash_red = 0.0f;
509 float Game_flash_green = 0.0f;
510 float Game_flash_blue = 0.0f;
511 float Sun_spot = 0.0f;
512 big_expl_flash Big_expl_flash = {0.0f, 0.0f, 0};
513 
514 // game shudder stuff (in ms)
515 int Game_shudder_time = -1;
516 int Game_shudder_total = 0;
517 float Game_shudder_intensity = 0.0f;			// should be between 0.0 and 100.0
518 
519 // EAX stuff
520 sound_env Game_sound_env;
521 sound_env Game_default_sound_env = { EAX_ENVIRONMENT_BATHROOM, 0.2f, 0.2f, 1.0f };
522 int Game_sound_env_update_timestamp;
523 
524 
game_find_builtin_mission(char * filename)525 fs_builtin_mission *game_find_builtin_mission(char *filename)
526 {
527 	int idx;
528 
529 	// look through all existing builtin missions
530 	for(idx=0; idx<Game_builtin_mission_count; idx++){
531 		if(!stricmp(Game_builtin_mission_list[idx].filename, filename)){
532 			return &Game_builtin_mission_list[idx];
533 		}
534 	}
535 
536 	// didn't find it
537 	return NULL;
538 }
539 
game_get_default_skill_level()540 int game_get_default_skill_level()
541 {
542 	return DEFAULT_SKILL_LEVEL;
543 }
544 
545 // Resets the flash
game_flash_reset()546 void game_flash_reset()
547 {
548 	Game_flash_red = 0.0f;
549 	Game_flash_green = 0.0f;
550 	Game_flash_blue = 0.0f;
551 	Sun_spot = 0.0f;
552 	Big_expl_flash.max_flash_intensity = 0.0f;
553 	Big_expl_flash.cur_flash_intensity = 0.0f;
554 	Big_expl_flash.flash_start = 0;
555 }
556 
557 float Gf_critical = -1.0f;					// framerate we should be above on the average for this mission
558 float Gf_critical_time = 0.0f;			// how much time we've been at the critical framerate
559 
game_framerate_check_init()560 void game_framerate_check_init()
561 {
562 	// zero critical time
563 	Gf_critical_time = 0.0f;
564 
565 	// nebula missions
566 	if(The_mission.flags & MISSION_FLAG_FULLNEB){
567 		Gf_critical = 15.0f;
568 	} else {
569 		Gf_critical = 25.0f;
570 	}
571 }
572 
573 extern float Framerate;
game_framerate_check()574 void game_framerate_check()
575 {
576 	int y_start = 100;
577 
578 	// if the current framerate is above the critical level, add frametime
579 	if(Framerate >= Gf_critical){
580 		Gf_critical_time += flFrametime;
581 	}
582 
583 	if (!Show_framerate) {
584 		return;
585 	}
586 
587 	// display if we're above the critical framerate
588 	if(Framerate < Gf_critical){
589 		gr_set_color_fast(&Color_bright_red);
590 		gr_string(200, y_start, "Framerate warning", GR_RESIZE_NONE);
591 
592 		y_start += 10;
593 	}
594 
595 	// display our current pct of good frametime
596 	if(f2fl(Missiontime) >= 0.0f){
597 		float pct = (Gf_critical_time / f2fl(Missiontime)) * 100.0f;
598 
599 		if(pct >= 85.0f){
600 			gr_set_color_fast(&Color_bright_green);
601 		} else {
602 			gr_set_color_fast(&Color_bright_red);
603 		}
604 
605 		gr_printf_no_resize(200, y_start, "%d%%", (int)pct);
606 
607 		y_start += 10;
608 	}
609 }
610 
611 
612 /**
613  * Adds a flash effect.
614  *
615  * These can be positive or negative. The range will get capped at around -1 to 1, so stick
616  * with a range like that.
617  *
618  * @param r red colour value
619  * @param g green colour value
620  * @param b blue colour value
621  */
game_flash(float r,float g,float b)622 void game_flash( float r, float g, float b )
623 {
624 	Game_flash_red += r;
625 	Game_flash_green += g;
626 	Game_flash_blue += b;
627 
628 	if ( Game_flash_red < -1.0f )	{
629 		Game_flash_red = -1.0f;
630 	} else if ( Game_flash_red > 1.0f )	{
631 		Game_flash_red = 1.0f;
632 	}
633 
634 	if ( Game_flash_green < -1.0f )	{
635 		Game_flash_green = -1.0f;
636 	} else if ( Game_flash_green > 1.0f )	{
637 		Game_flash_green = 1.0f;
638 	}
639 
640 	if ( Game_flash_blue < -1.0f )	{
641 		Game_flash_blue = -1.0f;
642 	} else if ( Game_flash_blue > 1.0f )	{
643 		Game_flash_blue = 1.0f;
644 	}
645 
646 }
647 
648 /**
649  * Adds a flash for Big Ship explosions
650  * @param flash flash intensity. Range capped from 0 to 1.
651  */
big_explosion_flash(float flash)652 void big_explosion_flash(float flash)
653 {
654 	CLAMP(flash, 0.0f, 1.0f);
655 
656 	Big_expl_flash.flash_start = timestamp(1);
657 	Big_expl_flash.max_flash_intensity = flash;
658 	Big_expl_flash.cur_flash_intensity = 0.0f;
659 }
660 
661 //	Amount to diminish palette towards normal, per second.
662 #define	DIMINISH_RATE	0.75f
663 #define	SUN_DIMINISH_RATE	6.00f
664 
665 int Sun_drew = 0;
666 
667 float sn_glare_scale = 1.7f;
668 DCF(sn_glare, "")
669 {
670 	dc_get_arg(ARG_FLOAT);
671 	sn_glare_scale = Dc_arg_float;
672 }
673 
674 float Supernova_last_glare = 0.0f;
675 bool stars_sun_has_glare(int index);
676 extern bool ls_on;
677 extern bool ls_force_off;
game_sunspot_process(float frametime)678 void game_sunspot_process(float frametime)
679 {
680 	int n_lights, idx;
681 	int sn_stage;
682 	float Sun_spot_goal = 0.0f;
683 
684 	// supernova
685 	sn_stage = supernova_active();
686 	if(sn_stage){
687 		// sunspot differently based on supernova stage
688 		switch(sn_stage){
689 		// approaching. player still in control
690 		case 1:
691 			float pct;
692 			pct = (1.0f - (supernova_time_left() / SUPERNOVA_CUT_TIME));
693 
694 			vec3d light_dir;
695 			light_get_global_dir(&light_dir, 0);
696 			float dot;
697 			dot = vm_vec_dot( &light_dir, &Eye_matrix.vec.fvec );
698 
699 			if(dot >= 0.0f){
700 				// scale it some more
701 				dot = dot * (0.5f + (pct * 0.5f));
702 				dot += 0.05f;
703 
704 				Sun_spot_goal += (dot * sn_glare_scale);
705 			}
706 
707 			// draw the sun glow
708 			if ( !shipfx_eye_in_shadow( &Eye_position, Viewer_obj, 0 ) )	{
709 				// draw the glow for this sun
710 				stars_draw_sun_glow(0);
711 			}
712 
713 			Supernova_last_glare = Sun_spot_goal;
714 			break;
715 
716 		// camera cut. player not in control. note : at this point camera starts out facing the sun. so we can go nice and bright
717 		case 2:
718 		case 3:
719 			Sun_spot_goal = 0.9f;
720 			Sun_spot_goal += (1.0f - (supernova_time_left() / SUPERNOVA_CUT_TIME)) * 0.1f;
721 
722 			if(Sun_spot_goal > 1.0f){
723 				Sun_spot_goal = 1.0f;
724 			}
725 
726 			Sun_spot_goal *= sn_glare_scale;
727 			Supernova_last_glare = Sun_spot_goal;
728 			break;
729 
730 		// fade to white. display dead popup
731 		case 4:
732 		case 5:
733 			Supernova_last_glare += (2.0f * flFrametime);
734 			if(Supernova_last_glare > 2.0f){
735 				Supernova_last_glare = 2.0f;
736 			}
737 
738 			Sun_spot_goal = Supernova_last_glare;
739 			break;
740 		}
741 
742 		Sun_drew = 0;
743 	} else {
744 		Sun_spot_goal = 0.0f;
745 		if ( Sun_drew )	{
746 			// check sunspots for all suns
747 			n_lights = light_get_global_count();
748 
749 			// check
750 			for(idx=0; idx<n_lights; idx++)	{
751 				if ( (ls_on && !ls_force_off) || !shipfx_eye_in_shadow( &Eye_position, Viewer_obj, idx ) )	{
752 					vec3d light_dir;
753 					light_get_global_dir(&light_dir, idx);
754 
755 					//only do sunglare stuff if this sun has one
756 					if (stars_sun_has_glare(idx))	{
757 						float dot = vm_vec_dot( &light_dir, &Eye_matrix.vec.fvec )*0.5f+0.5f;
758 						Sun_spot_goal += (float)pow(dot,85.0f);
759 					}
760 				}
761 			if (!shipfx_eye_in_shadow( &Eye_position, Viewer_obj, idx ) )	{
762 			// draw the glow for this sun
763 			stars_draw_sun_glow(idx);
764 			}
765 			}
766 
767 			Sun_drew = 0;
768 		}
769 	}
770 
771 	float dec_amount = frametime*SUN_DIMINISH_RATE;
772 
773 	if ( Sun_spot < Sun_spot_goal )	{
774 		Sun_spot += dec_amount;
775 		if ( Sun_spot > Sun_spot_goal )	{
776 			Sun_spot = Sun_spot_goal;
777 		}
778 	} else if ( Sun_spot > Sun_spot_goal )	{
779 		Sun_spot -= dec_amount;
780 		if ( Sun_spot < Sun_spot_goal )	{
781 			Sun_spot = Sun_spot_goal;
782 		}
783 	}
784 }
785 
786 
787 /**
788  * Call once a frame to diminish the flash effect to 0.
789  * @param frametime Period over which to dimish at ::DIMINISH_RATE
790  */
game_flash_diminish(float frametime)791 void game_flash_diminish(float frametime)
792 {
793 	float dec_amount = frametime*DIMINISH_RATE;
794 
795 	if ( Game_flash_red > 0.0f ) {
796 		Game_flash_red -= dec_amount;
797 		if ( Game_flash_red < 0.0f )
798 			Game_flash_red = 0.0f;
799 	} else {
800 		Game_flash_red += dec_amount;
801 		if ( Game_flash_red > 0.0f )
802 			Game_flash_red = 0.0f;
803 	}
804 
805 	if ( Game_flash_green > 0.0f ) {
806 		Game_flash_green -= dec_amount;
807 		if ( Game_flash_green < 0.0f )
808 			Game_flash_green = 0.0f;
809 	} else {
810 		Game_flash_green += dec_amount;
811 		if ( Game_flash_green > 0.0f )
812 			Game_flash_green = 0.0f;
813 	}
814 
815 	if ( Game_flash_blue > 0.0f ) {
816 		Game_flash_blue -= dec_amount;
817 		if ( Game_flash_blue < 0.0f )
818 			Game_flash_blue = 0.0f;
819 	} else {
820 		Game_flash_blue += dec_amount;
821 		if ( Game_flash_blue > 0.0f )
822 			Game_flash_blue = 0.0f;
823 	}
824 
825 	// update big_explosion_cur_flash
826 #define	TIME_UP		1500
827 #define	TIME_DOWN	2500
828 	int duration = TIME_UP + TIME_DOWN;
829 	int time = timestamp_until(Big_expl_flash.flash_start);
830 	if (time > -duration) {
831 		time = -time;
832 		if (time < TIME_UP) {
833 			Big_expl_flash.cur_flash_intensity = Big_expl_flash.max_flash_intensity * time / (float) TIME_UP;
834 		} else {
835 			time -= TIME_UP;
836 			Big_expl_flash.cur_flash_intensity = Big_expl_flash.max_flash_intensity * ((float) TIME_DOWN - time) / (float) TIME_DOWN;
837 		}
838 	}
839 
840 	if ( Use_palette_flash )	{
841 		int r,g,b;
842 
843 		// Change the 200 to change the color range of colors.
844 		r = fl2i( Game_flash_red*128.0f );
845 		g = fl2i( Game_flash_green*128.0f );
846 		b = fl2i( Game_flash_blue*128.0f );
847 
848 		if ( Sun_spot > 0.0f && (!ls_on || ls_force_off))	{
849 			r += fl2i(Sun_spot*128.0f);
850 			g += fl2i(Sun_spot*128.0f);
851 			b += fl2i(Sun_spot*128.0f);
852 		}
853 
854 		if ( Big_expl_flash.cur_flash_intensity  > 0.0f ) {
855 			r += fl2i(Big_expl_flash.cur_flash_intensity*128.0f);
856 			g += fl2i(Big_expl_flash.cur_flash_intensity*128.0f);
857 			b += fl2i(Big_expl_flash.cur_flash_intensity*128.0f);
858 		}
859 
860 		if ( r < 0 ) r = 0; else if ( r > 255 ) r = 255;
861 		if ( g < 0 ) g = 0; else if ( g > 255 ) g = 255;
862 		if ( b < 0 ) b = 0; else if ( b > 255 ) b = 255;
863 
864 		if ( (r!=0) || (g!=0) || (b!=0) ) {
865 			gr_flash( r, g, b );
866 		}
867 	}
868 
869 }
870 
871 
game_level_close()872 void game_level_close()
873 {
874 	//WMC - this is actually pretty damn dangerous, but I don't want a modder
875 	//to accidentally use an override here without realizing it.
876 	if(!Script_system.IsConditionOverride(CHA_MISSIONEND))
877 	{
878 		// save player-persistent variables
879 		mission_campaign_save_player_persistent_variables();	// Goober5000
880 
881 		// De-Initialize the game subsystems
882 		sexp_music_close();	// Goober5000
883 		event_music_level_close();
884 		game_stop_looped_sounds();
885 		snd_stop_all();
886 		obj_snd_level_close();					// uninit object-linked persistant sounds
887 		gamesnd_unload_gameplay_sounds();	// unload gameplay sounds from memory
888 		anim_level_close();						// stop and clean up any anim instances
889 		message_mission_shutdown();			// called after anim_level_close() to make sure instances are clear
890 		shockwave_level_close();
891 		fireball_close();
892 		shield_hit_close();
893 		mission_event_shutdown();
894 		asteroid_level_close();
895 		jumpnode_level_close();
896 		waypoint_level_close();
897 		flak_level_close();						// unload flak stuff
898 		neb2_level_close();						// shutdown gaseous nebula stuff
899 		ct_level_close();
900 		beam_level_close();
901 		mflash_level_close();
902 		mission_brief_common_reset();		// close out parsed briefing/mission stuff
903 		cam_close();
904 		subtitles_close();
905 		particle_close();
906 		trail_level_close();
907 		ship_clear_cockpit_displays();
908 		hud_level_close();
909 		model_instance_free_all();
910 		batch_render_close();
911 
912 		// be sure to not only reset the time but the lock as well
913 		set_time_compression(1.0f, 0.0f);
914 		lock_time_compression(false);
915 
916 		audiostream_unpause_all();
917 		Game_paused = 0;
918 
919 		if (gr_screen.envmap_render_target >= 0) {
920 			if ( bm_release(gr_screen.envmap_render_target, 1) ) {
921 				gr_screen.envmap_render_target = -1;
922 			}
923 		}
924 
925 		gr_set_ambient_light(120, 120, 120);
926 
927 		ENVMAP = Default_env_map;
928 	}
929 	else
930 	{
931 		Error(LOCATION, "Scripting Mission End override is not fully supported yet.");
932 	}
933 
934 	Script_system.RunCondition(CHA_MISSIONEND);
935 }
936 
937 uint load_gl_init;
938 uint load_mission_load;
939 uint load_post_level_init;
940 
941 /**
942  * Intializes game stuff.
943  *
944  * @return 0 on failure, 1 on success
945  */
game_level_init(int seed)946 void game_level_init(int seed)
947 {
948 	game_busy( NOX("** starting game_level_init() **") );
949 	load_gl_init = (uint) time(NULL);
950 	// seed the random number generator
951 	if ( seed == -1 ) {
952 		// if no seed was passed, seed the generator either from the time value, or from the
953 		// netgame security flags -- ensures that all players in multiplayer game will have the
954 		// same randon number sequence (with static rand functions)
955 		if ( Game_mode & GM_NORMAL ) {
956 			Game_level_seed = (int) time(NULL);
957 		} else {
958 			Game_level_seed = Netgame.security;
959 		}
960 	} else {
961 		Assert( !(Game_mode & GM_MULTIPLAYER) );
962 		Game_level_seed = seed;
963 	}
964 	srand( Game_level_seed );
965 
966 	// semirand function needs to get re-initted every time in multiplayer
967 	if ( Game_mode & GM_MULTIPLAYER ){
968 		init_semirand();
969 	}
970 
971 	Framecount = 0;
972 	game_reset_view_clip();
973 	game_reset_shade_frame();
974 
975 	Key_normal_game = (Game_mode & GM_NORMAL);
976 	Cheats_enabled = 0;
977 
978 	Game_shudder_time = -1;
979 
980 	Perspective_locked = false;
981 
982 	// reset the geometry map and distortion map batcher, this should to be done pretty soon in this mission load process (though it's not required)
983 	batch_reset();
984 
985 	// Initialize the game subsystems
986 	game_reset_time();			// resets time, and resets saved time too
987 
988 	Multi_ping_timestamp = -1;
989 
990 	obj_init();						// Must be inited before the other systems
991 
992 	if ( !(Game_mode & GM_STANDALONE_SERVER) ) {
993 		model_page_in_start();		// mark any existing models as unused but don't unload them yet
994 		mprintf(( "Beginning level bitmap paging...\n" ));
995 		bm_page_in_start();
996 	} else {
997 		model_free_all();			// Free all existing models if standalone server
998 	}
999 
1000 	mission_brief_common_init();		// Free all existing briefing/debriefing text
1001 	weapon_level_init();
1002 
1003 	NavSystem_Init();				// zero out the nav system
1004 
1005 	ai_level_init();				//	Call this before ship_init() because it reads ai.tbl.
1006 	ship_level_init();
1007 	player_level_init();
1008 	shipfx_flash_init();			// Init the ship gun flash system.
1009 	game_flash_reset();			// Reset the flash effect
1010 	particle_init();				// Reset the particle system
1011 	fireball_init();
1012 	debris_init();
1013 	shield_hit_init();				//	Initialize system for showing shield hits
1014 
1015 	mission_init_goals();
1016 	mission_log_init();
1017 	messages_init();
1018 	obj_snd_level_init();					// init object-linked persistant sounds
1019 	anim_level_init();
1020 	shockwave_level_init();
1021 	afterburner_level_init();
1022 	scoring_level_init( &Player->stats );
1023 	key_level_init();
1024 	asteroid_level_init();
1025 	control_config_clear_used_status();
1026 	collide_ship_ship_sounds_init();
1027 	Missiontime = 0;
1028 	Pre_player_entry = 1;			//	Means the player has not yet entered.
1029 	Entry_delay_time = 0;			//	Could get overwritten in mission read.
1030 	observer_init();
1031 	flak_level_init();				// initialize flak - bitmaps, etc
1032 	ct_level_init();				// initialize ships contrails, etc
1033 	awacs_level_init();				// initialize AWACS
1034 	beam_level_init();				// initialize beam weapons
1035 	mflash_level_init();
1036 	ssm_level_init();
1037 	supernova_level_init();
1038 	cam_init();
1039 	snd_aav_init();
1040 
1041 	// multiplayer dogfight hack
1042 	dogfight_blown = 0;
1043 
1044 	shipfx_engine_wash_level_init();
1045 
1046 	stars_pre_level_init();
1047 	neb2_level_init();
1048 	nebl_level_init();
1049 
1050 	Last_view_target = NULL;
1051 	Game_paused = 0;
1052 
1053 	Game_no_clear = 0;
1054 
1055 	// campaign wasn't ended
1056 	Campaign_ending_via_supernova = 0;
1057 
1058 	Env_cubemap_drawn = false;
1059 
1060 	load_gl_init = (uint) (time(NULL) - load_gl_init);
1061 
1062 	//WMC - Init multi players for level
1063 	if (Game_mode & GM_MULTIPLAYER && Player != NULL) {
1064 		Player->flags |= PLAYER_FLAGS_IS_MULTI;
1065 
1066 		// clear multiplayer stats
1067 		init_multiplayer_stats();
1068 	}
1069 }
1070 
1071 /**
1072  * Called when a mission is over -- does server specific stuff.
1073  */
freespace_stop_mission()1074 void freespace_stop_mission()
1075 {
1076 	game_level_close();
1077 	Game_mode &= ~GM_IN_MISSION;
1078 }
1079 
1080 /**
1081  * Called at frame interval to process networking stuff
1082  */
game_do_networking()1083 void game_do_networking()
1084 {
1085 	Assert( Net_player != NULL );
1086 	if (!(Game_mode & GM_MULTIPLAYER)){
1087 		return;
1088 	}
1089 
1090 	// see if this player should be reading/writing data.  Bit is set when at join
1091 	// screen onward until quits back to main menu.
1092 	if ( !(Net_player->flags & NETINFO_FLAG_DO_NETWORKING) ){
1093 		return;
1094 	}
1095 
1096 	if(gameseq_get_state()!=GS_STATE_MULTI_PAUSED){
1097 		multi_do_frame();
1098 	} else {
1099 		multi_pause_do_frame();
1100 	}
1101 }
1102 
1103 // An estimate as to how high the count passed to game_loading_callback will go.
1104 // This is just a guess, it seems to always be about the same.   The count is
1105 // proportional to the code being executed, not the time, so this works good
1106 // for a bar, assuming the code does about the same thing each time you
1107 // load a level.   You can find this value by looking at the return value
1108 // of game_busy_callback(NULL), which I conveniently print out to the
1109 // debug output window with the '=== ENDING LOAD ==' stuff.
1110 #define COUNT_ESTIMATE 425
1111 
1112 int Game_loading_callback_inited = 0;
1113 int Game_loading_background = -1;
1114 generic_anim Game_loading_ani;
1115 
1116 static int Game_loading_ani_coords[GR_NUM_RESOLUTIONS][2] = {
1117 	{
1118 		63, 316  // GR_640
1119 	},
1120 	{
1121 		101, 505	// GR_1024
1122 	}
1123 };
1124 
1125 #ifndef NDEBUG
1126 extern char Processing_filename[MAX_PATH_LEN];
1127 static int busy_shader_created = 0;
1128 shader busy_shader;
1129 #endif
1130 static int framenum;
1131 
1132 /**
1133  * This gets called 10x per second and count is the number of times ::game_busy() has been
1134  * called since the current callback function was set.
1135  */
game_loading_callback(int count)1136 void game_loading_callback(int count)
1137 {
1138 	int new_framenum;
1139 	game_do_networking();
1140 
1141 	Assert( Game_loading_callback_inited==1 );
1142 	Assertion( Game_loading_ani.num_frames > 0, "Load Screen animation %s not found, or corrupted. Needs to be an animation with at least 1 frame.", Game_loading_ani.filename );
1143 
1144 	int do_flip = 0;
1145 
1146 	new_framenum = ((Game_loading_ani.num_frames*count) / COUNT_ESTIMATE)+1;
1147 	if ( new_framenum > Game_loading_ani.num_frames-1 )	{
1148 		new_framenum = Game_loading_ani.num_frames-1;
1149 	} else if ( new_framenum < 0 )	{
1150 		new_framenum = 0;
1151 	}
1152 	//make sure we always run forwards - graphical hack
1153 	if(new_framenum > framenum)
1154 		framenum = new_framenum;
1155 
1156 	if ( Game_loading_ani.num_frames > 0 )	{
1157 		GR_MAYBE_CLEAR_RES(Game_loading_background);
1158 		if ( Game_loading_background > -1 )	{
1159 			gr_set_bitmap( Game_loading_background );
1160 			gr_bitmap(0,0,GR_RESIZE_MENU);
1161 		}
1162 
1163 		gr_set_bitmap( Game_loading_ani.first_frame + framenum );
1164 		gr_bitmap(Game_loading_ani_coords[gr_screen.res][0],Game_loading_ani_coords[gr_screen.res][1], GR_RESIZE_MENU);
1165 
1166 		do_flip = 1;
1167 	}
1168 
1169 #ifndef NDEBUG
1170 	// print the current filename being processed by game_busy(), the shader here is a quick hack
1171 	// since the background isn't always drawn so we can't clear the text away from the previous
1172 	// filename. the shader is completely opaque to hide the old text. must easier and faster than
1173 	// redrawing the entire screen every flip - taylor
1174 	if (!busy_shader_created) {
1175 		gr_create_shader(&busy_shader, 5, 5, 5, 255);
1176 		busy_shader_created = 1;
1177 	}
1178 
1179 	if (Processing_filename[0] != '\0') {
1180 		gr_set_shader(&busy_shader);
1181 		gr_shade(0, 0, gr_screen.clip_width_unscaled, 17, GR_RESIZE_MENU); // make sure it goes across the entire width
1182 
1183 		gr_set_color_fast(&Color_white);
1184 		gr_string(5, 5, Processing_filename, GR_RESIZE_MENU);
1185 
1186 		do_flip = 1;
1187 		memset( Processing_filename, 0, MAX_PATH_LEN );
1188 	}
1189 #endif
1190 
1191 #ifndef NDEBUG
1192 	if(Cmdline_show_mem_usage)
1193 	{
1194 #ifdef _WIN32
1195 		void memblockinfo_sort();
1196 		void memblockinfo_sort_get_entry(int index, char *filename, int *size);
1197 
1198 		char mem_buffer[1000];
1199 		char filename[35];
1200 		int size;
1201 		int i;
1202 	  	memblockinfo_sort();
1203 		for(i = 0; i < 30; i++)
1204 		{
1205 			memblockinfo_sort_get_entry(i, filename, &size);
1206 
1207 			size /= 1024;
1208 
1209 			if(size == 0)
1210 				break;
1211 
1212 			char *short_name = strrchr(filename, '\\');
1213 			if(short_name == NULL)
1214 				short_name = filename;
1215 			else
1216 				short_name++;
1217 
1218 			sprintf(mem_buffer,"%s:\t%d K", short_name, size);
1219 			gr_string( 20, 220 + (i*10), mem_buffer, GR_RESIZE_MENU);
1220 		}
1221 		sprintf(mem_buffer,"Total RAM:\t%d K", TotalRam / 1024);
1222 		gr_string( 20, 230 + (i*10), mem_buffer, GR_RESIZE_MENU);
1223 #endif	// _WIN32
1224 	}
1225 #endif	// !NDEBUG
1226 
1227 	if (do_flip)
1228 		gr_flip();
1229 }
1230 
game_loading_callback_init()1231 void game_loading_callback_init()
1232 {
1233 	Assert( Game_loading_callback_inited==0 );
1234 
1235 	Game_loading_background = bm_load(The_mission.loading_screen[gr_screen.res]);
1236 
1237 	if (Game_loading_background < 0)
1238 		Game_loading_background = bm_load(Game_loading_bground_fname[gr_screen.res]);
1239 
1240 	generic_anim_init(&Game_loading_ani, Game_loading_ani_fname[gr_screen.res]);
1241 	generic_anim_load(&Game_loading_ani);
1242 	Assertion( Game_loading_ani.num_frames > 0, "Load Screen animation %s not found, or corrupted. Needs to be an animation with at least 1 frame.", Game_loading_ani.filename );
1243 
1244 	Game_loading_callback_inited = 1;
1245 	Mouse_hidden = 1;
1246 	framenum = 0;
1247 	game_busy_callback( game_loading_callback, (COUNT_ESTIMATE/Game_loading_ani.num_frames)+1 );
1248 
1249 
1250 }
1251 
game_loading_callback_close()1252 void game_loading_callback_close()
1253 {
1254 	Assert( Game_loading_callback_inited==1 );
1255 
1256 	// Make sure bar shows all the way over.
1257 	game_loading_callback(COUNT_ESTIMATE);
1258 
1259 	int real_count = game_busy_callback( NULL );
1260  	Mouse_hidden = 0;
1261 
1262 	Game_loading_callback_inited = 0;
1263 
1264 #ifndef NDEBUG
1265 	mprintf(( "=================== ENDING LOAD ================\n" ));
1266 	mprintf(( "Real count = %d,  Estimated count = %d\n", real_count, COUNT_ESTIMATE ));
1267 	mprintf(( "================================================\n" ));
1268 #else
1269 	// to remove warnings in release build
1270 	real_count = 0;
1271 #endif
1272 
1273 	generic_anim_unload(&Game_loading_ani);
1274 
1275 	bm_release( Game_loading_background );
1276 	common_free_interface_palette();		// restore game palette
1277 	Game_loading_background = -1;
1278 
1279 	gr_set_font( FONT1 );
1280 }
1281 
1282 /**
1283  * Update the sound environment (ie change EAX settings based on proximity to large ships)
1284  */
game_maybe_update_sound_environment()1285 void game_maybe_update_sound_environment()
1286 {
1287 	// do nothing for now
1288 }
1289 
1290 /**
1291  * Assign the sound environment for the game, based on the current mission
1292  */
game_assign_sound_environment()1293 void game_assign_sound_environment()
1294 {
1295 	if (The_mission.sound_environment.id >= 0) {
1296 		Game_sound_env = The_mission.sound_environment;
1297 	} else if (SND_ENV_DEFAULT > 0) {
1298 		sound_env_get(&Game_sound_env, SND_ENV_DEFAULT);
1299 	} else {
1300 		Game_sound_env = Game_default_sound_env;
1301 	}
1302 
1303 	Game_sound_env_update_timestamp = timestamp(1);
1304 }
1305 
1306 /**
1307  * Function which gets called before actually entering the mission.
1308  */
freespace_mission_load_stuff()1309 void freespace_mission_load_stuff()
1310 {
1311 	// called if we're not on a freespace dedicated (non rendering, no pilot) server
1312 	// IE : we _don't_ want to load any sounds or bitmap/texture info on this machine.
1313 	if(!(Game_mode & GM_STANDALONE_SERVER)){
1314 
1315 		mprintf(( "=================== STARTING LEVEL DATA LOAD ==================\n" ));
1316 
1317 		game_busy( NOX("** setting up event music **") );
1318 		event_music_level_init(-1);	// preloads the first 2 seconds for each event music track
1319 
1320 		game_busy( NOX("** unloading interface sounds **") );
1321 		gamesnd_unload_interface_sounds();		// unload interface sounds from memory
1322 
1323 		game_busy( NOX("** preloading common game sounds **") );
1324 		gamesnd_preload_common_sounds();			// load in sounds that are expected to play
1325 
1326 		if (Cmdline_snd_preload) {
1327 			game_busy( NOX("** preloading gameplay sounds **") );
1328 			gamesnd_load_gameplay_sounds();			// preload in gameplay sounds if wanted
1329 		}
1330 
1331 		game_busy( NOX("** assigning sound environment for mission **") );
1332 		ship_assign_sound_all();	// assign engine sounds to ships
1333 		game_assign_sound_environment();	 // assign the sound environment for this mission
1334 
1335 		obj_merge_created_list();
1336 
1337 		if (!(Game_mode & GM_MULTIPLAYER)) {
1338 			// call function in missionparse.cpp to fixup player/ai stuff.
1339 			game_busy( NOX("** fixing up player/ai stuff **") );
1340 			mission_parse_fixup_players();
1341 		}
1342 
1343 		// Load in all the bitmaps for this level
1344 		level_page_in();
1345 
1346 		game_busy( NOX("** finished with level_page_in() **") );
1347 
1348 		if(Game_loading_callback_inited) {
1349 			game_loading_callback_close();
1350 		}
1351 	}
1352 	// the only thing we need to call on the standalone for now.
1353 	else {
1354 		obj_merge_created_list();
1355 
1356 		// Load in all the bitmaps for this level
1357 		level_page_in();
1358 	}
1359 }
1360 
1361 /**
1362  * Called after mission is loaded.
1363  *
1364  * Because player isn't created until after mission loads, some things must get initted after the level loads
1365  */
game_post_level_init()1366 void game_post_level_init()
1367 {
1368 	extern void game_environment_map_gen();
1369 	game_environment_map_gen();
1370 
1371  	HUD_init();
1372 	hud_setup_escort_list();
1373 	mission_hotkey_set_defaults();	// set up the default hotkeys (from mission file)
1374 
1375 	stars_post_level_init();
1376 
1377 	// While trying to track down the nebula bug I encountered a cool effect -
1378 	// comment this out to fly a mission in a void. Maybe we should develop this
1379 	// into a full effect or something, because it is seriously cool.
1380 	neb2_post_level_init();
1381 
1382 #ifndef NDEBUG
1383 	game_event_debug_init();
1384 #endif
1385 
1386 	training_mission_init();
1387 	asteroid_create_all();
1388 
1389 	// set ambient light for level
1390 	gr_set_ambient_light(The_mission.ambient_light_level & 0xff,
1391 							(The_mission.ambient_light_level >> 8) & 0xff,
1392 							(The_mission.ambient_light_level >> 16) & 0xff);
1393 
1394 	game_framerate_check_init();
1395 
1396 	// If this is a red alert mission in campaign mode, bash wingman status
1397 	if ( (Game_mode & GM_CAMPAIGN_MODE) && red_alert_mission() ) {
1398 		red_alert_bash_wingman_status();
1399 	}
1400 
1401 	freespace_mission_load_stuff();
1402 
1403 	// m!m Make hv.Player available in "On Mission Start" hook
1404 	if(Player_obj)
1405 		Script_system.SetHookObject("Player", Player_obj);
1406 
1407 	// HACK: That scripting hook should be in mission so GM_IN_MISSION has to be set
1408 	Game_mode |= GM_IN_MISSION;
1409 	Script_system.RunCondition(CHA_MISSIONSTART);
1410 	Game_mode &= ~GM_IN_MISSION;
1411 
1412 	if (Player_obj)
1413 		Script_system.RemHookVar("Player");
1414 }
1415 
1416 /**
1417  * Tells the server to load the mission and initialize structures
1418  */
game_start_mission()1419 int game_start_mission()
1420 {
1421 	mprintf(( "=================== STARTING LEVEL LOAD ==================\n" ));
1422 
1423 	// clear post processing settings
1424 	gr_post_process_set_defaults();
1425 
1426 	get_mission_info(Game_current_mission_filename, &The_mission, false);
1427 
1428 	if ( !(Game_mode & GM_STANDALONE_SERVER) )
1429 		game_loading_callback_init();
1430 
1431 	game_level_init();
1432 
1433 	if (Game_mode & GM_MULTIPLAYER) {
1434 		Player->flags |= PLAYER_FLAGS_IS_MULTI;
1435 
1436 		// clear multiplayer stats
1437 		init_multiplayer_stats();
1438 	}
1439 
1440 	game_busy( NOX("** starting mission_load() **") );
1441 	load_mission_load = (uint) time(NULL);
1442 	if (mission_load(Game_current_mission_filename)) {
1443 		if ( !(Game_mode & GM_MULTIPLAYER) ) {
1444 			popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR( "Attempt to load the mission failed", 169));
1445 			gameseq_post_event(GS_EVENT_MAIN_MENU);
1446 		} else {
1447 			multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_LOAD_FAIL);
1448 		}
1449 
1450 		if ( !(Game_mode & GM_STANDALONE_SERVER) ) {
1451 			game_loading_callback_close();
1452 		}
1453 
1454 		game_level_close();
1455 
1456 		return 0;
1457 	}
1458 	load_mission_load = (uint) (time(NULL) - load_mission_load);
1459 
1460 	// free up memory from parsing the mission
1461 	extern void stop_parse();
1462 	stop_parse();
1463 
1464 	game_busy( NOX("** starting game_post_level_init() **") );
1465 	load_post_level_init = (uint) time(NULL);
1466 	game_post_level_init();
1467 	load_post_level_init = (uint) (time(NULL) - load_post_level_init);
1468 
1469 #ifndef NDEBUG
1470 	{
1471 		void Do_model_timings_test();
1472 		Do_model_timings_test();
1473 	}
1474 #endif
1475 
1476 	bm_print_bitmaps();
1477 	return 1;
1478 }
1479 
1480 int Interface_framerate = 0;
1481 #ifndef NDEBUG
1482 
1483 DCF_BOOL( mouse_control, Use_mouse_to_fly )
1484 DCF_BOOL( show_framerate, Show_framerate )
1485 DCF_BOOL( show_target_debug_info, Show_target_debug_info )
1486 DCF_BOOL( show_target_weapons, Show_target_weapons )
1487 DCF_BOOL( lead_target_cheat, Players[Player_num].lead_target_cheat )
1488 DCF_BOOL( sound, Sound_enabled )
1489 DCF_BOOL( zbuffer, game_zbuffer )
1490 DCF_BOOL( show_shield_mesh, Show_shield_mesh)
1491 DCF_BOOL( player_attacking, Player_attacking_enabled )
1492 DCF_BOOL( show_waypoints, Show_waypoints )
1493 DCF_BOOL( show_area_effect, Show_area_effect )
1494 DCF_BOOL( show_net_stats, Show_net_stats )
1495 DCF_BOOL( log, Log_debug_output_to_file )
1496 extern int Training_message_method;
DCF_BOOL(training_msg_method,Training_message_method)1497 DCF_BOOL( training_msg_method, Training_message_method )
1498 DCF_BOOL( show_player_pos, Show_player_pos )
1499 DCF_BOOL(i_framerate, Interface_framerate )
1500 
1501 DCF(warp, "Tests warpin effect")
1502 {
1503 	if ( Dc_command )	{
1504 		bool warpin = true;
1505 		int idx = -1;
1506 
1507 		dc_get_arg(ARG_TRUE|ARG_FALSE|ARG_NONE);
1508 		if( Dc_arg_type & ARG_TRUE) warpin = true;
1509 		else if(Dc_arg_type & ARG_FALSE) warpin = false;
1510 
1511 		if(!(Dc_arg_type & ARG_NONE))
1512 		{
1513 			dc_get_arg(ARG_STRING|ARG_NONE);
1514 			if(Dc_arg_type & ARG_STRING)
1515 			{
1516 				idx = ship_name_lookup(Dc_arg);
1517 				if(idx > -1)
1518 				{
1519 					if(warpin)
1520 						shipfx_warpin_start(&Objects[Ships[idx].objnum]);
1521 					else
1522 						shipfx_warpout_start(&Objects[Ships[idx].objnum]);
1523 				}
1524 			}
1525 		}
1526 
1527 		if(idx < 0)
1528 		{
1529 			if(Player_ai->target_objnum > -1)
1530 			{
1531 				if(warpin)
1532 					shipfx_warpin_start(&Objects[Player_ai->target_objnum]);
1533 				else
1534 					shipfx_warpout_start(&Objects[Player_ai->target_objnum]);
1535 			}
1536 		}
1537 	}
1538 	if ( Dc_help )	dc_printf( "Usage: Show_mem\nWarps in if true, out if false, player target unless specific ship is specified\n" );
1539 }
1540 
1541 DCF(show_mem,"Toggles showing mem usage")
1542 {
1543 	if ( Dc_command )	{
1544 		dc_get_arg(ARG_TRUE|ARG_FALSE|ARG_NONE);
1545 		if ( Dc_arg_type & ARG_TRUE )	Show_mem = 1;
1546 		else if ( Dc_arg_type & ARG_FALSE ) Show_mem = 0;
1547 		else if ( Dc_arg_type & ARG_NONE ) Show_mem ^= 1;
1548 
1549 		if ( Show_mem )	{
1550 			Show_cpu = 0;
1551 		}
1552 	}
1553 	if ( Dc_help )	dc_printf( "Usage: Show_mem\nSets show_mem to true or false.  If nothing passed, then toggles it.\n" );
1554 	if ( Dc_status )	{
1555 		dc_printf( "Show_mem is %s\n", (Show_mem?"TRUE":"FALSE") );
1556 		dc_printf( "Show_cpu is %s\n", (Show_cpu?"TRUE":"FALSE") );
1557 	}
1558 }
1559 
1560 DCF(show_cpu,"Toggles showing cpu usage")
1561 {
1562 	if ( Dc_command )	{
1563 		dc_get_arg(ARG_TRUE|ARG_FALSE|ARG_NONE);
1564 		if ( Dc_arg_type & ARG_TRUE )	Show_cpu = 1;
1565 		else if ( Dc_arg_type & ARG_FALSE ) Show_cpu = 0;
1566 		else if ( Dc_arg_type & ARG_NONE ) Show_cpu ^= 1;
1567 
1568 		if ( Show_cpu )	{
1569 			Show_mem = 0;
1570 		}
1571 	}
1572 	if ( Dc_help )	dc_printf( "Usage: Show_cpu\nSets show_cpu to true or false.  If nothing passed, then toggles it.\n" );
1573 	if ( Dc_status )	{
1574 		dc_printf( "Show_mem is %s\n", (Show_mem?"TRUE":"FALSE") );
1575 		dc_printf( "Show_cpu is %s\n", (Show_cpu?"TRUE":"FALSE") );
1576 
1577 	}
1578 }
1579 
1580 #endif
1581 
1582 			int Game_init_seed;
1583 
1584 DCF(use_joy_mouse,"Makes joystick move mouse cursor")
1585 {
1586 	if ( Dc_command )	{
1587 		dc_get_arg(ARG_TRUE|ARG_FALSE|ARG_NONE);
1588 		if ( Dc_arg_type & ARG_TRUE )	Use_joy_mouse = 1;
1589 		else if ( Dc_arg_type & ARG_FALSE ) Use_joy_mouse = 0;
1590 		else if ( Dc_arg_type & ARG_NONE ) Use_joy_mouse ^= 1;
1591 	}
1592 	if ( Dc_help )	dc_printf( "Usage: use_joy_mouse [bool]\nSets use_joy_mouse to true or false.  If nothing passed, then toggles it.\n" );
1593 	if ( Dc_status )	dc_printf( "use_joy_mouse is %s\n", (Use_joy_mouse?"TRUE":"FALSE") );
1594 
1595 	os_config_write_uint( NULL, NOX("JoystickMovesCursor"), Use_joy_mouse );
1596 }
1597 
1598 DCF(palette_flash,"Toggles palette flash effect on/off")
1599 {
1600 	if ( Dc_command )	{
1601 		dc_get_arg(ARG_TRUE|ARG_FALSE|ARG_NONE);
1602 		if ( Dc_arg_type & ARG_TRUE )	Use_palette_flash = 1;
1603 		else if ( Dc_arg_type & ARG_FALSE ) Use_palette_flash = 0;
1604 		else if ( Dc_arg_type & ARG_NONE ) Use_palette_flash ^= 1;
1605 	}
1606 	if ( Dc_help )	dc_printf( "Usage: palette_flash [bool]\nSets palette_flash to true or false.  If nothing passed, then toggles it.\n" );
1607 	if ( Dc_status )	dc_printf( "palette_flash is %s\n", (Use_palette_flash?"TRUE":"FALSE") );
1608 }
1609 
1610 int Use_low_mem = 0;
1611 
1612 DCF(low_mem,"Uses low memory settings regardless of RAM")
1613 {
1614 	if ( Dc_command )	{
1615 		dc_get_arg(ARG_TRUE|ARG_FALSE|ARG_NONE);
1616 		if ( Dc_arg_type & ARG_TRUE )	Use_low_mem = 1;
1617 		else if ( Dc_arg_type & ARG_FALSE ) Use_low_mem = 0;
1618 		else if ( Dc_arg_type & ARG_NONE ) Use_low_mem ^= 1;
1619 	}
1620 	if ( Dc_help )	dc_printf( "Usage: low_mem [bool]\nSets low_mem to true or false.  If nothing passed, then toggles it.\n" );
1621 	if ( Dc_status )	dc_printf( "low_mem is %s\n", (Use_low_mem?"TRUE":"FALSE") );
1622 
1623 	os_config_write_uint( NULL, NOX("LowMem"), Use_low_mem );
1624 }
1625 
1626 
1627 #ifndef NDEBUG
1628 
1629 DCF(force_fullscreen, "Forces game to startup in fullscreen mode")
1630 {
1631 	if ( Dc_command )	{
1632 		dc_get_arg(ARG_TRUE|ARG_FALSE|ARG_NONE);
1633 		if ( Dc_arg_type & ARG_TRUE )	Use_fullscreen_at_startup = 1;
1634 		else if ( Dc_arg_type & ARG_FALSE ) Use_fullscreen_at_startup = 0;
1635 		else if ( Dc_arg_type & ARG_NONE ) Use_fullscreen_at_startup ^= 1;
1636 	}
1637 	if ( Dc_help )	dc_printf( "Usage: force_fullscreen [bool]\nSets force_fullscreen to true or false.  If nothing passed, then toggles it.\n" );
1638 	if ( Dc_status )	dc_printf( "force_fullscreen is %s\n", (Use_fullscreen_at_startup?"TRUE":"FALSE") );
1639 	os_config_write_uint( NULL, NOX("ForceFullscreen"), Use_fullscreen_at_startup );
1640 }
1641 #endif
1642 
1643 int	Framerate_delay = 0;
1644 
1645 float FreeSpace_gamma = 1.0f;
1646 
1647 DCF(gamma,"Sets Gamma factor")
1648 {
1649 	if ( Dc_command )	{
1650 		dc_get_arg(ARG_FLOAT|ARG_NONE);
1651 		if ( Dc_arg_type & ARG_FLOAT )	{
1652 			FreeSpace_gamma = Dc_arg_float;
1653 		} else {
1654 			dc_printf( "Gamma reset to 1.0f\n" );
1655 			FreeSpace_gamma = 1.0f;
1656 		}
1657 		if ( FreeSpace_gamma < 0.1f )	{
1658 			FreeSpace_gamma = 0.1f;
1659 		} else if ( FreeSpace_gamma > 5.0f )	{
1660 			FreeSpace_gamma = 5.0f;
1661 		}
1662 		gr_set_gamma(FreeSpace_gamma);
1663 
1664 		char tmp_gamma_string[32];
1665 		sprintf( tmp_gamma_string, NOX("%.2f"), FreeSpace_gamma );
1666 		os_config_write_string( NULL, NOX("Gamma"), tmp_gamma_string );
1667 	}
1668 
1669 	if ( Dc_help )	{
1670 		dc_printf( "Usage: gamma <float>\n" );
1671 		dc_printf( "Sets gamma in range 1-3, no argument resets to default 1.2\n" );
1672 		Dc_status = 0;	// don't print status if help is printed.  Too messy.
1673 	}
1674 
1675 	if ( Dc_status )	{
1676 		dc_printf( "Gamma = %.2f\n", FreeSpace_gamma );
1677 	}
1678 }
1679 
1680 #ifdef APPLE_APP
1681 char full_path[1024];
1682 #endif
1683 
1684 /**
1685  * Game initialisation
1686  */
game_init()1687 void game_init()
1688 {
1689 	int s1, e1;
1690 	const char *ptr;
1691 	char whee[MAX_PATH_LEN];
1692 
1693 	Game_current_mission_filename[0] = 0;
1694 
1695 	// Moved from rand32, if we're gonna break, break immediately.
1696 	Assert(RAND_MAX == 0x7fff || RAND_MAX >= 0x7ffffffd);
1697 	// seed the random number generator
1698 	Game_init_seed = (int) time(NULL);
1699 	srand( Game_init_seed );
1700 
1701 	Framerate_delay = 0;
1702 
1703 #ifndef NDEBUG
1704 	load_filter_info();
1705 #endif
1706 
1707 	// encrypt stuff
1708 	encrypt_init();
1709 
1710 	// Initialize the timer before the os
1711 	timer_init();
1712 
1713 #ifndef NDEBUG
1714 	outwnd_init(1);
1715 #endif
1716 
1717 	// init os stuff next
1718 	if ( !Is_standalone ) {
1719 		os_init( Osreg_class_name, Osreg_app_name );
1720 	}
1721 	else {
1722 		std_init_os();
1723 	}
1724 
1725 #ifndef NDEBUG
1726 	#if FS_VERSION_REVIS == 0
1727 		mprintf(("FreeSpace 2 Open version: %i.%i.%i\n", FS_VERSION_MAJOR, FS_VERSION_MINOR, FS_VERSION_BUILD));
1728 	#else
1729 		mprintf(("FreeSpace 2 Open version: %i.%i.%i.%i\n", FS_VERSION_MAJOR, FS_VERSION_MINOR, FS_VERSION_BUILD, FS_VERSION_REVIS));
1730 	#endif
1731 
1732 	extern void cmdline_debug_print_cmdline();
1733 	cmdline_debug_print_cmdline();
1734 #endif
1735 
1736 	GetCurrentDirectory(MAX_PATH_LEN-1, whee);
1737 
1738 	strcat_s(whee, DIR_SEPARATOR_STR);
1739 	strcat_s(whee, EXE_FNAME);
1740 
1741 	profile_init();
1742 	//Initialize the libraries
1743 	s1 = timer_get_milliseconds();
1744 
1745 	if ( cfile_init(whee, strlen(Game_CDROM_dir) ? Game_CDROM_dir : NULL) ) {			// initialize before calling any cfopen stuff!!!
1746 		exit(1);
1747 	}
1748 
1749 	e1 = timer_get_milliseconds();
1750 
1751 	// initialize localization module. Make sure this is done AFTER initialzing OS.
1752 	lcl_init( detect_lang() );
1753 	lcl_xstr_init();
1754 
1755 	mod_table_init();		// load in all the mod dependent settings
1756 
1757 	if (Is_standalone) {
1758 		// force off some cmdlines if they are on
1759 		Cmdline_spec = 0;
1760 		Cmdline_glow = 0;
1761 		Cmdline_env = 0;
1762 		Cmdline_3dwarp = 0;
1763 		Cmdline_normal = 0;
1764 
1765 		// now init the standalone server code
1766 		std_init_standalone();
1767 	}
1768 
1769 	// verify that he has a valid ships.tbl (will Game_ships_tbl_valid if so)
1770 	verify_ships_tbl();
1771 
1772 	// verify that he has a valid weapons.tbl
1773 	verify_weapons_tbl();
1774 
1775 
1776 	Use_joy_mouse = 0;
1777 	Use_low_mem = os_config_read_uint( NULL, NOX("LowMem"), 0 );
1778 
1779 #ifndef NDEBUG
1780 	Use_fullscreen_at_startup = os_config_read_uint( NULL, NOX("ForceFullscreen"), 1 );
1781 #endif
1782 
1783 	// change FPS cap if told to do so (for those who can't use vsync or where vsync isn't enough)
1784 	uint max_fps = 0;
1785 	if ( (max_fps = os_config_read_uint(NULL, NOX("MaxFPS"), 0)) != 0 ) {
1786 		if ( (max_fps > 15) && (max_fps < 120) ) {
1787 			Framerate_cap = (int)max_fps;
1788 		}
1789 	}
1790 
1791 	Asteroids_enabled = 1;
1792 
1793 /////////////////////////////
1794 // SOUND INIT START
1795 /////////////////////////////
1796 
1797 	if ( !Is_standalone ) {
1798 		snd_init();
1799 	}
1800 
1801 	if(fsspeech_init() == false) {
1802 		mprintf(("Failed to init speech\n"));
1803 
1804 		if(Cmdline_query_speech)
1805 		{
1806 			if(!fsspeech_was_compiled())
1807 				MessageBox((HWND)os_get_window(), "Speech is not compiled in this build in code.lib", "FS2_Open Warning", MB_ICONWARNING);
1808 			else
1809 				MessageBox((HWND)os_get_window(), "Speech is compiled, but failed to init", "FS2_Open Warning", MB_ICONWARNING);
1810 		}
1811 	} else if(Cmdline_query_speech) {
1812 		// Its bad practice to use a negative type, this is an exceptional case
1813 		fsspeech_play(-1,"Welcome to FS2 open");
1814 		MessageBox((HWND)os_get_window(), "Speech is compiled and initialised and should be working", "FS2_Open Info", MB_OK);
1815 	}
1816 
1817 /////////////////////////////
1818 // SOUND INIT END
1819 /////////////////////////////
1820 
1821 	if ( gr_init() == false ) {
1822 #ifdef _WIN32
1823 		ClipCursor(NULL);
1824 		ShowCursor(TRUE);
1825 		ShowWindow((HWND)os_get_window(),SW_MINIMIZE);
1826 		MessageBox( NULL, "Error intializing graphics!", "Error", MB_OK|MB_TASKMODAL|MB_SETFOREGROUND );
1827 #elif defined(SCP_UNIX)
1828 		fprintf(stderr, "Error initializing graphics!");
1829 
1830 		// the default entry should have been created already if it didn't exist, so if we're here then
1831 		// the current value is invalid and we need to replace it
1832 		os_config_write_string(NULL, NOX("VideocardFs2open"), NOX("OGL -(1024x768)x16 bit"));
1833 
1834 		// courtesy
1835 		fprintf(stderr, "The default video entry is now in place.  Please try running the game again...\n");
1836 		fprintf(stderr, "(edit ~/.fs2_open/fs2_open.ini to change from default resolution)\n");
1837 #endif
1838 		exit(1);
1839 		return;
1840 	}
1841 
1842 // Karajorma - Moved here from the sound init code cause otherwise windows complains
1843 #ifdef FS2_VOICER
1844 	if(Cmdline_voice_recognition)
1845 	{
1846 		bool voiceRectOn = VOICEREC_init((HWND)os_get_window(), WM_RECOEVENT, GRAMMARID1, IDR_CMD_CFG);
1847 
1848 		if(voiceRectOn == false)
1849 		{
1850 			MessageBox((HWND)os_get_window(), "Failed to init voice rec", "Error", MB_OK);
1851 		}
1852 	}
1853 
1854 #endif
1855 
1856 	// D3D's gamma system now works differently. 1.0 is the default value
1857 	ptr = os_config_read_string(NULL, NOX("GammaD3D"), NOX("1.0"));
1858 	FreeSpace_gamma = (float)atof(ptr);
1859 
1860 	script_init();			//WMC
1861 
1862 	gr_font_init();					// loads up all fonts
1863 
1864 	// add title screen
1865 	if(!Is_standalone){
1866 		// #Kazan# - moved this down - WATCH THESE calls - anything that shares code between standalone and normal
1867 		// cannot make gr_* calls in standalone mode because all gr_ calls are NULL pointers
1868 		gr_set_gamma(FreeSpace_gamma);
1869 		game_title_screen_display();
1870 	}
1871 
1872 	// attempt to load up master tracker registry info (login and password)
1873 	Multi_tracker_id = -1;
1874 
1875 	// should we be using this or not?
1876 	Om_tracker_flag = os_config_read_uint( "PXO", "FS2OpenPXO" , 0 );
1877 	// pxo login and password
1878 	ptr = os_config_read_string(NOX("PXO"),NOX("Login"),NULL);
1879 	if(ptr == NULL){
1880 		nprintf(("Network","Error reading in PXO login data\n"));
1881 		strcpy_s(Multi_tracker_login,"");
1882 	} else {
1883 		strcpy_s(Multi_tracker_login,ptr);
1884 	}
1885 	ptr = os_config_read_string(NOX("PXO"),NOX("Password"),NULL);
1886 	if(ptr == NULL){
1887 		nprintf(("Network","Error reading PXO password\n"));
1888 		strcpy_s(Multi_tracker_passwd,"");
1889 	} else {
1890 		strcpy_s(Multi_tracker_passwd,ptr);
1891 	}
1892 
1893 	// pxo squad name and password
1894 	ptr = os_config_read_string(NOX("PXO"),NOX("SquadName"),NULL);
1895 	if(ptr == NULL){
1896 		nprintf(("Network","Error reading in PXO squad name\n"));
1897 		strcpy_s(Multi_tracker_squad_name, "");
1898 	} else {
1899 		strcpy_s(Multi_tracker_squad_name, ptr);
1900 	}
1901 
1902 	// If less than 48MB of RAM, use low memory model.
1903 	if (
1904 #ifdef _WIN32
1905 		(FreeSpace_total_ram < 48*1024*1024) ||
1906 #endif
1907 		Use_low_mem )	{
1908 		mprintf(( "Using normal memory settings...\n" ));
1909 		bm_set_low_mem(1);		// Use every other frame of bitmaps
1910 	} else {
1911 		mprintf(( "Using high memory settings...\n" ));
1912 		bm_set_low_mem(0);		// Use all frames of bitmaps
1913 	}
1914 
1915 	//WMC - Initialize my new GUI system
1916 	//This may seem scary, but it should take up 0 processing time and very little memory
1917 	//as long as it's not being used.
1918 	//Otherwise, it just keeps the parsed interface.tbl in memory.
1919 	GUI_system.ParseClassInfo("interface.tbl");
1920 
1921 	// load non-darkening pixel defs
1922 	palman_load_pixels();
1923 
1924 	iff_init();						// Goober5000 - this must be done even before species_defs :p
1925 	species_init();					// Load up the species defs - this needs to be done FIRST -- Kazan
1926 
1927 	brief_parse_icon_tbl();
1928 
1929 	hud_init_comm_orders();	// Goober5000
1930 
1931 	control_config_common_init();				// sets up localization stuff in the control config
1932 
1933 	parse_rank_tbl();
1934 	parse_medal_tbl();
1935 
1936 	cutscene_init();
1937 	key_init();
1938 	mouse_init();
1939 	gamesnd_parse_soundstbl();
1940 
1941 	gameseq_init();
1942 
1943 	multi_init();
1944 
1945 	// start up the mission logfile
1946 	logfile_init(LOGFILE_EVENT_LOG);
1947 	log_string(LOGFILE_EVENT_LOG,"FS2_Open Mission Log - Opened \n\n", 1);
1948 
1949 	// standalone's don't use the joystick and it seems to sometimes cause them to not get shutdown properly
1950 	if(!Is_standalone){
1951 		joy_init();
1952 	}
1953 
1954 	player_controls_init();
1955 	model_init();
1956 
1957 	event_music_init();
1958 
1959 	// initialize alpha colors
1960 	// CommanderDJ: try with colors.tbl first, then use the old way if that doesn't work
1961 	if (!new_alpha_colors_init()) {
1962 		old_alpha_colors_init();
1963 	}
1964 
1965 	obj_init();
1966 	mflash_game_init();
1967 	armor_init();
1968 	ai_init();
1969 	ai_profiles_init();		// Goober5000
1970 	weapon_init();
1971 	ship_init();						// read in ships.tbl
1972 
1973 	player_init();
1974 	mission_campaign_init();		// load in the default campaign
1975 	anim_init();
1976 	context_help_init();
1977 	techroom_intel_init();			// parse species.tbl, load intel info
1978 	hud_positions_init();		//Setup hud positions
1979 
1980 	// initialize psnet
1981 	psnet_init( Multi_options_g.protocol, Multi_options_g.port );						// initialize the networking code
1982 
1983 	init_animating_pointer();
1984 	asteroid_init();
1985 	mission_brief_common_init();	// Mark all the briefing structures as empty.
1986 
1987 	neb2_init();						// fullneb stuff
1988 	nebl_init();
1989 	stars_init();
1990 	ssm_init();
1991 	player_tips_init();				// helpful tips
1992 	beam_init();
1993 
1994 	// load the list of pilot pic filenames (for barracks and pilot select popup quick reference)
1995 	pilot_load_pic_list();
1996 	pilot_load_squad_pic_list();
1997 
1998 	load_animating_pointer(NOX("cursor"), 0, 0);
1999 
2000 	if(!Cmdline_reparse_mainhall)
2001 	{
2002 		main_hall_table_init();
2003 	}
2004 
2005 	if (Cmdline_env) {
2006 		ENVMAP = Default_env_map = bm_load("cubemap");
2007 	}
2008 
2009 	Viewer_mode = 0;
2010 	Game_paused = 0;
2011 
2012 	Script_system.RunBytecode(Script_gameinithook);
2013 	Script_system.RunCondition(CHA_GAMEINIT);
2014 
2015 	game_title_screen_close();
2016 
2017 	// convert old pilot files (if they need it)
2018 	convert_pilot_files();
2019 
2020 #ifdef _WIN32
2021 	timeBeginPeriod(1);
2022 #endif
2023 
2024 	nprintf(("General", "Ships.tbl is : %s\n", Game_ships_tbl_valid ? "VALID" : "INVALID!!!!"));
2025 	nprintf(("General", "Weapons.tbl is : %s\n", Game_weapons_tbl_valid ? "VALID" : "INVALID!!!!"));
2026 
2027 	mprintf(("cfile_init() took %d\n", e1 - s1));
2028 	Script_system.RunBytecode(Script_gameinithook);
2029 }
2030 
2031 char transfer_text[128];
2032 
2033 float	Start_time = 0.0f;
2034 
2035 float Framerate = 0.0f;
2036 
2037 #ifndef NDEBUG
2038 float Timing_total = 0.0f;
2039 float Timing_render2 = 0.0f;
2040 float Timing_render3 = 0.0f;
2041 float Timing_flip = 0.0f;
2042 float Timing_clear = 0.0f;
2043 #endif
2044 
2045 MONITOR(NumPolysDrawn)
2046 MONITOR(NumPolys)
2047 MONITOR(NumVerts)
2048 MONITOR(BmpUsed)
2049 MONITOR(BmpNew)
2050 
2051 
2052 uint Mem_starttime_phys;
2053 uint Mem_starttime_pagefile;
2054 uint Mem_starttime_virtual;
2055 
game_get_framerate()2056 void game_get_framerate()
2057 {
2058 	if (frame_int == -1) {
2059 		for (int i = 0; i < FRAME_FILTER; i++)
2060 			frametimes[i] = 0.0f;
2061 
2062 		frametotal = 0.0f;
2063 		frame_int = 0;
2064 	}
2065 
2066 	frametotal -= frametimes[frame_int];
2067 	frametotal += flRealframetime;
2068 	frametimes[frame_int] = flRealframetime;
2069 	frame_int = (frame_int + 1 ) % FRAME_FILTER;
2070 
2071 	if (frametotal != 0.0f) {
2072 		if (Framecount >= FRAME_FILTER)
2073 			Framerate = FRAME_FILTER / frametotal;
2074 		else
2075 			Framerate = Framecount / frametotal;
2076 	}
2077 
2078 	Framecount++;
2079 }
2080 
2081 /**
2082  * Show FPS within game
2083  */
game_show_framerate()2084 void game_show_framerate()
2085 {
2086 	float	cur_time;
2087 
2088 	cur_time = f2fl(timer_get_approx_seconds());
2089 	if (cur_time - Start_time > 30.0f) {
2090 		mprintf(("%i frames executed in %7.3f seconds, %7.3f frames per second.\n", Framecount, cur_time - Start_time, Framecount/(cur_time - Start_time)));
2091 		Start_time += 1000.0f;
2092 	}
2093 
2094 #ifdef WMC
2095 	//WMC - this code spits out the target of all turrets
2096 	if ( (Player_ai->target_objnum != -1) && (Objects[Player_ai->target_objnum].type == OBJ_SHIP) ) {
2097 		//Debug crap
2098 		int t = 0;
2099 		ship_subsys	*pss;
2100 
2101 		gr_set_color_fast(&HUD_color_debug);
2102 
2103 		object *objp = &Objects[Player_ai->target_objnum];
2104 		for ( pss = GET_FIRST(&shipp->subsys_list); pss !=END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) {
2105 			if (pss->system_info->type == SUBSYSTEM_TURRET) {
2106 				if(pss->turret_enemy_objnum == -1)
2107 					gr_printf_no_resize(10, t*10, "Turret %d: <None>", t);
2108 				else if (Objects[pss->turret_enemy_objnum].type == OBJ_SHIP)
2109 					gr_printf_no_resize(10, t*10, "Turret %d: %s", t, Ships[Objects[pss->turret_enemy_objnum].instance].ship_name);
2110 				else
2111 					gr_printf_no_resize(10, t*10, "Turret %d: <Object %d>", t, pss->turret_enemy_objnum);
2112 
2113 				t++;
2114 			}
2115 		}
2116 	}
2117 #endif
2118 
2119 
2120 	if (Show_framerate || Cmdline_frame_profile)	{
2121 		gr_set_color_fast(&HUD_color_debug);
2122 
2123 		if (Cmdline_frame_profile) {
2124 			gr_string(20, 110, profile_output, GR_RESIZE_NONE);
2125 		}
2126 
2127 		if (Show_framerate) {
2128 			if (frametotal != 0.0f)
2129 				gr_printf_no_resize( 20, 100, "FPS: %0.1f", Framerate );
2130 			else
2131 				gr_string( 20, 100, "FPS: ?", GR_RESIZE_NONE );
2132 		}
2133 	}
2134 
2135 #ifndef NDEBUG
2136 	if ( Debug_dump_frames )
2137 		return;
2138 #endif
2139 
2140 	// possibly show control checking info
2141 	control_check_indicate();
2142 
2143 #ifdef _WIN32
2144 	if (Cmdline_show_stats && HUD_draw) {
2145 		char mem_buffer[50];
2146 
2147 		MEMORYSTATUS mem_stats;
2148 		GlobalMemoryStatus(&mem_stats);
2149 
2150 		// on win2k+, it should be == -1 if >4gig (indicates wrap around)
2151 		if ( ((int)Mem_starttime_phys == -1) || ((int)mem_stats.dwAvailPhys == -1) )
2152 			sprintf(mem_buffer, "Using Physical: *** (>4G)");
2153 		else
2154 			sprintf(mem_buffer,"Using Physical: %d Meg",(Mem_starttime_phys - mem_stats.dwAvailPhys)/1024/1024);
2155 
2156 		gr_string( 20, 120, mem_buffer, GR_RESIZE_NONE);
2157 		sprintf(mem_buffer,"Using Pagefile: %d Meg",(Mem_starttime_pagefile - mem_stats.dwAvailPageFile)/1024/1024);
2158 		gr_string( 20, 130, mem_buffer, GR_RESIZE_NONE);
2159 		sprintf(mem_buffer,"Using Virtual:  %d Meg",(Mem_starttime_virtual - mem_stats.dwAvailVirtual)/1024/1024);
2160 		gr_string( 20, 140, mem_buffer, GR_RESIZE_NONE);
2161 
2162 		if ( ((int)mem_stats.dwAvailPhys == -1) || ((int)mem_stats.dwTotalPhys == -1) )
2163 			sprintf(mem_buffer, "Physical Free: *** / *** (>4G)");
2164 		else
2165 			sprintf(mem_buffer,"Physical Free: %d / %d Meg",mem_stats.dwAvailPhys/1024/1024, mem_stats.dwTotalPhys/1024/1024);
2166 
2167 		gr_string( 20, 160, mem_buffer, GR_RESIZE_NONE);
2168 		sprintf(mem_buffer,"Pagefile Free: %d / %d Meg",mem_stats.dwAvailPageFile/1024/1024, mem_stats.dwTotalPageFile/1024/1024);
2169 		gr_string( 20, 170, mem_buffer, GR_RESIZE_NONE);
2170 		sprintf(mem_buffer,"Virtual Free:  %d / %d Meg",mem_stats.dwAvailVirtual/1024/1024, mem_stats.dwTotalVirtual/1024/1024);
2171 		gr_string( 20, 180, mem_buffer, GR_RESIZE_NONE);
2172 	}
2173 #endif
2174 
2175 #ifndef NDEBUG
2176 	if ( Show_cpu == 1 ) {
2177 
2178 		int sx,sy,dy;
2179 		sx = gr_screen.max_w - 154;
2180 		sy = 15;
2181 		dy = gr_get_font_height() + 1;
2182 
2183 		gr_set_color_fast(&HUD_color_debug);
2184 
2185 		gr_printf_no_resize( sx, sy, NOX("DMA: %s"), transfer_text );
2186 		sy += dy;
2187 		gr_printf_no_resize( sx, sy, NOX("POLYP: %d"), modelstats_num_polys );
2188 		sy += dy;
2189 		gr_printf_no_resize( sx, sy, NOX("POLYD: %d"), modelstats_num_polys_drawn );
2190 		sy += dy;
2191 		gr_printf_no_resize( sx, sy, NOX("VERTS: %d"), modelstats_num_verts );
2192 		sy += dy;
2193 
2194 		{
2195 
2196 			extern int Num_pairs;		// Number of object pairs that were checked.
2197 			gr_printf_no_resize( sx, sy, NOX("PAIRS: %d"), Num_pairs );
2198 			sy += dy;
2199 
2200 			extern int Num_pairs_checked;	// What percent of object pairs were checked.
2201 			gr_printf_no_resize( sx, sy, NOX("FVI: %d"), Num_pairs_checked );
2202 			sy += dy;
2203 			Num_pairs_checked = 0;
2204 
2205 		}
2206 
2207 		gr_printf_no_resize( sx, sy, NOX("Snds: %d"), snd_num_playing() );
2208 		sy += dy;
2209 
2210 		if ( Timing_total > 0.01f )	{
2211 			gr_printf_no_resize(  sx, sy, NOX("CLEAR: %.0f%%"), Timing_clear*100.0f/Timing_total );
2212 			sy += dy;
2213 			gr_printf_no_resize( sx, sy, NOX("REND2D: %.0f%%"), Timing_render2*100.0f/Timing_total );
2214 			sy += dy;
2215 			gr_printf_no_resize( sx, sy, NOX("REND3D: %.0f%%"), Timing_render3*100.0f/Timing_total );
2216 			sy += dy;
2217 			gr_printf_no_resize( sx, sy, NOX("FLIP: %.0f%%"), Timing_flip*100.0f/Timing_total );
2218 			sy += dy;
2219 			gr_printf_no_resize( sx, sy, NOX("GAME: %.0f%%"), (Timing_total-(Timing_render2+Timing_render3+Timing_flip+Timing_clear))*100.0f/Timing_total );
2220 			sy += dy;
2221 		}
2222 	}
2223 
2224 	if ( Show_mem  ) {
2225 
2226 		int sx,sy,dy;
2227 		sx = gr_screen.max_w - 154;
2228 		sy = 15;
2229 		dy = gr_get_font_height() + 1;
2230 
2231 		gr_set_color_fast(&HUD_color_debug);
2232 
2233 		{
2234 			extern int TotalRam;
2235 			gr_printf_no_resize( sx, sy, NOX("DYN: %d KB\n"), TotalRam/1024 );
2236 			sy += dy;
2237 		}
2238 
2239 		{
2240 			extern int Model_ram;
2241 			gr_printf_no_resize( sx, sy, NOX("POF: %d KB\n"), Model_ram/1024 );
2242 			sy += dy;
2243 		}
2244 
2245 		gr_printf_no_resize( sx, sy, NOX("%s: %d KB\n"), (Cmdline_cache_bitmaps) ? NOX("C-BMP") : NOX("BMP"), bm_texture_ram/1024 );
2246 		sy += dy;
2247 
2248 		gr_printf_no_resize( sx, sy, NOX("S-SRAM: %d KB\n"), Snd_sram/1024 );		// mem used to store game sound
2249 		sy += dy;
2250 
2251 		{
2252 			extern int GL_textures_in;
2253 			extern int GL_vertex_data_in;
2254 			gr_printf_no_resize( sx, sy, NOX("VRAM: %d KB\n"), (GL_textures_in + GL_vertex_data_in)/1024 );
2255 			sy += dy;
2256 		}
2257 	}
2258 
2259 
2260 	if ( Show_player_pos ) {
2261 		int sx, sy;
2262 		sx = 320;
2263 		sy = 100;
2264 		gr_printf_no_resize(sx, sy, NOX("Player Pos: (%d,%d,%d)"), fl2i(Player_obj->pos.xyz.x), fl2i(Player_obj->pos.xyz.y), fl2i(Player_obj->pos.xyz.z));
2265 	}
2266 
2267 #ifdef _WIN32
2268 	if (Cmdline_show_mem_usage) {
2269 		void memblockinfo_sort();
2270 		void memblockinfo_sort_get_entry(int index, char *filename, int *size);
2271 
2272 		char mem_buffer[1000];
2273 		char filename[MAX_PATH];
2274 		int size;
2275 
2276 	  	memblockinfo_sort();
2277 
2278 		int mi = 0;
2279 		for( ; mi < 30; mi++) {
2280 			memblockinfo_sort_get_entry(mi, filename, &size);
2281 
2282 			size /= 1024;
2283 
2284 			if (size == 0)
2285 				break;
2286 
2287 			char *short_name = strrchr(filename, '\\');
2288 
2289 			if (short_name == NULL)
2290 				short_name = filename;
2291 			else
2292 				short_name++;
2293 
2294 			sprintf(mem_buffer,"%s:\t%d K", short_name, size);
2295 			gr_string( 20, 220 + (mi*10), mem_buffer, GR_RESIZE_NONE);
2296 		}
2297 
2298 		sprintf(mem_buffer,"Total RAM:\t%d K", TotalRam / 1024);
2299 		gr_string( 20, 230 + (mi*10), mem_buffer, GR_RESIZE_NONE);
2300 	}
2301 #endif
2302 
2303 	MONITOR_INC(NumPolys, modelstats_num_polys);
2304 	MONITOR_INC(NumPolysDrawn, modelstats_num_polys_drawn );
2305 	MONITOR_INC(NumVerts, modelstats_num_verts );
2306 
2307 	modelstats_num_polys = 0;
2308 	modelstats_num_polys_drawn = 0;
2309 	modelstats_num_verts = 0;
2310 	modelstats_num_sortnorms = 0;
2311 #endif
2312 }
2313 
game_show_eye_pos(camid cid)2314 void game_show_eye_pos(camid cid)
2315 {
2316 	if ( !Cmdline_show_pos )
2317 		return;
2318 
2319 	if(!cid.isValid())
2320 		return;
2321 
2322 	camera *cam = cid.getCamera();
2323 	vec3d cam_pos = vmd_zero_vector;
2324 	matrix cam_orient = vmd_identity_matrix;
2325 	cam->get_info(&cam_pos, &cam_orient);
2326 
2327 	//Do stuff
2328 	int font_height = 2*gr_get_font_height();
2329 	angles rot_angles;
2330 
2331 	gr_set_color_fast(&HUD_color_debug);
2332 
2333 	//Position
2334 	gr_printf_no_resize(20, 100 - font_height, "X:%f Y:%f Z:%f", cam_pos.xyz.x, cam_pos.xyz.y, cam_pos.xyz.z);
2335 	font_height -= font_height/2;
2336 
2337 	//Orientation
2338 	vm_extract_angles_matrix(&rot_angles, &cam_orient);
2339 	rot_angles.p *= (180/PI);
2340 	rot_angles.b *= (180/PI);
2341 	rot_angles.h *= (180/PI);
2342 	gr_printf_no_resize(20, 100 - font_height, "Xr:%f Yr:%f Zr:%f", rot_angles.p, rot_angles.b, rot_angles.h);
2343 }
2344 
game_show_standalone_framerate()2345 void game_show_standalone_framerate()
2346 {
2347 	float frame_rate=30.0f;
2348 	if ( frame_int == -1 )	{
2349 		int i;
2350 		for (i=0; i<FRAME_FILTER; i++ )	{
2351 			frametimes[i] = 0.0f;
2352 		}
2353 		frametotal = 0.0f;
2354 		frame_int = 0;
2355 	}
2356 	frametotal -= frametimes[frame_int];
2357 	frametotal += flRealframetime;
2358 	frametimes[frame_int] = flRealframetime;
2359 	frame_int = (frame_int + 1 ) % FRAME_FILTER;
2360 
2361 	if ( frametotal != 0.0 )	{
2362 		if ( Framecount >= FRAME_FILTER ){
2363 			frame_rate = FRAME_FILTER / frametotal;
2364 		} else {
2365 			frame_rate = Framecount / frametotal;
2366 		}
2367 	}
2368 	std_set_standalone_fps(frame_rate);
2369 	Framecount++;
2370 }
2371 
2372 /**
2373  * Show the time remaining in a mission.  Used only when the end-mission sexpression is used
2374  *
2375  * mission_end_time is a global from missionparse.cpp that contains the mission time at which the
2376  * mission should end (in fixed seconds).  There is code in missionparse.cpp which actually handles
2377  * checking how much time is left.
2378  */
game_show_time_left()2379 void game_show_time_left()
2380 {
2381 	int diff;
2382 
2383 	if (Mission_end_time == -1)
2384 		return;
2385 
2386 	diff = f2i(Mission_end_time - Missiontime);
2387 	// be sure to bash to 0.  diff could be negative on frame that we quit mission
2388 	if (diff < 0)
2389 		diff = 0;
2390 
2391 	hud_set_default_color();
2392 	gr_printf_no_resize( 5, 40, XSTR( "Mission time remaining: %d seconds", 179), diff );
2393 }
2394 
2395 //========================================================================================
2396 //=================== NEW DEBUG CONSOLE COMMANDS TO REPLACE OLD DEBUG PAUSE MENU =========
2397 //========================================================================================
2398 
2399 #ifndef NDEBUG
2400 
2401 DCF(ai_pause,"Pauses ai")
2402 {
2403 	if ( Dc_command )	{
2404 		dc_get_arg(ARG_TRUE|ARG_FALSE|ARG_NONE);
2405 		if ( Dc_arg_type & ARG_TRUE )	ai_paused = 1;
2406 		else if ( Dc_arg_type & ARG_FALSE ) ai_paused = 0;
2407 		else if ( Dc_arg_type & ARG_NONE ) ai_paused = !ai_paused;
2408 
2409 		if (ai_paused)	{
2410 			obj_init_all_ships_physics();
2411 		}
2412 	}
2413 	if ( Dc_help )	dc_printf( "Usage: ai_paused [bool]\nSets ai_paused to true or false.  If nothing passed, then toggles it.\n" );
2414 	if ( Dc_status )	dc_printf( "ai_paused is %s\n", (ai_paused?"TRUE":"FALSE") );
2415 }
2416 
2417 DCF(single_step,"Single steps the game")
2418 {
2419 	if ( Dc_command )	{
2420 		dc_get_arg(ARG_TRUE|ARG_FALSE|ARG_NONE);
2421 		if ( Dc_arg_type & ARG_TRUE )	game_single_step = 1;
2422 		else if ( Dc_arg_type & ARG_FALSE ) game_single_step = 0;
2423 		else if ( Dc_arg_type & ARG_NONE ) game_single_step = !game_single_step;
2424 
2425 		last_single_step = 0;	// Make so single step waits a frame before stepping
2426 
2427 	}
2428 	if ( Dc_help )	dc_printf( "Usage: single_step [bool]\nSets single_step to true or false.  If nothing passed, then toggles it.\n" );
2429 	if ( Dc_status )	dc_printf( "single_step is %s\n", (game_single_step?"TRUE":"FALSE") );
2430 }
2431 
2432 DCF_BOOL(physics_pause, physics_paused)
2433 DCF_BOOL(ai_rendering, Ai_render_debug_flag)
2434 DCF_BOOL(ai_firing, Ai_firing_enabled )
2435 
2436 // Create some simple aliases to these commands...
2437 debug_command dc_s("s","shortcut for single_step",dcf_single_step);
2438 debug_command dc_p("p","shortcut for physics_pause", dcf_physics_pause );
2439 debug_command dc_r("r","shortcut for ai_rendering", dcf_ai_rendering );
2440 debug_command dc_f("f","shortcut for ai_firing", dcf_ai_firing);
2441 debug_command dc_a("a","shortcut for ai_pause", dcf_ai_pause);
2442 #endif
2443 
2444 //========================================================================================
2445 //========================================================================================
2446 
2447 
game_training_pause_do()2448 void game_training_pause_do()
2449 {
2450 	int key;
2451 
2452 	key = game_check_key();
2453 	if (key > 0){
2454 		gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
2455 	}
2456 
2457 	gr_flip();
2458 }
2459 
2460 
game_increase_skill_level()2461 void game_increase_skill_level()
2462 {
2463 	Game_skill_level++;
2464 	if (Game_skill_level >= NUM_SKILL_LEVELS){
2465 		Game_skill_level = 0;
2466 	}
2467 }
2468 
2469 int	Player_died_time;
2470 
2471 int View_percent = 100;
2472 
2473 
2474 DCF(view, "Sets the percent of the 3d view to render.")
2475 {
2476 	if ( Dc_command ) {
2477 		dc_get_arg(ARG_INT);
2478 		if ( (Dc_arg_int >= 5 ) || (Dc_arg_int <= 100) ) {
2479 			View_percent = Dc_arg_int;
2480 		} else {
2481 			dc_printf( "Illegal value for view. (Must be from 5-100) \n\n");
2482 			Dc_help = 1;
2483 		}
2484 	}
2485 
2486 	if ( Dc_help ) {
2487 		dc_printf("Usage: view [n]\nwhere n is percent of view to show (5-100).\n");
2488 	}
2489 
2490 	if ( Dc_status ) {
2491 		dc_printf("View is set to %d%%\n", View_percent );
2492 	}
2493 }
2494 
2495 
2496 /**
2497  * Set the clip region for the 3d rendering window
2498  */
game_reset_view_clip()2499 void game_reset_view_clip()
2500 {
2501 	Cutscene_bar_flags = CUB_NONE;
2502 	Cutscene_delta_time = 1.0f;
2503 	Cutscene_bars_progress = 1.0f;
2504 }
2505 
game_set_view_clip(float frametime)2506 void game_set_view_clip(float frametime)
2507 {
2508 	if ((Game_mode & GM_DEAD) || (supernova_active() >= 2))
2509 	{
2510 		// Set the clip region for the letterbox "dead view"
2511 		int yborder = gr_screen.max_h/4;
2512 
2513 		if (g3_in_frame() == 0) {
2514 			// Ensure that the bars are black
2515 			gr_set_color(0,0,0);
2516 			gr_set_bitmap(0); // Valathil - Don't ask me why this has to be here but otherwise the black bars don't draw
2517 			gr_rect(0, 0, gr_screen.max_w, yborder, GR_RESIZE_NONE);
2518 			gr_rect(0, gr_screen.max_h-yborder, gr_screen.max_w, yborder, GR_RESIZE_NONE);
2519 		} else {
2520 			//	Numeric constants encouraged by J "pig farmer" S, who shall remain semi-anonymous.
2521 			// J.S. I've changed my ways!! See the new "no constants" code!!!
2522 			gr_set_clip(0, yborder, gr_screen.max_w, gr_screen.max_h - yborder*2, GR_RESIZE_NONE );
2523 		}
2524 	}
2525 	else {
2526 		// Set the clip region for normal view
2527 		if ( View_percent >= 100 )	{
2528 			gr_reset_clip();
2529 		} else {
2530 			int xborder, yborder;
2531 
2532 			if ( View_percent < 5 )	{
2533 				View_percent = 5;
2534 			}
2535 
2536 			float fp = i2fl(View_percent)/100.0f;
2537 			int fi = fl2i(fl_sqrt(fp)*100.0f);
2538 			if ( fi > 100 ) fi=100;
2539 
2540 			xborder = ( gr_screen.max_w*(100-fi) )/200;
2541 			yborder = ( gr_screen.max_h*(100-fi) )/200;
2542 
2543 			gr_set_clip(xborder, yborder, gr_screen.max_w-xborder*2,gr_screen.max_h-yborder*2, GR_RESIZE_NONE );
2544 		}
2545 	}
2546 }
2547 
2548 
show_debug_stuff()2549 void show_debug_stuff()
2550 {
2551 	int	i;
2552 	int	laser_count = 0, missile_count = 0;
2553 
2554 	for (i=0; i<MAX_OBJECTS; i++) {
2555 		if (Objects[i].type == OBJ_WEAPON){
2556 			if (Weapon_info[Weapons[Objects[i].instance].weapon_info_index].subtype == WP_LASER){
2557 				laser_count++;
2558 			} else if (Weapon_info[Weapons[Objects[i].instance].weapon_info_index].subtype == WP_MISSILE){
2559 				missile_count++;
2560 			}
2561 		}
2562 	}
2563 
2564 	nprintf(("Mike", "Frame: %i Lasers: %4i, Missiles: %4i\n", Framecount, laser_count, missile_count));
2565 }
2566 
2567 extern int Tool_enabled;
2568 int tst = 0;
2569 int tst_time = 0;
2570 int tst_big = 0;
2571 vec3d tst_pos;
2572 int tst_bitmap = -1;
2573 float tst_x, tst_y;
2574 float tst_offset, tst_offset_total;
2575 int tst_mode;
2576 int tst_stamp;
game_tst_frame_pre()2577 void game_tst_frame_pre()
2578 {
2579 	// start tst
2580 	if(tst == 3){
2581 		tst = 0;
2582 
2583 		// screen position
2584 		vertex v;
2585 		g3_rotate_vertex(&v, &tst_pos);
2586 		g3_project_vertex(&v);
2587 
2588 		// offscreen
2589 		if(!(
2590 				(v.screen.xyw.x >= 0)
2591 				&& (v.screen.xyw.x <= gr_screen.max_w)
2592 				&& (v.screen.xyw.y >= 0)
2593 				&& (v.screen.xyw.y <= gr_screen.max_h)
2594 			))
2595 		{
2596 			return;
2597 		}
2598 
2599 		// big ship? always tst
2600 		if(tst_big){
2601 			// within 3000 meters
2602 			if( vm_vec_dist_quick(&tst_pos, &Eye_position) <= 3000.0f){
2603 				tst = 2;
2604 			}
2605 		} else {
2606 			// within 300 meters
2607 			if( (vm_vec_dist_quick(&tst_pos, &Eye_position) <= 300.0f) && ((tst_time == 0) || ((time(NULL) - tst_time) >= 10)) ){
2608 				tst = 2;
2609 			}
2610 		}
2611 	}
2612 
2613 }
game_tst_frame()2614 void game_tst_frame()
2615 {
2616 	int left = 0;
2617 
2618 	if(!Tool_enabled){
2619 		return;
2620 	}
2621 
2622 	// setup tst
2623 	if(tst == 2){
2624 		tst_time = (int) time(NULL);
2625 
2626 		// load the tst bitmap
2627 		switch((int)frand_range(0.0f, 3.0)){
2628 		case 0:
2629 			tst_bitmap = bm_load("ig_jim");
2630 			left = 1;
2631 			mprintf(("TST 0\n"));
2632 			break;
2633 
2634 		case 1:
2635 			tst_bitmap = bm_load("ig_kan");
2636 			left = 0;
2637 			mprintf(("TST 1\n"));
2638 			break;
2639 
2640 		case 2:
2641 			tst_bitmap = bm_load("ig_jim");
2642 			left = 1;
2643 			mprintf(("TST 2\n"));
2644 			break;
2645 
2646 		default:
2647 			tst_bitmap = bm_load("ig_kan");
2648 			left = 0;
2649 			mprintf(("TST 3\n"));
2650 			break;
2651 		}
2652 
2653 		if(tst_bitmap < 0){
2654 			tst = 0;
2655 			return;
2656 		}
2657 
2658 		// get the tst bitmap dimensions
2659 		int w, h;
2660 		bm_get_info(tst_bitmap, &w, &h);
2661 
2662 		// tst y
2663 		tst_y = frand_range(0.0f, (float)gr_screen.max_h - h);
2664 
2665 		snd_play(&Snds[SND_VASUDAN_BUP]);
2666 
2667 		// tst x and direction
2668 		tst_mode = 0;
2669 		if(left){
2670 			tst_x = (float)-w;
2671 			tst_offset_total = (float)w;
2672 			tst_offset = (float)w;
2673 		} else {
2674 			tst_x = (float)gr_screen.max_w;
2675 			tst_offset_total = (float)-w;
2676 			tst_offset = (float)w;
2677 		}
2678 
2679 		tst = 1;
2680 	}
2681 
2682 	// run tst
2683 	if(tst == 1){
2684 		float diff = (tst_offset_total / 0.5f) * flFrametime;
2685 
2686 		// move the bitmap
2687 		if(tst_mode == 0){
2688 			tst_x += diff;
2689 
2690 			tst_offset -= fl_abs(diff);
2691 		} else if(tst_mode == 2){
2692 			tst_x -= diff;
2693 
2694 			tst_offset -= fl_abs(diff);
2695 		}
2696 
2697 		// draw the bitmap
2698 		gr_set_bitmap(tst_bitmap);
2699 		gr_bitmap((int)tst_x, (int)tst_y, GR_RESIZE_NONE);
2700 
2701 		if(tst_mode == 1){
2702 			if(timestamp_elapsed_safe(tst_stamp, 1100)){
2703 				tst_mode = 2;
2704 			}
2705 		} else {
2706 			// if we passed the switch point
2707 			if(tst_offset <= 0.0f){
2708 				// switch modes
2709 				switch(tst_mode){
2710 				case 0:
2711 					tst_mode = 1;
2712 					tst_stamp = timestamp(1000);
2713 					tst_offset = fl_abs(tst_offset_total);
2714 					break;
2715 
2716 				case 2:
2717 					tst = 0;
2718 					return;
2719 				}
2720 			}
2721 		}
2722 	}
2723 }
game_tst_mark(object * objp,ship * shipp)2724 void game_tst_mark(object *objp, ship *shipp)
2725 {
2726 	ship_info *sip;
2727 
2728 	if(!Tool_enabled){
2729 		return;
2730 	}
2731 
2732 	// bogus
2733 	if((objp == NULL) || (shipp == NULL) || (shipp->ship_info_index < 0) || (shipp->ship_info_index >= Num_ship_classes)){
2734 		return;
2735 	}
2736 	sip = &Ship_info[shipp->ship_info_index];
2737 
2738 	// already tst
2739 	if(tst){
2740 		return;
2741 	}
2742 
2743 	tst_pos = objp->pos;
2744 	if(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)){
2745 		tst_big = 1;
2746 	}
2747 	tst = 3;
2748 }
2749 
2750 extern void render_shields();
2751 
player_repair_frame(float frametime)2752 void player_repair_frame(float frametime)
2753 {
2754 	if(MULTIPLAYER_MASTER){
2755 		int idx;
2756 		for(idx=0;idx<MAX_PLAYERS;idx++){
2757 			net_player *np;
2758 
2759 			np = &Net_players[idx];
2760 
2761 			if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != NULL) && (Net_player->player_id != Net_players[idx].player_id) && (Net_players[idx].m_player != NULL) && (Net_players[idx].m_player->objnum >= 0) && (Net_players[idx].m_player->objnum < MAX_OBJECTS)){
2762 
2763 				// don't rearm/repair if the player is dead or dying/departing
2764 				if ( !NETPLAYER_IS_DEAD(np) && !(Ships[Objects[np->m_player->objnum].instance].flags & (SF_DYING|SF_DEPARTING)) ) {
2765 					ai_do_repair_frame(&Objects[Net_players[idx].m_player->objnum],&Ai_info[Ships[Objects[Net_players[idx].m_player->objnum].instance].ai_index],frametime);
2766 				}
2767 			}
2768 		}
2769 	}
2770 
2771 	if ( (Player_obj != NULL) && (Player_obj->type == OBJ_SHIP) && !(Game_mode & GM_STANDALONE_SERVER) && (Player_ship != NULL) && !(Player_ship->flags & SF_DYING) ) {
2772 		ai_do_repair_frame(Player_obj, &Ai_info[Ships[Player_obj->instance].ai_index], frametime);
2773 	}
2774 }
2775 
2776 #define NUM_FRAMES_TEST		300
2777 #define NUM_MIXED_SOUNDS	16
do_timing_test(float frame_time)2778 void do_timing_test(float frame_time)
2779 {
2780 	static int framecount = 0;
2781 	static int test_running = 0;
2782 	static float test_time = 0.0f;
2783 
2784 	static int snds[NUM_MIXED_SOUNDS];
2785 	int i;
2786 
2787 	if ( test_running ) {
2788 		framecount++;
2789 		test_time += frame_time;
2790 		if ( framecount >= NUM_FRAMES_TEST ) {
2791 			test_running = 0;
2792 			nprintf(("General", "%d frames took %.3f seconds\n", NUM_FRAMES_TEST, test_time));
2793 			for ( i = 0; i < NUM_MIXED_SOUNDS; i++ )
2794 				snd_stop(snds[i]);
2795 		}
2796 	}
2797 
2798 	if ( Test_begin == 1 ) {
2799 		framecount = 0;
2800 		test_running = 1;
2801 		test_time = 0.0f;
2802 		Test_begin = 0;
2803 
2804 		for ( i = 0; i < NUM_MIXED_SOUNDS; i++ )
2805 			snds[i] = -1;
2806 
2807 		// start looping digital sounds
2808 		for ( i = 0; i < NUM_MIXED_SOUNDS; i++ )
2809 			snds[i] = snd_play_looping( &Snds[i], 0.0f, -1, -1);
2810 	}
2811 
2812 
2813 }
2814 
2815 DCF(dcf_fov, "Change the field of view of the main camera")
2816 {
2817 	camera *cam = Main_camera.getCamera();
2818 	if ( Dc_command )
2819 	{
2820 		if(cam == NULL)
2821 			return;
2822 
2823 		dc_get_arg(ARG_FLOAT|ARG_NONE);
2824 		if ( Dc_arg_type & ARG_NONE )	{
2825 			cam->set_fov(VIEWER_ZOOM_DEFAULT);
2826 			dc_printf( "Zoom factor reset\n" );
2827 		}
2828 		if ( Dc_arg_type & ARG_FLOAT )	{
2829 			if (Dc_arg_float < 0.25f) {
2830 				cam->set_fov(0.25f);
2831 				dc_printf("Zoom factor pinned at 0.25.\n");
2832 			} else if (Dc_arg_float > 1.25f) {
2833 				cam->set_fov(1.25f);
2834 				dc_printf("Zoom factor pinned at 1.25.\n");
2835 			} else {
2836 				cam->set_fov(Dc_arg_float);
2837 			}
2838 		}
2839 	}
2840 
2841 	if ( Dc_help )
2842 		dc_printf( "Usage: fov [factor]\nFactor is the zoom factor btwn .25 and 1.25\nNo parameter resets it to default.\n" );
2843 
2844 	if ( Dc_status )
2845 	{
2846 		if(cam == NULL)
2847 			dc_printf("Camera unavailable.");
2848 		else
2849 			dc_printf("Zoom factor set to %6.3f (original = 0.5, John = 0.75)", cam->get_fov());
2850 	}
2851 }
2852 
2853 
2854 DCF(framerate_cap, "Sets the framerate cap")
2855 {
2856 	if ( Dc_command ) {
2857 		dc_get_arg(ARG_INT);
2858 		if ( (Dc_arg_int >= 1 ) || (Dc_arg_int <= 120) ) {
2859 			Framerate_cap = Dc_arg_int;
2860 		} else {
2861 			dc_printf( "Illegal value for framerate cap. (Must be from 1-120) \n\n");
2862 			Dc_help = 1;
2863 		}
2864 	}
2865 
2866 	if ( Dc_help ) {
2867 		dc_printf("Usage: framerate_cap [n]\nwhere n is the frames per second to cap framerate at.\n");
2868 		dc_printf("If n is 0 or omitted, then the framerate cap is removed\n");
2869 		dc_printf("[n] must be from 1 to 120.\n");
2870 	}
2871 
2872 	if ( Dc_status ) {
2873 		if ( Framerate_cap )
2874 			dc_printf("Framerate cap is set to %d fps\n", Framerate_cap );
2875 		else
2876 			dc_printf("There is no framerate cap currently active.\n");
2877 	}
2878 }
2879 
2880 #define	MIN_DIST_TO_DEAD_CAMERA		50.0f
2881 int Show_viewing_from_self = 0;
2882 
say_view_target()2883 void say_view_target()
2884 {
2885 	object	*view_target;
2886 
2887 	if ((Viewer_mode & VM_OTHER_SHIP) && (Player_ai->target_objnum != -1))
2888 		view_target = &Objects[Player_ai->target_objnum];
2889 	else
2890 		view_target = Player_obj;
2891 
2892 	if (Game_mode & GM_DEAD) {
2893 		if (Player_ai->target_objnum != -1)
2894 			view_target = &Objects[Player_ai->target_objnum];
2895 	}
2896 
2897 	if (!(Game_mode & GM_DEAD_DIED) && ((Game_mode & (GM_DEAD_BLEW_UP)) || ((Last_view_target != NULL) && (Last_view_target != view_target)))) {
2898 		if (view_target != Player_obj){
2899 
2900 			char view_target_name[128] = "";
2901 			switch(Objects[Player_ai->target_objnum].type) {
2902 			case OBJ_SHIP:
2903 				if (Ships[Objects[Player_ai->target_objnum].instance].flags2 & SF2_HIDE_SHIP_NAME) {
2904 					strcpy_s(view_target_name, "targeted ship");
2905 				} else {
2906 					strcpy_s(view_target_name, Ships[Objects[Player_ai->target_objnum].instance].ship_name);
2907 				}
2908 				break;
2909 			case OBJ_WEAPON:
2910 				strcpy_s(view_target_name, Weapon_info[Weapons[Objects[Player_ai->target_objnum].instance].weapon_info_index].name);
2911 				Viewer_mode &= ~VM_OTHER_SHIP;
2912 				break;
2913 			case OBJ_JUMP_NODE: {
2914 				strcpy_s(view_target_name, XSTR( "jump node", 184));
2915 				Viewer_mode &= ~VM_OTHER_SHIP;
2916 				break;
2917 				}
2918 			case OBJ_DEBRIS: {
2919 				strcpy_s(view_target_name, "Debris");
2920 				Viewer_mode &= ~VM_OTHER_SHIP;
2921 				break;
2922 				}
2923 
2924 			default:
2925 				Int3();
2926 				break;
2927 			}
2928 
2929 			end_string_at_first_hash_symbol(view_target_name);
2930 			if ( strlen(view_target_name) ) {
2931 				hud_set_iff_color(&Objects[Player_ai->target_objnum], 1);
2932 				HUD_fixed_printf(0.0f, gr_screen.current_color, XSTR( "Viewing %s%s\n", 185), (Viewer_mode & VM_OTHER_SHIP) ? XSTR( "from ", 186) : "", view_target_name);
2933 				Show_viewing_from_self = 1;
2934 			}
2935 		} else {
2936 			color col;
2937 			gr_init_color(&col, 0, 255, 0);
2938 			if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_OBSERVER) && (Player_obj->type == OBJ_OBSERVER)){
2939 				HUD_fixed_printf(2.0f, col, XSTR( "Viewing from observer\n", 187));
2940 				Show_viewing_from_self = 1;
2941 			} else {
2942 				if (Show_viewing_from_self)
2943 					HUD_fixed_printf(2.0f, col, XSTR( "Viewing from self\n", 188));
2944 			}
2945 		}
2946 	}
2947 
2948 	Last_view_target = view_target;
2949 }
2950 
2951 
2952 float Game_hit_x = 0.0f;
2953 float Game_hit_y = 0.0f;
2954 
2955 // Reset at the beginning of each frame
game_whack_reset()2956 void game_whack_reset()
2957 {
2958 	Game_hit_x = 0.0f;
2959 	Game_hit_y = 0.0f;
2960 }
2961 
2962 // Apply a 2d whack to the player
game_whack_apply(float x,float y)2963 void game_whack_apply( float x, float y )
2964 {
2965 	// Do some force feedback
2966 	joy_ff_play_dir_effect(x * 80.0f, y * 80.0f);
2967 
2968 	// Move the eye
2969 	Game_hit_x += x;
2970 	Game_hit_y += y;
2971 
2972 //	mprintf(( "WHACK = %.1f, %.1f\n", Game_hit_x, Game_hit_y ));
2973 }
2974 
2975 // call to apply a "shudder"
game_shudder_apply(int time,float intensity)2976 void game_shudder_apply(int time, float intensity)
2977 {
2978 	Game_shudder_time = timestamp(time);
2979 	Game_shudder_total = time;
2980 	Game_shudder_intensity = intensity;
2981 }
2982 
get_shake(float intensity,int decay_time,int max_decay_time)2983 float get_shake(float intensity, int decay_time, int max_decay_time)
2984 {
2985 	int r = myrand();
2986 
2987 	float shake = intensity * (float) (r-RAND_MAX_2)/RAND_MAX;
2988 
2989 	if (decay_time >= 0) {
2990 		Assert(max_decay_time > 0);
2991 		shake *= (0.5f - fl_abs(0.5f - (float) decay_time / (float) max_decay_time));
2992 	}
2993 
2994 	return shake;
2995 }
2996 
2997 #define FF_SCALE	10000
2998 extern int Wash_on;
2999 extern float sn_shudder;
apply_view_shake(matrix * eye_orient)3000 void apply_view_shake(matrix *eye_orient)
3001 {
3002 	angles tangles;
3003 	tangles.p = 0.0f;
3004 	tangles.h = 0.0f;
3005 	tangles.b = 0.0f;
3006 
3007 	// do shakes that only affect the HUD
3008 	if (Viewer_obj == Player_obj) {
3009 		physics_info *pi = &Player_obj->phys_info;
3010 
3011 		// Make eye shake due to afterburner
3012 		if (!timestamp_elapsed(pi->afterburner_decay)) {
3013 			tangles.p += get_shake(0.07f, timestamp_until(pi->afterburner_decay), ABURN_DECAY_TIME);
3014 			tangles.h += get_shake(0.07f, timestamp_until(pi->afterburner_decay), ABURN_DECAY_TIME);
3015 		}
3016 
3017 		// Make eye shake due to engine wash
3018 		if (Player_obj->type == OBJ_SHIP && (Ships[Player_obj->instance].wash_intensity > 0) && Wash_on ) {
3019 			float wash_intensity = Ships[Player_obj->instance].wash_intensity;
3020 
3021 			tangles.p += get_shake(0.07f * wash_intensity, -1, 0);
3022 			tangles.h += get_shake(0.07f * wash_intensity, -1, 0);
3023 
3024 			// play the force feedback effect
3025 			vec3d rand_vec;
3026 			vm_vec_rand_vec_quick(&rand_vec);
3027 			joy_ff_play_dir_effect(FF_SCALE * wash_intensity * rand_vec.xyz.x, FF_SCALE * wash_intensity * rand_vec.xyz.y);
3028 		}
3029 
3030 		// Make eye shake due to shuddering
3031 		if (Game_shudder_time != -1) {
3032 			if (timestamp_elapsed(Game_shudder_time)) {
3033 				Game_shudder_time = -1;
3034 			} else {
3035 				tangles.p += get_shake(Game_shudder_intensity * 0.005f, timestamp_until(Game_shudder_time), Game_shudder_total);
3036 				tangles.h += get_shake(Game_shudder_intensity * 0.005f, timestamp_until(Game_shudder_time), Game_shudder_total);
3037 			}
3038 		}
3039 	}
3040 	// do shakes that affect external cameras
3041 	else {
3042 		// Make eye shake due to supernova
3043 		if (supernova_camera_cut()) {
3044 			float cut_pct = 1.0f - (supernova_time_left() / SUPERNOVA_CUT_TIME);
3045 			tangles.p += get_shake(0.07f * cut_pct * sn_shudder, -1, 0);
3046 			tangles.h += get_shake(0.07f * cut_pct * sn_shudder, -1, 0);
3047 		}
3048 	}
3049 
3050 	// maybe bail
3051 	if (tangles.p == 0.0f && tangles.h == 0.0f && tangles.b == 0.0f)
3052 		return;
3053 
3054 	matrix	tm, tm2;
3055 	vm_angles_2_matrix(&tm, &tangles);
3056 	Assert(vm_vec_mag(&tm.vec.fvec) > 0.0f);
3057 	Assert(vm_vec_mag(&tm.vec.rvec) > 0.0f);
3058 	Assert(vm_vec_mag(&tm.vec.uvec) > 0.0f);
3059 	vm_matrix_x_matrix(&tm2, eye_orient, &tm);
3060 	*eye_orient = tm2;
3061 }
3062 
3063 //	Player's velocity just before he blew up.  Used to keep camera target moving.
3064 vec3d	Dead_player_last_vel = { { { 1.0f, 1.0f, 1.0f } } };
3065 
3066 extern float View_zoom;
render_environment(int i,vec3d * eye_pos,matrix * new_orient,float new_zoom)3067 inline void render_environment(int i, vec3d *eye_pos, matrix *new_orient, float new_zoom)
3068 {
3069 	bm_set_render_target(gr_screen.envmap_render_target, i);
3070 
3071 	gr_clear();
3072 
3073 	g3_set_view_matrix( eye_pos, new_orient, new_zoom );
3074 
3075 	gr_set_proj_matrix( PI_2 * new_zoom, 1.0f, Min_draw_distance, Max_draw_distance);
3076 	gr_set_view_matrix( &Eye_position, &Eye_matrix );
3077 
3078 	if ( Game_subspace_effect ) {
3079 		stars_draw(0, 0, 0, 1, 1);
3080 	} else {
3081 		stars_draw(0, 1, 1, 0, 1);
3082 	}
3083 
3084 	gr_end_view_matrix();
3085 	gr_end_proj_matrix();
3086 }
3087 
setup_environment_mapping(camid cid)3088 void setup_environment_mapping(camid cid)
3089 {
3090 	matrix new_orient = IDENTITY_MATRIX;
3091 	float old_zoom = View_zoom, new_zoom = 1.0f;//0.925f;
3092 	int i = 0;
3093 
3094 
3095 	if (Cmdline_nohtl)
3096 		return;
3097 
3098 	if(!cid.isValid())
3099 		return;
3100 
3101 	vec3d cam_pos;
3102 	matrix cam_orient;
3103 	cid.getCamera()->get_info(&cam_pos, &cam_orient);
3104 
3105 	// prefer the mission specified envmap over the static-generated envmap, but
3106 	// the dynamic envmap should always get preference if in a subspace mission
3107 	if ( !Dynamic_environment && strlen(The_mission.envmap_name) ) {
3108 		ENVMAP = bm_load(The_mission.envmap_name);
3109 
3110 		if (ENVMAP >= 0)
3111 			return;
3112 	}
3113 
3114 	if (gr_screen.envmap_render_target < 0) {
3115 		if (ENVMAP >= 0)
3116 			return;
3117 
3118 		if (strlen(The_mission.envmap_name)) {
3119 			ENVMAP = bm_load(The_mission.envmap_name);
3120 
3121 			if (ENVMAP < 0)
3122 				ENVMAP = Default_env_map;
3123 		} else {
3124 			ENVMAP = Default_env_map;
3125 		}
3126 
3127 		return;
3128 	}
3129 
3130 	ENVMAP = gr_screen.envmap_render_target;
3131 
3132 /*
3133 	Envmap matrix setup -- left-handed
3134 	-------------------------------------------------
3135 	Face --	Forward		Up		Right
3136 	px		+X			+Y		-Z
3137 	nx		-X			+Y		+Z
3138 	py		+Y			-Z		+X
3139 	ny		-Y			+Z		+X
3140 	pz		+Z 			+Y		+X
3141 	nz		-Z			+Y		-X
3142 */
3143 
3144 	// NOTE: OpenGL needs up/down reversed
3145 
3146 	// face 1 (px / right)
3147 	memset( &new_orient, 0, sizeof(matrix) );
3148 	new_orient.vec.fvec.xyz.x =  1.0f;
3149 	new_orient.vec.uvec.xyz.y =  1.0f;
3150 	new_orient.vec.rvec.xyz.z = -1.0f;
3151 	render_environment(i, &cam_pos, &new_orient, new_zoom);
3152 	i++; // bump!
3153 
3154 	// face 2 (nx / left)
3155 	memset( &new_orient, 0, sizeof(matrix) );
3156 	new_orient.vec.fvec.xyz.x = -1.0f;
3157 	new_orient.vec.uvec.xyz.y =  1.0f;
3158 	new_orient.vec.rvec.xyz.z =  1.0f;
3159 	render_environment(i, &cam_pos, &new_orient, new_zoom);
3160 	i++; // bump!
3161 
3162 	// face 3 (py / up)
3163 	memset( &new_orient, 0, sizeof(matrix) );
3164 	new_orient.vec.fvec.xyz.y =  (gr_screen.mode == GR_OPENGL) ? -1.0f :  1.0f;
3165 	new_orient.vec.uvec.xyz.z =  (gr_screen.mode == GR_OPENGL) ?  1.0f : -1.0f;
3166 	new_orient.vec.rvec.xyz.x =  1.0f;
3167 	render_environment(i, &cam_pos, &new_orient, new_zoom);
3168 	i++; // bump!
3169 
3170 	// face 4 (ny / down)
3171 	memset( &new_orient, 0, sizeof(matrix) );
3172 	new_orient.vec.fvec.xyz.y =  (gr_screen.mode == GR_OPENGL) ?  1.0f : -1.0f;
3173 	new_orient.vec.uvec.xyz.z =  (gr_screen.mode == GR_OPENGL) ? -1.0f :  1.0f;
3174 	new_orient.vec.rvec.xyz.x =  1.0f;
3175 	render_environment(i, &cam_pos, &new_orient, new_zoom);
3176 	i++; // bump!
3177 
3178 	// face 5 (pz / forward)
3179 	memset( &new_orient, 0, sizeof(matrix) );
3180 	new_orient.vec.fvec.xyz.z =  1.0f;
3181 	new_orient.vec.uvec.xyz.y =  1.0f;
3182 	new_orient.vec.rvec.xyz.x =  1.0f;
3183 	render_environment(i, &cam_pos, &new_orient, new_zoom);
3184 	i++; // bump!
3185 
3186 	// face 6 (nz / back)
3187 	memset( &new_orient, 0, sizeof(matrix) );
3188 	new_orient.vec.fvec.xyz.z = -1.0f;
3189 	new_orient.vec.uvec.xyz.y =  1.0f;
3190 	new_orient.vec.rvec.xyz.x = -1.0f;
3191 	render_environment(i, &cam_pos, &new_orient, new_zoom);
3192 
3193 
3194 	// we're done, so now reset
3195 	bm_set_render_target(-1);
3196 	g3_set_view_matrix( &cam_pos, &cam_orient, old_zoom );
3197 }
3198 
3199 // setup the render target ready for this mission's environment map
game_environment_map_gen()3200 void game_environment_map_gen()
3201 {
3202 	const int size = 512;
3203 	int gen_flags = (BMP_FLAG_RENDER_TARGET_STATIC | BMP_FLAG_CUBEMAP);
3204 
3205 	if ( !Cmdline_env ) {
3206 		return;
3207 	}
3208 
3209 	if (gr_screen.envmap_render_target >= 0) {
3210 		if ( !bm_release(gr_screen.envmap_render_target, 1) ) {
3211 			Warning(LOCATION, "Unable to release environment map render target.");
3212 		}
3213 
3214 		gr_screen.envmap_render_target = -1;
3215 	}
3216 
3217 	if ( Dynamic_environment || (The_mission.flags & MISSION_FLAG_SUBSPACE) ) {
3218 		Dynamic_environment = true;
3219 		gen_flags &= ~BMP_FLAG_RENDER_TARGET_STATIC;
3220 		gen_flags |= BMP_FLAG_RENDER_TARGET_DYNAMIC;
3221 	}
3222 	// bail if we are going to be static, and have an envmap specified already
3223 	else if ( strlen(The_mission.envmap_name) ) {
3224 		return;
3225 	}
3226 
3227 	gr_screen.envmap_render_target = bm_make_render_target(size, size, gen_flags);
3228 }
3229 
3230 int Scripting_didnt_draw_hud = 1;
3231 
chase_get_camera()3232 camid chase_get_camera()
3233 {
3234 	static camid chase_camera;
3235 	if(!chase_camera.isValid())
3236 	{
3237 		chase_camera = cam_create("Chase camera");
3238 	}
3239 
3240 	return chase_camera;
3241 }
3242 
3243 extern vec3d Dead_camera_pos;
3244 
3245 //	Set eye_pos and eye_orient based on view mode.
game_render_frame_setup()3246 camid game_render_frame_setup()
3247 {
3248     bool fov_changed;
3249 
3250 	if(!Main_camera.isValid())
3251 	{
3252 		Main_camera = cam_create("Main camera");
3253 	}
3254 	camera *main_cam = Main_camera.getCamera();
3255 	if(main_cam == NULL)
3256 	{
3257 		Error(LOCATION, "Unable to generate main camera");
3258 		return camid();
3259 	}
3260 
3261 	vec3d	eye_pos;
3262 	matrix	eye_orient = vmd_identity_matrix;
3263 	vec3d	tmp_dir;
3264 
3265 	static int last_Viewer_mode = 0;
3266 	static int last_Game_mode = 0;
3267 	static int last_Viewer_objnum = -1;
3268 	static float last_FOV = Sexp_fov;
3269 
3270     fov_changed = ((last_FOV != Sexp_fov) && (Sexp_fov > 0.0f));
3271 
3272 	//First, make sure we take into account 2D Missions.
3273 	//These replace the normal player in-cockpit view with a topdown view.
3274 	if(The_mission.flags & MISSION_FLAG_2D_MISSION)
3275 	{
3276 		if(!Viewer_mode)
3277 		{
3278 			Viewer_mode = VM_TOPDOWN;
3279 		}
3280 	}
3281 
3282 	// This code is supposed to detect camera "cuts"... like going between
3283 	// different views.
3284 
3285 	// determine if we need to regenerate the nebula
3286 	if(	(!(last_Viewer_mode & VM_EXTERNAL) && (Viewer_mode & VM_EXTERNAL)) ||							// internal to external
3287 			((last_Viewer_mode & VM_EXTERNAL) && !(Viewer_mode & VM_EXTERNAL)) ||							// external to internal
3288 			(!(last_Viewer_mode & VM_DEAD_VIEW) && (Viewer_mode & VM_DEAD_VIEW)) ||							// non dead-view to dead-view
3289 			((last_Viewer_mode & VM_DEAD_VIEW) && !(Viewer_mode & VM_DEAD_VIEW)) ||							// dead-view to non dead-view
3290 			(!(last_Viewer_mode & VM_WARP_CHASE) && (Viewer_mode & VM_WARP_CHASE)) ||						// non warp-chase to warp-chase
3291 			((last_Viewer_mode & VM_WARP_CHASE) && !(Viewer_mode & VM_WARP_CHASE)) ||						// warp-chase to non warp-chase
3292 			(!(last_Viewer_mode & VM_OTHER_SHIP) && (Viewer_mode & VM_OTHER_SHIP)) ||						// non other-ship to other-ship
3293 			((last_Viewer_mode & VM_OTHER_SHIP) && !(Viewer_mode & VM_OTHER_SHIP)) ||						// other-ship to non-other ship
3294 			(!(last_Viewer_mode & VM_FREECAMERA) && (Viewer_mode & VM_FREECAMERA)) ||
3295 			((last_Viewer_mode & VM_FREECAMERA) && !(Viewer_mode & VM_FREECAMERA)) ||
3296 			(!(last_Viewer_mode & VM_TOPDOWN) && (Viewer_mode & VM_TOPDOWN)) ||
3297 			((last_Viewer_mode & VM_TOPDOWN) && !(Viewer_mode & VM_TOPDOWN)) ||
3298 			(fov_changed) ||
3299 			((Viewer_mode & VM_OTHER_SHIP) && (last_Viewer_objnum != Player_ai->target_objnum)) 		// other ship mode, but targets changes
3300 			) {
3301 
3302 		// regenerate the nebula
3303 		neb2_eye_changed();
3304 	}
3305 
3306 	if ( (last_Viewer_mode != Viewer_mode)
3307 		|| (last_Game_mode != Game_mode)
3308 		|| (fov_changed)
3309 		|| (Viewer_mode & VM_FREECAMERA))	{
3310 		//mprintf(( "************** Camera cut! ************\n" ));
3311 		last_Viewer_mode = Viewer_mode;
3312 		last_Game_mode = Game_mode;
3313 		last_FOV = main_cam->get_fov();
3314 
3315 		// Camera moved.  Tell stars & debris to not do blurring.
3316 		stars_camera_cut();
3317 	}
3318 
3319 	say_view_target();
3320 
3321 	if ( Viewer_mode & VM_PADLOCK_ANY ) {
3322 		player_display_padlock_view();
3323 	}
3324 
3325 	if (Game_mode & GM_DEAD) {
3326 		vec3d	vec_to_deader, view_pos;
3327 		float		dist;
3328 
3329 		Viewer_mode |= VM_DEAD_VIEW;
3330 
3331 		if (Player_ai->target_objnum != -1) {
3332 			int view_from_player = 1;
3333 
3334 			if (Viewer_mode & VM_OTHER_SHIP) {
3335 				//	View from target.
3336 				Viewer_obj = &Objects[Player_ai->target_objnum];
3337 
3338 				last_Viewer_objnum = Player_ai->target_objnum;
3339 
3340 				if ( Viewer_obj->type == OBJ_SHIP ) {
3341 					ship_get_eye( &eye_pos, &eye_orient, Viewer_obj );
3342 					view_from_player = 0;
3343 				}
3344 			} else {
3345 				last_Viewer_objnum = -1;
3346 			}
3347 
3348 			if(Viewer_obj)
3349 				Script_system.SetHookObject("Viewer", Viewer_obj);
3350 			else
3351 				Script_system.RemHookVar("Viewer");
3352 
3353 			if ( view_from_player ) {
3354 				//	View target from player ship.
3355 				Viewer_obj = NULL;
3356 				eye_pos = Player_obj->pos;
3357 				vm_vec_normalized_dir(&tmp_dir, &Objects[Player_ai->target_objnum].pos, &eye_pos);
3358 				vm_vector_2_matrix(&eye_orient, &tmp_dir, NULL, NULL);
3359 				//rtn_cid = ship_get_followtarget_eye( Player_obj );
3360 			}
3361 		} else {
3362 			dist = vm_vec_normalized_dir(&vec_to_deader, &Player_obj->pos, &Dead_camera_pos);
3363 
3364 			if (dist < MIN_DIST_TO_DEAD_CAMERA)
3365 				dist += flFrametime * 16.0f;
3366 
3367 			vm_vec_scale(&vec_to_deader, -dist);
3368 			vm_vec_add(&Dead_camera_pos, &Player_obj->pos, &vec_to_deader);
3369 
3370 			view_pos = Player_obj->pos;
3371 
3372 			if (!(Game_mode & GM_DEAD_BLEW_UP)) {
3373 				Viewer_mode &= ~(VM_EXTERNAL | VM_CHASE);
3374 				vm_vec_scale_add2(&Dead_camera_pos, &Original_vec_to_deader, 25.0f * flFrametime);
3375 				Dead_player_last_vel = Player_obj->phys_info.vel;
3376 				//nprintf(("AI", "Player death roll vel = %7.3f %7.3f %7.3f\n", Player_obj->phys_info.vel.x, Player_obj->phys_info.vel.y, Player_obj->phys_info.vel.z));
3377 			} else if (Player_ai->target_objnum != -1) {
3378 				view_pos = Objects[Player_ai->target_objnum].pos;
3379 			} else {
3380 				//	Make camera follow explosion, but gradually slow down.
3381 				vm_vec_scale_add2(&Player_obj->pos, &Dead_player_last_vel, flFrametime);
3382 				view_pos = Player_obj->pos;
3383 				vm_vec_scale(&Dead_player_last_vel, 0.99f);
3384 				vm_vec_scale_add2(&Dead_camera_pos, &Original_vec_to_deader, MIN(25.0f, vm_vec_mag_quick(&Dead_player_last_vel)) * flFrametime);
3385 			}
3386 
3387 			eye_pos = Dead_camera_pos;
3388 
3389 			vm_vec_normalized_dir(&tmp_dir, &Player_obj->pos, &eye_pos);
3390 
3391 			vm_vector_2_matrix(&eye_orient, &tmp_dir, NULL, NULL);
3392 			Viewer_obj = NULL;
3393 		}
3394 	}
3395 
3396 	// if supernova shockwave
3397 	if(supernova_camera_cut()){
3398 		// no viewer obj
3399 		Viewer_obj = NULL;
3400 
3401 		// call it dead view
3402 		Viewer_mode |= VM_DEAD_VIEW;
3403 
3404 		// set eye pos and orient
3405 		//rtn_cid = supernova_set_view();
3406 		supernova_get_eye(&eye_pos, &eye_orient);
3407 	} else {
3408 		//	If already blown up, these other modes can override.
3409 		if (!(Game_mode & (GM_DEAD | GM_DEAD_BLEW_UP))) {
3410 			Viewer_mode &= ~VM_DEAD_VIEW;
3411 
3412 			if(!(Viewer_mode & VM_FREECAMERA))
3413 				Viewer_obj = Player_obj;
3414 
3415 			if (Viewer_mode & VM_OTHER_SHIP) {
3416 				if (Player_ai->target_objnum != -1){
3417 					Viewer_obj = &Objects[Player_ai->target_objnum];
3418 					last_Viewer_objnum = Player_ai->target_objnum;
3419 				} else {
3420 					Viewer_mode &= ~VM_OTHER_SHIP;
3421 					last_Viewer_objnum = -1;
3422 				}
3423 			} else {
3424 				last_Viewer_objnum = -1;
3425 			}
3426 
3427 			if(Viewer_mode & VM_FREECAMERA) {
3428 				Viewer_obj = NULL;
3429 				return cam_get_current();
3430 			} else if (Viewer_mode & VM_EXTERNAL) {
3431 				matrix	tm, tm2;
3432 
3433 				vm_angles_2_matrix(&tm2, &Viewer_external_info.angles);
3434 				vm_matrix_x_matrix(&tm, &Viewer_obj->orient, &tm2);
3435 
3436 				vm_vec_scale_add(&eye_pos, &Viewer_obj->pos, &tm.vec.fvec, 2.0f * Viewer_obj->radius + Viewer_external_info.distance);
3437 
3438 				vm_vec_sub(&tmp_dir, &Viewer_obj->pos, &eye_pos);
3439 				vm_vec_normalize(&tmp_dir);
3440 				vm_vector_2_matrix(&eye_orient, &tmp_dir, &Viewer_obj->orient.vec.uvec, NULL);
3441 				Viewer_obj = NULL;
3442 
3443 				//	Modify the orientation based on head orientation.
3444 				compute_slew_matrix(&eye_orient, &Viewer_slew_angles);
3445 
3446 			} else if ( Viewer_mode & VM_CHASE ) {
3447 				vec3d	move_dir;
3448 				vec3d aim_pt;
3449 
3450 				if ( Viewer_obj->phys_info.speed < 62.5f )
3451 					move_dir = Viewer_obj->phys_info.vel;
3452 				else {
3453 					move_dir = Viewer_obj->phys_info.vel;
3454 					vm_vec_scale(&move_dir, (62.5f/Viewer_obj->phys_info.speed));
3455 				}
3456 
3457 				vec3d tmp_up;
3458 				matrix eyemat;
3459 				ship_get_eye(&tmp_up, &eyemat, Viewer_obj, false, false);
3460 
3461 				//create a better 3rd person view if this is the player ship
3462 				if (Viewer_obj==Player_obj)
3463 				{
3464 					//get a point 1000m forward of ship
3465 					vm_vec_copy_scale(&aim_pt,&Viewer_obj->orient.vec.fvec,1000.0f);
3466 					vm_vec_add2(&aim_pt,&Viewer_obj->pos);
3467 
3468 					vm_vec_scale_add(&eye_pos, &Viewer_obj->pos, &move_dir, -0.02f * Viewer_obj->radius);
3469 					vm_vec_scale_add2(&eye_pos, &eyemat.vec.fvec, -2.125f * Viewer_obj->radius - Viewer_chase_info.distance);
3470 					vm_vec_scale_add2(&eye_pos, &eyemat.vec.uvec, 0.625f * Viewer_obj->radius + 0.35f * Viewer_chase_info.distance);
3471 					vm_vec_sub(&tmp_dir, &aim_pt, &eye_pos);
3472 					vm_vec_normalize(&tmp_dir);
3473 				}
3474 				else
3475 				{
3476 					vm_vec_scale_add(&eye_pos, &Viewer_obj->pos, &move_dir, -0.02f * Viewer_obj->radius);
3477 					vm_vec_scale_add2(&eye_pos, &eyemat.vec.fvec, -2.5f * Viewer_obj->radius - Viewer_chase_info.distance);
3478 					vm_vec_scale_add2(&eye_pos, &eyemat.vec.uvec, 0.75f * Viewer_obj->radius + 0.35f * Viewer_chase_info.distance);
3479 					vm_vec_sub(&tmp_dir, &Viewer_obj->pos, &eye_pos);
3480 					vm_vec_normalize(&tmp_dir);
3481 				}
3482 
3483 				// JAS: I added the following code because if you slew up using
3484 				// Descent-style physics, tmp_dir and Viewer_obj->orient.vec.uvec are
3485 				// equal, which causes a zero-length vector in the vm_vector_2_matrix
3486 				// call because the up and the forward vector are the same.   I fixed
3487 				// it by adding in a fraction of the right vector all the time to the
3488 				// up vector.
3489 				tmp_up = eyemat.vec.uvec;
3490 				vm_vec_scale_add2( &tmp_up, &eyemat.vec.rvec, 0.00001f );
3491 
3492 				vm_vector_2_matrix(&eye_orient, &tmp_dir, &tmp_up, NULL);
3493 				Viewer_obj = NULL;
3494 
3495 				//	Modify the orientation based on head orientation.
3496 				compute_slew_matrix(&eye_orient, &Viewer_slew_angles);
3497 			} else if ( Viewer_mode & VM_WARP_CHASE ) {
3498 					Warp_camera.get_info(&eye_pos, NULL);
3499 
3500 					ship * shipp = &Ships[Player_obj->instance];
3501 
3502 					vec3d warp_pos = Player_obj->pos;
3503 					shipp->warpout_effect->getWarpPosition(&warp_pos);
3504 					vm_vec_sub(&tmp_dir, &warp_pos, &eye_pos);
3505 					vm_vec_normalize(&tmp_dir);
3506 					vm_vector_2_matrix(&eye_orient, &tmp_dir, &Player_obj->orient.vec.uvec, NULL);
3507 					Viewer_obj = NULL;
3508 			} else if (Viewer_mode & VM_TOPDOWN) {
3509 					angles rot_angles = { PI_2, 0.0f, 0.0f };
3510 					bool position_override = false;
3511 					if(Viewer_obj->type == OBJ_SHIP) {
3512 						ship_info *sip = &Ship_info[Ships[Viewer_obj->instance].ship_info_index];
3513 						if(sip->topdown_offset_def) {
3514 							eye_pos.xyz.x = Viewer_obj->pos.xyz.x + sip->topdown_offset.xyz.x;
3515 							eye_pos.xyz.y = Viewer_obj->pos.xyz.y + sip->topdown_offset.xyz.y;
3516 							eye_pos.xyz.z = Viewer_obj->pos.xyz.z + sip->topdown_offset.xyz.z;
3517 							position_override = true;
3518 						}
3519 					}
3520 					if(!position_override) {
3521 						eye_pos.xyz.x = Viewer_obj->pos.xyz.x;
3522 						eye_pos.xyz.y = Viewer_obj->pos.xyz.y + Viewer_obj->radius * 25.0f;
3523 						eye_pos.xyz.z = Viewer_obj->pos.xyz.z;
3524 					}
3525 					vm_angles_2_matrix(&eye_orient, &rot_angles);
3526 					Viewer_obj = NULL;
3527 			} else {
3528 				// get an eye position based upon the correct type of object
3529 				switch(Viewer_obj->type){
3530 				case OBJ_SHIP:
3531 					// make a call to get the eye point for the player object
3532 					ship_get_eye( &eye_pos, &eye_orient, Viewer_obj );
3533 					break;
3534 				case OBJ_OBSERVER:
3535 					// make a call to get the eye point for the player object
3536 					observer_get_eye( &eye_pos, &eye_orient, Viewer_obj );
3537 					break;
3538 				default :
3539 					mprintf(("Invalid Value for Viewer_obj->type. Expected values are OBJ_SHIP (1) and OBJ_OBSERVER (12), we encountered %d. Please tell a coder.\n", Viewer_obj->type));
3540 					Int3();
3541 				}
3542 
3543 				#ifdef JOHNS_DEBUG_CODE
3544 				john_debug_stuff(&eye_pos, &eye_orient);
3545 				#endif
3546 			}
3547 		}
3548 	}
3549 
3550 	main_cam->set_position(&eye_pos);
3551 	main_cam->set_rotation(&eye_orient);
3552 
3553 	// setup neb2 rendering
3554 	neb2_render_setup(Main_camera);
3555 
3556 	return Main_camera;
3557 }
3558 
3559 #ifndef NDEBUG
3560 extern void ai_debug_render_stuff();
3561 #endif
3562 
3563 int Game_subspace_effect = 0;
3564 DCF_BOOL( subspace, Game_subspace_effect )
3565 
3566 void clip_frame_view();
3567 
3568 // Does everything needed to render a frame
3569 extern SCP_vector<object*> effect_ships;
game_render_frame(camid cid)3570 void game_render_frame( camid cid )
3571 {
3572 
3573 	g3_start_frame(game_zbuffer);
3574 
3575 	camera *cam = cid.getCamera();
3576 	matrix eye_no_jitter = vmd_identity_matrix;
3577 	if(cam != NULL)
3578 	{
3579 		vec3d eye_pos;
3580 		matrix eye_orient;
3581 
3582 		//Get current camera info
3583 		cam->get_info(&eye_pos, &eye_orient);
3584 
3585 		//Handle jitter if not cutscene camera
3586 		eye_no_jitter = eye_orient;
3587 		if( !(Viewer_mode & VM_FREECAMERA) ) {
3588 			apply_view_shake(&eye_orient);
3589 			cam->set_rotation(&eye_orient);
3590 		}
3591 
3592 		//Maybe override FOV from SEXP
3593 		if(Sexp_fov <= 0.0f)
3594 			g3_set_view_matrix(&eye_pos, &eye_orient, cam->get_fov());
3595 		else
3596 			g3_set_view_matrix(&eye_pos, &eye_orient, Sexp_fov);
3597 	}
3598 	else
3599 	{
3600 		g3_set_view_matrix(&vmd_zero_vector, &vmd_identity_matrix, VIEWER_ZOOM_DEFAULT);
3601 	}
3602 
3603 	// maybe offset the HUD (jitter stuff) and measure the 2D displacement between the player's view and ship vector
3604 	int dont_offset = ((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_OBSERVER));
3605 	HUD_set_offsets(Viewer_obj, !dont_offset, &eye_no_jitter);
3606 
3607 	// for multiplayer clients, call code in Shield.cpp to set up the Shield_hit array.  Have to
3608 	// do this becaues of the disjointed nature of this system (in terms of setup and execution).
3609 	// must be done before ships are rendered
3610 	if ( MULTIPLAYER_CLIENT ) {
3611 		shield_point_multi_setup();
3612 	}
3613 
3614 	// this needs to happen after g3_start_frame() and before the primary projection and view matrix is setup
3615 	if ( Cmdline_env && !Env_cubemap_drawn ) {
3616 		setup_environment_mapping(cid);
3617 
3618 		if ( !Dynamic_environment ) {
3619 			Env_cubemap_drawn = true;
3620 		}
3621 	}
3622 	gr_zbuffer_clear(TRUE);
3623 
3624 	gr_scene_texture_begin();
3625 
3626 	neb2_render_setup(cid);
3627 
3628 #ifndef DYN_CLIP_DIST
3629 	if (!Cmdline_nohtl) {
3630 		gr_set_proj_matrix(Proj_fov, gr_screen.clip_aspect, Min_draw_distance, Max_draw_distance);
3631 		gr_set_view_matrix(&Eye_position, &Eye_matrix);
3632 	}
3633 #endif
3634 
3635 	if ( Game_subspace_effect )	{
3636 		stars_draw(0,0,0,1,0);
3637 	} else {
3638 		stars_draw(1,1,1,0,0);
3639 	}
3640 
3641 	bool draw_viewer_last = false;
3642 	obj_render_all(obj_render, &draw_viewer_last);
3643 
3644 	//	Why do we not show the shield effect in these modes?  Seems ok.
3645 	//if (!(Viewer_mode & (VM_EXTERNAL | VM_SLEWED | VM_CHASE | VM_DEAD_VIEW))) {
3646 	render_shields();
3647 	//}
3648 
3649 	PROFILE("Particles", particle_render_all());					// render particles after everything else.
3650 
3651 #ifdef DYN_CLIP_DIST
3652 	if(!Cmdline_nohtl)
3653 	{
3654 		gr_end_proj_matrix();
3655 		gr_end_view_matrix();
3656 		gr_set_proj_matrix(Proj_fov, gr_screen.clip_aspect, Min_draw_distance, Max_draw_distance);
3657 		gr_set_view_matrix(&Eye_position, &Eye_matrix);
3658 	}
3659 #endif
3660 
3661 	beam_render_all();						// render all beam weapons
3662 
3663 	PROFILE("Trails", trail_render_all());						// render missilie trails after everything else.
3664 
3665 	// render nebula lightning
3666 	nebl_render_all();
3667 
3668 	// render local player nebula
3669 	neb2_render_player();
3670 
3671 	// render all ships with shader effects on them
3672 	SCP_vector<object*>::iterator obji = effect_ships.begin();
3673 	for(;obji != effect_ships.end();++obji)
3674 		ship_render(*obji);
3675 	effect_ships.clear();
3676 	//Draw the viewer 'cause we didn't before.
3677 	//This is so we can change the minimum clipping distance without messing everything up.
3678 	if(draw_viewer_last && Viewer_obj)
3679 	{
3680 		gr_post_process_save_zbuffer();
3681 		ship_render_show_ship_cockpit(Viewer_obj);
3682 	}
3683 
3684 
3685 #ifndef NDEBUG
3686 	ai_debug_render_stuff();
3687 	extern void snd_spew_debug_info();
3688 	snd_spew_debug_info();
3689 #endif
3690 
3691 	if(!Cmdline_nohtl)
3692 	{
3693 		gr_end_proj_matrix();
3694 		gr_end_view_matrix();
3695 	}
3696 
3697 	//Draw viewer cockpit
3698 	if(Viewer_obj != NULL && Viewer_mode != VM_TOPDOWN && Ship_info[Ships[Viewer_obj->instance].ship_info_index].cockpit_model_num > 0)
3699 	{
3700 		gr_post_process_save_zbuffer();
3701 		ship_render_cockpit(Viewer_obj);
3702 	}
3703 
3704 	if (!Cmdline_nohtl) {
3705 		gr_set_proj_matrix(Proj_fov, gr_screen.clip_aspect, Min_draw_distance, Max_draw_distance);
3706 		gr_set_view_matrix(&Eye_position, &Eye_matrix);
3707 
3708 		// Do the sunspot
3709 		game_sunspot_process(flFrametime);
3710 
3711 		gr_end_proj_matrix();
3712 		gr_end_view_matrix();
3713 	}
3714 
3715 	//================ END OF 3D RENDERING STUFF ====================
3716 
3717 	gr_scene_texture_end();
3718 
3719 	extern int Multi_display_netinfo;
3720 	if(Multi_display_netinfo){
3721 		extern void multi_display_netinfo();
3722 		multi_display_netinfo();
3723 	}
3724 
3725 	game_tst_frame_pre();
3726 
3727 #ifndef NDEBUG
3728 	do_timing_test(flFrametime);
3729 
3730 	extern int OO_update_index;
3731 	multi_rate_display(OO_update_index, 375, 0);
3732 
3733 	// test
3734 	extern void oo_display();
3735 	oo_display();
3736 #endif
3737 
3738 	g3_end_frame();
3739 }
3740 
3741 //#define JOHNS_DEBUG_CODE	1
3742 
3743 #ifdef JOHNS_DEBUG_CODE
john_debug_stuff(vec3d * eye_pos,matrix * eye_orient)3744 void john_debug_stuff(vec3d *eye_pos, matrix *eye_orient)
3745 {
3746 	//if ( keyd_pressed[KEY_LSHIFT] )
3747 	{
3748 		ship_subsys *tsys = Players[Player_num].targeted_subobject;
3749 		if ( tsys )	{
3750 			model_subsystem *turret = tsys->system_info;
3751 
3752 			if (turret->type == SUBSYSTEM_TURRET )	{
3753 				vec3d fvec, uvec;
3754 				object * tobj = &Objects[Players[Player_num].targeted_subobject_parent];
3755 
3756 				ship_model_start(tobj);
3757 
3758 				model_find_world_point(eye_pos, &turret->turret_firing_point[0], turret->model_num, turret->turret_gun_sobj, &tobj->orient, &tobj->pos );
3759 				model_find_world_dir(&fvec, &turret->turret_matrix.vec.fvec, turret->model_num, turret->turret_gun_sobj, &tobj->orient, NULL );
3760 				model_find_world_dir(&uvec, &turret->turret_matrix.vec.uvec, turret->model_num, turret->turret_gun_sobj, &tobj->orient, NULL );
3761 
3762 				vm_vector_2_matrix( eye_orient, &fvec, &uvec, NULL );
3763 
3764 				ship_model_stop(tobj);
3765 
3766 				Viewer_obj = NULL;
3767 			}
3768 		}
3769 
3770 	}
3771 }
3772 #endif
3773 
3774 // following function for dumping frames for purposes of building trailers.
3775 #ifndef NDEBUG
3776 
3777 // function to toggle state of dumping every frame into PCX when playing the game
3778 DCF(dump_frames, "Starts/stop frame dumping at 15 hz")
3779 {
3780 	if ( Dc_command )	{
3781 
3782 		if ( Debug_dump_frames == 0 )	{
3783 			// Turn it on
3784 			Debug_dump_frames = 15;
3785 			Debug_dump_trigger = 0;
3786 			gr_dump_frame_start( Debug_dump_frame_num, DUMP_BUFFER_NUM_FRAMES );
3787 			dc_printf( "Frame dumping at 15 hz is now ON\n" );
3788 		} else {
3789 			// Turn it off
3790 			Debug_dump_frames = 0;
3791 			Debug_dump_trigger = 0;
3792 			gr_dump_frame_stop();
3793 			dc_printf( "Frame dumping is now OFF\n" );
3794 		}
3795 
3796 	}
3797 }
3798 
3799 DCF(dump_frames_trigger, "Starts/stop frame dumping at 15 hz")
3800 {
3801 	if ( Dc_command )	{
3802 
3803 		if ( Debug_dump_frames == 0 )	{
3804 			// Turn it on
3805 			Debug_dump_frames = 15;
3806 			Debug_dump_trigger = 1;
3807 			gr_dump_frame_start( Debug_dump_frame_num, DUMP_BUFFER_NUM_FRAMES );
3808 			dc_printf( "Frame dumping at 15 hz is now ON\n" );
3809 		} else {
3810 			// Turn it off
3811 			Debug_dump_frames = 0;
3812 			Debug_dump_trigger = 0;
3813 			gr_dump_frame_stop();
3814 			dc_printf( "Frame dumping is now OFF\n" );
3815 		}
3816 
3817 	}
3818 }
3819 
3820 DCF(dump_frames30, "Starts/stop frame dumping at 30 hz")
3821 {
3822 	if ( Dc_command )	{
3823 
3824 		if ( Debug_dump_frames == 0 )	{
3825 			// Turn it on
3826 			Debug_dump_frames = 30;
3827 			Debug_dump_trigger = 0;
3828 			gr_dump_frame_start( Debug_dump_frame_num, DUMP_BUFFER_NUM_FRAMES );
3829 			dc_printf( "Frame dumping at 30 hz is now ON\n" );
3830 		} else {
3831 			// Turn it off
3832 			Debug_dump_frames = 0;
3833 			Debug_dump_trigger = 0;
3834 			gr_dump_frame_stop();
3835 			dc_printf( "Frame dumping is now OFF\n" );
3836 		}
3837 
3838 	}
3839 }
3840 
3841 DCF(dump_frames30_trigger, "Starts/stop frame dumping at 30 hz")
3842 {
3843 	if ( Dc_command )	{
3844 
3845 		if ( Debug_dump_frames == 0 )	{
3846 			// Turn it on
3847 			Debug_dump_frames = 30;
3848 			Debug_dump_trigger = 1;
3849 			gr_dump_frame_start( Debug_dump_frame_num, DUMP_BUFFER_NUM_FRAMES );
3850 			dc_printf( "Triggered frame dumping at 30 hz is now ON\n" );
3851 		} else {
3852 			// Turn it off
3853 			Debug_dump_frames = 0;
3854 			Debug_dump_trigger = 0;
3855 			gr_dump_frame_stop();
3856 			dc_printf( "Triggered frame dumping is now OFF\n" );
3857 		}
3858 
3859 	}
3860 }
3861 
game_maybe_dump_frame()3862 void game_maybe_dump_frame()
3863 {
3864 	if ( !Debug_dump_frames ){
3865 		return;
3866 	}
3867 
3868 	if( Debug_dump_trigger && !keyd_pressed[KEY_Q] ){
3869 		return;
3870 	}
3871 
3872 	game_stop_time();
3873 
3874 	gr_dump_frame();
3875 	Debug_dump_frame_num++;
3876 
3877 	game_start_time();
3878 }
3879 #endif
3880 
3881 extern int Player_dead_state;
3882 
3883 //	Flip the page and time how long it took.
game_flip_page_and_time_it()3884 void game_flip_page_and_time_it()
3885 {
3886 	fix t1, t2,d;
3887 	int t;
3888 	t1 = timer_get_fixed_seconds();
3889 	gr_flip();
3890 	t2 = timer_get_fixed_seconds();
3891 	d = t2 - t1;
3892 	t = (gr_screen.max_w*gr_screen.max_h*gr_screen.bytes_per_pixel)/1024;
3893 	sprintf( transfer_text, NOX("%d MB/s"), (int)fixmuldiv(t,65,d) );
3894 }
3895 
game_simulation_frame()3896 void game_simulation_frame()
3897 {
3898 	//Do camera stuff
3899 	//This is for the warpout cam
3900 	if ( Player->control_mode != PCM_NORMAL )
3901 		Warp_camera.do_frame(flFrametime);
3902 
3903 	//Do ingame cutscenes stuff
3904 	if(!Time_compression_locked)
3905 	{
3906 		cam_do_frame(flFrametime);
3907 	}
3908 	else
3909 	{
3910 		cam_do_frame(flRealframetime);
3911 	}
3912 
3913 	// blow ships up in multiplayer dogfight
3914 	if( MULTIPLAYER_MASTER && (Net_player != NULL) && (Netgame.type_flags & NG_TYPE_DOGFIGHT) && (f2fl(Missiontime) >= 2.0f) && !dogfight_blown){
3915 		// blow up all non-player ships
3916 		ship_obj *moveup = GET_FIRST(&Ship_obj_list);
3917 		ship *shipp;
3918 		ship_info *sip;
3919 		while((moveup != END_OF_LIST(&Ship_obj_list)) && (moveup != NULL)){
3920 			// bogus
3921 			if((moveup->objnum < 0) || (moveup->objnum >= MAX_OBJECTS) || (Objects[moveup->objnum].type != OBJ_SHIP) || (Objects[moveup->objnum].instance < 0) || (Objects[moveup->objnum].instance >= MAX_SHIPS) || (Ships[Objects[moveup->objnum].instance].ship_info_index < 0) || (Ships[Objects[moveup->objnum].instance].ship_info_index >= Num_ship_classes)){
3922 				moveup = GET_NEXT(moveup);
3923 				continue;
3924 			}
3925 			shipp = &Ships[Objects[moveup->objnum].instance];
3926 			sip = &Ship_info[shipp->ship_info_index];
3927 
3928 			// only blow up small ships
3929 			if((sip->flags & SIF_SMALL_SHIP) && (multi_find_player_by_object(&Objects[moveup->objnum]) < 0) && (shipp->team == Iff_traitor) ){
3930 				// function to simply explode a ship where it is currently at
3931 				ship_self_destruct( &Objects[moveup->objnum] );
3932 			}
3933 
3934 			moveup = GET_NEXT(moveup);
3935 		}
3936 
3937 		dogfight_blown = 1;
3938 	}
3939 
3940 	// process AWACS stuff - do this first thing
3941 	awacs_process();
3942 
3943 	//Do autopilot stuff
3944 	NavSystem_Do();
3945 
3946 	// single player, set Player hits_this_frame to 0
3947 	if ( !(Game_mode & GM_MULTIPLAYER) && Player ) {
3948 		Player->damage_this_burst -= (flFrametime * MAX_BURST_DAMAGE  / (0.001f * BURST_DURATION));
3949 		Player->damage_this_burst = MAX(Player->damage_this_burst, 0.0f);
3950 	}
3951 
3952 	// supernova
3953 	supernova_process();
3954 	if(supernova_active() >= 5){
3955 		return;
3956 	}
3957 
3958 	// fire targeting lasers now so that
3959 	// 1 - created this frame
3960 	// 2 - collide this frame
3961 	// 3 - render this frame
3962 	// 4 - ignored and deleted next frame
3963 	// the basic idea being that because it con be confusing to deal with them on a multi-frame basis, they are only valid for
3964 	// frame
3965 	ship_process_targeting_lasers();
3966 
3967 	// do this here so that it works for multiplayer
3968 	if ( Viewer_obj ) {
3969 		// get viewer direction
3970 		int viewer_direction = PHYSICS_VIEWER_REAR;
3971 
3972 		if(Viewer_mode == 0){
3973 			viewer_direction = PHYSICS_VIEWER_FRONT;
3974 		}
3975 		if(Viewer_mode & VM_PADLOCK_UP){
3976 			viewer_direction = PHYSICS_VIEWER_UP;
3977 		}
3978 		else if(Viewer_mode & VM_PADLOCK_REAR){
3979 			viewer_direction = PHYSICS_VIEWER_REAR;
3980 		}
3981 		else if(Viewer_mode & VM_PADLOCK_LEFT){
3982 			viewer_direction = PHYSICS_VIEWER_LEFT;
3983 		}
3984 		else if(Viewer_mode & VM_PADLOCK_RIGHT){
3985 			viewer_direction = PHYSICS_VIEWER_RIGHT;
3986 		}
3987 
3988 		physics_set_viewer( &Viewer_obj->phys_info, viewer_direction );
3989 	} else {
3990 		physics_set_viewer( &Objects[Player->objnum].phys_info, PHYSICS_VIEWER_FRONT );
3991 	}
3992 
3993 	// evaluate mission departures and arrivals before we process all objects.
3994 	if ( !(Game_mode & GM_MULTIPLAYER) || (MULTIPLAYER_MASTER && !multi_endgame_ending()) ) {
3995 
3996 		// we don't want to evaluate mission stuff when any ingame joiner in multiplayer is receiving
3997 		// ships/wing packets.
3998 		if ( !((Game_mode & GM_MULTIPLAYER) && (Netgame.flags & NG_FLAG_INGAME_JOINING_CRITICAL))) {
3999 			mission_parse_eval_stuff();
4000 		}
4001 
4002 		// if we're an observer, move ourselves seperately from the standard physics
4003 		if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_OBSERVER)){
4004 			obj_observer_move(flFrametime);
4005 		}
4006 
4007 		// move all the objects now
4008 		PROFILE("Move Objects - Master", obj_move_all(flFrametime));
4009 
4010 		mission_eval_goals();
4011 	}
4012 
4013 	// always check training objectives, even in multiplayer missions. we need to do this so that the directives gauge works properly on clients
4014 	training_check_objectives();
4015 
4016 	// do all interpolation now
4017 	if ( MULTIPLAYER_CLIENT && !multi_endgame_ending() && !(Netgame.flags & NG_FLAG_SERVER_LOST)) {
4018 		// client side processing of warping in effect stages
4019 		multi_do_client_warp(flFrametime);
4020 
4021 		// client side movement of an observer
4022 		if((Net_player->flags & NETINFO_FLAG_OBSERVER) || (Player_obj->type == OBJ_OBSERVER)){
4023 			obj_observer_move(flFrametime);
4024 		}
4025 
4026 		// move all objects - does interpolation now as well
4027 		PROFILE("Move Objects - Client", obj_move_all(flFrametime));
4028 
4029 
4030 	}
4031 
4032 	// only process the message queue when the player is "in" the game
4033 	if ( !Pre_player_entry ){
4034 		message_queue_process();				// process any messages send to the player
4035 	}
4036 
4037 	message_maybe_distort();				// maybe distort incoming message if comms damaged
4038 	player_repair_frame(flFrametime);	//	AI objects get repaired in ai_process, called from move code...deal with player.
4039 	player_process_pending_praise();		// maybe send off a delayed praise message to the player
4040 	player_maybe_play_all_alone_msg();	// maybe tell the player he is all alone
4041 
4042 	if(!(Game_mode & GM_STANDALONE_SERVER)){
4043 		// process some stuff every frame (before frame is rendered)
4044 		emp_process_local();
4045 
4046 		hud_update_frame(flFrametime);						// update hud systems
4047 
4048 		if (!physics_paused)	{
4049 			// Move particle system
4050 			PROFILE("Move Particles", particle_move_all(flFrametime));
4051 
4052 			// Move missile trails
4053 			PROFILE("Move Trails", trail_move_all(flFrametime));
4054 
4055 			// Flash the gun flashes
4056 			shipfx_flash_do_frame(flFrametime);
4057 
4058 			shockwave_move_all(flFrametime);	// update all the shockwaves
4059 		}
4060 
4061 		// subspace missile strikes
4062 		ssm_process();
4063 
4064 		obj_snd_do_frame();						// update the object-linked persistant sounds
4065 
4066 		game_maybe_update_sound_environment();
4067 		snd_update_listener(&Eye_position, &Player_obj->phys_info.vel, &Eye_matrix);
4068 
4069 // AL: debug code used for testing ambient subspace sound (ie when enabling subspace through debug console)
4070 #ifndef NDEBUG
4071 		if ( Game_subspace_effect ) {
4072 			game_start_subspace_ambient_sound();
4073 		}
4074 #endif
4075 	}
4076 
4077 	Script_system.RunBytecode(Script_simulationhook);
4078 }
4079 
4080 // Maybe render and process the dead-popup
game_maybe_do_dead_popup(float frametime)4081 void game_maybe_do_dead_popup(float frametime)
4082 {
4083 	if ( popupdead_is_active() ) {
4084 		int leave_popup=1;
4085 		int choice = popupdead_do_frame(frametime);
4086 
4087 		if ( Game_mode & GM_NORMAL ) {
4088 			switch(choice) {
4089 			case 0:
4090 				gameseq_post_event(GS_EVENT_ENTER_GAME);
4091 				break;
4092 
4093 			case 1:
4094 				gameseq_post_event(GS_EVENT_END_GAME);
4095 				break;
4096 
4097 			case 2:
4098 				gameseq_post_event(GS_EVENT_START_GAME);
4099 				break;
4100 
4101 			// this should only happen during a red alert mission
4102 			case 3:
4103 				if (The_mission.flags & MISSION_FLAG_RED_ALERT)
4104 				{
4105 					// choose the previous mission
4106 					mission_campaign_previous_mission();
4107 				}
4108 				else
4109 				{
4110 					// bogus?
4111 					Int3();
4112 				}
4113 
4114 				gameseq_post_event(GS_EVENT_START_GAME);
4115 				break;
4116 
4117 			default:
4118 				leave_popup=0;
4119 				break;
4120 			}
4121 		} else {
4122 			switch( choice ) {
4123 
4124 			case POPUPDEAD_DO_MAIN_HALL:
4125 				multi_quit_game(PROMPT_NONE,-1);
4126 				break;
4127 
4128 			case POPUPDEAD_DO_RESPAWN:
4129 				multi_respawn_normal();
4130 				event_music_player_respawn();
4131 				break;
4132 
4133 			case POPUPDEAD_DO_OBSERVER:
4134 				multi_respawn_observer();
4135 				event_music_player_respawn_as_observer();
4136 				break;
4137 
4138 			default:
4139 				leave_popup = 0;
4140 				break;
4141 			}
4142 		}
4143 
4144 		if ( leave_popup ) {
4145 			popupdead_close();
4146 		}
4147 	}
4148 }
4149 
4150 // returns true if player is actually in a game_play stats
game_actually_playing()4151 int game_actually_playing()
4152 {
4153 	int state;
4154 
4155 	state = gameseq_get_state();
4156 	if ( (state != GS_STATE_GAME_PLAY) && (state != GS_STATE_DEATH_DIED) && (state != GS_STATE_DEATH_BLEW_UP) )
4157 		return 0;
4158 	else
4159 		return 1;
4160 }
4161 
game_render_hud(camid cid)4162 void game_render_hud(camid cid)
4163 {
4164 	gr_reset_clip();
4165 
4166 	if(cid.isValid()) {
4167 		g3_start_frame(0);		// 0 = turn zbuffering off
4168 		g3_set_view( cid.getCamera() );
4169 
4170 		hud_render_preprocess(flFrametime);
4171 
4172 		g3_end_frame();
4173 	}
4174 
4175 	// main HUD rendering function
4176 	hud_render_all();
4177 
4178 	// Diminish the palette effect
4179 	game_flash_diminish(flFrametime);
4180 }
4181 
4182 //100% blackness
game_reset_shade_frame()4183 void game_reset_shade_frame()
4184 {
4185 	Fade_type = FI_NONE;
4186 	gr_create_shader(&Viewer_shader, 0, 0, 0, 0);
4187 }
4188 
game_shade_frame(float frametime)4189 void game_shade_frame(float frametime)
4190 {
4191 	// only do frame shade if we are actually in a game play state
4192 	if ( !game_actually_playing() ) {
4193 		return;
4194 	}
4195 
4196 	if (Fade_type != FI_NONE) {
4197 		Assert(Fade_start_timestamp > 0);
4198 		Assert(Fade_end_timestamp > 0);
4199 		Assert(Fade_end_timestamp > Fade_start_timestamp);
4200 
4201 		if( timestamp() >= Fade_start_timestamp ) {
4202 			int startAlpha = 0;
4203 			int endAlpha = 0;
4204 
4205 			if (Fade_type == FI_FADEOUT) {
4206 				endAlpha = 255;
4207 			} else if (Fade_type == FI_FADEIN) {
4208 				startAlpha = 255;
4209 			}
4210 
4211 			int alpha = 0;
4212 
4213 			if( timestamp() < Fade_end_timestamp ) {
4214 				int duration = (Fade_end_timestamp - Fade_start_timestamp);
4215 				int elapsed = (timestamp() - Fade_start_timestamp);
4216 
4217 				alpha = fl2i( (float)startAlpha + (((float)endAlpha - (float)startAlpha) / (float)duration) * (float)elapsed );
4218 			} else {
4219 				//Fade finished
4220 				Fade_type = FI_NONE;
4221 				Fade_start_timestamp = 0;
4222 				Fade_end_timestamp = 0;
4223 
4224 				alpha = endAlpha;
4225 			}
4226 
4227 			Viewer_shader.c = (ubyte)alpha;
4228 		}
4229 	}
4230 
4231 	gr_flash_alpha(Viewer_shader.r, Viewer_shader.g, Viewer_shader.b, Viewer_shader.c);
4232 }
4233 
4234 const static int CUTSCENE_BAR_DIVISOR = 8;
bars_do_frame(float frametime)4235 void bars_do_frame(float frametime)
4236 {
4237 	if((Cutscene_bar_flags & CUB_GRADUAL) && Cutscene_bars_progress < 1.0f)
4238 	{
4239 		//Determine how far along we are
4240 		Assert(Cutscene_delta_time > 0.0f);
4241 
4242 		Cutscene_bars_progress += frametime / Cutscene_delta_time;
4243 		if(Cutscene_bars_progress >= 1.0f)
4244 		{
4245 			//Reset this stuff
4246 			Cutscene_delta_time = 1.0f;
4247 			Cutscene_bars_progress = 1.0f;
4248 		}
4249 
4250 		//Figure out where the bars should be
4251 		int yborder;
4252 		if(Cutscene_bar_flags & CUB_CUTSCENE)
4253 			yborder = fl2i(Cutscene_bars_progress*(gr_screen.max_h/CUTSCENE_BAR_DIVISOR));
4254 		else
4255 			yborder = gr_screen.max_h/CUTSCENE_BAR_DIVISOR - fl2i(Cutscene_bars_progress*(gr_screen.max_h/CUTSCENE_BAR_DIVISOR));
4256 
4257 		if (g3_in_frame() == 0) {
4258 			//Set rectangles
4259 			gr_set_color(0,0,0);
4260 			gr_set_bitmap(0); // Valathil - Don't ask me why this has to be here but otherwise the black bars don't draw
4261 			gr_rect(0, 0, gr_screen.max_w, yborder, GR_RESIZE_NONE);
4262 			gr_rect(0, gr_screen.max_h-yborder, gr_screen.max_w, yborder, GR_RESIZE_NONE);
4263 		} else {
4264 			//Set clipping
4265 			gr_reset_clip();
4266 			gr_set_clip(0, yborder, gr_screen.max_w, gr_screen.max_h - yborder*2, GR_RESIZE_NONE );
4267 		}
4268 	}
4269 	else if(Cutscene_bar_flags & CUB_CUTSCENE)
4270 	{
4271 		int yborder = gr_screen.max_h/CUTSCENE_BAR_DIVISOR;
4272 
4273 		if (g3_in_frame() == 0) {
4274 			gr_set_color(0,0,0);
4275 			gr_set_bitmap(0); // Valathil - Don't ask me why this has to be here but otherwise the black bars don't draw
4276 			gr_rect(0, 0, gr_screen.max_w, yborder, GR_RESIZE_NONE);
4277 			gr_rect(0, gr_screen.max_h-yborder, gr_screen.max_w, yborder, GR_RESIZE_NONE);
4278 		} else {
4279 			gr_reset_clip();
4280 			gr_set_clip(0, yborder, gr_screen.max_w, gr_screen.max_h - (yborder*2), GR_RESIZE_NONE );
4281 		}
4282 	}
4283 }
4284 
clip_frame_view()4285 void clip_frame_view() {
4286 	if(!Time_compression_locked) {
4287 		// Player is dead
4288 		game_set_view_clip(flFrametime);
4289 
4290 		// Cutscene bars
4291 		bars_do_frame(flRealframetime);
4292 	} else {
4293 		// Player is dead
4294 		game_set_view_clip(flRealframetime);
4295 
4296 		// Cutscene bars
4297 		bars_do_frame(flRealframetime);
4298 	}
4299 }
4300 
4301 //WMC - This does stuff like fading in and out and subtitles. Special FX?
4302 //Basically stuff you need rendered after everything else (including HUD)
game_render_post_frame()4303 void game_render_post_frame()
4304 {
4305 	float frametime = flFrametime;
4306 	if(Time_compression_locked)
4307 	{
4308 		frametime = flRealframetime;
4309 	}
4310 
4311 	subtitles_do_frame(frametime);
4312 	game_shade_frame(frametime);
4313 	subtitles_do_frame_post_shaded(frametime);
4314 }
4315 
4316 #ifndef NDEBUG
4317 #define DEBUG_GET_TIME(x)	{ x = timer_get_fixed_seconds(); }
4318 #else
4319 #define DEBUG_GET_TIME(x)
4320 #endif
4321 
game_frame(bool paused)4322 void game_frame(bool paused)
4323 {
4324 #ifndef NDEBUG
4325 	fix total_time1, total_time2;
4326 	fix render2_time1=0, render2_time2=0;
4327 	fix render3_time1=0, render3_time2=0;
4328 	fix flip_time1=0, flip_time2=0;
4329 	fix clear_time1=0, clear_time2=0;
4330 #endif
4331 	int actually_playing;
4332 
4333 #ifndef NDEBUG
4334 	if (Framerate_delay) {
4335 		int	start_time = timer_get_milliseconds();
4336 		while (timer_get_milliseconds() < start_time + Framerate_delay)
4337 			;
4338 	}
4339 #endif
4340 	// start timing frame
4341 	timing_frame_start();
4342 	profile_begin("Main Frame");
4343 
4344 	DEBUG_GET_TIME( total_time1 )
4345 
4346 	if(paused)
4347 	{
4348 		// Reset the lights here or they just keep on increasing
4349 		light_reset();
4350 	}
4351 	else
4352 	{
4353 		// var to hold which state we are in
4354 		actually_playing = game_actually_playing();
4355 
4356 		if ((!(Game_mode & GM_MULTIPLAYER)) || ((Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_OBSERVER))) {
4357 			if (!(Game_mode & GM_STANDALONE_SERVER)){
4358 				Assert( OBJ_INDEX(Player_obj) >= 0 );
4359 			}
4360 		}
4361 
4362 		if (Missiontime > Entry_delay_time){
4363 			Pre_player_entry = 0;
4364 		} else {
4365 			; //nprintf(("AI", "Framecount = %i, time = %7.3f\n", Framecount, f2fl(Missiontime)));
4366 		}
4367 
4368 		//	Note: These are done even before the player enters, else buffers can overflow.
4369 		if (! (Game_mode & GM_STANDALONE_SERVER)){
4370 			radar_frame_init();
4371 		}
4372 
4373 		shield_frame_init();
4374 
4375 		if ( !Pre_player_entry && actually_playing ) {
4376 			if (! (Game_mode & GM_STANDALONE_SERVER) ) {
4377 
4378 				if( (!popup_running_state()) && (!popupdead_is_active()) ){
4379 					game_process_keys();
4380 					read_player_controls( Player_obj, flFrametime);
4381 				}
4382 
4383 				// if we're not the master, we may have to send the server-critical ship status button_info bits
4384 				if (MULTIPLAYER_CLIENT && !(Net_player->flags & NETINFO_FLAG_OBSERVER)){
4385 					multi_maybe_send_ship_status();
4386 				}
4387 			}
4388 		}
4389 
4390 		// Reset the whack stuff
4391 		game_whack_reset();
4392 
4393 		// These two lines must be outside of Pre_player_entry code,
4394 		// otherwise too many lights are added.
4395 		light_reset();
4396 
4397 		if ((Game_mode & GM_MULTIPLAYER) && (Netgame.game_state == NETGAME_STATE_SERVER_TRANSFER)){
4398 			return;
4399 		}
4400 
4401 		PROFILE("Simulation", game_simulation_frame());
4402 
4403 		// if not actually in a game play state, then return.  This condition could only be true in
4404 		// a multiplayer game.
4405 		if (!actually_playing ) {
4406 			Assert( Game_mode & GM_MULTIPLAYER );
4407 			return;
4408 		}
4409 
4410 	}
4411 
4412 	if (!Pre_player_entry) {
4413 		if (! (Game_mode & GM_STANDALONE_SERVER)) {
4414 			DEBUG_GET_TIME( clear_time1 )
4415 			// clear the screen to black
4416 			gr_reset_clip();
4417 			if ( (Game_detail_flags & DETAIL_FLAG_CLEAR) ) {
4418 				gr_clear();
4419 			}
4420 
4421 			if(Player_obj)
4422 				Script_system.SetHookObject("Player", Player_obj);
4423 			else
4424 				Script_system.RemHookVar("Player");
4425 
4426 			DEBUG_GET_TIME( clear_time2 )
4427 			DEBUG_GET_TIME( render3_time1 )
4428 
4429 			camid cid = game_render_frame_setup();
4430 
4431 			PROFILE("Render", game_render_frame( cid ));
4432 
4433 			//Cutscene bars
4434 			clip_frame_view();
4435 
4436 			// save the eye position and orientation
4437 			if ( Game_mode & GM_MULTIPLAYER ) {
4438 				cid.getCamera()->get_info(&Net_player->s_info.eye_pos, &Net_player->s_info.eye_orient);
4439 			}
4440 
4441 			Scripting_didnt_draw_hud = 1;
4442 			Script_system.SetHookObject("Self", Viewer_obj);
4443 			if(Script_system.IsOverride(Script_hudhook) || Script_system.IsConditionOverride(CHA_HUDDRAW, Viewer_obj))
4444 				Scripting_didnt_draw_hud = 0;
4445 			Script_system.RemHookVar("Self");
4446 
4447 			if(Scripting_didnt_draw_hud) {
4448 				game_render_hud(cid);
4449 			}
4450 			HUD_reset_clip();
4451 
4452 			if( (Game_detail_flags & DETAIL_FLAG_HUD) && (!(Game_mode & GM_MULTIPLAYER) || ((Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_OBSERVER))) ) {
4453 				anim_render_all(0, flFrametime);
4454 			}
4455 
4456 			Script_system.SetHookObject("Self", Viewer_obj);
4457 			if (!hud_disabled_except_messages() && !(Viewer_mode & (VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY)))
4458 			{
4459 				Script_system.RunBytecode(Script_hudhook);
4460 				Script_system.RunCondition(CHA_HUDDRAW, '\0', NULL, Viewer_obj);
4461 			}
4462 			Script_system.RemHookVar("Self");
4463 
4464 			// check to see if we should display the death died popup
4465 			if(Game_mode & GM_DEAD_BLEW_UP){
4466 				if(Game_mode & GM_MULTIPLAYER){
4467 					// catch the situation where we're supposed to be warping out on this transition
4468 					if(Net_player->flags & NETINFO_FLAG_WARPING_OUT){
4469 						multi_handle_sudden_mission_end();
4470 						send_debrief_event();
4471 					} else if((Player_died_popup_wait != -1) && (timestamp_elapsed(Player_died_popup_wait))){
4472 						Player_died_popup_wait = -1;
4473 						popupdead_start();
4474 					}
4475 				} else {
4476 					if((Player_died_popup_wait != -1) && (timestamp_elapsed(Player_died_popup_wait))){
4477 						Player_died_popup_wait = -1;
4478 						popupdead_start();
4479 					}
4480 				}
4481 			}
4482 
4483 			// Goober5000 - check if we should red-alert
4484 			// (this is approximately where the red_alert_check_status() function tree began in the pre-HUD-overhaul code)
4485 			red_alert_maybe_move_to_next_mission();
4486 
4487 			DEBUG_GET_TIME( render3_time2 )
4488 			DEBUG_GET_TIME( render2_time1 )
4489 
4490 			gr_reset_clip();
4491 			game_get_framerate();
4492 			game_show_framerate();
4493 			game_show_eye_pos(cid);
4494 
4495 			game_show_time_left();
4496 
4497 			gr_reset_clip();
4498 			game_render_post_frame();
4499 
4500 			game_tst_frame();
4501 
4502 			DEBUG_GET_TIME( render2_time2 )
4503 
4504 			// maybe render and process the dead popup
4505 			game_maybe_do_dead_popup(flFrametime);
4506 
4507 			// start timing frame
4508 			timing_frame_stop();
4509 			// timing_display(30, 10);
4510 
4511 			// If a regular popup is active, don't flip (popup code flips)
4512 			if( !popup_running_state() ){
4513 				DEBUG_GET_TIME( flip_time1 )
4514 				PROFILE("Page Flip", game_flip_page_and_time_it());
4515 				DEBUG_GET_TIME( flip_time2 )
4516 			}
4517 
4518 #ifndef NDEBUG
4519 			game_maybe_dump_frame();			// used to dump pcx files for building trailers
4520 #endif
4521 		} else {
4522 			game_show_standalone_framerate();
4523 		}
4524 	}
4525 
4526 	game_do_training_checks();
4527 	asteroid_frame();
4528 
4529 	// process lightning (nebula only)
4530 	nebl_process();
4531 
4532 	profile_end("Main Frame");
4533 	profile_dump_output();
4534 
4535 	DEBUG_GET_TIME( total_time2 )
4536 
4537 #ifndef NDEBUG
4538 	// Got some timing numbers
4539 	Timing_total = f2fl( total_time2 - total_time1 ) * 1000.0f;
4540 	Timing_clear = f2fl( clear_time2 - clear_time1 ) * 1000.0f;
4541 	Timing_render2 = f2fl( render2_time2- render2_time1 ) * 1000.0f;
4542 	Timing_render3 = f2fl( render3_time2- render3_time1 ) * 1000.0f;
4543 	Timing_flip = f2fl( flip_time2 - flip_time1 ) * 1000.0f;
4544 #endif
4545 
4546 }
4547 
4548 #define	MAX_FRAMETIME	(F1_0/4)		// Frametime gets saturated at this.  Changed by MK on 11/1/97.
4549 												//	Some bug was causing Frametime to always get saturated at 2.0 seconds after the player
4550 												//	died.  This resulted in screwed up death sequences.
4551 
4552 fix Last_time = 0;						// The absolute time of game at end of last frame (beginning of this frame)
4553 fix Last_delta_time = 0;				// While game is paused, this keeps track of how much elapsed in the frame before paused.
4554 static int timer_paused=0;
4555 #if defined(TIMER_TEST) && !defined(NDEBUG)
4556 static int stop_count,start_count;
4557 static int time_stopped,time_started;
4558 #endif
4559 int saved_timestamp_ticker = -1;
4560 
game_reset_time()4561 void game_reset_time()
4562 {
4563 	if((Game_mode & GM_MULTIPLAYER) && (Netgame.game_state == NETGAME_STATE_SERVER_TRANSFER)){
4564 		return ;
4565 	}
4566 
4567 	//	Last_time = timer_get_fixed_seconds();
4568 	game_start_time();
4569 	timestamp_reset();
4570 	game_stop_time();
4571 }
4572 
game_stop_time()4573 void game_stop_time()
4574 {
4575 	if (timer_paused==0) {
4576 		fix time;
4577 		time = timer_get_fixed_seconds();
4578 		// Save how much time progressed so far in the frame so we can
4579 		// use it when we unpause.
4580 		Last_delta_time = time - Last_time;
4581 
4582 		//mprintf(("Last_time in game_stop_time = %7.3f\n", f2fl(Last_delta_time)));
4583 		if (Last_delta_time < 0) {
4584 			#if defined(TIMER_TEST) && !defined(NDEBUG)
4585 			Int3();		//get Matt!!!!
4586 			#endif
4587 			Last_delta_time = 0;
4588 		}
4589 		#if defined(TIMER_TEST) && !defined(NDEBUG)
4590 		time_stopped = time;
4591 		#endif
4592 
4593 		// Stop the timer_tick stuff...
4594 		// Normally, you should never access 'timestamp_ticker', consider this a low-level routine
4595 		saved_timestamp_ticker = timestamp_ticker;
4596 	}
4597 	timer_paused++;
4598 
4599 	#if defined(TIMER_TEST) && !defined(NDEBUG)
4600 	stop_count++;
4601 	#endif
4602 }
4603 
game_start_time()4604 void game_start_time()
4605 {
4606 	timer_paused--;
4607 	Assert(timer_paused >= 0);
4608 	if (timer_paused==0) {
4609 		fix time;
4610 		time = timer_get_fixed_seconds();
4611 		#if defined(TIMER_TEST) && !defined(NDEBUG)
4612 		if (Last_time < 0)
4613 			Int3();		//get Matt!!!!
4614 		}
4615 		#endif
4616 		// Take current time, and set it backwards to account for time
4617 		// that the frame already executed, so that timer_get_fixed_seconds() - Last_time
4618 		// will be correct when it goes to calculate the frametime next
4619 		// frame.
4620 		Last_time = time - Last_delta_time;
4621 		#if defined(TIMER_TEST) && !defined(NDEBUG)
4622 		time_started = time;
4623 		#endif
4624 
4625 		// Restore the timer_tick stuff...
4626 		// Normally, you should never access 'timestamp_ticker', consider this a low-level routine
4627 		Assert( saved_timestamp_ticker > -1 );		// Called out of order, get JAS
4628 		timestamp_ticker = saved_timestamp_ticker;
4629 		saved_timestamp_ticker = -1;
4630 	}
4631 
4632 	#if defined(TIMER_TEST) && !defined(NDEBUG)
4633 	start_count++;
4634 	#endif
4635 }
4636 
4637 void lock_time_compression(bool is_locked)
4638 {
4639 	Time_compression_locked = is_locked;
4640 }
4641 
4642 void change_time_compression(float multiplier)
4643 {
4644 	fix modified = fl2f( f2fl(Game_time_compression) * multiplier );
4645 
4646 	Desired_time_compression = Game_time_compression = modified;
4647 	Time_compression_change_rate = 0;
4648 }
4649 
4650 void set_time_compression(float multiplier, float change_time)
4651 {
4652 	if(change_time <= 0.0f)
4653 	{
4654 		Game_time_compression = Desired_time_compression = fl2f(multiplier);
4655 		Time_compression_change_rate = 0;
4656 		return;
4657 	}
4658 
4659 	Desired_time_compression = fl2f(multiplier);
4660 	Time_compression_change_rate = fl2f( f2fl(Desired_time_compression - Game_time_compression) / change_time );
4661 }
4662 
4663 void game_set_frametime(int state)
4664 {
4665 	fix thistime;
4666 	float frame_cap_diff;
4667 
4668 	thistime = timer_get_fixed_seconds();
4669 
4670 	if ( Last_time == 0 )
4671 		Frametime = F1_0 / 30;
4672 	else
4673 		Frametime = thistime - Last_time;
4674 
4675 //	Frametime = F1_0 / 30;
4676 
4677 #ifndef NDEBUG
4678 	fix	debug_frametime = Frametime;	//	Just used to display frametime.
4679 #endif
4680 
4681 	//	If player hasn't entered mission yet, make frame take 1/4 second.
4682 	if ((Pre_player_entry) && (state == GS_STATE_GAME_PLAY))
4683 		Frametime = F1_0/4;
4684 #ifndef NDEBUG
4685 	else if ((Debug_dump_frames) && (state == GS_STATE_GAME_PLAY)) {				// note link to above if!!!!!
4686 
4687 		fix frame_speed = F1_0 / Debug_dump_frames;
4688 
4689 		if (Frametime > frame_speed ){
4690 			nprintf(("warning","slow frame: %x\n",Frametime));
4691 		} else {
4692 			do {
4693 				thistime = timer_get_fixed_seconds();
4694 				Frametime = thistime - Last_time;
4695 			} while (Frametime < frame_speed );
4696 		}
4697 		Frametime = frame_speed;
4698 	}
4699 #endif
4700 
4701 	Assertion( Framerate_cap > 0, "Framerate cap %d is too low. Needs to be a positive, non-zero number", Framerate_cap );
4702 
4703 	// Cap the framerate so it doesn't get too high.
4704 	if (!Cmdline_NoFPSCap)
4705 	{
4706 		fix cap;
4707 
4708 		cap = F1_0/Framerate_cap;
4709 		if (Frametime < cap) {
4710 			thistime = cap - Frametime;
4711 //  			mprintf(("Sleeping for %6.3f seconds.\n", f2fl(thistime)));
4712 			Sleep( DWORD(f2fl(thistime) * 1000.0f) );
4713 			Frametime = cap;
4714 			thistime = timer_get_fixed_seconds();
4715 		}
4716 	}
4717 
4718 	if((Game_mode & GM_STANDALONE_SERVER) &&
4719 		(f2fl(Frametime) < ((float)1.0/(float)Multi_options_g.std_framecap))){
4720 
4721 		frame_cap_diff = ((float)1.0/(float)Multi_options_g.std_framecap) - f2fl(Frametime);
4722 		Sleep((DWORD)(frame_cap_diff*1000));
4723 
4724 		thistime += fl2f((frame_cap_diff));
4725 
4726 		Frametime = thistime - Last_time;
4727    }
4728 
4729 	// If framerate is too low, cap it.
4730 	if (Frametime > MAX_FRAMETIME)	{
4731 #ifndef NDEBUG
4732 		mprintf(("Frame %2i too long!!: frametime = %.3f (%.3f)\n", Framecount, f2fl(Frametime), f2fl(debug_frametime)));
4733 #endif
4734 		Frametime = MAX_FRAMETIME;
4735 	}
4736 
4737 	flRealframetime = f2fl(Frametime);
4738 
4739 	//Handle changes in time compression
4740 	if(Game_time_compression != Desired_time_compression)
4741 	{
4742 		bool ascending = Desired_time_compression > Game_time_compression;
4743 		if(Time_compression_change_rate)
4744 			Game_time_compression += fixmul(Time_compression_change_rate, Frametime);
4745 		if((ascending && Game_time_compression > Desired_time_compression)
4746 			|| (!ascending && Game_time_compression < Desired_time_compression))
4747 			Game_time_compression = Desired_time_compression;
4748 	}
4749 
4750 	Frametime = fixmul(Frametime, Game_time_compression);
4751 
4752     if (Frametime <= 0)
4753     {
4754         // If the Frametime is zero or below due to Game_time_compression, set
4755         // the Frametime to 1 (1/65536 of a second).
4756         Frametime = 1;
4757     }
4758 
4759 	Last_time = thistime;
4760 	//mprintf(("Frame %i, Last_time = %7.3f\n", Framecount, f2fl(Last_time)));
4761 	Last_frame_timestamp = timestamp();
4762 
4763 	flFrametime = f2fl(Frametime);
4764 	timestamp_inc(flFrametime);
4765 
4766 	// wrap overall frametime if needed
4767 	if ( FrametimeOverall > (INT_MAX - F1_0) )
4768 		FrametimeOverall = 0;
4769 
4770 	FrametimeOverall += Frametime;
4771 
4772 /*	if ((Framecount > 0) && (Framecount < 10)) {
4773 		mprintf(("Frame %2i: frametime = %.3f (%.3f)\n", Framecount, f2fl(Frametime), f2fl(debug_frametime)));
4774 	}
4775 */
4776 }
4777 
4778 fix game_get_overall_frametime()
4779 {
4780 	return FrametimeOverall;
4781 }
4782 
4783 // This is called from game_do_frame(), and from navmap_do_frame()
4784 void game_update_missiontime()
4785 {
4786 	// TODO JAS: Put in if and move this into game_set_frametime,
4787 	// fix navmap to call game_stop/start_time
4788 	//if ( !timer_paused )
4789 		Missiontime += Frametime;
4790 }
4791 
4792 void game_do_frame()
4793 {
4794 	game_set_frametime(GS_STATE_GAME_PLAY);
4795 	game_update_missiontime();
4796 
4797 //	if (Player_ship->flags & SF_DYING)
4798 //		flFrametime /= 15.0;
4799 
4800 	if (Game_mode & GM_STANDALONE_SERVER) {
4801 		std_multi_set_standalone_missiontime(f2fl(Missiontime));
4802 	}
4803 
4804 	if ( game_single_step && (last_single_step == game_single_step) ) {
4805 		os_set_title( NOX("SINGLE STEP MODE (Pause exits, any other key steps)") );
4806 		while( key_checkch() == 0 )
4807 			os_sleep(10);
4808 		os_set_title( XSTR( "FreeSpace", 171) );
4809   		Last_time = timer_get_fixed_seconds();
4810 	}
4811 
4812 	last_single_step = game_single_step;
4813 
4814 	if ((gameseq_get_state() == GS_STATE_GAME_PLAY) && Use_mouse_to_fly){
4815 		Keep_mouse_centered = 1;  // force mouse to center of our window (so we don't hit movement limits)
4816 	}
4817 	game_frame();
4818 
4819 	Keep_mouse_centered = 0;
4820 	monitor_update();			// Update monitor variables
4821 }
4822 
4823 void multi_maybe_do_frame()
4824 {
4825 	if ( (Game_mode & GM_MULTIPLAYER) && (Game_mode & GM_IN_MISSION) && !Multi_pause_status){
4826 		game_do_frame();
4827 	}
4828 }
4829 
4830 int Joymouse_button_status = 0;
4831 
4832 // Flush all input devices
4833 void game_flush()
4834 {
4835 	key_flush();
4836 	mouse_flush();
4837 	joy_flush();
4838 	snazzy_flush();
4839 
4840 	Joymouse_button_status = 0;
4841 
4842 	//mprintf(("Game flush!\n" ));
4843 }
4844 
4845 // function for multiplayer only which calls game_do_state_common() when running the
4846 // debug console
4847 void game_do_dc_networking()
4848 {
4849 	Assert( Game_mode & GM_MULTIPLAYER );
4850 
4851 	game_do_state_common( gameseq_get_state() );
4852 }
4853 
4854 // Call this whenever in a loop, or when you need to check for a keystroke.
4855 int game_check_key()
4856 {
4857 	int k;
4858 
4859 	k = game_poll();
4860 
4861 	// convert keypad enter to normal enter
4862 	if ((k & KEY_MASK) == KEY_PADENTER)
4863 		k = (k & ~KEY_MASK) | KEY_ENTER;
4864 
4865 	return k;
4866 }
4867 
4868 // same as game_check_key(), except this is used while actually in the game.  Since there
4869 // generally are differences between game control keys and general UI keys, makes sense to
4870 // have seperate functions for each case.  If you are not checking a game control while in a
4871 // mission, you should probably be using game_check_key() instead.
4872 int game_poll()
4873 {
4874 	int k, state;
4875 
4876 	if (!Cmdline_no_unfocus_pause)
4877 	{
4878 		if (!os_foreground()) {
4879 			game_stop_time();
4880 			os_sleep(1);
4881 			game_start_time();
4882 
4883 			// If we're in a single player game, pause it.
4884 			if (!(Game_mode & GM_MULTIPLAYER)){
4885 				if ((gameseq_get_state() == GS_STATE_GAME_PLAY) && (!popup_active()) && (!popupdead_is_active()))	{
4886 					game_process_pause_key();
4887 				}
4888 			}
4889 		}
4890 	}
4891 
4892 	k = key_inkey();
4893 
4894 	// Move the mouse cursor with the joystick.
4895 	if (os_foreground() && (!Mouse_hidden) && (Use_joy_mouse) )	{
4896 		// Move the mouse cursor with the joystick
4897 		int mx, my, dx, dy;
4898 		int jx, jy, jz, jr;
4899 
4900 		joy_get_pos( &jx, &jy, &jz, &jr );
4901 
4902 		dx = fl2i(f2fl(jx)*flFrametime*500.0f);
4903 		dy = fl2i(f2fl(jy)*flFrametime*500.0f);
4904 
4905 		if ( dx || dy )	{
4906 			mouse_get_real_pos( &mx, &my );
4907 			mouse_set_pos( mx+dx, my+dy );
4908 		}
4909 
4910 		int j, m;
4911 		j = joy_down(0);
4912 		m = mouse_down(MOUSE_LEFT_BUTTON);
4913 
4914 		if ( j != Joymouse_button_status )	{
4915 			//mprintf(( "Joy went from %d to %d, mouse is %d\n", Joymouse_button_status, j, m ));
4916 			Joymouse_button_status = j;
4917 			if ( j && (!m) )	{
4918 				mouse_mark_button( MOUSE_LEFT_BUTTON, 1 );
4919 			} else if ( (!j) && (m) )	{
4920 				mouse_mark_button( MOUSE_LEFT_BUTTON, 0 );
4921 			}
4922 		}
4923 	}
4924 
4925 	// if we should be ignoring keys because of some multiplayer situations
4926 	if((Game_mode & GM_MULTIPLAYER) && multi_ignore_controls(k)){
4927 		return 0;
4928 	}
4929 
4930 	state = gameseq_get_state();
4931 
4932 	// If a popup is running, don't process all the Fn keys
4933 	if( popup_active()) {
4934 		if (state != GS_STATE_DEATH_BLEW_UP) {
4935 			return k;
4936 		}
4937 	}
4938 
4939 //	if ( k ) nprintf(( "General", "Key = %x\n", k ));
4940 
4941 	switch (k) {
4942 		case KEY_DEBUGGED + KEY_BACKSP:
4943 			if(!(Game_mode & GM_MULTIPLAYER))
4944 			{
4945 				gameseq_post_event(GS_EVENT_LAB);
4946 				k = 0;
4947 			}
4948 			break;
4949 
4950 		case KEY_F1:
4951 			launch_context_help();
4952 			k = 0;
4953 			break;
4954 
4955 		case KEY_F2:
4956 //			if (state != GS_STATE_INITIAL_PLAYER_SELECT) {
4957 
4958 			// don't allow f2 while warping out in multiplayer
4959 			if((Game_mode & GM_MULTIPLAYER) && (Net_player != NULL) && (Net_player->flags & NETINFO_FLAG_WARPING_OUT)){
4960 				break;
4961 			}
4962 
4963 			switch (state) {
4964 				case GS_STATE_INITIAL_PLAYER_SELECT:
4965 				case GS_STATE_OPTIONS_MENU:
4966 				case GS_STATE_HUD_CONFIG:
4967 				case GS_STATE_CONTROL_CONFIG:
4968 				case GS_STATE_DEATH_DIED:
4969 //				case GS_STATE_DEATH_BLEW_UP:	// 	DEATH_BLEW_UP might be okay but do not comment out DEATH_DIED as otherwise no clean up is performed on the dead ship
4970 				case GS_STATE_VIEW_MEDALS:
4971 					break;
4972 
4973 				default:
4974 					gameseq_post_event(GS_EVENT_OPTIONS_MENU);
4975 					k = 0;
4976 					break;
4977 			}
4978 
4979 			break;
4980 
4981 			// hotkey selection screen -- only valid from briefing and beyond.
4982 		case KEY_F3:
4983 			if ( (state == GS_STATE_TEAM_SELECT) || (state == GS_STATE_BRIEFING) || (state == GS_STATE_SHIP_SELECT) || (state == GS_STATE_WEAPON_SELECT) || (state == GS_STATE_GAME_PLAY) || (state == GS_STATE_GAME_PAUSED) ) {
4984 				gameseq_post_event( GS_EVENT_HOTKEY_SCREEN );
4985 				k = 0;
4986 			}
4987 			break;
4988 
4989 		case KEY_DEBUGGED + KEY_F3:
4990 			gameseq_post_event( GS_EVENT_TOGGLE_FULLSCREEN );
4991 			break;
4992 
4993 		case KEY_F4:
4994 			if(Game_mode & GM_MULTIPLAYER){
4995 				if((state == GS_STATE_GAME_PLAY) || (state == GS_STATE_MULTI_PAUSED)){
4996 					gameseq_post_event( GS_EVENT_MISSION_LOG_SCROLLBACK );
4997 					k = 0;
4998 				}
4999 			} else {
5000 				if ((state == GS_STATE_GAME_PLAY) || (state == GS_STATE_DEATH_DIED) || (state == GS_STATE_DEATH_BLEW_UP) || (state == GS_STATE_GAME_PAUSED) ) {
5001 					gameseq_post_event( GS_EVENT_MISSION_LOG_SCROLLBACK );
5002 					k = 0;
5003 				}
5004 			}
5005 			break;
5006 
5007 		case KEY_ESC | KEY_SHIFTED:
5008 			// make sure to quit properly out of multiplayer
5009 			if(Game_mode & GM_MULTIPLAYER){
5010 				multi_quit_game(PROMPT_NONE);
5011 			}
5012 
5013 			gameseq_post_event( GS_EVENT_QUIT_GAME );
5014 			k = 0;
5015 
5016 			break;
5017 
5018 		case KEY_DEBUGGED + KEY_P:
5019 			break;
5020 
5021 		case KEY_PRINT_SCRN:
5022 			{
5023 				static int counter = os_config_read_uint(NULL, "ScreenshotNum", 0);
5024 				char tmp_name[MAX_FILENAME_LEN];
5025 
5026 				game_stop_time();
5027 
5028 				// we could probably go with .3 here for 1,000 shots but people really need to clean out
5029 				// their directories better than that so it's 100 for now.
5030 				sprintf( tmp_name, NOX("screen%.4i"), counter );
5031 				counter++;
5032 
5033 				// we've got two character precision so we can only have 100 shots at a time, reset if needed
5034 				//Now we have four digit precision :) -WMC
5035 				if (counter > 9999)
5036 				{
5037 					//This should pop up a dialogue or something ingame.
5038 					Warning(LOCATION, "Screenshot count has reached max of 9999. Resetting to 0.");
5039 					counter = 0;
5040 				}
5041 
5042 				mprintf(( "Dumping screen to '%s'\n", tmp_name ));
5043 				gr_print_screen(tmp_name);
5044 
5045 				game_start_time();
5046 				os_config_write_uint(NULL, "ScreenshotNum", counter);
5047 			}
5048 
5049 			k = 0;
5050 			break;
5051 
5052 		case KEY_SHIFTED | KEY_ENTER: {
5053 
5054 #if !defined(NDEBUG)
5055 
5056 			if ( Game_mode & GM_NORMAL ){
5057 				game_stop_time();
5058 			}
5059 
5060 			// if we're in multiplayer mode, do some special networking
5061 			if(Game_mode & GM_MULTIPLAYER){
5062 				debug_console(game_do_dc_networking);
5063 			} else {
5064 				debug_console();
5065 			}
5066 
5067 			game_flush();
5068 
5069 			if ( Game_mode & GM_NORMAL )
5070 				game_start_time();
5071 
5072 #endif
5073 
5074 			break;
5075 		}
5076 	}
5077 
5078 	return k;
5079 }
5080 
5081 void os_close()
5082 {
5083 	gameseq_post_event(GS_EVENT_QUIT_GAME);
5084 }
5085 
5086 // All code to process events.   This is the only place
5087 // that you should change the state of the game.
5088 void game_process_event( int current_state, int event )
5089 {
5090 	mprintf(("Got event %s (%d) in state %s (%d)\n", GS_event_text[event], event, GS_state_text[current_state], current_state));
5091 
5092 	switch (event) {
5093 		case GS_EVENT_SIMULATOR_ROOM:
5094 			gameseq_set_state(GS_STATE_SIMULATOR_ROOM);
5095 			break;
5096 
5097 		case GS_EVENT_MAIN_MENU:
5098 			gameseq_set_state(GS_STATE_MAIN_MENU);
5099 			break;
5100 
5101 		case GS_EVENT_OPTIONS_MENU:
5102 			gameseq_push_state( GS_STATE_OPTIONS_MENU );
5103 			break;
5104 
5105 		case GS_EVENT_BARRACKS_MENU:
5106 			gameseq_set_state(GS_STATE_BARRACKS_MENU);
5107 			break;
5108 
5109 		case GS_EVENT_TECH_MENU:
5110 			gameseq_set_state(GS_STATE_TECH_MENU);
5111 			break;
5112 		case GS_EVENT_LAB:
5113 			gameseq_push_state(GS_STATE_LAB);
5114 			break;
5115 		case GS_EVENT_TRAINING_MENU:
5116 			gameseq_set_state(GS_STATE_TRAINING_MENU);
5117 			break;
5118 
5119 		case GS_EVENT_START_GAME:
5120 			Select_default_ship = 0;
5121 			gameseq_set_state(GS_STATE_START_GAME);
5122 			break;
5123 
5124 		case GS_EVENT_START_GAME_QUICK:
5125 			Select_default_ship = 1;
5126 			gameseq_post_event(GS_EVENT_ENTER_GAME);
5127 			break;
5128 
5129 		case GS_EVENT_CMD_BRIEF:
5130 			gameseq_set_state(GS_STATE_CMD_BRIEF);
5131 			break;
5132 
5133 		case GS_EVENT_RED_ALERT:
5134 			gameseq_set_state(GS_STATE_RED_ALERT);
5135 			break;
5136 
5137 		case GS_EVENT_START_BRIEFING:
5138 			gameseq_set_state(GS_STATE_BRIEFING);
5139 			break;
5140 
5141 		case GS_EVENT_DEBRIEF:
5142 			// did we end the campaign in the main freespace 2 single player campaign?
5143 			// (specifically, did we successfully jump out when the supernova was in progress
5144 			// and the campaign was ending?)
5145 			if (Campaign_ending_via_supernova && (Game_mode & GM_CAMPAIGN_MODE)/* && !stricmp(Campaign.filename, "freespace2")*/) {
5146 				gameseq_post_event(GS_EVENT_END_CAMPAIGN);
5147 			} else {
5148 				gameseq_set_state(GS_STATE_DEBRIEF);
5149 			}
5150 			break;
5151 
5152 		case GS_EVENT_SHIP_SELECTION:
5153 			gameseq_set_state( GS_STATE_SHIP_SELECT );
5154 			break;
5155 
5156 		case GS_EVENT_WEAPON_SELECTION:
5157 			gameseq_set_state( GS_STATE_WEAPON_SELECT );
5158 			break;
5159 
5160 		case GS_EVENT_ENTER_GAME:
5161 			if (Game_mode & GM_MULTIPLAYER) {
5162 				// if we're respawning, make sure we change the view mode so that the hud shows up
5163 				if (current_state == GS_STATE_DEATH_BLEW_UP) {
5164 					Viewer_mode = 0;
5165 				}
5166 
5167 				gameseq_set_state(GS_STATE_GAME_PLAY);
5168 			} else {
5169 				gameseq_set_state(GS_STATE_GAME_PLAY, 1);
5170 			}
5171 
5172 			// clear multiplayer button info
5173 			extern button_info Multi_ship_status_bi;
5174 			memset(&Multi_ship_status_bi, 0, sizeof(button_info));
5175 
5176 			// Make hv.Player available in "On Gameplay Start" hook -zookeeper
5177 			if(Player_obj)
5178 				Script_system.SetHookObject("Player", Player_obj);
5179 
5180 			Script_system.RunCondition(CHA_GAMEPLAYSTART);
5181 
5182 			if (Player_obj)
5183 				Script_system.RemHookVar("Player");
5184 
5185 			Start_time = f2fl(timer_get_approx_seconds());
5186 			//Framecount = 0;
5187 			mprintf(("Entering game at time = %7.3f\n", Start_time));
5188 			break;
5189 
5190 		case GS_EVENT_END_GAME:
5191 			if ( (current_state == GS_STATE_GAME_PLAY) || (current_state == GS_STATE_DEATH_DIED) ||
5192 				(current_state == GS_STATE_DEATH_BLEW_UP) ||	(current_state == GS_STATE_DEBRIEF) || (current_state == GS_STATE_MULTI_DOGFIGHT_DEBRIEF)) {
5193 					gameseq_set_state(GS_STATE_MAIN_MENU);
5194 
5195 			} else
5196 				Int3();
5197 
5198 			break;
5199 
5200 		case GS_EVENT_QUIT_GAME:
5201 			main_hall_stop_music(true);
5202 			main_hall_stop_ambient();
5203 			gameseq_set_state(GS_STATE_QUIT_GAME);
5204 			break;
5205 
5206 		case GS_EVENT_GAMEPLAY_HELP:
5207 			gameseq_push_state( GS_STATE_GAMEPLAY_HELP );
5208 			break;
5209 
5210 		case GS_EVENT_PAUSE_GAME:
5211 			gameseq_push_state(GS_STATE_GAME_PAUSED);
5212 			break;
5213 
5214 		case GS_EVENT_DEBUG_PAUSE_GAME:
5215 			gameseq_push_state(GS_STATE_DEBUG_PAUSED);
5216 			break;
5217 
5218 		case GS_EVENT_TRAINING_PAUSE:
5219 			gameseq_push_state(GS_STATE_TRAINING_PAUSED);
5220 			break;
5221 
5222 		case GS_EVENT_PREVIOUS_STATE:
5223 			gameseq_pop_state();
5224 			break;
5225 
5226 		case GS_EVENT_LOAD_MISSION_MENU:
5227 			gameseq_set_state(GS_STATE_LOAD_MISSION_MENU);
5228 			break;
5229 
5230 		case GS_EVENT_MISSION_LOG_SCROLLBACK:
5231 			gameseq_push_state( GS_STATE_MISSION_LOG_SCROLLBACK );
5232 			break;
5233 
5234 		case GS_EVENT_HUD_CONFIG:
5235 			gameseq_push_state( GS_STATE_HUD_CONFIG );
5236 			break;
5237 
5238 		case GS_EVENT_CONTROL_CONFIG:
5239 			gameseq_push_state( GS_STATE_CONTROL_CONFIG );
5240 			break;
5241 
5242 		case GS_EVENT_DEATH_DIED:
5243 			gameseq_set_state( GS_STATE_DEATH_DIED );
5244 			break;
5245 
5246 		case GS_EVENT_DEATH_BLEW_UP:
5247 			if (  current_state == GS_STATE_DEATH_DIED )	{
5248 				gameseq_set_state( GS_STATE_DEATH_BLEW_UP );
5249 				event_music_player_death();
5250 			} else {
5251 				mprintf(( "Ignoring GS_EVENT_DEATH_BLEW_UP because we're in state %d\n", current_state ));
5252 			}
5253 			break;
5254 
5255 		case GS_EVENT_NEW_CAMPAIGN:
5256 			if (!mission_load_up_campaign()){
5257 				readyroom_continue_campaign();
5258 			}
5259 			break;
5260 
5261 		case GS_EVENT_CAMPAIGN_CHEAT:
5262 			if (!mission_load_up_campaign()){
5263 				readyroom_continue_campaign();
5264 			}
5265 			break;
5266 
5267 		case GS_EVENT_CAMPAIGN_ROOM:
5268 			gameseq_set_state(GS_STATE_CAMPAIGN_ROOM);
5269 			break;
5270 
5271 		case GS_EVENT_CREDITS:
5272 			gameseq_set_state( GS_STATE_CREDITS );
5273 			break;
5274 
5275 		case GS_EVENT_VIEW_MEDALS:
5276 			gameseq_push_state( GS_STATE_VIEW_MEDALS );
5277 			break;
5278 
5279 		case GS_EVENT_SHOW_GOALS:
5280 			gameseq_push_state( GS_STATE_SHOW_GOALS );	// use push_state() since we might get to this screen through a variety of states
5281 			break;
5282 
5283 		case GS_EVENT_HOTKEY_SCREEN:
5284 			gameseq_push_state( GS_STATE_HOTKEY_SCREEN );	// use push_state() since we might get to this screen through a variety of states
5285 			break;
5286 
5287 	// multiplayer stuff follow these comments
5288 		case GS_EVENT_PXO:
5289 			gameseq_set_state( GS_STATE_PXO );
5290 			break;
5291 
5292 		case GS_EVENT_PXO_HELP:
5293 			gameseq_set_state( GS_STATE_PXO_HELP );
5294 			break;
5295 
5296 		case GS_EVENT_MULTI_JOIN_GAME:
5297 			gameseq_set_state( GS_STATE_MULTI_JOIN_GAME );
5298 			break;
5299 
5300 		case GS_EVENT_MULTI_HOST_SETUP:
5301 			gameseq_set_state( GS_STATE_MULTI_HOST_SETUP );
5302 			break;
5303 
5304 		case GS_EVENT_MULTI_CLIENT_SETUP:
5305 			gameseq_set_state( GS_STATE_MULTI_CLIENT_SETUP );
5306 			break;
5307 
5308   		case GS_EVENT_GOTO_VIEW_CUTSCENES_SCREEN:
5309 			gameseq_set_state(GS_STATE_VIEW_CUTSCENES);
5310 			break;
5311 
5312 		case GS_EVENT_MULTI_STD_WAIT:
5313 			gameseq_set_state( GS_STATE_MULTI_STD_WAIT );
5314 			break;
5315 
5316 		case GS_EVENT_STANDALONE_MAIN:
5317 			gameseq_set_state( GS_STATE_STANDALONE_MAIN );
5318 			break;
5319 
5320 		case GS_EVENT_MULTI_PAUSE:
5321 			gameseq_push_state( GS_STATE_MULTI_PAUSED );
5322 			break;
5323 
5324 		case GS_EVENT_INGAME_PRE_JOIN:
5325 			gameseq_set_state( GS_STATE_INGAME_PRE_JOIN );
5326 			break;
5327 
5328 		case GS_EVENT_EVENT_DEBUG:
5329 			gameseq_push_state(GS_STATE_EVENT_DEBUG);
5330 			break;
5331 
5332 		// Start a warpout where player automatically goes 70 no matter what
5333 		// and can't cancel out of it.
5334 		case GS_EVENT_PLAYER_WARPOUT_START_FORCED:
5335 			Warpout_forced = 1;							// If non-zero, bash the player to speed and go through effect
5336 
5337 			// Same code as in GS_EVENT_PLAYER_WARPOUT_START only ignores current mode
5338 			Player->saved_viewer_mode = Viewer_mode;
5339 			Player->control_mode = PCM_WARPOUT_STAGE1;
5340 			Warpout_sound = snd_play(&Snds[SND_PLAYER_WARP_OUT]);
5341 			Warpout_time = 0.0f;			// Start timer!
5342 			break;
5343 
5344 		case GS_EVENT_PLAYER_WARPOUT_START:
5345 			if ( Player->control_mode != PCM_NORMAL )	{
5346 				mprintf(( "Player isn't in normal mode; cannot warp out.\n" ));
5347 			} else {
5348 				Player->saved_viewer_mode = Viewer_mode;
5349 				Player->control_mode = PCM_WARPOUT_STAGE1;
5350 				Warpout_sound = snd_play(&Snds[SND_PLAYER_WARP_OUT]);
5351 				Warpout_time = 0.0f;			// Start timer!
5352 				Warpout_forced = 0;				// If non-zero, bash the player to speed and go through effect
5353 			}
5354 			break;
5355 
5356 		case GS_EVENT_PLAYER_WARPOUT_STOP:
5357 			if ( Player->control_mode != PCM_NORMAL )	{
5358 				if ( !Warpout_forced )	{		// cannot cancel forced warpout
5359 					Player->control_mode = PCM_NORMAL;
5360 					Viewer_mode = Player->saved_viewer_mode;
5361 					hud_subspace_notify_abort();
5362 					mprintf(( "Player put back to normal mode.\n" ));
5363 					if ( Warpout_sound > -1 )	{
5364 						snd_stop( Warpout_sound );
5365 						Warpout_sound = -1;
5366 					}
5367 				}
5368 			}
5369 			break;
5370 
5371 		case GS_EVENT_PLAYER_WARPOUT_DONE_STAGE1:		// player ship got up to speed
5372 			if ( Player->control_mode != PCM_WARPOUT_STAGE1 )	{
5373 				gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_STOP );
5374 				mprintf(( "Player put back to normal mode, because of invalid sequence in stage1.\n" ));
5375 			} else {
5376 				mprintf(( "Hit target speed.  Starting warp effect and moving to stage 2!\n" ));
5377 				shipfx_warpout_start( Player_obj );
5378 				Player->control_mode = PCM_WARPOUT_STAGE2;
5379 
5380 				if (!(The_mission.ai_profile->flags2 & AIPF2_NO_WARP_CAMERA)) {
5381 					Player->saved_viewer_mode = Viewer_mode;
5382 					Viewer_mode |= VM_WARP_CHASE;
5383 					Warp_camera = warp_camera(Player_obj);
5384 				}
5385 			}
5386 			break;
5387 
5388 		case GS_EVENT_PLAYER_WARPOUT_DONE_STAGE2:		// player ship got into the warp effect
5389 			if ( Player->control_mode != PCM_WARPOUT_STAGE2 )	{
5390 				gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_STOP );
5391 				mprintf(( "Player put back to normal mode, because of invalid sequence in stage2.\n" ));
5392 			} else {
5393 				mprintf(( "Hit warp effect.  Moving to stage 3!\n" ));
5394 				Player->control_mode = PCM_WARPOUT_STAGE3;
5395 			}
5396 			break;
5397 
5398 		case GS_EVENT_PLAYER_WARPOUT_DONE:	// player ship got through the warp effect
5399 			mprintf(( "Player warped out.  Going to debriefing!\n" ));
5400 			Player->control_mode = PCM_NORMAL;
5401 			Viewer_mode = Player->saved_viewer_mode;
5402 			Warpout_sound = -1;
5403 
5404 			send_debrief_event();
5405 			break;
5406 
5407 		case GS_EVENT_STANDALONE_POSTGAME:
5408 			gameseq_set_state(GS_STATE_STANDALONE_POSTGAME);
5409 			break;
5410 
5411 		case GS_EVENT_INITIAL_PLAYER_SELECT:
5412 			gameseq_set_state(GS_STATE_INITIAL_PLAYER_SELECT);
5413 			break;
5414 
5415 		case GS_EVENT_GAME_INIT:
5416 			// see if the command line option has been set to use the last pilot, and act acoordingly
5417 			if( player_select_get_last_pilot() ) {
5418 				// always enter the main menu -- do the automatic network startup stuff elsewhere
5419 				// so that we still have valid checks for networking modes, etc.
5420 				gameseq_set_state(GS_STATE_MAIN_MENU);
5421 			} else {
5422 				gameseq_set_state(GS_STATE_INITIAL_PLAYER_SELECT);
5423 			}
5424 			break;
5425 
5426 		case GS_EVENT_MULTI_MISSION_SYNC:
5427 			gameseq_set_state(GS_STATE_MULTI_MISSION_SYNC);
5428 			break;
5429 
5430 		case GS_EVENT_MULTI_START_GAME:
5431 			gameseq_set_state(GS_STATE_MULTI_START_GAME);
5432 			break;
5433 
5434 		case GS_EVENT_MULTI_HOST_OPTIONS:
5435 			gameseq_set_state(GS_STATE_MULTI_HOST_OPTIONS);
5436 			break;
5437 
5438 		case GS_EVENT_MULTI_DOGFIGHT_DEBRIEF:
5439 			gameseq_set_state(GS_STATE_MULTI_DOGFIGHT_DEBRIEF);
5440 			break;
5441 
5442 		case GS_EVENT_TEAM_SELECT:
5443 			gameseq_set_state(GS_STATE_TEAM_SELECT);
5444 			break;
5445 
5446 		case GS_EVENT_END_CAMPAIGN:
5447 			gameseq_set_state(GS_STATE_END_OF_CAMPAIGN);
5448 			break;
5449 
5450 		case GS_EVENT_LOOP_BRIEF:
5451 			gameseq_set_state(GS_STATE_LOOP_BRIEF);
5452 			break;
5453 
5454 		case GS_EVENT_FICTION_VIEWER:
5455 			gameseq_set_state(GS_STATE_FICTION_VIEWER);
5456 			break;
5457 
5458 		case GS_EVENT_SCRIPTING:
5459 			gameseq_set_state(GS_STATE_SCRIPTING);
5460 			break;
5461 
5462 		default:
5463 			Int3();
5464 			break;
5465 	}
5466 }
5467 
5468 // Called when a state is being left.
5469 // The current state is still at old_state, but as soon as
5470 // this function leaves, then the current state will become
5471 // new state.     You should never try to change the state
5472 // in here... if you think you need to, you probably really
5473 // need to post an event, not change the state.
5474 void game_leave_state( int old_state, int new_state )
5475 {
5476 	int end_mission = 1;
5477 
5478 	switch (new_state) {
5479 		case GS_STATE_GAME_PAUSED:
5480 		case GS_STATE_DEBUG_PAUSED:
5481 		case GS_STATE_OPTIONS_MENU:
5482 		case GS_STATE_CONTROL_CONFIG:
5483 		case GS_STATE_MISSION_LOG_SCROLLBACK:
5484 		case GS_STATE_DEATH_DIED:
5485 		case GS_STATE_SHOW_GOALS:
5486 		case GS_STATE_HOTKEY_SCREEN:
5487 		case GS_STATE_MULTI_PAUSED:
5488 		case GS_STATE_TRAINING_PAUSED:
5489 		case GS_STATE_EVENT_DEBUG:
5490 		case GS_STATE_GAMEPLAY_HELP:
5491 		case GS_STATE_LAB:
5492 			end_mission = 0;  // these events shouldn't end a mission
5493 			break;
5494 	}
5495 
5496 	//WMC - Scripting override
5497 	/*
5498 	if(script_hook_valid(&GS_state_hooks[old_state]) && Script_system.IsOverride(GS_state_hooks[old_state])) {
5499 		return;
5500 	}
5501 	*/
5502 
5503 	if(Script_system.IsConditionOverride(CHA_ONSTATEEND)) {
5504 		Script_system.RunCondition(CHA_ONSTATEEND);
5505 		return;
5506 	}
5507 
5508 	//WMC - Clear scripting bitmaps
5509 	Script_system.UnloadImages();
5510 
5511 	switch (old_state) {
5512 		case GS_STATE_BRIEFING:
5513 			brief_stop_voices();
5514 			if ( (new_state != GS_STATE_OPTIONS_MENU) && (new_state != GS_STATE_WEAPON_SELECT)
5515 				  && (new_state != GS_STATE_SHIP_SELECT) && (new_state != GS_STATE_HOTKEY_SCREEN)
5516 				  && (new_state != GS_STATE_TEAM_SELECT) && (new_state != GS_STATE_MULTI_MISSION_SYNC)){
5517 				common_select_close();
5518 				if ( new_state == GS_STATE_MAIN_MENU ) {
5519 					freespace_stop_mission();
5520 				}
5521 			}
5522 
5523 			// COMMAND LINE OPTION
5524 			if (Cmdline_multi_stream_chat_to_file){
5525 				cfwrite_string(NOX("-------------------------------------------\n"),Multi_chat_stream);
5526 				cfclose(Multi_chat_stream);
5527 			}
5528 			break;
5529 
5530 		case GS_STATE_DEBRIEF:
5531 			if ( (new_state != GS_STATE_VIEW_MEDALS) && (new_state != GS_STATE_OPTIONS_MENU) ) {
5532 				debrief_close();
5533 				fsspeech_stop();
5534 			}
5535 			break;
5536 
5537 		case GS_STATE_MULTI_DOGFIGHT_DEBRIEF:
5538 			multi_df_debrief_close();
5539 			break;
5540 
5541 		case GS_STATE_LOAD_MISSION_MENU:
5542 			mission_load_menu_close();
5543 			break;
5544 
5545 		case GS_STATE_SIMULATOR_ROOM:
5546 			sim_room_close();
5547 			break;
5548 
5549 		case GS_STATE_CAMPAIGN_ROOM:
5550 			campaign_room_close();
5551 			break;
5552 
5553 		case GS_STATE_CMD_BRIEF:
5554 			if (new_state == GS_STATE_OPTIONS_MENU) {
5555 				cmd_brief_hold();
5556 
5557 			} else {
5558 				cmd_brief_close();
5559 				common_select_close();
5560 				if (new_state == GS_STATE_MAIN_MENU) {
5561 					freespace_stop_mission();
5562 				}
5563 			}
5564 			break;
5565 
5566 		case GS_STATE_RED_ALERT:
5567 			red_alert_close();
5568 			common_select_close();
5569 			if (new_state == GS_STATE_MAIN_MENU) {
5570 				freespace_stop_mission();
5571 			}
5572 			break;
5573 
5574 		case GS_STATE_SHIP_SELECT:
5575 			if ( new_state != GS_STATE_OPTIONS_MENU && new_state != GS_STATE_WEAPON_SELECT &&
5576 				  new_state != GS_STATE_HOTKEY_SCREEN &&
5577 				  new_state != GS_STATE_BRIEFING && new_state != GS_STATE_TEAM_SELECT  && (new_state != GS_STATE_MULTI_MISSION_SYNC)) {
5578 				common_select_close();
5579 				if ( new_state == GS_STATE_MAIN_MENU ) {
5580 					freespace_stop_mission();
5581 				}
5582 			}
5583 			break;
5584 
5585 		case GS_STATE_WEAPON_SELECT:
5586 			if ( new_state != GS_STATE_OPTIONS_MENU && new_state != GS_STATE_SHIP_SELECT &&
5587 				  new_state != GS_STATE_HOTKEY_SCREEN &&
5588 				  new_state != GS_STATE_BRIEFING && new_state != GS_STATE_TEAM_SELECT && (new_state != GS_STATE_MULTI_MISSION_SYNC)) {
5589 				common_select_close();
5590 				if ( new_state == GS_STATE_MAIN_MENU ) {
5591 					freespace_stop_mission();
5592 				}
5593 			}
5594 			break;
5595 
5596 		case GS_STATE_TEAM_SELECT:
5597 			if ( new_state != GS_STATE_OPTIONS_MENU && new_state != GS_STATE_SHIP_SELECT &&
5598 				  new_state != GS_STATE_HOTKEY_SCREEN &&
5599 				  new_state != GS_STATE_BRIEFING && new_state != GS_STATE_WEAPON_SELECT && (new_state != GS_STATE_MULTI_MISSION_SYNC)) {
5600 				common_select_close();
5601 				if ( new_state == GS_STATE_MAIN_MENU ) {
5602 					freespace_stop_mission();
5603 				}
5604 			}
5605 			break;
5606 
5607 		case GS_STATE_MAIN_MENU:
5608 			main_hall_close();
5609 			break;
5610 
5611 		case GS_STATE_OPTIONS_MENU:
5612 			//game_start_time();
5613 			if(new_state == GS_STATE_MULTI_JOIN_GAME){
5614 				multi_join_clear_game_list();
5615 			}
5616 			options_menu_close();
5617 			break;
5618 
5619 		case GS_STATE_BARRACKS_MENU:
5620 			if(new_state != GS_STATE_VIEW_MEDALS){
5621 				barracks_close();
5622 			}
5623 			break;
5624 
5625 		case GS_STATE_MISSION_LOG_SCROLLBACK:
5626 			hud_scrollback_close();
5627 			break;
5628 
5629 		case GS_STATE_TRAINING_MENU:
5630 			training_menu_close();
5631 			break;
5632 
5633 		case GS_STATE_GAME_PLAY:
5634 			if ( !(Game_mode & GM_STANDALONE_SERVER) ) {
5635 				player_save_target_and_weapon_link_prefs();
5636 				game_stop_looped_sounds();
5637 			}
5638 
5639 			sound_env_disable();
5640 			joy_ff_stop_effects();
5641 
5642 			// stop game time under certain conditions
5643 			if ( end_mission || (Game_mode & GM_NORMAL) || ((Game_mode & GM_MULTIPLAYER) && (new_state == GS_STATE_MULTI_PAUSED)) ){
5644 				game_stop_time();
5645 			}
5646 
5647 			if (end_mission) {
5648 				// when in multiplayer and going back to the main menu, send a leave game packet
5649 				// right away (before calling stop mission).  stop_mission was taking to long to
5650 				// close mission down and I want people to get notified ASAP.
5651 				if ( (Game_mode & GM_MULTIPLAYER) && (new_state == GS_STATE_MAIN_MENU) ){
5652 					multi_quit_game(PROMPT_NONE);
5653 				}
5654 				snd_aav_init();
5655 
5656 				freespace_stop_mission();
5657 			}
5658 			break;
5659 
5660 		case GS_STATE_TECH_MENU:
5661 			techroom_close();
5662 			break;
5663 
5664 		case GS_STATE_TRAINING_PAUSED:
5665 			Training_num_lines = 0;
5666 			// fall through to GS_STATE_GAME_PAUSED
5667 
5668 		case GS_STATE_GAME_PAUSED:
5669 			game_start_time();
5670 			if ( end_mission ) {
5671 				pause_close();
5672 			}
5673 			break;
5674 
5675 		case GS_STATE_DEBUG_PAUSED:
5676 			#ifndef NDEBUG
5677 				game_start_time();
5678 				pause_debug_close();
5679 			#endif
5680 			break;
5681 
5682 		case GS_STATE_HUD_CONFIG:
5683 			hud_config_close();
5684 			break;
5685 
5686 		case GS_STATE_PXO:
5687 			if (new_state != GS_STATE_PXO_HELP) {
5688 				multi_pxo_close();
5689 			}
5690 			break;
5691 
5692 		case GS_STATE_PXO_HELP:
5693 			multi_pxo_help_close();
5694 			break;
5695 
5696 		// join/start a game
5697 		case GS_STATE_MULTI_JOIN_GAME:
5698 			if(new_state != GS_STATE_OPTIONS_MENU){
5699 				multi_join_game_close();
5700 			}
5701 			break;
5702 
5703 		case GS_STATE_MULTI_HOST_SETUP:
5704 		case GS_STATE_MULTI_CLIENT_SETUP:
5705 			// if this is just the host going into the options screen, don't do anything
5706 			if((new_state == GS_STATE_MULTI_HOST_OPTIONS) || (new_state == GS_STATE_OPTIONS_MENU)){
5707 				break;
5708 			}
5709 
5710 			// close down the proper state
5711 			if(old_state == GS_STATE_MULTI_HOST_SETUP){
5712 				multi_create_game_close();
5713 			} else {
5714 				multi_game_client_setup_close();
5715 			}
5716 
5717 			// COMMAND LINE OPTION
5718 			if (Cmdline_multi_stream_chat_to_file){
5719 				if( (new_state != GS_STATE_TEAM_SELECT) && (Multi_chat_stream!=NULL) ) {
5720 					cfwrite_string(NOX("-------------------------------------------\n"),Multi_chat_stream);
5721 					cfclose(Multi_chat_stream);
5722 				}
5723 			}
5724 			break;
5725 
5726 		case GS_STATE_CONTROL_CONFIG:
5727 			control_config_close();
5728 			break;
5729 
5730 		case GS_STATE_DEATH_DIED:
5731 			Game_mode &= ~GM_DEAD_DIED;
5732 
5733 			if ( !(Game_mode & GM_MULTIPLAYER) ) {
5734 				if ( end_mission && (new_state == GS_STATE_DEBRIEF) ) {
5735 					freespace_stop_mission();
5736 				}
5737 			} else {
5738 				// early end while respawning or blowing up in a multiplayer game
5739 				if ( (new_state == GS_STATE_DEBRIEF) || (new_state == GS_STATE_MULTI_DOGFIGHT_DEBRIEF) ) {
5740 					game_stop_time();
5741 					freespace_stop_mission();
5742 				}
5743 			}
5744 			break;
5745 
5746 		case GS_STATE_DEATH_BLEW_UP:
5747 			Game_mode &= ~GM_DEAD_BLEW_UP;
5748 
5749 			// for single player, we might reload mission, etc.  For multiplayer, look at my new state
5750 			// to determine if I should do anything.
5751 			if ( !(Game_mode & GM_MULTIPLAYER) ) {
5752 				if ( end_mission ){
5753 					freespace_stop_mission();
5754 				}
5755 			} else {
5756 				// if we are not respawing as an observer or as a player, our new state will not
5757 				// be gameplay state.
5758 				if ( (new_state != GS_STATE_GAME_PLAY) && (new_state != GS_STATE_MULTI_PAUSED) ) {
5759 					game_stop_time();									// hasn't been called yet!!
5760 					freespace_stop_mission();
5761 				}
5762 			}
5763 			break;
5764 
5765 
5766 		case GS_STATE_CREDITS:
5767 			credits_close();
5768 			main_hall_start_music();
5769 			break;
5770 
5771 		case GS_STATE_VIEW_MEDALS:
5772 			medal_main_close();
5773 			break;
5774 
5775 		case GS_STATE_SHOW_GOALS:
5776 			mission_show_goals_close();
5777 			break;
5778 
5779 		case GS_STATE_HOTKEY_SCREEN:
5780 			if ( new_state != GS_STATE_OPTIONS_MENU ) {
5781 				mission_hotkey_close();
5782 			}
5783 			break;
5784 
5785 		case GS_STATE_MULTI_MISSION_SYNC:
5786 			common_select_close();
5787 
5788 			// if we're moving into the options menu we don't need to do anything else
5789 			if(new_state == GS_STATE_OPTIONS_MENU){
5790 				break;
5791 			}
5792 
5793 			Assert( Game_mode & GM_MULTIPLAYER );
5794 			multi_sync_close();
5795 			if ( new_state == GS_STATE_GAME_PLAY ){
5796 				// palette_restore_palette();
5797 
5798 				// change a couple of flags to indicate our state!!!
5799 				Net_player->state = NETPLAYER_STATE_IN_MISSION;
5800 				send_netplayer_update_packet();
5801 
5802 				// set the game mode
5803 				Game_mode |= GM_IN_MISSION;
5804 			}
5805 
5806 			main_hall_stop_music(true);
5807 			main_hall_stop_ambient();
5808 			break;
5809 
5810 		case GS_STATE_VIEW_CUTSCENES:
5811 			cutscenes_screen_close();
5812 			break;
5813 
5814 		case GS_STATE_MULTI_STD_WAIT:
5815 			multi_standalone_wait_close();
5816 	  		break;
5817 
5818 		case GS_STATE_STANDALONE_MAIN:
5819 			standalone_main_close();
5820 			if(new_state == GS_STATE_MULTI_STD_WAIT){
5821 				init_multiplayer_stats();
5822 			}
5823 			break;
5824 
5825 		case GS_STATE_STANDALONE_POSTGAME:
5826 			multi_standalone_postgame_close();
5827 			break;
5828 
5829 		case GS_STATE_MULTI_PAUSED:
5830 			multi_pause_close(end_mission);
5831 			break;
5832 
5833 		case GS_STATE_INGAME_PRE_JOIN:
5834 			multi_ingame_select_close();
5835 			break;
5836 
5837 		case GS_STATE_INITIAL_PLAYER_SELECT:
5838 			player_select_close();
5839 			break;
5840 
5841 		case GS_STATE_MULTI_START_GAME:
5842 			multi_start_game_close();
5843 			break;
5844 
5845 		case GS_STATE_MULTI_HOST_OPTIONS:
5846 			multi_host_options_close();
5847 			break;
5848 
5849 		case GS_STATE_END_OF_CAMPAIGN:
5850 			mission_campaign_end_close();
5851 			break;
5852 
5853 		case GS_STATE_LOOP_BRIEF:
5854 			loop_brief_close();
5855 			break;
5856 
5857 		case GS_STATE_FICTION_VIEWER:
5858 			fiction_viewer_close();
5859 			break;
5860 
5861 		case GS_STATE_LAB:
5862 			lab_close();
5863 			break;
5864 
5865 		case GS_STATE_SCRIPTING:
5866 			scripting_state_close();
5867 			break;
5868 	}
5869 
5870 	//WMC - Now run scripting stuff
5871 	Script_system.RunCondition(CHA_ONSTATEEND);
5872 }
5873 
5874 // variable used for automatic netgame starting/joining
5875 int Main_hall_netgame_started = 0;
5876 
5877 // Called when a state is being entered.
5878 // The current state is set to the state we're entering at
5879 // this point, and old_state is set to the state we're coming
5880 // from.    You should never try to change the state
5881 // in here... if you think you need to, you probably really
5882 // need to post an event, not change the state.
5883 
5884 void game_enter_state( int old_state, int new_state )
5885 {
5886 	//WMC - Scripting override
5887 	/*
5888 	if(script_hook_valid(&GS_state_hooks[new_state]) && Script_system.IsOverride(GS_state_hooks[new_state])) {
5889 		return;
5890 	}
5891 	*/
5892 	if(Script_system.IsConditionOverride(CHA_ONSTATESTART)) {
5893 		Script_system.RunCondition(CHA_ONSTATESTART);
5894 		return;
5895 	}
5896 
5897 	switch (new_state) {
5898 		case GS_STATE_MAIN_MENU:
5899 			// in multiplayer mode, be sure that we are not doing networking anymore.
5900 			if ( Game_mode & GM_MULTIPLAYER ) {
5901 				Assert( Net_player != NULL );
5902 				Net_player->flags &= ~NETINFO_FLAG_DO_NETWORKING;
5903 			}
5904 
5905 			// remove any multiplayer flags from the game mode
5906 			Game_mode &= ~(GM_MULTIPLAYER);
5907 
5908 			// set the game_mode based on the type of player
5909 			Assert( Player != NULL );
5910 
5911 			if ( Player->flags & PLAYER_FLAGS_IS_MULTI ){
5912 				Game_mode = GM_MULTIPLAYER;
5913 			} else {
5914 				Game_mode = GM_NORMAL;
5915 			}
5916 
5917 			// determine which ship this guy is currently based on
5918 			mission_load_up_campaign(Player);
5919 
5920 			// if we're coming from the end of a campaign, we want to load the first mainhall of the campaign
5921 			// otherwise load the mainhall for the mission the player's up to
5922 			if (Campaign.next_mission == -1) {
5923 				main_hall_init(Campaign.missions[0].main_hall);
5924 			} else {
5925 				main_hall_init(Campaign.missions[Campaign.next_mission].main_hall);
5926 			}
5927 
5928 			//if ( (Cmdline_start_netgame || (Cmdline_connect_addr != NULL)) && !Main_hall_netgame_started ) {
5929 			//	Main_hall_netgame_started = 1;
5930 			//	main_hall_do_multi_ready();
5931 			//} DTP commented out to keep original source
5932 			if ( (Cmdline_start_netgame || (Cmdline_connect_addr != NULL)) && (!Main_hall_netgame_started) /*&& (Game_mode == GM_MULTIPLAYER)*/) { // DTP added "&& (game_mode == GM_multiplayer)" so that ppl don't get thrown into Multiplayer with a Singleplayer Pilot.
5933 				Main_hall_netgame_started = 1;
5934 				main_hall_do_multi_ready();
5935 			}
5936 
5937 			if(Cmdline_start_mission) {
5938 				strcpy_s(Game_current_mission_filename, Cmdline_start_mission);
5939 				mprintf(( "Straight to mission '%s'\n", Game_current_mission_filename ));
5940  				gameseq_post_event(GS_EVENT_START_GAME);
5941 				// This stops the mission from loading again when you go back to the hall
5942 				Cmdline_start_mission = NULL;
5943 			}
5944 			break;
5945 
5946 		case GS_STATE_START_GAME:
5947 			main_hall_stop_music(true);
5948 			main_hall_stop_ambient();
5949 
5950 			if (Game_mode & GM_NORMAL) {
5951 				// this should put us into a new state on failure!
5952 				if (!game_start_mission())
5953 					break;
5954 			}
5955 
5956 			// maybe play a movie before the mission
5957 			mission_campaign_maybe_play_movie(CAMPAIGN_MOVIE_PRE_MISSION);
5958 
5959 			// determine where to go next
5960 			if (mission_has_fiction()) {
5961 				gameseq_post_event(GS_EVENT_FICTION_VIEWER);
5962 			} else if (mission_has_cmd_brief()) {
5963 				gameseq_post_event(GS_EVENT_CMD_BRIEF);
5964 			} else if (red_alert_mission()) {
5965 				gameseq_post_event(GS_EVENT_RED_ALERT);
5966 			} else {
5967 				gameseq_post_event(GS_EVENT_START_BRIEFING);
5968 			}
5969 			break;
5970 
5971 		case GS_STATE_FICTION_VIEWER:
5972 			common_maybe_play_cutscene(MOVIE_PRE_FICTION);
5973 			fiction_viewer_init();
5974 			break;
5975 
5976 		case GS_STATE_CMD_BRIEF: {
5977 			if (old_state == GS_STATE_OPTIONS_MENU) {
5978 				cmd_brief_unhold();
5979 			} else {
5980 				common_maybe_play_cutscene(MOVIE_PRE_CMD_BRIEF);
5981 				int team_num = 0;  // team number used as index for which cmd brief to use.
5982 				cmd_brief_init(team_num);
5983 			}
5984 			break;
5985 		}
5986 
5987 		case GS_STATE_RED_ALERT:
5988 			common_maybe_play_cutscene(MOVIE_PRE_BRIEF);
5989 			red_alert_init();
5990 			break;
5991 
5992 		case GS_STATE_BRIEFING:
5993 			common_maybe_play_cutscene(MOVIE_PRE_BRIEF);
5994 			brief_init();
5995 			break;
5996 
5997 		case GS_STATE_DEBRIEF:
5998 			game_stop_looped_sounds();
5999 			mission_goal_fail_incomplete();				// fail all incomplete goals before entering debriefing
6000 			if ( (old_state != GS_STATE_VIEW_MEDALS) && (old_state != GS_STATE_OPTIONS_MENU) ){
6001 				common_maybe_play_cutscene(MOVIE_PRE_DEBRIEF);
6002 				debrief_init();
6003 			}
6004 			break;
6005 
6006 		case GS_STATE_MULTI_DOGFIGHT_DEBRIEF:
6007 			multi_df_debrief_init();
6008 			break;
6009 
6010 		case GS_STATE_LOAD_MISSION_MENU:
6011 			mission_load_menu_init();
6012 			break;
6013 
6014 		case GS_STATE_SIMULATOR_ROOM:
6015 			sim_room_init();
6016 			break;
6017 
6018 		case GS_STATE_CAMPAIGN_ROOM:
6019 			campaign_room_init();
6020 			break;
6021 
6022 		case GS_STATE_SHIP_SELECT:
6023 			ship_select_init();
6024 			break;
6025 
6026 		case GS_STATE_WEAPON_SELECT:
6027 			weapon_select_init();
6028 			break;
6029 
6030 		case GS_STATE_TEAM_SELECT:
6031 			multi_ts_init();
6032 			break;
6033 
6034 		case GS_STATE_GAME_PAUSED:
6035 			game_stop_time();
6036 			pause_init();
6037 			break;
6038 
6039 		case GS_STATE_DEBUG_PAUSED:
6040 	//		game_stop_time();
6041 	//		os_set_title("FreeSpace - PAUSED");
6042 	//		break;
6043 	//
6044 		case GS_STATE_TRAINING_PAUSED:
6045 			#ifndef NDEBUG
6046 				game_stop_time();
6047 				pause_debug_init();
6048 			#endif
6049 			break;
6050 
6051 		case GS_STATE_OPTIONS_MENU:
6052 			//game_stop_time();
6053 			options_menu_init();
6054 			break;
6055 
6056 		case GS_STATE_GAME_PLAY:
6057 
6058 			// maybe play a cutscene
6059 			if((old_state == GS_STATE_BRIEFING) || (old_state == GS_STATE_SHIP_SELECT) ||
6060 			   (old_state == GS_STATE_WEAPON_SELECT) || (old_state == GS_STATE_RED_ALERT) ) {
6061 				common_maybe_play_cutscene(MOVIE_PRE_GAME);
6062 			}
6063 			// reset time compression to default level so it's right at the beginning of a mission - taylor
6064 			if(old_state != GS_STATE_GAME_PAUSED)
6065 			{
6066 				//Game_time_compression = F1_0;
6067 			}
6068 
6069 			/* game could be comming from a restart (rather than first start)
6070 			so make sure that we zero the hud gauge overrides (Sexp_hud_*)
6071 			\sa sexp_hud_display_gauge*/
6072 			if ( (old_state == GS_STATE_GAME_PLAY)
6073 				|| (old_state == GS_STATE_BRIEFING)
6074 				|| (old_state == GS_STATE_DEBRIEF)
6075 				|| (old_state == GS_STATE_SHIP_SELECT)
6076 				|| (old_state == GS_STATE_WEAPON_SELECT)
6077 				|| (old_state == GS_STATE_RED_ALERT) )
6078 			{
6079 				Sexp_hud_display_warpout = 0;
6080 			}
6081 
6082 			// Goober5000 - people may not have realized that pausing causes this state to be re-entered
6083 			if ((old_state != GS_STATE_GAME_PAUSED) && (old_state != GS_STATE_MULTI_PAUSED) && (old_state != GS_STATE_MAIN_MENU))
6084 			{
6085 				if ( !Is_standalone )
6086 					radar_mission_init();
6087 
6088 				//Set the current hud
6089 				set_current_hud();
6090 
6091 				if ( !Is_standalone ) {
6092 					ship_init_cockpit_displays(Player_ship);
6093 				}
6094 			}
6095 
6096 			// coming from the gameplay state or the main menu, we might need to load the mission
6097 			if ( (Game_mode & GM_NORMAL) && ((old_state == GS_STATE_MAIN_MENU) || (old_state == GS_STATE_GAME_PLAY) || (old_state == GS_STATE_DEATH_BLEW_UP)) ) {
6098 				if ( !game_start_mission() )		// this should put us into a new state.
6099 					// Failed!!!
6100 					break;
6101 			}
6102 
6103 			// if we are coming from the briefing, ship select, weapons loadout, or main menu (in the
6104 			// case of quick start), then do bitmap loads, etc  Don't do any of the loading stuff
6105 			// if we are in multiplayer -- this stuff is all handled in the multi-wait section
6106 			if ( !(Game_mode & GM_MULTIPLAYER) && ((old_state == GS_STATE_BRIEFING) || (old_state == GS_STATE_SHIP_SELECT) ||
6107 				(old_state == GS_STATE_WEAPON_SELECT) || (old_state == GS_STATE_MAIN_MENU) || (old_state == GS_STATE_MULTI_STD_WAIT) || (old_state == GS_STATE_SIMULATOR_ROOM)) ) {
6108 					// JAS: Used to do all paging here.
6109 
6110 					#ifndef NDEBUG
6111 					//XSTR:OFF
6112 						HUD_printf("Skill level is set to ** %s **", Skill_level_names(Game_skill_level));
6113 					//XSTR:ON
6114 					#endif
6115 
6116 					main_hall_stop_music(true);
6117 					main_hall_stop_ambient();
6118 					event_music_first_pattern();	// start the first pattern
6119 			}
6120 
6121 			// special code that restores player ship selection and weapons loadout when doing a quick start
6122 			if ( !(Game_mode & GM_MULTIPLAYER) && ((old_state == GS_STATE_MAIN_MENU) || (old_state == GS_STATE_DEATH_BLEW_UP)  || (old_state == GS_STATE_GAME_PLAY)) ) {
6123 				if ( !stricmp(Player_loadout.filename, Game_current_mission_filename) ) {
6124 					wss_direct_restore_loadout();
6125 				}
6126 			}
6127 
6128 			// single-player, quick-start after just died... we need to set weapon linking and kick off the event music
6129 			if (!(Game_mode & GM_MULTIPLAYER) && (old_state == GS_STATE_DEATH_BLEW_UP) ) {
6130 				event_music_first_pattern();	// start the first pattern
6131 			}
6132 
6133 			if ( !(Game_mode & GM_STANDALONE_SERVER) && ((old_state != GS_STATE_GAME_PAUSED) && (old_state != GS_STATE_MULTI_PAUSED)) ) {
6134 				event_music_first_pattern();	// start the first pattern
6135 			}
6136 			player_restore_target_and_weapon_link_prefs();
6137 
6138 			Game_mode |= GM_IN_MISSION;
6139 
6140 #ifndef NDEBUG
6141 			// required to truely make mouse deltas zeroed in debug mouse code
6142 void mouse_force_pos(int x, int y);
6143 			mouse_force_pos(gr_screen.max_w / 2, gr_screen.max_h / 2);
6144 #endif
6145 
6146 			game_flush();
6147 
6148 			// only start time if in single player, or coming from multi wait state
6149 			if (
6150 					(
6151 						(Game_mode & GM_NORMAL) &&
6152 						(old_state != GS_STATE_VIEW_CUTSCENES)
6153 					) || (
6154 						(Game_mode & GM_MULTIPLAYER) && (
6155 							(old_state == GS_STATE_MULTI_PAUSED) ||
6156 							(old_state == GS_STATE_MULTI_MISSION_SYNC)
6157 						)
6158 					)
6159 				)
6160 					game_start_time();
6161 
6162 			// when coming from the multi paused state, reset the timestamps
6163 			if ( (Game_mode & GM_MULTIPLAYER) && (old_state == GS_STATE_MULTI_PAUSED) ){
6164 				multi_reset_timestamps();
6165 			}
6166 
6167 			if ((Game_mode & GM_MULTIPLAYER) && (old_state != GS_STATE_DEATH_BLEW_UP) ) {
6168 				// initialize all object update details
6169 				multi_oo_gameplay_init();
6170 			}
6171 
6172 			// under certain circumstances, the server should reset the object update rate limiting stuff
6173 			if( MULTIPLAYER_MASTER && ((old_state == GS_STATE_MULTI_PAUSED) || (old_state == GS_STATE_MULTI_MISSION_SYNC)) ){
6174 
6175 				// reinitialize the rate limiting system for all clients
6176 				multi_oo_rate_init_all();
6177 			}
6178 
6179 			// multiplayer clients should always re-initialize their control info rate limiting system
6180 			if(MULTIPLAYER_CLIENT){
6181 				multi_oo_rate_init_all();
6182 			}
6183 
6184 			// reset ping times
6185 			if(Game_mode & GM_MULTIPLAYER){
6186 				multi_ping_reset_players();
6187 			}
6188 
6189 			Game_subspace_effect = 0;
6190 			if (The_mission.flags & MISSION_FLAG_SUBSPACE) {
6191 				Game_subspace_effect = 1;
6192 				if( !(Game_mode & GM_STANDALONE_SERVER) ){
6193 					game_start_subspace_ambient_sound();
6194 				}
6195 			}
6196 
6197 			sound_env_set(&Game_sound_env);
6198 			joy_ff_mission_init(Ship_info[Player_ship->ship_info_index].rotation_time);
6199 
6200 			// clear multiplayer button info			i
6201 			extern button_info Multi_ship_status_bi;
6202 			memset(&Multi_ship_status_bi, 0, sizeof(button_info));
6203 			break;
6204 
6205 		case GS_STATE_HUD_CONFIG:
6206 			hud_config_init();
6207 			break;
6208 
6209 		case GS_STATE_PXO:
6210 			if (old_state != GS_STATE_PXO_HELP) {
6211 				multi_pxo_init( 0 );
6212 			}
6213 			break;
6214 
6215 		case GS_STATE_PXO_HELP:
6216 			multi_pxo_help_init();
6217 			break;
6218 
6219 		case GS_STATE_MULTI_JOIN_GAME:
6220 			multi_join_clear_game_list();
6221 
6222 			if (old_state != GS_STATE_OPTIONS_MENU) {
6223 				multi_join_game_init();
6224 			}
6225 
6226 			break;
6227 
6228 		case GS_STATE_MULTI_HOST_SETUP:
6229 			// don't reinitialize if we're coming back from the host options screen
6230 			if ((old_state != GS_STATE_MULTI_HOST_OPTIONS) && (old_state != GS_STATE_OPTIONS_MENU)) {
6231 				multi_create_game_init();
6232 			}
6233 
6234 			break;
6235 
6236 		case GS_STATE_MULTI_CLIENT_SETUP:
6237 			if (old_state != GS_STATE_OPTIONS_MENU) {
6238 				multi_game_client_setup_init();
6239 			}
6240 
6241 			break;
6242 
6243 		case GS_STATE_CONTROL_CONFIG:
6244 			control_config_init();
6245 			break;
6246 
6247 		case GS_STATE_TECH_MENU:
6248 			techroom_init();
6249 			break;
6250 
6251 		case GS_STATE_BARRACKS_MENU:
6252 			if(old_state != GS_STATE_VIEW_MEDALS){
6253 				barracks_init();
6254 			}
6255 			break;
6256 
6257 		case GS_STATE_MISSION_LOG_SCROLLBACK:
6258 			hud_scrollback_init();
6259 			break;
6260 
6261 		case GS_STATE_DEATH_DIED:
6262  			Player_died_time = timestamp(10);
6263 
6264 			if(!(Game_mode & GM_MULTIPLAYER)){
6265 				player_show_death_message();
6266 			}
6267 			Game_mode |= GM_DEAD_DIED;
6268 			break;
6269 
6270 		case GS_STATE_DEATH_BLEW_UP:
6271 			if ( !popupdead_is_active() ) {
6272 				Player_ai->target_objnum = -1;
6273 			}
6274 
6275 			// stop any local EMP effect
6276 			emp_stop_local();
6277 
6278 			Players[Player_num].flags &= ~PLAYER_FLAGS_AUTO_TARGETING;	//	Prevent immediate switch to a hostile ship.
6279 			Game_mode |= GM_DEAD_BLEW_UP;
6280 			Show_viewing_from_self = 0;
6281 
6282 			// timestamp how long we should wait before displaying the died popup
6283 			if ( !popupdead_is_active() ) {
6284 				Player_died_popup_wait = timestamp(PLAYER_DIED_POPUP_WAIT);
6285 			}
6286 			break;
6287 
6288 		case GS_STATE_GAMEPLAY_HELP:
6289 			gameplay_help_init();
6290 			break;
6291 
6292 		case GS_STATE_CREDITS:
6293 			main_hall_stop_music(true);
6294 			main_hall_stop_ambient();
6295 			credits_init();
6296 			break;
6297 
6298 		case GS_STATE_VIEW_MEDALS:
6299 			medal_main_init(Player);
6300 			break;
6301 
6302 		case GS_STATE_SHOW_GOALS:
6303 			mission_show_goals_init();
6304 			break;
6305 
6306 		case GS_STATE_HOTKEY_SCREEN:
6307 			mission_hotkey_init();
6308 			break;
6309 
6310 		case GS_STATE_MULTI_MISSION_SYNC:
6311 			// if we're coming from the options screen, don't do any
6312 			if(old_state == GS_STATE_OPTIONS_MENU){
6313 				break;
6314 			}
6315 
6316 			switch(Multi_sync_mode){
6317 			case MULTI_SYNC_PRE_BRIEFING:
6318 				// if moving from game forming to the team select state
6319 				multi_sync_init();
6320 				break;
6321 			case MULTI_SYNC_POST_BRIEFING:
6322 				// if moving from briefing into the mission itself
6323 				multi_sync_init();
6324 
6325 				// tell everyone that we're now loading data
6326 				Net_player->state = NETPLAYER_STATE_DATA_LOAD;
6327 				send_netplayer_update_packet();
6328 
6329 				// JAS: Used to do all paging here!!!!
6330 
6331 				Net_player->state = NETPLAYER_STATE_WAITING;
6332 				send_netplayer_update_packet();
6333 				break;
6334 			case MULTI_SYNC_INGAME:
6335 				multi_sync_init();
6336 				break;
6337 			}
6338 			break;
6339 
6340 		case GS_STATE_VIEW_CUTSCENES:
6341 			cutscenes_screen_init();
6342 			break;
6343 
6344 		case GS_STATE_MULTI_STD_WAIT:
6345 			multi_standalone_wait_init();
6346 			break;
6347 
6348 		case GS_STATE_STANDALONE_MAIN:
6349 			// don't initialize if we're coming from one of these 2 states unless there are no
6350 			// players left (reset situation)
6351 			if((old_state != GS_STATE_STANDALONE_POSTGAME) || multi_endgame_ending()){
6352 				standalone_main_init();
6353 			}
6354 			break;
6355 
6356 		case GS_STATE_STANDALONE_POSTGAME:
6357 			multi_standalone_postgame_init();
6358 			break;
6359 
6360 		case GS_STATE_MULTI_PAUSED:
6361 			multi_pause_init();
6362 			break;
6363 
6364 		case GS_STATE_INGAME_PRE_JOIN:
6365 			multi_ingame_select_init();
6366 			break;
6367 
6368 		case GS_STATE_INITIAL_PLAYER_SELECT:
6369 			player_select_init();
6370 			break;
6371 
6372 		case GS_STATE_MULTI_START_GAME:
6373 			multi_start_game_init();
6374 			break;
6375 
6376 		case GS_STATE_MULTI_HOST_OPTIONS:
6377 			multi_host_options_init();
6378 			break;
6379 
6380 		case GS_STATE_END_OF_CAMPAIGN:
6381 			mission_campaign_end_init();
6382 			break;
6383 
6384 		case GS_STATE_LOOP_BRIEF:
6385 			loop_brief_init();
6386 			break;
6387 
6388 		case GS_STATE_LAB:
6389 			lab_init();
6390 			break;
6391 
6392 		case GS_STATE_SCRIPTING:
6393 			scripting_state_init();
6394 			break;
6395 	} // end switch
6396 
6397 	//WMC - now do user scripting stuff
6398 	Script_system.RunCondition(CHA_ONSTATESTART);
6399 }
6400 
6401 // do stuff that may need to be done regardless of state
6402 void game_do_state_common(int state,int no_networking)
6403 {
6404 	game_maybe_draw_mouse(flFrametime);		// determine if to draw the mouse this frame
6405 	snd_do_frame();								// update sound system
6406 	event_music_do_frame();						// music needs to play across many states
6407 
6408 	multi_log_process();
6409 
6410 	if (no_networking) {
6411 		return;
6412 	}
6413 
6414 	// maybe do a multiplayer frame based on game mode and state type
6415 	if (Game_mode & GM_MULTIPLAYER) {
6416 		switch (state) {
6417 			case GS_STATE_OPTIONS_MENU:
6418 			case GS_STATE_GAMEPLAY_HELP:
6419 			case GS_STATE_HOTKEY_SCREEN:
6420 			case GS_STATE_HUD_CONFIG:
6421 			case GS_STATE_CONTROL_CONFIG:
6422 			case GS_STATE_MISSION_LOG_SCROLLBACK:
6423 			case GS_STATE_SHOW_GOALS:
6424 			case GS_STATE_VIEW_CUTSCENES:
6425 			case GS_STATE_EVENT_DEBUG:
6426 				multi_maybe_do_frame();
6427 				break;
6428 		}
6429 
6430 		game_do_networking();
6431 	}
6432 }
6433 
6434 // Called once a frame.
6435 // You should never try to change the state
6436 // in here... if you think you need to, you probably really
6437 // need to post an event, not change the state.
6438 int Game_do_state_should_skip = 0;
6439 void game_do_state(int state)
6440 {
6441 	// always lets the do_state_common() function determine if the state should be skipped
6442 	Game_do_state_should_skip = 0;
6443 
6444 	// legal to set the should skip state anywhere in this function
6445 	game_do_state_common(state);	// do stuff that may need to be done regardless of state
6446 
6447 	if(Game_do_state_should_skip){
6448 		return;
6449 	}
6450 
6451 	if(Script_system.IsConditionOverride(CHA_ONFRAME)) {
6452 		game_set_frametime(state);
6453 		gr_clear();
6454 		gr_flip();	//Does state hook automagically
6455 		return;
6456 	}
6457 	/*
6458 	if(Script_system.IsOverride(GS_state_hooks[state]))
6459 	{
6460 		game_set_frametime(state);
6461 		gr_clear();
6462 		Script_system.RunBytecode(GS_state_hooks[state]);
6463 		gr_flip();
6464 		return;
6465 	}
6466 	*/
6467 
6468 	switch (state) {
6469 		case GS_STATE_MAIN_MENU:
6470 			game_set_frametime(GS_STATE_MAIN_MENU);
6471 			main_hall_do(flFrametime);
6472 			break;
6473 
6474 		case GS_STATE_OPTIONS_MENU:
6475 			game_set_frametime(GS_STATE_OPTIONS_MENU);
6476 			options_menu_do_frame(flFrametime);
6477 			break;
6478 
6479 		case GS_STATE_BARRACKS_MENU:
6480 			game_set_frametime(GS_STATE_BARRACKS_MENU);
6481 			barracks_do_frame(flFrametime);
6482 			break;
6483 
6484 		case GS_STATE_TRAINING_MENU:
6485 			game_set_frametime(GS_STATE_TRAINING_MENU);
6486 			training_menu_do_frame(flFrametime);
6487 			break;
6488 
6489 		case GS_STATE_TECH_MENU:
6490 			game_set_frametime(GS_STATE_TECH_MENU);
6491 			techroom_do_frame(flFrametime);
6492 			break;
6493 
6494 		case GS_STATE_GAMEPLAY_HELP:
6495 			game_set_frametime(GS_STATE_GAMEPLAY_HELP);
6496 			gameplay_help_do_frame(flFrametime);
6497 			break;
6498 
6499 		case GS_STATE_GAME_PLAY:	// do stuff that should be done during gameplay
6500 			game_do_frame();
6501 			break;
6502 
6503 		case GS_STATE_GAME_PAUSED:
6504 
6505 			if(pause_get_type() == PAUSE_TYPE_VIEWER)	{
6506 
6507 				read_player_controls( Player_obj, flFrametime);
6508 			//	game_process_keys();
6509 				game_frame(true);
6510 			}
6511 
6512 			pause_do();
6513 			break;
6514 
6515 		case GS_STATE_DEBUG_PAUSED:
6516 			#ifndef NDEBUG
6517 				game_set_frametime(GS_STATE_DEBUG_PAUSED);
6518 				pause_debug_do();
6519 			#endif
6520 			break;
6521 
6522 		case GS_STATE_TRAINING_PAUSED:
6523 			game_training_pause_do();
6524 			break;
6525 
6526 		case GS_STATE_LOAD_MISSION_MENU:
6527 			game_set_frametime(GS_STATE_LOAD_MISSION_MENU);
6528 			mission_load_menu_do();
6529 			break;
6530 
6531 		case GS_STATE_BRIEFING:
6532 			game_set_frametime(GS_STATE_BRIEFING);
6533 			brief_do_frame(flFrametime);
6534 			break;
6535 
6536 		case GS_STATE_DEBRIEF:
6537 			game_set_frametime(GS_STATE_DEBRIEF);
6538 			debrief_do_frame(flFrametime);
6539 			break;
6540 
6541 		case GS_STATE_MULTI_DOGFIGHT_DEBRIEF:
6542 			game_set_frametime(GS_STATE_MULTI_DOGFIGHT_DEBRIEF);
6543 			multi_df_debrief_do();
6544 			break;
6545 
6546 		case GS_STATE_SHIP_SELECT:
6547 			game_set_frametime(GS_STATE_SHIP_SELECT);
6548 			ship_select_do(flFrametime);
6549 			break;
6550 
6551 		case GS_STATE_WEAPON_SELECT:
6552 			game_set_frametime(GS_STATE_WEAPON_SELECT);
6553 			weapon_select_do(flFrametime);
6554 			break;
6555 
6556 		case GS_STATE_MISSION_LOG_SCROLLBACK:
6557 			game_set_frametime(GS_STATE_MISSION_LOG_SCROLLBACK);
6558 			hud_scrollback_do_frame(flFrametime);
6559 			break;
6560 
6561 		case GS_STATE_HUD_CONFIG:
6562 			game_set_frametime(GS_STATE_HUD_CONFIG);
6563 			hud_config_do_frame(flFrametime);
6564 			break;
6565 
6566 		case GS_STATE_PXO:
6567 			game_set_frametime(GS_STATE_PXO);
6568 			multi_pxo_do();
6569 			break;
6570 
6571 		case GS_STATE_PXO_HELP:
6572 			game_set_frametime(GS_STATE_PXO_HELP);
6573 			multi_pxo_help_do();
6574 			break;
6575 
6576 		case GS_STATE_MULTI_JOIN_GAME:
6577 			game_set_frametime(GS_STATE_MULTI_JOIN_GAME);
6578 			multi_join_game_do_frame();
6579 			break;
6580 
6581 		case GS_STATE_MULTI_HOST_SETUP:
6582 			game_set_frametime(GS_STATE_MULTI_HOST_SETUP);
6583 			multi_create_game_do();
6584 			break;
6585 
6586 		case GS_STATE_MULTI_CLIENT_SETUP:
6587 			game_set_frametime(GS_STATE_MULTI_CLIENT_SETUP);
6588 			multi_game_client_setup_do_frame();
6589 			break;
6590 
6591 		case GS_STATE_CONTROL_CONFIG:
6592 			game_set_frametime(GS_STATE_CONTROL_CONFIG);
6593 			control_config_do_frame(flFrametime);
6594 			break;
6595 
6596 		case GS_STATE_DEATH_DIED:
6597 			game_do_frame();
6598 			break;
6599 
6600 		case GS_STATE_DEATH_BLEW_UP:
6601 			game_do_frame();
6602 			break;
6603 
6604 		case GS_STATE_SIMULATOR_ROOM:
6605 			game_set_frametime(GS_STATE_SIMULATOR_ROOM);
6606 			sim_room_do_frame(flFrametime);
6607 			break;
6608 
6609 		case GS_STATE_CAMPAIGN_ROOM:
6610 			game_set_frametime(GS_STATE_CAMPAIGN_ROOM);
6611 			campaign_room_do_frame(flFrametime);
6612 			break;
6613 
6614 		case GS_STATE_RED_ALERT:
6615 			game_set_frametime(GS_STATE_RED_ALERT);
6616 			red_alert_do_frame(flFrametime);
6617 			break;
6618 
6619 		case GS_STATE_CMD_BRIEF:
6620 			game_set_frametime(GS_STATE_CMD_BRIEF);
6621 			cmd_brief_do_frame(flFrametime);
6622 			break;
6623 
6624 		case GS_STATE_CREDITS:
6625 			game_set_frametime(GS_STATE_CREDITS);
6626 			credits_do_frame(flFrametime);
6627 			break;
6628 
6629 		case GS_STATE_VIEW_MEDALS:
6630 			game_set_frametime(GS_STATE_VIEW_MEDALS);
6631 			medal_main_do();
6632 			break;
6633 
6634 		case GS_STATE_SHOW_GOALS:
6635 			game_set_frametime(GS_STATE_SHOW_GOALS);
6636 			mission_show_goals_do_frame(flFrametime);
6637 			break;
6638 
6639 		case GS_STATE_HOTKEY_SCREEN:
6640 			game_set_frametime(GS_STATE_HOTKEY_SCREEN);
6641 			mission_hotkey_do_frame(flFrametime);
6642 			break;
6643 
6644 		case GS_STATE_VIEW_CUTSCENES:
6645 			game_set_frametime(GS_STATE_VIEW_CUTSCENES);
6646 			cutscenes_screen_do_frame();
6647 			break;
6648 
6649 		case GS_STATE_MULTI_STD_WAIT:
6650 			game_set_frametime(GS_STATE_STANDALONE_MAIN);
6651 			multi_standalone_wait_do();
6652 			break;
6653 
6654 		case GS_STATE_STANDALONE_MAIN:
6655 			game_set_frametime(GS_STATE_STANDALONE_MAIN);
6656 			standalone_main_do();
6657 			break;
6658 
6659 		case GS_STATE_MULTI_PAUSED:
6660 			game_set_frametime(GS_STATE_MULTI_PAUSED);
6661 			multi_pause_do();
6662 			break;
6663 
6664 		case GS_STATE_TEAM_SELECT:
6665 			game_set_frametime(GS_STATE_TEAM_SELECT);
6666 			multi_ts_do();
6667 			break;
6668 
6669 		case GS_STATE_INGAME_PRE_JOIN:
6670 			game_set_frametime(GS_STATE_INGAME_PRE_JOIN);
6671 			multi_ingame_select_do();
6672 			break;
6673 
6674 		case GS_STATE_EVENT_DEBUG:
6675 	#ifndef NDEBUG
6676 			game_set_frametime(GS_STATE_EVENT_DEBUG);
6677 			game_show_event_debug(flFrametime);
6678 	#endif
6679 			break;
6680 
6681 		case GS_STATE_STANDALONE_POSTGAME:
6682 			game_set_frametime(GS_STATE_STANDALONE_POSTGAME);
6683 			multi_standalone_postgame_do();
6684 			break;
6685 
6686 		case GS_STATE_INITIAL_PLAYER_SELECT:
6687 			game_set_frametime(GS_STATE_INITIAL_PLAYER_SELECT);
6688 			player_select_do();
6689 			break;
6690 
6691 		case GS_STATE_MULTI_MISSION_SYNC:
6692 			game_set_frametime(GS_STATE_MULTI_MISSION_SYNC);
6693 			multi_sync_do();
6694 			break;
6695 
6696 		case GS_STATE_MULTI_START_GAME:
6697 			game_set_frametime(GS_STATE_MULTI_START_GAME);
6698 			multi_start_game_do();
6699 			break;
6700 
6701 		case GS_STATE_MULTI_HOST_OPTIONS:
6702 			game_set_frametime(GS_STATE_MULTI_HOST_OPTIONS);
6703 			multi_host_options_do();
6704 			break;
6705 
6706 		case GS_STATE_END_OF_CAMPAIGN:
6707 			mission_campaign_end_do();
6708 			break;
6709 
6710 		case GS_STATE_LOOP_BRIEF:
6711 			game_set_frametime(GS_STATE_LOOP_BRIEF);
6712 			loop_brief_do(flFrametime);
6713 			break;
6714 
6715 		case GS_STATE_FICTION_VIEWER:
6716 			game_set_frametime(GS_STATE_FICTION_VIEWER);
6717 			fiction_viewer_do_frame(flFrametime);
6718 			break;
6719 
6720 		case GS_STATE_LAB:
6721 			game_set_frametime(GS_STATE_LAB);
6722 			lab_do_frame(flFrametime);
6723 			break;
6724 
6725 		case GS_STATE_SCRIPTING:
6726 			game_set_frametime(GS_STATE_SCRIPTING);
6727 			scripting_state_do_frame(flFrametime);
6728 			break;
6729 
6730    } // end switch(gs_current_state)
6731 }
6732 
6733 
6734 #ifdef _WIN32
6735 // return 0 if there is enough RAM to run FreeSpace, otherwise return -1
6736 int game_do_ram_check(uint ram_in_bytes)
6737 {
6738 	if ( ram_in_bytes < 30*1024*1024 )	{
6739 		int allowed_to_run = 1;
6740 		if ( ram_in_bytes < 25*1024*1024 ) {
6741 			allowed_to_run = 0;
6742 		}
6743 
6744 		char tmp[1024];
6745 		uint FreeSpace_total_ram_MB;
6746 		FreeSpace_total_ram_MB = (uint)(ram_in_bytes/(1024*1024));
6747 
6748 		if ( allowed_to_run ) {
6749 
6750 			sprintf( tmp, XSTR( "FreeSpace has detected that you only have %dMB of free memory.\n\nFreeSpace requires at least 32MB of memory to run.  If you think you have more than %dMB of physical memory, ensure that you aren't running SmartDrive (SMARTDRV.EXE).  Any memory allocated to SmartDrive is not usable by applications\n\nPress 'OK' to continue running with less than the minimum required memory\n", 193), FreeSpace_total_ram_MB, FreeSpace_total_ram_MB);
6751 
6752 			int msgbox_rval;
6753 			msgbox_rval = MessageBox( NULL, tmp, XSTR( "Not Enough RAM", 194), MB_OKCANCEL );
6754 			if ( msgbox_rval == IDCANCEL ) {
6755 				return -1;
6756 			}
6757 
6758 		} else {
6759 			sprintf( tmp, XSTR( "FreeSpace has detected that you only have %dMB of free memory.\n\nFreeSpace requires at least 32MB of memory to run.  If you think you have more than %dMB of physical memory, ensure that you aren't running SmartDrive (SMARTDRV.EXE).  Any memory allocated to SmartDrive is not usable by applications\n", 195), FreeSpace_total_ram_MB, FreeSpace_total_ram_MB);
6760 			MessageBox( NULL, tmp, XSTR( "Not Enough RAM", 194), MB_OK );
6761 			return -1;
6762 		}
6763 	}
6764 
6765 	return 0;
6766 }
6767 
6768 
6769 #if 0 // no updater for fs2
6770 // Check if there is a freespace.exe in the /update directory (relative to where fs.exe is installed).
6771 // If so, copy it over and remove the update directory.
6772 void game_maybe_update_launcher(char *exe_dir)
6773 {
6774 	char src_filename[MAX_PATH];
6775 	char dest_filename[MAX_PATH];
6776 
6777 	strcpy_s(src_filename, exe_dir);
6778 	strcat_s(src_filename, NOX("\\update\\freespace.exe"));
6779 
6780 	strcpy_s(dest_filename, exe_dir);
6781 	strcat_s(dest_filename, NOX("\\freespace.exe"));
6782 
6783 	// see if src_filename exists
6784 	FILE *fp;
6785 	fp = fopen(src_filename, "rb");
6786 	if ( !fp ) {
6787 		return;
6788 	}
6789 	fclose(fp);
6790 
6791 	SetFileAttributes(dest_filename, FILE_ATTRIBUTE_NORMAL);
6792 
6793 	// copy updated freespace.exe to freespace exe dir
6794 	if ( CopyFile(src_filename, dest_filename, 0) == 0 ) {
6795 		MessageBox( NULL, XSTR("Unable to copy freespace.exe from update directory to installed directory.  You should copy freespace.exe from the update directory (located in your FreeSpace install directory) to your install directory", 988), NULL, MB_OK|MB_TASKMODAL|MB_SETFOREGROUND );
6796 		return;
6797 	}
6798 
6799 	// delete the file in the update directory
6800 	DeleteFile(src_filename);
6801 
6802 	// safe to assume directory is empty, since freespace.exe should only be the file ever in the update dir
6803 	char update_dir[MAX_PATH];
6804 	strcpy_s(update_dir, exe_dir);
6805 	strcat_s(update_dir, NOX("\\update"));
6806 	RemoveDirectory(update_dir);
6807 }
6808 #endif // no launcher
6809 
6810 #endif // ifdef WIN32
6811 
6812 void game_spew_pof_info_sub(int model_num, polymodel *pm, int sm, CFILE *out, int *out_total, int *out_destroyed_total)
6813 {
6814 	int i;
6815 	int sub_total = 0;
6816 	int sub_total_destroyed = 0;
6817 	int total = 0;
6818 	char str[255] = "";
6819 
6820 	// get the total for all his children
6821 	for (i=pm->submodel[sm].first_child; i >= 0; i = pm->submodel[i].next_sibling )	{
6822 		game_spew_pof_info_sub(model_num, pm, i, out, &sub_total, &sub_total_destroyed);
6823 	}
6824 
6825 	// find the # of faces for this _individual_ object
6826 	total = submodel_get_num_polys(model_num, sm);
6827 	if(strstr(pm->submodel[sm].name, "-destroyed")){
6828 		sub_total_destroyed = total;
6829 	}
6830 
6831 	// write out total
6832 	sprintf(str, "Submodel %s total : %d faces\n", pm->submodel[sm].name, total);
6833 	cfputs(str, out);
6834 
6835 	*out_total += total + sub_total;
6836 	*out_destroyed_total += sub_total_destroyed;
6837 }
6838 
6839 #define BAIL()			do { int _idx; for(_idx=0; _idx<num_files; _idx++){ if(pof_list[_idx] != NULL){vm_free(pof_list[_idx]); pof_list[_idx] = NULL;}} return;} while(0);
6840 void game_spew_pof_info()
6841 {
6842 	char *pof_list[1000];
6843 	int num_files;
6844 	CFILE *out;
6845 	int idx, model_num, i, j;
6846 	polymodel *pm;
6847 	int total, root_total, model_total, destroyed_total, counted;
6848 	char str[255] = "";
6849 
6850 	// get file list
6851 	num_files = cf_get_file_list(1000, pof_list, CF_TYPE_MODELS, "*.pof");
6852 
6853 	// spew info on all the pofs
6854 	if(!num_files){
6855 		return;
6856 	}
6857 
6858 	// go
6859 	out = cfopen("pofspew.txt", "wt", CFILE_NORMAL, CF_TYPE_DATA);
6860 	if(out == NULL){
6861 		BAIL();
6862 	}
6863 	counted = 0;
6864 	for(idx=0; idx<num_files; idx++, counted++){
6865 		sprintf(str, "%s.pof", pof_list[idx]);
6866 		model_num = model_load(str, 0, NULL);
6867 		if(model_num >= 0){
6868 			pm = model_get(model_num);
6869 
6870 			// if we have a real model
6871 			if(pm != NULL){
6872 				cfputs(str, out);
6873 				cfputs("\n", out);
6874 
6875 				// go through and print all raw submodels
6876 				cfputs("RAW\n", out);
6877 				total = 0;
6878 				model_total = 0;
6879 				for (i=0; i<pm->n_models; i++)	{
6880 					total = submodel_get_num_polys(model_num, i);
6881 
6882 					model_total += total;
6883 					sprintf(str, "Submodel %s total : %d faces\n", pm->submodel[i].name, total);
6884 					cfputs(str, out);
6885 				}
6886 				sprintf(str, "Model total %d\n", model_total);
6887 				cfputs(str, out);
6888 
6889 				// now go through and do it by LOD
6890 				cfputs("BY LOD\n\n", out);
6891 				for(i=0; i<pm->n_detail_levels; i++){
6892 					sprintf(str, "LOD %d\n", i);
6893 					cfputs(str, out);
6894 
6895 					// submodels
6896 					root_total = submodel_get_num_polys(model_num, pm->detail[i] );
6897 					total = 0;
6898 					destroyed_total = 0;
6899 					for (j=pm->submodel[pm->detail[i]].first_child; j >= 0; j = pm->submodel[j].next_sibling )	{
6900 						game_spew_pof_info_sub(model_num, pm, j, out, &total, &destroyed_total);
6901 					}
6902 
6903 					sprintf(str, "Submodel %s total : %d faces\n", pm->submodel[pm->detail[i]].name, root_total);
6904 					cfputs(str, out);
6905 
6906 					sprintf(str, "TOTAL: %d\n", total + root_total);
6907 					cfputs(str, out);
6908 					sprintf(str, "TOTAL not counting destroyed faces %d\n", (total + root_total) - destroyed_total);
6909 					cfputs(str, out);
6910 					sprintf(str, "TOTAL destroyed faces %d\n\n", destroyed_total);
6911 					cfputs(str, out);
6912 				}
6913 				cfputs("------------------------------------------------------------------------\n\n", out);
6914 			}
6915 		}
6916 
6917 		if(counted >= MAX_POLYGON_MODELS - 5){
6918 			model_free_all();
6919 			counted = 0;
6920 		}
6921 	}
6922 
6923 	cfclose(out);
6924 	model_free_all();
6925 	BAIL();
6926 }
6927 
6928 DCF(pofspew, "")
6929 {
6930 	game_spew_pof_info();
6931 }
6932 
6933 // returns:
6934 //		0 on an error
6935 //		1 on a clean exit
6936 int game_main(char *cmdline)
6937 {
6938 	int state;
6939 
6940 	// check if networking should be disabled, this could probably be done later but the sooner the better
6941 	// TODO: remove this when multi is fixed to handle more than MAX_SHIP_CLASSES_MULTI
6942 	if ( Num_ship_classes > MAX_SHIP_CLASSES_MULTI ) {
6943 		Networking_disabled = 1;
6944 	}
6945 
6946 #ifndef NDEBUG
6947 	extern void windebug_memwatch_init();
6948 	windebug_memwatch_init();
6949 #endif
6950 
6951 #ifdef _WIN32
6952 	// Find out how much RAM is on this machine
6953 	MEMORYSTATUS ms;
6954 	ms.dwLength = sizeof(MEMORYSTATUS);
6955 	GlobalMemoryStatus(&ms);
6956 	FreeSpace_total_ram = ms.dwTotalPhys;
6957 
6958 	Mem_starttime_phys      = ms.dwAvailPhys;
6959 	Mem_starttime_pagefile  = ms.dwAvailPageFile;
6960 	Mem_starttime_virtual   = ms.dwAvailVirtual;
6961 
6962 	if ( game_do_ram_check(FreeSpace_total_ram) == -1 ) {
6963 		return 1;
6964 	}
6965 
6966 	if ( ms.dwTotalVirtual < 1024 )	{
6967 		MessageBox( NULL, XSTR( "FreeSpace requires virtual memory to run.\r\n", 196), XSTR( "No Virtual Memory", 197), MB_OK );
6968 		return 1;
6969 	}
6970 
6971 	if (!vm_init(24*1024*1024)) {
6972 		MessageBox( NULL, XSTR( "Not enough memory to run FreeSpace.\r\nTry closing down some other applications.\r\n", 198), XSTR( "Not Enough Memory", 199), MB_OK );
6973 		return 1;
6974 	}
6975 
6976 	char *tmp_mem = (char *) vm_malloc(16 * 1024 * 1024);
6977 	if (!tmp_mem) {
6978 		MessageBox(NULL, XSTR( "Not enough memory to run FreeSpace.\r\nTry closing down some other applications.\r\n", 198), XSTR( "Not Enough Memory", 199), MB_OK);
6979 		return 1;
6980 	}
6981 
6982 	vm_free(tmp_mem);
6983 	tmp_mem = NULL;
6984 
6985 #else
6986 
6987 	vm_init(0);
6988 
6989 #endif // _WIN32
6990 
6991 
6992 	if ( !parse_cmdline(cmdline) ) {
6993 		return 1;
6994 	}
6995 
6996 
6997 	if (Is_standalone){
6998 		nprintf(("Network", "Standalone running\n"));
6999 	}
7000 
7001 
7002 #ifdef _WIN32
7003 	if ( !Is_standalone )
7004 		disableWindowsKey( );
7005 #endif
7006 
7007 
7008 	init_cdrom();
7009 
7010 	game_init();
7011 	// calling the function that will init all the function pointers for TrackIR stuff (Swifty)
7012 	int trackIrInitResult = gTirDll_TrackIR.Init( (HWND)os_get_window( ) );
7013 	if ( trackIrInitResult != SCP_INITRESULT_SUCCESS )
7014 	{
7015 		mprintf( ("TrackIR Init Failed - %d\n", trackIrInitResult) );
7016 	}
7017 	game_stop_time();
7018 
7019 	if (Cmdline_spew_mission_crcs) {
7020 		multi_spew_pxo_checksums(1024, "mission_crcs.csv");
7021 
7022 		if (Cmdline_spew_table_crcs) {
7023 			fs2netd_spew_table_checksums("table_crcs.csv");
7024 		}
7025 
7026 		game_shutdown();
7027 		return 0;
7028 	}
7029 
7030 
7031 	if (Cmdline_spew_table_crcs) {
7032 		fs2netd_spew_table_checksums("table_crcs.csv");
7033 		game_shutdown();
7034 		return 0;
7035 	}
7036 
7037 	// maybe spew pof stuff
7038 	if(Cmdline_spew_pof_info){
7039 		game_spew_pof_info();
7040 		game_shutdown();
7041 		return 0;
7042 	}
7043 
7044 
7045 	// maybe spew VP CRCs, and exit
7046 	if (Cmdline_verify_vps) {
7047 		extern void cfile_spew_pack_file_crcs();
7048 		cfile_spew_pack_file_crcs();
7049 		game_shutdown();
7050 		return 0;
7051 	}
7052 
7053 	if (!Is_standalone) {
7054 		movie_play( NOX("intro.mve") );
7055 	}
7056 
7057 	if (Is_standalone){
7058 		gameseq_post_event(GS_EVENT_STANDALONE_MAIN);
7059 	} else {
7060 		gameseq_post_event(GS_EVENT_GAME_INIT);		// start the game rolling -- check for default pilot, or go to the pilot select screen
7061 	}
7062 
7063 	while (1) {
7064 		// only important for non THREADED mode
7065 		os_poll();
7066 
7067 		state = gameseq_process_events();
7068 		if ( state == GS_STATE_QUIT_GAME ){
7069 			break;
7070 		}
7071 	}
7072 
7073 	game_shutdown();
7074 
7075 #ifdef _WIN32
7076 	if ( !Is_standalone )
7077 		enableWindowsKey( );
7078 #endif
7079 
7080 	return 0;
7081 }
7082 
7083 
7084 // ------------------------------------------------------------------------------
7085 // Platform specific main() functions, nothing directly related to game function
7086 // should go here.  Direct game related info should go in the game_main() function
7087 // TODO: this should end up in a separate file in the not too distant future.
7088 //
7089 
7090 #ifdef _WIN32
7091 // Windows Specific
7092 int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int nCmdShow)
7093 {
7094 	int result = -1;
7095 
7096 	// Don't let more than one instance of FreeSpace run.
7097 	HWND hwnd = FindWindow( NOX( "FreeSpaceClass" ), NULL );
7098 	if ( hwnd )	{
7099 		SetForegroundWindow(hwnd);
7100 		return 0;
7101 	}
7102 
7103 	::CoInitialize(NULL);
7104 
7105 #ifdef _DEBUG
7106 	void memblockinfo_output_memleak();
7107 	atexit(memblockinfo_output_memleak);
7108 #endif
7109 
7110 	//=====================================================
7111 	// Make sure we're running in the right directory.
7112 	char exe_dir[1024];
7113 
7114 	if ( GetModuleFileName( hInst, exe_dir, 1023 ) > 0 )	{
7115 		char *p = exe_dir + strlen(exe_dir);
7116 
7117 		// chop off the filename
7118 		while( (p>exe_dir) && (*p!='\\') && (*p!='/') && (*p!=':') )	{
7119 			p--;
7120 		}
7121 		*p = 0;
7122 
7123 		// Set directory
7124 		if ( strlen(exe_dir) > 0 )	{ //-V805
7125 			SetCurrentDirectory(exe_dir);
7126 		}
7127 	}
7128 
7129 	SCP_mspdbcs_Initialise( );
7130 
7131 #ifdef GAME_ERRORLOG_TXT
7132 #ifdef _MSC_VER
7133 	__try {
7134 #endif
7135 #endif
7136 		result = !game_main(szCmdLine);
7137 #ifdef GAME_ERRORLOG_TXT
7138 #ifdef _MSC_VER
7139 	} __except( RecordExceptionInfo(GetExceptionInformation(), "FreeSpace 2 Main Thread") ) {
7140 		// Do nothing here - RecordExceptionInfo() has already done
7141 		// everything that is needed. Actually this code won't even
7142 		// get called unless you return EXCEPTION_EXECUTE_HANDLER from
7143 		// the __except clause.
7144 	}
7145 #endif // _MSC_VER
7146 #endif
7147 
7148 	SCP_mspdbcs_Cleanup( );
7149 
7150 	::CoUninitialize();
7151 
7152 #ifndef _MINGW
7153 	_CrtDumpMemoryLeaks();
7154 #endif
7155 
7156 	return result;
7157 }
7158 
7159 #else
7160 
7161 // *NIX specific
7162 int main(int argc, char *argv[])
7163 {
7164 	int result = EXIT_FAILURE;
7165 	char *argptr = NULL;
7166 	int i, len = 0;
7167 	char userdir[MAX_PATH];
7168 
7169 #ifdef APPLE_APP
7170 	// Finder sets the working directory to the root of the drive so we have to get a little creative
7171 	// to find out where on the disk we should be running from for CFILE's sake.
7172 	strncpy(full_path, *argv, 1024);
7173 #endif
7174 
7175 	// create user's directory
7176 	snprintf(userdir, MAX_PATH - 1, "%s/%s/", detect_home(), Osreg_user_dir);
7177 	_mkdir(userdir);
7178 
7179 
7180 	// clean up the cmdline to just send arguments through
7181 	for (i = 1; i < argc; i++) {
7182 		len += strlen(argv[i]) + 1;
7183 	}
7184 
7185 	argptr = (char*) calloc(len + 1, sizeof(char));
7186 
7187 	if (argptr == NULL) {
7188 		fprintf(stderr, "ERROR: Out of memory in main()!\n");
7189 		exit(EXIT_FAILURE);
7190 	}
7191 
7192 	memset( argptr, 0, len+1 );
7193 
7194 	for (i = 1; i < argc; i++) {
7195 		strcat(argptr, argv[i]);
7196 		strcat(argptr, " ");
7197 	}
7198 
7199 	// switch to game_main()
7200 	try {
7201 		result = game_main(argptr);
7202 
7203 		if (argptr != NULL) {
7204 			free(argptr);
7205 			argptr = NULL;
7206 		}
7207 	} catch (std::exception &ex) {
7208 		fprintf(stderr, "Caught std::exception in main(): '%s'!\n", ex.what());
7209 		result = EXIT_FAILURE;
7210 	} catch ( ... ) {
7211 		fprintf(stderr, "Caught exception in main()!\n");
7212 		result = EXIT_FAILURE;
7213 	}
7214 
7215 	return result;
7216 }
7217 
7218 #endif // _WIN32
7219 
7220 //
7221 // End of platform specific main() section
7222 // ------------------------------------------------------------------------------
7223 
7224 
7225 #if 0  // don't have an updater for fs2_open
7226 // launch the fslauncher program on exit
7227 void game_launch_launcher_on_exit()
7228 {
7229 	STARTUPINFO si;
7230 	PROCESS_INFORMATION pi;
7231 	char cmd_line[2048];
7232 	char original_path[1024] = "";
7233 
7234 	memset( &si, 0, sizeof(STARTUPINFO) );
7235 	si.cb = sizeof(si);
7236 
7237 	// directory
7238 	_getcwd(original_path, 1023);
7239 
7240 	// set up command line
7241 	strcpy_s(cmd_line, original_path);
7242 	strcat_s(cmd_line, DIR_SEPARATOR_STR);
7243 	strcat_s(cmd_line, LAUNCHER_FNAME);
7244 	strcat_s(cmd_line, " -straight_to_update");
7245 
7246 	BOOL ret = CreateProcess(	NULL,									// pointer to name of executable module
7247 										cmd_line,							// pointer to command line string
7248 										NULL,									// pointer to process security attributes
7249 										NULL,									// pointer to thread security attributes
7250 										FALSE,								// handle inheritance flag
7251 										CREATE_DEFAULT_ERROR_MODE,		// creation flags
7252 										NULL,									// pointer to new environment block
7253 										NULL,									// pointer to current directory name
7254 										&si,									// pointer to STARTUPINFO
7255 										&pi									// pointer to PROCESS_INFORMATION
7256 										);
7257 	// to eliminate build warnings
7258 	ret;
7259 }
7260 #endif
7261 
7262 
7263 // game_shutdown()
7264 //
7265 // This function is called when FreeSpace terminates normally.
7266 //
7267 void game_shutdown(void)
7268 {
7269 	gTirDll_TrackIR.Close( );
7270 	profile_deinit();
7271 
7272 	fsspeech_deinit();
7273 #ifdef FS2_VOICER
7274 	if(Cmdline_voice_recognition)
7275 	{
7276 		VOICEREC_deinit();
7277 	}
7278 #endif
7279 
7280 	// don't ever flip a page on the standalone!
7281 	if(!(Game_mode & GM_STANDALONE_SERVER)){
7282 		gr_reset_clip();
7283 		gr_clear();
7284 		gr_flip();
7285 	}
7286 
7287    // if the player has left the "player select" screen and quit the game without actually choosing
7288 	// a player, Player will be NULL, in which case we shouldn't write the player file out!
7289 	if (!(Game_mode & GM_STANDALONE_SERVER) && (Player!=NULL) && !Is_standalone){
7290 		Pilot.save_player();
7291 		Pilot.save_savefile();
7292 	}
7293 
7294 	// load up common multiplayer icons
7295 	multi_unload_common_icons();
7296 	hud_close();
7297 	fireball_close();				// free fireball system
7298 	particle_close();			// close out the particle system
7299 	weapon_close();					// free any memory that was allocated for the weapons
7300 	ship_close();					// free any memory that was allocated for the ships
7301 	hud_free_scrollback_list();// free space allocated to store hud messages in hud scrollback
7302 	unload_animating_pointer();// frees the frames used for the animating mouse pointer
7303 	mission_campaign_clear();	// clear out the campaign stuff
7304 	message_mission_close();	// clear loaded table data from message.tbl
7305 	mission_parse_close();		// clear out any extra memory that may be in use by mission parsing
7306 	multi_voice_close();			// close down multiplayer voice (including freeing buffers, etc)
7307 	multi_log_close();
7308 	logfile_close(LOGFILE_EVENT_LOG); // close down the mission log
7309 #ifdef MULTI_USE_LAG
7310 	multi_lag_close();
7311 #endif
7312 	fs2netd_close();
7313 
7314 	if ( Cmdline_old_collision_sys ) {
7315 		obj_pairs_close();		// free memory from object collision pairs
7316 	} else {
7317 		obj_reset_colliders();
7318 	}
7319 	stars_close();			// clean out anything used by stars code
7320 
7321 	// the menu close functions will unload the bitmaps if they were displayed during the game
7322 	main_hall_close();
7323 	training_menu_close();
7324 	gr_close();
7325 
7326 	// free left over memory from table parsing
7327 	player_tips_close();
7328 
7329 	joy_close();
7330 
7331 	audiostream_close();
7332 	snd_close();
7333 	event_music_close();
7334 	gamesnd_close();		// close out gamesnd, needs to happen *after* other sounds are closed
7335 	psnet_close();
7336 
7337 	model_free_all();
7338 	bm_unload_all();			// unload/free bitmaps, has to be called *after* model_free_all()!
7339 
7340 	os_cleanup();
7341 
7342 	// although the comment in cmdline.cpp said this isn't needed,
7343 	// Valgrind disagrees (quite possibly incorrectly), but this is just cleaner
7344 	if (Cmdline_mod != NULL) {
7345 		delete[] Cmdline_mod;
7346 		Cmdline_mod = NULL;
7347 	}
7348 
7349 #if 0  // don't have an updater for fs2_open
7350 	// HACKITY HACK HACK
7351 	// if this flag is set, we should be firing up the launcher when exiting freespace
7352 	extern int Multi_update_fireup_launcher_on_exit;
7353 	if(Multi_update_fireup_launcher_on_exit){
7354 		game_launch_launcher_on_exit();
7355 	}
7356 #endif
7357 }
7358 
7359 // game_stop_looped_sounds()
7360 //
7361 // This function will call the appropriate stop looped sound functions for those
7362 // modules which use looping sounds.  It is not enough just to stop a looping sound
7363 // at the DirectSound level, the game is keeping track of looping sounds, and this
7364 // function is used to inform the game that looping sounds are being halted.
7365 //
7366 void game_stop_looped_sounds()
7367 {
7368 	hud_stop_looped_locking_sounds();
7369 	hud_stop_looped_engine_sounds();
7370 	afterburner_stop_sounds();
7371 	player_stop_looped_sounds();
7372 	obj_snd_stop_all();		// stop all object-linked persistant sounds
7373 	game_stop_subspace_ambient_sound();
7374 	snd_stop(Radar_static_looping);
7375 	Radar_static_looping = -1;
7376 	snd_stop(Target_static_looping);
7377 	shipfx_stop_engine_wash_sound();
7378 	Target_static_looping = -1;
7379 }
7380 
7381 //////////////////////////////////////////////////////////////////////////
7382 //
7383 // Code for supporting an animating mouse pointer
7384 //
7385 //
7386 //////////////////////////////////////////////////////////////////////////
7387 
7388 typedef struct animating_obj
7389 {
7390 	int	first_frame;
7391 	int	num_frames;
7392 	int	current_frame;
7393 	float time;
7394 	float elapsed_time;
7395 } animating_obj;
7396 
7397 static animating_obj Animating_mouse;
7398 
7399 // ----------------------------------------------------------------------------
7400 // init_animating_pointer()
7401 //
7402 // Called by load_animating_pointer() to ensure the Animating_mouse struct
7403 // gets properly initialized
7404 //
7405 void init_animating_pointer()
7406 {
7407 	Animating_mouse.first_frame	= -1;
7408 	Animating_mouse.num_frames		= 0;
7409 	Animating_mouse.current_frame	= -1;
7410 	Animating_mouse.time				= 0.0f;
7411 	Animating_mouse.elapsed_time	= 0.0f;
7412 }
7413 
7414 // ----------------------------------------------------------------------------
7415 // load_animating_pointer()
7416 //
7417 // Called at game init to load in the frames for the animating mouse pointer
7418 //
7419 // input:	filename	=>	filename of animation file that holds the animation
7420 //
7421 void load_animating_pointer(char *filename, int dx, int dy)
7422 {
7423 	int				fps;
7424 	animating_obj *am;
7425 
7426 	init_animating_pointer();
7427 
7428 //TEMP
7429 	mprintf(("loading animated cursor \"%s\"\n", filename));
7430 
7431 
7432 	am = &Animating_mouse;
7433 	am->first_frame = bm_load_animation(filename, &am->num_frames, &fps);
7434 	if ( am->first_frame == -1 )
7435 		Error(LOCATION, "Could not load animation %s for the mouse pointer\n", filename);
7436 	am->current_frame = 0;
7437 	am->time = am->num_frames / i2fl(fps);
7438 }
7439 
7440 // ----------------------------------------------------------------------------
7441 // unload_animating_pointer()
7442 //
7443 // Called at game shutdown to free the memory used to store the animation frames
7444 //
7445 void unload_animating_pointer()
7446 {
7447 	int				i;
7448 	animating_obj	*am;
7449 
7450 	am = &Animating_mouse;
7451 	for ( i = 0; i < am->num_frames; i++ ) {
7452 		Assert( (am->first_frame+i) >= 0 );
7453 
7454 		// if we are the current cursor then reset to avoid gr_close() issues - taylor
7455 		gr_unset_cursor_bitmap(am->first_frame + i);
7456 	}
7457 
7458 	// this will release all of the frames at once
7459 	if (am->first_frame >= 0)
7460 		bm_release(am->first_frame);
7461 
7462 	am->first_frame	= -1;
7463 	am->num_frames		= 0;
7464 	am->current_frame = -1;
7465 }
7466 
7467 // draw the correct frame of the game mouse... called from game_maybe_draw_mouse()
7468 void game_render_mouse(float frametime)
7469 {
7470 	int				mx, my;
7471 	animating_obj	*am;
7472 
7473 	// if animating cursor exists, play the next frame
7474 	am = &Animating_mouse;
7475 	if ( am->first_frame != -1 ) {
7476 		mouse_get_pos(&mx, &my);
7477 		am->elapsed_time += frametime;
7478 		am->current_frame = fl2i( ( am->elapsed_time / am->time ) * (am->num_frames-1) );
7479 		if ( am->current_frame >= am->num_frames ) {
7480 			am->current_frame = 0;
7481 			am->elapsed_time = 0.0f;
7482 		}
7483 		gr_set_cursor_bitmap(am->first_frame + am->current_frame);
7484 	}
7485 }
7486 
7487 // ----------------------------------------------------------------------------
7488 // game_maybe_draw_mouse()
7489 //
7490 // determines whether to draw the mouse pointer at all, and what frame of
7491 // animation to use if the mouse is animating
7492 //
7493 // Sets mouse.cpp globals Mouse_hidden and Mouse_moved based on the state of the game.
7494 //
7495 // input:	frametime => elapsed frame time in seconds since last call
7496 //
7497 void game_maybe_draw_mouse(float frametime)
7498 {
7499 	int game_state;
7500 
7501 	game_state = gameseq_get_state();
7502 
7503 	switch ( game_state ) {
7504 		case GS_STATE_GAME_PAUSED:
7505 		// case GS_STATE_MULTI_PAUSED:
7506 		case GS_STATE_GAME_PLAY:
7507 		case GS_STATE_DEATH_DIED:
7508 		case GS_STATE_DEATH_BLEW_UP:
7509 			if ( popup_active() || popupdead_is_active() ) {
7510 				Mouse_hidden = 0;
7511 			} else {
7512 				Mouse_hidden = 1;
7513 			}
7514 			break;
7515 
7516 		default:
7517 			Mouse_hidden = 0;
7518 			break;
7519 	}	// end switch
7520 
7521 	if ( !Mouse_hidden )
7522 		game_render_mouse(frametime);
7523 
7524 }
7525 
7526 void game_do_training_checks()
7527 {
7528 	int i, s;
7529 	float d;
7530 	waypoint_list *wplp;
7531 
7532 	if (Training_context & TRAINING_CONTEXT_SPEED) {
7533 		s = (int) Player_obj->phys_info.fspeed;
7534 		if ((s >= Training_context_speed_min) && (s <= Training_context_speed_max)) {
7535 			if (!Training_context_speed_set) {
7536 				Training_context_speed_set = 1;
7537 				Training_context_speed_timestamp = timestamp();
7538 			}
7539 
7540 		} else
7541 			Training_context_speed_set = 0;
7542 	}
7543 
7544 	if (Training_context & TRAINING_CONTEXT_FLY_PATH) {
7545 		wplp = Training_context_path;
7546 		if (wplp->get_waypoints().size() > (uint) Training_context_goal_waypoint) {
7547 			i = Training_context_goal_waypoint;
7548 			do {
7549 				waypoint *wpt = find_waypoint_at_index(wplp, i);
7550 				Assert(wpt != NULL);
7551 				d = vm_vec_dist(wpt->get_pos(), &Player_obj->pos);
7552 				if (d <= Training_context_distance) {
7553 					Training_context_at_waypoint = i;
7554 					if (Training_context_goal_waypoint == i) {
7555 						Training_context_goal_waypoint++;
7556 						snd_play(&Snds[SND_CARGO_REVEAL], 0.0f);
7557 					}
7558 
7559 					break;
7560 				}
7561 
7562 				i++;
7563 				if ((uint) i == wplp->get_waypoints().size())
7564 					i = 0;
7565 
7566 			} while (i != Training_context_goal_waypoint);
7567 		}
7568 	}
7569 
7570 	if ((Players_target == UNINITIALIZED) || (Player_ai->target_objnum != Players_target) || (Player_ai->targeted_subsys != Players_targeted_subsys)) {
7571 		Players_target = Player_ai->target_objnum;
7572 		Players_targeted_subsys = Player_ai->targeted_subsys;
7573 		Players_target_timestamp = timestamp();
7574 	}
7575 	// following added by Sesquipedalian for is_missile_locked
7576 	if ((Players_mlocked == UNINITIALIZED) || (Player_ai->current_target_is_locked != Players_mlocked)) {
7577 		Players_mlocked = Player_ai->current_target_is_locked;
7578 		Players_mlocked_timestamp = timestamp();
7579 	}
7580 
7581 }
7582 
7583 /////////// Following is for event debug view screen
7584 
7585 #ifndef NDEBUG
7586 
7587 #define EVENT_DEBUG_MAX	5000
7588 #define EVENT_DEBUG_EVENT 0x8000
7589 
7590 int Event_debug_index[EVENT_DEBUG_MAX];
7591 int ED_count;
7592 
7593 void game_add_event_debug_index(int n, int indent)
7594 {
7595 	if (ED_count < EVENT_DEBUG_MAX)
7596 		Event_debug_index[ED_count++] = n | (indent << 16);
7597 }
7598 
7599 void game_add_event_debug_sexp(int n, int indent)
7600 {
7601 	if (n < 0)
7602 		return;
7603 
7604 	if (Sexp_nodes[n].first >= 0) {
7605 		game_add_event_debug_sexp(Sexp_nodes[n].first, indent);
7606 		game_add_event_debug_sexp(Sexp_nodes[n].rest, indent);
7607 		return;
7608 	}
7609 
7610 	game_add_event_debug_index(n, indent);
7611 	if (Sexp_nodes[n].subtype == SEXP_ATOM_OPERATOR)
7612 		game_add_event_debug_sexp(Sexp_nodes[n].rest, indent + 1);
7613 	else
7614 		game_add_event_debug_sexp(Sexp_nodes[n].rest, indent);
7615 }
7616 
7617 void game_event_debug_init()
7618 {
7619 	int e;
7620 
7621 	ED_count = 0;
7622 	for (e=0; e<Num_mission_events; e++) {
7623 		game_add_event_debug_index(e | EVENT_DEBUG_EVENT, 0);
7624 		game_add_event_debug_sexp(Mission_events[e].formula, 1);
7625 	}
7626 }
7627 
7628 void game_show_event_debug(float frametime)
7629 {
7630 	char buf[256];
7631 	int i, k, z;
7632 	int font_height, font_width;
7633 	int y_index, y_max;
7634 	static int scroll_offset = 0;
7635 
7636 	k = game_check_key();
7637 	if (k)
7638 		switch (k) {
7639 			case KEY_UP:
7640 			case KEY_PAD8:
7641 				scroll_offset--;
7642 				if (scroll_offset < 0)
7643 					scroll_offset = 0;
7644 				break;
7645 
7646 			case KEY_DOWN:
7647 			case KEY_PAD2:
7648 				scroll_offset++;
7649 				break;
7650 
7651 			case KEY_PAGEUP:
7652 				scroll_offset -= 20;
7653 				if (scroll_offset < 0)
7654 					scroll_offset = 0;
7655 				break;
7656 
7657 			case KEY_PAGEDOWN:
7658 				scroll_offset += 20;	// not font-independent, hard-coded since I counted the lines!
7659 				break;
7660 
7661 			default:
7662 				gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
7663 				key_flush();
7664 				break;
7665 		} // end switch
7666 
7667 	gr_clear();
7668 	gr_set_color_fast(&Color_bright);
7669 	gr_set_font(FONT1);
7670 	gr_printf_no_resize(0x8000, 15, NOX("EVENT DEBUG VIEW"));
7671 
7672 	gr_set_color_fast(&Color_normal);
7673 	gr_set_font(FONT1);
7674 	gr_get_string_size(&font_width, &font_height, NOX("test"));
7675 	y_max = gr_screen.max_h - font_height - 5;
7676 	y_index = 45;
7677 
7678 	k = scroll_offset;
7679 	while (k < ED_count) {
7680 		if (y_index > y_max)
7681 			break;
7682 
7683 		z = Event_debug_index[k];
7684 		if (z & EVENT_DEBUG_EVENT) {
7685 			z &= 0x7fff;
7686 			sprintf(buf, NOX("%s%s (%s) %s%d %d"), (Mission_events[z].flags & MEF_CURRENT) ? NOX("* ") : "",
7687 				Mission_events[z].name, Mission_events[z].result ? NOX("True") : NOX("False"),
7688 				(Mission_events[z].chain_delay < 0) ? "" : NOX("x "),
7689 				Mission_events[z].repeat_count, Mission_events[z].interval);
7690 
7691 		} else {
7692 			i = (z >> 16) * 3;
7693 			buf[i] = 0;
7694 			while (i--)
7695 				buf[i] = ' ';
7696 
7697 			strcat_s(buf, Sexp_nodes[z & 0x7fff].text);
7698 			switch (Sexp_nodes[z & 0x7fff].value) {
7699 				case SEXP_TRUE:
7700 					strcat_s(buf, NOX(" (True)"));
7701 					break;
7702 
7703 				case SEXP_FALSE:
7704 					strcat_s(buf, NOX(" (False)"));
7705 					break;
7706 
7707 				case SEXP_KNOWN_TRUE:
7708 					strcat_s(buf, NOX(" (Always true)"));
7709 					break;
7710 
7711 				case SEXP_KNOWN_FALSE:
7712 					strcat_s(buf, NOX(" (Always false)"));
7713 					break;
7714 
7715 				case SEXP_CANT_EVAL:
7716 					strcat_s(buf, NOX(" (Can't eval)"));
7717 					break;
7718 
7719 				case SEXP_NAN:
7720 				case SEXP_NAN_FOREVER:
7721 					strcat_s(buf, NOX(" (Not a number)"));
7722 					break;
7723 			}
7724 		}
7725 
7726 		gr_printf_no_resize(10, y_index, buf);
7727 		y_index += font_height;
7728 		k++;
7729 	}
7730 
7731 	gr_flip();
7732 }
7733 
7734 #endif // NDEBUG
7735 
7736 #ifndef NDEBUG
7737 FILE * Time_fp;
7738 FILE * Texture_fp;
7739 
7740 int Tmap_npixels=0;
7741 int Tmap_num_too_big = 0;
7742 int Num_models_needing_splitting = 0;
7743 
7744 void Time_model( int modelnum )
7745 {
7746 //	mprintf(( "Timing ship '%s'\n", si->name ));
7747 
7748 	vec3d eye_pos, model_pos;
7749 	matrix eye_orient, model_orient;
7750 
7751 	polymodel *pm = model_get( modelnum );
7752 
7753 	int l = strlen(pm->filename);
7754 	while( (l>0) )	{
7755 		if ( (l == '/') || (l=='\\') || (l==':'))	{
7756 			l++;
7757 			break;
7758 		}
7759 		l--;
7760 	}
7761 	char *pof_file = &pm->filename[l];
7762 
7763 	int model_needs_splitting = 0;
7764 
7765 	//fprintf( Texture_fp, "Model: %s\n", pof_file );
7766 	int i;
7767 	for (i=0; i<pm->n_textures; i++ )	{
7768 		char filename[1024];
7769 		ubyte pal[768];
7770 		texture_map *tmap = &pm->maps[i];
7771 
7772 		for(int j = 0; j < TM_NUM_TYPES; j++)
7773 		{
7774 			int bmp_num = tmap->textures[j].GetOriginalTexture();
7775 			if ( bmp_num > -1 )	{
7776 				bm_get_palette(bmp_num, pal, filename );
7777 				int w,h;
7778 				bm_get_info( bmp_num,&w, &h );
7779 
7780 
7781 				if ( (w > 512) || (h > 512) )	{
7782 					fprintf( Texture_fp, "%s\t%s\t%d\t%d\n", pof_file, filename, w, h );
7783 					Tmap_num_too_big++;
7784 					model_needs_splitting++;
7785 				}
7786 			} else {
7787 				//fprintf( Texture_fp, "\tTexture %d is bogus\n", i );
7788 			}
7789 		}
7790 	}
7791 
7792 	if ( model_needs_splitting )	{
7793 		Num_models_needing_splitting++;
7794 	}
7795 	eye_orient = model_orient = vmd_identity_matrix;
7796 	eye_pos = model_pos = vmd_zero_vector;
7797 
7798 	eye_pos.xyz.z = -pm->rad*2.0f;
7799 
7800 	vec3d eye_to_model;
7801 
7802 	vm_vec_sub( &eye_to_model, &model_pos, &eye_pos );
7803 	vm_vector_2_matrix( &eye_orient, &eye_to_model, NULL, NULL );
7804 
7805 	fix t1 = timer_get_fixed_seconds();
7806 
7807 	angles ta;
7808 	ta.p = ta.b = ta.h = 0.0f;
7809 	int framecount = 0;
7810 
7811 	Tmap_npixels = 0;
7812 
7813 	int bitmaps_used_this_frame, bitmaps_new_this_frame;
7814 
7815 	bm_get_frame_usage(&bitmaps_used_this_frame,&bitmaps_new_this_frame);
7816 
7817 	modelstats_num_polys = modelstats_num_verts = 0;
7818 
7819 	while( ta.h < PI2 )	{
7820 
7821 		matrix m1;
7822 		vm_angles_2_matrix(&m1, &ta );
7823 		vm_matrix_x_matrix( &model_orient, &vmd_identity_matrix, &m1 );
7824 
7825 		gr_reset_clip();
7826 //		gr_clear();
7827 
7828 		g3_start_frame(1);
7829 
7830 		//WMC - I think I can set this to VIEWER_ZOOM_DEFAULT.
7831 		//If it's not appropriate, use cam_get_current()
7832 		g3_set_view_matrix( &eye_pos, &eye_orient, VIEWER_ZOOM_DEFAULT );
7833 
7834 		model_clear_instance( modelnum );
7835 		model_set_detail_level(0);		// use highest detail level
7836 		model_render( modelnum, &model_orient, &model_pos, MR_LOCK_DETAIL);	//|MR_NO_POLYS );
7837 
7838 		g3_end_frame();
7839 //		gr_flip();
7840 
7841 		framecount++;
7842 		ta.h += 0.1f;
7843 
7844 		int k = key_inkey();
7845 		if ( k == KEY_ESC ) {
7846 			exit(1);
7847 		}
7848 	}
7849 
7850 	fix t2 = timer_get_fixed_seconds();
7851 
7852 	bm_get_frame_usage(&bitmaps_used_this_frame,&bitmaps_new_this_frame);
7853 
7854 	modelstats_num_polys /= framecount;
7855 	modelstats_num_verts /= framecount;
7856 
7857 	Tmap_npixels /=framecount;
7858 
7859 
7860 	mprintf(( "'%s' is %.2f FPS\n", pof_file, i2fl(framecount)/f2fl(t2-t1) ));
7861 	fprintf( Time_fp, "\"%s\"\t%.0f\t%d\t%d\t%d\t%d\n", pof_file, i2fl(framecount)/f2fl(t2-t1), bitmaps_used_this_frame, modelstats_num_polys, modelstats_num_verts, Tmap_npixels );
7862 }
7863 
7864 int Time_models = 0;
7865 DCF_BOOL( time_models, Time_models )
7866 
7867 void Do_model_timings_test()
7868 {
7869 
7870 
7871 	if ( !Time_models ) return;
7872 
7873 	mprintf(( "Timing models!\n" ));
7874 
7875 	int i;
7876 
7877 	ubyte model_used[MAX_POLYGON_MODELS];
7878 	int model_id[MAX_POLYGON_MODELS];
7879 	for (i=0; i<MAX_POLYGON_MODELS; i++ )	{
7880 		model_used[i] = 0;
7881 	}
7882 
7883 	// Load them all
7884 	for (i=0; i<Num_ship_classes; i++ )	{
7885 		ship_info *sip = &Ship_info[i];
7886 
7887 		sip->model_num = model_load(sip->pof_file, 0, NULL);
7888 
7889 		model_used[sip->model_num % MAX_POLYGON_MODELS]++;
7890 		model_id[sip->model_num % MAX_POLYGON_MODELS] = sip->model_num;
7891 	}
7892 
7893 	Texture_fp = fopen( NOX("ShipTextures.txt"), "wt" );
7894 	if ( !Texture_fp ) return;
7895 
7896 	Time_fp = fopen( NOX("ShipTimings.txt"), "wt" );
7897 	if ( !Time_fp ) return;
7898 
7899 	fprintf( Time_fp, "Name\tFPS\tTRAM\tPolys\tVerts\tPixels\n" );
7900 //	fprintf( Time_fp, "FPS\tTRAM\tPolys\tVerts\tPixels\n" );
7901 
7902 	for (i=0; i<MAX_POLYGON_MODELS; i++ )	{
7903 		if ( model_used[i] )	{
7904 			Time_model( model_id[i] );
7905 		}
7906 	}
7907 
7908 	fprintf( Texture_fp, "Number too big: %d\n", Tmap_num_too_big );
7909 	fprintf( Texture_fp, "Number of models needing splitting: %d\n", Num_models_needing_splitting );
7910 
7911 	fclose(Time_fp);
7912 	fclose(Texture_fp);
7913 
7914 	exit(1);
7915 }
7916 #endif
7917 
7918 // Call this function when you want to inform the player that a feature is disabled in this build
7919 void game_feature_disabled_popup()
7920 {
7921 	popup(PF_USE_AFFIRMATIVE_ICON|PF_BODY_BIG, 1, POPUP_OK, XSTR( "Sorry, the requested feature is currently disabled in this build", 1621));
7922 }
7923 
7924 // format the specified time (fixed point) into a nice string
7925 void game_format_time(fix m_time,char *time_str)
7926 {
7927 	float mtime;
7928 	int hours,minutes,seconds;
7929 	char tmp[10];
7930 
7931 	mtime = f2fl(m_time);
7932 
7933 	// get the hours, minutes and seconds
7934 	hours = (int)(mtime / 3600.0f);
7935 	if(hours > 0){
7936 		mtime -= (3600.0f * (float)hours);
7937 	}
7938 	seconds = (int)mtime%60;
7939 	minutes = (int)mtime/60;
7940 
7941 	// print the hour if necessary
7942 	if(hours > 0){
7943 		sprintf(time_str,XSTR( "%d:", 201),hours);
7944 		// if there are less than 10 minutes, print a leading 0
7945 		if(minutes < 10){
7946 			strcpy_s(tmp,NOX("0"));
7947 			strcat(time_str,tmp);
7948 		}
7949 	}
7950 
7951 	// print the minutes
7952 	if(hours){
7953 		sprintf(tmp,XSTR( "%d:", 201),minutes);
7954 		strcat(time_str,tmp);
7955 	} else {
7956 		sprintf(time_str,XSTR( "%d:", 201),minutes);
7957 	}
7958 
7959 	// print the seconds
7960 	if(seconds < 10){
7961 		strcpy_s(tmp,NOX("0"));
7962 		strcat(time_str,tmp);
7963 	}
7964 	sprintf(tmp,"%d",seconds);
7965 	strcat(time_str,tmp);
7966 }
7967 
7968 //	Stuff version string in *str.
7969 void get_version_string(char *str, int max_size)
7970 {
7971 //XSTR:OFF
7972 	Assert( max_size > 6 );
7973 
7974 	#if FS_VERSION_REVIS == 0
7975 		sprintf(str, "FreeSpace 2 Open v%i.%i.%i", FS_VERSION_MAJOR, FS_VERSION_MINOR, FS_VERSION_BUILD);
7976 	#else
7977 		sprintf(str, "FreeSpace 2 Open v%i.%i.%i.%i", FS_VERSION_MAJOR, FS_VERSION_MINOR, FS_VERSION_BUILD, FS_VERSION_REVIS);
7978 	#endif
7979 
7980 #ifndef NDEBUG
7981 	strcat_s( str, max_size, " Debug" );
7982 #endif
7983 
7984 	// Lets get some more info in here
7985 	switch(gr_screen.mode)
7986 	{
7987 		case GR_OPENGL:
7988 			strcat_s( str, max_size, " OpenGL" );
7989 			break;
7990 	}
7991 
7992 	if (Cmdline_nohtl)
7993 		strcat_s( str, max_size, " non-HT&L" );
7994 }
7995 
7996 void get_version_string_short(char *str)
7997 {
7998 	sprintf(str,"v%d.%d", FS_VERSION_MAJOR, FS_VERSION_MINOR);
7999 }
8000 
8001 // ----------------------------------------------------------------
8002 //
8003 // Subspace Ambient Sound START
8004 //
8005 // ----------------------------------------------------------------
8006 
8007 static int Subspace_ambient_left_channel = -1;
8008 static int Subspace_ambient_right_channel = -1;
8009 
8010 //
8011 void game_start_subspace_ambient_sound()
8012 {
8013 	if ( Subspace_ambient_left_channel < 0 ) {
8014 		Subspace_ambient_left_channel = snd_play_looping(&Snds[SND_SUBSPACE_LEFT_CHANNEL], -1.0f);
8015 	}
8016 
8017 	if ( Subspace_ambient_right_channel < 0 ) {
8018 		Subspace_ambient_right_channel = snd_play_looping(&Snds[SND_SUBSPACE_RIGHT_CHANNEL], 1.0f);
8019 	}
8020 }
8021 
8022 void game_stop_subspace_ambient_sound()
8023 {
8024 	if ( Subspace_ambient_left_channel >= 0 ) {
8025 		snd_stop(Subspace_ambient_left_channel);
8026 		Subspace_ambient_left_channel = -1;
8027 	}
8028 
8029 	if ( Subspace_ambient_right_channel >= 0 ) {
8030 		snd_stop(Subspace_ambient_right_channel);
8031 		Subspace_ambient_right_channel = -1;
8032 	}
8033 }
8034 
8035 // ----------------------------------------------------------------
8036 //
8037 // Subspace Ambient Sound END
8038 //
8039 // ----------------------------------------------------------------
8040 
8041 
8042 // ----------------------------------------------------------------
8043 //
8044 // CDROM detection code START
8045 //
8046 // ----------------------------------------------------------------
8047 
8048 #define CD_SIZE_72_MINUTE_MAX			(697000000)
8049 
8050 uint game_get_cd_used_space(char *path)
8051 {
8052 #ifdef _WIN32
8053 	uint total = 0;
8054 	char use_path[512] = "";
8055 	char sub_path[512] = "";
8056 	WIN32_FIND_DATA	find;
8057 	HANDLE find_handle;
8058 
8059 	// recurse through all files and directories
8060 	strcpy_s(use_path, path);
8061 	strcat_s(use_path, "*.*");
8062 	find_handle = FindFirstFile(use_path, &find);
8063 
8064 	// bogus
8065 	if(find_handle == INVALID_HANDLE_VALUE){
8066 		return 0;
8067 	}
8068 
8069 	// whee
8070 	do {
8071 		// subdirectory. make sure to ignore . and ..
8072 		if((find.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && stricmp(find.cFileName, ".") && stricmp(find.cFileName, "..")){
8073 			// subsearch
8074 			strcpy_s(sub_path, path);
8075 			strcat_s(sub_path, find.cFileName);
8076 			strcat_s(sub_path, DIR_SEPARATOR_STR);
8077 			total += game_get_cd_used_space(sub_path);
8078 		} else {
8079 			total += (uint)find.nFileSizeLow;
8080 		}
8081 	} while(FindNextFile(find_handle, &find));
8082 
8083 	// close
8084 	FindClose(find_handle);
8085 
8086 	// total
8087 	return total;
8088 #else
8089 	if (path == NULL) {
8090 		// bail
8091 		mprintf(("NULL path passed to game_get_cd_used_space.\n"));
8092 		return 0;
8093 	}
8094 
8095 	STUB_FUNCTION;
8096 
8097 	return 0;
8098 #endif // _WIN32
8099 }
8100 
8101 
8102 // if volume_name is non-null, the CD name must match that
8103 int find_freespace_cd(char *volume_name)
8104 {
8105 #ifdef _WIN32
8106 	char oldpath[MAX_PATH];
8107 	char volume[256];
8108 	int i;
8109 	int cdrom_drive=-1;
8110 	int volume_match = 0;
8111 	_finddata_t find;
8112 	int find_handle;
8113 
8114 	GetCurrentDirectory(MAX_PATH-1, oldpath);
8115 
8116 	for (i = 0; i < 26; i++)
8117 	{
8118 //XSTR:OFF
8119 		char path[]="d:\\";
8120 //XSTR:ON
8121 
8122 		path[0] = (char)('A'+i);
8123 		if (GetDriveType(path) == DRIVE_CDROM) {
8124 			cdrom_drive = -3;
8125 			if ( GetVolumeInformation(path, volume, 256, NULL, NULL, NULL, NULL, 0) == TRUE ) {
8126 				nprintf(("CD", "CD volume: %s\n", volume));
8127 
8128 				// check for any CD volume
8129 				int volume1_present = 0;
8130 				int volume2_present = 0;
8131 				int volume3_present = 0;
8132 
8133 				char full_check[512] = "";
8134 
8135 				// look for setup.exe
8136 				strcpy_s(full_check, path);
8137 				strcat_s(full_check, "setup.exe");
8138 				find_handle = _findfirst(full_check, &find);
8139 				if(find_handle != -1){
8140 					volume1_present = 1;
8141 					_findclose(find_handle);
8142 				}
8143 
8144 				// look for intro.mve
8145 				strcpy_s(full_check, path);
8146 				strcat_s(full_check, "intro.mve");
8147 				find_handle = _findfirst(full_check, &find);
8148 				if(find_handle != -1){
8149 					volume2_present = 1;
8150 					_findclose(find_handle);
8151 				}
8152 
8153 				// look for endpart1.mve
8154 				strcpy_s(full_check, path);
8155 				strcat_s(full_check, "endpart1.mve");
8156 				find_handle = _findfirst(full_check, &find);
8157 				if(find_handle != -1){
8158 					volume3_present = 1;
8159 					_findclose(find_handle);
8160 				}
8161 
8162 				// see if we have the specific CD we're looking for
8163 				if ( volume_name ) {
8164 					// volume 1
8165 					if ( !stricmp(volume_name, FS_CDROM_VOLUME_1) && volume1_present) {
8166 						volume_match = 1;
8167 					}
8168 					// volume 2
8169 					if ( !stricmp(volume_name, FS_CDROM_VOLUME_2) && volume2_present) {
8170 						volume_match = 1;
8171 					}
8172 					// volume 3
8173 					if ( !stricmp(volume_name, FS_CDROM_VOLUME_3) && volume3_present) {
8174 						volume_match = 1;
8175 					}
8176 				} else {
8177 					if ( volume1_present || volume2_present || volume3_present ) {
8178 						volume_match = 1;
8179 					}
8180 				}
8181 
8182 				// here's where we make sure that CD's 2 and 3 are not just ripped - check to make sure its capacity is > 697,000,000 bytes
8183 				if ( volume_match ){
8184 					// we don't care about CD1 though. let it be whatever size it wants, since the game will demand CD's 2 and 3 at the proper time
8185 					if(volume2_present || volume3_present) {
8186 						// first step - check to make sure its a cdrom
8187 						if(GetDriveType(path) != DRIVE_CDROM){
8188 							break;
8189 						}
8190 						// oem not on 80 min cds, so don't check tha size
8191 						// check its size
8192 						uint used_space = game_get_cd_used_space(path);
8193 						if(used_space < CD_SIZE_72_MINUTE_MAX){
8194 							break;
8195 						}
8196 					}
8197 
8198 					cdrom_drive = i;
8199 					break;
8200 				}
8201 			}
8202 		}
8203 	}
8204 
8205 	SetCurrentDirectory(oldpath);
8206 	return cdrom_drive;
8207 #else
8208 //	STUB_FUNCTION;
8209 
8210 	if (volume_name != NULL) {
8211 		// volume specific checks
8212 		STUB_FUNCTION;
8213 	}
8214 
8215 	return -1;
8216 #endif // _WIN32
8217 }
8218 
8219 int set_cdrom_path(int drive_num)
8220 {
8221 	int rval;
8222 
8223 	if (drive_num < 0) {			//no CD
8224 //		#ifndef NDEBUG
8225 //		strcpy_s(CDROM_dir,"j:\\FreeSpaceCD\\");				//set directory
8226 //		rval = 1;
8227 //		#else
8228 		memset(Game_CDROM_dir, 0, sizeof(Game_CDROM_dir));
8229 		rval = 0;
8230 //		#endif
8231 	} else {
8232 		sprintf(Game_CDROM_dir,NOX("%c:\\"), 'a' + drive_num );			//set directory
8233 		rval = 1;
8234 	}
8235 
8236 	return rval;
8237 }
8238 
8239 int init_cdrom()
8240 {
8241 	int i, rval;
8242 
8243 	//scan for CD, etc.
8244 	i = find_freespace_cd();
8245 
8246 	rval = set_cdrom_path(i);
8247 
8248 	return rval;
8249 }
8250 
8251 int Last_cd_label_found = 0;
8252 char Last_cd_label[256];
8253 
8254 int game_cd_changed()
8255 {
8256 #ifdef _WIN32
8257 	char label[256];
8258 	int found;
8259 	int changed = 0;
8260 
8261 	if ( strlen(Game_CDROM_dir) == 0 ) { //-V805
8262 		init_cdrom();
8263 	}
8264 
8265 	if ( strlen(Game_CDROM_dir) == 0 ) { //-V805
8266 		return 0;
8267 	}
8268 
8269 	found = GetVolumeInformation(Game_CDROM_dir, label, 256, NULL, NULL, NULL, NULL, 0);
8270 
8271 	if ( found != Last_cd_label_found )	{
8272 		Last_cd_label_found = found;
8273 		if ( found )	{
8274 			mprintf(( "CD '%s' was inserted\n", label ));
8275 		} else {
8276 			mprintf(( "CD '%s' was removed\n", Last_cd_label ));
8277 		}
8278         changed = 1;
8279 	} else {
8280 		if ( Last_cd_label_found )	{
8281 			if ( !stricmp( Last_cd_label, label ))	{
8282 				//mprintf(( "CD didn't change\n" ));
8283 			} else {
8284 				mprintf(( "CD was changed from '%s' to '%s'\n", Last_cd_label, label ));
8285 				changed = 1;
8286 			}
8287 		} else {
8288 			// none found before, none found now.
8289 			//mprintf(( "still no CD...\n" ));
8290 		}
8291 	}
8292 
8293 	Last_cd_label_found = found;
8294 	if ( found )	{
8295 		strcpy_s( Last_cd_label, label );
8296 	} else {
8297 		strcpy_s( Last_cd_label, "" );
8298 	}
8299 
8300 	return changed;
8301 #else
8302 	STUB_FUNCTION;
8303 
8304 	return 0;
8305 #endif // _WIN32
8306 }
8307 
8308 // check if _any_ FreeSpace2 CDs are in the drive
8309 // return: 1	=> CD now in drive
8310 //			  0	=>	Could not find CD, they refuse to put it in the drive
8311 int game_do_cd_check(char *volume_name)
8312 {
8313 #if !defined(GAME_CD_CHECK)
8314 	return 1;
8315 #else
8316 	int cd_present = 0;
8317 	int cd_drive_num;
8318 
8319 	int num_attempts = 0;
8320 	int refresh_files = 0;
8321 	while(1) {
8322 		int path_set_ok, popup_rval;
8323 
8324 		cd_drive_num = find_freespace_cd(volume_name);
8325 		path_set_ok = set_cdrom_path(cd_drive_num);
8326 		if ( path_set_ok ) {
8327 			cd_present = 1;
8328 			if ( refresh_files ) {
8329 				cfile_refresh();
8330 				refresh_files = 0;
8331 			}
8332 			break;
8333 		}
8334 
8335 		// standalone mode
8336 		if(Is_standalone){
8337 			cd_present = 0;
8338 			break;
8339 		} else {
8340 			// no CD found, so prompt user
8341 			popup_rval = popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR( "FreeSpace 2 CD not found\n\nInsert a FreeSpace 2 CD to continue", 202));
8342 			refresh_files = 1;
8343 			if ( popup_rval != 1 ) {
8344 				cd_present = 0;
8345 				break;
8346 			}
8347 
8348 			if ( num_attempts++ > 5 ) {
8349 				cd_present = 0;
8350 				break;
8351 			}
8352 		}
8353 	}
8354 
8355 	return cd_present;
8356 #endif
8357 }
8358 
8359 // check if _any_ FreeSpace2 CDs are in the drive
8360 // return: 1	=> CD now in drive
8361 //			  0	=>	Could not find CD, they refuse to put it in the drive
8362 int game_do_cd_check_specific(char *volume_name, int cdnum)
8363 {
8364 	int cd_present = 0;
8365 	int cd_drive_num;
8366 
8367 	int num_attempts = 0;
8368 	int refresh_files = 0;
8369 	while(1) {
8370 		int path_set_ok, popup_rval;
8371 
8372 		cd_drive_num = find_freespace_cd(volume_name);
8373 		path_set_ok = set_cdrom_path(cd_drive_num);
8374 		if ( path_set_ok ) {
8375 			cd_present = 1;
8376 			if ( refresh_files ) {
8377 				cfile_refresh();
8378 				refresh_files = 0;
8379 			}
8380 			break;
8381 		}
8382 
8383 		if(Is_standalone){
8384 			cd_present = 0;
8385 			break;
8386 		} else {
8387 			// no CD found, so prompt user
8388 			popup_rval = popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Please insert CD %d", 1468), cdnum);
8389 			refresh_files = 1;
8390 			if ( popup_rval != 1 ) {
8391 				cd_present = 0;
8392 				break;
8393 			}
8394 
8395 			if ( num_attempts++ > 5 ) {
8396 				cd_present = 0;
8397 				break;
8398 			}
8399 		}
8400 	}
8401 
8402 	return cd_present;
8403 }
8404 
8405 // ----------------------------------------------------------------
8406 //
8407 // CDROM detection code END
8408 //
8409 // ----------------------------------------------------------------
8410 
8411 
8412 // ----------------------------------------------------------------
8413 // Language autodetection stuff
8414 //
8415 
8416 // default setting is "-1" to use registry setting with English as fall back
8417 // DO NOT change that default setting here or something uncouth might happen
8418 // in the localization code
8419 int detect_lang()
8420 {
8421 	uint file_checksum;
8422 	int idx;
8423 	char first_font[MAX_FILENAME_LEN];
8424 
8425 	// if the reg is set then let lcl_init() figure out what to do
8426 	if (os_config_read_string( NULL, NOX("Language"), NULL ) != NULL)
8427 		return -1;
8428 
8429 	// try and open the file to verify
8430 	gr_stuff_first_font(first_font, sizeof(first_font));
8431 	CFILE *detect = cfopen(first_font, "rb");
8432 
8433 	// will use default setting if something went wrong
8434 	if (!detect)
8435 		return -1;
8436 
8437 	// get the long checksum of the file
8438 	file_checksum = 0;
8439 	cfseek(detect, 0, SEEK_SET);
8440 	cf_chksum_long(detect, &file_checksum);
8441 	cfclose(detect);
8442 	detect = NULL;
8443 
8444 	// now compare the checksum/filesize against known #'s
8445 	for (idx=0; idx < (int)Lcl_languages.size(); idx++) {
8446 		if (Lcl_languages[idx].checksum == (int)file_checksum) {
8447 			mprintf(( "AutoLang: Language auto-detection successful...\n" ));
8448 			return idx;
8449 		}
8450 	}
8451 
8452 	// notify if a match was not found, include detected checksum
8453 	mprintf(( "ERROR: Unknown Language Checksum: %i\n", (int)file_checksum ));
8454 	mprintf(( "Using default language settings...\n" ));
8455 
8456 	return -1;
8457 }
8458 
8459 //
8460 // Eng Auto Lang stuff
8461 // ----------------------------------------------------------------
8462 
8463 
8464 // ----------------------------------------------------------------
8465 // SHIPS TBL VERIFICATION STUFF
8466 //
8467 
8468 // checksums, just keep a list of all valid ones, if it matches any of them, keep it
8469 #define NUM_SHIPS_TBL_CHECKSUMS		1
8470 
8471 int Game_ships_tbl_checksums[NUM_SHIPS_TBL_CHECKSUMS] = {
8472 //	-1022810006,					// 1.0 FULL
8473 	-1254285366						// 1.2 FULL (German)
8474 };
8475 
8476 void verify_ships_tbl()
8477 {
8478 	/*
8479 #ifdef NDEBUG
8480 	Game_ships_tbl_valid = 1;
8481 #else
8482 	*/
8483 	uint file_checksum;
8484 	int idx;
8485 
8486 	// detect if the packfile exists
8487 	CFILE *detect = cfopen("ships.tbl", "rb");
8488 	Game_ships_tbl_valid = 0;
8489 
8490 	// not mission-disk
8491 	if(!detect){
8492 		Game_ships_tbl_valid = 0;
8493 		return;
8494 	}
8495 
8496 	// get the long checksum of the file
8497 	file_checksum = 0;
8498 	cfseek(detect, 0, SEEK_SET);
8499 	cf_chksum_long(detect, &file_checksum);
8500 	cfclose(detect);
8501 	detect = NULL;
8502 
8503 	// now compare the checksum/filesize against known #'s
8504 	for(idx=0; idx<NUM_SHIPS_TBL_CHECKSUMS; idx++){
8505 		if(Game_ships_tbl_checksums[idx] == (int)file_checksum){
8506 			Game_ships_tbl_valid = 1;
8507 			return;
8508 		}
8509 	}
8510 // #endif
8511 }
8512 
8513 DCF(shipspew, "display the checksum for the current ships.tbl")
8514 {
8515 	uint file_checksum;
8516 	CFILE *detect = cfopen("ships.tbl", "rb");
8517 	// get the long checksum of the file
8518 	file_checksum = 0;
8519 	cfseek(detect, 0, SEEK_SET);
8520 	cf_chksum_long(detect, &file_checksum);
8521 	cfclose(detect);
8522 
8523 	dc_printf("%d", file_checksum);
8524 }
8525 
8526 // ----------------------------------------------------------------
8527 // WEAPONS TBL VERIFICATION STUFF
8528 //
8529 
8530 // checksums, just keep a list of all valid ones, if it matches any of them, keep it
8531 #define NUM_WEAPONS_TBL_CHECKSUMS		1
8532 
8533 int Game_weapons_tbl_checksums[NUM_WEAPONS_TBL_CHECKSUMS] = {
8534 //	399297860,				// 1.0 FULL
8535 	-553984927				// 1.2 FULL (german)
8536 };
8537 
8538 void verify_weapons_tbl()
8539 {
8540 	/*
8541 #ifdef NDEBUG
8542 	Game_weapons_tbl_valid = 1;
8543 #else
8544 	*/
8545 	uint file_checksum;
8546 	int idx;
8547 
8548 	// detect if the packfile exists
8549 	CFILE *detect = cfopen("weapons.tbl", "rb");
8550 	Game_weapons_tbl_valid = 0;
8551 
8552 	// not mission-disk
8553 	if(!detect){
8554 		Game_weapons_tbl_valid = 0;
8555 		return;
8556 	}
8557 
8558 	// get the long checksum of the file
8559 	file_checksum = 0;
8560 	cfseek(detect, 0, SEEK_SET);
8561 	cf_chksum_long(detect, &file_checksum);
8562 	cfclose(detect);
8563 	detect = NULL;
8564 
8565 	// now compare the checksum/filesize against known #'s
8566 	for(idx=0; idx<NUM_WEAPONS_TBL_CHECKSUMS; idx++){
8567 		if(Game_weapons_tbl_checksums[idx] == (int)file_checksum){
8568 			Game_weapons_tbl_valid = 1;
8569 			return;
8570 		}
8571 	}
8572 // #endif
8573 }
8574 
8575 DCF(wepspew, "display the checksum for the current weapons.tbl")
8576 {
8577 	uint file_checksum;
8578 	CFILE *detect = cfopen("weapons.tbl", "rb");
8579 	// get the long checksum of the file
8580 	file_checksum = 0;
8581 	cfseek(detect, 0, SEEK_SET);
8582 	cf_chksum_long(detect, &file_checksum);
8583 	cfclose(detect);
8584 
8585 	dc_printf("%d", file_checksum);
8586 }
8587 
8588 // if the game is running using hacked data
8589 static bool Hacked_data_check_ready = false;
8590 static bool Hacked_data = false;
8591 
8592 int game_hacked_data()
8593 {
8594 	int rc = 0;
8595 
8596 	if (Hacked_data) {
8597 		return 1;
8598 	}
8599 
8600 
8601 	if ( Om_tracker_flag && !(Hacked_data_check_ready) ) {
8602 		// this may fail the first time or two
8603 		if ( (rc = fs2netd_update_valid_tables()) != -1 ) {
8604 			Hacked_data = (rc != 0);
8605 			Hacked_data_check_ready = true;
8606 		}
8607 	}
8608 
8609 	// LAN game, only check if weapon and ship are valid since we can't or don't
8610 	// want to validate against PXO server
8611 	if ( !Om_tracker_flag && !(Game_weapons_tbl_valid && Game_ships_tbl_valid) ) {
8612 		Hacked_data = true;
8613 	}
8614 
8615 
8616 	return (int)Hacked_data;
8617 }
8618 
8619 
8620 //#define MAX_SPLASHSCREENS 64
8621 //char Splash_screens[MAX_SPLASHSCREENS][MAX_FILENAME_LEN];
8622 
8623 
8624 void game_title_screen_display()
8625 {
8626 /*	_finddata_t find;
8627 	long		find_handle;
8628 	char current_dir[256];
8629 
8630 	//Get the find string
8631 	_getcwd(current_dir, 256);
8632 	strcat_s(current_dir, DIR_SEPARATOR_STR);
8633 	strcat_s(current_dir, "*.pcx");
8634 
8635 	//Let the search begin!
8636 	find_handle = _findfirst(current_dir, &find);
8637 	int i = 0;
8638 	if(find_handle != -1)
8639 	{
8640 		char *p;
8641 
8642 		do {
8643 			if(!(find.attrib & _A_SUBDIR) && (strlen(find.name) < MAX_FILENAME_LEN)) {
8644 				p = strchr( find.name, '.' );
8645 				if(p) {
8646 					*p = '\0';
8647 				}
8648 
8649 				if(stricmp(find.name, Game_logo_screen_fname[gr_screen.res])
8650 					&& stricmp(find.name, Game_title_screen_fname[gr_screen.res]))
8651 				{
8652 					strcpy_s(Splash_screens[i], find.name);
8653 					i++;
8654 				}
8655 
8656 				if(i == MAX_SPLASHSCREENS) {
8657 					break;
8658 				}
8659 			}
8660 		} while(!_findnext(find_handle, &find));
8661 	}
8662 
8663 	if(i) {
8664 		srand(time(NULL));
8665 		title_bitmap = bm_load(Splash_screens[rand() % i]);
8666 
8667 	} else {
8668 		title_bitmap = bm_load(Game_title_screen_fname[gr_screen.res]);
8669 	}
8670 
8671 	if (title_bitmap == -1 && title_logo == -1) {
8672 //		return;
8673 	}
8674 	*/
8675 
8676 	//Script_system.SetHookVar("SplashScreenImage", 's', Game_title_screen_fname[gr_screen.res]);
8677 	//Script_system.SetHookVar("SplashScreenLogo", 's', Game_logo_screen_fname[gr_screen.res]);
8678 	bool globalhook_override = Script_system.IsOverride(Script_splashhook);
8679 	bool condhook_override = Script_system.IsConditionOverride(CHA_SPLASHSCREEN);
8680 	mprintf(("SCRIPTING: Splash screen overrides checked\n"));
8681 	if(!globalhook_override && !condhook_override)
8682 	{
8683 		Game_title_logo = bm_load(Game_logo_screen_fname[gr_screen.res]);
8684 		Game_title_bitmap = bm_load(Game_title_screen_fname[gr_screen.res]);
8685 
8686 		if (Game_title_bitmap != -1)
8687 		{
8688 			// set
8689 			gr_set_bitmap(Game_title_bitmap);
8690 
8691 			// get bitmap's width and height
8692 			int width, height;
8693 			bm_get_info(Game_title_bitmap, &width, &height);
8694 
8695 			// draw it in the center of the screen
8696 			gr_bitmap((gr_screen.max_w_unscaled - width)/2, (gr_screen.max_h_unscaled - height)/2, GR_RESIZE_MENU);
8697 		}
8698 
8699 		if (Game_title_logo != -1)
8700 		{
8701 			gr_set_bitmap(Game_title_logo);
8702 
8703 			gr_bitmap(0, 0, GR_RESIZE_MENU);
8704 
8705 		}
8706 	}
8707 
8708 	if(!condhook_override)
8709 		Script_system.RunBytecode(Script_splashhook);
8710 
8711 	mprintf(("SCRIPTING: Splash hook has been run\n"));
8712 
8713 	if(!globalhook_override || condhook_override)
8714 		Script_system.RunCondition(CHA_SPLASHSCREEN);
8715 
8716 	mprintf(("SCRIPTING: Splash screen conditional hook has been run\n"));
8717 
8718 	Script_system.RemHookVars(2, "SplashScreenImage", "SplashScreenLogo");
8719 
8720 	// flip
8721 	gr_flip();
8722 }
8723 
8724 void game_title_screen_close()
8725 {
8726 	if (Game_title_bitmap != -1) {
8727 		bm_release(Game_title_bitmap);
8728 		Game_title_bitmap = -1;
8729 	}
8730 
8731 	if (Game_title_logo != -1) {
8732 		bm_release(Game_title_logo);
8733 		Game_title_bitmap = -1;
8734 	}
8735 }
8736 
8737 // return true if the game is running with "low memory", which is less than 48MB
8738 bool game_using_low_mem()
8739 {
8740 	if (Use_low_mem == 0) {
8741 		return false;
8742 	} else {
8743 		return true;
8744 	}
8745 }
8746 
8747 //  place calls here that need to take effect immediately when the game is
8748 //  minimized.  Called from osapi.cpp
8749 void game_pause()
8750 {
8751 	// Protection against flipping out -- Kazan
8752 	if (!GameState_Stack_Valid())
8753 		return;
8754 
8755 	if (!(Game_mode & GM_MULTIPLAYER)){
8756 		switch ( gameseq_get_state() )
8757 		{
8758 			case GS_STATE_MAIN_MENU:
8759 				main_hall_pause(); // not an instant shutoff of misc anims and sounds
8760 				break;
8761 
8762 			case GS_STATE_BRIEFING:
8763 				brief_pause();
8764 				break;
8765 
8766 			case GS_STATE_DEBRIEF:
8767 				debrief_pause();
8768 				break;
8769 
8770 			case GS_STATE_CMD_BRIEF:
8771 				cmd_brief_pause();
8772 				break;
8773 
8774 			case GS_STATE_RED_ALERT:
8775 				red_alert_voice_pause();
8776 				break;
8777 
8778 			// anything that would leave the ambient mainhall sound going
8779 			case GS_STATE_TECH_MENU:
8780 			case GS_STATE_BARRACKS_MENU:
8781 				main_hall_stop_ambient();
8782 				main_hall_stop_music(true); // not an instant shutoff
8783 				break;
8784 
8785 			// things that would get music except if they are called while in-mission
8786 			case GS_STATE_OPTIONS_MENU:
8787 			case GS_STATE_HUD_CONFIG:
8788 				if ( !(Game_mode & GM_IN_MISSION) ) {
8789 					main_hall_stop_ambient();
8790 					main_hall_stop_music(true); // not an instant shutoff
8791 				}
8792 				break;
8793 
8794 			// only has the ambient sound, no music
8795 			case GS_STATE_INITIAL_PLAYER_SELECT:
8796 				main_hall_stop_ambient();
8797 				break;
8798 
8799 			// pause_init is a special case and we don't unpause it ourselves
8800 			case GS_STATE_GAME_PLAY:
8801 				if ( (!popup_active()) && (!popupdead_is_active()) )
8802 					pause_init();
8803 				break;
8804 
8805 			default:
8806 				audiostream_pause_all();
8807 		}
8808 	}
8809 }
8810 
8811 // calls to be executed when the game is restored from minimized or inactive state
8812 void game_unpause()
8813 {
8814 	if (!GameState_Stack_Valid())
8815 		return;
8816 
8817 	// automatically recover from everything but an in-mission pause
8818 	if (!(Game_mode & GM_MULTIPLAYER)) {
8819 		switch ( gameseq_get_state() )
8820 		{
8821 			case GS_STATE_MAIN_MENU:
8822 				main_hall_unpause();
8823 				break;
8824 
8825 			case GS_STATE_BRIEFING:
8826 				brief_unpause();
8827 				break;
8828 
8829 			case GS_STATE_DEBRIEF:
8830 				debrief_unpause();
8831 				break;
8832 
8833 			case GS_STATE_CMD_BRIEF:
8834 				cmd_brief_unpause();
8835 				break;
8836 
8837 			case GS_STATE_RED_ALERT:
8838 				red_alert_voice_unpause();
8839 				break;
8840 
8841 			// anything that would leave the ambient mainhall sound going
8842 			case GS_STATE_TECH_MENU:
8843 			case GS_STATE_BARRACKS_MENU:
8844 				main_hall_start_ambient();
8845 				main_hall_start_music(); // not an instant shutoff
8846 				break;
8847 
8848 			// things that would get music except if they are called while in-mission
8849 			case GS_STATE_OPTIONS_MENU:
8850 			case GS_STATE_HUD_CONFIG:
8851 				if ( !(Game_mode & GM_IN_MISSION) ) {
8852 					main_hall_start_ambient();
8853 					main_hall_start_music(); // not an instant shutoff
8854 				}
8855 				break;
8856 
8857 			// only has the ambient sound, no music
8858 			case GS_STATE_INITIAL_PLAYER_SELECT:
8859 				main_hall_start_ambient();
8860 				break;
8861 
8862 			// if in a game then do nothing, pause_init() should have been called
8863 			// and will get cleaned up elsewhere
8864 			case GS_STATE_GAME_PLAY:
8865 				break;
8866 
8867 			default:
8868 				audiostream_unpause_all();
8869 		}
8870 	}
8871 }
8872