fix: make choice options the only clickable area in dialogue choices
This commit is contained in:
parent
2494a44e4a
commit
be4dcd4cf9
11
src/Game.cpp
11
src/Game.cpp
@ -696,6 +696,13 @@ namespace ZL
|
||||
int my = static_cast<int>((float)event.motion.y / Environment::height * Environment::projectionHeight);
|
||||
handleMotion(ZL::UiManager::MOUSE_FINGER_ID, mx, my);
|
||||
|
||||
if (dialogueSystem.blocksGameplayInput()) {
|
||||
dialogueSystem.handlePointerMoved(
|
||||
static_cast<float>(mx),
|
||||
Environment::projectionHeight - static_cast<float>(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:
|
||||
|
||||
@ -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<int>(i) == model.selectedChoice;
|
||||
std::shared_ptr<Texture> choiceTexture = choiceMainTexture;
|
||||
if (model.choices[i].kind == ChoiceKind::Optional) {
|
||||
choiceTexture = choiceOptionalTexture;
|
||||
}
|
||||
if (isSelected) {
|
||||
const bool isHighlighted = static_cast<int>(i) == hoveredChoiceIndex || static_cast<int>(i) == model.selectedChoice;
|
||||
std::shared_ptr<Texture> 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<int>(i) == model.selectedChoice;
|
||||
const bool isHighlighted = static_cast<int>(i) == hoveredChoiceIndex || static_cast<int>(i) == model.selectedChoice;
|
||||
const std::array<float, 4> color = (model.choices[i].kind == ChoiceKind::Optional)
|
||||
? std::array<float, 4>{0.82f, 0.82f, 0.82f, 1.0f}
|
||||
: std::array<float, 4>{ 1.0f, 0.93f, 0.65f, 1.0f };
|
||||
: std::array<float, 4>{ 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<float, 4>{1.0f, 1.0f, 1.0f, 1.0f} : color
|
||||
isHighlighted ? std::array<float, 4>{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<int>(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<int>(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
|
||||
@ -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<TextRenderer> nameRenderer;
|
||||
std::unique_ptr<TextRenderer> bodyRenderer;
|
||||
std::unique_ptr<TextRenderer> choiceRenderer;
|
||||
@ -80,6 +83,7 @@ private:
|
||||
void drawQuad(Renderer& renderer, const TexturedQuad& quad, const std::shared_ptr<Texture>& 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(
|
||||
|
||||
@ -185,10 +185,26 @@ void DialogueRuntime::moveSelection(int delta) {
|
||||
}
|
||||
|
||||
const int count = static_cast<int>(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<int>(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 = {};
|
||||
|
||||
@ -26,6 +26,7 @@ public:
|
||||
|
||||
void confirmAdvance();
|
||||
void moveSelection(int delta);
|
||||
void selectChoice(int index);
|
||||
|
||||
const PresentationModel& getPresentation() const { return presentation; }
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -173,7 +173,7 @@ struct PresentationModel {
|
||||
std::string portraitPath;
|
||||
std::string backgroundPath;
|
||||
std::vector<PresentedChoice> choices;
|
||||
int selectedChoice = 0;
|
||||
int selectedChoice = -1;
|
||||
bool revealCompleted = true;
|
||||
bool showCutsceneSubtitle = false;
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user