/*
 * Copyright (C) Volition, Inc. 1999.  All rights reserved.
 *
 * All source code herein is the property of Volition, Inc. You may not sell 
 * or otherwise commercially exploit the source or things you created based on the 
 * source.
 *
*/

#ifndef _SEXP_H
#define _SEXP_H

#include "globalincs/globals.h"
#include "globalincs/pstypes.h"	// for NULL
#include "object/object_flags.h"
#include "ship/ship_flags.h"
#include "mission/mission_flags.h"
#include "ai/ai_flags.h"

class ship_subsys;
class ship;
struct prop;
class waypoint_list;
class object;
class waypoint;
class p_object;
struct ship_obj;
class gamesnd_id;

// bumped to 30 by Goober5000
#define	OPERATOR_LENGTH	30  // if this ever exceeds TOKEN_LENGTH, let JasonH know!

#define MAX_SEXP_VARIABLES 250

// Operator argument formats (data types of an argument)
enum sexp_opf_t : int {
	OPF_UNUSED,						// argument types need to start at 1 instead of 0
	OPF_NONE,						// argument cannot exist at this position if it's this
	OPF_NULL,						// no value.  Can still be used for type matching, however
	OPF_BOOL,
	OPF_NUMBER,
	OPF_SHIP,
	OPF_PROP,
	OPF_WING,
	OPF_SUBSYSTEM,
	OPF_POINT,						// either a 3d point in space, or a waypoint name
	OPF_IFF,
	OPF_AI_GOAL,					// special to match ai goals
	OPF_DOCKER_POINT,				// docking point on docker ship
	OPF_DOCKEE_POINT,				// docking point on dockee ship
	OPF_MESSAGE,					// the name (id) of a message in Messages[] array
	OPF_WHO_FROM,					// who sent the message -- doesn't necessarily have to be a ship!!!
	OPF_PRIORITY,					// priority for messages
	OPF_WAYPOINT_PATH,				// name of a waypoint
	OPF_POSITIVE,					// positive number or zero
	OPF_MISSION_NAME,				// name of a mission for various mission related things
	OPF_SHIP_POINT,					// a waypoint or a ship
	OPF_GOAL_NAME,					// name of goal (or maybe event?) from a mission
	OPF_SHIP_WING,					// either a ship or wing name (they don't conflict)
	OPF_SHIP_PROP,                  // either a ship or a prop
	OPF_SHIP_WING_WHOLETEAM,		// Karajorma - Ship, wing or an entire team's worth of ships
	OPF_SHIP_WING_SHIPONTEAM_POINT,	// name of a ship, wing, any ship on a team, or a point
	OPF_SHIP_WING_POINT,
	OPF_SHIP_WING_POINT_OR_NONE,	// WMC - Ship, wing, point or none
	OPF_SHIP_TYPE,					// type of ship (fighter/bomber/etc)... NOTE: the type "fighter/bomber" is allowed even though it's not a real ship type; SEXPs must account for this
	OPF_KEYPRESS,					// a default key
	OPF_EVENT_NAME,					// name of an event
	OPF_AI_ORDER,					// a squadmsg order player can give to a ship
	OPF_SKILL_LEVEL,				// current skill level of the game
	OPF_MEDAL_NAME,					// name of medals
	OPF_WEAPON_NAME,				// name of a weapon
	OPF_SHIP_CLASS_NAME,			// name of a ship class
	OPF_PROP_CLASS_NAME,            // name of a prop class
	OPF_CUSTOM_HUD_GAUGE,			// name of custom HUD gauge
	OPF_HUGE_WEAPON,				// name of a secondary bomb type weapon
	OPF_SHIP_NOT_PLAYER,			// a ship, but not a player ship
	OPF_JUMP_NODE_NAME,				// name of a jump node
	OPF_VARIABLE_NAME,				// variable name
	OPF_AMBIGUOUS,					// type used with variable
	OPF_AWACS_SUBSYSTEM,			// an awacs subsystem
	OPF_CARGO,						// Goober5000 - a cargo string (currently used for set-cargo and is-cargo)
	OPF_AI_CLASS,					// Goober5000 - an AI class
	OPF_SUPPORT_SHIP_CLASS,			// Goober5000 - a support ship class
	OPF_ARRIVAL_LOCATION,			// Goober5000 - a ship arrival location
	OPF_ARRIVAL_ANCHOR_ALL,			// Goober5000 - all of a ship's possible arrival anchors
	OPF_DEPARTURE_LOCATION,			// Goober5000 - a ship departure location
	OPF_SHIP_WITH_BAY,				// Goober5000 - a ship with a fighter bay
	OPF_SOUNDTRACK_NAME,			// Goober5000 - the name of a music soundtrack
	OPF_INTEL_NAME,					// Goober5000 - the name of an intel entry in species.tbl
	OPF_STRING,						// Goober5000 - any old string
	OPF_ROTATING_SUBSYSTEM,			// Goober5000 - a rotating subsystem
	OPF_NAV_POINT,					// Kazan	  - a Nav Point name
	OPF_SSM_CLASS,					// Goober5000 - an SSM class
	OPF_FLEXIBLE_ARGUMENT,			// Goober5000 - special to match for when-argument
	OPF_ANYTHING,					// Goober5000 - anything goes, except containers
	OPF_SKYBOX_MODEL_NAME,			// taylor - changing skybox model
	OPF_SHIP_OR_NONE,				// Goober5000 - an "optional" ship argument
	OPF_BACKGROUND_BITMAP,			// phreak - name of a background bitmap
	OPF_SUN_BITMAP,					// phreak - name of a background bitmap
	OPF_NEBULA_STORM_TYPE,			// phreak - name a nebula storm
	OPF_NEBULA_POOF,				// phreak - name of a nebula poof
	OPF_TURRET_TARGET_ORDER,		// WMC - name of a turret target type (see aiturret.cpp)
	OPF_SUBSYSTEM_OR_NONE,			// Goober5000 - an "optional" subsystem argument
	OPF_PERSONA,					// Karajorma - name of a persona
	OPF_SUBSYS_OR_GENERIC,			// Karajorma - a subsystem or a generic name (like engine) which covers all subsystems of that type
	OPF_ORDER_RECIPIENT,			// Karajorma - since orders can go to All Fighters as well as a ship or wing
	OPF_SUBSYSTEM_TYPE,				// Goober5000 - a generic subsystem type (navigation, engines, etc.) rather than a specific subsystem
	OPF_POST_EFFECT,				// Hery - type of post-processing effect
	OPF_TARGET_PRIORITIES,			// FUBAR - Target priority groups
	OPF_ARMOR_TYPE,					// FUBAR - Armor type or <none>
	OPF_FONT,						// Goober5000 - a FreeSpace font
	OPF_HUD_ELEMENT,				// A magic name of a specific HUD element
	OPF_SOUND_ENVIRONMENT,			// Goober5000 - one of EFX_presets, per Taylor
	OPF_SOUND_ENVIRONMENT_OPTION,	// Goober5000 - one of Taylor's options
	OPF_EXPLOSION_OPTION,			// Goober5000
	OPF_AUDIO_VOLUME_OPTION,		// The E
	OPF_WEAPON_BANK_NUMBER,			// Karajorma - The number of a primary/secondary/tertiary weapon bank or all of them
	OPF_MESSAGE_OR_STRING,			// Goober5000 - provides a list of messages like OPF_MESSAGE, but also allows entering arbitrary strings
	OPF_BUILTIN_HUD_GAUGE,			// The E
	OPF_DAMAGE_TYPE,				// FUBAR - Damage type or <none>
	OPF_SHIP_EFFECT,				// The E - per-ship effects, as defined in post-processing.tbl
	OPF_ANIMATION_TYPE,				// Goober5000 - as defined in modelanim.h
	OPF_MISSION_MOOD,				// Karajorma - Moods determine which builtin messages will be sent
	OPF_SHIP_FLAG,					// Karajorma - The name of a ship flag
	OPF_TEAM_COLOR,					// The E - Color settings as defined in Colors.tbl
	OPF_NEBULA_PATTERN,				// Axem - Full Nebula Background Patterns, as defined in nebula.tbl
	OPF_SKYBOX_FLAGS,				// niffiwan - valid skybox flags
	OPF_GAME_SND,					// m!m - A game sound
	OPF_FIREBALL,					// Goober5000 - an entry in fireball.tbl
	OPF_SPECIES,					// Goober5000
	OPF_LANGUAGE,					// Goober5000
	OPF_FUNCTIONAL_WHEN_EVAL_TYPE,	// Goober5000
	OPF_CONTAINER_NAME,				// Karajorma/jg18 - The name of a SEXP container
	OPF_LIST_CONTAINER_NAME,		// Karajorma/jg18 - The name of a SEXP list container
	OPF_MAP_CONTAINER_NAME,			// Karajorma/jg18 - The name of a SEXP map container
	OPF_ANIMATION_NAME,				// Lafiel
	OPF_CONTAINER_VALUE,			// jg18 - Container data and map container keys
	OPF_DATA_OR_STR_CONTAINER,		// jg18 - any data, or a container that is accessed via strings
	OPF_TRANSLATING_SUBSYSTEM,		// Goober5000 - a translating subsystem
	OPF_ANY_HUD_GAUGE,				// Goober5000 - both custom and builtin
	OPF_WING_FLAG,					// Goober5000 - The name of a wing flag
	OPF_ASTEROID_TYPES,				// MjnMixael - Asteroids from asteroids.tbl, asteroid types only
	OPF_DEBRIS_TYPES,				// MjnMixael - Asteroids from asteroids.tbl, debris types only
	OPF_WING_FORMATION,				// Goober5000 - as defined in ships.tbl
	OPF_MOTION_DEBRIS,				// MjnMixael - Motion debris types as defined in stars.tbl
	OPF_TURRET_TYPE,				// MjnMixael - Turret types as defined in aiturret.cpp
	OPF_BOLT_TYPE,					// MjnMixael - Lightning bolt types as defined in lightning.tbl
	OPF_TRAITOR_OVERRIDE,			// MjnMixael - Traitor overrides as defined in traitor.tbl
	OPF_LUA_GENERAL_ORDER,          // MjnMixael - General orders as defined in sexps.tbl
	OPF_CHILD_LUA_ENUM,			    // MjnMixael - Used to let Lua Enums reference Enums
	OPF_MISSION_CUSTOM_STRING,      // MjnMixael - The custom strings as defined in FRED
	OPF_MESSAGE_TYPE,      // naomimyselfandi - A message type (Attack Target et al.)

	//Must always be at the end of the list
	First_available_opf_id
};

struct dynamic_sexp_enum_list {
	SCP_string name;
	SCP_vector<SCP_string> list;
};

extern SCP_vector<dynamic_sexp_enum_list> Dynamic_enums;

struct dynamic_sexp_parameter_list {
	SCP_string operator_name;
	SCP_vector<std::pair<int, int>> parameter_map;
};

extern SCP_vector<dynamic_sexp_parameter_list> Dynamic_parameters;

struct dynamic_sexp_child_enum_suffixes {
	SCP_string operator_name;
	int param_index;
	SCP_string suffix;
};

extern SCP_vector<dynamic_sexp_child_enum_suffixes> Dynamic_enum_suffixes;

int get_dynamic_parameter_index(const SCP_string &op_name, int param);

int get_dynamic_enum_position(const SCP_string &enum_name);

SCP_string get_child_enum_suffix(const SCP_string& op_name, int param_index);

// Operand return types
enum sexp_opr_t : int {
	OPR_NONE,
	OPR_NUMBER,             // returns number
	OPR_BOOL,               // returns true/false value
	OPR_NULL,               // doesn't return a value
	OPR_AI_GOAL,	        // is an ai operator (doesn't really return a value, but used for type matching)
	OPR_POSITIVE,	        // returns a non-negative number
	OPR_STRING,             // not really a return type, but used for type matching.
	OPR_AMBIGUOUS,          // not really a return type, but used for type matching.
	OPR_FLEXIBLE_ARGUMENT,  // Goober5000 - is an argument operator (doesn't really return a value, but used for type matching)
};

#define	OP_INSERT_FLAG			0x8000
#define	OP_REPLACE_FLAG			0x4000

// if we ever have more than 1024 (!)
// total sexps, we're going to have to
// figure out a different way of
// distinguishing between sexp identifier
// and sexp array index
#define	FIRST_OP				0x0400

enum : int {
	OP_CATEGORY_NONE,
	OP_CATEGORY_OBJECTIVE,
	OP_CATEGORY_TIME,
	OP_CATEGORY_LOGICAL,
	OP_CATEGORY_ARITHMETIC,
	OP_CATEGORY_STATUS,
	OP_CATEGORY_CHANGE,
	OP_CATEGORY_CONDITIONAL,
	OP_CATEGORY_AI,
	OP_CATEGORY_TRAINING,
	OP_CATEGORY_UNLISTED,
	OP_CATEGORY_GOAL_EVENT,
	// this should come after every category
	First_available_category_id
};

// New subcategories! :) -- Goober5000
// Adding more subcategories is possible with the new code.  All that needs to be done is
// to add a line here, some appropriate case statements in get_subcategory() and
// category_from_subcategory(), and the submenu name in the op_submenu[] array in sexp.cpp.
enum : int {
	OP_SUBCATEGORY_NONE,
	CHANGE_SUBCATEGORY_MESSAGING,
	CHANGE_SUBCATEGORY_AI_CONTROL,
	CHANGE_SUBCATEGORY_SHIP_STATUS,
	CHANGE_SUBCATEGORY_SHIELDS_ENGINES_AND_WEAPONS,
	CHANGE_SUBCATEGORY_SUBSYSTEMS,
	CHANGE_SUBCATEGORY_CARGO,
	CHANGE_SUBCATEGORY_ARMOR_AND_DAMAGE_TYPES,
	CHANGE_SUBCATEGORY_BEAMS_AND_TURRETS,
	CHANGE_SUBCATEGORY_MODELS_AND_TEXTURES,
	CHANGE_SUBCATEGORY_COORDINATE_MANIPULATION,
	CHANGE_SUBCATEGORY_MISSION_AND_CAMPAIGN,
	CHANGE_SUBCATEGORY_MUSIC_AND_SOUND,
	CHANGE_SUBCATEGORY_HUD,
	CHANGE_SUBCATEGORY_NAV,
	CHANGE_SUBCATEGORY_CUTSCENES,
	CHANGE_SUBCATEGORY_BACKGROUND_AND_NEBULA,
	CHANGE_SUBCATEGORY_JUMP_NODES,
	CHANGE_SUBCATEGORY_SPECIAL_EFFECTS,
	CHANGE_SUBCATEGORY_VARIABLES,
	CHANGE_SUBCATEGORY_CONTAINERS,
	CHANGE_SUBCATEGORY_OTHER,
	STATUS_SUBCATEGORY_MISSION,
	STATUS_SUBCATEGORY_CUTSCENES,
	STATUS_SUBCATEGORY_PLAYER,
	STATUS_SUBCATEGORY_MULTIPLAYER,
	STATUS_SUBCATEGORY_SHIP_STATUS,
	STATUS_SUBCATEGORY_SHIELDS_ENGINES_AND_WEAPONS,
	STATUS_SUBCATEGORY_CARGO,
	STATUS_SUBCATEGORY_DAMAGE,
	STATUS_SUBCATEGORY_DISTANCE_AND_COORDINATES,
	STATUS_SUBCATEGORY_VARIABLES,
	STATUS_SUBCATEGORY_CONTAINERS,
	STATUS_SUBCATEGORY_OTHER,
	// this should come after every subcategory
	First_available_subcategory_id
};

enum : int {
	//an operator value of 0 represents a non-operator.
	//Because of this, OP_NOT_AN_OP must NEVER be changed to a different value and must always be assigned 0
	OP_NOT_AN_OP = 0, 

	// OP_CATEGORY_ARITHMETIC

	OP_PLUS = FIRST_OP,
	OP_MINUS,
	OP_MOD,
	OP_MUL,
	OP_DIV,
	OP_RAND,
	OP_ABS,	// Goober5000
	OP_MIN,	// Goober5000
	OP_MAX,	// Goober5000
	OP_AVG,	// Goober5000
	OP_RAND_MULTIPLE,	// Goober5000
	OP_POW,	// Goober5000
	OP_BITWISE_AND,	// Goober5000
	OP_BITWISE_OR,	// Goober5000
	OP_BITWISE_NOT,	// Goober5000
	OP_BITWISE_XOR,	// Goober5000

	OP_SET_BIT,	// Goober5000
	OP_UNSET_BIT,	// Goober5000
	OP_IS_BIT_SET,	// Goober5000
	OP_SIGNUM,	// Goober5000
	OP_IS_NAN,	// Goober5000
	OP_NAN_TO_NUMBER,	// Goober5000
	OP_ANGLE_VECTORS,	// Lafiel

	// OP_CATEGORY_LOGICAL

	OP_TRUE,
	OP_FALSE,
	OP_AND,
	OP_AND_IN_SEQUENCE,
	OP_OR,
	OP_EQUALS,
	OP_GREATER_THAN,
	OP_LESS_THAN,
	OP_HAS_TIME_ELAPSED,
	OP_NOT,
	OP_STRING_EQUALS,
	OP_STRING_GREATER_THAN,
	OP_STRING_LESS_THAN,
	OP_NOT_EQUAL,	// Goober5000
	OP_GREATER_OR_EQUAL,	// Goober5000
	OP_LESS_OR_EQUAL,	// Goober5000

	OP_XOR,	// Goober5000
	OP_PERFORM_ACTIONS_BOOL_FIRST,	// Goober5000
	OP_PERFORM_ACTIONS_BOOL_LAST,	// Goober5000
	OP_HAS_TIME_ELAPSED_MSECS,	// Goober5000
	OP_IS_TRUE_FOR_DURATION,	// Goober5000

	// OP_CATEGORY_GOAL_EVENT
	
	OP_GOAL_INCOMPLETE,
	OP_GOAL_TRUE_DELAY,
	OP_GOAL_FALSE_DELAY,
	OP_EVENT_INCOMPLETE,
	OP_EVENT_TRUE_DELAY,
	OP_EVENT_FALSE_DELAY,
	OP_PREVIOUS_EVENT_TRUE,
	OP_PREVIOUS_EVENT_FALSE,
	OP_PREVIOUS_GOAL_TRUE,
	OP_PREVIOUS_GOAL_FALSE,
	OP_EVENT_TRUE_MSECS_DELAY,
	OP_EVENT_FALSE_MSECS_DELAY,
	OP_RESET_EVENT,
	OP_RESET_GOAL,

	// OP_CATEGORY_OBJECTIVE
	
	OP_IS_DESTROYED_DELAY,
	OP_IS_SUBSYSTEM_DESTROYED_DELAY,
	OP_IS_DISABLED_DELAY,
	OP_IS_DISARMED_DELAY,
	OP_HAS_DOCKED_DELAY,
	OP_HAS_UNDOCKED_DELAY,
	OP_HAS_ARRIVED_DELAY,
	OP_HAS_DEPARTED_DELAY,
	OP_WAYPOINTS_DONE_DELAY,
	OP_SHIP_TYPE_DESTROYED,
	OP_PERCENT_SHIPS_DEPARTED,
	OP_PERCENT_SHIPS_DESTROYED,
	OP_DEPART_NODE_DELAY,
	OP_DESTROYED_DEPARTED_DELAY,
	OP_PERCENT_SHIPS_DISARMED,	// Goober5000
	OP_PERCENT_SHIPS_DISABLED,	// Goober5000

	OP_PERCENT_SHIPS_ARRIVED,	// FUBAR-BDHR
	OP_NAV_IS_VISITED,	// Kazan
	OP_WAS_DESTROYED_BY_DELAY,	// WCS
	OP_PERCENT_SHIPS_SCANNED,	// Goober5000
	
	// OP_CATEGORY_TIME
	
	OP_TIME_SHIP_DESTROYED,
	OP_TIME_SHIP_ARRIVED,
	OP_TIME_SHIP_DEPARTED,
	OP_TIME_WING_DESTROYED,
	OP_TIME_WING_ARRIVED,
	OP_TIME_WING_DEPARTED,
	OP_MISSION_TIME,
	OP_MISSION_TIME_MSECS,	// Goober5000
	OP_TIME_DOCKED,
	OP_TIME_UNDOCKED,
	OP_TIME_TO_GOAL, // tcrayford
	OP_SET_HUD_TIME_PAD, // MjnMixael
	
	// OP_CATEGORY_STATUS
	
	OP_SHIELDS_LEFT,
	OP_HITS_LEFT,
	OP_HITS_LEFT_SUBSYSTEM,	// deprecated
	OP_SIM_HITS_LEFT,
	OP_DISTANCE,
	OP_DISTANCE_CENTER_SUBSYSTEM,	// Goober5000
	OP_LAST_ORDER_TIME,
	OP_NUM_PLAYERS,
	OP_SKILL_LEVEL_AT_LEAST,
	OP_WAS_PROMOTION_GRANTED,
	OP_WAS_MEDAL_GRANTED,
	OP_CARGO_KNOWN_DELAY,
	OP_CAP_SUBSYS_CARGO_KNOWN_DELAY,
	OP_HAS_BEEN_TAGGED_DELAY,
	OP_IS_TAGGED,
	OP_NUM_KILLS,
	
	OP_NUM_TYPE_KILLS,
	OP_NUM_CLASS_KILLS,
	OP_SHIELD_RECHARGE_PCT,
	OP_ENGINE_RECHARGE_PCT,
	OP_WEAPON_RECHARGE_PCT,
	OP_SHIELD_QUAD_LOW,
	OP_SECONDARY_AMMO_PCT,
	OP_IS_SECONDARY_SELECTED,
	OP_IS_PRIMARY_SELECTED,
	OP_SPECIAL_WARP_DISTANCE,
	OP_IS_SHIP_VISIBLE,
	OP_TEAM_SCORE,
	OP_PRIMARY_AMMO_PCT,	// Goober5000
	OP_IS_SHIP_STEALTHY,	// Goober5000
	OP_IS_CARGO,	// Goober5000
	OP_IS_FRIENDLY_STEALTH_VISIBLE,	// Goober5000
	
	OP_GET_OBJECT_X,	// Goober5000
	OP_GET_OBJECT_Y,	// Goober5000
	OP_GET_OBJECT_Z,	// Goober5000
	OP_IS_AI_CLASS,	// Goober5000
	OP_IS_SHIP_TYPE,	// Goober5000
	OP_IS_SHIP_CLASS,	// Goober5000
	OP_NUM_SHIPS_IN_BATTLE,	// phreak
	OP_CURRENT_SPEED, // WMCoolmon
	OP_IS_IFF,	// Goober5000
	OP_IS_SPECIES,	// Goober5000
	OP_NUM_WITHIN_BOX,	// WMCoolmon
	OP_SCRIPT_EVAL_NUM, // WMCoolmon
	OP_SCRIPT_EVAL_STRING, // WMCoolmon
	OP_NUM_SHIPS_IN_WING,	// Karajorma
	OP_GET_PRIMARY_AMMO, // Karajorma
	OP_GET_SECONDARY_AMMO, // Karajorma

	OP_NUM_ASSISTS, // Karajorma
	OP_SHIP_SCORE, // Karajorma
	OP_SHIP_DEATHS, // Karajorma
	OP_RESPAWNS_LEFT, // Karajorma
	OP_IS_PLAYER, // Karajorma
	OP_GET_DAMAGE_CAUSED, // Karajorma
	OP_AFTERBURNER_LEFT, // Karajorma
	OP_WEAPON_ENERGY_LEFT, // Karajorma
	OP_PRIMARY_FIRED_SINCE, // Karajorma
	OP_SECONDARY_FIRED_SINCE, // Karajorma
	OP_CUTSCENES_GET_FOV, // Echelon9
	OP_HITS_LEFT_SUBSYSTEM_GENERIC, // Goober5000
	OP_HITS_LEFT_SUBSYSTEM_SPECIFIC, // Goober5000
	OP_GET_OBJECT_PITCH,	// Goober5000
	OP_GET_OBJECT_BANK,	// Goober5000
	OP_GET_OBJECT_HEADING,	// Goober5000
	
	OP_HAS_PRIMARY_WEAPON, // Karajorma
	OP_HAS_SECONDARY_WEAPON, // Karajorma
	OP_STRING_TO_INT, // Karajorma
	OP_STRING_GET_LENGTH, // Goober5000
	OP_GET_OBJECT_SPEED_X,
	OP_GET_OBJECT_SPEED_Y,
	OP_GET_OBJECT_SPEED_Z,
	OP_NAV_DISTANCE, // Kazan
	OP_NAV_ISLINKED, // Kazan
	OP_IS_FACING, // The E
	OP_DIRECTIVE_VALUE, // Karajorma
	OP_GET_NUM_COUNTERMEASURES, // Karajorma
	OP_IS_IN_BOX, // Sushi
	OP_IS_IN_MISSION, // Goober5000
	OP_ARE_SHIP_FLAGS_SET, // Karajorma
	OP_ARE_WING_FLAGS_SET, // Goober5000

	OP_GET_THROTTLE_SPEED, // Karajorma
	OP_HAS_ARMOR_TYPE, // MjnMixael
	OP_TURRET_GET_PRIMARY_AMMO, // DahBlount, part of the turret ammo code
	OP_TURRET_GET_SECONDARY_AMMO,	// DahBlount, part of the turret ammo code
	OP_TURRET_HAS_PRIMARY_WEAPON,      // MjnMixael
	OP_TURRET_HAS_SECONDARY_WEAPON, // MjnMixael
	OP_IS_DOCKED,	// Goober5000
	OP_IS_IN_TURRET_FOV,	// Goober5000
	OP_GET_HOTKEY, // wookieejedi
	OP_DISTANCE_CENTER, // Goober5000
	OP_DISTANCE_BBOX, // Goober5000
	OP_DISTANCE_BBOX_SUBSYSTEM, // Goober5000
	OP_IS_LANGUAGE,						// Goober5000
	OP_SCRIPT_EVAL_BOOL, // Goober5000
	OP_IS_CONTAINER_EMPTY,	// Karajorma/jg18
	OP_GET_CONTAINER_SIZE,	// Karajorma/jg18

	OP_LIST_HAS_DATA,	// Karajorma/jg18
	OP_LIST_DATA_INDEX,	// Karajorma/jg18
	OP_MAP_HAS_KEY,	// Karajorma/jg18
	OP_MAP_HAS_DATA_ITEM,	// Karajorma/jg18
	OP_ANGLE_FVEC_TARGET, // Lafiel
	OP_IS_SHIP_EMP_ACTIVE,	// MjnMixael
	OP_PLAYER_IS_CHEATING_BASTARD,	// The E
	OP_TURRET_FIRED_SINCE,	// Asteroth
	OP_GET_SUPERNOVA_STAGE, // Goober5000
	
	// OP_CATEGORY_CONDITIONAL
	// conditional sexpressions
	
	OP_WHEN,
	OP_WHEN_ARGUMENT,	// Goober5000
	OP_EVERY_TIME,	// Goober5000
	OP_EVERY_TIME_ARGUMENT,	// Goober5000
	OP_ANY_OF,	// Goober5000
	OP_EVERY_OF,	// Goober5000
	OP_RANDOM_OF,	// Goober5000
	OP_NUMBER_OF,	// Goober5000
	OP_INVALIDATE_ARGUMENT,	// Goober5000
	OP_RANDOM_MULTIPLE_OF,	// Karajorma
	OP_IN_SEQUENCE,	// Karajorma
	OP_VALIDATE_ARGUMENT,	// Karajorma
	OP_DO_FOR_VALID_ARGUMENTS,	// Karajorma
	OP_INVALIDATE_ALL_ARGUMENTS,	// Karajorma
	OP_VALIDATE_ALL_ARGUMENTS,	// Karajorma
	OP_FOR_COUNTER,	// Goober5000
	
	OP_IF_THEN_ELSE,	// Goober5000
	OP_NUM_VALID_ARGUMENTS,	// Karajorma
	OP_FUNCTIONAL_IF_THEN_ELSE,	// Goober5000
	OP_FOR_SHIP_CLASS,	// Goober5000
	OP_FOR_SHIP_TYPE,	// Goober5000
	OP_FOR_SHIP_TEAM,	// Goober5000
	OP_FOR_SHIP_SPECIES,	// Goober5000
	OP_FOR_PLAYERS,	// Goober5000
	OP_FIRST_OF,	// MageKing17
	OP_SWITCH,	// Goober5000
	OP_FUNCTIONAL_SWITCH,	// Goober5000
	OP_FUNCTIONAL_WHEN,	// Goober5000
	OP_FOR_CONTAINER_DATA,	// jg18
	OP_FOR_MAP_CONTAINER_KEYS,	// jg18
	OP_ON_MISSION_SKIP,	// Goober5000
	OP_FOR_SUBSYSTEMS,	// Goober5000

	// OP_CATEGORY_CHANGE
	// sexpressions with side-effects
	
	OP_CHANGE_IFF,
	OP_REPAIR_SUBSYSTEM,
	OP_SABOTAGE_SUBSYSTEM,
	OP_SET_SUBSYSTEM_STRNGTH,
	OP_PROTECT_SHIP,
	OP_SEND_MESSAGE,
	OP_SEND_BUILTIN_MESSAGE,
	OP_SELF_DESTRUCT,
	OP_CLEAR_GOALS,
	OP_ADD_GOAL,
	OP_REMOVE_GOAL,	// Goober5000
	OP_INVALIDATE_GOAL,
	OP_VALIDATE_GOAL,
	OP_SEND_RANDOM_MESSAGE,
	OP_TRANSFER_CARGO,
	OP_EXCHANGE_CARGO,
	OP_UNPROTECT_SHIP,
	
	OP_GOOD_REARM_TIME,
	OP_BAD_REARM_TIME,
	OP_GRANT_PROMOTION,
	OP_GRANT_MEDAL,
	OP_ALLOW_SHIP,
	OP_ALLOW_WEAPON,
	OP_GOOD_SECONDARY_TIME,
	OP_WARP_BROKEN,
	OP_WARP_NOT_BROKEN,
	OP_WARP_NEVER,
	OP_WARP_ALLOWED,
	OP_SHIP_INVISIBLE,
	OP_SHIP_VISIBLE,
	OP_SHIP_INVULNERABLE,
	OP_SHIP_VULNERABLE,
	OP_RED_ALERT,
	
	OP_TECH_ADD_SHIP,
	OP_TECH_ADD_WEAPON,
	OP_END_CAMPAIGN,
	OP_JETTISON_CARGO_DELAY,
	OP_MODIFY_VARIABLE,
	OP_NOP,
	OP_BEAM_FIRE,
	OP_BEAM_FREE,
	OP_BEAM_FREE_ALL,
	OP_BEAM_LOCK,
	OP_BEAM_LOCK_ALL,
	OP_BEAM_PROTECT_SHIP,
	OP_BEAM_UNPROTECT_SHIP,
	OP_TURRET_FREE,
	OP_TURRET_FREE_ALL,
	OP_TURRET_LOCK,
	
	OP_TURRET_LOCK_ALL,
	OP_ADD_REMOVE_ESCORT,
	OP_AWACS_SET_RADIUS,
	OP_SEND_MESSAGE_LIST,
	OP_CAP_WAYPOINT_SPEED,
	OP_SHIP_GUARDIAN,
	OP_SHIP_NO_GUARDIAN,
	OP_TURRET_TAGGED_ONLY_ALL,
	OP_TURRET_TAGGED_CLEAR_ALL,
	OP_SUBSYS_SET_RANDOM,
	OP_SUPERNOVA_START,
	OP_CARGO_NO_DEPLETE,
	OP_SET_SPECIAL_WARPOUT_NAME,
	OP_SHIP_VANISH,
	OP_PROP_VANISH, // MjnMixael
	OP_SHIELDS_ON,	//-Sesquipedalian
	OP_SHIELDS_OFF,	//-Sesquipedalian
	
	OP_CHANGE_AI_LEVEL,	//-Sesquipedalian
	OP_END_MISSION, //-Sesquipedalian. replaces end-mission-delay, which did nothing
	OP_SET_SCANNED,	// Goober5000
	OP_SET_UNSCANNED,	// Goober5000
	OP_SHIP_STEALTHY,	// Goober5000
	OP_SHIP_UNSTEALTHY,	// Goober5000
	OP_SET_CARGO,	// Goober5000
	OP_CHANGE_AI_CLASS,	// Goober5000
	OP_FRIENDLY_STEALTH_INVISIBLE,	// Goober5000
	OP_FRIENDLY_STEALTH_VISIBLE,	// Goober5000
	OP_DAMAGED_ESCORT_LIST, //phreak
	OP_DAMAGED_ESCORT_LIST_ALL,	// Goober5000
	OP_SHIP_VAPORIZE,	// Goober5000
	OP_SHIP_NO_VAPORIZE,	// Goober5000
	OP_COLLIDE_INVISIBLE,	// Goober5000
	OP_DONT_COLLIDE_INVISIBLE,	// Goober5000
	
	OP_PRIMITIVE_SENSORS_SET_RANGE,	// Goober5000
	OP_CHANGE_SHIP_CLASS,	// Goober5000
	OP_SCRIPT_EVAL, //WMC
	OP_SET_SUPPORT_SHIP,	// Goober5000
	OP_DEACTIVATE_GLOW_POINTS,	//-Bobboau
	OP_ACTIVATE_GLOW_POINTS,	//-Bobboau
	OP_DEACTIVATE_GLOW_MAPS,	//-Bobboau
	OP_ACTIVATE_GLOW_MAPS,	//-Bobboau
	OP_DEACTIVATE_GLOW_POINT_BANK,	//-Bobboau
	OP_ACTIVATE_GLOW_POINT_BANK,	//-Bobboau
	OP_CHANGE_SOUNDTRACK,	// Goober5000
	OP_TECH_ADD_INTEL,	// Goober5000
	OP_TECH_RESET_TO_DEFAULT,	// Goober5000
	OP_CREATE_BOLT,		//MjnMixael
	OP_EXPLOSION_EFFECT,	// Goober5000
	OP_WARP_EFFECT,	// Goober5000
	OP_SET_OBJECT_FACING,	// Goober5000
	
	OP_SET_OBJECT_FACING_OBJECT,	// Goober5000
	OP_SET_OBJECT_POSITION,	// Goober5000
	OP_PLAY_SOUND_FROM_TABLE,	// Goober5000
	OP_PLAY_SOUND_FROM_FILE,	// Goober5000
	OP_CLOSE_SOUND_FROM_FILE,	// Goober5000
	OP_HUD_DISABLE,	// Goober5000
	OP_KAMIKAZE,	//-Sesquipedalian
	OP_MISSION_SET_SUBSPACE,
	OP_TURRET_TAGGED_SPECIFIC, //phreak
	OP_TURRET_TAGGED_CLEAR_SPECIFIC, //phreak
	OP_LOCK_ROTATING_SUBSYSTEM,	// Goober5000
	OP_FREE_ROTATING_SUBSYSTEM,	// Goober5000
	OP_REVERSE_ROTATING_SUBSYSTEM,	// Goober5000
	OP_ROTATING_SUBSYS_SET_TURN_TIME,	// Goober5000
	OP_PLAYER_USE_AI,	// Goober5000
	OP_PLAYER_NOT_USE_AI,	// Goober5000
	OP_SET_PLAYER_TARGET, // LuytenKy
	OP_CLEAR_PLAYER_TARGET, // LuytenKy

	OP_HUD_DISABLE_EXCEPT_MESSAGES,	// Goober5000
	OP_FORCE_JUMP,	// Goober5000
	OP_HUD_SET_TEXT, //WMC
	OP_HUD_SET_XSTR,
	OP_HUD_SET_TEXT_NUM, //WMC
	OP_HUD_SET_COORDS, //WMC
	OP_HUD_SET_FRAME, //WMC
	OP_HUD_SET_COLOR, //WMC
	OP_HUD_RESET_COLOR, // Goober5000
	OP_HUD_SET_MAX_TARGETING_RANGE, // Goober5000
	OP_SHIP_TAG, // Goober5000
	OP_SHIP_UNTAG, // Goober5000
	OP_SHIP_CHANGE_ALT_NAME,	// Goober5000
	OP_SHIP_CHANGE_DISPLAY_NAME,
	OP_SCRAMBLE_MESSAGES,	// phreak
	OP_UNSCRAMBLE_MESSAGES,	// phreak
	OP_CUTSCENES_SET_CUTSCENE_BARS,	// WMC
	OP_CUTSCENES_UNSET_CUTSCENE_BARS,	// WMC

	OP_CUTSCENES_FADE_IN,	// WMC
	OP_CUTSCENES_FADE_OUT,	// WMC
	OP_CUTSCENES_SET_CAMERA_POSITION,	// WMC
	OP_CUTSCENES_SET_CAMERA_FACING,	// WMC
	OP_CUTSCENES_SET_CAMERA_FACING_OBJECT,	// WMC
	OP_CUTSCENES_SET_CAMERA_ROTATION,	// WMC
	OP_CUTSCENES_SET_FOV,	// WMC
	OP_CUTSCENES_RESET_FOV,	// WMC
	OP_CUTSCENES_RESET_CAMERA,	// WMC
	OP_CUTSCENES_SHOW_SUBTITLE,	// WMC / deprecated
	OP_CUTSCENES_SET_TIME_COMPRESSION,	// WMC
	OP_CUTSCENES_RESET_TIME_COMPRESSION,	// WMC
	OP_CUTSCENES_FORCE_PERSPECTIVE,	// WMC
	OP_ALLOW_PHOTO_MODE,
	OP_JUMP_NODE_SET_JUMPNODE_NAME,	// CommanderDJ
	OP_JUMP_NODE_SET_JUMPNODE_DISPLAY_NAME,
	OP_JUMP_NODE_SET_JUMPNODE_COLOR,	// WMC
	OP_JUMP_NODE_SET_JUMPNODE_MODEL,	// WMC
	OP_JUMP_NODE_SHOW_JUMPNODE,	// WMC
	
	OP_JUMP_NODE_HIDE_JUMPNODE,	// WMC
	OP_SHIP_GUARDIAN_THRESHOLD,	// Goober5000
	OP_SHIP_SUBSYS_GUARDIAN_THRESHOLD,	// Goober5000
	OP_SET_GUARD_RANGE, //MjnMixael
	OP_SET_SKYBOX_MODEL, // taylor
	OP_SHIP_CREATE,
	OP_PROP_CREATE,     // MjnMixael
	OP_WEAPON_CREATE,	// Goober5000
	OP_SET_OBJECT_SPEED_X, // Deprecated by wookieejedi
	OP_SET_OBJECT_SPEED_Y, // Deprecated by wookieejedi
	OP_SET_OBJECT_SPEED_Z, // Deprecated by wookieejedi
	OP_MISSION_SET_NEBULA,
	OP_ADD_BACKGROUND_BITMAP,
	OP_REMOVE_BACKGROUND_BITMAP,
	OP_ADD_SUN_BITMAP,
	OP_REMOVE_SUN_BITMAP,
	OP_NEBULA_CHANGE_STORM,
	OP_NEBULA_TOGGLE_POOF,
	OP_NEBULA_SET_POOFS,
	OP_NEBULA_FADE_POOF,
	OP_NEBULA_FADE_POOFS,
	OP_VOLUMETRICS_TOGGLE,
	
	OP_TURRET_CHANGE_WEAPON,
	OP_TURRET_SET_TARGET_ORDER,
	OP_SHIP_TURRET_TARGET_ORDER,
	OP_SET_PRIMARY_AMMO, // Karajorma
	OP_SET_SECONDARY_AMMO, // Karajorma
	OP_SHIP_BOMB_TARGETABLE,	//WMC
	OP_SHIP_BOMB_UNTARGETABLE,	//WMC
	OP_SHIP_SUBSYS_TARGETABLE,	// Goober5000
	OP_SHIP_SUBSYS_UNTARGETABLE,	// Goober5000
	OP_SET_DEATH_MESSAGE,	// Goober5000
	OP_SET_PRIMARY_WEAPON, // Karajorma
	OP_SET_SECONDARY_WEAPON, // Karajorma
	OP_DISABLE_BUILTIN_MESSAGES, // Karajorma
	OP_ENABLE_BUILTIN_MESSAGES, // Karajorma
	OP_LOCK_PRIMARY_WEAPON, // Karajorma
	OP_UNLOCK_PRIMARY_WEAPON, // Karajorma
	
	OP_LOCK_SECONDARY_WEAPON, // Karajorma
	OP_UNLOCK_SECONDARY_WEAPON, // Karajorma
	OP_SET_CAMERA_SHUDDER,	// Goober5000
	OP_SET_FRIENDLY_DAMAGE_CAPS, // Kestrellius
	OP_ALLOW_TREASON, // Karajorma
	OP_SHIP_COPY_DAMAGE,	// Goober5000
	OP_CHANGE_SUBSYSTEM_NAME,	// Karajorma
	OP_SET_PERSONA, // Karajorma
	OP_CHANGE_PLAYER_SCORE, // Karajorma
	OP_CHANGE_TEAM_SCORE, // Karajorma
	OP_CUTSCENES_SET_CAMERA_FOV,	// WMC
	OP_CUTSCENES_SET_CAMERA, // WMC
	OP_CUTSCENES_SET_CAMERA_HOST, // WMC
	OP_CUTSCENES_SET_CAMERA_TARGET, // WMC
	OP_LOCK_AFTERBURNER, // KeldorKatarn
	OP_UNLOCK_AFTERBURNER, // KeldorKatarn
	OP_SHIP_CHANGE_CALLSIGN,	// FUBAR
	
	OP_SET_RESPAWNS, // Karajorma
	OP_SET_AFTERBURNER_ENERGY, // Karajorma
	OP_SET_WEAPON_ENERGY, // Karajorma
	OP_SET_SHIELD_ENERGY, // Karajorma
	OP_SET_AMBIENT_LIGHT, // Karajorma
	OP_CHANGE_IFF_COLOR, // Wanderer
	OP_TURRET_SUBSYS_TARGET_DISABLE, // Wanderer
	OP_TURRET_SUBSYS_TARGET_ENABLE, // Wanderer
	OP_CLEAR_WEAPONS, // Wanderer
	OP_SHIP_MANEUVER, // Wanderer 
	OP_SHIP_ROT_MANEUVER, // Wanderer
	OP_SHIP_LAT_MANEUVER, // Wanderer
	OP_GET_VARIABLE_BY_INDEX, // Goober5000
	OP_SET_VARIABLE_BY_INDEX, // Goober5000
	OP_SET_POST_EFFECT, // Hery
	OP_TURRET_SET_OPTIMUM_RANGE, // FUBAR
	
	OP_TURRET_SET_DIRECTION_PREFERENCE, // FUBAR
	OP_TURRET_SET_TARGET_PRIORITIES, // FUBAR
	OP_SET_ARMOR_TYPE, // FUBAR
	OP_CUTSCENES_SHOW_SUBTITLE_TEXT,	// Goober5000
	OP_CUTSCENES_SHOW_SUBTITLE_IMAGE,	// Goober5000
	OP_HUD_DISPLAY_GAUGE,
	OP_SET_SOUND_ENVIRONMENT,	// Taylor
	OP_UPDATE_SOUND_ENVIRONMENT,	// Taylor
	OP_SET_EXPLOSION_OPTION,	// Goober5000
	OP_ADJUST_AUDIO_VOLUME, // The E
	OP_FORCE_GLIDE, // The E
	OP_TURRET_SET_RATE_OF_FIRE, // FUBAR
	OP_HUD_SET_MESSAGE, // The E
	OP_SHIP_SUBSYS_NO_REPLACE, // FUBAR
	OP_SET_IMMOBILE,	// Goober5000
	OP_SET_MOBILE,	// Goober5000
	
	OP_SHIP_SUBSYS_NO_LIVE_DEBRIS, // FUBAR
	OP_SHIP_SUBSYS_VANISHED, // FUBAR
	OP_SHIP_SUBSYS_IGNORE_IF_DEAD, // FUBAR
	OP_HUD_SET_DIRECTIVE, // The E
	OP_HUD_GAUGE_SET_ACTIVE, // The E - slightly deprecated
	OP_HUD_ACTIVATE_GAUGE_TYPE, // The E - slightly deprecated
	OP_SET_OBJECT_ORIENTATION,	// Goober5000
	OP_STRING_CONCATENATE,	// Goober5000
	OP_INT_TO_STRING, // Goober5000
	OP_WEAPON_SET_DAMAGE_TYPE, // FUBAR
	OP_SHIP_SET_DAMAGE_TYPE, // FUBAR
	OP_SHIP_SHOCKWAVE_SET_DAMAGE_TYPE, // FUBAR
	OP_FIELD_SET_DAMAGE_TYPE, // FUBAR
	OP_TURRET_PROTECT_SHIP,	// Goober5000
	OP_TURRET_UNPROTECT_SHIP,	// Goober5000
	OP_DISABLE_ETS, // The E
	
	OP_ENABLE_ETS, // The E
	OP_NAV_ADD_WAYPOINT,	// Kazan
	OP_NAV_ADD_SHIP,	// Kazan
	OP_NAV_DEL,	// Kazan
	OP_NAV_HIDE,	// Kazan
	OP_NAV_RESTRICT,	// Kazan
	OP_NAV_UNHIDE,	// Kazan
	OP_NAV_UNRESTRICT,	// Kazan
	OP_NAV_SET_VISITED,	// Kazan
	OP_NAV_SET_CARRY,	// Kazan
	OP_NAV_UNSET_CARRY,	// Kazan
	OP_NAV_UNSET_VISITED,	// Kazan
	OP_NAV_SET_NEEDSLINK,	// Kazan
	OP_NAV_UNSET_NEEDSLINK,	// Kazan
	OP_NAV_USECINEMATICS,	// Kazan
	OP_NAV_USEAP,	// Kazan
	
	// OP_CATEGORY_CHANGE2
	
	OP_STRING_GET_SUBSTRING,	// Goober5000
	OP_STRING_SET_SUBSTRING,	// Goober5000
	OP_SET_NUM_COUNTERMEASURES, // Karajorma
	OP_ADD_TO_COLGROUP, // The E
	OP_REMOVE_FROM_COLGROUP, // The E
	OP_GET_COLGROUP_ID, // The E
	OP_SHIP_EFFECT, // Valathil
	OP_CLEAR_SUBTITLES, // The E
	OP_BEAM_FIRE_COORDS,	// Goober5000
	OP_SET_DOCKED, // Sushi
	OP_SET_THRUSTERS, // The E
	OP_TRIGGER_SUBMODEL_ANIMATION,	// Goober5000
	OP_HUD_CLEAR_MESSAGES, // Swifty
	OP_SET_PLAYER_ORDERS,	// Karajorma
	OP_SUPERNOVA_STOP, //CommanderDJ
	OP_SET_PLAYER_THROTTLE_SPEED, //CommanderDJ
	
	OP_SET_DEBRIEFING_TOGGLED,	// Goober5000
	OP_SET_SUBSPACE_DRIVE,	// Goober5000
	OP_SET_ARRIVAL_INFO,	// Goober5000
	OP_SET_DEPARTURE_INFO,	// Goober5000
	OP_SET_SKYBOX_ORIENT,	// Goober5000
	OP_DESTROY_INSTANTLY,	// Admiral MS
	OP_DESTROY_SUBSYS_INSTANTLY,	// Admiral MS
	OP_DEBUG,	// Karajorma
	OP_SET_MISSION_MOOD,	// Karajorma
	OP_NAV_SELECT, 	// Talon1024
	OP_NAV_UNSELECT, 	// Talon1024
	OP_ALTER_SHIP_FLAG,	// Karajorma
	OP_CHANGE_TEAM_COLOR,	// The E
	OP_NEBULA_CHANGE_PATTERN,	// Axem
	OP_SET_WING_FORMATION,	// Goober5000
	OP_TECH_ADD_INTEL_XSTR,	// Goober5000
	
	OP_COPY_VARIABLE_FROM_INDEX, // Goober5000
	OP_COPY_VARIABLE_BETWEEN_INDEXES, // Goober5000
	OP_GET_ETS_VALUE,	// niffiwan
	OP_SET_ETS_VALUES,	// niffiwan
	OP_CALL_SSM_STRIKE, // X3N0-Life-Form
	OP_OVERRIDE_MOTION_DEBRIS,    // The E
	OP_HUD_SET_CUSTOM_GAUGE_ACTIVE, 	// The E, just revamped a bit by Axem
	OP_HUD_SET_BUILTIN_GAUGE_ACTIVE, 	// The E, just revamped a bit by Axem
	OP_SCRIPT_EVAL_MULTI,	// Karajorma
	OP_PAUSE_SOUND_FROM_FILE,	// Goober5000
	OP_SCRIPT_EVAL_BLOCK, // niffiwan
	OP_BEAM_FLOATING_FIRE,	// MageKing17
	OP_TURRET_SET_PRIMARY_AMMO,	// DahBlount, part of the turret ammo changes
	OP_TURRET_SET_SECONDARY_AMMO,	// DahBlount, part of the turret ammo changes
	OP_JETTISON_CARGO_NEW,	// Goober5000
	OP_STRING_CONCATENATE_BLOCK,	// Goober5000
	
	OP_MODIFY_VARIABLE_XSTR,	// m!m
	OP_RESET_POST_EFFECTS,	// Goober5000
	OP_ADD_REMOVE_HOTKEY,    // wookieejedi
	OP_TECH_REMOVE_INTEL_XSTR,    // wookieejedi
	OP_TECH_REMOVE_INTEL,   // wookieejedi
	OP_CHANGE_BACKGROUND,	// Goober5000
	OP_CLEAR_DEBRIS,	// Goober5000
	OP_SET_DEBRIEFING_PERSONA,	// Goober5000
	OP_SET_TRAITOR_OVERRIDE,	//MjnMixael
	OP_ADD_TO_COLGROUP_NEW,	// Goober5000
	OP_REMOVE_FROM_COLGROUP_NEW,	// Goober5000
	OP_GET_POWER_OUTPUT,	// The E
	OP_TURRET_SET_FORCED_TARGET,	// Asteroth
	OP_TURRET_SET_FORCED_SUBSYS_TARGET,	// Asteroth
	OP_TURRET_CLEAR_FORCED_TARGET,	// Asteroth
	OP_SEND_MESSAGE_CHAIN,	// Goober5000
	OP_TURRET_SET_INACCURACY,	// Asteroth
	
	OP_REPLACE_TEXTURE,	// Lafiel
	OP_REPLACE_TEXTURE_SKYBOX,	// Lafiel
	OP_NEBULA_CHANGE_FOG_COLOR,	// Asteroth
	OP_SET_ALPHA_MULT,	// Lafiel
	OP_DESTROY_INSTANTLY_WITH_DEBRIS,	// Asteroth
	OP_TRIGGER_ANIMATION_NEW,	// Lafiel
	OP_UPDATE_MOVEABLE,	// Lafiel
	OP_ADVANCE_MOVEABLE, // Lafiel
	OP_NAV_SET_COLOR, 	// Goober5000
	OP_NAV_SET_VISITED_COLOR, 	// Goober5000
	OP_CONTAINER_ADD_TO_LIST,	// Karajorma/jg18
	OP_CONTAINER_REMOVE_FROM_LIST,	// Karajorma/jg18
	OP_CONTAINER_ADD_TO_MAP,	// Karajorma/jg18
	OP_CONTAINER_REMOVE_FROM_MAP,	// Karajorma/jg18
	OP_CONTAINER_GET_MAP_KEYS,	// Karajorma/jg18
	OP_CLEAR_CONTAINER,	// Karajorma/jg18
	OP_ADD_BACKGROUND_BITMAP_NEW,	// Goober5000
	OP_ADD_SUN_BITMAP_NEW,	// Goober5000
	
	OP_CANCEL_FUTURE_WAVES,	// naomimyselfandi
	OP_COPY_CONTAINER,	// jg18
	OP_APPLY_CONTAINER_FILTER,	// jg18
	OP_STOP_LOOPING_ANIMATION,	// Lafiel
	OP_LOCK_TRANSLATING_SUBSYSTEM,	// Goober5000
	OP_FREE_TRANSLATING_SUBSYSTEM,	// Goober5000
	OP_REVERSE_TRANSLATING_SUBSYSTEM,	// Goober5000
	OP_TRANSLATING_SUBSYS_SET_SPEED,	// Goober5000
	OP_ALTER_WING_FLAG,	// Goober5000
	OP_TOGGLE_ASTEROID_FIELD,	// MjnMixael
	OP_HUD_FORCE_SENSOR_STATIC,	// MjnMixael
	OP_HUD_FORCE_EMP_EFFECT, // MjnMixael
	OP_SET_GRAVITY_ACCEL,	// Asteroth
	OP_FORCE_REARM,	 // MjnMixael
	OP_ABORT_REARM,  // MjnMixael
	OP_SET_ORDER_ALLOWED_TARGET,	// MjnMixael
	OP_ENABLE_GENERAL_ORDERS,	// MjnMixael
	OP_VALIDATE_GENERAL_ORDERS,		// MjnMixael
	OP_USED_CHEAT,	// Kiloku
	OP_SET_ASTEROID_FIELD,	// MjnMixael
	OP_SET_DEBRIS_FIELD,	// MjnMixael
	OP_CONFIG_ASTEROID_FIELD,  // MjnMixael
	OP_CONFIG_DEBRIS_FIELD,  // MjnMixael
	OP_CONFIG_FIELD_TARGETS,  // MjnMixael
	OP_SET_MOTION_DEBRIS,   // MjnMixael
	OP_GOOD_PRIMARY_TIME,	// plieblang
	OP_SET_SKYBOX_ALPHA,	// Goober5000
	OP_NEBULA_SET_RANGE,	// Goober5000
	OP_SET_SQUADRON_WINGS,	// Goober5000
	
	// OP_CATEGORY_AI
	// defined for AI goals
	
	OP_AI_CHASE,
	OP_AI_DOCK,
	OP_AI_UNDOCK,
	OP_AI_WARP_OUT,
	OP_AI_WAYPOINTS,
	OP_AI_WAYPOINTS_ONCE,
	OP_AI_DESTROY_SUBSYS,
	OP_AI_DISABLE_SHIP,
	OP_AI_DISARM_SHIP,
	OP_AI_GUARD,
	OP_AI_CHASE_ANY,
	OP_AI_EVADE_SHIP,
	OP_AI_STAY_NEAR_SHIP,
	OP_AI_KEEP_SAFE_DISTANCE,
	OP_AI_IGNORE,
	OP_AI_STAY_STILL,
	OP_AI_PLAY_DEAD,
	OP_AI_IGNORE_NEW,	// Goober5000
	OP_AI_FORM_ON_WING, // The E
	OP_AI_CHASE_SHIP_CLASS, // Goober5000
	OP_AI_CHASE_SHIP_TYPE,	// LuytenKy
	OP_AI_PLAY_DEAD_PERSISTENT,	// Goober5000
	OP_AI_FLY_TO_SHIP,	// Goober5000
	OP_AI_REARM_REPAIR,	// Goober5000
	OP_AI_DISABLE_SHIP_TACTICAL,	// Goober5000
	OP_AI_DISARM_SHIP_TACTICAL,	// Goober5000

	// OP_CATEGORY_UNLISTED
	
	OP_GOALS_ID,
	OP_NEXT_MISSION,		// used in campaign files for branching
	OP_IS_DESTROYED,
	OP_IS_SUBSYSTEM_DESTROYED,
	OP_IS_DISABLED,
	OP_IS_DISARMED,
	OP_HAS_DOCKED,
	OP_HAS_UNDOCKED,
	OP_HAS_ARRIVED,
	OP_HAS_DEPARTED,
	OP_WAYPOINTS_DONE,
	OP_ADD_SHIP_GOAL,
	OP_CLEAR_SHIP_GOALS,
	OP_ADD_WING_GOAL,
	OP_CLEAR_WING_GOALS,
	OP_AI_CHASE_WING,
	OP_AI_GUARD_WING,
	OP_EVENT_TRUE,
	OP_EVENT_FALSE,
	OP_PREVIOUS_GOAL_INCOMPLETE,
	OP_PREVIOUS_EVENT_INCOMPLETE,
	OP_AI_WARP,
	OP_IS_CARGO_KNOWN,
	OP_COND,
	OP_END_OF_CAMPAIGN,
	
	// OP_CATEGORY_TRAINING
	// training sexps
	
	OP_KEY_PRESSED,
	OP_KEY_RESET,
	OP_TARGETED,
	OP_SPEED,
	OP_FACING,
	OP_ORDER,
	OP_WAYPOINT_MISSED,
	OP_PATH_FLOWN,
	OP_WAYPOINT_TWICE,
	OP_TRAINING_MSG,
	OP_FLASH_HUD_GAUGE,
	OP_SPECIAL_CHECK,
	OP_SECONDARIES_DEPLETED,
	OP_FACING2,
	OP_PRIMARIES_DEPLETED,	// Goober5000
	OP_MISSILE_LOCKED,	// Sesquipedalian
	OP_SET_TRAINING_CONTEXT_FLY_PATH,
	OP_SET_TRAINING_CONTEXT_SPEED,
	OP_KEY_RESET_MULTIPLE,	// Goober5000
	OP_RESET_ORDERS, // Karajorma
	OP_QUERY_ORDERS, // Karajorma
	OP_NODE_TARGETED, // FUBAR
	OP_IGNORE_KEY, // Karajorma
	
	// this should come after every operator
	First_available_operator_id
};

// defines for string constants
#define SEXP_HULL_STRING			"Hull"
#define SEXP_SIM_HULL_STRING		"Simulated Hull"
#define SEXP_SHIELD_STRING			"Shields"
#define SEXP_ALL_GENERIC_SUBSYSTEM_STRING	"<all %s>"
#define SEXP_ALL_SUBSYSTEMS_STRING			"<all subsystems>"
#define SEXP_ARGUMENT_STRING		"<argument>"
#define SEXP_NONE_STRING			"<none>"
#define SEXP_ANY_STRING				"<any string>"
#define SEXP_ALL_BANKS_STRING		"<all weapon banks>"

// macros for accessing sexpression atoms
/**
 * @brief Returns the first element of a SEXP list
 *
 * The name CAR originates from the original LISP language where it was a function which retrieved the first element of
 * a list.
 *
 * @see https://en.wikipedia.org/wiki/CAR_and_CDR
 */
#define CAR(n)		((n < 0) ? -1 : Sexp_nodes[n].first)
/**
 * @brief Returns the rest of a SEXP list. The rest is everything starting from the second element.
 *
 * The name CDR originates from the original LISP language where it was a function which retrieved the "rest" of a list.
 *
 * @see https://en.wikipedia.org/wiki/CAR_and_CDR
 */
#define CDR(n)		((n < 0) ? -1 : Sexp_nodes[n].rest)
#define CADR(n)		CAR(CDR(n))
// #define CTEXT(n)	(Sexp_nodes[n].text)
const char *CTEXT(int n);

// added by Goober5000
#define CDDR(n)		CDR(CDR(n))
#define CDDDR(n)	CDR(CDDR(n))
#define CDDDDR(n)	CDR(CDDDR(n))
#define CDDDDDR(n)	CDR(CDDDDR(n))
#define CADDR(n)	CAR(CDDR(n))
#define CADDDR(n)	CAR(CDDDR(n))
#define CADDDDR(n)	CAR(CDDDDR(n))
#define CADDDDDR(n)	CAR(CDDDDDR(n))

enum class sexp_ref_type
{
	SHIP = 1,
	WING,
	PLAYER,
	WAYPOINT,
	WAYPOINT_PATH
};

enum class sexp_src
{
	NONE = 0,
	SHIP_ARRIVAL,
	SHIP_DEPARTURE,
	WING_ARRIVAL,
	WING_DEPARTURE,
	EVENT,
	MISSION_GOAL,
	MISSION_CUTSCENE,
	SHIP_ORDER,
	WING_ORDER,
	DEBRIEFING,
	BRIEFING,
	UNKNOWN
};

enum class sexp_mode
{
	GENERAL = 0,
	CAMPAIGN
};

// defines for type field of sexp nodes.  The actual type of the node will be stored in the lower
// two bytes of the field.  The upper two bytes will be used for flags (bleah...)
// Be sure not to conflict with type field of sexp_variable
#define SEXP_NOT_USED		0
#define SEXP_LIST				1
#define SEXP_ATOM				2

// flags for sexpressions -- masked onto the end of the type field
#define SEXP_FLAG_PERSISTENT				(1<<31)		// should this sexp node be kept across missions, i.e. not freed -- note, NOT the same as variable/container persistence
#define SEXP_FLAG_VARIABLE					(1<<30)

// sexp variable definitions
#define SEXP_VARIABLE_CHAR					('@')
// defines for type field of sexp_variable.  Be sure not to conflict with type field of sexp_node
#define SEXP_VARIABLE_NUMBER				(1<<4)	//	(0x0010)
#define SEXP_VARIABLE_STRING				(1<<5)	//	(0x0020)
#define SEXP_VARIABLE_UNKNOWN				(1<<6)	//	(0x0040)
#define SEXP_VARIABLE_NOT_USED				(1<<7)	//	(0x0080)

#define SEXP_VARIABLE_BLOCK					(1<<0)	//	(0x0001)
/*
#define SEXP_VARIABLE_BLOCK_EXP				(1<<1)	//	(0x0002)
#define SEXP_VARIABLE_BLOCK_HIT				(1<<2)	//	(0x0004)
*/
#define SEXP_VARIABLE_SAVE_ON_MISSION_CLOSE		(1<<3)	//	(0x0008)

// Goober5000 - hopefully this should work and not conflict with anything
#define SEXP_VARIABLE_SAVE_ON_MISSION_PROGRESS	(1<<29)	//	(0x0100)
//Karajorma
#define SEXP_VARIABLE_NETWORK				(1<<28)
#define SEXP_VARIABLE_SAVE_TO_PLAYER_FILE	(1<<27)

// There are three types of persistence for variables and containers:
// 1. No persistence: the value is only kept for the duration of a mission's gameplay
// 2. Campaign-persistence: the value is scoped to a campaign, and has no value outside the campaign
// 3. Player-persistence: the value is scoped to the player/pilot file, and can be referenced in any campaign
// And there are two ways that persistent variables/containers are saved:
// 1. When the mission progresses with an outcome that is "accepted" by the player
// 2. When the mission closes in any way (progress, quit, restart)
// So, there can be four combinations of persistence (campaign/player times progress/close).  When persistent variables
// were first implemented, there was an assumption that player-persistence implied save-on-close, and that
// campaign-persistence implied save-on-progress, and the original mission parsing code reflects that.  But after the
// 2018 rework, either type can be used with either save.  The 2018 rework also introduced new terminology:
// "eternal" means player-persistent, and "non-eternal" means campaign-persistent.
#define SEXP_VARIABLE_IS_PERSISTENT (SEXP_VARIABLE_SAVE_ON_MISSION_PROGRESS|SEXP_VARIABLE_SAVE_ON_MISSION_CLOSE)

#define BLOCK_EXP_SIZE					6
#define INNER_RAD							0
#define OUTER_RAD							1
#define DAMAGE								2
#define BLAST								3
#define PROPAGATE							4
#define SHOCK_SPEED						5

#define BLOCK_HIT_SIZE					2
#define SHIELD_STRENGTH					0
#define HULL_STRENGTH					1


#define SEXP_VARIABLE_SET				(0x0100)
#define SEXP_VARIABLE_MODIFIED		(0x0200)

#define SEXP_TYPE_MASK(t)	(t & 0x00ff)
#define SEXP_NODE_TYPE(n)	(Sexp_nodes[n].type & 0x00ff)

// defines for subtypes of atoms
#define SEXP_ATOM_LIST				0
#define SEXP_ATOM_OPERATOR			1
#define SEXP_ATOM_NUMBER			2
#define SEXP_ATOM_STRING			3
#define SEXP_ATOM_CONTAINER_NAME	4
#define SEXP_ATOM_CONTAINER_DATA	5

// defines to short circuit evaluation when possible. Also used when goals can't
// be satisfied yet because ship (or wing) hasn't been created yet.

#define SEXP_TRUE			1
#define SEXP_FALSE			0

// Goober5000: changed these to unlikely values, because now we have sexps using negative numbers
#define SEXP_KNOWN_FALSE	(INT_MIN+10)
#define SEXP_KNOWN_TRUE		(INT_MIN+11)
#define SEXP_UNKNOWN		(INT_MIN+12)
#define SEXP_NAN			(INT_MIN+13)	// not a number -- used when ships/wing part of boolean and haven't arrived yet
#define SEXP_NAN_FOREVER	(INT_MIN+14)	// not a number and will never change -- used to falsify boolean sexpressions
#define SEXP_CANT_EVAL		(INT_MIN+15)	// can't evaluate yet for whatever reason (acts like false)
#define SEXP_NUM_EVAL		(INT_MIN+16)	// already completed an arithmetic operation and result is stored
// in case we want to test for any of the above
#define SEXP_UNLIKELY_RETURN_VALUE_BOUND		(INT_MIN+17)

// defines for check_sexp_syntax
enum sexp_error_check
{
	SEXP_CHECK_NO_ERROR = 0,

	SEXP_CHECK_NONOP_ARGS,              // non-operator has arguments
	SEXP_CHECK_OP_EXPECTED,             // operator expected, but found data instead
	SEXP_CHECK_UNKNOWN_OP,              // unrecognized operator
	SEXP_CHECK_TYPE_MISMATCH,           // return type or data type mismatch
	SEXP_CHECK_BAD_ARG_COUNT,           // argument count in incorrect
	SEXP_CHECK_UNKNOWN_TYPE,            // unrecognized return type of data type

	SEXP_CHECK_INVALID_NUM = 101,       // number is not valid
	SEXP_CHECK_INVALID_SHIP,            // invalid ship name
	SEXP_CHECK_INVALID_PROP,            // invalid prop name
	SEXP_CHECK_INVALID_WING,            // invalid wing name
	SEXP_CHECK_INVALID_SUBSYS,          // invalid subsystem
	SEXP_CHECK_INVALID_IFF,             // invalid iff string
	SEXP_CHECK_INVALID_POINT,           // invalid point
	SEXP_CHECK_NEGATIVE_NUM,            // negative number wasn't allowed
	SEXP_CHECK_INVALID_SHIP_WING,       // invalid ship/wing
	SEXP_CHECK_INVALID_SHIP_PROP,       // invalid ship/prop
	SEXP_CHECK_INVALID_SHIP_TYPE,       // invalid ship type
	SEXP_CHECK_UNKNOWN_MESSAGE,         // invalid message
	SEXP_CHECK_INVALID_PRIORITY,        // invalid priority for a message
	SEXP_CHECK_INVALID_MISSION_NAME,    // invalid mission name
	SEXP_CHECK_INVALID_GOAL_NAME,       // invalid goal name
	SEXP_CHECK_INVALID_LEVEL,           // mission level too high in campaign
	SEXP_CHECK_INVALID_MSG_SOURCE,      // invalid 'who-from' for a message being sent
	SEXP_CHECK_INVALID_DOCKER_POINT,
	SEXP_CHECK_INVALID_DOCKEE_POINT,
	SEXP_CHECK_ORDER_NOT_ALLOWED,       // ship goal (order) isn't allowed for given ship
	SEXP_CHECK_DOCKING_NOT_ALLOWED,
	SEXP_CHECK_NUM_RANGE_INVALID,
	SEXP_CHECK_INVALID_EVENT_NAME,
	SEXP_CHECK_INVALID_SKILL_LEVEL,
	SEXP_CHECK_INVALID_MEDAL_NAME,
	SEXP_CHECK_INVALID_WEAPON_NAME,
	SEXP_CHECK_INVALID_SHIP_CLASS_NAME,
	SEXP_CHECK_INVALID_CUSTOM_HUD_GAUGE,
	SEXP_CHECK_INVALID_JUMP_NODE,
	SEXP_CHECK_INVALID_VARIABLE,
	SEXP_CHECK_INVALID_AI_CLASS,
	SEXP_CHECK_UNKNOWN_ERROR,
	SEXP_CHECK_INVALID_SUPPORT_SHIP_CLASS,
	SEXP_CHECK_INVALID_SHIP_WITH_BAY,
	SEXP_CHECK_INVALID_ARRIVAL_LOCATION,
	SEXP_CHECK_INVALID_DEPARTURE_LOCATION,
	SEXP_CHECK_INVALID_ARRIVAL_ANCHOR_ALL,
	SEXP_CHECK_INVALID_SOUNDTRACK_NAME,
	SEXP_CHECK_INVALID_INTEL_NAME,
	SEXP_CHECK_INVALID_SKYBOX_NAME,
	SEXP_CHECK_INVALID_PERSONA_NAME,
	SEXP_CHECK_INVALID_VARIABLE_TYPE,
	SEXP_CHECK_INVALID_SUBSYS_TYPE,
	SEXP_CHECK_INVALID_FONT,
	SEXP_CHECK_INVALID_HUD_ELEMENT,
	SEXP_CHECK_INVALID_SOUND_ENVIRONMENT,
	SEXP_CHECK_INVALID_SOUND_ENVIRONMENT_OPTION,
	SEXP_CHECK_INVALID_EXPLOSION_OPTION,
	SEXP_CHECK_INVALID_SHIP_EFFECT,
	SEXP_CHECK_INVALID_TURRET_TARGET_ORDER,
	SEXP_CHECK_INVALID_TURRET_TYPE,
	SEXP_CHECK_INVALID_ARMOR_TYPE,
	SEXP_CHECK_INVALID_DAMAGE_TYPE,
	SEXP_CHECK_INVALID_TARGET_PRIORITIES,
	SEXP_CHECK_INVALID_AUDIO_VOLUME_OPTION,
	SEXP_CHECK_INVALID_BUILTIN_HUD_GAUGE,
	SEXP_CHECK_INVALID_ANIMATION_TYPE,
	SEXP_CHECK_INVALID_MISSION_MOOD,
	SEXP_CHECK_INVALID_SHIP_FLAG,
	SEXP_CHECK_INVALID_TEAM_COLOR,
	SEXP_CHECK_INVALID_SKYBOX_FLAG,
	SEXP_CHECK_INVALID_GAME_SND,
	SEXP_CHECK_INVALID_SSM_CLASS,
	SEXP_CHECK_INVALID_FIREBALL,
	SEXP_CHECK_INVALID_SPECIES,
	SEXP_CHECK_INVALID_FUNCTIONAL_WHEN_EVAL_TYPE,
	SEXP_CHECK_MISPLACED_SPECIAL_ARGUMENT,
	SEXP_CHECK_AMBIGUOUS_GOAL_NAME,
	SEXP_CHECK_AMBIGUOUS_EVENT_NAME,
	SEXP_CHECK_INVALID_CONTAINER,
	SEXP_CHECK_MISSING_CONTAINER_MODIFIER,
	SEXP_CHECK_INVALID_LIST_MODIFIER,
	SEXP_CHECK_WRONG_MAP_KEY_TYPE,
	SEXP_CHECK_WRONG_CONTAINER_TYPE,
	SEXP_CHECK_INVALID_ANIMATION,
	SEXP_CHECK_WRONG_CONTAINER_DATA_TYPE,
	SEXP_CHECK_INVALID_SPECIAL_ARG_TYPE,
	SEXP_CHECK_INVALID_AWACS_SUBSYS,
	SEXP_CHECK_INVALID_ROTATING_SUBSYS,
	SEXP_CHECK_INVALID_TRANSLATING_SUBSYS,
	SEXP_CHECK_INVALID_ANY_HUD_GAUGE,
	SEXP_CHECK_INVALID_WING_FLAG,
	SEXP_CHECK_INVALID_WING_FORMATION,
	SEXP_CHECK_INVALID_ASTEROID,
	SEXP_CHECK_INVALID_MOTION_DEBRIS,
	SEXP_CHECK_INVALID_BOLT_TYPE,
	SEXP_CHECK_INVALID_TRAITOR_OVERRIDE,
	SEXP_CHECK_INVALID_LUA_GENERAL_ORDER,
	SEXP_CHECK_INVALID_SHIP_POINT,
	SEXP_CHECK_INVALID_SHIP_WING_SHIPONTEAM_POINT,
	SEXP_CHECK_INVALID_SHIP_WING_POINT,
	SEXP_CHECK_INVALID_ORDER_RECIPIENT,
	SEXP_CHECK_INVALID_SHIP_WING_WHOLETEAM,
	SEXP_CHECK_MUST_BE_INTEGER,
	SEXP_CHECK_INVALID_CUSTOM_STRING,
	SEXP_CHECK_INVALID_MESSAGE_TYPE,
	SEXP_CHECK_POTENTIAL_ISSUE,
};


#define TRAINING_CONTEXT_SPEED		(1<<0)
#define TRAINING_CONTEXT_FLY_PATH	(1<<1)

// numbers used in special_training_check() function
#define SPECIAL_CHECK_TRAINING_FAILURE	2000

enum ai_goal_mode : uint8_t;

struct sexp_ai_goal_link {
	ai_goal_mode ai_goal;
	int op_code;
};


enum class sexp_oper_type
{
	NONE = 0,
	CONDITIONAL,
	ARGUMENT,
	ACTION,
	ARITHMETIC,
	BOOLEAN,
	INTEGER,
	GOAL
};

typedef struct sexp_oper {
	SCP_string text;
	int	value;
	int	min, max;
	sexp_oper_type type;
} sexp_oper;

// Goober5000
struct sexp_cached_data
{
	int sexp_node_data_type = OPF_NONE;		// an OPF_ #define
	int numeric_literal = 0;				// i.e. a number
	int ship_registry_index = -1;			// because ship status is pretty common
	int other_index = -1;					// could be an IFF, a wing, a goal, or other index
	// jg18 - used to store result from sexp_container_CTEXT()
	char container_CTEXT_result[TOKEN_LENGTH] = "";

	sexp_cached_data() = default;

	sexp_cached_data(int _sexp_node_data_type)
		: sexp_node_data_type(_sexp_node_data_type)
	{}

	sexp_cached_data(int _sexp_node_data_type, int _other_index)
		: sexp_node_data_type(_sexp_node_data_type), other_index(_other_index)
	{}

	sexp_cached_data(int _sexp_node_data_type, int _numeric_literal, int _ship_registry_index)
		: sexp_node_data_type(_sexp_node_data_type), numeric_literal(_numeric_literal), ship_registry_index(_ship_registry_index)
	{}

	sexp_cached_data(int _sexp_node_data_type, const SCP_string &_container_CTEXT_result)
		: sexp_node_data_type(_sexp_node_data_type)
	{
		update_container_CTEXT_result(_container_CTEXT_result);
	}

	void update_container_CTEXT_result(const SCP_string &_container_CTEXT_result)
	{
		if (_container_CTEXT_result.empty()) {
			Warning(LOCATION, "assigning empty string to SEXP node text");
		} else if (_container_CTEXT_result.length() >= sizeof(container_CTEXT_result)) {
			Warning(LOCATION,
				"attempt to assign CTEXT() result %s which is too long (limit %d)",
				_container_CTEXT_result.c_str(),
				(int)(sizeof(container_CTEXT_result) - 1));
		}

		const auto length = _container_CTEXT_result.copy(container_CTEXT_result, sizeof(container_CTEXT_result) - 1);
		container_CTEXT_result[length] = 0;
	}
};

typedef struct sexp_node {
	char	text[TOKEN_LENGTH];
	int op_index;				// the index in the Operators array for the operator at this node (or -1 if not an operator)
	int	type;						// atom, list, or not used
	int	subtype;					// type of atom or list?
	int	first;					// if first parameter is sexp, index into Sexp_nodes
	int	rest;						// index into Sexp_nodes of rest of parameters
	int	value;					// known to be true, known to be false, or not known
	int flags;					// Goober5000

	sexp_cached_data *cache;	// Goober5000
	int cached_variable_index;	// Goober5000 - note, this can be used for special-arg nodes, not just variable nodes

	int duration_index;			// Goober5000 - only used if node is the is-true-for-duration operator
} sexp_node;

// Goober5000
#define SNF_ARGUMENT_VALID			(1<<0)
#define SNF_ARGUMENT_SELECT			(1<<1)
#define SNF_SPECIAL_ARG_IN_NODE		(1<<2)
#define SNF_SPECIAL_ARG_IN_TREE		(1<<3)
#define SNF_SPECIAL_ARG_NOT_IN_TREE	(1<<4)
#define SNF_CHECKED_ARG_FOR_VAR		(1<<5)
#define SNF_CHECKED_NODE_FOR_OPF_POSITIVE	(1<<6)
#define SNF_NODE_IS_OPF_POSITIVE	(1<<7)
#define SNF_DESCENDANT_OF_WHEN_ARG_OP		(1<<8)
#define SNF_NOT_DESCENDANT_OF_WHEN_ARG_OP	(1<<9)
#define SNF_DEFAULT_VALUE			SNF_ARGUMENT_VALID

typedef struct sexp_variable {
	int		type;
	char	text[TOKEN_LENGTH];
	char	variable_name[TOKEN_LENGTH];
} sexp_variable;

// next define used to eventually mark a directive as satisfied even though there may be more
// waves for a wing.  bascially a hack for the directives display.
#define DIRECTIVE_WING_ZERO		-999

// Goober5000 - it's dynamic now
//extern sexp_node Sexp_nodes[MAX_SEXP_NODES];

extern int Num_sexp_nodes;
extern sexp_node *Sexp_nodes;

extern sexp_variable Sexp_variables[MAX_SEXP_VARIABLES];
extern sexp_variable Block_variables[MAX_SEXP_VARIABLES];

extern SCP_vector<sexp_oper> Operators;
extern SCP_vector<int> Sorted_operator_indexes;
extern size_t Max_operator_length;

extern int Locked_sexp_true, Locked_sexp_false;
extern int Directive_count;
extern int Sexp_useful_number;  // a variable to pass useful info in from external modules
extern bool Assume_event_is_current;
extern int Sexp_clipboard;  // used by Fred

extern SCP_vector<int> Current_sexp_operator;


// event log stuff
extern SCP_vector<SCP_string> *Current_event_log_buffer;
extern SCP_vector<SCP_string> *Current_event_log_variable_buffer;
extern SCP_vector<SCP_string> *Current_event_log_container_buffer;
extern SCP_vector<SCP_string> *Current_event_log_argument_buffer;

extern void init_sexp();
extern void sexp_startup();
extern void sexp_shutdown();
extern int alloc_sexp(const char *text, int type, int subtype, int first, int rest);
extern int find_free_sexp();
extern int free_one_sexp(int num);
extern int free_sexp(int num, int calling_node = -1);
extern int free_sexp2(int num, int calling_node = -1);
extern int dup_sexp_chain(int node);
extern int cmp_sexp_chains(int node1, int node2);
extern int find_sexp_list(int num);
extern int find_parent_operator(int num);
extern int is_sexp_top_level( int node );

// Goober5000 - renamed these to be more clear, to prevent bugs :p
extern int get_operator_index(const char *token);
extern int get_operator_index(int node);
extern int get_operator_const(const char *token);
extern int get_operator_const(int node);
extern int find_operator_index(int op_const);

extern int check_sexp_syntax(int node, int return_type = OPR_BOOL, int recursive = 0, int *bad_node = 0 /*NULL*/, sexp_mode mode = sexp_mode::GENERAL);
extern int check_sexp_potential_issues(int node, int *bad_node, SCP_string &issue_msg);
extern int get_sexp_main(void);	//	Returns start node
extern int run_sexp(const char* sexpression, bool run_eval_num = false, bool *is_nan_or_nan_forever = nullptr); // debug and lua sexps
extern int stuff_sexp_variable_list();
extern int eval_sexp(int cur_node, int referenced_node = -1);
extern int eval_num(int n, bool &is_nan, bool &is_nan_forever);
extern gamesnd_id sexp_get_sound_index(int node);
extern bool is_sexp_true(int cur_node, int referenced_node = -1);
extern bool map_opf_to_opr(sexp_opf_t opf_type, sexp_opr_t &opr_type);
const char *opr_type_name(sexp_opr_t opr_type);
extern int query_operator_return_type(int op);
extern int query_operator_argument_type(int op, int argnum);
extern void update_sexp_references(const char *old_name, const char *new_name);
extern void update_sexp_references(const char *old_name, const char *new_name, int format);
extern std::pair<int, sexp_src> query_referenced_in_sexp(sexp_ref_type type, const char *name, int &node);
extern void stuff_sexp_text_string(SCP_string &dest, int node, int mode);
extern int build_sexp_string(SCP_string &accumulator, int cur_node, int level, int mode);
extern bool sexp_query_type_match(int opf, int opr);
extern int sexp_match_closest_operator(const SCP_string &str, int opf);
extern bool sexp_recoverable_error(int num);
extern const char *sexp_error_message(int num);
extern int count_free_sexp_nodes();


struct ship_registry_entry;
struct wing;

// Goober5000 - stuff with caching
// (included in the header file because Lua uses the first four)
extern const ship_registry_entry *eval_ship(int node);
extern const prop* eval_prop(int node);
extern wing *eval_wing(int node);
extern int sexp_get_variable_index(int node);
extern int sexp_atoi(int node);
extern bool sexp_can_construe_as_integer(int node);

// Goober5000 - for special-arg SEXPs
extern bool is_when_argument_op(int op_const);
extern bool is_argument_provider_op(int op_const);
extern bool is_implicit_argument_provider_op(int op_const); // jg18
extern int find_argument_provider(int node);

// functions to change the attributes of an sexpression tree to persistent or not persistent
extern void sexp_unmark_persistent( int n );
extern void sexp_mark_persistent( int n );
extern int verify_sexp_tree(int node);
extern int query_sexp_ai_goal_valid(int sexp_ai_goal, int ship);
int query_node_in_sexp(int node, int sexp);
void flush_sexp_tree(int node);

// sexp_variable
void sexp_modify_variable(const char *text, int index, bool sexp_callback = true);
int get_index_sexp_variable_name(const char *text);
int get_index_sexp_variable_name(SCP_string &text);	// Goober5000
int get_index_sexp_variable_name_special(const char *text);	// Goober5000
int get_index_sexp_variable_name_special(SCP_string &text, size_t startpos);	// Goober5000
bool sexp_replace_variable_names_with_values(char *text, int max_len);	// Goober5000
bool sexp_replace_variable_names_with_values(SCP_string &text);	// Goober5000
int get_nth_variable_index(int nth, int variable_type);	// Karajorma
int sexp_variable_count();
int sexp_variable_typed_count(int sexp_variables_index, int variable_type); // Karajorma
void sexp_variable_delete(int index);
void sexp_variable_sort();
void sexp_fred_modify_variable(const char *text, const char *var_name, int index, int type);
int sexp_add_variable(const char *text, const char *var_name, int type, int index=-1);
bool generate_special_explosion_block_variables();
int num_block_variables();
bool has_special_explosion_block_index(ship *shipp, int *index);

extern bool usable_in_campaign(int op_id);
extern int get_category(int op_id);
extern int category_of_subcategory(int subcategory_id);
extern int get_subcategory(int op_id);
extern const char *get_category_name(int category_id);

// Goober5000
extern void sexp_music_close();

//WMC - moved here from FRED
typedef struct sexp_help_struct {
	int id;
	SCP_string help;
} sexp_help_struct;

extern SCP_vector<sexp_help_struct> Sexp_help;

typedef struct op_menu_struct {
	SCP_string name;
	int id;
} op_menu_struct;

extern SCP_vector<op_menu_struct> op_menu;
extern SCP_vector<op_menu_struct> op_submenu;

//WMC
//Outputs sexp.html file
bool output_sexps(const char *filepath);

void multi_sexp_eval();

// Goober5000/Taylor
extern int Num_sound_environment_options;
extern const char *Sound_environment_option[];

// Goober5000
extern int Num_explosion_options;
extern const char *Explosion_option[];

extern int Num_functional_when_eval_types;
extern const char *Functional_when_eval_type[];

//The E
extern int Num_adjust_audio_options;
extern const char *Adjust_audio_options[];

extern int Num_skybox_flags;
extern const char *Skybox_flags[];

/** Global state variables for the hud-display-gauge sexp.
They all should be named Sexp_hud_display_*;
They all should follow the following symantics for the value of the
variable:
=0	don't show
=1	show until canceled
>1	timestamp when gauge should stop showing (set zero when expired)
\sa sexp_hud_display_warpout
*/
extern int Sexp_hud_display_warpout;

//Needed for scripting access to ship effects
int get_effect_from_name(const char* name);

void maybe_write_to_event_log(int result);

//OSWPT Stuff

enum class oswpt_type : uint8_t
{
	NONE = 0,
	SHIP,
	WING,
	WAYPOINT,
	SHIP_ON_TEAM,		// e.g. <any friendly>
	WHOLE_TEAM,			// e.g. Friendly
	PARSE_OBJECT,		// a "ship" that hasn't arrived yet
	EXITED,
	WING_NOT_PRESENT	// a wing that hasn't arrived yet or is between waves
};

namespace scripting {
	class ade_table_entry;
}
namespace luacpp {
	class LuaValue;
}
struct lua_State;

// Goober5000
struct object_ship_wing_point_team
{
	char object_name[NAME_LENGTH] = { 0 };
	oswpt_type type = oswpt_type::NONE;

	int ship_registry_index = -1;
	int objnum = -1;
	int wingnum = -1;
	int wp_list = -1;
	int wp_index = -1;
	int team = -1;

	object_ship_wing_point_team() = default;
	object_ship_wing_point_team(ship* sp);
	object_ship_wing_point_team(p_object* pop);
	object_ship_wing_point_team(ship_obj* sop);
	object_ship_wing_point_team(wing* wp);

	inline bool has_ship_entry() const { return ship_registry_index >= 0; }
	inline bool has_objp() const { return objnum >= 0; }
	inline bool has_wingp() const { return wingnum >= 0; }
	inline bool has_waypointp() const { return wp_list >= 0 && wp_index >= 0; }

	const ship_registry_entry* ship_entry() const;
	object* objp() const;
	wing* wingp() const;
	waypoint* waypointp() const;

	const ship_registry_entry* ship_entry_or_null() const;
	object* objp_or_null() const;
	wing* wingp_or_null() const;
	waypoint* waypointp_or_null() const;

	bool has_p_objp() const;
	bool has_shipp() const;

	p_object* p_objp() const;
	ship* shipp() const;

	p_object* p_objp_or_null() const;
	ship* shipp_or_null() const;

	bool matches(const ship* shipp) const;
	void clear();

	bool operator==(const object_ship_wing_point_team &other) const;
	bool operator!=(const object_ship_wing_point_team &other) const;
};

void eval_object_ship_wing_point_team(object_ship_wing_point_team* oswpt, int node, const char* ctext_override = nullptr);

bool sexp_check_flag_arrays(const char *flag_name, Object::Object_Flags &object_flag, Ship::Ship_Flags &ship_flag, Mission::Parse_Object_Flags &parse_obj_flag, AI::AI_Flags &ai_flag);
bool sexp_check_flag_array(const char* flag_name, Ship::Wing_Flags& wing_flag);

#endif
