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 #ifdef _MSC_VER
24 	#include "globalincs/msvc/stdint.h"
25 #else
26 	#include <stdint.h>
27 #endif
28 
29 #include "parse/parselo.h"
30 #include "parse/sexp.h"
31 #include "ship/ship.h"
32 #include "freespace2/freespace.h"
33 #include "weapon/weapon.h"
34 #include "mission/missionlog.h"
35 #include "mission/missionparse.h"		// for p_object definition
36 #include "mission/missionmessage.h"
37 #include "mission/missiontraining.h"
38 #include "globalincs/linklist.h"
39 #include "ai/aigoals.h"
40 #include "mission/missioncampaign.h"
41 #include "mission/missiongoals.h"
42 #include "mission/missionbriefcommon.h"
43 #include "io/timer.h"
44 #include "ship/shiphit.h"
45 #include "gamesequence/gamesequence.h"
46 #include "stats/medals.h"
47 #include "playerman/player.h"
48 #include "hud/hud.h"
49 #include "hud/hudconfig.h"
50 #include "missionui/redalert.h"
51 #include "jumpnode/jumpnode.h"
52 #include "hud/hudshield.h"
53 #include "hud/hudescort.h"
54 #include "weapon/beam.h"
55 #include "starfield/supernova.h"
56 #include "hud/hudets.h"
57 #include "math/fvi.h"
58 #include "ship/awacs.h"
59 #include "hud/hudsquadmsg.h"		// for the order sexp
60 #include "gamesnd/eventmusic.h"		// for change-soundtrack
61 #include "menuui/techmenu.h"		// for intel stuff
62 #include "fireball/fireballs.h"		// for explosion stuff
63 #include "gamesnd/gamesnd.h"
64 #include "render/3d.h"
65 #include "asteroid/asteroid.h"
66 #include "weapon/shockwave.h"
67 #include "weapon/emp.h"
68 #include "sound/audiostr.h"
69 #include "sound/ds.h"
70 #include "sound/sound.h"
71 #include "cmdline/cmdline.h"
72 #include "hud/hudparse.h"
73 #include "hud/hudmessage.h"
74 #include "starfield/starfield.h"
75 #include "hud/hudartillery.h"
76 #include "object/objectdock.h"
77 #include "globalincs/systemvars.h"
78 #include "globalincs/version.h"
79 #include "camera/camera.h"
80 #include "iff_defs/iff_defs.h"
81 #include "nebula/neb.h"
82 #include "nebula/neblightning.h"
83 #include "network/multi.h"
84 #include "network/multimsgs.h"
85 #include "network/multiutil.h"
86 #include "network/multi_team.h"
87 #include "network/multi_obj.h"
88 #include "parse/lua.h"
89 #include "parse/scripting.h"
90 #include "object/objcollide.h"
91 #include "object/waypoint.h"
92 #include "graphics/2d.h"
93 #include "object/objectsnd.h"
94 #include "graphics/font.h"
95 #include "asteroid/asteroid.h"
96 #include "mod_table/mod_table.h"
97 #include "ship/afterburner.h"
98 #include "globalincs/alphacolors.h"
99 
100 #ifndef NDEBUG
101 #include "hud/hudmessage.h"
102 #endif
103 
104 #include "autopilot/autopilot.h"
105 #include "object/objectshield.h"
106 #include "network/multi_sexp.h"
107 #include "io/keycontrol.h"
108 #include "parse/generic_log.h"
109 #include "localization/localize.h"
110 #include "hud/hudets.h"
111 
112 
113 
114 #define TRUE	1
115 #define FALSE	0
116 
117 
118 sexp_oper Operators[] = {
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,					1,	1,			SEXP_INTEGER_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-free",						OP_BEAM_FREE,							2,	INT_MAX,	SEXP_ACTION_OPERATOR,	},
481 	{ "beam-free-all",					OP_BEAM_FREE_ALL,						1,	INT_MAX,	SEXP_ACTION_OPERATOR,	},
482 	{ "beam-lock",						OP_BEAM_LOCK,							2,	INT_MAX,	SEXP_ACTION_OPERATOR,	},
483 	{ "beam-lock-all",					OP_BEAM_LOCK_ALL,						1,	INT_MAX,	SEXP_ACTION_OPERATOR,	},
484 	{ "turret-free",					OP_TURRET_FREE,							2,	INT_MAX,	SEXP_ACTION_OPERATOR,	},
485 	{ "turret-free-all",				OP_TURRET_FREE_ALL,						1,	INT_MAX,	SEXP_ACTION_OPERATOR,	},
486 	{ "turret-lock",					OP_TURRET_LOCK,							2,	INT_MAX,	SEXP_ACTION_OPERATOR,	},
487 	{ "turret-lock-all",				OP_TURRET_LOCK_ALL,						1,	INT_MAX,	SEXP_ACTION_OPERATOR,	},
488 	{ "turret-tagged-only",				OP_TURRET_TAGGED_ONLY_ALL,				1,	1,			SEXP_ACTION_OPERATOR,	},
489 	{ "turret-tagged-clear",			OP_TURRET_TAGGED_CLEAR_ALL,				1,	1,			SEXP_ACTION_OPERATOR,	},
490 	{ "turret-tagged-specific",			OP_TURRET_TAGGED_SPECIFIC,				2,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	//phreak
491 	{ "turret-tagged-clear-specific",	OP_TURRET_TAGGED_CLEAR_SPECIFIC,		2,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	//phreak
492 	{ "turret-change-weapon",			OP_TURRET_CHANGE_WEAPON,				5,	5,			SEXP_ACTION_OPERATOR,	},	//WMC
493 	{ "turret-set-direction-preference",OP_TURRET_SET_DIRECTION_PREFERENCE,		3,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	//FUBAR
494 	{ "turret-set-rate-of-fire",		OP_TURRET_SET_RATE_OF_FIRE,				3,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	//FUBAR
495 	{ "turret-set-optimum-range",		OP_TURRET_SET_OPTIMUM_RANGE,			3,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	//FUBAR
496 	{ "turret-set-target-priorities",	OP_TURRET_SET_TARGET_PRIORITIES,		3,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	//FUBAR
497 	{ "turret-set-target-order",		OP_TURRET_SET_TARGET_ORDER,				2,	2+NUM_TURRET_ORDER_TYPES,	SEXP_ACTION_OPERATOR,	},	//WMC
498 	{ "ship-turret-target-order",		OP_SHIP_TURRET_TARGET_ORDER,			1,	1+NUM_TURRET_ORDER_TYPES,	SEXP_ACTION_OPERATOR,	},	//WMC
499 	{ "turret-subsys-target-disable",	OP_TURRET_SUBSYS_TARGET_DISABLE,		2,	INT_MAX,	SEXP_ACTION_OPERATOR,	},
500 	{ "turret-subsys-target-enable",	OP_TURRET_SUBSYS_TARGET_ENABLE,			2,	INT_MAX,	SEXP_ACTION_OPERATOR,	},
501 
502 	//Models and Textures Sub-Category
503 	{ "change-ship-class",				OP_CHANGE_SHIP_CLASS,					2,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	// Goober5000
504 	{ "deactivate-glow-maps",			OP_DEACTIVATE_GLOW_MAPS,				1,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	//-Bobboau
505 	{ "activate-glow-maps",				OP_ACTIVATE_GLOW_MAPS,					1,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	//-Bobboau
506 	{ "deactivate-glow-points",			OP_DEACTIVATE_GLOW_POINTS,				1,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	//-Bobboau
507 	{ "activate-glow-points",			OP_ACTIVATE_GLOW_POINTS,				1,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	//-Bobboau
508 	{ "deactivate-glow-point-bank",		OP_DEACTIVATE_GLOW_POINT_BANK,			2,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	//-Bobboau
509 	{ "activate-glow-point-bank",		OP_ACTIVATE_GLOW_POINT_BANK,			2,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	//-Bobboau
510 	{ "set-thrusters-status",			OP_SET_THRUSTERS,						2,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	// The E
511 	{ "don't-collide-invisible",		OP_DONT_COLLIDE_INVISIBLE,				1,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	// Goober5000
512 	{ "collide-invisible",				OP_COLLIDE_INVISIBLE,					1,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	// Goober5000
513 	{ "add-to-collision-group",			OP_ADD_TO_COLGROUP,						2,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	// The E
514 	{ "remove-from-collision-group",	OP_REMOVE_FROM_COLGROUP,				2,	INT_MAX,	SEXP_ACTION_OPERATOR,	},
515 	{ "get-collision-group",			OP_GET_COLGROUP_ID,						1,	1,			SEXP_ACTION_OPERATOR,	},
516 	{ "change-team-color",				OP_CHANGE_TEAM_COLOR,					3,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	// The E
517 
518 	//Coordinate Manipulation Sub-Category
519 	{ "set-object-position",			OP_SET_OBJECT_POSITION,					4,	4,			SEXP_ACTION_OPERATOR,	},	// WMC
520 	{ "set-object-orientation",			OP_SET_OBJECT_ORIENTATION,				4,	4,			SEXP_ACTION_OPERATOR,	},	// Goober5000
521 	{ "set-object-facing",				OP_SET_OBJECT_FACING,					4,	6,			SEXP_ACTION_OPERATOR,	},	// Goober5000
522 	{ "set-object-facing-object",		OP_SET_OBJECT_FACING_OBJECT,			2,	4,			SEXP_ACTION_OPERATOR,	},	// Goober5000
523 	{ "set-object-speed-x",				OP_SET_OBJECT_SPEED_X,					2,	3,			SEXP_ACTION_OPERATOR,	},	// WMC
524 	{ "set-object-speed-y",				OP_SET_OBJECT_SPEED_Y,					2,	3,			SEXP_ACTION_OPERATOR,	},	// WMC
525 	{ "set-object-speed-z",				OP_SET_OBJECT_SPEED_Z,					2,	3,			SEXP_ACTION_OPERATOR,	},	// WMC
526 	{ "ship-maneuver",					OP_SHIP_MANEUVER,						10, 10,			SEXP_ACTION_OPERATOR,	},	// Wanderer
527 	{ "ship-rot-maneuver",				OP_SHIP_ROT_MANEUVER,					6,	6,			SEXP_ACTION_OPERATOR,	},	// Wanderer
528 	{ "ship-lat-maneuver",				OP_SHIP_LAT_MANEUVER,					6,	6,			SEXP_ACTION_OPERATOR,	},	// Wanderer
529 	{ "set-immobile",					OP_SET_IMMOBILE,						1,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	// Goober5000
530 	{ "set-mobile",						OP_SET_MOBILE,							1,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	// Goober5000
531 
532 	//Mission and Campaign Sub-Category
533 	{ "invalidate-goal",				OP_INVALIDATE_GOAL,						1,	INT_MAX,	SEXP_ACTION_OPERATOR,	},
534 	{ "validate-goal",					OP_VALIDATE_GOAL,						1,	INT_MAX,	SEXP_ACTION_OPERATOR,	},
535 	{ "red-alert",						OP_RED_ALERT,							0,	0,			SEXP_ACTION_OPERATOR,	},
536 	{ "end-mission",					OP_END_MISSION,							0,	2,			SEXP_ACTION_OPERATOR,	},	//-Sesquipedalian
537 	{ "force-jump",						OP_FORCE_JUMP,							0,	0,			SEXP_ACTION_OPERATOR,	},	// Goober5000
538 	{ "next-mission",					OP_NEXT_MISSION,						1,	1,			SEXP_ACTION_OPERATOR,	},
539 	{ "end-campaign",					OP_END_CAMPAIGN,						0,	0,			SEXP_ACTION_OPERATOR,	},
540 	{ "end-of-campaign",				OP_END_OF_CAMPAIGN,						0,	0,			SEXP_ACTION_OPERATOR,	},
541 	{ "set-debriefing-toggled",			OP_SET_DEBRIEFING_TOGGLED,				1,	1,			SEXP_ACTION_OPERATOR,	},	// Goober5000
542 	{ "allow-treason",					OP_ALLOW_TREASON,						1,	1,			SEXP_ACTION_OPERATOR,	},	// Karajorma
543 	{ "grant-promotion",				OP_GRANT_PROMOTION,						0,	0,			SEXP_ACTION_OPERATOR,	},
544 	{ "grant-medal",					OP_GRANT_MEDAL,							1,	1,			SEXP_ACTION_OPERATOR,	},
545 	{ "allow-ship",						OP_ALLOW_SHIP,							1,	1,			SEXP_ACTION_OPERATOR,	},
546 	{ "allow-weapon",					OP_ALLOW_WEAPON,						1,	1,			SEXP_ACTION_OPERATOR,	},
547 	{ "tech-add-ships",					OP_TECH_ADD_SHIP,						1,	INT_MAX,	SEXP_ACTION_OPERATOR,	},
548 	{ "tech-add-weapons",				OP_TECH_ADD_WEAPON,						1,	INT_MAX,	SEXP_ACTION_OPERATOR,	},
549 	{ "tech-add-intel",					OP_TECH_ADD_INTEL,						1,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	// Goober5000
550 	{ "tech-add-intel-xstr",			OP_TECH_ADD_INTEL_XSTR,					2,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	// Goober5000
551 	{ "tech-reset-to-default",			OP_TECH_RESET_TO_DEFAULT,				0,	0,			SEXP_ACTION_OPERATOR,	},	// Goober5000
552 	{ "change-player-score",			OP_CHANGE_PLAYER_SCORE,					2,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	// Karajorma
553 	{ "change-team-score",				OP_CHANGE_TEAM_SCORE,					2,	2,			SEXP_ACTION_OPERATOR,	},	// Karajorma
554 	{ "set-respawns",					OP_SET_RESPAWNS,						2,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	// Karajorma
555 
556 	//Music and Sound Sub-Category
557 	{ "change-soundtrack",				OP_CHANGE_SOUNDTRACK,					1,	1,			SEXP_ACTION_OPERATOR,	},	// Goober5000
558 	{ "play-sound-from-table",			OP_PLAY_SOUND_FROM_TABLE,				4,	4,			SEXP_ACTION_OPERATOR,	},	// Goober5000
559 	{ "play-sound-from-file",			OP_PLAY_SOUND_FROM_FILE,				1,	3,			SEXP_ACTION_OPERATOR,	},	// Goober5000
560 	{ "close-sound-from-file",			OP_CLOSE_SOUND_FROM_FILE,				1,	1,			SEXP_ACTION_OPERATOR,	},	// Goober5000
561 	{ "set-sound-environment",			OP_SET_SOUND_ENVIRONMENT,				1,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	// Taylor
562 	{ "update-sound-environment",		OP_UPDATE_SOUND_ENVIRONMENT,			2,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	// Taylor
563 	{ "adjust-audio-volume",			OP_ADJUST_AUDIO_VOLUME,					1,	3,			SEXP_ACTION_OPERATOR,	},
564 
565 	//HUD Sub-Category
566 	{ "hud-disable",					OP_HUD_DISABLE,							1,	1,			SEXP_ACTION_OPERATOR,	},	// Goober5000
567 	{ "hud-disable-except-messages",	OP_HUD_DISABLE_EXCEPT_MESSAGES,			1,	1,			SEXP_ACTION_OPERATOR,	},	// Goober5000
568 	{ "hud-set-custom-gauge-active",	OP_HUD_SET_CUSTOM_GAUGE_ACTIVE,			2, 	INT_MAX, 	SEXP_ACTION_OPERATOR,	},
569 	{ "hud-set-retail-gauge-active",	OP_HUD_SET_RETAIL_GAUGE_ACTIVE,			2, 	INT_MAX,	SEXP_ACTION_OPERATOR,	},
570 	{ "hud-set-text",					OP_HUD_SET_TEXT,						2,	2,			SEXP_ACTION_OPERATOR,	},	//WMCoolmon
571 	{ "hud-set-text-num",				OP_HUD_SET_TEXT_NUM,					2,	2,			SEXP_ACTION_OPERATOR,	},	//WMCoolmon
572 	{ "hud-set-message",				OP_HUD_SET_MESSAGE,						2,	2,			SEXP_ACTION_OPERATOR,	},	//The E
573 	{ "hud-set-directive",				OP_HUD_SET_DIRECTIVE,					2,	2,			SEXP_ACTION_OPERATOR,	},	//The E
574 	{ "hud-set-frame",					OP_HUD_SET_FRAME,						2,	2,			SEXP_ACTION_OPERATOR,	},	//WMCoolmon
575 	{ "hud-set-coords",					OP_HUD_SET_COORDS,						3,	3,			SEXP_ACTION_OPERATOR,	},	//WMCoolmon
576 	{ "hud-set-color",					OP_HUD_SET_COLOR,						4,	4,			SEXP_ACTION_OPERATOR,	},	//WMCoolmon
577 	{ "hud-display-gauge",				OP_HUD_DISPLAY_GAUGE,					2,	2,			SEXP_ACTION_OPERATOR,	},
578 	{ "hud-gauge-set-active",			OP_HUD_GAUGE_SET_ACTIVE,				2,	2,			SEXP_ACTION_OPERATOR,	},	//Deprecated
579 	{ "hud-activate-gauge-type",		OP_HUD_ACTIVATE_GAUGE_TYPE,				2,	2,			SEXP_ACTION_OPERATOR,	},	//Deprecated
580 	{ "hud-clear-messages",				OP_HUD_CLEAR_MESSAGES,					0,	0,			SEXP_ACTION_OPERATOR,	},	// swifty
581 	{ "hud-set-max-targeting-range",	OP_HUD_SET_MAX_TARGETING_RANGE,			1,	1,			SEXP_ACTION_OPERATOR,	},	// Goober5000
582 
583 	//Nav Sub-Category
584 	{ "add-nav-waypoint",				OP_NAV_ADD_WAYPOINT,					3,	4,			SEXP_ACTION_OPERATOR,	},	//kazan
585 	{ "add-nav-ship",					OP_NAV_ADD_SHIP,						2,	2,			SEXP_ACTION_OPERATOR,	},	//kazan
586 	{ "del-nav",						OP_NAV_DEL,								1,	1,			SEXP_ACTION_OPERATOR,	},	//kazan
587 	{ "hide-nav",						OP_NAV_HIDE,							1,	1,			SEXP_ACTION_OPERATOR,	},	//kazan
588 	{ "restrict-nav",					OP_NAV_RESTRICT,						1,	1,			SEXP_ACTION_OPERATOR,	},	//kazan
589 	{ "unhide-nav",						OP_NAV_UNHIDE,							1,	1,			SEXP_ACTION_OPERATOR,	},	//kazan
590 	{ "unrestrict-nav",					OP_NAV_UNRESTRICT,						1,	1,			SEXP_ACTION_OPERATOR,	},	//kazan
591 	{ "set-nav-visited",				OP_NAV_SET_VISITED,						1,	1,			SEXP_ACTION_OPERATOR,	},	//kazan
592 	{ "unset-nav-visited",				OP_NAV_UNSET_VISITED,					1,	1,			SEXP_ACTION_OPERATOR,	},	//kazan
593 	{ "set-nav-carry",					OP_NAV_SET_CARRY,						1,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	//kazan
594 	{ "unset-nav-carry",				OP_NAV_UNSET_CARRY,						1,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	//kazan
595 	{ "set-nav-needslink",				OP_NAV_SET_NEEDSLINK,					1,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	//kazan
596 	{ "unset-nav-needslink",			OP_NAV_UNSET_NEEDSLINK,					1,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	//kazan
597 	{ "is-nav-linked",					OP_NAV_ISLINKED,						1,	1,			SEXP_BOOLEAN_OPERATOR,	},	//kazan
598 	{ "use-nav-cinematics",				OP_NAV_USECINEMATICS,					1,	1,			SEXP_ACTION_OPERATOR,	},	//kazan
599 	{ "use-autopilot",					OP_NAV_USEAP,							1,	1,			SEXP_ACTION_OPERATOR,	},	//kazan
600 	{ "select-nav",						OP_NAV_SELECT,							1,	1,			SEXP_ACTION_OPERATOR,	},	//Talon1024
601 	{ "unselect-nav",					OP_NAV_UNSELECT,						0,	0,			SEXP_ACTION_OPERATOR,	},	//Talon1024
602 
603 	//Cutscene Sub-Category
604 	{ "set-cutscene-bars",				OP_CUTSCENES_SET_CUTSCENE_BARS,			0,	1,			SEXP_ACTION_OPERATOR,	},
605 	{ "unset-cutscene-bars",			OP_CUTSCENES_UNSET_CUTSCENE_BARS,		0,	1,			SEXP_ACTION_OPERATOR,	},
606 	{ "fade-in",						OP_CUTSCENES_FADE_IN,					0,	4,			SEXP_ACTION_OPERATOR,	},
607 	{ "fade-out",						OP_CUTSCENES_FADE_OUT,					0,	4,			SEXP_ACTION_OPERATOR,	},
608 	{ "set-camera",						OP_CUTSCENES_SET_CAMERA,				0,	1,			SEXP_ACTION_OPERATOR,	},
609 	{ "set-camera-position",			OP_CUTSCENES_SET_CAMERA_POSITION,		3,	6,			SEXP_ACTION_OPERATOR,	},
610 	{ "set-camera-facing",				OP_CUTSCENES_SET_CAMERA_FACING,			3,	6,			SEXP_ACTION_OPERATOR,	},
611 	{ "set-camera-facing-object",		OP_CUTSCENES_SET_CAMERA_FACING_OBJECT,	1,	4,			SEXP_ACTION_OPERATOR,	},
612 	{ "set-camera-rotation",			OP_CUTSCENES_SET_CAMERA_ROTATION,		3,	6,			SEXP_ACTION_OPERATOR,	},
613 	{ "set-camera-host",				OP_CUTSCENES_SET_CAMERA_HOST,			1,	2,			SEXP_ACTION_OPERATOR,	},
614 	{ "set-camera-target",				OP_CUTSCENES_SET_CAMERA_TARGET,			1,	2,			SEXP_ACTION_OPERATOR,	},
615 	{ "set-camera-fov",					OP_CUTSCENES_SET_CAMERA_FOV,			1,	5,			SEXP_ACTION_OPERATOR,	},
616 	{ "set-fov",						OP_CUTSCENES_SET_FOV,					1,	1,			SEXP_ACTION_OPERATOR,	},
617 	{ "get-fov",						OP_CUTSCENES_GET_FOV,					0,	0,			SEXP_INTEGER_OPERATOR,	},
618 	{ "reset-fov",						OP_CUTSCENES_RESET_FOV,					0,	0,			SEXP_ACTION_OPERATOR,	},
619 	{ "reset-camera",					OP_CUTSCENES_RESET_CAMERA,				0,	1,			SEXP_ACTION_OPERATOR,	},
620 	{ "show-subtitle",					OP_CUTSCENES_SHOW_SUBTITLE,				4,	13,			SEXP_ACTION_OPERATOR,	},
621 	{ "show-subtitle-text",				OP_CUTSCENES_SHOW_SUBTITLE_TEXT,		6,	13,			SEXP_ACTION_OPERATOR,	},
622 	{ "show-subtitle-image",			OP_CUTSCENES_SHOW_SUBTITLE_IMAGE,		8,	10,			SEXP_ACTION_OPERATOR,	},
623 	{ "clear-subtitles",				OP_CLEAR_SUBTITLES,						0,	0,			SEXP_ACTION_OPERATOR,	},
624 	{ "lock-perspective",				OP_CUTSCENES_FORCE_PERSPECTIVE,			1,	2,			SEXP_ACTION_OPERATOR,	},
625 	{ "set-camera-shudder",				OP_SET_CAMERA_SHUDDER,					2,	2,			SEXP_ACTION_OPERATOR,	},
626 	{ "supernova-start",				OP_SUPERNOVA_START,						1,	1,			SEXP_ACTION_OPERATOR,	},
627 	{ "supernova-stop",					OP_SUPERNOVA_STOP,						0,	0,			SEXP_ACTION_OPERATOR,	},	//CommanderDJ
628 	{ "set-motion-debris-override",		OP_SET_MOTION_DEBRIS,					1,  1,			SEXP_ACTION_OPERATOR,	},	// The E
629 
630 	//Background and Nebula Sub-Category
631 	{ "mission-set-nebula",				OP_MISSION_SET_NEBULA,					1,	1,			SEXP_ACTION_OPERATOR,	},	// Sesquipedalian
632 	{ "mission-set-subspace",			OP_MISSION_SET_SUBSPACE,				1,	1,			SEXP_ACTION_OPERATOR,	},
633 	{ "add-background-bitmap",			OP_ADD_BACKGROUND_BITMAP,				9,	9,			SEXP_ACTION_OPERATOR,	},	// phreak
634 	{ "remove-background-bitmap",		OP_REMOVE_BACKGROUND_BITMAP,			1,	1,			SEXP_ACTION_OPERATOR,	},	// phreak
635 	{ "add-sun-bitmap",					OP_ADD_SUN_BITMAP,						6,	6,			SEXP_ACTION_OPERATOR,	},	// phreak
636 	{ "remove-sun-bitmap",				OP_REMOVE_SUN_BITMAP,					1,	1,			SEXP_ACTION_OPERATOR,	},	// phreak
637 	{ "nebula-change-storm",			OP_NEBULA_CHANGE_STORM,					1,	1,			SEXP_ACTION_OPERATOR,	},	// phreak
638 	{ "nebula-toggle-poof",				OP_NEBULA_TOGGLE_POOF,					2,	2,			SEXP_ACTION_OPERATOR,	},	// phreak
639 	{ "nebula-change-pattern",			OP_NEBULA_CHANGE_PATTERN,				1,	1,			SEXP_ACTION_OPERATOR,	},	// Axem
640 	{ "set-skybox-model",				OP_SET_SKYBOX_MODEL,					1,	1,			SEXP_ACTION_OPERATOR,	},	// taylor
641 	{ "set-skybox-orientation",			OP_SET_SKYBOX_ORIENT,					3,	3,			SEXP_ACTION_OPERATOR,	},	// Goober5000
642 	{ "set-ambient-light",				OP_SET_AMBIENT_LIGHT,					3,	3,			SEXP_ACTION_OPERATOR,	},	// Karajorma
643 
644 	//Jump Node Sub-Category
645 	{ "set-jumpnode-name",				OP_JUMP_NODE_SET_JUMPNODE_NAME,			2,	2,			SEXP_ACTION_OPERATOR,	},	//CommanderDJ
646 	{ "set-jumpnode-color",				OP_JUMP_NODE_SET_JUMPNODE_COLOR,		5,	5,			SEXP_ACTION_OPERATOR,	},
647 	{ "set-jumpnode-model",				OP_JUMP_NODE_SET_JUMPNODE_MODEL,		3,	3,			SEXP_ACTION_OPERATOR,	},
648 	{ "show-jumpnode",					OP_JUMP_NODE_SHOW_JUMPNODE,				1,	INT_MAX,	SEXP_ACTION_OPERATOR,	},
649 	{ "hide-jumpnode",					OP_JUMP_NODE_HIDE_JUMPNODE,				1,	INT_MAX,	SEXP_ACTION_OPERATOR,	},
650 
651 	//Special Effects Sub-Category
652 	{ "set-post-effect",				OP_SET_POST_EFFECT,						2,	2,			SEXP_ACTION_OPERATOR,	},	// Hery
653 	{ "ship-effect",					OP_SHIP_EFFECT,							3,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	// Valathil
654 	{ "ship-create",					OP_SHIP_CREATE,							5,	8,			SEXP_ACTION_OPERATOR,	},	// WMC
655 	{ "weapon-create",					OP_WEAPON_CREATE,						5,	10,			SEXP_ACTION_OPERATOR,	},	// Goober5000
656 	{ "ship-vanish",					OP_SHIP_VANISH,							1,	INT_MAX,	SEXP_ACTION_OPERATOR,	},
657 	{ "ship-vaporize",					OP_SHIP_VAPORIZE,						1,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	// Goober5000
658 	{ "ship-no-vaporize",				OP_SHIP_NO_VAPORIZE,					1,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	// Goober5000
659 	{ "set-explosion-option",			OP_SET_EXPLOSION_OPTION,				3,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	// Goober5000
660 	{ "explosion-effect",				OP_EXPLOSION_EFFECT,					11,	13,			SEXP_ACTION_OPERATOR,	},	// Goober5000
661 	{ "warp-effect",					OP_WARP_EFFECT,							12, 12,			SEXP_ACTION_OPERATOR,	},	// Goober5000
662 	{ "remove-weapons",					OP_REMOVE_WEAPONS,						0,	1,			SEXP_ACTION_OPERATOR,	},	// Karajorma
663 	{ "set-time-compression",			OP_CUTSCENES_SET_TIME_COMPRESSION,		1,	3,			SEXP_ACTION_OPERATOR,	},
664 	{ "reset-time-compression",			OP_CUTSCENES_RESET_TIME_COMPRESSION,	0,	0,			SEXP_ACTION_OPERATOR,	},
665 	{ "call-ssm-strike",				OP_CALL_SSM_STRIKE,						3,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	// X3N0-Life-Form
666 
667 	//Variable Category
668 	{ "modify-variable",				OP_MODIFY_VARIABLE,						2,	2,			SEXP_ACTION_OPERATOR,	},
669 	{ "get-variable-by-index",			OP_GET_VARIABLE_BY_INDEX,				1,	1,			SEXP_INTEGER_OPERATOR,	},	// Goober5000
670 	{ "set-variable-by-index",			OP_SET_VARIABLE_BY_INDEX,				2,	2,			SEXP_ACTION_OPERATOR,	},	// Goober5000
671 	{ "copy-variable-from-index",		OP_COPY_VARIABLE_FROM_INDEX,			2,	2,			SEXP_ACTION_OPERATOR,	},	// Goober5000
672 	{ "copy-variable-between-indexes",	OP_COPY_VARIABLE_BETWEEN_INDEXES,		2,	2,			SEXP_ACTION_OPERATOR,	},	// Goober5000
673 	{ "int-to-string",					OP_INT_TO_STRING,						2,	2,			SEXP_ACTION_OPERATOR,	},	// Goober5000
674 	{ "string-concatenate",				OP_STRING_CONCATENATE,					3,	3,			SEXP_ACTION_OPERATOR,	},	// Goober5000
675 	{ "string-get-substring",			OP_STRING_GET_SUBSTRING,				4,	4,			SEXP_ACTION_OPERATOR,	},	// Goober5000
676 	{ "string-set-substring",			OP_STRING_SET_SUBSTRING,				5,	5,			SEXP_ACTION_OPERATOR,	},	// Goober5000
677 
678 	//Other Sub-Category
679 	{ "damaged-escort-priority",		OP_DAMAGED_ESCORT_LIST,					3,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	//phreak
680 	{ "damaged-escort-priority-all",	OP_DAMAGED_ESCORT_LIST_ALL,				1,	MAX_COMPLETE_ESCORT_LIST,	SEXP_ACTION_OPERATOR,	},	// Goober5000
681 	{ "set-support-ship",				OP_SET_SUPPORT_SHIP,					6,	7,			SEXP_ACTION_OPERATOR,	},	// Goober5000
682 	{ "script-eval",					OP_SCRIPT_EVAL,							1,	INT_MAX,	SEXP_ACTION_OPERATOR,	},
683 	{ "multi-eval",						OP_SCRIPT_EVAL_MULTI,					2,	INT_MAX,	SEXP_ACTION_OPERATOR,	},
684 	{ "debug",							OP_DEBUG,								2,	2,			SEXP_ACTION_OPERATOR,	},	// Karajorma
685 	{ "do-nothing",						OP_NOP,									0,	0,			SEXP_ACTION_OPERATOR,	},
686 
687 	//AI Goals Category
688 	{ "ai-chase",						OP_AI_CHASE,							2,	2,			SEXP_GOAL_OPERATOR,	},
689 	{ "ai-chase-wing",					OP_AI_CHASE_WING,						2,	2,			SEXP_GOAL_OPERATOR,	},
690 	{ "ai-chase-any",					OP_AI_CHASE_ANY,						1,	1,			SEXP_GOAL_OPERATOR,	},
691 	{ "ai-guard",						OP_AI_GUARD,							2,	2,			SEXP_GOAL_OPERATOR,	},
692 	{ "ai-guard-wing",					OP_AI_GUARD_WING,						2,	2,			SEXP_GOAL_OPERATOR,	},
693 	{ "ai-destroy-subsystem",			OP_AI_DESTROY_SUBSYS,					3,	3,			SEXP_GOAL_OPERATOR,	},
694 	{ "ai-disable-ship",				OP_AI_DISABLE_SHIP,						2,	2,			SEXP_GOAL_OPERATOR,	},
695 	{ "ai-disarm-ship",					OP_AI_DISARM_SHIP,						2,	2,			SEXP_GOAL_OPERATOR,	},
696 	{ "ai-warp",						OP_AI_WARP,								2,	2,			SEXP_GOAL_OPERATOR,	},
697 	{ "ai-warp-out",					OP_AI_WARP_OUT,							1,	1,			SEXP_GOAL_OPERATOR,	},
698 	{ "ai-dock",						OP_AI_DOCK,								4,	4,			SEXP_GOAL_OPERATOR,	},
699 	{ "ai-undock",						OP_AI_UNDOCK,							1,	2,			SEXP_GOAL_OPERATOR,	},
700 	{ "ai-waypoints",					OP_AI_WAYPOINTS,						2,	2,			SEXP_GOAL_OPERATOR,	},
701 	{ "ai-waypoints-once",				OP_AI_WAYPOINTS_ONCE,					2,	2,			SEXP_GOAL_OPERATOR,	},
702 	{ "ai-ignore",						OP_AI_IGNORE,							2,	2,			SEXP_GOAL_OPERATOR,	},
703 	{ "ai-ignore-new",					OP_AI_IGNORE_NEW,						2,	2,			SEXP_GOAL_OPERATOR,	},
704 	{ "ai-stay-near-ship",				OP_AI_STAY_NEAR_SHIP,					2,	2,			SEXP_GOAL_OPERATOR,	},
705 	{ "ai-evade-ship",					OP_AI_EVADE_SHIP,						2,	2,			SEXP_GOAL_OPERATOR,	},
706 	{ "ai-keep-safe-distance",			OP_AI_KEEP_SAFE_DISTANCE,				1,	1,			SEXP_GOAL_OPERATOR,	},
707 	{ "ai-stay-still",					OP_AI_STAY_STILL,						2,	2,			SEXP_GOAL_OPERATOR,	},
708 	{ "ai-play-dead",					OP_AI_PLAY_DEAD,						1,	1,			SEXP_GOAL_OPERATOR,	},
709 	{ "ai-form-on-wing",				OP_AI_FORM_ON_WING,						1,	1,			SEXP_GOAL_OPERATOR,	},
710 
711 	{ "goals",							OP_GOALS_ID,							1,	INT_MAX,	SEXP_ACTION_OPERATOR,	},
712 
713 	//Training Category
714 	{ "key-pressed",					OP_KEY_PRESSED,							1,	2,			SEXP_BOOLEAN_OPERATOR,	},
715 	{ "key-reset",						OP_KEY_RESET,							1,	INT_MAX,	SEXP_ACTION_OPERATOR,	},
716 	{ "key-reset-multiple",				OP_KEY_RESET_MULTIPLE,					1,	INT_MAX,	SEXP_ACTION_OPERATOR,	},
717 	{ "ignore-key",						OP_IGNORE_KEY,							2,	INT_MAX,	SEXP_ACTION_OPERATOR,	},	// Karajorma
718 	{ "targeted",						OP_TARGETED,							1,	3,			SEXP_BOOLEAN_OPERATOR,	},
719 	{ "node-targeted",					OP_NODE_TARGETED,						1,	2,			SEXP_BOOLEAN_OPERATOR,	},	// FUBAR
720 	{ "missile-locked",					OP_MISSILE_LOCKED,						1,	3,			SEXP_BOOLEAN_OPERATOR,	},	// Sesquipedalian
721 	{ "speed",							OP_SPEED,								1,	1,			SEXP_BOOLEAN_OPERATOR,	},
722 	{ "facing",							OP_FACING,								2,	2,			SEXP_BOOLEAN_OPERATOR,	},
723 	{ "facing-waypoint",				OP_FACING2,								2,	2,			SEXP_BOOLEAN_OPERATOR,	},
724 	{ "order",							OP_ORDER,								2,	3,			SEXP_BOOLEAN_OPERATOR,	},
725 	{ "query-orders",					OP_QUERY_ORDERS,						3,	6,			SEXP_BOOLEAN_OPERATOR,	}, // Karajorma
726 	{ "reset-orders",					OP_RESET_ORDERS,						0,	0,			SEXP_ACTION_OPERATOR,	}, // Karajorma
727 	{ "waypoint-missed",				OP_WAYPOINT_MISSED,						0,	0,			SEXP_BOOLEAN_OPERATOR,	},
728 	{ "waypoint-twice",					OP_WAYPOINT_TWICE,						0,	0,			SEXP_BOOLEAN_OPERATOR,	},
729 	{ "path-flown",						OP_PATH_FLOWN,							0,	0,			SEXP_BOOLEAN_OPERATOR,	},
730 	{ "training-msg",					OP_TRAINING_MSG,						1,	4,			SEXP_ACTION_OPERATOR,	},
731 	{ "flash-hud-gauge",				OP_FLASH_HUD_GAUGE,						1,	1,			SEXP_ACTION_OPERATOR,	},
732 	{ "primaries-depleted",				OP_PRIMARIES_DEPLETED,					1,	1,			SEXP_BOOLEAN_OPERATOR,	},
733 	{ "secondaries-depleted",			OP_SECONDARIES_DEPLETED,				1,	1,			SEXP_BOOLEAN_OPERATOR,	},
734 	{ "special-check",					OP_SPECIAL_CHECK,						1,	1,			SEXP_ACTION_OPERATOR,	},
735 	{ "set-training-context-fly-path",	OP_SET_TRAINING_CONTEXT_FLY_PATH,		2,	2,			SEXP_ACTION_OPERATOR,	},
736 	{ "set-training-context-speed",		OP_SET_TRAINING_CONTEXT_SPEED,			2,	2,			SEXP_ACTION_OPERATOR,	},
737 };
738 
739 sexp_ai_goal_link Sexp_ai_goal_links[] = {
740 	{ AI_GOAL_CHASE, OP_AI_CHASE },
741 	{ AI_GOAL_CHASE_WING, OP_AI_CHASE_WING },
742 	{ AI_GOAL_DOCK, OP_AI_DOCK },
743 	{ AI_GOAL_UNDOCK, OP_AI_UNDOCK },
744 	{ AI_GOAL_WARP, OP_AI_WARP_OUT },
745 	{ AI_GOAL_WARP, OP_AI_WARP },
746 	{ AI_GOAL_WAYPOINTS, OP_AI_WAYPOINTS },
747 	{ AI_GOAL_WAYPOINTS_ONCE, OP_AI_WAYPOINTS_ONCE },
748 	{ AI_GOAL_DESTROY_SUBSYSTEM, OP_AI_DESTROY_SUBSYS },
749 	{ AI_GOAL_DISABLE_SHIP, OP_AI_DISABLE_SHIP },
750 	{ AI_GOAL_DISARM_SHIP, OP_AI_DISARM_SHIP },
751 	{ AI_GOAL_GUARD, OP_AI_GUARD },
752 	{ AI_GOAL_CHASE_ANY, OP_AI_CHASE_ANY },
753 	{ AI_GOAL_GUARD_WING, OP_AI_GUARD_WING },
754 	{ AI_GOAL_EVADE_SHIP, OP_AI_EVADE_SHIP },
755 	{ AI_GOAL_STAY_NEAR_SHIP, OP_AI_STAY_NEAR_SHIP },
756 	{ AI_GOAL_KEEP_SAFE_DISTANCE, OP_AI_KEEP_SAFE_DISTANCE },
757 	{ AI_GOAL_IGNORE, OP_AI_IGNORE },
758 	{ AI_GOAL_IGNORE_NEW, OP_AI_IGNORE_NEW },
759 	{ AI_GOAL_STAY_STILL, OP_AI_STAY_STILL },
760 	{ AI_GOAL_PLAY_DEAD, OP_AI_PLAY_DEAD },
761 	{ AI_GOAL_FORM_ON_WING, OP_AI_FORM_ON_WING }
762 };
763 
764 char *HUD_gauge_text[NUM_HUD_GAUGES] =
765 {
766 	"LEAD_INDICATOR",
767 	"ORIENTATION_TEE",
768 	"HOSTILE_TRIANGLE",
769 	"TARGET_TRIANGLE",
770 	"MISSION_TIME",
771 	"RETICLE_CIRCLE",
772 	"THROTTLE_GAUGE",
773 	"RADAR",
774 	"TARGET_MONITOR",
775 	"CENTER_RETICLE",
776 	"TARGET_MONITOR_EXTRA_DATA",
777 	"TARGET_SHIELD_ICON",
778 	"PLAYER_SHIELD_ICON",
779 	"ETS_GAUGE",
780 	"AUTO_TARGET",
781 	"AUTO_SPEED",
782 	"WEAPONS_GAUGE",
783 	"ESCORT_VIEW",
784 	"DIRECTIVES_VIEW",
785 	"THREAT_GAUGE",
786 	"AFTERBURNER_ENERGY",
787 	"WEAPONS_ENERGY",
788 	"WEAPON_LINKING_GAUGE",
789 	"TARGER_MINI_ICON",
790 	"OFFSCREEN_INDICATOR",
791 	"TALKING_HEAD",
792 	"DAMAGE_GAUGE",
793 	"MESSAGE_LINES",
794 	"MISSILE_WARNING_ARROW",
795 	"CMEASURE_GAUGE",
796 	"OBJECTIVES_NOTIFY_GAUGE",
797 	"WINGMEN_STATUS",
798 	"OFFSCREEN RANGE",
799 	"KILLS GAUGE",
800 	"ATTACKING TARGET COUNT",
801 	"TEXT FLASH",
802 	"MESSAGE BOX",
803 	"SUPPORT GUAGE",
804 	"LAG GUAGE"
805 };
806 
807 
808 void sexp_set_skybox_model_preload(char *name); // taylor
809 
810 int	Directive_count;
811 int	Sexp_useful_number;  // a variable to pass useful info in from external modules
812 int	Locked_sexp_true, Locked_sexp_false;
813 int	Num_operators = sizeof(Operators) / sizeof(sexp_oper);
814 int	Num_sexp_ai_goal_links = sizeof(Sexp_ai_goal_links) / sizeof(sexp_ai_goal_link);
815 int	Sexp_clipboard = -1;  // used by Fred
816 int	Training_context = 0;
817 int	Training_context_speed_set;
818 int	Training_context_speed_min;
819 int	Training_context_speed_max;
820 int	Training_context_speed_timestamp;
821 waypoint_list *Training_context_path;
822 int Training_context_goal_waypoint;
823 int Training_context_at_waypoint;
824 float	Training_context_distance;
825 
826 #define SEXP_NODE_INCREMENT	250
827 int Num_sexp_nodes = 0;
828 sexp_node *Sexp_nodes = NULL;
829 
830 sexp_variable Sexp_variables[MAX_SEXP_VARIABLES];
831 sexp_variable Block_variables[MAX_SEXP_VARIABLES];			// used for compatibility with retail.
832 
833 int Num_special_expl_blocks;
834 
835 SCP_vector<int> Current_sexp_operator;
836 
837 int Players_target = UNINITIALIZED;
838 int Players_mlocked = UNINITIALIZED; // for is-missile-locked - Sesquipedalian
839 ship_subsys *Players_targeted_subsys;
840 int Players_target_timestamp;
841 int Players_mlocked_timestamp;
842 
843 // for play-music - Goober5000
844 int	Sexp_music_handle = -1;
845 void sexp_stop_music(int fade = 1);
846 
847 // for sound environments - Goober5000/Taylor
848 #define SEO_VOLUME		0
849 #define SEO_DECAY_TIME	1
850 #define SEO_DAMPING		2
851 int sexp_sound_environment_option_lookup(char *text);
852 char *Sound_environment_option[] = { "volume", "decay time", "damping" };
853 int Num_sound_environment_options = 3;
854 
855 // for adjust-audio-volume - The E
856 char *Adjust_audio_options[] = { "Music", "Voice", "Effects" };
857 int Num_adjust_audio_options = 3;
858 int audio_volume_option_lookup(char *text);
859 
860 int hud_gauge_type_lookup(char* name);
861 
862 // for explosions - Goober5000
863 #define EO_DAMAGE			0
864 #define EO_BLAST			1
865 #define EO_INNER_RADIUS		2
866 #define EO_OUTER_RADIUS		3
867 #define EO_SHOCKWAVE_SPEED	4
868 #define EO_DEATH_ROLL_TIME	5
869 int sexp_explosion_option_lookup(char *text);
870 char *Explosion_option[] = { "damage", "blast", "inner radius", "outer radius", "shockwave speed", "death roll time" };
871 int Num_explosion_options = 6;
872 
873 int get_sexp();
874 void build_extended_sexp_string(SCP_string &accumulator, int cur_node, int level, int mode);
875 void update_sexp_references(const char *old_name, const char *new_name, int format, int node);
876 int sexp_determine_team(char *subj);
877 int extract_sexp_variable_index(int node);
878 void init_sexp_vars();
879 int eval_num(int node);
880 
881 // for handling variables
882 void add_block_variable(const char *text, const char *var_name, int type, int index);
883 void sexp_modify_variable(int node);
884 int sexp_get_variable_by_index(int node);
885 void sexp_set_variable_by_index(int node);
886 void sexp_copy_variable_from_index(int node);
887 void sexp_copy_variable_between_indexes(int node);
888 
889 SCP_vector<char*> Sexp_replacement_arguments;
890 int Sexp_current_argument_nesting_level;
891 SCP_vector<char*> Applicable_arguments_temp;
892 
893 // Goober5000
894 arg_item Sexp_applicable_argument_list;
895 bool is_blank_argument_op(int op_const);
896 bool is_blank_of_op(int op_const);
897 
898 int get_handler_for_x_of_operator(int node);
899 
900 //Karajorma
901 int get_generic_subsys(char *subsy_name);
902 bool ship_class_unchanged(int ship_index);
903 void multi_sexp_modify_variable();
904 
905 int get_effect_from_name(char* name);
906 
907 #define NO_OPERATOR_INDEX_DEFINED		-2
908 #define NOT_A_SEXP_OPERATOR				-1
909 
910 // Karajorma - some useful helper methods
911 player * get_player_from_ship_node(int node, bool test_respawns = false);
912 ship * sexp_get_ship_from_node(int node);
913 
914 // hud-display-gauge magic values
915 #define SEXP_HUD_GAUGE_WARPOUT "warpout"
916 
917 // event log stuff
918 SCP_vector<SCP_string> *Current_event_log_buffer;
919 SCP_vector<SCP_string> *Current_event_log_variable_buffer;
920 SCP_vector<SCP_string> *Current_event_log_argument_buffer;
921 // Goober5000 - arg_item class stuff, borrowed from sexp_list_item class stuff -------------
add_data(char * str)922 void arg_item::add_data(char *str)
923 {
924 	arg_item *item, *ptr;
925 
926 	// create item
927 	item = new arg_item;
928 	item->text = str;
929 	item->nesting_level = Sexp_current_argument_nesting_level;
930 
931 	// prepend item to existing list
932 	ptr = this->next;
933 	this->next = item;
934 	item->next = ptr;
935 }
936 
add_data_dup(char * str)937 void arg_item::add_data_dup(char *str)
938 {
939 	arg_item *item, *ptr;
940 
941 	// create item
942 	item = new arg_item;
943 	item->text = strdup(str);
944 	item->flags |= ARG_ITEM_F_DUP;
945 	item->nesting_level = Sexp_current_argument_nesting_level;
946 
947 	// prepend item to existing list
948 	ptr = this->next;
949 	this->next = item;
950 	item->next = ptr;
951 }
952 
add_data_set_dup(char * str)953 void arg_item::add_data_set_dup(char *str)
954 {
955 	arg_item *item, *ptr;
956 
957 	// create item
958 	item = new arg_item;
959 	item->text = str;
960 	item->flags |= ARG_ITEM_F_DUP;
961 	item->nesting_level = Sexp_current_argument_nesting_level;
962 
963 	// prepend item to existing list
964 	ptr = this->next;
965 	this->next = item;
966 	item->next = ptr;
967 }
968 
get_next()969 arg_item* arg_item::get_next()
970 {
971 	if (this->next != NULL) {
972 		if (this->next->nesting_level >= Sexp_current_argument_nesting_level) {
973 			return this->next;
974 		}
975 	}
976 
977 	return NULL;
978 }
979 
expunge()980 void arg_item::expunge()
981 {
982 	arg_item *ptr;
983 
984 	// contiually delete first item of list
985 	while (this->next != NULL)
986 	{
987 		ptr = this->next->next;
988 
989 		if (this->next->flags & ARG_ITEM_F_DUP)
990 			free(this->next->text);
991 		delete this->next;
992 
993 		this->next = ptr;
994 	}
995 }
996 
clear_nesting_level()997 void arg_item::clear_nesting_level()
998 {
999 	arg_item *ptr;
1000 
1001 	// contiually delete first item of list
1002 	while (this->next != NULL && this->next->nesting_level >= Sexp_current_argument_nesting_level )
1003 	{
1004 		ptr = this->next->next;
1005 
1006 		if (this->next->flags & ARG_ITEM_F_DUP)
1007 			free(this->next->text);
1008 		delete this->next;
1009 
1010 		this->next = ptr;
1011 	}
1012 }
1013 
is_empty()1014 int arg_item::is_empty()
1015 {
1016 	return (this->next == NULL);
1017 }
1018 //-------------------------------------------------------------------------------------------------
1019 
sexp_nodes_init()1020 void sexp_nodes_init()
1021 {
1022 	if (Num_sexp_nodes == 0 || Sexp_nodes == NULL)
1023 		return;
1024 
1025 	nprintf(("SEXP", "Reinitializing sexp nodes...\n"));
1026 	nprintf(("SEXP", "Entered function with %d nodes.\n", Num_sexp_nodes));
1027 
1028 	// usually, the persistent nodes are grouped at the beginning of the array;
1029 	// so we ought to be able to free all the subsequent nodes
1030 	int i, last_persistent_node = -1;
1031 
1032 	for (i = 0; i < Num_sexp_nodes; i++)
1033 	{
1034 		if (Sexp_nodes[i].type & SEXP_FLAG_PERSISTENT)
1035 			last_persistent_node = i;					// keep track of it
1036 		else
1037 			Sexp_nodes[i].type = SEXP_NOT_USED;			// it's not needed
1038 	}
1039 
1040 	nprintf(("SEXP", "Last persistent node index is %d.\n", last_persistent_node));
1041 
1042 	// if all the persistent nodes are gone, free all the nodes
1043 	if (last_persistent_node == -1)
1044 	{
1045 		vm_free(Sexp_nodes);
1046 		Sexp_nodes = NULL;
1047 		Num_sexp_nodes = 0;
1048 	}
1049 	// if there's enough of a difference to make it worthwhile, free some nodes
1050 	else if (Num_sexp_nodes - (last_persistent_node + 1) > 2 * SEXP_NODE_INCREMENT)
1051 	{
1052 		// round it up to the next evenly divisible size
1053 		Num_sexp_nodes = (last_persistent_node + 1);
1054 		Num_sexp_nodes += SEXP_NODE_INCREMENT - (Num_sexp_nodes % SEXP_NODE_INCREMENT);
1055 
1056 		Sexp_nodes = (sexp_node *) vm_realloc(Sexp_nodes, sizeof(sexp_node) * Num_sexp_nodes);
1057 		Verify(Sexp_nodes != NULL);
1058 	}
1059 
1060 	nprintf(("SEXP", "Exited function with %d nodes.\n", Num_sexp_nodes));
1061 }
1062 
sexp_nodes_close()1063 static void sexp_nodes_close()
1064 {
1065 	// free all sexp nodes... should only be done on game shutdown
1066 	if (Sexp_nodes != NULL)
1067 	{
1068 		vm_free(Sexp_nodes);
1069 		Sexp_nodes = NULL;
1070 		Num_sexp_nodes = 0;
1071 	}
1072 }
1073 
init_sexp()1074 void init_sexp()
1075 {
1076 	// Goober5000
1077 	Sexp_replacement_arguments.clear();
1078 	Sexp_applicable_argument_list.expunge();
1079 	Sexp_current_argument_nesting_level = 0;
1080 	initalise_sexp_packet();
1081 
1082 	static bool done_sexp_atexit = false;
1083 	if (!done_sexp_atexit)
1084 	{
1085 		atexit(sexp_nodes_close);
1086 		done_sexp_atexit = true;
1087 	}
1088 
1089 	sexp_nodes_init();
1090 	init_sexp_vars();
1091 	Locked_sexp_false = Locked_sexp_true = -1;
1092 
1093 	Locked_sexp_false = alloc_sexp("false", SEXP_LIST, SEXP_ATOM_OPERATOR, -1, -1);
1094 	Assert(Locked_sexp_false != -1);
1095 	Sexp_nodes[Locked_sexp_false].type = SEXP_ATOM;  // fix bypassing value
1096 	Sexp_nodes[Locked_sexp_false].value = SEXP_KNOWN_FALSE;
1097 
1098 	Locked_sexp_true = alloc_sexp("true", SEXP_LIST, SEXP_ATOM_OPERATOR, -1, -1);
1099 	Assert(Locked_sexp_true != -1);
1100 	Sexp_nodes[Locked_sexp_true].type = SEXP_ATOM;  // fix bypassing value
1101 	Sexp_nodes[Locked_sexp_true].value = SEXP_KNOWN_TRUE;
1102 }
1103 
1104 /**
1105  * Allocate an sexp node.
1106  */
alloc_sexp(char * text,int type,int subtype,int first,int rest)1107 int alloc_sexp(char *text, int type, int subtype, int first, int rest)
1108 {
1109 	int node;
1110 	int sexp_const = get_operator_const(text);
1111 
1112 	if ((sexp_const == OP_TRUE) && (type == SEXP_ATOM) && (subtype == SEXP_ATOM_OPERATOR))
1113 		return Locked_sexp_true;
1114 
1115 	else if ((sexp_const == OP_FALSE) && (type == SEXP_ATOM) && (subtype == SEXP_ATOM_OPERATOR))
1116 		return Locked_sexp_false;
1117 
1118 	node = find_free_sexp();
1119 
1120 	// need more sexp nodes?
1121 	if (node == Num_sexp_nodes || node == -1)
1122 	{
1123 		int old_size = Num_sexp_nodes;
1124 
1125 		Assert(SEXP_NODE_INCREMENT > 0);
1126 
1127 		// allocate in blocks of SEXP_NODE_INCREMENT
1128 		Num_sexp_nodes += SEXP_NODE_INCREMENT;
1129 		Sexp_nodes = (sexp_node *) vm_realloc(Sexp_nodes, sizeof(sexp_node) * Num_sexp_nodes);
1130 
1131 		Verify(Sexp_nodes != NULL);
1132 		nprintf(("SEXP", "Bumping dynamic sexp node limit from %d to %d...\n", old_size, Num_sexp_nodes));
1133 
1134 		// clear all the new sexp nodes we just allocated
1135 		memset(&Sexp_nodes[old_size], 0, sizeof(sexp_node) * SEXP_NODE_INCREMENT); //-V512
1136 
1137 		// our new sexp is the first out of the ones we just created
1138 		node = old_size;
1139 	}
1140 
1141 	Assert(node != Locked_sexp_true);
1142 	Assert(node != Locked_sexp_false);
1143 	Assert(strlen(text) < TOKEN_LENGTH);
1144 	Assert(type >= 0);
1145 
1146 	strcpy_s(Sexp_nodes[node].text, text);
1147 	Sexp_nodes[node].type = type;
1148 	Sexp_nodes[node].subtype = subtype;
1149 	Sexp_nodes[node].first = first;
1150 	Sexp_nodes[node].rest = rest;
1151 	Sexp_nodes[node].value = SEXP_UNKNOWN;
1152 	Sexp_nodes[node].flags = SNF_DEFAULT_VALUE;	// Goober5000
1153 	Sexp_nodes[node].op_index = NO_OPERATOR_INDEX_DEFINED;
1154 
1155 	return node;
1156 }
1157 
1158 static int Sexp_hwm = 0;
1159 
count_free_sexp_nodes()1160 int count_free_sexp_nodes()
1161 {
1162 	int i, f = 0, p = 0;
1163 
1164 	for (i = 0; i < Num_sexp_nodes; i++)
1165 	{
1166 		if (Sexp_nodes[i].type == SEXP_NOT_USED)
1167 			f++;
1168 		else if (Sexp_nodes[i].type & SEXP_FLAG_PERSISTENT)
1169 			p++;
1170 	}
1171 
1172 	if (Num_sexp_nodes - f > Sexp_hwm)
1173 	{
1174 		nprintf(("Sexp", "Sexp nodes: Free=%d, Used=%d, Persistent=%d\n", f, Num_sexp_nodes - f, p));
1175 		Sexp_hwm = Num_sexp_nodes - f;
1176 	}
1177 
1178 	return f;
1179 }
1180 
1181 /**
1182  * Find the next free sexp and return its index.
1183  */
find_free_sexp()1184 int find_free_sexp()
1185 {
1186 	int i;
1187 
1188 	// sanity
1189 	if (Num_sexp_nodes == 0 || Sexp_nodes == NULL)
1190 		return -1;
1191 
1192 	for (i = 0; i < Num_sexp_nodes; i++)
1193 	{
1194 		if (Sexp_nodes[i].type == SEXP_NOT_USED)
1195 			return i;
1196 	}
1197 
1198 	return -1;
1199 }
1200 
1201 /**
1202  * Mark a whole sexp tree with the persistent flag so that it won't get re-used between missions
1203  */
sexp_mark_persistent(int n)1204 void sexp_mark_persistent(int n)
1205 {
1206 	if (n == -1){
1207 		return;
1208 	}
1209 
1210 	// total hack because of the true/false locked sexps -- we should make those persistent as well
1211 	if ( (n == Locked_sexp_true) || (n == Locked_sexp_false) ){
1212 		return;
1213 	}
1214 
1215 	Assert( !(Sexp_nodes[n].type & SEXP_FLAG_PERSISTENT) );
1216 	Sexp_nodes[n].type |= SEXP_FLAG_PERSISTENT;
1217 
1218 	sexp_mark_persistent(Sexp_nodes[n].first);
1219 	sexp_mark_persistent(Sexp_nodes[n].rest);
1220 
1221 }
1222 
1223 /**
1224  * Remove the persistent flag from all nodes in the tree
1225  */
sexp_unmark_persistent(int n)1226 void sexp_unmark_persistent(int n)
1227 {
1228 	if (n == -1){
1229 		return;
1230 	}
1231 
1232 	if ( (n == Locked_sexp_true) || (n == Locked_sexp_false) ){
1233 		return;
1234 	}
1235 
1236 	Assert( Sexp_nodes[n].type & SEXP_FLAG_PERSISTENT );
1237 	Sexp_nodes[n].type &= ~SEXP_FLAG_PERSISTENT;
1238 
1239 	sexp_unmark_persistent(Sexp_nodes[n].first);
1240 	sexp_unmark_persistent(Sexp_nodes[n].rest);
1241 }
1242 
1243 /**
1244  * Free up the specified sexp node,  Leaves link chains untouched.
1245  */
free_one_sexp(int num)1246 int free_one_sexp(int num)
1247 {
1248 	Assert((num >= 0) && (num < Num_sexp_nodes));
1249 	Assert(Sexp_nodes[num].type != SEXP_NOT_USED);  // make sure it is actually used
1250 	Assert(!(Sexp_nodes[num].type & SEXP_FLAG_PERSISTENT));
1251 
1252 	if ((num == Locked_sexp_true) || (num == Locked_sexp_false))
1253 		return 0;
1254 
1255 	Sexp_nodes[num].type = SEXP_NOT_USED;
1256 	return 1;
1257 }
1258 
1259 /**
1260  * Free a used sexp node, so it can be reused later.
1261  *
1262  * Should only be called on an atom or a list, and not an operator.  If on a list, the
1263  * list and everything in it will be freed (including the operator).
1264  */
free_sexp(int num)1265 int free_sexp(int num)
1266 {
1267 	int i, rest, count = 0;
1268 
1269 	Assert((num >= 0) && (num < Num_sexp_nodes));
1270 	Assert(Sexp_nodes[num].type != SEXP_NOT_USED);  // make sure it is actually used
1271 	Assert(!(Sexp_nodes[num].type & SEXP_FLAG_PERSISTENT));
1272 
1273 	if ((num == Locked_sexp_true) || (num == Locked_sexp_false))
1274 		return 0;
1275 
1276 	Sexp_nodes[num].type = SEXP_NOT_USED;
1277 	count++;
1278 
1279 	i = Sexp_nodes[num].first;
1280 	while (i != -1)
1281 	{
1282 		count += free_sexp(i);
1283 		i = Sexp_nodes[i].rest;
1284 	}
1285 
1286 	rest = Sexp_nodes[num].rest;
1287 	for (i = 0; i < Num_sexp_nodes; i++)
1288 	{
1289 		if (Sexp_nodes[i].first == num)
1290 			Sexp_nodes[i].first = rest;
1291 
1292 		if (Sexp_nodes[i].rest == num)
1293 			Sexp_nodes[i].rest = rest;
1294 	}
1295 
1296 	return count;  // total elements freed up.
1297 }
1298 
1299 /**
1300  * Free up an entire sexp tree.
1301  *
1302  * Because the root node is an operator, instead of a list, we can't simply call free_sexp().
1303  * This function should only be called on the root node of an sexp, otherwise the linking will get screwed up.
1304  */
free_sexp2(int num)1305 int free_sexp2(int num)
1306 {
1307 	int i, count = 0;
1308 
1309 	if ((num == -1) || (num == Locked_sexp_true) || (num == Locked_sexp_false)){
1310 		return 0;
1311 	}
1312 
1313 	i = Sexp_nodes[num].rest;
1314 	while (i != -1) {
1315 		count += free_sexp(i);
1316 		i = Sexp_nodes[i].rest;
1317 	}
1318 
1319 	count += free_sexp(num);
1320 	return count;
1321 }
1322 
1323 /**
1324  * Reset the status of all the nodes in a tree, forcing them to all be evaulated again.
1325  */
flush_sexp_tree(int node)1326 void flush_sexp_tree(int node)
1327 {
1328 	if (node < 0){
1329 		return;
1330 	}
1331 
1332 	Sexp_nodes[node].value = SEXP_UNKNOWN;
1333 	flush_sexp_tree(Sexp_nodes[node].first);
1334 	flush_sexp_tree(Sexp_nodes[node].rest);
1335 }
1336 
verify_sexp_tree(int node)1337 int verify_sexp_tree(int node)
1338 {
1339 	if (node == -1){
1340 		return 0;
1341 	}
1342 
1343 	if ((Sexp_nodes[node].type == SEXP_NOT_USED) ||
1344 		(Sexp_nodes[node].first == node) ||
1345 		(Sexp_nodes[node].rest == node)) {
1346 		Error(LOCATION, "Sexp node is corrupt");
1347 		return -1;
1348 	}
1349 
1350 	if (Sexp_nodes[node].first != -1){
1351 		verify_sexp_tree(Sexp_nodes[node].first);
1352 	}
1353 	if (Sexp_nodes[node].rest != -1){
1354 		verify_sexp_tree(Sexp_nodes[node].rest);
1355 	}
1356 
1357 	return 0;
1358 }
1359 
1360 /**
1361  * @todo CASE OF SEXP VARIABLES - ONLY 1 COPY OF VARIABLE
1362  */
dup_sexp_chain(int node)1363 int dup_sexp_chain(int node)
1364 {
1365 	int cur, first, rest;
1366 
1367 	if (node == -1){
1368 		return -1;
1369 	}
1370 
1371 	// TODO - CASE OF SEXP VARIABLES - ONLY 1 COPY OF VARIABLE
1372 	first = dup_sexp_chain(Sexp_nodes[node].first);
1373 	rest = dup_sexp_chain(Sexp_nodes[node].rest);
1374 	cur = alloc_sexp(Sexp_nodes[node].text, Sexp_nodes[node].type, Sexp_nodes[node].subtype, first, rest);
1375 
1376 	if (cur == -1) {
1377 		if (first != -1){
1378 			free_sexp(first);
1379 		}
1380 		if (rest != -1){
1381 			free_sexp(rest);
1382 		}
1383 	}
1384 
1385 	return cur;
1386 }
1387 
1388 /**
1389  * Compare SEXP chains
1390  * @return 1 if they are the same, 0 if different
1391  */
cmp_sexp_chains(int node1,int node2)1392 int cmp_sexp_chains(int node1, int node2)
1393 {
1394 	if ((node1 == -1) && (node2 == -1)){
1395 		return 1;
1396 	}
1397 
1398 	if ((node1 == -1) || (node2 == -1)){
1399 		return 0;
1400 	}
1401 
1402 	// DA: 1/7/99 Need to check the actual Sexp_node.text, not possible variable, which can be equal
1403 	if (stricmp(Sexp_nodes[node1].text, Sexp_nodes[node2].text)){
1404 		return 0;
1405 	}
1406 
1407 	if (!cmp_sexp_chains(Sexp_nodes[node1].first, Sexp_nodes[node2].first)){
1408 		return 0;
1409 	}
1410 
1411 	if (!cmp_sexp_chains(Sexp_nodes[node1].rest, Sexp_nodes[node2].rest)){
1412 		return 0;
1413 	}
1414 
1415 	return 1;
1416 }
1417 
1418 /**
1419  * Determine if an sexp node is within the given sexp chain.
1420  */
query_node_in_sexp(int node,int sexp)1421 int query_node_in_sexp(int node, int sexp)
1422 {
1423 	if (sexp == -1){
1424 		return 0;
1425 	}
1426 	if (node == sexp){
1427 		return 1;
1428 	}
1429 
1430 	if (query_node_in_sexp(node, Sexp_nodes[sexp].first)){
1431 		return 1;
1432 	}
1433 	if (query_node_in_sexp(node, Sexp_nodes[sexp].rest)){
1434 		return 1;
1435 	}
1436 
1437 	return 0;
1438 }
1439 
1440 /**
1441  * Find the index of the list associated with an operator
1442  */
find_sexp_list(int num)1443 int find_sexp_list(int num)
1444 {
1445 	int i;
1446 
1447 	for (i = 0; i < Num_sexp_nodes; i++)
1448 	{
1449 		if (Sexp_nodes[i].first == num)
1450 			return i;
1451 	}
1452 
1453 	// assume that it was the first item in the list
1454 	return num;
1455 }
1456 
1457 /**
1458  * Find node of operator that item is an argument of.
1459  */
find_parent_operator(int node)1460 int find_parent_operator(int node)
1461 {
1462 	int i;
1463 	int n = node;
1464 
1465 	Assert((node >= 0) && (node < Num_sexp_nodes));
1466 
1467 	if (Sexp_nodes[n].subtype == SEXP_ATOM_OPERATOR)
1468 		n = find_sexp_list(n);
1469 
1470 	Assert( (n >= 0) && (n < Num_sexp_nodes) );
1471 
1472 	while (Sexp_nodes[n].subtype != SEXP_ATOM_OPERATOR)
1473 	{
1474 		for (i = 0; i < Num_sexp_nodes; i++)
1475 		{
1476 			if (Sexp_nodes[i].rest == n)
1477 				break;
1478 		}
1479 
1480 		if (i == Num_sexp_nodes)
1481 			return -1;  // not found, probably at top node already.
1482 
1483 		n = i;
1484 	}
1485 
1486 	return n;
1487 }
1488 
1489 /**
1490  * Determine if an sexpression node is the top level node of an sexpression tree.
1491  *
1492  * Top level nodes do not have their node id in anyone elses first or rest index.
1493  */
is_sexp_top_level(int node)1494 int is_sexp_top_level( int node )
1495 {
1496 	int i;
1497 
1498 	Assert((node >= 0) && (node < Num_sexp_nodes));
1499 
1500 	if (Sexp_nodes[node].type == SEXP_NOT_USED)
1501 		return 0;
1502 
1503 	for (i = 0; i < Num_sexp_nodes; i++)
1504 	{
1505 		if ((Sexp_nodes[i].type == SEXP_NOT_USED) || (i == node ))				// don't check myself or unused nodes
1506 			continue;
1507 
1508 		if ((Sexp_nodes[i].first == node) || (Sexp_nodes[i].rest == node))
1509 			return 0;
1510 	}
1511 
1512 	return 1;
1513 }
1514 
1515 /**
1516  * Find argument number
1517  */
find_argnum(int parent,int arg)1518 int find_argnum(int parent, int arg)
1519 {
1520 	int n, tally;
1521 
1522 	n = CDR(parent);
1523 	tally = 0;
1524 
1525 	while (CAR(n) != arg)
1526 	{
1527 		if (n == -1)
1528 			return -1;
1529 
1530 		tally++;
1531 		n = CDR(n);
1532 	}
1533 
1534 	return tally;
1535 }
1536 
1537 /**
1538  * From an operator name, return its index in the array Operators
1539  */
get_operator_index(const char * token)1540 int get_operator_index(const char *token)
1541 {
1542 	int	i;
1543 
1544 	for (i=0; i<Num_operators; i++){
1545 		if (!stricmp(token, Operators[i].text)){
1546 			return i;
1547 		}
1548 	}
1549 
1550 	return NOT_A_SEXP_OPERATOR;
1551 }
1552 
1553 /**
1554  * From a sexp node, return the index in the array Operators or 0 if not an operator
1555  */
get_operator_index(int node)1556 int get_operator_index(int node)
1557 {
1558 	if (!Fred_running && (Sexp_nodes[node].op_index != NO_OPERATOR_INDEX_DEFINED) ) {
1559 		return Sexp_nodes[node].op_index;
1560 	}
1561 
1562 	int index = get_operator_index(Sexp_nodes[node].text);
1563 	Sexp_nodes[node].op_index = index;
1564 	return index;
1565 }
1566 
1567 
1568 /**
1569  * From an operator name, return its constant (the number it was define'd with)
1570  */
get_operator_const(const char * token)1571 int get_operator_const(const char *token)
1572 {
1573 	int	idx = get_operator_index(token);
1574 
1575 	if (idx == NOT_A_SEXP_OPERATOR)
1576 		return 0;
1577 
1578 	return Operators[idx].value;
1579 }
1580 
get_operator_const(int node)1581 int get_operator_const(int node)
1582 {
1583 	if (!Fred_running && Sexp_nodes[node].op_index >= 0) {
1584 		return Operators[Sexp_nodes[node].op_index].value;
1585 	}
1586 
1587 	int	idx = get_operator_index(node);
1588 
1589 	if (idx == NOT_A_SEXP_OPERATOR)
1590 		return 0;
1591 
1592 	return Operators[idx].value;
1593 }
1594 
query_sexp_args_count(int node,bool only_valid_args=false)1595 int query_sexp_args_count(int node, bool only_valid_args = false)
1596 {
1597 	int count = 0;
1598 	int n = CDR(node);
1599 
1600 	for ( ; n != -1; n = CDR(n))
1601 	{
1602 		if (only_valid_args && !(Sexp_nodes[n].flags & SNF_ARGUMENT_VALID))
1603 			continue;
1604 
1605 		count++;
1606 	}
1607 
1608 	return count;
1609 }
1610 
1611 /**
1612  * Needed to fix bug with sexps like send-message list which have arguments that need to be supplied as a block
1613  *
1614  * @return 0 if the number of arguments for the supplied operation is wrong, 1 otherwise.
1615  */
check_operator_argument_count(int count,int op)1616 int check_operator_argument_count(int count, int op)
1617 {
1618 	if (count < Operators[op].min || count > Operators[op].max)
1619 		return 0;
1620 
1621 	//send-message-list has arguments as blocks of 4
1622 	if (op == OP_SEND_MESSAGE_LIST)
1623 		if (count % 4 != 0)
1624 			return 0;
1625 
1626 	return 1;
1627 }
1628 
1629 /**
1630  * Check SEXP syntax
1631  * @return 0 if ok, negative if there's an error in expression..
1632  * See the returns types in sexp.h
1633  */
check_sexp_syntax(int node,int return_type,int recursive,int * bad_node,int mode)1634 int check_sexp_syntax(int node, int return_type, int recursive, int *bad_node, int mode)
1635 {
1636 	int i = 0, z, t, type, argnum = 0, count, op, type2 = 0, op2;
1637 	int op_node;
1638 	int var_index = -1;
1639 	size_t st;
1640 
1641 	Assert(node >= 0 && node < Num_sexp_nodes);
1642 	Assert(Sexp_nodes[node].type != SEXP_NOT_USED);
1643 	if (Sexp_nodes[node].subtype == SEXP_ATOM_NUMBER && return_type == OPR_BOOL) {
1644 		// special case Mark seems to want supported
1645 		Assert(Sexp_nodes[node].first == -1);  // only lists should have a first pointer
1646 		if (Sexp_nodes[node].rest != -1)  // anything after the number?
1647 			return SEXP_CHECK_NONOP_ARGS; // if so, it's a syntax error
1648 
1649 		return 0;
1650 	}
1651 
1652 	op_node = node;		// save the node of the operator since we need to get to other args.
1653 	if (bad_node)
1654 		*bad_node = op_node;
1655 
1656 	if (Sexp_nodes[op_node].subtype != SEXP_ATOM_OPERATOR)
1657 		return SEXP_CHECK_OP_EXPECTED;  // not an operator, which it should always be
1658 
1659 	op = get_operator_index(CTEXT(op_node));
1660 	if (op == -1)
1661 		return SEXP_CHECK_UNKNOWN_OP;  // unrecognized operator
1662 
1663 	// check that types match - except that OPR_AMBIGUOUS matches everything
1664 	if (return_type != OPR_AMBIGUOUS)
1665 	{
1666 		// get the return type of the next thing
1667 		z = query_operator_return_type(op);
1668 		if (z == OPR_POSITIVE && return_type == OPR_NUMBER)
1669 		{
1670 			// positive data type can map to number data type just fine
1671 		}
1672 		// Goober5000's number hack
1673 		else if (z == OPR_NUMBER && return_type == OPR_POSITIVE)
1674 		{
1675 			// this isn't kosher, but we hack it to make it work
1676 		}
1677 		else if (z != return_type)
1678 		{
1679 			// anything else is a mismatch
1680 			return SEXP_CHECK_TYPE_MISMATCH;
1681 		}
1682 	}
1683 
1684 	count = query_sexp_args_count(op_node);
1685 
1686 	if (!check_operator_argument_count(count, op))
1687 		return SEXP_CHECK_BAD_ARG_COUNT;  // incorrect number of arguments
1688 
1689 	// Goober5000 - if this is a list of stuff that has the special argument as
1690 	// an item in the list, assume it's valid
1691 	if (special_argument_appears_in_sexp_list(op_node))
1692 		return 0;
1693 
1694 	node = Sexp_nodes[op_node].rest;
1695 	while (node != -1) {
1696 		type = query_operator_argument_type(op, argnum);
1697 		Assert(Sexp_nodes[node].type != SEXP_NOT_USED);
1698 		if (bad_node)
1699 			*bad_node = node;
1700 
1701 		if (Sexp_nodes[node].subtype == SEXP_ATOM_LIST) {
1702 			i = Sexp_nodes[node].first;
1703 			if (bad_node)
1704 				*bad_node = i;
1705 
1706 			// be sure to check to see if this node is a list of stuff and not an actual operator type
1707 			// thing.  (i.e. in the case of a cond statement, the conditional will fall into this if
1708 			// statement.  MORE TO DO HERE!!!!
1709 			if (Sexp_nodes[i].subtype == SEXP_ATOM_LIST)
1710 				return 0;
1711 
1712 			op2 = get_operator_index(CTEXT(i));
1713 			if (op2 == -1)
1714 				return SEXP_CHECK_UNKNOWN_OP;
1715 
1716 			type2 = query_operator_return_type(op2);
1717 			if (recursive) {
1718 				switch (type) {
1719 					case OPF_NUMBER:
1720 						t = OPR_NUMBER;
1721 						break;
1722 
1723 					case OPF_POSITIVE:
1724 						t = OPR_POSITIVE;
1725 						break;
1726 
1727 					case OPF_BOOL:
1728 						t = OPR_BOOL;
1729 						break;
1730 
1731 					case OPF_NULL:
1732 						t = OPR_NULL;
1733 						break;
1734 
1735 					// Goober5000
1736 					case OPF_FLEXIBLE_ARGUMENT:
1737 						t = OPR_FLEXIBLE_ARGUMENT;
1738 						break;
1739 
1740 					case OPF_AI_GOAL:
1741 						t = OPR_AI_GOAL;
1742 						break;
1743 
1744 					// special case for modify-variable
1745 					case OPF_AMBIGUOUS:
1746 						t = OPR_AMBIGUOUS;
1747 						break;
1748 
1749 					default:
1750 						return SEXP_CHECK_UNKNOWN_TYPE;  // no other return types available
1751 				}
1752 
1753 				if ((z = check_sexp_syntax(i, t, recursive, bad_node)) != 0) {
1754 					return z;
1755 				}
1756 			}
1757 
1758 		} else if (Sexp_nodes[node].subtype == SEXP_ATOM_NUMBER) {
1759 			char *ptr;
1760 
1761 			type2 = OPR_POSITIVE;
1762 			ptr = CTEXT(node);
1763 			if (*ptr == '-') {
1764 				type2 = OPR_NUMBER;
1765 				ptr++;
1766 			}
1767 
1768 			if (type == OPF_BOOL)  // allow numbers to be used where boolean is required.
1769 				type2 = OPR_BOOL;
1770 
1771 			while (*ptr) {
1772 				if (!isdigit(*ptr))
1773 					return SEXP_CHECK_INVALID_NUM;  // not a valid number
1774 
1775 				ptr++;
1776 			}
1777 
1778 			i = atoi(CTEXT(node));
1779 			z = get_operator_const(CTEXT(op_node));
1780 			if ( (z == OP_HAS_DOCKED_DELAY) || (z == OP_HAS_UNDOCKED_DELAY) )
1781 				if ( (argnum == 2) && (i < 1) )
1782 					return SEXP_CHECK_NUM_RANGE_INVALID;
1783 
1784 			// valid color range 0 to 255 - FUBAR
1785 			if ((z == OP_CHANGE_IFF_COLOR)  && ((argnum >= 2) && (argnum <= 4)))
1786 			{
1787 				if ( i < 0 || i > 255)
1788 				{
1789 					return SEXP_CHECK_NUM_RANGE_INVALID;
1790 				}
1791 			}
1792 
1793 			z = get_operator_index(CTEXT(op_node));
1794 			if ( (query_operator_return_type(z) == OPR_AI_GOAL) && (argnum == Operators[op].min - 1) )
1795 				if ( (i < 0) || (i > 200) )
1796 					return SEXP_CHECK_NUM_RANGE_INVALID;
1797 
1798 		} else if (Sexp_nodes[node].subtype == SEXP_ATOM_STRING) {
1799 			type2 = SEXP_ATOM_STRING;
1800 
1801 		} else {
1802 			Assert(0);
1803 		}
1804 
1805 		// variables should only be typechecked.
1806 		if ((Sexp_nodes[node].type & SEXP_FLAG_VARIABLE) && (type != OPF_VARIABLE_NAME)) {
1807 			var_index = get_index_sexp_variable_from_node(node);
1808 			Assert(var_index != -1);
1809 
1810 			switch (type) {
1811 				case OPF_NUMBER:
1812 				case OPF_POSITIVE:
1813 					if (!(Sexp_variables[var_index].type & SEXP_VARIABLE_NUMBER))
1814 						return SEXP_CHECK_INVALID_VARIABLE_TYPE;
1815 				break;
1816 
1817                 case OPF_AMBIGUOUS:
1818                     break;
1819 
1820 				default:
1821 					if (!(Sexp_variables[var_index].type & SEXP_VARIABLE_STRING))
1822 						return SEXP_CHECK_INVALID_VARIABLE_TYPE;
1823 			}
1824 			node = Sexp_nodes[node].rest;
1825 			argnum++;
1826 			continue;
1827 		}
1828 
1829 		switch (type) {
1830 			case OPF_NAV_POINT:
1831 				if (type2 != SEXP_ATOM_STRING){
1832 					return SEXP_CHECK_TYPE_MISMATCH;
1833 				}
1834 				break;
1835 
1836 			case OPF_NUMBER:
1837 				if ((type2 != OPR_NUMBER) && (type2 != OPR_POSITIVE)){
1838 					return SEXP_CHECK_TYPE_MISMATCH;
1839 				}
1840 
1841 				break;
1842 
1843 			case OPF_POSITIVE:
1844 				if (type2 == OPR_NUMBER){
1845 					// Goober5000's number hack
1846 					break;
1847 					// return SEXP_CHECK_NEGATIVE_NUM;
1848 				}
1849 
1850 				if (type2 != OPR_POSITIVE){
1851 					return SEXP_CHECK_TYPE_MISMATCH;
1852 				}
1853 
1854 				break;
1855 
1856 			case OPF_SHIP_NOT_PLAYER:
1857 				if (type2 != SEXP_ATOM_STRING){
1858 					return SEXP_CHECK_TYPE_MISMATCH;
1859 				}
1860 
1861 				if (ship_name_lookup(CTEXT(node), 0) < 0)
1862 				{
1863 					if (Fred_running || !mission_parse_get_arrival_ship(CTEXT(node)))
1864 					{
1865 						return SEXP_CHECK_INVALID_SHIP;
1866 					}
1867 				}
1868 
1869 				break;
1870 
1871 			case OPF_SHIP_OR_NONE:
1872 				if (type2 != SEXP_ATOM_STRING)
1873 				{
1874 					return SEXP_CHECK_TYPE_MISMATCH;
1875 				}
1876 
1877 				if (stricmp(CTEXT(node), SEXP_NONE_STRING))		// none is okay
1878 				{
1879 					if (ship_name_lookup(CTEXT(node), 1) < 0)
1880 					{
1881 						if (Fred_running || !mission_parse_get_arrival_ship(CTEXT(node)))
1882 						{
1883 							return SEXP_CHECK_INVALID_SHIP;
1884 						}
1885 					}
1886 				}
1887 
1888 				break;
1889 
1890 			case OPF_SHIP:
1891 			case OPF_SHIP_POINT:
1892 				if (type2 != SEXP_ATOM_STRING){
1893 					return SEXP_CHECK_TYPE_MISMATCH;
1894 				}
1895 
1896 				if (ship_name_lookup(CTEXT(node), 1) < 0) {
1897 					if (Fred_running || !mission_parse_get_arrival_ship(CTEXT(node)))
1898 					{
1899 						if (type == OPF_SHIP)
1900 						{													// return invalid ship if not also looking for point
1901 							return SEXP_CHECK_INVALID_SHIP;
1902 						}
1903 
1904 						if (find_matching_waypoint(CTEXT(node)) == NULL)
1905 						{
1906 							if (verify_vector(CTEXT(node)))					// verify return non-zero on invalid point
1907 							{
1908 								return SEXP_CHECK_INVALID_POINT;
1909 							}
1910 						}
1911 					}
1912 				}
1913 
1914 				break;
1915 
1916 			case OPF_WING:
1917 				if (type2 != SEXP_ATOM_STRING){
1918 					return SEXP_CHECK_TYPE_MISMATCH;
1919 				}
1920 
1921 				if (wing_name_lookup(CTEXT(node), 1) < 0){
1922 					return SEXP_CHECK_INVALID_WING;
1923 				}
1924 
1925 				break;
1926 
1927 			case OPF_SHIP_WING:
1928 			case OPF_SHIP_WING_WHOLETEAM:
1929 			case OPF_SHIP_WING_SHIPONTEAM_POINT:
1930 			case OPF_SHIP_WING_POINT:
1931 			case OPF_SHIP_WING_POINT_OR_NONE:
1932 			case OPF_ORDER_RECIPIENT:
1933 				if ( type2 != SEXP_ATOM_STRING ){
1934 					return SEXP_CHECK_TYPE_MISMATCH;
1935 				}
1936 
1937 				if (type == OPF_ORDER_RECIPIENT) {
1938 					if (!strcmp ("<all fighters>", CTEXT(node))) {
1939 						break;
1940 					}
1941 				}
1942 
1943 				// all of these have ships and wings in common
1944 				if (ship_name_lookup(CTEXT(node), 1) >= 0 || wing_name_lookup(CTEXT(node), 1) >= 0) {
1945 					break;
1946 				}
1947 				// also check arrival list if we're running the game
1948 				if (!Fred_running && mission_parse_get_arrival_ship(CTEXT(node))) {
1949 					break;
1950 				}
1951 
1952 				// none is okay for _OR_NONE
1953 				if (type == OPF_SHIP_WING_POINT_OR_NONE && !stricmp(CTEXT(node), SEXP_NONE_STRING))	{
1954 					break;
1955 				}
1956 
1957 				// two different ways of checking teams
1958 				if ((type == OPF_SHIP_WING_WHOLETEAM) && iff_lookup(CTEXT(node)) >= 0) {
1959 					break;
1960 				}
1961 				if ((type == OPF_SHIP_WING_SHIPONTEAM_POINT) && sexp_determine_team(CTEXT(node)) >= 0)	{
1962 					break;
1963 				}
1964 
1965 				// only other possibility is waypoints
1966 				if (type == OPF_SHIP_WING_SHIPONTEAM_POINT || type == OPF_SHIP_WING_POINT || type == OPF_SHIP_WING_POINT_OR_NONE) {
1967 					if (find_matching_waypoint(CTEXT(node)) == NULL){
1968 						if (verify_vector(CTEXT(node))){  // non-zero on verify vector mean invalid!
1969 							return SEXP_CHECK_INVALID_POINT;
1970 						}
1971 					}
1972 					break;
1973 				}
1974 
1975 				// nothing left
1976 				return SEXP_CHECK_INVALID_SHIP_WING;
1977 
1978 			case OPF_AWACS_SUBSYSTEM:
1979 			case OPF_ROTATING_SUBSYSTEM:
1980 			case OPF_SUBSYSTEM:
1981 			case OPF_SUBSYSTEM_OR_NONE:
1982 			case OPF_SUBSYS_OR_GENERIC:
1983 			{
1984 				char *shipname;
1985 				int shipnum,ship_class;
1986 				int ship_index;
1987 
1988 				if (type2 != SEXP_ATOM_STRING){
1989 					return SEXP_CHECK_TYPE_MISMATCH;
1990 				}
1991 
1992 				// none is okay for subsys_or_none
1993 				if (type == OPF_SUBSYSTEM_OR_NONE && !stricmp(CTEXT(node), SEXP_NONE_STRING))
1994 				{
1995 					break;
1996 				}
1997 
1998 				//  subsys_or_generic has a few extra options it accepts
1999 				if (type == OPF_SUBSYS_OR_GENERIC && (!(stricmp(CTEXT(node), SEXP_ALL_ENGINES_STRING)) || !(stricmp(CTEXT(node), SEXP_ALL_TURRETS_STRING)) )) {
2000 					break;
2001 				}
2002 
2003 				// we must get the model of the ship that is part of this sexpression and find a subsystem
2004 				// with that name.  This code assumes by default that the ship is *always* the first name
2005 				// in the sexpression.  If this is ever not the case, the code here must be changed to
2006 				// get the correct ship name.
2007 				switch(get_operator_const(CTEXT(op_node)))
2008 				{
2009 					case OP_CAP_SUBSYS_CARGO_KNOWN_DELAY:
2010 					case OP_DISTANCE_SUBSYSTEM:
2011 					case OP_SET_CARGO:
2012 					case OP_IS_CARGO:
2013 					case OP_CHANGE_AI_CLASS:
2014 					case OP_IS_AI_CLASS:
2015 					case OP_MISSILE_LOCKED:
2016 					case OP_SHIP_SUBSYS_GUARDIAN_THRESHOLD:
2017 						ship_index = CDR(CDR(op_node));
2018 						break;
2019 
2020 					case OP_BEAM_FIRE:
2021 						if(argnum == 1){
2022 							ship_index = CDR(op_node);
2023 						} else {
2024 							ship_index = CDR(CDR(CDR(op_node)));
2025 						}
2026 						break;
2027 
2028 					case OP_QUERY_ORDERS:
2029 						ship_index = CDR(CDR(CDR(CDR(op_node))));
2030 						break;
2031 
2032 					case OP_WEAPON_CREATE:
2033 						ship_index = CDDDDDR(CDDDDR(op_node));
2034 						break;
2035 
2036 					default :
2037 						ship_index = CDR(op_node);
2038 						break;
2039 				}
2040 
2041 				shipname = CTEXT(ship_index);
2042 				shipnum = ship_name_lookup(shipname, 1);
2043 				if (shipnum >= 0)
2044 				{
2045 					ship_class = Ships[shipnum].ship_info_index;
2046 				}
2047 				else
2048 				{
2049 					// must try to find the ship in the arrival list
2050 					p_object *p_objp = mission_parse_get_arrival_ship(shipname);
2051 
2052 					if (!p_objp)
2053 					{
2054 						if (type == OPF_SUBSYSTEM_OR_NONE)
2055 							break;
2056 						else
2057 							return SEXP_CHECK_INVALID_SHIP;
2058 					}
2059 
2060 					ship_class = p_objp->ship_class;
2061 				}
2062 
2063 				// check for the special "hull" value
2064 				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)) {
2065 					if ( !stricmp( CTEXT(node), SEXP_HULL_STRING) || !stricmp( CTEXT(node), SEXP_SIM_HULL_STRING) ){
2066 						break;
2067 					}
2068 				}
2069 				// check for special "shields" value for armor types
2070 				if (Operators[op].value == OP_SET_ARMOR_TYPE) {
2071 					if ( !stricmp( CTEXT(node), SEXP_SHIELD_STRING) || !stricmp( CTEXT(node), SEXP_SIM_HULL_STRING) ){
2072 						break;
2073 					}
2074 				}
2075 
2076 				for (i=0; i<Ship_info[ship_class].n_subsystems; i++)
2077 				{
2078 					if (!subsystem_stricmp(Ship_info[ship_class].subsystems[i].subobj_name, CTEXT(node)))
2079 					{
2080 						break;
2081 					}
2082 				}
2083 
2084 				if (i == Ship_info[ship_class].n_subsystems)
2085 				{
2086 					return SEXP_CHECK_INVALID_SUBSYS;
2087 				}
2088 
2089 				if(Fred_running)
2090 				{
2091 					// if we're checking for an AWACS subsystem and this is not an awacs subsystem
2092 					if((type == OPF_AWACS_SUBSYSTEM) && !(Ship_info[ship_class].subsystems[i].flags & MSS_FLAG_AWACS))
2093 					{
2094 						return SEXP_CHECK_INVALID_SUBSYS;
2095 					}
2096 
2097 					// rotating subsystem, like above - Goober5000
2098 					if ((type == OPF_ROTATING_SUBSYSTEM) && !(Ship_info[ship_class].subsystems[i].flags & MSS_FLAG_ROTATES))
2099 					{
2100 						return SEXP_CHECK_INVALID_SUBSYS;
2101 					}
2102 				}
2103 
2104 				break;
2105 			}
2106 
2107 			case OPF_SUBSYSTEM_TYPE:
2108 				for (i = 0; i < SUBSYSTEM_MAX; i++)
2109 				{
2110 					if (!stricmp(CTEXT(node), Subsystem_types[i]))
2111 						break;
2112 				}
2113 
2114 				if (i == SUBSYSTEM_MAX)
2115 					return SEXP_CHECK_INVALID_SUBSYS_TYPE;
2116 
2117 				break;
2118 
2119 			case OPF_POINT:
2120 				if (type2 != SEXP_ATOM_STRING)
2121 				{
2122 					return SEXP_CHECK_TYPE_MISMATCH;
2123 				}
2124 
2125 				if (find_matching_waypoint(CTEXT(node)) == NULL)
2126 				{
2127 					if (verify_vector(CTEXT(node)))
2128 					{
2129 						return SEXP_CHECK_INVALID_POINT;
2130 					}
2131 				}
2132 
2133 				break;
2134 
2135 			case OPF_IFF:
2136 				if (type2 != SEXP_ATOM_STRING)
2137 				{
2138 					return SEXP_CHECK_TYPE_MISMATCH;
2139 				}
2140 
2141 				if (iff_lookup(CTEXT(node)) < 0)
2142 				{
2143 					return SEXP_CHECK_INVALID_IFF;
2144 				}
2145 
2146 				break;
2147 
2148 			case OPF_AI_CLASS:
2149 				if (type2 != SEXP_ATOM_STRING)
2150 				{
2151 					return SEXP_CHECK_TYPE_MISMATCH;
2152 				}
2153 
2154 				for (i=0; i<Num_ai_classes; i++)
2155 				{
2156 					if (!stricmp(Ai_class_names[i], CTEXT(node)))
2157 					{
2158 						break;
2159 					}
2160 				}
2161 
2162 				if (i == Num_ai_classes)
2163 				{
2164 					return SEXP_CHECK_INVALID_AI_CLASS;
2165 				}
2166 
2167 				break;
2168 
2169 			case OPF_ARRIVAL_LOCATION:
2170 				if (type2 != SEXP_ATOM_STRING)
2171 				{
2172 					return SEXP_CHECK_TYPE_MISMATCH;
2173 				}
2174 
2175 				for (i=0; i<MAX_ARRIVAL_NAMES; i++)
2176 				{
2177 					if (!stricmp(Arrival_location_names[i], CTEXT(node)))
2178 					{
2179 						break;
2180 					}
2181 				}
2182 
2183 				if (i == MAX_ARRIVAL_NAMES)
2184 				{
2185 					return SEXP_CHECK_INVALID_ARRIVAL_LOCATION;
2186 				}
2187 
2188 				break;
2189 
2190 			case OPF_DEPARTURE_LOCATION:
2191 				if (type2 != SEXP_ATOM_STRING)
2192 				{
2193 					return SEXP_CHECK_TYPE_MISMATCH;
2194 				}
2195 
2196 				for (i=0; i<MAX_DEPARTURE_NAMES; i++)
2197 				{
2198 					if (!stricmp(Departure_location_names[i], CTEXT(node)))
2199 					{
2200 						break;
2201 					}
2202 				}
2203 
2204 				if (i == MAX_DEPARTURE_NAMES)
2205 				{
2206 					return SEXP_CHECK_INVALID_DEPARTURE_LOCATION;
2207 				}
2208 
2209 				break;
2210 
2211 			case OPF_ARRIVAL_ANCHOR_ALL:
2212 				if (type2 != SEXP_ATOM_STRING)
2213 				{
2214 					return SEXP_CHECK_TYPE_MISMATCH;
2215 				}
2216 				else
2217 				{
2218 					int get_special_anchor(char *name);
2219 					int valid = 0;
2220 
2221 					// <any friendly>, etc.
2222 					if (get_special_anchor(CTEXT(node)) >= 0)
2223 					{
2224 						valid = 1;
2225 					}
2226 
2227 					if (ship_name_lookup(CTEXT(node), 1) >= 0)
2228 					{
2229 						valid = 1;
2230 					}
2231 
2232 					if (!Fred_running && mission_parse_get_arrival_ship(CTEXT(node)))
2233 					{
2234 						valid = 1;
2235 					}
2236 
2237 					if (!valid)
2238 					{
2239 						return SEXP_CHECK_INVALID_ARRIVAL_ANCHOR_ALL;
2240 					}
2241 				}
2242 
2243 				break;
2244 
2245 			case OPF_SOUNDTRACK_NAME:
2246 				if (type2 != SEXP_ATOM_STRING){
2247 					return SEXP_CHECK_TYPE_MISMATCH;
2248 				}
2249 
2250 				if (!stricmp(CTEXT(node), "<No Music>"))
2251 					break;
2252 
2253 				if (Cmdline_freespace_no_music)
2254 					break;
2255 
2256 				for (i=0; i<Num_soundtracks; i++)
2257 				{
2258 					if (!stricmp(CTEXT(node), Soundtracks[i].name))
2259 					{
2260 						break;
2261 					}
2262 				}
2263 
2264 				if (i == Num_soundtracks)
2265 					return SEXP_CHECK_INVALID_SOUNDTRACK_NAME;
2266 
2267 				break;
2268 
2269 			case OPF_SHIP_WITH_BAY:
2270 			{
2271 				char *name = CTEXT(node);
2272 				p_object *p_objp;
2273 				int shipnum = -1;
2274 
2275 				if (type2 != SEXP_ATOM_STRING)
2276 					return SEXP_CHECK_TYPE_MISMATCH;
2277 
2278 				if (!stricmp(name, "<no anchor>"))
2279 					break;
2280 
2281 				shipnum = ship_name_lookup(name, 1);
2282 				if (shipnum < 0)
2283 				{
2284 					if (Fred_running)
2285 						return SEXP_CHECK_INVALID_SHIP;
2286 
2287 					p_objp = mission_parse_get_arrival_ship(name);
2288 					if (p_objp == NULL)
2289 						return SEXP_CHECK_INVALID_SHIP;
2290 
2291 					// Goober5000 - since we can't check POFs for ships which have yet to arrive
2292 					// (not without a bit of work anyway), just assume they're okay
2293 					break;
2294 				}
2295 
2296 				// ship exists at this point
2297 
2298 				// now determine if this ship has a docking bay
2299 				if (!ship_has_dock_bay(shipnum))
2300 					return SEXP_CHECK_INVALID_SHIP_WITH_BAY;
2301 
2302 				break;
2303 			}
2304 
2305 			case OPF_SUPPORT_SHIP_CLASS:
2306 				if (type2 != SEXP_ATOM_STRING){
2307 					return SEXP_CHECK_TYPE_MISMATCH;
2308 				}
2309 
2310 				if (!stricmp(CTEXT(node), "<species support ship class>"))
2311 					break;
2312 
2313 				if (!stricmp(CTEXT(node), "<any support ship class>"))
2314 					break;
2315 
2316 				for (i = 0; i < Num_ship_classes; i++ ) {
2317 					if ( !stricmp(CTEXT(node), Ship_info[i].name) )
2318 					{
2319 						if (Ship_info[i].flags & SIF_SUPPORT)
2320 						{
2321 							break;
2322 						}
2323 					}
2324 				}
2325 
2326 				if ( i == Num_ship_classes )
2327 					return SEXP_CHECK_INVALID_SUPPORT_SHIP_CLASS;
2328 
2329 				break;
2330 
2331 			case OPF_BOOL:
2332 				if (type2 != OPR_BOOL){
2333 					return SEXP_CHECK_TYPE_MISMATCH;
2334 				}
2335 
2336 				break;
2337 
2338 			case OPF_AI_ORDER:
2339 				if ( type2 != SEXP_ATOM_STRING ){
2340 					return SEXP_CHECK_TYPE_MISMATCH;
2341 				}
2342 
2343 				break;
2344 
2345 			case OPF_NULL:
2346 				if (type2 != OPR_NULL){
2347 					return SEXP_CHECK_TYPE_MISMATCH;
2348 				}
2349 
2350 				break;
2351 
2352 			// Goober5000
2353 			case OPF_FLEXIBLE_ARGUMENT:
2354 				if (type2 != OPR_FLEXIBLE_ARGUMENT) {
2355 					return SEXP_CHECK_TYPE_MISMATCH;
2356 				}
2357 				break;
2358 
2359 			// Goober5000
2360 			case OPF_ANYTHING:
2361 				break;
2362 
2363 			case OPF_AI_GOAL:
2364 				if (type2 != OPR_AI_GOAL){
2365 					return SEXP_CHECK_TYPE_MISMATCH;
2366 				}
2367 
2368 				if (Fred_running) {
2369 					int ship_num, ship2, w = 0;
2370 
2371 					// if it's the "goals" operator, this is part of initial orders, so just assume it's okay
2372 					if (get_operator_const(Sexp_nodes[op_node].text) == OP_GOALS_ID) {
2373 						break;
2374 					}
2375 
2376 					ship_num = ship_name_lookup(CTEXT(Sexp_nodes[op_node].rest), 1);	// Goober5000 - include players
2377 					if (ship_num < 0) {
2378 						w = wing_name_lookup(CTEXT(Sexp_nodes[op_node].rest));
2379 						if (w < 0) {
2380 							if (bad_node){
2381 								*bad_node = Sexp_nodes[op_node].rest;
2382 							}
2383 
2384 							return SEXP_CHECK_INVALID_SHIP;  // should have already been caught earlier, but just in case..
2385 						}
2386 					}
2387 
2388 					Assert(Sexp_nodes[node].subtype == SEXP_ATOM_LIST);
2389 					z = Sexp_nodes[node].first;
2390 					Assert(Sexp_nodes[z].subtype != SEXP_ATOM_LIST);
2391 					z = get_operator_const(CTEXT(z));
2392 					if (ship_num >= 0) {
2393 						if (!query_sexp_ai_goal_valid(z, ship_num)){
2394 							return SEXP_CHECK_ORDER_NOT_ALLOWED;
2395 						}
2396 
2397 					} else {
2398 						for (i=0; i<Wings[w].wave_count; i++){
2399 							if (!query_sexp_ai_goal_valid(z, Wings[w].ship_index[i])){
2400 								return SEXP_CHECK_ORDER_NOT_ALLOWED;
2401 							}
2402 						}
2403 					}
2404 
2405 					if ((z == OP_AI_DOCK) && (Sexp_nodes[node].rest >= 0)) {
2406 						ship2 = ship_name_lookup(CTEXT(Sexp_nodes[node].rest), 1);	// Goober5000 - include players
2407 						if ((ship_num < 0) || !ship_docking_valid(ship_num, ship2)){
2408 							return SEXP_CHECK_DOCKING_NOT_ALLOWED;
2409 						}
2410 					}
2411 				}
2412 
2413 				// we should check the syntax of the actual goal!!!!
2414 				z = Sexp_nodes[node].first;
2415 				if ((z = check_sexp_syntax(z, OPR_AI_GOAL, recursive, bad_node)) != 0){
2416 					return z;
2417 				}
2418 
2419 				break;
2420 
2421 			case OPF_SHIP_TYPE:
2422 				if (type2 != SEXP_ATOM_STRING){
2423 					return SEXP_CHECK_TYPE_MISMATCH;
2424 				}
2425 
2426 				i = ship_type_name_lookup(CTEXT(node));
2427 
2428 				if (i < 0){
2429 					return SEXP_CHECK_INVALID_SHIP_TYPE;
2430 				}
2431 
2432 				break;
2433 
2434 			case OPF_WAYPOINT_PATH:
2435 				if (find_matching_waypoint_list(CTEXT(node)) == NULL) {
2436 					return SEXP_CHECK_TYPE_MISMATCH;
2437 				}
2438 				break;
2439 
2440 			case OPF_MESSAGE:
2441 				if (type2 != SEXP_ATOM_STRING)
2442 					return SEXP_CHECK_TYPE_MISMATCH;
2443 
2444 				if (Fred_running) {
2445 					for (i=0; i<Num_messages; i++)
2446 						if (!stricmp(Messages[i].name, CTEXT(node)))
2447 							break;
2448 
2449 					if (i == Num_messages)
2450 						return SEXP_CHECK_UNKNOWN_MESSAGE;
2451 				}
2452 
2453 				break;
2454 
2455 			case OPF_PRIORITY: {
2456 				if (type2 != SEXP_ATOM_STRING)
2457 					return SEXP_CHECK_TYPE_MISMATCH;
2458 
2459 				if (Fred_running) {  // should still check in Fred though..
2460 					char *name;
2461 
2462 					name = CTEXT(node);
2463 					if (!stricmp(name, "low") || !stricmp(name, "normal") || !stricmp(name, "high"))
2464 						break;
2465 
2466 					return SEXP_CHECK_INVALID_PRIORITY;
2467 				}
2468 
2469 				break;
2470 			}
2471 
2472 			case OPF_MISSION_NAME:
2473 				if (type2 != SEXP_ATOM_STRING)
2474 					return SEXP_CHECK_TYPE_MISMATCH;
2475 
2476 				if (Fred_running) {
2477 					if (mode == SEXP_MODE_CAMPAIGN) {
2478 						for (i=0; i<Campaign.num_missions; i++)
2479 							if (!stricmp(CTEXT(node), Campaign.missions[i].name)) {
2480 								if ((i != Sexp_useful_number) && (Campaign.missions[i].level >= Campaign.missions[Sexp_useful_number].level))
2481 									return SEXP_CHECK_INVALID_LEVEL;
2482 
2483 								break;
2484 							}
2485 
2486 						if (i == Campaign.num_missions)
2487 							return SEXP_CHECK_INVALID_MISSION_NAME;
2488 
2489 					} else {
2490 						// mwa -- put the following if statement to prevent Fred errors for possibly valid
2491 						// conditions.  We should do something else here!!!
2492 						if ( (Operators[op].value == OP_PREVIOUS_EVENT_TRUE) || (Operators[op].value == OP_PREVIOUS_EVENT_FALSE) || (Operators[op].value == OP_PREVIOUS_EVENT_INCOMPLETE)
2493 							|| (Operators[op].value == OP_PREVIOUS_GOAL_TRUE) || (Operators[op].value == OP_PREVIOUS_GOAL_FALSE) || (Operators[op].value == OP_PREVIOUS_GOAL_INCOMPLETE) )
2494 							break;
2495 
2496 						if (!(*Mission_filename) || stricmp(Mission_filename, CTEXT(node)))
2497 							return SEXP_CHECK_INVALID_MISSION_NAME;
2498 					}
2499 				}
2500 
2501 				break;
2502 
2503 			case OPF_GOAL_NAME:
2504 				if (type2 != SEXP_ATOM_STRING)
2505 					return SEXP_CHECK_TYPE_MISMATCH;
2506 
2507 				// we only need to check the campaign list if running in Fred and are in campaign mode.
2508 				// otherwise, check the set of current goals
2509 				if ( Fred_running && (mode == SEXP_MODE_CAMPAIGN) ) {
2510 					z = find_parent_operator(node);
2511 					Assert(z >= 0);
2512 					z = Sexp_nodes[z].rest;  // first argument of operator should be mission name
2513 					Assert(z >= 0);
2514 					for (i=0; i<Campaign.num_missions; i++)
2515 						if (!stricmp(CTEXT(z), Campaign.missions[i].name))
2516 							break;
2517 
2518 					// read the goal/event list from the mission file if both num_goals and num_events
2519 					// are < 0
2520 					if ((Campaign.missions[i].num_goals <= 0) && (Campaign.missions[i].num_events <= 0) )
2521 						read_mission_goal_list(i);
2522 
2523 					if (i < Campaign.num_missions) {
2524 						for (t=0; t<Campaign.missions[i].num_goals; t++)
2525 							if (!stricmp(CTEXT(node), Campaign.missions[i].goals[t].name))
2526 								break;
2527 
2528 						if (t == Campaign.missions[i].num_goals)
2529 							return SEXP_CHECK_INVALID_GOAL_NAME;
2530 					}
2531 				} else {
2532 					// MWA -- short circuit evaluation of these things for now.
2533 					if ( (Operators[op].value == OP_PREVIOUS_GOAL_TRUE) || (Operators[op].value == OP_PREVIOUS_GOAL_FALSE) || (Operators[op].value == OP_PREVIOUS_GOAL_INCOMPLETE) )
2534 						break;
2535 
2536 					for (i=0; i<Num_goals; i++)
2537 						if (!stricmp(CTEXT(node), Mission_goals[i].name))
2538 							break;
2539 
2540 					if (i == Num_goals)
2541 						return SEXP_CHECK_INVALID_GOAL_NAME;
2542 				}
2543 
2544 				break;
2545 
2546 			case OPF_EVENT_NAME:
2547 				if ( type2 != SEXP_ATOM_STRING )
2548 					return SEXP_CHECK_TYPE_MISMATCH;
2549 
2550 				// like above checking for goals, check events in the campaign only if in Fred
2551 				// and only if in campaign mode.  Otherwise, check the current set of events
2552 				if ( Fred_running && (mode == SEXP_MODE_CAMPAIGN) ) {
2553 					z = find_parent_operator(node);
2554 					Assert(z >= 0);
2555 					z = Sexp_nodes[z].rest;  // first argument of operator should be mission name
2556 					Assert(z >= 0);
2557 					for (i=0; i<Campaign.num_missions; i++)
2558 						if (!stricmp(CTEXT(z), Campaign.missions[i].name))
2559 							break;
2560 
2561 					// read the goal/event list from the mission file if both num_goals and num_events
2562 					// are < 0
2563 					if ((Campaign.missions[i].num_goals <= 0) && (Campaign.missions[i].num_events <= 0) )
2564 						read_mission_goal_list(i);
2565 
2566 					if (i < Campaign.num_missions) {
2567 						for (t=0; t<Campaign.missions[i].num_events; t++)
2568 							if (!stricmp(CTEXT(node), Campaign.missions[i].events[t].name))
2569 								break;
2570 
2571 						if (t == Campaign.missions[i].num_events)
2572 							return SEXP_CHECK_INVALID_EVENT_NAME;
2573 					}
2574 				} else {
2575 					// MWA -- short circuit evaluation of these things for now.
2576 					if ( (Operators[op].value == OP_PREVIOUS_EVENT_TRUE) || (Operators[op].value == OP_PREVIOUS_EVENT_FALSE) || (Operators[op].value == OP_PREVIOUS_EVENT_INCOMPLETE) )
2577 						break;
2578 
2579 					for ( i = 0; i < Num_mission_events; i++ ) {
2580 						if ( !stricmp(CTEXT(node), Mission_events[i].name) )
2581 							break;
2582 					}
2583 					if ( i == Num_mission_events )
2584 						return SEXP_CHECK_INVALID_EVENT_NAME;
2585 				}
2586 				break;
2587 
2588 			case OPF_DOCKER_POINT:
2589 				if (type2 != SEXP_ATOM_STRING)
2590 					return SEXP_CHECK_TYPE_MISMATCH;
2591 
2592 				// This makes massive assumptions about the structure of the SEXP using it. If you add any
2593 				// new SEXPs that use this OPF, you will probably need to edit this section to accommodate them.
2594 				if (Fred_running) {
2595 					int ship_num, model;
2596 
2597 					// Look for the node containing the docker ship as its first argument. For set-docked, we want
2598 					// the current SEXP. Otherwise (for ai-dock), we want its parent.
2599 					if (get_operator_const(Sexp_nodes[op_node].text) == OP_SET_DOCKED) {
2600 						z = op_node;
2601 					}
2602 					else {
2603 						z = find_parent_operator(op_node);
2604 
2605 						// if it's the "goals" operator, this is part of initial orders, so just assume it's okay
2606 						if (get_operator_const(Sexp_nodes[z].text) == OP_GOALS_ID) {
2607 							break;
2608 						}
2609 					}
2610 
2611 					// look for the ship this goal is being assigned to
2612 					ship_num = ship_name_lookup(CTEXT(Sexp_nodes[z].rest), 1);
2613 					if (ship_num < 0) {
2614 						if (bad_node)
2615 							*bad_node = Sexp_nodes[z].rest;
2616 
2617 						return SEXP_CHECK_INVALID_SHIP;  // should have already been caught earlier, but just in case..
2618 					}
2619 
2620 					model = Ship_info[Ships[ship_num].ship_info_index].model_num;
2621 					z = model_get_num_dock_points(model);
2622 					for (i=0; i<z; i++)
2623 						if (!stricmp(CTEXT(node), model_get_dock_name(model, i)))
2624 							break;
2625 
2626 					if (i == z)
2627 						return SEXP_CHECK_INVALID_DOCKER_POINT;
2628 				}
2629 
2630 				break;
2631 
2632 			case OPF_DOCKEE_POINT:
2633 				if (type2 != SEXP_ATOM_STRING)
2634 					return SEXP_CHECK_TYPE_MISMATCH;
2635 
2636 				// This makes massive assumptions about the structure of the SEXP using it. If you add any
2637 				// new SEXPs that use this OPF, you will probably need to edit this section to accommodate them.
2638 				if (Fred_running) {
2639 					int ship_num, model;
2640 
2641 					// If we're using set-docked, we want to look up the ship from the third SEXP argument.
2642 					if (get_operator_const(Sexp_nodes[op_node].text) == OP_SET_DOCKED) {
2643 						//Navigate to the third argument
2644 						z = op_node;
2645 						for (i = 0; i < 3; i++)
2646 							z = Sexp_nodes[z].rest;
2647 
2648 						ship_num = ship_name_lookup(Sexp_nodes[z].text, 1);
2649 					}
2650 					else {
2651 						ship_num = ship_name_lookup(CTEXT(Sexp_nodes[op_node].rest), 1);
2652 					}
2653 
2654 					if (ship_num < 0) {
2655 						if (bad_node)
2656 							*bad_node = Sexp_nodes[op_node].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)
2668 						return SEXP_CHECK_INVALID_DOCKEE_POINT;
2669 				}
2670 
2671 				break;
2672 
2673 			case OPF_WHO_FROM:
2674 				if (type2 != SEXP_ATOM_STRING)
2675 					return SEXP_CHECK_TYPE_MISMATCH;
2676 
2677 				if (*CTEXT(node) != '#') {  // not a manual source?
2678 					if ( stricmp(CTEXT(node), "<any wingman>"))
2679 						if ( stricmp(CTEXT(node), "<none>") ) // not a special token?
2680 							if ((ship_name_lookup(CTEXT(node), TRUE) < 0) && (wing_name_lookup(CTEXT(node), 1) < 0))  // is it in the mission?
2681 								if (Fred_running || !mission_parse_get_arrival_ship(CTEXT(node)))
2682 									return SEXP_CHECK_INVALID_MSG_SOURCE;
2683 				}
2684 
2685 				break;
2686 
2687 			//Karajorma
2688 			case OPF_PERSONA:
2689 				if (type2 != SEXP_ATOM_STRING) {
2690 					return SEXP_CHECK_TYPE_MISMATCH;
2691 				}
2692 
2693 				for (i=0; i < Num_personas ; i++) {
2694 					if (!strcmp(CTEXT(node), Personas[i].name)) {
2695 						break;
2696 					}
2697 				}
2698 
2699 				if (i == Num_personas) {
2700 					return SEXP_CHECK_INVALID_PERSONA_NAME;
2701 				}
2702 				break;
2703 
2704 			case OPF_MISSION_MOOD:
2705 				if (type2 != SEXP_ATOM_STRING) {
2706 					return SEXP_CHECK_TYPE_MISMATCH;
2707 				}
2708 
2709 				for (i = 0; i < (int)Builtin_moods.size(); i++) {
2710 					if (!strcmp(Builtin_moods[i].c_str(), CTEXT(node))) {
2711 						break;
2712 					}
2713 				}
2714 
2715 				if (i == (int)Builtin_moods.size()) {
2716 					return SEXP_CHECK_INVALID_MISSION_MOOD;
2717 				}
2718 
2719 				break;
2720 
2721 			case OPF_SHIP_FLAG:
2722 				{
2723 				bool found = false;
2724 				for ( i = 0; i < MAX_OBJECT_FLAG_NAMES; i++) {
2725 					if (!stricmp(Object_flag_names[i].flag_name, CTEXT(node))) {
2726 						found = true;
2727 						break;
2728 					}
2729 				}
2730 
2731 				if (!found) {
2732 					for ( i = 0; i < MAX_SHIP_FLAG_NAMES; i++) {
2733 						if (!stricmp(Ship_flag_names[i].flag_name, CTEXT(node))) {
2734 							found = true;
2735 							break;
2736 						}
2737 					}
2738 				}
2739 
2740 				if (!found) {
2741 					for ( i = 0; i < MAX_AI_FLAG_NAMES; i++) {
2742 						if (!stricmp(Ai_flag_names[i].flag_name, CTEXT(node))) {
2743 							found = true;
2744 							break;
2745 						}
2746 					}
2747 				}
2748 
2749 				if (!found) {
2750 					return SEXP_CHECK_INVALID_SHIP_FLAG;
2751 				}
2752 
2753 				break;
2754 				}
2755 
2756 			case OPF_TEAM_COLOR:
2757 				if (type2 != SEXP_ATOM_STRING) {
2758 					return SEXP_CHECK_TYPE_MISMATCH;
2759 				}
2760 
2761 				if (Team_Colors.find(CTEXT(node)) == Team_Colors.end())
2762 					return SEXP_CHECK_INVALID_TEAM_COLOR;
2763 
2764 				break;
2765 
2766 			case OPF_FONT:
2767 				if (type2 != SEXP_ATOM_STRING) {
2768 					return SEXP_CHECK_TYPE_MISMATCH;
2769 				}
2770 
2771 				for (i = 0; i < Num_fonts; i++) {
2772 					if (!stricmp(CTEXT(node), Fonts[i].filename)) {
2773 						break;
2774 					}
2775 				}
2776 
2777 				if (i == Num_fonts) {
2778 					return SEXP_CHECK_INVALID_FONT;
2779 				}
2780 				break;
2781 
2782 			case OPF_SOUND_ENVIRONMENT:
2783 				if (type2 != SEXP_ATOM_STRING) {
2784 					return SEXP_CHECK_TYPE_MISMATCH;
2785 				}
2786 
2787 				if (stricmp(CTEXT(node), SEXP_NONE_STRING) && ds_eax_get_preset_id(CTEXT(node)) < 0) {
2788 					return SEXP_CHECK_INVALID_SOUND_ENVIRONMENT;
2789 				}
2790 				break;
2791 
2792 			case OPF_AUDIO_VOLUME_OPTION:
2793 				if (type2 != SEXP_ATOM_STRING) {
2794 					return SEXP_CHECK_TYPE_MISMATCH;
2795 				}
2796 
2797 				if (audio_volume_option_lookup(CTEXT(node)) == -1)
2798 					return SEXP_CHECK_INVALID_AUDIO_VOLUME_OPTION;
2799 				break;
2800 
2801 			case OPF_HUD_GAUGE:
2802 				if (type2 != SEXP_ATOM_STRING) {
2803 					return SEXP_CHECK_TYPE_MISMATCH;
2804 				}
2805 
2806 				if (hud_gauge_type_lookup(CTEXT(node)) == -1)
2807 					return SEXP_CHECK_INVALID_HUD_GAUGE;
2808 				break;
2809 
2810 			case OPF_SOUND_ENVIRONMENT_OPTION:
2811 				if (type2 != SEXP_ATOM_STRING) {
2812 					return SEXP_CHECK_TYPE_MISMATCH;
2813 				}
2814 
2815 				if (sexp_sound_environment_option_lookup(CTEXT(node)) < 0) {
2816 					return SEXP_CHECK_INVALID_SOUND_ENVIRONMENT_OPTION;
2817 				}
2818 				break;
2819 
2820 			case OPF_EXPLOSION_OPTION:
2821 				if (type2 != SEXP_ATOM_STRING) {
2822 					return SEXP_CHECK_TYPE_MISMATCH;
2823 				}
2824 
2825 				if (sexp_explosion_option_lookup(CTEXT(node)) < 0) {
2826 					return SEXP_CHECK_INVALID_EXPLOSION_OPTION;
2827 				}
2828 				break;
2829 
2830 			case OPF_KEYPRESS:
2831 				if (type2 != SEXP_ATOM_STRING)
2832 					return SEXP_CHECK_TYPE_MISMATCH;
2833 
2834 				break;
2835 
2836 			case OPF_CARGO:
2837 			case OPF_STRING:
2838 			case OPF_MESSAGE_OR_STRING:
2839 				if (type2 != SEXP_ATOM_STRING)
2840 					return SEXP_CHECK_TYPE_MISMATCH;
2841 				break;
2842 
2843 			case OPF_SKILL_LEVEL:
2844 				if ( type2 != SEXP_ATOM_STRING )
2845 					return SEXP_CHECK_TYPE_MISMATCH;
2846 
2847 				for (i = 0; i < NUM_SKILL_LEVELS; i++) {
2848 					if ( !stricmp(CTEXT(node), Skill_level_names(i, 0)) )
2849 						break;
2850 				}
2851 				if ( i == NUM_SKILL_LEVELS )
2852 					return SEXP_CHECK_INVALID_SKILL_LEVEL;
2853 				break;
2854 
2855 			case OPF_MEDAL_NAME:
2856 				if ( type2 != SEXP_ATOM_STRING)
2857 					return SEXP_CHECK_TYPE_MISMATCH;
2858 
2859 				for (i = 0; i < Num_medals; i++) {
2860 					if ( !stricmp(CTEXT(node), Medals[i].name) )
2861 						break;
2862 				}
2863 
2864 				if ( i == Num_medals )
2865 					return SEXP_CHECK_INVALID_MEDAL_NAME;
2866 				break;
2867 
2868 			case OPF_HUGE_WEAPON:
2869 			case OPF_WEAPON_NAME:
2870 				if ( type2 != SEXP_ATOM_STRING )
2871 					return SEXP_CHECK_TYPE_MISMATCH;
2872 
2873 				for (i = 0; i < Num_weapon_types; i++ ) {
2874 					if ( !stricmp(CTEXT(node), Weapon_info[i].name) )
2875 						break;
2876 				}
2877 
2878 				if ( i == Num_weapon_types )
2879 					return SEXP_CHECK_INVALID_WEAPON_NAME;
2880 
2881 				// we need to be sure that for huge weapons, the WIF_HUGE flag is set
2882 				if ( type == OPF_HUGE_WEAPON ) {
2883 					if ( !(Weapon_info[i].wi_flags & WIF_HUGE) )
2884 						return SEXP_CHECK_INVALID_WEAPON_NAME;
2885 				}
2886 
2887 				break;
2888 
2889 			// Goober5000
2890 			case OPF_INTEL_NAME:
2891 				if ( type2 != SEXP_ATOM_STRING )
2892 					return SEXP_CHECK_TYPE_MISMATCH;
2893 
2894 				for (i = 0; i < Intel_info_size; i++ ) {
2895 					if ( !stricmp(CTEXT(node), Intel_info[i].name) )
2896 						break;
2897 				}
2898 
2899 				if ( i == Intel_info_size )
2900 					return SEXP_CHECK_INVALID_INTEL_NAME;
2901 
2902 				break;
2903 
2904 			case OPF_TURRET_TARGET_ORDER:
2905 				if ( type2 != SEXP_ATOM_STRING )
2906 					return SEXP_CHECK_TYPE_MISMATCH;
2907 
2908 				for (i = 0; i < NUM_TURRET_ORDER_TYPES; i++ ) {
2909 					if ( !stricmp(CTEXT(node), Turret_target_order_names[i]) )
2910 						break;
2911 				}
2912 
2913 				if ( i == NUM_TURRET_ORDER_TYPES )
2914 					return SEXP_CHECK_INVALID_TURRET_TARGET_ORDER;
2915 
2916 				break;
2917 
2918 			case OPF_ARMOR_TYPE:
2919 				if ( type2 != SEXP_ATOM_STRING )
2920 					return SEXP_CHECK_TYPE_MISMATCH;
2921 
2922 				if (!stricmp(CTEXT(node), SEXP_NONE_STRING))
2923 					break;
2924 
2925 				for (st = 0; st < Armor_types.size(); st++ ) {
2926 					if ( !stricmp(CTEXT(node), Armor_types[st].GetNamePtr()) )
2927 						break;
2928 				}
2929 
2930 				if ( st == Armor_types.size() )
2931 					return SEXP_CHECK_INVALID_ARMOR_TYPE;
2932 
2933 				break;
2934 
2935 			case OPF_DAMAGE_TYPE:
2936 				if ( type2 != SEXP_ATOM_STRING )
2937 					return SEXP_CHECK_TYPE_MISMATCH;
2938 
2939 				if (!stricmp(CTEXT(node), SEXP_NONE_STRING))
2940 					break;
2941 
2942 				for (st = 0; st < Damage_types.size(); st++ ) {
2943 					if ( !stricmp(CTEXT(node), Damage_types[st].name) )
2944 						break;
2945 				}
2946 
2947 				if ( st == Armor_types.size() )
2948 					return SEXP_CHECK_INVALID_DAMAGE_TYPE;
2949 
2950 				break;
2951 
2952 			case OPF_ANIMATION_TYPE:
2953 				if ( type2 != SEXP_ATOM_STRING )
2954 					return SEXP_CHECK_TYPE_MISMATCH;
2955 
2956 				i = model_anim_match_type(CTEXT(node));
2957 				if ( i == TRIGGER_TYPE_NONE )
2958 					return SEXP_CHECK_INVALID_ANIMATION_TYPE;
2959 
2960 				break;
2961 
2962 			case OPF_TARGET_PRIORITIES:
2963 				if ( type2 != SEXP_ATOM_STRING )
2964 					return SEXP_CHECK_TYPE_MISMATCH;
2965 
2966 				for(st = 0; st < Ai_tp_list.size(); st++) {
2967 					if ( !stricmp(CTEXT(node), Ai_tp_list[st].name) )
2968 						break;
2969 				}
2970 
2971 				if ( st == Ai_tp_list.size() )
2972 					return SEXP_CHECK_INVALID_TARGET_PRIORITIES;
2973 
2974 				break;
2975 
2976 			case OPF_SHIP_CLASS_NAME:
2977 				if ( type2 != SEXP_ATOM_STRING )
2978 					return SEXP_CHECK_TYPE_MISMATCH;
2979 
2980 				if (ship_info_lookup(CTEXT(node)) < 0)
2981 					return SEXP_CHECK_INVALID_SHIP_CLASS_NAME;
2982 
2983 				break;
2984 
2985 			case OPF_HUD_GAUGE_NAME:
2986 				if ( type2 != SEXP_ATOM_STRING )
2987 					return SEXP_CHECK_TYPE_MISMATCH;
2988 
2989 				for ( i = 0; i < NUM_HUD_GAUGES; i++ ) {
2990 					if ( !stricmp(CTEXT(node), HUD_gauge_text[i]) )
2991 						break;
2992 				}
2993 
2994 				// if we reached the end of the list, then the name is invalid
2995 				if ( i == NUM_HUD_GAUGES )
2996 					return SEXP_CHECK_INVALID_GAUGE_NAME;
2997 
2998 				break;
2999 
3000 			case OPF_SKYBOX_MODEL_NAME:
3001 				if ( type2 != SEXP_ATOM_STRING )
3002 					return SEXP_CHECK_TYPE_MISMATCH;
3003 
3004 				if ( stricmp(CTEXT(node), NOX("default")) && !strstr(CTEXT(node), NOX(".pof")) )
3005 					return SEXP_CHECK_INVALID_SKYBOX_NAME;
3006 
3007 				break;
3008 
3009 			case OPF_JUMP_NODE_NAME:
3010 				if ( type2 != SEXP_ATOM_STRING )
3011 					return SEXP_CHECK_TYPE_MISMATCH;
3012 
3013 				if (jumpnode_get_by_name(CTEXT(node)) == NULL)
3014 					return SEXP_CHECK_INVALID_JUMP_NODE;
3015 
3016 				break;
3017 
3018 
3019 			case OPF_VARIABLE_NAME:
3020 				var_index = get_index_sexp_variable_from_node(node);
3021 				if ( var_index  == -1) {
3022 					return SEXP_CHECK_INVALID_VARIABLE;
3023 				}
3024 
3025 				// some SEXPs demand a number variable
3026 				if ((argnum == 8 && Operators[op].value == OP_ADD_BACKGROUND_BITMAP) ||
3027 					(argnum == 5 && Operators[op].value == OP_ADD_SUN_BITMAP))
3028 				{
3029 					if (!(Sexp_variables[var_index].type & SEXP_VARIABLE_NUMBER))
3030 						return SEXP_CHECK_INVALID_VARIABLE_TYPE;
3031 				}
3032 				// otherwise anything goes
3033 				break;
3034 
3035 			case OPF_AMBIGUOUS:
3036 				// type checking for modify-variable
3037 				// string or number -- anything goes
3038 				break;
3039 
3040 			case OPF_BACKGROUND_BITMAP:
3041 			case OPF_SUN_BITMAP:
3042 			case OPF_NEBULA_STORM_TYPE:
3043 			case OPF_NEBULA_POOF:
3044 			case OPF_NEBULA_PATTERN:
3045 			case OPF_POST_EFFECT:
3046 				if (type2 != SEXP_ATOM_STRING) {
3047 					return SEXP_CHECK_TYPE_MISMATCH;
3048 				}
3049 				break;
3050 
3051 			case OPF_HUD_ELEMENT:
3052 				if (type2 != SEXP_ATOM_STRING) {
3053 					return SEXP_CHECK_TYPE_MISMATCH;
3054 				} else {
3055 					char *gauge = CTEXT(node);
3056 					if ( strcmp(SEXP_HUD_GAUGE_WARPOUT, gauge) == 0 ) {
3057 						break;
3058 					}
3059 				}
3060 				return SEXP_CHECK_INVALID_HUD_ELEMENT;
3061 
3062 			case OPF_WEAPON_BANK_NUMBER:
3063 				if (type2 != SEXP_ATOM_STRING) {
3064 					return SEXP_CHECK_TYPE_MISMATCH;
3065 				}
3066 
3067 				if (!stricmp(CTEXT(node), SEXP_ALL_BANKS_STRING)) {
3068 					break;
3069 				}
3070 
3071 				// if we haven't specified all banks we need to check the number of the bank is legal
3072 				else {
3073 					int num_banks = atoi(CTEXT(node));
3074 					if ((num_banks >= MAX_SHIP_PRIMARY_BANKS) && (num_banks >= MAX_SHIP_SECONDARY_BANKS)) {
3075 						return SEXP_CHECK_NUM_RANGE_INVALID;
3076 					}
3077 				}
3078 				break;
3079 
3080 			case OPF_SHIP_EFFECT:
3081 				if (type2 != SEXP_ATOM_STRING) {
3082 					return SEXP_CHECK_TYPE_MISMATCH;
3083 				}
3084 
3085 				if (get_effect_from_name(CTEXT(node)) == -1 ) {
3086 					return SEXP_CHECK_INVALID_SHIP_EFFECT;
3087 				}
3088 				break;
3089 
3090 
3091 			default:
3092 				Error(LOCATION, "Unhandled argument format");
3093 		}
3094 
3095 		node = Sexp_nodes[node].rest;
3096 		argnum++;
3097 	}
3098 
3099 	return 0;
3100 }
3101 
3102 // Goober5000
get_unformatted_sexp_variable_name(char * unformatted,char * formatted_pre)3103 void get_unformatted_sexp_variable_name(char *unformatted, char *formatted_pre)
3104 {
3105 	int end_index;
3106 	char *formatted;
3107 
3108 	// Goober5000 - trim @ if needed
3109 	if (formatted_pre[0] == SEXP_VARIABLE_CHAR)
3110 		formatted = formatted_pre+1;
3111 	else
3112 		formatted = formatted_pre;
3113 
3114 	// get variable name (up to '['
3115 	end_index = strcspn(formatted, "[");
3116 	Assert( (end_index != 0) && (end_index < TOKEN_LENGTH-1) );
3117 	strncpy(unformatted, formatted, end_index);
3118 	unformatted[end_index] = '\0';
3119 }
3120 
3121 /**
3122  * Get text to stuff into Sexp_node in case of variable
3123  *
3124  * If Fred_running - stuff Sexp_variables[].variable_name
3125  * otherwise - stuff index into Sexp_variables array.
3126  */
get_sexp_text_for_variable(char * text,char * token)3127 void get_sexp_text_for_variable(char *text, char *token)
3128 {
3129 	int sexp_var_index;
3130 
3131 	get_unformatted_sexp_variable_name(text, token);
3132 
3133 	if ( !Fred_running ) {
3134 		// freespace - get index into Sexp_variables array
3135 		sexp_var_index = get_index_sexp_variable_name(text);
3136 		Assert(sexp_var_index != -1);
3137 		sprintf(text, "%d", sexp_var_index);
3138 	}
3139 }
3140 
3141 // Goober5000
do_preload_for_arguments(void (* preloader)(char *),int arg_node,int arg_handler_node)3142 void do_preload_for_arguments(void (*preloader)(char *), int arg_node, int arg_handler_node)
3143 {
3144 	// we have a special argument
3145 	if (!strcmp(Sexp_nodes[arg_node].text, SEXP_ARGUMENT_STRING))
3146 	{
3147 		int n;
3148 
3149 		// we might not be handling it, either because this is not a *_of operator
3150 		// or because we've disabled preloading for special arguments
3151 		if (arg_handler_node < 0)
3152 			return;
3153 
3154 		// preload for each argument
3155 		for (n = CDR(arg_handler_node); n != -1; n = CDR(n))
3156 		{
3157 			preloader(CTEXT(n));
3158 		}
3159 	}
3160 	// we don't
3161 	else
3162 	{
3163 		// preload for just the one argument
3164 		preloader(CTEXT(arg_node));
3165 	}
3166 }
3167 
3168 // Goober5000
preload_change_ship_class(char * text)3169 void preload_change_ship_class(char *text)
3170 {
3171 	int idx;
3172 	ship_info *sip;
3173 
3174 	idx = ship_info_lookup(text);
3175 	if (idx < 0)
3176 		return;
3177 
3178 	// preload the model, just in case there is no other ship of this class in the mission
3179 	// (this eliminates the slight pause during a mission when changing to a previously unloaded model)
3180 	sip = &Ship_info[idx];
3181 	sip->model_num = model_load(sip->pof_file, sip->n_subsystems, &sip->subsystems[0]);
3182 
3183 	if (sip->model_num >= 0)
3184 		model_page_in_textures(sip->model_num, idx);
3185 }
3186 
3187 // Goober5000
preload_turret_change_weapon(char * text)3188 void preload_turret_change_weapon(char *text)
3189 {
3190 	int idx;
3191 
3192 	idx = weapon_info_lookup(text);
3193 	if (idx < 0)
3194 		return;
3195 
3196 	weapon_mark_as_used(idx);
3197 }
3198 
3199 /**
3200  * Returns the first sexp index of data this function allocates. (start of this sexp)
3201  */
get_sexp()3202 int get_sexp()
3203 {
3204 	int start, node, last, op, count;
3205 	char token[TOKEN_LENGTH];
3206 	char variable_text[TOKEN_LENGTH];
3207 
3208 	// start - the node allocated in first instance of fuction
3209 	// node - the node allocated in current instance of function
3210 	// count - number of nodes allocated this instance of function [do we set last.rest or .first]
3211 	// variable - whether string or number is a variable referencing Sexp_variables
3212 
3213 	// initialization
3214 	start = last = -1;
3215 	count = 0;
3216 
3217 	ignore_white_space();
3218 	while (*Mp != ')') {
3219 		Assert(*Mp != EOF_CHAR);
3220 
3221 		// Sexp list
3222 		if (*Mp == '(') {
3223 			Mp++;
3224 			node = alloc_sexp("", SEXP_LIST, SEXP_ATOM_LIST, get_sexp(), -1);
3225 		}
3226 
3227 		// Sexp string
3228 		else if (*Mp == '\"') {
3229 			int len = strcspn(Mp + 1, "\"");
3230 			Assert(Mp[len + 1] == '\"');    // hit EOF first (unterminated string)
3231 
3232 			// check if string variable
3233 			if ( *(Mp + 1) == SEXP_VARIABLE_CHAR ) {
3234 				char variable_token[2*TOKEN_LENGTH+2];	// variable_token[contents_token]
3235 
3236 				// reduce length by 1 for end \"
3237 				int length = len - 1;
3238 				if (length >= 2*TOKEN_LENGTH+2) {
3239 					Error(LOCATION, "Variable token %s is too long. Needs to be %d characters or shorter.", Mp, 2*TOKEN_LENGTH+2 - 1);
3240 					return -1;
3241 				}
3242 
3243 				// start copying after skipping 1st char
3244 				strncpy(variable_token, Mp + 2, length);
3245 				variable_token[length] = 0;
3246 
3247 				get_sexp_text_for_variable(variable_text, variable_token);
3248 				node = alloc_sexp(variable_text, (SEXP_ATOM | SEXP_FLAG_VARIABLE), SEXP_ATOM_STRING, -1, -1);
3249 			} else {
3250 				// token is too long?
3251 				if (len >= TOKEN_LENGTH) {
3252 					Error(LOCATION, "Token %s is too long. Needs to be %d characters or shorter.", Mp, TOKEN_LENGTH - 1);
3253 					return -1;
3254 				}
3255 
3256 				strncpy(token, Mp + 1, len);
3257 				token[len] = 0;
3258 				node = alloc_sexp(token, SEXP_ATOM, SEXP_ATOM_STRING, -1, -1);
3259 			}
3260 
3261 			// bump past closing \" by 1 char
3262 			Mp += len + 2;
3263 
3264 		}
3265 
3266 		// Sexp operator or number
3267 		else {
3268 			int len = 0;
3269 			bool variable = false;
3270 			while (*Mp != ')' && !is_white_space(*Mp)) {
3271 				if ( (len == 0) && (*Mp == SEXP_VARIABLE_CHAR) ) {
3272 					variable = true;
3273 					Mp++;
3274 					continue;
3275 				}
3276 				Assert(*Mp != EOF_CHAR);
3277 				Assert(len < TOKEN_LENGTH - 1);
3278 				token[len++] = *Mp++;
3279 			}
3280 			token[len] = 0;
3281 
3282 			// maybe replace deprecated names
3283 			if (!stricmp(token, "set-ship-position"))
3284 				strcpy_s(token, "set-object-position");
3285 			else if (!stricmp(token, "set-ship-facing"))
3286 				strcpy_s(token, "set-object-facing");
3287 			else if (!stricmp(token, "set-ship-facing-object"))
3288 				strcpy_s(token, "set-object-facing-object");
3289 			else if (!stricmp(token, "ai-chase-any-except"))
3290 				strcpy_s(token, "ai-chase-any");
3291 			else if (!stricmp(token, "change-ship-model"))
3292 				strcpy_s(token, "change-ship-class");
3293 			else if (!stricmp(token, "radar-set-max-range"))
3294 				strcpy_s(token, "hud-set-max-targeting-range");
3295 			else if (!stricmp(token, "ship-subsys-vanished"))
3296 				strcpy_s(token, "ship-subsys-vanish");
3297 			else if (!stricmp(token, "directive-is-variable"))
3298 				strcpy_s(token, "directive-value");
3299 			else if (!stricmp(token, "variable-array-get"))
3300 				strcpy_s(token, "get-variable-by-index");
3301 			else if (!stricmp(token, "variable-array-set"))
3302 				strcpy_s(token, "set-variable-by-index");
3303 
3304 			op = get_operator_index(token);
3305 			if (op != -1) {
3306 				node = alloc_sexp(token, SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, -1);
3307 			} else {
3308 				if ( variable ) {
3309 					// convert token text for variable
3310 					get_sexp_text_for_variable(variable_text, token);
3311 
3312 					node = alloc_sexp(variable_text, (SEXP_ATOM | SEXP_FLAG_VARIABLE), SEXP_ATOM_NUMBER, -1, -1);
3313 				} else {
3314 					node = alloc_sexp(token, SEXP_ATOM, SEXP_ATOM_NUMBER, -1, -1);
3315 				}
3316 			}
3317 		}
3318 
3319 		// update links
3320 		if (count++) {
3321 			Assert(last != -1);
3322 			Sexp_nodes[last].rest = node;
3323 		} else {
3324 			start = node;
3325 		}
3326 
3327 		Assert(node != -1);  // ran out of nodes.  Time to raise the MAX!
3328 		last = node;
3329 		ignore_white_space();
3330 	}
3331 
3332 	Mp++;  // skip past the ')'
3333 
3334 
3335 	// Goober5000 - backwards compatibility for removed ai-chase-any-except
3336 	if (get_operator_const(CTEXT(start)) == OP_AI_CHASE_ANY)
3337 	{
3338 		// if there is more than one argument, free the extras
3339 		int n = CDR(CDR(start));
3340 		if (n >= 0)
3341 		{
3342 			// free the entire rest of the argument list
3343 			free_sexp2(n);
3344 		}
3345 	}
3346 
3347 	// Goober5000 - preload stuff for certain sexps
3348 	if (!Fred_running)
3349 	{
3350 		int n, parent, arg_handler = -1;
3351 
3352 		// see if we're using special arguments
3353 		parent = find_parent_operator(start);
3354 		if (parent >= 0 && is_blank_argument_op(get_operator_const(CTEXT(parent))))
3355 		{
3356 			// get the first op of the parent, which should be a *_of operator
3357 			arg_handler = CADR(parent);
3358 			if (!is_blank_of_op(get_operator_const(CTEXT(arg_handler))))
3359 				arg_handler = -1;
3360 		}
3361 
3362 		// DISABLE ARGUMENT PRELOADING FOR NOW
3363 		// see Mantis #925 for discussion
3364 		// Also, the preloader will have to be moved to a different function (after the parsing is finished)
3365 		// in order to work properly with special arguments.  The current node is an orphan until get_sexp
3366 		// completes, at which time it will be linked into the sexp node list; this means that it is
3367 		// impossible to get the parent node.
3368 		arg_handler = -1;
3369 
3370 		// preload according to sexp
3371 		op = get_operator_const(CTEXT(start));
3372 		switch (op)
3373 		{
3374 			case OP_CHANGE_SHIP_CLASS:
3375 				// ship class is argument #1
3376 				n = CDR(start);
3377 				do_preload_for_arguments(preload_change_ship_class, n, arg_handler);
3378 				break;
3379 
3380 			case OP_SHIP_CREATE:
3381 				// ship class is argument #2
3382 				n = CDDR(start);
3383 				// page in ship classes of dynamically created ships
3384 				// preload_change_ship_class doesn't require a class change, so we can use that here -zookeeper
3385 				do_preload_for_arguments(preload_change_ship_class, n, arg_handler);
3386 				break;
3387 
3388 			case OP_SET_SPECIAL_WARPOUT_NAME:
3389 				// set flag for taylor
3390 				Knossos_warp_ani_used = 1;
3391 				break;
3392 
3393 			case OP_MISSION_SET_NEBULA:
3394 				// set flag for WMC
3395 				Nebula_sexp_used = true;
3396 				Dynamic_environment = true;
3397 				break;
3398 
3399 			case OP_MISSION_SET_SUBSPACE:
3400 				Dynamic_environment = true;
3401 				break;
3402 
3403 			case OP_WARP_EFFECT:
3404 				// type of warp is argument #11
3405 				n = CDDDDDR(start);
3406 				n = CDDDDDR(n);
3407 				n = CDR(n);
3408 
3409 				// set flag for taylor
3410 				if (CAR(n) != -1 || !strcmp(Sexp_nodes[n].text, SEXP_ARGUMENT_STRING))	// if it's evaluating a sexp or a special argument
3411 					Knossos_warp_ani_used = 1;												// set flag just in case
3412 				else if (atoi(CTEXT(n)) == 1)											// if it's the Knossos type
3413 					Knossos_warp_ani_used = 1;												// set flag for sure
3414 				break;
3415 
3416 			case OP_SET_SKYBOX_MODEL:
3417 				// model is argument #1
3418 				n = CDR(start);
3419 				do_preload_for_arguments(sexp_set_skybox_model_preload, n, arg_handler);
3420 				Dynamic_environment = true;
3421 				break;
3422 
3423 			case OP_TURRET_CHANGE_WEAPON:
3424 				// weapon to change to is arg #3
3425 				n = CDDDR(start);
3426 				do_preload_for_arguments(preload_turret_change_weapon, n, arg_handler);
3427 				break;
3428 
3429 			case OP_ADD_SUN_BITMAP:
3430 				n = CDR(start);
3431 				do_preload_for_arguments(stars_preload_sun_bitmap, n, arg_handler);
3432 				Dynamic_environment = true;
3433 				break;
3434 
3435 			case OP_ADD_BACKGROUND_BITMAP:
3436 				n = CDR(start);
3437 				do_preload_for_arguments(stars_preload_background_bitmap, n, arg_handler);
3438 				Dynamic_environment = true;
3439 				break;
3440 
3441 			case OP_TECH_ADD_INTEL_XSTR:
3442 				// do XSTR translation for each entry in the list
3443 				// we don't use the do_preload function because the preloader needs to access two nodes at a time
3444 				// also we're not using CTEXT or eval_num here because XSTR should really be constant, and
3445 				// also because we can't really run sexp stuff in a preloader
3446 				n = CDR(start);
3447 				while (n >= 0)
3448 				{
3449 					if (CDR(n) < 0)
3450 						break;
3451 
3452 					int id = atoi(Sexp_nodes[CDR(n)].text);
3453 					Assert(id < 10000000);
3454 					char xstr[NAME_LENGTH + 20];
3455 					sprintf(xstr, "XSTR(\"%s\", %d)", Sexp_nodes[n].text, id);
3456 
3457 					memset(Sexp_nodes[n].text, 0, NAME_LENGTH*sizeof(char));
3458 					lcl_ext_localize(xstr, Sexp_nodes[n].text, NAME_LENGTH - 1);
3459 
3460 					n = CDDR(n);
3461 				}
3462 				break;
3463 		}
3464 	}
3465 
3466 	return start;
3467 }
3468 
3469 
3470 /**
3471  * Stuffs a list of sexp variables
3472  */
stuff_sexp_variable_list()3473 int stuff_sexp_variable_list()
3474 {
3475 	int count;
3476 	char var_name[TOKEN_LENGTH];
3477 	char default_value[TOKEN_LENGTH];
3478 	char str_type[TOKEN_LENGTH];
3479 	char persistent[TOKEN_LENGTH];
3480 	char network[TOKEN_LENGTH];
3481 	int index;
3482 	int type;
3483 
3484 	count = 0;
3485 	required_string("$Variables:");
3486 	ignore_white_space();
3487 
3488 	// check for start of list
3489 	if (*Mp != '(') {
3490 		error_display(1, "Reading sexp variable list.  Found [%c].  Expecting '('.\n", *Mp);
3491 		longjmp(parse_abort, 6);
3492 	}
3493 
3494 	Mp++;
3495 	ignore_white_space();
3496 
3497 	while (*Mp != ')') {
3498 		Assert(count < MAX_SEXP_VARIABLES);
3499 
3500 		// get index - for debug
3501 		stuff_int(&index);
3502 		ignore_gray_space();
3503 
3504 		// get var_name
3505 		get_string(var_name);
3506 		ignore_gray_space();
3507 
3508 		// get default_value;
3509 		get_string(default_value);
3510 		ignore_gray_space();
3511 
3512 		// get type
3513 		get_string(str_type);
3514 		ignore_white_space();
3515 
3516 		// determine type
3517 		if (!stricmp(str_type, "number")) {
3518 			type = SEXP_VARIABLE_NUMBER;
3519 		} else if (!stricmp(str_type, "string")) {
3520 			type = SEXP_VARIABLE_STRING;
3521 		} else if (!stricmp(str_type, "block")) {
3522 			// Goober5000 - This looks dangerous... these flags are needed for certain things, but it
3523 			// looks like BLOCK_*_SIZE is the only thing that keeps a block from running off the end
3524 			// of its boundary.
3525 			type = SEXP_VARIABLE_BLOCK;
3526 		} else {
3527 			type = SEXP_VARIABLE_UNKNOWN;
3528 			Error(LOCATION, "SEXP variable '%s' is an unknown type!", var_name);
3529 		}
3530 
3531 		// possibly get network-variable
3532 		if (check_for_string("\"network-variable\"")) {
3533 			// eat it
3534 			get_string(network);
3535 			ignore_white_space();
3536 
3537 			// set type
3538 			type |= SEXP_VARIABLE_NETWORK;
3539 		}
3540 
3541 		// possibly get player-persistent
3542 		if (check_for_string("\"player-persistent\"")) {
3543 			// eat it
3544 			get_string(persistent);
3545 			ignore_white_space();
3546 
3547 			// set type
3548 			type |= SEXP_VARIABLE_PLAYER_PERSISTENT;
3549 		// possibly get campaign-persistent
3550 		} else if (check_for_string("\"campaign-persistent\"")) {
3551 			// eat it
3552 			get_string(persistent);
3553 			ignore_white_space();
3554 
3555 			// set type
3556 			type |= SEXP_VARIABLE_CAMPAIGN_PERSISTENT;
3557 		// trap error
3558 		} else if (check_for_string("\"")) {
3559 			// eat garbage
3560 			get_string(persistent);
3561 			ignore_white_space();
3562 
3563 			// notify of error
3564 			Error(LOCATION, "Error parsing sexp variables - unknown persistence type encountered.  You can continue from here without trouble.");
3565 		}
3566 
3567 		// check if variable name already exists
3568 		if ( (type & SEXP_VARIABLE_NUMBER) || (type & SEXP_VARIABLE_STRING) ) {
3569 			Assert(get_index_sexp_variable_name(var_name) == -1);
3570 		}
3571 
3572 		if ( type & SEXP_VARIABLE_BLOCK ) {
3573 			add_block_variable(default_value, var_name, type, index);
3574 		}
3575 		else {
3576 			count++;
3577 			sexp_add_variable(default_value, var_name, type, index);
3578 		}
3579 	}
3580 
3581 	Mp++;
3582 
3583 	return count;
3584 }
3585 
has_special_explosion_block_index(ship * shipp,int * index)3586 bool has_special_explosion_block_index(ship *shipp, int *index)
3587 {
3588 	int current_index;
3589 
3590 	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) ) {
3591 		if (
3592 			(atoi(Block_variables[current_index+INNER_RAD].text) == shipp->special_exp_inner) &&
3593 			(atoi(Block_variables[current_index+OUTER_RAD].text) == shipp->special_exp_outer) &&
3594 			(atoi(Block_variables[current_index+DAMAGE].text) == shipp->special_exp_damage) &&
3595 			(atoi(Block_variables[current_index+BLAST].text) == shipp->special_exp_blast) &&
3596 			(atoi(Block_variables[current_index+PROPAGATE].text) == (shipp->use_shockwave ? 1:0) ) &&
3597 			(atoi(Block_variables[current_index+SHOCK_SPEED].text) == shipp->special_exp_shockwave_speed)
3598 			) {
3599 				*index = current_index;
3600 				return true;
3601 		}
3602 	}
3603 
3604 	// if we got here, this ship's special explosions aren't represented in the Block_variables array
3605 	*index = current_index;
3606 	return false;
3607 }
3608 
generate_special_explosion_block_variables()3609 bool generate_special_explosion_block_variables()
3610 {
3611 	ship *shipp;
3612 	ship_obj *sop;
3613 	int current_index;
3614 	bool already_added = false;
3615 	int num_sexp_variables;
3616 	int i;
3617 
3618 	// since we're (re)generating the block variable index, we don't start off with any block variables
3619 	Num_special_expl_blocks = 0;
3620 
3621 	// get the number of sexp_variables we currently have. We must not try to add a block variable with an index below this.
3622 	num_sexp_variables = sexp_variable_count();
3623 
3624 	for ( sop = GET_FIRST(&Ship_obj_list); sop != END_OF_LIST(&Ship_obj_list); sop = GET_NEXT(sop) ) {
3625 		shipp=&Ships[Objects[sop->objnum].instance];
3626 
3627 		if (!(shipp->use_special_explosion)) {
3628 			continue;
3629 		}
3630 
3631 		already_added = has_special_explosion_block_index(shipp, &current_index);
3632 
3633 		// if we can't add an index for this add this ship to the list of those which failed
3634 		if (current_index < num_sexp_variables) {
3635 			// fail list code goes here
3636 			continue;
3637 		}
3638 
3639 		//if we haven't added this entry already, do so
3640 		if (!already_added) {
3641 			sprintf(Block_variables[current_index+INNER_RAD].text, "%d", shipp->special_exp_inner);
3642 			sprintf(Block_variables[current_index+OUTER_RAD].text, "%d", shipp->special_exp_outer);
3643 			sprintf(Block_variables[current_index+DAMAGE].text, "%d", shipp->special_exp_damage);
3644 			sprintf(Block_variables[current_index+BLAST].text, "%d", shipp->special_exp_blast);
3645 			sprintf(Block_variables[current_index+PROPAGATE].text, "%d", (shipp->use_shockwave ? 1:0) );
3646 			sprintf(Block_variables[current_index+SHOCK_SPEED].text, "%d", shipp->special_exp_shockwave_speed);
3647 
3648 			// add the names
3649 			for (i = current_index; i < (current_index + BLOCK_EXP_SIZE); i++ ) {
3650 				sprintf(Block_variables[i].variable_name, "%s", shipp->ship_name);
3651 			}
3652 
3653 			Num_special_expl_blocks++;
3654 		}
3655 	}
3656 
3657 	return true;
3658 }
3659 
num_block_variables()3660 int num_block_variables()
3661 {
3662 	return Num_special_expl_blocks * BLOCK_EXP_SIZE;
3663 }
3664 
3665 /**
3666  * Stuff SEXP text string
3667  */
stuff_sexp_text_string(SCP_string & dest,int node,int mode)3668 void stuff_sexp_text_string(SCP_string &dest, int node, int mode)
3669 {
3670 	Assert( (node >= 0) && (node < Num_sexp_nodes) );
3671 
3672 	if (Sexp_nodes[node].type & SEXP_FLAG_VARIABLE) {
3673 
3674 		int sexp_variables_index = get_index_sexp_variable_name(Sexp_nodes[node].text);
3675 		Assertion(sexp_variables_index != -1, "Couldn't find variable: %s\n", Sexp_nodes[node].text);
3676 		Assert( (Sexp_variables[sexp_variables_index].type & SEXP_VARIABLE_NUMBER) || (Sexp_variables[sexp_variables_index].type & SEXP_VARIABLE_STRING) );
3677 
3678 		// number
3679 		if (Sexp_nodes[node].subtype == SEXP_ATOM_NUMBER) {
3680 			Assert(Sexp_variables[sexp_variables_index].type & SEXP_VARIABLE_NUMBER);
3681 
3682 			// Error check - can be Fred or FreeSpace
3683 			if (mode == SEXP_ERROR_CHECK_MODE) {
3684 				if ( Fred_running ) {
3685 					sprintf(dest, "%s[%s] ", Sexp_nodes[node].text, Sexp_variables[sexp_variables_index].text);
3686 				} else {
3687 					sprintf(dest, "%s[%s] ", Sexp_variables[sexp_variables_index].variable_name, Sexp_variables[sexp_variables_index].text);
3688 				}
3689 			} else {
3690 				// Save as string - only  Fred
3691 				Assert(mode == SEXP_SAVE_MODE);
3692 				sprintf(dest, "@%s[%s] ", Sexp_nodes[node].text, Sexp_variables[sexp_variables_index].text);
3693 			}
3694 		} else {
3695 			// string
3696 			Assert(Sexp_nodes[node].subtype == SEXP_ATOM_STRING);
3697 			Assert(Sexp_variables[sexp_variables_index].type & SEXP_VARIABLE_STRING);
3698 
3699 			// Error check - can be Fred or FreeSpace
3700 			if (mode == SEXP_ERROR_CHECK_MODE) {
3701 				if ( Fred_running ) {
3702 					sprintf(dest, "%s[%s] ", Sexp_variables[sexp_variables_index].variable_name, Sexp_variables[sexp_variables_index].text);
3703 				} else {
3704 					sprintf(dest, "%s[%s] ", Sexp_nodes[node].text, Sexp_variables[sexp_variables_index].text);
3705 				}
3706 			} else {
3707 				// Save as string - only Fred
3708 				Assert(mode == SEXP_SAVE_MODE);
3709 				sprintf(dest, "\"@%s[%s]\" ", Sexp_nodes[node].text, Sexp_variables[sexp_variables_index].text);
3710 			}
3711 		}
3712 	} else {
3713 		// not a variable
3714 		if (Sexp_nodes[node].subtype == SEXP_ATOM_STRING) {
3715 			sprintf(dest, "\"%s\" ", CTEXT(node));
3716 		} else {
3717 			sprintf(dest, "%s ", CTEXT(node));
3718 		}
3719 	}
3720 }
3721 
build_sexp_string(SCP_string & accumulator,int cur_node,int level,int mode)3722 int build_sexp_string(SCP_string &accumulator, int cur_node, int level, int mode)
3723 {
3724 	SCP_string buf;
3725 	int node, old_length = accumulator.length();
3726 
3727 	accumulator += "( ";
3728 	node = cur_node;
3729 	while (node != -1) {
3730 		Assert(node >= 0 && node < Num_sexp_nodes);
3731 		if (Sexp_nodes[node].first == -1) {
3732 			// build text to string
3733 			stuff_sexp_text_string(buf, node, mode);
3734 			accumulator += buf;
3735 
3736 		} else {
3737 			build_sexp_string(accumulator, Sexp_nodes[node].first, level + 1, mode);
3738 		}
3739 
3740 		node = Sexp_nodes[node].rest;
3741 	}
3742 
3743 	accumulator += ") ";
3744 	if ((accumulator.length() - old_length) > 40) {
3745 		accumulator.resize(old_length);
3746 		build_extended_sexp_string(accumulator, cur_node, level, mode);
3747 		return 1;
3748 	}
3749 
3750 	return 0;
3751 }
3752 
build_extended_sexp_string(SCP_string & accumulator,int cur_node,int level,int mode)3753 void build_extended_sexp_string(SCP_string &accumulator, int cur_node, int level, int mode)
3754 {
3755 	SCP_string buf;
3756 	int i, flag = 0, node;
3757 
3758 	accumulator += "( ";
3759 	node = cur_node;
3760 	while (node != -1) {
3761 		// not the first line?
3762 		if (flag) {
3763 			for (i=0; i<level + 1; i++)
3764 				accumulator += "   ";
3765 		}
3766 
3767 		flag = 1;
3768 		Assert(node >= 0 && node < Num_sexp_nodes);
3769 		if (Sexp_nodes[node].first == -1) {
3770 			stuff_sexp_text_string(buf, node, mode);
3771 			accumulator += buf;
3772 
3773 		} else {
3774 			build_sexp_string(accumulator, Sexp_nodes[node].first, level + 1, mode);
3775 		}
3776 
3777 		accumulator += "\n";
3778 		node = Sexp_nodes[node].rest;
3779 	}
3780 
3781 	for (i=0; i<level; i++)
3782 		accumulator += "   ";
3783 
3784 	accumulator += ")";
3785 }
3786 
convert_sexp_to_string(SCP_string & dest,int cur_node,int mode)3787 void convert_sexp_to_string(SCP_string &dest, int cur_node, int mode)
3788 {
3789 	if (cur_node >= 0) {
3790 		dest = "";
3791 		build_sexp_string(dest, cur_node, 0, mode);
3792 	} else {
3793 		dest = "( )";
3794 	}
3795 }
3796 
3797 
3798 // -----------------------------------------------------------------------------------
3799 // Helper methods for getting data from nodes. Cause it's stupid to keep re-rolling this stuff for every single SEXP
3800 // -----------------------------------------------------------------------------------
3801 
3802 /**
3803  * Takes a SEXP node which contains the name of a ship and returns the player for that ship or NULL if it is an AI ship
3804  */
get_player_from_ship_node(int node,bool test_respawns)3805 player * get_player_from_ship_node(int node, bool test_respawns)
3806 {
3807 	int sindex, np_index = -1;
3808 	p_object *p_objp;
3809 
3810 	Assert (node != -1);
3811 
3812 	sindex = ship_name_lookup(CTEXT(node));
3813 
3814 	// singleplayer
3815 	if (!(Game_mode & GM_MULTIPLAYER)){
3816 		if(sindex >= 0){
3817 			if(Player_obj == &Objects[Ships[sindex].objnum]){
3818 				return Player;
3819 			}
3820 		}
3821 		return NULL;
3822 	}
3823 	// multiplayer
3824 	else {
3825 		if(sindex >= 0){
3826 			if(Ships[sindex].objnum >= 0) {
3827 				// try and find the player
3828 				np_index = multi_find_player_by_object(&Objects[Ships[sindex].objnum]);
3829 			}
3830 		}
3831 		if (test_respawns && np_index < 0) {
3832 			// Respawning ships don't have an objnum so we need to take a different approach
3833 			p_objp = mission_parse_get_arrival_ship(CTEXT(node));
3834 			if (p_objp != NULL) {
3835 				np_index = multi_find_player_by_parse_object(p_objp);
3836 			}
3837 		}
3838 
3839 		if((np_index >= 0) && (np_index < MAX_PLAYERS)){
3840 			return Net_players[np_index].m_player;
3841 		}
3842 
3843 		return NULL;
3844 	}
3845 }
3846 
3847 /**
3848  * Given a node, returns a pointer to the ship or NULL if this isn't the name of a ship
3849  */
sexp_get_ship_from_node(int node)3850 ship * sexp_get_ship_from_node(int node)
3851 {
3852 	int sindex;
3853 	ship *shipp = NULL;
3854 
3855 	sindex = ship_name_lookup( CTEXT(node) );
3856 
3857 	if (sindex < 0) {
3858 		return shipp;
3859 	}
3860 
3861 	if (Ships[sindex].objnum < 0) {
3862 		return shipp;
3863 	}
3864 
3865 	shipp = &Ships[sindex];
3866 	return shipp;
3867 }
3868 
3869 /**
3870  * Determine if the named ship or wing hasn't arrived yet (wing or ship must be on arrival list)
3871  */
sexp_query_has_yet_to_arrive(char * name)3872 int sexp_query_has_yet_to_arrive(char *name)
3873 {
3874 	int i;
3875 
3876 	if (ship_query_state(name) < 0)
3877 		return 1;
3878 
3879 	i = wing_name_lookup(name, 1);
3880 
3881 	// has not arrived yet, and never will arrive
3882 	if ((i >= 0) && (Wings[i].num_waves >= 0) && (Wings[i].flags & WF_NEVER_EXISTED)){
3883 		return 1;
3884 	}
3885 
3886 	// has not arrived yet
3887 	if ((i >= 0) && (Wings[i].num_waves >= 0) && !Wings[i].total_arrived_count){
3888 		return 1;
3889 	}
3890 
3891 	return 0;
3892 }
3893 
3894 // arithmetic functions
add_sexps(int n)3895 int add_sexps(int n)
3896 {
3897 	int	sum = 0, val;
3898 
3899 	if (n != -1) {
3900 		if ( CAR(n) != -1) {
3901 			sum = eval_sexp( CAR(n) );
3902 			// be sure to check for the NAN value when doing arithmetic -- this value should
3903 			// get propagated to the next highest function.
3904 			if ( Sexp_nodes[CAR(n)].value == SEXP_NAN )
3905 				return SEXP_NAN;
3906 			else if ( Sexp_nodes[CAR(n)].value == SEXP_NAN_FOREVER )
3907 				return SEXP_NAN_FOREVER;
3908 		}
3909 		else
3910 			sum = atoi( CTEXT(n) );
3911 
3912 		while (CDR(n) != -1) {
3913 			val = eval_sexp( CDR(n) );
3914 			// be sure to check for the NAN value when doing arithmetic -- this value should
3915 			// get propagated to the next highest function.
3916 			if ( Sexp_nodes[CDR(n)].value == SEXP_NAN )
3917 				return SEXP_NAN;
3918 			else if ( Sexp_nodes[CDR(n)].value == SEXP_NAN_FOREVER )
3919 				return SEXP_NAN_FOREVER;
3920 			sum += val;
3921 			n = CDR(n);
3922 		}
3923 	}
3924 
3925 	return sum;
3926 }
3927 
sub_sexps(int n)3928 int sub_sexps(int n)
3929 {
3930 	int	sum = 0;
3931 
3932 	if (n != -1) {
3933 		if (Sexp_nodes[n].first != -1)
3934 			sum = eval_sexp(CAR(n));
3935 		else
3936 			sum = atoi(CTEXT(n));
3937 
3938 		while (CDR(n) != -1) {
3939 			sum -= eval_sexp(CDR(n));
3940 			n = CDR(n);
3941 		}
3942 	}
3943 
3944 	return sum;
3945 }
3946 
mul_sexps(int n)3947 int mul_sexps(int n)
3948 {
3949 	int	sum = 0;
3950 
3951 	if (n != -1) {
3952 		if (Sexp_nodes[n].first != -1)
3953 			sum = eval_sexp(Sexp_nodes[n].first);
3954 		else
3955 			sum = atoi(CTEXT(n));
3956 
3957 		while (Sexp_nodes[n].rest != -1) {
3958 			sum *= eval_sexp(Sexp_nodes[n].rest);
3959 			n = Sexp_nodes[n].rest;
3960 		}
3961 	}
3962 
3963 	return sum;
3964 }
3965 
div_sexps(int n)3966 int div_sexps(int n)
3967 {
3968 	int	sum = 0;
3969 
3970 	if (n != -1) {
3971 		if (Sexp_nodes[n].first != -1)
3972 			sum = eval_sexp(Sexp_nodes[n].first);
3973 		else
3974 			sum = atoi(CTEXT(n));
3975 
3976 		while (Sexp_nodes[n].rest != -1) {
3977 			int div = eval_sexp(Sexp_nodes[n].rest);
3978 			n = Sexp_nodes[n].rest;
3979 			if (div == 0) {
3980 				Warning(LOCATION, "Division by zero in sexp. Please check all uses of the / operator for possible causes.\n");
3981 				Int3();
3982 				continue;
3983 			}
3984 			sum /= div;
3985 		}
3986 	}
3987 
3988 	return sum;
3989 }
3990 
mod_sexps(int n)3991 int mod_sexps(int n)
3992 {
3993 	int	sum = 0;
3994 
3995 	if (n != -1) {
3996 		if (Sexp_nodes[n].first != -1)
3997 			sum = eval_sexp(Sexp_nodes[n].first);
3998 		else
3999 			sum = atoi(CTEXT(n));
4000 
4001 		while (Sexp_nodes[n].rest != -1) {
4002 			sum = sum % eval_sexp(Sexp_nodes[n].rest);
4003 			n = Sexp_nodes[n].rest;
4004 		}
4005 	}
4006 
4007 	return sum;
4008 }
4009 
rand_internal(int low,int high,int seed=0)4010 int rand_internal(int low, int high, int seed = 0)
4011 {
4012 	int diff;
4013 
4014 	// maybe seed it
4015 	if (seed > 0)
4016 		srand(seed);
4017 
4018 	// get diff - don't allow negative or zero
4019 	diff = high - low;
4020 	if (diff < 0)
4021 		diff = 0;
4022 
4023 	return (low + rand32() % (diff + 1));
4024 }
4025 
4026 // Goober5000
abs_sexp(int n)4027 int abs_sexp(int n)
4028 {
4029 	return abs(eval_num(n));
4030 }
4031 
4032 // Goober5000
min_sexp(int n)4033 int min_sexp(int n)
4034 {
4035 	int temp, min_val = INT_MAX;
4036 
4037 	while (n != -1)
4038 	{
4039 		temp = eval_num(n);
4040 
4041 		if (temp < min_val)
4042 			min_val = temp;
4043 
4044 		n = CDR(n);
4045 	}
4046 
4047 	return min_val;
4048 }
4049 
4050 // Goober5000
max_sexp(int n)4051 int max_sexp(int n)
4052 {
4053 	int temp, max_val = INT_MIN;
4054 
4055 	while (n != -1)
4056 	{
4057 		temp = eval_num(n);
4058 
4059 		if (temp > max_val)
4060 			max_val = temp;
4061 
4062 		n = CDR(n);
4063 	}
4064 
4065 	return max_val;
4066 }
4067 
4068 // Goober5000
avg_sexp(int n)4069 int avg_sexp(int n)
4070 {
4071 	int num = 0, avg_val = 0;
4072 
4073 	while (n != -1)
4074 	{
4075 		avg_val += eval_num(n);
4076 		num++;
4077 
4078 		n = CDR(n);
4079 	}
4080 
4081 	return (int) floor(((double) avg_val / num) + 0.5);
4082 }
4083 
4084 // Goober5000
pow_sexp(int node)4085 int pow_sexp(int node)
4086 {
4087 	int num_1 = eval_num(node);
4088 	int num_2 = eval_num(CDR(node));
4089 
4090 	// this is disallowed in FRED, but can still happen through careless arithmetic
4091 	if (num_2 < 0)
4092 	{
4093 		Warning(LOCATION, "Power function pow(%d, %d) attempted to raise to a negative power!", num_1, num_2);
4094 		return 0;
4095 	}
4096 
4097 	double pow_result = pow(static_cast<double>(num_1), num_2);
4098 
4099 	if (pow_result > static_cast<double>(INT_MAX))
4100 	{
4101 		nprintf(("SEXP", "Power function pow(%d, %d) is greater than INT_MAX!  Returning INT_MAX.", num_1, num_2));
4102 		return INT_MAX;
4103 	}
4104 	else if (pow_result < static_cast<double>(INT_MIN))
4105 	{
4106 		nprintf(("SEXP", "Power function pow(%d, %d) is less than INT_MIN!  Returning INT_MIN.", num_1, num_2));
4107 		return INT_MIN;
4108 	}
4109 
4110 	return static_cast<int>(pow_result);
4111 }
4112 
4113 // Goober5000
signum_sexp(int node)4114 int signum_sexp(int node)
4115 {
4116 	int num = eval_num(node);
4117 
4118 	if (num == 0)
4119 		return 0;
4120 
4121 	if (num < 0)
4122 		return -1;
4123 
4124 	// hurr durr math
4125 	Assert(num > 0);
4126 	return 1;
4127 }
4128 
4129 // Goober5000
sexp_set_bit(int node,bool set_it)4130 int sexp_set_bit(int node, bool set_it)
4131 {
4132 	int val = eval_num(node);
4133 	int bit_index = eval_num(CDR(node));
4134 
4135 	if (bit_index < 0 || bit_index > 31)
4136 	{
4137 		Warning(LOCATION, "Bit index %d out of range!  Must be between 0 and 31.", bit_index);
4138 		return SEXP_NAN;
4139 	}
4140 
4141 	if (set_it)
4142 		return val | (1<<bit_index);
4143 	else
4144 		return val & ~(1<<bit_index);
4145 }
4146 
4147 // Goober5000
sexp_is_bit_set(int node)4148 int sexp_is_bit_set(int node)
4149 {
4150 	int val = eval_num(node);
4151 	int bit_index = eval_num(CDR(node));
4152 
4153 	if (bit_index < 0 || bit_index > 31)
4154 	{
4155 		Warning(LOCATION, "Bit index %d out of range!  Must be between 0 and 31.", bit_index);
4156 		return SEXP_NAN;
4157 	}
4158 
4159 	if (val & (1<<bit_index))
4160 		return SEXP_TRUE;
4161 	else
4162 		return SEXP_FALSE;
4163 }
4164 
4165 // Goober5000
sexp_bitwise_and(int node)4166 int sexp_bitwise_and(int node)
4167 {
4168 	int val = eval_num(node);
4169 
4170 	for (int n = CDR(node); n != -1; n = CDR(n))
4171 		val &= eval_num(n);
4172 
4173 	return val;
4174 }
4175 
4176 // Goober5000
sexp_bitwise_or(int node)4177 int sexp_bitwise_or(int node)
4178 {
4179 	int val = eval_num(node);
4180 
4181 	for (int n = CDR(node); n != -1; n = CDR(n))
4182 		val |= eval_num(n);
4183 
4184 	return val;
4185 }
4186 
4187 // Goober5000
sexp_bitwise_not(int node)4188 int sexp_bitwise_not(int node)
4189 {
4190 	int result = ~(eval_num(node));
4191 
4192 	// clear the sign bit
4193 	return result & INT_MAX;
4194 }
4195 
4196 // Goober5000
sexp_bitwise_xor(int node)4197 int sexp_bitwise_xor(int node)
4198 {
4199 	return eval_num(node) ^ eval_num(CDR(node));
4200 }
4201 
4202 // seeding added by Karajorma and Goober5000
rand_sexp(int n,bool multiple)4203 int rand_sexp(int n, bool multiple)
4204 {
4205 	int low, high, rand_num, seed;
4206 
4207 	if (n < 0)
4208 	{
4209 		Int3();
4210 		return 0;
4211 	}
4212 
4213 	// when getting a saved value
4214 	if (Sexp_nodes[n].value == SEXP_NUM_EVAL)
4215 	{
4216 		// don't regenerate new random number
4217 		return atoi(CTEXT(n));
4218 	}
4219 
4220 	low = eval_num(n);
4221 
4222 	// get high
4223 	high = eval_num(CDR(n));
4224 
4225 	// is there a seed provided?
4226 	if (CDDR(n) != -1)
4227 		seed = eval_num(CDDR(n));
4228 	else
4229 		seed = 0;
4230 
4231 	// get the random number
4232 	rand_num = rand_internal(low, high, seed);
4233 
4234 	// when saving the value
4235 	if (!multiple)
4236 	{
4237 		// set .value and .text so random number is generated only once.
4238 		Sexp_nodes[n].value = SEXP_NUM_EVAL;
4239 		sprintf(Sexp_nodes[n].text, "%d", rand_num);
4240 	}
4241 	// if this is multiple with a nonzero seed provided
4242 	else if (seed > 0)
4243 	{
4244 		// Set the seed to a new seeded random value. This will ensure that the next time the method
4245 		// is called it will return a predictable but different number from the previous time.
4246 		sprintf(Sexp_nodes[CDDR(n)].text, "%d", rand_internal(1, INT_MAX, seed));
4247 	}
4248 
4249 	return rand_num;
4250 }
4251 
4252 // boolean evaluation functions.  Evaluate all sexpressions in the 'or' operator.  Needed to mark
4253 // entries in the mission log as essential so when pruning the log, we know which entries we might
4254 // need to keep.
sexp_or(int n)4255 int sexp_or(int n)
4256 {
4257 	int all_false, result;
4258 
4259 	all_false = 1;
4260 	result = 0;
4261 	if (n != -1)
4262 	{
4263 		if (CAR(n) != -1)
4264 		{
4265 			result |= is_sexp_true(CAR(n));
4266 			if ( Sexp_nodes[CAR(n)].value == SEXP_KNOWN_TRUE )
4267 				return SEXP_KNOWN_TRUE;								// if one of the OR clauses is TRUE, whole clause is true
4268 			if ( Sexp_nodes[CAR(n)].value != SEXP_KNOWN_FALSE )		// if the value is still unknown, they all can't be false
4269 				all_false = 0;
4270 		}
4271 		else
4272 			result |= atoi(CTEXT(n));
4273 
4274 		while (CDR(n) != -1)
4275 		{
4276 			result |= is_sexp_true(CDR(n));
4277 			if ( Sexp_nodes[CDR(n)].value == SEXP_KNOWN_TRUE )
4278 				return SEXP_KNOWN_TRUE;								// if one of the OR clauses is TRUE, whole clause is true
4279 			if ( Sexp_nodes[CDR(n)].value != SEXP_KNOWN_FALSE )		// if the value is still unknown, they all can't be false
4280 				all_false = 0;
4281 
4282 			n = CDR(n);
4283 		}
4284 	}
4285 
4286 	if (all_false)
4287 		return SEXP_KNOWN_FALSE;
4288 
4289 	return result;
4290 }
4291 
4292 // this function does the 'and' operator.  It will short circuit evaluation  *but* it will still
4293 // evaluate other members of the and construct.  I do this because I need events in the mission log
4294 // to get marked as essential for goal purposes, and evaluation is pretty much the only way
sexp_and(int n)4295 int sexp_and(int n)
4296 {
4297 	int all_true, result;
4298 
4299 	result = -1;
4300 	all_true = 1;
4301 	if (n != -1)
4302 	{
4303 		if (CAR(n) != -1)
4304 		{
4305 			result &= is_sexp_true(CAR(n));
4306 			if ( Sexp_nodes[CAR(n)].value == SEXP_KNOWN_FALSE )
4307 				return SEXP_KNOWN_FALSE;							// if one of the AND clauses is FALSE, whole clause is false
4308 			if ( Sexp_nodes[CAR(n)].value != SEXP_KNOWN_TRUE )		// if the value is still unknown, they all can't be true
4309 				all_true = 0;
4310 		}
4311 		else
4312 			result &= atoi(CTEXT(n));
4313 
4314 		while (CDR(n) != -1)
4315 		{
4316 			int new_result;
4317 
4318 			new_result = is_sexp_true(CDR(n));
4319 			result &= new_result;
4320 			if ( Sexp_nodes[CDR(n)].value == SEXP_KNOWN_FALSE )
4321 				return SEXP_KNOWN_FALSE;							// if one of the AND clauses is FALSE, whole clause is false
4322 			if ( Sexp_nodes[CDR(n)].value != SEXP_KNOWN_TRUE )		// if the value is still unknown, they all can't be true
4323 				all_true = 0;
4324 
4325 			n = CDR(n);
4326 		}
4327 	}
4328 
4329 	if (all_true)
4330 		return SEXP_KNOWN_TRUE;
4331 
4332 	return result;
4333 }
4334 
4335 // this version of the 'and' operator determines whether or not it's arguments become true
4336 // in the order in which they are specified in the when statement.  Should be a simple matter of
4337 // seeing if anything evaluates to true later than something that evalueated to false
sexp_and_in_sequence(int n)4338 int sexp_and_in_sequence(int n)
4339 {
4340 	int result = -1;
4341 	int all_true;
4342 
4343 	all_true = 1;											// represents whether or not all nodes we have seen so far are true
4344 	if (n != -1)
4345 	{
4346 		if (CAR(n) != -1)
4347 		{
4348 			result &= is_sexp_true(CAR(n));
4349 			if ( Sexp_nodes[CAR(n)].value == SEXP_KNOWN_FALSE )
4350 				return SEXP_KNOWN_FALSE;														// if one of the AND clauses is FALSE, whole clause is false
4351 			if ( Sexp_nodes[CAR(n)].value != SEXP_KNOWN_TRUE )		// if value is true, mark our all_true variable for later checking
4352 				all_true = 0;
4353 		}
4354 		else
4355 			result &= atoi(CTEXT(n));
4356 
4357 		// a little test -- if the previous sexpressions was true, then mark the node itself as always
4358 		// true.  I did this because of the distance function.  It might become true, then when waiting for
4359 		// the second evalation, it might become false, rendering this function false.  So, when one becomes
4360 		// true -- mark it true forever.
4361 		if ( result )
4362 			Sexp_nodes[CAR(n)].value = SEXP_KNOWN_TRUE;
4363 
4364 		while (CDR(n) != -1)
4365 		{
4366 			int next_result;
4367 
4368 			next_result = is_sexp_true(CDR(n));
4369 			if ( next_result && !result )				// if current result is true, and our running result is false, thngs didn't become true in order
4370 				return SEXP_KNOWN_FALSE;
4371 			result &= next_result;
4372 			if ( Sexp_nodes[CDR(n)].value == SEXP_KNOWN_FALSE )
4373 				return SEXP_KNOWN_FALSE;															// if one of the OR clauses is TRUE, whole clause is true
4374 			if ( Sexp_nodes[CDR(n)].value != SEXP_KNOWN_TRUE )				// if the value is still unknown, they all can't be false
4375 				all_true = 0;
4376 
4377 			// see comment above for explanation of next lines
4378 			if ( result )
4379 				Sexp_nodes[CDR(n)].value = SEXP_KNOWN_TRUE;
4380 
4381 			n = CDR(n);
4382 		}
4383 	}
4384 
4385 	if ( all_true )
4386 		return SEXP_KNOWN_TRUE;
4387 
4388 	return result;
4389 }
4390 
4391 // for these four basic boolean operations (not, <, >, and =), we have special cases that we must deal
4392 // with.  We have sexpressions operators that might return a NAN type return value (such as the distance
4393 // between two ships when one of the ships is destroyed or departed).  These operations need to check for
4394 // this special NAN value and adjust their return types accordingly.  NAN values represent false return values
sexp_not(int n)4395 int sexp_not(int n)
4396 {
4397 	int result = 0;
4398 
4399 	if (n != -1)
4400 	{
4401 		if (CAR(n) != -1)
4402 		{
4403 			result = is_sexp_true(CAR(n));
4404 			if ( Sexp_nodes[CAR(n)].value == SEXP_KNOWN_FALSE )
4405 				return SEXP_KNOWN_TRUE;												// not KNOWN_FALSE == KNOWN_TRUE;
4406 			else if ( Sexp_nodes[CAR(n)].value == SEXP_KNOWN_TRUE )		// not KNOWN_TRUE == KNOWN_FALSE
4407 				return SEXP_KNOWN_FALSE;
4408 			else if ( Sexp_nodes[CAR(n)].value == SEXP_NAN )				// not NAN == TRUE (I think)
4409 				return SEXP_TRUE;
4410 			else if ( Sexp_nodes[CAR(n)].value == SEXP_NAN_FOREVER )
4411 				return SEXP_TRUE;
4412 		}
4413 		else
4414 			result = atoi(CTEXT(n));
4415 	}
4416 
4417 	return !result;
4418 }
4419 
sexp_xor(int node)4420 int sexp_xor(int node)
4421 {
4422 	int num_true = 0;
4423 
4424 	for (int n = node; n != -1; n = CDR(n))
4425 	{
4426 		if (is_sexp_true(n))
4427 			num_true++;
4428 	}
4429 
4430 	return (num_true == 1);
4431 }
4432 
4433 // Goober5000
sexp_number_compare(int n,int op)4434 int sexp_number_compare(int n, int op)
4435 {
4436 	int first_node = n;
4437 	int current_node;
4438 	int first_number = eval_sexp(first_node);
4439 	int current_number;
4440 
4441 	// bail on NANs
4442 	if (CAR(first_node) != -1)
4443 	{
4444 		if (Sexp_nodes[CAR(first_node)].value == SEXP_NAN) return SEXP_FALSE;
4445 		if (Sexp_nodes[CAR(first_node)].value == SEXP_NAN_FOREVER) return SEXP_KNOWN_FALSE;
4446 	}
4447 	if (CDR(first_node) != -1)
4448 	{
4449 		if (Sexp_nodes[CDR(first_node)].value == SEXP_NAN) return SEXP_FALSE;
4450 		if (Sexp_nodes[CDR(first_node)].value == SEXP_NAN_FOREVER) return SEXP_KNOWN_FALSE;
4451 	}
4452 
4453 	// compare first node with each of the others
4454 	for (current_node = CDR(first_node); current_node != -1; current_node = CDR(current_node))
4455 	{
4456 		// bail on NANs
4457 		if (CAR(current_node) != -1)
4458 		{
4459 			if (Sexp_nodes[CAR(current_node)].value == SEXP_NAN) return SEXP_FALSE;
4460 			if (Sexp_nodes[CAR(current_node)].value == SEXP_NAN_FOREVER) return SEXP_KNOWN_FALSE;
4461 		}
4462 		if (CDR(current_node) != -1)
4463 		{
4464 			if (Sexp_nodes[CDR(current_node)].value == SEXP_NAN) return SEXP_FALSE;
4465 			if (Sexp_nodes[CDR(current_node)].value == SEXP_NAN_FOREVER) return SEXP_KNOWN_FALSE;
4466 		}
4467 
4468 		current_number = eval_sexp(current_node);
4469 
4470 		// must satisfy our particular operator
4471 		switch(op)
4472 		{
4473 			case OP_EQUALS:
4474 				if (first_number != current_number) return SEXP_FALSE;
4475 				break;
4476 
4477 			case OP_NOT_EQUAL:
4478 				if (first_number == current_number) return SEXP_FALSE;
4479 				break;
4480 
4481 			case OP_GREATER_THAN:
4482 				if (first_number <= current_number) return SEXP_FALSE;
4483 				break;
4484 
4485 			case OP_GREATER_OR_EQUAL:
4486 				if (first_number < current_number) return SEXP_FALSE;
4487 				break;
4488 
4489 			case OP_LESS_THAN:
4490 				if (first_number >= current_number) return SEXP_FALSE;
4491 				break;
4492 
4493 			case OP_LESS_OR_EQUAL:
4494 				if (first_number > current_number) return SEXP_FALSE;
4495 				break;
4496 
4497 			default:
4498 				Warning(LOCATION, "Unhandled comparison case!  Operator = ", op);
4499 				break;
4500 		}
4501 	}
4502 
4503 	// it satisfies the operator for all the arguments
4504 	return SEXP_TRUE;
4505 }
4506 
4507 // Goober5000
sexp_string_compare(int n,int op)4508 int sexp_string_compare(int n, int op)
4509 {
4510 	int first_node = n;
4511 	int current_node;
4512 	int val;
4513 	char *first_string = CTEXT(first_node);
4514 
4515 	// compare first node with each of the others
4516 	for (current_node = CDR(first_node); current_node != -1; current_node = CDR(current_node))
4517 	{
4518 		val = strcmp(first_string, CTEXT(current_node));
4519 
4520 		// must satisfy our particular operator
4521 		switch(op)
4522 		{
4523 			case OP_STRING_EQUALS:
4524 				if (val != 0) return SEXP_FALSE;
4525 				break;
4526 
4527 			case OP_STRING_GREATER_THAN:
4528 				if (val <= 0) return SEXP_FALSE;
4529 				break;
4530 
4531 			case OP_STRING_LESS_THAN:
4532 				if (val >= 0) return SEXP_FALSE;
4533 				break;
4534 		}
4535 	}
4536 
4537 	// it satisfies the operator for all the arguments
4538 	return SEXP_TRUE;
4539 }
4540 
4541 #define OSWPT_TYPE_NONE				0
4542 #define OSWPT_TYPE_SHIP				1
4543 #define OSWPT_TYPE_WING				2
4544 #define OSWPT_TYPE_WAYPOINT			3
4545 #define OSWPT_TYPE_SHIP_ON_TEAM		4	// e.g. <any friendly>
4546 #define OSWPT_TYPE_WHOLE_TEAM		5	// e.g. Friendly
4547 #define OSWPT_TYPE_PARSE_OBJECT		6	// a "ship" that hasn't arrived yet
4548 #define OSWPT_TYPE_EXITED			7
4549 #define OSWPT_TYPE_WING_NOT_PRESENT	8	// a wing that hasn't arrived yet or is between waves
4550 
4551 // Goober5000
4552 typedef struct object_ship_wing_point_team
4553 {
4554 	char *object_name;
4555 	int type;
4556 
4557 	p_object *p_objp;
4558 	object *objp;
4559 	ship *shipp;
4560 	wing *wingp;
4561 	waypoint *waypointp;
4562 	int team;
4563 
4564 	void clear();
4565 }
4566 object_ship_wing_point_team;
4567 
clear()4568 void object_ship_wing_point_team::clear()
4569 {
4570 	object_name = NULL;
4571 	type = OSWPT_TYPE_NONE;
4572 
4573 	p_objp = NULL;
4574 	objp = NULL;
4575 	shipp = NULL;
4576 	waypointp = NULL;
4577 	wingp = NULL;
4578 	team = -1;
4579 }
4580 
4581 void sexp_object_ship_wing_point_team_set_ship(object_ship_wing_point_team *oswpt, ship *shipp, bool set_parse_flag_too = false);
4582 void sexp_object_ship_wing_point_team_set_ship(object_ship_wing_point_team *oswpt, ship_obj *so, bool set_parse_flag_too = false);
4583 void sexp_get_object_ship_wing_point_team(object_ship_wing_point_team *oswpt, char *object_name, bool set_parse_flag_too = false);
4584 
object_ship_wing_point_team_set_ship(object_ship_wing_point_team * oswpt,ship * shipp,bool set_parse_flag_too)4585 void object_ship_wing_point_team_set_ship(object_ship_wing_point_team *oswpt, ship *shipp, bool set_parse_flag_too)
4586 {
4587 	oswpt->clear();
4588 
4589 	oswpt->shipp = shipp;
4590 	oswpt->object_name = oswpt->shipp->ship_name;
4591 	oswpt->objp = &Objects[shipp->objnum];
4592 	oswpt->type = OSWPT_TYPE_SHIP;
4593 
4594 	if (set_parse_flag_too) {
4595 		oswpt->p_objp = mission_parse_get_arrival_ship(oswpt->object_name);
4596 	}
4597 }
4598 
4599 
object_ship_wing_point_team_set_ship(object_ship_wing_point_team * oswpt,ship_obj * so,bool set_parse_flag_too)4600 void object_ship_wing_point_team_set_ship(object_ship_wing_point_team *oswpt, ship_obj *so, bool set_parse_flag_too)
4601 {
4602 	object_ship_wing_point_team_set_ship(oswpt, &Ships[Objects[so->objnum].instance], set_parse_flag_too);
4603 }
4604 
4605 
4606 // Goober5000
sexp_get_object_ship_wing_point_team(object_ship_wing_point_team * oswpt,char * object_name,bool set_parse_flag_too)4607 void sexp_get_object_ship_wing_point_team(object_ship_wing_point_team *oswpt, char *object_name, bool set_parse_flag_too)
4608 {
4609 	int team, ship_num, wing_num;
4610 	waypoint *wpt;
4611 	p_object *p_objp;
4612 
4613 	Assert(oswpt != NULL);
4614 	Assert(object_name != NULL);
4615 
4616 	oswpt->clear();
4617 	oswpt->object_name = object_name;
4618 
4619 	if(!stricmp(object_name, SEXP_NONE_STRING))
4620 	{
4621 		oswpt->type = OSWPT_TYPE_NONE;
4622 		return;
4623 	}
4624 
4625 	// check to see if ship destroyed or departed.  In either case, do nothing.
4626 	if (mission_log_get_time(LOG_SHIP_DEPARTED, object_name, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, object_name, NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, object_name, NULL, NULL))
4627 	{
4628 		oswpt->type = OSWPT_TYPE_EXITED;
4629 		return;
4630 	}
4631 
4632 	// the object might be the name of a wing.  Check to see if the wing is destroyed or departed.
4633 	if (mission_log_get_time(LOG_WING_DESTROYED, object_name, NULL, NULL) || mission_log_get_time(LOG_WING_DEPARTED, object_name, NULL, NULL))
4634 	{
4635 		oswpt->type = OSWPT_TYPE_EXITED;
4636 		return;
4637 	}
4638 
4639 
4640 	// check if we have a ship for a target
4641 	ship_num = ship_name_lookup(object_name);
4642 	if (ship_num >= 0)
4643 	{
4644 		oswpt->type = OSWPT_TYPE_SHIP;
4645 
4646 		oswpt->shipp = &Ships[ship_num];
4647 		oswpt->objp = &Objects[oswpt->shipp->objnum];
4648 
4649 		if (!set_parse_flag_too) {
4650 			return;
4651 		}
4652 	}
4653 
4654 
4655 	// check to see if we have a parse object instead
4656 	p_objp = mission_parse_get_arrival_ship(object_name);
4657 	if (p_objp != NULL)
4658 	{
4659 		if (oswpt->type != OSWPT_TYPE_SHIP) {
4660 			oswpt->type = OSWPT_TYPE_PARSE_OBJECT;
4661 		}
4662 
4663 		oswpt->p_objp = p_objp;
4664 
4665 		return;
4666 	}
4667 
4668 
4669 	// check if we have a wing for a target
4670 	wing_num = wing_name_lookup(object_name, 1);
4671 	if (wing_num >= 0)
4672 	{
4673 		wing *wingp = &Wings[wing_num];
4674 
4675 		// make sure that at least one ship exists
4676 		if (wingp->current_count > 0)
4677 		{
4678 			oswpt->type = OSWPT_TYPE_WING;
4679 			oswpt->wingp = wingp;
4680 
4681 			// point to wing leader if he is valid
4682 			if ((wingp->special_ship >= 0) && (wingp->ship_index[wingp->special_ship] >= 0))
4683 			{
4684 				oswpt->shipp = &Ships[wingp->ship_index[wingp->special_ship]];
4685 				oswpt->objp = &Objects[oswpt->shipp->objnum];
4686 			}
4687 			// boo... well, just point to ship at index 0
4688 			else
4689 			{
4690 				oswpt->shipp = &Ships[wingp->ship_index[0]];
4691 				oswpt->objp = &Objects[oswpt->shipp->objnum];
4692 				Warning(LOCATION, "Substituting ship '%s' at index 0 for nonexistent wing leader at index %d!", oswpt->shipp->ship_name, oswpt->wingp->special_ship);
4693 			}
4694 		}
4695 		// it's still a valid wing even if nobody is here
4696 		else
4697 		{
4698 			oswpt->type = OSWPT_TYPE_WING_NOT_PRESENT;
4699 			oswpt->wingp = wingp;
4700 		}
4701 
4702 		return;
4703 	}
4704 
4705 
4706 	// check if we have a point for a target
4707 	wpt = find_matching_waypoint(object_name);
4708 	if ((wpt != NULL) && (wpt->get_objnum() >= 0))
4709 	{
4710 		oswpt->type = OSWPT_TYPE_WAYPOINT;
4711 
4712 		oswpt->waypointp = wpt;
4713 		oswpt->objp = &Objects[wpt->get_objnum()];
4714 
4715 		return;
4716 	}
4717 
4718 
4719 	// check if we have an "<any team>" type
4720 	team = sexp_determine_team(object_name);
4721 	if (team >= 0)
4722 	{
4723 		oswpt->type = OSWPT_TYPE_SHIP_ON_TEAM;
4724 		oswpt->team = team;
4725 	}
4726 
4727 
4728 	// check if we have a whole-team type
4729 	team = iff_lookup(object_name);
4730 	if (team >= 0)
4731 	{
4732 		oswpt->type = OSWPT_TYPE_WHOLE_TEAM;
4733 		oswpt->team = team;
4734 	}
4735 
4736 
4737 	// we apparently don't have anything legal
4738 	return;
4739 }
4740 
4741 /**
4742  * Return the number of ships of a given team in the area battle
4743  */
sexp_num_ships_in_battle(int n)4744 int sexp_num_ships_in_battle(int n)
4745 {
4746 	int count=0;
4747 	ship_obj	*so;
4748 	ship		*shipp;
4749 	object_ship_wing_point_team oswpt1;
4750 	if ( n == -1) {
4751     	for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
4752 		    shipp=&Ships[Objects[so->objnum].instance];
4753 		    count++;
4754 	    }
4755 
4756 	    return count;
4757 	}
4758 
4759 	while (n != -1) {
4760 		sexp_get_object_ship_wing_point_team(&oswpt1, CTEXT(n));
4761 
4762 	    switch (oswpt1.type){
4763  		    case OSWPT_TYPE_WHOLE_TEAM:
4764 			  for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
4765 				  shipp=&Ships[Objects[so->objnum].instance];
4766 				  if (shipp->team == oswpt1.team) {
4767 					 count++;
4768 				  }
4769 			  }
4770 			  break;
4771 
4772   		    case OSWPT_TYPE_SHIP:
4773 			  count++;
4774 			  break;
4775 
4776 		    case OSWPT_TYPE_WING:
4777 			  count += oswpt1.wingp->current_count;
4778 			  break;
4779 	    }
4780 
4781         n = CDR(n);
4782 	}
4783 
4784 	return count;
4785 }
4786 
4787 /**
4788  * Return the number of ships of a given wing or wings in the battle area
4789  */
sexp_num_ships_in_wing(int n)4790 int sexp_num_ships_in_wing(int n)
4791 {
4792 	char *name;
4793 	int num_ships = 0 ;
4794 
4795 	// A wing name must be provided, Assert that there is one.
4796 	Assert ( n != -1 );
4797 
4798 	//Cycle through the list of ships given
4799 	while (n != -1)
4800 	{
4801 		// Get the name of the wing
4802 		name = CTEXT(n);
4803 
4804 		int wingnum;
4805 		wingnum = wing_name_lookup( name, 0 );
4806 
4807 		//If the wing exists add the number of ships in it to the total
4808 		if (wingnum > -1)
4809 		{
4810 			num_ships += Wings[wingnum].current_count ;
4811 		}
4812 
4813 		//get the next node
4814 		n = CDR (n) ;
4815 	}
4816 
4817 	return num_ships ;
4818 }
4819 
4820 /**
4821  * Gets the 'real' speed of an object, taking into account docking
4822  */
sexp_get_real_speed(object * obj)4823 int sexp_get_real_speed(object *obj)
4824 {
4825 	return fl2i(dock_calc_docked_speed(obj));
4826 }
4827 
4828 /**
4829  * Gets the current speed of the specified object
4830  *
4831  * Uses a lot of code shamelessly ripped from get_object_coordinates
4832  */
sexp_current_speed(int n)4833 int sexp_current_speed(int n)
4834 {
4835 	object_ship_wing_point_team oswpt;
4836 	sexp_get_object_ship_wing_point_team(&oswpt, CTEXT(n));
4837 
4838 	switch (oswpt.type)
4839 	{
4840 		case OSWPT_TYPE_EXITED:
4841 			return SEXP_NAN;
4842 
4843 		case OSWPT_TYPE_SHIP:
4844 		case OSWPT_TYPE_WING:
4845 			return sexp_get_real_speed(oswpt.objp);
4846 	}
4847 
4848 	return 0;
4849 }
4850 
4851 /**
4852  * Evaluate if given ship is destroyed.
4853  * @return true if the ship in the expression has been destroyed.
4854  */
sexp_is_destroyed(int n,fix * latest_time)4855 int sexp_is_destroyed(int n, fix *latest_time)
4856 {
4857 	char	*name;
4858 	int	count, num_destroyed, wing_index;
4859 	fix	time;
4860 
4861 	Assert ( n != -1 );
4862 
4863 	count = 0;
4864 	num_destroyed = 0;
4865 	wing_index = -1;
4866 	while (n != -1) {
4867 		count++;
4868 		name = CTEXT(n);
4869 
4870 		if (sexp_query_has_yet_to_arrive(name))
4871 			return SEXP_CANT_EVAL;
4872 
4873 		// check to see if this ship/wing has departed.  If so, then function is known false
4874 		if ( mission_log_get_time (LOG_SHIP_DEPARTED, name, NULL, NULL) || mission_log_get_time (LOG_WING_DEPARTED, name, NULL, NULL) )
4875 			return SEXP_KNOWN_FALSE;
4876 
4877 		// check the mission log.  If ship/wing not destroyed, immediately return SEXP_FALSE.
4878 		if ( mission_log_get_time(LOG_SHIP_DESTROYED, name, NULL, &time) || mission_log_get_time(LOG_WING_DESTROYED, name, NULL, &time) || mission_log_get_time(LOG_SELF_DESTRUCTED, name, NULL, &time)) {
4879 			num_destroyed++;
4880 			if ( latest_time && (time > *latest_time) )
4881 				*latest_time = time;
4882 		} else {
4883 			// ship or wing isn't destroyed -- add to directive count
4884 			if ( (wing_index = wing_name_lookup( name, 1 )) >= 0 ) {
4885 				Directive_count += Wings[wing_index].current_count;
4886 			} else
4887 				Directive_count++;
4888 		}
4889 
4890 		// move to next ship/wing in list
4891 		n = CDR(n);
4892 	}
4893 
4894 	// special case to mark a directive for destroy wing objectives true after a short amount
4895 	// of time when there are more waves for this wing.
4896 	if ( (count == 1) && (wing_index >= 0) && (Directive_count == 0) ) {
4897 		if ( Wings[wing_index].current_wave < Wings[wing_index].num_waves )
4898 			Directive_count =	DIRECTIVE_WING_ZERO;
4899 	}
4900 
4901 	if ( count == num_destroyed )
4902 		return SEXP_KNOWN_TRUE;
4903 	else
4904 		return SEXP_FALSE;
4905 }
4906 
4907 /**
4908  * Return true if the subsystem of the given ship has been destroyed
4909  */
sexp_is_subsystem_destroyed(int n)4910 int sexp_is_subsystem_destroyed(int n)
4911 {
4912 	char *ship_name, *subsys_name;
4913 
4914 	Assert( n != -1 );
4915 
4916 	ship_name = CTEXT(n);
4917 	subsys_name = CTEXT(CDR(n));
4918 
4919 	if (sexp_query_has_yet_to_arrive(ship_name))
4920 		return SEXP_CANT_EVAL;
4921 
4922 	// if the ship has departed, no way to destroy it's subsystem.
4923 	if ( mission_log_get_time(LOG_SHIP_DEPARTED, ship_name, NULL, NULL ))
4924 		return SEXP_KNOWN_FALSE;
4925 
4926 	if ( mission_log_get_time(LOG_SHIP_SUBSYS_DESTROYED, ship_name, subsys_name, NULL) )
4927 		return SEXP_KNOWN_TRUE;
4928 
4929 	return SEXP_FALSE;
4930 
4931 }
4932 
4933 /**
4934  * Determine if a ship has arrived onto the scene
4935  */
sexp_has_arrived(int n,fix * latest_time)4936 int sexp_has_arrived(int n, fix *latest_time)
4937 {
4938 	char *name;
4939 	int	count, num_arrived;
4940 	fix	time;
4941 
4942 	count = 0;
4943 	num_arrived = 0;
4944 	while ( n != -1 ) {
4945 		count++;
4946 		name = CTEXT(n);
4947 		// if there is no log entry for this ship/wing for arrival, sexpression is false
4948 		if ( mission_log_get_time(LOG_SHIP_ARRIVED, name, NULL, &time) || mission_log_get_time(LOG_WING_ARRIVED, name, NULL, &time) ) {
4949 			num_arrived++;
4950 			if ( latest_time && (time > *latest_time) )
4951 				*latest_time = time;
4952 		}
4953 		n = CDR(n);
4954 	}
4955 
4956 	if ( count == num_arrived )
4957 		return SEXP_KNOWN_TRUE;
4958 	else
4959 		return SEXP_FALSE;
4960 }
4961 
4962 /**
4963  * Determine if a ship/wing has departed
4964  */
sexp_has_departed(int n,fix * latest_time)4965 int sexp_has_departed(int n, fix *latest_time)
4966 {
4967 	char *name;
4968 	int count, num_departed;
4969 	fix time;
4970 
4971 	count = 0;
4972 	num_departed = 0;
4973 	while ( n != -1 ) {
4974 		count++;
4975 		name = CTEXT(n);
4976 
4977 		if (sexp_query_has_yet_to_arrive(name))
4978 			return SEXP_CANT_EVAL;
4979 
4980 		// if ship/wing destroyed, sexpression is known false.  Also, if there is no departure log entry, then
4981 		// the sexpression is not true.
4982 		if ( mission_log_get_time(LOG_SHIP_DESTROYED, name, NULL, NULL) || mission_log_get_time(LOG_WING_DESTROYED, name, NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, name, NULL, NULL))
4983 			return SEXP_KNOWN_FALSE;
4984 		else if ( mission_log_get_time(LOG_SHIP_DEPARTED, name, NULL, &time) || mission_log_get_time(LOG_WING_DEPARTED, name, NULL, &time) ) {
4985 			num_departed++;
4986 			if ( latest_time && (time > *latest_time) )
4987 				*latest_time = time;
4988 		}
4989 		n = CDR(n);
4990 	}
4991 
4992 	if ( count == num_departed )
4993 		return SEXP_KNOWN_TRUE;
4994 	else
4995 		return SEXP_FALSE;
4996 }
4997 
4998 /**
4999  * Determine if a ship is disabled
5000  */
sexp_is_disabled(int n,fix * latest_time)5001 int sexp_is_disabled( int n, fix *latest_time )
5002 {
5003 	char *name;
5004 	int count, num_disabled;
5005 	fix time;
5006 
5007 	count = 0;
5008 	num_disabled = 0;
5009 	while ( n != -1 ) {
5010 		count++;
5011 		name = CTEXT(n);
5012 
5013 		if (sexp_query_has_yet_to_arrive(name))
5014 			return SEXP_CANT_EVAL;
5015 
5016 		// if ship/wing destroyed, sexpression is known false.  Also, if there is no disable log entry, then
5017 		// the sexpression is not true.
5018 		if ( mission_log_get_time(LOG_SHIP_DEPARTED, name, NULL, &time) || mission_log_get_time(LOG_SHIP_DESTROYED, name, NULL, &time) || mission_log_get_time(LOG_SELF_DESTRUCTED, name, NULL, &time) )
5019 			return SEXP_KNOWN_FALSE;
5020 		else if ( mission_log_get_time(LOG_SHIP_DISABLED, name, NULL, &time) ) {
5021 			num_disabled++;
5022 			if ( latest_time && (time > *latest_time) )
5023 				*latest_time = time;
5024 		}
5025 		n = CDR(n);
5026 	}
5027 
5028 	if ( count == num_disabled )
5029 		return SEXP_KNOWN_TRUE;
5030 	else
5031 		return SEXP_FALSE;
5032 }
5033 
5034 /**
5035  * Determine if a ship is done flying waypoints
5036  */
sexp_are_waypoints_done(int n)5037 int sexp_are_waypoints_done(int n)
5038 {
5039 	char *ship_name, *waypoint_name;
5040 
5041 	ship_name = CTEXT(n);
5042 	waypoint_name = CTEXT(CDR(n));
5043 
5044 	if (sexp_query_has_yet_to_arrive(ship_name))
5045 		return SEXP_CANT_EVAL;
5046 
5047 	// a destroyed or departed ship will never reach their goal -- return known false
5048 	if ( mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, ship_name, NULL, NULL) )
5049 		return SEXP_KNOWN_FALSE;
5050 	else if ( mission_log_get_time(LOG_SHIP_DEPARTED, ship_name, NULL, NULL) )
5051 		return SEXP_KNOWN_FALSE;
5052 
5053 	// now check the log for the waypoints done entry
5054 	if ( mission_log_get_time(LOG_WAYPOINTS_DONE, ship_name, waypoint_name, NULL) )
5055 		return SEXP_KNOWN_TRUE;
5056 
5057 	return SEXP_FALSE;
5058 }
5059 
5060 
5061 /**
5062  * Determine if ships are disarmed
5063  */
sexp_is_disarmed(int n,fix * latest_time)5064 int sexp_is_disarmed( int n, fix *latest_time )
5065 {
5066 	char *name;
5067 	int count, num_disarmed;
5068 	fix time;
5069 
5070 	count = 0;
5071 	num_disarmed = 0;
5072 	while ( n != -1 ) {
5073 		count++;
5074 		name = CTEXT(n);
5075 
5076 		if (sexp_query_has_yet_to_arrive(name))
5077 			return SEXP_CANT_EVAL;
5078 
5079 		// if ship/wing destroyed, sexpression is known false.  Also, if there is no disarm log entry, then
5080 		// the sexpression is not true.
5081 		if ( mission_log_get_time(LOG_SHIP_DEPARTED, name, NULL, &time) || mission_log_get_time(LOG_SHIP_DESTROYED, name, NULL, &time) || mission_log_get_time(LOG_SELF_DESTRUCTED, name, NULL, &time) )
5082 			return SEXP_KNOWN_FALSE;
5083 		else if ( mission_log_get_time(LOG_SHIP_DISARMED, name, NULL, &time) ) {
5084 			num_disarmed++;
5085 			if ( latest_time && (time > *latest_time) )
5086 				*latest_time = time;
5087 		}
5088 		n = CDR(n);
5089 	}
5090 
5091 	if ( count == num_disarmed )
5092 		return SEXP_KNOWN_TRUE;
5093 	else
5094 		return SEXP_FALSE;
5095 }
5096 
5097 // the following functions are similar to the above objective functions but return true/false
5098 // if N seconds have elasped after the corresponding function is true.
sexp_is_destroyed_delay(int n)5099 int sexp_is_destroyed_delay(int n)
5100 {
5101 	fix delay, time;
5102 	int val;
5103 
5104 	Assert ( n >= 0 );
5105 
5106 	time = 0;
5107 
5108 	delay = i2f(eval_num(n));
5109 
5110 	// check value of is_destroyed function.  KNOWN_FALSE should be returned immediately
5111 	val = sexp_is_destroyed( CDR(n), &time );
5112 	if ( val == SEXP_KNOWN_FALSE )
5113 		return val;
5114 
5115 	if ( val == SEXP_CANT_EVAL )
5116 		return SEXP_CANT_EVAL;
5117 
5118 	if ( val ) {
5119 
5120 		if ( (Missiontime - time) >= delay )
5121 			return SEXP_KNOWN_TRUE;
5122 	}
5123 
5124 	return SEXP_FALSE;
5125 }
5126 
5127 
5128 // First ship is the destroyer, rest of the arguments are the destroyed ships.
sexp_was_destroyed_by(int n,fix * latest_time)5129 int sexp_was_destroyed_by(int n, fix* latest_time)
5130 {
5131 	char* destroyer_ship_name;
5132 	char* destroyed_ship_name;
5133 	int count = 0, num_destroyed;
5134 	fix time;
5135 
5136 	Assert(n != -1);
5137 
5138 	destroyer_ship_name = CTEXT(n);
5139 
5140 	num_destroyed = 0;
5141 
5142 	for (n = CDR(n); n != -1; n = CDR(n))
5143 	{
5144 		count++;
5145 		destroyed_ship_name = CTEXT(n);
5146 
5147 		if (sexp_query_has_yet_to_arrive(destroyed_ship_name))
5148 			return SEXP_CANT_EVAL;
5149 
5150 		// check to see if this ship/wing has departed.  If so, then function is known false
5151 		if (mission_log_get_time(LOG_SHIP_DEPARTED, destroyed_ship_name, NULL, NULL))
5152 			return SEXP_KNOWN_FALSE;
5153 
5154 		// check the mission log.  If ship/wing not destroyed, immediately return SEXP_FALSE.
5155 		if (mission_log_get_time(LOG_SHIP_DESTROYED, destroyed_ship_name, destroyer_ship_name, &time))
5156 		{
5157 			num_destroyed++;
5158 			if (latest_time && (time > *latest_time))
5159 				*latest_time = time;
5160 		}
5161 	}
5162 
5163 	if (count == num_destroyed)
5164 		return SEXP_KNOWN_TRUE;
5165 	else
5166 		return SEXP_FALSE;
5167 }
5168 
sexp_was_destroyed_by_delay(int n)5169 int sexp_was_destroyed_by_delay(int n)
5170 {
5171 	fix delay, time;
5172 	int val;
5173 
5174 	Assert(n >= 0);
5175 
5176 	time = 0;
5177 
5178 	delay = i2f(eval_num(n));
5179 
5180 	// check value of is_destroyed function.  KNOWN_FALSE should be returned immediately
5181 	val = sexp_was_destroyed_by(CDR(n), &time);
5182 	if (val == SEXP_KNOWN_FALSE)
5183 		return val;
5184 
5185 	if (val == SEXP_CANT_EVAL)
5186 		return SEXP_CANT_EVAL;
5187 
5188 	if (val)
5189 	{
5190 
5191 		if ((Missiontime - time) >= delay)
5192 			return SEXP_KNOWN_TRUE;
5193 	}
5194 
5195 	return SEXP_FALSE;
5196 }
5197 
sexp_is_subsystem_destroyed_delay(int n)5198 int sexp_is_subsystem_destroyed_delay(int n)
5199 {
5200 	char *ship_name, *subsys_name;
5201 	fix delay, time;
5202 
5203 	Assert( n != -1 );
5204 
5205 	ship_name = CTEXT(n);
5206 	subsys_name = CTEXT(CDR(n));
5207 	delay = i2f(eval_num(CDR(CDR(n))));
5208 
5209 	if (sexp_query_has_yet_to_arrive(ship_name))
5210 		return SEXP_CANT_EVAL;
5211 
5212 	// if the ship has departed, no way to destroy it's subsystem.
5213 	if ( mission_log_get_time(LOG_SHIP_DEPARTED, ship_name, NULL, NULL ))
5214 		return SEXP_KNOWN_FALSE;
5215 
5216 	if ( mission_log_get_time(LOG_SHIP_SUBSYS_DESTROYED, ship_name, subsys_name, &time) ) {
5217 		if ( (Missiontime - time) >= delay )
5218 			return SEXP_KNOWN_TRUE;
5219 	}
5220 
5221 	return SEXP_FALSE;
5222 }
5223 
sexp_is_disabled_delay(int n)5224 int sexp_is_disabled_delay(int n)
5225 {
5226 	fix delay, time;
5227 	int val;
5228 
5229 	Assert ( n >= 0 );
5230 
5231 	time = 0;
5232 	delay = i2f(eval_num(n));
5233 
5234 	// check value of is_disable for known false and return immediately if it is.
5235 	val = sexp_is_disabled( CDR(n), &time );
5236 	if ( val == SEXP_KNOWN_FALSE )
5237 		return val;
5238 
5239 	if ( val == SEXP_CANT_EVAL )
5240 		return SEXP_CANT_EVAL;
5241 
5242 	if ( val ) {
5243 		if ( (Missiontime - time) >= delay )
5244 			return SEXP_KNOWN_TRUE;
5245 	}
5246 
5247 	return SEXP_FALSE;
5248 }
5249 
sexp_is_disarmed_delay(int n)5250 int sexp_is_disarmed_delay(int n)
5251 {
5252 	fix delay, time;
5253 	int val;
5254 
5255 	Assert ( n >= 0 );
5256 
5257 	time = 0;
5258 	delay = i2f(eval_num(n));
5259 
5260 	// check value of is_disarmed for a known false value and return that immediately if it is
5261 	val = sexp_is_disarmed( CDR(n), &time );
5262 	if ( val == SEXP_KNOWN_FALSE )
5263 		return val;
5264 
5265 	if ( val == SEXP_CANT_EVAL )
5266 		return SEXP_CANT_EVAL;
5267 
5268 	if ( val ) {
5269 		if ( (Missiontime - time) >= delay )
5270 			return SEXP_KNOWN_TRUE;
5271 	}
5272 
5273 	return SEXP_FALSE;
5274 }
5275 
sexp_has_docked_or_undocked(int n,int op_num)5276 int sexp_has_docked_or_undocked(int n, int op_num)
5277 {
5278 	Assert(op_num == OP_HAS_DOCKED || op_num == OP_HAS_UNDOCKED || op_num == OP_HAS_DOCKED_DELAY || op_num == OP_HAS_UNDOCKED_DELAY);
5279 
5280 	char *docker = CTEXT(n);
5281 	char *dockee = CTEXT(CDR(n));
5282 	int count = eval_num(CDR(CDR(n)));		// count of times that we should look for
5283 
5284 	if (count <= 0)
5285 	{
5286 		Warning(LOCATION, "Has-%sdocked%s count should be at least 1!  This has been automatically adjusted.", (op_num == OP_HAS_UNDOCKED || op_num == OP_HAS_UNDOCKED_DELAY ? "un" : ""), (op_num == OP_HAS_DOCKED_DELAY || op_num == OP_HAS_UNDOCKED_DELAY ? "-delay" : ""));
5287 		count = 1;
5288 	}
5289 
5290 	if (sexp_query_has_yet_to_arrive(docker))
5291 		return SEXP_CANT_EVAL;
5292 
5293 	if (sexp_query_has_yet_to_arrive(dockee))
5294 		return SEXP_CANT_EVAL;
5295 
5296 	if (op_num == OP_HAS_DOCKED_DELAY || op_num == OP_HAS_UNDOCKED_DELAY)
5297 	{
5298 		fix delay = i2f(eval_num(CDR(CDR(CDR(n)))));
5299 		fix time;
5300 
5301 		if ( mission_log_get_time_indexed(op_num == OP_HAS_DOCKED_DELAY ? LOG_SHIP_DOCKED : LOG_SHIP_UNDOCKED, docker, dockee, count, &time) )
5302 		{
5303 			if ( (Missiontime - time) >= delay )
5304 				return SEXP_KNOWN_TRUE;
5305 		}
5306 	}
5307 	else
5308 	{
5309 		if ( mission_log_get_time_indexed(op_num == OP_HAS_DOCKED ? LOG_SHIP_DOCKED : LOG_SHIP_UNDOCKED, docker, dockee, count, NULL) )
5310 			return SEXP_KNOWN_TRUE;
5311 	}
5312 
5313 	if ( mission_log_get_time(LOG_SHIP_DESTROYED, docker, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, dockee, NULL, NULL) )
5314 		return SEXP_KNOWN_FALSE;
5315 
5316 	if ( mission_log_get_time(LOG_SELF_DESTRUCTED, docker, NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, dockee, NULL, NULL) )
5317 		return SEXP_KNOWN_FALSE;
5318 
5319 	return SEXP_FALSE;
5320 }
5321 
sexp_has_arrived_delay(int n)5322 int sexp_has_arrived_delay(int n)
5323 {
5324 	fix delay, time;
5325 	int val;
5326 
5327 	Assert ( n >= 0 );
5328 
5329 	time = 0;
5330 	delay = i2f(eval_num(n));
5331 
5332 	// check return value from arrived function.  if can never arrive, then return that value here as well
5333 	val = sexp_has_arrived( CDR(n), &time );
5334 	if ( val == SEXP_KNOWN_FALSE )
5335 		return val;
5336 
5337 	if ( val == SEXP_CANT_EVAL )
5338 		return SEXP_CANT_EVAL;
5339 
5340 	if ( val ) {
5341 		if ( (Missiontime - time) >= delay )
5342 			return SEXP_KNOWN_TRUE;
5343 	}
5344 
5345 	return SEXP_FALSE;
5346 }
5347 
sexp_has_departed_delay(int n)5348 int sexp_has_departed_delay(int n)
5349 {
5350 	fix delay, time;
5351 	int val;
5352 
5353 	Assert ( n >= 0 );
5354 
5355 	time = 0;
5356 	delay = i2f(eval_num(n));
5357 
5358 	// must first check to see if the departed function could ever be true/false or is true or false.
5359 	// if it can never be true, return that value
5360 	val = sexp_has_departed( CDR(n), &time);
5361 	if ( val == SEXP_KNOWN_FALSE )
5362 		return val;
5363 
5364 	if ( val == SEXP_CANT_EVAL )
5365 		return SEXP_CANT_EVAL;
5366 
5367 	if ( val ) {
5368 		if ( (Missiontime - time) >= delay )
5369 			return SEXP_KNOWN_TRUE;
5370 	}
5371 
5372 	return SEXP_FALSE;
5373 }
5374 
5375 /**
5376  * Determine if a ship is done flying waypoints after N seconds
5377  */
sexp_are_waypoints_done_delay(int node)5378 int sexp_are_waypoints_done_delay(int node)
5379 {
5380 	char *ship_name, *waypoint_name;
5381 	int count, n = node;
5382 	fix time, delay;
5383 
5384 	ship_name = CTEXT(n);
5385 	n = CDR(n);
5386 	waypoint_name = CTEXT(n);
5387 	n = CDR(n);
5388 	delay = i2f(eval_num(n));
5389 	n = CDR(n);
5390 	count = (n >= 0) ? eval_num(n) : 1;
5391 	if (count <= 0)
5392 	{
5393 		Warning(LOCATION, "Are-waypoints-done-delay count should be at least 1!  This has been automatically adjusted.");
5394 		count = 1;
5395 	}
5396 
5397 	if (sexp_query_has_yet_to_arrive(ship_name))
5398 		return SEXP_CANT_EVAL;
5399 
5400 	// a destroyed or departed ship will never reach their goal -- return known false
5401 	//
5402 	// Not checking the entries below.  Ships which warp out after reaching their goal (or getting
5403 	// destroyed after their goal), but after reaching their waypoints, may have this goal incorrectly
5404 	// marked false!!!!
5405 
5406 	// now check the log for the waypoints done entry
5407 	if ( mission_log_get_time_indexed(LOG_WAYPOINTS_DONE, ship_name, waypoint_name, count, &time) ) {
5408 		if ( (Missiontime - time) >= delay )
5409 			return SEXP_KNOWN_TRUE;
5410 	} else {
5411 		if ( mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, ship_name, NULL, NULL) )
5412 			return SEXP_KNOWN_FALSE;
5413 		else if ( mission_log_get_time(LOG_SHIP_DEPARTED, ship_name, NULL, NULL) )
5414 			return SEXP_KNOWN_FALSE;
5415 	}
5416 
5417 	return SEXP_FALSE;
5418 }
5419 
5420 /**
5421  * Determine is all of a given ship type are destroyed
5422  */
sexp_ship_type_destroyed(int n)5423 int sexp_ship_type_destroyed(int n)
5424 {
5425 	int percent;
5426 	int type;
5427 	char *shiptype;
5428 
5429 	percent = eval_num(n);
5430 	shiptype = CTEXT(CDR(n));
5431 
5432 	type = ship_type_name_lookup(shiptype);
5433 
5434 	// bogus if we reach the end of this array!!!!
5435 	if ( type < 0 ) {
5436 		Warning(LOCATION, "Invalid shiptype passed to ship-type-destroyed");
5437 		return SEXP_FALSE;
5438 	}
5439 
5440 	if ( type >= (int)Ship_type_counts.size() || Ship_type_counts[type].total == 0 )
5441 		return SEXP_FALSE;
5442 
5443 	//We are safe from array indexing probs b/c of previous if.
5444 	// determine if the percentage of killed/total is >= percentage given in the expression
5445 	if ( (Ship_type_counts[type].killed * 100 / Ship_type_counts[type].total) >= percent)
5446 		return SEXP_KNOWN_TRUE;
5447 
5448 	return SEXP_FALSE;
5449 }
5450 
5451 
5452 // following are time based functions
sexp_has_time_elapsed(int n)5453 int sexp_has_time_elapsed(int n)
5454 {
5455 	int time = eval_num(n);
5456 
5457 	if ( f2i(Missiontime) >= time )
5458 		return SEXP_KNOWN_TRUE;
5459 
5460 	return SEXP_FALSE;
5461 }
5462 
5463 /**
5464  * Returns the time into the mission
5465  */
sexp_mission_time()5466 int sexp_mission_time()
5467 {
5468 	return f2i(Missiontime);
5469 }
5470 
5471 /**
5472  * Returns the time into the mission, in milliseconds
5473  */
sexp_mission_time_msecs()5474 int sexp_mission_time_msecs()
5475 {
5476 	// multiplying by 1000 can go over the limit for LONG_MAX so cast to long long int first
5477 	return f2i((longlong)Missiontime * 1000);
5478 }
5479 
5480 /**
5481  * Returns percent of length of distance to special warpout plane
5482  */
sexp_special_warp_dist(int n)5483 int sexp_special_warp_dist( int n)
5484 {
5485 	char *ship_name;
5486 	int shipnum;
5487 
5488 	// get shipname
5489 	ship_name = CTEXT(n);
5490 
5491 	// check to see if either ship was destroyed or departed.  If so, then make this node known false
5492 	if ( mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) || mission_log_get_time( LOG_SHIP_DEPARTED, ship_name, NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, ship_name, NULL, NULL) ) {
5493 		return SEXP_NAN_FOREVER;
5494 	}
5495 
5496 	// get ship name
5497 	shipnum = ship_name_lookup(ship_name);
5498 	if (shipnum < 0) {
5499 		return SEXP_NAN;
5500 	}
5501 
5502 	// check that ship has warpout_objnum
5503 	if (Ships[shipnum].special_warpout_objnum < 0) {
5504 		return SEXP_NAN;
5505 	}
5506 
5507 	Assert( (Ships[shipnum].special_warpout_objnum >= 0) && (Ships[shipnum].special_warpout_objnum < MAX_OBJECTS));
5508 	if ( (Ships[shipnum].special_warpout_objnum < 0) && (Ships[shipnum].special_warpout_objnum >= MAX_OBJECTS) ) {
5509 		return SEXP_NAN;
5510 	}
5511 
5512 	// check the special warpout device is valid
5513 	int valid = FALSE;
5514 	object *ship_objp = &Objects[Ships[shipnum].objnum];
5515 	object *warp_objp = &Objects[Ships[shipnum].special_warpout_objnum];
5516 	if (warp_objp->type == OBJ_SHIP) {
5517 		if (Ship_info[Ships[warp_objp->instance].ship_info_index].flags & SIF_KNOSSOS_DEVICE) {
5518 			valid = TRUE;
5519 		}
5520 	}
5521 
5522 	if (!valid) {
5523 		return SEXP_NAN;
5524 	}
5525 
5526 	// check if within 45 degree half-angle cone of facing
5527 	float dot = fl_abs(vm_vec_dotprod(&warp_objp->orient.vec.fvec, &ship_objp->orient.vec.fvec));
5528 	if (dot < 0.707f) {
5529 		return SEXP_NAN;
5530 	}
5531 
5532 	// get distance
5533 	vec3d hit_pt;
5534 	float dist = fvi_ray_plane(&hit_pt, &warp_objp->pos, &warp_objp->orient.vec.fvec, &ship_objp->pos, &ship_objp->orient.vec.fvec, 0.0f);
5535 	polymodel *pm = model_get(Ship_info[Ships[shipnum].ship_info_index].model_num);
5536 	dist += pm->mins.xyz.z;
5537 
5538 	// return as a percent of length
5539 	return (int) (100.0f * dist / ship_class_get_length(&Ship_info[Ships[shipnum].ship_info_index]));
5540 }
5541 
5542 
sexp_time_destroyed(int n)5543 int sexp_time_destroyed(int n)
5544 {
5545 	fix time;
5546 
5547 	if ( !mission_log_get_time( LOG_SHIP_DESTROYED, CTEXT(n), NULL, &time)
5548 		&& !mission_log_get_time( LOG_SELF_DESTRUCTED, CTEXT(n), NULL, &time) ) {		// returns 0 when not found
5549 		return SEXP_NAN;
5550 	}
5551 
5552 	return f2i(time);
5553 }
5554 
sexp_time_wing_destroyed(int n)5555 int sexp_time_wing_destroyed(int n)
5556 {
5557 	fix time;
5558 
5559 	if ( !mission_log_get_time( LOG_WING_DESTROYED, CTEXT(n), NULL, &time) ){
5560 		return SEXP_NAN;
5561 	}
5562 
5563 	return f2i(time);
5564 }
5565 
sexp_time_docked(int n)5566 int sexp_time_docked(int n)
5567 {
5568 	fix time;
5569 	char *docker = CTEXT(n);
5570 	char *dockee = CTEXT(CDR(n));
5571 	int count = eval_num(CDR(CDR(n)));
5572 
5573 	Assert ( count > 0 );
5574 	if ( !mission_log_get_time_indexed(LOG_SHIP_DOCKED, docker, dockee, count, &time) ){
5575 		return SEXP_NAN;
5576 	}
5577 
5578 	return f2i(time);
5579 }
5580 
sexp_time_undocked(int n)5581 int sexp_time_undocked(int n)
5582 {
5583 	fix time;
5584 	char *docker = CTEXT(n);
5585 	char *dockee = CTEXT(CDR(n));
5586 	int count = eval_num(CDR(CDR(n)));
5587 
5588 	Assert ( count > 0 );
5589 	if ( !mission_log_get_time_indexed(LOG_SHIP_UNDOCKED, docker, dockee, count, &time) ){
5590 		return SEXP_NAN;
5591 	}
5592 
5593 	return f2i(time);
5594 }
5595 
sexp_time_ship_arrived(int n)5596 int sexp_time_ship_arrived(int n)
5597 {
5598 	fix time;
5599 
5600 	Assert( n != -1 );
5601 	if ( !mission_log_get_time( LOG_SHIP_ARRIVED, CTEXT(n), NULL, &time ) ){
5602 		return SEXP_NAN;
5603 	}
5604 
5605 	return f2i(time);
5606 }
5607 
sexp_time_wing_arrived(int n)5608 int sexp_time_wing_arrived(int n)
5609 {
5610 	fix time;
5611 
5612 	Assert( n != -1 );
5613 	if ( !mission_log_get_time( LOG_WING_ARRIVED, CTEXT(n), NULL, &time ) ){
5614 		return SEXP_NAN;
5615 	}
5616 
5617 	return f2i(time);
5618 }
5619 
sexp_time_ship_departed(int n)5620 int sexp_time_ship_departed(int n)
5621 {
5622 	fix time;
5623 
5624 	Assert( n != -1 );
5625 	if ( !mission_log_get_time( LOG_SHIP_DEPARTED, CTEXT(n), NULL, &time ) ){
5626 		return SEXP_NAN;
5627 	}
5628 
5629 	return f2i(time);
5630 }
5631 
sexp_time_wing_departed(int n)5632 int sexp_time_wing_departed(int n)
5633 {
5634 	fix time;
5635 
5636 	Assert( n != -1 );
5637 	if ( !mission_log_get_time(LOG_WING_DEPARTED, CTEXT(n), NULL, &time ) ){
5638 		return SEXP_NAN;
5639 	}
5640 
5641 	return f2i(time);
5642 }
5643 
sexp_set_energy_pct(int node,int op_num)5644 void sexp_set_energy_pct (int node, int op_num)
5645 {
5646 	int sindex;
5647 	float new_pct;
5648 	char *shipname;
5649 	ship * shipp;
5650 	ship_info * sip;
5651 
5652 	Assert (node > -1);
5653 	new_pct = eval_num(node) / 100.0f;
5654 
5655 	// deal with ridiculous percentages
5656     CLAMP(new_pct, 0.0f, 1.0f);
5657 
5658 	// only need to send a packet for afterburners because shields and weapon energy are sent from server to clients
5659 	if (MULTIPLAYER_MASTER && (op_num == OP_SET_AFTERBURNER_ENERGY)) {
5660 		multi_start_callback();
5661 		multi_send_float(new_pct);
5662 	}
5663 
5664 	node = CDR(node);
5665 
5666 	while (node > -1) {
5667 		// get the ship
5668 		shipname = CTEXT(node);
5669 
5670 		sindex = ship_name_lookup( shipname );
5671 		if ( sindex == -1 ){					// hmm.. if true, must not have arrived yet
5672 			node = CDR(node);
5673 			continue;
5674 		}
5675 
5676 		shipp = &Ships[sindex];
5677 		sip = &Ship_info[Ships[sindex].ship_info_index];
5678 
5679 		switch (op_num) {
5680 			case OP_SET_AFTERBURNER_ENERGY:
5681 				shipp->afterburner_fuel = sip->afterburner_fuel_capacity * new_pct;
5682 				break;
5683 
5684 			case OP_SET_WEAPON_ENERGY:
5685 				if (!(ship_has_energy_weapons(shipp)) ) {
5686 					node = CDR(node);
5687 					continue;
5688 				}
5689 
5690 				shipp->weapon_energy = sip->max_weapon_reserve * new_pct;
5691 				break;
5692 
5693 			case OP_SET_SHIELD_ENERGY:
5694 				if (shipp->ship_max_shield_strength == 0.0f) {
5695 					node = CDR(node);
5696 					continue;
5697 				}
5698 
5699 				shield_set_strength(&Objects[shipp->objnum], (shipp->ship_max_shield_strength * new_pct));
5700 				break;
5701 		}
5702 
5703 		if (MULTIPLAYER_MASTER && (op_num == OP_SET_AFTERBURNER_ENERGY)) {
5704 			multi_send_ship(shipp);
5705 		}
5706 
5707 		node = CDR(node);
5708 	}
5709 
5710 	if (MULTIPLAYER_MASTER && (op_num == OP_SET_AFTERBURNER_ENERGY)) {
5711 		multi_end_callback();
5712 	}
5713 }
5714 
multi_sexp_set_energy_pct()5715 void multi_sexp_set_energy_pct()
5716 {
5717 	ship *shipp;
5718 	float new_pct;
5719 	ship_info * sip;
5720 
5721 	int op_num = multi_sexp_get_operator();
5722 
5723 	multi_get_float(new_pct);
5724 	while (multi_get_ship(shipp)) {
5725 		sip = &Ship_info[shipp->ship_info_index];
5726 
5727 		switch (op_num) {
5728 			case OP_SET_AFTERBURNER_ENERGY:
5729 				shipp->afterburner_fuel = sip->afterburner_fuel_capacity * new_pct;
5730 				break;
5731 
5732 			case OP_SET_WEAPON_ENERGY:
5733 				shipp->weapon_energy = sip->max_weapon_reserve * new_pct;
5734 				break;
5735 
5736 			case OP_SET_SHIELD_ENERGY:
5737 				shield_set_strength(&Objects[shipp->objnum], (shipp->ship_max_shield_strength * new_pct));
5738 				break;
5739 		}
5740 	}
5741 }
5742 
5743 
sexp_get_energy_pct(int node,int op_num)5744 int sexp_get_energy_pct (int node, int op_num)
5745 {
5746 	int sindex;
5747 	float maximum = 0.0f, current = 0.0f;
5748 	ship * shipp;
5749 	ship_info * sip;
5750 	char *shipname;
5751 
5752 	// get the ship
5753 	shipname = CTEXT(node);
5754 
5755 	sindex = ship_name_lookup( shipname );
5756 	if ( sindex == -1 ){					// has either not arrived yet or has departed
5757 		return SEXP_NAN;
5758 	}
5759 
5760 	shipp = &Ships[sindex];
5761 	sip = &Ship_info[Ships[sindex].ship_info_index];
5762 
5763 	switch (op_num) {
5764 		case OP_AFTERBURNER_LEFT:
5765 			maximum = sip->afterburner_fuel_capacity;
5766 			current = shipp->afterburner_fuel;
5767 			break;
5768 		case OP_WEAPON_ENERGY_LEFT:
5769 			if (ship_has_energy_weapons(shipp)) {
5770 				maximum = sip->max_weapon_reserve;
5771 				current = shipp->weapon_energy;
5772 			}
5773 			break;
5774 	}
5775 	if (maximum < WEAPON_RESERVE_THRESHOLD || current < WEAPON_RESERVE_THRESHOLD) {
5776 		return 0;
5777 	}
5778 
5779 	return (int)(100 * (current/maximum));
5780 }
5781 
5782 /**
5783  * Return the remaining shields as a percentage of the given ship.
5784  */
sexp_shields_left(int n)5785 int sexp_shields_left(int n)
5786 {
5787 	int shipnum, percent;
5788 	char *shipname;
5789 
5790 	shipname = CTEXT(n);
5791 
5792 	// if ship is gone or departed, cannot ever evaluate properly.  Return NAN_FOREVER
5793 	if ( mission_log_get_time(LOG_SHIP_DESTROYED, shipname, NULL, NULL) || mission_log_get_time( LOG_SHIP_DEPARTED, shipname, NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, shipname, NULL, NULL) ) {
5794 		return SEXP_NAN_FOREVER;
5795 	}
5796 
5797 	shipnum = ship_name_lookup( shipname );
5798 	if ( shipnum == -1 ){					// hmm.. if true, must not have arrived yet
5799 		return SEXP_NAN;
5800 	}
5801 
5802 	// Goober5000: in case ship has no shields
5803 	if (Ships[shipnum].ship_max_shield_strength == 0.0f)
5804 	{
5805 		return 0;
5806 	}
5807 
5808 	// now return the amount of shields left as a percentage of the whole.
5809 	percent = fl2i((get_shield_pct(&Objects[Ships[shipnum].objnum]) * 100.0f) + 0.5f);
5810 	return percent;
5811 }
5812 
5813 /**
5814  * Return the remaining hits left as a percentage of the whole.
5815  *
5816  * This hit amount counts for all hits on the ship (hull + subsystems).  Use hits_left_hull to find hull hits remaining.
5817  */
sexp_hits_left(int n)5818 int sexp_hits_left(int n)
5819 {
5820 	int shipnum, percent;
5821 	char *shipname;
5822 
5823 	shipname = CTEXT(n);
5824 
5825 	// if ship is gone or departed, cannot ever evaluate properly.  Return NAN_FOREVER
5826 	if ( mission_log_get_time(LOG_SHIP_DESTROYED, shipname, NULL, NULL) || mission_log_get_time( LOG_SHIP_DEPARTED, shipname, NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, shipname, NULL, NULL) ) {
5827 		return SEXP_NAN_FOREVER;
5828 	}
5829 
5830 	shipnum = ship_name_lookup( shipname );
5831 	if ( shipnum == -1 ){					// hmm.. if true, must not have arrived yet
5832 		return SEXP_NAN;
5833 	}
5834 
5835 	// now return the amount of hits left as a percentage of the whole.  Subtract the percentage from 100
5836 	// since we are working with total hit points taken, not total remaining.
5837 	ship		*shipp = &Ships[shipnum];
5838 	object	*objp = &Objects[shipp->objnum];
5839 	percent = fl2i((100.0f * get_hull_pct(objp)) + 0.5f);
5840 	return percent;
5841 }
5842 
sexp_sim_hits_left(int n)5843 int sexp_sim_hits_left(int n)
5844 {
5845 	int shipnum, percent;
5846 	char *shipname;
5847 
5848 	shipname = CTEXT(n);
5849 
5850 	// if ship is gone or departed, cannot ever evaluate properly.  Return NAN_FOREVER
5851 	if ( mission_log_get_time(LOG_SHIP_DESTROYED, shipname, NULL, NULL) || mission_log_get_time( LOG_SHIP_DEPARTED, shipname, NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, shipname, NULL, NULL) ) {
5852 		return SEXP_NAN_FOREVER;
5853 	}
5854 
5855 	shipnum = ship_name_lookup( shipname );
5856 	if ( shipnum == -1 ){					// hmm.. if true, must not have arrived yet
5857 		return SEXP_NAN;
5858 	}
5859 
5860 	// now return the amount of hits left as a percentage of the whole.  Subtract the percentage from 100
5861 	// since we are working with total hit points taken, not total remaining.
5862 	ship		*shipp = &Ships[shipnum];
5863 	object	*objp = &Objects[shipp->objnum];
5864 	percent = fl2i((100.0f * get_sim_hull_pct(objp)) + 0.5f);
5865 	return percent;
5866 }
5867 
5868 /**
5869  * Determine if ship visible on radar
5870  *
5871  * @return 0 - not visible
5872  * @return 1 - marginally targetable (jiggly on radar)
5873  * @return 2 - fully targetable
5874  */
sexp_is_ship_visible(int n)5875 int sexp_is_ship_visible(int n)
5876 {
5877 	char *shipname;
5878 	int shipnum;
5879 	int ship_is_visible = 0;
5880 
5881 	// if multiplayer, bail
5882 	if (Game_mode & GM_MULTIPLAYER) {
5883 		return SEXP_NAN_FOREVER;
5884 	}
5885 
5886 	shipname = CTEXT(n);
5887 
5888 	// if ship is gone or departed, cannot ever evaluate properly.  Return NAN_FOREVER
5889 	if ( mission_log_get_time(LOG_SHIP_DESTROYED, shipname, NULL, NULL) || mission_log_get_time( LOG_SHIP_DEPARTED, shipname, NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, shipname, NULL, NULL) ) {
5890 		return SEXP_NAN_FOREVER;
5891 	}
5892 
5893 	shipnum = ship_name_lookup( shipname );
5894 	if ( shipnum == -1 ){					// hmm.. if true, must not have arrived yet
5895 		return SEXP_NAN;
5896 	}
5897 
5898 	// get ship's *radar* visiblity
5899 	if (Player_ship != NULL)
5900 	{
5901 		if (ship_is_visible_by_team(&Objects[Ships[shipnum].objnum], Player_ship))
5902 		{
5903 			ship_is_visible = 2;
5904 		}
5905 	}
5906 
5907 	// only check awacs level if ship is not visible by team
5908 	if (Player_ship != NULL && !ship_is_visible) {
5909 		float awacs_level = awacs_get_level(&Objects[Ships[shipnum].objnum], Player_ship);
5910 		if (awacs_level >= 1.0f) {
5911 			ship_is_visible = 2;
5912 		} else if (awacs_level > 0) {
5913 			ship_is_visible = 1;
5914 		}
5915 	}
5916 
5917 	return ship_is_visible;
5918 }
5919 
5920 /**
5921  * Determine if the stealth flag set on this ship
5922  */
sexp_is_ship_stealthy(int n)5923 int sexp_is_ship_stealthy(int n)
5924 {
5925 	char *shipname;
5926 	int shipnum;
5927 
5928 	shipname = CTEXT(n);
5929 
5930 	// if ship is gone or departed, cannot ever evaluate properly.  Return NAN_FOREVER
5931 	if ( mission_log_get_time(LOG_SHIP_DESTROYED, shipname, NULL, NULL) || mission_log_get_time( LOG_SHIP_DEPARTED, shipname, NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, shipname, NULL, NULL) ) {
5932 		return SEXP_NAN_FOREVER;
5933 	}
5934 
5935 	shipnum = ship_name_lookup( shipname );
5936 	if ( shipnum == -1 ) {					// hmm.. if true, must not have arrived yet
5937 		return SEXP_NAN;
5938 	}
5939 
5940 	if (Ships[shipnum].flags2 & SF2_STEALTH)
5941 		return SEXP_TRUE;
5942 	else
5943 		return SEXP_FALSE;
5944 }
5945 
5946 /**
5947  * Determine if the friendly stealth ship visible
5948  */
sexp_is_friendly_stealth_visible(int n)5949 int sexp_is_friendly_stealth_visible(int n)
5950 {
5951 	char *shipname;
5952 	int shipnum;
5953 
5954 	shipname = CTEXT(n);
5955 
5956 	// if ship is gone or departed, cannot ever evaluate properly.  Return NAN_FOREVER
5957 	if ( mission_log_get_time(LOG_SHIP_DESTROYED, shipname, NULL, NULL) || mission_log_get_time( LOG_SHIP_DEPARTED, shipname, NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, shipname, NULL, NULL) ) {
5958 		return SEXP_NAN_FOREVER;
5959 	}
5960 
5961 	shipnum = ship_name_lookup( shipname );
5962 	if ( shipnum == -1 ) {					// hmm.. if true, must not have arrived yet
5963 		return SEXP_NAN;
5964 	}
5965 
5966 	if (Ships[shipnum].flags2 & SF2_FRIENDLY_STEALTH_INVIS)
5967 		return SEXP_FALSE;
5968 	else
5969 		return SEXP_TRUE;
5970 }
5971 
5972 // get multi team v team score
5973 // if not multi team v team return 0
5974 // if invalid team return 0
sexp_team_score(int node)5975 int sexp_team_score(int node)
5976 {
5977 	// if multi t vs t
5978 	if (Game_mode & GM_MULTIPLAYER) {
5979 		if (Netgame.type_flags & NG_TYPE_TEAM) {
5980 
5981 			int team = eval_num(node);
5982 
5983 			// Teams can only be 1 or 2 at the moment but we should use Num_teams in case more become possible in the future
5984 			if (team <= 0 || team > Num_teams)
5985 			{
5986 				// invalid team index
5987 				Warning(LOCATION, "sexp-team-score: team %d is not a valid team #", team);
5988 				return 0;
5989 			}
5990 
5991 			return Multi_team_score[team - 1];
5992 		}
5993 	}
5994 
5995 	return 0;
5996 }
5997 
5998 
5999 /**
6000  * Return the remaining hits left on a subsystem as a percentage of the whole.
6001  *
6002  * Goober5000 - this sexp is DEPRECATED because it works just like the new hits-left-substem-generic
6003  */
sexp_hits_left_subsystem(int n)6004 int sexp_hits_left_subsystem(int n)
6005 {
6006 	int shipnum, percent, type, single_subsystem = 0;
6007 	char *shipname;
6008 	char *subsys_name;
6009 
6010 	shipname = CTEXT(n);
6011 
6012 	// if ship is gone or departed, cannot ever evaluate properly.  Return NAN_FOREVER
6013 	if ( mission_log_get_time(LOG_SHIP_DESTROYED, shipname, NULL, NULL) || mission_log_get_time( LOG_SHIP_DEPARTED, shipname, NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, shipname, NULL, NULL) ) {
6014 		return SEXP_NAN_FOREVER;
6015 	}
6016 
6017 	shipnum = ship_name_lookup( shipname );
6018 	if ( shipnum == -1 ){					// hmm.. if true, must not have arrived yet
6019 		return SEXP_NAN;
6020 	}
6021 
6022 	subsys_name = CTEXT(CDR(n));
6023 	ship_subsys *ss = ship_get_subsys(&Ships[shipnum], subsys_name);
6024 	if (ss != NULL)
6025 		type = ss->system_info->type;
6026 	else
6027 		type = SUBSYSTEM_NONE;
6028 
6029 	if ( (type >= 0) && (type < SUBSYSTEM_MAX) ) {
6030 		// check for the optional argument
6031 		n = CDDR(n);
6032 		if (n >= 0) {
6033 			single_subsystem = is_sexp_true(n);
6034 		}
6035 
6036 		// if the third option is present or if this is an unknown subsystem type we only want to find the percentage of the
6037 		// named subsystem
6038 		if (single_subsystem || (type == SUBSYSTEM_UNKNOWN)) {
6039 			if (ss != NULL) {
6040 				percent = fl2i((ss->current_hits / ss->max_hits * 100.0f) + 0.5f);
6041 				return percent;
6042 			}
6043 
6044 			// we reached end of ship subsys list without finding subsys_name
6045 			if (ship_class_unchanged(shipnum)) {
6046 				Error(LOCATION, "Invalid subsystem '%s' passed to hits-left-subsystem", subsys_name);
6047 			}
6048 			return SEXP_NAN;
6049 
6050 		// by default we return as a percentage the hits remaining on the subsystem as a whole (i.e. for 3 engines,
6051 		// we are returning the sum of the hits on the 3 engines)
6052 		} else {
6053 			percent = fl2i((ship_get_subsystem_strength(&Ships[shipnum],type) * 100.0f) + 0.5f);
6054 			return percent;
6055 		}
6056 	}
6057 	return SEXP_NAN;			// if for some strange reason, the type field of the subsystem is bogus
6058 }
6059 
6060 // Goober5000
sexp_hits_left_subsystem_generic(int node)6061 int sexp_hits_left_subsystem_generic(int node)
6062 {
6063 	int i, ship_num, subsys_type;
6064 	char *ship_name, *subsys_type_name;
6065 
6066 	ship_name = CTEXT(node);
6067 	subsys_type_name = CTEXT(CDR(node));
6068 
6069 	// if ship is gone or departed, cannot ever evaluate properly.  Return NAN_FOREVER
6070 	if (mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) || mission_log_get_time(LOG_SHIP_DEPARTED, ship_name, NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, ship_name, NULL, NULL)) {
6071 		return SEXP_NAN_FOREVER;
6072 	}
6073 
6074 	ship_num = ship_name_lookup(ship_name);
6075 	if (ship_num < 0) {
6076 		return SEXP_NAN;
6077 	}
6078 
6079 	// find subsystem type
6080 	subsys_type = -1;
6081 	for (i = 0; i < SUBSYSTEM_MAX; i++)
6082 	{
6083 		if (!stricmp(subsys_type_name, Subsystem_types[i]))
6084 			subsys_type = i;
6085 	}
6086 
6087 	// error checking
6088 	if (subsys_type < 0) {
6089 		Warning(LOCATION, "Subsystem type '%s' not recognized in hits-left-subsystem-generic!", subsys_type_name);
6090 		return SEXP_NAN_FOREVER;
6091 	} else if (subsys_type == SUBSYSTEM_NONE) {
6092 		// as you wish...?
6093 		return 0;
6094 	} else if (subsys_type == SUBSYSTEM_UNKNOWN) {
6095 		Warning(LOCATION, "Cannot use SUBSYSTEM_UNKNOWN in hits-left-subsystem-generic!");
6096 		return SEXP_NAN_FOREVER;
6097 	}
6098 
6099 	// return as a percentage the hits remaining on the subsystem as a whole (i.e. for 3 engines,
6100 	// we are returning the sum of the hits on the 3 engines)
6101 	return fl2i((ship_get_subsystem_strength(&Ships[ship_num], subsys_type) * 100.0f) + 0.5f);
6102 }
6103 
6104 // Goober5000
sexp_hits_left_subsystem_specific(int node)6105 int sexp_hits_left_subsystem_specific(int node)
6106 {
6107 	int ship_num;
6108 	char *ship_name, *subsys_name;
6109 	ship_subsys *ss;
6110 
6111 	ship_name = CTEXT(node);
6112 	subsys_name = CTEXT(CDR(node));
6113 
6114 	// if ship is gone or departed, cannot ever evaluate properly.  Return NAN_FOREVER
6115 	if (mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) || mission_log_get_time(LOG_SHIP_DEPARTED, ship_name, NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, ship_name, NULL, NULL)) {
6116 		return SEXP_NAN_FOREVER;
6117 	}
6118 
6119 	ship_num = ship_name_lookup(ship_name);
6120 	if (ship_num < 0) {
6121 		return SEXP_NAN;
6122 	}
6123 
6124 	// find subsystem
6125 	ss = ship_get_subsys(&Ships[ship_num], subsys_name);
6126 	if (ss != NULL) {
6127 		// return as a percentage the hits remaining on this subsystem only
6128 		return fl2i((ss->current_hits / ss->max_hits * 100.0f) + 0.5f);
6129 	}
6130 
6131 	// we reached end of ship subsys list without finding subsys_name
6132 	if (ship_class_unchanged(ship_num)) {
6133 		Error(LOCATION, "Invalid subsystem '%s' passed to hits-left-subsystem", subsys_name);
6134 	}
6135 	return SEXP_NAN;
6136 }
6137 
sexp_directive_value(int n)6138 int sexp_directive_value(int n)
6139 {
6140 	int replace_current_value = SEXP_TRUE;
6141 	int directive_value;
6142 
6143 	Assert(n >= 0);
6144 
6145 	directive_value = eval_num(n);
6146 
6147 	if ((directive_value == SEXP_NAN) || (directive_value == SEXP_NAN_FOREVER)) {
6148 		directive_value = 0;
6149 	}
6150 
6151 	n = CDR(n);
6152 	if (n > -1) {
6153 		replace_current_value = eval_sexp(n);
6154 	}
6155 
6156 	if ((replace_current_value == SEXP_KNOWN_FALSE) || (replace_current_value == SEXP_FALSE) ) {
6157 		Directive_count += directive_value;
6158 	}
6159 	else {
6160 		Directive_count = directive_value;
6161 	}
6162 
6163 
6164 	return SEXP_TRUE;
6165 }
6166 
sexp_determine_team(char * subj)6167 int sexp_determine_team(char *subj)
6168 {
6169 	int len;
6170 	char team_name[NAME_LENGTH];
6171 
6172 	// quick check
6173 	if (strnicmp(subj, "<any ", 5))
6174 		return -1;
6175 
6176 	// grab IFF (rest of string except for closing angle bracket)
6177 	len = strlen(subj + 5) - 1;
6178 	strncpy(team_name, subj + 5, len);
6179 	team_name[len] = '\0';
6180 
6181 	// find it
6182 	return iff_lookup(team_name);
6183 }
6184 
6185 /**
6186  * Check distance between two given objects
6187  */
sexp_distance3(object * objp1,object * objp2)6188 int sexp_distance3(object *objp1, object *objp2)
6189 {
6190 	// if either object isn't present in the mission now
6191 	if (objp1 == NULL || objp2 == NULL)
6192 		return SEXP_NAN;
6193 
6194 	if ((objp1->type == OBJ_SHIP) && (objp2->type == OBJ_SHIP))
6195 	{
6196 		if (Player_obj == objp1)
6197 			return (int) hud_find_target_distance(objp2, objp1);
6198 		else
6199 			return (int) hud_find_target_distance(objp1, objp2);
6200 	}
6201 	else
6202 	{
6203 		return (int) vm_vec_dist_quick(&objp1->pos, &objp2->pos);
6204 	}
6205 }
6206 
6207 /**
6208  * Check distance between a given ship and a given subject (ship, wing, any \<team\>).
6209  */
sexp_distance2(object * objp1,object_ship_wing_point_team * oswpt2)6210 int sexp_distance2(object *objp1, object_ship_wing_point_team *oswpt2)
6211 {
6212 	int dist, dist_min = 0, inited = 0;
6213 
6214 	switch (oswpt2->type)
6215 	{
6216 		// we have a ship-on-team type, so check all ships of that type
6217 		case OSWPT_TYPE_SHIP_ON_TEAM:
6218 		{
6219 			for (ship_obj *so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so))
6220 			{
6221 				if (Ships[Objects[so->objnum].instance].team == oswpt2->team)
6222 				{
6223 					dist = sexp_distance3(objp1, &Objects[so->objnum]);
6224 					if (dist != SEXP_NAN)
6225 					{
6226 						if (!inited || (dist < dist_min))
6227 						{
6228 							dist_min = dist;
6229 							inited = 1;
6230 						}
6231 					}
6232 				}
6233 			}
6234 
6235 			// no objects were checked
6236 			if (!inited)
6237 				return SEXP_NAN;
6238 
6239 			return dist_min;
6240 		}
6241 
6242 		// check ships and points
6243 		case OSWPT_TYPE_SHIP:
6244 		case OSWPT_TYPE_WAYPOINT:
6245 		{
6246 			return sexp_distance3(objp1, oswpt2->objp);
6247 		}
6248 
6249 		// check wings
6250 		case OSWPT_TYPE_WING:
6251 		{
6252 			for (int i = 0; i < oswpt2->wingp->current_count; i++)
6253 			{
6254 				dist = sexp_distance3(objp1, &Objects[Ships[oswpt2->wingp->ship_index[i]].objnum]);
6255 				if (dist != SEXP_NAN)
6256 				{
6257 					if (!inited || (dist < dist_min))
6258 					{
6259 						dist_min = dist;
6260 						inited = 1;
6261 					}
6262 				}
6263 			}
6264 
6265 			// no objects were checked
6266 			if (!inited)
6267 				return SEXP_NAN;
6268 
6269 			return dist_min;
6270 		}
6271 	}
6272 
6273 	return SEXP_NAN;
6274 }
6275 
6276 /**
6277  * Returns the distance between two objects.
6278  *
6279  * If a wing is specified as one (or both) of the arguments to this function, we are looking for the closest distance
6280  */
sexp_distance(int n)6281 int sexp_distance(int n)
6282 {
6283 	int dist, dist_min = 0, inited = 0;
6284 	object_ship_wing_point_team oswpt1, oswpt2;
6285 
6286 	sexp_get_object_ship_wing_point_team(&oswpt1, CTEXT(n));
6287 	sexp_get_object_ship_wing_point_team(&oswpt2, CTEXT(CDR(n)));
6288 
6289 	// check to see if either object was destroyed or departed
6290 	if (oswpt1.type == OSWPT_TYPE_EXITED || oswpt2.type == OSWPT_TYPE_EXITED)
6291 		return SEXP_NAN_FOREVER;
6292 
6293 	switch (oswpt1.type)
6294 	{
6295 		// we have a ship-on-team type, so check all ships of that type
6296 		case OSWPT_TYPE_SHIP_ON_TEAM:
6297 		{
6298 			for (ship_obj *so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so))
6299 			{
6300 				if (Ships[Objects[so->objnum].instance].team == oswpt1.team)
6301 				{
6302 					dist = sexp_distance2(&Objects[so->objnum], &oswpt2);
6303 					if (dist != SEXP_NAN)
6304 					{
6305 						if (!inited || (dist < dist_min))
6306 						{
6307 							dist_min = dist;
6308 							inited = 1;
6309 						}
6310 					}
6311 				}
6312 			}
6313 
6314 			// no objects were checked
6315 			if (!inited)
6316 				return SEXP_NAN;
6317 
6318 			return dist_min;
6319 		}
6320 
6321 		// check ships and points
6322 		case OSWPT_TYPE_SHIP:
6323 		case OSWPT_TYPE_WAYPOINT:
6324 		{
6325 			return sexp_distance2(oswpt1.objp, &oswpt2);
6326 		}
6327 
6328 		// check wings
6329 		case OSWPT_TYPE_WING:
6330 		{
6331 			for (int i = 0; i < oswpt1.wingp->current_count; i++)
6332 			{
6333 				dist = sexp_distance2(&Objects[Ships[oswpt1.wingp->ship_index[i]].objnum], &oswpt2);
6334 				if (dist != SEXP_NAN)
6335 				{
6336 					if (!inited || (dist < dist_min))
6337 					{
6338 						dist_min = dist;
6339 						inited = 1;
6340 					}
6341 				}
6342 			}
6343 
6344 			// no objects were checked
6345 			if (!inited)
6346 				return SEXP_NAN;
6347 
6348 			return dist_min;
6349 		}
6350 	}
6351 
6352 	return SEXP_NAN;
6353 }
6354 
6355 /**
6356  * Locate the subsystem on a ship - Goober5000
6357  *
6358  * Switched to a boolean so that it can report failure to do so
6359  */
sexp_get_subsystem_world_pos(vec3d * subsys_world_pos,int shipnum,char * subsys_name)6360 bool sexp_get_subsystem_world_pos(vec3d *subsys_world_pos, int shipnum, char *subsys_name)
6361 {
6362 	Assert(subsys_name);
6363 	Assert(subsys_world_pos);
6364 
6365 	if(shipnum < 0)
6366 	{
6367 		Error(LOCATION, "Error - nonexistent ship.\n");
6368 	}
6369 
6370 	// find the ship subsystem
6371 	ship_subsys *ss = ship_get_subsys(&Ships[shipnum], subsys_name);
6372 	if (ss != NULL)
6373 	{
6374 		// find world position of subsystem on this object (the ship)
6375 		get_subsystem_world_pos(&Objects[Ships[shipnum].objnum], ss, subsys_world_pos);
6376 		return true;
6377 	}
6378 
6379 	// we reached end of ship subsys list without finding subsys_name
6380 	if (ship_class_unchanged(shipnum)) {
6381 		// this ship should have had the subsystem named as it shouldn't have changed class
6382 		Error(LOCATION, "sexp_get_subsystem_world_pos could not find subsystem '%s'", subsys_name);
6383 	}
6384 	return false;
6385 }
6386 
6387 /**
6388  * Returns the distance between an object and a ship subsystem.
6389  *
6390  * If a wing is specified as the object argument to this function, we are looking for the closest distance
6391  */
sexp_distance_subsystem(int n)6392 int sexp_distance_subsystem(int n)
6393 {
6394 	int ship_with_subsys_num, dist, dist_min = 0, inited = 0;
6395 	char *ship_with_subsys_name, *subsys_name;
6396 	vec3d subsys_pos;
6397 	object_ship_wing_point_team oswpt;
6398 
6399 	sexp_get_object_ship_wing_point_team(&oswpt, CTEXT(n));
6400 	ship_with_subsys_name = CTEXT(CDR(n));
6401 	subsys_name = CTEXT(CDR(CDR(n)));
6402 
6403 	// for the ship with the subsystem - see if it was destroyed or departed
6404 	if (mission_log_get_time(LOG_SHIP_DESTROYED, ship_with_subsys_name, NULL, NULL) || mission_log_get_time(LOG_SHIP_DEPARTED, ship_with_subsys_name, NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, ship_with_subsys_name, NULL, NULL))
6405 		return SEXP_NAN_FOREVER;
6406 
6407 	// check the other ship too
6408 	if (oswpt.type == OSWPT_TYPE_EXITED)
6409 		return SEXP_NAN_FOREVER;
6410 
6411 	// for the ship with the subsystem - get its index
6412 	ship_with_subsys_num = ship_name_lookup(ship_with_subsys_name);
6413 	if (ship_with_subsys_num < 0)
6414 		return SEXP_NAN;
6415 
6416 	// get the subsystem's coordinates or bail if we can't
6417 	if (!sexp_get_subsystem_world_pos(&subsys_pos, ship_with_subsys_num, subsys_name))
6418 		return SEXP_NAN;
6419 
6420 	switch (oswpt.type)
6421 	{
6422 		// we have a ship-on-team type, so check all ships of that type
6423 		case OSWPT_TYPE_SHIP_ON_TEAM:
6424 		{
6425 			for (ship_obj *so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so))
6426 			{
6427 				if (Ships[Objects[so->objnum].instance].team == oswpt.team)
6428 				{
6429 					dist = (int) vm_vec_dist_quick(&Objects[so->objnum].pos, &subsys_pos);
6430 
6431 					if (!inited || (dist < dist_min))
6432 					{
6433 						dist_min = dist;
6434 						inited = 1;
6435 					}
6436 				}
6437 			}
6438 
6439 			// no objects were checked
6440 			if (!inited)
6441 				return SEXP_NAN;
6442 
6443 			return dist_min;
6444 		}
6445 
6446 		// check ships and points
6447 		case OSWPT_TYPE_SHIP:
6448 		case OSWPT_TYPE_WAYPOINT:
6449 		{
6450 			return (int) vm_vec_dist_quick(&oswpt.objp->pos, &subsys_pos);
6451 		}
6452 
6453 		// check wings
6454 		case OSWPT_TYPE_WING:
6455 		{
6456 			for (int i = 0; i < oswpt.wingp->current_count; i++)
6457 			{
6458 				dist = (int) vm_vec_dist_quick(&Objects[Ships[oswpt.wingp->ship_index[i]].objnum].pos, &subsys_pos);
6459 
6460 				if (!inited || (dist < dist_min))
6461 				{
6462 					dist_min = dist;
6463 					inited = 1;
6464 				}
6465 			}
6466 
6467 			// no objects were checked
6468 			if (!inited)
6469 				return SEXP_NAN;
6470 
6471 			return dist_min;
6472 		}
6473 	}
6474 
6475 	return SEXP_NAN;
6476 }
6477 
sexp_helper_is_within_box(float * box_vals,vec3d * pos)6478 bool sexp_helper_is_within_box(float *box_vals, vec3d *pos)
6479 {
6480 	int i;
6481 	for(i = 0; i < 3; i++)
6482 	{
6483 		if(pos->a1d[i] < (box_vals[i] - box_vals[i+3])
6484 			|| pos->a1d[i] > (box_vals[i] + box_vals[i+3]))
6485 		{
6486 			return false;
6487 		}
6488 	}
6489 
6490 	return true;
6491 }
6492 
sexp_num_within_box(int n)6493 int sexp_num_within_box(int n)
6494 {
6495 	float box_vals[6];//x,y,z,width,height,depth
6496 	char *ship_wing;
6497 	int i, idx;
6498 	int retval = 0;
6499 
6500 	for(i = 0; i < 6; i++)
6501 	{
6502 		box_vals[i] = i2fl(eval_num(n));
6503 		n = CDR(n);
6504 	}
6505 
6506 
6507 	for(;n != -1; n = CDR(n))
6508 	{
6509 		ship_wing = CTEXT(n);
6510 
6511 		idx = ship_name_lookup(ship_wing);
6512 		if(idx > -1)
6513 		{
6514 			if(sexp_helper_is_within_box(box_vals, &Objects[Ships[idx].objnum].pos))
6515 				retval++;
6516 		}
6517 		else
6518 		{
6519 			idx = wing_name_lookup(ship_wing);
6520 			if(idx > -1)
6521 			{
6522 				bool wing_check = true;
6523 				for(i = 0; i < Wings[idx].current_count; i++)
6524 				{
6525 					if(!sexp_helper_is_within_box(box_vals, &Objects[Ships[Wings[idx].ship_index[i]].objnum].pos))
6526 					{
6527 						wing_check = false;
6528 						break;
6529 					}
6530 				}
6531 
6532 				if(wing_check)
6533 					retval++;
6534 			}
6535 		}
6536 	}
6537 
6538 	return retval;
6539 }
6540 
6541 // Goober5000
sexp_set_object_speed(object * objp,int speed,int axis,int subjective)6542 void sexp_set_object_speed(object *objp, int speed, int axis, int subjective)
6543 {
6544 	Assert(axis >= 0 && axis <= 2);
6545 
6546 	if (subjective)
6547 	{
6548 		vec3d subjective_vel;
6549 
6550 		// translate objective into subjective velocity
6551 		vm_vec_rotate(&subjective_vel, &objp->phys_info.vel, &objp->orient);
6552 
6553 		// set it
6554 		subjective_vel.a1d[axis] = i2fl(speed);
6555 
6556 		// translate it back to objective
6557 		vm_vec_unrotate(&objp->phys_info.vel, &subjective_vel, &objp->orient);
6558 	}
6559 	else
6560 	{
6561 		objp->phys_info.vel.a1d[axis] = i2fl(speed);
6562 	}
6563 }
6564 
6565 // Goober5000
sexp_set_object_speed(int n,int axis)6566 void sexp_set_object_speed(int n, int axis)
6567 {
6568 	Assert(n >= 0);
6569 
6570 	int speed, subjective = 0;
6571 	object_ship_wing_point_team oswpt;
6572 
6573 	sexp_get_object_ship_wing_point_team(&oswpt, CTEXT(n));
6574 	n = CDR(n);
6575 
6576 	speed = eval_num(n);
6577 	n = CDR(n);
6578 
6579 	if (n >= 0)
6580 	{
6581 		subjective = is_sexp_true(n);
6582 		n = CDR(n);
6583 	}
6584 
6585 	switch (oswpt.type)
6586 	{
6587 		case OSWPT_TYPE_SHIP:
6588 		case OSWPT_TYPE_WING:
6589 		{
6590 			sexp_set_object_speed(oswpt.objp, speed, axis, subjective);
6591 
6592 			//CommanderDJ - we put the multiplayer callback stuff in here to prevent doing unnecessary checks clientside
6593 			multi_start_callback();
6594 			multi_send_object(oswpt.objp);
6595 			multi_send_int(speed);
6596 			multi_send_int(axis);
6597 			multi_send_int(subjective);
6598 			multi_end_callback();
6599 
6600 			break;
6601 		}
6602 	}
6603 }
6604 
6605 //CommanderDJ
multi_sexp_set_object_speed()6606 void multi_sexp_set_object_speed()
6607 {
6608 	object *objp;
6609 	int speed = 0, axis = 0, subjective = 0;
6610 
6611 	multi_get_object(objp);
6612 	multi_get_int(speed);
6613 	multi_get_int(axis);
6614 	multi_get_int(subjective);
6615 
6616 	sexp_set_object_speed(objp, speed, axis, subjective);
6617 }
6618 
sexp_get_object_speed(object * objp,int axis,int subjective)6619 int sexp_get_object_speed(object *objp, int axis, int subjective)
6620 {
6621 	Assertion(((axis >= 0) && (axis <= 2)), "Axis is out of range (%d)", axis);
6622 	int speed;
6623 
6624 	if (subjective)
6625 	{
6626 		// return the speed based on the orentation of the object
6627 		vec3d subjective_vel;
6628 		vm_vec_rotate(&subjective_vel, &objp->phys_info.vel, &objp->orient);
6629 		speed = fl2i(subjective_vel.a1d[axis]);
6630 		vm_vec_unrotate(&objp->phys_info.vel, &subjective_vel, &objp->orient);
6631 	}
6632 	else
6633 	{
6634 		// return the speed according to the grid
6635 		speed = fl2i(objp->phys_info.vel.a1d[axis]);
6636 	}
6637 	return speed;
6638 }
6639 
sexp_get_object_speed(int n,int axis)6640 int sexp_get_object_speed(int n, int axis)
6641 {
6642 	Assert(n >= 0);
6643 
6644 	int speed, subjective = 0;
6645 	object_ship_wing_point_team oswpt;
6646 
6647 	sexp_get_object_ship_wing_point_team(&oswpt, CTEXT(n));
6648 	n = CDR(n);
6649 
6650 	if (n >= 0)
6651 	{
6652 		subjective = is_sexp_true(n);
6653 		n = CDR(n);
6654 	}
6655 
6656 	switch (oswpt.type)
6657 	{
6658 		case OSWPT_TYPE_EXITED:
6659 			return SEXP_NAN_FOREVER;
6660 
6661 		case OSWPT_TYPE_SHIP:
6662 		case OSWPT_TYPE_WING:
6663 			speed = sexp_get_object_speed(oswpt.objp, axis, subjective);
6664 			break;
6665 
6666 		default:
6667 			return SEXP_NAN;
6668 			break;
6669 	}
6670 	return speed;
6671 }
6672 
6673 // Goober5000
sexp_calculate_coordinate(vec3d * origin,matrix * orient,vec3d * relative_location,int axis)6674 int sexp_calculate_coordinate(vec3d *origin, matrix *orient, vec3d *relative_location, int axis)
6675 {
6676 	Assert(origin != NULL);
6677 	Assert(orient != NULL);
6678 	Assert(axis >= 0 && axis <= 2);
6679 
6680 	if (relative_location == NULL)
6681 	{
6682 		return fl2i(origin->a1d[axis]);
6683 	}
6684 	else
6685 	{
6686 		vec3d new_world_pos;
6687 
6688 		vm_vec_unrotate(&new_world_pos, relative_location, orient);
6689 		vm_vec_add2(&new_world_pos, origin);
6690 
6691 		return fl2i(new_world_pos.a1d[axis]);
6692 	}
6693 }
6694 
6695 // Goober5000
sexp_calculate_angle(matrix * orient,int axis)6696 int sexp_calculate_angle(matrix *orient, int axis)
6697 {
6698 	Assert(orient != NULL);
6699 	Assert(axis >= 0 && axis <= 2);
6700 
6701 	angles a;
6702 	vm_extract_angles_matrix(&a, orient);
6703 
6704 	// blugh
6705 	float rad;
6706 	switch (axis)
6707 	{
6708 		case 0:	rad = a.p; break;
6709 		case 1:	rad = a.b; break;
6710 		case 2:	rad = a.h; break;
6711 		default: rad = 0.0f; break;
6712 	}
6713 
6714 	int deg = static_cast<int>(fl_degrees(rad));
6715 	if (deg < 0)
6716 		deg += 360;
6717 
6718 	return deg;
6719 }
6720 
6721 // Goober5000
sexp_get_object_coordinate(int n,int axis)6722 int sexp_get_object_coordinate(int n, int axis)
6723 {
6724 	Assert(n >= 0);
6725 
6726 	char *subsystem_name = NULL;
6727 	vec3d *pos = NULL, *relative_location = NULL, relative_location_buf, subsys_pos_buf;
6728 	object_ship_wing_point_team oswpt;
6729 
6730 	sexp_get_object_ship_wing_point_team(&oswpt, CTEXT(n));
6731 	n = CDR(n);
6732 
6733 	if (n >= 0)
6734 	{
6735 		subsystem_name = CTEXT(n);
6736 		n = CDR(n);
6737 
6738 		if (n >= 0)
6739 		{
6740 			relative_location = &relative_location_buf;
6741 
6742 			relative_location->xyz.x = (float) eval_num(n);
6743 			n = CDR(n);
6744 			if (n >= 0) {
6745 				relative_location->xyz.y = (float) eval_num(n);
6746 				n = CDR(n);
6747 				if (n >= 0) {
6748 					relative_location->xyz.z = (float) eval_num(n);
6749 					n = CDR(n);
6750 				}
6751 			}
6752 		}
6753 	}
6754 
6755 	switch (oswpt.type)
6756 	{
6757 		case OSWPT_TYPE_EXITED:
6758 			return SEXP_NAN_FOREVER;
6759 
6760 		case OSWPT_TYPE_SHIP:
6761 		case OSWPT_TYPE_WING:
6762 		case OSWPT_TYPE_WAYPOINT:
6763 			pos = &oswpt.objp->pos;
6764 			break;
6765 
6766 		default:
6767 			return SEXP_NAN;
6768 	}
6769 
6770 	// see if we have a subsys
6771 	if (oswpt.objp->type == OBJ_SHIP)
6772 	{
6773 		if ((subsystem_name != NULL) && stricmp(subsystem_name, SEXP_NONE_STRING) && stricmp(subsystem_name, SEXP_HULL_STRING))
6774 		{
6775 			pos = &subsys_pos_buf;
6776 			// get the world pos but bail if we can't get one
6777 			if (!sexp_get_subsystem_world_pos(pos, oswpt.objp->instance, subsystem_name))
6778 				return SEXP_NAN;
6779 		}
6780 	}
6781 
6782 	return sexp_calculate_coordinate(pos, &oswpt.objp->orient, relative_location, axis);
6783 }
6784 
6785 // Goober5000
sexp_get_object_angle(int n,int axis)6786 int sexp_get_object_angle(int n, int axis)
6787 {
6788 	Assert(n >= 0);
6789 
6790 	object_ship_wing_point_team oswpt;
6791 	sexp_get_object_ship_wing_point_team(&oswpt, CTEXT(n));
6792 
6793 	switch (oswpt.type)
6794 	{
6795 		case OSWPT_TYPE_EXITED:
6796 			return SEXP_NAN_FOREVER;
6797 
6798 		case OSWPT_TYPE_SHIP:
6799 		case OSWPT_TYPE_WING:
6800 			return sexp_calculate_angle(&oswpt.objp->orient, axis);
6801 
6802 		default:
6803 			return SEXP_NAN;
6804 	}
6805 }
6806 
set_object_for_clients(object * objp)6807 void set_object_for_clients(object *objp)
6808 {
6809 	if (!(Game_mode & GM_MULTIPLAYER)) {
6810 		return;
6811 	}
6812 
6813 	// Tell the player (if this is a client) that they've moved.
6814 	if ((objp->flags & OF_PLAYER_SHIP) && (objp != Player_obj) ){
6815 		multi_oo_send_changed_object(objp);
6816 	}
6817 }
6818 
sexp_set_object_position(int n)6819 void sexp_set_object_position(int n)
6820 {
6821 	vec3d target_vec, orig_leader_vec;
6822 	object_ship_wing_point_team oswpt;
6823 
6824 	sexp_get_object_ship_wing_point_team(&oswpt, CTEXT(n));
6825 	n = CDR(n);
6826 
6827 	target_vec.xyz.x = i2fl(eval_num(n));
6828 	n = CDR(n);
6829 	target_vec.xyz.y = i2fl(eval_num(n));
6830 	n = CDR(n);
6831 	target_vec.xyz.z = i2fl(eval_num(n));
6832 	n = CDR(n);
6833 
6834 	// retime all collision checks so they're performed
6835 	// Goober5000 - only if we have a valid object (don't do this for departed ships, waypoints, etc.)
6836 	if (oswpt.type == OSWPT_TYPE_SHIP || oswpt.type == OSWPT_TYPE_WING)
6837 	{
6838 		if ( Cmdline_old_collision_sys ) {
6839 			obj_all_collisions_retime();
6840 		} else {
6841 			obj_collide_retime_cached_pairs();
6842 		}
6843 	}
6844 
6845 	// if this is a nebula mission and a player is being moved far enough,
6846 	// regenerate the nebula
6847 	extern neb2_detail *Nd;
6848 
6849 	if ( (oswpt.objp == Player_obj)
6850 		&& (The_mission.flags & MISSION_FLAG_FULLNEB)
6851 		&& (vm_vec_dist(&oswpt.objp->pos, &target_vec) >= Nd->cube_inner) )
6852 	{
6853 		neb2_eye_changed();
6854 	}
6855 
6856 	switch (oswpt.type)
6857 	{
6858 		case OSWPT_TYPE_SHIP:
6859 		{
6860 			oswpt.objp->pos = target_vec;
6861 			set_object_for_clients(oswpt.objp);
6862 			return;
6863 		}
6864 
6865 		case OSWPT_TYPE_WAYPOINT:
6866 		{
6867 			oswpt.objp->pos = target_vec;
6868 			oswpt.waypointp->set_pos(&target_vec);
6869 			multi_start_callback();
6870 			multi_send_ushort(oswpt.objp->net_signature);
6871 			multi_send_float(target_vec.xyz.x);
6872 			multi_send_float(target_vec.xyz.y);
6873 			multi_send_float(target_vec.xyz.z);
6874 			multi_end_callback();
6875 			return;
6876 		}
6877 
6878 		case OSWPT_TYPE_WING:
6879 		{
6880 			// move the wing leader first
6881 			orig_leader_vec = oswpt.objp->pos;
6882 			oswpt.objp->pos = target_vec;
6883 			set_object_for_clients(oswpt.objp);
6884 
6885 			// move everything in the wing
6886 			for (int i = 0; i < oswpt.wingp->current_count; i++)
6887 			{
6888 				object *objp = &Objects[Ships[oswpt.wingp->ship_index[i]].objnum];
6889 
6890 				if (objp != oswpt.objp)
6891 				{
6892 					vm_vec_sub2(&objp->pos, &orig_leader_vec);
6893 					vm_vec_add2(&objp->pos, &target_vec);
6894 					set_object_for_clients(objp);
6895 				}
6896 			}
6897 
6898 			return;
6899 		}
6900 	}
6901 }
6902 
6903 // only for waypoints cause they don't get transferred the normal way
multi_sexp_set_object_position()6904 void multi_sexp_set_object_position()
6905 {
6906 	object *objp;
6907 	vec3d wp_vec;
6908 	ushort obj_sig;
6909 	multi_get_ushort(obj_sig);
6910 	multi_get_float(wp_vec.xyz.x);
6911 	multi_get_float(wp_vec.xyz.y);
6912 	multi_get_float(wp_vec.xyz.z);
6913 	objp = multi_get_network_object(obj_sig);
6914 	if (objp->type == OBJ_WAYPOINT) {
6915 		objp->pos = wp_vec;
6916 		waypoint *wpt = find_waypoint_with_objnum(OBJ_INDEX(objp));
6917 		wpt->set_pos(&wp_vec);
6918 	}
6919 }
6920 
6921 // Goober5000
sexp_set_object_orientation(int n)6922 void sexp_set_object_orientation(int n)
6923 {
6924 	angles a;
6925 	matrix target_orient;
6926 	object_ship_wing_point_team oswpt;
6927 
6928 	sexp_get_object_ship_wing_point_team(&oswpt, CTEXT(n));
6929 	n = CDR(n);
6930 
6931 	a.p = fl_radians(eval_num(n) % 360);
6932 	n = CDR(n);
6933 	a.b = fl_radians(eval_num(n) % 360);
6934 	n = CDR(n);
6935 	a.h = fl_radians(eval_num(n) % 360);
6936 	n = CDR(n);
6937 
6938 	vm_angles_2_matrix(&target_orient, &a);
6939 
6940 	// retime all collision checks so they're performed
6941 	// Goober5000 - only if we have a valid object (don't do this for departed ships, waypoints, etc.)
6942 	if (oswpt.type == OSWPT_TYPE_SHIP || oswpt.type == OSWPT_TYPE_WING)
6943 	{
6944 		if ( Cmdline_old_collision_sys ) {
6945 			obj_all_collisions_retime();
6946 		} else {
6947 			obj_collide_retime_cached_pairs();
6948 		}
6949 	}
6950 
6951 	switch (oswpt.type)
6952 	{
6953 		case OSWPT_TYPE_SHIP:
6954 		{
6955 			oswpt.objp->orient = target_orient;
6956 			set_object_for_clients(oswpt.objp);
6957 			return;
6958 		}
6959 
6960 		case OSWPT_TYPE_WING:
6961 		{
6962 			// move everything in the wing
6963 			for (int i = 0; i < oswpt.wingp->current_count; i++)
6964 			{
6965 				object *objp = &Objects[Ships[oswpt.wingp->ship_index[i]].objnum];
6966 				objp->orient = target_orient;
6967 				set_object_for_clients(objp);
6968 			}
6969 
6970 			return;
6971 		}
6972 	}
6973 }
6974 
6975 // Goober5000
6976 // this is different from sexp_set_object_orientation
sexp_set_object_orient_sub(object * objp,vec3d * location,int turn_time,int bank)6977 void sexp_set_object_orient_sub(object *objp, vec3d *location, int turn_time, int bank)
6978 {
6979 	Assert(objp && location);
6980 
6981 	vec3d v_orient;
6982 	matrix m_orient;
6983 
6984 
6985 	// are we doing this via ai? -------------------
6986 	if (turn_time)
6987 	{
6988 		// set flag
6989 		int bankflag = 0;
6990 		if (!bank)
6991 		{
6992 			bankflag = AITTV_IGNORE_BANK;
6993 		}
6994 
6995 		// turn
6996 		ai_turn_towards_vector(location, objp, flFrametime, float(turn_time)/(1000.0f), NULL, NULL, 0.0f, 0, NULL, (AITTV_VIA_SEXP | bankflag));
6997 
6998 		// return
6999 		return;
7000 	}
7001 
7002 
7003 	// calculate orientation matrix ----------------
7004 
7005 	vm_vec_sub(&v_orient, location, &objp->pos);
7006 
7007 	if (IS_VEC_NULL_SQ_SAFE(&v_orient))
7008 	{
7009 		Warning(LOCATION, "error in sexp setting ship orientation: can't point to self; quitting...\n");
7010 		return;
7011 	}
7012 
7013 	vm_vector_2_matrix(&m_orient, &v_orient, NULL, NULL);
7014 
7015 
7016 	// set orientation -----------------------------
7017 	objp->orient = m_orient;
7018 	// Tell the player (assuming it's a client) that they've moved.
7019 	set_object_for_clients(objp);
7020 }
7021 
7022 // Goober5000
sexp_set_oswpt_facing(object_ship_wing_point_team * oswpt,vec3d * location,int turn_time=0,int bank=0)7023 void sexp_set_oswpt_facing(object_ship_wing_point_team *oswpt, vec3d *location, int turn_time = 0, int bank = 0)
7024 {
7025 	Assert(oswpt && location);
7026 
7027 	switch (oswpt->type)
7028 	{
7029 		case OSWPT_TYPE_SHIP:
7030 			sexp_set_object_orient_sub(oswpt->objp, location, turn_time, bank);
7031 			break;
7032 
7033 		case OSWPT_TYPE_WING:
7034 		{
7035 			for (int i = 0; i < oswpt->wingp->current_count; i++)
7036 			{
7037 				object *objp = &Objects[Ships[oswpt->wingp->ship_index[i]].objnum];
7038 
7039 				sexp_set_object_orient_sub(objp, location, turn_time, bank);
7040 			}
7041 
7042 			break;
7043 		}
7044 	}
7045 }
7046 
7047 // Goober5000
sexp_set_object_facing(int n,bool facing_object)7048 void sexp_set_object_facing(int n, bool facing_object)
7049 {
7050 	vec3d *location, location_buf;
7051 	int turn_time, bank;
7052 	object_ship_wing_point_team oswpt1, oswpt2;
7053 
7054 	sexp_get_object_ship_wing_point_team(&oswpt1, CTEXT(n));
7055 	n = CDR(n);
7056 
7057 	// ensure it's valid
7058 	if (oswpt1.objp == NULL)
7059 		return;
7060 
7061 	// get location
7062 	if (facing_object)
7063 	{
7064 		sexp_get_object_ship_wing_point_team(&oswpt2, CTEXT(n));
7065 		n = CDR(n);
7066 
7067 		// ensure it's valid
7068 		if (oswpt2.objp == NULL)
7069 			return;
7070 
7071 		location = &oswpt2.objp->pos;
7072 	}
7073 	else
7074 	{
7075 		location = &location_buf;
7076 
7077 		location->xyz.x = (float) eval_num(n);
7078 		n = CDR(n);
7079 		location->xyz.y = (float) eval_num(n);
7080 		n = CDR(n);
7081 		location->xyz.z = (float) eval_num(n);
7082 		n = CDR(n);
7083 	}
7084 
7085 	// get optional turn time and bank
7086 	turn_time = bank = 0;
7087 	if (n != -1)
7088 	{
7089 		turn_time = eval_num(n);
7090 		n = CDR(n);
7091 	}
7092 	if (n != -1)
7093 	{
7094 		bank = eval_num(n);
7095 	}
7096 
7097 	sexp_set_oswpt_facing(&oswpt1, location, turn_time, bank);
7098 }
7099 
sexp_set_ship_man(object * objp,int duration,int heading,int pitch,int bank,bool apply_all_rotate,int up,int sideways,int forward,bool apply_all_lat)7100 void sexp_set_ship_man(object *objp, int duration, int heading, int pitch, int bank, bool apply_all_rotate, int up, int sideways, int forward, bool apply_all_lat)
7101 {
7102 	if (objp->type != OBJ_SHIP)
7103 		return;
7104 
7105 	ship *shipp = &Ships[objp->instance];
7106 	ai_info	*aip = &Ai_info[shipp->ai_index];
7107 
7108 	aip->ai_override_timestamp = timestamp(duration);
7109 	aip->ai_override_flags = 0;
7110 
7111 	if (apply_all_rotate) {
7112 		aip->ai_override_flags |= AIORF_FULL;
7113 		aip->ai_override_ci.bank = bank / 100.0f;
7114 		aip->ai_override_ci.pitch = pitch / 100.0f;
7115 		aip->ai_override_ci.heading = heading / 100.0f;
7116 	} else {
7117 		if (bank != 0) {
7118 			aip->ai_override_flags |= AIORF_ROLL;
7119 			aip->ai_override_ci.bank = bank / 100.0f;
7120 		}
7121 		if (pitch != 0) {
7122 			aip->ai_override_flags |= AIORF_PITCH;
7123 			aip->ai_override_ci.pitch = pitch / 100.0f;
7124 		}
7125 		if (heading != 0) {
7126 			aip->ai_override_flags |= AIORF_HEADING;
7127 			aip->ai_override_ci.heading = heading / 100.0f;
7128 		}
7129 	}
7130 	if (apply_all_lat) {
7131 		aip->ai_override_flags |= AIORF_FULL_LAT;
7132 		aip->ai_override_ci.vertical = up / 100.0f;
7133 		aip->ai_override_ci.sideways = sideways / 100.0f;
7134 		aip->ai_override_ci.forward = forward / 100.0f;
7135 	} else {
7136 		if (up != 0) {
7137 			aip->ai_override_flags |= AIORF_UP;
7138 			aip->ai_override_ci.vertical = up / 100.0f;
7139 		}
7140 		if (sideways != 0) {
7141 			aip->ai_override_flags |= AIORF_SIDEWAYS;
7142 			aip->ai_override_ci.sideways = sideways / 100.0f;
7143 		}
7144 		if (forward != 0) {
7145 			aip->ai_override_flags |= AIORF_FORWARD;
7146 			aip->ai_override_ci.forward = forward / 100.0f;
7147 		}
7148 	}
7149 }
7150 
sexp_set_oswpt_maneuver(object_ship_wing_point_team * oswpt,int duration,int heading,int pitch,int bank,bool apply_all_rotate,int up,int sideways,int forward,bool apply_all_lat)7151 void sexp_set_oswpt_maneuver(object_ship_wing_point_team *oswpt, int duration, int heading, int pitch, int bank, bool apply_all_rotate, int up, int sideways, int forward, bool apply_all_lat)
7152 {
7153 	Assert(oswpt);
7154 
7155 	switch (oswpt->type)
7156 	{
7157 		case OSWPT_TYPE_SHIP:
7158 			sexp_set_ship_man(oswpt->objp, duration, heading, pitch, bank, apply_all_rotate, up, sideways, forward, apply_all_lat);
7159 			break;
7160 
7161 		case OSWPT_TYPE_WING:
7162 		{
7163 			for (int i = 0; i < oswpt->wingp->current_count; i++)
7164 			{
7165 				object *objp = &Objects[Ships[oswpt->wingp->ship_index[i]].objnum];
7166 
7167 				sexp_set_ship_man(objp, duration, heading, pitch, bank, apply_all_rotate, up, sideways, forward, apply_all_lat);
7168 			}
7169 
7170 			break;
7171 		}
7172 	}
7173 }
7174 
sexp_set_ship_maneuver(int n,int op_num)7175 void sexp_set_ship_maneuver(int n, int op_num)
7176 {
7177 	int bank = 0, heading = 0, pitch = 0;
7178 	int up = 0, sideways = 0, forward = 0;
7179 	int duration, i, temp;
7180 	bool apply_all_rotate = false, apply_all_lat = false;
7181 	object_ship_wing_point_team oswpt;
7182 
7183 	sexp_get_object_ship_wing_point_team(&oswpt, CTEXT(n));
7184 
7185 	n = CDR(n);
7186 	duration = eval_num(n);
7187 	if (duration < 2)
7188 		return;
7189 
7190 	if (op_num == OP_SHIP_ROT_MANEUVER || op_num == OP_SHIP_MANEUVER) {
7191 		for(i=0;i<3;i++) {
7192 			n = CDR(n);
7193 
7194 			temp = eval_num(n);
7195 			if (temp > 100) temp = 100;
7196 			if (temp < -100) temp = -100;
7197 
7198 			if (i == 0)
7199 				heading = temp;
7200 			else if (i == 1)
7201 				pitch = temp;
7202 			else
7203 				bank = temp;
7204 		}
7205 
7206 		n = CDR(n);
7207 		apply_all_rotate = (is_sexp_true(n) != 0);
7208 	}
7209 
7210 	if (op_num == OP_SHIP_LAT_MANEUVER || op_num == OP_SHIP_MANEUVER) {
7211 		for(i=0;i<3;i++) {
7212 			n = CDR(n);
7213 
7214 			temp = eval_num(n);
7215 			if (temp > 100) temp = 100;
7216 			if (temp < -100) temp = -100;
7217 
7218 			if (i == 0)
7219 				up = temp;
7220 			else if (i == 1)
7221 				sideways = temp;
7222 			else
7223 				forward = temp;
7224 		}
7225 
7226 		n = CDR(n);
7227 		apply_all_lat = (is_sexp_true(n) != 0);
7228 	}
7229 
7230 	if ((bank == 0) && (pitch == 0) && (heading == 0) && !apply_all_rotate && (up == 0) && (sideways == 0) && (forward == 0) && !apply_all_lat)
7231 		return;
7232 
7233 	sexp_set_oswpt_maneuver(&oswpt, duration, heading, pitch, bank, apply_all_rotate, up, sideways, forward, apply_all_lat);
7234 }
7235 
7236 /**
7237  * Determine when the last meaningful order was given to one or more ships.
7238  *
7239  * @return true or false depending on whether or not a meaningful order was received
7240  */
sexp_last_order_time(int n)7241 int sexp_last_order_time(int n)
7242 {
7243 	int instance, i;
7244 	fix time;
7245 	char *name;
7246 	ai_goal *aigp;
7247 
7248 	time = i2f(eval_num(n));
7249 	Assert ( time >= 0 );
7250 
7251 	n = CDR(n);
7252 	while ( n != -1 ) {
7253 		name = CTEXT(n);
7254 		instance = ship_name_lookup(name);
7255 		if ( instance != -1 ) {
7256 			aigp = Ai_info[Ships[instance].ai_index].goals;
7257 		} else {
7258 			instance = wing_name_lookup(name);
7259 			if ( instance == -1 )						// if we cannot find ship or wing, return SEXP_FALSE
7260 				return SEXP_FALSE;
7261 			aigp = Wings[instance].ai_goals;
7262 		}
7263 
7264 		// with the ship, check the ai_goals structure for this ship and determine if there are any
7265 		// orders which are < time seconds since current mission time
7266 		for ( i = 0; i < MAX_AI_GOALS; i++ ) {
7267 			int mode;
7268 
7269 			mode = aigp->ai_mode;
7270 			if ( (mode  != AI_GOAL_NONE) && (mode != AI_GOAL_WARP) )
7271 				if ( (aigp->time + time) > Missiontime )
7272 					break;
7273 			aigp++;
7274 		}
7275 		if ( i == MAX_AI_GOALS )
7276 			return SEXP_TRUE;
7277 
7278 		n = CDR(n);
7279 	}
7280 
7281 	return SEXP_FALSE;
7282 }
7283 
7284 /**
7285  * Return the number of players in the mission
7286  */
sexp_num_players()7287 int sexp_num_players()
7288 {
7289 	int count;
7290 	object *objp;
7291 
7292 	count = 0;
7293 	for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
7294 		if ( (objp->type == OBJ_SHIP) && (objp->flags & OF_PLAYER_SHIP) )
7295 			count++;
7296 	}
7297 
7298 	return count;
7299 }
7300 
7301 /**
7302  * Determine if the current skill level of the game is at least the skill level given in the SEXP
7303  */
sexp_skill_level_at_least(int n)7304 int sexp_skill_level_at_least(int n)
7305 {
7306 	int i;
7307 	char *level_name;
7308 
7309 	level_name = CTEXT(n);
7310 
7311 	if (level_name == NULL)
7312 		return SEXP_FALSE;
7313 
7314 	for (i = 0; i < NUM_SKILL_LEVELS; i++ ) {
7315 		if ( !stricmp(level_name, Skill_level_names(i, 0)) ) {
7316 			if ( Game_skill_level >= i ){
7317 				return SEXP_TRUE;
7318 			} else {
7319 				return SEXP_FALSE;
7320 			}
7321 		}
7322 	}
7323 
7324 	// return SEXP_FALSE if not found!!!
7325 	return SEXP_FALSE;
7326 }
7327 
sexp_was_promotion_granted(int n)7328 int sexp_was_promotion_granted(int n)
7329 {
7330 	if (Player->flags & PLAYER_FLAGS_PROMOTED)
7331 		return SEXP_TRUE;
7332 
7333 	return SEXP_FALSE;
7334 }
7335 
sexp_was_medal_granted(int n)7336 int sexp_was_medal_granted(int n)
7337 {
7338 	int i;
7339 	char *medal_name;
7340 
7341 	if (n < 0) {
7342 		if (Player->stats.m_medal_earned >= 0)
7343 			return SEXP_TRUE;
7344 
7345 		return SEXP_FALSE;
7346 	}
7347 
7348 	medal_name = CTEXT(n);
7349 
7350 	if (medal_name == NULL)
7351 		return SEXP_FALSE;
7352 
7353 	for (i=0; i<Num_medals; i++) {
7354 		if (!stricmp(medal_name, Medals[i].name))
7355 			break;
7356 	}
7357 
7358 	if ( (i < Num_medals) && (Player->stats.m_medal_earned == i) )
7359 		return SEXP_TRUE;
7360 
7361 	return SEXP_FALSE;
7362 }
7363 
7364 /**
7365  * @todo Add code to check the damage ships which have exited have taken
7366  */
get_damage_caused(int damaged_ship,int attacker)7367 float get_damage_caused(int damaged_ship, int attacker )
7368 {
7369 	int sindex, idx;
7370 	float damage_total = 0.0f;
7371 
7372 
7373 	// is the ship that took damage in the mission still?
7374 	sindex = ship_get_by_signature(damaged_ship);
7375 
7376 	if (sindex >= 0 ) {
7377 		for(idx=0; idx<MAX_DAMAGE_SLOTS; idx++){
7378 			if (Ships[sindex].damage_ship_id[idx] == attacker) {
7379 				damage_total += Ships[sindex].damage_ship[idx];
7380 				break;
7381 			}
7382 		}
7383 	}
7384 	else {
7385 		//TO DO - Add code to check the damage ships which have exited have taken
7386 
7387 		sindex = ship_find_exited_ship_by_signature(damaged_ship);
7388 		for(idx=0; idx<MAX_DAMAGE_SLOTS; idx++){
7389 			if (Ships_exited[sindex].damage_ship_id[idx] == attacker) {
7390 				damage_total += Ships_exited[sindex].damage_ship[idx];
7391 				break;
7392 			}
7393 		}
7394 
7395 
7396 	}
7397 	return damage_total;
7398 }
7399 
7400 // Karajorma
sexp_get_damage_caused(int node)7401 int sexp_get_damage_caused(int node)
7402 {
7403 	int sindex, damaged_sig, attacker_sig;
7404 	float damage_caused = 0.0f;
7405 	char	*name;
7406 	int ship_class;
7407 
7408 	name = CTEXT(node);
7409 	sindex = ship_name_lookup(name);
7410 	if (sindex < 0) {
7411 		// this ship may have exited already.
7412 		sindex = ship_find_exited_ship_by_name(CTEXT(node));
7413 		if (sindex < 0) {
7414 			// this is probably a ship which hasn't arrived and thus can't have taken any damage yet
7415 			return fl2i(damage_caused);
7416 		}
7417 		else {
7418 			damaged_sig = Ships_exited[sindex].obj_signature;
7419 			ship_class = Ships_exited[sindex].ship_class;
7420 		}
7421 	}
7422 	else {
7423 		damaged_sig = Objects[Ships[sindex].objnum].signature ;
7424 		ship_class = Ships[sindex].ship_info_index;
7425 	}
7426 
7427 
7428 	node = CDR(node);
7429 	Assert (node != -1);
7430 
7431 	// go through the list of ships who we think may have attacked the ship
7432 	for ( ; node != -1; node = CDR(node) ) {
7433 		name = CTEXT(node);
7434 		sindex = ship_name_lookup(name);
7435 		if (sindex < 0) {
7436 			sindex = ship_find_exited_ship_by_name(name);
7437 			if (sindex < 0) {
7438 				continue;
7439 			} else {
7440 				attacker_sig = Ships_exited[sindex].obj_signature;
7441 			}
7442 		}
7443 		else {
7444 			attacker_sig = Objects[Ships[sindex].objnum].signature ;
7445 		}
7446 
7447 		if (attacker_sig < 0) {
7448 			continue;
7449 		}
7450 
7451 		damage_caused += get_damage_caused (damaged_sig, attacker_sig);
7452 	}
7453 
7454 	Assert ((ship_class > -1) && (ship_class < MAX_SHIP_CLASSES));
7455 	return (int) ((damage_caused/Ship_info[ship_class].max_hull_strength) * 100.0f);
7456 }
7457 
7458 /**
7459  * Returns true if the percentage of ships (and ships in wings) departed is at least the percentage given.
7460  *
7461  * what determine if we should check destroyed or departed status
7462  * Goober5000 - added disarm and disable
7463  */
sexp_percent_ships_arrive_depart_destroy_disarm_disable(int n,int what)7464 int sexp_percent_ships_arrive_depart_destroy_disarm_disable(int n, int what)
7465 {
7466 	int percent;
7467 	int total, count;
7468 	char *name;
7469 
7470 	percent = eval_num(n);
7471 
7472 	total = 0;
7473 	count = 0;
7474 	// iterate through the rest of the ships/wings in the list and tally the departures and the
7475 	// total
7476 	for ( n = CDR(n); n != -1; n = CDR(n) ) {
7477 		int wingnum;
7478 
7479 		name = CTEXT(n);
7480 
7481 		wingnum = wing_name_lookup( name, 1 );
7482 		if ( wingnum != -1 ) {
7483 			// for wings, we can increment the total by the total number of ships that we expect for
7484 			// this wing, and the departures by the number of departures stored for this wing
7485 			total += (Wings[wingnum].wave_count * Wings[wingnum].num_waves);
7486 			if ( what == OP_PERCENT_SHIPS_DEPARTED )
7487 				count += Wings[wingnum].total_departed;
7488 			else if ( what == OP_PERCENT_SHIPS_DESTROYED )
7489 				count += Wings[wingnum].total_destroyed;
7490 			else if ( what == OP_PERCENT_SHIPS_ARRIVED )
7491 				count += Wings[wingnum].total_arrived_count;
7492 			else
7493 				Error(LOCATION, "Invalid status check '%d' for wing '%s' in sexp_percent_ships_arrive_depart_destroy_disarm_disable", what, name);
7494 		} else {
7495 			// must be a ship, so increment the total by 1, then determine if this ship has departed
7496 			total++;
7497 			if ( what == OP_PERCENT_SHIPS_DEPARTED ) {
7498 				if ( mission_log_get_time(LOG_SHIP_DEPARTED, name, NULL, NULL) )
7499 					count++;
7500 			} else if ( what == OP_PERCENT_SHIPS_DESTROYED ) {
7501 				if ( mission_log_get_time(LOG_SHIP_DESTROYED, name, NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, name, NULL, NULL) )
7502 					count++;
7503 			} else if ( what == OP_PERCENT_SHIPS_DISABLED ) {
7504 				if ( mission_log_get_time(LOG_SHIP_DISABLED, name, NULL, NULL) )
7505 					count++;
7506 			} else if ( what == OP_PERCENT_SHIPS_DISARMED ) {
7507 				if ( mission_log_get_time(LOG_SHIP_DISARMED, name, NULL, NULL) )
7508 					count++;
7509 			} else if ( what == OP_PERCENT_SHIPS_ARRIVED ) {
7510 				if ( mission_log_get_time(LOG_SHIP_ARRIVED, name, NULL, NULL) )
7511 					count++;
7512 			} else
7513 				Error("Invalid status check '%d' for ship '%s' in sexp_percent_ships_depart_destroy_disarm_disable", what, name);
7514 
7515 		}
7516 	}
7517 
7518 	// now, look at the percentage
7519 	if ( ((count * 100) / total) >= percent )
7520 		return SEXP_KNOWN_TRUE;
7521 	else
7522 		return SEXP_FALSE;
7523 }
7524 
7525 /**
7526  * Determine if a list of ships has departed from within a radius of a given jump node.
7527  * @return true N seconds after the list of ships have departed
7528  */
sexp_depart_node_delay(int n)7529 int sexp_depart_node_delay(int n)
7530 {
7531 	int delay, count, num_departed;
7532 	char *jump_node_name, *name;
7533 	fix latest_time, this_time;
7534 
7535 	Assert( n >= 0 );
7536 
7537 	delay = eval_num(n);
7538 	n = CDR(n);
7539 	jump_node_name = CTEXT(n);
7540 
7541 	// iterate through the list of ships
7542 	n = CDR(n);
7543 	latest_time = 0;
7544 	count = 0;
7545 	num_departed = 0;
7546 	while ( n != -1 ) {
7547 		count++;
7548 		name = CTEXT(n);
7549 
7550 		if (sexp_query_has_yet_to_arrive(name))
7551 			return SEXP_CANT_EVAL;
7552 
7553 		// if ship/wing destroyed, sexpression is known false.  Also, if there is no departure log entry, then
7554 		// the sexpression is not true.
7555 		if ( mission_log_get_time(LOG_SHIP_DESTROYED, name, NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, name, NULL, NULL) )
7556 			return SEXP_KNOWN_FALSE;
7557 		else if ( mission_log_get_time(LOG_SHIP_DEPARTED, name, jump_node_name, &this_time) ) {
7558 			num_departed++;
7559 			if ( this_time > latest_time )
7560 				latest_time = this_time;
7561 		}
7562 		n = CDR(n);
7563 	}
7564 
7565 	if ( (count == num_departed) && ((Missiontime - latest_time) >= delay) )
7566 		return SEXP_KNOWN_TRUE;
7567 	else
7568 		return SEXP_FALSE;
7569 }
7570 
7571 /**
7572  * Returns true when the listed ships/wings have all been destroyed or have departed.
7573  */
sexp_destroyed_departed_delay(int n)7574 int sexp_destroyed_departed_delay(int n)
7575 {
7576 	int count, total;
7577 	fix delay, latest_time;
7578 	char *name;
7579 
7580 	Assert( n >= 0 );
7581 
7582 	// get the delay
7583 	delay = i2f(eval_num(n));
7584 	n = CDR(n);
7585 
7586 	count = 0;					// number destroyed or departed
7587 	total = 0;					// total number of ships/wings to check
7588 	latest_time = 0;
7589 	while ( n != -1 ) {
7590 		int wingnum;
7591 		fix time_gone = 0;
7592 
7593 		total++;
7594 		name = CTEXT(n);
7595 
7596 		// for wings, check the WF_GONE flag to see if there are no more ships in this wing to arrive.
7597 		wingnum = wing_name_lookup(name, 1);
7598 		if ( wingnum != -1 ) {
7599 			if ( Wings[wingnum].flags & WF_WING_GONE ) {
7600 				// be sure to get the latest time of one of these
7601 				if ( Wings[wingnum].time_gone > latest_time ){
7602 					time_gone = Wings[wingnum].time_gone;
7603 				}
7604 				count++;
7605 			}
7606 		} else if ( mission_log_get_time(LOG_SHIP_DEPARTED, name, NULL, &time_gone) ) {
7607 			count++;
7608 		} else if ( mission_log_get_time(LOG_SHIP_DESTROYED, name, NULL, &time_gone) ) {
7609 			count++;
7610 		} else if ( mission_log_get_time(LOG_SELF_DESTRUCTED, name, NULL, &time_gone) ) {
7611 			count++;
7612 		}
7613 
7614 		// check our latest time
7615 		if ( time_gone > latest_time ){
7616 			latest_time = time_gone;
7617 		}
7618 
7619 		n = CDR(n);
7620 	}
7621 
7622 	if ( (count == total) && (Missiontime > (latest_time + delay)) )
7623 		return SEXP_KNOWN_TRUE;
7624 	else
7625 		return SEXP_FALSE;
7626 }
7627 
sexp_special_warpout_name(int node)7628 int sexp_special_warpout_name( int node )
7629 {
7630 	int shipnum, knossos_shipnum;
7631 	char *ship_name, *knossos;
7632 
7633 	ship_name = CTEXT(node);
7634 	knossos = CTEXT(CDR(node));
7635 
7636 	// check to see if either ship was destroyed or departed.  If so, then make this node known false
7637 	if ( mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) || mission_log_get_time( LOG_SHIP_DEPARTED, ship_name, NULL, NULL) || mission_log_get_time( LOG_SELF_DESTRUCTED, ship_name, NULL, NULL) ||
7638 		  mission_log_get_time(LOG_SHIP_DESTROYED, knossos, NULL, NULL) || mission_log_get_time( LOG_SHIP_DEPARTED, knossos, NULL, NULL) || mission_log_get_time( LOG_SELF_DESTRUCTED, knossos, NULL, NULL) )
7639 		return SEXP_NAN_FOREVER;
7640 
7641 	// get ship name
7642 	shipnum = ship_name_lookup(ship_name);
7643 	if (shipnum < 0) {
7644 		return SEXP_NAN;
7645 	}
7646 
7647 	// get knossos ship
7648 	knossos_shipnum = ship_name_lookup(knossos);
7649 	if (knossos_shipnum < 0) {
7650 		return SEXP_NAN;
7651 	}
7652 
7653 	// set special warpout objnum
7654 	Ships[shipnum].special_warpout_objnum = Ships[knossos_shipnum].objnum;
7655 	return SEXP_FALSE;
7656 }
7657 
7658 /**
7659  * Determines if N seconds have elapsed since all discovery of all cargo of given ships
7660  *
7661  * Goober5000 - I reworked this function to allow for the set-scanned and set-unscanned sexps
7662  * to work multiple times in a row and also to fix the potential bug where exited ships are
7663  * checked against their departure time, not against their cargo known time
7664  */
sexp_is_cargo_known(int n,int check_delay)7665 int sexp_is_cargo_known( int n, int check_delay )
7666 {
7667 	int count, ship_num, num_known, delay;
7668 
7669 	char *name;
7670 
7671 	Assert ( n >= 0 );
7672 
7673 	count = 0;
7674 	num_known = 0;
7675 
7676 	// get the delay value (if there is one)
7677 	delay = 0;
7678 	if ( check_delay )
7679 	{
7680 		delay = eval_num(n);
7681 		n = CDR(n);
7682 	}
7683 
7684 	while ( n != -1 )
7685 	{
7686 		fix time_known;
7687 		int is_known;
7688 
7689 		is_known = 0;
7690 
7691 		count++;
7692 
7693 		// see if we have already checked this entry
7694 		if ( Sexp_nodes[n].value == SEXP_KNOWN_TRUE )
7695 		{
7696 			num_known++;
7697 		}
7698 		else
7699 		{
7700 			name = CTEXT(n);
7701 
7702 			// find the index in the ship array (will be -1 if not in mission)
7703 			ship_num = ship_name_lookup(name);
7704 
7705 			// see if the ship has already exited the mission (either through departure or destruction)
7706 			int exited_index = ship_find_exited_ship_by_name(name);
7707 			if (exited_index != -1)
7708 			{
7709 				// if not known, the whole thing is known false
7710 				if ( !(Ships_exited[exited_index].flags & SEF_CARGO_KNOWN) )
7711 					return SEXP_KNOWN_FALSE;
7712 
7713 				// check the delay of when we found out
7714 				time_known = Missiontime - Ships_exited[exited_index].time_cargo_revealed;
7715 				if ( f2i(time_known) >= delay )
7716 				{
7717 					is_known = 1;
7718 
7719 					// here is the only place in the new sexp that this can be known true
7720 					Sexp_nodes[n].value = SEXP_KNOWN_TRUE;
7721 				}
7722 			}
7723 			// ship either in mission or not arrived yet
7724 			else
7725 			{
7726 				// if ship_name_lookup returns -1, then ship is either exited or yet to arrive,
7727 				// and we've already checked exited
7728 				if ( ship_num != -1 )
7729 				{
7730 					if ( Ships[ship_num].flags & SF_CARGO_REVEALED )
7731 					{
7732 						time_known = Missiontime - Ships[ship_num].time_cargo_revealed;
7733 						if ( f2i(time_known) >= delay )
7734 						{
7735 							is_known = 1;
7736 						}
7737 					}
7738 				}
7739 			}
7740 		}
7741 
7742 		// if cargo is known, mark our variable, but not the sexp, because it may change later
7743 		if ( is_known )
7744 		{
7745 			num_known++;
7746 		}
7747 
7748 		n = CDR(n);
7749 	}
7750 
7751 	Directive_count += count - num_known;
7752 	if ( count == num_known )
7753 		return SEXP_TRUE;
7754 	else
7755 		return SEXP_FALSE;
7756 }
7757 
get_cap_subsys_cargo_flags(int shipnum,char * subsys_name,int * known,fix * time_revealed)7758 void get_cap_subsys_cargo_flags(int shipnum, char *subsys_name, int *known, fix *time_revealed)
7759 {
7760 	// find the ship subsystem
7761 	ship_subsys *ss = ship_get_subsys(&Ships[shipnum], subsys_name);
7762 	if (ss != NULL)
7763 	{
7764 		// set the flags
7765 		*known = (ss->flags & SSF_CARGO_REVEALED);
7766 		*time_revealed = ss->time_subsys_cargo_revealed;
7767 	}
7768 	// if we didn't find the subsystem, the ship hasn't arrived yet
7769 	else
7770 	{
7771 		*known = -1;
7772 		*time_revealed = 0;
7773 	}
7774 }
7775 
7776 // reworked by Goober5000 to allow for set-scanned and set-unscanned to be used more than once
sexp_cap_subsys_cargo_known_delay(int n)7777 int sexp_cap_subsys_cargo_known_delay(int n)
7778 {
7779 	int delay, count, num_known, ship_num;
7780 	char *ship_name, *subsys_name;
7781 
7782 	num_known = 0;
7783 	count = 0;
7784 
7785 	Assert( n >= 0 );
7786 
7787 	// get delay
7788 	delay = eval_num(n);
7789 	n = CDR(n);
7790 
7791 	// get ship name
7792 	ship_name = CTEXT(n);
7793 	n = CDR(n);
7794 
7795 	// find the index in the ship array
7796 	ship_num = ship_name_lookup(ship_name);
7797 
7798 	while ( n != -1 )
7799 	{
7800 		fix time_known;
7801 		int is_known;
7802 
7803 		is_known = 0;
7804 		count++;
7805 
7806 		// see if we have already checked this entry
7807 		if ( Sexp_nodes[n].value == SEXP_KNOWN_TRUE )
7808 		{
7809 			num_known++;
7810 		}
7811 		else
7812 		{
7813 			// get subsys name
7814 			subsys_name = CTEXT(n);
7815 
7816 			// see if the ship has already exited the mission (either through departure or destruction)
7817 			if (ship_find_exited_ship_by_name(ship_name) != -1)
7818 			{
7819 				// check the delay of when we found out...
7820 				// Since there is no way to keep track of subsystem status once a ship has departed
7821 				// or has been destroyed, check the mission log.  This will work in 99.9999999% of
7822 				// all cases; however, if the mission designer repeatedly sets and resets the scanned
7823 				// status of the subsystem, the mission log will only return the first occurrence of the
7824 				// subsystem cargo being revealed (regardless of whether it was first hidden using
7825 				// set-unscanned).  Normally, ships keep track of cargo data in the subsystem struct,
7826 				// but once/ the ship has left the mission, the subsystem linked list is purged,
7827 				// causing the loss of this information.  I judged the significant rework of the
7828 				// subsystem code not worth the rare instance that this sexp may be required to work
7829 				// in this way, especially since this problem only occurs after the ship departs.  If
7830 				// the mission designer really needs this functionality, he or she can achieve the
7831 				// same result with creative combinations of event chaining and is-event-true.
7832 				if (!mission_log_get_time(LOG_CAP_SUBSYS_CARGO_REVEALED, ship_name, subsys_name, &time_known))
7833 				{
7834 					// if not known, the whole thing is known false
7835 					return SEXP_KNOWN_FALSE;
7836 				}
7837 
7838 				if (f2i(Missiontime - time_known) >= delay)
7839 				{
7840 					is_known = 1;
7841 
7842 					// here is the only place in the new sexp that this can be known true
7843 					Sexp_nodes[n].value = SEXP_KNOWN_TRUE;
7844 				}
7845 			}
7846 			// ship either in mission or not arrived yet
7847 			else
7848 			{
7849 				// if ship_name_lookup returns -1, then ship is either exited or yet to arrive,
7850 				// and we've already checked exited
7851 				if ( ship_num != -1 )
7852 				{
7853 					int cargo_revealed(0);
7854 					fix time_revealed(0);
7855 
7856 					// get flags
7857 					get_cap_subsys_cargo_flags(ship_num, subsys_name, &cargo_revealed, &time_revealed);
7858 
7859 					if (cargo_revealed)
7860 					{
7861 						time_known = Missiontime - time_revealed;
7862 						if ( f2i(time_known) >= delay )
7863 						{
7864 							is_known = 1;
7865 						}
7866 					}
7867 				}
7868 			}
7869 		}
7870 
7871 		// if cargo is known, mark our variable, but not the sexp, because it may change later
7872 		if (is_known)
7873 		{
7874 			num_known++;
7875 		}
7876 
7877 		n = CDR(n);
7878 	}
7879 
7880 	Directive_count += count - num_known;
7881 	if ( count == num_known )
7882 		return SEXP_TRUE;
7883 	else
7884 		return SEXP_FALSE;
7885 }
7886 
7887 // Goober5000
sexp_set_scanned_unscanned(int n,int flag)7888 void sexp_set_scanned_unscanned(int n, int flag)
7889 {
7890 	char *ship_name, *subsys_name;
7891 	int shipnum;
7892 	ship_subsys *ss;
7893 
7894 	// get ship name
7895 	ship_name = CTEXT(n);
7896 
7897 	// check to see the ship was destroyed or departed - if so, do nothing
7898 	if ( mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) || mission_log_get_time( LOG_SHIP_DEPARTED, ship_name, NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, ship_name, NULL, NULL) )
7899 	{
7900 		return;
7901 	}
7902 
7903 	// get ship number
7904 	shipnum = ship_name_lookup(ship_name);
7905 
7906 	// if the ship isn't in the mission, do nothing
7907 	if (shipnum == -1)
7908 	{
7909 		return;
7910 	}
7911 
7912 	// check for possible next optional argument: subsystem
7913 	n = CDR(n);
7914 
7915 	// if no subsystem specified, just do it for the ship and exit
7916 	if (n == -1)
7917 	{
7918 		if (flag)
7919 			ship_do_cargo_revealed(&Ships[shipnum]);
7920 		else
7921 			ship_do_cargo_hidden(&Ships[shipnum]);
7922 
7923 		return;
7924 	}
7925 
7926 	// iterate through all subsystems
7927 	while (n != -1)
7928 	{
7929 		subsys_name = CTEXT(n);
7930 
7931 		// find the ship subsystem
7932 		ss = ship_get_subsys(&Ships[shipnum], subsys_name);
7933 		if (ss != NULL)
7934 		{
7935 			// do it for the subsystem
7936 			if (flag)
7937 				ship_do_cap_subsys_cargo_revealed(&Ships[shipnum], ss);
7938 			else
7939 				ship_do_cap_subsys_cargo_hidden(&Ships[shipnum], ss);
7940 		}
7941 
7942 		// if we didn't find the subsystem -- bad
7943 		if (ss == NULL && ship_class_unchanged(shipnum)) {
7944 			Error(LOCATION, "Couldn't find subsystem '%s' on ship '%s' in sexp_set_scanned_unscanned", subsys_name, ship_name);
7945 		}
7946 
7947 		// but if it did, loop again
7948 		n = CDR(n);
7949 	}
7950 }
7951 
sexp_has_been_tagged_delay(int n)7952 int sexp_has_been_tagged_delay(int n)
7953 {
7954 	int count, shipnum, num_known, delay;
7955 	char *name;
7956 
7957 	Assert ( n >= 0 );
7958 
7959 	count = 0;
7960 	num_known = 0;
7961 
7962 	// get the delay value
7963 	delay = eval_num(n);
7964 
7965 	n = CDR(n);
7966 
7967 	while ( n != -1 ) {
7968 		fix time_known;
7969 		int is_known;
7970 
7971 		is_known = 0;
7972 
7973 		count++;
7974 
7975 		// see if we have already checked this entry
7976 		if ( Sexp_nodes[n].value == SEXP_KNOWN_TRUE ) {
7977 			num_known++;
7978 		} else {
7979 			int exited_index;
7980 
7981 			name = CTEXT(n);
7982 
7983 			// see if the ship has already exited the mission (either through departure or destruction).  If so,
7984 			// grab the status of whether the cargo is known from this list
7985 			exited_index = ship_find_exited_ship_by_name( name );
7986 			if (exited_index != -1 ) {
7987 				if ( !(Ships_exited[exited_index].flags & SEF_BEEN_TAGGED) )
7988 					return SEXP_KNOWN_FALSE;
7989 
7990 				// check the delay of when we found out.  We use the ship died time which isn't entirely accurate
7991 				// but won't cause huge delays.
7992 				time_known = Missiontime - Ships_exited[exited_index].time;
7993 				if ( f2i(time_known) >= delay )
7994 					is_known = 1;
7995 			} else {
7996 
7997 				// otherwise, ship should still be in the mission.  If ship_name_lookup returns -1, then ship
7998 				// is yet to arrive.
7999 				shipnum = ship_name_lookup( name );
8000 				if ( shipnum != -1 ) {
8001 					if ( Ships[shipnum].time_first_tagged != 0 ) {
8002 						time_known = Missiontime - Ships[shipnum].time_first_tagged;
8003 						if ( f2i(time_known) >= delay )
8004 							is_known = 1;
8005 					}
8006 				}
8007 			}
8008 		}
8009 
8010 		// if cargo is known, mark our variable and this sexpression.
8011 		if ( is_known ) {
8012 			num_known++;
8013 			Sexp_nodes[n].value = SEXP_KNOWN_TRUE;
8014 		}
8015 
8016 		n = CDR(n);
8017 	}
8018 
8019 	Directive_count += count - num_known;
8020 	if ( count == num_known )
8021 		return SEXP_KNOWN_TRUE;
8022 	else
8023 		return SEXP_FALSE;
8024 }
8025 
8026 // Karajorma
eval_when_for_each_special_argument(int cur_node)8027 void eval_when_for_each_special_argument( int cur_node )
8028 {
8029 	arg_item *ptr;
8030 
8031 	// loop through all the supplied arguments
8032 	ptr = Sexp_applicable_argument_list.get_next();
8033 	while (ptr != NULL)
8034 	{
8035 		// acquire argument to be used
8036 		Sexp_replacement_arguments.push_back(ptr->text);
8037 
8038 		Sexp_current_argument_nesting_level++;
8039 		Sexp_applicable_argument_list.add_data(ptr->text);
8040 
8041 		// execute sexp... CTEXT will insert the argument as necessary
8042 		eval_sexp(cur_node);
8043 
8044 		// clean up any special sexp stuff
8045 		Sexp_applicable_argument_list.clear_nesting_level();
8046 		Sexp_current_argument_nesting_level--;
8047 
8048 		// remove the argument
8049 		Sexp_replacement_arguments.pop_back();
8050 
8051 		// continue along argument list
8052 		ptr = ptr->get_next();
8053 	}
8054 }
8055 
8056 
8057 // Goober5000
do_action_for_each_special_argument(int cur_node)8058 void do_action_for_each_special_argument( int cur_node )
8059 {
8060 	arg_item *ptr;
8061 
8062 	// loop through all the supplied arguments
8063 	ptr = Sexp_applicable_argument_list.get_next();
8064 	while (ptr != NULL)
8065 	{
8066 		// acquire argument to be used
8067 		Sexp_replacement_arguments.push_back(ptr->text);
8068 
8069 		// execute sexp... CTEXT will insert the argument as necessary
8070 		// (since these are all actions, they don't return any meaningful values)
8071 		eval_sexp(cur_node);
8072 
8073 		// remove the argument
8074 		Sexp_replacement_arguments.pop_back();
8075 		// continue along argument list
8076 		ptr = ptr->get_next();
8077 	}
8078 }
8079 
8080 // Goober5000
special_argument_appears_in_sexp_tree(int node)8081 int special_argument_appears_in_sexp_tree(int node)
8082 {
8083 	// empty tree
8084 	if (node < 0)
8085 		return 0;
8086 
8087 	// special argument?
8088 	if (!strcmp(Sexp_nodes[node].text, SEXP_ARGUMENT_STRING))
8089 		return 1;
8090 
8091 	// we don't want to include special arguments if they are nested in a new argument SEXP
8092 	if (Sexp_nodes[node].type == SEXP_ATOM && Sexp_nodes[node].subtype == SEXP_ATOM_OPERATOR) {
8093 		if (is_blank_argument_op(get_operator_const(CTEXT(node)))) {
8094 			return 0;
8095 		}
8096 	}
8097 
8098 	return special_argument_appears_in_sexp_tree(CAR(node))
8099 		|| special_argument_appears_in_sexp_tree(CDR(node));
8100 }
8101 
8102 // Goober5000
special_argument_appears_in_sexp_list(int node)8103 int special_argument_appears_in_sexp_list(int node)
8104 {
8105 	// look through list
8106 	while (node != -1)
8107 	{
8108 		// special argument?
8109 		if (!strcmp(Sexp_nodes[node].text, SEXP_ARGUMENT_STRING))
8110 			return 1;
8111 
8112 		node = CDR(node);
8113 	}
8114 
8115 	return 0;
8116 }
8117 
8118 // conditional sexpressions follow
8119 
8120 // Goober5000
eval_when_do_one_exp(int exp)8121 void eval_when_do_one_exp(int exp)
8122 {
8123 	arg_item *ptr;
8124 	int do_node;
8125 
8126 	switch (get_operator_const(CTEXT(exp)))
8127 	{
8128 		// if the op is a conditional then we just evaluate it
8129 		case OP_WHEN:
8130 		case OP_EVERY_TIME:
8131 		case OP_IF_THEN_ELSE:
8132 			// need to account for the possibility this call uses <arguments>
8133 			if (special_argument_appears_in_sexp_tree(exp)) {
8134 				ptr = Sexp_applicable_argument_list.get_next();
8135 				if (ptr != NULL) {
8136 					eval_when_for_each_special_argument(exp);
8137 				}
8138 				else {
8139 					eval_sexp(exp);
8140 				}
8141 			}
8142 			else {
8143 				eval_sexp(exp);
8144 			}
8145 			break;
8146 
8147 		case OP_DO_FOR_VALID_ARGUMENTS:
8148 			if (special_argument_appears_in_sexp_tree(exp)) {
8149 				Warning(LOCATION, "<Argument> used within Do-for-valid-arguments SEXP. Skipping entire SEXP");
8150 				break;
8151 			}
8152 
8153 			do_node = CDR(exp);
8154 			while (do_node != -1) {
8155 				do_action_for_each_special_argument(do_node);
8156 				do_node = CDR(do_node);
8157 			}
8158 			break;
8159 
8160 		case OP_WHEN_ARGUMENT:
8161 		case OP_EVERY_TIME_ARGUMENT:
8162 			eval_sexp(exp);
8163 			break;
8164 
8165 		// otherwise we need to check if arguments are used
8166 		default:
8167 			// if we're using the special argument in this action
8168 			if (special_argument_appears_in_sexp_tree(exp))
8169 			{
8170 				do_action_for_each_special_argument(exp);			// these sexps eval'd only for side effects
8171 			}
8172 			// if not, just evaluate it once as-is
8173 			else
8174 			{
8175 				// Goober5000 - possible bug? (see when val is used below)
8176 				/*val = */eval_sexp(exp);							// these sexps eval'd only for side effects
8177 			}
8178 	}
8179 }
8180 
8181 
8182 // Karajorma
eval_when_do_all_exp(int all_actions,int when_op_num)8183 void eval_when_do_all_exp(int all_actions, int when_op_num)
8184 {
8185 	arg_item *ptr;
8186 	int exp;
8187 	int actions;
8188 	int op_num;
8189 
8190 	bool first_loop = true;
8191 
8192 	// loop through all the supplied arguments
8193 	ptr = Sexp_applicable_argument_list.get_next();
8194 
8195 	while (ptr != NULL)
8196 	{
8197 		// acquire argument to be used
8198 		Sexp_replacement_arguments.push_back(ptr->text);
8199 		actions = all_actions;
8200 
8201 		while (actions != -1)
8202 		{
8203 			exp = CAR(actions);
8204 
8205 			op_num = get_operator_const(CTEXT(exp));
8206 
8207 			if (op_num == OP_DO_FOR_VALID_ARGUMENTS) {
8208 				int do_node = CDR(exp);
8209 				while (do_node != -1) {
8210 					eval_sexp(do_node);
8211 					do_node = CDR(do_node);
8212 				}
8213 			}
8214 			else if ( first_loop || special_argument_appears_in_sexp_tree(exp) ) {
8215 				switch (op_num)
8216 				{
8217 					// if the op is a conditional we have to make sure that it can access arguments
8218 					case OP_WHEN:
8219 					case OP_EVERY_TIME:
8220 					case OP_IF_THEN_ELSE:
8221 						Sexp_current_argument_nesting_level++;
8222 						Sexp_applicable_argument_list.add_data(ptr->text);
8223 						eval_sexp(exp);
8224 						Sexp_applicable_argument_list.clear_nesting_level();
8225 						Sexp_current_argument_nesting_level--;
8226 						break;
8227 
8228 					default:
8229 						eval_sexp(exp);
8230 				}
8231 			}
8232 
8233 			// iterate
8234 			actions = CDR(actions);
8235 
8236 			// if-then-else only has one "if" action
8237 			if (when_op_num == OP_IF_THEN_ELSE)
8238 				break;
8239 		}
8240 
8241 		first_loop = false;
8242 
8243 		// remove the argument
8244 		Sexp_replacement_arguments.pop_back();
8245 		// continue along argument list
8246 		ptr = ptr->get_next();
8247 	}
8248 }
8249 
8250 /**
8251  * This is like using when, but it takes a lot of shortcuts.  It's clearer just to separate it out into its own function, especially since it's not supposed to start
8252  * a new level of special argument handling, like eval_when would do.  It's a lot like the original retail version of eval_when!
8253  */
eval_perform_actions(int n)8254 int eval_perform_actions(int n)
8255 {
8256 	int cond, val, actions;
8257 	Assert( n >= 0 );
8258 
8259 	cond = CAR(n);
8260 	actions = CDR(n);
8261 
8262 	// evaluate the conditional to see what value we eventually return
8263 	val = eval_sexp(cond);
8264 
8265 	// perform all the actions in the rest of the sexp
8266 	// (Since we are technically inside a condition already, no special argument handling is needed.  The special argument, if any,
8267 	// will have been provided by a higher level of nesting.)
8268 	while (actions != -1)
8269 	{
8270 		// get the operator
8271 		int exp = CAR(actions);
8272 		if (exp != -1)
8273 			eval_sexp(exp);
8274 
8275 		// iterate
8276 		actions = CDR(actions);
8277 	}
8278 
8279 	// return whatever val was, but don't return known-*
8280 	if (val == SEXP_KNOWN_TRUE)
8281 		return SEXP_TRUE;
8282 	else if (val == SEXP_KNOWN_FALSE)
8283 		return SEXP_FALSE;
8284 	else
8285 		return val;
8286 }
8287 
8288 /**
8289  * Evaluates the when conditional
8290  *
8291  * @note Goober5000 - added capability for arguments
8292  * @note Goober5000 - and also if-then-else and perform-actions
8293  */
eval_when(int n,int when_op_num)8294 int eval_when(int n, int when_op_num)
8295 {
8296 	int cond, val, actions;
8297 	Assert( n >= 0 );
8298 	arg_item *ptr;
8299 
8300 	// get the parts of the sexp and evaluate the conditional
8301 	if (is_blank_argument_op(when_op_num))
8302 	{
8303 		int arg_handler = CAR(n);
8304 		cond = CADR(n);
8305 		actions = CDDR(n);
8306 
8307 		Sexp_current_argument_nesting_level++;
8308 		// evaluate for custom arguments
8309 		val = eval_sexp(arg_handler, cond);
8310 	}
8311 	// normal evaluation
8312 	else
8313 	{
8314 		cond = CAR(n);
8315 		actions = CDR(n);
8316 
8317 		// evaluate just as-is
8318 		val = eval_sexp(cond);
8319 	}
8320 
8321 
8322 	// if value is true, perform the actions in the 'then' part
8323 	if (val == SEXP_TRUE || val == SEXP_KNOWN_TRUE)
8324 	{
8325 		// get the operator
8326 		int exp = CAR(actions);
8327 
8328 		// if the mod.tbl setting is in effect we want to each evaluate all the SEXPs for
8329 		// each argument
8330 		if (True_loop_argument_sexps && special_argument_appears_in_sexp_tree(exp)) {
8331 			if (exp != -1) {
8332 				eval_when_do_all_exp(actions, when_op_num);
8333 			}
8334 		}
8335 		// without the mod.tbl setting (or if there are no arguments in this SEXP) we loop
8336 		// through every action performing them for all arguments
8337 		else {
8338 			while (actions != -1)
8339 			{
8340 				// get the operator
8341 				exp = CAR(actions);
8342 				if (exp != -1)
8343 					eval_when_do_one_exp(exp);
8344 
8345 				// iterate
8346 				actions = CDR(actions);
8347 
8348 				// if-then-else only has one "if" action
8349 				if (when_op_num == OP_IF_THEN_ELSE)
8350 					break;
8351 			}
8352 		}
8353 	}
8354 	// if-then-else has actions to perform under "else"
8355 	else if ((val == SEXP_FALSE || val == SEXP_KNOWN_FALSE) && when_op_num == OP_IF_THEN_ELSE)
8356 	{
8357 		// skip past the "if" action
8358 		actions = CDR(actions);
8359 
8360 		// loop through every action
8361 		while (actions != -1)
8362 		{
8363 			// get the operator
8364 			int exp = CAR(actions);
8365 			if (exp != -1)
8366 				eval_when_do_one_exp(exp);
8367 
8368 			// iterate
8369 			actions = CDR(actions);
8370 		}
8371 
8372 		// invert val so that we behave like a when with opposite results
8373 		if (val == SEXP_KNOWN_FALSE)
8374 			val = SEXP_KNOWN_TRUE;
8375 		else
8376 			val = SEXP_TRUE;
8377 	}
8378 
8379 	if (is_blank_argument_op(when_op_num))
8380 	{
8381 		if (Log_event) {
8382 			ptr = Sexp_applicable_argument_list.get_next();
8383 			while(ptr != NULL) {
8384 				// See if we have an argument.
8385 				Current_event_log_argument_buffer->push_back(ptr->text);
8386 				ptr = ptr->get_next();
8387 			}
8388 		}
8389 
8390 		// clean up any special sexp stuff
8391 		Sexp_applicable_argument_list.clear_nesting_level();
8392 		Sexp_current_argument_nesting_level--;
8393 	}
8394 
8395 	if (Sexp_nodes[cond].value == SEXP_KNOWN_FALSE)
8396 		return SEXP_KNOWN_FALSE;  // no need to waste time on this anymore
8397 
8398 	if (val == SEXP_KNOWN_FALSE)
8399 		return SEXP_FALSE;  // can't return known false, as this would bypass future actions under the when
8400 
8401 	return val;
8402 }
8403 
8404 /**
8405  * Evaluate the conditional
8406  */
eval_cond(int n)8407 int eval_cond(int n)
8408 {
8409 	int cond = 0, node, val = SEXP_FALSE;
8410 
8411 	Assert (n >= 0);
8412 	while (n >= 0)
8413 	{
8414 		node = CAR(n);
8415 		cond = CAR(node);
8416 		val = eval_sexp(cond);
8417 
8418 		// if the conditional evaluated to true, then we must evaluate the rest of the expression returning
8419 		// the value of this evaluation
8420 		if (val == SEXP_TRUE || val == SEXP_KNOWN_TRUE)
8421 		{
8422 			int actions, exp;
8423 
8424 			val = SEXP_FALSE;
8425 			actions = CDR(node);
8426 			while (actions >= 0)
8427 			{
8428 				exp = CAR(actions);
8429 				if (exp >= -1)
8430 					val = eval_sexp(exp);								// these sexp evaled only for side effects
8431 
8432 				actions = CDR(actions);
8433 			}
8434 
8435 			break;
8436 		}
8437 
8438 		// move onto the next cond clause
8439 		n = CDR(n);
8440 	}
8441 
8442 	return val;
8443 }
8444 
8445 // Goober5000
8446 // NOTE: if you change this function, check to see if the following function should also be changed!
test_argument_nodes_for_condition(int n,int condition_node,int * num_true,int * num_false,int * num_known_true,int * num_known_false)8447 int test_argument_nodes_for_condition(int n, int condition_node, int *num_true, int *num_false, int *num_known_true, int *num_known_false)
8448 {
8449 	int val, num_valid_arguments;
8450 	Assert(n != -1 && condition_node != -1);
8451 	Assert((num_true != NULL) && (num_false != NULL) && (num_known_true != NULL) && (num_known_false != NULL));
8452 
8453 	// ensure special argument list is empty
8454 	Sexp_applicable_argument_list.clear_nesting_level();
8455 	Applicable_arguments_temp.clear();
8456 
8457 	// ditto for counters
8458 	num_valid_arguments = 0;
8459 	*num_true = 0;
8460 	*num_false = 0;
8461 	*num_known_true = 0;
8462 	*num_known_false = 0;
8463 
8464 	// loop through all arguments
8465 	while (n != -1)
8466 	{
8467 		// only eval this argument if it's valid
8468 		if (Sexp_nodes[n].flags & SNF_ARGUMENT_VALID)
8469 		{
8470 			// flush conditional to avoid short-circuiting
8471 			flush_sexp_tree(condition_node);
8472 
8473 			// evaluate conditional for current argument
8474 			Sexp_replacement_arguments.push_back(Sexp_nodes[n].text);
8475 			val = eval_sexp(condition_node);
8476 
8477 			switch (val)
8478 			{
8479 				case SEXP_TRUE:
8480 					(*num_true)++;
8481 					Applicable_arguments_temp.push_back(Sexp_nodes[n].text);
8482 					break;
8483 
8484 				case SEXP_FALSE:
8485 					(*num_false)++;
8486 					break;
8487 
8488 				case SEXP_KNOWN_TRUE:
8489 					(*num_known_true)++;
8490 					Applicable_arguments_temp.push_back(Sexp_nodes[n].text);
8491 					break;
8492 
8493 				case SEXP_KNOWN_FALSE:
8494 					(*num_known_false)++;
8495 					break;
8496 			}
8497 
8498 			// clear argument, but not list, as we'll need it later
8499 			Sexp_replacement_arguments.pop_back();
8500 
8501 			// increment
8502 			num_valid_arguments++;
8503 		}
8504 
8505 		// continue along argument list
8506 		n = CDR(n);
8507 	}
8508 
8509 	// now we write from the temporary store into the real one, reversing the order. We do this because
8510 	// Sexp_applicable_argument_list is a stack and we want the first argument in the list to be the first one out
8511 	while (!Applicable_arguments_temp.empty())
8512 	{
8513 		Sexp_applicable_argument_list.add_data(Applicable_arguments_temp.back());
8514 		Applicable_arguments_temp.pop_back();
8515 	}
8516 
8517 	return num_valid_arguments;
8518 }
8519 
8520 // Goober5000
8521 // NOTE: if you change this function, check to see if the previous function should also be changed!
test_argument_vector_for_condition(SCP_vector<char * > argument_vector,bool already_dupped,int condition_node,int * num_true,int * num_false,int * num_known_true,int * num_known_false)8522 int test_argument_vector_for_condition(SCP_vector<char*> argument_vector, bool already_dupped, int condition_node, int *num_true, int *num_false, int *num_known_true, int *num_known_false)
8523 {
8524 	int val, num_valid_arguments;
8525 	Assert(condition_node != -1);
8526 	Assert((num_true != NULL) && (num_false != NULL) && (num_known_true != NULL) && (num_known_false != NULL));
8527 
8528 	// ensure special argument list is empty
8529 	Sexp_applicable_argument_list.clear_nesting_level();
8530 	Applicable_arguments_temp.clear();
8531 
8532 	// ditto for counters
8533 	num_valid_arguments = 0;
8534 	*num_true = 0;
8535 	*num_false = 0;
8536 	*num_known_true = 0;
8537 	*num_known_false = 0;
8538 
8539 	// loop through all arguments
8540 	for (size_t i = 0; i < argument_vector.size(); i++)
8541 	{
8542 		// since we can't see or modify the validity, assume all are valid
8543 		{
8544 			// flush conditional to avoid short-circuiting
8545 			flush_sexp_tree(condition_node);
8546 
8547 			// evaluate conditional for current argument
8548 			Sexp_replacement_arguments.push_back(argument_vector[i]);
8549 			val = eval_sexp(condition_node);
8550 
8551 			switch (val)
8552 			{
8553 				case SEXP_TRUE:
8554 					(*num_true)++;
8555 					Applicable_arguments_temp.push_back(argument_vector[i]);
8556 					break;
8557 
8558 				case SEXP_FALSE:
8559 					(*num_false)++;
8560 					break;
8561 
8562 				case SEXP_KNOWN_TRUE:
8563 					(*num_known_true)++;
8564 					Applicable_arguments_temp.push_back(argument_vector[i]);
8565 					break;
8566 
8567 				case SEXP_KNOWN_FALSE:
8568 					(*num_known_false)++;
8569 					break;
8570 			}
8571 
8572 			// if the argument was already dup'd, but not added as an applicable argument,
8573 			// we need to free it here before we cause a memory leak
8574 			if ((val == SEXP_FALSE || val == SEXP_KNOWN_FALSE) && already_dupped)
8575 				free(argument_vector[i]);
8576 
8577 			// clear argument, but not list, as we'll need it later
8578 			Sexp_replacement_arguments.pop_back();
8579 
8580 			// increment
8581 			num_valid_arguments++;
8582 		}
8583 	}
8584 
8585 	// now we write from the temporary store into the real one, reversing the order. We do this because
8586 	// Sexp_applicable_argument_list is a stack and we want the first argument in the list to be the first one out
8587 	while (!Applicable_arguments_temp.empty())
8588 	{
8589 		// we need to dup the strings regardless, since we're not using Sexp_nodes[n].text as a string buffer,
8590 		// but we need to know whether the calling function dup'd them, or whether we should dup them here
8591 		if (already_dupped)
8592 			Sexp_applicable_argument_list.add_data_set_dup(Applicable_arguments_temp.back());
8593 		else
8594 			Sexp_applicable_argument_list.add_data_dup(Applicable_arguments_temp.back());
8595 
8596 		Applicable_arguments_temp.pop_back();
8597 	}
8598 
8599 	return num_valid_arguments;
8600 }
8601 
8602 // Goober5000
eval_any_of(int arg_handler_node,int condition_node)8603 int eval_any_of(int arg_handler_node, int condition_node)
8604 {
8605 	int n, num_valid_arguments, num_true, num_false, num_known_true, num_known_false;
8606 	Assert(arg_handler_node != -1 && condition_node != -1);
8607 
8608 	// the arguments should just be data, not operators, so we can skip the CAR
8609 	n = CDR(arg_handler_node);
8610 
8611 	// test the whole argument list
8612 	num_valid_arguments = test_argument_nodes_for_condition(n, condition_node, &num_true, &num_false, &num_known_true, &num_known_false);
8613 
8614 	// use the sexp_or algorithm
8615 	if (num_known_true)
8616 		return SEXP_KNOWN_TRUE;
8617 	else if (num_known_false == num_valid_arguments)
8618 		return SEXP_KNOWN_FALSE;
8619 	else if (num_true)
8620 		return SEXP_TRUE;
8621 	else
8622 		return SEXP_FALSE;
8623 }
8624 
8625 // Goober5000
eval_every_of(int arg_handler_node,int condition_node)8626 int eval_every_of(int arg_handler_node, int condition_node)
8627 {
8628 	int n, num_valid_arguments, num_true, num_false, num_known_true, num_known_false;
8629 	Assert(arg_handler_node != -1 && condition_node != -1);
8630 
8631 	// the arguments should just be data, not operators, so we can skip the CAR
8632 	n = CDR(arg_handler_node);
8633 
8634 	// test the whole argument list
8635 	num_valid_arguments = test_argument_nodes_for_condition(n, condition_node, &num_true, &num_false, &num_known_true, &num_known_false);
8636 
8637 	// use the sexp_and algorithm
8638 	if (num_known_false)
8639 		return SEXP_KNOWN_FALSE;
8640 	else if (num_known_true == num_valid_arguments)
8641 		return SEXP_KNOWN_TRUE;
8642 	else if (num_false)
8643 		return SEXP_FALSE;
8644 	else
8645 		return SEXP_TRUE;
8646 }
8647 
8648 // Goober5000
eval_number_of(int arg_handler_node,int condition_node)8649 int eval_number_of(int arg_handler_node, int condition_node)
8650 {
8651 	int n, num_valid_arguments, num_true, num_false, num_known_true, num_known_false, threshold;
8652 	Assert(arg_handler_node != -1 && condition_node != -1);
8653 
8654 	// the arguments should just be data, not operators, so we can skip the CAR
8655 	n = CDR(arg_handler_node);
8656 
8657 	// the first argument is the number threshold
8658 	threshold = eval_num(n);
8659 	n = CDR(n);
8660 
8661 	// test the whole argument list
8662 	num_valid_arguments = test_argument_nodes_for_condition(n, condition_node, &num_true, &num_false, &num_known_true, &num_known_false);
8663 
8664 	// use the sexp_or algorithm, modified
8665 	// (true if at least threshold arguments are true)
8666 	if (num_known_true >= threshold)
8667 		return SEXP_KNOWN_TRUE;
8668 	else if (num_valid_arguments - num_known_false < threshold)
8669 		return SEXP_KNOWN_FALSE;
8670 	else if (num_true + num_known_true >= threshold)
8671 		return SEXP_TRUE;
8672 	else
8673 		return SEXP_FALSE;
8674 }
8675 
8676 // Goober5000
8677 // this works a little differently... we randomly pick one argument to use
8678 // for our condition, but this argument must be saved among sexp calls...
8679 // so we select an argument and set its flag
8680 // addendum: hook karajorma's random-multiple-of into the same sexp
eval_random_of(int arg_handler_node,int condition_node,bool multiple)8681 int eval_random_of(int arg_handler_node, int condition_node, bool multiple)
8682 {
8683 	int n = -1, i, val, num_valid_args, random_argument;
8684 	Assert(arg_handler_node != -1 && condition_node != -1);
8685 
8686 	// find which argument we picked, if we picked one
8687 	if (!multiple)
8688 	{
8689 		n = CDR(arg_handler_node);
8690 
8691 		// iterate to the argument we previously selected
8692 		for ( ; n != -1; n = CDR(n))
8693 		{
8694 			if (Sexp_nodes[n].flags & SNF_ARGUMENT_SELECT)
8695 				break;
8696 		}
8697 	}
8698 
8699 	// if argument not found (or never specified in the first place), we have to pick one
8700 	if (n == -1)
8701 	{
8702 		n = CDR(arg_handler_node);
8703 
8704 		// get the number of valid arguments
8705 		num_valid_args = query_sexp_args_count(arg_handler_node, true);
8706 		if (num_valid_args == 0)
8707 		{
8708 			return SEXP_FALSE;
8709 		}
8710 
8711 		// pick an argument and iterate to it
8712 		random_argument = rand_internal(1, num_valid_args);
8713 		i = 0;
8714 		while (true)
8715 		{
8716 			Assert(n >= 0);
8717 
8718 			// count only valid arguments
8719 			if (Sexp_nodes[n].flags & SNF_ARGUMENT_VALID)
8720 				i++;
8721 
8722 			// if we're at the right one, we're done
8723 			if (i >= random_argument)
8724 				break;
8725 
8726 			// iterate
8727 			n = CDR(n);
8728 		}
8729 
8730 		// save it, if we're saving
8731 		if (!multiple)
8732 		{
8733 			Sexp_nodes[n].flags |= SNF_ARGUMENT_SELECT;
8734 		}
8735 	}
8736 
8737 	// only eval this argument if it's valid
8738 	val = SEXP_FALSE;
8739 	if (Sexp_nodes[n].flags & SNF_ARGUMENT_VALID)
8740 	{
8741 		// flush stuff
8742 		Sexp_applicable_argument_list.clear_nesting_level();
8743 		flush_sexp_tree(condition_node);
8744 
8745 		// evaluate conditional for current argument
8746 		Sexp_replacement_arguments.push_back(Sexp_nodes[n].text);
8747 		val = eval_sexp(condition_node);
8748 
8749 		// true?
8750 		if (val == SEXP_TRUE || val == SEXP_KNOWN_TRUE)
8751 		{
8752 			Sexp_applicable_argument_list.add_data(Sexp_nodes[n].text);
8753 		}
8754 
8755 		// clear argument, but not list, as we'll need it later
8756 		Sexp_replacement_arguments.pop_back();
8757 	}
8758 
8759 	// true if our selected argument is true
8760 	return val;
8761 }
8762 
8763 // Karajorma - this conditional returns the first valid option on its list.
eval_in_sequence(int arg_handler_node,int condition_node)8764 int eval_in_sequence(int arg_handler_node, int condition_node)
8765 {
8766 	int val = SEXP_FALSE;
8767 	int n = -1 ;
8768 
8769 	Assert(arg_handler_node != -1 && condition_node != -1);
8770 
8771 	// get the first argument
8772 	n = CDR(arg_handler_node);
8773 	Assert (n != -1);
8774 
8775 	// loop through the nodes until we find one that is holds a valid argument or run out of nodes
8776 	for (int i=1 ; i<query_sexp_args_count(arg_handler_node) ; i++)
8777 	{
8778 		if (!(Sexp_nodes[n].flags & SNF_ARGUMENT_VALID)) {
8779 			n = CDR(n) ;
8780 		}
8781 		// if we've found a valid node there is no need to continue
8782 		else {
8783 			break;
8784 		}
8785 	}
8786 
8787 	// Only execute if the argument is valid (if all nodes were invalid we would still reach this point)
8788 	if (Sexp_nodes[n].flags & SNF_ARGUMENT_VALID)
8789 	{
8790 		// flush stuff
8791 		Sexp_applicable_argument_list.clear_nesting_level();
8792 		flush_sexp_tree(condition_node);
8793 
8794 		// evaluate conditional for current argument
8795 		Sexp_replacement_arguments.push_back(Sexp_nodes[n].text);
8796 		val = eval_sexp(condition_node);
8797 
8798 		// true?
8799 		if (val == SEXP_TRUE || val == SEXP_KNOWN_TRUE)
8800 		{
8801 			Sexp_applicable_argument_list.add_data(Sexp_nodes[n].text);
8802 		}
8803 
8804 		// clear argument, but not list, as we'll need it later
8805 		Sexp_replacement_arguments.pop_back();
8806 	}
8807 
8808 	// return the value of the conditional
8809 	return val;
8810 }
8811 
8812 // is there a better place to put this?  seems useful...
8813 // credit to http://stackoverflow.com/questions/1903954/is-there-a-standard-sign-function-signum-sgn-in-c-c
8814 template <typename T>
sign(T t)8815 T sign(T t)
8816 {
8817     if (t == 0)
8818         return T(0);
8819     else
8820         return (t < 0) ? T(-1) : T(1);
8821 }
8822 
8823 // Goober5000
eval_for_counter(int arg_handler_node,int condition_node)8824 int eval_for_counter(int arg_handler_node, int condition_node)
8825 {
8826 	int n, num_valid_arguments, num_true, num_false, num_known_true, num_known_false;
8827 	int i, counter_start, counter_stop, counter_step;
8828 	char buf[NAME_LENGTH];
8829 	Assert(arg_handler_node != -1 && condition_node != -1);
8830 
8831 	// determine the counter parameters
8832 	n = CDR(arg_handler_node);
8833 	counter_start = eval_num(n);
8834 	n = CDR(n);
8835 	counter_stop = eval_num(n);
8836 	n = CDR(n);
8837 	counter_step = (n >= 0) ? eval_num(n) : 1;
8838 
8839 	// a bunch of error checking
8840 	if (counter_step == 0)
8841 	{
8842 		Warning(LOCATION, "A counter increment of 0 is illegal!  (start=%d, stop=%d, increment=%d)", counter_start, counter_stop, counter_step);
8843 		return SEXP_KNOWN_FALSE;
8844 	}
8845 	else if (counter_start == counter_stop)
8846 	{
8847 		Warning(LOCATION, "The counter start and stop values are identical!  (start=%d, stop=%d, increment=%d)", counter_start, counter_stop, counter_step);
8848 		return SEXP_KNOWN_FALSE;
8849 	}
8850 	else if (sign(counter_stop - counter_start) != sign(counter_step))
8851 	{
8852 		Warning(LOCATION, "The counter cannot complete with the given values!  (start=%d, stop=%d, increment=%d)", counter_start, counter_stop, counter_step);
8853 		return SEXP_KNOWN_FALSE;
8854 	}
8855 
8856 	// build a vector of counter values
8857 	SCP_vector<char*> argument_vector;
8858 	for (i = counter_start; ((counter_step > 0) ? i <= counter_stop : i >= counter_stop); i += counter_step)
8859 	{
8860 		sprintf(buf, "%d", i);
8861 		argument_vector.push_back(strdup(buf));
8862 	}
8863 
8864 	// test the whole argument vector
8865 	num_valid_arguments = test_argument_vector_for_condition(argument_vector, true, condition_node, &num_true, &num_false, &num_known_true, &num_known_false);
8866 
8867 	// use the sexp_or algorithm
8868 	if (num_known_true)
8869 		return SEXP_KNOWN_TRUE;
8870 	else if (num_known_false == num_valid_arguments)
8871 		return SEXP_KNOWN_FALSE;
8872 	else if (num_true)
8873 		return SEXP_TRUE;
8874 	else
8875 		return SEXP_FALSE;
8876 }
8877 
sexp_change_all_argument_validity(int n,bool invalidate)8878 void sexp_change_all_argument_validity(int n, bool invalidate)
8879 {
8880 	int arg_handler, arg_n;
8881 
8882 	arg_handler = get_handler_for_x_of_operator(n);
8883 
8884 	// can't change validity of for-counter
8885 	if (get_operator_const(CTEXT(arg_handler)) == OP_FOR_COUNTER)
8886 		return;
8887 
8888 	while (n != -1)
8889 	{
8890 		arg_n = CDR(arg_handler);
8891 		while (arg_n != -1) {
8892 			if (invalidate) {
8893 				Sexp_nodes[arg_n].flags &= ~SNF_ARGUMENT_VALID;
8894 			}
8895 			else {
8896 				Sexp_nodes[arg_n].flags |= SNF_ARGUMENT_VALID;
8897 			}
8898 			// iterate
8899 			arg_n = CDR(arg_n);
8900 		}
8901 
8902 		// iterate
8903 		n = CDR(n);
8904 	}
8905 }
8906 
sexp_num_valid_arguments(int n)8907 int sexp_num_valid_arguments( int n )
8908 {
8909 	int arg_handler, arg_n;
8910 	int matches = 0;
8911 
8912 	arg_handler = get_handler_for_x_of_operator(n);
8913 
8914 	// loop through arguments
8915 	arg_n = CDR(arg_handler);
8916 	while (arg_n != -1) {
8917 		if (Sexp_nodes[arg_n].flags & SNF_ARGUMENT_VALID) {
8918 			matches++;
8919 		}
8920 
8921 
8922 		// iterate
8923 		arg_n = CDR(arg_n);
8924 	}
8925 
8926 	return matches;
8927 }
8928 
8929 // Goober5000
sexp_change_argument_validity(int n,bool invalidate)8930 void sexp_change_argument_validity(int n, bool invalidate)
8931 {
8932 	int arg_handler, arg_n;
8933 	bool toggled;
8934 
8935 	arg_handler = get_handler_for_x_of_operator(n);
8936 
8937 	// can't change validity of for-counter
8938 	if (get_operator_const(CTEXT(arg_handler)) == OP_FOR_COUNTER)
8939 		return;
8940 
8941 	// loop through arguments
8942 	while (n != -1)
8943 	{
8944 		toggled = false;
8945 
8946 		// first we must check if the arg_handler marks a selection. At the moment random-of is the only one that does this
8947 		arg_n = CDR(arg_handler);
8948 		while (invalidate && (arg_n != -1)) {
8949 			if (Sexp_nodes[arg_n].flags & SNF_ARGUMENT_SELECT) {
8950 				// now check if the selected argument matches the one we want to invalidate
8951 				if (!strcmp(CTEXT(n), CTEXT(arg_n))) {
8952 					// set it as invalid
8953 					Sexp_nodes[arg_n].flags &= ~SNF_ARGUMENT_VALID;
8954 					toggled = true;
8955 				}
8956 			}
8957 
8958 			// iterate
8959 			arg_n = CDR(arg_n);
8960 		}
8961 
8962 		if (!toggled) {
8963 			// search for argument in arg_handler list
8964 			arg_n = CDR(arg_handler);
8965 			while (arg_n != -1)
8966 			{
8967 				// match?
8968 				if (!strcmp(CTEXT(n), CTEXT(arg_n)))
8969 				{
8970 					if (invalidate) {
8971 						// we need to check if the argument is already invalid as some argument lists may contain duplicates
8972 						if (Sexp_nodes[arg_n].flags & SNF_ARGUMENT_VALID) {
8973 							// set it as invalid
8974 							Sexp_nodes[arg_n].flags &= ~SNF_ARGUMENT_VALID;
8975 
8976 							// exit inner loop
8977 							break;
8978 						}
8979 					}
8980 					else {
8981 						if (!(Sexp_nodes[arg_n].flags & SNF_ARGUMENT_VALID)) {
8982 							// set it as valid
8983 							Sexp_nodes[arg_n].flags |= SNF_ARGUMENT_VALID;
8984 
8985 							// exit inner loop
8986 							break;
8987 						}
8988 					}
8989 				}
8990 
8991 				// iterate
8992 				arg_n = CDR(arg_n);
8993 			}
8994 		}
8995 
8996 		// iterate
8997 		n = CDR(n);
8998 	}
8999 }
9000 
get_handler_for_x_of_operator(int n)9001 int get_handler_for_x_of_operator(int n)
9002 {
9003 	int conditional, arg_handler;
9004 
9005 	if (n < 0) {
9006 		return -1;
9007 	}
9008 
9009 	conditional = n;
9010 	do {
9011 		// find the conditional sexp
9012 		conditional = find_parent_operator(conditional);
9013 		if (conditional == -1) {
9014 			return -1;
9015 		}
9016 	}
9017 	while (!is_blank_argument_op(get_operator_const(CTEXT(conditional))));
9018 
9019 	// get the first op of the parent, which should be a *_of operator
9020 	arg_handler = CADR(conditional);
9021 	if (!is_blank_of_op(get_operator_const(CTEXT(arg_handler)))) {
9022 		return -1;
9023 	}
9024 
9025 	return arg_handler;
9026 
9027 }
9028 
9029 // Goober5000
is_blank_argument_op(int op_const)9030 bool is_blank_argument_op(int op_const)
9031 {
9032 	switch (op_const)
9033 	{
9034 		case OP_WHEN_ARGUMENT:
9035 		case OP_EVERY_TIME_ARGUMENT:
9036 			return true;
9037 
9038 		default:
9039 			return false;
9040 	}
9041 }
9042 
9043 // Goober5000
is_blank_of_op(int op_const)9044 bool is_blank_of_op(int op_const)
9045 {
9046 	switch (op_const)
9047 	{
9048 		case OP_ANY_OF:
9049 		case OP_EVERY_OF:
9050 		case OP_NUMBER_OF:
9051 		case OP_RANDOM_OF:
9052 		case OP_RANDOM_MULTIPLE_OF:
9053 		case OP_IN_SEQUENCE:
9054 		case OP_FOR_COUNTER:
9055 			return true;
9056 
9057 		default:
9058 			return false;
9059 	}
9060 }
9061 
9062 // Goober5000 - added wing capability
sexp_is_iff(int n)9063 int sexp_is_iff(int n)
9064 {
9065 	int i, team;
9066 
9067 	// iff value is the first parameter, second is a list of one or more ships/wings to check to see if the
9068 	// iff value matches
9069 	team = iff_lookup(CTEXT(n));
9070 
9071 	n = CDR(n);
9072 	for ( ; n != -1; n = CDR(n) )
9073 	{
9074 		object_ship_wing_point_team oswpt;
9075 		sexp_get_object_ship_wing_point_team(&oswpt, CTEXT(n));
9076 
9077 		switch (oswpt.type)
9078 		{
9079 			case OSWPT_TYPE_SHIP:
9080 			{
9081 				// if the team doesn't match the team specified, return false immediately
9082 				if (oswpt.shipp->team != team)
9083 					return SEXP_FALSE;
9084 
9085 				break;
9086 			}
9087 
9088 			case OSWPT_TYPE_PARSE_OBJECT:
9089 			{
9090 				// if the team doesn't match the team specified, return false immediately
9091 				if (oswpt.p_objp->team != team)
9092 					return SEXP_FALSE;
9093 
9094 				break;
9095 			}
9096 
9097 			case OSWPT_TYPE_WING:
9098 			{
9099 				for (i = 0; i < oswpt.wingp->current_count; i++)
9100 				{
9101 					// if the team doesn't match the team specified, return false immediately
9102 					if (Ships[oswpt.wingp->ship_index[i]].team != team)
9103 						return SEXP_FALSE;
9104 				}
9105 
9106 				break;
9107 			}
9108 
9109 		}
9110 
9111 	}
9112 
9113 	// got this far: we must be okay for all ships/wings
9114 	return SEXP_TRUE;
9115 }
9116 
9117 // Goober5000
sexp_ingame_ship_change_iff(ship * shipp,int new_team)9118 void sexp_ingame_ship_change_iff(ship *shipp, int new_team)
9119 {
9120 	Assert(shipp != NULL);
9121 
9122 	shipp->team = new_team;
9123 
9124 	// send a network packet if we need to
9125 	if( MULTIPLAYER_MASTER && (Net_player != NULL) && (shipp->objnum >= 0))
9126 		send_change_iff_packet(Objects[shipp->objnum].net_signature, new_team);
9127 }
9128 
9129 // Goober5000
sexp_parse_ship_change_iff(p_object * parse_obj,int new_team)9130 void sexp_parse_ship_change_iff(p_object *parse_obj, int new_team)
9131 {
9132 	Assert(parse_obj);
9133 
9134 	parse_obj->team = new_team;
9135 }
9136 
9137 // Goober5000 - added wing capability
sexp_change_iff(int n)9138 void sexp_change_iff(int n)
9139 {
9140 	int new_team;
9141 
9142 	new_team = iff_lookup(CTEXT(n));
9143 	n = CDR(n);
9144 
9145 	for ( ; n != -1; n = CDR(n) )
9146 	{
9147 		object_ship_wing_point_team oswpt;
9148 		sexp_get_object_ship_wing_point_team(&oswpt, CTEXT(n));
9149 
9150 		switch (oswpt.type)
9151 		{
9152 			// change ingame ship
9153 			case OSWPT_TYPE_SHIP:
9154 			{
9155 				sexp_ingame_ship_change_iff(oswpt.shipp, new_team);
9156 
9157 				break;
9158 			}
9159 
9160 			// change ship yet to arrive
9161 			case OSWPT_TYPE_PARSE_OBJECT:
9162 			{
9163 				sexp_parse_ship_change_iff(oswpt.p_objp, new_team);
9164 
9165 				break;
9166 			}
9167 
9168 			// change wing (we must set the flags for all ships present as well as all ships yet to arrive)
9169 			case OSWPT_TYPE_WING:
9170 			case OSWPT_TYPE_WING_NOT_PRESENT:
9171 			{
9172 				// current ships
9173 				for (int i = 0; i < oswpt.wingp->current_count; i++)
9174 					sexp_ingame_ship_change_iff(&Ships[oswpt.wingp->ship_index[i]], new_team);
9175 
9176 				// ships yet to arrive
9177 				for (p_object *p_objp = GET_FIRST(&Ship_arrival_list); p_objp != END_OF_LIST(&Ship_arrival_list); p_objp = GET_NEXT(p_objp))
9178 				{
9179 					if (p_objp->wingnum == WING_INDEX(oswpt.wingp))
9180 						sexp_parse_ship_change_iff(p_objp, new_team);
9181 				}
9182 
9183 				break;
9184 			}
9185 		}
9186 	}
9187 }
9188 
sexp_ingame_ship_change_iff_color(ship * shipp,int observer_team,int observed_team,int alternate_iff_color)9189 void sexp_ingame_ship_change_iff_color(ship *shipp, int observer_team, int observed_team, int alternate_iff_color)
9190 {
9191 	Assert(shipp != NULL);
9192 
9193 	shipp->ship_iff_color[observer_team][observed_team] = alternate_iff_color;
9194 
9195 	// Like the rest of the change_iff_color functions copied with minor alterations from earlier change_iff functions.
9196 	if( MULTIPLAYER_MASTER && (Net_player != NULL) && (shipp->objnum >= 0))
9197 		send_change_iff_color_packet(Objects[shipp->objnum].net_signature, observer_team, observed_team, alternate_iff_color);
9198 }
9199 
sexp_parse_ship_change_iff_color(p_object * parse_obj,int observer_team,int observed_team,int alternate_iff_color)9200 void sexp_parse_ship_change_iff_color(p_object *parse_obj, int observer_team, int observed_team, int alternate_iff_color)
9201 {
9202 	Assert(parse_obj);
9203 
9204 	parse_obj->alt_iff_color[observer_team][observed_team] = alternate_iff_color;
9205 }
9206 
9207  // Wanderer
sexp_change_iff_color(int n)9208 void sexp_change_iff_color(int n)
9209 {
9210 	int observer_team, observed_team, alternate_iff_color;
9211 	int i;
9212 	int rgb[3];
9213 
9214 	// First node
9215 	if(n == -1){
9216 		Warning(LOCATION, "Detected missing observer team parameter in sexp-change_iff_color");
9217 		return;
9218 	}
9219 	observer_team = iff_lookup(CTEXT(n));
9220 
9221 	// Second node
9222 	n = CDR(n);
9223 	if(n == -1){
9224 		Warning(LOCATION, "Detected missing observed team parameter in sexp-change_iff_color");
9225 		return;
9226 	}
9227 	observed_team = iff_lookup(CTEXT(n));
9228 
9229 	// Three following nodes
9230 	for (i=0;i<3;i++)
9231 	{
9232 		n = CDR(n);
9233 		if(n == -1){
9234 			Warning(LOCATION, "Detected incomplete color parameter list in sexp-change_iff_color\n");
9235 			return;
9236 		}
9237 		rgb[i] = eval_num(n);
9238 		if (rgb[i] > 255) {
9239 			Warning(LOCATION, "Invalid argument for iff color in sexp-change-iff-color. Valid range is 0 to 255.\n");
9240 			rgb[i] = 255;
9241 		}
9242 	}
9243 	alternate_iff_color = iff_init_color(rgb[0],rgb[1],rgb[2]);
9244 
9245 	// Rest of the nodes
9246 	n = CDR(n);
9247 	for ( ; n != -1; n = CDR(n) )
9248 	{
9249 		object_ship_wing_point_team oswpt;
9250 		sexp_get_object_ship_wing_point_team(&oswpt, CTEXT(n));
9251 
9252 		switch (oswpt.type)
9253 		{
9254 			// change ingame ship
9255 			case OSWPT_TYPE_SHIP:
9256 			{
9257 				sexp_ingame_ship_change_iff_color(oswpt.shipp, observer_team, observed_team, alternate_iff_color);
9258 
9259 				break;
9260 			}
9261 
9262 			// change ship yet to arrive
9263 			case OSWPT_TYPE_PARSE_OBJECT:
9264 			{
9265 				sexp_parse_ship_change_iff_color(oswpt.p_objp, observer_team, observed_team, alternate_iff_color);
9266 
9267 				break;
9268 			}
9269 
9270 			// change wing (we must set the flags for all ships present as well as all ships yet to arrive)
9271 			case OSWPT_TYPE_WING:
9272 			case OSWPT_TYPE_WING_NOT_PRESENT:
9273 			{
9274 				// current ships
9275 				for (i = 0; i < oswpt.wingp->current_count; i++)
9276 					sexp_ingame_ship_change_iff_color(&Ships[oswpt.wingp->ship_index[i]], observer_team, observed_team, alternate_iff_color);
9277 
9278 				// ships yet to arrive
9279 				for (p_object *p_objp = GET_FIRST(&Ship_arrival_list); p_objp != END_OF_LIST(&Ship_arrival_list); p_objp = GET_NEXT(p_objp))
9280 				{
9281 					if (p_objp->wingnum == WING_INDEX(oswpt.wingp))
9282 						sexp_parse_ship_change_iff_color(p_objp, observer_team, observed_team, alternate_iff_color);
9283 				}
9284 
9285 				break;
9286 			}
9287 		}
9288 	}
9289 }
9290 
9291 // Goober5000
sexp_is_ship_class(int n)9292 int sexp_is_ship_class(int n)
9293 {
9294 	int ship_num, ship_class_num;
9295 
9296 	Assert( n >= 0 );
9297 
9298 	// get class
9299 	ship_class_num = ship_info_lookup(CTEXT(n));
9300 	n = CDR(n);
9301 
9302 	// eval ships
9303 	while (n != -1)
9304 	{
9305 		// get ship
9306 		ship_num = ship_name_lookup(CTEXT(n));
9307 
9308 		// we can't do anything with ships that aren't present
9309 		if (ship_num < 0)
9310 			return SEXP_CANT_EVAL;
9311 
9312 		// if it doesn't match, return false
9313 		if (Ships[ship_num].ship_info_index != ship_class_num)
9314 			return SEXP_FALSE;
9315 
9316 		// increment
9317 		n = CDR(n);
9318 	}
9319 
9320 	// we're this far; it must be true
9321 	return SEXP_TRUE;
9322 }
9323 
9324 // Goober5000
sexp_is_ship_type(int n)9325 int sexp_is_ship_type(int n)
9326 {
9327 	int ship_num, ship_type_num;
9328 
9329 	Assert( n >= 0 );
9330 
9331 	// get type
9332 	ship_type_num = ship_type_name_lookup(CTEXT(n));
9333 	n = CDR(n);
9334 
9335 	// eval ships
9336 	while (n != -1)
9337 	{
9338 		// get ship
9339 		ship_num = ship_name_lookup(CTEXT(n));
9340 
9341 		// we can't do anything with ships that aren't present
9342 		if (ship_num < 0)
9343 			return SEXP_CANT_EVAL;
9344 
9345 		// if it doesn't match, return false
9346 		if(ship_class_query_general_type(Ships[ship_num].ship_info_index) != ship_type_num)
9347 			return SEXP_FALSE;
9348 
9349 		// increment
9350 		n = CDR(n);
9351 	}
9352 
9353 	// we're this far; it must be true
9354 	return SEXP_TRUE;
9355 }
9356 
9357 // Goober5000
9358 // ai class value is the first parameter, second is a ship, rest are subsystems to check
sexp_is_ai_class(int n)9359 int sexp_is_ai_class(int n)
9360 {
9361 	char *ship_name, *subsystem;
9362 	int i, ship_num, ai_class_to_check;
9363 
9364 	Assert ( n >= 0 );
9365 
9366 	// find ai class
9367 	ai_class_to_check = -1;
9368 	for (i=0; i<Num_ai_classes; i++)
9369 	{
9370 		if (!stricmp(Ai_class_names[i], CTEXT(n)))
9371 			ai_class_to_check = i;
9372 	}
9373 
9374 	Assert(ai_class_to_check >= 0);
9375 
9376 	n = CDR(n);
9377 	ship_name = CTEXT(n);
9378 	n = CDR(n);
9379 
9380 	// find ship
9381 	ship_num = ship_name_lookup(ship_name);
9382 
9383 	// we can't do anything with ships that aren't present
9384 	if (ship_num < 0)
9385 		return SEXP_CANT_EVAL;
9386 
9387 	// subsys?
9388 	if (n != -1)
9389 	{
9390 		ship_subsys *ss;
9391 
9392 		// loopity-loop
9393 		for ( ; n != -1; n = CDR(n) )
9394 		{
9395 			subsystem = CTEXT(n);
9396 
9397 			// find the ship subsystem
9398 			// if it doesn't match, or subsys isn't found, return false immediately
9399 			ss = ship_get_subsys(&Ships[ship_num], subsystem);
9400 			if (ss != NULL)
9401 			{
9402 				if (ss->weapons.ai_class != ai_class_to_check)
9403 					return SEXP_FALSE;
9404 			}
9405 			else
9406 				return SEXP_FALSE;
9407 		}
9408 
9409 		// we've come this far; it must all be true
9410 		return SEXP_TRUE;
9411 	}
9412 	// just the ship
9413 	else
9414 	{
9415 		if (Ai_info[Ships[ship_num].ai_index].ai_class == ai_class_to_check)
9416 			return SEXP_TRUE;
9417 		else
9418 			return SEXP_FALSE;
9419 	}
9420 }
9421 
9422 // Goober5000
sexp_change_ai_class(int n)9423 void sexp_change_ai_class(int n)
9424 {
9425 	int i, ship_num, new_ai_class;
9426 
9427 	Assert ( n >= 0 );
9428 
9429 	// find ai class
9430 	new_ai_class = -1;
9431 	for (i=0; i<Num_ai_classes; i++)
9432 	{
9433 		if (!stricmp(Ai_class_names[i], CTEXT(n)))
9434 			new_ai_class = i;
9435 	}
9436 
9437 	Assert(new_ai_class >= 0);
9438 
9439 	// find ship
9440 	n = CDR(n);
9441 	ship_num = ship_name_lookup(CTEXT(n));
9442 	n = CDR(n);
9443 
9444 	// we can't do anything with ships that aren't present
9445 	if (ship_num < 0)
9446 		return;
9447 
9448 	// subsys?
9449 	if (n != -1)
9450 	{
9451 		// loopity-loop
9452 		for ( ; n != -1; n = CDR(n) )
9453 		{
9454 			ship_subsystem_set_new_ai_class(ship_num, CTEXT(n), new_ai_class);
9455 
9456 			// send a network packet if we need to
9457 			if( MULTIPLAYER_MASTER && (Net_player != NULL) && (Ships[ship_num].objnum >= 0))
9458 			{
9459 				send_change_ai_class_packet(Objects[Ships[ship_num].objnum].net_signature, CTEXT(n), new_ai_class);
9460 			}
9461 		}
9462 	}
9463 	// just the one ship
9464 	else
9465 	{
9466 		ship_set_new_ai_class(ship_num, new_ai_class);
9467 
9468 		// send a network packet if we need to
9469 		if( MULTIPLAYER_MASTER && (Net_player != NULL) && (Ships[ship_num].objnum >= 0))
9470 		{
9471 			send_change_ai_class_packet(Objects[Ships[ship_num].objnum].net_signature, NULL, new_ai_class);
9472 		}
9473 	}
9474 }
9475 
9476 // following routine adds an ai goal to a ship structure.  The sexpression index
9477 // passed in should be an ai-goal of the proper form.  The code in MissionGoal should
9478 // check the syntax.
9479 
sexp_add_ship_goal(int n)9480 void sexp_add_ship_goal(int n)
9481 {
9482 	int num, sindex;
9483 	char *ship_name;
9484 
9485 	Assert ( n >= 0 );
9486 	ship_name = CTEXT(n);
9487 	num = ship_name_lookup(ship_name, 1);	// Goober5000 - including player
9488 	if ( num < 0 )									// ship not around anymore???? then forget it!
9489 		return;
9490 
9491 	sindex = CDR(n);
9492 	ai_add_ship_goal_sexp( sindex, AIG_TYPE_EVENT_SHIP, &(Ai_info[Ships[num].ai_index]) );
9493 }
9494 
9495 // identical to above, except add a wing
sexp_add_wing_goal(int n)9496 void sexp_add_wing_goal(int n)
9497 {
9498 	int num, sindex;
9499 	char *wing_name;
9500 
9501 	Assert ( n >= 0 );
9502 	wing_name = CTEXT(n);
9503 	num = wing_name_lookup(wing_name);
9504 	if ( num < 0 )									// ship not around anymore???? then forget it!
9505 		return;
9506 
9507 	sindex = CDR(n);
9508 	ai_add_wing_goal_sexp( sindex, AIG_TYPE_EVENT_WING, num );
9509 }
9510 
9511 /**
9512  * Adds a goal to the specified entry (ships and wings have unique names between the two sets).
9513  */
sexp_add_goal(int n)9514 void sexp_add_goal(int n)
9515 {
9516 	int num, sindex;
9517 	char *name;
9518 
9519 	Assert ( n >= 0 );
9520 	name = CTEXT(n);
9521 	sindex = CDR(n);
9522 
9523 	// first, look for ship name -- if found, then add ship goal.  else look for wing name -- if
9524 	// found, add wing goal
9525 	if ( (num = ship_name_lookup(name, 1)) != -1 )	// Goober5000 - include players
9526 		ai_add_ship_goal_sexp( sindex, AIG_TYPE_EVENT_SHIP, &(Ai_info[Ships[num].ai_index]) );
9527 	else if ( (num = wing_name_lookup(name)) != -1 )
9528 		ai_add_wing_goal_sexp( sindex, AIG_TYPE_EVENT_WING, num );
9529 }
9530 
9531 // Goober5000
sexp_remove_goal(int n)9532 void sexp_remove_goal(int n)
9533 {
9534 	Assert( n >= 0 );
9535 	/* Grab the information that we need about this goal removal action */
9536 	int num, sindex;
9537 	int goalindex;
9538 	char *name;
9539 
9540 	name = CTEXT(n);
9541 	sindex = CDR(n);
9542 
9543 	// first, look for ship name -- if found, then add ship goal.  else look for wing name -- if
9544 	// found, add wing goal
9545 	if ( (num = ship_name_lookup(name, 1)) != -1 )
9546 	{
9547 		goalindex = ai_remove_goal_sexp_sub( sindex, Ai_info[Ships[num].ai_index].goals );
9548 		if ( goalindex >= 0 )
9549 		{
9550 			if ( Ai_info[Ships[num].ai_index].active_goal == goalindex )
9551 				Ai_info[Ships[num].ai_index].active_goal = AI_GOAL_NONE;
9552 		}
9553 	}
9554 	else if ( (num = wing_name_lookup(name)) != -1 )
9555 	{
9556 		ai_remove_wing_goal_sexp( sindex, num );
9557 	}
9558 
9559 }
9560 
9561 /**
9562  * Clear out all AI goals for a ship
9563  */
sexp_clear_ship_goals(int n)9564 void sexp_clear_ship_goals(int n)
9565 {
9566 	int num;
9567 	char *ship_name;
9568 
9569 	Assert ( n >= 0 );
9570 	ship_name = CTEXT(n);
9571 	if ( (num = ship_name_lookup(ship_name, 1)) != -1) 	// Goober5000 - include players
9572 	{
9573 		ai_clear_ship_goals( &(Ai_info[Ships[num].ai_index]) );
9574 	}
9575 }
9576 
9577 /**
9578  * Clear out AI goals for a wing
9579  */
sexp_clear_wing_goals(int n)9580 void sexp_clear_wing_goals(int n)
9581 {
9582 	int num;
9583 	char *wing_name;
9584 
9585 	Assert ( n >= 0 );
9586 	wing_name = CTEXT(n);
9587 	num = wing_name_lookup(wing_name);
9588 	if ( num < 0 )
9589 		return;
9590 	ai_clear_wing_goals( num );
9591 }
9592 
9593 /**
9594  * Clear all AI goals for the given ship or wing
9595  */
sexp_clear_goals(int n)9596 void sexp_clear_goals(int n)
9597 {
9598 	int num;
9599 	char *name;
9600 
9601 	Assert ( n >= 0 );
9602 	while ( n != -1 ) {
9603 		name = CTEXT(n);
9604 		if ( (num = ship_name_lookup(name, 1)) != -1 )	// Goober5000 - include players
9605 			ai_clear_ship_goals( &(Ai_info[Ships[num].ai_index]) );
9606 		else if ( (num = wing_name_lookup(name)) != -1 )
9607 			ai_clear_wing_goals( num );
9608 
9609 		n = CDR(n);
9610 	}
9611 }
9612 
9613 // Goober5000
sexp_hud_disable(int n)9614 void sexp_hud_disable(int n)
9615 {
9616 	int disable_hud = eval_num(n);
9617 	hud_set_draw(!disable_hud);
9618 
9619 	multi_start_callback();
9620 	multi_send_int(disable_hud);
9621 	multi_end_callback();
9622 }
9623 
multi_sexp_hud_disable()9624 void multi_sexp_hud_disable()
9625 {
9626 	int disable_hud;
9627 
9628 	if (multi_get_int(disable_hud)) {
9629 		hud_set_draw(!disable_hud);
9630 	}
9631 }
9632 
9633 // Goober5000
sexp_hud_disable_except_messages(int n)9634 void sexp_hud_disable_except_messages(int n)
9635 {
9636 	int disable_hud = eval_num(n);
9637 	hud_disable_except_messages(disable_hud);
9638 
9639 	multi_start_callback();
9640 	multi_send_int(disable_hud);
9641 	multi_end_callback();
9642 }
9643 
multi_sexp_hud_disable_except_messages()9644 void multi_sexp_hud_disable_except_messages()
9645 {
9646 	int disable_hud;
9647 
9648 	if (multi_get_int(disable_hud)) {
9649 		hud_disable_except_messages(disable_hud);
9650 	}
9651 }
9652 
sexp_hud_set_text_num(int n)9653 void sexp_hud_set_text_num(int n)
9654 {
9655 	char* gaugename = CTEXT(n);
9656 	char tmp[16] = "";
9657 
9658 	HudGauge* cg = hud_get_gauge(gaugename);
9659 	if(cg) {
9660 		sprintf( tmp, "%d", eval_num(CDR(n)) );
9661 		cg->updateCustomGaugeText(tmp);
9662 	}
9663 }
9664 
sexp_hud_set_text(int n)9665 void sexp_hud_set_text(int n)
9666 {
9667 	char* gaugename = CTEXT(n);
9668 	char* text = CTEXT(CDR(n));
9669 
9670 	HudGauge* cg = hud_get_gauge(gaugename);
9671 	if(cg) {
9672 		cg->updateCustomGaugeText(text);
9673 	}
9674 }
9675 
sexp_hud_set_message(int n)9676 void sexp_hud_set_message(int n)
9677 {
9678 	char* gaugename = CTEXT(n);
9679 	char* text = CTEXT(CDR(n));
9680 	SCP_string message;
9681 
9682 	for (int i = 0; i < Num_messages; i++) {
9683 		if ( !stricmp(text, Messages[i].name) ) {
9684 			message = Messages[i].message;
9685 
9686 			sexp_replace_variable_names_with_values(message);
9687 
9688 			HudGauge* cg = hud_get_gauge(gaugename);
9689 			if(cg) {
9690 				cg->updateCustomGaugeText(message);
9691 			} else {
9692 				WarningEx(LOCATION, "Could not find a hud gauge named %s\n", gaugename);
9693 			}
9694 			return;
9695 		}
9696 	}
9697 
9698 	WarningEx(LOCATION, "sexp_hud_set_message couldn't find a message by the name of %s in the mission\n", text);
9699 }
9700 
sexp_hud_set_directive(int n)9701 void sexp_hud_set_directive(int n)
9702 {
9703 	char* gaugename = CTEXT(n);
9704 	char* text = CTEXT(CDR(n));
9705 	char message[MESSAGE_LENGTH];
9706 
9707 	message_translate_tokens(message, text);
9708 
9709 	if (strlen(message) > MESSAGE_LENGTH) {
9710 		WarningEx(LOCATION, "Message %s is too long for use in a HUD gauge. Please shorten it to %d characters or less.", message, MESSAGE_LENGTH);
9711 		return;
9712 	}
9713 
9714 	HudGauge* cg = hud_get_gauge(gaugename);
9715 	if(cg) {
9716 		cg->updateCustomGaugeText(message);
9717 	} else {
9718 		WarningEx(LOCATION, "Could not find a hud gauge named %s\n", gaugename);
9719 	}
9720 }
9721 
sexp_hud_clear_messages()9722 void sexp_hud_clear_messages()
9723 {
9724 	if(Ship_info[Player_ship->ship_info_index].hud_gauges.size() > 0) {
9725 		size_t num_gauges = Ship_info[Player_ship->ship_info_index].hud_gauges.size();
9726 
9727 		for(size_t i = 0; i < num_gauges; i++) {
9728 			if (Ship_info[Player_ship->ship_info_index].hud_gauges[i]->getObjectType() == HUD_OBJECT_MESSAGES) {
9729 				HudGaugeMessages* gauge = static_cast<HudGaugeMessages*>(Ship_info[Player_ship->ship_info_index].hud_gauges[i]);
9730 				gauge->clearMessages();
9731 			}
9732 		}
9733 	} else {
9734 		size_t num_gauges = default_hud_gauges.size();
9735 
9736 		for(size_t i = 0; i < num_gauges; i++) {
9737 			if (default_hud_gauges[i]->getObjectType() == HUD_OBJECT_MESSAGES) {
9738 				HudGaugeMessages* gauge = static_cast<HudGaugeMessages*>(default_hud_gauges[i]);
9739 				gauge->clearMessages();
9740 			}
9741 		}
9742 	}
9743 }
9744 
sexp_hud_set_coords(int n)9745 void sexp_hud_set_coords(int n)
9746 {
9747 	char* gaugename = CTEXT(n);
9748 	int coord_x = eval_num(CDR(n));
9749 	int coord_y = eval_num(CDR(CDR(n)));
9750 
9751 	HudGauge* cg = hud_get_gauge(gaugename);
9752 	if(cg) {
9753 		cg->updateCustomGaugeCoords(coord_x, coord_y);
9754 	}
9755 }
9756 
sexp_hud_set_frame(int n)9757 void sexp_hud_set_frame(int n)
9758 {
9759 	char* gaugename = CTEXT(n);
9760 	int frame_num = eval_num(CDR(n));
9761 
9762 	HudGauge* cg = hud_get_gauge(gaugename);
9763 	if(cg) {
9764 		cg->updateCustomGaugeFrame(frame_num);
9765 	}
9766 }
9767 
sexp_hud_set_color(int n)9768 void sexp_hud_set_color(int n)
9769 {
9770 	char* gaugename = CTEXT(n);
9771 	ubyte red = (ubyte) eval_num(CDR(n));
9772 	ubyte green = (ubyte) eval_num(CDR(CDR(n)));
9773 	ubyte blue = (ubyte) eval_num(CDR(CDR(CDR(n))));
9774 
9775 	HudGauge* cg = hud_get_gauge(gaugename);
9776 	if(cg) {
9777 		cg->sexpLockConfigColor(false);
9778 		cg->updateColor(red, green, blue, (HUD_color_alpha+1)*16);
9779 		cg->sexpLockConfigColor(true);
9780 	}
9781 }
9782 
9783 
9784 // Goober5000
sexp_hud_set_max_targeting_range(int n)9785 void sexp_hud_set_max_targeting_range(int n)
9786 {
9787 	Hud_max_targeting_range = eval_num(n);
9788 
9789 	if (Hud_max_targeting_range < 0) {
9790 		Hud_max_targeting_range = 0;
9791 	}
9792 
9793 	multi_start_callback();
9794 	multi_send_int(Hud_max_targeting_range);
9795 	multi_end_callback();
9796 
9797 }
9798 
multi_sexp_hud_set_max_targeting_range()9799 void multi_sexp_hud_set_max_targeting_range()
9800 {
9801 	multi_get_int(Hud_max_targeting_range);
9802 }
9803 
9804 /* Make sure that the Sexp_hud_display_* get added to the game_state
9805 transitions in freespace.cpp (game_enter_state()). */
9806 int Sexp_hud_display_warpout = 0;
9807 
sexp_hud_display_gauge(int n)9808 void sexp_hud_display_gauge(int n) {
9809 	int show_for = eval_num(n);
9810 	char* gauge = CTEXT(CDR(n));
9811 
9812 	if ( stricmp(SEXP_HUD_GAUGE_WARPOUT, gauge) == 0 ) {
9813 		Sexp_hud_display_warpout = (show_for > 1)? timestamp(show_for) : (show_for);
9814 	}
9815 }
9816 
sexp_hud_gauge_set_active(int n)9817 void sexp_hud_gauge_set_active(int n) {
9818 	HudGauge* hg;
9819 	char* name = CTEXT(n);
9820 	bool active = (is_sexp_true(CDR(n)) != 0);
9821 
9822 	hg = hud_get_gauge(name);
9823 
9824 	if (hg != NULL) {
9825 		hg->updateActive(active);
9826 	}
9827 }
9828 
sexp_hud_set_custom_gauge_active(int node)9829 void sexp_hud_set_custom_gauge_active(int node) {
9830 	HudGauge* hg;
9831 	bool activate = (is_sexp_true(node) > 0);
9832 	node = CDR(node);
9833 	for(; node >= 0; node = CDR(node)) {
9834 
9835 		char* name = CTEXT(node);
9836 		hg = hud_get_gauge(name);
9837 
9838 		if (hg != NULL) {
9839 			hg->updateActive(activate);
9840 		}
9841 
9842 	}
9843 
9844 }
9845 
hud_gauge_type_lookup(char * name)9846 int hud_gauge_type_lookup(char* name) {
9847 	for(int i = 0; i < Num_hud_gauge_types; i++) {
9848 		if(!stricmp(name, Hud_gauge_types[i].name))
9849 			return Hud_gauge_types[i].def;
9850 	}
9851 	return -1;
9852 }
9853 
sexp_hud_activate_gauge_type(int n)9854 void sexp_hud_activate_gauge_type(int n) {
9855 	int config_type = hud_gauge_type_lookup(CTEXT(n));
9856 	bool active = (is_sexp_true(CDR(n)) != 0);
9857 
9858 	if (config_type != -1) {
9859 		if(Ship_info[Player_ship->ship_info_index].hud_gauges.size() > 0) {
9860 			size_t num_gauges = Ship_info[Player_ship->ship_info_index].hud_gauges.size();
9861 
9862 			for(size_t i = 0; i < num_gauges; i++) {
9863 				if (Ship_info[Player_ship->ship_info_index].hud_gauges[i]->getObjectType() == config_type)
9864 					Ship_info[Player_ship->ship_info_index].hud_gauges[i]->updateSexpOverride(!active);
9865 			}
9866 		} else {
9867 			size_t num_gauges = default_hud_gauges.size();
9868 
9869 			for(size_t i = 0; i < num_gauges; i++) {
9870 				if (default_hud_gauges[i]->getObjectType() == config_type)
9871 					default_hud_gauges[i]->updateSexpOverride(!active);
9872 			}
9873 		}
9874 	}
9875 }
9876 
sexp_hud_set_retail_gauge_active(int node)9877 void sexp_hud_set_retail_gauge_active(int node) {
9878 
9879 	bool activate = (is_sexp_true(node) > 0);
9880 	node = CDR(node);
9881 
9882 	for(; node >= 0; node = CDR(node)) {
9883 
9884 		int config_type = hud_gauge_type_lookup(CTEXT(node));
9885 
9886 		if (config_type != -1) {
9887 			if(Ship_info[Player_ship->ship_info_index].hud_gauges.size() > 0) {
9888 			size_t num_gauges = Ship_info[Player_ship->ship_info_index].hud_gauges.size();
9889 
9890 			for(size_t i = 0; i < num_gauges; i++) {
9891 				if (Ship_info[Player_ship->ship_info_index].hud_gauges[i]->getObjectType() == config_type)
9892 					Ship_info[Player_ship->ship_info_index].hud_gauges[i]->updateSexpOverride(!activate);
9893 				}
9894 			} else {
9895 			size_t num_gauges = default_hud_gauges.size();
9896 
9897 			for(size_t i = 0; i < num_gauges; i++) {
9898 				if (default_hud_gauges[i]->getObjectType() == config_type)
9899 					default_hud_gauges[i]->updateSexpOverride(!activate);
9900 				}
9901 			}
9902 		}
9903 
9904 	}
9905 
9906 }
9907 
multi_sexp_hud_display_gauge()9908 void multi_sexp_hud_display_gauge()
9909 {
9910 	int show_for;
9911 
9912 	if (multi_get_int(show_for)) {
9913 		Sexp_hud_display_warpout = (show_for > 1)? timestamp(show_for) : (show_for);
9914 	}
9915 }
9916 
9917 // Goober5000
9918 /**
9919  * Trigger whether player uses the game AI for stuff
9920  */
sexp_player_use_ai(int flag)9921 void sexp_player_use_ai(int flag)
9922 {
9923 	Player_use_ai = flag ? 1 : 0;
9924 }
9925 
9926 // Karajorma
sexp_allow_treason(int n)9927 void sexp_allow_treason (int n)
9928 {
9929 	n = CDR(n);
9930 	if (n != -1) {
9931 		if ( is_sexp_true(n) ) {
9932 			The_mission.flags |= MISSION_FLAG_NO_TRAITOR;
9933 		}
9934 		else {
9935 			The_mission.flags &= ~MISSION_FLAG_NO_TRAITOR;
9936 		}
9937 	}
9938 }
9939 
sexp_set_player_orders(int n)9940 void sexp_set_player_orders(int n)
9941 {
9942 	ship *shipp;
9943 	int i;
9944 	int allow_order;
9945 	int orders = 0;
9946 	int default_orders;
9947 
9948 	shipp = sexp_get_ship_from_node(n);
9949 
9950 	if (shipp == NULL) {
9951 		return;
9952 	}
9953 
9954 	// we need to know which orders this ship class can accept.
9955 	default_orders = ship_get_default_orders_accepted(&Ship_info[shipp->ship_info_index]);
9956 	n = CDR(n);
9957 	allow_order = is_sexp_true(n);
9958 	n = CDR(n);
9959 	do {
9960 		for (i = 0 ; i < NUM_COMM_ORDER_ITEMS ; i++) {
9961 			// since it's the cheaper test, test first if the ship will even accept this order first
9962 			if (default_orders & Sexp_comm_orders[i].item) {
9963 				if (!stricmp(CTEXT(n), Sexp_comm_orders[i].name)) {
9964 					orders |= Sexp_comm_orders[i].item;
9965 					break;
9966 				}
9967 			}
9968 		}
9969 
9970 		n = CDR(n);
9971 	}while (n >= 0);
9972 
9973 	// set or unset the orders
9974 	if (allow_order) {
9975 		shipp->orders_accepted |= orders;
9976 	}
9977 	else {
9978 		shipp->orders_accepted &= ~orders;
9979 	}
9980 }
9981 
9982 // Goober5000
sexp_change_soundtrack(int n)9983 void sexp_change_soundtrack(int n)
9984 {
9985 	event_sexp_change_soundtrack(CTEXT(n));
9986 
9987 	if (MULTIPLAYER_MASTER) {
9988 		multi_start_callback();
9989 		multi_send_string(CTEXT(n));
9990 		multi_end_callback();
9991 	}
9992 }
9993 
multi_sexp_change_soundtrack()9994 void multi_sexp_change_soundtrack()
9995 {
9996 	char new_track[NAME_LENGTH];
9997 
9998 	if (multi_get_string(new_track)) {
9999 		event_sexp_change_soundtrack(new_track);
10000 	}
10001 }
10002 
10003 // Goober5000
sexp_stop_music(int fade)10004 void sexp_stop_music(int fade)
10005 {
10006 	if ( Sexp_music_handle != -1 ) {
10007 		audiostream_close_file(Sexp_music_handle, fade);
10008 		Sexp_music_handle = -1;
10009 	}
10010 }
10011 
10012 // Goober5000
sexp_music_close()10013 void sexp_music_close()
10014 {
10015 	if ( Cmdline_freespace_no_music ) {
10016 		return;
10017 	}
10018 
10019 	sexp_stop_music();
10020 }
10021 
10022 // Goober5000
sexp_load_music(char * fname,int type=-1)10023 void sexp_load_music(char* fname, int type = -1)
10024 {
10025 	if ( Cmdline_freespace_no_music ) {
10026 		return;
10027 	}
10028 
10029 	if ( Sexp_music_handle != -1 )
10030 	{
10031 		sexp_stop_music();
10032 	}
10033 
10034 	if ( type < 0 )
10035 	{
10036 		type = ASF_MENUMUSIC;
10037 	}
10038 
10039 	if ( fname )
10040 	{
10041 		Sexp_music_handle = audiostream_open( fname, type );
10042 	}
10043 }
10044 
10045 // Goober5000
sexp_start_music(int loop)10046 void sexp_start_music(int loop)
10047 {
10048 	if ( Sexp_music_handle != -1 ) {
10049 		if ( !audiostream_is_playing(Sexp_music_handle) )
10050 			audiostream_play(Sexp_music_handle, (Master_event_music_volume * aav_music_volume), loop);
10051 	}
10052 	else {
10053 		nprintf(("Warning", "Can not play music. sexp_start_music called when no music file is set for Sexp_music_handle!\n"));
10054 	}
10055 }
10056 
10057 // Goober5000
sexp_play_sound_from_table(int n)10058 void sexp_play_sound_from_table(int n)
10059 {
10060 	vec3d origin;
10061 	int sound_index;
10062 
10063 	Assert( n >= 0 );
10064 
10065 	// read in data --------------------------------
10066 	origin.xyz.x = (float)eval_num(n);
10067 	n = CDR(n);
10068 	origin.xyz.y = (float)eval_num(n);
10069 	n = CDR(n);
10070 	origin.xyz.z = (float)eval_num(n);
10071 	n = CDR(n);
10072 	sound_index = eval_num(n);
10073 
10074 
10075 	// play sound effect ---------------------------
10076 	if (sound_index >= 0) {
10077 		game_snd *snd = &Snds[gamesnd_get_by_tbl_index(sound_index)];
10078 		if (snd->min == 0 && snd->max == 0) {
10079 			// if sound doesn't specify 3d range, don't play in 3d
10080 			snd_play( snd, 0.0f, 1.0f, SND_PRIORITY_MUST_PLAY );
10081 		} else {
10082 			snd_play_3d( snd, &origin, &View_position, 0.0f, NULL, 0, 1.0f, SND_PRIORITY_MUST_PLAY );
10083 		}
10084 	}
10085 
10086 	if (MULTIPLAYER_MASTER) {
10087 		multi_start_callback();
10088 		multi_send_float(origin.xyz.x);
10089 		multi_send_float(origin.xyz.y);
10090 		multi_send_float(origin.xyz.z);
10091 		multi_send_int(sound_index);
10092 		multi_end_callback();
10093 	}
10094 }
10095 
multi_sexp_play_sound_from_table()10096 void multi_sexp_play_sound_from_table()
10097 {
10098 	vec3d origin;
10099 	int sound_index = -1;
10100 
10101 	multi_get_float(origin.xyz.x);
10102 	multi_get_float(origin.xyz.y);
10103 	multi_get_float(origin.xyz.z);
10104 	multi_get_int(sound_index);
10105 
10106 
10107 	// play sound effect ---------------------------
10108 	if (sound_index >= 0) {
10109 		game_snd *snd = &Snds[gamesnd_get_by_tbl_index(sound_index)];
10110 		if (snd->min == 0 && snd->max == 0) {
10111 			// if sound doesn't specify 3d range, don't play in 3d
10112 			snd_play( snd, 0.0f, 1.0f, SND_PRIORITY_MUST_PLAY );
10113 		} else {
10114 			snd_play_3d( snd, &origin, &View_position, 0.0f, NULL, 0, 1.0f, SND_PRIORITY_MUST_PLAY );
10115 		}
10116 	}
10117 }
10118 
10119 // Goober5000
sexp_close_sound_from_file(int n)10120 void sexp_close_sound_from_file(int n)
10121 {
10122 	int fade = is_sexp_true(n);
10123 	sexp_stop_music(fade);
10124 
10125 	if (MULTIPLAYER_MASTER) {
10126 		multi_start_callback();
10127 		multi_send_bool(fade ? true : false);
10128 		multi_end_callback();
10129 	}
10130 }
10131 
multi_sexp_close_sound_from_file()10132 void multi_sexp_close_sound_from_file()
10133 {
10134 	bool fade;
10135 	if (multi_get_bool(fade)) {
10136 		sexp_stop_music(fade);
10137 	}
10138 }
10139 
10140 // Goober5000
sexp_play_sound_from_file(int n)10141 void sexp_play_sound_from_file(int n)
10142 {
10143 	int loop = 0;
10144 	int soundfx = 0;
10145 	int type = ASF_MENUMUSIC;
10146 
10147 	// third option, but needed when loading music -
10148 	soundfx = CDDR(n);
10149 
10150 	if (soundfx >= 0) {
10151 		soundfx = eval_sexp(soundfx);
10152 
10153 		if (soundfx > 0) {
10154 			type = ASF_SOUNDFX;
10155 		}
10156 	}
10157 
10158 	// load sound file -----------------------------
10159 	sexp_load_music(CTEXT(n), type);
10160 
10161 	if (MULTIPLAYER_MASTER) {
10162 		multi_start_callback();
10163 		multi_send_string(CTEXT(n));
10164 	}
10165 
10166 	n = CDR(n);
10167 	if (n >= 0) {
10168 		loop = eval_sexp(n);
10169 	}
10170 
10171 	// play sound file -----------------------------
10172 	sexp_start_music(loop);
10173 
10174 	if (MULTIPLAYER_MASTER) {
10175 		multi_send_bool(loop ? true : false);
10176 		multi_end_callback();
10177 	}
10178 }
10179 
multi_sexp_play_sound_from_file()10180 void multi_sexp_play_sound_from_file()
10181 {
10182 	char filename[NAME_LENGTH];
10183 	bool (loop);
10184 	if (!multi_get_string(filename)) {
10185 		return;
10186 	}
10187 	sexp_load_music(filename);
10188 
10189 	multi_get_bool(loop);
10190 	sexp_start_music(loop);
10191 }
10192 
sexp_sound_environment_option_lookup(char * text)10193 int sexp_sound_environment_option_lookup(char *text)
10194 {
10195 	int i;
10196 
10197 	Assert(text != NULL);
10198 	if (text == NULL) {
10199 		return -1;
10200 	}
10201 
10202 	for (i = 0; i < Num_sound_environment_options; i++) {
10203 		if (!strcmp(text, Sound_environment_option[i])) {
10204 			return i;
10205 		}
10206 	}
10207 
10208 	return -1;
10209 }
10210 
10211 // Taylor
sexp_set_sound_environment(int node)10212 void sexp_set_sound_environment(int node)
10213 {
10214 	int n = node;
10215 	sound_env env;
10216 	int preset_id = -1;
10217 
10218 	char *preset = CTEXT(n);
10219 	n = CDR(n);
10220 
10221 	if ( preset && !stricmp(preset, SEXP_NONE_STRING) ) {
10222 		sound_env_disable();
10223 		return;
10224 	}
10225 
10226 	preset_id = ds_eax_get_preset_id( preset );
10227 	if (preset_id < 0) {
10228 		return;
10229 	}
10230 
10231 	// fill in defaults for this preset, in case we don't set everything
10232 	if ( sound_env_get(&env, preset_id) ) {
10233 		return;
10234 	}
10235 
10236 	while (n >= 0) {
10237 		int option = sexp_sound_environment_option_lookup(CTEXT(n));
10238 		n = CDR(n);
10239 
10240 		// watch out for bogus options
10241 		if (n < 0) {
10242 			break;
10243 		}
10244 
10245 		float val = (float)eval_num(n) / 1000.0f;
10246 		n = CDR(n);
10247 
10248 		if ( option == SEO_VOLUME ) {
10249 			env.volume = val;
10250 		} else if ( option == SEO_DECAY_TIME ) {
10251 			env.decay = val;
10252 		} else if ( option == SEO_DAMPING ) {
10253 			env.damping = val;
10254 		}
10255 	}
10256 
10257 	sound_env_set(&env);
10258 }
10259 
10260 // Taylor
sexp_update_sound_environment(int node)10261 void sexp_update_sound_environment(int node)
10262 {
10263 	int n = node;
10264 
10265 	while (n >= 0) {
10266 		int option = sexp_sound_environment_option_lookup(CTEXT(n));
10267 		n = CDR(n);
10268 
10269 		// watch out for bogus options
10270 		if (n < 0) {
10271 			break;
10272 		}
10273 
10274 		float val = (float)eval_num(n) / 1000.0f;
10275 		n = CDR(n);
10276 
10277 		if ( option == SEO_VOLUME ) {
10278 			ds_eax_set_volume(val);
10279 		} else if ( option == SEO_DECAY_TIME ) {
10280 			ds_eax_set_decay_time(val);
10281 		} else if ( option == SEO_DAMPING ) {
10282 			ds_eax_set_damping(val);
10283 		}
10284 	}
10285 }
10286 
10287 //The E
10288 	//From sexp help:
10289 	//{ OP_ADJUST_AUDIO_VOLUME, "adjust-audio-volume\r\n"
10290 	//	"Adjusts the relative volume of one sound type. Takes 2 or 3 arguments....\r\n"
10291 	//	"\t1:\tSound Type to adjust, either Music, Voice or Effects\r\n"
10292 	//	"\t2:\tPercentage of the users' settings to adjust to, 0 will be silence, 100 means the maximum volume as set by the user\r\n"
10293 	//	"\t3:\tFade time (optional), time in milliseconds to adjust the volume"},
10294 
audio_volume_option_lookup(char * text)10295 int audio_volume_option_lookup(char *text)
10296 {
10297 	int i;
10298 
10299 	Assert(text != NULL);
10300 	if (text == NULL) {
10301 		return -1;
10302 	}
10303 
10304 	for (i = 0; i < Num_adjust_audio_options; i++) {
10305 		if (!strcmp(text, Adjust_audio_options[i])) {
10306 			return i;
10307 		}
10308 	}
10309 
10310 	return -1;
10311 }
10312 
sexp_adjust_audio_volume(int node)10313 void sexp_adjust_audio_volume(int node)
10314 {
10315 	int n = node;
10316 
10317 	if (n > 0) {
10318 		int option = audio_volume_option_lookup(CTEXT(n));
10319 		if (option > 0) {
10320 			n = CDR(n);
10321 
10322 			float target_volume = 1.0f;
10323 			if (n >= 0) {
10324 				target_volume = (float)eval_num(n) / 100;
10325 				CLAMP(target_volume, 0.0f, 1.0f);
10326 				n = CDR(n);
10327 			}
10328 
10329 			int time = 0;
10330 			if (n >= 0)
10331 				time = eval_num(n);
10332 
10333 			snd_adjust_audio_volume(option, target_volume, time);
10334 		}
10335 	}
10336 }
10337 
sexp_explosion_option_lookup(char * text)10338 int sexp_explosion_option_lookup(char *text)
10339 {
10340 	int i;
10341 
10342 	Assert(text != NULL);
10343 	if (text == NULL) {
10344 		return -1;
10345 	}
10346 
10347 	for (i = 0; i < Num_explosion_options; i++) {
10348 		if (!strcmp(text, Explosion_option[i])) {
10349 			return i;
10350 		}
10351 	}
10352 
10353 	return -1;
10354 }
10355 
10356 // Goober5000
sexp_set_explosion_option(int node)10357 void sexp_set_explosion_option(int node)
10358 {
10359 	int n = node, ship_num;
10360 	ship *shipp;
10361 	ship_info *sip;
10362 	shockwave_create_info *sci;
10363 
10364 	// get ship
10365 	ship_num = ship_name_lookup(CTEXT(n));
10366 	if (ship_num < 0)
10367 		return;
10368 
10369 	shipp = &Ships[ship_num];
10370 	sip = &Ship_info[shipp->ship_info_index];
10371 	sci = &sip->shockwave;
10372 
10373 	n = CDR(n);
10374 
10375 	// if we haven't changed anything yet, create a new special-exp with the same values as a standard exp
10376 	if (!shipp->use_special_explosion)
10377 	{
10378 		shipp->special_exp_damage = fl2i(sci->damage);
10379 		shipp->special_exp_blast = fl2i(sci->blast);
10380 		shipp->special_exp_inner = fl2i(sci->inner_rad);
10381 		shipp->special_exp_outer = fl2i(sci->outer_rad);
10382 		shipp->special_exp_shockwave_speed = fl2i(sci->speed);
10383 		shipp->special_exp_deathroll_time = 0;
10384 
10385 		shipp->use_special_explosion = true;
10386 		shipp->use_shockwave = (sci->speed > 0);
10387 	}
10388 
10389 	// process all options
10390 	while (n >= 0)
10391 	{
10392 		int option = sexp_explosion_option_lookup(CTEXT(n));
10393 		n = CDR(n);
10394 
10395 		// watch out for bogus options
10396 		if (n < 0)
10397 			break;
10398 
10399 		int val = eval_num(n);
10400 		Assert(val >= 0);	// should be true due to OPF_POSITIVE
10401 		n = CDR(n);
10402 
10403 		if (option == EO_DAMAGE) {
10404 			shipp->special_exp_damage = val;
10405 		} else if (option == EO_BLAST) {
10406 			shipp->special_exp_blast = val;
10407 		} else if (option == EO_INNER_RADIUS) {
10408 			shipp->special_exp_inner = val;
10409 		} else if (option == EO_OUTER_RADIUS) {
10410 			shipp->special_exp_outer = val;
10411 		} else if (option == EO_SHOCKWAVE_SPEED) {
10412 			shipp->special_exp_shockwave_speed = val;
10413 			shipp->use_shockwave = (val > 0);
10414 		} else if (option == EO_DEATH_ROLL_TIME) {
10415 			shipp->special_exp_deathroll_time = val;
10416 
10417 			// hmm, it would be cool to modify the explosion in progress
10418 			if (shipp->flags & SF_DYING && val >= 2) {
10419 				shipp->final_death_time = timestamp(val);
10420 			}
10421 		}
10422 	}
10423 
10424 	// if all our values are the same as a standard exp, turn off the special exp
10425 	if ((shipp->special_exp_damage == sci->damage) && (shipp->special_exp_blast == sci->blast) && (shipp->special_exp_inner == sci->inner_rad)
10426 		&& (shipp->special_exp_outer == sci->outer_rad) && (shipp->special_exp_shockwave_speed == sci->speed) && (shipp->special_exp_deathroll_time == 0))
10427 	{
10428 		shipp->use_special_explosion = false;
10429 		shipp->use_shockwave = false;
10430 
10431 		shipp->special_exp_damage = -1;
10432 		shipp->special_exp_blast = -1;
10433 		shipp->special_exp_inner = -1;
10434 		shipp->special_exp_outer = -1;
10435 		shipp->special_exp_shockwave_speed = -1;
10436 		shipp->special_exp_deathroll_time = 0;
10437 	}
10438 }
10439 
10440 // Goober5000
sexp_explosion_effect(int n)10441 void sexp_explosion_effect(int n)
10442 /* From the SEXP help...
10443 	{ OP_EXPLOSION_EFFECT, "explosion-effect\r\n"
10444 		"\tCauses an explosion at a given origin, with the given parameters.  "
10445 		"Takes 11 or 13 arguments...\r\n"
10446 		"\t1:  Origin X\r\n"
10447 		"\t2:  Origin Y\r\n"
10448 		"\t3:  Origin Z\r\n"
10449 		"\t4:  Damage\r\n"
10450 		"\t5:  Blast force\r\n"
10451 		"\t6:  Size of explosion (if 0, explosion will not be visible)\r\n"
10452 		"\t7:  Inner radius to apply damage (if 0, explosion will not be visible)\r\n"
10453 		"\t8:  Outer radius to apply damage (if 0, explosion will not be visible)\r\n"
10454 		"\t9:  Shockwave speed (if 0, there will be no shockwave)\r\n"
10455 		"\t10: Type (0 = medium, 1 = large1, 2 = large2)\r\n"  (otherwise use the index in fireball.tbl - FUBAR)
10456 		"\t11: Sound (index into sounds.tbl)\r\n"
10457 		"\t12: EMP intensity (optional)\r\n"
10458 		"\t13: EMP duration in seconds (optional)" },
10459 */
10460 // Basically, this function pretends that there's a ship at the origin that's blowing up, and
10461 // it does stuff accordingly.  In some places, it has to tiptoe around a little because the
10462 // code often expects a parent object when in fact there is none. <.<  >.>
10463 {
10464 	vec3d origin;
10465 	int max_damage, max_blast, explosion_size, inner_radius, outer_radius, shockwave_speed, fireball_type, sound_index;
10466 	int emp_intensity, emp_duration;
10467 
10468 	Assert( n >= 0 );
10469 
10470 	// read in data --------------------------------
10471 	origin.xyz.x = (float)eval_num(n);
10472 	n = CDR(n);
10473 	origin.xyz.y = (float)eval_num(n);
10474 	n = CDR(n);
10475 	origin.xyz.z = (float)eval_num(n);
10476 	n = CDR(n);
10477 
10478 	max_damage = eval_num(n);
10479 	n = CDR(n);
10480 	max_blast = eval_num(n);
10481 	n = CDR(n);
10482 
10483 	explosion_size = eval_num(n);
10484 	n = CDR(n);
10485 	inner_radius = eval_num(n);
10486 	n = CDR(n);
10487 	outer_radius = eval_num(n);
10488 	n = CDR(n);
10489 
10490 	shockwave_speed = eval_num(n);
10491 	n = CDR(n);
10492 
10493 	// fireball type
10494 	if (eval_num(n) == 0)
10495 	{
10496 		fireball_type = FIREBALL_EXPLOSION_MEDIUM;
10497 	}
10498 	else if (eval_num(n) == 1)
10499 	{
10500 		fireball_type = FIREBALL_EXPLOSION_LARGE1;
10501 	}
10502 	else if (eval_num(n) == 2)
10503 	{
10504 		fireball_type = FIREBALL_EXPLOSION_LARGE2;
10505 	}
10506 	else if (eval_num(n) >= Num_fireball_types)	{
10507 		Warning(LOCATION, "explosion-effect type is out of range; quitting the explosion...\n");
10508 		return;
10509 	}
10510 	else {
10511 		fireball_type = eval_num(n);
10512 	}
10513 	n = CDR(n);
10514 
10515 	sound_index = eval_num(n);
10516 	n = CDR(n);
10517 
10518 	// optional EMP
10519 	emp_intensity = 0;
10520 	emp_duration = 0;
10521 	if (n != -1)
10522 	{
10523 		emp_intensity = eval_num(n);
10524 		n = CDR(n);
10525 	}
10526 	if (n != -1)
10527 	{
10528 		emp_duration = eval_num(n);
10529 		n = CDR(n);
10530 	}
10531 
10532 
10533 	// play sound effect ---------------------------
10534 	if (sound_index >= 0)
10535 	{
10536 		snd_play_3d( &Snds[sound_index], &origin, &View_position, 0.0f, NULL, 0, 1.0f, SND_PRIORITY_MUST_PLAY  );
10537 	}
10538 
10539 
10540 	// create the fireball -------------------------
10541 	if (explosion_size && inner_radius && outer_radius)
10542 	{
10543 		if(fireball_type == FIREBALL_EXPLOSION_MEDIUM)
10544 			fireball_create( &origin, fireball_type, FIREBALL_MEDIUM_EXPLOSION, -1, (float)explosion_size );
10545 		else
10546 			fireball_create( &origin, fireball_type, FIREBALL_LARGE_EXPLOSION, -1, (float)explosion_size );
10547 	}
10548 
10549 	// apply area affect damage --------------------
10550 	if (max_damage || max_blast)
10551 	{
10552 		if ( shockwave_speed > 0 )
10553 		{
10554 			shockwave_create_info sci;
10555 			shockwave_create_info_init(&sci);
10556 
10557 			sci.inner_rad = (float)inner_radius;
10558 			sci.outer_rad = (float)outer_radius;
10559 			sci.blast = (float)max_blast;
10560 			sci.damage = (float)max_damage;
10561 			sci.speed = (float)shockwave_speed;
10562 			sci.rot_angles.p = frand_range(0.0f, 1.99f*PI);
10563 			sci.rot_angles.b = frand_range(0.0f, 1.99f*PI);
10564 			sci.rot_angles.h = frand_range(0.0f, 1.99f*PI);
10565 			shockwave_create(-1, &origin, &sci, SW_SHIP_DEATH);
10566 		}
10567 		else
10568 		{
10569 			object *objp;
10570 			float t_blast = 0.0f;
10571 			float t_damage = 0.0f;
10572 			for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) )
10573 			{
10574 				if ( (objp->type != OBJ_SHIP) && (objp->type != OBJ_ASTEROID) )
10575 				{
10576 					continue;
10577 				}
10578 
10579 				// don't blast navbuoys
10580 				if ( objp->type == OBJ_SHIP )
10581 				{
10582 					if ( ship_get_SIF(objp->instance) & SIF_NAVBUOY )
10583 					{
10584 						continue;
10585 					}
10586 				}
10587 
10588 				if ( ship_explode_area_calc_damage( &origin, &objp->pos, (float)inner_radius, (float)outer_radius, (float)max_damage, (float)max_blast, &t_damage, &t_blast ) == -1 )
10589 				{
10590 					continue;
10591 				}
10592 
10593 				switch ( objp->type )
10594 				{
10595 					case OBJ_SHIP:
10596 						ship_apply_global_damage( objp, NULL, &origin, t_damage );
10597 						vec3d force, vec_ship_to_impact;
10598 						vm_vec_sub( &vec_ship_to_impact, &objp->pos, &origin );
10599 						if (!IS_VEC_NULL_SQ_SAFE( &vec_ship_to_impact )) {
10600 							vm_vec_copy_normalize( &force, &vec_ship_to_impact );
10601 							vm_vec_scale( &force, (float)max_blast );
10602 							ship_apply_whack( &force, &vec_ship_to_impact, objp );
10603 						}
10604 						break;
10605 
10606 					case OBJ_ASTEROID:
10607 						asteroid_hit(objp, NULL, NULL, t_damage);
10608 						break;
10609 
10610 					default:
10611 						Int3();
10612 						break;
10613 				}
10614 			}	// end for
10615 		}
10616 	}
10617 
10618 
10619 	// apply emp damage if applicable --------------
10620 	if (emp_intensity && emp_duration)
10621 	{
10622 		emp_apply(&origin, (float)inner_radius, (float)outer_radius, (float)emp_intensity, (float)emp_duration);
10623 	}
10624 }
10625 
10626 // Goober5000
sexp_warp_effect(int n)10627 void sexp_warp_effect(int n)
10628 /* From the SEXP help...
10629 	{ OP_WARP_EFFECT, "warp-effect\r\n"
10630 		"\tCauses a subspace warp effect at a given origin, facing toward a given location, with the given parameters.  "
10631 		"Takes 12 arguments...\r\n"
10632 		"\t1:  Origin X\r\n"
10633 		"\t2:  Origin Y\r\n"
10634 		"\t3:  Origin Z\r\n"
10635 		"\t4:  Location X\r\n"
10636 		"\t5:  Location Y\r\n"
10637 		"\t6:  Location Z\r\n"
10638 		"\t7:  Radius\r\n"
10639 		"\t8:  Duration in seconds\r\n"
10640 		"\t9:  Warp opening sound (index into sounds.tbl)\r\n"
10641 		"\t10: Warp closing sound (index into sounds.tbl)\r\n"
10642 		"\t11: Type (0 for standard blue [default], 1 for Knossos green)\r\n"
10643 		"\t12: Shape (0 for 2-D [default], 1 for 3-D)" },
10644 */
10645 {
10646 	vec3d origin, location, v_orient;
10647 	matrix m_orient;
10648 	int radius, duration, warp_open_sound_index, warp_close_sound_index, fireball_type, extra_flags;
10649 	extra_flags = FBF_WARP_VIA_SEXP;
10650 
10651 	// read in data --------------------------------
10652 	origin.xyz.x = (float)eval_num(n);
10653 	n = CDR(n);
10654 	origin.xyz.y = (float)eval_num(n);
10655 	n = CDR(n);
10656 	origin.xyz.z = (float)eval_num(n);
10657 	n = CDR(n);
10658 
10659 	location.xyz.x = (float)eval_num(n);
10660 	n = CDR(n);
10661 	location.xyz.y = (float)eval_num(n);
10662 	n = CDR(n);
10663 	location.xyz.z = (float)eval_num(n);
10664 	n = CDR(n);
10665 
10666 	radius = eval_num(n);
10667 	n = CDR(n);
10668 	duration = eval_num(n);
10669 	if (duration < 4) duration = 4;
10670 	n = CDR(n);
10671 
10672 	warp_open_sound_index = eval_num(n);
10673 	n = CDR(n);
10674 	warp_close_sound_index = eval_num(n);
10675 	n = CDR(n);
10676 
10677 	// fireball type
10678 	if (eval_num(n) == 0)
10679 	{
10680 		fireball_type = FIREBALL_WARP;
10681 	}
10682 	else if (eval_num(n) == 1)
10683 	{
10684 		fireball_type = FIREBALL_KNOSSOS;
10685 	}
10686 	else
10687 	{
10688 		Warning(LOCATION, "warp-effect type is out of range; quitting the warp...\n");
10689 		return;
10690 	}
10691 	n = CDR(n);
10692 
10693 	// shape
10694 	if (eval_num(n) == 0)
10695 	{
10696 		// do nothing; this is standard
10697 	}
10698 	else if (eval_num(n) == 1)
10699 	{
10700 		extra_flags |= FBF_WARP_3D;
10701 	}
10702 	else
10703 	{
10704 		Warning(LOCATION, "warp-effect shape is out of range; quitting the warp...\n");
10705 		return;
10706 	}
10707 
10708 
10709 	// calculate orientation matrix ----------------
10710 
10711 	vm_vec_sub(&v_orient, &location, &origin);
10712 
10713 	if (IS_VEC_NULL_SQ_SAFE(&v_orient))
10714 	{
10715 		Warning(LOCATION, "error in warp-effect: warp can't point to itself; quitting the warp...\n");
10716 		return;
10717 	}
10718 
10719 	vm_vector_2_matrix(&m_orient, &v_orient, NULL, NULL);
10720 
10721 	// create fireball -----------------------------
10722 
10723 	fireball_create(&origin, fireball_type, FIREBALL_WARP_EFFECT, -1, (float)radius, 0, NULL, (float)duration, -1, &m_orient, 0, extra_flags, warp_open_sound_index, warp_close_sound_index);
10724 }
10725 
10726 // this function get called by send-message or send-message random with the name of the message, sender,
10727 // and priority.
sexp_send_one_message(char * name,char * who_from,char * priority,int group,int delay)10728 void sexp_send_one_message( char *name, char *who_from, char *priority, int group, int delay )
10729 {
10730 	int ipriority, num, ship_index, source;
10731 	ship *shipp;
10732 
10733 	if(physics_paused){
10734 		return;
10735 	}
10736 
10737 	Assert( (name != NULL) && (who_from != NULL) && (priority != NULL) );
10738 
10739 	// determine the priority of the message
10740 	if ( !stricmp(priority, "low") )
10741 		ipriority = MESSAGE_PRIORITY_LOW;
10742 	else if ( !stricmp(priority, "normal") )
10743 		ipriority = MESSAGE_PRIORITY_NORMAL;
10744 	else if ( !stricmp(priority, "high") )
10745 		ipriority = MESSAGE_PRIORITY_HIGH;
10746 	else {
10747 		Int3();
10748 		ipriority = MESSAGE_PRIORITY_NORMAL;
10749 	}
10750 
10751 	// check to see if the 'who_from' string is a ship that had been destroyed or departed.  If so,
10752 	// then don't send the message.  We must look at 'who_from' to determine what to look for.  who_from
10753 	// may be any allied person, any wingman, a wingman from a specific wing, or a specific ship
10754 	ship_index = -1;
10755 	shipp = NULL;
10756 	source = MESSAGE_SOURCE_COMMAND;
10757 	if ( who_from[0] == '#' ) {
10758 		message_send_unique_to_player( name, &(who_from[1]), MESSAGE_SOURCE_SPECIAL, ipriority, group, delay );
10759 		return;
10760 	} else if (!stricmp(who_from, "<any allied>")) {
10761 		return;
10762 	} else if ( (num = wing_name_lookup(who_from)) != -1 ) {
10763 		// message from a wing
10764 		// choose wing leader to speak for wing (hence "1" at end of ship_get_random_ship_in_wing)
10765 		ship_index = ship_get_random_ship_in_wing( num, SHIP_GET_UNSILENCED, 1 );
10766 		if ( ship_index == -1 ) {
10767 			if ( ipriority != MESSAGE_PRIORITY_HIGH )
10768 				return;
10769 		}
10770 
10771 	} else if ( mission_log_get_time(LOG_SHIP_DESTROYED, who_from, NULL, NULL) || mission_log_get_time(LOG_SHIP_DEPARTED, who_from, NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, who_from, NULL, NULL)
10772 		|| mission_log_get_time(LOG_WING_DESTROYED, who_from, NULL, NULL) || mission_log_get_time(LOG_WING_DEPARTED, who_from, NULL, NULL) ) {
10773 		// getting into this if statement means that the ship or wing (sender) is no longer in the mission
10774 		// if message is high priority, make it come from Terran Command
10775 		if ( ipriority != MESSAGE_PRIORITY_HIGH )
10776 			return;
10777 
10778 		source = MESSAGE_SOURCE_COMMAND;
10779 
10780 	} else if ( !stricmp(who_from, "<any wingman>") || (wing_name_lookup(who_from) != -1) ) {
10781 		source = MESSAGE_SOURCE_WINGMAN;
10782 	} else if ( !stricmp(who_from, "<none>") ) {
10783 		source = MESSAGE_SOURCE_NONE;
10784 	} else {
10785 		// Message from a apecific ship
10786 		source = MESSAGE_SOURCE_SHIP;
10787 		ship_index = ship_name_lookup(who_from);
10788 		if ( ship_index == -1 ) {
10789 			// bail if not high priority, otherwise reroute to command
10790 			if ( ipriority != MESSAGE_PRIORITY_HIGH )
10791 				return;
10792 			source = MESSAGE_SOURCE_COMMAND;
10793 		}
10794 	}
10795 
10796 	if ( ship_index == -1 ){
10797 		shipp = NULL;
10798 	} else {
10799 		shipp = &Ships[ship_index];
10800 	}
10801 
10802 	message_send_unique_to_player( name, shipp, source, ipriority, group, delay );
10803 }
10804 
sexp_send_message(int n)10805 void sexp_send_message(int n)
10806 {
10807 	char *name, *who_from, *priority, *tmp;
10808 
10809 	if(physics_paused){
10810 		return;
10811 	}
10812 
10813 	Assert ( n != -1 );
10814 	who_from = CTEXT(n);
10815 	priority = CTEXT(CDR(n));
10816 	name = CTEXT(CDR(CDR(n)));
10817 
10818 	// a temporary check to see if the name field matched a priority since I am in the process
10819 	// of reordering the arguments
10820 	if ( !stricmp(name, "low") || !stricmp(name, "normal") || !stricmp(name, "high") ) {
10821 		tmp = name;
10822 		name = priority;
10823 		priority = tmp;
10824 	}
10825 
10826 	sexp_send_one_message( name, who_from, priority, 0, 0 );
10827 }
10828 
sexp_send_message_list(int n)10829 void sexp_send_message_list(int n)
10830 {
10831 	char *name, *who_from, *priority;
10832 	int delay;
10833 
10834 	if(physics_paused){
10835 		return;
10836 	}
10837 
10838 	// send a bunch of messages
10839 	delay = 0;
10840 	while(n != -1){
10841 		who_from = CTEXT(n);
10842 
10843 		// next node
10844 		n = CDR(n);
10845 		if(n == -1){
10846 			Warning(LOCATION, "Detected incomplete parameter list in sexp-send-message-list");
10847 			return;
10848 		}
10849 		priority = CTEXT(n);
10850 
10851 		// next node
10852 		n = CDR(n);
10853 		if(n == -1){
10854 			Warning(LOCATION, "Detected incomplete parameter list in sexp-send-message-list");
10855 			return;
10856 		}
10857 		name = CTEXT(n);
10858 
10859 		// next node
10860 		n = CDR(n);
10861 		if(n == -1){
10862 			Warning(LOCATION, "Detected incomplete parameter list in sexp-send-message-list");
10863 			return;
10864 		}
10865 		delay += eval_num(n);
10866 
10867 		// send the message
10868 		sexp_send_one_message(name, who_from, priority, 1, delay);
10869 
10870 		// next node
10871 		n = CDR(n);
10872 	}
10873 }
10874 
sexp_send_random_message(int n)10875 void sexp_send_random_message(int n)
10876 {
10877 	char *name, *who_from, *priority;
10878 	int temp, num_messages, message_num;
10879 
10880 	Assert ( n != -1 );
10881 	who_from = CTEXT(n);
10882 	priority = CTEXT(CDR(n));
10883 
10884 	if(physics_paused){
10885 		return;
10886 	}
10887 
10888 	// count the number of messages that we have
10889 	n = CDR(CDR(n));
10890 	temp = n;
10891 	num_messages = 0;
10892 	while ( n != -1 ) {
10893 		n = CDR(n);
10894 		num_messages++;
10895 	}
10896 	Assert ( num_messages >= 1 );
10897 
10898 	// get a random message, and pass the parameters to send_one_message
10899 	message_num = myrand() % num_messages;
10900 	n = temp;
10901 	while ( n != -1 ) {
10902 		if ( message_num == 0 )
10903 			break;
10904 		message_num--;
10905 		n = CDR(n);
10906 	}
10907 	Assert (n != -1);		// should have found the message!!!
10908 	name = CTEXT(n);
10909 
10910 	sexp_send_one_message( name, who_from, priority, 0, 0 );
10911 }
10912 
sexp_self_destruct(int node)10913 void sexp_self_destruct(int node)
10914 {
10915 	int n, ship_num;
10916 
10917 	for (n = node; n != -1; n = CDR(n))	{
10918 		// get the ship
10919 		ship_num = ship_name_lookup(CTEXT(n));
10920 
10921 		// if it still exists, destroy it
10922 		if (ship_num >= 0) {
10923 			ship_self_destruct(&Objects[Ships[ship_num].objnum]);
10924 		}
10925 	}
10926 }
10927 
sexp_next_mission(int n)10928 void sexp_next_mission(int n)
10929 {
10930 	char *mission_name;
10931 	int i;
10932 
10933 	mission_name = CTEXT(n);
10934 
10935 	if (mission_name == NULL) {
10936 		Error( LOCATION, "Mission name is NULL in campaign file for next-mission command!");
10937 	}
10938 
10939 	for (i = 0; i < Campaign.num_missions; i++) {
10940 		if ( !stricmp(Campaign.missions[i].name, mission_name) ) {
10941 			Campaign.next_mission = i;
10942 			return;
10943 		}
10944 	}
10945 	Error(LOCATION, "Mission name %s not found in campaign file for next-mission command", mission_name);
10946 }
10947 
10948 /**
10949  * Deal with the end-of-campaign sexpression.
10950  */
sexp_end_of_campaign(int n)10951 void sexp_end_of_campaign(int n)
10952 {
10953 	// this is really a do-nothing sexpression.  It is pretty much a placeholder to allow
10954 	// campaigns to have repeat-mission branches at the end of the campaign.  By not setting
10955 	// anything in this function, the higher level campaign code will see this as end-of-campaign
10956 	// since next_mission isn't set to anything.  (To be safe, we'll set to -1).
10957 	Campaign.next_mission = -1;
10958 }
10959 
10960 // sexpression to end everything.  One parameter is the movie to play when this is over.
10961 // Goober5000 - edited to only to the FS2-specific code when actually ending the FS2 main
10962 // campaign, and otherwise to do the conventional code
sexp_end_campaign(int n)10963 void sexp_end_campaign(int n)
10964 {
10965 	if (!(Game_mode & GM_CAMPAIGN_MODE)) {
10966 		return;
10967 	}
10968 
10969 	// in FS2 our ending is a bit wacky. we'll just flag the mission as having ended the campaign
10970 	//
10971 	// changed this to check for an active supernova rather than a special campaign since the supernova
10972 	// code needs special time to execute and will post GS_EVENT_END_CAMPAIGN with Game_mode check
10973 	// or show death-popup when it's done - taylor
10974 	if (supernova_active() /*&& !stricmp(Campaign.filename, "freespace2")*/) {
10975 		Campaign_ending_via_supernova = 1;
10976 	} else {
10977 		// post and event to move us to the end-of-campaign state
10978 		gameseq_post_event(GS_EVENT_END_CAMPAIGN);
10979 	}
10980 }
10981 
10982 /**
10983  * Reduces the strength of a subsystem by the given percentage.
10984  *
10985  * If it is reduced to below 0%, then the hits of the subsystem are set to 0
10986  */
sexp_sabotage_subsystem(int n)10987 void sexp_sabotage_subsystem(int n)
10988 {
10989 	char *shipname, *subsystem;
10990 	int	percentage, shipnum, index, generic_type;
10991 	float sabotage_hits;
10992 	ship	*shipp;
10993 	ship_subsys *ss = NULL, *ss_start;
10994 	bool do_loop = true;
10995 
10996 	shipname = CTEXT(n);
10997 	subsystem = CTEXT(CDR(n));
10998 	percentage = eval_num(CDR(CDR(n)));
10999 
11000 	shipnum = ship_name_lookup(shipname);
11001 
11002 	// if no ship, then return immediately.
11003 	if ( shipnum == -1 )
11004 		return;
11005 	shipp = &Ships[shipnum];
11006 
11007 	// see if we are dealing with the HULL
11008 	if ( !stricmp( subsystem, SEXP_HULL_STRING) ) {
11009 		float ihs;
11010 		object *objp;
11011 
11012 		ihs = shipp->ship_max_hull_strength;
11013 		sabotage_hits = ihs * ((float)percentage / 100.0f);
11014 		objp = &Objects[shipp->objnum];
11015 		objp->hull_strength -= sabotage_hits;
11016 
11017 		// self destruct the ship if <= 0.
11018 		if ( objp->hull_strength <= 0.0f )
11019 			ship_self_destruct( objp );
11020 		return;
11021 	}
11022 
11023 	// see if we are dealing with the Simulated HULL
11024 	if ( !stricmp( subsystem, SEXP_SIM_HULL_STRING) ) {
11025 		float ihs;
11026 		object *objp;
11027 
11028 		ihs = shipp->ship_max_hull_strength;
11029 		sabotage_hits = ihs * ((float)percentage / 100.0f);
11030 		objp = &Objects[shipp->objnum];
11031 		objp->sim_hull_strength -= sabotage_hits;
11032 
11033 		return;
11034 	}
11035 
11036 	// now find the given subsystem on the ship.  This could be a generic type like <All Engines>
11037 	generic_type = get_generic_subsys(subsystem);
11038 	ss_start = GET_FIRST(&shipp->subsys_list);
11039 
11040 	while (do_loop) {
11041 		if (generic_type) {
11042 			// loop until we find a subsystem of that type
11043 			for ( ; ss_start != END_OF_LIST(&Ships[shipnum].subsys_list); ss_start = GET_NEXT(ss_start)) {
11044 				ss = NULL;
11045 				if (generic_type == ss_start->system_info->type) {
11046 					ss = ss_start;
11047 					ss_start = GET_NEXT(ss_start);
11048 					break;
11049 				}
11050 			}
11051 
11052 			// reached the end of the subsystem list
11053 			if (ss_start == END_OF_LIST(&Ships[shipnum].subsys_list)) {
11054 				// If the last subsystem wasn't of interest we don't need to go any further
11055 				if (ss == NULL) {
11056 					return;
11057 				}
11058 				do_loop = false;
11059 			}
11060 		}
11061 		else {
11062 			do_loop = false;
11063 			index = ship_get_subsys_index(shipp, subsystem, 1);	// Bypass any error since we supply one here
11064 			if ( index == -1 ) {
11065 				nprintf(("Warning", "Couldn't find subsystem %s on ship %s for sabotage subsystem\n", subsystem, shipp->ship_name));
11066 				return;
11067 			}
11068 			// get the pointer to the subsystem.  Check it's current hits against it's max hits, and
11069 			// set the strength to the given percentage if current strength is > given percentage
11070 			ss = ship_get_indexed_subsys( shipp, index );
11071 			if (ss == NULL) {
11072 				nprintf(("Warning", "Nonexistent subsystem for index %d on ship %s for sabotage subsystem\n", index, shipp->ship_name));
11073 				return;
11074 			}
11075 		}
11076 
11077 		sabotage_hits = ss->max_hits * ((float)percentage / 100.0f);
11078 		ss->current_hits -= sabotage_hits;
11079 		if ( ss->current_hits < 0.0f )
11080 			ss->current_hits = 0.0f;
11081 
11082 		// maybe blow up subsys
11083 		if (ss->current_hits <= 0) {
11084 			do_subobj_destroyed_stuff(shipp, ss, NULL);
11085 		}
11086 
11087 		ship_recalc_subsys_strength( shipp );
11088 	}
11089 }
11090 
11091 /**
11092  * Adds some percentage of hits to a subsystem.
11093  *
11094  * Anything repaired about 100% is set to max hits
11095  */
sexp_repair_subsystem(int n)11096 void sexp_repair_subsystem(int n)
11097 {
11098 	char *shipname, *subsystem;
11099 	int	percentage, shipnum, index, do_submodel_repair, generic_type;
11100 	float repair_hits;
11101 	ship *shipp;
11102 	ship_subsys *ss = NULL, *ss_start;
11103 	bool do_loop = true;
11104 
11105 	shipname = CTEXT(n);
11106 	subsystem = CTEXT(CDR(n));
11107 	shipnum = ship_name_lookup(shipname);
11108 
11109 	do_submodel_repair = is_sexp_true(CDDDR(n));
11110 
11111 	// if no ship, then return immediately.
11112 	if ( shipnum == -1 ) {
11113 		return;
11114 	}
11115 	shipp = &Ships[shipnum];
11116 
11117 	// get percentage
11118 	percentage = eval_num(CDR(CDR(n)));
11119 
11120 	// see if we are dealing with the HULL
11121 	if ( !stricmp( subsystem, SEXP_HULL_STRING) ) {
11122 		float ihs;
11123 		object *objp;
11124 
11125 		ihs = shipp->ship_max_hull_strength;
11126 		repair_hits = ihs * ((float)percentage / 100.0f);
11127 		objp = &Objects[shipp->objnum];
11128 		objp->hull_strength += repair_hits;
11129 		if ( objp->hull_strength > ihs )
11130 			objp->hull_strength = ihs;
11131 		return;
11132 	}
11133 
11134 	// see if we are dealing with the Simulated HULL
11135 	if ( !stricmp( subsystem, SEXP_SIM_HULL_STRING) ) {
11136 		float ihs;
11137 		object *objp;
11138 
11139 		ihs = shipp->ship_max_hull_strength;
11140 		repair_hits = ihs * ((float)percentage / 100.0f);
11141 		objp = &Objects[shipp->objnum];
11142 		objp->hull_strength += repair_hits;
11143 		if ( objp->sim_hull_strength > ihs )
11144 			objp->sim_hull_strength = ihs;
11145 		return;
11146 	}
11147 
11148 	// now find the given subsystem on the ship.This could be a generic type like <All Engines>
11149 	generic_type = get_generic_subsys(subsystem);
11150 	ss_start = GET_FIRST(&shipp->subsys_list);
11151 
11152 	while (do_loop) {
11153 		if (generic_type) {
11154 			// loop until we find a subsystem of that type
11155 			for ( ; ss_start != END_OF_LIST(&Ships[shipnum].subsys_list); ss_start = GET_NEXT(ss_start)) {
11156 				ss = NULL;
11157 				if (generic_type == ss_start->system_info->type) {
11158 					ss = ss_start;
11159 					ss_start = GET_NEXT(ss_start);
11160 					break;
11161 				}
11162 			}
11163 
11164 			// reached the end of the subsystem list
11165 			if (ss_start == END_OF_LIST(&Ships[shipnum].subsys_list)) {
11166 				// If the last subsystem wasn't of interest we don't need to go any further
11167 				if (ss == NULL) {
11168 					return;
11169 				}
11170 				do_loop = false;
11171 			}
11172 		}
11173 		else {
11174 			do_loop = false;
11175 			index = ship_get_subsys_index(shipp, subsystem, 1);	// Bypass any error since we supply one here
11176 			if ( index == -1 ) {
11177 				nprintf(("Warning", "Couldn't find subsystem %s on ship %s for repair subsystem\n", subsystem, shipp->ship_name));
11178 				return;
11179 			}
11180 			// get the pointer to the subsystem.  Check it's current hits against it's max hits, and
11181 			// set the strength to the given percentage if current strength is < given percentage
11182 			ss = ship_get_indexed_subsys( shipp, index );
11183 			if (ss == NULL) {
11184 				nprintf(("Warning", "Nonexistent subsystem for index %d on ship %s for repair subsystem\n", index, shipp->ship_name));
11185 				return;
11186 			}
11187 		}
11188 
11189 		repair_hits = ss->max_hits * ((float)percentage / 100.0f);
11190 		ss->current_hits += repair_hits;
11191 		if ( ss->current_hits > ss->max_hits )
11192 			ss->current_hits = ss->max_hits;
11193 
11194 		if ((ss->current_hits > 0) && (do_submodel_repair))
11195 		{
11196 			ss->submodel_info_1.blown_off = 0;
11197 			ss->submodel_info_2.blown_off = 0;
11198 		}
11199 
11200 		ship_recalc_subsys_strength( shipp );
11201 	}
11202 }
11203 
11204 /**
11205  * Set a subsystem of a ship at a specific percentage
11206  */
sexp_set_subsystem_strength(int n)11207 void sexp_set_subsystem_strength(int n)
11208 {
11209 	char *shipname, *subsystem;
11210 	int	percentage, shipnum, index, do_submodel_repair, generic_type;
11211 	ship *shipp;
11212 	ship_subsys *ss = NULL, *ss_start;
11213 	bool do_loop = true;
11214 
11215 	shipname = CTEXT(n);
11216 	subsystem = CTEXT(CDR(n));
11217 	percentage = eval_num(CDR(CDR(n)));
11218 
11219 	do_submodel_repair = is_sexp_true(CDDDR(n));
11220 
11221 	shipnum = ship_name_lookup(shipname);
11222 
11223 	// if no ship, then return immediately.
11224 	if ( shipnum == -1 )
11225 		return;
11226 	shipp = &Ships[shipnum];
11227 
11228 	if ( percentage > 100 ) {
11229 		mprintf(("Percentage for set_subsystem_strength > 100 on ship %s for subsystem '%s'-- setting to 100\n", shipname, subsystem));
11230 		percentage = 100;
11231 	} else if ( percentage < 0 ) {
11232 		mprintf(("Percantage for set_subsystem_strength < 0 on ship %s for subsystem '%s' -- setting to 0\n", shipname, subsystem));
11233 		percentage = 0;
11234 	}
11235 
11236 	// see if we are dealing with the HULL
11237 	if ( !stricmp( subsystem, SEXP_HULL_STRING) ) {
11238 		float ihs;
11239 		object *objp;
11240 
11241 		objp = &Objects[shipp->objnum];
11242 
11243 		// destroy the ship if percentage is 0
11244 		if ( percentage == 0 ) {
11245 			ship_self_destruct( objp );
11246 		} else {
11247 			ihs = shipp->ship_max_hull_strength;
11248 			objp->hull_strength = ihs * ((float)percentage / 100.0f);
11249 		}
11250 
11251 		return;
11252 	}
11253 
11254 	// see if we are dealing with the Simulated HULL
11255 	if ( !stricmp( subsystem, SEXP_SIM_HULL_STRING) ) {
11256 		float ihs;
11257 		object *objp;
11258 
11259 		objp = &Objects[shipp->objnum];
11260 		ihs = shipp->ship_max_hull_strength;
11261 		objp->sim_hull_strength = ihs * ((float)percentage / 100.0f);
11262 
11263 		return;
11264 	}
11265 
11266 	// now find the given subsystem on the ship.This could be a generic type like <All Engines>
11267 	generic_type = get_generic_subsys(subsystem);
11268 	ss_start = GET_FIRST(&shipp->subsys_list);
11269 
11270 	while (do_loop) {
11271 		if (generic_type) {
11272 			// loop until we find a subsystem of that type
11273 			for ( ; ss_start != END_OF_LIST(&Ships[shipnum].subsys_list); ss_start = GET_NEXT(ss_start)) {
11274 				ss = NULL;
11275 				if (generic_type == ss_start->system_info->type) {
11276 					ss = ss_start;
11277 					ss_start = GET_NEXT(ss_start);
11278 					break;
11279 				}
11280 			}
11281 
11282 			// reached the end of the subsystem list
11283 			if (ss_start == END_OF_LIST(&Ships[shipnum].subsys_list)) {
11284 				// If the last subsystem wasn't of interest we don't need to go any further
11285 				if (ss == NULL) {
11286 					return;
11287 				}
11288 				do_loop = false;
11289 			}
11290 		}
11291 		else {
11292 			do_loop = false;
11293 			index = ship_get_subsys_index(shipp, subsystem, 1);	// Bypass any error since we supply one here
11294 			if ( index == -1 ) {
11295 				nprintf(("Warning", "Couldn't find subsystem %s on ship %s for set subsystem strength\n", subsystem, shipp->ship_name));
11296 				return;
11297 			}
11298 
11299 			// get the pointer to the subsystem.  Check it's current hits against it's max hits, and
11300 			// set the strength to the given percentage
11301 			ss = ship_get_indexed_subsys( shipp, index );
11302 			if (ss == NULL) {
11303 				nprintf(("Warning", "Nonexistent subsystem for index %d on ship %s for set subsystem strength\n", index, shipp->ship_name));
11304 				return;
11305 			}
11306 		}
11307 
11308 		// maybe blow up subsys
11309 		if (ss->current_hits > 0) {
11310 			if (percentage < 1) {
11311 				do_subobj_destroyed_stuff(shipp, ss, NULL);
11312 			}
11313 		}
11314 
11315 		// set hit points
11316 		ss->current_hits = ss->max_hits * ((float)percentage / 100.0f);
11317 
11318 		if ((ss->current_hits > 0) && (do_submodel_repair))
11319 		{
11320 			ss->submodel_info_1.blown_off = 0;
11321 			ss->submodel_info_2.blown_off = 0;
11322 		}
11323 
11324 		ship_recalc_subsys_strength( shipp );
11325 	}
11326 }
11327 
11328 // destroys a subsystem without explosions
sexp_destroy_subsys_instantly(int n)11329 void sexp_destroy_subsys_instantly (int n)
11330 {
11331 	char *subsystem;
11332 	int	shipnum, index, generic_type;
11333 	ship *shipp;
11334 	ship_subsys *ss = NULL;
11335 
11336 	// if MULTIPLAYER bail
11337 	if (Game_mode & GM_MULTIPLAYER) {
11338 		return;
11339 	}
11340 
11341 	shipnum = ship_name_lookup(CTEXT(n));
11342 	// if no ship, then return immediately.
11343 	if ( shipnum == -1 ){
11344 		return;
11345 	}
11346 
11347 	shipp = &Ships[shipnum];
11348 	n = CDR(n);
11349 
11350 	//Process subsystems
11351 	while(n != -1)
11352 	{
11353 		subsystem = CTEXT(n);
11354 		if ( !stricmp( subsystem, SEXP_HULL_STRING) || !stricmp( subsystem, SEXP_SIM_HULL_STRING)){
11355 			n = CDR(n);
11356 			continue;
11357 		}
11358 		// deal with generic subsystems
11359 		generic_type = get_generic_subsys(subsystem);
11360 		if (generic_type) {
11361 			for (ss = GET_FIRST(&shipp->subsys_list); ss != END_OF_LIST(&shipp->subsys_list); ss = GET_NEXT(ss)) {
11362 				if (generic_type == ss->system_info->type) {
11363 					// do destruction stuff
11364 					ss->current_hits = 0;
11365 					ship_recalc_subsys_strength(shipp);
11366 					do_subobj_destroyed_stuff(shipp, ss, NULL, true);
11367 				}
11368 			}
11369 		}
11370 		else
11371 		{
11372 			// normal subsystems
11373 			ss = ship_get_subsys(shipp, subsystem);
11374 			index = ship_get_subsys_index(shipp, subsystem, 1);
11375 			if ( index == -1 ) {
11376 				nprintf(("Warning", "Couldn't find subsystem %s on ship %s for destroy-subsys-instantly\n", subsystem, shipp->ship_name));
11377 				n = CDR(n);
11378 				continue;
11379 			}
11380 			if(ss == NULL) {
11381 				nprintf(("Warning", "Nonexistent subsystem for index %d on ship %s for destroy-subsys-instantly\n", index, shipp->ship_name));
11382 				n = CDR(n);
11383 				continue;
11384 			}
11385 			// do destruction stuff
11386 			ss->current_hits = 0;
11387 			ship_recalc_subsys_strength(shipp);
11388 			do_subobj_destroyed_stuff(shipp, ss, NULL, true);
11389 		}
11390 		// next
11391 		n = CDR(n);
11392 	}
11393 }
11394 
11395 /**
11396  * Changes the validity of a goal.
11397  *
11398  * The flag paramater tells us whether to mark the goals as valid or invalid
11399  */
sexp_change_goal_validity(int n,int flag)11400 void sexp_change_goal_validity( int n, int flag )
11401 {
11402 	char *name;
11403 
11404 	while ( n != -1 ) {
11405 		name = CTEXT(n);
11406 		if ( flag )
11407 			mission_goal_mark_valid( name );
11408 		else
11409 			mission_goal_mark_invalid( name );
11410 
11411 		n = CDR(n);
11412 	}
11413 }
11414 
11415 // Goober5000
11416 // yeesh - be careful of the cargo-no-deplete flag :p
sexp_is_cargo(int n)11417 int sexp_is_cargo(int n)
11418 {
11419 	char *cargo, *ship_name, *subsystem;
11420 	int ship_num, cargo_index;
11421 
11422 	cargo = CTEXT(n);
11423 	ship_name = CTEXT(CDR(n));
11424 	if (CDR(CDR(n)) != -1)
11425 		subsystem = CTEXT(CDR(CDR(n)));
11426 	else
11427 		subsystem = NULL;
11428 
11429 	cargo_index = -1;
11430 
11431 	// find ship
11432 	ship_num = ship_name_lookup(ship_name);
11433 
11434 	// in-mission?
11435 	if (ship_num != -1)
11436 	{
11437 		if (subsystem)
11438 		{
11439 			// find the ship subsystem
11440 			ship_subsys *ss = ship_get_subsys(&Ships[ship_num], subsystem);
11441 			if (ss != NULL)
11442 			{
11443 				// set cargo
11444 				cargo_index = ss->subsys_cargo_name;
11445 			}
11446 		}
11447 		else
11448 		{
11449 			cargo_index = Ships[ship_num].cargo1;
11450 		}
11451 	}
11452 	else
11453 	{
11454 		// can't check subsys of ships not in mission
11455 		if (subsystem)
11456 		{
11457 			return SEXP_CANT_EVAL;
11458 		}
11459 
11460 		// departed?
11461 		int exited_index = ship_find_exited_ship_by_name(ship_name);
11462 		if (exited_index != -1)
11463 		{
11464 			cargo_index = Ships_exited[exited_index].cargo1;
11465 		}
11466 		// not arrived yet
11467 		else
11468 		{
11469 			p_object *p_objp;
11470 
11471 			// find cargo for the parse object
11472 			p_objp = mission_parse_get_arrival_ship(ship_name);
11473 			Assert (p_objp);
11474 			cargo_index = (int) p_objp->cargo1;
11475 		}
11476 	}
11477 
11478 	// did we get any cargo
11479 	if (cargo_index < 0)
11480 		return SEXP_FALSE;
11481 
11482 	// check cargo
11483 	if (!stricmp(Cargo_names[cargo_index & CARGO_INDEX_MASK], cargo))
11484 		return SEXP_TRUE;
11485 	else
11486 		return SEXP_FALSE;
11487 }
11488 
11489 // Goober5000
11490 // yeesh - be careful of the cargo-no-deplete flag :p
sexp_set_cargo(int n)11491 void sexp_set_cargo(int n)
11492 {
11493 	char *cargo, *ship_name, *subsystem;
11494 	int ship_num, i, cargo_index;
11495 
11496 	cargo = CTEXT(n);
11497 	ship_name = CTEXT(CDR(n));
11498 	if (CDR(CDR(n)) != -1)
11499 		subsystem = CTEXT(CDR(CDR(n)));
11500 	else
11501 		subsystem = NULL;
11502 
11503 	// check to see if ship destroyed or departed.  In either case, do nothing.
11504 	if ( mission_log_get_time(LOG_SHIP_DEPARTED, ship_name, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, ship_name, NULL, NULL) )
11505 		return;
11506 
11507 	cargo_index = -1;
11508 	// find this cargo in the cargo list
11509 	for (i = 0; i < Num_cargo; i++)
11510 	{
11511 		// found it?
11512 		if (!stricmp(cargo, Cargo_names[i]))
11513 		{
11514 			cargo_index = i;
11515 			break;
11516 		}
11517 	}
11518 
11519 	// not found
11520 	if (cargo_index == -1)
11521 	{
11522 		// make new entry if possible
11523 		if (Num_cargo + 1 >= MAX_CARGO)
11524 		{
11525 			Warning(LOCATION, "set-cargo: Maximum number of cargo names (%d) reached.  Ignoring new name.\n", MAX_CARGO);
11526 			return;
11527 		}
11528 
11529 		Assert(strlen(cargo) <= NAME_LENGTH - 1);
11530 
11531 		cargo_index = Num_cargo;
11532 		Num_cargo++;
11533 
11534 		strcpy(Cargo_names[cargo_index], cargo);
11535 	}
11536 
11537 	// get the ship
11538 	ship_num = ship_name_lookup(ship_name);
11539 
11540 	// we can only set subsystems if the ship is in the mission
11541 	if (ship_num != -1)
11542 	{
11543 		if (subsystem)
11544 		{
11545 			ship_subsys *ss = ship_get_subsys(&Ships[ship_num], subsystem);
11546 			if (ss == NULL) {
11547 				Assert (!ship_class_unchanged(ship_num));
11548 				return;
11549 			}
11550 
11551 			// set cargo
11552 			ss->subsys_cargo_name = cargo_index | (ss->subsys_cargo_name & CARGO_NO_DEPLETE);
11553 		}
11554 		else
11555 		{
11556 			// simply set the ship cargo
11557 			Ships[ship_num].cargo1 = char(cargo_index | (Ships[ship_num].cargo1 & CARGO_NO_DEPLETE));
11558 		}
11559 	}
11560 	else
11561 	{
11562 		if (!subsystem)
11563 		{
11564 			p_object *p_objp;
11565 
11566 			// set cargo for the parse object
11567 			p_objp = mission_parse_get_arrival_ship(ship_name);
11568 			Assert (p_objp);
11569 			p_objp->cargo1 = char(cargo_index | (p_objp->cargo1 & CARGO_NO_DEPLETE));
11570 		}
11571 	}
11572 }
11573 
11574 /**
11575  * Transfer cargo from one ship to another
11576  */
sexp_transfer_cargo(int n)11577 void sexp_transfer_cargo(int n)
11578 {
11579 	char *shipname1, *shipname2;
11580 	int shipnum1, shipnum2, i;
11581 
11582 	shipname1 = CTEXT(n);
11583 	shipname2 = CTEXT(CDR(n));
11584 
11585 	// find the ships -- if neither in the mission, the abort
11586 	shipnum1 = ship_name_lookup(shipname1);
11587 	shipnum2 = ship_name_lookup(shipname2);
11588 	if ( (shipnum1 == -1) || (shipnum2 == -1) )
11589 		return;
11590 
11591 	// we must be sure that these two objects are indeed docked
11592 	if (!dock_check_find_direct_docked_object(&Objects[Ships[shipnum1].objnum], &Objects[Ships[shipnum2].objnum]))
11593 	{
11594 		return;
11595 	}
11596 
11597 	if ( !stricmp(Cargo_names[Ships[shipnum1].cargo1 & CARGO_INDEX_MASK], "nothing") ) {
11598 		return;
11599 	}
11600 
11601 	// transfer cargo from ship1 to ship2
11602 #ifndef NDEBUG
11603 	// Don't give warning for large ships (cruiser on up)
11604 	if (! (Ship_info[Ships[shipnum2].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) ) {
11605 		if ( stricmp(Cargo_names[Ships[shipnum2].cargo1 & CARGO_INDEX_MASK], "nothing") ) {
11606 			Warning(LOCATION, "Transferring cargo to %s which already\nhas cargo %s.\nCargo will be replaced", Ships[shipnum2].ship_name, Cargo_names[Ships[shipnum2].cargo1 & CARGO_INDEX_MASK] );
11607 		}
11608 	}
11609 #endif
11610 	Ships[shipnum2].cargo1 = char((Ships[shipnum1].cargo1 & CARGO_INDEX_MASK) | (Ships[shipnum2].cargo1 & CARGO_NO_DEPLETE));
11611 
11612 	if ( !(Ships[shipnum1].cargo1 & CARGO_NO_DEPLETE) ) {
11613 		// need to set ship1's cargo to nothing.  scan the cargo_names array looking for the string nothing.
11614 		// add it if not found
11615 		for (i = 0; i < Num_cargo; i++ ) {
11616 			if ( !stricmp(Cargo_names[i], "nothing") ) {
11617 				Ships[shipnum1].cargo1 = char(i);
11618 				return;
11619 			}
11620 		}
11621 		strcpy(Cargo_names[i], "Nothing");
11622 		Num_cargo++;
11623 	}
11624 }
11625 
11626 /**
11627  * Exchanges cargo between two ships
11628  */
sexp_exchange_cargo(int n)11629 void sexp_exchange_cargo(int n)
11630 {
11631 	char *shipname1, *shipname2;
11632 	int shipnum1, shipnum2, temp;
11633 
11634 	shipname1 = CTEXT(n);
11635 	shipname2 = CTEXT(CDR(n));
11636 
11637 	// find the ships -- if neither in the mission, abort
11638 	shipnum1 = ship_name_lookup(shipname1);
11639 	shipnum2 = ship_name_lookup(shipname2);
11640 	if ( (shipnum1 == -1) || (shipnum2 == -1) )
11641 		return;
11642 
11643 	// we must be sure that these two objects are indeed docked
11644 	if (!dock_check_find_direct_docked_object(&Objects[Ships[shipnum1].objnum], &Objects[Ships[shipnum2].objnum]))
11645 	{
11646 		Int3();			// you are trying to transfer cargo between two ships not docked
11647 		return;
11648 	}
11649 
11650 	temp = (Ships[shipnum1].cargo1 & CARGO_INDEX_MASK);
11651 	Ships[shipnum1].cargo1 = char(Ships[shipnum2].cargo1 & CARGO_INDEX_MASK);
11652 	Ships[shipnum2].cargo1 = char(temp);
11653 }
11654 
sexp_cap_waypoint_speed(int n)11655 void sexp_cap_waypoint_speed(int n)
11656 {
11657 	char *shipname;
11658 	int shipnum;
11659 	int speed;
11660 
11661 	shipname = CTEXT(n);
11662 	speed = eval_num(CDR(n));
11663 
11664 	shipnum = ship_name_lookup(shipname);
11665 
11666 	if (shipnum == -1) {
11667 		// trying to set waypoint speed of ship not already in game
11668 		return;
11669 	}
11670 
11671 	// cap speed to range (-1, 32767) to store within int
11672 	if (speed < 0) {
11673 		speed = -1;
11674 	}
11675 
11676 	if (speed > 32767) {
11677 		speed = 32767;
11678 	}
11679 
11680 	Ai_info[Ships[shipnum].ai_index].waypoint_speed_cap = speed;
11681 }
11682 
11683 /**
11684  * Causes a ship to jettison its cargo
11685  * note that the 2nd arg (jettison delay) is not implemented
11686  */
sexp_jettison_cargo(int n)11687 void sexp_jettison_cargo(int n)
11688 {
11689 	char *shipname;
11690 	int ship_index;
11691 
11692 	// get some data
11693 	shipname = CTEXT(n);
11694 
11695 	// lookup the ship
11696 	ship_index = ship_name_lookup(shipname);
11697 	if(ship_index < 0)
11698 		return;
11699 	object *parent_objp = &Objects[Ships[ship_index].objnum];
11700 
11701 	// note: skipping over the unimplemented "jettison delay"
11702 	n = CDDR(n);
11703 
11704 	// no arguments - jettison all docked objects
11705 	if (n == -1)
11706 	{
11707 		// Goober5000 - as with ai_deathroll_start, we can't simply iterate through the dock list while we're
11708 		// undocking things.  So just repeatedly jettison the first object.
11709 		while (object_is_docked(parent_objp))
11710 		{
11711 			object_jettison_cargo(parent_objp, dock_get_first_docked_object(parent_objp));
11712 		}
11713 	}
11714 	// arguments - jettison only those objects
11715 	else
11716 	{
11717 		for (; n != -1; n = CDR(n))
11718 		{
11719 			// make sure ship exists
11720 			ship_index = ship_name_lookup(CTEXT(n));
11721 			if (ship_index < 0)
11722 				continue;
11723 
11724 			// make sure we are docked to it
11725 			if (!dock_check_find_direct_docked_object(parent_objp, &Objects[Ships[ship_index].objnum]))
11726 				continue;
11727 
11728 			object_jettison_cargo(parent_objp, &Objects[Ships[ship_index].objnum]);
11729 		}
11730 	}
11731 }
11732 
sexp_set_docked(int n)11733 void sexp_set_docked(int n)
11734 {
11735 	// get some data
11736 	char* docker_ship_name = CTEXT(n);
11737 	n = CDR(n);
11738 	char* docker_point_name = CTEXT(n);
11739 	n = CDR(n);
11740 	char* dockee_ship_name = CTEXT(n);
11741 	n = CDR(n);
11742 	char* dockee_point_name = CTEXT(n);
11743 	n = CDR(n);
11744 
11745 	// lookup the ships
11746 	int docker_ship_index = ship_name_lookup(docker_ship_name);
11747 	int dockee_ship_index = ship_name_lookup(dockee_ship_name);
11748 	if(docker_ship_index < 0 || dockee_ship_index < 0)
11749 		return;
11750 
11751 	ship* docker_ship = &Ships[docker_ship_index];
11752 	ship* dockee_ship = &Ships[dockee_ship_index];
11753 	object* docker_objp = &Objects[docker_ship->objnum];
11754 	object* dockee_objp = &Objects[dockee_ship->objnum];
11755 
11756 	//Get dockpoints by name
11757 	int docker_point_index = model_find_dock_name_index(Ship_info[docker_ship->ship_info_index].model_num, docker_point_name);
11758 	int dockee_point_index = model_find_dock_name_index(Ship_info[dockee_ship->ship_info_index].model_num, dockee_point_name);
11759 
11760 	Assertion(docker_point_index >= 0, "Docker point '%s' not found on docker ship '%s'", docker_point_name, docker_ship_name);
11761 	Assertion(dockee_point_index >= 0, "Dockee point '%s' not found on dockee ship '%s'", dockee_point_name, dockee_ship_name);
11762 
11763 	//Make sure that the specified dockpoints are all free (if not, do nothing)
11764 	if (dock_find_object_at_dockpoint(docker_objp, docker_point_index) != NULL ||
11765 		dock_find_object_at_dockpoint(dockee_objp, dockee_point_index) != NULL)
11766 	{
11767 		return;
11768 	}
11769 
11770 	//Set docked
11771 	dock_orient_and_approach(docker_objp, docker_point_index, dockee_objp, dockee_point_index, DOA_DOCK_STAY);
11772 	ai_do_objects_docked_stuff(docker_objp, docker_point_index, dockee_objp, dockee_point_index, true);
11773 }
11774 
sexp_cargo_no_deplete(int n)11775 void sexp_cargo_no_deplete(int n)
11776 {
11777 	char *shipname;
11778 	int ship_index, no_deplete = 1;
11779 
11780 	// get some data
11781 	shipname = CTEXT(n);
11782 
11783 	// lookup the ship
11784 	ship_index = ship_name_lookup(shipname);
11785 	if(ship_index < 0){
11786 		return;
11787 	}
11788 
11789 	if ( !(Ship_info[Ships[ship_index].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) ) {
11790 		Warning(LOCATION, "Trying to make non BIG or HUGE ship %s with non-depletable cargo.\n", Ships[ship_index].ship_name);
11791 		return;
11792 	}
11793 
11794 	if (CDR(n) != -1) {
11795 		no_deplete = eval_num(CDR(n));
11796 		Assert((no_deplete == 0) || (no_deplete == 1));
11797 		if ( (no_deplete != 0) && (no_deplete != 1) ) {
11798 			no_deplete = 1;
11799 		}
11800 	}
11801 
11802 	if (no_deplete) {
11803 		Ships[ship_index].cargo1 |= CARGO_NO_DEPLETE;
11804 	} else {
11805 		Ships[ship_index].cargo1 &= (~CARGO_NO_DEPLETE);
11806 	}
11807 
11808 }
11809 
11810 // Goober5000
sexp_force_jump()11811 void sexp_force_jump()
11812 {
11813 	// Shouldn't be gliding now....
11814 	Player_obj->phys_info.flags &= ~PF_GLIDING;
11815 	Player_obj->phys_info.flags &= ~PF_FORCE_GLIDE;
11816 
11817 	if (Game_mode & GM_MULTIPLAYER) {
11818 		multi_handle_end_mission_request();
11819 	}
11820 	else {
11821 		// forced warp, taken from training failure code
11822 		gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_START_FORCED );	//	Force player to warp out.
11823 	}
11824 
11825 }
11826 
sexp_mission_set_nebula(int n)11827 void sexp_mission_set_nebula(int n)
11828 {
11829 	stars_set_nebula(eval_num(n) > 0);
11830 }
11831 
11832 /* freespace.cpp does not have these availiable externally, and we must call
11833 them so that the main simulation loop does not have to constantly check for
11834 Game_subspace_effect so that it could turn on the subspace sounds.
11835 
11836 Because these are in freespace.cpp there are also stubs of these functions
11837 in fred.cpp as it does not deal with the game loop (obviously) but still
11838 links against code.lib. */
11839 extern void game_start_subspace_ambient_sound();
11840 extern void game_stop_subspace_ambient_sound();
11841 
sexp_mission_set_subspace(int n)11842 void sexp_mission_set_subspace(int n)
11843 {
11844 	if (eval_num(n) > 0) {
11845 		The_mission.flags |= MISSION_FLAG_SUBSPACE;
11846 		Game_subspace_effect = 1;
11847 		game_start_subspace_ambient_sound();
11848 	} else {
11849 		The_mission.flags &= ~MISSION_FLAG_SUBSPACE;
11850 		Game_subspace_effect = 0;
11851 		game_stop_subspace_ambient_sound();
11852 	}
11853 }
11854 
sexp_add_background_bitmap(int n)11855 void sexp_add_background_bitmap(int n)
11856 {
11857 	int sexp_var;
11858 	int new_number;
11859 	char number_as_str[TOKEN_LENGTH];
11860 	starfield_list_entry sle;
11861 
11862 	// filename
11863 	strcpy_s(sle.filename, CTEXT(n));
11864 	n = CDR(n);
11865 
11866 	// sanity checking
11867 	if (stars_find_bitmap(sle.filename) < 0)
11868 	{
11869 		Error(LOCATION, "sexp-add-background-bitmap: Background bitmap %s not found!", sle.filename);
11870 		return;
11871 	}
11872 
11873 	// angles
11874 	sle.ang.p = fl_radians(eval_num(n) % 360);
11875 	n = CDR(n);
11876 	sle.ang.b = fl_radians(eval_num(n) % 360);
11877 	n = CDR(n);
11878 	sle.ang.h = fl_radians(eval_num(n) % 360);
11879 	n = CDR(n);
11880 
11881 	// scale
11882 	sle.scale_x = eval_num(n) / 100.0f;
11883 	n = CDR(n);
11884 	sle.scale_y = eval_num(n) / 100.0f;
11885 	n = CDR(n);
11886 
11887 	// div
11888 	sle.div_x = eval_num(n);
11889 	n = CDR(n);
11890 	sle.div_y = eval_num(n);
11891 	n = CDR(n);
11892 
11893 	// restrict parameters
11894 	if (sle.scale_x > 18) sle.scale_x = 18;
11895 	if (sle.scale_x < 0.1f) sle.scale_x = 0.1f;
11896 	if (sle.scale_y > 18) sle.scale_y = 18;
11897 	if (sle.scale_y < 0.1f) sle.scale_y = 0.1f;
11898 	if (sle.div_x > 5) sle.div_x = 5;
11899 	if (sle.div_x < 1) sle.div_x = 1;
11900 	if (sle.div_y > 5) sle.div_y = 5;
11901 	if (sle.div_y < 1) sle.div_y = 1;
11902 
11903 	Assert((n >= 0) && (n < Num_sexp_nodes));
11904 
11905 	// ripped from sexp_modify_variable()
11906 	// get sexp_variable index
11907 	Assert(Sexp_nodes[n].first == -1);
11908 	sexp_var = atoi(Sexp_nodes[n].text);
11909 
11910 	// verify variable set
11911 	Assert(Sexp_variables[sexp_var].type & SEXP_VARIABLE_SET);
11912 
11913 	if (Sexp_variables[sexp_var].type & SEXP_VARIABLE_NUMBER)
11914 	{
11915         new_number = stars_add_bitmap_entry(&sle);
11916         if (new_number < 0)
11917         {
11918 		    Warning(LOCATION, "Unable to add starfield bitmap: '%s'!", sle.filename);
11919             new_number = 0;
11920         }
11921 
11922 		sprintf(number_as_str, "%d", new_number);
11923 
11924 		// assign to variable
11925 		sexp_modify_variable(number_as_str, sexp_var);
11926 	}
11927 	else
11928 	{
11929 		Error(LOCATION, "sexp-add-background-bitmap: Variable %s must be a number variable!", Sexp_variables[sexp_var].variable_name);
11930 		return;
11931 	}
11932 }
11933 
sexp_remove_background_bitmap(int n)11934 void sexp_remove_background_bitmap(int n)
11935 {
11936 	int slot = eval_num(n);
11937 
11938 	if (slot >= 0) {
11939 		int instances = stars_get_num_bitmaps();
11940 		if ( instances > slot ) {
11941 			stars_mark_bitmap_unused( slot );
11942 		} else {
11943 			Error(LOCATION, "remove-background-bitmap: slot %d does not exist. Slot must be less than %d.",
11944 				slot, instances);
11945 		}
11946 	}
11947 }
11948 
sexp_add_sun_bitmap(int n)11949 void sexp_add_sun_bitmap(int n)
11950 {
11951 	int sexp_var;
11952 	int new_number;
11953 	char number_as_str[TOKEN_LENGTH];
11954 	starfield_list_entry sle;
11955 
11956 	// filename
11957 	strcpy_s(sle.filename, CTEXT(n));
11958 	n = CDR(n);
11959 
11960 	// sanity checking
11961 	if (stars_find_sun(sle.filename) < 0)
11962 	{
11963 		Error(LOCATION, "sexp-add-sun-bitmap: Sun %s not found!", sle.filename);
11964 		return;
11965 	}
11966 
11967 	// angles
11968 	sle.ang.p = fl_radians(eval_num(n) % 360);
11969 	n = CDR(n);
11970 	sle.ang.b = fl_radians(eval_num(n) % 360);
11971 	n = CDR(n);
11972 	sle.ang.h = fl_radians(eval_num(n) % 360);
11973 	n = CDR(n);
11974 
11975 	// scale
11976 	sle.scale_x = eval_num(n) / 100.0f;
11977 	n = CDR(n);
11978 	sle.scale_y = sle.scale_x;
11979 
11980 	// div
11981 	sle.div_x = 1;
11982 	sle.div_y = 1;
11983 
11984 	// restrict parameters
11985 	if (sle.scale_x > 50) sle.scale_x = 50;
11986 	if (sle.scale_x < 0.1f) sle.scale_x = 0.1f;
11987 	if (sle.scale_y > 50) sle.scale_y = 50;
11988 	if (sle.scale_y < 0.1f) sle.scale_y = 0.1f;
11989 
11990 	Assert((n >= 0) && (n < Num_sexp_nodes));
11991 
11992 	// ripped from sexp_modify_variable()
11993 	// get sexp_variable index
11994 	Assert(Sexp_nodes[n].first == -1);
11995 	sexp_var = atoi(Sexp_nodes[n].text);
11996 
11997 	// verify variable set
11998 	Assert(Sexp_variables[sexp_var].type & SEXP_VARIABLE_SET);
11999 
12000 	if (Sexp_variables[sexp_var].type & SEXP_VARIABLE_NUMBER)
12001 	{
12002 		// get new numerical value
12003         new_number = stars_add_sun_entry(&sle);
12004 
12005         if (new_number < 0)
12006         {
12007 		    Warning(LOCATION, "Unable to add sun: '%s'!", sle.filename);
12008             new_number = 0;
12009         }
12010 
12011 		sprintf(number_as_str, "%d", new_number);
12012 
12013 		// assign to variable
12014 		sexp_modify_variable(number_as_str, sexp_var);
12015 	}
12016 	else
12017 	{
12018 		Error(LOCATION, "sexp-add-sun-bitmap: Variable %s must be a number variable!", Sexp_variables[sexp_var].variable_name);
12019 		return;
12020 	}
12021 }
12022 
sexp_remove_sun_bitmap(int n)12023 void sexp_remove_sun_bitmap(int n)
12024 {
12025 	int slot = eval_num(n);
12026 
12027 	if (slot >= 0) {
12028 		int instances = stars_get_num_suns();
12029 		if ( instances > slot ) {
12030 			stars_mark_sun_unused( slot );
12031 		} else {
12032 			Error(LOCATION, "remove-sun-bitmap: slot %d does not exist. Slot must be less than %d.",
12033 				slot, instances);
12034 		}
12035 	}
12036 }
12037 
sexp_nebula_change_storm(int n)12038 void sexp_nebula_change_storm(int n)
12039 {
12040 	if (!(The_mission.flags & MISSION_FLAG_FULLNEB)) return;
12041 
12042 	nebl_set_storm(CTEXT(n));
12043 }
12044 
sexp_nebula_toggle_poof(int n)12045 void sexp_nebula_toggle_poof(int n)
12046 {
12047 	char *name = CTEXT(n);
12048 	int result = is_sexp_true(CDR(n));
12049 	int i;
12050 
12051 	if (name == NULL) return;
12052 
12053 	for (i = 0; i < MAX_NEB2_POOFS; i++)
12054 	{
12055 		if (!stricmp(name,Neb2_poof_filenames[i])) break;
12056 	}
12057 
12058 	//coulnd't find the poof
12059 	if (i == MAX_NEB2_POOFS) return;
12060 
12061 	if (result) Neb2_poof_flags |= (1 << i);
12062 	else Neb2_poof_flags &= ~(1 << i);
12063 
12064 	neb2_eye_changed();
12065 }
12066 
sexp_nebula_change_pattern(int n)12067 void sexp_nebula_change_pattern(int n)
12068 {
12069 	if (!(The_mission.flags & MISSION_FLAG_FULLNEB)) return;
12070 
12071 	strcpy_s(Neb2_texture_name,(CTEXT(n)));
12072 
12073 	neb2_post_level_init();
12074 }
12075 
12076 /**
12077  * End the mission.
12078  *
12079  * Implemented by Sesquipedalian; fixed by EdrickV; enhanced by others
12080  */
sexp_end_mission(int n)12081 void sexp_end_mission(int n)
12082 {
12083 	int ignore_player_mortality = 1;
12084 	int boot_to_main_hall = 0;
12085 
12086 	if (n != -1) {
12087 		ignore_player_mortality = is_sexp_true(n);
12088 		n = CDR(n);
12089 	}
12090 	if (n != -1) {
12091 		boot_to_main_hall = is_sexp_true(n);
12092 		n = CDR(n);
12093 	}
12094 
12095 	// if the player is dead we may want to let the death screen handle things
12096 	if (!ignore_player_mortality && (Player_ship->flags & SF_DYING)) {
12097 		return;
12098 	}
12099 
12100 	// if we go straight to the main hall we have to clean up the mission without entering the debriefing
12101 	if (boot_to_main_hall && !(Game_mode & GM_MULTIPLAYER)) {
12102 		gameseq_post_event(GS_EVENT_END_GAME);
12103 	} else {
12104 		send_debrief_event();
12105 	}
12106 
12107 	// Karajorma - callback all the clients here.
12108 	if (MULTIPLAYER_MASTER)
12109 	{
12110 		multi_handle_sudden_mission_end();
12111 		send_force_end_mission_packet();
12112 	}
12113 }
12114 
12115 // Goober5000
sexp_set_debriefing_toggled(int node)12116 void sexp_set_debriefing_toggled(int node)
12117 {
12118 	if (is_sexp_true(node))
12119 		The_mission.flags |= MISSION_FLAG_TOGGLE_DEBRIEFING;
12120 	else
12121 		The_mission.flags &= ~MISSION_FLAG_TOGGLE_DEBRIEFING;
12122 }
12123 
12124 /**
12125  * Toggle the status bit for the AI code which tells the AI if it is a good time to rearm.
12126  *
12127  * The status being set means good time.  Status not being set (unset), means bad time. Designers must implement this.
12128  */
sexp_good_time_to_rearm(int n)12129 void sexp_good_time_to_rearm(int n)
12130 {
12131 	int team, time;
12132 
12133 	team = iff_lookup(CTEXT(n));
12134 	time = eval_num(CDR(n));						// this is the time for how long a good rearm is active -- in seconds
12135 
12136 	ai_set_rearm_status(team, time);
12137 }
12138 
12139 /**
12140  * Grants promotion to the player
12141  */
sexp_grant_promotion()12142 void sexp_grant_promotion()
12143 {
12144 	// short circuit multiplayer for now until we figure out what to do.
12145 	if ( Game_mode & GM_MULTIPLAYER )
12146 		return;
12147 
12148 	// set a bit to tell player should get promoted at the end of the mission.  I suppose the other
12149 	// thing that we could do would be to set the players score to at least the amount of
12150 	// points for the next level, but this way is better I think.
12151 	if ( Game_mode & GM_CAMPAIGN_MODE ) {
12152 		Player->flags |= PLAYER_FLAGS_PROMOTED;
12153 	}
12154 }
12155 
12156 /**
12157  * Gives the named medal to the players in the mission
12158  */
sexp_grant_medal(int n)12159 void sexp_grant_medal(int n)
12160 {
12161 	int i;
12162 	char *medal_name;
12163 
12164 	// don't give medals in normal gameplay when not in campaign mode
12165 	if ( (Game_mode & GM_NORMAL) && !(Game_mode & GM_CAMPAIGN_MODE) )
12166 		return;
12167 
12168 	medal_name = CTEXT(n);
12169 	if (medal_name == NULL)
12170 		return;
12171 
12172 	if (Player->stats.m_medal_earned >= 0) {
12173 		Warning(LOCATION, "Cannot grant more than one medal per mission!  New medal '%s' will replace old medal '%s'!", medal_name, Medals[Player->stats.m_medal_earned].name);
12174 	}
12175 
12176 	for (i = 0; i < Num_medals; i++ ) {
12177 		if ( !stricmp(medal_name, Medals[i].name) )
12178 			break;
12179 	}
12180 
12181 	if ( i < Num_medals ) {
12182 		Player->stats.m_medal_earned = i;
12183 
12184 		if ( Game_mode & GM_MULTIPLAYER ) {
12185 			for ( int j = 0; j < MAX_PLAYERS; j++ ) {
12186 				if ( MULTI_CONNECTED(Net_players[j]) ) {
12187 					Net_players[j].m_player->stats.m_medal_earned = i;
12188 				}
12189 			}
12190 		}
12191 	}
12192 }
12193 
sexp_change_player_score(int node)12194 void sexp_change_player_score(int node)
12195 {
12196 	int sindex;
12197 	int score;
12198 	int plr_index;
12199 
12200 	score = eval_num(node);
12201 	node = CDR(node);
12202 
12203 	if(!(Game_mode & GM_MULTIPLAYER)){
12204 		if ( (sindex = ship_name_lookup(CTEXT(node))) == -1) {
12205 			Warning(LOCATION, "Invalid shipname '%s' passed to sexp_change_player_score!", CTEXT(node));
12206 			return;
12207 		}
12208 
12209 		if (Player_ship != &Ships[sindex]) {
12210 			Warning(LOCATION, "Can not award points to '%s'. Ship is not a player!", CTEXT(node));
12211 			return;
12212 		}
12213 		Player->stats.m_score += score;
12214 		if (Player->stats.m_score < 0) {
12215 			Player->stats.m_score = 0;
12216 		}
12217 	}
12218 	else {
12219 		while (node >= 0) {
12220 			plr_index = multi_find_player_by_ship_name(CTEXT(node), true);
12221 			if (plr_index >= 0) {
12222 				Net_player[plr_index].m_player->stats.m_score += score;
12223 
12224 				if (Net_player[plr_index].m_player->stats.m_score < 0) {
12225 					Net_player[plr_index].m_player->stats.m_score = 0;
12226 				}
12227 			}
12228 
12229 			node = CDR(node);
12230 		}
12231 	}
12232 }
12233 
sexp_change_team_score(int node)12234 void sexp_change_team_score(int node)
12235 {
12236 	int i, score, team;
12237 
12238 	// since we only have a team score in TvT
12239 	if ( !(MULTI_TEAM) ) {
12240 		return;
12241 	}
12242 
12243 	score = eval_num(node);
12244 	team = eval_num(CDR(node));
12245 
12246 	if (team == 0) {
12247 		for (i = 0; i < MAX_TVT_TEAMS; i++) {
12248 			Multi_team_score[i] += score;
12249 		}
12250 	}
12251 	else if (team > 0 && team <= MAX_TVT_TEAMS) {
12252 		Multi_team_score[team - 1] += score;
12253 	}
12254 	else {
12255 		Warning(LOCATION, "Invalid team number. Team %d does not exist", team);
12256 	}
12257 }
12258 
12259 
12260 
sexp_tech_add_ship(int node)12261 void sexp_tech_add_ship(int node)
12262 {
12263 	int i;
12264 	char *name;
12265 
12266 	Assert(node >= 0);
12267 	// this function doesn't mean anything when not in campaign mode
12268 	if ( !(Game_mode & GM_CAMPAIGN_MODE) )
12269 		return;
12270 
12271 	while (node >= 0) {
12272 		name = CTEXT(node);
12273 		i = ship_info_lookup(name);
12274 		if (i >= 0)
12275 			Ship_info[i].flags |= SIF_IN_TECH_DATABASE;
12276 		else
12277 			Warning(LOCATION, "In tech-add-ship, ship class \"%s\" invalid", name);
12278 
12279 		node = CDR(node);
12280 	}
12281 }
12282 
sexp_tech_add_weapon(int node)12283 void sexp_tech_add_weapon(int node)
12284 {
12285 	int i;
12286 	char *name;
12287 
12288 	Assert(node >= 0);
12289 	// this function doesn't mean anything when not in campaign mode
12290 	if ( !(Game_mode & GM_CAMPAIGN_MODE) )
12291 		return;
12292 
12293 	while (node >= 0) {
12294 		name = CTEXT(node);
12295 		i = weapon_info_lookup(name);
12296 		if (i >= 0)
12297 			Weapon_info[i].wi_flags |= WIF_IN_TECH_DATABASE;
12298 		else
12299 			Warning(LOCATION, "In tech-add-weapon, weapon class \"%s\" invalid", name);
12300 
12301 		node = CDR(node);
12302 	}
12303 }
12304 
12305 // Goober5000
sexp_tech_add_intel(int node)12306 void sexp_tech_add_intel(int node)
12307 {
12308 	int i;
12309 	char *name;
12310 
12311 	Assert(node >= 0);
12312 	// this function doesn't mean anything when not in campaign mode
12313 	if ( !(Game_mode & GM_CAMPAIGN_MODE) )
12314 		return;
12315 
12316 	while (node >= 0) {
12317 		name = CTEXT(node);
12318 		i = intel_info_lookup(name);
12319 		if (i >= 0)
12320 			Intel_info[i].flags |= IIF_IN_TECH_DATABASE;
12321 		else
12322 			Warning(LOCATION, "In tech-add-intel, intel name \"%s\" invalid", name);
12323 
12324 		node = CDR(node);
12325 	}
12326 }
12327 
12328 
12329 // Goober5000
sexp_tech_add_intel_xstr(int node)12330 void sexp_tech_add_intel_xstr(int node)
12331 {
12332 	int i, id, n = node;
12333 	char *name;
12334 
12335 	Assert(n >= 0);
12336 	// this function doesn't mean anything when not in campaign mode
12337 	if ( !(Game_mode & GM_CAMPAIGN_MODE) )
12338 		return;
12339 
12340 	while (n >= 0)
12341 	{
12342 		// don't use things like CTEXT or eval_num, since we didn't in the preloader
12343 		name = Sexp_nodes[n].text;
12344 		n = CDR(n);
12345 		if (n < 0)
12346 			break;
12347 		id = atoi(Sexp_nodes[n].text);
12348 		n = CDR(n);
12349 
12350 		// we already translated this node in the preloader, so just look it up
12351 		i = intel_info_lookup(name);
12352 		if (i >= 0)
12353 			Intel_info[i].flags |= IIF_IN_TECH_DATABASE;
12354 		else
12355 			Warning(LOCATION, "Intel entry XSTR(\"%s\", %d) invalid", name, id);
12356 	}
12357 }
12358 
12359 // Goober5000 - reset all the tech entries to their default states
sexp_tech_reset_to_default()12360 void sexp_tech_reset_to_default()
12361 {
12362 	// this function doesn't mean anything when not in campaign mode
12363 	if ( !(Game_mode & GM_CAMPAIGN_MODE) )
12364 		return;
12365 
12366 	tech_reset_to_default();
12367 }
12368 
12369 /**
12370  * Set variables needed to grant a new ship/weapon to the player during the course
12371  * of a mission
12372  */
sexp_allow_ship(int n)12373 void sexp_allow_ship(int n)
12374 {
12375 	int idx;
12376 	char name[NAME_LENGTH], temp[NAME_LENGTH];
12377 
12378 	// this function doesn't mean anything when not in campaign mode
12379 	if ( !(Game_mode & GM_CAMPAIGN_MODE) )
12380 		return;
12381 
12382 	// get the base name of the ship
12383 	strcpy_s(name, CTEXT(n));
12384 	end_string_at_first_hash_symbol(name);
12385 
12386 	// add that ship, as well as any # equivalents
12387 	for (idx = 0; idx < Num_ship_classes; idx++)
12388 	{
12389 		strcpy_s(temp, Ship_info[idx].name);
12390 		end_string_at_first_hash_symbol(temp);
12391 
12392 		// we have a match, so allow this ship
12393 		if (!strcmp(name, temp))
12394 			mission_campaign_save_persistent(CAMPAIGN_PERSISTENT_SHIP, idx);
12395 	}
12396 }
12397 
sexp_allow_weapon(int n)12398 void sexp_allow_weapon(int n)
12399 {
12400 	int idx;
12401 	char name[NAME_LENGTH], temp[NAME_LENGTH];
12402 
12403 	// this function doesn't mean anything when not in campaign mode
12404 	if ( !(Game_mode & GM_CAMPAIGN_MODE) )
12405 		return;
12406 
12407 	// get the base name of the weapon
12408 	strcpy_s(name, CTEXT(n));
12409 	end_string_at_first_hash_symbol(name);
12410 
12411 	// add that weapon, as well as any # equivalents
12412 	for (idx = 0; idx < Num_weapon_types; idx++)
12413 	{
12414 		strcpy_s(temp, Weapon_info[idx].name);
12415 		end_string_at_first_hash_symbol(temp);
12416 
12417 		// we have a match, so allow this weapon
12418 		if (!strcmp(name, temp))
12419 			mission_campaign_save_persistent(CAMPAIGN_PERSISTENT_WEAPON, idx);
12420 	}
12421 }
12422 
12423 /**
12424  * generic function for all those sexps that set flags
12425  *
12426  * @note this function has a similar purpose to sexp_alter_ship_flag_helper; make sure you check/update both
12427  */
sexp_deal_with_ship_flag(int node,bool process_subsequent_nodes,int object_flag,int object_flag2,int ship_flag,int ship_flag2,int p_object_flag,int p_object_flag2,bool set_it,bool send_multiplayer=false,bool include_players_in_ship_lookup=false)12428 void sexp_deal_with_ship_flag(int node, bool process_subsequent_nodes, int object_flag, int object_flag2, int ship_flag, int ship_flag2, int p_object_flag, int p_object_flag2, bool set_it, bool send_multiplayer = false, bool include_players_in_ship_lookup = false)
12429 {
12430 	char *ship_name;
12431 	int ship_index;
12432 	int n = node;
12433 
12434 	if (send_multiplayer && MULTIPLAYER_MASTER) {
12435 		multi_start_callback();
12436 		multi_send_int(object_flag);
12437 		/* Uncommenting this will break compatibility with earlier builds but it is pointless to send it until object_flag2
12438 		is actually used by the engine
12439 		*/
12440 		// multi_send_int(object_flag2);
12441 		multi_send_int(ship_flag);
12442 		multi_send_int(ship_flag2);
12443 		multi_send_int(p_object_flag);
12444 		multi_send_int(p_object_flag2);
12445 		multi_send_bool(set_it);
12446 	}
12447 
12448 	// loop for all ships in the sexp
12449 	// NB: if the flag is set, we will continue acting on nodes until we run out of them;
12450 	//     if not, we will only act on the first one
12451 	for (; n >= 0; process_subsequent_nodes ? n = CDR(n) : n = -1)
12452 	{
12453 		// get ship name
12454 		ship_name = CTEXT(n);
12455 
12456 		// check to see if ship destroyed or departed.  In either case, do nothing.
12457 		if (mission_log_get_time(LOG_SHIP_DEPARTED, ship_name, NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, ship_name, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL))
12458 			continue;
12459 
12460 		// see if ship exists in-mission
12461 		ship_index = ship_name_lookup(ship_name, include_players_in_ship_lookup ? 1 : 0);
12462 
12463 		// if ship is in-mission
12464 		if (ship_index >= 0)
12465 		{
12466 			// save flags for state change comparisons
12467 			int object_flag_orig = Objects[Ships[ship_index].objnum].flags;
12468 
12469 			// see if we have an object flag to set
12470 			if (object_flag)
12471 			{
12472 				// set or clear?
12473 				if (set_it)
12474 					Objects[Ships[ship_index].objnum].flags |= object_flag;
12475 				else
12476 					Objects[Ships[ship_index].objnum].flags &= ~object_flag;
12477 			}
12478 
12479 			// handle ETS when modifying shields
12480 			if (object_flag == OF_NO_SHIELDS) {
12481 				if (set_it) {
12482 					zero_one_ets(&Ships[ship_index].shield_recharge_index, &Ships[ship_index].weapon_recharge_index, &Ships[ship_index].engine_recharge_index);
12483 				} else if (object_flag_orig & OF_NO_SHIELDS) {
12484 					set_default_recharge_rates(&Objects[Ships[ship_index].objnum]);
12485 				}
12486 			}
12487 
12488 			// see if we have an object flag2 to set
12489 			if (object_flag2)
12490 			{
12491 /*
12492 				// set or clear?
12493 				if (set_it)
12494 					Objects[Ships[ship_index].objnum].flags2 |= object_flag2;
12495 				else
12496 					Objects[Ships[ship_index].objnum].flags2 &= ~object_flag2;
12497 */
12498 			}
12499 
12500 			// see if we have a ship flag to set
12501 			if (ship_flag)
12502 			{
12503 				// set or clear?
12504 				if (set_it)
12505 					Ships[ship_index].flags |= ship_flag;
12506 				else
12507 					Ships[ship_index].flags &= ~ship_flag;
12508 			}
12509 
12510 			// see if we have a ship flag2 to set
12511 			if (ship_flag2)
12512 			{
12513 				// set or clear?
12514 				if (set_it)
12515 					Ships[ship_index].flags2 |= ship_flag2;
12516 				else
12517 					Ships[ship_index].flags2 &= ~ship_flag2;
12518 			}
12519 
12520 			// the lock afterburner SEXP also needs to set a physics flag
12521 			if (ship_flag2 == SF2_AFTERBURNER_LOCKED) {
12522 				if (set_it) {
12523 					afterburners_stop(&Objects[Ships[ship_index].objnum], 1);
12524 				}
12525 			}
12526 
12527 			if (send_multiplayer && MULTIPLAYER_MASTER) {
12528 				multi_send_bool(true);
12529 				multi_send_ship(ship_index);
12530 			}
12531 		}
12532 		// if it's not in-mission
12533 		else
12534 		{
12535 			// grab it from the arrival list
12536 			p_object *p_objp = mission_parse_get_arrival_ship(ship_name);
12537 
12538 			// ships that have had ship-vanish used on them should be skipped
12539 			if (!p_objp) {
12540 				continue;
12541 			}
12542 
12543 			// see if we have a p_object flag to set
12544 			if (p_object_flag)
12545 			{
12546 				// set or clear?
12547 				if (set_it)
12548 					p_objp->flags |= p_object_flag;
12549 				else
12550 					p_objp->flags &= ~p_object_flag;
12551 			}
12552 
12553 			// see if we have a p_object flag2 to set
12554 			if (p_object_flag2)
12555 			{
12556 				// set or clear?
12557 				if (set_it)
12558 					p_objp->flags2 |= p_object_flag2;
12559 				else
12560 					p_objp->flags2 &= ~p_object_flag2;
12561 			}
12562 
12563 			if (send_multiplayer && MULTIPLAYER_MASTER) {
12564 				multi_send_bool(false);
12565 				multi_send_parse_object(p_objp);
12566 			}
12567 		}
12568 	}
12569 
12570 	if (send_multiplayer && MULTIPLAYER_MASTER) {
12571 		multi_end_callback();
12572 	}
12573 }
12574 
multi_sexp_deal_with_ship_flag()12575 void multi_sexp_deal_with_ship_flag()
12576 {
12577 	int object_flag = 0;
12578 	// int object_flag2 = 0;
12579 	int ship_flag = 0;
12580 	int ship_flag2 = 0;
12581 	int p_object_flag = 0;
12582 	int p_object_flag2 = 0;
12583 	bool set_it = false;
12584 	bool ship_arrived = true;
12585 	ship *shipp = NULL;
12586 	p_object *pobjp = NULL;
12587 
12588 	multi_get_int(object_flag);
12589 	// multi_get_int(object_flag2);
12590 	multi_get_int(ship_flag);
12591 	multi_get_int(ship_flag2);
12592 	multi_get_int(p_object_flag);
12593 	multi_get_int(p_object_flag2);
12594 	multi_get_bool(set_it);
12595 
12596 	// if any of the above failed so will this loop
12597 	while (multi_get_bool(ship_arrived))
12598 	{
12599 
12600 		if (ship_arrived) {
12601 			multi_get_ship(shipp);
12602 			if (shipp == NULL) {
12603 				WarningEx(LOCATION, "Null ship pointer in multi_sexp_deal_with_ship_flag(), tell a coder.\n");
12604 				return;
12605 			}
12606 
12607 			// save flags for state change comparisons
12608 			int object_flag_orig = Objects[shipp->objnum].flags;
12609 
12610 			if (set_it) {
12611 				Objects[shipp->objnum].flags |= object_flag;
12612 				// Objects[shipp->objnum].flags2 |= object_flag2;
12613 				shipp->flags |= ship_flag;
12614 				shipp->flags2 |= ship_flag2;
12615 			}
12616 			else {
12617 				Objects[shipp->objnum].flags &= ~object_flag;
12618 				// Objects[shipp->objnum].flags2 &= ~object_flag2;
12619 				shipp->flags &= ~ship_flag;
12620 				shipp->flags2 &= ~ship_flag2;
12621 			}
12622 
12623 			// deal with side effects of these flags
12624 			if (object_flag == OF_NO_SHIELDS) {
12625 				if (set_it) {
12626 					zero_one_ets(&shipp->shield_recharge_index, &shipp->weapon_recharge_index, &shipp->engine_recharge_index);
12627 				} else if (object_flag_orig & OF_NO_SHIELDS) {
12628 					set_default_recharge_rates(&Objects[shipp->objnum]);
12629 				}
12630 			}
12631 
12632 			if (ship_flag2 == SF2_AFTERBURNER_LOCKED) {
12633 				if (set_it) {
12634 					afterburners_stop(&Objects[shipp->objnum], 1);
12635 				}
12636 			}
12637 
12638 			if (ship_flag2 == SF2_STEALTH && !set_it) {
12639 				if (shipp->flags & SF_ESCORT) {
12640 					hud_add_ship_to_escort(shipp->objnum, 1);
12641 				}
12642 			}
12643 			if ((ship_flag2 == SF2_FRIENDLY_STEALTH_INVIS) || (ship_flag == SF_HIDDEN_FROM_SENSORS)) {
12644 				if (set_it) {
12645 					if (Player_ai->target_objnum == shipp->objnum) {
12646 						hud_cease_targeting();
12647 					}
12648 				}
12649 				else {
12650 					if (shipp->flags & SF_ESCORT) {
12651 					hud_add_ship_to_escort(shipp->objnum, 1);
12652 					}
12653 				}
12654 			}
12655 		}
12656 		else {
12657 			multi_get_parse_object(pobjp);
12658 			if (pobjp != NULL) {
12659 				if (set_it) {
12660 					pobjp->flags |= p_object_flag;
12661 					pobjp->flags2 |= p_object_flag2;
12662 				}
12663 				else {
12664 					pobjp->flags &= ~p_object_flag;
12665 					pobjp->flags2 &= ~p_object_flag2;
12666 				}
12667 			}
12668 		}
12669 	}
12670 }
12671 
12672 /**
12673  * sets flags on objects from alter-ship-flag
12674  *
12675  * @note this function has a similar purpose to sexp_deal_with_ship_flag; make sure you check/update both
12676  */
sexp_alter_ship_flag_helper(object_ship_wing_point_team & oswpt,bool future_ships,int object_flag,int object_flag2,int ship_flag,int ship_flag2,int parse_obj_flag,int parse_obj_flag2,int ai_flag,int ai_flag2,bool set_flag)12677 void sexp_alter_ship_flag_helper(object_ship_wing_point_team &oswpt, bool future_ships, int object_flag, int object_flag2, int ship_flag, int ship_flag2, int parse_obj_flag, int parse_obj_flag2, int ai_flag, int ai_flag2, bool set_flag)
12678 {
12679 	int i, object_flag_orig;
12680 	ship_obj	*so;
12681 	object_ship_wing_point_team oswpt2;
12682 	p_object *p_objp;
12683 
12684 	switch (oswpt.type)
12685 	{
12686 		case OSWPT_TYPE_NONE:
12687 		case OSWPT_TYPE_EXITED:
12688 			return;
12689 
12690 		case OSWPT_TYPE_WHOLE_TEAM:
12691 			Assert (oswpt.team >= 0);
12692 			oswpt2.clear();
12693 			for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ){
12694 				if (Ships[Objects[so->objnum].instance].team == oswpt.team) {
12695 					object_ship_wing_point_team_set_ship(&oswpt2, so, future_ships);
12696 
12697 					// recurse
12698 					sexp_alter_ship_flag_helper(oswpt2, future_ships, object_flag, object_flag2, ship_flag, ship_flag2, parse_obj_flag, parse_obj_flag2, ai_flag, ai_flag2, set_flag);
12699 				}
12700 			}
12701 
12702 			if (future_ships) {
12703 				for (p_objp = GET_FIRST(&Ship_arrival_list); p_objp != END_OF_LIST(&Ship_arrival_list); p_objp = GET_NEXT(p_objp)) {
12704 					if (p_objp->team == oswpt.team) {
12705 						oswpt2.p_objp = p_objp;
12706 						oswpt2.type = OSWPT_TYPE_PARSE_OBJECT;
12707 						sexp_alter_ship_flag_helper(oswpt2, future_ships, object_flag, object_flag2, ship_flag, ship_flag2, parse_obj_flag, parse_obj_flag2, ai_flag, ai_flag2, set_flag);
12708 					}
12709 				}
12710 			}
12711 			break;
12712 
12713 		case OSWPT_TYPE_WING:
12714 		case OSWPT_TYPE_WING_NOT_PRESENT:
12715 			// if the wing isn't here, and we're only dealing with ships which are, we're done.
12716 			if  (!future_ships){
12717 				if (oswpt.type == OSWPT_TYPE_WING_NOT_PRESENT) {
12718 					return;
12719 				}
12720 			}
12721 			else {
12722 				for (p_objp = GET_FIRST(&Ship_arrival_list); p_objp != END_OF_LIST(&Ship_arrival_list); p_objp = GET_NEXT(p_objp)) {
12723 					if (p_objp->wingnum == WING_INDEX(oswpt.wingp)) {
12724 						oswpt2.p_objp = p_objp;
12725 						oswpt2.type = OSWPT_TYPE_PARSE_OBJECT;
12726 						sexp_alter_ship_flag_helper(oswpt2, future_ships, object_flag, object_flag2, ship_flag, ship_flag2, parse_obj_flag, parse_obj_flag2, ai_flag, ai_flag2, set_flag);
12727 					}
12728 				}
12729 			}
12730 
12731 			for (i = 0; i < oswpt.wingp->current_count; i++) {
12732 				object_ship_wing_point_team_set_ship(&oswpt2, &Ships[oswpt.wingp->ship_index[i]], future_ships);
12733 				sexp_alter_ship_flag_helper(oswpt2, future_ships, object_flag, object_flag2, ship_flag, ship_flag2, parse_obj_flag, parse_obj_flag2, ai_flag, ai_flag2, set_flag);
12734 			}
12735 
12736 			break;
12737 
12738 		// finally! If we actually have a ship, we can set its flags!
12739 		case OSWPT_TYPE_SHIP:
12740 			// save flags for state change comparisons
12741 			object_flag_orig = oswpt.objp->flags;
12742 
12743 			// see if we have an object flag to set
12744 			if (object_flag)
12745 			{
12746 				// set or clear?
12747 				if (set_flag)
12748 					oswpt.objp->flags |= object_flag;
12749 				else
12750 					oswpt.objp->flags &= ~object_flag;
12751 			}
12752 
12753 			// handle ETS when modifying shields
12754 			if (object_flag == OF_NO_SHIELDS) {
12755 				if (set_flag) {
12756 					zero_one_ets(&oswpt.shipp->shield_recharge_index, &oswpt.shipp->weapon_recharge_index, &oswpt.shipp->engine_recharge_index);
12757 				} else if (object_flag_orig & OF_NO_SHIELDS) {
12758 					set_default_recharge_rates(oswpt.objp);
12759 				}
12760 			}
12761 
12762 			// see if we have an object flag2 to set
12763 			if (object_flag2)
12764 			{
12765 				// Add code here when have flag2 settings
12766 			}
12767 
12768 			// see if we have a ship flag to set
12769 			if (ship_flag)
12770 			{
12771 				// set or clear?
12772 				if (set_flag)
12773 					oswpt.shipp->flags |= ship_flag;
12774 				else
12775 					oswpt.shipp->flags &= ~ship_flag;
12776 			}
12777 
12778 			// see if we have a ship flag2 to set
12779 			if (ship_flag2)
12780 			{
12781 				// set or clear?
12782 				if (set_flag)
12783 					oswpt.shipp->flags2 |= ship_flag2;
12784 				else
12785 					oswpt.shipp->flags2 &= ~ship_flag2;
12786 			}
12787 
12788 			// the lock afterburner SEXP also needs to set a physics flag
12789 			if (ship_flag2 == SF2_AFTERBURNER_LOCKED) {
12790 				if (set_flag) {
12791 					afterburners_stop(oswpt.objp, 1);
12792 				}
12793 			}
12794 
12795 			// see if we have an ai flag to set
12796 			if (ai_flag)
12797 			{
12798 				// set or clear?
12799 				if (set_flag)
12800 					Ai_info[oswpt.shipp->ai_index].ai_flags |= ai_flag;
12801 				else
12802 					Ai_info[oswpt.shipp->ai_index].ai_flags &= ~ai_flag;
12803 			}
12804 
12805 			// see if we have an object flag2 to set
12806 			if (ai_flag2)
12807 			{
12808 				// Add code here when have flag2 settings
12809 			}
12810 
12811 			// no break statement. We want to fall through.
12812 
12813 		case OSWPT_TYPE_PARSE_OBJECT:
12814 			if (!future_ships) {
12815 				return;
12816 			}
12817 
12818 			// see if we have a p_object flag to set
12819 			if (parse_obj_flag && oswpt.p_objp != NULL)
12820 			{
12821 				// set or clear?
12822 				if (set_flag)
12823 					oswpt.p_objp->flags |= parse_obj_flag;
12824 				else
12825 					oswpt.p_objp->flags &= ~parse_obj_flag;
12826 			}
12827 
12828 			// see if we have a p_object flag2 to set
12829 			if (parse_obj_flag2 && oswpt.p_objp != NULL)
12830 			{
12831 				// set or clear?
12832 				if (set_flag)
12833 					oswpt.p_objp->flags2 |= parse_obj_flag2;
12834 				else
12835 					oswpt.p_objp->flags2 &= ~parse_obj_flag2;
12836 			}
12837 			break;
12838 
12839 
12840 	}
12841 
12842 }
12843 
alter_flag_for_all_ships(bool future_ships,int object_flag,int object_flag2,int ship_flag,int ship_flag2,int parse_obj_flag,int parse_obj_flag2,int ai_flag,int ai_flag2,bool set_flag)12844 void alter_flag_for_all_ships(bool future_ships, int object_flag, int object_flag2, int ship_flag, int ship_flag2, int parse_obj_flag, int parse_obj_flag2, int ai_flag, int ai_flag2, bool set_flag)
12845 {
12846 	ship_obj	*so;
12847 	p_object	*p_objp;
12848 	object_ship_wing_point_team oswpt;
12849 
12850 	// set all the ships present in the mission
12851 	for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
12852 		object_ship_wing_point_team_set_ship(&oswpt, so, future_ships);
12853 
12854 		sexp_alter_ship_flag_helper(oswpt, future_ships, object_flag, object_flag2, ship_flag, ship_flag2, parse_obj_flag, parse_obj_flag2, ai_flag, ai_flag2, set_flag);
12855 	}
12856 
12857 	// set up all the ships which have yet to arrive
12858 	if (future_ships) {
12859 		oswpt.clear();
12860 		for (p_objp = GET_FIRST(&Ship_arrival_list); p_objp != END_OF_LIST(&Ship_arrival_list); p_objp = GET_NEXT(p_objp))
12861 		{
12862 			oswpt.p_objp = p_objp;
12863 			oswpt.type = OSWPT_TYPE_PARSE_OBJECT;
12864 			sexp_alter_ship_flag_helper(oswpt, future_ships, object_flag, object_flag2, ship_flag, ship_flag2, parse_obj_flag, parse_obj_flag2, ai_flag, ai_flag2, set_flag);
12865 		}
12866 	}
12867 }
12868 
sexp_check_flag_arrays(char * flag_name,int & object_flag,int & object_flag2,int & ship_flags,int & ship_flags2,int & parse_obj_flag,int & parse_obj_flag2,int & ai_flag,int & ai_flag2)12869 bool sexp_check_flag_arrays(char *flag_name, int &object_flag, int &object_flag2, int &ship_flags, int &ship_flags2, int &parse_obj_flag, int &parse_obj_flag2, int &ai_flag, int &ai_flag2)
12870 {
12871 	int i;
12872 	bool send_multi = false;
12873 
12874 	for ( i = 0; i < MAX_OBJECT_FLAG_NAMES; i++) {
12875 		if (!stricmp(Object_flag_names[i].flag_name, flag_name)) {
12876 			// make sure the list writes to the correct list of flags!
12877 			if (Object_flag_names[i].flag_list == 1) {
12878 				object_flag = Object_flag_names[i].flag;
12879 				send_multi = true;
12880 			}
12881 			else if (Object_flag_names[i].flag_list == 2) {
12882 				object_flag2 = Object_flag_names[i].flag;
12883 			}
12884 			break;
12885 		}
12886 	}
12887 
12888 	for ( i = 0; i < MAX_SHIP_FLAG_NAMES; i++) {
12889 		if (!stricmp(Ship_flag_names[i].flag_name, flag_name)) {
12890 			// make sure the list writes to the correct list of flags!
12891 			if (Ship_flag_names[i].flag_list == 1) {
12892 				ship_flags = Ship_flag_names[i].flag;
12893 			}
12894 			else if (Ship_flag_names[i].flag_list == 2) {
12895 				ship_flags2 = Ship_flag_names[i].flag;
12896 				// only ship flags2 and above need to be sent the game handles the first set of flags elsewhere
12897 				send_multi = true;
12898 			}
12899 			break;
12900 		}
12901 	}
12902 
12903 	// parse files already have a list of names in the same order as the flags, so we can do something slightly different here.
12904 	for ( i = 0; i < MAX_PARSE_OBJECT_FLAGS; i++) {
12905 		if (!stricmp(Parse_object_flags[i], flag_name)) {
12906 			parse_obj_flag = (1 << i);
12907 			break;
12908 		}
12909 	}
12910 
12911 	for ( i = 0; i < MAX_PARSE_OBJECT_FLAGS_2; i++) {
12912 		if (!stricmp(Parse_object_flags_2[i], flag_name)) {
12913 			parse_obj_flag2 = (1 << i);
12914 			break;
12915 		}
12916 	}
12917 
12918 	for ( i = 0; i < MAX_AI_FLAG_NAMES; i++) {
12919 		if (!stricmp(Ai_flag_names[i].flag_name, flag_name)) {
12920 			// make sure the list writes to the correct list of flags!
12921 			if (Ai_flag_names[i].flag_list == 1) {
12922 				ai_flag = Ai_flag_names[i].flag;
12923 			}
12924 			else if (Ai_flag_names[i].flag_list == 2) {
12925 				ai_flag2 = Ai_flag_names[i].flag;
12926 			}
12927 			break;
12928 		}
12929 	}
12930 
12931 	return send_multi;
12932 }
12933 
sexp_are_ship_flags_set(int node)12934 int sexp_are_ship_flags_set(int node)
12935 {
12936 	char *flag_name;
12937 	ship *shipp;
12938 	int object_flag = 0;
12939 	int object_flag2 = 0;
12940 	int ship_flags = 0;
12941 	int ship_flags2 = 0;
12942 	int parse_obj_flag = 0;
12943 	int parse_obj_flag2 = 0;
12944 	int ai_flag = 0;
12945 	int ai_flag2 = 0;
12946 
12947 	shipp = sexp_get_ship_from_node(node);
12948 
12949 	// return false if the ship doesn't exist
12950 	if (shipp == NULL) {
12951 		return 0;
12952 	}
12953 
12954 	node = CDR(node);
12955 
12956 	while (node != -1) {
12957 		flag_name = CTEXT(node);
12958 		sexp_check_flag_arrays(flag_name, object_flag, object_flag2, ship_flags, ship_flags2, parse_obj_flag, parse_obj_flag2, ai_flag, ai_flag2);
12959 
12960 		// now check the flags
12961 		if (object_flag) {
12962 			if (!(Objects[shipp->objnum].flags & object_flag))
12963 				return 0;
12964 		}
12965 		// if we ever get object flags 2 - they go here.
12966 
12967 		if (ship_flags) {
12968 			if (!(shipp->flags & ship_flags))
12969 				return 0;
12970 		}
12971 		if (ship_flags2) {
12972 			if (!(shipp->flags2 & ship_flags2))
12973 				return 0;
12974 		}
12975 
12976 		// we don't check parse flags
12977 
12978 		if (ai_flag) {
12979 			if (!(Ai_info[shipp->ai_index].ai_flags & ai_flag))
12980 				return 0;
12981 		}
12982 
12983 		// no ai flags 2 yet. When we do, they go here.
12984 		node = CDR(node);
12985 	}
12986 
12987 	// if we're still here, all the flags we were looking for were present
12988 	return 1;
12989 }
12990 
sexp_alter_ship_flag(int node)12991 void sexp_alter_ship_flag(int node)
12992 {
12993 	char *flag_name;
12994 	int object_flag = 0;
12995 	int object_flag2 = 0;
12996 	int ship_flags = 0;
12997 	int ship_flags2 = 0;
12998 	int parse_obj_flag = 0;
12999 	int parse_obj_flag2 = 0;
13000 	int ai_flag = 0;
13001 	int ai_flag2 = 0;
13002 	bool set_flag = false;
13003 	bool send_multi = false;
13004 	bool future_ships = false;
13005 	object_ship_wing_point_team oswpt;
13006 
13007 	flag_name = CTEXT(node);
13008 
13009 	send_multi = sexp_check_flag_arrays(flag_name, object_flag, object_flag2, ship_flags, ship_flags2, parse_obj_flag, parse_obj_flag2, ai_flag, ai_flag2);
13010 
13011 	node = CDR(node);
13012 	if (is_sexp_true(node)) {
13013 		set_flag = true;
13014 	}
13015 
13016 	node = CDR(node);
13017 	if (is_sexp_true(node)) {
13018 		future_ships = true;
13019 	}
13020 
13021 
13022 	// start the multiplayer packet
13023 	if (send_multi && MULTIPLAYER_MASTER) {
13024 		multi_start_callback();
13025 		multi_send_int(object_flag);
13026 		/* Uncommenting this will break compatibility with earlier builds but it is pointless to send it until object_flag2
13027 		is actually used by the engine
13028 		*/
13029 		// multi_send_int(object_flag2);
13030 		multi_send_int(ship_flags);
13031 		multi_send_int(ship_flags2);
13032 		multi_send_int(parse_obj_flag);
13033 		multi_send_int(parse_obj_flag2);
13034 		multi_send_int(ai_flag);
13035 		/* Uncommenting this will break compatibility with earlier builds but it is pointless to send it until ai_flag2
13036 		is actually used by the engine
13037 		*/
13038 		// multi_send_int(ai_flag2);
13039 		multi_send_bool(set_flag);
13040 		multi_send_bool(future_ships);
13041 	}
13042 
13043 	node = CDR(node);
13044 
13045 	// no 4th argument means do this to every ship in the mission (and if the flag is set, every ship that will be too).
13046 	if (node == -1) {
13047 		// send a message to the clients saying there were no more arguments
13048 		if (send_multi && MULTIPLAYER_MASTER) {
13049 			multi_send_bool(false);
13050 		}
13051 
13052 		alter_flag_for_all_ships(future_ships, object_flag, object_flag2, ship_flags, ship_flags2, parse_obj_flag, parse_obj_flag2, ai_flag, ai_flag2, set_flag);
13053 	}
13054 
13055 
13056 	else {
13057 		// send a message to the clients saying there are more arguments
13058 		if (send_multi && MULTIPLAYER_MASTER) {
13059 			multi_send_bool(true);
13060 		}
13061 
13062 		while (node != -1) {
13063 			sexp_get_object_ship_wing_point_team(&oswpt, CTEXT(node), future_ships);
13064 
13065 			// no point in setting these flags at all
13066 			if (oswpt.type == OSWPT_TYPE_NONE || oswpt.type == OSWPT_TYPE_EXITED ) {
13067 				node = CDR(node);
13068 				continue;
13069 			}
13070 
13071 			sexp_alter_ship_flag_helper(oswpt, future_ships, object_flag, object_flag2, ship_flags, ship_flags2, parse_obj_flag, parse_obj_flag2, ai_flag, ai_flag2, set_flag);
13072 			node = CDR(node);
13073 
13074 			if (send_multi && MULTIPLAYER_MASTER) {
13075 				multi_send_int(oswpt.type);
13076 
13077 				switch (oswpt.type) {
13078 					case OSWPT_TYPE_SHIP:
13079 						multi_send_ship(oswpt.shipp);
13080 						break;
13081 
13082 					case OSWPT_TYPE_PARSE_OBJECT:
13083 						multi_send_parse_object(oswpt.p_objp);
13084 						break;
13085 
13086 					case OSWPT_TYPE_WING_NOT_PRESENT:
13087 					case OSWPT_TYPE_WING:
13088 						multi_send_ushort(oswpt.wingp->net_signature);
13089 						break;
13090 
13091 					case OSWPT_TYPE_WHOLE_TEAM:
13092 						multi_send_int(oswpt.team);
13093 						break;
13094 				}
13095 			}
13096 		}
13097 	}
13098 	if (send_multi && MULTIPLAYER_MASTER) {
13099 		multi_end_callback();
13100 	}
13101 }
13102 
13103 
13104 
multi_sexp_alter_ship_flag()13105 void multi_sexp_alter_ship_flag()
13106 {
13107 	int i;
13108 	int object_flag = 0;
13109 	int object_flag2 = 0;
13110 	int ship_flag = 0;
13111 	int ship_flag2 = 0;
13112 	int parse_obj_flag = 0;
13113 	int parse_obj_flag2 = 0;
13114 	bool set_flag = false;
13115 	bool future_ships = true;
13116 	bool process_data = false;
13117 	int ai_flag = 0;
13118 	int ai_flag2 = 0;
13119 	int type = -1;
13120 	object_ship_wing_point_team oswpt;
13121 	ushort wing_sig;
13122 
13123 	multi_get_int(object_flag);
13124 	// multi_get_int(object_flag2);
13125 	multi_get_int(ship_flag);
13126 	multi_get_int(ship_flag2);
13127 	multi_get_int(parse_obj_flag);
13128 	multi_get_int(parse_obj_flag2);
13129 	multi_get_int(ai_flag);
13130 	multi_get_bool(set_flag);
13131 	multi_get_bool(future_ships);
13132 
13133 	// if any of the above failed so will this loop
13134 	if (!multi_get_bool(process_data))
13135 	{
13136 		return;
13137 	}
13138 
13139 	// no more data means do this to every ship in the mission
13140 	if (!process_data) {
13141 		alter_flag_for_all_ships(future_ships, object_flag, object_flag2, ship_flag, ship_flag2, parse_obj_flag, parse_obj_flag2, ai_flag, ai_flag2, set_flag);
13142 	}
13143 	else {
13144 		while (multi_get_int(type)) {
13145 			oswpt.clear();
13146 			oswpt.type = type;
13147 
13148 			switch (oswpt.type) {
13149 				case OSWPT_TYPE_SHIP:
13150 					if (multi_get_ship(oswpt.shipp)) {
13151 						oswpt.objp = &Objects[oswpt.shipp->objnum];
13152 					} else {
13153 						Warning(LOCATION, "OSWPT had an invalid ship in multi_sexp_alter_ship_flag(), skipping");
13154 						continue;
13155 					}
13156 					break;
13157 
13158 				case OSWPT_TYPE_PARSE_OBJECT:
13159 					multi_get_parse_object(oswpt.p_objp);
13160 					break;
13161 
13162 				case OSWPT_TYPE_WING_NOT_PRESENT:
13163 				case OSWPT_TYPE_WING:
13164 					multi_get_ushort(wing_sig);
13165 					for (i = 0; i < Num_wings; i++) {
13166 						if (Wings[i].net_signature == wing_sig) {
13167 							oswpt.wingp = &Wings[i];
13168 
13169 							// wing leader handling taken from sexp_get_object_ship_wing_point_team
13170 							if ((oswpt.wingp->special_ship >= 0) && (oswpt.wingp->ship_index[oswpt.wingp->special_ship] >= 0)) {
13171 								oswpt.shipp = &Ships[oswpt.wingp->ship_index[oswpt.wingp->special_ship]];
13172 								oswpt.objp = &Objects[oswpt.shipp->objnum];
13173 							} else {
13174 								oswpt.shipp = &Ships[oswpt.wingp->ship_index[0]];
13175 								oswpt.objp = &Objects[oswpt.shipp->objnum];
13176 								Warning(LOCATION, "Substituting ship '%s' at index 0 for nonexistent wing leader at index %d!", oswpt.shipp->ship_name, oswpt.wingp->special_ship);
13177 							}
13178 							break;
13179 						}
13180 					}
13181 
13182 					if (oswpt.wingp == NULL) {
13183 						Warning(LOCATION, "Unable to get wing to apply flags to in multi_sexp_alter_ship_flag()");
13184 					}
13185 					break;
13186 
13187 				case OSWPT_TYPE_WHOLE_TEAM:
13188 					multi_get_int(oswpt.team);
13189 					break;
13190 			}
13191 			sexp_alter_ship_flag_helper(oswpt, future_ships, object_flag, object_flag2, ship_flag, ship_flag2, parse_obj_flag, parse_obj_flag2, ai_flag, ai_flag2, set_flag);
13192 		}
13193 	}
13194 }
13195 
13196 
13197 // modified by Goober5000; now it should work properly
13198 // function to deal with breaking/fixing the warp engines on ships/wings.
13199 // --repairable is true when we are breaking the warp drive (can be repaired)
13200 // --damage_it is true when we are sabotaging it, one way or the other; false when fixing it
sexp_deal_with_warp(int n,bool repairable,bool damage_it)13201 void sexp_deal_with_warp( int n, bool repairable, bool damage_it )
13202 {
13203 	int ship_flag, p_object_flag;
13204 
13205 	if (repairable)
13206 	{
13207 		ship_flag = SF_WARP_BROKEN;
13208 		p_object_flag = P_SF_WARP_BROKEN;
13209 	}
13210 	else
13211 	{
13212 		ship_flag = SF_WARP_NEVER;
13213 		p_object_flag = P_SF_WARP_NEVER;
13214 	}
13215 
13216 	sexp_deal_with_ship_flag(n, true, 0, 0, ship_flag, 0, p_object_flag, 0, damage_it);
13217 }
13218 
13219 // Goober5000
sexp_set_subspace_drive(int node)13220 void sexp_set_subspace_drive(int node)
13221 {
13222 	bool set_flag = !is_sexp_true(node);
13223 
13224 	sexp_deal_with_ship_flag(CDR(node), true, 0, 0, 0, SF2_NO_SUBSPACE_DRIVE, 0, 0, set_flag);
13225 }
13226 
13227 /**
13228  * Tell the AI when it is okay to fire certain secondary weapons at other ships.
13229  */
sexp_good_secondary_time(int n)13230 void sexp_good_secondary_time(int n)
13231 {
13232 	char *team_name, *weapon_name, *ship_name;
13233 	int num_weapons, weapon_index, team;
13234 
13235 	team_name = CTEXT(n);
13236 	num_weapons = eval_num(CDR(n));
13237 	weapon_name = CTEXT(CDR(CDR(n)));
13238 	ship_name = CTEXT(CDR(CDR(CDR(n))));
13239 
13240 	weapon_index = weapon_info_lookup(weapon_name);
13241 	if ( weapon_index == -1 ) {
13242 		nprintf(("Warning", "couldn't find weapon %s for good-secondary-time\n", weapon_name));
13243 		return;
13244 	}
13245 
13246 	// get the team type from the team_name
13247 	team = iff_lookup(team_name);
13248 
13249 	// see if the ship has departed or has been destroyed.  If so, then we don't need to set up the
13250 	// AI stuff
13251 	if ( mission_log_get_time(LOG_SHIP_DEPARTED, ship_name, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, ship_name, NULL, NULL) )
13252 		return;
13253 
13254 	ai_good_secondary_time( team, weapon_index, num_weapons, ship_name );
13255 }
13256 
13257 // Karajorma - Turns the built in messages for pilots and command on or off
sexp_toggle_builtin_messages(int node,bool enable_messages)13258 void sexp_toggle_builtin_messages (int node, bool enable_messages)
13259 {
13260 	char *ship_name;
13261 	int wingnum, shipnum, ship_index;
13262 
13263 	// If no arguments were supplied then turn off all messages then bail
13264 	if (node < 0)
13265 	{
13266 		if (enable_messages)
13267 		{
13268 			The_mission.flags &= ~MISSION_FLAG_NO_BUILTIN_MSGS;
13269 			return;
13270 		}
13271 		else
13272 		{
13273 			The_mission.flags |= MISSION_FLAG_NO_BUILTIN_MSGS;
13274 			return;
13275 		}
13276 	}
13277 
13278 	// iterate through all the nodes supplied
13279 	while (node >= 0)
13280 	{
13281 		ship_name = CTEXT(node);
13282 
13283 		// check that this isn't a request to silence command.
13284 		if ((*ship_name == '#') && !stricmp(&ship_name[1], The_mission.command_sender))
13285 		{
13286 			// Either disable or enable messages from command
13287 			if (enable_messages)
13288 			{
13289 				The_mission.flags &= ~MISSION_FLAG_NO_BUILTIN_COMMAND;
13290 			}
13291 			else
13292 			{
13293 				The_mission.flags |= MISSION_FLAG_NO_BUILTIN_COMMAND;
13294 			}
13295 		}
13296 		else if (!stricmp(ship_name, "<Any Wingman>"))
13297 		{
13298 			// Since trying to determine whose wingman in a stand alone multiplayer game opens a can of worms
13299 			// Any Wingman silences all ships in wings regardless of whose side they're on.
13300 			for (wingnum = 0; wingnum < Num_wings; wingnum++ ) {
13301 				for ( shipnum = 0; shipnum < Wings[wingnum].current_count; shipnum++ ) {
13302 					ship_index = Wings[wingnum].ship_index[shipnum];
13303 					Assert( ship_index != -1 );
13304 
13305 					if (enable_messages) {
13306 						Ships[ship_index].flags2 &= ~SF2_NO_BUILTIN_MESSAGES;
13307 					}
13308 					else {
13309 						Ships[ship_index].flags2 |= SF2_NO_BUILTIN_MESSAGES;
13310 					}
13311 				}
13312 			}
13313 		}
13314 		// If it isn't command then assume that we're dealing with a ship
13315 		else
13316 		{
13317 			sexp_deal_with_ship_flag(node, false, 0, 0, 0, SF2_NO_BUILTIN_MESSAGES, 0, P2_SF2_NO_BUILTIN_MESSAGES, !enable_messages);
13318 		}
13319 
13320 		node = CDR(node);
13321 	}
13322 }
13323 
sexp_set_persona(int node)13324 void sexp_set_persona (int node)
13325 {
13326 	int persona_index = -1, sindex, i;
13327 	char *persona_name;
13328 
13329 	persona_name = CTEXT(node);
13330 
13331 	for (i = 0 ; i < Num_personas ; i++) {
13332 		if (!strcmp(persona_name, Personas[i].name) && (Personas[i].flags & PERSONA_FLAG_WINGMAN)) {
13333 			persona_index = i;
13334 			break;
13335 		}
13336 	}
13337 
13338 	if (persona_index == -1) {
13339 		Warning(LOCATION, "Unable to change to persona type: '%s'. Persona is not a wingman!", persona_name);
13340 		return;
13341 	}
13342 
13343 	node = CDR(node);
13344 	Assert (node >=0);
13345 
13346 	if (MULTIPLAYER_MASTER) {
13347 		multi_start_callback();
13348 		multi_send_int(persona_index);
13349 	}
13350 
13351 	// now loop through the list of ships
13352 	for ( ; node >= 0; node = CDR(node) ) {
13353 		sindex = ship_name_lookup( CTEXT(node) );
13354 
13355 		if (sindex < 0) {
13356 			continue;
13357 		}
13358 
13359 		if (Ships[sindex].objnum < 0) {
13360 			continue;
13361 		}
13362 
13363 		Ships[sindex].persona_index = persona_index;
13364 
13365 		if (MULTIPLAYER_MASTER) {
13366 			multi_send_ship(sindex);
13367 		}
13368 	}
13369 
13370 	if (MULTIPLAYER_MASTER) {
13371 		multi_end_callback();
13372 	}
13373 }
13374 
multi_sexp_set_persona()13375 void multi_sexp_set_persona()
13376 {
13377 	ship *shipp = NULL;
13378 	int persona_index = -1;
13379 
13380 	if (!multi_get_int(persona_index) ) {
13381 		return;
13382 	}
13383 
13384 	while (multi_get_ship(shipp)) {
13385 		Assert(persona_index != -1);
13386 		if (shipp != NULL) {
13387 			shipp->persona_index = persona_index;
13388 		}
13389 	}
13390 }
13391 
sexp_set_mission_mood(int node)13392 void sexp_set_mission_mood (int node)
13393 {
13394 	char *mood;
13395 
13396 	mood = CTEXT(node);
13397 	for (SCP_vector<SCP_string>::iterator iter = Builtin_moods.begin(); iter != Builtin_moods.end(); ++iter) {
13398 		if (!strcmp(iter->c_str(), mood)) {
13399 			Current_mission_mood = iter - Builtin_moods.begin();
13400 			return;
13401 		}
13402 	}
13403 
13404 	Warning(LOCATION, "Sexp-mission-mood attempted to set mood %s which does not exist in messages.tbl", mood);
13405 }
13406 
sexp_weapon_fired_delay(int node,int op_num)13407 int sexp_weapon_fired_delay(int node, int op_num)
13408 {
13409 	ship *shipp;
13410 
13411 	int requested_bank;
13412 	int delay;
13413 	int last_fired = -1;
13414 
13415 	shipp = sexp_get_ship_from_node(node);
13416 	if (shipp == NULL) {
13417 		return SEXP_FALSE;
13418 	}
13419 
13420 	// Get the bank to check
13421 	node = CDR(node);
13422 	requested_bank = eval_num(node);
13423 	if (requested_bank < 0) {
13424 		return SEXP_FALSE;
13425 	}
13426 
13427 	// get the delay
13428 	node = CDR(node);
13429 	delay = eval_num(node);
13430 	if (delay <= 0 ) {
13431 		return SEXP_FALSE;
13432 	}
13433 
13434 	switch (op_num) {
13435 		case OP_PRIMARY_FIRED_SINCE:
13436 			if (requested_bank >= shipp->weapons.num_primary_banks) {
13437 				return SEXP_FALSE;
13438 			}
13439 			last_fired = shipp->weapons.last_primary_fire_stamp[requested_bank];
13440 			break;
13441 
13442 		case OP_SECONDARY_FIRED_SINCE:
13443 			if (requested_bank >= shipp->weapons.num_secondary_banks) {
13444 				return SEXP_FALSE;
13445 			}
13446 			last_fired = shipp->weapons.last_secondary_fire_stamp[requested_bank];
13447 			break;
13448 	}
13449 
13450 	if (last_fired < 0) {
13451 		// weapon was never fired
13452 		return SEXP_FALSE;
13453 	}
13454 
13455 	if (timestamp() - delay < last_fired) {
13456 		return SEXP_TRUE;
13457 	}
13458 
13459 	return SEXP_FALSE;
13460 }
13461 
sexp_has_weapon(int node,int op_num)13462 int sexp_has_weapon(int node, int op_num)
13463 {
13464 	ship *shipp;
13465 	int i;
13466 	int requested_bank;
13467 	int weapon_index;
13468 	int num_weapon_banks = 0;
13469 	int *weapon_banks = NULL;
13470 
13471 	shipp = sexp_get_ship_from_node(node);
13472 	if (shipp == NULL) {
13473 		return SEXP_FALSE;
13474 	}
13475 
13476 	// Get the bank to check
13477 	node = CDR(node);
13478 	if (!strcmp(CTEXT(node), SEXP_ALL_BANKS_STRING)) {
13479 		requested_bank = -1;
13480 	}
13481 	else {
13482 		requested_bank = eval_num(node);
13483 	}
13484 	node = CDR(node);
13485 
13486 	switch (op_num) {
13487 		case OP_HAS_PRIMARY_WEAPON:
13488 			weapon_banks = shipp->weapons.primary_bank_weapons;
13489 			num_weapon_banks = shipp->weapons.num_primary_banks;
13490 			break;
13491 
13492 		case OP_HAS_SECONDARY_WEAPON:
13493 			weapon_banks = shipp->weapons.secondary_bank_weapons;
13494 			num_weapon_banks = shipp->weapons.num_secondary_banks;
13495 			break;
13496 
13497 		default:
13498 			Warning(LOCATION, "Unrecognised bank type used in has-x-weapon. Returning false");
13499 			return SEXP_FALSE;
13500 	}
13501 
13502 
13503 	//loop through the weapons and test them
13504 	while (node > -1) {
13505 		weapon_index = weapon_info_lookup(CTEXT(node));
13506 		Assertion (weapon_index >= 0, "Weapon name %s is unknown.", CTEXT(node));
13507 
13508 		// if we're checking every bank
13509 		if (requested_bank == -1) {
13510 			for (i = 0; i < num_weapon_banks; i++) {
13511 				if (weapon_index == weapon_banks[i]) {
13512 					return SEXP_TRUE;
13513 				}
13514 			}
13515 		}
13516 
13517 		// if we're only checking one bank
13518 		else {
13519 			if (weapon_index == weapon_banks[requested_bank]) {
13520 				return SEXP_TRUE;
13521 			}
13522 		}
13523 
13524 		node = CDR(node);
13525 	}
13526 
13527 	return SEXP_FALSE;
13528 }
13529 
13530 /**
13531  * Gets status of goals for previous missions (in the current campaign).
13532  *
13533  * @param n Sexp node number
13534  * @param status tell this function if we are looking for a goal_satisfied, goal_failed, or goal incomplete event
13535  */
sexp_previous_goal_status(int n,int status)13536 int sexp_previous_goal_status( int n, int status )
13537 {
13538 	int rval = 0;
13539 	char *goal_name, *mission_name;
13540 	int i, mission_num, default_value = 0, use_defaults = 1;
13541 
13542 	mission_name = CTEXT(n);
13543 	goal_name = CTEXT(CDR(n));
13544 
13545 	// check for possible next optional argument
13546 	n = CDR(CDR(n));
13547 	if ( n != -1 ) {
13548 		default_value = is_sexp_true(n);
13549 	}
13550 
13551 	// try to find the given mission name in the current list of missions in the campaign.
13552 	if ( Game_mode & GM_CAMPAIGN_MODE ) {
13553 		i = mission_campaign_find_mission( mission_name );
13554 
13555 		if ( i == -1 ) {
13556 			// if mission not found, assume that goal was false (so previous-goal-false returns true)
13557 			nprintf(("General", "Couldn't find mission name \"%s\" in current campaign's list of missions.\nReturning %s for goal-status function.", mission_name, (status==GOAL_COMPLETE)?"false":"true"));
13558 			if ( status == GOAL_COMPLETE )
13559 				rval = SEXP_KNOWN_FALSE;
13560 			else
13561 				rval = SEXP_KNOWN_TRUE;
13562 
13563 			use_defaults = 0;
13564 		} else if (Campaign.missions[i].flags & CMISSION_FLAG_SKIPPED) {
13565 			use_defaults = 1;
13566 		} else {
13567 			// now try and find the goal this mission
13568 			mission_num = i;
13569 			for (i = 0; i < Campaign.missions[mission_num].num_goals; i++) {
13570 				if ( !stricmp(Campaign.missions[mission_num].goals[i].name, goal_name) )
13571 					break;
13572 			}
13573 
13574 			if ( i == Campaign.missions[mission_num].num_goals ) {
13575 				Warning(LOCATION, "Couldn't find goal name \"%s\" in mission %s.\nReturning %s for goal-true function.", goal_name, mission_name, (status==GOAL_COMPLETE)?"false":"true");
13576 				if ( status == GOAL_COMPLETE )
13577 					rval = SEXP_KNOWN_FALSE;
13578 				else
13579 					rval = SEXP_KNOWN_TRUE;
13580 
13581 			} else {
13582 				// now return KNOWN_TRUE or KNOWN_FALSE based on the status field in the goal structure
13583 				if ( Campaign.missions[mission_num].goals[i].status == status )
13584 					rval = SEXP_KNOWN_TRUE;
13585 				else
13586 					rval = SEXP_KNOWN_FALSE;
13587 			}
13588 
13589 			use_defaults = 0;
13590 		}
13591 	}
13592 
13593 	if (use_defaults) {
13594 		// when not in campaign mode, always return KNOWN_TRUE when looking for goal complete, and KNOWN_FALSE
13595 		// otherwise
13596 		if ( n != -1 ) {
13597 			if ( default_value )
13598 				rval = SEXP_KNOWN_TRUE;
13599 			else
13600 				rval = SEXP_KNOWN_FALSE;
13601 		} else {
13602 			if ( status == GOAL_COMPLETE )
13603 				rval = SEXP_KNOWN_TRUE;
13604 			else
13605 				rval = SEXP_KNOWN_FALSE;
13606 		}
13607 	}
13608 
13609 	return rval;
13610 }
13611 
13612 // sexpression which gets the status of an event from a previous mission.  Like the above function but
13613 // dealing with events instead of goals.  Again, the status parameter tells the code if we are looking
13614 // for an event_true, event_false, or event_incomplete status
sexp_previous_event_status(int n,int status)13615 int sexp_previous_event_status( int n, int status )
13616 {
13617 	int rval = 0;
13618 	char *name, *mission_name;
13619 	int i, mission_num, default_value = 0, use_defaults = 1;
13620 
13621 	mission_name = CTEXT(n);
13622 	name = CTEXT(CDR(n));
13623 
13624 	// check for possible optional parameter
13625 	n = CDR(CDR(n));
13626 	if ( n != -1 ){
13627 		default_value = is_sexp_true(n);
13628 	}
13629 
13630 	if ( Game_mode & GM_CAMPAIGN_MODE ) {
13631 		// following function returns -1 when mission isn't found.
13632 		i = mission_campaign_find_mission( mission_name );
13633 
13634 		// if the mission name wasn't found -- make this return FALSE for the event status.
13635 		if ( i == -1 ) {
13636 			nprintf(("General", "Couldn't find mission name \"%s\" in current campaign's list of missions.\nReturning %s for event-status function.", mission_name, (status==EVENT_SATISFIED)?"false":"true"));
13637 			if ( status == EVENT_SATISFIED ) {
13638 				rval = SEXP_KNOWN_FALSE;
13639 			} else {
13640 				rval = SEXP_KNOWN_TRUE;
13641 			}
13642 
13643 			use_defaults = 0;
13644 		} else if (Campaign.missions[i].flags & CMISSION_FLAG_SKIPPED) {
13645 			use_defaults = 1;
13646 		} else {
13647 			// now try and find the goal this mission
13648 			mission_num = i;
13649 			for (i = 0; i < Campaign.missions[mission_num].num_events; i++) {
13650 				if ( !stricmp(Campaign.missions[mission_num].events[i].name, name) )
13651 					break;
13652 			}
13653 
13654 			if ( i == Campaign.missions[mission_num].num_events ) {
13655 				Warning(LOCATION, "Couldn't find event name \"%s\" in mission %s.\nReturning %s for event_status function.", name, mission_name, (status==EVENT_SATISFIED)?"false":"true");
13656 				if ( status == EVENT_SATISFIED )
13657 					rval = SEXP_KNOWN_FALSE;
13658 				else
13659 					rval = SEXP_KNOWN_TRUE;
13660 
13661 			} else {
13662 				// now return KNOWN_TRUE or KNOWN_FALSE based on the status field in the goal structure
13663 				if ( Campaign.missions[mission_num].events[i].status == status )
13664 					rval = SEXP_KNOWN_TRUE;
13665 				else
13666 					rval = SEXP_KNOWN_FALSE;
13667 			}
13668 
13669 			use_defaults = 0;
13670 		}
13671 	}
13672 
13673 	if (use_defaults) {
13674 		if ( n != -1 ) {
13675 			if ( default_value )
13676 				rval = SEXP_KNOWN_TRUE;
13677 			else
13678 				rval = SEXP_KNOWN_FALSE;
13679 		} else {
13680 			if ( status == EVENT_SATISFIED )
13681 				rval = SEXP_KNOWN_TRUE;
13682 			else
13683 				rval = SEXP_KNOWN_FALSE;
13684 		}
13685 	}
13686 
13687 	return rval;
13688 }
13689 
13690 /**
13691  * Return the status of an event in the current mission.
13692  *
13693  * @param n Sexp node number
13694  * @param want_true indicates if we are checking whether the event is true or the event is false.
13695  */
sexp_event_status(int n,int want_true)13696 int sexp_event_status( int n, int want_true )
13697 {
13698 	char *name;
13699 	int i, result;
13700 
13701 	name = CTEXT(n);
13702 
13703 	if (name == NULL) {
13704 		Int3();
13705 		return SEXP_FALSE;
13706 	}
13707 
13708 	for (i = 0; i < Num_mission_events; i++ ) {
13709 		// look for the event name, check it's status.  If formula is gone, we know the state won't ever change.
13710 		if ( !stricmp(Mission_events[i].name, name) ) {
13711 			result = Mission_events[i].result;
13712 			if (Mission_events[i].formula < 0) {
13713 				if ( (want_true && result) || (!want_true && !result) )
13714 					return SEXP_KNOWN_TRUE;
13715 				else
13716 					return SEXP_KNOWN_FALSE;
13717 
13718 			} else {
13719 				if ( (want_true && result) || (!want_true && !result) )
13720 					return SEXP_TRUE;
13721 				else
13722 					return SEXP_FALSE;
13723 			}
13724 		}
13725 	}
13726 
13727 	return SEXP_FALSE;
13728 }
13729 
13730 /**
13731  * Return the status of an event N seconds after the event is true or false.
13732  *
13733  * Similar to above function but waits N seconds before returning true
13734  */
sexp_event_delay_status(int n,int want_true,bool use_msecs=false)13735 int sexp_event_delay_status( int n, int want_true, bool use_msecs = false)
13736 {
13737 	char *name;
13738 	int i, result;
13739 	fix delay;
13740 	int rval = SEXP_FALSE;
13741 	int use_as_directive = 0;
13742 
13743 	name = CTEXT(n);
13744 
13745 	if (name == NULL) {
13746 		Int3();
13747 		return SEXP_FALSE;
13748 	}
13749 
13750 	uint64_t tempDelay = eval_num(CDR(n));
13751 
13752 	if (use_msecs) {
13753 		tempDelay = tempDelay << 16;
13754 		tempDelay = tempDelay / 1000;
13755 
13756 		delay = (fix) tempDelay;
13757 	} else {
13758 		delay = i2f(tempDelay);
13759 	}
13760 
13761 	for (i = 0; i < Num_mission_events; i++ ) {
13762 		// look for the event name, check it's status.  If formula is gone, we know the state won't ever change.
13763 		if ( !stricmp(Mission_events[i].name, name) ) {
13764 			if ( (fix) Mission_events[i].timestamp + delay >= Missiontime ) {
13765 				rval = SEXP_FALSE;
13766 				break;
13767 			}
13768 
13769 			result = Mission_events[i].result;
13770 			if (Mission_events[i].formula < 0) {
13771 				if ( (want_true && result) || (!want_true && !result) ) {
13772 					rval = SEXP_KNOWN_TRUE;
13773 					break;
13774 				} else {
13775 					rval = SEXP_KNOWN_FALSE;
13776 					break;
13777 				}
13778 			} else {
13779 				if ( want_true && result ) {  //) || (!want_true && !result) )
13780 					rval = SEXP_TRUE;
13781 					break;
13782 				} else {
13783 					rval = SEXP_FALSE;
13784 					break;
13785 				}
13786 			}
13787 		}
13788 	}
13789 
13790 	// check for possible optional parameter
13791 	n = CDDR(n);
13792 	if (n != -1)
13793 		use_as_directive = is_sexp_true(n);
13794 
13795 	// zero out Sexp_useful_number if it's not true and we don't want this for specific directive use
13796 	if ( !use_as_directive && (rval != SEXP_TRUE) && (rval != SEXP_KNOWN_TRUE) )
13797 		Sexp_useful_number = 0;  // indicate sexp isn't current yet
13798 
13799 	return rval;
13800 }
13801 
13802 /**
13803  * Returns true if the given event is still incomplete
13804  */
sexp_event_incomplete(int n)13805 int sexp_event_incomplete(int n)
13806 {
13807 	char *name;
13808 	int i;
13809 
13810 	name = CTEXT(n);
13811 
13812 	if (name == NULL) {
13813 		Int3();
13814 		return SEXP_FALSE;
13815 	}
13816 
13817 	for (i = 0; i < Num_mission_events; i++ ) {
13818 		if ( !stricmp(Mission_events[i].name, name ) ) {
13819 			// if the formula is still >= 0 (meaning it is still getting eval'ed), then
13820 			// the event is incomplete
13821 			if ( Mission_events[i].formula != -1 )
13822 				return SEXP_TRUE;
13823 			else
13824 				return SEXP_KNOWN_FALSE;
13825 		}
13826 	}
13827 
13828 	return SEXP_FALSE;
13829 }
13830 
13831 /**
13832  * Return the status of an goal N seconds after the goal is true or false.
13833  *
13834  * Similar to above function but operates on goals instead of events
13835  */
sexp_goal_delay_status(int n,int want_true)13836 int sexp_goal_delay_status( int n, int want_true )
13837 {
13838 	char *name;
13839 	fix delay, time;
13840 
13841 	name = CTEXT(n);
13842 	delay = i2f(eval_num(CDR(n)));
13843 
13844 	if ( want_true ) {
13845 		// if we are looking for a goal true entry and we find a false, then return known false here
13846 		if ( mission_log_get_time(LOG_GOAL_FAILED, name, NULL, NULL) )
13847 			return SEXP_KNOWN_FALSE;
13848 		else if ( mission_log_get_time(LOG_GOAL_SATISFIED, name, NULL, &time) ) {
13849 			if ( (Missiontime - time) >= delay )
13850 				return SEXP_KNOWN_TRUE;
13851 		}
13852 	} else {
13853 		// if we are looking for a goal false entry and we find a true, then return known false here
13854 		if ( mission_log_get_time(LOG_GOAL_SATISFIED, name, NULL, NULL) )
13855 			return SEXP_KNOWN_FALSE;
13856 		else if ( mission_log_get_time(LOG_GOAL_FAILED, name, NULL, &time) ) {
13857 			if ( (Missiontime - time) >= delay )
13858 				return SEXP_KNOWN_TRUE;
13859 		}
13860 	}
13861 
13862 	return SEXP_FALSE;
13863 }
13864 
13865 /**
13866  * Returns true if the given goal is still incomplete
13867  */
sexp_goal_incomplete(int n)13868 int sexp_goal_incomplete(int n)
13869 {
13870 	char *name;
13871 
13872 	name = CTEXT(n);
13873 
13874 	if ( mission_log_get_time( LOG_GOAL_SATISFIED, name, NULL, NULL) || mission_log_get_time( LOG_GOAL_FAILED, name, NULL, NULL) )
13875 		return SEXP_KNOWN_FALSE;
13876 	else
13877 		return SEXP_TRUE;
13878 }
13879 
13880 /**
13881  * Protects/unprotects a ship.
13882  *
13883  * @param n Sexp node number
13884  * @param flag Whether or not the protect bit should be set (flag==true) or cleared (flag==false)
13885  */
sexp_protect_ships(int n,bool flag)13886 void sexp_protect_ships(int n, bool flag)
13887 {
13888 	sexp_deal_with_ship_flag(n, true, OF_PROTECTED, 0, 0, 0, P_OF_PROTECTED, 0, flag);
13889 }
13890 
13891 /**
13892  * Protects/unprotects a ship from beams.
13893  *
13894  * @param n Sexp node number
13895  * @param flag Whether or not the protect bit should be set (flag==true) or cleared (flag==false)
13896  */
sexp_beam_protect_ships(int n,bool flag)13897 void sexp_beam_protect_ships(int n, bool flag)
13898 {
13899 	sexp_deal_with_ship_flag(n, true, OF_BEAM_PROTECTED, 0, 0, 0, P_OF_BEAM_PROTECTED, 0, flag);
13900 }
13901 
13902 /**
13903  * Protects/unprotects a ship from various turrets.
13904  *
13905  * @param n Sexp node number
13906  * @param flag Whether or not the protect bit should be set (flag==true) or cleared (flag==false)
13907  */
sexp_turret_protect_ships(int n,bool flag)13908 void sexp_turret_protect_ships(int n, bool flag)
13909 {
13910 	char *turret_type = CTEXT(n);
13911 	n = CDR(n);
13912 
13913 	if (!stricmp(turret_type, "beam"))
13914 		sexp_deal_with_ship_flag(n, true, OF_BEAM_PROTECTED, 0, 0, 0, P_OF_BEAM_PROTECTED, 0, flag);
13915 	else if (!stricmp(turret_type, "flak"))
13916 		sexp_deal_with_ship_flag(n, true, OF_FLAK_PROTECTED, 0, 0, 0, P_OF_FLAK_PROTECTED, 0, flag);
13917 	else if (!stricmp(turret_type, "laser"))
13918 		sexp_deal_with_ship_flag(n, true, OF_LASER_PROTECTED, 0, 0, 0, P_OF_LASER_PROTECTED, 0, flag);
13919 	else if (!stricmp(turret_type, "missile"))
13920 		sexp_deal_with_ship_flag(n, true, OF_MISSILE_PROTECTED, 0, 0, 0, P_OF_MISSILE_PROTECTED, 0, flag);
13921 	else
13922 		Warning(LOCATION, "Invalid turret type '%s'!", turret_type);
13923 }
13924 
13925 // Goober5000 - sets the "don't collide invisible" flag on a list of ships
sexp_dont_collide_invisible(int n,bool dont_collide)13926 void sexp_dont_collide_invisible(int n, bool dont_collide)
13927 {
13928 	sexp_deal_with_ship_flag(n, true, 0, 0, 0, SF2_DONT_COLLIDE_INVIS, 0, P_SF2_DONT_COLLIDE_INVIS, dont_collide);
13929 }
13930 
13931 // Goober5000 - sets the "immobile" flag on a list of ships
sexp_set_immobile(int n,bool immobile)13932 void sexp_set_immobile(int n, bool immobile)
13933 {
13934 	sexp_deal_with_ship_flag(n, true, OF_IMMOBILE, 0, 0, 0, 0, P2_OF_IMMOBILE, immobile);
13935 }
13936 
13937 // Goober5000 - sets the "no-ets" flag on a list of ships
sexp_disable_ets(int n,bool disable)13938 void sexp_disable_ets(int n, bool disable)
13939 {
13940 	sexp_deal_with_ship_flag(n, true, 0, 0, 0, SF2_NO_ETS, 0, P2_SF2_NO_ETS, disable);
13941 }
13942 
13943 // Goober5000 - sets the vaporize flag on a list of ships
sexp_ships_vaporize(int n,bool vaporize)13944 void sexp_ships_vaporize(int n, bool vaporize)
13945 {
13946 	sexp_deal_with_ship_flag(n, true, 0, 0, SF_VAPORIZE, 0, P_SF_VAPORIZE, 0, vaporize);
13947 }
13948 
13949 /**
13950  * Make ships "visible" and "invisible" to sensors.
13951  *
13952  * @param n Sexp node number
13953  * @param visible Is true when making ships visible, false otherwise
13954  */
sexp_ships_visible(int n,bool visible)13955 void sexp_ships_visible(int n, bool visible)
13956 {
13957 	sexp_deal_with_ship_flag(n, true, 0, 0, SF_HIDDEN_FROM_SENSORS, 0, P_SF_HIDDEN_FROM_SENSORS, 0, !visible, true);
13958 
13959 	// we also have to add any escort ships that were made visible
13960 	for (; n >= 0; n = CDR(n))
13961 	{
13962 		int shipnum = ship_name_lookup(CTEXT(n));
13963 		if (shipnum < 0)
13964 			continue;
13965 
13966 		if (!visible && Player_ai->target_objnum == Ships[shipnum].objnum) {
13967 			hud_cease_targeting();
13968 		}
13969 
13970 		else if (visible && (Ships[shipnum].flags & SF_ESCORT)) {
13971 				hud_add_ship_to_escort(Ships[shipnum].objnum, 1);
13972 		}
13973 	}
13974 }
13975 
13976 // Goober5000
sexp_ships_stealthy(int n,bool stealthy)13977 void sexp_ships_stealthy(int n, bool stealthy)
13978 {
13979 	sexp_deal_with_ship_flag(n, true, 0, 0, 0, SF2_STEALTH, 0, P_SF2_STEALTH, stealthy, true);
13980 
13981 	// we also have to add any escort ships that were made visible
13982 	if (!stealthy)
13983 	{
13984 		for (; n >= 0; n = CDR(n))
13985 		{
13986 			int shipnum = ship_name_lookup(CTEXT(n));
13987 			if (shipnum < 0)
13988 				continue;
13989 
13990 			if (Ships[shipnum].flags & SF_ESCORT)
13991 				hud_add_ship_to_escort(Ships[shipnum].objnum, 1);
13992 		}
13993 	}
13994 }
13995 
13996 // Goober5000
sexp_friendly_stealth_invisible(int n,bool invisible)13997 void sexp_friendly_stealth_invisible(int n, bool invisible)
13998 {
13999 	sexp_deal_with_ship_flag(n, true, 0, 0, 0, SF2_FRIENDLY_STEALTH_INVIS, 0, P_SF2_FRIENDLY_STEALTH_INVIS, invisible, true);
14000 
14001 	// we also have to add any escort ships that were made visible
14002 	if (!invisible)
14003 	{
14004 		for (; n >= 0; n = CDR(n))
14005 		{
14006 			int shipnum = ship_name_lookup(CTEXT(n));
14007 			if (shipnum < 0)
14008 				continue;
14009 
14010 			if (Ships[shipnum].flags & SF_ESCORT)
14011 				hud_add_ship_to_escort(Ships[shipnum].objnum, 1);
14012 		}
14013 	}
14014 }
14015 
14016 //FUBAR
14017 //generic function to deal with subsystem flag sexps.
14018 //setit only passed for backward compatibility with older sexps.
sexp_ship_deal_with_subsystem_flag(int node,int ss_flag,bool sendit=false,bool setit=false)14019 void sexp_ship_deal_with_subsystem_flag(int node, int ss_flag, bool sendit = false, bool setit = false)
14020 {
14021 	ship *shipp = NULL;
14022 	ship_subsys *ss = NULL;
14023 
14024 	// get ship
14025 	shipp = sexp_get_ship_from_node(node);
14026 	if (shipp == NULL) {
14027 		return;
14028 	}
14029 
14030 	//replace or not
14031 	// OP_SHIP_SUBSYS_TARGETABLE/UNTARGETABLE, OP_SHIP_SUBSYS_TARGETABLE and OP_TURRET_SUBSYS_TARGET_ENABLE/DISABLE
14032 	// will have already passed us this data we don't need to set it for them.
14033 	// backward compatibility hack for older sexps
14034 	if (!((ss_flag == SSF_UNTARGETABLE) || (ss_flag == SSF_NO_SS_TARGETING)))
14035 	{
14036 		node = CDR(node);
14037 		setit = (is_sexp_true(node) ? true : false);
14038 	}
14039 
14040 	//multiplayer packet start
14041 	if (sendit)
14042 	{
14043 		multi_start_callback();
14044 		multi_send_ship(shipp);
14045 		multi_send_bool(setit);
14046 	}
14047 
14048 	//Process subsystems
14049 	while(node != -1)
14050 	{
14051 		// deal with generic subsystem names
14052 		int generic_type = get_generic_subsys(CTEXT(node));
14053 		if (generic_type) {
14054 			for (ss = GET_FIRST(&shipp->subsys_list); ss != END_OF_LIST(&shipp->subsys_list); ss = GET_NEXT(ss)) {
14055 				if (generic_type == ss->system_info->type) {
14056 					if (setit)
14057 						ss->flags |= ss_flag;
14058 					else
14059 						ss->flags &= ~ss_flag;
14060 				}
14061 			}
14062 		}
14063 		else
14064 		{
14065 			// get the subsystem
14066 			ss = ship_get_subsys(shipp, CTEXT(node));
14067 			if(ss == NULL)
14068 			{
14069 				node = CDR(node);
14070 				continue;
14071 			}
14072 
14073 			// set the flag
14074 			if(setit)
14075 				ss->flags |= ss_flag;
14076 			else
14077 				ss->flags &= ~ss_flag;
14078 		}
14079 
14080 		// multiplayer send subsystem name
14081 		if (sendit)
14082 			multi_send_string(CTEXT(node));
14083 
14084 		// next
14085 		node = CDR(node);
14086 	}
14087 
14088 	// mulitplayer end of packet
14089 	if (sendit)
14090 		multi_end_callback();
14091 }
multi_sexp_deal_with_subsys_flag(int ss_flag)14092 void multi_sexp_deal_with_subsys_flag(int ss_flag)
14093 {
14094 	bool setit = false;
14095 	ship_subsys *ss = NULL;
14096     ship *shipp = NULL;
14097 	char ss_name[MAX_NAME_LEN];
14098 
14099 	multi_get_ship(shipp);
14100 	multi_get_bool(setit);
14101 
14102 	while (multi_get_string(ss_name))
14103 	{
14104 		// deal with generic subsystem names
14105 		int generic_type = get_generic_subsys(ss_name);
14106 		if (generic_type) {
14107 			for (ss = GET_FIRST(&shipp->subsys_list); ss != END_OF_LIST(&shipp->subsys_list); ss = GET_NEXT(ss)) {
14108 				if (generic_type == ss->system_info->type) {
14109 					if (setit)
14110 						ss->flags |= ss_flag;
14111 					else
14112 						ss->flags &= ~ss_flag;
14113 				}
14114 			}
14115 		}
14116 		else
14117 		{
14118 			ss = ship_get_subsys(shipp, ss_name);
14119 			if(ss != NULL)
14120 			{
14121 				// set the flag
14122 				if(setit)
14123 					ss->flags |= ss_flag;
14124 				else
14125 					ss->flags &= ~ss_flag;
14126 			}
14127 		}
14128 	}
14129 }
14130 // Goober5000
sexp_ship_tag(int n,int tag)14131 void sexp_ship_tag( int n, int tag )
14132 {
14133 	int ship_num, tag_level, tag_time, ssm_index(0);
14134     int ssm_team = 0;
14135 
14136 	// check to see if ship destroyed or departed.  In either case, do nothing.
14137 	if ( mission_log_get_time(LOG_SHIP_DEPARTED, CTEXT(n), NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, CTEXT(n), NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, CTEXT(n), NULL, NULL) )
14138 		return;
14139 
14140 	// get the ship num
14141 	ship_num = ship_name_lookup(CTEXT(n));
14142 	if ( ship_num < 0 )
14143 		return;
14144 
14145 	// if untag, then unset everything and leave
14146 	if (!tag)
14147 	{
14148 		Ships[ship_num].tag_left = -1.0f;
14149 		Ships[ship_num].level2_tag_left = -1.0f;
14150 		return;
14151 	}
14152 
14153 	// get the tag level and time
14154 	n = CDR(n);
14155 	tag_level = eval_num(n);
14156 	n = CDR(n);
14157 	tag_time = eval_num(n);
14158 
14159 	// get SSM info if needed
14160 	vec3d start;
14161 	if (tag_level == 3)
14162 	{
14163 		n = CDR(n);
14164 		if (n < 0)
14165 			return;
14166 		ssm_index = ssm_info_lookup(CTEXT(n));
14167 		if (ssm_index < 0)
14168 			return;
14169 
14170 		n = CDR(n);
14171 		if (n < 0)
14172 			return;
14173 		start.xyz.x = (float)eval_num(n);
14174 
14175 		n = CDR(n);
14176 		if (n < 0)
14177 			return;
14178 		start.xyz.y = (float)eval_num(n);
14179 
14180 		n = CDR(n);
14181 		if (n < 0)
14182 			return;
14183 		start.xyz.z = (float)eval_num(n);
14184 
14185         n = CDR(n);
14186 
14187         if (n < 0)
14188         {
14189             ssm_team = 0;
14190         }
14191         else
14192         {
14193             ssm_team = iff_lookup(CTEXT(n));
14194         }
14195 	}
14196 
14197 	ship_apply_tag(ship_num, tag_level, (float)tag_time, &Objects[Ships[ship_num].objnum], &start, ssm_index, ssm_team);
14198 }
14199 
14200 // sexpression to toggle invulnerability flag of ships.
sexp_ships_invulnerable(int n,bool invulnerable)14201 void sexp_ships_invulnerable( int n, bool invulnerable )
14202 {
14203 	sexp_deal_with_ship_flag(n, true, OF_INVULNERABLE, 0, 0, 0, P_OF_INVULNERABLE, 0, invulnerable);
14204 }
14205 
sexp_ships_bomb_targetable(int n,bool targetable)14206 void sexp_ships_bomb_targetable(int n, bool targetable)
14207 {
14208 	sexp_deal_with_ship_flag(n, true, OF_TARGETABLE_AS_BOMB, 0, 0, 0, 0, P2_OF_TARGETABLE_AS_BOMB, targetable, true);
14209 }
14210 
14211 // Goober5000
sexp_ship_guardian_threshold(int node)14212 void sexp_ship_guardian_threshold(int node)
14213 {
14214 	char *ship_name;
14215 	int ship_num, threshold;
14216 	int n = -1;
14217 
14218 	threshold = eval_num(node);
14219 
14220 	n = CDR(node);
14221 
14222 	// for all ships
14223 	for ( ; n != -1; n = CDR(n) ) {
14224 		// check to see if ship destroyed or departed.  In either case, do nothing.
14225 		ship_name = CTEXT(n);
14226 
14227 		if ( mission_log_get_time(LOG_SHIP_DEPARTED, ship_name, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, ship_name, NULL, NULL) ) {
14228 			continue;
14229 		}
14230 
14231 		// get the ship num.  If we get a -1 for the number here, ship has yet to arrive.
14232 		ship_num = ship_name_lookup(ship_name);
14233 
14234 		if (ship_num != -1) {
14235 			Ships[ship_num].ship_guardian_threshold = threshold;
14236 		}
14237 	}
14238 }
14239 
14240 // Goober5000
sexp_ship_subsys_guardian_threshold(int num)14241 void sexp_ship_subsys_guardian_threshold(int num)
14242 {
14243 	char *ship_name, *hull_name;
14244 	int ship_num, threshold;
14245 	ship_subsys *ss;
14246 	int n = -1;
14247 
14248 	threshold = eval_num(num);
14249 
14250 	n = CDR(num);
14251 
14252 	// check to see if ship destroyed or departed.  In either case, do nothing.
14253 	ship_name = CTEXT(n);
14254 
14255 	if ( mission_log_get_time(LOG_SHIP_DEPARTED, ship_name, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, ship_name, NULL, NULL) ) {
14256 		return;
14257 	}
14258 
14259 	// get the ship num.  If we get a -1 for the number here, ship has yet to arrive.
14260 	ship_num = ship_name_lookup(ship_name);
14261 
14262 	if (ship_num == -1) {
14263 		return;
14264 	}
14265 
14266 	n = CDR(n);
14267 
14268 	// for all subsystems
14269 	for ( ; n != -1; n = CDR(n) ) {
14270 		// check for HULL
14271 		hull_name = CTEXT(n);
14272 
14273 		if (hull_name != NULL) {
14274 			int generic_type = get_generic_subsys(hull_name);
14275 			if ( !strcmp(hull_name, SEXP_HULL_STRING) ) {
14276 				Ships[ship_num].ship_guardian_threshold = threshold;
14277 			}
14278 			else if (generic_type) {
14279 				// search through all subsystems
14280 				for (ss = GET_FIRST(&Ships[ship_num].subsys_list); ss != END_OF_LIST(&Ships[ship_num].subsys_list); ss = GET_NEXT(ss)) {
14281 					if (generic_type == ss->system_info->type) {
14282 						ss->subsys_guardian_threshold = threshold;
14283 					}
14284 				}
14285 			}
14286 			else {
14287 				ss = ship_get_subsys(&Ships[ship_num], hull_name);
14288 				if ( ss == NULL ) {
14289 					if (ship_class_unchanged(ship_num)) {
14290 						Warning(LOCATION, "Invalid subsystem passed to ship-subsys-guardian-threshold: %s does not have a %s subsystem", ship_name, hull_name);
14291 					}
14292 				}
14293 				else {
14294 					ss->subsys_guardian_threshold = threshold;
14295 				}
14296 			}
14297 		}
14298 	}
14299 }
14300 
14301 // sexpression to toggle KEEP ALIVE flag of ship object
sexp_ships_guardian(int n,int guardian)14302 void sexp_ships_guardian( int n, int guardian )
14303 {
14304 	char *ship_name;
14305 	int num;
14306 
14307 	for ( ; n != -1; n = CDR(n) )
14308 	{
14309 		ship_name = CTEXT(n);
14310 
14311 		// check to see if ship destroyed or departed.  In either case, do nothing.
14312 		if ( mission_log_get_time(LOG_SHIP_DEPARTED, ship_name, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, ship_name, NULL, NULL) )
14313 			continue;
14314 
14315 		// get the ship num.  If we get a -1 for the number here, ship has yet to arrive.  Store this ship
14316 		// in a list until created
14317 		num = ship_name_lookup(ship_name);
14318 		if ( num != -1 ) {
14319 			Ships[num].ship_guardian_threshold = guardian ? SHIP_GUARDIAN_THRESHOLD_DEFAULT : 0;
14320 		} else {
14321 			p_object *p_objp = mission_parse_get_arrival_ship(ship_name);
14322 			if (p_objp)
14323 			{
14324 				if ( guardian )
14325 					p_objp->flags |= P_SF_GUARDIAN;
14326 				else
14327 					p_objp->flags &= ~P_SF_GUARDIAN;
14328 			}
14329 		}
14330 	}
14331 }
14332 
sexp_ship_create(int n)14333 void sexp_ship_create(int n)
14334 {
14335 	int new_ship_class = -1;
14336 	char *new_ship_name;
14337 	vec3d new_ship_pos = vmd_zero_vector;
14338 	angles new_ship_ang = {0.0f, 0.0f, 0.0f};
14339 	matrix new_ship_ori = vmd_identity_matrix;
14340 	bool change_angles = false;
14341 
14342 	Assert( n >= 0 );
14343 
14344 	// get ship name - none means don't specify it
14345 	// if ship with this name already exists, ship_create will respond appropriately
14346 	if (!stricmp(CTEXT(n), SEXP_NONE_STRING))
14347 	{
14348 		new_ship_name = NULL;
14349 	}
14350 	else
14351 	{
14352 		new_ship_name = CTEXT(n);
14353 	}
14354 
14355 	//Get ship class
14356 	n = CDR(n);
14357 	new_ship_class = ship_info_lookup(CTEXT(n));
14358 
14359 	if(new_ship_class == -1) {
14360 		Warning(LOCATION, "Invalid ship class passed to ship-create; ship type '%s' does not exist", CTEXT(n));
14361 		return;
14362 	}
14363 
14364 	n = CDR(n);
14365 	new_ship_pos.xyz.x = (float) eval_num(n);
14366 	n = CDR(n);
14367 	new_ship_pos.xyz.y = (float) eval_num(n);
14368 	n = CDR(n);
14369 	new_ship_pos.xyz.z = (float) eval_num(n);
14370 
14371 	n = CDR(n);
14372 	if(n != -1) {
14373 		new_ship_ang.p = fl_radians(eval_num(n) % 360);
14374 		change_angles = true;
14375 	}
14376 
14377 	n = CDR(n);
14378 	if(n != -1) {
14379 		new_ship_ang.b = fl_radians(eval_num(n) % 360);
14380 		change_angles = true;
14381 	}
14382 
14383 	n = CDR(n);
14384 	if(n != -1) {
14385 		new_ship_ang.h = fl_radians(eval_num(n) % 360);
14386 		change_angles = true;
14387 	}
14388 
14389 	//This is a costly function, so only do it if needed
14390 	if(change_angles) {
14391 		vm_angles_2_matrix(&new_ship_ori, &new_ship_ang);
14392 	}
14393 
14394 	ship_create(&new_ship_ori, &new_ship_pos, new_ship_class, new_ship_name);
14395 }
14396 
14397 // Goober5000
sexp_weapon_create(int n)14398 void sexp_weapon_create(int n)
14399 {
14400 	int weapon_class, parent_objnum, target_objnum, weapon_objnum;
14401 	ship_subsys *targeted_ss;
14402 
14403 	vec3d weapon_pos = vmd_zero_vector;
14404 	angles weapon_angles = {0.0f, 0.0f, 0.0f};
14405 	matrix weapon_orient = vmd_identity_matrix;
14406 	int is_locked;
14407 	bool change_angles = false;
14408 
14409 	Assert( n >= 0 );
14410 
14411 	parent_objnum = -1;
14412 	if (stricmp(CTEXT(n), SEXP_NONE_STRING))
14413 	{
14414 		int parent_ship = ship_name_lookup(CTEXT(n));
14415 
14416 		if (parent_ship >= 0)
14417 			parent_objnum = Ships[parent_ship].objnum;
14418 	}
14419 	n = CDR(n);
14420 
14421 	weapon_class = weapon_info_lookup(CTEXT(n));
14422 	n = CDR(n);
14423 	if (weapon_class < 0)
14424 	{
14425 		Warning(LOCATION, "Invalid weapon class passed to weapon-create; weapon type '%s' does not exist", CTEXT(n));
14426 		return;
14427 	}
14428 
14429 	weapon_pos.xyz.x = (float) eval_num(n);
14430 	n = CDR(n);
14431 	weapon_pos.xyz.y = (float) eval_num(n);
14432 	n = CDR(n);
14433 	weapon_pos.xyz.z = (float) eval_num(n);
14434 	n = CDR(n);
14435 
14436 	if (n >= 0)
14437 	{
14438 		weapon_angles.p = fl_radians(eval_num(n) % 360);
14439 		n = CDR(n);
14440 		change_angles = true;
14441 	}
14442 
14443 	if (n >= 0)
14444 	{
14445 		weapon_angles.b = fl_radians(eval_num(n) % 360);
14446 		n = CDR(n);
14447 		change_angles = true;
14448 	}
14449 
14450 	if (n >= 0)
14451 	{
14452 		weapon_angles.h = fl_radians(eval_num(n) % 360);
14453 		n = CDR(n);
14454 		change_angles = true;
14455 	}
14456 
14457 	// This is a costly function, so only do it if needed
14458 	if (change_angles)
14459 	{
14460 		vm_angles_2_matrix(&weapon_orient, &weapon_angles);
14461 	}
14462 
14463 	target_objnum = -1;
14464 	if (n >= 0)
14465 	{
14466 		int target_ship = ship_name_lookup(CTEXT(n));
14467 
14468 		if (target_ship >= 0)
14469 			target_objnum = Ships[target_ship].objnum;
14470 
14471 		n = CDR(n);
14472 	}
14473 
14474 	targeted_ss = NULL;
14475 	if (n >= 0)
14476 	{
14477 		if (target_objnum >= 0)
14478 			targeted_ss = ship_get_subsys(&Ships[Objects[target_objnum].instance], CTEXT(n));
14479 
14480 		n = CDR(n);
14481 	}
14482 
14483 	is_locked = (target_objnum >= 0) ? 1 : 0;	// assume full lock; this lets lasers track if people want them to
14484 
14485 	// create the weapon
14486 	weapon_objnum = weapon_create(&weapon_pos, &weapon_orient, weapon_class, parent_objnum, -1, is_locked);
14487 
14488 	// maybe make the weapon track its target
14489 	if (is_locked)
14490 		weapon_set_tracking_info(weapon_objnum, parent_objnum, target_objnum, is_locked, targeted_ss);
14491 }
14492 
14493 // make ship vanish without a trace (and what its docked to)
sexp_ship_vanish(int n)14494 void sexp_ship_vanish(int n)
14495 {
14496 	char *ship_name;
14497 	int num;
14498 
14499 	// if MULTIPLAYER bail
14500 	if (Game_mode & GM_MULTIPLAYER) {
14501 		return;
14502 	}
14503 
14504 	for ( ; n != -1; n = CDR(n) ) {
14505 		ship_name = CTEXT(n);
14506 
14507 		// check to see if ship destroyed or departed.  In either case, do nothing.
14508 		if ( mission_log_get_time(LOG_SHIP_DEPARTED, ship_name, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, ship_name, NULL, NULL) )
14509 			continue;
14510 
14511 		// get the ship num.  If we get a -1 for the number here, ship has yet to arrive
14512 		num = ship_name_lookup(ship_name);
14513 		if ( num != -1 )
14514 			ship_actually_depart(num, SHIP_VANISHED);
14515 	}
14516 }
14517 
sexp_destroy_instantly(int n)14518 void sexp_destroy_instantly(int n)
14519 {
14520 	char *ship_name;
14521 	int ship_num;
14522 	object *ship_obj_p;
14523 
14524 	// if MULTIPLAYER bail
14525 	if (Game_mode & GM_MULTIPLAYER) {
14526 		return;
14527 	}
14528 
14529 	for ( ; n != -1; n = CDR(n) ) {
14530 		ship_name = CTEXT(n);
14531 
14532 		// check to see if ship destroyed or departed.  In either case, do nothing.
14533 		if ( mission_log_get_time(LOG_SHIP_DEPARTED, ship_name, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, ship_name, NULL, NULL) )
14534 			continue;
14535 		ship_num = ship_name_lookup(ship_name);
14536 
14537 		// if it still exists, destroy it
14538 		if (ship_num >= 0) {
14539 			ship_obj_p = &Objects[Ships[ship_num].objnum];
14540 
14541 			//if its the player don't destroy
14542 			if (ship_obj_p == Player_obj)
14543 				continue;
14544 			ship_destroy_instantly(ship_obj_p,ship_num);
14545 		}
14546 	}
14547 }
14548 
sexp_shields_off(int n,bool shields_off)14549 void sexp_shields_off(int n, bool shields_off ) //-Sesquipedalian
14550 {
14551 	sexp_deal_with_ship_flag(n, true, OF_NO_SHIELDS, 0, 0, 0, P_OF_NO_SHIELDS, 0, shields_off, true);
14552 }
14553 
14554 // Goober5000
sexp_ingame_ship_kamikaze(ship * shipp,int kdamage)14555 void sexp_ingame_ship_kamikaze(ship *shipp, int kdamage)
14556 {
14557 	Assert(shipp);
14558 
14559 	ai_info *aip = &Ai_info[shipp->ai_index];
14560 
14561 	if (kdamage > 0)
14562 	{
14563 		aip->ai_flags |= AIF_KAMIKAZE;
14564 		aip->kamikaze_damage = kdamage;
14565 	}
14566 	else
14567 	{
14568 		aip->ai_flags &= ~AIF_KAMIKAZE;
14569 		aip->kamikaze_damage = 0;
14570 	}
14571 }
14572 
14573 // Goober5000
sexp_parse_ship_kamikaze(p_object * parse_obj,int kdamage)14574 void sexp_parse_ship_kamikaze(p_object *parse_obj, int kdamage)
14575 {
14576 	Assert(parse_obj);
14577 
14578 	if (kdamage > 0)
14579 	{
14580 		parse_obj->flags |= P_AIF_KAMIKAZE;
14581 		parse_obj->kamikaze_damage = kdamage;
14582 	}
14583 	else
14584 	{
14585 		parse_obj->flags &= ~P_AIF_KAMIKAZE;
14586 		parse_obj->kamikaze_damage = 0;
14587 	}
14588 }
14589 
14590 // Goober5000 - redone, added wing stuff
sexp_kamikaze(int n,int kamikaze)14591 void sexp_kamikaze(int n, int kamikaze)
14592 {
14593 	int kdamage;
14594 
14595 	kdamage = 0;
14596 	if (kamikaze)
14597 	{
14598 		kdamage = eval_num(n);
14599 		n = CDR(n);
14600 	}
14601 
14602 	for ( ; n != -1; n = CDR(n) )
14603 	{
14604 		object_ship_wing_point_team oswpt;
14605 		sexp_get_object_ship_wing_point_team(&oswpt, CTEXT(n));
14606 
14607 		switch (oswpt.type)
14608 		{
14609 			// change ingame ship
14610 			case OSWPT_TYPE_SHIP:
14611 			{
14612 				sexp_ingame_ship_kamikaze(oswpt.shipp, kdamage);
14613 
14614 				break;
14615 			}
14616 
14617 			// change ship yet to arrive
14618 			case OSWPT_TYPE_PARSE_OBJECT:
14619 			{
14620 				sexp_parse_ship_kamikaze(oswpt.p_objp, kdamage);
14621 
14622 				break;
14623 			}
14624 
14625 			// change wing (we must set the flags for all ships present as well as all ships yet to arrive)
14626 			case OSWPT_TYPE_WING:
14627 			case OSWPT_TYPE_WING_NOT_PRESENT:
14628 			{
14629 				// current ships
14630 				for (int i = 0; i < oswpt.wingp->current_count; i++)
14631 					sexp_ingame_ship_kamikaze(&Ships[oswpt.wingp->ship_index[i]], kdamage);
14632 
14633 				// ships yet to arrive
14634 				for (p_object *p_objp = GET_FIRST(&Ship_arrival_list); p_objp != END_OF_LIST(&Ship_arrival_list); p_objp = GET_NEXT(p_objp))
14635 				{
14636 					if (p_objp->wingnum == WING_INDEX(oswpt.wingp))
14637 						sexp_parse_ship_kamikaze(p_objp, kdamage);
14638 				}
14639 			}
14640 		}
14641 	}
14642 }
14643 
14644 // Goober5000
sexp_ingame_ship_alt_name(ship * shipp,int alt_index)14645 void sexp_ingame_ship_alt_name(ship *shipp, int alt_index)
14646 {
14647 	Assert((shipp != NULL) && (alt_index < Mission_alt_type_count));
14648 
14649 	// we might be clearing it
14650 	if (alt_index < 0)
14651 	{
14652 		shipp->alt_type_index = -1;
14653 		return;
14654 	}
14655 
14656 	// see if this is actually the ship class
14657 	if (!stricmp(Ship_info[shipp->ship_info_index].name, Mission_alt_types[alt_index]))
14658 	{
14659 		shipp->alt_type_index = -1;
14660 		return;
14661 	}
14662 
14663 	shipp->alt_type_index = alt_index;
14664 }
14665 
14666 // Goober5000
sexp_parse_ship_alt_name(p_object * parse_obj,int alt_index)14667 void sexp_parse_ship_alt_name(p_object *parse_obj, int alt_index)
14668 {
14669 	Assert((parse_obj != NULL) && (alt_index < Mission_alt_type_count));
14670 
14671 	// we might be clearing it
14672 	if (alt_index < 0)
14673 	{
14674 		parse_obj->alt_type_index = -1;
14675 		return;
14676 	}
14677 
14678 	// see if this is actually the ship class
14679 	if (!stricmp(Ship_class_names[parse_obj->ship_class], Mission_alt_types[alt_index]))
14680 	{
14681 		parse_obj->alt_type_index = -1;
14682 		return;
14683 	}
14684 
14685 	parse_obj->alt_type_index = alt_index;
14686 }
14687 
14688 // Goober5000
sexp_ship_change_alt_name(int node)14689 void sexp_ship_change_alt_name(int node)
14690 {
14691 	int n = node, new_alt_index;
14692 	char *new_alt_name;
14693 
14694 	// get the alt-name
14695 	new_alt_name = CTEXT(n);
14696 	n = CDR(n);
14697 
14698 	// and its index
14699 	if (!*new_alt_name || !stricmp(new_alt_name, SEXP_ANY_STRING))
14700 	{
14701 		new_alt_index = -1;
14702 	}
14703 	else
14704 	{
14705 		new_alt_index = mission_parse_lookup_alt(new_alt_name);
14706 		if (new_alt_index < 0)
14707 			new_alt_index = mission_parse_add_alt(new_alt_name);
14708 	}
14709 
14710 	for ( ; n != -1; n = CDR(n) )
14711 	{
14712 		object_ship_wing_point_team oswpt;
14713 		sexp_get_object_ship_wing_point_team(&oswpt, CTEXT(n));
14714 
14715 		switch (oswpt.type)
14716 		{
14717 			// change ingame ship
14718 			case OSWPT_TYPE_SHIP:
14719 			{
14720 				sexp_ingame_ship_alt_name(oswpt.shipp, new_alt_index);
14721 				break;
14722 			}
14723 
14724 			// change ship yet to arrive
14725 			case OSWPT_TYPE_PARSE_OBJECT:
14726 			{
14727 				sexp_parse_ship_alt_name(oswpt.p_objp, new_alt_index);
14728 				break;
14729 			}
14730 
14731 			// change wing (we must set the flags for all ships present as well as all ships yet to arrive)
14732 			case OSWPT_TYPE_WING:
14733 			case OSWPT_TYPE_WING_NOT_PRESENT:
14734 			{
14735 				// current ships
14736 				for (int i = 0; i < oswpt.wingp->current_count; i++)
14737 					sexp_ingame_ship_alt_name(&Ships[oswpt.wingp->ship_index[i]], new_alt_index);
14738 
14739 				// ships yet to arrive
14740 				for (p_object *p_objp = GET_FIRST(&Ship_arrival_list); p_objp != END_OF_LIST(&Ship_arrival_list); p_objp = GET_NEXT(p_objp))
14741 				{
14742 					if (p_objp->wingnum == WING_INDEX(oswpt.wingp))
14743 						sexp_parse_ship_alt_name(p_objp, new_alt_index);
14744 				}
14745 				break;
14746 			}
14747 		}
14748 	}
14749 }
14750 
14751 // FUBAR
sexp_ship_change_callsign(int node)14752 void sexp_ship_change_callsign(int node)
14753 {
14754 	char *new_callsign;
14755 	int sindex, cindex;
14756 	ship *shipp = NULL;
14757 
14758 	// get the callsign
14759 	new_callsign = CTEXT(node);
14760 	node = CDR(node);
14761 
14762 	// and its index
14763 	if (!*new_callsign || !stricmp(new_callsign, SEXP_ANY_STRING))
14764 	{
14765 		cindex = -1;
14766 	}
14767 	else
14768 	{
14769 		cindex = mission_parse_lookup_callsign(new_callsign);
14770 		if (cindex < 0)
14771 			cindex = mission_parse_add_callsign(new_callsign);
14772 	}
14773 
14774 	// packets for multi
14775 	multi_start_callback();
14776 	multi_send_string(new_callsign);
14777 
14778 	while ( node >= 0 )
14779 	{
14780 		sindex = ship_name_lookup(CTEXT(node));
14781 		if (sindex >= 0)
14782 		{
14783 			shipp = &Ships[sindex];
14784 			shipp->callsign_index = cindex;
14785 			multi_send_ship(shipp);
14786 		}
14787 		node = CDR(node);
14788 	}
14789 
14790 	multi_end_callback();
14791 }
14792 
multi_sexp_ship_change_callsign()14793 void multi_sexp_ship_change_callsign()
14794 {
14795 	char new_callsign[TOKEN_LENGTH];
14796 	int cindex;
14797 	ship *shipp = NULL;
14798 
14799 	multi_get_string(new_callsign);
14800 	if (!new_callsign[0] || !stricmp(new_callsign, SEXP_ANY_STRING))
14801 	{
14802 		cindex = -1;
14803 	}
14804 	else
14805 	{
14806 		cindex = mission_parse_lookup_callsign(new_callsign);
14807 		if (cindex < 0)
14808 		{
14809 			cindex = mission_parse_add_callsign(new_callsign);
14810 		}
14811 	}
14812 
14813 	while (multi_get_ship(shipp))
14814 	{
14815 		if (shipp != NULL)
14816 		{
14817 			shipp->callsign_index = cindex;
14818 		}
14819 	}
14820 }
14821 
14822 // Goober5000
sexp_set_death_message(int n)14823 void sexp_set_death_message(int n)
14824 {
14825 	int i;
14826 
14827 	// we'll suppose it's the string for now
14828 	Player->death_message = CTEXT(n);
14829 
14830 	// but use an actual message if one exists
14831 	for (i=0; i<Num_messages; i++)
14832 	{
14833 		if (!stricmp(Messages[i].name, Player->death_message.c_str()))
14834 		{
14835 			Player->death_message = Messages[i].message;
14836 			break;
14837 		}
14838 	}
14839 
14840 	// apply localization
14841 	lcl_replace_stuff(Player->death_message);
14842 
14843 	sexp_replace_variable_names_with_values(Player->death_message);
14844 }
14845 
sexp_key_pressed(int node)14846 int sexp_key_pressed(int node)
14847 {
14848 	int z, t;
14849 
14850 	Assert(node != -1);
14851 	z = translate_key_to_index(CTEXT(node), false);
14852 	if (z < 0){
14853 		return SEXP_FALSE;
14854 	}
14855 
14856 	if (!Control_config[z].used){
14857 		return SEXP_FALSE;
14858 	}
14859 
14860 	if (CDR(node) < 0){
14861 		return SEXP_TRUE;
14862 	}
14863 
14864 	t = eval_num(CDR(node));
14865 	return timestamp_has_time_elapsed(Control_config[z].used, t * 1000);
14866 }
14867 
sexp_key_reset(int node)14868 void sexp_key_reset(int node)
14869 {
14870 	int n, z;
14871 
14872 	for (n = node; n != -1; n = CDR(n))
14873 	{
14874 		z = translate_key_to_index(CTEXT(n), false);
14875 		if (z >= 0)
14876 			Control_config[z].used = 0;
14877 	}
14878 }
14879 
sexp_ignore_key(int node)14880 void sexp_ignore_key(int node)
14881 {
14882 	int ignore_count;
14883 	int ignored_key;
14884 
14885 
14886 	ignore_count = eval_num(node);
14887 
14888 	multi_start_callback();
14889 	multi_send_int(ignore_count);
14890 
14891 	node = CDR(node);
14892 	while (node > -1) {
14893 		// get the key
14894 		ignored_key = translate_key_to_index(CTEXT(node), false);
14895 
14896 		if (ignored_key > -1) {
14897 			Ignored_keys[ignored_key] = ignore_count;
14898 		}
14899 
14900 		multi_send_int(ignored_key);
14901 
14902 		node = CDR(node);
14903 	}
14904 
14905 	multi_end_callback();
14906 }
14907 
multi_sexp_ignore_key()14908 void multi_sexp_ignore_key()
14909 {
14910 	int ignored_key, ignore_count;
14911 
14912 	multi_get_int(ignore_count);
14913 
14914 	while (multi_get_int(ignored_key)) {
14915 		Ignored_keys[ignored_key] = ignore_count;
14916 	}
14917 }
14918 
sexp_targeted(int node)14919 int sexp_targeted(int node)
14920 {
14921 	int z;
14922 	ship_subsys *ptr;
14923 
14924 	z = ship_query_state(CTEXT(node));
14925 	if (z == 1){
14926 		return SEXP_KNOWN_FALSE;  // ship isn't around, nor will it ever be
14927 	} else if (z == -1) {
14928 		return SEXP_CANT_EVAL;
14929 	}
14930 
14931 	z = ship_name_lookup(CTEXT(node), 1);
14932 	if ((z < 0) || !Player_ai || (Ships[z].objnum != Player_ai->target_objnum)){
14933 		return SEXP_FALSE;
14934 	}
14935 
14936 	if (CDR(node) >= 0) {
14937 		z = eval_num(CDR(node)) * 1000;
14938 		if (!timestamp_has_time_elapsed(Players_target_timestamp, z)){
14939 			return SEXP_FALSE;
14940 		}
14941 
14942 		if (CDR(CDR(node)) >= 0) {
14943 			ptr = Player_ai->targeted_subsys;
14944 			if (!ptr || subsystem_stricmp(ptr->system_info->subobj_name, CTEXT(CDR(CDR(node))))){
14945 				return SEXP_FALSE;
14946 			}
14947 		}
14948 	}
14949 
14950 	return SEXP_TRUE;
14951 }
14952 
sexp_node_targeted(int node)14953 int sexp_node_targeted(int node)
14954 {
14955 	int z;
14956 
14957 	CJumpNode *jnp = jumpnode_get_by_name(CTEXT(node));
14958 
14959 	if (jnp==NULL || !Player_ai || (jnp->GetSCPObjectNumber() != Player_ai->target_objnum)){
14960 		return SEXP_FALSE;
14961 	}
14962 
14963 	if (CDR(node) >= 0) {
14964 		z = eval_num(CDR(node)) * 1000;
14965 		if (!timestamp_has_time_elapsed(Players_target_timestamp, z)){
14966 			return SEXP_FALSE;
14967 		}
14968 	}
14969 
14970 	return SEXP_TRUE;
14971 }
14972 
sexp_speed(int node)14973 int sexp_speed(int node)
14974 {
14975 	if (Training_context & TRAINING_CONTEXT_SPEED) {
14976 		if (Training_context_speed_set){
14977 			if (timestamp_has_time_elapsed(Training_context_speed_timestamp, eval_num(node) * 1000)){
14978 				return SEXP_KNOWN_TRUE;
14979 			}
14980 		}
14981 	}
14982 
14983 	return SEXP_FALSE;
14984 }
14985 
sexp_get_throttle_speed(int node)14986 int sexp_get_throttle_speed(int node)
14987 {
14988 	player *the_player;
14989 
14990 	the_player = get_player_from_ship_node(node);
14991 
14992 	if (the_player != NULL) {
14993 		float max_speed = Ship_info[Ships[Objects[the_player->objnum].instance].ship_info_index].max_speed;
14994 		return (int)(the_player->ci.forward_cruise_percent / 100.0f * max_speed);
14995 	}
14996 
14997 	return 0;
14998 }
14999 
15000 // CommanderDJ
sexp_set_player_throttle_speed(int node)15001 void sexp_set_player_throttle_speed(int node)
15002 {
15003 	//get and sanity check the player first
15004 	player *the_player;
15005 	the_player = get_player_from_ship_node(node);
15006 
15007 	if(the_player != NULL)
15008 	{
15009 		//now the throttle percentage
15010 		node = CDR(node);
15011 		int throttle_percent = eval_num(node);
15012 		CLAMP(throttle_percent, 0, 100);
15013 
15014 		//now actually set the throttle
15015 		the_player->ci.forward_cruise_percent = (float) throttle_percent;
15016 	}
15017 }
15018 
15019 // Goober5000
sexp_primaries_depleted(int node)15020 int sexp_primaries_depleted(int node)
15021 {
15022 	int sindex, num_banks, num_depleted_banks;
15023 	ship *shipp;
15024 
15025 	// get ship
15026 	sindex = ship_name_lookup(CTEXT(node));
15027 	if (sindex < 0) {
15028 		return SEXP_FALSE;
15029 	}
15030 
15031 	shipp = &Ships[sindex];
15032 	if (shipp->objnum < 0) {
15033 		return SEXP_FALSE;
15034 	}
15035 
15036 	// see if ship has ballistic primary weapons
15037 	if (!(Ship_info[shipp->ship_info_index].flags & SIF_BALLISTIC_PRIMARIES))
15038 		return SEXP_FALSE;
15039 
15040 	// get num primary banks
15041 	num_banks = shipp->weapons.num_primary_banks;
15042 	num_depleted_banks = 0;
15043 
15044 	// get number of depleted banks
15045 	for (int idx=0; idx<num_banks; idx++)
15046 	{
15047 		// is this a ballistic bank?
15048 		if (Weapon_info[shipp->weapons.primary_bank_weapons[idx]].wi_flags2 & WIF2_BALLISTIC)
15049 		{
15050 			// is this bank out of ammo?
15051 			if (shipp->weapons.primary_bank_ammo[idx] == 0)
15052 			{
15053 				num_depleted_banks++;
15054 			}
15055 		}
15056 	}
15057 
15058 	// are they all depleted?
15059 	return (num_depleted_banks == num_banks);
15060 }
15061 
sexp_secondaries_depleted(int node)15062 int sexp_secondaries_depleted(int node)
15063 {
15064 	int sindex, num_banks, num_depleted_banks;
15065 	ship *shipp;
15066 
15067 	// get ship
15068 	sindex = ship_name_lookup(CTEXT(node));
15069 	if (sindex < 0) {
15070 		return SEXP_FALSE;
15071 	}
15072 
15073 	shipp = &Ships[sindex];
15074 	if (shipp->objnum < 0) {
15075 		return SEXP_FALSE;
15076 	}
15077 
15078 	// get num secondary banks
15079 	num_banks = shipp->weapons.num_secondary_banks;
15080 	num_depleted_banks = 0;
15081 
15082 	// get number of depleted banks
15083 	for (int idx=0; idx<num_banks; idx++) {
15084 		if (shipp->weapons.secondary_bank_ammo[idx] == 0) {
15085 			num_depleted_banks++;
15086 		}
15087 	}
15088 
15089 	// are they all depleted?
15090 	return (num_depleted_banks == num_banks);
15091 }
15092 
sexp_facing(int node)15093 int sexp_facing(int node)
15094 {
15095 	float a1, a2;
15096 	vec3d v1, v2;
15097 
15098 	if (!Player_obj) {
15099 		return SEXP_FALSE;
15100 	}
15101 
15102 	ship *target_shipp = sexp_get_ship_from_node(node);
15103 	if (target_shipp == NULL) {
15104 		// hasn't arrived yet
15105 		if (mission_parse_get_arrival_ship(CTEXT(node)) != NULL) {
15106 			return SEXP_CANT_EVAL;
15107 		}
15108 		// not found and won't arrive: invalid
15109 		return SEXP_KNOWN_FALSE;
15110 	}
15111 	double angle = atof(CTEXT(CDR(node)));
15112 
15113 	v1 = Player_obj->orient.vec.fvec;
15114 	vm_vec_normalize(&v1);
15115 
15116 	vm_vec_sub(&v2, &Objects[target_shipp->objnum].pos, &Player_obj->pos);
15117 	vm_vec_normalize(&v2);
15118 
15119 	a1 = vm_vec_dotprod(&v1, &v2);
15120 	a2 = (float) cos(ANG_TO_RAD(angle));
15121 	if (a1 >= a2){
15122 		return SEXP_TRUE;
15123 	}
15124 
15125 	return SEXP_FALSE;
15126 }
15127 
sexp_is_facing(int node)15128 int sexp_is_facing(int node)
15129 {
15130 	object *origin_objp, *target_objp;
15131 	float a1, a2;
15132 	vec3d v1, v2;
15133 
15134 	ship *origin_shipp = sexp_get_ship_from_node(node);
15135 	if (origin_shipp == NULL) {
15136 		// hasn't arrived yet
15137 		if (mission_parse_get_arrival_ship(CTEXT(node)) != NULL) {
15138 			return SEXP_CANT_EVAL;
15139 		}
15140 		// not found and won't arrive: invalid
15141 		return SEXP_KNOWN_FALSE;
15142 	}
15143 	node = CDR(node);
15144 
15145 	object_ship_wing_point_team oswpt;
15146 	sexp_get_object_ship_wing_point_team(&oswpt, CTEXT(node));
15147 
15148 	if (oswpt.type == OSWPT_TYPE_SHIP && sexp_get_ship_from_node(node) == NULL) {
15149 		return SEXP_KNOWN_FALSE;
15150 	}
15151 
15152 	// only true if ship has departed or not yet arrived
15153 	if (oswpt.type == OSWPT_TYPE_EXITED || oswpt.type == OSWPT_TYPE_PARSE_OBJECT) {
15154 		return SEXP_CANT_EVAL;
15155 	}
15156 
15157 	if (oswpt.type == OSWPT_TYPE_NONE)
15158 		return SEXP_CANT_EVAL;
15159 
15160 	origin_objp = &Objects[origin_shipp->objnum];
15161 	target_objp = oswpt.objp;
15162 
15163 	node = CDR(node);
15164 
15165 	double angle = atof(CTEXT(node));
15166 	node = CDR(node);
15167 
15168 	// check optional distance argument
15169 	if (node > 0 && (sexp_distance3(origin_objp, target_objp) > eval_num(node))) {
15170 		return SEXP_FALSE;
15171 	}
15172 
15173 	v1 = origin_objp->orient.vec.fvec;
15174 	vm_vec_normalize(&v1);
15175 
15176 	vm_vec_sub(&v2, &target_objp->pos, &origin_objp->pos);
15177 	vm_vec_normalize(&v2);
15178 
15179 	a1 = vm_vec_dotprod(&v1, &v2);
15180 	a2 = (float) cos(ANG_TO_RAD(angle));
15181 	if (a1 >= a2){
15182 		return SEXP_TRUE;
15183 	}
15184 
15185 	return SEXP_FALSE;
15186 }
15187 
15188 // is ship facing first waypoint in waypoint path
sexp_facing2(int node)15189 int sexp_facing2(int node)
15190 {
15191 	float a1, a2;
15192 	vec3d v1, v2;
15193 
15194 	// bail if Player_obj is not good
15195 	if (!Player_obj) {
15196 		return SEXP_CANT_EVAL;
15197 	}
15198 
15199 	// get player fvec
15200 	v1 = Player_obj->orient.vec.fvec;
15201 	vm_vec_normalize(&v1);
15202 
15203 	// get waypoint name
15204 	char *waypoint_name = CTEXT(node);
15205 
15206 	// get position of first waypoint
15207 	waypoint_list *wp_list = find_matching_waypoint_list(waypoint_name);
15208 
15209 	if (wp_list == NULL) {
15210 		return SEXP_CANT_EVAL;
15211 	}
15212 
15213 	vm_vec_sub(&v2, wp_list->get_waypoints().front().get_pos(), &Player_obj->pos);
15214 	vm_vec_normalize(&v2);
15215 	a1 = vm_vec_dotprod(&v1, &v2);
15216 	a2 = (float) cos(ANG_TO_RAD(atof(CTEXT(CDR(node)))));
15217 	if (a1 >= a2){
15218 		return SEXP_TRUE;
15219 	}
15220 
15221 	return SEXP_FALSE;
15222 }
15223 
sexp_order(int n)15224 int sexp_order(int n)
15225 {
15226 	char *order_to = CTEXT(n);
15227 	char *order = CTEXT(CDR(n));
15228 	char *target = NULL;
15229 
15230 	//target
15231 	n = CDDR(n);
15232 	if (n != -1)
15233 		target = CTEXT(n);
15234 
15235 	return hud_query_order_issued(order_to, order, target);
15236 }
15237 
sexp_query_orders(int n)15238 int sexp_query_orders (int n)
15239 {
15240 	char *order_to = CTEXT(n);
15241 	char *order = CTEXT(CDR(n));
15242 	char *target = NULL;
15243 	char *order_from = NULL;
15244 	char *special = NULL;
15245 	int timestamp = 0;
15246 
15247 	// delay
15248 	n = CDDR(n);
15249 	if (n != -1) {
15250 		timestamp = eval_sexp(n);
15251 		n = CDR(n);
15252 	}
15253 
15254 	//target
15255 	if (n != -1) {
15256 		target = CTEXT(n);
15257 		n = CDR(n);
15258 	}
15259 
15260 	// player order comes from
15261 	if (n != -1) {
15262 		order_from = CTEXT(n);
15263 		n = CDR(n);
15264 	}
15265 
15266 	// optional special argument
15267 	if (n != -1)
15268 		special = CTEXT(n);
15269 
15270 	return hud_query_order_issued(order_to, order, target, timestamp, order_from, special);
15271 }
15272 
15273 // Karajorma
sexp_reset_orders(int n)15274 void sexp_reset_orders (int n)
15275 {
15276 	Squadmsg_history.clear();
15277 }
15278 
sexp_waypoint_missed()15279 int sexp_waypoint_missed()
15280 {
15281 	if (Training_context & TRAINING_CONTEXT_FLY_PATH) {
15282 		if (Training_context_at_waypoint > Training_context_goal_waypoint){
15283 			return SEXP_TRUE;
15284 		}
15285 	}
15286 
15287 	return SEXP_FALSE;
15288 }
15289 
sexp_waypoint_twice()15290 int sexp_waypoint_twice()
15291 {
15292 	if (Training_context & TRAINING_CONTEXT_FLY_PATH) {
15293 		if (Training_context_at_waypoint < Training_context_goal_waypoint - 1){
15294 			return SEXP_TRUE;
15295 		}
15296 	}
15297 
15298 	return SEXP_FALSE;
15299 }
15300 
sexp_path_flown()15301 int sexp_path_flown()
15302 {
15303 	if (Training_context & TRAINING_CONTEXT_FLY_PATH) {
15304 		if ((uint) Training_context_goal_waypoint == Training_context_path->get_waypoints().size()){
15305 			return SEXP_TRUE;
15306 		}
15307 	}
15308 
15309 	return SEXP_FALSE;
15310 }
15311 
sexp_send_training_message(int node)15312 void sexp_send_training_message(int node)
15313 {
15314 	int t = -1, delay = 0;
15315 
15316 	if(physics_paused){
15317 		return;
15318 	}
15319 
15320 	Assert(node >= 0);
15321 	Assert(Event_index >= 0);
15322 
15323 	if ((CDR(node) >= 0) && (CDR(CDR(node)) >= 0)) {
15324 		delay = eval_num(CDR(CDR(node))) * 1000;
15325 		t = CDR(CDR(CDR(node)));
15326 		if (t >= 0){
15327 			t = eval_num(t);
15328 		}
15329 	}
15330 
15331 	multi_start_callback();
15332 	multi_send_int(t);
15333 	multi_send_int(delay);
15334 
15335 	if ((Mission_events[Event_index].repeat_count > 1) || (CDR(node) < 0)){
15336 		message_training_queue(CTEXT(node), timestamp(delay), t);
15337 		multi_send_string(CTEXT(node));
15338 	} else {
15339 		message_training_queue(CTEXT(CDR(node)), timestamp(delay), t);
15340 		multi_send_string(CTEXT(CDR(node)));
15341 	}
15342 	multi_end_callback();
15343 }
15344 
multi_sexp_send_training_message()15345 void multi_sexp_send_training_message()
15346 {
15347 	int t, delay;
15348 	char message[TOKEN_LENGTH];
15349 
15350 	multi_get_int(t);
15351 	multi_get_int(delay);
15352 	if (multi_get_string(message)) {
15353 		message_training_queue(message, timestamp(delay), t);
15354 	}
15355 
15356 }
15357 
sexp_shield_recharge_pct(int node)15358 int sexp_shield_recharge_pct(int node)
15359 {
15360 	int sindex;
15361 
15362 	// get the firing ship
15363 	sindex = ship_name_lookup(CTEXT(node));
15364 	if(sindex < 0){
15365 		return 0;
15366 	}
15367 	if(Ships[sindex].objnum < 0){
15368 		return 0;
15369 	}
15370 
15371 	// shield recharge pct
15372 	return (int)(100.0f * Energy_levels[Ships[sindex].shield_recharge_index]);
15373 }
15374 
sexp_engine_recharge_pct(int node)15375 int sexp_engine_recharge_pct(int node)
15376 {
15377 	int sindex;
15378 
15379 	// get the firing ship
15380 	sindex = ship_name_lookup(CTEXT(node));
15381 	if(sindex < 0){
15382 		return 0;
15383 	}
15384 	if(Ships[sindex].objnum < 0){
15385 		return 0;
15386 	}
15387 
15388 	// shield recharge pct
15389 	return (int)(100.0f * Energy_levels[Ships[sindex].engine_recharge_index]);
15390 }
15391 
sexp_weapon_recharge_pct(int node)15392 int sexp_weapon_recharge_pct(int node)
15393 {
15394 	int sindex;
15395 
15396 	// get the firing ship
15397 	sindex = ship_name_lookup(CTEXT(node));
15398 	if(sindex < 0){
15399 		return 0;
15400 	}
15401 	if(Ships[sindex].objnum < 0){
15402 		return 0;
15403 	}
15404 
15405 	// shield recharge pct
15406 	return (int)(100.0f * Energy_levels[Ships[sindex].weapon_recharge_index]);
15407 }
15408 
15409 /**
15410  * retrieve one ETS index from a ship
15411  */
sexp_get_ets_value(int node)15412 int sexp_get_ets_value(int node)
15413 {
15414 	int sindex;
15415 	SCP_string ets_type;
15416 
15417 	ets_type = CTEXT(node);
15418 	node = CDR(node);
15419 
15420 	sindex = ship_name_lookup(CTEXT(node));
15421 	if (sindex < 0) {
15422 		return SEXP_FALSE;
15423 	}
15424 	if (Ships[sindex].objnum < 0) {
15425 		return SEXP_FALSE;
15426 	}
15427 
15428 	if (!stricmp(ets_type.c_str(), "engine")) {
15429 		return Ships[sindex].engine_recharge_index;
15430 	} else if (!stricmp(ets_type.c_str(), "shield")) {
15431 		return Ships[sindex].shield_recharge_index;
15432 	} else if (!stricmp(ets_type.c_str(), "weapon")) {
15433 		return Ships[sindex].weapon_recharge_index;
15434 	} else {
15435 		return SEXP_FALSE;
15436 	}
15437 }
15438 
15439 /**
15440  * set all ETS indexes for one or more ships
15441  */
sexp_set_ets_values(int node)15442 void sexp_set_ets_values(int node)
15443 {
15444 	int sindex;
15445 	int ets_idx[num_retail_ets_gauges];
15446 
15447 	ets_idx[ENGINES] = eval_num(node);
15448 	node = CDR(node);
15449 	ets_idx[SHIELDS] = eval_num(node);
15450 	node = CDR(node);
15451 	ets_idx[WEAPONS] = eval_num(node);
15452 	node = CDR(node);
15453 
15454 	// sanity check inputs
15455 	sanity_check_ets_inputs(ets_idx);
15456 
15457 	multi_start_callback();
15458 
15459 	// apply ETS settings to specified ships
15460 	for ( ; node != -1; node = CDR(node)) {
15461 		sindex = ship_name_lookup(CTEXT(node));
15462 
15463 		if (sindex >= 0 && validate_ship_ets_indxes(sindex, ets_idx)) {
15464 			Ships[sindex].engine_recharge_index = ets_idx[ENGINES];
15465 			Ships[sindex].shield_recharge_index = ets_idx[SHIELDS];
15466 			Ships[sindex].weapon_recharge_index = ets_idx[WEAPONS];
15467 
15468 			multi_send_ship(sindex);
15469 			multi_send_int(ets_idx[ENGINES]);
15470 			multi_send_int(ets_idx[SHIELDS]);
15471 			multi_send_int(ets_idx[WEAPONS]);
15472 		}
15473 	}
15474 	multi_end_callback();
15475 }
15476 
multi_sexp_set_ets_values()15477 void multi_sexp_set_ets_values()
15478 {
15479 	int sindex;
15480 	int ets_idx[num_retail_ets_gauges];
15481 
15482 	while (multi_get_ship(sindex)) {
15483 		multi_get_int(ets_idx[ENGINES]);
15484 		multi_get_int(ets_idx[SHIELDS]);
15485 		multi_get_int(ets_idx[WEAPONS]);
15486 
15487 		Ships[sindex].engine_recharge_index = ets_idx[ENGINES];
15488 		Ships[sindex].shield_recharge_index = ets_idx[SHIELDS];
15489 		Ships[sindex].weapon_recharge_index = ets_idx[WEAPONS];
15490 	}
15491 }
15492 
sexp_shield_quad_low(int node)15493 int sexp_shield_quad_low(int node)
15494 {
15495 	int sindex, idx;
15496 	float max_quad, check;
15497 	ship_info *sip;
15498 	object *objp;
15499 
15500 	// get the ship
15501 	sindex = ship_name_lookup(CTEXT(node));
15502 	if(sindex < 0){
15503 		return SEXP_FALSE;
15504 	}
15505 	if((Ships[sindex].objnum < 0) || (Ships[sindex].objnum >= MAX_OBJECTS)){
15506 		return SEXP_FALSE;
15507 	}
15508 	if((Ships[sindex].ship_info_index < 0) || (Ships[sindex].ship_info_index >= Num_ship_classes)){
15509 		return SEXP_FALSE;
15510 	}
15511 	objp = &Objects[Ships[sindex].objnum];
15512 	sip = &Ship_info[Ships[sindex].ship_info_index];
15513 	if(!(sip->flags & SIF_SMALL_SHIP)){
15514 		return SEXP_FALSE;
15515 	}
15516 	max_quad = get_max_shield_quad(objp);
15517 
15518 	// shield pct
15519 	check = (float)eval_num(CDR(node));
15520 
15521 	// check his quadrants
15522 	for(idx=0; idx<objp->n_quadrants; idx++){
15523 		if( ((objp->shield_quadrant[idx] / max_quad) * 100.0f) <= check ){
15524 			return SEXP_TRUE;
15525 		}
15526 	}
15527 
15528 	// all good
15529 	return SEXP_FALSE;
15530 }
15531 
15532 // Goober5000
sexp_primary_ammo_pct(int node)15533 int sexp_primary_ammo_pct(int node)
15534 {
15535 	ship *shipp;
15536 	int sindex;
15537 	int check, idx;
15538 	int ret_sum[MAX_SHIP_PRIMARY_BANKS];
15539 	int ret = 0;
15540 
15541 	// get the ship
15542 	sindex = ship_name_lookup(CTEXT(node));
15543 	if(sindex < 0)
15544 	{
15545 		return 0;
15546 	}
15547 	if((Ships[sindex].objnum < 0) || (Ships[sindex].objnum >= MAX_OBJECTS))
15548 	{
15549 		return 0;
15550 	}
15551 	shipp = &Ships[sindex];
15552 
15553 	// bank to check
15554 	check = eval_num(CDR(node));
15555 
15556 	// bogus check?
15557 	if(check < 0){
15558 		return 0;
15559 	}
15560 
15561 	// cumulative sum?
15562 	if(check >= shipp->weapons.num_primary_banks)
15563 	{
15564 		for(idx=0; idx<shipp->weapons.num_primary_banks; idx++)
15565 		{
15566 			// ballistic
15567 			if (Weapon_info[shipp->weapons.primary_bank_weapons[idx]].wi_flags2 & WIF2_BALLISTIC)
15568 			{
15569 				ret_sum[idx] = (int)(((float)shipp->weapons.primary_bank_ammo[idx] / (float)shipp->weapons.primary_bank_start_ammo[idx]) * 100.0f);
15570 			}
15571 			// non-ballistic
15572 			else
15573 			{
15574 				ret_sum[idx] = 100;
15575 			}
15576 		}
15577 
15578 		// add it up
15579 		ret = 0;
15580 		for(idx=0; idx<shipp->weapons.num_primary_banks; idx++)
15581 		{
15582 			ret += ret_sum[idx];
15583 		}
15584 		ret = (int)((float)ret / (float)shipp->weapons.num_primary_banks);
15585 	}
15586 	else
15587 	{
15588 		// ballistic
15589 		if (Weapon_info[shipp->weapons.primary_bank_weapons[check]].wi_flags2 & WIF2_BALLISTIC)
15590 		{
15591 			ret = (int)(((float)shipp->weapons.primary_bank_ammo[check] / (float)shipp->weapons.primary_bank_start_ammo[check]) * 100.0f);
15592 		}
15593 		// non-ballistic
15594 		else
15595 		{
15596 			ret = 100;
15597 		}
15598 	}
15599 
15600 	// return
15601 	return ret;
15602 }
15603 
sexp_secondary_ammo_pct(int node)15604 int sexp_secondary_ammo_pct(int node)
15605 {
15606 	ship *shipp;
15607 	int sindex;
15608 	int check, idx;
15609 	int ret_sum[MAX_SHIP_SECONDARY_BANKS];
15610 	int ret = 0;
15611 
15612 	// get the ship
15613 	sindex = ship_name_lookup(CTEXT(node));
15614 	if(sindex < 0){
15615 		return 0;
15616 	}
15617 	if((Ships[sindex].objnum < 0) || (Ships[sindex].objnum >= MAX_OBJECTS)){
15618 		return 0;
15619 	}
15620 	shipp = &Ships[sindex];
15621 
15622 	// bank to check
15623 	check = eval_num(CDR(node));
15624 
15625 	// bogus check?
15626 	if(check < 0){
15627 		return 0;
15628 	}
15629 
15630 	// cumulative sum?
15631 	if(check >= shipp->weapons.num_secondary_banks){
15632 		for(idx=0; idx<shipp->weapons.num_secondary_banks; idx++){
15633 			ret_sum[idx] = (int)(((float)shipp->weapons.secondary_bank_ammo[idx] / (float)shipp->weapons.secondary_bank_start_ammo[idx]) * 100.0f);
15634 		}
15635 
15636 		// add it up
15637 		ret = 0;
15638 		for(idx=0; idx<shipp->weapons.num_secondary_banks; idx++){
15639 			ret += ret_sum[idx];
15640 		}
15641 		ret = (int)((float)ret / (float)shipp->weapons.num_secondary_banks);
15642 	} else {
15643 		ret = (int)(((float)shipp->weapons.secondary_bank_ammo[check] / (float)shipp->weapons.secondary_bank_start_ammo[check]) * 100.0f);
15644 	}
15645 
15646 	// return
15647 	return ret;
15648 }
15649 
15650 
15651 // Karajorma - returns the number of bullets left in an ballistic ammo bank. Unlike primary_ammo_pct
15652 // it does this as a numerical value rather than as a percentage of the maximum
sexp_get_primary_ammo(int node)15653 int sexp_get_primary_ammo(int node)
15654 {
15655 	ship *shipp;
15656 	int ammo_left = 0;
15657 	int sindex;
15658 	int check;
15659 
15660 	// get the ship
15661 	sindex = ship_name_lookup(CTEXT(node));
15662 	if(sindex < 0)
15663 	{
15664 		return 0;
15665 	}
15666 	if((Ships[sindex].objnum < 0) || (Ships[sindex].objnum >= MAX_OBJECTS))
15667 	{
15668 		return 0;
15669 	}
15670 	shipp = &Ships[sindex];
15671 
15672 	// bank to check
15673 	check = eval_num(CDR(node));
15674 
15675 	// bogus check?
15676 	if(check < 0){
15677 		return 0;
15678 	}
15679 
15680 	// cumulative sum?
15681 	if(check >= shipp->weapons.num_primary_banks)
15682 	{
15683 		for(int bank=0; bank<shipp->weapons.num_primary_banks; bank++)
15684 		{
15685 			// Only ballistic weapons need to be counted
15686 			if (Weapon_info[shipp->weapons.primary_bank_weapons[bank]].wi_flags2 & WIF2_BALLISTIC)
15687 			{
15688 				ammo_left += shipp->weapons.primary_bank_ammo[bank] ;
15689 			}
15690 		}
15691 	}
15692 	else
15693 	{
15694 		// Again only ballistic weapons need to be counted
15695 		if (Weapon_info[shipp->weapons.primary_bank_weapons[check]].wi_flags2 & WIF2_BALLISTIC)
15696 		{
15697 			ammo_left = shipp->weapons.primary_bank_ammo[check] ;
15698 		}
15699 	}
15700 
15701 	// return
15702 	return ammo_left;
15703 }
15704 
15705 
15706 // Karajorma - sets the amount of ammo in a certain ballistic weapon bank to the specified value
sexp_set_primary_ammo(int node)15707 void sexp_set_primary_ammo (int node)
15708 {
15709 	int sindex;
15710 	int requested_bank ;
15711 	int requested_weapons ;
15712 	int rearm_limit = 0;
15713 
15714 	// Check that a ship has been supplied
15715 	sindex = ship_name_lookup(CTEXT(node));
15716 	if (sindex < 0)
15717 	{
15718 		return ;
15719 	}
15720 
15721 	// Get the bank to set the number on
15722 	requested_bank = eval_num(CDR(node));
15723 	if (requested_bank < 0)
15724 	{
15725 		return ;
15726 	}
15727 
15728 	//  Get the number of weapons requested
15729 	node = CDR(node);
15730 	requested_weapons = eval_num(CDR(node));
15731 	if (requested_weapons < 0)
15732 	{
15733 		return ;
15734 	}
15735 
15736 	node = CDDR(node);
15737 
15738 	// If a rearm limit hasn't been specified simply change the ammo.Otherwise read in the rearm limit
15739 	if (node < 0) {
15740 		set_primary_ammo (sindex, requested_bank, requested_weapons);
15741 	}
15742 	else {
15743 		rearm_limit = eval_num(node);
15744 		set_primary_ammo (sindex, requested_bank, requested_weapons, rearm_limit);
15745 	}
15746 }
15747 
15748 //Karajorma - Helper function for the set-primary-ammo and weapon functions
set_primary_ammo(int ship_index,int requested_bank,int requested_ammo,int rearm_limit,bool update)15749 void set_primary_ammo (int ship_index, int requested_bank, int requested_ammo, int rearm_limit, bool update)
15750 {
15751 	ship *shipp;
15752 	int maximum_allowed ;
15753 
15754 	// Check that it's valid
15755 	if((Ships[ship_index].objnum < 0) || (Ships[ship_index].objnum >= MAX_OBJECTS)){
15756 		return ;
15757 	}
15758 	shipp = &Ships[ship_index];
15759 
15760 	// Can only set one bank at a time. Check that someone hasn't asked for the contents of all
15761 	// the banks or a non-existant bank
15762 	if ((requested_bank > shipp->weapons.num_primary_banks) || requested_bank < 0)
15763 	{
15764 		return ;
15765 	}
15766 
15767 	// Check that this isn't a non-ballistic bank as it's pointless to set the amount of ammo for those
15768 	if (!(Weapon_info[shipp->weapons.primary_bank_weapons[requested_bank]].wi_flags2 & WIF2_BALLISTIC))
15769 	{
15770 		return ;
15771 	}
15772 
15773 	//Check that a valid number of weapons have been specified.
15774 	if (requested_ammo < 0)
15775 	{
15776 		return ;
15777 	}
15778 
15779 	// Is the number requested larger than the maximum allowed for that particular bank?
15780 	maximum_allowed = fl2i((Ship_info[shipp->ship_info_index].primary_bank_ammo_capacity[requested_bank]
15781 		/ Weapon_info[shipp->weapons.primary_bank_weapons[requested_bank]].cargo_size)+0.5);
15782 	if (maximum_allowed < requested_ammo)
15783 	{
15784 		requested_ammo = maximum_allowed ;
15785 	}
15786 
15787 	// Set the number of weapons
15788 	shipp->weapons.primary_bank_ammo[requested_bank] = requested_ammo ;
15789 
15790 	// If a rearm limit has been specified set it.
15791 	if (rearm_limit >= 0)
15792 	{
15793 		// Don't allow more weapons than the bank can actually hold.
15794 		if (rearm_limit > maximum_allowed)
15795 		{
15796 			rearm_limit = maximum_allowed;
15797 		}
15798 
15799 		shipp->weapons.primary_bank_start_ammo[requested_bank] = rearm_limit;
15800 	}
15801 
15802 	// The change needs to be passed on if this is a multiplayer game
15803 	if (MULTIPLAYER_MASTER & update)
15804 	{
15805 		send_weapon_or_ammo_changed_packet(ship_index, 0, requested_bank, requested_ammo, rearm_limit, -1);
15806 	}
15807 }
15808 
15809 // Karajorma - returns the number of missiles left in an ammo bank. Unlike secondary_ammo_pct
15810 // it does this as a numerical value rather than as a percentage of the maximum
sexp_get_secondary_ammo(int node)15811 int sexp_get_secondary_ammo (int node)
15812 {
15813 	int ammo_left = 0 ;
15814 	ship *shipp ;
15815 	int sindex ;
15816 	int check ;
15817 
15818 	// Get the ship
15819 	sindex = ship_name_lookup(CTEXT(node));
15820 	if (sindex < 0)
15821 	{
15822 		return 0;
15823 	}
15824 	if((Ships[sindex].objnum < 0) || (Ships[sindex].objnum >= MAX_OBJECTS)){
15825 		return 0;
15826 	}
15827 	shipp = &Ships[sindex];
15828 
15829 	// bank to check
15830 	check = eval_num(CDR(node));
15831 
15832 	// bogus check?
15833 	if(check < 0){
15834 		return 0;
15835 	}
15836 
15837 	// Are we looking at the number of secondaries in all banks?
15838 	if(check > shipp->weapons.num_secondary_banks)
15839 	{
15840 		for(int bank=0; bank<shipp->weapons.num_secondary_banks; bank++)
15841 		{
15842 			ammo_left += shipp->weapons.secondary_bank_ammo[bank] ;
15843 		}
15844 	}
15845 	// If not return the value for the bank requested.
15846 	else
15847 	{
15848 		ammo_left = shipp->weapons.secondary_bank_ammo[check] ;
15849 	}
15850 
15851 	return ammo_left ;
15852 }
15853 
15854 // Karajorma - sets the amount of ammo in a certain weapon bank to the specified value
sexp_set_secondary_ammo(int node)15855 void sexp_set_secondary_ammo (int node)
15856 {
15857 	int sindex;
15858 	int requested_bank;
15859 	int requested_weapons;
15860 	int rearm_limit;
15861 
15862 	// Check that a ship has been supplied
15863 	sindex = ship_name_lookup(CTEXT(node));
15864 	if (sindex < 0)
15865 	{
15866 		return ;
15867 	}
15868 
15869 	// Get the bank to set the number on
15870 	requested_bank = eval_num(CDR(node));
15871 	if (requested_bank < 0)
15872 	{
15873 		return ;
15874 	}
15875 
15876 	//  Get the number of weapons requested
15877 	node = CDR(node);
15878 	requested_weapons = eval_num(CDR(node));
15879 	if (requested_weapons < 0)
15880 	{
15881 		return ;
15882 	}
15883 
15884 	node = CDDR(node);
15885 
15886 	// If a rearm limit hasn't been specified simply change the ammo.Otherwise read in the rearm limit
15887 	if (node < 0) {
15888 		set_secondary_ammo(sindex, requested_bank, requested_weapons);
15889 	}
15890 	else {
15891 		rearm_limit = eval_num(node);
15892 		set_secondary_ammo(sindex, requested_bank, requested_weapons, rearm_limit);
15893 	}
15894 }
15895 
15896 //Karajorma - Helper function for the set-secondary-ammo and weapon functions
set_secondary_ammo(int ship_index,int requested_bank,int requested_ammo,int rearm_limit,bool update)15897 void set_secondary_ammo (int ship_index, int requested_bank, int requested_ammo, int rearm_limit, bool update)
15898 {
15899 	ship *shipp;
15900 	int maximum_allowed;
15901 	// Check that it's valid
15902 	if((Ships[ship_index].objnum < 0) || (Ships[ship_index].objnum >= MAX_OBJECTS)){
15903 		return ;
15904 	}
15905 	shipp = &Ships[ship_index];
15906 
15907 	// Can only set one bank at a time. Check that someone hasn't asked for the contents of all
15908 	// the banks or a non-existant bank
15909 	if ((requested_bank > shipp->weapons.num_secondary_banks) || requested_bank < 0)
15910 	{
15911 		return ;
15912 	}
15913 
15914 	if (requested_ammo < 0)
15915 	{
15916 		return ;
15917 	}
15918 
15919 	// Is the number requested larger than the maximum allowed for that particular bank?
15920 	maximum_allowed = fl2i(shipp->weapons.secondary_bank_capacity[requested_bank]
15921 		/ Weapon_info[shipp->weapons.secondary_bank_weapons[requested_bank]].cargo_size);
15922 	if (maximum_allowed < requested_ammo)
15923 	{
15924 		requested_ammo = maximum_allowed ;
15925 	}
15926 
15927 	// Set the number of weapons
15928 	shipp->weapons.secondary_bank_ammo[requested_bank] = requested_ammo ;
15929 
15930 	// If a rearm limit has been specified set it.
15931 	if (rearm_limit >= 0)
15932 	{
15933 		// Don't allow more weapons than the bank can actually hold.
15934 		if (rearm_limit > maximum_allowed)
15935 		{
15936 			rearm_limit = maximum_allowed;
15937 		}
15938 
15939 		shipp->weapons.secondary_bank_start_ammo[requested_bank] = rearm_limit;
15940 	}
15941 
15942 	// The change needs to be passed on if this is a multiplayer game and we're not already updating the weapon
15943 	if (MULTIPLAYER_MASTER && update)
15944 	{
15945 		send_weapon_or_ammo_changed_packet(ship_index, 1, requested_bank, requested_ammo, rearm_limit, -1);
15946 	}
15947 }
15948 
15949 // Karajorma - Changes the weapon in the requested bank to the one supplied. Optionally sets the ammo and
15950 // rearm limit too.
sexp_set_weapon(int node,bool primary)15951 void sexp_set_weapon (int node, bool primary)
15952 {
15953 	ship *shipp;
15954 	int sindex, requested_bank, windex, requested_ammo=-1, rearm_limit=-1;
15955 
15956 	Assert (node != -1);
15957 
15958 	// Check that a ship has been supplied
15959 	sindex = ship_name_lookup(CTEXT(node));
15960 	if (sindex < 0)
15961 	{
15962 		return ;
15963 	}
15964 
15965 	// Check that it's valid
15966 	if((Ships[sindex].objnum < 0) || (Ships[sindex].objnum >= MAX_OBJECTS)){
15967 		return ;
15968 	}
15969 	shipp = &Ships[sindex];
15970 
15971 	// Get the bank to change the weapon of
15972 	requested_bank = eval_num(CDR(node));
15973 
15974 	// Skip to the node holding the weapon name
15975 	node = CDDR(node);
15976 
15977 	windex = weapon_info_lookup(CTEXT(node));
15978 	if (windex < 0)
15979 	{
15980 		return;
15981 	}
15982 
15983 	if (primary)
15984 	{
15985 		// Change the weapon
15986 		shipp->weapons.primary_bank_weapons[requested_bank] = windex ;
15987 	}
15988 	else
15989 	{
15990 		// Change the weapon
15991 		shipp->weapons.secondary_bank_weapons[requested_bank] = windex ;
15992 	}
15993 
15994 
15995 	// Check to see if the optional ammo and rearm_limit settings were supplied
15996 	node = CDR(node);
15997 	if (node >= 0) {
15998 		requested_ammo = eval_num(node);
15999 
16000 		if (requested_ammo < 0) {
16001 			requested_ammo = 0;
16002 		}
16003 		node = CDR(node);
16004 
16005 		// If nothing was supplied then set the rearm limit to a negative value so that it is ignored
16006 		if (node < 0) {
16007 			rearm_limit = -1;
16008 		}
16009 		else {
16010 			rearm_limit = eval_num(node);
16011 		}
16012 
16013 		// Set the ammo but do not send an ammo changed update packet as we'll do that later from here
16014 		if (primary) {
16015 			set_primary_ammo (sindex, requested_bank, requested_ammo, rearm_limit, false);
16016 		}
16017 		else {
16018 			set_secondary_ammo (sindex, requested_bank, requested_ammo, rearm_limit, false);
16019 		}
16020 	}
16021 	else {
16022 		// read the data
16023 		if (primary) {
16024 			requested_ammo = shipp->weapons.primary_bank_ammo[requested_bank];
16025 			rearm_limit = shipp->weapons.primary_bank_start_ammo[requested_bank];
16026 		}
16027 		else {
16028 			requested_ammo = shipp->weapons.secondary_bank_ammo[requested_bank];
16029 			rearm_limit = shipp->weapons.secondary_bank_start_ammo[requested_bank];
16030 		}
16031 	}
16032 
16033 	// Now pass this info on to clients if need be.
16034 	if (MULTIPLAYER_MASTER)
16035 	{
16036 		if (primary) {
16037 			send_weapon_or_ammo_changed_packet(sindex, 0, requested_bank, requested_ammo, rearm_limit, windex);
16038 		}
16039 		else {
16040 			send_weapon_or_ammo_changed_packet(sindex, 1, requested_bank, requested_ammo, rearm_limit, windex);
16041 		}
16042 	}
16043 }
16044 
sexp_get_countermeasures(int node)16045 int sexp_get_countermeasures(int node)
16046 {
16047 	ship *shipp;
16048 
16049 	shipp = sexp_get_ship_from_node(node);
16050 
16051 	if (shipp !=NULL) {
16052 		return shipp->cmeasure_count;
16053 	}
16054 	else {
16055 		return SEXP_NAN;
16056 	}
16057 }
16058 
sexp_set_countermeasures(int node)16059 void sexp_set_countermeasures(int node)
16060 {
16061 	ship *shipp;
16062 	int num_cmeasures;
16063 
16064 	shipp = sexp_get_ship_from_node(node);
16065 
16066 	if (shipp == NULL) {
16067 		return;
16068 	}
16069 	node = CDR(node);
16070 	num_cmeasures = eval_num(node);
16071 	if (num_cmeasures < 0) {
16072 		num_cmeasures = 0;
16073 	}
16074 	else if (num_cmeasures > Ship_info[shipp->ship_info_index].cmeasure_max) {
16075 		num_cmeasures = Ship_info[shipp->ship_info_index].cmeasure_max;
16076 	}
16077 
16078 	shipp->cmeasure_count = num_cmeasures;
16079 
16080 	multi_start_callback();
16081 	multi_send_ship(shipp);
16082 	multi_send_int(num_cmeasures);
16083 	multi_end_callback();
16084 }
16085 
multi_sexp_set_countermeasures()16086 void multi_sexp_set_countermeasures()
16087 {
16088 	int num_cmeasures = 0;
16089 	ship *shipp;
16090 
16091 	multi_get_ship(shipp);
16092 	if (shipp == NULL) {
16093 		return;
16094 	}
16095 	if (multi_get_int(num_cmeasures)) {
16096 		shipp->cmeasure_count = num_cmeasures;
16097 	}
16098 }
16099 
16100 // KeldorKatarn - Locks or unlocks the afterburner on the requested ship
sexp_deal_with_afterburner_lock(int node,bool lock)16101 void sexp_deal_with_afterburner_lock (int node, bool lock)
16102 {
16103 	Assert (node != -1);
16104 	sexp_deal_with_ship_flag(node, true, 0, 0, 0, SF2_AFTERBURNER_LOCKED, 0, 0, (lock ? 1:0), true);
16105 }
16106 
16107 // Karajorma - locks or unlocks primary weapons on the requested ship
sexp_deal_with_primary_lock(int node,bool lock)16108 void sexp_deal_with_primary_lock (int node, bool lock)
16109 {
16110 	Assert (node != -1);
16111 	sexp_deal_with_ship_flag(node, true, 0, 0, 0, SF2_PRIMARIES_LOCKED, 0, P2_SF2_PRIMARIES_LOCKED, (lock ? 1:0), true);
16112 
16113 }
16114 
sexp_deal_with_secondary_lock(int node,bool lock)16115 void sexp_deal_with_secondary_lock (int node, bool lock)
16116 {
16117 	Assert (node != -1);
16118 	sexp_deal_with_ship_flag(node, true, 0, 0, 0, SF2_SECONDARIES_LOCKED, 0, P2_SF2_SECONDARIES_LOCKED, (lock ? 1:0), true);
16119 
16120 }
16121 
16122 //Karajorma - Changes the subsystem name displayed on the HUD.
sexp_change_subsystem_name(int node)16123 void sexp_change_subsystem_name(int node)
16124 {
16125 	ship *shipp;
16126 	int ship_index;
16127 	char *new_name;
16128 	ship_subsys *subsystem_to_rename;
16129 
16130 	Assert (node != -1);
16131 
16132 	// Check that a ship has been supplied
16133 	ship_index = ship_name_lookup(CTEXT(node));
16134 	if (ship_index < 0) {
16135 		return;
16136 	}
16137 
16138 	shipp = &Ships[ship_index];
16139 	node = CDR(node);
16140 
16141 	if (node < 0) {
16142 		return;
16143 	}
16144 
16145 	new_name = CTEXT(node);
16146 	node = CDR(node);
16147 
16148 	if (MULTIPLAYER_MASTER) {
16149 		multi_start_callback();
16150 		multi_send_ship(shipp);
16151 		multi_send_string(new_name);
16152 	}
16153 
16154 	// loop through all the subsystems the SEXP has provided
16155 	while (node >= 0) {
16156 
16157 		//Get the new subsystem name
16158 		subsystem_to_rename = ship_get_subsys(&Ships[ship_index], CTEXT(node));
16159 		if (subsystem_to_rename != NULL) {
16160 			ship_subsys_set_name(subsystem_to_rename, new_name);
16161 
16162 			if (MULTIPLAYER_MASTER) {
16163 				multi_send_string(CTEXT(node));
16164 			}
16165 		}
16166 
16167 		node = CDR(node);
16168 	}
16169 
16170 	if (MULTIPLAYER_MASTER) {
16171 		multi_end_callback();
16172 	}
16173 }
16174 
multi_sexp_change_subsystem_name()16175 void multi_sexp_change_subsystem_name()
16176 {
16177 	ship *shipp = NULL;
16178 	char new_name[TOKEN_LENGTH];
16179 	char subsys_name[MAX_NAME_LEN];
16180 	ship_subsys *subsystem_to_rename;
16181 
16182 	multi_get_ship(shipp);
16183 	multi_get_string(new_name);
16184 	while (multi_get_string(subsys_name)) {
16185 		subsystem_to_rename = ship_get_subsys(shipp, subsys_name);
16186 		if (subsystem_to_rename != NULL) {
16187 			ship_subsys_set_name(subsystem_to_rename, new_name);
16188 		}
16189 	}
16190 }
16191 
16192 // Goober5000
sexp_change_ship_class(int n)16193 void sexp_change_ship_class(int n)
16194 {
16195 	int ship_num, class_num = ship_info_lookup(CTEXT(n));
16196 	Assert(class_num != -1);
16197 
16198 	n = CDR(n);
16199 
16200 	if (MULTIPLAYER_MASTER) {
16201 		multi_start_callback();
16202 		multi_send_int(class_num);
16203 	}
16204 
16205 	// all ships in the sexp
16206 	for ( ; n != -1; n = CDR(n))
16207 	{
16208 		ship_num = ship_name_lookup(CTEXT(n), 1);
16209 
16210 		// If the ship hasn't arrived we still want the ability to change its class.
16211 		if (ship_num == -1)
16212 		{
16213 			// Get the name of the ship that we are interested in
16214 			char* ship_name = CTEXT(n);
16215 			p_object *pobj ;
16216 			bool match_found = false;
16217 
16218 
16219 			// Search the Ship_arrival_list to see if the ship is waiting to arrive
16220 			for (pobj = GET_FIRST(&Ship_arrival_list); pobj != END_OF_LIST(&Ship_arrival_list); pobj = GET_NEXT(pobj))
16221 			{
16222 				if (!(strcmp(pobj->name, ship_name)))
16223 				{
16224 					match_found = true;
16225 					break;
16226 				}
16227 			}
16228 
16229 			if (match_found)
16230 			{
16231 				swap_parse_object(pobj, class_num);
16232 
16233 				if (MULTIPLAYER_MASTER) {
16234 					multi_send_bool(false);
16235 					multi_send_parse_object(pobj);
16236 				}
16237 			}
16238 		}
16239 		// If the ship is already in the mission
16240 		else
16241 		{
16242 			// don't mess with a ship that's occupied
16243 			if (!(Ships[ship_num].flags & (SF_DYING | SF_ARRIVING | SF_DEPARTING)))
16244 			{
16245 				change_ship_type(ship_num, class_num, 1);
16246 				if (&Ships[ship_num] == Player_ship) {
16247 					set_current_hud();
16248 				}
16249 
16250 				if (MULTIPLAYER_MASTER) {
16251 					multi_send_bool(true);
16252 					multi_send_ship(ship_num);
16253 				}
16254 			}
16255 		}
16256 	}
16257 
16258 	if (MULTIPLAYER_MASTER) {
16259 		multi_end_callback();
16260 	}
16261 }
16262 
multi_sexp_change_ship_class()16263 void multi_sexp_change_ship_class()
16264 {
16265 	int class_num = -1;
16266 	int ship_num = -1;
16267 	bool ship_arrived;
16268 	p_object *pobjp = NULL;
16269 
16270 	multi_get_int(class_num);
16271 	while (multi_get_bool(ship_arrived)) {
16272 		if (ship_arrived) {
16273 			multi_get_ship(ship_num);
16274 			if ((class_num >= 0) && (ship_num >= 0)) {
16275 				change_ship_type(ship_num, class_num, 1);
16276 				if (&Ships[ship_num] == Player_ship) {
16277 					set_current_hud();
16278 				}
16279 			}
16280 		}
16281 		else {
16282 			multi_get_parse_object(pobjp);
16283 			if ((class_num >= 0) && (pobjp != NULL)) {
16284 				swap_parse_object(pobjp, class_num);
16285 			}
16286 		}
16287 	}
16288 }
16289 
16290 // Goober5000
ship_copy_damage(ship * target_shipp,ship * source_shipp)16291 void ship_copy_damage(ship *target_shipp, ship *source_shipp)
16292 {
16293 	int i;
16294 	object *target_objp = &Objects[target_shipp->objnum];
16295 	object *source_objp = &Objects[source_shipp->objnum];
16296 	ship_subsys *source_ss;
16297 	ship_subsys *target_ss;
16298 
16299 	if (target_shipp->ship_info_index != source_shipp->ship_info_index)
16300 	{
16301 		nprintf(("SEXP", "Copying damage of ship %s to ship %s which has a different ship class.  Strange results might occur.\n", source_shipp->ship_name, target_shipp->ship_name));
16302 	}
16303 
16304 
16305 	// copy hull...
16306 	target_shipp->special_hitpoints = source_shipp->special_hitpoints;
16307 	target_shipp->ship_max_hull_strength = source_shipp->ship_max_hull_strength;
16308 	target_objp->hull_strength = source_objp->hull_strength;
16309 
16310 	// ...and shields
16311 	target_shipp->ship_max_shield_strength = source_shipp->ship_max_shield_strength;
16312 	for (i = 0; i < MIN(target_objp->n_quadrants, source_objp->n_quadrants); i++)
16313 		target_objp->shield_quadrant[i] = source_objp->shield_quadrant[i];
16314 
16315 
16316 	// search through all subsystems on source ship and map them onto target ship
16317 	for (source_ss = GET_FIRST(&source_shipp->subsys_list); source_ss != GET_LAST(&source_shipp->subsys_list); source_ss = GET_NEXT(source_ss))
16318 	{
16319 		// find subsystem to configure
16320 		target_ss = ship_get_subsys(target_shipp, source_ss->system_info->subobj_name);
16321 		if (target_ss == NULL)
16322 			continue;
16323 
16324 		// copy
16325 		target_ss->max_hits = source_ss->max_hits;
16326 		target_ss->current_hits = source_ss->current_hits;
16327 		target_ss->submodel_info_1.blown_off = source_ss->submodel_info_1.blown_off;
16328 		target_ss->submodel_info_2.blown_off = source_ss->submodel_info_2.blown_off;
16329 	}
16330 }
16331 
16332 extern int insert_subsys_status(p_object *pobjp);
16333 
16334 // Goober5000
parse_copy_damage(p_object * target_pobjp,ship * source_shipp)16335 void parse_copy_damage(p_object *target_pobjp, ship *source_shipp)
16336 {
16337 	object *source_objp = &Objects[source_shipp->objnum];
16338 	ship_subsys *source_ss;
16339 	subsys_status *target_sssp;
16340 
16341 	if (target_pobjp->ship_class != source_shipp->ship_info_index)
16342 	{
16343 		nprintf(("SEXP", "Copying damage of ship %s to ship %s which has a different ship class.  Strange results might occur.\n", source_shipp->ship_name, target_pobjp->name));
16344 	}
16345 
16346 	// copy hull...
16347 	target_pobjp->special_hitpoints = source_shipp->special_hitpoints;
16348 	target_pobjp->ship_max_hull_strength = source_shipp->ship_max_hull_strength;
16349 	target_pobjp->initial_hull = fl2i(get_hull_pct(source_objp) * 100.0f);
16350 
16351 	// ...and shields
16352 	target_pobjp->ship_max_shield_strength = source_shipp->ship_max_shield_strength;
16353 	target_pobjp->initial_shields = fl2i(get_shield_pct(source_objp) * 100.0f);
16354 
16355 
16356 	// search through all subsystems on source ship and map them onto target ship
16357 	for (source_ss = GET_FIRST(&source_shipp->subsys_list); source_ss != GET_LAST(&source_shipp->subsys_list); source_ss = GET_NEXT(source_ss))
16358 	{
16359 		// find subsystem to configure
16360 		target_sssp = parse_get_subsys_status(target_pobjp, source_ss->system_info->subobj_name);
16361 
16362 		// gak... none allocated; we need to allocate one!
16363 		if (target_sssp == NULL)
16364 		{
16365 			// jam in the new subsystem at the end of the existing list for this parse object
16366 			int new_idx = insert_subsys_status(target_pobjp);
16367 			target_sssp = &Subsys_status[new_idx];
16368 
16369 			strcpy_s(target_sssp->name, source_ss->system_info->subobj_name);
16370 		}
16371 
16372 		// copy
16373 		if (source_ss->max_hits == 0.0f)
16374 		{
16375 			Warning(LOCATION, "Why does %s's subsystem %s have a maximum strength of 0?", source_shipp->ship_name, source_ss->system_info->subobj_name);
16376 			target_sssp->percent = 100.0f;
16377 		}
16378 		else
16379 		{
16380 			target_sssp->percent = 100.0f - (source_ss->current_hits / source_ss->max_hits) * 100.0f;
16381 		}
16382 	}
16383 }
16384 
16385 // Goober5000
sexp_ship_copy_damage(int node)16386 void sexp_ship_copy_damage(int node)
16387 {
16388 	int n, source_shipnum, target_shipnum;
16389 	p_object *target_pobjp;
16390 
16391 	// source ship must be present
16392 	source_shipnum = ship_name_lookup(CTEXT(node));
16393 	if (source_shipnum < 0)
16394 		return;
16395 
16396 	// loop through all subsequent arguments
16397 	for (n = CDR(node); n != -1; n = CDR(n))
16398 	{
16399 		// maybe it's present in-mission
16400 		target_shipnum = ship_name_lookup(CTEXT(n));
16401 		if (target_shipnum >= 0)
16402 		{
16403 			ship_copy_damage(&Ships[target_shipnum], &Ships[source_shipnum]);
16404 			continue;
16405 		}
16406 
16407 		// maybe it's on the arrival list
16408 		target_pobjp = mission_parse_get_arrival_ship(CTEXT(n));
16409 		if (target_pobjp != NULL)
16410 		{
16411 			parse_copy_damage(target_pobjp, &Ships[source_shipnum]);
16412 			continue;
16413 		}
16414 
16415 		// must have departed or not even existed... do nothing
16416 	}
16417 }
16418 
16419 
16420 //-Bobboau
sexp_activate_deactivate_glow_points(int n,bool activate)16421 void sexp_activate_deactivate_glow_points(int n, bool activate)
16422 {
16423 	int sindex;
16424 	size_t i;
16425 
16426 	for ( ; n != -1; n = CDR(n))
16427 	{
16428 		sindex = ship_name_lookup(CTEXT(n), 1);
16429 		if (sindex >= 0)
16430 		{
16431 			for (i = 0; i < Ships[sindex].glow_point_bank_active.size(); i++)
16432 				Ships[sindex].glow_point_bank_active[i] = activate;
16433 		}
16434 	}
16435 }
16436 
16437 //-Bobboau
sexp_activate_deactivate_glow_point_bank(int n,bool activate)16438 void sexp_activate_deactivate_glow_point_bank(int n, bool activate)
16439 {
16440 	int sindex, num;
16441 
16442 	sindex = ship_name_lookup(CTEXT(n), 1);
16443 	if (sindex >= 0)
16444 	{
16445 		for ( n = CDR(n); n != -1; n = CDR(n))
16446 		{
16447 			num = eval_num(n);
16448 			if (num >= 0 && num < (int)Ships[sindex].glow_point_bank_active.size())
16449 			{
16450 				Ships[sindex].glow_point_bank_active[num] = activate;
16451 			}
16452 		}
16453 	}
16454 }
16455 
16456 //-Bobboau
sexp_activate_deactivate_glow_maps(int n,int activate)16457 void sexp_activate_deactivate_glow_maps(int n, int activate)
16458 {
16459 	int sindex;
16460 	ship *shipp;
16461 
16462 	for ( ; n != -1; n = CDR(n))
16463 	{
16464 		sindex = ship_name_lookup(CTEXT(n), 1);
16465 		if (sindex >= 0)
16466 		{
16467 			shipp = &Ships[sindex];
16468 
16469 			if (activate)
16470 				shipp->flags2 &= ~SF2_GLOWMAPS_DISABLED;
16471 			else
16472 				shipp->flags2 |= SF2_GLOWMAPS_DISABLED;
16473 		}
16474 	}
16475 }
16476 
sexp_set_ambient_light(int node)16477 void sexp_set_ambient_light(int node)
16478 {
16479 	int red, green, blue;
16480 	int level = 0;
16481 
16482 	Assert(node > -1);
16483 	red = eval_num(node);
16484 	if (red < 0 || red > 255) {
16485 		red = 0;
16486 	}
16487 
16488 	node = CDR(node);
16489 	Assert(node > -1);
16490 	green = eval_num(node);
16491 	if (green < 0 || green > 255) {
16492 		green = 0;
16493 	}
16494 
16495 	node = CDR(node);
16496 	Assert(node > -1);
16497 	blue = eval_num(node);
16498 	if (blue < 0 || blue > 255) {
16499 		blue = 0;
16500 	}
16501 
16502 	level |= red;
16503 	level |= green << 8;
16504 	level |= blue << 16;
16505 
16506 	// setting the ambient light level for the mission won't actually do anything as it is parsed at mission
16507 	// start but it should be updated in case someone changes that later
16508 	The_mission.ambient_light_level = level;
16509 
16510 	// call the graphics function to actually set the level
16511 	gr_set_ambient_light(red, green, blue);
16512 
16513 	// do the multiplayer callback
16514 	if (MULTIPLAYER_MASTER) {
16515 		multi_start_callback();
16516 		multi_send_int(level);
16517 		multi_end_callback();
16518 	}
16519 }
16520 
multi_sexp_set_ambient_light()16521 void multi_sexp_set_ambient_light()
16522 {
16523 	int level = 0;
16524 	if (multi_get_int(level)) {
16525 		The_mission.ambient_light_level = level;
16526 		gr_set_ambient_light((level & 0xff),((level >> 8) & 0xff), ((level >> 16) & 0xff));
16527 	}
16528 }
16529 
sexp_set_post_effect(int node)16530 void sexp_set_post_effect(int node)
16531 {
16532 	char *name = CTEXT(node);
16533 
16534 	if (name == NULL) {
16535 		return;
16536 	}
16537 
16538 	int amount = eval_num(CDR(node));
16539 
16540 	if (amount < 0 || amount > 100)
16541 		amount = 0;
16542 
16543 	gr_post_process_set_effect(name, amount);
16544 }
16545 
16546 // Goober5000
sexp_set_skybox_orientation(int n)16547 void sexp_set_skybox_orientation(int n)
16548 {
16549 	matrix m;
16550 	angles a;
16551 
16552 	a.p = fl_radians(eval_num(n) % 360);
16553 	n = CDR(n);
16554 
16555 	a.b = fl_radians(eval_num(n) % 360);
16556 	n = CDR(n);
16557 
16558 	a.h = fl_radians(eval_num(n) % 360);
16559 	n = CDR(n);
16560 
16561 	vm_angles_2_matrix(&m, &a);
16562 	stars_set_background_orientation(&m);
16563 }
16564 
16565 // taylor - load and set a skybox model
sexp_set_skybox_model(int n)16566 void sexp_set_skybox_model(int n)
16567 {
16568 	if ( !stricmp("default", CTEXT(n)) ) {
16569 		stars_set_background_model( The_mission.skybox_model, NULL );
16570 	} else {
16571 		// stars_level_init() will set the actual mission skybox after this gets
16572 		// evaluated during parse. by setting it now we get everything loaded so
16573 		// there is less slowdown when it actually swaps out - taylor
16574 		stars_set_background_model( CTEXT(n), NULL );
16575 	}
16576 }
16577 
16578 // taylor - preload a skybox model.  this doesn't set anything as viewable, just loads it into memory
sexp_set_skybox_model_preload(char * name)16579 void sexp_set_skybox_model_preload(char *name)
16580 {
16581 	int i;
16582 
16583 	if ( !stricmp("default", name) ) {
16584 		// if there isn't a mission skybox model then don't load one
16585 		if ( strlen(The_mission.skybox_model) ) {
16586 			i = model_load( The_mission.skybox_model, 0, NULL );
16587 			model_page_in_textures( i );
16588 		}
16589 	} else {
16590 		i = model_load( name, 0, NULL );
16591 		model_page_in_textures( i );
16592 	}
16593 }
16594 
sexp_beam_fire(int node,bool at_coords)16595 void sexp_beam_fire(int node, bool at_coords)
16596 {
16597 	int sindex, n = node;
16598 	beam_fire_info fire_info;
16599 	int idx;
16600 
16601 	// zero stuff out
16602 	memset(&fire_info, 0, sizeof(beam_fire_info));
16603 	fire_info.accuracy = 0.000001f;							// this will guarantee a hit
16604 
16605 	// get the firing ship
16606 	sindex = ship_name_lookup(CTEXT(n));
16607 	n = CDR(n);
16608 	if (sindex < 0) {
16609 		return;
16610 	}
16611 	if (Ships[sindex].objnum < 0) {
16612 		return;
16613 	}
16614 	fire_info.shooter = &Objects[Ships[sindex].objnum];
16615 
16616 	// get the subsystem
16617 	fire_info.turret = ship_get_subsys(&Ships[sindex], CTEXT(n));
16618 	n = CDR(n);
16619 	if (fire_info.turret == NULL) {
16620 		return;
16621 	}
16622 
16623 	if (at_coords) {
16624 		// get the target coordinates
16625 		fire_info.target_pos1.xyz.x = fire_info.target_pos2.xyz.x = static_cast<float>(eval_num(n));
16626 		n = CDR(n);
16627 		fire_info.target_pos1.xyz.y = fire_info.target_pos2.xyz.y = static_cast<float>(eval_num(n));
16628 		n = CDR(n);
16629 		fire_info.target_pos1.xyz.z = fire_info.target_pos2.xyz.z = static_cast<float>(eval_num(n));
16630 		n = CDR(n);
16631 		fire_info.bfi_flags |= BFIF_TARGETING_COORDS;
16632 		fire_info.target = NULL;
16633 		fire_info.target_subsys = NULL;
16634 	} else {
16635 		// get the target
16636 		sindex = ship_name_lookup(CTEXT(n));
16637 		n = CDR(n);
16638 		if (sindex < 0) {
16639 			return;
16640 		}
16641 		if (Ships[sindex].objnum < 0) {
16642 			return;
16643 		}
16644 		fire_info.target = &Objects[Ships[sindex].objnum];
16645 
16646 		// see if the optional subsystem can be found
16647 		fire_info.target_subsys = NULL;
16648 		if (n >= 0) {
16649 			fire_info.target_subsys = ship_get_subsys(&Ships[sindex], CTEXT(n));
16650 			n = CDR(n);
16651 		}
16652 	}
16653 
16654 	// optionally force firing
16655 	if (n >= 0 && is_sexp_true(n)) {
16656 		fire_info.bfi_flags |= BFIF_FORCE_FIRING;
16657 		n = CDR(n);
16658 	}
16659 
16660 	// get the second set of coordinates
16661 	if (at_coords) {
16662 		if (n >= 0) {
16663 			fire_info.target_pos2.xyz.x = static_cast<float>(eval_num(n));
16664 			n = CDR(n);
16665 		}
16666 		if (n >= 0) {
16667 			fire_info.target_pos2.xyz.y = static_cast<float>(eval_num(n));
16668 			n = CDR(n);
16669 		}
16670 		if (n >= 0) {
16671 			fire_info.target_pos2.xyz.z = static_cast<float>(eval_num(n));
16672 			n = CDR(n);
16673 		}
16674 	}
16675 
16676 	// --- done getting arguments ---
16677 
16678 	// if it has no primary weapons
16679 	if (fire_info.turret->weapons.num_primary_banks <= 0) {
16680 		Warning(LOCATION, "Couldn't fire turret on ship %s; subsystem %s has no primary weapons", CTEXT(node), CTEXT(CDR(node)));
16681 		return;
16682 	}
16683 
16684 	// if the turret is destroyed
16685 	if (!(fire_info.bfi_flags & BFIF_FORCE_FIRING) && fire_info.turret->current_hits <= 0.0f) {
16686 		return;
16687 	}
16688 
16689 	// hmm, this could be wacky. Let's just simply select the first beam weapon in the turret
16690 	fire_info.beam_info_index = -1;
16691 	for (idx=0; idx<fire_info.turret->weapons.num_primary_banks; idx++) {
16692 		// store the weapon info index
16693 		if (Weapon_info[fire_info.turret->weapons.primary_bank_weapons[idx]].wi_flags & WIF_BEAM) {
16694 			fire_info.beam_info_index = fire_info.turret->weapons.primary_bank_weapons[idx];
16695 		}
16696 	}
16697 
16698 	// fire the beam
16699 	if (fire_info.beam_info_index != -1) {
16700 		beam_fire(&fire_info);
16701 	} else {
16702 		// it would appear the turret doesn't have any beam weapons
16703 		Warning(LOCATION, "Couldn't fire turret on ship %s; subsystem %s has no beam weapons", CTEXT(node), CTEXT(CDR(node)));
16704 	}
16705 }
16706 
sexp_beam_free(int node)16707 void sexp_beam_free(int node)
16708 {
16709 	int sindex;
16710 	ship_subsys *turret = NULL;
16711 
16712 	// get the firing ship
16713 	sindex = ship_name_lookup(CTEXT(node));
16714 	if(sindex < 0){
16715 		return;
16716 	}
16717 	if(Ships[sindex].objnum < 0){
16718 		return;
16719 	}
16720 
16721 	node = CDR(node);
16722 	for ( ; node >= 0; node = CDR(node) ) {
16723 		// get the subsystem
16724 		turret = ship_get_subsys(&Ships[sindex], CTEXT(node));
16725 		if(turret == NULL){
16726 			continue;
16727 		}
16728 
16729 		// flag it as beam free :)
16730 		if (!(turret->weapons.flags & SW_FLAG_BEAM_FREE))
16731 		{
16732 			turret->weapons.flags |= SW_FLAG_BEAM_FREE;
16733 			turret->turret_next_fire_stamp = timestamp((int) frand_range(50.0f, 4000.0f));
16734 		}
16735 	}
16736 }
16737 
sexp_set_thrusters(int node)16738 void sexp_set_thrusters(int node)
16739 {
16740 	bool activate = is_sexp_true(node) > 0;
16741 	node = CDR(node);
16742 
16743 	for(; node >= 0; node = CDR(node)) {
16744 		int sindex = ship_name_lookup(CTEXT(node));
16745 
16746 		if (sindex < 0) {
16747 			continue;
16748 		}
16749 
16750 		if (Ships[sindex].objnum < 0) {
16751 			continue;
16752 		}
16753 
16754 		if (activate)
16755 			Ships[sindex].flags2 &= ~SF2_NO_THRUSTERS;
16756 		else
16757 			Ships[sindex].flags2 |= SF2_NO_THRUSTERS;
16758 	}
16759 }
16760 
sexp_beam_free_all(int node)16761 void sexp_beam_free_all(int node)
16762 {
16763 	ship_subsys *subsys;
16764 	int sindex;
16765 
16766 	for (int n = node; n >= 0; n = CDR(n)) {
16767 		// get the firing ship
16768 		sindex = ship_name_lookup( CTEXT(n) );
16769 
16770 		if (sindex < 0) {
16771 			continue;
16772 		}
16773 
16774 		if (Ships[sindex].objnum < 0) {
16775 			continue;
16776 		}
16777 
16778 		// free all beam weapons
16779 		subsys = GET_FIRST(&Ships[sindex].subsys_list);
16780 
16781 		while ( subsys != END_OF_LIST(&Ships[sindex].subsys_list) ) {
16782 			// just mark all turrets as beam free
16783 			if ((subsys->system_info->type == SUBSYSTEM_TURRET) && (!(subsys->weapons.flags & SW_FLAG_BEAM_FREE)))
16784 			{
16785 				subsys->weapons.flags |= SW_FLAG_BEAM_FREE;
16786 				subsys->turret_next_fire_stamp = timestamp((int) frand_range(50.0f, 4000.0f));
16787 			}
16788 
16789 			// next item
16790 			subsys = GET_NEXT(subsys);
16791 		}
16792 	}
16793 }
16794 
sexp_beam_lock(int node)16795 void sexp_beam_lock(int node)
16796 {
16797 	int sindex;
16798 	ship_subsys *turret = NULL;
16799 
16800 	// get the firing ship
16801 	sindex = ship_name_lookup(CTEXT(node));
16802 	if(sindex < 0){
16803 		return;
16804 	}
16805 	if(Ships[sindex].objnum < 0){
16806 		return;
16807 	}
16808 
16809 	node = CDR(node);
16810 	for ( ; node >= 0; node = CDR(node) ) {
16811 		// get the subsystem
16812 		turret = ship_get_subsys(&Ships[sindex], CTEXT(node));
16813 		if(turret == NULL){
16814 			continue;
16815 		}
16816 
16817 		// flag it as not beam free
16818 		turret->weapons.flags &= ~(SW_FLAG_BEAM_FREE);
16819 	}
16820 }
16821 
sexp_beam_lock_all(int node)16822 void sexp_beam_lock_all(int node)
16823 {
16824 	ship_subsys *subsys;
16825 	int sindex;
16826 
16827 	for (int n = node; n >= 0; n = CDR(n)) {
16828 		// get the firing ship
16829 		sindex = ship_name_lookup( CTEXT(n) );
16830 
16831 		if (sindex < 0) {
16832 			continue;
16833 		}
16834 
16835 		if (Ships[sindex].objnum < 0) {
16836 			continue;
16837 		}
16838 
16839 		// lock all beam weapons
16840 		subsys = GET_FIRST(&Ships[sindex].subsys_list);
16841 
16842 		while ( subsys != END_OF_LIST(&Ships[sindex].subsys_list) ) {
16843 			// just mark all turrets as not beam free
16844 			if (subsys->system_info->type == SUBSYSTEM_TURRET) {
16845 				subsys->weapons.flags &= ~(SW_FLAG_BEAM_FREE);
16846 			}
16847 
16848 			// next item
16849 			subsys = GET_NEXT(subsys);
16850 		}
16851 	}
16852 }
16853 
sexp_turret_free(int node)16854 void sexp_turret_free(int node)
16855 {
16856 	int sindex;
16857 	ship_subsys *turret = NULL;
16858 
16859 	// get the firing ship
16860 	sindex = ship_name_lookup(CTEXT(node));
16861 	if(sindex < 0){
16862 		return;
16863 	}
16864 	if(Ships[sindex].objnum < 0){
16865 		return;
16866 	}
16867 
16868 	node = CDR(node);
16869 	for ( ; node >= 0; node = CDR(node) ) {
16870 		// get the subsystem
16871 		turret = ship_get_subsys(&Ships[sindex], CTEXT(node));
16872 		if(turret == NULL){
16873 			continue;
16874 		}
16875 
16876 		// flag turret as no longer locked :)
16877 		if (turret->weapons.flags & SW_FLAG_TURRET_LOCK)
16878 		{
16879 			turret->weapons.flags &= (~SW_FLAG_TURRET_LOCK);
16880 			turret->turret_next_fire_stamp = timestamp((int) frand_range(50.0f, 4000.0f));
16881 		}
16882 	}
16883 }
16884 
sexp_turret_free_all(int node)16885 void sexp_turret_free_all(int node)
16886 {
16887 	ship_subsys *subsys;
16888 	int sindex;
16889 
16890 	for (int n = node; n >= 0; n = CDR(n)) {
16891 		// get the firing ship
16892 		sindex = ship_name_lookup( CTEXT(n) );
16893 
16894 		if (sindex < 0) {
16895 			continue;
16896 		}
16897 
16898 		if (Ships[sindex].objnum < 0) {
16899 			continue;
16900 		}
16901 
16902 		// free all turrets
16903 		subsys = GET_FIRST(&Ships[sindex].subsys_list);
16904 
16905 		while ( subsys != END_OF_LIST(&Ships[sindex].subsys_list) ) {
16906 			// just mark all turrets as free
16907 			if ((subsys->system_info->type == SUBSYSTEM_TURRET) && (subsys->weapons.flags & SW_FLAG_TURRET_LOCK)) {
16908 				subsys->weapons.flags &= (~SW_FLAG_TURRET_LOCK);
16909 				subsys->turret_next_fire_stamp = timestamp((int) frand_range(50.0f, 4000.0f));
16910 			}
16911 
16912 			// next item
16913 			subsys = GET_NEXT(subsys);
16914 		}
16915 	}
16916 }
16917 
sexp_turret_lock(int node)16918 void sexp_turret_lock(int node)
16919 {
16920 	int sindex;
16921 	ship_subsys *turret = NULL;
16922 
16923 	// get the firing ship
16924 	sindex = ship_name_lookup(CTEXT(node));
16925 	if(sindex < 0){
16926 		return;
16927 	}
16928 	if(Ships[sindex].objnum < 0){
16929 		return;
16930 	}
16931 
16932 	node = CDR(node);
16933 	for ( ; node >= 0; node = CDR(node) ) {
16934 		// get the subsystem
16935 		turret = ship_get_subsys(&Ships[sindex], CTEXT(node));
16936 		if(turret == NULL){
16937 			continue;
16938 		}
16939 
16940 		// flag turret as locked
16941 		turret->weapons.flags |= SW_FLAG_TURRET_LOCK;
16942 	}
16943 }
16944 
sexp_turret_lock_all(int node)16945 void sexp_turret_lock_all(int node)
16946 {
16947 	ship_subsys *subsys;
16948 	int sindex;
16949 
16950 	for (int n = node; n >= 0; n = CDR(n)) {
16951 		// get the firing ship
16952 		sindex = ship_name_lookup( CTEXT(n) );
16953 
16954 		if (sindex < 0) {
16955 			continue;
16956 		}
16957 
16958 		if (Ships[sindex].objnum < 0) {
16959 			continue;
16960 		}
16961 
16962 		// lock all turrets
16963 		subsys = GET_FIRST(&Ships[sindex].subsys_list);
16964 
16965 		while ( subsys != END_OF_LIST(&Ships[sindex].subsys_list) ) {
16966 			// just mark all turrets as locked
16967 			if (subsys->system_info->type == SUBSYSTEM_TURRET) {
16968 				subsys->weapons.flags |= SW_FLAG_TURRET_LOCK;
16969 			}
16970 
16971 			// next item
16972 			subsys = GET_NEXT(subsys);
16973 		}
16974 	}
16975 }
16976 
sexp_turret_tagged_only_all(int node)16977 void sexp_turret_tagged_only_all(int node)
16978 {
16979 	ship_subsys *subsys;
16980 	int sindex;
16981 
16982 	// get the firing ship
16983 	sindex = ship_name_lookup(CTEXT(node));
16984 	if(sindex < 0){
16985 		return;
16986 	}
16987 	if(Ships[sindex].objnum < 0){
16988 		return;
16989 	}
16990 
16991 	// mark all turrets to only target tagged ships
16992 	subsys = GET_FIRST(&Ships[sindex].subsys_list);
16993 	while(subsys != END_OF_LIST(&Ships[sindex].subsys_list)){
16994 		// just mark all turrets as locked
16995 		if(subsys->system_info->type == SUBSYSTEM_TURRET){
16996 			subsys->weapons.flags |= SW_FLAG_TAGGED_ONLY;
16997 		}
16998 
16999 		// next item
17000 		subsys = GET_NEXT(subsys);
17001 	}
17002 }
17003 
sexp_turret_tagged_clear_all(int node)17004 void sexp_turret_tagged_clear_all(int node)
17005 {
17006 	ship_subsys *subsys;
17007 	int sindex;
17008 
17009 	// get the firing ship
17010 	sindex = ship_name_lookup(CTEXT(node));
17011 	if(sindex < 0){
17012 		return;
17013 	}
17014 	if(Ships[sindex].objnum < 0){
17015 		return;
17016 	}
17017 
17018 	// mark all turrets so not restricted to only tagged ships
17019 	subsys = GET_FIRST(&Ships[sindex].subsys_list);
17020 	while(subsys != END_OF_LIST(&Ships[sindex].subsys_list)){
17021 		// just mark all turrets as locked
17022 		if(subsys->system_info->type == SUBSYSTEM_TURRET){
17023 			subsys->weapons.flags &= (~SW_FLAG_TAGGED_ONLY);
17024 		}
17025 
17026 		// next item
17027 		subsys = GET_NEXT(subsys);
17028 	}
17029 }
17030 
sexp_turret_change_weapon(int node)17031 void sexp_turret_change_weapon(int node)
17032 {
17033 	int sindex;
17034 	int windex;	//hehe
17035 	ship_subsys *turret = NULL;
17036 	ship_weapon *swp = NULL;
17037 
17038 	// get the firing ship
17039 	sindex = ship_name_lookup(CTEXT(node));
17040 	if(sindex < 0 || Ships[sindex].objnum < 0){
17041 		return;
17042 	}
17043 
17044 	//Get subsystem
17045 	node = CDR(node);
17046 	turret = ship_get_subsys(&Ships[sindex], CTEXT(node));
17047 	if(turret == NULL){
17048 		return;
17049 	}
17050 	swp = &turret->weapons;
17051 
17052 	node = CDR(node);
17053 	windex = weapon_info_lookup(CTEXT(node));
17054 	if(windex < 0) {
17055 		return;
17056 	}
17057 
17058 	//Get the slot
17059 	float capacity, size;
17060 	int prim_slot = 0;
17061 	int sec_slot = 0;
17062 
17063 	node = CDR(node);
17064 	prim_slot = eval_num(node);
17065 
17066 	node = CDR(node);
17067 	sec_slot = eval_num(node);
17068 
17069 	if(prim_slot)
17070 	{
17071 		if(prim_slot > MAX_SHIP_PRIMARY_BANKS) {
17072 			return;
17073 		}
17074 
17075 		if(prim_slot > swp->num_primary_banks) {
17076 			swp->num_primary_banks++;
17077 			prim_slot = swp->num_primary_banks;
17078 		}
17079 
17080 		//Get an index
17081 		prim_slot--;
17082 
17083 		//Get the max capacity
17084 		capacity = (float) swp->primary_bank_capacity[prim_slot];
17085 		size = (float) Weapon_info[windex].cargo_size;
17086 
17087 		//Set various vars
17088 		swp->primary_bank_start_ammo[prim_slot] = (int) (capacity / size);
17089 		swp->primary_bank_ammo[prim_slot] = swp->primary_bank_start_ammo[prim_slot];
17090 		swp->primary_bank_weapons[prim_slot] = windex;
17091 		swp->primary_bank_rearm_time[prim_slot] = timestamp(0);
17092 		swp->primary_bank_fof_cooldown[prim_slot] = 0.0f;
17093 	}
17094 	else if(sec_slot)
17095 	{
17096 		if(sec_slot > MAX_SHIP_PRIMARY_BANKS) {
17097 			return;
17098 		}
17099 
17100 		if(sec_slot > swp->num_secondary_banks) {
17101 			swp->num_secondary_banks++;
17102 			sec_slot = swp->num_secondary_banks;
17103 		}
17104 
17105 		//Get an index
17106 		sec_slot--;
17107 
17108 		//Get the max capacity
17109 		capacity = (float) swp->secondary_bank_capacity[sec_slot];
17110 		size = (float) Weapon_info[windex].cargo_size;
17111 
17112 		//Set various vars
17113 		swp->secondary_bank_start_ammo[sec_slot] = (int) (capacity / size);
17114 		swp->secondary_bank_ammo[sec_slot] = swp->secondary_bank_start_ammo[sec_slot];
17115 		swp->secondary_bank_weapons[sec_slot] = windex;
17116 		swp->secondary_bank_rearm_time[sec_slot] = timestamp(0);
17117 	}
17118 }
17119 
sexp_set_armor_type(int node)17120 void sexp_set_armor_type(int node)
17121 {
17122 	int sindex;
17123 	int armor, rset;
17124 	ship_subsys *ss = NULL;
17125 	ship *shipp = NULL;
17126 	ship_info *sip = NULL;
17127 
17128 	// get ship
17129 	sindex = ship_name_lookup(CTEXT(node));
17130 	if(sindex < 0) {
17131 		return;
17132 	}
17133 	if(Ships[sindex].objnum < 0) {
17134 		return;
17135 	}
17136 	shipp = &Ships[sindex];
17137 	sip = &Ship_info[shipp->ship_info_index];
17138 
17139 	// set or reset
17140 	node = CDR(node);
17141 	rset = is_sexp_true(node);
17142 
17143 	// get armor
17144 	node = CDR(node);
17145 	if (!stricmp(SEXP_NONE_STRING, CTEXT(node))) {
17146 		armor = -1;
17147 	} else {
17148 		armor = armor_type_get_idx(CTEXT(node));
17149 	}
17150 
17151 	//Set armor
17152 	while(node != -1)
17153 	{
17154 		if (!stricmp(SEXP_HULL_STRING, CTEXT(node)))
17155 		{
17156 			// we are setting the ship itself
17157 			if (!rset)
17158 				shipp->armor_type_idx = sip->armor_type_idx;
17159 			else
17160 				shipp->armor_type_idx = armor;
17161 		}
17162 		else if (!stricmp(SEXP_SHIELD_STRING, CTEXT(node)))
17163 		{
17164 			// we are setting the ships shields
17165 			if (!rset)
17166 				shipp->shield_armor_type_idx = sip->shield_armor_type_idx;
17167 			else
17168 				shipp->shield_armor_type_idx = armor;
17169 		}
17170 		else
17171 		{
17172 			// get the subsystem
17173 			ss = ship_get_subsys(&Ships[sindex], CTEXT(node));
17174 			if(ss == NULL){
17175 				node = CDR(node);
17176 				continue;
17177 			}
17178 
17179 			// set the range
17180 			if (!rset)
17181 				ss->armor_type_idx = ss->system_info->armor_type_idx;
17182 			else
17183 				ss->armor_type_idx = armor;
17184 		}
17185 		// next
17186 		node = CDR(node);
17187 	}
17188 }
17189 
sexp_weapon_set_damage_type(int node)17190 void sexp_weapon_set_damage_type(int node)
17191 {
17192 	int windex, damage, swave, rset;
17193 	size_t t;
17194 
17195 	// weapon or shockwave
17196 	swave = is_sexp_true(node);
17197 
17198 	// get damage type
17199 	node = CDR(node);
17200 	if (!stricmp(SEXP_NONE_STRING, CTEXT(node)))
17201 		damage = -1;
17202 	else
17203 	{
17204 		for(t = 0; t < Damage_types.size(); t++)
17205 		{
17206 			if ( !stricmp(Damage_types[t].name, CTEXT(node)))
17207 				break;
17208 		}
17209 		if (t == Damage_types.size())
17210 			return;
17211 		damage = (int)t;
17212 	}
17213 
17214 	//Set or reset to defualt
17215 	node = CDR(node);
17216 	rset = is_sexp_true(node);
17217 
17218 	//Set Damage
17219 	node = CDR(node);
17220 	while(node != -1)
17221 	{
17222 		// get the weapon
17223 		windex = weapon_info_lookup(CTEXT(node));
17224 		if(windex >= 0)
17225 		{
17226 			// set the damage type
17227 			if (swave)
17228 				if (!rset)
17229 					Weapon_info[windex].damage_type_idx = Weapon_info[windex].damage_type_idx_sav;
17230 				else
17231 					Weapon_info[windex].damage_type_idx = damage;
17232 			else
17233 				if (!rset)
17234 					Weapon_info[windex].shockwave.damage_type_idx = Weapon_info[windex].shockwave.damage_type_idx_sav;
17235 				else
17236 					Weapon_info[windex].shockwave.damage_type_idx = damage;
17237 		// next
17238 		}
17239 		node = CDR(node);
17240 	}
17241 }
17242 
sexp_ship_set_damage_type(int node)17243 void sexp_ship_set_damage_type(int node)
17244 {
17245 	int sindex, damage, debris, rset;
17246 	size_t t;
17247 	ship *shipp = NULL;
17248 
17249 	// collision or debris
17250 	debris = is_sexp_true(node);
17251 
17252 	// get damage type
17253 	node = CDR(node);
17254 	if (!stricmp(SEXP_NONE_STRING, CTEXT(node)))
17255 		damage = -1;
17256 	else
17257 	{
17258 		for(t = 0; t < Damage_types.size(); t++)
17259 		{
17260 			if ( !stricmp(Damage_types[t].name, CTEXT(node)))
17261 				break;
17262 		}
17263 		if (t == Damage_types.size())
17264 			return;
17265 		damage = (int)t;
17266 	}
17267 
17268 	//Set or reset to defualt
17269 	node = CDR(node);
17270 	rset = is_sexp_true(node);
17271 
17272 	//Set Damage
17273 	node = CDR(node);
17274 	while(node != -1)
17275 	{
17276 		// get the ship
17277 		sindex = ship_name_lookup(CTEXT(node));
17278 		if(sindex >= 0)
17279 		{
17280 			shipp = &Ships[sindex];
17281 			// set the damage type
17282 			if (debris)
17283 			{
17284 				if (!rset)
17285 					shipp->collision_damage_type_idx = Ship_info[shipp->ship_info_index].collision_damage_type_idx;
17286 				else
17287 					shipp->collision_damage_type_idx = damage;
17288 			}
17289 			else
17290 			{
17291 				if (!rset)
17292 					shipp->debris_damage_type_idx = Ship_info[shipp->ship_info_index].debris_damage_type_idx;
17293 				else
17294 					shipp->debris_damage_type_idx = damage;
17295 			}
17296 			// next
17297 		}
17298 		node = CDR(node);
17299 	}
17300 }
sexp_ship_shockwave_set_damage_type(int node)17301 void sexp_ship_shockwave_set_damage_type(int node)
17302 {
17303 	int sindex, damage, rset;
17304 	size_t t;
17305 
17306 	// get damage type
17307 	if (!stricmp(SEXP_NONE_STRING, CTEXT(node)))
17308 		damage = -1;
17309 	else
17310 	{
17311 		for(t = 0; t < Damage_types.size(); t++)
17312 		{
17313 			if ( !stricmp(Damage_types[t].name, CTEXT(node)))
17314 				break;
17315 		}
17316 		if (t == Damage_types.size())
17317 			return;
17318 		damage = (int)t;
17319 	}
17320 
17321 	//Set or reset to defualt
17322 	node = CDR(node);
17323 	rset = is_sexp_true(node);
17324 
17325 	//Set Damage
17326 	node = CDR(node);
17327 	while(node != -1)
17328 	{
17329 		// get the ship
17330 		sindex = ship_info_lookup(CTEXT(node));
17331 		if(sindex >= 0)
17332 		{
17333 			// set the damage type
17334 			if (!rset)
17335 				Ship_info[sindex].shockwave.damage_type_idx = Ship_info[sindex].shockwave.damage_type_idx_sav;
17336 			else
17337 				Ship_info[sindex].shockwave.damage_type_idx = damage;
17338 			// next
17339 		}
17340 		node = CDR(node);
17341 	}
17342 }
sexp_field_set_damage_type(int node)17343 void sexp_field_set_damage_type(int node)
17344 {
17345 	int damage, rset;
17346 	size_t t;
17347 
17348 	// get damage type
17349 	if (!stricmp(SEXP_NONE_STRING, CTEXT(node)))
17350 		damage = -1;
17351 	else
17352 	{
17353 		for(t = 0; t < Damage_types.size(); t++)
17354 		{
17355 			if ( !stricmp(Damage_types[t].name, CTEXT(node)))
17356 				break;
17357 		}
17358 		if (t == Damage_types.size())
17359 			return;
17360 		damage = (int)t;
17361 	}
17362 
17363 	//Set or reset to defualt
17364 	node = CDR(node);
17365 	rset = is_sexp_true(node);
17366 
17367 	//Set Damage
17368 	node = CDR(node);
17369 	for(t = 0; t < Asteroid_info.size(); t++)
17370 	if (!rset)
17371 		Asteroid_info[t].damage_type_idx = Asteroid_info[t].damage_type_idx_sav;
17372 	else
17373 		Asteroid_info[t].damage_type_idx = damage;
17374 }
sexp_turret_set_target_order(int node)17375 void sexp_turret_set_target_order(int node)
17376 {
17377 	int sindex;
17378 	ship_subsys *turret = NULL;
17379 	int oindex;
17380 	int i;
17381 
17382 	// get ship
17383 	sindex = ship_name_lookup(CTEXT(node));
17384 	if(sindex < 0){
17385 		return;
17386 	}
17387 	if(Ships[sindex].objnum < 0){
17388 		return;
17389 	}
17390 
17391 	//Get turret subsys
17392 	node = CDR(node);
17393 	turret = ship_get_subsys(&Ships[sindex], CTEXT(node));
17394 	if(turret == NULL){
17395 		return;
17396 	}
17397 
17398 	//Reset order
17399 	for(i = 0; i < NUM_TURRET_ORDER_TYPES; i++) {
17400 		turret->turret_targeting_order[i] = -1;
17401 	}
17402 
17403 	oindex = 0;
17404 	node = CDR(node);
17405 	while(node != -1)
17406 	{
17407 		if(oindex >= NUM_TURRET_ORDER_TYPES) {
17408 			break;
17409 		}
17410 
17411 		for(i = 0; i < NUM_TURRET_ORDER_TYPES; i++) {
17412 			if(!stricmp(Turret_target_order_names[i], CTEXT(node))) {
17413 				turret->turret_targeting_order[oindex] = i;
17414 			}
17415 		}
17416 
17417 		oindex++;
17418 	}
17419 }
sexp_turret_set_direction_preference(int node)17420 void sexp_turret_set_direction_preference(int node)
17421 {
17422 	int sindex;
17423 	ship_subsys *turret = NULL;
17424 
17425 	// get ship
17426 	sindex = ship_name_lookup(CTEXT(node));
17427 	if(sindex < 0){
17428 		return;
17429 	}
17430 	if(Ships[sindex].objnum < 0){
17431 		return;
17432 	}
17433 
17434 	//store direction preference
17435 	int dirpref = eval_num(CDR(node));
17436 
17437 	//Set range
17438 	while(node != -1){
17439 		// get the subsystem
17440 		turret = ship_get_subsys(&Ships[sindex], CTEXT(node));
17441 		if(turret == NULL){
17442 			node = CDR(node);
17443 			continue;
17444 		}
17445 
17446 		// set the range
17447 		if(dirpref < 0)
17448 			turret->optimum_range = turret->system_info->optimum_range;
17449 		else
17450 			if (dirpref == 0) {
17451 				turret->favor_current_facing = 0.0f;
17452 			} else {
17453 				CAP(dirpref, 1, 100);
17454 				turret->favor_current_facing = 1.0f + (((float) (100 - dirpref)) / 10.0f);
17455 			}
17456 
17457 		// next
17458 		node = CDR(node);
17459 	}
17460 }
17461 
sexp_turret_set_rate_of_fire(int node)17462 void sexp_turret_set_rate_of_fire(int node)
17463 {
17464 	int sindex;
17465 	ship_subsys *turret = NULL;
17466 
17467 	// get ship
17468 	sindex = ship_name_lookup(CTEXT(node));
17469 	if(sindex < 0){
17470 		return;
17471 	}
17472 	if(Ships[sindex].objnum < 0){
17473 		return;
17474 	}
17475 
17476 	//store rof
17477 	float rof = (float)eval_num(CDR(node));
17478 
17479 	//Set rof
17480 	while(node != -1){
17481 		// get the subsystem
17482 		turret = ship_get_subsys(&Ships[sindex], CTEXT(node));
17483 		if(turret == NULL){
17484 			node = CDR(node);
17485 			continue;
17486 		}
17487 
17488 		// set the range
17489 		if(rof < 0)
17490 			turret->rof_scaler = turret->system_info->turret_rof_scaler ;
17491 		else
17492 			turret->rof_scaler = rof/100;
17493 
17494 		// next
17495 		node = CDR(node);
17496 	}
17497 }
17498 
sexp_turret_set_optimum_range(int node)17499 void sexp_turret_set_optimum_range(int node)
17500 {
17501 	int sindex;
17502 	ship_subsys *turret = NULL;
17503 
17504 	// get ship
17505 	sindex = ship_name_lookup(CTEXT(node));
17506 	if(sindex < 0){
17507 		return;
17508 	}
17509 	if(Ships[sindex].objnum < 0){
17510 		return;
17511 	}
17512 
17513 	//store range
17514 	float range = (float)eval_num(CDR(node));
17515 
17516 	//Set range
17517 	while(node != -1){
17518 		// get the subsystem
17519 		turret = ship_get_subsys(&Ships[sindex], CTEXT(node));
17520 		if(turret == NULL){
17521 			node = CDR(node);
17522 			continue;
17523 		}
17524 
17525 		// set the range
17526 		if(range < 0)
17527 			turret->optimum_range = turret->system_info->optimum_range;
17528 		else
17529 			turret->optimum_range = range;
17530 
17531 		// next
17532 		node = CDR(node);
17533 	}
17534 }
17535 
sexp_turret_set_target_priorities(int node)17536 void sexp_turret_set_target_priorities(int node)
17537 {
17538 	int sindex;
17539 	ship_subsys *turret = NULL;
17540 	int i;
17541 	int j;
17542 
17543 	// get ship
17544 	sindex = ship_name_lookup(CTEXT(node));
17545 	if(sindex < 0){
17546 		return;
17547 	}
17548 	if(Ships[sindex].objnum < 0){
17549 		return;
17550 	}
17551 
17552 	//Get turret subsys
17553 	node = CDR(node);
17554 	turret = ship_get_subsys(&Ships[sindex], CTEXT(node));
17555 	if(turret == NULL){
17556 		return;
17557 	}
17558 
17559 	//Reset or new list
17560 	node = CDR(node);
17561 	if(!(is_sexp_true(node))) {	//Reset
17562 		turret->num_target_priorities = turret->system_info->num_target_priorities;
17563 		for (j = 0; j < 32; j++) {
17564 			turret->target_priority[j] = turret->system_info->target_priority[j];
17565 		}
17566 	}
17567 	else {					//New List
17568 		node = CDR(node);
17569 		//clear the list
17570 		turret->num_target_priorities = 0;
17571 		for (i = 0; i < 32; i++) {
17572 			turret->target_priority[i] = -1;
17573 		}
17574 		int num_groups = Ai_tp_list.size();
17575 		// set the target priorities
17576 		while(node != -1){
17577 			if(turret->num_target_priorities < 32){
17578 				for(j = 0; j < num_groups; j++) {
17579 					if ( !stricmp(Ai_tp_list[j].name, CTEXT(node)))  {
17580 							turret->target_priority[turret->num_target_priorities] = j;
17581 							turret->num_target_priorities++;
17582 					}
17583 				}
17584 			}
17585 		// next
17586 		node = CDR(node);
17587 		}
17588 	}
17589 }
17590 
sexp_ship_turret_target_order(int node)17591 void sexp_ship_turret_target_order(int node)
17592 {
17593 	int sindex;
17594 	ship_subsys *turret = NULL;
17595 	int oindex;
17596 	int i;
17597 	int new_target_order[NUM_TURRET_ORDER_TYPES];
17598 
17599 	// get ship
17600 	sindex = ship_name_lookup(CTEXT(node));
17601 	if(sindex < 0){
17602 		return;
17603 	}
17604 	if(Ships[sindex].objnum < 0){
17605 		return;
17606 	}
17607 
17608 	oindex = 0;
17609 	node = CDR(node);
17610 	while(node != -1)
17611 	{
17612 		if(oindex >= NUM_TURRET_ORDER_TYPES) {
17613 			break;
17614 		}
17615 
17616 		for(i = 0; i < NUM_TURRET_ORDER_TYPES; i++) {
17617 			if(!stricmp(Turret_target_order_names[i], CTEXT(node))) {
17618 				new_target_order[oindex] = i;
17619 			}
17620 		}
17621 
17622 		oindex++;
17623 	}
17624 
17625 	turret = GET_FIRST(&Ships[sindex].subsys_list);
17626 	while(turret != END_OF_LIST(&Ships[sindex].subsys_list))
17627 	{
17628 		//Reset order
17629 		for(i = 0; i < NUM_TURRET_ORDER_TYPES; i++) {
17630 			turret->turret_targeting_order[i] = -1;
17631 		}
17632 
17633 		memcpy(turret->turret_targeting_order, new_target_order, NUM_TURRET_ORDER_TYPES*sizeof(int));
17634 
17635 		// next item
17636 		turret = GET_NEXT(turret);
17637 	}
17638 }
17639 
17640 // Goober5000
sexp_set_subsys_rotation_lock_free(int node,int locked)17641 void sexp_set_subsys_rotation_lock_free(int node, int locked)
17642 {
17643 	int ship_num;
17644 	ship_subsys *rotate;
17645 
17646 	// get the ship
17647 	ship_num = ship_name_lookup(CTEXT(node));
17648 	if (ship_num < 0)
17649 		return;
17650 
17651 	if (Ships[ship_num].objnum < 0)
17652 		return;
17653 
17654 	node = CDR(node);
17655 
17656 	// loop for all specified subsystems
17657 	for ( ; node >= 0; node = CDR(node) )
17658 	{
17659 		// get the rotating subsystem
17660 		rotate = ship_get_subsys(&Ships[ship_num], CTEXT(node));
17661 		if (rotate == NULL)
17662 			continue;
17663 
17664 		// set rotate or not, depending on flag
17665 		if (locked)
17666 		{
17667 			rotate->flags &= ~SSF_ROTATES;
17668 			if (rotate->subsys_snd_flags & SSSF_ROTATE)
17669 			{
17670 				obj_snd_delete_type(Ships[ship_num].objnum, rotate->system_info->rotation_snd, rotate);
17671 				rotate->subsys_snd_flags &= ~SSSF_ROTATE;
17672 			}
17673 		}
17674 		else
17675 		{
17676 			rotate->flags |= SSF_ROTATES;
17677 			if (rotate->system_info->rotation_snd >= 0)
17678 			{
17679 				obj_snd_assign(Ships[ship_num].objnum, rotate->system_info->rotation_snd, &rotate->system_info->pnt, 0, OS_SUBSYS_ROTATION, rotate);
17680 				rotate->subsys_snd_flags |= SSSF_ROTATE;
17681 			}
17682 		}
17683 	}
17684 }
17685 
17686 // Goober5000
sexp_reverse_rotating_subsystem(int node)17687 void sexp_reverse_rotating_subsystem(int node)
17688 {
17689 	int ship_num;
17690 	ship_subsys *rotate;
17691 
17692 	// get the ship
17693 	ship_num = ship_name_lookup(CTEXT(node));
17694 	if (ship_num < 0)
17695 		return;
17696 
17697 	if (Ships[ship_num].objnum < 0)
17698 		return;
17699 
17700 	node = CDR(node);
17701 
17702 	// loop for all specified subsystems
17703 	for ( ; node >= 0; node = CDR(node) )
17704 	{
17705 		// get the rotating subsystem
17706 		rotate = ship_get_subsys(&Ships[ship_num], CTEXT(node));
17707 		if (rotate == NULL)
17708 			continue;
17709 
17710 		// switch direction of rotation
17711 		rotate->turn_rate *= -1.0f;
17712 		rotate->submodel_info_1.cur_turn_rate *= -1.0f;
17713 		rotate->submodel_info_1.desired_turn_rate *= -1.0f;
17714 	}
17715 }
17716 
17717 // Goober5000
sexp_rotating_subsys_set_turn_time(int node)17718 void sexp_rotating_subsys_set_turn_time(int node)
17719 {
17720 	int ship_num, n = node;
17721 	float turn_time, turn_accel;
17722 	ship_subsys *rotate;
17723 
17724 	// get the ship
17725 	ship_num = ship_name_lookup(CTEXT(n));
17726 	if (ship_num < 0)
17727 		return;
17728 	if (Ships[ship_num].objnum < 0)
17729 		return;
17730 	n = CDR(n);
17731 
17732 	// get the rotating subsystem
17733 	rotate = ship_get_subsys(&Ships[ship_num], CTEXT(n));
17734 	if (rotate == NULL)
17735 		return;
17736 	n = CDR(n);
17737 
17738 	// get and set the turn time
17739 	turn_time = eval_num(n) / 1000.0f;
17740 	rotate->submodel_info_1.desired_turn_rate = PI2 / turn_time;
17741 	n = CDR(n);
17742 
17743 	// maybe get and set the turn accel
17744 	if (n != -1)
17745 	{
17746 		turn_accel = eval_num(n) / 1000.0f;
17747 		rotate->submodel_info_1.turn_accel = PI2 / turn_accel;
17748 	}
17749 	else
17750 		rotate->submodel_info_1.cur_turn_rate = PI2 / turn_time;
17751 }
17752 
sexp_trigger_submodel_animation(int node)17753 void sexp_trigger_submodel_animation(int node)
17754 {
17755 	int ship_num, animation_type, animation_subtype, direction, n = node;
17756 	bool instant;
17757 
17758 	// get the ship
17759 	ship_num = ship_name_lookup(CTEXT(n));
17760 	if (ship_num < 0)
17761 		return;
17762 	if (Ships[ship_num].objnum < 0)
17763 		return;
17764 	n = CDR(n);
17765 
17766 	// get the type
17767 	animation_type = model_anim_match_type(CTEXT(n));
17768 	if (animation_type == TRIGGER_TYPE_NONE)
17769 	{
17770 		Warning(LOCATION, "Unable to match animation type \"%s\"!", CTEXT(n));
17771 		return;
17772 	}
17773 	n = CDR(n);
17774 
17775 	// get the subtype
17776 	animation_subtype = eval_num(n);
17777 	n = CDR(n);
17778 
17779 	// get the direction, 1 or -1
17780 	direction = eval_num(n);
17781 	if (direction != 1 && direction != -1)
17782 	{
17783 		Warning(LOCATION, "Direction is %d; it must be 1 or -1!", direction);
17784 		return;
17785 	}
17786 	n = CDR(n);
17787 
17788 	// instant or not
17789 	if (n >= 0)
17790 	{
17791 		instant = (is_sexp_true(n) != 0);
17792 		n = CDR(n);
17793 	}
17794 	else
17795 		instant = false;
17796 
17797 	// do we narrow it to a specific subsystem?
17798 	if (n >= 0)
17799 	{
17800 		ship_subsys *ss = ship_get_subsys(&Ships[ship_num], CTEXT(n));
17801 		if (ss == NULL)
17802 		{
17803 			Warning(LOCATION, "Subsystem \"%s\" not found on ship \"%s\"!", CTEXT(n), CTEXT(node));
17804 			return;
17805 		}
17806 		model_anim_start_type(ss, animation_type, animation_subtype, direction, instant);
17807 	}
17808 	else
17809 	{
17810 		model_anim_start_type(&Ships[ship_num], animation_type, animation_subtype, direction, instant);
17811 	}
17812 }
17813 
sexp_turret_tagged_specific(int node)17814 void sexp_turret_tagged_specific(int node)
17815 {
17816 	ship_subsys *subsys;
17817 	int sindex;
17818 
17819 	// get the firing ship
17820 	sindex = ship_name_lookup(CTEXT(node));
17821 	if(sindex < 0){
17822 		return;
17823 	}
17824 	if(Ships[sindex].objnum < 0){
17825 		return;
17826 	}
17827 
17828 	node = CDR(node);
17829 	for ( ; node >= 0; node = CDR(node) ) {
17830 		// get the subsystem
17831 		subsys = ship_get_subsys(&Ships[sindex], CTEXT(node));
17832 		if(subsys == NULL){
17833 			continue;
17834 		}
17835 
17836 		// flag turret as slaved to tag
17837 		subsys->weapons.flags |= SW_FLAG_TAGGED_ONLY;
17838 	}
17839 }
17840 
sexp_turret_tagged_clear_specific(int node)17841 void sexp_turret_tagged_clear_specific(int node)
17842 {
17843 	ship_subsys *subsys;
17844 	int sindex;
17845 
17846 	// get the firing ship
17847 	sindex = ship_name_lookup(CTEXT(node));
17848 	if(sindex < 0){
17849 		return;
17850 	}
17851 	if(Ships[sindex].objnum < 0){
17852 		return;
17853 	}
17854 
17855 	node = CDR(node);
17856 	for ( ; node >= 0; node = CDR(node) ) {
17857 		// get the subsystem
17858 		subsys = ship_get_subsys(&Ships[sindex], CTEXT(node));
17859 		if(subsys == NULL){
17860 			continue;
17861 		}
17862 
17863 		// flag turret as slaved to tag
17864 		subsys->weapons.flags &= (~SW_FLAG_TAGGED_ONLY);
17865 	}
17866 }
17867 
sexp_add_remove_escort(int node)17868 void sexp_add_remove_escort(int node)
17869 {
17870 	int sindex;
17871 	int flag;
17872 
17873 	// get the firing ship
17874 	sindex = ship_name_lookup(CTEXT(node));
17875 	if(sindex < 0){
17876 		return;
17877 	}
17878 	if(Ships[sindex].objnum < 0){
17879 		return;
17880 	}
17881 
17882 	// determine whether to add or remove it
17883 	flag = eval_num(CDR(node));
17884 
17885 	// add/remove
17886 	if(flag){
17887 		Ships[sindex].escort_priority = flag ;
17888 		hud_add_ship_to_escort(Ships[sindex].objnum, 1);
17889 	} else {
17890 		hud_remove_ship_from_escort(Ships[sindex].objnum);
17891 	}
17892 
17893 	multi_start_callback();
17894 	multi_send_int(sindex);
17895 	multi_send_int(flag);
17896 	multi_end_callback();
17897 }
17898 
multi_sexp_add_remove_escort()17899 void multi_sexp_add_remove_escort()
17900 {
17901 	int sindex;
17902 	int flag;
17903 
17904 	multi_get_int(sindex);
17905 	if (!(multi_get_int(flag))) {
17906 		return;
17907 	}
17908 
17909 	// add/remove
17910 	if(flag){
17911 		Ships[sindex].escort_priority = flag ;
17912 		hud_add_ship_to_escort(Ships[sindex].objnum, 1);
17913 	} else {
17914 		hud_remove_ship_from_escort(Ships[sindex].objnum);
17915 	}
17916 }
17917 
17918 //given: two escort priorities and a list of ships
17919 //do:    sets the most damaged one to the first priority and the rest to the second.
sexp_damage_escort_list(int node)17920 void sexp_damage_escort_list(int node)
17921 {
17922 	int n;
17923 	int priority1;		//escort priority to set the most damaged ship
17924 	int priority2;		//""         ""   to set the other ships
17925 
17926 	ship* shipp;
17927 	float smallest_hull_pct=1;		//smallest hull pct found
17928 	int small_shipnum=-1;		//index in Ships[] of the above
17929 	float current_hull_pct;			//hull pct of current ship we are evaluating
17930 	int shipnum=-1;				//index in Ships[] of the above
17931 
17932 	priority1=eval_num(node);
17933 	priority2=eval_num(CDR(node));
17934 
17935 	//go to start of ship list
17936 	n=CDR(CDR(node));
17937 
17938 	//loop through the ships
17939 	for ( ; n != -1; n = CDR(n) )
17940 	{
17941 		// check to see if ship destroyed or departed.  In either case, do nothing.
17942 		if ( mission_log_get_time(LOG_SHIP_DEPARTED, CTEXT(n), NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, CTEXT(n), NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, CTEXT(n), NULL, NULL) )
17943 			continue;
17944 
17945 		shipnum=ship_name_lookup(CTEXT(n));
17946 
17947 		//it may be dead
17948 		if (shipnum < 0)
17949 			continue;
17950 
17951 		if (Ships[shipnum].objnum < 0)
17952 			continue;
17953 
17954 		shipp=&Ships[shipnum];
17955 
17956 		//calc hull integrity and compare
17957 		current_hull_pct = get_hull_pct(&Objects[Ships[shipnum].objnum]);
17958 
17959 		if (current_hull_pct < smallest_hull_pct)
17960 		{
17961 			if (small_shipnum != -1) // avoid negative array index during 1st loop iteration
17962 			{
17963 				Ships[small_shipnum].escort_priority=priority2;		//give the previous smallest the lower priority
17964 			}
17965 
17966 			smallest_hull_pct=current_hull_pct;
17967 			small_shipnum=shipnum;
17968 
17969 			shipp->escort_priority=priority1;					//give the new smallest the higher priority
17970 			hud_add_ship_to_escort(Ships[shipnum].objnum,1);
17971 		}
17972 		else														//if its bigger to begin with give it lower priority
17973 		{
17974 			shipp->escort_priority=priority2;
17975 			hud_add_ship_to_escort(Ships[shipnum].objnum,1);
17976 		}
17977 	}
17978 }
17979 
17980 // Goober5000 - set stuff for mission support ship
sexp_set_support_ship(int n)17981 void sexp_set_support_ship(int n)
17982 {
17983 	int i, temp_val;
17984 
17985 	// get arrival location
17986 	temp_val = -1;
17987 	for (i=0; i<MAX_ARRIVAL_NAMES; i++)
17988 	{
17989 		if (!stricmp(CTEXT(n), Arrival_location_names[i]))
17990 			temp_val = i;
17991 	}
17992 	if (temp_val < 0)
17993 	{
17994 		Warning(LOCATION, "Support ship arrival location '%s' not found.\n", CTEXT(n));
17995 		return;
17996 	}
17997 	The_mission.support_ships.arrival_location = temp_val;
17998 
17999 	// get arrival anchor
18000 	n = CDR(n);
18001 	if (!stricmp(CTEXT(n), "<no anchor>"))
18002 	{
18003 		// if no anchor, set arrival location to hyperspace
18004 		The_mission.support_ships.arrival_location = 0;
18005 	}
18006 	else
18007 	{
18008 		// anchor must exist - look for it
18009 		temp_val = -1;
18010 		for (i=0; i<Num_parse_names; i++)
18011 		{
18012 			if (!stricmp(CTEXT(n), Parse_names[i]))
18013 				temp_val = i;
18014 		}
18015 		// if not found, make a new entry
18016 		if (temp_val < 0)
18017 		{
18018 			strcpy_s(Parse_names[Num_parse_names], CTEXT(n));
18019 			temp_val = Num_parse_names;
18020 			Num_parse_names++;
18021 		}
18022 		The_mission.support_ships.arrival_anchor = temp_val;
18023 	}
18024 
18025 	// get departure location
18026 	n = CDR(n);
18027 	temp_val = -1;
18028 	for (i=0; i<MAX_DEPARTURE_NAMES; i++)
18029 	{
18030 		if (!stricmp(CTEXT(n), Departure_location_names[i]))
18031 			temp_val = i;
18032 	}
18033 	if (temp_val < 0)
18034 	{
18035 		Warning(LOCATION, "Support ship departure location '%s' not found.\n", CTEXT(n));
18036 		return;
18037 	}
18038 	The_mission.support_ships.departure_location = temp_val;
18039 
18040 	// get departure anchor
18041 	n = CDR(n);
18042 	if (!stricmp(CTEXT(n), "<no anchor>"))
18043 	{
18044 		// if no anchor, set departure location to hyperspace
18045 		The_mission.support_ships.departure_location = 0;
18046 	}
18047 	else
18048 	{
18049 		// anchor must exist - look for it
18050 		temp_val = -1;
18051 		for (i=0; i<Num_parse_names; i++)
18052 		{
18053 			if (!stricmp(CTEXT(n), Parse_names[i]))
18054 				temp_val = i;
18055 		}
18056 		// if not found, make a new entry
18057 		if (temp_val < 0)
18058 		{
18059 			strcpy_s(Parse_names[Num_parse_names], CTEXT(n));
18060 			temp_val = Num_parse_names;
18061 			Num_parse_names++;
18062 		}
18063 		The_mission.support_ships.departure_anchor = temp_val;
18064 	}
18065 
18066 	// get ship class
18067 	n = CDR(n);
18068 	temp_val = ship_info_lookup(CTEXT(n));
18069 	if ((temp_val < 0) && ((stricmp(CTEXT(n), "<species support ship class>")) && (stricmp(CTEXT(n), "<any support ship class>"))) )
18070 	{
18071 		Warning(LOCATION, "Support ship class '%s' not found.\n", CTEXT(n));
18072 		return;
18073 	}
18074 	if ((temp_val >= 0) && !(Ship_info[temp_val].flags & SIF_SUPPORT))
18075 	{
18076 		Warning(LOCATION, "Ship %s is not a support ship!", Ship_info[temp_val].name);
18077 		return;
18078 	}
18079 	The_mission.support_ships.ship_class = temp_val;
18080 
18081 	// get max number of ships allowed
18082 	n = CDR(n);
18083 	The_mission.support_ships.max_support_ships = eval_num(n);
18084 
18085 	// get the number of concurrent ships allowed
18086 	n = CDR(n);
18087 	if ( n == -1 ) {
18088 		// 7th arg not specified, set default
18089 		The_mission.support_ships.max_concurrent_ships = 1;
18090 	} else {
18091 		The_mission.support_ships.max_concurrent_ships = eval_num(n);
18092 	}
18093 }
18094 
18095 // Goober5000 - set stuff for arriving ships or wings
sexp_set_arrival_info(int node)18096 void sexp_set_arrival_info(int node)
18097 {
18098 	int i, arrival_location, arrival_anchor, arrival_mask, arrival_distance, arrival_delay, n = node;
18099 	bool show_warp;
18100 	object_ship_wing_point_team oswpt;
18101 
18102 	// get ship or wing
18103 	sexp_get_object_ship_wing_point_team(&oswpt, CTEXT(n));
18104 	n = CDR(n);
18105 
18106 	// get arrival location
18107 	arrival_location = -1;
18108 	for (i=0; i<MAX_ARRIVAL_NAMES; i++)
18109 	{
18110 		if (!stricmp(CTEXT(n), Arrival_location_names[i]))
18111 			arrival_location = i;
18112 	}
18113 	if (arrival_location < 0)
18114 	{
18115 		Warning(LOCATION, "Arrival location '%s' not found.\n", CTEXT(n));
18116 		return;
18117 	}
18118 	n = CDR(n);
18119 
18120 	// get arrival anchor
18121 	arrival_anchor = -1;
18122 	if ((n < 0) || !stricmp(CTEXT(n), "<no anchor>"))
18123 	{
18124 		// if no anchor, set arrival location to hyperspace
18125 		arrival_location = 0;
18126 	}
18127 	else
18128 	{
18129 		// anchor must exist - look for it
18130 		for (i=0; i<Num_parse_names; i++)
18131 		{
18132 			if (!stricmp(CTEXT(n), Parse_names[i]))
18133 				arrival_anchor = i;
18134 		}
18135 		// if not found, make a new entry
18136 		if (arrival_anchor < 0)
18137 		{
18138 			strcpy_s(Parse_names[Num_parse_names], CTEXT(n));
18139 			arrival_anchor = Num_parse_names;
18140 			Num_parse_names++;
18141 		}
18142 	}
18143 	n = CDR(n);
18144 
18145 	// get arrival path mask
18146 	arrival_mask = 0;
18147 	if (n >= 0)
18148 		arrival_mask = eval_num(n);
18149 	n = CDR(n);
18150 
18151 	// get arrival distance
18152 	arrival_distance = 0;
18153 	if (n >= 0)
18154 		arrival_distance = eval_num(n);
18155 	n = CDR(n);
18156 
18157 	// get arrival delay
18158 	arrival_delay = 0;
18159 	if (n >= 0)
18160 		arrival_delay = eval_num(n);
18161 	n = CDR(n);
18162 
18163 	// get warp effect
18164 	show_warp = true;
18165 	if (n >= 0)
18166 		show_warp = (is_sexp_true(n) != 0);
18167 
18168 	// now set all that information depending on the first argument
18169 	if (oswpt.type == OSWPT_TYPE_SHIP)
18170 	{
18171 		oswpt.shipp->arrival_location = arrival_location;
18172 		oswpt.shipp->arrival_anchor = arrival_anchor;
18173 		oswpt.shipp->arrival_path_mask = arrival_mask;
18174 		oswpt.shipp->arrival_distance = arrival_distance;
18175 		oswpt.shipp->arrival_delay = arrival_delay;
18176 
18177 		if (show_warp)
18178 			oswpt.shipp->flags &= ~SF_NO_ARRIVAL_WARP;
18179 		else
18180 			oswpt.shipp->flags |= SF_NO_ARRIVAL_WARP;
18181 	}
18182 	else if (oswpt.type == OSWPT_TYPE_WING || oswpt.type == OSWPT_TYPE_WING_NOT_PRESENT)
18183 	{
18184 		oswpt.wingp->arrival_location = arrival_location;
18185 		oswpt.wingp->arrival_anchor = arrival_anchor;
18186 		oswpt.wingp->arrival_path_mask = arrival_mask;
18187 		oswpt.wingp->arrival_distance = arrival_distance;
18188 		oswpt.wingp->arrival_delay = arrival_delay;
18189 
18190 		if (show_warp)
18191 			oswpt.wingp->flags &= ~WF_NO_ARRIVAL_WARP;
18192 		else
18193 			oswpt.wingp->flags |= WF_NO_ARRIVAL_WARP;
18194 	}
18195 	else if (oswpt.type == OSWPT_TYPE_PARSE_OBJECT)
18196 	{
18197 		oswpt.p_objp->arrival_location = arrival_location;
18198 		oswpt.p_objp->arrival_anchor = arrival_anchor;
18199 		oswpt.p_objp->arrival_path_mask = arrival_mask;
18200 		oswpt.p_objp->arrival_distance = arrival_distance;
18201 		oswpt.p_objp->arrival_delay = arrival_delay;
18202 
18203 		if (show_warp)
18204 			oswpt.p_objp->flags &= ~P_SF_NO_ARRIVAL_WARP;
18205 		else
18206 			oswpt.p_objp->flags |= P_SF_NO_ARRIVAL_WARP;
18207 	}
18208 }
18209 
18210 // Goober5000 - set stuff for departing ships or wings
sexp_set_departure_info(int node)18211 void sexp_set_departure_info(int node)
18212 {
18213 	int i, departure_location, departure_anchor, departure_mask, departure_delay, n = node;
18214 	bool show_warp;
18215 	object_ship_wing_point_team oswpt;
18216 
18217 	// get ship or wing
18218 	sexp_get_object_ship_wing_point_team(&oswpt, CTEXT(n));
18219 	n = CDR(n);
18220 
18221 	// get departure location
18222 	departure_location = -1;
18223 	for (i=0; i<MAX_DEPARTURE_NAMES; i++)
18224 	{
18225 		if (!stricmp(CTEXT(n), Departure_location_names[i]))
18226 			departure_location = i;
18227 	}
18228 	if (departure_location < 0)
18229 	{
18230 		Warning(LOCATION, "Departure location '%s' not found.\n", CTEXT(n));
18231 		return;
18232 	}
18233 	n = CDR(n);
18234 
18235 	// get departure anchor
18236 	departure_anchor = -1;
18237 	if ((n < 0) || !stricmp(CTEXT(n), "<no anchor>"))
18238 	{
18239 		// if no anchor, set departure location to hyperspace
18240 		departure_location = 0;
18241 	}
18242 	else
18243 	{
18244 		// anchor must exist - look for it
18245 		for (i=0; i<Num_parse_names; i++)
18246 		{
18247 			if (!stricmp(CTEXT(n), Parse_names[i]))
18248 				departure_anchor = i;
18249 		}
18250 		// if not found, make a new entry
18251 		if (departure_anchor < 0)
18252 		{
18253 			strcpy_s(Parse_names[Num_parse_names], CTEXT(n));
18254 			departure_anchor = Num_parse_names;
18255 			Num_parse_names++;
18256 		}
18257 	}
18258 	n = CDR(n);
18259 
18260 	// get departure path mask
18261 	departure_mask = 0;
18262 	if (n >= 0)
18263 		departure_mask = eval_num(n);
18264 	n = CDR(n);
18265 
18266 	// get departure delay
18267 	departure_delay = 0;
18268 	if (n >= 0)
18269 		departure_delay = eval_num(n);
18270 	n = CDR(n);
18271 
18272 	// get warp effect
18273 	show_warp = true;
18274 	if (n >= 0)
18275 		show_warp = (is_sexp_true(n) != 0);
18276 
18277 	// now set all that information depending on the first argument
18278 	if (oswpt.type == OSWPT_TYPE_SHIP)
18279 	{
18280 		oswpt.shipp->departure_location = departure_location;
18281 		oswpt.shipp->departure_anchor = departure_anchor;
18282 		oswpt.shipp->departure_path_mask = departure_mask;
18283 		oswpt.shipp->departure_delay = departure_delay;
18284 
18285 		if (show_warp)
18286 			oswpt.shipp->flags &= ~SF_NO_DEPARTURE_WARP;
18287 		else
18288 			oswpt.shipp->flags |= SF_NO_DEPARTURE_WARP;
18289 	}
18290 	else if (oswpt.type == OSWPT_TYPE_WING || oswpt.type == OSWPT_TYPE_WING_NOT_PRESENT)
18291 	{
18292 		oswpt.wingp->departure_location = departure_location;
18293 		oswpt.wingp->departure_anchor = departure_anchor;
18294 		oswpt.wingp->departure_path_mask = departure_mask;
18295 		oswpt.wingp->departure_delay = departure_delay;
18296 
18297 		if (show_warp)
18298 			oswpt.wingp->flags &= ~WF_NO_DEPARTURE_WARP;
18299 		else
18300 			oswpt.wingp->flags |= WF_NO_DEPARTURE_WARP;
18301 	}
18302 	else if (oswpt.type == OSWPT_TYPE_PARSE_OBJECT)
18303 	{
18304 		oswpt.p_objp->departure_location = departure_location;
18305 		oswpt.p_objp->departure_anchor = departure_anchor;
18306 		oswpt.p_objp->departure_path_mask = departure_mask;
18307 		oswpt.p_objp->departure_delay = departure_delay;
18308 
18309 		if (show_warp)
18310 			oswpt.p_objp->flags &= ~P_SF_NO_DEPARTURE_WARP;
18311 		else
18312 			oswpt.p_objp->flags |= P_SF_NO_DEPARTURE_WARP;
18313 	}
18314 }
18315 
18316 // Goober5000
18317 // set *all* the escort priorities of ships in escort list as follows: most damaged ship gets
18318 // first priority in the argument list, next damaged gets next priority, etc.; if there are more
18319 // ships than priorities, all remaining ships get the final priority on the list
18320 // -- As indicated in the argument specification, there must be at least one argument but no more
18321 // than MAX_COMPLETE_ESCORT_LIST arguments
sexp_damage_escort_list_all(int n)18322 void sexp_damage_escort_list_all(int n)
18323 {
18324 	typedef struct
18325 	{
18326 		int index;
18327 		float hull;
18328 	} my_escort_ship;
18329 
18330 	int priority[MAX_COMPLETE_ESCORT_LIST];
18331 	my_escort_ship escort_ship[MAX_COMPLETE_ESCORT_LIST];
18332 	int i, j, num_escort_ships, num_priorities, temp_i;
18333 	float temp_f;
18334 	ship *shipp;
18335 
18336 	// build list of priorities
18337 	num_priorities = 0;
18338 	for ( ; n != -1; n = CDR(n) )
18339 	{
18340 		priority[num_priorities] = eval_num(n);
18341 		num_priorities++;
18342 	}
18343 
18344 	// build custom list of escort ships
18345 	num_escort_ships = 0;
18346 	for (i = 0; i < MAX_SHIPS; i++)
18347 	{
18348 		shipp = &Ships[i];
18349 
18350 		// make sure it exists
18351 		if ( shipp->objnum < 0 )
18352 			continue;
18353 
18354 		// make sure it's on the escort list
18355 		if ( !(shipp->flags & SF_ESCORT) )
18356 			continue;
18357 
18358 		// set index
18359 		escort_ship[num_escort_ships].index = i;
18360 
18361 		// calc and set hull integrity
18362 		escort_ship[num_escort_ships].hull = get_hull_pct(&Objects[shipp->objnum]);
18363 
18364 		num_escort_ships++;
18365 	}
18366 
18367 	// sort it bubbly, lowest hull to highest hull
18368 	for (i = 0; i < num_escort_ships; i++)
18369 	{
18370 		for (j = 0; j < i; j++)
18371 		{
18372 			if (escort_ship[i].hull < escort_ship[j].hull)
18373 			{
18374 				// swap
18375 				temp_i = escort_ship[i].index;
18376 				temp_f = escort_ship[i].hull;
18377 				escort_ship[i].index = escort_ship[j].index;
18378 				escort_ship[i].hull = escort_ship[j].hull;
18379 				escort_ship[j].index = temp_i;
18380 				escort_ship[j].hull = temp_f;
18381 			}
18382 		}
18383 	}
18384 
18385 	// loop through and assign priorities
18386 	for (i = 0; i < num_escort_ships; i++)
18387 	{
18388 		if (i >= num_priorities)
18389 			Ships[escort_ship[i].index].escort_priority = priority[num_priorities - 1];
18390 		else
18391 			Ships[escort_ship[i].index].escort_priority = priority[i];
18392 	}
18393 
18394 	// redo the escort list
18395 	hud_setup_escort_list();
18396 }
18397 
sexp_awacs_set_radius(int node)18398 void sexp_awacs_set_radius(int node)
18399 {
18400 	int sindex;
18401 	ship_subsys *awacs;
18402 
18403 	// get the firing ship
18404 	sindex = ship_name_lookup(CTEXT(node));
18405 	if(sindex < 0){
18406 		return;
18407 	}
18408 	if(Ships[sindex].objnum < 0){
18409 		return;
18410 	}
18411 
18412 	// get the awacs subsystem
18413 	awacs = ship_get_subsys(&Ships[sindex], CTEXT(CDR(node)));
18414 	if(awacs == NULL){
18415 		return;
18416 	}
18417 
18418 	if (!(awacs->system_info->flags & MSS_FLAG_AWACS))
18419 		return;
18420 
18421 	// set the new awacs radius
18422 	awacs->awacs_radius = (float)eval_num(CDR(CDR(node)));
18423 }
18424 
18425 // Goober5000
sexp_primitive_sensors_set_range(int n)18426 void sexp_primitive_sensors_set_range(int n)
18427 {
18428 	char *ship_name = CTEXT(n);
18429 	int ship_num, range = eval_num(CDR(n));
18430 
18431 	// check to see if ship destroyed or departed.  In either case, do nothing.
18432 	if ( mission_log_get_time(LOG_SHIP_DEPARTED, ship_name, NULL, NULL) || mission_log_get_time(LOG_SHIP_DESTROYED, ship_name, NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, ship_name, NULL, NULL) )
18433 		return;
18434 
18435 	// get the ship
18436 	ship_num = ship_name_lookup(ship_name);
18437 
18438 	// ship not yet in mission? do nothing
18439 	if (ship_num < 0)
18440 		return;
18441 
18442 	// set the new range
18443 	Ships[ship_num].primitive_sensor_range = range;
18444 }
18445 
18446 
18447 //*************************************************************************************************
18448 // Kazan
18449 // AutoNav/AutoPilot system SEXPS
18450 
18451 //text: set-nav-carry
18452 //args: 1+, Ship/Wing name
set_nav_carry_status(int node)18453 void set_nav_carry_status(int node)
18454 {
18455 	int n=node, i;
18456 	char *name;
18457 	bool skip;
18458 
18459 	while (n != -1)
18460 	{
18461 		name = CTEXT(n);
18462 		skip = false;
18463 
18464 		for (i = 0; i < MAX_WINGS; i++)
18465 		{
18466 			if (!stricmp(Wings[i].name, name))
18467 			{
18468 				Wings[i].flags |= WF_NAV_CARRY;
18469 				skip = true;
18470 				break;
18471 			}
18472 		}
18473 
18474 		if (!skip)
18475 		{
18476 			for (i = 0; i < MAX_SHIPS; i++)
18477 			{
18478 				if (Ships[i].objnum != -1 && !stricmp(Ships[i].ship_name, name))
18479 				{
18480 					Ships[i].flags2 |= SF2_NAVPOINT_CARRY;
18481 					break;
18482 				}
18483 			}
18484 		}
18485 
18486 		// move to next ship/wing in list
18487 		n = CDR(n);
18488 	}
18489 }
18490 
18491 //text: unset-nav-carry
18492 //args: 1+, Ship/Wing name
unset_nav_carry_status(int node)18493 void unset_nav_carry_status(int node)
18494 {
18495 	int n=node, i;
18496 	char *name;
18497 	bool skip;
18498 
18499 
18500 	while (n != -1)
18501 	{
18502 		name = CTEXT(n);
18503 		skip = false;
18504 
18505 		for (i = 0; i < MAX_WINGS; i++)
18506 		{
18507 			if (!stricmp(Wings[i].name, name))
18508 			{
18509 				Wings[i].flags &= ~WF_NAV_CARRY;
18510 				skip = true;
18511 				break;
18512 
18513 			}
18514 		}
18515 
18516 		if (!skip)
18517 		{
18518 			for (i = 0; i < MAX_SHIPS; i++)
18519 			{
18520 				if (Ships[i].objnum != -1 && !stricmp(Ships[i].ship_name, name))
18521 				{
18522 					Ships[i].flags2 &= ~SF2_NAVPOINT_CARRY;
18523 					break;
18524 				}
18525 			}
18526 		}
18527 
18528 		// move to next ship/wing in list
18529 		n = CDR(n);
18530 	}
18531 }
18532 
18533 
18534 //text: set-nav-needslink
18535 //args: 1+, Ship/Wing name
set_nav_needslink(int node)18536 void set_nav_needslink(int node)
18537 {
18538 	int n=node, i;
18539 	char *name;
18540 
18541 	while (n != -1)
18542 	{
18543 		name = CTEXT(n);
18544 
18545 		for (i = 0; i < MAX_SHIPS; i++)
18546 		{
18547 			if (Ships[i].objnum != -1 && !stricmp(Ships[i].ship_name, name))
18548 			{
18549 				Ships[i].flags2 &= ~SF2_NAVPOINT_CARRY;
18550 				Ships[i].flags2 |= SF2_NAVPOINT_NEEDSLINK;
18551 				break;
18552 			}
18553 		}
18554 		// move to next ship/wing in list
18555 		n = CDR(n);
18556 	}
18557 }
18558 
18559 //text: unset-nav-needslink
18560 //args: 1+, Ship/Wing name
unset_nav_needslink(int node)18561 void unset_nav_needslink(int node)
18562 {
18563 	int n=node, i;
18564 	char *name;
18565 
18566 	while (n != -1)
18567 	{
18568 		name = CTEXT(n);
18569 
18570 
18571 		for (i = 0; i < MAX_SHIPS; i++)
18572 		{
18573 			if (Ships[i].objnum != -1 && !stricmp(Ships[i].ship_name, name))
18574 			{
18575 				Ships[i].flags2 &= ~SF2_NAVPOINT_NEEDSLINK;
18576 				break;
18577 			}
18578 		}
18579 
18580 
18581 		// move to next ship/wing in list
18582 		n = CDR(n);
18583 	}
18584 }
18585 
add_nav_waypoint(char * nav,char * WP_path,int vert,char * oswpt_name)18586 void add_nav_waypoint(char *nav, char *WP_path, int vert, char *oswpt_name)
18587 {
18588 	int i;
18589 	object_ship_wing_point_team oswpt;
18590 	bool add_for_this_player = true;
18591 
18592 	if (oswpt_name != NULL) {
18593 		sexp_get_object_ship_wing_point_team(&oswpt, oswpt_name);
18594 
18595 		// we can't assume this nav should be visible to the player any more
18596 		add_for_this_player = false;
18597 
18598 		switch (oswpt.type)
18599 		{
18600 			case OSWPT_TYPE_WHOLE_TEAM:
18601 				if (oswpt.team == Player_ship->team) {
18602 					add_for_this_player = true;
18603 				}
18604 				break;
18605 
18606 			case OSWPT_TYPE_SHIP:
18607 				if (oswpt.shipp == Player_ship) {
18608 					add_for_this_player = true;
18609 				}
18610 				break;
18611 			case OSWPT_TYPE_WING:
18612 				for ( i = 0; i < oswpt.wingp->current_count; i++) {
18613 					if (Ships[oswpt.wingp->ship_index[i]].objnum == Player_ship->objnum) {
18614 						add_for_this_player = true;
18615 					}
18616 				}
18617 
18618 
18619 			// for all other oswpt types we simply ignore this
18620 			default:
18621 				break;
18622 		}
18623 	}
18624 
18625 	AddNav_Waypoint(nav, WP_path, vert, add_for_this_player ? 0 : NP_HIDDEN);
18626 }
18627 
18628 
18629 //text: add-nav-waypoint
18630 //args: 4, Nav Name, Waypoint Path Name, Waypoint Path point, ShipWingTeam
add_nav_waypoint(int node)18631 void add_nav_waypoint(int node)
18632 {
18633 	char *nav_name = CTEXT(node);
18634 	char *way_name = CTEXT(CDR(node));
18635 	int  vert = eval_num(CDR(CDR(node)));
18636 	char *oswpt_name;
18637 
18638 	node = CDR(CDR(CDR(node)));
18639 	if (node >=0) {
18640 		oswpt_name = CTEXT(node);
18641 	}
18642 	else {
18643 		oswpt_name = NULL;
18644 	}
18645 
18646 	add_nav_waypoint(nav_name, way_name, vert, oswpt_name);
18647 
18648 	multi_start_callback();
18649 	multi_send_string(nav_name);
18650 	multi_send_string(way_name);
18651 	multi_send_int(vert);
18652 
18653 	if (oswpt_name != NULL) {
18654 		multi_send_string(oswpt_name);
18655 	}
18656 
18657 	multi_end_callback();
18658 }
18659 
multi_sexp_add_nav_waypoint()18660 void multi_sexp_add_nav_waypoint()
18661 {
18662 	char nav_name[TOKEN_LENGTH];
18663 	char way_name[TOKEN_LENGTH];
18664 	char oswpt_name[TOKEN_LENGTH];
18665 	int vert;
18666 
18667 	if (!multi_get_string(nav_name)) {
18668 		return;
18669 	}
18670 
18671 	if (!multi_get_string(way_name)) {
18672 		return;
18673 	}
18674 
18675 	if (!multi_get_int(vert)) {
18676 		return;
18677 	}
18678 
18679 	if (!multi_get_string(oswpt_name)) {
18680 		add_nav_waypoint(nav_name, way_name, vert, NULL);
18681 	}
18682 	else {
18683 		add_nav_waypoint(nav_name, way_name, vert, oswpt_name);
18684 	}
18685 
18686 	AddNav_Waypoint(nav_name, way_name, vert, 0);
18687 }
18688 
18689 
18690 //text: add-nav-ship
18691 //args: 2, Nav Name, Ship Name
add_nav_ship(int node)18692 void add_nav_ship(int node)
18693 {
18694 	char *nav_name = CTEXT(node);
18695 	char *ship_name = CTEXT(CDR(node));
18696 	AddNav_Ship(nav_name, ship_name, 0);
18697 
18698 	multi_start_callback();
18699 	multi_send_string(nav_name);
18700 	multi_send_string(ship_name);
18701 	multi_end_callback();
18702 }
18703 
multi_add_nav_ship()18704 void multi_add_nav_ship()
18705 {
18706 	char nav_name[TOKEN_LENGTH];
18707 	char ship_name[TOKEN_LENGTH];
18708 
18709 	if (!multi_get_string(nav_name)) {
18710 		return;
18711 	}
18712 
18713 	if (!multi_get_string(ship_name)) {
18714 		return;
18715 	}
18716 
18717 	AddNav_Ship(nav_name, ship_name, 0);
18718 }
18719 
18720 
18721 //text: del-nav
18722 //args: 1, Nav Name
del_nav(int node)18723 void del_nav(int node)
18724 {
18725 	char *nav_name = CTEXT(node);
18726 	DelNavPoint(nav_name);
18727 
18728 	multi_start_callback();
18729 	multi_send_string(nav_name);
18730 	multi_end_callback();
18731 
18732 }
18733 
multi_del_nav()18734 void multi_del_nav()
18735 {
18736 	char nav_name[TOKEN_LENGTH];
18737 
18738 	if (!multi_get_string(nav_name)) {
18739 		return;
18740 	}
18741 
18742 	DelNavPoint(nav_name);
18743 }
18744 
18745 //text: use-nav-cinematics
18746 //args: 1, boolean enable/disable
set_use_ap_cinematics(int node)18747 void set_use_ap_cinematics(int node)
18748 {
18749 	if (is_sexp_true(node))
18750 	{
18751 		The_mission.flags |= MISSION_FLAG_USE_AP_CINEMATICS;
18752 	}
18753 	else
18754 	{
18755 		The_mission.flags &= ~MISSION_FLAG_USE_AP_CINEMATICS;
18756 	}
18757 }
18758 
18759 //text: use-autopilot
18760 //args: 1, boolean enable/disable
set_use_ap(int node)18761 void set_use_ap(int node)
18762 {
18763 	if (is_sexp_true(node))
18764 	{
18765 		The_mission.flags &= ~MISSION_FLAG_DEACTIVATE_AP;
18766 	}
18767 	else
18768 	{
18769 		The_mission.flags |= MISSION_FLAG_DEACTIVATE_AP;
18770 	}
18771 }
18772 
18773 //text: hide-nav
18774 //args: 1, Nav Name
hide_nav(int node)18775 void hide_nav(int node)
18776 {
18777 	char *nav_name = CTEXT(node);
18778 	Nav_Set_Hidden(nav_name);
18779 }
18780 
18781 //text: restrict-nav
18782 //args: 1, nav name
restrict_nav(int node)18783 void restrict_nav(int node)
18784 {
18785 	char *nav_name = CTEXT(node);
18786 	Nav_Set_NoAccess(nav_name);
18787 }
18788 
18789 //text: unhide-nav
18790 //args: 1, Nav name
unhide_nav(int node)18791 void unhide_nav(int node)
18792 {
18793 	char *nav_name = CTEXT(node);
18794 	Nav_UnSet_Hidden(nav_name);
18795 }
18796 
18797 //text: unrestrict-nav
18798 //args: 1, nav name
unrestrict_nav(int node)18799 void unrestrict_nav(int node)
18800 {
18801 	char *nav_name = CTEXT(node);
18802 	Nav_UnSet_NoAccess(nav_name);
18803 
18804 }
18805 
18806 //text: set-nav-visited
18807 //args: 1, Nav Name
set_nav_visited(int node)18808 void set_nav_visited(int node)
18809 {
18810 	char *nav_name = CTEXT(node);
18811 	Nav_Set_Visited(nav_name);
18812 }
18813 
18814 //text: unset-nav-visited
18815 //args: 1, Nav Name
unset_nav_visited(int node)18816 void unset_nav_visited(int node)
18817 {
18818 	char *nav_name = CTEXT(node);
18819 	Nav_UnSet_Visited(nav_name);
18820 }
18821 
18822 
18823 //text: is-nav-visited
18824 //args: 1, Nav Name
18825 //rets: true/false
is_nav_visited(int node)18826 int is_nav_visited(int node)
18827 {
18828 	char *nav_name = CTEXT(node);
18829 	return IsVisited(nav_name);
18830 }
18831 
18832 
18833 //text: is-nav_linked
18834 //args: 1, Ship name
18835 //rets: true/false
is_nav_linked(int node)18836 int is_nav_linked(int node)
18837 {
18838 	char *ship_name = CTEXT(node);
18839 	for (int i = 0; i < MAX_SHIPS; i++)
18840 	{
18841 		if (Ships[i].objnum != -1 && !stricmp(Ships[i].ship_name, ship_name))
18842 		{
18843 			return (Ships[i].flags2 & SF2_NAVPOINT_CARRY) != 0;
18844 		}
18845 	}
18846 	return 0;
18847 }
18848 
18849 //text: distance-to-nav
18850 //args: 1, Nav Name
18851 //rets: distance to nav
distance_to_nav(int node)18852 int distance_to_nav(int node)
18853 {
18854 	char *nav_name = CTEXT(node);
18855 	return DistanceTo(nav_name);
18856 }
18857 
select_nav(int node)18858 void select_nav(int node)
18859 {
18860 	char *nav_name = CTEXT(node);
18861 	SelectNav(nav_name);
18862 }
18863 
unselect_nav()18864 void unselect_nav()
18865 {
18866 	DeselectNav();
18867 }
18868 
18869 
18870 //*************************************************************************************************
18871 
18872 
sexp_is_tagged(int node)18873 int sexp_is_tagged(int node)
18874 {
18875 	int sindex;
18876 
18877 	// get the firing ship
18878 	sindex = ship_name_lookup(CTEXT(node));
18879 	if(sindex < 0){
18880 		return SEXP_FALSE;
18881 	}
18882 	if(Ships[sindex].objnum < 0){
18883 		return SEXP_FALSE;
18884 	}
18885 	object *caller = &Objects[Ships[sindex].objnum];
18886 	if(ship_is_tagged(caller)) { // This line and the one above were added.
18887 		return SEXP_TRUE;
18888 	}
18889 
18890 	// not tagged
18891 	return SEXP_FALSE;
18892 }
18893 
18894 // Joint effort of Sesquipedalian and Goober5000.  Sesq found the code, mucked around making
18895 // sexps with it and learned things, Goober taught Sesq and made the sexp work properly. =D
18896 // Returns true so long as the player has held a missile lock for the specified time.
18897 // If the optional ship and/or ship's subsystem are specified, returns true when that
18898 // has been locked onto, but otherwise returns as long as anything has been locked onto.
sexp_missile_locked(int node)18899 int sexp_missile_locked(int node)
18900 {
18901 	int z;
18902 
18903 	// if we aren't targeting anything, it's false
18904 	if ((Players_target == -1) || (Players_target == UNINITIALIZED))
18905 		return SEXP_FALSE;
18906 
18907 	// if we aren't locked on to anything, it's false
18908 	if (!Players_mlocked)
18909 		return SEXP_FALSE;
18910 
18911 	// do we have a specific ship?
18912 	if (CDR(node) != -1)
18913 	{
18914 		// if we're not targeting the specific ship, it's false
18915 		if (stricmp(Ships[Objects[Players_target].instance].ship_name, CTEXT(CDR(node))))
18916 			return SEXP_FALSE;
18917 
18918 		// do we have a specific subsystem?
18919 		if (CDR(CDR(node)) != -1)
18920 		{
18921 			// if we aren't targeting a subsystem at all, it's false
18922 			if (!Player_ai->targeted_subsys)
18923 				return SEXP_FALSE;
18924 
18925 			// if we're not targeting the specific subsystem, it's false
18926 			if (subsystem_stricmp(Player_ai->targeted_subsys->system_info->subobj_name, CTEXT(CDR(CDR(node)))))
18927 				return SEXP_FALSE;
18928 		}
18929 	}
18930 
18931 	// if we've gotten this far, we must have satisfied whatever conditions the sexp imposed
18932 	// finally, test if we've locked for a certain period of time
18933 	z = eval_num(node) * 1000;
18934 	if (timestamp_has_time_elapsed(Players_mlocked_timestamp, z))
18935 	{
18936 		return SEXP_TRUE;
18937 	}
18938 
18939 	return SEXP_FALSE;
18940 }
18941 
sexp_is_player(int node)18942 int sexp_is_player (int node)
18943 {
18944 	int sindex, np_index;
18945 	p_object *p_objp;
18946 
18947 	int standard_check = is_sexp_true(node);
18948 
18949 	if (!(Game_mode & GM_MULTIPLAYER)){
18950 		sindex = ship_name_lookup(CTEXT(CDR(node)));
18951 
18952 		// There can only be one player ship in singleplayer so if more than one ship is specifed the sexp is false
18953 		if (CDDR(node) < 0 ) {
18954 			return SEXP_FALSE;
18955 		}
18956 
18957 		if(sindex >= 0){
18958 			if(Player_obj == &Objects[Ships[sindex].objnum]){
18959 				if (standard_check) {
18960 					if (Player_use_ai) {
18961 						return SEXP_FALSE;
18962 					}
18963 				}
18964 				return SEXP_TRUE;
18965 			}
18966 		}
18967 		return SEXP_FALSE;
18968 	}
18969 
18970 	// For multiplayer we need to decide what to do about respawning players
18971 	else {
18972 		node = CDR(node);
18973 		while (node >= 0) {
18974 			// reset the netplayer index
18975 			np_index = -1;
18976 
18977 			sindex = ship_name_lookup(CTEXT(node));
18978 			if(sindex >= 0){
18979 				if(Ships[sindex].objnum >= 0) {
18980 					// try and find the player
18981 					np_index = multi_find_player_by_object(&Objects[Ships[sindex].objnum]);
18982 				}
18983 			}
18984 
18985 			if (standard_check && np_index < 0) {
18986 				// Respawning ships don't have an objnum so we need to take a different approach
18987 				p_objp = mission_parse_get_arrival_ship(CTEXT(node));
18988 				if (p_objp != NULL) {
18989 					np_index = multi_find_player_by_parse_object(p_objp);
18990 				}
18991 			}
18992 
18993 			// if we couldn't find a valid netplayer index then the ship isn't a player
18994 			if((np_index < 0) || (np_index >= MAX_PLAYERS)){
18995 				return SEXP_FALSE;
18996 			}
18997 
18998 			node = CDR(node);
18999 		}
19000 
19001 		// if we reached this far they all checked out
19002 		return SEXP_TRUE;
19003 	}
19004 }
19005 
sexp_set_respawns(int node)19006 void sexp_set_respawns(int node)
19007 {
19008 	int num_respawns;
19009 	p_object *p_objp;
19010 
19011 	// we're wasting our time if you can't respawn
19012 	if (!(Game_mode & GM_MULTIPLAYER)) {
19013 		return;
19014 	}
19015 
19016 	num_respawns = eval_num(node);
19017 
19018 	node = CDR(node);
19019 
19020 	// send the information to clients
19021 	multi_start_callback();
19022 	multi_send_int(num_respawns);
19023 
19024 	while (node != -1) {
19025 		// get the parse object for the ship
19026 		p_objp = mission_parse_get_arrival_ship(CTEXT(node));
19027 		if (p_objp != NULL) {
19028 			p_objp->respawn_count = num_respawns;
19029 		}
19030 
19031 		multi_send_string(CTEXT(node));
19032 
19033 		node = CDR(node);
19034 	}
19035 
19036 	multi_end_callback();
19037 }
19038 
multi_sexp_set_respawns()19039 void multi_sexp_set_respawns()
19040 {
19041 	p_object *p_objp;
19042 	int num_respawns;
19043 	char parse_name[NAME_LENGTH];
19044 
19045 	multi_get_int(num_respawns);
19046 	while (multi_get_string(parse_name)) {
19047 		// get the parse object for the ship
19048 		p_objp = mission_parse_get_arrival_ship(parse_name);
19049 		if (p_objp != NULL) {
19050 			p_objp->respawn_count = num_respawns;
19051 		}
19052 	}
19053 }
19054 
19055 // helper function for the remove-weapons SEXP
actually_remove_weapons(int weapon_info_index)19056 void actually_remove_weapons(int weapon_info_index)
19057 {
19058 	int i;
19059 
19060 	for (i = 0; i<MAX_WEAPONS; i++) {
19061 		// weapon doesn't match the optional weapon
19062 		if ((weapon_info_index > -1) && (Weapons[i].weapon_info_index != weapon_info_index)) {
19063 			continue;
19064 		}
19065 
19066 		if (Weapons[i].objnum >= 0) {
19067 			Objects[Weapons[i].objnum].flags |= OF_SHOULD_BE_DEAD;
19068 		}
19069 	}
19070 }
19071 
sexp_remove_weapons(int node)19072 void sexp_remove_weapons(int node)
19073 {
19074 	int weapon_info_index = -1;
19075 
19076 	// if we have the optional argument, read it in
19077 	if (node >= 0) {
19078 		weapon_info_index = weapon_info_lookup(CTEXT(node));
19079 		if (weapon_info_index == -1) {
19080 			char *buf = CTEXT(node);
19081 			mprintf(("Remove-weapons attempted to remove %s. Weapon not found. Remove-weapons will remove all weapons currently in the mission\n", buf));
19082 		}
19083 	}
19084 
19085 	actually_remove_weapons(weapon_info_index);
19086 
19087 	// send the information to clients
19088 	multi_start_callback();
19089 	multi_send_int(weapon_info_index);
19090 	multi_end_callback();
19091 }
19092 
multi_sexp_remove_weapons()19093 void multi_sexp_remove_weapons()
19094 {
19095 	int weapon_info_index = -1;
19096 
19097 	multi_get_int(weapon_info_index);
19098 
19099 	actually_remove_weapons(weapon_info_index);
19100 }
19101 
19102 
sexp_return_player_data(int node,int type)19103 int sexp_return_player_data(int node, int type)
19104 {
19105 	int sindex, np_index = -1;
19106 	player *p = NULL;
19107 	p_object *p_objp;
19108 
19109 	sindex = ship_name_lookup(CTEXT(node));
19110 
19111 	if(Game_mode & GM_MULTIPLAYER){
19112 		if(sindex >= 0){
19113 			if(Ships[sindex].objnum >= 0) {
19114 				// try and find the player
19115 				np_index = multi_find_player_by_object(&Objects[Ships[sindex].objnum]);
19116 			}
19117 		}
19118 
19119 		if (np_index < 0) {
19120 			// Respawning ships don't have an objnum so we need to take a different approach
19121 			p_objp = mission_parse_get_arrival_ship(CTEXT(node));
19122 			np_index = multi_find_player_by_parse_object(p_objp);
19123 		}
19124 
19125 		if((np_index >= 0) && (np_index < MAX_PLAYERS)){
19126 			p = Net_players[np_index].m_player;
19127 		}
19128 	}
19129 	// if we're in single player, we're only concerned with ourself
19130 	else {
19131 		if(sindex < 0){
19132 			return 0;
19133 		}
19134 		if(Ships[sindex].objnum < 0){
19135 			return 0;
19136 		}
19137 
19138 		if(Player_obj == &Objects[Ships[sindex].objnum]){
19139 			p = Player;
19140 		}
19141 	}
19142 
19143 	// now, if we have a valid player, return his kills
19144 	if(p != NULL) {
19145 		switch (type) {
19146 			case OP_NUM_KILLS:
19147 				return p->stats.m_kill_count_ok;
19148 
19149 			case OP_NUM_ASSISTS:
19150 				return p->stats.m_assists;
19151 
19152 			case OP_SHIP_SCORE:
19153 				return p->stats.m_score;
19154 
19155 			case OP_SHIP_DEATHS:
19156 				return p->stats.m_player_deaths;
19157 
19158 			case OP_RESPAWNS_LEFT:
19159 				// Dogfight missions have no respawn limit
19160 				if (MULTI_NOT_DOGFIGHT) {
19161 					if ( Net_players[np_index].flags & NETINFO_FLAG_RESPAWNING) {
19162 						// since the player hasn't actually respawned yet he hasn't used up a number or spawns equal to his deaths
19163 						// so add an extra life back.
19164 						return Netgame.respawn - p->stats.m_player_deaths + 1;
19165 					}
19166 					else {
19167 						return Netgame.respawn - p->stats.m_player_deaths;
19168 					}
19169 				}
19170 				break;
19171 
19172 			default:
19173 				Int3();
19174 		}
19175 	}
19176 	// AI ships also have a respawn count so we can return valid data for that at least
19177 	else if ( (Game_mode & GM_MULTIPLAYER) && (type == OP_SHIP_DEATHS || type == OP_RESPAWNS_LEFT ) ) {
19178 		p_objp = mission_parse_get_arrival_ship(CTEXT(node));
19179 		if (p_objp == NULL) {
19180 			return 0;
19181 		}
19182 
19183 		if (p_objp->flags & P_OF_PLAYER_START) {
19184 			switch (type) {
19185 				case OP_SHIP_DEATHS:
19186 					// when an AI ship is finally killed its respawn count won't be updated so get the number of deaths
19187 					// from the log instead
19188 					return mission_log_get_count(LOG_SHIP_DESTROYED, CTEXT(node), NULL) + mission_log_get_count(LOG_SELF_DESTRUCTED, CTEXT(node), NULL);
19189 
19190 				case OP_RESPAWNS_LEFT:
19191 					return Netgame.respawn - p_objp->respawn_count;
19192 
19193 				default:
19194 					Int3();
19195 			}
19196 		}
19197 	}
19198 
19199 	// AI ships
19200 	return 0;
19201 }
19202 
sexp_num_type_kills(int node)19203 int sexp_num_type_kills(int node)
19204 {
19205 	int sindex, st_index;
19206 	int idx, total;
19207 	player *p = NULL;
19208 
19209 	// get the ship we're interested in
19210 	sindex = ship_name_lookup(CTEXT(node));
19211 	if(sindex < 0){
19212 		return 0;
19213 	}
19214 	if(Ships[sindex].objnum < 0){
19215 		return 0;
19216 	}
19217 
19218 	int np_index;
19219 
19220 	// in multiplayer, search through all players
19221 	if(Game_mode & GM_MULTIPLAYER){
19222 		// try and find the player
19223 		np_index = multi_find_player_by_object(&Objects[Ships[sindex].objnum]);
19224 		if((np_index >= 0) && (np_index < MAX_PLAYERS)){
19225 			p = Net_players[np_index].m_player;
19226 		}
19227 	}
19228 	// if we're in single player, we're only concerned with ourself
19229 	else {
19230 		// me
19231 		if(Player_obj == &Objects[Ships[sindex].objnum]){
19232 			p = Player;
19233 		}
19234 	}
19235 
19236 	// bad
19237 	if(p == NULL){
19238 		return 0;
19239 	}
19240 
19241 	// lookup ship type name
19242 	st_index = ship_type_name_lookup(CTEXT(CDR(node)));
19243 	if(st_index < 0){
19244 		return 0;
19245 	}
19246 
19247 	// look stuff up
19248 	total = 0;
19249 	for(idx=0; idx<Num_ship_classes; idx++){
19250 		if((p->stats.m_okKills[idx] > 0) && ship_class_query_general_type(idx)==st_index){
19251 			total += p->stats.m_okKills[idx];
19252 		}
19253 	}
19254 
19255 	// total
19256 	return total;
19257 }
19258 
sexp_num_class_kills(int node)19259 int sexp_num_class_kills(int node)
19260 {
19261 	int sindex, si_index;
19262 	player *p = NULL;
19263 
19264 	// get the ship we're interested in
19265 	sindex = ship_name_lookup(CTEXT(node));
19266 	if(sindex < 0){
19267 		return 0;
19268 	}
19269 	if(Ships[sindex].objnum < 0){
19270 		return 0;
19271 	}
19272 
19273 	int np_index;
19274 
19275 	// in multiplayer, search through all players
19276 	if(Game_mode & GM_MULTIPLAYER){
19277 		// try and find the player
19278 		np_index = multi_find_player_by_object(&Objects[Ships[sindex].objnum]);
19279 		if((np_index >= 0) && (np_index < MAX_PLAYERS)){
19280 			p = Net_players[np_index].m_player;
19281 		}
19282 	}
19283 	// if we're in single player, we're only concerned with ourself
19284 	else {
19285 		// me
19286 		if(Player_obj == &Objects[Ships[sindex].objnum]){
19287 			p = Player;
19288 		}
19289 	}
19290 
19291 	// bad
19292 	if(p == NULL){
19293 		return 0;
19294 	}
19295 
19296 	// get the ship type we're looking for
19297 	si_index = ship_info_lookup(CTEXT(CDR(node)));
19298 	if((si_index < 0) || (si_index > Num_ship_classes)){
19299 		return 0;
19300 	}
19301 
19302 	// return the count
19303 	return p->stats.m_okKills[si_index];
19304 }
19305 
sexp_subsys_set_random(int node)19306 void sexp_subsys_set_random(int node)
19307 {
19308 	int sindex, low, high, n, idx, rand, exclusion_list[MAX_MODEL_SUBSYSTEMS];
19309 	ship_subsys *subsys;
19310 	ship *shipp;
19311 
19312 	// get ship
19313 	sindex = ship_name_lookup(CTEXT(node));
19314 	if(sindex < 0){
19315 		return;
19316 	}
19317 	if(Ships[sindex].objnum < 0){
19318 		return;
19319 	}
19320 	shipp = &Ships[sindex];
19321 
19322 	// get low
19323 	low = eval_num(CDR(node));
19324 	if (low < 0) {
19325 		low = 0;
19326 	}
19327 
19328 	// get high
19329 	high = eval_num(CDR(CDR(node)));
19330 	if (high > 100) {
19331 		high = 100;
19332 	}
19333 
19334 	if (low > high) {
19335 		Int3();
19336 		return;
19337 	}
19338 
19339 	n = CDR(CDR(CDR(node)));
19340 
19341 	// init exclusion list
19342 	memset(exclusion_list, 0, sizeof(int) * Ship_info[shipp->ship_info_index].n_subsystems);
19343 
19344 	// get exclusion list
19345 	while( n != -1) {
19346 		int exclude_index = ship_get_subsys_index(shipp, CTEXT(n), 0);
19347 		if (exclude_index >= 0) {
19348 			exclusion_list[exclude_index] = 1;
19349 		}
19350 
19351 		n = CDR(n);
19352 	}
19353 
19354 	// apply to all others
19355 	for (idx=0; idx<Ship_info[shipp->ship_info_index].n_subsystems; idx++) {
19356 		if ( exclusion_list[idx] == 0 ) {
19357 			// get non excluded subsystem
19358 			subsys = ship_get_indexed_subsys(shipp, idx, NULL);
19359 			if (subsys == NULL) {
19360 				nprintf(("Warning", "Nonexistent subsystem for index %d on ship %s for sabotage subsystem\n", idx, shipp->ship_name));
19361 				continue;
19362 			}
19363 
19364 			// randomize its hit points
19365 			rand = rand_internal(low, high);
19366 			subsys->current_hits = 0.01f * rand * subsys->max_hits;
19367 		}
19368 	}
19369 }
19370 
sexp_supernova_start(int node)19371 void sexp_supernova_start(int node)
19372 {
19373 	supernova_start(eval_num(node));
19374 }
19375 
sexp_supernova_stop(int node)19376 void sexp_supernova_stop(int node)
19377 {
19378 	supernova_stop();
19379 }
19380 
sexp_is_secondary_selected(int node)19381 int sexp_is_secondary_selected(int node)
19382 {
19383 	int sindex;
19384 	int bank;
19385 	ship *shipp;
19386 
19387 	// lookup ship
19388 	sindex = ship_name_lookup(CTEXT(node));
19389 	if(sindex < 0){
19390 		return SEXP_FALSE;
19391 	}
19392 	if(Ships[sindex].objnum < 0){
19393 		return SEXP_FALSE;
19394 	}
19395 	shipp = &Ships[sindex];
19396 
19397 	// bogus value?
19398 	bank = eval_num(CDR(node));
19399 	if(bank >= shipp->weapons.num_secondary_banks){
19400 		return SEXP_FALSE;
19401 	}
19402 
19403 	// is this the bank currently selected
19404 	if( bank == shipp->weapons.current_secondary_bank ){
19405 		return SEXP_TRUE;
19406 	}
19407 
19408 	// nope
19409 	return SEXP_FALSE;
19410 }
19411 
sexp_is_primary_selected(int node)19412 int sexp_is_primary_selected(int node)
19413 {
19414 	int sindex;
19415 	int bank;
19416 	ship *shipp;
19417 
19418 	// lookup ship
19419 	sindex = ship_name_lookup(CTEXT(node));
19420 	if(sindex < 0){
19421 		return SEXP_FALSE;
19422 	}
19423 	if(Ships[sindex].objnum < 0){
19424 		return SEXP_FALSE;
19425 	}
19426 	shipp = &Ships[sindex];
19427 
19428 	// bogus value?
19429 	bank = eval_num(CDR(node));
19430 	if(bank >= shipp->weapons.num_primary_banks){
19431 		return SEXP_FALSE;
19432 	}
19433 
19434 	// is this the bank currently selected
19435 	if( (bank == shipp->weapons.current_primary_bank) || (shipp->flags & SF_PRIMARY_LINKED) ){
19436 		return SEXP_TRUE;
19437 	}
19438 
19439 	// nope
19440 	return SEXP_FALSE;
19441 }
19442 
19443 //	Return SEXP_TRUE if quadrant quadnum is near max.
shield_quad_near_max(int quadnum)19444 int shield_quad_near_max(int quadnum)
19445 {
19446 	if (quadnum >= Player_obj->n_quadrants)
19447 		return SEXP_FALSE;
19448 
19449 	float	remaining = 0.0f;
19450 	for (int i=0; i<Player_obj->n_quadrants; i++) {
19451 		if (i == quadnum){
19452 			continue;
19453 		}
19454 		remaining += Player_obj->shield_quadrant[i];
19455 	}
19456 
19457 	if ((remaining < 2.0f) || (Player_obj->shield_quadrant[quadnum] > get_max_shield_quad(Player_obj) - 5.0f)) {
19458 		return SEXP_TRUE;
19459 	} else {
19460 		return SEXP_FALSE;
19461 	}
19462 }
19463 
19464 //	Return truth value for special SEXP.
19465 //	Used in training#5, perhaps in other missions.
process_special_sexps(int index)19466 int process_special_sexps(int index)
19467 {
19468 	switch (index) {
19469 	case 0:	//	Ship "Freighter 1" is aspect locked by player.
19470 		if (Player_ai->target_objnum != -1) {
19471 			if (!(stricmp(Ships[Objects[Player_ai->target_objnum].instance].ship_name, "Freighter 1"))) {
19472 				if (Player_ai->current_target_is_locked)
19473 					return SEXP_TRUE;
19474 			}
19475 		}
19476 		return SEXP_FALSE;
19477 		break;
19478 
19479 	case 1:	//	Fired Interceptors
19480 		object	*objp;
19481 		for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
19482 			if (objp->type == OBJ_WEAPON) {
19483 				if (!stricmp(Weapon_info[Weapons[objp->instance].weapon_info_index].name, "Interceptor#weak")) {
19484 					int target = Weapons[objp->instance].target_num;
19485 					if (target != -1) {
19486 						if (Objects[target].type == OBJ_SHIP) {
19487 							if (!(stricmp(Ships[Objects[target].instance].ship_name, "Freighter 1")))
19488 								return SEXP_TRUE;
19489 						}
19490 					}
19491 				}
19492 			}
19493 		}
19494 		return SEXP_FALSE;
19495 
19496 	case 2:	//	Ship "Freighter 1", subsystem "Weapons" is aspect locked by player.
19497 		if (Player_ai->target_objnum != -1) {
19498 			if (!(stricmp(Ships[Objects[Player_ai->target_objnum].instance].ship_name, "Freighter 1"))) {
19499 				if (!(subsystem_stricmp(Player_ai->targeted_subsys->system_info->name, "Weapons"))) {
19500 					if (Player_ai->current_target_is_locked){
19501 						return SEXP_TRUE;
19502 					}
19503 				}
19504 			}
19505 		}
19506 		return SEXP_FALSE;
19507 		break;
19508 
19509 	case 3:	//	Player ship suffering shield damage on front.
19510 		if (!(Player_ship->flags2 & SIF2_MODEL_POINT_SHIELDS)) {
19511 			apply_damage_to_shield(Player_obj, FRONT_QUAD, 10.0f);
19512 			hud_shield_quadrant_hit(Player_obj, FRONT_QUAD);
19513 			return SEXP_TRUE;
19514 		} else {
19515 			nprintf(("Warning", "Shield-related Special-check SEXPs do not work on ship %s because it uses model point shields.\n", Player_ship->ship_name));
19516 			return SEXP_FALSE;
19517 		}
19518 		break;
19519 
19520 	case 4:	//	Player ship suffering much damage.
19521 		if (!(Player_ship->flags2 & SIF2_MODEL_POINT_SHIELDS)) {
19522 			nprintf(("AI", "Frame %i\n", Framecount));
19523 			apply_damage_to_shield(Player_obj, FRONT_QUAD, 10.0f);
19524 			hud_shield_quadrant_hit(Player_obj, FRONT_QUAD);
19525 			if (Player_obj->shield_quadrant[FRONT_QUAD] < 2.0f)
19526 				return SEXP_TRUE;
19527 			else
19528 				return SEXP_FALSE;
19529 		} else {
19530 			nprintf(("Warning", "Shield-related Special-check SEXPs do not work on ship %s because it uses model point shields.\n", Player_ship->ship_name));
19531 			return SEXP_FALSE;
19532 		}
19533 		break;
19534 
19535 	case 5:	//	Player's shield is quick repaired
19536 		if (!(Player_ship->flags2 & SIF2_MODEL_POINT_SHIELDS)) {
19537 			nprintf(("AI", "Frame %i, recharged to %7.3f\n", Framecount, Player_obj->shield_quadrant[FRONT_QUAD]));
19538 
19539 			apply_damage_to_shield(Player_obj, FRONT_QUAD, -flFrametime*200.0f);
19540 
19541 			if (Player_obj->shield_quadrant[FRONT_QUAD] > get_max_shield_quad(Player_obj))
19542 			Player_obj->shield_quadrant[FRONT_QUAD] = get_max_shield_quad(Player_obj);
19543 
19544 			if (Player_obj->shield_quadrant[FRONT_QUAD] > Player_obj->shield_quadrant[(FRONT_QUAD+1)%DEFAULT_SHIELD_SECTIONS] - 2.0f)
19545 				return SEXP_TRUE;
19546 			else
19547 				return SEXP_FALSE;
19548 		} else {
19549 			nprintf(("Warning", "Shield-related Special-check SEXPs do not work on ship %s because it uses model point shields.\n", Player_ship->ship_name));
19550 			return SEXP_FALSE;
19551 		}
19552 		break;
19553 
19554 	case 6:	//	3 of player's shield quadrants are reduced to 0.
19555 		if (!(Player_ship->flags2 & SIF2_MODEL_POINT_SHIELDS)) {
19556 			Player_obj->shield_quadrant[1] = 1.0f;
19557 			Player_obj->shield_quadrant[2] = 1.0f;
19558 			Player_obj->shield_quadrant[3] = 1.0f;
19559 			hud_shield_quadrant_hit(Player_obj, FRONT_QUAD);
19560 		} else {
19561 			nprintf(("Warning", "Shield-related Special-check SEXPs do not work on ship %s because it uses model point shields.\n", Player_ship->ship_name));
19562 			return SEXP_FALSE;
19563 		}
19564 		return SEXP_TRUE;
19565 
19566 	case 7:	//	Make sure front quadrant has been maximized, or close to it.
19567 		if (!(Player_ship->flags2 & SIF2_MODEL_POINT_SHIELDS)) {
19568 			if (shield_quad_near_max(FRONT_QUAD)) return SEXP_TRUE; else return SEXP_FALSE;
19569 		} else {
19570 			nprintf(("Warning", "Shield-related Special-check SEXPs do not work on ship %s because it uses model point shields.\n", Player_ship->ship_name));
19571 			return SEXP_FALSE;
19572 		}
19573 		break;
19574 
19575 	case 8:	//	Make sure rear quadrant has been maximized, or close to it.
19576 		if (!(Player_ship->flags2 & SIF2_MODEL_POINT_SHIELDS)) {
19577 			if (shield_quad_near_max(REAR_QUAD)) return SEXP_TRUE; else return SEXP_FALSE;
19578 		} else {
19579 			nprintf(("Warning", "Shield-related Special-check SEXPs do not work on ship %s because it uses model point shields.\n", Player_ship->ship_name));
19580 			return SEXP_FALSE;
19581 		}
19582 		break;
19583 
19584 	case 9:	//	Zero left and right quadrants in preparation for maximizing rear quadrant.
19585 		if (!(Player_ship->flags2 & SIF2_MODEL_POINT_SHIELDS)) {
19586 			Player_obj->shield_quadrant[LEFT_QUAD] = 0.0f;
19587 			Player_obj->shield_quadrant[RIGHT_QUAD] = 0.0f;
19588 			hud_shield_quadrant_hit(Player_obj, LEFT_QUAD);
19589 			return SEXP_TRUE;
19590 		} else {
19591 			nprintf(("Warning", "Shield-related Special-check SEXPs do not work on ship %s because it uses model point shields.\n", Player_ship->ship_name));
19592 			return SEXP_FALSE;
19593 		}
19594 		break;
19595 
19596 	case 10:	//	Return true if player is low on Interceptors.
19597 		if (Player_ship->weapons.secondary_bank_ammo[0] + Player_ship->weapons.secondary_bank_ammo[1] < 8)
19598 			return SEXP_TRUE;
19599 		else
19600 			return SEXP_FALSE;
19601 		break;
19602 
19603 	case 11:	//	Return true if player has plenty of Interceptors.
19604 		if (Player_ship->weapons.secondary_bank_ammo[0] + Player_ship->weapons.secondary_bank_ammo[1] >= 8)
19605 			return SEXP_TRUE;
19606 		else
19607 			return SEXP_FALSE;
19608 		break;
19609 
19610 	case 12:	//	Return true if player is low on Interceptors.
19611 		if (Player_ship->weapons.secondary_bank_ammo[0] + Player_ship->weapons.secondary_bank_ammo[1] < 4)
19612 			return SEXP_TRUE;
19613 		else
19614 			return SEXP_FALSE;
19615 		break;
19616 
19617 	case 13:	// Zero front shield quadrant.  Added for Jim Boone on August 26, 1999 by MK.
19618 		if (!(Player_ship->flags2 & SIF2_MODEL_POINT_SHIELDS)) {
19619 			Player_obj->shield_quadrant[FRONT_QUAD] = 0.0f;
19620 			hud_shield_quadrant_hit(Player_obj, FRONT_QUAD);
19621 			return SEXP_TRUE;
19622 		} else {
19623 			nprintf(("Warning", "Shield-related Special-check SEXPs do not work on ship %s because it uses model point shields.\n", Player_ship->ship_name));
19624 			return SEXP_FALSE;
19625 		}
19626 		break;
19627 
19628 	case 100:	//	Return true if player is out of countermeasures.
19629 		if (Player_ship->cmeasure_count <= 0)
19630 			return SEXP_TRUE;
19631 		else
19632 			return SEXP_FALSE;
19633 
19634 	default:
19635 		Int3();	//	Unsupported node type.
19636 	}
19637 
19638 	return SEXP_FALSE;
19639 }
19640 
19641 // Karajorma / Goober5000
sexp_string_to_int(int n)19642 int sexp_string_to_int(int n)
19643 {
19644 	bool first_ch = true;
19645 	char *ch, *buf_ch, buf[TOKEN_LENGTH];
19646 	Assert (n != -1);
19647 
19648 	// copy all numeric characters to buf
19649 	// also, copy a sign symbol if we haven't copied numbers yet
19650 	buf_ch = buf;
19651 	for (ch = CTEXT(n); *ch != 0; ch++)
19652 	{
19653 		if ((first_ch && (*ch == '-' || *ch == '+')) || strchr("0123456789", *ch))
19654 		{
19655 			*buf_ch = *ch;
19656 			buf_ch++;
19657 
19658 			first_ch = false;
19659 		}
19660 
19661 		// don't save the fractional parts of decimal numbers
19662 		if (*ch == '.')
19663 			break;
19664 	}
19665 
19666 	// terminate string
19667 	*buf_ch = '\0';
19668 
19669 	return atoi(buf);
19670 }
19671 
19672 // Goober5000
sexp_int_to_string(int n)19673 void sexp_int_to_string(int n)
19674 {
19675 	int i, sexp_variable_index;
19676 	char new_text[TOKEN_LENGTH];
19677 
19678 	// Only do single player or multi host
19679 	if ( MULTIPLAYER_CLIENT )
19680 		return;
19681 
19682 	i = eval_num(n);
19683 	n = CDR(n);
19684 
19685 	// get sexp_variable index
19686 	Assert(Sexp_nodes[n].first == -1);
19687 	sexp_variable_index = atoi(Sexp_nodes[n].text);
19688 
19689 	// verify variable set
19690 	Assert(Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_SET);
19691 
19692 	// check variable type
19693 	if (!(Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_STRING))
19694 	{
19695 		Warning(LOCATION, "Cannot assign a string to a non-string variable!");
19696 		return;
19697 	}
19698 
19699 	// write string
19700 	sprintf(new_text, "%d", i);
19701 
19702 	// assign to variable
19703 	sexp_modify_variable(new_text, sexp_variable_index);
19704 }
19705 
19706 // Goober5000
sexp_string_concatenate(int n)19707 void sexp_string_concatenate(int n)
19708 {
19709 	int sexp_variable_index;
19710 	char new_text[TOKEN_LENGTH * 2];
19711 
19712 	// Only do single player or multi host
19713 	if ( MULTIPLAYER_CLIENT )
19714 		return;
19715 
19716 	char *str1 = CTEXT(n);
19717 	n = CDR(n);
19718 	char *str2 = CTEXT(n);
19719 	n = CDR(n);
19720 
19721 	// get sexp_variable index
19722 	Assert(Sexp_nodes[n].first == -1);
19723 	sexp_variable_index = atoi(Sexp_nodes[n].text);
19724 
19725 	// verify variable set
19726 	Assert(Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_SET);
19727 
19728 	// check variable type
19729 	if (!(Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_STRING))
19730 	{
19731 		Warning(LOCATION, "Cannot assign a string to a non-string variable!");
19732 		return;
19733 	}
19734 
19735 	// concatenate strings
19736 	strcpy_s(new_text, str1);
19737 	strcat_s(new_text, str2);
19738 
19739 	// check length
19740 	if (strlen(new_text) >= TOKEN_LENGTH)
19741 	{
19742 		Warning(LOCATION, "Concatenated string is too long and will be truncated.");
19743 		new_text[TOKEN_LENGTH] = 0;
19744 	}
19745 
19746 	// assign to variable
19747 	sexp_modify_variable(new_text, sexp_variable_index);
19748 }
19749 
19750 // Goober5000
sexp_string_get_length(int node)19751 int sexp_string_get_length(int node)
19752 {
19753 	return strlen(CTEXT(node));
19754 }
19755 
19756 // Goober5000
sexp_string_get_substring(int node)19757 void sexp_string_get_substring(int node)
19758 {
19759 	int n = node;
19760 	int sexp_variable_index;
19761 	char new_text[TOKEN_LENGTH];
19762 
19763 	// Only do single player or multi host
19764 	if ( MULTIPLAYER_CLIENT )
19765 		return;
19766 
19767 	char *parent = CTEXT(n);
19768 	n = CDR(n);
19769 	int pos = eval_num(n);
19770 	n = CDR(n);
19771 	int len = eval_num(n);
19772 	n = CDR(n);
19773 
19774 	// get sexp_variable index
19775 	Assert(Sexp_nodes[n].first == -1);
19776 	sexp_variable_index = atoi(Sexp_nodes[n].text);
19777 
19778 	// verify variable set
19779 	Assert(Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_SET);
19780 
19781 	// check variable type
19782 	if (!(Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_STRING))
19783 	{
19784 		Warning(LOCATION, "Cannot assign a string to a non-string variable!");
19785 		return;
19786 	}
19787 
19788 	int parent_len = strlen(parent);
19789 
19790 	// sanity
19791 	if (pos >= parent_len)
19792 	{
19793 		Warning(LOCATION, "( string-get-substring %s %d %d ) failed: starting position is larger than the string length!", parent, pos, len);
19794 		return;
19795 	}
19796 
19797 	// sanity
19798 	if (pos + len > parent_len)
19799 		len = parent_len - pos;
19800 
19801 	// copy substring
19802 	memset(new_text, 0, TOKEN_LENGTH);
19803 	strncpy(new_text, &parent[pos], len);
19804 
19805 	// assign to variable
19806 	sexp_modify_variable(new_text, sexp_variable_index);
19807 }
19808 
19809 // Goober5000
sexp_string_set_substring(int node)19810 void sexp_string_set_substring(int node)
19811 {
19812 	int n = node;
19813 	int sexp_variable_index;
19814 	char new_text[TOKEN_LENGTH * 2];
19815 
19816 	// Only do single player or multi host
19817 	if ( MULTIPLAYER_CLIENT )
19818 		return;
19819 
19820 	char *parent = CTEXT(n);
19821 	n = CDR(n);
19822 	int pos = eval_num(n);
19823 	n = CDR(n);
19824 	int len = eval_num(n);
19825 	n = CDR(n);
19826 	char *new_substring = CTEXT(n);
19827 	n = CDR(n);
19828 
19829 	// get sexp_variable index
19830 	Assert(Sexp_nodes[n].first == -1);
19831 	sexp_variable_index = atoi(Sexp_nodes[n].text);
19832 
19833 	// verify variable set
19834 	Assert(Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_SET);
19835 
19836 	// check variable type
19837 	if (!(Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_STRING))
19838 	{
19839 		Warning(LOCATION, "Cannot assign a string to a non-string variable!");
19840 		return;
19841 	}
19842 
19843 	int parent_len = strlen(parent);
19844 	int new_len = strlen(new_substring);
19845 
19846 	// sanity
19847 	if (pos >= parent_len)
19848 	{
19849 		Warning(LOCATION, "( string-set-substring %s %d %d %s ) failed: starting position is larger than the string length!", parent, pos, len, new_substring);
19850 		return;
19851 	}
19852 
19853 	// make the common case fast
19854 	if (len == 1 && new_len == 1)
19855 	{
19856 		strcpy_s(new_text, parent);
19857 		new_text[pos] = new_substring[0];
19858 	}
19859 	else
19860 	{
19861 		// sanity
19862 		if (pos + len > parent_len)
19863 			len = parent_len - pos;
19864 
19865 		// copy parent string up to the substring pos
19866 		strncpy(new_text, parent, pos);
19867 
19868 		// add new substring
19869 		strcpy(&new_text[pos], new_substring);
19870 
19871 		// add rest of parent string
19872 		strcat_s(new_text, &parent[pos + len]);
19873 
19874 		// check length
19875 		if (strlen(new_text) >= TOKEN_LENGTH)
19876 		{
19877 			Warning(LOCATION, "Concatenated string is too long and will be truncated.");
19878 			new_text[TOKEN_LENGTH] = 0;
19879 		}
19880 	}
19881 
19882 	// assign to variable
19883 	sexp_modify_variable(new_text, sexp_variable_index);
19884 }
19885 
19886 // breaks into the code or sends a warning
sexp_debug(int node)19887 void sexp_debug(int node)
19888 {
19889 	int i;
19890 	char *id;
19891 	char temp_buf[MESSAGE_LENGTH] = {""};
19892 
19893 	#ifdef NDEBUG
19894 	int no_release_message;
19895 	no_release_message = is_sexp_true(node);
19896 	#endif
19897 
19898 	node = CDR(node);
19899 	Assertion (node >= 0, "No message defined in debug SEXP");
19900 	id = CTEXT(node);
19901 
19902 	for (i=0; i<Num_messages; i++) {
19903 		// find the message
19904 		if ( !stricmp(id, Messages[i].name) ) {
19905 			//replace variables if necessary
19906 			strcpy_s(temp_buf, Messages[i].message);
19907 			sexp_replace_variable_names_with_values(temp_buf, MESSAGE_LENGTH);
19908 			break;
19909 		}
19910 	}
19911 
19912 	//send the message
19913 	#ifndef NDEBUG
19914 		Warning(LOCATION, temp_buf);
19915     #else
19916 	if (!no_release_message) {
19917 		Warning(LOCATION, temp_buf);
19918 	}
19919 	#endif
19920 }
19921 
19922 // custom sexp operator for handling misc training stuff
sexp_special_training_check(int node)19923 int sexp_special_training_check(int node)
19924 {
19925 	int num, rtn;
19926 
19927 	num = eval_num(node);
19928 	if (num == SPECIAL_CHECK_TRAINING_FAILURE)
19929 		return Training_failure ? SEXP_TRUE : SEXP_FALSE;
19930 
19931 	// To MK: do whatever you want with this number here.
19932 	rtn = process_special_sexps(eval_num(node));
19933 
19934 	return rtn;
19935 }
19936 
19937 // sexpression to flash a hud gauge.  gauge name is text valud of node
sexp_flash_hud_gauge(int node)19938 void sexp_flash_hud_gauge( int node )
19939 {
19940 	char *name;
19941 	int i;
19942 
19943 	name = CTEXT(node);
19944 	for (i = 0; i < NUM_HUD_GAUGES; i++ ) {
19945 		if ( !stricmp(HUD_gauge_text[i], name) ) {
19946 			hud_gauge_start_flash(i);	// call HUD function to flash gauge
19947 
19948 			multi_start_callback();
19949 			multi_send_int(i);
19950 			multi_end_callback();
19951 
19952 			break;
19953 		}
19954 	}
19955 }
19956 
multi_sexp_flash_hud_gauge()19957 void multi_sexp_flash_hud_gauge()
19958 {
19959 	int i;
19960 
19961 	if (multi_get_int(i)) {
19962 		hud_gauge_start_flash(i);
19963 	}
19964 }
19965 
sexp_set_training_context_fly_path(int node)19966 void sexp_set_training_context_fly_path(int node)
19967 {
19968 	waypoint_list *wp_list = find_matching_waypoint_list(CTEXT(node));
19969 	if (wp_list == NULL)
19970 		return;
19971 
19972 	Training_context |= TRAINING_CONTEXT_FLY_PATH;
19973 	Training_context_path = wp_list;
19974 	Training_context_distance = (float) atof(CTEXT(CDR(node)));
19975 	Training_context_goal_waypoint = 0;
19976 	Training_context_at_waypoint = -1;
19977 }
19978 
sexp_set_training_context_speed(int node)19979 void sexp_set_training_context_speed(int node)
19980 {
19981 	Training_context |= TRAINING_CONTEXT_SPEED;
19982 	Training_context_speed_min = eval_num(node);
19983 	Training_context_speed_max = eval_num(CDR(node));
19984 	Training_context_speed_set = 0;
19985 }
19986 
sexp_scramble_messages(int node,bool scramble)19987 void sexp_scramble_messages(int node, bool scramble)
19988 {
19989 	if (node < 0)
19990 	{
19991 		// scramble messages on player ship... this isn't multi compatible, but neither was the old version of the sexp
19992 		if (scramble)
19993 			Player_ship->flags2 |= SF2_SCRAMBLE_MESSAGES;
19994 		else
19995 			Player_ship->flags2 &= ~SF2_SCRAMBLE_MESSAGES;
19996 		return;
19997 	}
19998 
19999 	sexp_deal_with_ship_flag(node, true, 0, 0, 0, SF2_SCRAMBLE_MESSAGES, 0, P2_SF2_SCRAMBLE_MESSAGES, scramble, false, true);
20000 }
20001 
toggle_cutscene_bars(float delta_speed,int set)20002 void toggle_cutscene_bars(float delta_speed, int set)
20003 {
20004 	//Do we want the bars?
20005 	if (set) {
20006 		Cutscene_bar_flags |= CUB_CUTSCENE;
20007 	}
20008 	else {
20009 		Cutscene_bar_flags &= ~CUB_CUTSCENE;
20010 	}
20011 
20012 	if(delta_speed > 0.0f) {
20013 		Cutscene_bars_progress = 0.0f;
20014 		Cutscene_bar_flags |= CUB_GRADUAL;
20015 		Cutscene_delta_time = delta_speed;
20016 	}
20017 	else {
20018 		Cutscene_bar_flags &= ~CUB_GRADUAL;
20019 	}
20020 }
20021 
sexp_toggle_cutscene_bars(int node,int set)20022 void sexp_toggle_cutscene_bars(int node, int set)
20023 {
20024 	float delta_speed = 0.0f;
20025 
20026 	if(node != -1)
20027 		delta_speed = eval_num(node)/1000.0f;
20028 
20029 	toggle_cutscene_bars(delta_speed, set);
20030 
20031 	multi_start_callback();
20032 	multi_send_float(delta_speed);
20033 	multi_end_callback();
20034 }
20035 
multi_sexp_toggle_cutscene_bars(int set)20036 void multi_sexp_toggle_cutscene_bars(int set)
20037 {
20038 	float delta_speed;
20039 
20040 	if(multi_get_float(delta_speed) ) {
20041 		toggle_cutscene_bars(delta_speed, set);
20042 	}
20043 }
20044 
sexp_fade(bool fade_in,int duration,ubyte R,ubyte G,ubyte B)20045 void sexp_fade(bool fade_in, int duration, ubyte R, ubyte G, ubyte B)
20046 {
20047 	if (duration > 0)
20048 	{
20049 		Fade_start_timestamp = timestamp();
20050 		Fade_end_timestamp = timestamp(duration);
20051 		Fade_type = fade_in ? FI_FADEIN : FI_FADEOUT;
20052 		gr_create_shader(&Viewer_shader, R, G, B, Viewer_shader.c);
20053 	}
20054 	else
20055 	{
20056 		Fade_type = FI_NONE;
20057 		gr_create_shader(&Viewer_shader, R, G, B, fade_in ? 0 : 255);
20058 	}
20059 }
20060 
20061 static int Fade_out_r = -1;
20062 static int Fade_out_g = -1;
20063 static int Fade_out_b = -1;
20064 
sexp_fade(int n,bool fade_in)20065 void sexp_fade(int n, bool fade_in)
20066 {
20067 	int duration = 0;
20068 	int R = -1;
20069 	int G = -1;
20070 	int B = -1;
20071 
20072 	if (n != -1)
20073 	{
20074 		duration = eval_num(n);
20075 		n = CDR(n);
20076 
20077 		if (n != -1)
20078 		{
20079 			R = eval_num(n);
20080 			if (R < 0 || R > 255) R = -1;
20081 			n = CDR(n);
20082 
20083 			if (n != -1)
20084 			{
20085 				G = eval_num(n);
20086 				if (G < 0 || G > 255) G = -1;
20087 				n = CDR(n);
20088 
20089 				if (n != -1)
20090 				{
20091 					B = eval_num(n);
20092 					if (B < 0 || B > 255) B = -1;
20093 					n = CDR(n);
20094 				}
20095 			}
20096 		}
20097 	}
20098 
20099 	// select legacy (or default) fade color
20100 	if (R < 0 || G < 0 || B < 0)
20101 	{
20102 		// fade white
20103 		if (R == 1)
20104 		{
20105 			R = G = B = 255;
20106 		}
20107 		// fade red
20108 		else if (R == 2)
20109 		{
20110 			R = 255;
20111 			G = B = 0;
20112 		}
20113 		// default: fade black
20114 		else
20115 		{
20116 			// Mantis #2944: if we're fading in, and we previously faded out to some specific color, use that same color to fade in
20117 			if (fade_in && (Fade_out_r >= 0) && (Fade_out_g >= 0) && (Fade_out_b >= 0))
20118 			{
20119 				R = Fade_out_r;
20120 				G = Fade_out_g;
20121 				B = Fade_out_b;
20122 			}
20123 			else
20124 			{
20125 				R = G = B = 0;
20126 			}
20127 		}
20128 	}
20129 
20130 	// Mantis #2944, if we're fading out to some specific color, save that color
20131 	if (!fade_in && ((R > 0) || (G > 0) || (B > 0)))
20132 	{
20133 		Fade_out_r = R;
20134 		Fade_out_g = G;
20135 		Fade_out_b = B;
20136 	}
20137 
20138 	sexp_fade(fade_in, duration, (ubyte) R, (ubyte) G, (ubyte) B);
20139 
20140 	multi_start_callback();
20141 	multi_send_int(duration);
20142 	multi_send_int(R);
20143 	multi_send_int(G);
20144 	multi_send_int(B);
20145 	multi_end_callback();
20146 }
20147 
multi_sexp_fade(bool fade_in)20148 void multi_sexp_fade(bool fade_in)
20149 {
20150 	int duration = 0;
20151 	int R = 0;
20152 	int G = 0;
20153 	int B = 0;
20154 
20155 	if (multi_get_int(duration))
20156 		if (multi_get_int(R))
20157 			if (multi_get_int(G))
20158 				multi_get_int(B);
20159 
20160 	sexp_fade(fade_in, duration, (ubyte) R, (ubyte) G, (ubyte) B);
20161 }
20162 
sexp_get_set_camera(bool reset=false)20163 camera* sexp_get_set_camera(bool reset = false)
20164 {
20165 	static camid sexp_camera;
20166 	if(!reset)
20167 	{
20168 		if(Viewer_mode & VM_FREECAMERA)
20169 		{
20170 			camera *cam = cam_get_current().getCamera();
20171 			if(cam != NULL)
20172 				return cam;
20173 		}
20174 	}
20175 	if(!sexp_camera.isValid())
20176 	{
20177 		sexp_camera = cam_create("SEXP camera");
20178 	}
20179 
20180 	cam_set_camera(sexp_camera);
20181 
20182 	return sexp_camera.getCamera();
20183 }
20184 
sexp_set_camera(int node)20185 void sexp_set_camera(int node)
20186 {
20187 	if(node == -1)
20188 	{
20189 		sexp_get_set_camera(true);
20190 		return;
20191 	}
20192 
20193 	char *cam_name = CTEXT(node);
20194 	camid cid = cam_lookup(cam_name);
20195 	if(!cid.isValid())
20196 	{
20197 		cid = cam_create(cam_name);
20198 	}
20199 	cam_set_camera(cid);
20200 }
20201 
sexp_set_camera_position(int n)20202 void sexp_set_camera_position(int n)
20203 {
20204 	camera *cam = sexp_get_set_camera();
20205 
20206 	if(cam == NULL)
20207 		return;
20208 
20209 	vec3d camera_vec;
20210 	float camera_time = 0.0f;
20211 	float camera_acc_time = 0.0f;
20212 	float camera_dec_time = 0.0f;
20213 
20214 	camera_vec.xyz.x = i2fl(eval_num(n));
20215 	n = CDR(n);
20216 	camera_vec.xyz.y = i2fl(eval_num(n));
20217 	n = CDR(n);
20218 	camera_vec.xyz.z = i2fl(eval_num(n));
20219 	n = CDR(n);
20220 
20221 	if(n != -1)
20222 	{
20223 		camera_time = eval_num(n) / 1000.0f;
20224 		n = CDR(n);
20225 		if(n != -1)
20226 		{
20227 			camera_dec_time = camera_acc_time = eval_num(n) / 1000.0f;
20228 			n = CDR(n);
20229 			if(n != -1)
20230 			{
20231 				camera_dec_time = eval_num(n) / 1000.0f;
20232 			}
20233 		}
20234 	}
20235 
20236 	cam->set_position(&camera_vec, camera_time, camera_acc_time, camera_dec_time);
20237 
20238 	//multiplayer callback
20239 	multi_start_callback();
20240 	multi_send_float(camera_vec.xyz.x);
20241 	multi_send_float(camera_vec.xyz.y);
20242 	multi_send_float(camera_vec.xyz.z);
20243 	multi_send_float(camera_time);
20244 	multi_send_float(camera_acc_time);
20245 	multi_send_float(camera_dec_time);
20246 	multi_end_callback();
20247 }
20248 
20249 //CommanderDJ
multi_sexp_set_camera_position()20250 void multi_sexp_set_camera_position()
20251 {
20252 	camera *cam = sexp_get_set_camera();
20253 
20254 	if(cam == NULL) {
20255 		Int3();
20256 		return;
20257 	}
20258 
20259 	vec3d camera_vec;
20260 	float camera_time = 0.0f;
20261 	float camera_acc_time = 0.0f;
20262 	float camera_dec_time = 0.0f;
20263 
20264 	multi_get_float(camera_vec.xyz.x);
20265 	multi_get_float(camera_vec.xyz.y);
20266 	multi_get_float(camera_vec.xyz.z);
20267 	multi_get_float(camera_time);
20268 	multi_get_float(camera_acc_time);
20269 	multi_get_float(camera_dec_time);
20270 
20271 	cam->set_position(&camera_vec, camera_time, camera_acc_time, camera_dec_time);
20272 }
20273 
sexp_set_camera_rotation(int n)20274 void sexp_set_camera_rotation(int n)
20275 {
20276 	camera *cam = sexp_get_set_camera();
20277 	if(cam == NULL)
20278 		return;
20279 
20280 	angles rot_angles;
20281 	float rot_time = 0.0f;
20282 	float rot_acc_time = 0.0f;
20283 	float rot_dec_time = 0.0f;
20284 
20285 	//Angles are in degrees
20286 	rot_angles.p = fl_radians(eval_num(n) % 360);
20287 	n = CDR(n);
20288 	rot_angles.b = fl_radians(eval_num(n) % 360);
20289 	n = CDR(n);
20290 	rot_angles.h = fl_radians(eval_num(n) % 360);
20291 	n = CDR(n);
20292 	if(n != -1)
20293 	{
20294 		rot_time = eval_num(n) / 1000.0f;
20295 		n = CDR(n);
20296 		if(n != -1)
20297 		{
20298 			rot_dec_time = rot_acc_time = eval_num(n) / 1000.0f;
20299 			n = CDR(n);
20300 			if(n != -1)
20301 			{
20302 				rot_dec_time = eval_num(n) / 1000.0f;
20303 			}
20304 		}
20305 	}
20306 
20307 	cam->set_rotation(&rot_angles, rot_time, rot_acc_time, rot_dec_time);
20308 
20309 	multi_start_callback();
20310 	multi_send_float(rot_angles.b);
20311 	multi_send_float(rot_angles.h);
20312 	multi_send_float(rot_angles.p);
20313 	multi_send_float(rot_time);
20314 	multi_send_float(rot_acc_time);
20315 	multi_send_float(rot_dec_time);
20316 	multi_end_callback();
20317 }
20318 
multi_sexp_set_camera_rotation()20319 void multi_sexp_set_camera_rotation()
20320 {
20321 	camera *cam = sexp_get_set_camera();
20322 	if(cam == NULL)
20323 		return;
20324 
20325 	angles rot_angles;
20326 	float rot_time = 0.0f;
20327 	float rot_acc_time = 0.0f;
20328 	float rot_dec_time = 0.0f;
20329 
20330 	multi_get_float(rot_angles.b);
20331 	multi_get_float(rot_angles.h);
20332 	multi_get_float(rot_angles.p);
20333 	multi_get_float(rot_time);
20334 	multi_get_float(rot_acc_time);
20335 	multi_get_float(rot_dec_time);
20336 
20337 	cam->set_rotation(&rot_angles, rot_time, rot_acc_time, rot_dec_time);
20338 }
20339 
sexp_set_camera_facing(int n)20340 void sexp_set_camera_facing(int n)
20341 {
20342 	camera *cam = sexp_get_set_camera();
20343 	if(cam == NULL)
20344 		return;
20345 
20346 	vec3d location;
20347 	float rot_time = 0.0f;
20348 	float rot_acc_time = 0.0f;
20349 	float rot_dec_time = 0.0f;
20350 
20351 	location.xyz.x = i2fl(eval_num(n));
20352 	n = CDR(n);
20353 	location.xyz.y = i2fl(eval_num(n));
20354 	n = CDR(n);
20355 	location.xyz.z = i2fl(eval_num(n));
20356 	n = CDR(n);
20357 	if(n != -1)
20358 	{
20359 		rot_time = eval_num(n) / 1000.0f;
20360 		n = CDR(n);
20361 		if(n != -1)
20362 		{
20363 			rot_dec_time = rot_acc_time = eval_num(n) / 1000.0f;
20364 			n = CDR(n);
20365 			if(n != -1)
20366 			{
20367 				rot_dec_time = eval_num(n) / 1000.0f;
20368 			}
20369 		}
20370 	}
20371 
20372 	cam->set_rotation_facing(&location, rot_time, rot_acc_time, rot_dec_time);
20373 
20374 	//multiplayer callback
20375 	multi_start_callback();
20376 	multi_send_float(location.xyz.x);
20377 	multi_send_float(location.xyz.y);
20378 	multi_send_float(location.xyz.z);
20379 	multi_send_float(rot_time);
20380 	multi_send_float(rot_acc_time);
20381 	multi_send_float(rot_dec_time);
20382 	multi_end_callback();
20383 }
20384 
multi_sexp_set_camera_facing()20385 void multi_sexp_set_camera_facing()
20386 {
20387 	camera *cam = sexp_get_set_camera();
20388 	if(cam == NULL)
20389 		return;
20390 
20391 	vec3d location;
20392 	float rot_time = 0.0f;
20393 	float rot_acc_time = 0.0f;
20394 	float rot_dec_time = 0.0f;
20395 
20396 	multi_get_float(location.xyz.x);
20397 	multi_get_float(location.xyz.y);
20398 	multi_get_float(location.xyz.z);
20399 	multi_get_float(rot_time);
20400 	multi_get_float(rot_acc_time);
20401 	multi_get_float(rot_dec_time);
20402 
20403 	cam->set_rotation_facing(&location, rot_time, rot_acc_time, rot_dec_time);
20404 }
20405 
20406 //CommanderDJ
20407 /**
20408  * Helper function for set_camera_facing_object
20409  */
actually_set_camera_facing_object(char * object_name,float rot_time,float rot_acc_time,float rot_dec_time)20410 void actually_set_camera_facing_object(char *object_name, float rot_time, float rot_acc_time, float rot_dec_time)
20411 {
20412 	object_ship_wing_point_team oswpt;
20413 	sexp_get_object_ship_wing_point_team(&oswpt, object_name);
20414 
20415 	switch (oswpt.type)
20416 	{
20417 		case OSWPT_TYPE_EXITED:
20418 		{
20419 			Warning(LOCATION, "Camera tried to face destroyed/departed object %s", object_name);
20420 			return;
20421 		}
20422 
20423 		case OSWPT_TYPE_SHIP:
20424 		case OSWPT_TYPE_WING:
20425 		case OSWPT_TYPE_WAYPOINT:
20426 		{
20427 			camera *cam = sexp_get_set_camera();
20428 			if(cam == NULL)
20429 				return;
20430 			cam->set_rotation_facing(&oswpt.objp->pos, rot_time, rot_acc_time, rot_dec_time);
20431 			return;
20432 		}
20433 	}
20434 }
20435 
sexp_set_camera_facing_object(int n)20436 void sexp_set_camera_facing_object(int n)
20437 {
20438 	char *object_name = CTEXT(n);
20439 	float rot_time = 0.0f;
20440 	float rot_acc_time = 0.0f;
20441 	float rot_dec_time = 0.0f;
20442 
20443 	//Now get the rotation time values
20444 	n = CDR(n);
20445 	if(n != -1)
20446 	{
20447 		rot_time = eval_num(n) / 1000.0f;
20448 		n = CDR(n);
20449 		if(n != -1)
20450 		{
20451 			rot_dec_time = rot_acc_time = eval_num(n) / 1000.0f;
20452 			n = CDR(n);
20453 			if(n != -1)
20454 			{
20455 				rot_dec_time = eval_num(n) / 1000.0f;
20456 			}
20457 		}
20458 	}
20459 	actually_set_camera_facing_object(object_name, rot_time, rot_acc_time, rot_dec_time);
20460 
20461 	//multiplayer callback
20462 	multi_start_callback();
20463 	multi_send_string(object_name);
20464 	multi_send_float(rot_time);
20465 	multi_send_float(rot_acc_time);
20466 	multi_send_float(rot_dec_time);
20467 	multi_end_callback();
20468 }
20469 
20470 //CommanderDJ
multi_sexp_set_camera_facing_object()20471 void multi_sexp_set_camera_facing_object()
20472 {
20473 	char object_name[TOKEN_LENGTH];
20474 	float rot_time = 0.0f;
20475 	float rot_acc_time = 0.0f;
20476 	float rot_dec_time = 0.0f;
20477 
20478 	multi_get_string(object_name);
20479 	multi_get_float(rot_time);
20480 	multi_get_float(rot_acc_time);
20481 	multi_get_float(rot_dec_time);
20482 
20483 	actually_set_camera_facing_object(object_name, rot_time, rot_acc_time, rot_dec_time);
20484 }
20485 
20486 extern float VIEWER_ZOOM_DEFAULT;
sexp_set_camera_fov(int n)20487 void sexp_set_camera_fov(int n)
20488 {
20489 	camera *cam = sexp_get_set_camera();
20490 
20491 	if(cam == NULL)
20492 		return;
20493 
20494 	float camera_time = 0.0f;
20495 	float camera_acc_time = 0.0f;
20496 	float camera_dec_time = 0.0f;
20497 
20498 	float camera_fov = fl_radians(eval_num(n) % 360);
20499 	n = CDR(n);
20500 
20501 	if(n != -1)
20502 	{
20503 		camera_time = eval_num(n) / 1000.0f;
20504 		n = CDR(n);
20505 		if(n != -1)
20506 		{
20507 			camera_dec_time = camera_acc_time = eval_num(n) / 1000.0f;
20508 			n = CDR(n);
20509 			if(n != -1)
20510 			{
20511 				camera_dec_time = eval_num(n) / 1000.0f;
20512 			}
20513 		}
20514 	}
20515 
20516 	cam->set_fov(camera_fov, camera_time, camera_acc_time, camera_dec_time);
20517 
20518 
20519 	multi_start_callback();
20520 	multi_send_float(camera_fov);
20521 	multi_send_float(camera_time);
20522 	multi_send_float(camera_acc_time);
20523 	multi_send_float(camera_dec_time);
20524 	multi_end_callback();
20525 }
20526 
20527 //CommanderDJ
multi_sexp_set_camera_fov()20528 void multi_sexp_set_camera_fov()
20529 {
20530 	camera *cam = sexp_get_set_camera();
20531 
20532 	if(cam == NULL)
20533 		return;
20534 
20535 	float camera_fov = VIEWER_ZOOM_DEFAULT;
20536 	float camera_time = 0.0f;
20537 	float camera_acc_time = 0.0f;
20538 	float camera_dec_time = 0.0f;
20539 
20540 	multi_get_float(camera_fov);
20541 	multi_get_float(camera_time);
20542 	multi_get_float(camera_acc_time);
20543 	multi_get_float(camera_dec_time);
20544 
20545 	cam->set_fov(camera_fov, camera_time, camera_acc_time, camera_dec_time);
20546 }
20547 
20548 //Internal helper function for set-target and set-host
sexp_camera_get_objsub(int node,int * o_submodel)20549 object *sexp_camera_get_objsub(int node, int *o_submodel)
20550 {
20551 	//Get arguments
20552 	int n = node;
20553 	char *obj_name = NULL;
20554 	char *sub_name = NULL;
20555 
20556 	obj_name = CTEXT(n);
20557 	n = CDR(n);
20558 	if(n != -1)
20559 		sub_name = CTEXT(n);
20560 
20561 	//Important variables
20562 	object *objp = NULL;
20563 	int submodel = -1;
20564 
20565 	//*****Process obj_name
20566 	object_ship_wing_point_team oswpt;
20567 	sexp_get_object_ship_wing_point_team(&oswpt, obj_name);
20568 
20569 	switch (oswpt.type)
20570 	{
20571 		case OSWPT_TYPE_SHIP:
20572 		case OSWPT_TYPE_WING:
20573 		case OSWPT_TYPE_WAYPOINT:
20574 			objp = oswpt.objp;
20575 			break;
20576 
20577 		default:
20578 			objp = NULL;
20579 	}
20580 
20581 	//*****Process submodel
20582 	if(objp != NULL && sub_name != NULL && oswpt.type == OSWPT_TYPE_SHIP)
20583 	{
20584 		if(stricmp(sub_name, SEXP_NONE_STRING))
20585 		{
20586 			ship_subsys *ss = ship_get_subsys(&Ships[objp->instance], sub_name);
20587 			if (ss != NULL)
20588 			{
20589 				submodel = ss->system_info->subobj_num;
20590 			}
20591 		}
20592 	}
20593 
20594 	if(o_submodel != NULL)
20595 		*o_submodel = submodel;
20596 
20597 	return objp;
20598 }
20599 
sexp_set_camera_host(int node)20600 void sexp_set_camera_host(int node)
20601 {
20602 	//Try to get current camera
20603 	camera *cam = sexp_get_set_camera();
20604 
20605 	if(cam == NULL)
20606 		return;
20607 
20608 	//*****Get variables
20609 	int submodel = -1;
20610 	object *objp = sexp_camera_get_objsub(node, &submodel);
20611 
20612 	//*****Set
20613 	cam->set_object_host(objp, submodel);
20614 }
20615 
sexp_set_camera_target(int node)20616 void sexp_set_camera_target(int node)
20617 {
20618 	//Try to get current camera
20619 	camera *cam = sexp_get_set_camera();
20620 
20621 	if(cam == NULL)
20622 		return;
20623 
20624 	//*****Get variables
20625 	int submodel = -1;
20626 	object *objp = sexp_camera_get_objsub(node, &submodel);
20627 
20628 	//*****Set
20629 	cam->set_object_target(objp, submodel);
20630 
20631 	multi_start_callback();
20632 	multi_send_object(objp);
20633 	multi_send_int(submodel);
20634 	multi_end_callback();
20635 }
20636 
multi_sexp_set_camera_target()20637 void multi_sexp_set_camera_target()
20638 {
20639 	int submodel;
20640 	object *objp;
20641 
20642 	//Try to get current camera
20643 	camera *cam = sexp_get_set_camera();
20644 
20645 	if(cam == NULL)
20646 		return;
20647 
20648 	multi_get_object(objp);
20649 	multi_get_int(submodel);
20650 
20651 	cam->set_object_target(objp, submodel);
20652 }
20653 
sexp_set_fov(int n)20654 void sexp_set_fov(int n)
20655 {
20656 	camera *cam = Main_camera.getCamera();
20657 	if(cam == NULL) {
20658 		game_render_frame_setup();
20659 		cam = Main_camera.getCamera();
20660 	}
20661 
20662 	//Cap FOV to something reasonable.
20663 	float new_fov = (float)(eval_num(n) % 360);
20664 	Sexp_fov = fl_radians(new_fov);
20665 
20666 	multi_start_callback();
20667 	multi_send_float(new_fov);
20668 	multi_end_callback();
20669 }
20670 
multi_sexp_set_fov()20671 void multi_sexp_set_fov()
20672 {
20673 	float new_fov;
20674 
20675 	camera *cam = Main_camera.getCamera();
20676 	if(cam == NULL) {
20677 		game_render_frame_setup();
20678 		cam = Main_camera.getCamera();
20679 	}
20680 
20681 	multi_get_float(new_fov);
20682 	Sexp_fov = fl_radians(new_fov);
20683 }
20684 
sexp_get_fov()20685 int sexp_get_fov()
20686 {
20687 	camera *cam = Main_camera.getCamera();
20688 	if(cam == NULL)
20689 		return -1;
20690 	else if(Sexp_fov > 0.0f)
20691 		// SEXP override has been set
20692 		return (int) fl_degrees(Sexp_fov);
20693 	else
20694 		return (int) fl_degrees(cam->get_fov());
20695 }
20696 
20697 /**
20698  * @todo Check VIEWER_ZOOM_DEFAULT
20699  */
sexp_reset_fov()20700 void sexp_reset_fov()
20701 {
20702 	camera *cam = Main_camera.getCamera();
20703 	if(cam == NULL)
20704 		return;
20705 
20706 	Sexp_fov = 0.0;
20707 	//cam->set_fov(VIEWER_ZOOM_DEFAULT);
20708 
20709 	multi_do_callback();
20710 }
20711 
multi_sexp_reset_fov()20712 void multi_sexp_reset_fov()
20713 {
20714 	camera *cam = Main_camera.getCamera();
20715 	if(cam == NULL)
20716 		return;
20717 
20718 	Sexp_fov = 0.0;
20719 }
20720 
sexp_reset_camera(int node)20721 void sexp_reset_camera(int node)
20722 {
20723 	bool cam_reset = false;
20724 	camera *cam = cam_get_current().getCamera();
20725 	if(cam != NULL)
20726 	{
20727 		if(is_sexp_true(node))
20728 		{
20729 			cam->reset();
20730 			cam_reset = true;
20731 		}
20732 	}
20733 	cam_reset_camera();
20734 	multi_start_callback();
20735 	multi_send_bool(cam_reset);
20736 	multi_end_callback();
20737 }
20738 
multi_sexp_reset_camera()20739 void multi_sexp_reset_camera()
20740 {
20741 	camera *cam = cam_get_current().getCamera();
20742 	bool cam_reset = false;
20743 
20744 	multi_get_bool(cam_reset);
20745 	if((cam != NULL) && cam_reset) {
20746 		cam->reset();
20747 	}
20748 	cam_reset_camera();
20749 }
20750 
sexp_show_subtitle(int node)20751 void sexp_show_subtitle(int node)
20752 {
20753 	//These should be set to the default if not required to be explicitly defined
20754 	int x_pos, y_pos, width=0;
20755 	char *text, *imageanim=NULL;
20756 	float display_time, fade_time=0.0f;
20757 	int r=255, g=255, b=255;
20758 	bool center_x=false, center_y=false;
20759 	int n = -1;
20760 	bool post_shaded = false;
20761 
20762 	x_pos = eval_num(node);
20763 	if (gr_screen.max_w != 1024)
20764 		x_pos = (int) ((x_pos / 1024.0f) * gr_screen.max_w);
20765 
20766 	n = CDR(node);
20767 	y_pos = eval_num(n);
20768 	if (gr_screen.max_h != 768)
20769 		y_pos = (int) ((y_pos / 768.0f) * gr_screen.max_h);
20770 
20771 	n = CDR(n);
20772 	text = CTEXT(n);
20773 
20774 	n = CDR(n);
20775 	display_time = eval_num(n)/1000.0f;	//is in ms
20776 
20777 	n = CDR(n);
20778 	if(n != -1)
20779 	{
20780 		imageanim = CTEXT(n);
20781 
20782 		n = CDR(n);
20783 		if(n != -1)
20784 		{
20785 			fade_time = eval_num(n)/1000.0f; //also in ms
20786 
20787 			n = CDR(n);
20788 			if(n != -1)
20789 			{
20790 				center_x = is_sexp_true(n) != 0;
20791 
20792 				n = CDR(n);
20793 				if(n != -1)
20794 				{
20795 					center_y = is_sexp_true(n) != 0;
20796 
20797 					n = CDR(n);
20798 					if(n != -1)
20799 					{
20800 						width = eval_num(n);
20801 
20802 						n = CDR(n);
20803 						if(n != -1)
20804 						{
20805 							r = eval_num(n);
20806 
20807 							n = CDR(n);
20808 							if(n != -1)
20809 							{
20810 								g = eval_num(n);
20811 
20812 								n = CDR(n);
20813 								if(n != -1)
20814 								{
20815 									b = eval_num(n);
20816 
20817 									n = CDR(n);
20818 									if ( n !=-1 )
20819 									{
20820 										post_shaded = is_sexp_true(n) != 0;
20821 									}
20822 								}
20823 							}
20824 						}
20825 					}
20826 				}
20827 			}
20828 		}
20829 	}
20830 
20831 	if(r > 255)
20832 		r = 255;
20833 	if(g > 255)
20834 		g = 255;
20835 	if(b > 255)
20836 		b = 255;
20837 
20838 	//FINALLY !!
20839 	color new_color;
20840 	gr_init_alphacolor(&new_color, r, g, b, 255);
20841 
20842 	subtitle new_subtitle(x_pos, y_pos, text, imageanim, display_time, fade_time, &new_color, -1, center_x, center_y, width, 0, post_shaded);
20843 	Subtitles.push_back(new_subtitle);
20844 }
20845 
sexp_clear_subtitles()20846 void sexp_clear_subtitles()
20847 {
20848 	Subtitles.clear();
20849 
20850 	multi_do_callback();
20851 }
20852 
multi_sexp_clear_subtitles()20853 void multi_sexp_clear_subtitles()
20854 {
20855 	Subtitles.clear();
20856 }
20857 
sexp_show_subtitle_text(int node)20858 void sexp_show_subtitle_text(int node)
20859 {
20860 	int i, n = node;
20861 	char text[256];
20862 
20863 	// we'll suppose it's the string for now
20864 	char *buffer = CTEXT(n);
20865 
20866 	// but use an actual message if one exists
20867 	for (i=0; i<Num_messages; i++)
20868 	{
20869 		if (!stricmp(Messages[i].name, buffer))
20870 		{
20871 			buffer = Messages[i].message;
20872 			break;
20873 		}
20874 	}
20875 
20876 	if (strlen(buffer) > 255)
20877 	{
20878 		Warning(LOCATION, "The subtitle system only handles text up to 255 characters long! :(");
20879 		return;
20880 	}
20881 
20882 	// translate things like keypresses, e.g. $T$ for targeting key
20883 	// (we don't need to do variable replacements because the subtitle code already does that)
20884 	message_translate_tokens(text, buffer);
20885 
20886 	n = CDR(n);
20887 
20888 	int x_pct = eval_num(n);
20889 	n = CDR(n);
20890 
20891 	int y_pct = eval_num(n);
20892 	n = CDR(n);
20893 
20894 	bool center_x = is_sexp_true(n) != 0;
20895 	n = CDR(n);
20896 
20897 	bool center_y = is_sexp_true(n) != 0;
20898 	n = CDR(n);
20899 
20900 	float display_time = eval_num(n) / 1000.0f;
20901 	n = CDR(n);
20902 
20903 	float fade_time = 0.0f;
20904 	if (n >= 0)
20905 	{
20906 		fade_time = eval_num(n) / 1000.0f;
20907 		n = CDR(n);
20908 	}
20909 
20910 	int width_pct = 0;
20911 	if (n >= 0)
20912 	{
20913 		width_pct = eval_num(n);
20914 		n = CDR(n);
20915 	}
20916 
20917 	int red = 255;
20918 	if (n >= 0)
20919 	{
20920 		red = eval_num(n);
20921 		n = CDR(n);
20922 	}
20923 
20924 	int green = 255;
20925 	if (n >= 0)
20926 	{
20927 		green = eval_num(n);
20928 		n = CDR(n);
20929 	}
20930 
20931 	int blue = 255;
20932 	if (n >= 0)
20933 	{
20934 		blue = eval_num(n);
20935 		n = CDR(n);
20936 	}
20937 
20938 	int fontnum = -1;
20939 	if (n >= 0)
20940 	{
20941 		char *font_name = CTEXT(n);
20942 		n = CDR(n);
20943 
20944 		// perform font lookup
20945 		for (int j = 0; j < Num_fonts; j++)
20946 		{
20947 			if (!stricmp(font_name, Fonts[j].filename))
20948 			{
20949 				fontnum = j;
20950 				break;
20951 			}
20952 		}
20953 	}
20954 
20955 	bool post_shaded = false;
20956 	if (n >= 0)
20957 	{
20958 		post_shaded = is_sexp_true(n) != 0;
20959 		n = CDR(n);
20960 	}
20961 
20962 	// check bounds
20963 	if (x_pct < -100)
20964 		x_pct = -100;
20965 	if (x_pct > 100)
20966 		x_pct = 100;
20967 	if (y_pct < -100)
20968 		y_pct = -100;
20969 	if (y_pct > 100)
20970 		y_pct = 100;
20971 	if (width_pct > 100)
20972 		width_pct = 100;
20973 	if (red > 255)
20974 		red = 255;
20975 	if (green > 255)
20976 		green = 255;
20977 	if (blue > 255)
20978 		blue = 255;
20979 
20980 	color new_color;
20981 	gr_init_alphacolor(&new_color, red, green, blue, 255);
20982 
20983 	// calculate pixel positions
20984 	int x_pos = (int) (gr_screen.max_w * (x_pct / 100.0f));
20985 	int y_pos = (int) (gr_screen.max_h * (y_pct / 100.0f));
20986 	int width = (int) (gr_screen.max_w * (width_pct / 100.0f));
20987 
20988 	// add the subtitle
20989 	subtitle new_subtitle(x_pos, y_pos, text, NULL, display_time, fade_time, &new_color, fontnum, center_x, center_y, width, 0, post_shaded);
20990 	Subtitles.push_back(new_subtitle);
20991 
20992 	multi_start_callback();
20993 	multi_send_int(x_pos);
20994 	multi_send_int(y_pos);
20995 	multi_send_string(text);
20996 	multi_send_float(display_time);
20997 	multi_send_float(fade_time);
20998 	multi_send_int(red);
20999 	multi_send_int(green);
21000 	multi_send_int(blue);
21001 	multi_send_int(fontnum);
21002 	multi_send_bool(center_x);
21003 	multi_send_bool(center_y);
21004 	multi_send_int(width);
21005 	multi_send_bool(post_shaded);
21006 	multi_end_callback();
21007 }
21008 
multi_sexp_show_subtitle_text()21009 void multi_sexp_show_subtitle_text()
21010 {
21011 	int x_pos, y_pos, width=0, fontnum;
21012 	char text[TOKEN_LENGTH];
21013 	float display_time, fade_time=0.0f;
21014 	int red=255, green=255, blue=255;
21015 	bool center_x=false, center_y=false;
21016 	bool post_shaded = false;
21017 	color new_color;
21018 
21019 	multi_get_int(x_pos);
21020 	multi_get_int(y_pos);
21021 	multi_get_string(text);
21022 	multi_get_float(display_time);
21023 	multi_get_float(fade_time);
21024 	multi_get_int(red);
21025 	multi_get_int(green);
21026 	multi_get_int(blue);
21027 	multi_get_int(fontnum);
21028 	multi_get_bool(center_x);
21029 	multi_get_bool(center_y);
21030 	multi_get_int(width);
21031 	multi_get_bool(post_shaded);
21032 
21033 	gr_init_alphacolor(&new_color, red, green, blue, 255);
21034 
21035 	// add the subtitle
21036 	subtitle new_subtitle(x_pos, y_pos, text, NULL, display_time, fade_time, &new_color, fontnum, center_x, center_y, width, 0, post_shaded);
21037 	Subtitles.push_back(new_subtitle);
21038 
21039 }
21040 
sexp_show_subtitle_image(int node)21041 void sexp_show_subtitle_image(int node)
21042 {
21043 	int n = node;
21044 
21045 	char *image = CTEXT(n);
21046 	n = CDR(n);
21047 
21048 	int x_pct = eval_num(n);
21049 	n = CDR(n);
21050 
21051 	int y_pct = eval_num(n);
21052 	n = CDR(n);
21053 
21054 	bool center_x = is_sexp_true(n) != 0;
21055 	n = CDR(n);
21056 
21057 	bool center_y = is_sexp_true(n) != 0;
21058 	n = CDR(n);
21059 
21060 	int width_pct = eval_num(n);
21061 	n = CDR(n);
21062 
21063 	int height_pct = eval_num(n);
21064 	n = CDR(n);
21065 
21066 	float display_time = eval_num(n) / 1000.0f;
21067 	n = CDR(n);
21068 
21069 	float fade_time = 0.0f;
21070 	if (n >= 0)
21071 	{
21072 		fade_time = eval_num(n) / 1000.0f;
21073 		n = CDR(n);
21074 	}
21075 
21076 	bool post_shaded = false;
21077 	if (n >= 0)
21078 	{
21079 		post_shaded = is_sexp_true(n) != 0;
21080 		n = CDR(n);
21081 	}
21082 
21083 	// check bounds
21084 	if (x_pct < -100)
21085 		x_pct = -100;
21086 	if (x_pct > 100)
21087 		x_pct = 100;
21088 	if (y_pct < -100)
21089 		y_pct = -100;
21090 	if (y_pct > 100)
21091 		y_pct = 100;
21092 	if (width_pct < 0)
21093 		width_pct = 0;
21094 	if (width_pct > 100)
21095 		width_pct = 100;
21096 	if (height_pct < 0)
21097 		height_pct = 0;
21098 	if (height_pct > 100)
21099 		height_pct = 100;
21100 
21101 	// calculate pixel positions
21102 	int x_pos = (int)(gr_screen.max_w * (x_pct / 100.0f));
21103 	int y_pos = (int)(gr_screen.max_h * (y_pct / 100.0f));
21104 	int width = (int)(gr_screen.max_w * (width_pct / 100.0f));
21105 	int height = (int)(gr_screen.max_h * (height_pct / 100.0f));
21106 
21107 	// add the subtitle
21108 	subtitle new_subtitle(x_pos, y_pos, NULL, image, display_time, fade_time, NULL, -1, center_x, center_y, width, height, post_shaded);
21109 	Subtitles.push_back(new_subtitle);
21110 
21111 	multi_start_callback();
21112 	multi_send_int(x_pos);
21113 	multi_send_int(y_pos);
21114 	multi_send_string(image);
21115 	multi_send_float(display_time);
21116 	multi_send_float(fade_time);
21117 	multi_send_bool(center_x);
21118 	multi_send_bool(center_y);
21119 	multi_send_int(width);
21120 	multi_send_int(height);
21121 	multi_send_bool(post_shaded);
21122 	multi_end_callback();
21123 }
21124 
multi_sexp_show_subtitle_image()21125 void multi_sexp_show_subtitle_image()
21126 {
21127 	int x_pos, y_pos, width=0, height=0;
21128 	char image[TOKEN_LENGTH];
21129 	float display_time, fade_time=0.0f;
21130 	bool center_x=false, center_y=false;
21131 	bool post_shaded = false;
21132 
21133 	multi_get_int(x_pos);
21134 	multi_get_int(y_pos);
21135 	multi_get_string(image);
21136 	multi_get_float(display_time);
21137 	multi_get_float(fade_time);
21138 	multi_get_bool(center_x);
21139 	multi_get_bool(center_y);
21140 	multi_get_int(width);
21141 	multi_get_int(height);
21142 	multi_get_bool(post_shaded);
21143 
21144 	// add the subtitle
21145 	subtitle new_subtitle(x_pos, y_pos, NULL, image, display_time, fade_time, NULL, -1, center_x, center_y, width, height, post_shaded);
21146 	Subtitles.push_back(new_subtitle);
21147 
21148 }
21149 
sexp_set_time_compression(int n)21150 void sexp_set_time_compression(int n)
21151 {
21152 	float new_multiplier = 0.0f;
21153 	float new_change_time = 0.0f;
21154 	float current_multiplier = 0.0f;
21155 
21156 	multi_start_callback();
21157 
21158 	new_multiplier = eval_num(n)/100.0f;			//percent->decimal
21159 	multi_send_float(new_multiplier);
21160 
21161 
21162 	//Time to change
21163 	n = CDR(n);
21164 	if(n != -1) {
21165 		new_change_time = eval_num(n)/1000.0f;			//ms->seconds
21166 		multi_send_float(new_change_time);
21167 	}
21168 
21169 	//Override current time compression with this value
21170 	n = CDR(n);
21171 	if(n != -1) {
21172 		current_multiplier = eval_num(n)/100.0f;
21173 		set_time_compression(current_multiplier);
21174 		multi_send_float(current_multiplier);
21175 	}
21176 
21177 	multi_end_callback();
21178 
21179 	set_time_compression(new_multiplier, new_change_time);
21180 	lock_time_compression(true);
21181 }
21182 
multi_sexp_set_time_compression()21183 void multi_sexp_set_time_compression()
21184 {
21185 	float new_change_time = 0.0f;
21186 	float new_multiplier = 0.0f;
21187 	float current_multiplier = 0.0f;
21188 
21189 	multi_get_float(new_multiplier);
21190 	multi_get_float(new_change_time);
21191 	if (multi_get_float(current_multiplier)) {
21192 		set_time_compression(current_multiplier);
21193 	}
21194 
21195 	set_time_compression(new_multiplier, new_change_time);
21196 	lock_time_compression(true);
21197 }
21198 
sexp_reset_time_compression()21199 void sexp_reset_time_compression()
21200 {
21201 	set_time_compression(1);
21202 	lock_time_compression(false);
21203 
21204 	multi_start_callback();
21205 	multi_end_callback();
21206 }
21207 
multi_sexp_reset_time_compression()21208 void multi_sexp_reset_time_compression()
21209 {
21210 	set_time_compression(1);
21211 	lock_time_compression(false);
21212 }
21213 
21214 extern bool Perspective_locked;
21215 
sexp_force_perspective(int n)21216 void sexp_force_perspective(int n)
21217 {
21218 	Perspective_locked = (is_sexp_true(n) != 0);
21219 	n=CDR(n);
21220 
21221 	if(n != -1)
21222 	{
21223 		n = eval_num(n);
21224 		switch(n)
21225 		{
21226 			case 0:
21227 				Viewer_mode = 0;
21228 				break;
21229 			case 1:
21230 				Viewer_mode = VM_CHASE;
21231 				break;
21232 			case 2:
21233 				Viewer_mode = VM_EXTERNAL;
21234 				break;
21235 			case 3:
21236 				Viewer_mode = VM_TOPDOWN;
21237 				break;
21238 		}
21239 	}
21240 }
21241 
sexp_set_camera_shudder(int n)21242 void sexp_set_camera_shudder(int n)
21243 {
21244 	int time = eval_num(n);
21245 	float intensity = (float) eval_num(CDR(n)) * 0.01f;
21246 
21247 	game_shudder_apply(time, intensity);
21248 
21249 	multi_start_callback();
21250 	multi_send_int(time);
21251 	multi_send_float(intensity);
21252 	multi_end_callback();
21253 }
21254 
multi_sexp_set_camera_shudder()21255 void multi_sexp_set_camera_shudder()
21256 {
21257 	int time;
21258 	float intensity;
21259 
21260 	multi_get_int(time);
21261 	if (multi_get_float(intensity)) {
21262 		game_shudder_apply(time, intensity);
21263 	}
21264 }
21265 
sexp_set_jumpnode_name(int n)21266 void sexp_set_jumpnode_name(int n) //CommanderDJ
21267 {
21268 	char *old_name = CTEXT(n); //for multi
21269 
21270 	CJumpNode *jnp = jumpnode_get_by_name(old_name);
21271 
21272 	if(jnp==NULL) {
21273 		return;
21274 	}
21275 
21276 	n=CDR(n);
21277 	char *new_name = CTEXT(n); //for multi
21278 	jnp->SetName(new_name);
21279 
21280 	//multiplayer callback
21281 	multi_start_callback();
21282 	multi_send_string(old_name);
21283 	multi_send_string(new_name);
21284 	multi_end_callback();
21285 }
21286 
multi_sexp_set_jumpnode_name()21287 void multi_sexp_set_jumpnode_name() //CommanderDJ
21288 {
21289 	char old_name[TOKEN_LENGTH];
21290 	char new_name[TOKEN_LENGTH];
21291 
21292 	multi_get_string(old_name);
21293 	multi_get_string(new_name);
21294 
21295 	CJumpNode *jnp = jumpnode_get_by_name(old_name);
21296 
21297 	if(jnp==NULL)
21298 		return;
21299 
21300 	jnp->SetName(new_name);
21301 }
21302 
sexp_set_jumpnode_color(int n)21303 void sexp_set_jumpnode_color(int n)
21304 {
21305 	CJumpNode *jnp = jumpnode_get_by_name(CTEXT(n));
21306 
21307 	if(jnp==NULL)
21308 		return;
21309 
21310 	char* jumpnode_name = CTEXT(n); //for multi
21311 
21312 	n=CDR(n);
21313 
21314 	int red = eval_num(n);
21315 	int green = eval_num(CDR(n));
21316 	int blue = eval_num(CDR(CDR(n)));
21317 	int alpha = eval_num(CDR(CDR(CDR(n))));
21318 
21319 	jnp->SetAlphaColor(red, green, blue, alpha);
21320 
21321 	multi_start_callback();
21322 	multi_send_string(jumpnode_name);
21323 	multi_send_int(red);
21324 	multi_send_int(green);
21325 	multi_send_int(blue);
21326 	multi_send_int(alpha);
21327 	multi_end_callback();
21328 }
21329 
21330 //CommanderDJ
multi_sexp_set_jumpnode_color()21331 void multi_sexp_set_jumpnode_color()
21332 {
21333 	char jumpnode_name[TOKEN_LENGTH];
21334 	int red, blue, green, alpha;
21335 
21336 	multi_get_string(jumpnode_name);
21337 	CJumpNode *jnp = jumpnode_get_by_name(jumpnode_name);
21338 
21339 	if(jnp==NULL) {
21340 		multi_discard_remaining_callback_data();
21341 		return;
21342 	}
21343 
21344 	multi_get_int(red);
21345 	multi_get_int(green);
21346 	multi_get_int(blue);
21347 	multi_get_int(alpha);
21348 
21349 	jnp->SetAlphaColor(red, green, blue, alpha);
21350 }
21351 
sexp_set_jumpnode_model(int n)21352 void sexp_set_jumpnode_model(int n)
21353 {
21354 	char* jumpnode_name = CTEXT(n);
21355 	CJumpNode *jnp = jumpnode_get_by_name(jumpnode_name);
21356 
21357 	if(jnp==NULL)
21358 		return;
21359 
21360 	n=CDR(n);
21361 	char* model_name = CTEXT(n);
21362 	n=CDR(n);
21363 	bool show_polys = (is_sexp_true(n) != 0);
21364 
21365 	jnp->SetModel(model_name, show_polys);
21366 
21367 	multi_start_callback();
21368 	multi_send_string(jumpnode_name);
21369 	multi_send_string(model_name);
21370 	multi_send_bool(show_polys);
21371 	multi_end_callback();
21372 }
21373 
multi_sexp_set_jumpnode_model()21374 void multi_sexp_set_jumpnode_model()
21375 {
21376 	char jumpnode_name[TOKEN_LENGTH];
21377 	char model_name[TOKEN_LENGTH];
21378 	bool show_polys;
21379 
21380 	multi_get_string(jumpnode_name);
21381 	multi_get_string(model_name);
21382 
21383 	CJumpNode *jnp = jumpnode_get_by_name(jumpnode_name);
21384 
21385 	if(jnp==NULL) {
21386 		multi_discard_remaining_callback_data();
21387 		return;
21388 	}
21389 
21390 	show_polys = multi_get_bool(show_polys);
21391 
21392 	jnp->SetModel(model_name, show_polys);
21393 }
21394 
sexp_show_hide_jumpnode(int node,bool show)21395 void sexp_show_hide_jumpnode(int node, bool show)
21396 {
21397 	multi_start_callback();
21398 
21399 	for (int n = node; n >= 0; n = CDR(n))
21400 	{
21401 		CJumpNode *jnp = jumpnode_get_by_name(CTEXT(n));
21402 		if (jnp != NULL)
21403 		{
21404 			jnp->SetVisibility(show);
21405 			multi_send_string(CTEXT(n));
21406 		}
21407 	}
21408 
21409 	multi_end_callback();
21410 }
21411 
multi_sexp_show_hide_jumpnode(bool show)21412 void multi_sexp_show_hide_jumpnode(bool show)
21413 {
21414 	char jumpnode_name[TOKEN_LENGTH];
21415 
21416 	while (multi_get_string(jumpnode_name))
21417 	{
21418 		CJumpNode *jnp = jumpnode_get_by_name(jumpnode_name);
21419 		if (jnp != NULL)
21420 			jnp->SetVisibility(show);
21421 	}
21422 }
21423 
21424 //WMC - This is a bit of a hack, however, it's easier than
21425 //coding in a whole new Script_system function.
sexp_script_eval(int node,int return_type)21426 int sexp_script_eval(int node, int return_type)
21427 {
21428 	int n = node;
21429 	char *s = CTEXT(n);
21430 	bool success = false;
21431 
21432 	int r = -1;
21433 
21434 	switch(return_type)
21435 	{
21436 		case OPR_NUMBER:
21437 			success = Script_system.EvalString(s, "|i", &r, s);
21438 			break;
21439 		case OPR_STRING:
21440 			Error(LOCATION, "SEXP system does not support string return type; Goober must fix this before script-eval-string will work");
21441 			break;
21442 		case OPR_NULL:
21443 			while(n != -1)
21444 			{
21445 				success = Script_system.EvalString(s, NULL, NULL, CTEXT(n));
21446 				n = CDR(n);
21447 			}
21448 			break;
21449 		default:
21450 			Error(LOCATION, "Bad type passed to sexp_script_eval - get a coder");
21451 			break;
21452 	}
21453 
21454 	if(!success)
21455 		Warning(LOCATION, "sexp-script-eval failed to evaluate string \"%s\"; check your syntax", s);
21456 
21457 	return r;
21458 }
21459 
sexp_script_eval_multi(int node)21460 void sexp_script_eval_multi(int node)
21461 {
21462 	char s[TOKEN_LENGTH];
21463 	bool success = true;
21464 	int execute_on_server;
21465 	int sindex;
21466 	player *p;
21467 
21468 	strcpy_s(s, CTEXT(node));
21469 
21470 	node = CDR(node);
21471 
21472 	execute_on_server = is_sexp_true(node);
21473 
21474 	node = CDR(node);
21475 
21476 	multi_start_callback();
21477 	multi_send_string(s);
21478 	// evalutate on all clients
21479 	if (node == -1) {
21480 		multi_send_bool(true);
21481 		execute_on_server = 1;
21482 	}
21483 	// we have to send to all clients but we need to send a list of ships so that they know if they evaluate or not
21484 	else {
21485 		multi_send_bool(false);
21486 
21487 		do {
21488 			p = get_player_from_ship_node(node, true);
21489 
21490 			// not a player ship so skip it
21491 			if (p == NULL ){
21492 				node = CDR(node);
21493 				continue;
21494 			}
21495 			else {
21496 				// if this is me, flag that we should execute the script
21497 				if (p == Player) {
21498 					execute_on_server = 1;
21499 				}
21500 				// otherwise notify the clients
21501 				else {
21502 					sindex = ship_name_lookup(CTEXT(node));
21503 					multi_send_ship(sindex);
21504 				}
21505 			}
21506 
21507 			node = CDR(node);
21508 		} while (node != -1);
21509 	}
21510 
21511 	multi_end_callback();
21512 
21513 	if (execute_on_server) {
21514 		success = Script_system.EvalString(s, NULL, NULL, s);
21515 	}
21516 
21517 	if(!success) {
21518 		Warning(LOCATION, "sexp-script-eval failed to evaluate string \"%s\"; check your syntax", s);
21519 	}
21520 }
21521 
multi_sexp_script_eval_multi()21522 void multi_sexp_script_eval_multi()
21523 {
21524 	int sindex;
21525 	char s[TOKEN_LENGTH];
21526 	bool sent_to_all = false;
21527 	bool success = true;
21528 
21529 	multi_get_string(s);
21530 	multi_get_bool(sent_to_all);
21531 
21532 	if (sent_to_all) {
21533 		success = Script_system.EvalString(s, NULL, NULL, s);
21534 	}
21535 	// go through all the ships that were sent and see if any of them match this client.
21536 	else {
21537 		while (multi_get_ship(sindex)) {
21538 			Assertion(sindex >= 0, "Illegal value for the ship index sent in multi_sexp_script_eval_multi()! Ship %d does not exist!", sindex);
21539 			if (Player->objnum == Ships[sindex].objnum) {
21540 				success = Script_system.EvalString(s, NULL, NULL, s);
21541 			}
21542 		}
21543 	}
21544 
21545 	if(!success) {
21546 		Warning(LOCATION, "sexp-script-eval failed to evaluate string \"%s\"; check your syntax", s);
21547 	}
21548 }
21549 
21550 
sexp_force_glide(int node)21551 void sexp_force_glide(int node)
21552 {
21553 	ship *shipp;
21554 	int sindex;
21555 
21556 	// get ship
21557 	sindex = ship_name_lookup(CTEXT(node));
21558 
21559 	if (sindex < 0) {
21560 		return;
21561 	}
21562 
21563 	shipp = &Ships[sindex];
21564 	if (shipp->objnum < 0) {
21565 		return;
21566 	}
21567 
21568 	//Can this ship glide?
21569 	if (!Ship_info[shipp->ship_info_index].can_glide)
21570 		return;
21571 
21572 	int glide = is_sexp_true(CDR(node));
21573 
21574 	object_set_gliding(&Objects[shipp->objnum], (glide > 0), true);
21575 
21576 	return;
21577 }
21578 
test_point_within_box(vec3d * test_point,vec3d * box_corner_1,vec3d * box_corner_2,object * reference_ship_obj)21579 bool test_point_within_box(vec3d *test_point, vec3d *box_corner_1, vec3d *box_corner_2, object *reference_ship_obj)
21580 {
21581 	vec3d tempv, test_point_buf;
21582 
21583 	// If reference_ship is specified, rotate test_point into its reference frame
21584 	if (reference_ship_obj != NULL)
21585 	{
21586 		vm_vec_sub(&tempv, test_point, &reference_ship_obj->pos);
21587 		vm_vec_rotate(&test_point_buf, &tempv, &reference_ship_obj->orient);
21588 
21589 		test_point = &test_point_buf;
21590 	}
21591 
21592 	// Check to see if the test point is within the specified box as defined by two extreme corners
21593 	return ((test_point->xyz.x >= box_corner_1->xyz.x && test_point->xyz.x <= box_corner_2->xyz.x) &&
21594 			(test_point->xyz.y >= box_corner_1->xyz.y && test_point->xyz.y <= box_corner_2->xyz.y) &&
21595 			(test_point->xyz.z >= box_corner_1->xyz.z && test_point->xyz.z <= box_corner_2->xyz.z));
21596 }
21597 
sexp_is_in_box(int n)21598 int sexp_is_in_box(int n)
21599 {
21600 	int i;
21601 	Assert(n >= 0);
21602 
21603 	object_ship_wing_point_team oswpt;
21604 	sexp_get_object_ship_wing_point_team(&oswpt, CTEXT(n));
21605 	n = CDR(n);
21606 
21607 	// Get box corners
21608 	float x1 = (float) eval_num(n);
21609 	n = CDR(n);
21610 	float x2 = (float) eval_num(n);
21611 	n = CDR(n);
21612 	float y1 = (float) eval_num(n);
21613 	n = CDR(n);
21614 	float y2 = (float) eval_num(n);
21615 	n = CDR(n);
21616 	float z1 = (float) eval_num(n);
21617 	n = CDR(n);
21618 	float z2 = (float) eval_num(n);
21619 	n = CDR(n);
21620 	vec3d box_corner_1;
21621 	box_corner_1.xyz.x = MIN(x1, x2);
21622 	box_corner_1.xyz.y = MIN(y1, y2);
21623 	box_corner_1.xyz.z = MIN(z1, z2);
21624 	vec3d box_corner_2;
21625 	box_corner_2.xyz.x = MAX(x1, x2);
21626 	box_corner_2.xyz.y = MAX(y1, y2);
21627 	box_corner_2.xyz.z = MAX(z1, z2);
21628 
21629 	// Ship to define reference frame is optional
21630 	object *reference_ship_obj = NULL;
21631 	if (n != -1)
21632 	{
21633 		int sindex = ship_name_lookup(CTEXT(n));
21634 
21635 		if (sindex < 0 || Ships[sindex].objnum < 0)
21636 			return SEXP_FALSE;
21637 
21638 		reference_ship_obj = &Objects[Ships[sindex].objnum];
21639 	}
21640 
21641 	// Check position of object
21642 	switch (oswpt.type)
21643 	{
21644 		case OSWPT_TYPE_EXITED:
21645 			return SEXP_KNOWN_FALSE;
21646 
21647 		case OSWPT_TYPE_WING:
21648 			for (i = 0; i < oswpt.wingp->current_count; i++)
21649 				if (!test_point_within_box(&Objects[Ships[oswpt.wingp->ship_index[i]].objnum].pos, &box_corner_1, &box_corner_2, reference_ship_obj))
21650 					return SEXP_FALSE;
21651 			return SEXP_TRUE;
21652 
21653 		case OSWPT_TYPE_SHIP:
21654 		case OSWPT_TYPE_WAYPOINT:
21655 			return test_point_within_box(&oswpt.objp->pos, &box_corner_1, &box_corner_2, reference_ship_obj);
21656 
21657 		default:
21658 			return SEXP_FALSE;
21659 	}
21660 }
21661 
sexp_is_in_mission(int node)21662 int sexp_is_in_mission(int node)
21663 {
21664 	for (int n = node; n != -1; n = CDR(n))
21665 		if (ship_name_lookup(CTEXT(n)) < 0)
21666 			return SEXP_FALSE;
21667 
21668 	return SEXP_TRUE;
21669 }
21670 
sexp_manipulate_colgroup(int node,bool add_to_group)21671 void sexp_manipulate_colgroup(int node, bool add_to_group) {
21672 	object* objp;
21673 	ship* shipp;
21674 	int colgroup_id;
21675 
21676 	shipp = sexp_get_ship_from_node(node);
21677 
21678 	if (shipp == NULL)
21679 		return;
21680 
21681 	objp = &Objects[shipp->objnum];
21682 	colgroup_id = objp->collision_group_id;
21683 
21684 	node = CDR(node);
21685 
21686 	while (node != -1) {
21687 
21688 		int group = eval_num(node);
21689 
21690 		if (group < 0 || group > 31) {
21691 			WarningEx(LOCATION, "Invalid collision group id %d specified for object %s. Valid IDs range from 0 to 31.\n", group, shipp->ship_name);
21692 		} else {
21693 			if (add_to_group) {
21694 				colgroup_id |= (1<<group);
21695 			} else {
21696 				colgroup_id &= !(1<<group);
21697 			}
21698 		}
21699 
21700 		node = CDR(node);
21701 	}
21702 
21703 	objp->collision_group_id = colgroup_id;
21704 }
21705 
sexp_get_colgroup(int node)21706 int sexp_get_colgroup(int node) {
21707 	ship* shipp;
21708 
21709 	shipp = sexp_get_ship_from_node(CDR(node));
21710 
21711 	return Objects[shipp->objnum].collision_group_id;
21712 }
21713 
get_effect_from_name(char * name)21714 int get_effect_from_name(char* name) {
21715 	int i = 0;
21716 	for (SCP_vector<ship_effect>::iterator sei = Ship_effects.begin(); sei != Ship_effects.end(); ++sei) {
21717 		if (!stricmp(name, sei->name))
21718 			return i;
21719 		i++;
21720 	}
21721 	return -1;
21722 }
21723 
sexp_ship_effect(int n)21724 void sexp_ship_effect(int n)
21725 {
21726 	char	*name;
21727 	int ship_index, wing_index;
21728 
21729 	Assert ( n != -1 );
21730 
21731 	int effect_num = get_effect_from_name(CTEXT(n));
21732 	if (effect_num == -1) {
21733 		WarningEx(LOCATION, "Invalid effect name passed to ship-effect\n");
21734 		return;
21735 	}
21736 	n = CDR(n);
21737 	int effect_duration = eval_num(n);
21738 	n = CDR(n);
21739 
21740 	ship_index = -1;
21741 	wing_index = -1;
21742 	while (n != -1) {
21743 		name = CTEXT(n);
21744 
21745 		// check to see if this ship/wing has arrived yet.
21746 		if (sexp_query_has_yet_to_arrive(name)) {
21747 			n = CDR(n);
21748 			continue;
21749 		}
21750 
21751 		// check to see if this ship/wing has departed.
21752 		if ( mission_log_get_time (LOG_SHIP_DEPARTED, name, NULL, NULL) || mission_log_get_time (LOG_WING_DEPARTED, name, NULL, NULL) ) {
21753 			n = CDR(n);
21754 			continue;
21755 		}
21756 
21757 		// check to see if this ship/wing has been destroyed.
21758 		if ( mission_log_get_time(LOG_SHIP_DESTROYED, name, NULL, NULL) || mission_log_get_time(LOG_WING_DESTROYED, name, NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, name, NULL, NULL)) {
21759 			n = CDR(n);
21760 			continue;
21761 		}
21762 
21763 		ship *sp;
21764 		if((wing_index = wing_name_lookup(name)) >= 0)
21765 		{
21766 			wing *wp = &Wings[wing_index];
21767 			for(int i = 0; i < wp->current_count; i++)
21768 			{
21769 				if(wp->ship_index[i] >= 0)
21770 				{
21771 					sp = &Ships[wp->ship_index[i]];
21772 					sp->shader_effect_active = true;
21773 					sp->shader_effect_num = effect_num;
21774 					sp->shader_effect_duration = effect_duration;
21775 					sp->shader_effect_start_time = timer_get_milliseconds();
21776 				}
21777 			}
21778 		}
21779 		else
21780 		{
21781 			if((ship_index = ship_name_lookup(name)) >= 0)
21782 			{
21783 				sp = &Ships[ship_index];
21784 				sp->shader_effect_active = true;
21785 				sp->shader_effect_num = effect_num;
21786 				sp->shader_effect_duration = effect_duration;
21787 				sp->shader_effect_start_time = timer_get_milliseconds();
21788 			}
21789 			else
21790 				mprintf(("Invalid Shipname in SEXP ship-effect\n"));
21791 		}
21792 
21793 		// move to next ship/wing in list
21794 		n = CDR(n);
21795 	}
21796 }
21797 
sexp_change_team_color(int n)21798 void sexp_change_team_color(int n) {
21799 	SCP_string new_color = CTEXT(n);
21800 	SCP_vector<ship*> shippointers;
21801 	n = CDR(n);
21802 	int fade_time = eval_num(n);
21803 
21804 	n = CDR(n);
21805 
21806 	while (n != -1) {
21807 		ship* shipp = sexp_get_ship_from_node(n);
21808 
21809 		if (shipp != NULL) {
21810 			shippointers.push_back(shipp);
21811 		}
21812 
21813 		n = CDR(n);
21814 	}
21815 
21816 	multi_start_callback();
21817 	multi_send_string(new_color);
21818 	multi_send_int(fade_time);
21819 	multi_send_int(shippointers.size());
21820 
21821 	for (SCP_vector<ship*>::iterator shipp = shippointers.begin(); shipp != shippointers.end(); ++shipp) {
21822 		ship* shp = *shipp;
21823 		multi_send_ship(shp);
21824 		if (fade_time == 0) {
21825 			shp->team_name = new_color;
21826 		} else {
21827 			shp->secondary_team_name = new_color;
21828 			shp->team_change_time = fade_time;
21829 			shp->team_change_timestamp = Missiontime;
21830 		}
21831 	}
21832 
21833 	multi_end_callback();
21834 }
21835 
multi_sexp_change_team_color()21836 void multi_sexp_change_team_color() {
21837 	SCP_string new_color = "<none>";
21838 	int fade_time = 0;
21839 	int n_ships = 0;
21840 
21841 	multi_get_string(new_color);
21842 	multi_get_int(fade_time);
21843 	multi_get_int(n_ships);
21844 
21845 	for (int i = 0; i < n_ships; ++i) {
21846 		ship* shipp;
21847 		multi_get_ship(shipp);
21848 		if (fade_time == 0) {
21849 			shipp->team_name = new_color;
21850 		} else {
21851 			shipp->secondary_team_name = new_color;
21852 			shipp->team_change_time = fade_time;
21853 			shipp->team_change_timestamp = Missiontime;
21854 		}
21855 	}
21856 }
21857 
sexp_call_ssm_strike(int node)21858 void sexp_call_ssm_strike(int node) {
21859 	int ssm_index = eval_num(node);
21860 	node = CDR(node);
21861 	int calling_team = iff_lookup(CTEXT(node));
21862 	if (ssm_index < 0 || calling_team < 0)
21863 		return;
21864 
21865 	for (int n = node; n != -1; n = CDR(n)) {
21866 		int ship_num = ship_name_lookup(CTEXT(n));
21867 		// don't do anything if the ship isn't there
21868 		if (ship_num >= 0) {
21869 			int obj_num = Ships[ship_num].objnum;
21870 			object *target_ship = &Objects[obj_num];
21871 			vec3d *start = &target_ship->pos;
21872 
21873 			ssm_create(target_ship, start, ssm_index, NULL, calling_team);
21874 		}
21875 	}
21876 }
21877 
21878 extern int Cheats_enabled;
sexp_player_is_cheating_bastard()21879 int sexp_player_is_cheating_bastard() {
21880 	if (Cheats_enabled) {
21881 		return SEXP_KNOWN_TRUE;
21882 	}
21883 
21884 	return SEXP_FALSE;
21885 }
21886 
sexp_set_motion_debris(int node)21887 void sexp_set_motion_debris(int node) {
21888 	Motion_debris_override = is_sexp_true(node);
21889 }
21890 
21891 /**
21892  * Returns the subsystem type if the name of a subsystem is actually a generic type (e.g \<all engines\> or \<all turrets\>
21893  */
get_generic_subsys(char * subsys_name)21894 int get_generic_subsys(char *subsys_name)
21895 {
21896 	if (!strcmp(subsys_name, SEXP_ALL_ENGINES_STRING)) {
21897 		return SUBSYSTEM_ENGINE;
21898 	} else if (!strcmp(subsys_name, SEXP_ALL_TURRETS_STRING)) {
21899 		return SUBSYSTEM_TURRET;
21900 	}
21901 
21902 	Assert(SUBSYSTEM_NONE == 0);
21903 	return SUBSYSTEM_NONE;
21904 }
21905 
21906 // Karajorma - returns false if the ship class has changed since the mission was parsed in
21907 // Changes can be from use of the change-ship-class SEXP, loadout or any future method
ship_class_unchanged(int ship_index)21908 bool ship_class_unchanged(int ship_index)
21909 {
21910 	p_object *p_objp;
21911 
21912 	ship *shipp = &Ships[ship_index];
21913 	p_objp = mission_parse_get_parse_object(shipp->ship_name);
21914 
21915 	if ((p_objp != NULL) && (p_objp->ship_class == shipp->ship_info_index)) {
21916 		return true;
21917 	}
21918 
21919 	return false;
21920 }
21921 
21922 // Goober5000 - needed because any nonzero integer value is "true"
is_sexp_true(int cur_node,int referenced_node)21923 int is_sexp_true(int cur_node, int referenced_node)
21924 {
21925 	int result = eval_sexp(cur_node, referenced_node);
21926 
21927 	return ((result == SEXP_TRUE) || (result == SEXP_KNOWN_TRUE));
21928 }
21929 
21930 
21931 /**
21932 *
21933 */
generate_event_log_flags_mask(int result)21934 int generate_event_log_flags_mask(int result)
21935 {
21936 	int matches = 0;
21937 	mission_event *current_event = &Mission_events[Event_index];
21938 
21939 	switch (result) {
21940 		case SEXP_TRUE:
21941 			matches |= MLF_SEXP_TRUE;
21942 			break;
21943 
21944 		case SEXP_FALSE:
21945 			matches |= MLF_SEXP_FALSE;
21946 			break;
21947 
21948 		default:
21949 			Error(LOCATION, "SEXP has a value which isn't true or false.");
21950 	}
21951 
21952 	if (( result == SEXP_TRUE ) || (result == SEXP_KNOWN_TRUE)) {
21953 		// now deal with the flags depending on repeat and trigger counts
21954 		switch (current_event->mission_log_flags) {
21955 			case MLF_FIRST_REPEAT_ONLY:
21956 				if (current_event->repeat_count > 1) {
21957 					matches |= MLF_FIRST_REPEAT_ONLY;
21958 				}
21959 				break;
21960 
21961 			case MLF_LAST_REPEAT_ONLY:
21962 				if (current_event->repeat_count == 1) {
21963 					matches |= MLF_LAST_REPEAT_ONLY;
21964 				}
21965 				break;
21966 
21967 			case MLF_FIRST_TRIGGER_ONLY:
21968 				if (current_event->trigger_count > 1) {
21969 					matches |= MLF_FIRST_TRIGGER_ONLY;
21970 				}
21971 				break;
21972 
21973 			case MLF_LAST_TRIGGER_ONLY:
21974 				if ((current_event->trigger_count == 1) && (current_event->flags & MEF_USING_TRIGGER_COUNT)) {
21975 					matches |= MLF_LAST_TRIGGER_ONLY;
21976 				}
21977 				break;
21978 		}
21979 	}
21980 
21981 	return matches;
21982 }
21983 
current_log_to_backup_log_buffer()21984 void current_log_to_backup_log_buffer()
21985 {
21986 	Mission_events[Event_index].backup_log_buffer.clear();
21987 	if (!(Mission_events[Event_index].mission_log_flags & MLF_STATE_CHANGE)){
21988 		return;
21989 	}
21990 
21991 	for (int i = 0; i < (int)Current_event_log_buffer->size(); i++) {
21992 		Mission_events[Event_index].backup_log_buffer.push_back(Current_event_log_buffer->at(i));
21993 	}
21994 }
21995 
maybe_write_previous_event_to_log(int result)21996 void maybe_write_previous_event_to_log(int result)
21997 {
21998 	mission_event *this_event = &Mission_events[Event_index];
21999 
22000 	// if the old log is empty, all we do is record the result for the next evaluation
22001 	// the old log should only be empty at mission start
22002 	if (this_event->backup_log_buffer.empty()) {
22003 		this_event->previous_result = result;
22004 		return;
22005 	}
22006 
22007 	// if there's no change in state, we don't write the previous state to the log
22008 	if ((this_event->mission_log_flags & MLF_STATE_CHANGE) && (result == this_event->previous_result)) {
22009 		current_log_to_backup_log_buffer();
22010 		return;
22011 	}
22012 
22013 	log_string(LOGFILE_EVENT_LOG, "Event has changed state. Old state");
22014 	while (!this_event->backup_log_buffer.empty()) {
22015 		log_string(LOGFILE_EVENT_LOG, this_event->backup_log_buffer.back().c_str());
22016 		this_event->backup_log_buffer.pop_back();
22017 	}
22018 	log_string(LOGFILE_EVENT_LOG, "New state");
22019 
22020 	// backup the current buffer as this may be a repeating event
22021 	current_log_to_backup_log_buffer();
22022 }
22023 
22024 /**
22025 * Checks the mission logs flags for this event and writes to the log if this has been asked for
22026 */
maybe_write_to_event_log(int result)22027 void maybe_write_to_event_log(int result)
22028 {
22029 	char buffer [256];
22030 
22031 	int mask = generate_event_log_flags_mask(result);
22032 	sprintf(buffer, "Event: %s at mission time %d seconds (%d milliseconds)", Mission_events[Event_index].name, f2i(Missiontime), f2i((longlong)Missiontime * 1000));
22033 	Current_event_log_buffer->push_back(buffer);
22034 
22035 	if (!Snapshot_all_events && (!(mask &=  Mission_events[Event_index].mission_log_flags))) {
22036 		current_log_to_backup_log_buffer();
22037 		Current_event_log_buffer->clear();
22038 		return;
22039 	}
22040 
22041 	// remove some of the flags
22042 	if (mask & (MLF_FIRST_REPEAT_ONLY | MLF_FIRST_TRIGGER_ONLY)) {
22043 		Mission_events[Event_index].mission_log_flags &= ~(MLF_FIRST_REPEAT_ONLY | MLF_FIRST_TRIGGER_ONLY) ;
22044 	}
22045 
22046 	if (Mission_events[Event_index].mission_log_flags & MLF_STATE_CHANGE) {
22047 		maybe_write_previous_event_to_log(result);
22048 	}
22049 
22050 	while (!Current_event_log_buffer->empty()) {
22051 		log_string(LOGFILE_EVENT_LOG, Current_event_log_buffer->back().c_str());
22052 		Current_event_log_buffer->pop_back();
22053 	}
22054 	log_string(LOGFILE_EVENT_LOG, "");
22055 
22056 }
22057 
22058 /**
22059 * Returns the constant used as a SEXP's result as text for printing to the event log
22060 */
sexp_get_result_as_text(int result)22061 char *sexp_get_result_as_text(int result)
22062 {
22063 	switch (result) {
22064 		case SEXP_TRUE:
22065 			return "TRUE";
22066 
22067 		case SEXP_FALSE:
22068 			return "FALSE";
22069 
22070 		case SEXP_KNOWN_FALSE:
22071 			return "ALWAYS FALSE";
22072 
22073 		case SEXP_KNOWN_TRUE:
22074 			return "ALWAYS TRUE";
22075 
22076 		case SEXP_UNKNOWN:
22077 			return "UNKNOWN";
22078 
22079 		case SEXP_NAN:
22080 			return "NOT A NUMBER";
22081 
22082 		case SEXP_NAN_FOREVER:
22083 			return "CAN NEVER BE A NUMBER";
22084 
22085 		case SEXP_CANT_EVAL:
22086 			return "CAN'T EVALUATE";
22087 
22088 		default:
22089 			return NULL;
22090 	}
22091 }
22092 
22093 /**
22094 * Checks the mission logs flags for this event and writes to the log if this has been asked for
22095 */
add_to_event_log_buffer(int op_num,int result)22096 void add_to_event_log_buffer(int op_num, int result)
22097 {
22098 	Assertion ((Current_event_log_buffer != NULL) &&
22099 				(Current_event_log_variable_buffer != NULL)&&
22100 				(Current_event_log_argument_buffer != NULL), "Attempting to write to a non-existent log buffer");
22101 
22102 	if (op_num == -1) {
22103 		nprintf(("SEXP", "ERROR: op_num function returned %i, this should not happen. Contact a coder.\n", op_num));
22104 		return; //How does this happen?
22105 	}
22106 
22107 	char buffer[TOKEN_LENGTH];
22108 	SCP_string tmp;
22109 	tmp.append(Operators[op_num].text);
22110 	tmp.append(" returned ");
22111 
22112 	if ((Operators[op_num].type & (SEXP_INTEGER_OPERATOR|SEXP_ARITHMETIC_OPERATOR)) || (sexp_get_result_as_text(result) == NULL)) {
22113 		sprintf(buffer, "%d", result);
22114 		tmp.append(buffer);
22115 	}
22116 	else {
22117 		tmp.append(sexp_get_result_as_text(result));
22118 	}
22119 
22120 	if (True_loop_argument_sexps && !Sexp_replacement_arguments.empty()) {
22121 		tmp.append(" for argument ");
22122 		tmp.append(Sexp_replacement_arguments.back());
22123 	}
22124 
22125 	if (!Current_event_log_argument_buffer->empty()) {
22126 		tmp.append(" for the following arguments");
22127 		while (!Current_event_log_argument_buffer->empty()) {
22128 			tmp.append("\n");
22129 			tmp.append(Current_event_log_argument_buffer->back().c_str());
22130 			Current_event_log_argument_buffer->pop_back();
22131 		}
22132 	}
22133 
22134 	if (!Current_event_log_variable_buffer->empty()) {
22135 		tmp.append("\nVariables:\n");
22136 		while (!Current_event_log_variable_buffer->empty()) {
22137 			tmp.append(Current_event_log_variable_buffer->back().c_str());
22138 			Current_event_log_variable_buffer->pop_back();
22139 			tmp.append("[");
22140 			tmp.append(Current_event_log_variable_buffer->back().c_str());
22141 			Current_event_log_variable_buffer->pop_back();
22142 			tmp.append("]");
22143 		}
22144 	}
22145 
22146 	Current_event_log_buffer->push_back(tmp);
22147 }
22148 
22149 /**
22150  * High-level sexpression evaluator
22151  */
eval_sexp(int cur_node,int referenced_node)22152 int eval_sexp(int cur_node, int referenced_node)
22153 {
22154 	int node, type, sexp_val = UNINITIALIZED;
22155 	if (cur_node == -1)  // empty list, i.e. sexp: ( )
22156 		return SEXP_FALSE;
22157 
22158 	Assert(cur_node >= 0);			// we have special sexp nodes <= -1!!!  MWA
22159 									// which should be intercepted before we get here.  HOFFOSS
22160 	type = SEXP_NODE_TYPE(cur_node);
22161 	Assert( (type == SEXP_LIST) || (type == SEXP_ATOM) );
22162 
22163 	// trap known true and known false sexpressions.  We don't trap on SEXP_NAN sexpressions since
22164 	// they may yet evaluate to true or false.
22165 
22166 	if (Sexp_nodes[cur_node].value == SEXP_KNOWN_TRUE) {
22167 		if (Log_event) {
22168 			add_to_event_log_buffer(get_operator_index(cur_node), SEXP_KNOWN_TRUE);
22169 		}
22170 		return SEXP_TRUE;
22171 	}
22172 	else if (Sexp_nodes[cur_node].value == SEXP_KNOWN_FALSE) {
22173 		if (Log_event) {
22174 			add_to_event_log_buffer(get_operator_index(cur_node), SEXP_KNOWN_FALSE);
22175 		}
22176 		return SEXP_FALSE;
22177 	}
22178 
22179 	if (Sexp_nodes[cur_node].first != -1) {
22180 		node = CAR(cur_node);
22181 		sexp_val = eval_sexp(node);
22182 		Sexp_nodes[cur_node].value = Sexp_nodes[node].value;	// higher level node gets node value
22183 		return sexp_val;
22184 
22185 	} else {
22186 		int op_num;
22187 
22188 		node = CDR(cur_node);		// makes reading the next bit of code a little easier.
22189 
22190 		op_num = get_operator_const(cur_node);
22191 		// add the op_num to the stack if it is an actual operator rather than a number
22192 		if (op_num) {
22193 			Current_sexp_operator.push_back(op_num);
22194 		}
22195 		switch ( op_num ) {
22196 		// arithmetic operators will always return just their value
22197 			case OP_PLUS:
22198 				sexp_val = add_sexps(node);
22199 				break;
22200 
22201 			case OP_MINUS:
22202 				sexp_val = sub_sexps(node);
22203 				break;
22204 
22205 			case OP_MUL:
22206 				sexp_val = mul_sexps(node);
22207 				break;
22208 
22209 			case OP_MOD:
22210 				sexp_val = mod_sexps(node);
22211 				break;
22212 
22213 			case OP_DIV:
22214 				sexp_val = div_sexps(node);
22215 				break;
22216 
22217 			case OP_RAND:
22218 			case OP_RAND_MULTIPLE:
22219 				sexp_val = rand_sexp( node, (op_num == OP_RAND_MULTIPLE) );
22220 				break;
22221 
22222 			case OP_ABS:
22223 				sexp_val = abs_sexp(node);
22224 				break;
22225 
22226 			case OP_MIN:
22227 				sexp_val = min_sexp(node);
22228 				break;
22229 
22230 			case OP_MAX:
22231 				sexp_val = max_sexp(node);
22232 				break;
22233 
22234 			case OP_AVG:
22235 				sexp_val = avg_sexp(node);
22236 				break;
22237 
22238 			case OP_POW:
22239 				sexp_val = pow_sexp(node);
22240 				break;
22241 
22242 			case OP_SIGNUM:
22243 				sexp_val = signum_sexp(node);
22244 				break;
22245 
22246 			case OP_SET_BIT:
22247 			case OP_UNSET_BIT:
22248 				sexp_val = sexp_set_bit(node, op_num == OP_SET_BIT);
22249 				break;
22250 
22251 			case OP_IS_BIT_SET:
22252 				sexp_val = sexp_is_bit_set(node);
22253 				break;
22254 
22255 			case OP_BITWISE_AND:
22256 				sexp_val = sexp_bitwise_and(node);
22257 				break;
22258 
22259 			case OP_BITWISE_OR:
22260 				sexp_val = sexp_bitwise_or(node);
22261 				break;
22262 
22263 			case OP_BITWISE_NOT:
22264 				sexp_val = sexp_bitwise_not(node);
22265 				break;
22266 
22267 			case OP_BITWISE_XOR:
22268 				sexp_val = sexp_bitwise_xor(node);
22269 				break;
22270 
22271 			// boolean operators can have one of the special sexp values (known true, known false, unknown)
22272 			case OP_TRUE:
22273 				sexp_val = SEXP_KNOWN_TRUE;
22274 				break;
22275 
22276 			case OP_FALSE:
22277 				sexp_val = SEXP_KNOWN_FALSE;
22278 				break;
22279 
22280 			case OP_OR:
22281 				sexp_val = sexp_or(node);
22282 				break;
22283 
22284 			case OP_AND:
22285 				sexp_val = sexp_and(node);
22286 				break;
22287 
22288 			case OP_AND_IN_SEQUENCE:
22289 				sexp_val = sexp_and_in_sequence(node);
22290 				break;
22291 
22292 			case OP_XOR:
22293 				sexp_val = sexp_xor(node);
22294 				break;
22295 
22296 			case OP_EQUALS:
22297 			case OP_GREATER_THAN:
22298 			case OP_LESS_THAN:
22299 			case OP_NOT_EQUAL:
22300 			case OP_GREATER_OR_EQUAL:
22301 			case OP_LESS_OR_EQUAL:
22302 				sexp_val = sexp_number_compare( node, op_num );
22303 				break;
22304 
22305 			case OP_STRING_GREATER_THAN:
22306 			case OP_STRING_LESS_THAN:
22307 			case OP_STRING_EQUALS:
22308 				sexp_val = sexp_string_compare( node, op_num );
22309 				break;
22310 
22311 			case OP_IS_IFF:
22312 				sexp_val = sexp_is_iff(node);
22313 				break;
22314 
22315 			case OP_NOT:
22316 				sexp_val = sexp_not(node);
22317 				break;
22318 
22319 			case OP_PREVIOUS_GOAL_TRUE:
22320 				sexp_val = sexp_previous_goal_status( node, GOAL_COMPLETE );
22321 				break;
22322 
22323 			case OP_PREVIOUS_GOAL_FALSE:
22324 				sexp_val = sexp_previous_goal_status( node, GOAL_FAILED );
22325 				break;
22326 
22327 			case OP_PREVIOUS_GOAL_INCOMPLETE:
22328 				sexp_val = sexp_previous_goal_status( node, GOAL_INCOMPLETE );
22329 				break;
22330 
22331 			case OP_PREVIOUS_EVENT_TRUE:
22332 				sexp_val = sexp_previous_event_status( node, EVENT_SATISFIED );
22333 				break;
22334 
22335 			case OP_PREVIOUS_EVENT_FALSE:
22336 				sexp_val = sexp_previous_event_status( node, EVENT_FAILED );
22337 				break;
22338 
22339 			case OP_PREVIOUS_EVENT_INCOMPLETE:
22340 				sexp_val = sexp_previous_event_status( node, EVENT_INCOMPLETE );
22341 				break;
22342 
22343 			case OP_EVENT_TRUE:
22344 			case OP_EVENT_FALSE:
22345 				sexp_val = sexp_event_status( node, (op_num == OP_EVENT_TRUE?1:0) );
22346 				if ((sexp_val != SEXP_TRUE) && (sexp_val != SEXP_KNOWN_TRUE))
22347 					Sexp_useful_number = 0;  // indicate sexp isn't current yet
22348 				break;
22349 
22350 			case OP_EVENT_TRUE_DELAY:
22351 			case OP_EVENT_FALSE_DELAY:
22352 				sexp_val = sexp_event_delay_status( node, (op_num == OP_EVENT_TRUE_DELAY?1:0) );
22353 				break;
22354 
22355 			case OP_EVENT_TRUE_MSECS_DELAY:
22356 			case OP_EVENT_FALSE_MSECS_DELAY:
22357 				sexp_val = sexp_event_delay_status( node, (op_num == OP_EVENT_TRUE_MSECS_DELAY?1:0), true );
22358 				break;
22359 
22360 			case OP_GOAL_TRUE_DELAY:
22361 			case OP_GOAL_FALSE_DELAY:
22362 				sexp_val = sexp_goal_delay_status( node, (op_num == OP_GOAL_TRUE_DELAY?1:0) );
22363 				break;
22364 
22365 			case OP_EVENT_INCOMPLETE:
22366 				sexp_val = sexp_event_incomplete(node);
22367 				if ((sexp_val != SEXP_TRUE) && (sexp_val != SEXP_KNOWN_TRUE))
22368 					Sexp_useful_number = 0;  // indicate sexp isn't current yet
22369 				break;
22370 
22371 			case OP_GOAL_INCOMPLETE:
22372 				sexp_val = sexp_goal_incomplete(node);
22373 				break;
22374 
22375 			// destroy type sexpressions
22376 			case OP_IS_DESTROYED:
22377 				sexp_val = sexp_is_destroyed( node, NULL );
22378 				break;
22379 
22380 			case OP_WAS_DESTROYED_BY_DELAY:
22381 				sexp_val = sexp_was_destroyed_by_delay(node);
22382 				break;
22383 
22384 			case OP_IS_SUBSYSTEM_DESTROYED:
22385 				sexp_val = sexp_is_subsystem_destroyed(node);
22386 				break;
22387 
22388 			case OP_HAS_ARRIVED:
22389 				sexp_val = sexp_has_arrived( node, NULL );
22390 				break;
22391 
22392 			case OP_HAS_DEPARTED:
22393 				sexp_val = sexp_has_departed( node, NULL );
22394 				break;
22395 
22396 			case OP_IS_DISABLED:
22397 				sexp_val = sexp_is_disabled( node, NULL );
22398 				break;
22399 
22400 			case OP_IS_DISARMED:
22401 				sexp_val = sexp_is_disarmed( node, NULL );
22402 				break;
22403 
22404 			case OP_WAYPOINTS_DONE:
22405 				sexp_val = sexp_are_waypoints_done(node);
22406 				break;
22407 
22408 			// objective operators that use a delay
22409 			case OP_IS_DESTROYED_DELAY:
22410 				sexp_val = sexp_is_destroyed_delay(node);
22411 				break;
22412 
22413 			case OP_IS_SUBSYSTEM_DESTROYED_DELAY:
22414 				sexp_val = sexp_is_subsystem_destroyed_delay(node);
22415 				break;
22416 
22417 			case OP_HAS_DOCKED:
22418 			case OP_HAS_UNDOCKED:
22419 			case OP_HAS_DOCKED_DELAY:
22420 			case OP_HAS_UNDOCKED_DELAY:
22421 				sexp_val = sexp_has_docked_or_undocked(node, op_num);
22422 				break;
22423 
22424 			case OP_HAS_ARRIVED_DELAY:
22425 				sexp_val = sexp_has_arrived_delay(node);
22426 				break;
22427 
22428 			case OP_HAS_DEPARTED_DELAY:
22429 				sexp_val = sexp_has_departed_delay(node);
22430 				break;
22431 
22432 			case OP_IS_DISABLED_DELAY:
22433 				sexp_val = sexp_is_disabled_delay(node);
22434 				break;
22435 
22436 			case OP_IS_DISARMED_DELAY:
22437 				sexp_val = sexp_is_disarmed_delay(node);
22438 				break;
22439 
22440 			case OP_WAYPOINTS_DONE_DELAY:
22441 				sexp_val = sexp_are_waypoints_done_delay(node);
22442 				break;
22443 
22444 			case OP_SHIP_TYPE_DESTROYED:
22445 				sexp_val = sexp_ship_type_destroyed(node);
22446 				break;
22447 
22448 			// time based sexpressions
22449 			case OP_HAS_TIME_ELAPSED:
22450 				sexp_val = sexp_has_time_elapsed(node);
22451 				break;
22452 
22453 			case OP_MODIFY_VARIABLE:
22454 				sexp_modify_variable(node);
22455 				sexp_val = SEXP_TRUE;	// SEXP_TRUE means only do once.
22456 				break;
22457 
22458 			case OP_GET_VARIABLE_BY_INDEX:
22459 				sexp_val = sexp_get_variable_by_index(node);
22460 				break;
22461 
22462 			case OP_SET_VARIABLE_BY_INDEX:
22463 				sexp_set_variable_by_index(node);
22464 				sexp_val = SEXP_TRUE;
22465 				break;
22466 
22467 			case OP_COPY_VARIABLE_FROM_INDEX:
22468 				sexp_copy_variable_from_index(node);
22469 				sexp_val = SEXP_TRUE;
22470 				break;
22471 
22472 			case OP_COPY_VARIABLE_BETWEEN_INDEXES:
22473 				sexp_copy_variable_between_indexes(node);
22474 				sexp_val = SEXP_TRUE;
22475 				break;
22476 
22477 			case OP_TIME_SHIP_DESTROYED:
22478 				sexp_val = sexp_time_destroyed(node);
22479 				break;
22480 
22481 			case OP_TIME_WING_DESTROYED:
22482 				sexp_val = sexp_time_wing_destroyed(node);
22483 				break;
22484 
22485 			case OP_TIME_SHIP_ARRIVED:
22486 				sexp_val = sexp_time_ship_arrived(node);
22487 				break;
22488 
22489 			case OP_TIME_WING_ARRIVED:
22490 				sexp_val = sexp_time_wing_arrived(node);
22491 				break;
22492 
22493 			case OP_TIME_SHIP_DEPARTED:
22494 				sexp_val = sexp_time_ship_departed(node);
22495 				break;
22496 
22497 			case OP_TIME_WING_DEPARTED:
22498 				sexp_val = sexp_time_wing_departed(node);
22499 				break;
22500 
22501 			case OP_MISSION_TIME:
22502 				sexp_val = sexp_mission_time();
22503 				break;
22504 
22505 			case OP_MISSION_TIME_MSECS:
22506 				sexp_val = sexp_mission_time_msecs();
22507 				break;
22508 
22509 			case OP_TIME_DOCKED:
22510 				sexp_val = sexp_time_docked(node);
22511 				break;
22512 
22513 			case OP_TIME_UNDOCKED:
22514 				sexp_val = sexp_time_undocked(node);
22515 				break;
22516 
22517 			case OP_AFTERBURNER_LEFT:
22518 				sexp_val = sexp_get_energy_pct(node, op_num);
22519 				break;
22520 
22521 			case OP_WEAPON_ENERGY_LEFT:
22522 				sexp_val = sexp_get_energy_pct(node, op_num);
22523 				break;
22524 
22525 			case OP_SHIELDS_LEFT:
22526 				sexp_val = sexp_shields_left(node);
22527 				break;
22528 
22529 			case OP_HITS_LEFT:
22530 				sexp_val = sexp_hits_left(node);
22531 				break;
22532 
22533 			case OP_HITS_LEFT_SUBSYSTEM:
22534 				sexp_val = sexp_hits_left_subsystem(node);
22535 				break;
22536 
22537 			case OP_HITS_LEFT_SUBSYSTEM_GENERIC:
22538 				sexp_val = sexp_hits_left_subsystem_generic(node);
22539 				break;
22540 
22541 			case OP_HITS_LEFT_SUBSYSTEM_SPECIFIC:
22542 				sexp_val = sexp_hits_left_subsystem_specific(node);
22543 				break;
22544 
22545 			case OP_SIM_HITS_LEFT:
22546 				sexp_val = sexp_sim_hits_left(node);
22547 				break;
22548 
22549 			case OP_SPECIAL_WARP_DISTANCE:
22550 				sexp_val = sexp_special_warp_dist(node);
22551 				break;
22552 
22553 			case OP_DISTANCE:
22554 				sexp_val = sexp_distance(node);
22555 				break;
22556 
22557 			case OP_DISTANCE_SUBSYSTEM:
22558 				sexp_val = sexp_distance_subsystem(node);
22559 				break;
22560 
22561 			case OP_NUM_WITHIN_BOX:
22562 				sexp_val = sexp_num_within_box(node);
22563 				break;
22564 
22565 			case OP_IS_IN_BOX:
22566 				sexp_val = sexp_is_in_box(node);
22567 				break;
22568 
22569 			case OP_IS_IN_MISSION:
22570 				sexp_val = sexp_is_in_mission(node);
22571 				break;
22572 
22573 			case OP_IS_SHIP_VISIBLE:
22574 				sexp_val = sexp_is_ship_visible(node);
22575 				break;
22576 
22577 			case OP_IS_SHIP_STEALTHY:
22578 				sexp_val = sexp_is_ship_stealthy(node);
22579 				break;
22580 
22581 			case OP_IS_FRIENDLY_STEALTH_VISIBLE:
22582 				sexp_val = sexp_is_friendly_stealth_visible(node);
22583 				break;
22584 
22585 			case OP_TEAM_SCORE:
22586 				sexp_val = sexp_team_score(node);
22587 				break;
22588 
22589 			case OP_LAST_ORDER_TIME:
22590 				sexp_val = sexp_last_order_time(node);
22591 				break;
22592 
22593 			case OP_NUM_PLAYERS:
22594 				sexp_val = sexp_num_players();
22595 				break;
22596 
22597 			case OP_SKILL_LEVEL_AT_LEAST:
22598 				sexp_val = sexp_skill_level_at_least(node);
22599 				break;
22600 
22601 			case OP_IS_CARGO_KNOWN:
22602 			case OP_CARGO_KNOWN_DELAY:
22603 				sexp_val = sexp_is_cargo_known( node, (op_num==OP_IS_CARGO_KNOWN)?0:1 );
22604 				break;
22605 
22606 			case OP_HAS_BEEN_TAGGED_DELAY:
22607 				sexp_val = sexp_has_been_tagged_delay(node);
22608 				break;
22609 
22610 			case OP_ARE_SHIP_FLAGS_SET:
22611 				sexp_val = sexp_are_ship_flags_set(node);
22612 				break;
22613 
22614 			case OP_CAP_SUBSYS_CARGO_KNOWN_DELAY:
22615 				sexp_val = sexp_cap_subsys_cargo_known_delay(node);
22616 				break;
22617 
22618 			case OP_WAS_PROMOTION_GRANTED:
22619 				sexp_val = sexp_was_promotion_granted(node);
22620 				break;
22621 
22622 			case OP_WAS_MEDAL_GRANTED:
22623 				sexp_val = sexp_was_medal_granted(node);
22624 				break;
22625 
22626 			case OP_GET_DAMAGE_CAUSED:
22627 				sexp_val = sexp_get_damage_caused(node);
22628 				break;
22629 
22630 			case OP_PERCENT_SHIPS_ARRIVED:
22631 			case OP_PERCENT_SHIPS_DEPARTED:
22632 			case OP_PERCENT_SHIPS_DESTROYED:
22633 			case OP_PERCENT_SHIPS_DISARMED:
22634 			case OP_PERCENT_SHIPS_DISABLED:
22635 				sexp_val = sexp_percent_ships_arrive_depart_destroy_disarm_disable(node, op_num);
22636 				break;
22637 
22638 			case OP_DEPART_NODE_DELAY:
22639 				sexp_val = sexp_depart_node_delay(node);
22640 				break;
22641 
22642 			case OP_DESTROYED_DEPARTED_DELAY:
22643 				sexp_val = sexp_destroyed_departed_delay(node);
22644 				break;
22645 
22646 			// conditional sexpressions
22647 			case OP_WHEN:
22648 			case OP_WHEN_ARGUMENT:
22649 			case OP_IF_THEN_ELSE:
22650 				sexp_val = eval_when( node, op_num );
22651 				break;
22652 
22653 			case OP_PERFORM_ACTIONS:
22654 				sexp_val = eval_perform_actions( node );
22655 				break;
22656 
22657 			case OP_COND:
22658 				sexp_val = eval_cond(node);
22659 				break;
22660 
22661 			// Goober5000: special case; evaluate like when, but flush the sexp tree
22662 			// and return SEXP_NAN so this will always be re-evaluated
22663 			case OP_EVERY_TIME:
22664 			case OP_EVERY_TIME_ARGUMENT:
22665 				eval_when( node, op_num );
22666 				flush_sexp_tree(node);
22667 				sexp_val = SEXP_NAN;
22668 				break;
22669 
22670 			// Goober5000
22671 			case OP_ANY_OF:
22672 				sexp_val = eval_any_of( cur_node, referenced_node );
22673 				break;
22674 
22675 			// Goober5000
22676 			case OP_EVERY_OF:
22677 				sexp_val = eval_every_of( cur_node, referenced_node );
22678 				break;
22679 
22680 			// Goober5000 and Karajorma
22681 			case OP_RANDOM_OF:
22682 			case OP_RANDOM_MULTIPLE_OF:
22683 				sexp_val = eval_random_of( cur_node, referenced_node, (op_num == OP_RANDOM_MULTIPLE_OF) );
22684 				break;
22685 
22686 			// Goober5000
22687 			case OP_NUMBER_OF:
22688 				sexp_val = eval_number_of( cur_node, referenced_node );
22689 				break;
22690 
22691 			// Karajorma
22692 			case OP_IN_SEQUENCE:
22693 				sexp_val = eval_in_sequence( cur_node, referenced_node );
22694 				break;
22695 
22696 			// Goober5000
22697 			case OP_FOR_COUNTER:
22698 				sexp_val = eval_for_counter( cur_node, referenced_node );
22699 				break;
22700 
22701 			// Karajorma
22702 			case OP_INVALIDATE_ALL_ARGUMENTS:
22703 			case OP_VALIDATE_ALL_ARGUMENTS:
22704 				sexp_change_all_argument_validity(cur_node, (op_num == OP_INVALIDATE_ALL_ARGUMENTS));
22705 				sexp_val = SEXP_TRUE;
22706 			break;
22707 
22708 			// Goober5000
22709 			case OP_INVALIDATE_ARGUMENT:
22710 			case OP_VALIDATE_ARGUMENT:
22711 				sexp_change_argument_validity(node, (op_num == OP_INVALIDATE_ARGUMENT));
22712 				sexp_val = SEXP_TRUE;
22713 				break;
22714 
22715 			case OP_DO_FOR_VALID_ARGUMENTS:
22716 				// do-for-valid-arguments should only ever be called within eval_when()
22717 				Int3();
22718 				break;
22719 
22720 			case OP_NUM_VALID_ARGUMENTS:
22721 				sexp_val = sexp_num_valid_arguments( cur_node );
22722 				break;
22723 
22724 			// sexpressions with side effects
22725 			case OP_CHANGE_IFF:
22726 				sexp_change_iff(node);
22727 				sexp_val = SEXP_TRUE;
22728 				break;
22729 
22730 			case OP_ADD_SHIP_GOAL:
22731 				sexp_add_ship_goal(node);
22732 				sexp_val = SEXP_TRUE;
22733 				break;
22734 
22735 			case OP_ADD_WING_GOAL:
22736 				sexp_add_wing_goal(node);
22737 				sexp_val = SEXP_TRUE;
22738 				break;
22739 
22740 			case OP_ADD_GOAL:
22741 				sexp_add_goal(node);
22742 				sexp_val = SEXP_TRUE;
22743 				break;
22744 
22745 			case OP_REMOVE_GOAL:
22746 				sexp_remove_goal(node);
22747 				sexp_val = SEXP_TRUE;
22748 				break;
22749 
22750 			case OP_CLEAR_SHIP_GOALS:
22751 				sexp_clear_ship_goals(node);
22752 				sexp_val = SEXP_TRUE;
22753 				break;
22754 
22755 			case OP_CLEAR_WING_GOALS:
22756 				sexp_clear_wing_goals(node);
22757 				sexp_val = SEXP_TRUE;
22758 				break;
22759 
22760 			case OP_CLEAR_GOALS:
22761 				sexp_clear_goals(node);
22762 				sexp_val = SEXP_TRUE;
22763 				break;
22764 
22765 			case OP_PROTECT_SHIP:
22766 			case OP_UNPROTECT_SHIP:
22767 				sexp_protect_ships(node, (op_num == OP_PROTECT_SHIP));
22768 				sexp_val = SEXP_TRUE;
22769 				break;
22770 
22771 			case OP_BEAM_PROTECT_SHIP:
22772 			case OP_BEAM_UNPROTECT_SHIP:
22773 				sexp_beam_protect_ships(node, (op_num == OP_BEAM_PROTECT_SHIP));
22774 				sexp_val = SEXP_TRUE;
22775 				break;
22776 
22777 			case OP_TURRET_PROTECT_SHIP:
22778 			case OP_TURRET_UNPROTECT_SHIP:
22779 				sexp_turret_protect_ships(node, (op_num == OP_TURRET_PROTECT_SHIP));
22780 				sexp_val = SEXP_TRUE;
22781 				break;
22782 
22783 			case OP_SHIP_STEALTHY:
22784 			case OP_SHIP_UNSTEALTHY:
22785 				sexp_ships_stealthy(node, (op_num == OP_SHIP_STEALTHY));
22786 				sexp_val = SEXP_TRUE;
22787 				break;
22788 
22789 			case OP_FRIENDLY_STEALTH_INVISIBLE:
22790 			case OP_FRIENDLY_STEALTH_VISIBLE:
22791 				sexp_friendly_stealth_invisible(node, (op_num == OP_FRIENDLY_STEALTH_INVISIBLE));
22792 				sexp_val = SEXP_TRUE;
22793 				break;
22794 
22795 			case OP_SHIP_INVISIBLE:
22796 			case OP_SHIP_VISIBLE:
22797 				sexp_ships_visible(node, (op_num == OP_SHIP_VISIBLE));
22798 				sexp_val = SEXP_TRUE;
22799 				break;
22800 
22801 			// Goober5000
22802 			case OP_SHIP_TAG:
22803 			case OP_SHIP_UNTAG:
22804 				sexp_ship_tag(node, (op_num == OP_SHIP_TAG));
22805 				sexp_val = SEXP_TRUE;
22806 				break;
22807 
22808 			case OP_SHIP_CHANGE_ALT_NAME:
22809 				sexp_ship_change_alt_name(node);
22810 				sexp_val = SEXP_TRUE;
22811 				break;
22812 
22813 			case OP_SHIP_CHANGE_CALLSIGN:
22814 				sexp_ship_change_callsign(node);
22815 				sexp_val = SEXP_TRUE;
22816 				break;
22817 
22818 			case OP_SET_DEATH_MESSAGE:
22819 				sexp_set_death_message(node);
22820 				sexp_val = SEXP_TRUE;
22821 				break;
22822 
22823 			case OP_ALTER_SHIP_FLAG:
22824 				sexp_alter_ship_flag(node);
22825 				sexp_val = SEXP_TRUE;
22826 				break;
22827 
22828 			case OP_SHIP_VULNERABLE:
22829 			case OP_SHIP_INVULNERABLE:
22830 				sexp_ships_invulnerable(node, (op_num == OP_SHIP_INVULNERABLE));
22831 				sexp_val = SEXP_TRUE;
22832 				break;
22833 
22834 			case OP_SHIP_BOMB_TARGETABLE:
22835 			case OP_SHIP_BOMB_UNTARGETABLE:
22836 				sexp_ships_bomb_targetable(node, (op_num == OP_SHIP_BOMB_TARGETABLE));
22837 				sexp_val = SEXP_TRUE;
22838 				break;
22839 
22840 			case OP_SHIP_GUARDIAN:
22841 			case OP_SHIP_NO_GUARDIAN:
22842 				sexp_ships_guardian(node, (op_num == OP_SHIP_GUARDIAN));
22843 				sexp_val = SEXP_TRUE;
22844 				break;
22845 
22846 			case OP_SHIP_GUARDIAN_THRESHOLD:
22847 				sexp_ship_guardian_threshold(node);
22848 				sexp_val = SEXP_TRUE;
22849 				break;
22850 
22851 			case OP_SHIP_SUBSYS_GUARDIAN_THRESHOLD:
22852 				sexp_ship_subsys_guardian_threshold(node);
22853 				sexp_val = SEXP_TRUE;
22854 				break;
22855 
22856 			case OP_SHIP_SUBSYS_TARGETABLE:
22857 				sexp_ship_deal_with_subsystem_flag(node, SSF_UNTARGETABLE, true, false);
22858 				sexp_val = SEXP_TRUE;
22859 				break;
22860 
22861 			case OP_SHIP_SUBSYS_UNTARGETABLE:
22862 				sexp_ship_deal_with_subsystem_flag(node, SSF_UNTARGETABLE, true, true);
22863 				sexp_val = SEXP_TRUE;
22864 				break;
22865 
22866 			case OP_TURRET_SUBSYS_TARGET_DISABLE:
22867 				sexp_val = SEXP_TRUE;
22868 				sexp_ship_deal_with_subsystem_flag(node, SSF_NO_SS_TARGETING, false, true);
22869 				break;
22870 
22871 			case OP_TURRET_SUBSYS_TARGET_ENABLE:
22872 				sexp_val = SEXP_TRUE;
22873 				sexp_ship_deal_with_subsystem_flag(node, SSF_NO_SS_TARGETING, false, false);
22874 				break;
22875 
22876 			case OP_SHIP_SUBSYS_NO_REPLACE:
22877 				sexp_ship_deal_with_subsystem_flag(node, SSF_NO_REPLACE, true);
22878 				sexp_val = SEXP_TRUE;
22879 				break;
22880 
22881 			case OP_SHIP_SUBSYS_NO_LIVE_DEBRIS:
22882 				sexp_ship_deal_with_subsystem_flag(node, SSF_NO_LIVE_DEBRIS, true);
22883 				sexp_val = SEXP_TRUE;
22884 				break;
22885 
22886 			case OP_SHIP_SUBSYS_VANISHED:
22887 				sexp_ship_deal_with_subsystem_flag(node, SSF_VANISHED, true);
22888 				sexp_val = SEXP_TRUE;
22889 				break;
22890 
22891 			case OP_SHIP_SUBSYS_IGNORE_IF_DEAD:
22892 				sexp_ship_deal_with_subsystem_flag(node, SSF_MISSILES_IGNORE_IF_DEAD, false);
22893 				sexp_val = SEXP_TRUE;
22894 				break;
22895 
22896 			case OP_SHIP_CREATE:
22897 				sexp_ship_create(node);
22898 				sexp_val = SEXP_TRUE;
22899 				break;
22900 
22901 			case OP_WEAPON_CREATE:
22902 				sexp_weapon_create(node);
22903 				sexp_val = SEXP_TRUE;
22904 				break;
22905 
22906 			case OP_SHIP_VANISH:
22907 				sexp_ship_vanish(node);
22908 				sexp_val = SEXP_TRUE;
22909 				break;
22910 
22911 			case OP_DESTROY_INSTANTLY:
22912 				sexp_destroy_instantly(node);
22913 				sexp_val = SEXP_TRUE;
22914 				break;
22915 
22916 			//-Sesquipedalian
22917 			case OP_SHIELDS_ON:
22918 			case OP_SHIELDS_OFF:
22919 				sexp_shields_off(node, (op_num == OP_SHIELDS_OFF));
22920 				sexp_val = SEXP_TRUE;
22921 				break;
22922 
22923 			//-Sesquipedalian
22924 			case OP_KAMIKAZE:
22925 				sexp_kamikaze(node, (op_num == OP_KAMIKAZE));
22926 				sexp_val = SEXP_TRUE;
22927 				break;
22928 
22929 			case OP_SET_CARGO:
22930 				sexp_set_cargo(node);
22931 				sexp_val = SEXP_TRUE;
22932 				break;
22933 
22934 			case OP_IS_CARGO:
22935 				sexp_val = sexp_is_cargo(node);
22936 				break;
22937 
22938 			case OP_CHANGE_AI_CLASS:
22939 				sexp_change_ai_class(node);
22940 				sexp_val = SEXP_TRUE;
22941 				break;
22942 
22943 			case OP_IS_AI_CLASS:
22944 				sexp_val = sexp_is_ai_class(node);
22945 				break;
22946 
22947 			case OP_IS_SHIP_TYPE:
22948 				sexp_val = sexp_is_ship_type(node);
22949 				break;
22950 
22951 			case OP_IS_SHIP_CLASS:
22952 				sexp_val = sexp_is_ship_class(node);
22953 				break;
22954 
22955 			case OP_CHANGE_SOUNDTRACK:
22956 				sexp_change_soundtrack(node);
22957 				sexp_val = SEXP_TRUE;
22958 				break;
22959 
22960 			case OP_PLAY_SOUND_FROM_TABLE:
22961 				sexp_play_sound_from_table(node);
22962 				sexp_val = SEXP_TRUE;
22963 				break;
22964 
22965 			case OP_PLAY_SOUND_FROM_FILE:
22966 				sexp_play_sound_from_file(node);
22967 				sexp_val = SEXP_TRUE;
22968 				break;
22969 
22970 			case OP_CLOSE_SOUND_FROM_FILE:
22971 				sexp_close_sound_from_file(node);
22972 				sexp_val = SEXP_TRUE;
22973 				break;
22974 
22975 			case OP_SET_SOUND_ENVIRONMENT:
22976 				sexp_set_sound_environment(node);
22977 				sexp_val = SEXP_TRUE;
22978 				break;
22979 
22980 			case OP_UPDATE_SOUND_ENVIRONMENT:
22981 				sexp_update_sound_environment(node);
22982 				sexp_val = SEXP_TRUE;
22983 				break;
22984 
22985 			case OP_ADJUST_AUDIO_VOLUME:
22986 				sexp_adjust_audio_volume(node);
22987 				sexp_val = SEXP_TRUE;
22988 				break;
22989 
22990 			case OP_HUD_DISABLE:
22991 				sexp_hud_disable(node);
22992 				sexp_val = SEXP_TRUE;
22993 				break;
22994 
22995 			case OP_HUD_DISABLE_EXCEPT_MESSAGES:
22996 				sexp_hud_disable_except_messages(node);
22997 				sexp_val = SEXP_TRUE;
22998 				break;
22999 
23000 			case OP_HUD_SET_TEXT:
23001 				sexp_hud_set_text(node);
23002 				sexp_val = SEXP_TRUE;
23003 				break;
23004 
23005 			case OP_HUD_SET_TEXT_NUM:
23006 				sexp_hud_set_text_num(node);
23007 				sexp_val = SEXP_TRUE;
23008 				break;
23009 
23010 			case OP_HUD_SET_COORDS:
23011 				sexp_hud_set_coords(node);
23012 				sexp_val = SEXP_TRUE;
23013 				break;
23014 
23015 			case OP_HUD_SET_FRAME:
23016 				sexp_hud_set_frame(node);
23017 				sexp_val = SEXP_TRUE;
23018 				break;
23019 
23020 			case OP_HUD_SET_COLOR:
23021 				sexp_hud_set_color(node);
23022 				sexp_val = SEXP_TRUE;
23023 				break;
23024 
23025 			case OP_HUD_SET_MAX_TARGETING_RANGE:
23026 				sexp_hud_set_max_targeting_range(node);
23027 				sexp_val = SEXP_TRUE;
23028 				break;
23029 
23030 			case OP_HUD_DISPLAY_GAUGE:
23031 				sexp_hud_display_gauge(node);
23032 				sexp_val = SEXP_TRUE;
23033 				break;
23034 
23035 			case OP_HUD_SET_MESSAGE:
23036 				sexp_hud_set_message(node);
23037 				sexp_val = SEXP_TRUE;
23038 				break;
23039 
23040 			// Goober5000
23041 			case OP_PLAYER_USE_AI:
23042 			case OP_PLAYER_NOT_USE_AI:
23043 				sexp_player_use_ai(op_num == OP_PLAYER_USE_AI);
23044 				sexp_val = SEXP_TRUE;
23045 				break;
23046 
23047 			//Karajorma
23048 			case OP_ALLOW_TREASON:
23049 				sexp_allow_treason(node);
23050 				sexp_val = SEXP_TRUE;
23051 				break;
23052 
23053 			//Karajorma
23054 			case OP_SET_PLAYER_ORDERS:
23055 				sexp_set_player_orders(node);
23056 				sexp_val = SEXP_TRUE;
23057 				break;
23058 
23059 			// Goober5000
23060 			case OP_EXPLOSION_EFFECT:
23061 				sexp_explosion_effect(node);
23062 				sexp_val = SEXP_TRUE;
23063 				break;
23064 
23065 			// Goober5000
23066 			case OP_WARP_EFFECT:
23067 				sexp_warp_effect(node);
23068 				sexp_val = SEXP_TRUE;
23069 				break;
23070 
23071 			case OP_SEND_MESSAGE:
23072 				sexp_send_message(node);
23073 				sexp_val = SEXP_TRUE;
23074 				break;
23075 
23076 			// Karajorma
23077 			case OP_ENABLE_BUILTIN_MESSAGES:
23078 			case OP_DISABLE_BUILTIN_MESSAGES:
23079 				sexp_toggle_builtin_messages (node, op_num == OP_ENABLE_BUILTIN_MESSAGES);
23080 				sexp_val = SEXP_TRUE;
23081 				break;
23082 
23083 			case OP_SET_PERSONA:
23084 				sexp_set_persona (node);
23085 				sexp_val = SEXP_TRUE;
23086 				break;
23087 
23088 			case OP_SET_MISSION_MOOD:
23089 				sexp_set_mission_mood (node);
23090 				sexp_val = SEXP_TRUE;
23091 				break;
23092 
23093 			case OP_SEND_MESSAGE_LIST:
23094 				sexp_send_message_list(node);
23095 				sexp_val = SEXP_TRUE;
23096 				break;
23097 
23098 			case OP_SEND_RANDOM_MESSAGE:
23099 				sexp_send_random_message(node);
23100 				sexp_val = SEXP_TRUE;
23101 				break;
23102 
23103 			case OP_SELF_DESTRUCT:
23104 				sexp_self_destruct(node);
23105 				sexp_val = SEXP_TRUE;
23106 				break;
23107 
23108 			case OP_NEXT_MISSION:
23109 				sexp_next_mission(node);
23110 				sexp_val = SEXP_TRUE;
23111 				break;
23112 
23113 			case OP_END_OF_CAMPAIGN:
23114 				sexp_end_of_campaign(node);
23115 				sexp_val = SEXP_TRUE;
23116 				break;
23117 
23118 			case OP_END_CAMPAIGN:
23119 				sexp_end_campaign(node);
23120 				sexp_val = SEXP_TRUE;
23121 				break;
23122 
23123 			case OP_SABOTAGE_SUBSYSTEM:
23124 				sexp_sabotage_subsystem(node);
23125 				sexp_val = SEXP_TRUE;
23126 				break;
23127 
23128 			case OP_REPAIR_SUBSYSTEM:
23129 				sexp_repair_subsystem(node);
23130 				sexp_val = SEXP_TRUE;
23131 				break;
23132 
23133 			case OP_SET_SUBSYSTEM_STRNGTH:
23134 				sexp_set_subsystem_strength(node);
23135 				sexp_val = SEXP_TRUE;
23136 				break;
23137 
23138 			case OP_DESTROY_SUBSYS_INSTANTLY:
23139 				sexp_destroy_subsys_instantly(node);
23140 				sexp_val = SEXP_TRUE;
23141 				break;
23142 
23143 			case OP_INVALIDATE_GOAL:
23144 			case OP_VALIDATE_GOAL:
23145 				sexp_change_goal_validity( node, (op_num==OP_INVALIDATE_GOAL?0:1) );
23146 				sexp_val = SEXP_TRUE;
23147 				break;
23148 
23149 			case OP_TRANSFER_CARGO:
23150 				sexp_transfer_cargo(node);
23151 				sexp_val = SEXP_TRUE;
23152 				break;
23153 
23154 			case OP_EXCHANGE_CARGO:
23155 				sexp_exchange_cargo(node);
23156 				sexp_val = SEXP_TRUE;
23157 				break;
23158 
23159 
23160 			case OP_JETTISON_CARGO:
23161 				sexp_jettison_cargo(node);
23162 				sexp_val = SEXP_TRUE;
23163 				break;
23164 
23165 			case OP_SET_DOCKED:
23166 				sexp_set_docked(node);
23167 				sexp_val = SEXP_TRUE;
23168 				break;
23169 
23170 			case OP_CARGO_NO_DEPLETE:
23171 				sexp_cargo_no_deplete(node);
23172 				sexp_val = SEXP_TRUE;
23173 				break;
23174 
23175 			case OP_SET_SCANNED:	// Goober5000
23176 			case OP_SET_UNSCANNED:
23177 				sexp_set_scanned_unscanned(node, op_num == OP_SET_SCANNED);
23178 				sexp_val = SEXP_TRUE;
23179 				break;
23180 
23181 			case OP_SET_SPECIAL_WARPOUT_NAME:
23182 				sexp_special_warpout_name(node);
23183 				sexp_val = SEXP_TRUE;
23184 				break;
23185 
23186 			//-WMC
23187 			case OP_MISSION_SET_NEBULA:
23188 				sexp_mission_set_nebula(node);
23189 				sexp_val = SEXP_TRUE;
23190 				break;
23191 
23192 			case OP_MISSION_SET_SUBSPACE:
23193 				sexp_mission_set_subspace(node);
23194 				sexp_val = SEXP_TRUE;
23195 				break;
23196 
23197 			case OP_ADD_BACKGROUND_BITMAP:
23198 				sexp_add_background_bitmap(node);
23199 				sexp_val = SEXP_TRUE;
23200 				break;
23201 
23202 			case OP_REMOVE_BACKGROUND_BITMAP:
23203 				sexp_remove_background_bitmap(node);
23204 				sexp_val = SEXP_TRUE;
23205 				break;
23206 
23207 			case OP_ADD_SUN_BITMAP:
23208 				sexp_add_sun_bitmap(node);
23209 				sexp_val = SEXP_TRUE;
23210 				break;
23211 
23212 			case OP_REMOVE_SUN_BITMAP:
23213 				sexp_remove_sun_bitmap(node);
23214 				sexp_val = SEXP_TRUE;
23215 				break;
23216 
23217 			case OP_NEBULA_CHANGE_STORM:
23218 				sexp_nebula_change_storm(node);
23219 				sexp_val = SEXP_TRUE;
23220 				break;
23221 
23222 			case OP_NEBULA_TOGGLE_POOF:
23223 				sexp_nebula_toggle_poof(node);
23224 				sexp_val = SEXP_TRUE;
23225 				break;
23226 
23227 			case OP_NEBULA_CHANGE_PATTERN:
23228 				sexp_nebula_change_pattern(node);
23229 				sexp_val = SEXP_TRUE;
23230 				break;
23231 
23232 			case OP_END_MISSION:
23233 				sexp_end_mission(node);
23234 				sexp_val = SEXP_TRUE;
23235 				break;
23236 
23237 			// Goober5000
23238 			case OP_SET_DEBRIEFING_TOGGLED:
23239 				sexp_set_debriefing_toggled(node);
23240 				sexp_val = SEXP_TRUE;
23241 				break;
23242 
23243 			// Goober5000
23244 			case OP_FORCE_JUMP:
23245 				sexp_force_jump();
23246 				sexp_val = SEXP_TRUE;
23247 				break;
23248 
23249 				// sexpressions for setting flag for good/bad time for someone to reasm
23250 			case OP_GOOD_REARM_TIME:
23251 				sexp_good_time_to_rearm(node);
23252 				sexp_val = SEXP_TRUE;
23253 				break;
23254 
23255 			case OP_GRANT_PROMOTION:
23256 				sexp_grant_promotion();
23257 				sexp_val = SEXP_TRUE;
23258 				break;
23259 
23260 			case OP_GRANT_MEDAL:
23261 				sexp_grant_medal(node);
23262 				sexp_val = SEXP_TRUE;
23263 				break;
23264 
23265 			case OP_SHIP_VAPORIZE:
23266 			case OP_SHIP_NO_VAPORIZE:
23267 				sexp_ships_vaporize( node, (op_num == OP_SHIP_VAPORIZE) );
23268 				sexp_val = SEXP_TRUE;
23269 				break;
23270 
23271 			case OP_SET_EXPLOSION_OPTION:
23272 				sexp_set_explosion_option(node);
23273 				sexp_val = SEXP_TRUE;
23274 				break;
23275 
23276 			case OP_DONT_COLLIDE_INVISIBLE:
23277 			case OP_COLLIDE_INVISIBLE:
23278 				sexp_dont_collide_invisible( node, (op_num == OP_DONT_COLLIDE_INVISIBLE) );
23279 				sexp_val = SEXP_TRUE;
23280 				break;
23281 
23282 			case OP_SET_MOBILE:
23283 			case OP_SET_IMMOBILE:
23284 				sexp_set_immobile(node, (op_num == OP_SET_IMMOBILE));
23285 				sexp_val = SEXP_TRUE;
23286 				break;
23287 
23288 			case OP_IGNORE_KEY:
23289 				sexp_ignore_key(node);
23290 				sexp_val = SEXP_TRUE;
23291 				break;
23292 
23293 			// Goober5000 - sigh, was this messed up all along?
23294 			case OP_WARP_BROKEN:
23295 			case OP_WARP_NOT_BROKEN:
23296 			case OP_WARP_NEVER:
23297 			case OP_WARP_ALLOWED:
23298 				sexp_deal_with_warp( node, (op_num==OP_WARP_BROKEN) || (op_num==OP_WARP_NOT_BROKEN),
23299 					(op_num==OP_WARP_BROKEN) || (op_num==OP_WARP_NEVER) );
23300 				sexp_val = SEXP_TRUE;
23301 				break;
23302 
23303 			// Goober5000
23304 			case OP_SET_SUBSPACE_DRIVE:
23305 				sexp_set_subspace_drive(node);
23306 				break;
23307 
23308 			case OP_GOOD_SECONDARY_TIME:
23309 				sexp_good_secondary_time(node);
23310 				sexp_val = SEXP_TRUE;
23311 				break;
23312 
23313 			// sexpressions to allow shpis/weapons during the course of a mission
23314 			case OP_ALLOW_SHIP:
23315 				sexp_allow_ship(node);
23316 				sexp_val = SEXP_TRUE;
23317 				break;
23318 
23319 			case OP_ALLOW_WEAPON:
23320 				sexp_allow_weapon(node);
23321 				sexp_val = SEXP_TRUE;
23322 				break;
23323 
23324 			case OP_TECH_ADD_SHIP:
23325 				sexp_tech_add_ship(node);
23326 				sexp_val = SEXP_TRUE;
23327 				break;
23328 
23329 			case OP_TECH_ADD_WEAPON:
23330 				sexp_tech_add_weapon(node);
23331 				sexp_val = SEXP_TRUE;
23332 				break;
23333 
23334 			case OP_TECH_ADD_INTEL:
23335 				sexp_tech_add_intel(node);
23336 				sexp_val = SEXP_TRUE;
23337 				break;
23338 
23339 			case OP_TECH_ADD_INTEL_XSTR:
23340 				sexp_tech_add_intel_xstr(node);
23341 				sexp_val = SEXP_TRUE;
23342 				break;
23343 
23344 			case OP_TECH_RESET_TO_DEFAULT:
23345 				sexp_tech_reset_to_default();
23346 				sexp_val = SEXP_TRUE;
23347 				break;
23348 
23349 			case OP_CHANGE_PLAYER_SCORE:
23350 				sexp_change_player_score(node);
23351 				sexp_val = SEXP_TRUE;
23352 				break;
23353 
23354 			case OP_CHANGE_TEAM_SCORE:
23355 				sexp_change_team_score(node);
23356 				sexp_val = SEXP_TRUE;
23357 				break;
23358 
23359 				// in the case of a red_alert mission, simply call the red alert function to close
23360 				// the current campaign's mission and move forward to the next mission
23361 			case OP_RED_ALERT:
23362 				red_alert_start_mission();
23363 				sexp_val = SEXP_TRUE;
23364 				break;
23365 
23366 			case OP_SET_OBJECT_SPEED_X:
23367 			case OP_SET_OBJECT_SPEED_Y:
23368 			case OP_SET_OBJECT_SPEED_Z:
23369 				sexp_set_object_speed(node, op_num - OP_SET_OBJECT_SPEED_X);
23370 				sexp_val = SEXP_TRUE;
23371 				break;
23372 
23373 			case OP_GET_OBJECT_SPEED_X:
23374 			case OP_GET_OBJECT_SPEED_Y:
23375 			case OP_GET_OBJECT_SPEED_Z:
23376 				sexp_val = sexp_get_object_speed(node, op_num - OP_GET_OBJECT_SPEED_X);
23377 				break;
23378 
23379 			case OP_GET_OBJECT_X:
23380 			case OP_GET_OBJECT_Y:
23381 			case OP_GET_OBJECT_Z:
23382 				sexp_val = sexp_get_object_coordinate(node, op_num - OP_GET_OBJECT_X);
23383 				break;
23384 
23385 			case OP_GET_OBJECT_PITCH:
23386 			case OP_GET_OBJECT_BANK:
23387 			case OP_GET_OBJECT_HEADING:
23388 				sexp_val = sexp_get_object_angle(node, op_num - OP_GET_OBJECT_PITCH);
23389 				break;
23390 
23391 			case OP_SET_OBJECT_POSITION:
23392 				sexp_set_object_position(node);
23393 				sexp_val = SEXP_TRUE;
23394 				break;
23395 
23396 			case OP_SET_OBJECT_ORIENTATION:
23397 				sexp_set_object_orientation(node);
23398 				sexp_val = SEXP_TRUE;
23399 				break;
23400 
23401 			case OP_SET_OBJECT_FACING:
23402 			case OP_SET_OBJECT_FACING_OBJECT:
23403 				sexp_set_object_facing(node, op_num == OP_SET_OBJECT_FACING_OBJECT);
23404 				sexp_val = SEXP_TRUE;
23405 				break;
23406 
23407 			case OP_SHIP_MANEUVER:
23408 			case OP_SHIP_ROT_MANEUVER:
23409 			case OP_SHIP_LAT_MANEUVER:
23410 				sexp_set_ship_maneuver(node, op_num);
23411 				sexp_val = SEXP_TRUE;
23412 				break;
23413 
23414 			// training operators
23415 			case OP_KEY_PRESSED:
23416 				sexp_val = sexp_key_pressed(node);
23417 				break;
23418 
23419 			case OP_SPECIAL_CHECK:
23420 				sexp_val = sexp_special_training_check(node);
23421 				break;
23422 
23423 			case OP_KEY_RESET:
23424 				sexp_key_reset(node);
23425 				sexp_val = SEXP_KNOWN_TRUE;  // only do it first time in repeating events.
23426 				break;
23427 
23428 			case OP_KEY_RESET_MULTIPLE:
23429 				sexp_key_reset(node);
23430 				sexp_val = SEXP_TRUE;
23431 				break;
23432 
23433 			case OP_MISSILE_LOCKED:
23434 				sexp_val = sexp_missile_locked(node);
23435 				break;
23436 
23437 			case OP_TARGETED:
23438 				sexp_val = sexp_targeted(node);
23439 				break;
23440 
23441 			case OP_NODE_TARGETED:
23442 				sexp_val = sexp_node_targeted(node);
23443 				break;
23444 
23445 			case OP_SPEED:
23446 				sexp_val = sexp_speed(node);
23447 				break;
23448 
23449 			case OP_GET_THROTTLE_SPEED:
23450 				sexp_val = sexp_get_throttle_speed(node);
23451 				break;
23452 
23453 			case OP_SET_PLAYER_THROTTLE_SPEED:
23454 				sexp_set_player_throttle_speed(node);
23455 				sexp_val = SEXP_TRUE;
23456 				break;
23457 
23458 			case OP_PRIMARIES_DEPLETED:
23459 				sexp_val = sexp_primaries_depleted(node);
23460 				break;
23461 
23462 			case OP_SECONDARIES_DEPLETED:
23463 				sexp_val = sexp_secondaries_depleted(node);
23464 				break;
23465 
23466 			case OP_FACING:
23467 				sexp_val = sexp_facing(node);
23468 				break;
23469 
23470 			case OP_IS_FACING:
23471 				sexp_val = sexp_is_facing(node);
23472 				break;
23473 
23474 			case OP_FACING2:
23475 				sexp_val = sexp_facing2(node);
23476 				break;
23477 
23478 			case OP_ORDER:
23479 				sexp_val = sexp_order(node);
23480 				break;
23481 
23482 			case OP_QUERY_ORDERS:
23483 				sexp_val = sexp_query_orders(node);
23484 				break;
23485 
23486 			// Karajorma
23487 			case OP_RESET_ORDERS:
23488 				sexp_reset_orders(node);
23489 				sexp_val = SEXP_TRUE;
23490 				break;
23491 
23492 			case OP_WAYPOINT_MISSED:
23493 				sexp_val = sexp_waypoint_missed();
23494 				break;
23495 
23496 			case OP_WAYPOINT_TWICE:
23497 				sexp_val = sexp_waypoint_twice();
23498 				break;
23499 
23500 			case OP_PATH_FLOWN:
23501 				sexp_val = sexp_path_flown();
23502 				break;
23503 
23504 			case OP_TRAINING_MSG:
23505 				sexp_send_training_message(node);
23506 				sexp_val = SEXP_TRUE;
23507 				break;
23508 
23509 			case OP_FLASH_HUD_GAUGE:
23510 				sexp_flash_hud_gauge(node);
23511 				sexp_val = SEXP_TRUE;
23512 				break;
23513 
23514 			case OP_SET_TRAINING_CONTEXT_FLY_PATH:
23515 				sexp_set_training_context_fly_path(node);
23516 				sexp_val = SEXP_TRUE;
23517 				break;
23518 
23519 			case OP_SET_TRAINING_CONTEXT_SPEED:
23520 				sexp_set_training_context_speed(node);
23521 				sexp_val = SEXP_TRUE;
23522 				break;
23523 
23524 			// Karajorma
23525 			case OP_STRING_TO_INT:
23526 				sexp_val = sexp_string_to_int(node);
23527 				break;
23528 
23529 			// Goober5000
23530 			case OP_INT_TO_STRING:
23531 				sexp_int_to_string(node);
23532 				sexp_val = SEXP_TRUE;
23533 				break;
23534 
23535 			// Goober5000
23536 			case OP_STRING_CONCATENATE:
23537 				sexp_string_concatenate(node);
23538 				sexp_val = SEXP_TRUE;
23539 				break;
23540 
23541 			// Goober5000
23542 			case OP_STRING_GET_SUBSTRING:
23543 				sexp_string_get_substring(node);
23544 				sexp_val = SEXP_TRUE;
23545 				break;
23546 
23547 			// Goober5000
23548 			case OP_STRING_SET_SUBSTRING:
23549 				sexp_string_set_substring(node);
23550 				sexp_val = SEXP_TRUE;
23551 				break;
23552 
23553 			// Goober5000
23554 			case OP_STRING_GET_LENGTH:
23555 				sexp_val = sexp_string_get_length(node);
23556 				break;
23557 
23558 			// Karajorma
23559 			case OP_DEBUG:
23560 				sexp_debug(node);
23561 				sexp_val = SEXP_TRUE;
23562 				break;
23563 
23564 			case 0: // zero represents a non-operator
23565 				return eval_num(cur_node);
23566 
23567 			case OP_NOP:
23568 				sexp_val = SEXP_TRUE;
23569 				break;
23570 
23571 			case OP_BEAM_FIRE:
23572 			case OP_BEAM_FIRE_COORDS:
23573 				sexp_beam_fire(node, op_num == OP_BEAM_FIRE_COORDS);
23574 				sexp_val = SEXP_TRUE;
23575 				break;
23576 
23577 			case OP_IS_TAGGED:
23578 				sexp_val = sexp_is_tagged(node);
23579 				break;
23580 
23581 			case OP_IS_PLAYER:
23582 				sexp_val = sexp_is_player(node);
23583 				break;
23584 
23585 			case OP_NUM_KILLS:
23586 			case OP_NUM_ASSISTS:
23587 			case OP_SHIP_SCORE:
23588 			case OP_SHIP_DEATHS:
23589 			case OP_RESPAWNS_LEFT:
23590 				sexp_val = sexp_return_player_data(node, op_num);
23591 				break;
23592 
23593 			case OP_SET_RESPAWNS:
23594 				sexp_set_respawns(node);
23595 				sexp_val = SEXP_TRUE;
23596 				break;
23597 
23598 			case OP_REMOVE_WEAPONS:
23599 				sexp_remove_weapons(node);
23600 				sexp_val = SEXP_TRUE;
23601 				break;
23602 
23603 			case OP_NUM_TYPE_KILLS:
23604 				sexp_val = sexp_num_type_kills(node);
23605 				break;
23606 
23607 			case OP_NUM_CLASS_KILLS:
23608 				sexp_val = sexp_num_class_kills(node);
23609 				break;
23610 
23611 			case OP_BEAM_FREE:
23612 				sexp_val = SEXP_TRUE;
23613 				sexp_beam_free(node);
23614 				break;
23615 
23616 			case OP_BEAM_FREE_ALL:
23617 				sexp_val = SEXP_TRUE;
23618 				sexp_beam_free_all(node);
23619 				break;
23620 
23621 			case OP_BEAM_LOCK:
23622 				sexp_val = SEXP_TRUE;
23623 				sexp_beam_lock(node);
23624 				break;
23625 
23626 			case OP_BEAM_LOCK_ALL:
23627 				sexp_val = SEXP_TRUE;
23628 				sexp_beam_lock_all(node);
23629 				break;
23630 
23631 			case OP_TURRET_FREE:
23632 				sexp_val = SEXP_TRUE;
23633 				sexp_turret_free(node);
23634 				break;
23635 
23636 			case OP_TURRET_FREE_ALL:
23637 				sexp_val = SEXP_TRUE;
23638 				sexp_turret_free_all(node);
23639 				break;
23640 
23641 			case OP_TURRET_LOCK:
23642 				sexp_val = SEXP_TRUE;
23643 				sexp_turret_lock(node);
23644 				break;
23645 
23646 			case OP_TURRET_LOCK_ALL:
23647 				sexp_val = SEXP_TRUE;
23648 				sexp_turret_lock_all(node);
23649 				break;
23650 
23651 			case OP_TURRET_CHANGE_WEAPON:
23652 				sexp_val = SEXP_TRUE;
23653 				sexp_turret_change_weapon(node);
23654 				break;
23655 
23656 			case OP_TURRET_SET_DIRECTION_PREFERENCE:
23657 				sexp_val = SEXP_TRUE;
23658 				sexp_turret_set_direction_preference(node);
23659 				break;
23660 
23661 			case OP_TURRET_SET_RATE_OF_FIRE:
23662 				sexp_val = SEXP_TRUE;
23663 				sexp_turret_set_rate_of_fire(node);
23664 				break;
23665 
23666 			case OP_TURRET_SET_OPTIMUM_RANGE:
23667 				sexp_val = SEXP_TRUE;
23668 				sexp_turret_set_optimum_range(node);
23669 				break;
23670 
23671 			case OP_TURRET_SET_TARGET_PRIORITIES:
23672 				sexp_val = SEXP_TRUE;
23673 				sexp_turret_set_target_priorities(node);
23674 				break;
23675 
23676 			case OP_TURRET_SET_TARGET_ORDER:
23677 				sexp_val = SEXP_TRUE;
23678 				sexp_turret_set_target_order(node);
23679 				break;
23680 
23681 			case OP_SET_ARMOR_TYPE:
23682 				sexp_val = SEXP_TRUE;
23683 				sexp_set_armor_type(node);
23684 				break;
23685 
23686 			case OP_WEAPON_SET_DAMAGE_TYPE:
23687 				sexp_val = SEXP_TRUE;
23688 				sexp_weapon_set_damage_type(node);
23689 				break;
23690 
23691 			case OP_SHIP_SET_DAMAGE_TYPE:
23692 				sexp_val = SEXP_TRUE;
23693 				sexp_ship_set_damage_type(node);
23694 				break;
23695 
23696 			case OP_SHIP_SHOCKWAVE_SET_DAMAGE_TYPE:
23697 				sexp_val = SEXP_TRUE;
23698 				sexp_ship_shockwave_set_damage_type(node);
23699 				break;
23700 
23701 			case OP_FIELD_SET_DAMAGE_TYPE:
23702 				sexp_val = SEXP_TRUE;
23703 				sexp_field_set_damage_type(node);
23704 				break;
23705 
23706 			case OP_SHIP_TURRET_TARGET_ORDER:
23707 				sexp_val = SEXP_TRUE;
23708 				sexp_ship_turret_target_order(node);
23709 				break;
23710 
23711 			case OP_ADD_REMOVE_ESCORT:
23712 				sexp_val = SEXP_TRUE;
23713 				sexp_add_remove_escort(node);
23714 				break;
23715 
23716 			case OP_DAMAGED_ESCORT_LIST:
23717 				sexp_val = SEXP_TRUE;
23718 				sexp_damage_escort_list(node);
23719 				break;
23720 
23721 			case OP_DAMAGED_ESCORT_LIST_ALL:
23722 				sexp_val = SEXP_TRUE;
23723 				sexp_damage_escort_list_all(node);
23724 				break;
23725 
23726 			case OP_AWACS_SET_RADIUS:
23727 				sexp_val = SEXP_TRUE;
23728 				sexp_awacs_set_radius(node);
23729 				break;
23730 
23731 			case OP_PRIMITIVE_SENSORS_SET_RANGE:
23732 				sexp_primitive_sensors_set_range(node);
23733 				sexp_val = SEXP_TRUE;
23734 				break;
23735 
23736 			case OP_CAP_WAYPOINT_SPEED:
23737 				sexp_val = SEXP_TRUE;
23738 				sexp_cap_waypoint_speed(node);
23739 				break;
23740 
23741 			case OP_TURRET_TAGGED_ONLY_ALL:
23742 				sexp_val = SEXP_TRUE;
23743 				sexp_turret_tagged_only_all(node);
23744 				break;
23745 
23746 			case OP_TURRET_TAGGED_CLEAR_ALL:
23747 				sexp_val = SEXP_TRUE;
23748 				sexp_turret_tagged_clear_all(node);
23749 				break;
23750 
23751 			case OP_SUBSYS_SET_RANDOM:
23752 				sexp_val = SEXP_TRUE;
23753 				sexp_subsys_set_random(node);
23754 				break;
23755 
23756 			case OP_SUPERNOVA_START:
23757 				sexp_val = SEXP_TRUE;
23758 				sexp_supernova_start(node);
23759 				break;
23760 
23761 			case OP_SUPERNOVA_STOP:
23762 				sexp_val = SEXP_TRUE;
23763 				sexp_supernova_stop(node);
23764 				break;
23765 
23766 			case OP_SET_MOTION_DEBRIS:
23767 				sexp_val = SEXP_TRUE;
23768 				sexp_set_motion_debris(node);
23769 				break;
23770 
23771 			case OP_SHIELD_RECHARGE_PCT:
23772 				sexp_val = sexp_shield_recharge_pct(node);
23773 				break;
23774 
23775 			case OP_ENGINE_RECHARGE_PCT:
23776 				sexp_val = sexp_engine_recharge_pct(node);
23777 				break;
23778 
23779 			case OP_WEAPON_RECHARGE_PCT:
23780 				sexp_val = sexp_weapon_recharge_pct(node);
23781 				break;
23782 
23783 			case OP_GET_ETS_VALUE:
23784 				sexp_val = sexp_get_ets_value(node);
23785 				break;
23786 
23787 			case OP_SET_ETS_VALUES:
23788 				sexp_val = SEXP_TRUE;
23789 				sexp_set_ets_values(node);
23790 				break;
23791 
23792 			case OP_SHIELD_QUAD_LOW:
23793 				sexp_val = sexp_shield_quad_low(node);
23794 				break;
23795 
23796 			case OP_PRIMARY_AMMO_PCT:
23797 				sexp_val = sexp_primary_ammo_pct(node);
23798 				break;
23799 
23800 			case OP_SECONDARY_AMMO_PCT:
23801 				sexp_val = sexp_secondary_ammo_pct(node);
23802 				break;
23803 
23804 			// Karajorma
23805 			case OP_GET_PRIMARY_AMMO:
23806 				sexp_val = sexp_get_primary_ammo(node);
23807 				break;
23808 
23809 			// Karajorma
23810 			case OP_GET_SECONDARY_AMMO:
23811 				sexp_val = sexp_get_secondary_ammo(node);
23812 				break;
23813 
23814 			// Karajorma
23815 			case OP_GET_NUM_COUNTERMEASURES:
23816 				sexp_val = sexp_get_countermeasures(node);
23817 				break;
23818 
23819 			case OP_IS_SECONDARY_SELECTED:
23820 				sexp_val = sexp_is_secondary_selected(node);
23821 				break;
23822 
23823 			case OP_IS_PRIMARY_SELECTED:
23824 				sexp_val = sexp_is_primary_selected(node);
23825 				break;
23826 
23827 			// Goober5000
23828 			case OP_SET_SUPPORT_SHIP:
23829 				sexp_set_support_ship(node);
23830 				sexp_val = SEXP_TRUE;
23831 				break;
23832 
23833 			// Goober5000
23834 			case OP_SET_ARRIVAL_INFO:
23835 				sexp_set_arrival_info(node);
23836 				sexp_val = SEXP_TRUE;
23837 				break;
23838 
23839 			// Goober5000
23840 			case OP_SET_DEPARTURE_INFO:
23841 				sexp_set_departure_info(node);
23842 				sexp_val = SEXP_TRUE;
23843 				break;
23844 
23845 			// Goober5000
23846 			case OP_CHANGE_SHIP_CLASS:
23847 				sexp_change_ship_class(node);
23848 				sexp_val = SEXP_TRUE;
23849 				break;
23850 
23851 			// Goober5000
23852 			case OP_SHIP_COPY_DAMAGE:
23853 				sexp_ship_copy_damage(node);
23854 				sexp_val = SEXP_TRUE;
23855 				break;
23856 
23857 			//-Bobboau
23858 			case OP_ACTIVATE_GLOW_POINTS:
23859 			case OP_DEACTIVATE_GLOW_POINTS:
23860 				sexp_activate_deactivate_glow_points(node, op_num == OP_ACTIVATE_GLOW_POINTS);
23861 				sexp_val = SEXP_TRUE;
23862 				break;
23863 
23864 			//-Bobboau
23865 			case OP_ACTIVATE_GLOW_MAPS:
23866 			case OP_DEACTIVATE_GLOW_MAPS:
23867 				sexp_activate_deactivate_glow_maps(node, op_num == OP_ACTIVATE_GLOW_MAPS);
23868 				sexp_val = SEXP_TRUE;
23869 				break;
23870 
23871 			//-Bobboau
23872 			case OP_ACTIVATE_GLOW_POINT_BANK:
23873 			case OP_DEACTIVATE_GLOW_POINT_BANK:
23874 				sexp_activate_deactivate_glow_point_bank(node, op_num == OP_ACTIVATE_GLOW_POINT_BANK);
23875 				sexp_val = SEXP_TRUE;
23876 				break;
23877 
23878 			// taylor
23879 			case OP_SET_SKYBOX_MODEL:
23880 				sexp_set_skybox_model(node);
23881 				sexp_val = SEXP_TRUE;
23882 				break;
23883 
23884 			case OP_SET_SKYBOX_ORIENT:
23885 				sexp_set_skybox_orientation(node);
23886 				sexp_val = SEXP_TRUE;
23887 				break;
23888 
23889 			case OP_TURRET_TAGGED_SPECIFIC:
23890 				sexp_turret_tagged_specific(node);
23891 				sexp_val = SEXP_TRUE;
23892 				break;
23893 
23894 			case OP_TURRET_TAGGED_CLEAR_SPECIFIC:
23895 				sexp_turret_tagged_clear_specific(node);
23896 				sexp_val = SEXP_TRUE;
23897 				break;
23898 
23899 			case OP_LOCK_ROTATING_SUBSYSTEM:
23900 			case OP_FREE_ROTATING_SUBSYSTEM:
23901 				sexp_set_subsys_rotation_lock_free(node, op_num == OP_LOCK_ROTATING_SUBSYSTEM);
23902 				sexp_val = SEXP_TRUE;
23903 				break;
23904 
23905 			case OP_REVERSE_ROTATING_SUBSYSTEM:
23906 				sexp_reverse_rotating_subsystem(node);
23907 				sexp_val = SEXP_TRUE;
23908 				break;
23909 
23910 			case OP_ROTATING_SUBSYS_SET_TURN_TIME:
23911 				sexp_rotating_subsys_set_turn_time(node);
23912 				sexp_val = SEXP_TRUE;
23913 				break;
23914 
23915 			case OP_TRIGGER_SUBMODEL_ANIMATION:
23916 				sexp_trigger_submodel_animation(node);
23917 				sexp_val = SEXP_TRUE;
23918 				break;
23919 
23920 			// Karajorma
23921 			case OP_SET_PRIMARY_AMMO:
23922 				sexp_set_primary_ammo(node);
23923 				sexp_val = SEXP_TRUE;
23924 				break;
23925 
23926 			// Karajorma
23927 			case OP_SET_SECONDARY_AMMO:
23928 				sexp_set_secondary_ammo(node);
23929 				sexp_val = SEXP_TRUE;
23930 				break;
23931 
23932 			// Karajorma
23933 			case OP_SET_PRIMARY_WEAPON:
23934 			case OP_SET_SECONDARY_WEAPON:
23935 				sexp_set_weapon(node, op_num == OP_SET_PRIMARY_WEAPON);
23936 				sexp_val = SEXP_TRUE;
23937 				break;
23938 
23939 			// Karajorma
23940 			case OP_SET_NUM_COUNTERMEASURES:
23941 				sexp_set_countermeasures(node);
23942 				sexp_val = SEXP_TRUE;
23943 				break;
23944 
23945 			// Karajorma
23946 			case OP_LOCK_PRIMARY_WEAPON:
23947 			case OP_UNLOCK_PRIMARY_WEAPON:
23948 				sexp_deal_with_primary_lock(node, op_num == OP_LOCK_PRIMARY_WEAPON);
23949 				sexp_val = SEXP_TRUE;
23950 				break;
23951 
23952 			case OP_LOCK_SECONDARY_WEAPON:
23953 			case OP_UNLOCK_SECONDARY_WEAPON:
23954 				sexp_deal_with_secondary_lock(node, op_num == OP_LOCK_SECONDARY_WEAPON);
23955 				sexp_val = SEXP_TRUE;
23956 				break;
23957 
23958 			// KeldorKatarn
23959 			case OP_LOCK_AFTERBURNER:
23960 			case OP_UNLOCK_AFTERBURNER:
23961 				sexp_deal_with_afterburner_lock(node, op_num == OP_LOCK_AFTERBURNER);
23962 				sexp_val = SEXP_TRUE;
23963 				break;
23964 
23965 			case OP_SET_AFTERBURNER_ENERGY:
23966 			case OP_SET_WEAPON_ENERGY:
23967 			case OP_SET_SHIELD_ENERGY:
23968 				sexp_set_energy_pct(node, op_num);
23969 				sexp_val = SEXP_TRUE;
23970 				break;
23971 
23972 			case OP_SET_AMBIENT_LIGHT:
23973 				sexp_set_ambient_light(node);
23974 				sexp_val = SEXP_TRUE;
23975 				break;
23976 
23977 			case OP_SET_POST_EFFECT:
23978 				sexp_set_post_effect(node);
23979 				sexp_val = SEXP_TRUE;
23980 				break;
23981 
23982 			case OP_PRIMARY_FIRED_SINCE:
23983 			case OP_SECONDARY_FIRED_SINCE:
23984 				sexp_val = sexp_weapon_fired_delay(node, op_num);
23985 				break;
23986 
23987 			case OP_HAS_PRIMARY_WEAPON:
23988 			case OP_HAS_SECONDARY_WEAPON:
23989 				sexp_val = sexp_has_weapon(node, op_num);
23990 				break;
23991 
23992 			case OP_DIRECTIVE_VALUE:
23993 				sexp_val = sexp_directive_value(node);
23994 				break;
23995 
23996 			case OP_CHANGE_SUBSYSTEM_NAME:
23997 				sexp_change_subsystem_name(node);
23998 				sexp_val = SEXP_TRUE;
23999 				break;
24000 
24001 			case OP_NUM_SHIPS_IN_BATTLE:	// phreak
24002 				sexp_val = sexp_num_ships_in_battle(node);
24003 				break;
24004 
24005 			// Karajorma
24006 			case OP_NUM_SHIPS_IN_WING:
24007 				sexp_val=sexp_num_ships_in_wing(node);
24008 				break;
24009 
24010 			case OP_CURRENT_SPEED:
24011 				sexp_val = sexp_current_speed(node);
24012 				break;
24013 
24014 			case OP_NAV_IS_VISITED: //kazan
24015 				sexp_val = is_nav_visited(node);
24016 				break;
24017 
24018 			case OP_NAV_DISTANCE: //kazan
24019 				sexp_val = distance_to_nav(node);
24020 				break;
24021 
24022 			case OP_NAV_ADD_WAYPOINT: //kazan
24023 				sexp_val = SEXP_TRUE;
24024 				add_nav_waypoint(node);
24025 				break;
24026 
24027 			case OP_NAV_ADD_SHIP: //kazan
24028 				sexp_val = SEXP_TRUE;
24029 				add_nav_ship(node);
24030 				break;
24031 
24032 			case OP_NAV_DEL: //kazan
24033 				sexp_val = SEXP_TRUE;
24034 				del_nav(node);
24035 				break;
24036 
24037 			case OP_NAV_HIDE: //kazan
24038 				sexp_val = SEXP_TRUE;
24039 				hide_nav(node);
24040 				break;
24041 
24042 			case OP_NAV_RESTRICT: //kazan
24043 				sexp_val = SEXP_TRUE;
24044 				restrict_nav(node);
24045 				break;
24046 
24047 			case OP_NAV_UNHIDE: //kazan
24048 				sexp_val = SEXP_TRUE;
24049 				unhide_nav(node);
24050 				break;
24051 
24052 			case OP_NAV_UNRESTRICT: //kazan
24053 				sexp_val = SEXP_TRUE;
24054 				unrestrict_nav(node);
24055 				break;
24056 
24057 			case OP_NAV_SET_VISITED: //kazan
24058 				sexp_val = SEXP_TRUE;
24059 				set_nav_visited(node);
24060 				break;
24061 
24062 			case OP_NAV_UNSET_VISITED: //kazan
24063 				sexp_val = SEXP_TRUE;
24064 				unset_nav_visited(node);
24065 				break;
24066 
24067 			case OP_NAV_SET_CARRY: //kazan
24068 				sexp_val = SEXP_TRUE;
24069 				set_nav_carry_status(node);
24070 				break;
24071 
24072 			case OP_NAV_UNSET_CARRY: //kazan
24073 				sexp_val = SEXP_TRUE;
24074 				unset_nav_carry_status(node);
24075 				break;
24076 
24077 			case OP_NAV_SET_NEEDSLINK:
24078 				sexp_val = SEXP_TRUE;
24079 				set_nav_needslink(node);
24080 				break;
24081 
24082 			case OP_NAV_UNSET_NEEDSLINK:
24083 				sexp_val = SEXP_TRUE;
24084 				unset_nav_needslink(node);
24085 				break;
24086 
24087 			case OP_NAV_ISLINKED:
24088 				sexp_val = is_nav_linked(node);
24089 				break;
24090 
24091 			case OP_NAV_USEAP:
24092 				sexp_val = SEXP_TRUE;
24093 				set_use_ap(node);
24094 				break;
24095 
24096 			case OP_NAV_USECINEMATICS:
24097 				sexp_val = SEXP_TRUE;
24098 				set_use_ap_cinematics(node);
24099 				break;
24100 
24101 			//Talon1024
24102 			case OP_NAV_SELECT:
24103 				sexp_val = SEXP_TRUE;
24104 				select_nav(node);
24105 				break;
24106 
24107 			//Talon1024
24108 			case OP_NAV_UNSELECT:
24109 				sexp_val = SEXP_TRUE;
24110 				unselect_nav();
24111 				break;
24112 
24113 			case OP_SCRAMBLE_MESSAGES:
24114 			case OP_UNSCRAMBLE_MESSAGES:
24115 				sexp_scramble_messages(node, op_num == OP_SCRAMBLE_MESSAGES );
24116 				sexp_val = SEXP_TRUE;
24117 				break;
24118 
24119 			case OP_CUTSCENES_SET_CUTSCENE_BARS:
24120 			case OP_CUTSCENES_UNSET_CUTSCENE_BARS:
24121 				sexp_val = SEXP_TRUE;
24122 				sexp_toggle_cutscene_bars(node, op_num == OP_CUTSCENES_SET_CUTSCENE_BARS);
24123 				break;
24124 
24125 			case OP_CUTSCENES_FADE_IN:
24126 			case OP_CUTSCENES_FADE_OUT:
24127 				sexp_val = SEXP_TRUE;
24128 				sexp_fade(node, op_num == OP_CUTSCENES_FADE_IN);
24129 				break;
24130 
24131 			case OP_CUTSCENES_SET_CAMERA:
24132 				sexp_val = SEXP_TRUE;
24133 				sexp_set_camera(node);
24134 				break;
24135 			case OP_CUTSCENES_SET_CAMERA_FACING:
24136 				sexp_val = SEXP_TRUE;
24137 				sexp_set_camera_facing(node);
24138 				break;
24139 			case OP_CUTSCENES_SET_CAMERA_FACING_OBJECT:
24140 				sexp_val = SEXP_TRUE;
24141 				sexp_set_camera_facing_object(node);
24142 				break;
24143 			case OP_CUTSCENES_SET_CAMERA_FOV:
24144 				sexp_val = SEXP_TRUE;
24145 				sexp_set_camera_fov(node);
24146 				break;
24147 			case OP_CUTSCENES_SET_CAMERA_HOST:
24148 				sexp_val = SEXP_TRUE;
24149 				sexp_set_camera_host(node);
24150 				break;
24151 			case OP_CUTSCENES_SET_CAMERA_POSITION:
24152 				sexp_val = SEXP_TRUE;
24153 				sexp_set_camera_position(node);
24154 				break;
24155 			case OP_CUTSCENES_SET_CAMERA_ROTATION:
24156 				sexp_val = SEXP_TRUE;
24157 				sexp_set_camera_rotation(node);
24158 				break;
24159 			case OP_CUTSCENES_SET_CAMERA_TARGET:
24160 				sexp_val = SEXP_TRUE;
24161 				sexp_set_camera_target(node);
24162 				break;
24163 			case OP_CUTSCENES_SET_FOV:
24164 				sexp_val = SEXP_TRUE;
24165 				sexp_set_fov(node);
24166 				break;
24167 			case OP_CUTSCENES_GET_FOV:
24168 				sexp_val = sexp_get_fov();
24169 				break;
24170 			case OP_CUTSCENES_RESET_FOV:
24171 				sexp_val = SEXP_TRUE;
24172 				sexp_reset_fov();
24173 				break;
24174 			case OP_CUTSCENES_RESET_CAMERA:
24175 				sexp_val = SEXP_TRUE;
24176 				sexp_reset_camera(node);
24177 				break;
24178 			case OP_CUTSCENES_SHOW_SUBTITLE:
24179 				sexp_val = SEXP_TRUE;
24180 				sexp_show_subtitle(node);
24181 				break;
24182 			case OP_CUTSCENES_SHOW_SUBTITLE_TEXT:
24183 				sexp_val = SEXP_TRUE;
24184 				sexp_show_subtitle_text(node);
24185 				break;
24186 			case OP_CUTSCENES_SHOW_SUBTITLE_IMAGE:
24187 				sexp_val = SEXP_TRUE;
24188 				sexp_show_subtitle_image(node);
24189 				break;
24190 			case OP_CUTSCENES_SET_TIME_COMPRESSION:
24191 				sexp_val = SEXP_TRUE;
24192 				sexp_set_time_compression(node);
24193 				break;
24194 			case OP_CUTSCENES_RESET_TIME_COMPRESSION:
24195 				sexp_val = SEXP_TRUE;
24196 				sexp_reset_time_compression();
24197 				break;
24198 			case OP_CUTSCENES_FORCE_PERSPECTIVE:
24199 				sexp_val = SEXP_TRUE;
24200 				sexp_force_perspective(node);
24201 				break;
24202 
24203 			case OP_SET_CAMERA_SHUDDER:
24204 				sexp_val = SEXP_TRUE;
24205 				sexp_set_camera_shudder(node);
24206 				break;
24207 
24208 			case OP_JUMP_NODE_SET_JUMPNODE_NAME: //CommanderDJ
24209 				sexp_val = SEXP_TRUE;
24210 				sexp_set_jumpnode_name(node);
24211 				break;
24212 
24213 			case OP_JUMP_NODE_SET_JUMPNODE_COLOR:
24214 				sexp_val = SEXP_TRUE;
24215 				sexp_set_jumpnode_color(node);
24216 				break;
24217 			case OP_JUMP_NODE_SET_JUMPNODE_MODEL:
24218 				sexp_val = SEXP_TRUE;
24219 				sexp_set_jumpnode_model(node);
24220 				break;
24221 			case OP_JUMP_NODE_SHOW_JUMPNODE:
24222 			case OP_JUMP_NODE_HIDE_JUMPNODE:
24223 				sexp_show_hide_jumpnode(node, op_num == OP_JUMP_NODE_SHOW_JUMPNODE);
24224 				sexp_val = SEXP_TRUE;
24225 				break;
24226 
24227 			case OP_SCRIPT_EVAL_NUM:
24228 				sexp_val = sexp_script_eval(node, OPR_NUMBER);
24229 				break;
24230 
24231 			case OP_SCRIPT_EVAL_STRING:
24232 				sexp_val = sexp_script_eval(node, OPR_STRING);
24233 				break;
24234 
24235 			case OP_SCRIPT_EVAL:
24236 				sexp_val = sexp_script_eval(node, OPR_NULL);
24237 				break;
24238 
24239 			case OP_SCRIPT_EVAL_MULTI:
24240 				sexp_script_eval_multi(node);
24241 				sexp_val = SEXP_TRUE;
24242 				break;
24243 
24244 			case OP_CHANGE_IFF_COLOR:
24245 				sexp_change_iff_color(node);
24246 				sexp_val = SEXP_TRUE;
24247 				break;
24248 
24249 			case OP_DISABLE_ETS:
24250 			case OP_ENABLE_ETS:
24251 				sexp_disable_ets(node, (op_num == OP_DISABLE_ETS));
24252 				sexp_val = SEXP_TRUE;
24253 				break;
24254 
24255 			case OP_FORCE_GLIDE:
24256 				sexp_val = SEXP_TRUE;
24257 				sexp_force_glide(node);
24258 				break;
24259 
24260 			case OP_HUD_SET_DIRECTIVE:
24261 				sexp_val = SEXP_TRUE;
24262 				sexp_hud_set_directive(node);
24263 				break;
24264 
24265 			case OP_HUD_GAUGE_SET_ACTIVE:
24266 				sexp_val = SEXP_TRUE;
24267 				sexp_hud_gauge_set_active(node);
24268 				break;
24269 
24270 			case OP_HUD_SET_CUSTOM_GAUGE_ACTIVE:
24271 				sexp_val = SEXP_TRUE;
24272 				sexp_hud_set_custom_gauge_active(node);
24273 				break;
24274 
24275 			case OP_HUD_CLEAR_MESSAGES:
24276 				sexp_val = SEXP_TRUE;
24277 				sexp_hud_clear_messages();
24278 				break;
24279 
24280 			case OP_HUD_ACTIVATE_GAUGE_TYPE:
24281 				sexp_val = SEXP_TRUE;
24282 				sexp_hud_activate_gauge_type(node);
24283 				break;
24284 
24285 			case OP_HUD_SET_RETAIL_GAUGE_ACTIVE:
24286 				sexp_val = SEXP_TRUE;
24287 				sexp_hud_set_retail_gauge_active(node);
24288 				break;
24289 
24290 			case OP_ADD_TO_COLGROUP:
24291 				sexp_val = SEXP_TRUE;
24292 				sexp_manipulate_colgroup(node, true);
24293 				break;
24294 
24295 			case OP_REMOVE_FROM_COLGROUP:
24296 				sexp_val = SEXP_TRUE;
24297 				sexp_manipulate_colgroup(node, false);
24298 				break;
24299 
24300 			case OP_GET_COLGROUP_ID:
24301 				sexp_val = sexp_get_colgroup(node);
24302 				break;
24303 
24304 			case OP_SHIP_EFFECT:
24305 				sexp_val = SEXP_TRUE;
24306 				sexp_ship_effect(node);
24307 				break;
24308 
24309 			case OP_CLEAR_SUBTITLES:
24310 				sexp_val = SEXP_TRUE;
24311 				sexp_clear_subtitles();
24312 				break;
24313 
24314 			case OP_SET_THRUSTERS:
24315 				sexp_val = SEXP_TRUE;
24316 				sexp_set_thrusters(node);
24317 				break;
24318 
24319 			case OP_CHANGE_TEAM_COLOR:
24320 				sexp_val = SEXP_TRUE;
24321 				sexp_change_team_color(node);
24322 				break;
24323 
24324 			case OP_CALL_SSM_STRIKE:
24325 				sexp_val = SEXP_TRUE;
24326 				sexp_call_ssm_strike(node);
24327 				break;
24328 
24329 			case OP_PLAYER_IS_CHEATING_BASTARD:
24330 				sexp_val = sexp_player_is_cheating_bastard();
24331 				break;
24332 
24333 			default:
24334 				Error(LOCATION, "Looking for SEXP operator, found '%s'.\n", CTEXT(cur_node));
24335 				break;
24336 		}
24337 
24338 		if (Log_event) {
24339 			add_to_event_log_buffer(get_operator_index(cur_node), sexp_val);
24340 		}
24341 
24342 		Assert(!Current_sexp_operator.empty());
24343 		Current_sexp_operator.pop_back();
24344 
24345 		Assert(sexp_val != UNINITIALIZED);
24346 
24347 		// if we haven't returned, check the sexp value of the sexpression evaluation.  A special
24348 		// value of known true or known false means that we should set the sexp.value field for
24349 		// short circuit eval (and return that special value as well).
24350 		if (sexp_val == SEXP_KNOWN_TRUE) {
24351 			Sexp_nodes[cur_node].value = SEXP_KNOWN_TRUE;
24352 			return SEXP_TRUE;
24353 		}
24354 
24355 		if (sexp_val == SEXP_KNOWN_FALSE) {
24356 			Sexp_nodes[cur_node].value = SEXP_KNOWN_FALSE;
24357 			return SEXP_FALSE;
24358 		}
24359 
24360 		if ( sexp_val == SEXP_NAN ) {
24361 			Sexp_nodes[cur_node].value = SEXP_NAN;			// not a number values are false I would suspect
24362 			return SEXP_FALSE;
24363 		}
24364 
24365 		if ( sexp_val == SEXP_NAN_FOREVER ) {
24366 			Sexp_nodes[cur_node].value = SEXP_NAN_FOREVER;
24367 			return SEXP_FALSE;	// Goober5000 changed from sexp_val to SEXP_FALSE on 2/21/2006 in accordance with above comment
24368 		}
24369 
24370 		if ( sexp_val == SEXP_CANT_EVAL ) {
24371 			Sexp_nodes[cur_node].value = SEXP_CANT_EVAL;
24372 			Sexp_useful_number = 0;  // indicate sexp isn't current yet
24373 			return SEXP_FALSE;
24374 		}
24375 
24376 		if ( Sexp_nodes[cur_node].value == SEXP_NAN ) {	// if we had a nan, but now don't, reset the value
24377 			Sexp_nodes[cur_node].value = SEXP_UNKNOWN;
24378 			return sexp_val;
24379 		}
24380 
24381 		// now, reconcile positive and negative - Goober5000
24382 		if (sexp_val < 0)
24383 		{
24384 			int parent_node = find_parent_operator(cur_node);
24385 			int arg_num = find_argnum(parent_node, cur_node);
24386 
24387 			// make sure everything works okay
24388 			if (arg_num == -1)
24389 			{
24390 				SCP_string sexp_text;
24391 				convert_sexp_to_string(sexp_text, cur_node, SEXP_ERROR_CHECK_MODE);
24392 				Error(LOCATION, "Error finding sexp argument.  Received value %d for sexp:\n%s", sexp_val, sexp_text.c_str());
24393 			}
24394 
24395 			// if we need a positive value, make it positive
24396 			if (query_operator_argument_type(get_operator_index(parent_node), arg_num) == OPF_POSITIVE)
24397 			{
24398 				sexp_val *= -1;
24399 			}
24400 		}
24401 
24402 		if ( sexp_val ){
24403 			Sexp_nodes[cur_node].value = SEXP_TRUE;
24404 		} else {
24405 			Sexp_nodes[cur_node].value = SEXP_FALSE;
24406 		}
24407 
24408 		return sexp_val;
24409 	}
24410 }
24411 
24412 /**
24413  * Only runs on the client machines not the server. Evaluates the contents of a SEXP packet and calls the relevent multi_sexp_x
24414  * function(s).
24415  */
multi_sexp_eval()24416 void multi_sexp_eval()
24417 {
24418 	int op_num;
24419 
24420 	Assert (MULTIPLAYER_CLIENT);
24421 
24422 	while (Multi_sexp_bytes_left > 0) {
24423 		op_num = multi_sexp_get_next_operator();
24424 
24425 		Assert (Multi_sexp_bytes_left);
24426 
24427 		if (op_num < 0) {
24428 			Warning(LOCATION, "Received invalid operator number from host in multi_sexp_eval(). Entire packet may be corrupt. Discarding packet");
24429 			Int3();
24430 			return;
24431 		}
24432 
24433 		switch(op_num) {
24434 
24435 			case OP_CHANGE_SOUNDTRACK:
24436 				multi_sexp_change_soundtrack();
24437 				break;
24438 
24439 			case OP_SET_PERSONA:
24440 				multi_sexp_set_persona();
24441 				break;
24442 
24443 			case OP_CHANGE_SUBSYSTEM_NAME:
24444 				multi_sexp_change_subsystem_name();
24445 				break;
24446 
24447 			case OP_SHIP_SUBSYS_NO_REPLACE:
24448 				multi_sexp_deal_with_subsys_flag(SSF_NO_REPLACE);
24449 				break;
24450 			case OP_SHIP_SUBSYS_NO_LIVE_DEBRIS:
24451 				multi_sexp_deal_with_subsys_flag(SSF_NO_LIVE_DEBRIS);
24452 				break;
24453 			case OP_SHIP_SUBSYS_VANISHED:
24454 				multi_sexp_deal_with_subsys_flag(SSF_VANISHED);
24455 				break;
24456 			case OP_SHIP_SUBSYS_IGNORE_IF_DEAD:
24457 				multi_sexp_deal_with_subsys_flag(SSF_MISSILES_IGNORE_IF_DEAD);
24458 				break;
24459 			case OP_SHIP_SUBSYS_TARGETABLE:
24460 			case OP_SHIP_SUBSYS_UNTARGETABLE:
24461 				multi_sexp_deal_with_subsys_flag(SSF_UNTARGETABLE);
24462 				break;
24463 
24464 			case OP_SHIP_CHANGE_CALLSIGN:
24465 				multi_sexp_ship_change_callsign();
24466 				break;
24467 
24468 			case OP_SET_RESPAWNS:
24469 				multi_sexp_set_respawns();
24470 				break;
24471 
24472 			case OP_REMOVE_WEAPONS:
24473 				multi_sexp_remove_weapons();
24474 				break;
24475 
24476 			case OP_CHANGE_SHIP_CLASS:
24477 				multi_sexp_change_ship_class();
24478 				break;
24479 
24480 			case OP_PLAY_SOUND_FROM_TABLE:
24481 				multi_sexp_play_sound_from_table();
24482 				break;
24483 
24484 			case OP_PLAY_SOUND_FROM_FILE:
24485 				multi_sexp_play_sound_from_file();
24486 				break;
24487 
24488 			case OP_CLOSE_SOUND_FROM_FILE:
24489 				multi_sexp_close_sound_from_file();
24490 				break;
24491 
24492 			case OP_SHIP_BOMB_TARGETABLE:
24493 			case OP_SHIP_BOMB_UNTARGETABLE:
24494 			case OP_SHIP_INVISIBLE:
24495 			case OP_SHIP_VISIBLE:
24496 			case OP_SHIP_STEALTHY:
24497 			case OP_SHIP_UNSTEALTHY:
24498 			case OP_FRIENDLY_STEALTH_INVISIBLE:
24499 			case OP_FRIENDLY_STEALTH_VISIBLE:
24500 			case OP_LOCK_AFTERBURNER:
24501 			case OP_UNLOCK_AFTERBURNER:
24502 			case OP_LOCK_PRIMARY_WEAPON:
24503 			case OP_UNLOCK_PRIMARY_WEAPON:
24504 			case OP_LOCK_SECONDARY_WEAPON:
24505 			case OP_UNLOCK_SECONDARY_WEAPON:
24506 			case OP_SHIELDS_ON:
24507 			case OP_SHIELDS_OFF:
24508 				multi_sexp_deal_with_ship_flag();
24509 				break;
24510 
24511 			case OP_ALTER_SHIP_FLAG:
24512 				multi_sexp_alter_ship_flag();
24513 				break;
24514 
24515 			case OP_SET_AFTERBURNER_ENERGY:
24516 				multi_sexp_set_energy_pct();
24517 				break;
24518 
24519 			case OP_SET_AMBIENT_LIGHT:
24520 				multi_sexp_set_ambient_light();
24521 				break;
24522 
24523 			case OP_MODIFY_VARIABLE:
24524 				multi_sexp_modify_variable();
24525 				break;
24526 
24527 			case OP_NAV_ADD_WAYPOINT:
24528 				multi_sexp_add_nav_waypoint();
24529 				break;
24530 
24531 			case OP_CUTSCENES_FADE_IN:
24532 			case OP_CUTSCENES_FADE_OUT:
24533 				multi_sexp_fade(op_num == OP_CUTSCENES_FADE_IN);
24534 				break;
24535 
24536 			case OP_NAV_ADD_SHIP:
24537 				multi_add_nav_ship();
24538 				break;
24539 
24540 			case OP_NAV_DEL:
24541 				multi_del_nav();
24542 				break;
24543 
24544 			case OP_ADD_REMOVE_ESCORT:
24545 				multi_sexp_add_remove_escort();
24546 				break;
24547 
24548 			case OP_CUTSCENES_SHOW_SUBTITLE_TEXT:
24549 				 multi_sexp_show_subtitle_text();
24550 				 break;
24551 
24552 			case OP_CUTSCENES_SHOW_SUBTITLE_IMAGE:
24553 				 multi_sexp_show_subtitle_image();
24554 				 break;
24555 
24556 			case OP_TRAINING_MSG:
24557 				multi_sexp_send_training_message();
24558 				break;
24559 
24560 			case OP_HUD_DISABLE:
24561 				multi_sexp_hud_disable();
24562 				break;
24563 
24564 			case OP_HUD_DISABLE_EXCEPT_MESSAGES:
24565 				multi_sexp_hud_disable_except_messages();
24566 				break;
24567 
24568 			case OP_FLASH_HUD_GAUGE:
24569 				multi_sexp_flash_hud_gauge();
24570 				break;
24571 
24572 			case OP_HUD_DISPLAY_GAUGE:
24573 				multi_sexp_hud_display_gauge();
24574 				break;
24575 
24576 			case OP_CUTSCENES_SET_CUTSCENE_BARS:
24577 			case OP_CUTSCENES_UNSET_CUTSCENE_BARS:
24578 				multi_sexp_toggle_cutscene_bars(op_num == OP_CUTSCENES_SET_CUTSCENE_BARS );
24579 				break;
24580 
24581 			case OP_CUTSCENES_SET_CAMERA_FACING:
24582 				multi_sexp_set_camera_facing();
24583 				break;
24584 
24585 			case OP_CUTSCENES_SET_CAMERA_FACING_OBJECT:
24586 				multi_sexp_set_camera_facing_object();
24587 				break;
24588 
24589 			case OP_CUTSCENES_SET_CAMERA_TARGET :
24590 				multi_sexp_set_camera_target();
24591 				break;
24592 
24593 			case OP_CUTSCENES_SET_CAMERA_ROTATION:
24594 				multi_sexp_set_camera_rotation();
24595 				break;
24596 
24597 			case OP_CUTSCENES_SET_CAMERA_FOV:
24598 				multi_sexp_set_camera_fov();
24599 				break;
24600 
24601 			case OP_CUTSCENES_SET_CAMERA_POSITION:
24602 				multi_sexp_set_camera_position();
24603 				break;
24604 
24605 			case OP_SET_CAMERA_SHUDDER:
24606 				multi_sexp_set_camera_shudder();
24607 				break;
24608 
24609 			case OP_CUTSCENES_RESET_CAMERA:
24610 				multi_sexp_reset_camera();
24611 				break;
24612 
24613 			case OP_CUTSCENES_SET_FOV:
24614 				multi_sexp_set_fov();
24615 				break;
24616 
24617 			case OP_CUTSCENES_RESET_FOV:
24618 				multi_sexp_reset_fov();
24619 				break;
24620 
24621 			case OP_JUMP_NODE_SET_JUMPNODE_NAME:
24622 				multi_sexp_set_jumpnode_name();
24623 				break;
24624 
24625 			case OP_IGNORE_KEY:
24626 				multi_sexp_ignore_key();
24627 				break;
24628 
24629 			case OP_JUMP_NODE_SET_JUMPNODE_COLOR:
24630 				multi_sexp_set_jumpnode_color();
24631 				break;
24632 
24633 			case OP_JUMP_NODE_SET_JUMPNODE_MODEL:
24634 				multi_sexp_set_jumpnode_model();
24635 				break;
24636 
24637 			case OP_JUMP_NODE_SHOW_JUMPNODE:
24638 			case OP_JUMP_NODE_HIDE_JUMPNODE:
24639 				multi_sexp_show_hide_jumpnode(op_num == OP_JUMP_NODE_SHOW_JUMPNODE);
24640 				break;
24641 
24642 			case OP_CLEAR_SUBTITLES:
24643 				multi_sexp_clear_subtitles();
24644 				break;
24645 
24646 			case OP_SET_OBJECT_SPEED_X:
24647 			case OP_SET_OBJECT_SPEED_Y:
24648 			case OP_SET_OBJECT_SPEED_Z:
24649 				multi_sexp_set_object_speed();
24650 				break;
24651 			case OP_SET_OBJECT_POSITION:
24652 				multi_sexp_set_object_position();
24653 				break;
24654 
24655 			case OP_CHANGE_TEAM_COLOR:
24656 				multi_sexp_change_team_color();
24657 				break;
24658 
24659 			case OP_HUD_SET_MAX_TARGETING_RANGE:
24660 				multi_sexp_hud_set_max_targeting_range();
24661 				break;
24662 
24663 			case OP_CUTSCENES_SET_TIME_COMPRESSION:
24664 				multi_sexp_set_time_compression();
24665 				break;
24666 
24667 			case OP_CUTSCENES_RESET_TIME_COMPRESSION:
24668 				sexp_reset_time_compression();
24669 				break;
24670 
24671 			case OP_SET_ETS_VALUES:
24672 				multi_sexp_set_ets_values();
24673 				break;
24674 
24675 			case OP_SCRIPT_EVAL_MULTI:
24676 				multi_sexp_script_eval_multi();
24677 				break;
24678 
24679 			// bad sexp in the packet
24680 			default:
24681 				// probably just a version error where the host supports a SEXP but a client does not
24682 				if (multi_sexp_discard_operator()) {
24683 					Warning(LOCATION, "Received invalid SEXP operator number from host. Operator number %d is not supported by this version.", op_num);
24684 				}
24685 				// a more major problem
24686 				else {
24687 					Warning(LOCATION, "Received invalid SEXP packet from host. Function involving operator %d lacks termination. Entire packet may be corrupt. Discarding remaining packet", op_num);
24688 					Int3();
24689 					return;
24690 				}
24691 		}
24692 
24693 		multi_finished_callback();
24694 	}
24695 }
24696 
24697 //	Still a debug-level system.
24698 //	get_sexp_main reads and builds the internal representation for a
24699 //	symbolic expression.
24700 //	On entry:
24701 //		Mp points at first character in expression.
24702 //	The symbolic expression is built in Sexp_nodes beginning at node 0.
get_sexp_main()24703 int get_sexp_main()
24704 {
24705 	int	start_node, op;
24706 	char  *savep, ch;
24707 
24708 	ignore_white_space();
24709 
24710 	savep = Mp;
24711 	if (!strncmp(Mp, "( )", 3))
24712 		savep++;
24713 
24714 	Assert(*Mp == '(');
24715 	Mp++;
24716 	start_node = get_sexp();
24717 	// only need to check syntax if we have a operator
24718 	if (Fred_running || (start_node == -1))
24719 		return start_node;
24720 
24721 	ch = *Mp;
24722 	*Mp = '\0';
24723 
24724 	op = get_operator_index(CTEXT(start_node));
24725 	if (op == -1)
24726 		Error (LOCATION, "Can't find operator %s in operator list\n.", CTEXT(start_node) );
24727 
24728 	*Mp = ch;
24729 
24730 	return start_node;
24731 }
24732 
run_sexp(const char * sexpression)24733 int run_sexp(const char* sexpression)
24734 {
24735 	char* oldMp = Mp;
24736 	int n, i, sexp_val = UNINITIALIZED;
24737 	char buf[8192];
24738 
24739 	strcpy_s(buf, sexpression);
24740 
24741 	// HACK: ! -> "
24742 	for (i = 0; i < (int)strlen(buf); i++)
24743 		if (buf[i] == '!')
24744 			buf[i]='\"';
24745 
24746 	Mp = buf;
24747 
24748 	n = get_sexp_main();
24749 	if (n != -1)
24750 	{
24751 		sexp_val = eval_sexp(n);
24752 		free_sexp2(n);
24753 	}
24754 	Mp = oldMp;
24755 
24756 	return sexp_val;
24757 }
24758 
24759 DCF(sexpc, "Always runs the given sexp command ")
24760 {
24761 	if ( Dc_command )       {
24762 		if (Dc_command_line != NULL) {
24763 			char buf[8192];
24764 			snprintf(buf, 8191, "( when ( true ) ( %s ) )", Dc_command_line);
24765 
24766 			int sexp_val = run_sexp( buf );
24767 			dc_printf("SEXP '%s' run, sexp_val = %d\n", buf, sexp_val);
24768 			do {
24769 				dc_get_arg(ARG_ANY);
24770 			} while (Dc_arg_type != ARG_NONE);
24771 		}
24772 	}
24773 	if ( Dc_help )  {
24774 		dc_printf( "Usage: sexpc sexpression\n. Always runs the given sexp as '( when ( true ) ( sexp ) )' .\n" );
24775 	}
24776 }
24777 
24778 
24779 DCF(sexp,"Runs the given sexp")
24780 {
24781 	if ( Dc_command )       {
24782 		if (Dc_command_line != NULL) {
24783 			int sexp_val = run_sexp( Dc_command_line );
24784 			dc_printf("SEXP '%s' run, sexp_val = %d\n", Dc_command_line, sexp_val);
24785 			do {
24786 				dc_get_arg(ARG_ANY);
24787 			} while (Dc_arg_type != ARG_NONE);
24788 		}
24789 	}
24790 	if ( Dc_help )  {
24791 		dc_printf( "Usage: sexp 'sexpression'\n. Runs the given sexp.\n");
24792 	}
24793 }
24794 
24795 
test_sexps()24796 void test_sexps()
24797 {
24798 	Mp = Mission_text;
24799 	while (*Mp != '#') {
24800 		get_sexp_main();
24801 		diag_printf("\n----------------\n");
24802 		ignore_white_space();
24803 	}
24804 	exit(0);
24805 }
24806 
24807 // returns the data type returned by an operator
query_operator_return_type(int op)24808 int query_operator_return_type(int op)
24809 {
24810 	if (op < FIRST_OP)
24811 	{
24812 		Assert(op >= 0 && op < Num_operators);
24813 		op = Operators[op].value;
24814 	}
24815 
24816 	switch (op)
24817 	{
24818 		case OP_TRUE:
24819 		case OP_FALSE:
24820 		case OP_AND:
24821 		case OP_AND_IN_SEQUENCE:
24822 		case OP_OR:
24823 		case OP_NOT:
24824 		case OP_XOR:
24825 		case OP_EQUALS:
24826 		case OP_GREATER_THAN:
24827 		case OP_LESS_THAN:
24828 		case OP_NOT_EQUAL:
24829 		case OP_GREATER_OR_EQUAL:
24830 		case OP_LESS_OR_EQUAL:
24831 		case OP_STRING_EQUALS:
24832 		case OP_STRING_GREATER_THAN:
24833 		case OP_STRING_LESS_THAN:
24834 		case OP_PERFORM_ACTIONS:
24835 		case OP_IS_DESTROYED:
24836 		case OP_IS_SUBSYSTEM_DESTROYED:
24837 		case OP_IS_DISABLED:
24838 		case OP_IS_DISARMED:
24839 		case OP_HAS_DOCKED:
24840 		case OP_HAS_UNDOCKED:
24841 		case OP_HAS_ARRIVED:
24842 		case OP_HAS_DEPARTED:
24843 		case OP_IS_DESTROYED_DELAY:
24844 		case OP_WAS_DESTROYED_BY_DELAY:
24845 		case OP_IS_SUBSYSTEM_DESTROYED_DELAY:
24846 		case OP_IS_DISABLED_DELAY:
24847 		case OP_IS_DISARMED_DELAY:
24848 		case OP_HAS_DOCKED_DELAY:
24849 		case OP_HAS_UNDOCKED_DELAY:
24850 		case OP_HAS_ARRIVED_DELAY:
24851 		case OP_HAS_DEPARTED_DELAY:
24852 		case OP_IS_IFF:
24853 		case OP_IS_AI_CLASS:
24854 		case OP_IS_SHIP_TYPE:
24855 		case OP_IS_SHIP_CLASS:
24856 		case OP_HAS_TIME_ELAPSED:
24857 		case OP_GOAL_INCOMPLETE:
24858 		case OP_GOAL_TRUE_DELAY:
24859 		case OP_GOAL_FALSE_DELAY:
24860 		case OP_EVENT_INCOMPLETE:
24861 		case OP_EVENT_TRUE_DELAY:
24862 		case OP_EVENT_FALSE_MSECS_DELAY:
24863 		case OP_EVENT_TRUE_MSECS_DELAY:
24864 		case OP_EVENT_FALSE_DELAY:
24865 		case OP_PREVIOUS_EVENT_TRUE:
24866 		case OP_PREVIOUS_EVENT_FALSE:
24867 		case OP_PREVIOUS_EVENT_INCOMPLETE:
24868 		case OP_PREVIOUS_GOAL_TRUE:
24869 		case OP_PREVIOUS_GOAL_FALSE:
24870 		case OP_PREVIOUS_GOAL_INCOMPLETE:
24871 		case OP_WAYPOINTS_DONE:
24872 		case OP_WAYPOINTS_DONE_DELAY:
24873 		case OP_SHIP_TYPE_DESTROYED:
24874 		case OP_LAST_ORDER_TIME:
24875 		case OP_KEY_PRESSED:
24876 		case OP_TARGETED:
24877 		case OP_NODE_TARGETED:
24878 		case OP_SPEED:
24879 		case OP_FACING:
24880 		case OP_FACING2:
24881 		case OP_ORDER:
24882 		case OP_QUERY_ORDERS:
24883 		case OP_WAYPOINT_MISSED:
24884 		case OP_WAYPOINT_TWICE:
24885 		case OP_PATH_FLOWN:
24886 		case OP_EVENT_TRUE:
24887 		case OP_EVENT_FALSE:
24888 		case OP_SKILL_LEVEL_AT_LEAST:
24889 		case OP_IS_CARGO_KNOWN:
24890 		case OP_HAS_BEEN_TAGGED_DELAY:
24891 		case OP_CAP_SUBSYS_CARGO_KNOWN_DELAY:
24892 		case OP_CARGO_KNOWN_DELAY:
24893 		case OP_WAS_PROMOTION_GRANTED:
24894 		case OP_WAS_MEDAL_GRANTED:
24895 		case OP_PERCENT_SHIPS_DEPARTED:
24896 		case OP_PERCENT_SHIPS_DESTROYED:
24897 		case OP_PERCENT_SHIPS_DISARMED:
24898 		case OP_PERCENT_SHIPS_DISABLED:
24899 		case OP_PERCENT_SHIPS_ARRIVED:
24900 		case OP_DEPART_NODE_DELAY:
24901 		case OP_DESTROYED_DEPARTED_DELAY:
24902 		case OP_SPECIAL_CHECK:
24903 		case OP_IS_TAGGED:
24904 		case OP_PRIMARIES_DEPLETED:
24905 		case OP_SECONDARIES_DEPLETED:
24906 		case OP_SHIELD_QUAD_LOW:
24907 		case OP_IS_SECONDARY_SELECTED:
24908 		case OP_IS_PRIMARY_SELECTED:
24909 		case OP_IS_SHIP_STEALTHY:
24910 		case OP_IS_FRIENDLY_STEALTH_VISIBLE:
24911 		case OP_IS_CARGO:
24912 		case OP_MISSILE_LOCKED:
24913 		case OP_NAV_IS_VISITED:
24914 		case OP_NAV_ISLINKED:
24915 		case OP_IS_PLAYER:
24916 		case OP_PRIMARY_FIRED_SINCE:
24917 		case OP_SECONDARY_FIRED_SINCE:
24918 		case OP_IS_FACING:
24919 		case OP_HAS_PRIMARY_WEAPON:
24920 		case OP_HAS_SECONDARY_WEAPON:
24921 		case OP_IS_BIT_SET:
24922 		case OP_DIRECTIVE_VALUE:
24923 		case OP_IS_IN_BOX:
24924 		case OP_IS_IN_MISSION:
24925 		case OP_PLAYER_IS_CHEATING_BASTARD:
24926 		case OP_ARE_SHIP_FLAGS_SET:
24927 			return OPR_BOOL;
24928 
24929 		case OP_PLUS:
24930 		case OP_MINUS:
24931 		case OP_MOD:
24932 		case OP_MUL:
24933 		case OP_DIV:
24934 		case OP_RAND:
24935 		case OP_RAND_MULTIPLE:
24936 		case OP_MIN:
24937 		case OP_MAX:
24938 		case OP_AVG:
24939 		case OP_POW:
24940 		case OP_SIGNUM:
24941 		case OP_GET_OBJECT_X:
24942 		case OP_GET_OBJECT_Y:
24943 		case OP_GET_OBJECT_Z:
24944 		case OP_GET_OBJECT_PITCH:
24945 		case OP_GET_OBJECT_BANK:
24946 		case OP_GET_OBJECT_HEADING:
24947 		case OP_GET_OBJECT_SPEED_X:
24948 		case OP_GET_OBJECT_SPEED_Y:
24949 		case OP_GET_OBJECT_SPEED_Z:
24950 		case OP_SCRIPT_EVAL_NUM:
24951 		case OP_SCRIPT_EVAL_STRING:
24952 		case OP_STRING_TO_INT:
24953 		case OP_GET_THROTTLE_SPEED:
24954 		case OP_GET_VARIABLE_BY_INDEX:
24955 		case OP_GET_COLGROUP_ID:
24956 			return OPR_NUMBER;
24957 
24958 		case OP_ABS:
24959 		case OP_SET_BIT:
24960 		case OP_UNSET_BIT:
24961 		case OP_BITWISE_AND:
24962 		case OP_BITWISE_OR:
24963 		case OP_BITWISE_NOT:
24964 		case OP_BITWISE_XOR:
24965 		case OP_TIME_SHIP_DESTROYED:
24966 		case OP_TIME_SHIP_ARRIVED:
24967 		case OP_TIME_SHIP_DEPARTED:
24968 		case OP_TIME_WING_DESTROYED:
24969 		case OP_TIME_WING_ARRIVED:
24970 		case OP_TIME_WING_DEPARTED:
24971 		case OP_MISSION_TIME:
24972 		case OP_MISSION_TIME_MSECS:
24973 		case OP_TIME_DOCKED:
24974 		case OP_TIME_UNDOCKED:
24975 		case OP_AFTERBURNER_LEFT:
24976 		case OP_WEAPON_ENERGY_LEFT:
24977 		case OP_SHIELDS_LEFT:
24978 		case OP_HITS_LEFT:
24979 		case OP_HITS_LEFT_SUBSYSTEM:
24980 		case OP_HITS_LEFT_SUBSYSTEM_GENERIC:
24981 		case OP_HITS_LEFT_SUBSYSTEM_SPECIFIC:
24982 		case OP_SIM_HITS_LEFT:
24983 		case OP_DISTANCE:
24984 		case OP_DISTANCE_SUBSYSTEM:
24985 		case OP_NUM_WITHIN_BOX:
24986 		case OP_NUM_PLAYERS:
24987 		case OP_NUM_KILLS:
24988 		case OP_NUM_ASSISTS:
24989 		case OP_SHIP_DEATHS:
24990 		case OP_RESPAWNS_LEFT:
24991 		case OP_SHIP_SCORE:
24992 		case OP_NUM_TYPE_KILLS:
24993 		case OP_NUM_CLASS_KILLS:
24994 		case OP_SHIELD_RECHARGE_PCT:
24995 		case OP_ENGINE_RECHARGE_PCT:
24996 		case OP_WEAPON_RECHARGE_PCT:
24997 		case OP_PRIMARY_AMMO_PCT:
24998 		case OP_SECONDARY_AMMO_PCT:
24999 		case OP_GET_PRIMARY_AMMO:
25000 		case OP_GET_SECONDARY_AMMO:
25001 		case OP_GET_NUM_COUNTERMEASURES:
25002 		case OP_SPECIAL_WARP_DISTANCE:
25003 		case OP_IS_SHIP_VISIBLE:
25004 		case OP_TEAM_SCORE:
25005 		case OP_NUM_SHIPS_IN_BATTLE:
25006 		case OP_NUM_SHIPS_IN_WING:
25007 		case OP_CURRENT_SPEED:
25008 		case OP_NAV_DISTANCE:
25009 		case OP_GET_DAMAGE_CAUSED:
25010 		case OP_CUTSCENES_GET_FOV:
25011 		case OP_NUM_VALID_ARGUMENTS:
25012 		case OP_STRING_GET_LENGTH:
25013 		case OP_GET_ETS_VALUE:
25014 			return OPR_POSITIVE;
25015 
25016 		case OP_COND:
25017 		case OP_WHEN:
25018 		case OP_WHEN_ARGUMENT:
25019 		case OP_EVERY_TIME:
25020 		case OP_EVERY_TIME_ARGUMENT:
25021 		case OP_IF_THEN_ELSE:
25022 		case OP_INVALIDATE_ARGUMENT:
25023 		case OP_VALIDATE_ARGUMENT:
25024 		case OP_INVALIDATE_ALL_ARGUMENTS:
25025 		case OP_VALIDATE_ALL_ARGUMENTS:
25026 		case OP_DO_FOR_VALID_ARGUMENTS:
25027 		case OP_CHANGE_IFF:
25028 		case OP_CHANGE_AI_CLASS:
25029 		case OP_CLEAR_SHIP_GOALS:
25030 		case OP_CLEAR_WING_GOALS:
25031 		case OP_CLEAR_GOALS:
25032 		case OP_ADD_SHIP_GOAL:
25033 		case OP_ADD_WING_GOAL:
25034 		case OP_ADD_GOAL:
25035 		case OP_REMOVE_GOAL:
25036 		case OP_PROTECT_SHIP:
25037 		case OP_UNPROTECT_SHIP:
25038 		case OP_BEAM_PROTECT_SHIP:
25039 		case OP_BEAM_UNPROTECT_SHIP:
25040 		case OP_TURRET_PROTECT_SHIP:
25041 		case OP_TURRET_UNPROTECT_SHIP:
25042 		case OP_NOP:
25043 		case OP_GOALS_ID:
25044 		case OP_SEND_MESSAGE:
25045 		case OP_SELF_DESTRUCT:
25046 		case OP_NEXT_MISSION:
25047 		case OP_END_CAMPAIGN:
25048 		case OP_END_OF_CAMPAIGN:
25049 		case OP_SABOTAGE_SUBSYSTEM:
25050 		case OP_REPAIR_SUBSYSTEM:
25051 		case OP_INVALIDATE_GOAL:
25052 		case OP_VALIDATE_GOAL:
25053 		case OP_SEND_RANDOM_MESSAGE:
25054 		case OP_TRANSFER_CARGO:
25055 		case OP_EXCHANGE_CARGO:
25056 		case OP_SET_CARGO:
25057 		case OP_JETTISON_CARGO:
25058 		case OP_SET_DOCKED:
25059 		case OP_CARGO_NO_DEPLETE:
25060 		case OP_SET_SCANNED:
25061 		case OP_SET_UNSCANNED:
25062 		case OP_KEY_RESET:
25063 		case OP_KEY_RESET_MULTIPLE:
25064 		case OP_TRAINING_MSG:
25065 		case OP_SET_TRAINING_CONTEXT_FLY_PATH:
25066 		case OP_SET_TRAINING_CONTEXT_SPEED:
25067 		case OP_END_MISSION:
25068 		case OP_SET_DEBRIEFING_TOGGLED:
25069 		case OP_FORCE_JUMP:
25070 		case OP_SET_SUBSYSTEM_STRNGTH:
25071 		case OP_DESTROY_SUBSYS_INSTANTLY:
25072 		case OP_GOOD_REARM_TIME:
25073 		case OP_GRANT_PROMOTION:
25074 		case OP_GRANT_MEDAL:
25075 		case OP_ALLOW_SHIP:
25076 		case OP_ALLOW_WEAPON:
25077 		case OP_TECH_ADD_SHIP:
25078 		case OP_TECH_ADD_WEAPON:
25079 		case OP_TECH_ADD_INTEL:
25080 		case OP_TECH_ADD_INTEL_XSTR:
25081 		case OP_TECH_RESET_TO_DEFAULT:
25082 		case OP_CHANGE_PLAYER_SCORE:
25083 		case OP_CHANGE_TEAM_SCORE:
25084 		case OP_WARP_BROKEN:
25085 		case OP_WARP_NOT_BROKEN:
25086 		case OP_WARP_NEVER:
25087 		case OP_WARP_ALLOWED:
25088 		case OP_SET_SUBSPACE_DRIVE:
25089 		case OP_FLASH_HUD_GAUGE:
25090 		case OP_GOOD_SECONDARY_TIME:
25091 		case OP_SHIP_VISIBLE:
25092 		case OP_SHIP_INVISIBLE:
25093 		case OP_SHIP_TAG:
25094 		case OP_SHIP_UNTAG:
25095 		case OP_SHIP_VULNERABLE:
25096 		case OP_SHIP_INVULNERABLE:
25097 		case OP_SHIP_BOMB_TARGETABLE:
25098 		case OP_SHIP_BOMB_UNTARGETABLE:
25099 		case OP_SHIP_GUARDIAN:
25100 		case OP_SHIP_NO_GUARDIAN:
25101 		case OP_SHIP_GUARDIAN_THRESHOLD:
25102 		case OP_SHIP_SUBSYS_GUARDIAN_THRESHOLD:
25103 		case OP_SHIP_VANISH:
25104 		case OP_DESTROY_INSTANTLY:
25105 		case OP_SHIELDS_ON:
25106 		case OP_SHIELDS_OFF:
25107 		case OP_SHIP_STEALTHY:
25108 		case OP_SHIP_UNSTEALTHY:
25109 		case OP_FRIENDLY_STEALTH_INVISIBLE:
25110 		case OP_FRIENDLY_STEALTH_VISIBLE:
25111 		case OP_SHIP_SUBSYS_NO_REPLACE:
25112 		case OP_SHIP_SUBSYS_NO_LIVE_DEBRIS:
25113 		case OP_SHIP_SUBSYS_VANISHED:
25114 		case OP_SHIP_SUBSYS_IGNORE_IF_DEAD:
25115 		case OP_SHIP_SUBSYS_TARGETABLE:
25116 		case OP_SHIP_SUBSYS_UNTARGETABLE:
25117 		case OP_RED_ALERT:
25118 		case OP_MODIFY_VARIABLE:
25119 		case OP_SET_VARIABLE_BY_INDEX:
25120 		case OP_BEAM_FIRE:
25121 		case OP_BEAM_FIRE_COORDS:
25122 		case OP_BEAM_FREE:
25123 		case OP_BEAM_FREE_ALL:
25124 		case OP_BEAM_LOCK:
25125 		case OP_BEAM_LOCK_ALL:
25126 		case OP_TURRET_FREE:
25127 		case OP_TURRET_FREE_ALL:
25128 		case OP_TURRET_LOCK:
25129 		case OP_TURRET_LOCK_ALL:
25130 		case OP_TURRET_CHANGE_WEAPON:
25131 		case OP_TURRET_SET_DIRECTION_PREFERENCE:
25132 		case OP_TURRET_SET_RATE_OF_FIRE:
25133 		case OP_TURRET_SET_OPTIMUM_RANGE:
25134 		case OP_TURRET_SET_TARGET_PRIORITIES:
25135 		case OP_TURRET_SET_TARGET_ORDER:
25136 		case OP_SET_ARMOR_TYPE:
25137 		case OP_WEAPON_SET_DAMAGE_TYPE:
25138 		case OP_SHIP_SET_DAMAGE_TYPE:
25139 		case OP_SHIP_SHOCKWAVE_SET_DAMAGE_TYPE:
25140 		case OP_FIELD_SET_DAMAGE_TYPE:
25141 		case OP_SHIP_TURRET_TARGET_ORDER:
25142 		case OP_TURRET_SUBSYS_TARGET_DISABLE:
25143 		case OP_TURRET_SUBSYS_TARGET_ENABLE:
25144 		case OP_ADD_REMOVE_ESCORT:
25145 		case OP_DAMAGED_ESCORT_LIST:
25146 		case OP_DAMAGED_ESCORT_LIST_ALL:
25147 		case OP_AWACS_SET_RADIUS:
25148 		case OP_PRIMITIVE_SENSORS_SET_RANGE:
25149 		case OP_SEND_MESSAGE_LIST:
25150 		case OP_CAP_WAYPOINT_SPEED:
25151 		case OP_TURRET_TAGGED_ONLY_ALL:
25152 		case OP_TURRET_TAGGED_CLEAR_ALL:
25153 		case OP_SUBSYS_SET_RANDOM:
25154 		case OP_SUPERNOVA_START:
25155 		case OP_SUPERNOVA_STOP:
25156 		case OP_SET_SPECIAL_WARPOUT_NAME:
25157 		case OP_SHIP_VAPORIZE:
25158 		case OP_SHIP_NO_VAPORIZE:
25159 		case OP_SET_EXPLOSION_OPTION:
25160 		case OP_DONT_COLLIDE_INVISIBLE:
25161 		case OP_COLLIDE_INVISIBLE:
25162 		case OP_SET_MOBILE:
25163 		case OP_SET_IMMOBILE:
25164 		case OP_IGNORE_KEY:
25165 		case OP_CHANGE_SHIP_CLASS:
25166 		case OP_SHIP_COPY_DAMAGE:
25167 		case OP_DEACTIVATE_GLOW_POINTS:
25168 		case OP_ACTIVATE_GLOW_POINTS:
25169 		case OP_DEACTIVATE_GLOW_MAPS:
25170 		case OP_ACTIVATE_GLOW_MAPS:
25171 		case OP_DEACTIVATE_GLOW_POINT_BANK:
25172 		case OP_ACTIVATE_GLOW_POINT_BANK:
25173 		case OP_SET_SKYBOX_MODEL:
25174 		case OP_SET_SKYBOX_ORIENT:
25175 		case OP_SET_SUPPORT_SHIP:
25176 		case OP_SET_ARRIVAL_INFO:
25177 		case OP_SET_DEPARTURE_INFO:
25178 		case OP_CHANGE_SOUNDTRACK:
25179 		case OP_PLAY_SOUND_FROM_FILE:
25180 		case OP_CLOSE_SOUND_FROM_FILE:
25181 		case OP_PLAY_SOUND_FROM_TABLE:
25182 		case OP_SET_SOUND_ENVIRONMENT:
25183 		case OP_UPDATE_SOUND_ENVIRONMENT:
25184 		case OP_ADJUST_AUDIO_VOLUME:
25185 		case OP_EXPLOSION_EFFECT:
25186 		case OP_WARP_EFFECT:
25187 		case OP_SET_OBJECT_POSITION:
25188 		case OP_SET_OBJECT_ORIENTATION:
25189 		case OP_SET_OBJECT_FACING:
25190 		case OP_SET_OBJECT_FACING_OBJECT:
25191 		case OP_SHIP_MANEUVER:
25192 		case OP_SHIP_ROT_MANEUVER:
25193 		case OP_SHIP_LAT_MANEUVER:
25194 		case OP_HUD_DISABLE:
25195 		case OP_HUD_DISABLE_EXCEPT_MESSAGES:
25196 		case OP_KAMIKAZE:
25197 		case OP_TURRET_TAGGED_SPECIFIC:
25198 		case OP_TURRET_TAGGED_CLEAR_SPECIFIC:
25199 		case OP_LOCK_ROTATING_SUBSYSTEM:
25200 		case OP_FREE_ROTATING_SUBSYSTEM:
25201 		case OP_REVERSE_ROTATING_SUBSYSTEM:
25202 		case OP_ROTATING_SUBSYS_SET_TURN_TIME:
25203 		case OP_TRIGGER_SUBMODEL_ANIMATION:
25204 		case OP_PLAYER_USE_AI:
25205 		case OP_PLAYER_NOT_USE_AI:
25206 		case OP_ALLOW_TREASON:
25207 		case OP_SET_PLAYER_ORDERS:
25208 		case OP_NAV_ADD_WAYPOINT:
25209 		case OP_NAV_ADD_SHIP:
25210 		case OP_NAV_DEL:
25211 		case OP_NAV_HIDE:
25212 		case OP_NAV_RESTRICT:
25213 		case OP_NAV_UNHIDE:
25214 		case OP_NAV_UNRESTRICT:
25215 		case OP_NAV_SET_VISITED:
25216 		case OP_NAV_UNSET_VISITED:
25217 		case OP_NAV_SET_CARRY:
25218 		case OP_NAV_UNSET_CARRY:
25219 		case OP_NAV_SET_NEEDSLINK:
25220 		case OP_NAV_UNSET_NEEDSLINK:
25221 		case OP_NAV_USECINEMATICS:
25222 		case OP_NAV_USEAP:
25223 		case OP_NAV_SELECT:
25224 		case OP_NAV_UNSELECT:
25225 		case OP_HUD_SET_TEXT:
25226 		case OP_HUD_SET_TEXT_NUM:
25227 		case OP_HUD_SET_MESSAGE:
25228 		case OP_HUD_SET_COORDS:
25229 		case OP_HUD_SET_FRAME:
25230 		case OP_HUD_SET_COLOR:
25231 		case OP_HUD_SET_MAX_TARGETING_RANGE:
25232 		case OP_HUD_CLEAR_MESSAGES:
25233 		case OP_SHIP_CHANGE_ALT_NAME:
25234 		case OP_SHIP_CHANGE_CALLSIGN:
25235 		case OP_SET_DEATH_MESSAGE:
25236 		case OP_SCRAMBLE_MESSAGES:
25237 		case OP_UNSCRAMBLE_MESSAGES:
25238 		case OP_CUTSCENES_SET_CUTSCENE_BARS:
25239 		case OP_CUTSCENES_UNSET_CUTSCENE_BARS:
25240 		case OP_CUTSCENES_FADE_IN:
25241 		case OP_CUTSCENES_FADE_OUT:
25242 		case OP_CUTSCENES_SET_CAMERA:
25243 		case OP_CUTSCENES_SET_CAMERA_FACING:
25244 		case OP_CUTSCENES_SET_CAMERA_FACING_OBJECT:
25245 		case OP_CUTSCENES_SET_CAMERA_FOV:
25246 		case OP_CUTSCENES_SET_CAMERA_HOST:
25247 		case OP_CUTSCENES_SET_CAMERA_POSITION:
25248 		case OP_CUTSCENES_SET_CAMERA_ROTATION:
25249 		case OP_CUTSCENES_SET_CAMERA_TARGET:
25250 		case OP_CUTSCENES_SET_FOV:
25251 		case OP_CUTSCENES_RESET_FOV:
25252 		case OP_CUTSCENES_RESET_CAMERA:
25253 		case OP_CUTSCENES_SHOW_SUBTITLE:
25254 		case OP_CUTSCENES_SHOW_SUBTITLE_TEXT:
25255 		case OP_CUTSCENES_SHOW_SUBTITLE_IMAGE:
25256 		case OP_CUTSCENES_SET_TIME_COMPRESSION:
25257 		case OP_CUTSCENES_RESET_TIME_COMPRESSION:
25258 		case OP_CUTSCENES_FORCE_PERSPECTIVE:
25259 		case OP_SET_CAMERA_SHUDDER:
25260 		case OP_JUMP_NODE_SET_JUMPNODE_NAME:
25261 		case OP_JUMP_NODE_SET_JUMPNODE_COLOR:
25262 		case OP_JUMP_NODE_SET_JUMPNODE_MODEL:
25263 		case OP_JUMP_NODE_SHOW_JUMPNODE:
25264 		case OP_JUMP_NODE_HIDE_JUMPNODE:
25265 		case OP_SET_OBJECT_SPEED_X:
25266 		case OP_SET_OBJECT_SPEED_Y:
25267 		case OP_SET_OBJECT_SPEED_Z:
25268 		case OP_SHIP_CREATE:
25269 		case OP_WEAPON_CREATE:
25270 		case OP_MISSION_SET_NEBULA:
25271 		case OP_ADD_BACKGROUND_BITMAP:
25272 		case OP_REMOVE_BACKGROUND_BITMAP:
25273 		case OP_ADD_SUN_BITMAP:
25274 		case OP_REMOVE_SUN_BITMAP:
25275 		case OP_NEBULA_CHANGE_STORM:
25276 		case OP_NEBULA_TOGGLE_POOF:
25277 		case OP_SET_PRIMARY_AMMO:
25278 		case OP_SET_SECONDARY_AMMO:
25279 		case OP_SET_PRIMARY_WEAPON:
25280 		case OP_SET_SECONDARY_WEAPON:
25281 		case OP_SET_NUM_COUNTERMEASURES:
25282 		case OP_SCRIPT_EVAL:
25283 		case OP_SCRIPT_EVAL_MULTI:
25284 		case OP_ENABLE_BUILTIN_MESSAGES:
25285 		case OP_DISABLE_BUILTIN_MESSAGES:
25286 		case OP_LOCK_PRIMARY_WEAPON:
25287 		case OP_UNLOCK_PRIMARY_WEAPON:
25288 		case OP_LOCK_SECONDARY_WEAPON:
25289 		case OP_UNLOCK_SECONDARY_WEAPON:
25290 		case OP_LOCK_AFTERBURNER:
25291 		case OP_UNLOCK_AFTERBURNER:
25292 		case OP_RESET_ORDERS:
25293 		case OP_SET_PERSONA:
25294 		case OP_SET_MISSION_MOOD:
25295 		case OP_CHANGE_SUBSYSTEM_NAME:
25296 		case OP_SET_RESPAWNS:
25297 		case OP_SET_AFTERBURNER_ENERGY:
25298 		case OP_SET_WEAPON_ENERGY:
25299 		case OP_SET_SHIELD_ENERGY:
25300 		case OP_SET_AMBIENT_LIGHT:
25301 		case OP_SET_POST_EFFECT:
25302 		case OP_CHANGE_IFF_COLOR:
25303 		case OP_REMOVE_WEAPONS:
25304 		case OP_MISSION_SET_SUBSPACE:
25305 		case OP_HUD_DISPLAY_GAUGE:
25306 		case OP_FORCE_GLIDE:
25307 		case OP_HUD_SET_DIRECTIVE:
25308 		case OP_HUD_GAUGE_SET_ACTIVE:
25309 		case OP_HUD_ACTIVATE_GAUGE_TYPE:
25310 		case OP_STRING_CONCATENATE:
25311 		case OP_INT_TO_STRING:
25312 		case OP_DISABLE_ETS:
25313 		case OP_ENABLE_ETS:
25314 		case OP_STRING_GET_SUBSTRING:
25315 		case OP_STRING_SET_SUBSTRING:
25316 		case OP_ADD_TO_COLGROUP:
25317 		case OP_REMOVE_FROM_COLGROUP:
25318 		case OP_SHIP_EFFECT:
25319 		case OP_CLEAR_SUBTITLES:
25320 		case OP_SET_THRUSTERS:
25321 		case OP_SET_PLAYER_THROTTLE_SPEED:
25322 		case OP_DEBUG:
25323 		case OP_HUD_SET_CUSTOM_GAUGE_ACTIVE:
25324 		case OP_HUD_SET_RETAIL_GAUGE_ACTIVE:
25325 		case OP_ALTER_SHIP_FLAG:
25326 		case OP_CHANGE_TEAM_COLOR:
25327 		case OP_NEBULA_CHANGE_PATTERN:
25328 		case OP_COPY_VARIABLE_FROM_INDEX:
25329 		case OP_COPY_VARIABLE_BETWEEN_INDEXES:
25330 		case OP_SET_ETS_VALUES:
25331 		case OP_CALL_SSM_STRIKE:
25332 		case OP_SET_MOTION_DEBRIS:
25333 			return OPR_NULL;
25334 
25335 		case OP_AI_CHASE:
25336 		case OP_AI_DOCK:
25337 		case OP_AI_UNDOCK:
25338 		case OP_AI_WARP:						// this particular operator is obsolete
25339 		case OP_AI_WARP_OUT:
25340 		case OP_AI_WAYPOINTS:
25341 		case OP_AI_WAYPOINTS_ONCE:
25342 		case OP_AI_DESTROY_SUBSYS:
25343 		case OP_AI_CHASE_WING:
25344 		case OP_AI_DISABLE_SHIP:
25345 		case OP_AI_DISARM_SHIP:
25346 		case OP_AI_GUARD:
25347 		case OP_AI_GUARD_WING:
25348 		case OP_AI_CHASE_ANY:
25349 		case OP_AI_EVADE_SHIP:
25350 		case OP_AI_STAY_NEAR_SHIP:
25351 		case OP_AI_KEEP_SAFE_DISTANCE:
25352 		case OP_AI_IGNORE:
25353 		case OP_AI_IGNORE_NEW:
25354 		case OP_AI_STAY_STILL:
25355 		case OP_AI_PLAY_DEAD:
25356 		case OP_AI_FORM_ON_WING:
25357 			return OPR_AI_GOAL;
25358 
25359 		case OP_ANY_OF:
25360 		case OP_EVERY_OF:
25361 		case OP_RANDOM_OF:
25362 		case OP_RANDOM_MULTIPLE_OF:
25363 		case OP_NUMBER_OF:
25364 		case OP_IN_SEQUENCE:
25365 		case OP_FOR_COUNTER:
25366 			return OPR_FLEXIBLE_ARGUMENT;
25367 
25368 		default:
25369 			Int3();
25370 	}
25371 
25372 	return 0;
25373 }
25374 
25375 /**
25376  * Return the data type of a specified argument to an operator.
25377  *
25378  * @param op operator index
25379  * @param argnum is 0 indexed.
25380  */
query_operator_argument_type(int op,int argnum)25381 int query_operator_argument_type(int op, int argnum)
25382 {
25383 	int index = op;
25384 
25385 	if (op < FIRST_OP)
25386 	{
25387 		Assert(index >= 0 && index < Num_operators);
25388 		op = Operators[index].value;
25389 
25390 	} else {
25391 		Warning(LOCATION, "Possible unnecessary search for operator index.  Trace out and see if this is necessary.\n");
25392 
25393 		for (index=0; index<Num_operators; index++)
25394 			if (Operators[index].value == op)
25395 				break;
25396 
25397 		Assert(index < Num_operators);
25398 	}
25399 
25400 	if (argnum >= Operators[index].max)
25401 		return OPF_NONE;
25402 
25403 	switch (op) {
25404 		case OP_TRUE:
25405 		case OP_FALSE:
25406 		case OP_MISSION_TIME:
25407 		case OP_MISSION_TIME_MSECS:
25408 		case OP_NOP:
25409 		case OP_WAYPOINT_MISSED:
25410 		case OP_WAYPOINT_TWICE:
25411 		case OP_PATH_FLOWN:
25412 		case OP_GRANT_PROMOTION:
25413 		case OP_WAS_PROMOTION_GRANTED:
25414 		case OP_RED_ALERT:
25415 		case OP_FORCE_JUMP:
25416 		case OP_RESET_ORDERS:
25417 		case OP_INVALIDATE_ALL_ARGUMENTS:
25418 		case OP_VALIDATE_ALL_ARGUMENTS:
25419 		case OP_NUM_VALID_ARGUMENTS:
25420 		case OP_SUPERNOVA_STOP:
25421 		case OP_NAV_UNSELECT:
25422 		case OP_PLAYER_IS_CHEATING_BASTARD:
25423 			return OPF_NONE;
25424 
25425 		case OP_AND:
25426 		case OP_AND_IN_SEQUENCE:
25427 		case OP_OR:
25428 		case OP_NOT:
25429 		case OP_XOR:
25430 			return OPF_BOOL;
25431 
25432 		case OP_PLUS:
25433 		case OP_MINUS:
25434 		case OP_MOD:
25435 		case OP_MUL:
25436 		case OP_DIV:
25437 		case OP_EQUALS:
25438 		case OP_GREATER_THAN:
25439 		case OP_LESS_THAN:
25440 		case OP_NOT_EQUAL:
25441 		case OP_GREATER_OR_EQUAL:
25442 		case OP_LESS_OR_EQUAL:
25443 		case OP_RAND:
25444 		case OP_RAND_MULTIPLE:
25445 		case OP_ABS:
25446 		case OP_MIN:
25447 		case OP_MAX:
25448 		case OP_AVG:
25449 			return OPF_NUMBER;
25450 
25451 		case OP_POW:
25452 			if (argnum == 0)
25453 				return OPF_NUMBER;
25454 			else
25455 				return OPF_POSITIVE;
25456 
25457 		case OP_SIGNUM:
25458 			return OPF_NUMBER;
25459 
25460 		case OP_STRING_EQUALS:
25461 		case OP_STRING_GREATER_THAN:
25462 		case OP_STRING_LESS_THAN:
25463 		case OP_STRING_TO_INT:		// Karajorma
25464 		case OP_STRING_GET_LENGTH:	// Goober5000
25465 			return OPF_STRING;
25466 
25467 		case OP_STRING_CONCATENATE:
25468 			if (argnum == 0 || argnum == 1) {
25469 				return OPF_STRING;
25470 			} else if (argnum == 2) {
25471 				return OPF_VARIABLE_NAME;
25472 			}
25473 
25474 		case OP_INT_TO_STRING:
25475 			if (argnum == 0) {
25476 				return OPF_NUMBER;
25477 			} else if (argnum == 1) {
25478 				return OPF_VARIABLE_NAME;
25479 			}
25480 
25481 		case OP_STRING_GET_SUBSTRING:
25482 			if (argnum == 0) {
25483 				return OPF_STRING;
25484 			} else if (argnum == 1 || argnum == 2) {
25485 				return OPF_POSITIVE;
25486 			} else if (argnum == 3) {
25487 				return OPF_VARIABLE_NAME;
25488 			}
25489 
25490 		case OP_STRING_SET_SUBSTRING:
25491 			if (argnum == 0) {
25492 				return OPF_STRING;
25493 			} else if (argnum == 1 || argnum == 2) {
25494 				return OPF_POSITIVE;
25495 			} else if (argnum == 3) {
25496 				return OPF_STRING;
25497 			} else if (argnum == 4) {
25498 				return OPF_VARIABLE_NAME;
25499 			}
25500 
25501 		case OP_DEBUG:
25502 			if (argnum == 0) {
25503 				return OPF_BOOL;
25504 			}else if (argnum == 1) {
25505 				return OPF_MESSAGE;
25506 			}else  {
25507 				return OPF_STRING;
25508 			}
25509 
25510 		case OP_HAS_TIME_ELAPSED:
25511 		case OP_SPEED:
25512 		case OP_SET_TRAINING_CONTEXT_SPEED:
25513 		case OP_SPECIAL_CHECK:
25514 		case OP_AI_WARP_OUT:
25515 		case OP_TEAM_SCORE:
25516 		case OP_HUD_SET_MAX_TARGETING_RANGE:
25517 		case OP_MISSION_SET_NEBULA:	//WMC
25518 		case OP_MISSION_SET_SUBSPACE:
25519 		case OP_SET_BIT:
25520 		case OP_UNSET_BIT:
25521 		case OP_IS_BIT_SET:
25522 		case OP_BITWISE_AND:
25523 		case OP_BITWISE_OR:
25524 		case OP_BITWISE_NOT:
25525 		case OP_BITWISE_XOR:
25526 			return OPF_POSITIVE;
25527 
25528 		case OP_AI_WARP:								// this operator is obsolete
25529 		case OP_SET_TRAINING_CONTEXT_FLY_PATH:
25530 			if ( !argnum )
25531 				return OPF_WAYPOINT_PATH;
25532 			else
25533 				return OPF_NUMBER;
25534 
25535 		case OP_AI_WAYPOINTS:
25536 		case OP_AI_WAYPOINTS_ONCE:
25537 			if ( argnum == 0 )
25538 				return OPF_WAYPOINT_PATH;
25539 			else
25540 				return OPF_POSITIVE;
25541 
25542 		case OP_TURRET_PROTECT_SHIP:
25543 		case OP_TURRET_UNPROTECT_SHIP:
25544 			if (argnum == 0)
25545 				return OPF_STRING;
25546 			else
25547 				return OPF_SHIP;
25548 
25549 		case OP_IS_DISABLED:
25550 		case OP_IS_DISARMED:
25551 		case OP_TIME_SHIP_DESTROYED:
25552 		case OP_TIME_SHIP_ARRIVED:
25553 		case OP_TIME_SHIP_DEPARTED:
25554 		case OP_AFTERBURNER_LEFT:
25555 		case OP_WEAPON_ENERGY_LEFT:
25556 		case OP_SHIELDS_LEFT:
25557 		case OP_HITS_LEFT:
25558 		case OP_SIM_HITS_LEFT:
25559 		case OP_CLEAR_SHIP_GOALS:
25560 		case OP_PROTECT_SHIP:
25561 		case OP_UNPROTECT_SHIP:
25562 		case OP_BEAM_PROTECT_SHIP:
25563 		case OP_BEAM_UNPROTECT_SHIP:
25564 		case OP_TRANSFER_CARGO:
25565 		case OP_EXCHANGE_CARGO:
25566 		case OP_SHIP_INVISIBLE:
25567 		case OP_SHIP_VISIBLE:
25568 		case OP_SHIP_INVULNERABLE:
25569 		case OP_SHIP_VULNERABLE:
25570 		case OP_SHIP_BOMB_TARGETABLE:
25571 		case OP_SHIP_BOMB_UNTARGETABLE:
25572 		case OP_SHIP_GUARDIAN:
25573 		case OP_SHIP_NO_GUARDIAN:
25574 		case OP_SHIP_VANISH:
25575 		case OP_DESTROY_INSTANTLY:
25576 		case OP_SHIELDS_ON:
25577 		case OP_SHIELDS_OFF:
25578 		case OP_SHIP_STEALTHY:
25579 		case OP_SHIP_UNSTEALTHY:
25580 		case OP_FRIENDLY_STEALTH_INVISIBLE:
25581 		case OP_FRIENDLY_STEALTH_VISIBLE:
25582 		case OP_PRIMARIES_DEPLETED:
25583 		case OP_SECONDARIES_DEPLETED:
25584 		case OP_SPECIAL_WARP_DISTANCE:
25585 		case OP_SET_SPECIAL_WARPOUT_NAME:
25586 		case OP_IS_SHIP_VISIBLE:
25587 		case OP_IS_SHIP_STEALTHY:
25588 		case OP_IS_FRIENDLY_STEALTH_VISIBLE:
25589 		case OP_GET_DAMAGE_CAUSED:
25590 		case OP_GET_THROTTLE_SPEED:
25591 			return OPF_SHIP;
25592 
25593 		case OP_ALTER_SHIP_FLAG:
25594 			if(argnum == 0)
25595 				return OPF_SHIP_FLAG;
25596 			if(argnum == 1 || argnum == 2)
25597 				return OPF_BOOL;
25598 			else
25599 				return OPF_SHIP_WING_WHOLETEAM;
25600 
25601 		case OP_SET_PLAYER_THROTTLE_SPEED:
25602 			if(argnum == 0)
25603 				return OPF_SHIP;
25604 			else
25605 				return OPF_POSITIVE;
25606 
25607 		case OP_SHIP_CREATE:
25608 			if(argnum == 0)
25609 				return OPF_STRING;
25610 			else if(argnum == 1)
25611 				return OPF_SHIP_CLASS_NAME;
25612 			else
25613 				return OPF_NUMBER;
25614 
25615 		case OP_WEAPON_CREATE:
25616 			if (argnum == 0)
25617 				return OPF_SHIP_OR_NONE;
25618 			else if (argnum == 1)
25619 				return OPF_WEAPON_NAME;
25620 			else if (argnum == 8)
25621 				return OPF_SHIP;
25622 			else if (argnum == 9)
25623 				return OPF_SUBSYSTEM;
25624 			else
25625 				return OPF_NUMBER;
25626 
25627 		case OP_REMOVE_WEAPONS:
25628 			return OPF_WEAPON_NAME;
25629 
25630 		case OP_SHIP_GUARDIAN_THRESHOLD:
25631 			if (argnum == 0)
25632 				return OPF_POSITIVE;
25633 			else
25634 				return OPF_SHIP;
25635 
25636 		case OP_SHIP_SUBSYS_GUARDIAN_THRESHOLD:
25637 			if (argnum == 0)
25638 				return OPF_POSITIVE;
25639 			else if (argnum == 1)
25640 				return OPF_SHIP;
25641 			else
25642 				return OPF_SUBSYS_OR_GENERIC;
25643 
25644 		case OP_SHIP_SUBSYS_TARGETABLE:
25645 		case OP_SHIP_SUBSYS_UNTARGETABLE:
25646 			if (argnum == 0)
25647 				return OPF_SHIP;
25648 			else
25649 				return OPF_SUBSYS_OR_GENERIC;
25650 
25651 		case OP_SHIP_SUBSYS_NO_REPLACE:
25652 		case OP_SHIP_SUBSYS_NO_LIVE_DEBRIS:
25653 		case OP_SHIP_SUBSYS_VANISHED:
25654 		case OP_SHIP_SUBSYS_IGNORE_IF_DEAD:
25655 			if (argnum == 0)
25656 				return OPF_SHIP;
25657 			else if (argnum == 1)
25658 				return OPF_BOOL;
25659 			else
25660 				return OPF_SUBSYS_OR_GENERIC;
25661 
25662 		case OP_IS_DESTROYED:
25663 		case OP_HAS_ARRIVED:
25664 		case OP_HAS_DEPARTED:
25665 		case OP_CLEAR_GOALS:
25666 			return OPF_SHIP_WING;
25667 
25668 		case OP_IS_DISABLED_DELAY:
25669 		case OP_IS_DISARMED_DELAY:
25670 			if ( argnum == 0 )
25671 				return OPF_POSITIVE;
25672 			else
25673 				return OPF_SHIP;
25674 
25675 		case OP_SHIP_TAG:
25676 			if (argnum == 0)
25677 				return OPF_SHIP;
25678             else if (argnum == 7)
25679                 return OPF_IFF;
25680 			else
25681 				return OPF_POSITIVE;
25682 
25683 		case OP_SHIP_UNTAG:
25684 			return OPF_SHIP;
25685 
25686 		case OP_FACING:
25687 			if (argnum == 0)
25688 				return OPF_SHIP;
25689 			else
25690 				return OPF_POSITIVE;
25691 
25692 		case OP_FACING2:
25693 			if (argnum == 0) {
25694 				return OPF_WAYPOINT_PATH;
25695 			} else {
25696 				return OPF_POSITIVE;
25697 			}
25698 
25699 		case OP_ORDER:
25700 			if (argnum == 1)
25701 				return OPF_AI_ORDER;
25702 			else
25703 				return OPF_SHIP_WING;	// arg 0 or 2
25704 
25705 		case OP_QUERY_ORDERS:
25706 			if (argnum == 0)
25707 				return OPF_ORDER_RECIPIENT;
25708 			if (argnum == 1)
25709 				return OPF_AI_ORDER;
25710 			if (argnum == 2)
25711 				return OPF_POSITIVE;
25712 			if (argnum == 5)
25713 				return OPF_SUBSYSTEM;
25714 			else
25715 				return OPF_SHIP_WING;
25716 
25717 		case OP_WAS_DESTROYED_BY_DELAY:
25718 			if (argnum == 0)
25719 				return OPF_POSITIVE;
25720 			else
25721 				return OPF_SHIP;
25722 
25723 		case OP_IS_DESTROYED_DELAY:
25724 		case OP_HAS_ARRIVED_DELAY:
25725 		case OP_HAS_DEPARTED_DELAY:
25726 		case OP_LAST_ORDER_TIME:
25727 			if ( argnum == 0 )
25728 				return OPF_POSITIVE;
25729 			else
25730 				return OPF_SHIP_WING;
25731 
25732 		case OP_SHIP_CHANGE_ALT_NAME:
25733 			if (argnum == 0)
25734 				return OPF_STRING;
25735 			else
25736 				return OPF_SHIP_WING;
25737 
25738 		case OP_SHIP_CHANGE_CALLSIGN:
25739 			if (argnum == 0)
25740 				return OPF_STRING;
25741 			else
25742 				return OPF_SHIP;
25743 
25744 		case OP_SET_DEATH_MESSAGE:
25745 			return OPF_MESSAGE_OR_STRING;
25746 
25747 		case OP_DISTANCE:
25748 			return OPF_SHIP_WING_SHIPONTEAM_POINT;
25749 
25750 		case OP_SET_OBJECT_SPEED_X:
25751 		case OP_SET_OBJECT_SPEED_Y:
25752 		case OP_SET_OBJECT_SPEED_Z:
25753 			if (argnum == 0)
25754 				return OPF_SHIP_WING;
25755 			else if (argnum == 1)
25756 				return OPF_NUMBER;
25757 			else
25758 				return OPF_BOOL;
25759 
25760 		case OP_GET_OBJECT_SPEED_X:
25761 		case OP_GET_OBJECT_SPEED_Y:
25762 		case OP_GET_OBJECT_SPEED_Z:
25763 			if (argnum == 0)
25764 				return OPF_SHIP_WING;
25765 			else
25766 				return OPF_BOOL;
25767 
25768 		case OP_GET_OBJECT_X:
25769 		case OP_GET_OBJECT_Y:
25770 		case OP_GET_OBJECT_Z:
25771 			if (argnum == 0)
25772 				return OPF_SHIP_WING_POINT;
25773 			else if (argnum == 1)
25774 				return OPF_SUBSYSTEM_OR_NONE;
25775 			else
25776 				return OPF_NUMBER;
25777 
25778 		case OP_GET_OBJECT_PITCH:
25779 		case OP_GET_OBJECT_BANK:
25780 		case OP_GET_OBJECT_HEADING:
25781 			return OPF_SHIP_WING;
25782 
25783 		case OP_SET_OBJECT_POSITION:
25784 			if(argnum == 0)
25785 				return OPF_SHIP_WING_POINT;
25786 			else
25787 				return OPF_NUMBER;
25788 
25789 		case OP_SET_OBJECT_ORIENTATION:
25790 			if (argnum == 0)
25791 				return OPF_SHIP_WING;
25792 			else
25793 				return OPF_NUMBER;
25794 
25795 		case OP_SET_OBJECT_FACING:
25796 			if (argnum == 0)
25797 				return OPF_SHIP_WING;
25798 			else if (argnum < 4)
25799 				return OPF_NUMBER;
25800 			else
25801 				return OPF_POSITIVE;
25802 
25803 		case OP_SET_OBJECT_FACING_OBJECT:
25804 			if (argnum == 0)
25805 				return OPF_SHIP_WING;
25806 			else if (argnum == 1)
25807 				return OPF_SHIP_WING_POINT;
25808 			else
25809 				return OPF_POSITIVE;
25810 
25811 		case OP_SHIP_MANEUVER:
25812 			if (argnum == 0)
25813 				return OPF_SHIP_WING;
25814 			else if (argnum == 1)
25815 				return OPF_POSITIVE;
25816 			else if (argnum < 5)
25817 				return OPF_NUMBER;
25818 			else if (argnum == 5)
25819 				return OPF_BOOL;
25820 			else if (argnum < 9)
25821 				return OPF_NUMBER;
25822 			else
25823 				return OPF_BOOL;
25824 
25825 		case OP_SHIP_ROT_MANEUVER:
25826 		case OP_SHIP_LAT_MANEUVER:
25827 			if (argnum == 0)
25828 				return OPF_SHIP_WING;
25829 			else if (argnum == 1)
25830 				return OPF_POSITIVE;
25831 			else if (argnum < 5)
25832 				return OPF_NUMBER;
25833 			else
25834 				return OPF_BOOL;
25835 
25836 		case OP_MODIFY_VARIABLE:
25837 			if (argnum == 0) {
25838 				return OPF_VARIABLE_NAME;
25839 			} else {
25840 				return OPF_AMBIGUOUS;
25841 			}
25842 
25843 		case OP_GET_VARIABLE_BY_INDEX:
25844 		case OP_COPY_VARIABLE_BETWEEN_INDEXES:
25845 			return OPF_POSITIVE;
25846 
25847 		case OP_COPY_VARIABLE_FROM_INDEX:
25848 			if (argnum == 0) {
25849 				return OPF_POSITIVE;
25850 			} else {
25851 				return OPF_VARIABLE_NAME;
25852 			}
25853 
25854 		case OP_SET_VARIABLE_BY_INDEX:
25855 			if (argnum == 0) {
25856 				return OPF_POSITIVE;
25857 			} else {
25858 				return OPF_AMBIGUOUS;
25859 			}
25860 
25861 		case OP_HAS_DOCKED:
25862 		case OP_HAS_UNDOCKED:
25863 		case OP_HAS_DOCKED_DELAY:
25864 		case OP_HAS_UNDOCKED_DELAY:
25865 		case OP_TIME_DOCKED:
25866 		case OP_TIME_UNDOCKED:
25867 			if ( argnum < 2 )
25868 				return OPF_SHIP;
25869 			else
25870 				return OPF_POSITIVE;
25871 
25872 		case OP_TIME_WING_DESTROYED:
25873 		case OP_TIME_WING_ARRIVED:
25874 		case OP_TIME_WING_DEPARTED:
25875 		case OP_CLEAR_WING_GOALS:
25876 			return OPF_WING;
25877 
25878 		case OP_SET_SCANNED:
25879 		case OP_SET_UNSCANNED:
25880 		case OP_IS_SUBSYSTEM_DESTROYED:
25881 			if (!argnum)
25882 				return OPF_SHIP;
25883 			else
25884 				return OPF_SUBSYSTEM;
25885 
25886 		case OP_HITS_LEFT_SUBSYSTEM:
25887 			if (argnum == 0)
25888 				return OPF_SHIP;
25889 			else if (argnum == 1)
25890 				return OPF_SUBSYSTEM;
25891 			else
25892 				return OPF_BOOL;
25893 
25894 		case OP_HITS_LEFT_SUBSYSTEM_GENERIC:
25895 			if (argnum == 0)
25896 				return OPF_SHIP;
25897 			else
25898 				return OPF_SUBSYSTEM_TYPE;
25899 
25900 		case OP_HITS_LEFT_SUBSYSTEM_SPECIFIC:
25901 			if (argnum == 0)
25902 				return OPF_SHIP;
25903 			else if (argnum == 1)
25904 				return OPF_SUBSYSTEM;
25905 
25906 		case OP_DISTANCE_SUBSYSTEM:
25907 			if (argnum == 0)
25908 				return OPF_SHIP_WING_SHIPONTEAM_POINT;
25909 			else if (argnum == 1)
25910 				return OPF_SHIP;
25911 			else if (argnum == 2)
25912 				return OPF_SUBSYSTEM;
25913 			else
25914 				Int3();		// shouldn't happen
25915 
25916 		case OP_NUM_WITHIN_BOX:
25917 			if(argnum < 3)
25918 				return OPF_NUMBER;
25919 			else if(argnum < 6)
25920 				return OPF_POSITIVE;
25921 			else
25922 				return OPF_SHIP_WING;
25923 
25924 		case OP_IS_IN_BOX:
25925 			if (argnum == 0) // First arg is a ship/wing/point
25926 				return OPF_SHIP_WING_POINT;
25927 			else if (argnum <= 6) // Next 6 args are coordinates
25928 				return OPF_NUMBER;
25929 			else // Next arg is a ship
25930 				return OPF_SHIP;
25931 
25932 		case OP_IS_IN_MISSION:
25933 			return OPF_STRING;
25934 
25935 		// Sesquipedalian
25936 		case OP_MISSILE_LOCKED:
25937 			if (argnum == 0)
25938 				return OPF_POSITIVE;
25939 			else if (argnum == 1)
25940 				return OPF_SHIP;
25941 			else
25942 				return OPF_SUBSYSTEM;
25943 
25944 		case OP_TARGETED:
25945 			if (!argnum)
25946 				return OPF_SHIP;
25947 			else if (argnum == 1)
25948 				return OPF_POSITIVE;
25949 			else
25950 				return OPF_SUBSYSTEM;
25951 
25952 		case OP_NODE_TARGETED:
25953 			if (!argnum)
25954 				return OPF_JUMP_NODE_NAME;
25955 			else if (argnum == 1)
25956 				return OPF_POSITIVE;
25957 
25958 		case OP_IS_SUBSYSTEM_DESTROYED_DELAY:
25959 			if ( argnum == 0 )
25960 				return OPF_SHIP;
25961 			else if ( argnum == 1 )
25962 				return OPF_SUBSYSTEM;
25963 			else
25964 				return OPF_POSITIVE;
25965 
25966 		case OP_IS_IFF:
25967 		case OP_CHANGE_IFF:
25968 			if (!argnum)
25969 				return OPF_IFF;
25970 			else
25971 				return OPF_SHIP_WING;
25972 
25973 		case OP_ADD_SHIP_GOAL:
25974 			if (!argnum)
25975 				return OPF_SHIP;
25976 			else
25977 				return OPF_AI_GOAL;
25978 
25979 		case OP_ADD_WING_GOAL:
25980 			if (!argnum)
25981 				return OPF_WING;
25982 			else
25983 				return OPF_AI_GOAL;
25984 
25985 		case OP_ADD_GOAL:
25986 		case OP_REMOVE_GOAL:
25987 			if ( argnum == 0 )
25988 				return OPF_SHIP_WING;
25989 			else
25990 				return OPF_AI_GOAL;
25991 
25992 		case OP_COND:
25993 		case OP_WHEN:
25994 		case OP_EVERY_TIME:
25995 		case OP_IF_THEN_ELSE:
25996 		case OP_PERFORM_ACTIONS:
25997 			if (!argnum)
25998 				return OPF_BOOL;
25999 			else
26000 				return OPF_NULL;
26001 
26002 		case OP_WHEN_ARGUMENT:
26003 		case OP_EVERY_TIME_ARGUMENT:
26004 			if (argnum == 0)
26005 				return OPF_FLEXIBLE_ARGUMENT;
26006 			else if (argnum == 1)
26007 				return OPF_BOOL;
26008 			else
26009 				return OPF_NULL;
26010 
26011 		case OP_DO_FOR_VALID_ARGUMENTS:
26012 			return OPF_NULL;
26013 
26014 		case OP_ANY_OF:
26015 		case OP_EVERY_OF:
26016 		case OP_RANDOM_OF:
26017 		case OP_RANDOM_MULTIPLE_OF:
26018 		case OP_IN_SEQUENCE:
26019 			return OPF_ANYTHING;
26020 
26021 		case OP_NUMBER_OF:
26022 			if (argnum == 0)
26023 				return OPF_POSITIVE;
26024 			else
26025 				return OPF_ANYTHING;
26026 
26027 		case OP_FOR_COUNTER:
26028 			return OPF_NUMBER;
26029 
26030 		case OP_INVALIDATE_ARGUMENT:
26031 		case OP_VALIDATE_ARGUMENT:
26032 			return OPF_ANYTHING;
26033 
26034 		case OP_AI_DISABLE_SHIP:
26035 		case OP_AI_DISARM_SHIP:
26036 		case OP_AI_EVADE_SHIP:
26037 		case OP_AI_STAY_NEAR_SHIP:
26038 		case OP_AI_IGNORE:
26039 		case OP_AI_IGNORE_NEW:
26040 			if (!argnum)
26041 				return OPF_SHIP;
26042 			else
26043 				return OPF_POSITIVE;
26044 
26045 		case OP_AI_CHASE:
26046 		case OP_AI_GUARD:
26047 			if (!argnum)
26048 				return OPF_SHIP_WING;
26049 			else
26050 				return OPF_POSITIVE;
26051 
26052 		case OP_AI_KEEP_SAFE_DISTANCE:
26053 			return OPF_POSITIVE;
26054 
26055 		case OP_AI_DOCK:
26056 			if (!argnum)
26057 				return OPF_SHIP;
26058 			else if (argnum == 1)
26059 				return OPF_DOCKER_POINT;
26060 			else if (argnum == 2)
26061 				return OPF_DOCKEE_POINT;
26062 			else
26063 				return OPF_POSITIVE;
26064 
26065 		case OP_AI_UNDOCK:
26066 			if (argnum == 0)
26067 				return OPF_POSITIVE;
26068 			else
26069 				return OPF_SHIP;
26070 
26071 		case OP_AI_CHASE_WING:
26072 		case OP_AI_GUARD_WING:
26073 			if (!argnum)
26074 				return OPF_WING;
26075 			else
26076 				return OPF_POSITIVE;
26077 
26078 		case OP_AI_DESTROY_SUBSYS:
26079 			if (!argnum)
26080 				return OPF_SHIP;
26081 			else if (argnum == 1)
26082 				return OPF_SUBSYSTEM;
26083 			else
26084 				return OPF_POSITIVE;
26085 
26086 		case OP_GOALS_ID:
26087 			return OPF_AI_GOAL;
26088 
26089 		case OP_SET_CARGO:
26090 		case OP_IS_CARGO:
26091 			if (argnum == 0)
26092 				return OPF_CARGO;
26093 			else if (argnum == 1)
26094 				return OPF_SHIP;
26095 			else
26096 				return OPF_SUBSYSTEM;
26097 
26098 		case OP_CHANGE_AI_CLASS:
26099 		case OP_IS_AI_CLASS:
26100 			if (argnum == 0)
26101 				return OPF_AI_CLASS;
26102 			else if (argnum == 1)
26103 				return OPF_SHIP;
26104 			else
26105 				return OPF_SUBSYSTEM;
26106 
26107 		case OP_IS_SHIP_TYPE:
26108 			if (argnum == 0)
26109 				return OPF_SHIP_TYPE;
26110 			else
26111 				return OPF_SHIP;
26112 
26113 		case OP_IS_SHIP_CLASS:
26114 			if (argnum == 0)
26115 				return OPF_SHIP_CLASS_NAME;
26116 			else
26117 				return OPF_SHIP;
26118 
26119 		case OP_CHANGE_SOUNDTRACK:
26120 			return OPF_SOUNDTRACK_NAME;
26121 
26122 		case OP_PLAY_SOUND_FROM_TABLE:
26123 			return OPF_POSITIVE;
26124 
26125 		case OP_PLAY_SOUND_FROM_FILE:
26126 			if (argnum==0)
26127 				return OPF_STRING;
26128 			else
26129 				return OPF_POSITIVE;
26130 
26131 		case OP_CLOSE_SOUND_FROM_FILE:
26132 		case OP_ALLOW_TREASON:
26133 		case OP_END_MISSION:
26134 		case OP_SET_DEBRIEFING_TOGGLED:
26135 			return OPF_BOOL;
26136 
26137 		case OP_SET_PLAYER_ORDERS:
26138 			if (argnum==0)
26139 				return OPF_SHIP;
26140 			if (argnum==1)
26141 				return OPF_BOOL;
26142 			else
26143 				return OPF_AI_ORDER;
26144 
26145 		case OP_SET_SOUND_ENVIRONMENT:
26146 			if (argnum == 0)
26147 				return OPF_SOUND_ENVIRONMENT;
26148 
26149 			// fall through
26150 			argnum--;
26151 
26152 		case OP_UPDATE_SOUND_ENVIRONMENT:
26153 		{
26154 			// every two, the value repeats
26155 			int a_mod = argnum % 2;
26156 
26157 			if (a_mod == 0)
26158 				return OPF_SOUND_ENVIRONMENT_OPTION;
26159 			else
26160 				return OPF_POSITIVE;
26161 		}
26162 
26163 		case OP_ADJUST_AUDIO_VOLUME:
26164 		{
26165 			if (argnum == 0)
26166 				return OPF_AUDIO_VOLUME_OPTION;
26167 			else
26168 				return OPF_POSITIVE;
26169 		}
26170 
26171 		case OP_SET_EXPLOSION_OPTION:
26172 		{
26173 			// editing a ship
26174 			if (argnum == 0)
26175 				return OPF_SHIP;
26176 
26177 			// every two, the value repeats
26178 			int a_mod = (argnum - 1) % 2;
26179 
26180 			if (a_mod == 0)
26181 				return OPF_EXPLOSION_OPTION;
26182 			else
26183 				return OPF_POSITIVE;
26184 		}
26185 
26186 		case OP_HUD_DISABLE:
26187 		case OP_HUD_DISABLE_EXCEPT_MESSAGES:
26188 			return OPF_POSITIVE;
26189 
26190 		case OP_HUD_SET_TEXT:
26191 			return OPF_STRING;
26192 
26193 		case OP_HUD_SET_MESSAGE:
26194 			if(argnum == 0)
26195 				return OPF_STRING;
26196 			else
26197 				return OPF_MESSAGE;
26198 
26199 		case OP_HUD_SET_TEXT_NUM:
26200 		case OP_HUD_SET_COORDS:
26201 		case OP_HUD_SET_FRAME:
26202 		case OP_HUD_SET_COLOR:
26203 			if(argnum == 0)
26204 				return OPF_STRING;
26205 			else
26206 				return OPF_POSITIVE;
26207 
26208 		case OP_HUD_CLEAR_MESSAGES:
26209 			return OPF_NONE;
26210 
26211 		case OP_PLAYER_USE_AI:
26212 		case OP_PLAYER_NOT_USE_AI:
26213 			return OPF_NONE;
26214 
26215 		case OP_EXPLOSION_EFFECT:
26216 			if (argnum <= 2)
26217 				return OPF_NUMBER;
26218 			else
26219 				return OPF_POSITIVE;
26220 
26221 		case OP_WARP_EFFECT:
26222 			if (argnum <= 5)
26223 				return OPF_NUMBER;
26224 			else
26225 				return OPF_POSITIVE;
26226 
26227 		case OP_SEND_MESSAGE:
26228 		case OP_SEND_RANDOM_MESSAGE:
26229 			if ( argnum == 0 )
26230 				return OPF_WHO_FROM;
26231 			else if ( argnum == 1 )
26232 				return OPF_PRIORITY;
26233 			else
26234 				return OPF_MESSAGE;
26235 
26236 		case OP_SEND_MESSAGE_LIST:
26237 		{
26238 			// every four, the value repeats
26239 			int a_mod = argnum % 4;
26240 
26241 			// who from
26242 			if(a_mod == 0)
26243 				return OPF_WHO_FROM;
26244 			else if(a_mod == 1)
26245 				return OPF_PRIORITY;
26246 			else if(a_mod == 2)
26247 				return OPF_MESSAGE;
26248 			else if(a_mod == 3)
26249 				return OPF_POSITIVE;
26250 		}
26251 
26252 		case OP_TRAINING_MSG:
26253 			if (argnum < 2)
26254 				return OPF_MESSAGE;
26255 			else
26256 				return OPF_POSITIVE;
26257 
26258 		// Karajorma
26259 		case OP_ENABLE_BUILTIN_MESSAGES:
26260 		case OP_DISABLE_BUILTIN_MESSAGES:
26261 				return OPF_WHO_FROM;
26262 
26263 		case OP_SET_PERSONA:
26264 			if (argnum == 0)
26265 				return OPF_PERSONA;
26266 			else
26267 				return OPF_SHIP;
26268 
26269 		case OP_SET_MISSION_MOOD:
26270 			return OPF_MISSION_MOOD;
26271 
26272 		case OP_CHANGE_TEAM_COLOR:
26273 			if (argnum == 0)
26274 				return OPF_TEAM_COLOR;
26275 			else if (argnum == 1)
26276 				return OPF_NUMBER;
26277 			else
26278 				return OPF_SHIP;
26279 
26280 		case OP_CALL_SSM_STRIKE:
26281 			if (argnum == 0)
26282 				return OPF_NUMBER;
26283 			else if (argnum == 1)
26284 				return OPF_IFF;
26285 			else
26286 				return OPF_SHIP;
26287 
26288 		case OP_SELF_DESTRUCT:
26289 			return OPF_SHIP;
26290 
26291 		case OP_NEXT_MISSION:
26292 			return OPF_MISSION_NAME;
26293 
26294 		case OP_END_CAMPAIGN:
26295 		case OP_END_OF_CAMPAIGN:
26296 			return OPF_NONE;
26297 
26298 		case OP_PREVIOUS_GOAL_TRUE:
26299 		case OP_PREVIOUS_GOAL_FALSE:
26300 			if ( argnum == 0 )
26301 				return OPF_MISSION_NAME;
26302 			else if (argnum == 1 )
26303 				return OPF_GOAL_NAME;
26304 			else
26305 				return OPF_BOOL;
26306 
26307 		case OP_PREVIOUS_GOAL_INCOMPLETE:
26308 			return OPF_GOAL_NAME;
26309 
26310 		case OP_PREVIOUS_EVENT_TRUE:
26311 		case OP_PREVIOUS_EVENT_FALSE:
26312 		case OP_PREVIOUS_EVENT_INCOMPLETE:
26313 			if (!argnum)
26314 				return OPF_MISSION_NAME;
26315 			else if ( argnum == 1 )
26316 				return OPF_EVENT_NAME;
26317 			else
26318 				return OPF_BOOL;
26319 
26320 		case OP_SABOTAGE_SUBSYSTEM:
26321 			if (!argnum)
26322 				return OPF_SHIP;		// changed from OPF_SHIP_NOT_PLAYER by Goober5000: now it can be the player ship also
26323 			else if (argnum == 1 )
26324 				return OPF_SUBSYS_OR_GENERIC;
26325 			else
26326 				return OPF_POSITIVE;
26327 
26328 		case OP_REPAIR_SUBSYSTEM:
26329 		case OP_SET_SUBSYSTEM_STRNGTH:
26330 			if (!argnum)
26331 				return OPF_SHIP;		// changed from OPF_SHIP_NOT_PLAYER by Goober5000: now it can be the player ship also
26332 			else if (argnum == 1 )
26333 				return OPF_SUBSYS_OR_GENERIC;
26334 			else if (argnum == 2)
26335 				return OPF_POSITIVE;
26336 			else
26337 				return OPF_BOOL;
26338 
26339 		case OP_DESTROY_SUBSYS_INSTANTLY:
26340 			if (argnum == 0)
26341 				return OPF_SHIP;
26342 			else
26343 				return OPF_SUBSYS_OR_GENERIC;
26344 
26345 		case OP_WAYPOINTS_DONE:
26346 			if ( argnum == 0 )
26347 				return OPF_SHIP_WING;
26348 			else
26349 				return OPF_WAYPOINT_PATH;
26350 
26351 		case OP_WAYPOINTS_DONE_DELAY:
26352 			if ( argnum == 0 )
26353 				return OPF_SHIP_WING;
26354 			else if ( argnum == 1 )
26355 				return OPF_WAYPOINT_PATH;
26356 			else
26357 				return OPF_POSITIVE;
26358 
26359 		case OP_INVALIDATE_GOAL:
26360 		case OP_VALIDATE_GOAL:
26361 			return OPF_GOAL_NAME;
26362 
26363 		case OP_SHIP_TYPE_DESTROYED:
26364 			if ( argnum == 0 )
26365 				return OPF_POSITIVE;
26366 			else
26367 				return OPF_SHIP_TYPE;
26368 
26369 		case OP_KEY_PRESSED:
26370 			if (!argnum)
26371 				return OPF_KEYPRESS;
26372 			else
26373 				return OPF_POSITIVE;
26374 
26375 		case OP_KEY_RESET:
26376 		case OP_KEY_RESET_MULTIPLE:
26377 			return OPF_KEYPRESS;
26378 
26379 		case OP_EVENT_TRUE:
26380 		case OP_EVENT_FALSE:
26381 			return OPF_EVENT_NAME;
26382 
26383 		case OP_EVENT_INCOMPLETE:
26384 		case OP_EVENT_TRUE_DELAY:
26385 		case OP_EVENT_FALSE_DELAY:
26386 		case OP_EVENT_TRUE_MSECS_DELAY:
26387 		case OP_EVENT_FALSE_MSECS_DELAY:
26388 			if (argnum == 0)
26389 				return OPF_EVENT_NAME;
26390 			else if (argnum == 1)
26391 				return OPF_POSITIVE;
26392 			else if (argnum == 2)
26393 				return OPF_BOOL;
26394 
26395 		case OP_GOAL_INCOMPLETE:
26396 		case OP_GOAL_TRUE_DELAY:
26397 		case OP_GOAL_FALSE_DELAY:
26398 			if (!argnum)
26399 				return OPF_GOAL_NAME;
26400 			else
26401 				return OPF_POSITIVE;
26402 
26403 		case OP_AI_PLAY_DEAD:
26404 		case OP_AI_CHASE_ANY:
26405 			return OPF_POSITIVE;
26406 
26407 		case OP_AI_STAY_STILL:
26408 			if (!argnum)
26409 				return OPF_SHIP_POINT;
26410 			else
26411 				return OPF_POSITIVE;
26412 
26413 		case OP_AI_FORM_ON_WING:
26414 			return OPF_SHIP;
26415 
26416 		case OP_GOOD_REARM_TIME:
26417 			if ( argnum == 0 )
26418 				return OPF_IFF;
26419 			else
26420 				return OPF_POSITIVE;
26421 
26422 		case OP_NUM_PLAYERS:
26423 			return OPF_POSITIVE;
26424 
26425 		case OP_SKILL_LEVEL_AT_LEAST:
26426 			return OPF_SKILL_LEVEL;
26427 
26428 		case OP_GRANT_MEDAL:
26429 		case OP_WAS_MEDAL_GRANTED:
26430 			return OPF_MEDAL_NAME;
26431 
26432 		case OP_IS_CARGO_KNOWN:
26433 			return OPF_SHIP;
26434 
26435 		case OP_CARGO_KNOWN_DELAY:
26436 			if ( argnum == 0 )
26437 				return OPF_POSITIVE;
26438 			else
26439 				return OPF_SHIP;
26440 
26441 		case OP_HAS_BEEN_TAGGED_DELAY:
26442 			if ( argnum == 0 ) {
26443 				return OPF_POSITIVE;
26444 			} else {
26445 				return OPF_SHIP;
26446 			}
26447 
26448 		case OP_ARE_SHIP_FLAGS_SET:
26449 			if (argnum == 0) {
26450 				return OPF_SHIP;
26451 			} else {
26452 				return OPF_SHIP_FLAG;
26453 			}
26454 
26455 		case OP_CAP_SUBSYS_CARGO_KNOWN_DELAY:
26456 			if ( argnum == 0 ) {
26457 				return OPF_POSITIVE;
26458 			} else if ( argnum == 1 ) {
26459 				return OPF_SHIP;
26460 			} else {
26461 				return OPF_SUBSYSTEM;
26462 			}
26463 
26464 		case OP_ALLOW_SHIP:
26465 		case OP_TECH_ADD_SHIP:
26466 			return OPF_SHIP_CLASS_NAME;
26467 
26468 		case OP_ALLOW_WEAPON:
26469 		case OP_TECH_ADD_WEAPON:
26470 			return OPF_WEAPON_NAME;
26471 
26472 		case OP_TECH_ADD_INTEL:
26473 			return OPF_INTEL_NAME;
26474 
26475 		case OP_TECH_ADD_INTEL_XSTR:
26476 			return !(argnum % 2) ? OPF_INTEL_NAME : OPF_NUMBER;
26477 
26478 		case OP_TECH_RESET_TO_DEFAULT:
26479 			return OPF_NONE;
26480 
26481 		case OP_CHANGE_PLAYER_SCORE:
26482 			if (argnum == 0)
26483 				return OPF_NUMBER;
26484 			else
26485 				return OPF_SHIP;
26486 
26487 		case OP_CHANGE_TEAM_SCORE:
26488 			return OPF_NUMBER;
26489 
26490 		case OP_SHIP_VAPORIZE:
26491 		case OP_SHIP_NO_VAPORIZE:
26492 			return OPF_SHIP;
26493 
26494 		case OP_DONT_COLLIDE_INVISIBLE:
26495 		case OP_COLLIDE_INVISIBLE:
26496 			return OPF_SHIP;
26497 
26498 		case OP_SET_MOBILE:
26499 		case OP_SET_IMMOBILE:
26500 			return OPF_SHIP;
26501 
26502 		case OP_IGNORE_KEY:
26503 			if (argnum == 0)
26504 				return OPF_NUMBER;
26505 			else
26506 				return OPF_KEYPRESS;
26507 
26508 
26509 		case OP_WARP_BROKEN:
26510 		case OP_WARP_NOT_BROKEN:
26511 		case OP_WARP_NEVER:
26512 		case OP_WARP_ALLOWED:
26513 			return OPF_SHIP;
26514 
26515 		case OP_SET_SUBSPACE_DRIVE:
26516 			if (argnum == 0)
26517 				return OPF_BOOL;
26518 			else
26519 				return OPF_SHIP;
26520 
26521 		case OP_FLASH_HUD_GAUGE:
26522 			return OPF_HUD_GAUGE_NAME;
26523 
26524 		case OP_GOOD_SECONDARY_TIME:
26525 			if ( argnum == 0 )
26526 				return OPF_IFF;
26527 			else if ( argnum == 1 )
26528 				return OPF_POSITIVE;
26529 			else if ( argnum == 2 )
26530 				return OPF_HUGE_WEAPON;
26531 			else
26532 				return OPF_SHIP;
26533 
26534 		case OP_PERCENT_SHIPS_ARRIVED:
26535 		case OP_PERCENT_SHIPS_DEPARTED:
26536 		case OP_PERCENT_SHIPS_DESTROYED:
26537 			if ( argnum == 0 ){
26538 				return OPF_POSITIVE;
26539 			} else {
26540 				return OPF_SHIP_WING;
26541 			}
26542 			break;
26543 
26544 		case OP_PERCENT_SHIPS_DISARMED:
26545 		case OP_PERCENT_SHIPS_DISABLED:
26546 			if ( argnum == 0 ){
26547 				return OPF_POSITIVE;
26548 			} else {
26549 				return OPF_SHIP;
26550 			}
26551 			break;
26552 
26553 		case OP_DEPART_NODE_DELAY:
26554 			if ( argnum == 0 ){
26555 				return OPF_POSITIVE;
26556 			} else if ( argnum == 1 ){
26557 				return OPF_JUMP_NODE_NAME;
26558 			} else {
26559 				return OPF_SHIP;
26560 			}
26561 
26562 		case OP_DESTROYED_DEPARTED_DELAY:
26563 			if ( argnum == 0 ){
26564 				return OPF_POSITIVE;
26565 			} else {
26566 				return OPF_SHIP_WING;
26567 			}
26568 
26569 		case OP_JETTISON_CARGO:
26570 			if(argnum == 1){
26571 				return OPF_POSITIVE;
26572 			} else {
26573 				return OPF_SHIP;
26574 			}
26575 
26576 		case OP_SET_DOCKED:
26577 			if (argnum == 0) {
26578 				return OPF_SHIP;
26579 			} else if (argnum == 1) {
26580 				return OPF_DOCKER_POINT;
26581 			} else if (argnum == 2) {
26582 				return OPF_SHIP;
26583 			} else {
26584 				return OPF_DOCKEE_POINT;
26585 			}
26586 
26587 		case OP_CARGO_NO_DEPLETE:
26588 			if (argnum == 0) {
26589 				return OPF_SHIP;
26590 			} else {
26591 				return OPF_NUMBER;
26592 			}
26593 
26594 		case OP_BEAM_FIRE:
26595 			switch(argnum) {
26596 				case 0:
26597 					return OPF_SHIP;
26598 				case 1:
26599 					return OPF_SUBSYSTEM;
26600 				case 2:
26601 					return OPF_SHIP;
26602 				case 3:
26603 					return OPF_SUBSYSTEM;
26604 				case 4:
26605 					return OPF_BOOL;
26606 			}
26607 
26608 		case OP_BEAM_FIRE_COORDS:
26609 			switch(argnum) {
26610 				case 0:
26611 					return OPF_SHIP;
26612 				case 1:
26613 					return OPF_SUBSYSTEM;
26614 				case 5:
26615 					return OPF_BOOL;
26616 				default:
26617 					return OPF_NUMBER;
26618 			}
26619 
26620 		case OP_IS_TAGGED:
26621 			return OPF_SHIP;
26622 
26623 		case OP_IS_PLAYER:
26624 			if (argnum == 0) {
26625 				return OPF_BOOL;
26626 			} else {
26627 				return OPF_SHIP;
26628 			}
26629 
26630 		case OP_NUM_KILLS:
26631 		case OP_NUM_ASSISTS:
26632 		case OP_SHIP_SCORE:
26633 		case OP_SHIP_DEATHS:
26634 		case OP_RESPAWNS_LEFT:
26635 			return OPF_SHIP;
26636 
26637 		case OP_SET_RESPAWNS:
26638 			if (argnum == 0 ) {
26639 				return OPF_POSITIVE;
26640 			}
26641 			else {
26642 				return OPF_SHIP;
26643 			}
26644 
26645 		case OP_NUM_TYPE_KILLS:
26646 			if(argnum == 0){
26647 				return OPF_SHIP;
26648 			} else {
26649 				return OPF_SHIP_TYPE;
26650 			}
26651 
26652 		case OP_NUM_CLASS_KILLS:
26653 			if(argnum == 0){
26654 				return OPF_SHIP;
26655 			} else {
26656 				return OPF_SHIP_CLASS_NAME;
26657 			}
26658 
26659 		case OP_BEAM_FREE:
26660 		case OP_BEAM_LOCK:
26661 		case OP_TURRET_FREE:
26662 		case OP_TURRET_LOCK:
26663 		case OP_TURRET_TAGGED_SPECIFIC:
26664 		case OP_TURRET_TAGGED_CLEAR_SPECIFIC:
26665 			if(argnum == 0){
26666 				return OPF_SHIP;
26667 			} else {
26668 				return OPF_SUBSYSTEM;
26669 			}
26670 
26671 		case OP_TURRET_SUBSYS_TARGET_DISABLE:
26672 		case OP_TURRET_SUBSYS_TARGET_ENABLE:
26673 			if(argnum == 0){
26674 				return OPF_SHIP;
26675 			} else {
26676 				return OPF_SUBSYS_OR_GENERIC;
26677 			}
26678 
26679 		case OP_TURRET_CHANGE_WEAPON:
26680 			if(argnum == 0) {
26681 				return OPF_SHIP;
26682 			} else if(argnum == 1) {
26683 				return OPF_SUBSYSTEM;
26684 			} else if(argnum == 2) {
26685 				return OPF_WEAPON_NAME;
26686 			} else if(argnum > 2) {
26687 				return OPF_POSITIVE;
26688 			}
26689 
26690 		case OP_TURRET_SET_DIRECTION_PREFERENCE:
26691 			if(argnum == 0) {
26692 				return OPF_SHIP;
26693 			} else if(argnum == 1) {
26694 				return OPF_NUMBER;
26695 			} else {
26696 				return OPF_SUBSYSTEM;
26697 			}
26698 
26699 		case OP_TURRET_SET_RATE_OF_FIRE:
26700 			if(argnum == 0) {
26701 				return OPF_SHIP;
26702 			} else if(argnum == 1) {
26703 				return OPF_NUMBER;
26704 			} else {
26705 				return OPF_SUBSYSTEM;
26706 			}
26707 
26708 		case OP_TURRET_SET_OPTIMUM_RANGE:
26709 			if(argnum == 0) {
26710 				return OPF_SHIP;
26711 			} else if(argnum == 1) {
26712 				return OPF_NUMBER;
26713 			} else {
26714 				return OPF_SUBSYSTEM;
26715 			}
26716 
26717 		case OP_TURRET_SET_TARGET_PRIORITIES:
26718 			if(argnum == 0) {
26719 				return OPF_SHIP;
26720 			} else if(argnum == 1) {
26721 				return OPF_SUBSYSTEM;
26722 			} else if(argnum == 2) {
26723 				return OPF_BOOL;
26724 			} else {
26725 				return OPF_TARGET_PRIORITIES;
26726 			}
26727 
26728 		case OP_SET_ARMOR_TYPE:
26729 			if(argnum == 0) {
26730 				return OPF_SHIP;
26731 			} else if(argnum == 1) {
26732 				return OPF_BOOL;
26733 			} else if(argnum == 2) {
26734 				return OPF_ARMOR_TYPE;
26735 			} else {
26736 				return OPF_SUBSYSTEM;
26737 			}
26738 
26739 		case OP_WEAPON_SET_DAMAGE_TYPE:
26740 			if(argnum == 0) {
26741 				return OPF_BOOL;
26742 			} else if(argnum == 1) {
26743 				return OPF_DAMAGE_TYPE;
26744 			} else if(argnum == 2) {
26745 				return OPF_BOOL;
26746 			} else {
26747 				return OPF_WEAPON_NAME;
26748 			}
26749 
26750 		case OP_SHIP_SET_DAMAGE_TYPE:
26751 			if(argnum == 0) {
26752 				return OPF_BOOL;
26753 			} else if(argnum == 1) {
26754 				return OPF_DAMAGE_TYPE;
26755 			} else if(argnum == 2) {
26756 				return OPF_BOOL;
26757 			} else {
26758 				return OPF_SHIP;
26759 			}
26760 
26761 		case OP_SHIP_SHOCKWAVE_SET_DAMAGE_TYPE:
26762 			if(argnum == 0) {
26763 				return OPF_DAMAGE_TYPE;
26764 			} else if(argnum == 1) {
26765 				return OPF_BOOL;
26766 			} else {
26767 				return OPF_SHIP_CLASS_NAME;
26768 			}
26769 
26770 		case OP_FIELD_SET_DAMAGE_TYPE:
26771 			if(argnum == 0) {
26772 				return OPF_DAMAGE_TYPE;
26773 			} else {
26774 				return OPF_BOOL;
26775 			}
26776 
26777 		case OP_TURRET_SET_TARGET_ORDER:
26778 			if(argnum == 0) {
26779 				return OPF_SHIP;
26780 			} else if(argnum == 1) {
26781 				return OPF_SUBSYSTEM;
26782 			} else {
26783 				return OPF_TURRET_TARGET_ORDER;
26784 			}
26785 
26786 		case OP_SHIP_TURRET_TARGET_ORDER:
26787 			if(argnum == 0) {
26788 				return OPF_SHIP;
26789 			} else {
26790 				return OPF_TURRET_TARGET_ORDER;
26791 			}
26792 
26793 		case OP_LOCK_ROTATING_SUBSYSTEM:
26794 		case OP_FREE_ROTATING_SUBSYSTEM:
26795 		case OP_REVERSE_ROTATING_SUBSYSTEM:
26796 			if (argnum == 0)
26797 				return OPF_SHIP;
26798 			else
26799 				return OPF_ROTATING_SUBSYSTEM;
26800 
26801 		case OP_ROTATING_SUBSYS_SET_TURN_TIME:
26802 			if (argnum == 0)
26803 				return OPF_SHIP;
26804 			else if (argnum == 1)
26805 				return OPF_ROTATING_SUBSYSTEM;
26806 			else if (argnum == 2)
26807 				return OPF_NUMBER;
26808 			else
26809 				return OPF_POSITIVE;
26810 
26811 		case OP_TRIGGER_SUBMODEL_ANIMATION:
26812 			if (argnum == 0)
26813 				return OPF_SHIP;
26814 			else if (argnum == 1)
26815 				return OPF_ANIMATION_TYPE;
26816 			else if (argnum == 2 || argnum == 3)
26817 				return OPF_NUMBER;
26818 			else if (argnum == 4)
26819 				return OPF_BOOL;
26820 			else if (argnum == 5)
26821 				return OPF_SUBSYSTEM;
26822 
26823 		case OP_BEAM_FREE_ALL:
26824 		case OP_BEAM_LOCK_ALL:
26825 		case OP_TURRET_FREE_ALL:
26826 		case OP_TURRET_LOCK_ALL:
26827 		case OP_TURRET_TAGGED_ONLY_ALL:
26828 		case OP_TURRET_TAGGED_CLEAR_ALL:
26829 			return OPF_SHIP;
26830 
26831 		case OP_ADD_REMOVE_ESCORT:
26832 			if(argnum == 0){
26833 				return OPF_SHIP;
26834 			} else {
26835 				return OPF_NUMBER;
26836 			}
26837 
26838 		case OP_AWACS_SET_RADIUS:
26839 			if(argnum == 0){
26840 				return OPF_SHIP;
26841 			} else if(argnum == 1){
26842 				return OPF_AWACS_SUBSYSTEM;
26843 			} else {
26844 				return OPF_NUMBER;
26845 			}
26846 
26847 		case OP_PRIMITIVE_SENSORS_SET_RANGE:
26848 			if (!argnum)
26849 				return OPF_SHIP;
26850 			else
26851 				return OPF_NUMBER;
26852 
26853 		case OP_CAP_WAYPOINT_SPEED:
26854 			if (argnum == 0) {
26855 				return OPF_SHIP;
26856 			} else {
26857 				return OPF_NUMBER;
26858 			}
26859 
26860 		case OP_SUBSYS_SET_RANDOM:
26861 			if (argnum == 0) {
26862 				return OPF_SHIP;
26863 			} else if (argnum == 1 || argnum == 2) {
26864 				return OPF_NUMBER;
26865 			} else {
26866 				return OPF_SUBSYSTEM;
26867 			}
26868 
26869 		case OP_SUPERNOVA_START:
26870 			return OPF_POSITIVE;
26871 
26872 		case OP_SHIELD_RECHARGE_PCT:
26873 		case OP_WEAPON_RECHARGE_PCT:
26874 		case OP_ENGINE_RECHARGE_PCT:
26875 			return OPF_SHIP;
26876 
26877 		case OP_GET_ETS_VALUE:
26878 			if (argnum == 0) {
26879 				return OPF_STRING;
26880 			} else {
26881 				return OPF_SHIP;
26882 			}
26883 
26884 		case OP_SET_ETS_VALUES:
26885 			if (argnum < 3) {
26886 				return OPF_POSITIVE;
26887 			} else {
26888 				return OPF_SHIP;
26889 			}
26890 
26891 		case OP_SHIELD_QUAD_LOW:
26892 			if(argnum == 0){
26893 				return OPF_SHIP;
26894 			} else {
26895 				return OPF_NUMBER;
26896 			}
26897 
26898 		case OP_PRIMARY_AMMO_PCT:
26899 		case OP_SECONDARY_AMMO_PCT:
26900 			if(argnum == 0){
26901 				return OPF_SHIP;
26902 			} else {
26903 				return OPF_NUMBER;
26904 			}
26905 
26906 		// Karajorma
26907 		case OP_GET_PRIMARY_AMMO:
26908 		case OP_SET_PRIMARY_AMMO:
26909 		case OP_GET_SECONDARY_AMMO:
26910 		case OP_SET_SECONDARY_AMMO:
26911 			if(argnum == 0){
26912 				return OPF_SHIP;
26913 			} else {
26914 				return OPF_NUMBER;
26915 			}
26916 
26917 		// Karajorma
26918 		case OP_SET_PRIMARY_WEAPON:
26919 		case OP_SET_SECONDARY_WEAPON:
26920 			if(argnum == 0)
26921 			{
26922 				return OPF_SHIP;
26923 			}
26924 
26925 			else if (argnum == 2)
26926 			{
26927 				return OPF_WEAPON_NAME;
26928 			}
26929 			else
26930 			{
26931 				return OPF_NUMBER;
26932 			}
26933 
26934 
26935 		case OP_GET_NUM_COUNTERMEASURES:
26936 			return OPF_SHIP;
26937 
26938 		case OP_SET_NUM_COUNTERMEASURES:
26939 			if (argnum == 0)
26940 				return OPF_SHIP;
26941 			else
26942 				return OPF_POSITIVE;
26943 
26944 		// Karajorma
26945 		case OP_LOCK_PRIMARY_WEAPON:
26946 		case OP_UNLOCK_PRIMARY_WEAPON:
26947 		case OP_LOCK_SECONDARY_WEAPON:
26948 		case OP_UNLOCK_SECONDARY_WEAPON:
26949 		// KeldorKatarn
26950 		case OP_LOCK_AFTERBURNER:
26951 		case OP_UNLOCK_AFTERBURNER:
26952 			return OPF_SHIP;
26953 
26954 
26955 		case OP_SET_AFTERBURNER_ENERGY:
26956 		case OP_SET_WEAPON_ENERGY:
26957 		case OP_SET_SHIELD_ENERGY:
26958 			if (argnum == 0) {
26959 				return OPF_POSITIVE;
26960 			}
26961 			else {
26962 				return OPF_SHIP;
26963 			}
26964 
26965 		case OP_SET_AMBIENT_LIGHT:
26966 			return OPF_POSITIVE;
26967 
26968 		case OP_SET_POST_EFFECT:
26969 			if (argnum == 0)
26970 				return OPF_POST_EFFECT;
26971 			else
26972 				return OPF_POSITIVE;
26973 
26974 		case OP_CHANGE_SUBSYSTEM_NAME:
26975 			if (argnum == 0) {
26976 				return OPF_SHIP;
26977 			}
26978 			else if (argnum == 1) {
26979 				return OPF_STRING;
26980 			}
26981 			else {
26982 				return OPF_SUBSYSTEM;
26983 			}
26984 
26985 		case OP_IS_SECONDARY_SELECTED:
26986 		case OP_IS_PRIMARY_SELECTED:
26987 			if(argnum == 0){
26988 				return OPF_SHIP;
26989 			} else {
26990 				return OPF_NUMBER;
26991 			}
26992 
26993 		case OP_DAMAGED_ESCORT_LIST:
26994 			if (argnum < 2)
26995 			{
26996 				return OPF_NUMBER;
26997 			}
26998 			else
26999 			{
27000 				return OPF_SHIP;
27001 			}
27002 
27003 		case OP_DAMAGED_ESCORT_LIST_ALL:
27004 			return OPF_POSITIVE;
27005 
27006 		case OP_CHANGE_SHIP_CLASS:
27007 			if (!argnum)
27008 				return OPF_SHIP_CLASS_NAME;
27009 			else
27010 				return OPF_SHIP;
27011 
27012 		case OP_SHIP_COPY_DAMAGE:
27013 			return OPF_SHIP;
27014 
27015 		case OP_DEACTIVATE_GLOW_POINTS:	//-Bobboau
27016 		case OP_ACTIVATE_GLOW_POINTS:	//-Bobboau
27017 		case OP_DEACTIVATE_GLOW_MAPS:	//-Bobboau
27018 		case OP_ACTIVATE_GLOW_MAPS:		//-Bobboau
27019 			return OPF_SHIP;	//a list of ships that are to be activated/deactivated
27020 		case OP_DEACTIVATE_GLOW_POINT_BANK:
27021 		case OP_ACTIVATE_GLOW_POINT_BANK:
27022 			if (!argnum)
27023 				return OPF_SHIP;		//the ship
27024 			else
27025 				return OPF_POSITIVE;		//the glow bank to disable
27026 
27027 		// taylor
27028 		case OP_SET_SKYBOX_MODEL:
27029 			return OPF_SKYBOX_MODEL_NAME;
27030 
27031 		case OP_SET_SKYBOX_ORIENT:
27032 			return OPF_NUMBER;
27033 
27034 		// Goober5000 - this is complicated :)
27035 		case OP_SET_SUPPORT_SHIP:
27036 			if ((argnum == 0) || (argnum == 2))
27037 				return OPF_DEPARTURE_LOCATION;	// use this for both because we don't want Near Ship or In Front of Ship
27038 			if ((argnum == 1) || (argnum == 3))
27039 				return OPF_SHIP_WITH_BAY;		// same - we don't want to anchor around anything without a docking bay
27040 			if (argnum == 4)
27041 				return OPF_SUPPORT_SHIP_CLASS;
27042 			if (argnum == 5)
27043 				return OPF_NUMBER;
27044 			if (argnum == 6)
27045 				return OPF_NUMBER;
27046 			break;
27047 
27048 		// Goober5000
27049 		case OP_SET_ARRIVAL_INFO:
27050 			if (argnum == 0)
27051 				return OPF_SHIP_WING;
27052 			else if (argnum == 1)
27053 				return OPF_ARRIVAL_LOCATION;
27054 			else if (argnum == 2)
27055 				return OPF_ARRIVAL_ANCHOR_ALL;
27056 			else if (argnum == 3)
27057 				return OPF_NUMBER;
27058 			else if (argnum == 4 || argnum == 5)
27059 				return OPF_POSITIVE;
27060 			else if (argnum == 6)
27061 				return OPF_BOOL;
27062 			break;
27063 
27064 		// Goober5000
27065 		case OP_SET_DEPARTURE_INFO:
27066 			if (argnum == 0)
27067 				return OPF_SHIP_WING;
27068 			else if (argnum == 1)
27069 				return OPF_DEPARTURE_LOCATION;
27070 			else if (argnum == 2)
27071 				return OPF_SHIP_WITH_BAY;
27072 			else if (argnum == 3 || argnum == 4)
27073 				return OPF_NUMBER;
27074 			else if (argnum == 5)
27075 				return OPF_BOOL;
27076 			break;
27077 
27078 		case OP_KAMIKAZE:
27079 			if (argnum==0)
27080 				return OPF_POSITIVE;
27081 			else
27082 				return OPF_SHIP_WING;
27083 
27084 		case OP_NUM_SHIPS_IN_BATTLE:	//phreak modified by FUBAR
27085 			return OPF_SHIP_WING_WHOLETEAM;
27086 
27087 		case OP_NUM_SHIPS_IN_WING:	// Karajorma
27088 			return OPF_WING;
27089 
27090 		case OP_CURRENT_SPEED:
27091 			return OPF_SHIP_WING;
27092 
27093 		case OP_PRIMARY_FIRED_SINCE:
27094 		case OP_SECONDARY_FIRED_SINCE:
27095 			if (argnum == 0)
27096 				return OPF_SHIP;
27097 			else
27098 				return OPF_POSITIVE;
27099 
27100 		case OP_HAS_PRIMARY_WEAPON:
27101 		case OP_HAS_SECONDARY_WEAPON:
27102 			if (argnum == 0)
27103 				return OPF_SHIP;
27104 			else if (argnum == 1)
27105 				return OPF_WEAPON_BANK_NUMBER;
27106 			else
27107 				return OPF_WEAPON_NAME;
27108 
27109 		case OP_DIRECTIVE_VALUE:
27110 			if (argnum == 0)
27111 				return OPF_NUMBER;
27112 			else
27113 				return OPF_BOOL;
27114 
27115 		case OP_NAV_IS_VISITED:		//Kazan
27116 		case OP_NAV_DISTANCE:		//kazan
27117 		case OP_NAV_DEL:			//kazan
27118 		case OP_NAV_HIDE:			//kazan
27119 		case OP_NAV_RESTRICT:		//kazan
27120 		case OP_NAV_UNHIDE:			//kazan
27121 		case OP_NAV_UNRESTRICT:		//kazan
27122 		case OP_NAV_SET_VISITED:	//kazan
27123 		case OP_NAV_UNSET_VISITED:	//kazan
27124 		case OP_NAV_SELECT:			//Talon1024
27125 			return OPF_STRING;
27126 
27127 		case OP_NAV_SET_CARRY:		//kazan
27128 		case OP_NAV_UNSET_CARRY:	//kazan
27129 			return OPF_SHIP_WING;
27130 
27131 		case OP_NAV_SET_NEEDSLINK: // kazan
27132 		case OP_NAV_UNSET_NEEDSLINK:
27133 		case OP_NAV_ISLINKED:
27134 				return OPF_SHIP;
27135 
27136 		case OP_NAV_USECINEMATICS:
27137 		case OP_NAV_USEAP:
27138 			return OPF_BOOL;
27139 
27140 		case OP_NAV_ADD_WAYPOINT:	//kazan
27141 			if (argnum==0)
27142 				return OPF_STRING;
27143 			else if (argnum==1)
27144 				return OPF_WAYPOINT_PATH;
27145 			else if (argnum==2)
27146 				return OPF_POSITIVE;
27147 			else
27148 				return OPF_SHIP_WING_WHOLETEAM;
27149 
27150 		case OP_NAV_ADD_SHIP:		//kazan
27151 			if (argnum==0)
27152 				return OPF_STRING;
27153 			else
27154 				return OPF_SHIP;
27155 
27156 		//<Cutscenes>
27157 		case OP_SCRAMBLE_MESSAGES:
27158 		case OP_UNSCRAMBLE_MESSAGES:
27159 			return OPF_SHIP;
27160 
27161 		case OP_CUTSCENES_GET_FOV:
27162 			return OPF_NONE;
27163 
27164 		case OP_CUTSCENES_SET_CUTSCENE_BARS:
27165 		case OP_CUTSCENES_UNSET_CUTSCENE_BARS:
27166 		case OP_CUTSCENES_FADE_IN:
27167 		case OP_CUTSCENES_FADE_OUT:
27168 		case OP_CUTSCENES_SET_TIME_COMPRESSION:
27169 			return OPF_POSITIVE;
27170 
27171 		case OP_CUTSCENES_SET_FOV:
27172 			return OPF_NUMBER;
27173 
27174 		case OP_CUTSCENES_SET_CAMERA:
27175 			return OPF_STRING;
27176 
27177 		case OP_CUTSCENES_SET_CAMERA_POSITION:
27178 		case OP_CUTSCENES_SET_CAMERA_FACING:
27179 		case OP_CUTSCENES_SET_CAMERA_ROTATION:
27180 			if(argnum < 3)
27181 				return OPF_NUMBER;
27182 			else
27183 				return OPF_POSITIVE;
27184 
27185 		case OP_CUTSCENES_SET_CAMERA_FACING_OBJECT:
27186 			if(argnum < 1)
27187 				return OPF_SHIP_WING_POINT;
27188 			else
27189 				return OPF_POSITIVE;
27190 
27191 		case OP_CUTSCENES_SET_CAMERA_FOV:
27192 			return OPF_POSITIVE;
27193 
27194 		case OP_CUTSCENES_SET_CAMERA_HOST:
27195 		case OP_CUTSCENES_SET_CAMERA_TARGET:
27196 			if(argnum < 1)
27197 				return OPF_SHIP_WING_POINT_OR_NONE;
27198 			else
27199 				return OPF_SUBSYSTEM_OR_NONE;
27200 
27201 		case OP_CUTSCENES_RESET_CAMERA:
27202 			return OPF_BOOL;
27203 
27204 		case OP_CUTSCENES_RESET_FOV:
27205 		case OP_CUTSCENES_RESET_TIME_COMPRESSION:
27206 			return OPF_NONE;
27207 
27208 		case OP_CUTSCENES_FORCE_PERSPECTIVE:
27209 			if(argnum==0)
27210 				return OPF_BOOL;
27211 			else
27212 				return OPF_POSITIVE;
27213 
27214 		case OP_SET_CAMERA_SHUDDER:
27215 			return OPF_POSITIVE;
27216 
27217 		case OP_CUTSCENES_SHOW_SUBTITLE:
27218 			if(argnum < 2)
27219 				return OPF_NUMBER;
27220 			else if (argnum == 2)
27221 				return OPF_STRING;
27222 			else if (argnum == 3)
27223 				return OPF_POSITIVE;
27224 			else if (argnum == 4)
27225 				return OPF_STRING;
27226 			else if (argnum == 5)
27227 				return OPF_POSITIVE;
27228 			else if (argnum < 8)
27229 				return OPF_BOOL;
27230 			else if (argnum < 12)
27231 				return OPF_POSITIVE;
27232 			else if (argnum == 12 )
27233 				return OPF_BOOL;
27234 
27235 		case OP_CUTSCENES_SHOW_SUBTITLE_TEXT:
27236 			if (argnum == 0)
27237 				return OPF_MESSAGE_OR_STRING;
27238 			else if (argnum == 1 || argnum == 2)
27239 				return OPF_NUMBER;
27240 			else if (argnum == 3 || argnum == 4)
27241 				return OPF_BOOL;
27242 			else if (argnum >= 5 && argnum <= 10)
27243 				return OPF_POSITIVE;
27244 			else if (argnum == 11)
27245 				return OPF_FONT;
27246 			else if (argnum == 12)
27247 				return OPF_BOOL;
27248 
27249 		case OP_CUTSCENES_SHOW_SUBTITLE_IMAGE:
27250 			if (argnum == 0)
27251 				return OPF_STRING;
27252 			else if (argnum == 1 || argnum == 2)
27253 				return OPF_NUMBER;
27254 			else if (argnum == 3 || argnum == 4)
27255 				return OPF_BOOL;
27256 			else if (argnum >= 5 && argnum <= 8)
27257 				return OPF_POSITIVE;
27258 			else if (argnum == 9)
27259 				return OPF_BOOL;
27260 
27261 		//</Cutscenes>
27262 
27263 		case OP_JUMP_NODE_SET_JUMPNODE_NAME: //CommanderDJ
27264 			if(argnum==0)
27265 				return OPF_JUMP_NODE_NAME;
27266 			else if (argnum==1)
27267 				return OPF_STRING;
27268 
27269 		case OP_JUMP_NODE_SET_JUMPNODE_COLOR:
27270 			if(argnum==0)
27271 				return OPF_JUMP_NODE_NAME;
27272 			else
27273 				return OPF_POSITIVE;
27274 
27275 		case OP_JUMP_NODE_SET_JUMPNODE_MODEL:
27276 			if(argnum==0)
27277 				return OPF_JUMP_NODE_NAME;
27278 			else if (argnum == 1)
27279 				return OPF_STRING;
27280 			else
27281 				return OPF_BOOL;
27282 
27283 		case OP_JUMP_NODE_SHOW_JUMPNODE:
27284 		case OP_JUMP_NODE_HIDE_JUMPNODE:
27285 				return OPF_JUMP_NODE_NAME;
27286 
27287 		case OP_ADD_BACKGROUND_BITMAP:
27288 			if (argnum == 0)
27289 				return OPF_BACKGROUND_BITMAP;
27290 			else if (argnum == 8) return OPF_VARIABLE_NAME;
27291 			else return OPF_POSITIVE;
27292 
27293 		case OP_REMOVE_BACKGROUND_BITMAP:
27294 			return OPF_POSITIVE;
27295 
27296 		case OP_ADD_SUN_BITMAP:
27297 			if (argnum == 0)
27298 				return OPF_SUN_BITMAP;
27299 			else if (argnum == 5) return OPF_VARIABLE_NAME;
27300 			else return OPF_POSITIVE;
27301 
27302 		case OP_REMOVE_SUN_BITMAP:
27303 			return OPF_POSITIVE;
27304 
27305 		case OP_NEBULA_CHANGE_STORM:
27306 			return OPF_NEBULA_STORM_TYPE;
27307 
27308 		case OP_NEBULA_CHANGE_PATTERN:
27309 			return OPF_NEBULA_PATTERN;
27310 
27311 		case OP_NEBULA_TOGGLE_POOF:
27312 			if (argnum == 1) return OPF_BOOL;
27313 			else return OPF_NEBULA_POOF;
27314 
27315 		case OP_SCRIPT_EVAL_NUM:
27316 		case OP_SCRIPT_EVAL_STRING:
27317 		case OP_SCRIPT_EVAL:
27318 			return OPF_STRING;
27319 
27320 		case OP_SCRIPT_EVAL_MULTI:
27321 			if (argnum == 0)
27322 				return OPF_STRING;
27323 			else if (argnum == 1)
27324 				return OPF_BOOL;
27325 			else
27326 				return OPF_SHIP;
27327 
27328 		case OP_CHANGE_IFF_COLOR:
27329 			if ((argnum == 0) || (argnum == 1))
27330 				return OPF_IFF;
27331 			else if ((argnum >= 2) && (argnum <=4))
27332 				return OPF_POSITIVE;
27333 			else return OPF_SHIP_WING;
27334 
27335 		case OP_HUD_DISPLAY_GAUGE:
27336 			if ( argnum == 0 ) {
27337 				return OPF_POSITIVE;
27338 			} else {
27339 				return OPF_HUD_ELEMENT;
27340 			}
27341 
27342 		case OP_DISABLE_ETS:
27343 		case OP_ENABLE_ETS:
27344 				return OPF_SHIP;
27345 
27346 		case OP_IS_FACING:
27347 			if (argnum == 0)
27348 				return OPF_SHIP;
27349 			else if (argnum == 1)
27350 				return OPF_SHIP_POINT;
27351 			else
27352 				return OPF_POSITIVE;
27353 
27354 		case OP_FORCE_GLIDE:
27355 			if (argnum == 0)
27356 				return OPF_SHIP;
27357 			else
27358 				return OPF_BOOL;
27359 
27360 		case OP_HUD_SET_DIRECTIVE:
27361 			return OPF_STRING;
27362 
27363 		case OP_HUD_GAUGE_SET_ACTIVE:
27364 			if (argnum == 0)
27365 				return OPF_STRING;
27366 			else
27367 				return OPF_BOOL;
27368 
27369 		case OP_HUD_ACTIVATE_GAUGE_TYPE:
27370 			if (argnum == 0)
27371 				return OPF_HUD_GAUGE;
27372 			else
27373 				return OPF_BOOL;
27374 
27375 		case OP_HUD_SET_CUSTOM_GAUGE_ACTIVE:
27376 			if (argnum == 0)
27377 				return OPF_BOOL;
27378 			else
27379 				return OPF_STRING;
27380 
27381 		case OP_HUD_SET_RETAIL_GAUGE_ACTIVE:
27382 			if (argnum == 0)
27383 				return OPF_BOOL;
27384 			else
27385 				return OPF_HUD_GAUGE;
27386 
27387 		case OP_GET_COLGROUP_ID:
27388 			return OPF_SHIP;
27389 
27390 		case OP_ADD_TO_COLGROUP:
27391 			if (argnum == 0)
27392 				return OPF_SHIP;
27393 			else
27394 				return OPF_POSITIVE;
27395 
27396 		case OP_REMOVE_FROM_COLGROUP:
27397 			if (argnum == 0)
27398 				return OPF_SHIP;
27399 			else
27400 				return OPF_POSITIVE;
27401 
27402 		case OP_SHIP_EFFECT:
27403 			if (argnum == 0)
27404 				return OPF_SHIP_EFFECT;
27405 			else if (argnum == 1)
27406 				return OPF_NUMBER;
27407 			else
27408 				return OPF_SHIP_WING;
27409 
27410 		case OP_CLEAR_SUBTITLES:
27411 			return OPF_NONE;
27412 
27413 		case OP_SET_THRUSTERS:
27414 			if (argnum == 0)
27415 				return OPF_BOOL;
27416 			else
27417 				return OPF_SHIP;
27418 
27419 		case OP_SET_MOTION_DEBRIS:
27420 			return OPF_BOOL;
27421 
27422 		default:
27423 			Int3();
27424 	}
27425 
27426 	return 0;
27427 }
27428 
27429 // DA: 1/7/99  Used to rename ships and waypoints, not variables
27430 // Strictly used in FRED
update_sexp_references(const char * old_name,const char * new_name)27431 void update_sexp_references(const char *old_name, const char *new_name)
27432 {
27433 	int i;
27434 
27435 	Assert(strlen(new_name) < TOKEN_LENGTH);
27436 	for (i = 0; i < Num_sexp_nodes; i++)
27437 	{
27438 		if ((SEXP_NODE_TYPE(i) == SEXP_ATOM) && (Sexp_nodes[i].subtype == SEXP_ATOM_STRING))
27439 			if (!stricmp(CTEXT(i), old_name))
27440 				strcpy(CTEXT(i), new_name);
27441 	}
27442 }
27443 
27444 // DA: 1/7/99  Used to rename event names, goal names, not variables
27445 // Strictly used in FRED
update_sexp_references(const char * old_name,const char * new_name,int format)27446 void update_sexp_references(const char *old_name, const char *new_name, int format)
27447 {
27448 	int i;
27449 	if (!strcmp(old_name, new_name)) {
27450 		return;
27451 	}
27452 
27453 	Assert(strlen(new_name) < TOKEN_LENGTH);
27454 	for (i = 0; i < Num_sexp_nodes; i++)
27455 	{
27456 		if (is_sexp_top_level(i))
27457 			update_sexp_references(old_name, new_name, format, i);
27458 	}
27459 }
27460 
27461 // DA: 1/7/99  Used to rename event names, goal names, not variables
27462 // recursive function to update references to a certain type of data
update_sexp_references(const char * old_name,const char * new_name,int format,int node)27463 void update_sexp_references(const char *old_name, const char *new_name, int format, int node)
27464 {
27465 	int i, n, op;
27466 
27467 	if (node < 0)
27468 		return;
27469 
27470 	if ((SEXP_NODE_TYPE(node) == SEXP_LIST) && (Sexp_nodes[node].subtype == SEXP_ATOM_LIST))
27471 	{
27472 		if (Sexp_nodes[node].first)
27473 			update_sexp_references(old_name, new_name, format, Sexp_nodes[node].first);
27474 
27475 		if (Sexp_nodes[node].rest)
27476 			update_sexp_references(old_name, new_name, format, Sexp_nodes[node].rest);
27477 
27478 		return;
27479 	}
27480 
27481 	if (SEXP_NODE_TYPE(node) != SEXP_ATOM)
27482 		return;
27483 
27484 	if (Sexp_nodes[node].subtype != SEXP_ATOM_OPERATOR)
27485 		return;
27486 
27487 	op = get_operator_index(CTEXT(node));
27488 	Assert(Sexp_nodes[node].first < 0);
27489 	n = Sexp_nodes[node].rest;
27490 	i = 0;
27491 	while (n >= 0)
27492 	{
27493 		if (SEXP_NODE_TYPE(n) == SEXP_LIST)
27494 		{
27495 			update_sexp_references(old_name, new_name, format, Sexp_nodes[n].first);
27496 		}
27497 		else
27498 		{
27499 			Assert((SEXP_NODE_TYPE(n) == SEXP_ATOM) && ((Sexp_nodes[n].subtype == SEXP_ATOM_NUMBER) || (Sexp_nodes[n].subtype == SEXP_ATOM_STRING)));
27500 
27501 			if (query_operator_argument_type(op, i) == format)
27502 			{
27503 				if (!stricmp(CTEXT(n), old_name))
27504 					strcpy(CTEXT(n), new_name);
27505 			}
27506 		}
27507 
27508 		n = Sexp_nodes[n].rest;
27509 		i++;
27510 	}
27511 }
27512 
query_referenced_in_sexp(int mode,char * name,int * node)27513 int query_referenced_in_sexp(int mode, char *name, int *node)
27514 {
27515 	int i, n, j;
27516 
27517 	for (n=0; n<Num_sexp_nodes; n++){
27518 		if ((SEXP_NODE_TYPE(n) == SEXP_ATOM) && (Sexp_nodes[n].subtype == SEXP_ATOM_STRING)){
27519 			if (!stricmp(CTEXT(n), name)){
27520 				break;
27521 			}
27522 		}
27523 	}
27524 
27525 	if (n == Num_sexp_nodes){
27526 		return 0;
27527 	}
27528 
27529 	if (node){
27530 		*node = n;
27531 	}
27532 
27533 	// so we know it's being used somewhere..  Time to find out where..
27534 	for (i=0; i<MAX_SHIPS; i++)
27535 		if (Ships[i].objnum >= 0) {
27536 			if (query_node_in_sexp(n, Ships[i].arrival_cue)){
27537 				return i | SRC_SHIP_ARRIVAL;
27538 			}
27539 			if (query_node_in_sexp(n, Ships[i].departure_cue)){
27540 				return i | SRC_SHIP_DEPARTURE;
27541 			}
27542 		}
27543 
27544 	for (i=0; i<MAX_WINGS; i++){
27545 		if (Wings[i].wave_count) {
27546 			if (query_node_in_sexp(n, Wings[i].arrival_cue)){
27547 				return i | SRC_WING_ARRIVAL;
27548 			}
27549 			if (query_node_in_sexp(n, Wings[i].departure_cue)){
27550 				return i | SRC_WING_DEPARTURE;
27551 			}
27552 		}
27553 	}
27554 
27555 	for (i=0; i<Num_mission_events; i++){
27556 		if (query_node_in_sexp(n, Mission_events[i].formula)){
27557 			return i | SRC_EVENT;
27558 		}
27559 	}
27560 
27561 	for (i=0; i<Num_goals; i++){
27562 		if (query_node_in_sexp(n, Mission_goals[i].formula)){
27563 			return i | SRC_MISSION_GOAL;
27564 		}
27565 	}
27566 
27567 	for (j=0; j<Num_teams; j++) {
27568 		for (i=0; i<Debriefings[j].num_stages; i++) {
27569 			if (query_node_in_sexp(n, Debriefings[j].stages[i].formula)){
27570 				return i | SRC_DEBRIEFING;
27571 			}
27572 		}
27573 	}
27574 
27575 	for (j=0; j<Num_teams; j++) {
27576 		for (i=0; i<Briefings[j].num_stages; i++) {
27577 			if (query_node_in_sexp(n, Briefings[j].stages[i].formula)){
27578 				return i | SRC_BRIEFING;
27579 			}
27580 		}
27581 	}
27582 
27583 	return SRC_UNKNOWN;
27584 }
27585 
verify_vector(char * text)27586 int verify_vector(char *text)
27587 {
27588 	char *str;
27589 	int len = 0;
27590 
27591 	if (text == NULL)
27592 		return -1;
27593 
27594 	len = strlen(text);
27595 	if (text[0] != '(' || text[len - 1] != ')'){
27596 		return -1;
27597 	}
27598 
27599 	str = &text[0];
27600 	skip_white(&str);
27601 	if (*str != '('){
27602 		return -1;
27603 	}
27604 
27605 	str++;
27606 	skip_white(&str);
27607 	if (validate_float(&str)){
27608 		return -1;
27609 	}
27610 
27611 	skip_white(&str);
27612 	if (validate_float(&str)){
27613 		return -1;
27614 	}
27615 
27616 	skip_white(&str);
27617 	if (validate_float(&str)){
27618 		return -1;
27619 	}
27620 
27621 	skip_white(&str);
27622 	if (*str != ')'){
27623 		return -1;
27624 	}
27625 
27626 	str++;
27627 	skip_white(&str);
27628 	if (*str){
27629 		return -1;
27630 	}
27631 
27632 	return 0;
27633 }
27634 
skip_white(char ** str)27635 void skip_white(char **str)
27636 {
27637 	if ((**str == ' ') || (**str == '\t')){
27638 		(*str)++;
27639 	}
27640 }
27641 
validate_float(char ** str)27642 int validate_float(char **str)
27643 {
27644 	int count = 0, dot = 0;
27645 
27646 	while (isdigit(**str) || **str == '.') {
27647 		if (**str == '.') {
27648 			if (dot){
27649 				return -1;
27650 			}
27651 
27652 			dot = 1;
27653 		}
27654 
27655 		(*str)++;
27656 		count++;
27657 	}
27658 
27659 	if (!count){
27660 		return -1;
27661 	}
27662 
27663 	return 0;
27664 }
27665 
27666 /**
27667  * Check if operator return type opr is a valid match for operator argument type opf
27668  */
sexp_query_type_match(int opf,int opr)27669 int sexp_query_type_match(int opf, int opr)
27670 {
27671 	switch (opf) {
27672 		case OPF_NUMBER:
27673 			return ((opr == OPR_NUMBER) || (opr == OPR_POSITIVE));
27674 
27675 		case OPF_POSITIVE:
27676 			// Goober5000's number hack
27677 			return ((opr == OPR_POSITIVE) || (opr == OPR_NUMBER));
27678 
27679 		case OPF_BOOL:
27680 			return (opr == OPR_BOOL);
27681 
27682 		case OPF_NULL:
27683 			return (opr == OPR_NULL);
27684 
27685 		// Goober5000
27686 		case OPF_FLEXIBLE_ARGUMENT:
27687 			return (opr == OPR_FLEXIBLE_ARGUMENT);
27688 
27689 		// Goober5000
27690 		case OPF_ANYTHING:
27691 			// don't match any operators, only data
27692 			return 0;
27693 
27694 		case OPF_AI_GOAL:
27695 			return (opr == OPR_AI_GOAL);
27696 	}
27697 
27698 	return 0;
27699 }
27700 
sexp_error_message(int num)27701 char *sexp_error_message(int num)
27702 {
27703 	switch (num) {
27704 		case SEXP_CHECK_NONOP_ARGS:
27705 			return "Data shouldn't have arguments";
27706 
27707 		case SEXP_CHECK_OP_EXPECTED:
27708 			return "Operator expected instead of data";
27709 
27710 		case SEXP_CHECK_UNKNOWN_OP:
27711 			return "Unrecognized operator";
27712 
27713 		case SEXP_CHECK_TYPE_MISMATCH:
27714 			return "Argument type mismatch";
27715 
27716 		case SEXP_CHECK_BAD_ARG_COUNT:
27717 			return "Argument count is illegal";
27718 
27719 		case SEXP_CHECK_UNKNOWN_TYPE:
27720 			return "Unknown operator argument type";
27721 
27722 		case SEXP_CHECK_INVALID_NUM:
27723 			return "Not a number";
27724 
27725 		case SEXP_CHECK_INVALID_SHIP:
27726 			return "Invalid ship name";
27727 
27728 		case SEXP_CHECK_INVALID_WING:
27729 			return "Invalid wing name";
27730 
27731 		case SEXP_CHECK_INVALID_SUBSYS:
27732 			return "Invalid subsystem name";
27733 
27734 		case SEXP_CHECK_INVALID_SUBSYS_TYPE:
27735 			return "Invalid subsystem type";
27736 
27737 		case SEXP_CHECK_INVALID_IFF:
27738 			return "Invalid team name";
27739 
27740 		case SEXP_CHECK_INVALID_AI_CLASS:
27741 			return "Invalid AI class name";
27742 
27743 		case SEXP_CHECK_INVALID_POINT:
27744 			return "Invalid point";
27745 
27746 		case SEXP_CHECK_NEGATIVE_NUM:
27747 			return "Negative number not allowed";
27748 
27749 		case SEXP_CHECK_INVALID_SHIP_WING:
27750 			return "Invalid ship/wing name";
27751 
27752 		case SEXP_CHECK_INVALID_SHIP_TYPE:
27753 			return "Invalid ship type";
27754 
27755 		case SEXP_CHECK_UNKNOWN_MESSAGE:
27756 			return "Invalid message name";
27757 
27758 		case SEXP_CHECK_INVALID_PRIORITY:
27759 			return "Invalid priority";
27760 
27761 		case SEXP_CHECK_INVALID_MISSION_NAME:
27762 			return "Invalid mission filename";
27763 
27764 		case SEXP_CHECK_INVALID_GOAL_NAME:
27765 			return "Invalid goal name";
27766 
27767 		case SEXP_CHECK_INVALID_LEVEL:
27768 			return "Mission level too low in tree";
27769 
27770 		case SEXP_CHECK_INVALID_MSG_SOURCE:
27771 			return "Invalid message source";
27772 
27773 		case SEXP_CHECK_INVALID_DOCKER_POINT:
27774 			return "Invalid docker point";
27775 
27776 		case SEXP_CHECK_INVALID_DOCKEE_POINT:
27777 			return "Invalid dockee point";
27778 
27779 		case SEXP_CHECK_ORDER_NOT_ALLOWED:
27780 			return "Ship not allowed to have this order";
27781 
27782 		case SEXP_CHECK_DOCKING_NOT_ALLOWED:
27783 			return "Ship can't dock with target ship";
27784 
27785 		case SEXP_CHECK_NUM_RANGE_INVALID:
27786 			return "Number is out of range";
27787 
27788 		case SEXP_CHECK_INVALID_EVENT_NAME:
27789 			return "Event name is invalid (not known)";
27790 
27791 		case SEXP_CHECK_INVALID_SKILL_LEVEL:
27792 			return "Skill level name is invalid (not known)";
27793 
27794 		case SEXP_CHECK_INVALID_MEDAL_NAME:
27795 			return "Invalid medal name";
27796 
27797 		case SEXP_CHECK_INVALID_WEAPON_NAME:
27798 			return "Invalid weapon name";
27799 
27800 		case SEXP_CHECK_INVALID_INTEL_NAME:
27801 			return "Invalid intel name";
27802 
27803 		case SEXP_CHECK_INVALID_SHIP_CLASS_NAME:
27804 			return "Invalid ship class name";
27805 
27806 		case SEXP_CHECK_INVALID_GAUGE_NAME:
27807 			return "Invalid HUD gauges name";
27808 
27809 		case SEXP_CHECK_INVALID_JUMP_NODE:
27810 			return "Invalid Jump Node name";
27811 
27812 		case SEXP_CHECK_UNKNOWN_ERROR:
27813 			return "Unknown error";
27814 
27815 		case SEXP_CHECK_INVALID_SUPPORT_SHIP_CLASS:
27816 			return "Invalid support ship class";
27817 
27818 		case SEXP_CHECK_INVALID_SHIP_WITH_BAY:
27819 			return "Ship does not have a fighter bay";
27820 
27821 		case SEXP_CHECK_INVALID_ARRIVAL_LOCATION:
27822 			return "Invalid arrival location";
27823 
27824 		case SEXP_CHECK_INVALID_DEPARTURE_LOCATION:
27825 			return "Invalid departure location";
27826 
27827 		case SEXP_CHECK_INVALID_ARRIVAL_ANCHOR_ALL:
27828 			return "Invalid universal arrival anchor";
27829 
27830 		case SEXP_CHECK_INVALID_SOUNDTRACK_NAME:
27831 			return "Invalid soundtrack name";
27832 
27833 		case SEXP_CHECK_INVALID_PERSONA_NAME:
27834 			return "Invalid persona name";
27835 
27836 		case SEXP_CHECK_INVALID_VARIABLE:
27837 			return "Invalid variable name";
27838 
27839 		case SEXP_CHECK_INVALID_VARIABLE_TYPE:
27840 			return "Invalid variable type";
27841 
27842 		case SEXP_CHECK_INVALID_FONT:
27843 			return "Invalid font";
27844 
27845 		case SEXP_CHECK_INVALID_HUD_ELEMENT:
27846 			return "Invalid hud element magic name";
27847 
27848 		case SEXP_CHECK_INVALID_SOUND_ENVIRONMENT:
27849 			return "Invalid sound environment";
27850 
27851 		case SEXP_CHECK_INVALID_SOUND_ENVIRONMENT_OPTION:
27852 			return "Invalid sound environment option";
27853 
27854 		case SEXP_CHECK_INVALID_AUDIO_VOLUME_OPTION:
27855 			return "Invalid audio volume option";
27856 
27857 		case SEXP_CHECK_INVALID_EXPLOSION_OPTION:
27858 			return "Invalid explosion option";
27859 
27860 		case SEXP_CHECK_INVALID_SHIP_EFFECT:
27861 			return "Invalid ship effect name";
27862 
27863 		case SEXP_CHECK_INVALID_TURRET_TARGET_ORDER:
27864 			return "Invalid turret target order";
27865 
27866 		case SEXP_CHECK_INVALID_ARMOR_TYPE:
27867 			return "Invalid armor type";
27868 
27869 		case SEXP_CHECK_INVALID_DAMAGE_TYPE:
27870 			return "Invalid damage type";
27871 
27872 		case SEXP_CHECK_INVALID_HUD_GAUGE:
27873 			return "Invalid hud gauge";
27874 
27875 		case SEXP_CHECK_INVALID_TARGET_PRIORITIES:
27876 			return "Invalid target priorities";
27877 
27878 		case SEXP_CHECK_INVALID_ANIMATION_TYPE:
27879 			return "Invalid animation type";
27880 
27881 		case SEXP_CHECK_INVALID_MISSION_MOOD:
27882 			return "Invalid mission mood";
27883 
27884 		case SEXP_CHECK_INVALID_SHIP_FLAG:
27885 			return "Invalid ship flag";
27886 
27887 		default:
27888 			Warning(LOCATION, "Unhandled sexp error code %d!", num);
27889 			return "Unhandled sexp error code!";
27890 	}
27891 }
27892 
query_sexp_ai_goal_valid(int sexp_ai_goal,int ship_num)27893 int query_sexp_ai_goal_valid(int sexp_ai_goal, int ship_num)
27894 {
27895 	int i, op;
27896 
27897 	for (op=0; op<Num_operators; op++)
27898 		if (Operators[op].value == sexp_ai_goal)
27899 			break;
27900 
27901 	Assert(op < Num_operators);
27902 	for (i=0; i<Num_sexp_ai_goal_links; i++)
27903 		if (Sexp_ai_goal_links[i].op_code == sexp_ai_goal)
27904 			break;
27905 
27906 	Assert(i < Num_sexp_ai_goal_links);
27907 	return ai_query_goal_valid(ship_num, Sexp_ai_goal_links[i].ai_goal);
27908 }
27909 
27910 // Takes an Sexp_node.text pointing to a variable (of form "Sexp_variables[xx]=string" or "Sexp_variables[xx]=number")
27911 // and returns the index into the Sexp_variables array of the actual value
extract_sexp_variable_index(int node)27912 int extract_sexp_variable_index(int node)
27913 {
27914 	char *text = Sexp_nodes[node].text;
27915 	char char_index[8];
27916 	char *start_index;
27917 	int variable_index;
27918 
27919 	// get past the '['
27920 	start_index = text + 15;
27921 	Assert(isdigit(*start_index));
27922 
27923 	int len = 0;
27924 
27925 	while ( *start_index != ']' ) {
27926 		char_index[len++] = *(start_index++);
27927 		Assert(len < 3);
27928 	}
27929 
27930 	Assert(len > 0);
27931 	char_index[len] = 0;	// append null termination to string
27932 
27933 	variable_index = atoi(char_index);
27934 	Assert( (variable_index >= 0) && (variable_index < MAX_SEXP_VARIABLES) );
27935 
27936 	return variable_index;
27937 }
27938 
27939 
27940 /**
27941  * Wrapper around Sexp_node[xx].text for normal and variable
27942  */
CTEXT(int n)27943 char *CTEXT(int n)
27944 {
27945 	int sexp_variable_index = -1;
27946 	char variable_name[TOKEN_LENGTH];
27947 	char *current_argument;
27948 
27949 	if ( n < 0 ) {
27950 		Int3();
27951 		return NULL;
27952 	}
27953 
27954 	// Goober5000 - MWAHAHAHAHAHAHAHA!  Thank you, Volition programmers!  Without
27955 	// the CTEXT wrapper, when-argument would probably be infeasibly difficult to code.
27956 	if (!strcmp(Sexp_nodes[n].text, SEXP_ARGUMENT_STRING))
27957 	{
27958 		if (Fred_running)
27959 		{
27960 			// CTEXT is used when writing sexps to savefiles, so don't translate the argument
27961 			return Sexp_nodes[n].text;
27962 		}
27963 		else
27964 		{
27965 			// make sure we have an argument to replace it with
27966 			if (Sexp_replacement_arguments.empty())
27967 				return Sexp_nodes[n].text;
27968 		}
27969 
27970 		// if the replacement argument is a variable name, get the variable index
27971 		sexp_variable_index = get_index_sexp_variable_name(Sexp_replacement_arguments.back());
27972 
27973 		current_argument = Sexp_replacement_arguments.back();
27974 
27975 		// if the replacement argument is a formatted variable name, get the variable index
27976 		if (current_argument[0] == SEXP_VARIABLE_CHAR)
27977 		{
27978 			get_unformatted_sexp_variable_name(variable_name, current_argument);
27979 			sexp_variable_index = get_index_sexp_variable_name(variable_name);
27980 		}
27981 
27982 		// if we have a variable, return the variable value, else return the regular argument
27983 		if (sexp_variable_index != -1)
27984 			return Sexp_variables[sexp_variable_index].text;
27985 		else
27986 			return Sexp_replacement_arguments.back();
27987 	}
27988 
27989 	// Goober5000 - if not special argument, proceed as normal
27990 	if (Sexp_nodes[n].type & SEXP_FLAG_VARIABLE)
27991 	{
27992 		if (Fred_running)
27993 		{
27994 			sexp_variable_index = get_index_sexp_variable_name(Sexp_nodes[n].text);
27995 			Assert(sexp_variable_index != -1);
27996 		}
27997 		else
27998 		{
27999 			sexp_variable_index = atoi(Sexp_nodes[n].text);
28000 		}
28001 		// Reference a Sexp_variable
28002 		// string format -- "Sexp_variables[xx]=number" or "Sexp_variables[xx]=string", where xx is the index
28003 
28004 		Assert( !(Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_NOT_USED) );
28005 		Assert(Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_SET);
28006 
28007 		if (Log_event) {
28008 			Current_event_log_variable_buffer->push_back(Sexp_variables[sexp_variable_index].text);
28009 			Current_event_log_variable_buffer->push_back(Sexp_variables[sexp_variable_index].variable_name);
28010 		}
28011 
28012 		return Sexp_variables[sexp_variable_index].text;
28013 	}
28014 	else
28015 	{
28016 		return Sexp_nodes[n].text;
28017 	}
28018 }
28019 
28020 
28021 /**
28022  * Set all Sexp_variables to type uninitialized
28023  */
init_sexp_vars()28024 void init_sexp_vars()
28025 {
28026 	for (int i=0; i<MAX_SEXP_VARIABLES; i++) {
28027 		Sexp_variables[i].type = SEXP_VARIABLE_NOT_USED;
28028 		Block_variables[i].type = SEXP_VARIABLE_NOT_USED;
28029 	}
28030 }
28031 
28032 /**
28033  * Add a variable to the block variable array rather than the Sexp_variables array
28034  */
add_block_variable(const char * text,const char * var_name,int type,int index)28035 void add_block_variable(const char *text, const char *var_name, int type, int index)
28036 {
28037 	Assert( (index >= 0) && (index < MAX_SEXP_VARIABLES) );
28038 
28039 	strcpy_s(Block_variables[index].text, text);
28040 	strcpy_s(Block_variables[index].variable_name, var_name);
28041 	Block_variables[index].type &= ~SEXP_VARIABLE_NOT_USED;
28042 	Block_variables[index].type = (type | SEXP_VARIABLE_SET);
28043 
28044 }
28045 
28046 /**
28047  * Add a Sexp_variable to be used in a mission.
28048  *
28049  * This should be called from within mission parse.
28050  */
sexp_add_variable(const char * text,const char * var_name,int type,int index)28051 int sexp_add_variable(const char *text, const char *var_name, int type, int index)
28052 {
28053 	// if index == -1, find next open slot
28054 	if (index == -1) {
28055 		for (int i=0; i<MAX_SEXP_VARIABLES; i++) {
28056 			if (Sexp_variables[i].type == SEXP_VARIABLE_NOT_USED) {
28057 				index = i;
28058 				break;
28059 			}
28060 		}
28061 	} else {
28062 		Assert( (index >= 0) && (index < MAX_SEXP_VARIABLES) );
28063 	}
28064 
28065 	if (index >= 0) {
28066 		strcpy_s(Sexp_variables[index].text, text);
28067 		strcpy_s(Sexp_variables[index].variable_name, var_name);
28068 		Sexp_variables[index].type &= ~SEXP_VARIABLE_NOT_USED;
28069 		Sexp_variables[index].type = (type | SEXP_VARIABLE_SET);
28070 	}
28071 
28072 	return index;
28073 }
28074 
28075 // Goober5000 - minor variant of the above that is now required for variable arrays
sexp_add_array_block_variable(int index,bool is_numeric)28076 void sexp_add_array_block_variable(int index, bool is_numeric)
28077 {
28078 	Assert(index >= 0 && index < MAX_SEXP_VARIABLES);
28079 
28080 	strcpy_s(Sexp_variables[index].text, "");
28081 	strcpy_s(Sexp_variables[index].variable_name, "variable array block");
28082 
28083 	if (is_numeric)
28084 		Sexp_variables[index].type = SEXP_VARIABLE_NUMBER | SEXP_VARIABLE_SET;
28085 	else
28086 		Sexp_variables[index].type = SEXP_VARIABLE_STRING | SEXP_VARIABLE_SET;
28087 }
28088 
28089 /**
28090  * Modify a Sexp_variable to be used in a mission
28091  *
28092  * This should be called in mission when an sexp_variable is to be modified
28093  */
sexp_modify_variable(char * text,int index,bool sexp_callback)28094 void sexp_modify_variable(char *text, int index, bool sexp_callback)
28095 {
28096 	Assert(index >= 0 && index < MAX_SEXP_VARIABLES);
28097 	Assert(Sexp_variables[index].type & SEXP_VARIABLE_SET);
28098 	Assert( !MULTIPLAYER_CLIENT );
28099 
28100 	if (strchr(text, '$') != NULL)
28101 	{
28102 		// we want to use the same variable substitution that's in messages etc.
28103 		SCP_string temp_text = text;
28104 		sexp_replace_variable_names_with_values(temp_text);
28105 
28106 		// copy to original buffer
28107 		int len = temp_text.copy(Sexp_variables[index].text, TOKEN_LENGTH);
28108 		text[len] = 0;
28109 	}
28110 	else
28111 	{
28112 		// no variables, so no substitution
28113 		strcpy_s(Sexp_variables[index].text, text);
28114 	}
28115 	Sexp_variables[index].type |= SEXP_VARIABLE_MODIFIED;
28116 
28117 	// do multi_callback_here
28118 	// if we're called from the sexp code send a SEXP packet (more efficient)
28119 	if( MULTIPLAYER_MASTER && (Sexp_variables[index].type & SEXP_VARIABLE_NETWORK) && sexp_callback) {
28120 		multi_start_callback();
28121 		multi_send_int(index);
28122 		multi_send_string(Sexp_variables[index].text);
28123 		multi_end_callback();
28124 	}
28125 	// otherwise send a SEXP variable packet
28126 	else if ( (Game_mode & GM_MULTIPLAYER) && (Sexp_variables[index].type & SEXP_VARIABLE_NETWORK) ) {
28127 		send_variable_update_packet(index, Sexp_variables[index].text);
28128 	}
28129 }
28130 
multi_sexp_modify_variable()28131 void multi_sexp_modify_variable()
28132 {
28133 	char value[TOKEN_LENGTH];
28134 	int variable_index = -1;
28135 
28136 	// get the data
28137 	multi_get_int(variable_index);
28138 	if (!multi_get_string(value)) {
28139 		return;
28140 	}
28141 
28142 	// set the sexp_variable
28143 	if ( (variable_index >= 0) && (variable_index < MAX_SEXP_VARIABLES) ) {
28144 		// maybe create it first
28145 		if (!(Sexp_variables[variable_index].type & SEXP_VARIABLE_SET)) {
28146 			mprintf(("Warning; received multi packet for variable index which is not set!  Assuming this should be an array block variable...\n"));
28147 			sexp_add_array_block_variable(variable_index, can_construe_as_integer(value));
28148 		}
28149 
28150 		strcpy_s(Sexp_variables[variable_index].text, value);
28151 	}
28152 }
28153 
sexp_modify_variable(int n)28154 void sexp_modify_variable(int n)
28155 {
28156 	int sexp_variable_index;
28157 	int new_number;
28158 	char *new_text;
28159 	char number_as_str[TOKEN_LENGTH];
28160 
28161 	Assert(n >= 0);
28162 
28163 	// Only do single player or multi host
28164 	if ( MULTIPLAYER_CLIENT )
28165 		return;
28166 
28167 	// get sexp_variable index
28168 	Assert(Sexp_nodes[n].first == -1);
28169 	sexp_variable_index = atoi(Sexp_nodes[n].text);
28170 
28171 	// verify variable set
28172 	Assert(Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_SET);
28173 
28174 	if (Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_NUMBER)
28175 	{
28176 		// get new numerical value
28177 		new_number = eval_sexp(CDR(n));
28178 		sprintf(number_as_str, "%d", new_number);
28179 
28180 		// assign to variable
28181 		sexp_modify_variable(number_as_str, sexp_variable_index);
28182 	}
28183 	else if (Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_STRING)
28184 	{
28185 		// get new string
28186 		new_text = CTEXT(CDR(n));
28187 
28188 		// assign to variable
28189 		sexp_modify_variable(new_text, sexp_variable_index);
28190 	}
28191 	else
28192 	{
28193 		Error(LOCATION, "Invalid variable type.\n");
28194 	}
28195 }
28196 
is_sexp_node_numeric(int node)28197 bool is_sexp_node_numeric(int node)
28198 {
28199 	Assert(node >= 0);
28200 
28201 	// make the common case fast: if the node has a CAR node, that means it uses an operator;
28202 	// and operators cannot currently return strings
28203 	if (Sexp_nodes[node].first >= 0)
28204 		return true;
28205 
28206 	// if the node text is numeric, the node is too
28207 	if (can_construe_as_integer(Sexp_nodes[node].text))
28208 		return true;
28209 
28210 	// otherwise it's gotta be text
28211 	return false;
28212 }
28213 
28214 // By Goober5000. Very similar to sexp_modify_variable(). Even uses the same multi code! :)
sexp_set_variable_by_index(int node)28215 void sexp_set_variable_by_index(int node)
28216 {
28217 	int sexp_variable_index;
28218 	int new_number;
28219 	char *new_text;
28220 	char number_as_str[TOKEN_LENGTH];
28221 
28222 	Assert(node >= 0);
28223 
28224 	// Only do single player or multi host
28225 	if ( MULTIPLAYER_CLIENT )
28226 		return;
28227 
28228 	// get sexp_variable index
28229 	sexp_variable_index = eval_num(node);
28230 
28231 	// check range
28232 	if (sexp_variable_index < 0 || sexp_variable_index >= MAX_SEXP_VARIABLES)
28233 	{
28234 		Warning(LOCATION, "set-variable-by-index: sexp variable index %d out of range!  min is 0; max is %d", sexp_variable_index, MAX_SEXP_VARIABLES - 1);
28235 		return;
28236 	}
28237 
28238 	// verify variable set
28239 	if (!(Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_SET))
28240 	{
28241 		// well phooey.  go ahead and create it
28242 		sexp_add_array_block_variable(sexp_variable_index, is_sexp_node_numeric(CDR(node)));
28243 	}
28244 
28245 	if (Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_NUMBER)
28246 	{
28247 		// get new numerical value
28248 		new_number = eval_sexp(CDR(node));
28249 		sprintf(number_as_str, "%d", new_number);
28250 
28251 		// assign to variable
28252 		sexp_modify_variable(number_as_str, sexp_variable_index);
28253 	}
28254 	else if (Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_STRING)
28255 	{
28256 		// get new string
28257 		new_text = CTEXT(CDR(node));
28258 
28259 		// assign to variable
28260 		sexp_modify_variable(new_text, sexp_variable_index);
28261 	}
28262 	else
28263 	{
28264 		Error(LOCATION, "Invalid variable type.\n");
28265 	}
28266 }
28267 
28268 // Goober5000
sexp_get_variable_by_index(int node)28269 int sexp_get_variable_by_index(int node)
28270 {
28271 	int sexp_variable_index;
28272 
28273 	Assert(node >= 0);
28274 
28275 	// get sexp_variable index
28276 	sexp_variable_index = eval_num(node);
28277 
28278 	// check range
28279 	if (sexp_variable_index < 0 || sexp_variable_index >= MAX_SEXP_VARIABLES)
28280 	{
28281 		Warning(LOCATION, "get-variable-by-index: sexp variable index %d out of range!  min is 0; max is %d", sexp_variable_index, MAX_SEXP_VARIABLES - 1);
28282 		return 0;
28283 	}
28284 
28285 	if (Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_NOT_USED)
28286 	{
28287 		mprintf(("warning: retrieving a value from a sexp variable which is not in use!\n"));
28288 	}
28289 	else if (!(Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_SET))
28290 	{
28291 		mprintf(("warning: retrieving a value from a sexp variable which is not set!\n"));
28292 	}
28293 
28294 	if (Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_STRING)
28295 	{
28296 		mprintf(("warning: variable %d is a string but it is not possible to return a string value through a sexp!\n", sexp_variable_index));
28297 		return SEXP_NAN_FOREVER;
28298 	}
28299 
28300 	return atoi(Sexp_variables[sexp_variable_index].text);
28301 }
28302 
28303 // Goober5000
28304 // (yes, this reuses a lot of code, but it's a major pain to consolidate it)
sexp_copy_variable_from_index(int node)28305 void sexp_copy_variable_from_index(int node)
28306 {
28307 	int from_index;
28308 	int to_index;
28309 
28310 	Assert(node >= 0);
28311 
28312 	// Only do single player or multi host
28313 	if ( MULTIPLAYER_CLIENT )
28314 		return;
28315 
28316 	// get sexp_variable index
28317 	from_index = eval_num(node);
28318 
28319 	// check range
28320 	if (from_index < 0 || from_index >= MAX_SEXP_VARIABLES)
28321 	{
28322 		Warning(LOCATION, "copy-variable-from-index: sexp variable index %d out of range!  min is 0; max is %d", from_index, MAX_SEXP_VARIABLES - 1);
28323 		return;
28324 	}
28325 
28326 	if (Sexp_variables[from_index].type & SEXP_VARIABLE_NOT_USED)
28327 	{
28328 		mprintf(("warning: retrieving a value from a sexp variable which is not in use!\n"));
28329 	}
28330 	else if (!(Sexp_variables[from_index].type & SEXP_VARIABLE_SET))
28331 	{
28332 		mprintf(("warning: retrieving a value from a sexp variable which is not set!\n"));
28333 	}
28334 
28335 	// now get the variable we are modifying
28336 	to_index = atoi(Sexp_nodes[CDR(node)].text);
28337 
28338 	// verify variable set
28339 	Assert(Sexp_variables[to_index].type & SEXP_VARIABLE_SET);
28340 
28341 	// verify matching types
28342 	if ( ((Sexp_variables[from_index].type & SEXP_VARIABLE_NUMBER) && !(Sexp_variables[to_index].type & SEXP_VARIABLE_NUMBER))
28343 		|| ((Sexp_variables[from_index].type & SEXP_VARIABLE_STRING) && !(Sexp_variables[to_index].type & SEXP_VARIABLE_STRING)) )
28344 	{
28345 		Warning(LOCATION, "copy-variable-from-index: cannot copy variables of different types!  source = '%s', destination = '%s'", Sexp_variables[from_index].variable_name, Sexp_variables[to_index].variable_name);
28346 		return;
28347 	}
28348 
28349 	// assign to variable
28350 	sexp_modify_variable(Sexp_variables[from_index].text, to_index);
28351 }
28352 
28353 // Goober5000
28354 // (yes, this reuses a lot of code, but it's a major pain to consolidate it)
28355 // (and yes, that reused a comment :p)
sexp_copy_variable_between_indexes(int node)28356 void sexp_copy_variable_between_indexes(int node)
28357 {
28358 	int from_index;
28359 	int to_index;
28360 
28361 	Assert(node >= 0);
28362 
28363 	// Only do single player or multi host
28364 	if ( MULTIPLAYER_CLIENT )
28365 		return;
28366 
28367 	// get sexp_variable indexes
28368 	from_index = eval_num(node);
28369 	to_index = eval_num(CDR(node));
28370 
28371 	// check ranges
28372 	if (from_index < 0 || from_index >= MAX_SEXP_VARIABLES)
28373 	{
28374 		Warning(LOCATION, "copy-variable-between-indexes: sexp variable index %d out of range!  min is 0; max is %d", from_index, MAX_SEXP_VARIABLES - 1);
28375 		return;
28376 	}
28377 	if (to_index < 0 || to_index >= MAX_SEXP_VARIABLES)
28378 	{
28379 		Warning(LOCATION, "copy-variable-between-indexes: sexp variable index %d out of range!  min is 0; max is %d", to_index, MAX_SEXP_VARIABLES - 1);
28380 		return;
28381 	}
28382 
28383 	if (Sexp_variables[from_index].type & SEXP_VARIABLE_NOT_USED)
28384 	{
28385 		mprintf(("warning: retrieving a value from a sexp variable which is not in use!\n"));
28386 	}
28387 	else if (!(Sexp_variables[from_index].type & SEXP_VARIABLE_SET))
28388 	{
28389 		mprintf(("warning: retrieving a value from a sexp variable which is not set!\n"));
28390 	}
28391 
28392 	if (!(Sexp_variables[to_index].type & SEXP_VARIABLE_SET))
28393 	{
28394 		// well phooey.  go ahead and create it
28395 		sexp_add_array_block_variable(to_index, (Sexp_variables[from_index].type & SEXP_VARIABLE_NUMBER) != 0);
28396 	}
28397 
28398 	// verify matching types
28399 	if ( ((Sexp_variables[from_index].type & SEXP_VARIABLE_NUMBER) && !(Sexp_variables[to_index].type & SEXP_VARIABLE_NUMBER))
28400 		|| ((Sexp_variables[from_index].type & SEXP_VARIABLE_STRING) && !(Sexp_variables[to_index].type & SEXP_VARIABLE_STRING)) )
28401 	{
28402 		Warning(LOCATION, "copy-variable-between-indexes: cannot copy variables of different types!  source = '%s', destination = '%s'", Sexp_variables[from_index].variable_name, Sexp_variables[to_index].variable_name);
28403 		return;
28404 	}
28405 
28406 	// assign to variable
28407 	sexp_modify_variable(Sexp_variables[from_index].text, to_index);
28408 }
28409 
28410 // Different type needed for Fred (1) allow modification of type (2) no callback required
sexp_fred_modify_variable(const char * text,const char * var_name,int index,int type)28411 void sexp_fred_modify_variable(const char *text, const char *var_name, int index, int type)
28412 {
28413 	Assert(index >= 0 && index < MAX_SEXP_VARIABLES);
28414 	Assert(Sexp_variables[index].type & SEXP_VARIABLE_SET);
28415 	Assert( (type & SEXP_VARIABLE_NUMBER) || (type & SEXP_VARIABLE_STRING) );
28416 
28417 	strcpy_s(Sexp_variables[index].text, text);
28418 	strcpy_s(Sexp_variables[index].variable_name, var_name);
28419 	Sexp_variables[index].type = (SEXP_VARIABLE_SET | SEXP_VARIABLE_MODIFIED | type);
28420 }
28421 
28422 /**
28423  * Given a sexp node return the index of the variable at that node, -1 if not found
28424  */
get_index_sexp_variable_from_node(int node)28425 int get_index_sexp_variable_from_node (int node)
28426 {
28427 	int var_index;
28428 
28429 	if (!(Sexp_nodes[node].type & SEXP_FLAG_VARIABLE)) {
28430 		return -1;
28431 	}
28432 
28433 	if (Fred_running) {
28434 		var_index = get_index_sexp_variable_name(Sexp_nodes[node].text);
28435 	}
28436 	else {
28437 		var_index = atoi(Sexp_nodes[node].text);
28438 	}
28439 
28440 	return var_index;
28441 }
28442 
28443 /**
28444  * Return index of sexp_variable_name, -1 if not found
28445  */
get_index_sexp_variable_name(const char * text)28446 int get_index_sexp_variable_name(const char *text)
28447 {
28448 	for (int i=0; i<MAX_SEXP_VARIABLES; i++) {
28449 		if (Sexp_variables[i].type & SEXP_VARIABLE_SET) {
28450 			// check case sensitive
28451 			if ( !strcmp(Sexp_variables[i].variable_name, text) ) {
28452 				return i;
28453 			}
28454 		}
28455 	}
28456 
28457 	// not found
28458 	return -1;
28459 }
28460 
28461 /**
28462  * Return index of sexp_variable_name, -1 if not found
28463  */
get_index_sexp_variable_name(SCP_string & text)28464 int get_index_sexp_variable_name(SCP_string &text)
28465 {
28466 	for (int i=0; i<MAX_SEXP_VARIABLES; i++) {
28467 		if (Sexp_variables[i].type & SEXP_VARIABLE_SET) {
28468 			// check case sensitive
28469 			if ( text == Sexp_variables[i].variable_name ) {
28470 				return i;
28471 			}
28472 		}
28473 	}
28474 
28475 	// not found
28476 	return -1;
28477 }
28478 
28479 // Goober5000 - tests whether a variable name starts here
28480 // return index of sexp_variable_name, -1 if not found
get_index_sexp_variable_name_special(const char * startpos)28481 int get_index_sexp_variable_name_special(const char *startpos)
28482 {
28483 	for (int i = MAX_SEXP_VARIABLES - 1; i >= 0; i--) {
28484 		if (Sexp_variables[i].type & SEXP_VARIABLE_SET) {
28485 			// check case sensitive
28486 			// check number of chars in variable name
28487 			if ( !strncmp(startpos, Sexp_variables[i].variable_name, strlen(Sexp_variables[i].variable_name)) ) {
28488 				return i;
28489 			}
28490 		}
28491 	}
28492 
28493 	// not found
28494 	return -1;
28495 }
28496 
28497 // Goober5000 - tests whether a variable name starts here
28498 // return index of sexp_variable_name, -1 if not found
get_index_sexp_variable_name_special(SCP_string & text,size_t startpos)28499 int get_index_sexp_variable_name_special(SCP_string &text, size_t startpos)
28500 {
28501 	for (int i = MAX_SEXP_VARIABLES - 1; i >= 0; i--) {
28502 		if (Sexp_variables[i].type & SEXP_VARIABLE_SET) {
28503 			// check case sensitive
28504 			// check that the variable name starts here, as opposed to farther down the string
28505 			size_t pos = text.find(Sexp_variables[i].variable_name, startpos);
28506 			if (pos != SCP_string::npos && pos == startpos) {
28507 				return i;
28508 			}
28509 		}
28510 	}
28511 
28512 	// not found
28513 	return -1;
28514 }
28515 
28516 // Goober5000
sexp_replace_variable_names_with_values(char * text,int max_len)28517 bool sexp_replace_variable_names_with_values(char *text, int max_len)
28518 {
28519 	Assert(text != NULL);
28520 	Assert(max_len >= 0);
28521 
28522 	bool replaced_anything = false;
28523 	char *pos = text;
28524 	do {
28525 		// look for the meta-character
28526 		pos = strchr(pos, '$');
28527 
28528 		// found?
28529 		if (pos != NULL)
28530 		{
28531 			// see if a variable starts at the next char
28532 			int var_index = get_index_sexp_variable_name_special(pos+1);
28533 			if (var_index >= 0)
28534 			{
28535 				// get the replacement string ($variable)
28536 				char what_to_replace[TOKEN_LENGTH+1];
28537 				memset(what_to_replace, 0, TOKEN_LENGTH+1);
28538 				strncpy(what_to_replace, pos, strlen(Sexp_variables[var_index].variable_name) + 1);
28539 
28540 				// replace it
28541 				pos = text + replace_one(text, what_to_replace, Sexp_variables[var_index].text, max_len);
28542 				replaced_anything = true;
28543 			}
28544 			// no match... so keep iterating along the string
28545 			else
28546 			{
28547 				pos++;
28548 			}
28549 		}
28550 	} while (pos != NULL);
28551 
28552 	return replaced_anything;
28553 }
28554 
28555 // Goober5000
sexp_replace_variable_names_with_values(SCP_string & text)28556 bool sexp_replace_variable_names_with_values(SCP_string &text)
28557 {
28558 	bool replaced_anything = false;
28559 
28560 	size_t lookHere = 0;
28561 	size_t foundHere;
28562 
28563 	do {
28564 		// look for the meta-character
28565 		foundHere = text.find('$', lookHere);
28566 
28567 		// found?
28568 		if (foundHere != SCP_string::npos)
28569 		{
28570 			// see if a variable starts at the next char
28571 			int var_index = get_index_sexp_variable_name_special(text, foundHere+1);
28572 			if (var_index >= 0)
28573 			{
28574 				// replace $variable with the value
28575 				text.replace(foundHere, strlen(Sexp_variables[var_index].variable_name)+1, Sexp_variables[var_index].text);
28576 				replaced_anything = true;
28577 
28578 				lookHere = foundHere + strlen(Sexp_variables[var_index].text);
28579 			}
28580 			// no match... so keep iterating along the string
28581 			else
28582 			{
28583 				lookHere = foundHere + 1;
28584 			}
28585 		}
28586 	} while (foundHere != SCP_string::npos);
28587 
28588 	return replaced_anything;
28589 }
28590 
28591 // returns the index of the nth variable of the type given or -1 if there aren't that many
28592 // NOTE : Not 0th order. If you want the 4th string variable call it as get_nth_variable_index(4, SEXP_VARIABLE_STRING)
get_nth_variable_index(int nth,int variable_type)28593 int get_nth_variable_index(int nth, int variable_type)
28594 {
28595 	// Loop through Sexp_variables until we have found the one corresponding to the argument
28596 	Assert ((nth > 0) && (nth < MAX_SEXP_VARIABLES));
28597 	for (int i=0; i < MAX_SEXP_VARIABLES; i++)	{
28598 		if ((Sexp_variables[i].type & variable_type)) {
28599 			nth--;
28600 		}
28601 
28602 		if (!nth) {
28603 			return i;
28604 		}
28605 	}
28606 	return -1;
28607 }
28608 
28609 /**
28610  * Count number of sexp_variables that are set
28611  */
sexp_variable_count()28612 int sexp_variable_count()
28613 {
28614 	int count = 0;
28615 
28616 	for (int i=0; i<MAX_SEXP_VARIABLES; i++) {
28617 		if ( Sexp_variables[i].type & SEXP_VARIABLE_SET) {
28618 			count++;
28619 		}
28620 	}
28621 
28622 	return count;
28623 }
28624 
28625 /**
28626  * Count number of campaign-persistent sexp_variables that are set
28627  */
sexp_campaign_persistent_variable_count()28628 int sexp_campaign_persistent_variable_count()
28629 {
28630 	int count = 0;
28631 
28632 	for (int i=0; i<MAX_SEXP_VARIABLES; i++) {
28633 		if ( (Sexp_variables[i].type & SEXP_VARIABLE_SET) && (Sexp_variables[i].type & SEXP_VARIABLE_CAMPAIGN_PERSISTENT) ) {
28634 			count++;
28635 		}
28636 	}
28637 
28638 	return count;
28639 }
28640 
28641 /**
28642  * Given an index in Sexp_variables, returns the number variables of a type in the array until this point
28643  */
sexp_variable_typed_count(int sexp_variables_index,int variable_type)28644 int sexp_variable_typed_count(int sexp_variables_index, int variable_type)
28645 {
28646 	Assert ((sexp_variables_index >= 0) && (sexp_variables_index < MAX_SEXP_VARIABLES));
28647 	// Loop through Sexp_variables until we have found the one corresponding to the argument
28648 	int count = 0;
28649 	for (int i=0; i < MAX_SEXP_VARIABLES; i++)	{
28650 		if (!(Sexp_variables[i].type & variable_type)) {
28651 			continue;
28652 		}
28653 
28654 		if (i == sexp_variables_index) {
28655 			return count ;
28656 		}
28657 		count++;
28658 	}
28659 	// shouldn't ever get here
28660 	Int3();
28661 	return -1;
28662 }
28663 
28664 /**
28665  * Delete sexp_variable from active
28666  */
sexp_variable_delete(int index)28667 void sexp_variable_delete(int index)
28668 {
28669 	Assert(Sexp_variables[index].type & SEXP_VARIABLE_SET);
28670 
28671 	Sexp_variables[index].type = SEXP_VARIABLE_NOT_USED;
28672 }
28673 
sexp_var_compare(const void * var1,const void * var2)28674 int sexp_var_compare(const void *var1, const void *var2)
28675 {
28676 	int set1, set2;
28677 	sexp_variable *sexp_var1, *sexp_var2;
28678 
28679 	sexp_var1 = (sexp_variable*) var1;
28680 	sexp_var2 = (sexp_variable*) var2;
28681 
28682 	set1 = sexp_var1->type & SEXP_VARIABLE_SET;
28683 	set2 = sexp_var2->type & SEXP_VARIABLE_SET;
28684 
28685 	if (!set1 && !set2) {
28686 		return 0;
28687 	} else if (set1 && !set2) {
28688 		return -1;
28689 	} else if (!set1 && set2) {
28690 		return 1;
28691 	} else {
28692 		return stricmp( sexp_var1->variable_name, sexp_var2->variable_name);
28693 	}
28694 }
28695 
28696 /**
28697  * Sort sexp_variable list lexigraphically, with set before unset
28698  */
sexp_variable_sort()28699 void sexp_variable_sort()
28700 {
28701 	insertion_sort( (void *)Sexp_variables, (size_t)(MAX_SEXP_VARIABLES), sizeof(sexp_variable), sexp_var_compare );
28702 }
28703 
28704 /**
28705  * Evaluate number which may result from an operator or may be text
28706  */
eval_num(int n)28707 int eval_num(int n)
28708 {
28709 	if (n < 0)
28710 	{
28711 		Int3();
28712 		return 0;
28713 	}
28714 
28715 	if (CAR(n) != -1)				// if argument is a sexp
28716 		return eval_sexp(CAR(n));
28717 	else
28718 		return atoi(CTEXT(n));		// otherwise, just get the number
28719 }
28720 
28721 // Goober5000
get_sexp_id(char * sexp_name)28722 int get_sexp_id(char *sexp_name)
28723 {
28724 	for (int i = 0; i < Num_operators; i++)
28725 	{
28726 		if (!stricmp(sexp_name, Operators[i].text))
28727 			return Operators[i].value;
28728 	}
28729 	return -1;
28730 }
28731 
28732 // Goober5000
get_category(int sexp_id)28733 int get_category(int sexp_id)
28734 {
28735 	int category = (sexp_id & OP_CATEGORY_MASK);
28736 
28737 	// hack so that CHANGE and CHANGE2 show up in the same menu
28738 	if (category == OP_CATEGORY_CHANGE2)
28739 		category = OP_CATEGORY_CHANGE;
28740 
28741 	return category;
28742 }
28743 
28744 // Goober5000
category_of_subcategory(int subcategory_id)28745 int category_of_subcategory(int subcategory_id)
28746 {
28747 	int category = (subcategory_id & OP_CATEGORY_MASK);
28748 
28749 	// hack so that CHANGE and CHANGE2 show up in the same menu
28750 	if (category == OP_CATEGORY_CHANGE2)
28751 		category = OP_CATEGORY_CHANGE;
28752 
28753 	return category;
28754 }
28755 
28756 // Goober5000 - for FRED2 menu subcategories
get_subcategory(int sexp_id)28757 int get_subcategory(int sexp_id)
28758 {
28759 	switch(sexp_id)
28760 	{
28761 
28762 		case OP_SEND_MESSAGE_LIST:
28763 		case OP_SEND_MESSAGE:
28764 		case OP_SEND_RANDOM_MESSAGE:
28765 		case OP_SCRAMBLE_MESSAGES:
28766 		case OP_UNSCRAMBLE_MESSAGES:
28767 		case OP_ENABLE_BUILTIN_MESSAGES:
28768 		case OP_DISABLE_BUILTIN_MESSAGES:
28769 		case OP_SET_DEATH_MESSAGE:
28770 		case OP_SET_PERSONA:
28771 		case OP_SET_MISSION_MOOD:
28772 			return CHANGE_SUBCATEGORY_MESSAGING;
28773 
28774 
28775 		case OP_ADD_GOAL:
28776 		case OP_REMOVE_GOAL:
28777 		case OP_CLEAR_GOALS:
28778 		case OP_GOOD_REARM_TIME:
28779 		case OP_GOOD_SECONDARY_TIME:
28780 		case OP_CHANGE_AI_CLASS:
28781 		case OP_PLAYER_USE_AI:
28782 		case OP_PLAYER_NOT_USE_AI:
28783 		case OP_SET_PLAYER_ORDERS:
28784 		case OP_CAP_WAYPOINT_SPEED:
28785 			return CHANGE_SUBCATEGORY_AI_CONTROL;
28786 
28787 
28788 
28789 		case OP_ALTER_SHIP_FLAG:
28790 		case OP_PROTECT_SHIP:
28791 		case OP_UNPROTECT_SHIP:
28792 		case OP_BEAM_PROTECT_SHIP:
28793 		case OP_BEAM_UNPROTECT_SHIP:
28794 		case OP_TURRET_PROTECT_SHIP:
28795 		case OP_TURRET_UNPROTECT_SHIP:
28796 		case OP_SHIP_INVISIBLE:
28797 		case OP_SHIP_VISIBLE:
28798 		case OP_SHIP_STEALTHY:
28799 		case OP_SHIP_UNSTEALTHY:
28800 		case OP_FRIENDLY_STEALTH_INVISIBLE:
28801 		case OP_FRIENDLY_STEALTH_VISIBLE:
28802 		case OP_PRIMITIVE_SENSORS_SET_RANGE:
28803 		case OP_SHIP_BOMB_TARGETABLE:
28804 		case OP_SHIP_BOMB_UNTARGETABLE:
28805 		case OP_KAMIKAZE:
28806 		case OP_CHANGE_IFF:
28807 		case OP_CHANGE_IFF_COLOR:
28808 		case OP_ADD_REMOVE_ESCORT:
28809 		case OP_SHIP_CHANGE_ALT_NAME:
28810 		case OP_SHIP_CHANGE_CALLSIGN:
28811 		case OP_SHIP_TAG:
28812 		case OP_SHIP_UNTAG:
28813 		case OP_SET_ARRIVAL_INFO:
28814 		case OP_SET_DEPARTURE_INFO:
28815 			return CHANGE_SUBCATEGORY_SHIP_STATUS;
28816 
28817 		case OP_SET_WEAPON_ENERGY:
28818 		case OP_SET_SHIELD_ENERGY:
28819 		case OP_SET_PLAYER_THROTTLE_SPEED:
28820 		case OP_SET_AFTERBURNER_ENERGY:
28821 		case OP_SET_SUBSPACE_DRIVE:
28822 		case OP_SET_SPECIAL_WARPOUT_NAME:
28823 		case OP_SET_PRIMARY_WEAPON:		// Karajorma
28824 		case OP_SET_SECONDARY_WEAPON:	// Karajorma
28825 		case OP_SET_PRIMARY_AMMO:		// Karajorma
28826 		case OP_SET_SECONDARY_AMMO:		// Karajorma
28827 		case OP_SET_NUM_COUNTERMEASURES: // Karajorma
28828 		case OP_LOCK_PRIMARY_WEAPON:
28829 		case OP_UNLOCK_PRIMARY_WEAPON:
28830 		case OP_LOCK_SECONDARY_WEAPON:
28831 		case OP_UNLOCK_SECONDARY_WEAPON:
28832 		case OP_LOCK_AFTERBURNER:	// KeldorKatarn
28833 		case OP_UNLOCK_AFTERBURNER:	// KeldorKatarn
28834 		case OP_SHIELDS_ON:
28835 		case OP_SHIELDS_OFF:
28836 		case OP_FORCE_GLIDE:
28837 		case OP_DISABLE_ETS:
28838 		case OP_ENABLE_ETS:
28839 		case OP_WARP_BROKEN:
28840 		case OP_WARP_NOT_BROKEN:
28841 		case OP_WARP_NEVER:
28842 		case OP_WARP_ALLOWED:
28843 		case OP_SET_ETS_VALUES:
28844 			return CHANGE_SUBCATEGORY_SHIELDS_ENGINES_AND_WEAPONS;
28845 
28846 		case OP_SHIP_INVULNERABLE:
28847 		case OP_SHIP_VULNERABLE:
28848 		case OP_SHIP_GUARDIAN:
28849 		case OP_SHIP_NO_GUARDIAN:
28850 		case OP_SHIP_GUARDIAN_THRESHOLD:
28851 		case OP_SHIP_SUBSYS_GUARDIAN_THRESHOLD:
28852 		case OP_SELF_DESTRUCT:
28853 		case OP_DESTROY_INSTANTLY:
28854 		case OP_DESTROY_SUBSYS_INSTANTLY:
28855 		case OP_SABOTAGE_SUBSYSTEM:
28856 		case OP_REPAIR_SUBSYSTEM:
28857 		case OP_SHIP_COPY_DAMAGE:
28858 		case OP_SET_SUBSYSTEM_STRNGTH:
28859 		case OP_SUBSYS_SET_RANDOM:
28860 		case OP_LOCK_ROTATING_SUBSYSTEM:
28861 		case OP_FREE_ROTATING_SUBSYSTEM:
28862 		case OP_REVERSE_ROTATING_SUBSYSTEM:
28863 		case OP_ROTATING_SUBSYS_SET_TURN_TIME:
28864 		case OP_TRIGGER_SUBMODEL_ANIMATION:
28865 		case OP_CHANGE_SUBSYSTEM_NAME:
28866 		case OP_SHIP_SUBSYS_TARGETABLE:
28867 		case OP_SHIP_SUBSYS_UNTARGETABLE:
28868 		case OP_SHIP_SUBSYS_NO_REPLACE:
28869 		case OP_SHIP_SUBSYS_NO_LIVE_DEBRIS:
28870 		case OP_SHIP_SUBSYS_VANISHED:
28871 		case OP_SHIP_SUBSYS_IGNORE_IF_DEAD:
28872 		case OP_AWACS_SET_RADIUS:
28873 			return CHANGE_SUBCATEGORY_SUBSYSTEMS;
28874 
28875 		case OP_TRANSFER_CARGO:
28876 		case OP_EXCHANGE_CARGO:
28877 		case OP_SET_CARGO:
28878 		case OP_JETTISON_CARGO:
28879 		case OP_SET_DOCKED:
28880 		case OP_CARGO_NO_DEPLETE:
28881 		case OP_SET_SCANNED:
28882 		case OP_SET_UNSCANNED:
28883 			return CHANGE_SUBCATEGORY_CARGO;
28884 
28885 
28886 		case OP_SET_ARMOR_TYPE:
28887 		case OP_WEAPON_SET_DAMAGE_TYPE:
28888 		case OP_SHIP_SET_DAMAGE_TYPE:
28889 		case OP_SHIP_SHOCKWAVE_SET_DAMAGE_TYPE:
28890 		case OP_FIELD_SET_DAMAGE_TYPE:
28891 			return CHANGE_SUBCATEGORY_ARMOR_AND_DAMAGE_TYPES;
28892 
28893 		case OP_BEAM_FIRE:
28894 		case OP_BEAM_FIRE_COORDS:
28895 		case OP_BEAM_FREE:
28896 		case OP_BEAM_FREE_ALL:
28897 		case OP_BEAM_LOCK:
28898 		case OP_BEAM_LOCK_ALL:
28899 		case OP_TURRET_FREE:
28900 		case OP_TURRET_FREE_ALL:
28901 		case OP_TURRET_LOCK:
28902 		case OP_TURRET_LOCK_ALL:
28903 		case OP_TURRET_TAGGED_ONLY_ALL:
28904 		case OP_TURRET_TAGGED_CLEAR_ALL:
28905 		case OP_TURRET_TAGGED_SPECIFIC:
28906 		case OP_TURRET_TAGGED_CLEAR_SPECIFIC:
28907 		case OP_TURRET_CHANGE_WEAPON:
28908 		case OP_TURRET_SET_DIRECTION_PREFERENCE:
28909 		case OP_TURRET_SET_RATE_OF_FIRE:
28910 		case OP_TURRET_SET_OPTIMUM_RANGE:
28911 		case OP_TURRET_SET_TARGET_PRIORITIES:
28912 		case OP_TURRET_SET_TARGET_ORDER:
28913 		case OP_SHIP_TURRET_TARGET_ORDER:
28914 		case OP_TURRET_SUBSYS_TARGET_DISABLE:
28915 		case OP_TURRET_SUBSYS_TARGET_ENABLE:
28916 			return CHANGE_SUBCATEGORY_BEAMS_AND_TURRETS;
28917 
28918 
28919 
28920 		case OP_CHANGE_SHIP_CLASS:
28921 		case OP_DEACTIVATE_GLOW_MAPS:
28922 		case OP_ACTIVATE_GLOW_MAPS:
28923 		case OP_DEACTIVATE_GLOW_POINTS:
28924 		case OP_ACTIVATE_GLOW_POINTS:
28925 		case OP_DEACTIVATE_GLOW_POINT_BANK:
28926 		case OP_ACTIVATE_GLOW_POINT_BANK:
28927 		case OP_SET_THRUSTERS:
28928 		case OP_DONT_COLLIDE_INVISIBLE:
28929 		case OP_COLLIDE_INVISIBLE:
28930 		case OP_ADD_TO_COLGROUP:
28931 		case OP_REMOVE_FROM_COLGROUP:
28932 		case OP_GET_COLGROUP_ID:
28933 		case OP_CHANGE_TEAM_COLOR:
28934 			return CHANGE_SUBCATEGORY_MODELS_AND_TEXTURES;
28935 
28936 
28937 		case OP_SET_OBJECT_POSITION:
28938 		case OP_SET_OBJECT_ORIENTATION:
28939 		case OP_SET_OBJECT_FACING:
28940 		case OP_SET_OBJECT_FACING_OBJECT:
28941 		case OP_SET_OBJECT_SPEED_X:
28942 		case OP_SET_OBJECT_SPEED_Y:
28943 		case OP_SET_OBJECT_SPEED_Z:
28944 		case OP_SHIP_MANEUVER:
28945 		case OP_SHIP_ROT_MANEUVER:
28946 		case OP_SHIP_LAT_MANEUVER:
28947 		case OP_SET_MOBILE:
28948 		case OP_SET_IMMOBILE:
28949 			return CHANGE_SUBCATEGORY_COORDINATE_MANIPULATION;
28950 
28951 		case OP_INVALIDATE_GOAL:
28952 		case OP_VALIDATE_GOAL:
28953 		case OP_RED_ALERT:
28954 		case OP_END_MISSION:
28955 		case OP_FORCE_JUMP:
28956 		case OP_END_CAMPAIGN:
28957 		case OP_SET_DEBRIEFING_TOGGLED:
28958 		case OP_ALLOW_TREASON:
28959 		case OP_GRANT_PROMOTION:
28960 		case OP_GRANT_MEDAL:
28961 		case OP_ALLOW_SHIP:
28962 		case OP_ALLOW_WEAPON:
28963 		case OP_TECH_ADD_SHIP:
28964 		case OP_TECH_ADD_WEAPON:
28965 		case OP_TECH_ADD_INTEL:
28966 		case OP_TECH_ADD_INTEL_XSTR:
28967 		case OP_TECH_RESET_TO_DEFAULT:
28968 		case OP_CHANGE_PLAYER_SCORE:
28969 		case OP_CHANGE_TEAM_SCORE:
28970 		case OP_SET_RESPAWNS:
28971 			return CHANGE_SUBCATEGORY_MISSION_AND_CAMPAIGN;
28972 
28973 		case OP_CHANGE_SOUNDTRACK:
28974 		case OP_PLAY_SOUND_FROM_TABLE:
28975 		case OP_PLAY_SOUND_FROM_FILE:
28976 		case OP_CLOSE_SOUND_FROM_FILE:
28977 		case OP_SET_SOUND_ENVIRONMENT:
28978 		case OP_UPDATE_SOUND_ENVIRONMENT:
28979 		case OP_ADJUST_AUDIO_VOLUME:
28980 			return CHANGE_SUBCATEGORY_MUSIC_AND_SOUND;
28981 
28982 
28983 		case OP_HUD_DISABLE:
28984 		case OP_HUD_DISABLE_EXCEPT_MESSAGES:
28985 		case OP_HUD_SET_CUSTOM_GAUGE_ACTIVE:
28986 		case OP_HUD_SET_RETAIL_GAUGE_ACTIVE:
28987 		case OP_HUD_SET_TEXT:
28988 		case OP_HUD_SET_TEXT_NUM:
28989 		case OP_HUD_SET_MESSAGE:
28990 		case OP_HUD_SET_DIRECTIVE:
28991 		case OP_HUD_SET_FRAME:
28992 		case OP_HUD_SET_COLOR:
28993 		case OP_HUD_SET_COORDS:
28994 		case OP_HUD_DISPLAY_GAUGE:
28995 		case OP_HUD_GAUGE_SET_ACTIVE:
28996 		case OP_HUD_ACTIVATE_GAUGE_TYPE:
28997 		case OP_HUD_CLEAR_MESSAGES:
28998 		case OP_HUD_SET_MAX_TARGETING_RANGE:
28999 			return CHANGE_SUBCATEGORY_HUD;
29000 
29001 		case OP_NAV_ADD_WAYPOINT:
29002 		case OP_NAV_ADD_SHIP:
29003 		case OP_NAV_DEL:
29004 		case OP_NAV_HIDE:
29005 		case OP_NAV_RESTRICT:
29006 		case OP_NAV_UNHIDE:
29007 		case OP_NAV_UNRESTRICT:
29008 		case OP_NAV_SET_VISITED:
29009 		case OP_NAV_SET_CARRY:
29010 		case OP_NAV_UNSET_CARRY:
29011 		case OP_NAV_UNSET_VISITED:
29012 		case OP_NAV_SET_NEEDSLINK:
29013 		case OP_NAV_UNSET_NEEDSLINK:
29014 		case OP_NAV_USECINEMATICS:
29015 		case OP_NAV_USEAP:
29016 		case OP_NAV_SELECT:
29017 		case OP_NAV_UNSELECT:
29018 			return CHANGE_SUBCATEGORY_NAV;
29019 
29020 
29021 		case OP_CUTSCENES_SET_CUTSCENE_BARS:
29022 		case OP_CUTSCENES_UNSET_CUTSCENE_BARS:
29023 		case OP_CUTSCENES_FADE_IN:
29024 		case OP_CUTSCENES_FADE_OUT:
29025 		case OP_CUTSCENES_SET_CAMERA:
29026 		case OP_CUTSCENES_SET_CAMERA_POSITION:
29027 		case OP_CUTSCENES_SET_CAMERA_FACING:
29028 		case OP_CUTSCENES_SET_CAMERA_FACING_OBJECT:
29029 		case OP_CUTSCENES_SET_CAMERA_ROTATION:
29030 		case OP_CUTSCENES_SET_CAMERA_HOST:
29031 		case OP_CUTSCENES_SET_CAMERA_TARGET:
29032 		case OP_CUTSCENES_SET_CAMERA_FOV:
29033 		case OP_CUTSCENES_SET_FOV:
29034 		case OP_CUTSCENES_GET_FOV:
29035 		case OP_CUTSCENES_RESET_FOV:
29036 		case OP_CUTSCENES_RESET_CAMERA:
29037 		case OP_CUTSCENES_SHOW_SUBTITLE:
29038 		case OP_CUTSCENES_SHOW_SUBTITLE_TEXT:
29039 		case OP_CUTSCENES_SHOW_SUBTITLE_IMAGE:
29040 		case OP_CLEAR_SUBTITLES:
29041 		case OP_CUTSCENES_FORCE_PERSPECTIVE:
29042 		case OP_SET_CAMERA_SHUDDER:
29043 		case OP_SUPERNOVA_START:
29044 		case OP_SUPERNOVA_STOP:
29045 		case OP_SET_MOTION_DEBRIS:
29046 			return CHANGE_SUBCATEGORY_CUTSCENES;
29047 
29048 
29049 		case OP_SET_SKYBOX_MODEL:
29050 		case OP_SET_SKYBOX_ORIENT:
29051 		case OP_MISSION_SET_NEBULA:
29052 		case OP_MISSION_SET_SUBSPACE:
29053 		case OP_ADD_BACKGROUND_BITMAP:
29054 		case OP_REMOVE_BACKGROUND_BITMAP:
29055 		case OP_ADD_SUN_BITMAP:
29056 		case OP_REMOVE_SUN_BITMAP:
29057 		case OP_NEBULA_CHANGE_STORM:
29058 		case OP_NEBULA_TOGGLE_POOF:
29059 		case OP_NEBULA_CHANGE_PATTERN:
29060 		case OP_SET_AMBIENT_LIGHT:
29061 			return CHANGE_SUBCATEGORY_BACKGROUND_AND_NEBULA;
29062 
29063 		case OP_JUMP_NODE_SET_JUMPNODE_NAME: //CommanderDJ
29064 		case OP_JUMP_NODE_SET_JUMPNODE_COLOR:
29065 		case OP_JUMP_NODE_SET_JUMPNODE_MODEL:
29066 		case OP_JUMP_NODE_SHOW_JUMPNODE:
29067 		case OP_JUMP_NODE_HIDE_JUMPNODE:
29068 			return CHANGE_SUBCATEGORY_JUMP_NODES;
29069 
29070 		case OP_SET_POST_EFFECT:
29071 		case OP_SHIP_EFFECT:
29072 		case OP_SHIP_CREATE:
29073 		case OP_WEAPON_CREATE:
29074 		case OP_SHIP_VANISH:
29075 		case OP_SHIP_VAPORIZE:
29076 		case OP_SHIP_NO_VAPORIZE:
29077 		case OP_SET_EXPLOSION_OPTION:
29078 		case OP_EXPLOSION_EFFECT:
29079 		case OP_WARP_EFFECT:
29080 		case OP_REMOVE_WEAPONS:
29081 		case OP_CUTSCENES_SET_TIME_COMPRESSION:
29082 		case OP_CUTSCENES_RESET_TIME_COMPRESSION:
29083 		case OP_CALL_SSM_STRIKE:
29084 			return CHANGE_SUBCATEGORY_SPECIAL_EFFECTS;
29085 
29086 		case OP_MODIFY_VARIABLE:
29087 		case OP_GET_VARIABLE_BY_INDEX:
29088 		case OP_SET_VARIABLE_BY_INDEX:
29089 		case OP_COPY_VARIABLE_FROM_INDEX:
29090 		case OP_COPY_VARIABLE_BETWEEN_INDEXES:
29091 		case OP_INT_TO_STRING:
29092 		case OP_STRING_CONCATENATE:
29093 		case OP_STRING_GET_SUBSTRING:
29094 		case OP_STRING_SET_SUBSTRING:
29095 			return CHANGE_SUBCATEGORY_VARIABLES;
29096 
29097 		case OP_DAMAGED_ESCORT_LIST:
29098 		case OP_DAMAGED_ESCORT_LIST_ALL:
29099 		case OP_SET_SUPPORT_SHIP:
29100 		case OP_SCRIPT_EVAL:
29101 		case OP_SCRIPT_EVAL_MULTI:
29102 			return CHANGE_SUBCATEGORY_OTHER;
29103 
29104 		case OP_NUM_SHIPS_IN_BATTLE:
29105 		case OP_NUM_SHIPS_IN_WING:
29106 		case OP_DIRECTIVE_VALUE:
29107 			return STATUS_SUBCATEGORY_MISSION;
29108 
29109 		case OP_WAS_PROMOTION_GRANTED:
29110 		case OP_WAS_MEDAL_GRANTED:
29111 		case OP_SKILL_LEVEL_AT_LEAST:
29112 		case OP_NUM_KILLS:
29113 		case OP_NUM_ASSISTS:
29114 		case OP_NUM_TYPE_KILLS:
29115 		case OP_NUM_CLASS_KILLS:
29116 		case OP_SHIP_SCORE:
29117 		case OP_LAST_ORDER_TIME:
29118 		case OP_PLAYER_IS_CHEATING_BASTARD:
29119 			return STATUS_SUBCATEGORY_PLAYER;
29120 
29121 		case OP_NUM_PLAYERS:
29122 		case OP_TEAM_SCORE:
29123 		case OP_SHIP_DEATHS:
29124 		case OP_RESPAWNS_LEFT:
29125 		case OP_IS_PLAYER:
29126 			return STATUS_SUBCATEGORY_MULTIPLAYER;
29127 
29128 		case OP_HAS_BEEN_TAGGED_DELAY:
29129 		case OP_IS_TAGGED:
29130 		case OP_IS_SHIP_VISIBLE:
29131 		case OP_IS_SHIP_STEALTHY:
29132 		case OP_IS_FRIENDLY_STEALTH_VISIBLE:
29133 		case OP_IS_IFF:
29134 		case OP_IS_AI_CLASS:
29135 		case OP_IS_SHIP_CLASS:
29136 		case OP_IS_SHIP_TYPE:
29137 		case OP_CURRENT_SPEED:
29138 		case OP_GET_THROTTLE_SPEED:
29139 		case OP_IS_FACING:
29140 		case OP_IS_IN_MISSION:
29141 		case OP_NAV_ISLINKED:
29142 		case OP_ARE_SHIP_FLAGS_SET:
29143 			return STATUS_SUBCATEGORY_SHIP_STATUS;
29144 
29145 		case OP_SHIELD_RECHARGE_PCT:
29146 		case OP_ENGINE_RECHARGE_PCT:
29147 		case OP_WEAPON_RECHARGE_PCT:
29148 		case OP_SHIELD_QUAD_LOW:
29149 		case OP_PRIMARY_AMMO_PCT:
29150 		case OP_SECONDARY_AMMO_PCT:
29151 		case OP_IS_PRIMARY_SELECTED:
29152 		case OP_IS_SECONDARY_SELECTED:
29153 		case OP_GET_PRIMARY_AMMO:
29154 		case OP_GET_SECONDARY_AMMO:
29155 		case OP_GET_NUM_COUNTERMEASURES:
29156 		case OP_AFTERBURNER_LEFT:
29157 		case OP_WEAPON_ENERGY_LEFT:
29158 		case OP_PRIMARY_FIRED_SINCE:
29159 		case OP_SECONDARY_FIRED_SINCE:
29160 		case OP_HAS_PRIMARY_WEAPON:
29161 		case OP_HAS_SECONDARY_WEAPON:
29162 		case OP_GET_ETS_VALUE:
29163 			return STATUS_SUBCATEGORY_SHIELDS_ENGINES_AND_WEAPONS;
29164 
29165 		case OP_CARGO_KNOWN_DELAY:
29166 		case OP_CAP_SUBSYS_CARGO_KNOWN_DELAY:
29167 		case OP_IS_CARGO:
29168 			return STATUS_SUBCATEGORY_CARGO;
29169 
29170 		case OP_SHIELDS_LEFT:
29171 		case OP_HITS_LEFT:
29172 		case OP_HITS_LEFT_SUBSYSTEM:
29173 		case OP_HITS_LEFT_SUBSYSTEM_GENERIC:
29174 		case OP_HITS_LEFT_SUBSYSTEM_SPECIFIC:
29175 		case OP_SIM_HITS_LEFT:
29176 		case OP_GET_DAMAGE_CAUSED:
29177 			return STATUS_SUBCATEGORY_DAMAGE;
29178 
29179 		case OP_DISTANCE:
29180 		case OP_DISTANCE_SUBSYSTEM:
29181 		case OP_NAV_DISTANCE:
29182 		case OP_GET_OBJECT_X:
29183 		case OP_GET_OBJECT_Y:
29184 		case OP_GET_OBJECT_Z:
29185 		case OP_GET_OBJECT_PITCH:
29186 		case OP_GET_OBJECT_BANK:
29187 		case OP_GET_OBJECT_HEADING:
29188 		case OP_GET_OBJECT_SPEED_X:
29189 		case OP_GET_OBJECT_SPEED_Y:
29190 		case OP_GET_OBJECT_SPEED_Z:
29191 		case OP_NUM_WITHIN_BOX:
29192 		case OP_SPECIAL_WARP_DISTANCE:
29193 		case OP_IS_IN_BOX:
29194 			return STATUS_SUBCATEGORY_DISTANCE_AND_COORDINATES;
29195 
29196 		case OP_STRING_TO_INT:
29197 		case OP_STRING_GET_LENGTH:
29198 			return STATUS_SUBCATEGORY_VARIABLES;
29199 
29200 		case OP_SCRIPT_EVAL_STRING:
29201 		case OP_SCRIPT_EVAL_NUM:
29202 			return STATUS_SUBCATEGORY_OTHER;
29203 
29204 
29205 		default:
29206 			return -1;		// sexp doesn't have a subcategory
29207 	}
29208 }
29209 
29210 sexp_help_struct Sexp_help[] = {
29211 	{ OP_PLUS, "Plus (Arithmetic operator)\r\n"
29212 		"\tAdds numbers and returns results.\r\n\r\n"
29213 		"Returns a number.  Takes 2 or more numeric arguments." },
29214 
29215 	{ OP_MINUS, "Minus (Arithmetic operator)\r\n"
29216 		"\tSubtracts numbers and returns results.\r\n\r\n"
29217 		"Returns a number.  Takes 2 or more numeric arguments." },
29218 
29219 	{ OP_MOD, "Mod (Arithmetic operator)\r\n"
29220 		"\tDivides numbers and returns the remainer.\r\n\r\n"
29221 		"Returns a number.  Takes 2 or more numeric arguments." },
29222 
29223 	{ OP_MUL, "Multiply (Arithmetic operator)\r\n"
29224 		"\tMultiplies numbers and returns results.\r\n\r\n"
29225 		"Returns a number.  Takes 2 or more numeric arguments." },
29226 
29227 	{ OP_DIV, "Divide (Arithmetic operator)\r\n"
29228 		"\tDivides numbers and returns results.\r\n\r\n"
29229 		"Returns a number.  Takes 2 or more numeric arguments." },
29230 
29231 	{ OP_RAND, "Rand (Arithmetic operator)\r\n"
29232 		"\tGets a random number.  This number will not change on successive calls to this sexp.\r\n\r\n"
29233 		"Returns a number.  Takes 2 or 3 numeric arguments...\r\n"
29234 		"\t1:\tLow range of random number.\r\n"
29235 		"\t2:\tHigh range of random number.\r\n"
29236 		"\t3:\t(optional) A seed to use when generating numbers. (Setting this to 0 is the same as having no seed at all)" },
29237 
29238 	// Goober5000
29239 	{ OP_RAND_MULTIPLE, "Rand-multiple (Arithmetic operator)\r\n"
29240 		"\tGets a random number.  This number can and will change between successive calls to this sexp.\r\n\r\n"
29241 		"Returns a number.  Takes 2 or 3 numeric arguments...\r\n"
29242 		"\t1:\tLow range of random number.\r\n"
29243 		"\t2:\tHigh range of random number.\r\n"
29244 		"\t3:\t(optional) A seed to use when generating numbers. (Setting this to 0 is the same as having no seed at all)" },
29245 
29246 	// -------------------------- Nav Points --- Kazan --------------------------
29247 	{ OP_NAV_IS_VISITED, "is-nav-visited\r\n"
29248 		"Returns true when the player has visited the given navpoint (Has closed to within 1000m of it). Takes 1 argument...\r\n"
29249 		"\t1:\tThe name of the navpoint" },
29250 
29251 	{ OP_NAV_DISTANCE, "distance-to-nav\r\n"
29252 		"Returns the distance from the player ship to that nav point. Takes 1 argument..."
29253 		"\t1:\tThe name of the navpoint" },
29254 
29255 	{ OP_NAV_ADD_WAYPOINT, "add-nav-waypoint\r\n"
29256 		"Adds a Navpoint to a navpoint path. Takes 3 or 4 Arguments...\r\n"
29257 		"\t1:\tName of the new navpoint.\r\n"
29258 		"\t2:\tName of the navpoint path the new navpoint should be added to.\r\n"
29259 		"\t3:\tPosition where the new navpoint will be inserted. Note: This is 1-indexed, so the first waypoint in a path has position 1.\r\n"
29260 		"\t4:\t(Optional Argument) Controls the visibility of the new navpoint. Only entities belonging to the chosen category will be able to see it.\r\n" },
29261 
29262 	{ OP_NAV_ADD_SHIP, "add-nav-ship\r\n"
29263 		"Binds the named navpoint to the named ship - when the ship moves, the waypoint moves with it. Takes 2 Arguments...\r\n"
29264 		"\t1:\tThe NavPoint's Name\r\n"
29265 		"\t2:\tThe Ship's Name\r\n" },
29266 
29267 	{ OP_NAV_DEL, "del-nav\r\n"
29268 		"Deletes a nav point. Takes 1 Argument...\r\n"
29269 		"\t1:\tNavPoint Name" },
29270 
29271 	{ OP_NAV_HIDE, "hide-nav\r\n"
29272 		"Hides a nav point. Takes 1 Argument...\r\n"
29273 		"\t1:\tNavPoint Name, it then 'hides' that Nav Point\r\n" },
29274 
29275 	{ OP_NAV_RESTRICT, "restrict-nav\r\n"
29276 		"This causes the nav point to be unselectable. Takes 1 Argument...\r\n"
29277 		"\t1:\tThe Navpoint name\r\n" },
29278 
29279 	{ OP_NAV_UNHIDE, "unhide-nav\r\n"
29280 		"Restores a hidden navpoint. Takes 1 Argument...\r\n"
29281 		"\t1:\tThe Navpoint Name\r\n" },
29282 
29283 	{ OP_NAV_UNRESTRICT, "unrestrict-nav\r\n"
29284 		"Removes restrictions from a Navpoint. Takes 1 Argument...\r\n"
29285 		"\t1:\tThe Navpoint Name\r\n" },
29286 
29287 	{ OP_NAV_SET_VISITED, "set-nav-visited\r\n"
29288 		"Sets the status of the given Navpoint to \"visited\". Takes 1 Argument...\r\n"
29289 		"\t1:\tThe Navpoint Name\r\n" },
29290 
29291 	{ OP_NAV_UNSET_VISITED, "unset-nav-visited\r\n"
29292 		"Removes the \"visited\" status from a Navpoint. Takes 1 Argument...\r\n"
29293 		"\t1:\tThe Navpoint Name\r\n" },
29294 
29295 	{ OP_NAV_SET_CARRY, "nav-set-carry\r\n"
29296 		"Sets the Nav Carry flag for all listed ships. Vessels with this flag will follow the player into and out of autopilot.\r\n"
29297 		"Takes 1 or more arguments...\r\n"
29298 		"\t1:\tShips and Wings that should receive the Nav Carry flag.\r\n" },
29299 
29300 	{ OP_NAV_UNSET_CARRY, "unset-nav-carry\r\n"
29301 		"Removes the Nav Carry flag from all listed ships and wings. Takes 1 or more arguments...\r\n"
29302 		"\t1:\tShips and Wings to remove the Nav Carry flag from\r\n" },
29303 
29304 	{ OP_NAV_SET_NEEDSLINK, "set-nav-needslink\r\n"
29305 		"Marks all listed ships as needing to link up before entering autopilot.\r\n"
29306 		"Takes 1 or more arguments...\r\n"
29307 		"\t1:\tShips to mark\r\n" },
29308 
29309 	{ OP_NAV_UNSET_NEEDSLINK, "unset-nav-needslink\r\n"
29310 		"Removes the requirement for the listed ships to link up before entering autopilot.\r\n"
29311 		"Takes 1 or more arguments...\r\n"
29312 		"\t1:\tShips to mark\r\n" },
29313 
29314 	{ OP_NAV_ISLINKED, "is-nav-linked\r\n"
29315 		"Determines if a ship is linked for autopilot (\"set-nav-carry\" or \"set-nav-needslink\" + linked)"
29316 		"Takes 1 argument...\r\n"
29317 		"\t1:\tShip to check\r\n"},
29318 
29319 	{ OP_NAV_USECINEMATICS, "use-nav-cinematics\r\n"
29320 		"Controls the use of the cinematic autopilot camera. Takes 1 Argument..."
29321 		"\t1:\tSet to true to enable automatic cinematics, set to false to disable automatic cinematics." },
29322 
29323 	{ OP_NAV_USEAP, "use-autopilot\r\n"
29324 		"Takes 1 boolean argument.\r\n"
29325 		"\t1:\tSet to true to enable autopilot, set to false to disable autopilot." },
29326 
29327 	{ OP_NAV_SELECT, "nav-select (Action operator)\r\n"
29328 		"\tSelects a nav point.\r\n\r\n"
29329 		"Takes 1 argument...\r\n"
29330 		"\t1:\tName of the nav point." },
29331 
29332 	{ OP_NAV_UNSELECT, "nav-deselect (Action operator)\r\n"
29333 		"\tDeselects any navpoint selected.\r\n\r\n"
29334 		"Takes no arguments..." },
29335 
29336 	// -------------------------- -------------------------- --------------------------
29337 
29338 	// Goober5000
29339 	{ OP_ABS, "Absolute value (Arithmetic operator)\r\n"
29340 		"\tReturns the absolute value of a number.  Takes 1 numeric argument.\r\n" },
29341 
29342 	// Goober5000
29343 	{ OP_MIN, "Minimum value (Arithmetic operator)\r\n"
29344 		"\tReturns the minimum of a set of numbers.  Takes 1 or more numeric arguments.\r\n" },
29345 
29346 	// Goober5000
29347 	{ OP_MAX, "Maximum value (Arithmetic operator)\r\n"
29348 		"\tReturns the maximum of a set of numbers.  Takes 1 or more numeric arguments.\r\n" },
29349 
29350 	// Goober5000
29351 	{ OP_AVG, "Average value (Arithmetic operator)\r\n"
29352 		"\tReturns the average (rounded to the nearest integer) of a set of numbers.  Takes 1 or more numeric arguments.\r\n" },
29353 
29354 	// Goober5000
29355 	{ OP_POW, "Power (Arithmetic operator)\r\n"
29356 		"\tRaises one number to the power of the next number.  If the result will be larger than INT_MAX or smaller than INT_MIN, the appropriate limit will be returned.  Takes 2 numeric arguments.\r\n" },
29357 
29358 	// Goober5000
29359 	{ OP_SIGNUM, "Signum (Arithmetic operator)\r\n"
29360 		"\tReturns the sign of a number: -1 if it is negative, 0 if it is 0, and 1 if it is positive.  Takes one argument.\r\n" },
29361 
29362 	// Goober5000
29363 	{ OP_SET_BIT, "set-bit\r\n"
29364 		"\tTurns on (sets to 1) a certain bit in the provided number, returning the result.  This allows numbers to store up to 32 boolean flags, from 2^0 to 2^31.  Takes 2 numeric arguments...\r\n"
29365 		"\t1: The number whose bit should be set\r\n"
29366 		"\t2: The index of the bit to set.  Valid indexes are between 0 and 31, inclusive.\r\n" },
29367 
29368 	// Goober5000
29369 	{ OP_UNSET_BIT, "unset-bit\r\n"
29370 		"\tTurns off (sets to 0) a certain bit in the provided number, returning the result.  This allows numbers to store up to 32 boolean flags, from 2^0 to 2^31.  Takes 2 numeric arguments...\r\n"
29371 		"\t1: The number whose bit should be unset\r\n"
29372 		"\t2: The index of the bit to unset.  Valid indexes are between 0 and 31, inclusive.\r\n" },
29373 
29374 	// Goober5000
29375 	{ OP_IS_BIT_SET, "is-bit-set\r\n"
29376 		"\tReturns true if the specified bit is set (equal to 1) in the provided number.  Takes 2 numeric arguments...\r\n"
29377 		"\t1: The number whose bit should be tested\r\n"
29378 		"\t2: The index of the bit to test.  Valid indexes are between 0 and 31, inclusive.\r\n" },
29379 
29380 	// Goober5000
29381 	{ OP_BITWISE_AND, "bitwise-and\r\n"
29382 		"\tPerforms the bitwise AND operator on its arguments.  This is the same as if the logical AND operator was performed on each successive bit.  Takes 2 or more numeric arguments.\r\n" },
29383 
29384 	// Goober5000
29385 	{ OP_BITWISE_OR, "bitwise-or\r\n"
29386 		"\tPerforms the bitwise OR operator on its arguments.  This is the same as if the logical OR operator was performed on each successive bit.  Takes 2 or more numeric arguments.\r\n" },
29387 
29388 	// Goober5000
29389 	{ OP_BITWISE_NOT, "bitwise-not\r\n"
29390 		"\tPerforms the bitwise NOT operator on its argument.  This is the same as if the logical NOT operator was performed on each successive bit.\r\n\r\n"
29391 		"Note that the operation is performed as if on an unsigned integer whose maximum value is INT_MAX.  In other words, there is no need to worry about the sign bit.\r\n\r\n"
29392 		"Takes only 1 argument.\r\n" },
29393 
29394 	// Goober5000
29395 	{ OP_BITWISE_XOR, "bitwise-xor\r\n"
29396 		"\tPerforms the bitwise XOR operator on its arguments.  This is the same as if the logical XOR operator was performed on each successive bit.  Takes 2 or more numeric arguments.\r\n" },
29397 
29398 	{ OP_SET_OBJECT_SPEED_X, "set-object-speed-x\r\n"
29399 		"\tSets the X speed of a ship or wing."
29400 		"Takes 2 or 3 arguments...\r\n"
29401 		"\t1: The name of the object.\r\n"
29402 		"\t2: The speed to set.\r\n"
29403 		"\t3: Whether the speed on the axis should be set according to the universe grid (when false) or according to the object's facing (when true); You almost always want to set this to true; (optional; defaults to false).\r\n" },
29404 
29405 	{ OP_SET_OBJECT_SPEED_Y, "set-object-speed-y\r\n"
29406 		"\tSets the Y speed of a ship or wing."
29407 		"Takes 2 or 3 arguments...\r\n"
29408 		"\t1: The name of the object.\r\n"
29409 		"\t2: The speed to set.\r\n"
29410 		"\t3: Whether the speed on the axis should be set according to the universe grid (when false) or according to the object's facing (when true); You almost always want to set this to true; (optional; defaults to false).\r\n" },
29411 
29412 	{ OP_SET_OBJECT_SPEED_Z, "set-object-speed-z\r\n"
29413 		"\tSets the Z speed of a ship or wing."
29414 		"Takes 2 or 3 arguments...\r\n"
29415 		"\t1: The name of the object.\r\n"
29416 		"\t2: The speed to set.\r\n"
29417 		"\t3: Whether the speed on the axis should be set according to the universe grid (when false) or according to the object's facing (when true); You almost always want to set this to true; (optional; defaults to false).\r\n" },
29418 
29419 	{ OP_GET_OBJECT_SPEED_X, "get-object-speed-x\r\n"
29420 		"\tReturns the X speed of a ship or wing as an integer."
29421 		"Takes 2 or 3 arguments...\r\n"
29422 		"\t1: The name of the object.\r\n"
29423 		"\t2: Whether the speed on the axis should be set according to the universe grid (when false) or according to the object's facing (when true); You almost always want to set this to true; (optional; defaults to false).\r\n" },
29424 
29425 	{ OP_GET_OBJECT_SPEED_Y, "get-object-speed-y\r\n"
29426 		"\tReturns the Y speed of a ship or wing as an integer."
29427 		"Takes 2 or 3 arguments...\r\n"
29428 		"\t1: The name of the object.\r\n"
29429 		"\t2: Whether the speed on the axis should be set according to the universe grid (when false) or according to the object's facing (when true); You almost always want to set this to true; (optional; defaults to false).\r\n" },
29430 
29431 	{ OP_GET_OBJECT_SPEED_Z, "get-object-speed-z\r\n"
29432 		"\tReturns the Z speed of a ship or wing as an integer."
29433 		"Takes 2 or 3 arguments...\r\n"
29434 		"\t1: The name of the object.\r\n"
29435 		"\t2: Whether the speed on the axis should be set according to the universe grid (when false) or according to the object's facing (when true); You almost always want to set this to true; (optional; defaults to false).\r\n" },
29436 
29437 	// Goober5000
29438 	{ OP_GET_OBJECT_X, "get-object-x\r\n"
29439 		"\tReturns the absolute X coordinate of a set of coordinates relative to a particular object (or object's "
29440 		"subsystem).  The input coordinates are the coordinates relative to the object's position and orientation.  "
29441 		"If no input coordinates are specified, the coordinate returned is the coordinate of the object (or object's "
29442 		"subsystem) itself.  Takes 1 to 5 arguments...\r\n"
29443 		"\t1: The name of a ship, wing, or waypoint.\r\n"
29444 		"\t2: A ship subsystem (or \""SEXP_NONE_STRING"\" if the first argument is not a ship - optional).\r\n"
29445 		"\t3: The relative X coordinate (optional).\r\n"
29446 		"\t4: The relative Y coordinate (optional).\r\n"
29447 		"\t5: The relative Z coordinate (optional).\r\n" },
29448 
29449 	// Goober5000
29450 	{ OP_GET_OBJECT_Y, "get-object-y\r\n"
29451 		"\tReturns the absolute Y coordinate of a set of coordinates relative to a particular object (or object's "
29452 		"subsystem).  The input coordinates are the coordinates relative to the object's position and orientation.  "
29453 		"If no input coordinates are specified, the coordinate returned is the coordinate of the object (or object's "
29454 		"subsystem) itself.  Takes 1 to 5 arguments...\r\n"
29455 		"\t1: The name of a ship, wing, or waypoint.\r\n"
29456 		"\t2: A ship subsystem (or \""SEXP_NONE_STRING"\" if the first argument is not a ship - optional).\r\n"
29457 		"\t3: The relative X coordinate (optional).\r\n"
29458 		"\t4: The relative Y coordinate (optional).\r\n"
29459 		"\t5: The relative Z coordinate (optional).\r\n" },
29460 
29461 	// Goober5000
29462 	{ OP_GET_OBJECT_Z, "get-object-z\r\n"
29463 		"\tReturns the absolute Z coordinate of a set of coordinates relative to a particular object (or object's "
29464 		"subsystem).  The input coordinates are the coordinates relative to the object's position and orientation.  "
29465 		"If no input coordinates are specified, the coordinate returned is the coordinate of the object (or object's "
29466 		"subsystem) itself.  Takes 1 to 5 arguments...\r\n"
29467 		"\t1: The name of a ship, wing, or waypoint.\r\n"
29468 		"\t2: A ship subsystem (or \""SEXP_NONE_STRING"\" if the first argument is not a ship - optional).\r\n"
29469 		"\t3: The relative X coordinate (optional).\r\n"
29470 		"\t4: The relative Y coordinate (optional).\r\n"
29471 		"\t5: The relative Z coordinate (optional).\r\n" },
29472 
29473 	// Goober5000
29474 	{ OP_SET_OBJECT_POSITION, "set-object-position\r\n"
29475 		"\tInstantaneously sets an object's spatial coordinates."
29476 		"Takes 4 arguments...\r\n"
29477 		"\t1: The name of a ship, wing, or waypoint.\r\n"
29478 		"\t2: The new X coordinate.\r\n"
29479 		"\t3: The new Y coordinate.\r\n"
29480 		"\t4: The new Z coordinate." },
29481 
29482 	// Goober5000
29483 	{ OP_GET_OBJECT_PITCH, "get-object-pitch\r\n"
29484 		"\tReturns the pitch angle, in degrees, of a particular object.  The returned value will be between 0 and 360.  Takes 1 argument...\r\n"
29485 		"\t1: The name of a ship or wing.\r\n" },
29486 
29487 	// Goober5000
29488 	{ OP_GET_OBJECT_BANK, "get-object-bank\r\n"
29489 		"\tReturns the bank angle, in degrees, of a particular object.  The returned value will be between 0 and 360.  Takes 1 argument...\r\n"
29490 		"\t1: The name of a ship or wing.\r\n" },
29491 
29492 	// Goober5000
29493 	{ OP_GET_OBJECT_HEADING, "get-object-heading\r\n"
29494 		"\tReturns the heading angle, in degrees, of a particular object.  The returned value will be between 0 and 360.  Takes 1 argument...\r\n"
29495 		"\t1: The name of a ship or wing.\r\n" },
29496 
29497 	// Goober5000
29498 	{ OP_SET_OBJECT_ORIENTATION, "set-object-orientation\r\n"
29499 		"\tInstantaneously sets an object's spatial orientation."
29500 		"Takes 4 arguments...\r\n"
29501 		"\t1: The name of a ship or wing.\r\n"
29502 		"\t2: The new pitch angle, in degrees.  The angle can be any number; it does not have to be between 0 and 360.\r\n"
29503 		"\t3: The new bank angle, in degrees.  The angle can be any can be any number; it does not have to be between 0 and 360.\r\n"
29504 		"\t4: The new heading angle, in degrees.  The angle can be any number; it does not have to be between 0 and 360." },
29505 
29506 	{ OP_TRUE, "True (Boolean operator)\r\n"
29507 		"\tA true boolean state\r\n\r\n"
29508 		"Returns a boolean value." },
29509 
29510 	{ OP_FALSE, "False (Boolean operator)\r\n"
29511 		"\tA false boolean state\r\n\r\n"
29512 		"Returns a boolean value." },
29513 
29514 	{ OP_AND, "And (Boolean operator)\r\n"
29515 		"\tAnd is true if all of its arguments are true.\r\n\r\n"
29516 		"Returns a boolean value.  Takes 2 or more boolean arguments." },
29517 
29518 	{ OP_OR, "Or (Boolean operator)\r\n"
29519 		"\tOr is true if any of its arguments are true.\r\n\r\n"
29520 		"Returns a boolean value.  Takes 2 or more boolean arguments." },
29521 
29522 	{ OP_XOR, "Xor (Boolean operator)\r\n"
29523 		"\tXor is true if exactly one of its arguments is true.\r\n\r\n"
29524 		"Returns a boolean value.  Takes 2 or more boolean arguments." },
29525 
29526 	{ OP_EQUALS, "Equals (Boolean operator)\r\n"
29527 		"\tIs true if all of its arguments are equal.\r\n\r\n"
29528 		"Returns a boolean value.  Takes 2 or more numeric arguments." },
29529 
29530 	{ OP_GREATER_THAN, "Greater Than (Boolean operator)\r\n"
29531 		"\tTrue if the first argument is greater than the subsequent argument(s).\r\n\r\n"
29532 		"Returns a boolean value.  Takes 2 numeric arguments." },
29533 
29534 	{ OP_LESS_THAN, "Less Than (Boolean operator)\r\n"
29535 		"\tTrue if the first argument is less than the subsequent argument(s).\r\n\r\n"
29536 		"Returns a boolean value.  Takes 2 numeric arguments." },
29537 
29538 	{ OP_NOT_EQUAL, "Not Equal To (Boolean operator)\r\n"
29539 		"\tIs true if the first argument is not equal to any of the subsequent arguments.\r\n\r\n"
29540 		"Returns a boolean value.  Takes 2 or more numeric arguments." },
29541 
29542 	{ OP_GREATER_OR_EQUAL, "Greater Than Or Equal To (Boolean operator)\r\n"
29543 		"\tTrue if the first argument is greater than or equal to the subsequent argument(s).\r\n\r\n"
29544 		"Returns a boolean value.  Takes 2 numeric arguments." },
29545 
29546 	{ OP_LESS_OR_EQUAL, "Less Than Or Equal To (Boolean operator)\r\n"
29547 		"\tTrue if the first argument is less than or equal to the subsequent argument(s).\r\n\r\n"
29548 		"Returns a boolean value.  Takes 2 numeric arguments." },
29549 
29550 	// Goober5000
29551 	{ OP_PERFORM_ACTIONS, "perform-actions\r\n"
29552 		"\tThis sexp allows actions to be performed as part of a conditional test.  It is most useful for assigning variables or performing some sort of pre-test action within the conditional part of \"when\", etc.  "
29553 		"It works well as the first branch of an \"and\" sexp, provided it returns true so as to not affect the return value of the \"and\".\r\n\r\n"
29554 		"Returns a boolean value.  Takes 2 or more arguments.\r\n"
29555 		"\t1:\tA boolean value to return after all successive actions have been performed.\r\n"
29556 		"\tRest:\tActions to perform, which would normally appear in a \"when\" sexp.\r\n" },
29557 
29558 	// Goober5000
29559 	{ OP_STRING_EQUALS, "String Equals (Boolean operator)\r\n"
29560 		"\tIs true if all of its arguments are equal.\r\n\r\n"
29561 		"Returns a boolean value.  Takes 2 or more string arguments." },
29562 
29563 	// Goober5000
29564 	{ OP_STRING_GREATER_THAN, "String Greater Than (Boolean operator)\r\n"
29565 		"\tTrue if the first argument is greater than the second argument.\r\n\r\n"
29566 		"Returns a boolean value.  Takes 2 string arguments." },
29567 
29568 	// Goober5000
29569 	{ OP_STRING_LESS_THAN, "String Less Than (Boolean operator)\r\n"
29570 		"\tTrue if the first argument is less than the second argument.\r\n\r\n"
29571 		"Returns a boolean value.  Takes 2 string arguments." },
29572 
29573 	// Goober5000 - added wing capability
29574 	{ OP_IS_IFF, "Is IFF (Boolean operator)\r\n"
29575 		"\tTrue if ship(s) or wing(s) are all of the specified team.\r\n\r\n"
29576 		"Returns a boolean value.  Takes 2 or more arguments...\r\n"
29577 		"\t1:\tTeam (\"friendly\", \"hostile\", \"neutral\", or \"unknown\").\r\n"
29578 		"\tRest:\tName of ship or wing to check." },
29579 
29580 	// Goober5000
29581 	{ OP_IS_AI_CLASS, "Is AI Class (Boolean operator)\r\n"
29582 		"\tTrue if ship or ship subsystem(s) is/are all of the specified AI class.\r\n\r\n"
29583 		"Returns a boolean value.  Takes 2 or more arguments...\r\n"
29584 		"\t1:\tAI class (\"None\", \"Coward\", \"Lieutenant\", etc.)\r\n"
29585 		"\t2:\tName of ship to check.\r\n"
29586 		"\tRest:\tName of ship subsystem(s) to check (optional)" },
29587 
29588 	// Goober5000
29589 	{ OP_IS_SHIP_TYPE, "Is Ship Type (Boolean operator)\r\n"
29590 		"\tTrue if ship or ships is/are all of the specified ship type.\r\n\r\n"
29591 		"Returns a boolean value.  Takes 2 or more arguments...\r\n"
29592 		"\t1:\tShip type (\"fighter\", \"bomber\", etc.)\r\n"
29593 		"\t2:\tName of ship to check.\r\n" },
29594 
29595 	// Goober5000
29596 	{ OP_IS_SHIP_CLASS, "Is Ship Class (Boolean operator)\r\n"
29597 		"\tTrue if ship or ships is/are all of the specified ship class.\r\n\r\n"
29598 		"Returns a boolean value.  Takes 2 or more arguments...\r\n"
29599 		"\t1:\tShip class\r\n"
29600 		"\t2:\tName of ship to check.\r\n" },
29601 
29602 	{ OP_HAS_TIME_ELAPSED, "Has time elapsed (Boolean operator)\r\n"
29603 		"\tBecomes true when the specified amount of time has elapsed (Mission time "
29604 		"becomes greater than the specified time).\r\n"
29605 		"Returns a boolean value.  Takes 1 numeric argument...\r\n"
29606 		"\t1:\tThe amount of time in seconds." },
29607 
29608 	{ OP_NOT, "Not (Boolean operator)\r\n"
29609 		"\tReturns opposite boolean value of argument (True becomes false, and "
29610 		"false becomes true).\r\n\r\n"
29611 		"Returns a boolean value.  Takes 1 boolean argument." },
29612 
29613 	{ OP_PREVIOUS_GOAL_TRUE, "Previous Mission Goal True (Boolean operator)\r\n"
29614 		"\tReturns true if the specified goal in the specified mission is true "
29615 		"(or succeeded).  It returns false otherwise.\r\n\r\n"
29616 		"Returns a boolean value.  Takes 2 required arguments and 1 optional argument...\r\n"
29617 		"\t1:\tName of the mission.\r\n"
29618 		"\t2:\tName of the goal in the mission.\r\n"
29619 		"\t3:\t(Optional) True/False which signifies what this sexpression should return when "
29620 		"this mission is played as a single mission." },
29621 
29622 	{ OP_PREVIOUS_GOAL_FALSE, "Previous Mission Goal False (Boolean operator)\r\n"
29623 		"\tReturns true if the specified goal in the specified mission "
29624 		"is false (or failed).  It returns false otherwise.\r\n\r\n"
29625 		"Returns a boolean value.  Takes 2 required arguments and 1 optional argument...\r\n"
29626 		"\t1:\tName of the mission.\r\n"
29627 		"\t2:\tName of the goal in the mission.\r\n"
29628 		"\t3:\t(Optional) True/False which signifies what this sexpression should return when "
29629 		"this mission is played as a single mission." },
29630 
29631 	{ OP_PREVIOUS_GOAL_INCOMPLETE, "Previous Mission Goal Incomplete (Boolean operator)\r\n"
29632 		"\tReturns true if the specified goal in the specified mission "
29633 		"is incomplete (not true or false).  It returns false otherwise.\r\n\r\n"
29634 		"Returns a boolean value.  Takes 2 required arguments and 1 optional argument...\r\n"
29635 		"\t1:\tName of the mission.\r\n"
29636 		"\t2:\tName of the goal in the mission.\r\n"
29637 		"\t3:\t(Optional) True/False which signifies what this sexpression should return when "
29638 		"this mission is played as a single mission." },
29639 
29640 	{ OP_PREVIOUS_EVENT_TRUE, "Previous Mission Event True (Boolean operator)\r\n"
29641 		"\tReturns true if the specified event in the specified mission is true "
29642 		"(or succeeded).  It returns false otherwise.\r\n\r\n"
29643 		"Returns a boolean value.  Takes 2 required arguments and 1 optional argument...\r\n"
29644 		"\t1:\tName of the mission.\r\n"
29645 		"\t2:\tName of the event in the mission.\r\n"
29646 		"\t3:\t(Optional) True/False which signifies what this sexpression should return when "
29647 		"this mission is played as a single mission." },
29648 
29649 	{ OP_PREVIOUS_EVENT_FALSE, "Previous Mission Event False (Boolean operator)\r\n"
29650 		"\tReturns true if the specified event in the specified mission "
29651 		"is false (or failed).  It returns false otherwise.\r\n\r\n"
29652 		"Returns a boolean value.  Takes 2 required arguments and 1 optional argument...\r\n"
29653 		"\t1:\tName of the mission.\r\n"
29654 		"\t2:\tName of the event in the mission.\r\n"
29655 		"\t3:\t(Optional) True/False which signifies what this sexpression should return when "
29656 		"this mission is played as a single mission." },
29657 
29658 	{ OP_PREVIOUS_EVENT_INCOMPLETE, "Previous Mission Event Incomplete (Boolean operator)\r\n"
29659 		"\tReturns true if the specified event in the specified mission "
29660 		"is incomplete (not true or false).  It returns false otherwise.\r\n\r\n"
29661 		"Returns a boolean value.  Takes 2 required arguments and 1 optional argument...\r\n"
29662 		"\t1:\tName of the mission.\r\n"
29663 		"\t2:\tName of the event in the mission.\r\n"
29664 		"\t3:\t(Optional) True/False which signifies what this sexpression should return when "
29665 		"this mission is played as a single mission." },
29666 
29667 	{ OP_GOAL_TRUE_DELAY, "Mission Goal True (Boolean operator)\r\n"
29668 		"\tReturns true N seconds after the specified goal in the this mission is true "
29669 		"(or succeeded).  It returns false otherwise.\r\n\r\n"
29670 		"Returns a boolean value.  Takes 2 required arguments and 1 optional argument...\r\n"
29671 		"\t1:\tName of the event in the mission.\r\n"
29672 		"\t2:\tNumber of seconds to delay before returning true."},
29673 
29674 	{ OP_GOAL_FALSE_DELAY, "Mission Goal False (Boolean operator)\r\n"
29675 		"\tReturns true N seconds after the specified goal in the this mission is false "
29676 		"(or failed).  It returns false otherwise.\r\n\r\n"
29677 		"Returns a boolean value.  Takes 2 required arguments and 1 optional argument...\r\n"
29678 		"\t1:\tName of the event in the mission.\r\n"
29679 		"\t2:\tNumber of seconds to delay before returning true."},
29680 
29681 	{ OP_GOAL_INCOMPLETE, "Mission Goal Incomplete (Boolean operator)\r\n"
29682 		"\tReturns true if the specified goal in the this mission is incomplete.  This "
29683 		"sexpression will only be useful in conjunction with another sexpression like"
29684 		"has-time-elapsed.  Used alone, it will return true upon misison startup."
29685 		"Returns a boolean value.  Takes 1 argument...\r\n"
29686 		"\t1:\tName of the event in the mission."},
29687 
29688 	{ OP_EVENT_TRUE_DELAY, "Mission Event True (Boolean operator)\r\n"
29689 		"\tReturns true N seconds after the specified event in the this mission is true "
29690 		"(or succeeded).  It returns false otherwise.\r\n\r\n"
29691 		"Returns a boolean value.  Takes 2 required arguments and 1 optional argument...\r\n"
29692 		"\t1:\tName of the event in the mission.\r\n"
29693 		"\t2:\tNumber of seconds to delay before returning true.\r\n"
29694 		"\t3:\t(Optional) Defaults to False. When set to false, directives will only appear as soon as the specified event is true.\r\n"
29695 		"\t\tWhen set to true, the event only affects whether the directive succeeds/fails, and has no effect on when it appears"},
29696 
29697 	{ OP_EVENT_FALSE_DELAY, "Mission Event False (Boolean operator)\r\n"
29698 		"\tReturns true N seconds after the specified event in the this mission is false "
29699 		"(or failed).  It returns false otherwise.\r\n\r\n"
29700 		"Returns a boolean value.  Takes 2 required arguments and 1 optional argument...\r\n"
29701 		"\t1:\tName of the event in the mission.\r\n"
29702 		"\t2:\tNumber of seconds to delay before returning true.\r\n"
29703 		"\t3:\t(Optional) Defaults to False. When set to false, directives will only appear as soon as the specified event is true.\r\n"
29704 		"\t\tWhen set to true, the event only affects whether the directive succeeds/fails, and has no effect on when it appears"},
29705 
29706 	{ OP_EVENT_TRUE_MSECS_DELAY, "Mission Event True (Boolean operator)\r\n"
29707 		"\tReturns true N milliseconds after the specified event in the this mission is true "
29708 		"(or succeeded).  It returns false otherwise.\r\n\r\n"
29709 		"Returns a boolean value.  Takes 2 required arguments and 1 optional argument...\r\n"
29710 		"\t1:\tName of the event in the mission.\r\n"
29711 		"\t2:\tNumber of milliseconds to delay before returning true.\r\n"
29712 		"\t3:\t(Optional) Defaults to False. When set to false, directives will only appear as soon as the specified event is true.\r\n"
29713 		"\t\tWhen set to true, the event only affects whether the directive succeeds/fails, and has no effect on when it appears"},
29714 
29715 	{ OP_EVENT_FALSE_MSECS_DELAY, "Mission Event False (Boolean operator)\r\n"
29716 		"\tReturns true N milliseconds after the specified event in the this mission is false "
29717 		"(or failed).  It returns false otherwise.\r\n\r\n"
29718 		"Returns a boolean value.  Takes 2 required arguments and 1 optional argument...\r\n"
29719 		"\t1:\tName of the event in the mission.\r\n"
29720 		"\t2:\tNumber of milliseconds to delay before returning true.\r\n"
29721 		"\t3:\t(Optional) Defaults to False. When set to false, directives will only appear as soon as the specified event is true.\r\n"
29722 		"\t\tWhen set to true, the event only affects whether the directive succeeds/fails, and has no effect on when it appears"},
29723 
29724 	{ OP_EVENT_INCOMPLETE, "Mission Event Incomplete (Boolean operator)\r\n"
29725 		"\tReturns true if the specified event in the this mission is incomplete.  This "
29726 		"sexpression will only be useful in conjunction with another sexpression like"
29727 		"has-time-elapsed.  Used alone, it will return true upon misison startup."
29728 		"Returns a boolean value.  Takes 1 argument...\r\n"
29729 		"\t1:\tName of the event in the mission."},
29730 
29731 	{ OP_IS_DESTROYED_DELAY, "Is destroyed delay (Boolean operator)\r\n"
29732 		"\tBecomes true <delay> seconds after all specified ships have been destroyed.\r\n\r\n"
29733 		"Returns a boolean value.  Takes 2 or more arguments...\r\n"
29734 		"\t1:\tTime delay in seconds (see above).\r\n"
29735 		"\tRest:\tName of ship (or wing) to check status of." },
29736 
29737 	{ OP_WAS_DESTROYED_BY_DELAY, "Was destroyed by delay (Boolean operator)\r\n"
29738 		"\tBecomes true <delay> seconds after all specified ships have been destroyed by the specified first ship.\r\n\r\n"
29739 		"Returns a boolean value.  Takes 3 or more arguments...\r\n"
29740 		"\t1:\tTime delay in seconds (see above).\r\n"
29741 		"\t2:\tShip that should have destroyed the other ships (see below).\r\n"
29742 		"\tRest:\tName of ships to check status of." },
29743 
29744 	{ OP_IS_SUBSYSTEM_DESTROYED_DELAY, "Is subsystem destroyed delay (Boolean operator)\r\n"
29745 		"\tBecomes true <delay> seconds after the specified subsystem of the specified "
29746 		"ship is destroyed.\r\n\r\n"
29747 		"Returns a boolean value.  Takes 3 arguments...\r\n"
29748 		"\t1:\tName of ship the subsystem we are checking is on.\r\n"
29749 		"\t2:\tThe name of the subsystem we are checking status of.\r\n"
29750 		"\t3:\tTime delay in seconds (see above)." },
29751 
29752 	{ OP_IS_DISABLED_DELAY, "Is disabled delay (Boolean operator)\r\n"
29753 		"\tBecomes true <delay> seconds after the specified ship(s) are disabled.  A "
29754 		"ship is disabled when all of its engine subsystems are destroyed.  All "
29755 		"ships must be diabled for this function to return true.\r\n\r\n"
29756 		"Returns a boolean value.  Takes 2 or more arguments...\r\n"
29757 		"\t1:\tTime delay is seconds (see above).\r\n"
29758 		"\tRest:\tNames of ships to check disabled status of." },
29759 
29760 	{ OP_IS_DISARMED_DELAY, "Is disarmed delay (Boolean operator)\r\n"
29761 		"\tBecomes true <delay> seconds after the specified ship(s) are disarmed.  A "
29762 		"ship is disarmed when all of its turret subsystems are destroyed.  All "
29763 		"ships must be disarmed for this function to return true.\r\n\r\n"
29764 		"Returns a boolean value.  Takes 2 or more arguments...\r\n"
29765 		"\t1:\tTime delay is seconds (see above).\r\n"
29766 		"\tRest:\tNames of ships to check disarmed status of." },
29767 
29768 	{ OP_HAS_DOCKED_DELAY, "Has docked delay (Boolean operator)\r\n"
29769 		"\tBecomes true <delay> seconds after the specified ships have docked the "
29770 		"specified number of times.\r\n\r\n"
29771 		"Returns a boolean value.  Takes 4 arguments...\r\n"
29772 		"\t1:\tThe name of the docker ship\r\n"
29773 		"\t2:\tThe name of the dockee ship\r\n"
29774 		"\t3:\tThe number of times they have to have docked\r\n"
29775 		"\t4:\tTime delay in seconds (see above)." },
29776 
29777 	{ OP_HAS_UNDOCKED_DELAY, "Has undocked delay (Boolean operator)\r\n"
29778 		"\tBecomes true <delay> seconds after the specified ships have undocked the "
29779 		"specified number of times.\r\n\r\n"
29780 		"Returns a boolean value.  Takes 4 arguments...\r\n"
29781 		"\t1:\tThe name of the docker ship\r\n"
29782 		"\t2:\tThe name of the dockee ship\r\n"
29783 		"\t3:\tThe number of times they have to have undocked\r\n"
29784 		"\t4:\tTime delay in seconds (see above)." },
29785 
29786 	{ OP_HAS_ARRIVED_DELAY, "Has arrived delay (Boolean operator)\r\n"
29787 		"\tBecomes true <delay> seconds after the specified ship(s) have arrived into the mission\r\n\r\n"
29788 		"Returns a boolean value.  Takes 2 or more arguments...\r\n"
29789 		"\t1:\tTime delay in seconds (see above).\r\n"
29790 		"\tRest:\tName of ship (or wing) we want to check has arrived." },
29791 
29792 	{ OP_HAS_DEPARTED_DELAY, "Has departed delay (Boolean operator)\r\n"
29793 		"\tBecomes true <delay> seconds after the specified ship(s) or wing(s) have departed "
29794 		"from the mission by warping out.  If any ship was destroyed, this operator will "
29795 		"never be true.\r\n\r\n"
29796 		"Returns a boolean value.  Takes 2 or more arguments...\r\n"
29797 		"\t1:\tTime delay in seconds (see above).\r\n"
29798 		"\tRest:\tName of ship (or wing) we want to check has departed." },
29799 
29800 	{ OP_WAYPOINTS_DONE_DELAY, "Waypoints done delay (Boolean operator)\r\n"
29801 		"\tBecomes true <delay> seconds after the specified ship has completed flying the "
29802 		"specified waypoint path.\r\n\r\n"
29803 		"Returns a boolean value.  Takes 3 or 4 arguments...\r\n"
29804 		"\t1:\tName of ship we are checking.\r\n"
29805 		"\t2:\tWaypoint path we want to check if ship has flown.\r\n"
29806 		"\t3:\tTime delay in seconds (see above).\r\n"
29807 		"\t4:\tHow many times the ship has completed the waypoint path (optional)." },
29808 
29809 	{ OP_SHIP_TYPE_DESTROYED, "Ship Type Destroyed (Boolean operator)\r\n"
29810 		"\tBecomes true when the specified percentage of ship types in this mission "
29811 		"have been destroyed.  The ship type is a generic type such as fighter/bomber, "
29812 		"transport, etc.  Fighters and bombers count as the same type.\r\n\r\n"
29813 		"Returns a boolean value.  Takes 2 arguments...\r\n"
29814 		"\t1:\tPercentage of ships that must be destroyed.\r\n"
29815 		"\t2:\tShip type to check for." },
29816 
29817 	{ OP_TIME_SHIP_DESTROYED, "Time ship destroyed (Time operator)\r\n"
29818 		"\tReturns the time the specified ship was destroy.\r\n\r\n"
29819 		"Returns a numeric value.  Takes 1 argument...\r\n"
29820 		"\t1:\tName of ship we want to check." },
29821 
29822 	{ OP_TIME_SHIP_ARRIVED, "Time ship arrived (Time operator)\r\n"
29823 		"\tReturns the time the specified ship arrived into the mission.\r\n\r\n"
29824 		"Returns a numeric value.  Takes 1 argument...\r\n"
29825 		"\t1:\tName of ship we want to check." },
29826 
29827 	{ OP_TIME_SHIP_DEPARTED, "Time ship departed (Time operator)\r\n"
29828 		"\tReturns the time the specified ship departed the mission by warping out.  Being "
29829 		"destroyed doesn't count departed.\r\n\r\n"
29830 		"Returns a numeric value.  Takes 1 argument...\r\n"
29831 		"\t1:\tName of ship we want to check." },
29832 
29833 	{ OP_TIME_WING_DESTROYED, "Time wing destroyed (Time operator)\r\n"
29834 		"\tReturns the time the specified wing was destroy.\r\n\r\n"
29835 		"Returns a numeric value.  Takes 1 argument...\r\n"
29836 		"\t1:\tName of wing we want to check." },
29837 
29838 	{ OP_TIME_WING_ARRIVED, "Time wing arrived (Time operator)\r\n"
29839 		"\tReturns the time the specified wing arrived into the mission.\r\n\r\n"
29840 		"Returns a numeric value.  Takes 1 argument...\r\n"
29841 		"\t1:\tName of wing we want to check." },
29842 
29843 	{ OP_TIME_WING_DEPARTED, "Time wing departed (Time operator)\r\n"
29844 		"\tReturns the time the specified wing departed the mission by warping out.  All "
29845 		"ships in the wing have to have warped out.  If any are destroyed, the wing can "
29846 		"never be considered departed.\r\n\r\n"
29847 		"Returns a numeric value.  Takes 1 argument...\r\n"
29848 		"\t1:\tName of ship we want to check." },
29849 
29850 	{ OP_MISSION_TIME, "Mission time (Time operator)\r\n"
29851 		"\tReturns the current time into the mission.\r\n\r\n"
29852 		"Returns a numeric value.  Takes no arguments." },
29853 
29854 	{ OP_MISSION_TIME_MSECS, "Mission time, in milliseconds (Time operator)\r\n"
29855 		"\tReturns the current time into the mission, in milliseconds.  Useful for more fine-grained timing than possible with normal second-based sexps.  (Tip: when an event occurs, assign the result of mission-time-msecs to a variable.  Then, in another event, wait until mission-time-msecs is greater than the value of that variable plus some delay amount.  This second event should be chained or coupled with additional conditions so that it doesn't accidentally fire on an uninitialized variable!)\r\n\r\n"
29856 		"Returns a numeric value.  Takes no arguments." },
29857 
29858 	{ OP_TIME_DOCKED, "Time docked (Time operator)\r\n"
29859 		"\tReturns the time the specified ships docked.\r\n\r\n"
29860 		"Returns a numeric value.  Takes 3 arguments...\r\n"
29861 		"\t1:\tThe name of the docker ship.\r\n"
29862 		"\t2:\tThe name of the dockee ship.\r\n"
29863 		"\t3:\tThe number of times they must have docked to be true." },
29864 
29865 	{ OP_TIME_UNDOCKED, "Time undocked (Time operator)\r\n"
29866 		"\tReturns the time the specified ships undocked.\r\n\r\n"
29867 		"Returns a numeric value.  Takes 3 arguments...\r\n"
29868 		"\t1:\tThe name of the docker ship.\r\n"
29869 		"\t2:\tThe name of the dockee ship.\r\n"
29870 		"\t3:\tThe number of times they must have undocked to be true." },
29871 
29872 	{ OP_AFTERBURNER_LEFT, "Afterburner left\r\n"
29873 		"\tReturns a ship's current engine energy as a percentage.\r\n"
29874 		"\t1: Ship name\r\n" },
29875 
29876 	{ OP_WEAPON_ENERGY_LEFT, "Weapon energy left\r\n"
29877 		"\tReturns a ship's current weapon energy as a percentage.\r\n"
29878 		"\t1: Ship name\r\n" },
29879 
29880 	{ OP_SHIELDS_LEFT, "Shields left (Status operator)\r\n"
29881 		"\tReturns the current level of the specified ship's shields as a percentage.\r\n\r\n"
29882 		"Returns a numeric value.  Takes 1 argument...\r\n"
29883 		"\t1:\tName of ship to check." },
29884 
29885 	{ OP_HITS_LEFT, "Hits left (Status operator)\r\n"
29886 		"\tReturns the current level of the specified ship's hull as a percentage.\r\n\r\n"
29887 		"Returns a numeric value.  Takes 1 argument...\r\n"
29888 		"\t1:\tName of ship to check." },
29889 
29890 	{ OP_HITS_LEFT_SUBSYSTEM, "Hits left subsystem (status operator, deprecated)\r\n"
29891 		"\tReturns the current level of the specified ship's subsystem integrity as a percentage of the damage done to *all "
29892 		"subsystems of the same type*.  This operator provides the same functionality as the new hits-left-subsystem-generic "
29893 		"operator, except that it gets the subsystem type in a very misleading way.  Common consensus among SCP programmers is "
29894 		"that this operator was intended to behave like hits-left-subsystem-specific but was programmed incorrectly.  As such, "
29895 		"this operator is deprecated.  Mission designers are strongly encouraged to use hits-left-subsystem-specific rather than "
29896 		"the optional boolean parameter.\r\n\r\n"
29897 		"Returns a numeric value.  Takes 2 or 3 arguments...\r\n"
29898 		"\t1:\tName of ship to check.\r\n"
29899 		"\t2:\tName of subsystem on ship to check.\r\n"
29900 		"\t3:\t(Optional) True/False. When set to true only the subsystem supplied will be tested; when set to false (the default), "
29901 		"all subsystems of that type will be tested." },
29902 
29903 	{ OP_HITS_LEFT_SUBSYSTEM_GENERIC, "hits-left-subsystem-generic (status operator)\r\n"
29904 		"\tReturns the current level of integrity of a generic subsystem type, as a percentage.  A \"generic subsystem type\" "
29905 		"is a subsystem *category*, (for example, Engines), that includes one or more *individual* subsystems (for example, engine01, "
29906 		"engine02, and engine03) on a ship.\r\n\r\nThis is the way FreeSpace tests certain subsystem thresholds internally; for "
29907 		"example, if the integrity of all engine subsystems (that is, the combined strength of all engines divided by the maximum "
29908 		"total strength of all engines) is less than 30%, the player cannot warp out.\r\n\r\n"
29909 		"Returns a numeric value.  Takes 2 arguments...\r\n"
29910 		"\t1:\tName of ship to check\r\n"
29911 		"\t2:\tName of subsystem type to check\r\n" },
29912 
29913 	{ OP_HITS_LEFT_SUBSYSTEM_SPECIFIC, "hits-left-subsystem-specific (status operator)\r\n"
29914 		"\tReturns the current level of integrity of a specific subsystem, as a percentage.\r\n\r\n(If you were looking for the old "
29915 		"hits-left-subsystem operator, this is almost certainly the operator you want.  The hits-left-subsystem operator "
29916 		"suffers from a serious design flaw that causes it to behave like hits-left-subsystem-generic.  As such it has been deprecated "
29917 		"and will not appear in the operator list; it can only be used if you type it in manually.  Old missions using hits-left-subsystem "
29918 		"will still work, but mission designers are strongly encouraged to use the new operators instead.)\r\n\r\n"
29919 		"Returns a numeric value.  Takes 2 arguments...\r\n"
29920 		"\t1:\tName of ship to check\r\n"
29921 		"\t2:\tName of subsystem to check\r\n" },
29922 
29923 	{ OP_SIM_HITS_LEFT, "Simulated Hits left (Status operator)\r\n"
29924 		"\tReturns the current level of the specified ship's simulated hull as a percentage.\r\n\r\n"
29925 		"Returns a numeric value.  Takes 1 argument...\r\n"
29926 		"\t1:\tName of ship to check." },
29927 
29928 	{ OP_DISTANCE, "Distance (Status operator)\r\n"
29929 		"\tReturns the distance between two objects.  These objects can be either a ship, "
29930 		"a wing, or a waypoint.\r\n"
29931 		"When a wing or team is given (for either argument) the answer will be the shortest distance. \r\n\r\n"
29932 		"Returns a numeric value.  Takes 2 arguments...\r\n"
29933 		"\t1:\tThe name of one of the objects.\r\n"
29934 		"\t2:\tThe name of the other object." },
29935 
29936 	{ OP_DISTANCE_SUBSYSTEM, "Distance from ship subsystem (Status operator)\r\n"
29937 		"\tReturns the distance between an object and a ship subsystem.  The object can be either a ship, "
29938 		"a wing, or a waypoint.\r\n\r\n"
29939 		"Returns a numeric value.  Takes 3 arguments...\r\n"
29940 		"\t1:\tThe name of the object.\r\n"
29941 		"\t2:\tThe name of the ship which houses the subsystem.\r\n"
29942 		"\t3:\tThe name of the subsystem." },
29943 
29944 	{ OP_NUM_WITHIN_BOX, "Number of specified objects in the box specified\r\n"
29945 		"\t1: Box center (X)\r\n"
29946 		"\t2: Box center (Y)\r\n"
29947 		"\t3: Box center (Z)\r\n"
29948 		"\t4: Box width\r\n"
29949 		"\t5: Box height\r\n"
29950 		"\t6: Box depth\r\n"
29951 		"\tRest:\tShips or wings to check" },
29952 
29953 	{ OP_IS_IN_BOX, "Whether an object is in the box specified. If a second ship is specified, "
29954 		"the box is relative to that ship's reference frame. \r\n"
29955 		"\t1: Ships, wings, or points to check\r\n"
29956 		"\t2: Min X\r\n"
29957 		"\t3: Max X\r\n"
29958 		"\t4: Min Y\r\n"
29959 		"\t5: Max Y\r\n"
29960 		"\t6: Min Z\r\n"
29961 		"\t7: Max Z\r\n"
29962 		"\t8: Ship to use as reference frame (optional)." },
29963 
29964 	{ OP_IS_IN_MISSION, "Checks whether a given ship is presently in the mission.  This sexp doesn't check the arrival list or exited status; it only tests to see if the "
29965 		"ship is active.  This means that internally the sexp only returns SEXP_TRUE or SEXP_FALSE and does not use any of the special shortcut values.  This is useful "
29966 		"for ships created with ship-create, as those ships will not have used the conventional ship arrival list.\r\n\r\n"
29967 		"Takes 1 or more string arguments, which are checked against the ship list." },
29968 
29969 	{ OP_GET_DAMAGE_CAUSED, "Get damage caused (Status operator)\r\n"
29970 		"\tReturns the amount of damage one or more ships have done to a ship.\r\n\r\n"
29971 		"Takes 2 or more arguments...\r\n"
29972 		"\t1:\tShip that has been damaged.\r\n"
29973 		"\t2:\tName of ships that may have damaged it." },
29974 
29975 	{ OP_LAST_ORDER_TIME, "Last order time (Status operator)\r\n"
29976 		"\tReturns true if <count> seconds have elapsed since one or more ships have received "
29977 		"a meaningful order from the player.  A meaningful order is currently any order that "
29978 		"is not the warp out order.\r\n\r\n"
29979 		"Returns a boolean value.  Takes 2 or more arguments...\r\n"
29980 		"\t1:\tTime in seconds that must elapse.\r\n"
29981 		"\tRest:\tName of ship or wing to check for having received orders." },
29982 
29983 	{ OP_WHEN, "When (Conditional operator)\r\n"
29984 		"\tPerforms specified actions when a condition becomes true\r\n\r\n"
29985 		"Takes 2 or more arguments...\r\n"
29986 		"\t1:\tBoolean expression that must be true for actions to take place.\r\n"
29987 		"\tRest:\tActions to take when boolean expression becomes true." },
29988 
29989 	{ OP_COND, "Blah" },
29990 
29991 	// Goober5000
29992 	{ OP_WHEN_ARGUMENT, "When-argument (Conditional operator)\r\n"
29993 		"\tPerforms specified actions when a condition, given a set of arguments, becomes true.\r\n\r\n"
29994 		"Takes 3 or more arguments...\r\n"
29995 		"\t1:\tThe arguments to evaluate (see any-of, every-of, random-of, etc.).\r\n"
29996 		"\t2:\tBoolean expression that must be true for actions to take place.\r\n"
29997 		"\tRest:\tActions to take when the boolean expression becomes true." },
29998 
29999 	// Goober5000
30000 	{ OP_EVERY_TIME, "Every-time (Conditional operator)\r\n"
30001 		"\tThis is a version of \"when\" that will always evaluate its arguments.  It's useful "
30002 		"in situations where you need to repeatedly check things that may become true more than "
30003 		"once.  Since this sexp will execute every time it's evaluated, you may need to use it as "
30004 		"an argument to \"when\" if you want to impose restrictions on how it's called.\r\n\r\n"
30005 		"Takes 2 or more arguments...\r\n"
30006 		"\t1:\tBoolean expression that must be true for actions to take place.\r\n"
30007 		"\tRest:\tActions to take when boolean expression is true." },
30008 
30009 	// Goober5000
30010 	{ OP_EVERY_TIME_ARGUMENT, "Every-time-argument (Conditional operator)\r\n"
30011 		"\tThis is a version of \"when-argument\" that will always evaluate its arguments.  It's useful "
30012 		"in situations where you need to repeatedly check things that may become true more than "
30013 		"once.  Since this sexp will execute every time it's evaluated, you may need to use it as "
30014 		"an argument to \"when\" (not \"when-argument\") if you want to impose restrictions on how it's called.\r\n\r\n"
30015 		"Takes 3 or more arguments...\r\n"
30016 		"\t1:\tThe arguments to evaluate (see any-of, all-of, random-of, etc.).\r\n"
30017 		"\t2:\tBoolean expression that must be true for actions to take place.\r\n"
30018 		"\tRest:\tActions to take when the boolean expression becomes true." },
30019 
30020 	// Goober5000
30021 	{ OP_IF_THEN_ELSE, "If-then-else (Conditional operator)\r\n"
30022 		"\tPerforms one action if a condition is true (like \"when\"), or another action (or set of actions) if the condition is false.  "
30023 		"Note that this sexp only completes one of its branches once the condition has been determined; "
30024 		"it does not come back later and evaluate the other branch if the condition happens to switch truth values.\r\n\r\n"
30025 		"Takes 3 or more arguments...\r\n"
30026 		"\t1:\tBoolean expression to evaluate.\r\n"
30027 		"\t2:\tActions to take if that expression becomes true.\r\n"
30028 		"\tRest:\tActions to take if that expression becomes false.\r\n" },
30029 
30030 	// Karajorma
30031 	{ OP_DO_FOR_VALID_ARGUMENTS, "Do-for-valid-arguments (Conditional operator)\r\n"
30032 		"\tPerforms specified actions once for each valid " SEXP_ARGUMENT_STRING " in the parent conditional.\r\n"
30033 		"\tMust not be used for any SEXP that actually contains " SEXP_ARGUMENT_STRING " as these are already being executed\r\n"
30034 		"\tmultiple times without using Do-for-valid-arguments. Any use of "  SEXP_ARGUMENT_STRING " and will \r\n"
30035 		"\tprevent execution of the entire SEXP unless it is nested inside another when(or every-time)-argument SEXP.\r\n\r\n"
30036 		"Takes 1 or more arguments...\r\n"
30037 		"\tAll:\tActions to take." },
30038 
30039 	// Karajorma
30040 	{ OP_NUM_VALID_ARGUMENTS, "num-valid-arguments (Conditional operator)\r\n"
30041 		"\tReturns the number of valid arguments in the argument list.\r\n\r\n"
30042 		"Takes no arguments...\r\n"},
30043 
30044 	// Goober5000
30045 	{ OP_ANY_OF, "Any-of (Conditional operator)\r\n"
30046 		"\tSupplies arguments for the " SEXP_ARGUMENT_STRING " special data item.  Any of the supplied arguments can satisfy the expression(s) "
30047 		"in which " SEXP_ARGUMENT_STRING " is used.\r\n\r\n"
30048 		"In practice, this will behave like a standard \"for-each\" statement, evaluating the action operators for each argument that satisfies the condition.\r\n\r\n"
30049 		"Takes 1 or more arguments...\r\n"
30050 		"\tAll:\tAnything that could be used in place of " SEXP_ARGUMENT_STRING "." },
30051 
30052 	// Goober5000
30053 	{ OP_EVERY_OF, "Every-of (Conditional operator)\r\n"
30054 		"\tSupplies arguments for the " SEXP_ARGUMENT_STRING " special data item.  Every one of the supplied arguments will be evaluated to satisfy the expression(s) "
30055 		"in which " SEXP_ARGUMENT_STRING " is used.\r\n\r\n"
30056 		"Takes 1 or more arguments...\r\n"
30057 		"\tAll:\tAnything that could be used in place of " SEXP_ARGUMENT_STRING "." },
30058 
30059 	// Goober5000
30060 	{ OP_RANDOM_OF, "Random-of (Conditional operator)\r\n"
30061 		"\tSupplies arguments for the " SEXP_ARGUMENT_STRING " special data item.  A random supplied argument will be selected to satisfy the expression(s) "
30062 		"in which " SEXP_ARGUMENT_STRING " is used. The same argument will be returned by all subsequent calls\r\n\r\n"
30063 		"Takes 1 or more arguments...\r\n"
30064 		"\tAll:\tAnything that could be used in place of " SEXP_ARGUMENT_STRING "." },
30065 
30066 	// Karajorma
30067 	{ OP_RANDOM_MULTIPLE_OF, "Random-multiple-of (Conditional operator)\r\n"
30068 		"\tSupplies arguments for the " SEXP_ARGUMENT_STRING " special data item.  A random supplied argument will be selected to satisfy the expression(s) "
30069 		"in which " SEXP_ARGUMENT_STRING " is used.\r\n\r\n"
30070 		"Takes 1 or more arguments...\r\n"
30071 		"\tAll:\tAnything that could be used in place of " SEXP_ARGUMENT_STRING "." },
30072 
30073 	// Goober5000
30074 	{ OP_NUMBER_OF, "Number-of (Conditional operator)\r\n"
30075 		"\tSupplies arguments for the " SEXP_ARGUMENT_STRING " special data item.  Any [number] of the supplied arguments can satisfy the expression(s) "
30076 		"in which " SEXP_ARGUMENT_STRING " is used.\r\n\r\n"
30077 		"Takes 2 or more arguments...\r\n"
30078 		"\t1:\tNumber of arguments, as above\r\n"
30079 		"\tRest:\tAnything that could be used in place of " SEXP_ARGUMENT_STRING "." },
30080 
30081 	// Karajorma
30082 	{ OP_IN_SEQUENCE, "In-Sequence (Conditional operator)\r\n"
30083 		"\tSupplies arguments for the " SEXP_ARGUMENT_STRING " special data item.  The first argument in the list will be selected to satisfy the expression(s) "
30084 		"in which " SEXP_ARGUMENT_STRING " is used. The same argument will be returned by all subsequent calls\r\n\r\n"
30085 		"Takes 1 or more arguments...\r\n"
30086 		"\tAll:\tAnything that could be used in place of " SEXP_ARGUMENT_STRING "." },
30087 
30088 	// Goober5000
30089 	{ OP_FOR_COUNTER, "For-Counter (Conditional operator)\r\n"
30090 		"\tSupplies counter values for the " SEXP_ARGUMENT_STRING " special data item.  This sexp will count up from the start value to the stop value, and each value will be provided as an argument to the action operators.  "
30091 		"The default increment is 1, but if the optional increment parameter is provided, the counter will increment by that number.  The stop value will be supplied if appropriate; e.g. counting from 0 to 10 by 2 will supply 0, 2, 4, 6, 8, and 10; "
30092 		"but counting by 3 will supply 0, 3, 6, and 9.\r\n\r\n"
30093 		"Note that the counter values are all treated as valid arguments, and it is impossible to invalidate a counter argument.  If you want to invalidate a counter value, use Any-of and list the values explicitly.\r\n\r\n"
30094 		"This sexp will usually need to be accompanied by the string-to-int sexp, as the counter variables are provided in string format but are most useful in integer format.\r\n\r\n"
30095 		"Takes 2 or 3 arguments...\r\n"
30096 		"\t1:\tCounter start value\r\n"
30097 		"\t2:\tCounter stop value\r\n"
30098 		"\t3:\tCounter increment (optional)" },
30099 
30100 	// Goober5000
30101 	{ OP_INVALIDATE_ARGUMENT, "Invalidate-argument (Conditional operator)\r\n"
30102 		"\tRemoves an argument from future consideration as a " SEXP_ARGUMENT_STRING " special data item.\r\n"
30103 		"Takes 1 or more arguments...\r\n"
30104 		"\tAll:\tThe argument to remove from the preceding argument list." },
30105 
30106 	// Karajorma
30107 	{ OP_VALIDATE_ARGUMENT, "Validate-argument (Conditional operator)\r\n"
30108 		"\tRestores an argument for future consideration as a " SEXP_ARGUMENT_STRING " special data item.\r\n"
30109 		"\tIf the argument hasn't been previously invalidated, it will do nothing.\r\n"
30110 		"Takes 1 or more arguments...\r\n"
30111 		"\tAll:\tThe argument to restore to the preceding argument list." },
30112 
30113 	// Karajorma
30114 	{ OP_INVALIDATE_ALL_ARGUMENTS, "Invalidate-all-arguments (Conditional operator)\r\n"
30115 		"\tRemoves all argument from future consideration as " SEXP_ARGUMENT_STRING " special data items.\r\n"
30116 		"Takes no arguments." },
30117 
30118 	// Karajorma
30119 	{ OP_VALIDATE_ALL_ARGUMENTS, "Validate-all-arguments (Conditional operator)\r\n"
30120 		"\tRestores all arguments for future consideration as " SEXP_ARGUMENT_STRING " special data items.\r\n"
30121 		"\tIf the argument hasn't been previously invalidated, it will do nothing.\r\n"
30122 		"Takes no arguments." },
30123 
30124 	// Goober5000 - added wing capability
30125 	{ OP_CHANGE_IFF, "Change IFF (Action operator)\r\n"
30126 		"\tSets the specified ship(s) or wing(s) to the specified team.\r\n"
30127 		"Takes 2 or more arguments...\r\n"
30128 		"\t1:\tTeam to change to (\"friendly\", \"hostile\" or \"unknown\").\r\n"
30129 		"\tRest:\tName of ship or wing to change team status of." },
30130 
30131 	// Wanderer
30132 	{ OP_CHANGE_IFF_COLOR, "Change IFF Color (Action operator)\r\n"
30133 		"\tSets the specified ship(s) or wing(s) apparent color.\r\n"
30134 		"Takes 6 or more arguments...\r\n"
30135 		"\t1:\tName of the team from which target is observed from.\r\n"
30136 		"\t2:\tName of the team of the observed target to receive the alternate color.\r\n"
30137 		"\t3:\tRed color (value from 0 to 255).\r\n"
30138 		"\t4:\tGreen color (value from 0 to 255).\r\n"
30139 		"\t5:\tBlue color (value from 0 to 255).\r\n"
30140 		"\tRest:\tName of ship or wing to change team status of." },
30141 
30142 	// Goober5000
30143 	{ OP_CHANGE_AI_CLASS, "Change AI Class (Action operator)\r\n"
30144 		"\tSets the specified ship or ship subsystem(s) to the specified ai class.\r\n"
30145 		"Takes 2 or more arguments...\r\n"
30146 		"\t1:\tAI Class to change to (\"None\", \"Coward\", \"Lieutenant\", etc.)\r\n"
30147 		"\t2:\tName of ship to change AI class of\r\n"
30148 		"\tRest:\tName of subsystem to change AI class of (optional)" },
30149 
30150 	{ OP_MODIFY_VARIABLE, "Modify-variable (Misc. operator)\r\n"
30151 		"\tModifies variable to specified value\r\n\r\n"
30152 		"Takes 2 arguments...\r\n"
30153 		"\t1:\tName of Variable.\r\n"
30154 		"\t2:\tValue to be set." },
30155 
30156 	{ OP_GET_VARIABLE_BY_INDEX, "get-variable-by-index (originally variable-array-get)\r\n"
30157 		"\tGets the value of the variable specified by the given index.  This is an alternate way "
30158 		"to access variables rather than by their names, and it enables cool features such as "
30159 		"arrays and pointers.\r\n\r\nPlease note that only numeric variables are supported.  Any "
30160 		"attempt to access a string variable will result in a value of SEXP_NAN_FOREVER being returned.\r\n\r\n"
30161 		"Takes 1 argument...\r\n"
30162 		"\t1:\tIndex of variable, from 0 to MAX_SEXP_VARIABLES - 1." },
30163 
30164 	{ OP_SET_VARIABLE_BY_INDEX, "set-variable-by-index (originally variable-array-set)\r\n"
30165 		"\tSets the value of the variable specified by the given index.  This is an alternate way "
30166 		"to modify variables rather than by their names, and it enables cool features such as "
30167 		"arrays and pointers.\r\n\r\nIn contrast to get-variable-by-index, note that this sexp "
30168 		"*does* allow the modification of string variables.\r\n\r\n"
30169 		"Takes 2 arguments...\r\n"
30170 		"\t1:\tIndex of variable, from 0 to MAX_SEXP_VARIABLES - 1.\r\n"
30171 		"\t2:\tValue to be set." },
30172 
30173 	{ OP_COPY_VARIABLE_FROM_INDEX, "copy-variable-from-index\r\n"
30174 		"\tRetrieves the value of the variable specified by the given index and stores it in another variable.  "
30175 		"This is very similar to variable-array-get, except the result is stored in a new variable rather than "
30176 		"being returned by value.  One important difference is that this sexp can be used to copy string variables as well as numeric variables.\r\n\r\n"
30177 		"Takes 2 arguments...\r\n"
30178 		"\t1:\tIndex of source variable, from 0 to MAX_SEXP_VARIABLES - 1.\r\n"
30179 		"\t2:\tDestination variable.  The type of this variable must match the type of the variable referenced by the index." },
30180 
30181 	{ OP_COPY_VARIABLE_BETWEEN_INDEXES, "copy-variable-between-indexes\r\n"
30182 		"\tRetrieves the value of the variable specified by the first index and stores it in the variable specified by the second index.  The first variable is not modified.\r\n\r\n"
30183 		"Takes 2 arguments...\r\n"
30184 		"\t1:\tIndex of source variable, from 0 to MAX_SEXP_VARIABLES - 1.\r\n"
30185 		"\t2:\tIndex of destination variable, from 0 to MAX_SEXP_VARIABLES - 1.  The types of both variables must match." },
30186 
30187 	{ OP_PROTECT_SHIP, "Protect ship (Action operator)\r\n"
30188 		"\tProtects a ship from being attacked by any enemy ship.  Any ship "
30189 		"that is protected will not come under enemy fire.\r\n\r\n"
30190 		"Takes 1 or more arguments...\r\n"
30191 		"\tAll:\tName of ship(s) to protect." },
30192 
30193 	{ OP_UNPROTECT_SHIP, "Unprotect ship (Action operator)\r\n"
30194 		"\tUnprotects a ship from being attacked by any enemy ship.  Any ship "
30195 		"that is not protected can come under enemy fire.  This function is the opposite "
30196 		"of protect-ship.\r\n\r\n"
30197 		"Takes 1 or more arguments...\r\n"
30198 		"\tAll:\tName of ship(s) to unprotect." },
30199 
30200 	{ OP_BEAM_PROTECT_SHIP, "Beam Protect ship (Action operator)\r\n"
30201 		"\tProtects a ship from being attacked with beam weapon.  Any ship "
30202 		"that is beam protected will not come under enemy beam fire.\r\n\r\n"
30203 		"Takes 1 or more arguments...\r\n"
30204 		"\tAll:\tName of ship(s) to protect." },
30205 
30206 	{ OP_BEAM_UNPROTECT_SHIP, "Beam Unprotect ship (Action operator)\r\n"
30207 		"\tUnprotects a ship from being attacked with beam weapon.  Any ship "
30208 		"that is not beam protected can come under enemy beam fire.  This function is the opposite "
30209 		"of beam-protect-ship.\r\n\r\n"
30210 		"Takes 1 or more arguments...\r\n"
30211 		"\tAll:\tName of ship(s) to unprotect." },
30212 
30213 	// Goober5000
30214 	{ OP_TURRET_PROTECT_SHIP, "Turret Protect ship (Action operator)\r\n"
30215 		"\tProtects a ship from being attacked with a turret weapon of a given type.  Any ship "
30216 		"that is turret protected will not come under enemy fire from that type of turret, though it may come under fire by other turrets.\r\n\r\n"
30217 		"Takes 2 or more arguments...\r\n"
30218 		"\t1:\tType of turret (currently supported types are \"beam\", \"flak\", \"laser\", and \"missile\")\r\n"
30219 		"\tRest:\tName of ship(s) to protect." },
30220 
30221 	// Goober5000
30222 	{ OP_TURRET_UNPROTECT_SHIP, "Turret Unprotect ship (Action operator)\r\n"
30223 		"\tUnprotects a ship from being attacked with a turret weapon of a given type.  Any ship "
30224 		"that is not turret protected can come under enemy fire from that type of turret.  This function is the opposite "
30225 		"of turret-protect-ship.\r\n\r\n"
30226 		"Takes 2 or more arguments...\r\n"
30227 		"\t1:\tType of turret (currently supported types are \"beam\", \"flak\", \"laser\", and \"missile\")\r\n"
30228 		"\tRest:\tName of ship(s) to unprotect." },
30229 
30230 	{ OP_SEND_MESSAGE, "Send message (Action operator)\r\n"
30231 		"\tSends a message to the player.  Can be send by a ship, wing, or special "
30232 		"source.  To send it from a special source, make the first character of the first "
30233 		"argument a \"#\".\r\n\r\n"
30234 		"Takes 3 arguments...\r\n"
30235 		"\t1:\tName of who the message is from.\r\n"
30236 		"\t2:\tPriority of message (\"Low\", \"Normal\" or \"High\").\r\n"
30237 		"\t3:\tName of message (from message editor)." },
30238 
30239 	// Karajorma
30240 	{ OP_ENABLE_BUILTIN_MESSAGES, "Enable builtin messages (Action operator)\r\n"
30241 		"\tTurns the built in messages sent by command or pilots on\r\n"
30242 		"Takes 0 or more arguments...\r\n"
30243 		"If no arguments are supplied any ships not given individual silence orders will be able\r\n"
30244 		"to send built in messages. Command will also be unsilenced\r\n"
30245 		"Using the Any Wingman option cancels radio silence for all ships in wings.\r\n"
30246 		"\tAll:\tName of ship to allow to talk." },
30247 
30248 	// Karajorma
30249 	{ OP_DISABLE_BUILTIN_MESSAGES, "Disable builtin messages (Action operator)\r\n"
30250 		"\tTurns the built in messages sent by command or pilots off\r\n"
30251 		"Takes 0 or more arguments....\r\n"
30252 		"If no arguments are supplied all built in messages are disabled.\r\n"
30253 		"Using the Any Wingman option silences for all ships in wings.\r\n"
30254 		"\tAll:\tName of ship to be silenced." },
30255 
30256 	// Karajorma
30257 	{ OP_SET_MISSION_MOOD, "set Mission Mood (Action operator)\r\n"
30258 		"\tSets the mood of the mission, this affects the choice of builtin messages sent by wingmen\r\n"
30259 		"Takes 1 argument...\r\n"
30260 		"\t1:\tMission mood (from messages.tbl) to use." },
30261 
30262 	// Karajorma
30263 	{ OP_SET_PERSONA, "Set Persona (Action operator)\r\n"
30264 		"\tSets the persona of the supplied ship to the persona supplied\r\n"
30265 		"Takes 2 or more arguments...\r\n"
30266 		"\t1:\tPersona to use."
30267 		"\tRest:\tName of the ship." },
30268 
30269 	{ OP_SELF_DESTRUCT, "Self destruct (Action operator)\r\n"
30270 		"\tCauses the specified ship(s) to self destruct.\r\n\r\n"
30271 		"Takes 1 or more arguments...\r\n"
30272 		"\tAll:\tName of ship to self destruct." },
30273 
30274 	{ OP_NEXT_MISSION, "Next Mission (Action operator)\r\n"
30275 		"\tThe next mission operator is used for campaign branching in the campaign editor.  "
30276 		"It specifies which mission should played be next in the campaign.  This operator "
30277 		"generally follows a 'when' or 'cond' statment in the campaign file.\r\n\r\n"
30278 		"Takes 1 argument...\r\n"
30279 		"\t1:\tName of mission (filename) to proceed to." },
30280 
30281 	{ OP_CLEAR_GOALS, "Clear goals (Action operator)\r\n"
30282 		"\tClears the goals for the specified ships and/or wings.\r\n\r\n"
30283 		"Takes 1 or more arguments...\r\n"
30284 		"\tAll:\tName of ship or wing." },
30285 
30286 	{ OP_ADD_GOAL, "Add goal (Action operator)\r\n"
30287 		"\tAdds a goal to a ship or wing.\r\n\r\n"
30288 		"Takes 2 arguments...\r\n"
30289 		"\t1:\tName of ship or wing to add goal to.\r\n"
30290 		"\t2:\tGoal to add." },
30291 
30292 	// Goober5000
30293 	{ OP_REMOVE_GOAL, "Remove goal (Action operator)\r\n"
30294 		"\tRemoves a goal from a ship or wing.\r\n\r\n"
30295 		"Takes 2 arguments...\r\n"
30296 		"\t1:\tName of ship or wing to remove goal from.\r\n"
30297 		"\t2:\tGoal to remove." },
30298 
30299 	{ OP_SABOTAGE_SUBSYSTEM, "Sabotage subystem (Action operator)\r\n"
30300 		"\tReduces the specified subsystem integrity by the specified percentage."
30301 		"If the percntage strength of the subsystem (after completion) is less than 0%,"
30302 		"subsystem strength is set to 0%.\r\n\r\n"
30303 		"Takes 3 arguments...\r\n"
30304 		"\t1:\tName of ship subsystem is on.\r\n"
30305 		"\t2:\tName of subsystem to sabotage.\r\n"
30306 		"\t3:\tPercentage to reduce subsystem integrity by." },
30307 
30308 	{ OP_REPAIR_SUBSYSTEM, "Repair Subystem (Action operator)\r\n"
30309 		"\tIncreases the specified subsystem integrity by the specified percentage."
30310 		"If the percntage strength of the subsystem (after completion) is greater than 100%,"
30311 		"subsystem strength is set to 100%.\r\n\r\n"
30312 		"Takes 4 arguments...\r\n"
30313 		"\t1:\tName of ship subsystem is on.\r\n"
30314 		"\t2:\tName of subsystem to repair.\r\n"
30315 		"\t3:\tPercentage to increase subsystem integrity by.\r\n"
30316 		"\t4:\tRepair turret submodel.  Optional argument that defaults to true."},
30317 
30318 	{ OP_SET_SUBSYSTEM_STRNGTH, "Set Subsystem Strength (Action operator)\r\n"
30319 		"\tSets the specified subsystem to the the specified percentage."
30320 		"If the percentage specified is < 0, strength is set to 0.  If the percentage is "
30321 		"> 100 % the subsystem strength is set to 100%.\r\n\r\n"
30322 		"Takes 3 arguments...\r\n"
30323 		"\t1:\tName of ship subsystem is on.\r\n"
30324 		"\t2:\tName of subsystem to set strength.\r\n"
30325 		"\t3:\tPercentage to set subsystem integrity to.\r\n"
30326 		"\t4:\tRepair turret submodel.  Optional argument that defaults to true."},
30327 
30328 	{ OP_DESTROY_SUBSYS_INSTANTLY, "destroy-subsys-instantly\r\n"
30329 		"\tDetroys the specified subsystems without effects."
30330 		"\tSingle player only!"
30331 		"Takes 2 or more arguments...\r\n"
30332 		"\t1:\tName of ship subsystem is on.\r\n"
30333 		"\tRest:\tName of subsystem to destroy.\r\n"},
30334 
30335 	{ OP_INVALIDATE_GOAL, "Invalidate goal (Action operator)\r\n"
30336 		"\tMakes a mission goal invalid, which causes it to not show up on mission goals "
30337 		"screen, or be evaluated.\r\n"
30338 		"Takes 1 or more arguments...\r\n"
30339 		"\tAll:\tName of mission goal to invalidate." },
30340 
30341 	{ OP_VALIDATE_GOAL, "Validate goal (Action operator)\r\n"
30342 		"\tMakes a mission goal valid again, so it shows up on mission goals screen.\r\n"
30343 		"Takes 1 or more arguments...\r\n"
30344 		"\tAll:\tName of mission goal to validate." },
30345 
30346 	{ OP_SEND_RANDOM_MESSAGE, "Send random message (Action operator)\r\n"
30347 		"\tSends a random message to the player from those supplied.  Can be send by a "
30348 		"ship, wing, or special source.  To send it from a special source, make the first "
30349 		"character of the first argument a \"#\".\r\n\r\n"
30350 		"Takes 3 or more arguments...\r\n"
30351 		"\t1:\tName of who the message is from.\r\n"
30352 		"\t2:\tPriority of message (\"Low\", \"Normal\" or \"High\")."
30353 		"\tRest:\tName of message (from message editor)." },
30354 
30355 	{ OP_TRANSFER_CARGO, "Transfer Cargo (Action operator)\r\n"
30356 		"\tTransfers the cargo from one ship to another ship.\r\n\r\n"
30357 		"Takes 2 arguments...\r\n"
30358 		"\t1:\tName of ship that cargo is being transferred from.\r\n"
30359 		"\t2:\tName of ship that cargo is being transferred to." },
30360 
30361 	{ OP_EXCHANGE_CARGO, "Exchange Cargo (Action operator)\r\n"
30362 		"\tExchanges the cargos of two ships.  If one of the two ships contains no cargo, "
30363 		"the cargo is transferred instead.\r\n"
30364 		"Takes 2 arguments...\r\n"
30365 		"\t1:\tName of one of the ships.\r\n"
30366 		"\t2:\tName of the other ship." },
30367 
30368 	// Goober5000
30369 	{ OP_SET_CARGO, "Set Cargo (Action operator)\r\n"
30370 		"\tSets the cargo on a ship or ship subsystem.  The cargo-no-deplete flag status is carried through to the new cargo.\r\n"
30371 		"Takes 2 or 3 arguments...\r\n"
30372 		"\t1:\tName of the cargo\r\n"
30373 		"\t2:\tName of the ship\r\n"
30374 		"\t3:\tName of the ship subsystem (optional)" },
30375 
30376 	// Goober5000
30377 	{ OP_IS_CARGO, "Is Cargo (Status operator)\r\n"
30378 		"\tChecks whether the specified ship or ship subsystem contains a particular cargo.\r\n"
30379 		"Takes 2 or 3 arguments...\r\n"
30380 		"\t1:\tName of the cargo\r\n"
30381 		"\t2:\tName of the ship\r\n"
30382 		"\t3:\tName of the ship subsystem (optional)" },
30383 
30384 	// Goober5000
30385 	{ OP_CHANGE_SOUNDTRACK, "change-soundtrack\r\n"
30386 		"\tChanges the mission music.  Takes 1 argument...\r\n"
30387 		"\t1: Name of the music selection (taken from music.tbl)" },
30388 
30389 	// Goober5000
30390 	{ OP_PLAY_SOUND_FROM_TABLE, "play-sound-from-table\r\n"
30391 		"\tPlays a sound listed in the Game Sounds section of sounds.tbl.  Note that if the sound is a non-3D sound (if the min and max radius are 0, or unspecified), the sound will just play without being fixed at a particular position in space.  "
30392 		"In this case, the origin coordinates will be ignored.  (A better design would have put the sound index first and made the origin arguments optional, but the difference between 2D and 3D sounds was not understood at the time.  C'est la vie.)\r\n\r\n"
30393 		"Takes 4 arguments...\r\n"
30394 		"\t1: Origin X\r\n"
30395 		"\t2: Origin Y\r\n"
30396 		"\t3: Origin Z\r\n"
30397 		"\t4: Sound (index into sounds.tbl)" },
30398 
30399 	// Goober5000
30400 	{ OP_PLAY_SOUND_FROM_FILE, "play-sound-from-file\r\n"
30401 		"\tPlays a sound, such as a music soundtrack, from a file.  Important: Only one sound at a time can be played with this sexp!\r\n"
30402 		"Takes 1 to 3 arguments...\r\n"
30403 		"\t1: Sound (file name)\r\n"
30404 		"\t2: Enter a non-zero number to loop. default is off (optional).\r\n"
30405 		"\t3: Enter a non-zero number to use environment effects. default is off (optional)."
30406 	},
30407 
30408 	// Goober5000
30409 	{ OP_CLOSE_SOUND_FROM_FILE, "close-sound-from-file\r\n"
30410 		"\tCloses the currently playing sound started by play-sound-from-file, if there is any.  Takes 1 argument...\r\n"
30411 		"\t1: Fade (default is true)" },
30412 
30413 	// Taylor
30414 	{ OP_SET_SOUND_ENVIRONMENT, "set-sound-environment\r\n"
30415 		"Sets the EAX environment for all sound effects.  Optionally sets one or more parameters specific to the environment.  Takes 1 or more arguments...\r\n"
30416 		"\t1:\tSound environment name (a value of \"" SEXP_NONE_STRING "\" will disable the effects)\r\n"
30417 		"\t2:\tEnvironment option (optional)\r\n"
30418 		"\t3:\tEnvironment value x 1000, e.g. 10 is 0.01 (optional)\r\n"
30419 		"Use Add-Data to specify additional environment options in repeating option-value pairs, just like Send-Message-List can have additional messages in source-priority-message-delay groups.\r\n\r\n"
30420 		"IMPORTANT: each additional option in the list MUST HAVE two entries; any option without the two proper fields will be ignored, as will any successive options." },
30421 
30422 	// Taylor
30423 	{ OP_UPDATE_SOUND_ENVIRONMENT, "update-sound-environment\r\n"
30424 		"Updates the current EAX environment with new values.  Takes 2 or more arguments...\r\n"
30425 		"\t1:\tEnvironment option\r\n"
30426 		"\t2:\tEnvironment value x 1000, e.g. 10 is 0.01\r\n"
30427 		"Use Add-Data to specify additional environment options in repeating option-value pairs, just like Send-Message-List can have additional messages in source-priority-message-delay groups.\r\n\r\n"
30428 		"IMPORTANT: Each additional option in the list MUST HAVE two entries; any option without the two proper fields will be ignored, as will any successive options." },
30429 
30430 	//The E
30431 	{ OP_ADJUST_AUDIO_VOLUME, "adjust-audio-volume\r\n"
30432 		"Adjusts the relative volume of one sound type. Takes 1 to 3 arguments....\r\n"
30433 		"\t1:\tSound Type to adjust, either Music, Voice or Effects. Will act as a reset for the given category, if no other argument present\r\n"
30434 		"\t2:\tPercentage of the users' settings to adjust to (optional), 0 will be silence, 100 means the maximum volume as set by the user\r\n"
30435 		"\t3:\tFade time (optional), time in milliseconds to adjust the volume"},
30436 
30437 	// Goober5000
30438 	{ OP_SET_EXPLOSION_OPTION, "set-explosion-option\r\n"
30439 		"Sets an explosion option on a particular ship.  Takes 3 or more arguments...\r\n"
30440 		"\t1:\tShip name\r\n"
30441 		"\t2:\tExplosion option\r\n"
30442 		"\t3:\tExplosion value (for shockwave speed, 0 will produce no shockwave; for death roll time, 0 will use the default time)\r\n"
30443 		"Use Add-Data to specify additional explosion options in repeating option-value pairs, just like Send-Message-List can have additional messages in source-priority-message-delay groups.\r\n\r\n"
30444 		"IMPORTANT: Each additional option in the list MUST HAVE two entries; any option without the two proper fields will be ignored, as will any successive options." },
30445 
30446 	// Goober5000
30447 	{ OP_EXPLOSION_EFFECT, "explosion-effect\r\n"
30448 		"\tCauses an explosion at a given origin, with the given parameters.  "
30449 		"Takes 11 or 13 arguments...\r\n"
30450 		"\t1:  Origin X\r\n"
30451 		"\t2:  Origin Y\r\n"
30452 		"\t3:  Origin Z\r\n"
30453 		"\t4:  Damage\r\n"
30454 		"\t5:  Blast force\r\n"
30455 		"\t6:  Size of explosion (if 0, explosion will not be visible)\r\n"
30456 		"\t7:  Inner radius to apply damage (if 0, explosion will not be visible)\r\n"
30457 		"\t8:  Outer radius to apply damage (if 0, explosion will not be visible)\r\n"
30458 		"\t9:  Shockwave speed (if 0, there will be no shockwave)\r\n"
30459 		"\t10: Type - For backward compatibility 0 = medium, 1 = large1 (4th in table), 2 = large2 (5th in table)\r\n"
30460 		"           3 or greater link to respctive entry in fireball.tbl\r\n"
30461 		"\t11: Sound (index into sounds.tbl)\r\n"
30462 		"\t12: EMP intensity (optional)\r\n"
30463 		"\t13: EMP duration in seconds (optional)" },
30464 
30465 	// Goober5000
30466 	{ OP_WARP_EFFECT, "warp-effect\r\n"
30467 		"\tCauses a subspace warp effect at a given origin, facing toward a given location, with the given parameters.\r\n"
30468 		"Takes 12 arguments...\r\n"
30469 		"\t1:  Origin X\r\n"
30470 		"\t2:  Origin Y\r\n"
30471 		"\t3:  Origin Z\r\n"
30472 		"\t4:  Location X\r\n"
30473 		"\t5:  Location Y\r\n"
30474 		"\t6:  Location Z\r\n"
30475 		"\t7:  Radius\r\n"
30476 		"\t8:  Duration in seconds (values smaller than 4 are ignored)\r\n"
30477 		"\t9:  Warp opening sound (index into sounds.tbl)\r\n"
30478 		"\t10: Warp closing sound (index into sounds.tbl)\r\n"
30479 		"\t11: Type (0 for standard blue [default], 1 for Knossos green)\r\n"
30480 		"\t12: Shape (0 for 2-D [default], 1 for 3-D)" },
30481 
30482 	// Goober5000
30483 	{ OP_SET_OBJECT_FACING, "set-object-facing\r\n"
30484 		"\tSets an object's orientation to face the specified coordinates.  "
30485 		"Takes 4 arguments...\r\n"
30486 		"\t1: The name of a ship or wing.\r\n"
30487 		"\t2: The X coordinate to face.\r\n"
30488 		"\t3: The Y coordinate to face.\r\n"
30489 		"\t4: The Z coordinate to face.\r\n"
30490 		"\t5: Turn time in milliseconds (optional)\r\n"
30491 		"\t6: Bank (optional). Enter a non-zero value to enable banking." },
30492 
30493 	// Goober5000
30494 	{ OP_SET_OBJECT_FACING_OBJECT, "set-object-facing-object\r\n"
30495 		"\tSets an object's orientation to face the specified object.  "
30496 		"Takes 2 arguments...\r\n"
30497 		"\t1: The name of a ship or wing.\r\n"
30498 		"\t2: The object to face.\r\n"
30499 		"\t3: Turn time in milliseconds (optional)\r\n"
30500 		"\t4: Bank (optional). Enter a non-zero value to enable banking." },
30501 
30502 	// Wanderer
30503 	{ OP_SHIP_MANEUVER, "ship-maneuver\r\n"
30504 		"\tCombines the effects of the ship-rot-maneuver and ship-lat-maneuver sexps.  Takes 10 arguments:\r\n"
30505 		"\t1: The name of a ship or wing\r\n"
30506 		"\t2: Duration of the maneuver, in milliseconds\r\n"
30507 		"\t3: Heading movement velocity, as a percentage (-100 to 100) of the tabled maximum heading velocity, or 0 to not modify the ship's current value\r\n"
30508 		"\t4: Pitch movement velocity, as a percentage (-100 to 100) of the tabled maximum pitch velocity, or 0 to not modify the ship's current value\r\n"
30509 		"\t5: Bank movement velocity, as a percentage (-100 to 100) of the tabled maximum bank velocity, or 0 to not modify the ship's current value\r\n"
30510 		"\t6: Whether to apply all of the rotational velocity values even if any of them are 0\r\n"
30511 		"\t7: Vertical movement velocity, as a percentage (-100 to 100) of the tabled maximum vertical velocity, or 0 to not modify the ship's current value\r\n"
30512 		"\t8: Sideways movement velocity, as a percentage (-100 to 100) of the tabled maximum sideways velocity, or 0 to not modify the ship's current value\r\n"
30513 		"\t9: Forward movement velocity, as a percentage (-100 to 100) of the tabled maximum forward velocity, or 0 to not modify the ship's current value\r\n"
30514 		"\t10: Whether to apply all of the lateral velocity values even if any of them are 0\r\n" },
30515 
30516 	// Wanderer
30517 	{ OP_SHIP_ROT_MANEUVER, "ship-rot-maneuver\r\n"
30518 		"\tCauses a ship to move in a rotational direction.  For the purposes of this sexp, this means the ship rotates along its own heading, pitch, or bank axis (or a combination of axes) without regard to normal ship rotation rules.  "
30519 		"You may find it necessary to disable the ship AI (e.g. by issuing a play-dead order) before running this sexp.\r\n\r\n"
30520 		"Takes 6 arguments:\r\n"
30521 		"\t1: The name of a ship or wing\r\n"
30522 		"\t2: Duration of the maneuver, in milliseconds\r\n"
30523 		"\t3: Heading movement velocity, as a percentage (-100 to 100) of the tabled maximum heading velocity, or 0 to not modify the ship's current value\r\n"
30524 		"\t4: Pitch movement velocity, as a percentage (-100 to 100) of the tabled maximum pitch velocity, or 0 to not modify the ship's current value\r\n"
30525 		"\t5: Bank movement velocity, as a percentage (-100 to 100) of the tabled maximum bank velocity, or 0 to not modify the ship's current value\r\n"
30526 		"\t6: Whether to apply all of the above velocity values even if any of them are 0\r\n" },
30527 
30528 	// Wanderer
30529 	{ OP_SHIP_LAT_MANEUVER, "ship-lat-maneuver\r\n"
30530 		"\tCauses a ship to move in a lateral direction.  For the purposes of this sexp, this means the ship translates along its own X, Y, or Z axis (or a combination of axes) without regard to normal ship movement rules.  "
30531 		"You may find it necessary to disable the ship AI (e.g. by issuing a play-dead order) before running this sexp.\r\n\r\n"
30532 		"Takes 6 arguments:\r\n"
30533 		"\t1: The name of a ship or wing\r\n"
30534 		"\t2: Duration of the maneuver, in milliseconds\r\n"
30535 		"\t3: Vertical movement velocity, as a percentage (-100 to 100) of the tabled maximum vertical velocity, or 0 to not modify the ship's current value\r\n"
30536 		"\t4: Sideways movement velocity, as a percentage (-100 to 100) of the tabled maximum sideways velocity, or 0 to not modify the ship's current value\r\n"
30537 		"\t5: Forward movement velocity, as a percentage (-100 to 100) of the tabled maximum forward velocity, or 0 to not modify the ship's current value\r\n"
30538 		"\t6: Whether to apply all of the above velocity values even if any of them are 0\r\n" },
30539 
30540 	// Goober5000
30541 	{ OP_SHIP_TAG, "ship-tag\r\n"
30542 		"\tTags a ship.  Takes 3 or 8 arguments...\r\n"
30543 		"\t1: The name of a ship.\r\n"
30544 		"\t2: The tag level (currently 1, 2, or 3).\r\n"
30545 		"\t3: The tag time (in seconds).\r\n"
30546 		"\t4: A SSM missile (optional - used only for TAG-C).\r\n"
30547 		"\t5: The X origin of the SSM missile (optional - used only for TAG-C).\r\n"
30548 		"\t6: The Y origin of the SSM missile (optional - used only for TAG-C).\r\n"
30549 		"\t7: The Z origin of the SSM missile (optional - used only for TAG-C).\r\n"
30550         "\t8: The team the SSM missile belongs to (optional - used only for TAG-C).\r\n" },
30551 
30552 /*	made obsolete by Goober5000 - it only works in debug builds anyway
30553 	{ OP_INT3, "Error (Debug directive)\r\n"
30554 		"Causes the game to halt with an error." },
30555 */
30556 
30557 	{ OP_AI_CHASE, "Ai-chase (Ship goal)\r\n"
30558 		"\tCauses the specified ship to chase and attack the specified target.\r\n\r\n"
30559 		"Takes 2 arguments...\r\n"
30560 		"\t1:\tName of ship to chase.\r\n"
30561 		"\t2:\tGoal priority (number between 0 and 200. Player orders have a priority of 90-100)." },
30562 
30563 	{ OP_AI_DOCK, "Ai-dock (Ship goal)\r\n"
30564 		"\tCauses one ship to dock with another ship.\r\n\r\n"
30565 		"Takes 4 arguments...\r\n"
30566 		"\t1:\tName of dockee ship (The ship that \"docker\" will dock with).\r\n"
30567 		"\t2:\tDocker's docking point - Which dock point docker uses to dock.\r\n"
30568 		"\t3:\tDockee's docking point - Which dock point on dockee docker will move to.\r\n"
30569 		"\t4:\tGoal priority (number between 0 and 200. Player orders have a priority of 90-100)." },
30570 
30571 	{ OP_AI_UNDOCK, "Ai-undock (Ship goal)\r\n"
30572 		"\tCauses the specified ship to undock from who it is currently docked with.\r\n\r\n"
30573 		"Takes 1 or 2 arguments...\r\n"
30574 		"\t1:\tGoal priority (number between 0 and 89).\r\n"
30575 		"\t2 (optional):\tShip to undock from.  If none is specified, the code will pick the first docked ship." },
30576 
30577 	{ OP_AI_WARP_OUT, "Ai-warp-out (Ship/Wing Goal)\r\n"
30578 		"\tCauses the specified ship/wing to immediately warp out of the mission, from its current location.  "
30579 		"It will warp even if its departure cue is specified as a hangar bay.\r\n\r\n"
30580 		"Takes 2 arguments...\r\n"
30581 		"\t1:\tName of waypoint path to follow to warp out (not used).\r\n"
30582 		"\t2:\tGoal priority (number between 0 and 200. Player orders have a priority of 90-100)." },
30583 
30584 	{ OP_AI_WAYPOINTS, "Ai-waypoints (Ship goal)\r\n"
30585 		"\tCauses the specified ship to fly a waypoint path continuously.\r\n\r\n"
30586 		"Takes 2 arguments...\r\n"
30587 		"\t1:\tName of waypoint path to fly.\r\n"
30588 		"\t2:\tGoal priority (number between 0 and 200. Player orders have a priority of 90-100)." },
30589 
30590 	{ OP_AI_WAYPOINTS_ONCE, "Ai-waypoints once (Ship goal)\r\n"
30591 		"\tCauses the specified ship to fly a waypoint path.\r\n\r\n"
30592 		"Takes 2 arguments...\r\n"
30593 		"\t1:\tName of waypoint path to fly.\r\n"
30594 		"\t2:\tGoal priority (number between 0 and 200. Player orders have a priority of 90-100)." },
30595 
30596 	{ OP_AI_DESTROY_SUBSYS, "Ai-destroy subsys (Ship goal)\r\n"
30597 		"\tCauses the specified ship to attack and try and destroy the specified subsystem "
30598 		"on the specified ship.\r\n\r\n"
30599 		"Takes 3 arguments...\r\n"
30600 		"\t1:\tName of ship subsystem is on.\r\n"
30601 		"\t2:\tName of subsystem on the ship to attack and destroy.\r\n"
30602 		"\t3:\tGoal priority (number between 0 and 200. Player orders have a priority of 90-100)." },
30603 
30604 	{ OP_AI_CHASE_WING, "Ai-chase wing (Ship goal)\r\n"
30605 		"\tCauses the specified ship to chase and attack the specified target.\r\n\r\n"
30606 		"Takes 2 arguments...\r\n"
30607 		"\t1:\tName of wing to chase.\r\n"
30608 		"\t2:\tGoal priority (number between 0 and 200. Player orders have a priority of 90-100)." },
30609 
30610 	{ OP_AI_DISABLE_SHIP, "Ai-disable-ship (Ship/wing goal)\r\n"
30611 		"\tThis AI goal causes a ship/wing to destroy all of the engine subsystems on "
30612 		"the specified ship.  This goal is different than ai-destroy-subsystem since a ship "
30613 		"may have multiple engine subsystems requiring the use of > 1 ai-destroy-subsystem "
30614 		"goals.\r\n"
30615 		"Please note that this goal may call \"protect-ship\" on the target "
30616 		"to prevent overzealous AI ships from destroying it in the process of disabling it.  "
30617 		"If the ship must be destroyed later on, be sure to call an \"unprotect-ship\" sexp.\r\n\r\n"
30618 		"Takes 2 arguments...\r\n"
30619 		"\t1:\tName of ship whose engine subsystems should be destroyed\r\n"
30620 		"\t2:\tGoal priority (number between 0 and 200. Player orders have a priority of 90-100)." },
30621 
30622 	{ OP_AI_DISARM_SHIP, "Ai-disarm-ship (Ship/wing goal)\r\n"
30623 		"\tThis AI goal causes a ship/wing to destroy all of the turret subsystems on "
30624 		"the specified ship.  This goal is different than ai-destroy-subsystem since a ship "
30625 		"may have multiple turret subsystems requiring the use of > 1 ai-destroy-subsystem "
30626 		"goals.\r\n"
30627 		"Please note that this goal may call \"protect-ship\" on the target "
30628 		"to prevent overzealous AI ships from destroying it in the process of disarming it.  "
30629 		"If the ship must be destroyed later on, be sure to call an \"unprotect-ship\" sexp.\r\n\r\n"
30630 		"Takes 2 arguments...\r\n"
30631 		"\t1:\tName of ship whose turret subsystems should be destroyed\r\n"
30632 		"\t2:\tGoal priority (number between 0 and 200. Player orders have a priority of 90-100)." },
30633 
30634 	{ OP_AI_GUARD, "Ai-guard (Ship goal)\r\n"
30635 		"\tCauses the specified ship to guard a ship from other ships not on the same team.\r\n\r\n"
30636 		"Takes 2 arguments...\r\n"
30637 		"\t1:\tName of ship to guard.\r\n"
30638 		"\t2:\tGoal priority (number between 0 and 200. Player orders have a priority of 90-100)." },
30639 
30640 	{ OP_AI_CHASE_ANY, "Ai-chase-any (Ship goal)\r\n"
30641 		"\tCauses the specified ship to chase and attack any ship on the opposite team.\r\n\r\n"
30642 		"Takes 1 argument...\r\n"
30643 		"\t1:\tGoal priority (number between 0 and 200. Player orders have a priority of 90-100)." },
30644 
30645 	{ OP_AI_GUARD_WING, "Ai-guard wing (Ship goal)\r\n"
30646 		"\tCauses the specified ship to guard a wing of ships from other ships not on the "
30647 		"same team.\r\n\r\n"
30648 		"Takes 2 arguments...\r\n"
30649 		"\t1:\tName of wing to guard.\r\n"
30650 		"\t2:\tGoal priority (number between 0 and 200. Player orders have a priority of 90-100)." },
30651 
30652 	{ OP_NOP, "Do-nothing (Action operator)\r\n"
30653 		"\tDoes nothing.  This is used as the default for any required action arguments "
30654 		"of an operator." },
30655 
30656 	{ OP_KEY_PRESSED, "Key-pressed (Boolean training operator)\r\n"
30657 		"\tBecomes true when the specified default key has been pressed.  Default key "
30658 		"refers to the what the key normally is when not remapped.  FreeSpace will "
30659 		"automatically account for any keys that have been remapped.  If the optional "
30660 		"delay is specified, becomes true that many seconds after the key has been pressed.\r\n\r\n"
30661 		"Returns a boolean value.  Takes 1 or 2 arguments...\r\n"
30662 		"\t1:\tDefault key to check for.\r\n"
30663 		"\t2:\tDelay before operator registers as true (optional).\r\n" },
30664 
30665 	{ OP_KEY_RESET, "Key-reset (Training operator)\r\n"
30666 		"\tMarks the specified default key as having not been pressed, so key-pressed will be false "
30667 		"until the player presses it again.  See key-pressed help for more information about "
30668 		"what a default key is.\r\n\r\n"
30669 		"\tNote that this sexp will not work properly in repeating events.  Use key-reset-multiple "
30670 		"if this is to be called multiple times in one event.\r\n\r\n"
30671 		"Returns a boolean value.  Takes 1 or more arguments...\r\n"
30672 		"\tAll:\tDefault key to reset." },
30673 
30674 	// Goober5000
30675 	{ OP_KEY_RESET_MULTIPLE, "Key-reset-multiple (Training operator)\r\n"
30676 		"\tMarks the specified default key as having not been pressed, so key-pressed will be false "
30677 		"until the player presses it again.  See key-pressed help for more information about "
30678 		"what a default key is.\r\n\r\n"
30679 		"\tThis sexp, unlike key-reset, will work properly if called multiple times in one event.\r\n\r\n"
30680 		"Returns a boolean value.  Takes 1 or more arguments...\r\n"
30681 		"\tAll:\tDefault key to reset." },
30682 
30683 	{ OP_TARGETED, "Targeted (Boolean training operator)\r\n"
30684 		"\tIs true as long as the player has the specified ship (or ship's subsystem) targeted, "
30685 		"or has been targeted for the specified amount of time.\r\n\r\n"
30686 		"Returns a boolean value.  Takes 1 to 3 arguments (first required, rest optional):\r\n"
30687 		"\t1:\tName of ship to check if targeted by player.\r\n"
30688 		"\t2:\tLength of time target should have been kept for (optional).\r\n"
30689 		"\t3:\tName of subsystem on ship to check if targeted (optional)." },
30690 
30691 	{ OP_NODE_TARGETED, "Node-Targeted (Boolean training operator)\r\n"
30692 		"\tIs true as long as the player has the specified jump node targeted, "
30693 		"or has been targeted for the specified amount of time.\r\n\r\n"
30694 		"Returns a boolean value.  Takes 1 to 2 arguments (first required, rest optional):\r\n"
30695 		"\t1:\tName of Jump Node to check if targeted by player.\r\n"
30696 		"\t2:\tLength of time target should have been kept for (optional)."},
30697 
30698 	// Sesquipedalian
30699 	{ OP_MISSILE_LOCKED, "Missile-locked (Boolean training operator)\r\n"
30700 		"\tIs true as long as the player has had a missile lock for the specified amount of time. "
30701 		"Optional arguments require lock to be maintained on a specified ship (or ship's subsystem).\r\n\r\n"
30702 		"Returns a boolean value.  Takes 1 to 3 arguments (first required, rest optional):\r\n"
30703 		"\t1:\tLength of time missile lock should have been kept for.\r\n"
30704 		"\t2:\tName of ship to check if locked onto by player (optional).\r\n"
30705 		"\t3:\tName of subsystem on ship to check if locked onto (optional)." },
30706 
30707 	{ OP_SPEED, "Speed (Boolean training operator)\r\n"
30708 		"\tBecomes true when the player has been within the specified speed range set by "
30709 		"set-training-context-speed for the specified amount of time.\r\n\r\n"
30710 		"Returns a boolean value.  Takes 1 argument...\r\n"
30711 		"\t1:\tTime in seconds." },
30712 
30713 	{ OP_GET_THROTTLE_SPEED, "Get-Throttle-Speed (Training operator)\r\n"
30714 		"\tReturns the current throttle speed that the ship has been set to. Reverse speeds are returned as a negative value. "
30715 		"Takes 1 argument...\r\n"
30716 		"\t1:\tName of the player ship to check the throttle value for." },
30717 
30718 	{ OP_FACING, "Facing (Boolean training operator)\r\n"
30719 		"\tIs true as long as the specified ship is within the player's specified "
30720 		"forward cone.  A forward cone is defined as any point that the angle between the "
30721 		"vector of the point and the player, and the forward facing vector is within the "
30722 		"given angle.\r\n\r\n"
30723 		"Returns a boolean value.  Takes 2 argument...\r\n"
30724 		"\t1:\tShip to check is withing forward cone.\r\n"
30725 		"\t2:\tAngle in degrees of the forward cone." },
30726 
30727 	{ OP_IS_FACING, "Is Facing (Boolean training operator)\r\n"
30728 		"\tIs true as long as the second object is within the first ship's specified "
30729 		"forward cone.  A forward cone is defined as any point that the angle between the "
30730 		"vector of the ship and point, and the forward facing vector is within the "
30731 		"given angle. If the distance between the two is greater than the fourth"
30732 		"parameter, this will return false.\r\n\r\n"
30733 		"Returns a boolean value.  Takes 3 or 4 argument...\r\n"
30734 		"\t1:\tShip to check from.\r\n"
30735 		"\t2:\tObject to check is within forward cone.\r\n"
30736 		"\t3:\tAngle in degrees of the forward cone.\r\n"
30737 		"\t4:\tRange in meters (optional)."},
30738 
30739 	{ OP_FACING2, "Facing Waypoint(Boolean training operator)\r\n"
30740 		"\tIs true as long as the specified first waypoint is within the player's specified "
30741 		"forward cone.  A forward cone is defined as any point that the angle between the "
30742 		"vector of the point and the player, and the forward facing vector is within the "
30743 		"given angle.\r\n\r\n"
30744 		"Returns a boolean value.  Takes 2 argument...\r\n"
30745 		"\t1:\tName of waypoint path whose first point is within forward cone.\r\n"
30746 		"\t2:\tAngle in degrees of the forward cone." },
30747 
30748 	// fixed by Goober5000 and then deprecated by Karajorma
30749 	{ OP_ORDER, "Order (Boolean training operator)\r\n"
30750 		"\tDeprecated - Use Query-Orders in any new mission.\r\n\r\n"
30751 		"\tBecomes true when the player had given the specified ship or wing the specified order.\r\n\r\n"
30752 		"Returns a boolean value.  Takes 2 or 3 arguments...\r\n"
30753 		"\t1:\tName of ship or wing to check if given order to.\r\n"
30754 		"\t2:\tName of order to check if player has given.\r\n"
30755 		"\t3:\tName of the target of the order (optional)." },
30756 
30757 	{ OP_QUERY_ORDERS, "Query-Orders (Boolean training operator)\r\n"
30758 		"\tBecomes true when the player had given the specified ship or wing the specified order.\r\n\r\n"
30759 		"Returns a boolean value.  Takes 2 or more arguments...\r\n"
30760 		"\t1:\tName of ship or wing to check if given order to.\r\n"
30761 		"\t2:\tName of order to check if player has given.\r\n"
30762 		"\t3:\tMaximum length of time since order was given. Use 0 for any time in the mission.\r\n"
30763 		"\t4:\tName of the target of the order (optional).\r\n"
30764 		"\t5:\tName of player ship giving the order(optional).\r\n"
30765 		"\t6:\tName of the subsystem for Destroy Subsystem orders.(optional)" },
30766 
30767 	// Karajorma
30768 	{ OP_RESET_ORDERS, "Reset-Orders (Action training operator)\r\n"
30769 		"\tResets the list of orders the player has given.\r\n"
30770 		"Takes no arguments." },
30771 
30772 	{ OP_WAYPOINT_MISSED, "Waypoint-missed (Boolean training operator)\r\n"
30773 		"\tBecomes true when a waypoint is flown, but the waypoint is ahead of the one "
30774 		"they are supposed to be flying.  The one they are supposed to be flying is the "
30775 		"next one in sequence in the path after the last one they have hit.\r\n\r\n"
30776 		"Returns a boolean value.  Takes no arguments." },
30777 
30778 	{ OP_PATH_FLOWN, "Path-flown (Boolean training operator)\r\n"
30779 		"\tBecomes true when all the waypoints in the path have been flown, in sequence.\r\n\r\n"
30780 		"Returns a boolean value.  Takes no arguments." },
30781 
30782 	{ OP_WAYPOINT_TWICE, "Waypoint-twice (Boolean training operator)\r\n"
30783 		"\tBecomes true when a waypoint is hit that is before the last one hit, which "
30784 		"indicates they have flown a waypoint twice.\r\n\r\n"
30785 		"Returns a boolean value.  Takes no arguments." },
30786 
30787 	{ OP_TRAINING_MSG, "Training-msg (Action training operator)\r\n"
30788 		"\tSends the player a training message.  Uses the same messages as normal messages, "
30789 		"only they get displayed differently using this operator.  If a secondary message "
30790 		"is specified, it is sent the last time, while the primary message is sent all other "
30791 		"times (event should have a repeat count greater than 1).\r\n\r\n"
30792 		"Takes 1-3 arguments...\r\n"
30793 		"\t1:\tName of primary message to send.\r\n"
30794 		"\t2:\tName of secondary message to send (optional).\r\n"
30795 		"\t3:\tDelay (in seconds) to wait before sending message. (optional)\r\n"
30796 		"\t4:\tAmount of Time (in seconds) to display message (optional)." },
30797 
30798 	{ OP_SET_TRAINING_CONTEXT_FLY_PATH, "Set-training-context-fly-path (Training context operator)\r\n"
30799 		"\tTells FreeSpace that the player is expected to fly a waypoint path.  This must be "
30800 		"executed before waypoint-missed, waypoint-twice and path-flown operators become valid.\r\n\r\n"
30801 		"Takes 2 arguments...\r\n"
30802 		"\t1:\tName of waypoint path player should fly.\r\n"
30803 		"\t2:\tDistance away a player needs to be from a waypoint for it to be registered as flown." },
30804 
30805 	{ OP_SET_TRAINING_CONTEXT_SPEED, "Set-training-context-speed (Training context operator)\r\n"
30806 		"\tTells FreeSpace that the player is expected to fly within a certain speed range.  Once "
30807 		"this operator has been executed, you can measure how long they have been within this "
30808 		"speed range with the speed operator.\r\n\r\n"
30809 		"Takes 2 arguments...\r\n"
30810 		"\t1:\tMinimum speed of range player is to fly between.\r\n"
30811 		"\t2:\tMaximum speed of range player is to fly between." },
30812 
30813 	// Karajorma
30814 	{ OP_STRING_TO_INT, "string-to-int\r\n"
30815 		"\tConverts a string into an integer.  All non-numeric characters (except for the negative sign) will be ignored, as will any fractional part of a decimal number.  This behavior is somewhat different than the atoi() function in C or C++, which will abort if it encounters any non-numeric character.  For a string like \"turret31\", this sexp will return 31, but atoi() will return 0.\r\n\r\n"
30816 		"Takes 1 argument...\r\n"
30817 		"\t1:\tString to convert" },
30818 
30819 	// Goober5000
30820 	{ OP_STRING_GET_LENGTH, "string-get-length\r\n"
30821 		"\tReturns the length of the specified string.  Takes 1 argument." },
30822 
30823 	// Goober5000
30824 	{ OP_INT_TO_STRING, "int-to-string\r\n"
30825 		"\tConverts an integer into a string.  The destination must be a string variable.\r\n"
30826 		"Takes 2 argument...\r\n"
30827 		"\t1:\tInteger to convert\r\n"
30828 		"\t2:\tString variable to contain the result\r\n" },
30829 
30830 	// Goober5000
30831 	{ OP_STRING_CONCATENATE, "string-concatenate\r\n"
30832 		"\tConcatenates two strings, putting the result into a string variable.  If the length of the string will "
30833 		"exceed the sexp variable token limit (currently 32), it will be truncated.\r\n\r\n"
30834 		"Takes 3 arguments...\r\n"
30835 		"\t1: First string\r\n"
30836 		"\t2: Second string\r\n"
30837 		"\t3: String variable to hold the result\r\n" },
30838 
30839 	// Goober5000
30840 	{ OP_STRING_GET_SUBSTRING, "string-get-substring\r\n"
30841 		"\tExtracts a substring from a parent string, putting the result into a string variable.  If the length of the string will "
30842 		"exceed the sexp variable token limit (currently 32), it will be truncated.\r\n\r\n"
30843 		"Takes 3 arguments...\r\n"
30844 		"\t1: Parent string\r\n"
30845 		"\t2: Index at which the substring begins (0-based)\r\n"
30846 		"\t3: Length of the substring\r\n"
30847 		"\t4: String variable to hold the result\r\n" },
30848 
30849 	// Goober5000
30850 	{ OP_STRING_SET_SUBSTRING, "string-set-substring\r\n"
30851 		"\tReplaces a substring from a parent string with a new string, putting the result into a string variable.  If the length of the string will "
30852 		"exceed the sexp variable token limit (currently 32), it will be truncated.\r\n\r\n"
30853 		"Takes 3 arguments...\r\n"
30854 		"\t1: Parent string\r\n"
30855 		"\t2: Index at which the substring begins (0-based)\r\n"
30856 		"\t3: Length of the substring\r\n"
30857 		"\t4: New substring (which can be a different length than the old substring)\r\n"
30858 		"\t5: String variable to hold the result\r\n" },
30859 
30860 	// Karajorma
30861 	{ OP_DEBUG, "debug\r\n"
30862 		"\tPops up a warning on debug builds (and optionally on release builds)\r\n"
30863 		"Takes 1 or more arguments...\r\n"
30864 		"\t1:\t If false, popup messages in release builds too. Defaults to true.\r\n"
30865 		"\t2:\tName of a message which will appear in the warning (optional)\r\n"},
30866 
30867 	{ OP_GRANT_PROMOTION, "Grant promotion (Action operator)\r\n"
30868 		"\tIn a single player game, this function grants a player an automatic promotion to the "
30869 		"next rank which the player can obtain.  If he is already at the highest rank, this "
30870 		"operator has no effect.  It takes no arguments." },
30871 
30872 	{ OP_GRANT_MEDAL, "Grant medal (Action operator)\r\n"
30873 		"\tIn single player missions, this function grants the given medal to the player.  "
30874 		"Currently, only 1 medal will be allowed to be given per mission.\r\n\r\n"
30875 		"Takes 1 argument...\r\n"
30876 		"\t1:\tName of medal to grant to player." },
30877 
30878 	{ OP_GOOD_SECONDARY_TIME, "Set preferred secondary weapons\r\n"
30879 		"\tThis sexpression is used to inform the AI about preferred secondary weapons to "
30880 		"fire during combat.  When this expression is evaluated, any AI ships of the given "
30881 		"team prefer to fire the given weapon at the given ship. (Preferred over other "
30882 		"secondary weapons)\r\n\r\n"
30883 		"Takes 4 argument...\r\n"
30884 		"\t1:\tTeam name which will prefer firing given weapon\r\n"
30885 		"\t2:\tMaximum number of this type of weapon above team can fire.\r\n"
30886 		"\t3:\tWeapon name (list includes only the valid weapons for this expression\r\n"
30887 		"\t4:\tShip name at which the above named team should fire the above named weapon." },
30888 
30889 	{ OP_AND_IN_SEQUENCE, "And in sequence (Boolean operator)\r\n"
30890 		"\tReturns true if all of its arguments have become true in the order they are "
30891 		"listed in.\r\n\r\n"
30892 		"Returns a boolean value.  Takes 2 or more boolean arguments." },
30893 
30894 	{ OP_SKILL_LEVEL_AT_LEAST, "Skill level at least (Boolean operator)\r\n"
30895 		"\tReturns true if the player has selected the given skill level or higher.\r\n\r\n"
30896 		"Returns a boolean value.  Takes 1 argument...\r\n"
30897 		"\t1:\tName of the skill level to check." },
30898 
30899 	{ OP_NUM_PLAYERS, "Num players (Status operator)\r\n"
30900 		"\tReturns the current number of players (multiplayer) playing in the current mission.\r\n\r\n"
30901 		"Returns a numeric value.  Takes no arguments." },
30902 
30903 	{ OP_IS_CARGO_KNOWN, "Is cargo known (Boolean operator)\r\n"
30904 		"\tReturns true if all of the specified objects' cargo is known by the player (i.e. they "
30905 		"have scanned each one.\r\n\r\n"
30906 		"Returns a boolean value.  Takes 1 or more arguments...\r\n"
30907 		"\tAll:\tName of ship to check if its cargo is known." },
30908 
30909 	{ OP_HAS_BEEN_TAGGED_DELAY, "Has ship been tagged (delay) (Boolean operator)\r\n"
30910 		"\tReturns true if all of the specified ships have been tagged.\r\n\r\n"
30911 		"Returns a boolean value after <delay> seconds when all ships have been tagged.  Takes 2 or more arguments...\r\n"
30912 		"\t1:\tDelay in seconds after which sexpression will return true when all cargo scanned."
30913 		"\tRest:\tNames of ships to check if tagged.." },
30914 
30915 	{ OP_ARE_SHIP_FLAGS_SET, "Are ship flags set (Boolean operator)\r\n"
30916 		"\tReturns true if all of the specified flags have been set for this particular ship.\r\n\r\n"
30917 		"Takes 2 or more arguments...\r\n"
30918 		"\t1:\tName of the ship."
30919 		"\tRest:\tShip, object or ai flags which might be set for this ship.." },
30920 
30921 	{ OP_CAP_SUBSYS_CARGO_KNOWN_DELAY, "Is capital ship subsystem cargo known (delay) (Boolean operator)\r\n"
30922 		"\tReturns true if all of the specified subsystem cargo is known by the player.\r\n"
30923 		"\tNote: Cargo must be explicitly named.\r\n\r\n"
30924 		"Returns a boolean value after <delay> seconds when all cargo is known.  Takes 3 or more arguments...\r\n"
30925 		"\t1:\tDelay in seconds after which sexpression will return true when all cargo scanned.\r\n"
30926 		"\t2:\tName of capital ship\r\n"
30927 		"\tRest:\tNames of subsystems to check for cargo known.." },
30928 
30929 	{ OP_CARGO_KNOWN_DELAY, "Is cargo known (delay) (Boolean operator)\r\n"
30930 		"\tReturns true if all of the specified objects' cargo is known by the player (i.e. they "
30931 		"have scanned each one.\r\n\r\n"
30932 		"Returns a boolean value after <delay> seconds when all cargo is known.  Takes 2 or more arguments...\r\n"
30933 		"\t1:\tDelay in seconds after which sexpression will return true when all cargo scanned."
30934 		"\tRest:\tNames of ships/cargo to check for cargo known.." },
30935 
30936 	{ OP_WAS_PROMOTION_GRANTED, "Was promotion granted (Boolean operator)\r\n"
30937 		"\tReturns true if a promotion was granted via the 'Grant promotion' operator in the mission.\r\n\r\n"
30938 		"Returns a boolean value.  Takes no arguments." },
30939 
30940 	{ OP_WAS_MEDAL_GRANTED, "Was medal granted (Boolean operator)\r\n"
30941 		"\tReturns true if a medal was granted via via the 'Grant medal' operator in the mission.  "
30942 		"If you provide the optional argument to this operator, then true is only returned if the "
30943 		"specified medal was granted.\r\n\r\n"
30944 		"Returns a boolean value.  Takes 0 or 1 arguments...\r\n"
30945 		"\t1:\tName of medal to specifically check for (optional)." },
30946 
30947 	{ OP_GOOD_REARM_TIME, "Good rearm time (Action operator)\r\n"
30948 		"\tInforms the game logic that right now is a good time for a given team to attempt to "
30949 		"rearm their ships.  The time parameter specified how long the \"good time\" will last.\r\n\r\n"
30950 		"Takes 2 arguments...\r\n"
30951 		"\t1:\tTeam Name\r\n"
30952 		"\t2:\tTime in seconds rearm window should last" },
30953 
30954 	{ OP_ALLOW_SHIP, "Allow ship (Action operator)\r\n"
30955 		"\tThis operator makes the given ship type available to the Terran team.  Players will be "
30956 		"able to have ships of this type in their starting wings in all future missions of this "
30957 		"campaign.\r\n\r\n"
30958 		"Takes 1 argument...\r\n"
30959 		"\t1:\tName of ship type (or ship class) to allow." },
30960 
30961 	{ OP_ALLOW_WEAPON, "Allow weapon (Action operator)\r\n"
30962 		"\tThis operator makes the given weapon available to the Terran team.  Players will be "
30963 		"able to equip ships with in all future missions of this campaign.\r\n\r\n"
30964 		"Takes 1 argument...\r\n"
30965 		"\t1:\tName of weapon (primary or secondary) to allow." },
30966 
30967 	{ OP_TECH_ADD_SHIP, "Tech add ship (Action operator)\r\n"
30968 		"\tThis operator makes the given ship type available in the techroom database.  Players will "
30969 		"then be able to view this ship's specs there.\r\n\r\n"
30970 		"Takes 1 or more arguments...\r\n"
30971 		"\tAll:\tName of ship type (or ship class) to add." },
30972 
30973 	{ OP_TECH_ADD_WEAPON, "Tech add weapon (Action operator)\r\n"
30974 		"\tThis operator makes the given weapon available in the techroom database.  Players will "
30975 		"then be able to view this weapon's specs there.\r\n\r\n"
30976 		"Takes 1 or more arguments...\r\n"
30977 		"\tAll:\tName of weapon (primary or secondary) to add." },
30978 
30979 	{ OP_TECH_ADD_INTEL, "Tech add intel (Action operator, deprecated in favor of tech-add-intel-xstr)\r\n"
30980 		"\tThis operator makes the given intel entry available in the techroom database.  Players will "
30981 		"then be able to view this intel entry there.\r\n\r\n"
30982 		"Takes 1 or more arguments...\r\n"
30983 		"\tAll:\tName of intel entry to add." },
30984 
30985 	{ OP_TECH_ADD_INTEL_XSTR, "Tech add intel XSTR (Action operator)\r\n"
30986 		"\tThis operator makes the given intel entry available in the techroom database.  Players will "
30987 		"then be able to view this intel entry there.\r\n\r\n"
30988 		"Takes 2 or more arguments...\r\n"
30989 		"\t1:\tName of intel entry to add.\r\n"
30990 		"\t2:\tXSTR ID of intel entry, or -1 if there is no XSTR entry.\r\n"
30991 		"Use Add-Data for multiple entries.\r\n\r\n"
30992 		"IMPORTANT: Each additional entry in the list MUST HAVE two fields; "
30993 		"any entry without both fields will be ignored, as will any successive entries." },
30994 
30995 	{ OP_TECH_RESET_TO_DEFAULT, "Tech reset to default (Action operator)\r\n"
30996 		"\tThis operator resets the tech room to the default represented in the tables.  This is "
30997 		"useful for starting new campaigns, so that the player will not see tech entries carried over "
30998 		"from previous campaigns.\r\n\r\n"
30999 		"Takes no arguments." },
31000 
31001 	{ OP_CHANGE_PLAYER_SCORE, "Change Player Score (Action operator)\r\n"
31002 		"\tThis operator allows direct alteration of the player's score for this mission.\r\n\r\n"
31003 		"Takes 2 or more arguments."
31004 		"\t1:\tAmount to alter the player's score by.\r\n"
31005 		"\tRest:\tName of ship the player is flying."},
31006 
31007 	{ OP_CHANGE_TEAM_SCORE, "Change Team Score (Action operator)\r\n"
31008 		"\tThis operator allows direct alteration of the team's score for a TvT mission (Does nothing otherwise).\r\n\r\n"
31009 		"Takes 2 arguments."
31010 		"\t1:\tAmount to alter the team's score by.\r\n"
31011 		"\t2:\tThe team to alter the score for. (0 will add the score to all teams!)"},
31012 
31013 	{ OP_AI_EVADE_SHIP, "Ai-evade ship (Ship goal)\r\n"
31014 		"\tCauses the specified ship to go into evade mode and run away like the weak "
31015 		"sally-boy it is.\r\n\r\n"
31016 		"Takes 2 arguments...\r\n"
31017 		"\t1:\tName of ship to evade from.\r\n"
31018 		"\t2:\tGoal priority (number between 0 and 89)." },
31019 
31020 	{ OP_AI_STAY_NEAR_SHIP, "Ai-stay near ship (Ship goal)\r\n"
31021 		"\tCauses the specified ship to keep itself near the given ship and not stray too far "
31022 		"away from it.\r\n\r\n"
31023 		"Takes 2 arguments...\r\n"
31024 		"\t1:\tName of ship to stay near.\r\n"
31025 		"\t2:\tGoal priority (number between 0 and 89)." },
31026 
31027 	{ OP_AI_KEEP_SAFE_DISTANCE, "Ai-keep safe distance (Ship goal)\r\n"
31028 		"\tTells the specified ship to stay a safe distance away from any ship that isn't on the "
31029 		"same team as it.\r\n\r\n"
31030 		"Takes 1 argument...\r\n"
31031 		"\t1:\tGoal priority (number between 0 and 89)." },
31032 
31033 	{ OP_AI_IGNORE, "Ai-ignore (Ship goal)\r\n"
31034 		"\tTells all ships to ignore the given ship and not consider it as a valid "
31035 		"target to attack.\r\n\r\n"
31036 		"Takes 2 arguments...\r\n"
31037 		"\t1:\tName of ship to ignore.\r\n"
31038 		"\t2:\tGoal priority (number between 0 and 89)." },
31039 
31040 	// Goober5000
31041 	{ OP_AI_IGNORE_NEW, "Ai-ignore-new (Ship goal)\r\n"
31042 		"\tTells the specified ship to ignore the given ship and not consider it as a valid "
31043 		"target to attack.\r\n\r\n"
31044 		"Takes 2 arguments...\r\n"
31045 		"\t1:\tName of ship to ignore.\r\n"
31046 		"\t2:\tGoal priority (number between 0 and 89)." },
31047 
31048 	{ OP_AI_STAY_STILL, "Ai-stay still (Ship goal)\r\n"
31049 		"\tCauses the specified ship to stay still.  The ship will do nothing until attacked at "
31050 		"which time the ship will come to life and defend itself.\r\n\r\n"
31051 		"Takes 2 arguments...\r\n"
31052 		"\t1:\tShip or waypoint the ship staying still will directly face (currently not implemented)\r\n"
31053 		"\t2:\tGoal priority (number between 0 and 89)." },
31054 
31055 	{ OP_AI_PLAY_DEAD, "Ai-play dead (Ship goal)\r\n"
31056 		"\tCauses the specified ship to pretend that it is dead and not do anything.  This "
31057 		"expression should be used to indicate that a ship has no pilot and cannot respond "
31058 		"to any enemy threats.  A ship playing dead will not respond to any attack.  This "
31059 		"should really be named ai-is-dead\r\n\r\n"
31060 		"Takes 1 argument...\r\n"
31061 		"\t1:\tGoal priority (number between 0 and 89)." },
31062 
31063 	{ OP_AI_FORM_ON_WING, "Ai-form-on-wing (Ship Goal)\r\n"
31064 		"\tCauses the ship to form on the specified ship's wing. This works analogous to the "
31065 		"player order, and will cause all other goals specified for the ship to be purged.\r\n\r\n"
31066 		"Takes 1 argument...\r\n"
31067 		"\t1:\tShip to form on." },
31068 
31069 	{ OP_FLASH_HUD_GAUGE, "Ai-flash hud gauge (Training goal)\r\n"
31070 		"\tCauses the specified hud gauge to flash to draw the player's attention to it.\r\n\r\n"
31071 		"Takes 1 argument...\r\n"
31072 		"\t1:\tName of hud gauge to flash." },
31073 
31074 	{ OP_ALTER_SHIP_FLAG, "alter-ship-flag\r\n"
31075 		"\tSets a ships flag and/or parse flag.\r\n\r\n"
31076 		"Takes 4 or more arguments...\r\n"
31077 		"\t1:\tShip flag name\r\n"
31078 		"\t2:\tTrue if turning on, false if turning off\r\n"
31079 		"\t3:\tTrue\\False - Apply this flag to future waves of this wing. Apply to ship if not present\r\n"
31080 		"\tRest:\t (optional) Name of ships, wings, or entire teams. If not supplied, will work on all ships in the mission\r\n\r\n"
31081 		"Ship Flags:\r\n"
31082 		"invulnerable - Stops ship from taking any damage\r\n"
31083 		"protect-ship - Ship and Turret AI will ignore and not attack ship\r\n"
31084 		"beam-protect-ship - Turrets with beam weapons will ignore and not attack ship\r\n"
31085 		"no-shields - Ship will have no shields (ETS will be rebalanced if shields were off and are enabled)\r\n"
31086 		"targetable-as-bomb - Allows ship to be targetted with the bomb targetting key\r\n"
31087 		"flak-protect-ship - Turrets with flak weapons will ignore and not attack ship\r\n"
31088 		"laser-protect-ship - Turrets with laser weapons will ignore and not attack ship\r\n"
31089 		"missile-protect-ship - Turrets with missile weapons will ignore and not attack ship\r\n"
31090 		"immobile - Will not let a ship move or rotate in any fashion\r\n"
31091 		"vaporize - Causes a ship to vanish (no deathroll, no debris, no explosion) when destroyed\r\n"
31092 		"break-warp - Causes a ship's subspace drive to break. Can be repaired by a support ship\r\n"
31093 		"never-warp - Causes a ship's subspace drive to never work. Cannot be repaired by a support ship\r\n"
31094 		"afterburner-locked - Will stop a ship from firing their afterburner\r\n"
31095 		"primaries-locked - Will stop a ship from firing their primary weapons\r\n"
31096 		"secondaries-locked - Will stop a ship from firing their secondary weapons\r\n"
31097 		"no-subspace-drive - Will not allow a ship to jump into subspace\r\n"
31098 		"don't-collide-invisible - Will cause polygons with an invisible texture to stop colliding with objects\r\n"
31099 		"no-ets - Will not allow a ship to alter its ETS system\r\n"
31100 		"toggle-subsystem-scanning - Switches between being able to scan a whole ship or individual subsystems\r\n"
31101 		"scannable - Whether or not the ship can be scanned\r\n"
31102 		"cargo-known - If set, the ships cargo can be seen without scanning the ship\r\n"
31103 		"stealth - If set, the ship can't be targeted, is invisible on radar, and is ignored by AI unless firing\r\n"
31104 		"friendly-stealth-invisible - If set, the ship can't be targeted even by ships on the same team\r\n"
31105 		"hide-ship-name - If set, the ship name can't be seen when the ship is targeted\r\n"
31106 		"hidden-from-sensors - If set, the ship can't be targeted and appears on radar as a blinking dot\r\n"
31107 		"no-dynamic - Will stop allowing the AI to persue dynamic goals (eg: chasing ships it was not ordered to)\r\n"},
31108 
31109 	{ OP_SHIP_VISIBLE, "ship-visible\r\n"
31110 		"\tCauses the ships listed in this sexpression to be visible with player sensors.\r\n\r\n"
31111 		"Takes 1 or more arguments...\r\n"
31112 		"\t1+:\tName of ships to make visible to sensors." },
31113 
31114 	{ OP_SHIP_INVISIBLE, "ship-invisible\r\n"
31115 		"\tCauses the ships listed in this sexpression to be invisible to player sensors.\r\n\r\n"
31116 		"Takes 1 or more arguments...\r\n"
31117 		"\t1+:\tName of ships to make invisible to sensors." },
31118 
31119 	{ OP_SHIP_VULNERABLE, "ship-vulnerable\r\n"
31120 		"\tCauses the ship listed in this sexpression to be vulnerable to weapons.\r\n\r\n"
31121 		"Takes 1 or more arguments...\r\n"
31122 		"\t1+:\tName of ships to make vulnerable to weapons." },
31123 
31124 	{ OP_SHIP_INVULNERABLE, "ship-invulnerable\r\n"
31125 		"\tCauses the ships listed in this sexpression to be invulnerable to weapons.  Use with caution!!!!\r\n\r\n"
31126 		"Takes 1 or more arguments...\r\n"
31127 		"\t1+:\tName of ships to make invulnerable to weapons." },
31128 
31129 	{ OP_SHIP_BOMB_TARGETABLE, "ship-targetable-as-bomb\r\n"
31130 		"\tCauses the ships listed in this sexpression to be targetable with bomb targeting key.\r\n\r\n"
31131 		"Takes 1 or more arguments...\r\n"
31132 		"\t1+:\tName of ships to make targetable with bomb targeting key." },
31133 
31134 	{ OP_SHIP_BOMB_UNTARGETABLE, "ship-untargetable-as-bomb\r\n"
31135 		"\tCauses the ships listed in this sexpression to not be targetable with bomb targeting key.\r\n\r\n"
31136 		"Takes 1 or more arguments...\r\n"
31137 		"\t1+:\tName of ships to make nontargetable with bomb targeting key." },
31138 
31139 	{ OP_SHIELDS_ON, "shields-on\r\n" //-Sesquipedalian
31140 		"\tCauses the ship listed in this sexpression to have their shields activated.\r\n"
31141 		"If the ship had no-shields prior to the sexp being called, the ETS will be rebalanced to default.\r\n"
31142 		"Takes 1 or more arguments...\r\n"
31143 		"\t1+:\tName of ships to activate shields on." },
31144 
31145 	{ OP_SHIELDS_OFF, "shields-off\r\n" //-Sesquipedalian
31146 		"\tCauses the ships listed in this sexpression to have their shields deactivated.  \r\n\r\n"
31147 		"Takes 1 or more arguments...\r\n"
31148 		"\t1+:\tName of ships to deactivate shields on." },
31149 
31150 	{ OP_SHIP_GUARDIAN, "ship-guardian\r\n"
31151 		"\tCauses the ships listed in this sexpression to not be killable by weapons.  Use with caution!!!!\r\n\r\n"
31152 		"Takes 1 or more arguments...\r\n"
31153 		"\t1+:\tName of ships to make unkillable." },
31154 
31155 	{ OP_SHIP_NO_GUARDIAN, "ship-no-guardian\r\n"
31156 		"\tCauses the ships listed in this sexpression to be killable by weapons, if not invulnerable.\r\n\r\n"
31157 		"Takes 1 or more arguments...\r\n"
31158 		"\t1+:\tName of ships to make killable." },
31159 
31160 	// Goober5000
31161  	{ OP_SHIP_GUARDIAN_THRESHOLD, "ship-guardian-threshold\r\n"
31162  		"\tSame as ship-guardian, except the lowest possible hull value is specified by the sexp rather than defaulting to 1.\r\n"
31163  		"Call with a threshold of 0 (or use ship-no-guardian) to deactivate.\r\n\r\n"
31164  		"Takes 2 or more arguments...\r\n"
31165  		"\t1:\tThreshold value.\r\n"
31166  		"\t2+:\tName of ships to make unkillable." },
31167 
31168  	// Goober5000
31169 	{ OP_SHIP_SUBSYS_GUARDIAN_THRESHOLD, "ship-subsys-guardian-threshold\r\n"
31170 		"\tSame as ship-guardian-threshold, but works on subsystems.\r\n"
31171 		"Call with a threshold of 0 to deactivate.\r\n\r\n"
31172  		"Takes 3 or more arguments...\r\n"
31173 		"\t1:\tThreshold value.\r\n"
31174 		"\t2:\tShip housing the subsystem(s).\r\n"
31175 		"\t3+:\tSubsystems to make unkillable." },
31176 
31177 	// Goober5000
31178 	{ OP_SHIP_STEALTHY, "ship-stealthy\r\n"
31179 		"\tCauses the ships listed in this sexpression to become stealth ships (i.e. invisible to radar).\r\n\r\n"
31180 		"Takes 1 or more arguments...\r\n"
31181 		"\tAll:\tName of ships to make stealthy." },
31182 
31183 	// Goober5000
31184 	{ OP_SHIP_UNSTEALTHY, "ship-unstealthy\r\n"
31185 		"\tCauses the ships listed in this sexpression to become non-stealth ships (i.e. visible to radar).\r\n\r\n"
31186 		"Takes 1 or more arguments...\r\n"
31187 		"\tAll:\tName of ships to make non-stealthy." },
31188 
31189 	// Goober5000
31190 	{ OP_FRIENDLY_STEALTH_INVISIBLE, "friendly-stealth-invisible\r\n"
31191 		"\tCauses the friendly ships listed in this sexpression to be invisible to radar, just like hostile stealth ships."
31192 		"It doesn't matter if the ship is friendly at the time this sexp executes: as long as it is a stealth ship, it will"
31193 		"be invisible to radar both as hostile and as friendly.\r\n\r\n"
31194 		"Takes 1 or more arguments...\r\n"
31195 		"\tAll:\tName of ships" },
31196 
31197 	// Goober5000
31198 	{ OP_FRIENDLY_STEALTH_VISIBLE, "friendly-stealth-visible\r\n"
31199 		"\tCauses the friendly ships listed in this sexpression to resume their normal behavior of being visible to radar as"
31200 		"stealth friendlies.  Does not affect their visibility as stealth hostiles.\r\n\r\n"
31201 		"Takes 1 or more arguments...\r\n"
31202 		"\tAll:\tName of ships" },
31203 
31204 	// Goober5000
31205 	{ OP_SHIP_SUBSYS_TARGETABLE, "ship-subsys-targetable\r\n"
31206 		"\tCauses the specified ship subsystem(s) to be targetable on radar.\r\n"
31207 		"Takes 2 or more arguments...\r\n"
31208 		"\t1:\tName of a ship\r\n"
31209 		"\tRest: Name of the ship's subsystem(s)" },
31210 
31211 	// Goober5000
31212 	{ OP_SHIP_SUBSYS_UNTARGETABLE, "ship-subsys-untargetable\r\n"
31213 		"\tCauses the specified ship subsystem(s) to not be targetable on radar.\r\n"
31214 		"Takes 2 or more arguments...\r\n"
31215 		"\t1:\tName of a ship\r\n"
31216 		"\tRest: Name of the ship's subsystem(s)" },
31217 
31218 	// FUBAR
31219 	{ OP_SHIP_SUBSYS_NO_REPLACE, "ship-subsys-no-replace\r\n"
31220 		"\tCauses the -destroyed version of specified ship subsystem(s) to not render when it's destroyed.\r\n"
31221 		"Takes 3 or more arguments...\r\n"
31222 		"\t1:\tName of a ship\r\n"
31223 		"\t2:\tTrue = Do not render or False = render if exists\r\n"
31224 		"\tRest: Name of the ship's subsystem(s)"
31225 		"\tNote: If subsystem is already dead it will vanish or reappear out of thin air" },
31226 
31227 
31228 	// FUBAR
31229 	{ OP_SHIP_SUBSYS_NO_LIVE_DEBRIS, "ship-subsys-no-live-debris\r\n"
31230 		"\tCauses the specified ship subsystem(s) to not render live debris when it's destroyed.\r\n"
31231 		"Takes 3 or more arguments...\r\n"
31232 		"\t1:\tName of a ship\r\n"
31233 		"\t2:\tTrue = Do not render or False = render if exists\r\n"
31234 		"\tRest: Name of the ship's subsystem(s)" },
31235 
31236 	// FUBAR
31237 	{ OP_SHIP_SUBSYS_VANISHED, "ship-subsys-vanish\r\n"
31238 		"\tCauses the subsystem to vanish without a trace - no fanfare, notification, or special effects.  See also ship-vanish.\r\n"
31239 		"\tSingle Player Only!  Warning: This will cause subsystem destruction not to be logged, so 'has-departed', etc. will not work\r\n"
31240 		"Takes 3 or more arguments...\r\n"
31241 		"\t1:\tName of a ship\r\n"
31242 		"\t2:\tTrue = vanish or False = don't vanish\r\n"
31243 		"\tRest: Name of the ship's subsystem(s)"
31244 		"\tNote: Useful for replacing subsystems with actual docked models." },
31245 
31246 	// FUBAR
31247 	{ OP_SHIP_SUBSYS_IGNORE_IF_DEAD, "ship-subsys-ignore-if-dead\r\n"
31248 		"\tCauses secondary weapons to ignore dead ship subsystem(s)and home on hull instead.\r\n"
31249 		"Takes 3 or more arguments...\r\n"
31250 		"\t1:\tName of a ship\r\n"
31251 		"\t2:\tTrue = Ignore dead or False = don't ignore\r\n"
31252 		"\tRest: Name of the ship's subsystem(s)" },
31253 
31254 	// Goober5000
31255 	{ OP_SHIP_CHANGE_ALT_NAME, "ship-change-alt-name\r\n"
31256 		"\tChanges the alternate ship class name displayed in the HUD target window.  Takes 2 or more arguments...\r\n"
31257 		"\t1:\tThe ship class name to display\r\n"
31258 		"\tRest:\tThe ships to display the new class name" },
31259 
31260 	// FUBAR
31261 	{ OP_SHIP_CHANGE_CALLSIGN, "ship-change-callsign\r\n"
31262 		"\tChanges the callsign of a ship.  Takes 2 or more arguments...\r\n"
31263 		"\t1:\tThe callsign to display or empty to remove\r\n"
31264 		"\tRest:\tThe ships to display the new callsign" },
31265 
31266 	// Goober5000
31267 	{ OP_SET_DEATH_MESSAGE, "set-death-message\r\n"
31268 		"\tSets the message displayed when the specified players are killed.  Takes 1 or more arguments...\r\n"
31269 		"\t1:\tThe message\r\n"
31270 		"\tRest:\tThe players for whom this message is displayed (optional) (currently not implemented)" },
31271 
31272 	{ OP_PERCENT_SHIPS_ARRIVED, "percent-ships-arrived\r\n"
31273 		"\tBoolean function which returns true if the percentage of ships in the listed ships and wings "
31274 		"which have arrived is greater or equal to the given percentage.  For wings, all ships of all waves "
31275 		"are used for calculation for the total possible ships to arrive.\r\n\r\n"
31276 		"Takes 2 or more arguments...\r\n"
31277 		"\t1:\tPercentge of arriving ships at which this function will return true.\r\n"
31278 		"\t2+:\tList of ships/wings whose arrival status should be determined." },
31279 
31280 	{ OP_PERCENT_SHIPS_DEPARTED, "percent-ships-departed\r\n"
31281 		"\tBoolean function which returns true if the percentage of ships in the listed ships and wings "
31282 		"which have departed is greater or equal to the given percentage.  For wings, all ships of all waves "
31283 		"are used for calculation for the total possible ships to depart.\r\n\r\n"
31284 		"Takes 2 or more arguments...\r\n"
31285 		"\t1:\tPercentge of departed ships at which this function will return true.\r\n"
31286 		"\t2+:\tList of ships/wings whose departure status should be determined." },
31287 
31288 	{ OP_PERCENT_SHIPS_DESTROYED, "percent-ships-destroyed\r\n"
31289 		"\tBoolean function which returns true if the percentage of ships in the listed ships and wings "
31290 		"which have been destroyed is greater or equal to the given percentage.  For wings, all ships of all waves "
31291 		"are used for calculation for the total possible ships to be destroyed.\r\n\r\n"
31292 		"Takes 2 or more arguments...\r\n"
31293 		"\t1:\tPercentge of destroyed ships at which this function will return true.\r\n"
31294 		"\t2+:\tList of ships/wings whose destroyed status should be determined." },
31295 
31296 	// Goober5000
31297 	{ OP_PERCENT_SHIPS_DISARMED, "percent-ships-disarmed\r\n"
31298 		"\tBoolean function which returns true if the percentage of ships in the listed ships "
31299 		"which have been disarmed is greater or equal to the given percentage.\r\n\r\n"
31300 		"Takes 2 or more arguments...\r\n"
31301 		"\t1:\tPercentge of disarmed ships at which this function will return true.\r\n"
31302 		"\t2+:\tList of ships whose disarmed status should be determined." },
31303 
31304 	// Goober5000
31305 	{ OP_PERCENT_SHIPS_DISABLED, "percent-ships-disabled\r\n"
31306 		"\tBoolean function which returns true if the percentage of ships in the listed ships "
31307 		"which have been disabled is greater or equal to the given percentage.\r\n\r\n"
31308 		"Takes 2 or more arguments...\r\n"
31309 		"\t1:\tPercentge of disabled ships at which this function will return true.\r\n"
31310 		"\t2+:\tList of ships whose disabled status should be determined." },
31311 
31312 	{ OP_RED_ALERT, "red-alert\r\n"
31313 		"\tCauses Red Alert status in a mission.  This function ends the current mission, and moves to "
31314 		"the next mission in the campaign under red alert status.  There should only be one branch from "
31315 		"a mission that uses this expression\r\n\r\n"
31316 		"Takes no arguments."},
31317 
31318 	{ OP_MISSION_SET_NEBULA, "mission-set-nebula\r\n"
31319 		"\tTurns nebula on/off\r\n"
31320 		"\tTakes1 argument...\r\n"
31321 		"\t1:\t0 for nebula off, 1 for nebula on" },
31322 
31323 	{ OP_MISSION_SET_SUBSPACE, "mission-set-subspace\r\n"
31324 		"\tTurns subspace on/off\r\n"
31325 		"\tTakes 1 argument...\r\n"
31326 		"\r1:\t0 for subspace off, 1 for subspace on" },
31327 
31328 	//-Sesquipedalian
31329 	{ OP_END_MISSION, "end-mission\r\n"
31330 		"\tEnds the mission as if the player had engaged his subspace drive, but without him doing so.  Dumps the player back into a normal debriefing.  Does not invoke red-alert status.\r\n"
31331 		"\t1:\tEnd Mission even if the player is dead (optional; defaults to true)\r\n"
31332 		"\t2:\tBoot the player out into the main hall instead of going to the debriefing (optional; defaults to false; not supported in multi)"
31333 	},
31334 
31335 	// Goober5000
31336 	{ OP_SET_DEBRIEFING_TOGGLED, "set-debriefing-toggled\r\n"
31337 		"\tSets or clears the \"toggle debriefing\" mission flag.  If set, the mission will have its debriefing turned off, unless it is a multiplayer dogfight mission, in which case its debriefing will be turned on.  Takes 1 argument.\r\n"
31338 	},
31339 
31340 	// Goober5000
31341 	{ OP_FORCE_JUMP, "force-jump\r\n"
31342 		"\tForces activation of the player's subspace drive, thus ending the mission.  Takes no arguments."
31343 	},
31344 
31345 	// Goober5000
31346 	{ OP_SET_SCANNED, "set-scanned\r\n"
31347 		"\tSets the cargo on the specified ship or ship subsystem as known or scanned.  Takes 1 or more arguments...\r\n"
31348 		"\t1:\tName of a ship\r\n"
31349 		"\tRest:\tName of a subsystem on that ship (optional)\r\n" },
31350 
31351 	// Goober5000
31352 	{ OP_SET_UNSCANNED, "set-unscanned\r\n"
31353 		"\tSets the cargo on the specified ship or ship subsystem as unknown or unscanned.  Takes 1 or more arguments...\r\n"
31354 		"\t1:\tName of a ship\r\n"
31355 		"\tRest:\tName of a subsystem on that ship (optional)\r\n" },
31356 
31357 	{ OP_DEPART_NODE_DELAY, "depart-node-delay\r\n"
31358 		"\tReturns true N seconds after the listed ships depart, if those ships depart within the "
31359 		"radius of the given jump node.  The delay value is given in seconds.\r\n\r\n"
31360 		"Takes 3 or more arguments...r\n"
31361 		"\t1:\tDelay in seconds after the last ship listed departed before this expression can return true.\r\n"
31362 		"\t2:\tName of a jump node\r\n"
31363 		"\t3+:\tList of ships to check for departure within radius of the jump node." },
31364 
31365 	{ OP_DESTROYED_DEPARTED_DELAY, "destroyed-or-departed-delay\r\n"
31366 		"\tReturns true N seconds after all the listed ships or wings have been destroyed or have "
31367 		"departed.\r\n\r\n"
31368 		"Takes 2 or more arguments...\r\n"
31369 		"\t1:\tDelay in seconds after the last ship/wing is destroyed or departed before this expression can return true.\r\n"
31370 		"\t2+:\tName of a ship or wing" },
31371 
31372 	// description added by Goober5000
31373 	{ OP_SPECIAL_CHECK, "Special-check\r\n"
31374 		"\tMike K.'s special training sexp.  Returns a boolean value.  Takes 1 argument as follows:\r\n"
31375 		"\t0:    Ship \"Freighter 1\" is aspect locked by the player\r\n"
31376 		"\t1:    Player has fired Interceptor#Weak at Freighter 1\r\n"
31377 		"\t2:    Ship \"Freighter 1\", subsystem \"Weapons\" is aspect locked by the player\r\n"
31378 		"\t3:    Apply 10 points of damage to player's forward shields (action operator)\r\n"
31379 		"\t4:    Player's front shields are nearly gone\r\n"
31380 		"\t5:    Quickly recharge player's front shields (action operator)\r\n"
31381 		"\t6:    Reduce all shield quadrants except front to 0 (action operator)\r\n"
31382 		"\t7:    Front shield quadrant is near maximum strength\r\n"
31383 		"\t8:    Rear shield quadrant is near maximum strength\r\n"
31384 		"\t9:    Reduce left and right shield quadrants to 0 (action operator)\r\n"
31385 		"\t10:   Player has fewer than 8 missiles left\r\n"
31386 		"\t11:   Player has 8 or more missiles left\r\n"
31387 		"\t12:   Player has fewer than 4 missiles left\r\n"
31388 		"\t13:   Reduce front shield quadrant to 0 (action operator)\r\n"
31389 		"\t100:  Player is out of countermeasures\r\n"
31390 		"\t2000: Training failed"
31391 	},
31392 
31393 	{ OP_END_CAMPAIGN, "end-campaign\r\n"
31394 		"\tEnds the builtin campaign.  Should only be used by the main FreeSpace campaign\r\n" },
31395 
31396 	{ OP_SHIP_VAPORIZE, "ship-vaporize\r\n"
31397 		"\tSets the ship to vaporize when it is destroyed.  Does not actually destroy the ship - use self-destruct for that.\r\n"
31398 		"Takes 1 or more arguments...\r\n"
31399 		"\tAll:\tList of ships on which to set the vaporize flag" },
31400 
31401 	{ OP_SHIP_NO_VAPORIZE, "ship-no-vaporize\r\n"
31402 		"\tSets the ship to not vaporize when it is destroyed.  Does not actually destroy the ship - use self-destruct for that.\r\n"
31403 		"Takes 1 or more arguments...\r\n"
31404 		"\tAll:\tList of ships on which to unset the vaporize flag" },
31405 
31406 	{ OP_DONT_COLLIDE_INVISIBLE, "don't-collide-invisible\r\n"
31407 		"\tSets the \"don't collide invisible\" flag on a list of ships.\r\n"
31408 		"Takes 1 or more arguments...\r\n"
31409 		"\tAll:\tList of ships on which to set the \"don't collide invisible\" flag" },
31410 
31411 	{ OP_COLLIDE_INVISIBLE, "collide-invisible\r\n"
31412 		"\tUnsets the \"don't collide invisible\" flag on a list of ships.\r\n"
31413 		"Takes 1 or more arguments...\r\n"
31414 		"\tAll:\tList of ships on which to unset the \"don't collide invisible\" flag" },
31415 
31416 	{ OP_SET_MOBILE, "set-mobile\r\n"
31417 		"\tAllows the specified ship(s) to move.  Opposite of set-immobile.\r\n"
31418 		"Takes 1 or more arguments...\r\n"
31419 		"\tAll:\tList of ships on which to unset the \"immobile\" flag" },
31420 
31421 	{ OP_SET_IMMOBILE, "set-immobile\r\n"
31422 		"\tPrevents the specified ship(s) from moving in any way.\r\n"
31423 		"Takes 1 or more arguments...\r\n"
31424 		"\tAll:\tList of ships on which to set the \"immobile\" flag" },
31425 
31426 	{ OP_IGNORE_KEY, "ignore-key\r\n"
31427 		"\tCauses the game to ignore (or stop ignoring) a certain key.\r\n"
31428 		"Takes 2 or more arguments...\r\n"
31429 		"\t1: Number of times to ignore this key (-1 = forever, 0 = stop ignoring). \r\n"
31430 		"\tRest: Which key(s) to ignore.\r\n"
31431 	},
31432 
31433 	{ OP_WARP_BROKEN, "break-warp\r\n"
31434 		"\tBreak the warp drive on the specified ship.  A broken warp drive can be repaired by "
31435 		"a repair ship.  Takes 1 or more arguments...\r\n"
31436 		"\tAll:\tList of ships to break the warp drive on" },
31437 
31438 	{ OP_WARP_NOT_BROKEN, "fix-warp\r\n"
31439 		"\tFixes a broken warp drive instantaneously.  This option applies to warp drives broken with "
31440 		"the break-warp sepxression.  Takes 1 or more arguments...\r\n"
31441 		"\tAll:\tList of ships whose warp drive should be fixed"},
31442 
31443 	{ OP_WARP_NEVER, "never-warp\r\n"
31444 		"\tNever allows a ship to warp out.  When this sexpression is used, the given ships will "
31445 		"never be able to warp out.  The warp drive cannot be repaired.  Takes 1 or more arguments...\r\n"
31446 		"\tAll:\tList of ships whose are not allowed to warp out under any condition"},
31447 
31448 	{ OP_WARP_ALLOWED, "allow-warp\r\n"
31449 		"\tAllows a ship which was previously not allowed to warp out to do so.  When this sexpression is "
31450 		"used, the given ships will be able to warp out again.  Takes 1 or more arguments...\r\n"
31451 		"\tAll:\tList of ships whose are allowed to warp out"},
31452 
31453 	{ OP_SET_SUBSPACE_DRIVE, "set-subspace-drive\r\n"
31454 		"\tTurns on or off the subspace edrive for the given ships.  A ship with no subspace drive will act "
31455 		"as though it doesn't even occur to him to depart via subspace, and if ordered to do so, he will look "
31456 		"for a friendly ship with a hangar bay.  If the ship is the player, pressing Alt-J will not not initiate "
31457 		"a jump, nor give any indication that a jump failed.  Takes 2 or more arguments...\r\n"
31458 		"\t1:\tTrue if the ship should have a drive; false otherwise\r\n"
31459 		"\tRest:\tList of ships" },
31460 
31461 	{ OP_JETTISON_CARGO, "jettison-cargo-delay\r\n"
31462 		"\tCauses a cargo carrying ship to jettison its cargo without the undocking procedure.  Takes 2 or more arguments...\r\n"
31463 		"\t1: Ship to jettison cargo\r\n"
31464 		"\t2: Delay after which to jettison cargo (note that this isn't actually used)\r\n"
31465 		"\tRest (optional): Cargo to jettison.  If no optional arguments are specified, the ship jettisons all cargo.\r\n"
31466 	},
31467 
31468 	{ OP_SET_DOCKED, "set-docked\r\n"
31469 		"\tCauses one ship to become instantly docked to another at the specified docking ports.  Takes 4 arguments...\r\n"
31470 		"\t1: Docker ship\r\n"
31471 		"\t1: Docker point\r\n"
31472 		"\t1: Dockee ship\r\n"
31473 		"\t1: Dockee point\r\n"
31474 	},
31475 
31476 	{ OP_BEAM_FIRE, "fire-beam\r\n"
31477 		"\tFire a beam weapon from a specified subsystem\r\n"
31478 		"\t1:\tShip which will be firing\r\n"
31479 		"\t2:\tTurret which will fire the beam (note, this turret must have at least 1 beam weapon on it)\r\n"
31480 		"\t3:\tShip which will be targeted\r\n"
31481 		"\t4:\tSubsystem to target (optional)\r\n"
31482 		"\t5:\tWhether to force the beam to fire (disregarding FOV and subsystem status) (optional)\r\n" },
31483 
31484 	{ OP_BEAM_FIRE_COORDS, "fire-beam-at-coordinates\r\n"
31485 		"\tFire a beam weapon from a specified subsystem at a set of coordinates.  Not compatible with multiplayer.\r\n"
31486 		"\t1:\tShip which will be firing\r\n"
31487 		"\t2:\tTurret which will fire the beam (note, this turret must have at least 1 beam weapon on it)\r\n"
31488 		"\t3:\tx coordinate to be targeted\r\n"
31489 		"\t4:\ty coordinate to be targeted\r\n"
31490 		"\t5:\tz coordinate to be targeted\r\n"
31491 		"\t6:\t(Optional Operator) Whether to force the beam to fire (disregarding FOV and subsystem status). Defaults to False\r\n"
31492 		"\t7:\tsecond x coordinate to be targeted (optional; only used for slash beams)\r\n"
31493 		"\t8:\tsecond y coordinate to be targeted (optional; only used for slash beams)\r\n"
31494 		"\t9:\tsecond z coordinate to be targeted (optional; only used for slash beams)\r\n" },
31495 
31496 	{ OP_IS_TAGGED, "is-tagged\r\n"
31497 		"\tReturns whether a given ship is tagged or not\r\n"},
31498 
31499 	{ OP_IS_PLAYER, "is-player\r\n"
31500 		"\tReturns true if all the ships specified are currently under control of a player.\r\n"
31501 		"\t1 \t(SinglePlayer) When true ships under AI control return false even if they are the player ship\r\n"
31502 		"\t \t(Multiplayer) When true ships that are respawning return true if the player is still connected\r\n"
31503 		"\tRest \tList of ships to test"},
31504 
31505 	{ OP_NUM_KILLS, "num-kills\r\n"
31506 		"\tReturns the # of kills a player has. The ship specified in the first field should be the ship the player is in.\r\n"
31507 		"\tSo, for single player, this would be Alpha 1. For multiplayer, it can be any ship with a player in it. If, at any\r\n"
31508 		"\ttime there is no player in a given ship, this sexpression will return 0"},
31509 
31510 	{ OP_NUM_ASSISTS, "num-assists\r\n"
31511 		"\tReturns the # of assists a player has. The ship specified in the first field should be the ship the player is in.\r\n"
31512 		"\tSo, for single player, this would be Alpha 1. For multiplayer, it can be any ship with a player in it. If, at any\r\n"
31513 		"\ttime there is no player in a given ship, this sexpression will return 0"},
31514 
31515 	{ OP_SHIP_SCORE, "ship-score\r\n"
31516 		"\tReturns the score a player has. The ship specified in the first field should be the ship the player is in.\r\n"
31517 		"\tSo, for single player, this would be Alpha 1. For multiplayer, it can be any ship with a player in it. If, at any\r\n"
31518 		"\ttime there is no player in a given ship, this sexpression will return 0"},
31519 
31520 	{ OP_SHIP_DEATHS, "ship-deaths\r\n"
31521 		"\tReturns the # times a ship that is a player start has died.\r\n"
31522 		"\tThe ship specified in the first field should be the ship that could have a player in.it\r\n"
31523 		"\tOnly really useful for multiplayer."},
31524 
31525 	{ OP_RESPAWNS_LEFT, "respawns-left\r\n"
31526 		"\tReturns the # respawns a player (or AI that could have been a player) has remaining.\r\n"
31527 		"\tThe ship specified in the first field should be the player start.\r\n"
31528 		"\tOnly really useful for multiplayer."},
31529 
31530 	{ OP_REMOVE_WEAPONS, "remove-weapons\r\n"
31531 		"\tRemoves all live weapons currently in the game"
31532 		"\t1: (Optional) Remove only this specific weapon\r\n"},
31533 
31534 	{ OP_SET_RESPAWNS, "set-respawns\r\n"
31535 		"\tSet the # respawns a player (or AI that could have been a player) has used.\r\n"
31536 		"\t1: Number of respawns used up\r\n"
31537 		"\tRest: The player start ship to operate on.\r\n"
31538 		"\tOnly really useful for multiplayer."},
31539 
31540 	{ OP_NUM_TYPE_KILLS, "num-type-kills\r\n"
31541 		"\tReturns the # of kills a player has on a given ship type (fighter, bomber, cruiser, etc).\r\n"
31542 		"The ship specified in the first field should be the ship the player is in.\r\n"
31543 		"\tSo, for single player, this would be Alpha 1. For multiplayer, it can be any ship with a player in it. If, at any\r\n"
31544 		"\ttime there is no player in a given ship, this sexpression will return 0"},
31545 
31546 	{ OP_NUM_CLASS_KILLS, "num-class-kills\r\n"
31547 		"\tReturns the # of kills a player has on a specific ship class (Ulysses, Hercules, etc).\r\n"
31548 		"The ship specified in the first field should be the ship the player is in.\r\n"
31549 		"\tSo, for single player, this would be Alpha 1. For multiplayer, it can be any ship with a player in it. If, at any\r\n"
31550 		"\ttime there is no player in a given ship, this sexpression will return 0"},
31551 
31552 	{ OP_BEAM_FREE, "beam-free\r\n"
31553 		"\tSets one or more beam weapons to allow firing for a given ship\r\n"
31554 		"\t1: Ship to be operated on\r\n"
31555 		"\t2, 3, etc : List of turrets to activate\r\n"},
31556 
31557 	{ OP_BEAM_FREE_ALL, "beam-free-all\r\n"
31558 		"\tSets all beam weapons on the specified ship to be active\r\n"},
31559 
31560 	{ OP_BEAM_LOCK, "beam-lock\r\n"
31561 		"\tSets one or more beam weapons to NOT allow firing for a given ship\r\n"
31562 		"\t1: Ship to be operated on\r\n"
31563 		"\t2, 3, etc : List of turrets to deactivate\r\n"},
31564 
31565 	{ OP_BEAM_LOCK_ALL, "beam-lock-all\r\n"
31566 		"\tSets all beam weapons on the specified ship to be deactivated\r\n"},
31567 
31568 	{ OP_TURRET_FREE, "turret-free\r\n"
31569 		"\tSets one or more turret weapons to allow firing for a given ship\r\n"
31570 		"\t1: Ship to be operated on\r\n"
31571 		"\t2, 3, etc : List of turrets to activate\r\n"},
31572 
31573 	{ OP_TURRET_FREE_ALL, "turret-free-all\r\n"
31574 		"\tSets all turret weapons on the specified ship to be active\r\n"},
31575 
31576 	{ OP_TURRET_LOCK, "turret-lock\r\n"
31577 		"\tSets one or more turret weapons to NOT allow firing for a given ship\r\n"
31578 		"\t1: Ship to be operated on\r\n"
31579 		"\t2, 3, etc : List of turrets to deactivate\r\n"},
31580 
31581 	{ OP_TURRET_LOCK_ALL, "turret-lock-all\r\n"
31582 		"\tSets all turret weapons on the specified ship to be deactivated\r\n"},
31583 
31584 	{ OP_TURRET_CHANGE_WEAPON, "turret-change-weapon\r\n"
31585 		"\tSets a given turret weapon slot to the specified weapon\r\n"
31586 		"\t1: Ship turret is on\r\n"
31587 		"\t2: Turret\r\n"
31588 		"\t3: Weapon to set slot to\r\n"
31589 		"\t4: Primary slot (or 0 to use secondary)\r\n"
31590 		"\t5: Secondary slot (or 0 to use primary)"},
31591 
31592 	{ OP_TURRET_SET_DIRECTION_PREFERENCE, "turret-set-direction-preference\r\n"
31593 		"\tSets specified ship turrets direction preference to the specified value\r\n"
31594 		"\t1: Ship turrets are on\r\n"
31595 		"\t2: Preference to set, 0 to disable, or negative to reset to default\r\n"
31596 		"\trest: Turrets to set\r\n"},
31597 
31598 	{ OP_TURRET_SET_RATE_OF_FIRE, "turret-set-rate-of-fire\r\n"
31599 		"\tSets specified ship turrets rate of fire to the specified value\r\n"
31600 		"\t1: Ship turrets are on\r\n"
31601 		"\t2: Rate to set in percentage format (200 = 2x, 50 = .5x), 0 to set to number of fire points, or negative to reset to default\r\n"
31602 		"\trest: Turrets to set\r\n"},
31603 
31604 	{ OP_TURRET_SET_OPTIMUM_RANGE, "turret-set-optimum-range\r\n"
31605 		"\tSets specified ship turrets optimum range to the specified value\r\n"
31606 		"\t1: Ship turrets are on\r\n"
31607 		"\t2: Priority to set, 0 to disable, or negative to reset to default\r\n"
31608 		"\trest: Turrets to set\r\n"},
31609 
31610 	{ OP_TURRET_SET_TARGET_PRIORITIES, "turret-set-target-priorities\r\n"
31611 		"\tSets target priorities for the specified ship turret\r\n"
31612 		"\t1: Ship turret is on\r\n"
31613 		"\t2: Turret to set\r\n"
31614 		"\t3: True = Set new list, False = Reset to turret default\r\n"
31615 		"\trest: Priorities to set (max 32) or blank for no priorities\r\n"},
31616 
31617 	{ OP_SET_ARMOR_TYPE, "set-armor-type\r\n"
31618 		"\tSets the armor type for a ship or subsystem\r\n"
31619 		"\t1: Ship subsystem is on\r\n"
31620 		"\t2: Set = true/Reset to defualt = false\r\n"
31621 		"\t3: Armor type to set or <none>\r\n"
31622 		"\trest: Subsystems to set (hull for ship, shield for shields)\r\n"},
31623 
31624 	{ OP_WEAPON_SET_DAMAGE_TYPE, "weapon-set-damage-type\r\n"
31625 		"\tSets the damage type for weapons or their shockwaves\r\n"
31626 		"\t1: True = set weapon, False = set shockwave\r\n"
31627 		"\t2: damage type to set or <none>\r\n"
31628 		"\t3: Set = true/Reset to defualt = false\r\n"
31629 		"\trest: Weapons to set\r\n"},
31630 
31631 	{ OP_SHIP_SET_DAMAGE_TYPE, "ship-set-damage-type\r\n"
31632 		"\tSets the damage type for ships collision or debris\r\n"
31633 		"\t1: true = set collision, False = set debris\r\n"
31634 		"\t2: Damage type to set or <none>\r\n"
31635 		"\t3: Set = true/Reset to defualt = false\r\n"
31636 		"\trest: Ships to set\r\n"},
31637 
31638 	{ OP_SHIP_SHOCKWAVE_SET_DAMAGE_TYPE, "ship-set-shockwave-damage-type\r\n"
31639 		"\tSets the shockwave damage type for a class of ship.  All ships of that class are changed.\r\n"
31640 		"\t1: Damage type to set or <none>\r\n"
31641 		"\t2: Set = true/Reset to defualt = false\r\n"
31642 		"\trest: Ship classes to set\r\n"},
31643 
31644 	{ OP_FIELD_SET_DAMAGE_TYPE, "field-set-damage-type\r\n"
31645 		"\tSets the damage type for asteroid/debris fields\r\n"
31646 		"\t1: Damage type to set or <none>\r\n"
31647 		"\t2: Set = true/Reset to defualt = false\r\n"},
31648 
31649 	{ OP_TURRET_SET_TARGET_ORDER, "turret-set-target-order\r\n"
31650 		"\tSets targeting order of a given turret\r\n"
31651 		"\t1: Ship turret is on\r\n"
31652 		"\t2: Turret\r\n"
31653 		"\trest: Target order type (Bombs,ships,asteroids)"},
31654 
31655 	{ OP_SHIP_TURRET_TARGET_ORDER, "ship-turret-target-order\r\n"
31656 		"\tSets targeting order of all turrets on a given ship\r\n"
31657 		"\t1: Ship turrets are on\r\n"
31658 		"\trest: Target order type (Bombs,ships,asteroids)"},
31659 
31660 	{ OP_TURRET_SUBSYS_TARGET_DISABLE, "turret-subsys-target-disable\r\n"
31661 		"\tPrevents turrets from targeting only the subsystems when targeting large targets\r\n"
31662 		"\t1: Ship to be operated on\r\n"
31663 		"\trest: List of turrets that are affected\r\n"},
31664 
31665 	{ OP_TURRET_SUBSYS_TARGET_ENABLE, "turret-subsys-target-enable\r\n"
31666 		"\tSets turret to target the subsystems when targeting large targets\r\n"
31667 		"\t1: Ship to be operated on\r\n"
31668 		"\trest: List of turrets that are affected\r\n"},
31669 
31670 	{ OP_ADD_REMOVE_ESCORT, "add-remove-escort\r\n"
31671 		"\tAdds or removes a ship from an escort list.\r\n"
31672 		"\t1: Ship to be added or removed\r\n"
31673 		"\t2: 0 to remove from the list, any positive value will be used as the escort priority\r\n"
31674 		"NOTE : it _IS_ safe to add a ship which may already be on the list or remove\r\n"
31675 		"a ship which is not on the list\r\n"},
31676 
31677 	{ OP_AWACS_SET_RADIUS, "awacs-set-radius\r\n"
31678 		"\tSets the awacs radius for a given ship subsystem. NOTE : does not work properly in multiplayer\r\n"
31679 		"\t1: Ship which has the awacs subsystem\r\n"
31680 		"\t2: Awacs subsystem\r\n"
31681 		"\t3: New radius\r\n"},
31682 
31683 	// Goober5000
31684 	{ OP_PRIMITIVE_SENSORS_SET_RANGE, "primitive-sensors-set-range\r\n"
31685 		"\tSets the range of the primitive sensors on a ship.  Ships outside of this range will not appear on "
31686 		"sensors.  Has no effect on ships that do not have the \"primitive-sensors\" flag2 set.  Takes 2 arguments...\r\n"
31687 		"\t1: Ship on which to set range\r\n"
31688 		"\t2: Range, in meters\r\n" },
31689 
31690 	{ OP_SEND_MESSAGE_LIST, "send-message-list\r\n"
31691 		"\tSends a series of delayed messages. All times are accumulated.\r\n"
31692 		"\t1:\tName of who the message is from.\r\n"
31693 		"\t2:\tPriority of message (\"Low\", \"Normal\" or \"High\").\r\n"
31694 		"\t3:\tName of message (from message editor).\r\n"
31695 		"\t4:\tDelay from previous message in list (if any) in ms\r\n"
31696 		"Use Add-Data for multiple messages.\r\n\r\n"
31697 		"IMPORTANT: Each additional message in the list MUST HAVE four entries; "
31698 		"any message without the four proper fields will be ignored, as will any "
31699 		"successive messages."},
31700 
31701 	{ OP_CAP_WAYPOINT_SPEED, "cap-waypoint-speed\r\n"
31702 		"\tSets the maximum speed of a ship while flying waypoints.\r\n"
31703 		"\t1: Ship name\r\n"
31704 		"\t2: Maximum speed while flying waypoints\r\n"
31705 		"\tNOTE: This will only work if the ship is already in the game\r\n"
31706 		"\tNOTE: Set to -1 to reset\r\n"},
31707 
31708 	{ OP_TURRET_TAGGED_ONLY_ALL, "turret-tagged-only\r\n"
31709 		"\tMakes turrets target and hence fire strictly at tagged objects\r\n"
31710 		"\t1: Ship name\r\n"
31711 		"\tNOTE: Will not stop a turret already firing at an untagged ship\r\n"},
31712 
31713 	{ OP_TURRET_TAGGED_CLEAR_ALL, "turret-tagged-clear\r\n"
31714 		"\tRelaxes restriction on turrets targeting only tagged ships\r\n"
31715 		"\t1: Ship name\r\n"},
31716 
31717 	{ OP_PRIMARIES_DEPLETED, "primaries-depleted\r\n"
31718 		"\tReturns true if ship is out of primary weapons\r\n"
31719 		"\t1: Ship name\r\n"},
31720 
31721 	{ OP_SECONDARIES_DEPLETED, "secondaries-depleted\r\n"
31722 		"\tReturns true if ship is out of secondary weapons\r\n"
31723 		"\t1: Ship name\r\n"},
31724 
31725 	{ OP_SUBSYS_SET_RANDOM, "subsys-set-random\r\n"
31726 		"\tSets ship subsystem strength in a given range\r\n"
31727 		"\t1: Ship name\r\n"
31728 		"\t2: Low range\r\n"
31729 		"\t3: High range\r\n"
31730 		"\t4: List of subsys names not to be randomized\r\n"},
31731 
31732 	{ OP_SUPERNOVA_START, "supernova-start\r\n"
31733 		"\t1: Time in seconds until the supernova shockwave hits the player\r\n"},
31734 
31735 	{ OP_SUPERNOVA_STOP, "supernova-stop\r\n"
31736 		"\t Stops a supernova in progress.\r\n"
31737 		"\t Note this only works if the camera hasn't cut to the player's death animation yet.\r\n"},
31738 
31739 	{ OP_WEAPON_RECHARGE_PCT, "weapon-recharge-pct\r\n"
31740 		"\tReturns a percentage from 0 to 100\r\n"
31741 		"\t1: Ship name\r\n" },
31742 
31743 	{ OP_SHIELD_RECHARGE_PCT, "shield-recharge-pct\r\n"
31744 		"\tReturns a percentage from 0 to 100\r\n"
31745 		"\t1: Ship name\r\n" },
31746 
31747 	{ OP_ENGINE_RECHARGE_PCT, "engine-recharge-pct\r\n"
31748 		"\tReturns a percentage from 0 to 100\r\n"
31749 		"\t1: Ship name\r\n" },
31750 
31751 	{ OP_GET_ETS_VALUE, "get-ets-values\r\n"
31752 		"\tGets one ETS index for a ship\r\n"
31753 		"\t1: ETS index to get, Engine|Shield|Weapon\r\n"
31754 		"\t2: Ship name\r\n"},
31755 
31756 	{ OP_SET_ETS_VALUES, "set-ets-values\r\n"
31757 		"\tSets ETS indexes for a ship\r\n"
31758 		"\tUse values retrieved with get-ets-value\r\n"
31759 		"\tIf you use your own values, make sure they add up to 12\r\n"
31760 		"\t1: Engine percent\r\n"
31761 		"\t2: Shields percent\r\n"
31762 		"\t3: Weapons percent\r\n"
31763 		"\t4: Ship name\r\n"},
31764 
31765 	{ OP_CARGO_NO_DEPLETE, "cargo-no-deplete\r\n"
31766 		"\tCauses the named ship to have unlimited cargo.\r\n"
31767 		"\tNote:  only applies to BIG or HUGE ships\r\n"
31768 		"Takes 1 or more arguments...\r\n"
31769 		"\t1:\tName of one of the ships.\r\n"
31770 		"\t2:\toptional: 1 disallow depletion, 0 allow depletion." },
31771 
31772 	{ OP_SHIELD_QUAD_LOW, "shield-quad-low\r\n"
31773 		"\tReturns true if the specified ship has a shield quadrant below\r\n"
31774 		"\tthe specified threshold percentage\r\n"
31775 		"\t1: Ship name\r\n"
31776 		"\t2: Percentage\r\n" },
31777 
31778 	{ OP_PRIMARY_AMMO_PCT, "primary-ammo-pct\r\n"
31779 		"\tReturns the percentage of ammo remaining in the specified ballistic primary bank (0 to 100).  Non-ballistic primary banks return as 100%.\r\n"
31780 		"\t1: Ship name\r\n"
31781 		"\t2: Bank to check (from 0 to N-1, where N is the number of primary banks in the ship; N or higher will return the cumulative average for all banks)" },
31782 
31783 	// Karajorma
31784 	{ OP_GET_PRIMARY_AMMO, "get-primary-ammo\r\n"
31785 		"\tReturns the amount of ammo remaining in the specified bank\r\n"
31786 		"\t1: Ship name\r\n"
31787 		"\t2: Bank to check (from 0 to N-1, where N is the number of primary banks in the ship; N or higher will return the cumulative average for all banks)" },
31788 
31789 
31790 	{ OP_SECONDARY_AMMO_PCT, "secondary-ammo-pct\r\n"
31791 		"\tReturns the percentage of ammo remaining in the specified bank (0 to 100)\r\n"
31792 		"\t1: Ship name\r\n"
31793 		"\t2: Bank to check (from 0 to N-1, where N is the number of secondary banks in the ship; N or higher will return the cumulative average for all banks)" },
31794 
31795 	// Karajorma
31796 	{ OP_GET_SECONDARY_AMMO, "get-secondary-ammo\r\n"
31797 		"\tReturns the amount of ammo remaining in the specified bank\r\n"
31798 		"\t1: Ship name\r\n"
31799 		"\t2: Bank to check (from 0 to N-1, where N is the number of secondary banks in the ship; N or higher will return the cumulative average for all banks)" },
31800 
31801 	// Karajorma
31802 	{ OP_GET_NUM_COUNTERMEASURES, "get-num-countermeasures\r\n"
31803 		"\tReturns the amount of countermeasures remaining\r\n"
31804 		"\t1: Ship name\r\n" },
31805 
31806 	{ OP_IS_SECONDARY_SELECTED, "is-secondary-selected\r\n"
31807 		"\tReturns true if the specified bank is selected (0 .. num_banks - 1)\r\n"
31808 		"\t1: Ship name\r\n"
31809 		"\t2: Bank to check (0 .. num_banks - 1)\r\n"},
31810 
31811 	{ OP_IS_PRIMARY_SELECTED, "is-primary-selected\r\n"
31812 		"\tReturns true if the specified bank is selected (0 .. num_banks - 1)\r\n"
31813 		"\t1: Ship name\r\n"
31814 		"\t2: Bank to check (0 .. num_banks - 1)\r\n"},
31815 
31816 	{ OP_SPECIAL_WARP_DISTANCE, "special-warp-dist\r\n"
31817 		"\tReturns distance to the plane of the knossos device in percent length of ship\r\n"
31818 		"\t(ie, 100 means front of ship is 1 ship length from plane of knossos device)\r\n"
31819 		"\t1: Ship name\r\n"},
31820 
31821 	{ OP_SET_SPECIAL_WARPOUT_NAME, "special-warpout-name\r\n"
31822 		"\tSets the name of the knossos device to be used for warpout\r\n"
31823 		"\t1: Ship name to exit\r\n"
31824 		"\t2: Name of knossos device\r\n"},
31825 
31826 	{ OP_SHIP_VANISH, "ship-vanish\r\n"
31827 		"\tMakes the named ship vanish (no log and vanish)\r\n"
31828 		"\tSingle Player Only!  Warning: This will cause ship exit not to be logged, so 'has-departed', etc. will not work\r\n"
31829 		"\t1: List of ship names to vanish\r\n"},
31830 
31831 	{ OP_DESTROY_INSTANTLY, "destroy-instantly\r\n"
31832 		"\tSelf-destructs the named ship without explosion, death roll, or debris.  That is, the ship is instantly gone from the mission and the only indication of what happened is a mission log entry.\r\n"
31833 		"\tSingle Player Only! Non-player ship only!\r\n"
31834 		"\tAll: List of ship names to destroy.\r\n"},
31835 
31836 	{ OP_SHIP_CREATE, "ship-create\r\n"
31837 		"\tCreates a new ship\r\n"
31838 		"\tTakes 5 to 8 arguments...\r\n"
31839 		"\t1: Name of new ship (use \"" SEXP_NONE_STRING "\" for a default name)\r\n"
31840 		"\t2: Class of new ship\r\n"
31841 		"\t3: X position\r\n"
31842 		"\t4: Y position\r\n"
31843 		"\t5: Z position\r\n"
31844 		"\t6: Pitch (optional)\r\n"
31845 		"\t7: Bank (optional)\r\n"
31846 		"\t8: Heading (optional)\r\n"
31847 	},
31848 
31849 	// Goober5000
31850 	{ OP_WEAPON_CREATE, "weapon-create\r\n"
31851 		"\tCreates a new weapon\r\n"
31852 		"\tTakes 5 to 10 arguments...\r\n"
31853 		"\t 1: Name of parent ship (or \"" SEXP_NONE_STRING "\" for no parent)\r\n"
31854 		"\t 2: Class of new weapon\r\n"
31855 		"\t 3: X position\r\n"
31856 		"\t 4: Y position\r\n"
31857 		"\t 5: Z position\r\n"
31858 		"\t 6: Pitch (optional)\r\n"
31859 		"\t 7: Bank (optional)\r\n"
31860 		"\t 8: Heading (optional)\r\n"
31861 		"\t 9: Targeted ship (optional)\r\n"
31862 		"\t10: Targeted subsystem (optional)\r\n"
31863 	},
31864 
31865 	{ OP_IS_SHIP_VISIBLE, "is-ship-visible\r\n"
31866 		"\tCheck whether ship is visible on Player's radar\r\n"
31867 		"\tSingle Player Only!  Returns 0 - not visible, 1 - partially visible, 2 - fully visible.\r\n"
31868 		"\t1: Name of ship to check\r\n"},
31869 
31870 	// Goober5000
31871 	{ OP_IS_SHIP_STEALTHY, "is-ship-stealthy\r\n"
31872 		"\tCheck whether ship is currently stealthy.\r\n"
31873 		"\tTrue if stealth flag set, false otherwise.  Takes 1 argument...\r\n"
31874 		"\t1: Name of ship to check\r\n"},
31875 
31876 	// Goober5000
31877 	{ OP_IS_FRIENDLY_STEALTH_VISIBLE, "is-friendly-stealth-visible\r\n"
31878 		"\tCheck whether ship will be visible to radar as a stealth friendly.\r\n"
31879 		"\tTakes 1 argument...\r\n"
31880 		"\t1: Name of ship to check\r\n"},
31881 
31882 	{ OP_TEAM_SCORE, "team-score\r\n"
31883 		"\tGet the score of a multi team vs team game.\r\n"
31884 		"\t1: Team index (1 for team 1 and 2 for team 2)\r\n"},
31885 
31886 	// phreak
31887 	{ OP_DAMAGED_ESCORT_LIST, "damaged-escort-list\r\n"
31888 		"\tSets the most damaged ship in <ship list> to <priority1>, sets the others to <priority2>.  Don't use this sexp in the same mission as damaged-escort-list-all, or strange results might occur.\r\n"
31889 		"\t1: Priority1\r\n"
31890 		"\t2: Priority2\r\n"
31891 		"\tRest: <ship_list>\r\n\r\n"
31892 	},
31893 
31894 	// Goober5000
31895 	{ OP_DAMAGED_ESCORT_LIST_ALL, "damaged-escort-list-all\r\n"
31896 		"\tSets the most damaged ship in the entire existing escort list (even what's not shown onscreen) to <priority1>, the next most damaged to <priority2>, and so on.  "
31897 		"If there are more ships than priorities, the least most damaged ships are all set to the last priority in the list.  Don't use this sexp in the same mission as damaged-escort-list, or strange results might occur.\r\n"
31898 		"\tTakes between 1 and MAX_COMPLETE_ESCORT_LIST (currently 20) arguments...\r\n"
31899 		"\t1: Priority 1\r\n"
31900 		"\tRest: Priorities 2 through 20 (optional)\r\n\r\n"
31901 	},
31902 
31903 	// Goober5000
31904 	{ OP_CHANGE_SHIP_CLASS, "change-ship-class\r\n"
31905 		"\tCauses the listed ships' classes to be changed to the specified ship class.  Takes 2 or more arguments...\r\n"
31906 		"\t1: The name of the new ship class\r\n"
31907 		"\tRest: The list of ships to change the classes of"
31908 	},
31909 
31910 	// Goober5000
31911 	{ OP_SHIP_COPY_DAMAGE, "ship-copy-damage\r\n"
31912 		"\tCopies the damage (hull, shields, and subsystems) from the first ship in the list to the rest.  The initial ship must be currently "
31913 		"present in the mission, but the target ships may be either in-mission or on the arrival list.  Takes 2 or more arguments...\r\n"
31914 		"\t1: The name of the ship that supplies the damage stats\r\n"
31915 		"\tRest: The list of ships to be modified"
31916 	},
31917 
31918 	// Goober5000
31919 	{ OP_SET_SUPPORT_SHIP, "set-support-ship\r\n"
31920 		"\tSets information for all support ships in a mission.  Takes 6 or 7 arguments...\r\n"
31921 		"\t1: Arrival location\r\n"
31922 		"\t2: Arrival anchor\r\n"
31923 		"\t3: Departure location\r\n"
31924 		"\t4: Departure anchor\r\n"
31925 		"\t5: Ship class\r\n"
31926 		"\t6: Maximum number of support ships consecutively in this mission (use a negative number for infinity)\r\n"
31927 		"\t7: Maximum number of support ships concurrently in this mission (optional, default 1)\r\n"
31928 		"\r\n"
31929 		"Note: The support ship will emerge from or depart into hyperspace if the location is set as hyperspace *or* the anchor is set as <no anchor>."
31930 	},
31931 
31932 	// Goober5000
31933 	{ OP_SET_ARRIVAL_INFO, "set-arrival-info\r\n"
31934 		"\tSets arrival information for a ship or wing.  Takes 2 to 7 arguments...\r\n"
31935 		"\t1: Ship or wing name\r\n"
31936 		"\t2: Arrival location\r\n"
31937 		"\t3: Arrival anchor (optional; only required for certain locations)\r\n"
31938 		"\t4: Arrival path mask (optional; defaults to 0; note that this is a bitfield)\r\n"
31939 		"\t5: Arrival distance (optional; defaults to 0)\r\n"
31940 		"\t6: Arrival delay (optional; defaults to 0)\r\n"
31941 		"\t7: Whether to show a jump effect if arriving from subspace (optional; defaults to true)\r\n"
31942 	},
31943 
31944 	// Goober5000
31945 	{ OP_SET_DEPARTURE_INFO, "set-departure-info\r\n"
31946 		"\tSets departure information for a ship or wing.  Takes 2 to 6 arguments...\r\n"
31947 		"\t1: Ship or wing name\r\n"
31948 		"\t2: Departure location\r\n"
31949 		"\t3: Departure anchor (optional; only required for certain locations)\r\n"
31950 		"\t4: Departure path mask (optional; defaults to 0; note that this is a bitfield)\r\n"
31951 		"\t5: Departure delay (optional; defaults to 0)\r\n"
31952 		"\t6: Whether to show a jump effect if departing to subspace (optional; defaults to true)\r\n"
31953 	},
31954 
31955 	// Bobboau
31956 	{ OP_DEACTIVATE_GLOW_POINTS, "deactivate-glow-points\r\n"
31957 		"\tDeactivates all glow points on a ship.  Takes 1 or more arguments...\r\n"
31958 		"\tAll: Name of ship on which to deactivate glow points\r\n"
31959 	},
31960 
31961 	// Bobboau
31962 	{ OP_ACTIVATE_GLOW_POINTS, "activate-glow-points\r\n"
31963 		"\tActivates all glow points on a ship.  Takes 1 or more arguments...\r\n"
31964 		"\tAll: Name of ship on which to activate glow points\r\n"
31965 	},
31966 
31967 	// Bobboau
31968 	{ OP_DEACTIVATE_GLOW_MAPS, "deactivate-glow-maps\r\n"
31969 		"\tDeactivates the glow maps for a ship.  Takes 1 or more arguments...\r\n"
31970 		"\tAll: Name of ship on which to deactivate glow maps\r\n"
31971 	},
31972 
31973 	// Bobboau
31974 	{ OP_ACTIVATE_GLOW_MAPS, "activate-glow-maps\r\n"
31975 		"\tActivates the glow maps for a ship.  Takes 1 or more arguments...\r\n"
31976 		"\tAll: Name of ship on which to activate glow maps\r\n"
31977 	},
31978 
31979 	// Bobboau
31980 	{ OP_DEACTIVATE_GLOW_POINT_BANK, "deactivate-glow-point-bank\r\n"
31981 		"\tDeactivates one or more glow point bank(s) on a ship.  Takes 2 or more arguments...\r\n"
31982 		"\t1: Name of ship on which to deactivate glow point bank(s)\r\n"
31983 		"\tRest: Name of glow point bank to deactivate\r\n"
31984 	},
31985 
31986 	// Bobboau
31987 	{ OP_ACTIVATE_GLOW_POINT_BANK, "activate-glow-point-bank\r\n"
31988 		"\tActivates one or more glow point bank(s) on a ship.  Takes 2 or more arguments...\r\n"
31989 		"\t1: Name of ship on which to activate glow point bank(s)\r\n"
31990 		"\tRest: Name of glow point bank to activate\r\n"
31991 	},
31992 
31993 	//-Sesquipedalian
31994 	{ OP_KAMIKAZE, "kamikaze\r\n"
31995 		"\tTells ships to perform a kamikaze on its current target. Takes 2 or more arguments...\r\n"
31996 		"\t1: Damage dealt when kamikaze is done\r\n"
31997 		"\tRest: Names of ships to perform kamikaze\r\n"
31998 	},
31999 
32000 	//phreak
32001 	{ OP_TURRET_TAGGED_SPECIFIC, "turret-tagged-specific\r\n"
32002 		"\tSpecific turrets on a ship only fire at tagged targets, as opposed to all turrets doing this using turret-tagged-only\r\n"
32003 		"\tIt is safe to slave turrets already slaved\r\n"
32004 		"\tTakes 2 or more arguments...\r\n"
32005 		"\t1: Name of ship to slave some turrets to target only tagged ships\r\n"
32006 		"\tRest: Turrets to slave\r\n"
32007 	},
32008 
32009 	//phreak
32010 	{ OP_TURRET_TAGGED_CLEAR_SPECIFIC, "turret-tagged-clear-specific\r\n"
32011 		"\tSpecific turrets on a ship are free to fire on untagged ships, as opposed to all turrets doing this using turret-tagged-clear\r\n"
32012 		"\tIt is safe to unslave turrets already free\r\n"
32013 		"\tTakes 2 or more arguments...\r\n"
32014 		"\t1: Name of ship to unslave some turrets to target any hostile ship\r\n"
32015 		"\tRest: Turrets to unslave\r\n"
32016 	},
32017 
32018 	// Goober5000
32019 	{ OP_LOCK_ROTATING_SUBSYSTEM, "lock-rotating-subsystem\r\n"
32020 		"\tInstantaneously locks a rotating subsystem so that it cannot rotate unless freed by free-rotating-subsystem.  "
32021 		"Takes 2 or more arguments...\r\n"
32022 		"\t1:\tName of the ship housing the subsystem\r\n"
32023 		"\tRest:\tName of the rotating subsystem to lock"
32024 	},
32025 
32026 	// Goober5000
32027 	{ OP_FREE_ROTATING_SUBSYSTEM, "free-rotating-subsystem\r\n"
32028 		"\tInstantaneously frees a rotating subsystem previously locked by lock-rotating-subsystem.  "
32029 		"Takes 2 or more arguments...\r\n"
32030 		"\t1:\tName of the ship housing the subsystem\r\n"
32031 		"\tRest:\tName of the rotating subsystem to free"
32032 	},
32033 
32034 	// Goober5000
32035 	{ OP_REVERSE_ROTATING_SUBSYSTEM, "reverse-rotating-subsystem\r\n"
32036 		"\tInstantaneously reverses the rotation direction of a rotating subsystem.  "
32037 		"Takes 2 or more arguments...\r\n"
32038 		"\t1:\tName of the ship housing the subsystem\r\n"
32039 		"\tRest:\tName of the rotating subsystem to reverse"
32040 	},
32041 
32042 	// Goober5000
32043 	{ OP_ROTATING_SUBSYS_SET_TURN_TIME, "rotating-subsys-set-turn-time\r\n"
32044 		"\tSets the turn time of a rotating subsystem.  "
32045 		"Takes 3 or 4 arguments...\r\n"
32046 		"\t1:\tName of the ship housing the subsystem\r\n"
32047 		"\t2:\tName of the rotating subsystem to configure\r\n"
32048 		"\t3:\tThe time for one complete rotation, in milliseconds (positive is counterclockwise, negative is clockwise)\r\n"
32049 		"\t4:\tThe acceleration (x1000, just as #3 is seconds x1000) to change from the current turn rate to the desired turn rate.  "
32050 		"This is actually the time to complete one rotation that changes in one second, or the reciprocal of what you might expect, "
32051 		"meaning that larger numbers cause slower acceleration.  (FS2 defaults to 2pi/0.5, or about 12.566, which would be 12566 in this sexp.)  "
32052 		"The advantage of this method is so that this argument can be directly compared to the previous argument using a ratio, without worrying about pi.  "
32053 		"Omit this argument if you want an instantaneous change."
32054 	},
32055 
32056 	// Goober5000
32057 	{ OP_TRIGGER_SUBMODEL_ANIMATION, "trigger-submodel-animation\r\n"
32058 		"\tActivates a submodel animation trigger for a given ship.  Takes 4 to 6 arguments...\r\n"
32059 		"\t1: The ship on which the animation should run\r\n"
32060 		"\t2: The type of animation (named as one would see them in ships.tbl)\r\n"
32061 		"\t3: The subtype of animation, which is type-dependent.  For docking animations this is the dock index.\r\n"
32062 		"\t4: The animation direction: 1 for forward, or -1 for reverse\r\n"
32063 		"\t5: (Optional) Whether the animation should instantly snap to its final position\r\n"
32064 		"\t6: (Optional) A subsystem, if the animation should trigger on only a specific subsystem as opposed to all applicable subsystems\r\n"
32065 	},
32066 
32067 	// Karajorma
32068 	{ OP_SET_PRIMARY_AMMO, "set-primary-ammo\r\n"
32069 		"\tSets the amount of ammo for the specified ballistic bank\r\n"
32070 		"\t1: Ship name\r\n"
32071 		"\t2: Bank to check (0, 1, and 2 are legal banks)\r\n"
32072 		"\t3: Number to set this bank to (If this is larger than the maximimum, bank will be set to maximum).\r\n"
32073 		"\t4: Rearm Limit. Support ships will only supply this number of weapons (If this is larger than the maximimum, bank will be set to maximum)"
32074 	},
32075 
32076 	// Karajorma
32077 	{ OP_SET_SECONDARY_AMMO, "set-secondary-ammo\r\n"
32078 		"\tSets the amount of ammo for the specified bank\r\n"
32079 		"\t1: Ship name\r\n"
32080 		"\t2: Bank to check (0, 1, 2 and 3 are legal banks)\r\n"
32081 		"\t3: Number to set this bank to (If this is larger than the maximimum, bank will be set to maximum).\r\n"
32082 		"\t4: Rearm Limit. Support ships will only supply this number of weapons (If this is larger than the maximimum, bank will be set to maximum)"
32083 	},
32084 
32085 	// Karajorma
32086 	{ OP_SET_PRIMARY_WEAPON, "set-primary-weapon\r\n"
32087 		"\tSets the weapon for the specified bank\r\n"
32088 		"\t1: Ship name\r\n"
32089 		"\t2: Bank to check (0, 1 and 2 are legal banks)\r\n"
32090 		"\t3: Name of the primary weapon \r\n"
32091 		"\t4: Number to set this bank to (If this is larger than the maximimum, bank will be set to maximum)\r\n"
32092 		"\t5: Rearm Limit. Support ships will only supply this number of weapons (If this is larger than the maximimum, bank will be set to maximum)"
32093 	},
32094 
32095 	// Karajorma
32096 	{ OP_SET_SECONDARY_WEAPON, "set-secondary-weapon\r\n"
32097 		"\tSets the weapon for the specified bank\r\n"
32098 		"\t1: Ship name\r\n"
32099 		"\t2: Bank to check (0, 1, 2 and 3 are legal banks)\r\n"
32100 		"\t3: Name of the secondary weapon \r\n"
32101 		"\t4: Number to set this bank to (If this is larger than the maximimum, bank will be set to maximum)\r\n"
32102 		"\t5: Rearm Limit. Support ships will only supply this number of weapons (If this is larger than the maximimum, bank will be set to maximum)"
32103 	},
32104 
32105 
32106 
32107 	// Karajorma
32108 	{ OP_SET_NUM_COUNTERMEASURES, "set-num-countermeasures\r\n"
32109 		"\tSets the number of countermeasures the ship has\r\n"
32110 		"\tValues greater than the maximum a ship can carry are set to the maximum\r\n"
32111 		"\t1: Ship name\r\n"
32112 		"\t2: Number to set"
32113 	},
32114 
32115 	// Karajorma
32116 	{ OP_LOCK_PRIMARY_WEAPON, "lock-primary-weapon\r\n"
32117 		"\tLocks the primary banks for the specified ship(s)\r\n"
32118 		"\tTakes 1 or more arguments\r\n"
32119 		"\t(all): Name(s) of ship(s) to lock"
32120 	},
32121 
32122 	// Karajorma
32123 	{ OP_UNLOCK_PRIMARY_WEAPON, "unlock-primary-weapon\r\n"
32124 		"\tUnlocks the primary banks for the specified ship(s)\r\n"
32125 		"\tTakes 1 or more arguments\r\n"
32126 		"\t(all): Name(s) of ship(s) to lock"
32127 	},
32128 
32129 	// Karajorma
32130 	{ OP_LOCK_SECONDARY_WEAPON, "lock-secondary-weapon\r\n"
32131 		"\tLocks the secondary banks for the specified ship(s)\r\n"
32132 		"\tTakes 1 or more arguments\r\n"
32133 		"\t(all): Name(s) of ship(s) to lock"
32134 	},
32135 
32136 	// Karajorma
32137 	{ OP_UNLOCK_SECONDARY_WEAPON, "unlock-secondary-weapon\r\n"
32138 		"\tUnlocks the secondary banks for the specified ship(s)\r\n"
32139 		"\tTakes 1 or more arguments\r\n"
32140 		"\t(all): Name(s) of ship(s) to lock"
32141 	},
32142 
32143 	// KeldorKatarn
32144 	{ OP_LOCK_AFTERBURNER, "lock-afterburner\r\n"
32145 		"\tLocks the afterburners on the specified ship(s)\r\n"
32146 		"\tTakes 1 or more arguments\r\n"
32147 		"\t(all): Name(s) of ship(s) to lock"
32148 	},
32149 
32150 	// KeldorKatarn
32151 	{ OP_UNLOCK_AFTERBURNER, "unlock-afterburner\r\n"
32152 		"\tUnlocks the afterburners on the specified ship(s)\r\n"
32153 		"\tTakes 1 or more arguments\r\n"
32154 		"\t(all): Name(s) of ship(s) to lock"
32155 	},
32156 
32157 	// Karajorma
32158 	{ OP_SET_AFTERBURNER_ENERGY, "set-afterburner-energy\r\n"
32159 		"\tSets the afterburner energy for the specified ship(s)\r\n"
32160 		"\tTakes 2 or more arguments\r\n"
32161 		"\t1: percentage of maximum afterburner energy.\r\n"
32162 		"\t(rest): Name(s) of ship(s)"
32163 	},
32164 
32165 	{ OP_SET_WEAPON_ENERGY, "set-weapon-energy\r\n"
32166 		"\tSets the weapon energy for the specified ship(s)\r\n"
32167 		"\tTakes 2 or more arguments\r\n"
32168 		"\t1: percentage of maximum weapon energy.\r\n"
32169 		"\t(rest): Name(s) of ship(s)"
32170 	},
32171 
32172 	{ OP_SET_SHIELD_ENERGY, "set-shield-energy\r\n"
32173 		"\tSets the shield energy for the specified ship(s)\r\n"
32174 		"\tTakes 2 or more arguments\r\n"
32175 		"\t1: percentage of maximum shield energy.\r\n"
32176 		"\t(rest): Name(s) of ship(s)"
32177 	},
32178 
32179 	{ OP_SET_AMBIENT_LIGHT, "set-ambient-light\r\n"
32180 		"\tSets the ambient light level for the mission\r\n"
32181 		"\tTakes 3 arguments\r\n"
32182 		"\t1: Red (0 - 255).\r\n"
32183 		"\t2: Green (0 - 255).\r\n"
32184 		"\t3: Blue (0 - 255)."
32185 	},
32186 
32187 	{ OP_SET_POST_EFFECT, "set-post-effect\r\n"
32188 		"\tConfigures post-processing effect\r\n"
32189 		"\tTakes 2 arguments\r\n"
32190 		"\t1: Effect type\r\n"
32191 		"\t2: Effect intensity (0 - 100)."
32192 	},
32193 
32194 	{ OP_CHANGE_SUBSYSTEM_NAME, "change-subsystem-name\r\n"
32195 		"\tChanges the name of the specified subsystem on the specified ship\r\n"
32196 		"\tTakes 3 or more arguments\r\n"
32197 		"\t1: Name of the ship.\r\n"
32198 		"\t2: New name for the subsystem (names larger than the maximum display size will be truncated\r\n"
32199 		"\t3: Name(s) of subsystem(s) to rename\r\n"
32200 	},
32201 
32202 	//phreak
32203 	{ OP_NUM_SHIPS_IN_BATTLE, "num-ships-in-battle\r\n"
32204 		"\tReturns the number of ships in battle or the number of ships in battle out of a list of teams, wings, and ships.  Takes 1 or more arguments...\r\n"
32205 		"\t(all):\tTeams, Wings, and Ships to query (optional)"
32206 	},
32207 
32208 	// Karajorma
32209 	{ OP_NUM_SHIPS_IN_WING, "num-ships-in-wing\r\n"
32210 		"\tReturns the number of ships in battle which belong to a given wing.  Takes 1 or more arguments...\r\n"
32211 		"\t(all):\tName of wing(s) to check"
32212 	},
32213 
32214 	// Goober5000
32215 	{ OP_HUD_DISABLE, "hud-disable\r\n"
32216 		"\tSets whether the hud is disabled.  Takes 1 argument...\r\n"
32217 		"\t1: Flag (1 to disable, 0 to re-enable)"
32218 	},
32219 
32220 	// Goober5000
32221 	{ OP_HUD_DISABLE_EXCEPT_MESSAGES, "hud-disable-except-messages\r\n"
32222 		"\tSets whether the hud (except for messages) is disabled.  Takes 1 argument...\r\n"
32223 		"\t1: Flag (1 to disable, 0 to re-enable)"
32224 	},
32225 
32226 	// Goober5000
32227 	{ OP_HUD_SET_MAX_TARGETING_RANGE, "hud-set-max-targeting-range\r\n"
32228 		"\tSets the farthest distance at which an object can be targeted.  Takes 1 argument...\r\n"
32229 		"\1: Maximum targeting distance (0 for infinite)\r\n"
32230 	},
32231 
32232 	{ OP_HUD_DISPLAY_GAUGE, "hud-display-gauge <milliseconds> <gauge>\r\n"
32233 		"\tCauses specified hud gauge to appear or disappear for so many milliseconds.  Takes 1 argument...\r\n"
32234 		"\t1: Number of milliseconds that the warpout gauge should appear on the HUD."
32235 		" 0 will immediately cause the gauge to disappear.\r\n"
32236 		"\t2: Name of HUD element.  Must be one of:\r\n"
32237 		"\t\t" SEXP_HUD_GAUGE_WARPOUT " - the \"Subspace drive active\" box that appears above the viewscreen.\r\n"
32238 	},
32239 
32240 	// Goober5000
32241 	{ OP_PLAYER_USE_AI, "player-use-ai\r\n"
32242 		"\tCauses the player's ship to be controlled by the FreeSpace AI.  Takes 0 arguments.\r\n"
32243 	},
32244 
32245 	// Goober5000
32246 	{ OP_PLAYER_NOT_USE_AI, "player-not-use-ai\r\n"
32247 		"\tCauses the player's ship to not be controlled by the FreeSpace AI.  Takes 0 arguments.\r\n"
32248 	},
32249 
32250 	// Karajorma
32251 	{ OP_ALLOW_TREASON, "allow-treason\r\n"
32252 		"\tTurns the Allow Traitor switch on or off in mission. Takes 0 arguments.\r\n"
32253 		"\t1:\tTrue/False."
32254 	},
32255 
32256 	// Karajorma
32257 	{ OP_SET_PLAYER_ORDERS, "set-player-orders\r\n"
32258 		"\tChanges the orders friendly AI will accept. Takes 3 or more arguments.\r\n"
32259 		"\t1:\tShip Name\r\n"
32260 		"\t2:\tTrue/False as to whether this order is allowed or not\r\n"
32261 		"\tRest:\tOrder"
32262 	},
32263 
32264 	//WMC
32265 	{ OP_HUD_SET_TEXT, "hud-set-text\r\n"
32266 		"\tSets the text value of a given HUD gauge. Works for custom and certain retail gauges. Takes 2 arguments...\r\n"
32267 		"\t1:\tHUD gauge to be modified\r\n"
32268 		"\t2:\tText to be set"
32269 	},
32270 
32271 	{ OP_HUD_SET_TEXT_NUM, "hud-set-text-num\r\n"
32272 		"\tSets the text value of a given HUD gauge to a number. Works for custom and certain retail gauges. Takes 2 arguments...\r\n"
32273 		"\t1:\tHUD gauge to be modified\r\n"
32274 		"\t2:\tNumber to be set"
32275 	},
32276 
32277 	{ OP_HUD_SET_MESSAGE, "hud-set-message\r\n"
32278 		"\tSets the text value of a given HUD gauge to a message from the mission's message list. Works for custom and certain retail gauges. Takes 2 arguments...\r\n"
32279 		"\t1:\tHUD gauge to be modified\r\n"
32280 		"\t2:\tMessage"
32281 	},
32282 
32283 	//WMC
32284 	{ OP_HUD_SET_COORDS, "hud-set-coord\r\n"
32285 		"\tSets the coordinates of a given HUD gauge. Works for custom and retail gauges. Takes 3 arguments...\r\n"
32286 		"\t1:\tHUD gauge to be modified\r\n"
32287 		"\t2:\tCoordinate X component\r\n"
32288 		"\t2:\tCoordinate Y component"
32289 	},
32290 
32291 	//WMC
32292 	{ OP_HUD_SET_FRAME, "hud-set-frame\r\n"
32293 		"\tSets the frame of a given HUD gauge's associated image. Works for custom and certain retail gauges. Takes 2 arguments...\r\n"
32294 		"\t1:\tHUD gauge to be modified\r\n"
32295 		"\t2:\tFrame number to be changed to"
32296 	},
32297 
32298 	//WMC
32299 	{ OP_HUD_SET_COLOR, "hud-set-color\r\n"
32300 		"\tSets the color of a given HUD gauge. Works only for custom gauges Takes 4 arguments...\r\n"
32301 		"\t1:\tHUD gauge to be modified\r\n"
32302 		"\t2:\tRed component (0-255)\r\n"
32303 		"\t3:\tGreen component (0-255)\r\n"
32304 		"\t4:\tBlue component (0-255)"
32305 	},
32306 
32307 	//WMC
32308 	{ OP_CURRENT_SPEED, "current-speed\r\n"
32309 		"\tReturns the speed of the given object. Takes 1 argument...\r\n"
32310 		"\t1:\tName of the object"
32311 	},
32312 
32313 	// Karajora
32314 	{ OP_PRIMARY_FIRED_SINCE, "primary-fired-since\r\n"
32315 		"\tReturns true if the primary weapon bank specified has been fired within the supplied window of time. Takes 3 arguments...\r\n\r\n"
32316 		"\t1:\tShip name\r\n"
32317 		"\t2:\tWeapon bank number\r\n"
32318 		"\t3:\tTime period to check if the weapon was fired (in millieconds)\r\n"
32319 	},
32320 
32321 	// Karajora
32322 	{ OP_SECONDARY_FIRED_SINCE, "secondary-fired-since\r\n"
32323 		"\tReturns true if the secondary weapon bank specified has been fired within the supplied window of time. Takes 3 arguments...\r\n\r\n"
32324 		"\t1:\tShip name\r\n"
32325 		"\t2:\tWeapon bank number\r\n"
32326 		"\t3:\tTime period to check if the weapon was fired (in millieconds)\r\n"
32327 	},
32328 
32329 	// Karajora
32330 	{ OP_HAS_PRIMARY_WEAPON, "has-primary-weapon\r\n"
32331 		"\tReturns true if the primary weapon bank specified has any of the weapons listed. Takes 3 or more arguments...\r\n\r\n"
32332 		"\t1:\tShip name\r\n"
32333 		"\t2:\tWeapon bank number\r\n"
32334 		"\tRest:\tWeapon name\r\n"
32335 	},
32336 
32337 	// Karajora
32338 	{ OP_HAS_SECONDARY_WEAPON, "has-secondary-weapon\r\n"
32339 		"\tReturns true if the secondary weapon bank specified has any of the weapons listed. Takes 3 or more arguments...\r\n\r\n"
32340 		"\t1:\tShip name\r\n"
32341 		"\t2:\tWeapon bank number\r\n"
32342 		"\tRest:\tWeapon name\r\n"
32343 	},
32344 
32345 	// Karajora
32346 	{ OP_DIRECTIVE_VALUE, "directive-value\r\n"
32347 		"\tCauses a value to appear in the directive count\r\n"
32348 		"\tAlways returns true. Takes 1 or more arguments...\r\n\r\n"
32349 		"\t1:\tValue\r\n"
32350 		"\t2:\t(Optional) Ignore the directive count set by any earlier SEXPs in the event. If set to false it will add instead\r\n"
32351 	},
32352 
32353 	//phreak
32354 	{ OP_SCRAMBLE_MESSAGES, "scramble-messages\r\n"
32355 		"\tCauses messages to be sent as if the player has sustained communications subsystem or EMP damage.  This effect can be reversed using unscramble-messages.  Takes zero or more arguments.\r\n"
32356 		"\tAll (Optional):\tName of the ship for which to scramble messages.  If no ships are specified, message scrambling will be turned on for all messages the player receives.\r\n"
32357 	},
32358 
32359 	//phreak
32360 	{ OP_UNSCRAMBLE_MESSAGES, "unscramble-messages\r\n"
32361 		"\tUndoes the effects of scramble-messages, causing messages to be sent clearly.  Takes zero or more arguments.\r\n"
32362 		"\tAll (Optional):\tName of the ship for which to scramble messages.  If no ships are specified, message scrambling will be turned on for all messages the player receives.\r\n"
32363 	},
32364 
32365 	{ OP_CUTSCENES_SET_CUTSCENE_BARS, "set-cutscene-bars\r\n"
32366 		"\tShows bars at the top and bottom of screen.  "
32367 		"Takes 0 or 1 arguments...\r\n"
32368 		"\t1:\tMilliseconds for bars to slide in\r\n"
32369 	},
32370 
32371 	{ OP_CUTSCENES_UNSET_CUTSCENE_BARS, "unset-cutscene-bars\r\n"
32372 		"\tRemoves cutscene bars.  "
32373 		"Takes 0 or 1 arguments...\r\n"
32374 		"\t1:\tMilliseconds for bars to slide out\r\n"
32375 	},
32376 
32377 	{ OP_CUTSCENES_FADE_IN, "fade-in\r\n"
32378 		"\tFades in.  "
32379 		"Takes 0 to 4 arguments...\r\n"
32380 		"\t1:\tTime to fade in (in milliseconds)\r\n"
32381 		"\t2:\tColor to fade to (optional).  If arguments 3 and 4 are specified, this is the R component of an RGB color.  Otherwise it is 1 for white, 2 for red, and any other number for black.\r\n"
32382 		"\t3:\tG component of an RGB color (optional)\r\n"
32383 		"\t4:\tB component of an RGB color (optional)\r\n"
32384 	},
32385 
32386 	{ OP_CUTSCENES_FADE_OUT, "fade-out\r\n"
32387 		"\tFades out.  "
32388 		"Takes 0 to 4 arguments...\r\n"
32389 		"\t1:\tTime to fade in (in milliseconds)\r\n"
32390 		"\t2:\tColor to fade to (optional).  If arguments 3 and 4 are specified, this is the R component of an RGB color.  Otherwise it is 1 for white, 2 for red, and any other number for black.\r\n"
32391 		"\t3:\tG component of an RGB color (optional)\r\n"
32392 		"\t4:\tB component of an RGB color (optional)\r\n"
32393 	},
32394 
32395 	{ OP_CUTSCENES_SET_CAMERA, "set-camera\r\n"
32396 		"\tSets SEXP camera, or another specified cutscene camera.  "
32397 		"Takes 0 to 1 arguments...\r\n"
32398 		"\t(optional)\r\n"
32399 		"\t1:\tCamera name (created if nonexistent)\r\n"
32400 	},
32401 
32402 	{ OP_CUTSCENES_SET_CAMERA_FACING, "set-camera-facing\r\n"
32403 		"\tMakes the camera face the given point.  "
32404 		"Takes 3 to 6 arguments...\r\n"
32405 		"\t1:\tX position to face\r\n"
32406 		"\t2:\tY position to face\r\n"
32407 		"\t3:\tZ position to face\r\n"
32408 		"\t(optional)\r\n"
32409 		"\t4:\tTotal turn time (milliseconds)\r\n"
32410 		"\t5:\tTime to spend accelerating/decelerating (milliseconds)\r\n"
32411 		"\t6:\tTime to spend decelerating (milliseconds)\r\n"
32412 	},
32413 
32414 	{ OP_CUTSCENES_SET_CAMERA_FACING_OBJECT, "set-camera-facing-object\r\n"
32415 		"\tMakes the camera face the given object.  "
32416 		"Takes 1 to 4 arguments...\r\n"
32417 		"\t1:\tObject to face\r\n"
32418 		"\t(optional)\r\n"
32419 		"\t2:\tTotal turn time (milliseconds)\r\n"
32420 		"\t3:\tTime to spend accelerating/decelerating (milliseconds)\r\n"
32421 		"\t4:\tTime to spend decelerating (milliseconds)\r\n"
32422 	},
32423 
32424 	{ OP_CUTSCENES_SET_CAMERA_FOV, "set-camera-fov\r\n"
32425 		"\tSets the camera field of view.  "
32426 		"Takes 1 to 4 arguments...\r\n"
32427 		"\t1:\tNew FOV (degrees)\r\n"
32428 		"\t(optional)\r\n"
32429 		"\t2:\tTotal zoom time (milliseconds)\r\n"
32430 		"\t3:\tTime to spend accelerating/decelerating (milliseconds)\r\n"
32431 		"\t4:\tTime to spend decelerating (milliseconds)\r\n"
32432 	},
32433 
32434 	{ OP_CUTSCENES_SET_CAMERA_HOST, "set-camera-host\r\n"
32435 		"\tSets the object and subystem camera should view from. Camera position is offset from the host. "
32436 		"If the selected subsystem or one of its children has an eyepoint bound to it it will be used for the camera position and orientation."
32437 		"If the selected subsystem is a turret and has no eyepoint the camera will be at the first firing point and look along the firing direction."
32438 		"If a valid camera target is set the direction to the target will override any other orientation."
32439 		"Takes 1 to 2 arguments...\r\n"
32440 		"\t1:\tShip to mount camera on\r\n"
32441 		"\t(optional)\r\n"
32442 		"\t2:\tSubystem to mount camera on\r\n"
32443 	},
32444 
32445 	{ OP_CUTSCENES_SET_CAMERA_POSITION, "set-camera-position\r\n"
32446 		"\tSets the camera position to a spot in mission space.  "
32447 		"Takes 3 to 6 arguments...\r\n"
32448 		"\t1:\tX position\r\n"
32449 		"\t2:\tY position\r\n"
32450 		"\t3:\tZ position\r\n"
32451 		"\t(optional)\r\n"
32452 		"\t4:\tTotal turn time (milliseconds)\r\n"
32453 		"\t5:\tTime to spend accelerating/decelerating (milliseconds)\r\n"
32454 		"\t6:\tTime to spend decelerating (milliseconds)\r\n"
32455 	},
32456 
32457 	{ OP_CUTSCENES_SET_CAMERA_ROTATION, "set-camera-rotation\r\n"
32458 		"\tSets the camera rotation.  "
32459 		"Takes 3 to 6 arguments...\r\n"
32460 		"\t1:\tPitch (degrees)\r\n"
32461 		"\t2:\tBank (degrees)\r\n"
32462 		"\t3:\tHeading (degrees)\r\n"
32463 		"\t(optional)\r\n"
32464 		"\t4:\tTotal turn time (milliseconds)\r\n"
32465 		"\t5:\tTime to spend accelerating/decelerating (milliseconds)\r\n"
32466 		"\t6:\tTime to spend decelerating (milliseconds)\r\n"
32467 	},
32468 
32469 	{ OP_CUTSCENES_SET_CAMERA_TARGET, "set-camera-target\r\n"
32470 		"\tSets the object and subystem camera should track. Camera orientation is offset from the target.  "
32471 		"Takes 1 to 2 arguments...\r\n"
32472 		"\t1:\tShip to track\r\n"
32473 		"\t(optional)\r\n"
32474 		"\t2:\tSubystem to track\r\n"
32475 	},
32476 
32477 	{ OP_CUTSCENES_SET_FOV, "set-fov\r\n"
32478 		"\tSets the field of view - overrides all camera settings.  "
32479 		"Takes 1 argument...\r\n"
32480 		"\t1:\tNew FOV (degrees)\r\n"
32481 	},
32482 
32483 	// Echelon9
32484 	{ OP_CUTSCENES_GET_FOV, "get-fov\r\n"
32485 		"\tReturns the current field of view (in degrees) from the Main camera.\r\n\r\n"
32486 		"Returns a numeric value.  Takes no arguments."
32487 	},
32488 
32489 	{ OP_CUTSCENES_RESET_FOV, "reset-fov\r\n"
32490 		"\tResets the field of view.  "
32491 	},
32492 
32493 	{ OP_CUTSCENES_RESET_CAMERA, "reset-camera\r\n"
32494 		"\tReleases cutscene camera control.  "
32495 		"Takes 1 optional argument...\r\n"
32496 		"\t(optional)\r\n"
32497 		"\t1:\tReset camera data (Position, facing, FOV...) (default: false)"
32498 	},
32499 
32500 	{ OP_CUTSCENES_SHOW_SUBTITLE, "show-subtitle (deprecated)\r\n"
32501 		"\tDisplays a subtitle, either an image or a string of text.  As this operator tries to combine two functions into one and does not adjust coordinates for screen formats, it has been deprecated.\r\n"
32502 		"Takes 4 to 13 arguments...\r\n"
32503 		"\t1:\tX position (negative value to be from right of screen)\r\n"
32504 		"\t2:\tY position (negative value to be from bottom of screen)\r\n"
32505 		"\t3:\tText to display\r\n"
32506 		"\t4:\tTime to be displayed, not including fadein/out\r\n"
32507 		"\t5:\tImage name (optional)\r\n"
32508 		"\t6:\tFade in time (optional)\r\n"
32509 		"\t7:\tCenter horizontally? (optional)\r\n"
32510 		"\t8:\tCenter vertically? (optional)\r\n"
32511 		"\t9:\tWidth (optional)\r\n"
32512 		"\t10:\tText red component (0-255) (optional)\r\n"
32513 		"\t11:\tText green component (0-255) (optional)\r\n"
32514 		"\t12:\tText blue component (0-255) (optional)\r\n"
32515 		"\t13:\tDrawn after shading? (optional)"
32516 	},
32517 
32518 	{ OP_CUTSCENES_SHOW_SUBTITLE_TEXT, "show-subtitle-text\r\n"
32519 		"\tDisplays a subtitle in the form of text.  Note that because of the constraints of the subtitle system, textual subtitles are currently limited to 255 characters or fewer.\r\n"
32520 		"Takes 6 to 13 arguments...\r\n"
32521 		"\t1:\tText to display, or the name of a message containing text\r\n"
32522 		"\t2:\tX position, from 0 to 100% (positive measures from the left; negative measures from the right)\r\n"
32523 		"\t3:\tY position, from 0 to 100% (positive measures from the top; negative measures from the bottom)\r\n"
32524 		"\t4:\tCenter horizontally? (if true, overrides argument #2)\r\n"
32525 		"\t5:\tCenter vertically? (if true, overrides argument #3)\r\n"
32526 		"\t6:\tTime (in milliseconds) to be displayed, not including fade-in/fade-out\r\n"
32527 		"\t7:\tFade time (in milliseconds) to be used for both fade-in and fade-out (optional)\r\n"
32528 		"\t8:\tParagraph width, from 1 to 100% (optional; 0 uses default 200 pixels)\r\n"
32529 		"\t9:\tText red component (0-255) (optional)\r\n"
32530 		"\t10:\tText green component (0-255) (optional)\r\n"
32531 		"\t11:\tText blue component (0-255) (optional)\r\n"
32532 		"\t12:\tText font (optional)\r\n"
32533 		"\t13:\tDrawn after shading? (optional)"
32534 	},
32535 
32536 	{ OP_CUTSCENES_SHOW_SUBTITLE_IMAGE, "show-subtitle-image\r\n"
32537 		"\tDisplays a subtitle in the form of an image.\r\n"
32538 		"Takes 8 to 10 arguments...\r\n"
32539 		"\t1:\tImage to display\r\n"
32540 		"\t2:\tX position, from 0 to 100% (positive measures from the left; negative measures from the right)\r\n"
32541 		"\t3:\tY position, from 0 to 100% (positive measures from the top; negative measures from the bottom)\r\n"
32542 		"\t4:\tCenter horizontally? (if true, overrides argument #2)\r\n"
32543 		"\t5:\tCenter vertically? (if true, overrides argument #3)\r\n"
32544 		"\t6:\tImage width, from 1 to 100% (0 uses original width)\r\n"
32545 		"\t7:\tImage height, from 1 to 100% (0 uses original height)\r\n"
32546 		"\t8:\tTime (in milliseconds) to be displayed, not including fade-in/fade-out\r\n"
32547 		"\t9:\tFade time (in milliseconds) to be used for both fade-in and fade-out (optional)\r\n"
32548 		"\t10:\tDrawn after shading? (optional)"
32549 	},
32550 
32551 	{ OP_CUTSCENES_SET_TIME_COMPRESSION, "set-time-compression\r\n"
32552 		"\tSets the time compression and prevents it from being changed by the user.  "
32553 		"Takes 1 to 3 arguments...\r\n"
32554 		"\t1:\tNew time compression (% of 1x)\r\n"
32555 		"\t2:\tTime in ms for change to take\r\n"
32556 		"\t3:\tTime compression to start from\r\n"
32557 	},
32558 
32559 	{ OP_CUTSCENES_RESET_TIME_COMPRESSION, "reset-time-compression\r\n"
32560 		"\tResets the time compression - that is to say, time compression is set back to 1x, "
32561 		"and the time compression controls are unlocked.  Always call this when done with set-time-compression.  "
32562 	},
32563 
32564 	{ OP_CUTSCENES_FORCE_PERSPECTIVE, "lock-perspective\r\n"
32565 		"\tPrevents or allows the player from changing the view mode.  "
32566 		"Takes 1 or 2 arguments...\r\n"
32567 		"\t1:\tTrue to lock the view mode, false to unlock it\r\n"
32568 		"\t2:\tWhat view mode to lock; 0 for first-person, 1 for chase, 2 for external, 3 for top-down"
32569 	},
32570 
32571 	{ OP_SET_CAMERA_SHUDDER, "set-camera-shudder\r\n"
32572 		"\tCauses the camera to shudder.  Currently this will only work if the camera is showing the player's viewpoint (i.e. the HUD).\r\n\r\n"
32573 		"Takes 2 arguments...\r\n"
32574 		"\t1: Time (in milliseconds)\r\n"
32575 		"\t2: Intensity.  For comparison, the Maxim has an intensity of 1440."
32576 	},
32577 
32578 	{ OP_JUMP_NODE_SET_JUMPNODE_NAME, "set-jumpnode-name\r\n"
32579 		"\tSets the name of a jump node. Takes 2 arguments...\r\n"
32580 		"\t1: Name of jump node to change name for\r\n"
32581 		"\t2: New name for jump node\r\n\r\n"
32582 		"\tNote: SEXPs referencing the old name will not work after the name change.\r\n"
32583 	},
32584 
32585 	{ OP_JUMP_NODE_SET_JUMPNODE_COLOR, "set-jumpnode-color\r\n"
32586 		"\tSets the color of a jump node.  "
32587 		"Takes 5 arguments...\r\n"
32588 		"\t1:\tJump node to change color for\r\n"
32589 		"\t2:\tRed value\r\n"
32590 		"\t3:\tGreen value\r\n"
32591 		"\t4:\tBlue value\r\n"
32592 		"\t5:\tAlpha value\r\n"
32593 	},
32594 
32595 	{ OP_JUMP_NODE_SET_JUMPNODE_MODEL, "set-jumpnode-model\r\n"
32596 		"\tSets the model of a jump node.  "
32597 		"Takes 3 arguments...\r\n"
32598 		"\t1:\tJump node to change model for\r\n"
32599 		"\t2:\tModel filename\r\n"
32600 		"\t3:\tShow as normal model. When this is true, the jumpnode will be rendered like a normal model.\r\n"
32601 	},
32602 
32603 	{ OP_JUMP_NODE_SHOW_JUMPNODE, "show-jumpnode\r\n"
32604 		"\tSets a jump node to display on the screen.\r\n"
32605 		"\tAny:\tJump node to show\r\n"
32606 	},
32607 
32608 	{ OP_JUMP_NODE_HIDE_JUMPNODE, "hide-jumpnode\r\n"
32609 		"\tSets a jump node to not display on the screen.\r\n"
32610 		"\tAny:\tJump node to hide\r\n"
32611 	},
32612 
32613 	// taylor
32614 	{ OP_SET_SKYBOX_MODEL, "set-skybox-model\r\n"
32615 		"\tSets the current skybox model.  Takes 1 argument...\r\n"
32616 		"\t1:\tModel filename (with .pof extension) to switch to\r\n\r\n"
32617 		"Note: If the model filename is set to \"default\" with no extension then it will switch to the mission supplied default skybox."
32618 	},
32619 
32620 	// Goober5000
32621 	{ OP_SET_SKYBOX_ORIENT, "set-skybox-orientation\r\n"
32622 		"\tSets the current skybox orientation.  Takes 3 arguments...\r\n"
32623 		"\t1:\tPitch\r\n"
32624 		"\t2:\tBank\r\n"
32625 		"\t3:\tHeading\r\n"
32626 	},
32627 
32628 	{ OP_ADD_BACKGROUND_BITMAP, "add-background-bitmap\r\n"
32629 		"\tAdds a background bitmap to the sky.  Returns an integer that is stored in a variable so it can be deleted using remove-background-bitmap\r\n\r\n"
32630 		"Takes 9 arguments...\r\n"
32631 		"\t1:\tBackground bitmap name\r\n"
32632 		"\t2:\tPitch\r\n"
32633 		"\t3:\tBank\r\n"
32634 		"\t4:\tHeading\r\n"
32635 		"\t5:\tX scale (expressed as a percentage of the original size of the bitmap)\r\n"
32636 		"\t6:\tY scale (expressed as a percentage of the original size of the bitmap)\r\n"
32637 		"\t7:\tX divisions.\r\n"
32638 		"\t8:\tY divisions.\r\n"
32639 		"\t9:\tVariable in which to store result\r\n"
32640 	},
32641 
32642 	{ OP_REMOVE_BACKGROUND_BITMAP, "remove-background-bitmap\r\n"
32643 		"\tRemoves the nth background bitmap from the mission\r\n\r\n"
32644 		"Takes 1 argument...\r\n"
32645 		"\t1:\tZero based bitmap index from the \'Bitmap\' box in the background editor\r\n"
32646 		"\t\t\tYou can also use the result of a previous call to add-background-bitmap to remove that added bitmap\r\n"
32647 	},
32648 
32649 	{ OP_ADD_SUN_BITMAP, "add-sun-bitmap\r\n"
32650 		"\tAdds a sun bitmap to the sky.  Returns an integer that is stored in a variable so it can be deleted using remove-sun-bitmap\r\n\r\n"
32651 		"Takes 6 arguments...\r\n"
32652 		"\t1:\tSun bitmap name\r\n"
32653 		"\t2:\tPitch\r\n"
32654 		"\t3:\tBank\r\n"
32655 		"\t4:\tHeading\r\n"
32656 		"\t5:\tScale (expressed as a percentage of the original size of the bitmap)\r\n"
32657 		"\t6:\tVariable in which to store result\r\n"
32658 	},
32659 
32660 	{ OP_REMOVE_SUN_BITMAP, "remove-sun-bitmap\r\n"
32661 		"\tRemoves the nth sun from the mission\r\n\r\n"
32662 		"Takes 1 argument...\r\n"
32663 		"\t1:\tZero based sun index from the \'Suns\' box in the background editor\r\n"
32664 		"\t\t\tYou can also use the result of a previous call to add-sun-bitmap to remove that added sun\r\n"
32665 	},
32666 
32667 	{ OP_NEBULA_CHANGE_STORM, "nebula-change-storm\r\n"
32668 		"\tChanges the current nebula storm\r\n\r\n"
32669 		"Takes 1 argument...\r\n"
32670 		"\t1:\tNebula storm to change to\r\n"
32671 	},
32672 
32673 	{ OP_NEBULA_TOGGLE_POOF, "nebula-toggle-poof\r\n"
32674 		"\tToggles the state of a nebula poof\r\n\r\n"
32675 		"Takes 2 arguments...\r\n"
32676 		"\t1:\tName of nebula poof to toggle\r\n"
32677 		"\t2:\tA True boolean expression will toggle this poof on.  A false one will do the opposite."
32678 	},
32679 
32680 	{ OP_NEBULA_CHANGE_PATTERN, "nebula-change-pattern\r\n"
32681 	"\tChanges the current nebula background pattern (as defined in nebula.tbl)\r\n\r\n"
32682 		"Takes 1 argument...\r\n"
32683 		"\t1:\tNebula background pattern to change to\r\n"
32684 	},
32685 
32686 	{OP_SCRIPT_EVAL_NUM, "script-eval-num\r\n"
32687 		"\tEvaluates script to return a number"
32688 		"Takes 1 argument...\r\n"
32689 		"\t1:\tScript\r\n"
32690 	},
32691 
32692 	{ OP_DISABLE_ETS, "disable-ets\r\n"
32693 		"\tSwitches a ships' ETS system off\r\n\r\n"
32694 		"Takes at least 1 argument...\r\n"
32695 		"\tAll:\tList of ships this sexp applies to\r\n"
32696 	},
32697 
32698 	{ OP_ENABLE_ETS, "enable-ets\r\n"
32699 		"\tSwitches a ships' ETS system on\r\n\r\n"
32700 		"Takes at least 1 argument...\r\n"
32701 		"\tAll:\tList of ships this sexp applies to\r\n"
32702 	},
32703 
32704 	{OP_SCRIPT_EVAL_STRING, "script-eval-string\r\n"
32705 		"\tEvaluates script to return a string\r\n\r\n"
32706 		"Takes 1 argument...\r\n"
32707 		"\t1:\tScript\r\n"
32708 	},
32709 
32710 	{OP_SCRIPT_EVAL, "script-eval\r\n"
32711 		"\tEvaluates script\r\n\r\n"
32712 		"Takes at least 1 argument...\r\n"
32713 		"\t1:\tScript to evaluate\r\n"
32714 	},
32715 
32716 	{OP_SCRIPT_EVAL_MULTI, "multi-eval\r\n"
32717 		"\tEvaluates script\r\n\r\n"
32718 		"Takes at least 2 arguments...\r\n"
32719 		"\t1:\tScript to evaluate\r\n"
32720 		"\t2:\tTrue/False - Should the script evaluate on the server?\r\n"
32721 		"\t(rest):\tList of players who should evaluate this script. If no player is given, all clients will execute the script\r\n"
32722 	},
32723 
32724 	{OP_FORCE_GLIDE, "force-glide\r\n"
32725 		"\tForces a given ship into glide mode, provided it is capable of gliding. Note that the player will not be able to leave glide mode on his own,, and that a ship in glide mode cannot warp out or enter autopilot."
32726 		"Takes 2 Arguments...\r\n"
32727 		"\t1:\tShip to force\r\n"
32728 		"\t2:\tTrue to activate glide, False to deactivate\r\n"
32729 	},
32730 
32731 	{OP_HUD_SET_DIRECTIVE, "hud-set-directive\r\n"
32732 		"\tSets the text of a given custom hud gauge to the provided text."
32733 		"Takes 2 Arguments...\r\n"
32734 		"\t1:\tHUD Gauge name\r\n"
32735 		"\t2:\tText that will be displayed. This text will be treated as directive text, meaning that references to mapped keys will be replaced with the user's preferences.\r\n"
32736 	},
32737 
32738 	{OP_HUD_GAUGE_SET_ACTIVE, "hud-gauge-set-active (deprecated)\r\n"
32739 		"\tActivates or deactivates a given custom gauge."
32740 		"Takes 2 Arguments...\r\n"
32741 		"\t1:\tHUD Gauge name\r\n"
32742 		"\t2:\tBoolean, whether or not to display this gauge\r\n"
32743 	},
32744 
32745 	{OP_HUD_CLEAR_MESSAGES, "hud-clear-messages\r\n"
32746 		"\tClears active messages displayed on the HUD."
32747 		"Takes no arguments\r\n"
32748 	},
32749 
32750 	{OP_HUD_ACTIVATE_GAUGE_TYPE, "hud-activate-gauge-type (deprecated)\r\n"
32751 		"\tActivates or deactivates all hud gauges of a given type."
32752 		"Takes 2 Arguments...\r\n"
32753 		"\t1:\tGauge Type\r\n"
32754 		"\t2:\tBoolean, whether or not to display this gauge\r\n"
32755 	},
32756 
32757 	{OP_HUD_SET_CUSTOM_GAUGE_ACTIVE, "hud-set-custom-gauge-active\r\n"
32758 		"\tActivates or deactivates a custom hud gauge defined in hud_gauges.tbl."
32759 		"Takes 2 Arguments...\r\n"
32760 		"\t1:\tBoolean, whether or not to display this gauge\r\n"
32761 		"\tRest:\tHUD Gauge name\r\n"
32762 	},
32763 
32764 	{OP_HUD_SET_RETAIL_GAUGE_ACTIVE, "hud-set-custom-gauge-active\r\n"
32765 		"\tActivates or deactivates a retail hud gauge grouping."
32766 		"Takes 2 Arguments...\r\n"
32767 		"\t1:\tBoolean, whether or not to display this gauge\r\n"
32768 		"\tRest:\tHUD Gauge Group name\r\n"
32769 	},
32770 
32771 	{OP_ADD_TO_COLGROUP, "add-to-collision-group\r\n"
32772 		"\tAdds a ship to the specified collision group(s). Note that there are 32 collision groups,\r"
32773 		"\tand that an object may be in several collision groups at the same time\r\n"
32774 		"Takes 2 or more Arguments...\r\n"
32775 		"\t1:\tObject to add\r\n"
32776 		"\t2+:\tGroup IDs. Valid IDs are 0 through 31 inclusive.\r\n"
32777 	},
32778 
32779 	{OP_REMOVE_FROM_COLGROUP, "remove-from-collision-group\r\n"
32780 		"\tRemoves a ship from the specified collision group(s). Note that there are 32 collision groups,\n"
32781 		"\tand that an object may be in several collision groups at the same time\r\n"
32782 		"Takes 2 or more Arguments...\r\n"
32783 		"\t1:\tObject to add\r\n"
32784 		"\t2+:\tGroup IDs. Valid IDs are 0 through 31 inclusive.\r\n"
32785 	},
32786 
32787 	{OP_GET_COLGROUP_ID, "get-collision-group\r\n"
32788 		"\tReturns an objects' collision group ID. Note that this ID is a bitfield.\r\n"
32789 		"Takes 1 Argument...\r\n"
32790 		"\t1:\tObject name\r\n"
32791 	},
32792 
32793 	//Valathil
32794 	{OP_SHIP_EFFECT, "ship-effect\r\n"
32795 		"\tPlays an animated shader effect on the ship(s) or wing(s).\r\n"
32796 		"Takes 3 or more arguments...\r\n"
32797 		"\t1:\tEffect name (as defined in post_processing.tbl)\r\n"
32798 		"\t2:\tHow long the effect should take in milliseconds\r\n"
32799 		"\tRest:\tShip or wing name\r\n"
32800 	},
32801 
32802 	{OP_CLEAR_SUBTITLES, "clear-subtitles\r\n"
32803 		"\tClears the subtitle queue completely.\r\n"
32804 	},
32805 
32806 	{OP_SET_THRUSTERS, "set-thrusters-status\r\n"
32807 		"\tManipulates the thrusters on a ship.\r\n"
32808 		"Takes 2 or more arguments...\r\n"
32809 		"\t1:\tBoolean, true sets thrusters to visible, false deactivates them.\r\n"
32810 		"\t2:\tRest: List of ships this sexp will work on.\r\n"
32811 	},
32812 
32813 	{OP_SET_PLAYER_THROTTLE_SPEED, "set-player-throttle-speed\r\n"
32814 		"\tSets a player's throttle to a percentage of their maximum speed.\r\n"
32815 		"\tThis SEXP has no effect if used on an AI ship, however it will still work on an AI-controlled player ship.\r\n"
32816 		"Takes 2 arguments...\r\n"
32817 		"\t1:\tThe player ship to set the throttle of.\r\n"
32818 		"\t2:\tThe percentage of the player's maximum speed to set their throttle to.\r\n"
32819 		"\t\tThis is capped to either 0 or 100 if outside the valid range."
32820 	},
32821 
32822 	{OP_CHANGE_TEAM_COLOR, "change-team-color\r\n"
32823 		"\tChanges the team color setting for one or several ships.\r\n"
32824 		"\tThis sexp has no effect on ships that don't have team colors enabled for them.\r\n"
32825 		"\tTakes 3 or more arguments...\r\n"
32826 		"\t1:\tThe new team color name. Name must be defined in colors.tbl.\r\n"
32827 		"\t2:\tCrossfade time in milliseconds. During this time, colors will be mixed.\r\n"
32828 		"\t3:\tRest: List of ships this sexp will operate on."
32829 	},
32830 
32831 	{OP_CALL_SSM_STRIKE, "call-ssm-strike\r\n"
32832 		"\tCalls a subspace missile strike on the specified ship.\r\n"
32833 		"\tRequires a ssm table (ssm.tbl).\r\n"
32834 		"Takes 3 arguments...\r\n"
32835 		"\t1:\tStrike index.\r\n"
32836 		"\t2:\tCalling team.\r\n"
32837 		"\tRest:\tList of ships the strike will be called on."
32838 	},
32839 
32840 	{OP_PLAYER_IS_CHEATING_BASTARD, "player-is-cheating\r\n"
32841 		"\tReturns true if the player is or has been cheating in this mission.\r\n"
32842 	},
32843 
32844 	{ OP_SET_MOTION_DEBRIS, "set-motion-debris-override\r\n"
32845 		"\tControls whether or not motion debris should be active.\r\n"
32846 		"\tThis overrides any choice made by the user through the -nomotiondebris commandline flag."
32847 		"Takes 1 argument...\r\n"
32848 		"\t1:\tBoolean: True will disable motion debris, False reenable it.\r\n"
32849 	}
32850 };
32851 
32852 
32853 
32854 op_menu_struct op_menu[] =
32855 {
32856 	{ "Objectives",		OP_CATEGORY_OBJECTIVE },
32857 	{ "Time",			OP_CATEGORY_TIME },
32858 	{ "Logical",		OP_CATEGORY_LOGICAL },
32859 	{ "Arithmetic",		OP_CATEGORY_ARITHMETIC },
32860 	{ "Status",			OP_CATEGORY_STATUS },
32861 	{ "Change",			OP_CATEGORY_CHANGE },
32862 	{ "Conditionals",	OP_CATEGORY_CONDITIONAL },
32863 	{ "Ai goals",		OP_CATEGORY_AI },
32864 	{ "Event/Goals",	OP_CATEGORY_GOAL_EVENT },
32865 	{ "Training",		OP_CATEGORY_TRAINING },
32866 };
32867 
32868 // Goober5000's subcategorization of the Change menu (and possibly other menus in the future,
32869 // if people so choose - see sexp.h)
32870 op_menu_struct op_submenu[] =
32871 {
32872 	{	"Messages and Personas",		CHANGE_SUBCATEGORY_MESSAGING						},
32873 	{	"AI Control",					CHANGE_SUBCATEGORY_AI_CONTROL						},
32874 	{	"Ship Status",					CHANGE_SUBCATEGORY_SHIP_STATUS						},
32875 	{	"Weapons, Shields, and Engines",	CHANGE_SUBCATEGORY_SHIELDS_ENGINES_AND_WEAPONS		},
32876 	{	"Subsystems and Health",		CHANGE_SUBCATEGORY_SUBSYSTEMS						},
32877 	{	"Cargo",						CHANGE_SUBCATEGORY_CARGO							},
32878 	{	"Armor and Damage Types",		CHANGE_SUBCATEGORY_ARMOR_AND_DAMAGE_TYPES			},
32879 	{	"Beams and Turrets",			CHANGE_SUBCATEGORY_BEAMS_AND_TURRETS				},
32880 	{	"Models and Textures",			CHANGE_SUBCATEGORY_MODELS_AND_TEXTURES				},
32881 	{	"Coordinate Manipulation",		CHANGE_SUBCATEGORY_COORDINATE_MANIPULATION			},
32882 	{	"Mission and Campaign",			CHANGE_SUBCATEGORY_MISSION_AND_CAMPAIGN				},
32883 	{	"Music and Sound",				CHANGE_SUBCATEGORY_MUSIC_AND_SOUND					},
32884 	{	"HUD",							CHANGE_SUBCATEGORY_HUD								},
32885 	{	"Nav Points",					CHANGE_SUBCATEGORY_NAV								},
32886 	{	"Cutscenes",					CHANGE_SUBCATEGORY_CUTSCENES						},
32887 	{	"Backgrounds and Nebulae",		CHANGE_SUBCATEGORY_BACKGROUND_AND_NEBULA			},
32888 	{	"Jump Nodes",					CHANGE_SUBCATEGORY_JUMP_NODES						},
32889 	{	"Special Effects",				CHANGE_SUBCATEGORY_SPECIAL_EFFECTS					},
32890 	{	"Variables",					CHANGE_SUBCATEGORY_VARIABLES						},
32891 	{	"Other",						CHANGE_SUBCATEGORY_OTHER							},
32892 	{	"Mission",						STATUS_SUBCATEGORY_MISSION							},
32893 	{	"Player",						STATUS_SUBCATEGORY_PLAYER							},
32894 	{	"Multiplayer",						STATUS_SUBCATEGORY_MULTIPLAYER					},
32895 	{	"Ship Status",					STATUS_SUBCATEGORY_SHIP_STATUS						},
32896 	{	"Weapons, Shields, and Engines",	STATUS_SUBCATEGORY_SHIELDS_ENGINES_AND_WEAPONS	},
32897 	{	"Cargo",						STATUS_SUBCATEGORY_CARGO							},
32898 	{	"Damage",						STATUS_SUBCATEGORY_DAMAGE							},
32899 	{	"Distance and Coordinates",		STATUS_SUBCATEGORY_DISTANCE_AND_COORDINATES			},
32900 	{	"Variables",					STATUS_SUBCATEGORY_VARIABLES						},
32901 	{	"Other",						STATUS_SUBCATEGORY_OTHER							},
32902 
32903 
32904 };
32905 int Num_sexp_help = sizeof(Sexp_help) / sizeof(sexp_help_struct);
32906 int Num_op_menus = sizeof(op_menu) / sizeof(op_menu_struct);
32907 int Num_submenus = sizeof(op_submenu) / sizeof(op_menu_struct);
32908 
32909 /**
32910  * Internal file used by output_sexps, should not be called from output_sexps
32911  */
output_sexp_html(int sexp_idx,FILE * fp)32912 static void output_sexp_html(int sexp_idx, FILE *fp)
32913 {
32914 	if(sexp_idx < 0 || sexp_idx > Num_operators)
32915 		return;
32916 
32917 	bool printed=false;
32918 
32919 	for(int i = 0; i < Num_sexp_help; i++)
32920 	{
32921 		if(Sexp_help[i].id == Operators[sexp_idx].value)
32922 		{
32923 			char* new_buf = new char[2*strlen(Sexp_help[i].help)];
32924 			char* dest_ptr = new_buf;
32925 			char* curr_ptr = Sexp_help[i].help;
32926 			char* end_ptr = curr_ptr + strlen(Sexp_help[i].help);
32927 			while(curr_ptr < end_ptr)
32928 			{
32929 				if(*curr_ptr == '\n')
32930 				{
32931 					strcpy(dest_ptr, "\n<br>");
32932 					dest_ptr+=5;
32933 				}
32934 				else
32935 				{
32936 					*dest_ptr++ = *curr_ptr;
32937 				}
32938 				curr_ptr++;
32939 			}
32940 			*dest_ptr = '\0';
32941 
32942 			fprintf(fp, "<dt><b>%s</b></dt>\n<dd>%s</dd>\n", Operators[sexp_idx].text, new_buf);
32943 			delete[] new_buf;
32944 
32945 			printed = true;
32946 		}
32947 	}
32948 
32949 	if(!printed)
32950 		fprintf(fp, "<dt><b>%s</b></dt>\n<dd>Min arguments: %d, Max arguments: %d</dd>\n", Operators[sexp_idx].text, Operators[sexp_idx].min, Operators[sexp_idx].max);
32951 }
32952 
32953 /**
32954  * Output sexp.html file
32955  */
output_sexps(char * filepath)32956 bool output_sexps(char *filepath)
32957 {
32958 	FILE *fp = fopen(filepath,"w");
32959 
32960 	if(fp == NULL)
32961 	{
32962 		MessageBox(NULL,"Error creating SEXP operator list", "Error", MB_OK);
32963 		return false;
32964 	}
32965 
32966 	//Header
32967 	if (FS_VERSION_BUILD == 0 && FS_VERSION_REVIS == 0) //-V547
32968 	{
32969 		fprintf(fp, "<html>\n<head>\n\t<title>SEXP Output - FSO v%i.%i</title>\n</head>\n", FS_VERSION_MAJOR, FS_VERSION_MINOR);
32970 		fputs("<body>", fp);
32971 		fprintf(fp,"\t<h1>SEXP Output - FSO v%i.%i</h1>\n", FS_VERSION_MAJOR, FS_VERSION_MINOR);
32972 	}
32973 	else if (FS_VERSION_REVIS == 0)
32974 	{
32975 		fprintf(fp, "<html>\n<head>\n\t<title>SEXP Output - FSO v%i.%i.%i</title>\n</head>\n", FS_VERSION_MAJOR, FS_VERSION_MINOR, FS_VERSION_BUILD);
32976 		fputs("<body>", fp);
32977 		fprintf(fp,"\t<h1>SEXP Output - FSO v%i.%i.%i</h1>\n", FS_VERSION_MAJOR, FS_VERSION_MINOR, FS_VERSION_BUILD);
32978 	}
32979 	else
32980 	{
32981 		fprintf(fp, "<html>\n<head>\n\t<title>SEXP Output - FSO v%i.%i.%i.%i</title>\n</head>\n", FS_VERSION_MAJOR, FS_VERSION_MINOR, FS_VERSION_BUILD, FS_VERSION_REVIS);
32982 		fputs("<body>", fp);
32983 		fprintf(fp,"\t<h1>SEXP Output - FSO v%i.%i.%i.%i</h1>\n", FS_VERSION_MAJOR, FS_VERSION_MINOR, FS_VERSION_BUILD, FS_VERSION_REVIS);
32984 	}
32985 
32986 	SCP_vector<int> done_sexp_ids;
32987 	int x,y,z;
32988 
32989 	//Output an overview
32990 	fputs("<dl>", fp);
32991 	for(x = 0; x < Num_op_menus; x++)
32992 	{
32993 		fprintf(fp, "<dt><a href=\"#%d\">%s</a></dt>", (op_menu[x].id & OP_CATEGORY_MASK), op_menu[x].name);
32994 		for(y = 0; y < Num_submenus; y++)
32995 		{
32996 			if(((op_submenu[y].id & OP_CATEGORY_MASK) == op_menu[x].id))
32997 			{
32998 				fprintf(fp, "<dd><a href=\"#%d\">%s</a></dd>", op_submenu[y].id & (OP_CATEGORY_MASK | SUBCATEGORY_MASK), op_submenu[y].name);
32999 			}
33000 		}
33001 	}
33002 	fputs("</dl>", fp);
33003 
33004 	//Output the full descriptions
33005 	fputs("<dl>", fp);
33006 	for(x = 0; x < Num_op_menus; x++)
33007 	{
33008 		fprintf(fp, "<dt id=\"%d\"><h2>%s</h2></dt>\n", (op_menu[x].id & OP_CATEGORY_MASK), op_menu[x].name);
33009 		fputs("<dd>", fp);
33010 		fputs("<dl>", fp);
33011 		for(y = 0; y < Num_submenus; y++)
33012 		{
33013 			if(((op_submenu[y].id & OP_CATEGORY_MASK) == op_menu[x].id))
33014 			{
33015 				fprintf(fp, "<dt id=\"%d\"><h3>%s</h3></dt>\n", op_submenu[y].id & (OP_CATEGORY_MASK | SUBCATEGORY_MASK), op_submenu[y].name);
33016 				fputs("<dd>", fp);
33017 				fputs("<dl>", fp);
33018 				for(z = 0; z < Num_operators; z++)
33019 				{
33020 					if((get_category(Operators[z].value) == op_menu[x].id)
33021 						&& (get_subcategory(Operators[z].value) != -1)
33022 						&& (get_subcategory(Operators[z].value) == op_submenu[y].id))
33023 					{
33024 						output_sexp_html(z, fp);
33025 					}
33026 				}
33027 				fputs("</dl>", fp);
33028 				fputs("</dd>", fp);
33029 			}
33030 		}
33031 		for(z = 0; z < Num_operators; z++)
33032 		{
33033 			if((get_category(Operators[z].value) == op_menu[x].id)
33034 				&& (get_subcategory(Operators[z].value) == -1))
33035 			{
33036 				output_sexp_html(z, fp);
33037 			}
33038 		}
33039 		fputs("</dl>", fp);
33040 		fputs("</dd>", fp);
33041 	}
33042 	for(z = 0; z < Num_operators; z++)
33043 	{
33044 		if(!get_category(Operators[z].value))
33045 		{
33046 			output_sexp_html(z, fp);
33047 		}
33048 	}
33049 	fputs("</dl>", fp);
33050 	fputs("</body>\n</html>\n", fp);
33051 
33052 	fclose(fp);
33053 
33054 	return true;
33055 }
33056