diff --git a/code/parse/sexp.cpp b/code/parse/sexp.cpp index 941595d4644..2773b7adbc9 100644 --- a/code/parse/sexp.cpp +++ b/code/parse/sexp.cpp @@ -3970,18 +3970,22 @@ int check_sexp_syntax(int node, int desired_return_type, int recursive, int *bad case OPF_GAME_SND: if (node_subtype == SEXP_ATOM_NUMBER) { - if (!gamesnd_get_by_tbl_index(atoi(CTEXT(node))).isValid()) - { - return SEXP_CHECK_NUM_RANGE_INVALID; - } + int node_num = atoi(CTEXT(node)); + if (node_num == -1) + break; // explicitly allow a sound of -1, indicating either "no sound" or "default" + if (!gamesnd_get_by_tbl_index(node_num).isValid()) + return SEXP_CHECK_INVALID_GAME_SND; } else if (node_subtype == SEXP_ATOM_STRING) { - if (stricmp(CTEXT(node), SEXP_NONE_STRING) != 0 && !gamesnd_get_by_name(CTEXT(node)).isValid()) - { + auto node_text = CTEXT(node); + if (stricmp(node_text, SEXP_NONE_STRING) == 0) + break; // explicitly allow "no sound", although some sexps might interpret it as "default" + if (!gamesnd_get_by_name(node_text).isValid()) return SEXP_CHECK_INVALID_GAME_SND; - } } + else if (node_subtype != SEXP_ATOM_LIST) + return SEXP_CHECK_INVALID_GAME_SND; break; case OPF_FIREBALL: @@ -3989,17 +3993,15 @@ int check_sexp_syntax(int node, int desired_return_type, int recursive, int *bad { int num = atoi(CTEXT(node)); if (!SCP_vector_inbounds(Fireball_info, num)) - { - return SEXP_CHECK_NUM_RANGE_INVALID; - } + return SEXP_CHECK_INVALID_FIREBALL; } else if (node_subtype == SEXP_ATOM_STRING) { if (fireball_info_lookup(CTEXT(node)) < 0) - { return SEXP_CHECK_INVALID_FIREBALL; - } } + else if (node_subtype != SEXP_ATOM_LIST) + return SEXP_CHECK_INVALID_FIREBALL; break; case OPF_SPECIES: diff --git a/code/parse/sexp.h b/code/parse/sexp.h index ec111c0295f..68299082412 100644 --- a/code/parse/sexp.h +++ b/code/parse/sexp.h @@ -25,6 +25,7 @@ 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! @@ -1481,6 +1482,7 @@ extern int run_sexp(const char* sexpression, bool run_eval_num = false, bool *is 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); diff --git a/code/parse/sexp/LuaSEXP.cpp b/code/parse/sexp/LuaSEXP.cpp index 0fbf208cc84..f1b2f3a3560 100644 --- a/code/parse/sexp/LuaSEXP.cpp +++ b/code/parse/sexp/LuaSEXP.cpp @@ -262,8 +262,9 @@ luacpp::LuaValue LuaSEXP::sexpToLua(int node, int argnum, int parent_node) const return LuaValue::createValue(_action.getLuaState(), l_Weaponclass.Set(weapon_info_lookup(name))); } case OPF_GAME_SND: { - auto name = CTEXT(node); - return LuaValue::createValue(_action.getLuaState(), l_SoundEntry.Set(sound_entry_h(gamesnd_get_by_name(name)))); + // handle a table index, an operator, or a name (matching what the core SEXPs accept); + // -1 or yields an invalid handle that the script can check with :isValid() + return LuaValue::createValue(_action.getLuaState(), l_SoundEntry.Set(sound_entry_h(sexp_get_sound_index(node)))); } case OPF_STRING: { auto text = CTEXT(node);