diff --git a/resources/w/ui/hud_uni_ext.json b/resources/w/ui/hud_uni_ext.json new file mode 100644 index 0000000..b19d65d --- /dev/null +++ b/resources/w/ui/hud_uni_ext.json @@ -0,0 +1,88 @@ +{ + "root": { + "type": "FrameLayout", + "name": "hud_root", + "width": "match_parent", + "height": "match_parent", + "children": [ + { + "type": "Button", + "name": "phoneButton", + "horizontal_gravity": "right", + "vertical_gravity": "bottom", + "x": -46, + "y": -46, + "width": 229, + "height": 229, + "clickZoneWidth": 104, + "clickZoneHeight": 104, + "textures": { + "normal": "resources/w/ui/img/Phone001_State=Default.png", + "hover": "resources/w/ui/img/Phone001_State=Selected.png", + "pressed": "resources/w/ui/img/Phone001_State=Tap.png" + } + }, + { + "type": "Button", + "name": "settingsButton", + "horizontal_gravity": "right", + "vertical_gravity": "top", + "x": -23, + "y": -35, + "width": 200, + "height": 200, + "clickZoneWidth": 176, + "clickZoneHeight": 176, + "textures": { + "normal": "resources/w/ui/img/Settings001_State=Default001.png", + "hover": "resources/w/ui/img/Settings001_State=Selected001.png", + "pressed": "resources/w/ui/img/Settings001_State=Tap001.png" + } + }, + { + "type": "Button", + "name": "inventoryButton", + "horizontal_gravity": "right", + "vertical_gravity": "top", + "x": 110, + "y": -18, + "width": 165, + "height": 165, + "clickZoneWidth": 151, + "clickZoneHeight": 151, + "textures": { + "normal": "resources/w/ui/img/Inventory001_State=Default001.png", + "hover": "resources/w/ui/img/Inventory001_State=Selected001.png", + "pressed": "resources/w/ui/img/Inventory001_State=Tap001.png" + } + }, + { + "type": "Button", + "name": "journalButton", + "horizontal_gravity": "right", + "vertical_gravity": "top", + "x": 220, + "y": -18, + "width": 165, + "height": 165, + "clickZoneWidth": 89, + "clickZoneHeight": 89, + "textures": { + "normal": "resources/w/ui/img/Journal001_State=Default.png", + "hover": "resources/w/ui/img/Journal001_State=Selected.png", + "pressed": "resources/w/ui/img/Journal001_State=Tap.png" + } + }, + { + "type": "StaticImage", + "name": "location", + "width": 380, + "height": 64, + "y": 30, + "horizontal_gravity": "center", + "vertical_align": "top", + "texture": "resources/w/ui/img/Location_uni_ext.png" + } + ] + } +} diff --git a/resources/w/ui/hud_uni_int.json b/resources/w/ui/hud_uni_int.json new file mode 100644 index 0000000..1c58f58 --- /dev/null +++ b/resources/w/ui/hud_uni_int.json @@ -0,0 +1,88 @@ +{ + "root": { + "type": "FrameLayout", + "name": "hud_root", + "width": "match_parent", + "height": "match_parent", + "children": [ + { + "type": "Button", + "name": "phoneButton", + "horizontal_gravity": "right", + "vertical_gravity": "bottom", + "x": -46, + "y": -46, + "width": 229, + "height": 229, + "clickZoneWidth": 104, + "clickZoneHeight": 104, + "textures": { + "normal": "resources/w/ui/img/Phone001_State=Default.png", + "hover": "resources/w/ui/img/Phone001_State=Selected.png", + "pressed": "resources/w/ui/img/Phone001_State=Tap.png" + } + }, + { + "type": "Button", + "name": "settingsButton", + "horizontal_gravity": "right", + "vertical_gravity": "top", + "x": -23, + "y": -35, + "width": 200, + "height": 200, + "clickZoneWidth": 176, + "clickZoneHeight": 176, + "textures": { + "normal": "resources/w/ui/img/Settings001_State=Default001.png", + "hover": "resources/w/ui/img/Settings001_State=Selected001.png", + "pressed": "resources/w/ui/img/Settings001_State=Tap001.png" + } + }, + { + "type": "Button", + "name": "inventoryButton", + "horizontal_gravity": "right", + "vertical_gravity": "top", + "x": 110, + "y": -18, + "width": 165, + "height": 165, + "clickZoneWidth": 151, + "clickZoneHeight": 151, + "textures": { + "normal": "resources/w/ui/img/Inventory001_State=Default001.png", + "hover": "resources/w/ui/img/Inventory001_State=Selected001.png", + "pressed": "resources/w/ui/img/Inventory001_State=Tap001.png" + } + }, + { + "type": "Button", + "name": "journalButton", + "horizontal_gravity": "right", + "vertical_gravity": "top", + "x": 220, + "y": -18, + "width": 165, + "height": 165, + "clickZoneWidth": 89, + "clickZoneHeight": 89, + "textures": { + "normal": "resources/w/ui/img/Journal001_State=Default.png", + "hover": "resources/w/ui/img/Journal001_State=Selected.png", + "pressed": "resources/w/ui/img/Journal001_State=Tap.png" + } + }, + { + "type": "StaticImage", + "name": "location", + "width": 380, + "height": 64, + "y": 30, + "horizontal_gravity": "center", + "vertical_align": "top", + "texture": "resources/w/ui/img/Location_uni_int.png" + } + ] + } +} diff --git a/resources/w/ui/img/Darklands001_State=Default001.png b/resources/w/ui/img/Darklands001_State=Default001.png new file mode 100644 index 0000000..103f627 --- /dev/null +++ b/resources/w/ui/img/Darklands001_State=Default001.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5eb57e42514b538c6e235c93b08a7c0c672c64089edf6ee6bcc9ba9abe6ac4f5 +size 798847 diff --git a/resources/w/ui/img/Darklands001_State=Hover001.png b/resources/w/ui/img/Darklands001_State=Hover001.png new file mode 100644 index 0000000..b24d45d --- /dev/null +++ b/resources/w/ui/img/Darklands001_State=Hover001.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ee3994ebaf2f21b37ec714cecc2e41f1d519137eb52336c2e98b507d028b2ed3 +size 953963 diff --git a/resources/w/ui/img/Darklands001_State=Pressed001.png b/resources/w/ui/img/Darklands001_State=Pressed001.png new file mode 100644 index 0000000..cb11842 --- /dev/null +++ b/resources/w/ui/img/Darklands001_State=Pressed001.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:adef07163acf6f22d325099004ef467f86a8948c713b7fcaf6553e5e6280e3ed +size 725062 diff --git a/src/Game.cpp b/src/Game.cpp index ea21886..26ea6ec 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -364,6 +364,7 @@ namespace ZL } currentLocation->cameraAzimuth = destRotY; currentLocation->scriptEngine.callLocationEnterCallback(); + menuManager.onLocationChanged(destName); }; locations["uni_exterior"]->onTeleport = teleportCallback; diff --git a/src/MenuManager.cpp b/src/MenuManager.cpp index 5b8a100..dabf3e4 100644 --- a/src/MenuManager.cpp +++ b/src/MenuManager.cpp @@ -81,6 +81,8 @@ namespace ZL { hudStep5aRoot = loadUiFromFile("resources/w/ui/hud_step5a.json", renderer, zipFile); hudStep5bRoot = loadUiFromFile("resources/w/ui/hud_step5b.json", renderer, zipFile); hudStep5abRoot = loadUiFromFile("resources/w/ui/hud_step5ab.json", renderer, zipFile); + hudUniExtRoot = loadUiFromFile("resources/w/ui/hud_uni_ext.json", renderer, zipFile); + hudUniIntRoot = loadUiFromFile("resources/w/ui/hud_uni_int.json", renderer, zipFile); phoneChatListRoot = loadUiFromFile("resources/w/ui/screen_phone_chat_list.json", renderer, zipFile); phoneChat1Root = loadUiFromFile("resources/w/ui/screen_phone_chat1.json", renderer, zipFile); phoneChat2Root = loadUiFromFile("resources/w/ui/screen_phone_chat2.json", renderer, zipFile); @@ -123,12 +125,6 @@ namespace ZL { uiManager.setButtonCallback("inventoryExitButton", [this](const std::string&) { closeInventory(); }); - /* - uiManager.pushMenuFromSavedRoot(inventoryRoot); - - uiManager.setTextButtonCallback("close_inventory_button", [this](const std::string&) { - closeInventory(); - }); const auto& items = inventory->getItems(); std::string itemText; @@ -139,11 +135,74 @@ namespace ZL { itemText = "Inventory (" + std::to_string(items.size()) + " items)\n\n"; for (size_t i = 0; i < items.size(); ++i) { itemText += std::to_string(i + 1) + ". " + items[i].name + "\n"; + const int maxSlots = 7; + + for (int i = 0; i < maxSlots; ++i) { + const std::string btnName = "item" + std::to_string(i + 1) + "Button"; + if (i < static_cast(items.size())) { + uiManager.setNodeVisible(btnName, true); + + auto btn = uiManager.findButton(btnName); + if (btn) { + auto tex = renderer.textureManager.LoadFromPng(items[i].icon, zipFile_, true); + btn->texNormal = btn->texHover = btn->texPressed = tex; + } + + uiManager.setButtonCallback(btnName, [this, i](const std::string&) { + selectInventoryItem(i); + }); + } + else { + uiManager.setNodeVisible(btnName, false); + } + } + + inventorySelectedIndex_ = -1; + if (!items.empty()) { + selectInventoryItem(0); + } } } - uiManager.setText("inventory_items_text", itemText);*/ } + void MenuManager::selectInventoryItem(int index) { + const auto& items = inventory->getItems(); + if (index < 0 || index >= static_cast(items.size())) return; + + // Revert previously selected button to its regular icon + if (inventorySelectedIndex_ >= 0 && inventorySelectedIndex_ < static_cast(items.size())) { + const std::string prevBtnName = "item" + std::to_string(inventorySelectedIndex_ + 1) + "Button"; + auto prevBtn = uiManager.findButton(prevBtnName); + if (prevBtn) { + auto tex = renderer.textureManager.LoadFromPng(items[inventorySelectedIndex_].icon, zipFile_, true); + prevBtn->texNormal = prevBtn->texHover = prevBtn->texPressed = tex; + } + } + + inventorySelectedIndex_ = index; + const auto& item = items[index]; + + // Highlight newly selected button with its selected icon + const std::string btnName = "item" + std::to_string(index + 1) + "Button"; + auto btn = uiManager.findButton(btnName); + if (btn) { + const std::string& selPath = item.selectedIcon.empty() ? item.icon : item.selectedIcon; + auto tex = renderer.textureManager.LoadFromPng(selPath, zipFile_, true); + btn->texNormal = btn->texHover = btn->texPressed = tex; + } + + // Update the large selected picture on the right panel + auto img = uiManager.findStaticImage("selectedItemPic"); + if (img) { + const std::string& path = item.selectedIcon.empty() ? item.icon : item.selectedIcon; + img->texture = renderer.textureManager.LoadFromPng(path, zipFile_, true); + } + + uiManager.setText("selectedText", item.name); + uiManager.setText("selectedDescription", item.description); + } + + void MenuManager::closeInventory() { state = GameState::Gameplay; uiManager.popMenu(); @@ -267,6 +326,11 @@ namespace ZL { openPhoneScreen(); }); } + if (uiManager.findButton("inventoryButton")) { + uiManager.setButtonCallback("inventoryButton", [this](const std::string&) { + openInventory(); + }); + } if (uiManager.findButton("journalButton")) { uiManager.setButtonCallback("journalButton", [this](const std::string&) { openQuestJournal(); @@ -274,6 +338,37 @@ namespace ZL { } if (tutorialPhoneScreenOpened) uiManager.setNodeVisible("hint6a", false); if (tutorialJournalScreenOpened) uiManager.setNodeVisible("hint6b", false); + if (tutorialPhoneScreenOpened && tutorialJournalScreenOpened) { + tutorialStep = TutorialStep::Step6; + } + } + + void MenuManager::setupGameplayHudCallbacks() { + uiManager.setButtonCallback("phoneButton", [this](const std::string&) { + openPhoneScreen(); + }); + uiManager.setButtonCallback("inventoryButton", [this](const std::string&) { + openInventory(); + }); + uiManager.setButtonCallback("journalButton", [this](const std::string&) { + openQuestJournal(); + }); + } + + void MenuManager::onLocationChanged(const std::string& locationName) { + if (state != GameState::Gameplay) return; + + if (locationName == "uni_exterior") { + uiManager.replaceRoot(hudUniExtRoot); + setupGameplayHudCallbacks(); + } else if (locationName == "uni_interior") { + uiManager.replaceRoot(hudUniIntRoot); + setupGameplayHudCallbacks(); + } else { + // Returning to dorm: reuse step5ab, suppress already-completed hints + uiManager.replaceRoot(hudStep5abRoot); + setupStep5Callbacks(); + } } void MenuManager::advanceTutorialStep() { diff --git a/src/MenuManager.h b/src/MenuManager.h index 3e02e44..e52c914 100644 --- a/src/MenuManager.h +++ b/src/MenuManager.h @@ -28,6 +28,7 @@ namespace ZL { Step3, // Pinch-zoom hint Step4, // Pick-up item hint Step5, // Post-pickup reaction (sub-state: which items were collected) + Step6, // Tutorial complete — both phone and journal opened }; class MenuManager { @@ -40,6 +41,7 @@ namespace ZL { void setup(Inventory& inv, const std::string& zipFile); void openInventory(); + void selectInventoryItem(int index); void closeInventory(); void openQuestJournal(); @@ -58,6 +60,7 @@ namespace ZL { void advanceTutorialStep(); void onItemPickedUp(const std::string& itemId); + void onLocationChanged(const std::string& locationName); TutorialStep tutorialStep = TutorialStep::Step0; @@ -70,6 +73,7 @@ namespace ZL { void selectQuestByIndex(int index); void refreshItemPickupHud(); void setupStep5Callbacks(); + void setupGameplayHudCallbacks(); void resetPhoneChatNodes(); void recomputePhoneChatPositions(); void openPhoneChatFromList(std::shared_ptr chatRoot, const std::string& dialogueId); @@ -94,6 +98,8 @@ namespace ZL { std::shared_ptr hudStep5aRoot; std::shared_ptr hudStep5bRoot; std::shared_ptr hudStep5abRoot; + std::shared_ptr hudUniExtRoot; + std::shared_ptr hudUniIntRoot; std::shared_ptr phoneChatListRoot; std::shared_ptr phoneChat1Root; std::shared_ptr phoneChat2Root;