]> icculus.org git repositories - taylor/freespace2.git/blob - src/mission/missionbriefcommon.cpp
fix missing debrief text, crash on exit, path separator's, warning fixes, no GR_SOFT
[taylor/freespace2.git] / src / mission / missionbriefcommon.cpp
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
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/Mission/MissionBriefCommon.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * C module for briefing code common to FreeSpace and FRED
16  *
17  * $Log$
18  * Revision 1.10  2004/07/04 11:39:06  taylor
19  * fix missing debrief text, crash on exit, path separator's, warning fixes, no GR_SOFT
20  *
21  * Revision 1.9  2003/06/11 18:30:32  taylor
22  * plug memory leaks
23  *
24  * Revision 1.8  2003/06/03 04:00:40  taylor
25  * Polish language support (Janusz Dziemidowicz)
26  *
27  * Revision 1.7  2003/05/25 02:30:42  taylor
28  * Freespace 1 support
29  *
30  * Revision 1.6  2002/06/17 06:33:09  relnev
31  * ryan's struct patch for gcc 2.95
32  *
33  * Revision 1.5  2002/06/09 04:41:22  relnev
34  * added copyright header
35  *
36  * Revision 1.4  2002/06/02 04:26:34  relnev
37  * warning cleanup
38  *
39  * Revision 1.3  2002/06/01 07:12:33  relnev
40  * a few NDEBUG updates.
41  *
42  * removed a few warnings.
43  *
44  * Revision 1.2  2002/05/07 03:16:46  theoddone33
45  * The Great Newline Fix
46  *
47  * Revision 1.1.1.1  2002/05/03 03:28:10  root
48  * Initial import.
49  *
50  * 
51  * 19    11/02/99 3:23p Jefff
52  * translate briefing icon names
53  * 
54  * 18    9/09/99 9:44a Jefff
55  * doh, fixed reversed brief text color thingy.  i am stoopid.
56  * 
57  * 17    9/08/99 11:14a Jefff
58  * toned down hostile/friendly colors in briefing text
59  * 
60  * 16    9/07/99 12:20p Mikeb
61  * return pos of briefing icon even it does not fit on screen.
62  * 
63  * 15    9/03/99 1:32a Dave
64  * CD checking by act. Added support to play 2 cutscenes in a row
65  * seamlessly. Fixed super low level cfile bug related to files in the
66  * root directory of a CD. Added cheat code to set campaign mission # in
67  * main hall.
68  * 
69  * 14    8/10/99 7:28p Jefff
70  * shuffled some text around
71  * 
72  * 13    7/30/99 3:05p Jefff
73  * Fixed briefing icon fades -- in and out were reversed.
74  * 
75  * 12    7/26/99 1:52p Mikeb
76  * Fixed strange briefing bug where a NULL wasn't being checked for when
77  * copying briefing stage text. Odd.
78  * 
79  * 11    7/24/99 6:15p Jefff
80  * moved "stage x of y" text in multiplayer mode so its not covered by the
81  * chatbox
82  * 
83  * 10    7/20/99 7:09p Jefff
84  * briefing text occupies full window in 1024x768
85  * 
86  * 9     7/09/99 5:54p Dave
87  * Seperated cruiser types into individual types. Added tons of new
88  * briefing icons. Campaign screen.
89  * 
90  * 8     6/29/99 7:39p Dave
91  * Lots of small bug fixes.
92  * 
93  * 7     2/05/99 7:19p Neilk
94  * Removed black part from mission screen, fixed info text coords
95  * 
96  * 6     1/29/99 4:17p Dave
97  * New interface screens.
98  * 
99  * 5     12/18/98 1:13a Dave
100  * Rough 1024x768 support for Direct3D. Proper detection and usage through
101  * the launcher.
102  * 
103  * 4     10/23/98 3:51p Dave
104  * Full support for tstrings.tbl and foreign languages. All that remains
105  * is to make it active in Fred.
106  * 
107  * 3     10/13/98 9:28a Dave
108  * Started neatening up freespace.h. Many variables renamed and
109  * reorganized. Added AlphaColors.[h,cpp]
110  * 
111  * 2     10/07/98 10:53a Dave
112  * Initial checkin.
113  * 
114  * 1     10/07/98 10:49a Dave
115  * 
116  * 122   6/09/98 10:31a Hoffoss
117  * Created index numbers for all xstr() references.  Any new xstr() stuff
118  * added from here on out should be added to the end if the list.  The
119  * current list count can be found in FreeSpace.cpp (search for
120  * XSTR_SIZE).
121  * 
122  * 121   6/05/98 9:54a Lawrance
123  * OEM changes
124  * 
125  * 120   6/01/98 11:43a John
126  * JAS & MK:  Classified all strings for localization.
127  * 
128  * 119   5/23/98 10:38p Lawrance
129  * Avoid doing a cfile refresh when running debug
130  * 
131  * 118   5/23/98 6:49p Lawrance
132  * Fix problems with refreshing the file list when a CD is inserted
133  * 
134  * 117   5/21/98 6:57p Lawrance
135  * Don't prompt for the CD if voice not found
136  * 
137  * 116   5/21/98 12:35a Lawrance
138  * Tweak how CD is checked for
139  * 
140  * 115   5/12/98 11:46a John
141  * Changed the way the "glowing movement" type text draw.   Use Hoffoss's
142  * gr_get_string_size optional length parameter to determine length of
143  * string which accounts for kerning on the last character and then I only
144  * draw each character only once.
145  * 
146  * 114   5/08/98 5:32p Lawrance
147  * prompt for CD if can't load animations or voice
148  * 
149  * 113   5/06/98 5:30p John
150  * Removed unused cfilearchiver.  Removed/replaced some unused/little used
151  * graphics functions, namely gradient_h and _v and pixel_sp.   Put in new
152  * DirectX header files and libs that fixed the Direct3D alpha blending
153  * problems.
154  * 
155  * 112   4/27/98 9:08p Allender
156  * fix the debriefing stage problems when clients get to screen long after
157  * server
158  * 
159  * 111   4/25/98 3:49p Lawrance
160  * Save briefing auto-advance pref
161  * 
162  * 110   4/20/98 3:53p Lawrance
163  * Fix various bugs with auto-advancing through briefings.
164  * 
165  * $NoKeywords: $
166  */
167
168 #include "freespace.h"
169 #include "ship.h"
170 #include "key.h"
171 #include "2d.h"
172 #include "3d.h"
173 #include "line.h"
174 #include "timer.h"
175 #include "math.h"
176 #include "linklist.h"
177 #include "mouse.h"
178 #include "hud.h"
179 #include "osapi.h"
180 #include "object.h"
181 #include "multi.h"
182 #include "bmpman.h"
183 #include "missionbrief.h"
184 #include "missiongrid.h"
185 #include "missionbriefcommon.h"
186 #include "animplay.h"
187 #include "fvi.h"
188 #include "float.h"
189 #include "gamesnd.h"
190 #include "cmdline.h"
191 #include "parselo.h"
192 #include "audiostr.h"
193 #include "missioncmdbrief.h"
194 #include "missiondebrief.h"
195 #include "alphacolors.h"
196 #include "localize.h"
197 #include "vecmat.h"
198
199 // --------------------------------------------------------------------------------------
200 // briefing icons
201 // --------------------------------------------------------------------------------------
202 hud_frames      Icon_bitmaps[MAX_BRIEF_ICONS][MAX_SPECIES_NAMES];
203 hud_anim                Icon_highlight_anims[MAX_BRIEF_ICONS][MAX_SPECIES_NAMES];
204 hud_anim                Icon_fade_anims[MAX_BRIEF_ICONS][MAX_SPECIES_NAMES];
205
206
207 // --------------------------------------------------------------------------------------
208 // briefing screen
209 // --------------------------------------------------------------------------------------
210
211 brief_screen bscreen;
212
213 // briefing screen sections
214 #define BRIEF_CUP_X1                    400
215 #define BRIEF_CUP_Y1                    70
216 #define BRIEF_CUP_X2                    639
217 #define BRIEF_CUP_Y2                    245
218 #define BRIEF_CUPINFO_X1        445
219 #define BRIEF_CUPINFO_Y1        247
220 #define BRIEF_CUPINFO_X2        639
221 #define BRIEF_CUPINFO_Y2        438
222
223 char *Brief_static_name[GR_NUM_RESOLUTIONS] = {
224         "BriefMap",
225         "2_BriefMap"
226 };
227
228 int Brief_static_coords[GR_NUM_RESOLUTIONS][2] = {
229         { // GR_640
230 #ifdef MAKE_FS1
231                 42, 122
232 #else
233                 10, 130
234 #endif
235         },
236         { // GR_1024
237                 15, 208
238         }
239 };
240
241 int Brief_bmap_coords[GR_NUM_RESOLUTIONS][2] = {
242         { // GR_640
243 #ifdef MAKE_FS1
244                 42, 122
245 #else
246                 0, 115
247 #endif
248         },
249         { // GR_1024
250                 0, 184
251         }
252 };
253
254 int Brief_grid_coords[GR_NUM_RESOLUTIONS][4] = {
255         { // GR_640
256 #ifdef MAKE_FS1
257                 54, 133, 530, 239
258 #else
259                 19, 147, 555, 232
260 #endif
261         },
262         { // GR_1024
263                 30, 235, 888, 371
264         }
265 };
266
267 int Brief_text_coords[GR_NUM_RESOLUTIONS][4] = {
268         { // GR_640
269 #ifdef MAKE_FS1
270                 49, 415, 374, 56
271 #else
272                 28, 399, 395, 74
273 #endif
274         },
275         { // GR_1024
276                 46, 637, 630, 120
277         }
278 };
279
280 int Brief_stage_text_coords[GR_NUM_RESOLUTIONS][2] = {
281         { // GR_640
282 #ifdef MAKE_FS1
283                 61, 387
284 #else
285                 138, 117
286 #endif
287         },
288         { // GR_1024
289                 227, 194
290         }
291 };
292
293 int Brief_stage_text_coords_multi[GR_NUM_RESOLUTIONS][2] = {
294         { // GR_640
295 #ifdef MAKE_FS1
296                 61, 387
297 #else
298                 479, 385
299 #endif
300         },
301         { // GR_1024
302                 821, 616
303         }
304 };
305
306 int Brief_text_max_lines[GR_NUM_RESOLUTIONS] = {
307         6, 6
308 };
309
310 #define LOOKAT_DIST     500.0f
311
312 // --------------------------------------------------------------------------------------
313 // Game-wide global data
314 // --------------------------------------------------------------------------------------
315 briefing                Briefings[MAX_TEAMS];                   // there is exactly one briefing per mission
316 debriefing      Debriefings[MAX_TEAMS];                 // there can be multiple debriefings per mission
317 briefing                *Briefing;                                                      // pointer used in code -- points to correct briefing
318 debriefing      *Debriefing;                                            // pointer to correct debriefing
319
320 int                     Briefing_voice_enabled=1;               // flag which turn on/off voice playback of briefings/debriefings
321
322 // --------------------------------------------------------------------------------------
323 // Module global data
324 // --------------------------------------------------------------------------------------
325
326 static int Last_new_stage;
327 int     Cur_brief_id;
328
329 const char BRIEF_META_CHAR = '$';
330
331 // static int Brief_voice_ask_for_cd;
332
333 // camera related
334 static vector   Current_cam_pos;                // current camera position
335 static vector   Target_cam_pos;         // desired camera position
336 static matrix   Current_cam_orient;     // current camera orientation
337 static matrix   Target_cam_orient;      // desired camera orientation
338 static matrix   Start_cam_orient;               // start camera orientation
339 static vector   Start_cam_pos;                  // position of camera at the start of a translation
340 static vector   Cam_vel;                                        //      camera velocity
341 static vector   Current_lookat_pos;     // lookat point
342 static vector   Target_lookat_pos;      // lookat point
343 static vector   Start_lookat_pos;
344 static vector   Lookat_vel;                             //      lookat point velocity
345
346 static float    Start_cam_move;         // time at which camera started moving (seconds)
347 static float    Total_move_time;                // time in which camera should move from current pos to target pos (seconds)
348 static float    Elapsed_time;
349
350 static float    Start_dist;
351 static float    End_dist;
352 static float    Dist_change_rate;
353
354 static vector   Acc_limit;
355 static vector   Vel_limit;
356
357 static float    Total_dist;
358 static float    Peak_speed;
359 static float    Cam_accel;
360 static float    Last_dist;
361 static vector   W_init;
362
363 // flag to indicate that the sound for a spinning highlight animation has played
364 static int Brief_stage_highlight_sound_handle = -1;
365
366 // used for scrolling briefing text ( if necessary )
367 int             Num_brief_text_lines[MAX_TEXT_STREAMS];
368 int             Top_brief_text_line;
369 static char             Brief_text[MAX_BRIEF_LINES][MAX_BRIEF_LINE_LEN];
370
371 // Used to support drawing colored text for the briefing.  Gets complicates since we
372 // need to be able to draw one character at a time as well when the briefing text
373 // first appears.
374 typedef struct colored_char
375 {
376         char    letter;
377         ubyte   color;          // index into Brief_text_colors[]
378 } colored_char;
379
380 static colored_char Colored_text[MAX_TEXT_STREAMS][MAX_BRIEF_LINES][MAX_BRIEF_LINE_LEN];
381 static int Colored_text_len[MAX_TEXT_STREAMS][MAX_BRIEF_LINES];
382
383 #define MAX_BRIEF_TEXT_COLORS                   9
384 #define BRIEF_TEXT_WHITE                                0
385 #define BRIEF_TEXT_BRIGHT_WHITE         1
386 #define BRIEF_TEXT_RED                                  2
387 #define BRIEF_TEXT_GREEN                                3
388 #define BRIEF_TEXT_YELLOW                               4
389 #define BRIEF_TEXT_BLUE                                 5
390 #define BRIEF_IFF_FRIENDLY                              6
391 #define BRIEF_IFF_HOSTILE                               7
392 #define BRIEF_IFF_NEUTRAL                               8
393
394 color Brief_color_red, Brief_color_green;
395
396 color *Brief_text_colors[MAX_BRIEF_TEXT_COLORS] = 
397 {
398         &Color_white,
399         &Color_bright_white,
400         &Color_red,
401         &Color_green,
402         &Color_yellow,
403         &Color_blue,
404         &Brief_color_green,
405         &Brief_color_red,
406         &IFF_colors[IFF_COLOR_NEUTRAL][0],
407 };
408
409 #define BRIGHTEN_LEAD   2
410
411 float Brief_text_wipe_time_elapsed;
412 static int Max_briefing_line_len;
413
414 static int Brief_voice_ended;
415 static int Brief_textdraw_finished;
416 static int Brief_voice_started;
417 static int Brief_stage_time;
418
419 const float             BRIEF_TEXT_WIPE_TIME    = 1.5f;         // time in seconds for wipe to occur
420 static int              Brief_text_wipe_snd;                                    // sound handle of sound effect for text wipe
421 static int              Play_brief_voice;
422
423 // animation stuff
424 static int              Play_highlight_flag;
425 static int              Cam_target_reached;
426 static int              Cam_movement_done;
427
428 // moving icons
429 typedef struct icon_move_info
430 {
431         icon_move_info  *next, *prev;
432         int                             used;
433         int                             id;
434         vector                  start;
435         vector                  finish;
436         vector                  current;
437
438         // used to move icons smoothly
439         vector                  direction;
440         float                           total_dist;
441         float                           accel;
442         float                           total_move_time;
443         float                           peak_speed;
444         int                             reached_dest;
445         float                           last_dist;
446 } icon_move_info;
447
448 #define MAX_MOVE_ICONS  10
449 icon_move_info  Icon_movers[MAX_MOVE_ICONS];
450 icon_move_info  Icon_move_list; // head of linked list
451
452 // fading out icons
453 typedef struct fade_icon
454 {
455         hud_anim        fade_anim;              // anim info
456         vector  pos;
457         int             team;
458 } fade_icon;
459
460 #define         MAX_FADE_ICONS  30
461 fade_icon       Fading_icons[MAX_FADE_ICONS];
462 int                     Num_fade_icons;
463
464 // voice id's for briefing text
465 int Brief_voices[MAX_BRIEF_STAGES];
466
467 cmd_brief *Cur_cmd_brief;
468 cmd_brief Cmd_briefs[MAX_TEAMS];
469
470 // --------------------------------------------------------------------------------------
471 // forward declarations
472 // --------------------------------------------------------------------------------------
473 void    brief_render_elements(vector *pos, grid *gridp);
474 void    brief_render_icons(int stage_num, float frametime);
475 void    brief_parse_icon_tbl();
476 void    brief_grid_read_camera_controls( control_info * ci, float frametime );
477 void    brief_maybe_create_new_grid(grid *gridp, vector *pos, matrix *orient, int force = 0);
478 grid    *brief_create_grid(grid *gridp, vector *forward, vector *right, vector *center, int nrows, int ncols, float square_size);
479 grid    *brief_create_default_grid(void);
480 void    brief_render_grid(grid *gridp);
481 void    brief_modify_grid(grid *gridp);
482 void    brief_rpd_line(vector *v0, vector *v1);
483 void    brief_set_text_color(int color_index);
484 extern void get_camera_limits(matrix *start_camera, matrix *end_camera, float time, vector *acc_max, vector *w_max);
485 int brief_text_wipe_finished();
486
487 void brief_set_icon_color(int team)
488 {
489         switch (team) { 
490         case TEAM_FRIENDLY:     SET_COLOR_FRIENDLY;     break;
491         case TEAM_HOSTILE:      SET_COLOR_HOSTILE;      break;
492         case TEAM_NEUTRAL:      SET_COLOR_NEUTRAL;      break;
493         case TEAM_TRAITOR:      SET_COLOR_HOSTILE;      break;
494         default:
495                 SET_COLOR_HOSTILE;      break;
496         } // end switch
497 }
498
499 // --------------------------------------------------------------------------------------
500 //      brief_move_icon_reset()
501 //
502 //
503 void brief_move_icon_reset()
504 {
505         int i;
506
507         list_init(&Icon_move_list);
508         for ( i = 0; i < MAX_MOVE_ICONS; i++ )
509                 Icon_movers[i].used = 0;
510 }
511
512
513 // --------------------------------------------------------------------------------------
514 // Does one time initialization of the briefing and debriefing structures.
515 // Namely setting all malloc'ble pointers to NULL.  Called once at game startup.
516 void mission_brief_common_init()
517 {
518         int i,j;
519
520         // setup brief text colors
521         gr_init_alphacolor( &Brief_color_green, 50, 100, 50, 255 );
522         gr_init_alphacolor( &Brief_color_red, 140, 20, 20, 255 );
523
524         // extra catch to reset anything that's already loaded (ie. mission restart)
525         mission_brief_common_reset();
526         mission_debrief_common_reset();
527
528         if ( Fred_running )     {
529                 // If Fred is running malloc out max space
530                 for (i=0; i<MAX_TEAMS; i++ )    {
531                         for (j=0; j<MAX_BRIEF_STAGES; j++ )     {
532                                 Briefings[i].stages[j].new_text = (char *)malloc(MAX_BRIEF_LEN);
533                                 Assert(Briefings[i].stages[j].new_text!=NULL);
534                                 Briefings[i].stages[j].icons = (brief_icon *)malloc(sizeof(brief_icon)*MAX_STAGE_ICONS);
535                                 Assert(Briefings[i].stages[j].icons!=NULL);
536                                 Briefings[i].stages[j].lines = (brief_line *)malloc(sizeof(brief_line)*MAX_BRIEF_STAGE_LINES);
537                                 Assert(Briefings[i].stages[j].lines!=NULL);
538                                 Briefings[i].stages[j].num_icons = 0;
539                                 Briefings[i].stages[j].num_lines = 0;
540                         }
541                 }
542
543                 for (i=0; i<MAX_TEAMS; i++ )    {
544                         for (j=0; j<MAX_DEBRIEF_STAGES; j++ )   {
545                                 Debriefings[i].stages[j].new_text = (char *)malloc(MAX_DEBRIEF_LEN);
546                                 Assert(Debriefings[i].stages[j].new_text!=NULL);
547                                 Debriefings[i].stages[j].new_recommendation_text = (char *)malloc(MAX_RECOMMENDATION_LEN);
548                                 Assert(Debriefings[i].stages[j].new_recommendation_text!=NULL);
549                         }
550                 }
551
552         } else {
553                 // If game is running don't malloc anything
554                 for (i=0; i<MAX_TEAMS; i++ )    {
555                         for (j=0; j<MAX_BRIEF_STAGES; j++ )     {
556                                 Briefings[i].stages[j].new_text = NULL;
557                                 Briefings[i].stages[j].num_icons = 0;
558                                 Briefings[i].stages[j].icons = NULL;
559                                 Briefings[i].stages[j].num_lines = 0;
560                                 Briefings[i].stages[j].lines = NULL;
561                         }
562                 }
563
564                 for (i=0; i<MAX_TEAMS; i++ )    {
565                         for (j=0; j<MAX_DEBRIEF_STAGES; j++ )   {
566                                 Debriefings[i].stages[j].new_text = NULL;
567                                 Debriefings[i].stages[j].new_recommendation_text = NULL;
568                         }
569                 }
570
571         }
572
573                 
574 }
575
576 //--------------------------------------------------------------------------------------
577 // Frees all the memory allocated in the briefing and debriefing structures
578 // and sets all pointers to NULL.
579 void mission_brief_common_reset()
580 {
581         int i,j;
582
583         if ( Fred_running )     {
584                 return;                                         // Don't free these under Fred.
585         }
586
587         for (i=0; i<MAX_TEAMS; i++ )    {
588                 for (j=0; j<MAX_BRIEF_STAGES; j++ )     {
589                         if ( Briefings[i].stages[j].new_text )  {
590                                 free(Briefings[i].stages[j].new_text);
591                                 Briefings[i].stages[j].new_text = NULL;                 
592                         }
593         
594                         if ( Briefings[i].stages[j].icons )     {
595                                 free(Briefings[i].stages[j].icons);
596                                 Briefings[i].stages[j].icons = NULL;
597                         }
598
599                         if ( Briefings[i].stages[j].lines )     {
600                                 free(Briefings[i].stages[j].lines);
601                                 Briefings[i].stages[j].lines = NULL;
602                         }
603                 }
604         }
605 }
606
607 // split from above since we need to clear them separately
608 void mission_debrief_common_reset()
609 {
610         int i, j;
611
612         if ( Fred_running ) {
613                 return;                                         // Don't free these under Fred.
614         }
615
616         for (i=0; i<MAX_TEAMS; i++ )    {
617                 for (j=0; j<MAX_DEBRIEF_STAGES; j++ )   {
618                         if ( Debriefings[i].stages[j].new_text )        {
619                                 free(Debriefings[i].stages[j].new_text);
620                                 Debriefings[i].stages[j].new_text = NULL;
621                         }
622
623                         if ( Debriefings[i].stages[j].new_recommendation_text ) {
624                                 free(Debriefings[i].stages[j].new_recommendation_text);
625                                 Debriefings[i].stages[j].new_recommendation_text = NULL;
626                         }
627                 }
628         }               
629 }
630
631
632
633
634 // --------------------------------------------------------------------------------------
635 //      brief_reset()
636 //
637 //
638 void brief_reset()
639 {
640         int i;
641
642         Briefing = NULL;
643         for ( i = 0; i < MAX_TEAMS; i++ ) 
644                 Briefings[i].num_stages = 0;
645         Cur_brief_id = 1;
646 }
647
648 // --------------------------------------------------------------------------------------
649 //      debrief_reset()
650 //
651 //
652 void debrief_reset()
653 {
654         int i,j;
655
656         Debriefing = NULL;
657         for ( i = 0; i < MAX_TEAMS; i++ ) {
658                 Debriefings[i].num_stages = 0;
659                 for (j=0; j<MAX_DEBRIEF_STAGES; j++ )   {
660                         if ( Debriefings[i].stages[j].new_recommendation_text ) {
661                                 Debriefings[i].stages[j].new_recommendation_text[0] = 0;
662                         }
663                 }
664         }
665
666         // MWA 4/27/98 -- must initialize this variable here since we cannot do it as debrief
667         // init time because race conditions between all players in the game make that type of
668         // initialization unsafe.
669         Debrief_multi_stages_loaded = 0;
670 }
671
672 // --------------------------------------------------------------------------------------
673 //      brief_init_screen()
674 //
675 //      Set up the screen regions.  A mulitplayer briefing will look different than a single player
676 // briefing.
677 //
678 void brief_init_screen(int multiplayer_flag)
679 {
680         bscreen.map_x1                  = Brief_grid_coords[gr_screen.res][0];
681         bscreen.map_x2                  = Brief_grid_coords[gr_screen.res][0] + Brief_grid_coords[gr_screen.res][2];
682         bscreen.map_y1                  = Brief_grid_coords[gr_screen.res][1];
683         bscreen.map_y2                  = Brief_grid_coords[gr_screen.res][1] + Brief_grid_coords[gr_screen.res][3];
684         /*
685         bscreen.map_x1                  = BRIEF_GRID3_X1;
686         bscreen.map_x2                  = BRIEF_GRID0_X2;
687         bscreen.map_y1                  = BRIEF_GRID3_Y1;
688         bscreen.map_y2                  = BRIEF_GRID0_Y2+4;
689         bscreen.btext_x1                = BRIEF_TEXT_X1;
690         bscreen.btext_x2                = BRIEF_TEXT_X2;
691         bscreen.btext_y1                = BRIEF_TEXT_Y1;
692         bscreen.btext_y2                = BRIEF_TEXT_Y2;
693         bscreen.cup_x1                  = BRIEF_CUP_X1;
694         bscreen.cup_y1                  = BRIEF_CUP_Y1;
695         bscreen.cup_x2                  = BRIEF_CUP_X2;
696         bscreen.cup_y2                  = BRIEF_CUP_Y2;
697         bscreen.cupinfo_x1      = BRIEF_CUPINFO_X1;
698         bscreen.cupinfo_y1      = BRIEF_CUPINFO_Y1;
699         bscreen.cupinfo_x2      = BRIEF_CUPINFO_X2;
700         bscreen.cupinfo_y2      = BRIEF_CUPINFO_Y2;
701         */
702 }
703
704 // --------------------------------------------------------------------------------------
705 //      brief_init_icons()
706 //
707 //
708 void brief_init_icons()
709 {
710         if ( Fred_running ) {
711                 gr_init_alphacolor( &IFF_colors[IFF_COLOR_HOSTILE][0],  0xff, 0x00, 0x00, 15*16 );
712                 gr_init_alphacolor( &IFF_colors[IFF_COLOR_FRIENDLY][0], 0x00, 0xff, 0x00, 15*16 );
713         }
714
715         // Load in the bitmaps for the icons from icons.tbl
716         brief_parse_icon_tbl();
717 }
718
719 // Reset the highlight and fade anims... call before brief_parse_icon_tbl();
720 void brief_init_anims()
721 {
722 #ifndef MAKE_FS1
723         int idx;
724 #endif
725         int i;
726
727         for (i=0; i<MAX_BRIEF_ICONS; i++) {
728 #ifndef MAKE_FS1
729                 for(idx=0; idx<MAX_SPECIES_NAMES; idx++){
730                         Icon_highlight_anims[i][idx].num_frames=0;
731                         Icon_fade_anims[i][idx].num_frames=0;
732                 }
733 #else
734                 // one set of icons for all species in FS1
735                 Icon_highlight_anims[i][1].num_frames=0;
736                 Icon_fade_anims[i][1].num_frames=0;
737 #endif
738         }
739 }
740
741 // ------------------------------------------------------------------------
742 //      brief_unload_icons() 
743 //
744 //
745 void brief_unload_icons()
746 {
747         hud_frames              *ib;
748         int                             i, j, idx;
749
750         for ( i = 0; i < MAX_BRIEF_ICONS; i++ ) {
751                 for(idx=0; idx<MAX_SPECIES_NAMES; idx++){
752                         ib = &Icon_bitmaps[i][idx];
753
754                         for ( j=0; j<ib->num_frames; j++ ) {
755                                 bm_unload(ib->first_frame+j);
756                         }
757                 }
758         }
759 }
760
761 // determine if icon type is used in the current briefing
762 int brief_icon_used_in_briefing(int icon_type)
763 {
764         int num_icons, num_stages, i, j;
765
766         num_stages = Briefing->num_stages;
767
768         for ( i = 0; i < num_stages; i++ ) {
769                 num_icons = Briefing->stages[i].num_icons;
770                 for ( j = 0; j < num_icons; j++ ) {
771                         if ( Briefing->stages[i].icons[j].type == icon_type ) {
772                                 return 1;
773                         }
774                 }
775         }
776
777         return 0;
778 }
779
780 // --------------------------------------------------------------------------------------
781 //      brief_parse_icon_tbl()
782 //
783 //
784 void brief_parse_icon_tbl()
785 {
786         int                     num_icons, rval;
787         char                    name[NAME_LENGTH];
788         hud_frames      *hf;
789         hud_anim                *ha;
790         int idx;
791
792         // open localization
793         lcl_ext_open();
794
795         if ((rval = setjmp(parse_abort)) != 0) {
796                 Error(LOCATION, "Unable to parse icons.tbl!  Code = %i.\n", rval);
797         }
798         else {
799                 read_file_text("icons.tbl");
800                 reset_parse();          
801         }
802
803         num_icons = 0;
804         required_string("#Start");
805
806
807         int load_this_icon = 0;
808
809         while (required_string_either("#End","$Name:")) {
810                 for(idx=0; idx<MAX_SPECIES_NAMES; idx++){
811                         Assert( num_icons < MAX_BRIEF_ICONS);
812                         hf = &Icon_bitmaps[num_icons][idx];
813
814                         // load in regular frames
815                         required_string("$Name:");
816                         stuff_string(name, F_NAME, NULL);
817
818                         if ( Fred_running ) {
819                                 load_this_icon = 1;
820                         } else {
821                                 load_this_icon = brief_icon_used_in_briefing(num_icons);
822                         }
823
824                         if ( load_this_icon ) {
825                                 hf->first_frame = bm_load_animation(name, &hf->num_frames);
826                                 if ( hf->first_frame == -1 ) {
827                                         Int3(); // missing briefing icon
828                                 }
829                         }
830
831                         // load in fade frames
832                         required_string("$Name:");
833                         stuff_string(name, F_NAME, NULL);
834                         ha = &Icon_fade_anims[num_icons][idx];
835                         hud_anim_init(ha, 0, 0, name);
836
837                         // load in highlighting frames
838                         required_string("$Name:");
839                         stuff_string(name, F_NAME, NULL);
840                         ha = &Icon_highlight_anims[num_icons][idx];
841                         hud_anim_init(ha, 0, 0, name);
842                 }
843
844                 // next icon _type_
845                 num_icons++;
846         }
847         required_string("#End");
848
849         // close localization
850         lcl_ext_close();
851 }
852
853 // --------------------------------------------------------------------------------------
854 //      brief_close_map()
855 //
856 //
857 void brief_close_map()
858 {
859         brief_unload_icons();
860 }
861
862 void brief_preload_highlight_anim(brief_icon *bi)
863 {
864         hud_anim *ha;
865         int species = ship_get_species_by_type(bi->ship_class);
866
867         if(species < 0){
868                 return;
869         }
870
871         ha = &Icon_highlight_anims[bi->type][species];
872         if ( !stricmp(NOX("none"), ha->name) ) {
873                 return;
874         }
875
876         // force read of data from disk, so we don't glitch on initial playback
877         if ( ha->first_frame == -1 ) {
878                 hud_anim_load(ha);
879                 Assert(ha->first_frame >= 0);
880         }
881
882         bi->highlight_anim = *ha;
883
884         gr_set_bitmap(ha->first_frame);
885         gr_aabitmap(0, 0);
886 }
887
888 void brief_preload_fade_anim(brief_icon *bi)
889 {
890         hud_anim *ha;
891         int species = ship_get_species_by_type(bi->ship_class);
892
893         if(species < 0){
894                 return;
895         }
896
897         ha = &Icon_fade_anims[bi->type][species];
898         if ( !stricmp(NOX("none"), ha->name) ) {
899                 return;
900         }
901
902         // force read of data from disk, so we don't glitch on initial playback
903         if ( ha->first_frame == -1 ) {
904                 hud_anim_load(ha);
905                 Assert(ha->first_frame >= 0);
906         }
907
908         gr_set_bitmap(ha->first_frame);
909         gr_aabitmap(0, 0);
910 }
911
912 // preload highlight, fadein and fadeout animations that are used in this stage
913 void brief_preload_anims()
914 {
915         int                     num_icons, num_stages, i, j;
916         brief_icon      *bi;
917
918         num_stages = Briefing->num_stages;
919
920         for ( i = 0; i < num_stages; i++ ) {
921                 num_icons = Briefing->stages[i].num_icons;
922                 for ( j = 0; j < num_icons; j++ ) {
923                         bi = &Briefing->stages[i].icons[j];
924                         if ( bi->flags & BI_HIGHLIGHT ) {
925                                 brief_preload_highlight_anim(bi);
926                         }
927                         brief_preload_fade_anim(bi);
928                 }
929         }
930 }
931
932 // --------------------------------------------------------------------------------------
933 //      brief_init_map()
934 //
935 //
936 void brief_init_map()
937 {
938         vector *pos;
939         matrix *orient;
940
941         Assert( Briefing != NULL );
942
943         pos = &Briefing->stages[0].camera_pos;
944         orient = &Briefing->stages[0].camera_orient;
945         vm_vec_zero(&Current_lookat_pos);
946         vm_vec_zero(&Target_lookat_pos);
947         Elapsed_time = 0.0f;
948         Total_move_time = 0.0f;
949
950         The_grid = brief_create_default_grid();
951         brief_maybe_create_new_grid(The_grid, pos, orient, 1);
952
953         brief_init_anims();
954         brief_init_icons();
955         brief_move_icon_reset();
956         brief_preload_anims();
957
958         Brief_text_wipe_snd = -1;
959         Last_new_stage = -1;
960         Num_fade_icons=0;
961 }
962
963 #ifndef PLAT_UNIX
964 #pragma optimize("", off)
965 #endif
966
967 // render fade-out anim frame
968
969 void brief_render_fade_outs(float frametime)
970 {
971         int                     i,bx,by,w,h;
972         float                   bxf,byf;
973         vertex          tv;                     // temp vertex used to find screen position for text
974         fade_icon       *fi;
975         
976
977         for (i=0; i<Num_fade_icons; i++) {
978                 fi = &Fading_icons[i];
979
980                 g3_rotate_vertex(&tv, &fi->pos);
981
982                 if (!(tv.flags & PF_PROJECTED))
983                         g3_project_vertex(&tv);
984
985                 if (!(tv.flags & PF_OVERFLOW) ) {  // make sure point projected before drawing text
986
987                         brief_set_icon_color(fi->team);
988
989                         if ( fi->fade_anim.first_frame < 0 ) {
990                                 continue;
991                         }
992
993                         bm_get_info( fi->fade_anim.first_frame, &w, &h, NULL);
994                         
995                         bxf = tv.sx - w / 2.0f + 0.5f;
996                         byf = tv.sy - h / 2.0f + 0.5f;
997                         bx = fl2i(bxf);
998                         by = fl2i(byf);
999
1000                         if ( fi->fade_anim.first_frame >= 0 ) {
1001                                 fi->fade_anim.sx = bx;
1002                                 fi->fade_anim.sy = by;
1003 #ifdef MAKE_FS1
1004                                 // FS1 has the anis reversed from FS2 so play them backwards
1005                                 hud_anim_render(&fi->fade_anim, frametime, 1, 0, 0, 1);
1006 #else                   
1007                                 hud_anim_render(&fi->fade_anim, frametime, 1, 0, 0, 0);
1008 #endif
1009                         }
1010                 }
1011         }
1012 }
1013
1014 // figure out how far an icon should move based on the elapsed time
1015 float brief_icon_get_dist_moved(icon_move_info *mi, float elapsed_time)
1016 {
1017         float time, dist_moved=0.0f;
1018         
1019         // first half of movement
1020         if ( elapsed_time < mi->total_move_time/2.0f ) {
1021                 dist_moved=0.5f*mi->accel*elapsed_time*elapsed_time;    // d = 1/2at^2
1022                 return dist_moved;
1023         }
1024
1025         // second half of movement
1026         time=elapsed_time - mi->total_move_time/2.0f;
1027         dist_moved=(mi->total_dist/2.0f)+(mi->peak_speed*time) - 0.5f*mi->accel*time*time;
1028         return dist_moved;
1029 }
1030
1031 // Draw a line between two icons on the briefing screen
1032 void brief_render_icon_line(int stage_num, int line_num)
1033 {
1034         brief_line      *bl;
1035         brief_icon      *icon[2];
1036         vertex          icon_vertex[2];
1037         int                     icon_status[2] = {0,0};
1038         int                     icon_w, icon_h, i;
1039         float                   icon_x[2], icon_y[2];
1040
1041         bl = &Briefing->stages[stage_num].lines[line_num];
1042         icon[0] = &Briefing->stages[stage_num].icons[bl->start_icon];
1043         icon[1] = &Briefing->stages[stage_num].icons[bl->end_icon];
1044
1045         // project icons
1046         for (i=0; i<2; i++) {
1047                 g3_rotate_vertex(&icon_vertex[i],&icon[i]->pos);
1048                 if (!(icon_vertex[i].flags&PF_PROJECTED))
1049                         g3_project_vertex(&icon_vertex[i]);
1050
1051                 if (!(icon_vertex[i].flags & PF_OVERFLOW) ) {  // make sure point projected before drawing text
1052                         icon_status[i]=1;
1053                 }
1054         }
1055
1056         if ( !icon_status[0] || !icon_status[1] ) {
1057                 return;
1058         }
1059
1060         // get screen (x,y) for icons
1061         for (i=0; i<2; i++) {
1062                 brief_common_get_icon_dimensions(&icon_w, &icon_h, icon[i]->type, icon[i]->ship_class);
1063                 icon_x[i] = icon_vertex[i].sx;
1064                 icon_y[i] = icon_vertex[i].sy;
1065         }
1066
1067         brief_set_icon_color(icon[0]->team);
1068
1069         gr_line(fl2i(icon_x[0]), fl2i(icon_y[0]), fl2i(icon_x[1]), fl2i(icon_y[1]));
1070 }
1071
1072 // -------------------------------------------------------------------------------------
1073 // Draw a briefing icon
1074 //
1075 // parameters:          stage_num               =>              briefing stage number (start at 0)
1076 //                                              icon_num                        =>              icon number in stage
1077 //                                              frametime               =>              time elapsed in seconds
1078 //                                              selected                        =>              FRED only (will be 0 or non-zero)
1079 //                                              w_scale_factor  =>              scale icon in width by this amount (default 1.0f)
1080 //                                              h_scale_factor  =>              scale icon in height by this amount (default 1.0f)
1081 void brief_render_icon(int stage_num, int icon_num, float frametime, int selected, float w_scale_factor, float h_scale_factor)
1082 {
1083         brief_icon      *bi, *closeup_icon;
1084         hud_frames      *ib;
1085         vertex          tv;     // temp vertex used to find screen position for text
1086         vector          *pos = NULL;
1087         int                     bx,by,bc,w,h,icon_w,icon_h,icon_bitmap=-1;
1088         float                   bxf, byf, dist=0.0f;
1089
1090         Assert( Briefing != NULL );
1091         
1092         bi = &Briefing->stages[stage_num].icons[icon_num];
1093
1094         icon_move_info *mi, *next;
1095         int interp_pos_found = 0;
1096         
1097         mi = GET_FIRST(&Icon_move_list);
1098         if (mi)
1099                 while ( mi != &Icon_move_list ) {
1100                         next = mi->next;
1101                         if ( ( mi->id != 0 ) && ( mi->id == bi->id ) ) {
1102
1103                                 if ( !mi->reached_dest ) {
1104                                         dist = brief_icon_get_dist_moved(mi, Elapsed_time);
1105                                         if ( dist < mi->last_dist ) {
1106                                                 mi->reached_dest=1;
1107                                                 mi->last_dist=0.0f;
1108                                         }
1109                                         mi->last_dist=dist;
1110                                 }
1111
1112                                 if ( !mi->reached_dest ) {
1113                                         vector dist_moved;
1114                                         vm_vec_copy_scale(&dist_moved, &mi->direction, dist);
1115                                         vm_vec_add(&mi->current, &mi->start, &dist_moved);
1116                                 } else {
1117                                         mi->current = mi->finish;
1118                                 }
1119
1120                                 pos = &mi->current;
1121                                 interp_pos_found = 1;
1122                                 break;
1123                         }
1124                         mi = next;
1125                 }
1126         
1127         if ( !interp_pos_found )
1128                 pos = &bi->pos;
1129                 
1130         brief_render_elements(pos, The_grid);
1131         g3_rotate_vertex(&tv,pos);
1132
1133         if (!(tv.flags&PF_PROJECTED))
1134                 g3_project_vertex(&tv);
1135
1136         if (!(tv.flags & PF_OVERFLOW) ) {  // make sure point projected before drawing text
1137
1138                 brief_set_icon_color(bi->team);
1139
1140                 int species = ship_get_species_by_type(bi->ship_class);
1141
1142                 if(species < 0){
1143                         return;
1144                 }
1145
1146                 ib = &Icon_bitmaps[bi->type][species];
1147                 if ( ib->first_frame < 0 ) {
1148                         Int3();
1149                         return;
1150                 }
1151
1152                 brief_common_get_icon_dimensions(&icon_w, &icon_h, bi->type, bi->ship_class);
1153
1154                 closeup_icon = brief_get_closeup_icon();
1155
1156                 if ( bi == closeup_icon || selected ) {
1157                         icon_bitmap=ib->first_frame+1;
1158 //                      gr_set_bitmap(ib->first_frame+1);
1159                 }
1160                 else {
1161                         icon_bitmap=ib->first_frame;
1162 //                      gr_set_bitmap(ib->first_frame);
1163                 }
1164         
1165                 // draw icon centered at draw position
1166 //              bx = fl2i(tv.sx - ib->icon_w/2.0f);
1167 //              by = fl2i(tv.sy - ib->icon_h/2.0f);
1168 //              bc = bx + fl2i(ib->icon_w/2.0f);
1169                 //gr_aabitmap(bx, by);
1170
1171                 float scaled_w, scaled_h;
1172                 
1173                 scaled_w = icon_w * w_scale_factor;
1174                 scaled_h = icon_h * h_scale_factor;
1175                 bxf = tv.sx - scaled_w / 2.0f + 0.5f;
1176                 byf = tv.sy - scaled_h / 2.0f + 0.5f;
1177                 bx = fl2i(bxf);
1178                 by = fl2i(byf);
1179                 bc = fl2i(tv.sx);
1180
1181                 if ( (bx < 0) || (bx > gr_screen.max_w) || (by < 0) || (by > gr_screen.max_h) && !Fred_running ) {
1182                         bi->x = bx;
1183                         bi->y = by;
1184                         return;
1185                 }
1186
1187                 vertex va, vb;
1188                 va.sx = bxf;
1189                 va.sy = byf;
1190                 va.u = 0.0f;
1191                 va.v = 0.0f;
1192
1193                 vb.sx = bxf+scaled_w-1;
1194                 vb.sy = byf+scaled_h-1;
1195                 vb.u = 1.0f;
1196                 vb.v = 1.0f;
1197
1198                 // render highlight anim frame
1199                 if ( (bi->flags&BI_SHOWHIGHLIGHT) && (bi->flags&BI_HIGHLIGHT) ) {
1200                         hud_anim *ha = &bi->highlight_anim;
1201                         if ( ha->first_frame >= 0 ) {
1202                                 ha->sx = bi->hold_x;
1203                                 if ( strlen(bi->label) > 0 ) {
1204                                         ha->sy = bi->hold_y - fl2i(gr_get_font_height()/2.0f +0.5) - 2;
1205                                 } else {
1206                                         ha->sy = bi->hold_y;
1207                                 }
1208
1209                                 //hud_set_iff_color(bi->team);
1210                                 brief_set_icon_color(bi->team);
1211
1212                                 hud_anim_render(ha, frametime, 1, 0, 1);
1213
1214                                 if ( Brief_stage_highlight_sound_handle < 0 ) {
1215                                         if ( !Fred_running) {
1216                                                 Brief_stage_highlight_sound_handle = snd_play(&Snds_iface[SND_ICON_HIGHLIGHT]);                                 
1217                                         }
1218                                 }
1219                         }
1220                 }
1221
1222                 // render fade-in anim frame
1223                 if ( bi->flags & BI_FADEIN ) {
1224                         hud_anim *ha = &bi->fadein_anim;
1225                         if ( ha->first_frame >= 0 ) {
1226                                 ha->sx = bx;
1227                                 ha->sy = by;
1228 //                              hud_set_iff_color(bi->team);
1229                                 brief_set_icon_color(bi->team);
1230
1231 #ifdef MAKE_FS1
1232                                 // FS1 has the anims backwards from FS2 so play in reverse
1233                                 if ( hud_anim_render(ha, frametime, 1, 0, 0, 0) == 0 ) {
1234 #else
1235                                 if ( hud_anim_render(ha, frametime, 1, 0, 0, 1) == 0 ) {
1236 #endif
1237                                         bi->flags &= ~BI_FADEIN;
1238                                 }
1239                         } else {
1240                                 bi->flags &= ~BI_FADEIN;
1241                         }
1242                 }               
1243
1244                 if ( !(bi->flags & BI_FADEIN) ) {
1245                         gr_set_bitmap(icon_bitmap);
1246
1247                         if ( Fred_running )     {
1248                                 gr_aascaler(&va, &vb);
1249                         } else {
1250                                 // Don't bother scaling for the game
1251                                 gr_aabitmap(bx, by);
1252                         }
1253
1254                         // draw text centered over the icon (make text darker)
1255                         if ( bi->type == ICON_FIGHTER_PLAYER || bi->type == ICON_BOMBER_PLAYER ) {
1256                                 gr_get_string_size(&w,&h,Players[Player_num].callsign);
1257                                 gr_printf(bc - fl2i(w/2.0f), by - h, Players[Player_num].callsign);
1258                         }
1259                         else {
1260                                 if (Lcl_gr) {
1261                                         char buf[128];
1262                                         strcpy(buf, bi->label);
1263                                         lcl_translate_brief_icon_name(buf);
1264                                         gr_get_string_size(&w, &h, buf);
1265                                         gr_printf(bc - fl2i(w/2.0f), by - h, buf);
1266                                 } else if(Lcl_pl) {
1267                                         char buf[128];
1268                                         strcpy(buf, bi->label);
1269                                         lcl_translate_brief_icon_name_pl(buf);
1270                                         gr_get_string_size(&w, &h, buf);
1271                                         gr_printf(bc - fl2i(w/2.0f), by - h, buf);
1272                                 } else {
1273                                         gr_get_string_size(&w,&h,bi->label);
1274                                         gr_printf(bc - fl2i(w/2.0f), by - h, bi->label);
1275                                 }
1276                         }
1277
1278                         // show icon as selected (FRED only)
1279                         if ( selected ) {
1280                                 gr_get_string_size(&w,&h,NOX("(S)"));
1281                                 gr_printf(bc - fl2i(w/2.0f), by - h*2, NOX("(S)"));
1282                         }
1283                 }
1284
1285                 // store screen x,y,w,h
1286                 bi->x = bx;
1287                 bi->y = by;
1288                 bi->w = fl2i(scaled_w);
1289                 bi->h = fl2i(scaled_h);
1290
1291         }  // end if vertex is projected
1292 }
1293
1294 #ifndef PLAT_UNIX
1295 #pragma optimize("", on)
1296 #endif
1297
1298 // -------------------------------------------------------------------------------------
1299 // brief_render_icons()
1300 //
1301 void brief_render_icons(int stage_num, float frametime)
1302 {
1303         int i, num_icons, num_lines;
1304
1305         Assert( Briefing != NULL );
1306         
1307         num_icons = Briefing->stages[stage_num].num_icons;
1308         num_lines = Briefing->stages[stage_num].num_lines;
1309
1310         if ( Cam_target_reached ) {
1311                 for ( i = 0; i < num_lines; i++ ) {
1312                         brief_render_icon_line(stage_num, i);
1313                 }
1314         }
1315
1316         for ( i = 0; i < num_icons; i++ ) {
1317                 brief_render_icon(stage_num, i, frametime, 0);
1318         }
1319 }
1320
1321 // ------------------------------------------------------------------------------------
1322 // brief_start_highlight_anims()
1323 //
1324 //      see if there are any highlight animations to play
1325 //
1326 void brief_start_highlight_anims(int stage_num)
1327 {
1328         brief_stage             *bs;
1329         brief_icon              *bi;
1330         int                             x,y,i,anim_w,anim_h;
1331
1332         Assert( Briefing != NULL );
1333         bs = &Briefing->stages[stage_num];
1334         
1335         for ( i = 0; i < bs->num_icons; i++ ) {
1336                 bi = &bs->icons[i];
1337                 if ( bi->flags & BI_HIGHLIGHT ) {
1338                         bi->flags &= ~BI_SHOWHIGHLIGHT;
1339                         if ( bi->highlight_anim.first_frame < 0 ) {
1340                                 continue;
1341                         }
1342
1343                         bi->highlight_anim.time_elapsed=0.0f;
1344
1345                         bm_get_info( bi->highlight_anim.first_frame, &anim_w, &anim_h, NULL);
1346                         x = fl2i( i2fl(bi->x) + bi->w/2.0f - anim_w/2.0f );
1347                         y = fl2i( i2fl(bi->y) + bi->h/2.0f - anim_h/2.0f );
1348                         bi->hold_x = x;
1349                         bi->hold_y = y;
1350                         bi->flags |= BI_SHOWHIGHLIGHT;
1351                         bi->highlight_anim.time_elapsed=0.0f;
1352                 }
1353         }
1354 }
1355
1356 // -------------------------------------------------------------------------------------
1357 // brief_render_map()
1358 //
1359 //
1360 void brief_render_map(int stage_num, float frametime)
1361 {
1362         brief_stage *bs;
1363
1364         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);
1365         
1366         // REMOVED by neilk: removed gr_clear for FS2 because interface no longer calls for black background on grid
1367 #ifdef MAKE_FS1
1368         gr_clear();
1369 #endif
1370
1371   if (stage_num >= Briefing->num_stages) {
1372                 gr_reset_clip();
1373                 return;
1374         }
1375
1376         Assert(Briefing);
1377         bs = &Briefing->stages[stage_num];
1378
1379         g3_start_frame(0);
1380         g3_set_view_matrix(&Current_cam_pos, &Current_cam_orient, 0.5f);
1381
1382         brief_maybe_create_new_grid(The_grid, &Current_cam_pos, &Current_cam_orient);
1383         brief_render_grid(The_grid);
1384
1385         brief_render_fade_outs(frametime);
1386
1387         // go ahead and render everything that is in the active objects list
1388         brief_render_icons(stage_num, frametime);
1389
1390         if ( Cam_target_reached && brief_text_wipe_finished() ) {
1391
1392                 if ( Brief_textdraw_finished == 0 ) {
1393                         Brief_textdraw_finished = 1;
1394                         Brief_stage_time = 0;
1395                 }
1396
1397                 if ( Play_highlight_flag ) {
1398                         brief_start_highlight_anims(stage_num);
1399                         Play_highlight_flag = 0;
1400                 }
1401         }
1402
1403         anim_render_all(ON_BRIEFING_SELECT, frametime);
1404
1405         gr_reset_clip();
1406         g3_end_frame();
1407 }
1408
1409
1410 // -------------------------------------------------------------------------------------
1411 // brief_text_set_color()
1412 //
1413 void brief_text_set_color(char c) {
1414         switch ( c ) {
1415                 case 'f':
1416                         SET_COLOR_FRIENDLY;
1417                         break;
1418                 case 'h':
1419                         SET_COLOR_HOSTILE;
1420                         break;
1421                 case 'n':
1422                         SET_COLOR_NEUTRAL;
1423                         break;
1424                 case 'r':
1425                         gr_set_color_fast(&Color_red);
1426                         break;
1427                 case 'g':
1428                         gr_set_color_fast(&Color_green);
1429                         break;
1430                 case 'b':
1431                         gr_set_color_fast(&Color_blue);
1432                         break;
1433                 default:        
1434                         Int3(); // unsupported meta-code
1435                         break;
1436         } // end switch
1437 }
1438
1439 // Display what stage of the briefing is active
1440 void brief_blit_stage_num(int stage_num, int stage_max)
1441 {
1442         char buf[64];
1443         // int w;
1444
1445         Assert( Briefing != NULL );
1446 #ifdef MAKE_FS1
1447         gr_set_color_fast(&Color_bright_blue);
1448 #else
1449         gr_set_color_fast(&Color_text_heading);
1450 #endif
1451         sprintf(buf, XSTR( "Stage %d of %d", 394), stage_num + 1, stage_max);
1452         if (Game_mode & GM_MULTIPLAYER) {
1453                 gr_printf(Brief_stage_text_coords_multi[gr_screen.res][0], Brief_stage_text_coords_multi[gr_screen.res][1], buf);
1454         } else {
1455                 gr_printf(Brief_stage_text_coords[gr_screen.res][0], Brief_stage_text_coords[gr_screen.res][1], buf);
1456         }
1457
1458         // draw the title of the mission        
1459         // if this goes above briefing text, it will need to be raised 10 pixels in multiplayer to make
1460         // room for stage num, which makes toom for chat box
1461         /*
1462         if (Game_mode & GM_MULTIPLAYER) {
1463                 gr_get_string_size(&w,NULL,The_mission.name);
1464                 gr_string(bscreen.map_x2 - w, bscreen.map_y2 + 5, The_mission.name);            
1465         } else {
1466                 gr_get_string_size(&w,NULL,The_mission.name);
1467                 gr_string(bscreen.map_x2 - w, bscreen.map_y2 + 5, The_mission.name);            
1468         }
1469         */
1470 }
1471
1472 // Render a line of text for the briefings.  Lines are drawn in as a wipe, with leading bright
1473 // white characters.  Have to jump through some hoops since we support colored words.  This means
1474 // that we need to process the line one character at a time.
1475 void brief_render_line(int line_num, int x, int y, int instance)
1476 {
1477         int len, count, next, truncate_len, last_color, offset, w, h, bright_len, i;
1478         colored_char *src;
1479         char line[MAX_BRIEF_LINE_LEN];
1480
1481         src = &Colored_text[instance][line_num][0];
1482         len = Colored_text_len[instance][line_num];
1483
1484         if (len <= 0){
1485                 return;
1486         }
1487
1488         truncate_len = fl2i(Brief_text_wipe_time_elapsed / BRIEF_TEXT_WIPE_TIME * Max_briefing_line_len);
1489         if (truncate_len > len){
1490                 truncate_len = len;
1491         }
1492
1493         bright_len = 0;
1494         if (truncate_len < len) {
1495                 if (truncate_len <= BRIGHTEN_LEAD) {
1496                         bright_len = truncate_len;
1497                         truncate_len = 0;
1498
1499                 } else {
1500                         bright_len = BRIGHTEN_LEAD;
1501                         truncate_len -= BRIGHTEN_LEAD; 
1502                 }
1503         }
1504
1505         offset = 0;
1506         count  = 0;
1507         next     = 0;
1508
1509         gr_set_color_fast(&Color_white);
1510         last_color = BRIEF_TEXT_WHITE;
1511         for (i=0; i<truncate_len; i++) {
1512                 if (count >= truncate_len){
1513                         break;
1514                 }
1515
1516                 line[next] = src[count].letter;
1517
1518                 if (is_white_space(line[next])) {
1519                         // end of word reached, blit it
1520                         line[next + 1] = 0;
1521                         gr_string(x + offset, y, line);
1522                         gr_get_string_size(&w, &h, line);
1523                         offset += w;
1524                         next = 0;
1525
1526                         // reset color
1527                         if (last_color != BRIEF_TEXT_WHITE) {
1528                                 brief_set_text_color(BRIEF_TEXT_WHITE);
1529                                 last_color = BRIEF_TEXT_WHITE;
1530                         }
1531
1532                         count++;
1533                         continue;
1534                 }
1535
1536                 if (src[count].color != last_color) {
1537                         brief_set_text_color(src[count].color);
1538                         last_color = src[count].color;
1539                 }
1540
1541                 count++;
1542                 next++;
1543         }       // end for
1544
1545         line[next] = 0;
1546         gr_string(x + offset, y, line);
1547
1548
1549         // draw leading portion of the line bright white
1550         if (bright_len) {
1551
1552                 gr_set_color_fast(&Color_bright_white);
1553                 for (i=0; i<truncate_len+bright_len; i++) {
1554                         line[i] = src[i].letter;
1555                 }
1556
1557                 line[i] = 0;
1558
1559
1560                 if ( truncate_len > 0 ) {
1561                         int width_dim, height_dim;
1562                         gr_get_string_size(&width_dim, &height_dim, line, truncate_len );
1563                         gr_string(x+width_dim, y, &line[truncate_len]);
1564                 } else {
1565                         gr_string(x, y, line);
1566                 }
1567
1568                 // JAS: Not needed?
1569                 //              // now erase the part we don't want to be bright white
1570                 //              gr_set_color_fast(&Color_black);
1571                 //              if (i > BRIGHTEN_LEAD) {
1572                 //                      line[i - BRIGHTEN_LEAD] = 0;
1573                 //                      gr_get_string_size(&w, &h, line);
1574                 //                      gr_set_clip(x, y, w, gr_get_font_height());
1575                 //                      gr_clear();
1576                 //                      gr_reset_clip();
1577                 //              }
1578         }
1579 }
1580
1581 int brief_text_wipe_finished()
1582 {
1583         if ( Brief_text_wipe_time_elapsed > (BRIEF_TEXT_WIPE_TIME+0.5f) ) {
1584                 return 1;
1585         }
1586
1587         return 0;
1588 }
1589
1590 // -------------------------------------------------------------------------------------
1591 // brief_render_text()
1592 //
1593 // input:       frametime       =>      Time in seconds of previous frame
1594 //                              instance                =>      Optional parameter.  Used to indicate which text stream is used.
1595 //                                                                      This value is 0 unless multiple text streams are required
1596 int brief_render_text(int line_offset, int x, int y, int h, float frametime, int instance, int line_spacing)
1597 {
1598         int fh, line, yy;
1599
1600         fh = gr_get_font_height();
1601         if (Brief_text_wipe_time_elapsed == 0) {
1602                 if (snd_is_playing(Brief_text_wipe_snd)) {
1603                         snd_stop(Brief_text_wipe_snd);
1604                 }
1605                 gamesnd_play_iface(SND_BRIEF_TEXT_WIPE);
1606                 Play_brief_voice = 1;
1607         }
1608
1609         Brief_text_wipe_time_elapsed += frametime;
1610
1611         line = line_offset;
1612         yy = 0;
1613         while (yy + fh <= h) {
1614                 if (line >= Num_brief_text_lines[instance])
1615                         break;
1616
1617                 brief_render_line(line, x, y + yy, instance);
1618
1619                 line++;
1620                 yy += fh + line_spacing;
1621         }
1622
1623         if ( brief_text_wipe_finished() && (Play_brief_voice) ) {
1624                 Play_brief_voice = 0;
1625                 return 1;
1626         }
1627
1628         return 0;
1629 }
1630
1631 // ------------------------------------------------------------------------------------
1632 // brief_render_elements()
1633 //
1634 // Draw the lines that show objects positions on the grid
1635 //
1636 void brief_render_elements(vector *pos, grid* gridp)
1637 {
1638         vector  gpos;   //      Location of point on grid.
1639 //      vector  tpos;
1640         float           dxz;
1641         plane           tplane;
1642         vector  *gv;
1643         
1644         if ( pos->xyz.y < 1 && pos->xyz.y > -1 )
1645                 return;
1646
1647         tplane.A = gridp->gmatrix.v.uvec.xyz.x;
1648         tplane.B = gridp->gmatrix.v.uvec.xyz.y;
1649         tplane.C = gridp->gmatrix.v.uvec.xyz.z;
1650         tplane.D = gridp->planeD;
1651
1652         compute_point_on_plane(&gpos, &tplane, pos);
1653
1654         dxz = vm_vec_dist(pos, &gpos)/8.0f;
1655
1656         gv = &gridp->gmatrix.v.uvec;
1657         if (gv->xyz.x * pos->xyz.x + gv->xyz.y * pos->xyz.y + gv->xyz.z * pos->xyz.z < -gridp->planeD)
1658                 gr_set_color(127, 127, 127);
1659         else
1660                 gr_set_color(255, 255, 255);   // white
1661
1662 // AL 11-20-97: don't draw elevation lines.. they are confusing
1663 /*
1664         brief_rpd_line(&gpos, pos);     //      Line from grid to object center.
1665
1666         tpos = gpos;
1667
1668         vm_vec_scale_add2(&gpos, &gridp->gmatrix.v.rvec, -dxz/2);
1669         vm_vec_scale_add2(&gpos, &gridp->gmatrix.v.fvec, -dxz/2);
1670         
1671         vm_vec_scale_add2(&tpos, &gridp->gmatrix.v.rvec, dxz/2);
1672         vm_vec_scale_add2(&tpos, &gridp->gmatrix.v.fvec, dxz/2);
1673         
1674         brief_rpd_line(&gpos, &tpos);
1675
1676         vm_vec_scale_add2(&gpos, &gridp->gmatrix.v.rvec, dxz);
1677         vm_vec_scale_add2(&tpos, &gridp->gmatrix.v.rvec, -dxz);
1678
1679         brief_rpd_line(&gpos, &tpos);
1680 */
1681 }
1682
1683
1684 // ------------------------------------------------------------------------------------
1685 // brief_reset_icons()
1686 //
1687 void brief_reset_icons(int stage_num)
1688 {
1689         brief_stage             *bs;
1690         brief_icon              *bi;
1691         int                             i;
1692
1693         Assert( Briefing != NULL );
1694         bs = &Briefing->stages[stage_num];
1695
1696         for ( i = 0; i < bs->num_icons; i++ ) {
1697                 bi = &bs->icons[i];
1698                 bi->flags &= ~BI_SHOWHIGHLIGHT;
1699         }
1700 }
1701
1702 // ------------------------------------------------------------------------------------
1703 // brief_set_camera_target()
1704 //
1705 //      input:  pos             =>              target position for the camera
1706 //                              orient  =>              target orientation for the camera
1707 //                              time            =>              time in ms to reach target
1708 //
1709 void brief_set_camera_target(vector *pos, matrix *orient, int time)
1710 {
1711         float time_in_seconds;
1712         
1713         time_in_seconds = time / 1000.0f;
1714
1715         if ( time == 0 ) {
1716                 Current_cam_pos = *pos;
1717                 Current_cam_orient = *orient;
1718         }
1719
1720         Target_cam_pos = *pos;
1721         Target_cam_orient = *orient;
1722         Start_cam_orient = Current_cam_orient;
1723         Start_cam_pos = Current_cam_pos;                                                                // we need this when checking if camera movement complete
1724         Start_cam_move = timer_get_milliseconds()*1000.0f;              // start time, convert to seconds
1725         Total_move_time = time_in_seconds;
1726         Elapsed_time = 0.0f;
1727
1728         vm_vec_scale_add(&Start_lookat_pos, &Start_cam_pos, &Start_cam_orient.v.fvec, LOOKAT_DIST);
1729         vm_vec_scale_add(&Target_lookat_pos, &Target_cam_pos, &Target_cam_orient.v.fvec, LOOKAT_DIST);
1730
1731         Play_highlight_flag = 1;                                                                // once target reached, play highlight anims
1732         Cam_target_reached = 0;
1733         Cam_movement_done=0;
1734         anim_release_all_instances(ON_BRIEFING_SELECT); // stop any briefing-specific anims
1735         
1736         // calculate camera velocity
1737         vm_vec_sub(&Cam_vel, pos, &Current_cam_pos);
1738 //      vm_vec_scale(&Cam_vel, 1.0f/time_in_seconds);
1739         if ( !IS_VEC_NULL(&Cam_vel) ) {
1740                 vm_vec_normalize(&Cam_vel);
1741         }
1742
1743         // calculate lookat point velocity
1744         vm_vec_sub(&Lookat_vel, &Target_lookat_pos, &Current_lookat_pos);
1745         vm_vec_scale(&Lookat_vel, 1.0f/time_in_seconds);
1746
1747         Start_dist = vm_vec_dist(&Start_cam_pos, &Start_lookat_pos);
1748         End_dist = vm_vec_dist(&Target_cam_pos, &Target_lookat_pos);
1749         Dist_change_rate = (End_dist - Start_dist) / time_in_seconds;
1750
1751         Total_dist=vm_vec_dist(&Start_cam_pos, &Target_cam_pos);
1752
1753 //      Peak_speed=Total_dist/Total_move_time*1.5f;
1754 //      Cam_accel = Peak_speed/Total_move_time*3.0f;
1755
1756         Peak_speed=Total_dist/Total_move_time*2.0f;
1757         Cam_accel = 4*Total_dist/(Total_move_time*Total_move_time);
1758         Last_dist=0.0f;
1759
1760         vm_vec_zero(&W_init);
1761
1762         get_camera_limits(&Start_cam_orient, &Target_cam_orient, Total_move_time, &Acc_limit, &Vel_limit);
1763 }
1764
1765
1766 ubyte brief_return_color_index(char c)
1767 {
1768         switch (c) {
1769                 case 'f':
1770                         return BRIEF_IFF_FRIENDLY;
1771
1772                 case 'h':
1773                         return BRIEF_IFF_HOSTILE;
1774
1775                 case 'n':
1776                         return BRIEF_IFF_NEUTRAL;
1777
1778                 case 'r':
1779                         return BRIEF_TEXT_RED;
1780
1781                 case 'g':
1782                         return BRIEF_TEXT_GREEN;
1783
1784                 case 'b':
1785                         return BRIEF_TEXT_BLUE;
1786
1787                 default:        
1788                         Int3(); // unsupported meta-code
1789                         break;
1790         } // end switch
1791
1792         return BRIEF_TEXT_WHITE;
1793 }
1794
1795 void brief_set_text_color(int color_index)
1796 {
1797         Assert(color_index < MAX_BRIEF_TEXT_COLORS);
1798         gr_set_color_fast(Brief_text_colors[color_index]);
1799 }
1800
1801 // Set up the Colored_text array.
1802 // input:               index           =>              Index into Brief_text[] for source text.
1803 //                                      instance        =>              Which instance of Colored_text[] to use.  
1804 //                                                                              Value is 0 unless multiple text streams are required.
1805 int brief_text_colorize(int index, int instance)
1806 {
1807         char *src;
1808         int len, i, skip_to_next_word, dest_len;
1809         colored_char *dest;
1810         ubyte active_color_index;
1811
1812         src = Brief_text[index];
1813         dest = &Colored_text[instance][index][0];
1814         len = strlen(src);
1815
1816         skip_to_next_word = 0;
1817         dest_len = 0;
1818         active_color_index = BRIEF_TEXT_WHITE;
1819         for (i=0; i<len; i++) {
1820                 if (skip_to_next_word) {
1821                         if (is_white_space(src[i])) {
1822                                 skip_to_next_word = 0;
1823                         }
1824
1825                         continue;
1826                 }
1827
1828                 if ( src[i] == BRIEF_META_CHAR && is_white_space(src[i + 2]) ) {
1829                         active_color_index = brief_return_color_index(src[i + 1]);
1830                         skip_to_next_word = 1;
1831                         continue;
1832                 }
1833
1834                 if (is_white_space(src[i])) {
1835                         active_color_index = BRIEF_TEXT_WHITE;
1836                 }
1837
1838                 dest[dest_len].letter = src[i];
1839                 dest[dest_len].color  = active_color_index;
1840                 dest_len++;
1841         } // end for
1842
1843         dest[dest_len].letter = 0;
1844         Colored_text_len[instance][index] = dest_len;
1845         return len;
1846 }
1847
1848 // ------------------------------------------------------------------------------------
1849 // brief_color_text_init()
1850 //
1851 //      input:  src             =>              paragraph of text to process
1852 //                              w                       =>              max width of line in pixels
1853 //                              instance        =>              optional parameter, used when multiple text streams are required
1854 //                                                                      (default value is 0)
1855 int brief_color_text_init(char *src, int w, int instance)
1856 {
1857         int i, n_lines, len;
1858         int n_chars[MAX_BRIEF_LINES];
1859         char *p_str[MAX_BRIEF_LINES];
1860         
1861         Assert(src);
1862         n_lines = split_str(src, w, n_chars, p_str, MAX_BRIEF_LINES, BRIEF_META_CHAR);
1863         Assert(n_lines >= 0);
1864
1865         Max_briefing_line_len = 1;
1866         for (i=0; i<n_lines; i++) {
1867                 Assert(n_chars[i] < MAX_BRIEF_LINE_LEN);
1868                 strncpy(Brief_text[i], p_str[i], n_chars[i]);
1869                 Brief_text[i][n_chars[i]] = 0;
1870                 drop_leading_white_space(Brief_text[i]);
1871                 len = brief_text_colorize(i, instance);
1872                 if (len > Max_briefing_line_len)
1873                         Max_briefing_line_len = len;
1874         }
1875
1876         Brief_text_wipe_time_elapsed = 0.0f;
1877         Play_brief_voice = 0;
1878
1879         Num_brief_text_lines[instance] = n_lines;
1880         return n_lines;
1881 }
1882
1883 // ------------------------------------------------------------------------------------
1884 // brief_get_free_move_icon()
1885 //
1886 //      returns:                failure =>              -1
1887 //                                      success =>              handle to a free move icon struct
1888 //
1889 int brief_get_free_move_icon()
1890 {
1891         int i;
1892
1893         for ( i = 0; i < MAX_MOVE_ICONS; i++ ) {
1894                 if ( Icon_movers[i].used == 0 )
1895                         break;
1896         }
1897         
1898         if ( i == MAX_MOVE_ICONS ) 
1899                 return -1;
1900
1901         Icon_movers[i].used = 1;
1902         return i;
1903 }
1904
1905
1906 // ------------------------------------------------------------------------------------
1907 // brief_set_move_list()
1908 //
1909 //      input:  new_stage               =>              new stage number that briefing is now moving to
1910 //                              current_stage   =>              current stage that the briefing is on
1911 //                              time                            =>              time in seconds
1912 //
1913 int brief_set_move_list(int new_stage, int current_stage, float time)
1914 {
1915         brief_stage             *newb, *cb;     
1916         icon_move_info  *imi;   
1917         int                             i,j,k,num_movers,is_gone=0;
1918         vector                  zero_v = ZERO_VECTOR;
1919
1920         Assert(new_stage != current_stage);
1921         
1922         Assert( Briefing != NULL );
1923         newb = &Briefing->stages[new_stage];
1924         cb = &Briefing->stages[current_stage];
1925         num_movers = 0;
1926         
1927         for ( i = 0; i < cb->num_icons; i++ ) {
1928                 is_gone=1;
1929                 for ( j = 0; j < newb->num_icons; j++ ) {
1930                         if ( ( cb->icons[i].id != 0 ) && ( cb->icons[i].id == newb->icons[j].id ) ) {
1931                                 is_gone=0;
1932                                 if ( vm_vec_cmp(&cb->icons[i].pos, &newb->icons[j].pos) ) {
1933                                         //nprintf(("Alan","We found a match in icon %s\n", cb->icons[i].label));
1934                                         k = brief_get_free_move_icon();                         
1935                                         if ( k == -1 ) {
1936                                                 Int3(); // should never happen, get Alan
1937                                                 return 0;
1938                                         }
1939                                         imi = &Icon_movers[k];
1940                                         imi->id = cb->icons[i].id;
1941                                         imi->start = cb->icons[i].pos;
1942                                         imi->finish = newb->icons[j].pos;
1943                                         imi->current = imi->start;
1944                                         list_append(&Icon_move_list, imi);
1945
1946                                         imi->total_dist = vm_vec_dist(&imi->start, &imi->finish);
1947                                         imi->total_move_time = time;
1948                                         imi->peak_speed = imi->total_dist/imi->total_move_time*2.0f;
1949                                         imi->accel = 4*imi->total_dist/(time*time);
1950                                         imi->last_dist=0.0f;
1951                                         imi->reached_dest=0;
1952                                         imi->direction = zero_v;
1953
1954                                         vm_vec_sub(&imi->direction, &imi->finish, &imi->start);
1955                                         if ( !IS_VEC_NULL(&imi->direction) ) {
1956                                                 vm_vec_normalize(&imi->direction);
1957                                         }
1958
1959                                         num_movers++;
1960                                 }
1961                         }
1962                 }
1963
1964                 // Set up fading icon (to fade out)
1965                 if (is_gone == 1) {
1966                         if ( Num_fade_icons >= MAX_FADE_ICONS ) {
1967                                 Int3();
1968                                 Num_fade_icons=0;
1969                         }
1970
1971                         int species = ship_get_species_by_type(cb->icons[i].ship_class);
1972                         if(species < 0) {
1973                                 return 0;
1974                         }
1975
1976                         Fading_icons[Num_fade_icons].fade_anim = Icon_fade_anims[cb->icons[i].type][species];
1977                         Fading_icons[Num_fade_icons].pos = cb->icons[i].pos;
1978                         Fading_icons[Num_fade_icons].team = cb->icons[i].team;
1979                         Num_fade_icons++;
1980                 }
1981         }
1982
1983         // flag new icons for fading in
1984         for ( i=0; i<newb->num_icons; i++ ) {
1985                 int is_new = 1;
1986                 newb->icons[i].flags &= ~BI_FADEIN;
1987                 for ( j=0; j<cb->num_icons; j++ ) {
1988                         if ( ( cb->icons[j].id != 0 ) && ( cb->icons[j].id == newb->icons[i].id ) ) {
1989                                 is_new=0;
1990                         }
1991                 }
1992                 if ( is_new ) {
1993                         int species = ship_get_species_by_type(newb->icons[i].ship_class);
1994                         if(species < 0) {
1995                                 return 0;
1996                         }
1997
1998                         newb->icons[i].flags |= BI_FADEIN;
1999                         newb->icons[i].fadein_anim = Icon_fade_anims[newb->icons[i].type][species];
2000                         newb->icons[i].fadein_anim.time_elapsed = 0.0f;
2001                 }
2002         }
2003
2004         return num_movers;
2005 }
2006
2007 void brief_clear_fade_out_icons()
2008 {
2009         Num_fade_icons = 0;
2010 }
2011
2012
2013 // ------------------------------------------------------------------------------------
2014 // brief_set_new_stage()
2015 //
2016 //      input:  pos                     =>              target position for the camera
2017 //                              orient          =>              target orientation for the camera
2018 //                              time                    =>              time in ms to reach target
2019 //                              stage_num       =>              stage number of briefing (start numbering at 0)
2020 //
2021
2022 void brief_set_new_stage(vector *pos, matrix *orient, int time, int stage_num)
2023 {
2024         char msg[MAX_BRIEF_LEN];
2025         int num_movers, new_time, not_objv = 1;
2026
2027         Assert( Briefing != NULL );
2028         new_time = time;
2029
2030         if (stage_num >= Briefing->num_stages) {
2031                 not_objv = 0;  // turns out this is an objectives stage
2032                 new_time = 0;
2033         }
2034
2035         if ( stage_num == Last_new_stage ) {
2036                 return;
2037         }
2038
2039         num_movers = 0;
2040         brief_move_icon_reset();
2041         brief_clear_fade_out_icons();
2042         if ( (Last_new_stage != -1) && not_objv ) {
2043                 num_movers = brief_set_move_list(stage_num, Last_new_stage, new_time / 1000.0f);
2044         }
2045
2046         if ( (Last_new_stage != -1) && (num_movers == 0) && not_objv ) {
2047                 if ( !vm_vec_cmp( &Briefing->stages[stage_num].camera_pos, &Briefing->stages[Last_new_stage].camera_pos) ) {
2048                         if ( !vm_vec_cmp( &Briefing->stages[stage_num].camera_orient.v.fvec, &Briefing->stages[Last_new_stage].camera_orient.v.fvec) ){
2049                                 new_time = 0;
2050                         }
2051                 }
2052         }
2053
2054         if (not_objv) {
2055                 if(Briefing->stages[stage_num].new_text == NULL){
2056                         strcpy(msg, "");
2057                 } else {
2058                         strcpy(msg, Briefing->stages[stage_num].new_text);
2059                 }
2060         } else {
2061                 strcpy(msg, XSTR( "Please review your objectives for this mission.", 395));
2062         }
2063
2064         if (gr_screen.res == GR_640) {
2065                 // GR_640
2066                 Num_brief_text_lines[0] = brief_color_text_init(msg, MAX_BRIEF_LINE_W_640);
2067         } else {
2068                 // GR_1024
2069                 Num_brief_text_lines[0] = brief_color_text_init(msg, MAX_BRIEF_LINE_W_1024);            
2070         }
2071         Top_brief_text_line = 0;
2072
2073         if (not_objv){
2074                 brief_set_camera_target(pos, orient, new_time);
2075         }
2076
2077         if ( snd_is_playing(Brief_stage_highlight_sound_handle) ) {
2078                 snd_stop(Brief_stage_highlight_sound_handle);
2079         }
2080
2081         Brief_voice_ended = 0;
2082         Brief_textdraw_finished = 0;
2083         Brief_voice_started = 0;
2084         Brief_stage_time = 0;
2085
2086
2087         Brief_stage_highlight_sound_handle = -1;
2088         Last_new_stage = stage_num;
2089 }
2090
2091 // ------------------------------------------------------------------------------------
2092 // camera_pos_past_target()
2093 //
2094 //
2095 int camera_pos_past_target(vector *start, vector *current, vector *dest)
2096 {
2097         vector num, den;
2098         float ratio;
2099
2100         vm_vec_sub(&num, current, start);
2101         vm_vec_sub(&den, start, dest);
2102
2103         ratio = vm_vec_mag_quick(&num) / vm_vec_mag_quick(&den);
2104         if (ratio >= 1.0f)
2105                 return TRUE;
2106         
2107         return FALSE;
2108 }
2109
2110 // ------------------------------------------------------------------------------------
2111 // Interpolate between matrices.
2112 // elapsed_time/total_time gives percentage of interpolation between cur
2113 // and goal.
2114 void interpolate_matrix(matrix *result, matrix *goal, matrix *start, float elapsed_time, float total_time)
2115 {
2116         vector fvec, rvec;
2117         float   time0, time1;
2118         
2119         if ( !vm_matrix_cmp( goal, start ) ) {
2120                 return;
2121         }       
2122
2123         time0 = elapsed_time / total_time;
2124         time1 = (total_time - elapsed_time) / total_time;
2125
2126         vm_vec_copy_scale(&fvec, &start->v.fvec, time1);
2127         vm_vec_scale_add2(&fvec, &goal->v.fvec, time0);
2128
2129         vm_vec_copy_scale(&rvec, &start->v.rvec, time1);
2130         vm_vec_scale_add2(&rvec, &goal->v.rvec, time0);
2131
2132         vm_vector_2_matrix(result, &fvec, NULL, &rvec);
2133  }
2134
2135 // calculate how far the camera should have moved
2136 float brief_camera_get_dist_moved(float elapsed_time)
2137 {
2138         float time, dist_moved=0.0f;
2139         
2140         // first half of movement
2141         if ( elapsed_time < Total_move_time/2.0f ) {
2142                 dist_moved=0.5f*Cam_accel*elapsed_time*elapsed_time;    // d = 1/2at^2
2143                 return dist_moved;
2144         }
2145
2146         // second half of movement
2147         time=elapsed_time - Total_move_time/2.0f;
2148         dist_moved=(Total_dist/2.0f)+(Peak_speed*time) - 0.5f*Cam_accel*time*time;
2149         return dist_moved;
2150
2151 }
2152
2153 // ------------------------------------------------------------------------------------
2154 // Update the camera position
2155 void brief_camera_move(float frametime, int stage_num)
2156 {
2157         vector  dist_moved;
2158         float           dist;
2159         vector  w_out;
2160         matrix  result;
2161
2162         Elapsed_time += frametime;
2163
2164         if ( Cam_target_reached ) { 
2165 //              Current_cam_pos = Target_cam_pos;
2166 //              Current_lookat_pos = Target_lookat_pos;
2167 //              Current_cam_orient = Target_cam_orient;
2168                 return;
2169         }
2170
2171         // Update orientation
2172         if ( (Elapsed_time < Total_move_time) ) {
2173 //              interpolate_matrix(&Current_cam_orient, &Target_cam_orient, &Start_cam_orient, Elapsed_time, Total_move_time );
2174                 vm_matrix_interpolate(&Target_cam_orient, &Current_cam_orient, &W_init, frametime, &result, &w_out, &Vel_limit, &Acc_limit);
2175                 Current_cam_orient = result;
2176                 W_init = w_out;
2177         }
2178
2179         /*
2180         // interpolate lookat position
2181         if ( vm_vec_cmp( &Current_lookat_pos, &Target_lookat_pos ) ) {
2182                 vm_vec_copy_scale(&dist_moved, &Lookat_vel, Elapsed_time);
2183                 vm_vec_add(&Current_lookat_pos, &Start_lookat_pos, &dist_moved);
2184
2185                 if ( camera_pos_past_target(&Start_lookat_pos, &Current_lookat_pos, &Target_lookat_pos) ) {
2186                         Current_lookat_pos = Target_lookat_pos;
2187                 }
2188         }
2189
2190         cur_dist = Start_dist + Dist_change_rate * Elapsed_time;
2191         vm_vec_copy_scale(&dist_moved, &Current_cam_orient.v.fvec, -cur_dist);
2192         vm_vec_add(&Current_cam_pos, &Current_lookat_pos, &dist_moved);
2193         */
2194
2195         // use absolute pos to update position
2196         if ( vm_vec_cmp( &Current_cam_pos, &Target_cam_pos ) ) {
2197                 dist = brief_camera_get_dist_moved(Elapsed_time);
2198                 if ( dist < Last_dist ) {
2199                         Cam_movement_done=1;
2200                         Last_dist=0.0f;
2201                 }
2202                 Last_dist=dist;
2203
2204                 if ( Cam_movement_done == 0 ) {
2205                         vm_vec_copy_scale(&dist_moved, &Cam_vel, dist);
2206                         vm_vec_add(&Current_cam_pos, &Start_cam_pos, &dist_moved);
2207                 } else {
2208                         Current_cam_pos=Target_cam_pos;
2209                 }
2210         }
2211         else {
2212                 Cam_movement_done=1;
2213                 Current_cam_pos=Target_cam_pos;
2214         }
2215
2216         if ( Cam_movement_done && (Elapsed_time >= Total_move_time) ) {
2217                 Cam_target_reached=1;
2218         }
2219 }
2220
2221 //      Project the viewer's position onto the grid plane.  If more than threshold distance
2222 //      from grid center, move grid center.
2223 void brief_maybe_create_new_grid(grid* gridp, vector *pos, matrix *orient, int force)
2224 {
2225         int roundoff;
2226         plane   tplane;
2227         vector  gpos, tmp, c;
2228         float   dist_to_plane;
2229         float   square_size, ux, uy, uz;
2230
2231         ux = tplane.A = gridp->gmatrix.v.uvec.xyz.x;
2232         uy = tplane.B = gridp->gmatrix.v.uvec.xyz.y;
2233         uz = tplane.C = gridp->gmatrix.v.uvec.xyz.z;
2234         tplane.D = gridp->planeD;
2235
2236         compute_point_on_plane(&c, &tplane, pos);
2237         dist_to_plane = fl_abs(vm_dist_to_plane(pos, &gridp->gmatrix.v.uvec, &c));
2238         square_size = 1.0f;
2239
2240         while (dist_to_plane >= 25.0f)
2241         {
2242                 square_size *= 10.0f;
2243                 dist_to_plane /= 10.0f;
2244         }
2245         
2246         if (fvi_ray_plane(&gpos, &gridp->center, &gridp->gmatrix.v.uvec, pos, &orient->v.fvec, 0.0f)<0.0f)      {
2247                 vector p;
2248                 vm_vec_scale_add(&p,pos,&orient->v.fvec, 100.0f );
2249                 compute_point_on_plane(&gpos, &tplane, &p );
2250         }
2251
2252         if (vm_vec_dist(&gpos, &c) > 50.0f * square_size)
2253         {
2254                 vm_vec_sub(&tmp, &gpos, &c);
2255                 vm_vec_normalize(&tmp);
2256                 vm_vec_scale_add(&gpos, &c, &tmp, 50.0f * square_size);
2257         }
2258
2259         roundoff = (int) square_size * 10;
2260         if (!ux)
2261                 gpos.xyz.x = fl_roundoff(gpos.xyz.x, roundoff);
2262         if (!uy)
2263                 gpos.xyz.y = fl_roundoff(gpos.xyz.y, roundoff);
2264         if (!uz)
2265                 gpos.xyz.z = fl_roundoff(gpos.xyz.z, roundoff);
2266
2267         if ((square_size != gridp->square_size) ||
2268                 (gpos.xyz.x != gridp->center.xyz.x) ||
2269                 (gpos.xyz.y != gridp->center.xyz.y) ||
2270                 (gpos.xyz.z != gridp->center.xyz.z) || force)
2271         {
2272                 gridp->square_size = square_size;
2273                 gridp->center = gpos;
2274                 brief_modify_grid(gridp);
2275         }
2276 }
2277
2278 //      Create a grid
2279 //      *forward is vector pointing forward
2280 //      *right is vector pointing right
2281 //      *center is center point of grid
2282 //      length is length of grid
2283 //      width is width of grid
2284 //      square_size is size of a grid square
2285 //      For example:
2286 //              *forward = (0.0, 0.0, 1.0)
2287 //              *right   = (1.0, 0.0, 0.0)
2288 //              *center = (0.0, 0.0, 0.0)
2289 //              nrows = 10
2290 //              ncols =  50.0
2291 //              square_size = 10.0
2292 //      will generate a grid of squares 10 long by 5 wide.
2293 //      Each grid square will be 10.0 x 10.0 units.
2294 //      The center of the grid will be at the global origin.
2295 //      The grid will be parallel to the xz plane (because the normal is 0,1,0).
2296 //      (In fact, it will be the xz plane because it is centered on the origin.)
2297 //
2298 //      Stuffs grid in *gridp.  If gridp == NULL, mallocs and returns a grid.
2299 grid *brief_create_grid(grid *gridp, vector *forward, vector *right, vector *center, int nrows, int ncols, float square_size)
2300 {
2301         int     i, ncols2, nrows2, d = 1;
2302         vector  dfvec, drvec, cur, cur2, tvec, uvec, save, save2;
2303
2304         Assert(square_size > 0.0);
2305         if (double_fine_gridlines)
2306                 d = 2;
2307
2308         if (gridp == NULL)
2309                 gridp = (grid *) malloc(sizeof(grid));
2310
2311         Assert(gridp);
2312
2313         gridp->center = *center;
2314         gridp->square_size = square_size;
2315
2316         //      Create the plane equation.
2317         Assert(!IS_VEC_NULL(forward));
2318         Assert(!IS_VEC_NULL(right));
2319
2320         vm_vec_copy_normalize(&dfvec, forward);
2321         vm_vec_copy_normalize(&drvec, right);
2322
2323         vm_vec_cross(&uvec, &dfvec, &drvec);
2324         
2325         Assert(!IS_VEC_NULL(&uvec));
2326
2327         gridp->gmatrix.v.uvec = uvec;
2328
2329         gridp->planeD = -(center->xyz.x * uvec.xyz.x + center->xyz.y * uvec.xyz.y + center->xyz.z * uvec.xyz.z);
2330         Assert(!_isnan(gridp->planeD));
2331
2332         gridp->gmatrix.v.fvec = dfvec;
2333         gridp->gmatrix.v.rvec = drvec;
2334
2335         vm_vec_scale(&dfvec, square_size);
2336         vm_vec_scale(&drvec, square_size);
2337
2338         vm_vec_scale_add(&cur, center, &dfvec, (float) -nrows * d / 2);
2339         vm_vec_scale_add2(&cur, &drvec, (float) -ncols * d / 2);
2340         vm_vec_scale_add(&cur2, center, &dfvec, (float) -nrows * 5 / 2);
2341         vm_vec_scale_add2(&cur2, &drvec, (float) -ncols * 5 / 2);
2342         save = cur;
2343         save2 = cur2;
2344
2345         gridp->ncols = ncols;
2346         gridp->nrows = nrows;
2347         ncols2 = ncols / 2;
2348         nrows2 = nrows / 2;
2349         Assert(ncols < MAX_GRIDLINE_POINTS && nrows < MAX_GRIDLINE_POINTS);
2350
2351         // Create the points along the edges of the grid, so we can just draw lines
2352         // between them to form the grid.  
2353         for (i=0; i<=ncols*d; i++) {
2354                 gridp->gpoints1[i] = cur;  // small, dark gridline points
2355                 vm_vec_scale_add(&tvec, &cur, &dfvec, (float) nrows * d);
2356                 gridp->gpoints2[i] = tvec;
2357                 vm_vec_add2(&cur, &drvec);
2358         }
2359
2360         for (i=0; i<=ncols2; i++) {
2361                 gridp->gpoints5[i] = cur2;  // large, brighter gridline points
2362                 vm_vec_scale_add(&tvec, &cur2, &dfvec, (float) nrows2 * 10);
2363                 gridp->gpoints6[i] = tvec;
2364                 vm_vec_scale_add2(&cur2, &drvec, 10.0f);
2365         }
2366
2367         cur = save;
2368         cur2 = save2;
2369         for (i=0; i<=nrows*d; i++) {
2370                 gridp->gpoints3[i] = cur;  // small, dark gridline points
2371                 vm_vec_scale_add(&tvec, &cur, &drvec, (float) ncols * d);
2372                 gridp->gpoints4[i] = tvec;
2373                 vm_vec_add2(&cur, &dfvec);
2374         }
2375
2376         for (i=0; i<=nrows2; i++) {
2377                 gridp->gpoints7[i] = cur2;  // large, brighter gridline points
2378                 vm_vec_scale_add(&tvec, &cur2, &drvec, (float) ncols2 * 10);
2379                 gridp->gpoints8[i] = tvec;
2380                 vm_vec_scale_add2(&cur2, &dfvec, 10.0f);
2381         }
2382
2383         return gridp;
2384 }
2385
2386 //      Create a nice grid -- centered at origin, 10x10, 10.0 size squares, in xz plane.
2387 grid *brief_create_default_grid(void)
2388 {
2389         grid    *rgrid;
2390         vector  fvec, rvec, cvec;
2391
2392         rgrid = brief_create_grid(&Global_grid, vm_vec_make(&fvec, 0.0f, 0.0f, 1.0f),
2393                 vm_vec_make(&rvec, 1.0f, 0.0f, 0.0f),
2394                 vm_vec_make(&cvec, 0.0f, -10.0f, 0.0f), 100, 100, 5.0f);
2395
2396         physics_init(&rgrid->physics);
2397         rgrid->physics.flags |= (PF_ACCELERATES | PF_SLIDE_ENABLED);
2398         return rgrid;
2399 }
2400
2401 //      Rotate and project points and draw a line.
2402 void brief_rpd_line(vector *v0, vector *v1)
2403 {
2404         vertex  tv0, tv1;
2405         g3_rotate_vertex(&tv0, v0);
2406         g3_rotate_vertex(&tv1, v1);
2407
2408 /*
2409         g3_project_vertex(&tv0);        
2410         g3_project_vertex(&tv1);
2411
2412         if ( (tv0.flags & PF_OVERFLOW) || (tv1.flags & PF_OVERFLOW) )
2413                 return;
2414 */
2415
2416         gr_set_color_fast(&Color_grey);
2417         g3_draw_line(&tv0, &tv1);
2418 }
2419
2420 //      Renders a grid defined in a grid struct
2421 void brief_render_grid(grid *gridp)
2422 {
2423         int     i, ncols, nrows;
2424
2425         ncols = gridp->ncols;
2426         nrows = gridp->nrows;
2427         if (double_fine_gridlines)
2428         {
2429                 ncols *= 2;
2430                 nrows *= 2;
2431         }
2432
2433         gr_set_color(30,30,30);
2434 //      SET_DARK;
2435
2436         //      Draw the column lines.
2437         for (i=0; i<=ncols; i++)
2438                 brief_rpd_line(&gridp->gpoints1[i], &gridp->gpoints2[i]);
2439
2440         //      Draw the row lines.
2441         for (i=0; i<=nrows; i++)
2442                 brief_rpd_line(&gridp->gpoints3[i], &gridp->gpoints4[i]);
2443
2444         ncols = gridp->ncols / 2;
2445         nrows = gridp->nrows / 2;
2446
2447         // now draw the larger, brighter gridlines that is x10 the scale of smaller one.
2448 //      SET_MEDIUM;
2449 /*
2450         for (i=0; i<=ncols; i++)
2451                 brief_rpd_line(&gridp->gpoints5[i], &gridp->gpoints6[i]);
2452
2453         for (i=0; i<=nrows; i++)
2454                 brief_rpd_line(&gridp->gpoints7[i], &gridp->gpoints8[i]);
2455 */
2456 }
2457
2458 void brief_modify_grid(grid *gridp)
2459 {
2460         brief_create_grid(gridp, &gridp->gmatrix.v.fvec, &gridp->gmatrix.v.rvec, &gridp->center,
2461                 gridp->nrows, gridp->ncols, gridp->square_size);
2462 }
2463
2464 void brief_unload_anims()
2465 {
2466 #ifndef MAKE_FS1
2467         int idx;
2468 #endif
2469         int i;
2470         
2471         for (i=0; i<MAX_BRIEF_ICONS; i++) {
2472 #ifndef MAKE_FS1
2473                 for(idx=0; idx<MAX_SPECIES_NAMES; idx++){
2474                         hud_anim_release(&Icon_highlight_anims[i][idx]);
2475                         hud_anim_release(&Icon_fade_anims[i][idx]);
2476                 }
2477 #else
2478                 // one set of icons in FS1
2479                 hud_anim_release(&Icon_highlight_anims[i][1]);
2480                 hud_anim_release(&Icon_fade_anims[i][1]);
2481 #endif
2482         }
2483 }
2484
2485 void brief_common_close()
2486 {
2487         brief_close_map();
2488         brief_unload_anims();
2489 }
2490
2491
2492 void brief_restart_text_wipe()
2493 {
2494         Brief_stage_time = 0;
2495         Brief_voice_ended = 0;
2496         Brief_voice_started = 0;
2497         Brief_text_wipe_time_elapsed = 0.0f;
2498 }
2499
2500 // initialize the array of handles to the different voice streams
2501 void brief_voice_init()
2502 {
2503         int i;
2504         for ( i = 0; i < MAX_BRIEF_STAGES; i++ ) {
2505                 Brief_voices[i] = -1;
2506         }
2507 }
2508
2509 void brief_load_voice_file(int voice_num, char *name)
2510 {
2511         int load_attempts = 0;
2512         while(1) {
2513
2514                 if ( load_attempts++ > 5 ) {
2515                         break;
2516                 }
2517
2518                 Brief_voices[voice_num] = audiostream_open( name, ASF_VOICE );
2519                 if ( Brief_voices[voice_num] >= 0 ) {
2520                         break;
2521                 }
2522
2523                 // Don't bother to ask for the CD in multiplayer
2524                 if ( Game_mode & GM_MULTIPLAYER ) {
2525                         break;
2526                 }
2527
2528                 // couldn't load animation, ask user to insert CD (if necessary)
2529                 // if ( Brief_voice_ask_for_cd ) {
2530                         // if ( game_do_cd_check() == 0 ) {
2531                                 // Brief_voice_ask_for_cd = 0;
2532                                 // break;
2533                         // }
2534                 // }
2535         }
2536 }
2537
2538 // open and pre-load the stream buffers for the different voice streams
2539 void brief_voice_load_all()
2540 {
2541         int                     i;
2542         brief_stage     *bs;
2543
2544         // Brief_voice_ask_for_cd = 1;
2545
2546         Assert( Briefing != NULL );
2547         for ( i = 0; i < Briefing->num_stages; i++ ) {
2548                 bs = &Briefing->stages[i];
2549                 if ( strnicmp(bs->voice, NOX("none"), 4) ) {
2550                         brief_load_voice_file(i, bs->voice);
2551 //                      Brief_voices[i] = audiostream_open( bs->voice, ASF_VOICE );
2552                 }
2553         }
2554 }
2555
2556 // close all the briefing voice streams
2557 void brief_voice_unload_all()
2558 {
2559         int i;
2560
2561         for ( i = 0; i < MAX_BRIEF_STAGES; i++ ) {
2562                 if ( Brief_voices[i] != -1 ) {
2563                         audiostream_close_file(Brief_voices[i], 0);
2564                         Brief_voices[i] = -1;
2565                 }
2566         }
2567 }
2568
2569 // start playback of the voice for a particular briefing stage
2570 void brief_voice_play(int stage_num)
2571 {
2572         if ( Brief_voices[stage_num] == -1 )
2573                 return; // voice file doesn't exist
2574
2575         if ( !Briefing_voice_enabled ) {
2576                 return;
2577         }
2578
2579         if ( audiostream_is_playing( Brief_voices[stage_num]) )
2580                 return;
2581
2582         audiostream_play(Brief_voices[stage_num], Master_voice_volume, 0);
2583         Brief_voice_started = 1;
2584 }
2585
2586 // stop playback of the voice for a particular briefing stage
2587 void brief_voice_stop(int stage_num)
2588 {
2589         if ( Brief_voices[stage_num] == -1 )
2590                 return;
2591
2592         audiostream_stop(Brief_voices[stage_num]);      // stream is automatically rewound
2593 }
2594
2595 // pause playback of the voice for a particular briefing stage, to resume just
2596 // call brief_voice_unpause() again
2597 void brief_voice_pause(int stage_num)
2598 {
2599         if ( Brief_voices[stage_num] == -1 )
2600                 return;
2601
2602         audiostream_pause(Brief_voices[stage_num]);
2603 }
2604
2605 void brief_voice_unpause(int stage_num)
2606 {
2607         if ( Brief_voices[stage_num] == -1 )
2608                 return;
2609
2610         audiostream_unpause(Brief_voices[stage_num]);
2611 }
2612
2613 void brief_reset_last_new_stage()
2614 {
2615         Last_new_stage = -1;
2616 }
2617
2618 // get the dimensions for a briefing icon
2619 void brief_common_get_icon_dimensions(int *w, int *h, int type, int ship_class)
2620 {
2621         Assert(type >= 0 && type < MAX_BRIEF_ICONS);
2622
2623         // in case anything goes wrong
2624         *w=0;
2625         *h=0;
2626
2627         int species = ship_get_species_by_type(ship_class);
2628         if(species < 0){
2629                 return;
2630         }
2631
2632         if ( Icon_bitmaps[type][species].first_frame >= 0 ) {
2633                 bm_get_info( Icon_bitmaps[type][species].first_frame, w, h, NULL);
2634         } else {
2635                 *w=0;
2636                 *h=0;
2637         }
2638 }
2639
2640 void cmd_brief_reset()
2641 {
2642         int i, j;
2643         static int inited = 0;
2644
2645         if (inited) {
2646                 for (i=0; i<MAX_TEAMS; i++) {
2647                         for (j=0; j<Cmd_briefs[i].num_stages; j++) {
2648                                 if (Cmd_briefs[i].stage[j].text)
2649                                         free(Cmd_briefs[i].stage[j].text);
2650                         }
2651                 }
2652         }
2653
2654         inited = 1;
2655         for (i=0; i<MAX_TEAMS; i++)
2656                 Cmd_briefs[i].num_stages = 0;
2657 }
2658
2659 #define STAGE_ADVANCE_DELAY     1000            // time in ms to wait after voice stops before advancing stage
2660
2661 // should briefing advance to the next stage?
2662 int brief_time_to_advance(int stage_num, float frametime)
2663 {
2664         int voice_active, advance = 0;
2665         brief_icon *closeup_icon;
2666
2667         closeup_icon = brief_get_closeup_icon();
2668
2669         if ( closeup_icon ) {
2670                 return 0;
2671         }
2672
2673         if ( !Player->auto_advance ) {
2674                 return 0;
2675         }
2676
2677         Brief_stage_time += fl2i(frametime*1000 + 0.5f);
2678
2679         if ( (Brief_voices[stage_num] >= 0) && Briefing_voice_enabled ) {
2680                 voice_active = 1;
2681         } else {
2682                 voice_active = 0;
2683         }
2684
2685         if ( voice_active && (Brief_voice_ended == 0) && Brief_voice_started) {
2686                 if ( !audiostream_is_playing( Brief_voices[stage_num]) ) {
2687                         Brief_voice_ended = 1;
2688                         Brief_stage_time = 0;
2689                 }
2690         }
2691         
2692         if ( Brief_voice_ended ) {
2693                 if ( Brief_stage_time > STAGE_ADVANCE_DELAY ) {
2694                         advance = 1;
2695                 }
2696         }
2697
2698         if ( !voice_active && (Brief_textdraw_finished > 0) ) {
2699                 if ( Brief_stage_time > max(5000, Num_brief_text_lines[0] * 3500) ) {
2700                         advance = 1;
2701                 }
2702         }
2703
2704         return advance;
2705 }
2706