Working on dialog editor, on dialogs, on phone

This commit is contained in:
Vladislav Khorev committed 2026-06-05 21:17:51 +03:00
1 parent 0e27521bea
commit 8f7797789d
63 files changed
+9502 -1459

No files matched your search

+33 -1
View File
@@ -200,7 +200,7 @@ namespace ZL
LocationSetup uniInteriorParams;
uniInteriorParams.gameObjectsJsonPath = "resources/config2/gameobjects_uni_interior.json";
uniInteriorParams.npcsJsonPath = "resources/config2/npcs_uni_interior.json";
uniInteriorParams.dialoguesJsonPath = "resources/dialogue/uni_interior_dialogues.json";
uniInteriorParams.dialoguesJsonPath = "resources/dialogue/uni_interior_dialogues_003.json";
/*
@@ -242,6 +242,7 @@ namespace ZL
};
*/
uniInteriorParams.navigationJsonPaths = {
"resources/navigation/uni_interior3_all_locked.json",
"resources/navigation/uni_interior3_hall.json",
@@ -258,6 +259,23 @@ namespace ZL
"resources/navigation/uni_interior3_unlocked_n3.json",
"resources/navigation/uni_interior3_unlocked_hall.json"
};
/*
uniInteriorParams.navigationJsonPaths = {
"resources/navigation/uni_interior3_darklands_all_open.json",
"resources/navigation/uni_interior3_darklands_all_open.json",
"resources/navigation/uni_interior3_darklands_all_open.json",
"resources/navigation/uni_interior3_darklands_all_open.json",
"resources/navigation/uni_interior3_darklands_all_open.json",
"resources/navigation/uni_interior3_darklands_all_open.json",
"resources/navigation/uni_interior3_darklands_all_open.json", //6
"resources/navigation/uni_interior3_darklands_all_open.json",
"resources/navigation/uni_interior3_darklands_all_open.json",
"resources/navigation/uni_interior3_darklands_all_open.json",
"resources/navigation/uni_interior3_darklands_all_open.json",
"resources/navigation/uni_interior3_darklands_all_open.json",
"resources/navigation/uni_interior3_darklands_all_open.json",
"resources/navigation/uni_interior3_darklands_all_open.json"
};*/
uniInteriorParams.teleportsJsonPath = "resources/config2/teleports_uni_interior.json";
uniInteriorParams.triggerZonesJsonPath = "resources/config2/trigger_zones_uni_interior.json";
@@ -273,6 +291,7 @@ namespace ZL
locations["uni_interior"]->requestNightDayTransition = [this](bool isNight, bool isDawn) { this->menuManager.isNight = isNight; this->menuManager.isDawn = isDawn; };
locations["uni_interior"]->requestDarklandsTransition = [this]() { return startDarklandsTransition(); };
locations["uni_interior"]->requestAdvanceDarklandsHud = [this]() { menuManager.advanceUniIntDarklandsHud(); };
locations["uni_interior"]->requestClosePhone = [this]() { menuManager.closePhoneEntirely(); };
if (locations["uni_interior"]->player)
locations["uni_interior"]->player->onDeathAnimComplete = [this]() { startDarklandsTransition(); };
for (auto& npc : locations["uni_interior"]->npcs) {
@@ -301,6 +320,7 @@ namespace ZL
locations["uni_exterior"]->scriptEngine.setGlobalFloatStore(&globalFloats);
locations["uni_exterior"]->requestNightDayTransition = [this](bool isNight, bool isDawn) { this->menuManager.isNight = isNight; this->menuManager.isDawn = isDawn; };
locations["uni_exterior"]->requestDarklandsTransition = [this]() { return startDarklandsTransition(); };
locations["uni_exterior"]->requestClosePhone = [this]() { menuManager.closePhoneEntirely(); };
if (locations["uni_exterior"]->player)
locations["uni_exterior"]->player->onDeathAnimComplete = [this]() { startDarklandsTransition(); };
@@ -362,6 +382,7 @@ namespace ZL
locations["location_dorm"]->scriptEngine.setGlobalFloatStore(&globalFloats);
locations["location_dorm"]->requestNightDayTransition = [this](bool isNight, bool isDawn) { this->menuManager.isNight = isNight; this->menuManager.isDawn = isDawn; };
locations["location_dorm"]->requestDarklandsTransition = [this]() { return startDarklandsTransition(); };
locations["location_dorm"]->requestClosePhone = [this]() { menuManager.closePhoneEntirely(); };
if (locations["location_dorm"]->player)
locations["location_dorm"]->player->onDeathAnimComplete = [this]() { startDarklandsTransition(); };
@@ -391,6 +412,12 @@ namespace ZL
locations["uni_interior"]->onTeleport = teleportCallback;
locations["location_dorm"]->onTeleport = teleportCallback;
// Share the global int store with all dialogue runtimes so flags set via
// dialogue JSON and via Lua set_dialogue_flag all see the same state.
for (auto& [name, loc] : locations) {
loc->dialogueSystem.setGlobalFlagStore(&globalInts);
}
// Wire tutorial advance callbacks for all locations.
// advanceTutorialStep() guards against double-advancing, so sharing is safe.
for (auto& [name, loc] : locations) {
@@ -423,6 +450,11 @@ namespace ZL
startNightTransition();
};
menuManager.chatOpenCallback = [this](int chatIndex) {
if (currentLocation)
currentLocation->scriptEngine.callChatOpenCallback(chatIndex);
};
// Wire chat-bubble callback so dynamic bubbles appear as dialogue lines are shown.
for (auto& [name, loc] : locations) {
loc->dialogueSystem.setOnChatBubbleReady([this](const std::string& text, bool incoming) {
+2
View File
@@ -116,6 +116,8 @@ namespace ZL
// from step12 to step13 (trigger-zone encounter hint).
std::function<void()> requestAdvanceDarklandsHud;
std::function<void()> requestClosePhone;
// Navigation editor — toggle with 'N', save with 'B', right-click to finalize polygon
EditorMode editorMode = EditorMode::None;
LocationEditor editor;
+6 -9
View File
@@ -347,15 +347,15 @@ namespace ZL {
uiManager.setButtonCallback("phoneMain", [this](const std::string&) {});
uiManager.setTextButtonCallback("chat1button", [this](const std::string&) {
chatUnread_[0] = false;
openPhoneChatFromList(0, phoneChat1Root, "dialog_chat_aiperi001");
openPhoneChatFromList(0, phoneChat1Root);
});
uiManager.setTextButtonCallback("chat2button", [this](const std::string&) {
chatUnread_[1] = false;
openPhoneChatFromList(1, phoneChat2Root, "dialog_chat_parents001");
openPhoneChatFromList(1, phoneChat2Root);
});
uiManager.setTextButtonCallback("chat3button", [this](const std::string&) {
chatUnread_[2] = false;
openPhoneChatFromList(2, phoneChat3Root, "dialog_chat_news001");
openPhoneChatFromList(2, phoneChat3Root);
});
}
@@ -436,13 +436,11 @@ namespace ZL {
});
}
void MenuManager::openPhoneChatFromList(int chatIndex, std::shared_ptr<UiNode> chatRoot, const std::string& dialogueId) {
void MenuManager::openPhoneChatFromList(int chatIndex, std::shared_ptr<UiNode> chatRoot) {
activeChatIndex_ = chatIndex;
phoneChatVisibleBubbles_.clear();
uiManager.pushMenuFromSavedRoot(chatRoot);
const bool firstOpen = dialogueId.empty() || startedDialogues_.find(dialogueId) == startedDialogues_.end();
rebuildChatBubblesFromHistory(chatIndex);
uiManager.setButtonCallback("phoneExitButton", [this](const std::string&) {
@@ -453,9 +451,8 @@ namespace ZL {
returnToPhoneChatList();
});
if (firstOpen && startDialogueFunc && !dialogueId.empty()) {
startedDialogues_.insert(dialogueId);
startDialogueFunc(dialogueId);
if (chatOpenCallback) {
chatOpenCallback(chatIndex);
}
}
+2 -5
View File
@@ -8,7 +8,6 @@
#include <vector>
#include <string>
#include <memory>
#include <unordered_set>
namespace ZL {
@@ -69,6 +68,7 @@ namespace ZL {
std::function<void(const std::string&)> startDialogueFunc;
std::function<void()> startDarklandsTransitionFunc;
std::function<void()> startNightTransitionFunc;
std::function<void(int)> chatOpenCallback;
// Called when a chat message bubble should be shown (text + direction)
void onChatBubbleReady(const std::string& text, bool incoming);
@@ -104,7 +104,7 @@ namespace ZL {
void refreshChatUnreadIndicators();
void resetPhoneChatNodes();
void recomputePhoneChatPositions();
void openPhoneChatFromList(int chatIndex, std::shared_ptr<UiNode> chatRoot, const std::string& dialogueId);
void openPhoneChatFromList(int chatIndex, std::shared_ptr<UiNode> chatRoot);
void returnToPhoneChatList();
void closePhoneScreenFromChat();
void applyUniIntHud();
@@ -166,9 +166,6 @@ namespace ZL {
int selectedQuestIndex = -1;
std::vector<std::string> visibleQuestIds;
// Dialogues that have been started at least once; re-opening a chat won't restart them
std::unordered_set<std::string> startedDialogues_;
bool chatUnread_[3] = { true, true, true };
int money_ = 5500;
+70
View File
@@ -26,6 +26,7 @@ namespace ZL {
sol::protected_function darklandsEnterCallback;
sol::protected_function darklandsExitCallback;
sol::protected_function triggerNightEnterCallback;
sol::protected_function chatOpenCallbacks[3];
};
ScriptEngine::ScriptEngine() = default;
@@ -65,6 +66,44 @@ namespace ZL {
npcs[index]->setTarget(Eigen::Vector3f(x, y, z), std::move(cb));
});
api.set_function("player_walk_to",
[game](float x, float y, float z, sol::object on_arrived) {
std::function<void()> cb;
if (on_arrived.is<sol::protected_function>()) {
sol::protected_function fn = on_arrived.as<sol::protected_function>();
cb = [fn]() mutable {
auto result = fn();
if (!result.valid()) {
sol::error err = result;
std::cerr << "[script] on_arrived error: " << err.what() << "\n";
}
};
}
game->player->homePosition = Eigen::Vector3f(x, 0.f, z);
game->player->setTarget(Eigen::Vector3f(x, y, z), std::move(cb));
});
api.set_function("has_item", [inventory](const std::string& itemId) {
const Item* item = ItemRegistry::instance().findById(itemId);
if (item) {
if (inventory->hasItem(itemId)) {
std::cout << "[script] has_item: " << item->name << " returns true" << std::endl;
return true;
}
else
{
std::cout << "[script] has_item: " << item->name << " returns false" << std::endl;
return false;
}
}
else {
std::cerr << "[script] has_item: item '" << itemId << "' not found in ItemRegistry\n";
return false;
}
});
// pickup_item(item_id)
api.set_function("pickup_item", [inventory](const std::string& itemId) {
const Item* item = ItemRegistry::instance().findById(itemId);
@@ -229,6 +268,11 @@ namespace ZL {
return game->isNight;
});
api.set_function("is_dawn",
[game]() {
return game->isDawn;
});
// advance_darklands_hud()
// Advances the uni_interior darklands HUD from step12 to step13.
// Call when the player enters the ghost trigger zone in darklands.
@@ -319,6 +363,19 @@ namespace ZL {
this_impl->darklandsExitCallback = onExit.as<sol::protected_function>();
});
api.set_function("close_phone",
[game]() {
if (game->requestClosePhone)
game->requestClosePhone();
});
api.set_function("set_chat_callbacks",
[this_impl = impl.get()](sol::object cb0, sol::object cb1, sol::object cb2) {
if (cb0.is<sol::protected_function>()) this_impl->chatOpenCallbacks[0] = cb0.as<sol::protected_function>();
if (cb1.is<sol::protected_function>()) this_impl->chatOpenCallbacks[1] = cb1.as<sol::protected_function>();
if (cb2.is<sol::protected_function>()) this_impl->chatOpenCallbacks[2] = cb2.as<sol::protected_function>();
});
api.set_function("set_enter_night_callback",
[this_impl = impl.get()](sol::object onEnter) {
if (onEnter.is<sol::protected_function>())
@@ -357,6 +414,7 @@ namespace ZL {
}
Eigen::Vector3f pos(x, y, z);
npcs[index]->position = pos;
npcs[index]->homePosition = pos;
npcs[index]->setTarget(pos);
});
@@ -790,6 +848,18 @@ namespace ZL {
}
}
void ScriptEngine::callChatOpenCallback(int chatIndex) {
if (!impl) return;
if (chatIndex < 0 || chatIndex > 2) return;
auto& fn = impl->chatOpenCallbacks[chatIndex];
if (!fn.valid()) return;
auto result = fn();
if (!result.valid()) {
sol::error err = result;
std::cerr << "[SCRIPT] chat open callback error for chat " << chatIndex << ": " << err.what() << "\n";
}
}
void ScriptEngine::callNpcBumpsPlayerCallback(int npcIndex) {
if (!impl) return;
auto it = impl->npcBumpsPlayerCallbacks.find(npcIndex);
+2
View File
@@ -48,6 +48,8 @@ public:
void callCutsceneCompleteCallback(const std::string& cutsceneId);
void callChatOpenCallback(int chatIndex);
private:
struct Impl;
std::unique_ptr<Impl> impl;
+15 -12
View File
@@ -307,12 +307,17 @@ void DialogueRuntime::skipCurrentCutscene() {
}
void DialogueRuntime::setFlag(const std::string& name, int value) {
flags[name] = value;
if (flagStore) (*flagStore)[name] = value;
}
int DialogueRuntime::getFlag(const std::string& name) const {
auto it = flags.find(name);
return (it != flags.end()) ? it->second : 0;
if (!flagStore) return 0;
auto it = flagStore->find(name);
return (it != flagStore->end()) ? it->second : 0;
}
void DialogueRuntime::setGlobalFlagStore(std::unordered_map<std::string, int>* store) {
flagStore = store;
}
void DialogueRuntime::setQuestJournal(Quest::QuestJournal* journal) {
@@ -368,10 +373,10 @@ void DialogueRuntime::applyEffects(const std::vector<Effect>& effects) {
continue;
}
if (effect.relative) {
flags[effect.flag] += effect.value;
setFlag(effect.flag, getFlag(effect.flag) + effect.value);
}
else {
flags[effect.flag] = effect.value;
setFlag(effect.flag, effect.value);
}
}
}
@@ -424,6 +429,8 @@ bool DialogueRuntime::enterNode(const std::string& nodeId) {
return true;
case NodeType::End:
if (!node.luaCallback.empty() && onDialogueLineStarted)
onDialogueLineStarted(node.luaCallback);
stop();
return true;
@@ -907,7 +914,7 @@ int DialogueRuntime::computeCameraTrackDurationMs(const StaticCutsceneDefinition
}
return total;
}
/*
DialogueRuntime::json DialogueRuntime::buildSaveState() const {
json result;
result["active"] = isActive();
@@ -917,7 +924,6 @@ DialogueRuntime::json DialogueRuntime::buildSaveState() const {
result["selectedChoice"] = selectedChoice;
result["currentCutsceneLine"] = currentCutsceneLine;
result["cutsceneTimerMs"] = cutsceneTimerMs;
result["flags"] = flags;
result["consumedChoices"] = consumedChoices;
return result;
}
@@ -930,10 +936,7 @@ bool DialogueRuntime::restoreSaveState(const json& state) {
flags.clear();
consumedChoices.clear();
if (state.contains("flags")) {
flags = state["flags"].get<std::unordered_map<std::string, int>>();
}
if (state.contains("consumedChoices")) {
if (state.contains("consumedChoices")) {
consumedChoices = state["consumedChoices"].get<std::unordered_set<std::string>>();
}
@@ -963,5 +966,5 @@ bool DialogueRuntime::restoreSaveState(const json& state) {
}
return ok;
}
*/
} // namespace ZL::Dialogue
+5 -3
View File
@@ -43,10 +43,12 @@ public:
void setFlag(const std::string& name, int value);
int getFlag(const std::string& name) const;
void setGlobalFlagStore(std::unordered_map<std::string, int>* store);
void setQuestJournal(Quest::QuestJournal* journal);
json buildSaveState() const;
bool restoreSaveState(const json& state);
//json buildSaveState() const;
//bool restoreSaveState(const json& state);
private:
enum class Mode {
@@ -69,7 +71,7 @@ private:
const DialogueDefinition* activeDialogue = nullptr;
const StaticCutsceneDefinition* activeCutscene = nullptr;
std::unordered_map<std::string, int> flags;
std::unordered_map<std::string, int>* flagStore = nullptr;
std::unordered_set<std::string> consumedChoices;
std::string currentNodeId;
+2
View File
@@ -38,6 +38,8 @@ public:
void setFlag(const std::string& name, int value) { runtime.setFlag(name, value); }
int getFlag(const std::string& name) const { return runtime.getFlag(name); }
void setGlobalFlagStore(std::unordered_map<std::string, int>* store) { runtime.setGlobalFlagStore(store); }
void setQuestJournal(Quest::QuestJournal* journal) { runtime.setQuestJournal(journal); }
private: