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