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