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/MenuUI/PlayerMenu.cpp $
15 * Code to drive the Player Select initial screen
18 * Revision 1.9 2005/03/29 02:18:47 taylor
19 * Various 64-bit platform fixes
20 * Fix compiler errors with MAKE_FS1 and fix gr_set_bitmap() too
21 * Make sure that turrets can fire at asteroids for FS1 (needed for a couple missions)
22 * Streaming audio support (big thanks to Pierre Willenbrock!!)
23 * Removed dependance on strings.tbl for FS1 since we don't actually need it now
25 * Revision 1.8 2004/09/20 01:31:44 theoddone33
28 * Revision 1.7 2003/08/09 03:18:03 taylor
29 * fix tips popup not having any tips
31 * Revision 1.6 2003/06/11 18:30:32 taylor
34 * Revision 1.5 2003/05/25 02:30:42 taylor
37 * Revision 1.4 2003/05/22 16:13:35 taylor
38 * fix missed German build option for auto-lang
40 * Revision 1.3 2002/06/09 04:41:22 relnev
41 * added copyright header
43 * Revision 1.2 2002/05/07 03:16:46 theoddone33
44 * The Great Newline Fix
46 * Revision 1.1.1.1 2002/05/03 03:28:09 root
50 * 43 11/02/99 11:42a Jefff
51 * fixed copyright symbol in german fonts
53 * 42 10/27/99 12:27a Jefff
54 * localized tips correctly
56 * 41 9/13/99 4:52p Dave
59 * 40 9/02/99 11:10a Jefff
60 * fixed 1024 list display bug - was only showing 8 pilots at a time
62 * 39 8/26/99 8:51p Dave
63 * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
65 * 38 8/26/99 9:45a Dave
66 * First pass at easter eggs and cheats.
68 * 37 8/16/99 6:39p Jefff
70 * 36 8/16/99 6:37p Jefff
71 * minor string alterations
73 * 35 8/05/99 4:17p Dave
74 * Tweaks to client interpolation.
76 * 34 8/05/99 11:29a Mikek
77 * Jacked up number of comments from 20 to 40, thereby doubling the
78 * quality of our game.
80 * 33 8/04/99 10:53a Dave
81 * Added title to the user tips popup.
83 * 32 8/03/99 3:21p Jefff
85 * 31 8/03/99 10:32a Jefff
86 * raised location of bottom_text to not interfere w/ logo. changed
87 * "please enter callsign" to "type callsign and press enter"
89 * 30 8/02/99 9:13p Dave
92 * 29 7/30/99 10:29a Jefff
93 * fixed colors of bottom display texts
95 * 28 7/27/99 7:17p Jefff
96 * Replaced some art text with XSTR() text.
98 * 27 7/19/99 2:06p Jasons
99 * Remove all palette stuff from player select menu.
101 * 26 7/15/99 9:20a Andsager
102 * FS2_DEMO initial checkin
104 * 25 7/09/99 9:51a Dave
105 * Added thick polyline code.
107 * 24 6/11/99 11:13a Dave
108 * last minute changes before press tour build.
110 * 23 5/21/99 6:45p Dave
111 * Sped up ui loading a bit. Sped up localization disk access stuff. Multi
112 * start game screen, multi password, and multi pxo-help screen.
114 * 22 4/25/99 3:02p Dave
115 * Build defines for the E3 build.
117 * 21 3/25/99 2:31p Neilk
118 * Coordinate changes to handle new artwork
120 * 20 2/25/99 4:19p Dave
121 * Added multiplayer_beta defines. Added cd_check define. Fixed a few
122 * release build warnings. Added more data to the squad war request and
125 * 19 2/01/99 5:55p Dave
126 * Removed the idea of explicit bitmaps for buttons. Fixed text
127 * highlighting for disabled gadgets.
129 * 18 1/30/99 5:08p Dave
130 * More new hi-res stuff.Support for nice D3D textures.
132 * 17 1/30/99 1:53a Dave
133 * Fix some harcoded coords.
135 * 16 1/30/99 1:28a Dave
136 * 1024x768 full support.
138 * 15 1/29/99 1:25p Dave
139 * New code for choose pilot screen.
141 * 14 1/29/99 12:47a Dave
142 * Put in sounds for beam weapon. A bunch of interface screens (tech
145 * 13 1/12/99 12:53a Dave
146 * More work on beam weapons - made collision detection very efficient -
147 * collide against all object types properly - made 3 movement types
148 * smooth. Put in test code to check for possible non-darkening pixels on
151 * 12 12/18/98 1:13a Dave
152 * Rough 1024x768 support for Direct3D. Proper detection and usage through
155 * 11 12/06/98 2:36p Dave
156 * Drastically improved nebula fogging.
158 * 10 12/01/98 6:20p Dave
159 * Removed tga test bitmap code.
161 * 9 12/01/98 4:46p Dave
162 * Put in targa bitmap support (16 bit).
164 * 8 11/30/98 1:07p Dave
165 * 16 bit conversion, first run.
167 * 7 11/20/98 11:16a Dave
168 * Fixed up IPX support a bit. Making sure that switching modes and
169 * loading/saving pilot files maintains proper state.
171 * 6 11/19/98 4:19p Dave
172 * Put IPX sockets back in psnet. Consolidated all multiplayer config
175 * 5 11/05/98 4:18p Dave
176 * First run nebula support. Beefed up localization a bit. Removed all
177 * conditional compiles for foreign versions. Modified mission file
180 * 4 10/13/98 9:28a Dave
181 * Started neatening up freespace.h. Many variables renamed and
182 * reorganized. Added AlphaColors.[h,cpp]
184 * 3 10/09/98 2:57p Dave
185 * Starting splitting up OS stuff.
187 * 2 10/07/98 10:53a Dave
190 * 1 10/07/98 10:49a Dave
199 #include "playermenu.h"
206 #include "managepilot.h"
207 #include "missionscreencommon.h"
209 #include "freespace.h"
211 #include "gamesequence.h"
214 #include "osregistry.h"
216 #include "mainhallmenu.h"
220 #include "alphacolors.h"
221 #include "localize.h"
223 // --------------------------------------------------------------------------------------------------------
225 #if defined(FS1_DEMO)
226 static int Demo_title_active = 0;
227 static int Demo_title_bitmap = -1;
228 static int Demo_title_expire_timestamp = 0;
229 static int Demo_title_need_fade_in = 1;
230 static const char *Demo_title_bitmap_filename = NOX("DemoTitle1");
233 // --------------------------------------------------------------------------------------------------------
234 // PLAYER SELECT defines
237 //#define MAX_PLAYER_SELECT_LINES 8 // max # of pilots displayed at once
238 int Player_select_max_lines[GR_NUM_RESOLUTIONS] = { // max # of pilots displayed at once
243 // button control defines
244 #define NUM_PLAYER_SELECT_BUTTONS 8 // button control defines
246 #define CREATE_PILOT_BUTTON 0 //
247 #define CLONE_BUTTON 1 //
248 #define DELETE_BUTTON 2 //
249 #define SCROLL_LIST_UP_BUTTON 3 //
250 #define SCROLL_LIST_DOWN_BUTTON 4 //
251 #define ACCEPT_BUTTON 5 //
252 #define SINGLE_BUTTON 6 //
253 #define MULTI_BUTTON 7 //
255 // list text display area
256 int Choose_list_coords[GR_NUM_RESOLUTIONS][4] = {
269 const char *Player_select_background_bitmap_name[GR_NUM_RESOLUTIONS] = {
273 const char *Player_select_background_mask_bitmap[GR_NUM_RESOLUTIONS] = {
277 // #define PLAYER_SELECT_PALETTE NOX("ChoosePilotPalette") // palette for the screen
279 #define PLAYER_SELECT_MAIN_HALL_OVERLAY NOX("MainHall1") // main hall help overlay
281 // convenient struct for handling all button controls
282 struct barracks_buttons {
283 const char *filename;
286 UI_BUTTON button; // because we have a class inside this struct, we need the constructor below..
288 barracks_buttons(const char *name, int x1, int y1, int xt1, int yt1, int h) : filename(name), x(x1), y(y1), xt(xt1), yt(yt1), hotspot(h) {}
291 static barracks_buttons Player_select_buttons[GR_NUM_RESOLUTIONS][NUM_PLAYER_SELECT_BUTTONS] = {
294 barracks_buttons("CHP_00", 126, 211, -1, -1, 0), // create
295 barracks_buttons("CHP_13", 181, 211, -1, -1, 13), // clone
296 barracks_buttons("CHP_11", 228, 211, -1, -1, 11), // delete
297 barracks_buttons("CHP_01", 423, 232, -1, -1, 1), // scroll up
298 barracks_buttons("CHP_02", 452, 232, -1, -1, 2), // scroll down
299 barracks_buttons("CHP_12", 475, 213, -1, -1, 12), // accept
300 barracks_buttons("CHP_14", 438, 104, -1, -1, 14), // single
301 barracks_buttons("CHP_15", 485, 104, -1, -1, 15) // multi
303 // create, clone and delete (respectively)
304 barracks_buttons("CPB_00", 114, 205, 117, 240, 0),
305 barracks_buttons("CPB_01", 172, 205, 175, 240, 1),
306 barracks_buttons("CPB_02", 226, 205, 229, 240, 2),
308 // scroll up, scroll down, and accept (respectively)
309 barracks_buttons("CPB_03", 429, 213, -1, -1, 3),
310 barracks_buttons("CPB_04", 456, 213, -1, -1, 4),
311 barracks_buttons("CPB_05", 481, 207, 484, 246, 5),
313 // single player select and multiplayer select, respectively
314 barracks_buttons("CPB_06", 428, 82, 430, 108, 6),
315 barracks_buttons("CPB_07", 477, 82, 481, 108, 7)
319 // create, clone and delete (respectively)
320 barracks_buttons("2_CPB_00", 182, 328, 199, 384, 0),
321 barracks_buttons("2_CPB_01", 275, 328, 292, 384, 1),
322 barracks_buttons("2_CPB_02", 361, 328, 379, 384, 2),
324 // scroll up, scroll down, and accept (respectively)
325 barracks_buttons("2_CPB_03", 686, 341, -1, -1, 3),
326 barracks_buttons("2_CPB_04", 729, 341, -1, -1, 4),
327 barracks_buttons("2_CPB_05", 770, 332, 787, 394, 5),
329 // single player select and multiplayer select, respectively
330 barracks_buttons("2_CPB_06", 685, 132, 700, 173, 6),
331 barracks_buttons("2_CPB_07", 764, 132, 782, 173, 7)
335 // FIXME add to strings.tbl
337 #define PLAYER_SELECT_NUM_TEXT 1
339 UI_XSTR Player_select_text[GR_NUM_RESOLUTIONS][PLAYER_SELECT_NUM_TEXT] = {
341 { "Choose Pilot", 1436, 122, 90, UI_XSTR_COLOR_GREEN, -1, NULL }
344 { "Choose Pilot", 1436, 195, 143, UI_XSTR_COLOR_GREEN, -1, NULL }
349 UI_WINDOW Player_select_window; // ui window for this screen
350 UI_BUTTON Player_select_list_region; // button for detecting mouse clicks on this screen
351 UI_INPUTBOX Player_select_input_box; // input box for adding new pilot names
353 // #define PLAYER_SELECT_PALETTE_FNAME NOX("InterfacePalette")
354 int Player_select_background_bitmap; // bitmap for this screen
355 // int Player_select_palette; // palette bitmap for this screen
356 int Player_select_autoaccept = 0;
358 // flag indicating if this is the absolute first pilot created and selected. Used to determine
359 // if the main hall should display the help overlay screen
360 int Player_select_very_first_pilot = 0;
361 int Player_select_initial_count = 0;
362 char Player_select_very_first_pilot_callsign[CALLSIGN_LEN + 2];
364 extern int Main_hall_bitmap; // bitmap handle to the main hall bitmap
366 int Player_select_mode; // single or multiplayer - never set directly. use player_select_init_player_stuff()
367 int Player_select_num_pilots; // # of pilots on the list
368 int Player_select_list_start; // index of first list item to start displaying in the box
369 int Player_select_pilot; // index into the Pilot array of which is selected as the active pilot
370 int Player_select_input_mode; // 0 if the player _isn't_ typing a callsign, 1 if he is
371 char Pilots_arr[MAX_PILOTS][MAX_FILENAME_LEN];
372 char *Pilots[MAX_PILOTS];
373 int Player_select_clone_flag; // clone the currently selected pilot
374 char Player_select_last_pilot[CALLSIGN_LEN + 10]; // callsign of the last used pilot, or none if there wasn't one
375 int Player_select_last_is_multi;
377 int Player_select_force_bastion = 0;
379 // notification text areas
381 static int Player_select_bottom_text_y[GR_NUM_RESOLUTIONS] = {
390 static int Player_select_middle_text_y[GR_NUM_RESOLUTIONS] = {
399 char Player_select_bottom_text[150] = "";
400 char Player_select_middle_text[150] = "";
401 void player_select_set_bottom_text(const char *txt);
402 void player_select_set_middle_text(const char *txt);
405 // FORWARD DECLARATIONS
406 void player_select_init_player_stuff(int mode); // switch between single and multiplayer modes
407 void player_select_set_input_mode(int n);
408 void player_select_button_pressed(int n);
409 void player_select_scroll_list_up();
410 void player_select_scroll_list_down();
411 int player_select_create_new_pilot();
412 void player_select_delete_pilot();
413 void player_select_display_all_text();
414 void player_select_display_copyright();
415 void player_select_set_controls(int gray);
416 void player_select_draw_list();
417 void player_select_process_noninput(int k);
418 void player_select_process_input(int k);
419 int player_select_pilot_file_filter(const char *filename);
420 int player_select_get_last_pilot_info();
421 void player_select_eval_very_first_pilot();
422 void player_select_commit();
423 void player_select_cancel_create();
426 // basically, gray out all controls (gray == 1), or ungray the controls (gray == 0)
427 void player_select_set_controls(int gray)
431 for(idx=0;idx<NUM_PLAYER_SELECT_BUTTONS;idx++){
433 Player_select_buttons[gr_screen.res][idx].button.disable();
435 Player_select_buttons[gr_screen.res][idx].button.enable();
440 // functions for selecting single/multiplayer pilots at the very beginning of Freespace
441 void player_select_init()
449 // start a looping ambient sound
450 main_hall_start_ambient();
452 Player_select_force_bastion = 0;
455 Demo_title_bitmap = bm_load(Demo_title_bitmap_filename);
456 if ( Demo_title_bitmap >= 0 ) {
457 Demo_title_active = 1;
458 Demo_title_expire_timestamp = timestamp(5000);
460 Demo_title_active = 0;
464 // create the UI window
465 Player_select_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
466 Player_select_window.set_mask_bmap(Player_select_background_mask_bitmap[gr_screen.res]);
469 common_set_interface_palette("ChoosePilotPalette");
472 // initialize the control buttons
473 for (i=0; i<NUM_PLAYER_SELECT_BUTTONS; i++) {
474 b = &Player_select_buttons[gr_screen.res][i];
477 if ( (i == SCROLL_LIST_UP_BUTTON) || (i == SCROLL_LIST_DOWN_BUTTON) )
478 b->button.create(&Player_select_window, NULL, b->x, b->y, 60, 30, 1, 1);
480 b->button.create(&Player_select_window, NULL, b->x, b->y, 60, 30, 1, 1);
482 // set its highlight action
483 b->button.set_highlight_action(common_play_highlight_sound);
485 // set its animation bitmaps
486 b->button.set_bmaps(b->filename);
488 // link the mask hotspot
489 b->button.link_hotspot(b->hotspot);
494 w = &Player_select_window;
495 w->add_XSTR("Create", 1034, Player_select_buttons[gr_screen.res][CREATE_PILOT_BUTTON].xt, Player_select_buttons[gr_screen.res][CREATE_PILOT_BUTTON].yt, &Player_select_buttons[gr_screen.res][CREATE_PILOT_BUTTON].button, UI_XSTR_COLOR_GREEN);
496 w->add_XSTR("Clone", 1040, Player_select_buttons[gr_screen.res][CLONE_BUTTON].xt, Player_select_buttons[gr_screen.res][CLONE_BUTTON].yt, &Player_select_buttons[gr_screen.res][CLONE_BUTTON].button, UI_XSTR_COLOR_GREEN);
497 w->add_XSTR("Remove", 1038, Player_select_buttons[gr_screen.res][DELETE_BUTTON].xt, Player_select_buttons[gr_screen.res][DELETE_BUTTON].yt, &Player_select_buttons[gr_screen.res][DELETE_BUTTON].button, UI_XSTR_COLOR_GREEN);
499 w->add_XSTR("Select", 1039, Player_select_buttons[gr_screen.res][ACCEPT_BUTTON].xt, Player_select_buttons[gr_screen.res][ACCEPT_BUTTON].yt, &Player_select_buttons[gr_screen.res][ACCEPT_BUTTON].button, UI_XSTR_COLOR_PINK);
500 w->add_XSTR("Single", 1041, Player_select_buttons[gr_screen.res][SINGLE_BUTTON].xt, Player_select_buttons[gr_screen.res][SINGLE_BUTTON].yt, &Player_select_buttons[gr_screen.res][SINGLE_BUTTON].button, UI_XSTR_COLOR_GREEN);
501 w->add_XSTR("Multi", 1042, Player_select_buttons[gr_screen.res][MULTI_BUTTON].xt, Player_select_buttons[gr_screen.res][MULTI_BUTTON].yt, &Player_select_buttons[gr_screen.res][MULTI_BUTTON].button, UI_XSTR_COLOR_GREEN);
502 for(i=0; i<PLAYER_SELECT_NUM_TEXT; i++) {
503 w->add_XSTR(&Player_select_text[gr_screen.res][i]);
507 // create the list button text select region
508 Player_select_list_region.create(&Player_select_window, "", Choose_list_coords[gr_screen.res][0], Choose_list_coords[gr_screen.res][1], Choose_list_coords[gr_screen.res][2], Choose_list_coords[gr_screen.res][3], 0, 1);
509 Player_select_list_region.hide();
511 // create the pilot callsign input box
512 Player_select_input_box.create(&Player_select_window, Choose_list_coords[gr_screen.res][0], Choose_list_coords[gr_screen.res][1], Choose_list_coords[gr_screen.res][2] , CALLSIGN_LEN - 1, "", UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_KEYTHRU | UI_INPUTBOX_FLAG_LETTER_FIRST);
513 Player_select_input_box.set_valid_chars(VALID_PILOT_CHARS);
514 Player_select_input_box.hide();
515 Player_select_input_box.disable();
517 // not currently entering any text
518 Player_select_input_mode = 0;
520 // set up hotkeys for buttons so we draw the correct animation frame when a key is pressed
521 Player_select_buttons[gr_screen.res][SCROLL_LIST_UP_BUTTON].button.set_hotkey(SDLK_UP);
522 Player_select_buttons[gr_screen.res][SCROLL_LIST_DOWN_BUTTON].button.set_hotkey(SDLK_DOWN);
523 Player_select_buttons[gr_screen.res][ACCEPT_BUTTON].button.set_hotkey(SDLK_RETURN);
524 Player_select_buttons[gr_screen.res][CREATE_PILOT_BUTTON].button.set_hotkey(SDLK_c);
526 // disable the single player button in the multiplayer beta
527 #ifdef MULTIPLAYER_BETA_BUILD
528 Player_select_buttons[gr_screen.res][SINGLE_BUTTON].button.hide();
529 Player_select_buttons[gr_screen.res][SINGLE_BUTTON].button.disable();
530 #elif defined(E3_BUILD) || defined(PRESS_TOUR_BUILD)
531 Player_select_buttons[gr_screen.res][MULTI_BUTTON].button.hide();
532 Player_select_buttons[gr_screen.res][MULTI_BUTTON].button.disable();
536 // attempt to load in the background bitmap
537 Player_select_background_bitmap = bm_load(Player_select_background_bitmap_name[gr_screen.res]);
538 SDL_assert(Player_select_background_bitmap >= 0);
540 // load in the palette for the screen
541 // Player_select_palette = bm_load(PLAYER_SELECT_PALETTE);
542 // Player_select_palette_set = 0;
544 // unset the very first pilot data
545 Player_select_very_first_pilot = 0;
546 Player_select_initial_count = -1;
547 memset(Player_select_very_first_pilot_callsign, 0, CALLSIGN_LEN + 2);
549 // if(Player_select_num_pilots == 0){
550 // Player_select_autoaccept = 1;
553 // if we found a pilot
554 #if defined(DEMO) || defined(OEM_BUILD) || defined(E3_BUILD) || defined(PRESS_TOUR_BUILD) // not for FS2_DEMO
555 player_select_init_player_stuff(PLAYER_SELECT_MODE_SINGLE);
556 #elif defined(MULTIPLAYER_BETA_BUILD)
557 player_select_init_player_stuff(PLAYER_SELECT_MODE_MULTI);
559 if (player_select_get_last_pilot_info()) {
560 if (Player_select_last_is_multi) {
561 player_select_init_player_stuff(PLAYER_SELECT_MODE_MULTI);
563 player_select_init_player_stuff(PLAYER_SELECT_MODE_SINGLE);
566 // otherwise go to the single player mode by default
568 player_select_init_player_stuff(PLAYER_SELECT_MODE_SINGLE);
572 if((Player_select_num_pilots == 1) && Player_select_input_mode){
573 Player_select_autoaccept = 1;
578 // Display the demo title screen
579 void demo_title_blit()
585 if ( timestamp_elapsed(Demo_title_expire_timestamp) ) {
586 Demo_title_active = 0;
591 Demo_title_active = 0;
594 if ( Demo_title_need_fade_in ) {
598 gr_set_bitmap(Demo_title_bitmap);
603 if ( Demo_title_need_fade_in ) {
605 Demo_title_need_fade_in = 0;
608 if ( !Demo_title_active ) {
615 void player_select_do()
620 if ( Demo_title_active ) {
626 // set the input box at the "virtual" line 0 to be active so the player can enter a callsign
627 if (Player_select_input_mode){
628 Player_select_input_box.set_focus();
631 // process any ui window stuff
632 k = Player_select_window.process();
634 extern void game_process_cheats(int k);
635 game_process_cheats(k);
638 // switch between single and multiplayer modes
640 #if defined(DEMO) || defined(OEM_BUILD) // not for FS2_DEMO
644 if(Player_select_input_mode){
645 gamesnd_play_iface(SND_GENERAL_FAIL);
648 // play a little sound
649 gamesnd_play_iface(SND_USER_SELECT);
650 if(Player_select_mode == PLAYER_SELECT_MODE_MULTI){
652 player_select_set_bottom_text(XSTR( "Single Player Mode", 376));
654 player_select_set_bottom_text(XSTR( "Single-Player Mode", 376));
657 // reinitialize as single player mode
658 player_select_init_player_stuff(PLAYER_SELECT_MODE_SINGLE);
659 } else if(Player_select_mode == PLAYER_SELECT_MODE_SINGLE){
660 player_select_set_bottom_text(XSTR( "Multiplayer Mode", 377));
662 // reinitialize as multiplayer mode
663 player_select_init_player_stuff(PLAYER_SELECT_MODE_MULTI);
669 // draw the player select pseudo-dialog over it
670 gr_set_bitmap(Player_select_background_bitmap, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
673 // press the accept button
674 if (Player_select_autoaccept) {
675 Player_select_buttons[gr_screen.res][ACCEPT_BUTTON].button.press_button();
678 // draw any ui window stuf
679 Player_select_window.draw();
681 // light up the correct mode button (single or multi)
682 if (Player_select_mode == PLAYER_SELECT_MODE_SINGLE){
683 Player_select_buttons[gr_screen.res][SINGLE_BUTTON].button.draw_forced(2);
685 Player_select_buttons[gr_screen.res][MULTI_BUTTON].button.draw_forced(2);
688 // draw the pilot list text
689 player_select_draw_list();
691 // draw copyright message on the bottom on the screen
692 player_select_display_copyright();
694 if (!Player_select_input_mode) {
695 player_select_process_noninput(k);
697 player_select_process_input(k);
700 // draw any pending messages on the bottom or middle of the screen
701 player_select_display_all_text();
704 // gr_set_color_fast(&Color_bright_green);
705 // gr_string(0x8000, 10, "Development version - DO NOT RELEASE");
709 gr_set_color(255, 0, 0);
711 vector *arr[5] = {&whee[0], &whee[1], &whee[2], &whee[3], &whee[4]};
712 whee[0].x = 10; whee[0].y = 10; whee[0].z = 0.0f;
713 whee[1].x = 50; whee[1].y = 50; whee[1].z = 0.0f;
714 whee[2].x = 50; whee[2].y = 90; whee[2].z = 0.0f;
715 whee[3].x = 90; whee[3].y = 130; whee[3].z = 0.0f;
716 whee[4].x = 180; whee[4].y = 130; whee[4].z = 0.0f;
717 gr_pline_special(arr, 5, 2);
724 void player_select_close()
726 // destroy the player select window
727 Player_select_window.destroy();
729 // if we're in input mode - we should undo the pilot create reqeust
730 if(Player_select_input_mode){
731 player_select_cancel_create();
734 // actually set up the Player struct here
735 if((Player_select_pilot == -1) || (Player_select_num_pilots == 0)){
736 nprintf(("General","WARNING! No pilot selected! We should be exiting the game now!\n"));
740 // unload all bitmaps
741 if(Player_select_background_bitmap >= 0){
742 bm_release(Player_select_background_bitmap);
743 Player_select_background_bitmap = -1;
745 // if(Player_select_palette >= 0){
746 // bm_release(Player_select_palette);
747 //Player_select_palette = -1;
751 common_free_interface_palette();
754 // setup the player struct
756 Player = &Players[0];
757 Player->flags |= PLAYER_FLAGS_STRUCTURE_IN_USE;
759 // now read in a the pilot data
760 if (read_pilot_file(Pilots[Player_select_pilot], !Player_select_mode, Player) != 0) {
761 Error(LOCATION,"Couldn't load pilot file, bailing");
766 if (Player_select_force_bastion) {
767 Player->on_bastion = 1;
771 void player_select_set_input_mode(int n)
775 // set the input mode
776 Player_select_input_mode = n;
778 // enable all the player select buttons
779 for (i=0; i<NUM_PLAYER_SELECT_BUTTONS; i++){
780 Player_select_buttons[gr_screen.res][i].button.enable(!n);
783 Player_select_buttons[gr_screen.res][ACCEPT_BUTTON].button.set_hotkey(n ? -1 : SDLK_RETURN);
784 Player_select_buttons[gr_screen.res][CREATE_PILOT_BUTTON].button.set_hotkey(n ? -1 : SDLK_c);
786 // enable the player select input box
787 if(Player_select_input_mode){
788 Player_select_input_box.enable();
789 Player_select_input_box.unhide();
791 Player_select_input_box.hide();
792 Player_select_input_box.disable();
796 static void delete_pilot_callback(int choice)
805 player_select_delete_pilot();
808 void player_select_button_pressed(int n)
811 case SCROLL_LIST_UP_BUTTON:
812 player_select_set_bottom_text("");
814 player_select_scroll_list_up();
817 case SCROLL_LIST_DOWN_BUTTON:
818 player_select_set_bottom_text("");
820 player_select_scroll_list_down();
824 // make sure he has a valid pilot selected
825 if (Player_select_pilot < 0) {
826 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR( "You must select a valid pilot first", 378));
828 player_select_commit();
833 // if we're at max-pilots, don't allow another to be added
834 if (Player_select_num_pilots >= MAX_PILOTS) {
835 player_select_set_bottom_text(XSTR( "You already have the maximum # of pilots!", 379));
837 gamesnd_play_iface(SND_GENERAL_FAIL);
841 if (Player_select_pilot >= 0) {
842 // first we have to make sure this guy is actually loaded for when we create the clone
843 if (Player == NULL) {
844 Player = &Players[0];
845 Player->flags |= PLAYER_FLAGS_STRUCTURE_IN_USE;
848 // attempt to read in the pilot file of the guy to be cloned
849 if (read_pilot_file(Pilots[Player_select_pilot], !Player_select_mode, Player) != 0) {
850 Error(LOCATION,"Couldn't load pilot file, bailing");
855 // set the clone flag
856 Player_select_clone_flag = 1;
858 // create the new pilot (will be cloned with Player_select_clone_flag_set)
859 if (!player_select_create_new_pilot()) {
860 player_select_set_bottom_text(XSTR( "Error creating new pilot file!", 380));
861 Player_select_clone_flag = 0;
862 memset(Player,0,sizeof(player));
867 // clear the player out
868 // JH: What the hell? How do you clone a pilot if you clear out the source you are copying
869 // from? These next 2 lines are pure stupidity, so I commented them out!
870 // memset(Player,0,sizeof(player));
873 // display some text on the bottom of the dialog
874 player_select_set_bottom_text(XSTR( "Type Callsign and Press Enter", 381));
876 // gray out all controls in the dialog
877 player_select_set_controls(1);
881 case CREATE_PILOT_BUTTON:
882 // if we're at max-pilots, don't allow another to be added
883 if(Player_select_num_pilots >= MAX_PILOTS){
884 player_select_set_bottom_text(XSTR( "You already have the maximum # of pilots!", 379));
886 gamesnd_play_iface(SND_GENERAL_FAIL);
890 // create a new pilot
891 if (!player_select_create_new_pilot()) {
892 player_select_set_bottom_text(XSTR( "Type Callsign and Press Enter", 381));
895 // don't clone anyone
896 Player_select_clone_flag = 0;
898 // display some text on the bottom of the dialog
899 player_select_set_bottom_text(XSTR( "Type Callsign and Press Enter", 381));
901 // gray out all controls
902 player_select_set_controls(1);
906 player_select_set_bottom_text("");
908 if (Player_select_pilot >= 0) {
909 // display a popup requesting confirmation
910 popup_callback(delete_pilot_callback, PF_TITLE_BIG | PF_TITLE_RED, 2, POPUP_NO, POPUP_YES, XSTR( "Warning!\n\nAre you sure you wish to delete this pilot?", 382));
915 player_select_set_bottom_text("");
917 Player_select_autoaccept = 0;
918 // switch to single player mode
919 if (Player_select_mode != PLAYER_SELECT_MODE_SINGLE) {
920 // play a little sound
921 gamesnd_play_iface(SND_USER_SELECT);
923 player_select_set_bottom_text(XSTR( "Single Player Mode", 376));
925 // reinitialize as single player mode
926 player_select_init_player_stuff(PLAYER_SELECT_MODE_SINGLE);
928 gamesnd_play_iface(SND_GENERAL_FAIL);
933 player_select_set_bottom_text("");
935 Player_select_autoaccept = 0;
936 #if defined(DEMO) || defined(OEM_BUILD) || defined(__EMSCRIPTEN__) // not for FS2_DEMO
937 game_feature_not_in_demo_popup();
939 // switch to multiplayer mode
940 if (Player_select_mode != PLAYER_SELECT_MODE_MULTI) {
941 // play a little sound
942 gamesnd_play_iface(SND_USER_SELECT);
944 player_select_set_bottom_text(XSTR( "Multiplayer Mode", 377));
946 // reinitialize as multiplayer mode
947 player_select_init_player_stuff(PLAYER_SELECT_MODE_MULTI);
949 gamesnd_play_iface(SND_GENERAL_FAIL);
956 int player_select_create_new_pilot()
960 // make sure we haven't reached the max
961 if (Player_select_num_pilots >= MAX_PILOTS) {
962 gamesnd_play_iface(SND_GENERAL_FAIL);
966 int play_scroll_sound = 1;
969 if ( Demo_title_active ) {
970 play_scroll_sound = 0;
974 if ( play_scroll_sound ) {
975 gamesnd_play_iface(SND_SCROLL);
978 idx = Player_select_num_pilots;
980 // move all the pilots in the list up
982 SDL_strlcpy(Pilots[idx + 1], Pilots[idx], MAX_FILENAME_LEN);
985 // by default, set the default netgame protocol to be VMT
986 Multi_options_g.protocol = NET_TCP;
988 // select the beginning of the list
989 Player_select_pilot = 0;
990 Player_select_num_pilots++;
991 Pilots[Player_select_pilot][0] = 0;
992 Player_select_list_start= 0;
994 // set us to be in input mode
995 player_select_set_input_mode(1);
997 // set the input box to have focus
998 Player_select_input_box.set_focus();
999 Player_select_input_box.set_text("");
1000 Player_select_input_box.update_dimensions(Choose_list_coords[gr_screen.res][0], Choose_list_coords[gr_screen.res][1], Choose_list_coords[gr_screen.res][2], gr_get_font_height());
1005 void player_select_delete_pilot()
1007 char filename[MAX_PATH_LEN + 1];
1010 // tack on the full path and the pilot file extension
1011 // build up the path name length
1012 // make sure we do this based upon whether we're in single or multiplayer mode
1013 SDL_strlcpy( filename, Pilots[Player_select_pilot], SDL_arraysize(filename) );
1014 SDL_strlcat( filename, NOX(".plr"), SDL_arraysize(filename) );
1016 // attempt to delete the pilot
1017 if (Player_select_mode == PLAYER_SELECT_MODE_SINGLE) {
1018 cf_delete( filename, CF_TYPE_SINGLE_PLAYERS );
1020 cf_delete( filename, CF_TYPE_MULTI_PLAYERS );
1023 // delete all the campaign save files for this pilot.
1024 mission_campaign_delete_all_savefiles( Pilots[Player_select_pilot], (Player_select_mode != PLAYER_SELECT_MODE_SINGLE) );
1026 // move all the players down
1027 for (i=Player_select_pilot; i<Player_select_num_pilots-1; i++){
1028 SDL_strlcpy(Pilots[i], Pilots[i + 1], MAX_FILENAME_LEN);
1031 // correcly set the # of pilots and the currently selected pilot
1032 Player_select_num_pilots--;
1033 if (Player_select_pilot >= Player_select_num_pilots) {
1034 Player_select_pilot = Player_select_num_pilots - 1;
1039 // scroll the list of players up
1040 void player_select_scroll_list_up()
1042 if (Player_select_pilot == -1)
1045 // change the pilot selected index and play the appropriate sound
1046 if (Player_select_pilot) {
1047 Player_select_pilot--;
1048 gamesnd_play_iface(SND_SCROLL);
1050 gamesnd_play_iface(SND_GENERAL_FAIL);
1053 if (Player_select_pilot < Player_select_list_start){
1054 Player_select_list_start = Player_select_pilot;
1058 // scroll the list of players down
1059 void player_select_scroll_list_down()
1061 // change the pilot selected index and play the appropriate sound
1062 if (Player_select_pilot < Player_select_num_pilots - 1) {
1063 Player_select_pilot++;
1064 gamesnd_play_iface(SND_SCROLL);
1066 gamesnd_play_iface(SND_GENERAL_FAIL);
1069 if (Player_select_pilot >= (Player_select_list_start + Player_select_max_lines[gr_screen.res])){
1070 Player_select_list_start++;
1074 // fill in the data on the last played pilot (callsign and is_multi or not)
1075 int player_select_get_last_pilot_info()
1077 const char *last_player;
1079 last_player = os_config_read_string( NULL, "LastPlayer", NULL);
1081 if(last_player == NULL){
1084 SDL_strlcpy(Player_select_last_pilot, last_player, SDL_arraysize(Player_select_last_pilot));
1087 // determine if he was a single or multi-player based upon the last character in his callsign
1088 Player_select_last_is_multi = Player_select_last_pilot[strlen(Player_select_last_pilot)-1] == 'M' ? 1 : 0;
1089 Player_select_last_pilot[strlen(Player_select_last_pilot)-1]='\0';
1094 int player_select_get_last_pilot()
1096 // if the player has the Cmdline_use_last_pilot command line option set, try and drop out quickly
1097 if(Cmdline_use_last_pilot){
1100 if(!player_select_get_last_pilot_info()){
1104 if(Player_select_last_is_multi){
1105 Player_select_num_pilots = cf_get_file_list_preallocated(MAX_PILOTS, Pilots_arr, Pilots, CF_TYPE_MULTI_PLAYERS, NOX("*.plr"), CF_SORT_TIME);
1107 Player_select_num_pilots = cf_get_file_list_preallocated(MAX_PILOTS, Pilots_arr, Pilots, CF_TYPE_SINGLE_PLAYERS, NOX("*.plr"), CF_SORT_TIME);
1110 Player_select_pilot = -1;
1112 // pick the last player
1113 for(idx=0;idx<Player_select_num_pilots;idx++){
1114 if(strcmp(Player_select_last_pilot,Pilots_arr[idx])==0){
1115 Player_select_pilot = idx;
1120 // set this so that we don't incorrectly create a "blank" pilot - .plr
1121 // in the player_select_close() function
1122 Player_select_num_pilots = 0;
1124 // if we've actually found a valid pilot, load him up
1125 if(Player_select_pilot != -1){
1126 Player = &Players[0];
1127 read_pilot_file(Pilots_arr[idx],!Player_select_last_is_multi,Player);
1128 Player->flags |= PLAYER_FLAGS_STRUCTURE_IN_USE;
1136 void player_select_init_player_stuff(int mode)
1138 Player_select_list_start = 0;
1140 // set the select mode to single player for default
1141 Player_select_mode = mode;
1143 // load up the list of players based upon the Player_select_mode (single or multiplayer)
1144 Get_file_list_filter = player_select_pilot_file_filter;
1145 if (mode == PLAYER_SELECT_MODE_SINGLE){
1146 Player_select_num_pilots = cf_get_file_list_preallocated(MAX_PILOTS, Pilots_arr, Pilots, CF_TYPE_SINGLE_PLAYERS, NOX("*.plr"), CF_SORT_TIME);
1148 Player_select_num_pilots = cf_get_file_list_preallocated(MAX_PILOTS, Pilots_arr, Pilots, CF_TYPE_MULTI_PLAYERS, NOX("*.plr"), CF_SORT_TIME);
1153 // if this value is -1, it means we should set it to the num pilots count
1154 if(Player_select_initial_count == -1){
1155 Player_select_initial_count = Player_select_num_pilots;
1158 // select the first pilot if any exist, otherwise set to -1
1159 if (Player_select_num_pilots == 0) {
1160 Player_select_pilot = -1;
1162 player_select_set_middle_text(XSTR("Please Enter a Callsign", 381));
1164 player_select_set_middle_text(XSTR( "Type Callsign and Press Enter", 381));
1166 player_select_set_controls(1); // gray out the controls
1167 player_select_create_new_pilot();
1169 Player_select_pilot = 0;
1173 void player_select_draw_list()
1177 for (idx=0; idx<Player_select_max_lines[gr_screen.res]; idx++) {
1178 // only draw as many pilots as we have
1179 if ((idx + Player_select_list_start) == Player_select_num_pilots)
1182 // if the currently selected pilot is this line, draw it highlighted
1183 if ( (idx + Player_select_list_start) == Player_select_pilot) {
1184 // if he's the active pilot and is also the current selection, super-highlight him
1185 gr_set_color_fast(&Color_text_active);
1187 // otherwise draw him normally
1189 gr_set_color_fast(&Color_text_normal);
1192 // draw the actual callsign
1193 gr_printf(Choose_list_coords[gr_screen.res][0], Choose_list_coords[gr_screen.res][1] + (idx * gr_get_font_height()), Pilots[idx + Player_select_list_start]);
1197 void player_select_process_noninput(int k)
1201 // check for pressed buttons
1202 for (idx=0; idx<NUM_PLAYER_SELECT_BUTTONS; idx++) {
1203 if (Player_select_buttons[gr_screen.res][idx].button.pressed()) {
1204 player_select_button_pressed(idx);
1208 // check for keypresses
1210 // quit the game entirely
1212 gameseq_post_event(GS_EVENT_QUIT_GAME);
1215 case SDLK_RETURN | KEY_CTRLED:
1216 player_select_button_pressed(ACCEPT_BUTTON);
1219 // delete the currently highlighted pilot
1221 if (Player_select_pilot >= 0) {
1222 // display a popup requesting confirmation
1223 popup_callback(delete_pilot_callback, PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON,2,POPUP_NO,POPUP_YES,XSTR( "Are you sure you want to delete this pilot?", 383));
1228 // check to see if the user has clicked on the "list region" button
1229 // and change the selected pilot appropriately
1230 if (Player_select_list_region.pressed()) {
1232 // get the mouse position
1233 Player_select_list_region.get_mouse_pos(NULL, &click_y);
1235 // determine what index to select
1236 //idx = (click_y+5) / 10;
1237 idx = click_y / gr_get_font_height();
1240 // if he selected a valid item
1241 if(((idx + Player_select_list_start) < Player_select_num_pilots) && (idx >= 0)){
1242 Player_select_pilot = idx + Player_select_list_start;
1246 // if the player has double clicked on a valid pilot, choose it and hit the accept button
1247 if (Player_select_list_region.double_clicked()) {
1248 if ((Player_select_pilot >= 0) && (Player_select_pilot < Player_select_num_pilots)) {
1249 player_select_button_pressed(ACCEPT_BUTTON);
1254 void player_select_process_input(int k)
1256 char buf[CALLSIGN_LEN + 1];
1259 // if the player is in the process of typing in a new pilot name...
1261 // cancel create pilot
1263 player_select_cancel_create();
1266 // accept a new pilot name
1268 Player_select_input_box.get_text(buf);
1269 drop_white_space(buf);
1271 if (!isalpha(*buf)) {
1274 for (idx=1; buf[idx]; idx++) {
1275 if (!isalpha(buf[idx]) && !isdigit(buf[idx]) && !SDL_strchr(VALID_PILOT_CHARS, buf[idx])) {
1282 for (idx=1; idx<Player_select_num_pilots; idx++) {
1283 if (!SDL_strcasecmp(buf, Pilots[idx])) {
1284 // verify if it is ok to overwrite the file
1285 if (pilot_verify_overwrite() == 1) {
1286 // delete the pilot and select the beginning of the list
1287 Player_select_pilot = idx;
1288 player_select_delete_pilot();
1289 Player_select_pilot = 0;
1290 idx = Player_select_num_pilots;
1300 if (!*buf || (idx < Player_select_num_pilots)) {
1305 gamesnd_play_iface(SND_GENERAL_FAIL);
1309 // Create the new pilot, and write out his file
1310 SDL_strlcpy(Pilots[0], buf, MAX_FILENAME_LEN);
1312 // if this is the first guy, we should set the Player struct
1313 if (Player == NULL) {
1314 Player = &Players[0];
1315 memset(Player, 0, sizeof(player));
1316 Player->flags |= PLAYER_FLAGS_STRUCTURE_IN_USE;
1319 SDL_strlcpy(Player->callsign, buf, SDL_arraysize(Player->callsign));
1320 init_new_pilot(Player, !Player_select_clone_flag);
1322 // set him as being a multiplayer pilot if we're in the correct mode
1323 if (Player_select_mode == PLAYER_SELECT_MODE_MULTI) {
1324 Player->flags |= PLAYER_FLAGS_IS_MULTI;
1325 Player->stats.flags |= STATS_FLAG_MULTIPLAYER;
1328 // create his pilot file
1329 write_pilot_file(Player);
1332 memset(Player, 0, sizeof(player));
1335 // make this guy the selected pilot and put him first on the list
1336 Player_select_pilot = 0;
1338 // unset the input mode
1339 player_select_set_input_mode(0);
1341 // clear any pending bottom text
1342 player_select_set_bottom_text("");
1344 // clear any pending middle text
1345 player_select_set_middle_text("");
1347 // ungray all the controls
1348 player_select_set_controls(0);
1350 // evaluate whether or not this is the very first pilot
1351 player_select_eval_very_first_pilot();
1357 // always kill middle text when a char is pressed in input mode
1359 player_select_set_middle_text("");
1364 // draw copyright message on the bottom on the screen
1365 void player_select_display_copyright()
1368 char Copyright_msg1[256], Copyright_msg2[256];
1371 gr_set_color_fast(&Color_bright);
1374 SDL_snprintf(Copyright_msg1, SDL_arraysize(Copyright_msg1), XSTR("Descent: FreeSpace - The Great War, Copyright %c 1998, Volition, Inc.", 384), '\xA8');
1376 SDL_snprintf(Copyright_msg1, SDL_arraysize(Copyright_msg1), XSTR("Descent: FreeSpace - The Great War, Copyright %c 1998, Volition, Inc.", 384), '\x83');
1378 SDL_strlcpy(Copyright_msg2, XSTR("All Rights Reserved", 385), SDL_arraysize(Copyright_msg2));
1380 gr_set_color_fast(&Color_white);
1382 SDL_snprintf(Copyright_msg1, SDL_arraysize(Copyright_msg1), NOX("FreeSpace 2"));
1384 SDL_snprintf(Copyright_msg2, SDL_arraysize(Copyright_msg2), XSTR("Copyright %c 1999, Volition, Inc. All rights reserved.", 385), '\xA8');
1386 SDL_snprintf(Copyright_msg2, SDL_arraysize(Copyright_msg2), XSTR("Copyright %c 1999, Volition, Inc. All rights reserved.", 385), '\x83');
1390 gr_get_string_size(&w, NULL, Copyright_msg1);
1391 sx = fl2i((gr_screen.max_w / 2) - w/2.0f + 0.5f);
1392 sy = (gr_screen.max_h - 2) - 2*gr_get_font_height();
1393 gr_string(sx, sy, Copyright_msg1);
1395 gr_get_string_size(&w, NULL, Copyright_msg2);
1396 sx = fl2i((gr_screen.max_w / 2) - w/2.0f + 0.5f);
1397 sy = (gr_screen.max_h - 2) - gr_get_font_height();
1398 gr_string(sx, sy, Copyright_msg2);
1401 void player_select_display_all_text()
1405 // only draw if we actually have a valid string
1406 if (strlen(Player_select_bottom_text)) {
1407 gr_get_string_size(&w, &h, Player_select_bottom_text);
1409 w = (gr_screen.max_w - w) / 2;
1411 gr_set_color_fast(&Color_bright);
1413 gr_set_color_fast(&Color_bright_white);
1415 gr_printf(w, Player_select_bottom_text_y[gr_screen.res], Player_select_bottom_text);
1418 // only draw if we actually have a valid string
1419 if (strlen(Player_select_middle_text)) {
1420 gr_get_string_size(&w, &h, Player_select_middle_text);
1422 w = (gr_screen.max_w - w) / 2;
1424 gr_set_color_fast(&Color_bright);
1426 gr_set_color_fast(&Color_bright_white);
1428 gr_printf(w, Player_select_middle_text_y[gr_screen.res], Player_select_middle_text);
1432 int player_select_pilot_file_filter(const char *filename)
1434 return !verify_pilot_file(filename, Player_select_mode == PLAYER_SELECT_MODE_SINGLE);
1437 void player_select_set_bottom_text(const char *txt)
1440 SDL_strlcpy(Player_select_bottom_text, txt, SDL_arraysize(Player_select_bottom_text));
1444 void player_select_set_middle_text(const char *txt)
1447 SDL_strlcpy(Player_select_middle_text, txt, SDL_arraysize(Player_select_middle_text));
1451 void player_select_eval_very_first_pilot()
1453 // never bring up the initial main hall help overlay
1454 // Player_select_very_first_pilot = 0;
1456 // if we already have this flag set, check to see if our callsigns match
1457 if(Player_select_very_first_pilot){
1458 // if the callsign has changed, unset the flag
1459 if(strcmp(Player_select_very_first_pilot_callsign,Pilots[Player_select_pilot])){
1460 Player_select_very_first_pilot = 0;
1463 // otherwise check to see if there is only 1 pilot
1465 if((Player_select_num_pilots == 1) && (Player_select_initial_count == 0)){
1467 Player_select_very_first_pilot = 1;
1468 SDL_strlcpy(Player_select_very_first_pilot_callsign, Pilots[Player_select_pilot], SDL_arraysize(Player_select_very_first_pilot_callsign));
1473 void player_select_commit()
1475 // if we've gotten to this point, we should have ensured this was the case
1476 SDL_assert(Player_select_num_pilots > 0);
1478 gameseq_post_event(GS_EVENT_MAIN_MENU);
1479 gamesnd_play_iface(SND_COMMIT_PRESSED);
1481 // evaluate if this is the _very_ first pilot
1482 player_select_eval_very_first_pilot();
1485 void player_select_cancel_create()
1489 Player_select_num_pilots--;
1491 // make sure we correct the Selected_pilot index to account for the cancelled action
1492 if (Player_select_num_pilots == 0) {
1493 Player_select_pilot = -1;
1496 // move all pilots down
1497 for (idx=0; idx<Player_select_num_pilots; idx++) {
1498 SDL_strlcpy(Pilots[idx], Pilots[idx + 1], MAX_FILENAME_LEN);
1501 // unset the input mode
1502 player_select_set_input_mode(0);
1504 // clear any bottom text
1505 player_select_set_bottom_text("");
1507 // clear any middle text
1508 player_select_set_middle_text("");
1510 // ungray all controls
1511 player_select_set_controls(0);
1513 // disable the autoaccept
1514 Player_select_autoaccept = 0;
1517 DCF(bastion,"Sets the player to be on the bastion")
1519 if(gameseq_get_state() == GS_STATE_INITIAL_PLAYER_SELECT){
1520 Player_select_force_bastion = 1;
1521 dc_printf("Player is now in the Bastion\n");
1525 #define MAX_PLAYER_TIPS 40
1527 char *Player_tips[MAX_PLAYER_TIPS];
1528 int Num_player_tips;
1529 int Player_tips_shown = 0;
1532 void player_tips_init()
1535 Num_player_tips = 0;
1537 // begin external localization stuff
1541 read_file_text("tips.tbl");
1544 while(!optional_string("#end")){
1545 required_string("+Tip:");
1547 if(Num_player_tips >= MAX_PLAYER_TIPS){
1550 Player_tips[Num_player_tips++] = stuff_and_malloc_string(F_NAME, NULL, 1024);
1552 } catch (parse_error_t rval) {
1553 Error(LOCATION, "Unable to parse tips.tbl! Code = %i.\n", (int)rval);
1556 // stop externalizing, homey
1561 void player_tips_close()
1566 for (i=0; i<MAX_PLAYER_TIPS; i++) {
1567 if (Player_tips[i]) {
1568 free(Player_tips[i]);
1569 Player_tips[i] = NULL;
1576 static int Tip_index = 0;
1578 static void player_tips_popup_callback(int choice)
1583 if (Tip_index >= Num_player_tips) {
1588 SDL_snprintf(all_txt, SDL_arraysize(all_txt), XSTR("NEW USER TIP\n\n%s", 1565), Player_tips[Tip_index]);
1590 popup_change_text(all_txt);
1596 write_pilot_file(Player);
1602 void player_tips_popup()
1605 // player has disabled tips
1606 if((Player != NULL) && !Player->tips){
1609 // only show tips once per instance of Freespace
1610 if(Player_tips_shown == 1){
1613 Player_tips_shown = 1;
1615 // randomly pick one
1616 Tip_index = (int)frand_range(0.0f, (float)Num_player_tips - 1.0f);
1620 SDL_snprintf(all_txt, SDL_arraysize(all_txt), XSTR("NEW USER TIP\n\n%s", 1565), Player_tips[Tip_index]);
1621 popup_callback(player_tips_popup_callback, PF_NO_SPECIAL_BUTTONS | PF_TITLE | PF_TITLE_WHITE, 3, XSTR("&Ok", 669), XSTR("&Next", 1444), XSTR("Don't show me this again", 1443), all_txt);