2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
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
10 * $Logfile: /Freespace2/code/Mission/MissionGoals.cpp $
15 * Module for working with Mission goals
18 * Revision 1.6 2004/09/20 01:31:44 theoddone33
21 * Revision 1.5 2003/05/25 02:30:42 taylor
24 * Revision 1.4 2003/05/18 03:55:30 taylor
25 * automatic language selection support
27 * Revision 1.3 2002/06/09 04:41:22 relnev
28 * added copyright header
30 * Revision 1.2 2002/05/26 22:06:17 relnev
31 * makefile: disable stand_gui for now.
33 * rest: staticize some globals
35 * Revision 1.1.1.1 2002/05/03 03:28:09 root
39 * 15 10/27/99 5:22p Jefff
40 * Some coord changes for german ver
42 * 14 9/06/99 9:46p Jefff
43 * skip mission support
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.
49 * 12 8/26/99 8:51p Dave
50 * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
52 * 11 8/03/99 6:21p Jefff
53 * fixed stupid bug with objectives screen key
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.
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.
63 * 8 7/10/99 1:44p Andsager
64 * Modified directives listing so that current and recently
65 * satisfied/failed directives stay on screen.
67 * 7 2/17/99 2:10p Dave
68 * First full run of squad war. All freespace and tracker side stuff
71 * 6 2/02/99 4:35p Neilk
72 * fixed coordinate problem where primary goals was on top of interface in
75 * 5 1/30/99 5:08p Dave
76 * More new hi-res stuff.Support for nice D3D textures.
78 * 4 11/05/98 5:55p Dave
79 * Big pass at reducing #includes
81 * 3 10/13/98 9:28a Dave
82 * Started neatening up freespace.h. Many variables renamed and
83 * reorganized. Added AlphaColors.[h,cpp]
85 * 2 10/07/98 10:53a Dave
88 * 1 10/07/98 10:49a Dave
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
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
101 * 125 5/21/98 2:47a Lawrance
102 * Fix some problems with event music
104 * 124 4/15/98 9:05a Allender
105 * fix skpping of training mission with branchs
107 * 123 4/08/98 10:34p Allender
108 * make threat indicators work in multiplayer. Fix socket problem (once
111 * 122 4/03/98 2:47p Allender
112 * made directives act different when multiple waves of a wing take a long
115 * 121 4/01/98 9:21p John
116 * Made NDEBUG, optimized build with no warnings or errors.
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.
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
128 * 118 3/23/98 1:39p Hoffoss
129 * Fixed bug where long objects weren't being split at the right point.
131 * 117 3/17/98 12:40a Lawrance
132 * Add missing struct members to state
134 * 116 3/09/98 4:24p Lawrance
135 * Don't play directive sound effect if gauge is disabled
137 * 115 3/04/98 5:51p Hoffoss
140 * 114 3/02/98 9:30p Allender
141 * make sexpression evaluation for arrivals/departures and goals happen
144 * 113 3/02/98 10:08a Hoffoss
145 * Fixed bugs in saving/restoring state.
147 * 112 2/27/98 4:37p Hoffoss
148 * Combined Objectives screen into Mission Log screen.
150 * 111 2/26/98 10:07p Hoffoss
151 * Rewrote state saving and restoring to fix bugs and simplify the code.
153 * 110 2/23/98 3:06p Hoffoss
154 * Made objectives with only one target not show the [1] thing after it.
156 * 109 2/22/98 4:30p John
157 * More string externalization classification
159 * 108 2/22/98 12:19p John
160 * Externalized some strings
162 * 107 2/20/98 8:33p Lawrance
163 * Added mission_goals_incomplete()
165 * 106 2/06/98 12:12a Lawrance
166 * Play sound effect when continue is pressed.
168 * 105 2/05/98 10:14p Lawrance
169 * Implement Save and Quit
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
175 * 103 1/30/98 4:24p Hoffoss
176 * Added a 3 second delay for directives before they get displayed.
178 * 102 1/29/98 10:26a Hoffoss
179 * Made changes so arrow buttons repeat scrolling when held down.
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.
185 * 100 1/27/98 11:00a Lawrance
186 * Fix bug with showing number of resolved goals in the objective status
189 * 99 1/27/98 10:56a Allender
190 * slight changes to scoring stuff. Added work for Baranec :-)
192 * 98 1/26/98 10:02p Allender
195 * 97 1/20/98 2:26p Hoffoss
196 * Removed references to timestamp_ticker, used timestamp() instead.
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.
202 * 95 1/15/98 5:23p Lawrance
203 * Add HUD gauge to indicate completed objectives.
205 * 94 1/15/98 1:29p Hoffoss
206 * Made chained events only check next event if that event is also
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
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.
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.
222 * 90 1/10/98 1:14p John
223 * Added explanation to debug console commands
225 * 89 1/08/98 10:26a Lawrance
226 * Delay directive success sound effect about 1/2 second.
228 * 88 1/07/98 6:46p Lawrance
229 * If a goal is invalid, ignore it when evaluating mission success.
231 * 87 1/07/98 11:09a Lawrance
232 * Add sound hook for when directive gets completed.
234 * 86 1/02/98 3:07p Hoffoss
235 * Changed sexp evaluation to every half second.
237 * 85 12/27/97 8:08p Lawrance
238 * get savegames working again
240 * 84 12/26/97 10:02p Lawrance
241 * Add event music for when goals fail.
243 * 83 12/22/97 6:07p Hoffoss
244 * Made directives flash when completed, fixed but with is-destroyed
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.
252 * 81 12/19/97 12:43p Hoffoss
253 * Changed code to allow counts in directives.
255 * 80 12/15/97 5:26p Allender
256 * temporary code to display for 5 second completion status of objectives
258 * 79 12/03/97 4:16p Hoffoss
259 * Changed sound stuff used in interface screens for interface purposes.
261 * 78 12/03/97 11:35a Hoffoss
262 * Made changes to HUD messages send throughout the game.
264 * 77 12/02/97 10:47a Hoffoss
265 * Changed mention of goals to objectives.
267 * 76 12/01/97 12:26a Lawrance
268 * Add flag MGF_NO_MUSIC to mission_goal struct, to avoid playing music
271 * 75 11/21/97 2:16p Allender
272 * debug keys to mark all goals (p/s/b) as satisfied
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
278 * 73 11/13/97 10:16p Hoffoss
279 * Added icons to mission log scrollback.
281 * 72 11/13/97 4:05p Hoffoss
282 * Added hiding code for mission log entries.
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.
288 * 70 11/02/97 10:09p Lawrance
289 * add missing fields from mission_event to save/restore
291 * 69 10/31/97 4:28p Allender
292 * fail all incomplete mission goals when mission is over
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
298 * 67 10/28/97 10:05a Allender
299 * added function to mark all goals as failed
301 * 66 10/28/97 9:33a Lawrance
302 * change 'Alt-Q' in HUD message to use text for bound key
304 * 65 10/27/97 5:03p Hoffoss
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
311 * 63 10/16/97 2:35p Hoffoss
312 * Enhanced the mission goals screen a little.
314 * 62 10/16/97 1:28p Hoffoss
315 * New mission goals screen implemented.
317 * 61 10/10/97 6:15p Hoffoss
318 * Implemented a training objective list display.
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.
324 * 59 10/06/97 4:11p Lawrance
325 * add missing field from mission_events to save/restore
327 * 58 10/03/97 4:14p Hoffoss
328 * Augmented event debug view code.
330 * 57 9/30/97 10:01a Hoffoss
331 * Added event chaining support to Fred and FreeSpace.
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.
339 #include "freespace.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"
351 #include "linklist.h"
356 #include "eventmusic.h"
358 #include "multimsgs.h"
359 #include "stand_gui.h"
364 #include "alphacolors.h"
365 #include "multi_team.h"
366 #include "localize.h"
368 // timestamp stuff for evaluating mission goals
369 #define GOAL_TIMESTAMP 0 // make immediately eval
370 #define GOAL_TIMESTAMP_TRAINING 500 // every half second
372 #define MAX_GOALS_PER_LIST 15
373 #define MAX_GOAL_LINES 200
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
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
389 static int Goal_screen_text_coords[GR_NUM_RESOLUTIONS][4] = {
391 81,95,385,299 // GR_640
394 130,152,385,299 // GR_1024
398 static int Goal_screen_icon_xcoord[GR_NUM_RESOLUTIONS] = {
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] = {
419 static int Objective_key_icon_coords_gr[GR_NUM_RESOLUTIONS][3][2] = {
434 static int Objective_key_text_coords[GR_NUM_RESOLUTIONS][3][2] = {
448 static int Objective_key_icon_coords[GR_NUM_RESOLUTIONS][3][2] = {
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
472 mission_goal *list[MAX_GOALS_PER_LIST];
473 int line_offsets[MAX_GOALS_PER_LIST];
474 int line_spans[MAX_GOALS_PER_LIST];
476 goal_list() : count(0) {}
477 void add(mission_goal *m);
479 void icons_display(int y);
482 struct goal_buttons {
483 const char *filename;
486 UI_BUTTON button; // because we have a class inside this struct, we need the constructor below..
488 goal_buttons(const char *name, int x1, int y1, int h) : filename(name), x(x1), y(y1), hotspot(h) {}
493 int m_line_sizes[MAX_GOAL_LINES];
494 char *m_lines[MAX_GOAL_LINES];
497 int add(const char *text = NULL);
498 void display(int n, int y);
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;
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;
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
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
516 const char *Goal_type_text(int n)
520 return XSTR( "Primary", 396);
523 return XSTR( "Secondary", 397);
526 return XSTR( "Bonus", 398);
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;
538 static goal_list Primary_goal_list;
539 static goal_list Secondary_goal_list;
540 static goal_list Bonus_goal_list;
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;
549 goal_buttons Goal_buttons[NUM_GOAL_SCREEN_BUTTONS] = {
551 goal_buttons("MOB_00", 475, 288, 0),
552 goal_buttons("MOB_01", 475, 336, 1),
553 goal_buttons("MOB_02", 553, 409, 2),
557 void goal_screen_button_pressed(int num);
558 void goal_screen_scroll_up();
559 void goal_screen_scroll_down();
562 /// goal_list structure functions
565 void goal_list::add(mission_goal *m)
567 SDL_assert(count < MAX_GOALS_PER_LIST);
571 void goal_list::set()
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);
584 void goal_list::icons_display(int yoff)
586 int i, y, ys, bmp, font_height;
588 font_height = gr_get_font_height();
589 for (i=0; i<count; i++) {
590 y = line_offsets[i] - yoff;
592 bmp = -1; // initialize for safety.
593 switch (list[i]->satisfied) {
595 bmp = Goal_complete_bitmap;
598 case GOAL_INCOMPLETE:
599 bmp = Goal_incomplete_bitmap;
603 bmp = Goal_failed_bitmap;
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
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);
623 /// goal_text structure functions
626 // initializes the goal text struct (empties it out)
627 void goal_text::init()
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)
639 max = MAX_GOAL_LINES - m_num_lines;
641 Error(LOCATION, "Goal text space exhausted");
646 m_lines[m_num_lines] = NULL;
647 m_line_sizes[m_num_lines++] = 0;
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;
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)
662 char buf[MAX_GOAL_TEXT];
664 if ((n < 0) || (n >= m_num_lines) || (m_line_sizes[n] < 1))
665 return; // out of range, don't draw anything
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 = min(m_line_sizes[n], (int)sizeof(buf));
672 SDL_strlcpy(buf, m_lines[n] + 1, len);
674 gr_get_string_size(&w, &h, buf);
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);
680 gr_set_color_fast(&Color_text_normal);
681 len = min(m_line_sizes[n] + 1, (int)sizeof(buf));
682 SDL_strlcpy(buf, m_lines[n], len);
685 gr_printf(Goal_screen_text_x, y, buf);
688 // mission_init_goals: initializes info for goals. Called as part of mission initialization.
689 void mission_init_goals()
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;
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;
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
715 // called at the end of a mission for cleanup
716 void mission_event_shutdown()
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;
725 if (Mission_events[i].objective_key_text) {
726 free(Mission_events[i].objective_key_text);
727 Mission_events[i].objective_key_text = NULL;
732 // called once right before entering the show goals screen to do initializations.
733 void mission_show_goals_init()
735 int i, type, team_num=0; // JAS: I set team_num to 0 because it was used without being initialized.
739 Primary_goal_list.count = 0;
740 Secondary_goal_list.count = 0;
741 Bonus_goal_list.count = 0;
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];
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
755 if ( (Game_mode & GM_MULTIPLAYER) && (The_mission.game_type & MISSION_TYPE_MULTI_TEAMS) && (Mission_goals[i].team != team_num) ){
759 type = Mission_goals[i].type & GOAL_TYPE_MASK;
762 Primary_goal_list.add(&Mission_goals[i]);
766 Secondary_goal_list.add(&Mission_goals[i]);
770 if (Mission_goals[i].satisfied == GOAL_COMPLETE){
771 Bonus_goal_list.add(&Mission_goals[i]);
776 Error(LOCATION, "Unknown goal priority encountered when displaying goals in mission\n");
783 Goal_text.add(XSTR( "*Primary Objectives", 399));
785 Primary_goal_list.set();
787 if (Secondary_goal_list.count) {
790 Goal_text.add(XSTR( "*Secondary Objectives", 400));
792 Secondary_goal_list.set();
795 if (Bonus_goal_list.count) {
798 Goal_text.add(XSTR( "*Bonus Objectives", 401));
800 Bonus_goal_list.set();
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");
807 for (i=0; i<NUM_GOAL_SCREEN_BUTTONS; i++) {
808 b = &Goal_buttons[i];
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);
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);
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");
827 // if (Goal_incomplete_bitmap < 0) Int3();
829 if (Goals_screen_bg_bitmap < 0) {
830 Warning(LOCATION, "Could not load the background bitmap: ObjectivesBG.pcx");
834 // cleanup called when exiting the show goals screen
835 void mission_show_goals_close()
837 if (Goals_screen_bg_bitmap >= 0)
838 bm_unload(Goals_screen_bg_bitmap);
840 if (Goal_complete_bitmap)
841 bm_unload(Goal_complete_bitmap);
843 if (Goal_incomplete_bitmap)
844 bm_unload(Goal_incomplete_bitmap);
846 if (Goal_failed_bitmap)
847 bm_unload(Goal_failed_bitmap);
849 Goals_screen_ui_window.destroy();
850 common_free_interface_palette(); // restore game palette
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)
858 int font_height = gr_get_font_height();
860 k = Goals_screen_ui_window.process();
867 goal_screen_scroll_down();
871 goal_screen_scroll_up();
879 for (i=0; i<NUM_GOAL_SCREEN_BUTTONS; i++){
880 if (Goal_buttons[i].button.pressed()){
881 goal_screen_button_pressed(i);
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);
890 Goals_screen_ui_window.draw();
894 while (y + font_height <= Goal_screen_text_h) {
895 Goal_text.display(z, y);
900 Primary_goal_list.icons_display(Scroll_offset);
901 Secondary_goal_list.icons_display(Scroll_offset);
902 Bonus_goal_list.icons_display(Scroll_offset);
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)
911 int i, type, team_num;
913 Primary_goal_list.count = 0;
914 Secondary_goal_list.count = 0;
915 Bonus_goal_list.count = 0;
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;
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;
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
934 if ( (Game_mode & GM_MULTIPLAYER) && (The_mission.game_type & MISSION_TYPE_MULTI_TEAMS) && (Mission_goals[i].team != team_num) ){
938 type = Mission_goals[i].type & GOAL_TYPE_MASK;
941 Primary_goal_list.add(&Mission_goals[i]);
945 Secondary_goal_list.add(&Mission_goals[i]);
949 if (Mission_goals[i].satisfied == GOAL_COMPLETE){
950 Bonus_goal_list.add(&Mission_goals[i]);
955 Error(LOCATION, "Unknown goal priority encountered when displaying goals in mission\n");
962 Goal_text.add(XSTR( "*Primary Objectives", 399));
964 Primary_goal_list.set();
966 if (Secondary_goal_list.count) {
969 Goal_text.add(XSTR( "*Secondary Objectives", 400));
971 Secondary_goal_list.set();
974 if (Bonus_goal_list.count) {
977 Goal_text.add(XSTR( "*Bonus Objectives", 401));
979 Bonus_goal_list.set();
983 Goal_complete_bitmap = bm_load("ObjComp");
984 Goal_incomplete_bitmap = bm_load("ObjIncomp");
985 Goal_failed_bitmap = bm_load("ObjFail");
987 // if (Goal_incomplete_bitmap < 0) Int3();
989 return Goal_text.m_num_lines;
992 // cleanup called when exiting the show goals screen
993 void ML_objectives_close()
995 if (Goal_complete_bitmap >= 0) {
996 bm_unload(Goal_complete_bitmap);
999 if (Goal_incomplete_bitmap >= 0) {
1000 bm_unload(Goal_incomplete_bitmap);
1003 if (Goal_failed_bitmap >= 0) {
1004 bm_unload(Goal_failed_bitmap);
1008 void ML_objectives_do_frame(int scroll_offset)
1011 int font_height = gr_get_font_height();
1015 while (y + font_height <= Goal_screen_text_h) {
1016 Goal_text.display(z, y);
1021 Primary_goal_list.icons_display(scroll_offset);
1022 Secondary_goal_list.icons_display(scroll_offset);
1023 Bonus_goal_list.icons_display(scroll_offset);
1026 void ML_render_objectives_key()
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);
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]);
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));
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]);
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));
1057 // temporary hook for temporarily displaying objective completion/failure
1058 // extern void message_training_add_simple( char *text );
1060 void mission_goal_status_change( int goal_num, int new_status)
1064 SDL_assert(goal_num < Num_goals);
1065 SDL_assert((new_status == GOAL_FAILED) || (new_status == GOAL_COMPLETE));
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 );
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 ) {
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();
1084 //HUD_sourced_printf(HUD_SOURCE_FAILED, "%s goal failed at time %6.1f!", Goal_type_text(type), f2fl(Missiontime) );
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();
1095 mission_log_add_entry( LOG_GOAL_SATISFIED, Mission_goals[goal_num].name, NULL, goal_num );
1098 if(Game_mode & GM_MULTIPLAYER){
1100 multi_team_maybe_add_score((int)(Mission_goals[goal_num].score * scoring_get_scale_factor()), Mission_goals[goal_num].team);
1102 // deal with the score
1103 Player->stats.m_score += (int)(Mission_goals[goal_num].score * scoring_get_scale_factor());
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)
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 ) {
1118 // if this event is temporarily true, return as such
1119 if ( Mission_events[event].flags & MEF_DIRECTIVE_TEMP_TRUE ){
1120 return EVENT_SATISFIED;
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;
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();
1135 if (Mission_events[event].result) {
1136 return EVENT_SATISFIED;
1139 if (Mission_events[event].formula < 0) {
1140 return EVENT_FAILED;
1143 return EVENT_CURRENT;
1146 return EVENT_UNBORN;
1149 void mission_event_set_directive_special(int event)
1152 if((event < 0) || (event >= Num_mission_events)){
1156 Mission_events[event].flags |= MEF_DIRECTIVE_SPECIAL;
1158 // start from a known state
1159 Mission_events[event].flags &= ~MEF_DIRECTIVE_TEMP_TRUE;
1160 Mission_directive_special_timestamp = timestamp(DIRECTIVE_SPECIAL_DELAY);
1163 void mission_event_unset_directive_special(int event)
1166 if((event < 0) || (event >= Num_mission_events)){
1170 Mission_events[event].flags &= ~(MEF_DIRECTIVE_SPECIAL);
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;
1176 Mission_events[event].satisfied_time = 0;
1177 Mission_directive_special_timestamp = timestamp(-1);
1180 // function which evaluates and processes the given event
1181 void mission_process_event( int event )
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;
1190 Directive_count = 0;
1191 Event_index = event;
1192 sindex = Mission_events[event].formula;
1193 result = Mission_events[event].result;
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
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
1203 if ((event < Num_mission_events - 1) && Mission_events[event + 1].result && (Mission_events[event + 1].chain_delay >= 0)){
1204 sindex = -1; // bypass evaluation
1209 Sexp_useful_number = 1;
1210 result = eval_sexp(sindex);
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);
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);
1224 if (Mission_events[event].count || (Directive_count > 1)){
1225 Mission_events[event].count = Directive_count;
1228 if (Sexp_useful_number){
1229 Mission_events[event].flags |= MEF_CURRENT;
1234 Mission_events[event].result = result;
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;
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);
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;
1259 if(Game_mode & GM_MULTIPLAYER){
1261 multi_team_maybe_add_score((int)(Mission_events[event].score * scoring_get_scale_factor()), Mission_events[event].team);
1263 // deal with the player's score
1264 Player->stats.m_score += (int)(Mission_events[event].score * scoring_get_scale_factor());
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;
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);
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()
1284 if ( timestamp_elapsed(Mission_directive_sound_timestamp) ) {
1285 Mission_directive_sound_timestamp=0;
1286 snd_play( &Snds[SND_DIRECTIVE_COMPLETE] );
1290 void mission_eval_goals()
1292 int i, result;//, goal_changed = 0;
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) ){
1302 // if we get here, then the timestamp on the event has popped -- we should reevaluate
1303 mission_process_event(i);
1307 if ( !timestamp_elapsed(Mission_goal_timestamp) ){
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){
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 );
1324 } else if (result) {
1325 // goal_changed = 1;
1326 mission_goal_status_change(i, GOAL_COMPLETE );
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) );
1333 } // end if goals[i].satsified != GOAL_COMPLETE
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 );
1348 if (The_mission.game_type & MISSION_TYPE_TRAINING){
1349 Mission_goal_timestamp = timestamp(GOAL_TIMESTAMP_TRAINING);
1351 Mission_goal_timestamp = timestamp(GOAL_TIMESTAMP);
1354 if ( !hud_disabled() && hud_gauge_active(HUD_DIRECTIVES_VIEW) ) {
1355 mission_maybe_play_directive_success_sound();
1358 // update goal status if playing on a multiplayer standalone server
1359 if (Game_mode & GM_STANDALONE_SERVER){
1360 std_multi_update_goals();
1364 // evaluate_primary_goals() will determine if the primary goals for a mission are complete
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()
1370 int i, primary_goals_complete = PRIMARY_GOALS_COMPLETE;
1372 for (i=0; i<Num_goals; i++) {
1374 if ( Mission_goals[i].type & INVALID_GOAL ) {
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;
1387 return primary_goals_complete;
1390 // return 1 if all primary/secondary goals are complete... otherwise return 0
1391 int mission_goals_met()
1393 int i, all_goals_met = 1;
1395 for (i=0; i<Num_goals; i++) {
1397 if ( Mission_goals[i].type & INVALID_GOAL ) {
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 ) {
1405 } else if ( Mission_goals[i].satisfied == GOAL_FAILED ) {
1412 return all_goals_met;
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 )
1419 // only incomplete goals can have their status changed
1420 if ( Mission_goals[goal_num].satisfied != GOAL_INCOMPLETE ){
1424 // if in multiplayer, then send a packet
1425 if ( MULTIPLAYER_MASTER ){
1426 send_mission_goal_info_packet( goal_num, -1, valid );
1429 // change the valid status
1431 Mission_goals[goal_num].type &= ~INVALID_GOAL;
1433 Mission_goals[goal_num].type |= INVALID_GOAL;
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
1440 void mission_goal_mark_invalid( char *name )
1444 for (i=0; i<Num_goals; i++) {
1445 if ( !SDL_strcasecmp(Mission_goals[i].name, name) ) {
1446 mission_goal_validation_change( i, 0 );
1452 // the next function marks a goal as valid. A goal may always be marked valid.
1453 void mission_goal_mark_valid( char *name )
1457 for (i=0; i<Num_goals; i++) {
1458 if ( !SDL_strcasecmp(Mission_goals[i].name, name) ) {
1459 mission_goal_validation_change( i, 1 );
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()
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 );
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()
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 );
1491 // now for the events. Must set the formula to -1 and the result to 0 to be a failed
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;
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()
1507 for (i = 0; i < Num_goals; i++ ) {
1508 Mission_goals[i].satisfied = GOAL_COMPLETE;
1512 // small function used to mark all events as completed. Used in the skipping of missions.
1513 void mission_goal_mark_events_complete()
1517 for (i = 0; i < Num_mission_events; i++ ) {
1518 Mission_events[i].result = 1;
1519 Mission_events[i].formula = -1;
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")
1532 dc_printf("Usage: show_mission_goals\n\nList all mission goals and their current status.\n");
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");
1547 dc_printf("\t[unknown goal status].\n");
1553 DCF(change_mission_goal, "Change the mission goal")
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");
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;
1580 dc_printf("Unknown status %s. Use 'satisfied', 'failed', or 'unknown'\n", Dc_arg);
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");
1598 // debug functions to mark all primary/secondary/bonus goals as true
1601 void mission_goal_mark_all_true(int type)
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;
1610 HUD_sourced_printf(HUD_SOURCE_HIDDEN, NOX("All %s goals marked true"), Goal_type_text(type) );
1615 void goal_screen_button_pressed(int num)
1618 case GOAL_SCREEN_BUTTON_SCROLL_UP:
1619 goal_screen_scroll_up();
1622 case GOAL_SCREEN_BUTTON_SCROLL_DOWN:
1623 goal_screen_scroll_down();
1626 case GOAL_SCREEN_BUTTON_RETURN:
1627 mission_goal_exit();
1632 void goal_screen_scroll_up()
1634 if (Scroll_offset) {
1636 gamesnd_play_iface(SND_SCROLL);
1638 gamesnd_play_iface(SND_GENERAL_FAIL);
1642 void goal_screen_scroll_down()
1646 max_lines = Goal_screen_text_h / gr_get_font_height();
1647 if (Scroll_offset + max_lines < Goal_text.m_num_lines) {
1649 gamesnd_play_iface(SND_SCROLL);
1651 gamesnd_play_iface(SND_GENERAL_FAIL);
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)
1664 for (i=0; i<Num_goals; i++) {
1665 // if we're checking for team
1666 if((team >= 0) && (Mission_goals[i].team != team)){
1670 if (Mission_goals[i].type & INVALID_GOAL) {
1674 type = Mission_goals[i].type & GOAL_TYPE_MASK;
1675 if ( type != desired_type ) {
1679 *total = *total + 1;
1681 if (Mission_goals[i].satisfied != GOAL_INCOMPLETE) {
1682 *num_resolved = *num_resolved + 1;
1687 // Return whether there are any incomplete goals of the specified type
1688 int mission_goals_incomplete(int desired_type, int team)
1692 for (i=0; i<Num_goals; i++) {
1693 // if we're checking for team
1694 if((team >= 0) && (Mission_goals[i].team != team)){
1698 if (Mission_goals[i].type & INVALID_GOAL) {
1702 type = Mission_goals[i].type & GOAL_TYPE_MASK;
1703 if ( type != desired_type ) {
1707 if (Mission_goals[i].satisfied == GOAL_INCOMPLETE) {
1715 void mission_goal_exit()
1717 snd_play( &Snds_iface[SND_USER_SELECT] );
1718 gameseq_post_event(GS_EVENT_PREVIOUS_STATE);