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