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 static void skip_mission_callback(int choice)
581 // stay on this mission, so proceed to normal death popup
582 // in other words, do nothing.
583 } else if (choice == 1) {
585 Popupdead_active = 0;
586 mission_campaign_skip_to_next();
587 gameseq_post_event(GS_EVENT_START_GAME);
588 } else if (choice == 2) {
589 // don't show this popup again
590 Player->show_skip_popup = 0;
594 // Called once per frame to run the dead popup
595 int popupdead_do_frame(float frametime)
600 if (Popupdead_skip_active) {
601 // do the skip mission popup, and thats all
602 if (popupdead_skip_do_frame() == 1) {
603 return 2; // fake the "go to briefing" choice
605 return -1; // fake no choice
609 if ( !Popupdead_active ) {
613 // dont let dude skip 3-09. hack.
614 if(Game_mode & GM_CAMPAIGN_MODE){
615 if((Campaign.current_mission >= 0) && (Campaign.current_mission < MAX_CAMPAIGN_MISSIONS)){
616 if ((Campaign.missions[Campaign.current_mission].name != NULL) && !SDL_strcasecmp(Campaign.missions[Campaign.current_mission].name, "sm3-09.fs2")) {
617 Popupdead_skip_already_shown = 1;
622 // maybe show skip mission popup
623 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)) {
624 popup_callback(skip_mission_callback, 0, 3, XSTR("Do Not Skip This Mission", 1473),
625 XSTR("Advance To The Next Mission", 1474),
626 XSTR("Don't Show Me This Again", 1475),
627 XSTR("You have failed this mission five times. If you like, you may advance to the next mission.", 1472) );
629 Popupdead_skip_already_shown = 1;
633 k = Popupdead_window.process();
635 choice = popupdead_process_keys(k);
637 // do something different for single/multiplayer
638 if ( Game_mode & GM_NORMAL ) {
639 Popupdead_choice=choice;
641 SDL_assert( Popupdead_multi_type != -1 );
642 switch ( Popupdead_multi_type ) {
644 case POPUPDEAD_OBS_ONLY:
645 case POPUPDEAD_OBS_QUIT:
646 Popupdead_choice = POPUPDEAD_DO_OBSERVER;
647 if ( (Popupdead_multi_type == POPUPDEAD_OBS_QUIT) && (choice == 1) )
648 Popupdead_choice = POPUPDEAD_DO_MAIN_HALL;
651 case POPUPDEAD_RESPAWN_ONLY:
652 case POPUPDEAD_RESPAWN_QUIT:
653 Popupdead_choice = POPUPDEAD_DO_RESPAWN;
654 if ( (Popupdead_multi_type == POPUPDEAD_RESPAWN_QUIT) && (choice == 1) )
655 Popupdead_choice = POPUPDEAD_DO_MAIN_HALL;
665 choice = popupdead_check_buttons();
667 // do something different for single/multiplayer
668 if ( Game_mode & GM_NORMAL ) {
669 Popupdead_choice=choice;
671 SDL_assert( Popupdead_multi_type != -1 );
672 switch ( Popupdead_multi_type ) {
674 case POPUPDEAD_OBS_ONLY:
675 case POPUPDEAD_OBS_QUIT:
676 Popupdead_choice = POPUPDEAD_DO_OBSERVER;
677 if ( (Popupdead_multi_type == POPUPDEAD_OBS_QUIT) && (choice == 1) )
678 Popupdead_choice = POPUPDEAD_DO_MAIN_HALL;
681 case POPUPDEAD_RESPAWN_ONLY:
682 case POPUPDEAD_RESPAWN_QUIT:
683 Popupdead_choice = POPUPDEAD_DO_RESPAWN;
684 if ( (Popupdead_multi_type == POPUPDEAD_RESPAWN_QUIT) && (choice == 1) )
685 Popupdead_choice = POPUPDEAD_DO_MAIN_HALL;
695 Popupdead_window.draw();
696 popupdead_force_draw_buttons();
697 popupdead_draw_button_text();
699 return Popupdead_choice;
702 // Close down the dead popup
703 void popupdead_close()
705 if ( !Popupdead_active ) {
709 gamesnd_play_iface(SND_POPUP_DISAPPEAR);
710 Popupdead_window.destroy();
713 Popupdead_active = 0;
714 Popupdead_skip_active = 0;
715 Popupdead_skip_already_shown = 0;
718 // Is there a dead popup active?
719 int popupdead_is_active()
721 return Popupdead_active;