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