Working on dialog editor, on dialogs, on phone
This commit is contained in:
1 parent
0e27521bea
commit
8f7797789d
63 files changed
+9502
-1459
No files matched your search
+33
-1
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -48,6 +48,8 @@ public:
|
||||
|
||||
void callCutsceneCompleteCallback(const std::string& cutsceneId);
|
||||
|
||||
void callChatOpenCallback(int chatIndex);
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in new issue
Block a user