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 
14 #define POPUPDEAD_NUM_CHOICES				3		// normal
15 #define POPUPDEAD_NUM_CHOICES_RA			4		// red alert
16 #define POPUPDEAD_NUM_CHOICES_SKIP		3		// skip mission menu
17 
18 #define POPUPDEAD_NUM_CHOICES_MAX		4
19 
20 #include "freespace.h"
21 #include "gamesequence/gamesequence.h"
22 #include "gamesnd/gamesnd.h"
23 #include "globalincs/alphacolors.h"
24 #include "hud/hudmessage.h"
25 #include "io/key.h"
26 #include "io/timer.h"
27 #include "mission/missionparse.h"
28 #include "mission/missioncampaign.h"
29 #include "network/multi.h"
30 #include "network/multiutil.h"
31 #include "playerman/player.h"
32 #include "popup/popup.h"
33 #include "popup/popupdead.h"
34 #include "ui/ui.h"
35 
36 
37 UI_WINDOW	Popupdead_window;
38 UI_BUTTON	Popupdead_buttons[POPUPDEAD_NUM_CHOICES_MAX];				// actual lit buttons
39 UI_BUTTON	Popupdead_button_regions[POPUPDEAD_NUM_CHOICES_MAX];	// fake buttons used for mouse detection over text
40 
41 int Popupdead_region_coords[GR_NUM_RESOLUTIONS][POPUPDEAD_NUM_CHOICES_MAX][4] =
42 {
43 	{	// GR_640
44 		{464, 389, 497, 403},		// upper right pixel of text, lower right pixel of button (for tiny popup)
45 		{464, 413, 497, 427},
46 		{464, 435, 497, 446},
47 		{464, 457, 497, 466},
48 	},
49 	{	// GR_1024
50 		{745, 627, 809, 664},
51 		{745, 663, 809, 700},		// upper right pixel of text, lower right pixel of button (for tiny popup)
52 		{745, 699, 809, 736},
53 		{745, 735, 809, 772},
54 	},
55 };
56 
57 int Popupdead_button_coords[GR_NUM_RESOLUTIONS][POPUPDEAD_NUM_CHOICES_MAX][2] =
58 {
59 	{	// GR_640
60 		{478, 387},						// upper left pixel (tiny popup)
61 		{478, 410},
62 		{478, 432},
63 		{478, 455},
64 	},
65 	{	// GR_1024
66 		{760, 620},						// upper left pixel (tiny popup)
67 		{760, 656},
68 		{760, 692},
69 		{760, 728},
70 	}
71 };
72 
73 const char *Popupdead_background_filename[GR_NUM_RESOLUTIONS] = {
74 	"PopDeath",		// GR_640
75 	"2_PopDeath"	// GR-1024
76 };
77 
78 int Popupdead_background_coords[GR_NUM_RESOLUTIONS][2] =
79 {
80 	{ // GR_640
81 		131, 363
82 	},
83 	{ // GR_1024
84 		205, 581
85 	}
86 };
87 
88 const char *Popupdead_button_filenames[GR_NUM_RESOLUTIONS][POPUPDEAD_NUM_CHOICES_MAX] =
89 {
90 	{	// GR_640
91 		"PopD_00",				// first choice
92 		"PopD_01",				// second choice
93 		"PopD_02",				// third choice
94 		"PopD_03",				// fourth choice
95 	},
96 	{	// GR_1024
97 		"2_PopD_00",			// first choice
98 		"2_PopD_01",			// second choice
99 		"2_PopD_02",			// third choice
100 		"2_PopD_03",			// fourth choice
101 	}
102 };
103 
104 int Popupdead_skip_message_y[GR_NUM_RESOLUTIONS] = {
105 		96,	// GR_640
106 		160
107 };
108 
109 
110 static const char *Popupdead_button_text[POPUPDEAD_NUM_CHOICES_MAX];
111 
112 // multiplayer specifics to help with return values since they can vary
113 #define POPUPDEAD_OBS_ONLY			1
114 #define POPUPDEAD_OBS_QUIT			2
115 #define POPUPDEAD_RESPAWN_ONLY	3
116 #define POPUPDEAD_RESPAWN_QUIT	4
117 
118 int Popupdead_default_choice;		// What the default choice is (ie activated when Enter pressed)
119 int Popupdead_active	=	0;			// A dead popup is active
120 int Popupdead_choice;				// Index for choice picked (-1 if none picked)
121 int Popupdead_num_choices;			// number of buttons
122 int Popupdead_multi_type;			// what kind of popup is active for muliplayer
123 int Popupdead_skip_active = 0;	// The skip-misison popup is active
124 int Popupdead_skip_already_shown = 0;
125 
126 int Popupdead_timer;
127 
128 extern int Cmdline_mpnoreturn;
129 // Initialize the dead popup data
popupdead_start()130 void popupdead_start()
131 {
132 	int			i;
133 	UI_BUTTON	*b;
134 
135 	if ( Popupdead_active ) {
136 		return;
137 	}
138 
139 	// increment number of deaths
140 	Player->failures_this_session++;
141 
142 
143 	// create base window
144 	Popupdead_window.create(Popupdead_background_coords[gr_screen.res][0], Popupdead_background_coords[gr_screen.res][1], 1, 1, 0);
145 	Popupdead_window.set_foreground_bmap(Popupdead_background_filename[gr_screen.res]);
146 
147 	Popupdead_num_choices = 0;
148 	Popupdead_multi_type = -1;
149 
150 	if ((The_mission.max_respawn_delay >= 0) && ( Game_mode & GM_MULTIPLAYER )) {
151 		Popupdead_timer = timestamp(The_mission.max_respawn_delay * 1000);
152 		if (Game_mode & GM_MULTIPLAYER) {
153 			if(!(Net_player->flags & NETINFO_FLAG_LIMBO)){
154 				if (The_mission.max_respawn_delay) {
155 					HUD_printf("Player will automatically respawn in %d seconds", The_mission.max_respawn_delay);
156 				}
157 				else {
158 					HUD_printf("Player will automatically respawn now");
159 				}
160 			}
161 		}
162 	}
163 
164 	if ( Game_mode & GM_NORMAL ) {
165 		// also do a campaign check here?
166 		if (0) { //((Player->show_skip_popup) && (!Popupdead_skip_already_shown) && (Game_mode & GM_CAMPAIGN_MODE) && (Game_mode & GM_NORMAL) && (Player->failures_this_session >= PLAYER_MISSION_FAILURE_LIMIT)) {
167 			// init the special preliminary death popup that gives the skip option
168 			Popupdead_button_text[0] = XSTR( "Do Not Skip This Mission", 1473);
169 			Popupdead_button_text[1] = XSTR( "Advance To The Next Mission", 1474);
170 			Popupdead_button_text[2] = XSTR( "Don't Show Me This Again", 1475);
171 			Popupdead_num_choices = POPUPDEAD_NUM_CHOICES_SKIP;
172 			Popupdead_skip_active = 1;
173 		} else if(The_mission.flags[Mission::Mission_Flags::Red_alert]) {
174 			// We can't staticly declare these because they are externalized
175 			Popupdead_button_text[0] = XSTR( "Quick Start Mission", 105);
176 			Popupdead_button_text[1] = XSTR( "Return To Flight Deck", 106);
177 			Popupdead_button_text[2] = XSTR( "Return To Briefing", 107);
178 			Popupdead_button_text[3] = XSTR( "Replay previous mission", 1432);
179 			Popupdead_num_choices = POPUPDEAD_NUM_CHOICES_RA;
180 		} else {
181 			Popupdead_button_text[0] = XSTR( "Quick Start Mission", 105);
182 			Popupdead_button_text[1] = XSTR( "Return To Flight Deck", 106);
183 			Popupdead_button_text[2] = XSTR( "Return To Briefing", 107);
184 			Popupdead_num_choices = POPUPDEAD_NUM_CHOICES;
185 		}
186 	} else {
187 		// in multiplayer, we have different choices depending on respawn mode, etc.
188 
189 		// if the player has run out of respawns and must either quit and become an observer
190 		if(Net_player->flags & NETINFO_FLAG_LIMBO){
191 
192 			// the master should not be able to quit the game
193 			if( ((Net_player->flags & NETINFO_FLAG_AM_MASTER) && (multi_num_players() > 1)) || (Net_player->flags & NETINFO_FLAG_TEAM_CAPTAIN) ) {
194 				Popupdead_button_text[0] = XSTR( "Observer Mode", 108);
195 				Popupdead_num_choices = 1;
196 				Popupdead_multi_type = POPUPDEAD_OBS_ONLY;
197 			} else {
198 				Popupdead_button_text[0] = XSTR( "Observer Mode", 108);
199 				Popupdead_button_text[1] = XSTR( "Return To Flight Deck", 106);
200 				Popupdead_num_choices = 2;
201 				Popupdead_multi_type = POPUPDEAD_OBS_QUIT;
202 			}
203 		} else {
204 			// the master of the game should not be allowed to quit
205 			if ( ((Net_player->flags & NETINFO_FLAG_AM_MASTER) && (multi_num_players() > 1)) || (Net_player->flags & NETINFO_FLAG_TEAM_CAPTAIN) ) {
206 				Popupdead_button_text[0] = XSTR( "Respawn", 109);
207 				Popupdead_num_choices = 1;
208 				Popupdead_multi_type = POPUPDEAD_RESPAWN_ONLY;
209 			} else {
210 				Popupdead_button_text[0] = XSTR( "Respawn", 109);
211 				if(!Cmdline_mpnoreturn)
212 				{
213 					Popupdead_button_text[1] = XSTR( "Return To Flight Deck", 106);
214 					Popupdead_num_choices = 2;
215 				}
216 				else
217 				{
218 					Popupdead_num_choices = 1;
219 				}
220 				Popupdead_multi_type = POPUPDEAD_RESPAWN_QUIT;
221 			}
222 		}
223 	}
224 
225 	// create buttons
226 	for (i=0; i < Popupdead_num_choices; i++) {
227 		b = &Popupdead_buttons[i];
228 		b->create(&Popupdead_window, "", Popupdead_button_coords[gr_screen.res][i][0], Popupdead_button_coords[gr_screen.res][i][1], 30, 20, 0, 1);
229 		b->set_bmaps(Popupdead_button_filenames[gr_screen.res][i], 3, 0);
230 		b->set_highlight_action(common_play_highlight_sound);
231 
232 		// create invisible buttons to detect mouse presses... can't use mask since button region is dynamically sized
233 		int lx, w, h;
234 		gr_get_string_size(&w, &h, Popupdead_button_text[i]);
235 		lx = Popupdead_region_coords[gr_screen.res][i][0] - w;
236 		b = &Popupdead_button_regions[i];
237 		b->create(&Popupdead_window, "", lx, Popupdead_region_coords[gr_screen.res][i][1], Popupdead_region_coords[gr_screen.res][i][2]-lx, Popupdead_region_coords[gr_screen.res][i][3]-Popupdead_region_coords[gr_screen.res][i][1], 0, 1);
238 		b->hide();
239 	}
240 
241 	io::mouse::CursorManager::get()->pushStatus();
242 	io::mouse::CursorManager::get()->showCursor(true);
243 
244 	Popupdead_default_choice = 0;
245 	Popupdead_choice = -1;
246 	Popupdead_active = 1;
247 }
248 
249 // maybe play a sound when key up/down is pressed to switch default choice
popupdead_play_default_change_sound()250 void popupdead_play_default_change_sound()
251 {
252 	int i, mouse_over=0;
253 	UI_BUTTON *br, *b;
254 
255 	// only play if mouse not currently highlighting a choice
256 	for ( i = 0; i < Popupdead_num_choices; i++ ) {
257 		br = &Popupdead_button_regions[i];
258 		b = &Popupdead_buttons[i];
259 		if ( br->button_down() ) {
260 			mouse_over=1;
261 			break;
262 		}
263 
264 		if ( br->button_hilighted() && !b->button_down() ) {
265 			mouse_over=1;
266 			break;
267 		}
268 
269 		if ( b->button_hilighted() ) {
270 			mouse_over=1;
271 		}
272 	}
273 
274 	if (!mouse_over) {
275 		gamesnd_play_iface(InterfaceSounds::USER_SELECT);
276 	}
277 }
278 
279 // do any key processing here
280 // exit:	-1		=>	nothing was done
281 //			>=0	=> a choice was selected
popupdead_process_keys(int k)282 int popupdead_process_keys(int k)
283 {
284 	int masked_k;
285 
286 	if ( k <= 0 ) {
287 		return -1;
288 	}
289 
290 	switch(k) {
291 
292 	case KEY_ENTER:
293 		return Popupdead_default_choice;	// select the current default choice
294 		break;
295 
296 	case KEY_ESC:
297 		if (Popupdead_skip_active) {
298 			return 0;								// 0 mimics a "do not skip"
299 		} else {
300 			return 1;								// do nothing here for now - 1 mimics a "return to flight deck"
301 		}
302 		break;
303 
304 	case KEY_DOWN:
305 	case KEY_PAD2:
306 	case KEY_TAB:
307 		popupdead_play_default_change_sound();
308 		Popupdead_default_choice++;
309 		if ( Popupdead_default_choice >= Popupdead_num_choices ) {
310 			Popupdead_default_choice=0;
311 		}
312 		break;
313 
314 	case KEY_UP:
315 	case KEY_PAD8:
316 	case KEY_SHIFTED+KEY_TAB:
317 		popupdead_play_default_change_sound();
318 		Popupdead_default_choice--;
319 		if ( Popupdead_default_choice < 0 ) {
320 			Popupdead_default_choice=Popupdead_num_choices-1;
321 		}
322 		break;
323 
324 	case KEY_PAUSE:
325 		game_process_pause_key();
326 		break;
327 
328 	default:
329 		break;
330 	} // end switch
331 
332 	// read the dead key set
333 	masked_k = k & ~KEY_CTRLED;	// take out CTRL modifier only
334 	process_set_of_keys(masked_k, Dead_key_set_size, Dead_key_set);
335 	button_info_do(&Player->bi);	// call functions based on status of button_info bit vectors
336 
337 	return -1;
338 }
339 
340 
341 // see if any popup buttons have been pressed
342 // exit: -1						=> no buttons pressed
343 //			>=0					=>	button index that was pressed
popupdead_check_buttons()344 int popupdead_check_buttons()
345 {
346 	int			i;
347 	UI_BUTTON	*b;
348 
349 	for ( i = 0; i < Popupdead_num_choices; i++ ) {
350 		b = &Popupdead_button_regions[i];
351 		if ( b->pressed() ) {
352 			return i;
353 		}
354 
355 		b = &Popupdead_buttons[i];
356 		if ( b->pressed() ) {
357 			return i;
358 		}
359 	}
360 
361 	return -1;
362 }
363 
364 // See if any of the button should change appearance based on mouse position
popupdead_force_draw_buttons()365 void popupdead_force_draw_buttons()
366 {
367 	int i,mouse_is_highlighting=0;
368 	UI_BUTTON *br, *b;
369 
370 	for ( i = 0; i < Popupdead_num_choices; i++ ) {
371 		br = &Popupdead_button_regions[i];
372 		b = &Popupdead_buttons[i];
373 		if ( br->button_down() ) {
374 			b->draw_forced(2);
375 			mouse_is_highlighting=1;
376 			continue;
377 		}
378 
379 		if ( (b->button_hilighted()) || (br->button_hilighted() && !b->button_down()) ) {
380 			Popupdead_default_choice=i;
381 			mouse_is_highlighting=1;
382 			b->draw_forced(1);
383 		}
384 	}
385 
386 	// Only if mouse is not highlighting an option, let the default choice be drawn highlighted
387 	if ( (!mouse_is_highlighting) && (Popupdead_num_choices>1) ) {
388 		for ( i = 0; i < Popupdead_num_choices; i++ ) {
389 			b = &Popupdead_buttons[i];
390 			// highlight the default choice
391 			if ( i == Popupdead_default_choice ) {
392 				b->draw_forced(1);
393 			}
394 		}
395 	}
396 }
397 
398 // Draw the button text nicely formatted in the popup
popupdead_draw_button_text()399 void popupdead_draw_button_text()
400 {
401 	int w,h,i,sx,sy;
402 
403 	gr_set_color_fast(&Color_bright_blue);
404 
405 	for ( i=0; i < Popupdead_num_choices; i++ ) {
406 		gr_get_string_size(&w, &h, Popupdead_button_text[i]);
407 		sx = Popupdead_region_coords[gr_screen.res][i][0]-w;
408 		sy = Popupdead_region_coords[gr_screen.res][i][1]+4;
409 		gr_string(sx, sy, Popupdead_button_text[i], GR_RESIZE_MENU);
410 	}
411 }
412 
413 // Called once per frame to run the dead popup
popupdead_do_frame(float)414 int popupdead_do_frame(float  /*frametime*/)
415 {
416 	int k, choice = -1;
417 
418 	if ( !Popupdead_active ) {
419 		return -1;
420 	}
421 
422 	// maybe show skip mission popup
423 	if ((!Popupdead_skip_already_shown) && (Player->show_skip_popup) && (Game_mode & GM_NORMAL) && (Game_mode & GM_CAMPAIGN_MODE) && (Player->failures_this_session >= PLAYER_MISSION_FAILURE_LIMIT)) {
424 		int popup_choice = popup(0, 3, XSTR("Do Not Skip This Mission", 1473),
425 												 XSTR("Advance To The Next Mission", 1474),
426 												 XSTR("Don't Show Me This Again", 1475),
427 												 XSTR("You have failed this mission five times.  If you like, you may advance to the next mission.", 1472) );
428 		switch (popup_choice) {
429 		case 0:
430 			// stay on this mission, so proceed to normal death popup
431 			// in other words, do nothing.
432 			break;
433 		case 1:
434 			// skip this mission
435 			Popupdead_active = 0;
436 			mission_campaign_skip_to_next();
437 			gameseq_post_event(GS_EVENT_START_GAME);
438 			return -1;
439 		case 2:
440 			// don't show this again
441 			Player->show_skip_popup = 0;
442 			break;
443 		}
444 
445 		Popupdead_skip_already_shown = 1;
446 	}
447 
448 	// don't process keys/buttons if another popup is active
449 	if ( !popup_active() ) {
450 		k = Popupdead_window.process();
451 
452 		choice = popupdead_process_keys(k);
453 
454 		if (choice < 0) {
455 			choice = popupdead_check_buttons();
456 		}
457 
458 		if ( choice >= 0 ) {
459 			// do something different for single/multiplayer
460 			if ( Game_mode & GM_NORMAL ) {
461 				Popupdead_choice=choice;
462 			} else {
463 				Assert( Popupdead_multi_type != -1 );
464 				switch ( Popupdead_multi_type ) {
465 
466 				case POPUPDEAD_OBS_ONLY:
467 				case POPUPDEAD_OBS_QUIT:
468 					Popupdead_choice = POPUPDEAD_DO_OBSERVER;
469 					if ( (Popupdead_multi_type == POPUPDEAD_OBS_QUIT) && (choice == 1) )
470 						Popupdead_choice = POPUPDEAD_DO_MAIN_HALL;
471 					break;
472 
473 				case POPUPDEAD_RESPAWN_ONLY:
474 				case POPUPDEAD_RESPAWN_QUIT:
475 					Popupdead_choice = POPUPDEAD_DO_RESPAWN;
476 					if ( (Popupdead_multi_type == POPUPDEAD_RESPAWN_QUIT) && (choice == 1) )
477 						Popupdead_choice = POPUPDEAD_DO_MAIN_HALL;
478 					break;
479 
480 				default:
481 					UNREACHABLE("Invalid or unknown popupdead multi type!");
482 					break;
483 				}
484 			}
485 		}
486 	}
487 
488 	Popupdead_window.draw();
489 	popupdead_force_draw_buttons();
490 	popupdead_draw_button_text();
491 
492 	// maybe force the player to respawn if they've taken too long to choose
493 	if (( Game_mode & GM_MULTIPLAYER ) && (The_mission.max_respawn_delay >= 0) && (timestamp_elapsed(Popupdead_timer)) && (choice < 0)) {
494 		if (( Popupdead_multi_type == POPUPDEAD_RESPAWN_ONLY) || ( Popupdead_multi_type == POPUPDEAD_RESPAWN_QUIT)) {
495 			Popupdead_choice = POPUPDEAD_DO_RESPAWN;
496 		}
497 	}
498 
499 	return Popupdead_choice;
500 }
501 
502 // Close down the dead popup
popupdead_close()503 void popupdead_close()
504 {
505 	if ( !Popupdead_active ) {
506 		return;
507 	}
508 
509 	gamesnd_play_iface(InterfaceSounds::POPUP_DISAPPEAR);
510 	Popupdead_window.destroy();
511 	game_flush();
512 
513 	io::mouse::CursorManager::get()->popStatus();
514 
515 	Popupdead_active = 0;
516 	Popupdead_skip_active = 0;
517 	Popupdead_skip_already_shown = 0;
518 }
519 
520 // Is there a dead popup active?
popupdead_is_active()521 int popupdead_is_active()
522 {
523 	return Popupdead_active;
524 }
525