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