1 /*
2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
3 *
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on the
6 * source.
7 *
8 */
9
10
11
12 /*
13 * All states for game sequencing are defined in GameSequence.h.
14 * States should always be referred to using the macros.
15 */
16
17 #include "gamesequence/gamesequence.h"
18 #include "globalincs/pstypes.h"
19 #include "scripting/scripting.h"
20
21
22
23 // local defines
24 #define MAX_GAMESEQ_EVENTS 20 // maximum number of events on the game sequencing queue
25 #define GS_STACK_SIZE 10 // maximum number of stacked states
26
27 // local variables
28 typedef struct state_stack {
29 int current_state;
30 int previous_state;
31 int event_queue[MAX_GAMESEQ_EVENTS];
32 int queue_tail;
33 int queue_head;
34 int instance_id;
35 } state_stack;
36
37 // DO NOT MAKE THIS NON-STATIC!!!!
38 LOCAL state_stack gs[GS_STACK_SIZE];
39 LOCAL int gs_current_stack = -1; // index of top state on stack.
40
41 static int state_reentry = 0; // set if we are already in state processing
42 static int state_processing_event_post = 0; // set if we are already processing an event to switch states
43 static int state_in_event_processer = 0;
44 static int next_instance_id = 1;
45
46 // Text of state, corresponding to enum values for GS_STATE_*
47 //XSTR:OFF
48 const char *GS_event_text[] =
49 {
50 "GS_EVENT_MAIN_MENU", // 0
51 "GS_EVENT_START_GAME",
52 "GS_EVENT_ENTER_GAME",
53 "GS_EVENT_START_GAME_QUICK",
54 "GS_EVENT_END_GAME",
55 "GS_EVENT_QUIT_GAME", // 5
56 "GS_EVENT_PAUSE_GAME",
57 "GS_EVENT_PREVIOUS_STATE",
58 "GS_EVENT_OPTIONS_MENU",
59 "GS_EVENT_BARRACKS_MENU",
60 "GS_EVENT_TRAINING_MENU", // 10
61 "GS_EVENT_TECH_MENU",
62 "GS_EVENT_LOAD_MISSION_MENU",
63 "GS_EVENT_SHIP_SELECTION",
64 "GS_EVENT_TOGGLE_FULLSCREEN",
65 "GS_EVENT_START_BRIEFING", // 15
66 "GS_EVENT_DEBUG_PAUSE_GAME",
67 "GS_EVENT_HUD_CONFIG",
68 "GS_EVENT_MULTI_JOIN_GAME",
69 "GS_EVENT_CONTROL_CONFIG",
70 "GS_EVENT_EVENT_DEBUG", // 20
71 "GS_EVENT_WEAPON_SELECTION",
72 "GS_EVENT_MISSION_LOG_SCROLLBACK",
73 "GS_EVENT_GAMEPLAY_HELP",
74 "GS_EVENT_DEATH_DIED",
75 "GS_EVENT_DEATH_BLEW_UP", // 25
76 "GS_EVENT_NEW_CAMPAIGN",
77 "GS_EVENT_CREDITS",
78 "GS_EVENT_SHOW_GOALS",
79 "GS_EVENT_HOTKEY_SCREEN",
80 "GS_EVENT_VIEW_MEDALS", // 30
81 "GS_EVENT_MULTI_HOST_SETUP",
82 "GS_EVENT_MULTI_CLIENT_SETUP",
83 "GS_EVENT_DEBRIEF",
84 "GS_EVENT_GOTO_VIEW_CUTSCENES_SCREEN",
85 "GS_EVENT_MULTI_STD_WAIT", // 35
86 "GS_EVENT_STANDALONE_MAIN",
87 "GS_EVENT_MULTI_PAUSE",
88 "GS_EVENT_TEAM_SELECT",
89 "GS_EVENT_TRAINING_PAUSE",
90 "GS_EVENT_INGAME_PRE_JOIN", // 40
91 "GS_EVENT_PLAYER_WARPOUT_START",
92 "GS_EVENT_PLAYER_WARPOUT_START_FORCED",
93 "GS_EVENT_PLAYER_WARPOUT_STOP",
94 "GS_EVENT_PLAYER_WARPOUT_DONE_STAGE1",
95 "GS_EVENT_PLAYER_WARPOUT_DONE_STAGE2", // 45
96 "GS_EVENT_PLAYER_WARPOUT_DONE",
97 "GS_EVENT_STANDALONE_POSTGAME",
98 "GS_EVENT_INITIAL_PLAYER_SELECT",
99 "GS_EVENT_GAME_INIT",
100 "GS_EVENT_MULTI_MISSION_SYNC", // 50
101 "GS_EVENT_MULTI_START_GAME",
102 "GS_EVENT_MULTI_HOST_OPTIONS",
103 "GS_EVENT_MULTI_DOGFIGHT_DEBRIEF",
104 "GS_EVENT_CAMPAIGN_ROOM",
105 "GS_EVENT_CMD_BRIEF", // 55
106 "GS_EVENT_TOGGLE_GLIDE",
107 "GS_EVENT_RED_ALERT",
108 "GS_EVENT_SIMULATOR_ROOM",
109 "GS_EVENT_END_CAMPAIGN",
110 "GS_EVENT_LOOP_BRIEF", // 60
111 "GS_EVENT_CAMPAIGN_CHEAT",
112 "GS_EVENT_PXO",
113 "GS_EVENT_LAB",
114 "GS_EVENT_PXO_HELP",
115 "GS_EVENT_FICTION_VIEWER", // 65
116 "GS_EVENT_SCRIPTING"
117 };
118 //XSTR:ON
119
120 int Num_gs_event_text = sizeof(GS_event_text)/sizeof(char*);
121
122 // Text of state, corresponding to enum values for GS_STATE_*
123 //XSTR:OFF
124 const char *GS_state_text[] =
125 {
126 "NOT A VALID STATE", // 0
127 "GS_STATE_MAIN_MENU",
128 "GS_STATE_GAME_PLAY",
129 "GS_STATE_GAME_PAUSED",
130 "GS_STATE_QUIT_GAME",
131 "GS_STATE_OPTIONS_MENU", // 5
132 "GS_STATE_BARRACKS_MENU",
133 "GS_STATE_TECH_MENU",
134 "GS_STATE_TRAINING_MENU",
135 "GS_STATE_LOAD_MISSION_MENU",
136 "GS_STATE_BRIEFING", // 10
137 "GS_STATE_SHIP_SELECT",
138 "GS_STATE_DEBUG_PAUSED",
139 "GS_STATE_HUD_CONFIG",
140 "GS_STATE_MULTI_JOIN_GAME",
141 "GS_STATE_CONTROL_CONFIG", // 15
142 "GS_STATE_WEAPON_SELECT",
143 "GS_STATE_MISSION_LOG_SCROLLBACK",
144 "GS_STATE_DEATH_DIED",
145 "GS_STATE_DEATH_BLEW_UP",
146 "GS_STATE_SIMULATOR_ROOM", // 20
147 "GS_STATE_CREDITS",
148 "GS_STATE_SHOW_GOALS",
149 "GS_STATE_HOTKEY_SCREEN",
150 "GS_STATE_VIEW_MEDALS",
151 "GS_STATE_MULTI_HOST_SETUP", // 25
152 "GS_STATE_MULTI_CLIENT_SETUP",
153 "GS_STATE_DEBRIEF",
154 "GS_STATE_VIEW_CUTSCENES",
155 "GS_STATE_MULTI_STD_WAIT",
156 "GS_STATE_STANDALONE_MAIN", // 30
157 "GS_STATE_MULTI_PAUSED",
158 "GS_STATE_TEAM_SELECT",
159 "GS_STATE_TRAINING_PAUSED",
160 "GS_STATE_INGAME_PRE_JOIN",
161 "GS_STATE_EVENT_DEBUG", // 35
162 "GS_STATE_STANDALONE_POSTGAME",
163 "GS_STATE_INITIAL_PLAYER_SELECT",
164 "GS_STATE_MULTI_MISSION_SYNC",
165 "GS_STATE_MULTI_START_GAME",
166 "GS_STATE_MULTI_HOST_OPTIONS", // 40
167 "GS_STATE_MULTI_DOGFIGHT_DEBRIEF",
168 "GS_STATE_CAMPAIGN_ROOM",
169 "GS_STATE_CMD_BRIEF",
170 "GS_STATE_RED_ALERT",
171 "GS_STATE_END_OF_CAMPAIGN", // 45
172 "GS_STATE_GAMEPLAY_HELP",
173 "GS_STATE_LOOP_BRIEF",
174 "GS_STATE_PXO",
175 "GS_STATE_LAB",
176 "GS_STATE_PXO_HELP", // 50
177 "GS_STATE_START_GAME",
178 "GS_STATE_FICTION_VIEWER",
179 "GS_STATE_SCRIPTING"
180 };
181 //XSTR:ON
182
183 int Num_gs_state_text = sizeof(GS_state_text)/sizeof(char*);
184
gameseq_init()185 void gameseq_init()
186 {
187 int i;
188
189 for (i=0; i<GS_STACK_SIZE; i++ ) {
190 // gs[i].current_state = GS_STATE_MAIN_MENU;
191 gs[i].current_state = 0;
192 gs[i].previous_state = 0;
193 gs[i].queue_tail = 0;
194 gs[i].queue_head = 0;
195 gs[i].instance_id = 0;
196 }
197
198 gs_current_stack = 0;
199 state_reentry = 0;
200 state_processing_event_post = 0;
201 state_in_event_processer = 0;
202 }
203
204 // gameseq_post_event posts a new game sequencing event onto the gameseq
205 // event queue
206
gameseq_post_event(int event)207 void gameseq_post_event( int event )
208 {
209 if (state_processing_event_post) {
210 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] ));
211 }
212
213 Assert(gs[gs_current_stack].queue_tail < MAX_GAMESEQ_EVENTS);
214 gs[gs_current_stack].event_queue[gs[gs_current_stack].queue_tail++] = event;
215 if ( gs[gs_current_stack].queue_tail == MAX_GAMESEQ_EVENTS )
216 gs[gs_current_stack].queue_tail = 0;
217 }
218
219 // returns one of the GS_EVENT_ id's on the game sequencing queue
220
gameseq_get_event()221 int gameseq_get_event()
222 {
223 int event;
224
225 if ( gs[gs_current_stack].queue_head == gs[gs_current_stack].queue_tail )
226 return -1;
227 event = gs[gs_current_stack].event_queue[gs[gs_current_stack].queue_head++];
228 if ( gs[gs_current_stack].queue_head == MAX_GAMESEQ_EVENTS )
229 gs[gs_current_stack].queue_head = 0;
230
231 return event;
232 }
233
234 // Is our state stack valid
GameState_Stack_Valid()235 bool GameState_Stack_Valid()
236 {
237 return (gs_current_stack != -1);
238 }
239
240 // returns one of the GS_STATE_ macros
gameseq_get_state(int depth)241 int gameseq_get_state(int depth)
242 {
243 if (!GameState_Stack_Valid())
244 return GS_STATE_INVALID;
245
246 Assert(depth <= gs_current_stack);
247
248 return gs[gs_current_stack - depth].current_state;
249 }
250
gameseq_get_previous_state()251 int gameseq_get_previous_state()
252 {
253 return gs[gs_current_stack].previous_state;
254 }
255
gameseq_get_depth()256 int gameseq_get_depth()
257 {
258 return gs_current_stack;
259 }
260
gameseq_set_state(int new_state,int override)261 void gameseq_set_state(int new_state, int override)
262 {
263 int event, old_state;
264
265 if ( (new_state == gs[gs_current_stack].current_state) && !override )
266 return;
267
268 old_state = gs[gs_current_stack].current_state;
269
270 // Flush all events!!
271 while ( (event = gameseq_get_event()) != -1 ) {
272 mprintf(( "Throwing out event %d because of state set from %d to %d\n", event, old_state, new_state ));
273 }
274
275 Assert( state_reentry == 1 ); // Get John! (Invalid state sequencing!)
276 Assert( state_in_event_processer == 1 ); // can only call from game_process_event
277
278 state_processing_event_post++;
279 state_reentry++;
280 game_leave_state(gs[gs_current_stack].current_state,new_state);
281
282 gs[gs_current_stack].current_state = new_state;
283 gs[gs_current_stack].previous_state = old_state;
284 gs[gs_current_stack].instance_id = ++next_instance_id;
285
286 game_enter_state(old_state,gs[gs_current_stack].current_state);
287 state_reentry--;
288 state_processing_event_post--;
289 }
290
gameseq_push_state(int new_state)291 void gameseq_push_state( int new_state )
292 {
293 if ( new_state == gs[gs_current_stack].current_state )
294 return;
295
296 int old_state = gs[gs_current_stack].current_state;
297
298 // Flush all events!!
299 // I commented out because I'm not sure if we should throw out events when pushing or not.
300 // int event;
301 // while( (event = gameseq_get_event()) != -1 ) {
302 // mprintf(( "Throwing out event %d because of state push from %d to %d\n", event, old_state, new_state ));
303 // }
304
305 Assert( state_reentry == 1 ); // Get John! (Invalid state sequencing!)
306 Assert( state_in_event_processer == 1 ); // can only call from game_process_event
307
308 gs_current_stack++;
309 Assert(gs_current_stack < GS_STACK_SIZE);
310
311 state_processing_event_post++;
312 state_reentry++;
313 game_leave_state(old_state,new_state);
314
315 gs[gs_current_stack].current_state = new_state;
316 gs[gs_current_stack].previous_state = old_state;
317 gs[gs_current_stack].queue_tail = 0;
318 gs[gs_current_stack].queue_head = 0;
319 gs[gs_current_stack].instance_id = ++next_instance_id;
320
321 game_enter_state(old_state,gs[gs_current_stack].current_state);
322 state_reentry--;
323 state_processing_event_post--;
324 }
325
gameseq_pop_state()326 void gameseq_pop_state()
327 {
328 int popped_state = 0;
329
330 Assert(state_reentry == 1); // Get John! (Invalid state sequencing!)
331
332 if (gs_current_stack >= 1) {
333 int old_state;
334
335 // set the old state to be the state which is about to be popped off the queue
336 old_state = gs[gs_current_stack].current_state;
337
338 // set the popped_state to be the state which is going to be moved into
339 popped_state = gs[gs_current_stack-1].current_state;
340
341 // leave the current state
342 state_reentry++;
343 game_leave_state(gs[gs_current_stack].current_state,popped_state);
344
345 // set the popped_state to be the one we moved into
346 gs_current_stack--;
347 popped_state = gs[gs_current_stack].current_state;
348 gs[gs_current_stack].previous_state = old_state;
349
350 // swap all remaining events from the state which just got popped to this new state
351 while(gs[gs_current_stack+1].queue_head != gs[gs_current_stack+1].queue_tail){
352 gameseq_post_event(gs[gs_current_stack+1].event_queue[gs[gs_current_stack+1].queue_head++]);
353 }
354
355 game_enter_state(old_state, gs[gs_current_stack].current_state);
356 state_reentry--;
357
358 }
359
360 }
361
362 // gameseq_process_events gets called every time through high level loops
363 // (i.e. game loops, main menu loop). Function is responsible for pulling
364 // game sequence events off the queue and changing the state when necessary.
365 // Returns the current state.
366 // pull events game sequence events off of the queue. Process one at a time
367 // based on the current state and the new event.
368
gameseq_process_events()369 int gameseq_process_events()
370 {
371 int event, old_state;
372 old_state = gs[gs_current_stack].current_state;
373
374 Assert(state_reentry == 0); // Get John! (Invalid state sequencing!)
375
376 while ( (event = gameseq_get_event()) != -1 ) {
377 state_reentry++;
378 state_in_event_processer++;
379 game_process_event(gs[gs_current_stack].current_state, event);
380 state_in_event_processer--;
381 state_reentry--;
382 // break when state changes so that code will get called at
383 // least one frame for each state.
384 if (old_state != gs[gs_current_stack].current_state)
385 break;
386 }
387
388 state_reentry++;
389 game_do_state(gs[gs_current_stack].current_state);
390 state_reentry--;
391
392 return gs[gs_current_stack].current_state;
393 }
394
gameseq_get_event_idx(const char * s)395 int gameseq_get_event_idx(const char* s)
396 {
397 for(int i = 0; i < Num_gs_event_text; i++)
398 {
399 if(!stricmp(s, GS_event_text[i])) {
400 return i;
401 }
402 }
403
404 return -1;
405 }
406
gameseq_get_state_idx(const char * s)407 int gameseq_get_state_idx(const char* s)
408 {
409 for(int i = 0; i < Num_gs_state_text; i++)
410 {
411 if(!stricmp(s, GS_state_text[i])) {
412 return i;
413 }
414 }
415
416 return -1;
417 }
418
419 // If the given state exists in the stack then return the index, -1 if not
gameseq_get_state_idx(int state)420 int gameseq_get_state_idx(int state)
421 {
422 for(int i = 0; i <= gs_current_stack; i++)
423 {
424 if (gs[i].current_state == state) {
425 return i;
426 }
427 }
428
429 return -1;
430 }
431
gameseq_get_state_instance_id(int depth)432 int gameseq_get_state_instance_id(int depth) {
433 Assert(depth <= gs_current_stack);
434
435 return gs[gs_current_stack - depth].instance_id;
436 }
437