FS2_Open
Open source remastering of the Freespace 2 engine
sexp.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) Volition, Inc. 1999. All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell
5  * or otherwise commercially exploit the source or things you created based on the
6  * source.
7  *
8 */
9 
10 
11 
12 // Parse a symbolic expression.
13 // These are identical to Lisp functions.
14 // It uses a very baggy format, allocating 16 characters per token, regardless
15 // of how many are used.
16 
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <ctype.h>
21 #include <assert.h>
22 #include <limits.h>
23 #if defined _MSC_VER && _MSC_VER < 1600
24  #include "globalincs/msvc/stdint.h"
25 #else
26  #include <stdint.h>
27 #endif
28 
29 #include "ai/aigoals.h"
30 #include "asteroid/asteroid.h"
31 #include "autopilot/autopilot.h"
32 #include "camera/camera.h"
33 #include "cmdline/cmdline.h"
34 #include "debugconsole/console.h"
35 #include "fireball/fireballs.h" // for explosion stuff
36 #include "freespace2/freespace.h"
38 #include "gamesnd/eventmusic.h" // for change-soundtrack
39 #include "gamesnd/gamesnd.h"
40 #include "globalincs/alphacolors.h"
41 #include "globalincs/linklist.h"
42 #include "globalincs/systemvars.h"
43 #include "globalincs/version.h"
44 #include "graphics/2d.h"
45 #include "graphics/font.h"
46 #include "hud/hud.h"
47 #include "hud/hudartillery.h"
48 #include "hud/hudconfig.h"
49 #include "hud/hudescort.h"
50 #include "hud/hudets.h"
51 #include "hud/hudmessage.h"
52 #include "hud/hudparse.h"
53 #include "hud/hudshield.h"
54 #include "hud/hudsquadmsg.h" // for the order sexp
55 #include "iff_defs/iff_defs.h"
56 #include "io/keycontrol.h"
57 #include "io/timer.h"
58 #include "jumpnode/jumpnode.h"
59 #include "localization/localize.h"
60 #include "math/fvi.h"
61 #include "menuui/techmenu.h" // for intel stuff
64 #include "mission/missiongoals.h"
65 #include "mission/missionlog.h"
66 #include "mission/missionmessage.h"
67 #include "mission/missionparse.h" // for p_object definition
69 #include "missionui/redalert.h"
70 #include "mod_table/mod_table.h"
71 #include "nebula/neb.h"
72 #include "nebula/neblightning.h"
73 #include "network/multi.h"
74 #include "network/multi_obj.h"
75 #include "network/multi_sexp.h"
76 #include "network/multi_team.h"
77 #include "network/multimsgs.h"
78 #include "network/multiutil.h"
79 #include "object/objcollide.h"
80 #include "object/objectdock.h"
81 #include "object/objectshield.h"
82 #include "object/objectsnd.h"
83 #include "object/waypoint.h"
84 #include "parse/generic_log.h"
85 #include "parse/lua.h"
86 #include "parse/parselo.h"
87 #include "parse/scripting.h"
88 #include "parse/sexp.h"
89 #include "playerman/player.h"
90 #include "render/3d.h"
91 #include "ship/afterburner.h"
92 #include "ship/awacs.h"
93 #include "ship/ship.h"
94 #include "ship/shiphit.h"
95 #include "sound/audiostr.h"
96 #include "sound/ds.h"
97 #include "sound/sound.h"
98 #include "starfield/starfield.h"
99 #include "starfield/supernova.h"
100 #include "stats/medals.h"
101 #include "weapon/beam.h"
102 #include "weapon/emp.h"
103 #include "weapon/shockwave.h"
104 #include "weapon/weapon.h"
105 
106 #ifndef NDEBUG
107 #include "hud/hudmessage.h"
108 #endif
109 
110 
111 
112 
113 
114 #define TRUE 1
115 #define FALSE 0
116 
117 
119 // Operator, Identity, Min / Max arguments
120  //Arithmetic Category
121  { "+", OP_PLUS, 2, INT_MAX, SEXP_ARITHMETIC_OPERATOR, },
122  { "-", OP_MINUS, 2, INT_MAX, SEXP_ARITHMETIC_OPERATOR, },
123  { "*", OP_MUL, 2, INT_MAX, SEXP_ARITHMETIC_OPERATOR, },
124  { "/", OP_DIV, 2, INT_MAX, SEXP_ARITHMETIC_OPERATOR, },
125  { "mod", OP_MOD, 2, INT_MAX, SEXP_ARITHMETIC_OPERATOR, },
126  { "rand", OP_RAND, 2, 3, SEXP_ARITHMETIC_OPERATOR, },
127  { "rand-multiple", OP_RAND_MULTIPLE, 2, 3, SEXP_ARITHMETIC_OPERATOR, }, // Goober5000
128  { "abs", OP_ABS, 1, 1, SEXP_ARITHMETIC_OPERATOR, }, // Goober5000
129  { "min", OP_MIN, 1, INT_MAX, SEXP_ARITHMETIC_OPERATOR, }, // Goober5000
130  { "max", OP_MAX, 1, INT_MAX, SEXP_ARITHMETIC_OPERATOR, }, // Goober5000
131  { "avg", OP_AVG, 1, INT_MAX, SEXP_ARITHMETIC_OPERATOR, }, // Goober5000
132  { "pow", OP_POW, 2, 2, SEXP_ARITHMETIC_OPERATOR, }, // Goober5000
133  { "signum", OP_SIGNUM, 1, 1, SEXP_ARITHMETIC_OPERATOR, }, // Goober5000
134  { "set-bit", OP_SET_BIT, 2, 2, SEXP_ARITHMETIC_OPERATOR, }, // Goober5000
135  { "unset-bit", OP_UNSET_BIT, 2, 2, SEXP_ARITHMETIC_OPERATOR, }, // Goober5000
136  { "is-bit-set", OP_IS_BIT_SET, 2, 2, SEXP_BOOLEAN_OPERATOR, }, // Goober5000
137  { "bitwise-and", OP_BITWISE_AND, 2, INT_MAX, SEXP_ARITHMETIC_OPERATOR, }, // Goober5000
138  { "bitwise-or", OP_BITWISE_OR, 2, INT_MAX, SEXP_ARITHMETIC_OPERATOR, }, // Goober5000
139  { "bitwise-not", OP_BITWISE_NOT, 1, 1, SEXP_ARITHMETIC_OPERATOR, }, // Goober5000
140  { "bitwise-xor", OP_BITWISE_XOR, 2, 2, SEXP_ARITHMETIC_OPERATOR, }, // Goober5000
141 
142  //Logical Category
143  { "true", OP_TRUE, 0, 0, SEXP_BOOLEAN_OPERATOR, },
144  { "false", OP_FALSE, 0, 0, SEXP_BOOLEAN_OPERATOR, },
145  { "and", OP_AND, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
146  { "and-in-sequence", OP_AND_IN_SEQUENCE, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
147  { "or", OP_OR, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
148  { "not", OP_NOT, 1, 1, SEXP_BOOLEAN_OPERATOR, },
149  { "xor", OP_XOR, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, }, // Goober5000
150  { "=", OP_EQUALS, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
151  { "!=", OP_NOT_EQUAL, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, }, // Goober5000
152  { ">", OP_GREATER_THAN, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
153  { ">=", OP_GREATER_OR_EQUAL, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, }, // Goober5000
154  { "<", OP_LESS_THAN, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
155  { "<=", OP_LESS_OR_EQUAL, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, }, // Goober5000
156  { "string-equals", OP_STRING_EQUALS, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
157  { "string-greater-than", OP_STRING_GREATER_THAN, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
158  { "string-less-than", OP_STRING_LESS_THAN, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
159  { "perform-actions", OP_PERFORM_ACTIONS, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, }, // Goober5000
160  { "has-time-elapsed", OP_HAS_TIME_ELAPSED, 1, 1, SEXP_BOOLEAN_OPERATOR, },
161 
162  //Event/Goals Category
163  { "is-goal-true-delay", OP_GOAL_TRUE_DELAY, 2, 2, SEXP_BOOLEAN_OPERATOR, },
164  { "is-goal-false-delay", OP_GOAL_FALSE_DELAY, 2, 2, SEXP_BOOLEAN_OPERATOR, },
165  { "is-goal-incomplete", OP_GOAL_INCOMPLETE, 1, 1, SEXP_BOOLEAN_OPERATOR, },
166  { "is-event-true", OP_EVENT_TRUE, 1, 1, SEXP_BOOLEAN_OPERATOR, },
167  { "is-event-true-delay", OP_EVENT_TRUE_DELAY, 2, 3, SEXP_BOOLEAN_OPERATOR, },
168  { "is-event-true-msecs-delay", OP_EVENT_TRUE_MSECS_DELAY, 2, 3, SEXP_BOOLEAN_OPERATOR, },
169  { "is-event-false", OP_EVENT_FALSE, 1, 1, SEXP_BOOLEAN_OPERATOR, },
170  { "is-event-false-delay", OP_EVENT_FALSE_DELAY, 2, 3, SEXP_BOOLEAN_OPERATOR, },
171  { "is-event-false-msecs-delay", OP_EVENT_FALSE_MSECS_DELAY, 2, 3, SEXP_BOOLEAN_OPERATOR, },
172  { "is-event-incomplete", OP_EVENT_INCOMPLETE, 1, 1, SEXP_BOOLEAN_OPERATOR, },
173  { "is-previous-goal-true", OP_PREVIOUS_GOAL_TRUE, 2, 3, SEXP_BOOLEAN_OPERATOR, },
174  { "is-previous-goal-false", OP_PREVIOUS_GOAL_FALSE, 2, 3, SEXP_BOOLEAN_OPERATOR, },
175  { "is-previous-goal-incomplete", OP_PREVIOUS_GOAL_INCOMPLETE, 2, 3, SEXP_BOOLEAN_OPERATOR, },
176  { "is-previous-event-true", OP_PREVIOUS_EVENT_TRUE, 2, 3, SEXP_BOOLEAN_OPERATOR, },
177  { "is-previous-event-false", OP_PREVIOUS_EVENT_FALSE, 2, 3, SEXP_BOOLEAN_OPERATOR, },
178  { "is-previous-event-incomplete", OP_PREVIOUS_EVENT_INCOMPLETE, 2, 3, SEXP_BOOLEAN_OPERATOR, },
179 
180  //Objectives Category
181  { "is-destroyed", OP_IS_DESTROYED, 1, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
182  { "is-destroyed-delay", OP_IS_DESTROYED_DELAY, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
183  { "was-destroyed-by-delay", OP_WAS_DESTROYED_BY_DELAY, 3, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
184  { "is-subsystem-destroyed", OP_IS_SUBSYSTEM_DESTROYED, 2, 2, SEXP_BOOLEAN_OPERATOR, },
185  { "is-subsystem-destroyed-delay", OP_IS_SUBSYSTEM_DESTROYED_DELAY, 3, 3, SEXP_BOOLEAN_OPERATOR, },
186  { "is-disabled", OP_IS_DISABLED, 1, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
187  { "is-disabled-delay", OP_IS_DISABLED_DELAY, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
188  { "is-disarmed", OP_IS_DISARMED, 1, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
189  { "is-disarmed-delay", OP_IS_DISARMED_DELAY, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
190  { "has-docked", OP_HAS_DOCKED, 3, 3, SEXP_BOOLEAN_OPERATOR, },
191  { "has-docked-delay", OP_HAS_DOCKED_DELAY, 4, 4, SEXP_BOOLEAN_OPERATOR, },
192  { "has-undocked", OP_HAS_UNDOCKED, 3, 3, SEXP_BOOLEAN_OPERATOR, },
193  { "has-undocked-delay", OP_HAS_UNDOCKED_DELAY, 4, 4, SEXP_BOOLEAN_OPERATOR, },
194  { "has-arrived", OP_HAS_ARRIVED, 1, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
195  { "has-arrived-delay", OP_HAS_ARRIVED_DELAY, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
196  { "has-departed", OP_HAS_DEPARTED, 1, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
197  { "has-departed-delay", OP_HAS_DEPARTED_DELAY, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
198  { "are-waypoints-done", OP_WAYPOINTS_DONE, 2, 2, SEXP_BOOLEAN_OPERATOR, },
199  { "are-waypoints-done-delay", OP_WAYPOINTS_DONE_DELAY, 3, 4, SEXP_BOOLEAN_OPERATOR, },
200  { "is-nav-visited", OP_NAV_IS_VISITED, 1, 1, SEXP_BOOLEAN_OPERATOR, }, // Kazan
201  { "ship-type-destroyed", OP_SHIP_TYPE_DESTROYED, 2, 2, SEXP_BOOLEAN_OPERATOR, },
202  { "percent-ships-destroyed", OP_PERCENT_SHIPS_DESTROYED, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
203  { "percent-ships-disabled", OP_PERCENT_SHIPS_DISABLED, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
204  { "percent-ships-disarmed", OP_PERCENT_SHIPS_DISARMED, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
205  { "percent-ships-departed", OP_PERCENT_SHIPS_DEPARTED, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
206  { "percent-ships-arrived", OP_PERCENT_SHIPS_ARRIVED, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
207  { "depart-node-delay", OP_DEPART_NODE_DELAY, 3, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
208  { "destroyed-or-departed-delay", OP_DESTROYED_DEPARTED_DELAY, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
209 
210  //Status Category
211  //Mission Sub-Category
212  { "num-ships-in-battle", OP_NUM_SHIPS_IN_BATTLE, 0, INT_MAX, SEXP_INTEGER_OPERATOR, }, //phreak modified by FUBAR
213  { "num-ships-in-wing", OP_NUM_SHIPS_IN_WING, 1, INT_MAX, SEXP_INTEGER_OPERATOR, }, // Karajorma
214  { "directive-value", OP_DIRECTIVE_VALUE, 1, 2, SEXP_INTEGER_OPERATOR, }, // Karajorma
215 
216  //Player Sub-Category
217  { "was-promotion-granted", OP_WAS_PROMOTION_GRANTED, 0, 1, SEXP_BOOLEAN_OPERATOR, },
218  { "was-medal-granted", OP_WAS_MEDAL_GRANTED, 0, 1, SEXP_BOOLEAN_OPERATOR, },
219  { "skill-level-at-least", OP_SKILL_LEVEL_AT_LEAST, 1, 1, SEXP_BOOLEAN_OPERATOR, },
220  { "num_kills", OP_NUM_KILLS, 1, 1, SEXP_INTEGER_OPERATOR, },
221  { "num_assists", OP_NUM_ASSISTS, 1, 1, SEXP_INTEGER_OPERATOR, },
222  { "num_type_kills", OP_NUM_TYPE_KILLS, 2, 2, SEXP_INTEGER_OPERATOR, },
223  { "num_class_kills", OP_NUM_CLASS_KILLS, 2, 2, SEXP_INTEGER_OPERATOR, },
224  { "ship_score", OP_SHIP_SCORE, 1, 1, SEXP_INTEGER_OPERATOR, },
225  { "time-elapsed-last-order", OP_LAST_ORDER_TIME, 2, 2, SEXP_INTEGER_OPERATOR, },
226  { "player-is-cheating", OP_PLAYER_IS_CHEATING_BASTARD, 0, 0, SEXP_BOOLEAN_OPERATOR, },
227 
228  //Multiplayer Sub-Category
229  { "num-players", OP_NUM_PLAYERS, 0, 0, SEXP_INTEGER_OPERATOR, },
230  { "team-score", OP_TEAM_SCORE, 1, 1, SEXP_INTEGER_OPERATOR, },
231  { "ship-deaths", OP_SHIP_DEATHS, 1, 1, SEXP_INTEGER_OPERATOR, },
232  { "respawns-left", OP_RESPAWNS_LEFT, 1, 1, SEXP_INTEGER_OPERATOR, },
233  { "is-player", OP_IS_PLAYER, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, }, // Karajorma
234 
235  //Ship Status Sub-Category
236  { "is-in-mission", OP_IS_IN_MISSION, 1, INT_MAX, SEXP_BOOLEAN_OPERATOR, }, // Goober5000
237  { "is-ship-visible", OP_IS_SHIP_VISIBLE, 1, 1, SEXP_BOOLEAN_OPERATOR, },
238  { "is-ship-stealthy", OP_IS_SHIP_STEALTHY, 1, 1, SEXP_BOOLEAN_OPERATOR, },
239  { "is-friendly-stealth-visible", OP_IS_FRIENDLY_STEALTH_VISIBLE, 1, 1, SEXP_BOOLEAN_OPERATOR, },
240  { "is-iff", OP_IS_IFF, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
241  { "is-ai-class", OP_IS_AI_CLASS, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
242  { "is-ship-type", OP_IS_SHIP_TYPE, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
243  { "is-ship-class", OP_IS_SHIP_CLASS, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
244  { "is-facing", OP_IS_FACING, 3, 4, SEXP_BOOLEAN_OPERATOR, },
245  { "is_tagged", OP_IS_TAGGED, 1, 1, SEXP_BOOLEAN_OPERATOR, },
246  { "has-been-tagged-delay", OP_HAS_BEEN_TAGGED_DELAY, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
247  { "are-ship-flags-set", OP_ARE_SHIP_FLAGS_SET, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, }, // Karajorma
248 
249  //Shields, Engines and Weapons Sub-Category
250  { "has-primary-weapon", OP_HAS_PRIMARY_WEAPON, 3, INT_MAX, SEXP_BOOLEAN_OPERATOR, }, // Karajorma
251  { "has-secondary-weapon", OP_HAS_SECONDARY_WEAPON, 3, INT_MAX, SEXP_BOOLEAN_OPERATOR, }, // Karajorma
252  { "is-primary-selected", OP_IS_PRIMARY_SELECTED, 2, 2, SEXP_BOOLEAN_OPERATOR, },
253  { "is-secondary-selected", OP_IS_SECONDARY_SELECTED, 2, 2, SEXP_BOOLEAN_OPERATOR, },
254  { "primary-fired-since", OP_PRIMARY_FIRED_SINCE, 3, 3, SEXP_INTEGER_OPERATOR, }, // Karajorma
255  { "secondary-fired-since", OP_SECONDARY_FIRED_SINCE, 3, 3, SEXP_INTEGER_OPERATOR, }, // Karajorma
256  { "primary-ammo-pct", OP_PRIMARY_AMMO_PCT, 2, 2, SEXP_INTEGER_OPERATOR, },
257  { "secondary-ammo-pct", OP_SECONDARY_AMMO_PCT, 2, 2, SEXP_INTEGER_OPERATOR, },
258  { "get-primary-ammo", OP_GET_PRIMARY_AMMO, 2, 2, SEXP_INTEGER_OPERATOR, }, // Karajorma
259  { "get-secondary-ammo", OP_GET_SECONDARY_AMMO, 2, 2, SEXP_INTEGER_OPERATOR, }, // Karajorma
260  { "get-num-countermeasures", OP_GET_NUM_COUNTERMEASURES, 1, 1, SEXP_INTEGER_OPERATOR, }, // Karajorma
261  { "weapon-energy-pct", OP_WEAPON_ENERGY_LEFT, 1, 1, SEXP_INTEGER_OPERATOR, },
262  { "afterburner-energy-pct", OP_AFTERBURNER_LEFT, 1, 1, SEXP_INTEGER_OPERATOR, },
263  { "shield-recharge-pct", OP_SHIELD_RECHARGE_PCT, 1, 1, SEXP_INTEGER_OPERATOR, },
264  { "weapon-recharge-pct", OP_WEAPON_RECHARGE_PCT, 1, 1, SEXP_INTEGER_OPERATOR, },
265  { "engine-recharge-pct", OP_ENGINE_RECHARGE_PCT, 1, 1, SEXP_INTEGER_OPERATOR, },
266  { "shield-quad-low", OP_SHIELD_QUAD_LOW, 2, 2, SEXP_INTEGER_OPERATOR, },
267  { "get-throttle-speed", OP_GET_THROTTLE_SPEED, 1, 1, SEXP_INTEGER_OPERATOR, }, // Karajorma
268  { "current-speed", OP_CURRENT_SPEED, 1, 1, SEXP_INTEGER_OPERATOR, },
269 
270  //Cargo Sub-Category
271  { "is-cargo-known", OP_IS_CARGO_KNOWN, 1, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
272  { "is-cargo-known-delay", OP_CARGO_KNOWN_DELAY, 2, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
273  { "cap-subsys-cargo-known-delay", OP_CAP_SUBSYS_CARGO_KNOWN_DELAY, 3, INT_MAX, SEXP_BOOLEAN_OPERATOR, },
274  { "is-cargo", OP_IS_CARGO, 2, 3, SEXP_BOOLEAN_OPERATOR, },
275 
276  //Damage Sub-Category
277  { "shields-left", OP_SHIELDS_LEFT, 1, 1, SEXP_INTEGER_OPERATOR, },
278  { "hits-left", OP_HITS_LEFT, 1, 1, SEXP_INTEGER_OPERATOR, },
279  { "hits-left-subsystem", OP_HITS_LEFT_SUBSYSTEM, 2, 3, SEXP_INTEGER_OPERATOR, },
280  { "hits-left-subsystem-generic", OP_HITS_LEFT_SUBSYSTEM_GENERIC, 2, 2, SEXP_INTEGER_OPERATOR, }, // Goober5000
281  { "hits-left-subsystem-specific", OP_HITS_LEFT_SUBSYSTEM_SPECIFIC, 2, 2, SEXP_INTEGER_OPERATOR, }, // Goober5000
282  { "sim-hits-left", OP_SIM_HITS_LEFT, 1, 1, SEXP_INTEGER_OPERATOR, }, // Turey
283  { "get-damage-caused", OP_GET_DAMAGE_CAUSED, 2, INT_MAX, SEXP_INTEGER_OPERATOR, },
284 
285  //Distance and Coordinates Sub-Category
286  { "distance", OP_DISTANCE, 2, 2, SEXP_INTEGER_OPERATOR, },
287  { "distance-ship-subsystem", OP_DISTANCE_SUBSYSTEM, 3, 3, SEXP_INTEGER_OPERATOR, }, // Goober5000
288  { "distance-to-nav", OP_NAV_DISTANCE, 1, 1, SEXP_INTEGER_OPERATOR, }, // Kazan
289  { "num-within-box", OP_NUM_WITHIN_BOX, 7, INT_MAX, SEXP_INTEGER_OPERATOR, }, //WMC
290  { "is-in-box", OP_IS_IN_BOX, 7, 8, SEXP_INTEGER_OPERATOR, }, //Sushi
291  { "special-warp-dist", OP_SPECIAL_WARP_DISTANCE, 1, 1, SEXP_INTEGER_OPERATOR, },
292  { "get-object-x", OP_GET_OBJECT_X, 1, 5, SEXP_INTEGER_OPERATOR, }, // Goober5000
293  { "get-object-y", OP_GET_OBJECT_Y, 1, 5, SEXP_INTEGER_OPERATOR, }, // Goober5000
294  { "get-object-z", OP_GET_OBJECT_Z, 1, 5, SEXP_INTEGER_OPERATOR, }, // Goober5000
295  { "get-object-pitch", OP_GET_OBJECT_PITCH, 1, 1, SEXP_INTEGER_OPERATOR, }, // Goober5000
296  { "get-object-bank", OP_GET_OBJECT_BANK, 1, 1, SEXP_INTEGER_OPERATOR, }, // Goober5000
297  { "get-object-heading", OP_GET_OBJECT_HEADING, 1, 1, SEXP_INTEGER_OPERATOR, }, // Goober5000
298  { "get-object-speed-x", OP_GET_OBJECT_SPEED_X, 1, 2, SEXP_INTEGER_OPERATOR, },
299  { "get-object-speed-y", OP_GET_OBJECT_SPEED_Y, 1, 2, SEXP_INTEGER_OPERATOR, },
300  { "get-object-speed-z", OP_GET_OBJECT_SPEED_Z, 1, 2, SEXP_INTEGER_OPERATOR, },
301 
302  //Variables Sub-Category
303  { "string-to-int", OP_STRING_TO_INT, 1, 1, SEXP_INTEGER_OPERATOR, }, // Karajorma
304  { "string-get-length", OP_STRING_GET_LENGTH, 1, 1, SEXP_INTEGER_OPERATOR, }, // Goober5000
305 
306  //Other Sub-Category
307  { "script-eval-num", OP_SCRIPT_EVAL_NUM, 1, 1, SEXP_INTEGER_OPERATOR, },
308  { "script-eval-string", OP_SCRIPT_EVAL_STRING, 2, 2, SEXP_ACTION_OPERATOR, },
309 
310  //Time Category
311  { "time-ship-destroyed", OP_TIME_SHIP_DESTROYED, 1, 1, SEXP_INTEGER_OPERATOR, },
312  { "time-ship-arrived", OP_TIME_SHIP_ARRIVED, 1, 1, SEXP_INTEGER_OPERATOR, },
313  { "time-ship-departed", OP_TIME_SHIP_DEPARTED, 1, 1, SEXP_INTEGER_OPERATOR, },
314  { "time-wing-destroyed", OP_TIME_WING_DESTROYED, 1, 1, SEXP_INTEGER_OPERATOR, },
315  { "time-wing-arrived", OP_TIME_WING_ARRIVED, 1, 1, SEXP_INTEGER_OPERATOR, },
316  { "time-wing-departed", OP_TIME_WING_DEPARTED, 1, 1, SEXP_INTEGER_OPERATOR, },
317  { "mission-time", OP_MISSION_TIME, 0, 0, SEXP_INTEGER_OPERATOR, },
318  { "mission-time-msecs", OP_MISSION_TIME_MSECS, 0, 0, SEXP_INTEGER_OPERATOR, }, // Goober5000
319  { "time-docked", OP_TIME_DOCKED, 3, 3, SEXP_INTEGER_OPERATOR, },
320  { "time-undocked", OP_TIME_UNDOCKED, 3, 3, SEXP_INTEGER_OPERATOR, },
321 
322  //Conditionals Category
323  { "cond", OP_COND, 1, INT_MAX, SEXP_CONDITIONAL_OPERATOR,},
324  { "when", OP_WHEN, 2, INT_MAX, SEXP_CONDITIONAL_OPERATOR,},
325  { "when-argument", OP_WHEN_ARGUMENT, 3, INT_MAX, SEXP_CONDITIONAL_OPERATOR,}, // Goober5000
326  { "every-time", OP_EVERY_TIME, 2, INT_MAX, SEXP_CONDITIONAL_OPERATOR,}, // Goober5000
327  { "every-time-argument", OP_EVERY_TIME_ARGUMENT, 3, INT_MAX, SEXP_CONDITIONAL_OPERATOR,}, // Goober5000
328  { "if-then-else", OP_IF_THEN_ELSE, 3, INT_MAX, SEXP_CONDITIONAL_OPERATOR,}, // Goober5000
329  { "any-of", OP_ANY_OF, 1, INT_MAX, SEXP_ARGUMENT_OPERATOR, }, // Goober5000
330  { "every-of", OP_EVERY_OF, 1, INT_MAX, SEXP_ARGUMENT_OPERATOR, }, // Goober5000
331  { "random-of", OP_RANDOM_OF, 1, INT_MAX, SEXP_ARGUMENT_OPERATOR, }, // Goober5000
332  { "random-multiple-of", OP_RANDOM_MULTIPLE_OF, 1, INT_MAX, SEXP_ARGUMENT_OPERATOR, }, // Karajorma
333  { "number-of", OP_NUMBER_OF, 2, INT_MAX, SEXP_ARGUMENT_OPERATOR, }, // Goober5000
334  { "in-sequence", OP_IN_SEQUENCE, 1, INT_MAX, SEXP_ARGUMENT_OPERATOR, }, // Karajorma
335  { "for-counter", OP_FOR_COUNTER, 2, 3, SEXP_ARGUMENT_OPERATOR, }, // Goober5000
336  { "invalidate-argument", OP_INVALIDATE_ARGUMENT, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, // Goober5000
337  { "invalidate-all-arguments", OP_INVALIDATE_ALL_ARGUMENTS, 0, 0, SEXP_ACTION_OPERATOR, }, // Karajorma
338  { "validate-argument", OP_VALIDATE_ARGUMENT, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, // Karajorma
339  { "validate-all-arguments", OP_VALIDATE_ALL_ARGUMENTS, 0, 0, SEXP_ACTION_OPERATOR, }, // Karajorma
340  { "do-for-valid-arguments", OP_DO_FOR_VALID_ARGUMENTS, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, // Karajorma
341  { "num-valid-arguments", OP_NUM_VALID_ARGUMENTS, 0, 0, SEXP_ACTION_OPERATOR, }, // Karajorma
342 
343  //Change Category
344  //Messaging Sub-Category
345  { "send-message", OP_SEND_MESSAGE, 3, 3, SEXP_ACTION_OPERATOR, },
346  { "send-message-list", OP_SEND_MESSAGE_LIST, 4, INT_MAX, SEXP_ACTION_OPERATOR, },
347  { "send-random-message", OP_SEND_RANDOM_MESSAGE, 3, INT_MAX, SEXP_ACTION_OPERATOR, },
348  { "scramble-messages", OP_SCRAMBLE_MESSAGES, 0, INT_MAX, SEXP_ACTION_OPERATOR, },
349  { "unscramble-messages", OP_UNSCRAMBLE_MESSAGES, 0, INT_MAX, SEXP_ACTION_OPERATOR, },
350  { "disable-builtin-messages", OP_DISABLE_BUILTIN_MESSAGES, 0, INT_MAX, SEXP_ACTION_OPERATOR, }, // Karajorma
351  { "enable-builtin-messages", OP_ENABLE_BUILTIN_MESSAGES, 0, INT_MAX, SEXP_ACTION_OPERATOR, }, // Karajorma
352  { "set-persona", OP_SET_PERSONA, 2, INT_MAX, SEXP_ACTION_OPERATOR, }, // Karajorma
353  { "set-death-message", OP_SET_DEATH_MESSAGE, 1, 1, SEXP_ACTION_OPERATOR, }, // Goober5000
354  { "set-mission-mood", OP_SET_MISSION_MOOD, 1, 1, SEXP_ACTION_OPERATOR, }, // Karajorma
355 
356 
357  //AI Control Sub-Category
358  { "add-goal", OP_ADD_GOAL, 2, 2, SEXP_ACTION_OPERATOR, },
359  { "remove-goal", OP_REMOVE_GOAL, 2, 2, SEXP_ACTION_OPERATOR, }, // Goober5000
360  { "add-ship-goal", OP_ADD_SHIP_GOAL, 2, 2, SEXP_ACTION_OPERATOR, },
361  { "add-wing-goal", OP_ADD_WING_GOAL, 2, 2, SEXP_ACTION_OPERATOR, },
362  { "clear-goals", OP_CLEAR_GOALS, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
363  { "clear-ship-goals", OP_CLEAR_SHIP_GOALS, 1, 1, SEXP_ACTION_OPERATOR, },
364  { "clear-wing-goals", OP_CLEAR_WING_GOALS, 1, 1, SEXP_ACTION_OPERATOR, },
365  { "good-rearm-time", OP_GOOD_REARM_TIME, 2, 2, SEXP_ACTION_OPERATOR, },
366  { "good-secondary-time", OP_GOOD_SECONDARY_TIME, 4, 4, SEXP_ACTION_OPERATOR, },
367  { "change-ai-class", OP_CHANGE_AI_CLASS, 2, INT_MAX, SEXP_ACTION_OPERATOR, },
368  { "player-use-ai", OP_PLAYER_USE_AI, 0, 0, SEXP_ACTION_OPERATOR, }, // Goober5000
369  { "player-not-use-ai", OP_PLAYER_NOT_USE_AI, 0, 0, SEXP_ACTION_OPERATOR, }, // Goober5000
370  { "set-player-orders", OP_SET_PLAYER_ORDERS, 3, INT_MAX, SEXP_ACTION_OPERATOR, }, // Karajorma
371  { "cap-waypoint-speed", OP_CAP_WAYPOINT_SPEED, 2, 2, SEXP_ACTION_OPERATOR, },
372 
373  //Ship Status Sub-Category
374  { "protect-ship", OP_PROTECT_SHIP, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
375  { "unprotect-ship", OP_UNPROTECT_SHIP, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
376  { "beam-protect-ship", OP_BEAM_PROTECT_SHIP, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
377  { "beam-unprotect-ship", OP_BEAM_UNPROTECT_SHIP, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
378  { "turret-protect-ship", OP_TURRET_PROTECT_SHIP, 2, INT_MAX, SEXP_ACTION_OPERATOR, }, // Goober5000
379  { "turret-unprotect-ship", OP_TURRET_UNPROTECT_SHIP, 2, INT_MAX, SEXP_ACTION_OPERATOR, }, // Goober5000
380  { "ship-invisible", OP_SHIP_INVISIBLE, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
381  { "ship-visible", OP_SHIP_VISIBLE, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
382  { "ship-stealthy", OP_SHIP_STEALTHY, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
383  { "ship-unstealthy", OP_SHIP_UNSTEALTHY, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, // Goober5000
384  { "friendly-stealth-invisible", OP_FRIENDLY_STEALTH_INVISIBLE, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, // Goober5000
385  { "friendly-stealth-visible", OP_FRIENDLY_STEALTH_VISIBLE, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, // Goober5000
386  { "primitive-sensors-set-range", OP_PRIMITIVE_SENSORS_SET_RANGE, 2, 2, SEXP_ACTION_OPERATOR, }, // Goober5000
387  { "ship-targetable-as-bomb", OP_SHIP_BOMB_TARGETABLE, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
388  { "ship-untargetable-as-bomb", OP_SHIP_BOMB_UNTARGETABLE, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
389  { "kamikaze", OP_KAMIKAZE, 2, INT_MAX, SEXP_ACTION_OPERATOR, }, // Sesquipedalian
390  { "change-iff", OP_CHANGE_IFF, 2, INT_MAX, SEXP_ACTION_OPERATOR, },
391  { "change-iff-color", OP_CHANGE_IFF_COLOR, 6, INT_MAX, SEXP_ACTION_OPERATOR, },
392  { "add-remove-escort", OP_ADD_REMOVE_ESCORT, 2, 2, SEXP_ACTION_OPERATOR, },
393  { "ship-change-alt-name", OP_SHIP_CHANGE_ALT_NAME, 2, INT_MAX, SEXP_ACTION_OPERATOR, }, // Goober5000
394  { "ship-change-callsign", OP_SHIP_CHANGE_CALLSIGN, 2, INT_MAX, SEXP_ACTION_OPERATOR, }, // FUBAR
395  { "ship-tag", OP_SHIP_TAG, 3, 8, SEXP_ACTION_OPERATOR, }, // Goober5000
396  { "ship-untag", OP_SHIP_UNTAG, 1, 1, SEXP_ACTION_OPERATOR, }, // Goober5000
397  { "set-arrival-info", OP_SET_ARRIVAL_INFO, 2, 7, SEXP_ACTION_OPERATOR, }, // Goober5000
398  { "set-departure-info", OP_SET_DEPARTURE_INFO, 2, 6, SEXP_ACTION_OPERATOR, }, // Goober5000
399 
400  //Shields, Engines and Weapons Sub-Category
401  { "set-weapon-energy", OP_SET_WEAPON_ENERGY, 2, INT_MAX, SEXP_ACTION_OPERATOR, }, // Karajorma
402  { "set-shield-energy", OP_SET_SHIELD_ENERGY, 2, INT_MAX, SEXP_ACTION_OPERATOR, }, // Karajorma
403  { "set-player-throttle-speed", OP_SET_PLAYER_THROTTLE_SPEED, 2, 2, SEXP_ACTION_OPERATOR, }, // CommanderDJ
404  { "set-afterburner-energy", OP_SET_AFTERBURNER_ENERGY, 2, INT_MAX, SEXP_ACTION_OPERATOR, }, // Karajorma
405  { "set-subspace-drive", OP_SET_SUBSPACE_DRIVE, 2, INT_MAX, SEXP_ACTION_OPERATOR, },
406  { "set-primary-weapon", OP_SET_PRIMARY_WEAPON, 3, 5, SEXP_ACTION_OPERATOR, }, // Karajorma
407  { "set-secondary-weapon", OP_SET_SECONDARY_WEAPON, 3, 5, SEXP_ACTION_OPERATOR, }, // Karajorma
408  { "set-primary-ammo", OP_SET_PRIMARY_AMMO, 3, 4, SEXP_ACTION_OPERATOR, }, // Karajorma
409  { "set-secondary-ammo", OP_SET_SECONDARY_AMMO, 3, 4, SEXP_ACTION_OPERATOR, }, // Karajorma
410  { "set-num-countermeasures", OP_SET_NUM_COUNTERMEASURES, 2, 2, SEXP_ACTION_OPERATOR, }, // Karajorma
411  { "lock-primary-weapon", OP_LOCK_PRIMARY_WEAPON, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, // Karajorma
412  { "unlock-primary-weapon", OP_UNLOCK_PRIMARY_WEAPON, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, // Karajorma
413  { "lock-secondary-weapon", OP_LOCK_SECONDARY_WEAPON, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, // Karajorma
414  { "unlock-secondary-weapon", OP_UNLOCK_SECONDARY_WEAPON, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, // Karajorma
415  { "lock-afterburner", OP_LOCK_AFTERBURNER, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, // KeldorKatarn
416  { "unlock-afterburner", OP_UNLOCK_AFTERBURNER, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, // KeldorKatarn
417  { "shields-on", OP_SHIELDS_ON, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, // Sesquipedalian
418  { "shields-off", OP_SHIELDS_OFF, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, // Sesquipedalian
419  { "force-glide", OP_FORCE_GLIDE, 2, 2, SEXP_ACTION_OPERATOR, }, // The E
420  { "disable-ets", OP_DISABLE_ETS, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, // The E
421  { "enable-ets", OP_ENABLE_ETS, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, // The E
422  { "break-warp", OP_WARP_BROKEN, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
423  { "fix-warp", OP_WARP_NOT_BROKEN, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
424  { "never-warp", OP_WARP_NEVER, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
425  { "allow-warp", OP_WARP_ALLOWED, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
426  { "special-warpout-name", OP_SET_SPECIAL_WARPOUT_NAME, 2, 2, SEXP_ACTION_OPERATOR, },
427  { "get-ets-value", OP_GET_ETS_VALUE, 2, 2, SEXP_ACTION_OPERATOR, }, // niffiwan
428  { "set-ets-values", OP_SET_ETS_VALUES, 4, INT_MAX, SEXP_ACTION_OPERATOR, }, // niffiwan
429 
430  //Subsystems and Health Sub-Category
431  { "ship-invulnerable", OP_SHIP_INVULNERABLE, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
432  { "ship-vulnerable", OP_SHIP_VULNERABLE, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
433  { "ship-guardian", OP_SHIP_GUARDIAN, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
434  { "ship-no-guardian", OP_SHIP_NO_GUARDIAN, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
435  { "ship-guardian-threshold", OP_SHIP_GUARDIAN_THRESHOLD, 2, INT_MAX, SEXP_ACTION_OPERATOR, },
436  { "ship-subsys-guardian-threshold", OP_SHIP_SUBSYS_GUARDIAN_THRESHOLD, 3, INT_MAX, SEXP_ACTION_OPERATOR, },
437  { "self-destruct", OP_SELF_DESTRUCT, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
438  { "destroy-instantly", OP_DESTROY_INSTANTLY, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, // Admiral MS
439  { "destroy-subsys-instantly", OP_DESTROY_SUBSYS_INSTANTLY, 2, INT_MAX, SEXP_ACTION_OPERATOR, }, // Admiral MS
440  { "sabotage-subsystem", OP_SABOTAGE_SUBSYSTEM, 3, 3, SEXP_ACTION_OPERATOR, },
441  { "repair-subsystem", OP_REPAIR_SUBSYSTEM, 3, 4, SEXP_ACTION_OPERATOR, },
442  { "ship-copy-damage", OP_SHIP_COPY_DAMAGE, 2, INT_MAX, SEXP_ACTION_OPERATOR, }, // Goober5000
443  { "set-subsystem-strength", OP_SET_SUBSYSTEM_STRNGTH, 3, 4, SEXP_ACTION_OPERATOR, },
444  { "subsys-set-random", OP_SUBSYS_SET_RANDOM, 3, INT_MAX, SEXP_ACTION_OPERATOR, },
445  { "lock-rotating-subsystem", OP_LOCK_ROTATING_SUBSYSTEM, 2, INT_MAX, SEXP_ACTION_OPERATOR, }, // Goober5000
446  { "free-rotating-subsystem", OP_FREE_ROTATING_SUBSYSTEM, 2, INT_MAX, SEXP_ACTION_OPERATOR, }, // Goober5000
447  { "reverse-rotating-subsystem", OP_REVERSE_ROTATING_SUBSYSTEM, 2, INT_MAX, SEXP_ACTION_OPERATOR, }, // Goober5000
448  { "rotating-subsys-set-turn-time", OP_ROTATING_SUBSYS_SET_TURN_TIME, 3, INT_MAX, SEXP_ACTION_OPERATOR, }, // Goober5000
449  { "trigger-submodel-animation", OP_TRIGGER_SUBMODEL_ANIMATION, 4, 6, SEXP_ACTION_OPERATOR, }, // Goober5000
450  { "change-subsystem-name", OP_CHANGE_SUBSYSTEM_NAME, 3, INT_MAX, SEXP_ACTION_OPERATOR, }, // Karajorma
451  { "ship-subsys-targetable", OP_SHIP_SUBSYS_TARGETABLE, 2, INT_MAX, SEXP_ACTION_OPERATOR, }, // Goober5000
452  { "ship-subsys-untargetable", OP_SHIP_SUBSYS_UNTARGETABLE, 2, INT_MAX, SEXP_ACTION_OPERATOR, }, // Goober5000
453  { "ship-subsys-no-replace", OP_SHIP_SUBSYS_NO_REPLACE, 3, INT_MAX, SEXP_ACTION_OPERATOR, }, // FUBAR
454  { "ship-subsys-no-live-debris", OP_SHIP_SUBSYS_NO_LIVE_DEBRIS, 3, INT_MAX, SEXP_ACTION_OPERATOR, }, // FUBAR
455  { "ship-subsys-vanish", OP_SHIP_SUBSYS_VANISHED, 3, INT_MAX, SEXP_ACTION_OPERATOR, }, // FUBAR
456  { "ship-subsys-ignore_if_dead", OP_SHIP_SUBSYS_IGNORE_IF_DEAD, 3, INT_MAX, SEXP_ACTION_OPERATOR, }, // FUBAR
457  { "awacs-set-radius", OP_AWACS_SET_RADIUS, 3, 3, SEXP_ACTION_OPERATOR, },
458  { "alter-ship-flag", OP_ALTER_SHIP_FLAG, 3, INT_MAX, SEXP_ACTION_OPERATOR, }, // Karajorma
459 
460  //Cargo Sub-Category
461  { "transfer-cargo", OP_TRANSFER_CARGO, 2, 2, SEXP_ACTION_OPERATOR, },
462  { "exchange-cargo", OP_EXCHANGE_CARGO, 2, 2, SEXP_ACTION_OPERATOR, },
463  { "set-cargo", OP_SET_CARGO, 2, 3, SEXP_ACTION_OPERATOR, },
464  { "jettison-cargo-delay", OP_JETTISON_CARGO, 2, INT_MAX, SEXP_ACTION_OPERATOR, },
465  { "set-docked", OP_SET_DOCKED, 4, 4, SEXP_ACTION_OPERATOR, }, // Sushi
466  { "cargo-no-deplete", OP_CARGO_NO_DEPLETE, 1, 2, SEXP_ACTION_OPERATOR, },
467  { "set-scanned", OP_SET_SCANNED, 1, 2, SEXP_ACTION_OPERATOR, },
468  { "set-unscanned", OP_SET_UNSCANNED, 1, 2, SEXP_ACTION_OPERATOR, },
469 
470  //Armor and Damage Types Sub-Category
471  { "set-armor-type", OP_SET_ARMOR_TYPE, 4, INT_MAX, SEXP_ACTION_OPERATOR, }, // FUBAR
472  { "weapon-set-damage-type", OP_WEAPON_SET_DAMAGE_TYPE, 4, INT_MAX, SEXP_ACTION_OPERATOR, }, // FUBAR
473  { "ship-set-damage-type", OP_SHIP_SET_DAMAGE_TYPE, 4, INT_MAX, SEXP_ACTION_OPERATOR, }, // FUBAR
474  { "ship-set-shockwave-damage-type", OP_SHIP_SHOCKWAVE_SET_DAMAGE_TYPE, 3, INT_MAX, SEXP_ACTION_OPERATOR, }, // FUBAR
475  { "field-set-damage-type", OP_FIELD_SET_DAMAGE_TYPE, 2, 2, SEXP_ACTION_OPERATOR, }, // FUBAR
476 
477  //Beams and Turrets Sub-Category
478  { "fire-beam", OP_BEAM_FIRE, 3, 5, SEXP_ACTION_OPERATOR, },
479  { "fire-beam-at-coordinates", OP_BEAM_FIRE_COORDS, 5, 9, SEXP_ACTION_OPERATOR, },
480  { "beam-create", OP_BEAM_FLOATING_FIRE, 7, 14, SEXP_ACTION_OPERATOR, },
481  { "beam-free", OP_BEAM_FREE, 2, INT_MAX, SEXP_ACTION_OPERATOR, },
482  { "beam-free-all", OP_BEAM_FREE_ALL, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
483  { "beam-lock", OP_BEAM_LOCK, 2, INT_MAX, SEXP_ACTION_OPERATOR, },
484  { "beam-lock-all", OP_BEAM_LOCK_ALL, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
485  { "turret-free", OP_TURRET_FREE, 2, INT_MAX, SEXP_ACTION_OPERATOR, },
486  { "turret-free-all", OP_TURRET_FREE_ALL, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
487  { "turret-lock", OP_TURRET_LOCK, 2, INT_MAX, SEXP_ACTION_OPERATOR, },
488  { "turret-lock-all", OP_TURRET_LOCK_ALL, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
489  { "turret-tagged-only", OP_TURRET_TAGGED_ONLY_ALL, 1, 1, SEXP_ACTION_OPERATOR, },
490  { "turret-tagged-clear", OP_TURRET_TAGGED_CLEAR_ALL, 1, 1, SEXP_ACTION_OPERATOR, },
491  { "turret-tagged-specific", OP_TURRET_TAGGED_SPECIFIC, 2, INT_MAX, SEXP_ACTION_OPERATOR, }, //phreak
492  { "turret-tagged-clear-specific", OP_TURRET_TAGGED_CLEAR_SPECIFIC, 2, INT_MAX, SEXP_ACTION_OPERATOR, }, //phreak
493  { "turret-change-weapon", OP_TURRET_CHANGE_WEAPON, 5, 5, SEXP_ACTION_OPERATOR, }, //WMC
494  { "turret-set-direction-preference",OP_TURRET_SET_DIRECTION_PREFERENCE, 3, INT_MAX, SEXP_ACTION_OPERATOR, }, //FUBAR
495  { "turret-set-rate-of-fire", OP_TURRET_SET_RATE_OF_FIRE, 3, INT_MAX, SEXP_ACTION_OPERATOR, }, //FUBAR
496  { "turret-set-optimum-range", OP_TURRET_SET_OPTIMUM_RANGE, 3, INT_MAX, SEXP_ACTION_OPERATOR, }, //FUBAR
497  { "turret-set-target-priorities", OP_TURRET_SET_TARGET_PRIORITIES, 3, INT_MAX, SEXP_ACTION_OPERATOR, }, //FUBAR
498  { "turret-set-target-order", OP_TURRET_SET_TARGET_ORDER, 2, 2+NUM_TURRET_ORDER_TYPES, SEXP_ACTION_OPERATOR, }, //WMC
499  { "ship-turret-target-order", OP_SHIP_TURRET_TARGET_ORDER, 1, 1+NUM_TURRET_ORDER_TYPES, SEXP_ACTION_OPERATOR, }, //WMC
500  { "turret-subsys-target-disable", OP_TURRET_SUBSYS_TARGET_DISABLE, 2, INT_MAX, SEXP_ACTION_OPERATOR, },
501  { "turret-subsys-target-enable", OP_TURRET_SUBSYS_TARGET_ENABLE, 2, INT_MAX, SEXP_ACTION_OPERATOR, },
502  { "turret-set-primary-ammo", OP_TURRET_SET_PRIMARY_AMMO, 4, 4, SEXP_ACTION_OPERATOR, }, // DahBlount
503  { "turret-set-secondary-ammo", OP_TURRET_SET_SECONDARY_AMMO, 4, 4, SEXP_ACTION_OPERATOR, }, // DahBlount
504  { "turret-get-primary-ammo", OP_TURRET_GET_PRIMARY_AMMO, 3, 3, SEXP_INTEGER_OPERATOR, }, // DahBlount
505  { "turret-get-secondary-ammo", OP_TURRET_GET_SECONDARY_AMMO, 3, 3, SEXP_INTEGER_OPERATOR, }, // DahBlount
506 
507  //Models and Textures Sub-Category
508  { "change-ship-class", OP_CHANGE_SHIP_CLASS, 2, INT_MAX, SEXP_ACTION_OPERATOR, }, // Goober5000
509  { "deactivate-glow-maps", OP_DEACTIVATE_GLOW_MAPS, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, //-Bobboau
510  { "activate-glow-maps", OP_ACTIVATE_GLOW_MAPS, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, //-Bobboau
511  { "deactivate-glow-points", OP_DEACTIVATE_GLOW_POINTS, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, //-Bobboau
512  { "activate-glow-points", OP_ACTIVATE_GLOW_POINTS, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, //-Bobboau
513  { "deactivate-glow-point-bank", OP_DEACTIVATE_GLOW_POINT_BANK, 2, INT_MAX, SEXP_ACTION_OPERATOR, }, //-Bobboau
514  { "activate-glow-point-bank", OP_ACTIVATE_GLOW_POINT_BANK, 2, INT_MAX, SEXP_ACTION_OPERATOR, }, //-Bobboau
515  { "set-thrusters-status", OP_SET_THRUSTERS, 2, INT_MAX, SEXP_ACTION_OPERATOR, }, // The E
516  { "don't-collide-invisible", OP_DONT_COLLIDE_INVISIBLE, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, // Goober5000
517  { "collide-invisible", OP_COLLIDE_INVISIBLE, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, // Goober5000
518  { "add-to-collision-group", OP_ADD_TO_COLGROUP, 2, INT_MAX, SEXP_ACTION_OPERATOR, }, // The E
519  { "remove-from-collision-group", OP_REMOVE_FROM_COLGROUP, 2, INT_MAX, SEXP_ACTION_OPERATOR, },
520  { "get-collision-group", OP_GET_COLGROUP_ID, 1, 1, SEXP_ACTION_OPERATOR, },
521  { "change-team-color", OP_CHANGE_TEAM_COLOR, 3, INT_MAX, SEXP_ACTION_OPERATOR, }, // The E
522 
523  //Coordinate Manipulation Sub-Category
524  { "set-object-position", OP_SET_OBJECT_POSITION, 4, 4, SEXP_ACTION_OPERATOR, }, // WMC
525  { "set-object-orientation", OP_SET_OBJECT_ORIENTATION, 4, 4, SEXP_ACTION_OPERATOR, }, // Goober5000
526  { "set-object-facing", OP_SET_OBJECT_FACING, 4, 6, SEXP_ACTION_OPERATOR, }, // Goober5000
527  { "set-object-facing-object", OP_SET_OBJECT_FACING_OBJECT, 2, 4, SEXP_ACTION_OPERATOR, }, // Goober5000
528  { "set-object-speed-x", OP_SET_OBJECT_SPEED_X, 2, 3, SEXP_ACTION_OPERATOR, }, // WMC
529  { "set-object-speed-y", OP_SET_OBJECT_SPEED_Y, 2, 3, SEXP_ACTION_OPERATOR, }, // WMC
530  { "set-object-speed-z", OP_SET_OBJECT_SPEED_Z, 2, 3, SEXP_ACTION_OPERATOR, }, // WMC
531  { "ship-maneuver", OP_SHIP_MANEUVER, 10, 10, SEXP_ACTION_OPERATOR, }, // Wanderer
532  { "ship-rot-maneuver", OP_SHIP_ROT_MANEUVER, 6, 6, SEXP_ACTION_OPERATOR, }, // Wanderer
533  { "ship-lat-maneuver", OP_SHIP_LAT_MANEUVER, 6, 6, SEXP_ACTION_OPERATOR, }, // Wanderer
534  { "set-immobile", OP_SET_IMMOBILE, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, // Goober5000
535  { "set-mobile", OP_SET_MOBILE, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, // Goober5000
536 
537  //Mission and Campaign Sub-Category
538  { "invalidate-goal", OP_INVALIDATE_GOAL, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
539  { "validate-goal", OP_VALIDATE_GOAL, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
540  { "red-alert", OP_RED_ALERT, 0, 0, SEXP_ACTION_OPERATOR, },
541  { "end-mission", OP_END_MISSION, 0, 3, SEXP_ACTION_OPERATOR, }, //-Sesquipedalian
542  { "force-jump", OP_FORCE_JUMP, 0, 0, SEXP_ACTION_OPERATOR, }, // Goober5000
543  { "next-mission", OP_NEXT_MISSION, 1, 1, SEXP_ACTION_OPERATOR, },
544  { "end-campaign", OP_END_CAMPAIGN, 0, 1, SEXP_ACTION_OPERATOR, },
545  { "end-of-campaign", OP_END_OF_CAMPAIGN, 0, 0, SEXP_ACTION_OPERATOR, },
546  { "set-debriefing-toggled", OP_SET_DEBRIEFING_TOGGLED, 1, 1, SEXP_ACTION_OPERATOR, }, // Goober5000
547  { "allow-treason", OP_ALLOW_TREASON, 1, 1, SEXP_ACTION_OPERATOR, }, // Karajorma
548  { "grant-promotion", OP_GRANT_PROMOTION, 0, 0, SEXP_ACTION_OPERATOR, },
549  { "grant-medal", OP_GRANT_MEDAL, 1, 1, SEXP_ACTION_OPERATOR, },
550  { "allow-ship", OP_ALLOW_SHIP, 1, 1, SEXP_ACTION_OPERATOR, },
551  { "allow-weapon", OP_ALLOW_WEAPON, 1, 1, SEXP_ACTION_OPERATOR, },
552  { "tech-add-ships", OP_TECH_ADD_SHIP, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
553  { "tech-add-weapons", OP_TECH_ADD_WEAPON, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
554  { "tech-add-intel", OP_TECH_ADD_INTEL, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, // Goober5000
555  { "tech-add-intel-xstr", OP_TECH_ADD_INTEL_XSTR, 2, INT_MAX, SEXP_ACTION_OPERATOR, }, // Goober5000
556  { "tech-reset-to-default", OP_TECH_RESET_TO_DEFAULT, 0, 0, SEXP_ACTION_OPERATOR, }, // Goober5000
557  { "change-player-score", OP_CHANGE_PLAYER_SCORE, 2, INT_MAX, SEXP_ACTION_OPERATOR, }, // Karajorma
558  { "change-team-score", OP_CHANGE_TEAM_SCORE, 2, 2, SEXP_ACTION_OPERATOR, }, // Karajorma
559  { "set-respawns", OP_SET_RESPAWNS, 2, INT_MAX, SEXP_ACTION_OPERATOR, }, // Karajorma
560 
561  //Music and Sound Sub-Category
562  { "change-soundtrack", OP_CHANGE_SOUNDTRACK, 1, 1, SEXP_ACTION_OPERATOR, }, // Goober5000
563  { "play-sound-from-table", OP_PLAY_SOUND_FROM_TABLE, 4, 4, SEXP_ACTION_OPERATOR, }, // Goober5000
564  { "play-sound-from-file", OP_PLAY_SOUND_FROM_FILE, 1, 3, SEXP_ACTION_OPERATOR, }, // Goober5000
565  { "close-sound-from-file", OP_CLOSE_SOUND_FROM_FILE, 1, 1, SEXP_ACTION_OPERATOR, }, // Goober5000
566  { "pause-sound-from-file", OP_PAUSE_SOUND_FROM_FILE, 1, 1, SEXP_ACTION_OPERATOR, }, // Goober5000
567  { "set-sound-environment", OP_SET_SOUND_ENVIRONMENT, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, // Taylor
568  { "update-sound-environment", OP_UPDATE_SOUND_ENVIRONMENT, 2, INT_MAX, SEXP_ACTION_OPERATOR, }, // Taylor
569  { "adjust-audio-volume", OP_ADJUST_AUDIO_VOLUME, 1, 3, SEXP_ACTION_OPERATOR, },
570 
571  //HUD Sub-Category
572  { "hud-disable", OP_HUD_DISABLE, 1, 1, SEXP_ACTION_OPERATOR, }, // Goober5000
573  { "hud-disable-except-messages", OP_HUD_DISABLE_EXCEPT_MESSAGES, 1, 1, SEXP_ACTION_OPERATOR, }, // Goober5000
574  { "hud-set-custom-gauge-active", OP_HUD_SET_CUSTOM_GAUGE_ACTIVE, 2, INT_MAX, SEXP_ACTION_OPERATOR, },
575  { "hud-set-retail-gauge-active", OP_HUD_SET_RETAIL_GAUGE_ACTIVE, 2, INT_MAX, SEXP_ACTION_OPERATOR, },
576  { "hud-set-text", OP_HUD_SET_TEXT, 2, 2, SEXP_ACTION_OPERATOR, }, //WMCoolmon
577  { "hud-set-text-num", OP_HUD_SET_TEXT_NUM, 2, 2, SEXP_ACTION_OPERATOR, }, //WMCoolmon
578  { "hud-set-message", OP_HUD_SET_MESSAGE, 2, 2, SEXP_ACTION_OPERATOR, }, //The E
579  { "hud-set-directive", OP_HUD_SET_DIRECTIVE, 2, 2, SEXP_ACTION_OPERATOR, }, //The E
580  { "hud-set-frame", OP_HUD_SET_FRAME, 2, 2, SEXP_ACTION_OPERATOR, }, //WMCoolmon
581  { "hud-set-coords", OP_HUD_SET_COORDS, 3, 3, SEXP_ACTION_OPERATOR, }, //WMCoolmon
582  { "hud-set-color", OP_HUD_SET_COLOR, 4, 4, SEXP_ACTION_OPERATOR, }, //WMCoolmon
583  { "hud-display-gauge", OP_HUD_DISPLAY_GAUGE, 2, 2, SEXP_ACTION_OPERATOR, },
584  { "hud-gauge-set-active", OP_HUD_GAUGE_SET_ACTIVE, 2, 2, SEXP_ACTION_OPERATOR, }, //Deprecated
585  { "hud-activate-gauge-type", OP_HUD_ACTIVATE_GAUGE_TYPE, 2, 2, SEXP_ACTION_OPERATOR, }, //Deprecated
586  { "hud-clear-messages", OP_HUD_CLEAR_MESSAGES, 0, 0, SEXP_ACTION_OPERATOR, }, // swifty
587  { "hud-set-max-targeting-range", OP_HUD_SET_MAX_TARGETING_RANGE, 1, 1, SEXP_ACTION_OPERATOR, }, // Goober5000
588 
589  //Nav Sub-Category
590  { "add-nav-waypoint", OP_NAV_ADD_WAYPOINT, 3, 4, SEXP_ACTION_OPERATOR, }, //kazan
591  { "add-nav-ship", OP_NAV_ADD_SHIP, 2, 2, SEXP_ACTION_OPERATOR, }, //kazan
592  { "del-nav", OP_NAV_DEL, 1, 1, SEXP_ACTION_OPERATOR, }, //kazan
593  { "hide-nav", OP_NAV_HIDE, 1, 1, SEXP_ACTION_OPERATOR, }, //kazan
594  { "restrict-nav", OP_NAV_RESTRICT, 1, 1, SEXP_ACTION_OPERATOR, }, //kazan
595  { "unhide-nav", OP_NAV_UNHIDE, 1, 1, SEXP_ACTION_OPERATOR, }, //kazan
596  { "unrestrict-nav", OP_NAV_UNRESTRICT, 1, 1, SEXP_ACTION_OPERATOR, }, //kazan
597  { "set-nav-visited", OP_NAV_SET_VISITED, 1, 1, SEXP_ACTION_OPERATOR, }, //kazan
598  { "unset-nav-visited", OP_NAV_UNSET_VISITED, 1, 1, SEXP_ACTION_OPERATOR, }, //kazan
599  { "set-nav-carry", OP_NAV_SET_CARRY, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, //kazan
600  { "unset-nav-carry", OP_NAV_UNSET_CARRY, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, //kazan
601  { "set-nav-needslink", OP_NAV_SET_NEEDSLINK, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, //kazan
602  { "unset-nav-needslink", OP_NAV_UNSET_NEEDSLINK, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, //kazan
603  { "is-nav-linked", OP_NAV_ISLINKED, 1, 1, SEXP_BOOLEAN_OPERATOR, }, //kazan
604  { "use-nav-cinematics", OP_NAV_USECINEMATICS, 1, 1, SEXP_ACTION_OPERATOR, }, //kazan
605  { "use-autopilot", OP_NAV_USEAP, 1, 1, SEXP_ACTION_OPERATOR, }, //kazan
606  { "select-nav", OP_NAV_SELECT, 1, 1, SEXP_ACTION_OPERATOR, }, //Talon1024
607  { "unselect-nav", OP_NAV_UNSELECT, 0, 0, SEXP_ACTION_OPERATOR, }, //Talon1024
608 
609  //Cutscene Sub-Category
610  { "set-cutscene-bars", OP_CUTSCENES_SET_CUTSCENE_BARS, 0, 1, SEXP_ACTION_OPERATOR, },
611  { "unset-cutscene-bars", OP_CUTSCENES_UNSET_CUTSCENE_BARS, 0, 1, SEXP_ACTION_OPERATOR, },
612  { "fade-in", OP_CUTSCENES_FADE_IN, 0, 4, SEXP_ACTION_OPERATOR, },
613  { "fade-out", OP_CUTSCENES_FADE_OUT, 0, 4, SEXP_ACTION_OPERATOR, },
614  { "set-camera", OP_CUTSCENES_SET_CAMERA, 0, 1, SEXP_ACTION_OPERATOR, },
615  { "set-camera-position", OP_CUTSCENES_SET_CAMERA_POSITION, 3, 6, SEXP_ACTION_OPERATOR, },
616  { "set-camera-facing", OP_CUTSCENES_SET_CAMERA_FACING, 3, 6, SEXP_ACTION_OPERATOR, },
617  { "set-camera-facing-object", OP_CUTSCENES_SET_CAMERA_FACING_OBJECT, 1, 4, SEXP_ACTION_OPERATOR, },
618  { "set-camera-rotation", OP_CUTSCENES_SET_CAMERA_ROTATION, 3, 6, SEXP_ACTION_OPERATOR, },
619  { "set-camera-host", OP_CUTSCENES_SET_CAMERA_HOST, 1, 2, SEXP_ACTION_OPERATOR, },
620  { "set-camera-target", OP_CUTSCENES_SET_CAMERA_TARGET, 1, 2, SEXP_ACTION_OPERATOR, },
621  { "set-camera-fov", OP_CUTSCENES_SET_CAMERA_FOV, 1, 5, SEXP_ACTION_OPERATOR, },
622  { "set-fov", OP_CUTSCENES_SET_FOV, 1, 1, SEXP_ACTION_OPERATOR, },
623  { "get-fov", OP_CUTSCENES_GET_FOV, 0, 0, SEXP_INTEGER_OPERATOR, },
624  { "reset-fov", OP_CUTSCENES_RESET_FOV, 0, 0, SEXP_ACTION_OPERATOR, },
625  { "reset-camera", OP_CUTSCENES_RESET_CAMERA, 0, 1, SEXP_ACTION_OPERATOR, },
626  { "show-subtitle", OP_CUTSCENES_SHOW_SUBTITLE, 4, 13, SEXP_ACTION_OPERATOR, },
627  { "show-subtitle-text", OP_CUTSCENES_SHOW_SUBTITLE_TEXT, 6, 13, SEXP_ACTION_OPERATOR, },
628  { "show-subtitle-image", OP_CUTSCENES_SHOW_SUBTITLE_IMAGE, 8, 10, SEXP_ACTION_OPERATOR, },
629  { "clear-subtitles", OP_CLEAR_SUBTITLES, 0, 0, SEXP_ACTION_OPERATOR, },
630  { "lock-perspective", OP_CUTSCENES_FORCE_PERSPECTIVE, 1, 2, SEXP_ACTION_OPERATOR, },
631  { "set-camera-shudder", OP_SET_CAMERA_SHUDDER, 2, 2, SEXP_ACTION_OPERATOR, },
632  { "supernova-start", OP_SUPERNOVA_START, 1, 1, SEXP_ACTION_OPERATOR, },
633  { "supernova-stop", OP_SUPERNOVA_STOP, 0, 0, SEXP_ACTION_OPERATOR, }, //CommanderDJ
634  { "set-motion-debris-override", OP_SET_MOTION_DEBRIS, 1, 1, SEXP_ACTION_OPERATOR, }, // The E
635 
636  //Background and Nebula Sub-Category
637  { "mission-set-nebula", OP_MISSION_SET_NEBULA, 1, 1, SEXP_ACTION_OPERATOR, }, // Sesquipedalian
638  { "mission-set-subspace", OP_MISSION_SET_SUBSPACE, 1, 1, SEXP_ACTION_OPERATOR, },
639  { "add-background-bitmap", OP_ADD_BACKGROUND_BITMAP, 8, 9, SEXP_ACTION_OPERATOR, }, // phreak
640  { "remove-background-bitmap", OP_REMOVE_BACKGROUND_BITMAP, 1, 1, SEXP_ACTION_OPERATOR, }, // phreak
641  { "add-sun-bitmap", OP_ADD_SUN_BITMAP, 5, 6, SEXP_ACTION_OPERATOR, }, // phreak
642  { "remove-sun-bitmap", OP_REMOVE_SUN_BITMAP, 1, 1, SEXP_ACTION_OPERATOR, }, // phreak
643  { "nebula-change-storm", OP_NEBULA_CHANGE_STORM, 1, 1, SEXP_ACTION_OPERATOR, }, // phreak
644  { "nebula-toggle-poof", OP_NEBULA_TOGGLE_POOF, 2, 2, SEXP_ACTION_OPERATOR, }, // phreak
645  { "nebula-change-pattern", OP_NEBULA_CHANGE_PATTERN, 1, 1, SEXP_ACTION_OPERATOR, }, // Axem
646  { "set-skybox-model", OP_SET_SKYBOX_MODEL, 1, 8, SEXP_ACTION_OPERATOR, }, // taylor
647  { "set-skybox-orientation", OP_SET_SKYBOX_ORIENT, 3, 3, SEXP_ACTION_OPERATOR, }, // Goober5000
648  { "set-ambient-light", OP_SET_AMBIENT_LIGHT, 3, 3, SEXP_ACTION_OPERATOR, }, // Karajorma
649 
650  //Jump Node Sub-Category
651  { "set-jumpnode-name", OP_JUMP_NODE_SET_JUMPNODE_NAME, 2, 2, SEXP_ACTION_OPERATOR, }, //CommanderDJ
652  { "set-jumpnode-color", OP_JUMP_NODE_SET_JUMPNODE_COLOR, 5, 5, SEXP_ACTION_OPERATOR, },
653  { "set-jumpnode-model", OP_JUMP_NODE_SET_JUMPNODE_MODEL, 3, 3, SEXP_ACTION_OPERATOR, },
654  { "show-jumpnode", OP_JUMP_NODE_SHOW_JUMPNODE, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
655  { "hide-jumpnode", OP_JUMP_NODE_HIDE_JUMPNODE, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
656 
657  //Special Effects Sub-Category
658  { "set-post-effect", OP_SET_POST_EFFECT, 2, 2, SEXP_ACTION_OPERATOR, }, // Hery
659  { "ship-effect", OP_SHIP_EFFECT, 3, INT_MAX, SEXP_ACTION_OPERATOR, }, // Valathil
660  { "ship-create", OP_SHIP_CREATE, 5, 8, SEXP_ACTION_OPERATOR, }, // WMC
661  { "weapon-create", OP_WEAPON_CREATE, 5, 10, SEXP_ACTION_OPERATOR, }, // Goober5000
662  { "ship-vanish", OP_SHIP_VANISH, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
663  { "ship-vaporize", OP_SHIP_VAPORIZE, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, // Goober5000
664  { "ship-no-vaporize", OP_SHIP_NO_VAPORIZE, 1, INT_MAX, SEXP_ACTION_OPERATOR, }, // Goober5000
665  { "set-explosion-option", OP_SET_EXPLOSION_OPTION, 3, INT_MAX, SEXP_ACTION_OPERATOR, }, // Goober5000
666  { "explosion-effect", OP_EXPLOSION_EFFECT, 11, 13, SEXP_ACTION_OPERATOR, }, // Goober5000
667  { "warp-effect", OP_WARP_EFFECT, 12, 12, SEXP_ACTION_OPERATOR, }, // Goober5000
668  { "remove-weapons", OP_REMOVE_WEAPONS, 0, 1, SEXP_ACTION_OPERATOR, }, // Karajorma
669  { "set-time-compression", OP_CUTSCENES_SET_TIME_COMPRESSION, 1, 3, SEXP_ACTION_OPERATOR, },
670  { "reset-time-compression", OP_CUTSCENES_RESET_TIME_COMPRESSION, 0, 0, SEXP_ACTION_OPERATOR, },
671  { "call-ssm-strike", OP_CALL_SSM_STRIKE, 3, INT_MAX, SEXP_ACTION_OPERATOR, }, // X3N0-Life-Form
672 
673  //Variable Category
674  { "modify-variable", OP_MODIFY_VARIABLE, 2, 2, SEXP_ACTION_OPERATOR, },
675  { "get-variable-by-index", OP_GET_VARIABLE_BY_INDEX, 1, 1, SEXP_INTEGER_OPERATOR, }, // Goober5000
676  { "set-variable-by-index", OP_SET_VARIABLE_BY_INDEX, 2, 2, SEXP_ACTION_OPERATOR, }, // Goober5000
677  { "copy-variable-from-index", OP_COPY_VARIABLE_FROM_INDEX, 2, 2, SEXP_ACTION_OPERATOR, }, // Goober5000
678  { "copy-variable-between-indexes", OP_COPY_VARIABLE_BETWEEN_INDEXES, 2, 2, SEXP_ACTION_OPERATOR, }, // Goober5000
679  { "int-to-string", OP_INT_TO_STRING, 2, 2, SEXP_ACTION_OPERATOR, }, // Goober5000
680  { "string-concatenate", OP_STRING_CONCATENATE, 3, 3, SEXP_ACTION_OPERATOR, }, // Goober5000
681  { "string-get-substring", OP_STRING_GET_SUBSTRING, 4, 4, SEXP_ACTION_OPERATOR, }, // Goober5000
682  { "string-set-substring", OP_STRING_SET_SUBSTRING, 5, 5, SEXP_ACTION_OPERATOR, }, // Goober5000
683 
684  //Other Sub-Category
685  { "damaged-escort-priority", OP_DAMAGED_ESCORT_LIST, 3, INT_MAX, SEXP_ACTION_OPERATOR, }, //phreak
686  { "damaged-escort-priority-all", OP_DAMAGED_ESCORT_LIST_ALL, 1, MAX_COMPLETE_ESCORT_LIST, SEXP_ACTION_OPERATOR, }, // Goober5000
687  { "set-support-ship", OP_SET_SUPPORT_SHIP, 6, 7, SEXP_ACTION_OPERATOR, }, // Goober5000
688  { "script-eval", OP_SCRIPT_EVAL, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
689  { "script-eval-block", OP_SCRIPT_EVAL_BLOCK, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
690  { "multi-eval", OP_SCRIPT_EVAL_MULTI, 2, INT_MAX, SEXP_ACTION_OPERATOR, },
691  { "debug", OP_DEBUG, 2, 2, SEXP_ACTION_OPERATOR, }, // Karajorma
692  { "do-nothing", OP_NOP, 0, 0, SEXP_ACTION_OPERATOR, },
693 
694  //AI Goals Category
695  { "ai-chase", OP_AI_CHASE, 2, 3, SEXP_GOAL_OPERATOR, },
696  { "ai-chase-wing", OP_AI_CHASE_WING, 2, 3, SEXP_GOAL_OPERATOR, },
697  { "ai-chase-any", OP_AI_CHASE_ANY, 1, 1, SEXP_GOAL_OPERATOR, },
698  { "ai-guard", OP_AI_GUARD, 2, 2, SEXP_GOAL_OPERATOR, },
699  { "ai-guard-wing", OP_AI_GUARD_WING, 2, 2, SEXP_GOAL_OPERATOR, },
700  { "ai-destroy-subsystem", OP_AI_DESTROY_SUBSYS, 3, 4, SEXP_GOAL_OPERATOR, },
701  { "ai-disable-ship", OP_AI_DISABLE_SHIP, 2, 3, SEXP_GOAL_OPERATOR, },
702  { "ai-disarm-ship", OP_AI_DISARM_SHIP, 2, 3, SEXP_GOAL_OPERATOR, },
703  { "ai-warp", OP_AI_WARP, 2, 2, SEXP_GOAL_OPERATOR, },
704  { "ai-warp-out", OP_AI_WARP_OUT, 1, 1, SEXP_GOAL_OPERATOR, },
705  { "ai-dock", OP_AI_DOCK, 4, 4, SEXP_GOAL_OPERATOR, },
706  { "ai-undock", OP_AI_UNDOCK, 1, 2, SEXP_GOAL_OPERATOR, },
707  { "ai-waypoints", OP_AI_WAYPOINTS, 2, 2, SEXP_GOAL_OPERATOR, },
708  { "ai-waypoints-once", OP_AI_WAYPOINTS_ONCE, 2, 2, SEXP_GOAL_OPERATOR, },
709  { "ai-ignore", OP_AI_IGNORE, 2, 2, SEXP_GOAL_OPERATOR, },
710  { "ai-ignore-new", OP_AI_IGNORE_NEW, 2, 2, SEXP_GOAL_OPERATOR, },
711  { "ai-stay-near-ship", OP_AI_STAY_NEAR_SHIP, 2, 2, SEXP_GOAL_OPERATOR, },
712  { "ai-evade-ship", OP_AI_EVADE_SHIP, 2, 2, SEXP_GOAL_OPERATOR, },
713  { "ai-keep-safe-distance", OP_AI_KEEP_SAFE_DISTANCE, 1, 1, SEXP_GOAL_OPERATOR, },
714  { "ai-stay-still", OP_AI_STAY_STILL, 2, 2, SEXP_GOAL_OPERATOR, },
715  { "ai-play-dead", OP_AI_PLAY_DEAD, 1, 1, SEXP_GOAL_OPERATOR, },
716  { "ai-form-on-wing", OP_AI_FORM_ON_WING, 1, 1, SEXP_GOAL_OPERATOR, },
717 
718  { "goals", OP_GOALS_ID, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
719 
720  //Training Category
721  { "key-pressed", OP_KEY_PRESSED, 1, 2, SEXP_BOOLEAN_OPERATOR, },
722  { "key-reset", OP_KEY_RESET, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
723  { "key-reset-multiple", OP_KEY_RESET_MULTIPLE, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
724  { "ignore-key", OP_IGNORE_KEY, 2, INT_MAX, SEXP_ACTION_OPERATOR, }, // Karajorma
725  { "targeted", OP_TARGETED, 1, 3, SEXP_BOOLEAN_OPERATOR, },
726  { "node-targeted", OP_NODE_TARGETED, 1, 2, SEXP_BOOLEAN_OPERATOR, }, // FUBAR
727  { "missile-locked", OP_MISSILE_LOCKED, 1, 3, SEXP_BOOLEAN_OPERATOR, }, // Sesquipedalian
728  { "speed", OP_SPEED, 1, 1, SEXP_BOOLEAN_OPERATOR, },
729  { "facing", OP_FACING, 2, 2, SEXP_BOOLEAN_OPERATOR, },
730  { "facing-waypoint", OP_FACING2, 2, 2, SEXP_BOOLEAN_OPERATOR, },
731  { "order", OP_ORDER, 2, 3, SEXP_BOOLEAN_OPERATOR, },
732  { "query-orders", OP_QUERY_ORDERS, 3, 6, SEXP_BOOLEAN_OPERATOR, }, // Karajorma
733  { "reset-orders", OP_RESET_ORDERS, 0, 0, SEXP_ACTION_OPERATOR, }, // Karajorma
734  { "waypoint-missed", OP_WAYPOINT_MISSED, 0, 0, SEXP_BOOLEAN_OPERATOR, },
735  { "waypoint-twice", OP_WAYPOINT_TWICE, 0, 0, SEXP_BOOLEAN_OPERATOR, },
736  { "path-flown", OP_PATH_FLOWN, 0, 0, SEXP_BOOLEAN_OPERATOR, },
737  { "training-msg", OP_TRAINING_MSG, 1, 4, SEXP_ACTION_OPERATOR, },
738  { "flash-hud-gauge", OP_FLASH_HUD_GAUGE, 1, 1, SEXP_ACTION_OPERATOR, },
739  { "primaries-depleted", OP_PRIMARIES_DEPLETED, 1, 1, SEXP_BOOLEAN_OPERATOR, },
740  { "secondaries-depleted", OP_SECONDARIES_DEPLETED, 1, 1, SEXP_BOOLEAN_OPERATOR, },
741  { "special-check", OP_SPECIAL_CHECK, 1, 1, SEXP_ACTION_OPERATOR, },
742  { "set-training-context-fly-path", OP_SET_TRAINING_CONTEXT_FLY_PATH, 2, 2, SEXP_ACTION_OPERATOR, },
743  { "set-training-context-speed", OP_SET_TRAINING_CONTEXT_SPEED, 2, 2, SEXP_ACTION_OPERATOR, },
744 };
745 
769 };
770 
772 {
773  "LEAD_INDICATOR",
774  "ORIENTATION_TEE",
775  "HOSTILE_TRIANGLE",
776  "TARGET_TRIANGLE",
777  "MISSION_TIME",
778  "RETICLE_CIRCLE",
779  "THROTTLE_GAUGE",
780  "RADAR",
781  "TARGET_MONITOR",
782  "CENTER_RETICLE",
783  "TARGET_MONITOR_EXTRA_DATA",
784  "TARGET_SHIELD_ICON",
785  "PLAYER_SHIELD_ICON",
786  "ETS_GAUGE",
787  "AUTO_TARGET",
788  "AUTO_SPEED",
789  "WEAPONS_GAUGE",
790  "ESCORT_VIEW",
791  "DIRECTIVES_VIEW",
792  "THREAT_GAUGE",
793  "AFTERBURNER_ENERGY",
794  "WEAPONS_ENERGY",
795  "WEAPON_LINKING_GAUGE",
796  "TARGER_MINI_ICON",
797  "OFFSCREEN_INDICATOR",
798  "TALKING_HEAD",
799  "DAMAGE_GAUGE",
800  "MESSAGE_LINES",
801  "MISSILE_WARNING_ARROW",
802  "CMEASURE_GAUGE",
803  "OBJECTIVES_NOTIFY_GAUGE",
804  "WINGMEN_STATUS",
805  "OFFSCREEN RANGE",
806  "KILLS GAUGE",
807  "ATTACKING TARGET COUNT",
808  "TEXT FLASH",
809  "MESSAGE BOX",
810  "SUPPORT GUAGE",
811  "LAG GUAGE"
812 };
813 
814 
815 void sexp_set_skybox_model_preload(char *name); // taylor
817 char *Skybox_flags[] = {
818  "force-clamp",
819  "add-lighting",
820  "no-transparency",
821  "add-zbuffer",
822  "add-culling",
823  "no-glowmaps",
824 };
825 
827 int Sexp_useful_number; // a variable to pass useful info in from external modules
829 int Num_operators = sizeof(Operators) / sizeof(sexp_oper);
831 int Sexp_clipboard = -1; // used by Fred
841 
842 #define SEXP_NODE_INCREMENT 250
845 
847 sexp_variable Block_variables[MAX_SEXP_VARIABLES]; // used for compatibility with retail.
848 
850 
852 
854 int Players_mlocked = UNINITIALIZED; // for is-missile-locked - Sesquipedalian
858 
859 // for play-music - Goober5000
861 void sexp_stop_music(int fade = 1);
862 
863 // for sound environments - Goober5000/Taylor
864 #define SEO_VOLUME 0
865 #define SEO_DECAY_TIME 1
866 #define SEO_DAMPING 2
868 char *Sound_environment_option[] = { "volume", "decay time", "damping" };
870 
871 // for adjust-audio-volume - The E
872 char *Adjust_audio_options[] = { "Music", "Voice", "Effects" };
874 int audio_volume_option_lookup(char *text);
875 
876 int hud_gauge_type_lookup(char* name);
877 
878 // for explosions - Goober5000
879 #define EO_DAMAGE 0
880 #define EO_BLAST 1
881 #define EO_INNER_RADIUS 2
882 #define EO_OUTER_RADIUS 3
883 #define EO_SHOCKWAVE_SPEED 4
884 #define EO_DEATH_ROLL_TIME 5
885 int sexp_explosion_option_lookup(char *text);
886 char *Explosion_option[] = { "damage", "blast", "inner radius", "outer radius", "shockwave speed", "death roll time" };
888 
889 int get_sexp();
890 void build_extended_sexp_string(SCP_string &accumulator, int cur_node, int level, int mode);
891 void update_sexp_references(const char *old_name, const char *new_name, int format, int node);
892 int sexp_determine_team(char *subj);
893 int extract_sexp_variable_index(int node);
894 void init_sexp_vars();
895 int eval_num(int node);
896 
897 // for handling variables
898 void add_block_variable(const char *text, const char *var_name, int type, int index);
899 void sexp_modify_variable(int node);
900 int sexp_get_variable_by_index(int node);
901 void sexp_set_variable_by_index(int node);
902 void sexp_copy_variable_from_index(int node);
904 
908 
909 // Goober5000
911 bool is_blank_argument_op(int op_const);
912 bool is_blank_of_op(int op_const);
913 
914 int get_handler_for_x_of_operator(int node);
915 
916 //Karajorma
917 int get_generic_subsys(char *subsy_name);
918 bool ship_class_unchanged(int ship_index);
920 
921 int get_effect_from_name(char* name);
922 
923 #define NO_OPERATOR_INDEX_DEFINED -2
924 #define NOT_A_SEXP_OPERATOR -1
925 
926 // Karajorma - some useful helper methods
927 player * get_player_from_ship_node(int node, bool test_respawns = false);
928 ship * sexp_get_ship_from_node(int node);
929 
930 // hud-display-gauge magic values
931 #define SEXP_HUD_GAUGE_WARPOUT "warpout"
932 
933 // event log stuff
937 // Goober5000 - arg_item class stuff, borrowed from sexp_list_item class stuff -------------
938 void arg_item::add_data(char *str)
939 {
940  arg_item *item, *ptr;
941 
942  // create item
943  item = new arg_item;
944  item->text = str;
946 
947  // prepend item to existing list
948  ptr = this->next;
949  this->next = item;
950  item->next = ptr;
951 }
952 
953 void arg_item::add_data_dup(char *str)
954 {
955  arg_item *item, *ptr;
956 
957  // create item
958  item = new arg_item;
959  item->text = vm_strdup(str);
960  item->flags |= ARG_ITEM_F_DUP;
962 
963  // prepend item to existing list
964  ptr = this->next;
965  this->next = item;
966  item->next = ptr;
967 }
968 
970 {
971  arg_item *item, *ptr;
972 
973  // create item
974  item = new arg_item;
975  item->text = str;
976  item->flags |= ARG_ITEM_F_DUP;
978 
979  // prepend item to existing list
980  ptr = this->next;
981  this->next = item;
982  item->next = ptr;
983 }
984 
986 {
987  if (this->next != NULL) {
988  if (this->next->nesting_level >= Sexp_current_argument_nesting_level) {
989  return this->next;
990  }
991  }
992 
993  return NULL;
994 }
995 
997 {
998  arg_item *ptr;
999 
1000  // contiually delete first item of list
1001  while (this->next != NULL)
1002  {
1003  ptr = this->next->next;
1004 
1005  if (this->next->flags & ARG_ITEM_F_DUP)
1006  vm_free(this->next->text);
1007  delete this->next;
1008 
1009  this->next = ptr;
1010  }
1011 }
1012 
1014 {
1015  arg_item *ptr;
1016 
1017  // contiually delete first item of list
1018  while (this->next != NULL && this->next->nesting_level >= Sexp_current_argument_nesting_level )
1019  {
1020  ptr = this->next->next;
1021 
1022  if (this->next->flags & ARG_ITEM_F_DUP)
1023  vm_free(this->next->text);
1024  delete this->next;
1025 
1026  this->next = ptr;
1027  }
1028 }
1029 
1031 {
1032  return (this->next == NULL);
1033 }
1034 //-------------------------------------------------------------------------------------------------
1035 
1037 {
1038  if (Num_sexp_nodes == 0 || Sexp_nodes == NULL)
1039  return;
1040 
1041  nprintf(("SEXP", "Reinitializing sexp nodes...\n"));
1042  nprintf(("SEXP", "Entered function with %d nodes.\n", Num_sexp_nodes));
1043 
1044  // usually, the persistent nodes are grouped at the beginning of the array;
1045  // so we ought to be able to free all the subsequent nodes
1046  int i, last_persistent_node = -1;
1047 
1048  for (i = 0; i < Num_sexp_nodes; i++)
1049  {
1050  if (Sexp_nodes[i].type & SEXP_FLAG_PERSISTENT)
1051  last_persistent_node = i; // keep track of it
1052  else
1053  Sexp_nodes[i].type = SEXP_NOT_USED; // it's not needed
1054  }
1055 
1056  nprintf(("SEXP", "Last persistent node index is %d.\n", last_persistent_node));
1057 
1058  // if all the persistent nodes are gone, free all the nodes
1059  if (last_persistent_node == -1)
1060  {
1061  vm_free(Sexp_nodes);
1062  Sexp_nodes = NULL;
1063  Num_sexp_nodes = 0;
1064  }
1065  // if there's enough of a difference to make it worthwhile, free some nodes
1066  else if (Num_sexp_nodes - (last_persistent_node + 1) > 2 * SEXP_NODE_INCREMENT)
1067  {
1068  // round it up to the next evenly divisible size
1069  Num_sexp_nodes = (last_persistent_node + 1);
1070  Num_sexp_nodes += SEXP_NODE_INCREMENT - (Num_sexp_nodes % SEXP_NODE_INCREMENT);
1071 
1072  Sexp_nodes = (sexp_node *) vm_realloc(Sexp_nodes, sizeof(sexp_node) * Num_sexp_nodes);
1073  Verify(Sexp_nodes != NULL);
1074  }
1075 
1076  nprintf(("SEXP", "Exited function with %d nodes.\n", Num_sexp_nodes));
1077 }
1078 
1079 static void sexp_nodes_close()
1080 {
1081  // free all sexp nodes... should only be done on game shutdown
1082  if (Sexp_nodes != NULL)
1083  {
1084  vm_free(Sexp_nodes);
1085  Sexp_nodes = NULL;
1086  Num_sexp_nodes = 0;
1087  }
1088 }
1089 
1091 {
1092  // Goober5000
1093  Sexp_replacement_arguments.clear();
1094  Sexp_applicable_argument_list.expunge();
1095  Sexp_current_argument_nesting_level = 0;
1097 
1098  static bool done_sexp_atexit = false;
1099  if (!done_sexp_atexit)
1100  {
1101  atexit(sexp_nodes_close);
1102  done_sexp_atexit = true;
1103  }
1104 
1105  sexp_nodes_init();
1106  init_sexp_vars();
1108 
1110  Assert(Locked_sexp_false != -1);
1111  Sexp_nodes[Locked_sexp_false].type = SEXP_ATOM; // fix bypassing value
1113 
1115  Assert(Locked_sexp_true != -1);
1116  Sexp_nodes[Locked_sexp_true].type = SEXP_ATOM; // fix bypassing value
1117  Sexp_nodes[Locked_sexp_true].value = SEXP_KNOWN_TRUE;
1118 }
1119 
1123 int alloc_sexp(char *text, int type, int subtype, int first, int rest)
1124 {
1125  int node;
1126  int sexp_const = get_operator_const(text);
1127 
1128  if ((sexp_const == OP_TRUE) && (type == SEXP_ATOM) && (subtype == SEXP_ATOM_OPERATOR))
1129  return Locked_sexp_true;
1130 
1131  else if ((sexp_const == OP_FALSE) && (type == SEXP_ATOM) && (subtype == SEXP_ATOM_OPERATOR))
1132  return Locked_sexp_false;
1133 
1134  node = find_free_sexp();
1135 
1136  // need more sexp nodes?
1137  if (node == Num_sexp_nodes || node == -1)
1138  {
1139  int old_size = Num_sexp_nodes;
1140 
1142 
1143  // allocate in blocks of SEXP_NODE_INCREMENT
1144  Num_sexp_nodes += SEXP_NODE_INCREMENT;
1145  Sexp_nodes = (sexp_node *) vm_realloc(Sexp_nodes, sizeof(sexp_node) * Num_sexp_nodes);
1146 
1147  Verify(Sexp_nodes != NULL);
1148  nprintf(("SEXP", "Bumping dynamic sexp node limit from %d to %d...\n", old_size, Num_sexp_nodes));
1149 
1150  // clear all the new sexp nodes we just allocated
1151  memset(&Sexp_nodes[old_size], 0, sizeof(sexp_node) * SEXP_NODE_INCREMENT); //-V512
1152 
1153  // our new sexp is the first out of the ones we just created
1154  node = old_size;
1155  }
1156 
1157  Assert(node != Locked_sexp_true);
1158  Assert(node != Locked_sexp_false);
1159  Assert(strlen(text) < TOKEN_LENGTH);
1160  Assert(type >= 0);
1161 
1162  strcpy_s(Sexp_nodes[node].text, text);
1163  Sexp_nodes[node].type = type;
1164  Sexp_nodes[node].subtype = subtype;
1165  Sexp_nodes[node].first = first;
1166  Sexp_nodes[node].rest = rest;
1167  Sexp_nodes[node].value = SEXP_UNKNOWN;
1168  Sexp_nodes[node].flags = SNF_DEFAULT_VALUE; // Goober5000
1169  Sexp_nodes[node].op_index = NO_OPERATOR_INDEX_DEFINED;
1170 
1171  return node;
1172 }
1173 
1174 static int Sexp_hwm = 0;
1175 
1177 {
1178  int i, f = 0, p = 0;
1179 
1180  for (i = 0; i < Num_sexp_nodes; i++)
1181  {
1182  if (Sexp_nodes[i].type == SEXP_NOT_USED)
1183  f++;
1184  else if (Sexp_nodes[i].type & SEXP_FLAG_PERSISTENT)
1185  p++;
1186  }
1187 
1188  if (Num_sexp_nodes - f > Sexp_hwm)
1189  {
1190  nprintf(("Sexp", "Sexp nodes: Free=%d, Used=%d, Persistent=%d\n", f, Num_sexp_nodes - f, p));
1191  Sexp_hwm = Num_sexp_nodes - f;
1192  }
1193 
1194  return f;
1195 }
1196 
1201 {
1202  int i;
1203 
1204  // sanity
1205  if (Num_sexp_nodes == 0 || Sexp_nodes == NULL)
1206  return -1;
1207 
1208  for (i = 0; i < Num_sexp_nodes; i++)
1209  {
1210  if (Sexp_nodes[i].type == SEXP_NOT_USED)
1211  return i;
1212  }
1213 
1214  return -1;
1215 }
1216 
1221 {
1222  if (n == -1){
1223  return;
1224  }
1225 
1226  // total hack because of the true/false locked sexps -- we should make those persistent as well
1227  if ( (n == Locked_sexp_true) || (n == Locked_sexp_false) ){
1228  return;
1229  }
1230 
1231  Assert( !(Sexp_nodes[n].type & SEXP_FLAG_PERSISTENT) );
1232  Sexp_nodes[n].type |= SEXP_FLAG_PERSISTENT;
1233 
1234  sexp_mark_persistent(Sexp_nodes[n].first);
1235  sexp_mark_persistent(Sexp_nodes[n].rest);
1236 
1237 }
1238 
1243 {
1244  if (n == -1){
1245  return;
1246  }
1247 
1248  if ( (n == Locked_sexp_true) || (n == Locked_sexp_false) ){
1249  return;
1250  }
1251 
1252  Assert( Sexp_nodes[n].type & SEXP_FLAG_PERSISTENT );
1253  Sexp_nodes[n].type &= ~SEXP_FLAG_PERSISTENT;
1254 
1255  sexp_unmark_persistent(Sexp_nodes[n].first);
1256  sexp_unmark_persistent(Sexp_nodes[n].rest);
1257 }
1258 
1263 {
1264  Assert((num >= 0) && (num < Num_sexp_nodes));
1265  Assert(Sexp_nodes[num].type != SEXP_NOT_USED); // make sure it is actually used
1266  Assert(!(Sexp_nodes[num].type & SEXP_FLAG_PERSISTENT));
1267 
1268  if ((num == Locked_sexp_true) || (num == Locked_sexp_false))
1269  return 0;
1270 
1271  Sexp_nodes[num].type = SEXP_NOT_USED;
1272  return 1;
1273 }
1274 
1281 int free_sexp(int num)
1282 {
1283  int i, rest, count = 0;
1284 
1285  Assert((num >= 0) && (num < Num_sexp_nodes));
1286  Assert(Sexp_nodes[num].type != SEXP_NOT_USED); // make sure it is actually used
1287  Assert(!(Sexp_nodes[num].type & SEXP_FLAG_PERSISTENT));
1288 
1289  if ((num == Locked_sexp_true) || (num == Locked_sexp_false))
1290  return 0;
1291 
1292  Sexp_nodes[num].type = SEXP_NOT_USED;
1293  count++;
1294 
1295  i = Sexp_nodes[num].first;
1296  while (i != -1)
1297  {
1298  count += free_sexp(i);
1299  i = Sexp_nodes[i].rest;
1300  }
1301 
1302  rest = Sexp_nodes[num].rest;
1303  for (i = 0; i < Num_sexp_nodes; i++)
1304  {
1305  if (Sexp_nodes[i].first == num)
1306  Sexp_nodes[i].first = rest;
1307 
1308  if (Sexp_nodes[i].rest == num)
1309  Sexp_nodes[i].rest = rest;
1310  }
1311 
1312  return count; // total elements freed up.
1313 }
1314 
1321 int free_sexp2(int num)
1322 {
1323  int i, count = 0;
1324 
1325  if ((num == -1) || (num == Locked_sexp_true) || (num == Locked_sexp_false)){
1326  return 0;
1327  }
1328 
1329  i = Sexp_nodes[num].rest;
1330  while (i != -1) {
1331  count += free_sexp(i);
1332  i = Sexp_nodes[i].rest;
1333  }
1334 
1335  count += free_sexp(num);
1336  return count;
1337 }
1338 
1342 void flush_sexp_tree(int node)
1343 {
1344  if (node < 0){
1345  return;
1346  }
1347 
1348  Sexp_nodes[node].value = SEXP_UNKNOWN;
1349  flush_sexp_tree(Sexp_nodes[node].first);
1350  flush_sexp_tree(Sexp_nodes[node].rest);
1351 }
1352 
1353 int verify_sexp_tree(int node)
1354 {
1355  if (node == -1){
1356  return 0;
1357  }
1358 
1359  if ((Sexp_nodes[node].type == SEXP_NOT_USED) ||
1360  (Sexp_nodes[node].first == node) ||
1361  (Sexp_nodes[node].rest == node)) {
1362  Error(LOCATION, "Sexp node is corrupt");
1363  return -1;
1364  }
1365 
1366  if (Sexp_nodes[node].first != -1){
1367  verify_sexp_tree(Sexp_nodes[node].first);
1368  }
1369  if (Sexp_nodes[node].rest != -1){
1370  verify_sexp_tree(Sexp_nodes[node].rest);
1371  }
1372 
1373  return 0;
1374 }
1375 
1379 int dup_sexp_chain(int node)
1380 {
1381  int cur, first, rest;
1382 
1383  if (node == -1){
1384  return -1;
1385  }
1386 
1387  // TODO - CASE OF SEXP VARIABLES - ONLY 1 COPY OF VARIABLE
1388  first = dup_sexp_chain(Sexp_nodes[node].first);
1389  rest = dup_sexp_chain(Sexp_nodes[node].rest);
1390  cur = alloc_sexp(Sexp_nodes[node].text, Sexp_nodes[node].type, Sexp_nodes[node].subtype, first, rest);
1391 
1392  if (cur == -1) {
1393  if (first != -1){
1394  free_sexp(first);
1395  }
1396  if (rest != -1){
1397  free_sexp(rest);
1398  }
1399  }
1400 
1401  return cur;
1402 }
1403 
1408 int cmp_sexp_chains(int node1, int node2)
1409 {
1410  if ((node1 == -1) && (node2 == -1)){
1411  return 1;
1412  }
1413 
1414  if ((node1 == -1) || (node2 == -1)){
1415  return 0;
1416  }
1417 
1418  // DA: 1/7/99 Need to check the actual Sexp_node.text, not possible variable, which can be equal
1419  if (stricmp(Sexp_nodes[node1].text, Sexp_nodes[node2].text)){
1420  return 0;
1421  }
1422 
1423  if (!cmp_sexp_chains(Sexp_nodes[node1].first, Sexp_nodes[node2].first)){
1424  return 0;
1425  }
1426 
1427  if (!cmp_sexp_chains(Sexp_nodes[node1].rest, Sexp_nodes[node2].rest)){
1428  return 0;
1429  }
1430 
1431  return 1;
1432 }
1433 
1437 int query_node_in_sexp(int node, int sexp)
1438 {
1439  if (sexp == -1){
1440  return 0;
1441  }
1442  if (node == sexp){
1443  return 1;
1444  }
1445 
1446  if (query_node_in_sexp(node, Sexp_nodes[sexp].first)){
1447  return 1;
1448  }
1449  if (query_node_in_sexp(node, Sexp_nodes[sexp].rest)){
1450  return 1;
1451  }
1452 
1453  return 0;
1454 }
1455 
1460 {
1461  int i;
1462 
1463  for (i = 0; i < Num_sexp_nodes; i++)
1464  {
1465  if (Sexp_nodes[i].first == num)
1466  return i;
1467  }
1468 
1469  // not found
1470  return -1;
1471 }
1472 
1477 {
1478  int i;
1479  Assert((node >= 0) && (node < Num_sexp_nodes));
1480 
1481  if (Sexp_nodes[node].subtype == SEXP_ATOM_OPERATOR)
1482  {
1483  node = find_sexp_list(node);
1484 
1485  // are we already at the top of the list? this will happen for non-standard sexps
1486  // (sexps that fire instantly instead of using a conditional) such as:
1487  // $Formula: ( do-nothing )
1488  if (node < 0)
1489  return -1;
1490  }
1491 
1492  // iterate backwards through the sexps nodes (i.e. do the inverse of CDR)
1493  while (Sexp_nodes[node].subtype != SEXP_ATOM_OPERATOR)
1494  {
1495  for (i = 0; i < Num_sexp_nodes; i++)
1496  {
1497  if (Sexp_nodes[i].rest == node)
1498  break;
1499  }
1500 
1501  if (i == Num_sexp_nodes)
1502  return -1; // not found, probably at top node already.
1503 
1504  node = i;
1505  }
1506 
1507  return node;
1508 }
1509 
1515 int is_sexp_top_level( int node )
1516 {
1517  int i;
1518 
1519  Assert((node >= 0) && (node < Num_sexp_nodes));
1520 
1521  if (Sexp_nodes[node].type == SEXP_NOT_USED)
1522  return 0;
1523 
1524  for (i = 0; i < Num_sexp_nodes; i++)
1525  {
1526  if ((Sexp_nodes[i].type == SEXP_NOT_USED) || (i == node )) // don't check myself or unused nodes
1527  continue;
1528 
1529  if ((Sexp_nodes[i].first == node) || (Sexp_nodes[i].rest == node))
1530  return 0;
1531  }
1532 
1533  return 1;
1534 }
1535 
1539 int find_argnum(int parent, int arg)
1540 {
1541  int n, tally;
1542 
1543  n = CDR(parent);
1544  tally = 0;
1545 
1546  while (CAR(n) != arg)
1547  {
1548  if (n == -1)
1549  return -1;
1550 
1551  tally++;
1552  n = CDR(n);
1553  }
1554 
1555  return tally;
1556 }
1557 
1561 int get_operator_index(const char *token)
1562 {
1563  int i;
1564  Assertion(token != NULL, "get_operator_index(char*) called with a null token; get a coder!\n");
1565 
1566  for (i=0; i<Num_operators; i++){
1567  if (!stricmp(token, Operators[i].text)){
1568  return i;
1569  }
1570  }
1571 
1572  return NOT_A_SEXP_OPERATOR;
1573 }
1574 
1578 int get_operator_index(int node)
1579 {
1580  Assertion(node >= 0 && node < Num_sexp_nodes, "Passed an out-of-range node index (%d) to get_operator_index(int)!", node);
1581 
1582  if (!Fred_running && (Sexp_nodes[node].op_index != NO_OPERATOR_INDEX_DEFINED) ) {
1583  return Sexp_nodes[node].op_index;
1584  }
1585 
1586  int index = get_operator_index(Sexp_nodes[node].text);
1587  Sexp_nodes[node].op_index = index;
1588  return index;
1589 }
1590 
1591 
1595 int get_operator_const(const char *token)
1596 {
1597  int idx = get_operator_index(token);
1598 
1599  if (idx == NOT_A_SEXP_OPERATOR)
1600  return 0;
1601 
1602  return Operators[idx].value;
1603 }
1604 
1605 int get_operator_const(int node)
1606 {
1607  if (!Fred_running && Sexp_nodes[node].op_index >= 0) {
1608  return Operators[Sexp_nodes[node].op_index].value;
1609  }
1610 
1611  int idx = get_operator_index(node);
1612 
1613  if (idx == NOT_A_SEXP_OPERATOR)
1614  return 0;
1615 
1616  return Operators[idx].value;
1617 }
1618 
1619 int query_sexp_args_count(int node, bool only_valid_args = false)
1620 {
1621  int count = 0;
1622  int n = CDR(node);
1623 
1624  for ( ; n != -1; n = CDR(n))
1625  {
1626  if (only_valid_args && !(Sexp_nodes[n].flags & SNF_ARGUMENT_VALID))
1627  continue;
1628 
1629  count++;
1630  }
1631 
1632  return count;
1633 }
1634 
1641 {
1642  if (count < Operators[op].min || count > Operators[op].max)
1643  return 0;
1644 
1645  //send-message-list has arguments as blocks of 4
1646  if (op == OP_SEND_MESSAGE_LIST)
1647  if (count % 4 != 0)
1648  return 0;
1649 
1650  return 1;
1651 }
1652 
1658 int check_sexp_syntax(int node, int return_type, int recursive, int *bad_node, int mode)
1659 {
1660  int i = 0, z, t, type, argnum = 0, count, op, type2 = 0, op2;
1661  int op_node;
1662  int var_index = -1;
1663  size_t st;
1664 
1665  Assert(node >= 0 && node < Num_sexp_nodes);
1666  Assert(Sexp_nodes[node].type != SEXP_NOT_USED);
1667  if (Sexp_nodes[node].subtype == SEXP_ATOM_NUMBER && return_type == OPR_BOOL) {
1668  // special case Mark seems to want supported
1669  Assert(Sexp_nodes[node].first == -1); // only lists should have a first pointer
1670  if (Sexp_nodes[node].rest != -1) // anything after the number?
1671  return SEXP_CHECK_NONOP_ARGS; // if so, it's a syntax error
1672 
1673  return 0;
1674  }
1675 
1676  op_node = node; // save the node of the operator since we need to get to other args.
1677  if (bad_node)
1678  *bad_node = op_node;
1679 
1680  if (Sexp_nodes[op_node].subtype != SEXP_ATOM_OPERATOR)
1681  return SEXP_CHECK_OP_EXPECTED; // not an operator, which it should always be
1682 
1683  op = get_operator_index(CTEXT(op_node));
1684  if (op == -1)
1685  return SEXP_CHECK_UNKNOWN_OP; // unrecognized operator
1686 
1687  // check that types match - except that OPR_AMBIGUOUS matches everything
1688  if (return_type != OPR_AMBIGUOUS)
1689  {
1690  // get the return type of the next thing
1692  if (z == OPR_POSITIVE && return_type == OPR_NUMBER)
1693  {
1694  // positive data type can map to number data type just fine
1695  }
1696  // Goober5000's number hack
1697  else if (z == OPR_NUMBER && return_type == OPR_POSITIVE)
1698  {
1699  // this isn't kosher, but we hack it to make it work
1700  }
1701  else if (z != return_type)
1702  {
1703  // anything else is a mismatch
1704  return SEXP_CHECK_TYPE_MISMATCH;
1705  }
1706  }
1707 
1708  count = query_sexp_args_count(op_node);
1709 
1711  return SEXP_CHECK_BAD_ARG_COUNT; // incorrect number of arguments
1712 
1713  // Goober5000 - if this is a list of stuff that has the special argument as
1714  // an item in the list, assume it's valid
1716  return 0;
1717 
1718  node = Sexp_nodes[op_node].rest;
1719  while (node != -1) {
1720  type = query_operator_argument_type(op, argnum);
1721  Assert(Sexp_nodes[node].type != SEXP_NOT_USED);
1722  if (bad_node)
1723  *bad_node = node;
1724 
1725  if (Sexp_nodes[node].subtype == SEXP_ATOM_LIST) {
1726  i = Sexp_nodes[node].first;
1727  if (bad_node)
1728  *bad_node = i;
1729 
1730  // be sure to check to see if this node is a list of stuff and not an actual operator type
1731  // thing. (i.e. in the case of a cond statement, the conditional will fall into this if
1732  // statement. MORE TO DO HERE!!!!
1733  if (Sexp_nodes[i].subtype == SEXP_ATOM_LIST)
1734  return 0;
1735 
1736  op2 = get_operator_index(CTEXT(i));
1737  if (op2 == -1)
1738  return SEXP_CHECK_UNKNOWN_OP;
1739 
1740  type2 = query_operator_return_type(op2);
1741  if (recursive) {
1742  switch (type) {
1743  case OPF_NUMBER:
1744  t = OPR_NUMBER;
1745  break;
1746 
1747  case OPF_POSITIVE:
1748  t = OPR_POSITIVE;
1749  break;
1750 
1751  case OPF_BOOL:
1752  t = OPR_BOOL;
1753  break;
1754 
1755  case OPF_NULL:
1756  t = OPR_NULL;
1757  break;
1758 
1759  // Goober5000
1760  case OPF_FLEXIBLE_ARGUMENT:
1762  break;
1763 
1764  case OPF_AI_GOAL:
1765  t = OPR_AI_GOAL;
1766  break;
1767 
1768  // special case for modify-variable
1769  case OPF_AMBIGUOUS:
1770  t = OPR_AMBIGUOUS;
1771  break;
1772 
1773  default:
1774  return SEXP_CHECK_UNKNOWN_TYPE; // no other return types available
1775  }
1776 
1777  if ((z = check_sexp_syntax(i, t, recursive, bad_node)) != 0) {
1778  return z;
1779  }
1780  }
1781 
1782  } else if (Sexp_nodes[node].subtype == SEXP_ATOM_NUMBER) {
1783  char *ptr;
1784 
1785  type2 = OPR_POSITIVE;
1786  ptr = CTEXT(node);
1787  if (*ptr == '-') {
1788  type2 = OPR_NUMBER;
1789  ptr++;
1790  }
1791 
1792  if (type == OPF_BOOL) // allow numbers to be used where boolean is required.
1793  type2 = OPR_BOOL;
1794 
1795  while (*ptr) {
1796  if (!isdigit(*ptr))
1797  return SEXP_CHECK_INVALID_NUM; // not a valid number
1798 
1799  ptr++;
1800  }
1801 
1802  i = atoi(CTEXT(node));
1803  z = get_operator_const(CTEXT(op_node));
1804  if ( (z == OP_HAS_DOCKED_DELAY) || (z == OP_HAS_UNDOCKED_DELAY) )
1805  if ( (argnum == 2) && (i < 1) )
1807 
1808  // valid color range 0 to 255 - FUBAR
1809  if ((z == OP_CHANGE_IFF_COLOR) && ((argnum >= 2) && (argnum <= 4)))
1810  {
1811  if ( i < 0 || i > 255)
1812  {
1814  }
1815  }
1816 
1817  z = get_operator_index(CTEXT(op_node));
1818  if ( (query_operator_return_type(z) == OPR_AI_GOAL) && (argnum == Operators[op].min - 1) )
1819  if ( (i < 0) || (i > 200) )
1821 
1822  } else if (Sexp_nodes[node].subtype == SEXP_ATOM_STRING) {
1823  type2 = SEXP_ATOM_STRING;
1824 
1825  } else {
1826  Assert(0);
1827  }
1828 
1829  // variables should only be typechecked.
1830  if ((Sexp_nodes[node].type & SEXP_FLAG_VARIABLE) && (type != OPF_VARIABLE_NAME)) {
1831  var_index = get_index_sexp_variable_from_node(node);
1832  Assert(var_index != -1);
1833 
1834  switch (type) {
1835  case OPF_NUMBER:
1836  case OPF_POSITIVE:
1837  if (!(Sexp_variables[var_index].type & SEXP_VARIABLE_NUMBER))
1839  break;
1840 
1841  case OPF_AMBIGUOUS:
1842  break;
1843 
1844  default:
1845  if (!(Sexp_variables[var_index].type & SEXP_VARIABLE_STRING))
1847  }
1848  node = Sexp_nodes[node].rest;
1849  argnum++;
1850  continue;
1851  }
1852 
1853  switch (type) {
1854  case OPF_NAV_POINT:
1855  if (type2 != SEXP_ATOM_STRING){
1856  return SEXP_CHECK_TYPE_MISMATCH;
1857  }
1858  break;
1859 
1860  case OPF_NUMBER:
1861  if ((type2 != OPR_NUMBER) && (type2 != OPR_POSITIVE)){
1862  return SEXP_CHECK_TYPE_MISMATCH;
1863  }
1864 
1865  break;
1866 
1867  case OPF_POSITIVE:
1868  if (type2 == OPR_NUMBER){
1869  // Goober5000's number hack
1870  break;
1871  // return SEXP_CHECK_NEGATIVE_NUM;
1872  }
1873 
1874  if (type2 != OPR_POSITIVE){
1875  return SEXP_CHECK_TYPE_MISMATCH;
1876  }
1877 
1878  break;
1879 
1880  case OPF_SHIP_NOT_PLAYER:
1881  if (type2 != SEXP_ATOM_STRING){
1882  return SEXP_CHECK_TYPE_MISMATCH;
1883  }
1884 
1885  if (ship_name_lookup(CTEXT(node), 0) < 0)
1886  {
1888  {
1889  return SEXP_CHECK_INVALID_SHIP;
1890  }
1891  }
1892 
1893  break;
1894 
1895  case OPF_SHIP_OR_NONE:
1896  if (type2 != SEXP_ATOM_STRING)
1897  {
1898  return SEXP_CHECK_TYPE_MISMATCH;
1899  }
1900 
1901  if (stricmp(CTEXT(node), SEXP_NONE_STRING)) // none is okay
1902  {
1903  if (ship_name_lookup(CTEXT(node), 1) < 0)
1904  {
1906  {
1907  return SEXP_CHECK_INVALID_SHIP;
1908  }
1909  }
1910  }
1911 
1912  break;
1913 
1914  case OPF_SHIP:
1915  case OPF_SHIP_POINT:
1916  if (type2 != SEXP_ATOM_STRING){
1917  return SEXP_CHECK_TYPE_MISMATCH;
1918  }
1919 
1920  if (ship_name_lookup(CTEXT(node), 1) < 0) {
1922  {
1923  if (type == OPF_SHIP)
1924  { // return invalid ship if not also looking for point
1925  return SEXP_CHECK_INVALID_SHIP;
1926  }
1927 
1928  if (find_matching_waypoint(CTEXT(node)) == NULL)
1929  {
1930  if (verify_vector(CTEXT(node))) // verify return non-zero on invalid point
1931  {
1932  return SEXP_CHECK_INVALID_POINT;
1933  }
1934  }
1935  }
1936  }
1937 
1938  break;
1939 
1940  case OPF_WING:
1941  if (type2 != SEXP_ATOM_STRING){
1942  return SEXP_CHECK_TYPE_MISMATCH;
1943  }
1944 
1945  if (wing_name_lookup(CTEXT(node), 1) < 0){
1946  return SEXP_CHECK_INVALID_WING;
1947  }
1948 
1949  break;
1950 
1951  case OPF_SHIP_WING:
1954  case OPF_SHIP_WING_POINT:
1956  case OPF_ORDER_RECIPIENT:
1957  if ( type2 != SEXP_ATOM_STRING ){
1958  return SEXP_CHECK_TYPE_MISMATCH;
1959  }
1960 
1961  if (type == OPF_ORDER_RECIPIENT) {
1962  if (!strcmp ("<all fighters>", CTEXT(node))) {
1963  break;
1964  }
1965  }
1966 
1967  // all of these have ships and wings in common
1968  if (ship_name_lookup(CTEXT(node), 1) >= 0 || wing_name_lookup(CTEXT(node), 1) >= 0) {
1969  break;
1970  }
1971  // also check arrival list if we're running the game
1973  break;
1974  }
1975 
1976  // none is okay for _OR_NONE
1977  if (type == OPF_SHIP_WING_POINT_OR_NONE && !stricmp(CTEXT(node), SEXP_NONE_STRING)) {
1978  break;
1979  }
1980 
1981  // two different ways of checking teams
1982  if ((type == OPF_SHIP_WING_WHOLETEAM) && iff_lookup(CTEXT(node)) >= 0) {
1983  break;
1984  }
1985  if ((type == OPF_SHIP_WING_SHIPONTEAM_POINT) && sexp_determine_team(CTEXT(node)) >= 0) {
1986  break;
1987  }
1988 
1989  // only other possibility is waypoints
1991  if (find_matching_waypoint(CTEXT(node)) == NULL){
1992  if (verify_vector(CTEXT(node))){ // non-zero on verify vector mean invalid!
1993  return SEXP_CHECK_INVALID_POINT;
1994  }
1995  }
1996  break;
1997  }
1998 
1999  // nothing left
2001 
2002  case OPF_AWACS_SUBSYSTEM:
2004  case OPF_SUBSYSTEM:
2005  case OPF_SUBSYSTEM_OR_NONE:
2006  case OPF_SUBSYS_OR_GENERIC:
2007  {
2008  char *shipname;
2009  int shipnum,ship_class;
2010  int ship_index;
2011 
2012  if (type2 != SEXP_ATOM_STRING){
2013  return SEXP_CHECK_TYPE_MISMATCH;
2014  }
2015 
2016  // none is okay for subsys_or_none
2017  if (type == OPF_SUBSYSTEM_OR_NONE && !stricmp(CTEXT(node), SEXP_NONE_STRING))
2018  {
2019  break;
2020  }
2021 
2022  // subsys_or_generic has a few extra options it accepts
2024  break;
2025  }
2026 
2027  // we must get the model of the ship that is part of this sexpression and find a subsystem
2028  // with that name. This code assumes by default that the ship is *always* the first name
2029  // in the sexpression. If this is ever not the case, the code here must be changed to
2030  // get the correct ship name.
2031  switch(get_operator_const(CTEXT(op_node)))
2032  {
2034  case OP_DISTANCE_SUBSYSTEM:
2035  case OP_SET_CARGO:
2036  case OP_IS_CARGO:
2037  case OP_CHANGE_AI_CLASS:
2038  case OP_IS_AI_CLASS:
2039  case OP_MISSILE_LOCKED:
2041  ship_index = CDR(CDR(op_node));
2042  break;
2043 
2044  case OP_BEAM_FIRE:
2045  if(argnum == 1){
2046  ship_index = CDR(op_node);
2047  } else {
2048  ship_index = CDR(CDR(CDR(op_node)));
2049  }
2050  break;
2051 
2052  case OP_BEAM_FLOATING_FIRE:
2053  ship_index = CDDDDDR(CDDR(op_node));
2054  break;
2055 
2056  case OP_QUERY_ORDERS:
2057  ship_index = CDR(CDR(CDR(CDR(op_node))));
2058  break;
2059 
2060  case OP_WEAPON_CREATE:
2061  ship_index = CDDDDDR(CDDDDR(op_node));
2062  break;
2063 
2064  default :
2065  ship_index = CDR(op_node);
2066  break;
2067  }
2068 
2069  shipname = CTEXT(ship_index);
2070  shipnum = ship_name_lookup(shipname, 1);
2071  if (shipnum >= 0)
2072  {
2073  ship_class = Ships[shipnum].ship_info_index;
2074  }
2075  else
2076  {
2077  // must try to find the ship in the arrival list
2078  p_object *p_objp = mission_parse_get_arrival_ship(shipname);
2079 
2080  if (!p_objp)
2081  {
2082  if (type == OPF_SUBSYSTEM_OR_NONE)
2083  break;
2084  else
2085  return SEXP_CHECK_INVALID_SHIP;
2086  }
2087 
2088  ship_class = p_objp->ship_class;
2089  }
2090 
2091  // check for the special "hull" value
2092  if ( (Operators[op].value == OP_SABOTAGE_SUBSYSTEM) || (Operators[op].value == OP_REPAIR_SUBSYSTEM) || (Operators[op].value == OP_SET_SUBSYSTEM_STRNGTH) || (Operators[op].value == OP_SET_ARMOR_TYPE) || (Operators[op].value == OP_BEAM_FIRE)) {
2093  if ( !stricmp( CTEXT(node), SEXP_HULL_STRING) || !stricmp( CTEXT(node), SEXP_SIM_HULL_STRING) ){
2094  break;
2095  }
2096  }
2097  // check for special "shields" value for armor types
2098  if (Operators[op].value == OP_SET_ARMOR_TYPE) {
2099  if ( !stricmp( CTEXT(node), SEXP_SHIELD_STRING) || !stricmp( CTEXT(node), SEXP_SIM_HULL_STRING) ){
2100  break;
2101  }
2102  }
2103 
2104  for (i=0; i<Ship_info[ship_class].n_subsystems; i++)
2105  {
2106  if (!subsystem_stricmp(Ship_info[ship_class].subsystems[i].subobj_name, CTEXT(node)))
2107  {
2108  break;
2109  }
2110  }
2111 
2112  if (i == Ship_info[ship_class].n_subsystems)
2113  {
2115  }
2116 
2117  if(Fred_running)
2118  {
2119  // if we're checking for an AWACS subsystem and this is not an awacs subsystem
2120  if((type == OPF_AWACS_SUBSYSTEM) && !(Ship_info[ship_class].subsystems[i].flags & MSS_FLAG_AWACS))
2121  {
2123  }
2124 
2125  // rotating subsystem, like above - Goober5000
2126  if ((type == OPF_ROTATING_SUBSYSTEM) && !(Ship_info[ship_class].subsystems[i].flags & MSS_FLAG_ROTATES))
2127  {
2129  }
2130  }
2131 
2132  break;
2133  }
2134 
2135  case OPF_SUBSYSTEM_TYPE:
2136  for (i = 0; i < SUBSYSTEM_MAX; i++)
2137  {
2138  if (!stricmp(CTEXT(node), Subsystem_types[i]))
2139  break;
2140  }
2141 
2142  if (i == SUBSYSTEM_MAX)
2144 
2145  break;
2146 
2147  case OPF_POINT:
2148  if (type2 != SEXP_ATOM_STRING)
2149  {
2150  return SEXP_CHECK_TYPE_MISMATCH;
2151  }
2152 
2153  if (find_matching_waypoint(CTEXT(node)) == NULL)
2154  {
2155  if (verify_vector(CTEXT(node)))
2156  {
2157  return SEXP_CHECK_INVALID_POINT;
2158  }
2159  }
2160 
2161  break;
2162 
2163  case OPF_IFF:
2164  if (type2 != SEXP_ATOM_STRING)
2165  {
2166  return SEXP_CHECK_TYPE_MISMATCH;
2167  }
2168 
2169  if (iff_lookup(CTEXT(node)) < 0)
2170  {
2171  return SEXP_CHECK_INVALID_IFF;
2172  }
2173 
2174  break;
2175 
2176  case OPF_AI_CLASS:
2177  if (type2 != SEXP_ATOM_STRING)
2178  {
2179  return SEXP_CHECK_TYPE_MISMATCH;
2180  }
2181 
2182  for (i=0; i<Num_ai_classes; i++)
2183  {
2184  if (!stricmp(Ai_class_names[i], CTEXT(node)))
2185  {
2186  break;
2187  }
2188  }
2189 
2190  if (i == Num_ai_classes)
2191  {
2193  }
2194 
2195  break;
2196 
2197  case OPF_ARRIVAL_LOCATION:
2198  if (type2 != SEXP_ATOM_STRING)
2199  {
2200  return SEXP_CHECK_TYPE_MISMATCH;
2201  }
2202 
2203  for (i=0; i<MAX_ARRIVAL_NAMES; i++)
2204  {
2205  if (!stricmp(Arrival_location_names[i], CTEXT(node)))
2206  {
2207  break;
2208  }
2209  }
2210 
2211  if (i == MAX_ARRIVAL_NAMES)
2212  {
2214  }
2215 
2216  break;
2217 
2219  if (type2 != SEXP_ATOM_STRING)
2220  {
2221  return SEXP_CHECK_TYPE_MISMATCH;
2222  }
2223 
2224  for (i=0; i<MAX_DEPARTURE_NAMES; i++)
2225  {
2226  if (!stricmp(Departure_location_names[i], CTEXT(node)))
2227  {
2228  break;
2229  }
2230  }
2231 
2232  if (i == MAX_DEPARTURE_NAMES)
2233  {
2235  }
2236 
2237  break;
2238 
2240  if (type2 != SEXP_ATOM_STRING)
2241  {
2242  return SEXP_CHECK_TYPE_MISMATCH;
2243  }
2244  else
2245  {
2246  int get_special_anchor(char *name);
2247  int valid = 0;
2248 
2249  // <any friendly>, etc.
2250  if (get_special_anchor(CTEXT(node)) >= 0)
2251  {
2252  valid = 1;
2253  }
2254 
2255  if (ship_name_lookup(CTEXT(node), 1) >= 0)
2256  {
2257  valid = 1;
2258  }
2259 
2261  {
2262  valid = 1;
2263  }
2264 
2265  if (!valid)
2266  {
2268  }
2269  }
2270 
2271  break;
2272 
2273  case OPF_SOUNDTRACK_NAME:
2274  if (type2 != SEXP_ATOM_STRING){
2275  return SEXP_CHECK_TYPE_MISMATCH;
2276  }
2277 
2278  if (!stricmp(CTEXT(node), "<No Music>"))
2279  break;
2280 
2282  break;
2283 
2284  for (i=0; i<Num_soundtracks; i++)
2285  {
2286  if (!stricmp(CTEXT(node), Soundtracks[i].name))
2287  {
2288  break;
2289  }
2290  }
2291 
2292  if (i == Num_soundtracks)
2294 
2295  break;
2296 
2297  case OPF_SHIP_WITH_BAY:
2298  {
2299  char *name = CTEXT(node);
2300  p_object *p_objp;
2301  int shipnum = -1;
2302 
2303  if (type2 != SEXP_ATOM_STRING)
2304  return SEXP_CHECK_TYPE_MISMATCH;
2305 
2306  if (!stricmp(name, "<no anchor>"))
2307  break;
2308 
2309  shipnum = ship_name_lookup(name, 1);
2310  if (shipnum < 0)
2311  {
2312  if (Fred_running)
2313  return SEXP_CHECK_INVALID_SHIP;
2314 
2315  p_objp = mission_parse_get_arrival_ship(name);
2316  if (p_objp == NULL)
2317  return SEXP_CHECK_INVALID_SHIP;
2318 
2319  // Goober5000 - since we can't check POFs for ships which have yet to arrive
2320  // (not without a bit of work anyway), just assume they're okay
2321  break;
2322  }
2323 
2324  // ship exists at this point
2325 
2326  // now determine if this ship has a docking bay
2327  if (!ship_has_dock_bay(shipnum))
2329 
2330  break;
2331  }
2332 
2334  if (type2 != SEXP_ATOM_STRING){
2335  return SEXP_CHECK_TYPE_MISMATCH;
2336  }
2337 
2338  if (!stricmp(CTEXT(node), "<species support ship class>"))
2339  break;
2340 
2341  if (!stricmp(CTEXT(node), "<any support ship class>"))
2342  break;
2343 
2344  i = -1;
2345  for ( auto it = Ship_info.cbegin(); it != Ship_info.cend(); ++it ) {
2346  if ( !stricmp(CTEXT(node), it->name) )
2347  {
2348  if (it->flags & SIF_SUPPORT)
2349  {
2350  i = std::distance(Ship_info.cbegin(), it);
2351  break;
2352  }
2353  }
2354  }
2355 
2356  if ( i == -1 )
2358 
2359  break;
2360 
2361  case OPF_BOOL:
2362  if (type2 != OPR_BOOL){
2363  return SEXP_CHECK_TYPE_MISMATCH;
2364  }
2365 
2366  break;
2367 
2368  case OPF_AI_ORDER:
2369  if ( type2 != SEXP_ATOM_STRING ){
2370  return SEXP_CHECK_TYPE_MISMATCH;
2371  }
2372 
2373  break;
2374 
2375  case OPF_NULL:
2376  if (type2 != OPR_NULL){
2377  return SEXP_CHECK_TYPE_MISMATCH;
2378  }
2379 
2380  break;
2381 
2382  case OPF_SSM_CLASS:
2383  if ( type2 != SEXP_ATOM_STRING ) {
2384  return SEXP_CHECK_TYPE_MISMATCH;
2385  }
2386 
2387  if (ssm_info_lookup(CTEXT(node)) < 0) {
2389  }
2390 
2391  break;
2392 
2393  // Goober5000
2394  case OPF_FLEXIBLE_ARGUMENT:
2395  if (type2 != OPR_FLEXIBLE_ARGUMENT) {
2396  return SEXP_CHECK_TYPE_MISMATCH;
2397  }
2398  break;
2399 
2400  // Goober5000
2401  case OPF_ANYTHING:
2402  break;
2403 
2404  case OPF_AI_GOAL:
2405  if (type2 != OPR_AI_GOAL){
2406  return SEXP_CHECK_TYPE_MISMATCH;
2407  }
2408 
2409  if (Fred_running) {
2410  int ship_num, ship2, w = 0;
2411 
2412  // if it's the "goals" operator, this is part of initial orders, so just assume it's okay
2413  if (get_operator_const(Sexp_nodes[op_node].text) == OP_GOALS_ID) {
2414  break;
2415  }
2416 
2417  ship_num = ship_name_lookup(CTEXT(Sexp_nodes[op_node].rest), 1); // Goober5000 - include players
2418  if (ship_num < 0) {
2419  w = wing_name_lookup(CTEXT(Sexp_nodes[op_node].rest));
2420  if (w < 0) {
2421  if (bad_node){
2422  *bad_node = Sexp_nodes[op_node].rest;
2423  }
2424 
2425  return SEXP_CHECK_INVALID_SHIP; // should have already been caught earlier, but just in case..
2426  }
2427  }
2428 
2429  Assert(Sexp_nodes[node].subtype == SEXP_ATOM_LIST);
2430  z = Sexp_nodes[node].first;
2431  Assert(Sexp_nodes[z].subtype != SEXP_ATOM_LIST);
2433  if (ship_num >= 0) {
2434  if (!query_sexp_ai_goal_valid(z, ship_num)){
2436  }
2437 
2438  } else {
2439  for (i=0; i<Wings[w].wave_count; i++){
2440  if (!query_sexp_ai_goal_valid(z, Wings[w].ship_index[i])){
2442  }
2443  }
2444  }
2445 
2446  if ((z == OP_AI_DOCK) && (Sexp_nodes[node].rest >= 0)) {
2447  ship2 = ship_name_lookup(CTEXT(Sexp_nodes[node].rest), 1); // Goober5000 - include players
2448  if ((ship_num < 0) || !ship_docking_valid(ship_num, ship2)){
2450  }
2451  }
2452  }
2453 
2454  // we should check the syntax of the actual goal!!!!
2455  z = Sexp_nodes[node].first;
2456  if ((z = check_sexp_syntax(z, OPR_AI_GOAL, recursive, bad_node)) != 0){
2457  return z;
2458  }
2459 
2460  break;
2461 
2462  case OPF_SHIP_TYPE:
2463  if (type2 != SEXP_ATOM_STRING){
2464  return SEXP_CHECK_TYPE_MISMATCH;
2465  }
2466 
2467  i = ship_type_name_lookup(CTEXT(node));
2468 
2469  if (i < 0){
2471  }
2472 
2473  break;
2474 
2475  case OPF_WAYPOINT_PATH:
2476  if (find_matching_waypoint_list(CTEXT(node)) == NULL) {
2477  return SEXP_CHECK_TYPE_MISMATCH;
2478  }
2479  break;
2480 
2481  case OPF_MESSAGE:
2482  if (type2 != SEXP_ATOM_STRING)
2483  return SEXP_CHECK_TYPE_MISMATCH;
2484 
2485  if (Fred_running) {
2486  for (i=0; i<Num_messages; i++)
2487  if (!stricmp(Messages[i].name, CTEXT(node)))
2488  break;
2489 
2490  if (i == Num_messages)
2492  }
2493 
2494  break;
2495 
2496  case OPF_PRIORITY: {
2497  if (type2 != SEXP_ATOM_STRING)
2498  return SEXP_CHECK_TYPE_MISMATCH;
2499 
2500  if (Fred_running) { // should still check in Fred though..
2501  char *name;
2502 
2503  name = CTEXT(node);
2504  if (!stricmp(name, "low") || !stricmp(name, "normal") || !stricmp(name, "high"))
2505  break;
2506 
2508  }
2509 
2510  break;
2511  }
2512 
2513  case OPF_MISSION_NAME:
2514  if (type2 != SEXP_ATOM_STRING)
2515  return SEXP_CHECK_TYPE_MISMATCH;
2516 
2517  if (Fred_running) {
2518  if (mode == SEXP_MODE_CAMPAIGN) {
2519  for (i=0; i<Campaign.num_missions; i++)
2520  if (!stricmp(CTEXT(node), Campaign.missions[i].name)) {
2522  return SEXP_CHECK_INVALID_LEVEL;
2523 
2524  break;
2525  }
2526 
2527  if (i == Campaign.num_missions)
2529 
2530  } else {
2531  // mwa -- put the following if statement to prevent Fred errors for possibly valid
2532  // conditions. We should do something else here!!!
2533  if ( (Operators[op].value == OP_PREVIOUS_EVENT_TRUE) || (Operators[op].value == OP_PREVIOUS_EVENT_FALSE) || (Operators[op].value == OP_PREVIOUS_EVENT_INCOMPLETE)
2534  || (Operators[op].value == OP_PREVIOUS_GOAL_TRUE) || (Operators[op].value == OP_PREVIOUS_GOAL_FALSE) || (Operators[op].value == OP_PREVIOUS_GOAL_INCOMPLETE) )
2535  break;
2536 
2537  if (!(*Mission_filename) || stricmp(Mission_filename, CTEXT(node)))
2539  }
2540  }
2541 
2542  break;
2543 
2544  case OPF_GOAL_NAME:
2545  if (type2 != SEXP_ATOM_STRING)
2546  return SEXP_CHECK_TYPE_MISMATCH;
2547 
2548  // we only need to check the campaign list if running in Fred and are in campaign mode.
2549  // otherwise, check the set of current goals
2550  if ( Fred_running && (mode == SEXP_MODE_CAMPAIGN) ) {
2551  z = find_parent_operator(node);
2552  Assert(z >= 0);
2553  z = Sexp_nodes[z].rest; // first argument of operator should be mission name
2554  Assert(z >= 0);
2555  for (i=0; i<Campaign.num_missions; i++)
2556  if (!stricmp(CTEXT(z), Campaign.missions[i].name))
2557  break;
2558 
2559  // read the goal/event list from the mission file if both num_goals and num_events
2560  // are < 0
2561  if ((Campaign.missions[i].num_goals <= 0) && (Campaign.missions[i].num_events <= 0) )
2563 
2564  if (i < Campaign.num_missions) {
2565  for (t=0; t<Campaign.missions[i].num_goals; t++)
2566  if (!stricmp(CTEXT(node), Campaign.missions[i].goals[t].name))
2567  break;
2568 
2569  if (t == Campaign.missions[i].num_goals)
2571  }
2572  } else {
2573  // MWA -- short circuit evaluation of these things for now.
2574  if ( (Operators[op].value == OP_PREVIOUS_GOAL_TRUE) || (Operators[op].value == OP_PREVIOUS_GOAL_FALSE) || (Operators[op].value == OP_PREVIOUS_GOAL_INCOMPLETE) )
2575  break;
2576 
2577  for (i=0; i<Num_goals; i++)
2578  if (!stricmp(CTEXT(node), Mission_goals[i].name))
2579  break;
2580 
2581  if (i == Num_goals)
2583  }
2584 
2585  break;
2586 
2587  case OPF_EVENT_NAME:
2588  if ( type2 != SEXP_ATOM_STRING )
2589  return SEXP_CHECK_TYPE_MISMATCH;
2590 
2591  // like above checking for goals, check events in the campaign only if in Fred
2592  // and only if in campaign mode. Otherwise, check the current set of events
2593  if ( Fred_running && (mode == SEXP_MODE_CAMPAIGN) ) {
2594  z = find_parent_operator(node);
2595  Assert(z >= 0);
2596  z = Sexp_nodes[z].rest; // first argument of operator should be mission name
2597  Assert(z >= 0);
2598  for (i=0; i<Campaign.num_missions; i++)
2599  if (!stricmp(CTEXT(z), Campaign.missions[i].name))
2600  break;
2601 
2602  // read the goal/event list from the mission file if both num_goals and num_events
2603  // are < 0
2604  if ((Campaign.missions[i].num_goals <= 0) && (Campaign.missions[i].num_events <= 0) )
2606 
2607  if (i < Campaign.num_missions) {
2608  for (t=0; t<Campaign.missions[i].num_events; t++)
2609  if (!stricmp(CTEXT(node), Campaign.missions[i].events[t].name))
2610  break;
2611 
2612  if (t == Campaign.missions[i].num_events)
2614  }
2615  } else {
2616  // MWA -- short circuit evaluation of these things for now.
2617  if ( (Operators[op].value == OP_PREVIOUS_EVENT_TRUE) || (Operators[op].value == OP_PREVIOUS_EVENT_FALSE) || (Operators[op].value == OP_PREVIOUS_EVENT_INCOMPLETE) )
2618  break;
2619 
2620  for ( i = 0; i < Num_mission_events; i++ ) {
2621  if ( !stricmp(CTEXT(node), Mission_events[i].name) )
2622  break;
2623  }
2624  if ( i == Num_mission_events )
2626  }
2627  break;
2628 
2629  case OPF_DOCKER_POINT:
2630  if (type2 != SEXP_ATOM_STRING)
2631  return SEXP_CHECK_TYPE_MISMATCH;
2632 
2633  // This makes massive assumptions about the structure of the SEXP using it. If you add any
2634  // new SEXPs that use this OPF, you will probably need to edit this section to accommodate them.
2635  if (Fred_running) {
2636  int ship_num, model;
2637 
2638  // Look for the node containing the docker ship as its first argument. For set-docked, we want
2639  // the current SEXP. Otherwise (for ai-dock), we want its parent.
2640  if (get_operator_const(Sexp_nodes[op_node].text) == OP_SET_DOCKED) {
2641  z = op_node;
2642  }
2643  else {
2644  z = find_parent_operator(op_node);
2645 
2646  // if it's the "goals" operator, this is part of initial orders, so just assume it's okay
2647  if (get_operator_const(Sexp_nodes[z].text) == OP_GOALS_ID) {
2648  break;
2649  }
2650  }
2651 
2652  // look for the ship this goal is being assigned to
2653  ship_num = ship_name_lookup(CTEXT(Sexp_nodes[z].rest), 1);
2654  if (ship_num < 0) {
2655  if (bad_node)
2656  *bad_node = Sexp_nodes[z].rest;
2657 
2658  return SEXP_CHECK_INVALID_SHIP; // should have already been caught earlier, but just in case..
2659  }
2660 
2661  model = Ship_info[Ships[ship_num].ship_info_index].model_num;
2662  z = model_get_num_dock_points(model);
2663  for (i=0; i<z; i++)
2664  if (!stricmp(CTEXT(node), model_get_dock_name(model, i)))
2665  break;
2666 
2667  if (i == z)
2669  }
2670 
2671  break;
2672 
2673  case OPF_DOCKEE_POINT:
2674  if (type2 != SEXP_ATOM_STRING)
2675  return SEXP_CHECK_TYPE_MISMATCH;
2676 
2677  // This makes massive assumptions about the structure of the SEXP using it. If you add any
2678  // new SEXPs that use this OPF, you will probably need to edit this section to accommodate them.
2679  if (Fred_running) {
2680  int ship_num, model;
2681 
2682  // If we're using set-docked, we want to look up the ship from the third SEXP argument.
2683  if (get_operator_const(Sexp_nodes[op_node].text) == OP_SET_DOCKED) {
2684  //Navigate to the third argument
2685  z = op_node;
2686  for (i = 0; i < 3; i++)
2687  z = Sexp_nodes[z].rest;
2688 
2689  ship_num = ship_name_lookup(Sexp_nodes[z].text, 1);
2690  }
2691  else {
2692  ship_num = ship_name_lookup(CTEXT(Sexp_nodes[op_node].rest), 1);
2693  }
2694 
2695  if (ship_num < 0) {
2696  if (bad_node)
2697  *bad_node = Sexp_nodes[op_node].rest;
2698 
2699  return SEXP_CHECK_INVALID_SHIP; // should have already been caught earlier, but just in case..
2700  }
2701 
2702  model = Ship_info[Ships[ship_num].ship_info_index].model_num;
2703  z = model_get_num_dock_points(model);
2704  for (i=0; i<z; i++)
2705  if (!stricmp(CTEXT(node), model_get_dock_name(model, i)))
2706  break;
2707 
2708  if (i == z)
2710  }
2711 
2712  break;
2713 
2714  case OPF_WHO_FROM:
2715  if (type2 != SEXP_ATOM_STRING)
2716  return SEXP_CHECK_TYPE_MISMATCH;
2717 
2718  if (*CTEXT(node) != '#') { // not a manual source?
2719  if ( stricmp(CTEXT(node), "<any wingman>"))
2720  if ( stricmp(CTEXT(node), "<none>") ) // not a special token?
2721  if ((ship_name_lookup(CTEXT(node), TRUE) < 0) && (wing_name_lookup(CTEXT(node), 1) < 0)) // is it in the mission?
2724  }
2725 
2726  break;
2727 
2728  //Karajorma
2729  case OPF_PERSONA:
2730  if (type2 != SEXP_ATOM_STRING) {
2731  return SEXP_CHECK_TYPE_MISMATCH;
2732  }
2733 
2734  for (i=0; i < Num_personas ; i++) {
2735  if (!strcmp(CTEXT(node), Personas[i].name)) {
2736  break;
2737  }
2738  }
2739 
2740  if (i == Num_personas) {
2742  }
2743  break;
2744 
2745  case OPF_MISSION_MOOD:
2746  if (type2 != SEXP_ATOM_STRING) {
2747  return SEXP_CHECK_TYPE_MISMATCH;
2748  }
2749 
2750  for (i = 0; i < (int)Builtin_moods.size(); i++) {
2751  if (!strcmp(Builtin_moods[i].c_str(), CTEXT(node))) {
2752  break;
2753  }
2754  }
2755 
2756  if (i == (int)Builtin_moods.size()) {
2758  }
2759 
2760  break;
2761 
2762  case OPF_SHIP_FLAG:
2763  {
2764  bool found = false;
2765  for ( i = 0; i < MAX_OBJECT_FLAG_NAMES; i++) {
2766  if (!stricmp(Object_flag_names[i].flag_name, CTEXT(node))) {
2767  found = true;
2768  break;
2769  }
2770  }
2771 
2772  if (!found) {
2773  for ( i = 0; i < MAX_SHIP_FLAG_NAMES; i++) {
2774  if (!stricmp(Ship_flag_names[i].flag_name, CTEXT(node))) {
2775  found = true;
2776  break;
2777  }
2778  }
2779  }
2780 
2781  if (!found) {
2782  for ( i = 0; i < MAX_AI_FLAG_NAMES; i++) {
2783  if (!stricmp(Ai_flag_names[i].flag_name, CTEXT(node))) {
2784  found = true;
2785  break;
2786  }
2787  }
2788  }
2789 
2790  if (!found) {
2792  }
2793 
2794  break;
2795  }
2796 
2797  case OPF_TEAM_COLOR:
2798  if (type2 != SEXP_ATOM_STRING) {
2799  return SEXP_CHECK_TYPE_MISMATCH;
2800  }
2801 
2802  if (!stricmp(CTEXT(node), "none"))
2803  break;
2804 
2805  if (Team_Colors.find(CTEXT(node)) == Team_Colors.end())
2807 
2808  break;
2809 
2810  case OPF_FONT:
2811  if (type2 != SEXP_ATOM_STRING) {
2812  return SEXP_CHECK_TYPE_MISMATCH;
2813  }
2814 
2815  for (i = 0; i < Num_fonts; i++) {
2816  if (!stricmp(CTEXT(node), Fonts[i].filename)) {
2817  break;
2818  }
2819  }
2820 
2821  if (i == Num_fonts) {
2822  return SEXP_CHECK_INVALID_FONT;
2823  }
2824  break;
2825 
2826  case OPF_SOUND_ENVIRONMENT:
2827  if (type2 != SEXP_ATOM_STRING) {
2828  return SEXP_CHECK_TYPE_MISMATCH;
2829  }
2830 
2831  if (stricmp(CTEXT(node), SEXP_NONE_STRING) && ds_eax_get_preset_id(CTEXT(node)) < 0) {
2833  }
2834  break;
2835 
2837  if (type2 != SEXP_ATOM_STRING) {
2838  return SEXP_CHECK_TYPE_MISMATCH;
2839  }
2840 
2841  if (audio_volume_option_lookup(CTEXT(node)) == -1)
2843  break;
2844 
2845  case OPF_HUD_GAUGE:
2846  if (type2 != SEXP_ATOM_STRING) {
2847  return SEXP_CHECK_TYPE_MISMATCH;
2848  }
2849 
2850  if (hud_gauge_type_lookup(CTEXT(node)) == -1)
2852  break;
2853 
2855  if (type2 != SEXP_ATOM_STRING) {
2856  return SEXP_CHECK_TYPE_MISMATCH;
2857  }
2858 
2861  }
2862  break;
2863 
2864  case OPF_EXPLOSION_OPTION:
2865  if (type2 != SEXP_ATOM_STRING) {
2866  return SEXP_CHECK_TYPE_MISMATCH;
2867  }
2868 
2869  if (sexp_explosion_option_lookup(CTEXT(node)) < 0) {
2871  }
2872  break;
2873 
2874  case OPF_KEYPRESS:
2875  if (type2 != SEXP_ATOM_STRING)
2876  return SEXP_CHECK_TYPE_MISMATCH;
2877 
2878  break;
2879 
2880  case OPF_CARGO:
2881  case OPF_STRING:
2882  case OPF_MESSAGE_OR_STRING:
2883  if (type2 != SEXP_ATOM_STRING)
2884  return SEXP_CHECK_TYPE_MISMATCH;
2885  break;
2886 
2887  case OPF_SKILL_LEVEL:
2888  if ( type2 != SEXP_ATOM_STRING )
2889  return SEXP_CHECK_TYPE_MISMATCH;
2890 
2891  for (i = 0; i < NUM_SKILL_LEVELS; i++) {
2892  if ( !stricmp(CTEXT(node), Skill_level_names(i, 0)) )
2893  break;
2894  }
2895  if ( i == NUM_SKILL_LEVELS )
2897  break;
2898 
2899  case OPF_MEDAL_NAME:
2900  if ( type2 != SEXP_ATOM_STRING)
2901  return SEXP_CHECK_TYPE_MISMATCH;
2902 
2903  for (i = 0; i < Num_medals; i++) {
2904  if ( !stricmp(CTEXT(node), Medals[i].name) )
2905  break;
2906  }
2907 
2908  if ( i == Num_medals )
2910  break;
2911 
2912  case OPF_HUGE_WEAPON:
2913  case OPF_WEAPON_NAME:
2914  if ( type2 != SEXP_ATOM_STRING )
2915  return SEXP_CHECK_TYPE_MISMATCH;
2916 
2917  for (i = 0; i < Num_weapon_types; i++ ) {
2918  if ( !stricmp(CTEXT(node), Weapon_info[i].name) )
2919  break;
2920  }
2921 
2922  if ( i == Num_weapon_types )
2924 
2925  // we need to be sure that for huge weapons, the WIF_HUGE flag is set
2926  if ( type == OPF_HUGE_WEAPON ) {
2927  if ( !(Weapon_info[i].wi_flags & WIF_HUGE) )
2929  }
2930 
2931  break;
2932 
2933  // Goober5000
2934  case OPF_INTEL_NAME:
2935  if ( type2 != SEXP_ATOM_STRING )
2936  return SEXP_CHECK_TYPE_MISMATCH;
2937 
2938  for (i = 0; i < Intel_info_size; i++ ) {
2939  if ( !stricmp(CTEXT(node), Intel_info[i].name) )
2940  break;
2941  }
2942 
2943  if ( i == Intel_info_size )
2945 
2946  break;
2947 
2949  if ( type2 != SEXP_ATOM_STRING )
2950  return SEXP_CHECK_TYPE_MISMATCH;
2951 
2952  for (i = 0; i < NUM_TURRET_ORDER_TYPES; i++ ) {
2953  if ( !stricmp(CTEXT(node), Turret_target_order_names[i]) )
2954  break;
2955  }
2956 
2957  if ( i == NUM_TURRET_ORDER_TYPES )
2959 
2960  break;
2961 
2962  case OPF_ARMOR_TYPE:
2963  if ( type2 != SEXP_ATOM_STRING )
2964  return SEXP_CHECK_TYPE_MISMATCH;
2965 
2966  if (!stricmp(CTEXT(node), SEXP_NONE_STRING))
2967  break;
2968 
2969  for (st = 0; st < Armor_types.size(); st++ ) {
2970  if ( !stricmp(CTEXT(node), Armor_types[st].GetNamePtr()) )
2971  break;
2972  }
2973 
2974  if ( st == Armor_types.size() )
2976 
2977  break;
2978 
2979  case OPF_DAMAGE_TYPE:
2980  if ( type2 != SEXP_ATOM_STRING )
2981  return SEXP_CHECK_TYPE_MISMATCH;
2982 
2983  if (!stricmp(CTEXT(node), SEXP_NONE_STRING))
2984  break;
2985 
2986  for (st = 0; st < Damage_types.size(); st++ ) {
2987  if ( !stricmp(CTEXT(node), Damage_types[st].name) )
2988  break;
2989  }
2990 
2991  if ( st == Damage_types.size() )
2993 
2994  break;
2995 
2996  case OPF_ANIMATION_TYPE:
2997  if ( type2 != SEXP_ATOM_STRING )
2998  return SEXP_CHECK_TYPE_MISMATCH;
2999 
3000  i = model_anim_match_type(CTEXT(node));
3001  if ( i == TRIGGER_TYPE_NONE )
3003 
3004  break;
3005 
3006  case OPF_TARGET_PRIORITIES:
3007  if ( type2 != SEXP_ATOM_STRING )
3008  return SEXP_CHECK_TYPE_MISMATCH;
3009 
3010  for(st = 0; st < Ai_tp_list.size(); st++) {
3011  if ( !stricmp(CTEXT(node), Ai_tp_list[st].name) )
3012  break;
3013  }
3014 
3015  if ( st == Ai_tp_list.size() )
3017 
3018  break;
3019 
3020  case OPF_SHIP_CLASS_NAME:
3021  if ( type2 != SEXP_ATOM_STRING )
3022  return SEXP_CHECK_TYPE_MISMATCH;
3023 
3024  if (ship_info_lookup(CTEXT(node)) < 0)
3026 
3027  break;
3028 
3029  case OPF_HUD_GAUGE_NAME:
3030  if ( type2 != SEXP_ATOM_STRING )
3031  return SEXP_CHECK_TYPE_MISMATCH;
3032 
3033  for ( i = 0; i < NUM_HUD_GAUGES; i++ ) {
3034  if ( !stricmp(CTEXT(node), HUD_gauge_text[i]) )
3035  break;
3036  }
3037 
3038  // if we reached the end of the list, then the name is invalid
3039  if ( i == NUM_HUD_GAUGES )
3041 
3042  break;
3043 
3044  case OPF_SKYBOX_MODEL_NAME:
3045  if ( type2 != SEXP_ATOM_STRING )
3046  return SEXP_CHECK_TYPE_MISMATCH;
3047 
3048  if ( stricmp(CTEXT(node), NOX("default")) && !strstr(CTEXT(node), NOX(".pof")) )
3050 
3051  break;
3052 
3053  case OPF_SKYBOX_FLAGS:
3054  if ( type2 != SEXP_ATOM_STRING )
3055  return SEXP_CHECK_TYPE_MISMATCH;
3056 
3057  for ( i = 0; i < Num_skybox_flags; ++i ) {
3058  if ( !stricmp( CTEXT(node), Skybox_flags[i]) )
3059  break;
3060  }
3061 
3062  // if we reached the end of the list, then the flag is invalid
3063  if ( i == Num_skybox_flags )
3065 
3066  break;
3067 
3068  case OPF_JUMP_NODE_NAME:
3069  if ( type2 != SEXP_ATOM_STRING )
3070  return SEXP_CHECK_TYPE_MISMATCH;
3071 
3072  if (jumpnode_get_by_name(CTEXT(node)) == NULL)
3074 
3075  break;
3076 
3077 
3078  case OPF_VARIABLE_NAME:
3079  var_index = get_index_sexp_variable_from_node(node);
3080  if ( var_index == -1) {
3082  }
3083 
3084  switch (Operators[op].value)
3085  {
3086  // some SEXPs demand a number variable
3088  case OP_ADD_SUN_BITMAP:
3089  if (!(Sexp_variables[var_index].type & SEXP_VARIABLE_NUMBER))
3091  break;
3092 
3093  // some demand a string variable
3094  case OP_STRING_CONCATENATE:
3095  case OP_INT_TO_STRING:
3098  case OP_SCRIPT_EVAL_STRING:
3099  if (!(Sexp_variables[var_index].type & SEXP_VARIABLE_STRING))
3101  break;
3102 
3103  default:
3104  break;
3105  }
3106 
3107  // otherwise anything goes
3108  break;
3109 
3110  case OPF_AMBIGUOUS:
3111  // type checking for modify-variable
3112  // string or number -- anything goes
3113  break;
3114 
3115  case OPF_BACKGROUND_BITMAP:
3116  case OPF_SUN_BITMAP:
3117  case OPF_NEBULA_STORM_TYPE:
3118  case OPF_NEBULA_POOF:
3119  case OPF_NEBULA_PATTERN:
3120  case OPF_POST_EFFECT:
3121  if (type2 != SEXP_ATOM_STRING) {
3122  return SEXP_CHECK_TYPE_MISMATCH;
3123  }
3124  break;
3125 
3126  case OPF_HUD_ELEMENT:
3127  if (type2 != SEXP_ATOM_STRING) {
3128  return SEXP_CHECK_TYPE_MISMATCH;
3129  } else {
3130  char *gauge = CTEXT(node);
3131  if ( strcmp(SEXP_HUD_GAUGE_WARPOUT, gauge) == 0 ) {
3132  break;
3133  }
3134  }
3136 
3138  if (type2 != SEXP_ATOM_STRING) {
3139  return SEXP_CHECK_TYPE_MISMATCH;
3140  }
3141 
3142  if (!stricmp(CTEXT(node), SEXP_ALL_BANKS_STRING)) {
3143  break;
3144  }
3145 
3146  // if we haven't specified all banks we need to check the number of the bank is legal
3147  else {
3148  int num_banks = atoi(CTEXT(node));
3149  if ((num_banks >= MAX_SHIP_PRIMARY_BANKS) && (num_banks >= MAX_SHIP_SECONDARY_BANKS)) {
3151  }
3152  }
3153  break;
3154 
3155  case OPF_SHIP_EFFECT:
3156  if (type2 != SEXP_ATOM_STRING) {
3157  return SEXP_CHECK_TYPE_MISMATCH;
3158  }
3159 
3160  if (get_effect_from_name(CTEXT(node)) == -1 ) {
3162  }
3163  break;
3164 
3165  case OPF_GAME_SND:
3166  if (type2 == SEXP_ATOM_NUMBER)
3167  {
3168  if (gamesnd_get_by_tbl_index(atoi(CTEXT(node))) < 0)
3169  {
3171  }
3172  }
3173  else if (type2 == SEXP_ATOM_STRING)
3174  {
3175  if (stricmp(CTEXT(node), SEXP_NONE_STRING) && gamesnd_get_by_name(CTEXT(node)) < 0)
3176  {
3178  }
3179  }
3180  break;
3181 
3182  default:
3183  Error(LOCATION, "Unhandled argument format");
3184  }
3185 
3186  node = Sexp_nodes[node].rest;
3187  argnum++;
3188  }
3189 
3190  return 0;
3191 }
3192 
3193 // Goober5000
3194 void get_unformatted_sexp_variable_name(char *unformatted, char *formatted_pre)
3195 {
3196  int end_index;
3197  char *formatted;
3198 
3199  // Goober5000 - trim @ if needed
3200  if (formatted_pre[0] == SEXP_VARIABLE_CHAR)
3201  formatted = formatted_pre+1;
3202  else
3203  formatted = formatted_pre;
3204 
3205  // get variable name (up to '['
3206  end_index = strcspn(formatted, "[");
3207  Assert( (end_index != 0) && (end_index < TOKEN_LENGTH-1) );
3208  strncpy(unformatted, formatted, end_index);
3209  unformatted[end_index] = '\0';
3210 }
3211 
3218 void get_sexp_text_for_variable(char *text, char *token)
3219 {
3220  int sexp_var_index;
3221 
3223 
3224  if ( !Fred_running ) {
3225  // freespace - get index into Sexp_variables array
3226  sexp_var_index = get_index_sexp_variable_name(text);
3227  Assert(sexp_var_index != -1);
3228  sprintf(text, "%d", sexp_var_index);
3229  }
3230 }
3231 
3232 // Goober5000
3233 void do_preload_for_arguments(void (*preloader)(char *), int arg_node, int arg_handler_node)
3234 {
3235  // we have a special argument
3236  if (!strcmp(Sexp_nodes[arg_node].text, SEXP_ARGUMENT_STRING))
3237  {
3238  int n;
3239 
3240  // we might not be handling it, either because this is not a *_of operator
3241  // or because we've disabled preloading for special arguments
3242  if (arg_handler_node < 0)
3243  return;
3244 
3245  // preload for each argument
3246  for (n = CDR(arg_handler_node); n != -1; n = CDR(n))
3247  {
3248  preloader(CTEXT(n));
3249  }
3250  }
3251  // we don't
3252  else
3253  {
3254  // preload for just the one argument
3255  preloader(CTEXT(arg_node));
3256  }
3257 }
3258 
3259 // Goober5000
3261 {
3262  int idx;
3263  ship_info *sip;
3264 
3265  idx = ship_info_lookup(text);
3266  if (idx < 0)
3267  return;
3268 
3269  // preload the model, just in case there is no other ship of this class in the mission
3270  // (this eliminates the slight pause during a mission when changing to a previously unloaded model)
3271  sip = &Ship_info[idx];
3272  sip->model_num = model_load(sip->pof_file, sip->n_subsystems, &sip->subsystems[0]);
3273 
3274  if (sip->model_num >= 0)
3275  model_page_in_textures(sip->model_num, idx);
3276 }
3277 
3278 // Goober5000
3280 {
3281  int idx;
3282 
3283  idx = weapon_info_lookup(text);
3284  if (idx < 0)
3285  return;
3286 
3287  weapon_mark_as_used(idx);
3288 }
3289 
3296 {
3297  int start, node, last, op, count;
3298  char token[TOKEN_LENGTH];
3299  char variable_text[TOKEN_LENGTH];
3300 
3301  Assert(*(Mp-1) == '(');
3302 
3303  // start - the node allocated in first instance of function
3304  // node - the node allocated in current instance of function
3305  // count - number of nodes allocated this instance of function [do we set last.rest or .first]
3306  // variable - whether string or number is a variable referencing Sexp_variables
3307 
3308  // initialization
3309  start = last = -1;
3310  count = 0;
3311 
3313  while (*Mp != ')') {
3314  // end of string or end of file
3315  if (*Mp == '\0' || *Mp == EOF_CHAR) {
3316  Error(LOCATION, "Unexpected end of sexp!");
3317  return -1;
3318  }
3319 
3320  // Sexp list
3321  if (*Mp == '(') {
3322  Mp++;
3323  node = alloc_sexp("", SEXP_LIST, SEXP_ATOM_LIST, get_sexp(), -1);
3324  }
3325 
3326  // Sexp string
3327  else if (*Mp == '\"') {
3328  int len = strcspn(Mp + 1, "\"");
3329  // was closing quote not found?
3330  if (*(Mp + 1 + len) != '\"') {
3331  Error(LOCATION, "Unexpected end of quoted string embedded in sexp!");
3332  return -1;
3333  }
3334 
3335  // check if string variable
3336  if ( *(Mp + 1) == SEXP_VARIABLE_CHAR ) {
3337  char variable_token[2*TOKEN_LENGTH+2]; // variable_token[contents_token]
3338 
3339  // reduce length by 1 for end \"
3340  int length = len - 1;
3341  if (length >= 2*TOKEN_LENGTH+2) {
3342  Error(LOCATION, "Variable token %s is too long. Needs to be %d characters or shorter.", Mp, 2*TOKEN_LENGTH+2 - 1);
3343  return -1;
3344  }
3345 
3346  // start copying after skipping 1st char (i.e. variable char)
3347  strncpy(variable_token, Mp + 2, length);
3348  variable_token[length] = 0;
3349 
3350  get_sexp_text_for_variable(variable_text, variable_token);
3351  node = alloc_sexp(variable_text, (SEXP_ATOM | SEXP_FLAG_VARIABLE), SEXP_ATOM_STRING, -1, -1);
3352  } else {
3353  // token is too long?
3354  if (len >= TOKEN_LENGTH) {
3355  Error(LOCATION, "Token %s is too long. Needs to be %d characters or shorter.", Mp, TOKEN_LENGTH - 1);
3356  return -1;
3357  }
3358 
3359  strncpy(token, Mp + 1, len);
3360  token[len] = 0;
3361  node = alloc_sexp(token, SEXP_ATOM, SEXP_ATOM_STRING, -1, -1);
3362  }
3363 
3364  // bump past closing \" by 1 char
3365  Mp += (len + 2);
3366 
3367  }
3368 
3369  // Sexp operator or number
3370  else {
3371  int len = 0;
3372  bool variable = false;
3373  while (*Mp != ')' && !is_white_space(*Mp)) {
3374  // numeric variable?
3375  if ( (len == 0) && (*Mp == SEXP_VARIABLE_CHAR) ) {
3376  variable = true;
3377  Mp++;
3378  continue;
3379  }
3380 
3381  // end of string or end of file?
3382  if (*Mp == '\0' || *Mp == EOF_CHAR) {
3383  Error(LOCATION, "Unexpected end of sexp!");
3384  return -1;
3385  }
3386 
3387  // token is too long?
3388  if (len >= TOKEN_LENGTH - 1) {
3389  token[TOKEN_LENGTH - 1] = '\0';
3390  Error(LOCATION, "Token %s is too long. Needs to be %d characters or shorter.", token, TOKEN_LENGTH - 1);
3391  return -1;
3392  }
3393 
3394  // build the token
3395  token[len++] = *Mp++;
3396  }
3397  token[len] = 0;
3398 
3399  // maybe replace deprecated names
3400  if (!stricmp(token, "set-ship-position"))
3401  strcpy_s(token, "set-object-position");
3402  else if (!stricmp(token, "set-ship-facing"))
3403  strcpy_s(token, "set-object-facing");
3404  else if (!stricmp(token, "set-ship-facing-object"))
3405  strcpy_s(token, "set-object-facing-object");
3406  else if (!stricmp(token, "ai-chase-any-except"))
3407  strcpy_s(token, "ai-chase-any");
3408  else if (!stricmp(token, "change-ship-model"))
3409  strcpy_s(token, "change-ship-class");
3410  else if (!stricmp(token, "radar-set-max-range"))
3411  strcpy_s(token, "hud-set-max-targeting-range");
3412  else if (!stricmp(token, "ship-subsys-vanished"))
3413  strcpy_s(token, "ship-subsys-vanish");
3414  else if (!stricmp(token, "directive-is-variable"))
3415  strcpy_s(token, "directive-value");
3416  else if (!stricmp(token, "variable-array-get"))
3417  strcpy_s(token, "get-variable-by-index");
3418  else if (!stricmp(token, "variable-array-set"))
3419  strcpy_s(token, "set-variable-by-index");
3420 
3421  op = get_operator_index(token);
3422  if (op >= 0) {
3423  node = alloc_sexp(token, SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, -1);
3424  } else {
3425  if ( variable ) {
3426  // convert token text for variable
3427  get_sexp_text_for_variable(variable_text, token);
3428 
3429  node = alloc_sexp(variable_text, (SEXP_ATOM | SEXP_FLAG_VARIABLE), SEXP_ATOM_NUMBER, -1, -1);
3430  } else {
3431  node = alloc_sexp(token, SEXP_ATOM, SEXP_ATOM_NUMBER, -1, -1);
3432  }
3433  }
3434  }
3435 
3436  // update links
3437  if (count++) {
3438  Assert(last != -1);
3439  Sexp_nodes[last].rest = node;
3440  } else {
3441  start = node;
3442  }
3443 
3444  Assert(node != -1); // ran out of nodes. Time to raise the MAX!
3445  last = node;
3447  }
3448 
3449  Mp++; // skip past the ')'
3450 
3451 
3452  // Goober5000 - backwards compatibility for removed ai-chase-any-except
3453  if (get_operator_const(CTEXT(start)) == OP_AI_CHASE_ANY)
3454  {
3455  // if there is more than one argument, free the extras
3456  int n = CDR(CDR(start));
3457  if (n >= 0)
3458  {
3459  // free the entire rest of the argument list
3460  free_sexp2(n);
3461  }
3462  }
3463 
3464  // Goober5000 - preload stuff for certain sexps
3465  if (!Fred_running)
3466  {
3467  int n, parent, arg_handler = -1;
3468 
3469  // see if we're using special arguments
3470  parent = find_parent_operator(start);
3471  if (parent >= 0 && is_blank_argument_op(get_operator_const(CTEXT(parent))))
3472  {
3473  // get the first op of the parent, which should be a *_of operator
3474  arg_handler = CADR(parent);
3475  if (arg_handler >= 0 && !is_blank_of_op(get_operator_const(CTEXT(arg_handler))))
3476  arg_handler = -1;
3477  }
3478 
3479  // DISABLE ARGUMENT PRELOADING FOR NOW
3480  // see Mantis #925 for discussion
3481  // Also, the preloader will have to be moved to a different function (after the parsing is finished)
3482  // in order to work properly with special arguments. The current node is an orphan until get_sexp
3483  // completes, at which time it will be linked into the sexp node list; this means that it is
3484  // impossible to get the parent node.
3485  arg_handler = -1;
3486 
3487  // preload according to sexp
3488  op = get_operator_const(CTEXT(start));
3489  switch (op)
3490  {
3491  case OP_CHANGE_SHIP_CLASS:
3492  // ship class is argument #1
3493  n = CDR(start);
3495  break;
3496 
3497  case OP_SHIP_CREATE:
3498  // ship class is argument #2
3499  n = CDDR(start);
3500  // page in ship classes of dynamically created ships
3501  // preload_change_ship_class doesn't require a class change, so we can use that here -zookeeper
3503  break;
3504 
3506  // set flag for taylor
3508  break;
3509 
3510  case OP_MISSION_SET_NEBULA:
3511  // set flag for WMC
3512  Nebula_sexp_used = true;
3513  Dynamic_environment = true;
3514  break;
3515 
3517  Dynamic_environment = true;
3518  break;
3519 
3520  case OP_WARP_EFFECT:
3521  // type of warp is argument #11
3522  n = CDDDDDR(start);
3523  n = CDDDDDR(n);
3524  n = CDR(n);
3525 
3526  // set flag for taylor
3527  if (CAR(n) != -1 || !strcmp(Sexp_nodes[n].text, SEXP_ARGUMENT_STRING)) // if it's evaluating a sexp or a special argument
3528  Knossos_warp_ani_used = 1; // set flag just in case
3529  else if (atoi(CTEXT(n)) == 1) // if it's the Knossos type
3530  Knossos_warp_ani_used = 1; // set flag for sure
3531  break;
3532 
3533  case OP_SET_SKYBOX_MODEL:
3534  // model is argument #1
3535  n = CDR(start);
3537  Dynamic_environment = true;
3538  break;
3539 
3541  // weapon to change to is arg #3
3542  n = CDDDR(start);
3544  break;
3545 
3546  case OP_ADD_SUN_BITMAP:
3547  n = CDR(start);
3549  Dynamic_environment = true;
3550  break;
3551 
3553  n = CDR(start);
3555  Dynamic_environment = true;
3556  break;
3557 
3559  // do XSTR translation for each entry in the list
3560  // we don't use the do_preload function because the preloader needs to access two nodes at a time
3561  // also we're not using CTEXT or eval_num here because XSTR should really be constant, and
3562  // also because we can't really run sexp stuff in a preloader
3563  n = CDR(start);
3564  while (n >= 0)
3565  {
3566  if (CDR(n) < 0)
3567  break;
3568 
3569  int id = atoi(Sexp_nodes[CDR(n)].text);
3570  Assert(id < 10000000);
3571  char xstr[NAME_LENGTH + 20];
3572  sprintf(xstr, "XSTR(\"%s\", %d)", Sexp_nodes[n].text, id);
3573 
3574  memset(Sexp_nodes[n].text, 0, NAME_LENGTH*sizeof(char));
3575  lcl_ext_localize(xstr, Sexp_nodes[n].text, NAME_LENGTH - 1);
3576 
3577  n = CDDR(n);
3578  }
3579  break;
3580  }
3581  }
3582 
3583  return start;
3584 }
3585 
3586 
3591 {
3592  int count;
3593  char var_name[TOKEN_LENGTH];
3594  char default_value[TOKEN_LENGTH];
3595  char str_type[TOKEN_LENGTH];
3596  char persistent[TOKEN_LENGTH];
3597  char network[TOKEN_LENGTH];
3598  int index;
3599  int type;
3600 
3601  count = 0;
3602  required_string("$Variables:");
3604 
3605  // check for start of list
3606  if (*Mp != '(') {
3607  error_display(1, "Reading sexp variable list. Found [%c]. Expecting '('.\n", *Mp);
3608  throw parse::ParseException("Syntax error");
3609  }
3610 
3611  Mp++;
3613 
3614  while (*Mp != ')') {
3615  Assert(count < MAX_SEXP_VARIABLES);
3616 
3617  // get index - for debug
3618  stuff_int(&index);
3620 
3621  // get var_name
3622  get_string(var_name);
3624 
3625  // get default_value;
3626  get_string(default_value);
3628 
3629  // get type
3630  get_string(str_type);
3632 
3633  // determine type
3634  if (!stricmp(str_type, "number")) {
3635  type = SEXP_VARIABLE_NUMBER;
3636  } else if (!stricmp(str_type, "string")) {
3637  type = SEXP_VARIABLE_STRING;
3638  } else if (!stricmp(str_type, "block")) {
3639  // Goober5000 - This looks dangerous... these flags are needed for certain things, but it
3640  // looks like BLOCK_*_SIZE is the only thing that keeps a block from running off the end
3641  // of its boundary.
3642  type = SEXP_VARIABLE_BLOCK;
3643  } else {
3644  type = SEXP_VARIABLE_UNKNOWN;
3645  Error(LOCATION, "SEXP variable '%s' is an unknown type!", var_name);
3646  }
3647 
3648  // possibly get network-variable
3649  if (check_for_string("\"network-variable\"")) {
3650  // eat it
3651  get_string(network);
3653 
3654  // set type
3655  type |= SEXP_VARIABLE_NETWORK;
3656  }
3657 
3658  // possibly get player-persistent
3659  if (check_for_string("\"player-persistent\"")) {
3660  // eat it
3661  get_string(persistent);
3663 
3664  // set type
3666  // possibly get campaign-persistent
3667  } else if (check_for_string("\"campaign-persistent\"")) {
3668  // eat it
3669  get_string(persistent);
3671 
3672  // set type
3674  // trap error
3675  } else if (check_for_string("\"")) {
3676  // eat garbage
3677  get_string(persistent);
3679 
3680  // notify of error
3681  Error(LOCATION, "Error parsing sexp variables - unknown persistence type encountered. You can continue from here without trouble.");
3682  }
3683 
3684  // check if variable name already exists
3685  if ( (type & SEXP_VARIABLE_NUMBER) || (type & SEXP_VARIABLE_STRING) ) {
3686  Assert(get_index_sexp_variable_name(var_name) == -1);
3687  }
3688 
3689  if ( type & SEXP_VARIABLE_BLOCK ) {
3690  add_block_variable(default_value, var_name, type, index);
3691  }
3692  else {
3693  count++;
3694  sexp_add_variable(default_value, var_name, type, index);
3695  }
3696  }
3697 
3698  Mp++;
3699 
3700  return count;
3701 }
3702 
3704 {
3705  int current_index;
3706 
3707  for ( current_index = (MAX_SEXP_VARIABLES - BLOCK_EXP_SIZE) ; current_index >= (MAX_SEXP_VARIABLES - (BLOCK_EXP_SIZE * Num_special_expl_blocks)) ; current_index = (current_index - BLOCK_EXP_SIZE) ) {
3708  if (
3709  (atoi(Block_variables[current_index+INNER_RAD].text) == shipp->special_exp_inner) &&
3710  (atoi(Block_variables[current_index+OUTER_RAD].text) == shipp->special_exp_outer) &&
3711  (atoi(Block_variables[current_index+DAMAGE].text) == shipp->special_exp_damage) &&
3712  (atoi(Block_variables[current_index+BLAST].text) == shipp->special_exp_blast) &&
3713  (atoi(Block_variables[current_index+PROPAGATE].text) == (shipp->use_shockwave ? 1:0) ) &&
3714  (atoi(Block_variables[current_index+SHOCK_SPEED].text) == shipp->special_exp_shockwave_speed)
3715  ) {
3716  *index = current_index;
3717  return true;
3718  }
3719  }
3720 
3721  // if we got here, this ship's special explosions aren't represented in the Block_variables array
3722  *index = current_index;
3723  return false;
3724 }
3725 
3727 {
3728  ship *shipp;
3729  ship_obj *sop;
3730  int current_index;
3731  bool already_added = false;
3732  int num_sexp_variables;
3733  int i;
3734 
3735  // since we're (re)generating the block variable index, we don't start off with any block variables
3736  Num_special_expl_blocks = 0;
3737 
3738  // get the number of sexp_variables we currently have. We must not try to add a block variable with an index below this.
3739  num_sexp_variables = sexp_variable_count();
3740 
3741  for ( sop = GET_FIRST(&Ship_obj_list); sop != END_OF_LIST(&Ship_obj_list); sop = GET_NEXT(sop) ) {
3742  shipp=&Ships[Objects[sop->objnum].instance];
3743 
3744  if (!(shipp->use_special_explosion)) {
3745  continue;
3746  }
3747 
3748  already_added = has_special_explosion_block_index(shipp, &current_index);
3749 
3750  // if we can't add an index for this add this ship to the list of those which failed
3751  if (current_index < num_sexp_variables) {
3752  // fail list code goes here
3753  continue;
3754  }
3755 
3756  //if we haven't added this entry already, do so
3757  if (!already_added) {
3758  sprintf(Block_variables[current_index+INNER_RAD].text, "%d", shipp->special_exp_inner);
3759  sprintf(Block_variables[current_index+OUTER_RAD].text, "%d", shipp->special_exp_outer);
3760  sprintf(Block_variables[current_index+DAMAGE].text, "%d", shipp->special_exp_damage);
3761  sprintf(Block_variables[current_index+BLAST].text, "%d", shipp->special_exp_blast);
3762  sprintf(Block_variables[current_index+PROPAGATE].text, "%d", (shipp->use_shockwave ? 1:0) );
3763  sprintf(Block_variables[current_index+SHOCK_SPEED].text, "%d", shipp->special_exp_shockwave_speed);
3764 
3765  // add the names
3766  for (i = current_index; i < (current_index + BLOCK_EXP_SIZE); i++ ) {
3767  sprintf(Block_variables[i].variable_name, "%s", shipp->ship_name);
3768  }
3769 
3770  Num_special_expl_blocks++;
3771  }
3772  }
3773 
3774  return true;
3775 }
3776 
3778 {
3779  return Num_special_expl_blocks * BLOCK_EXP_SIZE;
3780 }
3781 
3785 void stuff_sexp_text_string(SCP_string &dest, int node, int mode)
3786 {
3787  Assert( (node >= 0) && (node < Num_sexp_nodes) );
3788 
3789  if (Sexp_nodes[node].type & SEXP_FLAG_VARIABLE) {
3790 
3791  int sexp_variables_index = get_index_sexp_variable_name(Sexp_nodes[node].text);
3792  Assertion(sexp_variables_index != -1, "Couldn't find variable: %s\n", Sexp_nodes[node].text);
3793  Assert( (Sexp_variables[sexp_variables_index].type & SEXP_VARIABLE_NUMBER) || (Sexp_variables[sexp_variables_index].type & SEXP_VARIABLE_STRING) );
3794 
3795  // number
3796  if (Sexp_nodes[node].subtype == SEXP_ATOM_NUMBER) {
3797  Assert(Sexp_variables[sexp_variables_index].type & SEXP_VARIABLE_NUMBER);
3798 
3799  // Error check - can be Fred or FreeSpace
3800  if (mode == SEXP_ERROR_CHECK_MODE) {
3801  if ( Fred_running ) {
3802  sprintf(dest, "%s[%s] ", Sexp_nodes[node].text, Sexp_variables[sexp_variables_index].text);
3803  } else {
3804  sprintf(dest, "%s[%s] ", Sexp_variables[sexp_variables_index].variable_name, Sexp_variables[sexp_variables_index].text);
3805  }
3806  } else {
3807  // Save as string - only Fred
3808  Assert(mode == SEXP_SAVE_MODE);
3809  sprintf(dest, "@%s[%s] ", Sexp_nodes[node].text, Sexp_variables[sexp_variables_index].text);
3810  }
3811  } else {
3812  // string
3813  Assert(Sexp_nodes[node].subtype == SEXP_ATOM_STRING);
3814  Assert(Sexp_variables[sexp_variables_index].type & SEXP_VARIABLE_STRING);
3815 
3816  // Error check - can be Fred or FreeSpace
3817  if (mode == SEXP_ERROR_CHECK_MODE) {
3818  if ( Fred_running ) {
3819  sprintf(dest, "%s[%s] ", Sexp_variables[sexp_variables_index].variable_name, Sexp_variables[sexp_variables_index].text);
3820  } else {
3821  sprintf(dest, "%s[%s] ", Sexp_nodes[node].text, Sexp_variables[sexp_variables_index].text);
3822  }
3823  } else {
3824  // Save as string - only Fred
3825  Assert(mode == SEXP_SAVE_MODE);
3826  sprintf(dest, "\"@%s[%s]\" ", Sexp_nodes[node].text, Sexp_variables[sexp_variables_index].text);
3827  }
3828  }
3829  } else {
3830  // not a variable
3831  if (Sexp_nodes[node].subtype == SEXP_ATOM_STRING) {
3832  sprintf(dest, "\"%s\" ", CTEXT(node));
3833  } else {
3834  sprintf(dest, "%s ", CTEXT(node));
3835  }
3836  }
3837 }
3838 
3839 int build_sexp_string(SCP_string &accumulator, int cur_node, int level, int mode)
3840 {
3841  SCP_string buf;
3842  int node, old_length = accumulator.length();
3843 
3844  accumulator += "( ";
3845  node = cur_node;
3846  while (node != -1) {
3847  Assert(node >= 0 && node < Num_sexp_nodes);
3848  if (Sexp_nodes[node].first == -1) {
3849  // build text to string
3850  stuff_sexp_text_string(buf, node, mode);
3851  accumulator += buf;
3852 
3853  } else {
3854  build_sexp_string(accumulator, Sexp_nodes[node].first, level + 1, mode);
3855  }
3856 
3857  node = Sexp_nodes[node].rest;
3858  }
3859 
3860  accumulator += ") ";
3861  if ((accumulator.length() - old_length) > 40) {
3862  accumulator.resize(old_length);
3863  build_extended_sexp_string(accumulator, cur_node, level, mode);
3864  return 1;
3865  }
3866 
3867  return 0;
3868 }
3869 
3870 void build_extended_sexp_string(SCP_string &accumulator, int cur_node, int level, int mode)
3871 {
3872  SCP_string buf;
3873  int i, flag = 0, node;
3874 
3875  accumulator += "( ";
3876  node = cur_node;
3877  while (node != -1) {
3878  // not the first line?
3879  if (flag) {
3880  for (i=0; i<level + 1; i++)
3881  accumulator += " ";
3882  }
3883 
3884  flag = 1;
3885  Assert(node >= 0 && node < Num_sexp_nodes);
3886  if (Sexp_nodes[node].first == -1) {
3887  stuff_sexp_text_string(buf, node, mode);
3888  accumulator += buf;
3889 
3890  } else {
3891  build_sexp_string(accumulator, Sexp_nodes[node].first, level + 1, mode);
3892  }
3893 
3894  accumulator += "\n";
3895  node = Sexp_nodes[node].rest;
3896  }
3897 
3898  for (i=0; i<level; i++)
3899  accumulator += " ";
3900 
3901  accumulator += ")";
3902 }
3903 
3904 void convert_sexp_to_string(SCP_string &dest, int cur_node, int mode)
3905 {
3906  if (cur_node >= 0) {
3907  dest = "";
3908  build_sexp_string(dest, cur_node, 0, mode);
3909  } else {
3910  dest = "( )";
3911  }
3912 }
3913 
3914 
3915 // -----------------------------------------------------------------------------------
3916 // Helper methods for getting data from nodes. Cause it's stupid to keep re-rolling this stuff for every single SEXP
3917 // -----------------------------------------------------------------------------------
3918 
3922 player * get_player_from_ship_node(int node, bool test_respawns)
3923 {
3924  int sindex, np_index = -1;
3925  p_object *p_objp;
3926 
3927  Assert (node != -1);
3928 
3929  sindex = ship_name_lookup(CTEXT(node));
3930 
3931  // singleplayer
3932  if (!(Game_mode & GM_MULTIPLAYER)){
3933  if(sindex >= 0){
3934  if(Player_obj == &Objects[Ships[sindex].objnum]){
3935  return Player;
3936  }
3937  }
3938  return NULL;
3939  }
3940  // multiplayer
3941  else {
3942  if(sindex >= 0){
3943  if(Ships[sindex].objnum >= 0) {
3944  // try and find the player
3945  np_index = multi_find_player_by_object(&Objects[Ships[sindex].objnum]);
3946  }
3947  }
3948  if (test_respawns && np_index < 0) {
3949  // Respawning ships don't have an objnum so we need to take a different approach
3950  p_objp = mission_parse_get_arrival_ship(CTEXT(node));
3951  if (p_objp != NULL) {
3952  np_index = multi_find_player_by_parse_object(p_objp);
3953  }
3954  }
3955 
3956  if((np_index >= 0) && (np_index < MAX_PLAYERS)){
3957  return Net_players[np_index].m_player;
3958  }
3959 
3960  return NULL;
3961  }
3962 }
3963 
3968 {
3969  int sindex;
3970  ship *shipp = NULL;
3971 
3972  sindex = ship_name_lookup( CTEXT(node) );
3973 
3974  if (sindex < 0) {
3975  return shipp;
3976  }
3977 
3978  if (Ships[sindex].objnum < 0) {
3979  return shipp;
3980  }
3981 
3982  shipp = &Ships[sindex];
3983  return shipp;
3984 }
3985 
3990 {
3991  int i;
3992 
3993  if (ship_query_state(name) < 0)
3994  return 1;
3995 
3996  i = wing_name_lookup(name, 1);
3997 
3998  // has not arrived yet
3999  if ((i >= 0) && (Wings[i].num_waves >= 0) && !Wings[i].total_arrived_count){
4000  return 1;
4001  }
4002 
4003  return 0;
4004 }
4005 
4006 // arithmetic functions
4007 int add_sexps(int n)
4008 {
4009  int sum = 0, val;
4010 
4011  if (n != -1) {
4012  if ( CAR(n) != -1) {
4013  sum = eval_sexp( CAR(n) );
4014  // be sure to check for the NAN value when doing arithmetic -- this value should
4015  // get propagated to the next highest function.
4016  if ( Sexp_nodes[CAR(n)].value == SEXP_NAN )
4017  return SEXP_NAN;
4018  else if ( Sexp_nodes[CAR(n)].value == SEXP_NAN_FOREVER )
4019  return SEXP_NAN_FOREVER;
4020  }
4021  else
4022  sum = atoi( CTEXT(n) );
4023 
4024  while (CDR(n) != -1) {
4025  val = eval_sexp( CDR(n) );
4026  // be sure to check for the NAN value when doing arithmetic -- this value should
4027  // get propagated to the next highest function.
4028  if ( Sexp_nodes[CDR(n)].value == SEXP_NAN )
4029  return SEXP_NAN;
4030  else if ( Sexp_nodes[CDR(n)].value == SEXP_NAN_FOREVER )
4031  return SEXP_NAN_FOREVER;
4032  sum += val;
4033  n = CDR(n);
4034  }
4035  }
4036 
4037  return sum;
4038 }
4039 
4040 int sub_sexps(int n)
4041 {
4042  int sum = 0;
4043 
4044  if (n != -1) {
4045  if (Sexp_nodes[n].first != -1)
4046  sum = eval_sexp(CAR(n));
4047  else
4048  sum = atoi(CTEXT(n));
4049 
4050  while (CDR(n) != -1) {
4051  sum -= eval_sexp(CDR(n));
4052  n = CDR(n);
4053  }
4054  }
4055 
4056  return sum;
4057 }
4058 
4059 int mul_sexps(int n)
4060 {
4061  int sum = 0;
4062 
4063  if (n != -1) {
4064  if (Sexp_nodes[n].first != -1)
4065  sum = eval_sexp(Sexp_nodes[n].first);
4066  else
4067  sum = atoi(CTEXT(n));
4068 
4069  while (Sexp_nodes[n].rest != -1) {
4070  sum *= eval_sexp(Sexp_nodes[n].rest);
4071  n = Sexp_nodes[n].rest;
4072  }
4073  }
4074 
4075  return sum;
4076 }
4077 
4078 int div_sexps(int n)
4079 {
4080  int sum = 0;
4081 
4082  if (n != -1) {
4083  if (Sexp_nodes[n].first != -1)
4084  sum = eval_sexp(Sexp_nodes[n].first);
4085  else
4086  sum = atoi(CTEXT(n));
4087 
4088  while (Sexp_nodes[n].rest != -1) {
4089  int div = eval_sexp(Sexp_nodes[n].rest);
4090  n = Sexp_nodes[n].rest;
4091  if (div == 0) {
4092  Warning(LOCATION, "Division by zero in sexp. Please check all uses of the / operator for possible causes.\n");
4093  Int3();
4094  continue;
4095  }
4096  sum /= div;
4097  }
4098  }
4099 
4100  return sum;
4101 }
4102 
4103 int mod_sexps(int n)
4104 {
4105  int sum = 0;
4106 
4107  if (n != -1) {
4108  if (Sexp_nodes[n].first != -1)
4109  sum = eval_sexp(Sexp_nodes[n].first);
4110  else
4111  sum = atoi(CTEXT(n));
4112 
4113  while (Sexp_nodes[n].rest != -1) {
4114  sum = sum % eval_sexp(Sexp_nodes[n].rest);
4115  n = Sexp_nodes[n].rest;
4116  }
4117  }
4118 
4119  return sum;
4120 }
4121 
4122 int rand_internal(int low, int high, int seed = 0)
4123 {
4124  int diff;
4125 
4126  // maybe seed it
4127  if (seed > 0)
4128  srand(seed);
4129 
4130  // get diff - don't allow negative or zero
4131  diff = high - low;
4132  if (diff < 0)
4133  diff = 0;
4134 
4135  return (low + rand32() % (diff + 1));
4136 }
4137 
4138 // Goober5000
4139 int abs_sexp(int n)
4140 {
4141  return abs(eval_num(n));
4142 }
4143 
4144 // Goober5000
4145 int min_sexp(int n)
4146 {
4147  int temp, min_val = INT_MAX;
4148 
4149  while (n != -1)
4150  {
4151  temp = eval_num(n);
4152 
4153  if (temp < min_val)
4154  min_val = temp;
4155 
4156  n = CDR(n);
4157  }
4158 
4159  return min_val;
4160 }
4161 
4162 // Goober5000
4163 int max_sexp(int n)
4164 {
4165  int temp, max_val = INT_MIN;
4166 
4167  while (n != -1)
4168  {
4169  temp = eval_num(n);
4170 
4171  if (temp > max_val)
4172  max_val = temp;
4173 
4174  n = CDR(n);
4175  }
4176 
4177  return max_val;
4178 }
4179 
4180 // Goober5000
4181 int avg_sexp(int n)
4182 {
4183  int num = 0, avg_val = 0;
4184 
4185  while (n != -1)
4186  {
4187  avg_val += eval_num(n);
4188  num++;
4189 
4190  n = CDR(n);
4191  }
4192 
4193  return (int) floor(((double) avg_val / num) + 0.5);
4194 }
4195 
4196 // Goober5000
4197 int pow_sexp(int node)
4198 {
4199  int num_1 = eval_num(node);
4200  int num_2 = eval_num(CDR(node));
4201 
4202  // this is disallowed in FRED, but can still happen through careless arithmetic
4203  if (num_2 < 0)
4204  {
4205  Warning(LOCATION, "Power function pow(%d, %d) attempted to raise to a negative power!", num_1, num_2);
4206  return 0;
4207  }
4208 
4209  double pow_result = pow(static_cast<double>(num_1), num_2);
4210 
4211  if (pow_result > static_cast<double>(INT_MAX))
4212  {
4213  nprintf(("SEXP", "Power function pow(%d, %d) is greater than INT_MAX! Returning INT_MAX.", num_1, num_2));
4214  return INT_MAX;
4215  }
4216  else if (pow_result < static_cast<double>(INT_MIN))
4217  {
4218  nprintf(("SEXP", "Power function pow(%d, %d) is less than INT_MIN! Returning INT_MIN.", num_1, num_2));
4219  return INT_MIN;
4220  }
4221 
4222  return static_cast<int>(pow_result);
4223 }
4224 
4225 // Goober5000
4226 int signum_sexp(int node)
4227 {
4228  int num = eval_num(node);
4229 
4230  if (num == 0)
4231  return 0;
4232 
4233  if (num < 0)
4234  return -1;
4235 
4236  // hurr durr math
4237  Assert(num > 0);
4238  return 1;
4239 }
4240 
4241 // Goober5000
4242 int sexp_set_bit(int node, bool set_it)
4243 {
4244  int val = eval_num(node);
4245  int bit_index = eval_num(CDR(node));
4246 
4247  if (bit_index < 0 || bit_index > 31)
4248  {
4249  Warning(LOCATION, "Bit index %d out of range! Must be between 0 and 31.", bit_index);
4250  return SEXP_NAN;
4251  }
4252 
4253  if (set_it)
4254  return val | (1<<bit_index);
4255  else
4256  return val & ~(1<<bit_index);
4257 }
4258 
4259 // Goober5000
4260 int sexp_is_bit_set(int node)
4261 {
4262  int val = eval_num(node);
4263  int bit_index = eval_num(CDR(node));
4264 
4265  if (bit_index < 0 || bit_index > 31)
4266  {
4267  Warning(LOCATION, "Bit index %d out of range! Must be between 0 and 31.", bit_index);
4268  return SEXP_NAN;
4269  }
4270 
4271  if (val & (1<<bit_index))
4272  return SEXP_TRUE;
4273  else
4274  return SEXP_FALSE;
4275 }
4276 
4277 // Goober5000
4278 int sexp_bitwise_and(int node)
4279 {
4280  int val = eval_num(node);
4281 
4282  for (int n = CDR(node); n != -1; n = CDR(n))
4283  val &= eval_num(n);
4284 
4285  return val;
4286 }
4287 
4288 // Goober5000
4289 int sexp_bitwise_or(int node)
4290 {
4291  int val = eval_num(node);
4292 
4293  for (int n = CDR(node); n != -1; n = CDR(n))
4294  val |= eval_num(n);
4295 
4296  return val;
4297 }
4298 
4299 // Goober5000
4300 int sexp_bitwise_not(int node)
4301 {
4302  int result = ~(eval_num(node));
4303 
4304  // clear the sign bit
4305  return result & INT_MAX;
4306 }
4307 
4308 // Goober5000
4309 int sexp_bitwise_xor(int node)
4310 {
4311  return eval_num(node) ^ eval_num(CDR(node));
4312 }
4313 
4314 // seeding added by Karajorma and Goober5000
4315 int rand_sexp(int n, bool multiple)
4316 {
4317  int low, high, rand_num, seed;
4318 
4319  if (n < 0)
4320  {
4321  Int3();
4322  return 0;
4323  }
4324 
4325  // when getting a saved value
4326  if (Sexp_nodes[n].value == SEXP_NUM_EVAL)
4327  {
4328  // don't regenerate new random number
4329  return atoi(CTEXT(n));
4330  }
4331 
4332  low = eval_num(n);
4333 
4334  // get high
4335  high = eval_num(CDR(n));
4336 
4337  // is there a seed provided?
4338  if (CDDR(n) != -1)
4339  seed = eval_num(CDDR(n));
4340  else
4341  seed = 0;
4342 
4343  // get the random number
4344  rand_num = rand_internal(low, high, seed);
4345 
4346  // when saving the value
4347  if (!multiple)
4348  {
4349  // set .value and .text so random number is generated only once.
4350  Sexp_nodes[n].value = SEXP_NUM_EVAL;
4351  sprintf(Sexp_nodes[n].text, "%d", rand_num);
4352  }
4353  // if this is multiple with a nonzero seed provided
4354  else if (seed > 0)
4355  {
4356  // Set the seed to a new seeded random value. This will ensure that the next time the method
4357  // is called it will return a predictable but different number from the previous time.
4358  sprintf(Sexp_nodes[CDDR(n)].text, "%d", rand_internal(1, INT_MAX, seed));
4359  }
4360 
4361  return rand_num;
4362 }
4363 
4364 // boolean evaluation functions. Evaluate all sexpressions in the 'or' operator. Needed to mark
4365 // entries in the mission log as essential so when pruning the log, we know which entries we might
4366 // need to keep.
4367 int sexp_or(int n)
4368 {
4369  int all_false, result;
4370 
4371  all_false = 1;
4372  result = 0;
4373  if (n != -1)
4374  {
4375  if (CAR(n) != -1)
4376  {
4377  result |= is_sexp_true(CAR(n));
4378  if ( Sexp_nodes[CAR(n)].value == SEXP_KNOWN_TRUE )
4379  return SEXP_KNOWN_TRUE; // if one of the OR clauses is TRUE, whole clause is true
4380  if ( Sexp_nodes[CAR(n)].value != SEXP_KNOWN_FALSE ) // if the value is still unknown, they all can't be false
4381  all_false = 0;
4382  }
4383  else
4384  result |= atoi(CTEXT(n));
4385 
4386  while (CDR(n) != -1)
4387  {
4388  result |= is_sexp_true(CDR(n));
4389  if ( Sexp_nodes[CDR(n)].value == SEXP_KNOWN_TRUE )
4390  return SEXP_KNOWN_TRUE; // if one of the OR clauses is TRUE, whole clause is true
4391  if ( Sexp_nodes[CDR(n)].value != SEXP_KNOWN_FALSE ) // if the value is still unknown, they all can't be false
4392  all_false = 0;
4393 
4394  n = CDR(n);
4395  }
4396  }
4397 
4398  if (all_false)
4399  return SEXP_KNOWN_FALSE;
4400 
4401  return result;
4402 }
4403 
4404 // this function does the 'and' operator. It will short circuit evaluation *but* it will still
4405 // evaluate other members of the and construct. I do this because I need events in the mission log
4406 // to get marked as essential for goal purposes, and evaluation is pretty much the only way
4407 int sexp_and(int n)
4408 {
4409  int all_true, result;
4410 
4411  result = -1;
4412  all_true = 1;
4413  if (n != -1)
4414  {
4415  if (CAR(n) != -1)
4416  {
4417  result &= is_sexp_true(CAR(n));
4418  if ( Sexp_nodes[CAR(n)].value == SEXP_KNOWN_FALSE || Sexp_nodes[CAR(n)].value == SEXP_NAN_FOREVER )
4419  return SEXP_KNOWN_FALSE; // if one of the AND clauses is FALSE, whole clause is false
4420  if ( Sexp_nodes[CAR(n)].value != SEXP_KNOWN_TRUE ) // if the value is still unknown, they all can't be true
4421  all_true = 0;
4422  }
4423  else
4424  result &= atoi(CTEXT(n));
4425 
4426  while (CDR(n) != -1)
4427  {
4428  int new_result;
4429 
4430  new_result = is_sexp_true(CDR(n));
4431  result &= new_result;
4432  if ( Sexp_nodes[CDR(n)].value == SEXP_KNOWN_FALSE || Sexp_nodes[CDR(n)].value == SEXP_NAN_FOREVER )
4433  return SEXP_KNOWN_FALSE; // if one of the AND clauses is FALSE, whole clause is false
4434  if ( Sexp_nodes[CDR(n)].value != SEXP_KNOWN_TRUE ) // if the value is still unknown, they all can't be true
4435  all_true = 0;
4436 
4437  n = CDR(n);
4438  }
4439  }
4440 
4441  if (all_true)
4442  return SEXP_KNOWN_TRUE;
4443 
4444  return result;
4445 }
4446 
4447 // this version of the 'and' operator determines whether or not it's arguments become true
4448 // in the order in which they are specified in the when statement. Should be a simple matter of
4449 // seeing if anything evaluates to true later than something that evalueated to false
4451 {
4452  int result = -1;
4453  int all_true;
4454 
4455  all_true = 1; // represents whether or not all nodes we have seen so far are true
4456  if (n != -1)
4457  {
4458  if (CAR(n) != -1)
4459  {
4460  result &= is_sexp_true(CAR(n));
4461  if ( Sexp_nodes[CAR(n)].value == SEXP_KNOWN_FALSE || Sexp_nodes[CAR(n)].value == SEXP_NAN_FOREVER )
4462  return SEXP_KNOWN_FALSE; // if one of the AND clauses is FALSE, whole clause is false
4463  if ( Sexp_nodes[CAR(n)].value != SEXP_KNOWN_TRUE ) // if value is true, mark our all_true variable for later checking
4464  all_true = 0;
4465  }
4466  else
4467  result &= atoi(CTEXT(n));
4468 
4469  // a little test -- if the previous sexpressions was true, then mark the node itself as always
4470  // true. I did this because of the distance function. It might become true, then when waiting for
4471  // the second evalation, it might become false, rendering this function false. So, when one becomes
4472  // true -- mark it true forever.
4473  if ( result )
4474  Sexp_nodes[CAR(n)].value = SEXP_KNOWN_TRUE;
4475 
4476  while (CDR(n) != -1)
4477  {
4478  int next_result;
4479 
4480  next_result = is_sexp_true(CDR(n));
4481  if ( next_result && !result ) // if current result is true, and our running result is false, thngs didn't become true in order
4482  return SEXP_KNOWN_FALSE;
4483  result &= next_result;
4484  if ( Sexp_nodes[CDR(n)].value == SEXP_KNOWN_FALSE || Sexp_nodes[CDR(n)].value == SEXP_NAN_FOREVER )
4485  return SEXP_KNOWN_FALSE; // if one of the OR clauses is TRUE, whole clause is true
4486  if ( Sexp_nodes[CDR(n)].value != SEXP_KNOWN_TRUE ) // if the value is still unknown, they all can't be false
4487  all_true = 0;
4488 
4489  // see comment above for explanation of next lines
4490  if ( result )
4491  Sexp_nodes[CDR(n)].value = SEXP_KNOWN_TRUE;
4492 
4493  n = CDR(n);
4494  }
4495  }
4496 
4497  if ( all_true )
4498  return SEXP_KNOWN_TRUE;
4499 
4500  return result;
4501 }
4502 
4503 // for these four basic boolean operations (not, <, >, and =), we have special cases that we must deal
4504 // with. We have sexpressions operators that might return a NAN type return value (such as the distance
4505 // between two ships when one of the ships is destroyed or departed). These operations need to check for
4506 // this special NAN value and adjust their return types accordingly. NAN values represent false return values
4507 int sexp_not(int n)
4508 {
4509  int result = 0;
4510 
4511  if (n != -1)
4512  {
4513  if (CAR(n) != -1)
4514  {
4515  result = is_sexp_true(CAR(n));
4516  if ( Sexp_nodes[CAR(n)].value == SEXP_KNOWN_FALSE || Sexp_nodes[CAR(n)].value == SEXP_NAN_FOREVER )
4517  return SEXP_KNOWN_TRUE; // not KNOWN_FALSE == KNOWN_TRUE;
4518  else if ( Sexp_nodes[CAR(n)].value == SEXP_KNOWN_TRUE ) // not KNOWN_TRUE == KNOWN_FALSE
4519  return SEXP_KNOWN_FALSE;
4520  else if ( Sexp_nodes[CAR(n)].value == SEXP_NAN ) // not NAN == TRUE (I think)
4521  return SEXP_TRUE;
4522  }
4523  else
4524  result = atoi(CTEXT(n));
4525  }
4526 
4527  return !result;
4528 }
4529 
4530 int sexp_xor(int node)
4531 {
4532  int num_true = 0;
4533 
4534  for (int n = node; n != -1; n = CDR(n))
4535  {
4536  if (is_sexp_true(n))
4537  num_true++;
4538  }
4539 
4540  return (num_true == 1);
4541 }
4542 
4543 // Goober5000
4544 int sexp_number_compare(int n, int op)
4545 {
4546  int first_node = n;
4547  int current_node;
4548  int first_number = eval_sexp(first_node);
4549  int current_number;
4550 
4551  // bail on NANs
4552  if (CAR(first_node) != -1)
4553  {
4554  if (Sexp_nodes[CAR(first_node)].value == SEXP_NAN) return SEXP_FALSE;
4555  if (Sexp_nodes[CAR(first_node)].value == SEXP_NAN_FOREVER) return SEXP_KNOWN_FALSE;
4556  }
4557  if (CDR(first_node) != -1)
4558  {
4559  if (Sexp_nodes[CDR(first_node)].value == SEXP_NAN) return SEXP_FALSE;
4560  if (Sexp_nodes[CDR(first_node)].value == SEXP_NAN_FOREVER) return SEXP_KNOWN_FALSE;
4561  }
4562 
4563  // compare first node with each of the others
4564  for (current_node = CDR(first_node); current_node != -1; current_node = CDR(current_node))
4565  {
4566  // bail on NANs
4567  if (CAR(current_node) != -1)
4568  {
4569  if (Sexp_nodes[CAR(current_node)].value == SEXP_NAN) return SEXP_FALSE;
4570  if (Sexp_nodes[CAR(current_node)].value == SEXP_NAN_FOREVER) return SEXP_KNOWN_FALSE;
4571  }
4572  if (CDR(current_node) != -1)
4573  {
4574  if (Sexp_nodes[CDR(current_node)].value == SEXP_NAN) return SEXP_FALSE;
4575  if (Sexp_nodes[CDR(current_node)].value == SEXP_NAN_FOREVER) return SEXP_KNOWN_FALSE;
4576  }
4577 
4578  current_number = eval_sexp(current_node);
4579 
4580  // must satisfy our particular operator
4581  switch(op)
4582  {
4583  case OP_EQUALS:
4584  if (first_number != current_number) return SEXP_FALSE;
4585  break;
4586 
4587  case OP_NOT_EQUAL:
4588  if (first_number == current_number) return SEXP_FALSE;
4589  break;
4590 
4591  case OP_GREATER_THAN:
4592  if (first_number <= current_number) return SEXP_FALSE;
4593  break;
4594 
4595  case OP_GREATER_OR_EQUAL:
4596  if (first_number < current_number) return SEXP_FALSE;
4597  break;
4598 
4599  case OP_LESS_THAN:
4600  if (first_number >= current_number) return SEXP_FALSE;
4601  break;
4602 
4603  case OP_LESS_OR_EQUAL:
4604  if (first_number > current_number) return SEXP_FALSE;
4605  break;
4606 
4607  default:
4608  Warning(LOCATION, "Unhandled comparison case! Operator = %d", op);
4609  break;
4610  }
4611  }
4612 
4613  // it satisfies the operator for all the arguments
4614  return SEXP_TRUE;
4615 }
4616 
4617 // Goober5000
4618 int sexp_string_compare(int n, int op)
4619 {
4620  int first_node = n;
4621  int current_node;
4622  int val;
4623  char *first_string = CTEXT(first_node);
4624 
4625  // compare first node with each of the others
4626  for (current_node = CDR(first_node); current_node != -1; current_node = CDR(current_node))
4627  {
4628  val = strcmp(first_string, CTEXT(current_node));
4629 
4630  // must satisfy our particular operator
4631  switch(op)
4632  {
4633  case OP_STRING_EQUALS:
4634  if (val != 0) return SEXP_FALSE;
4635  break;
4636 
4638  if (val <= 0) return SEXP_FALSE;
4639  break;
4640 
4641  case OP_STRING_LESS_THAN:
4642  if (val >= 0) return SEXP_FALSE;
4643  break;
4644  }
4645  }
4646 
4647  // it satisfies the operator for all the arguments
4648  return SEXP_TRUE;
4649 }
4650 
4651 #define OSWPT_TYPE_NONE 0
4652 #define OSWPT_TYPE_SHIP 1
4653 #define OSWPT_TYPE_WING 2
4654 #define OSWPT_TYPE_WAYPOINT 3
4655 #define OSWPT_TYPE_SHIP_ON_TEAM 4 // e.g. <any friendly>
4656 #define OSWPT_TYPE_WHOLE_TEAM 5 // e.g. Friendly
4657 #define OSWPT_TYPE_PARSE_OBJECT 6 // a "ship" that hasn't arrived yet
4658 #define OSWPT_TYPE_EXITED 7
4659 #define OSWPT_TYPE_WING_NOT_PRESENT 8 // a wing that hasn't arrived yet or is between waves
4660 
4661 // Goober5000
4663 {
4665  int type;
4666 
4668  object *objp;
4672  int team;
4673 
4674  void clear();
4675 }
4677 
4679 {
4680  object_name = NULL;
4682 
4683  p_objp = NULL;
4684  objp = NULL;
4685  shipp = NULL;
4686  waypointp = NULL;
4687  wingp = NULL;
4688  team = -1;
4689 }
4690 
4691 void sexp_object_ship_wing_point_team_set_ship(object_ship_wing_point_team *oswpt, ship *shipp, bool set_parse_flag_too = false);
4692 void sexp_object_ship_wing_point_team_set_ship(object_ship_wing_point_team *oswpt, ship_obj *so, bool set_parse_flag_too = false);
4693 void sexp_get_object_ship_wing_point_team(object_ship_wing_point_team *oswpt, char *object_name, bool set_parse_flag_too = false);
4694 
4696 {