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.3 2002/06/09 04:41:25 relnev
19 * added copyright header
21 * Revision 1.2 2002/05/07 03:16:51 theoddone33
22 * The Great Newline Fix
24 * Revision 1.1.1.1 2002/05/03 03:28:10 root
28 * 14 9/14/99 4:35a Dave
29 * Argh. Added all kinds of code to handle potential crashes in debriefing
32 * 13 9/14/99 12:51a Jefff
33 * sm3-09 no skip hack.
35 * 12 9/11/99 1:09a Mikek
36 * Removed semi-bogus Int3() in popupdead code which seemed to get tripped
39 * 11 9/10/99 6:49p Jefff
40 * changed skip popup to normal type popup
42 * 10 9/09/99 7:17p Jefff
44 * 9 9/07/99 4:44p Jefff
45 * fixed double counting of mission failures
47 * 8 9/07/99 1:54p Jefff
48 * skip mission cleanup
50 * 7 9/06/99 9:46p Jefff
51 * skip mission support
53 * 6 8/11/99 5:47p Jefff
54 * fixed button bitmap loading
56 * 5 7/24/99 1:54p Dave
57 * Hud text flash gauge. Reworked dead popup to use 4 buttons in red-alert
60 * 4 6/01/99 3:52p Dave
61 * View footage screen. Fixed xstrings to not display the & symbol. Popup,
62 * dead popup, pxo find player popup, pxo private room popup.
64 * 3 10/13/98 9:29a Dave
65 * Started neatening up freespace.h. Many variables renamed and
66 * reorganized. Added AlphaColors.[h,cpp]
68 * 2 10/07/98 10:53a Dave
71 * 1 10/07/98 10:51a Dave
73 * 11 6/09/98 10:31a Hoffoss
74 * Created index numbers for all xstr() references. Any new xstr() stuff
75 * added from here on out should be added to the end if the list. The
76 * current list count can be found in FreeSpace.cpp (search for
79 * 10 5/17/98 1:43a Dave
80 * Eradicated chatbox problems. Remove speed match for observers. Put in
81 * help screens for PXO. Fix messaging and end mission privelges. Fixed
82 * team select screen bugs. Misc UI fixes.
84 * 9 5/01/98 4:24p Lawrance
85 * Change "Main Hall" to "Flight Deck"
87 * 8 4/22/98 4:59p Allender
88 * new multiplayer dead popup. big changes to the comm menu system for
89 * team vs. team. Start of debriefing stuff for team vs. team Make form
90 * on my wing work with individual ships who have high priority orders
92 * 7 3/12/98 4:02p Lawrance
93 * Cleanup how pause works, fix couple pause-related bugs.
95 * 6 3/05/98 12:35p Lawrance
96 * Fix bug where two options could be highlighted at once.
98 * 5 2/22/98 4:30p John
99 * More string externalization classification
101 * 4 2/22/98 2:48p John
102 * More String Externalization Classification
104 * 3 2/22/98 12:19p John
105 * Externalized some strings
107 * 2 2/10/98 11:20p Lawrance
108 * Implement separate dead popup system
110 * 1 2/10/98 6:02p Lawrance
116 #define POPUPDEAD_NUM_CHOICES 3 // normal
117 #define POPUPDEAD_NUM_CHOICES_RA 4 // red alert
118 #define POPUPDEAD_NUM_CHOICES_SKIP 3 // skip mission menu
120 #define POPUPDEAD_NUM_CHOICES_MAX 4
125 #include "freespace.h"
127 #include "keycontrol.h"
130 #include "multiutil.h"
131 #include "popupdead.h"
132 #include "alphacolors.h"
133 #include "gamesequence.h"
136 UI_WINDOW Popupdead_window;
137 UI_BUTTON Popupdead_buttons[POPUPDEAD_NUM_CHOICES_MAX]; // actual lit buttons
138 UI_BUTTON Popupdead_button_regions[POPUPDEAD_NUM_CHOICES_MAX]; // fake buttons used for mouse detection over text
140 int Popupdead_region_coords[GR_NUM_RESOLUTIONS][POPUPDEAD_NUM_CHOICES_MAX][4] =
143 {464, 389, 497, 403}, // upper right pixel of text, lower right pixel of button (for tiny popup)
144 {464, 413, 497, 427},
145 {464, 435, 497, 446},
146 {464, 457, 497, 466},
149 {745, 627, 809, 664},
150 {745, 663, 809, 700}, // upper right pixel of text, lower right pixel of button (for tiny popup)
151 {745, 699, 809, 736},
152 {745, 735, 809, 772},
156 int Popupdead_button_coords[GR_NUM_RESOLUTIONS][POPUPDEAD_NUM_CHOICES_MAX][2] =
159 {478, 387}, // upper left pixel (tiny popup)
165 {760, 620}, // upper left pixel (tiny popup)
172 char *Popupdead_background_filename[GR_NUM_RESOLUTIONS] = {
173 "PopDeath", // GR_640
174 "2_PopDeath" // GR-1024
177 int Popupdead_background_coords[GR_NUM_RESOLUTIONS][2] =
187 char *Popupdead_button_filenames[GR_NUM_RESOLUTIONS][POPUPDEAD_NUM_CHOICES_MAX] =
190 "PopD_00", // first choice
191 "PopD_01", // second choice
192 "PopD_02", // third choice
193 "PopD_03", // fourth choice
196 "2_PopD_00", // first choice
197 "2_PopD_01", // second choice
198 "2_PopD_02", // third choice
199 "2_PopD_03", // fourth choice
203 int Popupdead_skip_message_y[GR_NUM_RESOLUTIONS] = {
209 static char *Popupdead_button_text[POPUPDEAD_NUM_CHOICES_MAX];
211 // multiplayer specifics to help with return values since they can vary
212 #define POPUPDEAD_OBS_ONLY 1
213 #define POPUPDEAD_OBS_QUIT 2
214 #define POPUPDEAD_RESPAWN_ONLY 3
215 #define POPUPDEAD_RESPAWN_QUIT 4
217 int Popupdead_default_choice; // What the default choice is (ie activated when Enter pressed)
218 int Popupdead_active = 0; // A dead popup is active
219 int Popupdead_choice; // Index for choice picked (-1 if none picked)
220 int Popupdead_num_choices; // number of buttons
221 int Popupdead_multi_type; // what kind of popup is active for muliplayer
222 int Popupdead_skip_active = 0; // The skip-misison popup is active
223 int Popupdead_skip_already_shown = 0;
225 // Initialize the dead popup data
226 void popupdead_start()
231 if ( Popupdead_active ) {
236 // increment number of deaths
237 Player->failures_this_session++;
240 // create base window
241 Popupdead_window.create(Popupdead_background_coords[gr_screen.res][0], Popupdead_background_coords[gr_screen.res][1], 1, 1, 0);
242 Popupdead_window.set_foreground_bmap(Popupdead_background_filename[gr_screen.res]);
244 Popupdead_num_choices = 0;
245 Popupdead_multi_type = -1;
247 if ( Game_mode & GM_NORMAL ) {
248 // also do a campaign check here?
249 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)) {
250 // init the special preliminary death popup that gives the skip option
251 Popupdead_button_text[0] = XSTR( "Do Not Skip This Mission", 1473);
252 Popupdead_button_text[1] = XSTR( "Advance To The Next Mission", 1474);
253 Popupdead_button_text[2] = XSTR( "Don't Show Me This Again", 1475);
254 Popupdead_num_choices = POPUPDEAD_NUM_CHOICES_SKIP;
255 Popupdead_skip_active = 1;
256 } else if(The_mission.red_alert) {
257 // We can't staticly declare these because they are externalized
258 Popupdead_button_text[0] = XSTR( "Quick Start Mission", 105);
259 Popupdead_button_text[1] = XSTR( "Return To Flight Deck", 106);
260 Popupdead_button_text[2] = XSTR( "Return To Briefing", 107);
261 Popupdead_button_text[3] = XSTR( "Replay previous mission", 1432);
262 Popupdead_num_choices = POPUPDEAD_NUM_CHOICES_RA;
264 Popupdead_button_text[0] = XSTR( "Quick Start Mission", 105);
265 Popupdead_button_text[1] = XSTR( "Return To Flight Deck", 106);
266 Popupdead_button_text[2] = XSTR( "Return To Briefing", 107);
267 Popupdead_num_choices = POPUPDEAD_NUM_CHOICES;
270 // in multiplayer, we have different choices depending on respawn mode, etc.
272 // if the player has run out of respawns and must either quit and become an observer
273 if(Net_player->flags & NETINFO_FLAG_LIMBO){
275 // the master should not be able to quit the game
276 if( ((Net_player->flags & NETINFO_FLAG_AM_MASTER) && (multi_num_players() > 1)) || (Net_player->flags & NETINFO_FLAG_TEAM_CAPTAIN) ) {
277 Popupdead_button_text[0] = XSTR( "Observer Mode", 108);
278 Popupdead_num_choices = 1;
279 Popupdead_multi_type = POPUPDEAD_OBS_ONLY;
281 Popupdead_button_text[0] = XSTR( "Observer Mode", 108);
282 Popupdead_button_text[1] = XSTR( "Return To Flight Deck", 106);
283 Popupdead_num_choices = 2;
284 Popupdead_multi_type = POPUPDEAD_OBS_QUIT;
287 // the master of the game should not be allowed to quit
288 if ( ((Net_player->flags & NETINFO_FLAG_AM_MASTER) && (multi_num_players() > 1)) || (Net_player->flags & NETINFO_FLAG_TEAM_CAPTAIN) ) {
289 Popupdead_button_text[0] = XSTR( "Respawn", 109);
290 Popupdead_num_choices = 1;
291 Popupdead_multi_type = POPUPDEAD_RESPAWN_ONLY;
293 Popupdead_button_text[0] = XSTR( "Respawn", 109);
294 Popupdead_button_text[1] = XSTR( "Return To Flight Deck", 106);
295 Popupdead_num_choices = 2;
296 Popupdead_multi_type = POPUPDEAD_RESPAWN_QUIT;
302 for (i=0; i < Popupdead_num_choices; i++) {
303 b = &Popupdead_buttons[i];
304 b->create(&Popupdead_window, "", Popupdead_button_coords[gr_screen.res][i][0], Popupdead_button_coords[gr_screen.res][i][1], 30, 20, 0, 1);
305 b->set_bmaps(Popupdead_button_filenames[gr_screen.res][i], 3, 0);
306 b->set_highlight_action(common_play_highlight_sound);
308 // create invisible buttons to detect mouse presses... can't use mask since button region is dynamically sized
310 gr_get_string_size(&w, &h, Popupdead_button_text[i]);
311 lx = Popupdead_region_coords[gr_screen.res][i][0] - w;
312 b = &Popupdead_button_regions[i];
313 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);
317 Popupdead_default_choice = 0;
318 Popupdead_choice = -1;
319 Popupdead_active = 1;
322 // maybe play a sound when key up/down is pressed to switch default choice
323 void popupdead_play_default_change_sound()
328 // only play if mouse not currently highlighting a choice
329 for ( i = 0; i < Popupdead_num_choices; i++ ) {
330 br = &Popupdead_button_regions[i];
331 b = &Popupdead_buttons[i];
332 if ( br->button_down() ) {
337 if ( br->button_hilighted() && !b->button_down() ) {
342 if ( b->button_hilighted() ) {
348 gamesnd_play_iface(SND_USER_SELECT);
352 // do any key processing here
353 // exit: -1 => nothing was done
354 // >=0 => a choice was selected
355 int popupdead_process_keys(int k)
366 return Popupdead_default_choice; // select the current default choice
370 if (Popupdead_skip_active) {
371 return 0; // 0 mimics a "do not skip"
373 return 1; // do nothing here for now - 1 mimics a "return to flight deck"
380 popupdead_play_default_change_sound();
381 Popupdead_default_choice++;
382 if ( Popupdead_default_choice >= Popupdead_num_choices ) {
383 Popupdead_default_choice=0;
389 case KEY_SHIFTED+KEY_TAB:
390 popupdead_play_default_change_sound();
391 Popupdead_default_choice--;
392 if ( Popupdead_default_choice < 0 ) {
393 Popupdead_default_choice=Popupdead_num_choices-1;
398 game_process_pause_key();
405 // read the dead key set
406 masked_k = k & ~KEY_CTRLED; // take out CTRL modifier only
407 process_set_of_keys(masked_k, Dead_key_set_size, Dead_key_set);
408 button_info_do(&Player->bi); // call functions based on status of button_info bit vectors
414 // see if any popup buttons have been pressed
415 // exit: -1 => no buttons pressed
416 // >=0 => button index that was pressed
417 int popupdead_check_buttons()
422 for ( i = 0; i < Popupdead_num_choices; i++ ) {
423 b = &Popupdead_button_regions[i];
424 if ( b->pressed() ) {
428 b = &Popupdead_buttons[i];
429 if ( b->pressed() ) {
437 // See if any of the button should change appearance based on mouse position
438 void popupdead_force_draw_buttons()
440 int i,mouse_is_highlighting=0;
443 for ( i = 0; i < Popupdead_num_choices; i++ ) {
444 br = &Popupdead_button_regions[i];
445 b = &Popupdead_buttons[i];
446 if ( br->button_down() ) {
448 mouse_is_highlighting=1;
452 if ( (b->button_hilighted()) || (br->button_hilighted() && !b->button_down()) ) {
453 Popupdead_default_choice=i;
454 mouse_is_highlighting=1;
459 // Only if mouse is not highlighting an option, let the default choice be drawn highlighted
460 if ( (!mouse_is_highlighting) && (Popupdead_num_choices>1) ) {
461 for ( i = 0; i < Popupdead_num_choices; i++ ) {
462 b = &Popupdead_buttons[i];
463 // highlight the default choice
464 if ( i == Popupdead_default_choice ) {
471 // Draw the button text nicely formatted in the popup
472 void popupdead_draw_button_text()
476 gr_set_color_fast(&Color_bright_blue);
478 for ( i=0; i < Popupdead_num_choices; i++ ) {
479 gr_get_string_size(&w, &h, Popupdead_button_text[i]);
480 sx = Popupdead_region_coords[gr_screen.res][i][0]-w;
481 sy = Popupdead_region_coords[gr_screen.res][i][1]+4;
482 gr_string(sx, sy, Popupdead_button_text[i]);
486 // reinits the death popup
487 // used for skip popup to death popup transition
488 void popupdead_restart() {
489 Popupdead_skip_already_shown = 1;
490 Popupdead_skip_active = 0;
491 Popupdead_active = 0;
492 Player->failures_this_session--; // hacky correction for a double count when calling popupdead_start() twice
496 // do the "skip mission" version of the death popup
497 int popupdead_skip_do_frame()
499 int k = Popupdead_window.process();
502 int choice = popupdead_process_keys(k);
504 choice = popupdead_check_buttons();
507 // take appropriate options
510 // try this mission again, so proceed to normal death popup
511 gamesnd_play_iface(SND_USER_SELECT);
516 mission_campaign_skip_to_next();
518 gamesnd_play_iface(SND_USER_SELECT);
521 // dont show this again
522 Player->show_skip_popup = 0;
524 gamesnd_play_iface(SND_USER_SELECT);
530 Popupdead_window.draw();
531 popupdead_force_draw_buttons();
532 popupdead_draw_button_text();
534 // render skip mission message
535 gr_set_color_fast(&Color_bright_white);
536 gr_string(0x8000, Popupdead_skip_message_y[gr_screen.res], XSTR("You have failed this mission five times.", 1470));
537 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));
543 // Called once per frame to run the dead popup
544 int popupdead_do_frame(float frametime)
549 if (Popupdead_skip_active) {
550 // do the skip mission popup, and thats all
551 if (popupdead_skip_do_frame() == 1) {
552 return 2; // fake the "go to briefing" choice
554 return -1; // fake no choice
558 if ( !Popupdead_active ) {
562 // dont let dude skip 3-09. hack.
563 if(Game_mode & GM_CAMPAIGN_MODE){
564 if((Campaign.current_mission >= 0) && (Campaign.current_mission < MAX_CAMPAIGN_MISSIONS)){
565 if ((Campaign.missions[Campaign.current_mission].name != NULL) && !stricmp(Campaign.missions[Campaign.current_mission].name, "sm3-09.fs2")) {
566 Popupdead_skip_already_shown = 1;
571 // maybe show skip mission popup
572 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)) {
573 int popup_choice = popup(0, 3, XSTR("Do Not Skip This Mission", 1473),
574 XSTR("Advance To The Next Mission", 1474),
575 XSTR("Don't Show Me This Again", 1475),
576 XSTR("You have failed this mission five times. If you like, you may advance to the next mission.", 1472) );
577 switch (popup_choice) {
579 // stay on this mission, so proceed to normal death popup
580 // in other words, do nothing.
584 Popupdead_active = 0;
585 mission_campaign_skip_to_next();
586 gameseq_post_event(GS_EVENT_START_GAME);
589 // dont show this again
590 Player->show_skip_popup = 0;
594 Popupdead_skip_already_shown = 1;
598 k = Popupdead_window.process();
600 choice = popupdead_process_keys(k);
602 // do something different for single/multiplayer
603 if ( Game_mode & GM_NORMAL ) {
604 Popupdead_choice=choice;
606 Assert( Popupdead_multi_type != -1 );
607 switch ( Popupdead_multi_type ) {
609 case POPUPDEAD_OBS_ONLY:
610 case POPUPDEAD_OBS_QUIT:
611 Popupdead_choice = POPUPDEAD_DO_OBSERVER;
612 if ( (Popupdead_multi_type == POPUPDEAD_OBS_QUIT) && (choice == 1) )
613 Popupdead_choice = POPUPDEAD_DO_MAIN_HALL;
616 case POPUPDEAD_RESPAWN_ONLY:
617 case POPUPDEAD_RESPAWN_QUIT:
618 Popupdead_choice = POPUPDEAD_DO_RESPAWN;
619 if ( (Popupdead_multi_type == POPUPDEAD_RESPAWN_QUIT) && (choice == 1) )
620 Popupdead_choice = POPUPDEAD_DO_MAIN_HALL;
630 choice = popupdead_check_buttons();
632 // do something different for single/multiplayer
633 if ( Game_mode & GM_NORMAL ) {
634 Popupdead_choice=choice;
636 Assert( Popupdead_multi_type != -1 );
637 switch ( Popupdead_multi_type ) {
639 case POPUPDEAD_OBS_ONLY:
640 case POPUPDEAD_OBS_QUIT:
641 Popupdead_choice = POPUPDEAD_DO_OBSERVER;
642 if ( (Popupdead_multi_type == POPUPDEAD_OBS_QUIT) && (choice == 1) )
643 Popupdead_choice = POPUPDEAD_DO_MAIN_HALL;
646 case POPUPDEAD_RESPAWN_ONLY:
647 case POPUPDEAD_RESPAWN_QUIT:
648 Popupdead_choice = POPUPDEAD_DO_RESPAWN;
649 if ( (Popupdead_multi_type == POPUPDEAD_RESPAWN_QUIT) && (choice == 1) )
650 Popupdead_choice = POPUPDEAD_DO_MAIN_HALL;
660 Popupdead_window.draw();
661 popupdead_force_draw_buttons();
662 popupdead_draw_button_text();
664 return Popupdead_choice;
667 // Close down the dead popup
668 void popupdead_close()
670 if ( !Popupdead_active ) {
674 gamesnd_play_iface(SND_POPUP_DISAPPEAR);
675 Popupdead_window.destroy();
678 Popupdead_active = 0;
679 Popupdead_skip_active = 0;
680 Popupdead_skip_already_shown = 0;
683 // Is there a dead popup active?
684 int popupdead_is_active()
686 return Popupdead_active;