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