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