]> icculus.org git repositories - taylor/freespace2.git/blob - src/gamesequence/gamesequence.cpp
36e5fa2587680d6191ef1d850e18ff203e543472
[taylor/freespace2.git] / src / gamesequence / gamesequence.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/GameSequence/GameSequence.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * File to control Game Sequencing
16  *
17  * $Log$
18  * Revision 1.2  2002/06/09 04:41:17  relnev
19  * added copyright header
20  *
21  * Revision 1.1.1.1  2002/05/03 03:28:09  root
22  * Initial import.
23  *
24  * 
25  * 3     2/23/99 2:29p Dave
26  * First run of oldschool dogfight mode. 
27  * 
28  * 2     10/07/98 10:52a Dave
29  * Initial checkin.
30  * 
31  * 1     10/07/98 10:48a Dave
32  * 
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.
36  * 
37  * 64    5/12/98 2:46a Dave
38  * Rudimentary communication between Parallax Online and freespace. Can
39  * get and store channel lists.
40  * 
41  * 63    5/06/98 1:12a Allender
42  * fix sequencing names, added nprintf to help respawn debugging
43  * 
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
48  * 
49  * 61    4/23/98 7:08p John
50  * Removed some obsoleted states.
51  * 
52  * 60    4/16/98 4:31p Hoffoss
53  * Changed demo screen referenced to view cutscenes screen, which is now
54  * what it's called.
55  * 
56  * 59    4/02/98 5:40p Hoffoss
57  * Added the Load Mission screen to FreeSpace.
58  * 
59  * 58    3/11/98 5:32p Lawrance
60  * Fix up text arrays for events/states
61  * 
62  * 57    3/09/98 12:13a Lawrance
63  * Add support for Red Alert missions
64  * 
65  * 56    3/05/98 4:12p John
66  * Made Debug+F4 switch Glide and windowed.
67  * 
68  * 55    3/03/98 1:00p Hoffoss
69  * Added new command briefing event and state.
70  * 
71  * 54    3/02/98 4:23p Hoffoss
72  * Forgot to add state label.
73  * 
74  * 53    3/02/98 3:44p Hoffoss
75  * Added new Campaign Room state and event.
76  * 
77  * 52    2/21/98 11:58a John
78  * Put in some stuff to externalize strings
79  * 
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.
83  * 
84  * 50    2/18/98 10:21p Dave
85  * Ripped out old file xfer system. Put in brand new xfer system.
86  * 
87  * 49    2/08/98 5:07p Dave
88  * Put in support for multiplayer furball mode.
89  * 
90  * 48    1/28/98 6:21p Dave
91  * Made the standalone use ~8 megs less memory. Fixed multiplayer submenu
92  * endgame problem.
93  * 
94  * 47    1/23/98 5:43p Dave
95  * Finished bringing standalone up to speed. Coded in new host options
96  * screen.
97  * 
98  * 46    1/22/98 5:25p Dave
99  * Modified some pregame sequencing packets. Starting to repair broken
100  * standalone stuff.
101  * 
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
104  * training mission.
105  * 
106  * 44    1/20/98 5:42p Dave
107  * Moved ingame join to its own module. Improved it a bit.
108  * 
109  * 43    1/19/98 12:57p Allender
110  * removed confusing comment
111  * 
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).
115  * 
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.
119  * 
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
123  * into it.
124  * 
125  * 39    1/05/98 10:05a Dave
126  * Big re-sequencing of server transfer. Centralized _all_ server transfer
127  * code to one module.
128  * 
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
132  * 
133  * 37    12/24/97 8:56p Lawrance
134  * took out obsolete state used for non-existant sound config screen
135  * 
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.
139  * 
140  * 35    11/15/97 2:36p Dave
141  * Added more multiplayer campaign support.
142  * 
143  * 34    11/13/97 7:01p Hoffoss
144  * Fixed GS_state_text[], which didn't match the current states we have
145  * available.
146  * 
147  * 33    11/10/97 6:02p Hoffoss
148  * Added new debug paused state.
149  * 
150  * 32    11/03/97 10:12p Hoffoss
151  * Finished up work on the hud message/mission log scrollback screen.
152  * 
153  * 31    10/22/97 11:00p Lawrance
154  * modify pop_and_discard() to allow discarding of all states on the stack
155  * 
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.
159  * 
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
163  * such.
164  * 
165  * 28    9/23/97 11:53p Lawrance
166  * add state do perform multiplayer on-line help
167  * 
168  * 27    9/22/97 4:55p Hoffoss
169  * Added a training message window display thingy.
170  * 
171  * 26    9/19/97 4:24p Allender
172  * added team selection state -- initialze player* variable in
173  * player_level_init
174  * 
175  * 25    9/18/97 10:17p Lawrance
176  * add help state for briefing
177  * 
178  * 24    9/18/97 10:15p Lawrance
179  * add help state for briefing
180  * 
181  * 23    7/14/97 12:03a Lawrance
182  * added navmap state
183  * 
184  * 22    6/13/97 2:30p Lawrance
185  * Added debriefings
186  * 
187  * 21    5/20/97 10:02a Lawrance
188  * added view medals screen
189  * 
190  * 20    4/28/97 2:17p Lawrance
191  * added help state for hotkey assignment screen
192  * 
193  * 19    4/25/97 3:41p Lawrance
194  * added support for hotkey assignment screen
195  * 
196  * 18    4/23/97 9:54a Lawrance
197  * made show goals screen a separate state
198  * 
199  * 17    4/22/97 11:06a Lawrance
200  * added credits state
201  * 
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
205  * 
206  * 15    4/03/97 8:40p Lawrance
207  * add new player death states to GS_state_text[]
208  * 
209  * 14    3/05/97 5:04p Lawrance
210  * added new states for different context help
211  * 
212  * 13    1/09/97 12:41a Lawrance
213  * added function to pop a state without restoring that state
214  * 
215  * 12    12/22/96 3:41p Lawrance
216  * integrating energy transfer system
217  * 
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.
221  * 
222  * 10    12/08/96 1:54a Lawrance
223  * put check in to see if a state change request is invalid (ie already in
224  * that state)
225  * 
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.
229  * 
230  * 8     11/13/96 4:02p Lawrance
231  * complete over-haul of the menu system and the states associated with
232  * them
233  * 
234  * 7     10/23/96 9:08a Allender
235  * Removed primary and secondary goal complete states -- to be implemented
236  * later.
237  *
238 */
239
240 /*
241  *  All states for game sequencing are defined in GameSequence.h.
242  *  States should always be referred to using the macros.
243 */
244
245 #include "freespace.h"
246 #include "gamesequence.h"
247
248 // local defines
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
251
252 // local variables
253 typedef struct state_stack {
254         int     current_state;
255         int     event_queue[MAX_GAMESEQ_EVENTS];
256         int     queue_tail, queue_head;
257 } state_stack;
258
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.
262
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;
266
267 // Text of state, corresponding to #define values for GS_STATE_*
268 //XSTR:OFF
269 const char *GS_event_text[] =
270 {
271         "GS_EVENT_MAIN_MENU",                                   // 0
272         "GS_EVENT_START_GAME",
273         "GS_EVENT_ENTER_GAME",
274         "GS_EVENT_START_GAME_QUICK",
275         "GS_EVENT_END_GAME",
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_START_BRIEFING",                              // 15
287         "GS_EVENT_DEBUG_PAUSE_GAME",
288         "GS_EVENT_HUD_CONFIG",
289         "GS_EVENT_MULTI_JOIN_GAME",
290         "GS_EVENT_CONTROL_CONFIG",
291         "GS_EVENT_EVENT_DEBUG",                                 // 20
292         "GS_EVENT_WEAPON_SELECTION",
293         "GS_EVENT_MISSION_LOG_SCROLLBACK",
294         "GS_EVENT_GAMEPLAY_HELP",
295         "GS_EVENT_DEATH_DIED",
296         "GS_EVENT_DEATH_BLEW_UP",                               // 25
297         "GS_EVENT_NEW_CAMPAIGN",
298         "GS_EVENT_CREDITS",
299         "GS_EVENT_SHOW_GOALS",
300         "GS_EVENT_HOTKEY_SCREEN",
301         "GS_EVENT_VIEW_MEDALS",                                 // 30
302         "GS_EVENT_MULTI_HOST_SETUP",
303         "GS_EVENT_MULTI_CLIENT_SETUP",
304         "GS_EVENT_DEBRIEF",
305         "GS_EVENT_GOTO_VIEW_CUTSCENES_SCREEN",
306         "GS_EVENT_MULTI_STD_WAIT",                              // 35
307         "GS_EVENT_STANDALONE_MAIN",
308         "GS_EVENT_MULTI_PAUSE",
309         "GS_EVENT_TEAM_SELECT",
310         "GS_EVENT_TRAINING_PAUSE",
311         "GS_EVENT_INGAME_PRE_JOIN",                             // 40
312         "GS_EVENT_PLAYER_WARPOUT_START",
313         "GS_EVENT_PLAYER_WARPOUT_START_FORCED",
314         "GS_EVENT_PLAYER_WARPOUT_STOP",
315         "GS_EVENT_PLAYER_WARPOUT_DONE_STAGE1",
316         "GS_EVENT_PLAYER_WARPOUT_DONE_STAGE2",  // 45
317         "GS_EVENT_PLAYER_WARPOUT_DONE",
318         "GS_EVENT_STANDALONE_POSTGAME",
319         "GS_EVENT_INITIAL_PLAYER_SELECT",
320         "GS_EVENT_GAME_INIT",
321         "GS_EVENT_MULTI_MISSION_SYNC",                  // 50
322         "GS_EVENT_MULTI_START_GAME",
323         "GS_EVENT_MULTI_HOST_OPTIONS",
324         "GS_EVENT_MULTI_DOGFIGHT_DEBRIEF",
325         "GS_EVENT_CAMPAIGN_ROOM",
326         "GS_EVENT_CMD_BRIEF",                                   // 55
327         "GS_EVENT_TOGGLE_GLIDE",
328         "GS_EVENT_RED_ALERT",
329         "GS_EVENT_SIMULATOR_ROOM",
330         "GS_EVENT_EMD_CAMPAIGN",
331         "GS_EVENT_END_DEMO",                                    // 60
332         "GS_EVENT_LOOP_BRIEF",
333         "GS_EVENT_CAMPAIGN_CHEAT",
334         "GS_EVENT_PXO",
335         "GS_EVENT_PXO_HELP",
336         "GS_EVENT_DEMO_UPSELL"
337 };
338 //XSTR:ON
339
340 // Text of state, corresponding to #define values for GS_STATE_*
341 //XSTR:OFF
342 const char *GS_state_text[] =
343 {
344         "NOT A VALID STATE",
345         "GS_STATE_MAIN_MENU",                           // 1
346         "GS_STATE_GAME_PLAY",
347         "GS_STATE_GAME_PAUSED",
348         "GS_STATE_QUIT_GAME",
349         "GS_STATE_OPTIONS_MENU",                        // 5
350         "GS_STATE_BARRACKS_MENU",
351         "GS_STATE_TECH_MENU",
352         "GS_STATE_TRAINING_MENU",
353         "GS_STATE_LOAD_MISSION_MENU",
354         "GS_STATE_BRIEFING",                            // 10
355         "GS_STATE_SHIP_SELECT",
356         "GS_STATE_DEBUG_PAUSED",
357         "GS_STATE_HUD_CONFIG",
358         "GS_STATE_MULTI_JOIN_GAME",
359         "GS_STATE_CONTROL_CONFIG",                      // 15
360         "GS_STATE_WEAPON_SELECT",
361         "GS_STATE_MISSION_LOG_SCROLLBACK",
362         "GS_STATE_DEATH_DIED",
363         "GS_STATE_DEATH_BLEW_UP",
364         "GS_STATE_SIMULATOR_ROOM",                      // 20
365         "GS_STATE_CREDITS",
366         "GS_STATE_SHOW_GOALS",
367         "GS_STATE_HOTKEY_SCREEN",
368         "GS_STATE_VIEW_MEDALS",
369         "GS_STATE_MULTI_HOST_SETUP",            // 25
370         "GS_STATE_MULTI_CLIENT_SETUP",
371         "GS_STATE_DEBRIEF",
372         "GS_STATE_VIEW_CUTSCENES",
373         "GS_STATE_MULTI_STD_WAIT",
374         "GS_STATE_STANDALONE_MAIN",                     // 30
375         "GS_STATE_MULTI_PAUSED",
376         "GS_STATE_TEAM_SELECT",
377         "GS_STATE_TRAINING_PAUSED",
378         "GS_STATE_INGAME_PRE_JOIN",
379         "GS_STATE_EVENT_DEBUG",                         // 35
380         "GS_STATE_STANDALONE_POSTGAME",
381         "GS_STATE_INITIAL_PLAYER_SELECT",
382         "GS_STATE_MULTI_MISSION_SYNC",
383         "GS_STATE_MULTI_START_GAME",
384         "GS_STATE_MULTI_HOST_OPTIONS",          // 40
385         "GS_STATE_MULTI_DOGFIGHT_DEBRIEF",
386         "GS_STATE_CAMPAIGN_ROOM",
387         "GS_STATE_CMD_BRIEF",
388         "GS_STATE_RED_ALERT",
389         "GS_STATE_END_OF_CAMPAIGN",                     // 45
390         "GS_STATE_GAMEPLAY_HELP",
391         "GS_STATE_END_DEMO",
392         "GS_STATE_LOOP_BRIEF",
393         "GS_STATE_PXO",
394         "GS_STATE_PXO_HELP",                                    // 50
395         "GS_STATE_DEMO_UPSELL"
396 };
397 //XSTR:ON
398
399 void gameseq_init()
400 {
401         int i;
402
403         for (i=0; i<GS_STACK_SIZE; i++ )        {
404                 // gs[i].current_state = GS_STATE_MAIN_MENU;
405                 gs[i].current_state = 0;
406                 gs[i].queue_tail=0;
407                 gs[i].queue_head=0;
408         }
409
410         gs_current_stack = 0;
411         state_reentry = 0;
412         state_processing_event_post = 0;
413         state_in_event_processer = 0;
414 }
415
416 // gameseq_post_event posts a new game sequencing event onto the gameseq
417 // event queue
418
419 void gameseq_post_event( int event )
420 {
421         if (state_processing_event_post) {
422                 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] ));
423         }
424
425         SDL_assert(gs[gs_current_stack].queue_tail < MAX_GAMESEQ_EVENTS);
426         gs[gs_current_stack].event_queue[gs[gs_current_stack].queue_tail++] = event;
427         if ( gs[gs_current_stack].queue_tail == MAX_GAMESEQ_EVENTS )
428                 gs[gs_current_stack].queue_tail = 0;
429 }
430
431 // returns one of the GS_EVENT_ id's on the game sequencing queue
432
433 int gameseq_get_event()
434 {
435         int event;
436
437         if ( gs[gs_current_stack].queue_head == gs[gs_current_stack].queue_tail )
438                 return -1;
439         event = gs[gs_current_stack].event_queue[gs[gs_current_stack].queue_head++];
440         if ( gs[gs_current_stack].queue_head == MAX_GAMESEQ_EVENTS )
441                 gs[gs_current_stack].queue_head = 0;
442
443         return event;
444 }          
445
446 // returns one of the GS_STATE_ macros
447 int gameseq_get_state(int depth)
448 {       
449         SDL_assert(depth <= gs_current_stack);
450                         
451         return gs[gs_current_stack - depth].current_state;
452 }
453
454 int gameseq_get_depth()
455 {
456         return gs_current_stack;
457 }
458
459 void gameseq_set_state(int new_state, int override)
460 {
461         int event, old_state;
462
463         if ( (new_state == gs[gs_current_stack].current_state) && !override )
464                 return;
465
466         old_state = gs[gs_current_stack].current_state;
467
468         // Flush all events!!
469         while ( (event = gameseq_get_event()) != -1 ) {
470                 mprintf(( "Throwing out event %d because of state set from %d to %d\n", event, old_state, new_state ));
471         }
472
473         SDL_assert( state_reentry == 1 );               // Get John! (Invalid state sequencing!)
474         SDL_assert( state_in_event_processer == 1 );            // can only call from game_process_event
475
476         state_processing_event_post++;
477         state_reentry++;
478         game_leave_state(gs[gs_current_stack].current_state,new_state);
479
480         gs[gs_current_stack].current_state = new_state;
481
482         game_enter_state(old_state,gs[gs_current_stack].current_state);
483         state_reentry--;
484         state_processing_event_post--;
485 }
486         
487 void gameseq_push_state( int new_state )
488 {
489         if ( new_state == gs[gs_current_stack].current_state )
490                 return;
491
492         int old_state = gs[gs_current_stack].current_state;
493
494         // Flush all events!!
495 // I commented out because I'm not sure if we should throw out events when pushing or not.
496 //      int event;
497 //      while( (event = gameseq_get_event()) != -1 )    {
498 //              mprintf(( "Throwing out event %d because of state push from %d to %d\n", event, old_state, new_state ));
499 //      }
500
501         SDL_assert( state_reentry == 1 );               // Get John! (Invalid state sequencing!)
502         SDL_assert( state_in_event_processer == 1 );            // can only call from game_process_event
503
504         gs_current_stack++;
505         SDL_assert(gs_current_stack < GS_STACK_SIZE);
506
507         state_processing_event_post++;
508         state_reentry++;
509         game_leave_state(old_state,new_state);
510
511         gs[gs_current_stack].current_state = new_state;
512         gs[gs_current_stack].queue_tail = 0;
513         gs[gs_current_stack].queue_head = 0;
514
515         game_enter_state(old_state,gs[gs_current_stack].current_state);
516         state_reentry--;
517         state_processing_event_post--;
518 }
519
520 void gameseq_pop_state()
521 {
522         int popped_state = 0;
523
524         SDL_assert(state_reentry == 1);         // Get John! (Invalid state sequencing!)
525
526         if (gs_current_stack >= 1) {
527                 int old_state;
528
529                 // set the old state to be the state which is about to be popped off the queue
530                 old_state = gs[gs_current_stack].current_state;
531
532                 // set the popped_state to be the state which is going to be moved into
533                 popped_state = gs[gs_current_stack-1].current_state;
534
535                 // leave the current state
536                 state_reentry++;
537                 game_leave_state(gs[gs_current_stack].current_state,popped_state);
538
539                 // set the popped_state to be the one we moved into
540                 gs_current_stack--;
541
542                 // swap all remaining events from the state which just got popped to this new state
543                 while(gs[gs_current_stack+1].queue_head != gs[gs_current_stack+1].queue_tail){
544                         gameseq_post_event(gs[gs_current_stack+1].event_queue[gs[gs_current_stack+1].queue_head++]);
545                 }
546
547                 game_enter_state(old_state, gs[gs_current_stack].current_state);
548                 state_reentry--;
549
550         }
551
552 }
553
554 // gameseq_pop_and_discard_state() is used to remove a state that was pushed onto the stack, but
555 // will never need to be popped.  An example of this is entering a state that may require returning
556 // to the previous state (then you would call gameseq_pop_state).  Or you may simply continue to
557 // another part of the game, to avoid filling up the stack with states that may never be popped, you
558 // call this function to discard the top of the gs.
559 //
560
561 void gameseq_pop_and_discard_state()
562 {
563         if (gs_current_stack > 0 ) {
564                 gs_current_stack--;
565         }
566 }
567
568 // Returns the last state pushed on stack
569 int gameseq_get_pushed_state()
570 {
571         if (gs_current_stack >= 1) {
572                 return gs[gs_current_stack-1].current_state;
573         } else  
574                 return -1;
575 }
576
577 // gameseq_process_events gets called every time through high level loops
578 // (i.e. game loops, main menu loop).  Function is responsible for pulling
579 // game sequence events off the queue and changing the state when necessary.
580 // Returns the current state.
581                 // pull events game sequence events off of the queue.  Process one at a time
582                 // based on the current state and the new event.
583
584 int gameseq_process_events()    
585 {
586         int event, old_state;
587         old_state = gs[gs_current_stack].current_state;
588
589         SDL_assert(state_reentry == 0);         // Get John! (Invalid state sequencing!)
590
591         while ( (event = gameseq_get_event()) != -1 ) {
592                 state_reentry++;
593                 state_in_event_processer++;
594                 game_process_event(gs[gs_current_stack].current_state, event);
595                 state_in_event_processer--;
596                 state_reentry--;
597                 // break when state changes so that code will get called at
598                 // least one frame for each state.
599                 if ( !old_state || (old_state != gs[gs_current_stack].current_state) )
600                         break;  
601         }
602
603         state_reentry++;
604         game_do_state(gs[gs_current_stack].current_state);
605         state_reentry--;
606
607         return gs[gs_current_stack].current_state;
608
609