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