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