FS2_Open
Open source remastering of the Freespace 2 engine
missiontraining.cpp
Go to the documentation of this file.
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 "cfile/cfile.h"
15 #include "globalincs/alphacolors.h"
16 #include "hud/hudmessage.h"
17 #include "io/timer.h"
18 #include "mission/missiongoals.h"
19 #include "mission/missionmessage.h"
20 #include "mission/missionparse.h"
22 #include "mod_table/mod_table.h"
23 #include "network/multi.h"
24 #include "parse/parselo.h"
25 #include "parse/sexp.h"
26 #include "playerman/player.h"
27 #include "popup/popup.h"
28 #include "ship/ship.h"
29 #include "sound/audiostr.h"
30 #include "sound/sound.h"
31 #include "weapon/emp.h"
32 
33 
34 
35 #define MAX_TRAINING_MESSAGE_LINES 10
36 #define TRAINING_MESSAGE_WINDOW_WIDTH 266
37 #define TRAINING_LINE_WIDTH 250 // width in pixels of actual text
38 #define TRAINING_TIMING 150 // milliseconds per character to display messages
39 #define TRAINING_TIMING_BASE 1000 // Minimum milliseconds to display any message
40 #define TRAINING_OBJ_WND_WIDTH 170 // number of pixels wide window is.
41 #define TRAINING_OBJ_LINE_WIDTH 150 // number of pixels wide text can be
42 #define TRAINING_OBJ_LINES 50 // number of lines to track in objective list
43 #define TRAINING_OBJ_DISPLAY_LINES 5 // only display this many lines on screen max
44 #define MAX_TRAINING_MESSAGE_MODS 20
45 #define TRAINING_MESSAGE_QUEUE_MAX 40
46 
47 #define TRAINING_OBJ_STATUS_UNKNOWN (1 << 28) // directive status is unknown
48 #define TRAINING_OBJ_STATUS_KNOWN (1 << 29) // directive status is known (satisfied or failed)
49 #define TRAINING_OBJ_LINES_KEY (1 << 30) // flag indicating line describes the key associated with objective
50 #define TRAINING_OBJ_LINES_EVENT_STATUS_MASK (TRAINING_OBJ_STATUS_KNOWN | TRAINING_OBJ_STATUS_UNKNOWN)
51 
52 #define TRAINING_OBJ_LINES_MASK(n) (Training_obj_lines[n] & 0xffff)
53 
54 #define TMMOD_NORMAL 0
55 #define TMMOD_BOLD 1
56 
57 typedef struct {
58  char *pos;
59  int mode; // what function to perform at given position (TMMOD_*)
60 } training_message_mods; // training message modifiers
61 
62 typedef struct {
63  int num;
64  int timestamp;
65  int length;
68 
70 const char *Training_lines[MAX_TRAINING_MESSAGE_LINES]; // Training message split into lines
72 
78 int Training_voice = -1;
81 int Training_flag = 0;
84 int Training_bind_warning = -1; // Missiontime at which we last gave warning
87 
88 // coordinates for training messages
90  { 174, 40 },
91  { 379, 125 }
92 };
93 
94 // coordinates for "directives" window on hud
96  { 0, 187 },
97  { 0, 287 }
98 };
99 
100 
101 // training objectives global vars.
105 
106 // local module prototypes
108 void message_translate_tokens(char *buf, char *text);
109 
110 
111 #define NUM_DIRECTIVE_GAUGES 3
112 static hud_frames Directive_gauge[NUM_DIRECTIVE_GAUGES];
113 static int Directive_frames_loaded = 0;
114 
115 #define DIRECTIVE_H 9
116 #define DIRECTIVE_X 5
117 #define NUM_DIRECTIVE_COORDS 3
118 #define DIRECTIVE_COORDS_TOP 0
119 #define DIRECTIVE_COORDS_MIDDLE 1
120 #define DIRECTIVE_COORDS_TITLE 2
121 
124 {
125 }
126 
128 {
129  header_offsets[0] = x;
130  header_offsets[1] = y;
131 }
132 
134 {
136 }
137 
139 {
140  text_start_offsets[0] = x;
141  text_start_offsets[1] = y;
142 }
143 
145 {
146  text_h = h;
147 }
148 
150 {
151  max_line_width = w;
152 }
153 
155 {
157 }
158 
159 void HudGaugeDirectives::initBitmaps(char *fname_top, char *fname_middle, char *fname_bottom)
160 {
162  if ( directives_top.first_frame < 0 ) {
163  Warning(LOCATION,"Cannot load hud ani: %s\n", fname_top);
164  }
165 
167  if ( directives_middle.first_frame < 0 ) {
168  Warning(LOCATION,"Cannot load hud ani: %s\n", fname_middle);
169  }
170 
172  if ( directives_bottom.first_frame < 0 ) {
173  Warning(LOCATION,"Cannot load hud ani: %s\n", fname_bottom);
174  }
175 }
176 
178 {
179  if (sexp_override) {
180  return false;
181  }
182 
184  return false;
185  }
186 
188  return false;
189  }
190 
191  // force the directives list to display in training missions even if this gauge isn't active.
193  return false;
194 
195  if ( !(Game_detail_flags & DETAIL_FLAG_HUD) ) {
196  return false;
197  }
198 
199  if ((Viewer_mode & disabled_views)) {
200  return false;
201  }
202 
203  if(pop_up) {
204  if(!popUpActive()) {
205  return false;
206  }
207  }
208 
209  if (gauge_config == HUD_ETS_GAUGE) {
211  return false;
212  }
213  }
214 
215  return true;
216 }
217 
219 {
223 }
224 
225 void HudGaugeDirectives::render(float frametime)
226 {
227  char buf[256], *second_line;
228  int i, t, x, y, z, end, offset, bx, by, y_count;
229  color *c;
230 
232  return;
233  }
234 
235  offset = 0;
237  if (end > Max_directives) {
238  end = Max_directives;
239  offset = Training_obj_num_lines - end;
240  }
241 
242  // draw top of objective display
243  setGaugeColor();
244 
246 
247  // print out title
248  renderPrintf(position[0] + header_offsets[0], position[1] + header_offsets[1], EG_OBJ_TITLE, XSTR( "directives", 422));
249 
250  bx = position[0];
252 
253  y_count = 0;
254  for (i=0; i<end; i++) {
255  x = position[0] + text_start_offsets[0];
256  y = position[1] + text_start_offsets[1] + y_count * text_h;
257  z = TRAINING_OBJ_LINES_MASK(i + offset);
258 
259  c = &Color_normal;
260  if (Training_obj_lines[i + offset] & TRAINING_OBJ_LINES_KEY) {
261  message_translate_tokens(buf, Mission_events[z].objective_key_text); // remap keys
262  c = &Color_bright_green;
263  } else {
264  strcpy_s(buf, Mission_events[z].objective_text);
265  if (Mission_events[z].count){
266  sprintf(buf + strlen(buf), NOX(" [%d]"), Mission_events[z].count);
267  }
268 
269  // if this is a multiplayer tvt game, and this is event is not for my team, don't display it
270  if((MULTI_TEAM) && (Net_player != NULL)){
271  if((Mission_events[z].team != -1) && (Net_player->p_info.team != Mission_events[z].team)){
272  continue;
273  }
274  }
275 
276  switch (mission_get_event_status(z)) {
277  case EVENT_CURRENT:
278  c = &Color_bright_white;
279  break;
280 
281  case EVENT_FAILED:
282  c = &Color_bright_red;
283  break;
284 
285  case EVENT_SATISFIED:
287  if (t + i2f(2) > Missiontime) {
288  if (Missiontime % fl2f(.4f) < fl2f(.2f)){
289  c = &Color_bright_blue;
290  } else {
291  c = &Color_bright_white;
292  }
293  } else {
294  c = &Color_bright_blue;
295  }
296  break;
297  }
298  }
299 
300  // maybe split the directives line
301  second_line = split_str_once(buf, max_line_width);
302  Assert( second_line != buf );
303 
304  // blit the background frames
305  setGaugeColor();
306 
308 
309  by += text_h;
310 
311  if ( second_line ) {
313 
314  by += text_h;
315  }
316 
317  // blit the text
319 
320  renderString(x, y, EG_OBJ1 + i, buf);
321 
322  y_count++;
323 
324  if ( second_line ) {
325  y = position[1] + text_start_offsets[1] + y_count * text_h;
326 
327  renderString(x+12, y, EG_OBJ1 + i + 1, second_line);
328 
329  y_count++;
330  }
331  }
332 
333  // draw the bottom of objective display
334  setGaugeColor();
335 
337 }
338 
343 {
344  int i;
345 
349  Training_failure = 0;
352  }
353  for (i=0; i<TRAINING_OBJ_LINES; i++)
354  Training_obj_lines[i] = -1;
355 
356  // Goober5000
357  for (i = 0; i < TRAINING_MESSAGE_QUEUE_MAX; i++)
358  Training_message_queue[i].special_message = NULL;
359 
360  // The E: This is now handled by the new HUD code. No need to check here.
361  Directive_frames_loaded = 1;
362 
363  // only clear player flags if this is actually a training mission
366  }
367 }
368 
370 {
371  int i;
372  for ( i = 0; i < NUM_DIRECTIVE_GAUGES; i++ ) {
373  bm_page_in_aabitmap( Directive_gauge[i].first_frame, Directive_gauge[i].num_frames );
374  }
375 }
376 
377 
378 int comp_training_lines_by_born_on_date(const void *m1, const void *m2)
379 {
380  int *e1, *e2;
381  e1 = (int*) m1;
382  e2 = (int*) m2;
383 
384  Assert(Mission_events[*e1 & 0xffff].born_on_date != 0);
385  Assert(Mission_events[*e2 & 0xffff].born_on_date != 0);
386 
387  return (Mission_events[*e1 & 0xffff].born_on_date - Mission_events[*e2 & 0xffff].born_on_date);
388 }
389 
390 
396 #define MIN_SATISFIED_TIME 5
397 #define MIN_FAILED_TIME 7
399 {
400  int i, event_status, offset;
401 
402  // start by sorting on born on date
404 
405  // get the index of the first directive that will be displayed
406  // if less than 0, display all lines
408 
409  if (offset <= 0) {
410  return;
411  }
412 
413  // go through lines 0 to offset-1 and check if there are any CURRENT or RECENTLY_KNOWN events that should be shown
414  int num_offset_events = 0;
415  for (i=0; i<offset; i++) {
417 
418  // if this is a multiplayer tvt game, and this is event is for another team, don't touch it
419  if(MULTI_TEAM && (Net_player != NULL)){
421  continue ;
422  }
423  }
424 
425  if (event_status == EVENT_CURRENT) {
427  num_offset_events++;
428  } else if (event_status == EVENT_SATISFIED) {
431  num_offset_events++;
432  } else {
434  }
435  } else if (event_status == EVENT_FAILED) {
438  num_offset_events++;
439  } else {
441  }
442  }
443  }
444 
445  // if there are no directives which should be moved, we're done
446  if (num_offset_events == 0) {
447  return;
448  }
449 
450  // go through lines offset to Training_obj_num_lines to check which should be shown, since some will need to be bumped
451  for (i=offset; i<Training_obj_num_lines; i++) {
453 
454  // if this is a multiplayer tvt game, and this is event is for another team, it can be bumped
455  if(MULTI_TEAM && (Net_player != NULL)){
458  continue ;
459  }
460  }
461 
462  if (event_status == EVENT_CURRENT) {
464  } else if (event_status == EVENT_SATISFIED) {
467  } else {
469  }
470  } else if (event_status == EVENT_FAILED) {
473  } else {
475  }
476  }
477  }
478 
479 
480  int slot_idx, unkn_vis, last_directive;
481  // go through list and bump as needed
482  for (i=0; i<num_offset_events; i++) {
483 
484  // find most recent directive that would not be shown
485  for (unkn_vis=offset-1; unkn_vis>=0; unkn_vis--) {
487  break;
488  }
489  }
490 
491  // find first slot that can be bumped
492  // look at the last (N-4 to N) positions
493  for (slot_idx=0; slot_idx<Max_directives; slot_idx++) {
494  if ( Training_obj_lines[i+offset] & TRAINING_OBJ_STATUS_KNOWN ) {
495  break;
496  }
497  }
498 
499  // shift and replace (mark old one as STATUS_KNOWN)
500  // store the directive that won't be shown
501  last_directive = Training_obj_lines[Training_obj_num_lines-1];
502 
503  for (int j=slot_idx; j>0; j--) {
504  Training_obj_lines[j+offset-1] = Training_obj_lines[j+offset-2];
505  }
507  Training_obj_lines[unkn_vis] = last_directive;
510  }
511 
512  // remove event status
513  for (i=0; i<Training_obj_num_lines; i++) {
515  }
516 }
517 
524 {
525  int i, event_idx, event_status;
526 
528  for (event_idx=0; event_idx<Num_mission_events; event_idx++) {
529  event_status = mission_get_event_status(event_idx);
530  if ( (event_status != EVENT_UNBORN) && Mission_events[event_idx].objective_text && (timestamp() > Mission_events[event_idx].born_on_date + Directive_wait_time) ) {
531  if (!Training_failure || !strnicmp(Mission_events[event_idx].name, XSTR( "Training failed", 423), 15)) {
532 
533  // check for the actual objective
534  for (i=0; i<Training_obj_num_lines; i++) {
535  if (Training_obj_lines[i] == event_idx) {
536  break;
537  }
538  }
539 
540  // not in objective list, need to add it
541  if (i == Training_obj_num_lines) {
542  if (Training_obj_num_lines == TRAINING_OBJ_LINES) {
543 
544  // objective list full, remove topmost objective
545  for (i=1; i<TRAINING_OBJ_LINES; i++) {
547  }
548  Training_obj_num_lines--;
549  }
550  // add the new directive
551  Training_obj_lines[Training_obj_num_lines++] = event_idx;
552  }
553 
554  // now check for the associated keypress text
555  for (i=0; i<Training_obj_num_lines; i++) {
556  if (Training_obj_lines[i] == (event_idx | TRAINING_OBJ_LINES_KEY)) {
557  break;
558  }
559  }
560 
561  // if there is a keypress message with directive, process that too.
562  if (Mission_events[event_idx].objective_key_text) {
563  if (event_status == EVENT_CURRENT) {
564 
565  // not in objective list, need to add it
566  if (i == Training_obj_num_lines) {
567  if (Training_obj_num_lines == TRAINING_OBJ_LINES) {
568 
569  // objective list full, remove topmost objective
570  for (i=1; i<Training_obj_num_lines; i++) {
572  }
573  Training_obj_num_lines--;
574  }
575  // mark objective as having key text
576  Training_obj_lines[Training_obj_num_lines++] = event_idx | TRAINING_OBJ_LINES_KEY;
577  }
578 
579  } else {
580  // message with key press text is no longer valid, so remove it
581  if (i < Training_obj_num_lines) {
582  for (; i<Training_obj_num_lines - 1; i++) {
584  }
585  Training_obj_num_lines--;
586  }
587  }
588  }
589  }
590  }
591  }
592 
593  // now sort list of events
594  // sort on EVENT_CURRENT and born on date, for other events (EVENT_SATISFIED, EVENT_FAILED) sort on born on date
596 }
597 
602 {
603  int i;
604 
605  if (Training_voice >= 0) {
606  if (Training_voice_type) {
608 
609  } else {
611  }
612  }
613 
614  // Goober5000
615  for (i = 0; i < TRAINING_MESSAGE_QUEUE_MAX; i++)
616  {
617  if (Training_message_queue[i].special_message != NULL)
618  {
619  vm_free(Training_message_queue[i].special_message);
620  Training_message_queue[i].special_message = NULL;
621  }
622  }
623 
624  Training_voice = -1;
626 
627  *Training_buf = 0;
628 }
629 
633 char *translate_message_token(char *str)
634 {
635  if (!stricmp(str, NOX("wp"))) {
636  sprintf(str, "%d", Training_context_goal_waypoint + 1);
637  return str;
638  }
639 
640  return NULL;
641 }
642 
646 void message_translate_tokens(char *buf, char *text)
647 {
648  char temp[40], *toke1, *toke2, *ptr;
649  int r;
650 
651  *buf = 0;
652  toke1 = strchr(text, '$');
653  toke2 = strchr(text, '#');
654  while (toke1 || toke2) { // is either token types present?
655  if (!toke2 || (toke1 && (toke1 < toke2))) { // found $ before #
656  strncpy(buf, text, toke1 - text + 1); // copy text up to token
657  buf += toke1 - text + 1;
658  text = toke1 + 1; // advance pointers past processed data
659 
660  toke2 = strchr(text, '$');
661  if (!toke2 || ((toke2 - text) == 0)) // No second one, or possibly a double?
662  break;
663 
664  // make sure we aren't going to have any type of out-of-bounds issues
665  if ( ((toke2 - text) < 0) || ((toke2 - text) >= (ptr_s)sizeof(temp)) ) {
666  Int3();
667  } else {
668  strncpy(temp, text, toke2 - text); // isolate token into seperate buffer
669  temp[toke2 - text] = 0; // null terminate string
670  ptr = translate_key(temp); // try and translate key
671  if (ptr) { // was key translated properly?
672  if (!stricmp(ptr, NOX("none")) && (Training_bind_warning != Missiontime)) {
674  r = popup(PF_TITLE_BIG | PF_TITLE_RED, 2, XSTR( "&Bind Control", 424), XSTR( "&Abort mission", 425),
675  XSTR( "Warning\nYou have no control bound to the action \"%s\". You must do so before you can continue with your training.", 426),
677 
678  if (r) { // do they want to abort the mission?
680  return;
681  }
682 
683  gameseq_post_event(GS_EVENT_CONTROL_CONFIG); // goto control config screen to bind the control
684  }
685  }
686 
687  buf--; // erase the $
688  strcpy(buf, ptr); // put translated key in place of token
689  buf += strlen(buf);
690  text = toke2 + 1;
691  }
692  }
693  } else {
694  strncpy(buf, text, toke2 - text + 1); // copy text up to token
695  buf += toke2 - text + 1;
696  text = toke2 + 1; // advance pointers past processed data
697 
698  toke1 = strchr(text, '#');
699  if (!toke1 || ((toke1 - text) == 0)) // No second one, or possibly a double?
700  break;
701 
702  // make sure we aren't going to have any type of out-of-bounds issues
703  if ( ((toke1 - text) < 0) || ((toke1 - text) >= (ptr_s)sizeof(temp)) ) {
704  Int3();
705  } else {
706  strncpy(temp, text, toke1 - text); // isolate token into seperate buffer
707  temp[toke1 - text] = 0; // null terminate string
708  ptr = translate_message_token(temp); // try and translate key
709  if (ptr) { // was key translated properly?
710  buf--; // erase the #
711  strcpy(buf, ptr); // put translated key in place of token
712  buf += strlen(buf);
713  text = toke1 + 1;
714  }
715  }
716  }
717 
718  toke1 = strchr(text, '$');
719  toke2 = strchr(text, '#');
720  }
721 
722  strcpy(buf, text);
723  return;
724 }
725 
733 {
734  int len;
735  CFILE *fp;
736 
737  if (index < 0) {
738  if (Training_voice >= 0) {
739  if (Training_voice_type) {
741 
742  } else {
744  }
745  }
746 
747  Training_voice = -1;
748  return -1;
749  }
750 
751  if (Message_waves[index].num < 0) {
752  fp = cfopen(Message_waves[index].name, "rb");
753  if (!fp)
754  return -1;
755 
756  len = cfilelength(fp);
757  cfclose(fp);
758  if (len > 100000) {
759  if ((Training_voice < 0) || !Training_voice_type || (Training_voice != index)) {
760  if (Training_voice >= 0) {
761  if (Training_voice_type) {
762  if (Training_voice == index)
764  else
766 
767  } else {
769  }
770  }
771 
772  if (strnicmp(Message_waves[index].name, NOX("none.wav"), 4)) {
774  if (Training_voice_handle < 0) {
775  nprintf(("Warning", "Unable to load voice file %s\n", Message_waves[index].name));
776  }
777  }
778  } // Training_voice should be valid and loaded now
779 
781  if (Training_voice_handle >= 0)
783 
785  return Training_voice;
786 
787  } else {
788  game_snd tmp_gs;
789  strcpy_s(tmp_gs.filename, Message_waves[index].name);
790  Message_waves[index].num = snd_load(&tmp_gs, 0);
791  if (Message_waves[index].num < 0) {
792  nprintf(("Warning", "Cannot load message wave: %s. Will not play\n", Message_waves[index].name));
793  return -1;
794  }
795  }
796  }
797 
798  if (Training_voice >= 0) {
799  if (Training_voice_type) {
801 
802  } else {
804  }
805  }
806 
808  if (Message_waves[index].num >= 0)
810  else
812 
814  return Training_voice;
815 }
816 
823 void message_training_setup(int m, int length, char *special_message)
824 {
825  if ((m < 0) || !Messages[m].message[0]) { // remove current message from the screen
826  Training_num_lines = 0;
827  return;
828  }
829 
830  // translate tokens in message to the real things
831  if (special_message == NULL)
833  else
834  message_translate_tokens(Training_buf, special_message);
835 
837 
838  // moved from message_training_display() because we got rid of an extra buffer and we have to determine
839  // the number of lines earlier to avoid inadvertant modification of Training_buf. - taylor
842 
843  Assert( Training_num_lines >= 0 );
844 
845  if (message_play_training_voice(Messages[m].wave_info.index) < 0) {
846  if (length > 0)
847  Training_message_timestamp = timestamp(length * 1000);
848  else
849  Training_message_timestamp = timestamp(TRAINING_TIMING_BASE + strlen(Messages[m].message) * TRAINING_TIMING); // no voice file playing
850 
851  } else
853 }
854 
858 void message_training_queue(char *text, int timestamp, int length)
859 {
860  int m;
861  char temp_buf[TRAINING_MESSAGE_LENGTH];
862 
865  if (!stricmp(text, NOX("none"))) {
866  m = -1;
867  } else {
868  for (m=0; m<Num_messages; m++)
869  if (!stricmp(text, Messages[m].name))
870  break;
871 
872  Assert(m < Num_messages);
873  if (m >= Num_messages)
874  return;
875  }
876 
877  Training_message_queue[Training_message_queue_count].num = m;
878  Training_message_queue[Training_message_queue_count].timestamp = timestamp;
879  Training_message_queue[Training_message_queue_count].length = length;
880 
881  // Goober5000 - this shouldn't happen, but let's be safe
882  if (Training_message_queue[Training_message_queue_count].special_message != NULL)
883  {
884  Int3();
885  vm_free(Training_message_queue[Training_message_queue_count].special_message);
886  Training_message_queue[Training_message_queue_count].special_message = NULL;
887  }
888 
889  // Goober5000 - replace variables if necessary
890  strcpy_s(temp_buf, Messages[m].message);
892  Training_message_queue[Training_message_queue_count].special_message = vm_strdup(temp_buf);
893 
895  }
896 }
897 
902 {
903  // we're overwriting all messages with the next message, but to
904  // avoid memory leaks, we should free the special message entry
905  if (Training_message_queue[idx].special_message != NULL)
906  {
907  vm_free(Training_message_queue[idx].special_message);
908  Training_message_queue[idx].special_message = NULL;
909  }
910 
911  // replace current message with the one above it, etc.
912  for (int j=idx+1; j<Training_message_queue_count; j++)
913  Training_message_queue[j - 1] = Training_message_queue[j];
914 
915  // delete the topmost message
916  Training_message_queue_count--;
917  Training_message_queue[Training_message_queue_count].length = -1;
918  Training_message_queue[Training_message_queue_count].num = -1;
919  Training_message_queue[Training_message_queue_count].timestamp = -1;
920  Training_message_queue[Training_message_queue_count].special_message = NULL; // not a memory leak because we copied the pointer
921 }
922 
927 {
928  int i, iship_num;
929 
930  // get the instructor's ship.
931  iship_num = ship_name_lookup(NOX("instructor"));
932 
933  // if the instructor is dying or departing, do nothing
934  if ( iship_num != -1 ) // added by Goober5000
935  if (Ships[iship_num].flags & (SF_DYING | SF_DEPARTING))
936  return;
937 
938  if (Training_failure)
939  return;
940 
941  for (i=0; i<Training_message_queue_count; i++) {
942  if (timestamp_elapsed(Training_message_queue[i].timestamp)) {
943  message_training_setup(Training_message_queue[i].num, Training_message_queue[i].length, Training_message_queue[i].special_message);
944 
945  // remove this message from the queue now.
947  i--;
948  }
949  }
950 }
951 
953 {
954  int z;
955 
957  return;
958  }
959 
961  return;
962  }
963 
966 
967  if (Training_failure){
968  return;
969  }
970 
972  return;
973  }
974 
975  // the code that preps the training message and counts the number of lines
976  // has been moved to message_training_setup()
977 
978  if (Training_num_lines <= 0){
979  return;
980  }
981 
983 
987  else
989 
990  if (!z)
991  Training_message_timestamp = timestamp(2000); // 2 second delay
992  }
993 
995 }
996 
999 {
1000 }
1001 
1003 {
1005  return false;
1006  }
1007 
1008  if ( !(Game_detail_flags & DETAIL_FLAG_HUD) ) {
1009  return false;
1010  }
1011 
1012  if ((Viewer_mode & disabled_views)) {
1013  return false;
1014  }
1015 
1016  if(pop_up) {
1017  if(!popUpActive()) {
1018  return false;
1019  }
1020  }
1021 
1022  return true;
1023 }
1024 
1026 {
1027 }
1028 
1033 {
1034  const char *str;
1035  char buf[256];
1036  int i, z, x, y, height, mode, count;
1037 
1038  if (Training_failure){
1039  return;
1040  }
1041 
1043  return;
1044  }
1045 
1046  if (Training_num_lines <= 0){
1047  return;
1048  }
1049 
1051  height = gr_get_font_height();
1055 
1057  mode = count = 0;
1058  for (i=0; i<Training_num_lines; i++) { // loop through all lines of message
1059  str = Training_lines[i];
1060  z = 0;
1062  y = position[1] + i * height + height / 2 + 1;
1063 
1064  while ((str - Training_lines[i]) < Training_line_lengths[i]) { // loop through each character of each line
1065  if ((count < MAX_TRAINING_MESSAGE_MODS) && (str == Training_message_mods[count].pos)) {
1066  buf[z] = 0;
1067  renderPrintf(x, y, buf);
1068  gr_get_string_size(&z, NULL, buf);
1069  x += z;
1070  z = 0;
1071 
1072  mode = Training_message_mods[count++].mode;
1073  switch (mode) {
1074  case TMMOD_NORMAL:
1076  break;
1077 
1078  case TMMOD_BOLD:
1080  break;
1081  }
1082  }
1083 
1084  buf[z++] = *str++;
1085  }
1086 
1087  if (z) {
1088  buf[z] = 0;
1089  renderPrintf(x, y, "%s", buf);
1090  }
1091  }
1092 }
1093 
1098 {
1099  int count;
1100  char *src, *dest, buf[TRAINING_MESSAGE_LENGTH];
1101 
1102  message_translate_tokens(buf, message);
1103  count = 0;
1104  src = buf;
1105  dest = Training_buf;
1106  while (*src) {
1107  if (!strnicmp(src, NOX("<b>"), 3)) {
1109  src += 3;
1110  Training_message_mods[count].pos = dest;
1111  Training_message_mods[count].mode = TMMOD_BOLD;
1112  count++;
1113  }
1114 
1115  if (!strnicmp(src, NOX("</b>"), 4)) {
1117  src += 4;
1118  Training_message_mods[count].pos = dest;
1119  Training_message_mods[count].mode = TMMOD_NORMAL;
1120  count++;
1121  }
1122 
1123  *dest++ = *src++;
1124  }
1125 
1126  *dest = 0;
1127  if (count < MAX_TRAINING_MESSAGE_MODS)
1128  Training_message_mods[count].pos = NULL;
1129 }
1130 
1132 {
1133  Training_failure = 1;
1134  // JasonH: Add code here to suspend training and display a directive to warp out.
1135  // Suspend training.
1136  // Give directive to warp out.
1137  // Also ensure that a special failure debriefing is given. Must mention firing at instructor.
1138  // Ask Sandeep to write it (or you can).
1139 }
int timestamp(int delta_ms)
Definition: timer.cpp:226
training_message_mods Training_message_mods[MAX_TRAINING_MESSAGE_MODS]
#define PLAYER_FLAGS_MATCH_TARGET
Definition: player.h:36
int i
Definition: multi_pxo.cpp:466
fix Missiontime
Definition: systemvars.cpp:19
#define vm_free(ptr)
Definition: pstypes.h:548
void message_training_update_frame()
int disabled_views
Definition: hud.h:221
int Num_mission_events
int flags
Definition: player.h:104
GLfloat GLfloat GLfloat GLfloat h
Definition: Glext.h:7280
int game_type
Definition: missionparse.h:138
int Training_failure
#define EVENT_SATISFIED
Definition: missiongoals.h:78
net_player * Net_player
Definition: multi.cpp:94
#define NUM_DIRECTIVE_GAUGES
int Training_message_timestamp
int snd_load(game_snd *gs, int allow_hardware_load)
Definition: sound.cpp:281
GLuint index
Definition: Glext.h:5608
#define ASF_VOICE
Definition: audiostr.h:21
void message_training_setup(int m, int length, char *special_message)
void training_mission_shutdown()
#define VM_WARP_CHASE
Definition: systemvars.h:37
int message_play_training_voice(int index)
void setGaugeColor(int bright_index=-4)
Definition: hud.cpp:445
int Training_flag
#define EVENT_UNBORN
Definition: missiongoals.h:76
int Training_message_method
void _cdecl void void _cdecl void _cdecl Warning(char *filename, int line, SCP_FORMAT_STRING const char *format,...) SCP_FORMAT_STRING_ARGS(3
int Training_line_lengths[MAX_TRAINING_MESSAGE_LINES]
Assert(pm!=NULL)
#define GR_NUM_RESOLUTIONS
Definition: 2d.h:651
#define EVENT_CURRENT
Definition: missiongoals.h:77
int popUpActive()
Definition: hud.cpp:578
#define PLAYER_FLAGS_LINK_PRIMARY
Definition: player.h:45
void training_fail()
void initMaxLineWidth(int w)
#define VM_EXTERNAL
Definition: systemvars.h:31
Definition: 2d.h:95
char * translate_key(char *key)
CButton * team
int base_h
Definition: hud.h:205
void initBottomBgOffset(int offset)
#define CONTROL_CONFIG_XSTR
GLclampf f
Definition: Glext.h:7097
const char * Training_lines[MAX_TRAINING_MESSAGE_LINES]
int snd_is_playing(int sig)
Definition: sound.cpp:1047
Definition: cfile.h:28
#define EG_OBJ1
Definition: emp.h:40
hud_frames directives_bottom
int Training_obj_window_coords[GR_NUM_RESOLUTIONS][2]
#define TRAINING_MESSAGE_WINDOW_WIDTH
GLenum mode
Definition: Glext.h:5794
void sort_training_objectives()
#define TMMOD_NORMAL
void training_process_message(char *message)
void gr_set_screen_scale(int w, int h, int zoom_w, int zoom_h, int max_w, int max_h, int center_w, int center_h, bool force_stretch)
Definition: 2d.cpp:104
#define PLAYER_FLAGS_AUTO_TARGETING
Definition: player.h:38
GLenum GLuint GLenum GLsizei const GLchar * message
Definition: Glext.h:5156
void gr_set_color_fast(color *dst)
Definition: 2d.cpp:1197
void training_check_objectives()
hud_frames directives_top
shader Training_msg_glass
Definition: hudtarget.cpp:84
#define HUD_DIRECTIVES_VIEW
Definition: hudgauges.h:36
char Training_voice_filename[NAME_LENGTH]
#define Int3()
Definition: pstypes.h:292
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition: Glext.h:7308
void training_mission_page_in()
SCP_vector< message_extra > Message_waves
int Training_obj_num_lines
#define PLAYER_FLAGS_AUTO_MATCH_SPEED
Definition: player.h:39
char filename[MAX_FILENAME_LEN]
Definition: sound.h:76
config_item Control_config[]
Stores the keyboard configuration.
int bm_load_animation(const char *real_filename, int *nframes, int *fps, int *keyframe, int can_drop_frames, int dir_type)
Loads a bitmap sequance so we can draw with it.
Definition: bmpman.cpp:1420
Definition: hud.h:201
int audiostream_is_playing(int i)
Definition: audiostr.cpp:1827
int Training_context_goal_waypoint
Definition: sexp.cpp:838
SCP_vector< MMessage > Messages
int Training_voice_type
int instance
Definition: object.h:150
#define TRAINING_MESSAGE_QUEUE_MAX
GLintptr offset
Definition: Glext.h:5497
#define VM_DEAD_VIEW
Definition: systemvars.h:33
net_player_info p_info
Definition: multi.h:473
GLdouble GLdouble GLdouble r
Definition: Glext.h:5337
#define cfopen(...)
Definition: cfile.h:134
#define TRAINING_OBJ_LINES
color Color_bright_green
Definition: alphacolors.cpp:31
#define nprintf(args)
Definition: pstypes.h:239
void audiostream_stop(int i, int rewind, int paused)
Definition: audiostr.cpp:1840
#define f2i(a)
Definition: fix.h:22
#define TRAINING_OBJ_LINES_KEY
#define HUD_OBJECT_TRAINING_MESSAGES
Definition: hudparse.h:73
#define TRAINING_LINE_WIDTH
void render(float frametime)
int num_frames
Definition: hud.h:34
#define strnicmp(s1, s2, n)
Definition: config.h:272
int Training_num_lines
mission_event Mission_events[MAX_MISSION_EVENTS]
bool sexp_override
Definition: hud.h:217
int base_w
Definition: hud.h:205
#define TRAINING_TIMING_BASE
void message_translate_tokens(char *buf, char *text)
#define w(p)
Definition: modelsinc.h:68
sprintf(buf,"(%f,%f,%f)", v3->xyz.x, v3->xyz.y, v3->xyz.z)
void gr_shade(int x, int y, int w, int h, int resize_mode)
Definition: 2d.cpp:2085
GLdouble GLdouble z
Definition: Glext.h:5451
#define TRAINING_OBJ_STATUS_UNKNOWN
void initMiddleFrameOffsetY(int y)
int Max_directives
#define vm_strdup(ptr)
Definition: pstypes.h:549
#define EG_OBJ_TITLE
Definition: emp.h:39
int comp_training_lines_by_born_on_date(const void *m1, const void *m2)
Definition: hud.h:32
int Directive_wait_time
Definition: mod_table.cpp:18
#define TRAINING_TIMING
int split_str(const char *src, int max_pixel_w, int *n_chars, const char **p_str, int max_lines, char ignore_char)
Definition: parselo.cpp:3412
int Training_voice_handle
void insertion_sort(void *array_base, size_t array_size, size_t element_size, int(*fncompare)(const void *, const void *))
Definition: systemvars.cpp:599
#define DETAIL_FLAG_HUD
Definition: systemvars.h:114
void message_training_queue(char *text, int timestamp, int length)
int idx
Definition: multiui.cpp:761
cfbp fp
Definition: cfile.cpp:1065
GLdouble GLdouble t
Definition: Glext.h:5329
#define TRAINING_OBJ_DISPLAY_LINES
#define MIN_FAILED_TIME
void renderString(int x, int y, const char *str)
Definition: hud.cpp:665
#define HUD_SOURCE_TRAINING
Definition: hudmessage.h:22
int gauge_config
Definition: hud.h:207
GLint GLint GLint GLint GLint x
Definition: Glext.h:5182
#define MESSAGE_LENGTH
Definition: globals.h:23
int hud_disabled()
Checks if HUD disabled.
Definition: hud.cpp:1306
void message_training_remove_from_queue(int idx)
#define TRAINING_OBJ_LINES_MASK(n)
int Training_message_visible
void message_training_queue_check()
#define TRAINING_MESSAGE_LENGTH
Definition: globals.h:24
const char * XSTR(const char *str, int index)
Definition: localize.cpp:851
#define MULTI_TEAM
Definition: multi.h:655
color Color_bright_red
Definition: alphacolors.cpp:34
#define NOX(s)
Definition: pstypes.h:473
int Training_message_queue_count
training_message_queue Training_message_queue[TRAINING_MESSAGE_QUEUE_MAX]
GLbitfield flags
Definition: Glext.h:6722
int Training_obj_lines[TRAINING_OBJ_LINES]
GLuint const GLchar * name
Definition: Glext.h:5608
int first_frame
Definition: hud.h:33
#define MISSION_TYPE_TRAINING
Definition: missionparse.h:63
bool sexp_replace_variable_names_with_values(char *text, int max_len)
Definition: sexp.cpp:29395
GLbyte by
Definition: Glext.h:8237
ship Ships[MAX_SHIPS]
Definition: ship.cpp:122
void initTextStartOffsets(int x, int y)
uint Game_detail_flags
Definition: systemvars.cpp:52
void audiostream_close_file(int i, int fade)
Definition: audiostr.cpp:1772
#define SF_DEPARTING
Definition: ship.h:475
GLuint GLuint num
Definition: Glext.h:9089
color Color_bright_white
Definition: alphacolors.cpp:32
#define EVENT_FAILED
Definition: missiongoals.h:79
color Color_white
Definition: alphacolors.cpp:32
void HUD_add_to_scrollback(const char *text, int source)
Definition: hudmessage.cpp:642
void initHeaderOffsets(int x, int y)
void initTextHeight(int h)
GLint GLsizei GLsizei height
Definition: Gl.h:1505
#define SF2_NO_ETS
Definition: ship.h:503
#define NAME_LENGTH
Definition: globals.h:15
GLubyte GLubyte GLubyte GLubyte w
Definition: Glext.h:5679
int Failed_key_index
int position[2]
Definition: hud.h:204
void bm_page_in_aabitmap(int bitmapnum, int nframes)
Marks a texture as being used for this level, and is anti-aliased.
Definition: bmpman.cpp:2354
player * Player
bool active
Definition: hud.h:215
void gr_get_string_size(int *w, int *h, const char *text, int len=9999)
Definition: font.cpp:196
int Training_message_window_coords[GR_NUM_RESOLUTIONS][2]
char * split_str_once(char *src, int max_pixel_w)
Definition: parselo.cpp:3330
int Num_messages
int gr_get_font_height()
Definition: font.cpp:187
GLenum src
Definition: Glext.h:5917
#define LOCATION
Definition: pstypes.h:245
char * translate_message_token(char *str)
#define VM_PADLOCK_ANY
Definition: systemvars.h:46
void gr_reset_screen_scale()
Definition: 2d.cpp:176
int Viewer_mode
Definition: systemvars.cpp:28
void initBitmaps(char *fname_top, char *fname_middle, char *fname_bottom)
#define timestamp_elapsed(stamp)
Definition: timer.h:102
void audiostream_play(int i, float volume, int looping)
Definition: audiostr.cpp:1803
const GLfloat * m
Definition: Glext.h:10319
color Color_normal
Definition: alphacolors.cpp:28
bool pop_up
Definition: hud.h:218
void renderPrintf(int x, int y, const char *format,...)
Definition: hud.cpp:724
GLenum GLuint GLenum GLsizei length
Definition: Glext.h:5156
GLint GLsizei count
Definition: Gl.h:1491
#define i2f(a)
Definition: fix.h:23
GLenum GLsizei len
Definition: Glext.h:6283
object * Player_obj
Definition: object.cpp:56
int Training_bind_warning
#define HUD_OBJECT_DIRECTIVES
Definition: hudparse.h:97
#define TMMOD_BOLD
uint flags2
Definition: ship.h:645
float aav_voice_volume
Definition: sound.cpp:78
#define SF_DYING
Definition: ship.h:447
int temp
Definition: lua.cpp:4996
int audiostream_open(const char *filename, int type)
Definition: audiostr.cpp:1713
#define MAX_TRAINING_MESSAGE_LINES
#define MIN_SATISFIED_TIME
#define VM_OTHER_SHIP
Definition: systemvars.h:35
int ship_name_lookup(const char *name, int inc_players)
Definition: ship.cpp:12900
false
Definition: lua.cpp:6789
hud_frames directives_middle
color Color_bright_blue
Definition: alphacolors.cpp:31
int ptr_s
Definition: pstypes.h:56
void snd_stop(int sig)
Definition: sound.cpp:875
#define TRAINING_OBJ_LINES_EVENT_STATUS_MASK
mission The_mission
int cfclose(CFILE *cfile)
Definition: cfile.cpp:895
void gr_set_shader(shader *shade)
Definition: 2d.cpp:1220
int hud_disabled_except_messages()
Like hud_disabled(), except messages are still drawn.
Definition: hud.cpp:1297
#define MAX_TRAINING_MESSAGE_MODS
int Training_voice
void render(float frametime)
char Training_buf[TRAINING_MESSAGE_LENGTH]
void gameseq_post_event(int event)
#define TRAINING_OBJ_STATUS_KNOWN
void training_mission_init()
#define PLAYER_FLAGS_MSG_MODE
Definition: player.h:37
#define stricmp(s1, s2)
Definition: config.h:271
int cfilelength(CFILE *cfile)
Definition: cfile.cpp:1393
const GLubyte * c
Definition: Glext.h:8376
float Master_voice_volume
Definition: sound.cpp:54
GLuint GLuint end
Definition: Gl.h:1502
#define fl2f(fl)
Definition: floating.h:38
#define HUD_ETS_GAUGE
Definition: hudgauges.h:31
#define PLAYER_FLAGS_LINK_SECONDARY
Definition: player.h:46
GLint y
Definition: Gl.h:1505
int mission_get_event_status(int event)
#define strcpy_s(...)
Definition: safe_strings.h:67
Definition: sound.h:72
int snd_play_raw(int soundnum, float pan, float vol_scale, int priority)
Definition: sound.cpp:477
void renderBitmap(int x, int y)
Definition: hud.cpp:782