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