From 69cd172879f5fe23c6129077563dd805c3322aac Mon Sep 17 00:00:00 2001 From: Jan Date: Mon, 25 Dec 2023 09:48:29 +0100 Subject: [PATCH] Accept setLocalVar scripts without value as default value expression --- .../EventHandlerSetScopeSequences.cpp | 45 +++++++++++++++---- .../EventHandlerSetScopeSequencesTests.cpp | 22 +++++++++ 2 files changed, 59 insertions(+), 8 deletions(-) diff --git a/src/ObjLoading/Parsing/Menu/Sequence/EventHandlerSetScopeSequences.cpp b/src/ObjLoading/Parsing/Menu/Sequence/EventHandlerSetScopeSequences.cpp index 04aa7a6f0..228ea1c1b 100644 --- a/src/ObjLoading/Parsing/Menu/Sequence/EventHandlerSetScopeSequences.cpp +++ b/src/ObjLoading/Parsing/Menu/Sequence/EventHandlerSetScopeSequences.cpp @@ -568,8 +568,15 @@ namespace menu::event_handler_set_scope_sequences create.ScriptKeyword("setLocalVarString").Tag(TAG_STRING), }), create.ScriptText().Capture(CAPTURE_VAR_NAME), - create.Label(MenuExpressionMatchers::LABEL_EXPRESSION), - create.Optional(create.Char(';')), + create.Or({ + create.And({ + create.Label(MenuExpressionMatchers::LABEL_EXPRESSION), + create.Optional(create.Char(';')), + }), + // The game seems to accept setLocalVar expressions without value as setting the var to the default value + // This only applies to the menu parser though and not to the script parser so we need to separately parse this + create.Char(';'), + }), }); } @@ -661,6 +668,26 @@ namespace menu::event_handler_set_scope_sequences std::make_unique(type, varName, std::move(expression))); } + static std::unique_ptr DefaultExpressionForType(const SetLocalVarType type) + { + switch (type) + { + case SetLocalVarType::INT: + case SetLocalVarType::BOOL: + return std::make_unique(0); + + case SetLocalVarType::FLOAT: + return std::make_unique(0.0); + + case SetLocalVarType::STRING: + return std::make_unique(std::string()); + + default: + assert(false); + return nullptr; + } + } + protected: void ProcessMatch(MenuFileParserState* state, SequenceResult& result) const override { @@ -671,13 +698,15 @@ namespace menu::event_handler_set_scope_sequences const auto& varName = MenuMatcherFactory::TokenTextValue(varNameToken); auto expression = expressionMatchers.ProcessExpression(result); - if (!expression) - throw ParsingException(varNameToken.GetPos(), "No expression"); - - if (expression && expression->IsStatic()) - EmitStaticSetLocalVar(state, varNameToken.GetPos(), typeTag, varName, std::move(expression)); + if (expression) + { + if (expression->IsStatic()) + EmitStaticSetLocalVar(state, varNameToken.GetPos(), typeTag, varName, std::move(expression)); + else + EmitDynamicSetLocalVar(state, typeTag, varName, std::move(expression)); + } else - EmitDynamicSetLocalVar(state, typeTag, varName, std::move(expression)); + EmitStaticSetLocalVar(state, varNameToken.GetPos(), typeTag, varName, DefaultExpressionForType(typeTag)); } }; diff --git a/test/ObjLoadingTests/Parsing/Menu/Sequence/EventHandlerSetScopeSequencesTests.cpp b/test/ObjLoadingTests/Parsing/Menu/Sequence/EventHandlerSetScopeSequencesTests.cpp index 63c87085e..5f2741754 100644 --- a/test/ObjLoadingTests/Parsing/Menu/Sequence/EventHandlerSetScopeSequencesTests.cpp +++ b/test/ObjLoadingTests/Parsing/Menu/Sequence/EventHandlerSetScopeSequencesTests.cpp @@ -1037,6 +1037,28 @@ namespace test::parsing::menu::sequence::event_handler_set REQUIRE(setLocalVarElement->m_value->IsStatic() == false); } + TEST_CASE("EventHandlerSetScopeSequences: Ensure setLocalVarString uses missing value as default value", "[parsing][sequence][menu]") + { + EventHandlerSetSequenceTestsHelper helper(FeatureLevel::IW4, true); + const TokenPos pos; + helper.Tokens({ + SimpleParserValue::Identifier(TokenPos(), new std::string("setLocalVarString")), + SimpleParserValue::Identifier(TokenPos(), new std::string("ui_hint_text")), + SimpleParserValue::Character(TokenPos(), ';'), + SimpleParserValue::Character(TokenPos(), ';'), + SimpleParserValue::EndOfFile(pos), + }); + + const auto result = helper.PerformTest(); + REQUIRE(result); + REQUIRE(helper.m_consumed_token_count == 3); + + REQUIRE(helper.m_event_handler_set->m_elements.size() == 0); + + const auto currentScript = helper.m_state->m_current_script.str(); + REQUIRE(currentScript == R"("setLocalVarString" "ui_hint_text" "" ; )"); + } + #pragma endregion #pragma region Unit Tests for If/ElseIf/Else/CloseBracket