diff --git a/src/Game.cpp b/src/Game.cpp index 19c260f..c1aa867 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -696,6 +696,13 @@ namespace ZL int my = static_cast((float)event.motion.y / Environment::height * Environment::projectionHeight); handleMotion(ZL::UiManager::MOUSE_FINGER_ID, mx, my); + if (dialogueSystem.blocksGameplayInput()) { + dialogueSystem.handlePointerMoved( + static_cast(mx), + Environment::projectionHeight - static_cast(my) + ); + } + if (rightMouseDown) { int dx = event.motion.x - lastMouseX; int dy = event.motion.y - lastMouseY; @@ -732,13 +739,13 @@ namespace ZL if (event.type == SDL_KEYDOWN && event.key.repeat == 0) { switch (event.key.keysym.sym) { case SDLK_f: - dialogueSystem.startDialogue("test_cutscene_pan_dialogue_silent"); + dialogueSystem.startDialogue("test_choice_dialogue"); break; case SDLK_e: dialogueSystem.startDialogue("test_cutscene_pan_dialogue"); break; - + case SDLK_RETURN: default: diff --git a/src/dialogue/DialogueOverlay.cpp b/src/dialogue/DialogueOverlay.cpp index 5937e49..a74a1c8 100644 --- a/src/dialogue/DialogueOverlay.cpp +++ b/src/dialogue/DialogueOverlay.cpp @@ -84,6 +84,12 @@ bool DialogueOverlay::init(Renderer& renderer, const std::string& zipFile) { return ok; } +void DialogueOverlay::update(const PresentationModel& model, int deltaMs) { + if (model.mode != PresentationMode::Choice) { + hoveredChoiceIndex = -1; + } +} + void DialogueOverlay::draw(Renderer& renderer, const PresentationModel& model) { if (model.mode == PresentationMode::Hidden) { lastChoiceRects.clear(); @@ -170,12 +176,9 @@ void DialogueOverlay::drawDialogue(Renderer& renderer, const PresentationModel& lastChoiceRects.push_back(rect); choiceQuads[i].rebuild(rect); - const bool isSelected = static_cast(i) == model.selectedChoice; - std::shared_ptr choiceTexture = choiceMainTexture; - if (model.choices[i].kind == ChoiceKind::Optional) { - choiceTexture = choiceOptionalTexture; - } - if (isSelected) { + const bool isHighlighted = static_cast(i) == hoveredChoiceIndex || static_cast(i) == model.selectedChoice; + std::shared_ptr choiceTexture = (model.choices[i].kind == ChoiceKind::Optional) ? choiceOptionalTexture : choiceMainTexture; + if (isHighlighted) { choiceTexture = choiceSelectedTexture; } drawQuad(renderer, choiceQuads[i], choiceTexture); @@ -187,10 +190,10 @@ void DialogueOverlay::drawDialogue(Renderer& renderer, const PresentationModel& for (size_t i = 0; i < model.choices.size(); ++i) { const UiRect& rect = lastChoiceRects[i]; - const bool isSelected = static_cast(i) == model.selectedChoice; + const bool isHighlighted = static_cast(i) == hoveredChoiceIndex || static_cast(i) == model.selectedChoice; const std::array color = (model.choices[i].kind == ChoiceKind::Optional) ? std::array{0.82f, 0.82f, 0.82f, 1.0f} - : std::array{ 1.0f, 0.93f, 0.65f, 1.0f }; + : std::array{ 1.0f, 0.93f, 0.65f, 1.0f }; choiceRenderer->drawText( wrapText(model.choices[i].text, 52), @@ -198,7 +201,7 @@ void DialogueOverlay::drawDialogue(Renderer& renderer, const PresentationModel& rect.y + 9.0f, 1.0f, false, - isSelected ? std::array{1.0f, 1.0f, 1.0f, 1.0f} : color + isHighlighted ? std::array{1.0f, 1.0f, 1.0f, 1.0f} : color ); } } @@ -417,21 +420,48 @@ void DialogueOverlay::drawCutscene(Renderer& renderer, const PresentationModel& glDisable(GL_BLEND); } -bool DialogueOverlay::handlePointerReleased(float x, float y, const PresentationModel& model, int& outChoiceIndex) const { +void DialogueOverlay::handlePointerDown(float x, float y, const PresentationModel& model) { + if (model.mode == PresentationMode::Choice) { + handlePointerMoved(x, y, model); + return; + } +} + +void DialogueOverlay::handlePointerMoved(float x, float y, const PresentationModel& model) { + if (model.mode == PresentationMode::Choice) { + hoveredChoiceIndex = -1; + for (size_t i = 0; i < lastChoiceRects.size(); ++i) { + if (rectContains(lastChoiceRects[i], x, y)) { + hoveredChoiceIndex = static_cast(i); + break; + } + } + return; + } + + hoveredChoiceIndex = -1; +} + +bool DialogueOverlay::handlePointerReleased(float x, float y, const PresentationModel& model, int& outChoiceIndex, bool& outAdvanceDialogue) { outChoiceIndex = -1; + outAdvanceDialogue = false; if (model.mode == PresentationMode::Choice) { for (size_t i = 0; i < lastChoiceRects.size(); ++i) { - if (lastChoiceRects[i].contains(x, y)) { + if (rectContains(lastChoiceRects[i], x, y)) { outChoiceIndex = static_cast(i); return true; } } - return lastDialogueAdvanceRect.contains(x, y); + return false; } if (model.mode == PresentationMode::Dialogue) { - return lastDialogueAdvanceRect.contains(x, y); + if (lastDialogueAdvanceRect.contains(x, y)) { + outAdvanceDialogue = true; + return true; + } + return false; } if (model.mode == PresentationMode::Cutscene) { @@ -494,4 +524,8 @@ std::string DialogueOverlay::wrapText(const std::string& input, size_t maxLineLe return output; } +bool DialogueOverlay::rectContains(const UiRect& rect, float x, float y) { + return x >= rect.x && x <= rect.x + rect.w && y >= rect.y && y <= rect.y + rect.h; +} + } // namespace ZL::Dialogue \ No newline at end of file diff --git a/src/dialogue/DialogueOverlay.h b/src/dialogue/DialogueOverlay.h index e761937..66fc822 100644 --- a/src/dialogue/DialogueOverlay.h +++ b/src/dialogue/DialogueOverlay.h @@ -15,11 +15,12 @@ namespace ZL::Dialogue { class DialogueOverlay { public: bool init(Renderer& renderer, const std::string& zipFile = ""); + void update(const PresentationModel& model, int deltaMs); void draw(Renderer& renderer, const PresentationModel& model); - // Coordinates are expected in the game's UI projection space - // Returns true only when the click should advance/select. - bool handlePointerReleased(float x, float y, const PresentationModel& model, int& outChoiceIndex) const; + void handlePointerDown(float x, float y, const PresentationModel& model); + void handlePointerMoved(float x, float y, const PresentationModel& model); + bool handlePointerReleased(float x, float y, const PresentationModel& model, int& outChoiceIndex, bool& outAdvanceDialogue); private: struct TexturedQuad { @@ -60,6 +61,8 @@ private: mutable UiRect lastCutsceneAdvanceRect{}; mutable bool cutsceneAdvanceEnabled = false; + int hoveredChoiceIndex = -1; + std::unique_ptr nameRenderer; std::unique_ptr bodyRenderer; std::unique_ptr choiceRenderer; @@ -80,6 +83,7 @@ private: void drawQuad(Renderer& renderer, const TexturedQuad& quad, const std::shared_ptr& texture) const; static std::string wrapText(const std::string& input, size_t maxLineLength); + static bool rectContains(const UiRect& rect, float x, float y); static float lerpFloat(float a, float b, float t); static ResolvedViewport resolveViewportPose( diff --git a/src/dialogue/DialogueRuntime.cpp b/src/dialogue/DialogueRuntime.cpp index c68deed..3bbc84b 100644 --- a/src/dialogue/DialogueRuntime.cpp +++ b/src/dialogue/DialogueRuntime.cpp @@ -185,10 +185,26 @@ void DialogueRuntime::moveSelection(int delta) { } const int count = static_cast(visibleChoices.size()); - selectedChoice = (selectedChoice + delta) % count; - if (selectedChoice < 0) { - selectedChoice += count; + if (selectedChoice < 0 || selectedChoice >= count) { + selectedChoice = (delta >= 0) ? 0 : (count - 1); } + else { + selectedChoice = (selectedChoice + delta) % count; + if (selectedChoice < 0) { + selectedChoice += count; + } + } + presentation.selectedChoice = selectedChoice; +} + +void DialogueRuntime::selectChoice(int index) { + if (mode != Mode::WaitingForChoice || visibleChoices.empty()) { + return; + } + if (index < 0 || index >= static_cast(visibleChoices.size())) { + return; + } + selectedChoice = index; presentation.selectedChoice = selectedChoice; } @@ -346,14 +362,14 @@ void DialogueRuntime::presentChoices(const Node& node) { } mode = Mode::WaitingForChoice; - selectedChoice = 0; + selectedChoice = -1; presentation.mode = PresentationMode::Choice; presentation.speaker = node.speaker; presentation.fullText = node.text; presentation.visibleText = node.text; presentation.portraitPath = node.portrait; presentation.backgroundPath.clear(); - presentation.selectedChoice = 0; + presentation.selectedChoice = -1; presentation.revealCompleted = true; presentation.showCutsceneSubtitle = false; presentation.cutsceneCamera = {}; diff --git a/src/dialogue/DialogueRuntime.h b/src/dialogue/DialogueRuntime.h index 78df448..6f266bc 100644 --- a/src/dialogue/DialogueRuntime.h +++ b/src/dialogue/DialogueRuntime.h @@ -26,6 +26,7 @@ public: void confirmAdvance(); void moveSelection(int delta); + void selectChoice(int index); const PresentationModel& getPresentation() const { return presentation; } diff --git a/src/dialogue/DialogueSystem.cpp b/src/dialogue/DialogueSystem.cpp index 7491259..38269b8 100644 --- a/src/dialogue/DialogueSystem.cpp +++ b/src/dialogue/DialogueSystem.cpp @@ -66,24 +66,43 @@ bool DialogueSystem::handleKeyDown(SDL_Keycode key) { } } +void DialogueSystem::handlePointerDown(float x, float y) { + if (!runtime.isActive()) { + return; + } + overlay.handlePointerDown(x, y, runtime.getPresentation()); +} + +void DialogueSystem::handlePointerMoved(float x, float y) { + if (!runtime.isActive()) { + return; + } + overlay.handlePointerMoved(x, y, runtime.getPresentation()); +} + bool DialogueSystem::handlePointerReleased(float x, float y) { if (!runtime.isActive()) { return false; } int choiceIndex = -1; + bool advanceDialogue = false; const PresentationModel& model = runtime.getPresentation(); - if (!overlay.handlePointerReleased(x, y, model, choiceIndex)) { + if (!overlay.handlePointerReleased(x, y, model, choiceIndex, advanceDialogue)) { return false; } if (choiceIndex >= 0) { - while (runtime.getPresentation().selectedChoice != choiceIndex) { - runtime.moveSelection(1); - } + runtime.selectChoice(choiceIndex); + runtime.confirmAdvance(); + return true; + } + + if (advanceDialogue) { + runtime.confirmAdvance(); + return true; } - runtime.confirmAdvance(); return true; } diff --git a/src/dialogue/DialogueSystem.h b/src/dialogue/DialogueSystem.h index 3cd464b..1756272 100644 --- a/src/dialogue/DialogueSystem.h +++ b/src/dialogue/DialogueSystem.h @@ -27,6 +27,8 @@ public: void draw(Renderer& renderer); bool handleKeyDown(SDL_Keycode key); + void handlePointerDown(float x, float y); + void handlePointerMoved(float x, float y); bool handlePointerReleased(float x, float y); bool startDialogue(const std::string& dialogueId); diff --git a/src/dialogue/DialogueTypes.h b/src/dialogue/DialogueTypes.h index 89df3c1..65c5e32 100644 --- a/src/dialogue/DialogueTypes.h +++ b/src/dialogue/DialogueTypes.h @@ -173,7 +173,7 @@ struct PresentationModel { std::string portraitPath; std::string backgroundPath; std::vector choices; - int selectedChoice = 0; + int selectedChoice = -1; bool revealCompleted = true; bool showCutsceneSubtitle = false;