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