Working on game state
This commit is contained in:
parent
b01e970554
commit
2807d82d51
@ -109,6 +109,8 @@ set(SOURCES
|
|||||||
../src/LocationEditor.cpp
|
../src/LocationEditor.cpp
|
||||||
../src/GameConstants.h
|
../src/GameConstants.h
|
||||||
../src/GameConstants.cpp
|
../src/GameConstants.cpp
|
||||||
|
../src/GameState.h
|
||||||
|
../src/GameState.cpp
|
||||||
../src/ScriptEngine.h
|
../src/ScriptEngine.h
|
||||||
../src/ScriptEngine.cpp
|
../src/ScriptEngine.cpp
|
||||||
../src/navigation/PathFinder.h
|
../src/navigation/PathFinder.h
|
||||||
|
|||||||
@ -64,6 +64,8 @@ add_executable(witcher001
|
|||||||
../src/LocationEditor.cpp
|
../src/LocationEditor.cpp
|
||||||
../src/GameConstants.h
|
../src/GameConstants.h
|
||||||
../src/GameConstants.cpp
|
../src/GameConstants.cpp
|
||||||
|
../src/GameState.h
|
||||||
|
../src/GameState.cpp
|
||||||
../src/ScriptEngine.h
|
../src/ScriptEngine.h
|
||||||
../src/ScriptEngine.cpp
|
../src/ScriptEngine.cpp
|
||||||
../src/navigation/PathFinder.h
|
../src/navigation/PathFinder.h
|
||||||
|
|||||||
363
src/Game.cpp
363
src/Game.cpp
@ -64,10 +64,16 @@ namespace ZL
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Location* Game::currentLocation() const {
|
||||||
|
if (gameState.currentLocationName.empty()) return nullptr;
|
||||||
|
auto it = gameState.locations.find(gameState.currentLocationName);
|
||||||
|
return (it != gameState.locations.end()) ? it->second.get() : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
Game::Game()
|
Game::Game()
|
||||||
: newTickCount(0)
|
: newTickCount(0)
|
||||||
, lastTickCount(0)
|
, lastTickCount(0)
|
||||||
, menuManager(renderer, globalInts)
|
, menuManager(renderer, gameState)
|
||||||
, audioPlayer(std::make_unique<AudioPlayerAsync>())
|
, audioPlayer(std::make_unique<AudioPlayerAsync>())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -236,7 +242,7 @@ namespace ZL
|
|||||||
|
|
||||||
ItemRegistry::instance().loadFromJson("resources/config2/items.json", CONST_ZIP_FILE);
|
ItemRegistry::instance().loadFromJson("resources/config2/items.json", CONST_ZIP_FILE);
|
||||||
|
|
||||||
globalFloats["player_hp"] = 200;
|
gameState.globalFloats["player_hp"] = 200;
|
||||||
|
|
||||||
LocationSetup uniInteriorParams;
|
LocationSetup uniInteriorParams;
|
||||||
uniInteriorParams.gameObjectsJsonPath = "resources/config2/gameobjects_uni_interior_x.json";
|
uniInteriorParams.gameObjectsJsonPath = "resources/config2/gameobjects_uni_interior_x.json";
|
||||||
@ -311,24 +317,24 @@ namespace ZL
|
|||||||
uniInteriorParams.interactiveObjectsJsonPath = "resources/config2/interactive_objects_uni_interior_x.json";
|
uniInteriorParams.interactiveObjectsJsonPath = "resources/config2/interactive_objects_uni_interior_x.json";
|
||||||
uniInteriorParams.playerPosition = Eigen::Vector3f(0.942694, 0, -9.63104);
|
uniInteriorParams.playerPosition = Eigen::Vector3f(0.942694, 0, -9.63104);
|
||||||
|
|
||||||
locations["uni_interior"] = std::make_shared<Location>(renderer, inventory);
|
gameState.locations["uni_interior"] = std::make_shared<Location>(renderer, gameState.inventory);
|
||||||
locations["uni_interior"]->setup(uniInteriorParams, &menuManager.questJournal);
|
gameState.locations["uni_interior"]->setup(uniInteriorParams, &gameState.questJournal);
|
||||||
locations["uni_interior"]->scriptEngine.setGlobalStore(&globalInts);
|
gameState.locations["uni_interior"]->scriptEngine.setGlobalStore(&gameState.globalInts);
|
||||||
locations["uni_interior"]->scriptEngine.setGlobalFloatStore(&globalFloats);
|
gameState.locations["uni_interior"]->scriptEngine.setGlobalFloatStore(&gameState.globalFloats);
|
||||||
locations["uni_interior"]->requestNightDayTransition = [this](bool isNight, bool isDawn) { this->menuManager.isNight = isNight; this->menuManager.isDawn = isDawn; };
|
gameState.locations["uni_interior"]->requestNightDayTransition = [this](bool isNight, bool isDawn) { gameState.isNight = isNight; gameState.isDawn = isDawn; };
|
||||||
locations["uni_interior"]->requestDarklandsTransition = [this]() { return startDarklandsTransition(); };
|
gameState.locations["uni_interior"]->requestDarklandsTransition = [this]() { return startDarklandsTransition(); };
|
||||||
locations["uni_interior"]->requestAdvanceDarklandsHud = [this]() { menuManager.advanceUniIntDarklandsHud(); };
|
gameState.locations["uni_interior"]->requestAdvanceDarklandsHud = [this]() { menuManager.advanceUniIntDarklandsHud(); };
|
||||||
locations["uni_interior"]->requestClosePhone = [this]() { menuManager.closePhoneEntirely(); };
|
gameState.locations["uni_interior"]->requestClosePhone = [this]() { menuManager.closePhoneEntirely(); };
|
||||||
locations["uni_interior"]->requestReturnToMainMenu = [this]() {
|
gameState.locations["uni_interior"]->requestReturnToMainMenu = [this]() {
|
||||||
this->currentLocation = nullptr;
|
gameState.currentLocationName.clear();
|
||||||
menuManager.showMainMenu();
|
menuManager.showMainMenu();
|
||||||
};
|
};
|
||||||
locations["uni_interior"]->requestSetChatUnread = [this](int idx, bool unread, const std::string& msg) {
|
gameState.locations["uni_interior"]->requestSetChatUnread = [this](int idx, bool unread, const std::string& msg) {
|
||||||
menuManager.setChatUnread(idx, unread, msg);
|
menuManager.setChatUnread(idx, unread, msg);
|
||||||
};
|
};
|
||||||
if (locations["uni_interior"]->player)
|
if (gameState.locations["uni_interior"]->player)
|
||||||
locations["uni_interior"]->player->state.onDeathAnimComplete = [this]() { startDarklandsTransition(); };
|
gameState.locations["uni_interior"]->player->state.onDeathAnimComplete = [this]() { startDarklandsTransition(); };
|
||||||
for (auto& npc : locations["uni_interior"]->npcs) {
|
for (auto& npc : gameState.locations["uni_interior"]->npcs) {
|
||||||
if (npc && npc->state.canAttack) {
|
if (npc && npc->state.canAttack) {
|
||||||
npc->state.onDeathAnimComplete = [this]() { menuManager.onEnemyKilledInUniInterior(); };
|
npc->state.onDeathAnimComplete = [this]() { menuManager.onEnemyKilledInUniInterior(); };
|
||||||
}
|
}
|
||||||
@ -348,28 +354,22 @@ namespace ZL
|
|||||||
uniExteriorParams.npcsJsonPath = "resources/config2/npcs_uni_exterior.json";
|
uniExteriorParams.npcsJsonPath = "resources/config2/npcs_uni_exterior.json";
|
||||||
uniExteriorParams.dialoguesJsonPath = "resources/dialogue/uni_exterior_dialogues.json";
|
uniExteriorParams.dialoguesJsonPath = "resources/dialogue/uni_exterior_dialogues.json";
|
||||||
|
|
||||||
locations["uni_exterior"] = std::make_shared<Location>(renderer, inventory);
|
gameState.locations["uni_exterior"] = std::make_shared<Location>(renderer, gameState.inventory);
|
||||||
locations["uni_exterior"]->setup(uniExteriorParams, &menuManager.questJournal);
|
gameState.locations["uni_exterior"]->setup(uniExteriorParams, &gameState.questJournal);
|
||||||
locations["uni_exterior"]->scriptEngine.setGlobalStore(&globalInts);
|
gameState.locations["uni_exterior"]->scriptEngine.setGlobalStore(&gameState.globalInts);
|
||||||
locations["uni_exterior"]->scriptEngine.setGlobalFloatStore(&globalFloats);
|
gameState.locations["uni_exterior"]->scriptEngine.setGlobalFloatStore(&gameState.globalFloats);
|
||||||
locations["uni_exterior"]->requestNightDayTransition = [this](bool isNight, bool isDawn) {
|
gameState.locations["uni_exterior"]->requestNightDayTransition = [this](bool isNight, bool isDawn) {
|
||||||
this->menuManager.isNight = isNight;
|
gameState.isNight = isNight;
|
||||||
this->menuManager.isDawn = isDawn;
|
gameState.isDawn = isDawn;
|
||||||
/*for (auto& locPair : locations) {
|
|
||||||
if (locPair.second) {
|
|
||||||
locPair.second->isNight = isNight;
|
|
||||||
locPair.second->isDawn = isDawn;
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
};
|
};
|
||||||
locations["uni_exterior"]->requestDarklandsTransition = [this]() { return startDarklandsTransition(); };
|
gameState.locations["uni_exterior"]->requestDarklandsTransition = [this]() { return startDarklandsTransition(); };
|
||||||
locations["uni_exterior"]->requestClosePhone = [this]() { menuManager.closePhoneEntirely(); };
|
gameState.locations["uni_exterior"]->requestClosePhone = [this]() { menuManager.closePhoneEntirely(); };
|
||||||
locations["uni_exterior"]->requestReturnToMainMenu = [this]() { menuManager.showMainMenu(); };
|
gameState.locations["uni_exterior"]->requestReturnToMainMenu = [this]() { menuManager.showMainMenu(); };
|
||||||
locations["uni_exterior"]->requestSetChatUnread = [this](int idx, bool unread, const std::string& msg) {
|
gameState.locations["uni_exterior"]->requestSetChatUnread = [this](int idx, bool unread, const std::string& msg) {
|
||||||
menuManager.setChatUnread(idx, unread, msg);
|
menuManager.setChatUnread(idx, unread, msg);
|
||||||
};
|
};
|
||||||
if (locations["uni_exterior"]->player)
|
if (gameState.locations["uni_exterior"]->player)
|
||||||
locations["uni_exterior"]->player->state.onDeathAnimComplete = [this]() { startDarklandsTransition(); };
|
gameState.locations["uni_exterior"]->player->state.onDeathAnimComplete = [this]() { startDarklandsTransition(); };
|
||||||
|
|
||||||
LocationSetup params_dorm;
|
LocationSetup params_dorm;
|
||||||
//params_dorm.gameObjectsJsonPath = "resources/config2/gameobjects_dorm.json";
|
//params_dorm.gameObjectsJsonPath = "resources/config2/gameobjects_dorm.json";
|
||||||
@ -404,96 +404,96 @@ namespace ZL
|
|||||||
params_dorm.playerPosition = Eigen::Vector3f(6.76345, 0, -14.6022);
|
params_dorm.playerPosition = Eigen::Vector3f(6.76345, 0, -14.6022);
|
||||||
|
|
||||||
|
|
||||||
locations["location_dorm"] = std::make_shared<Location>(renderer, inventory);
|
gameState.locations["location_dorm"] = std::make_shared<Location>(renderer, gameState.inventory);
|
||||||
locations["location_dorm"]->setup(params_dorm, &menuManager.questJournal);
|
gameState.locations["location_dorm"]->setup(params_dorm, &gameState.questJournal);
|
||||||
locations["location_dorm"]->scriptEngine.setGlobalStore(&globalInts);
|
gameState.locations["location_dorm"]->scriptEngine.setGlobalStore(&gameState.globalInts);
|
||||||
locations["location_dorm"]->scriptEngine.setGlobalFloatStore(&globalFloats);
|
gameState.locations["location_dorm"]->scriptEngine.setGlobalFloatStore(&gameState.globalFloats);
|
||||||
locations["location_dorm"]->requestNightDayTransition = [this](bool isNight, bool isDawn) { this->menuManager.isNight = isNight; this->menuManager.isDawn = isDawn; };
|
gameState.locations["location_dorm"]->requestNightDayTransition = [this](bool isNight, bool isDawn) { gameState.isNight = isNight; gameState.isDawn = isDawn; };
|
||||||
locations["location_dorm"]->requestDarklandsTransition = [this]() { return startDarklandsTransition(); };
|
gameState.locations["location_dorm"]->requestDarklandsTransition = [this]() { return startDarklandsTransition(); };
|
||||||
locations["location_dorm"]->requestClosePhone = [this]() { menuManager.closePhoneEntirely(); };
|
gameState.locations["location_dorm"]->requestClosePhone = [this]() { menuManager.closePhoneEntirely(); };
|
||||||
locations["location_dorm"]->requestReturnToMainMenu = [this]() { menuManager.showMainMenu(); };
|
gameState.locations["location_dorm"]->requestReturnToMainMenu = [this]() { menuManager.showMainMenu(); };
|
||||||
locations["location_dorm"]->requestSetChatUnread = [this](int idx, bool unread, const std::string& msg) {
|
gameState.locations["location_dorm"]->requestSetChatUnread = [this](int idx, bool unread, const std::string& msg) {
|
||||||
menuManager.setChatUnread(idx, unread, msg);
|
menuManager.setChatUnread(idx, unread, msg);
|
||||||
};
|
};
|
||||||
if (locations["location_dorm"]->player)
|
if (gameState.locations["location_dorm"]->player)
|
||||||
locations["location_dorm"]->player->state.onDeathAnimComplete = [this]() { startDarklandsTransition(); };
|
gameState.locations["location_dorm"]->player->state.onDeathAnimComplete = [this]() { startDarklandsTransition(); };
|
||||||
|
|
||||||
locations["location_dorm"]->onPlayerTaxiRequired = [this]() {
|
gameState.locations["location_dorm"]->onPlayerTaxiRequired = [this]() {
|
||||||
menuManager.tutorialShowTaxiHint();
|
menuManager.tutorialShowTaxiHint();
|
||||||
};
|
};
|
||||||
|
|
||||||
locations["location_dorm"]->state.tutorialInteractiveObjectsLocked = true;
|
gameState.locations["location_dorm"]->state.tutorialInteractiveObjectsLocked = true;
|
||||||
|
|
||||||
menuManager.tutorialUnlockInteractiveObjectsFunc = [this]()
|
menuManager.tutorialUnlockInteractiveObjectsFunc = [this]()
|
||||||
{
|
{
|
||||||
if (locations["location_dorm"])
|
if (gameState.locations["location_dorm"])
|
||||||
{
|
{
|
||||||
locations["location_dorm"]->state.tutorialInteractiveObjectsLocked = false;
|
gameState.locations["location_dorm"]->state.tutorialInteractiveObjectsLocked = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
menuManager.callTaxiFunc = [this]()
|
menuManager.callTaxiFunc = [this]()
|
||||||
{
|
{
|
||||||
if (locations["location_dorm"])
|
if (gameState.locations["location_dorm"])
|
||||||
{
|
{
|
||||||
locations["location_dorm"]->onPlayerTaxiRequired = nullptr;
|
gameState.locations["location_dorm"]->onPlayerTaxiRequired = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentLocation)
|
if (currentLocation())
|
||||||
{
|
{
|
||||||
currentLocation->scriptEngine.callCallTaxiCallback();
|
currentLocation()->scriptEngine.callCallTaxiCallback();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Teleport callbacks: destination name and position come from the teleport zone data.
|
// Teleport callbacks: destination name and position come from the teleport zone data.
|
||||||
auto teleportCallback = [this](const std::string& destName, const Eigen::Vector3f& destPos, float destRotY) {
|
auto teleportCallback = [this](const std::string& destName, const Eigen::Vector3f& destPos, float destRotY) {
|
||||||
std::cout << "[TELEPORT] " << " -> " << destName << std::endl;
|
std::cout << "[TELEPORT] " << " -> " << destName << std::endl;
|
||||||
auto it = locations.find(destName);
|
auto it = gameState.locations.find(destName);
|
||||||
if (it == locations.end()) {
|
if (it == gameState.locations.end()) {
|
||||||
std::cerr << "[TELEPORT] Unknown destination location: " << destName << std::endl;
|
std::cerr << "[TELEPORT] Unknown destination location: " << destName << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (currentLocation)
|
if (currentLocation())
|
||||||
{
|
{
|
||||||
currentLocation->scriptEngine.callLocationExitCallback();
|
currentLocation()->scriptEngine.callLocationExitCallback();
|
||||||
currentLocation->dialogueSystem.stopDialogue();
|
currentLocation()->dialogueSystem.stopDialogue();
|
||||||
}
|
}
|
||||||
currentLocation = it->second;
|
gameState.currentLocationName = destName;
|
||||||
if (currentLocation->player) {
|
Location* loc = currentLocation();
|
||||||
currentLocation->player->state.position = destPos;
|
if (loc->player) {
|
||||||
currentLocation->player->setTarget(destPos);
|
loc->player->state.position = destPos;
|
||||||
currentLocation->player->stopInPlace();
|
loc->player->setTarget(destPos);
|
||||||
currentLocation->player->state.facingAngle = destRotY;
|
loc->player->stopInPlace();
|
||||||
currentLocation->player->state.targetFacingAngle = destRotY;
|
loc->player->state.facingAngle = destRotY;
|
||||||
|
loc->player->state.targetFacingAngle = destRotY;
|
||||||
}
|
}
|
||||||
currentLocation->state.cameraAzimuth = destRotY;
|
loc->state.cameraAzimuth = destRotY;
|
||||||
currentLocation->state.isDarklands = isDarklands;
|
loc->state.isDarklands = gameState.isDarklands;
|
||||||
currentLocation->state.isNight = menuManager.isNight;
|
loc->state.isNight = gameState.isNight;
|
||||||
currentLocation->state.isDawn = menuManager.isDawn;
|
loc->state.isDawn = gameState.isDawn;
|
||||||
currentLocation->scriptEngine.callLocationEnterCallback();
|
loc->scriptEngine.callLocationEnterCallback();
|
||||||
|
|
||||||
|
|
||||||
menuManager.onLocationChanged(destName);
|
menuManager.onLocationChanged(destName);
|
||||||
};
|
};
|
||||||
|
|
||||||
locations["uni_exterior"]->onTeleport = teleportCallback;
|
gameState.locations["uni_exterior"]->onTeleport = teleportCallback;
|
||||||
locations["uni_interior"]->onTeleport = teleportCallback;
|
gameState.locations["uni_interior"]->onTeleport = teleportCallback;
|
||||||
locations["location_dorm"]->onTeleport = teleportCallback;
|
gameState.locations["location_dorm"]->onTeleport = teleportCallback;
|
||||||
|
|
||||||
// Share the global int store with all dialogue runtimes so flags set via
|
// 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.
|
// dialogue JSON and via Lua set_dialogue_flag all see the same state.
|
||||||
for (auto& [name, loc] : locations) {
|
for (auto& [name, loc] : gameState.locations) {
|
||||||
loc->dialogueSystem.setGlobalFlagStore(&globalInts);
|
loc->dialogueSystem.setGlobalFlagStore(&gameState.globalInts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wire tutorial advance callbacks for all locations.
|
// Wire tutorial advance callbacks for all locations.
|
||||||
// advanceTutorialStep() guards against double-advancing, so sharing is safe.
|
// advanceTutorialStep() guards against double-advancing, so sharing is safe.
|
||||||
for (auto& [name, loc] : locations) {
|
for (auto& [name, loc] : gameState.locations) {
|
||||||
loc->dialogueSystem.setOnDialogueAdvanced([this]() {
|
loc->dialogueSystem.setOnDialogueAdvanced([this]() {
|
||||||
menuManager.advanceTutorialStep();
|
menuManager.advanceTutorialStep();
|
||||||
});
|
});
|
||||||
loc->onPlayerFloorWalk = [this]() {
|
loc->onPlayerFloorWalk = [this]() {
|
||||||
if (menuManager.tutorialStep == TutorialStep::Step2) {
|
if (gameState.tutorialStep == TutorialStep::Step2) {
|
||||||
menuManager.advanceTutorialStep();
|
menuManager.advanceTutorialStep();
|
||||||
}
|
}
|
||||||
menuManager.onPlayerStartedWalking();
|
menuManager.onPlayerStartedWalking();
|
||||||
@ -509,19 +509,19 @@ namespace ZL
|
|||||||
|
|
||||||
|
|
||||||
// Wire inventory callbacks: tutorial tracking + toast notifications.
|
// Wire inventory callbacks: tutorial tracking + toast notifications.
|
||||||
inventory.onItemAdded = [this](const std::string& itemId) {
|
gameState.inventory.onItemAdded = [this](const std::string& itemId) {
|
||||||
menuManager.onItemPickedUp(itemId);
|
menuManager.onItemPickedUp(itemId);
|
||||||
const Item* item = ItemRegistry::instance().findById(itemId);
|
const Item* item = ItemRegistry::instance().findById(itemId);
|
||||||
if (item) menuManager.showToast("resources/w/ui/img/toast/item_received001.png", item->name);
|
if (item) menuManager.showToast("resources/w/ui/img/toast/item_received001.png", item->name);
|
||||||
};
|
};
|
||||||
inventory.onItemRemoved = [this](const std::string& itemId) {
|
gameState.inventory.onItemRemoved = [this](const std::string& itemId) {
|
||||||
const Item* item = ItemRegistry::instance().findById(itemId);
|
const Item* item = ItemRegistry::instance().findById(itemId);
|
||||||
if (item) menuManager.showToast("resources/w/ui/img/toast/item_removed001.png", item->name);
|
if (item) menuManager.showToast("resources/w/ui/img/toast/item_removed001.png", item->name);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Wire phone dialogue start function so MenuManager can trigger dialogues.
|
// Wire phone dialogue start function so MenuManager can trigger dialogues.
|
||||||
menuManager.startDialogueFunc = [this](const std::string& id) {
|
menuManager.startDialogueFunc = [this](const std::string& id) {
|
||||||
if (currentLocation) currentLocation->dialogueSystem.startDialogue(id);
|
if (currentLocation()) currentLocation()->dialogueSystem.startDialogue(id);
|
||||||
};
|
};
|
||||||
|
|
||||||
menuManager.startDarklandsTransitionFunc = [this]() {
|
menuManager.startDarklandsTransitionFunc = [this]() {
|
||||||
@ -533,19 +533,19 @@ namespace ZL
|
|||||||
};
|
};
|
||||||
|
|
||||||
menuManager.chatOpenCallback = [this](int chatIndex) {
|
menuManager.chatOpenCallback = [this](int chatIndex) {
|
||||||
if (currentLocation)
|
if (currentLocation())
|
||||||
currentLocation->scriptEngine.callChatOpenCallback(chatIndex);
|
currentLocation()->scriptEngine.callChatOpenCallback(chatIndex);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Wire chat-bubble callback so dynamic bubbles appear as dialogue lines are shown.
|
// Wire chat-bubble callback so dynamic bubbles appear as dialogue lines are shown.
|
||||||
for (auto& [name, loc] : locations) {
|
for (auto& [name, loc] : gameState.locations) {
|
||||||
loc->dialogueSystem.setOnChatBubbleReady([this](const std::string& text, bool incoming) {
|
loc->dialogueSystem.setOnChatBubbleReady([this](const std::string& text, bool incoming) {
|
||||||
menuManager.onChatBubbleReady(text, incoming);
|
menuManager.onChatBubbleReady(text, incoming);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wire cutscene HUD: show skip button when cutscene starts, restore HUD when it ends.
|
// Wire cutscene HUD: show skip button when cutscene starts, restore HUD when it ends.
|
||||||
for (auto& [name, loc] : locations) {
|
for (auto& [name, loc] : gameState.locations) {
|
||||||
loc->dialogueSystem.setOnCutsceneStarted([this]() {
|
loc->dialogueSystem.setOnCutsceneStarted([this]() {
|
||||||
menuManager.onCutsceneStarted();
|
menuManager.onCutsceneStarted();
|
||||||
});
|
});
|
||||||
@ -554,13 +554,13 @@ namespace ZL
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
menuManager.skipCutsceneFunc = [this]() {
|
menuManager.skipCutsceneFunc = [this]() {
|
||||||
if (currentLocation) currentLocation->dialogueSystem.skipCutscene();
|
if (currentLocation()) currentLocation()->dialogueSystem.skipCutscene();
|
||||||
};
|
};
|
||||||
|
|
||||||
std::cout << "Load resurces step 13" << std::endl;
|
std::cout << "Load resurces step 13" << std::endl;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
menuManager.setup(inventory, CONST_ZIP_FILE);
|
menuManager.setup(gameState.inventory, CONST_ZIP_FILE);
|
||||||
std::cout << "UI loaded successfully" << std::endl;
|
std::cout << "UI loaded successfully" << std::endl;
|
||||||
}
|
}
|
||||||
catch (const std::exception& e) {
|
catch (const std::exception& e) {
|
||||||
@ -569,12 +569,12 @@ namespace ZL
|
|||||||
}
|
}
|
||||||
|
|
||||||
menuManager.startGameFunc = [this]() {
|
menuManager.startGameFunc = [this]() {
|
||||||
currentLocation = locations["location_dorm"];
|
gameState.currentLocationName = "location_dorm";
|
||||||
currentLocation->scriptEngine.callLocationEnterCallback();
|
currentLocation()->scriptEngine.callLocationEnterCallback();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Wire HP-change callbacks so all player instances update the health bar HUD.
|
// Wire HP-change callbacks so all player instances update the health bar HUD.
|
||||||
for (auto& [name, loc] : locations) {
|
for (auto& [name, loc] : gameState.locations) {
|
||||||
if (loc->player) {
|
if (loc->player) {
|
||||||
loc->player->state.onHpChanged = [this](float hp, float maxHp) {
|
loc->player->state.onHpChanged = [this](float hp, float maxHp) {
|
||||||
menuManager.updateHealthBar(hp, maxHp);
|
menuManager.updateHealthBar(hp, maxHp);
|
||||||
@ -608,9 +608,9 @@ namespace ZL
|
|||||||
|
|
||||||
if (!menuManager.cutsceneHudActive_)
|
if (!menuManager.cutsceneHudActive_)
|
||||||
menuManager.uiManager.draw(renderer);
|
menuManager.uiManager.draw(renderer);
|
||||||
if (currentLocation)
|
if (currentLocation())
|
||||||
{
|
{
|
||||||
currentLocation->dialogueSystem.draw(renderer);
|
currentLocation()->dialogueSystem.draw(renderer);
|
||||||
}
|
}
|
||||||
glEnable(GL_BLEND);
|
glEnable(GL_BLEND);
|
||||||
menuManager.topUiManager.draw(renderer);
|
menuManager.topUiManager.draw(renderer);
|
||||||
@ -635,30 +635,31 @@ namespace ZL
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (currentLocation)
|
Location* loc = currentLocation();
|
||||||
|
if (loc)
|
||||||
{
|
{
|
||||||
// Sync global flags so Location's draw functions see them.
|
// Sync global flags so Location's draw functions see them.
|
||||||
currentLocation->state.isDarklands = isDarklands;
|
loc->state.isDarklands = gameState.isDarklands;
|
||||||
currentLocation->state.isNight = menuManager.isNight;
|
loc->state.isNight = gameState.isNight;
|
||||||
currentLocation->state.isDawn = menuManager.isDawn;
|
loc->state.isDawn = gameState.isDawn;
|
||||||
|
|
||||||
if (isDarklands) {
|
if (gameState.isDarklands) {
|
||||||
currentLocation->drawGameDarklands();
|
loc->drawGameDarklands();
|
||||||
CheckGlError(__FILE__, __LINE__);
|
CheckGlError(__FILE__, __LINE__);
|
||||||
}
|
}
|
||||||
else if (menuManager.isNight) {
|
else if (gameState.isNight) {
|
||||||
currentLocation->drawGameNight();
|
loc->drawGameNight();
|
||||||
CheckGlError(__FILE__, __LINE__);
|
CheckGlError(__FILE__, __LINE__);
|
||||||
}
|
}
|
||||||
else if (currentLocation->shadowMap) {
|
else if (loc->shadowMap) {
|
||||||
CheckGlError(__FILE__, __LINE__);
|
CheckGlError(__FILE__, __LINE__);
|
||||||
currentLocation->drawShadowDepthPass();
|
loc->drawShadowDepthPass();
|
||||||
CheckGlError(__FILE__, __LINE__);
|
CheckGlError(__FILE__, __LINE__);
|
||||||
currentLocation->drawGameWithShadows();
|
loc->drawGameWithShadows();
|
||||||
CheckGlError(__FILE__, __LINE__);
|
CheckGlError(__FILE__, __LINE__);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
currentLocation->drawGame();
|
loc->drawGame();
|
||||||
CheckGlError(__FILE__, __LINE__);
|
CheckGlError(__FILE__, __LINE__);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -838,9 +839,9 @@ namespace ZL
|
|||||||
if (!menuManager.isMainMenuOpen())
|
if (!menuManager.isMainMenuOpen())
|
||||||
menuManager.update(static_cast<float>(delta));
|
menuManager.update(static_cast<float>(delta));
|
||||||
|
|
||||||
if (currentLocation)
|
if (currentLocation())
|
||||||
{
|
{
|
||||||
currentLocation->update(delta);
|
currentLocation()->update(delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -928,8 +929,8 @@ namespace ZL
|
|||||||
else if (event.button.button == SDL_BUTTON_RIGHT
|
else if (event.button.button == SDL_BUTTON_RIGHT
|
||||||
&& event.type == SDL_MOUSEBUTTONUP
|
&& event.type == SDL_MOUSEBUTTONUP
|
||||||
&& editorMode == EditorMode::Navigation
|
&& editorMode == EditorMode::Navigation
|
||||||
&& currentLocation) {
|
&& currentLocation()) {
|
||||||
currentLocation->editor.handleRightClick();
|
currentLocation()->editor.handleRightClick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (event.type == SDL_MOUSEMOTION) {
|
else if (event.type == SDL_MOUSEMOTION) {
|
||||||
@ -956,7 +957,7 @@ namespace ZL
|
|||||||
}
|
}
|
||||||
std::cout << "Current zoom: " << Environment::zoom << std::endl;
|
std::cout << "Current zoom: " << Environment::zoom << std::endl;
|
||||||
// Tutorial step3 → step4: any mouse-wheel scroll counts as "zoom gesture".
|
// Tutorial step3 → step4: any mouse-wheel scroll counts as "zoom gesture".
|
||||||
if (menuManager.tutorialStep == TutorialStep::Step3) {
|
if (gameState.tutorialStep == TutorialStep::Step3) {
|
||||||
menuManager.advanceTutorialStep();
|
menuManager.advanceTutorialStep();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -964,32 +965,27 @@ namespace ZL
|
|||||||
if (event.type == SDL_KEYDOWN && event.key.repeat == 0) {
|
if (event.type == SDL_KEYDOWN && event.key.repeat == 0) {
|
||||||
switch (event.key.keysym.sym) {
|
switch (event.key.keysym.sym) {
|
||||||
case SDLK_8:
|
case SDLK_8:
|
||||||
if (editorMode == EditorMode::InteractiveObjects && currentLocation) {
|
if (editorMode == EditorMode::InteractiveObjects && currentLocation()) {
|
||||||
currentLocation->editor.selectInteractiveObject(8);
|
currentLocation()->editor.selectInteractiveObject(8);
|
||||||
} else {
|
} else {
|
||||||
menuManager.isDawn = !menuManager.isDawn;
|
gameState.isDawn = !gameState.isDawn;
|
||||||
if (menuManager.isDawn) menuManager.isNight = true;
|
if (gameState.isDawn) gameState.isNight = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SDLK_9:
|
case SDLK_9:
|
||||||
if (editorMode == EditorMode::InteractiveObjects && currentLocation) {
|
if (editorMode == EditorMode::InteractiveObjects && currentLocation()) {
|
||||||
currentLocation->editor.selectInteractiveObject(9);
|
currentLocation()->editor.selectInteractiveObject(9);
|
||||||
} else {
|
} else {
|
||||||
startNightTransition();
|
startNightTransition();
|
||||||
/*if (menuManager.isDawn) {
|
|
||||||
menuManager.isDawn = false; // step back: dawn → plain night
|
|
||||||
} else {
|
|
||||||
menuManager.isNight = !menuManager.isNight;
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SDLK_0:
|
case SDLK_0:
|
||||||
|
|
||||||
if (editorMode == EditorMode::InteractiveObjects && currentLocation) {
|
if (editorMode == EditorMode::InteractiveObjects && currentLocation()) {
|
||||||
currentLocation->editor.selectInteractiveObject(event.key.keysym.sym - SDLK_0);
|
currentLocation()->editor.selectInteractiveObject(event.key.keysym.sym - SDLK_0);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
currentLocation->requestDarklandsTransition();
|
currentLocation()->requestDarklandsTransition();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SDLK_1:
|
case SDLK_1:
|
||||||
@ -1000,20 +996,20 @@ namespace ZL
|
|||||||
case SDLK_6:
|
case SDLK_6:
|
||||||
case SDLK_7:
|
case SDLK_7:
|
||||||
|
|
||||||
if (editorMode == EditorMode::InteractiveObjects && currentLocation) {
|
if (editorMode == EditorMode::InteractiveObjects && currentLocation()) {
|
||||||
currentLocation->editor.selectInteractiveObject(event.key.keysym.sym - SDLK_0);
|
currentLocation()->editor.selectInteractiveObject(event.key.keysym.sym - SDLK_0);
|
||||||
} else {
|
} else {
|
||||||
currentLocation->switchNavigation(event.key.keysym.sym - SDLK_0);
|
currentLocation()->switchNavigation(event.key.keysym.sym - SDLK_0);
|
||||||
std::cout << "Switched to nav mesh " << (event.key.keysym.sym - SDLK_0) << std::endl;
|
std::cout << "Switched to nav mesh " << (event.key.keysym.sym - SDLK_0) << std::endl;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SDLK_f:
|
case SDLK_f:
|
||||||
currentLocation->dialogueSystem.startDialogue("phone_night_aiperi001");
|
currentLocation()->dialogueSystem.startDialogue("phone_night_aiperi001");
|
||||||
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case SDLK_e:
|
case SDLK_e:
|
||||||
currentLocation->dialogueSystem.startCutscene("computer_cutscene001"); //.startDialogue("test_cutscene_pan_dialogue");
|
currentLocation()->dialogueSystem.startCutscene("computer_cutscene001"); //.startDialogue("test_cutscene_pan_dialogue");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
@ -1024,12 +1020,12 @@ namespace ZL
|
|||||||
editorMode = EditorMode::InteractiveObjects;
|
editorMode = EditorMode::InteractiveObjects;
|
||||||
else
|
else
|
||||||
editorMode = EditorMode::None;
|
editorMode = EditorMode::None;
|
||||||
if (currentLocation) {
|
if (currentLocation()) {
|
||||||
currentLocation->editorMode = editorMode;
|
currentLocation()->editorMode = editorMode;
|
||||||
if (editorMode == EditorMode::Navigation)
|
if (editorMode == EditorMode::Navigation)
|
||||||
currentLocation->editor.buildNavMeshes();
|
currentLocation()->editor.buildNavMeshes();
|
||||||
else if (editorMode == EditorMode::InteractiveObjects)
|
else if (editorMode == EditorMode::InteractiveObjects)
|
||||||
currentLocation->editor.buildInteractiveObjectBoundsMeshes();
|
currentLocation()->editor.buildInteractiveObjectBoundsMeshes();
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const char* modeName = (editorMode == EditorMode::Navigation) ? "Navigation"
|
const char* modeName = (editorMode == EditorMode::Navigation) ? "Navigation"
|
||||||
@ -1055,15 +1051,15 @@ namespace ZL
|
|||||||
//std::cout << "current y: " << y << std::endl;
|
//std::cout << "current y: " << y << std::endl;
|
||||||
//y = y - 0.002;
|
//y = y - 0.002;
|
||||||
|
|
||||||
std::cout << "Player pos: " << currentLocation->player->state.position.transpose() << std::endl;
|
std::cout << "Player pos: " << currentLocation()->player->state.position.transpose() << std::endl;
|
||||||
//currentLocation->npcs[0]->walkSpeed -= 0.01f;
|
//currentLocation->npcs[0]->walkSpeed -= 0.01f;
|
||||||
//std::cout << "Walk speed: " << currentLocation->npcs[0]->walkSpeed << std::endl;
|
//std::cout << "Walk speed: " << currentLocation->npcs[0]->walkSpeed << std::endl;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDLK_p:
|
case SDLK_p:
|
||||||
currentLocation = locations["uni_interior"];
|
gameState.currentLocationName = "uni_interior";
|
||||||
currentLocation->player->state.position = Eigen::Vector3f(-0.0189243, 0, -13.4314);
|
currentLocation()->player->state.position = Eigen::Vector3f(-0.0189243, 0, -13.4314);
|
||||||
currentLocation->player->setTarget(currentLocation->player->state.position);
|
currentLocation()->player->setTarget(currentLocation()->player->state.position);
|
||||||
//std::cout << "Switched to location " << ((currentLocation == locations["location1"]) ? "1" : "2") << std::endl;
|
//std::cout << "Switched to location " << ((currentLocation == locations["location1"]) ? "1" : "2") << std::endl;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1071,8 +1067,8 @@ namespace ZL
|
|||||||
//x = x - 1;
|
//x = x - 1;
|
||||||
//std::cout << "current x: " << x << std::endl;
|
//std::cout << "current x: " << x << std::endl;
|
||||||
|
|
||||||
std::cout << "Azimuth: " << currentLocation->state.cameraAzimuth << std::endl;
|
std::cout << "Azimuth: " << currentLocation()->state.cameraAzimuth << std::endl;
|
||||||
std::cout << "Inclination: " << currentLocation->state.cameraInclination << std::endl;
|
std::cout << "Inclination: " << currentLocation()->state.cameraInclination << std::endl;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDLK_c:
|
case SDLK_c:
|
||||||
@ -1086,22 +1082,22 @@ namespace ZL
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case SDLK_b:
|
case SDLK_b:
|
||||||
if (editorMode != EditorMode::None && currentLocation) {
|
if (editorMode != EditorMode::None && currentLocation()) {
|
||||||
currentLocation->editor.saveAll();
|
currentLocation()->editor.saveAll();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDLK_j:
|
case SDLK_j:
|
||||||
if (editorMode != EditorMode::None && currentLocation) {
|
if (editorMode != EditorMode::None && currentLocation()) {
|
||||||
currentLocation->editor.placeTree();
|
currentLocation()->editor.placeTree();
|
||||||
} else {
|
} else {
|
||||||
menuManager.toggleQuestJournal();
|
menuManager.toggleQuestJournal();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDLK_v:
|
case SDLK_v:
|
||||||
if (editorMode != EditorMode::None && currentLocation) {
|
if (editorMode != EditorMode::None && currentLocation()) {
|
||||||
currentLocation->editor.saveObjects();
|
currentLocation()->editor.saveObjects();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1152,13 +1148,13 @@ namespace ZL
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Game::activateSlowMoEffect() {
|
void Game::activateSlowMoEffect() {
|
||||||
if (!currentLocation) return;
|
if (!currentLocation()) return;
|
||||||
|
|
||||||
if (currentLocation->player) {
|
if (currentLocation()->player) {
|
||||||
currentLocation->player->state.slowMoTimeRemaining = currentLocation->player->state.slowMoActiveTime;
|
currentLocation()->player->state.slowMoTimeRemaining = currentLocation()->player->state.slowMoActiveTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& npc : currentLocation->npcs) {
|
for (auto& npc : currentLocation()->npcs) {
|
||||||
if (npc) {
|
if (npc) {
|
||||||
npc->state.slowMoTimeRemaining = npc->state.slowMoActiveTime;
|
npc->state.slowMoTimeRemaining = npc->state.slowMoActiveTime;
|
||||||
}
|
}
|
||||||
@ -1168,25 +1164,25 @@ namespace ZL
|
|||||||
void Game::enterCameraDragMode(int eventX, int eventY)
|
void Game::enterCameraDragMode(int eventX, int eventY)
|
||||||
{
|
{
|
||||||
cameraDragging = true;
|
cameraDragging = true;
|
||||||
if (currentLocation) {
|
if (currentLocation()) {
|
||||||
currentLocation->cameraDragging = true;
|
currentLocation()->cameraDragging = true;
|
||||||
// Anchor on the *current* position, not the original press, so the
|
// Anchor on the *current* position, not the original press, so the
|
||||||
// camera doesn't snap by however far the finger drifted before the
|
// camera doesn't snap by however far the finger drifted before the
|
||||||
// movement threshold was crossed.
|
// movement threshold was crossed.
|
||||||
currentLocation->lastMouseX = eventX;
|
currentLocation()->lastMouseX = eventX;
|
||||||
currentLocation->lastMouseY = eventY;
|
currentLocation()->lastMouseY = eventY;
|
||||||
|
|
||||||
// Snapshot current angles so we can measure how far the user rotates.
|
// Snapshot current angles so we can measure how far the user rotates.
|
||||||
dragStartAzimuth = currentLocation->state.cameraAzimuth;
|
dragStartAzimuth = currentLocation()->state.cameraAzimuth;
|
||||||
dragStartInclination = currentLocation->state.cameraInclination;
|
dragStartInclination = currentLocation()->state.cameraInclination;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Game::exitCameraDragMode()
|
void Game::exitCameraDragMode()
|
||||||
{
|
{
|
||||||
cameraDragging = false;
|
cameraDragging = false;
|
||||||
if (currentLocation) {
|
if (currentLocation()) {
|
||||||
currentLocation->cameraDragging = false;
|
currentLocation()->cameraDragging = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1237,7 +1233,7 @@ namespace ZL
|
|||||||
Environment::zoom = newZoom;
|
Environment::zoom = newZoom;
|
||||||
|
|
||||||
// Tutorial step3 → step4: detect a significant pinch-zoom (≥ 2 zoom units).
|
// Tutorial step3 → step4: detect a significant pinch-zoom (≥ 2 zoom units).
|
||||||
if (menuManager.tutorialStep == TutorialStep::Step3) {
|
if (gameState.tutorialStep == TutorialStep::Step3) {
|
||||||
if (std::abs(Environment::zoom - pinchStartZoom) >= 2.0f) {
|
if (std::abs(Environment::zoom - pinchStartZoom) >= 2.0f) {
|
||||||
menuManager.advanceTutorialStep();
|
menuManager.advanceTutorialStep();
|
||||||
}
|
}
|
||||||
@ -1263,7 +1259,7 @@ namespace ZL
|
|||||||
const int uiY = Environment::projectionHeight - my;
|
const int uiY = Environment::projectionHeight - my;
|
||||||
menuManager.topUiManager.onTouchDown(fingerId, uiX, uiY);
|
menuManager.topUiManager.onTouchDown(fingerId, uiX, uiY);
|
||||||
const bool capturedByTopUi = menuManager.topUiManager.isUiInteractionForFinger(fingerId);
|
const bool capturedByTopUi = menuManager.topUiManager.isUiInteractionForFinger(fingerId);
|
||||||
const bool dialogueActive = currentLocation && currentLocation->dialogueSystem.isActive();
|
const bool dialogueActive = currentLocation() && currentLocation()->dialogueSystem.isActive();
|
||||||
if (!dialogueActive && !capturedByTopUi) {
|
if (!dialogueActive && !capturedByTopUi) {
|
||||||
menuManager.uiManager.onTouchDown(fingerId, uiX, uiY);
|
menuManager.uiManager.onTouchDown(fingerId, uiX, uiY);
|
||||||
}
|
}
|
||||||
@ -1299,7 +1295,7 @@ namespace ZL
|
|||||||
const int uiX = mx;
|
const int uiX = mx;
|
||||||
const int uiY = Environment::projectionHeight - my;
|
const int uiY = Environment::projectionHeight - my;
|
||||||
menuManager.topUiManager.onTouchUp(fingerId, uiX, uiY);
|
menuManager.topUiManager.onTouchUp(fingerId, uiX, uiY);
|
||||||
const bool dialogueActive = currentLocation && currentLocation->dialogueSystem.isActive();
|
const bool dialogueActive = currentLocation() && currentLocation()->dialogueSystem.isActive();
|
||||||
if (!dialogueActive) {
|
if (!dialogueActive) {
|
||||||
menuManager.uiManager.onTouchUp(fingerId, uiX, uiY);
|
menuManager.uiManager.onTouchUp(fingerId, uiX, uiY);
|
||||||
}
|
}
|
||||||
@ -1334,8 +1330,8 @@ namespace ZL
|
|||||||
|
|
||||||
// Tap → walk-to / interact, using the original press coords so the target
|
// Tap → walk-to / interact, using the original press coords so the target
|
||||||
// isn't shifted by tiny finger drift before release.
|
// isn't shifted by tiny finger drift before release.
|
||||||
if (currentLocation) {
|
if (currentLocation()) {
|
||||||
currentLocation->handleDown(fingerId, st.downEventX, st.downEventY, st.downMx, st.downMy);
|
currentLocation()->handleDown(fingerId, st.downEventX, st.downEventY, st.downMx, st.downMy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1344,7 +1340,7 @@ namespace ZL
|
|||||||
const int uiX = mx;
|
const int uiX = mx;
|
||||||
const int uiY = Environment::projectionHeight - my;
|
const int uiY = Environment::projectionHeight - my;
|
||||||
menuManager.topUiManager.onTouchMove(fingerId, uiX, uiY);
|
menuManager.topUiManager.onTouchMove(fingerId, uiX, uiY);
|
||||||
const bool dialogueActive = currentLocation && currentLocation->dialogueSystem.isActive();
|
const bool dialogueActive = currentLocation() && currentLocation()->dialogueSystem.isActive();
|
||||||
if (!dialogueActive) {
|
if (!dialogueActive) {
|
||||||
menuManager.uiManager.onTouchMove(fingerId, uiX, uiY);
|
menuManager.uiManager.onTouchMove(fingerId, uiX, uiY);
|
||||||
}
|
}
|
||||||
@ -1373,18 +1369,18 @@ namespace ZL
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentLocation) {
|
if (currentLocation()) {
|
||||||
// Forwarded for dialogue hover and (when cameraDragging) camera rotation.
|
// Forwarded for dialogue hover and (when cameraDragging) camera rotation.
|
||||||
currentLocation->handleMotion(fingerId, eventX, eventY, mx, my);
|
currentLocation()->handleMotion(fingerId, eventX, eventY, mx, my);
|
||||||
|
|
||||||
// Tutorial step1 → step2: detect a significant camera rotation on BOTH axes.
|
// Tutorial step1 → step2: detect a significant camera rotation on BOTH axes.
|
||||||
// ~0.15 rad (≈8.6°) per axis ensures the user intentionally panned in 2D,
|
// ~0.15 rad (≈8.6°) per axis ensures the user intentionally panned in 2D,
|
||||||
// not just nudged a single axis by accident.
|
// not just nudged a single axis by accident.
|
||||||
static constexpr float TUTORIAL_ROTATION_THRESHOLD = 0.15f;
|
static constexpr float TUTORIAL_ROTATION_THRESHOLD = 0.15f;
|
||||||
if (cameraDragging
|
if (cameraDragging
|
||||||
&& menuManager.tutorialStep == TutorialStep::Step1) {
|
&& gameState.tutorialStep == TutorialStep::Step1) {
|
||||||
float deltaAz = std::abs(currentLocation->state.cameraAzimuth - dragStartAzimuth);
|
float deltaAz = std::abs(currentLocation()->state.cameraAzimuth - dragStartAzimuth);
|
||||||
float deltaInc = std::abs(currentLocation->state.cameraInclination - dragStartInclination);
|
float deltaInc = std::abs(currentLocation()->state.cameraInclination - dragStartInclination);
|
||||||
if (deltaAz >= TUTORIAL_ROTATION_THRESHOLD && deltaInc >= TUTORIAL_ROTATION_THRESHOLD) {
|
if (deltaAz >= TUTORIAL_ROTATION_THRESHOLD && deltaInc >= TUTORIAL_ROTATION_THRESHOLD) {
|
||||||
menuManager.advanceTutorialStep();
|
menuManager.advanceTutorialStep();
|
||||||
}
|
}
|
||||||
@ -1394,15 +1390,15 @@ namespace ZL
|
|||||||
|
|
||||||
bool Game::startDarklandsTransition()
|
bool Game::startDarklandsTransition()
|
||||||
{
|
{
|
||||||
if (!menuManager.isNight)
|
if (!gameState.isNight)
|
||||||
{
|
{
|
||||||
currentLocation->dialogueSystem.startDialogue("darklands_day_dialog001");
|
currentLocation()->dialogueSystem.startDialogue("darklands_day_dialog001");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (menuManager.isDawn)
|
if (gameState.isDawn)
|
||||||
{
|
{
|
||||||
currentLocation->dialogueSystem.startDialogue("darklands_morning_dialog001");
|
currentLocation()->dialogueSystem.startDialogue("darklands_morning_dialog001");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1434,25 +1430,24 @@ namespace ZL
|
|||||||
darklandsFlashAlpha = min(darklandsFlashAlpha + step, 1.0f);
|
darklandsFlashAlpha = min(darklandsFlashAlpha + step, 1.0f);
|
||||||
if (darklandsFlashAlpha >= 1.0f) {
|
if (darklandsFlashAlpha >= 1.0f) {
|
||||||
if (isNightTransition) {
|
if (isNightTransition) {
|
||||||
menuManager.isNight = !menuManager.isNight;
|
gameState.isNight = !gameState.isNight;
|
||||||
if (currentLocation)
|
if (currentLocation())
|
||||||
{
|
{
|
||||||
//currentLocation->dialogueSystem.startDialogue("dialog_video001");
|
currentLocation()->state.isNight = gameState.isNight;
|
||||||
currentLocation->state.isNight = menuManager.isNight;
|
currentLocation()->scriptEngine.callTriggerNightEnterCallback();
|
||||||
currentLocation->scriptEngine.callTriggerNightEnterCallback();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
isDarklands = !isDarklands;
|
gameState.isDarklands = !gameState.isDarklands;
|
||||||
|
|
||||||
// Change HUD
|
// Change HUD
|
||||||
menuManager.setDarklandsMode(isDarklands);
|
menuManager.setDarklandsMode(gameState.isDarklands);
|
||||||
|
|
||||||
if (currentLocation) {
|
if (currentLocation()) {
|
||||||
currentLocation->state.isDarklands = isDarklands;
|
currentLocation()->state.isDarklands = gameState.isDarklands;
|
||||||
if (isDarklands)
|
if (gameState.isDarklands)
|
||||||
currentLocation->scriptEngine.callDarklandsEnterCallback();
|
currentLocation()->scriptEngine.callDarklandsEnterCallback();
|
||||||
else
|
else
|
||||||
currentLocation->scriptEngine.callDarklandsExitCallback();
|
currentLocation()->scriptEngine.callDarklandsExitCallback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
darklandsFlashFadingIn = false;
|
darklandsFlashFadingIn = false;
|
||||||
|
|||||||
14
src/Game.h
14
src/Game.h
@ -20,6 +20,7 @@
|
|||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include "Location.h"
|
#include "Location.h"
|
||||||
#include "AudioPlayerAsync.h"
|
#include "AudioPlayerAsync.h"
|
||||||
|
#include "GameState.h"
|
||||||
namespace ZL {
|
namespace ZL {
|
||||||
|
|
||||||
class Game {
|
class Game {
|
||||||
@ -42,23 +43,14 @@ namespace ZL {
|
|||||||
VertexRenderStruct loadingMesh;
|
VertexRenderStruct loadingMesh;
|
||||||
bool loadingCompleted = false;
|
bool loadingCompleted = false;
|
||||||
|
|
||||||
std::unordered_map<std::string, std::shared_ptr<Location>> locations;
|
GameState gameState;
|
||||||
std::shared_ptr<Location> currentLocation;
|
|
||||||
|
|
||||||
EditorMode editorMode = EditorMode::None;
|
EditorMode editorMode = EditorMode::None;
|
||||||
|
|
||||||
// Global darklands state — persists across location transitions.
|
|
||||||
bool isDarklands = false;
|
|
||||||
// Returns false if a transition is already in progress.
|
// Returns false if a transition is already in progress.
|
||||||
bool startDarklandsTransition();
|
bool startDarklandsTransition();
|
||||||
bool startNightTransition();
|
bool startNightTransition();
|
||||||
|
|
||||||
Inventory inventory;
|
|
||||||
InteractiveObject* pickedUpObject = nullptr;
|
|
||||||
|
|
||||||
std::unordered_map<std::string, int> globalInts;
|
|
||||||
std::unordered_map<std::string, float> globalFloats;
|
|
||||||
|
|
||||||
MenuManager menuManager;
|
MenuManager menuManager;
|
||||||
|
|
||||||
void activateSlowMoEffect();
|
void activateSlowMoEffect();
|
||||||
@ -95,6 +87,8 @@ namespace ZL {
|
|||||||
|
|
||||||
std::unique_ptr<AudioPlayerAsync> audioPlayer;
|
std::unique_ptr<AudioPlayerAsync> audioPlayer;
|
||||||
|
|
||||||
|
Location* currentLocation() const;
|
||||||
|
|
||||||
int64_t getSyncTimeMs();
|
int64_t getSyncTimeMs();
|
||||||
void processTickCount();
|
void processTickCount();
|
||||||
void drawScene();
|
void drawScene();
|
||||||
|
|||||||
1
src/GameState.cpp
Normal file
1
src/GameState.cpp
Normal file
@ -0,0 +1 @@
|
|||||||
|
#include "GameState.h"
|
||||||
71
src/GameState.h
Normal file
71
src/GameState.h
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "items/Item.h"
|
||||||
|
#include "quest/QuestJournal.h"
|
||||||
|
#include "Location.h"
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace ZL {
|
||||||
|
|
||||||
|
enum class TutorialStep {
|
||||||
|
Step0, // Dialogue hint: "click to advance"
|
||||||
|
Step1, // Camera rotation hint
|
||||||
|
Step2, // Floor tap / walk hint
|
||||||
|
Step3, // Pinch-zoom hint
|
||||||
|
Step4, // Pick-up item hint
|
||||||
|
Step5, // Post-pickup reaction
|
||||||
|
Step6, // Tutorial complete
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class UniIntTutorialState {
|
||||||
|
Step10,
|
||||||
|
Step11,
|
||||||
|
DarklandsActive,
|
||||||
|
DarklandsStep13,
|
||||||
|
DarklandsFull
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StoredChatMessage {
|
||||||
|
std::string text;
|
||||||
|
bool incoming;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GameState {
|
||||||
|
// --- World ---
|
||||||
|
std::unordered_map<std::string, std::shared_ptr<Location>> locations;
|
||||||
|
std::string currentLocationName;
|
||||||
|
bool isDarklands = false;
|
||||||
|
bool isNight = false;
|
||||||
|
bool isDawn = false;
|
||||||
|
|
||||||
|
// --- Player resources ---
|
||||||
|
Inventory inventory;
|
||||||
|
float playerHp = 200.f;
|
||||||
|
float playerMaxHp = 200.f;
|
||||||
|
int money = 5500;
|
||||||
|
|
||||||
|
// --- Script globals ---
|
||||||
|
std::unordered_map<std::string, int> globalInts;
|
||||||
|
std::unordered_map<std::string, float> globalFloats;
|
||||||
|
|
||||||
|
// --- Quest system ---
|
||||||
|
Quest::QuestJournal questJournal;
|
||||||
|
|
||||||
|
// --- Tutorial ---
|
||||||
|
TutorialStep tutorialStep = TutorialStep::Step0;
|
||||||
|
UniIntTutorialState uniIntTutorialState = UniIntTutorialState::Step10;
|
||||||
|
bool tutorialPhonePickedUp = false;
|
||||||
|
bool tutorialJournalPickedUp = false;
|
||||||
|
bool tutorialPhoneChatScreenOpened = false;
|
||||||
|
bool tutorialJournalScreenOpened = false;
|
||||||
|
bool tutorialNeedOpenTaxiScreen = false;
|
||||||
|
|
||||||
|
// --- Phone / chat ---
|
||||||
|
bool chatUnread[3] = { true, true, true };
|
||||||
|
std::string chatPreviewMsg[3];
|
||||||
|
std::vector<StoredChatMessage> chatHistory[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ZL
|
||||||
@ -1869,6 +1869,11 @@ namespace ZL
|
|||||||
}
|
}
|
||||||
out["interactiveObjectStates"] = std::move(ioArr);
|
out["interactiveObjectStates"] = std::move(ioArr);
|
||||||
|
|
||||||
|
// Dialogue runtime state
|
||||||
|
nlohmann::json dialogueState;
|
||||||
|
dialogueSystem.saveDialogueState(dialogueState);
|
||||||
|
out["dialogueState"] = std::move(dialogueState);
|
||||||
|
|
||||||
// Lua global variable state
|
// Lua global variable state
|
||||||
nlohmann::json scriptState;
|
nlohmann::json scriptState;
|
||||||
scriptEngine.saveScriptGlobals(scriptState);
|
scriptEngine.saveScriptGlobals(scriptState);
|
||||||
@ -1919,6 +1924,11 @@ namespace ZL
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dialogue runtime state
|
||||||
|
if (in.contains("dialogueState")) {
|
||||||
|
dialogueSystem.loadDialogueState(in["dialogueState"]);
|
||||||
|
}
|
||||||
|
|
||||||
// Lua globals
|
// Lua globals
|
||||||
if (in.contains("scriptGlobals")) {
|
if (in.contains("scriptGlobals")) {
|
||||||
scriptEngine.loadScriptGlobals(in["scriptGlobals"]);
|
scriptEngine.loadScriptGlobals(in["scriptGlobals"]);
|
||||||
|
|||||||
@ -91,9 +91,9 @@ namespace ZL {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MenuManager::MenuManager(Renderer& iRenderer, std::unordered_map<std::string, int>& globalInts) :
|
MenuManager::MenuManager(Renderer& iRenderer, GameState& gameState) :
|
||||||
renderer(iRenderer),
|
renderer(iRenderer),
|
||||||
globalInts(globalInts)
|
gameState_(gameState)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,30 +147,25 @@ namespace ZL {
|
|||||||
texItemSelected_ = renderer.textureManager.LoadFromPng("resources/w/ui/img/journal/ButtonBkg001.png", zipFile, true);
|
texItemSelected_ = renderer.textureManager.LoadFromPng("resources/w/ui/img/journal/ButtonBkg001.png", zipFile, true);
|
||||||
texItemTransparent_ = renderer.textureManager.LoadFromPng("resources/w/ui/img/journal/ButtonBkgTransparent001.png", zipFile, true);
|
texItemTransparent_ = renderer.textureManager.LoadFromPng("resources/w/ui/img/journal/ButtonBkgTransparent001.png", zipFile, true);
|
||||||
|
|
||||||
questJournal.loadFromFile("resources/quests/quests.json", zipFile);
|
gameState_.questJournal.loadFromFile("resources/quests/quests.json", zipFile);
|
||||||
|
|
||||||
renderer.textureManager.LoadFromPng("resources/w/ui/img/toast/quest_new001.png", zipFile, true);
|
renderer.textureManager.LoadFromPng("resources/w/ui/img/toast/quest_new001.png", zipFile, true);
|
||||||
renderer.textureManager.LoadFromPng("resources/w/ui/img/toast/quest_completed001.png", zipFile, true);
|
renderer.textureManager.LoadFromPng("resources/w/ui/img/toast/quest_completed001.png", zipFile, true);
|
||||||
renderer.textureManager.LoadFromPng("resources/w/ui/img/toast/quest_failed001.png", zipFile, true);
|
renderer.textureManager.LoadFromPng("resources/w/ui/img/toast/quest_failed001.png", zipFile, true);
|
||||||
|
|
||||||
|
|
||||||
questJournal.onQuestUnlocked = [this](const std::string& id) {
|
gameState_.questJournal.onQuestUnlocked = [this](const std::string& id) {
|
||||||
const Quest::QuestState* q = questJournal.findQuest(id);
|
const Quest::QuestState* q = gameState_.questJournal.findQuest(id);
|
||||||
if (q) showToast("resources/w/ui/img/toast/quest_new001.png", q->definition.title);
|
if (q) showToast("resources/w/ui/img/toast/quest_new001.png", q->definition.title);
|
||||||
};
|
};
|
||||||
questJournal.onQuestCompleted = [this](const std::string& id) {
|
gameState_.questJournal.onQuestCompleted = [this](const std::string& id) {
|
||||||
const Quest::QuestState* q = questJournal.findQuest(id);
|
const Quest::QuestState* q = gameState_.questJournal.findQuest(id);
|
||||||
if (q) showToast("resources/w/ui/img/toast/quest_completed001.png", q->definition.title);
|
if (q) showToast("resources/w/ui/img/toast/quest_completed001.png", q->definition.title);
|
||||||
};
|
};
|
||||||
questJournal.onQuestFailed = [this](const std::string& id) {
|
gameState_.questJournal.onQuestFailed = [this](const std::string& id) {
|
||||||
const Quest::QuestState* q = questJournal.findQuest(id);
|
const Quest::QuestState* q = gameState_.questJournal.findQuest(id);
|
||||||
if (q) showToast("resources/w/ui/img/toast/quest_failed001.png", q->definition.title);
|
if (q) showToast("resources/w/ui/img/toast/quest_failed001.png", q->definition.title);
|
||||||
};
|
};
|
||||||
/*
|
|
||||||
questJournal.onObjectiveCompleted = [this](const std::string& id, const std::string&) {
|
|
||||||
const Quest::QuestState* q = questJournal.findQuest(id);
|
|
||||||
if (q) showToast("resources/w/ui/img/toast/objective_completed001.png", q->definition.title);
|
|
||||||
};*/
|
|
||||||
|
|
||||||
const std::string imgDir = "resources/w/ui/img/phone/";
|
const std::string imgDir = "resources/w/ui/img/phone/";
|
||||||
texBubbleInCenter_ = renderer.textureManager.LoadFromPng(imgDir + "bubble_in_center.png", zipFile, true);
|
texBubbleInCenter_ = renderer.textureManager.LoadFromPng(imgDir + "bubble_in_center.png", zipFile, true);
|
||||||
@ -188,30 +183,20 @@ namespace ZL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MenuManager::enterGameplay() {
|
void MenuManager::enterGameplay() {
|
||||||
if (state == GameState::MainMenu && startGameFunc) startGameFunc();
|
if (uiState_ == GameUiState::MainMenu && startGameFunc) startGameFunc();
|
||||||
state = GameState::Gameplay;
|
uiState_ = GameUiState::Gameplay;
|
||||||
uiManager.replaceRoot(hudRoot);
|
uiManager.replaceRoot(hudRoot);
|
||||||
topUiManager.replaceRoot(hudTopHintRoot_);
|
topUiManager.replaceRoot(hudTopHintRoot_);
|
||||||
|
|
||||||
|
|
||||||
applyCurrentHealthBar();
|
applyCurrentHealthBar();
|
||||||
/*
|
|
||||||
uiManager.setTextButtonCallback("inventory_button", [this](const std::string&) {
|
|
||||||
openInventory();
|
|
||||||
});
|
|
||||||
|
|
||||||
uiManager.setTextButtonCallback("quest_journal_button", [this](const std::string&) {
|
|
||||||
openQuestJournal();
|
|
||||||
});*/
|
|
||||||
|
|
||||||
uiManager.setButtonCallback("inventoryButton", [this](const std::string&) {
|
uiManager.setButtonCallback("inventoryButton", [this](const std::string&) {
|
||||||
openInventory();
|
openInventory();
|
||||||
});
|
});
|
||||||
//openInventory()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuManager::showMainMenu() {
|
void MenuManager::showMainMenu() {
|
||||||
state = GameState::MainMenu;
|
uiState_ = GameUiState::MainMenu;
|
||||||
uiManager.replaceRoot(mainMenuRoot);
|
uiManager.replaceRoot(mainMenuRoot);
|
||||||
|
|
||||||
uiManager.setTextButtonCallback("menuStartButton", [this](const std::string&) {
|
uiManager.setTextButtonCallback("menuStartButton", [this](const std::string&) {
|
||||||
@ -239,7 +224,7 @@ namespace ZL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MenuManager::openInventory() {
|
void MenuManager::openInventory() {
|
||||||
state = GameState::Inventory;
|
uiState_ = GameUiState::Inventory;
|
||||||
uiManager.pushMenuFromSavedRoot(newInventoryRoot);
|
uiManager.pushMenuFromSavedRoot(newInventoryRoot);
|
||||||
uiManager.setButtonCallback("inventoryExitButton", [this](const std::string&) {
|
uiManager.setButtonCallback("inventoryExitButton", [this](const std::string&) {
|
||||||
closeInventory();
|
closeInventory();
|
||||||
@ -284,53 +269,53 @@ namespace ZL {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuManager::selectInventoryItem(int index) {
|
void MenuManager::selectInventoryItem(int index) {
|
||||||
const auto& items = inventory->getItems();
|
const auto& items = inventory->getItems();
|
||||||
if (index < 0 || index >= static_cast<int>(items.size())) return;
|
if (index < 0 || index >= static_cast<int>(items.size())) return;
|
||||||
|
|
||||||
// Revert previously selected button to its regular icon
|
// Revert previously selected button to its regular icon
|
||||||
if (inventorySelectedIndex_ >= 0 && inventorySelectedIndex_ < static_cast<int>(items.size())) {
|
if (inventorySelectedIndex_ >= 0 && inventorySelectedIndex_ < static_cast<int>(items.size())) {
|
||||||
const std::string prevBtnName = "item" + std::to_string(inventorySelectedIndex_ + 1) + "Button";
|
const std::string prevBtnName = "item" + std::to_string(inventorySelectedIndex_ + 1) + "Button";
|
||||||
auto prevBtn = uiManager.findButton(prevBtnName);
|
auto prevBtn = uiManager.findButton(prevBtnName);
|
||||||
if (prevBtn) {
|
if (prevBtn) {
|
||||||
auto tex = renderer.textureManager.LoadFromPng(items[inventorySelectedIndex_].icon, CONST_ZIP_FILE, true);
|
auto tex = renderer.textureManager.LoadFromPng(items[inventorySelectedIndex_].icon, CONST_ZIP_FILE, true);
|
||||||
prevBtn->texNormal = prevBtn->texHover = prevBtn->texPressed = tex;
|
prevBtn->texNormal = prevBtn->texHover = prevBtn->texPressed = tex;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inventorySelectedIndex_ = index;
|
|
||||||
const auto& item = items[index];
|
|
||||||
|
|
||||||
// Highlight newly selected button with its selected icon
|
|
||||||
const std::string btnName = "item" + std::to_string(index + 1) + "Button";
|
|
||||||
auto btn = uiManager.findButton(btnName);
|
|
||||||
if (btn) {
|
|
||||||
const std::string& selPath = item.selectedIcon.empty() ? item.icon : item.selectedIcon;
|
|
||||||
auto tex = renderer.textureManager.LoadFromPng(selPath, CONST_ZIP_FILE, true);
|
|
||||||
btn->texNormal = btn->texHover = btn->texPressed = tex;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the large selected picture on the right panel
|
|
||||||
auto img = uiManager.findStaticImage("selectedItemPic");
|
|
||||||
if (img) {
|
|
||||||
const std::string& path = item.selectedIcon.empty() ? item.icon : item.selectedIcon;
|
|
||||||
img->texture = renderer.textureManager.LoadFromPng(path, CONST_ZIP_FILE, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
uiManager.setText("selectedText", item.name);
|
|
||||||
uiManager.setText("selectedDescription", item.description);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inventorySelectedIndex_ = index;
|
||||||
|
const auto& item = items[index];
|
||||||
|
|
||||||
|
// Highlight newly selected button with its selected icon
|
||||||
|
const std::string btnName = "item" + std::to_string(index + 1) + "Button";
|
||||||
|
auto btn = uiManager.findButton(btnName);
|
||||||
|
if (btn) {
|
||||||
|
const std::string& selPath = item.selectedIcon.empty() ? item.icon : item.selectedIcon;
|
||||||
|
auto tex = renderer.textureManager.LoadFromPng(selPath, CONST_ZIP_FILE, true);
|
||||||
|
btn->texNormal = btn->texHover = btn->texPressed = tex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the large selected picture on the right panel
|
||||||
|
auto img = uiManager.findStaticImage("selectedItemPic");
|
||||||
|
if (img) {
|
||||||
|
const std::string& path = item.selectedIcon.empty() ? item.icon : item.selectedIcon;
|
||||||
|
img->texture = renderer.textureManager.LoadFromPng(path, CONST_ZIP_FILE, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
uiManager.setText("selectedText", item.name);
|
||||||
|
uiManager.setText("selectedDescription", item.description);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void MenuManager::closeInventory() {
|
void MenuManager::closeInventory() {
|
||||||
state = GameState::Gameplay;
|
uiState_ = GameUiState::Gameplay;
|
||||||
uiManager.popMenu();
|
uiManager.popMenu();
|
||||||
uiManager.updateAllLayouts();
|
uiManager.updateAllLayouts();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuManager::openQuestJournal() {
|
void MenuManager::openQuestJournal() {
|
||||||
state = GameState::QuestJournal;
|
uiState_ = GameUiState::QuestJournal;
|
||||||
tutorialJournalScreenOpened = true;
|
gameState_.tutorialJournalScreenOpened = true;
|
||||||
|
|
||||||
uiManager.setNodeVisible("hint6b", false);
|
uiManager.setNodeVisible("hint6b", false);
|
||||||
uiManager.setNodeVisible("hint6barrow", false);
|
uiManager.setNodeVisible("hint6barrow", false);
|
||||||
@ -358,7 +343,7 @@ namespace ZL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MenuManager::closeQuestJournal() {
|
void MenuManager::closeQuestJournal() {
|
||||||
state = GameState::Gameplay;
|
uiState_ = GameUiState::Gameplay;
|
||||||
selectedQuestIndex = -1;
|
selectedQuestIndex = -1;
|
||||||
visibleQuestIds.clear();
|
visibleQuestIds.clear();
|
||||||
uiManager.popMenu();
|
uiManager.popMenu();
|
||||||
@ -367,11 +352,11 @@ namespace ZL {
|
|||||||
|
|
||||||
void MenuManager::toggleQuestJournal() {
|
void MenuManager::toggleQuestJournal() {
|
||||||
std::cout << "[quest] toggleQuestJournal: " << (isQuestJournalOpen() ? "closing" : "opening") << std::endl;
|
std::cout << "[quest] toggleQuestJournal: " << (isQuestJournalOpen() ? "closing" : "opening") << std::endl;
|
||||||
if (state == GameState::QuestJournal) {
|
if (uiState_ == GameUiState::QuestJournal) {
|
||||||
closeQuestJournal();
|
closeQuestJournal();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (state == GameState::Inventory) {
|
if (uiState_ == GameUiState::Inventory) {
|
||||||
closeInventory();
|
closeInventory();
|
||||||
}
|
}
|
||||||
openQuestJournal();
|
openQuestJournal();
|
||||||
@ -379,23 +364,17 @@ namespace ZL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MenuManager::openPhoneScreen() {
|
void MenuManager::openPhoneScreen() {
|
||||||
state = GameState::PhoneScreen;
|
uiState_ = GameUiState::PhoneScreen;
|
||||||
|
|
||||||
//uiManager.setNodeVisible("hint6a", false);
|
if (gameState_.tutorialNeedOpenTaxiScreen && (gameState_.tutorialPhoneChatScreenOpened == false))
|
||||||
//uiManager.setNodeVisible("hint6aarrow", false);
|
|
||||||
|
|
||||||
//tutorialPhoneScreenOpened = true;
|
|
||||||
|
|
||||||
|
|
||||||
if (tutorialNeedOpenTaxiScreen && (tutorialPhoneChatScreenOpened == false))
|
|
||||||
{
|
{
|
||||||
uiManager.pushMenuFromSavedRoot(phoneMainHintABRoot);
|
uiManager.pushMenuFromSavedRoot(phoneMainHintABRoot);
|
||||||
}
|
}
|
||||||
else if (tutorialNeedOpenTaxiScreen)
|
else if (gameState_.tutorialNeedOpenTaxiScreen)
|
||||||
{
|
{
|
||||||
uiManager.pushMenuFromSavedRoot(phoneMainHintBRoot);
|
uiManager.pushMenuFromSavedRoot(phoneMainHintBRoot);
|
||||||
}
|
}
|
||||||
else if (tutorialPhoneChatScreenOpened == false)
|
else if (gameState_.tutorialPhoneChatScreenOpened == false)
|
||||||
{
|
{
|
||||||
uiManager.pushMenuFromSavedRoot(phoneMainHintARoot);
|
uiManager.pushMenuFromSavedRoot(phoneMainHintARoot);
|
||||||
}
|
}
|
||||||
@ -420,9 +399,9 @@ namespace ZL {
|
|||||||
openPhoneTaxi();
|
openPhoneTaxi();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isNight)
|
if (gameState_.isNight)
|
||||||
{
|
{
|
||||||
if (isDawn)
|
if (gameState_.isDawn)
|
||||||
{
|
{
|
||||||
uiManager.setNodeVisible("phoneTimeDay", false);
|
uiManager.setNodeVisible("phoneTimeDay", false);
|
||||||
uiManager.setNodeVisible("phoneTimeNight", false);
|
uiManager.setNodeVisible("phoneTimeNight", false);
|
||||||
@ -445,7 +424,7 @@ namespace ZL {
|
|||||||
|
|
||||||
void MenuManager::openPhoneMessenger() {
|
void MenuManager::openPhoneMessenger() {
|
||||||
|
|
||||||
if (tutorialPhoneChatScreenOpened)
|
if (gameState_.tutorialPhoneChatScreenOpened)
|
||||||
{
|
{
|
||||||
uiManager.pushMenuFromSavedRoot(phoneChatListRoot);
|
uiManager.pushMenuFromSavedRoot(phoneChatListRoot);
|
||||||
}
|
}
|
||||||
@ -453,7 +432,6 @@ namespace ZL {
|
|||||||
{
|
{
|
||||||
uiManager.pushMenuFromSavedRoot(phoneChatListHintRoot);
|
uiManager.pushMenuFromSavedRoot(phoneChatListHintRoot);
|
||||||
}
|
}
|
||||||
//uiManager.pushMenuFromSavedRoot(phoneChatListRoot);
|
|
||||||
refreshChatUnreadIndicators();
|
refreshChatUnreadIndicators();
|
||||||
|
|
||||||
uiManager.setButtonCallback("phoneExitButton", [this](const std::string&) {
|
uiManager.setButtonCallback("phoneExitButton", [this](const std::string&) {
|
||||||
@ -461,15 +439,15 @@ namespace ZL {
|
|||||||
});
|
});
|
||||||
uiManager.setButtonCallback("phoneMain", [this](const std::string&) {});
|
uiManager.setButtonCallback("phoneMain", [this](const std::string&) {});
|
||||||
uiManager.setTextButtonCallback("chat1button", [this](const std::string&) {
|
uiManager.setTextButtonCallback("chat1button", [this](const std::string&) {
|
||||||
chatUnread_[0] = false;
|
gameState_.chatUnread[0] = false;
|
||||||
openPhoneChatFromList(0, phoneChat1Root);
|
openPhoneChatFromList(0, phoneChat1Root);
|
||||||
});
|
});
|
||||||
uiManager.setTextButtonCallback("chat2button", [this](const std::string&) {
|
uiManager.setTextButtonCallback("chat2button", [this](const std::string&) {
|
||||||
chatUnread_[1] = false;
|
gameState_.chatUnread[1] = false;
|
||||||
openPhoneChatFromList(1, phoneChat2Root);
|
openPhoneChatFromList(1, phoneChat2Root);
|
||||||
});
|
});
|
||||||
uiManager.setTextButtonCallback("chat3button", [this](const std::string&) {
|
uiManager.setTextButtonCallback("chat3button", [this](const std::string&) {
|
||||||
chatUnread_[2] = false;
|
gameState_.chatUnread[2] = false;
|
||||||
openPhoneChatFromList(2, phoneChat3Root);
|
openPhoneChatFromList(2, phoneChat3Root);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -478,30 +456,30 @@ namespace ZL {
|
|||||||
static const char* kUnreadNodes[3] = { "chat1Unread", "chat2Unread", "chat3Unread" };
|
static const char* kUnreadNodes[3] = { "chat1Unread", "chat2Unread", "chat3Unread" };
|
||||||
static const char* kMsgNodes[3] = { "chat1msg", "chat2msg", "chat3msg" };
|
static const char* kMsgNodes[3] = { "chat1msg", "chat2msg", "chat3msg" };
|
||||||
for (int i = 0; i < 3; ++i) {
|
for (int i = 0; i < 3; ++i) {
|
||||||
uiManager.setNodeVisible(kUnreadNodes[i], chatUnread_[i]);
|
uiManager.setNodeVisible(kUnreadNodes[i], gameState_.chatUnread[i]);
|
||||||
if (!chatPreviewMsg_[i].empty())
|
if (!gameState_.chatPreviewMsg[i].empty())
|
||||||
uiManager.setText(kMsgNodes[i], chatPreviewMsg_[i]);
|
uiManager.setText(kMsgNodes[i], gameState_.chatPreviewMsg[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuManager::setChatUnread(int chatIndex, bool unread, const std::string& previewMsg) {
|
void MenuManager::setChatUnread(int chatIndex, bool unread, const std::string& previewMsg) {
|
||||||
if (chatIndex < 0 || chatIndex > 2) return;
|
if (chatIndex < 0 || chatIndex > 2) return;
|
||||||
chatUnread_[chatIndex] = unread;
|
gameState_.chatUnread[chatIndex] = unread;
|
||||||
if (!previewMsg.empty()) {
|
if (!previewMsg.empty()) {
|
||||||
chatPreviewMsg_[chatIndex] = trimChatPreview(previewMsg);
|
gameState_.chatPreviewMsg[chatIndex] = trimChatPreview(previewMsg);
|
||||||
} else if (!unread && !chatHistory_[chatIndex].empty()) {
|
} else if (!unread && !gameState_.chatHistory[chatIndex].empty()) {
|
||||||
chatPreviewMsg_[chatIndex] = trimChatPreview(chatHistory_[chatIndex].back().text);
|
gameState_.chatPreviewMsg[chatIndex] = trimChatPreview(gameState_.chatHistory[chatIndex].back().text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuManager::spendMoney(int amount) {
|
void MenuManager::spendMoney(int amount) {
|
||||||
money_ -= amount;
|
gameState_.money -= amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void MenuManager::openPhoneBank() {
|
void MenuManager::openPhoneBank() {
|
||||||
uiManager.pushMenuFromSavedRoot(phoneBankRoot);
|
uiManager.pushMenuFromSavedRoot(phoneBankRoot);
|
||||||
uiManager.setText("balanceText", formatMoney(money_));
|
uiManager.setText("balanceText", formatMoney(gameState_.money));
|
||||||
|
|
||||||
uiManager.setButtonCallback("phoneExitButton", [this](const std::string&) {
|
uiManager.setButtonCallback("phoneExitButton", [this](const std::string&) {
|
||||||
closePhoneEntirely();
|
closePhoneEntirely();
|
||||||
@ -525,13 +503,13 @@ namespace ZL {
|
|||||||
uiManager.setButtonCallback("videoSkip", [this](const std::string&) {
|
uiManager.setButtonCallback("videoSkip", [this](const std::string&) {
|
||||||
closePhoneEntirely();
|
closePhoneEntirely();
|
||||||
|
|
||||||
if (isNight)
|
if (gameState_.isNight)
|
||||||
{
|
{
|
||||||
startDialogueFunc("dialog_video002");
|
startDialogueFunc("dialog_video002");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto day = globalInts["day"];
|
auto day = gameState_.globalInts["day"];
|
||||||
if (day == 0)
|
if (day == 0)
|
||||||
{
|
{
|
||||||
startDialogueFunc("dialog_video003");
|
startDialogueFunc("dialog_video003");
|
||||||
@ -545,10 +523,10 @@ namespace ZL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MenuManager::openPhoneTaxi() {
|
void MenuManager::openPhoneTaxi() {
|
||||||
if (currentLocationName_ == "uni_interior") {
|
if (gameState_.currentLocationName == "uni_interior") {
|
||||||
closePhoneEntirely();
|
closePhoneEntirely();
|
||||||
if (startDialogueFunc) startDialogueFunc("dialog_taxi003");
|
if (startDialogueFunc) startDialogueFunc("dialog_taxi003");
|
||||||
} else if (currentLocationName_ == "uni_exterior") {
|
} else if (gameState_.currentLocationName == "uni_exterior") {
|
||||||
openPhoneMapScreen(phoneMapUniRoot);
|
openPhoneMapScreen(phoneMapUniRoot);
|
||||||
} else {
|
} else {
|
||||||
openPhoneMapScreen(phoneMapDormRoot);
|
openPhoneMapScreen(phoneMapDormRoot);
|
||||||
@ -566,13 +544,13 @@ namespace ZL {
|
|||||||
uiManager.updateAllLayouts();
|
uiManager.updateAllLayouts();
|
||||||
});
|
});
|
||||||
uiManager.setButtonCallback("mapGo", [this](const std::string&) {
|
uiManager.setButtonCallback("mapGo", [this](const std::string&) {
|
||||||
tutorialNeedOpenTaxiScreen = false;
|
gameState_.tutorialNeedOpenTaxiScreen = false;
|
||||||
|
|
||||||
if (callTaxiFunc)
|
if (callTaxiFunc)
|
||||||
{
|
{
|
||||||
callTaxiFunc();
|
callTaxiFunc();
|
||||||
}
|
}
|
||||||
money_ -= 500;
|
gameState_.money -= 500;
|
||||||
closePhoneEntirely();
|
closePhoneEntirely();
|
||||||
if (startDialogueFunc) startDialogueFunc("dialog_taxi002");
|
if (startDialogueFunc) startDialogueFunc("dialog_taxi002");
|
||||||
});
|
});
|
||||||
@ -581,7 +559,7 @@ namespace ZL {
|
|||||||
void MenuManager::openPhoneChatFromList(int chatIndex, std::shared_ptr<UiNode> chatRoot) {
|
void MenuManager::openPhoneChatFromList(int chatIndex, std::shared_ptr<UiNode> chatRoot) {
|
||||||
activeChatIndex_ = chatIndex;
|
activeChatIndex_ = chatIndex;
|
||||||
phoneChatVisibleBubbles_.clear();
|
phoneChatVisibleBubbles_.clear();
|
||||||
tutorialPhoneChatScreenOpened = true;
|
gameState_.tutorialPhoneChatScreenOpened = true;
|
||||||
uiManager.pushMenuFromSavedRoot(chatRoot);
|
uiManager.pushMenuFromSavedRoot(chatRoot);
|
||||||
|
|
||||||
rebuildChatBubblesFromHistory(chatIndex);
|
rebuildChatBubblesFromHistory(chatIndex);
|
||||||
@ -601,15 +579,15 @@ namespace ZL {
|
|||||||
|
|
||||||
void MenuManager::returnToPhoneChatList() {
|
void MenuManager::returnToPhoneChatList() {
|
||||||
if (activeChatIndex_ >= 0 && activeChatIndex_ <= 2
|
if (activeChatIndex_ >= 0 && activeChatIndex_ <= 2
|
||||||
&& !chatHistory_[activeChatIndex_].empty()) {
|
&& !gameState_.chatHistory[activeChatIndex_].empty()) {
|
||||||
chatPreviewMsg_[activeChatIndex_] =
|
gameState_.chatPreviewMsg[activeChatIndex_] =
|
||||||
trimChatPreview(chatHistory_[activeChatIndex_].back().text);
|
trimChatPreview(gameState_.chatHistory[activeChatIndex_].back().text);
|
||||||
}
|
}
|
||||||
activeChatIndex_ = -1;
|
activeChatIndex_ = -1;
|
||||||
phoneChatVisibleBubbles_.clear();
|
phoneChatVisibleBubbles_.clear();
|
||||||
uiManager.popMenu();
|
uiManager.popMenu();
|
||||||
uiManager.updateAllLayouts();
|
uiManager.updateAllLayouts();
|
||||||
if (tutorialPhoneChatScreenOpened)
|
if (gameState_.tutorialPhoneChatScreenOpened)
|
||||||
{
|
{
|
||||||
uiManager.setNodeVisible("hint_m002", false);
|
uiManager.setNodeVisible("hint_m002", false);
|
||||||
}
|
}
|
||||||
@ -623,18 +601,18 @@ namespace ZL {
|
|||||||
|
|
||||||
void MenuManager::closePhoneEntirely() {
|
void MenuManager::closePhoneEntirely() {
|
||||||
if (activeChatIndex_ >= 0 && activeChatIndex_ <= 2
|
if (activeChatIndex_ >= 0 && activeChatIndex_ <= 2
|
||||||
&& !chatHistory_[activeChatIndex_].empty()) {
|
&& !gameState_.chatHistory[activeChatIndex_].empty()) {
|
||||||
chatPreviewMsg_[activeChatIndex_] =
|
gameState_.chatPreviewMsg[activeChatIndex_] =
|
||||||
trimChatPreview(chatHistory_[activeChatIndex_].back().text);
|
trimChatPreview(gameState_.chatHistory[activeChatIndex_].back().text);
|
||||||
}
|
}
|
||||||
activeChatIndex_ = -1;
|
activeChatIndex_ = -1;
|
||||||
state = GameState::Gameplay;
|
uiState_ = GameUiState::Gameplay;
|
||||||
phoneChatVisibleBubbles_.clear();
|
phoneChatVisibleBubbles_.clear();
|
||||||
const int depth = uiManager.menuStackSize();
|
const int depth = uiManager.menuStackSize();
|
||||||
for (int i = 0; i < depth; ++i) uiManager.popMenu();
|
for (int i = 0; i < depth; ++i) uiManager.popMenu();
|
||||||
uiManager.updateAllLayouts();
|
uiManager.updateAllLayouts();
|
||||||
|
|
||||||
if (tutorialNeedOpenTaxiScreen)
|
if (gameState_.tutorialNeedOpenTaxiScreen)
|
||||||
{
|
{
|
||||||
uiManager.setNodeVisible("hint7", true);
|
uiManager.setNodeVisible("hint7", true);
|
||||||
uiManager.setNodeVisible("hint7arrow", true);
|
uiManager.setNodeVisible("hint7arrow", true);
|
||||||
@ -647,7 +625,7 @@ namespace ZL {
|
|||||||
uiManager.setNodeVisible("hint7", false);
|
uiManager.setNodeVisible("hint7", false);
|
||||||
uiManager.setNodeVisible("hint7arrow", false);
|
uiManager.setNodeVisible("hint7arrow", false);
|
||||||
|
|
||||||
if (tutorialPhoneChatScreenOpened)
|
if (gameState_.tutorialPhoneChatScreenOpened)
|
||||||
{
|
{
|
||||||
uiManager.setNodeVisible("hint6a", false);
|
uiManager.setNodeVisible("hint6a", false);
|
||||||
uiManager.setNodeVisible("hint6aarrow", false);
|
uiManager.setNodeVisible("hint6aarrow", false);
|
||||||
@ -658,10 +636,6 @@ namespace ZL {
|
|||||||
uiManager.setNodeVisible("hint6aarrow", true);
|
uiManager.setNodeVisible("hint6aarrow", true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuManager::closePhoneScreenFromChat() {
|
void MenuManager::closePhoneScreenFromChat() {
|
||||||
@ -675,15 +649,14 @@ namespace ZL {
|
|||||||
void MenuManager::tutorialShowTaxiHint()
|
void MenuManager::tutorialShowTaxiHint()
|
||||||
{
|
{
|
||||||
std::cout << "tutorialShowTaxiHint" << std::endl;
|
std::cout << "tutorialShowTaxiHint" << std::endl;
|
||||||
tutorialNeedOpenTaxiScreen = true;
|
gameState_.tutorialNeedOpenTaxiScreen = true;
|
||||||
if (state == GameState::Gameplay)
|
if (uiState_ == GameUiState::Gameplay)
|
||||||
{
|
{
|
||||||
uiManager.setNodeVisible("hint6a", false);
|
uiManager.setNodeVisible("hint6a", false);
|
||||||
uiManager.setNodeVisible("hint6aarrow", false);
|
uiManager.setNodeVisible("hint6aarrow", false);
|
||||||
|
|
||||||
uiManager.setNodeVisible("hint7", true);
|
uiManager.setNodeVisible("hint7", true);
|
||||||
uiManager.setNodeVisible("hint7arrow", true);
|
uiManager.setNodeVisible("hint7arrow", true);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -706,7 +679,7 @@ namespace ZL {
|
|||||||
openQuestJournal();
|
openQuestJournal();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (tutorialNeedOpenTaxiScreen)
|
if (gameState_.tutorialNeedOpenTaxiScreen)
|
||||||
{
|
{
|
||||||
uiManager.setNodeVisible("hint6a", false);
|
uiManager.setNodeVisible("hint6a", false);
|
||||||
uiManager.setNodeVisible("hint6aarrow", false);
|
uiManager.setNodeVisible("hint6aarrow", false);
|
||||||
@ -720,17 +693,17 @@ namespace ZL {
|
|||||||
uiManager.setNodeVisible("hint7arrow", false);
|
uiManager.setNodeVisible("hint7arrow", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tutorialPhoneChatScreenOpened) {
|
if (gameState_.tutorialPhoneChatScreenOpened) {
|
||||||
uiManager.setNodeVisible("hint6a", false);
|
uiManager.setNodeVisible("hint6a", false);
|
||||||
uiManager.setNodeVisible("hint6aarrow", false);
|
uiManager.setNodeVisible("hint6aarrow", false);
|
||||||
}
|
}
|
||||||
if (tutorialJournalScreenOpened)
|
if (gameState_.tutorialJournalScreenOpened)
|
||||||
{
|
{
|
||||||
uiManager.setNodeVisible("hint6b", false);
|
uiManager.setNodeVisible("hint6b", false);
|
||||||
uiManager.setNodeVisible("hint6barrow", false);
|
uiManager.setNodeVisible("hint6barrow", false);
|
||||||
}
|
}
|
||||||
if (tutorialPhoneChatScreenOpened && tutorialJournalScreenOpened) {
|
if (gameState_.tutorialPhoneChatScreenOpened && gameState_.tutorialJournalScreenOpened) {
|
||||||
tutorialStep = TutorialStep::Step6;
|
gameState_.tutorialStep = TutorialStep::Step6;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -748,11 +721,11 @@ namespace ZL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MenuManager::onLocationChanged(const std::string& locationName) {
|
void MenuManager::onLocationChanged(const std::string& locationName) {
|
||||||
if (state != GameState::Gameplay) return;
|
if (uiState_ != GameUiState::Gameplay) return;
|
||||||
currentLocationName_ = locationName;
|
gameState_.currentLocationName = locationName;
|
||||||
|
|
||||||
if (locationName == "uni_exterior") {
|
if (locationName == "uni_exterior") {
|
||||||
uiManager.replaceRoot(currentIsDarklands_ ? hudUniExtDarkRoot : hudUniExtRoot);
|
uiManager.replaceRoot(gameState_.isDarklands ? hudUniExtDarkRoot : hudUniExtRoot);
|
||||||
applyCurrentHealthBar();
|
applyCurrentHealthBar();
|
||||||
setupGameplayHudCallbacks();
|
setupGameplayHudCallbacks();
|
||||||
} else if (locationName == "uni_interior") {
|
} else if (locationName == "uni_interior") {
|
||||||
@ -768,22 +741,22 @@ namespace ZL {
|
|||||||
void MenuManager::advanceTutorialStep() {
|
void MenuManager::advanceTutorialStep() {
|
||||||
std::shared_ptr<UiNode> nextRoot;
|
std::shared_ptr<UiNode> nextRoot;
|
||||||
|
|
||||||
switch (tutorialStep) {
|
switch (gameState_.tutorialStep) {
|
||||||
case TutorialStep::Step0:
|
case TutorialStep::Step0:
|
||||||
tutorialStep = TutorialStep::Step1;
|
gameState_.tutorialStep = TutorialStep::Step1;
|
||||||
nextRoot = hudStep1Root;
|
nextRoot = hudStep1Root;
|
||||||
topUiManager.replaceRoot(nullptr);
|
topUiManager.replaceRoot(nullptr);
|
||||||
break;
|
break;
|
||||||
case TutorialStep::Step1:
|
case TutorialStep::Step1:
|
||||||
tutorialStep = TutorialStep::Step2;
|
gameState_.tutorialStep = TutorialStep::Step2;
|
||||||
nextRoot = hudStep2Root;
|
nextRoot = hudStep2Root;
|
||||||
break;
|
break;
|
||||||
case TutorialStep::Step2:
|
case TutorialStep::Step2:
|
||||||
tutorialStep = TutorialStep::Step3;
|
gameState_.tutorialStep = TutorialStep::Step3;
|
||||||
nextRoot = hudStep3Root;
|
nextRoot = hudStep3Root;
|
||||||
break;
|
break;
|
||||||
case TutorialStep::Step3:
|
case TutorialStep::Step3:
|
||||||
tutorialStep = TutorialStep::Step4;
|
gameState_.tutorialStep = TutorialStep::Step4;
|
||||||
nextRoot = hudStep4Root;
|
nextRoot = hudStep4Root;
|
||||||
if (tutorialUnlockInteractiveObjectsFunc)
|
if (tutorialUnlockInteractiveObjectsFunc)
|
||||||
{
|
{
|
||||||
@ -795,7 +768,7 @@ namespace ZL {
|
|||||||
return; // Step4/Step5 transitions are driven by onItemPickedUp
|
return; // Step4/Step5 transitions are driven by onItemPickedUp
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state == GameState::Gameplay && nextRoot) {
|
if (uiState_ == GameUiState::Gameplay && nextRoot) {
|
||||||
uiManager.replaceRoot(nextRoot);
|
uiManager.replaceRoot(nextRoot);
|
||||||
applyCurrentHealthBar();
|
applyCurrentHealthBar();
|
||||||
|
|
||||||
@ -806,41 +779,41 @@ namespace ZL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MenuManager::onItemPickedUp(const std::string& itemId) {
|
void MenuManager::onItemPickedUp(const std::string& itemId) {
|
||||||
if (itemId == "note_spell" && uniIntTutorialState_ == UniIntTutorialState::Step10) {
|
if (itemId == "note_spell" && gameState_.uniIntTutorialState == UniIntTutorialState::Step10) {
|
||||||
uniIntTutorialState_ = UniIntTutorialState::Step11;
|
gameState_.uniIntTutorialState = UniIntTutorialState::Step11;
|
||||||
if (currentLocationName_ == "uni_interior" && state == GameState::Gameplay)
|
if (gameState_.currentLocationName == "uni_interior" && uiState_ == GameUiState::Gameplay)
|
||||||
applyUniIntHud();
|
applyUniIntHud();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tutorialStep != TutorialStep::Step4 && tutorialStep != TutorialStep::Step5) {
|
if (gameState_.tutorialStep != TutorialStep::Step4 && gameState_.tutorialStep != TutorialStep::Step5) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dorm tutorial HUD logic must not run in other locations.
|
// Dorm tutorial HUD logic must not run in other locations.
|
||||||
// currentLocationName_ is empty only before the first teleport (still in dorm).
|
// currentLocationName is empty only before the first teleport (still in dorm).
|
||||||
if (!currentLocationName_.empty() && currentLocationName_ != "location_dorm") {
|
if (!gameState_.currentLocationName.empty() && gameState_.currentLocationName != "location_dorm") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (itemId == "phone") tutorialPhonePickedUp = true;
|
if (itemId == "phone") gameState_.tutorialPhonePickedUp = true;
|
||||||
if (itemId == "journal") tutorialJournalPickedUp = true;
|
if (itemId == "journal") gameState_.tutorialJournalPickedUp = true;
|
||||||
|
|
||||||
if (tutorialStep == TutorialStep::Step4) {
|
if (gameState_.tutorialStep == TutorialStep::Step4) {
|
||||||
tutorialStep = TutorialStep::Step5;
|
gameState_.tutorialStep = TutorialStep::Step5;
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshItemPickupHud();
|
refreshItemPickupHud();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuManager::refreshItemPickupHud() {
|
void MenuManager::refreshItemPickupHud() {
|
||||||
if (state != GameState::Gameplay) return;
|
if (uiState_ != GameUiState::Gameplay) return;
|
||||||
|
|
||||||
std::shared_ptr<UiNode> nextRoot;
|
std::shared_ptr<UiNode> nextRoot;
|
||||||
if (tutorialPhonePickedUp && tutorialJournalPickedUp) {
|
if (gameState_.tutorialPhonePickedUp && gameState_.tutorialJournalPickedUp) {
|
||||||
nextRoot = hudStep5abRoot;
|
nextRoot = hudStep5abRoot;
|
||||||
} else if (tutorialPhonePickedUp) {
|
} else if (gameState_.tutorialPhonePickedUp) {
|
||||||
nextRoot = hudStep5aRoot;
|
nextRoot = hudStep5aRoot;
|
||||||
} else if (tutorialJournalPickedUp) {
|
} else if (gameState_.tutorialJournalPickedUp) {
|
||||||
nextRoot = hudStep5bRoot;
|
nextRoot = hudStep5bRoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -854,7 +827,7 @@ namespace ZL {
|
|||||||
|
|
||||||
void MenuManager::refreshQuestJournalUi() {
|
void MenuManager::refreshQuestJournalUi() {
|
||||||
visibleQuestIds.clear();
|
visibleQuestIds.clear();
|
||||||
auto quests = questJournal.getVisibleQuests();
|
auto quests = gameState_.questJournal.getVisibleQuests();
|
||||||
|
|
||||||
std::sort(quests.begin(), quests.end(), [](const Quest::QuestState* a, const Quest::QuestState* b) {
|
std::sort(quests.begin(), quests.end(), [](const Quest::QuestState* a, const Quest::QuestState* b) {
|
||||||
const int pa = questStatusPriority(a->status);
|
const int pa = questStatusPriority(a->status);
|
||||||
@ -920,7 +893,7 @@ namespace ZL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
selectedQuestIndex = index;
|
selectedQuestIndex = index;
|
||||||
Quest::QuestState* quest = questJournal.findQuest(visibleQuestIds[index]);
|
Quest::QuestState* quest = gameState_.questJournal.findQuest(visibleQuestIds[index]);
|
||||||
if (!quest) {
|
if (!quest) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1007,7 +980,7 @@ namespace ZL {
|
|||||||
phoneChatVisibleBubbles_.clear();
|
phoneChatVisibleBubbles_.clear();
|
||||||
|
|
||||||
if (chatIndex < 0 || chatIndex > 2) return;
|
if (chatIndex < 0 || chatIndex > 2) return;
|
||||||
for (const auto& msg : chatHistory_[chatIndex]) {
|
for (const auto& msg : gameState_.chatHistory[chatIndex]) {
|
||||||
const bool inc = msg.incoming;
|
const bool inc = msg.incoming;
|
||||||
const std::string nodeName = uiManager.addChatBubble(
|
const std::string nodeName = uiManager.addChatBubble(
|
||||||
"chatMessagesContainer", msg.text, inc,
|
"chatMessagesContainer", msg.text, inc,
|
||||||
@ -1028,13 +1001,13 @@ namespace ZL {
|
|||||||
void MenuManager::onChatBubbleReady(const std::string& text, bool incoming) {
|
void MenuManager::onChatBubbleReady(const std::string& text, bool incoming) {
|
||||||
if (activeChatIndex_ < 0) return;
|
if (activeChatIndex_ < 0) return;
|
||||||
|
|
||||||
auto& history = chatHistory_[activeChatIndex_];
|
auto& history = gameState_.chatHistory[activeChatIndex_];
|
||||||
if (static_cast<int>(history.size()) >= 5) {
|
if (static_cast<int>(history.size()) >= 5) {
|
||||||
history.erase(history.begin());
|
history.erase(history.begin());
|
||||||
}
|
}
|
||||||
history.push_back({ text, incoming });
|
history.push_back({ text, incoming });
|
||||||
|
|
||||||
if (state != GameState::PhoneScreen) return;
|
if (uiState_ != GameUiState::PhoneScreen) return;
|
||||||
|
|
||||||
const std::string nodeName = uiManager.addChatBubble(
|
const std::string nodeName = uiManager.addChatBubble(
|
||||||
"chatMessagesContainer", text, incoming,
|
"chatMessagesContainer", text, incoming,
|
||||||
@ -1060,15 +1033,14 @@ namespace ZL {
|
|||||||
void MenuManager::setDarklandsMode(bool enabled)
|
void MenuManager::setDarklandsMode(bool enabled)
|
||||||
{
|
{
|
||||||
std::cout << "MenuManager::setDarklandsMode called" << std::endl;
|
std::cout << "MenuManager::setDarklandsMode called" << std::endl;
|
||||||
currentIsDarklands_ = enabled;
|
|
||||||
|
|
||||||
if (currentLocationName_ == "uni_interior") {
|
if (gameState_.currentLocationName == "uni_interior") {
|
||||||
if (enabled && uniIntTutorialState_ == UniIntTutorialState::Step11) {
|
if (enabled && gameState_.uniIntTutorialState == UniIntTutorialState::Step11) {
|
||||||
uniIntTutorialState_ = UniIntTutorialState::DarklandsActive;
|
gameState_.uniIntTutorialState = UniIntTutorialState::DarklandsActive;
|
||||||
}
|
}
|
||||||
applyUniIntHud();
|
applyUniIntHud();
|
||||||
} else if (currentLocationName_ == "uni_exterior") {
|
} else if (gameState_.currentLocationName == "uni_exterior") {
|
||||||
if (state == GameState::Gameplay) {
|
if (uiState_ == GameUiState::Gameplay) {
|
||||||
uiManager.replaceRoot(enabled ? hudUniExtDarkRoot : hudUniExtRoot);
|
uiManager.replaceRoot(enabled ? hudUniExtDarkRoot : hudUniExtRoot);
|
||||||
applyCurrentHealthBar();
|
applyCurrentHealthBar();
|
||||||
setupGameplayHudCallbacks();
|
setupGameplayHudCallbacks();
|
||||||
@ -1081,31 +1053,31 @@ namespace ZL {
|
|||||||
|
|
||||||
void MenuManager::applyUniIntHud()
|
void MenuManager::applyUniIntHud()
|
||||||
{
|
{
|
||||||
if (state != GameState::Gameplay) return;
|
if (uiState_ != GameUiState::Gameplay) return;
|
||||||
std::shared_ptr<UiNode> root;
|
std::shared_ptr<UiNode> root;
|
||||||
if (currentIsDarklands_) {
|
if (gameState_.isDarklands) {
|
||||||
switch (uniIntTutorialState_) {
|
switch (gameState_.uniIntTutorialState) {
|
||||||
case UniIntTutorialState::DarklandsStep13: root = hudUniIntStep13Root; break;
|
case UniIntTutorialState::DarklandsStep13: root = hudUniIntStep13Root; break;
|
||||||
case UniIntTutorialState::DarklandsFull: root = hudUniIntDarkFullRoot; break;
|
case UniIntTutorialState::DarklandsFull: root = hudUniIntDarkFullRoot; break;
|
||||||
default: root = hudUniIntStep12Root; break;
|
default: root = hudUniIntStep12Root; break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
switch (uniIntTutorialState_) {
|
switch (gameState_.uniIntTutorialState) {
|
||||||
case UniIntTutorialState::Step10: root = hudUniIntStep10Root; break;
|
case UniIntTutorialState::Step10: root = hudUniIntStep10Root; break;
|
||||||
case UniIntTutorialState::Step11: root = hudUniIntStep11Root; break;
|
case UniIntTutorialState::Step11: root = hudUniIntStep11Root; break;
|
||||||
default: root = hudUniIntFullRoot; break;
|
default: root = hudUniIntFullRoot; break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
uiManager.replaceRoot(root);
|
uiManager.replaceRoot(root);
|
||||||
applyCurrentHealthBar();
|
applyCurrentHealthBar();
|
||||||
setupGameplayHudCallbacks(); // already calls hideAllToastWidgets + applyToastsToUi
|
setupGameplayHudCallbacks();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuManager::onPlayerStartedWalking()
|
void MenuManager::onPlayerStartedWalking()
|
||||||
{
|
{
|
||||||
if (currentLocationName_ == "uni_interior"
|
if (gameState_.currentLocationName == "uni_interior"
|
||||||
&& currentIsDarklands_
|
&& gameState_.isDarklands
|
||||||
&& uniIntTutorialState_ == UniIntTutorialState::DarklandsActive) {
|
&& gameState_.uniIntTutorialState == UniIntTutorialState::DarklandsActive) {
|
||||||
uiManager.setNodeVisible("hint_darklands003", false);
|
uiManager.setNodeVisible("hint_darklands003", false);
|
||||||
uiManager.setNodeVisible("hint_darklands003_arrow", false);
|
uiManager.setNodeVisible("hint_darklands003_arrow", false);
|
||||||
}
|
}
|
||||||
@ -1113,24 +1085,24 @@ namespace ZL {
|
|||||||
|
|
||||||
void MenuManager::advanceUniIntDarklandsHud()
|
void MenuManager::advanceUniIntDarklandsHud()
|
||||||
{
|
{
|
||||||
if (uniIntTutorialState_ != UniIntTutorialState::DarklandsActive) return;
|
if (gameState_.uniIntTutorialState != UniIntTutorialState::DarklandsActive) return;
|
||||||
uniIntTutorialState_ = UniIntTutorialState::DarklandsStep13;
|
gameState_.uniIntTutorialState = UniIntTutorialState::DarklandsStep13;
|
||||||
if (currentLocationName_ == "uni_interior" && currentIsDarklands_ && state == GameState::Gameplay)
|
if (gameState_.currentLocationName == "uni_interior" && gameState_.isDarklands && uiState_ == GameUiState::Gameplay)
|
||||||
applyUniIntHud();
|
applyUniIntHud();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuManager::onEnemyKilledInUniInterior()
|
void MenuManager::onEnemyKilledInUniInterior()
|
||||||
{
|
{
|
||||||
if (uniIntTutorialState_ != UniIntTutorialState::DarklandsActive
|
if (gameState_.uniIntTutorialState != UniIntTutorialState::DarklandsActive
|
||||||
&& uniIntTutorialState_ != UniIntTutorialState::DarklandsStep13) return;
|
&& gameState_.uniIntTutorialState != UniIntTutorialState::DarklandsStep13) return;
|
||||||
uniIntTutorialState_ = UniIntTutorialState::DarklandsFull;
|
gameState_.uniIntTutorialState = UniIntTutorialState::DarklandsFull;
|
||||||
if (currentLocationName_ == "uni_interior" && currentIsDarklands_ && state == GameState::Gameplay)
|
if (gameState_.currentLocationName == "uni_interior" && gameState_.isDarklands && uiState_ == GameUiState::Gameplay)
|
||||||
applyUniIntHud();
|
applyUniIntHud();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuManager::updateHealthBar(float hp, float maxHp) {
|
void MenuManager::updateHealthBar(float hp, float maxHp) {
|
||||||
currentPlayerHp_ = hp;
|
gameState_.playerHp = hp;
|
||||||
currentPlayerMaxHp_ = maxHp;
|
gameState_.playerMaxHp = maxHp;
|
||||||
applyCurrentHealthBar();
|
applyCurrentHealthBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1145,22 +1117,22 @@ namespace ZL {
|
|||||||
void MenuManager::onCutsceneFinished() {
|
void MenuManager::onCutsceneFinished() {
|
||||||
cutsceneHudActive_ = false;
|
cutsceneHudActive_ = false;
|
||||||
topUiManager.replaceRoot(nullptr);
|
topUiManager.replaceRoot(nullptr);
|
||||||
if (state == GameState::Gameplay)
|
if (uiState_ == GameUiState::Gameplay)
|
||||||
onLocationChanged(currentLocationName_);
|
onLocationChanged(gameState_.currentLocationName);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuManager::applyCurrentHealthBar() {
|
void MenuManager::applyCurrentHealthBar() {
|
||||||
std::cout << "MenuManager::applyCurrentHealthBar called step 1" << std::endl;
|
std::cout << "MenuManager::applyCurrentHealthBar called step 1" << std::endl;
|
||||||
std::cout << "currentPlayerMaxHp_ is " << currentPlayerMaxHp_ << std::endl;
|
std::cout << "currentPlayerMaxHp_ is " << gameState_.playerMaxHp << std::endl;
|
||||||
if (currentPlayerMaxHp_ <= 0.f) return;
|
if (gameState_.playerMaxHp <= 0.f) return;
|
||||||
std::cout << "MenuManager::applyCurrentHealthBar called step 2" << std::endl;
|
std::cout << "MenuManager::applyCurrentHealthBar called step 2" << std::endl;
|
||||||
std::cout << "currentPlayerHp_ is " << currentPlayerHp_ << std::endl;
|
std::cout << "currentPlayerHp_ is " << gameState_.playerHp << std::endl;
|
||||||
|
|
||||||
const float fraction = std::clamp(currentPlayerHp_ / currentPlayerMaxHp_, 0.f, 1.f);
|
const float fraction = std::clamp(gameState_.playerHp / gameState_.playerMaxHp, 0.f, 1.f);
|
||||||
|
|
||||||
uiManager.setSliderValue("healthBarFill", fraction);
|
uiManager.setSliderValue("healthBarFill", fraction);
|
||||||
std::string hpText = std::to_string(static_cast<int>(currentPlayerHp_)) + "/" +
|
std::string hpText = std::to_string(static_cast<int>(gameState_.playerHp)) + "/" +
|
||||||
std::to_string(static_cast<int>(currentPlayerMaxHp_));
|
std::to_string(static_cast<int>(gameState_.playerMaxHp));
|
||||||
if (hpText.size() < 7) hpText.insert(0, 7 - hpText.size(), ' ');
|
if (hpText.size() < 7) hpText.insert(0, 7 - hpText.size(), ' ');
|
||||||
uiManager.setText("healthValue", hpText);
|
uiManager.setText("healthValue", hpText);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
#include "render/TextureManager.h"
|
#include "render/TextureManager.h"
|
||||||
#include "UiManager.h"
|
#include "UiManager.h"
|
||||||
#include "items/Item.h"
|
#include "items/Item.h"
|
||||||
#include "quest/QuestJournal.h"
|
#include "GameState.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -14,7 +14,7 @@ namespace ZL {
|
|||||||
|
|
||||||
extern const char* CONST_ZIP_FILE;
|
extern const char* CONST_ZIP_FILE;
|
||||||
|
|
||||||
enum class GameState {
|
enum class GameUiState {
|
||||||
MainMenu,
|
MainMenu,
|
||||||
About,
|
About,
|
||||||
Gameplay,
|
Gameplay,
|
||||||
@ -23,29 +23,12 @@ namespace ZL {
|
|||||||
PhoneScreen
|
PhoneScreen
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class TutorialStep {
|
|
||||||
Step0, // Dialogue hint: "click to advance"
|
|
||||||
Step1, // Camera rotation hint
|
|
||||||
Step2, // Floor tap / walk hint
|
|
||||||
Step3, // Pinch-zoom hint
|
|
||||||
Step4, // Pick-up item hint
|
|
||||||
Step5, // Post-pickup reaction (sub-state: which items were collected)
|
|
||||||
Step6, // Tutorial complete — both phone and journal opened
|
|
||||||
};
|
|
||||||
|
|
||||||
class MenuManager {
|
class MenuManager {
|
||||||
public:
|
public:
|
||||||
UiManager uiManager;
|
UiManager uiManager;
|
||||||
UiManager topUiManager;
|
UiManager topUiManager;
|
||||||
ZL::Quest::QuestJournal questJournal;
|
|
||||||
std::unordered_map<std::string, int>& globalInts;
|
|
||||||
|
|
||||||
// Global night mode state — persists across location transitions.
|
MenuManager(Renderer& iRenderer, GameState& gameState);
|
||||||
bool isNight = false;
|
|
||||||
bool isDawn = false; // sub-variant of night: brighter pink ambient, same lighting
|
|
||||||
|
|
||||||
|
|
||||||
MenuManager(Renderer& iRenderer, std::unordered_map<std::string, int>& globalInts);
|
|
||||||
|
|
||||||
void setup(Inventory& inv, const std::string& zipFile);
|
void setup(Inventory& inv, const std::string& zipFile);
|
||||||
|
|
||||||
@ -57,15 +40,15 @@ namespace ZL {
|
|||||||
void closeQuestJournal();
|
void closeQuestJournal();
|
||||||
void toggleQuestJournal();
|
void toggleQuestJournal();
|
||||||
|
|
||||||
bool isInventoryOpen() const { return state == GameState::Inventory; }
|
bool isInventoryOpen() const { return uiState_ == GameUiState::Inventory; }
|
||||||
bool isQuestJournalOpen() const { return state == GameState::QuestJournal; }
|
bool isQuestJournalOpen() const { return uiState_ == GameUiState::QuestJournal; }
|
||||||
|
|
||||||
void showMainMenu();
|
void showMainMenu();
|
||||||
bool isMainMenuOpen() const { return state == GameState::MainMenu; }
|
bool isMainMenuOpen() const { return uiState_ == GameUiState::MainMenu; }
|
||||||
|
|
||||||
void openPhoneScreen();
|
void openPhoneScreen();
|
||||||
void closePhoneScreen();
|
void closePhoneScreen();
|
||||||
bool isPhoneScreenOpen() const { return state == GameState::PhoneScreen; }
|
bool isPhoneScreenOpen() const { return uiState_ == GameUiState::PhoneScreen; }
|
||||||
void closePhoneEntirely();
|
void closePhoneEntirely();
|
||||||
|
|
||||||
void tutorialShowTaxiHint();
|
void tutorialShowTaxiHint();
|
||||||
@ -73,7 +56,7 @@ namespace ZL {
|
|||||||
void setChatUnread(int chatIndex, bool unread, const std::string& previewMsg = "");
|
void setChatUnread(int chatIndex, bool unread, const std::string& previewMsg = "");
|
||||||
|
|
||||||
void spendMoney(int amount);
|
void spendMoney(int amount);
|
||||||
int getMoney() const { return money_; }
|
int getMoney() const { return gameState_.money; }
|
||||||
|
|
||||||
std::function<void()> startGameFunc;
|
std::function<void()> startGameFunc;
|
||||||
std::function<void(const std::string&)> startDialogueFunc;
|
std::function<void(const std::string&)> startDialogueFunc;
|
||||||
@ -106,12 +89,12 @@ namespace ZL {
|
|||||||
void showToast(const std::string& iconPath, const std::string& text);
|
void showToast(const std::string& iconPath, const std::string& text);
|
||||||
void update(float deltaMs);
|
void update(float deltaMs);
|
||||||
|
|
||||||
TutorialStep tutorialStep = TutorialStep::Step0;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Renderer& renderer;
|
Renderer& renderer;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
GameState& gameState_;
|
||||||
|
|
||||||
void enterGameplay();
|
void enterGameplay();
|
||||||
void refreshQuestJournalUi();
|
void refreshQuestJournalUi();
|
||||||
void selectQuestByIndex(int index);
|
void selectQuestByIndex(int index);
|
||||||
@ -153,25 +136,12 @@ namespace ZL {
|
|||||||
static constexpr float TOAST_FADE_MS = 1000.0f;
|
static constexpr float TOAST_FADE_MS = 1000.0f;
|
||||||
static constexpr float TOAST_VISIBLE_MS = 2000.0f;
|
static constexpr float TOAST_VISIBLE_MS = 2000.0f;
|
||||||
|
|
||||||
GameState state = GameState::Gameplay;
|
GameUiState uiState_ = GameUiState::Gameplay;
|
||||||
Inventory* inventory = nullptr;
|
Inventory* inventory = nullptr;
|
||||||
//std::string zipFile_;
|
|
||||||
int inventorySelectedIndex_ = -1;
|
int inventorySelectedIndex_ = -1;
|
||||||
|
|
||||||
enum class UniIntTutorialState { Step10, Step11, DarklandsActive, DarklandsStep13, DarklandsFull };
|
int selectedQuestIndex = -1;
|
||||||
UniIntTutorialState uniIntTutorialState_ = UniIntTutorialState::Step10;
|
std::vector<std::string> visibleQuestIds;
|
||||||
std::string currentLocationName_;
|
|
||||||
bool currentIsDarklands_ = false;
|
|
||||||
|
|
||||||
bool tutorialPhonePickedUp = false;
|
|
||||||
bool tutorialJournalPickedUp = false;
|
|
||||||
//bool tutorialPhoneScreenOpened = false;
|
|
||||||
bool tutorialPhoneChatScreenOpened = false;
|
|
||||||
bool tutorialJournalScreenOpened = false;
|
|
||||||
bool tutorialMessengerScreenOpened = false;
|
|
||||||
//bool tutorialTaxiScreenOpened = false;
|
|
||||||
|
|
||||||
bool tutorialNeedOpenTaxiScreen = false;
|
|
||||||
|
|
||||||
std::shared_ptr<UiNode> hudRoot;
|
std::shared_ptr<UiNode> hudRoot;
|
||||||
std::shared_ptr<UiNode> hudStep1Root;
|
std::shared_ptr<UiNode> hudStep1Root;
|
||||||
@ -190,11 +160,9 @@ namespace ZL {
|
|||||||
std::shared_ptr<UiNode> hudUniIntDarkFullRoot;
|
std::shared_ptr<UiNode> hudUniIntDarkFullRoot;
|
||||||
std::shared_ptr<UiNode> hudUniExtDarkRoot;
|
std::shared_ptr<UiNode> hudUniExtDarkRoot;
|
||||||
|
|
||||||
|
|
||||||
std::shared_ptr<UiNode> hudCutsceneRoot_;
|
std::shared_ptr<UiNode> hudCutsceneRoot_;
|
||||||
std::shared_ptr<UiNode> hudTopHintRoot_;
|
std::shared_ptr<UiNode> hudTopHintRoot_;
|
||||||
|
|
||||||
|
|
||||||
std::shared_ptr<UiNode> phoneMainRoot;
|
std::shared_ptr<UiNode> phoneMainRoot;
|
||||||
std::shared_ptr<UiNode> phoneMainHintARoot;
|
std::shared_ptr<UiNode> phoneMainHintARoot;
|
||||||
std::shared_ptr<UiNode> phoneMainHintBRoot;
|
std::shared_ptr<UiNode> phoneMainHintBRoot;
|
||||||
@ -221,27 +189,14 @@ namespace ZL {
|
|||||||
std::shared_ptr<Texture> texItemSelected_;
|
std::shared_ptr<Texture> texItemSelected_;
|
||||||
std::shared_ptr<Texture> texItemTransparent_;
|
std::shared_ptr<Texture> texItemTransparent_;
|
||||||
|
|
||||||
float currentPlayerHp_ = 200.f;
|
|
||||||
float currentPlayerMaxHp_ = 200.f;
|
|
||||||
void applyCurrentHealthBar();
|
void applyCurrentHealthBar();
|
||||||
|
|
||||||
int selectedQuestIndex = -1;
|
// Phone chat UI state (transient — rebuilt from gameState_.chatHistory on open)
|
||||||
std::vector<std::string> visibleQuestIds;
|
|
||||||
|
|
||||||
bool chatUnread_[3] = { true, true, true };
|
|
||||||
std::string chatPreviewMsg_[3]; // empty = keep JSON hardcoded text
|
|
||||||
int money_ = 5500;
|
|
||||||
|
|
||||||
// Phone chat state
|
|
||||||
struct PhoneChatBubbleInfo {
|
struct PhoneChatBubbleInfo {
|
||||||
std::string nodeName;
|
std::string nodeName;
|
||||||
float height;
|
float height;
|
||||||
};
|
};
|
||||||
std::vector<PhoneChatBubbleInfo> phoneChatVisibleBubbles_;
|
std::vector<PhoneChatBubbleInfo> phoneChatVisibleBubbles_;
|
||||||
|
|
||||||
// Per-chat message history (max 5 messages each)
|
|
||||||
struct StoredChatMessage { std::string text; bool incoming; };
|
|
||||||
std::vector<StoredChatMessage> chatHistory_[3];
|
|
||||||
int activeChatIndex_ = -1;
|
int activeChatIndex_ = -1;
|
||||||
|
|
||||||
// Preloaded bubble textures
|
// Preloaded bubble textures
|
||||||
|
|||||||
@ -364,4 +364,46 @@ void DialogueRuntime::presentChoices(const Node& node) {
|
|||||||
presentation.cutsceneBlackAlpha = 0.0f;
|
presentation.cutsceneBlackAlpha = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DialogueRuntime::save(nlohmann::json& out) const
|
||||||
|
{
|
||||||
|
if (!isActive() || !activeDialogue) {
|
||||||
|
out["active"] = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
out["active"] = true;
|
||||||
|
out["dialogueId"] = activeDialogue->id;
|
||||||
|
out["currentNodeId"] = currentNodeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DialogueRuntime::load(const nlohmann::json& in)
|
||||||
|
{
|
||||||
|
stop();
|
||||||
|
if (!in.value("active", false)) return;
|
||||||
|
|
||||||
|
const std::string dialogueId = in.value("dialogueId", "");
|
||||||
|
const std::string nodeId = in.value("currentNodeId", "");
|
||||||
|
if (dialogueId.empty() || nodeId.empty() || !database) return;
|
||||||
|
|
||||||
|
const DialogueDefinition* dialogue = database->findDialogue(dialogueId);
|
||||||
|
if (!dialogue) {
|
||||||
|
std::cerr << "[dialogue] load: dialogue '" << dialogueId << "' not found\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
activeDialogue = dialogue;
|
||||||
|
currentNodeId.clear();
|
||||||
|
visibleChoices.clear();
|
||||||
|
selectedChoice = -1;
|
||||||
|
revealCharacters = 0.0f;
|
||||||
|
presentation = {};
|
||||||
|
presentation.dialogueId = dialogue->id;
|
||||||
|
|
||||||
|
enterNode(nodeId);
|
||||||
|
|
||||||
|
// Snap text to fully revealed so the typing animation doesn't replay.
|
||||||
|
presentation.visibleText = presentation.fullText;
|
||||||
|
presentation.revealCompleted = true;
|
||||||
|
revealCharacters = static_cast<float>(presentation.fullText.size());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ZL::Dialogue
|
} // namespace ZL::Dialogue
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "dialogue/DialogueDatabase.h"
|
#include "dialogue/DialogueDatabase.h"
|
||||||
#include "quest/QuestJournal.h"
|
#include "quest/QuestJournal.h"
|
||||||
|
#include "external/nlohmann/json.hpp"
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
@ -40,6 +41,13 @@ public:
|
|||||||
void setGlobalFlagStore(std::unordered_map<std::string, int>* store);
|
void setGlobalFlagStore(std::unordered_map<std::string, int>* store);
|
||||||
void setQuestJournal(Quest::QuestJournal* journal);
|
void setQuestJournal(Quest::QuestJournal* journal);
|
||||||
|
|
||||||
|
// Saves whether a dialogue is active, its id, and current node.
|
||||||
|
// Cutscene state is not saved.
|
||||||
|
void save(nlohmann::json& out) const;
|
||||||
|
// Restores saved dialogue state. Text reveal snaps to completion so the
|
||||||
|
// typing animation doesn't replay. No callbacks are fired on restore.
|
||||||
|
void load(const nlohmann::json& in);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class Mode {
|
enum class Mode {
|
||||||
Inactive,
|
Inactive,
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "external/nlohmann/json.hpp"
|
||||||
#include "dialogue/DialogueOverlay.h"
|
#include "dialogue/DialogueOverlay.h"
|
||||||
#include "dialogue/DialogueRuntime.h"
|
#include "dialogue/DialogueRuntime.h"
|
||||||
#include "cutscene/CutsceneDatabase.h"
|
#include "cutscene/CutsceneDatabase.h"
|
||||||
@ -41,6 +42,9 @@ public:
|
|||||||
void setOnDialogueAdvanced(std::function<void()> cb);
|
void setOnDialogueAdvanced(std::function<void()> cb);
|
||||||
void stopDialogue();
|
void stopDialogue();
|
||||||
|
|
||||||
|
void saveDialogueState(nlohmann::json& out) const { dialogueRuntime.save(out); }
|
||||||
|
void loadDialogueState(const nlohmann::json& in) { dialogueRuntime.load(in); }
|
||||||
|
|
||||||
bool isActive() const { return dialogueRuntime.isActive() || cutsceneRuntime.isActive(); }
|
bool isActive() const { return dialogueRuntime.isActive() || cutsceneRuntime.isActive(); }
|
||||||
bool blocksGameplayInput() const { return isActive(); }
|
bool blocksGameplayInput() const { return isActive(); }
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user