1 /*
2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
3 *
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on the
6 * source.
7 *
8 */
9
10
11 #include <limits.h>
12 #include <ctype.h>
13
14
15 #include "menuui/playermenu.h"
16 #include "ui/ui.h"
17 #include "gamesnd/gamesnd.h"
18 #include "playerman/player.h"
19 #include "io/key.h"
20 #include "playerman/managepilot.h"
21 #include "pilotfile/pilotfile.h"
22 #include "freespace2/freespace.h"
23 #include "gamesequence/gamesequence.h"
24 #include "cmdline/cmdline.h"
25 #include "osapi/osregistry.h"
26 #include "menuui/mainhallmenu.h"
27 #include "popup/popup.h"
28 #include "globalincs/alphacolors.h"
29 #include "localization/localize.h"
30 #include "mission/missioncampaign.h"
31 #include "parse/parselo.h"
32 #include "cfile/cfile.h"
33 #include "network/multi.h"
34
35
36 // --------------------------------------------------------------------------------------------------------
37 // PLAYER SELECT defines
38 //
39
40 int Max_lines; //Max number of pilots displayed in Window. Gets set in player_select_draw_list()
41
42 // button control defines
43 #define NUM_PLAYER_SELECT_BUTTONS 8 // button control defines
44
45 #define CREATE_PILOT_BUTTON 0 //
46 #define CLONE_BUTTON 1 //
47 #define DELETE_BUTTON 2 //
48 #define SCROLL_LIST_UP_BUTTON 3 //
49 #define SCROLL_LIST_DOWN_BUTTON 4 //
50 #define ACCEPT_BUTTON 5 //
51 #define SINGLE_BUTTON 6 //
52 #define MULTI_BUTTON 7 //
53
54 // list text display area
55 int Choose_list_coords[GR_NUM_RESOLUTIONS][4] = {
56 { // GR_640
57 114, 117, 400, 87
58 },
59 { // GR_1024
60 183, 186, 640, 139
61 }
62 };
63
64 char *Player_select_background_bitmap_name[GR_NUM_RESOLUTIONS] = {
65 "ChoosePilot",
66 "2_ChoosePilot"
67 };
68 char *Player_select_background_mask_bitmap[GR_NUM_RESOLUTIONS] = {
69 "ChoosePilot-m",
70 "2_ChoosePilot-m"
71 };
72 // #define PLAYER_SELECT_PALETTE NOX("ChoosePilotPalette") // palette for the screen
73
74 #define PLAYER_SELECT_MAIN_HALL_OVERLAY NOX("MainHall1") // main hall help overlay
75
76 // convenient struct for handling all button controls
77 struct barracks_buttons {
78 char *filename;
79 int x, y, xt, yt;
80 int hotspot;
81 UI_BUTTON button; // because we have a class inside this struct, we need the constructor below..
82
barracks_buttonsbarracks_buttons83 barracks_buttons(char *name, int x1, int y1, int xt1, int yt1, int h) : filename(name), x(x1), y(y1), xt(xt1), yt(yt1), hotspot(h) {}
84 };
85
86 static barracks_buttons Player_select_buttons[GR_NUM_RESOLUTIONS][NUM_PLAYER_SELECT_BUTTONS] = {
87 { // GR_640
88 // create, clone and delete (respectively)
89 barracks_buttons("CPB_00", 114, 205, 117, 240, 0),
90 barracks_buttons("CPB_01", 172, 205, 175, 240, 1),
91 barracks_buttons("CPB_02", 226, 205, 229, 240, 2),
92
93 // scroll up, scroll down, and accept (respectively)
94 barracks_buttons("CPB_03", 429, 213, -1, -1, 3),
95 barracks_buttons("CPB_04", 456, 213, -1, -1, 4),
96 barracks_buttons("CPB_05", 481, 207, 484, 246, 5),
97
98 // single player select and multiplayer select, respectively
99 barracks_buttons("CPB_06", 428, 82, 430, 108, 6),
100 barracks_buttons("CPB_07", 477, 82, 481, 108, 7)
101 },
102 { // GR_1024
103 // create, clone and delete (respectively)
104 barracks_buttons("2_CPB_00", 182, 328, 199, 384, 0),
105 barracks_buttons("2_CPB_01", 275, 328, 292, 384, 1),
106 barracks_buttons("2_CPB_02", 361, 328, 379, 384, 2),
107
108 // scroll up, scroll down, and accept (respectively)
109 barracks_buttons("2_CPB_03", 686, 341, -1, -1, 3),
110 barracks_buttons("2_CPB_04", 729, 341, -1, -1, 4),
111 barracks_buttons("2_CPB_05", 770, 332, 787, 394, 5),
112
113 // single player select and multiplayer select, respectively
114 barracks_buttons("2_CPB_06", 685, 132, 700, 173, 6),
115 barracks_buttons("2_CPB_07", 764, 132, 782, 173, 7)
116 }
117 };
118
119 // FIXME add to strings.tbl
120 #define PLAYER_SELECT_NUM_TEXT 1
121 UI_XSTR Player_select_text[GR_NUM_RESOLUTIONS][PLAYER_SELECT_NUM_TEXT] = {
122 { // GR_640
123 { "Choose Pilot", 1436, 122, 90, UI_XSTR_COLOR_GREEN, -1, NULL }
124 },
125 { // GR_1024
126 { "Choose Pilot", 1436, 195, 143, UI_XSTR_COLOR_GREEN, -1, NULL }
127 }
128 };
129
130 UI_WINDOW Player_select_window; // ui window for this screen
131 UI_BUTTON Player_select_list_region; // button for detecting mouse clicks on this screen
132 UI_INPUTBOX Player_select_input_box; // input box for adding new pilot names
133
134 // #define PLAYER_SELECT_PALETTE_FNAME NOX("InterfacePalette")
135 int Player_select_background_bitmap; // bitmap for this screen
136 // int Player_select_palette; // palette bitmap for this screen
137 int Player_select_autoaccept = 0;
138 // int Player_select_palette_set = 0;
139
140 // flag indicating if this is the absolute first pilot created and selected. Used to determine
141 // if the main hall should display the help overlay screen
142 int Player_select_very_first_pilot = 0;
143 int Player_select_initial_count = 0;
144 char Player_select_very_first_pilot_callsign[CALLSIGN_LEN + 2];
145
146 extern int Main_hall_bitmap; // bitmap handle to the main hall bitmap
147
148 int Player_select_mode; // single or multiplayer - never set directly. use player_select_init_player_stuff()
149 int Player_select_num_pilots; // # of pilots on the list
150 int Player_select_list_start; // index of first list item to start displaying in the box
151 int Player_select_pilot; // index into the Pilot array of which is selected as the active pilot
152 int Player_select_input_mode; // 0 if the player _isn't_ typing a callsign, 1 if he is
153 char Pilots_arr[MAX_PILOTS][MAX_FILENAME_LEN];
154 char *Pilots[MAX_PILOTS];
155 int Player_select_clone_flag; // clone the currently selected pilot
156 char Player_select_last_pilot[CALLSIGN_LEN + 10]; // callsign of the last used pilot, or none if there wasn't one
157 int Player_select_last_is_multi;
158
159 SCP_string Player_select_force_main_hall = "";
160
161 static int Player_select_no_save_pilot = 0; // to skip save of pilot in pilot_select_close()
162
163 int Player_select_screen_active = 0; // for pilot savefile loading - taylor
164
165 // notification text areas
166
167 static int Player_select_bottom_text_y[GR_NUM_RESOLUTIONS] = {
168 314, // GR_640
169 502 // GR_1024
170 };
171
172 static int Player_select_middle_text_y[GR_NUM_RESOLUTIONS] = {
173 253, // GR_640
174 404 // GR_1024
175 };
176
177 char Player_select_bottom_text[150] = "";
178 char Player_select_middle_text[150] = "";
179
180
181 // FORWARD DECLARATIONS
182 void player_select_init_player_stuff(int mode); // switch between single and multiplayer modes
183 void player_select_set_input_mode(int n);
184 void player_select_button_pressed(int n);
185 void player_select_scroll_list_up();
186 void player_select_scroll_list_down();
187 int player_select_create_new_pilot();
188 void player_select_delete_pilot();
189 void player_select_display_all_text();
190 void player_select_display_copyright();
191 void player_select_set_bottom_text(const char *txt);
192 void player_select_set_middle_text(const char *txt);
193 void player_select_set_controls(int gray);
194 void player_select_draw_list();
195 void player_select_process_noninput(int k);
196 void player_select_process_input(int k);
197 int player_select_pilot_file_filter(const char *filename);
198 int player_select_get_last_pilot_info();
199 void player_select_eval_very_first_pilot();
200 void player_select_commit();
201 void player_select_cancel_create();
202
203 extern int delete_pilot_file(char *pilot_name);
204
205
206 // basically, gray out all controls (gray == 1), or ungray the controls (gray == 0)
player_select_set_controls(int gray)207 void player_select_set_controls(int gray)
208 {
209 int idx;
210
211 for(idx=0;idx<NUM_PLAYER_SELECT_BUTTONS;idx++) {
212 if(gray) {
213 Player_select_buttons[gr_screen.res][idx].button.disable();
214 } else {
215 Player_select_buttons[gr_screen.res][idx].button.enable();
216 }
217 }
218 }
219
220 // functions for selecting single/multiplayer pilots at the very beginning of FreeSpace
player_select_init()221 void player_select_init()
222 {
223 int i;
224 barracks_buttons *b;
225 UI_WINDOW *w;
226
227 // start a looping ambient sound
228 main_hall_start_ambient();
229
230 Player_select_force_main_hall = "";
231
232 Player_select_screen_active = 1;
233
234 // create the UI window
235 Player_select_window.create(0, 0, gr_screen.max_w_unscaled, gr_screen.max_h_unscaled, 0);
236 Player_select_window.set_mask_bmap(Player_select_background_mask_bitmap[gr_screen.res]);
237
238 // initialize the control buttons
239 for (i=0; i<NUM_PLAYER_SELECT_BUTTONS; i++) {
240 b = &Player_select_buttons[gr_screen.res][i];
241
242 // create the button
243 b->button.create(&Player_select_window, NULL, b->x, b->y, 60, 30, 1, 1);
244
245 // set its highlight action
246 b->button.set_highlight_action(common_play_highlight_sound);
247
248 // set its animation bitmaps
249 b->button.set_bmaps(b->filename);
250
251 // link the mask hotspot
252 b->button.link_hotspot(b->hotspot);
253 }
254
255 // add some text
256 w = &Player_select_window;
257 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);
258 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);
259 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);
260
261 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);
262 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);
263 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);
264 for(i=0; i<PLAYER_SELECT_NUM_TEXT; i++) {
265 w->add_XSTR(&Player_select_text[gr_screen.res][i]);
266 }
267
268
269 // create the list button text select region
270 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);
271 Player_select_list_region.hide();
272
273 // create the pilot callsign input box
274 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);
275 Player_select_input_box.set_valid_chars(VALID_PILOT_CHARS);
276 Player_select_input_box.hide();
277 Player_select_input_box.disable();
278
279 // not currently entering any text
280 Player_select_input_mode = 0;
281
282 // set up hotkeys for buttons so we draw the correct animation frame when a key is pressed
283 Player_select_buttons[gr_screen.res][SCROLL_LIST_UP_BUTTON].button.set_hotkey(KEY_UP);
284 Player_select_buttons[gr_screen.res][SCROLL_LIST_DOWN_BUTTON].button.set_hotkey(KEY_DOWN);
285 Player_select_buttons[gr_screen.res][ACCEPT_BUTTON].button.set_hotkey(KEY_ENTER);
286 Player_select_buttons[gr_screen.res][CREATE_PILOT_BUTTON].button.set_hotkey(KEY_C);
287
288 // attempt to load in the background bitmap
289 Player_select_background_bitmap = bm_load(Player_select_background_bitmap_name[gr_screen.res]);
290 Assert(Player_select_background_bitmap >= 0);
291
292 // load in the palette for the screen
293 // Player_select_palette = bm_load(PLAYER_SELECT_PALETTE);
294 // Player_select_palette_set = 0;
295
296 // unset the very first pilot data
297 Player_select_very_first_pilot = 0;
298 Player_select_initial_count = -1;
299 memset(Player_select_very_first_pilot_callsign, 0, CALLSIGN_LEN + 2);
300
301 // if(Player_select_num_pilots == 0){
302 // Player_select_autoaccept = 1;
303 // }
304
305 // if we found a pilot
306 if ( player_select_get_last_pilot_info() ) {
307 if (Player_select_last_is_multi && !Networking_disabled) {
308 player_select_init_player_stuff(PLAYER_SELECT_MODE_MULTI);
309 } else {
310 player_select_init_player_stuff(PLAYER_SELECT_MODE_SINGLE);
311 }
312 } else { // otherwise go to the single player mode by default
313 player_select_init_player_stuff(PLAYER_SELECT_MODE_SINGLE);
314 }
315
316 if ( (Player_select_num_pilots == 1) && Player_select_input_mode ) {
317 Player_select_autoaccept = 1;
318 }
319 }
320
321 // no need to reset this to false because we only ever see player_select once per game run
322 static bool Startup_warning_dialog_displayed = false;
323
player_select_do()324 void player_select_do()
325 {
326 int k;
327
328 // Goober5000 - display a popup warning about problems in the mod
329 if ((Global_warning_count > 10 || Global_error_count > 0) && !Startup_warning_dialog_displayed) {
330 char text[512];
331 sprintf(text, "Warning!\n\nThe currently active mod has generated %d warnings and/or errors during program startup. These could have been caused by anything from incorrectly formated table files to corrupt models. While FreeSpace Open will attempt to compensate for these issues, it cannot guarantee a trouble-free gameplay experience. Source Code Project staff cannot provide assistance or support for these problems, as they are caused by the mod's data files, not FreeSpace Open's source code.", Global_warning_count + Global_error_count);
332 popup(PF_TITLE_BIG | PF_TITLE_RED | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, text);
333 Startup_warning_dialog_displayed = true;
334 }
335
336 // set the input box at the "virtual" line 0 to be active so the player can enter a callsign
337 if (Player_select_input_mode) {
338 Player_select_input_box.set_focus();
339 }
340
341 // process any ui window stuff
342 k = Player_select_window.process();
343
344 if (k) {
345 extern void game_process_cheats(int k);
346 game_process_cheats(k);
347 }
348
349 switch (k) {
350 // switch between single and multiplayer modes
351 case KEY_TAB: {
352 if (Player_select_input_mode) {
353 gamesnd_play_iface(SND_GENERAL_FAIL);
354 break;
355 }
356
357 // play a little sound
358 gamesnd_play_iface(SND_USER_SELECT);
359
360 if (Player_select_mode == PLAYER_SELECT_MODE_MULTI) {
361 player_select_set_bottom_text(XSTR( "Single-Player Mode", 376));
362
363 // reinitialize as single player mode
364 player_select_init_player_stuff(PLAYER_SELECT_MODE_SINGLE);
365 } else if (Player_select_mode == PLAYER_SELECT_MODE_SINGLE) {
366 player_select_set_bottom_text(XSTR( "Multiplayer Mode", 377));
367
368 // reinitialize as multiplayer mode
369 player_select_init_player_stuff(PLAYER_SELECT_MODE_MULTI);
370 }
371
372 break;
373 }
374
375 case KEY_ESC: {
376 // we can hit ESC to get out of text input mode, and we don't want
377 // to set this var in that case since it will crash on a NULL Player
378 // ptr when going to the mainhall
379 if ( !Player_select_input_mode ) {
380 Player_select_no_save_pilot = 1;
381 }
382
383 break;
384 }
385 }
386
387 // draw the player select pseudo-dialog over it
388 GR_MAYBE_CLEAR_RES(Player_select_background_bitmap);
389 gr_set_bitmap(Player_select_background_bitmap);
390 gr_bitmap(0,0,GR_RESIZE_MENU);
391
392 // press the accept button
393 if (Player_select_autoaccept) {
394 Player_select_buttons[gr_screen.res][ACCEPT_BUTTON].button.press_button();
395 }
396
397 // draw any ui window stuf
398 Player_select_window.draw();
399
400 // light up the correct mode button (single or multi)
401 if (Player_select_mode == PLAYER_SELECT_MODE_SINGLE) {
402 Player_select_buttons[gr_screen.res][SINGLE_BUTTON].button.draw_forced(2);
403 } else {
404 Player_select_buttons[gr_screen.res][MULTI_BUTTON].button.draw_forced(2);
405 }
406
407 // draw the pilot list text
408 player_select_draw_list();
409
410 // draw copyright message on the bottom on the screen
411 player_select_display_copyright();
412
413 if (!Player_select_input_mode) {
414 player_select_process_noninput(k);
415 } else {
416 player_select_process_input(k);
417 }
418
419 // draw any pending messages on the bottom or middle of the screen
420 player_select_display_all_text();
421
422 gr_flip();
423 }
424
player_select_close()425 void player_select_close()
426 {
427 // destroy the player select window
428 Player_select_window.destroy();
429
430 // if we're in input mode - we should undo the pilot create reqeust
431 if(Player_select_input_mode) {
432 player_select_cancel_create();
433 }
434
435 // if we are just exiting then don't try to save any pilot files - taylor
436 if (Player_select_no_save_pilot) {
437 Player = NULL;
438 return;
439 }
440
441 // actually set up the Player struct here
442 if ( (Player_select_pilot == -1) || (Player_select_num_pilots == 0) ) {
443 nprintf(("General","WARNING! No pilot selected! We should be exiting the game now!\n"));
444 return;
445 }
446
447 // unload all bitmaps
448 if(Player_select_background_bitmap >= 0) {
449 bm_release(Player_select_background_bitmap);
450 Player_select_background_bitmap = -1;
451 }
452 // if(Player_select_palette >= 0){
453 // bm_release(Player_select_palette);
454 //Player_select_palette = -1;
455 // }
456
457 // setup the player struct
458 Player_num = 0;
459 Player = &Players[0];
460 Player->flags |= PLAYER_FLAGS_STRUCTURE_IN_USE;
461
462 // New pilot file makes no distinction between multi pilots and regular ones, so let's do this here.
463 if (Player_select_mode == PLAYER_SELECT_MODE_MULTI) {
464 Player->flags |= PLAYER_FLAGS_IS_MULTI;
465 }
466
467 // WMC - Set appropriate game mode
468 if ( Player->flags & PLAYER_FLAGS_IS_MULTI ) {
469 Game_mode = GM_MULTIPLAYER;
470 } else {
471 Game_mode = GM_NORMAL;
472 }
473
474 // now read in a the pilot data
475 if ( !Pilot.load_player(Pilots[Player_select_pilot], Player) ) {
476 Error(LOCATION,"Couldn't load pilot file, bailing");
477 Player = NULL;
478 } else {
479 // NOTE: this may fail if there is no current campaign, it's not fatal
480 Pilot.load_savefile(Player->current_campaign);
481 }
482
483 if (Player_select_force_main_hall != "") {
484 main_hall_init(Player_select_force_main_hall);
485 }
486
487 // free memory from all parsing so far, all tbls found during game_init()
488 // and the current campaign which we loaded here
489 stop_parse();
490
491 Player_select_screen_active = 0;
492 }
493
player_select_set_input_mode(int n)494 void player_select_set_input_mode(int n)
495 {
496 int i;
497
498 // set the input mode
499 Player_select_input_mode = n;
500
501 // enable all the player select buttons
502 for (i=0; i<NUM_PLAYER_SELECT_BUTTONS; i++) {
503 Player_select_buttons[gr_screen.res][i].button.enable(!n);
504 }
505
506 Player_select_buttons[gr_screen.res][ACCEPT_BUTTON].button.set_hotkey(n ? -1 : KEY_ENTER);
507 Player_select_buttons[gr_screen.res][CREATE_PILOT_BUTTON].button.set_hotkey(n ? -1 : KEY_C);
508
509 // enable the player select input box
510 if (Player_select_input_mode) {
511 Player_select_input_box.enable();
512 Player_select_input_box.unhide();
513 } else {
514 Player_select_input_box.hide();
515 Player_select_input_box.disable();
516 }
517 }
518
player_select_button_pressed(int n)519 void player_select_button_pressed(int n)
520 {
521 int ret;
522
523 switch (n) {
524 case SCROLL_LIST_UP_BUTTON:
525 player_select_set_bottom_text("");
526
527 player_select_scroll_list_up();
528 break;
529
530 case SCROLL_LIST_DOWN_BUTTON:
531 player_select_set_bottom_text("");
532
533 player_select_scroll_list_down();
534 break;
535
536 case ACCEPT_BUTTON:
537 // make sure he has a valid pilot selected
538 if (Player_select_pilot < 0) {
539 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR( "You must select a valid pilot first", 378));
540 } else {
541 player_select_commit();
542 }
543 break;
544
545 case CLONE_BUTTON:
546 // if we're at max-pilots, don't allow another to be added
547 if (Player_select_num_pilots >= MAX_PILOTS) {
548 player_select_set_bottom_text(XSTR( "You already have the maximum # of pilots!", 379));
549
550 gamesnd_play_iface(SND_GENERAL_FAIL);
551 break;
552 }
553
554 if (Player_select_pilot >= 0) {
555 // first we have to make sure this guy is actually loaded for when we create the clone
556 if (Player == NULL) {
557 Player = &Players[0];
558 Player->flags |= PLAYER_FLAGS_STRUCTURE_IN_USE;
559 }
560
561 // attempt to read in the pilot file of the guy to be cloned
562 if ( !Pilot.load_player(Pilots[Player_select_pilot], Player) ) {
563 Error(LOCATION,"Couldn't load pilot file, bailing");
564 Player = NULL;
565 Int3();
566 }
567
568 // set the clone flag
569 Player_select_clone_flag = 1;
570
571 // create the new pilot (will be cloned with Player_select_clone_flag_set)
572 if ( !player_select_create_new_pilot() ) {
573 player_select_set_bottom_text(XSTR( "Error creating new pilot file!", 380));
574 Player_select_clone_flag = 0;
575 Player->reset();
576 Player = NULL;
577 break;
578 }
579
580 // display some text on the bottom of the dialog
581 player_select_set_bottom_text(XSTR( "Type Callsign and Press Enter", 381));
582
583 // gray out all controls in the dialog
584 player_select_set_controls(1);
585 }
586 break;
587
588 case CREATE_PILOT_BUTTON:
589 // if we're at max-pilots, don't allow another to be added
590 if(Player_select_num_pilots >= MAX_PILOTS) {
591 player_select_set_bottom_text(XSTR( "You already have the maximum # of pilots!", 379));
592
593 gamesnd_play_iface(SND_GENERAL_FAIL);
594 break;
595 }
596
597 // create a new pilot
598 if ( !player_select_create_new_pilot() ) {
599 player_select_set_bottom_text(XSTR( "Type Callsign and Press Enter", 381));
600 }
601
602 // don't clone anyone
603 Player_select_clone_flag = 0;
604
605 // display some text on the bottom of the dialog
606 player_select_set_bottom_text(XSTR( "Type Callsign and Press Enter", 381));
607
608 // gray out all controls
609 player_select_set_controls(1);
610 break;
611
612 case DELETE_BUTTON:
613 player_select_set_bottom_text("");
614
615 if (Player_select_pilot >= 0) {
616 if (Player_select_mode == PLAYER_SELECT_MODE_MULTI) {
617 popup(PF_TITLE_BIG | PF_TITLE_RED | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Disabled!\n\nMulti and single player pilots are now identical. "
618 "Deleting a multi-player pilot will also delete all single-player data for that pilot.\n\nAs a safety precaution, pilots can only be "
619 "deleted from the single-player menu.", 1610));
620 } else {
621 // display a popup requesting confirmation
622 ret = popup(PF_TITLE_BIG | PF_TITLE_RED, 2, POPUP_NO, POPUP_YES, XSTR( "Warning!\n\nAre you sure you wish to delete this pilot?", 382));
623
624 // delete the pilot
625 if (ret == 1) {
626 player_select_delete_pilot();
627 }
628 }
629 }
630 break;
631
632 case SINGLE_BUTTON:
633 player_select_set_bottom_text("");
634
635 Player_select_autoaccept = 0;
636 // switch to single player mode
637 if (Player_select_mode != PLAYER_SELECT_MODE_SINGLE) {
638 // play a little sound
639 gamesnd_play_iface(SND_USER_SELECT);
640
641 player_select_set_bottom_text(XSTR( "Single Player Mode", 376));
642
643 // reinitialize as single player mode
644 player_select_init_player_stuff(PLAYER_SELECT_MODE_SINGLE);
645 } else {
646 gamesnd_play_iface(SND_GENERAL_FAIL);
647 }
648 break;
649
650 case MULTI_BUTTON:
651 player_select_set_bottom_text("");
652
653 Player_select_autoaccept = 0;
654 if ( Networking_disabled ) {
655 game_feature_disabled_popup();
656 break;
657 }
658
659 // switch to multiplayer mode
660 if (Player_select_mode != PLAYER_SELECT_MODE_MULTI) {
661 // play a little sound
662 gamesnd_play_iface(SND_USER_SELECT);
663
664 player_select_set_bottom_text(XSTR( "Multiplayer Mode", 377));
665
666 // reinitialize as multiplayer mode
667 player_select_init_player_stuff(PLAYER_SELECT_MODE_MULTI);
668 } else {
669 gamesnd_play_iface(SND_GENERAL_FAIL);
670 }
671 break;
672 }
673 }
674
player_select_create_new_pilot()675 int player_select_create_new_pilot()
676 {
677 int idx;
678
679 // make sure we haven't reached the max
680 if (Player_select_num_pilots >= MAX_PILOTS) {
681 gamesnd_play_iface(SND_GENERAL_FAIL);
682 return 0;
683 }
684
685 int play_scroll_sound = 1;
686
687 if ( play_scroll_sound ) {
688 gamesnd_play_iface(SND_SCROLL);
689 }
690
691 idx = Player_select_num_pilots;
692
693 // move all the pilots in the list up
694 while (idx--) {
695 strcpy(Pilots[idx + 1], Pilots[idx]);
696 }
697
698 // by default, set the default netgame protocol to be VMT
699 Multi_options_g.protocol = NET_TCP;
700
701 // select the beginning of the list
702 Player_select_pilot = 0;
703 Player_select_num_pilots++;
704 Pilots[Player_select_pilot][0] = 0;
705 Player_select_list_start= 0;
706
707 // set us to be in input mode
708 player_select_set_input_mode(1);
709
710 // set the input box to have focus
711 Player_select_input_box.set_focus();
712 Player_select_input_box.set_text("");
713 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());
714
715 return 1;
716 }
717
player_select_delete_pilot()718 void player_select_delete_pilot()
719 {
720 char filename[MAX_PATH_LEN + 1];
721 int i, del_rval;
722
723 // tack on the full path and the pilot file extension
724 // build up the path name length
725 // make sure we do this based upon whether we're in single or multiplayer mode
726 strcpy_s( filename, Pilots[Player_select_pilot] );
727
728 del_rval = delete_pilot_file(filename);
729
730 if ( !del_rval ) {
731 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_BIG | PF_TITLE_RED, 1, POPUP_OK, XSTR("Error\nFailed to delete pilot file. File may be read-only.", 1599));
732 return;
733 }
734
735 // move all the players down
736 for ( i=Player_select_pilot; i<Player_select_num_pilots-1; i++ ) {
737 strcpy(Pilots[i], Pilots[i + 1]);
738 }
739
740 // correcly set the # of pilots and the currently selected pilot
741 Player_select_num_pilots--;
742 if (Player_select_pilot >= Player_select_num_pilots) {
743 Player_select_pilot = Player_select_num_pilots - 1;
744 }
745
746 }
747
748 // scroll the list of players up
player_select_scroll_list_up()749 void player_select_scroll_list_up()
750 {
751 if (Player_select_pilot == -1) {
752 return;
753 }
754
755 // change the pilot selected index and play the appropriate sound
756 if (Player_select_pilot) {
757 Player_select_pilot--;
758 gamesnd_play_iface(SND_SCROLL);
759 } else {
760 gamesnd_play_iface(SND_GENERAL_FAIL);
761 }
762
763 if (Player_select_pilot < Player_select_list_start) {
764 Player_select_list_start = Player_select_pilot;
765 }
766 }
767
768 // scroll the list of players down
player_select_scroll_list_down()769 void player_select_scroll_list_down()
770 {
771 // change the pilot selected index and play the appropriate sound
772 if ( Player_select_pilot < Player_select_num_pilots - 1 ) {
773 Player_select_pilot++;
774 gamesnd_play_iface(SND_SCROLL);
775 } else {
776 gamesnd_play_iface(SND_GENERAL_FAIL);
777 }
778
779 if ( Player_select_pilot >= (Player_select_list_start + Max_lines) ) {
780 Player_select_list_start++;
781 }
782 }
783
784 // fill in the data on the last played pilot (callsign and is_multi or not)
player_select_get_last_pilot_info()785 int player_select_get_last_pilot_info()
786 {
787 // TODO: Replace this with a function that does this properly for the new pilot code.
788
789 const char *last_player = os_config_read_string( NULL, "LastPlayer", NULL);
790
791 if (last_player == NULL) {
792 return 0;
793 } else {
794 strcpy_s(Player_select_last_pilot, last_player);
795 }
796
797 // handle changing from pre-pilot code to post-pilot code
798 if (Player_select_last_pilot[strlen(Player_select_last_pilot)-1] == 'M' || Player_select_last_pilot[strlen(Player_select_last_pilot)-1] == 'S') {
799 Player_select_last_pilot[strlen(Player_select_last_pilot)-1]='\0'; // chop off last char, M|P
800 }
801
802 if ( !Pilot.load_player(Player_select_last_pilot, Player) ) {
803 Player_select_last_is_multi = 0;
804 } else {
805 Player_select_last_is_multi = Player->player_was_multi;
806 }
807
808 return 1;
809 }
810
player_select_get_last_pilot()811 int player_select_get_last_pilot()
812 {
813 // if the player has the Cmdline_use_last_pilot command line option set, try and drop out quickly
814 if (Cmdline_use_last_pilot) {
815 int idx;
816
817 if ( !player_select_get_last_pilot_info() ) {
818 return 0;
819 }
820
821 Get_file_list_filter = player_select_pilot_file_filter;
822
823 Player_select_num_pilots = cf_get_file_list_preallocated(MAX_PILOTS, Pilots_arr, Pilots, CF_TYPE_PLAYERS, NOX("*.plr"), CF_SORT_TIME);
824
825 Player_select_pilot = -1;
826 idx = 0;
827 // pick the last player
828 for (idx=0;idx<Player_select_num_pilots;idx++) {
829 if (strcmp(Player_select_last_pilot,Pilots_arr[idx])==0) {
830 Player_select_pilot = idx;
831 break;
832 }
833 }
834
835 // set this so that we don't incorrectly create a "blank" pilot - .plr
836 // in the player_select_close() function
837 Player_select_num_pilots = 0;
838
839 // if we've actually found a valid pilot, load him up
840 if (Player_select_pilot != -1) {
841 Player = &Players[0];
842 Pilot.load_player(Pilots_arr[idx], Player);
843 Player->flags |= PLAYER_FLAGS_STRUCTURE_IN_USE;
844 return 1;
845 }
846 }
847
848 return 0;
849 }
850
player_select_init_player_stuff(int mode)851 void player_select_init_player_stuff(int mode)
852 {
853 Player_select_list_start = 0;
854
855 // set the select mode to single player for default
856 Player_select_mode = mode;
857
858 // load up the list of players based upon the Player_select_mode (single or multiplayer)
859 Get_file_list_filter = player_select_pilot_file_filter;
860
861 Player_select_num_pilots = cf_get_file_list_preallocated(MAX_PILOTS, Pilots_arr, Pilots, CF_TYPE_PLAYERS, NOX("*.plr"), CF_SORT_TIME);
862
863 // if we have a "last_player", and they're in the list, bash them to the top of the list
864 if (Player_select_last_pilot[0] != '\0') {
865 int i,j;
866 for (i = 0; i < Player_select_num_pilots; ++i) {
867 if (!stricmp(Player_select_last_pilot,Pilots[i])) {
868 break;
869 }
870 }
871 if (i != Player_select_num_pilots) {
872 for (j = i; j > 0; --j) {
873 strncpy(Pilots[j], Pilots[j-1], strlen(Pilots[j-1])+1);
874 }
875 strncpy(Pilots[0], Player_select_last_pilot, strlen(Player_select_last_pilot)+1);
876 }
877 }
878
879 Player = NULL;
880
881 // if this value is -1, it means we should set it to the num pilots count
882 if (Player_select_initial_count == -1) {
883 Player_select_initial_count = Player_select_num_pilots;
884 }
885
886 // select the first pilot if any exist, otherwise set to -1
887 if (Player_select_num_pilots == 0) {
888 Player_select_pilot = -1;
889 player_select_set_middle_text(XSTR( "Type Callsign and Press Enter", 381));
890 player_select_set_controls(1); // gray out the controls
891 player_select_create_new_pilot();
892 } else {
893 Player_select_pilot = 0;
894 }
895 }
896
player_select_draw_list()897 void player_select_draw_list()
898 {
899 int idx;
900
901 if (gr_screen.res == 1) {
902 Max_lines = 145/gr_get_font_height(); //Make the max number of lines dependent on the font height. 145 and 85 are magic numbers, based on the window size in retail.
903 } else {
904 Max_lines = 85/gr_get_font_height();
905 }
906
907 for (idx=0; idx<Max_lines; idx++) {
908 // only draw as many pilots as we have
909 if ((idx + Player_select_list_start) == Player_select_num_pilots) {
910 break;
911 }
912
913 // if the currently selected pilot is this line, draw it highlighted
914 if ( (idx + Player_select_list_start) == Player_select_pilot) {
915 // if he's the active pilot and is also the current selection, super-highlight him
916 gr_set_color_fast(&Color_text_active);
917 } else { // otherwise draw him normally
918 gr_set_color_fast(&Color_text_normal);
919 }
920 // draw the actual callsign
921 gr_printf_menu(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]);
922 }
923 }
924
player_select_process_noninput(int k)925 void player_select_process_noninput(int k)
926 {
927 int idx;
928
929 // check for pressed buttons
930 for (idx=0; idx<NUM_PLAYER_SELECT_BUTTONS; idx++) {
931 if (Player_select_buttons[gr_screen.res][idx].button.pressed()) {
932 player_select_button_pressed(idx);
933 }
934 }
935
936 // check for keypresses
937 switch (k) {
938 // quit the game entirely
939 case KEY_ESC:
940 gameseq_post_event(GS_EVENT_QUIT_GAME);
941 break;
942
943 case KEY_ENTER | KEY_CTRLED:
944 player_select_button_pressed(ACCEPT_BUTTON);
945 break;
946
947 // delete the currently highlighted pilot
948 case KEY_DELETE:
949 if (Player_select_pilot >= 0) {
950 int ret;
951
952 if (Player_select_mode == PLAYER_SELECT_MODE_MULTI) {
953 popup(PF_TITLE_BIG | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Pilots can only be deleted from the single player menu!", 1611));
954 } else {
955 // display a popup requesting confirmation
956 ret = popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON,2,POPUP_NO,POPUP_YES,XSTR( "Are you sure you want to delete this pilot?", 383));
957
958 // delete the pilot
959 if (ret == 1) {
960 player_select_delete_pilot();
961 }
962 }
963 }
964 break;
965 }
966
967 // check to see if the user has clicked on the "list region" button
968 // and change the selected pilot appropriately
969 if (Player_select_list_region.pressed()) {
970 int click_y;
971 // get the mouse position
972 Player_select_list_region.get_mouse_pos(NULL, &click_y);
973
974 // determine what index to select
975 //idx = (click_y+5) / 10;
976 idx = click_y / gr_get_font_height();
977
978
979 // if he selected a valid item
980 if ( ((idx + Player_select_list_start) < Player_select_num_pilots) && (idx >= 0) ) {
981 Player_select_pilot = idx + Player_select_list_start;
982 }
983 }
984
985 // if the player has double clicked on a valid pilot, choose it and hit the accept button
986 if (Player_select_list_region.double_clicked()) {
987 if ((Player_select_pilot >= 0) && (Player_select_pilot < Player_select_num_pilots)) {
988 player_select_button_pressed(ACCEPT_BUTTON);
989 }
990 }
991 }
992
player_select_process_input(int k)993 void player_select_process_input(int k)
994 {
995 char buf[CALLSIGN_LEN + 1];
996 int idx,z;
997
998 // if the player is in the process of typing in a new pilot name...
999 switch (k) {
1000 // cancel create pilot
1001 case KEY_ESC:
1002 player_select_cancel_create();
1003 break;
1004
1005 // accept a new pilot name
1006 case KEY_ENTER:
1007 Player_select_input_box.get_text(buf);
1008 drop_white_space(buf);
1009 z = 0;
1010 if (!isalpha(*buf)) {
1011 z = 1;
1012 } else {
1013 for (idx=1; buf[idx]; idx++) {
1014 if (!isalpha(buf[idx]) && !isdigit(buf[idx]) && !strchr(VALID_PILOT_CHARS, buf[idx])) {
1015 z = 1;
1016 break;
1017 }
1018 }
1019 }
1020
1021 for (idx=1; idx<Player_select_num_pilots; idx++) {
1022 if (!stricmp(buf, Pilots[idx])) {
1023 // verify if it is ok to overwrite the file
1024 if (pilot_verify_overwrite() == 1) {
1025 // delete the pilot and select the beginning of the list
1026 Player_select_pilot = idx;
1027 player_select_delete_pilot();
1028 Player_select_pilot = 0;
1029 idx = Player_select_num_pilots;
1030 z = 0;
1031
1032 } else
1033 z = 1;
1034
1035 break;
1036 }
1037 }
1038
1039 if (!*buf || (idx < Player_select_num_pilots)) {
1040 z = 1;
1041 }
1042
1043 if (z) {
1044 gamesnd_play_iface(SND_GENERAL_FAIL);
1045 break;
1046 }
1047
1048 // Create the new pilot, and write out his file
1049 strcpy(Pilots[0], buf);
1050
1051 // if this is the first guy, we should set the Player struct
1052 if (Player == NULL) {
1053 Player = &Players[0];
1054 Player->reset();
1055 Player->flags |= PLAYER_FLAGS_STRUCTURE_IN_USE;
1056 }
1057
1058 strcpy_s(Player->callsign, buf);
1059 init_new_pilot(Player, !Player_select_clone_flag);
1060
1061 // set him as being a multiplayer pilot if we're in the correct mode
1062 if (Player_select_mode == PLAYER_SELECT_MODE_MULTI) {
1063 Player->flags |= PLAYER_FLAGS_IS_MULTI;
1064 Player->stats.flags |= STATS_FLAG_MULTIPLAYER;
1065 }
1066
1067 // create his pilot file
1068 Pilot.save_player(Player);
1069
1070 // unset the player
1071 Player->reset();
1072 Player = NULL;
1073
1074 // make this guy the selected pilot and put him first on the list
1075 Player_select_pilot = 0;
1076
1077 // unset the input mode
1078 player_select_set_input_mode(0);
1079
1080 // clear any pending bottom text
1081 player_select_set_bottom_text("");
1082
1083 // clear any pending middle text
1084 player_select_set_middle_text("");
1085
1086 // ungray all the controls
1087 player_select_set_controls(0);
1088
1089 // evaluate whether or not this is the very first pilot
1090 player_select_eval_very_first_pilot();
1091 break;
1092
1093 case 0:
1094 break;
1095
1096 // always kill middle text when a char is pressed in input mode
1097 default:
1098 player_select_set_middle_text("");
1099 break;
1100 }
1101 }
1102
1103 // draw copyright message on the bottom on the screen
player_select_display_copyright()1104 void player_select_display_copyright()
1105 {
1106 int sx, sy, w;
1107 char Copyright_msg1[256], Copyright_msg2[256];
1108
1109 // strcpy_s(Copyright_msg1, XSTR("Descent: FreeSpace - The Great War, Copyright c 1998, Volition, Inc.", -1));
1110 gr_set_color_fast(&Color_white);
1111
1112 // sprintf(Copyright_msg1, NOX("FreeSpace 2"));
1113 get_version_string(Copyright_msg1, sizeof(Copyright_msg1));
1114 if (Lcl_gr) {
1115 sprintf(Copyright_msg2, XSTR("Copyright %c 1999, Volition, Inc. All rights reserved.", 385), '\xA8');
1116 } else {
1117 sprintf(Copyright_msg2, XSTR("Copyright %c 1999, Volition, Inc. All rights reserved.", 385), '\x83');
1118 }
1119
1120 gr_get_string_size(&w, NULL, Copyright_msg1);
1121 sx = fl2i((gr_screen.max_w_unscaled / 2) - w/2.0f + 0.5f);
1122 sy = (gr_screen.max_h_unscaled - 2) - 2*gr_get_font_height();
1123 gr_string(sx, sy, Copyright_msg1, GR_RESIZE_MENU);
1124
1125 gr_get_string_size(&w, NULL, Copyright_msg2);
1126 sx = fl2i((gr_screen.max_w_unscaled / 2) - w/2.0f + 0.5f);
1127 sy = (gr_screen.max_h_unscaled - 2) - gr_get_font_height();
1128
1129 gr_string(sx, sy, Copyright_msg2, GR_RESIZE_MENU);
1130 }
1131
player_select_display_all_text()1132 void player_select_display_all_text()
1133 {
1134 int w, h;
1135
1136 // only draw if we actually have a valid string
1137 if (strlen(Player_select_bottom_text)) {
1138 gr_get_string_size(&w, &h, Player_select_bottom_text);
1139
1140 w = (gr_screen.max_w_unscaled - w) / 2;
1141 gr_set_color_fast(&Color_bright_white);
1142 gr_printf_menu(w, Player_select_bottom_text_y[gr_screen.res], Player_select_bottom_text);
1143 }
1144
1145 // only draw if we actually have a valid string
1146 if (strlen(Player_select_middle_text)) {
1147 gr_get_string_size(&w, &h, Player_select_middle_text);
1148
1149 w = (gr_screen.max_w_unscaled - w) / 2;
1150 gr_set_color_fast(&Color_bright_white);
1151 gr_printf_menu(w, Player_select_middle_text_y[gr_screen.res], Player_select_middle_text);
1152 }
1153 }
1154
player_select_pilot_file_filter(const char * filename)1155 int player_select_pilot_file_filter(const char *filename)
1156 {
1157 return (int)Pilot.verify(filename);
1158 }
1159
player_select_set_bottom_text(const char * txt)1160 void player_select_set_bottom_text(const char *txt)
1161 {
1162 if (txt) {
1163 strncpy(Player_select_bottom_text, txt, 149);
1164 }
1165 }
1166
player_select_set_middle_text(const char * txt)1167 void player_select_set_middle_text(const char *txt)
1168 {
1169 if (txt) {
1170 strncpy(Player_select_middle_text, txt, 149);
1171 }
1172 }
1173
player_select_eval_very_first_pilot()1174 void player_select_eval_very_first_pilot()
1175 {
1176 // never bring up the initial main hall help overlay
1177 // Player_select_very_first_pilot = 0;
1178
1179 // if we already have this flag set, check to see if our callsigns match
1180 if(Player_select_very_first_pilot) {
1181 // if the callsign has changed, unset the flag
1182 if(strcmp(Player_select_very_first_pilot_callsign,Pilots[Player_select_pilot])){
1183 Player_select_very_first_pilot = 0;
1184 }
1185 } else { // otherwise check to see if there is only 1 pilot
1186 if((Player_select_num_pilots == 1) && (Player_select_initial_count == 0)){
1187 // set up the data
1188 Player_select_very_first_pilot = 1;
1189 strcpy_s(Player_select_very_first_pilot_callsign,Pilots[Player_select_pilot]);
1190 }
1191 }
1192 }
1193
player_select_commit()1194 void player_select_commit()
1195 {
1196 // if we've gotten to this point, we should have ensured this was the case
1197 Assert(Player_select_num_pilots > 0);
1198
1199 gameseq_post_event(GS_EVENT_MAIN_MENU);
1200 gamesnd_play_iface(SND_COMMIT_PRESSED);
1201
1202 // evaluate if this is the _very_ first pilot
1203 player_select_eval_very_first_pilot();
1204 }
1205
player_select_cancel_create()1206 void player_select_cancel_create()
1207 {
1208 int idx;
1209
1210 Player_select_num_pilots--;
1211
1212 // make sure we correct the Selected_pilot index to account for the cancelled action
1213 if (Player_select_num_pilots == 0) {
1214 Player_select_pilot = -1;
1215 }
1216
1217 // move all pilots down
1218 for (idx=0; idx<Player_select_num_pilots; idx++) {
1219 strcpy(Pilots[idx], Pilots[idx + 1]);
1220 }
1221
1222 // unset the input mode
1223 player_select_set_input_mode(0);
1224
1225 // clear any bottom text
1226 player_select_set_bottom_text("");
1227
1228 // clear any middle text
1229 player_select_set_middle_text("");
1230
1231 // ungray all controls
1232 player_select_set_controls(0);
1233
1234 // disable the autoaccept
1235 Player_select_autoaccept = 0;
1236 }
1237
1238 DCF(bastion,"Sets the player to be on the bastion (or any other main hall)")
1239 {
1240 if(gameseq_get_state() != GS_STATE_INITIAL_PLAYER_SELECT) {
1241 dc_printf("This command can only be run in the initial player select screen.\n");
1242 return;
1243 }
1244
1245 if (Dc_command) {
1246 dc_get_arg(ARG_INT | ARG_NONE);
1247
1248 if (Dc_arg_type & ARG_INT) {
1249 int idx = Dc_arg_int;
1250
1251 Assert(Main_hall_defines.at(gr_screen.res).size() < INT_MAX);
1252 if (idx < 0 || idx >= (int) Main_hall_defines.at(gr_screen.res).size()) {
1253 dc_printf("Main hall index out of range\n");
1254 } else {
1255 main_hall_get_name(Player_select_force_main_hall, idx);
1256 dc_printf("Player is now on main hall '%d'\n", Player_select_force_main_hall.c_str());
1257 }
1258 } else {
1259 Player_select_force_main_hall = "1";
1260 dc_printf("Player is now on the Bastion... hopefully\n");
1261 }
1262 Dc_status = 0;
1263 }
1264
1265 if (Dc_help) {
1266 dc_printf("Usage: bastion [index]\n");
1267 dc_printf(" [index] -- optional main hall index; if not supplied, defaults to 1\n");
1268 Dc_status = 0;
1269 }
1270
1271 if (Dc_status) {
1272 dc_printf("There is no current main hall, as the player has not been selected yet!\n");
1273 }
1274 }
1275
1276 #define MAX_PLAYER_TIPS 40
1277
1278 char *Player_tips[MAX_PLAYER_TIPS];
1279 int Num_player_tips;
1280 int Player_tips_shown = 0;
1281
1282 // tooltips
player_tips_init()1283 void player_tips_init()
1284 {
1285 int rval;
1286
1287 Num_player_tips = 0;
1288
1289 // begin external localization stuff
1290 lcl_ext_open();
1291
1292 if ((rval = setjmp(parse_abort)) != 0) {
1293 mprintf(("TABLES: Unable to parse '%s'! Error code = %i.\n", "tips.tbl", rval));
1294 lcl_ext_close();
1295 return;
1296 }
1297
1298 read_file_text("tips.tbl", CF_TYPE_TABLES);
1299 reset_parse();
1300
1301 while(!optional_string("#end")) {
1302 required_string("+Tip:");
1303
1304 if(Num_player_tips >= MAX_PLAYER_TIPS) {
1305 break;
1306 }
1307 Player_tips[Num_player_tips++] = stuff_and_malloc_string(F_NAME, NULL);
1308 }
1309
1310 // stop externalizing, homey
1311 lcl_ext_close();
1312 }
1313
1314 // close out player tips - *only call from game_shutdown()*
player_tips_close()1315 void player_tips_close()
1316 {
1317 int i;
1318
1319 for (i=0; i<MAX_PLAYER_TIPS; i++) {
1320 if (Player_tips[i] != NULL) {
1321 vm_free(Player_tips[i]);
1322 Player_tips[i] = NULL;
1323 }
1324 }
1325 }
1326
player_tips_popup()1327 void player_tips_popup()
1328 {
1329 int tip, ret;
1330
1331 // player has disabled tips
1332 if ( (Player != NULL) && !Player->tips ) {
1333 return;
1334 }
1335 // only show tips once per instance of FreeSpace
1336 if(Player_tips_shown == 1) {
1337 return;
1338 }
1339 Player_tips_shown = 1;
1340
1341 // randomly pick one
1342 tip = (int)frand_range(0.0f, (float)Num_player_tips - 1.0f);
1343
1344 char all_txt[2048];
1345
1346 do {
1347 sprintf(all_txt, XSTR("NEW USER TIP\n\n%s", 1565), Player_tips[tip]);
1348 ret = popup(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);
1349
1350 // now what?
1351 switch(ret){
1352 // next
1353 case 1:
1354 if(tip >= Num_player_tips - 1) {
1355 tip = 0;
1356 } else {
1357 tip++;
1358 }
1359 break;
1360
1361 // don't show me this again
1362 case 2:
1363 ret = 0;
1364 Player->tips = 0;
1365 Pilot.save_player(Player);
1366 Pilot.save_savefile();
1367 break;
1368 }
1369 } while(ret > 0);
1370 }
1371