2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
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
10 * $Logfile: /Freespace2/code/Popup/PopupDead.cpp $
15 * C module for the death popup
18 * Revision 1.4 2003/05/25 02:30:43 taylor
21 * Revision 1.3 2002/06/09 04:41:25 relnev
22 * added copyright header
24 * Revision 1.2 2002/05/07 03:16:51 theoddone33
25 * The Great Newline Fix
27 * Revision 1.1.1.1 2002/05/03 03:28:10 root
31 * 14 9/14/99 4:35a Dave
32 * Argh. Added all kinds of code to handle potential crashes in debriefing
35 * 13 9/14/99 12:51a Jefff
36 * sm3-09 no skip hack.
38 * 12 9/11/99 1:09a Mikek
39 * Removed semi-bogus Int3() in popupdead code which seemed to get tripped
42 * 11 9/10/99 6:49p Jefff
43 * changed skip popup to normal type popup
45 * 10 9/09/99 7:17p Jefff
47 * 9 9/07/99 4:44p Jefff
48 * fixed double counting of mission failures
50 * 8 9/07/99 1:54p Jefff
51 * skip mission cleanup
53 * 7 9/06/99 9:46p Jefff
54 * skip mission support
56 * 6 8/11/99 5:47p Jefff
57 * fixed button bitmap loading
59 * 5 7/24/99 1:54p Dave
60 * Hud text flash gauge. Reworked dead popup to use 4 buttons in red-alert
63 * 4 6/01/99 3:52p Dave
64 * View footage screen. Fixed xstrings to not display the & symbol. Popup,
65 * dead popup, pxo find player popup, pxo private room popup.
67 * 3 10/13/98 9:29a Dave
68 * Started neatening up freespace.h. Many variables renamed and
69 * reorganized. Added AlphaColors.[h,cpp]
71 * 2 10/07/98 10:53a Dave
74 * 1 10/07/98 10:51a Dave
76 * 11 6/09/98 10:31a Hoffoss
77 * Created index numbers for all xstr() references. Any new xstr() stuff
78 * added from here on out should be added to the end if the list. The
79 * current list count can be found in FreeSpace.cpp (search for
82 * 10 5/17/98 1:43a Dave
83 * Eradicated chatbox problems. Remove speed match for observers. Put in
84 * help screens for PXO. Fix messaging and end mission privelges. Fixed
85 * team select screen bugs. Misc UI fixes.
87 * 9 5/01/98 4:24p Lawrance
88 * Change "Main Hall" to "Flight Deck"
90 * 8 4/22/98 4:59p Allender
91 * new multiplayer dead popup. big changes to the comm menu system for
92 * team vs. team. Start of debriefing stuff for team vs. team Make form
93 * on my wing work with individual ships who have high priority orders
95 * 7 3/12/98 4:02p Lawrance
96 * Cleanup how pause works, fix couple pause-related bugs.
98 * 6 3/05/98 12:35p Lawrance
99 * Fix bug where two options could be highlighted at once.
101 * 5 2/22/98 4:30p John
102 * More string externalization classification
104 * 4 2/22/98 2:48p John
105 * More String Externalization Classification
107 * 3 2/22/98 12:19p John
108 * Externalized some strings
110 * 2 2/10/98 11:20p Lawrance
111 * Implement separate dead popup system
113 * 1 2/10/98 6:02p Lawrance
119 #define POPUPDEAD_NUM_CHOICES 3 // normal
121 #define POPUPDEAD_NUM_CHOICES_RA 3 // Theres on 3 possible in FS1
123 #define POPUPDEAD_NUM_CHOICES_RA 4 // red alert
125 #define POPUPDEAD_NUM_CHOICES_SKIP 3 // skip mission menu
128 #define POPUPDEAD_NUM_CHOICES_MAX 3
130 #define POPUPDEAD_NUM_CHOICES_MAX 4
136 #include "freespace.h"
138 #include "keycontrol.h"
141 #include "multiutil.h"
142 #include "popupdead.h"
143 #include "alphacolors.h"
144 #include "gamesequence.h"
147 UI_WINDOW Popupdead_window;
148 UI_BUTTON Popupdead_buttons[POPUPDEAD_NUM_CHOICES_MAX]; // actual lit buttons
149 UI_BUTTON Popupdead_button_regions[POPUPDEAD_NUM_CHOICES_MAX]; // fake buttons used for mouse detection over text
151 int Popupdead_region_coords[GR_NUM_RESOLUTIONS][POPUPDEAD_NUM_CHOICES_MAX][4] =
155 {464, 402, 505, 421}, // upper right pixel of text, lower right pixel of button (for tiny popup)
156 {464, 422, 505, 442},
157 {464, 442, 505, 462},
159 {464, 389, 497, 403}, // upper right pixel of text, lower right pixel of button (for tiny popup)
160 {464, 413, 497, 427},
161 {464, 435, 497, 446},
162 {464, 457, 497, 466},
166 {745, 627, 809, 664},
167 {745, 663, 809, 700}, // upper right pixel of text, lower right pixel of button (for tiny popup)
168 {745, 699, 809, 736},
170 {745, 735, 809, 772},
175 int Popupdead_button_coords[GR_NUM_RESOLUTIONS][POPUPDEAD_NUM_CHOICES_MAX][2] =
179 {474, 394}, // upper left pixel (tiny popup)
183 {478, 387}, // upper left pixel (tiny popup)
190 {760, 620}, // upper left pixel (tiny popup)
199 const char *Popupdead_background_filename[GR_NUM_RESOLUTIONS] = {
200 "PopDeath", // GR_640
201 "2_PopDeath" // GR-1024
204 int Popupdead_background_coords[GR_NUM_RESOLUTIONS][2] =
214 const char *Popupdead_button_filenames[GR_NUM_RESOLUTIONS][POPUPDEAD_NUM_CHOICES_MAX] =
217 "PopD_00", // first choice
218 "PopD_01", // second choice
219 "PopD_02", // third choice
221 "PopD_03", // fourth choice
225 "2_PopD_00", // first choice
226 "2_PopD_01", // second choice
227 "2_PopD_02", // third choice
229 "2_PopD_03", // fourth choice
234 int Popupdead_skip_message_y[GR_NUM_RESOLUTIONS] = {
240 static const char *Popupdead_button_text[POPUPDEAD_NUM_CHOICES_MAX];
242 // multiplayer specifics to help with return values since they can vary
243 #define POPUPDEAD_OBS_ONLY 1
244 #define POPUPDEAD_OBS_QUIT 2
245 #define POPUPDEAD_RESPAWN_ONLY 3
246 #define POPUPDEAD_RESPAWN_QUIT 4
248 int Popupdead_default_choice; // What the default choice is (ie activated when Enter pressed)
249 int Popupdead_active = 0; // A dead popup is active
250 int Popupdead_choice; // Index for choice picked (-1 if none picked)
251 int Popupdead_num_choices; // number of buttons
252 int Popupdead_multi_type; // what kind of popup is active for muliplayer
253 int Popupdead_skip_active = 0; // The skip-misison popup is active
254 int Popupdead_skip_already_shown = 0;
256 // Initialize the dead popup data
257 void popupdead_start()
262 if ( Popupdead_active ) {
267 // increment number of deaths
268 Player->failures_this_session++;
271 // create base window
272 Popupdead_window.create(Popupdead_background_coords[gr_screen.res][0], Popupdead_background_coords[gr_screen.res][1], 1, 1, 0);
273 Popupdead_window.set_foreground_bmap(Popupdead_background_filename[gr_screen.res]);
275 Popupdead_num_choices = 0;
276 Popupdead_multi_type = -1;
278 if ( Game_mode & GM_NORMAL ) {
279 // also do a campaign check here?
280 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)) {
281 // init the special preliminary death popup that gives the skip option
282 Popupdead_button_text[0] = XSTR( "Do Not Skip This Mission", 1473);
283 Popupdead_button_text[1] = XSTR( "Advance To The Next Mission", 1474);
284 Popupdead_button_text[2] = XSTR( "Don't Show Me This Again", 1475);
285 Popupdead_num_choices = POPUPDEAD_NUM_CHOICES_SKIP;
286 Popupdead_skip_active = 1;
287 } else if(The_mission.red_alert) {
288 // We can't staticly declare these because they are externalized
289 Popupdead_button_text[0] = XSTR( "Quick Start Mission", 105);
290 Popupdead_button_text[1] = XSTR( "Return To Flight Deck", 106);
291 Popupdead_button_text[2] = XSTR( "Return To Briefing", 107);
293 Popupdead_button_text[3] = XSTR( "Replay previous mission", 1432);
295 Popupdead_num_choices = POPUPDEAD_NUM_CHOICES_RA;
297 Popupdead_button_text[0] = XSTR( "Quick Start Mission", 105);
298 Popupdead_button_text[1] = XSTR( "Return To Flight Deck", 106);
299 Popupdead_button_text[2] = XSTR( "Return To Briefing", 107);
300 Popupdead_num_choices = POPUPDEAD_NUM_CHOICES;
303 // in multiplayer, we have different choices depending on respawn mode, etc.
305 // if the player has run out of respawns and must either quit and become an observer
306 if(Net_player->flags & NETINFO_FLAG_LIMBO){
308 // the master should not be able to quit the game
309 if( ((Net_player->flags & NETINFO_FLAG_AM_MASTER) && (multi_num_players() > 1)) || (Net_player->flags & NETINFO_FLAG_TEAM_CAPTAIN) ) {
310 Popupdead_button_text[0] = XSTR( "Observer Mode", 108);
311 Popupdead_num_choices = 1;
312 Popupdead_multi_type = POPUPDEAD_OBS_ONLY;
314 Popupdead_button_text[0] = XSTR( "Observer Mode", 108);
315 Popupdead_button_text[1] = XSTR( "Return To Flight Deck", 106);
316 Popupdead_num_choices = 2;
317 Popupdead_multi_type = POPUPDEAD_OBS_QUIT;
320 // the master of the game should not be allowed to quit
321 if ( ((Net_player->flags & NETINFO_FLAG_AM_MASTER) && (multi_num_players() > 1)) || (Net_player->flags & NETINFO_FLAG_TEAM_CAPTAIN) ) {
322 Popupdead_button_text[0] = XSTR( "Respawn", 109);
323 Popupdead_num_choices = 1;
324 Popupdead_multi_type = POPUPDEAD_RESPAWN_ONLY;
326 Popupdead_button_text[0] = XSTR( "Respawn", 109);
327 Popupdead_button_text[1] = XSTR( "Return To Flight Deck", 106);
328 Popupdead_num_choices = 2;
329 Popupdead_multi_type = POPUPDEAD_RESPAWN_QUIT;
335 for (i=0; i < Popupdead_num_choices; i++) {
336 b = &Popupdead_buttons[i];
337 b->create(&Popupdead_window, "", Popupdead_button_coords[gr_screen.res][i][0], Popupdead_button_coords[gr_screen.res][i][1], 30, 20, 0, 1);
338 b->set_bmaps(Popupdead_button_filenames[gr_screen.res][i], 3, 0);
339 b->set_highlight_action(common_play_highlight_sound);
341 // create invisible buttons to detect mouse presses... can't use mask since button region is dynamically sized
343 gr_get_string_size(&w, &h, Popupdead_button_text[i]);
344 lx = Popupdead_region_coords[gr_screen.res][i][0] - w;
345 b = &Popupdead_button_regions[i];
346 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);
350 Popupdead_default_choice = 0;
351 Popupdead_choice = -1;
352 Popupdead_active = 1;
355 // maybe play a sound when key up/down is pressed to switch default choice
356 void popupdead_play_default_change_sound()
361 // only play if mouse not currently highlighting a choice
362 for ( i = 0; i < Popupdead_num_choices; i++ ) {
363 br = &Popupdead_button_regions[i];
364 b = &Popupdead_buttons[i];
365 if ( br->button_down() ) {
370 if ( br->button_hilighted() && !b->button_down() ) {
375 if ( b->button_hilighted() ) {
381 gamesnd_play_iface(SND_USER_SELECT);
385 // do any key processing here
386 // exit: -1 => nothing was done
387 // >=0 => a choice was selected
388 int popupdead_process_keys(int k)
399 return Popupdead_default_choice; // select the current default choice
403 if (Popupdead_skip_active) {
404 return 0; // 0 mimics a "do not skip"
406 return 1; // do nothing here for now - 1 mimics a "return to flight deck"
413 popupdead_play_default_change_sound();
414 Popupdead_default_choice++;
415 if ( Popupdead_default_choice >= Popupdead_num_choices ) {
416 Popupdead_default_choice=0;
422 case KEY_SHIFTED+SDLK_TAB:
423 popupdead_play_default_change_sound();
424 Popupdead_default_choice--;
425 if ( Popupdead_default_choice < 0 ) {
426 Popupdead_default_choice=Popupdead_num_choices-1;
431 game_process_pause_key();
438 // read the dead key set
439 masked_k = k & ~KEY_CTRLED; // take out CTRL modifier only
440 process_set_of_keys(masked_k, Dead_key_set_size, Dead_key_set);
441 button_info_do(&Player->bi); // call functions based on status of button_info bit vectors
447 // see if any popup buttons have been pressed
448 // exit: -1 => no buttons pressed
449 // >=0 => button index that was pressed
450 int popupdead_check_buttons()
455 for ( i = 0; i < Popupdead_num_choices; i++ ) {
456 b = &Popupdead_button_regions[i];
457 if ( b->pressed() ) {
461 b = &Popupdead_buttons[i];
462 if ( b->pressed() ) {
470 // See if any of the button should change appearance based on mouse position
471 void popupdead_force_draw_buttons()
473 int i,mouse_is_highlighting=0;
476 for ( i = 0; i < Popupdead_num_choices; i++ ) {
477 br = &Popupdead_button_regions[i];
478 b = &Popupdead_buttons[i];
479 if ( br->button_down() ) {
481 mouse_is_highlighting=1;
485 if ( (b->button_hilighted()) || (br->button_hilighted() && !b->button_down()) ) {
486 Popupdead_default_choice=i;
487 mouse_is_highlighting=1;
492 // Only if mouse is not highlighting an option, let the default choice be drawn highlighted
493 if ( (!mouse_is_highlighting) && (Popupdead_num_choices>1) ) {
494 for ( i = 0; i < Popupdead_num_choices; i++ ) {
495 b = &Popupdead_buttons[i];
496 // highlight the default choice
497 if ( i == Popupdead_default_choice ) {
504 // Draw the button text nicely formatted in the popup
505 void popupdead_draw_button_text()
509 gr_set_color_fast(&Color_bright_blue);
511 for ( i=0; i < Popupdead_num_choices; i++ ) {
512 gr_get_string_size(&w, &h, Popupdead_button_text[i]);
513 sx = Popupdead_region_coords[gr_screen.res][i][0]-w;
514 sy = Popupdead_region_coords[gr_screen.res][i][1]+4;
515 gr_string(sx, sy, Popupdead_button_text[i]);
519 // reinits the death popup
520 // used for skip popup to death popup transition
521 void popupdead_restart() {
522 Popupdead_skip_already_shown = 1;
523 Popupdead_skip_active = 0;
524 Popupdead_active = 0;
525 Player->failures_this_session--; // hacky correction for a double count when calling popupdead_start() twice
529 // do the "skip mission" version of the death popup
530 int popupdead_skip_do_frame()
532 int k = Popupdead_window.process();
535 int choice = popupdead_process_keys(k);
537 choice = popupdead_check_buttons();
540 // take appropriate options
543 // try this mission again, so proceed to normal death popup
544 gamesnd_play_iface(SND_USER_SELECT);
549 mission_campaign_skip_to_next();
551 gamesnd_play_iface(SND_USER_SELECT);
554 // dont show this again
555 Player->show_skip_popup = 0;
557 gamesnd_play_iface(SND_USER_SELECT);
563 Popupdead_window.draw();
564 popupdead_force_draw_buttons();
565 popupdead_draw_button_text();
567 // render skip mission message
568 gr_set_color_fast(&Color_bright_white);
569 gr_string(0x8000, Popupdead_skip_message_y[gr_screen.res], XSTR("You have failed this mission five times.", 1470));
570 gr_string(0x8000, Popupdead_skip_message_y[gr_screen.res] + gr_get_font_height(), XSTR("If you like, you may advance to the next mission.", 1471));
576 // Called once per frame to run the dead popup
577 int popupdead_do_frame(float frametime)
582 if (Popupdead_skip_active) {
583 // do the skip mission popup, and thats all
584 if (popupdead_skip_do_frame() == 1) {
585 return 2; // fake the "go to briefing" choice
587 return -1; // fake no choice
591 if ( !Popupdead_active ) {
595 // dont let dude skip 3-09. hack.
596 if(Game_mode & GM_CAMPAIGN_MODE){
597 if((Campaign.current_mission >= 0) && (Campaign.current_mission < MAX_CAMPAIGN_MISSIONS)){
598 if ((Campaign.missions[Campaign.current_mission].name != NULL) && !SDL_strcasecmp(Campaign.missions[Campaign.current_mission].name, "sm3-09.fs2")) {
599 Popupdead_skip_already_shown = 1;
604 // maybe show skip mission popup
605 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)) {
606 int popup_choice = popup(0, 3, XSTR("Do Not Skip This Mission", 1473),
607 XSTR("Advance To The Next Mission", 1474),
608 XSTR("Don't Show Me This Again", 1475),
609 XSTR("You have failed this mission five times. If you like, you may advance to the next mission.", 1472) );
610 switch (popup_choice) {
612 // stay on this mission, so proceed to normal death popup
613 // in other words, do nothing.
617 Popupdead_active = 0;
618 mission_campaign_skip_to_next();
619 gameseq_post_event(GS_EVENT_START_GAME);
622 // dont show this again
623 Player->show_skip_popup = 0;
627 Popupdead_skip_already_shown = 1;
631 k = Popupdead_window.process();
633 choice = popupdead_process_keys(k);
635 // do something different for single/multiplayer
636 if ( Game_mode & GM_NORMAL ) {
637 Popupdead_choice=choice;
639 SDL_assert( Popupdead_multi_type != -1 );
640 switch ( Popupdead_multi_type ) {
642 case POPUPDEAD_OBS_ONLY:
643 case POPUPDEAD_OBS_QUIT:
644 Popupdead_choice = POPUPDEAD_DO_OBSERVER;
645 if ( (Popupdead_multi_type == POPUPDEAD_OBS_QUIT) && (choice == 1) )
646 Popupdead_choice = POPUPDEAD_DO_MAIN_HALL;
649 case POPUPDEAD_RESPAWN_ONLY:
650 case POPUPDEAD_RESPAWN_QUIT:
651 Popupdead_choice = POPUPDEAD_DO_RESPAWN;
652 if ( (Popupdead_multi_type == POPUPDEAD_RESPAWN_QUIT) && (choice == 1) )
653 Popupdead_choice = POPUPDEAD_DO_MAIN_HALL;
663 choice = popupdead_check_buttons();
665 // do something different for single/multiplayer
666 if ( Game_mode & GM_NORMAL ) {
667 Popupdead_choice=choice;
669 SDL_assert( Popupdead_multi_type != -1 );
670 switch ( Popupdead_multi_type ) {
672 case POPUPDEAD_OBS_ONLY:
673 case POPUPDEAD_OBS_QUIT:
674 Popupdead_choice = POPUPDEAD_DO_OBSERVER;
675 if ( (Popupdead_multi_type == POPUPDEAD_OBS_QUIT) && (choice == 1) )
676 Popupdead_choice = POPUPDEAD_DO_MAIN_HALL;
679 case POPUPDEAD_RESPAWN_ONLY:
680 case POPUPDEAD_RESPAWN_QUIT:
681 Popupdead_choice = POPUPDEAD_DO_RESPAWN;
682 if ( (Popupdead_multi_type == POPUPDEAD_RESPAWN_QUIT) && (choice == 1) )
683 Popupdead_choice = POPUPDEAD_DO_MAIN_HALL;
693 Popupdead_window.draw();
694 popupdead_force_draw_buttons();
695 popupdead_draw_button_text();
697 return Popupdead_choice;
700 // Close down the dead popup
701 void popupdead_close()
703 if ( !Popupdead_active ) {
707 gamesnd_play_iface(SND_POPUP_DISAPPEAR);
708 Popupdead_window.destroy();
711 Popupdead_active = 0;
712 Popupdead_skip_active = 0;
713 Popupdead_skip_already_shown = 0;
716 // Is there a dead popup active?
717 int popupdead_is_active()
719 return Popupdead_active;