mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2026-09-08 15:07:06 +00:00
* feat: add T4 menu dumping * chore: align with latest changes * feat: add T4 menu loading Load T4 menulists and menu files through the shared menu parser. Preserve T4-specific menu and listbox fields and cover loading, dumping, linking, and round-trip behaviour. * chore: make flag fields unsigned * chore: rename `expOperationEnum` -> `operationEnum` * chore: use default enum numeration for T4 operationEnum * chore: remove unused members of operationEnum * chore: remove redundant op from operationEnum * refactor: align T4 expression operand names with IW3 * chore: small adjustment to iw3,t4 types --------- Co-authored-by: Jan Laupetin <[email protected]>
270 lines
9.7 KiB
C++
270 lines
9.7 KiB
C++
#include "Game/T4/Menu/LoaderMenuListT4.h"
|
|
|
|
#include "Game/T4/GameT4.h"
|
|
#include "SearchPath/MockSearchPath.h"
|
|
|
|
#include <catch2/catch_approx.hpp>
|
|
#include <catch2/catch_test_macros.hpp>
|
|
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
|
#include <memory>
|
|
#include <string>
|
|
|
|
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>();
|
|
material->info.name = m_zone.Memory().Dup(name.c_str());
|
|
m_context.AddAsset<AssetMaterial>(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<menuDef_t*>(asset->m_ptr);
|
|
}
|
|
|
|
private:
|
|
Zone m_zone;
|
|
MockSearchPath m_search_path;
|
|
std::unique_ptr<IAssetCreator> 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<MenuList*>(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
|