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 #include "anim/animplay.h"
13 #include "anim/packunpack.h"
14 #include "gamehelp/contexthelp.h"
15 #include "gamesequence/gamesequence.h"
16 #include "gamesnd/eventmusic.h"
17 #include "gamesnd/gamesnd.h"
18 #include "globalincs/alphacolors.h"
19 #include "graphics/font.h"
20 #include "io/key.h"
21 #include "io/timer.h"
22 #include "mission/missionparse.h"
23 #include "mission/missionbriefcommon.h"
24 #include "missionui/missioncmdbrief.h"
25 #include "missionui/missionscreencommon.h"
26 #include "missionui/missionshipchoice.h"
27 #include "missionui/missionweaponchoice.h"
28 #include "missionui/redalert.h"
29 #include "network/multi.h"
30 #include "network/multi_endgame.h"
31 #include "network/multiteamselect.h"
32 #include "playerman/player.h"
33 #include "sound/audiostr.h"
34 #include "sound/fsspeech.h"
35 #include "ui/uidefs.h"
36 
37 
38 
39 #define NUM_CMD_SETTINGS	2
40 
41 const char *Cmd_brief_fname[NUM_CMD_SETTINGS][GR_NUM_RESOLUTIONS] =
42 {
43 	{
44 		"CommandBrief",
45 		"2_CommandBrief"
46 	},
47 	{
48 		"CommandBriefb",
49 		"2_CommandBriefb"
50 	}
51 };
52 
53 
54 const char *Cmd_brief_mask[NUM_CMD_SETTINGS][GR_NUM_RESOLUTIONS] =
55 {
56 	{
57 		"CommandBrief-m",
58 		"2_CommandBrief-m"
59 	},
60 	{
61 		"CommandBrief-mb",
62 		"2_CommandBrief-mb"
63 	},
64 };
65 
66 
67 // lookups for coordinates
68 #define CMD_X_COORD 0
69 #define CMD_Y_COORD 1
70 #define CMD_W_COORD 2
71 #define CMD_H_COORD 3
72 
73 int Cmd_text_wnd_coords[NUM_CMD_SETTINGS][GR_NUM_RESOLUTIONS][4] =
74 {
75 	// original
76 	{
77 		{
78 			17, 109, 606, 108			// GR_640
79 		},
80 		{
81 			28, 174, 969, 174			// GR_1024
82 		}
83 	},
84 	// buttons
85 	{
86 		{
87 			17, 109, 587, 108			// GR_640
88 		},
89 		{
90 			28, 174, 939, 174			// GR_1024
91 		}
92 	}
93 };
94 
95 int Cmd_stage_y[GR_NUM_RESOLUTIONS] =
96 {
97 	90,		// GR_640
98 	145		// GR_1024
99 };
100 
101 // Goober5000 - center coordinates only
102 int Cmd_image_center_coords[GR_NUM_RESOLUTIONS][2] =
103 {
104 	{
105 		246, 358				// GR_640
106 	},
107 	{
108 		394, 573				// GR_1024
109 	}
110 };
111 
112 int Top_cmd_brief_text_line;
113 
114 int Max_cmdbrief_Lines; // Maximum number of lines to be displayed
115 
116 #define MAX_CMD_BRIEF_BUTTONS	10
117 #define MIN_CMD_BRIEF_BUTTONS	8
118 #define NUM_CMD_BRIEF_BUTTONS	(Uses_scroll_buttons ? MAX_CMD_BRIEF_BUTTONS : MIN_CMD_BRIEF_BUTTONS)
119 
120 #define CMD_BRIEF_BUTTON_FIRST_STAGE	0
121 #define CMD_BRIEF_BUTTON_PREV_STAGE		1
122 #define CMD_BRIEF_BUTTON_PAUSE			2
123 #define CMD_BRIEF_BUTTON_NEXT_STAGE		3
124 #define CMD_BRIEF_BUTTON_LAST_STAGE		4
125 #define CMD_BRIEF_BUTTON_HELP			5
126 #define CMD_BRIEF_BUTTON_OPTIONS		6
127 #define CMD_BRIEF_BUTTON_ACCEPT			7
128 #define CMD_BRIEF_BUTTON_SCROLL_UP		8
129 #define CMD_BRIEF_BUTTON_SCROLL_DOWN	9
130 
131 // buttons
132 ui_button_info Cmd_brief_buttons[GR_NUM_RESOLUTIONS][MAX_CMD_BRIEF_BUTTONS] =
133 {
134 	{ // GR_640
135 		ui_button_info("CBB_00",	504,	221,	-1,	-1,	0),
136 		ui_button_info("CBB_01",	527,	221,	-1,	-1,	1),
137 		ui_button_info("CBB_02",	555,	221,	-1,	-1,	2),
138 		ui_button_info("CBB_03",	583,	221,	-1,	-1,	3),
139 		ui_button_info("CBB_04",	607,	221,	-1,	-1,	4),
140 		ui_button_info("CBB_05",	539,	431,	-1,	-1,	5),
141 		ui_button_info("CBB_06",	538,	455,	-1,	-1,	6),
142 		ui_button_info("CBB_07",	575,	432,	-1,	-1,	7),
143 		ui_button_info("CBB_08",	615,	144,	-1,	-1,	8),
144 		ui_button_info("CBB_09",	615,	186,	-1,	-1,	9),
145 	},
146 	{ // GR_1024
147 		ui_button_info("2_CBB_00",	806,	354,	-1,	-1,	0),
148 		ui_button_info("2_CBB_01",	844,	354,	-1,	-1,	1),
149 		ui_button_info("2_CBB_02",	888,	354,	-1,	-1,	2),
150 		ui_button_info("2_CBB_03",	933,	354,	-1,	-1,	3),
151 		ui_button_info("2_CBB_04",	971,	354,	-1,	-1,	4),
152 		ui_button_info("2_CBB_05",	863,	690,	-1,	-1,	5),
153 		ui_button_info("2_CBB_06",	861,	728,	-1,	-1,	6),
154 		ui_button_info("2_CBB_07",	920,	692,	-1,	-1,	7),
155 		ui_button_info("2_CBB_08",	985,	232,	-1,	-1,	8),
156 		ui_button_info("2_CBB_09",	985,	299,	-1,	-1,	9),
157 	}
158 };
159 
160 // text
161 #define CMD_BRIEF_NUM_TEXT		3
162 UI_XSTR Cmd_brief_text[GR_NUM_RESOLUTIONS][CMD_BRIEF_NUM_TEXT] =
163 {
164 	{ // GR_640
165 		{ "Help",		928,	500,	440,	UI_XSTR_COLOR_GREEN,	-1,	&Cmd_brief_buttons[0][CMD_BRIEF_BUTTON_HELP].button },
166 		{ "Options",	1036,	479,	464,	UI_XSTR_COLOR_GREEN,	-1,	&Cmd_brief_buttons[0][CMD_BRIEF_BUTTON_OPTIONS].button },
167 		{ "Continue",	1069,	564,	413,	UI_XSTR_COLOR_PINK,	-1,	&Cmd_brief_buttons[0][CMD_BRIEF_BUTTON_ACCEPT].button },
168 	},
169 	{ // GR_1024
170 		{ "Help",		928,	820,	704,	UI_XSTR_COLOR_GREEN,	-1,	&Cmd_brief_buttons[1][CMD_BRIEF_BUTTON_HELP].button },
171 		{ "Options",	1036,	797,	743,	UI_XSTR_COLOR_GREEN,	-1,	&Cmd_brief_buttons[1][CMD_BRIEF_BUTTON_OPTIONS].button },
172 		{ "Continue",	1069,	936,	661,	UI_XSTR_COLOR_PINK,	-1,	&Cmd_brief_buttons[1][CMD_BRIEF_BUTTON_ACCEPT].button },
173 	}
174 };
175 
176 static UI_WINDOW Ui_window;
177 static int Cmd_brief_background_bitmap;					// bitmap for the background of the cmd_briefing
178 static int Cur_stage;
179 static int Cmd_brief_inited = 0;
180 static int Voice_good_to_go = 0;
181 static int Voice_started_time = 0;
182 static int Voice_ended_time;
183 static generic_anim Cur_Anim;
184 static const char *Cur_anim_filename = "~~~~";
185 
186 static int Cmd_brief_last_voice;
187 static int Cmd_brief_last_stage;
188 static int Cmd_brief_paused = 0;
189 
190 static int Uses_scroll_buttons = 0;
191 
192 int Cmd_brief_overlay_id;
193 
cmd_brief_init_voice()194 void cmd_brief_init_voice()
195 {
196 	int i;
197 
198 	Assert(Cur_cmd_brief);
199 	for (i=0; i<Cur_cmd_brief->num_stages; i++) {
200 		Cur_cmd_brief->stage[i].wave = -1;
201 		if (stricmp(Cur_cmd_brief->stage[i].wave_filename, NOX("none")) != 0 && Cur_cmd_brief->stage[i].wave_filename[0]) {
202 			Cur_cmd_brief->stage[i].wave = audiostream_open(Cur_cmd_brief->stage[i].wave_filename, ASF_VOICE);
203 			if (Cur_cmd_brief->stage[i].wave < 0) {
204 				nprintf(("General", "Failed to load \"%s\"\n", Cur_cmd_brief->stage[i].wave_filename));
205 			}
206 		}
207 	}
208 
209 	Cmd_brief_last_voice = -1;
210 	Cmd_brief_last_stage = -1;
211 }
212 
cmd_brief_check_stage_done()213 int cmd_brief_check_stage_done()
214 {
215 	if (!Voice_good_to_go)
216 		return 0;
217 
218 	if (Cmd_brief_paused)
219 		return 0;
220 
221 	if (Voice_ended_time && (timer_get_milliseconds() - Voice_ended_time >= 1000))
222 		return 1;
223 
224 	// check normal speech
225 	if (Briefing_voice_enabled && (Cmd_brief_last_voice >= 0)) {
226 		if (audiostream_is_playing(Cmd_brief_last_voice)) {
227 			return 0;
228 		}
229 
230 		if (!Voice_ended_time) {
231 			Voice_ended_time = timer_get_milliseconds();
232 		}
233 
234 		return 0;
235 	}
236 
237 	// check simulated speech
238 	if (Briefing_voice_enabled && (Cmd_brief_last_stage >= 0)) {
239 		if (fsspeech_playing()) {
240 			return 0;
241 		}
242 
243 		if (!Voice_ended_time) {
244 			Voice_ended_time = timer_get_milliseconds();
245 		}
246 
247 		return 0;
248 	}
249 
250 	// if we get here, there is no voice, so we simulate the time it would take instead
251 	if (!Voice_ended_time)
252 		Voice_ended_time = Voice_started_time + MAX(5000, Num_brief_text_lines[0] * 3500);
253 
254 	return 0;
255 }
256 
257 /**
258  * Start playback of the voice for a particular briefing stage
259  * @param stage_num Particular briefing stage
260  */
cmd_brief_voice_play(int stage_num)261 void cmd_brief_voice_play(int stage_num)
262 {
263 	int voice = -1;
264 	int stage = -1;
265 
266 	if (!Voice_good_to_go) {
267 		Voice_started_time = 0;
268 		return;
269 	}
270 
271 	if (!Voice_started_time) {
272 		Voice_started_time = timer_get_milliseconds();
273 		Voice_ended_time = 0;
274 	}
275 
276 	if (!Briefing_voice_enabled){
277 		return;
278 	}
279 
280 	if (Cur_stage >= 0 && Cur_stage < Cur_cmd_brief->num_stages){
281 		voice = Cur_cmd_brief->stage[stage_num].wave;
282 		stage = stage_num;
283 	}
284 
285 	// do we need to play simulated speech?
286 	if (voice < 0 && fsspeech_play_from(FSSPEECH_FROM_BRIEFING)) {
287 		// are we still on the same stage?
288 		if (Cmd_brief_last_stage == stage) {
289 			return;  // no changes, nothing to do.
290 		}
291 
292 		// if previous stage is still playing, stop it first.
293 		if (Cmd_brief_last_stage >= 0) {
294 			fsspeech_stop();
295 			Cmd_brief_last_stage = -1;
296 		}
297 
298 		// ok, new text needs speaking
299 		Cmd_brief_last_stage = stage;
300 		if (stage >= 0) {
301 			fsspeech_play(FSSPEECH_FROM_BRIEFING, Cur_cmd_brief->stage[stage_num].text.c_str());
302 		}
303 	} else {
304 		// are we still on same voice that is currently playing/played?
305 		if (Cmd_brief_last_voice == voice) {
306 			return;  // no changes, nothing to do.
307 		}
308 
309 		// if previous wave is still playing, stop it first.
310 		if (Cmd_brief_last_voice >= 0) {
311 			audiostream_stop(Cmd_brief_last_voice, 1, 0);  // stream is automatically rewound
312 			Cmd_brief_last_voice = -1;
313 		}
314 
315 		// ok, new wave needs playing, so we can start playing it now (and it becomes the current wave)
316 		Cmd_brief_last_voice = voice;
317 		if (voice >= 0) {
318 			audiostream_play(voice, Master_voice_volume, 0);
319 		}
320 	}
321 }
322 
323 /**
324  * called to leave the command briefing screen
325  */
cmd_brief_exit()326 void cmd_brief_exit()
327 {
328 	// I know, going to red alert from cmd brief is stupid, but we have stupid fredders
329 	if (red_alert_mission()) {
330 		gameseq_post_event(GS_EVENT_RED_ALERT);
331 	} else {
332 		gameseq_post_event(GS_EVENT_START_BRIEFING);
333 	}
334 }
335 
336 /**
337  * Doesn't actually stop playing ANIs any more, just stops audio
338  */
cmd_brief_stop_anim()339 void cmd_brief_stop_anim()
340 {
341 	Voice_good_to_go = 0;
342 	if (Cmd_brief_last_voice >= 0) {
343 		audiostream_stop(Cmd_brief_last_voice, 1, 0);  // stream is automatically rewound
344 		Cmd_brief_last_voice = -1;
345 	}
346 	if (Cmd_brief_last_stage >= 0) {
347 		fsspeech_stop();
348 		Cmd_brief_last_stage = -1;
349 	}
350 }
351 
cmd_brief_new_stage(int stage)352 void cmd_brief_new_stage(int stage)
353 {
354 	if (stage < 0) {
355 		cmd_brief_stop_anim();
356 		Cur_stage = -1;
357 	}
358 
359 	// Make sure that the text wrapping and the rendering code use the same font
360 	font::set_font(font::FONT1);
361 
362 	// fire the script hook
363 	common_fire_stage_script_hook(stage, Cur_stage);
364 
365 	Cur_stage = stage;
366 	brief_color_text_init(Cur_cmd_brief->stage[stage].text.c_str(), Cmd_text_wnd_coords[Uses_scroll_buttons][gr_screen.res][CMD_W_COORD], default_command_briefing_color);
367 
368 	// load a new animation if it's different to what's already playing
369 	if (strcmp(Cur_anim_filename, Cur_cmd_brief->stage[stage].ani_filename) != 0) {
370 		// unload the previous anim
371 		if(Cur_Anim.num_frames > 0) {
372 			generic_anim_unload(&Cur_Anim);
373 		}
374 
375 		// save new filename
376 		Cur_anim_filename = Cur_cmd_brief->stage[stage].ani_filename;
377 
378 		// try to load the new anim in either high or low res
379 		int stream_result = generic_anim_init_and_stream(&Cur_Anim, Cur_anim_filename, bm_get_type(Cmd_brief_background_bitmap), true);
380 
381 		// we've failed to load any animation
382 		if (stream_result < 0) {
383 			// load an image and treat it like a 1 frame animation
384 			Cur_Anim.first_frame = bm_load(Cur_cmd_brief->stage[stage].ani_filename);	//if we fail here, the value is still -1
385 			if(Cur_Anim.first_frame != -1) {
386 				Cur_Anim.num_frames = 1;
387 			}
388 		}
389 	}
390 
391 	//resetting the audio here
392 	cmd_brief_stop_anim();
393 
394 	Top_cmd_brief_text_line = 0;
395 }
396 
cmd_brief_hold()397 void cmd_brief_hold()
398 {
399 	cmd_brief_stop_anim();
400 }
401 
cmd_brief_unhold()402 void cmd_brief_unhold()
403 {
404 	cmd_brief_new_stage(Cur_stage);
405 }
406 
407 extern int Briefing_music_handle;
408 
cmd_brief_pause()409 void cmd_brief_pause()
410 {
411 	if (Cmd_brief_paused)
412 		return;
413 
414 	Cmd_brief_paused = 1;
415 
416 	if (Cmd_brief_last_voice >= 0) {
417 		audiostream_pause(Cmd_brief_last_voice);
418 	}
419 	if (Cmd_brief_last_stage >= 0) {
420 		fsspeech_pause(true);
421 	}
422 
423 	if (Briefing_music_handle >= 0) {
424 		audiostream_pause(Briefing_music_handle);
425 	}
426 }
427 
cmd_brief_unpause()428 void cmd_brief_unpause()
429 {
430 	if (!Cmd_brief_paused)
431 		return;
432 
433 	Cmd_brief_paused = 0;
434 
435 	if (Cmd_brief_last_voice >= 0) {
436 		audiostream_unpause(Cmd_brief_last_voice);
437 	}
438 	if (Cmd_brief_last_stage >= 0) {
439 		fsspeech_pause(false);
440 	}
441 
442 	if (Briefing_music_handle >= 0) {
443 		audiostream_unpause(Briefing_music_handle);
444 	}
445 }
446 
cmd_brief_button_pressed(int n)447 void cmd_brief_button_pressed(int n)
448 {
449 	switch (n) {
450 		case CMD_BRIEF_BUTTON_HELP:
451 			launch_context_help();
452 			gamesnd_play_iface(InterfaceSounds::HELP_PRESSED);
453 			break;
454 
455 		case CMD_BRIEF_BUTTON_OPTIONS:
456 			gamesnd_play_iface(InterfaceSounds::SWITCH_SCREENS);
457 			gameseq_post_event(GS_EVENT_OPTIONS_MENU);
458 			break;
459 
460 		case CMD_BRIEF_BUTTON_FIRST_STAGE:
461 			if (common_num_cutscenes_valid(MOVIE_PRE_CMD_BRIEF)) {
462 				audiostream_stop(Cmd_brief_last_voice);
463 				common_maybe_play_cutscene(MOVIE_PRE_CMD_BRIEF, true, SCORE_BRIEFING);
464 				cmd_brief_new_stage(0);
465 			}
466 			else if (Cur_stage) {
467 				cmd_brief_new_stage(0);
468 				gamesnd_play_iface(InterfaceSounds::BRIEF_STAGE_CHG);
469 			}
470 			else {
471 				gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
472 			}
473 
474 			break;
475 
476 		case CMD_BRIEF_BUTTON_PREV_STAGE:
477 			if (!Cur_stage && common_num_cutscenes_valid(MOVIE_PRE_CMD_BRIEF)) {
478 				audiostream_stop(Cmd_brief_last_voice);
479 				common_maybe_play_cutscene(MOVIE_PRE_CMD_BRIEF, true, SCORE_BRIEFING);
480 				cmd_brief_new_stage(0);
481 			}
482 			else if (Cur_stage) {
483 				cmd_brief_new_stage(Cur_stage - 1);
484 				gamesnd_play_iface(InterfaceSounds::BRIEF_STAGE_CHG);
485 			} else {
486 				gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
487 			}
488 
489 			break;
490 
491 		case CMD_BRIEF_BUTTON_NEXT_STAGE:
492 			if (Cur_stage < Cur_cmd_brief->num_stages - 1) {
493 				cmd_brief_new_stage(Cur_stage + 1);
494 				gamesnd_play_iface(InterfaceSounds::BRIEF_STAGE_CHG);
495 			} else {
496 				gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
497 			}
498 
499 			break;
500 
501 		case CMD_BRIEF_BUTTON_LAST_STAGE:
502 			if (Cur_stage < Cur_cmd_brief->num_stages - 1) {
503 				cmd_brief_new_stage(Cur_cmd_brief->num_stages - 1);
504 				gamesnd_play_iface(InterfaceSounds::BRIEF_STAGE_CHG);
505 			} else {
506 				gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
507 			}
508 			break;
509 
510 		case CMD_BRIEF_BUTTON_ACCEPT:
511 			cmd_brief_exit();
512 			gamesnd_play_iface(InterfaceSounds::COMMIT_PRESSED);
513 			break;
514 
515 		case CMD_BRIEF_BUTTON_PAUSE:
516 			gamesnd_play_iface(InterfaceSounds::USER_SELECT);
517 			fsspeech_pause(Player->auto_advance != 0);
518 			Player->auto_advance ^= 1;
519 			break;
520 
521 		case CMD_BRIEF_BUTTON_SCROLL_UP:
522 			Top_cmd_brief_text_line--;
523 			if ( Top_cmd_brief_text_line < 0 ) {
524 				Top_cmd_brief_text_line = 0;
525 				gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
526 			} else {
527 				gamesnd_play_iface(InterfaceSounds::SCROLL);
528 			}
529 			break;
530 
531 		case CMD_BRIEF_BUTTON_SCROLL_DOWN:
532 			Top_cmd_brief_text_line++;
533 			if ( (Num_brief_text_lines[0] - Top_cmd_brief_text_line) < Max_cmdbrief_Lines) {
534 				Top_cmd_brief_text_line--;
535 				gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
536 			} else {
537 				gamesnd_play_iface(InterfaceSounds::SCROLL);
538 			}
539 			break;
540 	}
541 }
542 
cmd_brief_ani_wave_init(int index)543 void cmd_brief_ani_wave_init(int index)
544 {
545 	const char *name;
546 
547 	// this is the first instance of the given anim filename
548 	name = Cur_cmd_brief->stage[index].ani_filename;
549 	if (!name[0] || !stricmp(name, NOX("<default>")) || !stricmp(name, NOX("none.ani"))) {
550 		name = NOX("CB_default");
551 		strcpy_s(Cur_cmd_brief->stage[index].ani_filename, name);
552 	}
553 }
554 
cmd_brief_init(int team)555 void cmd_brief_init(int team)
556 {
557 	common_music_init(SCORE_BRIEFING);
558 
559 	int i;
560 	ui_button_info *b;
561 
562 	Cmd_brief_inited = 0;
563 	Cur_cmd_brief = &Cmd_briefs[team];
564 
565 	// Goober5000 - replace any variables (probably persistent variables) with their values
566 	for (i = 0; i < Cur_cmd_brief->num_stages; i++)
567 		sexp_replace_variable_names_with_values(Cur_cmd_brief->stage[i].text);
568 
569 	if (Cur_cmd_brief->num_stages <= 0)
570 		return;
571 
572 	// for multiplayer, change the state in my netplayer structure
573 	if (Game_mode & GM_MULTIPLAYER) {
574 		Net_player->state = NETPLAYER_STATE_CMD_BRIEFING;
575 	}
576 
577 	gr_reset_clip();
578 	gr_clear();
579 	gr_flip();
580 
581 	// first determine which layout to use
582 	Uses_scroll_buttons = 1;	// assume true
583 	Cmd_brief_background_bitmap = mission_ui_background_load(Cur_cmd_brief->background[gr_screen.res], Cmd_brief_fname[Uses_scroll_buttons][gr_screen.res]);	// try to load extra one first
584 	if (Cmd_brief_background_bitmap < 0)	// failed to load
585 	{
586 		Uses_scroll_buttons = 0;	// nope, sorry
587 		Cmd_brief_background_bitmap = mission_ui_background_load(NULL, Cmd_brief_fname[Uses_scroll_buttons][gr_screen.res]);
588 	}
589 
590 	Ui_window.create(0, 0, gr_screen.max_w_unscaled, gr_screen.max_h_unscaled, 0);
591 	Ui_window.set_mask_bmap(Cmd_brief_mask[Uses_scroll_buttons][gr_screen.res]);
592 
593 	for (i=0; i<NUM_CMD_BRIEF_BUTTONS; i++) {
594 		b = &Cmd_brief_buttons[gr_screen.res][i];
595 
596 		b->button.create(&Ui_window, "", b->x, b->y, 60, 30, 0, 1);
597 		// set up callback for when a mouse first goes over a button
598 		b->button.set_highlight_action(common_play_highlight_sound);
599 		b->button.set_bmaps(b->filename);
600 		b->button.link_hotspot(b->hotspot);
601 	}
602 
603 	// add text
604 	for(i=0; i<CMD_BRIEF_NUM_TEXT; i++){
605 		Ui_window.add_XSTR(&Cmd_brief_text[gr_screen.res][i]);
606 	}
607 
608 	// set up readyrooms for buttons so we draw the correct animation frame when a key is pressed
609 	Cmd_brief_buttons[gr_screen.res][CMD_BRIEF_BUTTON_FIRST_STAGE].button.set_hotkey(KEY_SHIFTED | KEY_LEFT);
610 	Cmd_brief_buttons[gr_screen.res][CMD_BRIEF_BUTTON_LAST_STAGE].button.set_hotkey(KEY_SHIFTED | KEY_RIGHT);
611 	Cmd_brief_buttons[gr_screen.res][CMD_BRIEF_BUTTON_PREV_STAGE].button.set_hotkey(KEY_LEFT);
612 	Cmd_brief_buttons[gr_screen.res][CMD_BRIEF_BUTTON_NEXT_STAGE].button.set_hotkey(KEY_RIGHT);
613 	Cmd_brief_buttons[gr_screen.res][CMD_BRIEF_BUTTON_ACCEPT].button.set_hotkey(KEY_CTRLED | KEY_ENTER);
614 	Cmd_brief_buttons[gr_screen.res][CMD_BRIEF_BUTTON_HELP].button.set_hotkey(KEY_F1);
615 	Cmd_brief_buttons[gr_screen.res][CMD_BRIEF_BUTTON_OPTIONS].button.set_hotkey(KEY_F2);
616 
617 	// extra - Goober5000
618 	if (Uses_scroll_buttons)
619 	{
620 		Cmd_brief_buttons[gr_screen.res][CMD_BRIEF_BUTTON_SCROLL_UP].button.set_hotkey(KEY_UP);
621 		Cmd_brief_buttons[gr_screen.res][CMD_BRIEF_BUTTON_SCROLL_DOWN].button.set_hotkey(KEY_DOWN);
622 	}
623 
624 	// load in help overlay bitmap
625 	Cmd_brief_overlay_id = help_overlay_get_index(CMD_BRIEF_OVERLAY);
626 	help_overlay_set_state(Cmd_brief_overlay_id,gr_screen.res,0);
627 
628 	for (i=0; i<Cur_cmd_brief->num_stages; i++)
629 		cmd_brief_ani_wave_init(i);
630 
631 	cmd_brief_init_voice();
632 	cmd_brief_new_stage(0);
633 	Cmd_brief_paused = 0;
634 	Cmd_brief_inited = 1;
635 }
636 
cmd_brief_close()637 void cmd_brief_close()
638 {
639 	int i;
640 
641 	if (Cmd_brief_inited) {
642 		cmd_brief_stop_anim();
643 		generic_anim_unload(&Cur_Anim);
644 		for (i=0; i<Cur_cmd_brief->num_stages; i++) {
645 			if (Cur_cmd_brief->stage[i].wave >= 0)
646 				audiostream_close_file(Cur_cmd_brief->stage[i].wave, 0);
647 
648 		}
649 
650 		// so that the same ani will reload properly upon return
651 		Cur_anim_filename = "~~~~";
652 
653 		if (Cmd_brief_background_bitmap >= 0)
654 			bm_release(Cmd_brief_background_bitmap);
655 
656 		Ui_window.destroy();
657 
658 		game_flush();
659 		Cmd_brief_inited = 0;
660 	}
661 
662 	// Stop any speech from running over
663 	fsspeech_stop();
664 }
665 
cmd_brief_do_frame(float frametime)666 void cmd_brief_do_frame(float frametime)
667 {
668 	char buf[40];
669 	int i, k, w, h, x, y;
670 
671 	// if no command briefing exists, skip this screen.
672 	if (!Cmd_brief_inited) {
673 		cmd_brief_exit();
674 		return;
675 	}
676 
677 	if ( help_overlay_active(Cmd_brief_overlay_id) ) {
678 		Cmd_brief_buttons[gr_screen.res][CMD_BRIEF_BUTTON_HELP].button.reset_status();
679 		Ui_window.set_ignore_gadgets(1);
680 	}
681 
682 	k = Ui_window.process() & ~KEY_DEBUGGED;
683 
684 	if ( (k > 0) || B1_JUST_RELEASED ) {
685 		if ( help_overlay_active(Cmd_brief_overlay_id) ) {
686 			help_overlay_set_state(Cmd_brief_overlay_id, gr_screen.res, 0);
687 			Ui_window.set_ignore_gadgets(0);
688 			k = 0;
689 		}
690 	}
691 
692 	if ( !help_overlay_active(Cmd_brief_overlay_id) ) {
693 		Ui_window.set_ignore_gadgets(0);
694 	}
695 
696 	switch (k) {
697 	case KEY_ESC:
698 		if (Game_mode & GM_MULTIPLAYER) {
699 			gamesnd_play_iface(InterfaceSounds::USER_SELECT);
700 			multi_quit_game(PROMPT_ALL);
701 		} else {
702 			common_music_close();
703 			gameseq_post_event(GS_EVENT_MAIN_MENU);
704 		}
705 		break;
706 	}	// end switch
707 
708 	for (i=0; i<NUM_CMD_BRIEF_BUTTONS; i++){
709 		if (Cmd_brief_buttons[gr_screen.res][i].button.pressed()){
710 			cmd_brief_button_pressed(i);
711 		}
712 	}
713 
714 	cmd_brief_voice_play(Cur_stage);
715 	common_music_do();
716 
717 	if (cmd_brief_check_stage_done() && Player->auto_advance && (Cur_stage < Cur_cmd_brief->num_stages - 1)){
718 		if((Cur_Anim.num_frames <= 1) || Cur_Anim.done_playing) {
719 			cmd_brief_new_stage(Cur_stage + 1);
720 		}
721 	}
722 
723 	GR_MAYBE_CLEAR_RES(Cmd_brief_background_bitmap);
724 	if (Cmd_brief_background_bitmap >= 0) {
725 		gr_set_bitmap(Cmd_brief_background_bitmap);
726 		gr_bitmap(0, 0, GR_RESIZE_MENU);
727 	}
728 
729 	if(Cur_Anim.num_frames > 0) {
730 		bm_get_info((Cur_Anim.streaming) ? Cur_Anim.bitmap_id : Cur_Anim.first_frame, &x, &y, NULL, NULL, NULL);
731 		x = Cmd_image_center_coords[gr_screen.res][CMD_X_COORD] - x / 2;
732 		y = Cmd_image_center_coords[gr_screen.res][CMD_Y_COORD] - y / 2;
733 		generic_anim_render(&Cur_Anim, (Cmd_brief_paused) ? 0 : frametime, x, y, true);
734 	}
735 
736 	Ui_window.draw();
737 
738 	if (!Player->auto_advance){
739 		Cmd_brief_buttons[gr_screen.res][CMD_BRIEF_BUTTON_PAUSE].button.draw_forced(2);
740 	}
741 
742 	font::set_font(font::FONT1);
743 	gr_set_color_fast(&Color_text_heading);
744 
745 	sprintf(buf, XSTR( "Stage %d of %d", 464), Cur_stage + 1, Cur_cmd_brief->num_stages);
746 	gr_get_string_size(&w, NULL, buf);
747 	gr_string(Cmd_text_wnd_coords[Uses_scroll_buttons][gr_screen.res][CMD_X_COORD] + Cmd_text_wnd_coords[Uses_scroll_buttons][gr_screen.res][CMD_W_COORD] - w, Cmd_stage_y[gr_screen.res], buf, GR_RESIZE_MENU);
748 
749 	if (brief_render_text(Top_cmd_brief_text_line, Cmd_text_wnd_coords[Uses_scroll_buttons][gr_screen.res][CMD_X_COORD], Cmd_text_wnd_coords[Uses_scroll_buttons][gr_screen.res][CMD_Y_COORD], Cmd_text_wnd_coords[Uses_scroll_buttons][gr_screen.res][CMD_H_COORD], frametime, 0, 1)){
750 		Voice_good_to_go = 1;
751 	}
752 
753 	Max_cmdbrief_Lines = Cmd_text_wnd_coords[Uses_scroll_buttons][gr_screen.res][CMD_H_COORD]/(gr_get_font_height() + 1); //Make the max number of lines dependent on the font height, keeping in mind that we have an extra pixel between lines.
754 
755 	// maybe output the "more" indicator
756 	if ( (Max_cmdbrief_Lines + Top_cmd_brief_text_line) < Num_brief_text_lines[0] ) {
757 		// can be scrolled down
758 		int more_txt_x = Cmd_text_wnd_coords[Uses_scroll_buttons][gr_screen.res][CMD_X_COORD] + (Cmd_text_wnd_coords[Uses_scroll_buttons][gr_screen.res][CMD_W_COORD]/2) - 10;
759 		int more_txt_y = Cmd_text_wnd_coords[Uses_scroll_buttons][gr_screen.res][CMD_Y_COORD] + Cmd_text_wnd_coords[Uses_scroll_buttons][gr_screen.res][CMD_H_COORD] - 2;				// located below brief text, centered
760 
761 		gr_get_string_size(&w, &h, XSTR("more", 1469), static_cast<int>(strlen(XSTR("more", 1469))));
762 		gr_set_color_fast(&Color_black);
763 		gr_rect(more_txt_x-2, more_txt_y, w+3, h, GR_RESIZE_MENU);
764 		gr_set_color_fast(&Color_more_indicator);
765 		gr_string(more_txt_x, more_txt_y, XSTR("more", 1469), GR_RESIZE_MENU);  // base location on the input x and y?
766 	}
767 
768 	// blit help overlay if active
769 	help_overlay_maybe_blit(Cmd_brief_overlay_id, gr_screen.res);
770 
771 	gr_flip();
772 }
773 
mission_has_cmd_brief()774 int mission_has_cmd_brief()
775 {
776 	return (Cur_cmd_brief != NULL && Cur_cmd_brief->num_stages > 0);
777 }
778