I've been going on for
years about doing this but I've only recently had the time to give this a try. This is a very, very alpha build of FS2 with support for collections. What does that mean? Well basically this build allows the FREDder to define a list in much the same way they can currently define a variable. So for instance you can have this
$Name: ShipsList
$Data: ( "Beta 1" "Beta 2" "Beta 3")
You can then do this
Do something To Beta
when
-Whatever Trigger
-Do something to
--Whichever ship is the first is on the list
-Do something else to
--Whichever ship is the first is on the list
-Remove the first ship from the list
-Add Gamma 4 to the list
Yes you can do this with arguments, but unlike arguments you aren't limited to one list at a time, there is no reason you can't do this
when
-Whatever
-Repair-ship
--First ship on Ships's List
-Send Message
--First ship on Ships's List
--High
--First message on SayThankYouMessages (and remove it)
-Add Ship to Ships List
-Repair Ship
--Last Ship on Ships List
-Send Message
--Third ship on Ships's List
--High
--First Message on SayThankYouMessages (but don't remove it)
I'm including a test build and a mission to show how the feature works. There's currently no support for FRED (although I've included a build which can understand the syntax). You'll need to do everything in notepad for now.
Basically you define a list immediately following the SEXP Variables section like this
#Sexp_containers
$Lists
$Name: SomeShipsList
$Data Type: String
$Data: ( "Beta 1" "Beta 2" "Beta 3" )
Then in the SEXP section you call the list like this
( send-message
&SomeShipsList& 16
"High"
&MessageList& 128
)
The numbers following the list name tell the SEXP how to use the list. The ones that work at the moment are
8 - Get the first entry from the list
16 - Get the last entry from the list
64 - Get the first entry from the list and then remove it from the list
128 - Get the last entry from the list and then remove it from the list
I've also added preliminary support for 512 in action operators. 512 will make the SEXP do this action repeatedly for every ship in the list. So self-destruct &ShipsList& 512 will make all the ships on the list self-destruct. don't try using 512 in anything other than an action operator, I suspect you'll crash the game!
You can find the build
here if you want to play with it.
Here's the code.
Index: code/globalincs/vmallocator.h
===================================================================
--- code/globalincs/vmallocator.h (revision 11070)
+++ code/globalincs/vmallocator.h (working copy)
@@ -112,7 +112,12 @@
template< typename T >
class SCP_vector : public std::vector< T, SCP_vm_allocator< T > > { };
+/*
template< typename T >
+class SCP_deque : public std::deque< T, SCP_vm_allocator< T > > { };
+*/
+
+template< typename T >
class SCP_list : public std::list< T, SCP_vm_allocator< T > > { };
typedef std::basic_string<char, std::char_traits<char>, SCP_vm_allocator<char> > SCP_string;
Index: code/mission/missionparse.cpp
===================================================================
--- code/mission/missionparse.cpp (revision 11070)
+++ code/mission/missionparse.cpp (working copy)
@@ -5396,6 +5396,24 @@
}
}
+void parse_list_collections()
+{
+ if (! optional_string("#Sexp_containers") ) {
+ return;
+ }
+ else {
+ if (optional_string("$Lists")) {
+ stuff_sexp_list_container();
+ required_string("$End Lists");
+ }
+
+ if (optional_string("$Maps")) {
+ stuff_sexp_map_container();
+ required_string("$End Maps");
+ }
+ }
+}
+
void parse_variables()
{
int i, j, num_variables = 0;
@@ -5498,6 +5516,7 @@
parse_plot_info(pm);
parse_variables();
+ parse_list_collections();
parse_briefing_info(pm); // TODO: obsolete code, keeping so we don't obsolete existing mission files
parse_cutscenes(pm);
parse_fiction(pm);
Index: code/parse/sexp.cpp
===================================================================
--- code/parse/sexp.cpp (revision 11070)
+++ code/parse/sexp.cpp (working copy)
@@ -677,6 +677,9 @@
{ "string-get-substring", OP_STRING_GET_SUBSTRING, 4, 4, SEXP_ACTION_OPERATOR, }, // Goober5000
{ "string-set-substring", OP_STRING_SET_SUBSTRING, 5, 5, SEXP_ACTION_OPERATOR, }, // Goober5000
+ //Variable Category
+ { "add-to-list", OP_COLLECTION_ADD_TO_LIST, 2, 2, SEXP_ACTION_OPERATOR, }, // Karajorma
+
//Other Sub-Category
{ "damaged-escort-priority", OP_DAMAGED_ESCORT_LIST, 3, INT_MAX, SEXP_ACTION_OPERATOR, }, //phreak
{ "damaged-escort-priority-all", OP_DAMAGED_ESCORT_LIST_ALL, 1, MAX_COMPLETE_ESCORT_LIST, SEXP_ACTION_OPERATOR, }, // Goober5000
@@ -806,6 +809,7 @@
"LAG GUAGE"
};
+ SCP_vector<SCP_string> Temp_CTEXT_strings;
void sexp_set_skybox_model_preload(char *name); // taylor
int Num_skybox_flags = 6;
@@ -841,6 +845,9 @@
sexp_variable Sexp_variables[MAX_SEXP_VARIABLES];
sexp_variable Block_variables[MAX_SEXP_VARIABLES]; // used for compatibility with retail.
+SCP_vector<sexp_map_container> Sexp_map_containers;
+SCP_vector<sexp_list_container> Sexp_list_containers;
+
int Num_special_expl_blocks;
SCP_vector<int> Current_sexp_operator;
@@ -885,8 +892,8 @@
void build_extended_sexp_string(SCP_string &accumulator, int cur_node, int level, int mode);
void update_sexp_references(const char *old_name, const char *new_name, int format, int node);
int sexp_determine_team(char *subj);
-int extract_sexp_variable_index(int node);
void init_sexp_vars();
+void init_sexp_collections();
int eval_num(int node);
// for handling variables
@@ -897,6 +904,9 @@
void sexp_copy_variable_from_index(int node);
void sexp_copy_variable_between_indexes(int node);
+// for handling collections
+void sexp_add_to_list(int node);
+
SCP_vector<char*> Sexp_replacement_arguments;
int Sexp_current_argument_nesting_level;
SCP_vector<char*> Applicable_arguments_temp;
@@ -1099,6 +1109,7 @@
sexp_nodes_init();
init_sexp_vars();
+ init_sexp_collections();
Locked_sexp_false = Locked_sexp_true = -1;
Locked_sexp_false = alloc_sexp("false", SEXP_LIST, SEXP_ATOM_OPERATOR, -1, -1);
@@ -1162,6 +1173,8 @@
Sexp_nodes[node].value = SEXP_UNKNOWN;
Sexp_nodes[node].flags = SNF_DEFAULT_VALUE; // Goober5000
Sexp_nodes[node].op_index = NO_OPERATOR_INDEX_DEFINED;
+ Sexp_nodes[node].iteration = -1;
+ Sexp_nodes[node].safe_to_update_iterations = true;
return node;
}
@@ -1345,6 +1358,17 @@
flush_sexp_tree(Sexp_nodes[node].rest);
}
+void sexp_tree_reset_iterations(int node)
+{
+ if (node < 0){
+ return;
+ }
+
+ Sexp_nodes[node].iteration = -1;
+ sexp_tree_reset_iterations(Sexp_nodes[node].first);
+ sexp_tree_reset_iterations(Sexp_nodes[node].rest);
+}
+
int verify_sexp_tree(int node)
{
if (node == -1){
@@ -1709,6 +1733,13 @@
if (bad_node)
*bad_node = node;
+ if (Sexp_nodes[node].subtype & SEXP_ATOM_CONTAINER) {
+ // Ignore containers for now.
+ node = Sexp_nodes[node].rest;
+ argnum++;
+ continue;
+ }
+
if (Sexp_nodes[node].subtype == SEXP_ATOM_LIST) {
i = Sexp_nodes[node].first;
if (bad_node)
@@ -1720,49 +1751,51 @@
if (Sexp_nodes[i].subtype == SEXP_ATOM_LIST)
return 0;
- op2 = get_operator_index(CTEXT(i));
- if (op2 == -1)
- return SEXP_CHECK_UNKNOWN_OP;
+ else {
+ op2 = get_operator_index(CTEXT(i));
+ if (op2 == -1)
+ return SEXP_CHECK_UNKNOWN_OP;
- type2 = query_operator_return_type(op2);
- if (recursive) {
- switch (type) {
- case OPF_NUMBER:
- t = OPR_NUMBER;
- break;
+ type2 = query_operator_return_type(op2);
+ if (recursive) {
+ switch (type) {
+ case OPF_NUMBER:
+ t = OPR_NUMBER;
+ break;
- case OPF_POSITIVE:
- t = OPR_POSITIVE;
- break;
+ case OPF_POSITIVE:
+ t = OPR_POSITIVE;
+ break;
- case OPF_BOOL:
- t = OPR_BOOL;
- break;
+ case OPF_BOOL:
+ t = OPR_BOOL;
+ break;
- case OPF_NULL:
- t = OPR_NULL;
- break;
+ case OPF_NULL:
+ t = OPR_NULL;
+ break;
- // Goober5000
- case OPF_FLEXIBLE_ARGUMENT:
- t = OPR_FLEXIBLE_ARGUMENT;
- break;
+ // Goober5000
+ case OPF_FLEXIBLE_ARGUMENT:
+ t = OPR_FLEXIBLE_ARGUMENT;
+ break;
- case OPF_AI_GOAL:
- t = OPR_AI_GOAL;
- break;
+ case OPF_AI_GOAL:
+ t = OPR_AI_GOAL;
+ break;
- // special case for modify-variable
- case OPF_AMBIGUOUS:
- t = OPR_AMBIGUOUS;
- break;
+ // special case for modify-variable
+ case OPF_AMBIGUOUS:
+ t = OPR_AMBIGUOUS;
+ break;
- default:
- return SEXP_CHECK_UNKNOWN_TYPE; // no other return types available
- }
+ default:
+ return SEXP_CHECK_UNKNOWN_TYPE; // no other return types available
+ }
- if ((z = check_sexp_syntax(i, t, recursive, bad_node)) != 0) {
- return z;
+ if ((z = check_sexp_syntax(i, t, recursive, bad_node)) != 0) {
+ return z;
+ }
}
}
@@ -3115,6 +3148,10 @@
return SEXP_CHECK_INVALID_SHIP_EFFECT;
}
break;
+
+ case OPF_LIST_CONTAINER_NAME:
+ // TO DO : Add error code here!
+ break;
default:
@@ -3292,6 +3329,25 @@
}
+ // container
+ else if (*Mp == SEXP_CONTAINER_CHAR) {
+ Mp++;
+
+ char container[TOKEN_LENGTH];
+ stuff_string(container, F_NAME, TOKEN_LENGTH, "&");
+
+ // bump past closing '&'
+ Mp += 2;
+
+ //read the container function flag
+ int flags;
+ stuff_int(&flags);
+
+ node = alloc_sexp(container, (SEXP_ATOM | SEXP_FLAG_CONTAINER), SEXP_ATOM_CONTAINER, -1, -1);
+ Sexp_nodes[node].flags |= flags;
+ ignore_white_space();
+ }
+
// Sexp operator or number
else {
int len = 0;
@@ -3495,8 +3551,115 @@
return start;
}
+bool stuff_one_generic_sexp_container(SCP_string &name, int &type, SCP_vector<SCP_string> &data)
+{
+ SCP_string temp_type_string;
+ data.clear();
+ required_string("$Name:");
+ stuff_string(name, F_NAME);
+
+ required_string("$Data Type:");
+ stuff_string(temp_type_string, F_NAME);
+ if (!strcmp(temp_type_string.c_str(), "Number")) {
+ type |= SEXP_CONTAINER_NUMBER_DATA;
+ }
+ else if (!strcmp(temp_type_string.c_str(), "String")) {
+ type |= SEXP_CONTAINER_STRING_DATA;
+ }
+ else {
+ Warning(LOCATION, "Unknown SEXP Container type %s found", temp_type_string.c_str());
+ log_printf(LOGFILE_EVENT_LOG, "Unknown SEXP Container type %s found", temp_type_string.c_str());
+ return false;
+ }
+
+ if (optional_string("$Key Type:")) {
+ Assertion ((type & SEXP_CONTAINER_MAP), "$Key Type: found for container which doesn't use keys!");
+
+ stuff_string(temp_type_string, F_NAME);
+ if (!strcmp(temp_type_string.c_str(), "Number")) {
+ type |= SEXP_CONTAINER_NUMBER_KEYS;
+ }
+ else if (!strcmp(temp_type_string.c_str(), "String")) {
+ type |= SEXP_CONTAINER_STRING_KEYS;
+ }
+ else {
+ Warning(LOCATION, "Unknown SEXP Container type %s found", temp_type_string.c_str());
+ log_printf(LOGFILE_EVENT_LOG, "Unknown SEXP Container type %s found", temp_type_string.c_str());
+ return false;
+ }
+ }
+
+ if (optional_string("+Strongly Typed Keys")) {
+ Assertion ((type & SEXP_CONTAINER_MAP), "+Strongly Typed Keys found for container which doesn't use keys!");
+ type |= SEXP_CONTAINER_STRONGLY_TYPED_KEYS;
+ }
+
+ if (optional_string("+Strongly Typed Data")) {
+ type |= SEXP_CONTAINER_STRONGLY_TYPED_DATA;
+ }
+
+ required_string("$Data:");
+ stuff_string_list(data);
+
+ return true;
+}
+
/**
+ * Stuffs a sexp list type container
+ */
+void stuff_sexp_list_container()
+{
+ sexp_list_container temp_list;
+ SCP_vector<SCP_string> parsed_data;
+
+
+ while (required_string_either("$End Lists", "$Name:")) {
+ temp_list.type = SEXP_CONTAINER_LIST;
+ if (stuff_one_generic_sexp_container(temp_list.list_name, temp_list.type, parsed_data)){
+ temp_list.data.clear();
+ for (int i = 0; i < (int)parsed_data.size(); i++) {
+ temp_list.data.push_back(parsed_data[i]);
+ }
+ }
+
+ Sexp_list_containers.push_back(temp_list);
+ }
+}
+
+/**
+ * Stuffs a sexp map type container
+ */
+void stuff_sexp_map_container()
+{
+ sexp_map_container temp_map;
+ sexp_list_container temp_list;
+ SCP_vector<SCP_string> parsed_data;
+
+
+ while (required_string_either("$End Maps", "$Name:")) {
+ temp_map.type = SEXP_CONTAINER_MAP;
+ if (stuff_one_generic_sexp_container(temp_map.list_name, temp_map.type, parsed_data)){
+
+ // if the data is corrupt, discard it
+ if (parsed_data.size() % 2 != 0) {
+ Warning(LOCATION, "Data in the SEXP Map container is corrupt. Must be an even number of entries. Instead have %d", parsed_data.size() );
+ log_printf(LOGFILE_EVENT_LOG, "Data in the SEXP Map container is corrupt. Must be an even number of entries. Instead have %d", parsed_data.size() );
+ continue;
+ }
+
+ // move the data into the new entry
+ temp_map.data.clear();
+ for (int i = 0; i < (int)parsed_data.size(); i = i+2) {
+ temp_map.data.insert(std::pair<SCP_string, SCP_string>(parsed_data[i], parsed_data[i+1]));
+ }
+ }
+
+ Sexp_map_containers.push_back(temp_map);
+ }
+}
+
+/**
* Stuffs a list of sexp variables
*/
int stuff_sexp_variable_list()
@@ -22314,7 +22477,7 @@
} else {
int op_num;
-
+
node = CDR(cur_node); // makes reading the next bit of code a little easier.
op_num = get_operator_const(cur_node);
@@ -22604,6 +22767,11 @@
sexp_val = SEXP_TRUE;
break;
+ case OP_COLLECTION_ADD_TO_LIST:
+ sexp_add_to_list(node);
+ sexp_val = SEXP_TRUE;
+ break;
+
case OP_TIME_SHIP_DESTROYED:
sexp_val = sexp_time_destroyed(node);
break;
@@ -24468,7 +24636,23 @@
if (Log_event) {
add_to_event_log_buffer(get_operator_index(cur_node), sexp_val);
}
+
+ // since we're leaving the SEXP, we can update the number of iterations
+ Sexp_nodes[cur_node].safe_to_update_iterations = true;
+ // if this sexp has any containers, we might need to recurse so that we can deal with iterations
+ if (Sexp_nodes[cur_node].iteration != -1) {
+ // we've just run the last iteration. No need to recurse any further
+ if (Sexp_nodes[cur_node].flags & SNF_CONTAINER_FINAL_ITERATION) {
+ Sexp_nodes[cur_node].flags &= ~SNF_CONTAINER_FINAL_ITERATION;
+ sexp_tree_reset_iterations(cur_node);
+ }
+ // recurse
+ else {
+ eval_sexp(cur_node, referenced_node);
+ }
+ }
+
Assert(!Current_sexp_operator.empty());
Current_sexp_operator.pop_back();
@@ -25455,6 +25639,7 @@
case OP_NEBULA_CHANGE_PATTERN:
case OP_COPY_VARIABLE_FROM_INDEX:
case OP_COPY_VARIABLE_BETWEEN_INDEXES:
+ case OP_COLLECTION_ADD_TO_LIST:
case OP_SET_ETS_VALUES:
case OP_CALL_SSM_STRIKE:
case OP_SET_MOTION_DEBRIS:
@@ -25971,6 +26156,14 @@
case OP_GET_VARIABLE_BY_INDEX:
case OP_COPY_VARIABLE_BETWEEN_INDEXES:
return OPF_POSITIVE;
+
+ case OP_COLLECTION_ADD_TO_LIST:
+ if (argnum == 0) {
+ return OPF_LIST_CONTAINER_NAME;
+ }
+ else {
+ return OPF_AMBIGUOUS;
+ }
case OP_COPY_VARIABLE_FROM_INDEX:
if (argnum == 0) {
@@ -28048,36 +28241,116 @@
return ai_query_goal_valid(ship_num, Sexp_ai_goal_links[i].ai_goal);
}
-// Takes an Sexp_node.text pointing to a variable (of form "Sexp_variables[xx]=string" or "Sexp_variables[xx]=number")
-// and returns the index into the Sexp_variables array of the actual value
-int extract_sexp_variable_index(int node)
+char* deal_with_container(int n)
{
- char *text = Sexp_nodes[node].text;
- char char_index[8];
- char *start_index;
- int variable_index;
+ if (Sexp_nodes[n].type & SEXP_FLAG_LIST_CONTAINER) {
+ // the node flags will tell us what we're supposed to do with this container
+ int actual_flag = Sexp_nodes[n].flags & SNF_CONTAINER_MASK;
- // get past the '['
- start_index = text + 15;
- Assert(isdigit(*start_index));
+ int index = get_index_sexp_container_name(Sexp_nodes[n].text);
+ if (index < 0) {
+ return "";
+ }
+
+ if (Sexp_list_containers[index].data.empty()) {
+ return "";
+ }
- int len = 0;
+ SCP_string temp_string;
+ int parent = -1;
+ int parent_node = -1;
+ int recurse_at_this_node = -1;
+ int op_type;
- while ( *start_index != ']' ) {
- char_index[len++] = *(start_index++);
- Assert(len < 3);
- }
+ switch (actual_flag) {
+ case SNF_CONTAINER_GET_FIRST:
+ temp_string.assign(Sexp_list_containers[index].data.front());
+ break;
- Assert(len > 0);
- char_index[len] = 0; // append null termination to string
+ case SNF_CONTAINER_GET_LAST:
+ temp_string.assign(Sexp_list_containers[index].data.back());
+ break;
- variable_index = atoi(char_index);
- Assert( (variable_index >= 0) && (variable_index < MAX_SEXP_VARIABLES) );
+ case SNF_CONTAINER_REMOVE_FIRST:
+ temp_string.assign(Sexp_list_containers[index].data.front());
+ Sexp_list_containers[index].data.pop_front();
+ break;
- return variable_index;
+ case SNF_CONTAINER_REMOVE_LAST:
+ temp_string.assign(Sexp_list_containers[index].data.back());
+ Sexp_list_containers[index].data.pop_back();
+ break;
+
+ case SNF_CONTAINER_ITERATE_FORWARDS:
+ // get the parent node
+ parent = find_parent_operator(n);
+ if (parent == -1) {
+ return "";
+ }
+ parent_node = parent;
+
+ // we need to find the point we're going to recurse from
+ while (true) {
+ op_type = Operators[get_operator_index(parent)].type;
+
+ // Action operators are suitable
+ if (op_type & SEXP_ACTION_OPERATOR) {
+ recurse_at_this_node = parent;
+ break;
+ }
+
+ // Conditional operators are also suitable
+ if (op_type & SEXP_CONDITIONAL_OPERATOR) {
+ recurse_at_this_node = parent;
+ break;
+ }
+
+ // anything else, back up further
+ parent = find_parent_operator(parent);
+ if (parent == -1) {
+ return "";
+ }
+ }
+
+ // does the parent node know we are iterating already?
+ if(Sexp_nodes[recurse_at_this_node].iteration == -1) {
+ Sexp_nodes[recurse_at_this_node].iteration = (int)Sexp_list_containers[index].data.size();
+ }
+
+ if (Sexp_nodes[n].iteration < (int)Sexp_list_containers[index].data.size()) {
+ if (Sexp_nodes[parent_node].safe_to_update_iterations == true) {
+ // since we're entering the eval code, it is no longer safe to update the number of iterations
+ Sexp_nodes[parent_node].safe_to_update_iterations = false;
+ Sexp_nodes[n].iteration++ ; // we start at -1 so this needs to go up BEFORE we use it as an index!
+ }
+ temp_string.assign(Sexp_list_containers[index].data[Sexp_nodes[n].iteration]);
+ }
+ // Well, this was clumsy mission design! Someone has used two or more lists in the same SEXP and a later one is shorter than the other(s)!
+ else {
+ // just return the last entry and hope it's valid
+ temp_string.assign(Sexp_list_containers[index].data.back());
+ Warning(LOCATION, "Error - SEXP List %s does not contain %d members but has been asked to provide that number of entries", Sexp_list_containers[index].list_name.c_str(), Sexp_nodes[recurse_at_this_node].iteration);
+ log_printf(LOGFILE_EVENT_LOG, "Error - SEXP List %s does not contain %d members but has been asked to provide that number of entries", Sexp_list_containers[index].list_name.c_str(), Sexp_nodes[recurse_at_this_node].iteration);
+ }
+
+ // final iteration for this container
+ if (Sexp_nodes[n].iteration + 1 == (int)Sexp_list_containers[index].data.size()) {
+ Sexp_nodes[recurse_at_this_node].flags |= SNF_CONTAINER_FINAL_ITERATION;
+ }
+
+ break;
+
+ default:
+ return "";
+ }
+
+ Temp_CTEXT_strings.push_back(temp_string);
+ return const_cast<char*>(Temp_CTEXT_strings.back().c_str());
+ }
+
+ return "";
}
-
/**
* Wrapper around Sexp_node[xx].text for normal and variable
*/
@@ -28152,8 +28425,18 @@
return Sexp_variables[sexp_variable_index].text;
}
- else
- {
+
+ // Karajorma - check we're not dealing with a list container
+ if (Sexp_nodes[n].type & SEXP_FLAG_LIST_CONTAINER) {
+ return deal_with_container(n);
+ }
+
+ // Karajorma - check we're not dealing with a map container
+ if (Sexp_nodes[n].type & SEXP_FLAG_MAP_CONTAINER) {
+ return "";
+ }
+
+ else {
return Sexp_nodes[n].text;
}
}
@@ -28170,6 +28453,12 @@
}
}
+void init_sexp_collections()
+{
+ Sexp_map_containers.clear();
+ Sexp_list_containers.clear();
+}
+
/**
* Add a variable to the block variable array rather than the Sexp_variables array
*/
@@ -28843,6 +29132,39 @@
}
/**
+ * Return index of sexp_container by its name, -1 if not found
+ */
+int get_index_sexp_container_name(const char *text)
+{
+ for (int i = 0; i < (int)Sexp_list_containers.size() ; i++) {
+ if ( !stricmp(Sexp_list_containers[i].list_name.c_str(), text) ) {
+ return i;
+ }
+ }
+
+ // not found
+ return -1;
+}
+
+/**
+ * Add an entry to a SEXP collection.
+ */
+void sexp_add_to_list (int node)
+{
+ int list_index = get_index_sexp_container_name(CTEXT(node));
+
+ if (list_index < 0) {
+ Warning(LOCATION, "Collection %s does not exist.", CTEXT(node));
+ }
+
+ node = CDR(node);
+
+ // TO DO - ADD TYPE CHECKING CODE HERE!
+
+ Sexp_list_containers[list_index].data.push_back(CTEXT(node));
+}
+
+/**
* Evaluate number which may result from an operator or may be text
*/
int eval_num(int n)
@@ -29235,6 +29557,9 @@
case OP_STRING_SET_SUBSTRING:
return CHANGE_SUBCATEGORY_VARIABLES;
+ case OP_COLLECTION_ADD_TO_LIST:
+ return CHANGE_SUBCATEGORY_COLLECTIONS;
+
case OP_DAMAGED_ESCORT_LIST:
case OP_DAMAGED_ESCORT_LIST_ALL:
case OP_SET_SUPPORT_SHIP:
@@ -30325,6 +30650,12 @@
"\t1:\tIndex of source variable, from 0 to MAX_SEXP_VARIABLES - 1.\r\n"
"\t2:\tIndex of destination variable, from 0 to MAX_SEXP_VARIABLES - 1. The types of both variables must match." },
+ { OP_COLLECTION_ADD_TO_LIST, "add-to-list\r\n"
+ "\tAdds a new entry to a SEXP collection.\r\n\r\n"
+ "Takes 2 arguments...\r\n"
+ "\t1:\tName of the list.\r\n"
+ "\t2:\tThe data to be added. In strongly typed collections, this type must match the type of data of the class" },
+
{ OP_PROTECT_SHIP, "Protect ship (Action operator)\r\n"
"\tProtects a ship from being attacked by any enemy ship. Any ship "
"that is protected will not come under enemy fire.\r\n\r\n"
@@ -33036,6 +33367,7 @@
{ "Jump Nodes", CHANGE_SUBCATEGORY_JUMP_NODES },
{ "Special Effects", CHANGE_SUBCATEGORY_SPECIAL_EFFECTS },
{ "Variables", CHANGE_SUBCATEGORY_VARIABLES },
+ { "Collections", CHANGE_SUBCATEGORY_COLLECTIONS },
{ "Other", CHANGE_SUBCATEGORY_OTHER },
{ "Mission", STATUS_SUBCATEGORY_MISSION },
{ "Player", STATUS_SUBCATEGORY_PLAYER },
Index: code/parse/sexp.h
===================================================================
--- code/parse/sexp.h (revision 11070)
+++ code/parse/sexp.h (working copy)
@@ -112,6 +112,8 @@
#define OPF_TEAM_COLOR 85 // The E - Color settings as defined in Colors.tbl
#define OPF_NEBULA_PATTERN 86 // Axem - Full Nebula Background Patterns, as defined in nebula.tbl
#define OPF_SKYBOX_FLAGS 87 // niffiwan - valid skybox flags
+#define OPF_LIST_CONTAINER_NAME 88 // Karajorma - The name of a list container class
+#define OPF_MAP_CONTAINER_NAME 89 // Karajorma - The name of a map container class
// Operand return types
#define OPR_NUMBER 1 // returns number
@@ -197,7 +199,8 @@
#define CHANGE_SUBCATEGORY_JUMP_NODES (0x0010 | OP_CATEGORY_CHANGE)
#define CHANGE_SUBCATEGORY_SPECIAL_EFFECTS (0x0011 | OP_CATEGORY_CHANGE)
#define CHANGE_SUBCATEGORY_VARIABLES (0x0012 | OP_CATEGORY_CHANGE)
-#define CHANGE_SUBCATEGORY_OTHER (0x0013 | OP_CATEGORY_CHANGE)
+#define CHANGE_SUBCATEGORY_COLLECTIONS (0x0013 | OP_CATEGORY_CHANGE)
+#define CHANGE_SUBCATEGORY_OTHER (0x0014 | OP_CATEGORY_CHANGE)
#define STATUS_SUBCATEGORY_MISSION (0x0000 | OP_CATEGORY_STATUS)
@@ -724,6 +727,7 @@
#define OP_HUD_SET_CUSTOM_GAUGE_ACTIVE (0x0026 | OP_CATEGORY_CHANGE2 | OP_NONCAMPAIGN_FLAG) // The E, just revamped a bit by Axem
#define OP_HUD_SET_RETAIL_GAUGE_ACTIVE (0x0027 | OP_CATEGORY_CHANGE2 | OP_NONCAMPAIGN_FLAG) // The E, just revamped a bit by Axem
#define OP_SCRIPT_EVAL_MULTI (0x0028 | OP_CATEGORY_CHANGE2 | OP_NONCAMPAIGN_FLAG) // Karajorma
+#define OP_COLLECTION_ADD_TO_LIST (0x0029 | OP_CATEGORY_CHANGE2 | OP_NONCAMPAIGN_FLAG) // Karajorma
// defined for AI goals
#define OP_AI_CHASE (0x0000 | OP_CATEGORY_AI | OP_NONCAMPAIGN_FLAG)
@@ -858,7 +862,12 @@
// flags for sexpressions -- masked onto the end of the type field
#define SEXP_FLAG_PERSISTENT (1<<31) // should this sexp node be persistant across missions
#define SEXP_FLAG_VARIABLE (1<<30)
+#define SEXP_FLAG_LIST_CONTAINER (1<<29)
+#define SEXP_FLAG_MAP_CONTAINER (1<<28)
+
+#define SEXP_FLAG_CONTAINER (SEXP_FLAG_LIST_CONTAINER | SEXP_FLAG_MAP_CONTAINER)
+
// 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
@@ -879,6 +888,16 @@
//Karajorma
#define SEXP_VARIABLE_NETWORK (1<<28)
+#define SEXP_CONTAINER_CHAR ('&')
+#define SEXP_CONTAINER_LIST (1<<0)
+#define SEXP_CONTAINER_MAP (1<<1)
+#define SEXP_CONTAINER_NUMBER_DATA (1<<2)
+#define SEXP_CONTAINER_STRING_DATA (1<<3)
+#define SEXP_CONTAINER_NUMBER_KEYS (1<<4)
+#define SEXP_CONTAINER_STRING_KEYS (1<<5)
+#define SEXP_CONTAINER_STRONGLY_TYPED_KEYS (1<<6)
+#define SEXP_CONTAINER_STRONGLY_TYPED_DATA (1<<7)
+
#define BLOCK_EXP_SIZE 6
#define INNER_RAD 0
#define OUTER_RAD 1
@@ -903,6 +922,7 @@
#define SEXP_ATOM_OPERATOR 1
#define SEXP_ATOM_NUMBER 2
#define SEXP_ATOM_STRING 3
+#define SEXP_ATOM_CONTAINER 4
// 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.
@@ -1022,12 +1042,28 @@
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
+ int iteration;
+ bool safe_to_update_iterations;
} sexp_node;
// Goober5000
-#define SNF_ARGUMENT_VALID (1<<0)
-#define SNF_ARGUMENT_SELECT (1<<1)
+#define SNF_ARGUMENT_VALID (1<<0)
+#define SNF_ARGUMENT_SELECT (1<<1)
+// Karajorma - If a container flag is added, SNF_CONTAINER_MASK should be updated to include it.
+#define SNF_CONTAINER_GET_DATA (1<<2)
+#define SNF_CONTAINER_GET_FIRST (1<<3)
+#define SNF_CONTAINER_GET_LAST (1<<4)
+#define SNF_CONTAINER_ADD_BACK (1<<5)
+#define SNF_CONTAINER_REMOVE_FIRST (1<<6)
+#define SNF_CONTAINER_REMOVE_LAST (1<<7)
+#define SNF_CONTAINER_GET_SIZE (1<<8)
+#define SNF_CONTAINER_ITERATE_FORWARDS (1<<9)
+#define SNF_CONTAINER_ITERATE_BACKWARDS (1<<10)
+
+#define SNF_CONTAINER_FINAL_ITERATION (1<<31)
+
#define SNF_DEFAULT_VALUE SNF_ARGUMENT_VALID
+#define SNF_CONTAINER_MASK ( SNF_CONTAINER_GET_DATA | SNF_CONTAINER_GET_FIRST | SNF_CONTAINER_GET_LAST | SNF_CONTAINER_ADD_BACK | SNF_CONTAINER_REMOVE_FIRST | SNF_CONTAINER_REMOVE_LAST | SNF_CONTAINER_GET_SIZE | SNF_CONTAINER_ITERATE_FORWARDS | SNF_CONTAINER_ITERATE_BACKWARDS)
typedef struct sexp_variable {
int type;
@@ -1035,7 +1071,19 @@
char variable_name[TOKEN_LENGTH];
} sexp_variable;
+typedef struct sexp_list_container {
+ SCP_deque<SCP_string> data;
+ SCP_string list_name;
+ int type;
+}sexp_list_container;
+typedef struct sexp_map_container {
+ SCP_hash_map<SCP_string, SCP_string> data;
+ SCP_string list_name;
+ int type;
+}sexp_map_container;
+
+
#define ARG_ITEM_F_DUP (1<<0)
// Goober5000 - adapted from sexp_list_item in Sexp_tree.h
@@ -1071,6 +1119,9 @@
extern sexp_variable Sexp_variables[MAX_SEXP_VARIABLES];
extern sexp_variable Block_variables[MAX_SEXP_VARIABLES];
+extern SCP_vector<sexp_map_container> Sexp_map_containers;
+extern SCP_vector<sexp_list_container> Sexp_list_containers;
+
extern sexp_oper Operators[];
extern int Num_operators;
extern int Locked_sexp_true, Locked_sexp_false;
@@ -1120,6 +1171,8 @@
extern int get_sexp_main(void); // Returns start node
extern int run_sexp(const char* sexpression); // debug and lua sexps
extern int stuff_sexp_variable_list();
+extern void stuff_sexp_list_container();
+extern void stuff_sexp_map_container();
extern int eval_sexp(int cur_node, int referenced_node = -1);
extern int is_sexp_true(int cur_node, int referenced_node = -1);
extern int query_operator_return_type(int op);
@@ -1169,6 +1222,9 @@
int num_block_variables();
bool has_special_explosion_block_index(ship *shipp, int *index);
+// sexp_container
+int get_index_sexp_container_name(const char *text);
+
// Karajorma
void set_primary_ammo (int ship_index, int requested_bank, int requested_ammo, int rearm_limit=-1, bool update=true);
void set_secondary_ammo (int ship_index, int requested_bank, int requested_ammo, int rearm_limit=-1, bool update=true);
Apologies for the mess, I haven't tidied it up in the slightest!
I'd like some feedback on the idea before I go forwards, but so far I have planned to add the following soonish
- The ability to get the element at an index in the array, not just the start and end (including one at random).
- A SEXP for removing elements
- Support for maps as well as lists is on the drawing board (as you can see the build will load them, it just can't use them).
[attachment kidnapped by pirates]