#include "Game/T4/Menu/LoaderMenuListT4.h" #include "Game/T4/GameT4.h" #include "SearchPath/MockSearchPath.h" #include #include #include #include #include using namespace T4; using namespace std::literals; using namespace Catch; using namespace Catch::Matchers; namespace { class MenuLoadingTestHelper { public: MenuLoadingTestHelper() : m_zone("MockZone", 0, GameId::T4, GamePlatform::PC), m_creator_collection(m_zone), m_context(m_zone, &m_creator_collection, &m_ignored_asset_lookup) { m_loader = menu::CreateMenuListLoaderT4(m_zone.Memory(), m_search_path); } void AddFile(std::string name, std::string contents) { m_search_path.AddFileData(std::move(name), std::move(contents)); } Material* AddMaterial(const std::string& name) { auto* material = m_zone.Memory().Alloc(); material->info.name = m_zone.Memory().Dup(name.c_str()); m_context.AddAsset(name, material); return material; } AssetCreationResult LoadMenuList(const std::string& name) { return m_loader->CreateAsset(name, m_context); } menuDef_t* GetMenu(const std::string& name) { const auto* asset = m_zone.m_pools.GetAsset(ASSET_TYPE_MENU, name); REQUIRE(asset); return static_cast(asset->m_ptr); } private: Zone m_zone; MockSearchPath m_search_path; std::unique_ptr m_loader; AssetCreatorCollection m_creator_collection; IgnoredAssetLookup m_ignored_asset_lookup; AssetCreationContext m_context; }; TEST_CASE("MenuListLoaderT4: Loads dumped menus and preserves T4 data", "[t4][menu][assetloader]") { MenuLoadingTestHelper helper; const auto* backgroundMaterial = helper.AddMaterial("background_material"); const auto* selectMaterial = helper.AddMaterial("select_material"); const auto* listboxBackgroundMaterial = helper.AddMaterial("listbox_background_material"); const auto* highlightMaterial = helper.AddMaterial("highlight_material"); helper.AddFile("ui_mp/menus.txt", R"MENU({ loadMenu { "ui_mp/main.menu" } loadMenu { "ui_mp/popup.menu" } } )MENU"); helper.AddFile("ui_mp/main.menu", R"MENU({ menuDef { name "main" fullscreen 1 rect 0 0 640 480 0 0 background "background_material" forecolor 0.8 0.7 0.6 0.5 disablecolor 0.2 0.3 0.4 0.5 visible when(dvarbool("ui_show_main")); onOpen { play menu_open; setLocalVarInt ui_highlight 5; } onFocus { setdvar ui_menu_focused 1; } itemDef { name "empty_text" text "" rect 10 20 180 24 1 2 type 1 visible 1 forecolor 0.9 0.9 1 0.07 dvar "ui_fallback_text" action { setItemColor empty_text borderColor 0.1 0.1 0.12 0.5; execondvarstringvalue ui_zfeather 0 "set cg_laserLight 0"; } exp material (localvarstring("ui_material")); } itemDef { name "server_list" rect 20 60 300 200 0 0 type 6 columns 2 0 120 32 0 120 180 48 1 elementwidth 300 elementheight 20 feeder 2 elementtype 1 notselectable noscrollbars usepaging disablecolor 0.6 0.5 0.4 0.3 selectBorder 1 1 1 1 focusColor 0.1 0.2 0.3 0.4 selectIcon "select_material" backgroundItemListbox "listbox_background_material" highlightTexture "highlight_material" doubleclick { play mouse_click; } onListboxSelectionChange { setdvar ui_server_selected 1; } } itemDef { name "default_text" text "-" maxChars 2 } } } )MENU"); helper.AddFile("ui_mp/popup.menu", R"MENU({ menuDef { name "popup" rect 40 40 200 100 0 0 popup visible when(partyismissingmappack()); onESC { close self; } } } )MENU"); const auto result = helper.LoadMenuList("ui_mp/menus.txt"); REQUIRE(result.HasBeenSuccessful()); const auto* menuList = static_cast(result.GetAssetInfo()->m_ptr); const auto* mainMenu = helper.GetMenu("main"); const auto* popupMenu = helper.GetMenu("popup"); REQUIRE(menuList->name == "ui_mp/menus.txt"s); REQUIRE(menuList->menuCount == 2); REQUIRE(menuList->menus[0] == mainMenu); REQUIRE(menuList->menus[1] == popupMenu); REQUIRE(mainMenu->window.background == backgroundMaterial); REQUIRE(mainMenu->window.dynamicFlags[0] & WINDOW_FLAG_NON_DEFAULT_FORECOLOR); REQUIRE_THAT(mainMenu->disableColor[0], WithinRel(0.2f)); REQUIRE_THAT(mainMenu->disableColor[3], WithinRel(0.5f)); REQUIRE(mainMenu->visibleExp.numEntries == 3); REQUIRE(mainMenu->visibleExp.entries[0]->type == EET_OPERATOR); REQUIRE(mainMenu->visibleExp.entries[0]->data.op == OP_DVARBOOL); REQUIRE(mainMenu->visibleExp.entries[1]->data.operand.dataType == VAL_STRING); REQUIRE(mainMenu->visibleExp.entries[1]->data.operand.internals.stringVal == "ui_show_main"s); REQUIRE(mainMenu->visibleExp.entries[2]->data.op == OP_RIGHTPAREN); REQUIRE(mainMenu->onOpen == R"("play" "menu_open" ; "setLocalVarInt" "ui_highlight" "5" ; )"s); REQUIRE(mainMenu->onFocus == R"("setdvar" "ui_menu_focused" "1" ; )"s); REQUIRE(mainMenu->itemCount == 3); const auto* emptyTextItem = mainMenu->items[0]; REQUIRE(emptyTextItem->parent == mainMenu); REQUIRE(emptyTextItem->text != nullptr); REQUIRE(emptyTextItem->text == ""s); REQUIRE(emptyTextItem->dvar == "ui_fallback_text"s); REQUIRE(emptyTextItem->window.dynamicFlags[0] & WINDOW_FLAG_VISIBLE); REQUIRE(emptyTextItem->window.dynamicFlags[0] & WINDOW_FLAG_NON_DEFAULT_FORECOLOR); REQUIRE( emptyTextItem->action == R"("setItemColor" "empty_text" "borderColor" "0.1" "0.1" "0.12" "0.5" ; "execondvarstringvalue" "ui_zfeather" "0" "set cg_laserLight 0" ; )"s); REQUIRE(emptyTextItem->materialExp.numEntries == 3); REQUIRE(emptyTextItem->materialExp.entries[0]->data.op == OP_LOCALVARSTRING); const auto* listItem = mainMenu->items[1]; REQUIRE(listItem->typeData.listBox); REQUIRE(listItem->typeData.listBox->numColumns == 2); REQUIRE(listItem->typeData.listBox->columnInfo[1].pos == 120); REQUIRE(listItem->typeData.listBox->columnInfo[1].width == 180); REQUIRE(listItem->typeData.listBox->notselectable == 1); REQUIRE(listItem->typeData.listBox->noScrollBars == 1); REQUIRE(listItem->typeData.listBox->usePaging == 1); REQUIRE_THAT(listItem->typeData.listBox->disableColor[0], WithinRel(0.6f)); REQUIRE_THAT(listItem->typeData.listBox->focusColor[2], WithinRel(0.3f)); REQUIRE(listItem->typeData.listBox->selectIcon == selectMaterial); REQUIRE(listItem->typeData.listBox->backgroundItemListbox == listboxBackgroundMaterial); REQUIRE(listItem->typeData.listBox->highlightTexture == highlightMaterial); REQUIRE(listItem->typeData.listBox->onDoubleClick == R"("play" "mouse_click" ; )"s); REQUIRE(listItem->onListboxSelectionChange == R"("setdvar" "ui_server_selected" "1" ; )"s); const auto* defaultTextItem = mainMenu->items[2]; REQUIRE(defaultTextItem->type == 0); REQUIRE(defaultTextItem->typeData.editField); REQUIRE(defaultTextItem->typeData.editField->maxChars == 2); REQUIRE(popupMenu->window.staticFlags & WINDOW_FLAG_POPUP); REQUIRE(popupMenu->visibleExp.numEntries == 2); REQUIRE(popupMenu->visibleExp.entries[0]->data.op == OP_PARTYISMISSINGMAPPACK); REQUIRE(popupMenu->visibleExp.entries[1]->data.op == OP_RIGHTPAREN); REQUIRE(popupMenu->onESC == R"("close" "self" ; )"s); } TEST_CASE("MenuListLoaderT4: Ensure optimizing item rect considers relative positions", "[t4][menu][assetloader]") { MenuLoadingTestHelper helper; helper.AddFile("ui_mp/test.menu", R"MENU({ menuDef { name "test" rect 2 4 100 100 itemDef { rect 1 2 20 20 exp rect x (5) exp rect y (7) } } } )MENU"); const auto result = helper.LoadMenuList("ui_mp/test.menu"); REQUIRE(result.HasBeenSuccessful()); const auto* testMenu = helper.GetMenu("test"); REQUIRE(testMenu->window.rectClient.x == Approx(2)); REQUIRE(testMenu->window.rectClient.y == Approx(4)); REQUIRE(testMenu->window.rect.x == Approx(2)); REQUIRE(testMenu->window.rect.y == Approx(4)); REQUIRE(testMenu->itemCount == 1); const auto* item = testMenu->items[0]; REQUIRE(item->rectXExp.numEntries == 0); REQUIRE(item->rectYExp.numEntries == 0); REQUIRE(item->window.rectClient.x == Approx(5)); REQUIRE(item->window.rectClient.y == Approx(7)); REQUIRE(item->window.rect.x == Approx(7)); REQUIRE(item->window.rect.y == Approx(11)); } } // namespace