diff --git a/src/ObjLoading/Parsing/Menu/Matcher/MenuMatcherFactory.cpp b/src/ObjLoading/Parsing/Menu/Matcher/MenuMatcherFactory.cpp index fb3134c5..0bfafe71 100644 --- a/src/ObjLoading/Parsing/Menu/Matcher/MenuMatcherFactory.cpp +++ b/src/ObjLoading/Parsing/Menu/Matcher/MenuMatcherFactory.cpp @@ -67,6 +67,22 @@ MatcherFactoryWrapper MenuMatcherFactory::TextNoChain() const })); } +MatcherFactoryWrapper MenuMatcherFactory::TextNoChainOrNumeric() const +{ + return Or({ + TextNoChain(), + Numeric().Transform( + [](const token_list_t& tokens) -> SimpleParserValue + { + const auto& token = tokens[0].get(); + if (token.m_type == SimpleParserValueType::INTEGER) + return SimpleParserValue::String(token.GetPos(), new std::string(std::to_string(token.IntegerValue()))); + + return SimpleParserValue::String(token.GetPos(), new std::string(std::format("{:g}", token.FloatingPointValue()))); + }), + }); +} + MatcherFactoryWrapper MenuMatcherFactory::Numeric() const { return MatcherFactoryWrapper(Or({ diff --git a/src/ObjLoading/Parsing/Menu/Matcher/MenuMatcherFactory.h b/src/ObjLoading/Parsing/Menu/Matcher/MenuMatcherFactory.h index 9ba057ad..b02e6e33 100644 --- a/src/ObjLoading/Parsing/Menu/Matcher/MenuMatcherFactory.h +++ b/src/ObjLoading/Parsing/Menu/Matcher/MenuMatcherFactory.h @@ -27,6 +27,7 @@ namespace menu [[nodiscard]] MatcherFactoryWrapper Text() const; [[nodiscard]] MatcherFactoryWrapper TextOrNumeric() const; [[nodiscard]] MatcherFactoryWrapper TextNoChain() const; + [[nodiscard]] MatcherFactoryWrapper TextNoChainOrNumeric() const; [[nodiscard]] MatcherFactoryWrapper Numeric() const; [[nodiscard]] MatcherFactoryWrapper TextExpression() const; diff --git a/src/ObjLoading/Parsing/Menu/Sequence/ItemScopeSequences.cpp b/src/ObjLoading/Parsing/Menu/Sequence/ItemScopeSequences.cpp index 20baf92a..05f2b5e9 100644 --- a/src/ObjLoading/Parsing/Menu/Sequence/ItemScopeSequences.cpp +++ b/src/ObjLoading/Parsing/Menu/Sequence/ItemScopeSequences.cpp @@ -385,7 +385,7 @@ namespace create.OptionalLoop(create.And({ create.TextNoChain().Capture(CAPTURE_STEP_NAME), create.Optional(create.Char(';')), - create.TextNoChain().Capture(CAPTURE_STEP_VALUE), + create.TextNoChainOrNumeric().Capture(CAPTURE_STEP_VALUE), create.Optional(create.Char(';')), })), create.Char('}'), diff --git a/test/ObjLoadingTests/Parsing/Menu/Sequence/ItemScopeSequencesTests.cpp b/test/ObjLoadingTests/Parsing/Menu/Sequence/ItemScopeSequencesTests.cpp index 6324e102..a6f1e356 100644 --- a/test/ObjLoadingTests/Parsing/Menu/Sequence/ItemScopeSequencesTests.cpp +++ b/test/ObjLoadingTests/Parsing/Menu/Sequence/ItemScopeSequencesTests.cpp @@ -402,4 +402,36 @@ namespace test::parsing::menu::sequence::item REQUIRE(multiValueFeatures->m_string_values[2] == "wide 16:10"); REQUIRE(multiValueFeatures->m_string_values[3] == "wide 16:9"); } + + TEST_CASE("ItemScopeSequences: dvarStrList accepts numeric values", "[parsing][sequence][menu]") + { + ItemSequenceTestsHelper helper(FeatureLevel::IW4, false); + const TokenPos pos; + helper.Tokens({ + SimpleParserValue::Identifier(pos, new std::string("dvarStrList")), + SimpleParserValue::Character(pos, '{'), + SimpleParserValue::String(pos, new std::string("@MPUI_RULES_5MINUTES")), + SimpleParserValue::Integer(pos, 5), + SimpleParserValue::String(pos, new std::string()), + SimpleParserValue::Integer(pos, 0), + SimpleParserValue::Character(pos, '}'), + SimpleParserValue::EndOfFile(pos), + }); + + helper.m_item->m_feature_type = CommonItemFeatureType::MULTI_VALUE; + helper.m_item->m_multi_value_features = std::make_unique(); + + const auto result = helper.PerformTest(); + + REQUIRE(result); + REQUIRE(helper.m_consumed_token_count == 7); + + const auto* item = helper.m_state->m_current_item; + REQUIRE(item); + const auto* multiValueFeatures = item->m_multi_value_features.get(); + REQUIRE(multiValueFeatures); + + REQUIRE(multiValueFeatures->m_step_names == std::vector{"@MPUI_RULES_5MINUTES", ""}); + REQUIRE(multiValueFeatures->m_string_values == std::vector{"5", "0"}); + } } // namespace test::parsing::menu::sequence::item