]> icculus.org git repositories - taylor/freespace2.git/blob - src/mission/missiongoals.cpp
makefile: disable stand_gui for now.
[taylor/freespace2.git] / src / mission / missiongoals.cpp
1 /*
2  * $Logfile: /Freespace2/code/Mission/MissionGoals.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * Module for working with Mission goals
8  *
9  * $Log$
10  * Revision 1.2  2002/05/26 22:06:17  relnev
11  * makefile: disable stand_gui for now.
12  *
13  * rest: staticize some globals
14  *
15  * Revision 1.1.1.1  2002/05/03 03:28:09  root
16  * Initial import.
17  *
18  * 
19  * 15    10/27/99 5:22p Jefff
20  * Some coord changes for german ver
21  * 
22  * 14    9/06/99 9:46p Jefff
23  * skip mission support
24  * 
25  * 13    8/28/99 4:54p Dave
26  * Fixed directives display for multiplayer clients for wings with
27  * multiple waves. Fixed hud threat indicator rendering color.
28  * 
29  * 12    8/26/99 8:51p Dave
30  * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
31  * 
32  * 11    8/03/99 6:21p Jefff
33  * fixed stupid bug with objectives screen key
34  * 
35  * 10    7/29/99 2:58p Jefff
36  * Ingame objective screen icon key now uses normal objective icons and
37  * text is drawn in code.
38  * 
39  * 9     7/24/99 4:19p Dave
40  * Fixed dumb code with briefing bitmaps. Made d3d zbuffer work much
41  * better. Made model code use zbuffer more intelligently.
42  * 
43  * 8     7/10/99 1:44p Andsager
44  * Modified directives listing so that current and recently
45  * satisfied/failed directives stay on screen.
46  * 
47  * 7     2/17/99 2:10p Dave
48  * First full run of squad war. All freespace and tracker side stuff
49  * works.
50  * 
51  * 6     2/02/99 4:35p Neilk
52  * fixed coordinate problem where primary goals was on top of interface in
53  * mission briefing
54  * 
55  * 5     1/30/99 5:08p Dave
56  * More new hi-res stuff.Support for nice D3D textures.
57  * 
58  * 4     11/05/98 5:55p Dave
59  * Big pass at reducing #includes
60  * 
61  * 3     10/13/98 9:28a Dave
62  * Started neatening up freespace.h. Many variables renamed and
63  * reorganized. Added AlphaColors.[h,cpp]
64  * 
65  * 2     10/07/98 10:53a Dave
66  * Initial checkin.
67  * 
68  * 1     10/07/98 10:49a Dave
69  * 
70  * 127   9/15/98 11:44a Dave
71  * Renamed builtin ships and wepaons appropriately in FRED. Put in scoring
72  * scale factors. Fixed standalone filtering of MD missions to non-MD
73  * hosts.
74  * 
75  * 126   6/09/98 10:31a Hoffoss
76  * Created index numbers for all xstr() references.  Any new xstr() stuff
77  * added from here on out should be added to the end if the list.  The
78  * current list count can be found in FreeSpace.cpp (search for
79  * XSTR_SIZE).
80  * 
81  * 125   5/21/98 2:47a Lawrance
82  * Fix some problems with event music
83  * 
84  * 124   4/15/98 9:05a Allender
85  * fix skpping of training mission with branchs
86  * 
87  * 123   4/08/98 10:34p Allender
88  * make threat indicators work in multiplayer.  Fix socket problem (once
89  * and for all???)
90  * 
91  * 122   4/03/98 2:47p Allender
92  * made directives act different when multiple waves of a wing take a long
93  * time to reappear
94  * 
95  * 121   4/01/98 9:21p John
96  * Made NDEBUG, optimized build with no warnings or errors.
97  * 
98  * 120   3/31/98 5:18p John
99  * Removed demo/save/restore.  Made NDEBUG defined compile.  Removed a
100  * bunch of debug stuff out of player file.  Made model code be able to
101  * unload models and malloc out only however many models are needed.
102  *  
103  * 
104  * 119   3/31/98 4:42p Allender
105  * mission objective support for team v. team mode.  Chatbox changes to
106  * make input box be correct length when typing
107  * 
108  * 118   3/23/98 1:39p Hoffoss
109  * Fixed bug where long objects weren't being split at the right point.
110  * 
111  * 117   3/17/98 12:40a Lawrance
112  * Add missing struct members to state
113  * 
114  * 116   3/09/98 4:24p Lawrance
115  * Don't play directive sound effect if gauge is disabled
116  * 
117  * 115   3/04/98 5:51p Hoffoss
118  * Fixed warning
119  * 
120  * 114   3/02/98 9:30p Allender
121  * make sexpression evaluation for arrivals/departures and goals happen
122  * every frame
123  * 
124  * 113   3/02/98 10:08a Hoffoss
125  * Fixed bugs in saving/restoring state.
126  * 
127  * 112   2/27/98 4:37p Hoffoss
128  * Combined Objectives screen into Mission Log screen.
129  * 
130  * 111   2/26/98 10:07p Hoffoss
131  * Rewrote state saving and restoring to fix bugs and simplify the code.
132  * 
133  * 110   2/23/98 3:06p Hoffoss
134  * Made objectives with only one target not show the [1] thing after it.
135  * 
136  * 109   2/22/98 4:30p John
137  * More string externalization classification
138  * 
139  * 108   2/22/98 12:19p John
140  * Externalized some strings
141  * 
142  * 107   2/20/98 8:33p Lawrance
143  * Added mission_goals_incomplete()
144  * 
145  * 106   2/06/98 12:12a Lawrance
146  * Play sound effect when continue is pressed.
147  * 
148  * 105   2/05/98 10:14p Lawrance
149  * Implement Save and Quit
150  * 
151  * 104   2/04/98 4:32p Allender
152  * support for multiple briefings and debriefings.  Changes to mission
153  * type (now a bitfield).  Bitfield defs for multiplayer modes
154  * 
155  * 103   1/30/98 4:24p Hoffoss
156  * Added a 3 second delay for directives before they get displayed.
157  * 
158  * 102   1/29/98 10:26a Hoffoss
159  * Made changes so arrow buttons repeat scrolling when held down.
160  * 
161  * 101   1/28/98 6:19p Dave
162  * Reduced standalone memory usage ~8 megs. Put in support for handling
163  * multiplayer submenu handling for endgame, etc.
164  * 
165  * 100   1/27/98 11:00a Lawrance
166  * Fix bug with showing number of resolved goals in the objective status
167  * popup.
168  * 
169  * 99    1/27/98 10:56a Allender
170  * slight changes to scoring stuff.  Added work for Baranec :-)
171  * 
172  * 98    1/26/98 10:02p Allender
173  * some scoring stuff
174  * 
175  * 97    1/20/98 2:26p Hoffoss
176  * Removed references to timestamp_ticker, used timestamp() instead.
177  * 
178  * 96    1/19/98 9:37p Allender
179  * Great Compiler Warning Purge of Jan, 1998.  Used pragma's in a couple
180  * of places since I was unsure of what to do with code.
181  * 
182  * 95    1/15/98 5:23p Lawrance
183  * Add HUD gauge to indicate completed objectives.
184  * 
185  * 94    1/15/98 1:29p Hoffoss
186  * Made chained events only check next event if that event is also
187  * chained.
188  * 
189  * 93    1/13/98 5:37p Dave
190  * Reworked a lot of standalone interface code. Put in single and
191  * multiplayer popups for death sequence. Solidified multiplayer kick
192  * code.
193  * 
194  * 92    1/12/98 5:17p Allender
195  * fixed primary fired problem and ship warp out problem.  Made new
196  * mission goal info packet to deal with goals more accurately. 
197  * 
198  * 91    1/11/98 10:02p Allender
199  * removed <winsock.h> from headers which included it.  Made psnet_socket
200  * type which is defined just as SOCKET type is.
201  * 
202  * 90    1/10/98 1:14p John
203  * Added explanation to debug console commands
204  * 
205  * 89    1/08/98 10:26a Lawrance
206  * Delay directive success sound effect about 1/2 second.
207  * 
208  * 88    1/07/98 6:46p Lawrance
209  * If a goal is invalid, ignore it when evaluating mission success.
210  * 
211  * 87    1/07/98 11:09a Lawrance
212  * Add sound hook for when directive gets completed.
213  * 
214  * 86    1/02/98 3:07p Hoffoss
215  * Changed sexp evaluation to every half second.
216  * 
217  * 85    12/27/97 8:08p Lawrance
218  * get savegames working again
219  * 
220  * 84    12/26/97 10:02p Lawrance
221  * Add event music for when goals fail.
222  * 
223  * 83    12/22/97 6:07p Hoffoss
224  * Made directives flash when completed, fixed but with is-destroyed
225  * operator.
226  * 
227  * 82    12/21/97 4:33p John
228  * Made debug console functions a class that registers itself
229  * automatically, so you don't need to add the function to
230  * debugfunctions.cpp.  
231  * 
232  * 81    12/19/97 12:43p Hoffoss
233  * Changed code to allow counts in directives.
234  * 
235  * 80    12/15/97 5:26p Allender
236  * temporary code to display for 5 second completion status of objectives
237  * 
238  * 79    12/03/97 4:16p Hoffoss
239  * Changed sound stuff used in interface screens for interface purposes.
240  * 
241  * 78    12/03/97 11:35a Hoffoss
242  * Made changes to HUD messages send throughout the game.
243  * 
244  * 77    12/02/97 10:47a Hoffoss
245  * Changed mention of goals to objectives.
246  * 
247  * 76    12/01/97 12:26a Lawrance
248  * Add flag  MGF_NO_MUSIC to mission_goal struct, to avoid playing music
249  * for certain goals
250  * 
251  * 75    11/21/97 2:16p Allender
252  * debug keys to mark all goals (p/s/b) as satisfied
253  * 
254  * 74    11/17/97 11:45a Johnson
255  * when marking goals false, add log entry.  Be sure that "delay" function
256  * check for equal to delay as well
257  * 
258  * 73    11/13/97 10:16p Hoffoss
259  * Added icons to mission log scrollback.
260  * 
261  * 72    11/13/97 4:05p Hoffoss
262  * Added hiding code for mission log entries.
263  * 
264  * 71    11/05/97 7:11p Hoffoss
265  * Made changed to the hud message system.  Hud messages can now have
266  * sources so they can be color coded.
267  * 
268  * 70    11/02/97 10:09p Lawrance
269  * add missing fields from mission_event to save/restore
270  * 
271  * 69    10/31/97 4:28p Allender
272  * fail all incomplete mission goals when mission is over
273  * 
274  * 68    10/29/97 11:06a Jasen
275  * eval goals/events every 1.75 seconds instead of every 2.5 seconds for
276  * slightly more accurate timing of events
277  * 
278  * 67    10/28/97 10:05a Allender
279  * added function to mark all goals as failed
280  * 
281  * 66    10/28/97 9:33a Lawrance
282  * change 'Alt-Q' in HUD message to use text for bound key
283  * 
284  * 65    10/27/97 5:03p Hoffoss
285  * Fixed comment.
286  * 
287  * 64    10/23/97 2:16p Johnson
288  * make pointers point to NULl after freeing to prevent problems when
289  * tried to free multiple times
290  * 
291  * 63    10/16/97 2:35p Hoffoss
292  * Enhanced the mission goals screen a little.
293  * 
294  * 62    10/16/97 1:28p Hoffoss
295  * New mission goals screen implemented.
296  * 
297  * 61    10/10/97 6:15p Hoffoss
298  * Implemented a training objective list display.
299  * 
300  * 60    10/09/97 4:44p Hoffoss
301  * Dimmed training window glass and made it less transparent, added flags
302  * to events, set he stage for detecting current events.
303  * 
304  * 59    10/06/97 4:11p Lawrance
305  * add missing field from mission_events to save/restore
306  * 
307  * 58    10/03/97 4:14p Hoffoss
308  * Augmented event debug view code.
309  * 
310  * 57    9/30/97 10:01a Hoffoss
311  * Added event chaining support to Fred and FreeSpace.
312  * 
313  * 56    9/29/97 1:58p Hoffoss
314  * Need to check in changes so I can get merged code to continue working
315  * on event chaining code.
316  *
317 */
318
319 #include "freespace.h"
320 #include "object.h"
321 #include "missiongoals.h"
322 #include "missionparse.h"
323 #include "missionlog.h"
324 #include "missiontraining.h"
325 #include "missionscreencommon.h"
326 #include "gamesequence.h"
327 #include "hud.h"
328 #include "key.h"
329 #include "2d.h"
330 #include "timer.h"
331 #include "linklist.h"
332 #include "ship.h"
333 #include "ai.h"
334 #include "parselo.h"
335 #include "sexp.h"
336 #include "eventmusic.h"
337 #include "multi.h"
338 #include "multimsgs.h"
339 #include "stand_gui.h"
340 #include "ui.h"
341 #include "bmpman.h"
342 #include "sound.h"
343 #include "gamesnd.h"
344 #include "alphacolors.h"
345 #include "multi_team.h"
346
347 // timestamp stuff for evaluating mission goals
348 #define GOAL_TIMESTAMP                          0                               // make immediately eval
349 #define GOAL_TIMESTAMP_TRAINING 500                     // every half second
350
351 #define MAX_GOALS_PER_LIST                      15
352 #define MAX_GOAL_LINES  200
353
354 // indicies for coordinates
355 #define GOAL_SCREEN_X_COORD 0
356 #define GOAL_SCREEN_Y_COORD 1
357 #define GOAL_SCREEN_W_COORD 2
358 #define GOAL_SCREEN_H_COORD 3
359
360 /*
361 #define GOAL_SCREEN_TEXT_X      81
362 #define GOAL_SCREEN_TEXT_Y      95
363 #define GOAL_SCREEN_TEXT_W      385
364 #define GOAL_SCREEN_TEXT_H      299
365 #define GOAL_SCREEN_ICON_X 45
366 */
367
368 static int Goal_screen_text_coords[GR_NUM_RESOLUTIONS][4] = {
369         {
370                 81,95,385,299           // GR_640
371         },
372         {
373                 130,152,385,299 // GR_1024
374         }
375 };
376
377 static int Goal_screen_icon_xcoord[GR_NUM_RESOLUTIONS] = {
378         45,     // GR_640
379         72              // GR_1024
380 };
381
382
383 #if defined(GERMAN_BUILD)
384         // german version gets slightly diff coords
385         static int Objective_key_text_coords[GR_NUM_RESOLUTIONS][3][2] = {
386                 {
387                         // GR_640
388                         {175, 344},
389                         {316, 344},
390                         {432, 344}
391                 },
392                 {
393                         // GR_1024
394                         {310, 546},
395                         {536, 546},
396                         {688, 546}
397                 }
398         };
399         static int Objective_key_icon_coords[GR_NUM_RESOLUTIONS][3][2] = {
400         {
401                 // GR_640
402                 {150, 339},
403                 {290, 339},
404                 {406, 339}
405         },
406         {
407                 // GR_1024
408                 {272, 542},
409                 {498, 542},
410                 {650, 542}
411         }
412 };
413 #else
414         static int Objective_key_text_coords[GR_NUM_RESOLUTIONS][3][2] = {
415                 {
416                         // GR_640
417                         {195, 344},
418                         {306, 344},
419                         {432, 344}
420                 },
421                 {
422                         // GR_1024
423                         {310, 546},
424                         {486, 546},
425                         {688, 546}
426                 }
427         };
428         static int Objective_key_icon_coords[GR_NUM_RESOLUTIONS][3][2] = {
429         {
430                 // GR_640
431                 {170, 339},
432                 {280, 339},
433                 {406, 339}
434         },
435         {
436                 // GR_1024
437                 {272, 542},
438                 {448, 542},
439                 {650, 542}
440         }
441 };
442 #endif
443
444
445 #define NUM_GOAL_SCREEN_BUTTONS                 3  // total number of buttons
446 #define GOAL_SCREEN_BUTTON_SCROLL_UP    0
447 #define GOAL_SCREEN_BUTTON_SCROLL_DOWN  1
448 #define GOAL_SCREEN_BUTTON_RETURN               2
449
450 struct goal_list {
451         int count;
452         mission_goal *list[MAX_GOALS_PER_LIST];
453         int line_offsets[MAX_GOALS_PER_LIST];
454         int line_spans[MAX_GOALS_PER_LIST];
455
456         goal_list() : count(0) {}
457         void add(mission_goal *m);
458         void set();
459         void icons_display(int y);
460 };
461
462 struct goal_buttons {
463         char *filename;
464         int x, y;
465         int hotspot;
466         UI_BUTTON button;  // because we have a class inside this struct, we need the constructor below..
467
468         goal_buttons(char *name, int x1, int y1, int h) : filename(name), x(x1), y(y1), hotspot(h) {}
469 };
470
471 struct goal_text {
472         int m_num_lines;
473         int m_line_sizes[MAX_GOAL_LINES];
474         char *m_lines[MAX_GOAL_LINES];
475
476         void init();
477         int add(char *text = NULL);
478         void display(int n, int y);
479 };
480
481 int Num_mission_events;
482 int Num_goals = 0;                                                              // number of goals for this mission
483 int Event_index;  // used by sexp code to tell what event it came from
484 int Mission_goal_timestamp;
485
486 mission_event Mission_events[MAX_MISSION_EVENTS];
487 mission_goal Mission_goals[MAX_GOALS];          // structure for the goals of this mission
488 static goal_text Goal_text;
489
490 #define DIRECTIVE_SOUND_DELAY                   500                                     // time directive success sound effect is delayed
491 #define DIRECTIVE_SPECIAL_DELAY         7000                                    // mark special directives as true after 7 seconds
492
493 static int Mission_directive_sound_timestamp;   // timestamp to control when directive succcess sound gets played
494 static int Mission_directive_special_timestamp; // used to specially mark a directive as true even though it's not
495
496 char *Goal_type_text(int n)
497 {
498         switch (n) {
499                 case 0: 
500                         return XSTR( "Primary", 396);
501
502                 case 1:
503                         return XSTR( "Secondary", 397);
504
505                 case 2:
506                         return XSTR( "Bonus", 398);
507         }
508
509         return NULL;
510 };
511
512 static int Goal_screen_text_x;
513 static int Goal_screen_text_y;
514 static int Goal_screen_text_w;
515 static int Goal_screen_text_h;
516 static int Goal_screen_icon_x;
517
518 static goal_list Primary_goal_list;
519 static goal_list Secondary_goal_list;
520 static goal_list Bonus_goal_list;
521
522 static int Scroll_offset;
523 static int Goals_screen_bg_bitmap;
524 static int Goal_complete_bitmap;
525 static int Goal_incomplete_bitmap;
526 static int Goal_failed_bitmap;
527 static UI_WINDOW Goals_screen_ui_window;
528
529 goal_buttons Goal_buttons[NUM_GOAL_SCREEN_BUTTONS] = {
530 //XSTR:OFF
531         goal_buttons("MOB_00", 475, 288, 0),
532         goal_buttons("MOB_01", 475, 336, 1),
533         goal_buttons("MOB_02", 553, 409, 2),
534 //XSTR:ON
535 };
536
537 void goal_screen_button_pressed(int num);
538 void goal_screen_scroll_up();
539 void goal_screen_scroll_down();
540
541 //
542 /// goal_list structure functions
543 //
544
545 void goal_list::add(mission_goal *m)
546 {
547         Assert(count < MAX_GOALS_PER_LIST);
548         list[count++] = m;
549 }
550
551 void goal_list::set()
552 {
553         int i;
554
555         for (i=0; i<count; i++) {
556                 line_offsets[i] = Goal_text.m_num_lines;
557                 line_spans[i] = Goal_text.add(list[i]->message);
558                 
559                 if (i < count - 1)
560                         Goal_text.add();
561         }
562 }
563
564 void goal_list::icons_display(int yoff)
565 {
566         int i, y, ys, bmp, font_height;
567
568         font_height = gr_get_font_height();
569         for (i=0; i<count; i++) {
570                 y = line_offsets[i] - yoff;
571
572                 bmp = -1;                       // initialize for safety.
573                 switch (list[i]->satisfied) {
574                         case GOAL_COMPLETE:
575                                 bmp = Goal_complete_bitmap;
576                                 break;
577
578                         case GOAL_INCOMPLETE:
579                                 bmp = Goal_incomplete_bitmap;
580                                 break;
581
582                         case GOAL_FAILED:
583                                 bmp = Goal_failed_bitmap;
584                                 break;
585                 }
586
587                 if (bmp >= 0) {
588                         bm_get_info(bmp, NULL, &ys, NULL);
589                         y = Goal_screen_text_y                                          // offset of text window on screen
590                                 + y * font_height                                                       // relative line position offset
591                                 + line_spans[i] * font_height / 2       // center of text offset
592                                 - ys / 2;                                                                       // center of icon offest
593
594                         if ((y >= Goal_screen_text_y - ys / 2) && (y + ys <= Goal_screen_text_y + Goal_screen_text_h + ys / 2)) {
595                                 gr_set_bitmap(bmp);
596                                 gr_bitmap(Goal_screen_icon_x, y);
597                         }
598                 }
599         }
600 }
601
602 //
603 /// goal_text structure functions
604 //
605
606 // initializes the goal text struct (empties it out)
607 void goal_text::init()
608 {
609         m_num_lines = 0;
610 }
611
612 // Adds lines of goal text.  If passed NULL (or nothing passed) a blank line is added.  If
613 // the text is too long, it is automatically split into more than one line.
614 // Returns the number of lines added.
615 int goal_text::add(char *text)
616 {
617         int max, count;
618
619         max = MAX_GOAL_LINES - m_num_lines;
620         if (max < 1) {
621                 Error(LOCATION, "Goal text space exhausted");
622                 return 0;
623         }
624
625         if (!text) {
626                 m_lines[m_num_lines] = NULL;
627                 m_line_sizes[m_num_lines++] = 0;
628                 return 1;
629         }
630
631         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);
632         m_num_lines += count;
633         return count;
634 }
635
636 // Display a line of goal text
637 //   n = goal text line number
638 //   y = y offset to draw relative to goal text area top
639 void goal_text::display(int n, int y)
640 {
641         int y1, w, h;
642         char buf[MAX_GOAL_TEXT];
643
644         if ((n < 0) || (n >= m_num_lines) || (m_line_sizes[n] < 1))
645                 return;  // out of range, don't draw anything
646
647         Assert(m_line_sizes[n] < MAX_GOAL_TEXT);
648         y += Goal_screen_text_y;
649         if (*m_lines[n] == '*') {  // header line
650                 gr_set_color_fast(&Color_text_heading);
651                 strncpy(buf, m_lines[n] + 1, m_line_sizes[n] - 1);
652                 buf[m_line_sizes[n] - 1] = 0;
653
654                 gr_get_string_size(&w, &h, buf);
655                 y1 = y + h / 2 - 1;
656                 gr_line(Goal_screen_icon_x, y1, Goal_screen_text_x - 2, y1);
657                 gr_line(Goal_screen_text_x + w + 1, y1, Goal_screen_icon_x + Goal_screen_text_w, y1);
658
659         } else {
660                 gr_set_color_fast(&Color_text_normal);
661                 strncpy(buf, m_lines[n], m_line_sizes[n]);
662                 buf[m_line_sizes[n]] = 0;
663         }
664
665         gr_printf(Goal_screen_text_x, y, buf);
666 }
667
668 // mission_init_goals: initializes info for goals.  Called as part of mission initialization.
669 void mission_init_goals()
670 {
671         int i;
672
673         Num_goals = 0;
674         for (i=0; i<MAX_GOALS; i++) {
675                 Mission_goals[i].satisfied = GOAL_INCOMPLETE;
676                 Mission_goals[i].flags = 0;
677                 Mission_goals[i].team = 0;
678         }
679
680         Num_mission_events = 0;
681         for (i=0; i<MAX_MISSION_EVENTS; i++) {
682                 Mission_events[i].result = 0;
683                 Mission_events[i].flags = 0;
684                 Mission_events[i].count = 0;
685                 Mission_events[i].satisfied_time = 0;
686                 Mission_events[i].born_on_date = 0;
687                 Mission_events[i].team = -1;
688         }
689
690         Mission_goal_timestamp = timestamp(GOAL_TIMESTAMP);
691         Mission_directive_sound_timestamp = 0;
692         Mission_directive_special_timestamp = timestamp(-1);            // need to make invalid right away
693 }
694
695 // called at the end of a mission for cleanup
696 void mission_event_shutdown()
697 {
698         int i;
699
700         for (i=0; i<Num_mission_events; i++) {
701                 if (Mission_events[i].objective_text) {
702                         free(Mission_events[i].objective_text);
703                         Mission_events[i].objective_text = NULL;
704                 }
705                 if (Mission_events[i].objective_key_text) {
706                         free(Mission_events[i].objective_key_text);
707                         Mission_events[i].objective_key_text = NULL;
708                 }
709         }
710 }
711
712 // called once right before entering the show goals screen to do initializations.
713 void mission_show_goals_init()
714 {
715         int i, type, team_num=0;                // JAS: I set team_num to 0 because it was used without being initialized.
716         goal_buttons *b;
717
718         Scroll_offset = 0;
719         Primary_goal_list.count = 0;
720         Secondary_goal_list.count = 0;
721         Bonus_goal_list.count = 0;
722
723         Goal_screen_text_x = Goal_screen_text_coords[gr_screen.res][GOAL_SCREEN_X_COORD];
724         Goal_screen_text_y = Goal_screen_text_coords[gr_screen.res][GOAL_SCREEN_Y_COORD];
725         Goal_screen_text_w = Goal_screen_text_coords[gr_screen.res][GOAL_SCREEN_W_COORD];
726         Goal_screen_text_h = Goal_screen_text_coords[gr_screen.res][GOAL_SCREEN_H_COORD];
727         Goal_screen_icon_x = Goal_screen_icon_xcoord[gr_screen.res];
728
729         // fill up the lists so we can display the goals appropriately
730         for (i=0; i<Num_goals; i++) {
731                 if (Mission_goals[i].type & INVALID_GOAL){  // don't count invalid goals here
732                         continue;
733                 }
734
735                 if ( (Game_mode & GM_MULTIPLAYER) && (The_mission.game_type & MISSION_TYPE_MULTI_TEAMS) && (Mission_goals[i].team != team_num) ){
736                         continue;
737                 }
738
739                 type = Mission_goals[i].type & GOAL_TYPE_MASK;
740                 switch (type) {
741                 case PRIMARY_GOAL:
742                         Primary_goal_list.add(&Mission_goals[i]);
743                         break;
744
745                 case SECONDARY_GOAL:
746                         Secondary_goal_list.add(&Mission_goals[i]);
747                         break;
748
749                 case BONUS_GOAL:
750                         if (Mission_goals[i].satisfied == GOAL_COMPLETE){
751                                 Bonus_goal_list.add(&Mission_goals[i]);
752                         }
753                         break;
754         
755                 default:
756                         Error(LOCATION, "Unknown goal priority encountered when displaying goals in mission\n");
757                         break;
758                 } // end switch
759         } // end for
760
761         Goal_text.init();
762
763         Goal_text.add(XSTR( "*Primary Objectives", 399));
764         Goal_text.add();
765         Primary_goal_list.set();
766
767         if (Secondary_goal_list.count) {
768                 Goal_text.add();
769                 Goal_text.add();
770                 Goal_text.add(XSTR( "*Secondary Objectives", 400));
771                 Goal_text.add();
772                 Secondary_goal_list.set();
773         }
774
775         if (Bonus_goal_list.count) {
776                 Goal_text.add();
777                 Goal_text.add();
778                 Goal_text.add(XSTR( "*Bonus Objectives", 401));
779                 Goal_text.add();
780                 Bonus_goal_list.set();
781         }
782
783         common_set_interface_palette("ObjectivesPalette");  // set the interface palette
784         Goals_screen_ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
785         Goals_screen_ui_window.set_mask_bmap("Objectives-m");
786
787         for (i=0; i<NUM_GOAL_SCREEN_BUTTONS; i++) {
788                 b = &Goal_buttons[i];
789
790                 b->button.create(&Goals_screen_ui_window, "", b->x, b->y, 60, 30, (i < 2), 1);
791                 // set up callback for when a mouse first goes over a button
792                 b->button.set_highlight_action(common_play_highlight_sound);
793                 b->button.set_bmaps(b->filename);
794                 b->button.link_hotspot(b->hotspot);
795         }
796
797         // set up hotkeys for buttons so we draw the correct animation frame when a key is pressed
798         Goal_buttons[GOAL_SCREEN_BUTTON_SCROLL_UP].button.set_hotkey(KEY_UP);
799         Goal_buttons[GOAL_SCREEN_BUTTON_SCROLL_DOWN].button.set_hotkey(KEY_DOWN);
800
801         Goals_screen_bg_bitmap = bm_load("ObjectivesBG");
802         Goal_complete_bitmap = bm_load("ObjComp");
803         Goal_incomplete_bitmap = bm_load("ObjIncomp");
804         Goal_failed_bitmap = bm_load("ObjFail");
805
806
807         // if (Goal_incomplete_bitmap < 0) Int3();
808
809         if (Goals_screen_bg_bitmap < 0) {
810                 Warning(LOCATION, "Could not load the background bitmap: ObjectivesBG.pcx");
811         }
812 }
813
814 // cleanup called when exiting the show goals screen
815 void mission_show_goals_close()
816 {
817         if (Goals_screen_bg_bitmap >= 0)
818                 bm_unload(Goals_screen_bg_bitmap);
819
820         if (Goal_complete_bitmap)
821                 bm_unload(Goal_complete_bitmap);
822         
823         if (Goal_incomplete_bitmap)
824                 bm_unload(Goal_incomplete_bitmap);
825         
826         if (Goal_failed_bitmap)
827                 bm_unload(Goal_failed_bitmap);
828
829         Goals_screen_ui_window.destroy();
830         common_free_interface_palette();                // restore game palette
831         game_flush();
832 }
833         
834 // called once a frame during show goals state to process events and render the screen
835 void mission_show_goals_do_frame(float frametime)
836 {
837         int k, i, y, z;
838         int font_height = gr_get_font_height();
839         
840         k = Goals_screen_ui_window.process();
841         switch (k) {
842                 case KEY_ESC:
843                         mission_goal_exit();                    
844                         break;
845                 
846                 case KEY_DOWN:
847                         goal_screen_scroll_down();
848                         break;
849
850                 case KEY_UP:
851                         goal_screen_scroll_up();
852                         break;
853
854                 default:
855                         // do nothing
856                         break;
857         }       // end switch
858
859         for (i=0; i<NUM_GOAL_SCREEN_BUTTONS; i++){
860                 if (Goal_buttons[i].button.pressed()){
861                         goal_screen_button_pressed(i);
862                 }
863         }
864
865         GR_MAYBE_CLEAR_RES(Goals_screen_bg_bitmap);
866         if (Goals_screen_bg_bitmap >= 0) {
867                 gr_set_bitmap(Goals_screen_bg_bitmap);
868                 gr_bitmap(0, 0);
869         }
870         Goals_screen_ui_window.draw();
871
872         y = 0;
873         z = Scroll_offset;
874         while (y + font_height <= Goal_screen_text_h) {
875                 Goal_text.display(z, y);
876                 y += font_height;
877                 z++;
878         }
879
880         Primary_goal_list.icons_display(Scroll_offset);
881         Secondary_goal_list.icons_display(Scroll_offset);
882         Bonus_goal_list.icons_display(Scroll_offset);
883
884         gr_flip();
885 }
886
887 // Mission Log Objectives subscreen init.
888 // Called once right before entering the Mission Log screen to do initializations.
889 int ML_objectives_init(int x, int y, int w, int h)
890 {
891         int i, type, team_num;
892
893         Primary_goal_list.count = 0;
894         Secondary_goal_list.count = 0;
895         Bonus_goal_list.count = 0;
896
897         Goal_screen_text_x = x - Goal_screen_icon_xcoord[gr_screen.res] + Goal_screen_text_coords[gr_screen.res][GOAL_SCREEN_X_COORD];
898         Goal_screen_text_y = y;
899         Goal_screen_text_w = w;
900         Goal_screen_text_h = h;
901         Goal_screen_icon_x = x;
902
903         team_num = 0;                   // this is the default team -- we will change it if in a multiplayer team v. team game
904         if ( (Game_mode & GM_MULTIPLAYER) && (The_mission.game_type & MISSION_TYPE_MULTI_TEAMS) ){
905                 team_num = Net_player->p_info.team;
906         }
907
908         // fill up the lists so we can display the goals appropriately
909         for (i=0; i<Num_goals; i++) {
910                 if (Mission_goals[i].type & INVALID_GOAL){  // don't count invalid goals here
911                         continue;
912                 }
913
914                 if ( (Game_mode & GM_MULTIPLAYER) && (The_mission.game_type & MISSION_TYPE_MULTI_TEAMS) && (Mission_goals[i].team != team_num) ){
915                         continue;
916                 }
917
918                 type = Mission_goals[i].type & GOAL_TYPE_MASK;
919                 switch (type) {
920                         case PRIMARY_GOAL:
921                                 Primary_goal_list.add(&Mission_goals[i]);
922                                 break;
923
924                         case SECONDARY_GOAL:
925                                 Secondary_goal_list.add(&Mission_goals[i]);
926                                 break;
927
928                         case BONUS_GOAL:
929                                 if (Mission_goals[i].satisfied == GOAL_COMPLETE){
930                                         Bonus_goal_list.add(&Mission_goals[i]);
931                                 }
932                                 break;
933         
934                         default:
935                                 Error(LOCATION, "Unknown goal priority encountered when displaying goals in mission\n");
936                                 break;
937                 } // end switch
938         } // end for
939
940         Goal_text.init();
941
942         Goal_text.add(XSTR( "*Primary Objectives", 399));
943         Goal_text.add();
944         Primary_goal_list.set();
945
946         if (Secondary_goal_list.count) {
947                 Goal_text.add();
948                 Goal_text.add();
949                 Goal_text.add(XSTR( "*Secondary Objectives", 400));
950                 Goal_text.add();
951                 Secondary_goal_list.set();
952         }
953
954         if (Bonus_goal_list.count) {
955                 Goal_text.add();
956                 Goal_text.add();
957                 Goal_text.add(XSTR( "*Bonus Objectives", 401));
958                 Goal_text.add();
959                 Bonus_goal_list.set();
960         }
961
962
963         Goal_complete_bitmap = bm_load("ObjComp");
964         Goal_incomplete_bitmap = bm_load("ObjIncomp");
965         Goal_failed_bitmap = bm_load("ObjFail");
966
967         // if (Goal_incomplete_bitmap < 0) Int3();
968
969         return Goal_text.m_num_lines;
970 }
971
972 // cleanup called when exiting the show goals screen
973 void ML_objectives_close()
974 {
975         if (Goal_complete_bitmap >= 0) {
976                 bm_unload(Goal_complete_bitmap);
977         }
978         
979         if (Goal_incomplete_bitmap >= 0) {
980                 bm_unload(Goal_incomplete_bitmap);
981         }
982         
983         if (Goal_failed_bitmap >= 0) {
984                 bm_unload(Goal_failed_bitmap);
985         }
986 }
987
988 void ML_objectives_do_frame(int scroll_offset)
989 {
990         int y, z;
991         int font_height = gr_get_font_height();
992
993         y = 0;
994         z = scroll_offset;
995         while (y + font_height <= Goal_screen_text_h) {
996                 Goal_text.display(z, y);
997                 y += font_height;
998                 z++;
999         }
1000
1001         Primary_goal_list.icons_display(scroll_offset);
1002         Secondary_goal_list.icons_display(scroll_offset);
1003         Bonus_goal_list.icons_display(scroll_offset);
1004 }
1005
1006 void ML_render_objectives_key()
1007 {
1008         // display icon key at the bottom
1009         gr_set_bitmap(Goal_complete_bitmap);
1010         gr_bitmap(Objective_key_icon_coords[gr_screen.res][0][0], Objective_key_icon_coords[gr_screen.res][0][1]);
1011         gr_set_bitmap(Goal_incomplete_bitmap);
1012         gr_bitmap(Objective_key_icon_coords[gr_screen.res][1][0], Objective_key_icon_coords[gr_screen.res][1][1]);
1013         gr_set_bitmap(Goal_failed_bitmap);
1014         gr_bitmap(Objective_key_icon_coords[gr_screen.res][2][0], Objective_key_icon_coords[gr_screen.res][2][1]);
1015         
1016         gr_string(Objective_key_text_coords[gr_screen.res][0][0], Objective_key_text_coords[gr_screen.res][0][1] , XSTR("Complete",     1437));
1017         gr_string(Objective_key_text_coords[gr_screen.res][1][0], Objective_key_text_coords[gr_screen.res][1][1] , XSTR("Incomplete", 1438));
1018         gr_string(Objective_key_text_coords[gr_screen.res][2][0], Objective_key_text_coords[gr_screen.res][2][1] , XSTR("Failed",               1439));
1019 }
1020
1021         
1022 // temporary hook for temporarily displaying objective completion/failure
1023 // extern void message_training_add_simple( char *text );
1024
1025 void mission_goal_status_change( int goal_num, int new_status)
1026 {
1027         int type;
1028
1029         Assert(goal_num < Num_goals);
1030         Assert((new_status == GOAL_FAILED) || (new_status == GOAL_COMPLETE));
1031
1032         // if in a multiplayer game, send a status change to clients
1033         if ( MULTIPLAYER_MASTER ){
1034                 send_mission_goal_info_packet( goal_num, new_status, -1 );
1035         }
1036
1037         type = Mission_goals[goal_num].type & GOAL_TYPE_MASK;
1038         Mission_goals[goal_num].satisfied = new_status;
1039         if ( new_status == GOAL_FAILED ) {
1040                 // don't display bonus goal failure
1041                 if ( type != BONUS_GOAL ) {
1042
1043                         // only do HUD and music is goals are my teams goals.
1044                         if ( (Game_mode & GM_NORMAL) || ((Net_player != NULL) && (Net_player->p_info.team == Mission_goals[goal_num].team)) ) {
1045                                 hud_add_objective_messsage(type, new_status);
1046                                 if ( !Mission_goals[goal_num].flags & MGF_NO_MUSIC ) {  // maybe play event music
1047                                         event_music_primary_goal_failed();
1048                                 }
1049                                 //HUD_sourced_printf(HUD_SOURCE_FAILED, "%s goal failed at time %6.1f!", Goal_type_text(type), f2fl(Missiontime) );
1050                         }
1051                 }
1052                 mission_log_add_entry( LOG_GOAL_FAILED, Mission_goals[goal_num].name, NULL, goal_num );
1053         } else if ( new_status == GOAL_COMPLETE ) {
1054                 if ( (Game_mode & GM_NORMAL) || ((Net_player != NULL) && (Net_player->p_info.team == Mission_goals[goal_num].team))) {
1055                         hud_add_objective_messsage(type, new_status);
1056                         // cue for Event Music
1057                         if ( !(Mission_goals[goal_num].flags & MGF_NO_MUSIC) ) {
1058                                 event_music_primary_goals_met();
1059                         }                       
1060                         mission_log_add_entry( LOG_GOAL_SATISFIED, Mission_goals[goal_num].name, NULL, goal_num );
1061                 }       
1062                 
1063                 if(Game_mode & GM_MULTIPLAYER){
1064                         // squad war
1065                         multi_team_maybe_add_score((int)(Mission_goals[goal_num].score * scoring_get_scale_factor()), Mission_goals[goal_num].team);    
1066                 } else {
1067                         // deal with the score
1068                         Player->stats.m_score += (int)(Mission_goals[goal_num].score * scoring_get_scale_factor());                     
1069                 }
1070         }
1071 }
1072
1073 // return value:
1074 //   EVENT_UNBORN    = event has yet to be available (not yet evaluatable)
1075 //   EVENT_CURRENT   = current (evaluatable), but not yet true
1076 //   EVENT_SATISFIED = event has occured (true)
1077 //   EVENT_FAILED    = event failed, can't possibly become true anymore
1078 int mission_get_event_status(int event)
1079 {
1080         // check for directive special events first.  We will always return from this part of the if statement
1081         if ( Mission_events[event].flags & MEF_DIRECTIVE_SPECIAL ) {
1082
1083                 // if this event is temporarily true, return as such
1084                 if ( Mission_events[event].flags & MEF_DIRECTIVE_TEMP_TRUE ){
1085                         return EVENT_SATISFIED;
1086                 }
1087
1088                 // if the timestamp has elapsed, we can "mark" this directive as true although it's really not.
1089                 if ( timestamp_elapsed(Mission_directive_special_timestamp) ) {
1090                         Mission_events[event].satisfied_time = Missiontime;
1091                         Mission_events[event].flags |= MEF_DIRECTIVE_TEMP_TRUE;
1092                 }
1093
1094                 return EVENT_CURRENT;
1095         } else if (Mission_events[event].flags & MEF_CURRENT) {
1096                 if (!Mission_events[event].born_on_date){
1097                         Mission_events[event].born_on_date = timestamp();
1098                 }
1099
1100                 if (Mission_events[event].result) {
1101                         return EVENT_SATISFIED;
1102                 }
1103
1104                 if (Mission_events[event].formula < 0) {
1105                         return EVENT_FAILED;
1106                 }
1107
1108                 return EVENT_CURRENT;
1109         }
1110
1111         return EVENT_UNBORN;
1112 }
1113
1114 void mission_event_set_directive_special(int event)
1115 {
1116         // bogus
1117         if((event < 0) || (event >= Num_mission_events)){
1118                 return;
1119         }
1120
1121         Mission_events[event].flags |= MEF_DIRECTIVE_SPECIAL;
1122
1123         // start from a known state
1124         Mission_events[event].flags &= ~MEF_DIRECTIVE_TEMP_TRUE;
1125         Mission_directive_special_timestamp = timestamp(DIRECTIVE_SPECIAL_DELAY);
1126 }
1127
1128 void mission_event_unset_directive_special(int event)
1129 {
1130         // bogus
1131         if((event < 0) || (event >= Num_mission_events)){
1132                 return;
1133         }
1134
1135         Mission_events[event].flags &= ~(MEF_DIRECTIVE_SPECIAL);
1136
1137         // this event may be marked temporarily true -- if so, then unmark this value!!!
1138         if ( Mission_events[event].flags & MEF_DIRECTIVE_TEMP_TRUE ){
1139                 Mission_events[event].flags &= ~MEF_DIRECTIVE_TEMP_TRUE;
1140         }
1141         Mission_events[event].satisfied_time = 0;
1142         Mission_directive_special_timestamp = timestamp(-1);
1143 }
1144
1145 // function which evaluates and processes the given event
1146 void mission_process_event( int event )
1147 {
1148         int store_flags = Mission_events[event].flags;
1149         int store_formula = Mission_events[event].formula;
1150         int store_result = Mission_events[event].result;
1151         int store_count = Mission_events[event].count;
1152
1153         int result, sindex;
1154
1155         Directive_count = 0;
1156         Event_index = event;
1157         sindex = Mission_events[event].formula;
1158         result = Mission_events[event].result;
1159
1160         // if chained, insure that previous event is true and next event is false
1161         if (Mission_events[event].chain_delay >= 0) {  // this indicates it's chained
1162                 if (event > 0){
1163                         if (!Mission_events[event - 1].result || ((fix) Mission_events[event - 1].timestamp + i2f(Mission_events[event].chain_delay) > Missiontime)){
1164                                 sindex = -1;  // bypass evaluation
1165                         }
1166                 }
1167
1168                 if ((event < Num_mission_events - 1) && Mission_events[event + 1].result && (Mission_events[event + 1].chain_delay >= 0)){
1169                         sindex = -1;  // bypass evaluation
1170                 }
1171         }
1172
1173         if (sindex >= 0) {
1174                 Sexp_useful_number = 1;
1175                 result = eval_sexp(sindex);
1176
1177                 // if the directive count is a special value, deal with that first.  Mark the event as a special
1178                 // event, and unmark it when the directive is true again.
1179                 if ( (Directive_count == DIRECTIVE_WING_ZERO) && !(Mission_events[event].flags & MEF_DIRECTIVE_SPECIAL) ) {                     
1180                         // make it special - which basically just means that its true until the next wave arrives
1181                         mission_event_set_directive_special(event);
1182
1183                         Directive_count = 0;
1184                 } else if ( (Mission_events[event].flags & MEF_DIRECTIVE_SPECIAL) && Directive_count > 1 ) {                    
1185                         // make it non special
1186                         mission_event_unset_directive_special(event);
1187                 }
1188
1189                 if (Mission_events[event].count || (Directive_count > 1)){
1190                         Mission_events[event].count = Directive_count;
1191                 }
1192
1193                 if (Sexp_useful_number){
1194                         Mission_events[event].flags |= MEF_CURRENT;
1195                 }
1196         }
1197
1198         Event_index = 0;
1199         Mission_events[event].result = result;
1200
1201         // if the sexpression is known false, then no need to evaluate anymore
1202         if ((sindex >= 0) && (Sexp_nodes[sindex].value == SEXP_KNOWN_FALSE)) {
1203                 Mission_events[event].timestamp = (int) Missiontime;
1204                 Mission_events[event].satisfied_time = Missiontime;
1205                 Mission_events[event].repeat_count = -1;
1206                 Mission_events[event].formula = -1;
1207                 return;
1208         }
1209
1210         if (result && !Mission_events[event].satisfied_time) {
1211                 Mission_events[event].satisfied_time = Missiontime;
1212                 if ( Mission_events[event].objective_text ) {
1213                         Mission_directive_sound_timestamp = timestamp(DIRECTIVE_SOUND_DELAY);
1214                 }
1215         }
1216
1217         // decrement the repeat count.  When at 0, don't eval this function anymore
1218         if ( result || timestamp_valid(Mission_events[event].timestamp) ) {
1219                 Mission_events[event].repeat_count--;
1220                 if ( Mission_events[event].repeat_count <= 0 ) {
1221                         Mission_events[event].timestamp = (int)Missiontime;
1222                         Mission_events[event].formula = -1;
1223
1224                         if(Game_mode & GM_MULTIPLAYER){
1225                                 // squad war
1226                                 multi_team_maybe_add_score((int)(Mission_events[event].score * scoring_get_scale_factor()), Mission_events[event].team);
1227                         } else {
1228                                 // deal with the player's score
1229                                 Player->stats.m_score += (int)(Mission_events[event].score * scoring_get_scale_factor());                       
1230                         }
1231                 } else {
1232                         // set the timestamp to time out 'interval' seconds in the future.  We must also reset the
1233                         // value at the sexpresion node to unknown so that it will get reevaled
1234                         Mission_events[event].timestamp = timestamp( Mission_events[event].interval * 1000 );
1235 //                      Sexp_nodes[Mission_events[event].formula].value = SEXP_UNKNOWN;
1236                 }
1237         }
1238
1239         // see if anything has changed  
1240         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)) ){
1241                 send_event_update_packet(event);
1242         }       
1243 }
1244
1245 // Maybe play a directive success sound... need to poll since the sound is delayed from when
1246 // the directive is actually satisfied.
1247 void mission_maybe_play_directive_success_sound()
1248 {
1249         if ( timestamp_elapsed(Mission_directive_sound_timestamp) ) {
1250                 Mission_directive_sound_timestamp=0;
1251                 snd_play( &Snds[SND_DIRECTIVE_COMPLETE] );
1252         }
1253 }
1254
1255 void mission_eval_goals()
1256 {
1257         int i, result, goal_changed = 0;
1258
1259         // before checking whether or not we should evaluate goals, we should run through the events and
1260         // process any whose timestamp is valid and has expired.  This would catch repeating events only
1261         for (i=0; i<Num_mission_events; i++) {
1262                 if (Mission_events[i].formula != -1) {
1263                         if ( !timestamp_valid(Mission_events[i].timestamp) || !timestamp_elapsed(Mission_events[i].timestamp) ){
1264                                 continue;
1265                         }
1266
1267                         // if we get here, then the timestamp on the event has popped -- we should reevaluate
1268                         mission_process_event(i);
1269                 }
1270         }
1271         
1272         if ( !timestamp_elapsed(Mission_goal_timestamp) ){
1273                 return;
1274         }
1275
1276         // first evaluate the players goals
1277         for (i=0; i<Num_goals; i++) {
1278                 // don't evaluate invalid goals
1279                 if (Mission_goals[i].type & INVALID_GOAL){
1280                         continue;
1281                 }
1282
1283                 if (Mission_goals[i].satisfied == GOAL_INCOMPLETE) {
1284                         result = eval_sexp(Mission_goals[i].formula);
1285                         if ( Sexp_nodes[Mission_goals[i].formula].value == SEXP_KNOWN_FALSE ) {
1286                                 goal_changed = 1;
1287                                 mission_goal_status_change( i, GOAL_FAILED );
1288
1289                         } else if (result) {
1290                                 goal_changed = 1;
1291                                 mission_goal_status_change(i, GOAL_COMPLETE );
1292                         } // end if result
1293                         
1294                         // tell the player how to end the mission
1295                         //if ( goal_changed && mission_evaluate_primary_goals() != PRIMARY_GOALS_INCOMPLETE ) {
1296                         //      HUD_sourced_printf(HUD_SOURCE_IMPORTANT, "Press %s to end mission and return to base", textify_scancode(Control_config[END_MISSION].key_id) );
1297                         //}
1298                 }       // end if goals[i].satsified != GOAL_COMPLETE
1299         } // end for
1300
1301         // now evaluate any mission events
1302         for (i=0; i<Num_mission_events; i++) {
1303                 if ( Mission_events[i].formula != -1 ) {
1304                         // only evaluate this event if the timestamp is not valid.  We do this since
1305                         // we will evaluate repeatable events at the top of the file so we can get
1306                         // the exact interval that the designer asked for.
1307                         if ( !timestamp_valid( Mission_events[i].timestamp) ){
1308                                 mission_process_event( i );
1309                         }
1310                 }
1311         }
1312
1313         if (The_mission.game_type & MISSION_TYPE_TRAINING){
1314                 Mission_goal_timestamp = timestamp(GOAL_TIMESTAMP_TRAINING);
1315         } else {
1316                 Mission_goal_timestamp = timestamp(GOAL_TIMESTAMP);
1317         }
1318
1319         if ( !hud_disabled() && hud_gauge_active(HUD_DIRECTIVES_VIEW) ) {
1320                 mission_maybe_play_directive_success_sound();
1321         }
1322
1323    // update goal status if playing on a multiplayer standalone server
1324         if (Game_mode & GM_STANDALONE_SERVER){
1325                 std_multi_update_goals();
1326         }
1327 }
1328
1329 //      evaluate_primary_goals() will determine if the primary goals for a mission are complete
1330 //
1331 //      returns 1 - all primary goals are all complete or imcomplete (or there are no primary goals at all)
1332 // returns 0 - not all primary goals are complete
1333 int mission_evaluate_primary_goals()
1334 {
1335         int i, primary_goals_complete = PRIMARY_GOALS_COMPLETE;
1336
1337         for (i=0; i<Num_goals; i++) {
1338
1339                 if ( Mission_goals[i].type & INVALID_GOAL ) {
1340                         continue;
1341                 }
1342
1343                 if ( (Mission_goals[i].type & GOAL_TYPE_MASK) == PRIMARY_GOAL ) {
1344                         if ( Mission_goals[i].satisfied == GOAL_INCOMPLETE ) {
1345                                 return PRIMARY_GOALS_INCOMPLETE;
1346                         } else if ( Mission_goals[i].satisfied == GOAL_FAILED ) {
1347                                 primary_goals_complete = PRIMARY_GOALS_FAILED;
1348                         }
1349                 }
1350         }       // end for
1351
1352         return primary_goals_complete;
1353 }
1354
1355 // return 1 if all primary/secondary goals are complete... otherwise return 0
1356 int mission_goals_met()
1357 {
1358         int i, all_goals_met = 1;
1359
1360         for (i=0; i<Num_goals; i++) {
1361
1362                 if ( Mission_goals[i].type & INVALID_GOAL ) {
1363                         continue;
1364                 }
1365
1366                 if ( ((Mission_goals[i].type & GOAL_TYPE_MASK) == PRIMARY_GOAL) || ((Mission_goals[i].type & GOAL_TYPE_MASK) == SECONDARY_GOAL) ) {
1367                         if ( Mission_goals[i].satisfied == GOAL_INCOMPLETE ) {
1368                                 all_goals_met = 0;
1369                                 break;
1370                         } else if ( Mission_goals[i].satisfied == GOAL_FAILED ) {
1371                                 all_goals_met = 0;
1372                                 break;
1373                         }
1374                 }
1375         }       // end for
1376
1377         return all_goals_met;
1378 }
1379
1380 // function used to actually change the status (valid/invalid) of a goal.  Called externally
1381 // with multiplayer code
1382 void mission_goal_validation_change( int goal_num, int valid )
1383 {
1384         // only incomplete goals can have their status changed
1385         if ( Mission_goals[goal_num].satisfied != GOAL_INCOMPLETE ){
1386                 return;
1387         }
1388
1389         // if in multiplayer, then send a packet
1390         if ( MULTIPLAYER_MASTER ){
1391                 send_mission_goal_info_packet( goal_num, -1, valid );
1392         }
1393
1394         // change the valid status
1395         if ( valid ){
1396                 Mission_goals[goal_num].type &= ~INVALID_GOAL;
1397         } else {
1398                 Mission_goals[goal_num].type |= INVALID_GOAL;
1399         }
1400 }
1401
1402         // the following function marks a goal invalid.  It can only mark the goal invalid if the goal
1403 // is not complete.  The name passed into this funciton should match the name field in the goal
1404 // structure
1405 void mission_goal_mark_invalid( char *name )
1406 {
1407         int i;
1408
1409         for (i=0; i<Num_goals; i++) {
1410                 if ( !stricmp(Mission_goals[i].name, name) ) {
1411                         mission_goal_validation_change( i, 0 );
1412                         return;
1413                 }
1414         }
1415 }
1416
1417 // the next function marks a goal as valid.  A goal may always be marked valid.
1418 void mission_goal_mark_valid( char *name )
1419 {
1420         int i;
1421
1422         for (i=0; i<Num_goals; i++) {
1423                 if ( !stricmp(Mission_goals[i].name, name) ) {
1424                         mission_goal_validation_change( i, 1 );
1425                         return;
1426                 }
1427         }
1428 }
1429
1430 // function to fail all mission goals.  Don't fail events here since this funciton is currently
1431 // called in mission when something bad happens to the player (like he makes too many shots on friendlies).
1432 // Events can still happen.  Things will just be really bad for the player.
1433 void mission_goal_fail_all()
1434 {
1435         int i;
1436
1437         for (i=0; i<Num_goals; i++) {
1438                 Mission_goals[i].satisfied = GOAL_FAILED;
1439                 mission_log_add_entry( LOG_GOAL_FAILED, Mission_goals[i].name, NULL, i );
1440         }
1441 }
1442
1443 // function to mark all incomplete goals as failed.  Happens at the end of a mission
1444 // mark the events which are not currently satisfied as failed as well.
1445 void mission_goal_fail_incomplete()
1446 {
1447         int i;
1448
1449         for (i = 0; i < Num_goals; i++ ) {
1450                 if ( Mission_goals[i].satisfied == GOAL_INCOMPLETE ) {
1451                         Mission_goals[i].satisfied = GOAL_FAILED;
1452                         mission_log_add_entry( LOG_GOAL_FAILED, Mission_goals[i].name, NULL, i );
1453                 }
1454         }
1455
1456         // now for the events.  Must set the formula to -1 and the result to 0 to be a failed
1457         // event.
1458         for ( i = 0; i < Num_mission_events; i++ ) {
1459                 if ( Mission_events[i].formula != -1 ) {
1460                         Mission_events[i].formula = -1;
1461                         Mission_events[i].result = 0;
1462                 }
1463         }
1464 }
1465
1466 // small function used to mark all objectives as true.  Used as a debug function and as a way
1467 // to skip past training misisons
1468 void mission_goal_mark_objectives_complete()
1469 {
1470         int i;
1471
1472         for (i = 0; i < Num_goals; i++ ) {
1473                 Mission_goals[i].satisfied = GOAL_COMPLETE;
1474         }
1475 }
1476
1477 // small function used to mark all events as completed.  Used in the skipping of missions.
1478 void mission_goal_mark_events_complete()
1479 {
1480         int i;
1481
1482         for (i = 0; i < Num_mission_events; i++ ) {
1483                 Mission_events[i].result = 1;
1484                 Mission_events[i].formula = -1;
1485         }
1486 }
1487
1488 // some debug console functions to help list and change the status of mission goals
1489 DCF(show_mission_goals,"List and change the status of mission goals")
1490 {
1491         int i, type;
1492
1493         if (Dc_command)
1494                 Dc_status = 1;
1495
1496         if (Dc_help) {
1497                 dc_printf("Usage: show_mission_goals\n\nList all mission goals and their current status.\n");
1498                 Dc_status = 0;
1499         }
1500
1501         if (Dc_status) {
1502                 for (i=0; i<Num_goals; i++) {
1503                         type = Mission_goals[i].type & GOAL_TYPE_MASK;
1504                         dc_printf("%2d. %32s(%10s) -- ", i, Mission_goals[i].name, Goal_type_text(type));
1505                         if ( Mission_goals[i].satisfied == GOAL_COMPLETE )
1506                                 dc_printf("satisfied.\n");
1507                         else if ( Mission_goals[i].satisfied == GOAL_INCOMPLETE )
1508                                 dc_printf("not satisfied\n");
1509                         else if ( Mission_goals[i].satisfied == GOAL_FAILED )
1510                                 dc_printf("failed\n");
1511                         else
1512                                 dc_printf("\t[unknown goal status].\n");
1513                 }
1514         }
1515 }
1516
1517 //XSTR:OFF
1518 DCF(change_mission_goal, "Change the mission goal")
1519 {
1520         int num;
1521
1522         if ( Dc_command ) {
1523                 dc_get_arg(ARG_INT);
1524                 if ( Dc_arg_int >= Num_goals ) {
1525                         dc_printf ("First parameter must be a valid goal number.\n");
1526                         return;
1527                 }
1528
1529                 num = Dc_arg_int;
1530                 dc_get_arg(ARG_TRUE|ARG_FALSE|ARG_STRING);
1531                 if ( Dc_arg_type & ARG_TRUE )
1532                         Mission_goals[num].satisfied = GOAL_COMPLETE;
1533                 else if ( Dc_arg_type & ARG_FALSE )
1534                         Mission_goals[num].satisfied = GOAL_FAILED;
1535                 else if ( Dc_arg_type & ARG_NONE )
1536                         Mission_goals[num].satisfied = GOAL_INCOMPLETE;
1537                 else if ( Dc_arg_type & ARG_STRING) {
1538                         if ( !stricmp(Dc_arg, "satisfied") )
1539                                 Mission_goals[num].satisfied = GOAL_COMPLETE;
1540                         else if ( !stricmp( Dc_arg, "failed") )
1541                                 Mission_goals[num].satisfied = GOAL_FAILED;
1542                         else if ( !stricmp( Dc_arg, "unknown") )
1543                                 Mission_goals[num].satisfied = GOAL_INCOMPLETE;
1544                         else
1545                                 dc_printf("Unknown status %s.  Use 'satisfied', 'failed', or 'unknown'\n", Dc_arg);
1546                 }
1547         }
1548
1549         if ( Dc_help ) {
1550                 dc_printf("Usage: change_mission_goal <goal_num> <status>\n");
1551                 dc_printf("<goal_num> --  Integer number of goal to change.  See show_mission_goals\n");
1552                 dc_printf("<status>   --  [bool] where a true value makes the goal satisfied,\n");
1553                 dc_printf("               a false value makes the goal failed.\n");
1554                 dc_printf("The <status> field may also be one of 'satisfied', 'failed', or 'unknown'\n");
1555                 dc_printf("\nExamples:\n\n'change_mission_goal 1 true' makes goal 1 successful.\n");
1556                 dc_printf("'change_mission_goal 2' marks goal 2 not complete\n");
1557                 dc_printf("'change_mission_goal 0 satisfied' marks goal 0 as satisfied\n");
1558                 Dc_status = 0;
1559         }
1560 }
1561 //XSTR:ON
1562
1563 // debug functions to mark all primary/secondary/bonus goals as true
1564 #ifndef DEBUG
1565
1566 void mission_goal_mark_all_true(int type)
1567 {
1568         int i;
1569
1570         for (i = 0; i < Num_goals; i++ ) {
1571                 if ( (Mission_goals[i].type & GOAL_TYPE_MASK) == type )
1572                         Mission_goals[i].satisfied = GOAL_COMPLETE;
1573         }
1574
1575         HUD_sourced_printf(HUD_SOURCE_HIDDEN, NOX("All %s goals marked true"), Goal_type_text(type) );
1576 }
1577
1578 #endif
1579
1580 void goal_screen_button_pressed(int num)
1581 {
1582         switch (num) {
1583         case GOAL_SCREEN_BUTTON_SCROLL_UP:
1584                 goal_screen_scroll_up();
1585                 break;
1586
1587         case GOAL_SCREEN_BUTTON_SCROLL_DOWN:
1588                 goal_screen_scroll_down();
1589                 break;
1590
1591         case GOAL_SCREEN_BUTTON_RETURN:
1592                 mission_goal_exit();
1593                 break;
1594         }
1595 }
1596
1597 void goal_screen_scroll_up()
1598 {
1599         if (Scroll_offset) {
1600                 Scroll_offset--;
1601                 gamesnd_play_iface(SND_SCROLL);
1602         } else {
1603                 gamesnd_play_iface(SND_GENERAL_FAIL);
1604         }
1605 }
1606
1607 void goal_screen_scroll_down()
1608 {
1609         int max_lines;
1610
1611         max_lines = Goal_screen_text_h / gr_get_font_height();
1612         if (Scroll_offset + max_lines < Goal_text.m_num_lines) {
1613                 Scroll_offset++;
1614                 gamesnd_play_iface(SND_SCROLL);
1615         } else {
1616                 gamesnd_play_iface(SND_GENERAL_FAIL);
1617         }
1618 }
1619
1620 // Return the number of resolved goals in num_resolved, return the total
1621 // number of valid goals in total
1622 void mission_goal_fetch_num_resolved(int desired_type, int *num_resolved, int *total, int team)
1623 {
1624         int i,type;
1625
1626         *num_resolved=0;
1627         *total=0;
1628
1629         for (i=0; i<Num_goals; i++) {
1630                 // if we're checking for team
1631                 if((team >= 0) && (Mission_goals[i].team != team)){
1632                         continue;
1633                 }
1634
1635                 if (Mission_goals[i].type & INVALID_GOAL) {
1636                         continue;
1637                 }
1638
1639                 type = Mission_goals[i].type & GOAL_TYPE_MASK;
1640                 if ( type != desired_type ) {
1641                         continue;
1642                 }
1643
1644                 *total = *total + 1;
1645
1646                 if (Mission_goals[i].satisfied != GOAL_INCOMPLETE) {
1647                         *num_resolved = *num_resolved + 1;
1648                 }
1649         }
1650 }
1651
1652 // Return whether there are any incomplete goals of the specified type
1653 int mission_goals_incomplete(int desired_type, int team)
1654 {
1655         int i, type;
1656
1657         for (i=0; i<Num_goals; i++) {           
1658                 // if we're checking for team
1659                 if((team >= 0) && (Mission_goals[i].team != team)){
1660                         continue;
1661                 }
1662
1663                 if (Mission_goals[i].type & INVALID_GOAL) {
1664                         continue;
1665                 }
1666
1667                 type = Mission_goals[i].type & GOAL_TYPE_MASK;
1668                 if ( type != desired_type ) {
1669                         continue;
1670                 }
1671
1672                 if (Mission_goals[i].satisfied == GOAL_INCOMPLETE) {
1673                         return 1;
1674                 }
1675         }
1676
1677         return 0;
1678 }
1679
1680 void mission_goal_exit()
1681 {
1682         snd_play( &Snds_iface[SND_USER_SELECT] );
1683         gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
1684
1685