1 /*
2 	INTERFACE.C
3 
4 	Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
5 	and the "Aleph One" developers.
6 
7 	This program is free software; you can redistribute it and/or modify
8 	it under the terms of the GNU General Public License as published by
9 	the Free Software Foundation; either version 3 of the License, or
10 	(at your option) any later version.
11 
12 	This program is distributed in the hope that it will be useful,
13 	but WITHOUT ANY WARRANTY; without even the implied warranty of
14 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 	GNU General Public License for more details.
16 
17 	This license is contained in the file "COPYING",
18 	which is included with this source code; it is available online at
19 	http://www.gnu.org/licenses/gpl.html
20 
21 	Thursday, December 30, 1993 6:56:22 PM
22 	Mac specific code.....
23 
24 	Friday, July 8, 1994 2:32:44 PM (alain)
25 		All old code in here is obsolete. This now has interface for the top-level
26 		interface (Begin Game, etc�)
27 	Saturday, September 10, 1994 12:45:48 AM  (alain)
28 		the interface gutted again. just the stuff that handles the menu though, the rest stayed
29 		the same.
30 	Thursday, June 8, 1995 2:56:16 PM (ryan)
31 		Pillaged, raped, & burned. (in that order)
32 
33 Jan 30, 2000 (Loren Petrich):
34 	Added some typecasts
35 	Removed some "static" declarations that conflict with "extern"
36 	Surrounded choose_saved_game_to_load with "extern "C""
37 
38 Feb. 4, 2000 (Loren Petrich):
39 	Changed halt() to assert(false) for better debugging
40 
41 Feb. 9, 2000 (Loren Petrich):
42 	Changed NUMBER_OF_INTRO_SCREENS to 3
43 	Changed NUMBER_OF_CREDIT_SCREENS to Hamish Sinclair's favorite number
44 
45 	Fixed multiple-clicks-necessary problem for too few screens.
46 	Was in next_game_state(); set game_state.phase (countdown value) to zero.
47 
48 Feb 19, 2000 (Loren Petrich):
49 	Set the single-player color to the player color set in the preferences,
50 	for the benefit of chase-cam users.
51 
52 Mar 5, 2000 (Loren Petrich):
53 	Added reset_screen() when starting a game, so that extravision
54 	will not be persistent.
55 
56 May 13, 2000 (Loren Petrich):
57 	Added Rhys Hill's fix for problems with quitting OpenGL
58 
59 Aug 12, 2000 (Loren Petrich):
60 	Using object-oriented file handler
61 
62 Aug 24, 2000 (Loren Petrich):
63 	Added source selector to calculate_picture_clut(), in order to better deal with
64 	object-oriented file handlers
65 
66 Nov 25, 2000 (Loren Petrich):
67 	Added support for movies that start at any level, including at the end of a game.
68 	Also added end-screen control.
69 
70 Jan 31, 2001 (Loren Petrich):
71 	In pause_game(), will stop the liquid faders that are active
72 
73 Jan 25, 2002 (Br'fin (Jeremy Parsons)):
74 	Disabled network and network microphone calls under Carbon
75 
76 Feb 27, 2002 (Br'fin (Jeremy Parsons)):
77 	Renabled network calls, but not microphone calls under Carbon
78 
79 May 16, 2002 (Woody Zenfell):
80     Enforcing standard player behavior with regard to films and netplay
81 
82 Jun 5, 2002 (Loren Petrich):
83 	Added do-nothing "case _revert_game:" in portable_process_screen_click()
84 	at the request of Michael Adams.
85 
86 Feb 1, 2003 (Woody Zenfell):
87         Reenabling network microphone support on all platforms, trying to share code
88         and present consistent interfaces to the greatest degree practical.
89 
90 Feb 8-12, 2003 (Woody Zenfell):
91         Introducing support for generalized game startup (will enable resumption of saved-games
92         as netgames, among other things).
93 
94 Feb 13, 2003 (Woody Zenfell):
95         We can now resume games as network games.
96 */
97 
98 // NEED VISIBLE FEEDBACK WHEN APPLETALK IS NOT AVAILABLE!!!
99 
100 /* ZZZ: more on enforcing standard behavior...
101     + Standard behavior forced when playing a network game.
102     + Standard behavior forced when replaying a film.
103     + Custom behavior allowed when starting or restoring a single-player game.
104     + No film recorded in single-player if custom behavior != standard behavior.
105 
106     Once films and netplay properly record each player's behavior prefs,
107     and the relevant code uses per-player settings, this won't be necessary.
108     Try a mass-search for "player_behavior" to find the areas affected.
109 */
110 
111 #include "cseries.h" // sorry ryan, nov. 4
112 #include <string.h>
113 #include <stdlib.h>
114 #include <limits.h>
115 #include <algorithm>
116 #include <sstream>
117 
118 #ifdef PERFORMANCE
119 #include <perf.h>
120 
121 extern TP2PerfGlobals perf_globals;
122 #endif
123 
124 #include "map.h"
125 #include "shell.h"
126 #include "interface.h"
127 #include "player.h"
128 #include "network.h"
129 #include "screen_drawing.h"
130 #include "SoundManager.h"
131 #include "fades.h"
132 #include "game_window.h"
133 #include "game_errors.h"
134 #include "Mixer.h"
135 #include "Music.h"
136 #include "images.h"
137 #include "screen.h"
138 #include "network.h"
139 #include "vbl.h"
140 #include "shell.h"
141 #include "preferences.h"
142 #include "FileHandler.h"
143 #include "lua_script.h" // PostIdle
144 #include "interface_menus.h"
145 #include "XML_LevelScript.h"
146 #include "Music.h"
147 #include "Movie.h"
148 #include "QuickSave.h"
149 #include "Plugins.h"
150 #include "Statistics.h"
151 
152 #ifdef HAVE_SMPEG
153 #include <smpeg/smpeg.h>
154 #endif
155 #ifdef HAVE_FFMPEG
156 #include "SDL_ffmpeg.h"
157 #endif
158 
159 #include "sdl_dialogs.h"
160 #include "sdl_widgets.h"
161 #include "network_dialog_widgets_sdl.h"
162 
163 /* Change this when marathon changes & replays are no longer valid */
164 enum recording_version {
165 	RECORDING_VERSION_UNKNOWN = 0,
166 	RECORDING_VERSION_MARATHON = 1,
167 	RECORDING_VERSION_MARATHON_2 = 2,
168 	RECORDING_VERSION_MARATHON_INFINITY = 3,
169 	RECORDING_VERSION_ALEPH_ONE_EARLY = 4,
170 	RECORDING_VERSION_ALEPH_ONE_PRE_NET = 5,
171 	RECORDING_VERSION_ALEPH_ONE_PRE_PIN = 6,
172 	RECORDING_VERSION_ALEPH_ONE_1_0 = 7,
173 	RECORDING_VERSION_ALEPH_ONE_1_1 = 8,
174 	RECORDING_VERSION_ALEPH_ONE_1_2 = 9
175 };
176 const short default_recording_version = RECORDING_VERSION_ALEPH_ONE_1_2;
177 const short max_handled_recording= RECORDING_VERSION_ALEPH_ONE_1_2;
178 
179 #include "screen_definitions.h"
180 #include "interface_menus.h"
181 
182 // LP addition: getting OpenGL rendering stuff
183 #include "render.h"
184 #include "OGL_Render.h"
185 #include "OGL_Blitter.h"
186 #include "alephversion.h"
187 
188 // To tell it to stop playing,
189 // and also to run the end-game script
190 #include "XML_LevelScript.h"
191 
192 // Network microphone/speaker
193 #include "network_sound.h"
194 #include "network_distribution_types.h"
195 
196 // ZZZ: should the function that uses these (join_networked_resume_game()) go elsewhere?
197 #include "wad.h"
198 #include "game_wad.h"
199 
200 #include "motion_sensor.h" // for reset_motion_sensor()
201 
202 #include "lua_hud_script.h"
203 
204 using alephone::Screen;
205 
206 /* ------------- enums */
207 
208 /* ------------- constants */
209 #define DISPLAY_PICT_RESOURCE_TYPE 'PICT'
210 #define CLOSE_WITHOUT_WARNING_DELAY (5*TICKS_PER_SECOND)
211 
212 #define NUMBER_OF_INTRO_SCREENS (3)
213 #define INTRO_SCREEN_DURATION (215 * MACHINE_TICKS_PER_SECOND / TICKS_PER_SECOND) // fudge to align with sound
214 
215 #ifdef DEMO
216 #define INTRO_SCREEN_TO_START_SONG_ON (1)
217 #else
218 #define INTRO_SCREEN_TO_START_SONG_ON (0)
219 #endif
220 
221 #define INTRO_SCREEN_BETWEEN_DEMO_BASE (INTRO_SCREEN_BASE+1) /* +1 to get past the powercomputing */
222 #define NUMBER_OF_INTRO_SCREENS_BETWEEN_DEMOS (1)
223 #define DEMO_INTRO_SCREEN_DURATION (10 * MACHINE_TICKS_PER_SECOND)
224 
225 #define TICKS_UNTIL_DEMO_STARTS (30 * MACHINE_TICKS_PER_SECOND)
226 
227 #define NUMBER_OF_PROLOGUE_SCREENS 0
228 #define PROLOGUE_DURATION (10 * MACHINE_TICKS_PER_SECOND)
229 
230 #define NUMBER_OF_EPILOGUE_SCREENS 1
231 #define EPILOGUE_DURATION (INDEFINATE_TIME_DELAY)
232 
233 #define NUMBER_OF_CREDIT_SCREENS 7
234 #define CREDIT_SCREEN_DURATION (15 * 60 * MACHINE_TICKS_PER_SECOND)
235 
236 #define NUMBER_OF_CHAPTER_HEADINGS 0
237 #define CHAPTER_HEADING_DURATION (7*MACHINE_TICKS_PER_SECOND)
238 
239 // For exiting the Marathon app
240 // #if defined(DEBUG) || !defined(DEMO)
241 #define NUMBER_OF_FINAL_SCREENS 0
242 // #else
243 // #define NUMBER_OF_FINAL_SCREENS 1
244 // #endif
245 #define FINAL_SCREEN_DURATION (INDEFINATE_TIME_DELAY)
246 
247 /* For teleportation, end movie, etc. */
248 #define EPILOGUE_LEVEL_NUMBER 256
249 
250 /* ------------- structures */
251 struct game_state {
252 	short state;
253 	short flags;
254 	short user;
255 	int32 phase;
256 	int32 last_ticks_on_idle;
257 	short current_screen;
258 	bool suppress_background_tasks;
259 	bool current_netgame_allows_microphone;
260 	short main_menu_display_count; // how many times have we shown the main menu?
261 	short highlighted_main_menu_item;
262 };
263 
264 struct screen_data {
265 	short screen_base;
266 	short screen_count;
267 	int32 duration;
268 };
269 
270 /* -------------- constants */
271 struct screen_data display_screens[]= {
272 	{ INTRO_SCREEN_BASE, NUMBER_OF_INTRO_SCREENS, INTRO_SCREEN_DURATION },
273 	{ MAIN_MENU_BASE, 1, 0 },
274 	{ CHAPTER_SCREEN_BASE, NUMBER_OF_CHAPTER_HEADINGS, CHAPTER_HEADING_DURATION },
275 	{ PROLOGUE_SCREEN_BASE, NUMBER_OF_PROLOGUE_SCREENS, PROLOGUE_DURATION },
276 	{ EPILOGUE_SCREEN_BASE, NUMBER_OF_EPILOGUE_SCREENS, EPILOGUE_DURATION },
277 	{ CREDIT_SCREEN_BASE, NUMBER_OF_CREDIT_SCREENS, CREDIT_SCREEN_DURATION},
278 	{ INTRO_SCREEN_BETWEEN_DEMO_BASE, NUMBER_OF_INTRO_SCREENS_BETWEEN_DEMOS, DEMO_INTRO_SCREEN_DURATION },
279 	{ FINAL_SCREEN_BASE, NUMBER_OF_FINAL_SCREENS, FINAL_SCREEN_DURATION }
280 };
281 
282 struct screen_data m1_display_screens[]= {
283 	{ 1111, 4, INTRO_SCREEN_DURATION },
284 	{ MAIN_MENU_BASE, 1, 0 },
285 	{ 10000, NUMBER_OF_CHAPTER_HEADINGS, CHAPTER_HEADING_DURATION },
286 	{ PROLOGUE_SCREEN_BASE, NUMBER_OF_PROLOGUE_SCREENS, PROLOGUE_DURATION },
287 	{ EPILOGUE_SCREEN_BASE, NUMBER_OF_EPILOGUE_SCREENS, EPILOGUE_DURATION },
288 	{ 1000, 1, CREDIT_SCREEN_DURATION},
289 	{ INTRO_SCREEN_BETWEEN_DEMO_BASE, NUMBER_OF_INTRO_SCREENS_BETWEEN_DEMOS, DEMO_INTRO_SCREEN_DURATION },
290 	{ FINAL_SCREEN_BASE, NUMBER_OF_FINAL_SCREENS, FINAL_SCREEN_DURATION }
291 };
292 
293 
294 
295 /* -------------- local globals */
296 static struct game_state game_state;
297 static FileSpecifier DraggedReplayFile;
298 static bool interface_fade_in_progress= false;
299 static short interface_fade_type;
300 static short current_picture_clut_depth;
301 static struct color_table *animated_color_table= NULL;
302 static struct color_table *current_picture_clut= NULL;
303 
304 /* -------------- externs */
305 extern short interface_bit_depth;
306 extern short bit_depth;
307 extern bool insecure_lua;
308 extern bool shapes_file_is_m1();
309 
310 /* ----------- prototypes/PREPROCESS_MAP_MAC.C */
311 extern bool load_game_from_file(FileSpecifier& File, bool run_scripts, bool *was_map_found);
312 extern bool choose_saved_game_to_load(FileSpecifier& File);
313 
314 /* ---------------------- prototypes */
315 static void display_credits(void);
316 static void draw_button(short index, bool pressed);
317 static void draw_powered_by_aleph_one();
318 static void handle_replay(bool last_replay);
319 static bool begin_game(short user, bool cheat);
320 static void start_game(short user, bool changing_level);
321 // LP: "static" removed
322 void handle_load_game(void);
323 static void handle_save_film(void);
324 static void finish_game(bool return_to_main_menu);
325 static void clean_up_after_failed_game(bool inNetgame, bool inRecording, bool inFullCleanup);
326 static void handle_network_game(bool gatherer);
327 static void next_game_screen(void);
328 static void handle_interface_menu_screen_click(short x, short y, bool cheatkeys_down);
329 
330 static void display_introduction(void);
331 static void display_loading_map_error(void);
332 static void display_quit_screens(void);
333 static void	display_screen(short base_pict_id);
334 static void display_introduction_screen_for_demo(void);
335 static void display_epilogue(void);
336 static void display_about_dialog();
337 
338 static void force_system_colors(void);
339 static bool point_in_rectangle(short x, short y, screen_rectangle *rect);
340 
341 static void start_interface_fade(short type, struct color_table *original_color_table);
342 static void update_interface_fades(void);
343 static void interface_fade_out(short pict_resource_number, bool fade_music);
344 static bool can_interface_fade_out(void);
345 static void transfer_to_new_level(short level_number);
346 static void try_and_display_chapter_screen(short level, bool interface_table_is_valid, bool text_block);
347 
348 static screen_data *get_screen_data(
349 	short index);
350 
351 /* ---------------------- code begins */
352 
get_screen_data(short index)353 screen_data *get_screen_data(
354 	short index)
355 {
356 	assert(index>=0 && index<NUMBER_OF_SCREENS);
357 	if (shapes_file_is_m1())
358 		return m1_display_screens+index;
359 	return display_screens+index;
360 }
361 
initialize_game_state(void)362 void initialize_game_state(
363 	void)
364 {
365 	game_state.state= _display_intro_screens;
366 	game_state.user= _single_player;
367 	game_state.flags= 0;
368 	game_state.current_screen= 0;
369 	game_state.suppress_background_tasks= true;
370 	game_state.main_menu_display_count= 0;
371 
372 	toggle_menus(false);
373 
374 	if(insecure_lua) {
375 	  alert_user(expand_app_variables("Insecure Lua has been manually enabled. Malicious Lua scripts can use Insecure Lua to take over your computer. Unless you specifically trust every single Lua script that will be running, you should quit $appName$ IMMEDIATELY.").c_str());
376 	}
377 
378 	display_introduction();
379 }
380 
force_game_state_change(void)381 void force_game_state_change(
382 	void)
383 {
384 	game_state.phase= 0;
385 }
386 
player_controlling_game(void)387 bool player_controlling_game(
388 	void)
389 {
390 	bool player_in_control= false;
391 
392 	if( (game_state.user==_single_player || game_state.user==_network_player) && (game_state.state==_game_in_progress || game_state.state==_switch_demo) )
393 	{
394 		player_in_control= true;
395 	}
396 
397 	return player_in_control;
398 }
399 
toggle_suppression_of_background_tasks(void)400 void toggle_suppression_of_background_tasks(
401 	void)
402 {
403 	game_state.suppress_background_tasks= !game_state.suppress_background_tasks;
404 }
405 
set_game_state(short new_state)406 void set_game_state(
407 	short new_state)
408 {
409 	short old_state= game_state.state;
410 
411 	switch(old_state)
412 	{
413 		case _game_in_progress:
414 			switch(new_state)
415 			{
416 				case _display_epilogue:
417 					game_state.state= _begin_display_of_epilogue;
418 					game_state.phase= 0;
419 					break;
420 
421 				case _close_game:
422 					finish_game(true);
423 					break;
424 
425 				case _quit_game:
426 					finish_game(false);
427 					display_quit_screens();
428 					break;
429 
430 				case _switch_demo:
431 					/* Because Alain's code calls us at interrupt level 1, */
432 					/*  we must defer processing of this message until idle */
433 					game_state.state= _switch_demo;
434 					game_state.phase= 0;
435 					break;
436 
437 				case _revert_game:
438 					/* Because reverting a game in the middle of the update_world loop sounds */
439 					/*  sketchy, this is not done until idle time.. */
440 					game_state.state= new_state;
441 					game_state.phase= 0;
442 					break;
443 
444 				case _change_level:
445 					game_state.state= new_state;
446 					game_state.phase= 0;
447 					break;
448 
449 				default:
450 					assert(false);
451 					break;
452 			}
453 			break;
454 
455 		default:
456 			game_state.state= new_state;
457 			break;
458 	}
459 }
460 
get_game_state(void)461 short get_game_state(
462 	void)
463 {
464 	return game_state.state;
465 }
466 
current_netgame_allows_microphone()467 bool current_netgame_allows_microphone()
468 {
469 	return game_state.current_netgame_allows_microphone;
470 }
471 
suppress_background_events(void)472 bool suppress_background_events(
473 	void)
474 {
475 	return game_state.suppress_background_tasks;
476 }
477 
get_game_controller(void)478 short get_game_controller(
479 	void)
480 {
481 	return game_state.user;
482 }
483 
set_change_level_destination(short level_number)484 void set_change_level_destination(
485 	short level_number)
486 {
487 	assert(game_state.state== _change_level);
488 	game_state.current_screen= level_number;
489 }
490 
get_difficulty_level(void)491 static short get_difficulty_level(void)
492 {
493 	return player_preferences->difficulty_level;
494 }
495 
496 
497 // ----- ZZZ start support for generalized game startup -----
498 // (should this be split out (with some other game startup code) to a new file?)
499 
500 // In this scheme, a "start" corresponds to a player available at the moment, who will
501 // participate in the game we're starting up.  In general when this code talks about a
502 // 'player', it is referring to an already-existing player in the game world (which should
503 // already be restored or initialized fairly well before these routines are used).
504 // Generalized game startup will match starts to existing players, set leftover players
505 // as "zombies" so they exist but just stand there, and create new players to correspond
506 // with any remaining starts.  As such it can handle both resume-game (some players already
507 // exist) and new-game (dynamic_world->player_count == 0) operations.
508 
construct_single_player_start(player_start_data * outStartArray,short * outStartCount)509 static void construct_single_player_start(player_start_data* outStartArray, short* outStartCount)
510 {
511         if(outStartCount != NULL)
512         {
513                 *outStartCount = 1;
514         }
515 
516         outStartArray[0].team = player_preferences->color;
517         outStartArray[0].color = player_preferences->color;
518         outStartArray[0].identifier = 0;
519         strncpy(outStartArray[0].name, player_preferences->name, MAXIMUM_PLAYER_START_NAME_LENGTH+1);
520 
521         set_player_start_doesnt_auto_switch_weapons_status(&outStartArray[0], dont_switch_to_new_weapon());
522 }
523 
524 #if !defined(DISABLE_NETWORKING)
construct_multiplayer_starts(player_start_data * outStartArray,short * outStartCount)525 static void construct_multiplayer_starts(player_start_data* outStartArray, short* outStartCount)
526 {
527         int number_of_players = NetGetNumberOfPlayers();
528 
529         if(outStartCount != NULL)
530         {
531                 *outStartCount = number_of_players;
532         }
533 
534         for(int player_index= 0; player_index<number_of_players; ++player_index)
535         {
536                 player_info *player_information = (player_info *)NetGetPlayerData(player_index);
537                 outStartArray[player_index].team = player_information->team;
538                 outStartArray[player_index].color= player_information->color;
539                 outStartArray[player_index].identifier = NetGetPlayerIdentifier(player_index);
540                 strncpy(outStartArray[player_index].name, player_information->name, MAXIMUM_PLAYER_START_NAME_LENGTH+1);
541         }
542 }
543 #endif // !defined(DISABLE_NETWORKING)
544 
545 // This should be safe to use whether starting or resuming and whether single-player or multiplayer.
match_starts_with_existing_players(player_start_data * ioStartArray,short * ioStartCount)546 void match_starts_with_existing_players(player_start_data* ioStartArray, short* ioStartCount)
547 {
548         // This code could be smarter, but it doesn't run very often, doesn't get big data sets, etc.
549         // so I'm not going to worry about it.
550 
551         bool startAssigned[MAXIMUM_NUMBER_OF_PLAYERS];
552         int8 startAssignedToPlayer[MAXIMUM_NUMBER_OF_PLAYERS];
553         for(int i = 0; i < MAXIMUM_NUMBER_OF_PLAYERS; i++)
554         {
555                 startAssigned[i] = false;
556                 startAssignedToPlayer[i] = NONE;
557         }
558 
559         // First, match starts to players by name.
560         for(int s = 0; s < *ioStartCount; s++)
561         {
562                 for(int p = 0; p < dynamic_world->player_count; p++)
563                 {
564                         if(startAssignedToPlayer[p] == NONE)
565                         {
566                                 if(strcmp(ioStartArray[s].name, get_player_data(p)->name) == 0)
567                                 {
568                                         startAssignedToPlayer[p] = s;
569                                         startAssigned[s] = true;
570                                         break;
571                                 }
572                         }
573                 }
574         }
575 
576         // Match remaining starts to remaining players arbitrarily.
577         for(int s = 0; s < *ioStartCount; s++)
578         {
579                 if(!startAssigned[s])
580                 {
581                         for(int p = 0; p < dynamic_world->player_count; p++)
582                         {
583                                 if(startAssignedToPlayer[p] == NONE)
584                                 {
585                                         startAssignedToPlayer[p] = s;
586                                         startAssigned[s] = true;
587                                         break;
588                                 }
589                         }
590                 }
591         }
592 
593         // Create new starts for any players not covered.
594         int p = 0;
595         while(*ioStartCount < dynamic_world->player_count)
596         {
597                 if(startAssignedToPlayer[p] == NONE)
598                 {
599                         player_data* thePlayer = get_player_data(p);
600                         ioStartArray[*ioStartCount].team = thePlayer->team;
601                         ioStartArray[*ioStartCount].color = thePlayer->color;
602                         ioStartArray[*ioStartCount].identifier = NONE;
603                         strncpy(ioStartArray[*ioStartCount].name, thePlayer->name, MAXIMUM_PLAYER_START_NAME_LENGTH+1);
604                         startAssignedToPlayer[p] = *ioStartCount;
605                         startAssigned[*ioStartCount] = true;
606                         (*ioStartCount)++;
607                 }
608 
609                 p++;
610         }
611 
612         // Assign remaining starts to players that don't exist yet
613         p = dynamic_world->player_count;
614         for(int s = 0; s < *ioStartCount; s++)
615         {
616                 if(!startAssigned[s])
617                 {
618                         startAssignedToPlayer[p] = s;
619                         startAssigned[s] = true;
620                         p++;
621                 }
622         }
623 
624         // Reorder starts to match players - this is particularly unclever
625         player_start_data theOriginalStarts[MAXIMUM_NUMBER_OF_PLAYERS];
626         memcpy(theOriginalStarts, ioStartArray, sizeof(theOriginalStarts));
627         for(p = 0; p < *ioStartCount; p++)
628         {
629                 ioStartArray[p] = theOriginalStarts[startAssignedToPlayer[p]];
630         }
631 }
632 
633 // This should be safe to use whether starting or resuming, and whether single- or multiplayer.
synchronize_players_with_starts(const player_start_data * inStartArray,short inStartCount,short inLocalPlayerIndex)634 static void synchronize_players_with_starts(const player_start_data* inStartArray, short inStartCount, short inLocalPlayerIndex)
635 {
636         assert(inLocalPlayerIndex >= 0 && inLocalPlayerIndex < inStartCount);
637 
638         // s will walk through all the starts
639         int s = 0;
640 
641         // First we process existing players
642         for( ; s < dynamic_world->player_count; s++)
643         {
644                 player_data* thePlayer = get_player_data(s);
645 
646                 if(inStartArray[s].identifier == NONE)
647                 {
648                         // No start (live player) to go with this player (stored player)
649                         SET_PLAYER_ZOMBIE_STATUS(thePlayer, true);
650                 }
651                 else
652                 {
653                         // Update player's appearance to match the start
654                         thePlayer->team = inStartArray[s].team;
655                         thePlayer->color = inStartArray[s].color;
656                         thePlayer->identifier = player_identifier_value(inStartArray[s].identifier);
657                         strncpy(thePlayer->name, inStartArray[s].name, MAXIMUM_PLAYER_NAME_LENGTH+1);
658 
659                         SET_PLAYER_DOESNT_AUTO_SWITCH_WEAPONS_STATUS(thePlayer,
660                             player_identifier_doesnt_auto_switch_weapons(inStartArray[s].identifier));
661 
662                         // Make sure if player was saved as zombie, they're not now.
663                         SET_PLAYER_ZOMBIE_STATUS(thePlayer, false);
664                 }
665         }
666 
667         // Designate the local player if they already exist
668         if (inLocalPlayerIndex < s)
669         {
670             set_local_player_index(inLocalPlayerIndex);
671             set_current_player_index(inLocalPlayerIndex);
672         }
673 
674         // If there are any starts left, we need new players for them
675         for( ; s < inStartCount; s++)
676         {
677                 new_player_flags flags = (s == inLocalPlayerIndex ? new_player_make_local_and_current : 0);
678                 int theIndex = new_player(inStartArray[s].team, inStartArray[s].color, inStartArray[s].identifier, flags);
679                 assert(theIndex == s);
680                 player_data* thePlayer = get_player_data(theIndex);
681                 strncpy(thePlayer->name, inStartArray[s].name, MAXIMUM_PLAYER_NAME_LENGTH+1);
682         }
683 }
684 
find_start_for_identifier(const player_start_data * inStartArray,short inStartCount,short _inIdentifier)685 static short find_start_for_identifier(const player_start_data* inStartArray, short inStartCount, short _inIdentifier)
686 {
687         short inIdentifier= player_identifier_value(_inIdentifier);
688         short s;
689         for(s = 0; s < inStartCount; s++)
690         {
691                 if(player_identifier_value(inStartArray[s].identifier) == inIdentifier)
692                 {
693                         break;
694                 }
695         }
696 
697         return (s == inStartCount) ? NONE : s;
698 }
699 
700 // The single-player machine, gatherer, and joiners all will use this routine.  It should take most of its
701 // cues from the "extras" that load_game_from_file() does.
make_restored_game_relevant(bool inNetgame,const player_start_data * inStartArray,short inStartCount)702 static bool make_restored_game_relevant(bool inNetgame, const player_start_data* inStartArray, short inStartCount)
703 {
704         game_is_networked = inNetgame;
705 
706         // set_random_seed() needs to happen before synchronize_players_with_starts()
707         // since the latter calls new_player() which almost certainly uses global_random().
708         // Note we always take the random seed directly from the dynamic_world, no need to screw around
709         // with copying it from game_information or the like.
710         set_random_seed(dynamic_world->random_seed);
711 
712         short theLocalPlayerIndex;
713 
714 #if !defined(DISABLE_NETWORKING)
715         // Much of the code in this if()...else is very similar to code in begin_game(), should probably try to share.
716         if(inNetgame)
717         {
718                 game_info *network_game_info= (game_info *)NetGetGameData();
719 
720                 dynamic_world->game_information.game_time_remaining= network_game_info->time_limit;
721                 dynamic_world->game_information.kill_limit= network_game_info->kill_limit;
722                 dynamic_world->game_information.game_type= network_game_info->net_game_type;
723                 dynamic_world->game_information.game_options= network_game_info->game_options;
724                 dynamic_world->game_information.initial_random_seed= network_game_info->initial_random_seed;
725                 dynamic_world->game_information.difficulty_level= network_game_info->difficulty_level;
726                 dynamic_world->game_information.cheat_flags= network_game_info->cheat_flags;
727 
728                 if (network_game_info->allow_mic)
729                 {
730                         install_network_microphone();
731                         game_state.current_netgame_allows_microphone= true;
732                 } else {
733                         game_state.current_netgame_allows_microphone= false;
734                 }
735 
736                 // ZZZ: until players specify their behavior modifiers over the network,
737                 // to avoid out-of-sync we must force them all the same.
738                 standardize_player_behavior_modifiers();
739 
740                 theLocalPlayerIndex = NetGetLocalPlayerIndex();
741         }
742         else
743 #endif // !defined(DISABLE_NETWORKING)
744         {
745                 dynamic_world->game_information.difficulty_level= get_difficulty_level();
746                 restore_custom_player_behavior_modifiers();
747                 theLocalPlayerIndex = find_start_for_identifier(inStartArray, inStartCount, 0);
748         }
749 
750         assert(theLocalPlayerIndex != NONE);
751 
752         synchronize_players_with_starts(inStartArray, inStartCount, theLocalPlayerIndex);
753 
754         bool success = entering_map(true /*restoring game*/);
755 
756         reset_motion_sensor(theLocalPlayerIndex);
757 
758         if(!success) clean_up_after_failed_game(inNetgame, false /*recording*/, true /*full cleanup*/);
759 
760         return success;
761 }
762 
763 // ZZZ end generalized game startup support -----
764 
765 #if !defined(DISABLE_NETWORKING)
766 // ZZZ: this will get called (eventually) shortly after NetUpdateJoinState() returns netStartingResumeGame
join_networked_resume_game()767 bool join_networked_resume_game()
768 {
769         bool success = true;
770 
771         // Get the saved-game data
772         byte* theSavedGameFlatData = NetReceiveGameData(false /* do_physics */);
773         if(theSavedGameFlatData == NULL)
774         {
775                 success = false;
776         }
777 
778         if(success)
779         {
780                 // Use the saved-game data
781                 wad_header theWadHeader;
782                 wad_data* theWad;
783 
784                 theWad = inflate_flat_data(theSavedGameFlatData, &theWadHeader);
785                 if(theWad == NULL)
786                 {
787                         success = false;
788                         free(theSavedGameFlatData);
789                 }
790 
791                 if(success)
792                 {
793                         success = process_map_wad(theWad, true /* resuming */, theWadHeader.data_version);
794                         free_wad(theWad); /* Note that the flat data points into the wad. */
795                         // ZZZ: maybe this is what the Bungie comment meant, but apparently
796                         // free_wad() somehow (voodoo) frees theSavedGameFlatData as well.
797                 }
798 
799                 if(success)
800                 {
801                         Plugins::instance()->set_mode(Plugins::kMode_Net);
802                         Crosshairs_SetActive(player_preferences->crosshairs_active);
803                         LoadHUDLua();
804                         RunLuaHUDScript();
805 
806                         // try to locate the Map file for the saved-game, so that (1) we have a crack
807                         // at continuing the game if the original gatherer disappears, and (2) we can
808                         // save the game on our own machine and continue it properly (as part of a bigger scenario) later.
809                         uint32 theParentChecksum = theWadHeader.parent_checksum;
810                         if(use_map_file(theParentChecksum))
811                         {
812                                 // LP: getting the level scripting off of the map file
813                                 // Being careful to carry over errors so that Pfhortran errors can be ignored
814                                 short SavedType, SavedError = get_game_error(&SavedType);
815                                 RunLevelScript(dynamic_world->current_level_number);
816 				RunScriptChunks();
817 				LoadStatsLua();
818                                 set_game_error(SavedType,SavedError);
819                         }
820                         else
821                         {
822                                 /* Tell the user they�re screwed when they try to leave this level. */
823                                 // ZZZ: should really issue a different warning since the ramifications are different
824                                 alert_user(infoError, strERRORS, cantFindMap, 0);
825 
826                                 // LP addition: makes the game look normal
827                                 hide_cursor();
828 
829                                 /* Set to the default map. */
830                                 set_to_default_map();
831 
832 				ResetLevelScript();
833 				RunScriptChunks();
834 				LoadStatsLua();
835                         }
836 
837                         // set the revert-game info to defaults (for full-auto saving on the local machine)
838                         set_saved_game_name_to_default();
839 
840                         player_start_data theStarts[MAXIMUM_NUMBER_OF_PLAYERS];
841                         short theStartCount;
842                         construct_multiplayer_starts(theStarts, &theStartCount);
843 
844 			RunLuaScript();
845                         success = make_restored_game_relevant(true /* multiplayer */, theStarts, theStartCount);
846                 }
847         }
848 
849         if(success)
850 	{
851 		Music::instance()->PreloadLevelMusic();
852 		start_game(_network_player, false /*changing level?*/);
853 	}
854 
855         return success;
856 }
857 #endif // !defined(DISABLE_NETWORKING)
858 
859 extern bool load_and_start_game(FileSpecifier& File);
860 
861 // ZZZ: changes to use generalized game startup support
862 // This will be used only on the machine that picked "Continue Saved Game".
load_and_start_game(FileSpecifier & File)863 bool load_and_start_game(FileSpecifier& File)
864 {
865 	bool success;
866 
867 	hide_cursor();
868 	if (can_interface_fade_out())
869 	{
870 		interface_fade_out(MAIN_MENU_BASE, true);
871 	}
872 
873 	// run scripts after we decide single vs. multiplayer
874 	bool found_map;
875 	success= load_game_from_file(File, false, &found_map);
876 
877 	if (!success)
878 	{
879 		/* Reset the system colors, since the screen clut is all black.. */
880 		force_system_colors();
881 		show_cursor(); // JTP: Was hidden by force system colors
882 		display_loading_map_error();
883 	}
884 
885 	bool userWantsMultiplayer;
886 	size_t theResult = UNONE;
887 
888 	if (success)
889 	{
890 		theResult = should_restore_game_networked(File);
891 	}
892 
893 	if (theResult == UNONE)
894 	{
895 		// cancelled
896 		success = false;
897 	}
898 	else
899 	{
900 		userWantsMultiplayer = (theResult != 0);
901 	}
902 
903 	if (success)
904 	{
905 #if !defined(DISABLE_NETWORKING)
906 		if (userWantsMultiplayer)
907 		{
908 			set_game_state(_displaying_network_game_dialogs);
909 			success = network_gather(true /*resuming*/);
910 		}
911 #endif // !defined(DISABLE_NETWORKING)
912 
913 		if (success)
914 		{
915 			Plugins::instance()->set_mode(userWantsMultiplayer ? Plugins::kMode_Net : Plugins::kMode_Solo);
916 			Crosshairs_SetActive(player_preferences->crosshairs_active);
917 			LoadHUDLua();
918 			RunLuaHUDScript();
919 
920 			// load the scripts we put off before
921 			short SavedType, SavedError = get_game_error(&SavedType);
922 			if (found_map)
923 			{
924 				RunLevelScript(dynamic_world->current_level_number);
925 			}
926 			else
927 			{
928 				ResetLevelScript();
929 			}
930 			RunScriptChunks();
931 			if (!userWantsMultiplayer)
932 			{
933 				LoadSoloLua();
934 			}
935 			LoadStatsLua();
936 			set_game_error(SavedType,SavedError);
937 
938 			player_start_data theStarts[MAXIMUM_NUMBER_OF_PLAYERS];
939 			short theNumberOfStarts;
940 
941 #if !defined(DISABLE_NETWORKING)
942 			if (userWantsMultiplayer)
943 			{
944 				construct_multiplayer_starts(theStarts, &theNumberOfStarts);
945 			}
946 			else
947 #endif // !defined(DISABLE_NETWORKING)
948 			{
949 				construct_single_player_start(theStarts, &theNumberOfStarts);
950 			}
951 
952 			match_starts_with_existing_players(theStarts, &theNumberOfStarts);
953 
954 #if !defined(DISABLE_NETWORKING)
955 			if (userWantsMultiplayer)
956 			{
957 				NetSetupTopologyFromStarts(theStarts, theNumberOfStarts);
958 				success = NetStart();
959 				if (success)
960 				{
961 					byte* theSavedGameFlatData = (byte*)get_flat_data(File, false /* union wad? */, 0 /* level # */);
962 					if (theSavedGameFlatData == NULL)
963 					{
964 						success = false;
965 					}
966 
967 					if (success)
968 					{
969 						int32 theSavedGameFlatDataLength = get_flat_data_length(theSavedGameFlatData);
970 						OSErr theError = NetDistributeGameDataToAllPlayers(theSavedGameFlatData, theSavedGameFlatDataLength, false /* do_physics? */);
971 						if (theError != noErr)
972 						{
973 							success = false;
974 						}
975 						free(theSavedGameFlatData);
976 					}
977 				}
978 			}
979 #endif // !defined(DISABLE_NETWORKING)
980 
981 			if (success)
982 			{
983 				RunLuaScript();
984 				success = make_restored_game_relevant(userWantsMultiplayer, theStarts, theNumberOfStarts);
985 				if (success)
986 				{
987 					Music::instance()->PreloadLevelMusic();
988 					start_game(userWantsMultiplayer ? _network_player : _single_player, false);
989 				}
990 			}
991 		}
992 	}
993 
994 	if (!success) {
995 		/* We failed.  Balance the cursor */
996 		/* Should this also force the system colors or something? */
997 		show_cursor();
998 	}
999 
1000 	return success;
1001 }
1002 
1003 extern bool handle_open_replay(FileSpecifier& File);
1004 
handle_open_replay(FileSpecifier & File)1005 bool handle_open_replay(FileSpecifier& File)
1006 {
1007 	DraggedReplayFile = File;
1008 
1009 	bool success;
1010 
1011 	force_system_colors();
1012 	success= begin_game(_replay_from_file, false);
1013 	if(!success) display_main_menu();
1014 	return success;
1015 }
1016 
1017 // Called from within update_world..
check_level_change(void)1018 bool check_level_change(
1019 	void)
1020 {
1021 	bool level_changed= false;
1022 
1023 	if(game_state.state==_change_level)
1024 	{
1025 		transfer_to_new_level(game_state.current_screen);
1026 		level_changed= true;
1027 	}
1028 
1029 	return level_changed;
1030 }
1031 
pause_game(void)1032 void pause_game(
1033 	void)
1034 {
1035 	stop_fade();
1036 	if (!OGL_IsActive() || !(TEST_FLAG(Get_OGL_ConfigureData().Flags,OGL_Flag_Fader)))
1037 		set_fade_effect(NONE);
1038 	darken_world_window();
1039 	set_keyboard_controller_status(false);
1040 	show_cursor();
1041 }
1042 
resume_game(void)1043 void resume_game(
1044 	void)
1045 {
1046 	hide_cursor();
1047 	if (!OGL_IsActive() || !(TEST_FLAG(Get_OGL_ConfigureData().Flags,OGL_Flag_Fader)))
1048 		SetFadeEffectDelay(TICKS_PER_SECOND/2);
1049 	if (OGL_IsActive())
1050 		OGL_Blitter::BoundScreen(true);
1051 	validate_world_window();
1052 	set_keyboard_controller_status(true);
1053 }
1054 
draw_menu_button_for_command(short index)1055 void draw_menu_button_for_command(
1056 	short index)
1057 {
1058 	short rectangle_index= index-1+START_OF_MENU_INTERFACE_RECTS;
1059 
1060 	assert(get_game_state()==_display_main_menu);
1061 
1062 	/* Draw it initially depressed.. */
1063 	draw_button(rectangle_index, true);
1064 	SDL_Delay(1000 / 12);
1065 	draw_button(rectangle_index, false);
1066 }
1067 
update_interface_display(void)1068 void update_interface_display(
1069 	void)
1070 {
1071 	struct screen_data *data;
1072 
1073 	data= get_screen_data(game_state.state);
1074 
1075 	/* Use this to avoid the fade.. */
1076 	draw_full_screen_pict_resource_from_images(data->screen_base+game_state.current_screen);
1077 
1078 	if (game_state.state == _display_main_menu)
1079 	{
1080 		draw_powered_by_aleph_one();
1081 		if (game_state.highlighted_main_menu_item >= 0)
1082 		{
1083 			draw_button(game_state.highlighted_main_menu_item + START_OF_MENU_INTERFACE_RECTS - 1, true);
1084 		}
1085 	}
1086 }
1087 
idle_game_state(uint32 time)1088 bool idle_game_state(uint32 time)
1089 {
1090 	int machine_ticks_elapsed = time - game_state.last_ticks_on_idle;
1091 
1092 	if(machine_ticks_elapsed || game_state.phase==0)
1093 	{
1094 		if(game_state.phase != INDEFINATE_TIME_DELAY)
1095 		{
1096 			game_state.phase-= machine_ticks_elapsed;
1097 		}
1098 
1099 		/* Note that we still go through this if we have an indefinate phase.. */
1100 		if(game_state.phase<=0)
1101 		{
1102 			switch(get_game_state())
1103 			{
1104 				case _display_quit_screens:
1105 				case _display_intro_screens:
1106 				case _display_prologue:
1107 				case _display_epilogue:
1108 				case _display_credits:
1109 					next_game_screen();
1110 					break;
1111 
1112 				case _display_intro_screens_for_demo:
1113 				case _display_main_menu:
1114 					/* Start the demo.. */
1115 					if(!begin_game(_demo, false))
1116 					{
1117 						/* This means that there was not a valid demo to play */
1118 						game_state.phase= TICKS_UNTIL_DEMO_STARTS;
1119 					}
1120 					break;
1121 
1122 				case _close_game:
1123 					display_main_menu();
1124 					break;
1125 
1126 				case _switch_demo:
1127 					/* This is deferred to the idle task because it */
1128 					/*  occurs at interrupt time.. */
1129 					switch(game_state.user)
1130 					{
1131 						case _replay:
1132 							finish_game(true);
1133 							break;
1134 
1135 						case _demo:
1136 							finish_game(false);
1137 							display_introduction_screen_for_demo();
1138 							break;
1139 
1140 						default:
1141 							assert(false);
1142 							break;
1143 					}
1144 					break;
1145 
1146 				case _display_chapter_heading:
1147 					dprintf("Chapter heading...");
1148 					break;
1149 
1150 				case _quit_game:
1151 					/* About to quit, but can still hit this through order of ops.. */
1152 					break;
1153 
1154 				case _revert_game:
1155 					/* Reverting while in the update loop sounds sketchy.. */
1156 					if(revert_game())
1157 					{
1158 						game_state.state= _game_in_progress;
1159 						game_state.phase = 15 * MACHINE_TICKS_PER_SECOND;
1160 						game_state.last_ticks_on_idle= machine_tick_count();
1161 						update_interface(NONE);
1162 					} else {
1163 						/* Give them the error... */
1164 						display_loading_map_error();
1165 
1166 						/* And finish their current game.. */
1167 						finish_game(true);
1168 					}
1169 					break;
1170 
1171 				case _begin_display_of_epilogue:
1172 					finish_game(false);
1173 					display_epilogue();
1174 					break;
1175 
1176 				case _game_in_progress:
1177 					game_state.phase = 15 * MACHINE_TICKS_PER_SECOND;
1178 					//game_state.last_ticks_on_idle= machine_tick_count();
1179 					break;
1180 
1181 				case _change_level:
1182 				case _displaying_network_game_dialogs:
1183 					break;
1184 
1185 				default:
1186 					assert(false);
1187 					break;
1188 			}
1189 		}
1190 		game_state.last_ticks_on_idle= machine_tick_count();
1191 	}
1192 
1193 	/* if we�re not paused and there�s something to draw (i.e., anything different from
1194 		last time), render a frame */
1195 	if(game_state.state==_game_in_progress)
1196 	{
1197 		// ZZZ change: update_world() whether or not get_keyboard_controller_status() is true
1198 		// This way we won't fill up queues and stall netgames if one player switches out for a bit.
1199 		std::pair<bool, int16> theUpdateResult= update_world();
1200 		short ticks_elapsed= theUpdateResult.second;
1201 
1202 		if (get_keyboard_controller_status())
1203 		{
1204 			// ZZZ: I don't know for sure that render_screen works best with the number of _real_
1205 			// ticks elapsed rather than the number of (potentially predictive) ticks elapsed.
1206 			// This is a guess.
1207 			if (theUpdateResult.first)
1208 				render_screen(ticks_elapsed);
1209 		}
1210 
1211 		return theUpdateResult.first;
1212 	} else {
1213 		/* Update the fade ins, etc.. */
1214 		update_interface_fades();
1215 		return false;
1216 	}
1217 }
1218 
1219 extern SDL_Surface *draw_surface;	// from screen_drawing.cpp
1220 //void draw_intro_screen(void);		// from screen.cpp
1221 
1222 static SDL_Surface *powered_by_alephone_surface = 0;
1223 #include "powered_by_alephone.h"
1224 
1225 extern void set_about_alephone_rect(int width, int height);
1226 
draw_powered_by_aleph_one()1227 static void draw_powered_by_aleph_one()
1228 {
1229 	if (!powered_by_alephone_surface)
1230 	{
1231 		SDL_RWops *rw = SDL_RWFromConstMem(powered_by_alephone_bmp, sizeof(powered_by_alephone_bmp));
1232 		powered_by_alephone_surface = SDL_LoadBMP_RW(rw, 0);
1233 		SDL_FreeRW(rw);
1234 
1235 		set_about_alephone_rect(powered_by_alephone_surface->w, powered_by_alephone_surface->h);
1236 	}
1237 
1238 	SDL_Rect rect;
1239 	rect.x = 640 - powered_by_alephone_surface->w;
1240 	rect.y = 480 - powered_by_alephone_surface->h;
1241 	rect.w = powered_by_alephone_surface->w;
1242 	rect.h = powered_by_alephone_surface->h;
1243 
1244 	_set_port_to_intro();
1245 	SDL_BlitSurface(powered_by_alephone_surface, NULL, draw_surface, &rect);
1246 	_restore_port();
1247 
1248 	// have to reblit :(
1249 	draw_intro_screen();
1250 }
1251 
display_main_menu(void)1252 void display_main_menu(
1253 	void)
1254 {
1255 	game_state.state= _display_main_menu;
1256 	game_state.current_screen= 0;
1257 	game_state.phase= TICKS_UNTIL_DEMO_STARTS;
1258 	game_state.last_ticks_on_idle= machine_tick_count();
1259 	game_state.user= _single_player;
1260 	game_state.flags= 0;
1261 	game_state.highlighted_main_menu_item= -1;
1262 
1263 	Plugins::instance()->set_mode(Plugins::kMode_Menu);
1264 	change_screen_mode(_screentype_menu);
1265 	display_screen(MAIN_MENU_BASE);
1266 
1267 	/* Start up the song! */
1268 	if(!Music::instance()->Playing() && game_state.main_menu_display_count==0)
1269 	{
1270 		Music::instance()->RestartIntroMusic();
1271 	}
1272 
1273 	draw_powered_by_aleph_one();
1274 
1275 	game_state.main_menu_display_count++;
1276 }
1277 
1278 
1279 // Kludge for Carbon/Classic -- when exiting a main-menu dialog box, redisplay
ForceRepaintMenuDisplay()1280 static void ForceRepaintMenuDisplay()
1281 {
1282 }
1283 
1284 
do_menu_item_command(short menu_id,short menu_item,bool cheat)1285 void do_menu_item_command(
1286 	short menu_id,
1287 	short menu_item,
1288 	bool cheat)
1289 {
1290 	switch(menu_id)
1291 	{
1292 
1293 		case mGame:
1294 			switch(menu_item)
1295 			{
1296 				case iPause:
1297 					switch(game_state.user)
1298 					{
1299 						case _single_player:
1300 						case _replay:
1301 							if (get_keyboard_controller_status())
1302 							{
1303 							  pause_game();
1304 							}
1305 							else
1306 							{
1307 								resume_game();
1308 							}
1309 							break;
1310 
1311 						case _demo:
1312 							finish_game(true);
1313 							break;
1314 
1315 						case _network_player:
1316 							break;
1317 
1318 						default:
1319 							assert(false);
1320 							break;
1321 					}
1322 					break;
1323 
1324 				case iSave:
1325 					switch(game_state.user)
1326 					{
1327 						case _single_player:
1328 #if 0
1329 							save_game();
1330 							validate_world_window();
1331 #endif
1332 							break;
1333 
1334 						case _demo:
1335 						case _replay:
1336 							finish_game(true);
1337 							break;
1338 
1339 						case _network_player:
1340 							break;
1341 
1342 						default:
1343 							assert(false);
1344 							break;
1345 					}
1346 					break;
1347 
1348 				case iRevert:
1349 					/* Not implemented.. */
1350 					break;
1351 
1352 				case iCloseGame:
1353 				case iQuitGame:
1354 					{
1355 						bool really_wants_to_quit= false;
1356 
1357 						switch(game_state.user)
1358 						{
1359 							case _single_player:
1360 								if(PLAYER_IS_DEAD(local_player) ||
1361 									dynamic_world->tick_count-local_player->ticks_at_last_successful_save<CLOSE_WITHOUT_WARNING_DELAY)
1362 								{
1363 									really_wants_to_quit= true;
1364 								} else {
1365 									pause_game();
1366 									show_cursor();
1367 									really_wants_to_quit= quit_without_saving();
1368 									hide_cursor();
1369 									resume_game();
1370 								}
1371 								break;
1372 
1373 							case _demo:
1374 							case _replay:
1375 							case _network_player:
1376 								really_wants_to_quit= true;
1377 								break;
1378 
1379 							default:
1380 								assert(false);
1381 								break;
1382 						}
1383 
1384 						if(really_wants_to_quit)
1385 						{
1386 							// Rhys Hill fix for crash when quitting OpenGL
1387 							if (!OGL_IsActive())
1388 								render_screen(0); /* Get rid of hole.. */
1389 							set_game_state(_close_game);
1390 						}
1391 					}
1392 					break;
1393 
1394 				default:
1395 					assert(false);
1396 					break;
1397 			}
1398 			break;
1399 
1400 		case mInterface:
1401 			switch(menu_item)
1402 			{
1403 				case iNewGame:
1404 					begin_game(_single_player, cheat);
1405 					ForceRepaintMenuDisplay();
1406 					break;
1407 				case iPlaySingletonLevel:
1408 					begin_game(_single_player,2);
1409 					break;
1410 
1411 				case iJoinGame:
1412 					handle_network_game(false);
1413 					break;
1414 
1415 				case iGatherGame:
1416 					handle_network_game(true);
1417 					break;
1418 
1419 				case iLoadGame:
1420 					handle_load_game();
1421 					break;
1422 
1423 				case iReplayLastFilm:
1424 				case iReplaySavedFilm:
1425 					handle_replay(menu_item==iReplayLastFilm);
1426 					break;
1427 
1428 				case iCredits:
1429 					display_credits();
1430 					break;
1431 
1432 				case iPreferences:
1433 					do_preferences();
1434 					game_state.phase= TICKS_UNTIL_DEMO_STARTS;
1435 					game_state.last_ticks_on_idle= machine_tick_count();
1436 					ForceRepaintMenuDisplay();
1437 					break;
1438 
1439 				case iCenterButton:
1440 					SoundManager::instance()->PlaySound(Sound_Center_Button(), 0, NONE);
1441 					break;
1442 
1443 				case iSaveLastFilm:
1444 					handle_save_film();
1445 					break;
1446 
1447 				case iQuit:
1448 					display_quit_screens();
1449 					break;
1450 				case iAbout:
1451 					display_about_dialog();
1452 					game_state.phase= TICKS_UNTIL_DEMO_STARTS;
1453 					game_state.last_ticks_on_idle= machine_tick_count();
1454 					break;
1455 
1456 				default:
1457 					assert(false);
1458 					break;
1459 			}
1460 			break;
1461 
1462 		default:
1463 			assert(false);
1464 			break;
1465 	}
1466 }
1467 
portable_process_screen_click(short x,short y,bool cheatkeys_down)1468 void portable_process_screen_click(
1469 	short x,
1470 	short y,
1471 	bool cheatkeys_down)
1472 {
1473 	switch(get_game_state())
1474 	{
1475 		case _game_in_progress:
1476 		case _begin_display_of_epilogue:
1477 		case _change_level:
1478 		case _displaying_network_game_dialogs:
1479 		case _quit_game:
1480 		case _close_game:
1481 		case _switch_demo:
1482 		case _revert_game:
1483 			break;
1484 
1485 		case _display_intro_screens_for_demo:
1486 			/* Get out of user mode. */
1487 			display_main_menu();
1488 			break;
1489 
1490 		case _display_quit_screens:
1491 		case _display_intro_screens:
1492 		case _display_chapter_heading:
1493 		case _display_prologue:
1494 		case _display_epilogue:
1495 		case _display_credits:
1496 			/* Force the state change next time through.. */
1497 			force_game_state_change();
1498 			break;
1499 
1500 		case _display_main_menu:
1501 			handle_interface_menu_screen_click(x, y, cheatkeys_down);
1502 			break;
1503 
1504 		default:
1505 			assert(false);
1506 			break;
1507 	}
1508 }
1509 
process_main_menu_highlight_advance(bool reverse)1510 void process_main_menu_highlight_advance(bool reverse)
1511 {
1512 	if (get_game_state() != _display_main_menu)
1513 		return;
1514 
1515 	int old_button = game_state.highlighted_main_menu_item;
1516 
1517 	// iterate through M2/Moo order
1518 	int item_order[] = {
1519 		iNewGame, iLoadGame, iGatherGame, iJoinGame,
1520 		iReplaySavedFilm, iReplayLastFilm, iSaveLastFilm,
1521 		iPreferences, iQuit, iCredits, iAbout };
1522 	int num_items = sizeof(item_order)/sizeof(int);
1523 
1524 	if (game_state.highlighted_main_menu_item == -1) {
1525 		game_state.highlighted_main_menu_item = reverse ? item_order[0] : item_order[num_items - 1];
1526 	}
1527 	do {
1528 		int cur_idx = 0;
1529 		for (int i = 0; i < num_items; ++i) {
1530 			if (item_order[i] == game_state.highlighted_main_menu_item) {
1531 				cur_idx = i;
1532 				break;
1533 			}
1534 		}
1535 		int next_idx = (cur_idx + num_items + (reverse ? -1 : 1)) % num_items;
1536 		game_state.highlighted_main_menu_item = item_order[next_idx];
1537 	} while (!enabled_item(game_state.highlighted_main_menu_item));
1538 
1539 	if (old_button != -1)
1540 		draw_button(old_button + START_OF_MENU_INTERFACE_RECTS - 1, false);
1541 	draw_button(game_state.highlighted_main_menu_item + START_OF_MENU_INTERFACE_RECTS - 1, true);
1542 }
1543 
process_main_menu_highlight_select(bool cheatkeys_down)1544 void process_main_menu_highlight_select(bool cheatkeys_down)
1545 {
1546 	if (get_game_state() != _display_main_menu)
1547 		return;
1548 	if (game_state.highlighted_main_menu_item == -1)
1549 		return;
1550 	if (!enabled_item(game_state.highlighted_main_menu_item))
1551 		return;
1552 	do_menu_item_command(mInterface, game_state.highlighted_main_menu_item, cheatkeys_down);
1553 
1554 }
1555 
enabled_item(short item)1556 bool enabled_item(
1557 	short item)
1558 {
1559 	bool enabled= true;
1560 
1561 	switch(item)
1562 	{
1563 		case iNewGame:
1564 		case iLoadGame:
1565 		case iPlaySingletonLevel:
1566 		case iPreferences:
1567 		case iReplaySavedFilm:
1568 		case iCredits:
1569 		case iQuit:
1570 	        case iAbout:
1571 		case iCenterButton:
1572 			break;
1573 
1574 		case iReplayLastFilm:
1575 		case iSaveLastFilm:
1576 			enabled= has_recording_file();
1577 			break;
1578 
1579 		case iGatherGame:
1580 		case iJoinGame:
1581 			enabled= networking_available();
1582 			break;
1583 
1584 		default:
1585 			assert(false);
1586 			break;
1587 	}
1588 
1589 	return enabled;
1590 }
1591 
paint_window_black(void)1592 void paint_window_black(
1593 	void)
1594 {
1595 	_set_port_to_screen_window();
1596 	clear_screen(true);
1597 	_restore_port();
1598 
1599 	_set_port_to_intro();
1600 	SDL_FillRect(draw_surface, NULL, SDL_MapRGB(draw_surface->format, 0, 0, 0));
1601 	_restore_port();
1602 }
1603 
1604 static LoadedResource SoundRsrc;
1605 
1606 /* --------------------- static code */
1607 
display_introduction(void)1608 static void display_introduction(
1609 	void)
1610 {
1611 	struct screen_data *screen_data= get_screen_data(_display_intro_screens);
1612 
1613 	paint_window_black();
1614 	game_state.state= _display_intro_screens;
1615 	game_state.current_screen= 0;
1616 	if (screen_data->screen_count)
1617 	{
1618 		if (game_state.state==_display_intro_screens && game_state.current_screen==INTRO_SCREEN_TO_START_SONG_ON)
1619 		{
1620 			Music::instance()->RestartIntroMusic();
1621 		}
1622 
1623 		game_state.phase= screen_data->duration;
1624 		game_state.last_ticks_on_idle= machine_tick_count();
1625 		display_screen(screen_data->screen_base);
1626 
1627 		Mixer::instance()->StopSoundResource();
1628 		SoundRsrc.Unload();
1629 		if (get_sound_resource_from_images(screen_data->screen_base, SoundRsrc))
1630 		{
1631 			Mixer::instance()->PlaySoundResource(SoundRsrc);
1632 		}
1633 	}
1634 	else
1635 	{
1636 		display_main_menu();
1637 	}
1638 }
1639 
display_introduction_screen_for_demo(void)1640 static void display_introduction_screen_for_demo(
1641 	void)
1642 {
1643 	struct screen_data *screen_data= get_screen_data(_display_intro_screens_for_demo);
1644 
1645 	game_state.state= _display_intro_screens_for_demo;
1646 	game_state.current_screen= 0;
1647 	if(screen_data->screen_count)
1648 	{
1649 		game_state.phase= screen_data->duration;
1650 		game_state.last_ticks_on_idle= machine_tick_count();
1651 		display_screen(screen_data->screen_base);
1652 	} else {
1653 		display_main_menu();
1654 	}
1655 }
1656 
display_epilogue(void)1657 static void display_epilogue(
1658 	void)
1659 {
1660 	Music::instance()->RestartIntroMusic();
1661 
1662 	{
1663 		int32 ticks= machine_tick_count();
1664 
1665 		do
1666 		{
1667 			Music::instance()->Idle();
1668 		}
1669 		while (machine_tick_count()-ticks<10);
1670 	}
1671 
1672 	game_state.state= _display_epilogue;
1673 	game_state.phase= 0;
1674 	game_state.current_screen= 0;
1675 	game_state.last_ticks_on_idle= machine_tick_count();
1676 
1677 	hide_cursor();
1678 	// Setting of the end-screen parameters has been moved to XML_LevelScript.cpp
1679 	int end_offset = EndScreenIndex;
1680 	int end_count = NumEndScreens;
1681 	if (shapes_file_is_m1()) // should check map, but this is easier
1682 	{
1683 		// ignore M2 defaults set in LoadLevelScripts()
1684 		end_offset = 100;
1685 		end_count = 2;
1686 	}
1687 	for (int i=0; i<end_count; i++)
1688 		try_and_display_chapter_screen(end_offset+i, true, true);
1689 	show_cursor();
1690 }
1691 
1692 class w_authors_list : public w_string_list
1693 {
1694 public:
w_authors_list(const vector<string> & items,dialog * d)1695 	w_authors_list(const vector<string>& items, dialog* d) :
1696 		w_string_list(items, d, 0) {}
1697 
item_selected(void)1698 	void item_selected(void) { }
1699 };
1700 
display_about_dialog()1701 static void display_about_dialog()
1702 {
1703 	force_system_colors();
1704 
1705 	dialog d;
1706 
1707 	tab_placer* tabs = new tab_placer();
1708 
1709 	vertical_placer* placer = new vertical_placer;
1710 	std::vector<std::string> labels;
1711 	labels.push_back("ABOUT");
1712 	labels.push_back("AUTHORS");
1713 	w_tab *tab_w = new w_tab(labels, tabs);
1714 
1715 	placer->dual_add(new w_title("ALEPH ONE"), d);
1716 	placer->add(new w_spacer, true);
1717 
1718 	placer->dual_add(tab_w, d);
1719 	placer->add(new w_spacer, true);
1720 
1721 	vertical_placer* about_placer = new vertical_placer;
1722 
1723 	if (strcmp(get_application_name().c_str(), "Aleph One") != 0)
1724 	{
1725 		about_placer->dual_add(new w_static_text(expand_app_variables("$appName$ is powered by").c_str()), d);
1726 	}
1727 	about_placer->dual_add(new w_static_text(expand_app_variables("Aleph One $appVersion$ ($appDate$)").c_str()), d);
1728 
1729 	about_placer->add(new w_spacer, true);
1730 
1731 	about_placer->dual_add(new w_hyperlink(A1_HOMEPAGE_URL), d);
1732 
1733 	about_placer->add(new w_spacer(2 * get_theme_space(SPACER_WIDGET)), true);
1734 
1735 	about_placer->dual_add(new w_static_text(expand_app_variables("Aleph One is free software with ABSOLUTELY NO WARRANTY.").c_str()), d);
1736 	about_placer->dual_add(new w_static_text("You are welcome to redistribute it under certain conditions."), d);
1737 	about_placer->dual_add(new w_hyperlink("http://www.gnu.org/licenses/gpl-3.0.html"), d);
1738 
1739 	about_placer->add(new w_spacer, true);
1740 
1741 	about_placer->dual_add(new w_static_text("This license does not apply to game content."), d);
1742 
1743 	about_placer->add(new w_spacer, true);
1744 
1745 	about_placer->dual_add(new w_static_text(expand_app_variables("Scenario loaded: $scenarioName$ $scenarioVersion$").c_str()), d);
1746 
1747 	vertical_placer *authors_placer = new vertical_placer();
1748 
1749 	authors_placer->dual_add(new w_static_text("Aleph One is based on the source code for Marathon 2 and"), d);
1750 	authors_placer->dual_add(new w_static_text("Marathon Infinity, which was developed by Bungie software."), d);
1751 	authors_placer->add(new w_spacer, true);
1752 
1753 	authors_placer->dual_add(new w_static_text("The enhancements and extensions to Marathon 2 and Marathon"), d);
1754 	authors_placer->dual_add(new w_static_text("Infinity that constitute Aleph One have been made by:"), d);
1755 
1756 	authors_placer->add(new w_spacer, true);
1757 
1758 	std::vector<std::string> authors;
1759 	authors.push_back("Joey Adams");
1760 	authors.push_back("Michael Adams (mdmkolbe)");
1761 	authors.push_back("Falko Axmann");
1762 	authors.push_back("Christian Bauer");
1763 	authors.push_back("Mike Benonis");
1764 	authors.push_back("Steven Bytnar");
1765 	authors.push_back("Glen Ditchfield");
1766 	authors.push_back("Will Dyson");
1767 	authors.push_back("Carl Gherardi");
1768 	authors.push_back("Thomas Herzog");
1769 	authors.push_back("Peter Hessler");
1770 	authors.push_back("Matthew Hielscher");
1771 	authors.push_back("Rhys Hill");
1772 	authors.push_back("Alan Jenkins");
1773 	authors.push_back("Richard Jenkins (Solra Bizna)");
1774 	authors.push_back("Jeremy, the MSVC guy");
1775 	authors.push_back("Mark Levin");
1776 	authors.push_back("Bo Lindbergh");
1777 	authors.push_back("Chris Lovell");
1778 	authors.push_back("Jesse Luehrs");
1779 	authors.push_back("Marshall (darealshinji)");
1780 	authors.push_back("Derek Moeller");
1781 	authors.push_back("Jeremiah Morris");
1782 	authors.push_back("Sam Morris");
1783 	authors.push_back("Benoit Nadeau (Benad)");
1784 	authors.push_back("Mihai Parparita");
1785 	authors.push_back("Jeremy Parsons (brefin)");
1786 	authors.push_back("Eric Peterson");
1787 	authors.push_back("Loren Petrich");
1788 	authors.push_back("Ian Pitcher");
1789 	authors.push_back("Chris Pruett");
1790 	authors.push_back("Matthew Reda");
1791 	authors.push_back("Ian Rickard");
1792 	authors.push_back("Etienne Samson (tiennou)");
1793 	authors.push_back("Catherine Seppanen");
1794 	authors.push_back("Gregory Smith (treellama)");
1795 	authors.push_back("Scott Smith (pickle136)");
1796 	authors.push_back("Wolfgang Sourdeau");
1797 	authors.push_back("Peter Stirling");
1798 	authors.push_back("Alexander Strange (mrvacbob)");
1799 	authors.push_back("Alexei Svitkine");
1800 	authors.push_back("Ben Thompson");
1801 	authors.push_back("Clemens Unterkofler (hogdotmac)");
1802 	authors.push_back("James Willson");
1803 	authors.push_back("Woody Zenfell III");
1804 
1805 	w_authors_list *authors_w = new w_authors_list(authors, &d);
1806 	authors_placer->dual_add(authors_w, d);
1807 
1808 	tabs->add(about_placer, true);
1809 	tabs->add(authors_placer, true);
1810 
1811 	placer->add(tabs, true);
1812 
1813 	placer->add(new w_spacer, true);
1814 
1815 	placer->dual_add(new w_button("OK", dialog_ok, &d), d);
1816 
1817 	d.set_widget_placer(placer);
1818 
1819 	clear_screen();
1820 
1821 	d.run();
1822 
1823 	display_main_menu();
1824 }
1825 
display_credits(void)1826 static void display_credits(
1827 	void)
1828 {
1829 	if (NUMBER_OF_CREDIT_SCREENS)
1830 	{
1831 		struct screen_data *screen_data= get_screen_data(_display_credits);
1832 
1833 		game_state.state= _display_credits;
1834 		game_state.current_screen= 0;
1835 		game_state.user= _single_player;
1836 		game_state.flags= 0;
1837 
1838 		game_state.phase= screen_data->duration;
1839 		game_state.last_ticks_on_idle= machine_tick_count();
1840 		display_screen(screen_data->screen_base);
1841 	}
1842 }
1843 
display_quit_screens(void)1844 static void display_quit_screens(
1845 	void)
1846 {
1847 	struct screen_data *screen_data= get_screen_data(_display_quit_screens);
1848 
1849 	if(screen_data->screen_count)
1850 	{
1851 		game_state.state= _display_quit_screens;
1852 		game_state.current_screen= 0;
1853 		game_state.user= _single_player;
1854 		game_state.flags= 0;
1855 		game_state.phase= screen_data->duration;
1856 		game_state.last_ticks_on_idle= machine_tick_count();
1857 
1858 		display_screen(screen_data->screen_base);
1859 	} else {
1860 		StatsManager::instance()->Finish();
1861 		/* No screens. */
1862 		game_state.state= _quit_game;
1863 		game_state.phase= 0;
1864 	}
1865 }
1866 
transfer_to_new_level(short level_number)1867 static void transfer_to_new_level(
1868 	short level_number)
1869 {
1870 	struct entry_point entry;
1871 	bool success= true;
1872 
1873 	entry.level_number= level_number;
1874 
1875 #if !defined(DISABLE_NETWORKING)
1876 	/* Only can transfer if NetUnSync returns true */
1877 	if(game_is_networked)
1878 	{
1879 		if(NetUnSync())
1880 		{
1881 			success= true;
1882 		} else {
1883 			set_game_error(gameError, errUnsyncOnLevelChange);
1884 			success= false;
1885 		}
1886 	}
1887 #endif // !defined(DISABLE_NETWORKING)
1888 
1889 	if(success)
1890 	{
1891 		stop_fade();
1892 		set_fade_effect(NONE);
1893 		Music::instance()->StopLevelMusic();
1894 //		if(OGL_IsActive())
1895 		{
1896 			exit_screen();
1897 			// Enter_screen will be called again in start_game
1898 		}
1899 		set_keyboard_controller_status(false);
1900 		FindLevelMovie(entry.level_number);
1901 		show_movie(entry.level_number);
1902 
1903 		// if this is the EPILOGUE_LEVEL_NUMBER, then it is time to get
1904 		// out of here already (as we've just played the epilogue movie,
1905 		// we can move on to the _display_epilogue game state)
1906 		if (level_number == (shapes_file_is_m1() ? 100 : EPILOGUE_LEVEL_NUMBER)) {
1907 			finish_game(false);
1908 			show_cursor(); // for some reason, cursor stays hidden otherwise
1909 			set_game_state(_begin_display_of_epilogue);
1910 			force_game_state_change();
1911 			return;
1912 		}
1913 
1914 		if (!game_is_networked) try_and_display_chapter_screen(level_number, true, false);
1915 		success= goto_level(&entry, false, dynamic_world->player_count);
1916 		set_keyboard_controller_status(true);
1917 	}
1918 
1919 	if(success)
1920 	{
1921 		start_game(game_state.user, true);
1922 	} else {
1923 		display_loading_map_error();
1924 		finish_game(true);
1925 	}
1926 }
1927 
1928 /* The port is set.. */
draw_button(short index,bool pressed)1929 static void draw_button(
1930 	short index,
1931 	bool pressed)
1932 {
1933 	if (index == _about_alephone_rect) return;
1934 
1935 	screen_rectangle *screen_rect= get_interface_rectangle(index);
1936 	short pict_resource_number= MAIN_MENU_BASE + pressed;
1937 
1938 	set_drawing_clip_rectangle(screen_rect->top, screen_rect->left, screen_rect->bottom, screen_rect->right);
1939 
1940 	/* Use this to avoid the fade.. */
1941 	draw_full_screen_pict_resource_from_images(pict_resource_number);
1942 
1943 	set_drawing_clip_rectangle(SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX);
1944 }
1945 
handle_replay(bool last_replay)1946 static void handle_replay( /* This is gross. */
1947 	bool last_replay)
1948 {
1949 	bool success;
1950 
1951 	if(!last_replay) force_system_colors();
1952 	success= begin_game(_replay, !last_replay);
1953 	if(!success) display_main_menu();
1954 }
1955 
1956 // ZZZ: some modifications to use generalized game-startup
begin_game(short user,bool cheat)1957 static bool begin_game(
1958 	short user,
1959 	bool cheat)
1960 {
1961 	struct entry_point entry;
1962 	struct player_start_data starts[MAXIMUM_NUMBER_OF_PLAYERS];
1963 	struct game_data game_information;
1964 	short number_of_players;
1965 	bool success= true;
1966 	bool is_networked= false;
1967 	bool clean_up_on_failure= true;
1968 	bool record_game= false;
1969 	uint32 parent_checksum = 0;
1970 
1971 	clear_game_error();
1972 	objlist_clear(starts, MAXIMUM_NUMBER_OF_PLAYERS);
1973 
1974 	switch(user)
1975 	{
1976 		case _network_player:
1977 #if !defined(DISABLE_NETWORKING)
1978 			{
1979 				game_info *network_game_info= (game_info *)NetGetGameData();
1980 
1981 				construct_multiplayer_starts(starts, &number_of_players);
1982 
1983 				game_information.game_time_remaining= network_game_info->time_limit;
1984 				game_information.kill_limit= network_game_info->kill_limit;
1985 				game_information.game_type= network_game_info->net_game_type;
1986 				game_information.game_options= network_game_info->game_options;
1987 				game_information.initial_random_seed= network_game_info->initial_random_seed;
1988 				game_information.difficulty_level= network_game_info->difficulty_level;
1989 				parent_checksum = network_game_info->parent_checksum;
1990 				entry.level_number = network_game_info->level_number;
1991 				entry.level_name[0] = 0;
1992 
1993 				if (network_game_info->allow_mic)
1994 				{
1995 					install_network_microphone();
1996 					game_state.current_netgame_allows_microphone= true;
1997 				} else {
1998 					game_state.current_netgame_allows_microphone= false;
1999 				}
2000 				game_information.cheat_flags = network_game_info->cheat_flags;
2001 				std::fill_n(game_information.parameters, 2, 0);
2002 
2003 				is_networked= true;
2004 				record_game= true;
2005 				// ZZZ: until players specify their behavior modifiers over the network,
2006 				// to avoid out-of-sync we must force them all the same.
2007 				standardize_player_behavior_modifiers();
2008 			}
2009 #endif // !defined(DISABLE_NETWORKING)
2010 			break;
2011 
2012 		case _replay_from_file:
2013 		case _replay:
2014 		case _demo:
2015 			switch(user)
2016 			{
2017 				case _replay:
2018 					{
2019 						FileSpecifier ReplayFile;
2020 						show_cursor(); // JTP: Hidden one way or another :p
2021 
2022 						bool prompt_to_export = false;
2023 #ifndef MAC_APP_STORE
2024 						SDL_Keymod m = SDL_GetModState();
2025 						if ((m & KMOD_ALT) || (m & KMOD_GUI)) prompt_to_export = true;
2026 #endif
2027 
2028 						success= find_replay_to_use(cheat, ReplayFile);
2029 						if(success)
2030 						{
2031 							if(!get_map_file().Exists())
2032 							{
2033 								set_game_error(systemError, ENOENT);
2034 								display_loading_map_error();
2035 								success= false;
2036 							}
2037 							else
2038 							{
2039 								success= setup_for_replay_from_file(ReplayFile, get_current_map_checksum(), prompt_to_export);
2040 
2041 								hide_cursor();
2042 							}
2043 						}
2044 					}
2045 					break;
2046 
2047 				case _demo:
2048 					// setup_replay_from_random_resource always returns false,
2049 					// so don't bother to checksum the map
2050 					success= false;
2051 					// success= setup_replay_from_random_resource(get_current_map_checksum());
2052 					break;
2053 
2054 				case _replay_from_file:
2055 					success= setup_for_replay_from_file(DraggedReplayFile, get_current_map_checksum());
2056 					user= _replay;
2057 					break;
2058 
2059 				default:
2060 					assert(false);
2061 					break;
2062 			}
2063 
2064 			if(success)
2065 			{
2066 				uint32 unused1;
2067 				short recording_version;
2068 
2069 				get_recording_header_data(&number_of_players,
2070 					&entry.level_number, &unused1, &recording_version,
2071 					starts, &game_information);
2072 
2073 				if(recording_version > max_handled_recording)
2074 				{
2075 					stop_replay();
2076 					alert_user(infoError, strERRORS, replayVersionTooNew, 0);
2077 					success= false;
2078 				}
2079 				else
2080 				{
2081 					switch (recording_version)
2082 					{
2083 					case RECORDING_VERSION_MARATHON_2:
2084 						load_film_profile(FILM_PROFILE_MARATHON_2);
2085 						break;
2086 					case RECORDING_VERSION_MARATHON_INFINITY:
2087 						load_film_profile(FILM_PROFILE_MARATHON_INFINITY);
2088 						break;
2089 					case RECORDING_VERSION_ALEPH_ONE_1_0:
2090 						load_film_profile(FILM_PROFILE_ALEPH_ONE_1_0);
2091 						break;
2092 					case RECORDING_VERSION_ALEPH_ONE_1_1:
2093 						load_film_profile(FILM_PROFILE_ALEPH_ONE_1_1);
2094 						break;
2095 					case RECORDING_VERSION_ALEPH_ONE_1_2:
2096 						load_film_profile(FILM_PROFILE_DEFAULT);
2097 						break;
2098 					default:
2099 						load_film_profile(environment_preferences->film_profile);
2100 						break;
2101 					}
2102 
2103 					entry.level_name[0] = 0;
2104 					game_information.game_options |= _overhead_map_is_omniscient;
2105 					record_game= false;
2106 					// ZZZ: until films store behavior modifiers, we must require
2107 					// that they record and playback only with standard modifiers.
2108 					standardize_player_behavior_modifiers();
2109 				}
2110 			}
2111 			break;
2112 
2113 		case _single_player:
2114 			if(cheat)
2115 			{
2116 				entry.level_number= get_level_number_from_user();
2117 				if(entry.level_number==NONE) success= false; /* Cancelled */
2118 			} else {
2119 				entry.level_number= 0;
2120 			}
2121 
2122 			// ZZZ: let the user use his behavior modifiers in single-player.
2123 			restore_custom_player_behavior_modifiers();
2124 
2125 			entry.level_name[0] = 0;
2126 			starts[0].identifier = 0;
2127                         //AS: make things clearer
2128                         memset(entry.level_name,0,66);
2129 
2130                         construct_single_player_start(starts, &number_of_players);
2131 
2132 			game_information.game_time_remaining= INT32_MAX;
2133 			game_information.kill_limit = 0;
2134 			game_information.game_type= _game_of_kill_monsters;
2135 			game_information.game_options= _burn_items_on_death|_ammo_replenishes|_weapons_replenish|_monsters_replenish;
2136 			game_information.initial_random_seed= machine_tick_count();
2137 			game_information.difficulty_level= get_difficulty_level();
2138 			std::fill_n(game_information.parameters, 2, 0);
2139 
2140                         // ZZZ: until film files store player behavior flags, we must require
2141                         // that all films recorded be made with standard behavior.
2142 			record_game= is_player_behavior_standard();
2143 
2144             break;
2145 
2146 		default:
2147 			assert(false);
2148 			break;
2149 	}
2150 
2151 	if(success)
2152 	{
2153 		if(record_game)
2154 		{
2155 			if(!get_map_file().Exists())
2156 			{
2157 				set_game_error(systemError, ENOENT);
2158 				display_loading_map_error();
2159 				success= false;
2160 			}
2161 			else
2162 			{
2163 				set_recording_header_data(number_of_players, entry.level_number, (user == _network_player) ? parent_checksum : get_current_map_checksum(),
2164 					default_recording_version, starts, &game_information);
2165 				start_recording();
2166 			}
2167 		}
2168 	}
2169 	if(success)
2170 	{
2171 		hide_cursor();
2172 		/* This has already been done to get to gather/join */
2173 		if(can_interface_fade_out())
2174 		{
2175 			interface_fade_out(MAIN_MENU_BASE, true);
2176 		}
2177 
2178 		/* Try to display the first chapter screen.. */
2179 		if (user != _network_player && user != _demo)
2180 		{
2181 			FindLevelMovie(entry.level_number);
2182 			show_movie(entry.level_number);
2183 			try_and_display_chapter_screen(entry.level_number, false, false);
2184 		}
2185 
2186 		Plugins::instance()->set_mode(number_of_players > 1 ? Plugins::kMode_Net : Plugins::kMode_Solo);
2187 		Crosshairs_SetActive(player_preferences->crosshairs_active);
2188 		LoadHUDLua();
2189 		RunLuaHUDScript();
2190 
2191 		/* Begin the game! */
2192 		success= new_game(number_of_players, is_networked, &game_information, starts, &entry);
2193 		if(success)
2194 		{
2195 			start_game(user, false);
2196 		} else {
2197 			clean_up_after_failed_game(user == _network_player, record_game, clean_up_on_failure);
2198 		}
2199 	} else {
2200 		/* This means that some weird replay problem happened: */
2201 		/*  1) User cancelled */
2202 		/*  2) Demos not present */
2203 		/*  3) Error... */
2204 		/* Either way, we eat them.. */
2205 	}
2206 
2207 	return success;
2208 }
2209 
start_game(short user,bool changing_level)2210 static void start_game(
2211 	short user,
2212 	bool changing_level)
2213 {
2214 	/* Change our menus.. */
2215 	toggle_menus(true);
2216 
2217 	// LP change: reset screen so that extravision will not be persistent
2218 	reset_screen();
2219 
2220 	enter_screen();
2221 	if (!changing_level)
2222 		L_Call_HUDInit();
2223 
2224 	// LP: this is in case we are starting underneath a liquid
2225 	if (!OGL_IsActive() || !(TEST_FLAG(Get_OGL_ConfigureData().Flags,OGL_Flag_Fader)))
2226 	{
2227 		set_fade_effect(NONE);
2228 		SetFadeEffectDelay(TICKS_PER_SECOND/2);
2229 	}
2230 
2231 	// Screen should already be black!
2232 	validate_world_window();
2233 
2234 	draw_interface();
2235 
2236 #ifdef PERFORMANCE
2237 	PerfControl(perf_globals, true);
2238 #endif
2239 
2240 	// ZZZ: If it's a netgame, we want prediction; else no.
2241 	set_prediction_wanted(user == _network_player);
2242 
2243 	game_state.state= _game_in_progress;
2244 	game_state.current_screen= 0;
2245 	game_state.phase = MACHINE_TICKS_PER_SECOND;
2246 	game_state.last_ticks_on_idle= machine_tick_count();
2247 	game_state.user= user;
2248 	game_state.flags= 0;
2249 
2250 	assert((!changing_level&&!get_keyboard_controller_status()) || (changing_level && get_keyboard_controller_status()));
2251 	if(!changing_level)
2252 	{
2253 		set_keyboard_controller_status(true);
2254 	}
2255 }
2256 
2257 // LP: "static" removed
handle_load_game(void)2258 void handle_load_game(
2259 	void)
2260 {
2261 	FileSpecifier FileToLoad;
2262 	bool success= false;
2263 
2264 	force_system_colors();
2265 	show_cursor(); // JTP: Was hidden by force system colors
2266 	if(choose_saved_game_to_load(FileToLoad))
2267 	{
2268 		if(load_and_start_game(FileToLoad))
2269                 {
2270 			success= true;
2271 		}
2272 	}
2273 
2274 	if(!success)
2275 	{
2276 		hide_cursor(); // JTP: Will be shown when fade stops
2277 		display_main_menu();
2278 	}
2279 }
2280 
2281 extern bool current_net_game_has_scores();
2282 
finish_game(bool return_to_main_menu)2283 static void finish_game(
2284 	bool return_to_main_menu)
2285 {
2286 	set_keyboard_controller_status(false);
2287 
2288 #ifdef PERFORMANCE
2289 	PerfControl(perf_globals, false);
2290 #endif
2291 	/* Note that we have to deal with the switch demo state later because */
2292 	/* Alain's code calls us at interrupt level 1. (so we defer it) */
2293 	assert(game_state.state==_game_in_progress || game_state.state==_switch_demo || game_state.state==_revert_game || game_state.state==_change_level || game_state.state==_begin_display_of_epilogue);
2294 	toggle_menus(false);
2295 
2296 	stop_fade();
2297 	set_fade_effect(NONE);
2298 	L_Call_HUDCleanup();
2299 	exit_screen();
2300 
2301 	/* Stop the replay */
2302 	switch(game_state.user)
2303 	{
2304 		case _single_player:
2305 		case _network_player:
2306 			stop_recording();
2307 			break;
2308 
2309 		case _demo:
2310 		case _replay:
2311 			stop_replay();
2312 			break;
2313 
2314 		default:
2315 			vhalt(csprintf(temporary, "What is user %d?", game_state.user));
2316 			break;
2317 	}
2318 	Movie::instance()->StopRecording();
2319 
2320 	/* Fade out! (Pray) */ // should be interface_color_table for valkyrie, but doesn't work.
2321 	Music::instance()->ClearLevelMusic();
2322 	Music::instance()->FadeOut(MACHINE_TICKS_PER_SECOND / 2);
2323 	full_fade(_cinematic_fade_out, interface_color_table);
2324 	paint_window_black();
2325 	full_fade(_end_cinematic_fade_out, interface_color_table);
2326 
2327 	show_cursor();
2328 
2329 	leaving_map();
2330 	CloseLuaHUDScript();
2331 
2332 	// LP: stop playing the background music if it was present
2333 	Music::instance()->StopLevelMusic();
2334 
2335 	/* Get as much memory back as we can. */
2336 	free_and_unlock_memory(); // this could call free_map..
2337 	unload_all_collections();
2338 	SoundManager::instance()->UnloadAllSounds();
2339 
2340 #if !defined(DISABLE_NETWORKING)
2341 	if (game_state.user==_network_player)
2342 	{
2343 		if(game_state.current_netgame_allows_microphone)
2344 		{
2345 			remove_network_microphone();
2346 		}
2347 		NetUnSync(); // gracefully exit from the game
2348 
2349 		/* Don't update the screen, etc.. */
2350 		game_state.state= _displaying_network_game_dialogs;
2351 
2352 		change_screen_mode(_screentype_menu);
2353 		force_system_colors();
2354 		display_net_game_stats();
2355 		exit_networking();
2356 	}
2357 	else
2358 #endif // !defined(DISABLE_NETWORKING)
2359 	if (game_state.user == _replay && !(dynamic_world->game_information.game_type == _game_of_kill_monsters && dynamic_world->player_count == 1))
2360 	{
2361 		game_state.state = _displaying_network_game_dialogs;
2362 
2363 		force_system_colors();
2364 		display_net_game_stats();
2365 	}
2366 
2367 	set_local_player_index(NONE);
2368 	set_current_player_index(NONE);
2369 
2370 	load_environment_from_preferences();
2371 	if (game_state.user == _replay || game_state.user == _demo)
2372 	{
2373 		Plugins::instance()->set_mode(Plugins::kMode_Menu);
2374 		load_film_profile(FILM_PROFILE_DEFAULT);
2375 	}
2376 	if(return_to_main_menu) display_main_menu();
2377 }
2378 
clean_up_after_failed_game(bool inNetgame,bool inRecording,bool inFullCleanup)2379 static void clean_up_after_failed_game(bool inNetgame, bool inRecording, bool inFullCleanup)
2380 {
2381         /* Stop recording.. */
2382         if(inRecording)
2383         {
2384                 stop_recording();
2385         }
2386 
2387         set_local_player_index(NONE);
2388         set_current_player_index(NONE);
2389 
2390         /* Show the cursor here on failure. */
2391         show_cursor();
2392 
2393         /* The only time we don't clean up is on the replays.. */
2394         if(inFullCleanup)
2395         {
2396                 if (inNetgame)
2397                 {
2398 #if !defined(DISABLE_NETWORKING)
2399                         if(game_state.current_netgame_allows_microphone)
2400                         {
2401                                 remove_network_microphone();
2402                         }
2403                         exit_networking();
2404 #endif // !defined(DISABLE_NETWORKING)
2405                 } else {
2406 /* NOTE: The network code is now responsible for displaying its own errors!!!! */
2407                         /* Give them the error... */
2408                         display_loading_map_error();
2409                 }
2410 
2411                 /* Display the main menu on failure.... */
2412                 display_main_menu();
2413         }
2414         set_game_error(systemError, errNone);
2415 }
2416 
handle_network_game(bool gatherer)2417 static void handle_network_game(
2418 	bool gatherer)
2419 {
2420 #if !defined(DISABLE_NETWORKING)
2421 	bool successful_gather = false;
2422 	bool joined_resume_game = false;
2423 
2424 	force_system_colors();
2425 
2426 	/* Don't update the screen, etc.. */
2427 	game_state.state= _displaying_network_game_dialogs;
2428 	if(gatherer)
2429 	{
2430 		successful_gather= network_gather(false);
2431 		if (successful_gather) successful_gather= NetStart();
2432 	} else {
2433 		int theNetworkJoinResult= network_join();
2434 		if (theNetworkJoinResult == kNetworkJoinedNewGame || theNetworkJoinResult == kNetworkJoinedResumeGame) successful_gather= true;
2435 		if (theNetworkJoinResult == kNetworkJoinedResumeGame) joined_resume_game= true;
2436 	}
2437 
2438 	if (successful_gather)
2439 	{
2440 		if (joined_resume_game)
2441 		{
2442 			if (join_networked_resume_game() == false) clean_up_after_failed_game(true /*netgame*/, false /*recording*/, true /*full cleanup*/);
2443 		}
2444 		else
2445 		{
2446 			begin_game(_network_player, false);
2447 		}
2448 	} else {
2449 		/* We must restore the colors on cancel. */
2450 		display_main_menu();
2451 	}
2452 #else // !defined(DISABLE_NETWORKING)
2453 	alert_user(infoError, strERRORS, networkNotSupportedForDemo, 0);
2454 #endif // !defined(DISABLE_NETWORKING)
2455 }
2456 
handle_save_film(void)2457 static void handle_save_film(
2458 	void)
2459 {
2460 	force_system_colors();
2461 	show_cursor(); // JTP: Hidden by force_system_colors
2462 	move_replay();
2463 	hide_cursor(); // JTP: Will be shown by display_main_menu
2464 	display_main_menu();
2465 }
2466 
next_game_screen(void)2467 static void next_game_screen(
2468 	void)
2469 {
2470 	struct screen_data *data= get_screen_data(game_state.state);
2471 
2472 	stop_interface_fade();
2473 	if(++game_state.current_screen>=data->screen_count)
2474 	{
2475 		switch(game_state.state)
2476 		{
2477 			case _display_main_menu:
2478 				/* Whoops.  didn't get it. */
2479 				alert_out_of_memory();
2480 				break;
2481 
2482 			case _display_quit_screens:
2483 				StatsManager::instance()->Finish();
2484 
2485 				/* Fade out.. */
2486 				interface_fade_out(data->screen_base+game_state.current_screen, true);
2487 				game_state.state= _quit_game;
2488 				break;
2489 
2490 			default:
2491 				display_main_menu();
2492 				break;
2493 		}
2494 	} else {
2495 		if(game_state.state==_display_intro_screens &&
2496 			game_state.current_screen==INTRO_SCREEN_TO_START_SONG_ON)
2497 		{
2498 			Music::instance()->RestartIntroMusic();
2499 		}
2500 		// LP addition: check to see if a picture exists before drawing it.
2501 		// Otherwise, set the countdown value to zero.
2502 		short pict_resource_number= data->screen_base+game_state.current_screen;
2503 		if (images_picture_exists(pict_resource_number))
2504 		{
2505 			game_state.phase= data->duration;
2506 			game_state.last_ticks_on_idle= machine_tick_count();
2507 			display_screen(data->screen_base);
2508 			if (game_state.state == _display_intro_screens)
2509 			{
2510 				Mixer::instance()->StopSoundResource();
2511 				SoundRsrc.Unload();
2512 				if (get_sound_resource_from_images(pict_resource_number, SoundRsrc))
2513 				{
2514 					_fixed pitch = (shapes_file_is_m1() && game_state.state==_display_intro_screens) ? _m1_high_frequency : _normal_frequency;
2515 					Mixer::instance()->PlaySoundResource(SoundRsrc, pitch);
2516 				}
2517 			}
2518 		}
2519 		else
2520 		{
2521 			game_state.phase= 0;
2522 			game_state.last_ticks_on_idle= machine_tick_count();
2523 		}
2524 	}
2525 }
2526 
display_loading_map_error(void)2527 static void display_loading_map_error(
2528 	void)
2529 {
2530 	short error, type;
2531 
2532 	/* Give them the error... */
2533 	error= get_game_error(&type);
2534 	if(type==gameError)
2535 	{
2536 		short string_id;
2537 
2538 		switch(error)
2539 		{
2540 			case errServerDied:
2541 				string_id= serverQuitInCooperativeNetGame;
2542 				break;
2543 			case errUnsyncOnLevelChange:
2544 				string_id= unableToGracefullyChangeLevelsNet;
2545 				break;
2546 
2547 			case errMapFileNotSet:
2548 			case errIndexOutOfRange:
2549 			case errTooManyOpenFiles:
2550 			case errUnknownWadVersion:
2551 			case errWadIndexOutOfRange:
2552 			default:
2553 				string_id= badReadMapGameError;
2554 				break;
2555 		}
2556 		alert_user(infoError, strERRORS, string_id, error);
2557 	} else {
2558 		alert_user(infoError, strERRORS, badReadMapSystemError, error);
2559 	}
2560 	set_game_error(systemError, errNone);
2561 }
2562 
force_system_colors(void)2563 static void force_system_colors(
2564 	void)
2565 {
2566 	if(can_interface_fade_out())
2567 	{
2568 		interface_fade_out(MAIN_MENU_BASE, true);
2569 	}
2570 
2571 	if(interface_bit_depth==8)
2572 	{
2573 		struct color_table *system_colors= build_8bit_system_color_table();
2574 
2575 		assert_world_color_table(system_colors, (struct color_table *) NULL);
2576 
2577 		delete system_colors;
2578 	}
2579 }
2580 
display_screen(short base_pict_id)2581 static void display_screen(
2582 	short base_pict_id)
2583 {
2584 	short pict_resource_number= base_pict_id+game_state.current_screen;
2585 	static bool picture_drawn= false;
2586 
2587 	if (images_picture_exists(pict_resource_number))
2588 	{
2589 		stop_interface_fade();
2590 
2591 		if(current_picture_clut)
2592 		{
2593 			interface_fade_out(pict_resource_number, false);
2594 		}
2595 
2596 		assert(!current_picture_clut);
2597 		current_picture_clut= calculate_picture_clut(CLUTSource_Images,pict_resource_number);
2598 		current_picture_clut_depth= interface_bit_depth;
2599 
2600 		if(current_picture_clut)
2601 		{
2602 			/* slam the entire clut to black, now. */
2603 			if (interface_bit_depth==8)
2604 			{
2605 				assert_world_color_table(current_picture_clut, (struct color_table *) NULL);
2606 			}
2607 
2608 			full_fade(_start_cinematic_fade_in, current_picture_clut);
2609 
2610 			draw_full_screen_pict_resource_from_images(pict_resource_number);
2611 			picture_drawn= true;
2612 
2613 			assert(current_picture_clut);
2614 			start_interface_fade(_long_cinematic_fade_in, current_picture_clut);
2615 		}
2616 	}
2617 
2618 	if(!picture_drawn)
2619 	{
2620 dprintf("Didn't draw: %d;g", pict_resource_number);
2621 		/* Go for the next one.. */
2622 		next_game_screen();
2623 	}
2624 }
2625 
point_in_rectangle(short x,short y,screen_rectangle * rect)2626 static bool point_in_rectangle(
2627 	short x,
2628 	short y,
2629 	screen_rectangle *rect)
2630 {
2631 	bool in_rectangle= false;
2632 
2633 	if(x>=rect->left && x<rect->right && y>=rect->top && y<rect->bottom)
2634 	{
2635 		in_rectangle= true;
2636 	}
2637 
2638 	return in_rectangle;
2639 }
2640 
handle_interface_menu_screen_click(short x,short y,bool cheatkeys_down)2641 static void handle_interface_menu_screen_click(
2642 	short x,
2643 	short y,
2644 	bool cheatkeys_down)
2645 {
2646 	short index;
2647 	screen_rectangle *screen_rect;
2648 	short xoffset = 0, yoffset = 0;
2649 
2650 	/* find it.. */
2651 	for(index= START_OF_MENU_INTERFACE_RECTS; index<END_OF_MENU_INTERFACE_RECTS; ++index)
2652 	{
2653 		screen_rect= get_interface_rectangle(index);
2654 		if (point_in_rectangle(x - xoffset, y - yoffset, screen_rect))
2655 			break;
2656 	}
2657 
2658 	/* we found one.. */
2659 	if(index!=END_OF_MENU_INTERFACE_RECTS)
2660 	{
2661 		if(enabled_item(index-START_OF_MENU_INTERFACE_RECTS+1))
2662 		{
2663 			bool last_state= true;
2664 
2665 			stop_interface_fade();
2666 
2667 			screen_rect= get_interface_rectangle(index);
2668 
2669 			/* Draw it initially depressed.. */
2670 			draw_button(index, last_state);
2671 
2672 			bool mouse_down = true;
2673 			while (mouse_down)
2674 			{
2675 				int mx = x, my = y;
2676 				bool mouse_changed = false;
2677 
2678 				SDL_Event e;
2679 				if (SDL_PollEvent(&e))
2680 				{
2681 					switch (e.type)
2682 					{
2683 						case SDL_MOUSEBUTTONUP:
2684 							mx = e.button.x;
2685 							my = e.button.y;
2686 							mouse_changed = true;
2687 							mouse_down = false;
2688 							break;
2689 						case SDL_MOUSEMOTION:
2690 							mx = e.motion.x;
2691 							my = e.motion.y;
2692 							mouse_changed = true;
2693 							break;
2694 					}
2695 				}
2696 				else
2697 				{
2698 					SDL_Delay(10);
2699 				}
2700 				if (mouse_changed)
2701 				{
2702 					alephone::Screen::instance()->window_to_screen(mx, my);
2703 					bool state = point_in_rectangle(mx - xoffset, my - yoffset, screen_rect);
2704 					if (state != last_state)
2705 					{
2706 						draw_button(index, state);
2707 						last_state = state;
2708 					}
2709 				}
2710 			}
2711 
2712 			/* Draw it unpressed.. */
2713 			draw_button(index, false);
2714 
2715 			if(last_state)
2716 			{
2717 				do_menu_item_command(mInterface, index-START_OF_MENU_INTERFACE_RECTS+1, cheatkeys_down);
2718 			}
2719 		}
2720 	}
2721 }
2722 
2723 /* Note that this is modal. This sucks... */
try_and_display_chapter_screen(short level,bool interface_table_is_valid,bool text_block)2724 static void try_and_display_chapter_screen(
2725 	short level,
2726 	bool interface_table_is_valid,
2727 	bool text_block)
2728 {
2729 	if (Movie::instance()->IsRecording())
2730 		return;
2731 
2732 	short pict_resource_number = get_screen_data(_display_chapter_heading)->screen_base + level;
2733 	/* If the picture exists... */
2734 	if (scenario_picture_exists(pict_resource_number))
2735 	{
2736 		short existing_state= game_state.state;
2737 		game_state.state= _display_chapter_heading;
2738 		free_and_unlock_memory();
2739 
2740 		/* This will NOT work if the initial level entered has a chapter screen, which is why */
2741 		/*  we perform this check. (The interface_color_table is not valid...) */
2742 		if(interface_table_is_valid)
2743 		{
2744 			full_fade(_cinematic_fade_out, interface_color_table);
2745 			paint_window_black();
2746 		}
2747 
2748 		change_screen_mode(_screentype_chapter);
2749 
2750 		/* Fade the screen to black.. */
2751 		assert(!current_picture_clut);
2752 		current_picture_clut= calculate_picture_clut(CLUTSource_Scenario,pict_resource_number);
2753 		current_picture_clut_depth= interface_bit_depth;
2754 
2755 		if (current_picture_clut)
2756 		{
2757 			LoadedResource SoundRsrc;
2758 
2759 			/* slam the entire clut to black, now. */
2760 			if (interface_bit_depth==8)
2761 			{
2762 				assert_world_color_table(current_picture_clut, (struct color_table *) NULL);
2763 			}
2764 			full_fade(_start_cinematic_fade_in, current_picture_clut);
2765 
2766 			/* Draw the picture */
2767 			draw_full_screen_pict_resource_from_scenario(pict_resource_number);
2768 
2769 			if (get_sound_resource_from_scenario(pict_resource_number,SoundRsrc))
2770 			{
2771 				_fixed pitch = (shapes_file_is_m1() && level == 101) ? _m1_high_frequency : _normal_frequency;
2772 				Mixer::instance()->PlaySoundResource(SoundRsrc, pitch);
2773 			}
2774 
2775 			/* Fade in.... */
2776 			assert(current_picture_clut);
2777 			full_fade(_long_cinematic_fade_in, current_picture_clut);
2778 
2779 			scroll_full_screen_pict_resource_from_scenario(pict_resource_number, text_block);
2780 
2781 			wait_for_click_or_keypress(text_block ? -1 : 10*MACHINE_TICKS_PER_SECOND);
2782 
2783 			/* Fade out! (Pray) */
2784 			interface_fade_out(pict_resource_number, false);
2785 
2786 			Mixer::instance()->StopSoundResource();
2787 		}
2788 		game_state.state= existing_state;
2789 	}
2790 }
2791 
2792 
2793 #if !defined(DISABLE_NETWORKING)
2794 /*
2795  *  Network microphone handling
2796  */
2797 
install_network_microphone(void)2798 void install_network_microphone(
2799 	void)
2800 {
2801 	open_network_speaker();
2802 	NetAddDistributionFunction(kNewNetworkAudioDistributionTypeID, received_network_audio_proc, true);
2803 	open_network_microphone();
2804 }
2805 
remove_network_microphone(void)2806 void remove_network_microphone(
2807 	void)
2808 {
2809 	close_network_microphone();
2810 	NetRemoveDistributionFunction(kNewNetworkAudioDistributionTypeID);
2811 	close_network_speaker();
2812 }
2813 #endif // !defined(DISABLE_NETWORKING)
2814 
2815 
2816 /* ------------ interface fade code */
2817 /* Be aware that we could try to change bit depths before a fade is completed. */
start_interface_fade(short type,struct color_table * original_color_table)2818 static void start_interface_fade(
2819 	short type,
2820 	struct color_table *original_color_table)
2821 {
2822 	hide_cursor();
2823 	assert(!interface_fade_in_progress);
2824 	animated_color_table= new color_table;
2825 	obj_copy(*animated_color_table, *original_color_table);
2826 
2827 	if(animated_color_table)
2828 	{
2829 		interface_fade_in_progress= true;
2830 		interface_fade_type= type;
2831 
2832 		explicit_start_fade(type, original_color_table, animated_color_table);
2833 	}
2834 }
2835 
update_interface_fades(void)2836 static void update_interface_fades(
2837 	void)
2838 {
2839 	bool still_fading= false;
2840 
2841 	if(interface_fade_in_progress)
2842 	{
2843 		still_fading= update_fades();
2844 		if(!still_fading)
2845 		{
2846 			stop_interface_fade();
2847 		}
2848 	}
2849 }
2850 
stop_interface_fade(void)2851 void stop_interface_fade(
2852 	void)
2853 {
2854 	if(interface_fade_in_progress)
2855 	{
2856 		stop_fade();
2857 		interface_fade_in_progress= false;
2858 
2859 		assert(animated_color_table);
2860 		delete animated_color_table;
2861 
2862 		if (interface_bit_depth==8)
2863 		{
2864 			assert_world_color_table(current_picture_clut, (struct color_table *) NULL);
2865 		}
2866 
2867 		if(game_state.state==_display_main_menu)
2868 		{
2869 			/* This isn't a showcursor because of balancing problems (first time through..) */
2870 			show_cursor();
2871 		}
2872 	}
2873 }
2874 
2875 /* Called right before we start a game.. */
interface_fade_out(short pict_resource_number,bool fade_music)2876 void interface_fade_out(
2877 	short pict_resource_number,
2878 	bool fade_music)
2879 {
2880 	assert(current_picture_clut);
2881 	if(current_picture_clut)
2882 	{
2883 		struct color_table *fadeout_animated_color_table;
2884 
2885 		/* We have to check this because they could go into preferences and change on us, */
2886 		/*  the evil swine. */
2887 		if(current_picture_clut_depth != interface_bit_depth)
2888 		{
2889 			delete current_picture_clut;
2890 			current_picture_clut= calculate_picture_clut(CLUTSource_Images,pict_resource_number);
2891 			current_picture_clut_depth= interface_bit_depth;
2892 		}
2893 
2894 		hide_cursor();
2895 
2896 		fadeout_animated_color_table= new color_table;
2897 		obj_copy(*fadeout_animated_color_table, *current_picture_clut);
2898 
2899 		if(fade_music)
2900 			Music::instance()->FadeOut(MACHINE_TICKS_PER_SECOND/2);
2901 		if (fadeout_animated_color_table)
2902 		{
2903 			explicit_start_fade(_cinematic_fade_out, current_picture_clut, fadeout_animated_color_table);
2904 			while (update_fades())
2905 				Music::instance()->Idle();
2906 
2907 			/* Oops.  Founda  memory leak.. */
2908 			delete fadeout_animated_color_table;
2909 		}
2910 
2911 		if(fade_music)
2912 		{
2913 			Mixer::instance()->StopSoundResource();
2914 			while(Music::instance()->Playing())
2915 				Music::instance()->Idle();
2916 
2917 			/* and give up the memory */
2918 			Music::instance()->Pause();
2919 		}
2920 
2921 		paint_window_black();
2922 		full_fade(_end_cinematic_fade_out, current_picture_clut);
2923 
2924 		/* Hopefully we can do this here.. */
2925 		delete current_picture_clut;
2926 		current_picture_clut= NULL;
2927 	}
2928 }
2929 
can_interface_fade_out(void)2930 static bool can_interface_fade_out(
2931 	void)
2932 {
2933 	return (current_picture_clut==NULL) ? false : true;
2934 }
2935 
interface_fade_finished(void)2936 bool interface_fade_finished(
2937 	void)
2938 {
2939 	return fade_finished();
2940 }
2941 
2942 
do_preferences(void)2943 void do_preferences(void)
2944 {
2945 	struct screen_mode_data mode = graphics_preferences->screen_mode;
2946 
2947 	force_system_colors();
2948 	handle_preferences();
2949 
2950 	if (mode.bit_depth != graphics_preferences->screen_mode.bit_depth) {
2951 		paint_window_black();
2952 		Screen::instance()->Initialize(&graphics_preferences->screen_mode);
2953 
2954 		/* Re fade in, so that we get the proper colortable loaded.. */
2955 		display_main_menu();
2956 	} else if (memcmp(&mode, &graphics_preferences->screen_mode, sizeof(struct screen_mode_data)))
2957 		change_screen_mode(&graphics_preferences->screen_mode, false);
2958 }
2959 
2960 
2961 /*
2962  *  Toggle system hotkeys
2963  */
2964 
toggle_menus(bool game_started)2965 void toggle_menus(bool game_started)
2966 {
2967 	// nothing to do
2968 }
2969 
2970 
2971 /*
2972  *  Update game window
2973  */
2974 
update_game_window(void)2975 void update_game_window(void)
2976 {
2977 	switch(get_game_state()) {
2978 		case _game_in_progress:
2979 			update_screen_window();
2980 			break;
2981 
2982 		case _display_quit_screens:
2983 		case _display_intro_screens_for_demo:
2984 		case _display_intro_screens:
2985 		case _display_chapter_heading:
2986 		case _display_prologue:
2987 		case _display_epilogue:
2988 		case _display_credits:
2989 		case _display_main_menu:
2990 			update_interface_display();
2991 			break;
2992 
2993 		default:
2994 			break;
2995 	}
2996 }
2997 
2998 
2999 /*
3000  *  Exit networking
3001  */
3002 
exit_networking(void)3003 void exit_networking(void)
3004 {
3005 #if !defined(DISABLE_NETWORKING)
3006 	NetExit();
3007 #endif // !defined(DISABLE_NETWORKING)
3008 }
3009 
3010 
3011 /*
3012  *  Show movie
3013  */
3014 
3015 #ifdef HAVE_OPENGL
3016 #if defined(HAVE_FFMPEG) || defined(HAVE_SMPEG)
3017 static OGL_Blitter show_movie_blitter;
3018 #endif
3019 #ifdef HAVE_SMPEG
3020 static SDL_mutex *show_movie_mutex = NULL;
3021 
3022 // SMPEG callback for use under OpenGL
show_movie_frame(SDL_Surface * frame,int x,int y,unsigned int w,unsigned int h)3023 static void show_movie_frame(SDL_Surface* frame, int x, int y, unsigned int w, unsigned int h)
3024 {
3025 	if (show_movie_mutex && SDL_LockMutex(show_movie_mutex) != -1)
3026 	{
3027 		if (!show_movie_blitter.Loaded())
3028 		{
3029 			show_movie_blitter.Load(*frame);
3030 			// let main thread know there's a frame ready
3031 			SDL_Event ev;
3032 			ev.type = SDL_USEREVENT;
3033 			SDL_PushEvent(&ev);
3034 		}
3035 		SDL_UnlockMutex(show_movie_mutex);
3036 	}
3037 }
3038 #endif
3039 #endif
3040 #ifdef HAVE_FFMPEG
3041 static SDL_mutex *movie_audio_mutex = NULL;
3042 static const int AUDIO_BUF_SIZE = 10;
3043 static SDL_ffmpegAudioFrame *aframes[AUDIO_BUF_SIZE];
3044 static uint64_t movie_sync = 0;
movie_audio_callback(void * data,Uint8 * stream,int length)3045 void movie_audio_callback(void *data, Uint8 *stream, int length)
3046 {
3047 	if (movie_audio_mutex && SDL_LockMutex(movie_audio_mutex) != -1)
3048 	{
3049 		if (aframes[0]->size == length)
3050 		{
3051 			movie_sync = aframes[0]->pts;
3052 			memcpy(stream, aframes[0]->buffer, aframes[0]->size);
3053 			aframes[0]->size = 0;
3054 
3055 			SDL_ffmpegAudioFrame *f = aframes[0];
3056 			for (int i = 1; i < AUDIO_BUF_SIZE; i++)
3057 				aframes[i - 1] = aframes[i];
3058 			aframes[AUDIO_BUF_SIZE - 1] = f;
3059 		}
3060 		else
3061 		{
3062 			memset(stream, 0, length);
3063 		}
3064 		SDL_UnlockMutex(movie_audio_mutex);
3065 	}
3066 }
3067 #endif
3068 
3069 extern bool option_nosound;
3070 
show_movie(short index)3071 void show_movie(short index)
3072 {
3073 	if (Movie::instance()->IsRecording())
3074 		return;
3075 
3076 #if defined(HAVE_FFMPEG) || defined(HAVE_SMPEG)
3077 	float PlaybackSize = 2;
3078 
3079 	FileSpecifier IntroMovie;
3080 	FileSpecifier *File = GetLevelMovie(PlaybackSize);
3081 
3082 	if (!File && index == 0)
3083 	{
3084 		if (IntroMovie.SetNameWithPath(getcstr(temporary, strFILENAMES, filenameMOVIE)))
3085 			File = &IntroMovie;
3086 	}
3087 
3088 	if (!File) return;
3089 
3090 	change_screen_mode(_screentype_chapter);
3091 
3092 	{
3093 		SoundManager::Pause pauseSoundManager;
3094 		SDL_Rect dst_rect = { 0, 0, 640, 480 };
3095 
3096 #ifdef HAVE_FFMPEG
3097 		SDL_ffmpegFile *sffile = SDL_ffmpegOpen(File->GetPath());
3098 		if (!sffile)
3099 			return;
3100 
3101 		SDL_ffmpegSelectVideoStream(sffile, 0);
3102 		SDL_ffmpegStream *vstream = SDL_ffmpegGetVideoStream(sffile, 0);
3103 
3104 		SDL_ffmpegSelectAudioStream(sffile, 0);
3105 		SDL_ffmpegStream *astream = SDL_ffmpegGetAudioStream(sffile, 0);
3106 
3107 		SDL_ffmpegVideoFrame *vframe = vstream ? SDL_ffmpegCreateVideoFrame() : NULL;
3108 
3109 		if (vframe)
3110 		{
3111 			vframe->surface = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_rect.w, dst_rect.h, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000);
3112 			if (!vframe->surface)
3113 			{
3114 				SDL_ffmpegFreeVideoFrame(vframe);
3115 				vframe = NULL;
3116 			}
3117 		}
3118 
3119 
3120 		if (astream)
3121 		{
3122 			movie_audio_mutex = SDL_CreateMutex();
3123 			SDL_AudioSpec specs = SDL_ffmpegGetAudioSpec(sffile, 512, movie_audio_callback);
3124 			if (SDL_OpenAudio(&specs, 0) >= 0)
3125 			{
3126 				int frameSize = specs.channels * specs.samples * 2;
3127 				for (int i = 0; i < AUDIO_BUF_SIZE; i++)
3128 				{
3129 					aframes[i] = SDL_ffmpegCreateAudioFrame(sffile, frameSize);
3130 					SDL_ffmpegGetAudioFrame(sffile, aframes[i]);
3131 				}
3132 			}
3133 		}
3134 
3135 #ifdef HAVE_OPENGL
3136 		if (OGL_IsActive())
3137 			OGL_ClearScreen();
3138 #endif
3139 
3140 		SDL_PauseAudio(false);
3141 		bool done = false;
3142 		while (!done)
3143 		{
3144 			SDL_Event event;
3145 			while (SDL_PollEvent(&event) )
3146 			{
3147 				switch (event.type) {
3148 				case SDL_KEYDOWN:
3149 				case SDL_MOUSEBUTTONDOWN:
3150 				case SDL_CONTROLLERBUTTONDOWN:
3151 					done = true;
3152 					break;
3153 				default:
3154 					break;
3155 				}
3156 			}
3157 
3158 			if (astream)
3159 			{
3160 				SDL_LockMutex(movie_audio_mutex);
3161 				for (int i = 0; i < AUDIO_BUF_SIZE; i++)
3162 				{
3163 					if (!aframes[i]->size)
3164 					{
3165 						SDL_ffmpegGetAudioFrame(sffile, aframes[i]);
3166 					}
3167 				}
3168 				if (!aframes[AUDIO_BUF_SIZE - 1]->size && aframes[AUDIO_BUF_SIZE - 1]->last)
3169 					done = true;
3170 				SDL_UnlockMutex(movie_audio_mutex);
3171 			}
3172 
3173 			if (vframe)
3174 			{
3175 				if (!vframe->ready)
3176 				{
3177 					SDL_ffmpegGetVideoFrame(sffile, vframe);
3178 				}
3179 				else if (vframe->pts <= movie_sync)
3180 				{
3181 #ifdef HAVE_OPENGL
3182 					if (OGL_IsActive())
3183 					{
3184 						OGL_Blitter::BoundScreen();
3185 						show_movie_blitter.Load(*(vframe->surface));
3186 						show_movie_blitter.Draw(dst_rect);
3187 						show_movie_blitter.Unload();
3188 						MainScreenSwap();
3189 					}
3190 					else
3191 #endif
3192 					{
3193 						SDL_BlitSurface(vframe->surface, 0, MainScreenSurface(), &dst_rect);
3194 						MainScreenUpdateRects(1, &dst_rect);
3195 					}
3196 					vframe->ready = 0;
3197 					if (vframe->last)
3198 						done = true;
3199 				}
3200 				else
3201 				{
3202 					SDL_Delay(MIN(30, vframe->pts - movie_sync));
3203 				}
3204 			}
3205 		}
3206 
3207 		SDL_PauseAudio(true);
3208 		if (astream)
3209 		{
3210 			for (int i = 0; i < AUDIO_BUF_SIZE; i++)
3211 			{
3212 				SDL_ffmpegFreeAudioFrame(aframes[i]);
3213 			}
3214 			SDL_DestroyMutex(movie_audio_mutex);
3215 			movie_audio_mutex = NULL;
3216 		}
3217 
3218 		if (vframe)
3219 			SDL_ffmpegFreeVideoFrame(vframe);
3220 		SDL_ffmpegFree(sffile);
3221 		SDL_CloseAudio();
3222 
3223 #elif defined(HAVE_SMPEG) // end HAVE_FFMPEG
3224 
3225 		SMPEG_Info info;
3226 		SMPEG *movie;
3227 
3228 		movie = SMPEG_new(File->GetPath(), &info, option_nosound ? 0 : 1);
3229 		if (!movie) return;
3230 		if (!info.has_video) {
3231 			SMPEG_delete(movie);
3232 			return;
3233 		}
3234 
3235 #ifdef HAVE_OPENGL
3236 		SDL_Surface *gl_surface = NULL;
3237 		if (OGL_IsActive())
3238 		{
3239 			if (PlatformIsLittleEndian()) {
3240 				gl_surface = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_rect.w, dst_rect.h, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000);
3241 			} else {
3242 				gl_surface = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_rect.w, dst_rect.h, 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x00000000);
3243 			}
3244 			SMPEG_setdisplay(movie, gl_surface, NULL, show_movie_frame);
3245 			SMPEG_scaleXY(movie, dst_rect.w, dst_rect.h);
3246 			show_movie_mutex = SDL_CreateMutex();
3247 			OGL_ClearScreen();
3248 		}
3249 		else
3250 #endif
3251 		{
3252 			SMPEG_setdisplay(movie, MainScreenSurface(), NULL, NULL);
3253 			SMPEG_scaleXY(movie, dst_rect.w, dst_rect.h);
3254 			SMPEG_move(movie, dst_rect.x, dst_rect.y);
3255 		}
3256 
3257 		bool done = false;
3258 		SMPEG_play(movie);
3259 		while (!done && SMPEG_status(movie) == SMPEG_PLAYING)
3260 		{
3261 			SDL_Event event;
3262 			while (SDL_PollEvent(&event) )
3263 			{
3264 				switch (event.type) {
3265 				case SDL_KEYDOWN:
3266 				case SDL_MOUSEBUTTONDOWN:
3267 				case SDL_CONTROLLERBUTTONDOWN:
3268 					done = true;
3269 					break;
3270 #ifdef HAVE_OPENGL
3271 				case SDL_USEREVENT:
3272 					if (SDL_LockMutex(show_movie_mutex) != -1)
3273 					{
3274 						OGL_Blitter::BoundScreen();
3275 						show_movie_blitter.Draw(dst_rect);
3276 						show_movie_blitter.Unload();
3277 						SDL_UnlockMutex(show_movie_mutex);
3278 						MainScreenSwap();
3279 					}
3280 					break;
3281 #endif
3282 				default:
3283 					break;
3284 				}
3285 			}
3286 
3287 			SDL_Delay(100);
3288 		}
3289 		SMPEG_delete(movie);
3290 #ifdef HAVE_OPENGL
3291 		SDL_FreeSurface(gl_surface);
3292 		SDL_DestroyMutex(show_movie_mutex);
3293 		show_movie_mutex = NULL;
3294 		show_movie_blitter.Unload();
3295 #endif
3296 #endif // HAVE_SMPEG
3297 	}
3298 #endif // HAVE_FFMPEG || HAVE_SMPEG
3299 }
3300 
3301 
should_restore_game_networked(FileSpecifier & file)3302 size_t should_restore_game_networked(FileSpecifier& file)
3303 {
3304 	// We return -1 (NONE) for "cancel", 0 for "not networked", and 1 for "networked".
3305 	size_t theResult = saved_game_was_networked(file);
3306 	if (theResult != UNONE)
3307 		return theResult;
3308 
3309         dialog d;
3310 
3311 	vertical_placer *placer = new vertical_placer;
3312 	placer->dual_add(new w_title("RESUME GAME"), d);
3313 	placer->add(new w_spacer, true);
3314 
3315 	horizontal_placer *resume_as_placer = new horizontal_placer;
3316         w_toggle* theRestoreAsNetgameToggle = new w_toggle(dynamic_world->player_count > 1, 0);
3317         theRestoreAsNetgameToggle->set_labels_stringset(kSingleOrNetworkStringSetID);
3318 	resume_as_placer->dual_add(theRestoreAsNetgameToggle->label("Resume as"), d);
3319 	resume_as_placer->dual_add(theRestoreAsNetgameToggle, d);
3320 
3321 	placer->add(resume_as_placer, true);
3322 
3323 	placer->add(new w_spacer(), true);
3324 	placer->add(new w_spacer(), true);
3325 
3326 	horizontal_placer *button_placer = new horizontal_placer;
3327 	button_placer->dual_add(new w_button("RESUME", dialog_ok, &d), d);
3328 	button_placer->dual_add(new w_button("CANCEL", dialog_cancel, &d), d);
3329 
3330 	placer->add(button_placer, true);
3331 
3332 
3333 	d.set_widget_placer(placer);
3334 
3335         if(d.run() == 0)
3336         {
3337                 theResult = theRestoreAsNetgameToggle->get_selection();
3338         }
3339         else
3340         {
3341                 theResult = UNONE;
3342         }
3343 
3344         return theResult;
3345 }
3346 
3347 OpenedResourceFile ExternalResources;
3348 
set_external_resources_file(FileSpecifier & f)3349 void set_external_resources_file(FileSpecifier& f)
3350 {
3351 	f.Open(ExternalResources);
3352 }
3353 
close_external_resources()3354 void close_external_resources()
3355 {
3356 	ExternalResources.Close();
3357 }
3358