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 
12 
13 #include <climits>		// this is need even when not building debug!!
14 #include <type_traits>
15 
16 #include "anim/animplay.h"
17 #include "cmdline/cmdline.h"
18 #include "cutscene/cutscenes.h"
19 #include "cutscene/movie.h"
20 #include "gamehelp/contexthelp.h"
21 #include "gamesequence/gamesequence.h"
22 #include "gamesnd/eventmusic.h"
23 #include "gamesnd/gamesnd.h"
24 #include "globalincs/linklist.h"
25 #include "graphics/2d.h"
26 #include "graphics/matrix.h"
27 #include "graphics/shadows.h"
28 #include "hud/hudwingmanstatus.h"
29 #include "io/key.h"
30 #include "io/mouse.h"
31 #include "io/timer.h"
32 #include "lighting/lighting.h"
33 #include "missionui/chatbox.h"
34 #include "missionui/missionbrief.h"
35 #include "missionui/missionscreencommon.h"
36 #include "missionui/missionshipchoice.h"
37 #include "missionui/missionweaponchoice.h"
38 #include "network/multi.h"
39 #include "network/multi_endgame.h"
40 #include "network/multimsgs.h"
41 #include "network/multiteamselect.h"
42 #include "network/multiutil.h"
43 #include "parse/sexp.h"
44 #include "popup/popup.h"
45 #include "render/3d.h"
46 #include "render/batching.h"
47 #include "scripting/scripting.h"
48 #include "ship/ship.h"
49 #include "ui/uidefs.h"
50 #include "weapon/weapon.h"
51 
52 //////////////////////////////////////////////////////////////////
53 // Game Globals
54 //////////////////////////////////////////////////////////////////
55 
56 int Common_select_inited = 0;
57 
58 // Dependent on when mouse button goes up
59 int Drop_icon_mflag, Drop_on_wing_mflag, Brief_mouse_up_flag;
60 
61 int Mouse_down_last_frame = 0;
62 
63 // Timers used to flash buttons after timeouts
64 #define MSC_FLASH_AFTER_TIME	60000		//	time before flashing a button
65 #define MSC_FLASH_INTERVAL		200		// time between flashes
66 int Flash_timer;								//	timestamp used to start flashing
67 int Flash_toggle;								// timestamp used to toggle flashing
68 int Flash_bright;								// state of button to flash
69 
70 //////////////////////////////////////////////////////////////////
71 // Global to modulde
72 //////////////////////////////////////////////////////////////////
73 int Current_screen;
74 int Next_screen;
75 static int InterfacePaletteBitmap = -1; // PCX file that holds the interface palette
76 color Icon_colors[NUM_ICON_FRAMES];
77 shader Icon_shaders[NUM_ICON_FRAMES];
78 
79 loadout_data Player_loadout;	// what the ship and weapon loadout is... used since we want to use the
80 								// same loadout if the mission is played again
81 
82 //wss_unit	Wss_slots[MAX_WSS_SLOTS];				// slot data struct
83 //int		Wl_pool[MAX_WEAPON_TYPES];				// weapon pool
84 //int		Ss_pool[MAX_SHIP_CLASSES];				// ship pool
85 //int		Wss_num_wings;								// number of player wings
86 
87 wss_unit	Wss_slots_teams[MAX_TVT_TEAMS][MAX_WSS_SLOTS];
88 int		Wl_pool_teams[MAX_TVT_TEAMS][MAX_WEAPON_TYPES];
89 int		Ss_pool_teams[MAX_TVT_TEAMS][MAX_SHIP_CLASSES];
90 int		Wss_num_wings_teams[MAX_TVT_TEAMS];
91 
92 wss_unit	*Wss_slots = NULL;
93 int		*Wl_pool = NULL;
94 int		*Ss_pool = NULL;
95 int		Wss_num_wings;
96 
97 //////////////////////////////////////////////////////////////////
98 // Externs
99 //////////////////////////////////////////////////////////////////
100 extern void ss_set_team_pointers(int team);
101 extern void ss_reset_team_pointers();
102 extern void wl_set_team_pointers(int team);
103 extern void wl_reset_team_pointers();
104 extern int anim_timer_start;
105 extern void ss_reset_selected_ship();
106 
107 //////////////////////////////////////////////////////////////////
108 // UI
109 //////////////////////////////////////////////////////////////////
110 UI_WINDOW	*Active_ui_window;
111 
112 brief_common_buttons Common_buttons[3][GR_NUM_RESOLUTIONS][NUM_COMMON_BUTTONS] = {
113 	{	// UGH
114 		{ // GR_640
115 			brief_common_buttons("CB_00",			7,		3,		37,	7,		0),
116 			brief_common_buttons("CB_01",			7,		19,	37,	23,	1),
117 			brief_common_buttons("CB_02",			7,		35,	37,	39,	2),
118 			brief_common_buttons("CB_05",			571,	425,	572,	413,	5),
119 			brief_common_buttons("CB_06",			533,	425,	500,	440,	6),
120 			brief_common_buttons("CB_07",			533,	455,	479,	464,	7),
121 		},
122 		{ // GR_1024
123 			brief_common_buttons("2_CB_00",		12,	5,		59,	12,	0),
124 			brief_common_buttons("2_CB_01",		12,	31,	59,	37,	1),
125 			brief_common_buttons("2_CB_02",		12,	56,	59,	62,	2),
126 			brief_common_buttons("2_CB_05",		914,	681,	937,	671,	5),
127 			brief_common_buttons("2_CB_06",		854,	681,	822,	704,	6),
128 			brief_common_buttons("2_CB_07",		854,	724,	800,	743,	7),
129 		}
130 	},
131 	{	// UGH
132 		{ // GR_640
133 			brief_common_buttons("CB_00",			7,		3,		37,	7,		0),
134 			brief_common_buttons("CB_01",			7,		19,	37,	23,	1),
135 			brief_common_buttons("CB_02",			7,		35,	37,	39,	2),
136 			brief_common_buttons("CB_05",			571,	425,	572,	413,	5),
137 			brief_common_buttons("CB_06",			533,	425,	500,	440,	6),
138 			brief_common_buttons("CB_07",			533,	455,	479,	464,	7),
139 		},
140 		{ // GR_1024
141 			brief_common_buttons("2_CB_00",		12,	5,		59,	12,	0),
142 			brief_common_buttons("2_CB_01",		12,	31,	59,	37,	1),
143 			brief_common_buttons("2_CB_02",		12,	56,	59,	62,	2),
144 			brief_common_buttons("2_CB_05",		914,	681,	937,	671,	5),
145 			brief_common_buttons("2_CB_06",		854,	681,	822,	704,	6),
146 			brief_common_buttons("2_CB_07",		854,	724,	800,	743,	7),
147 		}
148 	},
149 	{	// UGH
150 		{ // GR_640
151 			brief_common_buttons("CB_00",			7,		3,		37,	7,		0),
152 			brief_common_buttons("CB_01",			7,		19,	37,	23,	1),
153 			brief_common_buttons("CB_02",			7,		35,	37,	39,	2),
154 			brief_common_buttons("CB_05",			571,	425,	572,	413,	5),
155 			brief_common_buttons("CB_06",			533,	425,	500,	440,	6),
156 			brief_common_buttons("CB_07",			533,	455,	479,	464,	7),
157 		},
158 		{ // GR_1024
159 			brief_common_buttons("2_CB_00",		12,	5,		59,	12,	0),
160 			brief_common_buttons("2_CB_01",		12,	31,	59,	37,	1),
161 			brief_common_buttons("2_CB_02",		12,	56,	59,	62,	2),
162 			brief_common_buttons("2_CB_05",		914,	681,	937,	671,	5),
163 			brief_common_buttons("2_CB_06",		854,	681,	822,	704,	6),
164 			brief_common_buttons("2_CB_07",		854,	724,	800,	743,	7),
165 		}
166 	}
167 };
168 
169 #define COMMON_BRIEFING_BUTTON					0
170 #define COMMON_SS_BUTTON							1
171 #define COMMON_WEAPON_BUTTON						2
172 #define COMMON_COMMIT_BUTTON						3
173 #define COMMON_HELP_BUTTON							4
174 #define COMMON_OPTIONS_BUTTON						5
175 
176 int Background_playing;			// Flag to indicate background animation is playing
177 static anim *Background_anim;	// Ids for the anim data that is loaded
178 
179 // value for which Team_data entry to use
180 int	Common_team;
181 
182 // Ids for the instance of the anim that is playing
183 //static anim_instance *Background_anim_instance;
184 
185 int Wing_slot_empty_bitmap;
186 int Wing_slot_disabled_bitmap;
187 
188 // prototypes
189 int wss_slots_all_empty();
190 
191 // Display the no ships selected error
common_show_no_ship_error()192 void common_show_no_ship_error()
193 {
194 	popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR( "At least one ship must be selected before proceeding to weapons loadout", 460));
195 }
196 
197 // Check the status of the buttons common to the loadout screens
common_check_buttons()198 void common_check_buttons()
199 {
200 	int			i;
201 	UI_BUTTON	*b;
202 
203 	for ( i = 0; i < NUM_COMMON_BUTTONS; i++ ) {
204 		b = &Common_buttons[Current_screen-1][gr_screen.res][i].button;
205 		if ( b->pressed() ) {
206 
207 			common_button_do(i);
208 		}
209 	}
210 
211 /*
212 	// AL 11-23-97: let a joystick button press commit
213 	if ( joy_down_count(0) || joy_down_count(1) ) {
214 		Commit_pressed = 1;
215 	}
216 */
217 
218 }
219 
220 // -------------------------------------------------------------------
221 // common_redraw_pressed_buttons()
222 //
223 // Redraw any common buttons that are pressed down.  This function is needed
224 // since we sometimes need to draw pressed buttons last to ensure the entire
225 // button gets drawn (and not overlapped by other buttons)
226 //
common_redraw_pressed_buttons()227 void common_redraw_pressed_buttons()
228 {
229 	int			i;
230 	UI_BUTTON	*b;
231 
232 	for ( i = 0; i < NUM_COMMON_BUTTONS; i++ ) {
233 		b = &Common_buttons[Current_screen-1][gr_screen.res][i].button;
234 		if ( b->button_down() ) {
235 			b->draw_forced(2);
236 		}
237 	}
238 }
239 
common_buttons_maybe_reload(UI_WINDOW *)240 void common_buttons_maybe_reload(UI_WINDOW * /*ui_window*/)
241 {
242 	UI_BUTTON	*b;
243 	int			i;
244 
245 	for ( i = 0; i < NUM_COMMON_BUTTONS; i++ ) {
246 		b = &Common_buttons[Current_screen-1][gr_screen.res][i].button;
247 		b->set_bmaps(Common_buttons[Current_screen-1][gr_screen.res][i].filename);
248 	}
249 }
250 
common_buttons_init(UI_WINDOW * ui_window)251 void common_buttons_init(UI_WINDOW *ui_window)
252 {
253 	UI_BUTTON	*b;
254 	int			i;
255 
256 	for ( i = 0; i < NUM_COMMON_BUTTONS; i++ ) {
257 		b = &Common_buttons[Current_screen-1][gr_screen.res][i].button;
258 		b->create( ui_window, "", Common_buttons[Current_screen-1][gr_screen.res][i].x, Common_buttons[Current_screen-1][gr_screen.res][i].y,  60, 30, 0, 1);
259 		// set up callback for when a mouse first goes over a button
260 		b->set_highlight_action( common_play_highlight_sound );
261 		b->set_bmaps(Common_buttons[Current_screen-1][gr_screen.res][i].filename);
262 		b->link_hotspot(Common_buttons[Current_screen-1][gr_screen.res][i].hotspot);
263 	}
264 
265 	// add some text
266 	ui_window->add_XSTR("Briefing", 1504, Common_buttons[Current_screen-1][gr_screen.res][COMMON_BRIEFING_BUTTON].xt, Common_buttons[Current_screen-1][gr_screen.res][COMMON_BRIEFING_BUTTON].yt, &Common_buttons[Current_screen-1][gr_screen.res][COMMON_BRIEFING_BUTTON].button, UI_XSTR_COLOR_GREEN);
267 	ui_window->add_XSTR("Ship Selection", 1067, Common_buttons[Current_screen-1][gr_screen.res][COMMON_SS_BUTTON].xt, Common_buttons[Current_screen-1][gr_screen.res][COMMON_SS_BUTTON].yt, &Common_buttons[Current_screen-1][gr_screen.res][COMMON_SS_BUTTON].button, UI_XSTR_COLOR_GREEN);
268 	ui_window->add_XSTR("Weapon Loadout", 1068, Common_buttons[Current_screen-1][gr_screen.res][COMMON_WEAPON_BUTTON].xt, Common_buttons[Current_screen-1][gr_screen.res][COMMON_WEAPON_BUTTON].yt, &Common_buttons[Current_screen-1][gr_screen.res][COMMON_WEAPON_BUTTON].button, UI_XSTR_COLOR_GREEN);
269 	ui_window->add_XSTR("Commit", 1062, Common_buttons[Current_screen-1][gr_screen.res][COMMON_COMMIT_BUTTON].xt, Common_buttons[Current_screen-1][gr_screen.res][COMMON_COMMIT_BUTTON].yt, &Common_buttons[Current_screen-1][gr_screen.res][COMMON_COMMIT_BUTTON].button, UI_XSTR_COLOR_PINK);
270 	ui_window->add_XSTR("Help", 928, Common_buttons[Current_screen-1][gr_screen.res][COMMON_HELP_BUTTON].xt, Common_buttons[Current_screen-1][gr_screen.res][COMMON_HELP_BUTTON].yt, &Common_buttons[Current_screen-1][gr_screen.res][COMMON_HELP_BUTTON].button, UI_XSTR_COLOR_GREEN);
271 	ui_window->add_XSTR("Options", 1036, Common_buttons[Current_screen-1][gr_screen.res][COMMON_OPTIONS_BUTTON].xt, Common_buttons[Current_screen-1][gr_screen.res][COMMON_OPTIONS_BUTTON].yt, &Common_buttons[Current_screen-1][gr_screen.res][COMMON_OPTIONS_BUTTON].button, UI_XSTR_COLOR_GREEN);
272 
273 	common_reset_buttons();
274 
275 	Common_buttons[Current_screen-1][gr_screen.res][COMMON_COMMIT_BUTTON].button.set_hotkey(KEY_CTRLED+KEY_ENTER);
276 	Common_buttons[Current_screen-1][gr_screen.res][COMMON_HELP_BUTTON].button.set_hotkey(KEY_F1);
277 	Common_buttons[Current_screen-1][gr_screen.res][COMMON_OPTIONS_BUTTON].button.set_hotkey(KEY_F2);
278 
279 	// for scramble or training missions, disable the ship/weapon selection regions
280 	if ( brief_only_allow_briefing() ) {
281 		Common_buttons[Current_screen-1][gr_screen.res][COMMON_SS_REGION].button.disable();
282 		Common_buttons[Current_screen-1][gr_screen.res][COMMON_WEAPON_REGION].button.disable();
283 	}
284 }
285 
286 // Try to load background bitmaps as appropriate
mission_ui_background_load(const char * custom_background,const char * single_background,const char * multi_background)287 int mission_ui_background_load(const char *custom_background, const char *single_background, const char *multi_background)
288 {
289 	int background_bitmap = -1;
290 
291 	if (custom_background && (*custom_background != '\0'))
292 	{
293 		background_bitmap = bm_load(custom_background);
294 		if (background_bitmap < 0)
295 			mprintf(("Failed to load custom background bitmap %s!\n", custom_background));
296 	}
297 
298 	// if special background failed to load, or if no special background was supplied, load the standard bitmap
299 	if (background_bitmap < 0)
300 	{
301 		if (multi_background && (Game_mode & GM_MULTIPLAYER))
302 			background_bitmap = bm_load(multi_background);
303 		else
304 			background_bitmap = bm_load(single_background);
305 	}
306 
307 	// return what we've got
308 	return background_bitmap;
309 }
310 
set_active_ui(UI_WINDOW * ui_window)311 void set_active_ui(UI_WINDOW *ui_window)
312 {
313 	Active_ui_window = ui_window;
314 }
315 
common_music_get_filename(int score_index)316 SCP_string common_music_get_filename(int score_index)
317 {
318 	if (Cmdline_freespace_no_music) {
319 		return SCP_string();
320 	}
321 
322 	Assertion(score_index >= 0 && score_index < NUM_SCORES, "Invalid score index %d.", score_index);
323 
324 	if (Mission_music[score_index] < 0) {
325 		if (Num_music_files > 0) {
326 			Mission_music[score_index] = 0;
327 			nprintf(("Sound",
328 				"No briefing music is selected, so play first briefing track: %s\n",
329 				Spooled_music[Mission_music[score_index]].name));
330 		} else {
331 			return SCP_string();
332 		}
333 	}
334 
335 	return Spooled_music[Mission_music[score_index]].filename;
336 }
337 
common_music_init(int score_index)338 void common_music_init(int score_index)
339 {
340 	const auto file_name = common_music_get_filename(score_index);
341 
342 	if (file_name.empty()) {
343 		return;
344 	}
345 
346 	briefing_load_music(file_name.c_str());
347 	// Use this id to trigger the start of music playing on the briefing screen
348 	Briefing_music_begin_timestamp = timestamp(BRIEFING_MUSIC_DELAY);
349 }
350 
common_music_do()351 void common_music_do()
352 {
353 	if ( Cmdline_freespace_no_music ) {
354 		return;
355 	}
356 
357 	// Use this id to trigger the start of music playing on the briefing screen
358 	if ( timestamp_elapsed( Briefing_music_begin_timestamp) ) {
359 		Briefing_music_begin_timestamp = 0;
360 		briefing_start_music();
361 	}
362 }
363 
common_music_close()364 void common_music_close()
365 {
366 	if ( Cmdline_freespace_no_music ) {
367 		return;
368 	}
369 
370 	if ( Num_music_files <= 0 )
371 		return;
372 
373 	briefing_stop_music(true);
374 }
375 
common_num_cutscenes_valid(int movie_type)376 int common_num_cutscenes_valid(int movie_type)
377 {
378 	int num_valid_cutscenes = 0;
379 
380 	for (uint i = 0; i < The_mission.cutscenes.size(); i++) {
381 		if (movie_type == The_mission.cutscenes[i].type) {
382 			if (!eval_sexp( The_mission.cutscenes[i].formula )) {
383 				continue;
384 			}
385 
386 			num_valid_cutscenes++;
387 		}
388 	}
389 
390 	return num_valid_cutscenes;
391 }
392 
common_maybe_play_cutscene(int movie_type,bool restart_music,int music)393 void common_maybe_play_cutscene(int movie_type, bool restart_music, int music)
394 {
395 	bool music_off = false;
396 
397 	for (uint i = 0; i < The_mission.cutscenes.size(); i++) {
398 		if (movie_type == The_mission.cutscenes[i].type) {
399 			if (!eval_sexp( The_mission.cutscenes[i].formula )) {
400 				continue;
401 			}
402 
403 			if ( strlen(The_mission.cutscenes[i].filename) ) {
404 				common_music_close();
405 				music_off = true;
406 				movie::play(The_mission.cutscenes[i].filename);	//Play the movie!
407 				cutscene_mark_viewable( The_mission.cutscenes[i].filename );
408 			}
409 		}
410 	}
411 
412 	if (music_off && restart_music) {
413 		common_music_init(music);
414 	}
415 }
416 
417 // function that sets the current palette to the interface palette.  This function
418 // needs to be followed by common_free_interface_palette() to restore the game palette.
common_set_interface_palette(const char * filename)419 void common_set_interface_palette(const char *filename)
420 {
421 	static char buf[MAX_FILENAME_LEN + 1] = {0};
422 
423 	if (!filename)
424 		filename = NOX("palette01");
425 
426 	Assert(strlen(filename) <= MAX_FILENAME_LEN);
427 	if ( (InterfacePaletteBitmap != -1) && !stricmp(filename, buf) )
428 		return;  // already set to this palette
429 
430 	strcpy_s(buf, filename);
431 
432 	// unload the interface bitmap from memory
433 	if (InterfacePaletteBitmap != -1) {
434 		bm_release(InterfacePaletteBitmap);
435 		InterfacePaletteBitmap = -1;
436 	}
437 
438 	// ugh - we don't need this anymore
439 	/*
440 	InterfacePaletteBitmap = bm_load(filename);
441 	if (InterfacePaletteBitmap < 0) {
442 		Error(LOCATION, "Could not load in \"%s\"!", filename);
443 	}
444 	*/
445 }
446 
447 // release the interface palette .pcx file, and restore the game palette
common_free_interface_palette()448 void common_free_interface_palette()
449 {
450 	// unload the interface bitmap from memory
451 	if (InterfacePaletteBitmap != -1) {
452 		bm_release(InterfacePaletteBitmap);
453 		InterfacePaletteBitmap = -1;
454 	}
455 }
456 
457 // Init timers used for flashing buttons
common_flash_button_init()458 void common_flash_button_init()
459 {
460 	Flash_timer = timestamp(MSC_FLASH_AFTER_TIME);
461 	Flash_toggle = 1;
462 	Flash_bright = 0;
463 }
464 
465 // determine if we should draw a button as bright
common_flash_bright()466 int common_flash_bright()
467 {
468 	if ( timestamp_elapsed(Flash_timer) ) {
469 		if ( timestamp_elapsed(Flash_toggle) ) {
470 			Flash_toggle = timestamp(MSC_FLASH_INTERVAL);
471 			Flash_bright ^= 1;
472 		}
473 	}
474 
475 	return Flash_bright;
476 }
477 
478 // set the necessary pointers
common_set_team_pointers(int team)479 void common_set_team_pointers(int team)
480 {
481 	Assert( (team >= 0) && (team < MAX_TVT_TEAMS) );
482 
483 	Wss_slots = Wss_slots_teams[team];
484 	Ss_pool = Ss_pool_teams[team];
485 	Wl_pool = Wl_pool_teams[team];
486 
487 	ss_set_team_pointers(team);
488 	wl_set_team_pointers(team);
489 }
490 
491 // reset the necessary pointers to defaults
common_reset_team_pointers()492 void common_reset_team_pointers()
493 {
494 	ss_reset_team_pointers();
495 	wl_reset_team_pointers();
496 
497 	// these are done last so that we can make use of the Assert()'s in the above
498 	// functions to make sure the screens are exited and this is safe
499 	Wss_slots = NULL;
500 	Ss_pool = NULL;
501 	Wl_pool = NULL;
502 }
503 
504 // common_select_init() will load in animations and bitmaps that are common to the
505 // briefing/ship select/weapon select screens.  The global Common_select_inited is set
506 // after this function is called once, and is only cleared when common_select_close()
507 // is called.  This prevents multiple loadings of animations/bitmaps.
508 //
509 // This function also sets the palette based on the file palette01.pcx
common_select_init()510 void common_select_init()
511 {
512 	if ( Common_select_inited ) {
513 		nprintf(("Alan","common_select_init() returning without doing anything\n"));
514 		return;
515 	}
516 
517 	nprintf(("Alan","entering common_select_init()\n"));
518 
519 	// No anims are playing
520 	Background_playing = 0;
521 	Background_anim = NULL;
522 
523 	Current_screen = Next_screen = ON_BRIEFING_SELECT;
524 
525 	// load in the icons for the wing slots
526 	load_wing_icons(NOX("iconwing01"));
527 
528 	Current_screen = Next_screen = ON_BRIEFING_SELECT;
529 
530 	Commit_pressed = 0;
531 
532 	Common_select_inited = 1;
533 
534 	// this handles the case where the player played a multiplayer game but now is in single player (in one instance
535 	// of FreeSpace)
536 	if(!(Game_mode & GM_MULTIPLAYER)){
537 		chatbox_close();
538 	}
539 
540 	// get the value of the team
541 	Common_team = 0;							// assume the first team -- we'll change this value if we need to
542 
543 	if ( (Game_mode & GM_MULTIPLAYER) && IS_MISSION_MULTI_TEAMS )
544 		Common_team = Net_player->p_info.team;
545 
546 	common_set_team_pointers(Common_team);
547 
548 	ship_select_common_init();
549 	weapon_select_common_init();
550 	common_flash_button_init();
551 
552 	if ( Game_mode & GM_MULTIPLAYER ) {
553 		multi_ts_common_init();
554 	}
555 
556 	// restore loadout from Player_loadout if this is the same mission as the one previously played
557 	if ( !(Game_mode & GM_MULTIPLAYER) ) {
558 		if ( !stricmp(Player_loadout.filename, Game_current_mission_filename) ) {
559 			wss_maybe_restore_loadout();
560 			ss_synch_interface();
561 			wl_synch_interface();
562 		}
563 	}
564 
565 	ss_reset_selected_ship();
566 
567 	Drop_icon_mflag = 0;
568 	Drop_on_wing_mflag = 0;
569 
570 	//init colors
571 	gr_init_alphacolor(&Icon_colors[ICON_FRAME_NORMAL], 32, 128, 128, 255);
572 	gr_init_alphacolor(&Icon_colors[ICON_FRAME_HOT], 48, 160, 160, 255);
573 	gr_init_alphacolor(&Icon_colors[ICON_FRAME_SELECTED], 64, 192, 192, 255);
574 	gr_init_alphacolor(&Icon_colors[ICON_FRAME_PLAYER], 192, 128, 64, 255);
575 	gr_init_alphacolor(&Icon_colors[ICON_FRAME_DISABLED], 175, 175, 175, 255);
576 	gr_init_alphacolor(&Icon_colors[ICON_FRAME_DISABLED_HIGH], 100, 100, 100, 255);
577 	//init shaders
578 	gr_create_shader(&Icon_shaders[ICON_FRAME_NORMAL], 32, 128, 128, 255);
579 	gr_create_shader(&Icon_shaders[ICON_FRAME_HOT], 48, 160, 160, 255);
580 	gr_create_shader(&Icon_shaders[ICON_FRAME_SELECTED], 64, 192, 192, 255);
581 	gr_create_shader(&Icon_shaders[ICON_FRAME_PLAYER], 192, 128, 64, 255);
582 	gr_create_shader(&Icon_shaders[ICON_FRAME_DISABLED], 175, 175, 175, 255);
583 	gr_create_shader(&Icon_shaders[ICON_FRAME_DISABLED_HIGH], 100, 100, 100, 255);
584 }
585 
common_reset_buttons()586 void common_reset_buttons()
587 {
588 	int			i;
589 	UI_BUTTON	*b;
590 
591 	for ( i = 0; i < NUM_COMMON_BUTTONS; i++ ) {
592 		b = &Common_buttons[Current_screen-1][gr_screen.res][i].button;
593 		b->reset_status();
594 	}
595 
596 	switch(Current_screen) {
597 	case ON_BRIEFING_SELECT:
598 		Common_buttons[Current_screen-1][gr_screen.res][COMMON_BRIEFING_REGION].button.skip_first_highlight_callback();
599 		break;
600 	case ON_SHIP_SELECT:
601 		Common_buttons[Current_screen-1][gr_screen.res][COMMON_SS_REGION].button.skip_first_highlight_callback();
602 		break;
603 	case ON_WEAPON_SELECT:
604 		Common_buttons[Current_screen-1][gr_screen.res][COMMON_WEAPON_REGION].button.skip_first_highlight_callback();
605 		break;
606 	}
607 }
608 
609 // common_select_do() is called once per loop in the interface screens and is used
610 // for drawing and changing the common animations and blitting common bitmaps.
common_select_do(float)611 int common_select_do(float  /*frametime*/)
612 {
613 	int	k, new_k;
614 
615 
616 	if ( help_overlay_active(Briefing_overlay_id) || help_overlay_active(Ship_select_overlay_id) || help_overlay_active(Weapon_select_overlay_id) ) {
617 		Common_buttons[0][gr_screen.res][COMMON_HELP_BUTTON].button.reset_status();
618 		Common_buttons[1][gr_screen.res][COMMON_HELP_BUTTON].button.reset_status();
619 		Common_buttons[2][gr_screen.res][COMMON_HELP_BUTTON].button.reset_status();
620 		Active_ui_window->set_ignore_gadgets(1);
621 	} else {
622 		Active_ui_window->set_ignore_gadgets(0);
623 	}
624 
625 	k = chatbox_process();
626 
627 	if ( Game_mode & GM_NORMAL ) {
628 		new_k = Active_ui_window->process(k);
629 	} else {
630 		new_k = Active_ui_window->process(k, 0);
631 	}
632 
633 	if ( (k > 0) || (new_k > 0) || B1_JUST_RELEASED ) {
634 		if ( help_overlay_active(Briefing_overlay_id) || help_overlay_active(Ship_select_overlay_id) || help_overlay_active(Weapon_select_overlay_id) ) {
635 			help_overlay_set_state(Briefing_overlay_id, gr_screen.res, 0);
636 			help_overlay_set_state(Ship_select_overlay_id, gr_screen.res, 0);
637 			help_overlay_set_state(Weapon_select_overlay_id, gr_screen.res, 0);
638 			Active_ui_window->set_ignore_gadgets(0);
639 			k = 0;
640 			new_k = 0;
641 		}
642 	}
643 
644 	// test for mouse buttons,  must be done after Active_ui_window->process()
645 	// has been called to work properly
646 	//
647 	Drop_icon_mflag = 0;
648 	Drop_on_wing_mflag = 0;
649 	Brief_mouse_up_flag = 0;
650 	Mouse_down_last_frame = 0;
651 
652 	// if the left mouse button was released...
653 	if ( B1_RELEASED ) {
654 		Drop_icon_mflag = 1;
655 		Drop_on_wing_mflag = 1;
656 	}
657 
658 	// if the left mouse button was pressed...
659 	if ( B1_PRESSED ) {
660 		Mouse_down_last_frame = 1;
661 	}
662 
663 	// basically a "click", only check for the click here to avoid action-on-over on briefing map
664 	if ( B1_JUST_PRESSED ) {
665 		Brief_mouse_up_flag = 1;
666 	}
667 
668 	// reset timers for flashing buttons if key pressed
669 	if ( (k>0) || (new_k>0) ) {
670 		common_flash_button_init();
671 	}
672 
673 	common_music_do();
674 
675 	/*
676 	if ( Background_playing ) {
677 
678 		if ( Background_anim_instance->frame_num == BUTTON_SLIDE_IN_FRAME ) {
679 			gamesnd_play_iface(SND_BTN_SLIDE);
680 		}
681 
682 		if ( Background_anim_instance->frame_num == Background_anim_instance->stop_at ) {
683 			// Free up the big honking background animation, since we won't be playing it again
684 			anim_release_render_instance(Background_anim_instance);
685 			anim_free(Background_anim);
686 
687 			Background_playing = 0;
688 			Current_screen = Next_screen = ON_BRIEFING_SELECT;
689 		}
690 	}
691 	*/
692 
693 	if ( Current_screen != Next_screen ) {
694 		switch( Next_screen ) {
695 			case ON_BRIEFING_SELECT:
696 				gameseq_post_event( GS_EVENT_START_BRIEFING );
697 				break;
698 
699 			case ON_SHIP_SELECT:
700 				// go to the specialized multiplayer team/ship select screen
701 				if(Game_mode & GM_MULTIPLAYER){
702 					gameseq_post_event(GS_EVENT_TEAM_SELECT);
703 				}
704 				// go to the normal ship select screen
705 				else {
706 					gameseq_post_event(GS_EVENT_SHIP_SELECTION);
707 				}
708 				break;
709 
710 			case ON_WEAPON_SELECT:
711 				if ( !wss_slots_all_empty() ) {
712 					gameseq_post_event(GS_EVENT_WEAPON_SELECTION);
713 				} else {
714 					common_show_no_ship_error();
715 				}
716 				break;
717 		} // end switch
718 	}
719 
720    return new_k;
721 }
722 
723 // -------------------------------------------------------------------------------------
724 // common_render()
725 //
common_render(float frametime)726 void common_render(float frametime)
727 {
728 	if ( !Background_playing ) {
729 		GR_MAYBE_CLEAR_RES(Brief_background_bitmap);
730 		gr_set_bitmap(Brief_background_bitmap);
731 		gr_bitmap(0, 0, GR_RESIZE_MENU);
732 	}
733 
734 	anim_render_all(0, frametime);
735 	anim_render_all(ON_SHIP_SELECT, frametime);
736 }
737 
738 // -------------------------------------------------------------------------------------
739 // common_render_selected_screen_button()
740 //
741 //	A very ugly piece of special purpose code.  This is used to draw the pressed button
742 // frame for whatever stage of the briefing/ship select/weapons loadout we are on.
743 //
common_render_selected_screen_button()744 void common_render_selected_screen_button()
745 {
746 	Common_buttons[Next_screen-1][gr_screen.res][Next_screen-1].button.draw_forced(2);
747 }
748 
749 // -------------------------------------------------------------------------------------
750 // common_button_do() do the button action for the specified pressed button
751 //
common_button_do(int i)752 void common_button_do(int i)
753 {
754 	if ( i == COMMON_COMMIT_BUTTON ) {
755 		Commit_pressed = 1;
756 		return;
757 	}
758 
759 	if ( Background_playing )
760 		return;
761 
762 	switch ( i ) {
763 
764 	case COMMON_BRIEFING_BUTTON:
765 		if ( Current_screen != ON_BRIEFING_SELECT ) {
766 			gamesnd_play_iface(InterfaceSounds::SCREEN_MODE_PRESSED);
767 			Next_screen = ON_BRIEFING_SELECT;
768 		}
769 		break;
770 
771 	case COMMON_WEAPON_BUTTON:
772 		if ( Current_screen != ON_WEAPON_SELECT ) {
773 			if ( !wss_slots_all_empty() ) {
774 				gamesnd_play_iface(InterfaceSounds::SCREEN_MODE_PRESSED);
775 				Next_screen = ON_WEAPON_SELECT;
776 			} else {
777 				common_show_no_ship_error();
778 			}
779 		}
780 		break;
781 
782 	case COMMON_SS_BUTTON:
783 		if ( Current_screen != ON_SHIP_SELECT ) {
784 			gamesnd_play_iface(InterfaceSounds::SCREEN_MODE_PRESSED);
785 			Next_screen = ON_SHIP_SELECT;
786 		}
787 		break;
788 
789 	case COMMON_OPTIONS_BUTTON:
790 		gamesnd_play_iface(InterfaceSounds::SWITCH_SCREENS);
791 		gameseq_post_event( GS_EVENT_OPTIONS_MENU );
792 		break;
793 
794 	case COMMON_HELP_BUTTON:
795 		gamesnd_play_iface(InterfaceSounds::HELP_PRESSED);
796 		launch_context_help();
797 		break;
798 
799 	} // end switch
800 }
801 
802 // common_check_keys() will check for keypresses common to all the interface screens.
common_check_keys(int k)803 void common_check_keys(int k)
804 {
805 	switch (k) {
806 
807 		case KEY_ESC: {
808 
809 			if ( Current_screen == ON_BRIEFING_SELECT ) {
810 				if ( brief_get_closeup_icon() != NULL ) {
811 					brief_turn_off_closeup_icon();
812 					break;
813 				}
814 			}
815 
816 			// prompt the host of a multiplayer game
817 			if(Game_mode & GM_MULTIPLAYER){
818 				multi_quit_game(PROMPT_ALL);
819 			} else {
820 				// go through the single player quit process
821 				// return to the main menu
822 /*
823 				int return_to_menu, pf_flags;
824 				pf_flags = PF_USE_AFFIRMATIVE_ICON|PF_USE_NEGATIVE_ICON;
825 				return_to_menu = popup(pf_flags, 2, POPUP_NO, POPUP_YES, XSTR( "Do you want to return to the Main Hall?\n(Your campaign position will be saved)", -1));
826 				if ( return_to_menu == 1 ) {
827 					gameseq_post_event(GS_EVENT_MAIN_MENU);
828 				}
829 */
830 				gameseq_post_event(GS_EVENT_MAIN_MENU);
831 			}
832 			break;
833 		}
834 
835 		case KEY_CTRLED + KEY_ENTER:
836 			Commit_pressed = 1;
837 			break;
838 
839 		case KEY_B:
840 			if ( Current_screen != ON_BRIEFING_SELECT && !Background_playing ) {
841 				Next_screen = ON_BRIEFING_SELECT;
842 			}
843 			break;
844 
845 		case KEY_W:
846 			if ( brief_only_allow_briefing() ) {
847 				gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
848 				break;
849 			}
850 
851 			if ( Current_screen != ON_WEAPON_SELECT && !Background_playing ) {
852 				if ( !wss_slots_all_empty() ) {
853 					Next_screen = ON_WEAPON_SELECT;
854 				} else {
855 					common_show_no_ship_error();
856 				}
857 			}
858 
859 			break;
860 
861 		case KEY_S:
862 
863 			if ( brief_only_allow_briefing() ) {
864 				gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
865 				break;
866 			}
867 
868 			if ( Current_screen != ON_SHIP_SELECT && !Background_playing ) {
869 				Next_screen = ON_SHIP_SELECT;
870 			}
871 
872 			break;
873 
874 		case KEY_SHIFTED+KEY_TAB:
875 
876 			if ( brief_only_allow_briefing() ) {
877 				gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
878 				break;
879 			}
880 
881 			if ( !Background_playing ) {
882 				switch ( Current_screen ) {
883 					case ON_BRIEFING_SELECT:
884 						if ( !wss_slots_all_empty() ) {
885 							Next_screen = ON_WEAPON_SELECT;
886 						} else {
887 							common_show_no_ship_error();
888 						}
889 						break;
890 
891 					case ON_SHIP_SELECT:
892 						Next_screen = ON_BRIEFING_SELECT;
893 						break;
894 
895 					case ON_WEAPON_SELECT:
896 						Next_screen = ON_SHIP_SELECT;
897 						break;
898 					default:
899 						Int3();
900 						break;
901 				}	// end switch
902 			}
903 
904 			break;
905 
906 		case KEY_TAB:
907 
908 			if ( brief_only_allow_briefing() ) {
909 				gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
910 				break;
911 			}
912 
913 			if ( !Background_playing ) {
914 				switch ( Current_screen ) {
915 					case ON_BRIEFING_SELECT:
916 						Next_screen = ON_SHIP_SELECT;
917 						break;
918 
919 					case ON_SHIP_SELECT:
920 						if ( !wss_slots_all_empty() ) {
921 							Next_screen = ON_WEAPON_SELECT;
922 						} else {
923 							common_show_no_ship_error();
924 						}
925 						break;
926 
927 					case ON_WEAPON_SELECT:
928 						Next_screen = ON_BRIEFING_SELECT;
929 						break;
930 					default:
931 						Int3();
932 						break;
933 				}	// end switch
934 			}
935 
936 			break;
937 
938 		case KEY_P:
939 			if ( Anim_paused )
940 				Anim_paused = 0;
941 			else
942 				Anim_paused = 1;
943 			break;
944 	} // end switch
945 }
946 
947 // common_select_close() will release the memory for animations and bitmaps that
948 // were loaded in common_select_init().  This function will abort if the Common_select_inited
949 // flag is not set.  The last thing common_select_close() does in clear the Common_select_inited
950 // flag.
951 //
952 // weapon_select_close() and ship_select_close() are both called, since common_select_close()
953 // is the function that is called the interface screens are finally exited.
common_select_close()954 void common_select_close()
955 {
956 	if ( !Common_select_inited ) {
957 		nprintf(("Alan","common_select_close() returning without doing anything\n"));
958 		return;
959 	}
960 
961 	nprintf(("Alan","entering common_select_close()\n"));
962 
963 	// catch open anims that weapon_select_init_team() opened when not in weapon_select - taylor
964 	// *** not the same as weapon_select_close() ***
965 	weapon_select_close_team();
966 
967 	weapon_select_close();
968 
969 	if(Game_mode & GM_MULTIPLAYER){
970 		multi_ts_close();
971 	}
972 
973 	ship_select_close();
974 	brief_close();
975 
976 	common_free_interface_palette();
977 
978 	// release the bitmpas that were previously extracted from anim files
979 	unload_wing_icons();
980 
981 	// Release any instances that may still exist
982 	anim_release_all_instances();
983 
984 	// free the anim's that were loaded into memory
985 	/*
986 	if ( Background_anim ) {
987 		anim_free(Background_anim);
988 		Background_anim = NULL;
989 	}
990 	*/
991 
992 	common_music_close();
993 
994 	common_reset_team_pointers();
995 
996 	Common_select_inited = 0;
997 }
998 
999 // ------------------------------------------------------------------------
1000 //	load_wing_icons() creates the bitmaps for wing icons
1001 //
load_wing_icons(const char * filename)1002 void load_wing_icons(const char *filename)
1003 {
1004 	int first_frame, num_frames;
1005 
1006 	first_frame = bm_load_animation(filename, &num_frames);
1007 	if ( first_frame == -1 ) {
1008 		Error(LOCATION, "Could not load icons from %s\n", filename);
1009 		return;
1010 	}
1011 
1012 	Wing_slot_disabled_bitmap = first_frame;
1013 	Wing_slot_empty_bitmap = first_frame + 1;
1014 //	Wing_slot_player_empty_bitmap = first_frame + 2;
1015 }
1016 
1017 // ------------------------------------------------------------------------
1018 //	common_scroll_up_pressed()
1019 //
common_scroll_up_pressed(int * start,int size,int max_show)1020 int common_scroll_up_pressed(int *start, int size, int max_show)
1021 {
1022 	// check if we even need to scroll at all
1023 	if ( size <= max_show ) {
1024 		return 0;
1025 	}
1026 
1027 	if ( (size - *start) > max_show ) {
1028 		*start += 1;
1029 		return 1;
1030 	}
1031 	return 0;
1032 }
1033 
1034 // ------------------------------------------------------------------------
1035 //	common_scroll_down_pressed()
1036 //
common_scroll_down_pressed(int * start,int size,int max_show)1037 int common_scroll_down_pressed(int *start, int size, int max_show)
1038 {
1039 	// check if we even need to scroll at all
1040 	if ( size <= max_show ) {
1041 		return 0;
1042 	}
1043 
1044 	if ( *start > 0 ) {
1045 		*start -= 1;
1046 		return 1;
1047 	}
1048 	return 0;
1049 }
1050 
common_fire_stage_script_hook(int old_stage,int new_stage)1051 void common_fire_stage_script_hook(int old_stage, int new_stage)
1052 {
1053 	if (!Script_system.IsActiveAction(CHA_ONBRIEFSTAGE)) {
1054 		return;
1055 	}
1056 	// call a scripting hook for switching stages
1057 	// note that we add 1 because Lua arrays are 1-based
1058 	Script_system.SetHookVar("OldStage", 'i', old_stage + 1);
1059 	Script_system.SetHookVar("NewStage", 'i', new_stage + 1);
1060 	Script_system.RunCondition(CHA_ONBRIEFSTAGE);
1061 	Script_system.RemHookVars({"OldStage", "NewStage"});
1062 }
1063 
1064 // NEWSTUFF BEGIN
1065 
1066 // save ship selection loadout to the Player_loadout struct
wss_save_loadout()1067 void wss_save_loadout()
1068 {
1069 	int i,j;
1070 
1071 	Assert( (Ss_pool != NULL) && (Wl_pool != NULL) && (Wss_slots != NULL) );
1072 
1073 	// save the ship pool
1074 	for ( i = 0; i < MAX_SHIP_CLASSES; i++ ) {
1075 		Player_loadout.ship_pool[i] = Ss_pool[i];
1076 	}
1077 
1078 	// save the weapons pool
1079 	for ( i = 0; i < MAX_WEAPON_TYPES; i++ ) {
1080 		Player_loadout.weapon_pool[i] = Wl_pool[i];
1081 	}
1082 
1083 	// save the ship class / weapons for each slot
1084 	for ( i = 0; i < MAX_WSS_SLOTS; i++ ) {
1085 		Player_loadout.unit_data[i].ship_class = Wss_slots[i].ship_class;
1086 
1087 		for ( j = 0; j < MAX_SHIP_WEAPONS; j++ ) {
1088 			Player_loadout.unit_data[i].wep[j] = Wss_slots[i].wep[j];
1089 			Player_loadout.unit_data[i].wep_count[j] = Wss_slots[i].wep_count[j];
1090 		}
1091 	}
1092 }
1093 
1094 // restore ship/weapons loadout from the Player_loadout struct
wss_maybe_restore_loadout()1095 void wss_maybe_restore_loadout()
1096 {
1097 	int i,j;
1098 	wss_unit	*slot;
1099 
1100 	Assert( (Ss_pool != NULL) && (Wl_pool != NULL) && (Wss_slots != NULL) );
1101 
1102 	// only restore if mission hasn't changed
1103 	if ( stricmp(Player_loadout.last_modified, The_mission.modified) != 0 ) {
1104 		return;
1105 	}
1106 
1107 	// first we generate a pool of ships and weapons used the last time this mission was played. We also generate a pool of what is
1108 	// available in this mission.
1109 	int	last_loadout_ships[MAX_SHIP_CLASSES];
1110 	int	this_loadout_ships[MAX_SHIP_CLASSES];
1111 
1112 	int	last_loadout_weapons[MAX_WEAPON_TYPES];
1113 	int	this_loadout_weapons[MAX_WEAPON_TYPES];
1114 
1115 	// zero all pools
1116 	for (i = 0; i < MAX_SHIP_CLASSES; i++) {
1117 		last_loadout_ships[i] = 0;
1118 		this_loadout_ships[i] = 0;
1119 	}
1120 	for (i = 0; i < MAX_WEAPON_TYPES; i++) {
1121 		last_loadout_weapons[i] = 0;
1122 		this_loadout_weapons[i] = 0;
1123 	}
1124 
1125 	// record the ship classes / weapons used last time
1126 	for ( i = 0; i < MAX_WSS_SLOTS; i++ ) {
1127 		slot = &Player_loadout.unit_data[i];
1128 		if ((slot->ship_class >= 0) && (slot->ship_class < ship_info_size())) {
1129 			++last_loadout_ships[slot->ship_class];
1130 
1131 			for ( j = 0; j < MAX_SHIP_WEAPONS; j++ ) {
1132 				if ((slot->wep[j] >= 0) && (slot->wep[j] < weapon_info_size())) {
1133 					last_loadout_weapons[slot->wep[j]] += slot->wep_count[j];
1134 				}
1135 			}
1136 		}
1137 	}
1138 
1139 	// record the ships classes / weapons used by the player and wingmen. We don't include the amount in the pools yet
1140 	for ( i = 0; i < MAX_WSS_SLOTS; i++ ) {
1141 		if ((Wss_slots[i].ship_class >= 0) && (Wss_slots[i].ship_class < ship_info_size())) {
1142 			++this_loadout_ships[Wss_slots[i].ship_class];
1143 
1144 			for ( j = 0; j < MAX_SHIP_WEAPONS; j++ ) {
1145 				if ((Wss_slots[i].wep[j] >= 0) && (Wss_slots[i].wep[j] < weapon_info_size())) {
1146 					this_loadout_weapons[Wss_slots[i].wep[j]] += Wss_slots[i].wep_count[j];
1147 				}
1148 			}
1149 		}
1150 	}
1151 
1152 	// now compare the two, adding in what was left in the pools. If there are less of a ship or weapon class in the mission now
1153 	// than there were last time, we can't restore and must abort.
1154 	for (i = 0; i < ship_info_size(); i++) {
1155 		if (Ss_pool[i] >= 1) {
1156 			this_loadout_ships[i] += Ss_pool[i];
1157 		}
1158 		if ( this_loadout_ships[i] < last_loadout_ships[i]) {
1159 			return;
1160 		}
1161 	}
1162 
1163 	for (i = 0; i < weapon_info_size(); i++) {
1164 		if (Wl_pool[i] >= 1) {
1165 			this_loadout_weapons[i] += Wl_pool[i];
1166 		}
1167 		if ( this_loadout_weapons[i] < last_loadout_weapons[i]) {
1168 			return;
1169 		}
1170 	}
1171 
1172 	// go through the slots and restore the previous runthrough's loadout. Also remove that ship from total of ships in this mission
1173 	for ( i = 0; i < MAX_WSS_SLOTS; i++ ) {
1174 		slot = &Player_loadout.unit_data[i];
1175 
1176 		if ((slot->ship_class >= 0) && (slot->ship_class < ship_info_size())) {
1177 			--this_loadout_ships[slot->ship_class];
1178 			Assertion((this_loadout_ships[slot->ship_class] >= 0), "Attempting to restore the previous missions loadout has resulted in an invalid number of ships available");
1179 
1180 		}
1181 		// restore the ship class for each slot
1182 		Wss_slots[i].ship_class = slot->ship_class;
1183 
1184 		for ( j = 0; j < MAX_SHIP_WEAPONS; j++ ) {
1185 			if ((slot->ship_class >= 0) && (slot->wep[j] >= 0) && (slot->wep[j] < weapon_info_size())) {
1186 				this_loadout_weapons[slot->wep[j]] -= slot->wep_count[j];
1187 				Assertion((this_loadout_weapons[slot->wep[j]] >= 0), "Attempting to restore the previous missions loadout has resulted in an invalid number of weapons available");
1188 			}
1189 
1190 			Wss_slots[i].wep[j]= slot->wep[j];
1191 			Wss_slots[i].wep_count[j] = slot->wep_count[j];
1192 		}
1193 	}
1194 
1195 	// restore the ship pool
1196 	for ( i = 0; i < ship_info_size(); i++ ) {
1197 		Ss_pool[i] = this_loadout_ships[i];
1198 	}
1199 
1200 	// restore the weapons pool
1201 	for ( i = 0; i < weapon_info_size(); i++ ) {
1202 		Wl_pool[i] = this_loadout_weapons[i];
1203 	}
1204 }
1205 
1206 // Do a direct restore of the Player_loadout ship/weapon data to the wings
wss_direct_restore_loadout()1207 void wss_direct_restore_loadout()
1208 {
1209 	int				i, j;
1210 	wing				*wp;
1211 	wss_unit			*slot;
1212 
1213 	// only restore if mission hasn't changed
1214 	if ( stricmp(Player_loadout.last_modified, The_mission.modified) != 0 ) {
1215 		return;
1216 	}
1217 
1218 	// niffiwan: if Starting_wings[] has missing wings, Player_loadout.unit_data
1219 	// skips the missing wings.  Use a new variable to track the number of valid
1220 	// wings in Starting_wings[], otherwise mission can crash on restart
1221 	int valid_wing_index = -1;
1222 
1223 	for ( i = 0; i < MAX_WING_BLOCKS; i++ ) {
1224 
1225 		if ( Starting_wings[i] < 0 )
1226 			continue;
1227 
1228 		valid_wing_index++;
1229 		wp = &Wings[Starting_wings[i]];
1230 
1231 		// If this wing is still on the arrival list, then update the parse objects
1232 		if ( wp->ship_index[0] == -1 ) {
1233 			p_object *p_objp;
1234 			j=0;
1235 			for ( p_objp = GET_FIRST(&Ship_arrival_list); p_objp != END_OF_LIST(&Ship_arrival_list); p_objp = GET_NEXT(p_objp) ) {
1236 				if ( p_objp->wingnum == WING_INDEX(wp) ) {
1237 					// niffiwan: don't overrun the array
1238 					if (j >= MAX_WING_SLOTS) {
1239 						Warning(LOCATION, "Starting Wing '%s' has more than 'MAX_WING_SLOTS' ships\n", Starting_wing_names[i]);
1240 						break;
1241 					}
1242 					slot = &Player_loadout.unit_data[valid_wing_index*MAX_WING_SLOTS+j];
1243 					p_objp->ship_class = slot->ship_class;
1244 					wl_update_parse_object_weapons(p_objp, slot);
1245 					j++;
1246 				}
1247 			}
1248 		} else {
1249 			int	k;
1250 			int cleanup_ship_index[MAX_WING_SLOTS];
1251 			ship	*shipp;
1252 
1253 			for ( k = 0; k < MAX_WING_SLOTS; k++ ) {
1254 				cleanup_ship_index[k] = -1;
1255 			}
1256 
1257 			// This wing is already created, so directly update the ships
1258 			for ( j = 0; j < MAX_WING_SLOTS; j++ ) {
1259 				if ( wp->ship_index[j] == -1 ) { // if this is an invalid ship, move on
1260 					continue;
1261 				}
1262 
1263 				slot = &Player_loadout.unit_data[valid_wing_index*MAX_WING_SLOTS+j];
1264 				shipp = &Ships[wp->ship_index[j]];
1265 				if ( shipp->ship_info_index != slot->ship_class ) {
1266 
1267 					if ( slot->ship_class == -1 ) {
1268 						cleanup_ship_index[j] = wp->ship_index[j];
1269 						ship_add_exited_ship( shipp, Ship::Exit_Flags::Player_deleted );
1270 						obj_delete(shipp->objnum);
1271 						hud_set_wingman_status_none( shipp->wing_status_wing_index, shipp->wing_status_wing_pos);
1272 						continue;
1273 					} else {
1274 						change_ship_type(wp->ship_index[j], slot->ship_class);
1275 					}
1276 				}
1277 				wl_bash_ship_weapons(&Ships[wp->ship_index[j]].weapons, slot);
1278 			}
1279 
1280 			for ( k = 0; k < MAX_WING_SLOTS; k++ ) {
1281 				if ( cleanup_ship_index[k] != -1 ) {
1282 					ship_wing_cleanup( cleanup_ship_index[k], wp );
1283 				}
1284 			}
1285 
1286 		}
1287 	} // end for
1288 }
wss_slots_all_empty()1289 int wss_slots_all_empty()
1290 {
1291 	int i;
1292 
1293 	Assert( Wss_slots != NULL );
1294 
1295 	for ( i = 0; i < MAX_WSS_SLOTS; i++ ) {
1296 		if ( Wss_slots[i].ship_class >= 0 )
1297 			break;
1298 	}
1299 
1300 	if ( i == MAX_WSS_SLOTS )
1301 		return 1;
1302 	else
1303 		return 0;
1304 }
1305 
1306 // determine the mode (WSS_...) based on slot/list index values
wss_get_mode(int from_slot,int from_list,int to_slot,int to_list,int wl_ship_slot)1307 int wss_get_mode(int from_slot, int from_list, int to_slot, int to_list, int wl_ship_slot)
1308 {
1309 	int mode, to_slot_empty=0;
1310 
1311 	Assert( Wss_slots != NULL );
1312 
1313 	if ( wl_ship_slot >= 0 ) {
1314 		// weapons loadout
1315 		if ( to_slot >= 0 ) {
1316 			if ( Wss_slots[wl_ship_slot].wep_count[to_slot] == 0 ) {
1317 				to_slot_empty = 1;
1318 			}
1319 		}
1320 	} else {
1321 		// ship select
1322 		if ( to_slot >= 0 ) {
1323 			if ( Wss_slots[to_slot].ship_class == -1 ){
1324 				to_slot_empty = 1;
1325 			}
1326 		}
1327 	}
1328 
1329 	// determine mode
1330 	if ( from_slot >= 0 && to_slot >= 0 ) {
1331 		mode = WSS_SWAP_SLOT_SLOT;
1332 	} else if ( from_slot >= 0 && to_list >= 0 ) {
1333 		mode = WSS_DUMP_TO_LIST;
1334 	} else if ( (from_list >= 0) && (to_slot >= 0) && (to_slot_empty) ) {
1335 		mode = WSS_GRAB_FROM_LIST;
1336 	} else if ( (from_list >= 0) && (to_slot >= 0) && (!to_slot_empty) ) {
1337 		mode = WSS_SWAP_LIST_SLOT;
1338 	} else {
1339 		mode = -1;	// no changes required
1340 	}
1341 
1342 	return mode;
1343 }
1344 
1345 // store all the unit data and pool data
store_wss_data(ubyte * data,__UNUSED const unsigned int max_size,interface_snd_id sound,int player_index)1346 int store_wss_data(ubyte *data, __UNUSED const unsigned int max_size, interface_snd_id sound, int player_index)
1347 {
1348 	int j, i, packet_size = 0;
1349 	ubyte val;
1350 	short player_id;
1351 	ushort pool_size;
1352 
1353 	// this function assumes that the data is going to be used over the network
1354 	// so make a non-network version of this function if needed
1355 	Assert( Game_mode & GM_MULTIPLAYER );
1356 	Assert( (Ss_pool != NULL) && (Wl_pool != NULL) && (Wss_slots != NULL) );
1357 
1358 	if ( !(Game_mode & GM_MULTIPLAYER) )
1359 		return 0;
1360 
1361 	// write the ship pool
1362 	pool_size = 0;
1363 	for (i = 0; i < ship_info_size(); i++) {
1364 		if (Ss_pool[i] > 0) {
1365 			++pool_size;
1366 		}
1367 	}
1368 
1369 	ADD_USHORT(pool_size);
1370 
1371 	Assertion((((sizeof(short)+sizeof(short)) * pool_size) + packet_size) < max_size, "Size of ship pool exceeds max data size!");
1372 
1373 	for (i = 0; i < ship_info_size(); i++) {
1374 		if (Ss_pool[i] > 0) {
1375 			ADD_SHORT(static_cast<short>(i));
1376 			ADD_SHORT(static_cast<short>(Ss_pool[i]));
1377 		}
1378 	}
1379 
1380 	// write the weapon pool
1381 	pool_size = 0;
1382 	for (i = 0; i < weapon_info_size(); i++) {
1383 		if (Wl_pool[i] > 0) {
1384 			++pool_size;
1385 		}
1386 	}
1387 
1388 	ADD_USHORT(pool_size);
1389 
1390 	Assertion((((sizeof(short)+sizeof(short)) * pool_size) + packet_size) < max_size, "Size of weapon pool exceeds max data size!");
1391 
1392 	for (i = 0; i < weapon_info_size(); i++) {
1393 		if (Wl_pool[i] > 0) {
1394 			ADD_SHORT(static_cast<short>(i));
1395 			ADD_SHORT(static_cast<short>(Wl_pool[i]));
1396 		}
1397 	}
1398 
1399 	// write the unit data
1400 	val = MAX_WSS_SLOTS;
1401 	ADD_DATA(val);
1402 	val = MAX_SHIP_WEAPONS;
1403 	ADD_DATA(val);
1404 
1405 	Assertion((((sizeof(short) + ((sizeof(short)+sizeof(short)) * MAX_SHIP_WEAPONS)) * MAX_WSS_SLOTS) + packet_size) < max_size, "Size of wss data exceeds max data size!");
1406 
1407 	for (i = 0; i < MAX_WSS_SLOTS; i++) {
1408 		ADD_SHORT(static_cast<short>(Wss_slots[i].ship_class));
1409 
1410 		for (j = 0; j < MAX_SHIP_WEAPONS; j++) {
1411 			ADD_SHORT(static_cast<short>(Wss_slots[i].wep[j]));
1412 			Assert(Wss_slots[i].wep_count[j] < SHRT_MAX);
1413 			ADD_SHORT(static_cast<short>(Wss_slots[i].wep_count[j]));
1414 		}
1415 	}
1416 
1417 	// any sound index
1418 	ADD_SHORT(static_cast<short>(sound.value()));
1419 
1420 	// add a netplayer address to identify who should play the sound
1421 	player_id = -1;
1422 
1423 	if(player_index != -1){
1424 		player_id = Net_players[player_index].player_id;
1425 	}
1426 
1427 	ADD_SHORT(player_id);
1428 
1429 	Assert( packet_size < static_cast<int>(max_size) );
1430 	return packet_size;
1431 }
1432 
restore_wss_data(ubyte * data)1433 int restore_wss_data(ubyte *data)
1434 {
1435 	int	i, j, offset = 0;
1436 	ubyte num_slots, num_weapons;
1437 	short b1, b2;
1438 	short player_id, sound;
1439 	ushort pool_size;
1440 
1441 	// this function assumes that the data is going to be used over the network
1442 	// so make a non-network version of this function if needed
1443 	Assert( Game_mode & GM_MULTIPLAYER );
1444 	Assert( (Ss_pool != NULL) && (Wl_pool != NULL) && (Wss_slots != NULL) );
1445 
1446 	if ( !(Game_mode & GM_MULTIPLAYER) )
1447 		return 0;
1448 
1449 	// restore ship pool
1450 	memset(Ss_pool, 0, MAX_SHIP_CLASSES*sizeof(int));
1451 	GET_USHORT(pool_size);
1452 
1453 	for (i = 0; i < pool_size; i++) {
1454 		GET_SHORT(b1);
1455 		GET_SHORT(b2);
1456 
1457 		if (b1 < MAX_SHIP_CLASSES) {
1458 			Ss_pool[b1] = b2;
1459 		}
1460 	}
1461 
1462 	// restore weapons pool
1463 	memset(Wl_pool, 0, MAX_WEAPON_TYPES*sizeof(int));
1464 	GET_USHORT(pool_size);
1465 
1466 	for (i = 0; i < pool_size; i++) {
1467 		GET_SHORT(b1);
1468 		GET_SHORT(b2);
1469 
1470 		if (b1 < MAX_SHIP_CLASSES) {
1471 			Wl_pool[b1] = b2;
1472 		}
1473 	}
1474 
1475 
1476 	// restore unit data
1477 	for (i = 0; i < MAX_WSS_SLOTS; i++) {
1478 		Wss_slots[i].ship_class = -1;
1479 
1480 		for (j = 0; j < MAX_SHIP_WEAPONS; j++) {
1481 			Wss_slots[i].wep[j] = -1;
1482 			Wss_slots[i].wep_count[j] = 0;
1483 		}
1484 	}
1485 
1486 	GET_DATA(num_slots);
1487 	GET_DATA(num_weapons);
1488 
1489 	for (i = 0; i < num_slots; i++) {
1490 		GET_SHORT(b1);
1491 
1492 		if (i < MAX_WSS_SLOTS) {
1493 			Wss_slots[i].ship_class = b1;
1494 		}
1495 
1496 		for (j = 0; j < num_weapons; j++) {
1497 			GET_SHORT(b1);
1498 			GET_SHORT(b2);
1499 
1500 			if ( (i < MAX_WSS_SLOTS) && (j < MAX_SHIP_WEAPONS) ) {
1501 				Wss_slots[i].wep[j] = b1;
1502 				Wss_slots[i].wep_count[j] = b2;
1503 			}
1504 		}
1505 	}
1506 
1507 	// read in the sound data
1508 	GET_SHORT(sound);
1509 
1510 	// read in the player address
1511 	GET_SHORT(player_id);
1512 
1513 	// determine if I'm the guy who should be playing the sound
1514 	if((Net_player != NULL) && (Net_player->player_id == player_id)){
1515 		// play the sound
1516 		if (sound >= 0) {
1517 			gamesnd_play_iface(static_cast<InterfaceSounds>(sound));
1518 		}
1519 	}
1520 
1521 	if(!(Game_mode & GM_MULTIPLAYER)){
1522 		ss_synch_interface();
1523 	}
1524 
1525 	return offset;
1526 }
1527 
draw_model_icon(int model_id,int flags,float closeup_zoom,int x,int y,int w,int h,ship_info * sip,int resize_mode,const vec3d * closeup_pos)1528 void draw_model_icon(int model_id, int flags, float closeup_zoom, int x, int y, int w, int h, ship_info *sip, int resize_mode, const vec3d *closeup_pos)
1529 {
1530 	matrix	object_orient	= IDENTITY_MATRIX;
1531 	angles rot_angles = vmd_zero_angles;
1532 	float zoom = closeup_zoom * 2.5f;
1533 
1534 	if(sip == NULL)
1535 	{
1536 		//Assume it's a weapon
1537 		rot_angles.h = -(PI_2);
1538 	}
1539 	else if(sip->model_icon_angles.p != 0.0f || sip->model_icon_angles.b != 0.0f || sip->model_icon_angles.h != 0.0f)
1540 	{
1541 		// If non-zero model_icon_angles exists, always use that
1542 		rot_angles = sip->model_icon_angles;
1543 	}
1544 	else if(sip->is_small_ship())
1545 	{
1546 		rot_angles.p = -(PI_2);
1547 	}
1548 	else if((sip->max_speed <= 0.0f) && !(sip->flags[Ship::Info_Flags::Cargo]))
1549 	{
1550 		//Probably an installation or Knossos
1551 		rot_angles.h = PI;
1552 	}
1553 	else
1554 	{
1555 		//Probably a capship
1556 		rot_angles.h = PI_2;
1557 	}
1558 	vm_angles_2_matrix(&object_orient, &rot_angles);
1559 
1560 	gr_set_clip(x, y, w, h, resize_mode);
1561 	g3_start_frame(1);
1562 	if(sip != NULL)
1563 	{
1564 		g3_set_view_matrix( &sip->closeup_pos, &vmd_identity_matrix, zoom);
1565 
1566 		gr_set_proj_matrix(0.5f*Proj_fov, gr_screen.clip_aspect, Min_draw_distance, Max_draw_distance);
1567 	}
1568 	else
1569 	{
1570 		polymodel *pm = model_get(model_id);
1571 		bsp_info *bs = NULL;	//tehe
1572 		for(int i = 0; i < pm->n_models; i++)
1573 		{
1574 			if(!pm->submodel[i].is_thruster)
1575 			{
1576 				bs = &pm->submodel[i];
1577 				break;
1578 			}
1579 		}
1580 
1581 		if(bs == NULL)
1582 		{
1583 			bs = &pm->submodel[0];
1584 		}
1585 
1586 		vec3d weap_closeup = *closeup_pos;
1587 		float y_closeup;
1588 		float tm_zoom = closeup_zoom;
1589 
1590 		//Find the center of teh submodel
1591 		weap_closeup.xyz.x = -(bs->min.xyz.z + (bs->max.xyz.z - bs->min.xyz.z)/2.0f);
1592 		weap_closeup.xyz.y = -(bs->min.xyz.y + (bs->max.xyz.y - bs->min.xyz.y)/2.0f);
1593 		//weap_closeup.xyz.z = (weap_closeup.xyz.x/tanf(zoom / 2.0f));
1594 		weap_closeup.xyz.z = -(bs->rad/tanf(tm_zoom/2.0f));
1595 
1596 		y_closeup = -(weap_closeup.xyz.y/tanf(tm_zoom / 2.0f));
1597 		if(y_closeup < weap_closeup.xyz.z)
1598 		{
1599 			weap_closeup.xyz.z = y_closeup;
1600 		}
1601 		if(bs->min.xyz.x < weap_closeup.xyz.z)
1602 		{
1603 			weap_closeup.xyz.z = bs->min.xyz.x;
1604 		}
1605 		g3_set_view_matrix( &weap_closeup, &vmd_identity_matrix, tm_zoom);
1606 
1607 		gr_set_proj_matrix(0.5f*Proj_fov, gr_screen.clip_aspect, 0.05f, 1000.0f);
1608 	}
1609 
1610 	model_render_params render_info;
1611 	render_info.set_detail_level_lock(0);
1612 
1613 	gr_set_view_matrix(&Eye_position, &Eye_matrix);
1614 
1615 	if(!(flags & MR_NO_LIGHTING))
1616 	{
1617 		light_reset();
1618 		vec3d light_dir = vmd_zero_vector;
1619 		light_dir.xyz.x = -0.5;
1620 		light_dir.xyz.y = 2.0f;
1621 		light_dir.xyz.z = -2.0f;
1622 		light_add_directional(&light_dir, 0.65f, 1.0f, 1.0f, 1.0f);
1623 		light_rotate_all();
1624 	}
1625 
1626 	if (sip != NULL && sip->replacement_textures.size() > 0)
1627 	{
1628 		render_info.set_replacement_textures(model_id, sip->replacement_textures);
1629 	}
1630 
1631 	Glowpoint_override = true;
1632 	model_clear_instance(model_id);
1633 
1634 	render_info.set_flags(flags);
1635 	model_render_immediate(&render_info, model_id, &object_orient, &vmd_zero_vector);
1636 	Glowpoint_override = false;
1637 
1638 	gr_end_view_matrix();
1639 	gr_end_proj_matrix();
1640 
1641 	g3_end_frame();
1642 	gr_reset_clip();
1643 }
1644 
1645 void light_set_all_relevent();
draw_model_rotating(model_render_params * render_info,int model_id,int x1,int y1,int x2,int y2,float * rotation_buffer,vec3d * closeup_pos,float closeup_zoom,float rev_rate,int flags,int resize_mode,int effect)1646 void draw_model_rotating(model_render_params *render_info, int model_id, int x1, int y1, int x2, int y2, float *rotation_buffer, vec3d *closeup_pos, float closeup_zoom, float rev_rate, int flags, int resize_mode, int effect)
1647 {
1648 	//WMC - Can't draw a non-model
1649 	if (model_id < 0)
1650 		return;
1651 
1652 	float time = (timer_get_milliseconds()-anim_timer_start)/1000.0f;
1653 	angles rot_angles, view_angles;
1654 	matrix model_orient;
1655 
1656 	if (effect == 2) {  // FS2 Effect; Phase 0 Expand scanline, Phase 1 scan the grid and wireframe, Phase 2 scan up and reveal the ship, Phase 3 tilt the camera, Phase 4 start rotating the ship
1657 		// rotate the ship as much as required for this frame
1658 		if (time >= 3.6f) // Phase 4
1659 			*rotation_buffer += PI2 * flFrametime / rev_rate;
1660 		else
1661 			*rotation_buffer = PI; // No rotation before Phase 4
1662 		while (*rotation_buffer > PI2){
1663 			*rotation_buffer -= PI2;
1664 		}
1665 
1666 		view_angles.p = -PI_2;
1667 
1668 		if (time >= 3.0f) { // Phase 3
1669 			if (time >= 3.6f) { // done tilting
1670 				view_angles.p = -0.6f;
1671 			} else {
1672 				view_angles.p = (PI_2-0.6f)*(time-3.0f)*1.66667f - PI_2; // Phase 3 Tilt animation
1673 			}
1674 		}
1675 
1676 		view_angles.b = 0.0f;
1677 		view_angles.h = 0.0f;
1678 		vm_angles_2_matrix(&model_orient, &view_angles);
1679 		rot_angles.p = 0.0f;
1680 		rot_angles.b = 0.0f;
1681 		rot_angles.h = *rotation_buffer;
1682 		vm_rotate_matrix_by_angles(&model_orient, &rot_angles);
1683 		gr_set_clip(x1, y1, x2, y2, resize_mode);
1684 		vec3d wire_normal,ship_normal,plane_point;
1685 
1686 		// Clip the wireframe below the scanline
1687 		wire_normal.xyz.x = 0.0f;
1688 		wire_normal.xyz.y = 1.0f;
1689 		wire_normal.xyz.z = 0.0f;
1690 
1691 		// Clip the ship above the scanline
1692 		ship_normal.xyz.x = 0.0f;
1693 		ship_normal.xyz.y = -1.0f;
1694 		ship_normal.xyz.z = 0.0f;
1695 		polymodel *pm = model_get(model_id);
1696 
1697 		//Make the clipping plane
1698 		float clip = -pm->rad*0.7f;
1699 		if (time < 1.5f && time >= 0.5f) // Phase 1 Move down
1700 			clip = pm->rad*(time-1.0f)*1.4f;
1701 
1702 		if (time >= 1.5f)
1703 			clip = pm->rad*(time-2.0f)*(-1.4f); // Phase 2 Move up
1704 
1705 		vm_vec_scale_sub(&plane_point,&vmd_zero_vector,&wire_normal,clip);
1706 
1707 		g3_start_frame(1);
1708 		if ( (closeup_pos != NULL) && (vm_vec_mag(closeup_pos) > 0.0f) ) {
1709 			g3_set_view_matrix(closeup_pos, &vmd_identity_matrix, closeup_zoom);
1710 		} else {
1711 			vec3d pos = { { { 0.0f, 0.0f, -(pm->rad * 1.5f) } } };
1712 			g3_set_view_matrix(&pos, &vmd_identity_matrix, closeup_zoom);
1713 		}
1714 
1715 		gr_set_proj_matrix(Proj_fov, gr_screen.clip_aspect, Min_draw_distance, Max_draw_distance);
1716 		gr_set_view_matrix(&Eye_position, &Eye_matrix);
1717 
1718 		vec3d start, stop;
1719 		float size = pm->rad*0.7f;
1720 		float start_scale = MIN(time,0.5f)*2.5f;
1721 		float offset = size*0.5f*MIN(MAX(time-3.0f,0.0f),0.6f)*1.66667f;
1722 
1723 		g3_start_instance_angles(&vmd_zero_vector,&view_angles);
1724 
1725 		if (time < 0.5f) { // Do the expanding scanline in phase 0
1726 			gr_set_color(0,255,0);
1727 			start.xyz.x = size*start_scale;
1728 			start.xyz.y = 0.0f;
1729 			start.xyz.z = -clip;
1730 			stop.xyz.x = -size*start_scale;
1731 			stop.xyz.y = 0.0f;
1732 			stop.xyz.z = -clip;
1733 			//g3_draw_htl_line(&start,&stop);
1734 			g3_render_line_3d(true, &start, &stop);
1735 		}
1736 		g3_done_instance(true);
1737 
1738 		gr_zbuffer_set(GR_ZBUFF_NONE); // Turn off Depthbuffer so we don't get gridlines over the ship or a disappearing scanline
1739 		Glowpoint_use_depth_buffer = false; // Since we don't have one
1740 		if (time >= 0.5f) { // Phase 1 onward draw the grid
1741 			int i;
1742 			start.xyz.y = -offset;
1743 			start.xyz.z = size+offset*0.5f;
1744 			stop.xyz.y = -offset;
1745 			stop.xyz.z = -size+offset*0.5f;
1746 			gr_set_color(0,200,0);
1747 			g3_start_instance_angles(&vmd_zero_vector,&view_angles);
1748 
1749 			if (time < 1.5f) {
1750 				stop.xyz.z = -clip;
1751 			}
1752 
1753 			for (i = -3; i < 4; i++) {
1754 				start.xyz.x = stop.xyz.x = size*0.333f*i;
1755 				//g3_draw_htl_line(&start,&stop);
1756 				g3_render_line_3d(false, &start, &stop);
1757 			}
1758 
1759 			start.xyz.x = size;
1760 			stop.xyz.x = -size;
1761 
1762 			for (i = 3; i > -4; i--) {
1763 				start.xyz.z = stop.xyz.z = size*0.333f*i+offset*0.5f;
1764 				if ((time < 1.5f) && (start.xyz.z <= -clip))
1765 					break;
1766 				//g3_draw_htl_line(&start,&stop);
1767 				g3_render_line_3d(false, &start, &stop);
1768 			}
1769 
1770 			g3_done_instance(true);
1771 
1772 			// lighting for techroom
1773 			light_reset();
1774 			vec3d light_dir = vmd_zero_vector;
1775 			light_dir.xyz.y = 1.0f;
1776 			light_dir.xyz.x = 0.0000001f;
1777 			light_add_directional(&light_dir, 0.65f, 1.0f, 1.0f, 1.0f);
1778 			light_rotate_all();
1779 			// lighting for techroom
1780 
1781 			// render the ships
1782 			model_clear_instance(model_id);
1783 			render_info->set_detail_level_lock(0);
1784 
1785 			gr_zbuffer_set(true);
1786 			if(Shadow_quality != ShadowQuality::Disabled)
1787             {
1788 				gr_end_view_matrix();
1789 				gr_end_proj_matrix();
1790 
1791 				gr_reset_clip();
1792 
1793 				model_render_params shadow_render_info;
1794 
1795 				shadow_render_info.set_detail_level_lock(0);
1796 				shadow_render_info.set_flags(flags | MR_NO_TEXTURING | MR_NO_LIGHTING);
1797 
1798 				if ( flags & MR_IS_MISSILE )  {
1799 					shadows_start_render(&Eye_matrix, &Eye_position, Proj_fov, gr_screen.clip_aspect, -closeup_pos->xyz.z + pm->rad, -closeup_pos->xyz.z + pm->rad + 20.0f, -closeup_pos->xyz.z + pm->rad + 200.0f, -closeup_pos->xyz.z + pm->rad + 1000.0f);
1800 				} else {
1801 					shadows_start_render(&Eye_matrix, &Eye_position, Proj_fov, gr_screen.clip_aspect, -closeup_pos->xyz.z + pm->rad, -closeup_pos->xyz.z + pm->rad + 200.0f, -closeup_pos->xyz.z + pm->rad + 2000.0f, -closeup_pos->xyz.z + pm->rad + 10000.0f);
1802 				}
1803 
1804 				model_render_immediate(&shadow_render_info, model_id, &model_orient, &vmd_zero_vector);
1805 				shadows_end_render();
1806 
1807 				gr_set_clip(x1, y1, x2, y2, resize_mode);
1808 
1809 				gr_set_proj_matrix(Proj_fov, gr_screen.clip_aspect, Min_draw_distance, Max_draw_distance);
1810 				gr_set_view_matrix(&Eye_position, &Eye_matrix);
1811             }
1812 			gr_zbuffer_set(false);
1813 			gr_set_color(80,49,160);
1814 			render_info->set_color(80, 49, 160);
1815 
1816 			render_info->set_animated_effect(ANIMATED_SHADER_LOADOUTSELECT_FS2, -clip);
1817 
1818 			if ( (time < 2.5f) && (time >= 0.5f) ) { // Phase 1 and 2 render the wireframe
1819 				if (time >= 1.5f) // Just clip the wireframe after Phase 1
1820 					render_info->set_clip_plane(plane_point,wire_normal);
1821 
1822 				render_info->set_flags(flags | MR_SHOW_OUTLINE_HTL | MR_NO_POLYS | MR_NO_TEXTURING | MR_NO_LIGHTING);
1823 
1824 				model_render_immediate(render_info, model_id, &model_orient, &vmd_zero_vector);
1825 			}
1826 
1827 			if (time >= 1.5f) { // Render the ship in Phase 2 onwards
1828 				render_info->set_clip_plane(plane_point,ship_normal);
1829 				render_info->set_flags(flags);
1830 
1831 				model_render_immediate(render_info, model_id, &model_orient, &vmd_zero_vector);
1832 			}
1833 
1834 			if (time < 2.5f) { // Render the scanline in Phase 1 and 2
1835 				gr_set_color(0,255,0);
1836 				start.xyz.x = size*1.25f;
1837 				start.xyz.y = 0.0f;
1838 				start.xyz.z = -clip;
1839 				stop.xyz.x = -size*1.25f;
1840 				stop.xyz.y = 0.0f;
1841 				stop.xyz.z = -clip;
1842 				g3_start_instance_angles(&vmd_zero_vector,&view_angles);
1843 				//g3_draw_htl_line(&start,&stop);
1844 				g3_render_line_3d(false, &start, &stop);
1845 				g3_done_instance(true);
1846 			}
1847 		}
1848 
1849 		gr_zbuffer_set(GR_ZBUFF_FULL); // Turn off depthbuffer again
1850 
1851 		batching_render_all();
1852 		Glowpoint_use_depth_buffer = true; // Back to normal
1853 
1854 		gr_end_view_matrix();
1855 		gr_end_proj_matrix();
1856 		g3_end_frame();
1857 		gr_reset_clip();
1858 
1859 	} else {
1860 		// rotate the ship as much as required for this frame
1861 		*rotation_buffer += PI2 * flFrametime / rev_rate;
1862 		while (*rotation_buffer > PI2){
1863 			*rotation_buffer -= PI2;
1864 		}
1865 
1866 		view_angles.p = -0.6f;
1867 		view_angles.b = 0.0f;
1868 		view_angles.h = 0.0f;
1869 		vm_angles_2_matrix(&model_orient, &view_angles);
1870 
1871 		rot_angles.p = 0.0f;
1872 		rot_angles.b = 0.0f;
1873 		rot_angles.h = *rotation_buffer;
1874 		vm_rotate_matrix_by_angles(&model_orient, &rot_angles);
1875 
1876 		g3_start_frame(1);
1877 
1878 		polymodel *pm = model_get(model_id);
1879 
1880 		// render the wodel
1881 		if ( (closeup_pos != NULL) && (vm_vec_mag(closeup_pos) > 0.0f) ) {
1882 			g3_set_view_matrix(closeup_pos, &vmd_identity_matrix, closeup_zoom);
1883 		} else {
1884 			vec3d pos = { { { 0.0f, 0.0f, -(pm->rad * 1.5f) } } };
1885 			g3_set_view_matrix(&pos, &vmd_identity_matrix, closeup_zoom);
1886 		}
1887 
1888 		// lighting for techroom
1889 		light_reset();
1890 		vec3d light_dir = vmd_zero_vector;
1891 		light_dir.xyz.y = 1.0f;
1892 		light_add_directional(&light_dir, 0.65f, 1.0f, 1.0f, 1.0f);
1893 		light_rotate_all();
1894 		// lighting for techroom
1895 
1896 		model_clear_instance(model_id);
1897 
1898 		render_info->set_detail_level_lock(0);
1899 
1900 		if(Shadow_quality != ShadowQuality::Disabled)
1901 		{
1902 			if ( flags & MR_IS_MISSILE )  {
1903 				shadows_start_render(&Eye_matrix, &Eye_position, Proj_fov, gr_screen.clip_aspect, -closeup_pos->xyz.z + pm->rad, -closeup_pos->xyz.z + pm->rad + 20.0f, -closeup_pos->xyz.z + pm->rad + 200.0f, -closeup_pos->xyz.z + pm->rad + 1000.0f);
1904 			} else {
1905 				shadows_start_render(&Eye_matrix, &Eye_position, Proj_fov, gr_screen.clip_aspect, -closeup_pos->xyz.z + pm->rad, -closeup_pos->xyz.z + pm->rad + 200.0f, -closeup_pos->xyz.z + pm->rad + 2000.0f, -closeup_pos->xyz.z + pm->rad + 10000.0f);
1906 			}
1907 
1908 			model_render_params shadow_render_info;
1909 
1910 			shadow_render_info.set_flags(flags | MR_NO_TEXTURING | MR_NO_LIGHTING);
1911 			shadow_render_info.set_detail_level_lock(0);
1912 
1913 			model_render_immediate(&shadow_render_info, model_id, &model_orient, &vmd_zero_vector);
1914 			shadows_end_render();
1915 		}
1916 
1917 		gr_set_clip(x1, y1, x2, y2, resize_mode);
1918 
1919 		gr_set_proj_matrix(Proj_fov, gr_screen.clip_aspect, Min_draw_distance, Max_draw_distance);
1920 		gr_set_view_matrix(&Eye_position, &Eye_matrix);
1921 
1922 		gr_set_color(0,128,0);
1923 
1924 		if (effect == 1) { // FS1 effect
1925 			render_info->set_animated_effect(ANIMATED_SHADER_LOADOUTSELECT_FS1, MIN(time*0.5f,2.0f));
1926 			render_info->set_flags(flags);
1927 		} else {
1928 			render_info->set_flags(flags);
1929 		}
1930 
1931 		model_render_immediate(render_info, model_id, &model_orient, &vmd_zero_vector);
1932 
1933 		batching_render_all();
1934 
1935 		gr_end_view_matrix();
1936 		gr_end_proj_matrix();
1937 		g3_end_frame();
1938 		gr_reset_clip();
1939 	}
1940 }
1941 
1942 // NEWSTUFF END
1943