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