Working on UI improvement

This commit is contained in:
Vladislav Khorev committed 2026-05-27 17:30:41 +03:00
1 parent 0da1ae124c
commit 80af0e61ae
43 files changed
+889 -64

No files matched your search

+1 -1
View File
@@ -15,7 +15,7 @@ namespace ZL {
using std::max;
#endif
constexpr float DEFAULT_ZOOM = 24.f;
constexpr float DEFAULT_ZOOM = 6.f;
class Environment {
public:
+52 -2
View File
@@ -369,6 +369,24 @@ namespace ZL
locations["uni_interior"]->onTeleport = teleportCallback;
locations["location_dorm"]->onTeleport = teleportCallback;
// Wire tutorial advance callbacks for all locations.
// advanceTutorialStep() guards against double-advancing, so sharing is safe.
for (auto& [name, loc] : locations) {
loc->dialogueSystem.setOnDialogueAdvanced([this]() {
menuManager.advanceTutorialStep();
});
loc->onPlayerFloorWalk = [this]() {
if (menuManager.tutorialStep == TutorialStep::Step2) {
menuManager.advanceTutorialStep();
}
};
}
// Wire inventory item-pickup callback for tutorial step4/5 item tracking.
inventory.onItemAdded = [this](const std::string& itemId) {
menuManager.onItemPickedUp(itemId);
};
currentLocation = locations["location_dorm"];
currentLocation->scriptEngine.callLocationEnterCallback();
@@ -552,6 +570,8 @@ namespace ZL
updateDarklandsFlash(delta);
menuManager.uiManager.update(static_cast<float>(delta));
if (currentLocation)
{
currentLocation->update(delta);
@@ -661,6 +681,10 @@ namespace ZL
if (Environment::zoom < zoomstep) {
Environment::zoom = zoomstep;
}
// Tutorial step3 → step4: any mouse-wheel scroll counts as "zoom gesture".
if (menuManager.tutorialStep == TutorialStep::Step3) {
menuManager.advanceTutorialStep();
}
}
if (event.type == SDL_KEYDOWN && event.key.repeat == 0) {
@@ -722,8 +746,10 @@ namespace ZL
break;
case SDLK_l:
x = x - 1;
std::cout << "current x: " << x << std::endl;
//x = x - 1;
//std::cout << "current x: " << x << std::endl;
std::cout << "Azimuth: " << currentLocation->cameraAzimuth << std::endl;
std::cout << "Inclination: " << currentLocation->cameraInclination << std::endl;
break;
case SDLK_c:
@@ -826,6 +852,10 @@ namespace ZL
// movement threshold was crossed.
currentLocation->lastMouseX = eventX;
currentLocation->lastMouseY = eventY;
// Snapshot current angles so we can measure how far the user rotates.
dragStartAzimuth = currentLocation->cameraAzimuth;
dragStartInclination = currentLocation->cameraInclination;
}
}
@@ -881,6 +911,13 @@ namespace ZL
static const float zoomMin = 2.0f;
if (newZoom < zoomMin) newZoom = zoomMin;
Environment::zoom = newZoom;
// Tutorial step3 → step4: detect a significant pinch-zoom (≥ 2 zoom units).
if (menuManager.tutorialStep == TutorialStep::Step3) {
if (std::abs(Environment::zoom - pinchStartZoom) >= 2.0f) {
menuManager.advanceTutorialStep();
}
}
}
void Game::endPinch()
@@ -1004,6 +1041,19 @@ namespace ZL
if (currentLocation) {
// Forwarded for dialogue hover and (when cameraDragging) camera rotation.
currentLocation->handleMotion(fingerId, eventX, eventY, mx, my);
// Tutorial step1 → step2: detect a significant camera rotation on BOTH axes.
// ~0.15 rad (≈8.6°) per axis ensures the user intentionally panned in 2D,
// not just nudged a single axis by accident.
static constexpr float TUTORIAL_ROTATION_THRESHOLD = 0.15f;
if (cameraDragging
&& menuManager.tutorialStep == TutorialStep::Step1) {
float deltaAz = std::abs(currentLocation->cameraAzimuth - dragStartAzimuth);
float deltaInc = std::abs(currentLocation->cameraInclination - dragStartInclination);
if (deltaAz >= TUTORIAL_ROTATION_THRESHOLD && deltaInc >= TUTORIAL_ROTATION_THRESHOLD) {
menuManager.advanceTutorialStep();
}
}
}
}
+5
View File
@@ -87,6 +87,11 @@ namespace ZL {
float pinchStartDistance = 0.0f;
float pinchStartZoom = 0.0f;
// Tutorial: azimuth and inclination captured at the start of each camera drag,
// used to measure how far the user has rotated before advancing the tutorial step.
float dragStartAzimuth = 0.0f;
float dragStartInclination = 0.0f;
std::unique_ptr<AudioPlayerAsync> audioPlayer;
int64_t getSyncTimeMs();
+1
View File
@@ -1519,6 +1519,7 @@ namespace ZL
targetInteractNpc = nullptr;
targetInteractNpcIndex = -1;
targetTeleportZone = nullptr;
if (onPlayerFloorWalk) onPlayerFloorWalk();
}
}
else {
+6 -3
View File
@@ -51,12 +51,11 @@ namespace ZL
std::vector<InteractiveObject> interactiveObjects;
std::unique_ptr<Character> player;
std::vector<std::unique_ptr<Character>> npcs;
float cameraAzimuth = 0.0f;
float cameraInclination = M_PI * 30.f / 180.f;
float cameraAzimuth = -2.35;
float cameraInclination = 1.1036;//M_PI * 30.f / 180.f;
std::vector<PathFinder> navigationMaps;
PathFinder* navigation = nullptr;
@@ -87,6 +86,10 @@ namespace ZL
// Read by draw functions and raycast — do not write from Location code.
bool isDarklands = false;
// Called when the player successfully taps the ground and a floor walk target is set.
// Used by the tutorial system to detect the "tap to walk" gesture.
std::function<void()> onPlayerFloorWalk;
// Set by Game after setup(). Lua and onDeathAnimComplete call this to
// request the transition without coupling Location to Game directly.
std::function<bool()> requestDarklandsTransition;
+123 -1
View File
@@ -34,7 +34,16 @@ namespace ZL {
inventory = &inv;
//hudRoot = loadUiFromFile("resources/config2/hud.json", renderer, zipFile);
hudRoot = loadUiFromFile("resources/w/ui/hud_step0.json", renderer, zipFile);
hudRoot = loadUiFromFile("resources/w/ui/hud_step0.json", renderer, zipFile);
hudStep1Root = loadUiFromFile("resources/w/ui/hud_step1.json", renderer, zipFile);
hudStep2Root = loadUiFromFile("resources/w/ui/hud_step2.json", renderer, zipFile);
hudStep3Root = loadUiFromFile("resources/w/ui/hud_step3.json", renderer, zipFile);
hudStep4Root = loadUiFromFile("resources/w/ui/hud_step4.json", renderer, zipFile);
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);
phoneScreenRoot = loadUiFromFile("resources/w/ui/screen_phone.json", renderer, zipFile);
journalScreenRoot= loadUiFromFile("resources/w/ui/screen_journal.json", renderer, zipFile);
inventoryRoot = loadUiFromFile("resources/config2/ui_inventory.json", renderer, zipFile);
questJournalRoot = loadUiFromFile("resources/config2/ui_quest_journal.json", renderer, zipFile);
@@ -124,6 +133,119 @@ namespace ZL {
}
}
void MenuManager::openPhoneScreen() {
state = GameState::PhoneScreen;
tutorialPhoneScreenOpened = true;
// Hide the phone hint on the current HUD so it stays hidden when we return.
uiManager.setNodeVisible("hint6a", false);
uiManager.pushMenuFromSavedRoot(phoneScreenRoot);
uiManager.setButtonCallback("phoneExitButton", [this](const std::string&) {
closePhoneScreen();
});
}
void MenuManager::closePhoneScreen() {
state = GameState::Gameplay;
uiManager.popMenu();
}
void MenuManager::openJournalScreen() {
state = GameState::JournalScreen;
tutorialJournalScreenOpened = true;
// Hide the journal hint on the current HUD so it stays hidden when we return.
uiManager.setNodeVisible("hint6b", false);
uiManager.pushMenuFromSavedRoot(journalScreenRoot);
uiManager.setButtonCallback("journalExitButton", [this](const std::string&) {
closeJournalScreen();
});
}
void MenuManager::closeJournalScreen() {
state = GameState::Gameplay;
uiManager.popMenu();
}
// Registers phoneButton / journalButton callbacks on the current HUD root
// and hides any hints that have already been completed.
// Called after every replaceRoot during step 5.
void MenuManager::setupStep5Callbacks() {
if (uiManager.findButton("phoneButton")) {
uiManager.setButtonCallback("phoneButton", [this](const std::string&) {
openPhoneScreen();
});
}
if (uiManager.findButton("journalButton")) {
uiManager.setButtonCallback("journalButton", [this](const std::string&) {
openJournalScreen();
});
}
if (tutorialPhoneScreenOpened) uiManager.setNodeVisible("hint6a", false);
if (tutorialJournalScreenOpened) uiManager.setNodeVisible("hint6b", false);
}
void MenuManager::advanceTutorialStep() {
std::shared_ptr<UiNode> nextRoot;
switch (tutorialStep) {
case TutorialStep::Step0:
tutorialStep = TutorialStep::Step1;
nextRoot = hudStep1Root;
break;
case TutorialStep::Step1:
tutorialStep = TutorialStep::Step2;
nextRoot = hudStep2Root;
break;
case TutorialStep::Step2:
tutorialStep = TutorialStep::Step3;
nextRoot = hudStep3Root;
break;
case TutorialStep::Step3:
tutorialStep = TutorialStep::Step4;
nextRoot = hudStep4Root;
break;
default:
return; // Step4/Step5 transitions are driven by onItemPickedUp
}
if (state == GameState::Gameplay && nextRoot) {
uiManager.replaceRoot(nextRoot);
}
}
void MenuManager::onItemPickedUp(const std::string& itemId) {
if (tutorialStep != TutorialStep::Step4 && tutorialStep != TutorialStep::Step5) {
return;
}
if (itemId == "phone") tutorialPhonePickedUp = true;
if (itemId == "journal") tutorialJournalPickedUp = true;
if (tutorialStep == TutorialStep::Step4) {
tutorialStep = TutorialStep::Step5;
}
refreshItemPickupHud();
}
void MenuManager::refreshItemPickupHud() {
if (state != GameState::Gameplay) return;
std::shared_ptr<UiNode> nextRoot;
if (tutorialPhonePickedUp && tutorialJournalPickedUp) {
nextRoot = hudStep5abRoot;
} else if (tutorialPhonePickedUp) {
nextRoot = hudStep5aRoot;
} else if (tutorialJournalPickedUp) {
nextRoot = hudStep5bRoot;
}
if (nextRoot) {
uiManager.replaceRoot(nextRoot);
// Register item-screen buttons and re-apply any already-completed hint visibility.
setupStep5Callbacks();
}
}
void MenuManager::refreshQuestJournalUi() {
visibleQuestIds.clear();
auto quests = questJournal.getVisibleQuests();
+38 -1
View File
@@ -16,7 +16,18 @@ namespace ZL {
enum class GameState {
Gameplay,
Inventory,
QuestJournal
QuestJournal,
PhoneScreen,
JournalScreen
};
enum class TutorialStep {
Step0, // Dialogue hint: "click to advance"
Step1, // Camera rotation hint
Step2, // Floor tap / walk hint
Step3, // Pinch-zoom hint
Step4, // Pick-up item hint
Step5, // Post-pickup reaction (sub-state: which items were collected)
};
class MenuManager {
@@ -38,6 +49,16 @@ namespace ZL {
bool isInventoryOpen() const { return state == GameState::Inventory; }
bool isQuestJournalOpen() const { return state == GameState::QuestJournal; }
void openPhoneScreen();
void closePhoneScreen();
void openJournalScreen();
void closeJournalScreen();
void advanceTutorialStep();
void onItemPickedUp(const std::string& itemId);
TutorialStep tutorialStep = TutorialStep::Step0;
protected:
Renderer& renderer;
@@ -45,11 +66,27 @@ namespace ZL {
void enterGameplay();
void refreshQuestJournalUi();
void selectQuestByIndex(int index);
void refreshItemPickupHud();
void setupStep5Callbacks();
GameState state = GameState::Gameplay;
Inventory* inventory = nullptr;
bool tutorialPhonePickedUp = false;
bool tutorialJournalPickedUp = false;
bool tutorialPhoneScreenOpened = false;
bool tutorialJournalScreenOpened = false;
std::shared_ptr<UiNode> hudRoot;
std::shared_ptr<UiNode> hudStep1Root;
std::shared_ptr<UiNode> hudStep2Root;
std::shared_ptr<UiNode> hudStep3Root;
std::shared_ptr<UiNode> hudStep4Root;
std::shared_ptr<UiNode> hudStep5aRoot;
std::shared_ptr<UiNode> hudStep5bRoot;
std::shared_ptr<UiNode> hudStep5abRoot;
std::shared_ptr<UiNode> phoneScreenRoot;
std::shared_ptr<UiNode> journalScreenRoot;
std::shared_ptr<UiNode> inventoryRoot;
std::shared_ptr<UiNode> questJournalRoot;
+68 -6
View File
@@ -373,9 +373,17 @@ namespace ZL {
void UiStaticImage::draw(Renderer& renderer) const {
if (!texture) return;
const float alpha = (fadeInEnabled && fadeInDurationMs > 0.0f)
? std::clamp(fadeInElapsedMs / fadeInDurationMs, 0.0f, 1.0f)
: 1.0f;
renderer.RenderUniform1i(textureUniformName, 0);
renderer.RenderUniform1f("uAlpha", alpha);
glBindTexture(GL_TEXTURE_2D, texture->getTexID());
renderer.DrawVertexRenderStruct(mesh);
if (alpha < 1.0f) {
renderer.RenderUniform1f("uAlpha", 1.0f); // restore for subsequent draws
}
}
void UiTextField::draw(Renderer& renderer) const {
@@ -504,6 +512,8 @@ namespace ZL {
btn->texDisabled = loadTex("disabled");
btn->border = j.value("border", 0.0f);
if (j.contains("clickZoneWidth")) btn->clickZoneWidth = j["clickZoneWidth"].get<float>();
if (j.contains("clickZoneHeight")) btn->clickZoneHeight = j["clickZoneHeight"].get<float>();
node->button = btn;
}
@@ -616,6 +626,8 @@ namespace ZL {
if (j.contains("color") && j["color"].is_array() && j["color"].size() == 4) {
for (int i = 0; i < 4; ++i) tb->color[i] = j["color"][i].get<float>();
}
if (j.contains("clickZoneWidth")) tb->clickZoneWidth = j["clickZoneWidth"].get<float>();
if (j.contains("clickZoneHeight")) tb->clickZoneHeight = j["clickZoneHeight"].get<float>();
tb->textRenderer = std::make_unique<TextRenderer>();
if (!tb->textRenderer->init(renderer, tb->fontPath, tb->fontSize, zipFile)) {
@@ -679,6 +691,23 @@ namespace ZL {
}
node->staticImage = img;
// Optional fade-in on display
if (j.contains("fadeIn") && j["fadeIn"].is_object()) {
const auto& fi = j["fadeIn"];
img->fadeInEnabled = true;
img->fadeInDurationMs = fi.value("durationMs", 1000.0f);
img->fadeInElapsedMs = 0.0f;
}
// Optional pulse-scale animation
if (j.contains("pulse") && j["pulse"].is_object()) {
const auto& p = j["pulse"];
node->pulseEnabled = true;
node->pulseMinScale = p.value("minScale", 0.9f);
node->pulseMaxScale = p.value("maxScale", 1.1f);
node->pulsePeriodMs = p.value("periodMs", 1000.0f);
}
}
if (typeStr == "TextView") {
@@ -777,6 +806,7 @@ namespace ZL {
textViews.clear();
textFields.clear();
staticImages.clear();
pulsingNodes.clear();
collectButtonsAndSliders(root);
nodeActiveAnims.clear();
@@ -1020,6 +1050,12 @@ namespace ZL {
}
if (node->staticImage) {
staticImages.push_back(node->staticImage);
if (node->staticImage->fadeInEnabled) {
node->staticImage->fadeInElapsedMs = 0.0f; // restart fade every time UI is shown
}
}
if (node->pulseEnabled && node->staticImage) {
pulsingNodes.push_back(node);
}
for (auto& c : node->children) collectButtonsAndSliders(c);
}
@@ -1128,6 +1164,7 @@ namespace ZL {
prev.textViews = textViews;
prev.textFields = textFields;
prev.staticImages = staticImages;
prev.pulsingNodes = pulsingNodes;
prev.pressedButtons = pressedButtons;
prev.pressedTextButtons = pressedTextButtons;
prev.pressedSliders = pressedSliders;
@@ -1190,6 +1227,7 @@ namespace ZL {
textViews = s.textViews;
textFields = s.textFields;
staticImages = s.staticImages;
pulsingNodes = s.pulsingNodes;
pressedButtons = s.pressedButtons;
pressedTextButtons = s.pressedTextButtons;
pressedSliders = s.pressedSliders;
@@ -1233,6 +1271,7 @@ namespace ZL {
renderer.PushProjectionMatrix(Environment::projectionWidth, Environment::projectionHeight, -1, 1);
renderer.PushMatrix();
renderer.LoadIdentity();
renderer.RenderUniform1f("uAlpha", 1.0f); // ensure alpha is fully opaque unless a widget overrides it
std::function<void(const std::shared_ptr<UiNode>&)> drawNode =
[&](const std::shared_ptr<UiNode>& node) {
@@ -1301,6 +1340,29 @@ namespace ZL {
void UiManager::update(float deltaMs) {
if (!root) return;
// Fade-in animations (StaticImage elements with fadeInEnabled == true)
for (auto& img : staticImages) {
if (!img || !img->fadeInEnabled) continue;
if (img->fadeInElapsedMs < img->fadeInDurationMs) {
img->fadeInElapsedMs += deltaMs;
if (img->fadeInElapsedMs > img->fadeInDurationMs)
img->fadeInElapsedMs = img->fadeInDurationMs;
}
}
// Pulse-scale animations (StaticImage nodes with pulseEnabled == true)
for (auto& node : pulsingNodes) {
if (!node) continue;
node->pulseElapsedMs += deltaMs;
if (node->pulseElapsedMs >= node->pulsePeriodMs)
node->pulseElapsedMs = std::fmod(node->pulseElapsedMs, node->pulsePeriodMs);
const float phase = node->pulseElapsedMs / node->pulsePeriodMs;
const float t = (std::sin(phase * 2.0f * static_cast<float>(M_PI)) + 1.0f) * 0.5f;
const float s = node->pulseMinScale + (node->pulseMaxScale - node->pulseMinScale) * t;
node->scaleX = s;
node->scaleY = s;
}
std::vector<std::pair<std::shared_ptr<UiNode>, size_t>> animationsToRemove;
std::vector<std::function<void()>> pendingCallbacks;
@@ -1490,7 +1552,7 @@ namespace ZL {
for (auto& b : buttons) {
if (b->state != ButtonState::Disabled)
{
if (b->rect.containsConsideringBorder((float)x, (float)y, b->border)) {
if (b->getClickZoneRect().containsConsideringBorder((float)x, (float)y, b->border)) {
if (b->state != ButtonState::Pressed) b->state = ButtonState::Hover;
}
else {
@@ -1501,7 +1563,7 @@ namespace ZL {
for (auto& tb : textButtons) {
if (tb->state != ButtonState::Disabled)
{
if (tb->rect.containsConsideringBorder((float)x, (float)y, tb->border)) {
if (tb->getClickZoneRect().containsConsideringBorder((float)x, (float)y, tb->border)) {
if (tb->state != ButtonState::Pressed) tb->state = ButtonState::Hover;
}
else {
@@ -1534,7 +1596,7 @@ namespace ZL {
for (auto& b : buttons) {
if (b->state != ButtonState::Disabled)
{
if (b->rect.containsConsideringBorder((float)x, (float)y, b->border)) {
if (b->getClickZoneRect().containsConsideringBorder((float)x, (float)y, b->border)) {
b->state = ButtonState::Pressed;
pressedButtons[fingerId] = b;
if (b->onPress) b->onPress(b->name);
@@ -1546,7 +1608,7 @@ namespace ZL {
for (auto& tb : textButtons) {
if (tb->state != ButtonState::Disabled)
{
if (tb->rect.containsConsideringBorder((float)x, (float)y, tb->border)) {
if (tb->getClickZoneRect().containsConsideringBorder((float)x, (float)y, tb->border)) {
tb->state = ButtonState::Pressed;
pressedTextButtons[fingerId] = tb;
if (tb->onPress) tb->onPress(tb->name);
@@ -1593,7 +1655,7 @@ namespace ZL {
if (btnIt != pressedButtons.end()) {
auto b = btnIt->second;
if (b) {
bool contains = b->rect.contains((float)x, (float)y);
bool contains = b->getClickZoneRect().contains((float)x, (float)y);
if (b->state == ButtonState::Pressed) {
if (contains) {
clicked.push_back(b);
@@ -1609,7 +1671,7 @@ namespace ZL {
if (tbIt != pressedTextButtons.end()) {
auto tb = tbIt->second;
if (tb) {
bool contains = tb->rect.contains((float)x, (float)y);
bool contains = tb->getClickZoneRect().contains((float)x, (float)y);
if (tb->state == ButtonState::Pressed) {
if (contains) {
clickedText.push_back(tb);
+43
View File
@@ -93,6 +93,17 @@ namespace ZL {
VertexRenderStruct mesh;
// Optional explicit hit-test zone, centred on rect.
// 0 means "use rect.w / rect.h" (default behaviour).
float clickZoneWidth = 0.0f;
float clickZoneHeight = 0.0f;
UiRect getClickZoneRect() const {
float cw = (clickZoneWidth > 0.0f) ? clickZoneWidth : rect.w;
float ch = (clickZoneHeight > 0.0f) ? clickZoneHeight : rect.h;
return { rect.x + (rect.w - cw) * 0.5f, rect.y + (rect.h - ch) * 0.5f, cw, ch };
}
std::function<void(const std::string&)> onClick;
std::function<void(const std::string&)> onPress; // fires on touch/mouse down
@@ -139,6 +150,17 @@ namespace ZL {
ButtonState state = ButtonState::Normal;
VertexRenderStruct mesh;
// Optional explicit hit-test zone, centred on rect.
// 0 means "use rect.w / rect.h" (default behaviour).
float clickZoneWidth = 0.0f;
float clickZoneHeight = 0.0f;
UiRect getClickZoneRect() const {
float cw = (clickZoneWidth > 0.0f) ? clickZoneWidth : rect.w;
float ch = (clickZoneHeight > 0.0f) ? clickZoneHeight : rect.h;
return { rect.x + (rect.w - cw) * 0.5f, rect.y + (rect.h - ch) * 0.5f, cw, ch };
}
// Text drawn on top of the button
std::string text;
std::string fontPath = "resources/fonts/DroidSans.ttf";
@@ -209,6 +231,11 @@ namespace ZL {
VertexRenderStruct mesh;
// Fade-in on first display (triggers each time the containing UI is shown)
bool fadeInEnabled = false;
float fadeInDurationMs = 1000.0f;
float fadeInElapsedMs = 0.0f; // runtime, reset by collectButtonsAndSliders
void buildMesh();
void draw(Renderer& renderer) const;
};
@@ -236,6 +263,13 @@ namespace ZL {
bool visible = true;
// Pulse-scale animation (StaticImage only; auto-starts when pulseEnabled == true)
bool pulseEnabled = false;
float pulseMinScale = 1.0f;
float pulseMaxScale = 1.1f;
float pulsePeriodMs = 1000.0f;
float pulseElapsedMs = 0.0f; // runtime, not persisted in JSON
// Иерархия
std::vector<std::shared_ptr<UiNode>> children;
@@ -322,6 +356,13 @@ namespace ZL {
tb->animScaleY = 1.0f;
}
}
for (auto& n : pulsingNodes) {
if (n) {
n->scaleX = 1.0f;
n->scaleY = 1.0f;
n->pulseElapsedMs = 0.0f;
}
}
}
std::shared_ptr<UiButton> findButton(const std::string& name);
@@ -404,6 +445,7 @@ namespace ZL {
std::vector<std::shared_ptr<UiTextView>> textViews;
std::vector<std::shared_ptr<UiTextField>> textFields;
std::vector<std::shared_ptr<UiStaticImage>> staticImages;
std::vector<std::shared_ptr<UiNode>> pulsingNodes;
std::map<std::shared_ptr<UiNode>, std::vector<ActiveAnim>> nodeActiveAnims;
std::map<std::pair<std::string, std::string>, std::function<void()>> animCallbacks; // key: (nodeName, animName)
@@ -422,6 +464,7 @@ namespace ZL {
std::vector<std::shared_ptr<UiTextView>> textViews;
std::vector<std::shared_ptr<UiTextField>> textFields;
std::vector<std::shared_ptr<UiStaticImage>> staticImages;
std::vector<std::shared_ptr<UiNode>> pulsingNodes;
std::map<int64_t, std::shared_ptr<UiButton>> pressedButtons;
std::map<int64_t, std::shared_ptr<UiTextButton>> pressedTextButtons;
std::map<int64_t, std::shared_ptr<UiSlider>> pressedSliders;
+5
View File
@@ -104,6 +104,7 @@ bool DialogueSystem::handlePointerReleased(float x, float y) {
if (advanceDialogue) {
runtime.confirmAdvance();
if (onDialogueAdvancedCallback) onDialogueAdvancedCallback();
return true;
}
@@ -143,4 +144,8 @@ void DialogueSystem::stopDialogue() {
runtime.stop();
}
void DialogueSystem::setOnDialogueAdvanced(std::function<void()> cb) {
onDialogueAdvancedCallback = std::move(cb);
}
} // namespace ZL::Dialogue
+2
View File
@@ -27,6 +27,7 @@ public:
void setOnDialogueLineStarted(std::function<void(const std::string&)> cb);
void setOnCutsceneLineStarted(std::function<void(const std::string&)> cb);
void setOnCutsceneFadeInComplete(std::function<void(const std::string&)> cb);
void setOnDialogueAdvanced(std::function<void()> cb);
void stopDialogue();
bool isActive() const { return runtime.isActive(); }
@@ -39,6 +40,7 @@ private:
DialogueDatabase database;
DialogueRuntime runtime;
DialogueOverlay overlay;
std::function<void()> onDialogueAdvancedCallback;
};
} // namespace ZL::Dialogue
+1
View File
@@ -7,6 +7,7 @@ namespace ZL {
void Inventory::addItem(const Item& item) {
items.push_back(item);
std::cout << "Item added to inventory: " << item.name << std::endl;
if (onItemAdded) onItemAdded(item.id);
}
void Inventory::removeItem(const std::string& itemId) {
+4
View File
@@ -2,6 +2,7 @@
#include <string>
#include <memory>
#include <vector>
#include <functional>
namespace ZL {
@@ -28,6 +29,9 @@ namespace ZL {
bool hasItem(const std::string& itemId) const;
void clear() { items.clear(); }
size_t getCount() const { return items.size(); }
// Called whenever an item is added; receives the item id.
std::function<void(const std::string&)> onItemAdded;
};
} // namespace ZL