@@ -991,8 +991,12 @@ bool ship_class_unchanged(const ship_registry_entry *ship_entry);
991991void multi_sexp_modify_variable();
992992
993993// jg18
994- // container_value_index is for using one specific value from acontainer
994+ // container_value_index is for using one specific value from a container
995995int copy_node_to_replacement_args(int node, int container_value_index = -1);
996+ // checks whether the node's value can change over the course of the mission
997+ bool is_node_value_dynamic(int node);
998+ // returns 0 on success or a SEXP_CHECK_* value on failure
999+ int check_dynamic_value_node_type(int node, bool is_string, bool is_number);
9961000
9971001#define NO_OPERATOR_INDEX_DEFINED -2
9981002#define NOT_A_SEXP_OPERATOR -1
@@ -2464,9 +2468,16 @@ int check_sexp_syntax(int node, int return_type, int recursive, int *bad_node, i
24642468 }
24652469 Assert(ship_node >= 0);
24662470
2467- // we can't check special-arg ships
2468- if (Sexp_nodes[ship_node].flags & SNF_SPECIAL_ARG_IN_NODE)
2469- break;
2471+ if (is_node_value_dynamic(ship_node)) {
2472+ const int dyn_val_check = check_dynamic_value_node_type(ship_node, true, false);
2473+ if (dyn_val_check) {
2474+ return dyn_val_check;
2475+ } else {
2476+ // since the value changes at runtime
2477+ // we can check only the type
2478+ break;
2479+ }
2480+ }
24702481
24712482 auto shipname = CTEXT(ship_node);
24722483 shipnum = ship_name_lookup(shipname, 1);
@@ -2551,9 +2562,16 @@ int check_sexp_syntax(int node, int return_type, int recursive, int *bad_node, i
25512562
25522563 ship_node = CDR(op_node);
25532564
2554- // we can't check special-arg ships
2555- if (Sexp_nodes[ship_node].flags & SNF_SPECIAL_ARG_IN_NODE)
2556- break;
2565+ if (is_node_value_dynamic(ship_node)) {
2566+ const int dyn_val_check = check_dynamic_value_node_type(ship_node, true, false);
2567+ if (dyn_val_check) {
2568+ return dyn_val_check;
2569+ } else {
2570+ // since the value changes at runtime
2571+ // we can check only the type
2572+ break;
2573+ }
2574+ }
25572575
25582576 auto shipname = CTEXT(ship_node);
25592577 shipnum = ship_name_lookup(shipname, 1);
@@ -2920,9 +2938,16 @@ int check_sexp_syntax(int node, int return_type, int recursive, int *bad_node, i
29202938 auto ship_node = Sexp_nodes[op_node].rest;
29212939 Assert(ship_node >= 0);
29222940
2923- // we can't check special-arg ships
2924- if (Sexp_nodes[ship_node].flags & SNF_SPECIAL_ARG_IN_NODE)
2925- break;
2941+ if (is_node_value_dynamic(ship_node)) {
2942+ const int dyn_val_check = check_dynamic_value_node_type(ship_node, true, false);
2943+ if (dyn_val_check) {
2944+ return dyn_val_check;
2945+ } else {
2946+ // since the value changes at runtime
2947+ // we can check only the type
2948+ break;
2949+ }
2950+ }
29262951
29272952 ship_num = ship_name_lookup(CTEXT(ship_node), 1); // Goober5000 - include players
29282953 if (ship_num < 0) {
@@ -3060,9 +3085,16 @@ int check_sexp_syntax(int node, int return_type, int recursive, int *bad_node, i
30603085 z = Sexp_nodes[z].rest; // first argument of operator should be mission name
30613086 Assert(z >= 0);
30623087
3063- // can't check special-arg
3064- if (Sexp_nodes[z].flags & SNF_SPECIAL_ARG_IN_NODE)
3065- break;
3088+ if (is_node_value_dynamic(z)) {
3089+ const int dyn_val_check = check_dynamic_value_node_type(z, true, false);
3090+ if (dyn_val_check) {
3091+ return dyn_val_check;
3092+ } else {
3093+ // since the value changes at runtime
3094+ // we can check only the type
3095+ break;
3096+ }
3097+ }
30663098
30673099 // look for mission
30683100 count = count_items_with_name(CTEXT(z), Campaign.missions, Campaign.num_missions);
@@ -3145,9 +3177,16 @@ int check_sexp_syntax(int node, int return_type, int recursive, int *bad_node, i
31453177 }
31463178 Assert(ship_node >= 0);
31473179
3148- // can't check special-arg
3149- if (Sexp_nodes[ship_node].flags & SNF_SPECIAL_ARG_IN_NODE)
3150- break;
3180+ if (is_node_value_dynamic(ship_node)) {
3181+ const int dyn_val_check = check_dynamic_value_node_type(ship_node, true, false);
3182+ if (dyn_val_check) {
3183+ return dyn_val_check;
3184+ } else {
3185+ // since the value changes at runtime
3186+ // we can check only the type
3187+ break;
3188+ }
3189+ }
31513190
31523191 // look for the ship that has this dockpoint
31533192 ship_num = ship_name_lookup(CTEXT(ship_node), 1);
@@ -4904,8 +4943,8 @@ const ship_registry_entry *eval_ship(int node)
49044943 auto ship_it = Ship_registry_map.find(CTEXT(node));
49054944 if (ship_it != Ship_registry_map.end())
49064945 {
4907- // cache the value, unless this node is a variable or argument because the value may change
4908- if (!(Sexp_nodes[ node].type & SEXP_FLAG_VARIABLE) && !(Sexp_nodes[node].flags & SNF_SPECIAL_ARG_IN_NODE ))
4946+ // cache the value if it can't change later
4947+ if (!is_node_value_dynamic( node))
49094948 Sexp_nodes[node].cache = new sexp_cached_data(OPF_SHIP, -1, ship_it->second);
49104949
49114950 return &Ship_registry[ship_it->second];
@@ -4947,8 +4986,8 @@ wing *eval_wing(int node)
49474986 {
49484987 auto wingp = &Wings[wing_num];
49494988
4950- // cache the value, unless this node is a variable or argument because the value may change
4951- if (!(Sexp_nodes[ node].type & SEXP_FLAG_VARIABLE) && !(Sexp_nodes[node].flags & SNF_SPECIAL_ARG_IN_NODE ))
4989+ // cache the value if it can't change later
4990+ if (!is_node_value_dynamic( node))
49524991 Sexp_nodes[node].cache = new sexp_cached_data(OPF_WING, wingp);
49534992
49544993 return wingp;
@@ -4989,10 +5028,8 @@ int sexp_atoi(int node)
49895028
49905029 int num = atoi(CTEXT(node));
49915030
4992- // cache the value, unless this node is a variable, argument, or container data, because the value may change
4993- if (!(Sexp_nodes[node].type & SEXP_FLAG_VARIABLE) &&
4994- !(Sexp_nodes[node].flags & SNF_SPECIAL_ARG_IN_NODE) &&
4995- (Sexp_nodes[node].subtype != SEXP_ATOM_CONTAINER_DATA))
5031+ // cache the value if it can't change later
5032+ if (!is_node_value_dynamic(node))
49965033 Sexp_nodes[node].cache = new sexp_cached_data(OPF_NUMBER, num, -1);
49975034
49985035 return num;
@@ -22056,8 +22093,8 @@ int sexp_string_to_int(int n)
2205622093
2205722094 int num = atoi(buf);
2205822095
22059- // cache the value, unless this node is a variable or argument because the value may change
22060- if (!(Sexp_nodes[n].type & SEXP_FLAG_VARIABLE) && !(Sexp_nodes[n].flags & SNF_SPECIAL_ARG_IN_NODE ))
22096+ // cache the value if it can't change later
22097+ if (!is_node_value_dynamic(n ))
2206122098 Sexp_nodes[n].cache = new sexp_cached_data(OPF_NUMBER, num, -1);
2206222099
2206322100 return num;
@@ -31336,6 +31373,9 @@ const char *sexp_error_message(int num)
3133631373 case SEXP_CHECK_WRONG_CONTAINER_DATA_TYPE:
3133731374 return "Wrong container data type";
3133831375
31376+ case SEXP_CHECK_INVALID_SPECIAL_ARG_TYPE:
31377+ return "Invalid special argument type";
31378+
3133931379 default:
3134031380 Warning(LOCATION, "Unhandled sexp error code %d!", num);
3134131381 return "Unhandled sexp error code!";
@@ -31666,6 +31706,47 @@ int copy_node_to_replacement_args(int node, int container_value_index)
3166631706 return num_args;
3166731707}
3166831708
31709+ bool is_node_value_dynamic(int node)
31710+ {
31711+ Assertion(node >= 0, "Attempt to check whether a non-existent node has a dynamic value. Please report!");
31712+
31713+ return (Sexp_nodes[node].type & SEXP_FLAG_VARIABLE) || (Sexp_nodes[node].flags & SNF_SPECIAL_ARG_IN_NODE) ||
31714+ (Sexp_nodes[node].subtype == SEXP_ATOM_CONTAINER_DATA);
31715+ }
31716+
31717+ int check_dynamic_value_node_type(int node, bool is_string, bool is_number)
31718+ {
31719+ Assertion(is_node_value_dynamic(node),
31720+ "Attempt to check dynamic value type of a node that isn't a dynamic value. Please report!");
31721+
31722+ if (Sexp_nodes[node].type & SEXP_FLAG_VARIABLE) {
31723+ const int var_index = sexp_get_variable_index(node);
31724+ Assertion(var_index != -1,
31725+ "Attempt to get index of invalid variable %s. Please report!",
31726+ Sexp_nodes[node].text);
31727+ const int var_type = Sexp_variables[var_index].type;
31728+ if (((var_type & SEXP_VARIABLE_STRING) && !is_string) || ((var_type & SEXP_VARIABLE_NUMBER) && !is_number)) {
31729+ return SEXP_CHECK_INVALID_VARIABLE_TYPE;
31730+ }
31731+ } else if (Sexp_nodes[node].flags & SNF_SPECIAL_ARG_IN_NODE) {
31732+ if (((Sexp_nodes[node].subtype == SEXP_ATOM_STRING) && !is_string) ||
31733+ ((Sexp_nodes[node].subtype == SEXP_ATOM_NUMBER) && !is_number)) {
31734+ return SEXP_CHECK_INVALID_SPECIAL_ARG_TYPE;
31735+ }
31736+ } else if (Sexp_nodes[node].subtype == SEXP_ATOM_CONTAINER_DATA) {
31737+ const auto *p_container = get_sexp_container(Sexp_nodes[node].text);
31738+ Assertion(p_container,
31739+ "Attempt to check dynamic value node type of data of nonexistent container %s. Please report!",
31740+ Sexp_nodes[node].text);
31741+ if (!check_container_data_sexp_arg_type(p_container->type, is_string, is_number)) {
31742+ return SEXP_CHECK_WRONG_CONTAINER_DATA_TYPE;
31743+ }
31744+ } else {
31745+ UNREACHABLE("Unhandled dynamic value node %s. Please report!", Sexp_nodes[node].text);
31746+ }
31747+
31748+ return 0;
31749+ }
3166931750
3167031751void sexp_modify_variable(int n)
3167131752{
0 commit comments