]> icculus.org git repositories - taylor/freespace2.git/blob - src/parse/sexp.cpp
clean up and simplify effects and updating
[taylor/freespace2.git] / src / parse / sexp.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/parse/SEXP.CPP $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * main sexpression generator
16  *
17  * $Log$
18  * Revision 1.5  2003/05/25 02:30:43  taylor
19  * Freespace 1 support
20  *
21  * Revision 1.4  2002/06/17 06:15:25  relnev
22  * ryan's struct patch (and cr removal)
23  *
24  * Revision 1.3  2002/06/09 04:41:25  relnev
25  * added copyright header
26  *
27  * Revision 1.2  2002/05/07 03:16:48  theoddone33
28  * The Great Newline Fix
29  *
30  * Revision 1.1.1.1  2002/05/03 03:28:10  root
31  * Initial import.
32  *
33  * 
34  * 66    9/13/99 11:38a Andsager
35  * Stupid switch for sexp_team_score.  Don't forget *break*
36  * 
37  * 65    9/09/99 11:40p Dave
38  * Handle an SDL_assert() in beam code. Added supernova sounds. Play the right
39  * 2 end movies properly, based upon what the player did in the mission.
40  * 
41  * 64    9/07/99 1:05a Andsager
42  * Added team-score sexp for multi team vs team missions
43  * 
44  * 63    9/06/99 9:46p Jefff
45  * skip mission support
46  * 
47  * 62    8/29/99 2:33p Andsager
48  * Fix bug in sexp_is_ship_visible()
49  * 
50  * 61    8/27/99 9:07p Dave
51  * LOD explosions. Improved beam weapon accuracy.
52  * 
53  * 60    8/27/99 4:07p Andsager
54  * Add is-ship-visible sexp.  Make ship-vanish sexp SINGLE player only
55  * 
56  * 59    8/26/99 4:21p Mikek
57  * Add new special-check for JimB, zeroing front shield quadrant.
58  * 
59  * 58    8/24/99 4:25p Andsager
60  * Add ship-vanish sexp
61  * 
62  * 57    8/23/99 6:05p Johnson
63  * Oops. Self destruct log should also go into the is-destroyed
64  * sexpression.
65  * 
66  * 56    8/23/99 3:23p Johnson
67  * Fix bug in sexp_facing2 when Player_obj does not yet existl
68  * 
69  * 55    8/19/99 3:30p Andsager
70  * Make sexp_special_warp_dist check if ship if facing knossos device
71  * 
72  * 54    8/19/99 9:20a Andsager
73  * Enable flashing for all guages
74  * 
75  * 53    8/19/99 12:31a Andsager
76  * Add support guage as one that can flash
77  * 
78  * 52    8/18/99 12:09p Andsager
79  * Add debug if message has no anim for message.  Make messages come from
80  * wing leader.
81  * 
82  * 51    8/16/99 10:04p Andsager
83  * Add special-warp-dist and special-warpout-name sexp for Knossos device
84  * warpout.
85  * 
86  * 50    8/09/99 3:32p Andsager
87  * Make has_time_elapsed work with arithmatic args.
88  * 
89  * 49    8/09/99 2:00p Dave
90  * 2 new sexpressions.
91  * 
92  * 48    8/02/99 4:26p Dave
93  * Added 2 new sexpressions.
94  * 
95  * 47    7/31/99 2:30p Dave
96  * Added nifty mission message debug viewing keys.
97  * 
98  * 46    7/28/99 1:36p Andsager
99  * Modify cargo1 to include flag CARGO_NO_DEPLETE.  Add sexp
100  * cargo-no-deplete (only for BIG / HUGE).  Modify ship struct to pack
101  * better.
102  * 
103  * 45    7/24/99 4:56p Dave
104  * Added 3 new sexpressions.
105  * 
106  * 44    7/21/99 8:10p Dave
107  * First run of supernova effect.
108  * 
109  * 43    7/20/99 9:19p Andsager
110  * Added facing waypoint sexp
111  * 
112  * 42    7/20/99 9:54a Andsager
113  * Add subsys-set-random sexp
114  * 
115  * 41    7/19/99 12:02p Andsager
116  * Allow AWACS on any ship subsystem. Fix sexp_set_subsystem_strength to
117  * only blow up subsystem if its strength is > 0
118  * 
119  * 40    7/15/99 9:22p Andsager
120  * modify secondaries_depleted sexp to report true only when all
121  * secondaries gone
122  * 
123  * 39    7/13/99 3:37p Andsager
124  * Add secondaries-depleted sexp
125  * 
126  * 38    7/08/99 12:06p Andsager
127  * Add turret-tagged-only and turret-tagged-clear sexp.
128  * 
129  * 37    6/28/99 4:51p Andsager
130  * Add ship-guardian sexp (does not allow ship to be killed)
131  * 
132  * 36    6/25/99 2:51p Andsager
133  * Make event_delay, goal_delay, and subsys strength work with sexp_rand
134  * 
135  * 35    6/23/99 5:51p Andsager
136  * Add waypoint-cap-speed.  Checkin stealth ai - inactive.
137  * 
138  * 34    6/16/99 10:21a Dave
139  * Added send-message-list sexpression.
140  * 
141  * 33    6/15/99 2:53p Andsager
142  * Fixed subsystem checking for AWACS type subsystems.
143  * 
144  * 32    6/01/99 8:35p Dave
145  * Finished lockarm weapons. Added proper supercap weapons/damage. Added
146  * awacs-set-radius sexpression.
147  * 
148  * 31    5/27/99 12:14p Andsager
149  * Some fixes for live debris when more than one subsys on ship with live
150  * debris.  Set subsys strength (when 0) blows off subsystem.
151  * sexp_hits_left_subsystem works for SUBSYSTEM_UNKNOWN.
152  * 
153  * 30    5/24/99 11:28a Dave
154  * Sexpression for adding/removing ships from the hud escort list.
155  * 
156  * 29    4/28/99 9:33a Andsager
157  * Add turret-free and turret-lock (and -all) sexp.  Stargger start time
158  * of beam weapons beam-free and beam-free-all.
159  * 
160  * 28    4/26/99 2:14p Andsager
161  * Add beam-protect-ship and beam-unprotect-ship sexp.
162  * 
163  * 27    4/23/99 12:01p Johnson
164  * Added SIF_HUGE_SHIP
165  * 
166  * 26    4/23/99 9:45a Andsager
167  * Modify rand_sexp to return the same value each time it is called on a
168  * particular node.  This prevents random time delay defaulting to min.
169  * 
170  * 25    4/21/99 6:15p Dave
171  * Did some serious housecleaning in the beam code. Made it ready to go
172  * for anti-fighter "pulse" weapons. Fixed collision pair creation. Added
173  * a handy macro for recalculating collision pairs for a given object.
174  * 
175  * 24    4/19/99 2:28p Johnson
176  * DA:  Use ship_info flags, not ship_flags for ship type.  DOH!!
177  * 
178  * 23    4/19/99 11:46a Johnson
179  * DA: DOH!
180  * 
181  * 22    4/19/99 11:36a Johnson
182  * Don't display multiple warning messges when large ship gets more than 1
183  * cargo transfer.
184  * 
185  * 21    4/02/99 9:55a Dave
186  * Added a few more options in the weapons.tbl for beam weapons. Attempt
187  * at putting "pain" packets into multiplayer.
188  * 
189  * 20    3/31/99 2:22p Johnson
190  * Fix sexp_repair_subsystem() to accept sexp increments
191  * 
192  * 19    3/28/99 5:58p Dave
193  * Added early demo code. Make objects move. Nice and framerate
194  * independant, but not much else. Don't use yet unless you're me :)
195  * 
196  * 18    3/20/99 3:46p Dave
197  * Added support for model-based background nebulae. Added 3 new
198  * sexpressions.
199  * 
200  * 17    3/08/99 7:03p Dave
201  * First run of new object update system. Looks very promising.
202  * 
203  * 16    3/04/99 6:09p Dave
204  * Added in sexpressions for firing beams and checking for if a ship is
205  * tagged.
206  * 
207  * 15    3/04/99 9:22a Andsager
208  * Make escort list work with ship-is-visible.  When not visible, dump,
209  * when becoming visible, maybe add.
210  * 
211  * 14    2/26/99 6:01p Andsager
212  * Add sexp has-been-tagged-delay and cap-subsys-cargo-known-delay
213  * 
214  * 13    2/26/99 4:14p Dave
215  * Put in the ability to have multiple shockwaves for ships.
216  * 
217  * 12    2/11/99 5:22p Andsager
218  * Fixed bugs, generalized block Sexp_variables
219  * 
220  * 11    2/11/99 2:15p Andsager
221  * Add ship explosion modification to FRED
222  * 
223  * 10    1/25/99 4:29p Andsager
224  * Modify sexp_modify_variable() to handle type string. added quick out
225  * for multiplayer_client
226  * 
227  * 9     1/25/99 8:11a Andsager
228  * Add sexp_modify_variable().  Changed syntax checking to allow, adding
229  * operator return type ambiguous
230  * 
231  * 8     1/24/99 11:37p Dave
232  * First full rev of beam weapons. Very customizable. Removed some bogus
233  * Int3()'s in low level net code.
234  * 
235  * 7     1/20/99 9:37a Andsager
236  * 
237  * 6     1/19/99 3:57p Andsager
238  * Round 2 of variables
239  * 
240  * 5     1/07/99 1:52p Andsager
241  * Initial check in of Sexp_variables
242  * 
243  * 4     11/05/98 5:55p Dave
244  * Big pass at reducing #includes
245  * 
246  * 3     10/13/98 9:29a Dave
247  * Started neatening up freespace.h. Many variables renamed and
248  * reorganized. Added AlphaColors.[h,cpp]
249  * 
250  * 2     10/07/98 10:53a Dave
251  * Initial checkin.
252  * 
253  * 1     10/07/98 10:50a Dave
254  * 
255  * 321   9/20/98 7:20p Dave
256  * Added CHANGE_IFF packet. 
257  * 
258  * 320   9/16/98 10:42a Hoffoss
259  * Added sexp node counting to fsm files for end user designers.
260  * 
261  * 319   7/06/98 4:31p Hoffoss
262  * Fixed bug in sexp parsing when formula is ( ).
263  * 
264  * 318   6/09/98 5:15p Lawrance
265  * French/German localization
266  * 
267  * 317   5/26/98 11:54a Allender
268  * fix multiplayer problems and sexpression crash
269  * 
270  * 316   5/23/98 3:16p Allender
271  * work on object update packet optimizations (a new updating system).
272  * Don't allow medals/promotions/badges when playing singple player
273  * missions through the simulator
274  * 
275  * 315   5/20/98 1:34a Allender
276  * fixed observer problems.  Removed some code offending BoundsCheccker.
277  * Fixed medals for multiplayer
278  * 
279  * 314   5/17/98 12:46p Mike
280  * Change special-check functions for cueing rearm suggestion.
281  * 
282  * 313   5/14/98 6:29p Hoffoss
283  * Fixed some warnings a release rebuild all turned up.
284  * 
285  * 312   5/14/98 10:15a Allender
286  * add optional argument to prevous-goal/event operators to specify what
287  * sexpression should return when being played as a single mission
288  * 
289  * 311   5/08/98 10:16a Lawrance
290  * Add new "ship attacking count" gauge
291  * 
292  * 310   4/29/98 11:39p Allender
293  * fixed nasty problem with is-event-false always returning true right
294  * away
295  * 
296  * 
297  */
298
299 //      Parse a symbolic expression.
300 //      These are identical to Lisp functions.
301 //      It uses a very baggy format, allocating 16 characters per token, regardless
302 //      of how many are used.
303
304 #include        <stdio.h>
305 #include <stdlib.h>
306 #include <string.h>
307 #include <ctype.h>
308 #include <assert.h>
309 #include <limits.h>
310
311 #include "parselo.h"
312 #include "sexp.h"
313 #include "ship.h"
314 #include "freespace.h"
315 #include "fix.h"
316 #include "weapon.h"
317 #include "missionlog.h"
318 #include "missionparse.h"               // for p_object definition
319 #include "missionmessage.h"
320 #include "missiontraining.h"
321 #include "vecmat.h"
322 #include "linklist.h"
323 #include "model.h"                              // for subsystem types
324 #include "aigoals.h"
325 #include "missioncampaign.h"
326 #include "missiongoals.h"
327 #include "controlsconfig.h"
328 #include "timer.h"
329 #include "shiphit.h"
330 #include "gamesequence.h"
331 #include "scoring.h"
332 #include "medals.h"
333 #include "player.h"
334 #include "hudmessage.h"
335 #include "hud.h"
336 #include "redalert.h"
337 #include "jumpnode.h"
338 #include "hudshield.h"
339 #include "multi.h"
340 #include "multimsgs.h"
341 #include "multiutil.h"
342 #include "hudescort.h"
343 #include "beam.h"
344 #include "supernova.h"
345 #include "hudets.h"
346 #include "fvi.h"
347 #include "awacs.h"
348 #include "multi_team.h"
349
350 #define TRUE    1
351 #define FALSE   0
352
353 sexp_oper Operators[] = {
354 //   Operator, Identity, Min / Max arguments
355         { "+",          OP_PLUS,                2,      INT_MAX },
356         { "-",          OP_MINUS,       2,      INT_MAX },
357         { "*",          OP_MUL,         2,      INT_MAX },
358         { "/",          OP_DIV,         2,      INT_MAX },
359         { "mod",                OP_MOD,         2,      INT_MAX },
360         { "rand",       OP_RAND,                2,      2                       },
361
362         { "true",                                                       OP_TRUE,                                                        0,      0,                      },
363         { "false",                                                      OP_FALSE,                                               0,      0,                      },
364         { "and",                                                                OP_AND,                                                 2,      INT_MAX,        },
365         { "and-in-sequence",                            OP_AND_IN_SEQUENCE,                     2, INT_MAX, },
366         { "or",                                                         OP_OR,                                                  2,      INT_MAX,        },
367         { "not",                                                                OP_NOT,                                                 1, 1,                   },
368         { "=",                                                          OP_EQUALS,                                              2,      INT_MAX,        },
369         { ">",                                                          OP_GREATER_THAN,                                2,      2,                      },
370         { "<",                                                          OP_LESS_THAN,                                   2,      2,                      },
371         { "is-iff",                                                     OP_IS_IFF,                                              2, INT_MAX,     },
372         { "has-time-elapsed",                   OP_HAS_TIME_ELAPSED,                    1,      1,                      },
373         { "modify-variable",                            OP_MODIFY_VARIABLE,                     2,      2,                      },
374
375         { "is-goal-incomplete",                                 OP_GOAL_INCOMPLETE,                             1, 1,   },
376         { "is-goal-true-delay",                                 OP_GOAL_TRUE_DELAY,                             2, 2,   },
377         { "is-goal-false-delay",                                OP_GOAL_FALSE_DELAY,                            2, 2,   },
378         { "is-event-incomplete",                                OP_EVENT_INCOMPLETE,                            1, 1,   },
379         { "is-event-true-delay",                                OP_EVENT_TRUE_DELAY,                            2, 2,   },
380         { "is-event-false-delay",                               OP_EVENT_FALSE_DELAY,                   2, 2,   },
381         { "is-previous-goal-incomplete",                OP_PREVIOUS_GOAL_INCOMPLETE,    2, 3,   },
382         { "is-previous-goal-true",                              OP_PREVIOUS_GOAL_TRUE,                  2, 3,   },
383         { "is-previous-goal-false",                     OP_PREVIOUS_GOAL_FALSE,                 2, 3,   },
384         { "is-previous-event-incomplete",       OP_PREVIOUS_EVENT_INCOMPLETE,   2, 3,   },
385         { "is-previous-event-true",                     OP_PREVIOUS_EVENT_TRUE,                 2, 3,   },
386         { "is-previous-event-false",                    OP_PREVIOUS_EVENT_FALSE,                2, 3,   },
387
388         { "is-destroyed",                                                       OP_IS_DESTROYED,                                                1,      INT_MAX,        },
389         { "is-subsystem-destroyed",                     OP_IS_SUBSYSTEM_DESTROYED,                      2, 2,                   },
390         { "is-disabled",                                                        OP_IS_DISABLED,                                         1, INT_MAX,     },
391         { "is-disarmed",                                                        OP_IS_DISARMED,                                         1, INT_MAX,     },
392         { "has-docked",                                                 OP_HAS_DOCKED,                                                  3, 3,                   },
393         { "has-undocked",                                                       OP_HAS_UNDOCKED,                                                3, 3,                   },
394         { "has-arrived",                                                        OP_HAS_ARRIVED,                                         1, INT_MAX,     },
395         { "has-departed",                                                       OP_HAS_DEPARTED,                                                1, INT_MAX,     },
396         { "is-destroyed-delay",                                 OP_IS_DESTROYED_DELAY,                          2,      INT_MAX,        },
397         { "is-subsystem-destroyed-delay",       OP_IS_SUBSYSTEM_DESTROYED_DELAY,        3, 3,                   },
398         { "is-disabled-delay",                                  OP_IS_DISABLED_DELAY,                           2, INT_MAX,     },
399         { "is-disarmed-delay",                                  OP_IS_DISARMED_DELAY,                           2, INT_MAX,     },
400         { "has-docked-delay",                                   OP_HAS_DOCKED_DELAY,                                    4, 4,                   },
401         { "has-undocked-delay",                                 OP_HAS_UNDOCKED_DELAY,                          4, 4,                   },
402         { "has-arrived-delay",                                  OP_HAS_ARRIVED_DELAY,                           2, INT_MAX,     },
403         { "has-departed-delay",                                 OP_HAS_DEPARTED_DELAY,                          2, INT_MAX,     },
404         { "are-waypoints-done",                                 OP_WAYPOINTS_DONE,                                      2, 2,                   },
405         { "are-waypoints-done-delay",                   OP_WAYPOINTS_DONE_DELAY,                        3, 3,                   },
406         { "ship-type-destroyed",                                OP_SHIP_TYPE_DESTROYED,                         2, 2,                   },
407         { "is-event-true",                                              OP_EVENT_TRUE,                                                  1, 1,                   },
408         { "is-event-false",                                             OP_EVENT_FALSE,                                         1, 1,                   },
409         { "is-cargo-known",                                             OP_IS_CARGO_KNOWN,                                      1, INT_MAX,     },
410         { "was-promotion-granted",                              OP_WAS_PROMOTION_GRANTED,                       0, 1,                   },
411         { "was-medal-granted",                                  OP_WAS_MEDAL_GRANTED,                           0, 1,                   },
412         { "percent-ships-departed",                     OP_PERCENT_SHIPS_DEPARTED,                      2, INT_MAX      },
413         { "percent-ships-destroyed",                    OP_PERCENT_SHIPS_DESTROYED,             2, INT_MAX      },
414         { "is-cargo-known-delay",                               OP_CARGO_KNOWN_DELAY,                           2, INT_MAX,     },
415         { "cap-subsys-cargo-known-delay",       OP_CAP_SUBSYS_CARGO_KNOWN_DELAY,        3, INT_MAX,     },
416         { "has-been-tagged-delay",                              OP_HAS_BEEN_TAGGED_DELAY,                       2, INT_MAX,     },
417         { "depart-node-delay",                                  OP_DEPART_NODE_DELAY,                           3, INT_MAX, },  
418         { "destroyed-or-departed-delay",                OP_DESTROYED_DEPARTED_DELAY,            2, INT_MAX, },  
419         { "is_tagged",                                                          OP_IS_TAGGED,                                                   1, 1                    },
420         { "num_kills",                                                          OP_NUM_KILLS,                                                   1, 1                    },
421         { "num_type_kills",                                             OP_NUM_TYPE_KILLS,                                      2,      2                       },
422         { "num_class_kills",                                            OP_NUM_CLASS_KILLS,                                     2,      2                       },
423         { "shield-recharge-pct",                                OP_SHIELD_RECHARGE_PCT,                         1, 1                    },
424         { "engine-recharge-pct",                                OP_ENGINE_RECHARGE_PCT,                         1, 1                    },
425         { "weapon-recharge-pct",                                OP_WEAPON_RECHARGE_PCT,                         1, 1                    },
426         { "shield-quad-low",                                            OP_SHIELD_QUAD_LOW,                                     2,      2                       },
427         { "secondary-ammo-pct",                                 OP_SECONDARY_AMMO_PCT,                          2,      2                       },
428         { "is-secondary-selected",                              OP_IS_SECONDARY_SELECTED,                       2,      2                       },
429         { "is-primary-selected",                                OP_IS_PRIMARY_SELECTED,                         2,      2                       },
430
431         { "time-ship-destroyed",        OP_TIME_SHIP_DESTROYED, 1,      1,      },
432         { "time-ship-arrived",          OP_TIME_SHIP_ARRIVED,   1,      1,      },
433         { "time-ship-departed",         OP_TIME_SHIP_DEPARTED,  1,      1,      },
434         { "time-wing-destroyed",        OP_TIME_WING_DESTROYED, 1,      1,      },
435         { "time-wing-arrived",          OP_TIME_WING_ARRIVED,   1,      1,      },
436         { "time-wing-departed",         OP_TIME_WING_DEPARTED,  1,      1,      },
437         { "mission-time",                               OP_MISSION_TIME,                        0, 0,   },
438         { "time-docked",                                OP_TIME_DOCKED,                 3, 3, },
439         { "time-undocked",                      OP_TIME_UNDOCKED,                       3, 3, },
440
441         { "shields-left",                                       OP_SHIELDS_LEFT,                                1, 1, },
442         { "hits-left",                                          OP_HITS_LEFT,                                   1, 1, },
443         { "hits-left-subsystem",                OP_HITS_LEFT_SUBSYSTEM,         2, 2, },
444         { "distance",                                           OP_DISTANCE,                                    2, 2, },
445         { "is-ship-visible",                            OP_IS_SHIP_VISIBLE,                     1, 1, },
446         { "team-score",                                 OP_TEAM_SCORE,                                  1,      1,      }, 
447         { "time-elapsed-last-order",    OP_LAST_ORDER_TIME,                     2, 2, /*INT_MAX*/ },
448         { "skill-level-at-least",               OP_SKILL_LEVEL_AT_LEAST,        1, 1, },
449         { "num-players",                                        OP_NUM_PLAYERS,                         0, 0, },
450         { "special-warp-dist",                  OP_SPECIAL_WARP_DISTANCE,       1, 1,   },
451
452         { "special-warpout-name",               OP_SET_SPECIAL_WARPOUT_NAME,    2, 2 },
453
454         { "do-nothing", OP_NOP, 0, 0,                   },
455         { "when",                       OP_WHEN,        2, INT_MAX, },
456         { "cond",                       OP_COND, 1, INT_MAX, },
457
458         { "add-goal",                                           OP_ADD_GOAL,                                            2, 2,                   },
459         { "add-ship-goal",                              OP_ADD_SHIP_GOAL,                                       2, 2,                   },
460         { "add-wing-goal",                              OP_ADD_WING_GOAL,                                       2, 2,                   },
461         { "clear-goals",                                        OP_CLEAR_GOALS,                                 1, INT_MAX,     },
462         { "clear-ship-goals",                   OP_CLEAR_SHIP_GOALS,                            1, 1,                   },
463         { "clear-wing-goals",                   OP_CLEAR_WING_GOALS,                            1, 1,                   },
464         { "change-iff",                                 OP_CHANGE_IFF,                                          2,      INT_MAX,        },
465         { "protect-ship",                                       OP_PROTECT_SHIP,                                        1, INT_MAX,     },
466         { "unprotect-ship",                             OP_UNPROTECT_SHIP,                              1, INT_MAX,     },
467         { "beam-protect-ship",                  OP_BEAM_PROTECT_SHIP,                   1, INT_MAX,     },
468         { "beam-unprotect-ship",                OP_BEAM_UNPROTECT_SHIP,                 1, INT_MAX,     },
469         { "send-message",                                       OP_SEND_MESSAGE,                                        3, 3,                   },
470         { "self-destruct",                              OP_SELF_DESTRUCT,                                       1, INT_MAX,     },
471         { "next-mission",                                       OP_NEXT_MISSION,                                        1, 1,                   },
472         { "end-campaign",                                       OP_END_CAMPAIGN,                                        0, 0,                   },
473         { "end-of-campaign",                            OP_END_OF_CAMPAIGN,                             0, 0,                   },
474         { "sabotage-subsystem",                 OP_SABOTAGE_SUBSYSTEM,                  3, 3,                   },
475         { "repair-subsystem",                   OP_REPAIR_SUBSYSTEM,                            3, 3,                   },
476         { "set-subsystem-strength",     OP_SET_SUBSYSTEM_STRNGTH,               3, 3,                   },
477         { "invalidate-goal",                            OP_INVALIDATE_GOAL,                             1, INT_MAX,     },
478         { "validate-goal",                              OP_VALIDATE_GOAL,                                       1, INT_MAX,     },
479         { "send-random-message",                OP_SEND_RANDOM_MESSAGE,                 3, INT_MAX,     },
480         { "transfer-cargo",                             OP_TRANSFER_CARGO,                              2, 2,                   },
481         { "exchange-cargo",                             OP_EXCHANGE_CARGO,                              2, 2,                   },
482         { "end-misison-delay",                  OP_END_MISSION_DELAY,                   1, 1,                   },
483         { "good-rearm-time",                            OP_GOOD_REARM_TIME,                             2,      2,                      },
484         { "grant-promotion",                            OP_GRANT_PROMOTION,                             0, 0,                   },
485         { "grant-medal",                                        OP_GRANT_MEDAL,                                 1, 1,                   },
486         { "allow-ship",                                 OP_ALLOW_SHIP,                                          1, 1,                   },
487         { "allow-weapon",                                       OP_ALLOW_WEAPON,                                        1, 1,                   },
488         { "break-warp",                                 OP_WARP_BROKEN,                                 1, INT_MAX,     },
489         { "fix-warp",                                           OP_WARP_NOT_BROKEN,                             1, INT_MAX,     },
490         { "never-warp",                                 OP_WARP_NEVER,                                          1, INT_MAX, },
491         { "allow-warp",                                 OP_WARP_ALLOWED,                                        1, INT_MAX, },
492         { "good-secondary-time",                OP_GOOD_SECONDARY_TIME,                 4, 4,                   },
493         { "ship-invisible",                             OP_SHIP_INVISIBLE,                              1, INT_MAX      },
494         { "ship-visible",                                       OP_SHIP_VISIBLE,                                        1, INT_MAX      },
495         { "ship-invulnerable",                  OP_SHIP_INVULNERABLE,                   1, INT_MAX      },
496         { "ship-vulnerable",                            OP_SHIP_VULNERABLE,                             1, INT_MAX      },
497         { "red-alert",                                          OP_RED_ALERT,                                           0, 0                    },
498         { "tech-add-ships",                             OP_TECH_ADD_SHIP,                                       1, INT_MAX      },
499         { "tech-add-weapons",                   OP_TECH_ADD_WEAPON,                             1, INT_MAX      },
500         { "jettison-cargo-delay",               OP_JETTISON_CARGO,                              2, 2                    },
501         { "fire-beam",                                          OP_BEAM_FIRE,                                           3, 4                    },
502         { "beam-free",                                          OP_BEAM_FREE,                                           2, INT_MAX      },
503         { "beam-free-all",                              OP_BEAM_FREE_ALL,                                       1, 1                    },
504         { "beam-lock",                                          OP_BEAM_LOCK,                                           2, INT_MAX      },
505         { "beam-lock-all",                              OP_BEAM_LOCK_ALL,                                       1, 1                    },
506         { "turret-free",                                        OP_TURRET_FREE,                                 2, INT_MAX      },
507         { "turret-free-all",                            OP_TURRET_FREE_ALL,                             1, 1                    },
508         { "turret-lock",                                        OP_TURRET_LOCK,                                 2, INT_MAX      },
509         { "turret-lock-all",                            OP_TURRET_LOCK_ALL,                             1, 1                    },
510         { "add-remove-escort",                  OP_ADD_REMOVE_ESCORT,                   2, 2                    },
511         { "awacs-set-radius",                   OP_AWACS_SET_RADIUS,                            3,      3                       },
512         { "send-message-list",                  OP_SEND_MESSAGE_LIST,                   4, INT_MAX      },
513         { "cap-waypoint-speed",                 OP_CAP_WAYPOINT_SPEED,                  2, 2                    },
514         { "ship-guardian",                              OP_SHIP_GUARDIAN,                                       1, INT_MAX      },
515         { "ship-no-guardian",                   OP_SHIP_NO_GUARDIAN,                            1, INT_MAX      },
516         { "ship-vanish",                                        OP_SHIP_VANISH,                                 1, INT_MAX      },
517         { "turret-tagged-only",                 OP_TURRET_TAGGED_ONLY_ALL,              1,      1                       },
518         { "turret-tagged-clear",                OP_TURRET_TAGGED_CLEAR_ALL,     1,      1                       },
519         { "subsys-set-random",                  OP_SUBSYS_SET_RANDOM,                   3, INT_MAX      },
520         { "supernova-start",                            OP_SUPERNOVA_START,                             1,      1                       },
521         { "cargo-no-deplete",                   OP_CARGO_NO_DEPLETE,                            1,      2                       },
522
523         { "error",      OP_INT3,        0, 0 },
524
525         { "ai-chase",                                   OP_AI_CHASE,                                    2, 2, },
526         { "ai-chase-wing",                      OP_AI_CHASE_WING,                               2, 2, },
527         { "ai-dock",                                    OP_AI_DOCK,                                             4, 4, },
528         { "ai-undock",                                  OP_AI_UNDOCK,                                   1, 1, },
529         { "ai-warp",                                    OP_AI_WARP,                                             2, 2, },
530         { "ai-warp-out",                                OP_AI_WARP_OUT,                         1, 1, },
531         { "ai-waypoints",                               OP_AI_WAYPOINTS,                                2, 2, },
532         { "ai-waypoints-once",          OP_AI_WAYPOINTS_ONCE,           2, 2, },
533         { "ai-destroy-subsystem",       OP_AI_DESTROY_SUBSYS,           3, 3, },
534         { "ai-disable-ship",                    OP_AI_DISABLE_SHIP,                     2, 2, },
535         { "ai-disarm-ship",                     OP_AI_DISARM_SHIP,                      2, 2, },
536         { "ai-guard",                                   OP_AI_GUARD,                                    2, 2, },
537         { "ai-chase-any",                               OP_AI_CHASE_ANY,                                1, 1, },
538         { "ai-guard-wing",                      OP_AI_GUARD_WING,                               2, 2, },
539         { "ai-evade-ship",                      OP_AI_EVADE_SHIP,                               2, 2,   },
540         { "ai-stay-near-ship",          OP_AI_STAY_NEAR_SHIP,           2, 2, },
541         { "ai-keep-safe-distance",      OP_AI_KEEP_SAFE_DISTANCE,       1, 1, },
542         { "ai-ignore",                                  OP_AI_IGNORE,                                   2, 2, },
543         { "ai-stay-still",                      OP_AI_STAY_STILL,                               2, 2, },
544         { "ai-play-dead",                               OP_AI_PLAY_DEAD,                                1, 1, },
545
546         { "goals",      OP_GOALS_ID,    1, INT_MAX, },
547
548         { "key-pressed",                                OP_KEY_PRESSED,                         1,      2,                      },
549         { "key-reset",                                  OP_KEY_RESET,                                   1, INT_MAX,     },
550         { "targeted",                                   OP_TARGETED,                                    1, 3,                   },
551         { "speed",                                              OP_SPEED,                                               1, 1,                   },
552         { "facing",                                             OP_FACING,                                              2, 2,                   },
553         { "facing-waypoint",                    OP_FACING2,                                             2, 2,                   },
554         { "order",                                              OP_ORDER,                                               2, 2,                   },
555         { "waypoint-missed",                    OP_WAYPOINT_MISSED,                     0, 0,                   },
556         { "waypoint-twice",                     OP_WAYPOINT_TWICE,                      0, 0,                   },
557         { "path-flown",                         OP_PATH_FLOWN,                                  0, 0,                   },
558         { "training-msg",                               OP_TRAINING_MSG,                                1, 4,                   },
559         { "flash-hud-gauge",                    OP_FLASH_HUD_GAUGE,                     1, 1,                   },
560         { "special-check",                      OP_SPECIAL_CHECK,                               1, 1,                   },
561         { "secondaries-depleted",       OP_SECONDARIES_DEPLETED,        1, 1,                   },
562
563         { "set-training-context-fly-path",      OP_SET_TRAINING_CONTEXT_FLY_PATH,       2, 2, },
564         { "set-training-context-speed",         OP_SET_TRAINING_CONTEXT_SPEED,          2, 2, },
565 };
566
567 sexp_ai_goal_link Sexp_ai_goal_links[] = {
568         { AI_GOAL_CHASE, OP_AI_CHASE },
569         { AI_GOAL_CHASE_WING, OP_AI_CHASE_WING },
570         { AI_GOAL_DOCK, OP_AI_DOCK },
571         { AI_GOAL_UNDOCK, OP_AI_UNDOCK },
572         { AI_GOAL_WARP, OP_AI_WARP_OUT },
573         { AI_GOAL_WARP, OP_AI_WARP },
574         { AI_GOAL_WAYPOINTS, OP_AI_WAYPOINTS },
575         { AI_GOAL_WAYPOINTS_ONCE, OP_AI_WAYPOINTS_ONCE },
576         { AI_GOAL_DESTROY_SUBSYSTEM, OP_AI_DESTROY_SUBSYS },
577         { AI_GOAL_DISABLE_SHIP, OP_AI_DISABLE_SHIP },
578         { AI_GOAL_DISARM_SHIP, OP_AI_DISARM_SHIP },
579         { AI_GOAL_GUARD, OP_AI_GUARD },
580         { AI_GOAL_CHASE_ANY, OP_AI_CHASE_ANY },
581         { AI_GOAL_GUARD_WING, OP_AI_GUARD_WING },
582         { AI_GOAL_EVADE_SHIP, OP_AI_EVADE_SHIP },
583         { AI_GOAL_STAY_NEAR_SHIP, OP_AI_STAY_NEAR_SHIP },
584         { AI_GOAL_KEEP_SAFE_DISTANCE, OP_AI_KEEP_SAFE_DISTANCE },
585         { AI_GOAL_IGNORE, OP_AI_IGNORE },
586         { AI_GOAL_STAY_STILL, OP_AI_STAY_STILL },
587         { AI_GOAL_PLAY_DEAD, OP_AI_PLAY_DEAD },
588 };
589
590 const char *HUD_gauge_text[NUM_HUD_GAUGES] = 
591 {
592         "LEAD_INDICATOR",
593         "ORIENTATION_TEE",
594         "HOSTILE_TRIANGLE",
595         "TARGET_TRIANGLE",
596         "MISSION_TIME",
597         "RETICLE_CIRCLE",
598         "THROTTLE_GAUGE",
599         "RADAR",
600         "TARGET_MONITOR",
601         "CENTER_RETICLE",
602         "TARGET_MONITOR_EXTRA_DATA",
603         "TARGET_SHIELD_ICON",
604         "PLAYER_SHIELD_ICON",
605         "ETS_GAUGE",
606         "AUTO_TARGET",
607         "AUTO_SPEED",
608         "WEAPONS_GAUGE",
609         "ESCORT_VIEW",
610         "DIRECTIVES_VIEW",
611         "THREAT_GAUGE",
612         "AFTERBURNER_ENERGY",
613         "WEAPONS_ENERGY",
614         "WEAPON_LINKING_GAUGE",
615         "TARGER_MINI_ICON",
616         "OFFSCREEN_INDICATOR",
617         "TALKING_HEAD",
618         "DAMAGE_GAUGE",
619         "MESSAGE_LINES",
620         "MISSILE_WARNING_ARROW",
621         "CMEASURE_GAUGE",
622         "OBJECTIVES_NOTIFY_GAUGE",
623         "WINGMEN_STATUS",
624         "OFFSCREEN RANGE",
625         "KILLS GAUGE",
626         "ATTACKING TARGET COUNT",
627         "TEXT FLASH",
628         "MESSAGE BOX",
629 #ifndef FS2_DEMO
630         "SUPPORT GUAGE",
631         "LAG GUAGE"
632 #endif
633 };
634
635
636 int     Directive_count;
637 int     Sexp_useful_number;  // a variable to pass useful info in from external modules
638 int     Locked_sexp_true, Locked_sexp_false;
639 int     Num_operators = sizeof(Operators) / sizeof(sexp_oper);
640 int     Num_sexp_ai_goal_links = sizeof(Sexp_ai_goal_links) / sizeof(sexp_ai_goal_link);
641 int     Sexp_build_flag;
642 int     Sexp_clipboard = -1;  // used by Fred
643 int     Training_context = 0;
644 int     Training_context_speed_set;
645 int     Training_context_speed_min;
646 int     Training_context_speed_max;
647 int     Training_context_speed_timestamp;
648 int     Training_context_path;
649 int     Training_context_goal_waypoint;
650 int     Training_context_at_waypoint;
651 float   Training_context_distance;
652 char    Sexp_error_text[MAX_SEXP_TEXT];
653 char    *Sexp_string; //[1024] = {0};
654 int Sexp_string_len;
655 sexp_node Sexp_nodes[MAX_SEXP_NODES];
656 sexp_variable Sexp_variables[MAX_SEXP_VARIABLES];
657
658 int Players_target = UNINITIALIZED;
659 ship_subsys *Players_targeted_subsys;
660 int Players_target_timestamp;
661
662 int get_sexp(char *token);
663 int eval_sexp(int cur_node);
664 void build_extended_sexp_string(int cur_node, int level, int mode);
665 void update_sexp_references(char *old_name, char *new_name, int format, int node);
666 int sexp_determine_team(char *subj);
667 int sexp_distance2(int obj1, char *subj);
668 int sexp_distance3(int obj1, int obj2);
669 int extract_sexp_variable_index(int node);
670 void init_sexp_vars();
671 int num_eval(int node);
672
673 void init_sexp()
674 {
675         int i;
676
677         for (i=0; i<MAX_SEXP_NODES; i++) {
678                 if ( !(Sexp_nodes[i].type & SEXP_FLAG_PERSISTENT) ){
679                         Sexp_nodes[i].type = SEXP_NOT_USED;
680                 }
681         }
682
683         init_sexp_vars();
684
685         Locked_sexp_false = Locked_sexp_true = -1;
686         Locked_sexp_false = alloc_sexp("false", SEXP_LIST, SEXP_ATOM_OPERATOR, -1, -1);
687         SDL_assert(Locked_sexp_false != -1);
688         Sexp_nodes[Locked_sexp_false].type = SEXP_ATOM;  // fix bypassing value
689         Locked_sexp_true = alloc_sexp("true", SEXP_LIST, SEXP_ATOM_OPERATOR, -1, -1);
690         SDL_assert(Locked_sexp_true != -1);
691         Sexp_nodes[Locked_sexp_true].type = SEXP_ATOM;  // fix bypassing value
692 }
693
694 // allocates an sexp node.
695 int alloc_sexp(const char *text, int type, int subtype, int first, int rest)
696 {
697         int i;
698
699         i = find_operator(text);
700         if ((i == OP_TRUE) && (type == SEXP_ATOM) &&    (subtype == SEXP_ATOM_OPERATOR)) {
701                 return Locked_sexp_true;
702         } else if ((i == OP_FALSE) && (type == SEXP_ATOM) && (subtype == SEXP_ATOM_OPERATOR)) {
703                 return Locked_sexp_false;
704         }
705
706         i = find_free_sexp();
707         SDL_assert(i != Locked_sexp_true);
708         SDL_assert(i != Locked_sexp_false);
709         if (i == MAX_SEXP_NODES){
710                 return -1;
711         }
712
713         SDL_assert(strlen(text) < TOKEN_LENGTH);
714         SDL_strlcpy(Sexp_nodes[i].text, text, TOKEN_LENGTH);
715         SDL_assert(type >= 0);
716         Sexp_nodes[i].type = type;
717         Sexp_nodes[i].subtype = subtype;
718         Sexp_nodes[i].first = first;
719         Sexp_nodes[i].rest = rest;
720         Sexp_nodes[i].value = SEXP_UNKNOWN;
721         return i;
722 }
723
724 int Sexp_hwm = 0;
725
726 int count_free_sexp_nodes()
727 {
728         int i, f = 0, p = 0;
729
730         for (i=0; i<MAX_SEXP_NODES; i++) {
731                 if (Sexp_nodes[i].type == SEXP_NOT_USED){
732                         f++;
733                 } else if (Sexp_nodes[i].type & SEXP_FLAG_PERSISTENT){
734                         p++;
735                 }
736         }
737
738         if (MAX_SEXP_NODES - f > Sexp_hwm) {
739                 nprintf(("Sexp", "Sexp nodes: Free=%d, Used=%d, Persistent=%d\n", f, MAX_SEXP_NODES - f, p));
740                 Sexp_hwm = MAX_SEXP_NODES - f;
741         }
742
743         return f;
744 }
745
746 // find the next free sexp and return it's index.
747 int find_free_sexp()
748 {
749         int i;
750
751         for (i=0; i<MAX_SEXP_NODES; i++){
752                 if (Sexp_nodes[i].type == SEXP_NOT_USED){
753                         break;
754                 }
755         }
756
757 #ifndef NDEBUG
758         //count_free_sexp_nodes();
759 #endif
760
761         SDL_assert(i != MAX_SEXP_NODES);  // time to raise the limit..
762         if (i == MAX_SEXP_NODES){
763                 return -1;
764         }
765
766         return i;
767 }
768
769 // sexp_mark_persistent() marks a whole sexp tree with the persistent flag so that it won't
770 // get re-used between missions
771 void sexp_mark_persistent( int n )
772 {
773         if (n == -1){
774                 return;
775         }
776
777         // total hack because of the true/false locked sexps -- we should make those persistent as well
778         if ( (n == Locked_sexp_true) || (n == Locked_sexp_false) ){
779                 return;
780         }
781
782         SDL_assert( !(Sexp_nodes[n].type & SEXP_FLAG_PERSISTENT) );
783         Sexp_nodes[n].type |= SEXP_FLAG_PERSISTENT;
784
785         sexp_mark_persistent(Sexp_nodes[n].first);
786         sexp_mark_persistent(Sexp_nodes[n].rest);
787
788 }
789
790 // sexp_unmark_persistent() removes the persistent flag from all nodes in the tree
791 void sexp_unmark_persistent( int n )
792 {
793         if (n == -1){
794                 return;
795         }
796
797         if ( (n == Locked_sexp_true) || (n == Locked_sexp_false) ){
798                 return;
799         }
800
801         SDL_assert( Sexp_nodes[n].type & SEXP_FLAG_PERSISTENT );
802         Sexp_nodes[n].type &= ~SEXP_FLAG_PERSISTENT;
803
804         sexp_unmark_persistent(Sexp_nodes[n].first);
805         sexp_unmark_persistent(Sexp_nodes[n].rest);
806 }
807
808 // just frees up the specified sexp node,  Leaves link chains untouched.
809 int free_one_sexp(int num)
810 {
811         SDL_assert(Sexp_nodes[num].type != SEXP_NOT_USED);  // make sure it is actually used
812         SDL_assert( !(Sexp_nodes[num].type & SEXP_FLAG_PERSISTENT) );
813
814         if ((num == Locked_sexp_true) || (num == Locked_sexp_false)){
815                 return 0;
816         }
817
818         Sexp_nodes[num].type = SEXP_NOT_USED;
819         return 1;
820 }
821
822 // frees a used sexp node, so it can be reused later.  Should only be called on
823 // an atom or a list, and not an operator.  If on a list, the list and everything
824 // in it will be freed (including the operator).
825 int free_sexp(int num)
826 {
827         int i, rest, count = 0;
828
829         SDL_assert(Sexp_nodes[num].type != SEXP_NOT_USED);  // make sure it is actually used
830         SDL_assert( !(Sexp_nodes[num].type & SEXP_FLAG_PERSISTENT) );
831
832         if ((num == Locked_sexp_true) || (num == Locked_sexp_false) || (num == -1) ){
833                 return 0;
834         }
835
836         Sexp_nodes[num].type = SEXP_NOT_USED;
837         count++;
838
839         i = Sexp_nodes[num].first;
840         while (i != -1){
841                 count += free_sexp(i);
842                 i = Sexp_nodes[i].rest;
843         }
844
845         rest = Sexp_nodes[num].rest;
846         for (i=0; i<MAX_SEXP_NODES; i++) {
847                 if (Sexp_nodes[i].first == num){
848                         Sexp_nodes[i].first = rest;
849                 }
850
851                 if (Sexp_nodes[i].rest == num){
852                         Sexp_nodes[i].rest = rest;
853                 }
854         }
855
856         return count;  // total elements freed up.
857 }
858
859 // used to free up an entire sexp tree.  Because the root node is an operator, instead of
860 // a list, we can't simply call free_sexp().  This function should only be called on the
861 // root node of an sexp, otherwise the linking will get screwed up.
862 int free_sexp2(int num)
863 {       
864         int i, count = 0;
865
866         if ((num == -1) || (num == Locked_sexp_true) || (num == Locked_sexp_false)){
867                 return 0;
868         }
869
870         i = Sexp_nodes[num].rest;
871         while (i != -1) {
872                 count += free_sexp(i);
873                 i = Sexp_nodes[i].rest;
874         }
875
876         count += free_sexp(num);
877         return count;
878 }
879
880 // This function resets the status of all the nodes in a tree, forcing them to all be
881 // evaulated again.
882 void flush_sexp_tree(int node)
883 {
884         if (node < 0){
885                 return;
886         }
887
888         Sexp_nodes[node].value = SEXP_UNKNOWN;
889         flush_sexp_tree(Sexp_nodes[node].first);
890         flush_sexp_tree(Sexp_nodes[node].rest);
891 }
892
893 int verify_sexp_tree(int node)
894 {
895         if (node == -1){
896                 return 0;
897         }
898
899         if ((Sexp_nodes[node].type == SEXP_NOT_USED) ||
900                 (Sexp_nodes[node].first == node) ||
901                 (Sexp_nodes[node].rest == node)) {
902                 Error(LOCATION, "Sexp node is corrupt");
903                 return -1;
904         }
905
906         if (Sexp_nodes[node].first != -1){
907                 verify_sexp_tree(Sexp_nodes[node].first);
908         }
909         if (Sexp_nodes[node].rest != -1){
910                 verify_sexp_tree(Sexp_nodes[node].rest);
911         }
912
913         return 0;
914 }
915
916 int dup_sexp_chain(int node)
917 {
918         int cur, first, rest;
919
920         if (node == -1){
921                 return -1;
922         }
923
924         // TODO - CASE OF SEXP VARIABLES - ONLY 1 COPY OF VARIABLE
925         first = dup_sexp_chain(Sexp_nodes[node].first);
926         rest = dup_sexp_chain(Sexp_nodes[node].rest);
927         cur = alloc_sexp(Sexp_nodes[node].text, Sexp_nodes[node].type, Sexp_nodes[node].subtype, first, rest);
928
929         if (cur == -1) {
930                 if (first != -1){
931                         free_sexp(first);
932                 }
933                 if (rest != -1){
934                         free_sexp(rest);
935                 }
936         }
937
938         return cur;
939 }
940
941 // returns 1 if they are the same, 0 if different
942 int cmp_sexp_chains(int node1, int node2)
943 {
944         if ((node1 == -1) && (node2 == -1)){
945                 return 1;
946         }
947
948         if ((node1 == -1) || (node2 == -1)){
949                 return 0;
950         }
951
952         // DA: 1/7/99 Need to check the actual Sexp_node.text, not possible variable, which can be equal
953         if (SDL_strcasecmp(Sexp_nodes[node1].text, Sexp_nodes[node2].text)){
954                 return 0;
955         }
956
957         if (!cmp_sexp_chains(Sexp_nodes[node1].first, Sexp_nodes[node2].first)){
958                 return 0;
959         }
960
961         if (!cmp_sexp_chains(Sexp_nodes[node1].rest, Sexp_nodes[node2].rest)){
962                 return 0;
963         }
964
965         return 1;
966 }
967
968 // determine if an sexp node is within the given sexp chain.
969 int query_node_in_sexp(int node, int sexp)
970 {
971         if (sexp == -1){
972                 return 0;
973         }
974         if (node == sexp){
975                 return 1;
976         }
977
978         if (query_node_in_sexp(node, Sexp_nodes[sexp].first)){
979                 return 1;
980         }
981         if (query_node_in_sexp(node, Sexp_nodes[sexp].rest)){
982                 return 1;
983         }
984
985         return 0;
986 }
987
988 // find the index of the list associated with an operator
989 int find_sexp_list(int num)
990 {
991         int i;
992
993         for (i=0; i<MAX_SEXP_NODES; i++){
994                 if (Sexp_nodes[i].first == num){
995                         return i;
996                 }
997         }
998
999         return -1;
1000 }
1001
1002 // find index of operator that item is an argument of.
1003 int find_parent_operator(int num)
1004 {
1005         int i;
1006
1007         if (Sexp_nodes[num].subtype == SEXP_ATOM_OPERATOR){
1008                 num = find_sexp_list(num);
1009         }
1010
1011         while (Sexp_nodes[num].subtype != SEXP_ATOM_OPERATOR) {
1012                 for (i=0; i<MAX_SEXP_NODES; i++){
1013                         if (Sexp_nodes[i].rest == num){
1014                                 break;
1015                         }
1016                 }
1017
1018                 if (i == MAX_SEXP_NODES){
1019                         return -1;  // not found, probably at top node already.
1020                 }
1021
1022                 num = i;
1023         }
1024
1025         return num;
1026 }
1027
1028 // function to determine if an sexpression node is the top level node of an sexpression tree.  Top
1029 // level nodes do not have their node id in anyone elses first or rest index
1030 int is_sexp_top_level( int node )
1031 {
1032         int i;
1033
1034         if ( Sexp_nodes[node].type == SEXP_NOT_USED ){
1035                 return 0;
1036         }
1037
1038         for (i = 0; i < MAX_SEXP_NODES; i++ ) {
1039                 if ( (Sexp_nodes[i].type == SEXP_NOT_USED) || ( i == node ) ){                          // don't check myself or unused nodes
1040                         continue;
1041                 }
1042                 if ( (Sexp_nodes[i].first == node) || (Sexp_nodes[i].rest == node) ){
1043                         return 0;
1044                 }
1045         }
1046
1047         return 1;
1048 }
1049
1050 int identify_operator(const char *token)
1051 {
1052         int     i;
1053
1054         for (i=0; i<Num_operators; i++){
1055                 if (!SDL_strcasecmp(token, Operators[i].text)){
1056                         return i;
1057                 }
1058         }
1059
1060         return -1;
1061 }
1062
1063 int find_operator(const char *token)
1064 {
1065         int     i;
1066
1067         for (i=0; i<Num_operators; i++){
1068                 if (!SDL_strcasecmp(token, Operators[i].text)){
1069                         return Operators[i].value;
1070                 }
1071         }
1072
1073         return 0;
1074 }
1075
1076 int query_sexp_args_count(int index)
1077 {
1078         int count = 0;
1079
1080         while (Sexp_nodes[index].rest != -1){
1081                 count++;
1082                 index = Sexp_nodes[index].rest;
1083         }
1084
1085         return count;
1086 }
1087
1088 // returns 0 if ok, negative if there's an error in expression..
1089 // See the returns types in sexp.h
1090
1091 int check_sexp_syntax(int index, int return_type, int recursive, int *bad_index, int mode)
1092 {
1093         int i = 0, z, t, type, argnum = 0, count, op, type2 = 0, op2;
1094         int op_index;
1095
1096         SDL_assert(index >= 0 && index < MAX_SEXP_NODES);
1097         SDL_assert(Sexp_nodes[index].type != SEXP_NOT_USED);
1098         if (Sexp_nodes[index].subtype == SEXP_ATOM_NUMBER && return_type == OPR_BOOL) {
1099                 // special case Mark seems to want supported
1100                 SDL_assert(Sexp_nodes[index].first == -1);  // only lists should have a first pointer
1101                 if (Sexp_nodes[index].rest != -1)  // anything after the number?
1102                         return SEXP_CHECK_NONOP_ARGS; // if so, it's a syntax error
1103
1104                 return 0;
1105         }
1106
1107         op_index = index;               // save the index of the operator since we need to get to other args.
1108         if (bad_index)
1109                 *bad_index = op_index;
1110
1111         if (Sexp_nodes[op_index].subtype != SEXP_ATOM_OPERATOR)
1112                 return SEXP_CHECK_OP_EXPTECTED;  // not an operator, which it should always be
1113
1114         op = identify_operator(CTEXT(op_index));
1115         if (op == -1)
1116                 return SEXP_CHECK_UNKNOWN_OP;  // unrecognized operator
1117
1118         //special case - OPR_AMBIGUOUS matches all
1119         if (return_type != OPR_AMBIGUOUS) {
1120                 if (query_operator_return_type(op) != return_type) {
1121                         return SEXP_CHECK_TYPE_MISMATCH;
1122                 }
1123         } 
1124
1125         count = query_sexp_args_count(op_index);
1126         if (count < Operators[op].min || count > Operators[op].max)
1127                 return SEXP_CHECK_BAD_ARG_COUNT;  // incorrect number of arguments
1128
1129         index = Sexp_nodes[op_index].rest;
1130         while (index != -1) {
1131                 type = query_operator_argument_type(op, argnum);
1132                 SDL_assert(Sexp_nodes[index].type != SEXP_NOT_USED);
1133                 if (bad_index)
1134                         *bad_index = index;
1135
1136                 if (Sexp_nodes[index].subtype == SEXP_ATOM_LIST) {
1137                         i = Sexp_nodes[index].first;
1138                         if (bad_index)
1139                                 *bad_index = i;
1140                         
1141                         // be sure to check to see if this node is a list of stuff and not an actual operator type
1142                         // thing.  (i.e. in the case of a cond statement, the conditional will fall into this if
1143                         // statement.  MORE TO DO HERE!!!!
1144                         if (Sexp_nodes[i].subtype == SEXP_ATOM_LIST)
1145                                 return 0;
1146
1147                         op2 = identify_operator(CTEXT(i));
1148                         if (op2 == -1)
1149                                 return SEXP_CHECK_UNKNOWN_OP;
1150
1151                         type2 = query_operator_return_type(op2);
1152                         if (recursive) {
1153                                 switch (type) {
1154                                         case OPF_NUMBER:
1155                                                 t = OPR_NUMBER;
1156                                                 break;
1157
1158                                         case OPF_POSITIVE:
1159                                                 t = OPR_POSITIVE;
1160                                                 break;
1161
1162                                         case OPF_BOOL:
1163                                                 t = OPR_BOOL;
1164                                                 break;
1165
1166                                         case OPF_NULL:
1167                                                 t = OPR_NULL;
1168                                                 break;
1169
1170                                         case OPF_AI_GOAL:
1171                                                 t = OPR_AI_GOAL;
1172                                                 break;
1173
1174                                         // special case for modify-variable"
1175                                         case OPF_AMBIGUOUS:
1176                                                 t = OPR_AMBIGUOUS;
1177                                                 break;
1178
1179                                         default:
1180                                                 return SEXP_CHECK_UNKNOWN_TYPE;  // no other return types available
1181                                 }
1182
1183                                 if ((z = check_sexp_syntax(i, t, recursive, bad_index)) != 0) {
1184                                         return z;
1185                                 }
1186                         }
1187
1188                 } else if (Sexp_nodes[index].subtype == SEXP_ATOM_NUMBER) {
1189                         char *ptr;
1190
1191                         type2 = OPR_POSITIVE;
1192                         ptr = CTEXT(index);
1193                         if (*ptr == '-') {
1194                                 type2 = OPR_NUMBER;
1195                                 ptr++;
1196                         }
1197
1198                         if (type == OPF_BOOL)  // allow numbers to be used where boolean is required.
1199                                 type2 = OPR_BOOL;
1200
1201                         while (*ptr) {
1202                                 if (!isdigit(*ptr))
1203                                         return SEXP_CHECK_INVALID_NUM;  // not a valid number
1204
1205                                 ptr++;
1206                         }
1207
1208                         i = atoi(CTEXT(index));
1209                         z = find_operator(CTEXT(op_index));
1210                         if ( (z == OP_HAS_DOCKED_DELAY) || (z == OP_HAS_UNDOCKED_DELAY) )
1211                                 if ( (argnum == 2) && (i < 1) )
1212                                         return SEXP_CHECK_NUM_RANGE_INVALID;
1213
1214                         z = identify_operator(CTEXT(op_index));
1215                         if ( (query_operator_return_type(z) == OPR_AI_GOAL) && (argnum == Operators[op].min - 1) )
1216                                 if ( (i < 0) || (i > 89) )
1217                                         return SEXP_CHECK_NUM_RANGE_INVALID;
1218
1219                 } else if (Sexp_nodes[index].subtype == SEXP_ATOM_STRING) {
1220                         type2 = SEXP_ATOM_STRING;
1221
1222                 } else {
1223                         SDL_assert(0);
1224                 }
1225
1226                 switch (type) {
1227                         case OPF_NUMBER:
1228                                 if ((type2 != OPR_NUMBER) && (type2 != OPR_POSITIVE)){
1229                                         return SEXP_CHECK_TYPE_MISMATCH;
1230                                 }
1231
1232                                 break;
1233
1234                         case OPF_POSITIVE:
1235                                 if (type2 == OPR_NUMBER){
1236                                         return SEXP_CHECK_NEGATIVE_NUM;
1237                                 }
1238
1239                                 if (type2 != OPR_POSITIVE){
1240                                         return SEXP_CHECK_TYPE_MISMATCH;
1241                                 }
1242
1243                                 break;
1244
1245                         case OPF_SHIP_NOT_PLAYER:
1246                                 if (type2 != SEXP_ATOM_STRING){
1247                                         return SEXP_CHECK_TYPE_MISMATCH;
1248                                 }
1249
1250                                 if (ship_name_lookup(CTEXT(index), 0) < 0) {
1251                                         if (Fred_running || mission_parse_ship_arrived(CTEXT(index))){  // == 0 when still on arrival list
1252                                                 return SEXP_CHECK_INVALID_SHIP;
1253                                         }
1254                                 }
1255
1256                                 break;
1257
1258                         case OPF_SHIP:
1259                         case OPF_SHIP_POINT:
1260                                 if (type2 != SEXP_ATOM_STRING){
1261                                         return SEXP_CHECK_TYPE_MISMATCH;
1262                                 }
1263
1264                                 if (ship_name_lookup(CTEXT(index), 1) < 0) {
1265                                         if (Fred_running || mission_parse_ship_arrived(CTEXT(index))) {         // == 0 when still on arrival list
1266                                                 if (type == OPF_SHIP){                                                                                                  // return invalid ship if not also looking for point
1267                                                         return SEXP_CHECK_INVALID_SHIP;
1268                                                 }
1269
1270                                                 if (waypoint_lookup(CTEXT(index)) < 0){
1271                                                         if (verify_vector(CTEXT(index))){                                               // verify return non-zero on invalid point
1272                                                                 return SEXP_CHECK_INVALID_POINT;
1273                                                         }
1274                                                 }
1275                                         }
1276                                 }
1277
1278                                 break;
1279
1280                         case OPF_WING:
1281                                 if (type2 != SEXP_ATOM_STRING){
1282                                         return SEXP_CHECK_TYPE_MISMATCH;
1283                                 }
1284
1285                                 if (wing_name_lookup(CTEXT(index), 1) < 0){
1286                                         return SEXP_CHECK_INVALID_WING;
1287                                 }
1288
1289                                 break;
1290
1291                         case OPF_SHIP_WING:
1292                         case OPF_SHIP_WING_POINT:
1293                                 if ( type2 != SEXP_ATOM_STRING ){
1294                                         return SEXP_CHECK_TYPE_MISMATCH;
1295                                 }
1296
1297                                 if ((ship_name_lookup(CTEXT(index), 1) < 0) && (wing_name_lookup(CTEXT(index), 1) < 0)) {
1298                                         if (Fred_running || mission_parse_ship_arrived(CTEXT(index))) {         // == 0 when still on arrival list
1299                                                 if (type != OPF_SHIP_WING_POINT){                                                                       // return invalid if not also looking for point
1300                                                         return SEXP_CHECK_INVALID_SHIP_WING;
1301                                                 }
1302
1303                                                 if (waypoint_lookup(CTEXT(index)) < 0){
1304                                                         if (verify_vector(CTEXT(index))){  // non-zero on verify vector mean invalid!
1305                                                                 if (!sexp_determine_team(CTEXT(index))){
1306                                                                         return SEXP_CHECK_INVALID_POINT;
1307                                                                 }
1308                                                         }
1309                                                 }
1310                                         }
1311                                 }
1312
1313                                 break;
1314
1315                         case OPF_AWACS_SUBSYSTEM:
1316                         case OPF_SUBSYSTEM: {
1317                                 char *shipname;
1318                                 int shipnum,ship_class;
1319                                 int ship_index;                         
1320
1321                                 if (type2 != SEXP_ATOM_STRING){
1322                                         return SEXP_CHECK_TYPE_MISMATCH;
1323                                 }
1324
1325                                 // we must get the model of the ship that is part of this sexpression and find a subsystem
1326                                 // with that name.  This code assumes that the ship *always* preceeds the subsystem in an sexpression.
1327                                 // if this current fact is ever not the case, the next lines of code must be changes to get the correct
1328                                 // shipname.                            
1329                                 switch(Operators[identify_operator(CTEXT(op_index))].value){
1330                                 case OP_CAP_SUBSYS_CARGO_KNOWN_DELAY:
1331                                         ship_index = Sexp_nodes[Sexp_nodes[op_index].rest].rest;
1332                                         break;
1333
1334                                 case OP_BEAM_FIRE:
1335                                         if(argnum == 1){
1336                                                 ship_index = Sexp_nodes[op_index].rest;
1337                                         } else {
1338                                                 ship_index = Sexp_nodes[Sexp_nodes[Sexp_nodes[op_index].rest].rest].rest;
1339                                         }
1340                                         break;
1341                                 
1342                                 default :
1343                                         ship_index = Sexp_nodes[op_index].rest;
1344                                         break;
1345                                 }
1346
1347                                 shipname = CTEXT(ship_index);
1348                                 shipnum = ship_name_lookup(shipname);
1349                                 if (shipnum >= 0){
1350                                         ship_class = Ships[shipnum].ship_info_index;
1351                                 } else {
1352                                         // must try to find the ship in the ship_arrival_list
1353                                         p_object *parse_obj;
1354
1355                                         parse_obj = mission_parse_get_arrival_ship( shipname );
1356                                         if ( parse_obj == NULL ){
1357                                                 return SEXP_CHECK_INVALID_SHIP;
1358                                         }
1359
1360                                         ship_class = parse_obj->ship_class;
1361                                 }
1362
1363                                 // check for the special "hull" value
1364                                 if ( (Operators[op].value == OP_SABOTAGE_SUBSYSTEM) || (Operators[op].value == OP_REPAIR_SUBSYSTEM) || (Operators[op].value == OP_SET_SUBSYSTEM_STRNGTH) ) {
1365                                         if ( !SDL_strcasecmp( CTEXT(index), SEXP_HULL_STRING) ){
1366                                                 break;
1367                                         }
1368                                 }
1369
1370                                 for (i=0; i<Ship_info[ship_class].n_subsystems; i++){
1371                                         if (!SDL_strcasecmp(Ship_info[ship_class].subsystems[i].subobj_name, CTEXT(index))){
1372                                                 break;
1373                                         }
1374                                 }
1375
1376                                 if (i == Ship_info[ship_class].n_subsystems){
1377                                         return SEXP_CHECK_INVALID_SUBSYS;
1378                                 }
1379
1380                                 // if we're checking for an AWACS subsystem and this is not an awacs subsystem
1381                                 if(Fred_running){
1382                                         if((type == OPF_AWACS_SUBSYSTEM) && !(Ship_info[ship_class].subsystems[i].flags & MSS_FLAG_AWACS)){
1383                                                 return SEXP_CHECK_INVALID_SUBSYS;
1384                                         }
1385                                 }
1386
1387                                 break;
1388                         }
1389
1390                         case OPF_POINT:
1391                                 if (waypoint_lookup(CTEXT(index)) < 0){
1392                                         if (verify_vector(CTEXT(index))){
1393                                                 return SEXP_CHECK_INVALID_POINT;
1394                                         }
1395                                 }
1396
1397                                 if (type2 != SEXP_ATOM_STRING){
1398                                         return SEXP_CHECK_TYPE_MISMATCH;
1399                                 }
1400
1401                                 break;
1402
1403                         case OPF_IFF:
1404                                 if (type2 == SEXP_ATOM_STRING){
1405                                         for (i=0; i<Num_team_names; i++){
1406                                                 if (!SDL_strcasecmp(Team_names[i], CTEXT(index))){
1407                                                         break;
1408                                                 }
1409                                         }
1410                                 }
1411
1412                                 if (i == Num_team_names){
1413                                         return SEXP_CHECK_INVALID_IFF;
1414                                 }
1415
1416                                 if (type2 != SEXP_ATOM_STRING){
1417                                         return SEXP_CHECK_TYPE_MISMATCH;
1418                                 }
1419
1420                                 break;
1421
1422                         case OPF_BOOL:
1423                                 if (type2 != OPR_BOOL){
1424                                         return SEXP_CHECK_TYPE_MISMATCH;
1425                                 }
1426
1427                                 break;
1428
1429                         case OPF_NULL:
1430                                 if (type2 != OPR_NULL){
1431                                         return SEXP_CHECK_TYPE_MISMATCH;
1432                                 }
1433
1434                                 break;
1435
1436                         case OPF_AI_ORDER:
1437                                 if ( type2 != SEXP_ATOM_STRING ){
1438                                         return SEXP_CHECK_TYPE_MISMATCH;
1439                                 }
1440
1441                                 break;
1442
1443                         case OPF_AI_GOAL:
1444                                 if (type2 != OPR_AI_GOAL){
1445                                         return SEXP_CHECK_TYPE_MISMATCH;
1446                                 }
1447
1448                                 if (Fred_running) {
1449                                         int ship_num, ship2, w = 0;
1450
1451                                         ship_num = ship_name_lookup(CTEXT(Sexp_nodes[op_index].rest));
1452                                         if (ship_num < 0) {
1453                                                 w = wing_name_lookup(CTEXT(Sexp_nodes[op_index].rest));
1454                                                 if (w < 0) {
1455                                                         if (bad_index){
1456                                                                 *bad_index = Sexp_nodes[op_index].rest;
1457                                                         }
1458
1459                                                         return SEXP_CHECK_INVALID_SHIP;  // should have already been caught earlier, but just in case..
1460                                                 }
1461                                         }
1462
1463                                         SDL_assert(Sexp_nodes[index].subtype == SEXP_ATOM_LIST);
1464                                         z = Sexp_nodes[index].first;
1465                                         SDL_assert(Sexp_nodes[z].subtype != SEXP_ATOM_LIST);
1466                                         z = find_operator(CTEXT(z));
1467                                         if (ship_num >= 0) {
1468                                                 if (!query_sexp_ai_goal_valid(z, ship_num)){
1469                                                         return SEXP_CHECK_ORDER_NOT_ALLOWED;
1470                                                 }
1471
1472                                         } else {
1473                                                 for (i=0; i<Wings[w].wave_count; i++){
1474                                                         if (!query_sexp_ai_goal_valid(z, Wings[w].ship_index[i])){
1475                                                                 return SEXP_CHECK_ORDER_NOT_ALLOWED;
1476                                                         }
1477                                                 }
1478                                         }
1479
1480                                         if ((z == OP_AI_DOCK) && (Sexp_nodes[index].rest >= 0)) {
1481                                                 ship2 = ship_name_lookup(CTEXT(Sexp_nodes[index].rest));
1482                                                 if ((ship_num < 0) || !ship_docking_valid(ship_num, ship2)){
1483                                                         return SEXP_CHECK_DOCKING_NOT_ALLOWED;
1484                                                 }
1485                                         }
1486                                 }
1487
1488                                 // we should check the syntax of the actual goal!!!!
1489                                 z = Sexp_nodes[index].first;
1490                                 if ((z = check_sexp_syntax(z, OPR_AI_GOAL, recursive, bad_index)) != 0){
1491                                         return z;
1492                                 }
1493
1494                                 break;
1495
1496                         case OPF_SHIP_TYPE:
1497                                 if (type2 != SEXP_ATOM_STRING){
1498                                         return SEXP_CHECK_TYPE_MISMATCH;
1499                                 }
1500
1501                                 for (i=0; i<MAX_SHIP_TYPE_COUNTS; i++){
1502                                         if (!SDL_strcasecmp( Ship_type_names[i], CTEXT(index))){
1503                                                 break;
1504                                         }
1505                                 }
1506
1507                                 if (i == MAX_SHIP_TYPE_COUNTS){
1508                                         return SEXP_CHECK_INVALID_SHIP_TYPE;
1509                                 }
1510
1511                                 break;
1512
1513                         case OPF_WAYPOINT_PATH:
1514                                 for (i=0; i<Num_waypoint_lists; i++){
1515                                         if (!SDL_strcasecmp(Waypoint_lists[i].name, CTEXT(index))){
1516                                                 break;
1517                                         }
1518                                 }
1519
1520                                 if (i == Num_waypoint_lists){
1521                                         return SEXP_CHECK_TYPE_MISMATCH;
1522                                 }
1523                                 break;
1524
1525                         case OPF_MESSAGE:
1526                                 // code commented out because of order reversing of arguments to messaging code.  Maybe
1527                                 // I'll comment it back in someday when older missions get fixed.
1528                                 if (type2 != SEXP_ATOM_STRING)
1529                                         return SEXP_CHECK_TYPE_MISMATCH;
1530
1531                                 if (Fred_running) {
1532                                         for (i=0; i<Num_messages; i++)
1533                                                 if (!SDL_strcasecmp(Messages[i].name, CTEXT(index)))
1534                                                         break;
1535
1536                                         if (i == Num_messages)
1537                                                 return SEXP_CHECK_UNKNOWN_MESSAGE;
1538                                 }
1539                                 
1540                                 break;
1541
1542                         case OPF_PRIORITY: {
1543                                 // following code must be removed since I changed the order of the operators in the send-message
1544                                 // function to take priority second.  Maybe someday, I'll require that all designers fix the
1545                                 // old missions.
1546                                 if (type2 != SEXP_ATOM_STRING)
1547                                         return SEXP_CHECK_TYPE_MISMATCH;
1548
1549                                 if (Fred_running) {  // should still check in Fred though..
1550                                         char *name;
1551
1552                                         name = CTEXT(index);
1553                                         if (!SDL_strcasecmp(name, "low") || !SDL_strcasecmp(name, "normal") || !SDL_strcasecmp(name, "high"))
1554                                                 break;
1555
1556                                         return SEXP_CHECK_INVALID_PRIORITY;
1557                                 }
1558
1559                                 break;
1560                         }
1561
1562                         case OPF_MISSION_NAME:
1563                                 if (type2 != SEXP_ATOM_STRING)
1564                                         return SEXP_CHECK_TYPE_MISMATCH;
1565
1566                                 if (Fred_running) {
1567                                         if (mode == SEXP_MODE_CAMPAIGN) {
1568                                                 for (i=0; i<Campaign.num_missions; i++)
1569                                                         if (!SDL_strcasecmp(CTEXT(index), Campaign.missions[i].name)) {
1570                                                                 if ((i != Sexp_useful_number) && (Campaign.missions[i].level >= Campaign.missions[Sexp_useful_number].level))
1571                                                                         return SEXP_CHECK_INVALID_LEVEL;
1572
1573                                                                 break;
1574                                                         }
1575
1576                                                 if (i == Campaign.num_missions)
1577                                                         return SEXP_CHECK_INVALID_MISSION_NAME;
1578
1579                                         } else {
1580                                                 // mwa -- put the following if statement to prevent Fred errors for possibly valid
1581                                                 // conditions.  We should do something else here!!!
1582                                                 if ( (Operators[op].value == OP_PREVIOUS_EVENT_TRUE) || (Operators[op].value == OP_PREVIOUS_EVENT_FALSE) || (Operators[op].value == OP_PREVIOUS_EVENT_INCOMPLETE)
1583                                                         || (Operators[op].value == OP_PREVIOUS_GOAL_TRUE) || (Operators[op].value == OP_PREVIOUS_GOAL_FALSE) || (Operators[op].value == OP_PREVIOUS_GOAL_INCOMPLETE) )
1584                                                         break;
1585
1586                                                 if (!(*Mission_filename) || SDL_strcasecmp(Mission_filename, CTEXT(index)))
1587                                                         return SEXP_CHECK_INVALID_MISSION_NAME;
1588                                         }
1589                                 }
1590
1591                                 break;
1592
1593                         case OPF_GOAL_NAME:
1594                                 if (type2 != SEXP_ATOM_STRING)
1595                                         return SEXP_CHECK_TYPE_MISMATCH;
1596
1597                                 // we only need to check the campaign list if running in Fred and are in campaign mode.
1598                                 // otherwise, check the set of current goals
1599                                 if ( Fred_running && (mode == SEXP_MODE_CAMPAIGN) ) {
1600                                         z = find_parent_operator(index);
1601                                         SDL_assert(z >= 0);
1602                                         z = Sexp_nodes[z].rest;  // first argument of operator should be mission name
1603                                         SDL_assert(z >= 0);
1604                                         for (i=0; i<Campaign.num_missions; i++)
1605                                                 if (!SDL_strcasecmp(CTEXT(z), Campaign.missions[i].name))
1606                                                         break;
1607
1608                                         // read the goal/event list from the mission file if both num_goals and num_events
1609                                         // are < 0
1610                                         if ((Campaign.missions[i].num_goals <= 0) && (Campaign.missions[i].num_events <= 0) )
1611                                                 read_mission_goal_list(i);
1612                                         
1613                                         if (i < Campaign.num_missions) {
1614                                                 for (t=0; t<Campaign.missions[i].num_goals; t++)
1615                                                         if (!SDL_strcasecmp(CTEXT(index), Campaign.missions[i].goals[t].name))
1616                                                                 break;
1617
1618                                                 if (t == Campaign.missions[i].num_goals)
1619                                                         return SEXP_CHECK_INVALID_GOAL_NAME;
1620                                         }
1621                                 } else {
1622                                         // MWA -- short circuit evaluation of these things for now.
1623                                         if ( (Operators[op].value == OP_PREVIOUS_GOAL_TRUE) || (Operators[op].value == OP_PREVIOUS_GOAL_FALSE) || (Operators[op].value == OP_PREVIOUS_GOAL_INCOMPLETE) )
1624                                                 break;
1625
1626                                         for (i=0; i<Num_goals; i++)
1627                                                 if (!SDL_strcasecmp(CTEXT(index), Mission_goals[i].name))
1628                                                         break;
1629
1630                                         if (i == Num_goals)
1631                                                 return SEXP_CHECK_INVALID_GOAL_NAME;
1632                                 }
1633
1634                                 break;
1635
1636                         case OPF_EVENT_NAME:
1637                                 if ( type2 != SEXP_ATOM_STRING )
1638                                         return SEXP_CHECK_TYPE_MISMATCH;
1639
1640                                 // like above checking for goals, check events in the campaign only if in Fred
1641                                 // and only if in campaign mode.  Otherwise, check the current set of events
1642                                 if ( Fred_running && (mode == SEXP_MODE_CAMPAIGN) ) {
1643                                         z = find_parent_operator(index);
1644                                         SDL_assert(z >= 0);
1645                                         z = Sexp_nodes[z].rest;  // first argument of operator should be mission name
1646                                         SDL_assert(z >= 0);
1647                                         for (i=0; i<Campaign.num_missions; i++)
1648                                                 if (!SDL_strcasecmp(CTEXT(z), Campaign.missions[i].name))
1649                                                         break;
1650
1651                                         // read the goal/event list from the mission file if both num_goals and num_events
1652                                         // are < 0
1653                                         if ((Campaign.missions[i].num_goals <= 0) && (Campaign.missions[i].num_events <= 0) )
1654                                                 read_mission_goal_list(i);
1655                                         
1656                                         if (i < Campaign.num_missions) {
1657                                                 for (t=0; t<Campaign.missions[i].num_events; t++)
1658                                                         if (!SDL_strcasecmp(CTEXT(index), Campaign.missions[i].events[t].name))
1659                                                                 break;
1660
1661                                                 if (t == Campaign.missions[i].num_events)
1662                                                         return SEXP_CHECK_INVALID_EVENT_NAME;
1663                                         }
1664                                 } else {
1665                                         // MWA -- short circuit evaluation of these things for now.
1666                                         if ( (Operators[op].value == OP_PREVIOUS_EVENT_TRUE) || (Operators[op].value == OP_PREVIOUS_EVENT_FALSE) || (Operators[op].value == OP_PREVIOUS_EVENT_INCOMPLETE) )
1667                                                 break;
1668
1669                                         for ( i = 0; i < Num_mission_events; i++ ) {
1670                                                 if ( !SDL_strcasecmp(CTEXT(index), Mission_events[i].name) )
1671                                                         break;
1672                                         }
1673                                         if ( i == Num_mission_events )
1674                                                 return SEXP_CHECK_INVALID_EVENT_NAME;
1675                                 }
1676                                 break;
1677
1678                         case OPF_DOCKER_POINT:
1679                                 if (type2 != SEXP_ATOM_STRING)
1680                                         return SEXP_CHECK_TYPE_MISMATCH;
1681
1682                                 if (Fred_running) {
1683                                         int ship_num, model;
1684
1685                                         z = find_parent_operator(op_index);
1686                                         ship_num = ship_name_lookup(CTEXT(Sexp_nodes[z].rest));
1687                                         if (ship_num < 0) {
1688                                                 if (bad_index)
1689                                                         *bad_index = Sexp_nodes[z].rest;
1690
1691                                                 return SEXP_CHECK_INVALID_SHIP;  // should have already been caught earlier, but just in case..
1692                                         }
1693
1694                                         model = Ships[ship_num].modelnum;
1695                                         z = model_get_num_dock_points(model);
1696                                         for (i=0; i<z; i++)
1697                                                 if (!SDL_strcasecmp(CTEXT(index), model_get_dock_name(model, i)))
1698                                                         break;
1699
1700                                         if (i == z)
1701                                                 return SEXP_CHECK_INVALID_DOCKER_POINT;
1702                                 }
1703
1704                                 break;
1705
1706                         case OPF_DOCKEE_POINT:
1707                                 if (type2 != SEXP_ATOM_STRING)
1708                                         return SEXP_CHECK_TYPE_MISMATCH;
1709
1710                                 if (Fred_running) {
1711                                         int ship_num, model;
1712
1713                                         ship_num = ship_name_lookup(CTEXT(Sexp_nodes[op_index].rest));
1714                                         if (ship_num < 0) {
1715                                                 if (bad_index)
1716                                                         *bad_index = Sexp_nodes[op_index].rest;
1717
1718                                                 return SEXP_CHECK_INVALID_SHIP;  // should have already been caught earlier, but just in case..
1719                                         }
1720
1721                                         model = Ships[ship_num].modelnum;
1722                                         z = model_get_num_dock_points(model);
1723                                         for (i=0; i<z; i++)
1724                                                 if (!SDL_strcasecmp(CTEXT(index), model_get_dock_name(model, i)))
1725                                                         break;
1726
1727                                         if (i == z)
1728                                                 return SEXP_CHECK_INVALID_DOCKEE_POINT;
1729                                 }
1730
1731                                 break;
1732
1733                         case OPF_WHO_FROM:
1734                                 if (type2 != SEXP_ATOM_STRING)
1735                                         return SEXP_CHECK_TYPE_MISMATCH;
1736
1737                                 if (*CTEXT(index) != '#') {  // not a manual source?
1738                                         //if ( !SDL_strcasecmp(CTEXT(index), "<any allied>") )
1739                                         //      return SEXP_CHECK_INVALID_MSG_SOURCE;
1740
1741                                         if ( SDL_strcasecmp(CTEXT(index), "<any wingman>"))  // not a special token?
1742                                                 if ((ship_name_lookup(CTEXT(index)) < 0) && (wing_name_lookup(CTEXT(index), 1) < 0))  // is it in the mission?
1743                                                         if (Fred_running || mission_parse_ship_arrived(CTEXT(index)))  // == 0 when still on arrival list
1744                                                                 return SEXP_CHECK_INVALID_MSG_SOURCE;
1745                                 }
1746
1747                                 break;
1748                                 
1749                         case OPF_KEYPRESS:
1750                                 if (type2 != SEXP_ATOM_STRING)
1751                                         return SEXP_CHECK_TYPE_MISMATCH;
1752
1753                                 break;
1754
1755                         case OPF_SKILL_LEVEL:
1756                                 if ( type2 != SEXP_ATOM_STRING )
1757                                         return SEXP_CHECK_TYPE_MISMATCH;
1758
1759                                 for (i = 0; i < NUM_SKILL_LEVELS; i++) {
1760                                         if ( !SDL_strcasecmp(CTEXT(index), Skill_level_names(i, 0)) )
1761                                                 break;
1762                                 }
1763                                 if ( i == NUM_SKILL_LEVELS )
1764                                         return SEXP_CHECK_INVALID_SKILL_LEVEL;
1765                                 break;
1766
1767                         case OPF_MEDAL_NAME:
1768                                 if ( type2 != SEXP_ATOM_STRING)
1769                                         return SEXP_CHECK_TYPE_MISMATCH;
1770
1771                                 for (i = 0; i < NUM_MEDALS; i++) {
1772                                         if ( !SDL_strcasecmp(CTEXT(index), Medals[i].name) )
1773                                                 break;
1774                                 }
1775
1776                                 if ( i == NUM_MEDALS )
1777                                         return SEXP_CHECK_INVALID_MEDAL_NAME;
1778                                 break;
1779
1780                         case OPF_HUGE_WEAPON:
1781                         case OPF_WEAPON_NAME:
1782                                 if ( type2 != SEXP_ATOM_STRING )
1783                                         return SEXP_CHECK_TYPE_MISMATCH;
1784
1785                                 for (i = 0; i < Num_weapon_types; i++ ) {
1786                                         if ( !SDL_strcasecmp(CTEXT(index), Weapon_info[i].name) )
1787                                                 break;
1788                                 }
1789
1790                                 if ( i == Num_weapon_types )
1791                                         return SEXP_CHECK_INVALID_WEAPON_NAME;
1792
1793                                 // we need to be sure that for huge weapons, the WIF_HUGE flag is set
1794                                 if ( type == OPF_HUGE_WEAPON ) {
1795                                         if ( !(Weapon_info[i].wi_flags & WIF_HUGE) )
1796                                                 return SEXP_CHECK_INVALID_WEAPON_NAME;
1797                                 }
1798
1799                                 break;
1800         
1801                         case OPF_SHIP_CLASS_NAME:
1802                                 if ( type2 != SEXP_ATOM_STRING )
1803                                         return SEXP_CHECK_TYPE_MISMATCH;
1804
1805                                 for (i = 0; i < Num_ship_types; i++ ) {
1806                                         if ( !SDL_strcasecmp(CTEXT(index), Ship_info[i].name) )
1807                                                 break;
1808                                 }
1809
1810                                 if ( i == Num_ship_types )
1811                                         return SEXP_CHECK_INVALID_SHIP_CLASS_NAME;
1812                                 break;
1813
1814                         case OPF_HUD_GAUGE_NAME:
1815                                 if ( type2 != SEXP_ATOM_STRING )
1816                                         return SEXP_CHECK_TYPE_MISMATCH;
1817
1818                                 for ( i = 0; i < NUM_HUD_GAUGES; i++ ) {
1819                                         if ( !SDL_strcasecmp(CTEXT(index), HUD_gauge_text[i]) )
1820                                                 break;
1821                                 }
1822
1823                                 // if we reached the end of the list, then the name is invalid
1824                                 if ( i == NUM_HUD_GAUGES )
1825                                         return SEXP_CHECK_INVALID_GAUGE_NAME;
1826                                 
1827                                 break;
1828
1829                         case OPF_JUMP_NODE_NAME:
1830                                 if ( type2 != SEXP_ATOM_STRING )
1831                                         return SEXP_CHECK_TYPE_MISMATCH;
1832
1833                                 for ( i = 0; i < Num_jump_nodes; i++ ) {
1834                                         if ( !SDL_strcasecmp(Jump_nodes[i].name, CTEXT(index)) )
1835                                                 break;
1836                                 }
1837
1838                                 if ( i == Num_jump_nodes )
1839                                         return SEXP_CHECK_INVALID_JUMP_NODE;
1840
1841                                 break;
1842
1843
1844                         case OPF_VARIABLE_NAME:
1845                                 if ( Fred_running ) {
1846                                         if ( get_index_sexp_variable_name(Sexp_nodes[index].text)  == -1) {
1847                                                 return SEXP_CHECK_INVALID_VARIABLE;
1848                                         }
1849                                 }
1850                                 // if Fred not running anything goes
1851                                 break;
1852
1853                         case OPF_AMBIGUOUS:
1854                                 // type checking for modify-variable
1855                                 // string or number -- anything goes
1856                                 break;                                          
1857
1858                         default:
1859                                 Int3();  // currently unhandled argument format (so add it now)
1860                 }
1861
1862                 index = Sexp_nodes[index].rest;
1863                 argnum++;
1864         }
1865
1866         return 0;
1867 }
1868
1869 //      Stuff a string (" chars ") in *str, return length.
1870 //      Updates Mp.
1871 int get_string(char *str)
1872 {
1873         int     len;
1874
1875         len = strcspn(Mp + 1, "\"");
1876         SDL_strlcpy(str, Mp + 1, len+1);
1877
1878         Mp += len + 2;
1879         return len;
1880 }
1881
1882
1883 // get text to stuff into Sexp_node in case of variable
1884 // if Fred_running - stuff Sexp_variables[].variable_name
1885 // otherwise - stuff index into Sexp_variables array.
1886 void get_sexp_text_for_variable(char *text, char *token)
1887 {
1888         int end_index;
1889         int sexp_var_index;
1890         
1891         // get variable name (up to '['
1892         end_index = strcspn(token, "[");
1893         SDL_assert( (end_index != 0) && (end_index < TOKEN_LENGTH-1) );
1894         SDL_strlcpy(text, token, end_index+1);
1895
1896         if ( !Fred_running ) {
1897                 // freespace - get index into Sexp_variables array
1898                 sexp_var_index = get_index_sexp_variable_name(text);
1899                 SDL_assert(sexp_var_index != -1);
1900                 SDL_snprintf(text, TOKEN_LENGTH, "%d", sexp_var_index);
1901         }
1902 }
1903
1904
1905 // returns the first sexp index of data this function allocates. (start of this sexp)
1906 // recursive function - always sets first and then rest
1907 int get_sexp(char *token)
1908 {
1909         int start, node, last, len, op, count;
1910         char variable_text[TOKEN_LENGTH];
1911
1912         // start - the node allocated in first instance of fuction
1913         // node - the node allocated in current instance of function
1914         // count - number of nodes allocated this instance of function [do we set last.rest or first]
1915         // variable - whether string or number is a variable referencing Sexp_variables
1916
1917         // initialization
1918         start = last = -1;
1919         count = 0;
1920
1921         ignore_white_space();
1922         while (*Mp != ')') {
1923                 SDL_assert(*Mp != EOF_CHAR);
1924                 if (*Mp == '(') {
1925                         // Sexp list
1926                         Mp++;
1927                         node = alloc_sexp("", SEXP_LIST, SEXP_ATOM_LIST, get_sexp(token), -1);
1928
1929                 } else if (*Mp == '\"') {
1930                         // Sexp string
1931                         len = strcspn(Mp + 1, "\"");
1932                         
1933                         SDL_assert(Mp[len + 1] == '\"');    // hit EOF first (unterminated string)
1934                         SDL_assert_release(len < TOKEN_LENGTH);  // token is too long.
1935
1936                         // check if string variable
1937                         if ( *(Mp + 1) == SEXP_VARIABLE_CHAR ) {
1938
1939                                 // reduce length by 1 for end \"
1940                                 int length = len - 1;
1941                                 SDL_assert_release(length >= 1);
1942                         //      SDL_assert(length < 2*TOKEN_LENGTH+2);
1943
1944                                 // start copying after skipping 1st char
1945                                 SDL_strlcpy(token, Mp + 2, length+1);
1946
1947                                 get_sexp_text_for_variable(variable_text, token);
1948                                 node = alloc_sexp(variable_text, (SEXP_ATOM | SEXP_FLAG_VARIABLE), SEXP_ATOM_STRING, -1, -1);
1949                         } else {
1950                                 SDL_strlcpy(token, Mp + 1, len+1);
1951
1952                                 node = alloc_sexp(token, SEXP_ATOM, SEXP_ATOM_STRING, -1, -1);
1953                         }
1954
1955                         // bump past closing \" by 1 char
1956                         Mp += len + 2;
1957
1958                 } else {
1959                         // Sexp operator or number
1960                         len = 0;
1961                         bool variable = false;
1962                         while (*Mp != ')' && !is_white_space(*Mp)) {
1963                                 if ( (len == 0) && (*Mp == SEXP_VARIABLE_CHAR) ) {
1964                                         variable = true;
1965                                         Mp++;
1966                                         continue;
1967                                 }
1968                                 SDL_assert(*Mp != EOF_CHAR);
1969                                 SDL_assert(len < TOKEN_LENGTH - 1);
1970                                 token[len++] = *Mp++;
1971                         }
1972
1973                         token[len] = 0;
1974
1975                         op = identify_operator(token);
1976                         if (op != -1) {
1977                                 node = alloc_sexp(token, SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, -1);
1978                         } else {
1979                                 if ( variable ) {
1980                                         // convert token text for variable
1981                                         get_sexp_text_for_variable(variable_text, token);
1982
1983                                         node = alloc_sexp(variable_text, (SEXP_ATOM | SEXP_FLAG_VARIABLE), SEXP_ATOM_NUMBER, -1, -1);
1984                                 } else {
1985                                         node = alloc_sexp(token, SEXP_ATOM, SEXP_ATOM_NUMBER, -1, -1);
1986                                 }
1987                         }
1988                 }
1989
1990                 // update links
1991                 if (count++) {
1992                         SDL_assert(last != -1);
1993                         Sexp_nodes[last].rest = node;
1994                 } else {
1995                         start = node;
1996                 }
1997
1998                 SDL_assert(node != -1);  // ran out of nodes.  Time to raise the MAX!
1999                 last = node;
2000                 ignore_white_space();
2001         }
2002
2003         Mp++;  // skip past the ')'
2004         return start;
2005 }
2006
2007
2008 // Stuffs a list of sexp variables
2009 int stuff_sexp_variable_list()
2010 {       
2011         int count;
2012         char var_name[TOKEN_LENGTH];
2013         char default_value[TOKEN_LENGTH];
2014         char str_type[TOKEN_LENGTH];
2015         int index;
2016         int type;
2017
2018         count = 0;
2019         required_string("$Variables:");
2020         ignore_white_space();
2021
2022         // check for start of list
2023         if (*Mp != '(') {
2024                 error_display(1, "Reading sexp variable list.  Found [%c].  Expecting '('.\n", *Mp);
2025                 throw PARSE_ERROR_INT_LIST;
2026         }
2027
2028         Mp++;
2029         ignore_white_space();
2030
2031         while (*Mp != ')') {
2032                 SDL_assert(count < MAX_SEXP_VARIABLES);
2033
2034                 // get index - for debug
2035                 stuff_int(&index);
2036                 ignore_gray_space();
2037
2038                 // get var_name
2039                 get_string(var_name);
2040                 ignore_gray_space();
2041
2042                 // get default_value;
2043                 get_string(default_value);
2044                 ignore_gray_space();
2045
2046                 // get type
2047                 get_string(str_type);
2048                 ignore_white_space();
2049
2050
2051                 if (!SDL_strcasecmp(str_type, "number")) {
2052                         type = SEXP_VARIABLE_NUMBER;
2053                 } else if (!SDL_strcasecmp(str_type, "string")) {
2054                         type = SEXP_VARIABLE_STRING;
2055                 } else if (!SDL_strcasecmp(str_type, "block")) {
2056                         type = SEXP_VARIABLE_BLOCK | SEXP_VARIABLE_BLOCK_EXP;
2057                 } else {
2058                         type = SEXP_VARIABLE_UNKNOWN;
2059                         Int3();
2060                 }
2061
2062                 count++;
2063
2064                 // check if variable name already exists
2065                 if ( (type == SEXP_VARIABLE_NUMBER) || (type == SEXP_VARIABLE_STRING) ) {
2066                         SDL_assert(get_index_sexp_variable_name(var_name) == -1);
2067                 }
2068
2069                 sexp_add_variable(default_value, var_name, type, index);
2070         }
2071
2072         Mp++;
2073
2074         return count;
2075 }
2076
2077 //
2078 void build_sexp_text_string(char *buffer, const int max_bufsize, int node, int mode)
2079 {
2080         if (Sexp_nodes[node].type & SEXP_FLAG_VARIABLE) {
2081
2082                 int sexp_variables_index = get_index_sexp_variable_name(Sexp_nodes[node].text);
2083                 SDL_assert(sexp_variables_index != -1);
2084                 SDL_assert( (Sexp_variables[sexp_variables_index].type & SEXP_VARIABLE_NUMBER) || (Sexp_variables[sexp_variables_index].type & SEXP_VARIABLE_STRING) );
2085
2086                 // number
2087                 if (Sexp_nodes[node].subtype == SEXP_ATOM_NUMBER) {
2088                         SDL_assert(Sexp_variables[sexp_variables_index].type & SEXP_VARIABLE_NUMBER);
2089                 
2090                         // Error check - can be Fred or Freespace
2091                         if (mode == SEXP_ERROR_CHECK_MODE) {
2092                                 if ( Fred_running ) {
2093                                         SDL_snprintf(buffer, max_bufsize, "%s[%s] ", Sexp_nodes[node].text, Sexp_variables[sexp_variables_index].text);
2094                                 } else {
2095                                         SDL_snprintf(buffer, max_bufsize, "%s[%s] ", Sexp_variables[sexp_variables_index].variable_name, Sexp_variables[sexp_variables_index].text);
2096                                 }
2097                         } else {
2098                                 // Save as string - only  Fred
2099                                 SDL_assert(mode == SEXP_SAVE_MODE);
2100                                 SDL_snprintf(buffer, max_bufsize, "@%s[%s] ", Sexp_nodes[node].text, Sexp_variables[sexp_variables_index].text);
2101                         }
2102                 } else {
2103                         // string
2104                         SDL_assert(Sexp_nodes[node].subtype == SEXP_ATOM_STRING);
2105                         SDL_assert(Sexp_variables[sexp_variables_index].type & SEXP_VARIABLE_STRING);
2106
2107                         // Error check - can be Fred or Freespace
2108                         if (mode == SEXP_ERROR_CHECK_MODE) {
2109                                 if ( Fred_running ) {
2110                                         SDL_snprintf(buffer, max_bufsize, "%s[%s] ", Sexp_variables[sexp_variables_index].variable_name, Sexp_variables[sexp_variables_index].text);
2111                                 } else {
2112                                         SDL_snprintf(buffer, max_bufsize, "%s[%s] ", Sexp_nodes[node].text, Sexp_variables[sexp_variables_index].text);
2113                                 }
2114                         } else {
2115                                 // Save as string - only Fred
2116                                 SDL_assert(mode == SEXP_SAVE_MODE);
2117                                 SDL_snprintf(buffer, max_bufsize, "\"@%s[%s]\" ", Sexp_nodes[node].text, Sexp_variables[sexp_variables_index].text);
2118                         }
2119                 }
2120         } else {
2121                 // not a variable
2122                 if (Sexp_nodes[node].subtype == SEXP_ATOM_STRING) {
2123                         SDL_snprintf(buffer, max_bufsize, "\"%s\" ", CTEXT(node));
2124                 } else {
2125                         SDL_snprintf(buffer, max_bufsize, "%s ", CTEXT(node));
2126                 }
2127         }
2128
2129 }
2130
2131
2132 int build_sexp_string(int cur_node, int level, int mode)
2133 {
2134         char    pstr[128];
2135         int len, offset, node;
2136
2137         Sexp_build_flag = 0;
2138         offset = strlen(Sexp_string);
2139         SDL_strlcat(Sexp_string, "( ", Sexp_string_len);
2140         node = cur_node;
2141         while (node != -1) {
2142                 SDL_assert(node >= 0 && node < MAX_SEXP_NODES);
2143                 if (Sexp_nodes[node].first == -1) {
2144                         // build text to string
2145                         build_sexp_text_string(pstr, SDL_arraysize(pstr), node, mode);
2146                         SDL_strlcat(Sexp_string, pstr, Sexp_string_len);
2147
2148                 } else {
2149                         build_sexp_string(Sexp_nodes[node].first, level + 1, mode);
2150                 }
2151
2152                 node = Sexp_nodes[node].rest;
2153         }
2154
2155         SDL_strlcat(Sexp_string, ") ", Sexp_string_len);
2156         len = strlen(Sexp_string) - offset;
2157         if (len > 40) {
2158                 Sexp_string[offset] = 0;
2159                 build_extended_sexp_string(cur_node, level, mode);
2160                 return 1;
2161         }
2162
2163         return 0;
2164 }
2165
2166 void build_extended_sexp_string(int cur_node, int level, int mode)
2167 {
2168         char pstr[128];
2169         int i, flag = 0, node;
2170
2171         SDL_strlcat(Sexp_string, "( ", Sexp_string_len);
2172         node = cur_node;
2173         while (node != -1) {
2174                 if (flag)  // not the first line?
2175                         for (i=0; i<level + 1; i++)
2176                                 SDL_strlcat(Sexp_string, "   ", Sexp_string_len);
2177
2178                 flag = 1;
2179                 SDL_assert(node >= 0 && node < MAX_SEXP_NODES);
2180                 if (Sexp_nodes[node].first == -1) {
2181                         build_sexp_text_string(pstr, SDL_arraysize(pstr),node, mode);
2182                         SDL_strlcat(Sexp_string, pstr, Sexp_string_len);
2183
2184                 } else {
2185                         build_sexp_string(Sexp_nodes[node].first, level + 1, mode);
2186                 }
2187
2188                 SDL_strlcat(Sexp_string, "\n", Sexp_string_len);
2189                 node = Sexp_nodes[node].rest;
2190         }
2191
2192         for (i=0; i<level; i++)
2193                 SDL_strlcat(Sexp_string, "   ", Sexp_string_len);
2194
2195         SDL_strlcat(Sexp_string, ")", Sexp_string_len);
2196 }
2197
2198 void convert_sexp_to_string(int cur_node, char *outstr, const int outstr_len, int mode)
2199 {
2200         Sexp_string = outstr;
2201         Sexp_string_len = outstr_len;
2202         *outstr = 0;
2203         if (cur_node >= 0)
2204                 build_sexp_string(cur_node, 0, mode);
2205         else
2206                 SDL_strlcpy(Sexp_string, "( )", Sexp_string_len);
2207 }
2208
2209 // determine if the named ship or wing hasn't arrived yet (wing or ship must be on arrival list)
2210 int sexp_query_has_yet_to_arrive(char *name)
2211 {
2212         int i;
2213
2214         if (ship_query_state(name) < 0)
2215                 return 1;
2216
2217         i = wing_name_lookup(name, 1);
2218
2219         // has not arrived yet, and never will arrive
2220         if ((i >= 0) && (Wings[i].num_waves >= 0) && (Wings[i].flags & WF_NEVER_EXISTED)){
2221                 return 1;
2222         }
2223
2224         // has not arrived yet
2225         if ((i >= 0) && (Wings[i].num_waves >= 0) && !Wings[i].total_arrived_count){
2226                 return 1;
2227         }
2228
2229         return 0;
2230 }
2231
2232 // arithmetic functions
2233 int add_sexps(int n)
2234 {
2235         int     sum = 0, val;
2236
2237         if (n != -1) {
2238                 if ( CAR(n) != -1)
2239                         sum = eval_sexp( CAR(n) );
2240                 else
2241                         sum = atoi( CTEXT(n) );
2242
2243                 // be sure to check for the NAN value when doing arithmetic -- this value should
2244                 // get propagated to the next highest function.
2245                 if ( Sexp_nodes[CAR(n)].value == SEXP_NAN )
2246                         return SEXP_NAN;
2247                 else if ( Sexp_nodes[CAR(n)].value == SEXP_NAN_FOREVER )
2248                         return SEXP_NAN_FOREVER;
2249
2250                 while (CDR(n) != -1) {
2251                         val = eval_sexp( CDR(n) );
2252                         // be sure to check for the NAN value when doing arithmetic -- this value should
2253                         // get propagated to the next highest function.
2254                         if ( Sexp_nodes[CDR(n)].value == SEXP_NAN )
2255                                 return SEXP_NAN;
2256                         else if ( Sexp_nodes[CAR(n)].value == SEXP_NAN_FOREVER )
2257                                 return SEXP_NAN_FOREVER;
2258                         sum += val;
2259                         n = CDR(n);
2260                 }
2261         }
2262
2263         return sum;
2264 }
2265
2266 int sub_sexps(int n)
2267 {
2268         int     sum = 0;
2269
2270         if (n != -1) { 
2271                 if (Sexp_nodes[n].first != -1)
2272                         sum = eval_sexp(CAR(n));
2273                 else
2274                         sum = atoi(CTEXT(n));
2275
2276                 while (CDR(n) != -1) {
2277                         sum -= eval_sexp(CDR(n));
2278                         n = CDR(n);
2279                 }
2280         }
2281
2282         return sum;
2283 }
2284
2285 int mul_sexps(int n)
2286 {
2287         int     sum = 0;
2288
2289         if (n != -1) {
2290                 if (Sexp_nodes[n].first != -1)
2291                         sum = eval_sexp(Sexp_nodes[n].first);
2292                 else
2293                         sum = atoi(CTEXT(n));
2294
2295                 while (Sexp_nodes[n].rest != -1) {
2296                         sum *= eval_sexp(Sexp_nodes[n].rest);
2297                         n = Sexp_nodes[n].rest;
2298                 }
2299         }
2300
2301         return sum;
2302 }
2303
2304 int div_sexps(int n)
2305 {
2306         int     sum = 0;
2307
2308         if (n != -1) {
2309                 if (Sexp_nodes[n].first != -1)
2310                         sum = eval_sexp(Sexp_nodes[n].first);
2311                 else
2312                         sum = atoi(CTEXT(n));
2313
2314                 while (Sexp_nodes[n].rest != -1) {
2315                         sum /= eval_sexp(Sexp_nodes[n].rest);
2316                         n = Sexp_nodes[n].rest;
2317                 }
2318         }
2319
2320         return sum;
2321 }
2322
2323 int mod_sexps(int n)
2324 {
2325         int     sum = 0;
2326
2327         if (n != -1) {
2328                 if (Sexp_nodes[n].first != -1)
2329                         sum = eval_sexp(Sexp_nodes[n].first);
2330                 else
2331                         sum = atoi(CTEXT(n));
2332
2333                 while (Sexp_nodes[n].rest != -1) {
2334                         sum = sum % eval_sexp(Sexp_nodes[n].rest);
2335                         n = Sexp_nodes[n].rest;
2336                 }
2337         }
2338
2339         return sum;
2340 }
2341
2342 int rand_internal(int low, int high)
2343 {
2344         int diff;
2345
2346         // get diff - don't allow negative or zero
2347         diff = high - low;
2348         if (diff < 0) {
2349                 diff = 0;
2350         }
2351
2352         return (low + myrand() % (diff + 1));
2353 }
2354
2355
2356 int rand_sexp(int n, int multiple=0)
2357 {
2358         int low = 0;
2359         int high = 0;
2360         int rand_num = 0;
2361
2362         if (n != -1) {
2363                 if (Sexp_nodes[n].value == SEXP_NUM_EVAL) {
2364                         // don't regenerate new random number
2365                         rand_num = atoi(CTEXT(n));
2366                 } else {
2367                         // get low
2368 //                      if (Sexp_nodes[n].first != -1) {
2369 //                              low = eval_sexp(Sexp_nodes[n].first);
2370 //                      } else {
2371 //                              low = atoi(CTEXT(n));
2372 //                      }
2373                         low = num_eval(n);
2374
2375                         // get high
2376                         high = num_eval(CDR(n));
2377
2378                         // get the random number
2379                         rand_num = rand_internal(low, high);
2380
2381                         if (!multiple) {
2382                                 // set .value and .text so random number is generated only once.
2383                                 Sexp_nodes[n].value = SEXP_NUM_EVAL;
2384                                 SDL_snprintf(Sexp_nodes[n].text, TOKEN_LENGTH, "%d", rand_num);
2385                         }
2386                 }
2387         }
2388
2389         return rand_num;
2390 }
2391
2392 // boolean evaluation functions.  Evaluate all sexpressions in the 'or' operator.  Needed to mark
2393 // entries in the mission log as essential so when pruning the log, we know which entries we might
2394 // need to keep.
2395 int sexp_or(int n)
2396 {
2397         int all_false, result;
2398
2399         all_false = 1;
2400         result = 0;
2401         if (n != -1) {
2402                 if (Sexp_nodes[n].first != -1) {
2403                         result |= eval_sexp(Sexp_nodes[n].first);
2404                         if ( Sexp_nodes[Sexp_nodes[n].first].value == SEXP_KNOWN_TRUE )
2405                                 return SEXP_KNOWN_TRUE;                                                                                                                 // if one of the OR clauses is TRUE, whole clause is true
2406                         if ( Sexp_nodes[Sexp_nodes[n].first].value != SEXP_KNOWN_FALSE )                // if the value is still unknown, they all can't be false
2407                                 all_false = 0;
2408                 } else
2409                         result |= atoi(CTEXT(n));
2410
2411                 // don't return on true value -- keep evaluating for mission log purposes
2412                 //if ( result )
2413                 //      return result;
2414
2415                 while (Sexp_nodes[n].rest != -1) {
2416                         result |= eval_sexp(Sexp_nodes[n].rest);
2417                         if ( Sexp_nodes[Sexp_nodes[n].rest].value == SEXP_KNOWN_TRUE )
2418                                 return SEXP_KNOWN_TRUE;                                                                                                                 // if one of the OR clauses is TRUE, whole clause is true
2419                         if ( Sexp_nodes[Sexp_nodes[n].rest].value != SEXP_KNOWN_FALSE )         // if the value is still unknown, they all can't be false
2420                                 all_false = 0;
2421                         n = Sexp_nodes[n].rest;
2422                         // don't return on true value -- keep evaluating for mission log purposes
2423                         //if ( result )
2424                         //      return result;
2425                 }
2426         }
2427
2428         if ( all_false )
2429                 return SEXP_KNOWN_FALSE;
2430
2431         return result;
2432 }
2433
2434 // this function does the 'and' operator.  It will short circuit evaluation  *but* it will still
2435 // evaluate other members of the and construct.  I do this because I need events in the mission log
2436 // to get marked as essential for goal purposes, and evaluation is pretty much the only way
2437 int sexp_and(int n)
2438 {
2439         int all_true, result;
2440
2441         result = -1;
2442         all_true = 1;
2443         if (n != -1) {
2444                 if (Sexp_nodes[n].first != -1) {
2445                         result &= eval_sexp( CAR(n) );
2446                         if ( Sexp_nodes[Sexp_nodes[n].first].value == SEXP_KNOWN_FALSE )
2447                                 return SEXP_KNOWN_FALSE;                                                                                                                // if one of the AND clauses is FALSE, whole clause is false
2448                         if ( Sexp_nodes[Sexp_nodes[n].first].value != SEXP_KNOWN_TRUE )         // if the value is still unknown, they all can't be true
2449                                 all_true = 0;
2450                 } else
2451                         result &= atoi(CTEXT(n));
2452
2453                 // don't short circuit -- evaluate everything for purposes of marking mission log
2454                 //if ( !result )
2455                 //      return result;
2456
2457                 while (Sexp_nodes[n].rest != -1) {
2458                         int new_result;
2459
2460                         new_result = eval_sexp( CDR(n) );
2461                         result &= new_result;
2462                         if ( Sexp_nodes[Sexp_nodes[n].rest].value == SEXP_KNOWN_FALSE )
2463                                 return SEXP_KNOWN_FALSE;                                                                                                                        // if one of the OR clauses is TRUE, whole clause is true
2464                         if ( Sexp_nodes[Sexp_nodes[n].rest].value != SEXP_KNOWN_TRUE )                          // if the value is still unknown, they all can't be false
2465                                 all_true = 0;
2466                         // don't short circuit -- evaluate everything for purposes of marking mission log
2467                         //if ( !result )
2468                                 //return result;
2469
2470                         n = Sexp_nodes[n].rest;
2471                 }
2472         }
2473
2474         if ( all_true )
2475                 return SEXP_KNOWN_TRUE;
2476
2477         return result;
2478 }
2479
2480 // this version of the 'and' operator determines whether or not it's arguments become true
2481 // in the order in which they are specified in the when statement.  Should be a simple matter of 
2482 // seeing if anything evaluates to true later than something that evalueated to false
2483 int sexp_and_in_sequence(int n)
2484 {
2485         int result = -1;
2486         int all_true;
2487
2488         all_true = 1;                                                                                   // represents whether or not all nodes we have seen so far are true
2489         if (n != -1) {
2490                 if (Sexp_nodes[n].first != -1) {
2491                         result &= eval_sexp( CAR(n) );
2492                         if ( Sexp_nodes[Sexp_nodes[n].first].value == SEXP_KNOWN_FALSE )
2493                                 return SEXP_KNOWN_FALSE;                                                                                                                // if one of the AND clauses is FALSE, whole clause is false
2494                         if ( Sexp_nodes[Sexp_nodes[n].first].value != SEXP_KNOWN_TRUE )         // if value is true, mark our all_true variable for later checking
2495                                 all_true = 0;
2496                 } else
2497                         result &= atoi(CTEXT(n));
2498
2499                 // a little test -- if the previous sexpressions was true, then mark the node itself as always
2500                 // true.  I did this because of the distance function.  It might become true, then when waiting for
2501                 // the second evalation, it might become false, rendering this function false.  So, when one becomes
2502                 // true -- mark it true forever.
2503                 if ( result )
2504                         Sexp_nodes[Sexp_nodes[n].first].value = SEXP_KNOWN_TRUE;
2505
2506                 while (Sexp_nodes[n].rest != -1) {
2507                         int next_result;
2508
2509                         next_result = eval_sexp( CDR(n) );
2510                         if ( next_result && !result )                           // if current result is true, and our running result is false, thngs didn't become true in order
2511                                 return SEXP_KNOWN_FALSE;
2512                         result &= next_result;
2513                         if ( Sexp_nodes[Sexp_nodes[n].rest].value == SEXP_KNOWN_FALSE )
2514                                 return SEXP_KNOWN_FALSE;                                                                                                                        // if one of the OR clauses is TRUE, whole clause is true
2515                         if ( Sexp_nodes[Sexp_nodes[n].rest].value != SEXP_KNOWN_TRUE )                          // if the value is still unknown, they all can't be false
2516                                 all_true = 0;
2517                         // see comment above for explanation of next lines
2518                         if ( result )
2519                                 Sexp_nodes[Sexp_nodes[n].rest].value = SEXP_KNOWN_TRUE;
2520                         n = Sexp_nodes[n].rest;
2521                 }
2522         }
2523
2524         if ( all_true )
2525                 return SEXP_KNOWN_TRUE;
2526
2527         return result;
2528 }
2529
2530 // for these four basic boolean operations (not, <, >, and =), we have special cases that we must deal
2531 // with.  We have sexpressions operators that might return a NAN type return value (such as the distance
2532 // between two ships when one of the ships is destroyed or departed).  These operations need to check for
2533 // this special NAN value and adjust their return types accordingly.  NAN values represent false return values
2534 int sexp_not( int n )
2535 {
2536         int result = 0;
2537
2538         if (n != -1) {
2539                 if (Sexp_nodes[n].first != -1) {
2540                         result = eval_sexp( CAR(n) );
2541                         if ( Sexp_nodes[CAR(n)].value == SEXP_KNOWN_FALSE )
2542                                 return SEXP_KNOWN_TRUE;                                                                                         // not KNOWN_FALSE == KNOWN_TRUE;
2543                         else if ( Sexp_nodes[CAR(n)].value == SEXP_KNOWN_TRUE )         // not KNOWN_TRUE == KNOWN_FALSE
2544                                 return SEXP_KNOWN_FALSE;
2545                         else if ( Sexp_nodes[CAR(n)].value == SEXP_NAN )                                // not NAN == TRUE (I think)
2546                                 return 1;
2547                         else if ( Sexp_nodes[CAR(n)].value == SEXP_NAN_FOREVER )
2548                                 return 1;
2549                 } else
2550                         result = atoi(CTEXT(n));
2551         }
2552
2553         return !result;
2554 }
2555
2556 int sexp_gt(int n)
2557 {
2558         int exp1, exp2;
2559
2560         exp1 = eval_sexp( n );
2561         exp2 = eval_sexp( CDR(n) );
2562         
2563         // check for the NAN value
2564         if ( ( Sexp_nodes[CAR(n)].value == SEXP_NAN ) || ( Sexp_nodes[CDR(n)].value == SEXP_NAN ) )
2565                 return 0;
2566         else if ( ( Sexp_nodes[CAR(n)].value == SEXP_NAN_FOREVER ) || ( Sexp_nodes[CDR(n)].value == SEXP_NAN_FOREVER ) )
2567                 return SEXP_KNOWN_FALSE;
2568
2569         if ( exp1 > exp2 )
2570                 return 1;
2571         return 0;
2572 }
2573
2574 int sexp_lt(int n)
2575 {
2576         int exp1, exp2;
2577
2578         exp1 = eval_sexp( n );
2579         exp2 = eval_sexp( CDR(n) );
2580
2581                 // check for the NAN value
2582         if ( ( Sexp_nodes[CAR(n)].value == SEXP_NAN ) || ( Sexp_nodes[CDR(n)].value == SEXP_NAN ) )
2583                 return 0;
2584         else if ( ( Sexp_nodes[CAR(n)].value == SEXP_NAN_FOREVER ) || ( Sexp_nodes[CDR(n)].value == SEXP_NAN_FOREVER ) )
2585                 return SEXP_KNOWN_FALSE;
2586
2587         if ( exp1 < exp2 )
2588                 return 1;
2589         return 0;
2590 }
2591
2592 int sexp_equal(int n)
2593 {
2594         int exp1, exp2;
2595
2596         exp1 = eval_sexp( n );
2597         exp2 = eval_sexp( CDR(n) );
2598
2599                 // check for the NAN value
2600         if ( ( Sexp_nodes[CAR(n)].value == SEXP_NAN ) || ( Sexp_nodes[CDR(n)].value == SEXP_NAN ) )
2601                 return 0;
2602         else if ( ( Sexp_nodes[CAR(n)].value == SEXP_NAN_FOREVER ) || ( Sexp_nodes[CDR(n)].value == SEXP_NAN_FOREVER ) )
2603                 return SEXP_KNOWN_FALSE;
2604
2605         if ( exp1 == exp2 )
2606                 return 1;
2607         return 0;
2608 }
2609
2610 // Evaluate if given ship is destroyed.
2611 //      Return true if the ship in the expression has been destroyed.
2612 int sexp_is_destroyed(int n, fix *latest_time)
2613 {
2614         char    *name;
2615         int     count, num_destroyed, wing_index;
2616         fix     time;
2617
2618         SDL_assert ( n != -1 );
2619
2620         count = 0;
2621         num_destroyed = 0;
2622         wing_index = -1;
2623         while (n != -1) {
2624                 count++;
2625                 name = CTEXT(n);
2626
2627                 if (sexp_query_has_yet_to_arrive(name))
2628                         return SEXP_CANT_EVAL;
2629
2630                 // check to see if this ship/wing has departed.  If so, then function is known false
2631                 if ( mission_log_get_time (LOG_SHIP_DEPART, name, NULL, NULL) || mission_log_get_time (LOG_WING_DEPART, name, NULL, NULL) )
2632                         return SEXP_KNOWN_FALSE;
2633
2634                 // check the mission log.  If ship/wing not destroyed, immediately return 0.
2635                 if ( mission_log_get_time(LOG_SHIP_DESTROYED, name, NULL, &time) || mission_log_get_time(LOG_WING_DESTROYED, name, NULL, &time) || mission_log_get_time(LOG_SELF_DESTRUCT, name, NULL, &time)) {
2636                         num_destroyed++;
2637                         if ( latest_time && (time > *latest_time) )
2638                                 *latest_time = time;
2639                 } else {
2640                         // ship or wing isn't destroyed -- add to directive count
2641                         if ( (wing_index = wing_name_lookup( name, 1 )) >= 0 ) {
2642                                 Directive_count += Wings[wing_index].current_count;
2643                         } else
2644                                 Directive_count++;
2645                 }
2646
2647                 // move to next ship/wing in list
2648                 n = CDR(n);
2649         }
2650
2651         // special case to mark a directive for destroy wing objectives true after a short amount
2652         // of time when there are more waves for this wing.
2653         if ( (count == 1) && (wing_index >= 0) && (Directive_count == 0) ) {
2654                 if ( Wings[wing_index].current_wave < Wings[wing_index].num_waves )
2655                         Directive_count =       DIRECTIVE_WING_ZERO;
2656         }
2657
2658         if ( count == num_destroyed )
2659                 return SEXP_KNOWN_TRUE;
2660         else
2661                 return 0;
2662 }
2663
2664
2665 // return true if the subsystem of the given ship has been destroyed or not
2666 int sexp_is_subsystem_destroyed(int n)
2667 {
2668         char *ship_name, *subsys_name;
2669
2670         SDL_assert( n != -1 );
2671         
2672         ship_name = CTEXT(n);
2673         subsys_name = CTEXT(CDR(n));
2674
2675         if (sexp_query_has_yet_to_arrive(ship_name))
2676                 return SEXP_CANT_EVAL;
2677
2678         // if the ship has departed, no way to destroy it's subsystem.
2679         if ( mission_log_get_time(LOG_SHIP_DEPART, ship_name, NULL, NULL ))
2680                 return SEXP_KNOWN_FALSE;
2681         
2682         if ( mission_log_get_time(LOG_SHIP_SUBSYS_DESTROYED, ship_name, subsys_name, NULL) )
2683                 return SEXP_KNOWN_TRUE;
2684
2685         return 0;
2686
2687 }
2688
2689 // determines if a ship has docked
2690 int sexp_has_docked(int n)
2691 {
2692         char *docker = CTEXT(n);
2693         char *dockee = CTEXT(CDR(n));
2694         int count = atoi(CTEXT(CDR(CDR(n))));           // count of times that we should look for
2695
2696         if (sexp_query_has_yet_to_arrive(docker))
2697                 return SEXP_CANT_EVAL;
2698
2699         if (sexp_query_has_yet_to_arrive(dockee))
2700                 return SEXP_CANT_EVAL;
2701
2702         SDL_assert ( count > 0 );
2703         if ( mission_log_get_time(LOG_SHIP_DESTROYED, docker, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, dockee, NULL, NULL) )
2704                 return SEXP_KNOWN_FALSE;
2705
2706         if ( !mission_log_get_time_indexed(LOG_SHIP_DOCK, docker, dockee, count, NULL) )
2707                 return 0;
2708
2709         return SEXP_KNOWN_TRUE;
2710 }
2711
2712 // determines if a ship has undocked
2713 int sexp_has_undocked(int n)
2714 {
2715         char *docker = CTEXT(n);
2716         char *dockee = CTEXT(CDR(n));
2717         int count = atoi(CTEXT(CDR(CDR(n))));
2718
2719         if (sexp_query_has_yet_to_arrive(docker))
2720                 return SEXP_CANT_EVAL;
2721
2722         if (sexp_query_has_yet_to_arrive(dockee))
2723                 return SEXP_CANT_EVAL;
2724
2725         SDL_assert ( count > 0 );
2726         if ( !mission_log_get_time_indexed(LOG_SHIP_UNDOCK, docker, dockee, count, NULL) ) {
2727                 // if either ship destroyed before they dock, then sexp is known false
2728                 if ( mission_log_get_time(LOG_SHIP_DESTROYED, docker, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, dockee, NULL, NULL) )
2729                         return SEXP_KNOWN_FALSE;
2730                 else
2731                         return 0;
2732         }
2733
2734         return SEXP_KNOWN_TRUE;
2735 }
2736
2737 // determines if a ship has arrived onto the scene
2738 int sexp_has_arrived(int n, fix *latest_time)
2739 {
2740         char *name;
2741         int     count, num_arrived;
2742         fix     time;
2743
2744         count = 0;
2745         num_arrived = 0;
2746         while ( n != -1 ) {
2747                 count++;
2748                 name = CTEXT(n);
2749                 // if there is no log entry for this ship/wing for arrival, sexpression is false
2750                 if ( mission_log_get_time(LOG_SHIP_ARRIVE, name, NULL, &time) || mission_log_get_time(LOG_WING_ARRIVE, name, NULL, &time) ) {
2751                         num_arrived++;
2752                         if ( latest_time && (time > *latest_time) )
2753                                 *latest_time = time;
2754                 }
2755                 n = CDR(n);
2756         }
2757
2758         if ( count == num_arrived )
2759                 return SEXP_KNOWN_TRUE;
2760         else
2761                 return 0;
2762 }
2763
2764 // determines if a ship/wing has departed
2765 int sexp_has_departed(int n, fix *latest_time)
2766 {
2767         char *name;
2768         int count, num_departed;
2769         fix time;
2770
2771         count = 0;
2772         num_departed = 0;
2773         while ( n != -1 ) {
2774                 count++;
2775                 name = CTEXT(n);
2776
2777                 if (sexp_query_has_yet_to_arrive(name))
2778                         return SEXP_CANT_EVAL;
2779
2780                 // if ship/wing destroyed, sexpression is known false.  Also, if there is no departure log entry, then
2781                 // the sexpression is not true.
2782                 if ( mission_log_get_time(LOG_SHIP_DESTROYED, name, NULL, NULL) || mission_log_get_time(LOG_WING_DESTROYED, name, NULL, NULL))
2783                         return SEXP_KNOWN_FALSE;
2784                 else if ( mission_log_get_time(LOG_SHIP_DEPART, name, NULL, &time) || mission_log_get_time(LOG_WING_DEPART, name, NULL, &time) ) {
2785                         num_departed++;
2786                         if ( latest_time && (time > *latest_time) )
2787                                 *latest_time = time;
2788                 }
2789                 n = CDR(n);
2790         }
2791
2792         if ( count == num_departed )
2793                 return SEXP_KNOWN_TRUE;
2794         else
2795                 return 0;
2796 }
2797
2798 // determines if ships are disabled
2799 int sexp_is_disabled( int n, fix *latest_time )
2800 {
2801         char *name;
2802         int count, num_disabled;
2803         fix time;
2804
2805         count = 0;
2806         num_disabled = 0;
2807         while ( n != -1 ) {
2808                 count++;
2809                 name = CTEXT(n);
2810
2811                 if (sexp_query_has_yet_to_arrive(name))
2812                         return SEXP_CANT_EVAL;
2813
2814                 // if ship/wing destroyed, sexpression is known false.  Also, if there is no disable log entry, then
2815                 // the sexpression is not true.
2816                 if ( mission_log_get_time(LOG_SHIP_DEPART, name, NULL, &time) || mission_log_get_time(LOG_SHIP_DESTROYED, name, NULL, &time) )
2817                         return SEXP_KNOWN_FALSE;
2818                 else if ( mission_log_get_time(LOG_SHIP_DISABLED, name, NULL, &time) ) {
2819                         num_disabled++;
2820                         if ( latest_time && (time > *latest_time) )
2821                                 *latest_time = time;
2822                 }
2823                 n = CDR(n);
2824         }
2825
2826         if ( count == num_disabled )
2827                 return SEXP_KNOWN_TRUE;
2828         else
2829                 return 0;
2830 }
2831
2832 // function to determine if a ship is done flying waypoints
2833 int sexp_are_waypoints_done( int n )
2834 {
2835         char *ship_name, *waypoint_name;
2836
2837         ship_name = CTEXT(n);
2838         waypoint_name = CTEXT(CDR(n));
2839
2840         if (sexp_query_has_yet_to_arrive(ship_name))
2841                 return SEXP_CANT_EVAL;
2842
2843         // a destroyed or departed ship will never reach their goal -- return known false
2844         if ( mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) )
2845                 return SEXP_KNOWN_FALSE;
2846         else if ( mission_log_get_time(LOG_SHIP_DEPART, ship_name, NULL, NULL) )
2847                 return SEXP_KNOWN_FALSE;
2848
2849         // now check the log for the waypoints done entry
2850         if ( mission_log_get_time(LOG_WAYPOINTS_DONE, ship_name, waypoint_name, NULL) )
2851                 return SEXP_KNOWN_TRUE;
2852
2853         return 0;
2854 }
2855
2856
2857 // determines if ships are disarmed
2858 int sexp_is_disarmed( int n, fix *latest_time )
2859 {
2860         char *name;
2861         int count, num_disarmed;
2862         fix time;
2863
2864         count = 0;
2865         num_disarmed = 0;
2866         while ( n != -1 ) {
2867                 count++;
2868                 name = CTEXT(n);
2869
2870                 if (sexp_query_has_yet_to_arrive(name))
2871                         return SEXP_CANT_EVAL;
2872
2873                 // if ship/wing destroyed, sexpression is known false.  Also, if there is no disarm log entry, then
2874                 // the sexpression is not true.
2875                 if ( mission_log_get_time(LOG_SHIP_DEPART, name, NULL, &time) || mission_log_get_time(LOG_SHIP_DESTROYED, name, NULL, &time) )
2876                         return SEXP_KNOWN_FALSE;
2877                 else if ( mission_log_get_time(LOG_SHIP_DISARMED, name, NULL, &time) ) {
2878                         num_disarmed++;
2879                         if ( latest_time && (time > *latest_time) )
2880                                 *latest_time = time;
2881                 }
2882                 n = CDR(n);
2883         }
2884
2885         if ( count == num_disarmed )
2886                 return SEXP_KNOWN_TRUE;
2887         else
2888                 return 0;
2889 }
2890
2891 // the following functions are similar to the above objective functions but return true/false
2892 // if N seconds have elasped after the corresponding function is true.
2893 int sexp_is_destroyed_delay(int n)
2894 {
2895         fix delay, time;
2896         int val;
2897
2898         SDL_assert ( n >= 0 );
2899
2900         time = 0;
2901
2902         delay = i2f(num_eval(n));
2903
2904         // check value of is_destroyed function.  KNOWN_FALSE should be returned immediately
2905         val = sexp_is_destroyed( CDR(n), &time );
2906         if ( val == SEXP_KNOWN_FALSE )
2907                 return val;
2908
2909         if ( val == SEXP_CANT_EVAL )
2910                 return SEXP_CANT_EVAL;
2911
2912         if ( val ) {
2913
2914                 if ( (Missiontime - time) >= delay )
2915                         return SEXP_KNOWN_TRUE;
2916         }
2917
2918         return 0;
2919 }
2920
2921 int sexp_is_subsystem_destroyed_delay( int n )
2922 {
2923         char *ship_name, *subsys_name;
2924         fix delay, time;
2925
2926         SDL_assert( n != -1 );
2927         
2928         ship_name = CTEXT(n);
2929         subsys_name = CTEXT(CDR(n));
2930         delay = i2f(atoi(CTEXT(CDR(CDR(n)))));
2931
2932         if (sexp_query_has_yet_to_arrive(ship_name))
2933                 return SEXP_CANT_EVAL;
2934
2935         // if the ship has departed, no way to destroy it's subsystem.
2936         if ( mission_log_get_time(LOG_SHIP_DEPART, ship_name, NULL, NULL ))
2937                 return SEXP_KNOWN_FALSE;
2938
2939         if ( mission_log_get_time(LOG_SHIP_SUBSYS_DESTROYED, ship_name, subsys_name, &time) ) {
2940                 if ( (Missiontime - time) >= delay )
2941                         return SEXP_KNOWN_TRUE;
2942         }
2943
2944         return 0;
2945 }
2946
2947 int sexp_is_disabled_delay( int n )
2948 {
2949         fix delay, time;
2950         int val;
2951
2952         SDL_assert ( n >= 0 );
2953
2954         time = 0;
2955         delay = i2f(atoi(CTEXT(n)));
2956
2957         // check value of is_disable for known false and return immediately if it is.
2958         val = sexp_is_disabled( CDR(n), &time );
2959         if ( val == SEXP_KNOWN_FALSE )
2960                 return val;
2961
2962         if ( val == SEXP_CANT_EVAL )
2963                 return SEXP_CANT_EVAL;
2964
2965         if ( val ) {
2966                 if ( (Missiontime - time) >= delay )
2967                         return SEXP_KNOWN_TRUE;
2968         }
2969
2970         return 0;
2971 }
2972
2973 int sexp_is_disarmed_delay( int n )
2974 {
2975         fix delay, time;
2976         int val;
2977
2978         SDL_assert ( n >= 0 );
2979
2980         time = 0;
2981         delay = i2f(atoi(CTEXT(n)));
2982         
2983         // check value of is_disarmed for a known false value and return that immediately if it is
2984         val = sexp_is_disarmed( CDR(n), &time );
2985         if ( val == SEXP_KNOWN_FALSE )
2986                 return val;
2987
2988         if ( val == SEXP_CANT_EVAL )
2989                 return SEXP_CANT_EVAL;
2990
2991         if ( val ) {
2992                 if ( (Missiontime - time) >= delay )
2993                         return SEXP_KNOWN_TRUE;
2994         }
2995
2996         return 0;
2997 }
2998
2999 int sexp_has_docked_delay( int n )
3000 {
3001         char *docker = CTEXT(n);
3002         char *dockee = CTEXT(CDR(n));
3003         int count = atoi(CTEXT(CDR(CDR(n))));           // count of times that we should look for
3004         fix delay = i2f(atoi(CTEXT(CDR(CDR(CDR(n))))));
3005         fix time;
3006
3007         SDL_assert ( count > 0 );
3008         if ( mission_log_get_time(LOG_SHIP_DESTROYED, docker, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, dockee, NULL, NULL) )
3009                 return SEXP_KNOWN_FALSE;
3010
3011         if (sexp_query_has_yet_to_arrive(docker))
3012                 return SEXP_CANT_EVAL;
3013
3014         if (sexp_query_has_yet_to_arrive(dockee))
3015                 return SEXP_CANT_EVAL;
3016
3017         if ( !mission_log_get_time_indexed(LOG_SHIP_DOCK, docker, dockee, count, &time) )
3018                 return 0;
3019
3020         if ( (Missiontime - time) >= delay )
3021                 return SEXP_KNOWN_TRUE;
3022         else
3023                 return 0;
3024 }
3025
3026 int sexp_has_undocked_delay( int n )
3027 {
3028         char *docker = CTEXT(n);
3029         char *dockee = CTEXT(CDR(n));
3030         int count = atoi(CTEXT(CDR(CDR(n))));
3031         fix delay = i2f(atoi(CTEXT(CDR(CDR(CDR(n))))));
3032         fix time;
3033
3034         if (sexp_query_has_yet_to_arrive(docker))
3035                 return SEXP_CANT_EVAL;
3036
3037         if (sexp_query_has_yet_to_arrive(dockee))
3038                 return SEXP_CANT_EVAL;
3039
3040         SDL_assert ( count > 0 );
3041         if ( !mission_log_get_time_indexed(LOG_SHIP_UNDOCK, docker, dockee, count, &time) ) {
3042                 // if either ship destroyed before they dock, then sexp is known false
3043                 if ( mission_log_get_time(LOG_SHIP_DESTROYED, docker, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, dockee, NULL, NULL) )
3044                         return SEXP_KNOWN_FALSE;
3045                 else
3046                         return 0;
3047         }
3048
3049         if ( (Missiontime - time) >= delay )
3050                 return SEXP_KNOWN_TRUE;
3051         else
3052                 return 0;
3053 }
3054
3055 int sexp_has_arrived_delay( int n )
3056 {
3057         fix delay, time;
3058         int val;
3059
3060         SDL_assert ( n >= 0 );
3061
3062         time = 0;
3063         delay = i2f(atoi(CTEXT(n)));
3064
3065         // check return value from arrived function.  if can never arrive, then return that value here as well
3066         val = sexp_has_arrived( CDR(n), &time );
3067         if ( val == SEXP_KNOWN_FALSE )
3068                 return val;
3069
3070         if ( val == SEXP_CANT_EVAL )
3071                 return SEXP_CANT_EVAL;
3072
3073         if ( val ) {
3074                 if ( (Missiontime - time) >= delay )
3075                         return SEXP_KNOWN_TRUE;
3076         }
3077
3078         return 0;
3079 }
3080
3081 int sexp_has_departed_delay( int n )
3082 {
3083         fix delay, time;
3084         int val;
3085
3086         SDL_assert ( n >= 0 );
3087
3088         time = 0;
3089         delay = i2f(atoi(CTEXT(n)));
3090
3091         // must first check to see if the departed function could ever be true/false or is true or false.
3092         // if it can never be true, return that value
3093         val = sexp_has_departed( CDR(n), &time);
3094         if ( val == SEXP_KNOWN_FALSE )
3095                 return val;
3096
3097         if ( val == SEXP_CANT_EVAL )
3098                 return SEXP_CANT_EVAL;
3099
3100         if ( val ) {
3101                 if ( (Missiontime - time) >= delay )
3102                         return SEXP_KNOWN_TRUE;
3103         }
3104
3105         return 0;
3106 }
3107
3108 // function to determine if a ship is done flying waypoints after N seconds
3109 int sexp_are_waypoints_done_delay( int n )
3110 {
3111         char *ship_name, *waypoint_name;
3112         fix time, delay;
3113
3114         ship_name = CTEXT(n);
3115         waypoint_name = CTEXT(CDR(n));
3116         delay = i2f(atoi(CTEXT(CDR(CDR(n)))));
3117
3118         if (sexp_query_has_yet_to_arrive(ship_name))
3119                 return SEXP_CANT_EVAL;
3120
3121         // a destroyed or departed ship will never reach their goal -- return known false
3122         // 
3123         // Not checking the entries below.  Ships which warp out after reaching their goal (or getting
3124         // destroyed after their goal), but after reaching their waypoints, may have this goal incorrectly
3125         // marked false!!!!
3126
3127         // now check the log for the waypoints done entry
3128         if ( mission_log_get_time(LOG_WAYPOINTS_DONE, ship_name, waypoint_name, &time) ) {
3129                 if ( (Missiontime - time) >= delay )
3130                         return SEXP_KNOWN_TRUE;
3131         } else {
3132                 if ( mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) )
3133                         return SEXP_KNOWN_FALSE;
3134                 else if ( mission_log_get_time(LOG_SHIP_DEPART, ship_name, NULL, NULL) )
3135                         return SEXP_KNOWN_FALSE;
3136         }
3137
3138         return 0;
3139 }
3140
3141 // function to determine is all of a given ship type are destroyed
3142 int sexp_ship_type_destroyed( int n )
3143 {
3144         int percent, type;
3145         char *shiptype;
3146
3147         percent = atoi(CTEXT(n));
3148         shiptype = CTEXT(CDR(n));
3149
3150         for ( type = 0; type < MAX_SHIP_TYPE_COUNTS; type++ ) {
3151                 if ( !SDL_strcasecmp( Ship_type_names[type], shiptype) )
3152                         break;
3153         }
3154
3155         // bogus if we reach the end of this array!!!!
3156         if ( type == MAX_SHIP_TYPE_COUNTS ) {
3157                 Int3();
3158                 return 0;
3159         }
3160
3161         if ( Ship_counts[type].total == 0 )
3162                 return 0;
3163
3164         // determine if the percentage of killed/total is >= percentage given in the expression
3165         if ( (Ship_counts[type].killed * 100 / Ship_counts[type].total) >= percent)
3166                 return SEXP_KNOWN_TRUE;
3167         
3168         return 0;
3169 }
3170
3171
3172 // following are time based functions
3173 int sexp_has_time_elapsed(int n)
3174 {
3175         int time = num_eval(n);
3176
3177         if ( f2i(Missiontime) >= time )
3178                 return SEXP_KNOWN_TRUE;
3179
3180         return 0;
3181 }
3182
3183 // next function returns the time into the mission
3184 int sexp_mission_time()
3185 {
3186         return f2i(Missiontime);
3187 }
3188
3189 // returns percent of length of distance to special warpout plane
3190 int sexp_special_warp_dist( int n)
3191 {
3192         char *ship_name;
3193         int shipnum;
3194
3195         // get shipname
3196         ship_name = CTEXT(n);
3197
3198         // check to see if either ship was destroyed or departed.  If so, then make this node known
3199         // false
3200         if ( mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) || mission_log_get_time( LOG_SHIP_DEPART, ship_name, NULL, NULL) ) {
3201                 return SEXP_NAN_FOREVER;
3202         }
3203
3204         // get ship name
3205         shipnum = ship_name_lookup(ship_name);
3206         if (shipnum < 0) {
3207                 return SEXP_NAN;
3208         }
3209
3210         // check that ship has warpout_objnum
3211         if (Ships[shipnum].special_warp_objnum == -1) {
3212                 return SEXP_NAN;
3213         }
3214         
3215         SDL_assert( (Ships[shipnum].special_warp_objnum >= 0) && (Ships[shipnum].special_warp_objnum < MAX_OBJECTS));
3216         if ( (Ships[shipnum].special_warp_objnum < 0) && (Ships[shipnum].special_warp_objnum >= MAX_OBJECTS) ) {
3217                 return SEXP_NAN;
3218         }
3219
3220         // check the special warpout device is valid
3221         int valid = FALSE;
3222         object *ship_objp = &Objects[Ships[shipnum].objnum];
3223         object *warp_objp = &Objects[Ships[shipnum].special_warp_objnum];
3224         if (warp_objp->type == OBJ_SHIP) {
3225                 if (Ship_info[Ships[warp_objp->instance].ship_info_index].flags & SIF_KNOSSOS_DEVICE) {
3226                         valid = TRUE;
3227                 }
3228         }
3229
3230         if (!valid) {
3231                 return SEXP_NAN;
3232         }
3233
3234         // check if within 45 degree half-angle cone of facing 
3235         float dot = fl_abs(vm_vec_dotprod(&warp_objp->orient.v.fvec, &ship_objp->orient.v.fvec));
3236         if (dot < 0.707f) {
3237                 return SEXP_NAN;
3238         }
3239
3240         // get distance
3241         vector hit_pt;
3242         float dist = fvi_ray_plane(&hit_pt, &warp_objp->pos, &warp_objp->orient.v.fvec, &ship_objp->pos, &ship_objp->orient.v.fvec, 0.0f);
3243         polymodel *pm = model_get(Ships[shipnum].modelnum);
3244         dist += pm->mins.xyz.z;
3245
3246         // return as a percent of length
3247         return (int) (100.0f * dist / ship_get_length(&Ships[shipnum]));
3248 }
3249
3250
3251 int sexp_time_destroyed(int n)
3252 {
3253         fix time;
3254
3255         if ( !mission_log_get_time( LOG_SHIP_DESTROYED, CTEXT(n), NULL, &time) ){               // returns 0 when not found
3256                 return SEXP_NAN;
3257         }
3258         
3259         return f2i(time);
3260 }
3261
3262 int sexp_time_wing_destroyed(int n)
3263 {
3264         fix time;
3265
3266         if ( !mission_log_get_time( LOG_WING_DESTROYED, CTEXT(n), NULL, &time) ){
3267                 return SEXP_NAN;
3268         }
3269         
3270         return f2i(time);
3271 }
3272
3273 int sexp_time_docked(int n)
3274 {
3275         fix time;
3276         char *docker = CTEXT(n);
3277         char *dockee = CTEXT(CDR(n));
3278         int count = atoi(CTEXT(CDR(CDR(n))));
3279
3280         SDL_assert ( count > 0 );
3281         if ( !mission_log_get_time_indexed(LOG_SHIP_DOCK, docker, dockee, count, &time) ){
3282                 return SEXP_NAN;
3283         }
3284
3285         return f2i(time);
3286 }
3287
3288 int sexp_time_undocked(int n)
3289 {
3290         fix time;
3291         char *docker = CTEXT(n);
3292         char *dockee = CTEXT(CDR(n));
3293         int count = atoi(CTEXT(CDR(CDR(n))));
3294
3295         SDL_assert ( count > 0 );
3296         if ( !mission_log_get_time_indexed(LOG_SHIP_UNDOCK, docker, dockee, count, &time) ){
3297                 return SEXP_NAN;
3298         }
3299
3300         return f2i(time);
3301 }
3302
3303 int sexp_time_ship_arrived(int n)
3304 {
3305         fix time;
3306
3307         SDL_assert( n != -1 );
3308         if ( !mission_log_get_time( LOG_SHIP_ARRIVE, CTEXT(n), NULL, &time ) ){
3309                 return SEXP_NAN;
3310         }
3311
3312         return f2i(time);
3313 }
3314
3315 int sexp_time_wing_arrived(int n)
3316 {
3317         fix time;
3318
3319         SDL_assert( n != -1 );
3320         if ( !mission_log_get_time( LOG_WING_ARRIVE, CTEXT(n), NULL, &time ) ){
3321                 return SEXP_NAN;
3322         }
3323
3324         return f2i(time);
3325 }
3326
3327 int sexp_time_ship_departed(int n)
3328 {
3329         fix time;
3330
3331         SDL_assert( n != -1 );
3332         if ( !mission_log_get_time( LOG_SHIP_DEPART, CTEXT(n), NULL, &time ) ){
3333                 return SEXP_NAN;
3334         }
3335
3336         return f2i(time);
3337 }
3338
3339 int sexp_time_wing_departed(int n)
3340 {
3341         fix time;
3342
3343         SDL_assert( n != -1 );
3344         if ( !mission_log_get_time( LOG_WING_DEPART, CTEXT(n), NULL, &time ) ){
3345                 return SEXP_NAN;
3346         }
3347
3348         return f2i(time);
3349 }
3350
3351 // function to return the remaining shields as a percentage of the given ship.
3352 int sexp_shields_left(int n)
3353 {
3354         int shipnum, percent;
3355         char *shipname;
3356
3357         shipname = CTEXT(n);
3358         
3359         // if ship is gone or departed, cannot ever evaluate properly.  Return NAN_FOREVER
3360         if ( mission_log_get_time(LOG_SHIP_DESTROYED, shipname, NULL, NULL) || mission_log_get_time( LOG_SHIP_DEPART, shipname, NULL, NULL) ){
3361                 return SEXP_NAN_FOREVER;
3362         }
3363
3364         shipnum = ship_name_lookup( shipname );
3365         if ( shipnum == -1 ){                                   // hmm.. if true, must not have arrived yet
3366                 return SEXP_NAN;
3367         }
3368
3369         // now return the amount of shields left as a percentage of the whole.
3370         percent = (int)(get_shield_strength(&Objects[Ships[shipnum].objnum]) / Ship_info[Ships[shipnum].ship_info_index].shields * 100.0f);
3371         return percent;
3372 }
3373
3374 // function to return the remaining hits left as a percentage of the whole.  This hit amount counts for
3375 // all hits on the ship (hull + subsystems).  Use hits_left_hull to find hull hits remaining.
3376 int sexp_hits_left(int n)
3377 {
3378         int shipnum, percent;
3379         char *shipname;
3380
3381         shipname = CTEXT(n);
3382         
3383         // if ship is gone or departed, cannot ever evaluate properly.  Return NAN_FOREVER
3384         if ( mission_log_get_time(LOG_SHIP_DESTROYED, shipname, NULL, NULL) || mission_log_get_time( LOG_SHIP_DEPART, shipname, NULL, NULL) ){
3385                 return SEXP_NAN_FOREVER;
3386         }
3387
3388         shipnum = ship_name_lookup( shipname );
3389         if ( shipnum == -1 ){                                   // hmm.. if true, must not have arrived yet
3390                 return SEXP_NAN;
3391         }
3392
3393         // now return the amount of hits left as a percentage of the whole.  Subtract the percentage from 100
3394         // since we are working with total hit points taken, not total remaining.
3395         ship            *shipp = &Ships[shipnum];
3396         ship_info *sip  = &Ship_info[shipp->ship_info_index];
3397         object  *objp = &Objects[shipp->objnum];
3398         percent = (int) (100.0f * objp->hull_strength / sip->initial_hull_strength);
3399         return percent;
3400 }
3401
3402 // is ship visible on radar
3403 // returns 0 - not visible
3404 // returns 1 - marginally targetable (jiggly on radar)
3405 // returns 2 - fully targetable
3406 int sexp_is_ship_visible(int n)
3407 {
3408         char *shipname;
3409         int shipnum;
3410         int ship_is_visible = 0;
3411
3412         // if multiplayer, bail
3413         if (Game_mode & GM_MULTIPLAYER) {
3414                 return SEXP_NAN_FOREVER;
3415         }
3416
3417         shipname = CTEXT(n);
3418         
3419         // if ship is gone or departed, cannot ever evaluate properly.  Return NAN_FOREVER
3420         if ( mission_log_get_time(LOG_SHIP_DESTROYED, shipname, NULL, NULL) || mission_log_get_time( LOG_SHIP_DEPART, shipname, NULL, NULL) ){
3421                 return SEXP_NAN_FOREVER;
3422         }
3423
3424         shipnum = ship_name_lookup( shipname );
3425         if ( shipnum == -1 ){                                   // hmm.. if true, must not have arrived yet
3426                 return SEXP_NAN;
3427         }
3428
3429         // get ship's *radar* visiblity
3430         if (Player_ship != NULL) {
3431                 if (ship_is_visible_by_team(shipnum, Player_ship->team)) {
3432                         ship_is_visible = 2;
3433                 }
3434         }
3435
3436         // only check awacs level if ship is not visible by team
3437         if (Player_ship != NULL && !ship_is_visible) {
3438                 float awacs_level = awacs_get_level(&Objects[Ships[shipnum].objnum], Player_ship);
3439                 if (awacs_level >= 1.0f) {
3440                         ship_is_visible = 2;
3441                 } else if (awacs_level > 0) {
3442                         ship_is_visible = 1;
3443                 }
3444         }
3445
3446         return ship_is_visible;
3447 }
3448
3449 // get multi team v team score
3450 // if not multi team v team return 0
3451 // if invalid team return 0
3452 int sexp_team_score(int node)
3453 {
3454         // if multi t vs t
3455         if (Game_mode & GM_MULTIPLAYER) {
3456                 if (Netgame.type_flags & NG_TYPE_TEAM) {
3457
3458                         int team = atoi(CTEXT(node));
3459
3460                         if (team == 1) {
3461                                 return Multi_team0_score;
3462                         } else if (team == 2) {
3463                                 return Multi_team1_score;
3464                         } else {
3465                                 // invalid team index
3466                                 Int3();
3467                                 return 0;
3468                         }
3469                 }
3470         }
3471
3472         return 0;
3473 }
3474
3475
3476 // function to return the remaining hits left on a subsystem as a percentage of thw whole.
3477 int sexp_hits_left_subsystem(int n)
3478 {
3479         int shipnum, percent, type;
3480         char *shipname;
3481         char *subsys_name;
3482
3483         shipname = CTEXT(n);
3484         
3485         // if ship is gone or departed, cannot ever evaluate properly.  Return NAN_FOREVER
3486         if ( mission_log_get_time(LOG_SHIP_DESTROYED, shipname, NULL, NULL) || mission_log_get_time( LOG_SHIP_DEPART, shipname, NULL, NULL) ){
3487                 return SEXP_NAN_FOREVER;
3488         }
3489
3490         shipnum = ship_name_lookup( shipname );
3491         if ( shipnum == -1 ){                                   // hmm.. if true, must not have arrived yet
3492                 return SEXP_NAN;
3493         }
3494
3495         subsys_name = CTEXT(CDR(n));
3496         type = ai_get_subsystem_type( subsys_name );
3497         if ( (type >= 0) && (type < SUBSYSTEM_MAX) ) {
3498                 // return as a percentage the hits remaining on the subsystem as a whole (i.e. for 3 engines,
3499                 // we are returning the sum of the hits on the 3 engines)
3500                 if (type == SUBSYSTEM_UNKNOWN) {
3501                         // find the ship subsystem by searching ship's subsys_list
3502                         ship_subsys *ss;
3503                         ss = GET_FIRST( &Ships[shipnum].subsys_list );
3504                         while ( ss != END_OF_LIST( &Ships[shipnum].subsys_list ) ) {
3505
3506                                 if ( !SDL_strcasecmp(ss->system_info->subobj_name, subsys_name)) {
3507                                         percent = (int) (ss->current_hits / ss->system_info->max_hits * 100.0f);
3508                                         return percent;
3509                                 }
3510
3511                                 ss = GET_NEXT( ss );
3512                         }
3513                         // we reached end of ship subsys list without finding subsys_name
3514                         Int3();
3515
3516                 } else {
3517                         percent = (int)(ship_get_subsystem_strength(&Ships[shipnum],type) * 100.0f);
3518                         return percent;
3519                 }
3520         }
3521         return SEXP_NAN;                        // if for some strange reason, the type field of the subsystem is bogus
3522 }
3523
3524 int sexp_determine_team(char *subj)
3525 {
3526         int team = 0;
3527
3528         if (!SDL_strcasecmp(subj, "<any friendly>")){
3529                 team = TEAM_FRIENDLY;
3530         } else if (!SDL_strcasecmp(subj, "<any hostile>")){
3531                 team = TEAM_HOSTILE;
3532         } else if (!SDL_strcasecmp(subj, "<any neutral>")){
3533                 team = TEAM_NEUTRAL;
3534         } else if (!SDL_strcasecmp(subj, "<any unknown>")){
3535                 team = TEAM_UNKNOWN;
3536         } else if (!SDL_strcasecmp(subj, "<any traitor>")){
3537                 team = TEAM_TRAITOR;
3538         }
3539
3540         return team;
3541 }
3542
3543 // returns the distance between two objects.  If a wing is specificed as one (or both) or the arguments
3544 // to this function, we are looking for the closest distance
3545 int sexp_distance(int n)
3546 {
3547         int i, team, obj, dist, dist_min = 0, inited = 0;
3548         char *sname1, *sname2;
3549         wing *wingp;
3550         ship_obj *so;
3551
3552         sname1 = CTEXT(n);
3553         sname2 = CTEXT(CDR(n));
3554
3555         // check to see if either ship was destroyed or departed.  If so, then make this node known
3556         // false
3557         if ( mission_log_get_time(LOG_SHIP_DESTROYED, sname1, NULL, NULL) || mission_log_get_time( LOG_SHIP_DEPART, sname1, NULL, NULL) ||
3558                   mission_log_get_time(LOG_SHIP_DESTROYED, sname2, NULL, NULL) || mission_log_get_time( LOG_SHIP_DEPART, sname2, NULL, NULL) ) 
3559                 return SEXP_NAN_FOREVER;
3560
3561         // one of the names might be the name of a wing.  Check to see if the wing is detroyed or departed
3562         if ( mission_log_get_time(LOG_WING_DESTROYED, sname1, NULL, NULL) || mission_log_get_time( LOG_WING_DEPART, sname1, NULL, NULL) ||
3563                   mission_log_get_time(LOG_WING_DESTROYED, sname2, NULL, NULL) || mission_log_get_time( LOG_WING_DEPART, sname2, NULL, NULL) ) 
3564                 return SEXP_NAN_FOREVER;
3565
3566         team = sexp_determine_team(sname1);
3567         if (team) {  // we have a team type, so check all ships of that type
3568                 so = GET_FIRST(&Ship_obj_list);
3569                 while (so != END_OF_LIST(&Ship_obj_list)) {
3570                         if (Ships[Objects[so->objnum].instance].team == team) {
3571                                 obj = so->objnum;
3572                                 dist = sexp_distance2(obj, sname2);
3573                                 if (dist != SEXP_NAN) {
3574                                         if (!inited || (dist < dist_min)) {
3575                                                 dist_min = dist;
3576                                                 inited = 1;
3577                                         }
3578                                 }
3579                         }
3580
3581                         so = GET_NEXT(so);
3582                 }
3583
3584                 if (!inited)  // no objects were checked
3585                         return SEXP_NAN;
3586
3587                 return dist_min;
3588         }
3589
3590         // at this point, we must have a wing, ship or point for a subj
3591         obj = ship_name_lookup(sname1);
3592         if (obj >= 0)
3593                 return sexp_distance2(Ships[obj].objnum, sname2);
3594
3595         // at this point, we must have a wing or point for a subj
3596         obj = waypoint_lookup(sname1);
3597         if (obj >= 0)
3598                 return sexp_distance2(obj, sname2);
3599                 
3600         // at this point, we must have a wing for a subj
3601         obj = wing_name_lookup(sname1);
3602         if (obj < 0)
3603                 return SEXP_NAN;  // we apparently don't have anything legal
3604
3605         wingp = &Wings[obj];
3606         for (i=0; i<wingp->current_count; i++) {
3607                 obj = Ships[wingp->ship_index[i]].objnum;
3608                 dist = sexp_distance2(obj, sname2);
3609                 if (dist != SEXP_NAN) {
3610                         if (!inited || (dist < dist_min)) {
3611                                 dist_min = dist;
3612                                 inited = 1;
3613                         }
3614                 }
3615         }
3616
3617         if (!inited)  // no objects were checked
3618                 return SEXP_NAN;
3619
3620         return dist_min;
3621 }
3622
3623 // check distance between a given ship and a given subject (ship, wing, any <team>).
3624 int sexp_distance2(int obj1, char *subj)
3625 {
3626         int i, team, obj2, dist, dist_min = 0, inited = 0;
3627         wing *wingp;
3628         ship_obj        *so;
3629         
3630         team = sexp_determine_team(subj);
3631         if (team) {  // we have a team type, so check all ships of that type
3632                 so = GET_FIRST(&Ship_obj_list);
3633                 while (so != END_OF_LIST(&Ship_obj_list)) {
3634                         if (Ships[Objects[so->objnum].instance].team == team) {
3635                                 obj2 = so->objnum;
3636                                 dist = sexp_distance3(obj1, obj2);
3637                                 if (dist != SEXP_NAN) {
3638                                         if (!inited || (dist < dist_min)) {
3639                                                 dist_min = dist;
3640                                                 inited = 1;
3641                                         }
3642                                 }
3643                         }
3644
3645                         so = GET_NEXT(so);
3646                 }
3647
3648                 if (!inited)  // no objects were checked
3649                         return SEXP_NAN;
3650
3651                 return dist_min;
3652         }
3653
3654         // at this point, we must have a wing, ship or point for a subj
3655         obj2 = ship_name_lookup(subj);
3656         if (obj2 >= 0)
3657                 return sexp_distance3(obj1, Ships[obj2].objnum);
3658
3659         // at this point, we must have a wing or point for a subj
3660         obj2 = waypoint_lookup(subj);
3661         if (obj2 >= 0)
3662                 return sexp_distance3(obj1, obj2);
3663                 
3664         // at this point, we must have a wing for a subj
3665         obj2 = wing_name_lookup(subj);
3666         if (obj2 < 0)
3667                 return SEXP_NAN;  // we apparently don't have anything legal
3668
3669         wingp = &Wings[obj2];
3670         for (i=0; i<wingp->current_count; i++) {
3671                 obj2 = Ships[wingp->ship_index[i]].objnum;
3672                 dist = sexp_distance3(obj1, obj2);
3673                 if (dist != SEXP_NAN) {
3674                         if (!inited || (dist < dist_min)) {
3675                                 dist_min = dist;
3676                                 inited = 1;
3677                         }
3678                 }
3679         }
3680
3681         if (!inited)  // no objects were checked
3682                 return SEXP_NAN;
3683
3684         return dist_min;
3685 }
3686
3687 // check distance between two given objects
3688 int sexp_distance3(int obj1, int obj2)
3689 {
3690         if ( (obj1 == -1) || (obj2 == -1) )                             // if either object isn't present in the mission now
3691                 return SEXP_NAN;                                                                        // return a really small number
3692
3693         if ( (Objects[obj1].type == OBJ_SHIP) && (Objects[obj2].type == OBJ_SHIP) ) {
3694                 if (OBJ_INDEX(Player_obj) == obj1)
3695                         return (int) hud_find_target_distance( &Objects[obj2], &Objects[obj1] );
3696                 else
3697                         return (int) hud_find_target_distance( &Objects[obj1], &Objects[obj2] );
3698
3699         } else {
3700                 return (int) vm_vec_dist_quick( &Objects[obj1].pos, &Objects[obj2].pos );
3701         }
3702 }
3703
3704 // funciton to determine when the last meaningful order was given to one or more ships.  Returns
3705 // true or false depending on whether or not a meaningful order was received
3706 int sexp_last_order_time( int n )
3707 {
3708         int instance, i;
3709         fix time;
3710         char *name;
3711         ai_goals *aigp;
3712
3713         time = i2f(atoi(CTEXT(n)));
3714         SDL_assert ( time >= 0 );
3715
3716         n = CDR(n);
3717         while ( n != -1 ) {
3718                 name = CTEXT(n);
3719                 instance = ship_name_lookup(name);
3720                 if ( instance != -1 ) {
3721                         aigp = Ai_info[Ships[instance].ai_index].goals;
3722                 } else {
3723                         instance = wing_name_lookup(name);
3724                         if ( instance == -1 )                                           // if we cannot find ship or wing, return 0
3725                                 return 0;
3726                         aigp = Wings[instance].ai_goals;
3727                 }
3728
3729                 // with the ship, check the ai_goals structure for this ship and determine if there are any
3730                 // orders which are < time seconds since current mission time
3731                 for ( i = 0; i < MAX_AI_GOALS; i++ ) {
3732                         int mode;
3733
3734                         mode = aigp->ai_mode;
3735                         if ( (mode  != AI_GOAL_NONE) && (mode != AI_GOAL_WARP) )
3736                                 if ( (aigp->time + time) > Missiontime )
3737                                         break;
3738                         aigp++;
3739                 }
3740                 if ( i == MAX_AI_GOALS )
3741                         return 1;
3742
3743                 n = CDR(n);
3744         }
3745
3746         return 0;
3747 }
3748
3749 // sexpression to return the number of players in the mission
3750 int sexp_num_players()
3751 {
3752         int count;
3753         object *objp;
3754
3755         count = 0;
3756         for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
3757                 if ( (objp->type == OBJ_SHIP) && (objp->flags & OF_PLAYER_SHIP) )
3758                         count++;
3759         }
3760
3761         return count;
3762 }
3763
3764 // expression to determine if the current skill level of the game is at least
3765 // the skill level given in the sexpression
3766 int sexp_skill_level_at_least( int n )
3767 {
3768         int i;
3769         char *level_name;
3770
3771         level_name = CTEXT(n);
3772         for (i = 0; i < NUM_SKILL_LEVELS; i++ ) {
3773                 if ( !SDL_strcasecmp(level_name, Skill_level_names(i, 0)) ) {
3774                         if ( Game_skill_level >= i ){
3775                                 return 1;
3776                         } else {
3777                                 return 0;
3778                         }
3779                 }
3780         }
3781
3782         // return 0 if not found!!!
3783         return 0;
3784 }
3785
3786 int sexp_was_promotion_granted(int n)
3787 {
3788         if (Player->flags & PLAYER_FLAGS_PROMOTED)
3789                 return 1;
3790
3791         return 0;
3792 }
3793
3794 int sexp_was_medal_granted(int n)
3795 {
3796         int i;
3797         char *medal_name;
3798
3799         if (n < 0) {
3800                 if (Player->stats.m_medal_earned >= 0)
3801                         return 1;
3802
3803                 return 0;
3804         }
3805
3806         medal_name = CTEXT(n);
3807         for (i=0; i<NUM_MEDALS; i++) {
3808                 if (!SDL_strcasecmp(medal_name, Medals[i].name))
3809                         break;
3810         }
3811
3812         if ( (i < NUM_MEDALS) && (Player->stats.m_medal_earned == i) )
3813                 return 1;
3814
3815         return 0;
3816 }
3817
3818 // function which returns true if the percentage of ships (and ships in wings) departed is at
3819 // least the percentage given.  what determine if we should check destroyed or departed status
3820 int sexp_percent_ships_depart_destroy(int n, int what)
3821 {
3822         int percent;
3823         int total, count;
3824         char *name;
3825
3826         percent = atoi(CTEXT(n));
3827
3828         total = 0;
3829         count = 0;
3830         // iterate through the rest of the ships/wings in the list and tally the departures and the
3831         // total
3832         for ( n = CDR(n); n != -1; n = CDR(n) ) {
3833                 int wingnum;
3834
3835                 name = CTEXT(n);
3836
3837                 wingnum = wing_name_lookup( name, 1 );
3838                 if ( wingnum != -1 ) {
3839                         // for wings, we can increment the total by the total number of ships that we expect for
3840                         // this wing, and the departures by the number of departures stored for this wing
3841                         total += (Wings[wingnum].wave_count * Wings[wingnum].num_waves);
3842                         if ( what == OP_PERCENT_SHIPS_DEPARTED )
3843                                 count += Wings[wingnum].total_departed;
3844                         else if ( what == OP_PERCENT_SHIPS_DESTROYED )
3845                                 count += Wings[wingnum].total_destroyed;
3846                         else
3847                                 Int3();                 // this would be very bogus!
3848                 } else {
3849                         // must be a ship, so increment the total by 1, then determine if this ship has departed
3850                         total++;
3851                         if ( what == OP_PERCENT_SHIPS_DEPARTED ) {
3852                                 if ( mission_log_get_time(LOG_SHIP_DEPART, name, NULL, NULL) )
3853                                         count++;
3854                         } else if ( what == OP_PERCENT_SHIPS_DESTROYED ) {
3855                                 if ( mission_log_get_time(LOG_SHIP_DESTROYED, name, NULL, NULL) )
3856                                         count++;
3857                         } else
3858                                 Int3();                 // this would be very bogus as well.
3859
3860                 }
3861         }
3862
3863         // now, look at the percentage
3864         if ( (total > 0) && (((count * 100) / total) >= percent) )
3865                 return SEXP_KNOWN_TRUE;
3866         else
3867                 return 0;
3868 }
3869
3870 // function to tell is a list of ships has departed from within a radius of a given jump node.
3871 // returns true N seconds after the list of ships have departed
3872 int sexp_depart_node_delay( int n )
3873 {
3874         int delay, count, num_departed;
3875         char *jump_node_name, *name;
3876         fix latest_time, this_time;
3877
3878         delay = atoi( CTEXT(n) );
3879         n = CDR(n);
3880         jump_node_name = CTEXT(n);
3881
3882         // iterate through the list of ships
3883         n = CDR(n);
3884         latest_time = 0;
3885         count = 0;
3886         num_departed = 0;
3887         while ( n != -1 ) {
3888                 count++;
3889                 name = CTEXT(n);
3890
3891                 if (sexp_query_has_yet_to_arrive(name))
3892                         return SEXP_CANT_EVAL;
3893
3894                 // if ship/wing destroyed, sexpression is known false.  Also, if there is no departure log entry, then
3895                 // the sexpression is not true.
3896                 if ( mission_log_get_time(LOG_SHIP_DESTROYED, name, NULL, NULL) )
3897                         return SEXP_KNOWN_FALSE;
3898                 else if ( mission_log_get_time(LOG_SHIP_DEPART, name, jump_node_name, &this_time) ) {
3899                         num_departed++;
3900                         if ( this_time > latest_time )
3901                                 latest_time = this_time;
3902                 }
3903                 n = CDR(n);
3904         }
3905
3906         if ( (count == num_departed) && ((Missiontime - latest_time) >= delay) )
3907                 return SEXP_KNOWN_TRUE;
3908         else
3909                 return 0;
3910 }
3911
3912 // sexpression which returns true when the listed ships/wings have all been destroyed or
3913 // have departed.
3914 int sexp_destroyed_departed_delay( int n )
3915 {
3916         int count, total;
3917         fix delay, latest_time;
3918         char *name;
3919
3920         // get the delay
3921         delay = i2f(atoi(CTEXT(n)));
3922         n = CDR(n);
3923
3924         count = 0;                                      // number destroyed or departed
3925         total = 0;                                      // total number of ships/wings to check
3926         latest_time = 0;
3927         while ( n != -1 ) {
3928                 int wingnum;
3929                 fix time_gone = 0;
3930
3931                 total++;
3932                 name = CTEXT(n);
3933
3934                 // for wings, check the WF_GONE flag to see if there are no more ships in this wing to arrive.
3935                 wingnum = wing_name_lookup(name, 1);
3936                 if ( wingnum != -1 ) {
3937                         if ( Wings[wingnum].flags & WF_WING_GONE ) {
3938                                 // be sure to get the latest time of one of these 
3939                                 if ( Wings[wingnum].time_gone > latest_time ){
3940                                         time_gone = Wings[wingnum].time_gone;
3941                                 }
3942                                 count++;
3943                         }
3944                 } else if ( mission_log_get_time(LOG_SHIP_DEPART, name, NULL, &time_gone) ) {
3945                         count++;
3946                 } else if ( mission_log_get_time(LOG_SHIP_DESTROYED, name, NULL, &time_gone) ) {
3947                         count++;
3948                 }
3949
3950                 // check our latest time
3951                 if ( time_gone > latest_time ){
3952                         latest_time = time_gone;
3953                 }
3954
3955                 n = CDR(n);
3956         }
3957
3958         if ( (count == total) && (Missiontime > (latest_time + delay)) )
3959                 return SEXP_KNOWN_TRUE;
3960         else
3961                 return 0;
3962 }
3963
3964 int sexp_special_warpout_name( int node )
3965 {
3966         int shipnum, knossos_num;
3967         char *ship_name, *knossos;
3968
3969         ship_name = CTEXT(node);
3970         knossos = CTEXT(CDR(node));
3971
3972         // check to see if either ship was destroyed or departed.  If so, then make this node known
3973         // false
3974         if ( mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) || mission_log_get_time( LOG_SHIP_DEPART, ship_name, NULL, NULL) ||
3975                   mission_log_get_time(LOG_SHIP_DESTROYED, knossos, NULL, NULL) || mission_log_get_time( LOG_SHIP_DEPART, knossos, NULL, NULL) ) 
3976                 return SEXP_NAN_FOREVER;
3977
3978         // get ship name
3979         shipnum = ship_name_lookup(ship_name);
3980         if (shipnum < 0) {
3981                 return SEXP_NAN;
3982         }
3983
3984         // get knossos ship
3985         knossos_num = ship_name_lookup(knossos);
3986         if (knossos_num < 0) {
3987                 return SEXP_NAN;
3988         }
3989
3990         // set special warpout objnum
3991         Ships[shipnum].special_warp_objnum = knossos_num;
3992         return 0;
3993 }
3994
3995
3996 // function which determines if N seconds have elpased since all discovery of all cargo
3997 // of given ships
3998 int sexp_is_cargo_known( int n, int check_delay )
3999 {
4000         int count, shipnum, num_known, delay;
4001         char *name;
4002
4003         SDL_assert ( n >= 0 );
4004
4005         count = 0;
4006         num_known = 0;
4007
4008         // get the delay value (if there is one)
4009         delay = 0;
4010         if ( check_delay ) {
4011                 delay = atoi(CTEXT(n) );
4012                 n = CDR(n);
4013         }
4014
4015         while ( n != -1 ) {
4016                 fix time_known;
4017                 int is_known;
4018
4019                 is_known = 0;
4020
4021                 count++;
4022
4023                 // see if we have already checked this entry
4024                 if ( Sexp_nodes[n].value == SEXP_KNOWN_TRUE ) {
4025                         num_known++;
4026                 } else {
4027                         int exited_index;
4028
4029                         name = CTEXT(n);
4030
4031                         // see if the ship has already exited the mission (either through departure or destruction).  If so,
4032                         // grab the status of whether the cargo is known from this list
4033                         exited_index = ship_find_exited_ship_by_name( name );
4034                         if (exited_index != -1 ) {
4035                                 if ( !(Ships_exited[exited_index].flags & SEF_CARGO_KNOWN) )
4036                                         return SEXP_KNOWN_FALSE;
4037
4038                                 // check the delay of when we found out.  We use the ship died time which isn't entirely accurate
4039                                 // but won't cause huge delays.
4040                                 time_known = Missiontime - Ships_exited[exited_index].time;
4041                                 if ( f2i(time_known) >= delay )
4042                                         is_known = 1;
4043                         } else {
4044
4045                                 // otherwise, ship should still be in the mission.  If ship_name_lookup returns -1, then ship
4046                                 // is yet to arrive.
4047                                 shipnum = ship_name_lookup( name );
4048                                 if ( shipnum != -1 ) {
4049                                         if ( Ships[shipnum].flags & SF_CARGO_REVEALED ) {
4050                                                 time_known = Missiontime - Ships[shipnum].time_cargo_revealed;
4051                                                 if ( f2i(time_known) >= delay )
4052                                                         is_known = 1;
4053                                         }
4054                                 }
4055                         }
4056                 }
4057
4058                 // if cargo is known, mark our variable and this sexpression.
4059                 if ( is_known ) {
4060                         num_known++;
4061                         Sexp_nodes[n].value = SEXP_KNOWN_TRUE;
4062                 }
4063
4064                 n = CDR(n);
4065         }
4066
4067         Directive_count += count - num_known;
4068         if ( count == num_known )
4069                 return SEXP_KNOWN_TRUE;
4070         else
4071                 return 0;
4072 }
4073
4074 int sexp_has_been_tagged_delay(int n)
4075 {
4076         int count, shipnum, num_known, delay;
4077         char *name;
4078
4079         SDL_assert ( n >= 0 );
4080
4081         count = 0;
4082         num_known = 0;
4083
4084         // get the delay value
4085         delay = atoi(CTEXT(n) );
4086
4087         n = CDR(n);
4088
4089         while ( n != -1 ) {
4090                 fix time_known;
4091                 int is_known;
4092
4093                 is_known = 0;
4094
4095                 count++;
4096
4097                 // see if we have already checked this entry
4098                 if ( Sexp_nodes[n].value == SEXP_KNOWN_TRUE ) {
4099                         num_known++;
4100                 } else {
4101                         int exited_index;
4102
4103                         name = CTEXT(n);
4104
4105                         // see if the ship has already exited the mission (either through departure or destruction).  If so,
4106                         // grab the status of whether the cargo is known from this list
4107                         exited_index = ship_find_exited_ship_by_name( name );
4108                         if (exited_index != -1 ) {
4109                                 if ( !(Ships_exited[exited_index].flags & SEF_BEEN_TAGGED) )
4110                                         return SEXP_KNOWN_FALSE;
4111
4112                                 // check the delay of when we found out.  We use the ship died time which isn't entirely accurate
4113                                 // but won't cause huge delays.
4114                                 time_known = Missiontime - Ships_exited[exited_index].time;
4115                                 if ( f2i(time_known) >= delay )
4116                                         is_known = 1;
4117                         } else {
4118
4119                                 // otherwise, ship should still be in the mission.  If ship_name_lookup returns -1, then ship
4120                                 // is yet to arrive.
4121                                 shipnum = ship_name_lookup( name );
4122                                 if ( shipnum != -1 ) {
4123                                         if ( Ships[shipnum].time_first_tagged != 0 ) {
4124                                                 time_known = Missiontime - Ships[shipnum].time_first_tagged;
4125                                                 if ( f2i(time_known) >= delay )
4126                                                         is_known = 1;
4127                                         }
4128                                 }
4129                         }
4130                 }
4131
4132                 // if cargo is known, mark our variable and this sexpression.
4133                 if ( is_known ) {
4134                         num_known++;
4135                         Sexp_nodes[n].value = SEXP_KNOWN_TRUE;
4136                 }
4137
4138                 n = CDR(n);
4139         }
4140
4141         Directive_count += count - num_known;
4142         if ( count == num_known )
4143                 return SEXP_KNOWN_TRUE;
4144         else
4145                 return 0;
4146 }
4147
4148 int sexp_cap_subsys_cargo_known_delay(int n)
4149 {
4150         int delay, count, delta_time, num_known;
4151         char *ship_name, *subsys_name;
4152
4153         num_known = 0;
4154         count = 0;
4155
4156         // get delay
4157         delay = atoi(CTEXT(n));
4158         n = CDR(n);
4159
4160         // get shipname
4161         ship_name = CTEXT(n);
4162         n = CDR(n);
4163
4164         while ( n != -1 ) {
4165                 fix time_known;
4166                 int is_known;
4167                 int logged = 0;
4168
4169                 is_known = 0;
4170                 count++;
4171
4172                 // see if we have already checked this entry
4173                 if ( Sexp_nodes[n].value == SEXP_KNOWN_TRUE ) {
4174                         num_known++;
4175                 } else {
4176                         // get subsys name
4177                         subsys_name = CTEXT(n);
4178
4179                         logged = mission_log_get_time(LOG_CAP_SUBSYS_CARGO_REVEALED, ship_name, subsys_name, &time_known);
4180                         if (logged) {
4181                                 delta_time = f2i(Missiontime - time_known);
4182                                 if (delta_time >= delay) {
4183                                         is_known = 1;
4184                                 }
4185                         }
4186
4187                         // if (exited or destroyed) and not logged, known false
4188                         // otherwise, still out there and cargo not yet known
4189                         if ( (mission_log_get_time(LOG_SHIP_DEPART, ship_name, NULL, NULL ) || mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL )) && !logged ) {
4190                                 return SEXP_KNOWN_FALSE;
4191                         }
4192                 }
4193
4194                 if (is_known) {
4195                         num_known++;
4196                         Sexp_nodes[n].value = SEXP_KNOWN_TRUE;
4197                 }
4198
4199                 n = CDR(n);
4200         }
4201
4202         Directive_count += count - num_known;
4203         if ( count == num_known )
4204                 return SEXP_KNOWN_TRUE;
4205         else
4206                 return 0;
4207 }
4208
4209
4210 // return object index of waypoint or -1 if no such waypoint
4211 int waypoint_lookup(char *name)
4212 {
4213         char buf[128];
4214         int i;
4215         object *ptr;
4216
4217         ptr = GET_FIRST(&obj_used_list);
4218         while (ptr != END_OF_LIST(&obj_used_list)) {
4219                 if (ptr->type == OBJ_WAYPOINT) {
4220                         i = ptr->instance;
4221                         SDL_snprintf(buf, SDL_arraysize(buf), "%s:%d", Waypoint_lists[i / 65536].name, (i & 0xffff) + 1);
4222                         if ( !SDL_strcasecmp(buf, name) )
4223                                 return OBJ_INDEX(ptr);
4224                 }
4225
4226                 ptr = GET_NEXT(ptr);
4227         }
4228
4229         return -1;
4230 }
4231
4232 // conditional sexpressions follow
4233         
4234 // eval_when evaluates the when conditional
4235 int eval_when(int n)
4236 {
4237         int cond, val;
4238
4239         SDL_assert( n >= 0 );                           // must have valid sexp index
4240
4241         cond = CAR(n);
4242         val = eval_sexp(cond);          // get the value of the the conditional
4243         if ( val ) {                                    // if the value is true, perform the actions is the 'then' part of the if
4244                 int actions, exp;
4245
4246                 actions = CDR(n);
4247                 while ( actions != -1 ) {
4248                         exp = CAR(actions);
4249                         if ( exp != -1 )
4250                                 val = eval_sexp(exp);                                                           // these sexp evaled only for side effects
4251                         actions = CDR(actions);
4252                 }
4253         }
4254
4255         if (Sexp_nodes[cond].value == SEXP_KNOWN_FALSE)
4256                 return SEXP_KNOWN_FALSE;  // no need to waste time on this anymore
4257
4258         if (val == SEXP_KNOWN_FALSE)
4259                 return 0;  // can't return known false, as this would bypass future actions under the when
4260
4261         return val;
4262 }
4263
4264 // eval_cond() evaluates the cond conditional
4265 int eval_cond( int n )
4266 {
4267         int cond = 0, node, val = 0;
4268
4269         SDL_assert (n >= 0);
4270         while (n >= 0) {
4271                 node = CAR(n);
4272                 cond = CAR(node);
4273                 val = eval_sexp(cond);
4274
4275                 // if the conditional evaluated to true, then we must evaluate the rest of the expression returning
4276                 // the value of this evaluation
4277                 if (val) {
4278                         int actions, exp;
4279
4280                         val = 0;
4281                         actions = CDR(node);
4282                         while (actions >= 0) {
4283                                 exp = CAR(actions);
4284                                 if (exp >= -1)
4285                                         val = eval_sexp(exp);                                                           // these sexp evaled only for side effects
4286
4287                                 actions = CDR(actions);
4288                         }
4289
4290                         break;
4291                 }
4292
4293                 // move onto the next cond clause
4294                 n = CDR(n);
4295         }
4296
4297         return val;
4298 }
4299
4300 int sexp_is_iff( int n )
4301 {
4302         char *ship_name, *iff;
4303         int num, team;
4304
4305         SDL_assert ( n >= 0 );
4306
4307         // iff value is the first parameter, second is a list of one or more ships to check to see if the
4308         // iff value matches
4309         iff = CTEXT(n);
4310         if ( !SDL_strcasecmp(iff, "friendly") )
4311                 team = TEAM_FRIENDLY;
4312         else if ( !SDL_strcasecmp(iff, "hostile") )
4313                 team = TEAM_HOSTILE;
4314         else if ( !SDL_strcasecmp(iff, "neutral") )
4315                 team = TEAM_NEUTRAL;
4316         else if ( !SDL_strcasecmp(iff, "unknown") )
4317                 team = TEAM_UNKNOWN;
4318         else if ( !SDL_strcasecmp(iff, "traitor") )
4319                 team = TEAM_TRAITOR;
4320         else {
4321                 Int3();
4322                 mprintf(("Warning: Team %s no longer supported.  Just Friendly and Hostile.\n", iff));
4323                 team = TEAM_HOSTILE;
4324         }
4325
4326         n = CDR(n);
4327         while ( n != -1 ) {
4328                 ship_name = CTEXT(n);
4329                 // find the ship and check to be sure that it is still around.
4330                 num = ship_name_lookup(ship_name);
4331                 if ( num < 0 )                  // if the ship is gone, can't check it's iff.
4332                         continue;
4333
4334                 // if the team doesn't match the team specified, return 0 immediately
4335                 if ( Ships[num].team != team)
4336                         return 0;
4337
4338                 n = CDR(n);
4339         }
4340
4341         return 1;
4342 }
4343
4344 void sexp_change_iff( int n )
4345 {
4346         char *ship_name, *new_iff;
4347         int num, new_team;
4348
4349         SDL_assert ( n >= 0 );
4350         new_iff = CTEXT(n);
4351         if ( !SDL_strcasecmp(new_iff, "friendly") )
4352                 new_team = TEAM_FRIENDLY;
4353         else if ( !SDL_strcasecmp(new_iff, "hostile") )
4354                 new_team = TEAM_HOSTILE;
4355         else if ( !SDL_strcasecmp(new_iff, "neutral") )
4356                 new_team = TEAM_NEUTRAL;
4357         else if ( !SDL_strcasecmp(new_iff, "unknown") )
4358                 new_team = TEAM_UNKNOWN;
4359         else if ( !SDL_strcasecmp(new_iff, "traitor") )
4360                 new_team = TEAM_TRAITOR;
4361         else {
4362                 mprintf(("Warning: Team %s no longer supported.  Just Friendly and Hostile.\n", new_iff));
4363                 new_team = TEAM_HOSTILE;
4364                 Int3();
4365         }
4366
4367         n = CDR(n);
4368         while ( n != -1 ) {
4369                 ship_name = CTEXT(n);
4370
4371                 // find the ship and check to be sure that it is still around.
4372                 num = ship_name_lookup(ship_name);
4373                 if ( num >= 0 ) {                                       // only change iff if we found the ship
4374                         Ships[num].team = new_team;
4375
4376                         // send a network packet if we need to
4377                         if((Game_mode & GM_MULTIPLAYER) && (Net_player != NULL) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) && (Ships[num].objnum >= 0)){
4378                                 send_change_iff_packet(Objects[Ships[num].objnum].net_signature, new_team);
4379                         }
4380                 }
4381
4382                 n = CDR(n);
4383         }
4384         
4385 }
4386
4387 // following routine adds an ai goal to a ship structure.  The sexpression index
4388 // passed in should be an ai-goal of the proper form.  The code in MissionGoal should
4389 // check the syntax.
4390
4391 void sexp_add_ship_goal( int n )
4392 {
4393         int num, sindex;
4394         char *ship_name;
4395
4396         SDL_assert ( n >= 0 );
4397         ship_name = CTEXT(n);
4398         num = ship_name_lookup(ship_name);
4399         if ( num < 0 )                                                                  // ship not around anymore???? then forget it!
4400                 return;
4401
4402         sindex = CDR(n);
4403         ai_add_ship_goal_sexp( sindex, AIG_TYPE_EVENT_SHIP, &(Ai_info[Ships[num].ai_index]) );
4404 }
4405
4406 // identical to above, except add a wing
4407 void sexp_add_wing_goal( int n )
4408 {
4409         int num, sindex;
4410         char *wing_name;
4411
4412         SDL_assert ( n >= 0 );
4413         wing_name = CTEXT(n);
4414         num = wing_name_lookup(wing_name);
4415         if ( num < 0 )                                                                  // ship not around anymore???? then forget it!
4416                 return;
4417
4418         sindex = CDR(n);
4419         ai_add_wing_goal_sexp( sindex, AIG_TYPE_EVENT_WING, num );
4420 }
4421
4422 // sexp_add_goal adds a goal to the specified entiry (ships and wings have unique names between
4423 // the two sets).
4424 void sexp_add_goal( int n )
4425 {
4426         int num, sindex;
4427         char *name;
4428
4429         SDL_assert ( n >= 0 );
4430         name = CTEXT(n);
4431         sindex = CDR(n);
4432
4433         // first, look for ship name -- if found, then add ship goal.  else look for wing name -- if
4434         // found, add wing goal
4435         if ( (num = ship_name_lookup(name)) != -1 )
4436                 ai_add_ship_goal_sexp( sindex, AIG_TYPE_EVENT_SHIP, &(Ai_info[Ships[num].ai_index]) );
4437         else if ( (num = wing_name_lookup(name)) != -1 )
4438                 ai_add_wing_goal_sexp( sindex, AIG_TYPE_EVENT_WING, num );
4439 }
4440
4441 // clears out all ai goals for a ship
4442 void sexp_clear_ship_goals( int n )
4443 {
4444         int num;
4445         char *ship_name;
4446
4447         SDL_assert ( n >= 0 );
4448         ship_name = CTEXT(n);
4449         num = ship_name_lookup(ship_name);
4450         ai_clear_ship_goals( &(Ai_info[Ships[num].ai_index]) );
4451 }
4452
4453 // clears out ai goals for a wing
4454 void sexp_clear_wing_goals( int n )
4455 {
4456         int num;
4457         char *wing_name;
4458
4459         SDL_assert ( n >= 0 );
4460         wing_name = CTEXT(n);
4461         num = wing_name_lookup(wing_name);
4462         if ( num < 0 )
4463                 return;
4464         ai_clear_wing_goals( num );
4465 }
4466
4467 // this function clears all ai goals for the given ship or wing
4468 void sexp_clear_goals( int n )
4469 {
4470         int num;
4471         char *name;
4472
4473         SDL_assert ( n >= 0 );
4474         while ( n != -1 ) {
4475                 name = CTEXT(n);
4476                 if ( (num = ship_name_lookup(name)) != -1 )
4477                         ai_clear_ship_goals( &(Ai_info[Ships[num].ai_index]) );
4478                 else if ( (num = wing_name_lookup(name)) != -1 )
4479                         ai_clear_wing_goals( num );
4480
4481                 n = CDR(n);
4482         }
4483 }
4484
4485 // this function get called by send-message or send-message random with the name of the message, sender,
4486 // and priority.
4487 void sexp_send_one_message( char *name, char *who_from, char *priority, int group, int delay )
4488 {
4489         int ipriority, num, ship_index, source;
4490         ship *shipp;
4491
4492         if(physics_paused){
4493                 return;
4494         }
4495
4496         // determine the priority of the message
4497         if ( !SDL_strcasecmp(priority, "low") )
4498                 ipriority = MESSAGE_PRIORITY_LOW;
4499         else if ( !SDL_strcasecmp(priority, "normal") )
4500                 ipriority = MESSAGE_PRIORITY_NORMAL;
4501         else if ( !SDL_strcasecmp(priority, "high") )
4502                 ipriority = MESSAGE_PRIORITY_HIGH;
4503         else {
4504                 Int3();
4505                 ipriority = MESSAGE_PRIORITY_NORMAL;
4506         }
4507
4508         // check to see if the 'who_from' string is a ship that had been destroyed or departed.  If so,
4509         // then don't send the message.  We must look at 'who_from' to determine what to look for.  who_from
4510         // may be any allied person, any wingman, a wingman from a specific wing, or a specific ship
4511         ship_index = -1;
4512         shipp = NULL;
4513         source = MESSAGE_SOURCE_COMMAND;
4514         if ( who_from[0] == '#' ) {
4515                 message_send_unique_to_player( name, &(who_from[1]), MESSAGE_SOURCE_SPECIAL, ipriority, group, delay );
4516                 return;
4517         } else if (!SDL_strcasecmp(who_from, "<any allied>")) {
4518                 //Int3();                       // no longer supported
4519                 return;
4520         } else if ( (num = wing_name_lookup(who_from)) != -1 ) {
4521                 // message from a wing
4522                 // this will be an invalid case soon
4523                 // Int3();
4524                 // choose wing leader to speak for wing (hence "1" at end of ship_get_random_ship_in_wing)
4525                 ship_index = ship_get_random_ship_in_wing( num, SHIP_GET_NO_PLAYERS, 1 );
4526                 if ( ship_index == -1 ) {
4527                         if ( ipriority != MESSAGE_PRIORITY_HIGH )
4528                                 return;
4529                 }
4530
4531         } else if ( mission_log_get_time(LOG_SHIP_DESTROYED, who_from, NULL, NULL) || mission_log_get_time(LOG_SHIP_DEPART, who_from, NULL, NULL) 
4532                 || mission_log_get_time(LOG_WING_DESTROYED, who_from, NULL, NULL) || mission_log_get_time(LOG_WING_DEPART, who_from, NULL, NULL) ) {
4533                 // getting into this if statement means that the ship or wing (sender) is no longer in the mission
4534                 // if message is high priority, make it come from Terran Command
4535                 if ( ipriority != MESSAGE_PRIORITY_HIGH )
4536                         return;
4537                 
4538                 source = MESSAGE_SOURCE_COMMAND;
4539
4540         } else if ( !SDL_strcasecmp(who_from, "<any wingman>") || (wing_name_lookup(who_from) != -1) ) {
4541                 source = MESSAGE_SOURCE_WINGMAN;
4542         } else {
4543                 // Message from a apecific ship
4544                 // bail if not high priority, otherwise reroute to command
4545                 source = MESSAGE_SOURCE_SHIP;
4546                 ship_index = ship_name_lookup(who_from);
4547                 if ( ship_index == -1 ) {
4548                         if ( ipriority != MESSAGE_PRIORITY_HIGH )
4549                                 return;
4550                         source = MESSAGE_SOURCE_COMMAND;
4551                 }
4552         }
4553
4554         if ( ship_index == -1 ){
4555                 shipp = NULL;
4556         } else {
4557                 shipp = &Ships[ship_index];
4558         }
4559
4560         message_send_unique_to_player( name, shipp, source, ipriority, group, delay );
4561 }
4562
4563 void sexp_send_message( int n )
4564 {
4565         char *name, *who_from, *priority, *tmp;
4566
4567         if(physics_paused){
4568                 return;
4569         }
4570
4571         SDL_assert ( n != -1 );
4572         who_from = CTEXT(n);
4573         priority = CTEXT(CDR(n));
4574         name = CTEXT(CDR(CDR(n)));
4575
4576         // a temporary check to see if the name field matched a priority since I am in the process
4577         // of reordering the arguments
4578         if ( !SDL_strcasecmp(name, "low") || !SDL_strcasecmp(name, "normal") || !SDL_strcasecmp(name, "high") ) {
4579                 tmp = name;
4580                 name = priority;
4581                 priority = tmp;
4582         }
4583
4584         sexp_send_one_message( name, who_from, priority, 0, 0 );
4585 }
4586
4587 void sexp_send_message_list( int n )
4588 {
4589         char *name, *who_from, *priority;
4590         int delay;
4591
4592         if(physics_paused){
4593                 return;
4594         }
4595
4596         // send a bunch of messages
4597         delay = 0;
4598         while(n != -1){
4599                 who_from = CTEXT(n);
4600
4601                 // next node
4602                 n = CDR(n);
4603                 if(n == -1){
4604                         Warning(LOCATION, "Detected incomplete parameter list in sexp-send-message-list");
4605                         return;
4606                 }
4607                 priority = CTEXT(n);
4608
4609                 // next node
4610                 n = CDR(n);
4611                 if(n == -1){
4612                         Warning(LOCATION, "Detected incomplete parameter list in sexp-send-message-list");
4613                         return;
4614                 }
4615                 name = CTEXT(n);
4616
4617                 // next node
4618                 n = CDR(n);
4619                 if(n == -1){
4620                         Warning(LOCATION, "Detected incomplete parameter list in sexp-send-message-list");
4621                         return;
4622                 }
4623                 delay += atoi(CTEXT(n));
4624
4625                 // send the message
4626                 sexp_send_one_message(name, who_from, priority, 1, delay);
4627
4628                 // next node
4629                 n = CDR(n);
4630         }
4631 }
4632
4633 void sexp_send_random_message( int n )
4634 {
4635         char *name, *who_from, *priority;
4636         int temp, num_messages, message_num;
4637
4638         SDL_assert ( n != -1 );
4639         who_from = CTEXT(n);
4640         priority = CTEXT(CDR(n));
4641
4642         if(physics_paused){
4643                 return;
4644         }
4645
4646         // count the number of messages that we have
4647         n = CDR(CDR(n));
4648         temp = n;
4649         num_messages = 0;
4650         while ( n != -1 ) {
4651                 n = CDR(n);
4652                 num_messages++;
4653         }
4654
4655         if (num_messages == 0) {
4656                 Int3();
4657                 return;
4658         }
4659
4660         // get a random message, and pass the parameters to send_one_message
4661         message_num = myrand() % num_messages;
4662         n = temp;
4663         while ( n != -1 ) {
4664                 if ( message_num == 0 )
4665                         break;
4666                 message_num--;
4667                 n = CDR(n);
4668         }
4669         SDL_assert (n != -1);           // should have found the message!!!
4670         name = CTEXT(n);
4671
4672         sexp_send_one_message( name, who_from, priority, 0, 0 );
4673 }
4674
4675 void sexp_self_destruct( int n )
4676 {
4677         char *ship_name;
4678         int shipnum;
4679
4680         while ( n != -1 ) {
4681                 // get the ship name and be sure that it is still in the mission.  Destroy it if we find it
4682                 ship_name = CTEXT(n);
4683                 shipnum = ship_name_lookup( ship_name );
4684                 if ( shipnum == -1 )
4685                         return;
4686                 ship_self_destruct( &Objects[Ships[shipnum].objnum] );
4687
4688                 n = CDR(n);
4689         }
4690 }
4691
4692 void sexp_next_mission( int n )
4693 {
4694         char *mission_name;
4695         int i;
4696
4697         mission_name = CTEXT(n);
4698         for (i = 0; i < Campaign.num_missions; i++) {
4699                 if ( !SDL_strcasecmp(Campaign.missions[i].name, mission_name) ) {
4700                         Campaign.next_mission = i;
4701                         return;
4702                 }
4703         }
4704         Error(LOCATION, "Mission name %s not found in campaign file for next-mission command", mission_name);
4705 }
4706
4707 // function to deal with the end-of-campaign sexpression.  
4708 void sexp_end_of_campaign( int n )
4709 {
4710         // this is really a do-nothing sexpression.  It is pretty much a placeholder to allow
4711         // campaigns to have repeat-mission branches at the end of the campaign.  By not setting
4712         // anything in this function, the higher level campaign code will see this as end-of-campaign
4713         // since next_mission isn't set to anything.  (To be safe, we'll set to -1).
4714         Campaign.next_mission = -1;     
4715 }
4716
4717 // sexpression to end everything.  One parameter is the movie to play when this is over.
4718 void sexp_end_campaign( int n )
4719 {
4720         // post and event to move us to the end-of-campaign state.  There we will play a movie, then
4721         // go to debriefing.
4722 #ifdef MAKE_FS1
4723         // needed to actually end the game
4724         gameseq_post_event( GS_EVENT_END_CAMPAIGN );
4725 #else
4726         // in FS2 our ending is a bit wacky. we'll just flag the mission as having ended the campaign   
4727         Campaign_ended_in_mission = 1;
4728 #endif
4729 }
4730
4731 // sabotage subsystem reduces the strength of a subsystem by the given percentage.  If it is reduced to
4732 // below 0%, then the hits of the subsystem are set to 0
4733 void sexp_sabotage_subsystem( int n )
4734 {
4735         char *shipname, *subsystem;
4736         int     percentage, shipnum, index;
4737         float sabotage_hits;
4738         ship    *shipp;
4739         ship_subsys *ss;
4740
4741         shipname = CTEXT(n);
4742         subsystem = CTEXT(CDR(n));
4743         percentage = atoi(CTEXT(CDR(CDR(n))));
4744
4745         shipnum = ship_name_lookup(shipname);
4746         
4747         // if no ship, then return immediately.
4748         if ( shipnum == -1 )
4749                 return;
4750         shipp = &Ships[shipnum];
4751
4752         // see if we are dealing with the HULL
4753         if ( !SDL_strcasecmp( subsystem, SEXP_HULL_STRING) ) {
4754                 float ihs;
4755                 object *objp;
4756
4757                 ihs = Ship_info[shipp->ship_info_index].initial_hull_strength;
4758                 sabotage_hits = ihs * ((float)percentage / 100.0f);
4759                 objp = &Objects[shipp->objnum];
4760                 objp->hull_strength -= sabotage_hits;
4761
4762                 // self destruct the ship if <= 0.
4763                 if ( objp->hull_strength <= 0.0f )
4764                         ship_self_destruct( objp );
4765                 return;
4766         }
4767
4768         // now find the given subsystem on the ship.  The subsystem should be an actual subsystem name
4769         // and not a generic type (generic type meaning SUBSYSTEM_ENGINE, etc).
4770
4771         index = ship_get_subsys_index(shipp, subsystem);
4772         if ( index == -1 ) {
4773                 nprintf(("Warning", "Couldn't find subsystem %s on ship %s for sabotage subsystem\n", subsystem, shipp->ship_name));
4774                 return;
4775         }
4776
4777         // get the pointer to the subsystem.  Check it's current hits against it's max hits, and
4778         // set the strength to the given percentage if current strength is > given percentage
4779         ss = ship_get_indexed_subsys( shipp, index );
4780         sabotage_hits = ss->system_info->max_hits * ((float)percentage / 100.0f);
4781         ss->current_hits -= sabotage_hits;
4782         if ( ss->current_hits < 0.0f )
4783                 ss->current_hits = 0.0f;
4784         ship_recalc_subsys_strength( shipp );
4785 }
4786
4787 // repair_subsystem adds some percentage of hits to a subsystem.  Anything repaired about 100% is
4788 // set to max hits
4789 void sexp_repair_subsystem( int n )
4790 {
4791         char *shipname, *subsystem;
4792         int     percentage, shipnum, index;
4793         float repair_hits;
4794         ship *shipp;
4795         ship_subsys *ss;
4796
4797         shipname = CTEXT(n);
4798         subsystem = CTEXT(CDR(n));
4799         shipnum = ship_name_lookup(shipname);
4800         
4801         // if no ship, then return immediately.
4802         if ( shipnum == -1 ) {
4803                 return;
4804         }
4805         shipp = &Ships[shipnum];
4806         
4807         // check if we've got a number or an op
4808         if ( CAR(CDR(CDR(n))) != -1) {
4809                 percentage = eval_sexp( CAR(CDR(CDR(n))) );
4810         } else {
4811                 percentage = atoi(CTEXT(CDR(CDR(n))));
4812         }
4813
4814         // see if we are dealing with the HULL
4815         if ( !SDL_strcasecmp( subsystem, SEXP_HULL_STRING) ) {
4816                 float ihs;
4817                 object *objp;
4818
4819                 ihs = Ship_info[shipp->ship_info_index].initial_hull_strength;
4820                 repair_hits = ihs * ((float)percentage / 100.0f);
4821                 objp = &Objects[shipp->objnum];
4822                 objp->hull_strength += repair_hits;
4823                 if ( objp->hull_strength > ihs )
4824                         objp->hull_strength = ihs;
4825                 return;
4826         }
4827
4828         // now find the given subsystem on the ship.  The subsystem should be an actual subsystem name
4829         // and not a generic type (generic type meaning SUBSYSTEM_ENGINE, etc).
4830
4831         index = ship_get_subsys_index(shipp, subsystem);
4832         if ( index == -1 ) {
4833                 nprintf(("Warning", "Couldn't find subsystem %s on ship %s for repair subsystem\n", subsystem, shipp->ship_name));
4834                 return;
4835         }
4836
4837         // get the pointer to the subsystem.  Check it's current hits against it's max hits, and
4838         // set the strength to the given percentage if current strength is < given percentage
4839         ss = ship_get_indexed_subsys( shipp, index );
4840         repair_hits = ss->system_info->max_hits * ((float)percentage / 100.0f);
4841         ss->current_hits += repair_hits;
4842         if ( ss->current_hits > ss->system_info->max_hits )
4843                 ss->current_hits = ss->system_info->max_hits;
4844         ship_recalc_subsys_strength( shipp );
4845 }
4846
4847 // sexpression code to set a subsystem of a ship at a specific percentage
4848 void sexp_set_subsystem_strength( int n )
4849 {
4850         char *shipname, *subsystem;
4851         int     percentage, shipnum, index;
4852         ship *shipp;
4853         ship_subsys *ss;
4854
4855         shipname = CTEXT(n);
4856         subsystem = CTEXT(CDR(n));
4857         percentage = num_eval(CDR(CDR(n)));
4858
4859         shipnum = ship_name_lookup(shipname);
4860         
4861         // if no ship, then return immediately.
4862         if ( shipnum == -1 )
4863                 return;
4864         shipp = &Ships[shipnum];
4865
4866         if ( percentage > 100 ) {
4867                 nprintf(("Warning", "percentage for set_subsystem_strength > 100 -- setting to 100\n"));
4868                 percentage = 100;
4869         } else if ( percentage < 0 ) {
4870                 nprintf(("Werning", "percantage for set_subsystem_strength < 0 -- setting to 0\n"));
4871                 percentage = 0;
4872         }
4873
4874         // see if we are dealing with the HULL
4875         if ( !SDL_strcasecmp( subsystem, SEXP_HULL_STRING) ) {
4876                 float ihs;
4877                 object *objp;
4878
4879                 objp = &Objects[shipp->objnum];
4880
4881                 // destroy the ship if percentage is 0
4882                 if ( percentage == 0 ) {
4883                         ship_self_destruct( objp );
4884                 } else {
4885                         ihs = Ship_info[shipp->ship_info_index].initial_hull_strength;
4886                         objp->hull_strength = ihs * ((float)percentage / 100.0f);
4887                 }
4888
4889                 return;
4890         }
4891
4892         // now find the given subsystem on the ship.  The subsystem should be an actual subsystem name
4893         // and not a generic type (generic type meaning SUBSYSTEM_ENGINE, etc).
4894
4895         index = ship_get_subsys_index(shipp, subsystem);
4896         if ( index == -1 ) {
4897                 nprintf(("Warning", "Couldn't find subsystem %s on ship %s for repair subsystem\n", subsystem, shipp->ship_name));
4898                 return;
4899         }
4900
4901         // get the pointer to the subsystem.  Check it's current hits against it's max hits, and
4902         // set the strength to the given percentage if current strength is < given percentage
4903         ss = ship_get_indexed_subsys( shipp, index );
4904
4905         // maybe blow up subsys
4906         if (ss->current_hits > 0) {
4907                 if (percentage < 1) {
4908                         do_subobj_destroyed_stuff(shipp, ss, NULL);
4909                 }
4910         }
4911
4912         // set hit points
4913         ss->current_hits = ss->system_info->max_hits * ((float)percentage / 100.0f);
4914
4915         ship_recalc_subsys_strength( shipp );
4916 }
4917
4918 // function which changes the validity of a goal.  The flag paramater tells us whether to mark the goals
4919 // as valid or invalid
4920 void sexp_change_goal_validity( int n, int flag )
4921 {
4922         char *name;
4923
4924         while ( n != -1 ) {
4925                 name = CTEXT(n);
4926                 if ( flag )
4927                         mission_goal_mark_valid( name );
4928                 else
4929                         mission_goal_mark_invalid( name );
4930
4931                 n = CDR(n);
4932         }
4933 }
4934
4935 // function to transfer cargo from one ship to another
4936 void sexp_transfer_cargo( int n )
4937 {
4938         char *shipname1, *shipname2;
4939         int shipnum1, shipnum2, i;
4940         object *objp;
4941
4942         shipname1 = CTEXT(n);
4943         shipname2 = CTEXT(CDR(n));
4944
4945         // find the ships -- if neither in the mission, the abort
4946         shipnum1 = ship_name_lookup(shipname1);
4947         shipnum2 = ship_name_lookup(shipname2);
4948         if ( (shipnum1 == -1) || (shipnum2 == -1) )
4949                 return;
4950
4951         // we must be sure that these two objects are indeed docked
4952         objp = ai_find_docked_object( &Objects[Ships[shipnum1].objnum] );
4953         if ( objp != &Objects[Ships[shipnum2].objnum] ) {
4954                 Int3();                 // you are trying to transfer cargo between two ships not docked
4955                 return;
4956         }
4957
4958         if ( !SDL_strcasecmp(Cargo_names[Ships[shipnum1].cargo1 & CARGO_INDEX_MASK], "nothing") ) {
4959                 Int3();                 // you are transfering no cargo!!!!
4960                 return;
4961         }
4962
4963         // transfer cargo from ship1 to ship2
4964 #ifndef NDEBUG
4965         // Don't give warning for large ships (cruiser on up) 
4966         if (! (Ship_info[Ships[shipnum2].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) ) {
4967                 if ( SDL_strcasecmp(Cargo_names[Ships[shipnum2].cargo1 & CARGO_INDEX_MASK], "nothing") ) {
4968                         Warning(LOCATION, "Transfering cargo to %s which already\nhas cargo %s.\nCargo will be replaced", Ships[shipnum2].ship_name, Cargo_names[Ships[shipnum2].cargo1 & CARGO_INDEX_MASK] );
4969                 }
4970         }
4971 #endif
4972         Ships[shipnum2].cargo1 = char(Ships[shipnum1].cargo1 & CARGO_INDEX_MASK);
4973
4974         if ( !(Ships[shipnum1].cargo1 & CARGO_NO_DEPLETE) ) {
4975                 // need to set ship1's cargo to nothing.  scan the cargo_names array looking for the string nothing.
4976                 // add it if not found
4977                 for (i = 0; i < Num_cargo; i++ ) {
4978                         if ( !SDL_strcasecmp(Cargo_names[i], "nothing") ) {
4979                                 Ships[shipnum1].cargo1 = char(i);
4980                                 return;
4981                         }
4982                 }
4983                 SDL_strlcpy(Cargo_names[i], "Nothing", NAME_LENGTH);
4984                 Num_cargo++;
4985         }
4986 }
4987
4988 // this function exchanges cargo between two ships
4989 void sexp_exchange_cargo( int n )
4990 {
4991         char *shipname1, *shipname2;
4992         int shipnum1, shipnum2, temp;
4993         object *objp;
4994
4995         shipname1 = CTEXT(n);
4996         shipname2 = CTEXT(CDR(n));
4997
4998         // find the ships -- if neither in the mission, the abort
4999         shipnum1 = ship_name_lookup(shipname1);
5000         shipnum2 = ship_name_lookup(shipname2);
5001         if ( (shipnum1 == -1) || (shipnum2 == -1) )
5002                 return;
5003
5004         // we must be sure that these two objects are indeed docked
5005         objp = ai_find_docked_object( &Objects[Ships[shipnum1].objnum] );
5006         if ( objp != &Objects[Ships[shipnum2].objnum] ) {
5007                 Int3();                 // you are trying to transfer cargo between two ships not docked
5008                 return;
5009         }
5010
5011         temp = (Ships[shipnum1].cargo1 & CARGO_INDEX_MASK);
5012         Ships[shipnum1].cargo1 = char(Ships[shipnum2].cargo1 & CARGO_INDEX_MASK);
5013         Ships[shipnum2].cargo1 = char(temp);
5014 }
5015
5016 void sexp_cap_waypont_speed(int n)
5017 {
5018         char *shipname;
5019         int shipnum;
5020         int speed;
5021
5022         shipname = CTEXT(n);
5023         speed = atoi(CTEXT(CDR(n)));
5024
5025         shipnum = ship_name_lookup(shipname);
5026
5027         if (shipnum == -1) {
5028                 Int3(); // trying to set waypoint speed of ship not already in game
5029                 return;
5030         }
5031
5032         // cap speed to range (-1, 127) to store within char
5033         if (speed < 0) {
5034                 speed = -1;
5035         }
5036
5037         if (speed > 127) {
5038                 speed = 127;
5039         }
5040
5041         Ai_info[Ships[shipnum].ai_index].waypoint_speed_cap = (char) speed;
5042 }
5043
5044 // this function causes a ship to jettison its cargo
5045 void sexp_jettison_cargo( int n )
5046 {
5047         char *shipname;
5048         int ship_index;//, jettison_delay;
5049
5050         // get some data
5051         shipname = CTEXT(n);
5052 //      jettison_delay = atoi(CTEXT(CDR(n)));
5053
5054         // lookup the ship
5055         ship_index = ship_name_lookup(shipname);
5056         if(ship_index < 0){
5057                 return;
5058         }
5059         
5060         // jettison cargo
5061         ship_jettison_cargo(&Ships[ship_index]);
5062 }
5063
5064 void sexp_cargo_no_deplete( int n )
5065 {
5066         char *shipname;
5067         int ship_index, no_deplete = 1;
5068
5069         // get some data
5070         shipname = CTEXT(n);
5071
5072         // lookup the ship
5073         ship_index = ship_name_lookup(shipname);
5074         if(ship_index < 0){
5075                 return;
5076         }
5077
5078         if ( !(Ship_info[Ships[ship_index].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) ) {
5079                 Warning(LOCATION, "Trying to make non BIG or HUGE ship %s with non-depletable cargo.\n", Ships[ship_index].ship_name);
5080                 return;
5081         }
5082
5083         if (CDR(n) != -1) {
5084                 no_deplete = atoi(CTEXT(CDR(n)));
5085                 SDL_assert((no_deplete == 0) || (no_deplete == 1));
5086                 if ( (no_deplete != 0) && (no_deplete != 1) ) {
5087                         no_deplete = 1;
5088                 }
5089         }
5090
5091         if (no_deplete) {
5092                 Ships[ship_index].cargo1 |= CARGO_NO_DEPLETE;
5093         } else {
5094                 Ships[ship_index].cargo1 &= (~CARGO_NO_DEPLETE);
5095         }
5096
5097 }
5098
5099 // sexpression to end the mission after N seconds!
5100 void sexp_end_mission_delay( int n )
5101 {
5102         //int delay;
5103
5104         //delay = atoi(CTEXT(n));
5105         //mission_parse_set_end_time( delay );
5106         mprintf(("Not ending mission -- end-mission sexpression no longer works!\n"));
5107 }
5108
5109 // funciton to toggle the status bit for the AI code which tells the AI if it is a good time
5110 // to rearm.  The status being set means good time.  Status not being set (unset), means bad time.
5111 // designers must implement this.
5112 void sexp_good_time_to_rearm( int n )
5113 {
5114         int i, time;
5115         char *team_name;
5116
5117         team_name = CTEXT(n);
5118         time = atoi(CTEXT(CDR(n)));                                             // this is the time for how long a good rearm is active -- in seconds
5119         for ( i = 0; i < Num_team_names; i++ ) {
5120                 if ( !SDL_strcasecmp(team_name, Team_names[i]) ) {
5121                         int team;
5122
5123                         team = 1 << i;
5124                         ai_set_rearm_status( team, time );
5125                 }
5126         }
5127 }
5128
5129 // function which grants promotion to the player
5130 void sexp_grant_promotion()
5131 {
5132         // short circuit multiplayer for now until we figure out what to do.
5133         if ( Game_mode & GM_MULTIPLAYER )
5134                 return;
5135
5136         // set a bit to tell player should get promoted at the end of the mission.  I suppose the other
5137         // thing that we could do would be to set the players score to at least the amount of
5138         // points for the next level, but this way is better I think.
5139         if ( Game_mode & GM_CAMPAIGN_MODE ) {
5140                 Player->flags |= PLAYER_FLAGS_PROMOTED;
5141         }
5142 }
5143
5144 // function which gives the named medal to the players in the mission
5145 void sexp_grant_medal( int n )
5146 {
5147         int i, j;
5148         char *medal_name;
5149
5150         // don't give medals in normal gameplay when not in campaign mode
5151         if ( (Game_mode & GM_NORMAL) && !(Game_mode & GM_CAMPAIGN_MODE) )
5152                 return;
5153
5154         SDL_assert(Player->stats.m_medal_earned < 0);  // Mission has problems.  Tried to grant 2 medals in 1 mission.
5155         medal_name = CTEXT(n);
5156         for (i = 0; i < NUM_MEDALS; i++ ) {
5157                 if ( !SDL_strcasecmp(medal_name, Medals[i].name) )
5158                         break;
5159         }
5160
5161         if ( i < NUM_MEDALS ) {
5162                 Player->stats.m_medal_earned = i;
5163                 if ( Game_mode & GM_MULTIPLAYER ) {
5164                         for ( j = 0; j < MAX_PLAYERS; j++ ) {
5165                                 if ( MULTI_CONNECTED(Net_players[j]) ) {
5166                                         Net_players[j].player->stats.m_medal_earned = i;
5167                                 }
5168                         }
5169                 }
5170         }
5171 }
5172
5173 void sexp_tech_add_ship(int node)
5174 {
5175         int i;
5176         char *name;
5177
5178         SDL_assert(node >= 0);
5179         // this function doesn't mean anything when not in campaign mode
5180         if ( !(Game_mode & GM_CAMPAIGN_MODE) )
5181                 return;
5182
5183         while (node >= 0) {
5184                 name = CTEXT(node);
5185                 i = ship_info_lookup(name);
5186                 if (i >= 0)
5187                         Ship_info[i].flags |= SIF_IN_TECH_DATABASE;
5188                 else
5189                         Error(LOCATION, "Ship class \"%s\" invalid", name);
5190
5191                 node = CDR(node);
5192         }
5193 }
5194
5195 void sexp_tech_add_weapon(int node)
5196 {
5197         int i;
5198         char *name;
5199
5200         SDL_assert(node >= 0);
5201         // this function doesn't mean anything when not in campaign mode
5202         if ( !(Game_mode & GM_CAMPAIGN_MODE) )
5203                 return;
5204
5205         while (node >= 0) {
5206                 name = CTEXT(node);
5207                 i = weapon_info_lookup(name);
5208                 if (i >= 0)
5209                         Weapon_info[i].wi_flags |= WIF_IN_TECH_DATABASE;
5210                 else
5211                         Error(LOCATION, "Ship class \"%s\" invalid", name);
5212
5213                 node = CDR(node);
5214         }
5215 }
5216
5217 // function to set variables needed to grant a new ship/weapon to the player during the course
5218 // of a mission
5219 void sexp_allow_ship( int n )
5220 {
5221         int sindex;
5222         char *name;
5223
5224         // this function doesn't mean anything when not in campaign mode
5225         if ( !(Game_mode & GM_CAMPAIGN_MODE) )
5226                 return;
5227
5228         // get the name of the ship and lookup up the ship_info index for it
5229         name = CTEXT(n);
5230         sindex = ship_info_lookup( name );
5231         if ( sindex == -1 )
5232                 return;
5233
5234         // now we have a valid index --
5235         mission_campaign_save_persistent( CAMPAIGN_PERSISTENT_SHIP, sindex );
5236 }
5237
5238 void sexp_allow_weapon( int n )
5239 {
5240         int sindex;
5241         char *name;
5242
5243         // this function doesn't mean anything when not in campaign mode
5244         if ( !(Game_mode & GM_CAMPAIGN_MODE) )
5245                 return;
5246
5247         // get the name of the weapon and lookup up the weapon_info index for it
5248         name = CTEXT(n);
5249         sindex = weapon_info_lookup( name );
5250         if ( sindex == -1 )
5251                 return;
5252
5253         // now we have a valid index --
5254         mission_campaign_save_persistent( CAMPAIGN_PERSISTENT_WEAPON, sindex );
5255 }
5256
5257 // functions to deal with breaking/fixing the warp engines on ships/wings.  should_break is true when
5258 // we are breaking the warp drive (can be repaired). The parameter is 0 when is getting broken (i.e.
5259 // can be fixed by repair).  The repair parameter tells us whether we are clearing the destroyed or broken
5260 // flag (1), or setting them (0).
5261 void sexp_deal_with_warp( int n, int should_break, int nix )
5262 {
5263         int index, flag;
5264         char *name;
5265
5266         for ( ; n != -1; n = CDR(n) ) {
5267                 name = CTEXT(n);
5268                 index = ship_name_lookup(name);
5269
5270                 // check to see if ship destroyed or departed.  In either case, do nothing.
5271                 if ( mission_log_get_time(LOG_SHIP_DEPART, name, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, name, NULL, NULL) )
5272                         continue;
5273
5274                 // we can only operate on ships which are in the mission
5275                 if ( index != -1 ) {
5276
5277                         // set the flag value accoring to whether we are destroying the warp or just breaking it
5278                         if ( should_break )
5279                                 flag = SF_WARP_BROKEN;
5280                         else
5281                                 flag = SF_WARP_NEVER;
5282
5283                         if ( nix )
5284                                 Ships[index].flags |= flag;
5285                         else
5286                                 Ships[index].flags &= ~flag;
5287
5288                 } else {
5289                         // maybe this ship has yet to arrive.  Get a possible parse object and set the flag if found
5290                         p_object *pobjp;
5291
5292                         pobjp = mission_parse_get_arrival_ship( name );
5293 #ifndef NDEBUG
5294                         if ( pobjp == NULL ) {
5295                                 Int3();                         // warning, find allender -- should be impossible
5296                                 continue;
5297                         }
5298 #endif
5299                         if ( should_break )
5300                                 flag = P_SF_WARP_BROKEN;
5301                         else
5302                                 flag = P_SF_WARP_BROKEN;
5303
5304                         if ( nix )
5305                                 pobjp->flags |= flag;
5306                         else
5307                                 pobjp->flags &= ~flag;
5308
5309                 }
5310         }
5311 }
5312
5313 // function which is used to tell the AI when it is okay to fire certain secondary
5314 // weapons at other ships.
5315 void sexp_good_secondary_time( int n )
5316 {
5317         char *team_name, *weapon_name, *ship_name;
5318         int num_weapons, weapon_index, team, i;
5319
5320         team_name = CTEXT(n);
5321         num_weapons = atoi(CTEXT(CDR(n)));
5322         weapon_name = CTEXT(CDR(CDR(n)));
5323         ship_name = CTEXT(CDR(CDR(CDR(n))));
5324
5325         weapon_index = weapon_info_lookup(weapon_name);
5326         if ( weapon_index == -1 ) {
5327                 nprintf(("Warning", "couldn't find weapon %s for good-secondary-time\n", weapon_name));
5328                 return;
5329         }
5330
5331         // get the team type from the team_name
5332         for ( i = 0; i < Num_team_names; i++ ) {
5333                 if ( !SDL_strcasecmp(Team_names[i], team_name) )
5334                         break;
5335         }
5336         if ( i == Num_team_names ) {
5337                 nprintf(("Warning", "couldn't find team %s for good-secondary-time\n", team_name ));
5338                 return;
5339         }
5340         team = (1<<i);                  // this is the magic formula to get to a team type.
5341
5342         // see if the ship has departed or has been destroyed.  If so, then we don't need to set up the
5343         // AI stuff
5344         if ( mission_log_get_time(LOG_SHIP_DEPART, ship_name, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) )
5345                 return;
5346
5347         ai_good_secondary_time( team, weapon_index, num_weapons, ship_name );
5348 }
5349
5350
5351 // function to deal with getting status of goals for previous missions (in the current campaign).
5352 // the status parameter is used to tell this function if we are looking for a goal_satisfied, goal_failed,
5353 // or goal incomplete event
5354 int sexp_previous_goal_status( int n, int status )
5355 {
5356         char rval = 0, *mission_name;
5357         char *goal_name;
5358         int i, mission_num, default_value = 0, use_defaults = 1;
5359
5360         mission_name = CTEXT(n);
5361         goal_name = CTEXT(CDR(n));
5362
5363         // check for possible next optional argument
5364         n = CDR(CDR(n));
5365         if ( n != -1 ) {
5366                 default_value = eval_sexp(n);
5367         }
5368
5369         // try to find the given mission name in the current list of missions in the campaign.
5370         if ( Game_mode & GM_CAMPAIGN_MODE ) {
5371                 i = mission_campaign_find_mission( mission_name );
5372
5373                 if ( i == -1 ) {
5374                         // if mission not found, assume that goal was false (so previous-goal-false returns true)
5375                         nprintf(("General", "Couldn't find mission name %s in current campaign's list of missions.\nReturning %s for goal-status function.", mission_name, (status==GOAL_COMPLETE)?"false":"true"));
5376                         if ( status == GOAL_COMPLETE )
5377                                 rval = SEXP_KNOWN_FALSE;
5378                         else
5379                                 rval = SEXP_KNOWN_TRUE;
5380
5381                         use_defaults = 0;
5382                 } else if (Campaign.missions[i].flags & CMISSION_FLAG_SKIPPED) {
5383                         use_defaults = 1;
5384                 } else {
5385                         // now try and find the goal this mission
5386                         mission_num = i;
5387                         for (i = 0; i < Campaign.missions[mission_num].num_goals; i++) {
5388                                 if ( !SDL_strcasecmp(Campaign.missions[mission_num].goals[i].name, goal_name) )
5389                                         break;
5390                         }
5391
5392                         if ( i == Campaign.missions[mission_num].num_goals ) {
5393                                 Warning(LOCATION, "Couldn't find goal name %s in mission %s.\nReturning %s for goal-true function.", goal_name, mission_name, (status==GOAL_COMPLETE)?"false":"true");
5394                                 if ( status == GOAL_COMPLETE )
5395                                         rval = SEXP_KNOWN_FALSE;
5396                                 else
5397                                         rval = SEXP_KNOWN_TRUE;
5398
5399                         } else {
5400                                 // now return KNOWN_TRUE or KNOWN_FALSE based on the status field in the goal structure
5401                                 if ( Campaign.missions[mission_num].goals[i].status == status )
5402                                         rval = SEXP_KNOWN_TRUE;
5403                                 else
5404                                         rval = SEXP_KNOWN_FALSE;
5405                         }
5406
5407                         use_defaults = 0;
5408                 }
5409         }
5410
5411         if (use_defaults) {
5412                 // when not in campaign mode, always return KNOWN_TRUE when looking for goal complete, and KNOWN_FALSE
5413                 // otherwise
5414                 if ( n != -1 ) {
5415                         if ( default_value )
5416                                 rval = SEXP_KNOWN_TRUE;
5417                         else
5418                                 rval = SEXP_KNOWN_FALSE;
5419                 } else {
5420                         if ( status == GOAL_COMPLETE )
5421                                 rval = SEXP_KNOWN_TRUE;
5422                         else
5423                                 rval = SEXP_KNOWN_FALSE;
5424                 }
5425         }
5426
5427         return rval;
5428 }
5429
5430 // sexpression which gets the status of an event from a previous mission.  Like the above function but
5431 // dealing with events instead of goals.  Again, the status parameter tells the code if we are looking
5432 // for an event_true, event_false, or event_incomplete status
5433 int sexp_previous_event_status( int n, int status )
5434 {
5435         char rval = 0, *mission_name;
5436         char *name;
5437         int i, mission_num, default_value = 0, use_defaults = 1;
5438
5439         mission_name = CTEXT(n);
5440         name = CTEXT(CDR(n));
5441
5442         // check for possible optional parameter
5443         n = CDR(CDR(n));
5444         if ( n != -1 ){
5445                 default_value = eval_sexp(n);
5446         }
5447
5448         if ( Game_mode & GM_CAMPAIGN_MODE ) {
5449                 // following function returns -1 when mission isn't found.
5450                 i = mission_campaign_find_mission( mission_name );
5451
5452                 // if the mission name wasn't found -- make this return FALSE for the event status.
5453                 if ( i == -1 ) {
5454                         nprintf(("General", "Couldn't find mission name %s in current campaign's list of missions.\nReturning %s for event-status function.", mission_name, (status==EVENT_SATISFIED)?"false":"true"));
5455                         if ( status == EVENT_SATISFIED ) {
5456                                 rval = SEXP_KNOWN_FALSE;
5457                         } else {
5458                                 rval = SEXP_KNOWN_TRUE;
5459                         }
5460
5461                         use_defaults = 0;
5462                 } else if (Campaign.missions[i].flags & CMISSION_FLAG_SKIPPED) {
5463                         use_defaults = 1;
5464                 } else {
5465                         // now try and find the goal this mission
5466                         mission_num = i;
5467                         for (i = 0; i < Campaign.missions[mission_num].num_events; i++) {
5468                                 if ( !SDL_strcasecmp(Campaign.missions[mission_num].events[i].name, name) )
5469                                         break;
5470                         }
5471
5472                         if ( i == Campaign.missions[mission_num].num_events ) {
5473                                 Warning(LOCATION, "Couldn't find event name %s in mission %s.\nReturning %s for event_status function.", name, mission_name, (status==EVENT_SATISFIED)?"false":"true");
5474                                 if ( status == EVENT_SATISFIED )
5475                                         rval = SEXP_KNOWN_FALSE;
5476                                 else
5477                                         rval = SEXP_KNOWN_TRUE;
5478
5479                         } else {
5480                                 // now return KNOWN_TRUE or KNOWN_FALSE based on the status field in the goal structure
5481                                 if ( Campaign.missions[mission_num].events[i].status == status )
5482                                         rval = SEXP_KNOWN_TRUE;
5483                                 else
5484                                         rval = SEXP_KNOWN_FALSE;
5485                         }
5486
5487                         use_defaults = 0;
5488                 }
5489         } 
5490
5491         if (use_defaults) {
5492                 if ( n != -1 ) {
5493                         if ( default_value )
5494                                 rval = SEXP_KNOWN_TRUE;
5495                         else
5496                                 rval = SEXP_KNOWN_FALSE;
5497                 } else {
5498                         if ( status == EVENT_SATISFIED )
5499                                 rval = SEXP_KNOWN_TRUE;
5500                         else
5501                                 rval = SEXP_KNOWN_FALSE;
5502                 }
5503         }
5504
5505         return rval;
5506 }
5507
5508 // function to return the status of an event in the current mission.  The passed parameter indicates
5509 // if we are checking whether the event is true or the event is false.
5510 int sexp_event_status( int n, int want_true )
5511 {
5512         char *name;
5513         int i, result;
5514
5515         name = CTEXT(n);
5516         for (i = 0; i < Num_mission_events; i++ ) {
5517                 // look for the event name, check it's status.  If formula is gone, we know the state won't ever change.
5518                 if ( !SDL_strcasecmp(Mission_events[i].name, name) ) {
5519                         result = Mission_events[i].result;
5520                         if (Mission_events[i].formula < 0) {
5521                                 if ( (want_true && result) || (!want_true && !result) )
5522                                         return SEXP_KNOWN_TRUE;
5523                                 else
5524                                         return SEXP_KNOWN_FALSE;
5525
5526                         } else {
5527                                 if ( (want_true && result) || (!want_true && !result) )
5528                                         return SEXP_TRUE;
5529                                 else
5530                                         return SEXP_FALSE;
5531                         }
5532                 }
5533         }
5534
5535         return 0;
5536 }
5537
5538 // function to return the status of an event N seconds after the event is true or false.  Similar
5539 // to above function but waits N seconds before returning true
5540 int sexp_event_delay_status( int n, int want_true )
5541 {
5542         char *name;
5543         int i, result;
5544         fix delay;
5545
5546         name = CTEXT(n);
5547         delay = i2f(num_eval(CDR(n)));
5548         for (i = 0; i < Num_mission_events; i++ ) {
5549                 // look for the event name, check it's status.  If formula is gone, we know the state won't ever change.
5550                 if ( !SDL_strcasecmp(Mission_events[i].name, name) ) {
5551                         if ( (fix) Mission_events[i].timestamp + delay >= Missiontime )
5552                                 return SEXP_FALSE;
5553
5554                         result = Mission_events[i].result;
5555                         if (Mission_events[i].formula < 0) {
5556                                 if ( (want_true && result) || (!want_true && !result) )
5557                                         return SEXP_KNOWN_TRUE;
5558                                 else
5559                                         return SEXP_KNOWN_FALSE;
5560
5561                         } else {
5562                                 if ( want_true && result )  //) || (!want_true && !result) )
5563                                         return SEXP_TRUE;
5564                                 else
5565                                         return SEXP_FALSE;
5566                         }
5567                 }
5568         }
5569
5570         return 0;
5571 }
5572
5573 // function which returns true if the given event is still incomplete
5574 int sexp_event_incomplete( int n )
5575 {
5576         char *name;
5577         int i;
5578
5579         name = CTEXT(n);
5580
5581         for (i = 0; i < Num_mission_events; i++ ) {
5582                 if ( !SDL_strcasecmp(Mission_events[i].name, name ) ) {
5583                         // if the formula is still >= 0 (meaning it is still getting eval'ed), then
5584                         // the event is incomplete
5585                         if ( Mission_events[i].formula != -1 )
5586                                 return SEXP_TRUE;
5587                         else
5588                                 return SEXP_KNOWN_FALSE;
5589                 }
5590         }
5591
5592         return 0;
5593 }
5594
5595 // function to return the status of an goal N seconds after the goal is true or false.  Similar
5596 // to above function but operates on goals instead of events
5597 int sexp_goal_delay_status( int n, int want_true )
5598 {
5599         char *name;
5600         fix delay, time;
5601
5602         name = CTEXT(n);
5603         delay = i2f(num_eval(CDR(n)));
5604         
5605         if ( want_true ) {
5606                 // if we are looking for a goal true entry and we find a false, then return known false here
5607                 if ( mission_log_get_time(LOG_GOAL_FAILED, name, NULL, NULL) )
5608                         return SEXP_KNOWN_FALSE;
5609                 else if ( mission_log_get_time(LOG_GOAL_SATISFIED, name, NULL, &time) ) {
5610                         if ( (Missiontime - time) >= delay )
5611                                 return SEXP_KNOWN_TRUE;
5612                 }
5613         } else {
5614                 // if we are looking for a goal false entry and we find a true, then return known false here
5615                 if ( mission_log_get_time(LOG_GOAL_SATISFIED, name, NULL, NULL) )
5616                         return SEXP_KNOWN_FALSE;
5617                 else if ( mission_log_get_time(LOG_GOAL_FAILED, name, NULL, &time) ) {
5618                         if ( (Missiontime - time) >= delay )
5619                                 return SEXP_KNOWN_TRUE;
5620                 }
5621         }
5622
5623         return 0;
5624 }
5625
5626 // function which returns true if the given goal is still incomplete
5627 int sexp_goal_incomplete( int n )
5628 {
5629         char *name;
5630
5631         name = CTEXT(n);
5632
5633         if ( mission_log_get_time( LOG_GOAL_SATISFIED, name, NULL, NULL) || mission_log_get_time( LOG_GOAL_FAILED, name, NULL, NULL) )
5634                 return SEXP_KNOWN_FALSE;
5635         else
5636                 return SEXP_TRUE;
5637 }
5638
5639
5640 // protects/unprotects a ship.  The flag tells us whether or not the protect bit should be set (flag==1)
5641 // or cleared (flag==0)
5642 void sexp_protect_ships( int n, int flag )
5643 {
5644         char *ship_name;
5645         int num;
5646
5647         while ( n != -1 ) {
5648                 ship_name = CTEXT(n);
5649
5650                 // check to see if ship destroyed or departed.  In either case, do nothing.
5651                 if ( mission_log_get_time(LOG_SHIP_DEPART, ship_name, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) )
5652                         return;
5653
5654                 // get the ship num.  If we get a -1 for the number here, ship has yet to arrive.  Store this ship
5655                 // in a list until created
5656                 num = ship_name_lookup(ship_name);
5657                 if ( num >= 0 ) {
5658                         if ( flag )
5659                                 Objects[Ships[num].objnum].flags |= OF_PROTECTED;
5660                         else
5661                                 Objects[Ships[num].objnum].flags &= ~OF_PROTECTED;
5662                 } else {
5663                         p_object *parse_obj;
5664
5665                         parse_obj = mission_parse_get_arrival_ship( ship_name );
5666                         if ( parse_obj ) {
5667                                 if ( flag )
5668                                         parse_obj->flags |= P_OF_PROTECTED;
5669                                 else
5670                                         parse_obj->flags &= ~P_OF_PROTECTED;
5671                                 break;
5672         #ifndef NDEBUG
5673                         } else {
5674                                 Int3(); // get allender -- could be a potential problem here
5675         #endif
5676                         }
5677                 }
5678
5679                 n = CDR(n);
5680         }
5681 }
5682
5683 // protects/unprotects a ship.  The flag tells us whether or not the protect bit should be set (flag==1)
5684 // or cleared (flag==0)
5685 void sexp_beam_protect_ships( int n, int flag )
5686 {
5687         char *ship_name;
5688         int num;
5689
5690         while ( n != -1 ) {
5691                 ship_name = CTEXT(n);
5692
5693                 // check to see if ship destroyed or departed.  In either case, do nothing.
5694                 if ( mission_log_get_time(LOG_SHIP_DEPART, ship_name, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) )
5695                         return;
5696
5697                 // get the ship num.  If we get a -1 for the number here, ship has yet to arrive.  Store this ship
5698                 // in a list until created
5699                 num = ship_name_lookup(ship_name);
5700                 if ( num >= 0 ) {
5701                         if ( flag )
5702                                 Objects[Ships[num].objnum].flags |= OF_BEAM_PROTECTED;
5703                         else
5704                                 Objects[Ships[num].objnum].flags &= ~OF_BEAM_PROTECTED;
5705                 } else {
5706                         p_object *parse_obj;
5707
5708                         parse_obj = mission_parse_get_arrival_ship( ship_name );
5709                         if ( parse_obj ) {
5710                                 if ( flag )
5711                                         parse_obj->flags |= P_OF_BEAM_PROTECTED;
5712                                 else
5713                                         parse_obj->flags &= ~P_OF_BEAM_PROTECTED;
5714                                 break;
5715         #ifndef NDEBUG
5716                         } else {
5717                                 Int3(); // get allender -- could be a potential problem here
5718         #endif
5719                         }
5720                 }
5721
5722                 n = CDR(n);
5723         }
5724 }
5725
5726 // sexpression to make ships "visible" and "invisible" to sensors.  The visible parameter is true
5727 // when making ships visible, false otherwise
5728 void sexp_ships_visible( int n, int visible )
5729 {
5730         char *ship_name;
5731         int num;
5732
5733         for ( ; n != -1; n = CDR(n) ) {
5734                 ship_name = CTEXT(n);
5735
5736                 // check to see if ship destroyed or departed.  In either case, do nothing.
5737                 if ( mission_log_get_time(LOG_SHIP_DEPART, ship_name, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) )
5738                         continue;;
5739
5740                 // get the ship num.  If we get a -1 for the number here, ship has yet to arrive.  Store this ship
5741                 // in a list until created
5742                 num = ship_name_lookup(ship_name);
5743                 if ( num != -1 ) {
5744                         if ( !visible ) {
5745                                 Ships[num].flags |= SF_HIDDEN_FROM_SENSORS;
5746                         } else {
5747                                 Ships[num].flags &= ~SF_HIDDEN_FROM_SENSORS;
5748                                 if (Ships[num].flags & SF_ESCORT) {
5749                                         // SEND add escort request
5750                                         hud_add_ship_to_escort(Ships[num].objnum, 1);
5751                                 }
5752                         }
5753
5754                 } else {
5755                         p_object *parse_obj;
5756
5757                         parse_obj = mission_parse_get_arrival_ship( ship_name );
5758                         if ( parse_obj ) {
5759                                 if ( !visible )
5760                                         parse_obj->flags |= P_SF_HIDDEN_FROM_SENSORS;
5761                                 else
5762                                         parse_obj->flags &= ~P_SF_HIDDEN_FROM_SENSORS;
5763                                 break;
5764         #ifndef NDEBUG
5765                         } else {
5766                                 Int3(); // get allender -- could be a potential problem here
5767         #endif
5768                         }
5769                 }
5770         }
5771 }
5772
5773 // sexpression to toggle invulnerability flag of ships.
5774 void sexp_ships_invulnerable( int n, int invulnerable )
5775 {
5776         char *ship_name;
5777         object *objp;
5778         int num;
5779
5780         for ( ; n != -1; n = CDR(n) ) {
5781                 ship_name = CTEXT(n);
5782
5783                 // check to see if ship destroyed or departed.  In either case, do nothing.
5784                 if ( mission_log_get_time(LOG_SHIP_DEPART, ship_name, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) )
5785                         continue;
5786
5787                 // get the ship num.  If we get a -1 for the number here, ship has yet to arrive.  Store this ship
5788                 // in a list until created
5789                 num = ship_name_lookup(ship_name);
5790                 if ( num != -1 ) {
5791                         objp = &Objects[Ships[num].objnum];
5792                         if ( invulnerable )
5793                                 objp->flags |= OF_INVULNERABLE;
5794                         else
5795                                 objp->flags &= ~OF_INVULNERABLE;
5796                 } else {
5797                         p_object *parse_obj;
5798
5799                         parse_obj = mission_parse_get_arrival_ship( ship_name );
5800                         if ( parse_obj ) {
5801                                 if ( invulnerable )
5802                                         parse_obj->flags |= P_SF_INVULNERABLE;
5803                                 else
5804                                         parse_obj->flags &= ~P_SF_INVULNERABLE;
5805                                 break;
5806         #ifndef NDEBUG
5807                         } else {
5808                                 Int3(); // get allender -- could be a potential problem here
5809         #endif
5810                         }
5811                 }
5812         }
5813 }
5814
5815 // sexpression to toggle KEEP ALIVE flag of ship object
5816 void sexp_ships_guardian( int n, int guardian )
5817 {
5818         char *ship_name;
5819         object *objp;
5820         int num;
5821
5822         for ( ; n != -1; n = CDR(n) ) {
5823                 ship_name = CTEXT(n);
5824
5825                 // check to see if ship destroyed or departed.  In either case, do nothing.
5826                 if ( mission_log_get_time(LOG_SHIP_DEPART, ship_name, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) )
5827                         continue;
5828
5829                 // get the ship num.  If we get a -1 for the number here, ship has yet to arrive.  Store this ship
5830                 // in a list until created
5831                 num = ship_name_lookup(ship_name);
5832                 if ( num != -1 ) {
5833                         objp = &Objects[Ships[num].objnum];
5834                         if ( guardian )
5835                                 objp->flags |= OF_GUARDIAN;
5836                         else
5837                                 objp->flags &= ~OF_GUARDIAN;
5838                 } else {
5839                         p_object *parse_obj;
5840
5841                         parse_obj = mission_parse_get_arrival_ship( ship_name );
5842                         if ( parse_obj ) {
5843                                 if ( guardian )
5844                                         parse_obj->flags |= P_SF_GUARDIAN;
5845                                 else
5846                                         parse_obj->flags &= ~P_SF_GUARDIAN;
5847                                 break;
5848         #ifndef NDEBUG
5849                         } else {
5850                                 Int3(); // get allender -- could be a potential problem here
5851         #endif
5852                         }
5853                 }
5854         }
5855 }
5856
5857 // make ship vanish without a trace (and what its docked to)
5858 void ship_vanished(int);
5859 void sexp_ship_vanish( int n )
5860 {
5861         char *ship_name;
5862         object *objp, *docked_objp;
5863         int num;
5864
5865         // if MULTIPLAYER bail
5866         if (Game_mode & GM_MULTIPLAYER) {
5867                 return;
5868         }
5869
5870         for ( ; n != -1; n = CDR(n) ) {
5871                 ship_name = CTEXT(n);
5872
5873                 // check to see if ship destroyed or departed.  In either case, do nothing.
5874                 if ( mission_log_get_time(LOG_SHIP_DEPART, ship_name, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) )
5875                         continue;
5876
5877                 // get the ship num.  If we get a -1 for the number here, ship has yet to arrive.  Store this ship
5878                 // in a list until created
5879                 num = ship_name_lookup(ship_name);
5880                 if ( num != -1 ) {
5881                         objp = &Objects[Ships[num].objnum];
5882
5883                         // check if docked
5884                         docked_objp = ai_find_docked_object( objp );
5885
5886                         // kill
5887                         objp->flags |= OF_SHOULD_BE_DEAD;
5888
5889                         // make vanish
5890                         ship_vanished(num);
5891
5892                         if (docked_objp) {
5893                                 // kill
5894                                 docked_objp->flags |= OF_SHOULD_BE_DEAD;
5895
5896                                 // vanish
5897                                 ship_vanished(docked_objp->instance);
5898                         }
5899                 }
5900         }
5901 }
5902
5903 int sexp_key_pressed(int node)
5904 {
5905         int z, t;
5906
5907         SDL_assert(node != -1);
5908         z = translate_key_to_index(CTEXT(node));
5909         if (z < 0){
5910                 return 0;
5911         }
5912
5913         if (!Control_config[z].used){
5914                 return 0;
5915         }
5916
5917         if (CDR(node) < 0){
5918                 return 1;
5919         }
5920
5921         t = atoi(CTEXT(CDR(node)));
5922         return timestamp_has_time_elapsed(Control_config[z].used, t * 1000);
5923 }
5924
5925 void sexp_key_reset(int node)
5926 {
5927         int z;
5928
5929         SDL_assert(node != -1);
5930         z = translate_key_to_index(CTEXT(node));
5931         if (z >= 0)
5932                 Control_config[z].used = 0;
5933 }
5934
5935 int sexp_targeted(int node)
5936 {
5937         int z;
5938         ship_subsys *ptr;
5939
5940         z = ship_query_state(CTEXT(node));
5941         if (z == 1){
5942                 return SEXP_KNOWN_FALSE;  // ship isn't around, nor will it ever be
5943         } else if (z == -1) {
5944                 return SEXP_CANT_EVAL;
5945         }
5946
5947         z = ship_name_lookup(CTEXT(node), 1);
5948         if ((z < 0) || !Player_ai || (Ships[z].objnum != Player_ai->target_objnum)){
5949                 return 0;
5950         }
5951
5952         if (CDR(node) >= 0) {
5953                 z = atoi(CTEXT(CDR(node))) * 1000;
5954                 if (!timestamp_has_time_elapsed(Players_target_timestamp, z)){
5955                         return 0;
5956                 }
5957
5958                 if (CDR(CDR(node)) >= 0) {
5959                         ptr = Player_ai->targeted_subsys;
5960                         if (!ptr || SDL_strcasecmp(ptr->system_info->subobj_name, CTEXT(CDR(CDR(node))))){
5961                                 return 0;
5962                         }
5963                 }
5964         }
5965
5966         return 1;
5967 }
5968
5969 int sexp_speed(int node)
5970 {
5971         if (Training_context & TRAINING_CONTEXT_SPEED) {
5972                 if (Training_context_speed_set){
5973                         if (timestamp_has_time_elapsed(Training_context_speed_timestamp, atoi(CTEXT(node)) * 1000)){
5974                                 return SEXP_KNOWN_TRUE;
5975                         }
5976                 }
5977         }
5978
5979         return 0;
5980 }
5981
5982 int sexp_secondaries_depleted(int node)
5983 {
5984         int sindex, num_banks, num_depleted_banks;
5985         ship *shipp;
5986
5987         // get ship
5988         sindex = ship_name_lookup(CTEXT(node));
5989         if (sindex < 0) {
5990                 return 0;
5991         }
5992
5993         shipp = &Ships[sindex];
5994         if (shipp->objnum < 0) {
5995                 return 0;
5996         }
5997
5998         // get num secondary banks
5999         num_banks = shipp->weapons.num_secondary_banks;
6000         num_depleted_banks = 0;
6001
6002         // get number of depleted banks
6003         for (int idx=0; idx<num_banks; idx++) {
6004                 if (shipp->weapons.secondary_bank_ammo[idx] == 0) {
6005                         num_depleted_banks++;
6006                 }
6007         }
6008
6009         // are they all depleted?
6010         return (num_depleted_banks == num_banks);
6011 }
6012
6013 int sexp_facing(int node)
6014 {
6015         int obj, sh;
6016         float a1, a2;
6017         vector v1, v2;
6018
6019         if (ship_query_state(CTEXT(node)) < 0){
6020                 return SEXP_KNOWN_FALSE;
6021         }
6022
6023         sh = ship_name_lookup(CTEXT(node));
6024         if ((sh < 0) || !Player_obj){
6025                 return 0;
6026         }
6027
6028         obj = Ships[sh].objnum;
6029         v1 = Player_obj->orient.v.fvec;
6030         vm_vec_normalize(&v1);
6031         vm_vec_sub(&v2, &Objects[obj].pos, &Player_obj->pos);
6032         vm_vec_normalize(&v2);
6033         a1 = vm_vec_dotprod(&v1, &v2);
6034         a2 = (float) cos(ANG_TO_RAD(atof(CTEXT(CDR(node)))));
6035         if (a1 >= a2){
6036                 return 1;
6037         }
6038
6039         return 0;
6040 }
6041
6042 // is ship facing first waypoint in waypoint path
6043 int sexp_facing2(int node)
6044 {
6045         int i;
6046         float a1, a2;
6047         vector v1, v2;
6048
6049         // bail if Player_obj is not good
6050         if (!Player_obj) {
6051                 return SEXP_CANT_EVAL;
6052         }
6053
6054         // get player fvec
6055         v1 = Player_obj->orient.v.fvec;
6056         vm_vec_normalize(&v1);
6057
6058         // get waypoint name
6059         char *waypoint_name = CTEXT(node);
6060
6061         // get position of first waypoint
6062         int wp_index = -1;
6063         for (i=0; i<Num_waypoint_lists; i++) {
6064                 if (!SDL_strcasecmp(waypoint_name, Waypoint_lists[i].name)) {
6065                         wp_index = i;
6066                         break;
6067                 }
6068         }
6069
6070         if (wp_index == -1) {
6071                 return SEXP_CANT_EVAL;
6072         }
6073
6074         // Waypoint_lists[wp_index].waypoints[0]
6075
6076         vm_vec_sub(&v2, &Waypoint_lists[wp_index].waypoints[0], &Player_obj->pos);
6077         vm_vec_normalize(&v2);
6078         a1 = vm_vec_dotprod(&v1, &v2);
6079         a2 = (float) cos(ANG_TO_RAD(atof(CTEXT(CDR(node)))));
6080         if (a1 >= a2){
6081                 return 1;
6082         }
6083
6084         return 0;
6085 }
6086
6087 int sexp_order(int node)
6088 {
6089         return 0;
6090 }
6091
6092 int sexp_waypoint_missed()
6093 {
6094         if (Training_context & TRAINING_CONTEXT_FLY_PATH) {
6095                 if (Training_context_at_waypoint > Training_context_goal_waypoint){
6096                         return 1;
6097                 }
6098         }
6099
6100         return 0;
6101 }
6102
6103 int sexp_waypoint_twice()
6104 {
6105         if (Training_context & TRAINING_CONTEXT_FLY_PATH) {
6106                 if (Training_context_at_waypoint < Training_context_goal_waypoint - 1){
6107                         return 1;
6108                 }
6109         }
6110
6111         return 0;
6112 }
6113
6114 int sexp_path_flown()
6115 {
6116         if (Training_context & TRAINING_CONTEXT_FLY_PATH) {
6117                 if (Training_context_goal_waypoint == Waypoint_lists[Training_context_path].count){
6118                         return 1;
6119                 }
6120         }
6121
6122         return 0;
6123 }
6124
6125 void sexp_send_training_message(int node)
6126 {
6127         int t = -1, delay = 0;
6128
6129         if(physics_paused){
6130                 return;
6131         }
6132
6133         SDL_assert(node >= 0);
6134         SDL_assert(Event_index >= 0);
6135
6136         if ((CDR(node) >= 0) && (CDR(CDR(node)) >= 0)) {
6137                 delay = atoi(CTEXT(CDR(CDR(node)))) * 1000;
6138                 t = CDR(CDR(CDR(node)));
6139                 if (t >= 0){
6140                         t = atoi(CTEXT(t));
6141                 }
6142         }
6143
6144         if ((Mission_events[Event_index].repeat_count > 1) || (CDR(node) < 0)){
6145                 message_training_que(CTEXT(node), timestamp(delay), t);
6146         } else {
6147                 message_training_que(CTEXT(CDR(node)), timestamp(delay), t);
6148         }
6149
6150 //      if (Training_msg_method)
6151 //              gameseq_post_event(GS_EVENT_TRAINING_PAUSE);
6152 }
6153
6154 int sexp_shield_recharge_pct(int node)
6155 {
6156         int sindex;
6157
6158         // get the firing ship
6159         sindex = ship_name_lookup(CTEXT(node));
6160         if(sindex < 0){
6161                 return 0;
6162         }
6163         if(Ships[sindex].objnum < 0){
6164                 return 0;
6165         }
6166
6167         // shield recharge pct
6168         return (int)(100.0f * Energy_levels[Ships[sindex].shield_recharge_index]);
6169 }
6170
6171 int sexp_engine_recharge_pct(int node)
6172 {
6173         int sindex;
6174
6175         // get the firing ship
6176         sindex = ship_name_lookup(CTEXT(node));
6177         if(sindex < 0){
6178                 return 0;
6179         }
6180         if(Ships[sindex].objnum < 0){
6181                 return 0;
6182         }
6183
6184         // shield recharge pct
6185         return (int)(100.0f * Energy_levels[Ships[sindex].engine_recharge_index]);
6186 }
6187
6188 int sexp_weapon_recharge_pct(int node)
6189 {
6190         int sindex;
6191
6192         // get the firing ship
6193         sindex = ship_name_lookup(CTEXT(node));
6194         if(sindex < 0){
6195                 return 0;
6196         }
6197         if(Ships[sindex].objnum < 0){
6198                 return 0;
6199         }
6200
6201         // shield recharge pct
6202         return (int)(100.0f * Energy_levels[Ships[sindex].weapon_recharge_index]);
6203 }
6204
6205 int sexp_shield_quad_low(int node)
6206 {
6207         int sindex, idx;        
6208         float max_quad, check;
6209         ship_info *sip;
6210         object *objp;
6211
6212         // get the ship
6213         sindex = ship_name_lookup(CTEXT(node));
6214         if(sindex < 0){
6215                 return 0;
6216         }
6217         if((Ships[sindex].objnum < 0) || (Ships[sindex].objnum >= MAX_OBJECTS)){
6218                 return 0;
6219         }
6220         if((Ships[sindex].ship_info_index < 0) || (Ships[sindex].ship_info_index >= Num_ship_types)){
6221                 return 0;
6222         }
6223         objp = &Objects[Ships[sindex].objnum];
6224         sip = &Ship_info[Ships[sindex].ship_info_index];
6225         if(!(sip->flags & SIF_SMALL_SHIP)){
6226                 return 0;
6227         }
6228         max_quad = sip->shields / (float)MAX_SHIELD_SECTIONS;   
6229
6230         // shield pct
6231         check = (float)atoi(CTEXT(CDR(node)));
6232
6233         // check his quadrants
6234         for(idx=0; idx<MAX_SHIELD_SECTIONS; idx++){
6235                 if( ((objp->shields[idx] / max_quad) * 100.0f) <= check ){
6236                         return 1;
6237                 }
6238         }
6239
6240         // all good
6241         return 0;       
6242 }
6243
6244 int sexp_secondary_ammo_pct(int node)
6245 {
6246         ship *shipp;
6247         int sindex;
6248         int check, idx;
6249         int ret_sum[4];
6250         int ret = 0;
6251
6252         // get the ship
6253         sindex = ship_name_lookup(CTEXT(node));
6254         if(sindex < 0){
6255                 return 0;
6256         }
6257         if((Ships[sindex].objnum < 0) || (Ships[sindex].objnum >= MAX_OBJECTS)){
6258                 return 0;
6259         }
6260         shipp = &Ships[sindex];
6261         
6262         // bank to check
6263         check = atoi(CTEXT(CDR(node)));
6264
6265         // bogus check? (3 == cumulative sum of all banks)
6266         if((check != 3) && (check > shipp->weapons.num_secondary_banks)){
6267                 return 0;
6268         }
6269
6270         // cumulative sum?
6271         if(check == 3){
6272                 for(idx=0; idx<shipp->weapons.num_secondary_banks; idx++){
6273                         ret_sum[idx] = (int)(((float)shipp->weapons.secondary_bank_ammo[idx] / (float)shipp->weapons.secondary_bank_start_ammo[idx]) * 100.0f);
6274                 }
6275
6276                 // add it up
6277                 ret = 0;
6278                 for(idx=0; idx<shipp->weapons.num_secondary_banks; idx++){
6279                         ret += ret_sum[idx];
6280                 }
6281                 ret = (int)((float)ret / (float)shipp->weapons.num_secondary_banks);
6282         } else {
6283                 ret = (int)(((float)shipp->weapons.secondary_bank_ammo[check] / (float)shipp->weapons.secondary_bank_start_ammo[check]) * 100.0f);
6284         }       
6285
6286         // return
6287         return ret;
6288 }
6289
6290 void sexp_beam_fire(int node)
6291 {
6292         int sindex;
6293         beam_fire_info fire_info;               
6294         int idx;        
6295
6296         // zero stuff out
6297         memset(&fire_info, 0, sizeof(beam_fire_info));
6298         fire_info.accuracy = 0.000001f;                                                 // this will guarantee a hit
6299
6300         // get the firing ship
6301         sindex = ship_name_lookup(CTEXT(node));
6302         if(sindex < 0){
6303                 return;
6304         }
6305         if(Ships[sindex].objnum < 0){
6306                 return;
6307         }
6308         fire_info.shooter = &Objects[Ships[sindex].objnum];
6309
6310         // get the subsystem
6311         fire_info.turret = ship_get_subsys(&Ships[sindex], CTEXT(CDR(node)));
6312         if(fire_info.turret == NULL){
6313                 return;
6314         }
6315
6316         // get the target
6317         sindex = ship_name_lookup(CTEXT(CDR(CDR(node))));
6318         if(sindex < 0){
6319                 return;
6320         }
6321         if(Ships[sindex].objnum < 0){
6322                 return;
6323         }
6324         fire_info.target = &Objects[Ships[sindex].objnum];
6325
6326         // see if the optional subsystem can be found   
6327         fire_info.target_subsys = NULL;
6328         fire_info.target_subsys = ship_get_subsys(&Ships[sindex], CTEXT(CDR(CDR(CDR(node)))));  
6329
6330         // if it has no primary weapons
6331         if(fire_info.turret->weapons.num_primary_banks <= 0){
6332                 Int3();
6333                 return;
6334         }
6335
6336         // if the turret is destroyed
6337         if(fire_info.turret->current_hits <= 0.0f){
6338                 return;
6339         }
6340
6341         // hmm, this could be wacky. Let's just simply select the first beam weapon in the turret
6342         fire_info.beam_info_index = -1; 
6343         for(idx=0; idx<fire_info.turret->weapons.num_primary_banks; idx++){
6344                 // store the weapon info index
6345                 if(Weapon_info[fire_info.turret->weapons.primary_bank_weapons[idx]].wi_flags & WIF_BEAM){
6346                         fire_info.beam_info_index = fire_info.turret->weapons.primary_bank_weapons[idx];
6347                 }
6348         }
6349
6350         // fire the beam
6351         if(fire_info.beam_info_index != -1){            
6352                 beam_fire(&fire_info);
6353         } else {
6354                 // it would appear the turret doesn't have any beam weapons, dumbass
6355                 Int3();
6356         }
6357 }       
6358
6359 void sexp_beam_free(int node)
6360 {
6361         int sindex;
6362         ship_subsys *turret = NULL;     
6363
6364         // get the firing ship
6365         sindex = ship_name_lookup(CTEXT(node));
6366         if(sindex < 0){
6367                 return;
6368         }
6369         if(Ships[sindex].objnum < 0){
6370                 return;
6371         }
6372
6373         node = CDR(node);
6374         while(node != -1){
6375                 // get the subsystem
6376                 turret = ship_get_subsys(&Ships[sindex], CTEXT(node));
6377                 if(turret == NULL){
6378                         return;
6379                 }
6380
6381                 // flag it as beam free :)
6382                 turret->weapons.flags |= SW_FLAG_BEAM_FREE;
6383                 turret->turret_next_fire_stamp = timestamp((int) frand_range(50.0f, 4000.0f));
6384
6385                 // next
6386                 node = CDR(node);
6387         }
6388 }
6389
6390 void sexp_beam_free_all(int node)
6391 {
6392         ship_subsys *subsys;
6393         int sindex;
6394
6395         // get the firing ship
6396         sindex = ship_name_lookup(CTEXT(node));
6397         if(sindex < 0){
6398                 return;
6399         }
6400         if(Ships[sindex].objnum < 0){
6401                 return;
6402         }
6403
6404         // free all beam weapons
6405         subsys = GET_FIRST(&Ships[sindex].subsys_list);
6406         while(subsys != END_OF_LIST(&Ships[sindex].subsys_list)){
6407                 // just mark all turrets as beam free
6408                 if(subsys->system_info->type == SUBSYSTEM_TURRET){
6409                         subsys->weapons.flags |= SW_FLAG_BEAM_FREE;
6410                         subsys->turret_next_fire_stamp = timestamp((int) frand_range(50.0f, 4000.0f));
6411                 }
6412
6413                 // next item
6414                 subsys = GET_NEXT(subsys);
6415         }
6416 }
6417
6418 void sexp_beam_lock(int node)
6419 {       
6420         int sindex;
6421         ship_subsys *turret = NULL;     
6422
6423         // get the firing ship
6424         sindex = ship_name_lookup(CTEXT(node));
6425         if(sindex < 0){
6426                 return;
6427         }
6428         if(Ships[sindex].objnum < 0){
6429                 return;
6430         }
6431
6432         node = CDR(node);
6433         while(node != -1){
6434                 // get the subsystem
6435                 turret = ship_get_subsys(&Ships[sindex], CTEXT(node));
6436                 if(turret == NULL){
6437                         return;
6438                 }
6439
6440                 // flag it as not beam free
6441                 turret->weapons.flags &= ~(SW_FLAG_BEAM_FREE);
6442
6443                 // next
6444                 node = CDR(node);
6445         }
6446 }
6447
6448 void sexp_beam_lock_all(int node)
6449 {
6450         ship_subsys *subsys;
6451         int sindex;
6452
6453         // get the firing ship
6454         sindex = ship_name_lookup(CTEXT(node));
6455         if(sindex < 0){
6456                 return;
6457         }
6458         if(Ships[sindex].objnum < 0){
6459                 return;
6460         }
6461
6462         // free all beam weapons
6463         subsys = GET_FIRST(&Ships[sindex].subsys_list);
6464         while(subsys != END_OF_LIST(&Ships[sindex].subsys_list)){
6465                 // just mark all turrets as not beam free
6466                 if(subsys->system_info->type == SUBSYSTEM_TURRET){
6467                         subsys->weapons.flags &= ~(SW_FLAG_BEAM_FREE);
6468                 }
6469
6470                 // next item
6471                 subsys = GET_NEXT(subsys);
6472         }
6473 }
6474
6475 void sexp_turret_free(int node)
6476 {
6477         int sindex;
6478         ship_subsys *turret = NULL;     
6479
6480         // get the firing ship
6481         sindex = ship_name_lookup(CTEXT(node));
6482         if(sindex < 0){
6483                 return;
6484         }
6485         if(Ships[sindex].objnum < 0){
6486                 return;
6487         }
6488
6489         node = CDR(node);
6490         while(node != -1){
6491                 // get the subsystem
6492                 turret = ship_get_subsys(&Ships[sindex], CTEXT(node));
6493                 if(turret == NULL){
6494                         return;
6495                 }
6496
6497                 // flag turret as no longer locked :)
6498                 turret->weapons.flags &= (~SW_FLAG_TURRET_LOCK);
6499                 turret->turret_next_fire_stamp = timestamp((int) frand_range(50.0f, 4000.0f));
6500
6501                 // next
6502                 node = CDR(node);
6503         }
6504 }
6505
6506 void sexp_turret_free_all(int node)
6507 {
6508         ship_subsys *subsys;
6509         int sindex;
6510
6511         // get the firing ship
6512         sindex = ship_name_lookup(CTEXT(node));
6513         if(sindex < 0){
6514                 return;
6515         }
6516         if(Ships[sindex].objnum < 0){
6517                 return;
6518         }
6519
6520         // free all turrets
6521         subsys = GET_FIRST(&Ships[sindex].subsys_list);
6522         while(subsys != END_OF_LIST(&Ships[sindex].subsys_list)){
6523                 // just mark all turrets as free
6524                 if(subsys->system_info->type == SUBSYSTEM_TURRET){
6525                         subsys->weapons.flags &= (~SW_FLAG_TURRET_LOCK);
6526                         subsys->turret_next_fire_stamp = timestamp((int) frand_range(50.0f, 4000.0f));
6527                 }
6528
6529                 // next item
6530                 subsys = GET_NEXT(subsys);
6531         }
6532 }
6533
6534 void sexp_turret_lock(int node)
6535 {       
6536         int sindex;
6537         ship_subsys *turret = NULL;     
6538
6539         // get the firing ship
6540         sindex = ship_name_lookup(CTEXT(node));
6541         if(sindex < 0){
6542                 return;
6543         }
6544         if(Ships[sindex].objnum < 0){
6545                 return;
6546         }
6547
6548         node = CDR(node);
6549         while(node != -1){
6550                 // get the subsystem
6551                 turret = ship_get_subsys(&Ships[sindex], CTEXT(node));
6552                 if(turret == NULL){
6553                         return;
6554                 }
6555
6556                 // flag turret as locked
6557                 turret->weapons.flags |= SW_FLAG_TURRET_LOCK;
6558
6559                 // next
6560                 node = CDR(node);
6561         }
6562 }
6563
6564 void sexp_turret_lock_all(int node)
6565 {
6566         ship_subsys *subsys;
6567         int sindex;
6568
6569         // get the firing ship
6570         sindex = ship_name_lookup(CTEXT(node));
6571         if(sindex < 0){
6572                 return;
6573         }
6574         if(Ships[sindex].objnum < 0){
6575                 return;
6576         }
6577
6578         // lcck all turrets
6579         subsys = GET_FIRST(&Ships[sindex].subsys_list);
6580         while(subsys != END_OF_LIST(&Ships[sindex].subsys_list)){
6581                 // just mark all turrets as locked
6582                 if(subsys->system_info->type == SUBSYSTEM_TURRET){
6583                         subsys->weapons.flags |= SW_FLAG_TURRET_LOCK;
6584                 }
6585
6586                 // next item
6587                 subsys = GET_NEXT(subsys);
6588         }
6589 }
6590
6591 void sexp_turret_tagged_only_all(int node)
6592 {
6593         ship_subsys *subsys;
6594         int sindex;
6595
6596         // get the firing ship
6597         sindex = ship_name_lookup(CTEXT(node));
6598         if(sindex < 0){
6599                 return;
6600         }
6601         if(Ships[sindex].objnum < 0){
6602                 return;
6603         }
6604
6605         // mark all turrets to only target tagged ships
6606         subsys = GET_FIRST(&Ships[sindex].subsys_list);
6607         while(subsys != END_OF_LIST(&Ships[sindex].subsys_list)){
6608                 // just mark all turrets as locked
6609                 if(subsys->system_info->type == SUBSYSTEM_TURRET){
6610                         subsys->weapons.flags |= SW_FLAG_TAGGED_ONLY;
6611                 }
6612
6613                 // next item
6614                 subsys = GET_NEXT(subsys);
6615         }
6616 }
6617
6618 void sexp_turret_tagged_clear_all(int node)
6619 {
6620         ship_subsys *subsys;
6621         int sindex;
6622
6623         // get the firing ship
6624         sindex = ship_name_lookup(CTEXT(node));
6625         if(sindex < 0){
6626                 return;
6627         }
6628         if(Ships[sindex].objnum < 0){
6629                 return;
6630         }
6631
6632         // mark all turrets so not restricted to only tagged ships
6633         subsys = GET_FIRST(&Ships[sindex].subsys_list);
6634         while(subsys != END_OF_LIST(&Ships[sindex].subsys_list)){
6635                 // just mark all turrets as locked
6636                 if(subsys->system_info->type == SUBSYSTEM_TURRET){
6637                         subsys->weapons.flags &= (~SW_FLAG_TAGGED_ONLY);
6638                 }
6639
6640                 // next item
6641                 subsys = GET_NEXT(subsys);
6642         }
6643 }
6644
6645 void sexp_add_remove_escort(int node)
6646 {
6647         int sindex;
6648         int flag;
6649
6650         // get the firing ship
6651         sindex = ship_name_lookup(CTEXT(node));
6652         if(sindex < 0){
6653                 return;
6654         }
6655         if(Ships[sindex].objnum < 0){
6656                 return;
6657         }
6658
6659         // determine whether to add or remove it
6660         flag = atoi(CTEXT(CDR(node)));
6661
6662         // add/remove
6663         if(flag){       
6664                 hud_add_ship_to_escort(Ships[sindex].objnum, 1);
6665         } else {
6666                 hud_remove_ship_from_escort(Ships[sindex].objnum);
6667         }
6668 }
6669
6670 void sexp_awacs_set_radius(int node)
6671 {
6672         int sindex;             
6673         ship_subsys *awacs;
6674
6675         // get the firing ship
6676         sindex = ship_name_lookup(CTEXT(node));
6677         if(sindex < 0){
6678                 return;
6679         }
6680         if(Ships[sindex].objnum < 0){
6681                 return;
6682         }       
6683
6684         // get the awacs subsystem
6685         awacs = ship_get_subsys(&Ships[sindex], CTEXT(CDR(node)));
6686         if(awacs == NULL){
6687                 return;
6688         }
6689
6690         // make sure this _is_ an awacs subsystem
6691         SDL_assert(awacs->system_info->flags & MSS_FLAG_AWACS);
6692         if(awacs->system_info->flags & MSS_FLAG_AWACS){
6693                 return;
6694         }
6695
6696         // set the new awacs radius
6697         awacs->awacs_radius = (float)atoi(CTEXT(CDR(CDR(node))));       
6698 }
6699
6700 int sexp_is_tagged(int node)
6701 {
6702         int sindex;
6703
6704         // get the firing ship
6705         sindex = ship_name_lookup(CTEXT(node));
6706         if(sindex < 0){
6707                 return 0;
6708         }
6709         if(Ships[sindex].objnum < 0){
6710                 return 0;
6711         }
6712         if(Ships[sindex].tag_left > 0.0f){
6713                 return 1;
6714         }
6715
6716         // not tagged
6717         return 0;
6718 }
6719
6720 int sexp_num_kills(int node)
6721 {
6722         int sindex, np_index;
6723         player *p = NULL;
6724
6725         // get the ship we're interested in
6726         sindex = ship_name_lookup(CTEXT(node));
6727         if(sindex < 0){
6728                 return 0;
6729         }
6730         if(Ships[sindex].objnum < 0){
6731                 return 0;
6732         }
6733         
6734         // in multiplayer, search through all players
6735         if(Game_mode & GM_MULTIPLAYER){
6736                 // try and find the player
6737                 np_index = multi_find_player_by_object(&Objects[Ships[sindex].objnum]);
6738                 if((np_index >= 0) && (np_index < MAX_PLAYERS)){
6739                         p = Net_players[np_index].player;
6740                 }
6741         }
6742         // if we're in single player, we're only concerned with ourself
6743         else {
6744                 // me
6745                 if(Player_obj == &Objects[Ships[sindex].objnum]){
6746                         p = Player;
6747                 }
6748         }
6749
6750         // now, if we have a valid player, return his kills
6751         if(p != NULL){
6752                 return p->stats.m_kill_count_ok;
6753         }
6754
6755         // bad
6756         return 0;
6757 }
6758
6759 int sexp_num_type_kills(int node)
6760 {
6761         int sindex, np_index, st_index;
6762         int idx, total;
6763         player *p = NULL;
6764
6765         // get the ship we're interested in
6766         sindex = ship_name_lookup(CTEXT(node));
6767         if(sindex < 0){
6768                 return 0;
6769         }
6770         if(Ships[sindex].objnum < 0){
6771                 return 0;
6772         }
6773         
6774         // in multiplayer, search through all players
6775         if(Game_mode & GM_MULTIPLAYER){
6776                 // try and find the player
6777                 np_index = multi_find_player_by_object(&Objects[Ships[sindex].objnum]);
6778                 if((np_index >= 0) && (np_index < MAX_PLAYERS)){
6779                         p = Net_players[np_index].player;
6780                 }
6781         }
6782         // if we're in single player, we're only concerned with ourself
6783         else {
6784                 // me
6785                 if(Player_obj == &Objects[Ships[sindex].objnum]){
6786                         p = Player;
6787                 }
6788         }
6789
6790         // bad
6791         if(p == NULL){
6792                 return 0;
6793         }
6794
6795         // lookup ship type name
6796         st_index = ship_type_name_lookup(CTEXT(CDR(node)));
6797         if(st_index < 0){
6798                 return 0;
6799         }
6800
6801         // look stuff up        
6802         total = 0;
6803         for(idx=0; idx<Num_ship_types; idx++){
6804                 if((p->stats.m_okKills[idx] > 0) && (Ship_info[idx].flags & Ship_type_flags[st_index])){
6805                         total += p->stats.m_okKills[idx];
6806                 }
6807         }
6808
6809         // total
6810         return total;
6811 }
6812
6813 int sexp_num_class_kills(int node)
6814 {
6815         int sindex, np_index, si_index;
6816         player *p = NULL;
6817
6818         // get the ship we're interested in
6819         sindex = ship_name_lookup(CTEXT(node));
6820         if(sindex < 0){
6821                 return 0;
6822         }
6823         if(Ships[sindex].objnum < 0){
6824                 return 0;
6825         }
6826         
6827         // in multiplayer, search through all players
6828         if(Game_mode & GM_MULTIPLAYER){
6829                 // try and find the player
6830                 np_index = multi_find_player_by_object(&Objects[Ships[sindex].objnum]);
6831                 if((np_index >= 0) && (np_index < MAX_PLAYERS)){
6832                         p = Net_players[np_index].player;
6833                 }
6834         }
6835         // if we're in single player, we're only concerned with ourself
6836         else {
6837                 // me
6838                 if(Player_obj == &Objects[Ships[sindex].objnum]){
6839                         p = Player;
6840                 }
6841         }
6842
6843         // bad
6844         if(p == NULL){
6845                 return 0;
6846         }
6847
6848         // get the ship type we're looking for
6849         si_index = ship_info_lookup(CTEXT(CDR(node)));
6850         if((si_index < 0) || (si_index > Num_ship_types)){
6851                 return 0;
6852         }
6853
6854         // return the count     
6855         return p->stats.m_okKills[si_index];
6856 }
6857
6858 void sexp_subsys_set_random(int node)
6859 {
6860         int sindex, low, high, n, idx, rand, exclusion_list[MAX_MODEL_SUBSYSTEMS];              
6861         ship_subsys *subsys;
6862         ship *shipp;
6863
6864         // get ship
6865         sindex = ship_name_lookup(CTEXT(node));
6866         if(sindex < 0){
6867                 return;
6868         }
6869         if(Ships[sindex].objnum < 0){
6870                 return;
6871         }
6872         shipp = &Ships[sindex];
6873
6874         // get low
6875         low = num_eval(CDR(node));
6876         if (low < 0) {
6877                 low = 0;
6878         }
6879
6880         // get high
6881         high = num_eval(CDR(CDR(node)));
6882         if (high > 100) {
6883                 high = 100;
6884         }
6885
6886         if (low > high) {
6887                 Int3();
6888                 return;
6889         }
6890
6891         n = CDR(CDR(CDR(node)));
6892
6893         // init exclusion list
6894         memset(exclusion_list, 0, sizeof(int) * Ship_info[shipp->ship_info_index].n_subsystems);
6895
6896         // get exclusion list
6897         while( n != -1) {
6898                 int exclude_index = ship_get_subsys_index(shipp, CTEXT(n), 0);
6899                 if (exclude_index >= 0) {
6900                         exclusion_list[exclude_index] = 1;
6901                 }
6902
6903                 n = CDR(n);
6904         }
6905
6906         // apply to all others
6907         for (idx=0; idx<Ship_info[shipp->ship_info_index].n_subsystems; idx++) {
6908                 if ( exclusion_list[idx] == 0 ) {
6909                         // get non excluded subsystem
6910                         subsys = ship_get_indexed_subsys(shipp, idx, NULL);
6911
6912                         // randomize its hit points
6913                         rand = rand_internal(low, high);
6914                         subsys->current_hits = 0.01f * rand * subsys->system_info->max_hits;
6915                 }
6916         }
6917 }
6918
6919 void sexp_supernova_start(int node)
6920 {
6921         supernova_start(atoi(CTEXT(node)));
6922 }
6923
6924 int sexp_is_secondary_selected(int node)
6925 {
6926         int sindex;
6927         int bank;
6928         ship *shipp;
6929
6930         // lookup ship
6931         sindex = ship_name_lookup(CTEXT(node));
6932         if(sindex < 0){
6933                 return 0;
6934         }
6935         if(Ships[sindex].objnum < 0){
6936                 return 0;
6937         }
6938         shipp = &Ships[sindex];
6939
6940         // bogus value?
6941         bank = atoi(CTEXT(CDR(node)));
6942         if(bank >= shipp->weapons.num_secondary_banks){
6943                 return 0;
6944         }
6945
6946         // is this the bank currently selected
6947         if(bank == shipp->weapons.current_secondary_bank){
6948                 return 1;
6949         }
6950
6951         // nope
6952         return 0;
6953 }
6954
6955 int sexp_is_primary_selected(int node)
6956 {
6957         int sindex;
6958         int bank;
6959         ship *shipp;
6960
6961         // lookup ship
6962         sindex = ship_name_lookup(CTEXT(node));
6963         if(sindex < 0){
6964                 return 0;
6965         }
6966         if(Ships[sindex].objnum < 0){
6967                 return 0;
6968         }
6969         shipp = &Ships[sindex];
6970
6971         // bogus value?
6972         bank = atoi(CTEXT(CDR(node)));
6973         if(bank >= shipp->weapons.num_primary_banks){
6974                 return 0;
6975         }
6976
6977         // is this the bank currently selected
6978         if(bank == shipp->weapons.current_primary_bank){
6979                 return 1;
6980         }
6981
6982         // nope
6983         return 0;
6984 }
6985
6986 #define RIGHT_QUAD      0
6987 #define FRONT_QUAD      1
6988 #define LEFT_QUAD       3
6989 #define REAR_QUAD       2
6990
6991 //      Return SEXP_TRUE if quadrant quadnum is near max.
6992 int shield_quad_near_max(int quadnum)
6993 {
6994         float   remaining = 0.0f;
6995         for (int i=0; i<MAX_SHIELD_SECTIONS; i++) {
6996                 if (i == quadnum){
6997                         continue;
6998                 }
6999                 remaining += Player_obj->shields[i];
7000         }
7001
7002         if ((remaining < 2.0f) || (Player_obj->shields[quadnum] > Ship_info[Player_ship->ship_info_index].shields/MAX_SHIELD_SECTIONS - 5.0f)) {
7003                 return SEXP_TRUE;
7004         } else {
7005                 return SEXP_FALSE;
7006         }
7007 }
7008
7009 //      Return truth value for special SEXP.
7010 //      Used in training#5, perhaps in other missions.
7011 int process_special_sexps(int index)
7012 {
7013         switch (index) {
7014         case 0: //      Ship "Freighter 1" is aspect locked by player.
7015                 if (Player_ai->target_objnum != -1) {
7016                         if (!(SDL_strcasecmp(Ships[Objects[Player_ai->target_objnum].instance].ship_name, "Freighter 1"))) {
7017                                 if (Player_ai->current_target_is_locked)
7018                                         return SEXP_TRUE;
7019                         }
7020                 }
7021                 return SEXP_FALSE;
7022                 break;
7023         case 1: //      Fired Interceptors
7024                 object  *objp;
7025                 for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
7026                         if (objp->type == OBJ_WEAPON) {
7027                                 if (!SDL_strcasecmp(Weapon_info[Weapons[objp->instance].weapon_info_index].name, "Interceptor#weak")) {
7028                                         int target = Weapons[objp->instance].target_num;
7029                                         if (target != -1) {
7030                                                 if (Objects[target].type == OBJ_SHIP) {
7031                                                         if (!(SDL_strcasecmp(Ships[Objects[target].instance].ship_name, "Freighter 1")))
7032                                                                 return SEXP_TRUE;
7033                                                 }
7034                                         }
7035                                 }
7036                         }
7037                 }
7038                 return SEXP_FALSE;
7039         case 2: //      Ship "Freighter 1", subsystem "Weapons" is aspect locked by player.
7040                 if (Player_ai->target_objnum != -1) {
7041                         if (!(SDL_strcasecmp(Ships[Objects[Player_ai->target_objnum].instance].ship_name, "Freighter 1"))) {
7042                                 if (!(SDL_strcasecmp(Player_ai->targeted_subsys->system_info->name, "Weapons"))) {
7043                                         if (Player_ai->current_target_is_locked){
7044                                                 return SEXP_TRUE;
7045                                         }
7046                                 }
7047                         }
7048                 }
7049                 return SEXP_FALSE;
7050                 break;
7051         case 3: //      Player ship suffering shield damage on front.
7052                 apply_damage_to_shield(Player_obj, FRONT_QUAD, 10.0f);
7053                 hud_shield_quadrant_hit(Player_obj, FRONT_QUAD);
7054                 return SEXP_TRUE;
7055                 break;
7056         case 4: //      Player ship suffering much damage.
7057                 nprintf(("AI", "Frame %i\n", Framecount));
7058                 apply_damage_to_shield(Player_obj, FRONT_QUAD, 10.0f);
7059                 hud_shield_quadrant_hit(Player_obj, FRONT_QUAD);
7060                 if (Player_obj->shields[FRONT_QUAD] < 2.0f)
7061                         return SEXP_TRUE;
7062                 else
7063                         return SEXP_FALSE;
7064                 break;
7065         case 5: //      Player's shield is quick repaired
7066                 nprintf(("AI", "Frame %i, recharged to %7.3f\n", Framecount, Player_obj->shields[FRONT_QUAD]));
7067
7068                 apply_damage_to_shield(Player_obj, FRONT_QUAD, -flFrametime*200.0f);
7069
7070                 if (Player_obj->shields[FRONT_QUAD] > Ship_info[Player_ship->ship_info_index].shields/4.0f)
7071                         Player_obj->shields[FRONT_QUAD] = Ship_info[Player_ship->ship_info_index].shields/4.0f;
7072
7073                 //hud_shield_quadrant_hit(Player_obj, FRONT_QUAD);
7074                 if (Player_obj->shields[FRONT_QUAD] > Player_obj->shields[(FRONT_QUAD+1)%4] - 2.0f)
7075                         return SEXP_TRUE;
7076                 else
7077                         return SEXP_FALSE;
7078                 break;
7079         case 6: //      3 of player's shield quadrants are reduced to 0.
7080                 Player_obj->shields[1] = 1.0f;
7081                 Player_obj->shields[2] = 1.0f;
7082                 Player_obj->shields[3] = 1.0f;
7083                 //apply_damage_to_shield(Player_obj, FRONT_QUAD, 1.0f);
7084                 hud_shield_quadrant_hit(Player_obj, FRONT_QUAD);
7085                 return SEXP_TRUE;
7086         case 7: //      Make sure front quadrant has been maximized, or close to it.
7087                 if (shield_quad_near_max(FRONT_QUAD)) return SEXP_TRUE; else return SEXP_FALSE;
7088                 break;
7089
7090         case 8: //      Make sure rear quadrant has been maximized, or close to it.
7091                 if (shield_quad_near_max(REAR_QUAD)) return SEXP_TRUE; else return SEXP_FALSE;
7092                 break;
7093         
7094         case 9: //      Zero left and right quadrants in preparation for maximizing rear quadrant.
7095                 Player_obj->shields[LEFT_QUAD] = 0.0f;
7096                 Player_obj->shields[RIGHT_QUAD] = 0.0f;
7097                 hud_shield_quadrant_hit(Player_obj, LEFT_QUAD);
7098                 return SEXP_TRUE;
7099                 break;
7100
7101         case 10:        //      Return true if player is low on Interceptors.
7102                 if (Player_ship->weapons.secondary_bank_ammo[0] + Player_ship->weapons.secondary_bank_ammo[1] < 8)
7103                         return SEXP_TRUE;
7104                 else
7105                         return SEXP_FALSE;
7106                 break;
7107
7108         case 11:        //      Return true if player has plenty of Interceptors.
7109                 if (Player_ship->weapons.secondary_bank_ammo[0] + Player_ship->weapons.secondary_bank_ammo[1] >= 8)
7110                         return SEXP_TRUE;
7111                 else
7112                         return SEXP_FALSE;
7113                 break;
7114
7115         case 12:        //      Return true if player is low on Interceptors.
7116                 if (Player_ship->weapons.secondary_bank_ammo[0] + Player_ship->weapons.secondary_bank_ammo[1] < 4)
7117                         return SEXP_TRUE;
7118                 else
7119                         return SEXP_FALSE;
7120                 break;
7121
7122         case 13:        // Zero front shield quadrant.  Added for Jim Boone on August 26, 1999 by MK.
7123                 Player_obj->shields[FRONT_QUAD] = 0.0f;
7124                 hud_shield_quadrant_hit(Player_obj, FRONT_QUAD);
7125                 return SEXP_TRUE;
7126                 break;
7127
7128         case 100:       //      Return true if player is out of countermeasures.
7129                 if (Player_ship->cmeasure_count <= 0)
7130                         return SEXP_TRUE;
7131                 else
7132                         return SEXP_FALSE;
7133
7134         default:
7135                 Int3(); //      Unsupported node type.
7136         }
7137
7138         return SEXP_FALSE;
7139 }
7140
7141 // custom sexp operator for handling misc training stuff
7142 int sexp_special_training_check(int node)
7143 {
7144         int num, rtn;
7145
7146         num = atoi(CTEXT(node));
7147         if (num == SPECIAL_CHECK_TRAINING_FAILURE)
7148                 return Training_failure ? SEXP_TRUE : SEXP_FALSE;
7149
7150         // To MK: do whatever you want with this number here.
7151         rtn = process_special_sexps(atoi(CTEXT(node)));
7152
7153         return rtn;
7154 }
7155
7156 // sexpression to flash a hud gauge.  gauge name is text valud of node
7157 void sexp_flash_hud_gauge( int node )
7158 {
7159         char *name;
7160         int i;
7161
7162         name = CTEXT(node);
7163         for (i = 0; i < NUM_HUD_GAUGES; i++ ) {
7164                 if ( !SDL_strcasecmp(HUD_gauge_text[i], name) ) {
7165                         hud_gauge_start_flash(i);       // call HUD function to flash gauge
7166                         break;
7167                 }
7168         }
7169 }
7170
7171 void sexp_set_training_context_fly_path(int node)
7172 {
7173         int i;
7174
7175         for (i=0; i<Num_waypoint_lists; i++)
7176                 if (!SDL_strcasecmp(CTEXT(node), Waypoint_lists[i].name))
7177                         break;
7178
7179         if (i < Num_waypoint_lists) {
7180                 Training_context |= TRAINING_CONTEXT_FLY_PATH;
7181                 Training_context_path = i;
7182                 Training_context_distance = (float) atof(CTEXT(CDR(node)));
7183                 Training_context_goal_waypoint = 0;
7184                 Training_context_at_waypoint = -1;
7185         }
7186 }
7187
7188 void sexp_set_training_context_speed(int node)
7189 {
7190         Training_context |= TRAINING_CONTEXT_SPEED;
7191         Training_context_speed_min = atoi(CTEXT(node));
7192         Training_context_speed_max = atoi(CTEXT(CDR(node)));
7193         Training_context_speed_set = 0;
7194 }
7195
7196 // high-level sexpression evaluator
7197 int eval_sexp(int cur_node)
7198 {
7199         int node, sexp_val = UNINITIALIZED;
7200         if (cur_node == -1)  // empty list, i.e. sexp: ( )
7201                 return FALSE;
7202
7203         SDL_assert(cur_node >= 0);                      // we have special sexp nodes <= -1!!!  MWA
7204                                                                         // which should be intercepted before we get here.  HOFFOSS
7205
7206         SDL_assert( (SEXP_NODE_TYPE(cur_node) == SEXP_LIST) || (SEXP_NODE_TYPE(cur_node) == SEXP_ATOM) );
7207
7208         // trap known true and known false sexpressions.  We don't trap on SEXP_NAN sexpressions since
7209         // they may yet evaluate to true or false.
7210
7211         if (Sexp_nodes[cur_node].value == SEXP_KNOWN_TRUE)
7212                 return 1;
7213         else if (Sexp_nodes[cur_node].value == SEXP_KNOWN_FALSE)
7214                 return 0;
7215
7216         if (Sexp_nodes[cur_node].first != -1) {
7217                 node = CAR(cur_node);
7218                 sexp_val = eval_sexp(node);
7219                 Sexp_nodes[cur_node].value = Sexp_nodes[node].value;    // higher level node gets node value
7220                 return sexp_val;
7221
7222         } else {
7223                 int op_num;
7224
7225                 node = CDR(cur_node);           // makes reading the next bit of code a little easier.
7226
7227                 op_num = find_operator(CTEXT(cur_node)); 
7228                 switch ( op_num ) {
7229                 // arithmetic operators will always return just their value
7230                         case OP_PLUS:
7231                                 sexp_val = add_sexps( node );
7232                                 break;
7233
7234                         case OP_MINUS:
7235                                 sexp_val = sub_sexps( node );
7236                                 break;
7237
7238                         case OP_MUL:
7239                                 sexp_val = mul_sexps( node );
7240                                 break;
7241
7242                         case OP_MOD:
7243                                 sexp_val = mod_sexps( node );
7244                                 break;
7245
7246                         case OP_DIV:
7247                                 sexp_val = div_sexps( node );
7248                                 break;
7249
7250                         case OP_RAND:
7251                                 sexp_val = rand_sexp( node );
7252                                 break;
7253
7254                 // boolean operators can have one of the special sexp values (known true, known false, unknown)
7255                         case OP_TRUE:
7256                                 sexp_val = SEXP_KNOWN_TRUE;
7257                                 break;
7258
7259                         case OP_FALSE:
7260                                 sexp_val = SEXP_KNOWN_FALSE;
7261                                 break;
7262
7263                         case OP_OR:
7264                                 sexp_val = sexp_or( node );
7265                                 break;
7266
7267                         case OP_AND:
7268                                 sexp_val = sexp_and( node );
7269                                 break;
7270
7271                         case OP_AND_IN_SEQUENCE:
7272                                 sexp_val = sexp_and_in_sequence( node );
7273                                 break;
7274
7275                         case OP_GREATER_THAN:
7276                                 sexp_val = sexp_gt( node );
7277                                 break;
7278                                 
7279                         case OP_LESS_THAN:
7280                                 sexp_val = sexp_lt( node );
7281                                 break;
7282
7283                         case OP_EQUALS:
7284                                 sexp_val = sexp_equal( node );
7285                                 break;
7286
7287                         case OP_IS_IFF:
7288                                 sexp_val = sexp_is_iff( node );
7289                                 break;
7290
7291                         case OP_NOT:
7292                                 sexp_val = sexp_not( node );
7293                                 break;
7294
7295                         case OP_PREVIOUS_GOAL_TRUE:
7296                                 sexp_val = sexp_previous_goal_status( node, GOAL_COMPLETE );
7297                                 break;
7298
7299                         case OP_PREVIOUS_GOAL_FALSE:
7300                                 sexp_val = sexp_previous_goal_status( node, GOAL_FAILED );
7301                                 break;
7302
7303                         case OP_PREVIOUS_GOAL_INCOMPLETE:
7304                                 sexp_val = sexp_previous_goal_status( node, GOAL_INCOMPLETE );
7305                                 break;
7306
7307                         case OP_PREVIOUS_EVENT_TRUE:
7308                                 sexp_val = sexp_previous_event_status( node, EVENT_SATISFIED );
7309                                 break;
7310
7311                         case OP_PREVIOUS_EVENT_FALSE:
7312                                 sexp_val = sexp_previous_event_status( node, EVENT_FAILED );
7313                                 break;
7314
7315                         case OP_PREVIOUS_EVENT_INCOMPLETE:
7316                                 sexp_val = sexp_previous_event_status( node, EVENT_INCOMPLETE );
7317                                 break;
7318
7319                         case OP_EVENT_TRUE:
7320                         case OP_EVENT_FALSE:
7321                                 sexp_val = sexp_event_status( node, (op_num == OP_EVENT_TRUE?1:0) );
7322                                 if ((sexp_val != SEXP_TRUE) && (sexp_val != SEXP_KNOWN_TRUE))
7323                                         Sexp_useful_number = 0;  // indicate sexp isn't current yet
7324
7325                                 break;
7326
7327                         case OP_EVENT_TRUE_DELAY:
7328                         case OP_EVENT_FALSE_DELAY:
7329                                 sexp_val = sexp_event_delay_status( node, (op_num == OP_EVENT_TRUE_DELAY?1:0) );
7330                                 if ((sexp_val != SEXP_TRUE) && (sexp_val != SEXP_KNOWN_TRUE))
7331                                         Sexp_useful_number = 0;  // indicate sexp isn't current yet
7332                                 break;
7333
7334                         case OP_GOAL_TRUE_DELAY:
7335                         case OP_GOAL_FALSE_DELAY:
7336                                 sexp_val = sexp_goal_delay_status( node, (op_num == OP_GOAL_TRUE_DELAY?1:0) );
7337                                 break;
7338
7339                         case OP_EVENT_INCOMPLETE:
7340                                 sexp_val = sexp_event_incomplete( node );
7341                                 if ((sexp_val != SEXP_TRUE) && (sexp_val != SEXP_KNOWN_TRUE))
7342                                         Sexp_useful_number = 0;  // indicate sexp isn't current yet
7343                                 break;
7344
7345                         case OP_GOAL_INCOMPLETE:
7346                                 sexp_val = sexp_goal_incomplete( node );
7347                                 break;
7348
7349                         // destroy type sexpressions
7350                         case OP_IS_DESTROYED:
7351                                 sexp_val = sexp_is_destroyed( node, NULL );
7352                                 break;
7353
7354                         case OP_IS_SUBSYSTEM_DESTROYED:
7355                                 sexp_val = sexp_is_subsystem_destroyed( node );
7356                                 break;
7357
7358                         case OP_HAS_DOCKED:
7359                                 sexp_val = sexp_has_docked( node );
7360                                 break;
7361
7362                         case OP_HAS_ARRIVED:
7363                                 sexp_val = sexp_has_arrived( node, NULL );
7364                                 break;
7365
7366                         case OP_HAS_DEPARTED:
7367                                 sexp_val = sexp_has_departed( node, NULL );
7368                                 break;
7369
7370                         case OP_HAS_UNDOCKED:
7371                                 sexp_val = sexp_has_undocked( node );
7372                                 break;
7373
7374                         case OP_IS_DISABLED:
7375                                 sexp_val = sexp_is_disabled( node, NULL );
7376                                 break;
7377
7378                         case OP_IS_DISARMED:
7379                                 sexp_val = sexp_is_disarmed( node, NULL );
7380                                 break;
7381
7382                         case OP_WAYPOINTS_DONE:
7383                                 sexp_val = sexp_are_waypoints_done( node );
7384                                 break;
7385
7386                         // objective operators that use a delay
7387                         case OP_IS_DESTROYED_DELAY:
7388                                 sexp_val = sexp_is_destroyed_delay( node );
7389                                 break;
7390
7391                         case OP_IS_SUBSYSTEM_DESTROYED_DELAY:
7392                                 sexp_val = sexp_is_subsystem_destroyed_delay( node );
7393                                 break;
7394
7395                         case OP_HAS_DOCKED_DELAY:
7396                                 sexp_val = sexp_has_docked_delay( node );
7397                                 break;
7398
7399                         case OP_HAS_ARRIVED_DELAY:
7400                                 sexp_val = sexp_has_arrived_delay( node );
7401                                 break;
7402
7403                         case OP_HAS_DEPARTED_DELAY:
7404                                 sexp_val = sexp_has_departed_delay( node );
7405                                 break;
7406
7407                         case OP_HAS_UNDOCKED_DELAY:
7408                                 sexp_val = sexp_has_undocked_delay( node );
7409                                 break;
7410
7411                         case OP_IS_DISABLED_DELAY:
7412                                 sexp_val = sexp_is_disabled_delay( node );
7413                                 break;
7414
7415                         case OP_IS_DISARMED_DELAY:
7416                                 sexp_val = sexp_is_disarmed_delay( node );
7417                                 break;
7418
7419                         case OP_WAYPOINTS_DONE_DELAY:
7420                                 sexp_val = sexp_are_waypoints_done_delay( node );
7421                                 break;
7422
7423                         case OP_SHIP_TYPE_DESTROYED:
7424                                 sexp_val = sexp_ship_type_destroyed( node );
7425                                 break;
7426
7427                         // time based sexpressions
7428                         case OP_HAS_TIME_ELAPSED:
7429                                 sexp_val = sexp_has_time_elapsed( node );
7430                                 break;
7431
7432                         case OP_MODIFY_VARIABLE:
7433                                 sexp_modify_variable( node );
7434                                 sexp_val = 1;   // 1 means only do once.
7435                                 break;
7436
7437                         case OP_TIME_SHIP_DESTROYED:
7438                                 sexp_val = sexp_time_destroyed( node );
7439                                 break;
7440                         
7441                         case OP_TIME_WING_DESTROYED:
7442                                 sexp_val = sexp_time_wing_destroyed( node );
7443                                 break;
7444                         
7445                         case OP_TIME_SHIP_ARRIVED:
7446                                 sexp_val = sexp_time_ship_arrived( node );
7447                                 break;
7448                         
7449                         case OP_TIME_WING_ARRIVED:
7450                                 sexp_val = sexp_time_wing_arrived( node );
7451                                 break;
7452                         
7453                         case OP_TIME_SHIP_DEPARTED:
7454                                 sexp_val = sexp_time_ship_departed( node );
7455                                 break;
7456                         
7457                         case OP_TIME_WING_DEPARTED:
7458                                 sexp_val = sexp_time_wing_departed( node );
7459                                 break;
7460
7461                         case OP_MISSION_TIME:
7462                                 sexp_val = sexp_mission_time();
7463                                 break;
7464
7465                         case OP_TIME_DOCKED:
7466                                 sexp_val = sexp_time_docked( node );
7467                                 break;
7468
7469                         case OP_TIME_UNDOCKED:
7470                                 sexp_val = sexp_time_undocked( node );
7471                                 break;
7472
7473                         // info based sexpressions (like shields, hits
7474                         case OP_SHIELDS_LEFT:
7475                                 sexp_val = sexp_shields_left( node );
7476                                 break;
7477
7478                         case OP_HITS_LEFT:
7479                                 sexp_val = sexp_hits_left( node );
7480                                 break;
7481
7482                         case OP_SPECIAL_WARP_DISTANCE:
7483                                 sexp_val = sexp_special_warp_dist( node );
7484                                 break;
7485
7486                         case OP_HITS_LEFT_SUBSYSTEM:
7487                                 sexp_val = sexp_hits_left_subsystem( node );
7488                                 break;
7489
7490                         case OP_DISTANCE:
7491                                 sexp_val = sexp_distance( node );
7492                                 break;
7493
7494                         case OP_IS_SHIP_VISIBLE:
7495                                 sexp_val = sexp_is_ship_visible( node );
7496                                 break;
7497
7498                         case OP_TEAM_SCORE:
7499                                 sexp_val = sexp_team_score( node );
7500                                 break;
7501
7502                         case OP_LAST_ORDER_TIME:
7503                                 sexp_val = sexp_last_order_time( node );
7504                                 break;
7505
7506                         case OP_NUM_PLAYERS:
7507                                 sexp_val = sexp_num_players();
7508                                 break;
7509
7510                         case OP_SKILL_LEVEL_AT_LEAST:
7511                                 sexp_val = sexp_skill_level_at_least( node );
7512                                 break;
7513
7514                         case OP_IS_CARGO_KNOWN:
7515                         case OP_CARGO_KNOWN_DELAY:
7516                                 sexp_val = sexp_is_cargo_known( node, (op_num==OP_IS_CARGO_KNOWN)?0:1 );
7517                                 break;
7518
7519                         case OP_HAS_BEEN_TAGGED_DELAY:
7520                                 sexp_val = sexp_has_been_tagged_delay(node);
7521                                 break;
7522
7523                         case OP_CAP_SUBSYS_CARGO_KNOWN_DELAY:
7524                                 sexp_val = sexp_cap_subsys_cargo_known_delay(node);
7525                                 break;
7526
7527                         case OP_WAS_PROMOTION_GRANTED:
7528                                 sexp_val = sexp_was_promotion_granted(node);
7529                                 break;
7530
7531                         case OP_WAS_MEDAL_GRANTED:
7532                                 sexp_val = sexp_was_medal_granted(node);
7533                                 break;
7534
7535                         case OP_PERCENT_SHIPS_DEPARTED:
7536                         case OP_PERCENT_SHIPS_DESTROYED:
7537                                 sexp_val = sexp_percent_ships_depart_destroy(node, op_num);
7538                                 break;
7539
7540                         case OP_DEPART_NODE_DELAY:
7541                                 sexp_val = sexp_depart_node_delay( node );
7542                                 break;
7543
7544                         case OP_DESTROYED_DEPARTED_DELAY:
7545                                 sexp_val = sexp_destroyed_departed_delay( node );
7546                                 break;
7547
7548                         // conditional sexpressions
7549                         case OP_WHEN:
7550                                 sexp_val = eval_when( node );
7551                                 break;
7552
7553                         case OP_COND:
7554                                 sexp_val = eval_cond( node );
7555                                 break;
7556
7557                         // sexpressions with side effects
7558                         case OP_CHANGE_IFF:
7559                                 sexp_change_iff( node );
7560                                 sexp_val = 1;
7561                                 break;
7562
7563                         case OP_ADD_SHIP_GOAL:
7564                                 sexp_add_ship_goal( node );
7565                                 sexp_val = 1;
7566                                 break;
7567
7568                         case OP_ADD_WING_GOAL:
7569                                 sexp_add_wing_goal( node );
7570                                 sexp_val = 1;
7571                                 break;
7572
7573                         case OP_ADD_GOAL:
7574                                 sexp_add_goal( node );
7575                                 sexp_val = 1;
7576                                 break;
7577
7578                         case OP_CLEAR_SHIP_GOALS:
7579                                 sexp_clear_ship_goals( node );
7580                                 sexp_val = 1;
7581                                 break;
7582
7583                         case OP_CLEAR_WING_GOALS:
7584                                 sexp_clear_wing_goals( node );
7585                                 sexp_val = 1;
7586                                 break;
7587
7588                         case OP_CLEAR_GOALS:
7589                                 sexp_clear_goals( node );
7590                                 sexp_val = 1;
7591                                 break;
7592
7593                         case OP_PROTECT_SHIP:
7594                         case OP_UNPROTECT_SHIP:
7595                                 sexp_protect_ships( node, (op_num==OP_PROTECT_SHIP?1:0) );
7596                                 sexp_val = 1;
7597                                 break;
7598
7599                         case OP_BEAM_PROTECT_SHIP:
7600                         case OP_BEAM_UNPROTECT_SHIP:
7601                                 sexp_beam_protect_ships( node, (op_num==OP_BEAM_PROTECT_SHIP?1:0) );
7602                                 sexp_val = 1;
7603                                 break;
7604
7605                         case OP_SHIP_INVISIBLE:
7606                         case OP_SHIP_VISIBLE:
7607                                 sexp_ships_visible( node, (op_num==OP_SHIP_VISIBLE?1:0) );
7608                                 sexp_val = 1;
7609                                 break;
7610
7611                         case OP_SHIP_VULNERABLE:
7612                         case OP_SHIP_INVULNERABLE:
7613                                 sexp_ships_invulnerable( node, (op_num==OP_SHIP_INVULNERABLE?1:0) );
7614                                 sexp_val = 1;
7615                                 break;
7616
7617                         case OP_SHIP_GUARDIAN:
7618                         case OP_SHIP_NO_GUARDIAN:
7619                                 sexp_ships_guardian( node, (op_num==OP_SHIP_GUARDIAN?1:0) );
7620                                 sexp_val = 1;
7621                                 break;
7622
7623                         case OP_SHIP_VANISH:
7624                                 sexp_ship_vanish( node );
7625                                 sexp_val = 1;
7626                                 break;
7627
7628                         case OP_SEND_MESSAGE:
7629                                 sexp_send_message( node );
7630                                 sexp_val = 1;
7631                                 break;
7632
7633                         case OP_SEND_MESSAGE_LIST:
7634                                 sexp_send_message_list( node );
7635                                 sexp_val = 1;
7636                                 break;
7637
7638                         case OP_SEND_RANDOM_MESSAGE:
7639                                 sexp_send_random_message( node );
7640                                 sexp_val = 1;
7641                                 break;
7642
7643                         case OP_SELF_DESTRUCT:
7644                                 sexp_self_destruct( node );
7645                                 sexp_val = 1;
7646                                 break;
7647
7648                         case OP_NEXT_MISSION:
7649                                 sexp_next_mission( node );
7650                                 sexp_val = 1;
7651                                 break;
7652                                 
7653                         case OP_END_OF_CAMPAIGN:
7654                                 sexp_end_of_campaign( node );
7655                                 sexp_val = 1;
7656                                 break;
7657
7658                         case OP_END_CAMPAIGN:
7659                                 sexp_end_campaign( node );
7660                                 sexp_val = 1;
7661                                 break;
7662
7663                         case OP_SABOTAGE_SUBSYSTEM:
7664                                 sexp_sabotage_subsystem( node );
7665                                 sexp_val = 1;
7666                                 break;
7667
7668                         case OP_REPAIR_SUBSYSTEM:
7669                                 sexp_repair_subsystem( node );
7670                                 sexp_val = 1;
7671                                 break;
7672
7673                         case OP_SET_SUBSYSTEM_STRNGTH:
7674                                 sexp_set_subsystem_strength( node );
7675                                 sexp_val = 1;
7676                                 break;
7677
7678                         case OP_INVALIDATE_GOAL:
7679                         case OP_VALIDATE_GOAL:
7680                                 sexp_change_goal_validity( node, (op_num==OP_INVALIDATE_GOAL?0:1) );
7681                                 sexp_val = 1;
7682                                 break;
7683
7684                         case OP_TRANSFER_CARGO:
7685                                 sexp_transfer_cargo( node );
7686                                 sexp_val = 1;
7687                                 break;
7688
7689                         case OP_EXCHANGE_CARGO:
7690                                 sexp_exchange_cargo( node );
7691                                 sexp_val = 1;
7692                                 break;
7693
7694
7695                         case OP_JETTISON_CARGO:
7696                                 sexp_jettison_cargo( node );
7697                                 sexp_val = 1;
7698                                 break;
7699
7700                         case OP_CARGO_NO_DEPLETE:
7701                                         sexp_cargo_no_deplete( node );
7702                                         sexp_val = 1;
7703                                         break;
7704
7705                         case OP_SET_SPECIAL_WARPOUT_NAME:
7706                                 sexp_special_warpout_name( node );
7707                                 sexp_val = 1;
7708                                 break;
7709
7710                         case OP_END_MISSION_DELAY:
7711                                 sexp_end_mission_delay( node );
7712                                 sexp_val = 1;
7713                                 break;
7714
7715                                 // sexpressions for setting flag for good/bad time for someone to reasm
7716                         case OP_GOOD_REARM_TIME:
7717                                 sexp_good_time_to_rearm( node );
7718                                 sexp_val = 1;
7719                                 break;
7720
7721                         case OP_GRANT_PROMOTION:
7722                                 sexp_grant_promotion();
7723                                 sexp_val = 1;
7724                                 break;
7725
7726                         case OP_GRANT_MEDAL:
7727                                 sexp_grant_medal( node );
7728                                 sexp_val = 1;
7729                                 break;
7730
7731                         case OP_WARP_BROKEN:
7732                         case OP_WARP_NOT_BROKEN:
7733                                 sexp_deal_with_warp( node, 0, op_num==OP_WARP_BROKEN?1:0 );
7734                                 sexp_val = 1;
7735                                 break;
7736
7737                         case OP_WARP_NEVER:
7738                         case OP_WARP_ALLOWED:
7739                                 sexp_deal_with_warp( node, 1, op_num==OP_WARP_NEVER?1:0 );
7740                                 sexp_val = 1;
7741                                 break;
7742
7743                         case OP_GOOD_SECONDARY_TIME:
7744                                 sexp_good_secondary_time( node );
7745                                 sexp_val = 1;
7746                                 break;
7747
7748                         // sexpressions to allow shpis/weapons during the course of a mission
7749                         case OP_ALLOW_SHIP:
7750                                 sexp_allow_ship( node );
7751                                 sexp_val = 1;
7752                                 break;
7753
7754                         case OP_ALLOW_WEAPON:
7755                                 sexp_allow_weapon( node );
7756                                 sexp_val = 1;
7757                                 break;
7758
7759                         case OP_TECH_ADD_SHIP:
7760                                 sexp_tech_add_ship(node);
7761                                 sexp_val = 1;
7762                                 break;
7763
7764                         case OP_TECH_ADD_WEAPON:
7765                                 sexp_tech_add_weapon(node);
7766                                 sexp_val = 1;
7767                                 break;
7768
7769                                 // in the case of a red_alert mission, simply call the red alert function to close
7770                                 // the current campaign's mission and move forward to the next mission
7771                         case OP_RED_ALERT:
7772                                 red_alert_start_mission();
7773                                 sexp_val = 1;
7774                                 break;
7775
7776                         // training operators
7777                         case OP_KEY_PRESSED:
7778                                 sexp_val = sexp_key_pressed(node);
7779                                 break;
7780
7781                         case OP_SPECIAL_CHECK:
7782                                 sexp_val = sexp_special_training_check(node);
7783                                 break;
7784
7785                         case OP_KEY_RESET:
7786                                 sexp_key_reset(node);
7787                                 sexp_val = SEXP_KNOWN_TRUE;  // only do it first time in repeating events.
7788                                 break;
7789
7790                         case OP_TARGETED:
7791                                 sexp_val = sexp_targeted(node);
7792                                 break;
7793
7794                         case OP_SPEED:
7795                                 sexp_val = sexp_speed(node);
7796                                 break;
7797
7798                         case OP_SECONDARIES_DEPLETED:
7799                                 sexp_val = sexp_secondaries_depleted(node);
7800                                 break;
7801
7802                         case OP_FACING:
7803                                 sexp_val = sexp_facing(node);
7804                                 break;
7805
7806                         case OP_FACING2:
7807                                 sexp_val = sexp_facing2(node);
7808                                 break;
7809
7810                         case OP_ORDER:
7811                                 sexp_val = sexp_order(node);
7812                                 break;
7813
7814                         case OP_WAYPOINT_MISSED:
7815                                 sexp_val = sexp_waypoint_missed();
7816                                 break;
7817
7818                         case OP_WAYPOINT_TWICE:
7819                                 sexp_val = sexp_waypoint_twice();
7820                                 break;
7821
7822                         case OP_PATH_FLOWN:
7823                                 sexp_val = sexp_path_flown();
7824                                 break;
7825
7826                         case OP_TRAINING_MSG:
7827                                 sexp_send_training_message(node);
7828                                 sexp_val = 1;
7829                                 break;
7830
7831                         case OP_FLASH_HUD_GAUGE:
7832                                 sexp_flash_hud_gauge(node);
7833                                 sexp_val = 1;
7834                                 break;
7835
7836                         case OP_SET_TRAINING_CONTEXT_FLY_PATH:
7837                                 sexp_set_training_context_fly_path(node);
7838                                 sexp_val = 1;
7839                                 break;
7840
7841                         case OP_SET_TRAINING_CONTEXT_SPEED:
7842                                 sexp_set_training_context_speed(node);
7843                                 sexp_val = 1;
7844                                 break;
7845
7846                         // debugging operators
7847                         case OP_INT3:
7848                                 Int3();
7849                                 sexp_val = 0;
7850                                 break;
7851
7852                         case 0: // zero represents a non-operator
7853                                 return atoi(CTEXT(cur_node));
7854
7855                         case OP_NOP:
7856                                 sexp_val = 1;
7857                                 break;
7858
7859                         case OP_BEAM_FIRE:
7860                                 sexp_beam_fire(node);
7861                                 sexp_val = 1;
7862                                 break;
7863
7864                         case OP_IS_TAGGED:
7865                                 sexp_val = sexp_is_tagged(node);
7866                                 break;
7867
7868                         case OP_NUM_KILLS:
7869                                 sexp_val = sexp_num_kills(node);
7870                                 break;
7871
7872                         case OP_NUM_TYPE_KILLS:
7873                                 sexp_val = sexp_num_type_kills(node);
7874                                 break;
7875
7876                         case OP_NUM_CLASS_KILLS:
7877                                 sexp_val = sexp_num_class_kills(node);
7878                                 break;
7879
7880                         case OP_BEAM_FREE:
7881                                 sexp_val = 1;
7882                                 sexp_beam_free(node);
7883                                 break;
7884
7885                         case OP_BEAM_FREE_ALL:
7886                                 sexp_val = 1;
7887                                 sexp_beam_free_all(node);
7888                                 break;
7889
7890                         case OP_BEAM_LOCK:
7891                                 sexp_val = 1;
7892                                 sexp_beam_lock(node);
7893                                 break;
7894
7895                         case OP_BEAM_LOCK_ALL:
7896                                 sexp_val = 1;
7897                                 sexp_beam_lock_all(node);
7898                                 break;
7899
7900                         case OP_TURRET_FREE:
7901                                 sexp_val = 1;
7902                                 sexp_turret_free(node);
7903                                 break;
7904
7905                         case OP_TURRET_FREE_ALL:
7906                                 sexp_val = 1;
7907                                 sexp_turret_free_all(node);
7908                                 break;
7909
7910                         case OP_TURRET_LOCK:
7911                                 sexp_val = 1;
7912                                 sexp_turret_lock(node);
7913                                 break;
7914
7915                         case OP_TURRET_LOCK_ALL:
7916                                 sexp_val = 1;
7917                                 sexp_turret_lock_all(node);
7918                                 break;
7919
7920                         case OP_ADD_REMOVE_ESCORT:
7921                                 sexp_val = 1;
7922                                 sexp_add_remove_escort(node);
7923                                 break;
7924
7925                         case OP_AWACS_SET_RADIUS:
7926                                 sexp_val = 1;
7927                                 sexp_awacs_set_radius(node);
7928                                 break;
7929
7930                         case OP_CAP_WAYPOINT_SPEED:
7931                                 sexp_val = 1;
7932                                 sexp_cap_waypont_speed(node);
7933                                 break;
7934
7935                         case OP_TURRET_TAGGED_ONLY_ALL:
7936                                 sexp_val = 1;
7937                                 sexp_turret_tagged_only_all(node);
7938                                 break;
7939
7940                         case OP_TURRET_TAGGED_CLEAR_ALL:
7941                                 sexp_val = 1;
7942                                 sexp_turret_tagged_clear_all(node);
7943                                 break;
7944
7945                         case OP_SUBSYS_SET_RANDOM:
7946                                 sexp_val = 1;
7947                                 sexp_subsys_set_random(node);
7948                                 break;
7949
7950                         case OP_SUPERNOVA_START:
7951                                 sexp_val = 1;
7952                                 sexp_supernova_start(node);
7953                                 break;
7954
7955                         case OP_SHIELD_RECHARGE_PCT:
7956                                 sexp_val = sexp_shield_recharge_pct(node);
7957                                 break;
7958
7959                         case OP_ENGINE_RECHARGE_PCT:
7960                                 sexp_val = sexp_engine_recharge_pct(node);
7961                                 break;
7962
7963                         case OP_WEAPON_RECHARGE_PCT:
7964                                 sexp_val = sexp_weapon_recharge_pct(node);
7965                                 break;
7966
7967                         case OP_SHIELD_QUAD_LOW:
7968                                 sexp_val = sexp_shield_quad_low(node);
7969                                 break;
7970
7971                         case OP_SECONDARY_AMMO_PCT:
7972                                 sexp_val = sexp_secondary_ammo_pct(node);
7973                                 break;
7974
7975                         case OP_IS_SECONDARY_SELECTED:
7976                                 sexp_val = sexp_is_secondary_selected(node);
7977                                 break;
7978
7979                         case OP_IS_PRIMARY_SELECTED:
7980                                 sexp_val = sexp_is_primary_selected(node);
7981                                 break;
7982
7983                         default:
7984                                 Error(LOCATION, "Looking for SEXP operator, found '%s'.\n", CTEXT(cur_node));
7985                                 break;
7986                 }
7987
7988                 SDL_assert(sexp_val != UNINITIALIZED);
7989
7990                 // if we haven't returned, check the sexp value of the sexpression evaluation.  A special
7991                 // value of known true or known false means that we should set the sexp.value field for
7992                 // short circuit eval (and return that special value as well).
7993                 if (sexp_val == SEXP_KNOWN_TRUE) {
7994                         Sexp_nodes[cur_node].value = SEXP_KNOWN_TRUE;
7995                         return 1;
7996                 }
7997
7998                 if (sexp_val == SEXP_KNOWN_FALSE) {
7999                         Sexp_nodes[cur_node].value = SEXP_KNOWN_FALSE;
8000                         return 0;
8001                 }
8002
8003                 if ( sexp_val == SEXP_NAN ) {
8004                         Sexp_nodes[cur_node].value = SEXP_NAN;                  // not a number values are false I would suspect
8005                         return 0;
8006                 }
8007
8008                 if ( sexp_val == SEXP_NAN_FOREVER ) {
8009                         Sexp_nodes[cur_node].value = SEXP_NAN_FOREVER;
8010                         return sexp_val;
8011                 }
8012
8013                 if ( sexp_val == SEXP_CANT_EVAL ) {
8014                         Sexp_nodes[cur_node].value = SEXP_CANT_EVAL;
8015                         Sexp_useful_number = 0;  // indicate sexp isn't current yet
8016                         return 0;
8017                 }
8018
8019                 if ( Sexp_nodes[cur_node].value == SEXP_NAN ) { // if we had a nan, but now don't, reset the value
8020                         Sexp_nodes[cur_node].value = SEXP_UNKNOWN;
8021                         return sexp_val;
8022                 }
8023
8024                 if ( sexp_val ){
8025                         Sexp_nodes[cur_node].value = SEXP_TRUE;
8026                 } else {
8027                         Sexp_nodes[cur_node].value = SEXP_FALSE;
8028                 }
8029
8030                 return sexp_val;
8031         }
8032 }
8033
8034 //      Still a debug-level system.
8035 //      get_sexp_main reads and builds the internal representation for a
8036 //      symbolic expression.
8037 //      On entry:
8038 //              Mp points at first character in expression.
8039 //      The symbolic expression is built in Sexp_nodes beginning at node 0.
8040 int get_sexp_main()
8041 {
8042         int     start_node, op;
8043         char    token[TOKEN_LENGTH];
8044         char  *savep, ch;
8045
8046         ignore_white_space();
8047
8048         savep = Mp;
8049         if (!strncmp(Mp, "( )", 3))
8050                 savep++;
8051
8052         SDL_assert(*Mp == '(');
8053         Mp++;
8054         start_node = get_sexp(token);
8055         // only need to check syntax if we have a operator
8056         if ( /*Sexp_nodes[start_node].subtype != SEXP_ATOM_OPERATOR  ||*/ Fred_running || (start_node == -1))
8057                 return start_node;
8058
8059         ch = *Mp;
8060         *Mp = '\0';
8061
8062         op = identify_operator(CTEXT(start_node));
8063         if (op == -1)
8064                 Error (LOCATION, "Can't find operator %s in operator list\n.", CTEXT(start_node) );
8065
8066         *Mp = ch;
8067
8068         return start_node;
8069 }
8070
8071 void test_sexps()
8072 {
8073         Mp = Mission_text;
8074         while (*Mp != '#') {
8075                 get_sexp_main();
8076                 diag_printf("\n----------------\n");
8077                 ignore_white_space();
8078         }
8079         exit(0);
8080 }
8081
8082 // returns the data type returned by an operator
8083 int query_operator_return_type(int op)
8084 {
8085         if (op < FIRST_OP)
8086         {
8087                 SDL_assert(op >= 0 && op < Num_operators);
8088                 op = Operators[op].value;
8089         }
8090
8091         switch (op)
8092         {
8093                 case OP_TRUE:
8094                 case OP_FALSE:
8095                 case OP_AND:
8096                 case OP_AND_IN_SEQUENCE:
8097                 case OP_OR:
8098                 case OP_NOT:
8099                 case OP_EQUALS:
8100                 case OP_GREATER_THAN:
8101                 case OP_LESS_THAN:
8102                 case OP_IS_DESTROYED:
8103                 case OP_IS_SUBSYSTEM_DESTROYED:
8104                 case OP_IS_DISABLED:
8105                 case OP_IS_DISARMED:
8106                 case OP_HAS_DOCKED:
8107                 case OP_HAS_UNDOCKED:
8108                 case OP_HAS_ARRIVED:
8109                 case OP_HAS_DEPARTED:
8110                 case OP_IS_DESTROYED_DELAY:
8111                 case OP_IS_SUBSYSTEM_DESTROYED_DELAY:
8112                 case OP_IS_DISABLED_DELAY:
8113                 case OP_IS_DISARMED_DELAY:
8114                 case OP_HAS_DOCKED_DELAY:
8115                 case OP_HAS_UNDOCKED_DELAY:
8116                 case OP_HAS_ARRIVED_DELAY:
8117                 case OP_HAS_DEPARTED_DELAY:
8118                 case OP_IS_IFF:
8119                 case OP_HAS_TIME_ELAPSED:
8120                 case OP_GOAL_INCOMPLETE:
8121                 case OP_GOAL_TRUE_DELAY:
8122                 case OP_GOAL_FALSE_DELAY:
8123                 case OP_EVENT_INCOMPLETE:
8124                 case OP_EVENT_TRUE_DELAY:
8125                 case OP_EVENT_FALSE_DELAY:
8126                 case OP_PREVIOUS_EVENT_TRUE:
8127                 case OP_PREVIOUS_EVENT_FALSE:
8128                 case OP_PREVIOUS_EVENT_INCOMPLETE:
8129                 case OP_PREVIOUS_GOAL_TRUE:
8130                 case OP_PREVIOUS_GOAL_FALSE:
8131                 case OP_PREVIOUS_GOAL_INCOMPLETE:
8132                 case OP_WAYPOINTS_DONE:
8133                 case OP_WAYPOINTS_DONE_DELAY:
8134                 case OP_SHIP_TYPE_DESTROYED:
8135                 case OP_LAST_ORDER_TIME:
8136                 case OP_KEY_PRESSED:
8137                 case OP_TARGETED:
8138                 case OP_SPEED:
8139                 case OP_FACING:
8140                 case OP_FACING2:
8141                 case OP_ORDER:
8142                 case OP_WAYPOINT_MISSED:
8143                 case OP_WAYPOINT_TWICE:
8144                 case OP_PATH_FLOWN:
8145                 case OP_EVENT_TRUE:
8146                 case OP_EVENT_FALSE:
8147                 case OP_SKILL_LEVEL_AT_LEAST:
8148                 case OP_IS_CARGO_KNOWN:
8149                 case OP_HAS_BEEN_TAGGED_DELAY:
8150                 case OP_CAP_SUBSYS_CARGO_KNOWN_DELAY:
8151                 case OP_CARGO_KNOWN_DELAY:
8152                 case OP_WAS_PROMOTION_GRANTED:
8153                 case OP_WAS_MEDAL_GRANTED:
8154                 case OP_PERCENT_SHIPS_DEPARTED:
8155                 case OP_PERCENT_SHIPS_DESTROYED:
8156                 case OP_DEPART_NODE_DELAY:
8157                 case OP_DESTROYED_DEPARTED_DELAY:
8158                 case OP_SPECIAL_CHECK:
8159                 case OP_IS_TAGGED:
8160                 case OP_SECONDARIES_DEPLETED:
8161                 case OP_SHIELD_QUAD_LOW:
8162                 case OP_IS_SECONDARY_SELECTED:
8163                 case OP_IS_PRIMARY_SELECTED:
8164                         return OPR_BOOL;
8165
8166                 case OP_PLUS:
8167                 case OP_MINUS:
8168                 case OP_MOD:
8169                 case OP_MUL:
8170                 case OP_DIV:
8171                 case OP_RAND:
8172                 case OP_TIME_SHIP_DESTROYED:
8173                 case OP_TIME_SHIP_ARRIVED:
8174                 case OP_TIME_SHIP_DEPARTED:
8175                 case OP_TIME_WING_DESTROYED:
8176                 case OP_TIME_WING_ARRIVED:
8177                 case OP_TIME_WING_DEPARTED:
8178                 case OP_MISSION_TIME:
8179                 case OP_TIME_DOCKED:
8180                 case OP_TIME_UNDOCKED:
8181                 case OP_SHIELDS_LEFT:
8182                 case OP_HITS_LEFT:
8183                 case OP_HITS_LEFT_SUBSYSTEM:
8184                 case OP_DISTANCE:
8185                 case OP_NUM_PLAYERS:
8186                 case OP_NUM_KILLS:
8187                 case OP_NUM_TYPE_KILLS:
8188                 case OP_NUM_CLASS_KILLS:
8189                 case OP_SHIELD_RECHARGE_PCT:
8190                 case OP_ENGINE_RECHARGE_PCT:
8191                 case OP_WEAPON_RECHARGE_PCT:
8192                 case OP_SECONDARY_AMMO_PCT:
8193                 case OP_SPECIAL_WARP_DISTANCE:
8194                 case OP_IS_SHIP_VISIBLE:
8195                 case OP_TEAM_SCORE:
8196                         return OPR_POSITIVE;
8197
8198                 case OP_COND:
8199                 case OP_WHEN:
8200                 case OP_CHANGE_IFF:
8201                 case OP_CLEAR_SHIP_GOALS:
8202                 case OP_CLEAR_WING_GOALS:
8203                 case OP_CLEAR_GOALS:
8204                 case OP_ADD_SHIP_GOAL:
8205                 case OP_ADD_WING_GOAL:
8206                 case OP_ADD_GOAL:
8207                 case OP_PROTECT_SHIP:
8208                 case OP_UNPROTECT_SHIP:
8209                 case OP_BEAM_PROTECT_SHIP:
8210                 case OP_BEAM_UNPROTECT_SHIP:
8211                 case OP_INT3:
8212                 case OP_NOP:
8213                 case OP_GOALS_ID:
8214                 case OP_SEND_MESSAGE:
8215                 case OP_SELF_DESTRUCT:
8216                 case OP_NEXT_MISSION:
8217                 case OP_END_CAMPAIGN:
8218                 case OP_END_OF_CAMPAIGN:
8219                 case OP_SABOTAGE_SUBSYSTEM:
8220                 case OP_REPAIR_SUBSYSTEM:
8221                 case OP_INVALIDATE_GOAL:
8222                 case OP_VALIDATE_GOAL:
8223                 case OP_SEND_RANDOM_MESSAGE:
8224                 case OP_TRANSFER_CARGO:
8225                 case OP_EXCHANGE_CARGO:
8226                 case OP_JETTISON_CARGO:
8227                 case OP_CARGO_NO_DEPLETE:
8228                 case OP_KEY_RESET:
8229                 case OP_TRAINING_MSG:
8230                 case OP_SET_TRAINING_CONTEXT_FLY_PATH:
8231                 case OP_SET_TRAINING_CONTEXT_SPEED:
8232                 case OP_END_MISSION_DELAY:
8233                 case OP_SET_SUBSYSTEM_STRNGTH:
8234                 case OP_GOOD_REARM_TIME:
8235                 case OP_GRANT_PROMOTION:
8236                 case OP_GRANT_MEDAL:
8237                 case OP_ALLOW_SHIP:
8238                 case OP_ALLOW_WEAPON:
8239                 case OP_TECH_ADD_SHIP:
8240                 case OP_TECH_ADD_WEAPON:
8241                 case OP_WARP_BROKEN:
8242                 case OP_WARP_NOT_BROKEN:
8243                 case OP_WARP_NEVER:
8244                 case OP_WARP_ALLOWED:
8245                 case OP_FLASH_HUD_GAUGE:
8246                 case OP_GOOD_SECONDARY_TIME:
8247                 case OP_SHIP_VISIBLE:
8248                 case OP_SHIP_INVISIBLE:
8249                 case OP_SHIP_VULNERABLE:
8250                 case OP_SHIP_INVULNERABLE:
8251                 case OP_SHIP_GUARDIAN:
8252                 case OP_SHIP_VANISH:
8253                 case OP_SHIP_NO_GUARDIAN:
8254                 case OP_RED_ALERT:
8255                 case OP_MODIFY_VARIABLE:
8256                 case OP_BEAM_FIRE:
8257                 case OP_BEAM_FREE:
8258                 case OP_BEAM_FREE_ALL:
8259                 case OP_BEAM_LOCK:
8260                 case OP_BEAM_LOCK_ALL:
8261                 case OP_TURRET_FREE:
8262                 case OP_TURRET_FREE_ALL:
8263                 case OP_TURRET_LOCK:
8264                 case OP_TURRET_LOCK_ALL:
8265                 case OP_ADD_REMOVE_ESCORT:
8266                 case OP_AWACS_SET_RADIUS:
8267                 case OP_SEND_MESSAGE_LIST:
8268                 case OP_CAP_WAYPOINT_SPEED:
8269                 case OP_TURRET_TAGGED_ONLY_ALL:
8270                 case OP_TURRET_TAGGED_CLEAR_ALL:
8271                 case OP_SUBSYS_SET_RANDOM:
8272                 case OP_SUPERNOVA_START:
8273                 case OP_SET_SPECIAL_WARPOUT_NAME:
8274                         return OPR_NULL;
8275
8276                 case OP_AI_CHASE:
8277                 case OP_AI_DOCK:
8278                 case OP_AI_UNDOCK:
8279                 case OP_AI_WARP:                                                // this particular operator is obsolete
8280                 case OP_AI_WARP_OUT:
8281                 case OP_AI_WAYPOINTS:
8282                 case OP_AI_WAYPOINTS_ONCE:
8283                 case OP_AI_DESTROY_SUBSYS:
8284                 case OP_AI_CHASE_WING:
8285                 case OP_AI_DISABLE_SHIP:
8286                 case OP_AI_DISARM_SHIP:
8287                 case OP_AI_GUARD:
8288                 case OP_AI_GUARD_WING:
8289                 case OP_AI_CHASE_ANY:
8290                 case OP_AI_EVADE_SHIP:
8291                 case OP_AI_STAY_NEAR_SHIP:
8292                 case OP_AI_KEEP_SAFE_DISTANCE:
8293                 case OP_AI_IGNORE:
8294                 case OP_AI_STAY_STILL:
8295                 case OP_AI_PLAY_DEAD:
8296                         return OPR_AI_GOAL;
8297
8298                 default:
8299                         Int3();
8300         }
8301
8302         return 0;
8303 }
8304
8305 // returns the data type of a specified argument to an operator.  Argnum is 0 indexed.
8306 int query_operator_argument_type(int op, int argnum)
8307 {
8308         int index = op;
8309
8310         if (op < FIRST_OP)
8311         {
8312                 SDL_assert(index >= 0 && index < Num_operators);
8313                 op = Operators[index].value;
8314
8315         } else {
8316                 for (index=0; index<Num_operators; index++)
8317                         if (Operators[index].value == op)
8318                                 break;
8319
8320                 SDL_assert(index < Num_operators);
8321         }
8322
8323         if (argnum >= Operators[index].max)
8324                 return OPF_NONE;
8325
8326         switch (op) {
8327                 case OP_TRUE:
8328                 case OP_FALSE:
8329                 case OP_MISSION_TIME:
8330                 case OP_INT3:
8331                 case OP_NOP:
8332                 case OP_WAYPOINT_MISSED:
8333                 case OP_WAYPOINT_TWICE:
8334                 case OP_PATH_FLOWN:
8335                 case OP_GRANT_PROMOTION:
8336                 case OP_WAS_PROMOTION_GRANTED:
8337                 case OP_RED_ALERT:
8338                         return OPF_NONE;
8339
8340                 case OP_AND:
8341                 case OP_AND_IN_SEQUENCE:
8342                 case OP_OR:
8343                 case OP_NOT:
8344                         return OPF_BOOL;
8345
8346                 case OP_PLUS:
8347                 case OP_MINUS:
8348                 case OP_MOD:
8349                 case OP_MUL:
8350                 case OP_DIV:
8351                 case OP_RAND:
8352                 case OP_EQUALS:
8353                 case OP_GREATER_THAN:
8354                 case OP_LESS_THAN:
8355                 case OP_HAS_TIME_ELAPSED:
8356                 case OP_SPEED:
8357                 case OP_SET_TRAINING_CONTEXT_SPEED:
8358                 case OP_END_MISSION_DELAY:
8359                 case OP_SPECIAL_CHECK:
8360                 case OP_AI_WARP_OUT:
8361                 case OP_TEAM_SCORE:
8362                         return OPF_POSITIVE;
8363
8364                 case OP_AI_WARP:                                                                // this operator is obsolete
8365                 case OP_SET_TRAINING_CONTEXT_FLY_PATH:
8366                         if ( !argnum )
8367                                 return OPF_WAYPOINT_PATH;
8368                         else
8369                                 return OPF_NUMBER;
8370                 
8371                 case OP_AI_WAYPOINTS:
8372                 case OP_AI_WAYPOINTS_ONCE:
8373                         if ( argnum == 0 )
8374                                 return OPF_WAYPOINT_PATH;
8375                         else
8376                                 return OPF_POSITIVE;
8377
8378                 case OP_IS_DISABLED:
8379                 case OP_IS_DISARMED:
8380                 case OP_TIME_SHIP_DESTROYED:
8381                 case OP_TIME_SHIP_ARRIVED:
8382                 case OP_TIME_SHIP_DEPARTED:
8383                 case OP_SHIELDS_LEFT:
8384                 case OP_HITS_LEFT:
8385                 case OP_CLEAR_SHIP_GOALS:
8386                 case OP_PROTECT_SHIP:
8387                 case OP_UNPROTECT_SHIP:
8388                 case OP_BEAM_PROTECT_SHIP:
8389                 case OP_BEAM_UNPROTECT_SHIP:
8390                 case OP_TRANSFER_CARGO:
8391                 case OP_EXCHANGE_CARGO:
8392                 case OP_SHIP_INVISIBLE:
8393                 case OP_SHIP_VISIBLE:
8394                 case OP_SHIP_INVULNERABLE:
8395                 case OP_SHIP_VULNERABLE:
8396                 case OP_SHIP_GUARDIAN:
8397                 case OP_SHIP_VANISH:
8398                 case OP_SHIP_NO_GUARDIAN:
8399                 case OP_SECONDARIES_DEPLETED:
8400                 case OP_SPECIAL_WARP_DISTANCE:
8401                 case OP_SET_SPECIAL_WARPOUT_NAME:
8402                 case OP_IS_SHIP_VISIBLE:
8403                         return OPF_SHIP;
8404
8405                 case OP_IS_DESTROYED:
8406                 case OP_HAS_ARRIVED:
8407                 case OP_HAS_DEPARTED:
8408                 case OP_CLEAR_GOALS:
8409                         return OPF_SHIP_WING;
8410
8411                 case OP_IS_DISABLED_DELAY:
8412                 case OP_IS_DISARMED_DELAY:
8413                         if ( argnum == 0 )
8414                                 return OPF_POSITIVE;
8415                         else
8416                                 return OPF_SHIP;
8417
8418                 case OP_FACING:
8419                         if (argnum == 0)
8420                                 return OPF_SHIP;
8421                         else
8422                                 return OPF_POSITIVE;
8423
8424                 case OP_FACING2:
8425                         if (argnum == 0) {
8426                                 return OPF_WAYPOINT_PATH;
8427                         } else {
8428                                 return OPF_POSITIVE;
8429                         }
8430
8431                 case OP_ORDER:
8432                         if (argnum == 1)
8433                                 return OPF_AI_ORDER;
8434                         else
8435                                 return OPF_SHIP_WING;
8436
8437                 case OP_IS_DESTROYED_DELAY:
8438                 case OP_HAS_ARRIVED_DELAY:
8439                 case OP_HAS_DEPARTED_DELAY:
8440                 case OP_LAST_ORDER_TIME:
8441                         if ( argnum == 0 )
8442                                 return OPF_POSITIVE;
8443                         else
8444                                 return OPF_SHIP_WING;
8445
8446                 case OP_DISTANCE:
8447                         return OPF_SHIP_WING_POINT;
8448
8449                 case OP_MODIFY_VARIABLE:
8450                         if (argnum == 0) {
8451                                 return OPF_VARIABLE_NAME;
8452                         } else {
8453                                 return OPF_AMBIGUOUS; 
8454                         }
8455
8456                 case OP_HAS_DOCKED:
8457                 case OP_HAS_UNDOCKED:
8458                 case OP_HAS_DOCKED_DELAY:
8459                 case OP_HAS_UNDOCKED_DELAY:
8460                 case OP_TIME_DOCKED:
8461                 case OP_TIME_UNDOCKED:
8462                         if ( argnum < 2 )
8463                                 return OPF_SHIP;
8464                         else
8465                                 return OPF_POSITIVE;
8466
8467                 case OP_TIME_WING_DESTROYED:
8468                 case OP_TIME_WING_ARRIVED:
8469                 case OP_TIME_WING_DEPARTED:
8470                 case OP_CLEAR_WING_GOALS:
8471                         return OPF_WING;
8472
8473                 case OP_IS_SUBSYSTEM_DESTROYED:
8474                 case OP_HITS_LEFT_SUBSYSTEM:
8475                         if (!argnum)
8476                                 return OPF_SHIP;
8477                         else
8478                                 return OPF_SUBSYSTEM;
8479
8480                 case OP_TARGETED:
8481                         if (!argnum)
8482                                 return OPF_SHIP;
8483                         else if (argnum == 1)
8484                                 return OPF_POSITIVE;
8485                         else
8486                                 return OPF_SUBSYSTEM;
8487
8488                 case OP_IS_SUBSYSTEM_DESTROYED_DELAY:
8489                         if ( argnum == 0 )
8490                                 return OPF_SHIP;
8491                         else if ( argnum == 1 )
8492                                 return OPF_SUBSYSTEM;
8493                         else
8494                                 return OPF_POSITIVE;
8495
8496                 case OP_IS_IFF:
8497                 case OP_CHANGE_IFF:
8498                         if (!argnum)
8499                                 return OPF_IFF;
8500                         else
8501                                 return OPF_SHIP;
8502
8503                 case OP_ADD_SHIP_GOAL:
8504                         if (!argnum)
8505                                 return OPF_SHIP;
8506                         else
8507                                 return OPF_AI_GOAL;
8508
8509                 case OP_ADD_WING_GOAL:
8510                         if (!argnum)
8511                                 return OPF_WING;
8512                         else
8513                                 return OPF_AI_GOAL;
8514
8515                 case OP_ADD_GOAL:
8516                         if ( argnum == 0 )
8517                                 return OPF_SHIP_WING;
8518                         else
8519                                 return OPF_AI_GOAL;
8520
8521                 case OP_WHEN:
8522                 case OP_COND:
8523                         if (!argnum)
8524                                 return OPF_BOOL;
8525                         else
8526                                 return OPF_NULL;
8527
8528                 case OP_AI_DISABLE_SHIP:
8529                 case OP_AI_DISARM_SHIP:
8530                 case OP_AI_EVADE_SHIP:
8531                 case OP_AI_STAY_NEAR_SHIP:
8532                 case OP_AI_IGNORE:
8533                         if (!argnum)
8534                                 return OPF_SHIP;
8535                         else
8536                                 return OPF_POSITIVE;
8537
8538                 case OP_AI_CHASE:
8539                 case OP_AI_GUARD:
8540                         if (!argnum)
8541                                 return OPF_SHIP_WING;
8542                         else
8543                                 return OPF_POSITIVE;
8544
8545                 case OP_AI_UNDOCK:
8546                 case OP_AI_KEEP_SAFE_DISTANCE:
8547                         return OPF_POSITIVE;
8548
8549                 case OP_AI_DOCK:
8550                         if (!argnum)
8551                                 return OPF_SHIP;
8552                         else if (argnum == 1)
8553                                 return OPF_DOCKER_POINT;
8554                         else if (argnum == 2)
8555                                 return OPF_DOCKEE_POINT;
8556                         else
8557                                 return OPF_POSITIVE;
8558
8559                 case OP_AI_CHASE_WING:
8560                 case OP_AI_GUARD_WING:
8561                         if (!argnum)
8562                                 return OPF_WING;
8563                         else
8564                                 return OPF_POSITIVE;
8565
8566                 case OP_AI_DESTROY_SUBSYS:
8567                         if (!argnum)
8568                                 return OPF_SHIP;
8569                         else if (argnum == 1)
8570                                 return OPF_SUBSYSTEM;
8571                         else
8572                                 return OPF_POSITIVE;
8573                         
8574                 case OP_GOALS_ID:
8575                         return OPF_AI_GOAL;
8576
8577                 case OP_SEND_MESSAGE:
8578                 case OP_SEND_RANDOM_MESSAGE:
8579                         if ( argnum == 0 )
8580                                 return OPF_WHO_FROM;
8581                         else if ( argnum == 1 )
8582                                 return OPF_PRIORITY;
8583                         else
8584                                 return OPF_MESSAGE;
8585
8586                 case OP_SEND_MESSAGE_LIST:
8587                         int a_mod;
8588
8589                         // every four, the value repeats
8590                         a_mod = argnum % 4;                     
8591
8592                         // who from
8593                         if(a_mod == 0)
8594                                 return OPF_WHO_FROM;
8595                         else if(a_mod == 1)
8596                                 return OPF_PRIORITY;
8597                         else if(a_mod == 2)
8598                                 return OPF_MESSAGE;
8599                         else if(a_mod == 3)
8600                                 return OPF_POSITIVE;
8601
8602                 case OP_TRAINING_MSG:
8603                         if (argnum < 2)
8604                                 return OPF_MESSAGE;
8605                         else
8606                                 return OPF_POSITIVE;
8607
8608                 case OP_SELF_DESTRUCT:
8609                         return OPF_SHIP;
8610
8611                 case OP_NEXT_MISSION:
8612                         return OPF_MISSION_NAME;
8613
8614                 case OP_END_CAMPAIGN:
8615                 case OP_END_OF_CAMPAIGN:
8616                         return OPF_NONE;
8617
8618                 case OP_PREVIOUS_GOAL_TRUE:
8619                 case OP_PREVIOUS_GOAL_FALSE:
8620                         if ( argnum == 0 )
8621                                 return OPF_MISSION_NAME;
8622                         else if (argnum == 1 )
8623                                 return OPF_GOAL_NAME;
8624                         else
8625                                 return OPF_BOOL;
8626
8627                 case OP_PREVIOUS_GOAL_INCOMPLETE:
8628                         return OPF_GOAL_NAME;
8629
8630                 case OP_PREVIOUS_EVENT_TRUE:
8631                 case OP_PREVIOUS_EVENT_FALSE:
8632                 case OP_PREVIOUS_EVENT_INCOMPLETE:
8633                         if (!argnum)
8634                                 return OPF_MISSION_NAME;
8635                         else if ( argnum == 1 )
8636                                 return OPF_EVENT_NAME;
8637                         else
8638                                 return OPF_BOOL;
8639
8640                 case OP_SABOTAGE_SUBSYSTEM:
8641                 case OP_REPAIR_SUBSYSTEM:
8642                 case OP_SET_SUBSYSTEM_STRNGTH:
8643                         if (!argnum)
8644                                 return OPF_SHIP_NOT_PLAYER;
8645                         else if (argnum == 1 )
8646                                 return OPF_SUBSYSTEM;
8647                         else
8648                                 return OPF_POSITIVE;
8649
8650                 case OP_WAYPOINTS_DONE:
8651                         if ( argnum == 0 )
8652                                 return OPF_SHIP_WING;
8653                         else
8654                                 return OPF_WAYPOINT_PATH;
8655
8656                 case OP_WAYPOINTS_DONE_DELAY:
8657                         if ( argnum == 0 )
8658                                 return OPF_SHIP_WING;
8659                         else if ( argnum == 1 )
8660                                 return OPF_WAYPOINT_PATH;
8661                         else
8662                                 return OPF_POSITIVE;
8663
8664                 case OP_INVALIDATE_GOAL:
8665                 case OP_VALIDATE_GOAL:
8666                         return OPF_GOAL_NAME;
8667
8668                 case OP_SHIP_TYPE_DESTROYED:
8669                         if ( argnum == 0 )
8670                                 return OPF_POSITIVE;
8671                         else
8672                                 return OPF_SHIP_TYPE;
8673
8674                 case OP_KEY_PRESSED:
8675                         if (!argnum)
8676                                 return OPF_KEYPRESS;
8677                         else
8678                                 return OPF_POSITIVE;
8679
8680                 case OP_KEY_RESET:
8681                         return OPF_KEYPRESS;
8682
8683                 case OP_EVENT_TRUE:
8684                 case OP_EVENT_FALSE:
8685                         return OPF_EVENT_NAME;
8686
8687                 case OP_EVENT_INCOMPLETE:
8688                 case OP_EVENT_TRUE_DELAY:
8689                 case OP_EVENT_FALSE_DELAY:
8690                         if (!argnum)
8691                                 return OPF_EVENT_NAME;
8692                         else
8693                                 return OPF_POSITIVE;
8694
8695                 case OP_GOAL_INCOMPLETE:
8696                 case OP_GOAL_TRUE_DELAY:
8697                 case OP_GOAL_FALSE_DELAY:
8698                         if (!argnum)
8699                                 return OPF_GOAL_NAME;
8700                         else
8701                                 return OPF_POSITIVE;
8702
8703                 case OP_AI_PLAY_DEAD:
8704                 case OP_AI_CHASE_ANY:
8705                         return OPF_POSITIVE;
8706
8707                 case OP_AI_STAY_STILL:
8708                         if (!argnum)
8709                                 return OPF_SHIP_POINT;
8710                         else
8711                                 return OPF_POSITIVE;
8712
8713                 case OP_GOOD_REARM_TIME:
8714                         if ( argnum == 0 )
8715                                 return OPF_IFF;
8716                         else
8717                                 return OPF_POSITIVE;
8718
8719                 case OP_NUM_PLAYERS:
8720                         return OPF_POSITIVE;
8721
8722                 case OP_SKILL_LEVEL_AT_LEAST:
8723                         return OPF_SKILL_LEVEL;
8724
8725                 case OP_GRANT_MEDAL:
8726                 case OP_WAS_MEDAL_GRANTED:
8727                         return OPF_MEDAL_NAME;
8728
8729                 case OP_IS_CARGO_KNOWN:
8730                         return OPF_SHIP;
8731
8732                 case OP_CARGO_KNOWN_DELAY:
8733                         if ( argnum == 0 )
8734                                 return OPF_POSITIVE;
8735                         else
8736                                 return OPF_SHIP;
8737
8738                 case OP_HAS_BEEN_TAGGED_DELAY:
8739                         if ( argnum == 0 ) {
8740                                 return OPF_POSITIVE;
8741                         } else {
8742                                 return OPF_SHIP;
8743                         }
8744
8745                 case OP_CAP_SUBSYS_CARGO_KNOWN_DELAY:
8746                         if ( argnum == 0 ) {
8747                                 return OPF_POSITIVE;
8748                         } else if ( argnum == 1 ) {
8749                                 return OPF_SHIP;
8750                         } else {
8751                                 return OPF_SUBSYSTEM;
8752                         }
8753
8754                 case OP_ALLOW_SHIP:
8755                 case OP_TECH_ADD_SHIP:
8756                         return OPF_SHIP_CLASS_NAME;
8757
8758                 case OP_ALLOW_WEAPON:
8759                 case OP_TECH_ADD_WEAPON:
8760                         return OPF_WEAPON_NAME;
8761
8762                 case OP_WARP_BROKEN:
8763                 case OP_WARP_NOT_BROKEN:
8764                 case OP_WARP_NEVER:
8765                 case OP_WARP_ALLOWED:
8766                         return OPF_SHIP;
8767
8768                 case OP_FLASH_HUD_GAUGE:
8769                         return OPF_HUD_GAUGE_NAME;
8770
8771                 case OP_GOOD_SECONDARY_TIME:
8772                         if ( argnum == 0 )
8773                                 return OPF_IFF;
8774                         else if ( argnum == 1 )
8775                                 return OPF_POSITIVE;
8776                         else if ( argnum == 2 )
8777                                 return OPF_HUGE_WEAPON;
8778                         else
8779                                 return OPF_SHIP;
8780
8781                 case OP_PERCENT_SHIPS_DEPARTED:
8782                 case OP_PERCENT_SHIPS_DESTROYED:
8783                         if ( argnum == 0 ){
8784                                 return OPF_POSITIVE;
8785                         } else {
8786                                 return OPF_SHIP_WING;
8787                         }
8788                         break;
8789
8790                 case OP_DEPART_NODE_DELAY:      
8791                         if ( argnum == 0 ){
8792                                 return OPF_POSITIVE;
8793                         } else if ( argnum == 1 ){
8794                                 return OPF_JUMP_NODE_NAME;
8795                         } else {
8796                                 return OPF_SHIP;
8797                         }
8798
8799                 case OP_DESTROYED_DEPARTED_DELAY:
8800                         if ( argnum == 0 ){
8801                                 return OPF_POSITIVE;
8802                         } else {
8803                                 return OPF_SHIP_WING;
8804                         }
8805
8806                 case OP_JETTISON_CARGO:
8807                         if(argnum == 0){
8808                                 return OPF_SHIP;
8809                         } else {
8810                                 return OPF_POSITIVE;
8811                         }
8812
8813                 case OP_CARGO_NO_DEPLETE:
8814                         if (argnum == 0) {
8815                                 return OPF_SHIP;
8816                         } else {
8817                                 return OPF_NUMBER;
8818                         }
8819
8820                 case OP_BEAM_FIRE:
8821                         switch(argnum){
8822                         case 0:
8823                                 return OPF_SHIP;
8824                         case 1:
8825                                 return OPF_SUBSYSTEM;
8826                         case 2:
8827                                 return OPF_SHIP;
8828                         case 3:
8829                                 return OPF_SUBSYSTEM;
8830                         }
8831
8832                 case OP_IS_TAGGED:
8833                         return OPF_SHIP;
8834
8835                 case OP_NUM_KILLS:
8836                         return OPF_SHIP;
8837
8838                 case OP_NUM_TYPE_KILLS:
8839                         if(argnum == 0){
8840                                 return OPF_SHIP;
8841                         } else {
8842                                 return OPF_SHIP_TYPE;
8843                         }
8844
8845                 case OP_NUM_CLASS_KILLS:
8846                         if(argnum == 0){
8847                                 return OPF_SHIP;
8848                         } else {
8849                                 return OPF_SHIP_CLASS_NAME;
8850                         }
8851
8852                 case OP_BEAM_FREE:
8853                 case OP_BEAM_LOCK:
8854                 case OP_TURRET_FREE:
8855                 case OP_TURRET_LOCK:
8856                         if(argnum == 0){
8857                                 return OPF_SHIP;
8858                         } else {
8859                                 return OPF_SUBSYSTEM;
8860                         }
8861
8862                 case OP_BEAM_FREE_ALL:
8863                 case OP_BEAM_LOCK_ALL:
8864                 case OP_TURRET_FREE_ALL:
8865                 case OP_TURRET_LOCK_ALL:
8866                 case OP_TURRET_TAGGED_ONLY_ALL:
8867                 case OP_TURRET_TAGGED_CLEAR_ALL:
8868                         return OPF_SHIP;
8869
8870                 case OP_ADD_REMOVE_ESCORT:
8871                         if(argnum == 0){
8872                                 return OPF_SHIP;
8873                         } else {
8874                                 return OPF_NUMBER;
8875                         }
8876
8877                 case OP_AWACS_SET_RADIUS:
8878                         if(argnum == 0){
8879                                 return OPF_SHIP;
8880                         } else if(argnum == 1){
8881                                 return OPF_AWACS_SUBSYSTEM;
8882                         } else {
8883                                 return OPF_NUMBER;
8884                         }
8885
8886                 case OP_CAP_WAYPOINT_SPEED:
8887                         if (argnum == 0) {
8888                                 return OPF_SHIP;
8889                         } else {
8890                                 return OPF_NUMBER;
8891                         }
8892
8893                 case OP_SUBSYS_SET_RANDOM:
8894                         if (argnum == 0) {
8895                                 return OPF_SHIP;
8896                         } else if (argnum == 1 || argnum == 2) {
8897                                 return OPF_NUMBER;
8898                         } else {
8899                                 return OPF_SUBSYSTEM;
8900                         }
8901
8902                 case OP_SUPERNOVA_START:
8903                         return OPF_NUMBER;
8904
8905                 case OP_SHIELD_RECHARGE_PCT:
8906                 case OP_WEAPON_RECHARGE_PCT:
8907                 case OP_ENGINE_RECHARGE_PCT:
8908                         return OPF_SHIP;                        
8909
8910                 case OP_SHIELD_QUAD_LOW:
8911                         if(argnum == 0){
8912                                 return OPF_SHIP;
8913                         } else {
8914                                 return OPF_NUMBER;
8915                         }
8916
8917                 case OP_SECONDARY_AMMO_PCT:
8918                         if(argnum == 0){
8919                                 return OPF_SHIP;
8920                         } else {
8921                                 return OPF_NUMBER;
8922                         }
8923
8924                 case OP_IS_SECONDARY_SELECTED:
8925                 case OP_IS_PRIMARY_SELECTED:
8926                         if(argnum == 0){
8927                                 return OPF_SHIP;
8928                         } else {
8929                                 return OPF_NUMBER;
8930                         }
8931
8932                 default:
8933                         Int3();
8934         }
8935
8936         return 0;
8937 }
8938
8939 void update_block_names(const char *old_name, const char *new_name)
8940 {
8941         int i;
8942
8943         for (i=0; i<MAX_SEXP_VARIABLES; i++) {
8944                 if (Sexp_variables[i].type & SEXP_VARIABLE_BLOCK) {
8945                         if ( !SDL_strcasecmp(old_name, Sexp_variables[i].variable_name) ) {
8946                                 SDL_strlcpy(Sexp_variables[i].variable_name, new_name, TOKEN_LENGTH);
8947                         }
8948                 }
8949         }
8950 }
8951
8952 // DA: 1/7/99  Used to rename ships and waypoints, not variables
8953 // Strictly used in FRED
8954 void update_sexp_references(char *old_name, char *new_name)
8955 {
8956         int i;
8957
8958         // update_block_names
8959         update_block_names(old_name, new_name);
8960
8961         SDL_assert(strlen(new_name) < TOKEN_LENGTH);
8962         for (i=0; i<MAX_SEXP_NODES; i++){
8963                 if ((SEXP_NODE_TYPE(i) == SEXP_ATOM) && (Sexp_nodes[i].subtype == SEXP_ATOM_STRING)){
8964                         if (!SDL_strcasecmp(CTEXT(i), old_name)){
8965                                 SDL_strlcpy(CTEXT(i), new_name, TOKEN_LENGTH);
8966                         }
8967                 }
8968         }
8969 }
8970
8971 // DA: 1/7/99  Used to rename event names, goal names, not variables
8972 // Strictly used in FRED
8973 void update_sexp_references(char *old_name, char *new_name, int format)
8974 {
8975         int i;
8976
8977         SDL_assert(strlen(new_name) < TOKEN_LENGTH);
8978         for (i=0; i<MAX_SEXP_NODES; i++){
8979                 if (is_sexp_top_level(i)){
8980                         update_sexp_references(old_name, new_name, format, i);
8981                 }
8982         }
8983 }
8984
8985 // DA: 1/7/99  Used to rename event names, goal names, not variables
8986 // recursive function to update references to a certain type of data
8987 void update_sexp_references(char *old_name, char *new_name, int format, int node)
8988 {
8989         int i, n, op;
8990
8991         if (node < 0){
8992                 return;
8993         }
8994
8995         if ((SEXP_NODE_TYPE(node) == SEXP_LIST) && (Sexp_nodes[node].subtype == SEXP_ATOM_LIST)) {
8996                 if (Sexp_nodes[node].first){
8997                         update_sexp_references(old_name, new_name, format, Sexp_nodes[node].first);
8998                 }
8999                 if (Sexp_nodes[node].rest){
9000                         update_sexp_references(old_name, new_name, format, Sexp_nodes[node].rest);
9001                 }
9002
9003                 return;
9004         }
9005
9006         if (SEXP_NODE_TYPE(node) != SEXP_ATOM){
9007                 return;
9008         }
9009
9010         if (Sexp_nodes[node].subtype != SEXP_ATOM_OPERATOR){
9011                 return;
9012         }
9013
9014         op = identify_operator(CTEXT(node));
9015         SDL_assert(Sexp_nodes[node].first < 0);
9016         n = Sexp_nodes[node].rest;
9017         i = 0;
9018         while (n >= 0) {
9019                 if (SEXP_NODE_TYPE(n) == SEXP_LIST){
9020                         update_sexp_references(old_name, new_name, format, Sexp_nodes[n].first);
9021                 } else {
9022                         SDL_assert((SEXP_NODE_TYPE(n) == SEXP_ATOM) && ((Sexp_nodes[n].subtype == SEXP_ATOM_NUMBER) || (Sexp_nodes[n].subtype == SEXP_ATOM_STRING)));
9023                         if (query_operator_argument_type(op, i) == format) {
9024                                 if (!SDL_strcasecmp(CTEXT(n), old_name)){
9025                                         SDL_strlcpy(CTEXT(n), new_name, TOKEN_LENGTH);
9026                                 }
9027                         }
9028                 }
9029
9030                 n = Sexp_nodes[n].rest;
9031                 i++;
9032         }
9033 }
9034
9035 int query_referenced_in_sexp(int mode, char *name, int *node)
9036 {
9037         int i, n, j;
9038
9039         for (n=0; n<MAX_SEXP_NODES; n++){
9040                 if ((SEXP_NODE_TYPE(n) == SEXP_ATOM) && (Sexp_nodes[n].subtype == SEXP_ATOM_STRING)){
9041                         if (!SDL_strcasecmp(CTEXT(n), name)){
9042                                 break;
9043                         }
9044                 }
9045         }
9046
9047         if (n == MAX_SEXP_NODES){
9048                 return 0;
9049         }
9050
9051         if (node){
9052                 *node = n;
9053         }
9054
9055         // so we know it's being used somewhere..  Time to find out where..
9056         for (i=0; i<MAX_SHIPS; i++)
9057                 if (Ships[i].objnum >= 0) {
9058                         if (query_node_in_sexp(n, Ships[i].arrival_cue)){
9059                                 return i | SRC_SHIP_ARRIVAL;
9060                         }
9061                         if (query_node_in_sexp(n, Ships[i].departure_cue)){
9062                                 return i | SRC_SHIP_DEPARTURE;
9063                         }
9064                 }
9065
9066         for (i=0; i<MAX_WINGS; i++){
9067                 if (Wings[i].wave_count) {
9068                         if (query_node_in_sexp(n, Wings[i].arrival_cue)){
9069                                 return i | SRC_WING_ARRIVAL;
9070                         }
9071                         if (query_node_in_sexp(n, Wings[i].departure_cue)){
9072                                 return i | SRC_WING_DEPARTURE;
9073                         }
9074                 }
9075         }
9076
9077         for (i=0; i<Num_mission_events; i++){
9078                 if (query_node_in_sexp(n, Mission_events[i].formula)){
9079                         return i | SRC_EVENT;
9080                 }
9081         }
9082
9083         for (i=0; i<Num_goals; i++){
9084                 if (query_node_in_sexp(n, Mission_goals[i].formula)){
9085                         return i | SRC_MISSION_GOAL;
9086                 }
9087         }
9088
9089         for (j=0; j<Num_teams; j++) {
9090                 for (i=0; i<Debriefings[j].num_stages; i++) {
9091                         if (query_node_in_sexp(n, Debriefings[j].stages[i].formula)){
9092                                 return i | SRC_DEBRIEFING;
9093                         }
9094                 }
9095         }
9096
9097         for (j=0; j<Num_teams; j++) {
9098                 for (i=0; i<Briefings[j].num_stages; i++) {
9099                         if (query_node_in_sexp(n, Briefings[j].stages[i].formula)){
9100                                 return i | SRC_BRIEFING;
9101                         }
9102                 }
9103         }
9104
9105         return SRC_UNKNOWN;
9106 }
9107
9108 int verify_vector(char *text)
9109 {
9110         char *str;
9111         int i, z, len = 0;
9112
9113         for (i=0; i<Num_waypoint_lists; i++) {
9114                 len = strlen(str = Waypoint_lists[i].name);
9115                 if (!SDL_strncasecmp(str, text, len)){
9116                         if (!text[len] || text[len] == ':'){
9117                                 break;
9118                         }
9119                 }
9120         }
9121
9122         if (i < Num_waypoint_lists) {
9123                 if (!text[len]){
9124                         return 0;  // a valid waypoint path
9125                 }
9126
9127                 str = &text[len + 1];
9128                 while (*str){
9129                         if (!isdigit(*str++)){
9130                                 return -1;  // not a valid number
9131                         }
9132                 }
9133
9134                 z = atoi(&text[len + 1]);
9135                 if (z < 1 || z > Waypoint_lists[i].count){
9136                         return -1;  // out of range
9137                 }
9138
9139                 return 0;  // a valid waypoint
9140         }
9141
9142         len = strlen(text);
9143         if (text[0] != '(' || text[len - 1] != ')'){
9144                 return -1;
9145         }
9146
9147         str = &text[0];
9148         skip_white(&str);
9149         if (*str != '('){
9150                 return -1;
9151         }
9152
9153         str++;
9154         skip_white(&str);
9155         if (validate_float(&str)){
9156                 return -1;
9157         }
9158
9159         skip_white(&str);
9160         if (validate_float(&str)){
9161                 return -1;
9162         }
9163
9164         skip_white(&str);
9165         if (validate_float(&str)){
9166                 return -1;
9167         }
9168
9169         skip_white(&str);
9170         if (*str != ')'){
9171                 return -1;
9172         }
9173
9174         str++;
9175         skip_white(&str);
9176         if (*str){
9177                 return -1;
9178         }
9179
9180         return 0;
9181 }
9182
9183 void skip_white(char **str)
9184 {
9185         if ((**str == ' ') || (**str == '\t')){
9186                 (*str)++;
9187         }
9188 }
9189
9190 int validate_float(char **str)
9191 {
9192         int count = 0, dot = 0;
9193
9194         while (isdigit(**str) || **str == '.') {
9195                 if (**str == '.') {
9196                         if (dot){
9197                                 return -1;
9198                         }
9199
9200                         dot = 1;
9201                 }
9202
9203                 (*str)++;
9204                 count++;
9205         }
9206
9207         if (!count){
9208                 return -1;
9209         }
9210
9211         return 0;
9212 }
9213
9214 // check if operator return type opr is a valid match for operator argument type opf
9215 int sexp_query_type_match(int opf, int opr)
9216 {
9217         switch (opf) {
9218                 case OPF_NUMBER:
9219                         return ((opr == OPR_NUMBER) || (opr == OPR_POSITIVE));
9220
9221                 case OPF_POSITIVE:
9222                         return (opr == OPR_POSITIVE);
9223
9224                 case OPF_BOOL:
9225                         return (opr == OPR_BOOL);
9226
9227                 case OPF_NULL:
9228                         return (opr == OPR_NULL);
9229
9230                 case OPF_AI_GOAL:
9231                         return (opr == OPR_AI_GOAL);
9232         }
9233
9234         return 0;
9235 }
9236
9237 const char *sexp_error_message(int num)
9238 {
9239         switch (num) {
9240                 case SEXP_CHECK_NONOP_ARGS:
9241                         return "Data shouldn't have arguments";
9242
9243                 case SEXP_CHECK_OP_EXPTECTED:
9244                         return "Operator expected instead of data";
9245
9246                 case SEXP_CHECK_UNKNOWN_OP:
9247                         return "Unrecognized operator";
9248
9249                 case SEXP_CHECK_TYPE_MISMATCH:
9250                         return "Argument type mismatch";
9251
9252                 case SEXP_CHECK_BAD_ARG_COUNT:
9253                         return "Argument count is illegal";
9254
9255                 case SEXP_CHECK_UNKNOWN_TYPE:
9256                         return "Unknown operator argument type";
9257
9258                 case SEXP_CHECK_INVALID_NUM:
9259                         return "Not a number";
9260
9261                 case SEXP_CHECK_INVALID_SHIP:
9262                         return "Invalid ship name";
9263
9264                 case SEXP_CHECK_INVALID_WING:
9265                         return "Invalid wing name";
9266
9267                 case SEXP_CHECK_INVALID_SUBSYS:
9268                         return "Invalid subsystem name";
9269
9270                 case SEXP_CHECK_INVALID_IFF:
9271                         return "Invalid team name";
9272
9273                 case SEXP_CHECK_INVALID_POINT:
9274                         return "Invalid point";
9275
9276                 case SEXP_CHECK_NEGATIVE_NUM:
9277                         return "Negative number not allowed";
9278
9279                 case SEXP_CHECK_INVALID_SHIP_WING:
9280                         return "Invalid ship/wing name";
9281
9282                 case SEXP_CHECK_INVALID_SHIP_TYPE:
9283                         return "Invalid ship type";
9284
9285                 case SEXP_CHECK_UNKNOWN_MESSAGE:
9286                         return "Invalid message name";
9287
9288                 case SEXP_CHECK_INVALID_PRIORITY:
9289                         return "Invalid priority";
9290
9291                 case SEXP_CHECK_INVALID_MISSION_NAME:
9292                         return "Invalid mission filename";
9293
9294                 case SEXP_CHECK_INVALID_GOAL_NAME:
9295                         return "Invalid goal name";
9296
9297                 case SEXP_CHECK_INVALID_LEVEL:
9298                         return "Mission level too low in tree";
9299
9300                 case SEXP_CHECK_INVALID_MSG_SOURCE:
9301                         return "Invalid message source";
9302
9303                 case SEXP_CHECK_INVALID_DOCKER_POINT:
9304                         return "Invalid docker point";
9305
9306                 case SEXP_CHECK_INVALID_DOCKEE_POINT:
9307                         return "Invalid dockee point";
9308
9309                 case SEXP_CHECK_ORDER_NOT_ALLOWED:
9310                         return "Ship not allowed to have this order";
9311
9312                 case SEXP_CHECK_DOCKING_NOT_ALLOWED:
9313                         return "Ship can't dock with target ship";
9314
9315                 case SEXP_CHECK_NUM_RANGE_INVALID:
9316                         return "Number is out of range";
9317
9318                 case SEXP_CHECK_INVALID_EVENT_NAME:
9319                         return "Event name is invalid (not known)";
9320
9321                 case SEXP_CHECK_INVALID_SKILL_LEVEL:
9322                         return "Skill level name is invalid (not known)";
9323
9324                 case SEXP_CHECK_INVALID_MEDAL_NAME:
9325                         return "Invalid medal name";
9326
9327                 case SEXP_CHECK_INVALID_WEAPON_NAME:
9328                         return "Invalid weapon name";
9329
9330                 case SEXP_CHECK_INVALID_SHIP_CLASS_NAME:
9331                         return "Invalid ship class name";
9332
9333                 case SEXP_CHECK_INVALID_GAUGE_NAME:
9334                         return "Invalid HUD gauges name";
9335
9336                 case SEXP_CHECK_INVALID_JUMP_NODE:
9337                         return "Invalid Jump Node name";
9338         }
9339
9340         SDL_snprintf(Sexp_error_text, SDL_arraysize(Sexp_error_text), "Sexp error code %d", num);
9341         return Sexp_error_text;
9342 }
9343
9344 int query_sexp_ai_goal_valid(int sexp_ai_goal, int ship)
9345 {
9346         int i, op;
9347
9348         for (op=0; op<Num_operators; op++)
9349                 if (Operators[op].value == sexp_ai_goal)
9350                         break;
9351
9352         SDL_assert(op < Num_operators);
9353         for (i=0; i<Num_sexp_ai_goal_links; i++)
9354                 if (Sexp_ai_goal_links[i].op_code == sexp_ai_goal)
9355                         break;
9356
9357         SDL_assert(i < Num_sexp_ai_goal_links);
9358         return ai_query_goal_valid(ship, Sexp_ai_goal_links[i].ai_goal);
9359 }
9360
9361 // Takes an Sexp_node.text pointing to a variable (of form "Sexp_variables[xx]=string" or "Sexp_variables[xx]=number")
9362 // and returns the index into the Sexp_variables array of the actual value 
9363 int extract_sexp_variable_index(int node)
9364 {
9365         char *text = Sexp_nodes[node].text;
9366         char char_index[8];
9367         char *start_index;
9368         int variable_index;
9369
9370         // get past the '['
9371         start_index = text + 15;
9372         SDL_assert(isdigit(*start_index));
9373
9374         int len = 0;
9375
9376         while ( *start_index != ']' ) {
9377                 char_index[len++] = *(start_index++);
9378                 SDL_assert(len < 3);
9379         }
9380
9381         SDL_assert(len > 0);
9382         char_index[len] = 0;    // append null termination to string
9383
9384         variable_index = atoi(char_index);
9385         SDL_assert( (variable_index >= 0) && (variable_index < MAX_SEXP_VARIABLES) );
9386
9387         return variable_index;
9388 }
9389
9390
9391 // wrapper around Sexp_node[xx].text for normal and variable
9392 char *CTEXT(int n)
9393 {
9394         if (Sexp_nodes[n].type & SEXP_FLAG_VARIABLE) {
9395                 int sexp_variable_index;
9396                 if (Fred_running) {
9397                         sexp_variable_index = get_index_sexp_variable_name(Sexp_nodes[n].text);
9398                         SDL_assert(sexp_variable_index != -1);
9399                 } else {
9400 //                      sexp_variable_index = extract_sexp_variable_index(n);
9401                         sexp_variable_index = atoi(Sexp_nodes[n].text);
9402                 }
9403                 // Reference a Sexp_variable
9404                 // string format -- "Sexp_variables[xx]=number" or "Sexp_variables[xx]=string", where xx is the index
9405
9406                 SDL_assert( !(Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_NOT_USED) );
9407                 SDL_assert(Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_SET);
9408
9409                 return Sexp_variables[sexp_variable_index].text;
9410         } else {
9411                 return Sexp_nodes[n].text;
9412         }
9413 }
9414
9415
9416 // Set all Sexp_variables to type uninitialized
9417 void init_sexp_vars()
9418 {
9419         for (int i=0; i<MAX_SEXP_VARIABLES; i++) {
9420 //              if ( !(Sexp_nodes[i].type & SEXP_FLAG_PERSISTENT) )
9421 //                      Sexp_nodes[i].type = SEXP_NOT_USED;
9422                 Sexp_variables[i].type = SEXP_VARIABLE_NOT_USED;
9423         }
9424 }
9425
9426
9427 // Adds an Sexp_variable to be used in a mission.
9428 // This should be called from within mission parse.
9429 int sexp_add_variable(const char *text, const char *var_name, int type, int index)
9430 {
9431         // if index == -1, find next open slot
9432         if (index == -1) {
9433                 for (int i=0; i<MAX_SEXP_VARIABLES; i++) {
9434                         if (Sexp_variables[i].type == SEXP_VARIABLE_NOT_USED) {
9435                                 index = i;
9436                                 break;
9437                         }
9438                 }
9439         } else {
9440                 SDL_assert( (index >= 0) && (index < MAX_SEXP_VARIABLES) );
9441         }
9442
9443         if (index >= 0) {
9444                 SDL_strlcpy(Sexp_variables[index].text, text, TOKEN_LENGTH);
9445                 SDL_strlcpy(Sexp_variables[index].variable_name, var_name, TOKEN_LENGTH);
9446                 Sexp_variables[index].type &= ~SEXP_VARIABLE_NOT_USED;
9447                 Sexp_variables[index].type = (type | SEXP_VARIABLE_SET);
9448         }
9449
9450         return index;
9451 }
9452
9453
9454 // Modifies and Sexp_variable to be used in a mission
9455 // This should be called in mission when an sexp_variable is to be modified
9456 void sexp_modify_variable(char *text, int index)
9457 {
9458         SDL_assert(index >= 0 && index < MAX_SEXP_VARIABLES);
9459         SDL_assert(Sexp_variables[index].type & SEXP_VARIABLE_SET);
9460         SDL_assert( !MULTIPLAYER_CLIENT );
9461
9462         SDL_strlcpy(Sexp_variables[index].text, text, TOKEN_LENGTH);
9463         Sexp_variables[index].type |= SEXP_VARIABLE_MODIFIED;
9464
9465         // do multi_callback_here
9466 }
9467
9468 void sexp_modify_variable(int n)
9469 {
9470         int sexp_variable_index;
9471         int new_number;
9472 //      char *new_char_var;
9473         char number_as_str[TOKEN_LENGTH];
9474
9475         // Only do single player of multi host
9476         if ( MULTIPLAYER_CLIENT ) {
9477                 return;
9478         }
9479
9480
9481         if (n != -1) {
9482                 // get sexp_variable index
9483                 SDL_assert(Sexp_nodes[n].first == -1);
9484                 sexp_variable_index = atoi(Sexp_nodes[n].text);
9485
9486                 // verify variable set
9487                 SDL_assert(Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_SET);
9488
9489                 if (Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_NUMBER) {
9490                         // get new numerical value
9491                         new_number = eval_sexp(Sexp_nodes[n].rest);
9492
9493                         SDL_snprintf(number_as_str, SDL_arraysize(number_as_str), "%d", new_number);
9494                         sexp_modify_variable(number_as_str, sexp_variable_index);
9495                 } else {
9496                         // get new string
9497                         SDL_assert(Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_STRING);
9498
9499                         char *new_text = Sexp_nodes[Sexp_nodes[n].rest].text;
9500                         sexp_modify_variable(new_text, sexp_variable_index);
9501                 }
9502         }
9503 }
9504         
9505
9506
9507 // Different type needed for Fred (1) allow modification of type (2) no callback required
9508 void sexp_fred_modify_variable(const char *text, const char *var_name, int index, int type)
9509 {
9510         SDL_assert(index >= 0 && index < MAX_SEXP_VARIABLES);
9511         SDL_assert(Sexp_variables[index].type & SEXP_VARIABLE_SET);
9512         SDL_assert( (type & SEXP_VARIABLE_NUMBER) || (type & SEXP_VARIABLE_STRING) );
9513
9514         SDL_strlcpy(Sexp_variables[index].text, text, TOKEN_LENGTH);
9515         SDL_strlcpy(Sexp_variables[index].variable_name, var_name, TOKEN_LENGTH);
9516         Sexp_variables[index].type = (SEXP_VARIABLE_SET | SEXP_VARIABLE_MODIFIED | type);
9517 }
9518
9519 // return index of sexp_variable_name, -1 if not found
9520 int get_index_sexp_variable_name(const char* temp_name)
9521 {
9522         for (int i=0; i<MAX_SEXP_VARIABLES; i++) {
9523                 if (Sexp_variables[i].type & SEXP_VARIABLE_SET) {
9524                         // check case sensitive
9525                         if ( !strcmp(Sexp_variables[i].variable_name, temp_name) ) {
9526                                 return i;
9527                         }
9528                 }
9529         }
9530
9531         // not found
9532         return -1;
9533 }
9534
9535
9536 // counts number of sexp_variables that are set
9537 int sexp_variable_count()
9538 {
9539         int count = 0;
9540
9541         for (int i=0; i<MAX_SEXP_VARIABLES; i++) {
9542                 if ( (Sexp_variables[i].type & SEXP_VARIABLE_SET)  && !(Sexp_variables[i].type & SEXP_VARIABLE_BLOCK) ) {
9543                         count++;
9544                 }
9545         }
9546
9547         return count;
9548 }
9549
9550
9551 // deletes sexp_variable from active
9552 void sexp_variable_delete(int index)
9553 {
9554         SDL_assert(Sexp_variables[index].type & SEXP_VARIABLE_SET);
9555
9556         Sexp_variables[index].type = SEXP_VARIABLE_NOT_USED;
9557 }
9558
9559 int sexp_var_compare(const void *var1, const void *var2)
9560 {
9561         int set1, set2;
9562         sexp_variable *sexp_var1, *sexp_var2;
9563
9564         sexp_var1 = (sexp_variable*) var1;
9565         sexp_var2 = (sexp_variable*) var2;
9566
9567         set1 = sexp_var1->type & SEXP_VARIABLE_SET;
9568         set2 = sexp_var2->type & SEXP_VARIABLE_SET;
9569
9570         if (!set1 && !set2) {
9571                 return 0;
9572         } else if (set1 && !set2) {
9573                 return -1;
9574         } else if (!set1 && set2) {
9575                 return 1;
9576         } else {
9577                 return SDL_strcasecmp( sexp_var1->variable_name, sexp_var2->variable_name);
9578         }
9579 }
9580
9581 // Count number of variables in block
9582 int sexp_variable_block_count()
9583 {
9584         int count = 0;
9585         for (int i=0; i<MAX_SEXP_VARIABLES; i++) {
9586                 if (Sexp_variables[i].type & SEXP_VARIABLE_BLOCK) {
9587                         count++;
9588                 }
9589         }
9590
9591         return count;
9592 }
9593
9594 // Sort sexp_variable list lexigraphically, with set before unset
9595 void sexp_variable_sort()
9596 {
9597         qsort( (void *)Sexp_variables, (size_t)(MAX_SEXP_VARIABLES - sexp_variable_block_count()), sizeof(sexp_variable), sexp_var_compare );
9598 }
9599
9600 int sexp_variable_allocate_block(const char* block_name, int block_type)
9601 {
9602         int num_blocks, block_count, var_count, start;
9603         block_count = sexp_variable_block_count();
9604         var_count = sexp_variable_count();
9605
9606         if (block_type & SEXP_VARIABLE_BLOCK_EXP) {
9607                 num_blocks = BLOCK_EXP_SIZE;
9608         } else {
9609                 Int3(); // add new block type here with size
9610                 return -1;
9611         }
9612
9613         if (block_count + var_count > (MAX_SEXP_VARIABLES - num_blocks)) {
9614                 // not enough free space
9615                 return -1;
9616         }
9617
9618         // squeeze all variables to front of array
9619         sexp_variable_sort();
9620
9621         // squeeze all block to end of array
9622         sexp_variable_condense_block();
9623
9624         start = MAX_SEXP_VARIABLES - block_count - num_blocks;
9625
9626         for (int idx=start; idx<start+num_blocks; idx++) {
9627                 SDL_assert(Sexp_variables[idx].type == SEXP_VARIABLE_NOT_USED);
9628                 Sexp_variables[idx].type = SEXP_VARIABLE_BLOCK | block_type;
9629                 SDL_strlcpy(Sexp_variables[idx].variable_name, block_name, TOKEN_LENGTH);
9630         }
9631
9632         return start;
9633 }
9634
9635 // squeeze all blocks to top of array
9636 void sexp_variable_condense_block()
9637 {
9638         int temp_idx, idx, var_count;
9639
9640         var_count = sexp_variable_count();
9641         temp_idx = MAX_SEXP_VARIABLES-1;
9642
9643         for (idx=MAX_SEXP_VARIABLES-1; idx>var_count-1; idx--) {
9644                 if (Sexp_variables[idx].type & SEXP_VARIABLE_BLOCK) {
9645                         if (temp_idx > idx) {
9646                                 Sexp_variables[temp_idx] = Sexp_variables[idx];
9647                                 Sexp_variables[idx].type = SEXP_VARIABLE_NOT_USED;
9648                         }
9649                         temp_idx--;
9650                 }
9651         }
9652 }
9653
9654
9655 void sexp_variable_block_free(const char *ship_name, int start_index, int block_type)
9656 {
9657         int num_blocks;
9658
9659         if (block_type & SEXP_VARIABLE_BLOCK_EXP) {
9660                 num_blocks = BLOCK_EXP_SIZE;
9661         } else {
9662                 Int3();  // new type of block
9663                 return;
9664         }
9665
9666         for (int i=start_index; i<(start_index + num_blocks); i++) {
9667                 SDL_assert(!SDL_strcasecmp(Sexp_variables[i].variable_name, ship_name));
9668
9669                 SDL_assert(Sexp_variables[i].type & block_type);
9670
9671                 Sexp_variables[i].type = SEXP_VARIABLE_NOT_USED;
9672         }
9673
9674         sexp_variable_condense_block();
9675 }
9676
9677 // evaluate number which may result from an operator or may be text
9678 // only useful for first operand
9679 int num_eval(int node)
9680 {
9681         if ( CAR(node) != -1 ) {
9682                 return eval_sexp(CAR(node));
9683         } else {
9684                 return atoi(CTEXT(node));
9685         }
9686 }
9687
9688