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 "mission/missionbriefcommon.h"
14 #include "mission/missionparse.h"
15 #include "parse/parselo.h"
16 #include "playerman/player.h"
17 #include "ship/ship.h"
18 #include "render/3d.h"
19 #include "io/timer.h"
20 #include "globalincs/linklist.h"
21 #include "io/mouse.h"
22 #include "missionui/missionbrief.h"
23 #include "mission/missiongrid.h"
24 #include "anim/animplay.h"
25 #include "math/fvi.h"
26 #include "gamesnd/gamesnd.h"
27 #include "sound/audiostr.h"
28 #include "missionui/missioncmdbrief.h"
29 #include "missionui/missiondebrief.h"
30 #include "globalincs/alphacolors.h"
31 #include "localization/localize.h"
32 #include "sound/fsspeech.h"
33 #include "species_defs/species_defs.h"
34 #include "iff_defs/iff_defs.h"
35 
36 
37 // --------------------------------------------------------------------------------------
38 // briefing screen
39 // --------------------------------------------------------------------------------------
40 
41 brief_screen bscreen;
42 
43 // briefing screen sections
44 #define BRIEF_CUP_X1			400
45 #define BRIEF_CUP_Y1			70
46 #define BRIEF_CUP_X2			639
47 #define BRIEF_CUP_Y2			245
48 #define BRIEF_CUPINFO_X1	445
49 #define BRIEF_CUPINFO_Y1	247
50 #define BRIEF_CUPINFO_X2	639
51 #define BRIEF_CUPINFO_Y2	438
52 
53 char *Brief_static_name[GR_NUM_RESOLUTIONS] = {
54 	"BriefMap",
55 	"2_BriefMap"
56 };
57 
58 int Brief_static_coords[GR_NUM_RESOLUTIONS][2] = {
59 	{ // GR_640
60 		10, 130
61 	},
62 	{ // GR_1024
63 		15, 208
64 	}
65 };
66 
67 int Brief_bmap_coords[GR_NUM_RESOLUTIONS][2] = {
68 	{ // GR_640
69 		0, 115
70 	},
71 	{ // GR_1024
72 		0, 184
73 	}
74 };
75 
76 int Brief_grid_coords[GR_NUM_RESOLUTIONS][4] = {
77 	{ // GR_640
78 		19, 147, 555, 232
79 	},
80 	{ // GR_1024
81 		30, 235, 888, 371
82 	}
83 };
84 
85 int Brief_text_coords[GR_NUM_RESOLUTIONS][4] = {
86 	{ // GR_640
87 		28, 399, 395, 74
88 	},
89 	{ // GR_1024
90 		46, 637, 630, 120
91 	}
92 };
93 
94 int Brief_stage_text_coords[GR_NUM_RESOLUTIONS][2] = {
95 	{ // GR_640
96 		138, 117
97 	},
98 	{ // GR_1024
99 		227, 194
100 	}
101 };
102 
103 int Brief_stage_text_coords_multi[GR_NUM_RESOLUTIONS][2] = {
104 	{ // GR_640
105 		479, 385
106 	},
107 	{ // GR_1024
108 		821, 616
109 	}
110 };
111 
112 int Brief_text_max_lines[GR_NUM_RESOLUTIONS] = {
113 	8, 13
114 };
115 
116 #define LOOKAT_DIST	500.0f
117 #define STAGE_ADVANCE_DELAY	1000		// time in ms to wait after voice stops before advancing stage
118 
119 // --------------------------------------------------------------------------------------
120 // Game-wide global data
121 // --------------------------------------------------------------------------------------
122 briefing		Briefings[MAX_TVT_TEAMS];			// there is exactly one briefing per mission
123 debriefing	Debriefings[MAX_TVT_TEAMS];			// there can be multiple debriefings per mission
124 briefing		*Briefing;							// pointer used in code -- points to correct briefing
125 debriefing	*Debriefing;						// pointer to correct debriefing
126 
127 int			Briefing_voice_enabled=1;		// flag which turn on/off voice playback of briefings/debriefings
128 
129 // --------------------------------------------------------------------------------------
130 // Module global data
131 // --------------------------------------------------------------------------------------
132 
133 static int Last_new_stage;
134 int	Cur_brief_id;
135 
136 const char BRIEF_META_CHAR = '$';
137 
138 // camera related
139 static vec3d	Current_cam_pos;		// current camera position
140 static vec3d	Target_cam_pos;		// desired camera position
141 static matrix	Current_cam_orient;	// current camera orientation
142 static matrix	Target_cam_orient;	// desired camera orientation
143 static matrix	Start_cam_orient;		// start camera orientation
144 static vec3d	Start_cam_pos;			// position of camera at the start of a translation
145 static vec3d	Cam_vel;					//	camera velocity
146 static vec3d	Current_lookat_pos;	// lookat point
147 static vec3d	Target_lookat_pos;	// lookat point
148 static vec3d	Start_lookat_pos;
149 static vec3d	Lookat_vel;				//	lookat point velocity
150 
151 static float	Start_cam_move;		// time at which camera started moving (seconds)
152 static float	Total_move_time;		// time in which camera should move from current pos to target pos (seconds)
153 static float	Elapsed_time;
154 
155 static float	Start_dist;
156 static float	End_dist;
157 static float	Dist_change_rate;
158 
159 static vec3d	Acc_limit;
160 static vec3d	Vel_limit;
161 
162 static float	Total_dist;
163 static float	Peak_speed;
164 static float	Cam_accel;
165 static float	Last_dist;
166 static vec3d	W_init;
167 
168 // flag to indicate that the sound for a spinning highlight animation has played
169 static int Brief_stage_highlight_sound_handle = -1;
170 
171 // used for scrolling briefing text ( if necessary )
172 int		Num_brief_text_lines[MAX_TEXT_STREAMS];
173 int		Top_brief_text_line;
174 
175 // Used to support drawing colored text for the briefing.  Gets complicates since we
176 // need to be able to draw one character at a time as well when the briefing text
177 // first appears.
178 typedef struct colored_char
179 {
180 	char	letter;
181 	ubyte	color;		// index into Brief_text_colors[]
182 } colored_char;
183 
184 typedef SCP_vector<colored_char> briefing_line;
185 typedef SCP_vector<briefing_line> briefing_stream;
186 static briefing_stream Colored_stream[MAX_TEXT_STREAMS];
187 
188 #define MAX_BRIEF_TEXT_COLORS		20
189 #define BRIEF_TEXT_WHITE			0
190 #define BRIEF_TEXT_BRIGHT_WHITE		1
191 #define BRIEF_TEXT_RED				2
192 #define BRIEF_TEXT_GREEN			3
193 #define BRIEF_TEXT_YELLOW			4
194 #define BRIEF_TEXT_BLUE				5
195 #define BRIEF_TEXT_FRIENDLY			6
196 #define BRIEF_TEXT_HOSTILE			7
197 #define BRIEF_TEXT_NEUTRAL			8
198 #define BRIEF_TEXT_BRIGHT_BLUE		9
199 #define BRIEF_TEXT_BRIGHT_GREEN		10
200 #define BRIEF_TEXT_BRIGHT_RED		11
201 #define BRIEF_TEXT_BRIGHT_YELLOW	12
202 #define BRIEF_TEXT_BLACK			13
203 #define BRIEF_TEXT_GREY				14
204 #define BRIEF_TEXT_SILVER			15
205 #define BRIEF_TEXT_VIOLET_GRAY		16
206 #define BRIEF_TEXT_VIOLET			17
207 #define BRIEF_TEXT_PINK				18
208 #define BRIEF_TEXT_LIGHT_PINK		19
209 
210 color Brief_color_red, Brief_color_green, Brief_color_legacy_neutral;
211 
212 color *Brief_text_colors[MAX_BRIEF_TEXT_COLORS] =
213 {
214 	&Color_white,
215 	&Color_bright_white,
216 	&Color_red,
217 	&Color_green,
218 	&Color_yellow,
219 	&Color_blue,
220 	&Brief_color_green,
221 	&Brief_color_red,
222 	&Brief_color_legacy_neutral,
223 	&Color_bright_blue,
224 	&Color_bright_green,
225 	&Color_bright_red,
226 	&Color_bright_yellow,
227 	&Color_black,
228 	&Color_grey,
229 	&Color_silver,
230 	&Color_violet_gray,
231 	&Color_violet,
232 	&Color_pink,
233 	&Color_light_pink,
234 };
235 
236 #define BRIGHTEN_LEAD	2
237 
238 float Brief_text_wipe_time_elapsed;
239 static int Max_briefing_line_len;
240 
241 static int Voice_started_time;
242 static int Voice_ended_time;
243 
244 const float		BRIEF_TEXT_WIPE_TIME	= 1.5f;		// time in seconds for wipe to occur
245 static int		Brief_text_wipe_snd;					// sound handle of sound effect for text wipe
246 static int		Play_brief_voice;
247 
248 // animation stuff
249 static int		Play_highlight_flag;
250 static int		Cam_target_reached;
251 static int		Cam_movement_done;
252 
253 // moving icons
254 typedef struct icon_move_info
255 {
256 	icon_move_info	*next, *prev;
257 	int				used;
258 	int				id;
259 	vec3d			start;
260 	vec3d			finish;
261 	vec3d			current;
262 
263 	// used to move icons smoothly
264 	vec3d			direction;
265 	float				total_dist;
266 	float				accel;
267 	float				total_move_time;
268 	float				peak_speed;
269 	int				reached_dest;
270 	float				last_dist;
271 } icon_move_info;
272 
273 #define MAX_MOVING_ICONS	MAX_STAGE_ICONS
274 
275 icon_move_info	Icon_movers[MAX_MOVING_ICONS];
276 icon_move_info	Icon_move_list;	// head of linked list
277 
278 // fading out icons
279 typedef struct icon_fade_info
280 {
281 	hud_anim	fade_anim;
282 	vec3d	pos;
283 	int		team;
284 } fade_icon;
285 
286 #define		MAX_FADING_ICONS	MAX_STAGE_ICONS
287 
288 icon_fade_info	Fading_icons[MAX_FADING_ICONS];
289 int				Num_fade_icons;
290 
291 // voice id's for briefing text
292 int Brief_voices[MAX_BRIEF_STAGES];
293 
294 cmd_brief *Cur_cmd_brief;
295 cmd_brief Cmd_briefs[MAX_TVT_TEAMS];
296 
297 SCP_vector<briefing_icon_info> Briefing_icon_info;
298 
299 // --------------------------------------------------------------------------------------
300 // forward declarations
301 // --------------------------------------------------------------------------------------
302 void	brief_render_elements(vec3d *pos, grid *gridp);
303 void	brief_render_icons(int stage_num, float frametime);
304 void	brief_grid_read_camera_controls( control_info * ci, float frametime );
305 void	brief_maybe_create_new_grid(grid *gridp, vec3d *pos, matrix *orient, int force = 0);
306 grid	*brief_create_grid(grid *gridp, vec3d *forward, vec3d *right, vec3d *center, int nrows, int ncols, float square_size);
307 grid	*brief_create_default_grid(void);
308 void	brief_render_grid(grid *gridp);
309 void	brief_modify_grid(grid *gridp);
310 void	brief_rpd_line(vec3d *v0, vec3d *v1);
311 void	brief_set_text_color(int color_index);
312 extern void get_camera_limits(matrix *start_camera, matrix *end_camera, float time, vec3d *acc_max, vec3d *w_max);
313 int brief_text_wipe_finished();
314 
315 // --------------------------------------------------------------------------------------
316 //	brief_parse_icon_tbl()
317 //
318 //
brief_parse_icon_tbl()319 void brief_parse_icon_tbl()
320 {
321 	int rval, icon;
322 	size_t species;
323 	char name[MAX_FILENAME_LEN];
324 
325 	Assert(!Species_info.empty());
326 	const size_t max_icons = Species_info.size() * MIN_BRIEF_ICONS;
327 
328 	// open localization
329 	lcl_ext_open();
330 
331 	if ((rval = setjmp(parse_abort)) != 0) {
332 		mprintf(("TABLES: Unable to parse '%s'!  Error code = %i.\n", "icons.tbl", rval));
333 		lcl_ext_close();
334 
335 		return;
336 	}
337 
338 	read_file_text("icons.tbl", CF_TYPE_TABLES);
339 	reset_parse();
340 
341 	required_string("#Start");
342 
343 	Briefing_icon_info.clear();
344 	while (required_string_either("#End","$Name:"))
345 	{
346 		if(Briefing_icon_info.size() >= max_icons) {
347 			Warning(LOCATION, "Too many icons in icons.tbl; only the first %d will be used", max_icons);
348 			skip_to_start_of_string("#End");
349 			break;
350 		}
351 
352 		briefing_icon_info bii;
353 
354 		// parse regular frames
355 		required_string("$Name:");
356 		stuff_string(name, F_NAME, MAX_FILENAME_LEN);
357 		generic_anim_init(&bii.regular, name);
358 
359 		// parse fade frames
360 		required_string("$Name:");
361 		stuff_string(name, F_NAME, MAX_FILENAME_LEN);
362 		hud_anim_init(&bii.fade, 0, 0, name);
363 
364 		// parse highlighting frames
365 		required_string("$Name:");
366 		stuff_string(name, F_NAME, MAX_FILENAME_LEN);
367 		hud_anim_init(&bii.highlight, 0, 0, name);
368 
369 		// add it to the collection
370 		Briefing_icon_info.push_back(bii);
371 	}
372 	required_string("#End");
373 
374 	// close localization
375 	lcl_ext_close();
376 
377 
378 	// now assign the icons to their species
379 	const size_t num_species_covered = Briefing_icon_info.size() / MIN_BRIEF_ICONS;
380 	size_t bii_index = 0;
381 	for (icon = 0; icon < MIN_BRIEF_ICONS; icon++)
382 	{
383 		for (species = 0; species < num_species_covered; species++)
384 			Species_info[species].bii_index[icon] = bii_index++;
385 	}
386 
387 	// error check
388 	if (num_species_covered < Species_info.size())
389 	{
390 		SCP_string errormsg = "The following species are missing icon info in icons.tbl:\n";
391 
392 		for (species = num_species_covered; species < Species_info.size(); species++)
393 		{
394 			errormsg += Species_info[species].species_name;
395 			errormsg += "\n";
396 		}
397 
398 		Error(LOCATION, errormsg.c_str());
399 	}
400 }
401 
brief_set_icon_color(int team)402 void brief_set_icon_color(int team)
403 {
404 	gr_set_color_fast(iff_get_color_by_team(team, -1, 0));
405 }
406 
407 // --------------------------------------------------------------------------------------
408 //	brief_move_icon_reset()
409 //
410 //
brief_move_icon_reset()411 void brief_move_icon_reset()
412 {
413 	int i;
414 
415 	list_init(&Icon_move_list);
416 	for ( i = 0; i < MAX_MOVING_ICONS; i++ )
417 		Icon_movers[i].used = 0;
418 }
419 
420 
421 /**
422  * Does one time initialization of the briefing and debriefing structures.
423  * Namely setting all malloc'ble pointers to NULL.  Called once at game startup.
424  */
mission_brief_common_init()425 void mission_brief_common_init()
426 {
427 	int i,j;
428 
429 	// setup brief text colors
430 	gr_init_alphacolor( &Brief_color_green, 50, 100, 50, 255 );
431 	gr_init_alphacolor( &Brief_color_red, 140, 20, 20, 255 );
432 	gr_init_alphacolor( &Brief_color_legacy_neutral, 255, 0, 0, iff_get_alpha_value(false));
433 
434 	// extra catch to reset everything that's already loaded - taylor
435 	mission_brief_common_reset();
436 	mission_debrief_common_reset();
437 
438 	if ( Fred_running )	{
439 		// If Fred is running malloc out max space
440 		for (i = 0; i < MAX_TVT_TEAMS; i++) {
441 			for (j = 0; j < MAX_BRIEF_STAGES; j++) {
442 				Briefings[i].stages[j].text = "";
443 
444 				if (Briefings[i].stages[j].icons == NULL) {
445 					Briefings[i].stages[j].icons = (brief_icon *)vm_malloc(sizeof(brief_icon) * MAX_STAGE_ICONS);
446 					Assert( Briefings[i].stages[j].icons != NULL );
447 					memset( Briefings[i].stages[j].icons, 0, sizeof(brief_icon) * MAX_STAGE_ICONS );
448 				}
449 
450 				if (Briefings[i].stages[j].lines == NULL) {
451 					Briefings[i].stages[j].lines = (brief_line *)vm_malloc(sizeof(brief_line) * MAX_BRIEF_STAGE_LINES);
452 					Assert( Briefings[i].stages[j].lines != NULL );
453 					memset( Briefings[i].stages[j].lines, 0, sizeof(brief_line) * MAX_BRIEF_STAGE_LINES );
454 				}
455 
456 				Briefings[i].stages[j].num_icons = 0;
457 				Briefings[i].stages[j].num_lines = 0;
458 			}
459 		}
460 
461 		for (i = 0; i < MAX_TVT_TEAMS; i++) {
462 			for (j = 0; j < MAX_DEBRIEF_STAGES; j++) {
463 				Debriefings[i].stages[j].text = "";
464 				Debriefings[i].stages[j].recommendation_text = "";
465 			}
466 		}
467 
468 	} else {
469 		// If game is running don't malloc anything
470 		for (i=0; i<MAX_TVT_TEAMS; i++ )	{
471 			for (j=0; j<MAX_BRIEF_STAGES; j++ )	{
472 				Briefings[i].stages[j].text = "";
473 				Briefings[i].stages[j].num_icons = 0;
474 				Briefings[i].stages[j].icons = NULL;
475 				Briefings[i].stages[j].num_lines = 0;
476 				Briefings[i].stages[j].lines = NULL;
477 			}
478 		}
479 
480 		for (i=0; i<MAX_TVT_TEAMS; i++ )	{
481 			for (j=0; j<MAX_DEBRIEF_STAGES; j++ )	{
482 				Debriefings[i].stages[j].text = "";
483 				Debriefings[i].stages[j].recommendation_text = "";
484 			}
485 		}
486 
487 	}
488 }
489 
490 /**
491  * Frees all the memory allocated in the briefing and debriefing structures and sets all pointers to NULL.
492  */
mission_brief_common_reset()493 void mission_brief_common_reset()
494 {
495 	int i, j;
496 
497 	for (i = 0; i < MAX_TVT_TEAMS; i++) {
498 		Briefings[i].num_stages = 0;
499 
500 		for (j = 0; j < MAX_BRIEF_STAGES; j++) {
501 			Briefings[i].stages[j].num_icons = 0;
502 			Briefings[i].stages[j].num_lines = 0;
503 			Briefings[i].stages[j].text = "";
504 
505 			if (Fred_running) {
506 				if ( Briefings[i].stages[j].icons ) {
507 					memset( Briefings[i].stages[j].icons, 0, sizeof(brief_icon) * MAX_STAGE_ICONS );
508 					Briefings[i].stages[j].icons->ship_class = -1;
509 					Briefings[i].stages[j].icons->modelnum = -1;
510 					Briefings[i].stages[j].icons->bitmap_id = -1;
511 				}
512 
513 				if ( Briefings[i].stages[j].lines )
514 					memset( Briefings[i].stages[j].lines, 0, sizeof(brief_line) * MAX_BRIEF_STAGE_LINES );
515 			} else {
516 				if ( Briefings[i].stages[j].icons )	{
517 					vm_free(Briefings[i].stages[j].icons);
518 					Briefings[i].stages[j].icons = NULL;
519 				}
520 
521 				if ( Briefings[i].stages[j].lines )	{
522 					vm_free(Briefings[i].stages[j].lines);
523 					Briefings[i].stages[j].lines = NULL;
524 				}
525 			}
526 		}
527 	}
528 }
529 
530 /**
531  * Split from above since we need to clear them separately
532  * @see mission_brief_common_reset()
533  */
mission_debrief_common_reset()534 void mission_debrief_common_reset()
535 {
536 	int i, j;
537 
538 	for (i = 0; i < MAX_TVT_TEAMS; i++) {
539 		Debriefings[i].num_stages = 0;
540 
541 		for (j = 0; j < MAX_DEBRIEF_STAGES; j++) {
542 			Debriefings[i].stages[j].text = "";
543 			Debriefings[i].stages[j].recommendation_text = "";
544 		}
545 	}
546 }
547 
548 
549 
550 
551 // --------------------------------------------------------------------------------------
552 //	brief_reset()
553 //
554 //
brief_reset()555 void brief_reset()
556 {
557 	mission_brief_common_reset();
558 
559 	Briefing = NULL;
560 	Cur_brief_id = 1;
561 }
562 
563 // --------------------------------------------------------------------------------------
564 //	debrief_reset()
565 //
566 //
debrief_reset()567 void debrief_reset()
568 {
569 	mission_debrief_common_reset();
570 
571 	Debriefing = NULL;
572 
573 	// MWA 4/27/98 -- must initialize this variable here since we cannot do it as debrief
574 	// init time because race conditions between all players in the game make that type of
575 	// initialization unsafe.
576 	Debrief_multi_stages_loaded = 0;
577 }
578 
579 /**
580  * Set up the screen regions.  A mulitplayer briefing will look different than a single player briefing.
581  */
brief_init_screen(int multiplayer_flag)582 void brief_init_screen(int multiplayer_flag)
583 {
584 	bscreen.map_x1			= Brief_grid_coords[gr_screen.res][0];
585 	bscreen.map_x2			= Brief_grid_coords[gr_screen.res][0] + Brief_grid_coords[gr_screen.res][2];
586 	bscreen.map_y1			= Brief_grid_coords[gr_screen.res][1];
587 	bscreen.map_y2			= Brief_grid_coords[gr_screen.res][1] + Brief_grid_coords[gr_screen.res][3];
588 }
589 
590 // --------------------------------------------------------------------------------------
591 //	brief_init_colors()
592 //
593 //
brief_init_colors()594 void brief_init_colors()
595 {
596 }
597 
brief_get_icon_info(brief_icon * bi)598 briefing_icon_info *brief_get_icon_info(brief_icon *bi)
599 {
600 	if (bi->ship_class < 0)
601 		return NULL;
602 	ship_info *sip = &Ship_info[bi->ship_class];
603 
604 	// ship info might override the usual briefing icon
605 	if (sip->bii_index_ship >= 0)
606 	{
607 		if (bi->flags & BI_USE_WING_ICON)
608 		{
609 			if (bi->flags & BI_USE_CARGO_ICON)
610 			{
611 				if (sip->bii_index_wing_with_cargo >= 0)
612 					return &Briefing_icon_info[sip->bii_index_wing_with_cargo];
613 				else
614 					mprintf(("Ship '%s' is missing the wing-with-cargo briefing icon!", sip->name));
615 			}
616 			else
617 			{
618 				if (sip->bii_index_wing >= 0)
619 					return &Briefing_icon_info[sip->bii_index_wing];
620 				else
621 					mprintf(("Ship '%s' is missing the wing briefing icon!", sip->name));
622 			}
623 		}
624 		else
625 		{
626 			if (bi->flags & BI_USE_CARGO_ICON)
627 			{
628 				if (sip->bii_index_ship_with_cargo >= 0)
629 					return &Briefing_icon_info[sip->bii_index_ship_with_cargo];
630 				else
631 					mprintf(("Ship '%s' is missing the ship-with-cargo briefing icon!", sip->name));
632 			}
633 		}
634 
635 		// this will be reached if we just want the plain ship icon, or if we specified icon modifiers which didn't exist
636 		return &Briefing_icon_info[sip->bii_index_ship];
637 	}
638 
639 	if (sip->species < 0)
640 		return NULL;
641 
642 	int bii_index = Species_info[sip->species].bii_index[bi->type];
643 	if (bii_index < 0)
644 		return NULL;
645 
646 	return &Briefing_icon_info[bii_index];
647 }
648 
brief_preload_icon_anim(brief_icon * bi)649 void brief_preload_icon_anim(brief_icon *bi)
650 {
651 	briefing_icon_info *bii = brief_get_icon_info(bi);
652 	if (bii == NULL)
653 		return;
654 
655 	generic_anim *ga = &bii->regular;
656 	if ( !stricmp(NOX("none"), ga->filename) )
657 		return;
658 
659 	// force read of data from disk, so we don't glitch on initial playback
660 	if ( ga->first_frame == -1 ) {
661 		ga->first_frame = bm_load_animation(ga->filename, &ga->num_frames);
662 		Assert(ga->first_frame >= 0);
663 	}
664 }
665 
brief_preload_fade_anim(brief_icon * bi)666 void brief_preload_fade_anim(brief_icon *bi)
667 {
668 	briefing_icon_info *bii = brief_get_icon_info(bi);
669 	if (bii == NULL)
670 		return;
671 
672 	hud_anim *ha = &bii->fade;
673 	if ( !stricmp(NOX("none"), ha->filename) )
674 		return;
675 
676 	// force read of data from disk, so we don't glitch on initial playback
677 	if ( ha->first_frame == -1 ) {
678 		hud_anim_load(ha);
679 		Assert(ha->first_frame >= 0);
680 	}
681 
682 	gr_set_bitmap(ha->first_frame);
683 	gr_aabitmap(0, 0);
684 }
685 
brief_preload_highlight_anim(brief_icon * bi)686 void brief_preload_highlight_anim(brief_icon *bi)
687 {
688 	briefing_icon_info *bii = brief_get_icon_info(bi);
689 	if (bii == NULL)
690 		return;
691 
692 	hud_anim *ha = &bii->highlight;
693 	if ( !stricmp(NOX("none"), ha->filename) )
694 		return;
695 
696 	// force read of data from disk, so we don't glitch on initial playback
697 	if ( ha->first_frame == -1 ) {
698 		hud_anim_load(ha);
699 		Assert(ha->first_frame >= 0);
700 	}
701 
702 	bi->highlight_anim = *ha;
703 
704 	gr_set_bitmap(ha->first_frame);
705 	gr_aabitmap(0, 0);
706 }
707 
708 /**
709  * Preload highlight, fadein and fadeout animations that are used in each stage
710  */
brief_preload_anims()711 void brief_preload_anims()
712 {
713 	int			num_icons, num_stages, i, j;
714 	brief_icon	*bi;
715 
716 	num_stages = Briefing->num_stages;
717 
718 	for ( i = 0; i < num_stages; i++ ) {
719 		num_icons = Briefing->stages[i].num_icons;
720 		for ( j = 0; j < num_icons; j++ ) {
721 			bi = &Briefing->stages[i].icons[j];
722 
723 			brief_preload_icon_anim(bi);
724 			brief_preload_fade_anim(bi);
725 			if ( bi->flags & BI_HIGHLIGHT ) {
726 				brief_preload_highlight_anim(bi);
727 			}
728 		}
729 	}
730 }
731 
732 // --------------------------------------------------------------------------------------
733 //	brief_init_map()
734 //
735 //
brief_init_map()736 void brief_init_map()
737 {
738 	vec3d *pos;
739 	matrix *orient;
740 
741 	Assert( Briefing != NULL );
742 
743 	pos = &Briefing->stages[0].camera_pos;
744 	orient = &Briefing->stages[0].camera_orient;
745 	vm_vec_zero(&Current_lookat_pos);
746 	vm_vec_zero(&Target_lookat_pos);
747 	Elapsed_time = 0.0f;
748 	Total_move_time = 0.0f;
749 
750 	The_grid = brief_create_default_grid();
751 	brief_maybe_create_new_grid(The_grid, pos, orient, 1);
752 
753 	brief_init_colors();
754 	brief_move_icon_reset();
755 
756 	brief_preload_anims();
757 
758 	Brief_text_wipe_snd = -1;
759 	Last_new_stage = -1;
760 	Num_fade_icons=0;
761 }
762 
763 
764 #pragma optimize("", off)
765 
brief_render_fade_outs(float frametime)766 void brief_render_fade_outs(float frametime)
767 {
768 	int			i,bx,by,w,h;
769 	float			bxf,byf;
770 	vertex		tv;			// temp vertex used to find screen position for text
771 	fade_icon	*fi;
772 
773 
774 	for (i=0; i<Num_fade_icons; i++) {
775 		fi = &Fading_icons[i];
776 
777 		g3_rotate_vertex(&tv, &fi->pos);
778 
779 		if (!(tv.flags & PF_PROJECTED))
780 			g3_project_vertex(&tv);
781 
782 		if (!(tv.flags & PF_OVERFLOW) ) {  // make sure point projected before drawing text
783 
784 			brief_set_icon_color(fi->team);
785 
786 			if ( fi->fade_anim.first_frame < 0 ) {
787 				continue;
788 			}
789 
790 			bm_get_info( fi->fade_anim.first_frame, &w, &h, NULL);
791 			float screenX = tv.screen.xyw.x;
792 			float screenY = tv.screen.xyw.y;
793 			gr_unsize_screen_posf( &screenX, &screenY, NULL, NULL, GR_RESIZE_MENU_NO_OFFSET );
794 
795 			bxf = screenX - w / 2.0f + 0.5f;
796 			byf = screenY - h / 2.0f + 0.5f;
797 			bx = fl2i(bxf);
798 			by = fl2i(byf);
799 
800 			if ( fi->fade_anim.first_frame >= 0 ) {
801 				fi->fade_anim.sx = bx;
802 				fi->fade_anim.sy = by;
803 				hud_anim_render(&fi->fade_anim, frametime, 1, 0, 0, 0, GR_RESIZE_MENU);
804 			}
805 		}
806 	}
807 }
808 
809 /**
810  * Figure out how far an icon should move based on the elapsed time
811  */
brief_icon_get_dist_moved(icon_move_info * mi,float elapsed_time)812 float brief_icon_get_dist_moved(icon_move_info *mi, float elapsed_time)
813 {
814 	float time, dist_moved=0.0f;
815 
816 	// first half of movement
817 	if ( elapsed_time < mi->total_move_time/2.0f ) {
818 		dist_moved=0.5f*mi->accel*elapsed_time*elapsed_time;	// d = 1/2at^2
819 		return dist_moved;
820 	}
821 
822 	// second half of movement
823 	time=elapsed_time - mi->total_move_time/2.0f;
824 	dist_moved=(mi->total_dist/2.0f)+(mi->peak_speed*time) - 0.5f*mi->accel*time*time;
825 	return dist_moved;
826 }
827 
828 /**
829  * Draw a line between two icons on the briefing screen
830  */
brief_render_icon_line(int stage_num,int line_num)831 void brief_render_icon_line(int stage_num, int line_num)
832 {
833 	brief_line	*bl;
834 	brief_stage *bs;
835 	brief_icon	*icon[2];
836 	int			i;
837 	vertex		icon_vertex[2];
838 	int			icon_status[2] = {0,0};
839 	int			icon_w, icon_h;
840 	float			icon_x[2], icon_y[2];
841 
842 	bl = &Briefing->stages[stage_num].lines[line_num];
843 	bs = &Briefing->stages[stage_num];
844 
845 	if(bl->start_icon < 0 || bl->start_icon >= bs->num_icons)
846 	{
847 		Warning(LOCATION, "Start icon (%d/%d) missing for line %d in briefing stage %d", bl->start_icon, bs->num_icons, line_num, stage_num);
848 		//Remove line
849 		bs->num_lines--;
850 		for(i = line_num; i < bs->num_lines; i++)
851 			bs->lines[i] = bs->lines[i+1];
852 		return;
853 	}
854 	if(bl->end_icon < 0 || bl->end_icon >= Briefing->stages[stage_num].num_icons)
855 	{
856 		Warning(LOCATION, "End icon (%d/%d) missing for line %d in briefing stage %d", bl->end_icon, bs->num_icons, line_num, stage_num);
857 		//Remove line
858 		bs->num_lines--;
859 		for(i = line_num; i < bs->num_lines; i++)
860 			bs->lines[i] = bs->lines[i+1];
861 		return;
862 	}
863 
864 	icon[0] = &Briefing->stages[stage_num].icons[bl->start_icon];
865 	icon[1] = &Briefing->stages[stage_num].icons[bl->end_icon];
866 
867 	// project icons
868 	for (i=0; i<2; i++) {
869 		g3_rotate_vertex(&icon_vertex[i],&icon[i]->pos);
870 		if (!(icon_vertex[i].flags&PF_PROJECTED))
871 			g3_project_vertex(&icon_vertex[i]);
872 
873 		if (!(icon_vertex[i].flags & PF_OVERFLOW) ) {  // make sure point projected before drawing text
874 			icon_status[i]=1;
875 		}
876 	}
877 
878 	if ( !icon_status[0] || !icon_status[1] ) {
879 		return;
880 	}
881 
882 	// get screen (x,y) for icons
883 	for (i=0; i<2; i++) {
884 		brief_common_get_icon_dimensions(&icon_w, &icon_h, icon[i]);
885 		icon_x[i] = icon_vertex[i].screen.xyw.x;
886 		icon_y[i] = icon_vertex[i].screen.xyw.y;
887 	}
888 
889 	brief_set_icon_color(icon[0]->team);
890 
891 	gr_line(fl2i(icon_x[0]), fl2i(icon_y[0]), fl2i(icon_x[1]), fl2i(icon_y[1]), GR_RESIZE_NONE);
892 }
893 
894 /**
895  * Draw a briefing icon
896  *
897  * @param stage_num	briefing stage number (start at 0)
898  * @param icon_num icon number in stage
899  * @param frametime	time elapsed in seconds
900  * @param selected FRED only (will be 0 or non-zero)
901  * @param w_scale_factor scale icon in width by this amount (default 1.0f)
902  * @param h_scale_factor scale icon in height by this amount (default 1.0f)
903  */
brief_render_icon(int stage_num,int icon_num,float frametime,int selected,float w_scale_factor,float h_scale_factor)904 void brief_render_icon(int stage_num, int icon_num, float frametime, int selected, float w_scale_factor, float h_scale_factor)
905 {
906 	brief_icon	*bi, *closeup_icon;
907 	generic_anim *ga;
908 	vertex		tv;	// temp vertex used to find screen position for text
909 	vec3d		*pos = NULL;
910 	int			bx,by,bc,w,h,icon_w,icon_h,icon_bitmap=-1;
911 	float			bxf, byf, dist=0.0f;
912 	bool mirror_icon;
913 
914 	Assert( Briefing != NULL );
915 
916 	bi = &Briefing->stages[stage_num].icons[icon_num];
917 	mirror_icon = (bi->flags & BI_MIRROR_ICON)? true:false;
918 
919 	icon_move_info *mi, *next;
920 	int interp_pos_found = 0;
921 
922 	mi = GET_FIRST(&Icon_move_list);
923 	if (mi)
924 		while ( mi != &Icon_move_list ) {
925 			next = mi->next;
926 			if ( ( mi->id != 0 ) && ( mi->id == bi->id ) ) {
927 
928 				if ( !mi->reached_dest ) {
929 					dist = brief_icon_get_dist_moved(mi, Elapsed_time);
930 					if ( dist < mi->last_dist ) {
931 						mi->reached_dest=1;
932 						mi->last_dist=0.0f;
933 					}
934 					mi->last_dist=dist;
935 				}
936 
937 				if ( !mi->reached_dest ) {
938 					vec3d dist_moved;
939 					vm_vec_copy_scale(&dist_moved, &mi->direction, dist);
940 					vm_vec_add(&mi->current, &mi->start, &dist_moved);
941 				} else {
942 					mi->current = mi->finish;
943 				}
944 
945 				pos = &mi->current;
946 				interp_pos_found = 1;
947 				break;
948 			}
949 			mi = next;
950 		}
951 
952 	if ( !interp_pos_found )
953 		pos = &bi->pos;
954 
955 	brief_render_elements(pos, The_grid);
956 	g3_rotate_vertex(&tv,pos);
957 
958 	if (!(tv.flags&PF_PROJECTED))
959 		g3_project_vertex(&tv);
960 
961 	if (!(tv.flags & PF_OVERFLOW) ) {  // make sure point projected before drawing text
962 
963 		brief_set_icon_color(bi->team);
964 
965 		briefing_icon_info *bii = brief_get_icon_info(bi);
966 		if (bii == NULL) {
967 			return;
968 		}
969 
970 		ga = &bii->regular;
971 		if (ga->first_frame < 0) {
972 			Int3();
973 			return;
974 		}
975 
976 		brief_common_get_icon_dimensions(&icon_w, &icon_h, bi);
977 
978 		closeup_icon = brief_get_closeup_icon();
979 		if ( bi == closeup_icon || selected ) {
980 			icon_bitmap = ga->first_frame+1;
981 		}
982 		else {
983 			icon_bitmap = ga->first_frame;
984 		}
985 
986 		float scaled_w, scaled_h;
987 
988 		float sx = tv.screen.xyw.x;
989 		float sy = tv.screen.xyw.y;
990 		gr_unsize_screen_posf( &sx, &sy, NULL, NULL, GR_RESIZE_MENU_NO_OFFSET );
991 
992 		scaled_w = icon_w * w_scale_factor;
993 		scaled_h = icon_h * h_scale_factor;
994 		bxf = sx - scaled_w / 2.0f + 0.5f;
995 		byf = sy - scaled_h / 2.0f + 0.5f;
996 		bx = fl2i(bxf);
997 		by = fl2i(byf);
998 		bc = fl2i(sx);
999 
1000 		if ( ( (bx < 0) || (bx > gr_screen.max_w_unscaled) || (by < 0) || (by > gr_screen.max_h_unscaled) ) && !Fred_running ) {
1001 			bi->x = bx;
1002 			bi->y = by;
1003 			return;
1004 		}
1005 
1006 		// render highlight anim frame
1007 		if ( (bi->flags&BI_SHOWHIGHLIGHT) && (bi->flags&BI_HIGHLIGHT) ) {
1008 			hud_anim *ha = &bi->highlight_anim;
1009 			if ( ha->first_frame >= 0 ) {
1010 				ha->sx = bi->hold_x;
1011 				if (bi->label[0] != '\0') {
1012 					ha->sy = bi->hold_y - fl2i(gr_get_font_height()/2.0f +0.5) - 2;
1013 				} else {
1014 					ha->sy = bi->hold_y;
1015 				}
1016 
1017 				//hud_set_iff_color(bi->team);
1018 				brief_set_icon_color(bi->team);
1019 
1020 				hud_anim_render(ha, frametime, 1, 0, 1, 0, GR_RESIZE_MENU, mirror_icon);
1021 
1022 				if ( Brief_stage_highlight_sound_handle < 0 ) {
1023 					if ( !Fred_running) {
1024 						Brief_stage_highlight_sound_handle = snd_play(&Snds_iface[SND_ICON_HIGHLIGHT]);
1025 					}
1026 				}
1027 			}
1028 		}
1029 
1030 		// render fade-in anim frame
1031 		if ( bi->flags & BI_FADEIN ) {
1032 			hud_anim *ha = &bi->fadein_anim;
1033 			if ( ha->first_frame >= 0 ) {
1034 				ha->sx = bx;
1035 				ha->sy = by;
1036 				brief_set_icon_color(bi->team);
1037 
1038 				if ( hud_anim_render(ha, frametime, 1, 0, 0, 1, GR_RESIZE_MENU, mirror_icon) == 0 ) {
1039 					bi->flags &= ~BI_FADEIN;
1040 				}
1041 			} else {
1042 				bi->flags &= ~BI_FADEIN;
1043 			}
1044 		}
1045 
1046 		if ( !(bi->flags & BI_FADEIN) ) {
1047 			gr_set_bitmap(icon_bitmap);
1048 			gr_aabitmap(bx, by, GR_RESIZE_MENU,mirror_icon);
1049 
1050 			// draw text centered over the icon (make text darker)
1051 			if ( bi->type == ICON_FIGHTER_PLAYER || bi->type == ICON_BOMBER_PLAYER ) {
1052 				gr_get_string_size(&w,&h,Players[Player_num].callsign);
1053 				gr_string(bc - fl2i(w/2.0f), by - h, Players[Player_num].callsign, GR_RESIZE_MENU);
1054 			}
1055 			else {
1056 				if (Lcl_gr) {
1057 					char buf[128];
1058 					strcpy_s(buf, bi->label);
1059 					lcl_translate_brief_icon_name_gr(buf);
1060 					gr_get_string_size(&w, &h, buf);
1061 					gr_string(bc - fl2i(w/2.0f), by - h, buf, GR_RESIZE_MENU);
1062 				} else {
1063 					gr_get_string_size(&w,&h,bi->label);
1064 					gr_string(bc - fl2i(w/2.0f), by - h, bi->label, GR_RESIZE_MENU);
1065 				}
1066 			}
1067 
1068 			// show icon as selected (FRED only)
1069 			if ( selected ) {
1070 				gr_get_string_size(&w,&h,NOX("(S)"));
1071 				gr_printf(bc - fl2i(w/2.0f), by - h*2, NOX("(S)"));
1072 			}
1073 		}
1074 
1075 		// store screen x,y,w,h
1076 		bi->x = bx;
1077 		bi->y = by;
1078 		bi->w = fl2i(scaled_w);
1079 		bi->h = fl2i(scaled_h);
1080 
1081 	}  // end if vertex is projected
1082 }
1083 
1084 #pragma optimize("", on)
1085 
1086 // -------------------------------------------------------------------------------------
1087 // brief_render_icons()
1088 //
brief_render_icons(int stage_num,float frametime)1089 void brief_render_icons(int stage_num, float frametime)
1090 {
1091 	int i, num_icons, num_lines;
1092 
1093 	Assert( Briefing != NULL );
1094 
1095 	num_icons = Briefing->stages[stage_num].num_icons;
1096 	num_lines = Briefing->stages[stage_num].num_lines;
1097 
1098 	if ( Cam_target_reached ) {
1099 		for ( i = 0; i < num_lines; i++ ) {
1100 			brief_render_icon_line(stage_num, i);
1101 		}
1102 	}
1103 
1104 	for ( i = 0; i < num_icons; i++ ) {
1105 		brief_render_icon(stage_num, i, frametime, 0);
1106 	}
1107 }
1108 
1109 /**
1110  * See if there are any highlight animations to play
1111  */
brief_start_highlight_anims(int stage_num)1112 void brief_start_highlight_anims(int stage_num)
1113 {
1114 	brief_stage		*bs;
1115 	brief_icon		*bi;
1116 	int				x,y,i,anim_w,anim_h;
1117 
1118 	Assert( Briefing != NULL );
1119 	bs = &Briefing->stages[stage_num];
1120 
1121 	for ( i = 0; i < bs->num_icons; i++ ) {
1122 		bi = &bs->icons[i];
1123 		if ( bi->flags & BI_HIGHLIGHT ) {
1124 			bi->flags &= ~BI_SHOWHIGHLIGHT;
1125 			if ( bi->highlight_anim.first_frame < 0 ) {
1126 				continue;
1127 			}
1128 
1129 			bi->highlight_anim.time_elapsed=0.0f;
1130 
1131 			bm_get_info( bi->highlight_anim.first_frame, &anim_w, &anim_h, NULL);
1132 			x = fl2i( i2fl(bi->x) + bi->w/2.0f - anim_w/2.0f );
1133 			y = fl2i( i2fl(bi->y) + bi->h/2.0f - anim_h/2.0f );
1134 			bi->hold_x = x;
1135 			bi->hold_y = y;
1136 			bi->flags |= BI_SHOWHIGHLIGHT;
1137 			bi->highlight_anim.time_elapsed=0.0f;
1138 		}
1139 	}
1140 }
1141 
1142 // -------------------------------------------------------------------------------------
1143 // brief_render_map()
1144 //
1145 //
brief_render_map(int stage_num,float frametime)1146 void brief_render_map(int stage_num, float frametime)
1147 {
1148 	gr_set_clip(bscreen.map_x1 + 1, bscreen.map_y1 + 1, bscreen.map_x2 - bscreen.map_x1 - 1, bscreen.map_y2 - bscreen.map_y1 - 2, GR_RESIZE_MENU);
1149 
1150     if (stage_num >= Briefing->num_stages) {
1151 		gr_reset_clip();
1152 		return;
1153 	}
1154 
1155 	Assert(Briefing);
1156 
1157 	g3_start_frame(0);
1158 	g3_set_view_matrix(&Current_cam_pos, &Current_cam_orient, 0.5f);
1159 
1160 	brief_maybe_create_new_grid(The_grid, &Current_cam_pos, &Current_cam_orient);
1161 	brief_render_grid(The_grid);
1162 
1163 	brief_render_fade_outs(frametime);
1164 
1165 	// go ahead and render everything that is in the active objects list
1166 	brief_render_icons(stage_num, frametime);
1167 
1168 	if ( Cam_target_reached && brief_text_wipe_finished() ) {
1169 		if ( Play_highlight_flag ) {
1170 			brief_start_highlight_anims(stage_num);
1171 			Play_highlight_flag = 0;
1172 		}
1173 	}
1174 
1175 	anim_render_all(ON_BRIEFING_SELECT, frametime);
1176 
1177 	gr_reset_clip();
1178 	g3_end_frame();
1179 }
1180 
1181 /**
1182  * Display what stage of the briefing is active
1183  */
brief_blit_stage_num(int stage_num,int stage_max)1184 void brief_blit_stage_num(int stage_num, int stage_max)
1185 {
1186 	char buf[64];
1187 
1188 	Assert( Briefing != NULL );
1189 	gr_set_color_fast(&Color_text_heading);
1190 	sprintf(buf, XSTR( "Stage %d of %d", 394), stage_num + 1, stage_max);
1191 	if (Game_mode & GM_MULTIPLAYER) {
1192 		gr_printf_menu(Brief_stage_text_coords_multi[gr_screen.res][0], Brief_stage_text_coords_multi[gr_screen.res][1], buf);
1193 	} else {
1194 		gr_printf_menu(Brief_stage_text_coords[gr_screen.res][0], Brief_stage_text_coords[gr_screen.res][1], buf);
1195 	}
1196 }
1197 
1198 /**
1199  * Render a line of text for the briefings.  Lines are drawn in as a wipe, with leading bright
1200  * white characters.  Have to jump through some hoops since we support colored words.  This means
1201  * that we need to process the line one character at a time.
1202  *
1203  * @param line_num number of the line of the briefing page to be drawn
1204  * @param x horizontal position where the text is drawn
1205  * @param y vertical position where the text is drawn
1206  * @param instance index of Colored_stream of the text page to display
1207  */
brief_render_line(int line_num,int x,int y,int instance)1208 void brief_render_line(int line_num, int x, int y, int instance)
1209 {
1210 	Assert( 0<=instance && instance < (int)(sizeof(Colored_stream)/sizeof(*Colored_stream)) );
1211 
1212 	SCP_vector<colored_char> *src = &Colored_stream[instance].at(line_num);
1213 
1214 	// empty strings do not have to be drawn
1215 	int src_len = src->size();
1216 	if (src_len == 0){
1217 		return;
1218 	}
1219 	// truncate_len is the number of characters currently displayed including the bright white characters
1220 	int truncate_len = fl2i(Brief_text_wipe_time_elapsed / BRIEF_TEXT_WIPE_TIME * Max_briefing_line_len);
1221 	if (truncate_len > src_len){
1222 		truncate_len = src_len;
1223 	}
1224 	// truncate_len is going to be the number of characters displayed with normal intensity
1225 	// bright_len is the additional characters displayed with high intensity
1226 	// bright_len+truncate_len<len chars are displayed
1227 	int bright_len = 0;
1228 	if (truncate_len < src_len) {
1229 		if (truncate_len <= BRIGHTEN_LEAD) {
1230 			bright_len = truncate_len;
1231 			truncate_len = 0;
1232 		} else {
1233 			bright_len = BRIGHTEN_LEAD;
1234 			truncate_len -= BRIGHTEN_LEAD;
1235 		}
1236 	}
1237 	int char_seq_pos=0; //Cursor position into the following character sequence
1238 	char char_seq[MAX_BRIEF_LINE_LEN];
1239 	int offset = 0; //offset is the horizontal position of the screen where strings are drawn
1240 	gr_set_color_fast(&Color_white);
1241 
1242 	// PART1: Draw the the briefing line part with normal intensity and word colors.
1243 	// The following algorithm builds a character sequence of color 'last_color' into 'line' buffer.
1244 	// When the color changes, the buffer is drawn and reset.
1245 	{
1246 		int last_color = src->at(0).color;
1247 		for (int current_pos=0; current_pos<truncate_len; current_pos++) {
1248 			colored_char &current_char=src->at(current_pos);
1249 			//when the current color changes, the accumulated character sequence is drawn.
1250 			if (current_char.color != last_color){
1251 				//add a 0 terminal character to make line a valid C string
1252 				Assert(char_seq_pos < (int)sizeof(char_seq));
1253 				char_seq[char_seq_pos] = 0;
1254 				{	// Draw coloured text, and increment cariage position
1255 					int w=0,h=0;
1256 					brief_set_text_color(last_color);
1257 					gr_string(x + offset, y, char_seq, GR_RESIZE_MENU);
1258 					gr_get_string_size(&w, &h, char_seq);
1259 					offset += w;
1260 				}
1261 				//clear char buffer
1262 				char_seq_pos = 0;
1263 				last_color = current_char.color;
1264 			}
1265 			Assert(char_seq_pos < (int)sizeof(char_seq));
1266 			char_seq[char_seq_pos++] = current_char.letter;
1267 		}
1268 		// Draw the final chunk of acumulated characters
1269 		// Add a 0 terminal character to make line a valid C string
1270 		Assert(char_seq_pos < (int)sizeof(char_seq));
1271 		char_seq[char_seq_pos] = 0;
1272         {	// Draw coloured text, and increment cariage position
1273 			int w=0,h=0;
1274 			brief_set_text_color(last_color);
1275 			gr_string(x + offset, y, char_seq, GR_RESIZE_MENU);
1276 			gr_get_string_size(&w, &h, char_seq);
1277 			offset += w;
1278 		}
1279 	}
1280 
1281 	{	// PART2: Draw leading bright white characters
1282 		char_seq_pos = 0;
1283 		for( int current_pos = truncate_len; current_pos<truncate_len + bright_len; current_pos++){
1284 			Assert(char_seq_pos < (int)sizeof(char_seq));
1285 			char_seq[char_seq_pos++] = src->at(current_pos).letter;
1286 		}
1287 		Assert(char_seq_pos < (int)sizeof(char_seq));
1288 		char_seq[char_seq_pos] = 0;
1289 		gr_set_color_fast(&Color_bright_white);
1290 		gr_string(x + offset, y, char_seq, GR_RESIZE_MENU);
1291 	}
1292 }
1293 
brief_text_wipe_finished()1294 int brief_text_wipe_finished()
1295 {
1296 	if ( Brief_text_wipe_time_elapsed > (BRIEF_TEXT_WIPE_TIME+0.5f) ) {
1297 		return 1;
1298 	}
1299 
1300 	return 0;
1301 }
1302 
1303 /**
1304  * brief_render_text()
1305  *
1306  * @param line_offset
1307  * @param x
1308  * @param y
1309  * @param h
1310  * @param frametime	time in seconds of previous frame
1311  * @param instance optional parameter.  Used to indicate which text stream is used. This value is 0 unless multiple text streams are required
1312  * @param line_spacing
1313  * @return
1314  */
brief_render_text(int line_offset,int x,int y,int h,float frametime,int instance,int line_spacing)1315 int brief_render_text(int line_offset, int x, int y, int h, float frametime, int instance, int line_spacing)
1316 {
1317 	int fh, line, yy;
1318 
1319 	fh = gr_get_font_height();
1320 	if (Brief_text_wipe_time_elapsed == 0) {
1321 		if (snd_is_playing(Brief_text_wipe_snd)) {
1322 			snd_stop(Brief_text_wipe_snd);
1323 		}
1324 		gamesnd_play_iface(SND_BRIEF_TEXT_WIPE);
1325 		Play_brief_voice = 1;
1326 	}
1327 
1328 	Brief_text_wipe_time_elapsed += frametime;
1329 
1330 	line = line_offset;
1331 	yy = 0;
1332 	while (yy + fh <= h) {
1333 		if (line >= Num_brief_text_lines[instance])
1334 			break;
1335 
1336 		brief_render_line(line, x, y + yy, instance);
1337 
1338 		line++;
1339 		yy += fh + line_spacing;
1340 	}
1341 
1342 	if ( brief_text_wipe_finished() && (Play_brief_voice) ) {
1343 		Play_brief_voice = 0;
1344 		return 1;
1345 	}
1346 
1347 	return 0;
1348 }
1349 
1350 /**
1351  * Draw the lines that show objects positions on the grid
1352  *
1353  */
brief_render_elements(vec3d * pos,grid * gridp)1354 void brief_render_elements(vec3d *pos, grid* gridp)
1355 {
1356 	vec3d	gpos;	//	Location of point on grid.
1357 	plane		tplane;
1358 	vec3d	*gv;
1359 
1360 	if ( pos->xyz.y < 1 && pos->xyz.y > -1 )
1361 		return;
1362 
1363 	tplane.A = gridp->gmatrix.vec.uvec.xyz.x;
1364 	tplane.B = gridp->gmatrix.vec.uvec.xyz.y;
1365 	tplane.C = gridp->gmatrix.vec.uvec.xyz.z;
1366 	tplane.D = gridp->planeD;
1367 
1368 	compute_point_on_plane(&gpos, &tplane, pos);
1369 
1370 	gv = &gridp->gmatrix.vec.uvec;
1371 	if (gv->xyz.x * pos->xyz.x + gv->xyz.y * pos->xyz.y + gv->xyz.z * pos->xyz.z < -gridp->planeD)
1372 		gr_set_color(127, 127, 127);
1373 	else
1374 		gr_set_color(255, 255, 255);   // white
1375 }
1376 
1377 
1378 // ------------------------------------------------------------------------------------
1379 // brief_reset_icons()
1380 //
brief_reset_icons(int stage_num)1381 void brief_reset_icons(int stage_num)
1382 {
1383 	brief_stage		*bs;
1384 	brief_icon		*bi;
1385 	int				i;
1386 
1387 	Assert( Briefing != NULL );
1388 	bs = &Briefing->stages[stage_num];
1389 
1390 	for ( i = 0; i < bs->num_icons; i++ ) {
1391 		bi = &bs->icons[i];
1392 		bi->flags &= ~BI_SHOWHIGHLIGHT;
1393 	}
1394 }
1395 
1396 
1397 /**
1398  * Direct camera to look at target
1399  *
1400  * @param pos target position for the camera
1401  * @param orient target orientation for the camera
1402  * @param time time in ms to reach target
1403  */
brief_set_camera_target(vec3d * pos,matrix * orient,int time)1404 void brief_set_camera_target(vec3d *pos, matrix *orient, int time)
1405 {
1406 	float time_in_seconds;
1407 
1408 	time_in_seconds = time / 1000.0f;
1409 
1410 	if ( time == 0 ) {
1411 		Current_cam_pos = *pos;
1412 		Current_cam_orient = *orient;
1413 	}
1414 
1415 	Target_cam_pos = *pos;
1416 	Target_cam_orient = *orient;
1417 	Start_cam_orient = Current_cam_orient;
1418 	Start_cam_pos = Current_cam_pos;								// we need this when checking if camera movement complete
1419 	Start_cam_move = timer_get_milliseconds()*1000.0f;		// start time, convert to seconds
1420 	Total_move_time = time_in_seconds;
1421 	Elapsed_time = 0.0f;
1422 
1423 	vm_vec_scale_add(&Start_lookat_pos, &Start_cam_pos, &Start_cam_orient.vec.fvec, LOOKAT_DIST);
1424 	vm_vec_scale_add(&Target_lookat_pos, &Target_cam_pos, &Target_cam_orient.vec.fvec, LOOKAT_DIST);
1425 
1426 	Play_highlight_flag = 1;								// once target reached, play highlight anims
1427 	Cam_target_reached = 0;
1428 	Cam_movement_done=0;
1429 	anim_release_all_instances(ON_BRIEFING_SELECT);	// stop any briefing-specific anims
1430 
1431 	// calculate camera velocity
1432 	vm_vec_sub(&Cam_vel, pos, &Current_cam_pos);
1433 
1434 	if ( !IS_VEC_NULL_SQ_SAFE(&Cam_vel) ) {
1435 		vm_vec_normalize(&Cam_vel);
1436 	}
1437 
1438 	// calculate lookat point velocity
1439 	vm_vec_sub(&Lookat_vel, &Target_lookat_pos, &Current_lookat_pos);
1440 	vm_vec_scale(&Lookat_vel, 1.0f/time_in_seconds);
1441 
1442 	Start_dist = vm_vec_dist(&Start_cam_pos, &Start_lookat_pos);
1443 	End_dist = vm_vec_dist(&Target_cam_pos, &Target_lookat_pos);
1444 	Dist_change_rate = (End_dist - Start_dist) / time_in_seconds;
1445 
1446 	Total_dist=vm_vec_dist(&Start_cam_pos, &Target_cam_pos);
1447 
1448 	Peak_speed=Total_dist/Total_move_time*2.0f;
1449 	Cam_accel = 4*Total_dist/(Total_move_time*Total_move_time);
1450 	Last_dist=0.0f;
1451 
1452 	vm_vec_zero(&W_init);
1453 
1454 	get_camera_limits(&Start_cam_orient, &Target_cam_orient, Total_move_time, &Acc_limit, &Vel_limit);
1455 }
1456 
1457 
brief_return_color_index(char c)1458 ubyte brief_return_color_index(char c)
1459 {
1460 	switch (c) {
1461 		case 'f':
1462 			return BRIEF_TEXT_FRIENDLY;
1463 
1464 		case 'h':
1465 			return BRIEF_TEXT_HOSTILE;
1466 
1467 		case 'n':
1468 			return BRIEF_TEXT_NEUTRAL;
1469 
1470 		case 'r':
1471 			return BRIEF_TEXT_RED;
1472 
1473 		case 'g':
1474 			return BRIEF_TEXT_GREEN;
1475 
1476 		case 'b':
1477 			return BRIEF_TEXT_BLUE;
1478 
1479 //The following added by Zacam for expanded BRIEF colors
1480  		case 'w':
1481  			return BRIEF_TEXT_WHITE;
1482 
1483  		case 'y':
1484  			return BRIEF_TEXT_YELLOW;
1485 
1486 		case 'W':
1487 			return BRIEF_TEXT_BRIGHT_WHITE;
1488 
1489 		case 'B':
1490 			return BRIEF_TEXT_BRIGHT_BLUE;
1491 
1492 		case 'G':
1493 			return BRIEF_TEXT_BRIGHT_GREEN;
1494 
1495 		case 'R':
1496 			return BRIEF_TEXT_BRIGHT_RED;
1497 
1498 		case 'Y':
1499 			return BRIEF_TEXT_BRIGHT_YELLOW;
1500 
1501 		case 'k':
1502 			return BRIEF_TEXT_BLACK;
1503 
1504 		case 'e':
1505 			return BRIEF_TEXT_GREY;
1506 
1507 		case 'E':
1508 			return BRIEF_TEXT_SILVER;
1509 
1510 		case 'v':
1511 			return BRIEF_TEXT_VIOLET_GRAY;
1512 
1513 		case 'V':
1514 			return BRIEF_TEXT_VIOLET;
1515 
1516 		case 'p':
1517 			return BRIEF_TEXT_PINK;
1518 
1519 		case 'P':
1520 			return BRIEF_TEXT_LIGHT_PINK;
1521 
1522 		case '|':	//This is not a duplicate, but a Non-Breaking Space case. Do not remove.
1523 			return BRIEF_TEXT_WHITE;
1524 
1525 		default:	//Zacam: Changed fron an Int3() in order to provide better feedback while still allowing play.
1526 			Warning(LOCATION, "Unrecognized or undefined case character: '$%c' used in Briefing in mission: '%s'. Tell Zacam.", c, Mission_filename);
1527 	} // end switch
1528 
1529 	return BRIEF_TEXT_WHITE;
1530 }
1531 
brief_set_text_color(int color_index)1532 void brief_set_text_color(int color_index)
1533 {
1534 	Assert(color_index < MAX_BRIEF_TEXT_COLORS);
1535 	gr_set_color_fast(Brief_text_colors[color_index]);
1536 }
1537 
1538 /**
1539  * Checks if a character is a word separator
1540  * @param character the character to be analysed.
1541  * @return true when the given character is a word separator, and false when the character is part of a word.
1542  */
is_a_word_separator(char character)1543 bool is_a_word_separator(char character)
1544 {
1545 	return character <= 32;					//  all control characters including space, newline, and tab
1546 }
1547 
1548 /**
1549  * Builds a vector of colored characters from a string containing color markups
1550  * and stores it to Colored_stream table.
1551  *
1552  * A color markup is made of a minimum of three characters:
1553  *   '$' + a char standing for a color + contigous multiple spaces (chars t n and ' ')
1554  * The markup is completely removed from the resulting character sequence.
1555  *
1556  * @param src a not null pointer to a C string terminated by a /0 char.
1557  * @param instance index into Colored_stream where the result should be placed. Value is 0 unless multiple text streams are required.
1558  * @return number of character of the resulting sequence.
1559  */
brief_text_colorize(char * src,int instance)1560 int brief_text_colorize(char *src, int instance)
1561 {
1562 	Assert(src);
1563 	Assert((0 <= instance) && (instance < (int)(sizeof(Colored_stream) / sizeof(*Colored_stream))));
1564 
1565 	// manage different default colors (don't use a SCP_ stack because eh)
1566 	const int HIGHEST_COLOR_STACK_INDEX = 9;
1567 	ubyte default_color_stack[10];
1568 	int color_stack_index = 0;
1569 
1570 	briefing_line dest_line;	//the resulting vector of colored character
1571 	ubyte active_color_index;	//the current drawing color
1572 
1573 	// start off with white
1574 	default_color_stack[0] = active_color_index = BRIEF_TEXT_WHITE;
1575 
1576 	int src_len = strlen(src);
1577 	for (int i = 0; i < src_len; i++)
1578 	{
1579 		// Is the character a color markup?
1580 		// text markup consists of a '$' plus a character plus an optional space
1581 		if ( (i < src_len - 1)  && (src[i] == BRIEF_META_CHAR) )
1582 		{
1583 			i++;   // Consume the $ character
1584 
1585 			// it's possible that there's a closing brace here
1586 			if (src[i] == '}')
1587 			{
1588 				if (color_stack_index > 0)
1589 				{
1590 					color_stack_index--;
1591 					active_color_index = default_color_stack[color_stack_index];
1592 				}
1593 				i++;	// consume the }
1594 			}
1595 			// normal $c or $c{
1596 			else
1597 			{
1598 				active_color_index = brief_return_color_index(src[i]);
1599 				i++; // Consume the color identifier and focus on the white character (if any)
1600 			}
1601 
1602 			// special case: color spans (different default color within braces)
1603 			// (there's a slim chance that src[i] could be the null-terminator, but that's okay here)
1604 			if (src[i] == '{')
1605 			{
1606 				if (color_stack_index < HIGHEST_COLOR_STACK_INDEX)
1607 				{
1608 					color_stack_index++;
1609 					default_color_stack[color_stack_index] = active_color_index;
1610 				}
1611 				i++;	// consume the {
1612 			}
1613 
1614 			// Skip every whitespace until the next word is reached
1615 			while ( (i < src_len) && is_white_space(src[i]) )
1616 				i++;
1617 
1618 			//The next character is not a whitespace, let's process it as usual
1619 			//(subtract 1 because the for loop will add it again)
1620 			i--;
1621 			continue;
1622  		}
1623 
1624 		// When the word is terminated, reset color to default
1625 		if ( (is_white_space(src[i]) ) || ( is_a_word_separator(src[i]) ))
1626 			active_color_index = default_color_stack[color_stack_index];
1627 
1628 		// Append the character to the result structure
1629 		colored_char dest;
1630 		dest.letter = src[i];
1631 		dest.color = active_color_index;
1632 		dest_line.push_back(dest);
1633 	}
1634 
1635 	Colored_stream[instance].push_back(dest_line);
1636 	return dest_line.size();
1637 }
1638 
1639 /**
1640  * Initialise briefing coloured text
1641  *
1642  * @param src paragraph of text to process
1643  * @param w	max width of line in pixels
1644  * @param instance optional parameter, used when multiple text streams are required (default value is 0)
1645  * @param max_lines maximum number of lines
1646  */
brief_color_text_init(const char * src,int w,int instance,int max_lines)1647 int brief_color_text_init(const char* src, int w, int instance, int max_lines)
1648 {
1649 	int i, n_lines, len;
1650 	SCP_vector<int> n_chars;
1651 	SCP_vector<const char*> p_str;
1652 	char brief_line[MAX_BRIEF_LINE_LEN];
1653 
1654 	Assert(src != NULL);
1655 	n_lines = split_str(src, w, n_chars, p_str, BRIEF_META_CHAR);
1656 	Assert(n_lines >= 0);
1657 
1658 	//for compatability reasons truncate text from everything except the fiction viewer
1659 	if ((max_lines > 0) && (n_lines > max_lines)) {
1660 		n_lines = max_lines;
1661 	}
1662 
1663 	Max_briefing_line_len = 1;
1664 	Colored_stream[instance].clear();
1665 	for (i=0; i<n_lines; i++) {
1666 		Assert(n_chars[i] < MAX_BRIEF_LINE_LEN);
1667 		strncpy(brief_line, p_str[i], n_chars[i]);
1668 		brief_line[n_chars[i]] = 0;
1669 		drop_leading_white_space(brief_line);
1670 		len = brief_text_colorize(&brief_line[0], instance);
1671 		if (len > Max_briefing_line_len)
1672 			Max_briefing_line_len = len;
1673 	}
1674 
1675 	Brief_text_wipe_time_elapsed = 0.0f;
1676 	Play_brief_voice = 0;
1677 
1678 	Num_brief_text_lines[instance] = n_lines;
1679 	return n_lines;
1680 }
1681 
1682 /**
1683  * Get free handle to a move icon
1684  *
1685  * @return -1 on failure, a handle to a free move icon struct on success
1686  */
brief_get_free_move_icon()1687 int brief_get_free_move_icon()
1688 {
1689 	int i;
1690 
1691 	for ( i = 0; i < MAX_MOVING_ICONS; i++ ) {
1692 		if ( Icon_movers[i].used == 0 )
1693 			break;
1694 	}
1695 
1696 	if ( i == MAX_MOVING_ICONS )
1697 		return -1;
1698 
1699 	Icon_movers[i].used = 1;
1700 	return i;
1701 }
1702 
1703 /**
1704  * Set move list in briefing
1705  *
1706  * @param new_stage new stage number that briefing is now moving to
1707  * @param current_stage	current stage that the briefing is on
1708  * @param time	time in seconds
1709  */
brief_set_move_list(int new_stage,int current_stage,float time)1710 int brief_set_move_list(int new_stage, int current_stage, float time)
1711 {
1712 	brief_stage		*newb, *cb;
1713 	icon_move_info	*imi;
1714 	int				i,j,k,num_movers,is_gone=0;
1715 	vec3d			zero_v = ZERO_VECTOR;
1716 
1717 	Assert(new_stage != current_stage);
1718 
1719 	Assert( Briefing != NULL );
1720 	newb = &Briefing->stages[new_stage];
1721 	cb = &Briefing->stages[current_stage];
1722 	num_movers = 0;
1723 
1724 	for ( i = 0; i < cb->num_icons; i++ ) {
1725 		is_gone=1;
1726 		for ( j = 0; j < newb->num_icons; j++ ) {
1727 			if ( ( cb->icons[i].id != 0 ) && ( cb->icons[i].id == newb->icons[j].id ) ) {
1728 				is_gone=0;
1729 				if ( vm_vec_cmp(&cb->icons[i].pos, &newb->icons[j].pos) ) {
1730 
1731 					k = brief_get_free_move_icon();
1732 					if ( k == -1 ) {
1733 						Warning(LOCATION, "Too many briefing icons are moving simultaneously!");
1734 						return 0;
1735 					}
1736 					imi = &Icon_movers[k];
1737 					imi->id = cb->icons[i].id;
1738 					imi->start = cb->icons[i].pos;
1739 					imi->finish = newb->icons[j].pos;
1740 					imi->current = imi->start;
1741 					list_append(&Icon_move_list, imi);
1742 
1743 					imi->total_dist = vm_vec_dist(&imi->start, &imi->finish);
1744 					imi->total_move_time = time;
1745 					imi->peak_speed = imi->total_dist/imi->total_move_time*2.0f;
1746 					imi->accel = 4*imi->total_dist/(time*time);
1747 					imi->last_dist=0.0f;
1748 					imi->reached_dest=0;
1749 					imi->direction = zero_v;
1750 
1751 					vm_vec_sub(&imi->direction, &imi->finish, &imi->start);
1752 					if ( !IS_VEC_NULL_SQ_SAFE(&imi->direction) ) {
1753 						vm_vec_normalize(&imi->direction);
1754 					}
1755 
1756 					num_movers++;
1757 				}
1758 			}
1759 		}
1760 
1761 		// Set up fading icon (to fade out)
1762 		if (is_gone == 1) {
1763 			if ( Num_fade_icons >= MAX_FADING_ICONS ) {
1764 				Int3();
1765 				Num_fade_icons = 0;
1766 			}
1767 
1768 			briefing_icon_info *bii = brief_get_icon_info(&cb->icons[i]);
1769 			if (bii == NULL) {
1770 				return 0;
1771 			}
1772 
1773 			Fading_icons[Num_fade_icons].fade_anim = bii->fade;
1774 			Fading_icons[Num_fade_icons].pos = cb->icons[i].pos;
1775 			Fading_icons[Num_fade_icons].team = cb->icons[i].team;
1776 			Num_fade_icons++;
1777 		}
1778 	}
1779 
1780 	// flag new icons for fading in
1781 	for ( i=0; i<newb->num_icons; i++ ) {
1782 		int is_new = 1;
1783 		newb->icons[i].flags &= ~BI_FADEIN;
1784 		for ( j=0; j<cb->num_icons; j++ ) {
1785 			if ( ( cb->icons[j].id != 0 ) && ( cb->icons[j].id == newb->icons[i].id ) ) {
1786 				is_new=0;
1787 			}
1788 		}
1789 		if ( is_new ) {
1790 			briefing_icon_info *bii = brief_get_icon_info(&newb->icons[i]);
1791 			if (bii == NULL) {
1792 				return 0;
1793 			}
1794 
1795 			newb->icons[i].flags |= BI_FADEIN;
1796 			newb->icons[i].fadein_anim = bii->fade;
1797 			newb->icons[i].fadein_anim.time_elapsed = 0.0f;
1798 		}
1799 	}
1800 
1801 	return num_movers;
1802 }
1803 
brief_clear_fade_out_icons()1804 void brief_clear_fade_out_icons()
1805 {
1806 	Num_fade_icons = 0;
1807 }
1808 
1809 
1810 /**
1811  * Set new stage in briefing
1812  *
1813  * @param pos target position for the camera
1814  * @param orient target orientation for the camera
1815  * @param time time in ms to reach target
1816  * @param stage_num	stage number of briefing (start numbering at 0)
1817  */
brief_set_new_stage(vec3d * pos,matrix * orient,int time,int stage_num)1818 void brief_set_new_stage(vec3d *pos, matrix *orient, int time, int stage_num)
1819 {
1820 	const char *msg;
1821 	int num_movers, new_time, not_objv = 1;
1822 
1823 	Assert( Briefing != NULL );
1824 	new_time = time;
1825 
1826 	if (stage_num >= Briefing->num_stages) {
1827 		not_objv = 0;  // turns out this is an objectives stage
1828 		new_time = 0;
1829 	}
1830 
1831 	if ( stage_num == Last_new_stage ) {
1832 		return;
1833 	}
1834 
1835 	num_movers = 0;
1836 	brief_move_icon_reset();
1837 	brief_clear_fade_out_icons();
1838 	if ( (Last_new_stage != -1) && not_objv ) {
1839 		num_movers = brief_set_move_list(stage_num, Last_new_stage, new_time / 1000.0f);
1840 	}
1841 
1842 	if ( (Last_new_stage != -1) && (num_movers == 0) && not_objv ) {
1843 		if ( !vm_vec_cmp( &Briefing->stages[stage_num].camera_pos, &Briefing->stages[Last_new_stage].camera_pos) ) {
1844 			if ( !vm_vec_cmp( &Briefing->stages[stage_num].camera_orient.vec.fvec, &Briefing->stages[Last_new_stage].camera_orient.vec.fvec) ){
1845 				new_time = 0;
1846 			}
1847 		}
1848 	}
1849 
1850 	if (not_objv) {
1851 		msg = Briefing->stages[stage_num].text.c_str();
1852 	} else {
1853 		msg = XSTR( "Please review your objectives for this mission.", 395);
1854 	}
1855 
1856 	if (gr_screen.res == GR_640) {
1857 		// GR_640
1858 		Num_brief_text_lines[0] = brief_color_text_init(msg, MAX_BRIEF_LINE_W_640);
1859 	} else {
1860 		// GR_1024
1861 		Num_brief_text_lines[0] = brief_color_text_init(msg, MAX_BRIEF_LINE_W_1024);
1862 	}
1863 
1864 	Top_brief_text_line = 0;
1865 
1866 	if (not_objv){
1867 		brief_set_camera_target(pos, orient, new_time);
1868 	}
1869 
1870 	if ( snd_is_playing(Brief_stage_highlight_sound_handle) ) {
1871 		snd_stop(Brief_stage_highlight_sound_handle);
1872 	}
1873 
1874 	Voice_started_time = 0;
1875 	Voice_ended_time = 0;
1876 
1877 	Brief_stage_highlight_sound_handle = -1;
1878 	Last_new_stage = stage_num;
1879 }
1880 
1881 // ------------------------------------------------------------------------------------
1882 // camera_pos_past_target()
1883 //
1884 //
camera_pos_past_target(vec3d * start,vec3d * current,vec3d * dest)1885 int camera_pos_past_target(vec3d *start, vec3d *current, vec3d *dest)
1886 {
1887 	vec3d num, den;
1888 	float ratio;
1889 
1890 	vm_vec_sub(&num, current, start);
1891 	vm_vec_sub(&den, start, dest);
1892 
1893 	ratio = vm_vec_mag_quick(&num) / vm_vec_mag_quick(&den);
1894 	if (ratio >= 1.0f)
1895 		return TRUE;
1896 
1897 	return FALSE;
1898 }
1899 
1900 /**
1901  * Interpolate between matrices.
1902  * elapsed_time/total_time gives percentage of interpolation between cur and goal.
1903  */
interpolate_matrix(matrix * result,matrix * goal,matrix * start,float elapsed_time,float total_time)1904 void interpolate_matrix(matrix *result, matrix *goal, matrix *start, float elapsed_time, float total_time)
1905 {
1906 	vec3d fvec, rvec;
1907 	float	time0, time1;
1908 
1909 	if ( !vm_matrix_cmp( goal, start ) ) {
1910 		return;
1911 	}
1912 
1913 	time0 = elapsed_time / total_time;
1914 	time1 = (total_time - elapsed_time) / total_time;
1915 
1916 	vm_vec_copy_scale(&fvec, &start->vec.fvec, time1);
1917 	vm_vec_scale_add2(&fvec, &goal->vec.fvec, time0);
1918 
1919 	vm_vec_copy_scale(&rvec, &start->vec.rvec, time1);
1920 	vm_vec_scale_add2(&rvec, &goal->vec.rvec, time0);
1921 
1922 	vm_vector_2_matrix(result, &fvec, NULL, &rvec);
1923  }
1924 
1925 /**
1926  * Calculate how far the camera should have moved
1927  */
brief_camera_get_dist_moved(float elapsed_time)1928 float brief_camera_get_dist_moved(float elapsed_time)
1929 {
1930 	float time, dist_moved=0.0f;
1931 
1932 	// first half of movement
1933 	if ( elapsed_time < Total_move_time/2.0f ) {
1934 		dist_moved=0.5f*Cam_accel*elapsed_time*elapsed_time;	// d = 1/2at^2
1935 		return dist_moved;
1936 	}
1937 
1938 	// second half of movement
1939 	time=elapsed_time - Total_move_time/2.0f;
1940 	dist_moved=(Total_dist/2.0f)+(Peak_speed*time) - 0.5f*Cam_accel*time*time;
1941 	return dist_moved;
1942 
1943 }
1944 
1945 /**
1946  * Update the camera position
1947  */
brief_camera_move(float frametime,int stage_num)1948 void brief_camera_move(float frametime, int stage_num)
1949 {
1950 	vec3d	dist_moved;
1951 	float		dist;
1952 	vec3d	w_out;
1953 	matrix	result;
1954 
1955 	Elapsed_time += frametime;
1956 
1957 	if ( Cam_target_reached )
1958 		return;
1959 
1960 	// Update orientation
1961 	if ( (Elapsed_time < Total_move_time) ) {
1962 		vm_matrix_interpolate(&Target_cam_orient, &Current_cam_orient, &W_init, frametime, &result, &w_out, &Vel_limit, &Acc_limit);
1963 		Current_cam_orient = result;
1964 		W_init = w_out;
1965 	}
1966 
1967 	// use absolute pos to update position
1968 	if ( vm_vec_cmp( &Current_cam_pos, &Target_cam_pos ) ) {
1969 		dist = brief_camera_get_dist_moved(Elapsed_time);
1970 		if ( dist < Last_dist ) {
1971 			Cam_movement_done=1;
1972 			Last_dist=0.0f;
1973 		}
1974 		Last_dist=dist;
1975 
1976 		if ( Cam_movement_done == 0 ) {
1977 			vm_vec_copy_scale(&dist_moved, &Cam_vel, dist);
1978 			vm_vec_add(&Current_cam_pos, &Start_cam_pos, &dist_moved);
1979 		} else {
1980 			Current_cam_pos=Target_cam_pos;
1981 		}
1982 	}
1983 	else {
1984 		Cam_movement_done=1;
1985 		Current_cam_pos=Target_cam_pos;
1986 	}
1987 
1988 	if ( Cam_movement_done && (Elapsed_time >= Total_move_time) ) {
1989 		Cam_target_reached=1;
1990 	}
1991 }
1992 
1993 /**
1994  * Project the viewer's position onto the grid plane.
1995  * If more than threshold distance from grid center, move grid center.
1996  */
brief_maybe_create_new_grid(grid * gridp,vec3d * pos,matrix * orient,int force)1997 void brief_maybe_create_new_grid(grid* gridp, vec3d *pos, matrix *orient, int force)
1998 {
1999 	int roundoff;
2000 	plane	tplane;
2001 	vec3d	gpos, tmp, c;
2002 	float	dist_to_plane;
2003 	float	square_size, ux, uy, uz;
2004 
2005 	ux = tplane.A = gridp->gmatrix.vec.uvec.xyz.x;
2006 	uy = tplane.B = gridp->gmatrix.vec.uvec.xyz.y;
2007 	uz = tplane.C = gridp->gmatrix.vec.uvec.xyz.z;
2008 	tplane.D = gridp->planeD;
2009 
2010 	compute_point_on_plane(&c, &tplane, pos);
2011 	dist_to_plane = fl_abs(vm_dist_to_plane(pos, &gridp->gmatrix.vec.uvec, &c));
2012 	square_size = 1.0f;
2013 
2014 	while (dist_to_plane >= 25.0f)
2015 	{
2016 		square_size *= 10.0f;
2017 		dist_to_plane /= 10.0f;
2018 	}
2019 
2020 	if (fvi_ray_plane(&gpos, &gridp->center, &gridp->gmatrix.vec.uvec, pos, &orient->vec.fvec, 0.0f)<0.0f)	{
2021 		vec3d p;
2022 		vm_vec_scale_add(&p,pos,&orient->vec.fvec, 100.0f );
2023 		compute_point_on_plane(&gpos, &tplane, &p );
2024 	}
2025 
2026 	if (vm_vec_dist(&gpos, &c) > 50.0f * square_size)
2027 	{
2028 		vm_vec_sub(&tmp, &gpos, &c);
2029 		vm_vec_normalize(&tmp);
2030 		vm_vec_scale_add(&gpos, &c, &tmp, 50.0f * square_size);
2031 	}
2032 
2033 	roundoff = (int) square_size * 10;
2034 	if (!ux)
2035 		gpos.xyz.x = fl_roundoff(gpos.xyz.x, roundoff);
2036 	if (!uy)
2037 		gpos.xyz.y = fl_roundoff(gpos.xyz.y, roundoff);
2038 	if (!uz)
2039 		gpos.xyz.z = fl_roundoff(gpos.xyz.z, roundoff);
2040 
2041 	if ((square_size != gridp->square_size) ||
2042 		(gpos.xyz.x != gridp->center.xyz.x) ||
2043 		(gpos.xyz.y != gridp->center.xyz.y) ||
2044 		(gpos.xyz.z != gridp->center.xyz.z) || force)
2045 	{
2046 		gridp->square_size = square_size;
2047 		gridp->center = gpos;
2048 		brief_modify_grid(gridp);
2049 	}
2050 }
2051 
2052 //	Create a grid
2053 //	*forward is vector pointing forward
2054 //	*right is vector pointing right
2055 //	*center is center point of grid
2056 //	length is length of grid
2057 //	width is width of grid
2058 //	square_size is size of a grid square
2059 //	For example:
2060 //		*forward = (0.0, 0.0, 1.0)
2061 //		*right   = (1.0, 0.0, 0.0)
2062 //		*center = (0.0, 0.0, 0.0)
2063 //		nrows = 10
2064 //		ncols =  50.0
2065 //		square_size = 10.0
2066 //	will generate a grid of squares 10 long by 5 wide.
2067 //	Each grid square will be 10.0 x 10.0 units.
2068 //	The center of the grid will be at the global origin.
2069 //	The grid will be parallel to the xz plane (because the normal is 0,1,0).
2070 //	(In fact, it will be the xz plane because it is centered on the origin.)
2071 //
2072 //	Stuffs grid in *gridp.  If gridp == NULL, mallocs and returns a grid.
brief_create_grid(grid * gridp,vec3d * forward,vec3d * right,vec3d * center,int nrows,int ncols,float square_size)2073 grid *brief_create_grid(grid *gridp, vec3d *forward, vec3d *right, vec3d *center, int nrows, int ncols, float square_size)
2074 {
2075 	int	i, ncols2, nrows2, d = 1;
2076 	vec3d	dfvec, drvec, cur, cur2, tvec, uvec, save, save2;
2077 
2078 	Assert(square_size > 0.0);
2079 	if (double_fine_gridlines)
2080 		d = 2;
2081 
2082 	if (gridp == NULL)
2083 		gridp = (grid *) vm_malloc(sizeof(grid));
2084 
2085 	Assert(gridp);
2086 
2087 	gridp->center = *center;
2088 	gridp->square_size = square_size;
2089 
2090 	//	Create the plane equation.
2091 	Assert(!IS_VEC_NULL(forward));
2092 	Assert(!IS_VEC_NULL(right));
2093 
2094 	vm_vec_copy_normalize(&dfvec, forward);
2095 	vm_vec_copy_normalize(&drvec, right);
2096 
2097 	vm_vec_cross(&uvec, &dfvec, &drvec);
2098 
2099 	Assert(!IS_VEC_NULL(&uvec));
2100 
2101 	gridp->gmatrix.vec.uvec = uvec;
2102 
2103 	gridp->planeD = -(center->xyz.x * uvec.xyz.x + center->xyz.y * uvec.xyz.y + center->xyz.z * uvec.xyz.z);
2104 	Assert(!_isnan(gridp->planeD));
2105 
2106 	gridp->gmatrix.vec.fvec = dfvec;
2107 	gridp->gmatrix.vec.rvec = drvec;
2108 
2109 	vm_vec_scale(&dfvec, square_size);
2110 	vm_vec_scale(&drvec, square_size);
2111 
2112 	vm_vec_scale_add(&cur, center, &dfvec, (float) -nrows * d / 2);
2113 	vm_vec_scale_add2(&cur, &drvec, (float) -ncols * d / 2);
2114 	vm_vec_scale_add(&cur2, center, &dfvec, (float) -nrows * 5 / 2);
2115 	vm_vec_scale_add2(&cur2, &drvec, (float) -ncols * 5 / 2);
2116 	save = cur;
2117 	save2 = cur2;
2118 
2119 	gridp->ncols = ncols;
2120 	gridp->nrows = nrows;
2121 	ncols2 = ncols / 2;
2122 	nrows2 = nrows / 2;
2123 	Assert(ncols < MAX_GRIDLINE_POINTS && nrows < MAX_GRIDLINE_POINTS);
2124 
2125 	// Create the points along the edges of the grid, so we can just draw lines
2126 	// between them to form the grid.
2127 	for (i=0; i<=ncols*d; i++) {
2128 		gridp->gpoints1[i] = cur;  // small, dark gridline points
2129 		vm_vec_scale_add(&tvec, &cur, &dfvec, (float) nrows * d);
2130 		gridp->gpoints2[i] = tvec;
2131 		vm_vec_add2(&cur, &drvec);
2132 	}
2133 
2134 	for (i=0; i<=ncols2; i++) {
2135 		gridp->gpoints5[i] = cur2;  // large, brighter gridline points
2136 		vm_vec_scale_add(&tvec, &cur2, &dfvec, (float) nrows2 * 10);
2137 		gridp->gpoints6[i] = tvec;
2138 		vm_vec_scale_add2(&cur2, &drvec, 10.0f);
2139 	}
2140 
2141 	cur = save;
2142 	cur2 = save2;
2143 	for (i=0; i<=nrows*d; i++) {
2144 		gridp->gpoints3[i] = cur;  // small, dark gridline points
2145 		vm_vec_scale_add(&tvec, &cur, &drvec, (float) ncols * d);
2146 		gridp->gpoints4[i] = tvec;
2147 		vm_vec_add2(&cur, &dfvec);
2148 	}
2149 
2150 	for (i=0; i<=nrows2; i++) {
2151 		gridp->gpoints7[i] = cur2;  // large, brighter gridline points
2152 		vm_vec_scale_add(&tvec, &cur2, &drvec, (float) ncols2 * 10);
2153 		gridp->gpoints8[i] = tvec;
2154 		vm_vec_scale_add2(&cur2, &dfvec, 10.0f);
2155 	}
2156 
2157 	return gridp;
2158 }
2159 
2160 /**
2161  * Create a nice default grid.
2162  * Centered at origin, 10x10, 10.0 size squares, in xz plane.
2163  */
brief_create_default_grid(void)2164 grid *brief_create_default_grid(void)
2165 {
2166 	grid	*rgrid;
2167 	vec3d	fvec, rvec, cvec;
2168 
2169 	vm_vec_make(&fvec, 0.0f, 0.0f, 1.0f);
2170 	vm_vec_make(&rvec, 1.0f, 0.0f, 0.0f);
2171 	vm_vec_make(&cvec, 0.0f, -10.0f, 0.0f);
2172 
2173 	rgrid = brief_create_grid(&Global_grid, &fvec, &rvec, &cvec, 100, 100, 5.0f);
2174 
2175 	physics_init(&rgrid->physics);
2176 	rgrid->physics.flags |= (PF_ACCELERATES | PF_SLIDE_ENABLED);
2177 	return rgrid;
2178 }
2179 
2180 /**
2181  * Rotate and project points and draw a line.
2182  */
brief_rpd_line(vec3d * v0,vec3d * v1)2183 void brief_rpd_line(vec3d *v0, vec3d *v1)
2184 {
2185 	vertex	tv0, tv1;
2186 	g3_rotate_vertex(&tv0, v0);
2187 	g3_rotate_vertex(&tv1, v1);
2188 
2189 	gr_set_color_fast(&Color_grey);
2190 	g3_draw_line(&tv0, &tv1);
2191 }
2192 
2193 /**
2194  * Renders a grid
2195  *
2196  * @param gridp Grid defined in a grid struct to render
2197  */
brief_render_grid(grid * gridp)2198 void brief_render_grid(grid *gridp)
2199 {
2200 	int	i, ncols, nrows;
2201 
2202 	ncols = gridp->ncols;
2203 	nrows = gridp->nrows;
2204 	if (double_fine_gridlines)
2205 	{
2206 		ncols *= 2;
2207 		nrows *= 2;
2208 	}
2209 
2210 	gr_set_color(30,30,30);
2211 
2212 	//	Draw the column lines.
2213 	for (i=0; i<=ncols; i++)
2214 		brief_rpd_line(&gridp->gpoints1[i], &gridp->gpoints2[i]);
2215 
2216 	//	Draw the row lines.
2217 	for (i=0; i<=nrows; i++)
2218 		brief_rpd_line(&gridp->gpoints3[i], &gridp->gpoints4[i]);
2219 }
2220 
brief_modify_grid(grid * gridp)2221 void brief_modify_grid(grid *gridp)
2222 {
2223 	brief_create_grid(gridp, &gridp->gmatrix.vec.fvec, &gridp->gmatrix.vec.rvec, &gridp->center,
2224 		gridp->nrows, gridp->ncols, gridp->square_size);
2225 }
2226 
brief_unload_anims()2227 void brief_unload_anims()
2228 {
2229 	for (SCP_vector<briefing_icon_info>::iterator ii = Briefing_icon_info.begin(); ii != Briefing_icon_info.end(); ++ii)
2230 	{
2231 		if (ii->regular.first_frame >= 0)
2232 		{
2233 			bm_unload(ii->regular.first_frame);
2234 			ii->regular.first_frame = -1;
2235 		}
2236 
2237 		if (ii->fade.first_frame >= 0)
2238 		{
2239 			bm_unload(ii->fade.first_frame);
2240 			ii->fade.first_frame = -1;
2241 		}
2242 
2243 		if (ii->highlight.first_frame >= 0)
2244 		{
2245 			bm_unload(ii->highlight.first_frame);
2246 			ii->highlight.first_frame = -1;
2247 		}
2248 	}
2249 }
2250 
brief_common_close()2251 void brief_common_close()
2252 {
2253 	brief_unload_anims();
2254 }
2255 
brief_restart_text_wipe()2256 void brief_restart_text_wipe()
2257 {
2258 	Voice_started_time = 0;
2259 	Voice_ended_time = 0;
2260 	Brief_text_wipe_time_elapsed = 0.0f;
2261 }
2262 
2263 /**
2264  * Initialize the array of handles to the different voice streams
2265  */
brief_voice_init()2266 void brief_voice_init()
2267 {
2268 	int i;
2269 	for ( i = 0; i < MAX_BRIEF_STAGES; i++ ) {
2270 		Brief_voices[i] = -1;
2271 	}
2272 }
2273 
brief_load_voice_file(int voice_num,char * name)2274 void brief_load_voice_file(int voice_num, char *name)
2275 {
2276 	int load_attempts = 0;
2277 	while(1) {
2278 
2279 		if ( load_attempts++ > 5 ) {
2280 			break;
2281 		}
2282 
2283 		Brief_voices[voice_num] = audiostream_open( name, ASF_VOICE );
2284 		if ( Brief_voices[voice_num] >= 0 ) {
2285 			break;
2286 		}
2287 
2288 		// Don't bother to ask for the CD in multiplayer
2289 		if ( Game_mode & GM_MULTIPLAYER ) {
2290 			break;
2291 		}
2292 	}
2293 }
2294 
2295 /**
2296  * Open and pre-load the stream buffers for the different voice streams
2297  */
brief_voice_load_all()2298 void brief_voice_load_all()
2299 {
2300 	int			i;
2301 	brief_stage	*bs;
2302 
2303 	Assert( Briefing != NULL );
2304 	for ( i = 0; i < Briefing->num_stages; i++ ) {
2305 		bs = &Briefing->stages[i];
2306 		if ( strnicmp(bs->voice, NOX("none"), 4) ) {
2307 			brief_load_voice_file(i, bs->voice);
2308 		}
2309 	}
2310 }
2311 
2312 /**
2313  * Close all the briefing voice streams
2314  */
brief_voice_unload_all()2315 void brief_voice_unload_all()
2316 {
2317 	int i;
2318 
2319 	for ( i = 0; i < MAX_BRIEF_STAGES; i++ ) {
2320 		if ( Brief_voices[i] != -1 ) {
2321 			audiostream_close_file(Brief_voices[i], 0);
2322 			Brief_voices[i] = -1;
2323 		}
2324 	}
2325 }
2326 
2327 /**
2328  * Start playback of the voice for a particular briefing stage
2329  */
brief_voice_play(int stage_num)2330 void brief_voice_play(int stage_num)
2331 {
2332 	if (!Voice_started_time) {
2333 		Voice_started_time = timer_get_milliseconds();
2334 		Voice_ended_time = 0;
2335 	}
2336 
2337 	if ( !Briefing_voice_enabled ) {
2338 		return;
2339 	}
2340 
2341 	if ( Brief_voices[stage_num] < 0 ) {
2342 		// play simulated speech?
2343 		if (fsspeech_play_from(FSSPEECH_FROM_BRIEFING)) {
2344 			if (fsspeech_playing()) {
2345 				return;
2346 			}
2347 
2348 			fsspeech_play(FSSPEECH_FROM_BRIEFING, Briefing->stages[stage_num].text.c_str());
2349 		}
2350 	} else {
2351 		if ( audiostream_is_playing( Brief_voices[stage_num]) ) {
2352 			return;
2353 		}
2354 
2355 		audiostream_play(Brief_voices[stage_num], Master_voice_volume, 0);
2356 	}
2357 }
2358 
2359 /**
2360  * Stop playback of the voice for a particular briefing stage
2361  */
brief_voice_stop(int stage_num)2362 void brief_voice_stop(int stage_num)
2363 {
2364 	if (Brief_voices[stage_num] < 0) {
2365 		fsspeech_stop();
2366 	} else {
2367 		audiostream_stop(Brief_voices[stage_num], 1, 0);	// stream is automatically rewound
2368 	}
2369 }
2370 
2371 /**
2372  * Pause playback of the voice for a particular briefing stage, to resume just
2373  * call brief_voice_unpause() again
2374  */
brief_voice_pause(int stage_num)2375 void brief_voice_pause(int stage_num)
2376 {
2377 	if (Brief_voices[stage_num] < 0) {
2378 		fsspeech_stop();
2379 	} else {
2380 		audiostream_pause(Brief_voices[stage_num]);
2381 	}
2382 }
2383 
brief_voice_unpause(int stage_num)2384 void brief_voice_unpause(int stage_num)
2385 {
2386 	if (Brief_voices[stage_num] < 0) {
2387 		fsspeech_stop();
2388 	} else {
2389 		audiostream_unpause(Brief_voices[stage_num]);
2390 	}
2391 }
2392 
brief_reset_last_new_stage()2393 void brief_reset_last_new_stage()
2394 {
2395 	Last_new_stage = -1;
2396 }
2397 
2398 /**
2399  * Get the dimensions for a briefing icon
2400  */
brief_common_get_icon_dimensions(int * w,int * h,brief_icon * bi)2401 void brief_common_get_icon_dimensions(int *w, int *h, brief_icon *bi)
2402 {
2403 	Assert(bi != NULL);
2404 
2405 	// in case anything goes wrong
2406 	*w=0;
2407 	*h=0;
2408 
2409 	briefing_icon_info *bii = brief_get_icon_info(bi);
2410 	if (bii == NULL) {
2411 		return;
2412 	}
2413 
2414 	if (bii->regular.first_frame >= 0) {
2415 		bm_get_info(bii->regular.first_frame, w, h, NULL);
2416 	}
2417 }
2418 
cmd_brief_reset()2419 void cmd_brief_reset()
2420 {
2421 	int i, j;
2422 	static int inited = 0;
2423 
2424 	if (inited) {
2425 		for (i=0; i<MAX_TVT_TEAMS; i++) {
2426 			for (j=0; j<Cmd_briefs[i].num_stages; j++) {
2427 				Cmd_briefs[i].stage[j].text = "";
2428 			}
2429 		}
2430 	}
2431 
2432 	inited = 1;
2433 	for (i=0; i<MAX_TVT_TEAMS; i++)
2434 		Cmd_briefs[i].num_stages = 0;
2435 }
2436 
2437 /**
2438  * Should briefing advance to the next stage?
2439  */
brief_time_to_advance(int stage_num)2440 int brief_time_to_advance(int stage_num)
2441 {
2442 	if (brief_get_closeup_icon() != NULL)
2443 		return 0;
2444 
2445 	if (Briefing_paused)
2446 		return 0;
2447 
2448 	if ( !Player->auto_advance )
2449 		return 0;
2450 
2451 	if (!brief_text_wipe_finished())
2452 		return 0;
2453 
2454 	if (Voice_ended_time && (timer_get_milliseconds() - Voice_ended_time >= STAGE_ADVANCE_DELAY))
2455 		return 1;
2456 
2457 	// check normal speech
2458 	if (Briefing_voice_enabled && (Brief_voices[stage_num] >= 0)) {
2459 		if (audiostream_is_playing(Brief_voices[stage_num])) {
2460 			return 0;
2461 		}
2462 
2463 		if (!Voice_ended_time) {
2464 			Voice_ended_time = timer_get_milliseconds();
2465 		}
2466 
2467 		return 0;
2468 	}
2469 
2470 	// check simulated speech
2471 	if (Briefing_voice_enabled && (Brief_voices[stage_num] < 0) && fsspeech_play_from(FSSPEECH_FROM_BRIEFING)) {
2472 		if (fsspeech_playing()) {
2473 			return 0;
2474 		}
2475 
2476 		if (!Voice_ended_time) {
2477 			Voice_ended_time = timer_get_milliseconds();
2478 		}
2479 
2480 		return 0;
2481 	}
2482 
2483 	// if we get here, there is no voice, so we simulate the time it would take instead
2484 	if (!Voice_ended_time)
2485 		Voice_ended_time = Voice_started_time + MAX(5000, Num_brief_text_lines[0] * 3500);
2486 
2487 	return 0;
2488 }
2489