From 11530dd7fc0af1f7dd0a15e53e873d16b6c3ce07 Mon Sep 17 00:00:00 2001 From: Sergey Lapin Date: Sun, 3 May 2026 01:25:25 +0300 Subject: [PATCH] Dialogue uses arrays --- .../components/DialogueComponent.hpp | 8 +-- .../lua-examples/dialogue_basic_show.lua | 11 ++-- .../lua-examples/dialogue_component_api.lua | 5 +- .../lua-examples/dialogue_event_handler.lua | 6 +- .../lua-examples/dialogue_event_subscribe.lua | 10 ++-- .../lua-examples/dialogue_sequence.lua | 24 ++++---- .../editScene/lua/LuaComponentApi.cpp | 6 +- .../editScene/systems/DialogueSystem.cpp | 60 ++++++++----------- 8 files changed, 61 insertions(+), 69 deletions(-) diff --git a/src/features/editScene/components/DialogueComponent.hpp b/src/features/editScene/components/DialogueComponent.hpp index 2216e00..1dd76bb 100644 --- a/src/features/editScene/components/DialogueComponent.hpp +++ b/src/features/editScene/components/DialogueComponent.hpp @@ -17,10 +17,10 @@ * Only active in game mode (GamePlayState::Playing). * * Event payload (EventParams) parameters: - * "text" (string) - Narration text to display - * "choices" (string) - Comma-separated list of choice labels - * "speaker" (string) - Optional speaker name - * "auto_progress" (int) - If 1, clicking anywhere progresses (no choices) + * "text" (string) - Narration text to display + * "choices" (string_array) - Array of choice label strings (Lua table) + * "speaker" (string) - Optional speaker name + * "auto_progress" (int) - If 1, clicking anywhere progresses (no choices) * * Component state transitions: * Idle -> Showing (on show() or event) diff --git a/src/features/editScene/lua-examples/dialogue_basic_show.lua b/src/features/editScene/lua-examples/dialogue_basic_show.lua index 1f1144e..50c5158 100644 --- a/src/features/editScene/lua-examples/dialogue_basic_show.lua +++ b/src/features/editScene/lua-examples/dialogue_basic_show.lua @@ -10,8 +10,7 @@ -- Event payload parameters: -- "text" (string) - Narration text to display -- "speaker" (string) - Optional speaker name (shown above text) --- "choices" (string) - Comma-separated choice labels (optional) --- "auto_progress" (int) - If 1, click anywhere progresses (no choices) +-- "choices" (table) - Array of choice label strings (optional) -- ============================================================================= -- --------------------------------------------------------------------------- @@ -55,13 +54,13 @@ print("Sent basic narration dialogue") -- --------------------------------------------------------------------------- -- 3. Show dialogue with player choices -- --------------------------------------------------------------------------- --- When "choices" is provided (comma-separated), the dialogue box shows +-- When "choices" is provided as a table, the dialogue box shows -- buttons instead of click-to-progress. The player must pick one. ecs.send_event("dialogue_show", { text = "Where would you like to go?", speaker = "Guide", - choices = "The Forest,The Village,The Mountains" + choices = { "The Forest", "The Village", "The Mountains" } }) print("Sent dialogue with choices") @@ -92,10 +91,10 @@ print("Sent multi-line dialogue") -- ============================================================================= -- To show dialogue from Lua: -- 1. Ensure an entity with DialogueComponent exists (create one if needed) --- 2. Call ecs.send_event("dialogue_show", { text = "...", speaker = "...", choices = "..." }) +-- 2. Call ecs.send_event("dialogue_show", { text = "...", speaker = "...", choices = { ... } }) -- 3. Required: text = "The narration text" -- 4. Optional: speaker = "Speaker Name" --- 5. Optional: choices = "Choice1,Choice2,Choice3" (comma-separated) +-- 5. Optional: choices = { "Choice1", "Choice2", "Choice3" } (table of strings) -- 6. EventParams uses flat key-value pairs (no nested stringValues/floatValues/etc.) -- 7. Type metadata is available via params._types table -- ============================================================================= diff --git a/src/features/editScene/lua-examples/dialogue_component_api.lua b/src/features/editScene/lua-examples/dialogue_component_api.lua index ec583aa..e191858 100644 --- a/src/features/editScene/lua-examples/dialogue_component_api.lua +++ b/src/features/editScene/lua-examples/dialogue_component_api.lua @@ -180,7 +180,6 @@ update_npc_dialogue(guard, "At ease, friend. The town is safe with you around.") function show_dialogue_with_dynamic_choices(npc_entity, base_text, choice_list) -- choice_list is a table of strings - local choices_str = table.concat(choice_list, ",") -- Update the component ecs.set_field(npc_entity, "Dialogue", "text", base_text) @@ -189,7 +188,7 @@ function show_dialogue_with_dynamic_choices(npc_entity, base_text, choice_list) ecs.send_event("dialogue_show", { text = base_text, speaker = ecs.get_field(npc_entity, "Dialogue", "speaker"), - choices = choices_str + choices = choice_list }) end @@ -210,7 +209,7 @@ show_dialogue_with_dynamic_choices(merchant, "What would you like to buy?", shop -- -- EventBus (ecs.send_event "dialogue_show"): -- - Triggers proper state transitions --- - Parses choices from comma-separated string +-- - Parses choices from table of strings -- - Best for showing dialogue to the player -- -- Best practice: Use the EventBus to SHOW dialogue, and the component API diff --git a/src/features/editScene/lua-examples/dialogue_event_handler.lua b/src/features/editScene/lua-examples/dialogue_event_handler.lua index ec84f57..d96c004 100644 --- a/src/features/editScene/lua-examples/dialogue_event_handler.lua +++ b/src/features/editScene/lua-examples/dialogue_event_handler.lua @@ -87,7 +87,7 @@ function on_player_near_npc(npc_name, distance) ecs.send_event("dialogue_show", { text = "Hello there! I have a quest for a brave adventurer.", speaker = npc_name, - choices = "I'll help!,What's the reward?,Not interested" + choices = { "I'll help!", "What's the reward?", "Not interested" } }) end end @@ -119,14 +119,14 @@ local choice_sub = ecs.subscribe_event("dialogue_choice", function(event, params ecs.send_event("dialogue_show", { text = "Excellent! The ancient artifact was stolen from the temple.\nBring it back and you'll be richly rewarded!", speaker = "QuestGiver", - choices = "Where is the temple?,I'm on it!,Tell me more" + choices = { "Where is the temple?", "I'm on it!", "Tell me more" } }) elseif choice_text == "What's the reward?" then ecs.send_event("dialogue_show", { text = "100 gold pieces and a magical amulet! What do you say?", speaker = "QuestGiver", - choices = "I'll help!,Sounds good,Maybe later" + choices = { "I'll help!", "Sounds good", "Maybe later" } }) elseif choice_text == "Not interested" then diff --git a/src/features/editScene/lua-examples/dialogue_event_subscribe.lua b/src/features/editScene/lua-examples/dialogue_event_subscribe.lua index 37080eb..8fee93d 100644 --- a/src/features/editScene/lua-examples/dialogue_event_subscribe.lua +++ b/src/features/editScene/lua-examples/dialogue_event_subscribe.lua @@ -74,12 +74,12 @@ print("Subscribed to dialogue_dismiss events (ID: " .. dismiss_sub .. ")") local show_sub = ecs.subscribe_event("dialogue_show", function(event, params) local text = params.text or "" local speaker = params.speaker or "Unknown" - local choices = params.choices or "" + local choices = params.choices or {} print("[Dialogue Log] " .. speaker .. ": \"" .. text .. "\"") - if choices ~= "" then - print("[Dialogue Log] Choices: " .. choices) + if #choices > 0 then + print("[Dialogue Log] Choices: " .. table.concat(choices, ", ")) end end) @@ -95,7 +95,7 @@ function show_branching_dialogue() ecs.send_event("dialogue_show", { text = "You see a dark cave entrance. What do you do?", speaker = "Narrator", - choices = "Enter the cave,Look around first,Leave" + choices = { "Enter the cave", "Look around first", "Leave" } }) -- Step 2: The choice will be handled by our subscriber above. @@ -114,7 +114,7 @@ function npc_greeting(npc_name, greeting_text) ecs.send_event("dialogue_show", { text = greeting_text, speaker = npc_name, - choices = "Who are you?,Tell me about this place,Goodbye" + choices = { "Who are you?", "Tell me about this place", "Goodbye" } }) -- The choice subscriber will handle the response. diff --git a/src/features/editScene/lua-examples/dialogue_sequence.lua b/src/features/editScene/lua-examples/dialogue_sequence.lua index 41d5a92..8720322 100644 --- a/src/features/editScene/lua-examples/dialogue_sequence.lua +++ b/src/features/editScene/lua-examples/dialogue_sequence.lua @@ -56,14 +56,14 @@ end) --- Show a line of dialogue and wait for the player to respond. --- @param text string The narration text --- @param speaker string|nil Optional speaker name ---- @param choices string|nil Comma-separated choices (nil = click to dismiss) +--- @param choices table|nil Array of choice label strings (nil = click to dismiss) --- @return number choice_index (0 if dismissed, 1+ for choices) function show_and_wait(text, speaker, choices) -- Send the dialogue event ecs.send_event("dialogue_show", { text = text, speaker = speaker or "", - choices = choices or "" + choices = choices or {} }) -- Wait for player response @@ -106,7 +106,7 @@ function simple_conversation() ecs.send_event("dialogue_show", { text = "I've been expecting you. The darkness grows stronger each day.", speaker = "Old Man", - choices = "Tell me more,How can I help?,I must go" + choices = { "Tell me more", "How can I help?", "I must go" } }) print(" (Player would now see choices and pick one)") @@ -212,23 +212,21 @@ function run_conversation(tree, start_node) break end - -- Build choices string from the node's choices table - local choices_str = "" + -- Build choices table from the node's choices + local choices = {} local choice_map = {} if node.choices then - local parts = {} for i, choice in ipairs(node.choices) do - table.insert(parts, choice.text) + table.insert(choices, choice.text) choice_map[i] = choice end - choices_str = table.concat(parts, ",") end -- Show the dialogue ecs.send_event("dialogue_show", { text = node.text, speaker = node.speaker or "", - choices = choices_str + choices = choices }) -- In a real game, you'd wait for the player's choice here. @@ -272,7 +270,7 @@ function talk_to_elder_marcus() ecs.send_event("dialogue_show", { text = "Ah, a new face! I am Elder Marcus, keeper of this village.\nIt's been so long since we had visitors.", speaker = "Elder Marcus", - choices = "Pleasure to meet you,I've heard stories about you,Hello" + choices = { "Pleasure to meet you", "I've heard stories about you", "Hello" } }) elseif npc_state.quest_active and npc_state.quest_completed then -- Quest completed @@ -281,21 +279,21 @@ function talk_to_elder_marcus() ecs.send_event("dialogue_show", { text = "You did it! The village is safe thanks to you.\nPlease, take this reward.", speaker = "Elder Marcus", - choices = "Thank you, elder,I was happy to help" + choices = { "Thank you, elder", "I was happy to help" } }) elseif npc_state.quest_active then -- Quest in progress ecs.send_event("dialogue_show", { text = "Have you dealt with those bandits yet?\nThe villagers are growing anxious.", speaker = "Elder Marcus", - choices = "I'm working on it,I need more information,Not yet" + choices = { "I'm working on it", "I need more information", "Not yet" } }) else -- Regular greeting ecs.send_event("dialogue_show", { text = "Welcome back, friend. The village is peaceful today.", speaker = "Elder Marcus", - choices = "Any news?,I need supplies,Goodbye" + choices = { "Any news?", "I need supplies", "Goodbye" } }) end end diff --git a/src/features/editScene/lua/LuaComponentApi.cpp b/src/features/editScene/lua/LuaComponentApi.cpp index 3cc0b54..3973867 100644 --- a/src/features/editScene/lua/LuaComponentApi.cpp +++ b/src/features/editScene/lua/LuaComponentApi.cpp @@ -1251,7 +1251,8 @@ static void registerAllComponents() lua_pushstring(L, c.text.c_str()); lua_setfield(L, -2, "text"); lua_pushstring(L, c.speaker.c_str()); - lua_setfield(L, -2, "speaker"); + lua_setfield(L, -2, "speaker"); pushStringVector(L, c.choices); + lua_setfield(L, -2, "choices"); lua_pushstring(L, c.fontName.c_str()); lua_setfield(L, -2, "fontName"); lua_pushnumber(L, c.fontSize); lua_setfield(L, -2, "fontSize"); @@ -1269,6 +1270,9 @@ static void registerAllComponents() if (lua_getfield(L, idx, "speaker"), lua_isstring(L, -1)) c.speaker = lua_tostring(L, -1); lua_pop(L, 1); + if (lua_getfield(L, idx, "choices"), lua_istable(L, -1)) + c.choices = readStringVector(L, lua_gettop(L)); + lua_pop(L, 1); if (lua_getfield(L, idx, "fontName"), lua_isstring(L, -1)) c.fontName = lua_tostring(L, -1); lua_pop(L, 1); diff --git a/src/features/editScene/systems/DialogueSystem.cpp b/src/features/editScene/systems/DialogueSystem.cpp index 72ce87b..eac8e9b 100644 --- a/src/features/editScene/systems/DialogueSystem.cpp +++ b/src/features/editScene/systems/DialogueSystem.cpp @@ -21,45 +21,37 @@ DialogueSystem::DialogueSystem(flecs::world &world, "dialogue_show", [this](const Ogre::String &, const editScene::EventParams ¶ms) { // Find the first entity with DialogueComponent - m_world.query().each([&](flecs::entity - e, - DialogueComponent - &dc) { - if (!dc.enabled) - return; + m_world.query().each( + [&](flecs::entity e, DialogueComponent &dc) { + if (!dc.enabled) + return; - Ogre::String text = params.getString("text"); - if (text.empty()) - return; + Ogre::String text = + params.getString("text"); + if (text.empty()) + return; - // Parse choices from comma-separated - // string - std::vector choices; - Ogre::String choicesStr = - params.getString("choices"); - if (!choicesStr.empty()) { - Ogre::String::size_type start = 0; - Ogre::String::size_type end; - while ((end = choicesStr.find(",", - start)) != - Ogre::String::npos) { - choices.push_back( - choicesStr.substr( - start, - end - start)); - start = end + 1; + // Parse choices from string array + std::vector choices; + const editScene::EventValue *choicesVal = + params.get("choices"); + if (choicesVal && + choicesVal->getType() == + editScene::EventValue:: + STRING_ARRAY) { + const auto &arr = + choicesVal + ->getStringArray(); + choices.reserve(arr.size()); + for (const auto &s : arr) + choices.push_back(s); } - if (start < choicesStr.length()) - choices.push_back( - choicesStr.substr( - start)); - } - Ogre::String speaker = - params.getString("speaker"); + Ogre::String speaker = + params.getString("speaker"); - dc.show(text, choices, speaker); - }); + dc.show(text, choices, speaker); + }); }); }