2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/GameSequence/GameSequence.cpp $
15 * File to control Game Sequencing
18 * Revision 1.2 2002/06/09 04:41:17 relnev
19 * added copyright header
21 * Revision 1.1.1.1 2002/05/03 03:28:09 root
25 * 3 2/23/99 2:29p Dave
26 * First run of oldschool dogfight mode.
28 * 2 10/07/98 10:52a Dave
31 * 1 10/07/98 10:48a Dave
33 * 65 5/15/98 12:09a Dave
34 * New tracker api code. New game tracker code. Finished up first run of
35 * the PXO screen. Fixed a few game server list exceptions.
37 * 64 5/12/98 2:46a Dave
38 * Rudimentary communication between Parallax Online and freespace. Can
39 * get and store channel lists.
41 * 63 5/06/98 1:12a Allender
42 * fix sequencing names, added nprintf to help respawn debugging
44 * 62 4/25/98 7:39p Allender
45 * fixd some small hotkey stuff. Worked on turret orientation being
46 * correct for multiplayer. new sexpression called end-campaign will will
47 * end the main campaign
49 * 61 4/23/98 7:08p John
50 * Removed some obsoleted states.
52 * 60 4/16/98 4:31p Hoffoss
53 * Changed demo screen referenced to view cutscenes screen, which is now
56 * 59 4/02/98 5:40p Hoffoss
57 * Added the Load Mission screen to FreeSpace.
59 * 58 3/11/98 5:32p Lawrance
60 * Fix up text arrays for events/states
62 * 57 3/09/98 12:13a Lawrance
63 * Add support for Red Alert missions
65 * 56 3/05/98 4:12p John
66 * Made Debug+F4 switch Glide and windowed.
68 * 55 3/03/98 1:00p Hoffoss
69 * Added new command briefing event and state.
71 * 54 3/02/98 4:23p Hoffoss
72 * Forgot to add state label.
74 * 53 3/02/98 3:44p Hoffoss
75 * Added new Campaign Room state and event.
77 * 52 2/21/98 11:58a John
78 * Put in some stuff to externalize strings
80 * 51 2/19/98 6:26p Dave
81 * Fixed a few file xfer bugs. Tweaked mp team select screen. Put in
82 * initial support for player data uploading.
84 * 50 2/18/98 10:21p Dave
85 * Ripped out old file xfer system. Put in brand new xfer system.
87 * 49 2/08/98 5:07p Dave
88 * Put in support for multiplayer furball mode.
90 * 48 1/28/98 6:21p Dave
91 * Made the standalone use ~8 megs less memory. Fixed multiplayer submenu
94 * 47 1/23/98 5:43p Dave
95 * Finished bringing standalone up to speed. Coded in new host options
98 * 46 1/22/98 5:25p Dave
99 * Modified some pregame sequencing packets. Starting to repair broken
102 * 45 1/22/98 4:15p Hoffoss
103 * Added code to allow popup to tell player he needs to bind a key for the
106 * 44 1/20/98 5:42p Dave
107 * Moved ingame join to its own module. Improved it a bit.
109 * 43 1/19/98 12:57p Allender
110 * removed confusing comment
112 * 42 1/19/98 12:56p Allender
113 * fix problem of freespace_start_misison possibly failing due to mission
114 * not properly loading (for single player only right now).
116 * 41 1/15/98 6:12p Dave
117 * Fixed weapons loadout bugs with multiplayer respawning. Added
118 * multiplayer start screen. Fixed a few chatbox bugs.
120 * 40 1/15/98 6:00p Hoffoss
121 * Added option to quit menu (in game) to restart the mission. Doesn't
122 * seem to quite work, though. Checking code in so someone else can look
125 * 39 1/05/98 10:05a Dave
126 * Big re-sequencing of server transfer. Centralized _all_ server transfer
127 * code to one module.
129 * 38 12/30/97 4:28p Lawrance
130 * Give text descriptions for events, change debug output to give text
131 * desciption of event/state
133 * 37 12/24/97 8:56p Lawrance
134 * took out obsolete state used for non-existant sound config screen
136 * 36 11/19/97 8:28p Dave
137 * Hooked in Main Hall screen. Put in Anim support for ping ponging
138 * animations as well as general reversal of anim direction.
140 * 35 11/15/97 2:36p Dave
141 * Added more multiplayer campaign support.
143 * 34 11/13/97 7:01p Hoffoss
144 * Fixed GS_state_text[], which didn't match the current states we have
147 * 33 11/10/97 6:02p Hoffoss
148 * Added new debug paused state.
150 * 32 11/03/97 10:12p Hoffoss
151 * Finished up work on the hud message/mission log scrollback screen.
153 * 31 10/22/97 11:00p Lawrance
154 * modify pop_and_discard() to allow discarding of all states on the stack
156 * 30 10/22/97 5:08p John
157 * fixed a whole slew of bugs and clean up a bunch of stuff dealing with
158 * end of mission stuff.
160 * 29 10/02/97 9:49p Hoffoss
161 * Added event evaluation analysis debug screen so we can determine the
162 * state of events and their sexp trees to track down logic problems and
165 * 28 9/23/97 11:53p Lawrance
166 * add state do perform multiplayer on-line help
168 * 27 9/22/97 4:55p Hoffoss
169 * Added a training message window display thingy.
171 * 26 9/19/97 4:24p Allender
172 * added team selection state -- initialze player* variable in
175 * 25 9/18/97 10:17p Lawrance
176 * add help state for briefing
178 * 24 9/18/97 10:15p Lawrance
179 * add help state for briefing
181 * 23 7/14/97 12:03a Lawrance
184 * 22 6/13/97 2:30p Lawrance
187 * 21 5/20/97 10:02a Lawrance
188 * added view medals screen
190 * 20 4/28/97 2:17p Lawrance
191 * added help state for hotkey assignment screen
193 * 19 4/25/97 3:41p Lawrance
194 * added support for hotkey assignment screen
196 * 18 4/23/97 9:54a Lawrance
197 * made show goals screen a separate state
199 * 17 4/22/97 11:06a Lawrance
200 * added credits state
202 * 16 4/17/97 9:01p Allender
203 * start of campaign stuff. Campaigns now stored in external file (no
204 * filenames in code). Continuing campaign won't work at this point
206 * 15 4/03/97 8:40p Lawrance
207 * add new player death states to GS_state_text[]
209 * 14 3/05/97 5:04p Lawrance
210 * added new states for different context help
212 * 13 1/09/97 12:41a Lawrance
213 * added function to pop a state without restoring that state
215 * 12 12/22/96 3:41p Lawrance
216 * integrating energy transfer system
218 * 11 12/09/96 2:35p Allender
219 * modifed game sequencing so that game_leave_state and game_enter_state
220 * are *always* called.
222 * 10 12/08/96 1:54a Lawrance
223 * put check in to see if a state change request is invalid (ie already in
226 * 9 11/18/96 5:07p John
227 * Changed sequencing code to call entry,leave functions for each state
228 * change. Added Shift+Pause debug pause thing.
230 * 8 11/13/96 4:02p Lawrance
231 * complete over-haul of the menu system and the states associated with
234 * 7 10/23/96 9:08a Allender
235 * Removed primary and secondary goal complete states -- to be implemented
241 * All states for game sequencing are defined in GameSequence.h.
242 * States should always be referred to using the macros.
245 #include "freespace.h"
246 #include "gamesequence.h"
249 #define MAX_GAMESEQ_EVENTS 20 // maximum number of events on the game sequencing queue
250 #define GS_STACK_SIZE 10 // maximum number of stacked states
253 typedef struct state_stack {
255 int event_queue[MAX_GAMESEQ_EVENTS];
256 int queue_tail, queue_head;
259 // DO NOT MAKE THIS NON-STATIC!!!!
260 static state_stack gs[GS_STACK_SIZE];
261 static int gs_current_stack = -1; // index of top state on stack.
263 static int state_reentry = 0; // set if we are already in state processing
264 static int state_processing_event_post = 0; // set if we are already processing an event to switch states
265 static int state_in_event_processer = 0;
267 // Text of state, corresponding to #define values for GS_STATE_*
269 const char *GS_event_text[] =
271 "GS_EVENT_MAIN_MENU",
272 "GS_EVENT_START_GAME",
273 "GS_EVENT_ENTER_GAME",
274 "GS_EVENT_START_GAME_QUICK",
276 "GS_EVENT_QUIT_GAME", // 5
277 "GS_EVENT_PAUSE_GAME",
278 "GS_EVENT_PREVIOUS_STATE",
279 "GS_EVENT_OPTIONS_MENU",
280 "GS_EVENT_BARRACKS_MENU",
281 "GS_EVENT_TRAINING_MENU", // 10
282 "GS_EVENT_TECH_MENU",
283 "GS_EVENT_LOAD_MISSION_MENU",
284 "GS_EVENT_SHIP_SELECTION",
285 "GS_EVENT_TOGGLE_FULLSCREEN",
286 "GS_EVENT_WEAPON_SELECT_HELP", // 15
287 "GS_EVENT_START_BRIEFING",
288 "GS_EVENT_DEBUG_PAUSE_GAME",
289 "GS_EVENT_HUD_CONFIG",
290 "GS_EVENT_MULTI_SETUP",
291 "GS_EVENT_MULTI_JOIN_GAME", // 20
292 "GS_EVENT_CONTROL_CONFIG",
293 "GS_EVENT_EVENT_DEBUG",
294 "GS_EVENT_MULTI_PROTO_CHOICE",
295 "GS_EVENT_SAVE_RESTORE",
296 "GS_EVENT_CHOOSE_SAVE_OR_RESTORE", // 25
297 "GS_EVENT_WEAPON_SELECTION",
298 "GS_EVENT_MISSION_LOG_SCROLLBACK",
299 "GS_EVENT_MAIN_MENU_HELP",
300 "GS_EVENT_GAMEPLAY_HELP",
301 "GS_EVENT_SHIP_SELECT_HELP", // 30
302 "GS_EVENT_DEATH_DIED",
303 "GS_EVENT_DEATH_BLEW_UP",
304 "GS_EVENT_NEW_CAMPAIGN",
306 "GS_EVENT_SHOW_GOALS", // 35
307 "GS_EVENT_HOTKEY_SCREEN",
308 "GS_EVENT_HOTKEY_SCREEN_HELP",
309 "GS_EVENT_VIEW_MEDALS",
310 "GS_EVENT_MULTI_HOST_SETUP",
311 "GS_EVENT_MULTI_CLIENT_SETUP", // 40
314 "GS_EVENT_MULTI_JOIN_TRACKER",
315 "GS_EVENT_GOTO_VIEW_CUTSCENES_SCREEN",
316 "GS_EVENT_MULTI_STD_WAIT", // 45
317 "GS_EVENT_STANDALONE_MAIN",
318 "GS_EVENT_MULTI_PAUSE",
319 "GS_EVENT_BRIEFING_HELP",
320 "GS_EVENT_TEAM_SELECT",
321 "GS_EVENT_TRAINING_PAUSE", // 50
322 "GS_EVENT_MULTI_HELP",
323 "GS_EVENT_INGAME_PRE_JOIN",
324 "GS_EVENT_PLAYER_WARPOUT_START",
325 "GS_EVENT_PLAYER_WARPOUT_START_FORCED",
326 "GS_EVENT_PLAYER_WARPOUT_STOP", // 55
327 "GS_EVENT_PLAYER_WARPOUT_DONE_STAGE1",
328 "GS_EVENT_PLAYER_WARPOUT_DONE_STAGE2",
329 "GS_EVENT_PLAYER_WARPOUT_DONE",
330 "GS_EVENT_STANDALONE_POSTGAME",
331 "GS_EVENT_INITIAL_PLAYER_SELECT", // 60
332 "GS_EVENT_GAME_INIT",
333 "GS_EVENT_MULTI_MISSION_SYNC",
334 "GS_EVENT_MULTI_CAMPAIGN_SELECT",
335 "GS_EVENT_MULTI_SERVER_TRANSFER",
336 "GS_EVENT_MULTI_START_GAME", // 65
337 "GS_EVENT_MULTI_HOST_OPTIONS",
338 "GS_EVENT_MULTI_DOGFIGHT_DEBRIEF",
339 "GS_EVENT_CAMPAIGN_ROOM",
340 "GS_EVENT_CMD_BRIEF",
341 "GS_EVENT_TOGGLE_GLIDE", // 70
342 "GS_EVENT_RED_ALERT",
343 "GS_EVENT_SIMULATOR_ROOM",
344 "GS_EVENT_EMD_CAMPAIGN",
350 // Text of state, corresponding to #define values for GS_STATE_*
352 const char *GS_state_text[] =
355 "GS_STATE_MAIN_MENU", // 1
356 "GS_STATE_GAME_PLAY",
357 "GS_STATE_GAME_PAUSED",
358 "GS_STATE_QUIT_GAME",
359 "GS_STATE_OPTIONS_MENU", // 5
360 "GS_EVENT_WEAPON_SELECT_HELP",
361 "GS_STATE_BARRACKS_MENU",
362 "GS_STATE_TECH_MENU",
363 "GS_STATE_TRAINING_MENU",
364 "GS_STATE_LOAD_MISSION_MENU", // 10
366 "GS_STATE_SHIP_SELECT",
367 "GS_STATE_DEBUG_PAUSED",
368 "GS_STATE_HUD_CONFIG",
369 "GS_STATE_MULTI_SETUP", // 15
370 "GS_STATE_MULTI_JOIN_GAME",
371 "GS_STATE_CONTROL_CONFIG",
372 "GS_STATE_MULTI_PROTO_CHOICE",
373 "GS_STATE_SAVE_RESTORE",
374 "GS_STATE_WEAPON_SELECT", // 20
375 "GS_STATE_MISSION_LOG_SCROLLBACK",
376 "GS_STATE_MAIN_MENU_HELP",
377 "GS_STATE_GAMEPLAY_HELP",
378 "GS_STATE_SHIP_SELECT_HELP",
379 "GS_STATE_DEATH_DIED", // 25
380 "GS_STATE_DEATH_BLEW_UP",
381 "GS_STATE_SIMULATOR_ROOM",
383 "GS_STATE_SHOW_GOALS",
384 "GS_STATE_HOTKEY_SCREEN", // 30
385 "GS_STATE_HOTKEY_SCREEN_HELP",
386 "GS_STATE_VIEW_MEDALS",
387 "GS_STATE_MULTI_HOST_SETUP",
388 "GS_STATE_MULTI_CLIENT_SETUP",
389 "GS_STATE_DEBRIEF", // 35
391 "GS_STATE_MULTI_JOIN_TRACKER",
392 "GS_STATE_VIEW_CUTSCENES",
393 "GS_STATE_MULTI_STD_WAIT",
394 "GS_STATE_STANDALONE_MAIN", // 40
395 "GS_STATE_MULTI_PAUSED",
396 "GS_STATE_BRIEFING_HELP",
397 "GS_STATE_TEAM_SELECT",
398 "GS_STATE_TRAINING_PAUSED",
399 "GS_STATE_MULTI_HELP", // 45
400 "GS_STATE_INGAME_PRE_JOIN",
401 "GS_STATE_EVENT_DEBUG",
402 "GS_STATE_STANDALONE_POSTGAME",
403 "GS_STATE_INITIAL_PLAYER_SELECT",
404 "GS_STATE_MULTI_MISSION_SYNC", // 50
405 "GS_STATE_MULTI_SERVER_TRANSFER",
406 "GS_STATE_MULTI_START_GAME",
407 "GS_STATE_MULTI_HOST_OPTIONS",
408 "GS_STATE_MULTI_DOGFIGHT_DEBRIEF",
409 "GS_STATE_CAMPAIGN_ROOM", // 55
410 "GS_STATE_CMD_BRIEF",
411 "GS_STATE_RED_ALERT",
412 "GS_STATE_END_OF_CAMPAIGN",
414 "GS_STATE_PXO_HELP" // 60
422 for (i=0; i<GS_STACK_SIZE; i++ ) {
423 // gs[i].current_state = GS_STATE_MAIN_MENU;
424 gs[i].current_state = 0;
429 gs_current_stack = 0;
431 state_processing_event_post = 0;
432 state_in_event_processer = 0;
435 // gameseq_post_event posts a new game sequencing event onto the gameseq
438 void gameseq_post_event( int event )
440 if (state_processing_event_post) {
441 nprintf(("Warning", "Received post for event %s during state transtition. Find Allender if you are unsure if this is bad.\n", GS_event_text[event] ));
444 SDL_assert(gs[gs_current_stack].queue_tail < MAX_GAMESEQ_EVENTS);
445 gs[gs_current_stack].event_queue[gs[gs_current_stack].queue_tail++] = event;
446 if ( gs[gs_current_stack].queue_tail == MAX_GAMESEQ_EVENTS )
447 gs[gs_current_stack].queue_tail = 0;
450 // returns one of the GS_EVENT_ id's on the game sequencing queue
452 int gameseq_get_event()
456 if ( gs[gs_current_stack].queue_head == gs[gs_current_stack].queue_tail )
458 event = gs[gs_current_stack].event_queue[gs[gs_current_stack].queue_head++];
459 if ( gs[gs_current_stack].queue_head == MAX_GAMESEQ_EVENTS )
460 gs[gs_current_stack].queue_head = 0;
465 // returns one of the GS_STATE_ macros
466 int gameseq_get_state(int depth)
468 SDL_assert(depth <= gs_current_stack);
470 return gs[gs_current_stack - depth].current_state;
473 int gameseq_get_depth()
475 return gs_current_stack;
478 void gameseq_set_state(int new_state, int override)
480 int event, old_state;
482 if ( (new_state == gs[gs_current_stack].current_state) && !override )
485 old_state = gs[gs_current_stack].current_state;
487 // Flush all events!!
488 while ( (event = gameseq_get_event()) != -1 ) {
489 mprintf(( "Throwing out event %d because of state set from %d to %d\n", event, old_state, new_state ));
492 SDL_assert( state_reentry == 1 ); // Get John! (Invalid state sequencing!)
493 SDL_assert( state_in_event_processer == 1 ); // can only call from game_process_event
495 state_processing_event_post++;
497 game_leave_state(gs[gs_current_stack].current_state,new_state);
499 gs[gs_current_stack].current_state = new_state;
501 game_enter_state(old_state,gs[gs_current_stack].current_state);
503 state_processing_event_post--;
506 void gameseq_push_state( int new_state )
508 if ( new_state == gs[gs_current_stack].current_state )
511 int old_state = gs[gs_current_stack].current_state;
513 // Flush all events!!
514 // I commented out because I'm not sure if we should throw out events when pushing or not.
516 // while( (event = gameseq_get_event()) != -1 ) {
517 // mprintf(( "Throwing out event %d because of state push from %d to %d\n", event, old_state, new_state ));
520 SDL_assert( state_reentry == 1 ); // Get John! (Invalid state sequencing!)
521 SDL_assert( state_in_event_processer == 1 ); // can only call from game_process_event
524 SDL_assert(gs_current_stack < GS_STACK_SIZE);
526 state_processing_event_post++;
528 game_leave_state(old_state,new_state);
530 gs[gs_current_stack].current_state = new_state;
531 gs[gs_current_stack].queue_tail = 0;
532 gs[gs_current_stack].queue_head = 0;
534 game_enter_state(old_state,gs[gs_current_stack].current_state);
536 state_processing_event_post--;
539 void gameseq_pop_state()
541 int popped_state = 0;
543 SDL_assert(state_reentry == 1); // Get John! (Invalid state sequencing!)
545 if (gs_current_stack >= 1) {
548 // set the old state to be the state which is about to be popped off the queue
549 old_state = gs[gs_current_stack].current_state;
551 // set the popped_state to be the state which is going to be moved into
552 popped_state = gs[gs_current_stack-1].current_state;
554 // leave the current state
556 game_leave_state(gs[gs_current_stack].current_state,popped_state);
558 // set the popped_state to be the one we moved into
561 // swap all remaining events from the state which just got popped to this new state
562 while(gs[gs_current_stack+1].queue_head != gs[gs_current_stack+1].queue_tail){
563 gameseq_post_event(gs[gs_current_stack+1].event_queue[gs[gs_current_stack+1].queue_head++]);
566 game_enter_state(old_state, gs[gs_current_stack].current_state);
573 // gameseq_pop_and_discard_state() is used to remove a state that was pushed onto the stack, but
574 // will never need to be popped. An example of this is entering a state that may require returning
575 // to the previous state (then you would call gameseq_pop_state). Or you may simply continue to
576 // another part of the game, to avoid filling up the stack with states that may never be popped, you
577 // call this function to discard the top of the gs.
580 void gameseq_pop_and_discard_state()
582 if (gs_current_stack > 0 ) {
587 // Returns the last state pushed on stack
588 int gameseq_get_pushed_state()
590 if (gs_current_stack >= 1) {
591 return gs[gs_current_stack-1].current_state;
596 // gameseq_process_events gets called every time through high level loops
597 // (i.e. game loops, main menu loop). Function is responsible for pulling
598 // game sequence events off the queue and changing the state when necessary.
599 // Returns the current state.
600 // pull events game sequence events off of the queue. Process one at a time
601 // based on the current state and the new event.
603 int gameseq_process_events()
605 int event, old_state;
606 old_state = gs[gs_current_stack].current_state;
608 SDL_assert(state_reentry == 0); // Get John! (Invalid state sequencing!)
610 while ( (event = gameseq_get_event()) != -1 ) {
612 state_in_event_processer++;
613 game_process_event(gs[gs_current_stack].current_state, event);
614 state_in_event_processer--;
616 // break when state changes so that code will get called at
617 // least one frame for each state.
618 if (old_state != gs[gs_current_stack].current_state)
623 game_do_state(gs[gs_current_stack].current_state);
626 return gs[gs_current_stack].current_state;