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