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