FS2_Open
Open source remastering of the Freespace 2 engine
missiongoals.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 "debugconsole/console.h"
14 #include "freespace2/freespace.h"
16 #include "gamesnd/eventmusic.h"
17 #include "gamesnd/gamesnd.h"
18 #include "globalincs/alphacolors.h"
19 #include "hud/hud.h"
20 #include "hud/hudmessage.h"
21 #include "io/key.h"
22 #include "io/timer.h"
23 #include "localization/localize.h"
24 #include "mission/missiongoals.h"
25 #include "mission/missionlog.h"
27 #include "mod_table/mod_table.h"
28 #include "network/multi.h"
29 #include "network/multi_sexp.h"
30 #include "network/multi_team.h"
31 #include "network/multimsgs.h"
32 #include "network/stand_gui.h"
33 #include "parse/parselo.h"
34 #include "parse/sexp.h"
35 #include "playerman/player.h"
36 #include "ui/ui.h"
37 
38 
39 
40 // timestamp stuff for evaluating mission goals
41 #define GOAL_TIMESTAMP 0 // make immediately eval
42 #define GOAL_TIMESTAMP_TRAINING 500 // every half second
43 
44 #define MAX_GOALS_PER_LIST 15
45 #define MAX_GOAL_LINES 200
46 
47 // indicies for coordinates
48 #define GOAL_SCREEN_X_COORD 0
49 #define GOAL_SCREEN_Y_COORD 1
50 #define GOAL_SCREEN_W_COORD 2
51 #define GOAL_SCREEN_H_COORD 3
52 
53 /*
54 #define GOAL_SCREEN_TEXT_X 81
55 #define GOAL_SCREEN_TEXT_Y 95
56 #define GOAL_SCREEN_TEXT_W 385
57 #define GOAL_SCREEN_TEXT_H 299
58 #define GOAL_SCREEN_ICON_X 45
59 */
60 
61 static int Goal_screen_text_coords[GR_NUM_RESOLUTIONS][4] = {
62  {
63  81,95,385,299 // GR_640
64  },
65  {
66  130,152,385,299 // GR_1024
67  }
68 };
69 
70 static int Goal_screen_icon_xcoord[GR_NUM_RESOLUTIONS] = {
71  45, // GR_640
72  72 // GR_1024
73 };
74 
75 
76 // german version gets slightly diff coords
77 static int Objective_key_text_coords_gr[GR_NUM_RESOLUTIONS][3][2] = {
78  {
79  // GR_640
80  {175, 344},
81  {316, 344},
82  {432, 344}
83  },
84  {
85  // GR_1024
86  {310, 546},
87  {536, 546},
88  {688, 546}
89  }
90 };
91 static int Objective_key_icon_coords_gr[GR_NUM_RESOLUTIONS][3][2] = {
92  {
93  // GR_640
94  {150, 339},
95  {290, 339},
96  {406, 339}
97  },
98  {
99  // GR_1024
100  {272, 542},
101  {498, 542},
102  {650, 542}
103  }
104 };
105 
106 static int Objective_key_text_coords[GR_NUM_RESOLUTIONS][3][2] = {
107  {
108  // GR_640
109  {195, 344},
110  {306, 344},
111  {432, 344}
112  },
113  {
114  // GR_1024
115  {310, 546},
116  {486, 546},
117  {688, 546}
118  }
119 };
120 static int Objective_key_icon_coords[GR_NUM_RESOLUTIONS][3][2] = {
121  {
122  // GR_640
123  {170, 339},
124  {280, 339},
125  {406, 339}
126  },
127  {
128  // GR_1024
129  {272, 542},
130  {448, 542},
131  {650, 542}
132  }
133 };
134 
135 
136 #define NUM_GOAL_SCREEN_BUTTONS 3 // total number of buttons
137 #define GOAL_SCREEN_BUTTON_SCROLL_UP 0
138 #define GOAL_SCREEN_BUTTON_SCROLL_DOWN 1
139 #define GOAL_SCREEN_BUTTON_RETURN 2
140 
141 struct goal_list {
142  int count;
146 
147  goal_list() : count(0) {}
148  void add(mission_goal *m);
149  void set();
150  void icons_display(int y);
151 };
152 
153 struct goal_buttons {
154  char *filename;
155  int x, y;
156  int hotspot;
157  UI_BUTTON button; // because we have a class inside this struct, we need the constructor below..
158 
159  goal_buttons(char *name, int x1, int y1, int h) : filename(name), x(x1), y(y1), hotspot(h) {}
160 };
161 
162 struct goal_text {
165  const char *m_lines[MAX_GOAL_LINES];
166 
167  void init();
168  int add(const char *text = NULL);
169  void display(int n, int y);
170 };
171 
173 int Num_goals = 0; // number of goals for this mission
174 int Event_index = -1; // used by sexp code to tell what event it came from
175 bool Log_event = false;
176 bool Snapshot_all_events = false;
178 
180 mission_goal Mission_goals[MAX_GOALS]; // structure for the goals of this mission
181 static goal_text Goal_text;
182 
183 #define DIRECTIVE_SOUND_DELAY 500 // time directive success sound effect is delayed
184 #define DIRECTIVE_SPECIAL_DELAY 7000 // mark special directives as true after 7 seconds
185 
186 static int Mission_directive_sound_timestamp; // timestamp to control when directive succcess sound gets played
187 static int Mission_directive_special_timestamp; // used to specially mark a directive as true even though it's not
188 
189 const char *Goal_type_text(int n)
190 {
191  switch (n) {
192  case 0:
193  return XSTR( "Primary", 396);
194 
195  case 1:
196  return XSTR( "Secondary", 397);
197 
198  case 2:
199  return XSTR( "Bonus", 398);
200  }
201 
202  return NULL;
203 }
204 
205 static int Goal_screen_text_x;
206 static int Goal_screen_text_y;
207 static int Goal_screen_text_w;
208 static int Goal_screen_text_h;
209 static int Goal_screen_icon_x;
210 
211 static goal_list Primary_goal_list;
212 static goal_list Secondary_goal_list;
213 static goal_list Bonus_goal_list;
214 
215 static int Scroll_offset;
216 static int Goals_screen_bg_bitmap;
217 static int Goal_complete_bitmap;
218 static int Goal_incomplete_bitmap;
219 static int Goal_failed_bitmap;
220 static UI_WINDOW Goals_screen_ui_window;
221 
223 //XSTR:OFF
224  goal_buttons("MOB_00", 475, 288, 0),
225  goal_buttons("MOB_01", 475, 336, 1),
226  goal_buttons("MOB_02", 553, 409, 2),
227 //XSTR:ON
228 };
229 
231 void goal_screen_scroll_up();
233 
234 //
236 //
237 
239 {
241  list[count++] = m;
242 }
243 
245 {
246  int i;
247 
248  for (i=0; i<count; i++) {
249  line_offsets[i] = Goal_text.m_num_lines;
250  line_spans[i] = Goal_text.add(list[i]->message);
251 
252  if (i < count - 1)
253  Goal_text.add();
254  }
255 }
256 
258 {
259  int i, y, ys, bmp, font_height;
260 
261  font_height = gr_get_font_height();
262  for (i=0; i<count; i++) {
263  y = line_offsets[i] - yoff;
264 
265  bmp = -1; // initialize for safety.
266  switch (list[i]->satisfied) {
267  case GOAL_COMPLETE:
268  bmp = Goal_complete_bitmap;
269  break;
270 
271  case GOAL_INCOMPLETE:
272  bmp = Goal_incomplete_bitmap;
273  break;
274 
275  case GOAL_FAILED:
276  bmp = Goal_failed_bitmap;
277  break;
278  }
279 
280  if (bmp >= 0) {
281  bm_get_info(bmp, NULL, &ys, NULL);
282  y = Goal_screen_text_y // offset of text window on screen
283  + y * font_height // relative line position offset
284  + line_spans[i] * font_height / 2 // center of text offset
285  - ys / 2; // center of icon offest
286 
287  if ((y >= Goal_screen_text_y - ys / 2) && (y + ys <= Goal_screen_text_y + Goal_screen_text_h + ys / 2)) {
288  gr_set_bitmap(bmp);
289  gr_bitmap(Goal_screen_icon_x, y, GR_RESIZE_MENU);
290  }
291  }
292  }
293 }
294 
295 //
297 //
298 
299 // initializes the goal text struct (empties it out)
301 {
302  m_num_lines = 0;
303 }
304 
305 // Adds lines of goal text. If passed NULL (or nothing passed) a blank line is added. If
306 // the text is too long, it is automatically split into more than one line.
307 // Returns the number of lines added.
308 int goal_text::add(const char *text)
309 {
310  int max, count;
311 
312  max = MAX_GOAL_LINES - m_num_lines;
313  if (max < 1) {
314  Error(LOCATION, "Goal text space exhausted");
315  return 0;
316  }
317 
318  if (!text) {
319  m_lines[m_num_lines] = NULL;
320  m_line_sizes[m_num_lines++] = 0;
321  return 1;
322  }
323 
324  count = split_str(text, Goal_screen_text_w - Goal_screen_text_coords[gr_screen.res][GOAL_SCREEN_X_COORD] + Goal_screen_icon_xcoord[gr_screen.res], m_line_sizes + m_num_lines, m_lines + m_num_lines, max);
325  m_num_lines += count;
326  return count;
327 }
328 
329 // Display a line of goal text
330 // n = goal text line number
331 // y = y offset to draw relative to goal text area top
332 void goal_text::display(int n, int y)
333 {
334  int y1, w, h;
335  char buf[MAX_GOAL_TEXT];
336 
337  if ((n < 0) || (n >= m_num_lines) || (m_line_sizes[n] < 1))
338  return; // out of range, don't draw anything
339 
341  y += Goal_screen_text_y;
342  if (*m_lines[n] == '*') { // header line
344  strncpy(buf, m_lines[n] + 1, m_line_sizes[n] - 1);
345  buf[m_line_sizes[n] - 1] = 0;
346 
347  gr_get_string_size(&w, &h, buf);
348  y1 = y + h / 2 - 1;
349 
350  // custom_size me
351  gr_line(Goal_screen_icon_x, y1, Goal_screen_text_x - 2, y1, GR_RESIZE_MENU);
352  gr_line(Goal_screen_text_x + w + 1, y1, Goal_screen_icon_x + Goal_screen_text_w, y1, GR_RESIZE_MENU);
353 
354  } else {
356  strncpy(buf, m_lines[n], m_line_sizes[n]);
357  buf[m_line_sizes[n]] = 0;
358  }
359 
360  gr_printf_menu(Goal_screen_text_x, y, buf);
361 }
362 
363 // mission_init_goals: initializes info for goals. Called as part of mission initialization.
365 {
366  int i;
367 
368  Num_goals = 0;
369  for (i=0; i<MAX_GOALS; i++) {
370  Mission_goals[i].satisfied = GOAL_INCOMPLETE;
371  Mission_goals[i].flags = 0;
372  Mission_goals[i].team = 0;
373  }
374 
375  Num_mission_events = 0;
376  for (i=0; i<MAX_MISSION_EVENTS; i++) {
377  Mission_events[i].result = 0;
378  Mission_events[i].flags = 0;
379  Mission_events[i].count = 0;
380  Mission_events[i].satisfied_time = 0;
381  Mission_events[i].born_on_date = 0;
382  Mission_events[i].team = -1;
383  Mission_events[i].mission_log_flags = 0;
384  }
385 
387  Mission_directive_sound_timestamp = 0;
388  Mission_directive_special_timestamp = timestamp(-1); // need to make invalid right away
389 }
390 
391 // called at the end of a mission for cleanup
393 {
394  int i;
395 
396  for (i=0; i<Num_mission_events; i++) {
397  if (Mission_events[i].objective_text) {
398  vm_free(Mission_events[i].objective_text);
399  Mission_events[i].objective_text = NULL;
400  }
401  if (Mission_events[i].objective_key_text) {
402  vm_free(Mission_events[i].objective_key_text);
403  Mission_events[i].objective_key_text = NULL;
404  }
405  }
406 }
407 
408 // called once right before entering the show goals screen to do initializations.
410 {
411  int i, type, team_num=0; // JAS: I set team_num to 0 because it was used without being initialized.
412  goal_buttons *b;
413 
414  Scroll_offset = 0;
415  Primary_goal_list.count = 0;
416  Secondary_goal_list.count = 0;
417  Bonus_goal_list.count = 0;
418 
419  Goal_screen_text_x = Goal_screen_text_coords[gr_screen.res][GOAL_SCREEN_X_COORD];
420  Goal_screen_text_y = Goal_screen_text_coords[gr_screen.res][GOAL_SCREEN_Y_COORD];
421  Goal_screen_text_w = Goal_screen_text_coords[gr_screen.res][GOAL_SCREEN_W_COORD];
422  Goal_screen_text_h = Goal_screen_text_coords[gr_screen.res][GOAL_SCREEN_H_COORD];
423  Goal_screen_icon_x = Goal_screen_icon_xcoord[gr_screen.res];
424 
425  // fill up the lists so we can display the goals appropriately
426  for (i=0; i<Num_goals; i++) {
427  if (Mission_goals[i].type & INVALID_GOAL){ // don't count invalid goals here
428  continue;
429  }
430 
431  if ( (Game_mode & GM_MULTIPLAYER) && (The_mission.game_type & MISSION_TYPE_MULTI_TEAMS) && (Mission_goals[i].team != team_num) ){
432  continue;
433  }
434 
435  type = Mission_goals[i].type & GOAL_TYPE_MASK;
436  switch (type) {
437  case PRIMARY_GOAL:
438  Primary_goal_list.add(&Mission_goals[i]);
439  break;
440 
441  case SECONDARY_GOAL:
442  Secondary_goal_list.add(&Mission_goals[i]);
443  break;
444 
445  case BONUS_GOAL:
446  if (Mission_goals[i].satisfied == GOAL_COMPLETE){
447  Bonus_goal_list.add(&Mission_goals[i]);
448  }
449  break;
450 
451  default:
452  Error(LOCATION, "Unknown goal priority encountered when displaying goals in mission\n");
453  break;
454  } // end switch
455  } // end for
456 
457  Goal_text.init();
458 
459  Goal_text.add(XSTR( "*Primary Objectives", 399));
460  Goal_text.add();
461  Primary_goal_list.set();
462 
463  if (Secondary_goal_list.count) {
464  Goal_text.add();
465  Goal_text.add();
466  Goal_text.add(XSTR( "*Secondary Objectives", 400));
467  Goal_text.add();
468  Secondary_goal_list.set();
469  }
470 
471  if (Bonus_goal_list.count) {
472  Goal_text.add();
473  Goal_text.add();
474  Goal_text.add(XSTR( "*Bonus Objectives", 401));
475  Goal_text.add();
476  Bonus_goal_list.set();
477  }
478 
479  common_set_interface_palette("ObjectivesPalette"); // set the interface palette
480  Goals_screen_ui_window.create(0, 0, gr_screen.max_w_unscaled, gr_screen.max_h_unscaled, 0);
481  Goals_screen_ui_window.set_mask_bmap("Objectives-m");
482 
483  for (i=0; i<NUM_GOAL_SCREEN_BUTTONS; i++) {
484  b = &Goal_buttons[i];
485 
486  b->button.create(&Goals_screen_ui_window, "", b->x, b->y, 60, 30, (i < 2), 1);
487  // set up callback for when a mouse first goes over a button
489  b->button.set_bmaps(b->filename);
490  b->button.link_hotspot(b->hotspot);
491  }
492 
493  // set up hotkeys for buttons so we draw the correct animation frame when a key is pressed
496 
497  Goals_screen_bg_bitmap = bm_load("ObjectivesBG");
498  Goal_complete_bitmap = bm_load("ObjComp");
499  Goal_incomplete_bitmap = bm_load("ObjIncomp");
500  Goal_failed_bitmap = bm_load("ObjFail");
501 
502  if (Goals_screen_bg_bitmap < 0) {
503  Warning(LOCATION, "Could not load the background bitmap: ObjectivesBG.pcx");
504  }
505 }
506 
507 // cleanup called when exiting the show goals screen
509 {
510  if (Goals_screen_bg_bitmap >= 0)
511  bm_release(Goals_screen_bg_bitmap);
512 
513  if (Goal_complete_bitmap)
514  bm_release(Goal_complete_bitmap);
515 
516  if (Goal_incomplete_bitmap)
517  bm_release(Goal_incomplete_bitmap);
518 
519  if (Goal_failed_bitmap)
520  bm_release(Goal_failed_bitmap);
521 
522  Goals_screen_ui_window.destroy();
523  common_free_interface_palette(); // restore game palette
524  game_flush();
525 }
526 
527 // called once a frame during show goals state to process events and render the screen
528 void mission_show_goals_do_frame(float frametime)
529 {
530  int k, i, y, z;
531  int font_height = gr_get_font_height();
532 
533  k = Goals_screen_ui_window.process();
534  switch (k) {
535  case KEY_ESC:
537  break;
538 
539  case KEY_DOWN:
541  break;
542 
543  case KEY_UP:
545  break;
546 
547  default:
548  // do nothing
549  break;
550  } // end switch
551 
552  for (i=0; i<NUM_GOAL_SCREEN_BUTTONS; i++){
553  if (Goal_buttons[i].button.pressed()){
555  }
556  }
557 
558  GR_MAYBE_CLEAR_RES(Goals_screen_bg_bitmap);
559  if (Goals_screen_bg_bitmap >= 0) {
560  gr_set_bitmap(Goals_screen_bg_bitmap);
561  gr_bitmap(0, 0, GR_RESIZE_MENU);
562  }
563  Goals_screen_ui_window.draw();
564 
565  y = 0;
566  z = Scroll_offset;
567  while (y + font_height <= Goal_screen_text_h) {
568  Goal_text.display(z, y);
569  y += font_height;
570  z++;
571  }
572 
573  Primary_goal_list.icons_display(Scroll_offset);
574  Secondary_goal_list.icons_display(Scroll_offset);
575  Bonus_goal_list.icons_display(Scroll_offset);
576 
577  gr_flip();
578 }
579 
580 // Mission Log Objectives subscreen init.
581 // Called once right before entering the Mission Log screen to do initializations.
582 int ML_objectives_init(int x, int y, int w, int h)
583 {
584  int i, type, team_num;
585 
586  Primary_goal_list.count = 0;
587  Secondary_goal_list.count = 0;
588  Bonus_goal_list.count = 0;
589 
590  Goal_screen_text_x = x - Goal_screen_icon_xcoord[gr_screen.res] + Goal_screen_text_coords[gr_screen.res][GOAL_SCREEN_X_COORD];
591  Goal_screen_text_y = y;
592  Goal_screen_text_w = w;
593  Goal_screen_text_h = h;
594  Goal_screen_icon_x = x;
595 
596  team_num = 0; // this is the default team -- we will change it if in a multiplayer team v. team game
597 
599  team_num = Net_player->p_info.team;
600  }
601 
602  // fill up the lists so we can display the goals appropriately
603  for (i=0; i<Num_goals; i++) {
604  if (Mission_goals[i].type & INVALID_GOAL){ // don't count invalid goals here
605  continue;
606  }
607 
608  if ( (Game_mode & GM_MULTIPLAYER) && (The_mission.game_type & MISSION_TYPE_MULTI_TEAMS) && (Mission_goals[i].team != team_num) ){
609  continue;
610  }
611 
612  type = Mission_goals[i].type & GOAL_TYPE_MASK;
613  switch (type) {
614  case PRIMARY_GOAL:
615  Primary_goal_list.add(&Mission_goals[i]);
616  break;
617 
618  case SECONDARY_GOAL:
619  Secondary_goal_list.add(&Mission_goals[i]);
620  break;
621 
622  case BONUS_GOAL:
623  if (Mission_goals[i].satisfied == GOAL_COMPLETE){
624  Bonus_goal_list.add(&Mission_goals[i]);
625  }
626  break;
627 
628  default:
629  Error(LOCATION, "Unknown goal priority encountered when displaying goals in mission\n");
630  break;
631  } // end switch
632  } // end for
633 
634  Goal_text.init();
635 
636  Goal_text.add(XSTR( "*Primary Objectives", 399));
637  Goal_text.add();
638  Primary_goal_list.set();
639 
640  if (Secondary_goal_list.count) {
641  Goal_text.add();
642  Goal_text.add();
643  Goal_text.add(XSTR( "*Secondary Objectives", 400));
644  Goal_text.add();
645  Secondary_goal_list.set();
646  }
647 
648  if (Bonus_goal_list.count) {
649  Goal_text.add();
650  Goal_text.add();
651  Goal_text.add(XSTR( "*Bonus Objectives", 401));
652  Goal_text.add();
653  Bonus_goal_list.set();
654  }
655 
656 
657  Goal_complete_bitmap = bm_load("ObjComp");
658  Goal_incomplete_bitmap = bm_load("ObjIncomp");
659  Goal_failed_bitmap = bm_load("ObjFail");
660 
661  return Goal_text.m_num_lines;
662 }
663 
664 // cleanup called when exiting the show goals screen
666 {
667  if (Goal_complete_bitmap >= 0) {
668  bm_release(Goal_complete_bitmap);
669  }
670 
671  if (Goal_incomplete_bitmap >= 0) {
672  bm_release(Goal_incomplete_bitmap);
673  }
674 
675  if (Goal_failed_bitmap >= 0) {
676  bm_release(Goal_failed_bitmap);
677  }
678 }
679 
680 void ML_objectives_do_frame(int scroll_offset)
681 {
682  int y, z;
683  int font_height = gr_get_font_height();
684 
685  y = 0;
686  z = scroll_offset;
687  while (y + font_height <= Goal_screen_text_h) {
688  Goal_text.display(z, y);
689  y += font_height;
690  z++;
691  }
692 
693  Primary_goal_list.icons_display(scroll_offset);
694  Secondary_goal_list.icons_display(scroll_offset);
695  Bonus_goal_list.icons_display(scroll_offset);
696 }
697 
699 {
700  // display icon key at the bottom
701  if (Lcl_gr) {
702  gr_set_bitmap(Goal_complete_bitmap);
703  gr_bitmap(Objective_key_icon_coords_gr[gr_screen.res][0][0], Objective_key_icon_coords_gr[gr_screen.res][0][1], GR_RESIZE_MENU);
704  gr_set_bitmap(Goal_incomplete_bitmap);
705  gr_bitmap(Objective_key_icon_coords_gr[gr_screen.res][1][0], Objective_key_icon_coords_gr[gr_screen.res][1][1], GR_RESIZE_MENU);
706  gr_set_bitmap(Goal_failed_bitmap);
707  gr_bitmap(Objective_key_icon_coords_gr[gr_screen.res][2][0], Objective_key_icon_coords_gr[gr_screen.res][2][1], GR_RESIZE_MENU);
708 
709  gr_string(Objective_key_text_coords_gr[gr_screen.res][0][0], Objective_key_text_coords_gr[gr_screen.res][0][1] , XSTR("Complete", 1437), GR_RESIZE_MENU);
710  gr_string(Objective_key_text_coords_gr[gr_screen.res][1][0], Objective_key_text_coords_gr[gr_screen.res][1][1] , XSTR("Incomplete", 1438), GR_RESIZE_MENU);
711  gr_string(Objective_key_text_coords_gr[gr_screen.res][2][0], Objective_key_text_coords_gr[gr_screen.res][2][1] , XSTR("Failed", 1439), GR_RESIZE_MENU);
712  } else {
713  gr_set_bitmap(Goal_complete_bitmap);
714  gr_bitmap(Objective_key_icon_coords[gr_screen.res][0][0], Objective_key_icon_coords[gr_screen.res][0][1], GR_RESIZE_MENU);
715  gr_set_bitmap(Goal_incomplete_bitmap);
716  gr_bitmap(Objective_key_icon_coords[gr_screen.res][1][0], Objective_key_icon_coords[gr_screen.res][1][1], GR_RESIZE_MENU);
717  gr_set_bitmap(Goal_failed_bitmap);
718  gr_bitmap(Objective_key_icon_coords[gr_screen.res][2][0], Objective_key_icon_coords[gr_screen.res][2][1], GR_RESIZE_MENU);
719 
720  gr_string(Objective_key_text_coords[gr_screen.res][0][0], Objective_key_text_coords[gr_screen.res][0][1] , XSTR("Complete", 1437), GR_RESIZE_MENU);
721  gr_string(Objective_key_text_coords[gr_screen.res][1][0], Objective_key_text_coords[gr_screen.res][1][1] , XSTR("Incomplete", 1438), GR_RESIZE_MENU);
722  gr_string(Objective_key_text_coords[gr_screen.res][2][0], Objective_key_text_coords[gr_screen.res][2][1] , XSTR("Failed", 1439), GR_RESIZE_MENU);
723  }
724 }
725 
726 // maybe distribute the event/goal score between the players on that team
727 void multi_player_maybe_add_score (int score, int team)
728 {
729  int players_in_team = 0;
730  int idx;
731 
732  // if i'm not the server of a multiplayer game, bail here
733  if(!MULTIPLAYER_MASTER){
734  return;
735  }
736 
738  return;
739  }
740 
741  // first count the number of players in this team
742  for (idx=0; idx<MAX_PLAYERS; idx++) {
743  if (MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].p_info.team == team)) {
744  players_in_team++;
745  }
746  }
747 
748  if (!players_in_team) {
749  return;
750  }
751 
752  for (idx=0; idx<MAX_PLAYERS; idx++) {
753  if (MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].p_info.team == team)) {
754  Net_players[idx].m_player->stats.m_score += (int)(score * scoring_get_scale_factor() / players_in_team );
755  }
756  }
757 }
758 
762 void mission_goal_status_change( int goal_num, int new_status)
763 {
764  int type;
765 
766  Assert(goal_num < Num_goals);
767  Assert((new_status == GOAL_FAILED) || (new_status == GOAL_COMPLETE));
768 
769  // if in a multiplayer game, send a status change to clients
770  if ( MULTIPLAYER_MASTER ){
771  send_mission_goal_info_packet( goal_num, new_status, -1 );
772  }
773 
774  type = Mission_goals[goal_num].type & GOAL_TYPE_MASK;
775  Mission_goals[goal_num].satisfied = new_status;
776  if ( new_status == GOAL_FAILED ) {
777  // don't display bonus goal failure
778  if ( type != BONUS_GOAL ) {
779 
780  // only do HUD and music is goals are my teams goals.
781  if ( (Game_mode & GM_NORMAL) || ((Net_player != NULL) && (Net_player->p_info.team == Mission_goals[goal_num].team)) ) {
782  hud_add_objective_messsage(type, new_status);
783  if ( !( Mission_goals[goal_num].flags & MGF_NO_MUSIC ) ) { // maybe play event music
785  }
786  }
787  }
788  mission_log_add_entry( LOG_GOAL_FAILED, Mission_goals[goal_num].name, NULL, goal_num );
789  } else if ( new_status == GOAL_COMPLETE ) {
790  if ( (Game_mode & GM_NORMAL) || ((Net_player != NULL) && (Net_player->p_info.team == Mission_goals[goal_num].team))) {
791  hud_add_objective_messsage(type, new_status);
792  // cue for Event Music
793  if ( !(Mission_goals[goal_num].flags & MGF_NO_MUSIC) ) {
795  }
796  mission_log_add_entry( LOG_GOAL_SATISFIED, Mission_goals[goal_num].name, NULL, goal_num );
797  }
798 
800  multi_team_maybe_add_score(Mission_goals[goal_num].score, Mission_goals[goal_num].team);
801  multi_player_maybe_add_score(Mission_goals[goal_num].score, Mission_goals[goal_num].team);
802  } else {
803  // deal with the score
804  Player->stats.m_score += (int)(Mission_goals[goal_num].score * scoring_get_scale_factor());
805  }
806  }
807 }
808 
809 // return value:
810 // EVENT_UNBORN = event has yet to be available (not yet evaluatable)
811 // EVENT_CURRENT = current (evaluatable), but not yet true
812 // EVENT_SATISFIED = event has occured (true)
813 // EVENT_FAILED = event failed, can't possibly become true anymore
815 {
816  // check for directive special events first. We will always return from this part of the if statement
817  if ( Mission_events[event].flags & MEF_DIRECTIVE_SPECIAL ) {
818 
819  // if this event is temporarily true, return as such
820  if ( Mission_events[event].flags & MEF_DIRECTIVE_TEMP_TRUE ){
821  return EVENT_SATISFIED;
822  }
823 
824  // if the timestamp has elapsed, we can "mark" this directive as true although it's really not.
825  if ( timestamp_elapsed(Mission_directive_special_timestamp) ) {
826  Mission_events[event].satisfied_time = Missiontime;
827  Mission_events[event].flags |= MEF_DIRECTIVE_TEMP_TRUE;
828  }
829 
830  return EVENT_CURRENT;
831  } else if (Mission_events[event].flags & MEF_CURRENT) {
832  if (!Mission_events[event].born_on_date){
833  Mission_events[event].born_on_date = timestamp();
834  }
835 
836  if (Mission_events[event].result) {
837  return EVENT_SATISFIED;
838  }
839 
840  if (Mission_events[event].formula < 0) {
841  return EVENT_FAILED;
842  }
843 
844  return EVENT_CURRENT;
845  }
846 
847  return EVENT_UNBORN;
848 }
849 
851 {
852  // bogus
853  if((event < 0) || (event >= Num_mission_events)){
854  return;
855  }
856 
857  Mission_events[event].flags |= MEF_DIRECTIVE_SPECIAL;
858 
859  // start from a known state
860  Mission_events[event].flags &= ~MEF_DIRECTIVE_TEMP_TRUE;
861  Mission_directive_special_timestamp = timestamp(DIRECTIVE_SPECIAL_DELAY);
862 }
863 
865 {
866  // bogus
867  if((event < 0) || (event >= Num_mission_events)){
868  return;
869  }
870 
871  Mission_events[event].flags &= ~(MEF_DIRECTIVE_SPECIAL);
872 
873  // this event may be marked temporarily true -- if so, then unmark this value!!!
874  if ( Mission_events[event].flags & MEF_DIRECTIVE_TEMP_TRUE ){
875  Mission_events[event].flags &= ~MEF_DIRECTIVE_TEMP_TRUE;
876  }
877  Mission_events[event].satisfied_time = 0;
878  Mission_directive_special_timestamp = timestamp(-1);
879 }
880 
881 // function which evaluates and processes the given event
883 {
884  int store_flags = Mission_events[event].flags;
885  int store_formula = Mission_events[event].formula;
886  int store_result = Mission_events[event].result;
887  int store_count = Mission_events[event].count;
888 
889  int result, sindex;
890  bool bump_timestamp = false;
891  Log_event = false;
892 
893  Directive_count = 0;
894  Event_index = event;
895  sindex = Mission_events[event].formula;
896  result = Mission_events[event].result;
897 
898  // if chained, insure that previous event is true and next event is false
899  if (Mission_events[event].chain_delay >= 0) { // this indicates it's chained
900  // What everyone expected the chaining behavior to be, as specified in Karajorma's original fix to Mantis #82
902  if (event > 0){
903  if (!Mission_events[event - 1].result || ((fix) Mission_events[event - 1].satisfied_time + i2f(Mission_events[event].chain_delay) > Missiontime)){
904  sindex = -1; // bypass evaluation
905  }
906  }
907  }
908  // Volition's original chaining behavior as used in retail and demonstrated in e.g. btm-01.fsm (or btm-01.fs2 in the Port)
909  else {
910  if (event > 0){
911  if (!Mission_events[event - 1].result || ((fix) Mission_events[event - 1].timestamp + i2f(Mission_events[event].chain_delay) > Missiontime)){
912  sindex = -1; // bypass evaluation
913  }
914  }
915 
916  if ((event < Num_mission_events - 1) && Mission_events[event + 1].result && (Mission_events[event + 1].chain_delay >= 0)){
917  sindex = -1; // bypass evaluation
918  }
919  }
920  }
921 
922  if (sindex >= 0) {
923  Sexp_useful_number = 1;
924  if (Snapshot_all_events || Mission_events[event].mission_log_flags != 0) {
925  Log_event = true;
926 
930  }
931  result = eval_sexp(sindex);
932 
933  // if the directive count is a special value, deal with that first. Mark the event as a special
934  // event, and unmark it when the directive is true again.
935  if ( (Directive_count == DIRECTIVE_WING_ZERO) && !(Mission_events[event].flags & MEF_DIRECTIVE_SPECIAL) ) {
936  // make it special - which basically just means that its true until the next wave arrives
938 
939  Directive_count = 0;
940  } else if ( (Mission_events[event].flags & MEF_DIRECTIVE_SPECIAL) && Directive_count > 1 ) {
941  // make it non special
943  }
944 
945  if (Mission_events[event].count || (Directive_count > 1)){
946  Mission_events[event].count = Directive_count;
947  }
948 
949  if (Sexp_useful_number){
950  Mission_events[event].flags |= MEF_CURRENT;
951  }
952 
953  if ((Mission_events[event].mission_log_flags != 0) || Snapshot_all_events){
954  maybe_write_to_event_log(result);
955  }
956  }
957 
958  Log_event = false;
959 
960  Event_index = -1;
961  Mission_events[event].result = result;
962 
963  // if the sexpression is known false, then no need to evaluate anymore
964  if ((sindex >= 0) && (Sexp_nodes[sindex].value == SEXP_KNOWN_FALSE)) {
965  Mission_events[event].timestamp = (int) Missiontime;
966  Mission_events[event].satisfied_time = Missiontime;
967  // _argv[-1] - repeat_count of -1 would mean repeat indefinitely, so set to 0 instead.
968  Mission_events[event].repeat_count = 0;
969  Mission_events[event].formula = -1;
970  return;
971  }
972 
973  if (result && !Mission_events[event].satisfied_time) {
974  Mission_events[event].satisfied_time = Missiontime;
975  if ( Mission_events[event].objective_text ) {
976  Mission_directive_sound_timestamp = timestamp(DIRECTIVE_SOUND_DELAY);
977  }
978  }
979 
980  // decrement the trigger count. When at 0, set the repeat count to 0 so we don't eval this function anymore
981  if (result && (Mission_events[event].trigger_count != 0) && (Mission_events[event].flags & MEF_USING_TRIGGER_COUNT) ) {
982  if (Mission_events[event].trigger_count > 0)
983  Mission_events[event].trigger_count--;
984  if (Mission_events[event].trigger_count == 0) {
985  Mission_events[event].repeat_count = 0;
986  }
987  else {
988  bump_timestamp = true;
989  }
990  }
991 
992  // decrement the repeat count. When at 0, don't eval this function anymore
993  if ( result || timestamp_valid(Mission_events[event].timestamp) ) {
994  // _argv[-1] - negative repeat count means repeat indefinitely.
995  if ( Mission_events[event].repeat_count > 0 )
996  Mission_events[event].repeat_count--;
997  if ( Mission_events[event].repeat_count == 0 ) {
998  Mission_events[event].timestamp = (int)Missiontime;
999  Mission_events[event].formula = -1;
1000 
1001  if(Game_mode & GM_MULTIPLAYER){
1002  // multiplayer missions (scoring is scaled in the multi_team_maybe_add_score() function)
1003  multi_team_maybe_add_score(Mission_events[event].score, Mission_events[event].team);
1004  multi_player_maybe_add_score(Mission_events[event].score, Mission_events[event].team);
1005  } else {
1006  // deal with the player's score
1007  Player->stats.m_score += (int)(Mission_events[event].score * scoring_get_scale_factor());
1008  }
1009  }
1010  // Set the timestamp for the next check on this event unless we only have a trigger count and no repeat count and
1011  // this event didn't trigger this frame.
1012  else if (bump_timestamp || (!((Mission_events[event].repeat_count == -1) && (Mission_events[event].flags & MEF_USING_TRIGGER_COUNT) && (Mission_events[event].trigger_count != 0)))) {
1013  // set the timestamp to time out 'interval' seconds in the future.
1014  Mission_events[event].timestamp = timestamp( Mission_events[event].interval * 1000 );
1015  }
1016  }
1017 
1018  // see if anything has changed
1019  if(MULTIPLAYER_MASTER && ((store_flags != Mission_events[event].flags) || (store_formula != Mission_events[event].formula) || (store_result != Mission_events[event].result) || (store_count != Mission_events[event].count)) ){
1020  send_event_update_packet(event);
1021  }
1022 }
1023 
1024 // Maybe play a directive success sound... need to poll since the sound is delayed from when
1025 // the directive is actually satisfied.
1027 {
1028  if ( timestamp_elapsed(Mission_directive_sound_timestamp) ) {
1029  Mission_directive_sound_timestamp=0;
1031  }
1032 }
1033 
1035 {
1036  int i, result;
1037 
1038  // before checking whether or not we should evaluate goals, we should run through the events and
1039  // process any whose timestamp is valid and has expired. This would catch repeating events only
1040  for (i=0; i<Num_mission_events; i++) {
1041  if (Mission_events[i].formula != -1) {
1042  if ( !timestamp_valid(Mission_events[i].timestamp) || !timestamp_elapsed(Mission_events[i].timestamp) ){
1043  continue;
1044  }
1045 
1046  // if we get here, then the timestamp on the event has popped -- we should reevaluate
1047  PROFILE("Repeating events", mission_process_event(i));
1048  }
1049  }
1050 
1052  return;
1053  }
1054 
1055  // first evaluate the players goals
1056  for (i=0; i<Num_goals; i++) {
1057  // don't evaluate invalid goals
1058  if (Mission_goals[i].type & INVALID_GOAL){
1059  continue;
1060  }
1061 
1062  if (Mission_goals[i].satisfied == GOAL_INCOMPLETE) {
1063  result = eval_sexp(Mission_goals[i].formula);
1064  if ( Sexp_nodes[Mission_goals[i].formula].value == SEXP_KNOWN_FALSE ) {
1066 
1067  } else if (result) {
1069  } // end if result
1070 
1071  } // end if goals[i].satsified != GOAL_COMPLETE
1072  } // end for
1073 
1074  // now evaluate any mission events
1075  for (i=0; i<Num_mission_events; i++) {
1076  if ( Mission_events[i].formula != -1 ) {
1077  // only evaluate this event if the timestamp is not valid. We do this since
1078  // we will evaluate repeatable events at the top of the file so we can get
1079  // the exact interval that the designer asked for.
1080  if ( !timestamp_valid( Mission_events[i].timestamp) ){
1081  PROFILE("Nonrepeating events", mission_process_event( i ));
1082  }
1083  }
1084  }
1085 
1086  // send and remaining sexp data to the clients
1087  if (MULTIPLAYER_MASTER) {
1089  }
1090 
1093  } else {
1095  }
1096 
1099  }
1100 
1101  // update goal status if playing on a multiplayer standalone server
1104  }
1105 
1106  Snapshot_all_events = false;
1107 }
1108 
1109 // evaluate_primary_goals() will determine if the primary goals for a mission are complete
1110 //
1111 // returns 1 - all primary goals are all complete or imcomplete (or there are no primary goals at all)
1112 // returns 0 - not all primary goals are complete
1114 {
1115  int i, primary_goals_complete = PRIMARY_GOALS_COMPLETE;
1116 
1117  for (i=0; i<Num_goals; i++) {
1118 
1119  if ( Mission_goals[i].type & INVALID_GOAL ) {
1120  continue;
1121  }
1122 
1123  if ( (Mission_goals[i].type & GOAL_TYPE_MASK) == PRIMARY_GOAL ) {
1124  if ( Mission_goals[i].satisfied == GOAL_INCOMPLETE ) {
1125  return PRIMARY_GOALS_INCOMPLETE;
1126  } else if ( Mission_goals[i].satisfied == GOAL_FAILED ) {
1127  primary_goals_complete = PRIMARY_GOALS_FAILED;
1128  }
1129  }
1130  } // end for
1131 
1132  return primary_goals_complete;
1133 }
1134 
1135 // return 1 if all primary/secondary goals are complete... otherwise return 0
1137 {
1138  int i, all_goals_met = 1;
1139 
1140  for (i=0; i<Num_goals; i++) {
1141 
1142  if ( Mission_goals[i].type & INVALID_GOAL ) {
1143  continue;
1144  }
1145 
1146  if ( ((Mission_goals[i].type & GOAL_TYPE_MASK) == PRIMARY_GOAL) || ((Mission_goals[i].type & GOAL_TYPE_MASK) == SECONDARY_GOAL) ) {
1147  if ( Mission_goals[i].satisfied == GOAL_INCOMPLETE ) {
1148  all_goals_met = 0;
1149  break;
1150  } else if ( Mission_goals[i].satisfied == GOAL_FAILED ) {
1151  all_goals_met = 0;
1152  break;
1153  }
1154  }
1155  } // end for
1156 
1157  return all_goals_met;
1158 }
1159 
1160 // function used to actually change the status (valid/invalid) of a goal. Called externally
1161 // with multiplayer code
1162 void mission_goal_validation_change( int goal_num, int valid )
1163 {
1164  // only incomplete goals can have their status changed
1165  if ( Mission_goals[goal_num].satisfied != GOAL_INCOMPLETE ){
1166  return;
1167  }
1168 
1169  // if in multiplayer, then send a packet
1170  if ( MULTIPLAYER_MASTER ){
1171  send_mission_goal_info_packet( goal_num, -1, valid );
1172  }
1173 
1174  // change the valid status
1175  if ( valid ){
1176  Mission_goals[goal_num].type &= ~INVALID_GOAL;
1177  } else {
1178  Mission_goals[goal_num].type |= INVALID_GOAL;
1179  }
1180 }
1181 
1182  // the following function marks a goal invalid. It can only mark the goal invalid if the goal
1183 // is not complete. The name passed into this funciton should match the name field in the goal
1184 // structure
1186 {
1187  int i;
1188 
1189  for (i=0; i<Num_goals; i++) {
1190  if ( !stricmp(Mission_goals[i].name, name) ) {
1192  return;
1193  }
1194  }
1195 }
1196 
1197 // the next function marks a goal as valid. A goal may always be marked valid.
1199 {
1200  int i;
1201 
1202  for (i=0; i<Num_goals; i++) {
1203  if ( !stricmp(Mission_goals[i].name, name) ) {
1205  return;
1206  }
1207  }
1208 }
1209 
1210 // function to fail all mission goals. Don't fail events here since this funciton is currently
1211 // called in mission when something bad happens to the player (like he makes too many shots on friendlies).
1212 // Events can still happen. Things will just be really bad for the player.
1214 {
1215  int i;
1216 
1217  for (i=0; i<Num_goals; i++) {
1218  Mission_goals[i].satisfied = GOAL_FAILED;
1219  mission_log_add_entry( LOG_GOAL_FAILED, Mission_goals[i].name, NULL, i );
1220  }
1221 }
1222 
1223 // function to mark all incomplete goals as failed. Happens at the end of a mission
1224 // mark the events which are not currently satisfied as failed as well.
1226 {
1227  int i;
1228 
1229  for (i = 0; i < Num_goals; i++ ) {
1230  if ( Mission_goals[i].satisfied == GOAL_INCOMPLETE ) {
1231  Mission_goals[i].satisfied = GOAL_FAILED;
1232  mission_log_add_entry( LOG_GOAL_FAILED, Mission_goals[i].name, NULL, i );
1233  }
1234  }
1235 
1236  // now for the events. Must set the formula to -1 and the result to 0 to be a failed
1237  // event.
1238  for ( i = 0; i < Num_mission_events; i++ ) {
1239  if ( Mission_events[i].formula != -1 ) {
1240  Mission_events[i].formula = -1;
1241  Mission_events[i].result = 0;
1242  }
1243  }
1244 }
1245 
1246 // small function used to mark all objectives as true. Used as a debug function and as a way
1247 // to skip past training misisons
1249 {
1250  int i;
1251 
1252  for (i = 0; i < Num_goals; i++ ) {
1253  Mission_goals[i].satisfied = GOAL_COMPLETE;
1254  }
1255 }
1256 
1257 // small function used to mark all events as completed. Used in the skipping of missions.
1259 {
1260  int i;
1261 
1262  for (i = 0; i < Num_mission_events; i++ ) {
1263  Mission_events[i].result = 1;
1264  Mission_events[i].formula = -1;
1265  }
1266 }
1267 
1268 // some debug console functions to help list and change the status of mission goals
1269 DCF(show_mission_goals,"Lists the status of mission goals")
1270 {
1271  int i, type;
1272 
1273  if (dc_optional_string_either("help", "--help")) {
1274  dc_printf("Usage: show_mission_goals\n\nList all mission goals and their current status.\n");
1275  return;
1276  }
1277 
1278  if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
1279  // Don't do anything, but advance the parser past the flag
1280  }
1281 
1282  for (i=0; i<Num_goals; i++) {
1283  type = Mission_goals[i].type & GOAL_TYPE_MASK;
1284  dc_printf("%2d. %32s(%10s) -- ", i, Mission_goals[i].name, Goal_type_text(type));
1285  if ( Mission_goals[i].satisfied == GOAL_COMPLETE )
1286  dc_printf("satisfied.\n");
1287  else if ( Mission_goals[i].satisfied == GOAL_INCOMPLETE )
1288  dc_printf("unsatisfied\n");
1289  else if ( Mission_goals[i].satisfied == GOAL_FAILED )
1290  dc_printf("failed\n");
1291  else
1292  dc_printf("Warning! Mission goal %i is in an invalid state! (value: %i)", i, Mission_goals[i].satisfied);
1293  }
1294 
1295 }
1296 
1297 //XSTR:OFF
1298 DCF(change_mission_goal, "Changes the mission goal status")
1299 {
1300  int num;
1301  bool val_b;
1302  char *string;
1303 
1304  if (dc_optional_string_either("help", "--help")) {
1305  dc_printf("Usage: change_mission_goal <goal_num> [status]\n");
1306  dc_printf("<goal_num> -- Integer number of goal to change. See show_mission_goals\n");
1307  dc_printf("[status] -- Goal status to change to.\n\n");
1308 
1309  dc_printf("The optional [status] field may be either a bool type or a string.\n");
1310  dc_printf("\ttrue -- Goal status set to 'complete'\n");
1311  dc_printf("\tfalse -- Goal status set to 'failed'\n\n");
1312 
1313  dc_printf("A string value of 'satisfied', 'failed', or 'unknown' will set the goal status to the respective state.\n");
1314  dc_printf("If [status] is not given, then the goal status will be set to 'unknown'");
1315 
1316  dc_printf("Examples:\n");
1317  dc_printf("\t'change_mission_goal 1 true' makes goal 1 as successful.\n");
1318  dc_printf("\t'change_mission_goal 2' marks goal 2 as unknown/incomplete\n");
1319  dc_printf("\t'change_mission_goal 0 satisfied' marks goal 0 as satisfied\n");
1320  return;
1321  }
1322 
1323  dc_stuff_int(&num);
1324  if ( num >= Num_goals ) {
1325  dc_printf (" Error: Invalid value for <goal_num>. Valid values: 0 - %i\n", Num_goals);
1326  return;
1327  }
1328 
1329  if (dc_optional_string("satisfied")) {
1330  Mission_goals[num].satisfied = GOAL_COMPLETE;
1331 
1332  } else if (dc_optional_string("failed")) {
1333  Mission_goals[num].satisfied = GOAL_FAILED;
1334 
1335  } else if (dc_optional_string("unsatisfied")) {
1336  Mission_goals[num].satisfied = GOAL_INCOMPLETE;
1337 
1338  } else if (dc_maybe_stuff_boolean(&val_b)) {
1339  val_b ? Mission_goals[num].satisfied = GOAL_COMPLETE : Mission_goals[num].satisfied = GOAL_FAILED;
1340 
1341  } else {
1342  // No argument given
1343  Mission_goals[num].satisfied = GOAL_INCOMPLETE;
1344  }
1345 
1346  switch(Mission_goals[num].satisfied) {
1347  case GOAL_COMPLETE:
1348  string = "satisfied";
1349  break;
1350 
1351  case GOAL_FAILED:
1352  string = "failed";
1353  break;
1354 
1355  case GOAL_INCOMPLETE:
1356  string = "unsatisfied";
1357  break;
1358 
1359  default:
1360  dc_printf("Warning! Mission goal %i is in an invalid state! (value: %i)", num, Mission_goals[num].satisfied);
1361  return;
1362  }
1363  dc_printf("Mission goal %i set to '%s'\n", num, string);
1364 }
1365 //XSTR:ON
1366 
1367 // debug functions to mark all primary/secondary/bonus goals as true
1368 #ifndef DEBUG
1369 
1371 {
1372  int i;
1373 
1374  for (i = 0; i < Num_goals; i++ ) {
1375  if ( (Mission_goals[i].type & GOAL_TYPE_MASK) == type )
1376  Mission_goals[i].satisfied = GOAL_COMPLETE;
1377  }
1378 
1379  HUD_sourced_printf(HUD_SOURCE_HIDDEN, NOX("All %s goals marked true"), Goal_type_text(type) );
1380 }
1381 
1382 #endif
1383 
1385 {
1386  switch (num) {
1389  break;
1390 
1393  break;
1394 
1397  break;
1398  }
1399 }
1400 
1402 {
1403  if (Scroll_offset) {
1404  Scroll_offset--;
1406  } else {
1408  }
1409 }
1410 
1412 {
1413  int max_lines;
1414 
1415  max_lines = Goal_screen_text_h / gr_get_font_height();
1416  if (Scroll_offset + max_lines < Goal_text.m_num_lines) {
1417  Scroll_offset++;
1419  } else {
1421  }
1422 }
1423 
1424 // Return the number of resolved goals in num_resolved, return the total
1425 // number of valid goals in total
1426 void mission_goal_fetch_num_resolved(int desired_type, int *num_resolved, int *total, int team)
1427 {
1428  int i,type;
1429 
1430  *num_resolved=0;
1431  *total=0;
1432 
1433  for (i=0; i<Num_goals; i++) {
1434  // if we're checking for team
1435  if((team >= 0) && (Mission_goals[i].team != team)){
1436  continue;
1437  }
1438 
1439  if (Mission_goals[i].type & INVALID_GOAL) {
1440  continue;
1441  }
1442 
1443  type = Mission_goals[i].type & GOAL_TYPE_MASK;
1444  if ( type != desired_type ) {
1445  continue;
1446  }
1447 
1448  *total = *total + 1;
1449 
1450  if (Mission_goals[i].satisfied != GOAL_INCOMPLETE) {
1451  *num_resolved = *num_resolved + 1;
1452  }
1453  }
1454 }
1455 
1456 // Return whether there are any incomplete goals of the specified type
1457 int mission_goals_incomplete(int desired_type, int team)
1458 {
1459  int i, type;
1460 
1461  for (i=0; i<Num_goals; i++) {
1462  // if we're checking for team
1463  if((team >= 0) && (Mission_goals[i].team != team)){
1464  continue;
1465  }
1466 
1467  if (Mission_goals[i].type & INVALID_GOAL) {
1468  continue;
1469  }
1470 
1471  type = Mission_goals[i].type & GOAL_TYPE_MASK;
1472  if ( type != desired_type ) {
1473  continue;
1474  }
1475 
1476  if (Mission_goals[i].satisfied == GOAL_INCOMPLETE) {
1477  return 1;
1478  }
1479  }
1480 
1481  return 0;
1482 }
1483 
1485 {
1488 }
void game_flush()
Definition: fredstubs.cpp:83
GLuint64EXT * result
Definition: Glext.h:10775
bool Log_event
void set_highlight_action(void(*_user_function)(void))
Definition: button.cpp:375
int timestamp(int delta_ms)
Definition: timer.cpp:226
int i
Definition: multi_pxo.cpp:466
fix Missiontime
Definition: systemvars.cpp:19
#define vm_free(ptr)
Definition: pstypes.h:548
#define MAX_GOALS_PER_LIST
void mission_eval_goals()
#define BONUS_GOAL
Definition: missiongoals.h:30
void icons_display(int y)
int Num_mission_events
#define KEY_DOWN
Definition: key.h:180
int m_line_sizes[MAX_GOAL_LINES]
void mission_log_add_entry(int type, char *pname, char *sname, int info_index)
Definition: missionlog.cpp:184
int Game_mode
Definition: systemvars.cpp:24
void mission_goal_mark_valid(char *name)
int ML_objectives_init(int x, int y, int w, int h)
GLfloat GLfloat GLfloat GLfloat h
Definition: Glext.h:7280
int game_type
Definition: missionparse.h:138
void gr_flip()
Definition: 2d.cpp:2113
SCP_vector< SCP_string > * Current_event_log_buffer
Definition: sexp.cpp:934
int mission_goals_met()
#define EVENT_SATISFIED
Definition: missiongoals.h:78
net_player * Net_player
Definition: multi.cpp:94
#define GR_RESIZE_MENU
Definition: 2d.h:684
SCP_vector< game_snd > Snds
Definition: gamesnd.cpp:19
void mission_goal_exit()
void mission_goal_fetch_num_resolved(int desired_type, int *num_resolved, int *total, int team)
GLsizei const GLfloat * value
Definition: Glext.h:5646
SCP_vector< SCP_string > * Current_event_log_variable_buffer
Definition: sexp.cpp:935
#define MEF_DIRECTIVE_TEMP_TRUE
Definition: missiongoals.h:84
player * m_player
Definition: multi.h:459
bool Alternate_chaining_behavior
Definition: mod_table.cpp:23
bool Snapshot_all_events
#define LOG_GOAL_FAILED
Definition: missionlog.h:34
#define EVENT_UNBORN
Definition: missiongoals.h:76
void _cdecl void void _cdecl void _cdecl Warning(char *filename, int line, SCP_FORMAT_STRING const char *format,...) SCP_FORMAT_STRING_ARGS(3
Assert(pm!=NULL)
void goal_screen_scroll_down()
void goal_screen_button_pressed(int num)
#define GR_NUM_RESOLUTIONS
Definition: 2d.h:651
int line_spans[MAX_GOALS_PER_LIST]
#define EVENT_CURRENT
Definition: missiongoals.h:77
__inline void gr_string(int x, int y, const char *string, int resize_mode=GR_RESIZE_FULL)
Definition: 2d.h:769
#define DIRECTIVE_WING_ZERO
Definition: sexp.h:1073
void add(mission_goal *m)
goal_list structure functions
general failure sound for any event
Definition: gamesnd.h:297
int res
Definition: 2d.h:370
int max_h_unscaled
Definition: 2d.h:361
int bm_get_info(int handle, int *w, int *h, ubyte *flags, int *nframes, int *fps)
Gets info on the bitmap indexed by handle.
Definition: bmpman.cpp:769
int line_offsets[MAX_GOALS_PER_LIST]
void display(int n, int y)
int hud_gauge_active(int gauge_index)
Determine if the specified HUD gauge should be displayed.
Definition: hud.cpp:3006
CButton * team
void mission_goal_validation_change(int goal_num, int valid)
SCP_vector< SCP_string > * Current_event_log_argument_buffer
Definition: sexp.cpp:936
void ML_render_objectives_key()
void mission_goal_fail_all()
#define PRIMARY_GOAL
Definition: missiongoals.h:28
SCP_vector< SCP_string > event_log_argument_buffer
Definition: missiongoals.h:123
void mission_goal_mark_all_true(int type)
void multi_team_maybe_add_score(int points, int team)
Definition: multi_team.cpp:105
#define GR_MAYBE_CLEAR_RES(bmap)
Definition: 2d.h:639
#define MEF_DIRECTIVE_SPECIAL
Definition: missiongoals.h:83
int Mission_goal_timestamp
GLenum GLuint GLenum GLsizei const GLchar * message
Definition: Glext.h:5156
void mission_maybe_play_directive_success_sound()
void gr_set_color_fast(color *dst)
Definition: 2d.cpp:1197
Definition: ui.h:195
void gr_set_bitmap(int bitmap_num, int alphablend_mode, int bitblt_mode, float alpha)
Definition: 2d.cpp:2105
SCP_vector< SCP_string > event_log_buffer
Definition: missiongoals.h:121
#define HUD_DIRECTIVES_VIEW
Definition: hudgauges.h:36
int max_w_unscaled
Definition: 2d.h:361
void HUD_sourced_printf(int source, const char *format,...)
Definition: hudmessage.cpp:571
int mission_goals_incomplete(int desired_type, int team)
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition: Glext.h:7308
void multi_sexp_flush_packet()
Definition: multi_sexp.cpp:215
DCF(show_mission_goals,"Lists the status of mission goals")
mission_goal Mission_goals[MAX_GOALS]
int bm_release(int handle, int clear_render_targets)
Frees both a bitmap's data and it's associated slot.
Definition: bmpman.cpp:2603
void mission_init_goals()
#define PROFILE(name, function)
Definition: systemvars.h:251
#define GOAL_TIMESTAMP
SCP_vector< SCP_string > event_log_variable_buffer
Definition: missiongoals.h:122
int mission_evaluate_primary_goals()
void mission_event_shutdown()
GLenum type
Definition: Gl.h:1492
char * objective_text
Definition: missiongoals.h:112
void common_set_interface_palette(char *filename)
int Directive_count
Definition: sexp.cpp:826
#define INVALID_GOAL
Definition: missiongoals.h:33
void send_event_update_packet(int event)
Definition: multimsgs.cpp:8054
void destroy()
Definition: window.cpp:189
void send_mission_goal_info_packet(int goal_num, int new_status, int valid)
Definition: multimsgs.cpp:5574
typedef int(SCP_EXT_CALLCONV *SCPDLL_PFVERSION)(SCPDLL_Version *)
void set_mask_bmap(char *fname)
Definition: window.cpp:75
int set_bmaps(char *ani_filename, int nframes=3, int start_frame=1)
Definition: gadget.cpp:71
net_player_info p_info
Definition: multi.h:473
void common_free_interface_palette()
void mission_goal_mark_objectives_complete()
void mission_goal_status_change(int goal_num, int new_status)
int add(const char *text=NULL)
void ML_objectives_close()
#define LOG_GOAL_SATISFIED
Definition: missionlog.h:33
#define GM_MULTIPLAYER
Definition: systemvars.h:18
void hud_add_objective_messsage(int type, int status)
Add objective status on the HUD.
Definition: hud.cpp:3230
#define MAX_GOALS
Definition: missiongoals.h:23
ai_profile_t * ai_profile
Definition: missionparse.h:168
mission_goal * list[MAX_GOALS_PER_LIST]
#define GOAL_SCREEN_BUTTON_RETURN
#define DIRECTIVE_SPECIAL_DELAY
void mission_event_set_directive_special(int event)
mission_event Mission_events[MAX_MISSION_EVENTS]
void mission_process_event(int event)
#define w(p)
Definition: modelsinc.h:68
GLdouble GLdouble z
Definition: Glext.h:5451
int Sexp_useful_number
Definition: sexp.cpp:827
#define HUD_SOURCE_HIDDEN
Definition: hudmessage.h:23
int snd_play(game_snd *gs, float pan, float vol_scale, int priority, bool is_voice_msg)
Definition: sound.cpp:517
float scoring_get_scale_factor()
Definition: scoring.cpp:1447
bool dc_maybe_stuff_boolean(bool *b)
Tries to stuff a bool from the Command_string.
GLsizei const GLchar ** string
Definition: Glext.h:5636
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
#define MAX_PLAYERS
Definition: pstypes.h:32
#define DIRECTIVE_SOUND_DELAY
bool dc_optional_string_either(const char *str1, const char *str2)
Searches for an optional string and it's alias.
#define GOAL_SCREEN_Y_COORD
int idx
Definition: multiui.cpp:761
sexp_node * Sexp_nodes
Definition: sexp.cpp:844
#define GOAL_SCREEN_H_COORD
#define MGF_NO_MUSIC
Definition: missiongoals.h:52
void maybe_write_to_event_log(int result)
Definition: sexp.cpp:22769
UI_BUTTON button
GLint GLint GLint GLint GLint x
Definition: Glext.h:5182
long fix
Definition: pstypes.h:54
GLclampd n
Definition: Glext.h:7286
SCP_vector< game_snd > Snds_iface
Definition: gamesnd.cpp:20
int hud_disabled()
Checks if HUD disabled.
Definition: hud.cpp:1306
#define GOAL_TYPE_MASK
Definition: missiongoals.h:34
void mission_event_unset_directive_special(int event)
const char * XSTR(const char *str, int index)
Definition: localize.cpp:851
void mission_show_goals_do_frame(float frametime)
#define PRIMARY_GOALS_FAILED
Definition: missiongoals.h:44
#define SECONDARY_GOAL
Definition: missiongoals.h:29
user_click (mouse selects a control)
Definition: gamesnd.h:305
void mission_goal_mark_invalid(char *name)
#define NOX(s)
Definition: pstypes.h:473
#define MEF_USING_TRIGGER_COUNT
Definition: missiongoals.h:85
#define MAX_GOAL_TEXT
Definition: missiongoals.h:50
void _cdecl void void _cdecl Error(const char *filename, int line, SCP_FORMAT_STRING const char *format,...) SCP_FORMAT_STRING_ARGS(3
#define GM_STANDALONE_SERVER
Definition: systemvars.h:27
GLbitfield flags
Definition: Glext.h:6722
void set_hotkey(int keycode)
Definition: gadget.cpp:280
#define GOAL_INCOMPLETE
Definition: missiongoals.h:40
void dc_stuff_int(int *i)
Stuffs an int to the given variable. Supports binary (0b), hexadecimal (0x), and octal (0o) formats...
GLuint const GLchar * name
Definition: Glext.h:5608
__inline void gr_line(int x1, int y1, int x2, int y2, int resize_mode=GR_RESIZE_FULL)
Definition: 2d.h:791
#define AIPF_ALLOW_MULTI_EVENT_SCORING
Definition: ai_profiles.h:39
void mission_goal_fail_incomplete()
#define GOAL_SCREEN_X_COORD
void goal_screen_scroll_up()
#define MISSION_TYPE_TRAINING
Definition: missionparse.h:63
int bm_load(const char *real_filename)
Loads a bitmap so we can draw with it later.
Definition: bmpman.cpp:1119
GLboolean GLboolean GLboolean b
Definition: Glext.h:5781
GLuint GLuint num
Definition: Glext.h:9089
#define EVENT_FAILED
Definition: missiongoals.h:79
scroll pressed (and scroll)
Definition: gamesnd.h:296
void link_hotspot(int num)
Definition: gadget.cpp:50
void create(UI_WINDOW *wnd, char *_text, int _x, int _y, int _w, int _h, int do_repeat=0, int ignore_focus=0)
Definition: button.cpp:26
#define KEY_ESC
Definition: key.h:124
GLubyte GLubyte GLubyte GLubyte w
Definition: Glext.h:5679
#define GOAL_TIMESTAMP_TRAINING
#define MULTI_CONNECTED(np)
Definition: multi.h:136
void create(int _x, int _y, int _w, int _h, int _flags, int _f_id=-1)
Definition: window.cpp:140
int eval_sexp(int cur_node, int referenced_node)
Definition: sexp.cpp:22894
player * Player
Definition: ui.h:584
screen gr_screen
Definition: 2d.cpp:46
goal_buttons(char *name, int x1, int y1, int h)
void gr_get_string_size(int *w, int *h, const char *text, int len=9999)
Definition: font.cpp:196
directive complete
Definition: gamesnd.h:125
int event_music_primary_goal_failed()
Definition: eventmusic.cpp:973
#define GOAL_SCREEN_BUTTON_SCROLL_DOWN
int gr_get_font_height()
Definition: font.cpp:187
#define MULTIPLAYER_MASTER
Definition: multi.h:130
int Event_index
void std_multi_update_goals()
An overhauled/updated debug console to allow monitoring, testing, and general debugging of new featur...
struct _cl_event * event
Definition: Glext.h:7296
#define LOCATION
Definition: pstypes.h:245
void common_play_highlight_sound()
Definition: gamesnd.cpp:1151
#define timestamp_elapsed(stamp)
Definition: timer.h:102
#define GOAL_SCREEN_W_COORD
#define MEF_CURRENT
Definition: missiongoals.h:82
char * objective_key_text
Definition: missiongoals.h:113
const GLfloat * m
Definition: Glext.h:10319
void ML_objectives_do_frame(int scroll_offset)
#define MAX_MISSION_EVENTS
Definition: missiongoals.h:71
const char * Goal_type_text(int n)
GLint GLsizei count
Definition: Gl.h:1491
void gr_bitmap(int _x, int _y, int resize_mode)
Definition: 2d.cpp:1303
#define KEY_UP
Definition: key.h:179
#define i2f(a)
Definition: fix.h:23
bool dc_optional_string(const char *pstr)
Searches for an optional string.
color Color_text_normal
Definition: alphacolors.cpp:26
void dc_printf(const char *format,...)
Prints the given char string to the debug console.
Definition: console.cpp:358
color Color_text_heading
Definition: alphacolors.cpp:28
void mission_goal_mark_events_complete()
#define NUM_GOAL_SCREEN_BUTTONS
#define GM_NORMAL
Definition: systemvars.h:19
#define SEXP_KNOWN_FALSE
Definition: sexp.h:921
void gamesnd_play_iface(int n)
Definition: gamesnd.cpp:260
mission The_mission
#define GOAL_COMPLETE
Definition: missiongoals.h:39
void multi_player_maybe_add_score(int score, int team)
void draw()
Definition: window.cpp:220
int Num_goals
goal_buttons Goal_buttons[NUM_GOAL_SCREEN_BUTTONS]
int process(int key_in=-1, int process_mouse=1)
Definition: window.cpp:401
void init()
goal_text structure functions
int event_music_primary_goals_met()
net_player Net_players[MAX_PLAYERS]
Definition: multi.cpp:93
void _cdecl gr_printf_menu(int x, int y, const char *format,...)
Definition: font.cpp:314
void gameseq_post_event(int event)
#define stricmp(s1, s2)
Definition: config.h:271
#define timestamp_valid(stamp)
Definition: timer.h:104
#define PRIMARY_GOALS_COMPLETE
Definition: missiongoals.h:42
int Lcl_gr
Definition: localize.cpp:48
#define MAX_GOAL_LINES
#define PRIMARY_GOALS_INCOMPLETE
Definition: missiongoals.h:43
GLint y
Definition: Gl.h:1505
void mission_show_goals_init()
int mission_get_event_status(int event)
const char * m_lines[MAX_GOAL_LINES]
scoring_struct stats
Definition: player.h:127
#define GOAL_SCREEN_BUTTON_SCROLL_UP
#define GOAL_FAILED
Definition: missiongoals.h:38
#define MISSION_TYPE_MULTI_TEAMS
Definition: missionparse.h:65
void mission_show_goals_close()