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