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