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