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 #include "cfile/cfile.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 "globalincs/globals.h"
20 #include "graphics/font.h"
21 #include "iff_defs/iff_defs.h"
22 #include "io/key.h"
23 #include "io/timer.h"
24 #include "localization/localize.h"
25 #include "mission/missionbriefcommon.h"
26 #include "mission/missioncampaign.h"
27 #include "mission/missiongoals.h"
28 #include "missionui/chatbox.h"
29 #include "missionui/missiondebrief.h"
30 #include "missionui/missionpause.h"
31 #include "missionui/missionscreencommon.h"
32 #include "network/multi.h"
33 #include "network/multi_campaign.h"
34 #include "network/multi_endgame.h"
35 #include "network/multi_kick.h"
36 #include "network/multi_pinfo.h"
37 #include "network/multimsgs.h"
38 #include "network/multiui.h"
39 #include "network/multiutil.h"
40 #include "osapi/osapi.h"
41 #include "parse/parselo.h"
42 #include "pilotfile/pilotfile.h"
43 #include "playerman/player.h"
44 #include "popup/popup.h"
45 #include "ship/ship.h"
46 #include "sound/audiostr.h"
47 #include "sound/fsspeech.h"
48 #include "stats/medals.h"
49 #include "stats/stats.h"
50 #include "ui/uidefs.h"
51
52
53 #define MAX_TOTAL_DEBRIEF_LINES 200
54
55 #define TEXT_TYPE_NORMAL 1
56 #define TEXT_TYPE_RECOMMENDATION 2
57
58 #define DEBRIEF_NUM_STATS_PAGES 4
59 #define DEBRIEF_MISSION_STATS 0
60 #define DEBRIEF_MISSION_KILLS 1
61 #define DEBRIEF_ALLTIME_STATS 2
62 #define DEBRIEF_ALLTIME_KILLS 3
63
64 const int DEBRIEFING_FONT = font::FONT1;
65
66 extern float Brief_text_wipe_time_elapsed;
67
68 // 3rd coord is max width in pixels
69 int Debrief_title_coords[GR_NUM_RESOLUTIONS][3] = {
70 { // GR_640
71 18, 118, 174
72 },
73 { // GR_1024
74 28, 193, 280
75 }
76 };
77
78 int Debrief_text_wnd_coords[GR_NUM_RESOLUTIONS][4] = {
79 { // GR_640
80 43, 140, 339, 303
81 },
82 { // GR_1024
83 69, 224, 535, 485
84 }
85 };
86
87 int Debrief_text_x2[GR_NUM_RESOLUTIONS] = {
88 276, // GR_640
89 450 // GR_1024
90 };
91
92 int Debrief_stage_info_coords[GR_NUM_RESOLUTIONS][2] = {
93 { // GR_640
94 379, 137
95 },
96 { // GR_1024
97 578, 224
98 }
99 };
100
101 int Debrief_more_coords[GR_NUM_RESOLUTIONS][2] = {
102 { // GR_640
103 323, 453
104 },
105 { // GR_1024
106 323, 453
107 }
108 };
109
110 #define MULTI_LIST_TEAM_OFFSET 16
111
112 int Debrief_multi_list_team_max_display[GR_NUM_RESOLUTIONS] = {
113 9, // GR_640
114 12 // GR_1024
115 };
116
117 int Debrief_list_coords[GR_NUM_RESOLUTIONS][4] = {
118 { // GR_640
119 416, 280, 195, 101
120 },
121 { // GR_1024
122 666, 448, 312, 162
123 }
124 };
125
126 int Debrief_award_wnd_coords[GR_NUM_RESOLUTIONS][2] = {
127 { // GR_640
128 411, 126
129 },
130 { // GR_1024
131 658, 203
132 }
133 };
134
135
136 int Debrief_award_coords[GR_NUM_RESOLUTIONS][2] = {
137 { // GR_640
138 416, 140
139 },
140 { // GR_1024
141 666, 224
142 }
143 };
144
145 // 0=x, 1=y, 2=width of the field
146 int Debrief_medal_text_coords[GR_NUM_RESOLUTIONS][3] = {
147 { // GR_640
148 423, 247, 189
149 },
150 { // GR_1024
151 666, 333, 67
152 }
153 };
154
155 // 0=x, 1=y, 2=height of the field
156 int Debrief_award_text_coords[GR_NUM_RESOLUTIONS][3] = {
157 { // GR_640
158 416, 210, 42
159 },
160 { // GR_1024
161 666, 333, 67
162 }
163 };
164
165 // 0 = with medal
166 // 1 = without medal (text will use medal space)
167 #define DB_WITH_MEDAL 0
168 #define DB_WITHOUT_MEDAL 1
169 int Debrief_award_text_width[GR_NUM_RESOLUTIONS][2] = {
170 { // GR_640
171 123, 203
172 },
173 { // GR_1024
174 196, 312
175 }
176 };
177
178 const char *Debrief_single_name[GR_NUM_RESOLUTIONS] = {
179 "DebriefSingle", // GR_640
180 "2_DebriefSingle" // GR_1024
181 };
182 const char *Debrief_multi_name[GR_NUM_RESOLUTIONS] = {
183 "DebriefMulti", // GR_640
184 "2_DebriefMulti" // GR_1024
185 };
186 const char *Debrief_mask_name[GR_NUM_RESOLUTIONS] = {
187 "Debrief-m", // GR_640
188 "2_Debrief-m" // GR_1024
189 };
190
191 #define NUM_BUTTONS 18
192 #define NUM_TABS 2
193
194 #define DEBRIEF_TAB 0
195 #define STATS_TAB 1
196 #define TEXT_SCROLL_UP 2
197 #define TEXT_SCROLL_DOWN 3
198 #define REPLAY_MISSION 4
199 #define RECOMMENDATIONS 5
200 #define FIRST_STAGE 6
201 #define PREV_STAGE 7
202 #define NEXT_STAGE 8
203 #define LAST_STAGE 9
204 #define MULTI_PINFO_POPUP 10
205 #define MULTI_KICK 11
206 #define MEDALS_BUTTON 12
207 #define PLAYER_SCROLL_UP 13
208 #define PLAYER_SCROLL_DOWN 14
209 #define HELP_BUTTON 15
210 #define OPTIONS_BUTTON 16
211 #define ACCEPT_BUTTON 17
212
213 #define REPEAT 1
214
215 //XSTR:OFF
216 const char* Debrief_loading_bitmap_fname[GR_NUM_RESOLUTIONS] = {
217 "PleaseWait", // GR_640
218 "2_PleaseWait" // GR_1024
219 };
220
221 //XSTR:ON
222
223 typedef struct {
224 char text[NAME_LENGTH+1]; // name of ship type with a colon
225 int num; // how many ships of this type player has killed
226 } debrief_stats_kill_info;
227
228 typedef struct {
229 int net_player_index; // index into Net_players[] array
230 int rank_bitmap; // bitmap id for rank
231 char callsign[CALLSIGN_LEN];
232 } debrief_multi_list_info;
233
234 static ui_button_info Buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS] = {
235 { // GR_640
236 ui_button_info("DB_00", 6, 1, 37, 7, 0), // debriefing
237 ui_button_info("DB_01", 6, 21, 37, 23, 1), // statistics
238 ui_button_info("DB_02", 1, 195, -1, -1, 2), // scroll stats up
239 ui_button_info("DB_03", 1, 236, -1, -1, 3), // scroll stats down
240 ui_button_info("DB_04", 1, 428, 49, 447, 4), // replay mission
241 ui_button_info("DB_05", 17, 459, 49, 464, 5), // recommendations
242 ui_button_info("DB_06", 323, 454, -1, -1, 6), // first page
243 ui_button_info("DB_07", 348, 454, -1, -1, 7), // prev page
244 ui_button_info("DB_08", 372, 454, -1, -1, 8), // next page
245 ui_button_info("DB_09", 396, 454, -1, -1, 9), // last page
246 ui_button_info("DB_10", 441, 384, 433, 413, 10), // pilot info
247 ui_button_info("DB_11", 511, 384, 510, 413, 11), // kick
248 ui_button_info("DB_12", 613, 226, -1, -1, 12), // medals
249 ui_button_info("DB_13", 615, 329, -1, -1, 13), // scroll pilots up
250 ui_button_info("DB_14", 615, 371, -1, -1, 14), // scroll pilots down
251 ui_button_info("DB_15", 538, 431, 500, 440, 15), // help
252 ui_button_info("DB_16", 538, 455, 479, 464, 16), // options
253 ui_button_info("DB_17", 573, 432, 572, 413, 17), // accept
254 },
255 { // GR_1024
256 ui_button_info("2_DB_00", 10, 1, 59, 12, 0), // debriefing
257 ui_button_info("2_DB_01", 10, 33, 59, 37, 1), // statistics
258 ui_button_info("2_DB_02", 1, 312, -1, -1, 2), // scroll stats up
259 ui_button_info("2_DB_03", 1, 378, -1, -1, 3), // scroll stats down
260 ui_button_info("2_DB_04", 1, 685, 79, 715, 4), // replay mission
261 ui_button_info("2_DB_05", 28, 735, 79, 743, 5), // recommendations
262 ui_button_info("2_DB_06", 517, 726, -1, -1, 6), // first page
263 ui_button_info("2_DB_07", 556, 726, -1, -1, 7), // prev page
264 ui_button_info("2_DB_08", 595, 726, -1, -1, 8), // next page
265 ui_button_info("2_DB_09", 633, 726, -1, -1, 9), // last page
266 ui_button_info("2_DB_10", 706, 615, 700, 661, 10), // pilot info
267 ui_button_info("2_DB_11", 817, 615, 816, 661, 11), // kick
268 ui_button_info("2_DB_12", 981, 362, -1, -1, 12), // medals
269 ui_button_info("2_DB_13", 984, 526, -1, -1, 13), // scroll pilots up
270 ui_button_info("2_DB_14", 984, 594, -1, -1, 14), // scroll pilots down
271 ui_button_info("2_DB_15", 861, 689, 801, 705, 15), // help
272 ui_button_info("2_DB_16", 861, 728, 777, 744, 16), // options
273 ui_button_info("2_DB_17", 917, 692, 917, 692, 17), // accept
274 }
275 };
276
277 // text
278 #define NUM_DEBRIEF_TEXT 10
279 #define MP_TEXT_INDEX_1 4
280 #define MP_TEXT_INDEX_2 5
281 #define MP_TEXT_INDEX_3 6
282 UI_XSTR Debrief_strings[GR_NUM_RESOLUTIONS][NUM_DEBRIEF_TEXT] = {
283 { // GR_640
284 { "Debriefing", 804, 37, 7, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][DEBRIEF_TAB].button },
285 { "Statistics", 1333, 37, 26, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][STATS_TAB].button },
286 { "Replay Mission", 444, 49, 447, UI_XSTR_COLOR_PINK, -1, &Buttons[0][REPLAY_MISSION].button },
287 { "Recommendations", 1334, 49, 464, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][RECOMMENDATIONS].button },
288 { "Pilot", 1310, 433, 413, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][MULTI_PINFO_POPUP].button },
289 { "Info", 1311, 433, 423, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][MULTI_PINFO_POPUP].button },
290 { "Kick", 1266, 510, 413, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][MULTI_KICK].button },
291 { "Help", 928, 500, 440, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][HELP_BUTTON].button },
292 { "Options", 1036, 479, 464, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][OPTIONS_BUTTON].button },
293 { "Accept", 1035, 572, 413, UI_XSTR_COLOR_PINK, -1, &Buttons[0][ACCEPT_BUTTON].button },
294 },
295 { // GR_1024
296 { "Debriefing", 804, 59, 12, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][DEBRIEF_TAB].button },
297 { "Statistics", 1333, 59, 47, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][STATS_TAB].button },
298 { "Replay Mission", 444, 79, 715, UI_XSTR_COLOR_PINK, -1, &Buttons[1][REPLAY_MISSION].button },
299 { "Recommendations", 1334, 79, 743, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][RECOMMENDATIONS].button },
300 { "Pilot", 1310, 700, 661, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][MULTI_PINFO_POPUP].button },
301 { "Info", 1311, 700, 679, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][MULTI_PINFO_POPUP].button },
302 { "Kick", 1266, 816, 661, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][MULTI_KICK].button },
303 { "Help", 928, 801, 705, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][HELP_BUTTON].button },
304 { "Options", 1036, 780, 744, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][OPTIONS_BUTTON].button },
305 { "Accept", 1035, 917, 672, UI_XSTR_COLOR_PINK, -1, &Buttons[1][ACCEPT_BUTTON].button },
306 }
307 };
308
309
310 char Debrief_current_callsign[CALLSIGN_LEN+10];
311 player *Debrief_player;
312
313 static UI_WINDOW Debrief_ui_window;
314 static UI_BUTTON List_region;
315 static int Background_bitmap; // bitmap for the background of the debriefing
316 static int Award_bg_bitmap;
317 static int Debrief_multi_loading_bitmap;
318 static int Rank_bitmap;
319 static int Medal_bitmap;
320 static int Badge_bitmap;
321
322 static int Promoted;
323 static int Debrief_accepted;
324 static int Turned_traitor;
325 static int Must_replay_mission;
326
327 static int Current_mode;
328 static int New_mode;
329 static int Recommend_active;
330 static int Award_active;
331 static int Text_offset;
332 static int Num_text_lines = 0;
333
334 static int Debrief_inited = 0;
335 static int New_stage;
336 static int Current_stage;
337 static int Num_stages;
338 static int Num_debrief_stages;
339 static int Stage_voice = -1;
340 static int Debrief_music_timeout = 0;
341
342 static int Multi_list_size;
343 static int Multi_list_offset;
344
345 int Debrief_overlay_id;
346
347 int Debrief_multi_stages_loaded = 0;
348 int Debrief_multi_voice_loaded = 0;
349
350 // static int Debrief_voice_ask_for_cd;
351
352 // voice id's for debriefing text
353 static int Debrief_voices[MAX_DEBRIEF_STAGES];
354
355 #define DEBRIEF_VOICE_DELAY 400 // time to delay voice playback when a new stage starts
356 static int Debrief_cue_voice; // timestamp to cue the playback of the voice
357 static int Debrief_first_voice_flag = 1; // used to delay the first voice playback extra long
358
359 static int Debriefing_paused = 0;
360
361 int Max_debrief_Lines;
362
363 // pointer used for getting to debriefing information
364 debriefing Traitor_debriefing; // used when player is a traitor
365
366 // pointers to the active stages for this debriefing
367 static debrief_stage *Debrief_stages[MAX_DEBRIEF_STAGES];
368 static debrief_stage Promotion_stage, Badge_stage;
369 static debrief_stats_kill_info *Debrief_stats_kills = NULL;
370 static debrief_multi_list_info Multi_list[MAX_PLAYERS];
371 int Multi_list_select;
372
373 // flag indicating if we should display info for the given player (in multiplayer)
374 int Debrief_should_show_popup = 1;
375
376 // already shown skip mission popup?
377 static int Debrief_skip_popup_already_shown = 0;
378
379 void debrief_text_init();
380 void debrief_accept(int ok_to_post_start_game_event = 1);
381 void debrief_kick_selected_player();
382
383
384 // promotion voice selection stuff
385 #define NUM_VOLITION_CAMPAIGNS 1
386 typedef struct {
387 char campaign_name[32];
388 int num_missions;
389 } v_campaign;
390
391 v_campaign Volition_campaigns[NUM_VOLITION_CAMPAIGNS] = {
392 {
393 BUILTIN_CAMPAIGN, // the only campaign for now, but this leaves room for a mission pack
394 35 // make sure this is equal to the number of missions you gave in the corresponding Debrief_promotion_voice_mapping
395 }
396 };
397
398
399 // data for which voice goes w/ which mission
400 typedef struct voice_map {
401 char mission_file[32];
402 int persona_index;
403 } voice_map;
404
405 voice_map Debrief_promotion_voice_mapping[NUM_VOLITION_CAMPAIGNS][MAX_CAMPAIGN_MISSIONS] = {
406 { // FreeSpace2 campaign
407 { "SM1-01.fs2", 1 },
408 { "SM1-02.fs2", 1 },
409 { "SM1-03.fs2", 1 },
410 { "SM1-04.fs2", 2 },
411 { "SM1-05.fs2", 2 },
412 { "SM1-06.fs2", 2 },
413 { "SM1-07.fs2", 2 },
414 { "SM1-08.fs2", 3 },
415 { "SM1-09.fs2", 3 },
416 { "SM1-10.fs2", 3 },
417
418 { "SM2-01.fs2", 6 },
419 { "SM2-02.fs2", 6 },
420 { "SM2-03.fs2", 6 },
421 { "SM2-04.fs2", 7 },
422 { "SM2-05.fs2", 7 },
423 { "SM2-06.fs2", 7 },
424 { "SM2-07.fs2", 8 },
425 { "SM2-08.fs2", 8 },
426 { "SM2-09.fs2", 8 },
427 { "SM2-10.fs2", 8 },
428
429 { "SM3-01.fs2", 8 },
430 { "SM3-02.fs2", 8 },
431 { "SM3-03.fs2", 8 },
432 { "SM3-04.fs2", 8 },
433 { "SM3-05.fs2", 8 },
434 { "SM3-06.fs2", 9 },
435 { "SM3-07.fs2", 9 },
436 { "SM3-08.fs2", 9 },
437 { "SM3-09.fs2", 9 },
438 { "SM3-10.fs2", 9 }, // no debriefing for 3-10
439
440 { "loop1-1.fs2", 4 },
441 { "loop1-2.fs2", 4 },
442 { "loop1-3.fs2", 5 },
443 { "loop2-1.fs2", 4 },
444 { "loop2-2.fs2", 4 }
445 }
446 };
447
448 static const char* Debrief_award_background[GR_NUM_RESOLUTIONS] = {
449 "DebriefAward",
450 "2_DebriefAward"
451 };
452
453 #define AWARD_TEXT_MAX_LINES 5
454 #define AWARD_TEXT_MAX_LINE_LENGTH 128
455 char Debrief_award_text[AWARD_TEXT_MAX_LINES][AWARD_TEXT_MAX_LINE_LENGTH];
456 int Debrief_award_text_num_lines = 0;
457
458
459
460 // prototypes, you know you love 'em
461 void debrief_add_award_text(const char *str);
462 void debrief_award_text_clear();
463 void debrief_replace_stage_text(debrief_stage &stage);
464
465
466
467 // functions
debrief_tooltip_handler(const char * str)468 const char *debrief_tooltip_handler(const char *str)
469 {
470 if (!stricmp(str, NOX("@.Medal"))) {
471 if (Award_active){
472 return XSTR( "Medal", 435);
473 }
474
475 } else if (!stricmp(str, NOX("@.Rank"))) {
476 if (Award_active){
477 return XSTR( "Rank", 436);
478 }
479
480 } else if (!stricmp(str, NOX("@.Badge"))) {
481 if (Award_active){
482 return XSTR( "Badge", 437);
483 }
484
485 } else if (!stricmp(str, NOX("@Medal"))) {
486 if (Medal_bitmap >= 0){
487 return Medals[Player->stats.m_medal_earned].get_display_name();
488 }
489
490 } else if (!stricmp(str, NOX("@Rank"))) {
491 if (Rank_bitmap >= 0){
492 return Ranks[Promoted].name;
493 }
494
495 } else if (!stricmp(str, NOX("@Badge"))) {
496 if (Badge_bitmap >= 0){
497 return Medals[Player->stats.m_badge_earned.back()].get_display_name();
498 }
499 }
500
501 return NULL;
502 }
503
504 // initialize the array of handles to the different voice streams
debrief_voice_init()505 void debrief_voice_init()
506 {
507 int i;
508
509 for (i=0; i<MAX_DEBRIEF_STAGES; i++) {
510 Debrief_voices[i] = -1;
511 }
512 }
513
debrief_load_voice_file(int voice_num,char * name)514 void debrief_load_voice_file(int voice_num, char *name)
515 {
516 int load_attempts = 0;
517 while(1) {
518
519 if ( load_attempts++ > 5 ) {
520 break;
521 }
522
523 Debrief_voices[voice_num] = audiostream_open( name, ASF_VOICE );
524 if ( Debrief_voices[voice_num] >= 0 ) {
525 break;
526 }
527
528 // Don't bother to ask for the CD in multiplayer
529 if ( Game_mode & GM_MULTIPLAYER ) {
530 break;
531 }
532
533 }
534 }
535
536 // open and pre-load the stream buffers for the different voice streams
debrief_voice_load_all()537 void debrief_voice_load_all()
538 {
539 int i;
540
541 // Debrief_voice_ask_for_cd = 1;
542
543 for ( i=0; i<Num_debrief_stages; i++ ) {
544 if ( strlen(Debrief_stages[i]->voice) <= 0 ) {
545 continue;
546 }
547 if ( strnicmp(Debrief_stages[i]->voice, NOX("none"), 4) != 0 ) {
548 debrief_load_voice_file(i, Debrief_stages[i]->voice);
549 // Debrief_voices[i] = audiostream_open(Debrief_stages[i]->voice, ASF_VOICE);
550 }
551 }
552 }
553
554 // close all the briefing voice streams
debrief_voice_unload_all()555 void debrief_voice_unload_all()
556 {
557 int i;
558
559 for ( i=0; i<MAX_DEBRIEF_STAGES; i++ ) {
560 if ( Debrief_voices[i] != -1 ) {
561 audiostream_close_file(Debrief_voices[i], 0);
562 Debrief_voices[i] = -1;
563 }
564 }
565 }
566
567 // start playback of the voice for a particular briefing stage
debrief_voice_play()568 void debrief_voice_play()
569 {
570 if (!Briefing_voice_enabled || (Current_mode != DEBRIEF_TAB)){
571 return;
572 }
573
574 // no more stages? We are done then.
575 if (Stage_voice >= Num_debrief_stages){
576 return;
577 }
578
579 // if in delayed start, see if delay has elapsed and start voice if so
580 if (Debrief_cue_voice) {
581 if (!timestamp_elapsed(Debrief_cue_voice)){
582 return;
583 }
584
585 Stage_voice++; // move up to next voice
586 if ((Stage_voice < Num_debrief_stages) && (Debrief_voices[Stage_voice] >= 0)) {
587 audiostream_play(Debrief_voices[Stage_voice], Master_voice_volume, 0);
588 Debrief_cue_voice = 0; // indicate no longer in delayed start checking
589 }
590
591 return;
592 }
593
594 // see if voice is still playing. If so, do nothing yet.
595 if ((Stage_voice >= 0) && audiostream_is_playing(Debrief_voices[Stage_voice])){
596 return;
597 }
598
599 // set voice to play in a little while from now.
600 Debrief_cue_voice = timestamp(DEBRIEF_VOICE_DELAY);
601 }
602
603 // stop playback of the voice for a particular briefing stage
debrief_voice_stop()604 void debrief_voice_stop()
605 {
606 if ((Stage_voice < 0) || (Stage_voice > Num_debrief_stages) || (Debrief_voices[Stage_voice] < 0))
607 return;
608
609 audiostream_stop(Debrief_voices[Stage_voice], 1, 0); // stream is automatically rewound
610 Stage_voice = -1;
611
612 fsspeech_stop();
613 }
614
615 extern int Briefing_music_handle;
616
debrief_pause()617 void debrief_pause()
618 {
619 if (Debriefing_paused)
620 return;
621
622 Debriefing_paused = 1;
623
624 if (Briefing_music_handle >= 0) {
625 audiostream_pause(Briefing_music_handle);
626 }
627
628 if ((Stage_voice < 0) || (Stage_voice > Num_debrief_stages) || (Debrief_voices[Stage_voice] < 0))
629 return;
630
631 audiostream_pause(Debrief_voices[Stage_voice]);
632
633 fsspeech_pause(true);
634 }
635
debrief_unpause()636 void debrief_unpause()
637 {
638 if (!Debriefing_paused)
639 return;
640
641 Debriefing_paused = 1;
642
643 if (Briefing_music_handle >= 0) {
644 audiostream_unpause(Briefing_music_handle);
645 }
646
647 if ((Stage_voice < 0) || (Stage_voice > Num_debrief_stages) || (Debrief_voices[Stage_voice] < 0))
648 return;
649
650 audiostream_unpause(Debrief_voices[Stage_voice]);
651
652 fsspeech_pause(false);
653 }
654
655 // function to deal with inserting possible promition and badge stages into the debriefing
656 // on the clients
debrief_multi_fixup_stages()657 void debrief_multi_fixup_stages()
658 {
659 int i;
660
661 // possibly insert the badge stage first, them the promotion stage since they are
662 // inserted at the front of the debrief stages.
663 if ( Badge_bitmap >= 0 ) {
664 // move all stages forward one. Don't
665 for ( i = Num_debrief_stages; i > 0; i-- ) {
666 Debrief_stages[i] = Debrief_stages[i-1];
667 }
668 Debrief_stages[0] = &Badge_stage;
669 Num_debrief_stages++;
670 }
671
672 if ( Promoted >= 0) {
673 // move all stages forward one
674 for ( i = Num_debrief_stages; i > 0; i-- ) {
675 Debrief_stages[i] = Debrief_stages[i-1];
676 }
677 Debrief_stages[0] = &Promotion_stage;
678 Num_debrief_stages++;
679 }
680 }
681
682
683 // function called from multiplayer clients to set up the debriefing information for them
684 // (sent from the server).
debrief_set_multi_clients(int stage_count,int active_stages[])685 void debrief_set_multi_clients( int stage_count, int active_stages[] )
686 {
687 int i;
688
689 // set up the right briefing for this guy
690 if(MULTI_TEAM){
691 Debriefing = &Debriefings[Net_player->p_info.team];
692 } else {
693 Debriefing = &Debriefings[0];
694 }
695
696 // see if this client was promoted -- if so, then add the first stage.
697 Num_debrief_stages = 0;
698
699 // set the pointers to the debriefings for this client
700 for (i = 0; i < stage_count; i++) {
701 Debrief_stages[Num_debrief_stages++] = &Debriefing->stages[active_stages[i]];
702 // in multi, debriefing text replacement must occur client-side
703 // update most recently added stage only, in case replacement has side effects
704 debrief_replace_stage_text(*Debrief_stages[Num_debrief_stages - 1]);
705 }
706
707 Debrief_multi_stages_loaded = 1;
708 }
709
710 // evaluate all stages for all teams. Server of a multiplayer game will have to send that
711 // information to all clients after leaving this screen.
debrief_multi_server_stuff()712 void debrief_multi_server_stuff()
713 {
714 debriefing *debriefp;
715
716 int stage_active[MAX_TVT_TEAMS][MAX_DEBRIEF_STAGES], *stages[MAX_TVT_TEAMS];
717 int i, j, num_stages, stage_count[MAX_TVT_TEAMS];
718
719 memset( stage_active, 0, sizeof(stage_active) );
720
721 for (i=0; i<Num_teams; i++) {
722 debriefp = &Debriefings[i];
723 num_stages = 0;
724 stages[i] = stage_active[i];
725 for (j=0; j<debriefp->num_stages; j++) {
726 if ( eval_sexp(debriefp->stages[j].formula) ) {
727 stage_active[i][num_stages] = j;
728 num_stages++;
729 }
730 }
731
732 stage_count[i] = num_stages;
733 }
734
735 // if we're in campaign mode, evaluate campaign stuff
736 if (Netgame.campaign_mode == MP_CAMPAIGN) {
737 multi_campaign_eval_debrief();
738 }
739
740 // send the information to all clients.
741 send_debrief_info( stage_count, stages );
742 }
743
744
745 // --------------------------------------------------------------------------------------
746 // debrief_set_stages_and_multi_stuff()
747 //
748 // Set up the active stages for this debriefing
749 //
750 // returns: number of active debriefing stages
751 //
debrief_set_stages_and_multi_stuff()752 int debrief_set_stages_and_multi_stuff()
753 {
754 int i;
755 debriefing *debriefp;
756
757 if ( MULTIPLAYER_CLIENT ) {
758 return 0;
759 }
760
761 Num_debrief_stages = 0;
762
763 if ( Game_mode & GM_MULTIPLAYER ) {
764 debrief_multi_server_stuff();
765 }
766
767 // check to see if player is a traitor (looking at his team). If so, use the special
768 // traitor debriefing. Only done in single player
769 debriefp = Debriefing;
770 if ( !(Game_mode & GM_MULTIPLAYER) ) {
771 // although the no traitor flag check seems redundant it allows the mission designer to turn the traitor
772 // debriefing off before the mission ends and supply one themselves
773 if ((Player_ship->team == Iff_traitor) && !(The_mission.flags[Mission::Mission_Flags::No_traitor]))
774 debriefp = &Traitor_debriefing;
775 }
776
777 Num_debrief_stages = 0;
778 if (Promoted >= 0) {
779 Debrief_stages[Num_debrief_stages++] = &Promotion_stage;
780 }
781
782 if (Badge_bitmap >= 0) {
783 Debrief_stages[Num_debrief_stages++] = &Badge_stage;
784 }
785
786 for (i=0; i<debriefp->num_stages; i++) {
787 if (eval_sexp(debriefp->stages[i].formula) == SEXP_TRUE) {
788 // replacement in single-player and for host/server in multi
789 debrief_replace_stage_text(debriefp->stages[i]);
790 Debrief_stages[Num_debrief_stages++] = &debriefp->stages[i];
791 }
792 }
793
794 return Num_debrief_stages;
795 }
796
797 // init the buttons that are specific to the debriefing screen
debrief_buttons_init()798 void debrief_buttons_init()
799 {
800 ui_button_info *b;
801 int i;
802
803 for ( i=0; i<NUM_BUTTONS; i++ ) {
804 b = &Buttons[gr_screen.res][i];
805 b->button.create( &Debrief_ui_window, "", b->x, b->y, 60, 30, 0 /*b->flags & REPEAT*/, 1 );
806 // set up callback for when a mouse first goes over a button
807 b->button.set_highlight_action( common_play_highlight_sound );
808 b->button.set_bmaps(b->filename);
809 b->button.link_hotspot(b->hotspot);
810 }
811
812 // add all xstrs
813 for(i=0; i<NUM_DEBRIEF_TEXT; i++){
814 // multiplayer specific text
815 if((i == MP_TEXT_INDEX_1) || (i == MP_TEXT_INDEX_2) || (i == MP_TEXT_INDEX_3)){
816 // only add if in multiplayer mode
817 if(Game_mode & GM_MULTIPLAYER){
818 Debrief_ui_window.add_XSTR(&Debrief_strings[gr_screen.res][i]);
819 }
820 }
821 // all other text
822 else {
823 Debrief_ui_window.add_XSTR(&Debrief_strings[gr_screen.res][i]);
824 }
825 }
826
827 // set up hotkeys for buttons so we draw the correct animation frame when a key is pressed
828 Buttons[gr_screen.res][NEXT_STAGE].button.set_hotkey(KEY_RIGHT);
829 Buttons[gr_screen.res][PREV_STAGE].button.set_hotkey(KEY_LEFT);
830 Buttons[gr_screen.res][LAST_STAGE].button.set_hotkey(KEY_SHIFTED | KEY_RIGHT);
831 Buttons[gr_screen.res][FIRST_STAGE].button.set_hotkey(KEY_SHIFTED | KEY_LEFT);
832 Buttons[gr_screen.res][TEXT_SCROLL_UP].button.set_hotkey(KEY_UP);
833 Buttons[gr_screen.res][TEXT_SCROLL_DOWN].button.set_hotkey(KEY_DOWN);
834 Buttons[gr_screen.res][ACCEPT_BUTTON].button.set_hotkey(KEY_CTRLED+KEY_ENTER);
835
836 // if in multiplayer, disable the button for all players except the host
837 // also disable for squad war matches
838 if(Game_mode & GM_MULTIPLAYER){
839 if((Netgame.type_flags & NG_TYPE_SW) || !(Net_player->flags & NETINFO_FLAG_GAME_HOST)){
840 Buttons[gr_screen.res][REPLAY_MISSION].button.disable();
841 }
842 }
843 }
844
845 // --------------------------------------------------------------------------------------
846 // debrief_ui_init()
847 //
debrief_ui_init()848 void debrief_ui_init()
849 {
850 // init ship selection masks and buttons
851 common_set_interface_palette("DebriefPalette"); // set the interface palette
852 Debrief_ui_window.create( 0, 0, gr_screen.max_w_unscaled, gr_screen.max_h_unscaled, 0 );
853 Debrief_ui_window.set_mask_bmap(Debrief_mask_name[gr_screen.res]);
854 Debrief_ui_window.tooltip_handler = debrief_tooltip_handler;
855 debrief_buttons_init();
856
857 // load in help overlay bitmap
858 Debrief_overlay_id = help_overlay_get_index(DEBRIEFING_OVERLAY);
859 help_overlay_set_state(Debrief_overlay_id,gr_screen.res,0);
860
861 if ( Game_mode & GM_MULTIPLAYER ) {
862 // close down any old instances of the chatbox
863 chatbox_close();
864
865 // create the new one
866 chatbox_create();
867 List_region.create(&Debrief_ui_window, "", Debrief_list_coords[gr_screen.res][0], Debrief_list_coords[gr_screen.res][1], Debrief_list_coords[gr_screen.res][2], Debrief_list_coords[gr_screen.res][3], 0, 1);
868 List_region.hide();
869
870 }
871
872 Background_bitmap = mission_ui_background_load(Debriefing->background[gr_screen.res], Debrief_single_name[gr_screen.res], Debrief_multi_name[gr_screen.res]);
873
874 if ( Background_bitmap < 0 ) {
875 Warning(LOCATION, "Could not load the background bitmap for debrief screen");
876 }
877
878 Award_bg_bitmap = bm_load(Debrief_award_background[gr_screen.res]);
879 Debrief_multi_loading_bitmap = bm_load(Debrief_loading_bitmap_fname[gr_screen.res]);
880 }
881
882 // Goober5000
debrief_find_persona_index()883 int debrief_find_persona_index()
884 {
885 int i, j;
886
887 if ((Campaign.current_mission >= 0) && (Campaign.missions[Campaign.current_mission].name))
888 {
889 // Goober5000 - first see if the campaign supplied a persona index
890 // (0 means use the Volition default)
891 if (Campaign.missions[Campaign.current_mission].debrief_persona_index != 0)
892 return Campaign.missions[Campaign.current_mission].debrief_persona_index;
893
894 // search through all official campaigns for our current campaign
895 for (i = 0; i < NUM_VOLITION_CAMPAIGNS; i++)
896 {
897 if (!stricmp(Campaign.filename, Volition_campaigns[i].campaign_name))
898 {
899 // now search through the mission filenames
900 for (j = 0; j < Volition_campaigns[i].num_missions; j++)
901 {
902 // found it!
903 if (!stricmp(Campaign.missions[Campaign.current_mission].name, Debrief_promotion_voice_mapping[i][j].mission_file))
904 {
905 return Debrief_promotion_voice_mapping[i][j].persona_index;
906 }
907 }
908 }
909 }
910 }
911
912 // not found
913 return -1;
914 }
915
916 // Goober5000
917 // V sez: "defaults to number 9 (Petrarch) for non-volition missions
918 // this is an ugly, nasty, hateful way of doing this, but it saves us changing the missions at this point"
debrief_choose_voice(char * voice_dest,size_t buf_size,char * voice_base,int persona_index,int default_to_base=0)919 void debrief_choose_voice(char *voice_dest, size_t buf_size, char *voice_base, int persona_index, int default_to_base = 0)
920 {
921 if (persona_index >= 0)
922 {
923 // get voice file, also check for too long base file names via the return value of snprintf
924 if (snprintf(voice_dest, buf_size, NOX("%d_%s"), persona_index, voice_base) < static_cast<int>(buf_size))
925 {
926 // if it exists, we're done
927 if (cf_exists_full(voice_dest, CF_TYPE_VOICE_DEBRIEFINGS))
928 return;
929 }
930
931 }
932
933 // that didn't work, so use the default
934
935 // use the base file; don't prepend anything to it
936 if (default_to_base)
937 {
938 strncpy(voice_dest, voice_base, buf_size);
939 }
940 // default to Petrarch
941 else
942 {
943 strncpy(voice_dest, "9_", buf_size);
944 strncat(voice_dest, voice_base, buf_size);
945 }
946 // Ensure null terminator
947 voice_dest[buf_size] = '\0';
948 }
949
debrief_choose_medal_variant(char * buf,int medal_earned,int zero_based_version_index)950 void debrief_choose_medal_variant(char *buf, int medal_earned, int zero_based_version_index)
951 {
952 Assert(buf != NULL && medal_earned >= 0 && zero_based_version_index >= 0);
953
954 // start with the regular file name, adapted for resolution
955 sprintf(buf, NOX("%s%s"), Resolution_prefixes[gr_screen.res], Medals[medal_earned].debrief_bitmap);
956
957 // if the medal has multiple versions, we may want to choose a specific bitmap
958 char *p = strstr(buf, "##");
959 if (p != NULL)
960 {
961 int version;
962 char number[8];
963
964 // possible to earn more than we have variants for
965 if (zero_based_version_index >= Medals[medal_earned].num_versions)
966 version = Medals[medal_earned].num_versions - 1;
967 else
968 version = zero_based_version_index;
969
970 // also, this is dumb
971 if (Medals[medal_earned].version_starts_at_1)
972 version++;
973
974 sprintf(number, NOX("%.2d"), version);
975 Assert(strlen(number) == 2);
976 strncpy(p, number, 2);
977 }
978 }
979
debrief_award_init()980 void debrief_award_init()
981 {
982 char buf[80];
983
984 Rank_bitmap = -1;
985 Medal_bitmap = -1;
986 Badge_bitmap = -1;
987 Promoted = -1;
988
989 // be sure there are no old award texts floating around
990 debrief_award_text_clear();
991
992 // handle medal earned
993 if ( Player->stats.m_medal_earned != -1 ) {
994 debrief_choose_medal_variant(buf, Player->stats.m_medal_earned, Player->stats.medal_counts[Player->stats.m_medal_earned] - 1);
995 Medal_bitmap = bm_load(buf);
996
997 debrief_add_award_text(Medals[Player->stats.m_medal_earned].get_display_name());
998 }
999
1000 // handle promotions
1001 if ( Player->stats.m_promotion_earned != -1 ) {
1002 Promoted = Player->stats.m_promotion_earned;
1003 debrief_choose_medal_variant(buf, Rank_medal_index, Promoted);
1004 Rank_bitmap = bm_load(buf);
1005
1006 // see if we have a persona
1007 int persona_index = The_mission.debriefing_persona;
1008
1009 // use persona-specific promotion text if it exists; otherwise, use default
1010 if (Ranks[Promoted].promotion_text.find(persona_index) != Ranks[Promoted].promotion_text.end()) {
1011 Promotion_stage.text = Ranks[Promoted].promotion_text[persona_index];
1012 } else {
1013 Promotion_stage.text = Ranks[Promoted].promotion_text[-1];
1014 }
1015 Promotion_stage.recommendation_text = "";
1016
1017 // choose appropriate promotion voice for this mission
1018 debrief_choose_voice(Promotion_stage.voice, sizeof(Promotion_stage.voice), Ranks[Promoted].promotion_voice_base, persona_index);
1019
1020 debrief_add_award_text(Ranks[Promoted].name);
1021 }
1022
1023 // handle badge earned
1024 // only grant badge if earned and allowed. (no_promotion really means no promotion and no badges)
1025 if ( Player->stats.m_badge_earned.size() ) {
1026 debrief_choose_medal_variant(buf, Player->stats.m_badge_earned.back(), Player->stats.medal_counts[Player->stats.m_badge_earned.back()] - 1);
1027 Badge_bitmap = bm_load(buf);
1028
1029 // see if we have a persona
1030 int persona_index = The_mission.debriefing_persona;
1031
1032 // use persona-specific badge text if it exists; otherwise, use default
1033 if (Medals[Player->stats.m_badge_earned.back()].promotion_text.find(persona_index) != Medals[Player->stats.m_badge_earned.back()].promotion_text.end()) {
1034 Badge_stage.text = Medals[Player->stats.m_badge_earned.back()].promotion_text[persona_index];
1035 } else {
1036 Badge_stage.text = Medals[Player->stats.m_badge_earned.back()].promotion_text[-1];
1037 }
1038 Badge_stage.recommendation_text = "";
1039
1040 // choose appropriate badge voice for this mission
1041 debrief_choose_voice(Badge_stage.voice, sizeof(Badge_stage.voice), Medals[Player->stats.m_badge_earned.back()].voice_base, persona_index);
1042
1043 debrief_add_award_text(Medals[Player->stats.m_badge_earned.back()].get_display_name());
1044 }
1045
1046 if ((Rank_bitmap >= 0) || (Medal_bitmap >= 0) || (Badge_bitmap >= 0)) {
1047 Award_active = 1;
1048 } else {
1049 Award_active = 0;
1050 }
1051 }
1052
1053 // debrief_traitor_init() initializes local data which could be used if the player leaves the
1054 // mission a traitor. The same debriefing always gets played
debrief_traitor_init()1055 void debrief_traitor_init()
1056 {
1057 Debrief_accepted = 0;
1058 Turned_traitor = Must_replay_mission = 0;
1059
1060 if (Player_ship->team == Iff_traitor) {
1061 Turned_traitor = 1;
1062
1063 // if traitor, set up persona-specific traitor debriefing
1064 auto stagep = &Traitor_debriefing.stages[0];
1065
1066 // see if we have a persona
1067 int persona_index = The_mission.debriefing_persona;
1068
1069 // use persona-specific traitor text if it exists; otherwise, use default
1070 if (Traitor.debriefing_text.find(persona_index) != Traitor.debriefing_text.end())
1071 stagep->text = Traitor.debriefing_text[persona_index];
1072 else
1073 stagep->text = Traitor.debriefing_text[-1];
1074
1075 // choose appropriate traitor voice for this mission
1076 debrief_choose_voice(stagep->voice, sizeof(stagep->voice), Traitor.traitor_voice_base, persona_index, 1);
1077
1078 stagep->recommendation_text = Traitor.recommendation_text;
1079 }
1080
1081 if (!(Game_mode & GM_MULTIPLAYER) && (Game_mode & GM_CAMPAIGN_MODE)) {
1082 if (Campaign.next_mission == Campaign.current_mission){
1083 Must_replay_mission = 1;
1084 }
1085 }
1086
1087 // disable the accept button if in single player and I am a traitor
1088 if (Turned_traitor || Must_replay_mission) {
1089 Buttons[gr_screen.res][ACCEPT_BUTTON].button.hide();
1090
1091 // kill off any stats
1092 Player->flags &= ~PLAYER_FLAGS_PROMOTED;
1093 scoring_level_init( &Player->stats );
1094 }
1095 }
1096
1097 // initialization for listing of players in game
debrief_multi_list_init()1098 void debrief_multi_list_init()
1099 {
1100 Multi_list_size = 0; // number of net players to choose from
1101 Multi_list_offset = 0;
1102
1103 Multi_list_select = -1;
1104
1105 if ( !(Game_mode & GM_MULTIPLAYER) )
1106 return;
1107
1108 debrief_rebuild_player_list();
1109
1110 // switch stats display to this newly selected player
1111 set_player_stats(Multi_list[0].net_player_index);
1112 strcpy_s(Debrief_current_callsign, Multi_list[0].callsign);
1113 Debrief_player = Player;
1114 }
1115
debrief_multi_list_scroll_up()1116 void debrief_multi_list_scroll_up()
1117 {
1118 // if we're at the beginning of the list, don't do anything
1119 if(Multi_list_offset == 0){
1120 gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
1121 return;
1122 }
1123
1124 // otherwise scroll up
1125 Multi_list_offset--;
1126 gamesnd_play_iface(InterfaceSounds::USER_SELECT);
1127 }
1128
debrief_multi_list_scroll_down()1129 void debrief_multi_list_scroll_down()
1130 {
1131 // if we can scroll down no further
1132 if(Multi_list_size < Debrief_multi_list_team_max_display[gr_screen.res]){
1133 gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
1134 return;
1135 }
1136 if((Multi_list_offset + Debrief_multi_list_team_max_display[gr_screen.res]) >= Multi_list_size){
1137 gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
1138 return;
1139 }
1140
1141 // otherwise scroll down
1142 Multi_list_offset++;
1143 gamesnd_play_iface(InterfaceSounds::USER_SELECT);
1144 }
1145
1146 // draw the connected net players
debrief_multi_list_draw()1147 void debrief_multi_list_draw()
1148 {
1149 int y, z, font_height,idx;
1150 char str[CALLSIGN_LEN+5];
1151 net_player *np;
1152
1153 font_height = gr_get_font_height();
1154
1155 // if we currently have no item picked, pick a reasonable one
1156 if((Multi_list_size >= 0) && (Multi_list_select == -1)){
1157 // select the entry which corresponds to the local player
1158 Multi_list_select = 0;
1159 for(idx=0;idx<Multi_list_size;idx++){
1160 if(Multi_list[idx].net_player_index == MY_NET_PLAYER_NUM){
1161 Multi_list_select = idx;
1162
1163 // switch stats display to this newly selected player
1164 set_player_stats(Multi_list[idx].net_player_index);
1165 strcpy_s(Debrief_current_callsign, Multi_list[idx].callsign);
1166 Debrief_player = Net_players[Multi_list[idx].net_player_index].m_player;
1167 break;
1168 }
1169 }
1170 }
1171
1172 // draw the list itself
1173 y = 0;
1174 z = Multi_list_offset;
1175 while (y + font_height <= Debrief_list_coords[gr_screen.res][3]){
1176 np = &Net_players[Multi_list[z].net_player_index];
1177
1178 if (z >= Multi_list_size){
1179 break;
1180 }
1181 // set the proper text color for the highlight
1182 if(np->flags & NETINFO_FLAG_GAME_HOST){
1183 if(Multi_list_select == z){
1184 gr_set_color_fast(&Color_text_active_hi);
1185 } else {
1186 gr_set_color_fast(&Color_bright);
1187 }
1188 } else {
1189 if(Multi_list_select == z){
1190 gr_set_color_fast(&Color_text_active);
1191 } else {
1192 gr_set_color_fast(&Color_text_normal);
1193 }
1194 }
1195
1196 // blit the proper indicator - skipping observers
1197 if(!((np->flags & NETINFO_FLAG_OBSERVER) && !(np->flags & NETINFO_FLAG_OBS_PLAYER))){
1198 if(Netgame.type_flags & NG_TYPE_TEAM){
1199 // team 0
1200 if(np->p_info.team == 0){
1201 // draw his "selected" icon
1202 if(((np->state == NETPLAYER_STATE_DEBRIEF_ACCEPT) || (np->state == NETPLAYER_STATE_DEBRIEF_REPLAY)) && (Multi_common_icons[MICON_TEAM0_SELECT] != -1)){
1203 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]);
1204 gr_bitmap(Debrief_list_coords[gr_screen.res][0], Debrief_list_coords[gr_screen.res][1] + y - 2, GR_RESIZE_MENU);
1205 }
1206 // draw his "normal" icon
1207 else if(Multi_common_icons[MICON_TEAM0] != -1){
1208 gr_set_bitmap(Multi_common_icons[MICON_TEAM0]);
1209 gr_bitmap(Debrief_list_coords[gr_screen.res][0], Debrief_list_coords[gr_screen.res][1] + y - 2, GR_RESIZE_MENU);
1210 }
1211 } else if(np->p_info.team == 1){
1212 // draw his "selected" icon
1213 if(((np->state == NETPLAYER_STATE_DEBRIEF_ACCEPT) || (np->state == NETPLAYER_STATE_DEBRIEF_REPLAY)) && (Multi_common_icons[MICON_TEAM1_SELECT] != -1)){
1214 gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT]);
1215 gr_bitmap(Debrief_list_coords[gr_screen.res][0], Debrief_list_coords[gr_screen.res][1] + y - 2, GR_RESIZE_MENU);
1216 }
1217 // draw his "normal" icon
1218 else if(Multi_common_icons[MICON_TEAM1] != -1){
1219 gr_set_bitmap(Multi_common_icons[MICON_TEAM1]);
1220 gr_bitmap(Debrief_list_coords[gr_screen.res][0], Debrief_list_coords[gr_screen.res][1] + y - 2, GR_RESIZE_MENU);
1221 }
1222 }
1223 } else {
1224 // draw the team 0 selected icon
1225 if(((np->state == NETPLAYER_STATE_DEBRIEF_ACCEPT) || (np->state == NETPLAYER_STATE_DEBRIEF_REPLAY)) && (Multi_common_icons[MICON_TEAM0_SELECT] != -1)){
1226 gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]);
1227 gr_bitmap(Debrief_list_coords[gr_screen.res][0], Debrief_list_coords[gr_screen.res][1] + y - 2, GR_RESIZE_MENU);
1228 }
1229 }
1230 }
1231
1232 strcpy_s(str,Multi_list[z].callsign);
1233 if(Net_players[Multi_list[z].net_player_index].flags & NETINFO_FLAG_OBSERVER && !(Net_players[Multi_list[z].net_player_index].flags & NETINFO_FLAG_OBS_PLAYER)){
1234 strcat_s(str,XSTR( "(O)", 438));
1235 }
1236
1237 // bli
1238 gr_string(Debrief_list_coords[gr_screen.res][0] + MULTI_LIST_TEAM_OFFSET, Debrief_list_coords[gr_screen.res][1] + y, str, GR_RESIZE_MENU);
1239
1240 y += font_height;
1241 z++;
1242 }
1243 }
1244
debrief_kick_selected_player()1245 void debrief_kick_selected_player()
1246 {
1247 if(Multi_list_select >= 0){
1248 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
1249 multi_kick_player(Multi_list[Multi_list_select].net_player_index);
1250 }
1251 }
1252
1253
1254 // get optional mission popup text
debrief_assemble_optional_mission_popup_text(char * buffer,char * mission_loop_desc)1255 void debrief_assemble_optional_mission_popup_text(char *buffer, char *mission_loop_desc)
1256 {
1257 Assert(buffer != NULL);
1258 // base message
1259
1260 if (mission_loop_desc == NULL) {
1261 strcpy(buffer, XSTR("<No Mission Loop Description Available>", 1490));
1262 mprintf(("No mission loop description available\n"));
1263 } else {
1264 strcpy(buffer, mission_loop_desc);
1265 }
1266
1267 strcat(buffer, XSTR("\n\n\nDo you want to play the optional mission?", 1491));
1268 }
1269
1270 // what to do when the accept button is hit
debrief_accept(int ok_to_post_start_game_event)1271 void debrief_accept(int ok_to_post_start_game_event)
1272 {
1273 int go_loop = 0;
1274
1275 if ( (/*Cheats_enabled ||*/ Turned_traitor || Must_replay_mission) && (Game_mode & GM_CAMPAIGN_MODE) ) {
1276 const char *str;
1277 int z;
1278
1279 if (Game_mode & GM_MULTIPLAYER) {
1280 return;
1281 }
1282
1283 if (Player_ship->team == Iff_traitor){
1284 str = XSTR( "Your career is over, Traitor! You can't accept new missions!", 439);
1285 }/* else if (Cheats_enabled) {
1286 str = XSTR( "You are a cheater. You cannot accept this mission!", 440);
1287 }*/ else {
1288 str = XSTR( "You have failed this mission and cannot accept. What do you you wish to do instead?", 441);
1289 }
1290
1291 z = popup(0, 3, XSTR( "Return to &Debriefing", 442), XSTR( "Go to &Flight Deck", 443), XSTR( "&Replay Mission", 444), str);
1292 if (z == 2){
1293 gameseq_post_event(GS_EVENT_START_GAME); // restart mission
1294 } else if ( z == 1 ) {
1295 gameseq_post_event(GS_EVENT_END_GAME); // return to main hall, tossing stats
1296 }
1297
1298 return;
1299 }
1300
1301 Debrief_accepted = 1;
1302 // save mission stats
1303 if (Game_mode & GM_MULTIPLAYER) {
1304 // note that multi_debrief_accept_hit() will handle all mission_campaign_* calls on its own
1305 // as well as doing stats transfers, etc.
1306 multi_debrief_accept_hit();
1307 } else {
1308 int play_commit_sound = 1;
1309 // only write the player's stats if he's accepted
1310
1311 // if we are just playing a single mission, then don't do many of the things
1312 // that need to be done. Nothing much should happen when just playing a single
1313 // mission that isn't in a campaign.
1314 if ( Game_mode & GM_CAMPAIGN_MODE ) {
1315
1316 mission_campaign_store_variables(SEXP_VARIABLE_SAVE_ON_MISSION_PROGRESS);
1317
1318 // check for possible mission loop
1319 // check for (1) mission loop available, (2) don't have to repeat last mission
1320 if(!(Game_mode & GM_MULTIPLAYER)){
1321 int cur = Campaign.current_mission;
1322 bool require_repeat_mission = (Campaign.current_mission == Campaign.next_mission);
1323 if (Campaign.missions[cur].flags & CMISSION_FLAG_HAS_LOOP) {
1324 Assert(Campaign.loop_mission != CAMPAIGN_LOOP_MISSION_UNINITIALIZED);
1325 }
1326
1327 if ( (Campaign.missions[cur].flags & CMISSION_FLAG_HAS_LOOP) && (Campaign.loop_mission != -1) && !require_repeat_mission ) {
1328 /*
1329 char buffer[512];
1330 debrief_assemble_optional_mission_popup_text(buffer, Campaign.missions[cur].mission_loop_desc);
1331
1332 int choice = popup(0 , 2, POPUP_NO, POPUP_YES, buffer);
1333 if (choice == 1) {
1334 Campaign.loop_enabled = 1;
1335 Campaign.next_mission = Campaign.loop_mission;
1336 }
1337 */
1338 go_loop = 1;
1339 }
1340 }
1341
1342 // loopy loopy time
1343 if (go_loop && ok_to_post_start_game_event) {
1344 gameseq_post_event( GS_EVENT_LOOP_BRIEF );
1345 }
1346 // continue as normal
1347 else {
1348 // end the mission
1349 // if we can loop, but don't want to right now, then setup so that we can later
1350 mission_campaign_mission_over( (go_loop) ? false : true );
1351
1352 // check to see if we are out of the loop now
1353 if ( Campaign.next_mission == Campaign.loop_reentry ) {
1354 Campaign.loop_enabled = 0;
1355 }
1356
1357 // check if campaign is over, or if FREDer wants the mainhall
1358 if ( Campaign.next_mission == -1 || (The_mission.flags[Mission::Mission_Flags::End_to_mainhall]) ) {
1359 gameseq_post_event(GS_EVENT_MAIN_MENU);
1360 } else {
1361 if ( ok_to_post_start_game_event ) {
1362 gameseq_post_event(GS_EVENT_START_GAME);
1363 } else {
1364 play_commit_sound = 0;
1365 }
1366 }
1367 }
1368 } else {
1369 gameseq_post_event(GS_EVENT_MAIN_MENU);
1370 }
1371
1372 // Goober5000
1373 if ( play_commit_sound && !(The_mission.flags[Mission::Mission_Flags::Toggle_debriefing])) {
1374 gamesnd_play_iface(InterfaceSounds::COMMIT_PRESSED);
1375 }
1376
1377 game_flush();
1378 }
1379 }
1380
debrief_next_tab()1381 void debrief_next_tab()
1382 {
1383 New_mode = Current_mode + 1;
1384 if (New_mode >= NUM_TABS)
1385 New_mode = 0;
1386 }
1387
debrief_prev_tab()1388 void debrief_prev_tab()
1389 {
1390 New_mode = Current_mode - 1;
1391 if (New_mode < 0)
1392 New_mode = NUM_TABS - 1;
1393 }
1394
1395 // --------------------------------------------------------------------------------------
1396 // debrief_next_stage()
1397 //
debrief_next_stage()1398 void debrief_next_stage()
1399 {
1400 if (Current_stage < Num_stages - 1) {
1401 New_stage = Current_stage + 1;
1402 gamesnd_play_iface(InterfaceSounds::BRIEF_STAGE_CHG);
1403
1404 } else
1405 gamesnd_play_iface(InterfaceSounds::BRIEF_STAGE_CHG_FAIL);
1406 }
1407
1408 // --------------------------------------------------------------------------------------
1409 // debrief_prev_stage()
1410 //
debrief_prev_stage()1411 void debrief_prev_stage()
1412 {
1413 if (Current_stage) {
1414 New_stage = Current_stage - 1;
1415 gamesnd_play_iface(InterfaceSounds::BRIEF_STAGE_CHG);
1416
1417 } else
1418 gamesnd_play_iface(InterfaceSounds::BRIEF_STAGE_CHG_FAIL);
1419 }
1420
1421 // --------------------------------------------------------------------------------------
1422 // debrief_first_stage()
debrief_first_stage()1423 void debrief_first_stage()
1424 {
1425 if (Current_stage) {
1426 New_stage = 0;
1427 gamesnd_play_iface(InterfaceSounds::BRIEF_STAGE_CHG);
1428
1429 } else
1430 gamesnd_play_iface(InterfaceSounds::BRIEF_STAGE_CHG_FAIL);
1431 }
1432
1433 // --------------------------------------------------------------------------------------
1434 // debrief_last_stage()
debrief_last_stage()1435 void debrief_last_stage()
1436 {
1437 if (Current_stage != Num_stages - 1) {
1438 New_stage = Num_stages - 1;
1439 gamesnd_play_iface(InterfaceSounds::BRIEF_STAGE_CHG);
1440
1441 } else
1442 gamesnd_play_iface(InterfaceSounds::BRIEF_STAGE_CHG_FAIL);
1443 }
1444
1445 // draw what stage number the debriefing is on
debrief_render_stagenum()1446 void debrief_render_stagenum()
1447 {
1448 int w;
1449 char buf[64];
1450
1451 if (Num_stages < 2)
1452 return;
1453
1454 sprintf(buf, XSTR( "%d of %d", 445), Current_stage + 1, Num_stages);
1455 gr_get_string_size(&w, NULL, buf);
1456 gr_set_color_fast(&Color_bright_blue);
1457 gr_string(Debrief_stage_info_coords[gr_screen.res][0] - w, Debrief_stage_info_coords[gr_screen.res][1], buf, GR_RESIZE_MENU);
1458 gr_set_color_fast(&Color_white);
1459 }
1460
1461 // render the mission difficulty at the specified y location
debrief_render_mission_difficulty(int y_loc)1462 void debrief_render_mission_difficulty(int y_loc)
1463 {
1464 gr_string(0, y_loc, XSTR( "Skill Level", 1509), GR_RESIZE_MENU);
1465 gr_string(Debrief_text_x2[gr_screen.res], y_loc, Skill_level_names(Game_skill_level), GR_RESIZE_MENU);
1466 }
1467
1468 // render the mission time at the specified y location
debrief_render_mission_time(int y_loc)1469 void debrief_render_mission_time(int y_loc)
1470 {
1471 char time_str[30];
1472
1473 game_format_time(Missiontime, time_str);
1474 gr_string(0, y_loc, XSTR( "Mission Time", 446), GR_RESIZE_MENU);
1475 gr_string(Debrief_text_x2[gr_screen.res], y_loc, time_str, GR_RESIZE_MENU);
1476 }
1477
1478 // render out the stats info to the scroll window
1479 //
debrief_stats_render()1480 void debrief_stats_render()
1481 {
1482 int i, y, font_height;
1483
1484 gr_set_color_fast(&Color_blue);
1485 gr_set_clip(Debrief_text_wnd_coords[gr_screen.res][0], Debrief_text_wnd_coords[gr_screen.res][1], Debrief_text_wnd_coords[gr_screen.res][2], Debrief_text_wnd_coords[gr_screen.res][3], GR_RESIZE_MENU);
1486 gr_string(0, 0, Debrief_current_callsign, GR_RESIZE_MENU);
1487 font_height = gr_get_font_height();
1488 y = 30;
1489
1490 gr_set_color_fast(&Color_white);
1491
1492 debrief_render_mission_difficulty(y);
1493 y += 2*font_height;
1494
1495 switch ( Current_stage ) {
1496 case DEBRIEF_MISSION_STATS:
1497 i = Current_stage - 1;
1498 if ( i < 0 )
1499 i = 0;
1500
1501 // display mission completion time
1502 debrief_render_mission_time(y);
1503
1504 y += 2*font_height;
1505 show_stats_label(i, 0, y, font_height);
1506 show_stats_numbers(i, Debrief_text_x2[gr_screen.res], y, font_height);
1507 break;
1508 case DEBRIEF_ALLTIME_STATS:
1509 i = Current_stage - 1;
1510 if ( i < 0 )
1511 i = 0;
1512
1513 show_stats_label(i, 0, y, font_height);
1514 show_stats_numbers(i, Debrief_text_x2[gr_screen.res], y, font_height);
1515 break;
1516
1517 case DEBRIEF_ALLTIME_KILLS:
1518 case DEBRIEF_MISSION_KILLS:
1519 i = Text_offset;
1520 while (y + font_height <= Debrief_text_wnd_coords[gr_screen.res][3]) {
1521 if (i >= Num_text_lines)
1522 break;
1523
1524 if (!i) {
1525 if ( Current_stage == DEBRIEF_MISSION_KILLS )
1526 gr_printf_menu(0, y, "%s", XSTR( "Mission Kills by Ship Type", 447));
1527 else
1528 gr_printf_menu(0, y, "%s", XSTR( "All-time Kills by Ship Type", 448));
1529
1530 } else if (i > 1) {
1531 //Assert: Was debrief_setup_ship_kill_stats called?
1532 Assert(Debrief_stats_kills != NULL);
1533
1534 gr_printf_menu(0, y, "%s", Debrief_stats_kills[i - 2].text);
1535 gr_printf_menu(Debrief_text_x2[gr_screen.res], y, "%d", Debrief_stats_kills[i - 2].num);
1536 }
1537
1538 y += font_height;
1539 i++;
1540 }
1541
1542 if (Num_text_lines == 2) {
1543 if ( Current_stage == DEBRIEF_MISSION_KILLS )
1544 gr_printf_menu(0, y, "%s", XSTR( "(No ship kills this mission)", 449));
1545 else
1546 gr_printf_menu(0, y, "%s", XSTR( "(No ship kills)", 450));
1547 }
1548
1549 break;
1550
1551 default:
1552 Int3();
1553 break;
1554 }
1555
1556 gr_reset_clip();
1557 }
1558
1559 // do action for when the replay button is pressed
debrief_replay_pressed()1560 void debrief_replay_pressed()
1561 {
1562 if (!Turned_traitor && !Must_replay_mission && (Game_mode & GM_CAMPAIGN_MODE)) {
1563 int choice;
1564 choice = popup(0, 2, POPUP_CANCEL, XSTR( "&Replay", 451), XSTR( "If you choose to replay this mission, you will be required to complete it again before proceeding to future missions.\n\nIn addition, any statistics gathered during this mission will be discarded if you choose to replay.", 452));
1565
1566 if (choice != 1){
1567 return;
1568 }
1569 }
1570
1571 gameseq_post_event(GS_EVENT_START_GAME); // restart mission
1572 gamesnd_play_iface(InterfaceSounds::COMMIT_PRESSED);
1573 }
1574
1575 // -------------------------------------------------------------------
1576 // debrief_redraw_pressed_buttons()
1577 //
1578 // Redraw any debriefing buttons that are pressed down. This function is needed
1579 // since we sometimes need to draw pressed buttons last to ensure the entire
1580 // button gets drawn (and not overlapped by other buttons)
1581 //
debrief_redraw_pressed_buttons()1582 void debrief_redraw_pressed_buttons()
1583 {
1584 int i;
1585 UI_BUTTON *b;
1586
1587 for ( i=0; i<NUM_BUTTONS; i++ ) {
1588 b = &Buttons[gr_screen.res][i].button;
1589 // don't draw the recommendations button if we're in stats mode
1590 if ( b->button_down() ) {
1591 b->draw_forced(2);
1592 }
1593 }
1594 }
1595
1596 // debrief specific button with hotspot 'i' has been pressed, so perform the associated action
1597 //
debrief_button_pressed(int num)1598 void debrief_button_pressed(int num)
1599 {
1600 switch (num) {
1601 case DEBRIEF_TAB:
1602 Buttons[gr_screen.res][RECOMMENDATIONS].button.enable();
1603 // Debrief_ui_window.use_hack_to_get_around_stupid_problem_flag = 0;
1604 if (num != Current_mode){
1605 gamesnd_play_iface(InterfaceSounds::SCREEN_MODE_PRESSED);
1606 }
1607 New_mode = num;
1608 break;
1609 case STATS_TAB:
1610 // Debrief_ui_window.use_hack_to_get_around_stupid_problem_flag = 1; // allows failure sound to be played
1611 Buttons[gr_screen.res][RECOMMENDATIONS].button.disable();
1612 if (num != Current_mode){
1613 gamesnd_play_iface(InterfaceSounds::SCREEN_MODE_PRESSED);
1614 }
1615 New_mode = num;
1616 break;
1617
1618 case TEXT_SCROLL_UP:
1619 if (Text_offset) {
1620 Text_offset--;
1621 gamesnd_play_iface(InterfaceSounds::SCROLL);
1622 } else {
1623 gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
1624 }
1625 break;
1626
1627 case TEXT_SCROLL_DOWN:
1628 if (Max_debrief_Lines < (Num_text_lines - Text_offset)) {
1629 Text_offset++;
1630 gamesnd_play_iface(InterfaceSounds::SCROLL);
1631 } else {
1632 gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
1633 }
1634 break;
1635
1636 case REPLAY_MISSION:
1637 if(Game_mode & GM_MULTIPLAYER){
1638 multi_debrief_replay_hit();
1639 } else {
1640 debrief_replay_pressed();
1641 }
1642 break;
1643
1644 case RECOMMENDATIONS:
1645 gamesnd_play_iface(InterfaceSounds::USER_SELECT);
1646 Recommend_active = !Recommend_active;
1647 debrief_text_init();
1648 break;
1649
1650 case FIRST_STAGE:
1651 debrief_first_stage();
1652 break;
1653
1654 case PREV_STAGE:
1655 debrief_prev_stage();
1656 break;
1657
1658 case NEXT_STAGE:
1659 debrief_next_stage();
1660 break;
1661
1662 case LAST_STAGE:
1663 debrief_last_stage();
1664 break;
1665
1666 case HELP_BUTTON:
1667 gamesnd_play_iface(InterfaceSounds::HELP_PRESSED);
1668 launch_context_help();
1669 break;
1670
1671 case OPTIONS_BUTTON:
1672 gamesnd_play_iface(InterfaceSounds::SWITCH_SCREENS);
1673 gameseq_post_event( GS_EVENT_OPTIONS_MENU );
1674 break;
1675
1676 case ACCEPT_BUTTON:
1677 debrief_accept();
1678 break;
1679
1680 case MEDALS_BUTTON:
1681 gamesnd_play_iface(InterfaceSounds::SWITCH_SCREENS);
1682 gameseq_post_event(GS_EVENT_VIEW_MEDALS);
1683 break;
1684
1685 case PLAYER_SCROLL_UP:
1686 debrief_multi_list_scroll_up();
1687 break;
1688
1689 case PLAYER_SCROLL_DOWN:
1690 debrief_multi_list_scroll_down();
1691 break;
1692
1693 case MULTI_PINFO_POPUP:
1694 Debrief_should_show_popup = 1;
1695 break;
1696
1697 case MULTI_KICK:
1698 debrief_kick_selected_player();
1699 break;
1700 } // end swtich
1701 }
1702
debrief_setup_ship_kill_stats(int)1703 void debrief_setup_ship_kill_stats(int /*stage_num*/)
1704 {
1705 int i;
1706 //ushort *kill_arr;
1707 int *kill_arr; //DTP max ships
1708 debrief_stats_kill_info *kill_info;
1709
1710 Assert(Current_stage < DEBRIEF_NUM_STATS_PAGES);
1711 if ( Current_stage == DEBRIEF_MISSION_STATS || Current_stage == DEBRIEF_ALLTIME_STATS )
1712 return;
1713
1714 if(Debrief_stats_kills == NULL)
1715 {
1716 Debrief_stats_kills = new debrief_stats_kill_info[Ship_info.size()];
1717 }
1718
1719 Assert(Debrief_player != NULL);
1720
1721 // kill_ar points to an array of MAX_SHIP_TYPE ints
1722 if ( Current_stage == DEBRIEF_MISSION_KILLS ) {
1723 kill_arr = Debrief_player->stats.m_okKills;
1724 } else {
1725 kill_arr = Debrief_player->stats.kills;
1726 }
1727
1728 Num_text_lines = 0;
1729 i = 0;
1730 for ( auto it = Ship_info.begin(); it != Ship_info.end(); i++, ++it ) {
1731
1732 // code used to add in mission kills, but the new system assumes that the player will accept, so
1733 // all time stats already have mission stats added in.
1734 if ( kill_arr[i] <= 0 ){
1735 continue;
1736 }
1737
1738
1739 kill_info = &Debrief_stats_kills[Num_text_lines++];
1740
1741 kill_info->num = kill_arr[i];
1742
1743 strcpy_s(kill_info->text, it->name);
1744 strcat_s(kill_info->text, NOX(":"));
1745 }
1746
1747 Num_text_lines += 2;
1748 }
1749
1750 // Iterate through the debriefing buttons, checking if they are pressed
debrief_check_buttons()1751 void debrief_check_buttons()
1752 {
1753 int i;
1754
1755 for ( i=0; i<NUM_BUTTONS; i++ ) {
1756 if ( Buttons[gr_screen.res][i].button.pressed() ) {
1757 debrief_button_pressed(i);
1758 }
1759 }
1760
1761 int y, z;
1762
1763 if ( !(Game_mode & GM_MULTIPLAYER) )
1764 return;
1765
1766 if (List_region.pressed()) {
1767 List_region.get_mouse_pos(NULL, &y);
1768 z = Multi_list_offset + y / gr_get_font_height();
1769 if ((z >= 0) && (z < Multi_list_size)) {
1770 // switch stats display to this newly selected player
1771 set_player_stats(Multi_list[z].net_player_index);
1772 strcpy_s(Debrief_current_callsign, Multi_list[z].callsign);
1773 Debrief_player = Net_players[Multi_list[z].net_player_index].m_player;
1774 Multi_list_select = z;
1775 debrief_setup_ship_kill_stats(Current_stage);
1776 gamesnd_play_iface(InterfaceSounds::USER_SELECT);
1777 }
1778 }
1779
1780 // if the player was double clicked on - we should popup a player info popup
1781 /*
1782 if (List_region.double_clicked()) {
1783 Debrief_should_show_popup = 1;
1784 }
1785 */
1786 }
1787
1788 // setup the debriefing text lines for rendering
debrief_text_init()1789 void debrief_text_init()
1790 {
1791 int r_count = 0;
1792 const char *src;
1793 int i;
1794
1795 // If no wav files are being used use speech simulation
1796 bool use_sim_speech = true;
1797 for (i = 0; i < MAX_DEBRIEF_STAGES; i++) {
1798 if(Debrief_voices[i] != -1) {
1799 use_sim_speech = false;
1800 break;
1801 }
1802 }
1803
1804 // Make sure that the text wrapping and the rendering code use the same font
1805 font::set_font(DEBRIEFING_FONT);
1806
1807 Num_text_lines = Text_offset = brief_color_text_init("", Debrief_text_wnd_coords[gr_screen.res][2], default_debriefing_color, 0, 0); // Initialize color stuff -MageKing17
1808
1809 fsspeech_start_buffer();
1810
1811 if (Current_mode == DEBRIEF_TAB) {
1812 for (i=0; i<Num_debrief_stages; i++) {
1813 if (i)
1814 // add a blank line between stages
1815 Num_text_lines += brief_color_text_init("\n", Debrief_text_wnd_coords[gr_screen.res][2], default_debriefing_color, 0, MAX_DEBRIEF_LINES, true);
1816
1817 src = Debrief_stages[i]->text.c_str();
1818
1819 if (*src) {
1820 Num_text_lines += brief_color_text_init(src, Debrief_text_wnd_coords[gr_screen.res][2], default_debriefing_color, 0, MAX_DEBRIEF_LINES, true);
1821
1822 if (use_sim_speech && !Recommend_active) {
1823 fsspeech_stuff_buffer(src);
1824 fsspeech_stuff_buffer("\n");
1825 }
1826 }
1827
1828 if (Recommend_active) {
1829 src = Debrief_stages[i]->recommendation_text.c_str();
1830
1831 if ((i == Num_debrief_stages - 1) && !r_count && !*src)
1832 src = XSTR( "We have no recommendations for you.", 1054);
1833
1834 if (*src) {
1835 Num_text_lines += brief_color_text_init("\n", Debrief_text_wnd_coords[gr_screen.res][2], default_recommendation_color, 0, MAX_DEBRIEF_LINES, true);
1836
1837 Num_text_lines += brief_color_text_init(src, Debrief_text_wnd_coords[gr_screen.res][2], default_recommendation_color, 0, MAX_DEBRIEF_LINES, true);
1838 r_count++;
1839
1840 if (use_sim_speech) {
1841 fsspeech_stuff_buffer(src);
1842 fsspeech_stuff_buffer("\n");
1843 }
1844 }
1845 }
1846 }
1847 Brief_text_wipe_time_elapsed = BRIEF_TEXT_WIPE_TIME; // Skip the wipe effect
1848
1849 if(use_sim_speech) {
1850 fsspeech_play_buffer(FSSPEECH_FROM_BRIEFING);
1851 }
1852 return;
1853 }
1854
1855 // not in debriefing mode, must be in stats mode
1856 Num_text_lines = 0;
1857 debrief_setup_ship_kill_stats(Current_stage);
1858 }
1859
1860
1861 // --------------------------------------------------------------------------------------
1862 //
1863
1864 // start up the appropriate music
debrief_init_music()1865 static void debrief_init_music()
1866 {
1867 int score = SCORE_DEBRIEF_AVERAGE;
1868
1869 Debrief_music_timeout = 0;
1870
1871 if ( Turned_traitor || ((Game_mode & GM_CAMPAIGN_MODE) && (Campaign.next_mission == Campaign.current_mission)) ) {
1872 // you failed the mission, so you get the fail music
1873 score = SCORE_DEBRIEF_FAIL;
1874 } else if ( mission_goals_met() ) {
1875 // you completed all primaries and secondaries, so you get the win music
1876 score = SCORE_DEBRIEF_SUCCESS;
1877 } else {
1878 // you somehow passed the mission, so you get a little something for your efforts.
1879 score = SCORE_DEBRIEF_AVERAGE;
1880 }
1881
1882 // if multi client then give a slight delay before playing average music
1883 // since we'd like to eval the goals once more for slow clients
1884 if ( MULTIPLAYER_CLIENT && (score == SCORE_DEBRIEF_AVERAGE) ) {
1885 Debrief_music_timeout = timestamp(2000);
1886 return;
1887 }
1888
1889 common_music_init(score);
1890 }
1891
debrief_init()1892 void debrief_init()
1893 {
1894 Assert(!Debrief_inited);
1895 // Campaign.loop_enabled = 0;
1896 Campaign.loop_mission = CAMPAIGN_LOOP_MISSION_UNINITIALIZED;
1897
1898 // MageKing17 - Set the font so that wordwrapping in brief_color_text_init() calculates based on the same font as the debriefing itself.
1899 font::set_font(DEBRIEFING_FONT);
1900
1901 // set up the right briefing for this guy
1902 if(MULTI_TEAM){
1903 Debriefing = &Debriefings[Net_player->p_info.team];
1904 } else {
1905 Debriefing = &Debriefings[0];
1906 }
1907
1908 // no longer is mission
1909 Game_mode &= ~(GM_IN_MISSION);
1910
1911 game_flush();
1912 Current_mode = -1;
1913 New_mode = DEBRIEF_TAB;
1914 Recommend_active = Award_active = 0;
1915
1916 Current_stage = -1;
1917 New_stage = 0;
1918 Debrief_cue_voice = 0;
1919 Num_text_lines = 0;
1920 Debrief_first_voice_flag = 1;
1921
1922 Debrief_multi_voice_loaded = 0;
1923
1924 if ( (Game_mode & GM_CAMPAIGN_MODE) && ( !MULTIPLAYER_CLIENT ) ) {
1925 // MUST store goals and events first - may be used to evaluate next mission
1926 // store goals and events
1927 mission_campaign_store_goals_and_events();
1928
1929 // evaluate next mission
1930 mission_campaign_eval_next_mission();
1931 }
1932
1933 // call traitor init before calling scoring_level_close. traitor init will essentially nullify
1934 // any stats
1935 if ( !(Game_mode & GM_MULTIPLAYER) ) { // only do for single player
1936 debrief_traitor_init(); // initialize data needed if player becomes traitor.
1937 }
1938
1939 // call scoring level close for my stats. Needed for award_init. The stats will
1940 // be backed out if used chooses to replace them.
1941 scoring_level_close();
1942
1943 debrief_ui_init(); // init UI items
1944 debrief_award_init();
1945 show_stats_init();
1946 debrief_voice_init();
1947
1948 debrief_multi_list_init();
1949
1950 // rank_bitmaps_clear();
1951 // rank_bitmaps_load();
1952
1953 strcpy_s(Debrief_current_callsign, Player->callsign);
1954 Debrief_player = Player;
1955 // Debrief_current_net_player_index = debrief_multi_list[0].net_player_index;
1956
1957 // set up the Debrief_stages[] and Recommendations[] arrays. Only do the following stuff
1958 // for non-clients (i.e. single and game server). Multiplayer clients will get their debriefing
1959 // info directly from the server.
1960 if ( !MULTIPLAYER_CLIENT ) {
1961 debrief_set_stages_and_multi_stuff();
1962
1963 if ( Num_debrief_stages <= 0 ) {
1964 Num_debrief_stages = 0;
1965 } else {
1966 debrief_voice_load_all();
1967 }
1968 } else {
1969 // multiplayer client may have already received their debriefing info. If they have not,
1970 // then set the num debrief stages to 0
1971 if ( !Debrief_multi_stages_loaded ) {
1972 Num_debrief_stages = 0;
1973 }
1974 }
1975
1976 /*
1977 if (mission_evaluate_primary_goals() == PRIMARY_GOALS_COMPLETE) {
1978 common_music_init(SCORE_DEBRIEF_SUCCESS);
1979 } else {
1980 common_music_init(SCORE_DEBRIEF_FAIL);
1981 }
1982 */
1983
1984 // Just calculate this once instead of every frame. -MageKing17
1985 Max_debrief_Lines = Debrief_text_wnd_coords[gr_screen.res][3]/gr_get_font_height(); //Make the max number of lines dependent on the font height.
1986
1987 // start up the appropriate music
1988 debrief_init_music();
1989
1990 if (Game_mode & GM_MULTIPLAYER) {
1991 multi_debrief_init();
1992
1993 // if i'm not the host of the game, disable the multi kick button
1994 if (!(Net_player->flags & NETINFO_FLAG_GAME_HOST)) {
1995 Buttons[gr_screen.res][MULTI_KICK].button.disable();
1996 }
1997 } else {
1998 Buttons[gr_screen.res][PLAYER_SCROLL_UP].button.disable();
1999 Buttons[gr_screen.res][PLAYER_SCROLL_DOWN].button.disable();
2000 Buttons[gr_screen.res][MULTI_PINFO_POPUP].button.disable();
2001 Buttons[gr_screen.res][MULTI_KICK].button.disable();
2002 Buttons[gr_screen.res][PLAYER_SCROLL_UP].button.hide();
2003 Buttons[gr_screen.res][PLAYER_SCROLL_DOWN].button.hide();
2004 Buttons[gr_screen.res][MULTI_PINFO_POPUP].button.hide();
2005 Buttons[gr_screen.res][MULTI_KICK].button.hide();
2006 }
2007
2008 if (!Award_active) {
2009 Buttons[gr_screen.res][MEDALS_BUTTON].button.disable();
2010 Buttons[gr_screen.res][MEDALS_BUTTON].button.hide();
2011 }
2012
2013 Debrief_skip_popup_already_shown = 0;
2014
2015 Debrief_inited = 1;
2016 }
2017
2018 // --------------------------------------------------------------------------------------
2019 // debrief_close()
debrief_close()2020 void debrief_close()
2021 {
2022 int i;
2023 scoring_struct *sc;
2024
2025 Assert(Debrief_inited);
2026
2027 // if the mission wasn't accepted, clear out my stats
2028 // we need to evaluate a little differently for multiplayer since the conditions for "accepting"
2029 // are a little bit different
2030 if (Game_mode & GM_MULTIPLAYER) {
2031 // if stats weren't accepted, backout my own stats
2032 if (multi_debrief_stats_accept_code() != 1) {
2033 if(MULTIPLAYER_MASTER){
2034 for(i=0; i<MAX_PLAYERS; i++){
2035 if(MULTI_CONNECTED(Net_players[i]) && !MULTI_STANDALONE(Net_players[i]) && !MULTI_PERM_OBSERVER(Net_players[i]) && (Net_players[i].m_player != NULL)){
2036 sc = &Net_players[i].m_player->stats;
2037 scoring_backout_accept(sc);
2038
2039 if (Net_player == &Net_players[i]) {
2040 Pilot.update_stats_backout( sc );
2041 }
2042 }
2043 }
2044 } else {
2045 scoring_backout_accept( &Player->stats );
2046 Pilot.update_stats_backout( &Player->stats );
2047 }
2048 }
2049 } else {
2050 // single player
2051 if( !Debrief_accepted || !(Game_mode & GM_CAMPAIGN_MODE) ){
2052 // Make sure we don't skip any missions in a campaign.
2053 if ( Game_mode & GM_CAMPAIGN_MODE ) {
2054 Campaign.next_mission = Campaign.current_mission;
2055 }
2056 scoring_backout_accept( &Player->stats );
2057 Pilot.update_stats_backout( &Player->stats );
2058 }
2059 }
2060
2061 // if dude passed the misson and accepted, reset his show skip popup flag
2062 if (Debrief_accepted) {
2063 Player->show_skip_popup = 1;
2064 }
2065
2066 // clear out debrief info parsed from mission file - taylor
2067 mission_debrief_common_reset();
2068
2069 // clear out award text
2070 Debrief_award_text_num_lines = 0;
2071
2072 debrief_voice_unload_all();
2073 common_music_close();
2074 chatbox_close();
2075
2076 // unload bitmaps
2077 if (Background_bitmap >= 0){
2078 bm_release(Background_bitmap);
2079 }
2080
2081 if (Award_bg_bitmap >= 0){
2082 bm_release(Award_bg_bitmap);
2083 }
2084
2085 if (Rank_bitmap >= 0){
2086 bm_release(Rank_bitmap);
2087 }
2088
2089 if (Medal_bitmap >= 0){
2090 bm_release(Medal_bitmap);
2091 }
2092
2093 if (Badge_bitmap >= 0){
2094 bm_release(Badge_bitmap);
2095 }
2096
2097 Debrief_ui_window.destroy();
2098 common_free_interface_palette(); // restore game palette
2099 show_stats_close();
2100
2101 if (Game_mode & GM_MULTIPLAYER){
2102 multi_debrief_close();
2103 }
2104
2105 if(Debrief_stats_kills != NULL)
2106 {
2107 delete[] Debrief_stats_kills;
2108 Debrief_stats_kills = NULL;
2109 }
2110 game_flush();
2111
2112 Stage_voice = -1;
2113
2114 Debriefing_paused = 0;
2115
2116 Debrief_inited = 0;
2117 }
2118
2119 // handle keypresses in debriefing
debrief_do_keys(int new_k)2120 void debrief_do_keys(int new_k)
2121 {
2122 switch (new_k) {
2123 case KEY_TAB:
2124 debrief_next_tab();
2125 break;
2126
2127 case KEY_SHIFTED | KEY_TAB:
2128 debrief_prev_tab();
2129 break;
2130
2131 case KEY_ESC: {
2132 int pf_flags;
2133 int choice;
2134
2135 // multiplayer accept popup is a little bit different
2136 if (Game_mode & GM_MULTIPLAYER) {
2137 multi_debrief_esc_hit();
2138 } else {
2139 // display the normal debrief popup
2140 if (!Turned_traitor && !Must_replay_mission && (Game_mode & GM_CAMPAIGN_MODE)) {
2141 pf_flags = PF_BODY_BIG; // | PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON;
2142 choice = popup(pf_flags, 3, POPUP_CANCEL, XSTR( "&Yes", 454), XSTR( "&No, retry later", 455), XSTR( "Accept this mission outcome?", 456));
2143 if (choice == 1) { // accept and continue on
2144 debrief_accept(0);
2145 gameseq_post_event(GS_EVENT_MAIN_MENU);
2146 }
2147
2148 if (choice < 1)
2149 break;
2150
2151 } else if ( ( Turned_traitor || Must_replay_mission ) && (Game_mode & GM_CAMPAIGN_MODE)) {
2152 // need to popup saying that mission was a failure and must be replayed
2153 choice = popup(0, 2, POPUP_NO, POPUP_YES, XSTR( "Because this mission was a failure, you must replay this mission when you continue your campaign.\n\nReturn to the Flight Deck?", 457));
2154 if (choice <= 0) {
2155 break;
2156 }
2157 }
2158
2159 // Return to Main Hall
2160 gameseq_post_event(GS_EVENT_END_GAME);
2161 }
2162 }
2163
2164 default:
2165 break;
2166 } // end switch
2167 }
2168
2169 // uuuuuugly
debrief_draw_award_text()2170 void debrief_draw_award_text()
2171 {
2172 int start_y, curr_y, i, x, sw;
2173 int fh = gr_get_font_height();
2174 int field_width = (Medal_bitmap > 0) ? Debrief_award_text_width[gr_screen.res][DB_WITH_MEDAL] : Debrief_award_text_width[gr_screen.res][DB_WITHOUT_MEDAL];
2175
2176 // vertically centered within field
2177 start_y = Debrief_award_text_coords[gr_screen.res][1] + ((Debrief_award_text_coords[gr_screen.res][2] - (fh * Debrief_award_text_num_lines)) / 2);
2178 curr_y = start_y;
2179
2180 // draw the strings
2181 for (i=0; i<Debrief_award_text_num_lines; i++) {
2182 gr_get_string_size(&sw, NULL, Debrief_award_text[i]);
2183 x = (Medal_bitmap < 0) ? (Debrief_award_text_coords[gr_screen.res][0] + (field_width - sw) / 2) : Debrief_award_text_coords[gr_screen.res][0];
2184 if (i==AWARD_TEXT_MAX_LINES-1) x += 7; // hack because of the shape of the box
2185 gr_set_color_fast(&Color_white);
2186 gr_string(x, curr_y, Debrief_award_text[i], GR_RESIZE_MENU);
2187
2188 // adjust y pos, including a little extra between the "pairs"
2189 curr_y += fh;
2190 if ((i == 1) || (i == 3)) {
2191 curr_y += ((gr_screen.res == GR_640) ? 2 : 6);
2192 }
2193 }
2194 }
2195
2196 // clears out text array so we don't have old award text showing up on new awards.
debrief_award_text_clear()2197 void debrief_award_text_clear() {
2198 int i;
2199
2200 Debrief_award_text_num_lines = 0;
2201 for (i=0; i<AWARD_TEXT_MAX_LINES; i++) {
2202 //Debrief_award_text[i][0] = 0;
2203 memset(Debrief_award_text[i], 0, sizeof(char)*AWARD_TEXT_MAX_LINE_LENGTH);
2204 }
2205 }
2206
2207 // this is the nastiest code I have ever written. if you are modifying this, i feel bad for you.
debrief_add_award_text(const char * str)2208 void debrief_add_award_text(const char *str)
2209 {
2210 Assert(Debrief_award_text_num_lines < AWARD_TEXT_MAX_LINES);
2211 if (Debrief_award_text_num_lines >= AWARD_TEXT_MAX_LINES) {
2212 return;
2213 }
2214
2215 char *line2;
2216 int field_width = (Medal_bitmap > 0) ? Debrief_award_text_width[gr_screen.res][DB_WITH_MEDAL] : Debrief_award_text_width[gr_screen.res][DB_WITHOUT_MEDAL];
2217
2218 // copy in the line
2219 strcpy_s(Debrief_award_text[Debrief_award_text_num_lines], str);
2220
2221 if (!Disable_built_in_translations) {
2222 // maybe translate for displaying
2223 if (Lcl_gr) {
2224 lcl_translate_medal_name_gr(Debrief_award_text[Debrief_award_text_num_lines]);
2225 } else if (Lcl_pl) {
2226 lcl_translate_medal_name_pl(Debrief_award_text[Debrief_award_text_num_lines]);
2227 }
2228 }
2229
2230 Debrief_award_text_num_lines++;
2231
2232 // if its too long, split once ONLY
2233 // assumes text isnt > 2 lines, but this is a safe assumption due to the char limits of the ranks/badges/etc
2234 if (Debrief_award_text_num_lines < AWARD_TEXT_MAX_LINES) {
2235 line2 = split_str_once(Debrief_award_text[Debrief_award_text_num_lines-1], field_width);
2236 if (line2 != NULL) {
2237 sprintf(Debrief_award_text[Debrief_award_text_num_lines], " %s", line2); // indent a space
2238 }
2239 Debrief_award_text_num_lines++; // leave blank line even if it all fits into 1
2240 }
2241 }
2242
2243 // called once per frame to drive all the input reading and rendering
debrief_do_frame(float frametime)2244 void debrief_do_frame(float frametime)
2245 {
2246 int k=0, new_k=0;
2247 const char *please_wait_str = XSTR("Please Wait", 1242);
2248 char buf[256];
2249
2250 Assert(Debrief_inited);
2251
2252 // Goober5000 - accept immediately if skipping debriefing
2253 if (The_mission.flags[Mission::Mission_Flags::Toggle_debriefing])
2254 {
2255 // make sure that we can actually advance - we don't want an endless loop!!!
2256 if ( !((/*Cheats_enabled ||*/ Turned_traitor || Must_replay_mission) && (Game_mode & GM_CAMPAIGN_MODE)) )
2257 {
2258 debrief_accept();
2259 return;
2260 }
2261 }
2262
2263 int str_w, str_h;
2264
2265 // first thing is to load the files
2266 if ( MULTIPLAYER_CLIENT && !Debrief_multi_stages_loaded ) {
2267 // draw the background, etc
2268 GR_MAYBE_CLEAR_RES(Background_bitmap);
2269 if (Background_bitmap >= 0) {
2270 gr_set_bitmap(Background_bitmap);
2271 gr_bitmap(0, 0, GR_RESIZE_MENU);
2272 }
2273
2274 Debrief_ui_window.draw();
2275 chatbox_render();
2276 if ( Debrief_multi_loading_bitmap > -1 ){
2277 gr_set_bitmap(Debrief_multi_loading_bitmap);
2278 gr_bitmap( Please_wait_coords[gr_screen.res][0], Please_wait_coords[gr_screen.res][1], GR_RESIZE_MENU );
2279 }
2280
2281 // draw "Please Wait"
2282 gr_set_color_fast(&Color_normal);
2283 font::set_font(font::FONT2);
2284 gr_get_string_size(&str_w, &str_h, please_wait_str);
2285 gr_string((gr_screen.max_w_unscaled - str_w) / 2, (gr_screen.max_h_unscaled - str_h) / 2, please_wait_str, GR_RESIZE_MENU);
2286 font::set_font(font::FONT1);
2287
2288 gr_flip();
2289
2290 // make sure we run the debrief do frame
2291 if (Game_mode & GM_MULTIPLAYER) {
2292 multi_debrief_do_frame();
2293 }
2294
2295 // esc pressed?
2296 os_poll();
2297 int keypress = game_check_key();
2298 if(keypress == KEY_ESC){
2299 // popup to leave
2300 multi_quit_game(PROMPT_CLIENT);
2301 }
2302
2303 return;
2304 }
2305
2306 // if multiplayer client, and not loaded voice, then load it
2307 if ( MULTIPLAYER_CLIENT && !Debrief_multi_voice_loaded ) {
2308 debrief_multi_fixup_stages();
2309 debrief_voice_load_all();
2310 Debrief_multi_voice_loaded = 1;
2311 }
2312
2313 if ( help_overlay_active(Debrief_overlay_id) ) {
2314 Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
2315 Debrief_ui_window.set_ignore_gadgets(1);
2316 }
2317
2318
2319 k = chatbox_process();
2320
2321 if ( Game_mode & GM_NORMAL ) {
2322 new_k = Debrief_ui_window.process(k);
2323 } else {
2324 new_k = Debrief_ui_window.process(k, 0);
2325 }
2326
2327 if ( (k > 0) || (new_k > 0) || B1_JUST_RELEASED ) {
2328 if ( help_overlay_active(Debrief_overlay_id) ) {
2329 help_overlay_set_state(Debrief_overlay_id, gr_screen.res, 0);
2330 Debrief_ui_window.set_ignore_gadgets(0);
2331 k = 0;
2332 new_k = 0;
2333 }
2334 }
2335
2336 if ( !help_overlay_active(Debrief_overlay_id) ) {
2337 Debrief_ui_window.set_ignore_gadgets(0);
2338 }
2339
2340 // don't show pilot info popup by default
2341 Debrief_should_show_popup = 0;
2342
2343 // see if the mode has changed and handle it if so.
2344 if ( Current_mode != New_mode ) {
2345 debrief_voice_stop();
2346 Current_mode = New_mode;
2347 Current_stage = -1;
2348 New_stage = 0;
2349 if (New_mode == DEBRIEF_TAB) {
2350 Num_stages = 1;
2351 Debrief_cue_voice = 0;
2352 Stage_voice = -1;
2353 if (Debrief_first_voice_flag) {
2354 Debrief_cue_voice = timestamp(DEBRIEF_VOICE_DELAY * 3);
2355 Debrief_first_voice_flag = 0;
2356 }
2357 } else {
2358 Num_stages = DEBRIEF_NUM_STATS_PAGES;
2359 }
2360 }
2361
2362 if ((Num_stages > 0) && (New_stage != Current_stage)) {
2363 Current_stage = New_stage;
2364 debrief_text_init();
2365 }
2366
2367 debrief_voice_play();
2368
2369 // multi clients get a slight delay before music start to check goals again
2370 if ( timestamp_valid(Debrief_music_timeout) ) {
2371 if ( timestamp_elapsed(Debrief_music_timeout) ) {
2372 Debrief_music_timeout = 0;
2373
2374 if ( mission_goals_met() ) {
2375 common_music_init(SCORE_DEBRIEF_SUCCESS);
2376 } else {
2377 common_music_init(SCORE_DEBRIEF_AVERAGE);
2378 }
2379
2380 common_music_do();
2381 }
2382 } else {
2383 common_music_do();
2384 }
2385
2386 if (Game_mode & GM_MULTIPLAYER) {
2387 multi_debrief_do_frame();
2388 }
2389
2390 // Now do all the rendering for the frame
2391 GR_MAYBE_CLEAR_RES(Background_bitmap);
2392 if (Background_bitmap >= 0) {
2393 gr_set_bitmap(Background_bitmap);
2394 gr_bitmap(0, 0, GR_RESIZE_MENU);
2395 }
2396
2397 // draw the awarded stuff, G
2398 if ( Award_active && (Award_bg_bitmap >= 0) ) {
2399 gr_set_bitmap(Award_bg_bitmap);
2400 gr_bitmap(Debrief_award_wnd_coords[gr_screen.res][0], Debrief_award_wnd_coords[gr_screen.res][1], GR_RESIZE_MENU);
2401 if (Rank_bitmap >= 0) {
2402 gr_set_bitmap(Rank_bitmap);
2403 gr_bitmap(Debrief_award_coords[gr_screen.res][0], Debrief_award_coords[gr_screen.res][1], GR_RESIZE_MENU);
2404 }
2405
2406 if (Medal_bitmap >= 0) {
2407 gr_set_bitmap(Medal_bitmap);
2408 gr_bitmap(Debrief_award_coords[gr_screen.res][0], Debrief_award_coords[gr_screen.res][1], GR_RESIZE_MENU);
2409 }
2410
2411 if (Badge_bitmap >= 0) {
2412 gr_set_bitmap(Badge_bitmap);
2413 gr_bitmap(Debrief_award_coords[gr_screen.res][0], Debrief_award_coords[gr_screen.res][1], GR_RESIZE_MENU);
2414 }
2415
2416 // draw medal/badge/rank labels
2417 debrief_draw_award_text();
2418 }
2419
2420 Debrief_ui_window.draw();
2421 debrief_redraw_pressed_buttons();
2422 Buttons[gr_screen.res][Current_mode].button.draw_forced(2);
2423 if (Recommend_active && (Current_mode != STATS_TAB)) {
2424 Buttons[gr_screen.res][RECOMMENDATIONS].button.draw_forced(2);
2425 }
2426
2427 // draw the title of the mission
2428 gr_set_color_fast(&Color_bright_white);
2429 strcpy_s(buf, The_mission.name);
2430 font::force_fit_string(buf, 255, Debrief_title_coords[gr_screen.res][2]);
2431 gr_string(Debrief_title_coords[gr_screen.res][0], Debrief_title_coords[gr_screen.res][1], buf, GR_RESIZE_MENU);
2432
2433 #if !defined(NDEBUG)
2434 gr_set_color_fast(&Color_normal);
2435 gr_printf_menu(Debrief_title_coords[gr_screen.res][0], Debrief_title_coords[gr_screen.res][1] - 10, NOX("[name: %s, mod: %s]"), Mission_filename, The_mission.modified);
2436 #endif
2437
2438 // Set the font for the debriefing instead of relying on the implicit font-setting of Debrief_ui_window.draw() -MageKing17
2439 font::set_font(DEBRIEFING_FONT);
2440
2441 // draw the screen-specific text
2442 switch (Current_mode) {
2443 case DEBRIEF_TAB:
2444 if ( Num_debrief_stages <= 0 ) {
2445 gr_set_color_fast(&Color_white);
2446 Assert( Game_current_mission_filename != NULL );
2447 gr_printf_menu(Debrief_text_wnd_coords[gr_screen.res][0], Debrief_text_wnd_coords[gr_screen.res][1], XSTR( "No Debriefing for mission: %s", 458), Game_current_mission_filename);
2448
2449 } else {
2450 brief_render_text(Text_offset, Debrief_text_wnd_coords[gr_screen.res][0], Debrief_text_wnd_coords[gr_screen.res][1], Debrief_text_wnd_coords[gr_screen.res][3], frametime);
2451 }
2452
2453 break;
2454
2455 case STATS_TAB:
2456 debrief_stats_render();
2457 break;
2458 } // end switch
2459
2460 if ( (Max_debrief_Lines + Text_offset) < Num_text_lines ) {
2461 int w;
2462
2463 gr_set_color_fast(&Color_more_indicator);
2464 gr_get_string_size(&w, NULL, XSTR( "More", 459));
2465 gr_printf_menu(Debrief_text_wnd_coords[gr_screen.res][0] + Debrief_text_wnd_coords[gr_screen.res][2] / 2 - w / 2, Debrief_text_wnd_coords[gr_screen.res][1] + Debrief_text_wnd_coords[gr_screen.res][3], "%s", XSTR( "More", 459));
2466 }
2467
2468 debrief_render_stagenum();
2469 debrief_multi_list_draw();
2470
2471 // render some extra stuff in multiplayer
2472 if (Game_mode & GM_MULTIPLAYER) {
2473 // render the chatbox last
2474 chatbox_render();
2475
2476 // draw tooltips
2477 Debrief_ui_window.draw_tooltip();
2478
2479 // render the status indicator for the voice system
2480 multi_common_voice_display_status();
2481 }
2482
2483 // AL 3-6-98: Needed to move key reading here, since popups are launched from this code, and we don't
2484 // want to include the mouse pointer which is drawn in the flip
2485
2486 if ( !help_overlay_active(Debrief_overlay_id) ) {
2487 debrief_check_buttons();
2488 debrief_do_keys(new_k);
2489 }
2490
2491 // blit help overlay if active
2492 help_overlay_maybe_blit(Debrief_overlay_id, gr_screen.res);
2493
2494 gr_flip();
2495
2496 // maybe show skip mission popup
2497 if ( Must_replay_mission && (!Debrief_skip_popup_already_shown) && (Player->show_skip_popup) && (Game_mode & GM_NORMAL) && (Game_mode & GM_CAMPAIGN_MODE) && (Player->failures_this_session >= PLAYER_MISSION_FAILURE_LIMIT) && !(Game_mode & GM_MULTIPLAYER)) {
2498 int popup_choice = popup(0, 3, XSTR("Do Not Skip This Mission", 1473),
2499 XSTR("Advance To The Next Mission", 1474),
2500 XSTR("Don't Show Me This Again", 1475),
2501 XSTR("You have failed this mission five times. If you like, you may advance to the next mission.", 1472) );
2502 switch (popup_choice) {
2503 case 0:
2504 // stay on this mission, so proceed to normal debrief
2505 // in other words, do nothing.
2506 break;
2507 case 1:
2508 // skip this mission
2509 mission_campaign_skip_to_next();
2510 gameseq_post_event(GS_EVENT_START_GAME);
2511 break;
2512 case 2:
2513 // don't show this again
2514 Player->show_skip_popup = 0;
2515 break;
2516 }
2517
2518 Debrief_skip_popup_already_shown = 1;
2519 }
2520
2521 // check to see if we should be showing a pilot info popup in multiplayer (if a guy was double clicked)
2522 if ((Game_mode & GM_MULTIPLAYER) && Debrief_should_show_popup) {
2523 Assert((Multi_list_select >= 0) && (Multi_list_select < Multi_list_size));
2524 multi_pinfo_popup(&Net_players[Multi_list[Multi_list_select].net_player_index]);
2525
2526 Debrief_should_show_popup = 0;
2527 }
2528 }
2529
debrief_rebuild_player_list()2530 void debrief_rebuild_player_list()
2531 {
2532 int i;
2533 net_player *np;
2534 debrief_multi_list_info *list;
2535
2536 Multi_list_size = 0; // number of net players to choose from
2537
2538 for ( i=0; i<MAX_PLAYERS; i++ ) {
2539 np = &Net_players[i];
2540 // remember not to include the standalone.
2541 if ( MULTI_CONNECTED((*np)) && !MULTI_STANDALONE((*np))){
2542 list = &Multi_list[Multi_list_size++];
2543 list->net_player_index = i;
2544 strcpy_s(list->callsign, np->m_player->callsign);
2545
2546 // make sure to leave some room to blit the team indicator
2547 font::force_fit_string(list->callsign, CALLSIGN_LEN - 1, Debrief_list_coords[gr_screen.res][2] - MULTI_LIST_TEAM_OFFSET);
2548 }
2549 } // end for
2550 }
2551
debrief_handle_player_drop()2552 void debrief_handle_player_drop()
2553 {
2554 debrief_rebuild_player_list();
2555 }
2556
debrief_disable_accept()2557 void debrief_disable_accept()
2558 {
2559 }
2560
2561 // Goober5000 - replace any variables with their values
debrief_replace_stage_text(debrief_stage & stage)2562 void debrief_replace_stage_text(debrief_stage &stage)
2563 {
2564 sexp_replace_variable_names_with_values(stage.text);
2565 sexp_replace_variable_names_with_values(stage.recommendation_text);
2566 }
2567