Working on reset state and the end game
This commit is contained in:
parent
124208f2a4
commit
1c2c80f0ea
1527
resources/config/start_state.json
Normal file
1527
resources/config/start_state.json
Normal file
File diff suppressed because it is too large
Load Diff
143
src/Game.cpp
143
src/Game.cpp
@ -289,7 +289,64 @@ namespace ZL
|
|||||||
|
|
||||||
std::cout << "Load resurces step 13" << std::endl;
|
std::cout << "Load resurces step 13" << std::endl;
|
||||||
|
|
||||||
menuManager.onResetGame = [this]() {
|
try {
|
||||||
|
menuManager.setup(gameState.inventory, CONST_ZIP_FILE);
|
||||||
|
std::cout << "UI loaded successfully" << std::endl;
|
||||||
|
}
|
||||||
|
catch (const std::exception& e) {
|
||||||
|
|
||||||
|
std::cerr << "Failed to load UI: " << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
menuManager.startGameFunc = [this]() {
|
||||||
|
mainThreadHandler.EnqueueMainThreadTask([this]() {
|
||||||
|
performResetToInitialState();
|
||||||
|
gameState.currentLocationName = "location_dorm";
|
||||||
|
currentLocation()->scriptEngine.callLocationEnterCallback();
|
||||||
|
this->audioPlayer->crossFadeMusicAsync("audio/obshaga.ogg");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
menuManager.onSaveGame = [this](int slot) { saveGame(slot); };
|
||||||
|
menuManager.onLoadGame = [this](int slot) { loadGame(slot); };
|
||||||
|
menuManager.getSlotInfoFunc = [this](int slot) { return readSlotInfo(slot); };
|
||||||
|
|
||||||
|
if (audioPlayer->init()) {
|
||||||
|
audioPlayer->setMusicVolume(100);
|
||||||
|
audioPlayer->setSoundVolume(80);
|
||||||
|
std::cout << "Audio initialized successfully" << std::endl;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::cout << "Audio initialization failed" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
menuManager.loadSettings();
|
||||||
|
|
||||||
|
audioPlayer->crossFadeMusicAsync("audio/main menu final.ogg");
|
||||||
|
|
||||||
|
loadingCompleted = true;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Game::rearmDormOneShotCallbacks() {
|
||||||
|
auto it = gameState.locations.find("location_dorm");
|
||||||
|
if (it == gameState.locations.end() || !it->second) return;
|
||||||
|
|
||||||
|
it->second->onPlayerTaxiRequired = [this]() {
|
||||||
|
menuManager.tutorialShowTaxiHint();
|
||||||
|
};
|
||||||
|
|
||||||
|
menuManager.tutorialUnlockInteractiveObjectsFunc = [this]()
|
||||||
|
{
|
||||||
|
if (gameState.locations["location_dorm"])
|
||||||
|
{
|
||||||
|
gameState.locations["location_dorm"]->state.tutorialInteractiveObjectsLocked = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Game::performFullReset() {
|
||||||
gameState.currentLocationName.clear();
|
gameState.currentLocationName.clear();
|
||||||
gameState.isDarklands = false;
|
gameState.isDarklands = false;
|
||||||
gameState.isNight = false;
|
gameState.isNight = false;
|
||||||
@ -314,43 +371,53 @@ namespace ZL
|
|||||||
gameState.inventory.clear();
|
gameState.inventory.clear();
|
||||||
gameState.questJournal.loadFromFile(localizedConfigPath("resources/config/quests.json", g_currentLanguage), CONST_ZIP_FILE);
|
gameState.questJournal.loadFromFile(localizedConfigPath("resources/config/quests.json", g_currentLanguage), CONST_ZIP_FILE);
|
||||||
createLocations();
|
createLocations();
|
||||||
};
|
|
||||||
|
|
||||||
|
// Developer workflow: uncomment to (re)generate resources/config/start_state.json
|
||||||
|
// from this freshly-rebuilt state after changing gameplay/content JSON or Lua.
|
||||||
|
// Re-comment before shipping.
|
||||||
|
//saveInitialState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Game::performResetToInitialState() {
|
||||||
|
const std::string path = "resources/config/start_state.json";
|
||||||
|
const std::string content = ZL::readTextFile(path);
|
||||||
|
if (!content.empty()) {
|
||||||
try {
|
try {
|
||||||
menuManager.setup(gameState.inventory, CONST_ZIP_FILE);
|
nlohmann::json root = nlohmann::json::parse(content);
|
||||||
std::cout << "UI loaded successfully" << std::endl;
|
// GameState::load() merges into existing maps rather than replacing them,
|
||||||
|
// so any Lua globals set during play that aren't in the pristine snapshot
|
||||||
|
// must be cleared explicitly before loading it.
|
||||||
|
gameState.globalInts.clear();
|
||||||
|
gameState.globalFloats.clear();
|
||||||
|
gameState.load(root); // updates existing Location objects in place -- no
|
||||||
|
// destruction, no re-parsing JSON/nav files, no new
|
||||||
|
// sol2 state (GameState::load only touches map entries
|
||||||
|
// that already exist).
|
||||||
|
// Locations survive this reset, so any one-shot callbacks they consumed
|
||||||
|
// during the previous playthrough (tutorial unlock, taxi request) must be
|
||||||
|
// re-armed here -- createLocations() normally does this, but doesn't run
|
||||||
|
// on this fast path.
|
||||||
|
rearmDormOneShotCallbacks();
|
||||||
|
return;
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << "[reset] Failed to parse " << path << ": " << e.what()
|
||||||
|
<< " -- falling back to full rebuild" << std::endl;
|
||||||
}
|
}
|
||||||
catch (const std::exception& e) {
|
}
|
||||||
|
performFullReset();
|
||||||
std::cerr << "Failed to load UI: " << e.what() << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
menuManager.startGameFunc = [this]() {
|
void Game::saveInitialState() {
|
||||||
gameState.currentLocationName = "location_dorm";
|
nlohmann::json root;
|
||||||
currentLocation()->scriptEngine.callLocationEnterCallback();
|
gameState.save(root);
|
||||||
this->audioPlayer->crossFadeMusicAsync("audio/obshaga.ogg");
|
const std::string path = "resources/config/start_state.json";
|
||||||
};
|
std::ofstream file(path);
|
||||||
|
if (file.is_open()) {
|
||||||
menuManager.onSaveGame = [this](int slot) { saveGame(slot); };
|
file << root.dump(2);
|
||||||
menuManager.onLoadGame = [this](int slot) { loadGame(slot); };
|
std::cout << "[reset] Saved initial state snapshot to " << path << std::endl;
|
||||||
menuManager.getSlotInfoFunc = [this](int slot) { return readSlotInfo(slot); };
|
} else {
|
||||||
|
std::cerr << "[reset] Could not open " << path << " for writing" << std::endl;
|
||||||
if (audioPlayer->init()) {
|
|
||||||
audioPlayer->setMusicVolume(100);
|
|
||||||
audioPlayer->setSoundVolume(80);
|
|
||||||
std::cout << "Audio initialized successfully" << std::endl;
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
std::cout << "Audio initialization failed" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
menuManager.loadSettings();
|
|
||||||
|
|
||||||
audioPlayer->crossFadeMusicAsync("audio/main menu final.ogg");
|
|
||||||
|
|
||||||
loadingCompleted = true;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Game::createLocations()
|
void Game::createLocations()
|
||||||
@ -561,19 +628,9 @@ namespace ZL
|
|||||||
if (gameState.locations["location_dorm"]->player)
|
if (gameState.locations["location_dorm"]->player)
|
||||||
gameState.locations["location_dorm"]->player->state.onDeathAnimComplete = [this]() { startDarklandsTransition(); };
|
gameState.locations["location_dorm"]->player->state.onDeathAnimComplete = [this]() { startDarklandsTransition(); };
|
||||||
|
|
||||||
gameState.locations["location_dorm"]->onPlayerTaxiRequired = [this]() {
|
|
||||||
menuManager.tutorialShowTaxiHint();
|
|
||||||
};
|
|
||||||
|
|
||||||
gameState.locations["location_dorm"]->state.tutorialInteractiveObjectsLocked = true;
|
gameState.locations["location_dorm"]->state.tutorialInteractiveObjectsLocked = true;
|
||||||
|
|
||||||
menuManager.tutorialUnlockInteractiveObjectsFunc = [this]()
|
rearmDormOneShotCallbacks();
|
||||||
{
|
|
||||||
if (gameState.locations["location_dorm"])
|
|
||||||
{
|
|
||||||
gameState.locations["location_dorm"]->state.tutorialInteractiveObjectsLocked = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
menuManager.callTaxiFunc = [this]()
|
menuManager.callTaxiFunc = [this]()
|
||||||
{
|
{
|
||||||
|
|||||||
14
src/Game.h
14
src/Game.h
@ -101,6 +101,20 @@ namespace ZL {
|
|||||||
SaveSlotInfo readSlotInfo(int slot) const;
|
SaveSlotInfo readSlotInfo(int slot) const;
|
||||||
void createLocations();
|
void createLocations();
|
||||||
|
|
||||||
|
// Reset entry point used by "Start New Game": loads resources/config/start_state.json
|
||||||
|
// into the existing Location objects if present, else falls back to performFullReset().
|
||||||
|
// Must only ever run from a deferred task (see mainThreadHandler.EnqueueMainThreadTask),
|
||||||
|
// never synchronously from a Lua/cutscene call stack.
|
||||||
|
void performResetToInitialState();
|
||||||
|
void performFullReset();
|
||||||
|
void saveInitialState();
|
||||||
|
|
||||||
|
// (Re-)arms one-shot gameplay callbacks (tutorial unlock, taxi request) that
|
||||||
|
// createLocations() wires up but that get consumed (set to nullptr) once used
|
||||||
|
// during play. Must be called after gameState.load() on the fast reset path,
|
||||||
|
// since that path keeps the existing Location objects instead of recreating them.
|
||||||
|
void rearmDormOneShotCallbacks();
|
||||||
|
|
||||||
int64_t getSyncTimeMs();
|
int64_t getSyncTimeMs();
|
||||||
void processTickCount();
|
void processTickCount();
|
||||||
void drawScene();
|
void drawScene();
|
||||||
|
|||||||
@ -320,7 +320,7 @@ namespace ZL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MenuManager::showMainMenu() {
|
void MenuManager::showMainMenu() {
|
||||||
if (onResetGame) onResetGame();
|
gameState_.currentLocationName.clear();
|
||||||
|
|
||||||
audioPlayer_.crossFadeMusicAsync("audio/main menu final.ogg");
|
audioPlayer_.crossFadeMusicAsync("audio/main menu final.ogg");
|
||||||
|
|
||||||
@ -1195,11 +1195,24 @@ namespace ZL {
|
|||||||
uiManager.setNodeVisible("hint6a", false);
|
uiManager.setNodeVisible("hint6a", false);
|
||||||
uiManager.setNodeVisible("hint6aarrow", false);
|
uiManager.setNodeVisible("hint6aarrow", false);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!gameState_.tutorialNeedOpenTaxiScreen)
|
||||||
|
{
|
||||||
|
uiManager.setNodeVisible("hint6a", true);
|
||||||
|
uiManager.setNodeVisible("hint6aarrow", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (gameState_.tutorialJournalScreenOpened)
|
if (gameState_.tutorialJournalScreenOpened)
|
||||||
{
|
{
|
||||||
uiManager.setNodeVisible("hint6b", false);
|
uiManager.setNodeVisible("hint6b", false);
|
||||||
uiManager.setNodeVisible("hint6barrow", false);
|
uiManager.setNodeVisible("hint6barrow", false);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uiManager.setNodeVisible("hint6b", true);
|
||||||
|
uiManager.setNodeVisible("hint6barrow", true);
|
||||||
|
}
|
||||||
if (gameState_.tutorialPhoneChatScreenOpened && gameState_.tutorialJournalScreenOpened) {
|
if (gameState_.tutorialPhoneChatScreenOpened && gameState_.tutorialJournalScreenOpened) {
|
||||||
gameState_.tutorialStep = TutorialStep::Step6;
|
gameState_.tutorialStep = TutorialStep::Step6;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -78,7 +78,6 @@ namespace ZL {
|
|||||||
std::function<void(int)> onSaveGame;
|
std::function<void(int)> onSaveGame;
|
||||||
std::function<void(int)> onLoadGame;
|
std::function<void(int)> onLoadGame;
|
||||||
std::function<SaveSlotInfo(int)> getSlotInfoFunc;
|
std::function<SaveSlotInfo(int)> getSlotInfoFunc;
|
||||||
std::function<void()> onResetGame;
|
|
||||||
|
|
||||||
// Called when a chat message bubble should be shown (text + direction)
|
// Called when a chat message bubble should be shown (text + direction)
|
||||||
void onChatBubbleReady(const std::string& text, bool incoming);
|
void onChatBubbleReady(const std::string& text, bool incoming);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user