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