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