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