Major refactoring - internal server state machine implemented

This commit is contained in:
Vladislav Khorev committed 2026-10-29 00:09:16 +06:00
1 parent 71c654e5b6
commit 66e93d46f4
48 files changed
+3948 -3696

No files matched your search

+131 -114
View File
@@ -201,9 +201,9 @@ namespace FRG {
}
#endif
MenuManager::MenuManager(Renderer& iRenderer, GameState& gameState, AudioPlayerAsync& audioPlayer) :
MenuManager::MenuManager(Renderer& iRenderer, IGameServer& server, AudioPlayerAsync& audioPlayer) :
renderer(iRenderer),
gameState_(gameState),
server_(server),
audioPlayer_(audioPlayer)
{
//Localization
@@ -338,9 +338,7 @@ namespace FRG {
saveGameScreenRoot = loadLocalizedUi("resources/w/ui/screen_save.json", zipFile);
}
void MenuManager::setup(Inventory& inv, const std::string& zipFile) {
inventory = &inv;
void MenuManager::setup(const std::string& zipFile) {
loadUiRoots(zipFile);
texObjectiveCompleted_ = renderer.textureManager.LoadFromPng("resources/w/ui/img/journal/quest_objective_completed.png", zipFile, true);
@@ -360,19 +358,6 @@ namespace FRG {
languageLoadingRu = renderer.textureManager.LoadFromPng("resources/loading_land003_ru.png", zipFile);
languageLoadingEn = renderer.textureManager.LoadFromPng("resources/loading_land003_en.png", zipFile);
gameState_.questJournal.onQuestUnlocked = [this](const std::string& id) {
const Quest::QuestState* q = gameState_.questJournal.findQuest(id);
if (q) showToast(toastTextureQuestNew, q->definition.title);
};
gameState_.questJournal.onQuestCompleted = [this](const std::string& id) {
const Quest::QuestState* q = gameState_.questJournal.findQuest(id);
if (q) showToast(toastTextureQuestCompleted, q->definition.title);
};
gameState_.questJournal.onQuestFailed = [this](const std::string& id) {
const Quest::QuestState* q = gameState_.questJournal.findQuest(id);
if (q) showToast(toastTextureQuestFailed, q->definition.title);
};
const std::string imgDir = "resources/w/ui/img/phone/";
texBubbleInCenter_ = renderer.textureManager.LoadFromPng(imgDir + "bubble_in_center.png", zipFile, true);
texBubbleInLT_ = renderer.textureManager.LoadFromPng(imgDir + "bubble_in_corner_left_top.png", zipFile, true);
@@ -419,9 +404,10 @@ namespace FRG {
}
std::shared_ptr<UiNode> MenuManager::gameplayHudRoot() const {
const std::string& loc = gameState_.currentLocationName;
if (loc == "uni_exterior") return gameState_.isDarklands ? hudUniExtDarkRoot : hudUniExtRoot;
if (loc == "uni_interior") return gameState_.isDarklands ? hudUniIntDarkRoot : hudUniIntRoot;
const auto snapshot = server_.snapshot();
const std::string& loc = snapshot->currentLocationId;
if (loc == "uni_exterior") return snapshot->isDarklands ? hudUniExtDarkRoot : hudUniExtRoot;
if (loc == "uni_interior") return snapshot->isDarklands ? hudUniIntDarkRoot : hudUniIntRoot;
return hudDormRoot; // dorm, or a new game before the location is set
}
@@ -434,21 +420,22 @@ namespace FRG {
}
void MenuManager::refreshHudVisibility() {
if (!inventory) return; // update() already ticks during loading, before setup()
const auto itemIds = server_.snapshot()->inventoryItemIds;
const TutorialStep step = gameState_.tutorialStep;
const bool tutorial = gameState_.isTutorialActive();
const bool hasPhone = inventory->hasItem("phone");
const bool hasJournal = inventory->hasItem("journal");
const bool hasNote = inventory->hasItem("note_spell");
const bool dark = gameState_.isDarklands;
const auto snapshot = server_.snapshot();
const TutorialStep step = snapshot->tutorialStep;
const bool tutorial = snapshot->isTutorialActive();
const bool hasPhone = std::find(itemIds.begin(), itemIds.end(), "phone") != itemIds.end();
const bool hasJournal = std::find(itemIds.begin(), itemIds.end(), "journal") != itemIds.end();
const bool hasNote = std::find(itemIds.begin(), itemIds.end(), "note_spell") != itemIds.end();
const bool dark = snapshot->isDarklands;
auto show = [this](std::initializer_list<const char*> names, bool visible) {
for (const char* name : names) uiManager.setNodeVisible(name, visible);
};
// --- HUD buttons: present while the matching item is in the inventory ---
show({ "inventoryButton" }, inventory->getCount() > 0);
show({ "inventoryButton" }, !itemIds.empty());
show({ "phoneButton" }, hasPhone && !dark);
show({ "journalButton" }, hasJournal);
show({ "darklandsButton" }, hasNote && !dark);
@@ -466,9 +453,9 @@ namespace FRG {
show({ "hint4" }, step == TutorialStep::Zoom);
show({ "hint5" }, step == TutorialStep::PickUpItem);
// ...then the hints for the picked-up items, which can be done in any order.
const bool chatHint = tutorial && !gameState_.tutorialPhoneChatScreenOpened;
const bool journalHint = tutorial && !gameState_.tutorialJournalScreenOpened;
const bool taxiHint = tutorial && gameState_.tutorialNeedOpenTaxiScreen;
const bool chatHint = tutorial && !snapshot->tutorialPhoneChatScreenOpened;
const bool journalHint = tutorial && !snapshot->tutorialJournalScreenOpened;
const bool taxiHint = tutorial && snapshot->tutorialNeedOpenTaxiScreen;
show({ "hint6a", "hint6aarrow" }, hasPhone && chatHint && !taxiHint);
show({ "hint6b", "hint6barrow" }, hasJournal && journalHint);
show({ "hint7", "hint7arrow" }, taxiHint);
@@ -485,7 +472,7 @@ namespace FRG {
}
void MenuManager::showMainMenu() {
gameState_.currentLocationName.clear();
server_.submit(SetCurrentLocationCommand{""});
audioPlayer_.crossFadeMusicAsync("music/main menu final.ogg");
lastMusicTrack = "music/main menu final.ogg";
@@ -546,7 +533,6 @@ namespace FRG {
}
void MenuManager::showMenuAfterGameCompleted() {
//gameState_.currentLocationName.clear();
audioPlayer_.crossFadeMusicAsync("music/main menu final.ogg");
lastMusicTrack = "music/main menu final.ogg";
@@ -1060,8 +1046,8 @@ namespace FRG {
steps.push_back([this]() {
ItemRegistry::instance().loadFromJson("resources/config/items.json", CONST_ZIP_FILE);
if (const CampaignConfig* campaign = CampaignRegistry::instance().find(gameState_.campaignId))
gameState_.questJournal.reloadDefinitionsPreservingState(campaign->questsPath, CONST_ZIP_FILE);
if (const CampaignConfig* campaign = CampaignRegistry::instance().find(server_.snapshot()->campaignId))
server_.submit(ReloadQuestDefinitionsCommand{campaign->questsPath, CONST_ZIP_FILE});
});
steps.push_back([this]() {
@@ -1197,7 +1183,7 @@ namespace FRG {
uiManager.setButtonCallback("inventoryMain", [this](const std::string&) {});
const auto& items = inventory->getItems();
const auto items = inventoryItems();
/*logger() << "Item count: " << items.size() << std::endl;
for (int x = 0; x < items.size(); x++)
{
@@ -1236,7 +1222,7 @@ namespace FRG {
void MenuManager::selectInventoryItem(int index) {
logger() << "MenuManager::selectInventoryItem: " << index << std::endl;
const auto& items = inventory->getItems();
const auto items = inventoryItems();
if (index < 0 || index >= static_cast<int>(items.size())) return;
if (isPortraitMode() && inventoryMenuStackDepth_ == 1) {
@@ -1288,7 +1274,10 @@ namespace FRG {
void MenuManager::openQuestJournal() {
uiState_ = GameUiState::QuestJournal;
gameState_.tutorialJournalScreenOpened = true;
auto tutorial = server_.snapshot();
server_.submit(SetTutorialFlagsCommand{
tutorial->tutorialPhoneChatScreenOpened, true,
tutorial->tutorialNeedOpenTaxiScreen, tutorial->tutorialTaxiHintDone});
journalMenuStackDepth_ = 1;
const bool portrait = isPortraitMode();
@@ -1380,9 +1369,10 @@ namespace FRG {
audioPlayer_.playSoundAsync("resources/audio/537061__imafoley__message-pop-sound.ogg");
});
if (gameState_.isNight)
const auto timeState = server_.snapshot();
if (timeState->isNight)
{
if (gameState_.isDawn)
if (timeState->isDawn)
{
uiManager.setNodeVisible("phoneTimeDay", false);
uiManager.setNodeVisible("phoneTimeNight", false);
@@ -1418,51 +1408,67 @@ namespace FRG {
});
uiManager.setButtonCallback("phoneMain", [this](const std::string&) {});
uiManager.setTextButtonCallback("chat1button", [this](const std::string&) {
gameState_.chatUnread[0] = false;
setChatUnread(0, false);
openPhoneChatFromList(0, isPortraitMode() ? verticalPhoneChat1Root : phoneChat1Root);
audioPlayer_.playSoundAsync("resources/audio/537061__imafoley__message-pop-sound.ogg");
});
uiManager.setTextButtonCallback("chat2button", [this](const std::string&) {
gameState_.chatUnread[1] = false;
setChatUnread(1, false);
openPhoneChatFromList(1, isPortraitMode() ? verticalPhoneChat2Root : phoneChat2Root);
audioPlayer_.playSoundAsync("resources/audio/537061__imafoley__message-pop-sound.ogg");
});
uiManager.setTextButtonCallback("chat3button", [this](const std::string&) {
gameState_.chatUnread[2] = false;
setChatUnread(2, false);
openPhoneChatFromList(2, isPortraitMode() ? verticalPhoneChat3Root : phoneChat3Root);
audioPlayer_.playSoundAsync("resources/audio/537061__imafoley__message-pop-sound.ogg");
});
}
void MenuManager::refreshChatUnreadIndicators() {
const auto state = server_.snapshot();
static const char* kUnreadNodes[3] = { "chat1Unread", "chat2Unread", "chat3Unread" };
static const char* kMsgNodes[3] = { "chat1msg", "chat2msg", "chat3msg" };
for (int i = 0; i < 3; ++i) {
uiManager.setNodeVisible(kUnreadNodes[i], gameState_.chatUnread[i]);
if (!gameState_.chatPreviewMsg[i].empty())
uiManager.setText(kMsgNodes[i], gameState_.chatPreviewMsg[i]);
uiManager.setNodeVisible(kUnreadNodes[i], state->chatUnread[i]);
if (!state->chatPreview[i].empty())
uiManager.setText(kMsgNodes[i], state->chatPreview[i]);
}
}
void MenuManager::setChatUnread(int chatIndex, bool unread, const std::string& previewMsg) {
if (chatIndex < 0 || chatIndex > 2) return;
gameState_.chatUnread[chatIndex] = unread;
const auto state = server_.snapshot();
std::string preview = state->chatPreview[chatIndex];
if (!previewMsg.empty()) {
gameState_.chatPreviewMsg[chatIndex] = trimChatPreview(previewMsg);
} else if (!unread && !gameState_.chatHistory[chatIndex].empty()) {
gameState_.chatPreviewMsg[chatIndex] = trimChatPreview(gameState_.chatHistory[chatIndex].back().text);
preview = trimChatPreview(previewMsg);
} else if (!unread && !state->chatHistory[chatIndex].empty()) {
preview = trimChatPreview(state->chatHistory[chatIndex].back().text);
}
server_.submit(SetChatCommand{chatIndex, unread, std::move(preview)});
}
void MenuManager::spendMoney(int amount) {
gameState_.money -= amount;
server_.submit(SpendMoneyCommand{amount});
}
std::vector<Item> MenuManager::inventoryItems() const {
std::vector<Item> items;
for (const std::string& id : server_.snapshot()->inventoryItemIds) {
if (const Item* item = ItemRegistry::instance().findById(id)) items.push_back(*item);
}
return items;
}
bool MenuManager::hasInventoryItem(const std::string& id) const {
const auto& ids = server_.snapshot()->inventoryItemIds;
return std::find(ids.begin(), ids.end(), id) != ids.end();
}
void MenuManager::openPhoneBank() {
currentPhoneSubScreen_ = PhoneSubScreen::Bank;
uiManager.pushMenuFromSavedRoot(isPortraitMode() ? verticalPhoneBankRoot : phoneBankRoot);
uiManager.setText("balanceText", formatMoney(gameState_.money));
uiManager.setText("balanceText", formatMoney(server_.snapshot()->money));
uiManager.setButtonCallback("phoneExitButton", [this](const std::string&) {
closePhoneEntirely();
@@ -1492,13 +1498,15 @@ namespace FRG {
});
uiManager.setButtonCallback("videoSkip", [this](const std::string&) {
closePhoneEntirely();
if (gameState_.isNight)
const auto state = server_.snapshot();
if (state->isNight)
{
startDialogueFunc("dialog_video002");
}
else
{
auto day = gameState_.globalInts["day"];
auto dayIt = state->globalInts.find("day");
const int day = dayIt == state->globalInts.end() ? 0 : dayIt->second;
if (day == 0)
{
startDialogueFunc("dialog_video003");
@@ -1513,10 +1521,11 @@ namespace FRG {
}
void MenuManager::openPhoneTaxi() {
if (gameState_.currentLocationName == "uni_interior") {
const std::string location = server_.snapshot()->currentLocationId;
if (location == "uni_interior") {
closePhoneEntirely();
if (startDialogueFunc) startDialogueFunc("dialog_taxi003");
} else if (gameState_.currentLocationName == "uni_exterior") {
} else if (location == "uni_exterior") {
currentPhoneSubScreen_ = PhoneSubScreen::MapUni;
openPhoneMapScreen(isPortraitMode() ? verticalPhoneMapUniRoot : phoneMapUniRoot);
} else {
@@ -1539,17 +1548,18 @@ namespace FRG {
audioPlayer_.playSoundAsync("resources/audio/537061__imafoley__message-pop-sound.ogg");
});
uiManager.setButtonCallback("mapGo", [this](const std::string&) {
gameState_.tutorialNeedOpenTaxiScreen = false;
gameState_.tutorialTaxiHintDone = true;
auto state = server_.snapshot();
server_.submit(SetTutorialFlagsCommand{
state->tutorialPhoneChatScreenOpened, state->tutorialJournalScreenOpened,
false, true});
if (gameState_.taxiIsCalled == false)
if (!state->taxiIsCalled)
{
gameState_.taxiIsCalled = true;
if (callTaxiFunc)
{
callTaxiFunc();
}
gameState_.money -= 500;
server_.submit(SetTaxiCalledCommand{true});
server_.submit(StartTaxiCommand{state->currentLocationId});
server_.submit(SetTriggerZoneEnabledCommand{
state->currentLocationId, "taxi_zone001", false});
server_.submit(SpendMoneyCommand{500});
closePhoneEntirely();
if (startDialogueFunc) startDialogueFunc("dialog_taxi002");
audioPlayer_.playSoundAsync("resources/audio/537061__imafoley__message-pop-sound.ogg");
@@ -1569,7 +1579,10 @@ namespace FRG {
activeChatIndex_ = chatIndex;
currentPhoneSubScreen_ = PhoneSubScreen::Chat;
phoneChatVisibleBubbles_.clear();
gameState_.tutorialPhoneChatScreenOpened = true;
auto tutorial = server_.snapshot();
server_.submit(SetTutorialFlagsCommand{
true, tutorial->tutorialJournalScreenOpened,
tutorial->tutorialNeedOpenTaxiScreen, tutorial->tutorialTaxiHintDone});
uiManager.pushMenuFromSavedRoot(chatRoot);
// Build a dedicated chatUiManager root sized to the FBO chat area
@@ -1616,11 +1629,7 @@ namespace FRG {
}
void MenuManager::returnToPhoneChatList() {
if (activeChatIndex_ >= 0 && activeChatIndex_ <= 2
&& !gameState_.chatHistory[activeChatIndex_].empty()) {
gameState_.chatPreviewMsg[activeChatIndex_] =
trimChatPreview(gameState_.chatHistory[activeChatIndex_].back().text);
}
if (activeChatIndex_ >= 0 && activeChatIndex_ <= 2) setChatUnread(activeChatIndex_, false);
activeChatIndex_ = -1;
currentPhoneSubScreen_ = PhoneSubScreen::ChatList;
phoneChatVisibleBubbles_.clear();
@@ -1634,11 +1643,7 @@ namespace FRG {
void MenuManager::closePhoneEntirely() {
if (activeChatIndex_ >= 0 && activeChatIndex_ <= 2
&& !gameState_.chatHistory[activeChatIndex_].empty()) {
gameState_.chatPreviewMsg[activeChatIndex_] =
trimChatPreview(gameState_.chatHistory[activeChatIndex_].back().text);
}
if (activeChatIndex_ >= 0 && activeChatIndex_ <= 2) setChatUnread(activeChatIndex_, false);
activeChatIndex_ = -1;
currentPhoneSubScreen_ = PhoneSubScreen::None;
uiState_ = GameUiState::Gameplay;
@@ -1699,8 +1704,11 @@ namespace FRG {
void MenuManager::tutorialShowTaxiHint()
{
logger() << "tutorialShowTaxiHint" << std::endl;
if (!gameState_.isTutorialActive() || gameState_.tutorialTaxiHintDone) return;
gameState_.tutorialNeedOpenTaxiScreen = true; // cleared when the taxi is ordered
auto state = server_.snapshot();
if (!state->isTutorialActive() || state->tutorialTaxiHintDone) return;
server_.submit(SetTutorialFlagsCommand{
state->tutorialPhoneChatScreenOpened, state->tutorialJournalScreenOpened,
true, state->tutorialTaxiHintDone});
refreshHudVisibility();
}
@@ -1723,16 +1731,17 @@ namespace FRG {
void MenuManager::onLocationChanged(const std::string& locationName) {
if (uiState_ != GameUiState::Gameplay) return;
gameState_.currentLocationName = locationName;
server_.submit(SetCurrentLocationCommand{locationName});
const auto state = server_.snapshot();
showGameplayHud();
if (locationName == "uni_exterior" || locationName == "uni_interior") {
if (gameState_.isDarklands)
if (state->isDarklands)
{
audioPlayer_.crossFadeMusicAsync("music/bishkek fight calm.ogg");
lastMusicTrack = "music/bishkek fight calm.ogg";
}
else if (gameState_.isNight)
else if (state->isNight)
{
audioPlayer_.crossFadeMusicAsync("music/bishkek night.ogg");
lastMusicTrack = "music/bishkek night.ogg";
@@ -1743,7 +1752,7 @@ namespace FRG {
lastMusicTrack = "music/bishkek univer day.ogg";
}
} else {
if (gameState_.isNight)
if (state->isNight)
{
audioPlayer_.crossFadeMusicAsync("music/bishkek night.ogg");
lastMusicTrack = "music/bishkek night.ogg";
@@ -1759,13 +1768,16 @@ namespace FRG {
void MenuManager::onItemPickedUp(const std::string& itemId) {
// The new item's HUD button appears through refreshHudVisibility().
if (itemId == "phone" || itemId == "journal")
gameState_.completeTutorialStep(TutorialStep::PickUpItem);
server_.submit(CompleteTutorialStepCommand{TutorialStep::PickUpItem});
refreshHudVisibility();
}
void MenuManager::refreshQuestJournalUi() {
visibleQuestIds.clear();
auto quests = gameState_.questJournal.getVisibleQuests();
const auto state = server_.snapshot();
std::vector<const Quest::QuestState*> quests;
quests.reserve(state->quests.size());
for (const auto& quest : state->quests) quests.push_back(&quest);
std::sort(quests.begin(), quests.end(), [](const Quest::QuestState* a, const Quest::QuestState* b) {
const int pa = questStatusPriority(a->status);
@@ -1840,7 +1852,10 @@ namespace FRG {
}
selectedQuestIndex = index;
Quest::QuestState* quest = gameState_.questJournal.findQuest(visibleQuestIds[index]);
const auto state = server_.snapshot();
const Quest::QuestState* quest = nullptr;
for (const auto& candidate : state->quests)
if (candidate.definition.id == visibleQuestIds[index]) quest = &candidate;
if (!quest) {
return;
}
@@ -1888,7 +1903,7 @@ namespace FRG {
}
void MenuManager::openPortraitInventoryItem(int index) {
const auto& items = inventory->getItems();
const auto items = inventoryItems();
inventorySelectedIndex_ = index;
inventoryMenuStackDepth_ = 2;
uiManager.pushMenuFromSavedRoot(portraitInventoryItemRoot);
@@ -1943,7 +1958,10 @@ namespace FRG {
uiManager.setButtonCallback("journalMain", [this](const std::string&) {});
Quest::QuestState* quest = gameState_.questJournal.findQuest(visibleQuestIds[index]);
const auto state = server_.snapshot();
const Quest::QuestState* quest = nullptr;
for (const auto& candidate : state->quests)
if (candidate.definition.id == visibleQuestIds[index]) quest = &candidate;
if (!quest) return;
const auto& def = quest->definition;
@@ -2070,7 +2088,8 @@ namespace FRG {
phoneChatVisibleBubbles_.clear();
if (chatIndex < 0 || chatIndex > 2) return;
for (const auto& msg : gameState_.chatHistory[chatIndex]) {
const auto state = server_.snapshot();
for (const auto& msg : state->chatHistory[chatIndex]) {
const bool inc = msg.incoming;
const std::string nodeName = chatUiManager.addChatBubble(
"chatMessagesContainer", msg.text, inc,
@@ -2100,11 +2119,7 @@ namespace FRG {
void MenuManager::onChatBubbleReady(const std::string& text, bool incoming) {
if (activeChatIndex_ < 0) return;
auto& history = gameState_.chatHistory[activeChatIndex_];
if (static_cast<int>(history.size()) >= 5) {
history.erase(history.begin());
}
history.push_back({ text, incoming });
server_.submit(AppendChatMessageCommand{activeChatIndex_, text, incoming});
if (uiState_ != GameUiState::PhoneScreen) return;
@@ -2143,8 +2158,8 @@ namespace FRG {
{
logger() << "MenuManager::setDarklandsMode called" << std::endl;
if (enabled && gameState_.currentLocationName == "uni_interior" && inventory->hasItem("note_spell"))
gameState_.completeTutorialStep(TutorialStep::EnterDarklands);
if (enabled && server_.snapshot()->currentLocationId == "uni_interior" && hasInventoryItem("note_spell"))
server_.submit(CompleteTutorialStepCommand{TutorialStep::EnterDarklands});
if (uiState_ == GameUiState::Gameplay)
showGameplayHud(); // darklands has its own HUD layouts
@@ -2152,30 +2167,31 @@ namespace FRG {
void MenuManager::onPlayerStartedWalking()
{
if (gameState_.currentLocationName == "uni_interior" && gameState_.isDarklands)
gameState_.completeTutorialStep(TutorialStep::DarklandsHealth);
const auto state = server_.snapshot();
if (state->currentLocationId == "uni_interior" && state->isDarklands)
server_.submit(CompleteTutorialStepCommand{TutorialStep::DarklandsHealth});
}
void MenuManager::advanceUniIntDarklandsHud()
{
// The ghost zone can be reached without a floor tap (DarklandsHealth never completed).
if (gameState_.tutorialStep == TutorialStep::DarklandsHealth
|| gameState_.tutorialStep == TutorialStep::DarklandsExplore)
gameState_.tutorialStep = TutorialStep::DarklandsAttack;
const TutorialStep step = server_.snapshot()->tutorialStep;
if (step == TutorialStep::DarklandsHealth || step == TutorialStep::DarklandsExplore)
server_.submit(SetTutorialStepCommand{TutorialStep::DarklandsAttack});
}
void MenuManager::onEnemyKilledInUniInterior()
{
// Every location's enemies report here; only the university interior drives the tutorial.
if (gameState_.currentLocationName != "uni_interior") return;
const auto state = server_.snapshot();
if (state->currentLocationId != "uni_interior") return;
// The last tutorial step; like the attack hint, also ends the earlier darklands hints.
if (gameState_.tutorialStep >= TutorialStep::DarklandsHealth)
gameState_.tutorialStep = TutorialStep::Completed;
if (state->tutorialStep >= TutorialStep::DarklandsHealth)
server_.submit(SetTutorialStepCommand{TutorialStep::Completed});
}
void MenuManager::updateHealthBar(float hp, float maxHp) {
gameState_.playerHp = hp;
gameState_.playerMaxHp = maxHp;
server_.submit(SetPlayerHealthCommand{hp, maxHp});
applyCurrentHealthBar();
}
@@ -2191,21 +2207,22 @@ namespace FRG {
cutsceneHudActive_ = false;
topUiManager.replaceRoot(nullptr);
if (uiState_ == GameUiState::Gameplay)
onLocationChanged(gameState_.currentLocationName);
onLocationChanged(server_.snapshot()->currentLocationId);
}
void MenuManager::applyCurrentHealthBar() {
logger() << "MenuManager::applyCurrentHealthBar called step 1" << std::endl;
logger() << "currentPlayerMaxHp_ is " << gameState_.playerMaxHp << std::endl;
if (gameState_.playerMaxHp <= 0.f) return;
const auto state = server_.snapshot();
logger() << "currentPlayerMaxHp_ is " << state->playerMaxHp << std::endl;
if (state->playerMaxHp <= 0.f) return;
logger() << "MenuManager::applyCurrentHealthBar called step 2" << std::endl;
logger() << "currentPlayerHp_ is " << gameState_.playerHp << std::endl;
logger() << "currentPlayerHp_ is " << state->playerHp << std::endl;
const float fraction = std::clamp(gameState_.playerHp / gameState_.playerMaxHp, 0.f, 1.f);
const float fraction = std::clamp(state->playerHp / state->playerMaxHp, 0.f, 1.f);
uiManager.setSliderValue("healthBarFill", fraction);
std::string hpText = std::to_string(static_cast<int>(gameState_.playerHp)) + "/" +
std::to_string(static_cast<int>(gameState_.playerMaxHp));
std::string hpText = std::to_string(static_cast<int>(state->playerHp)) + "/" +
std::to_string(static_cast<int>(state->playerMaxHp));
if (hpText.size() < 7) hpText.insert(0, 7 - hpText.size(), ' ');
uiManager.setText("healthValue", hpText);
}
@@ -2447,7 +2464,7 @@ namespace FRG {
openInventory();
const int itemCount = static_cast<int>(inventory->getItems().size());
const int itemCount = static_cast<int>(inventoryItems().size());
if (isPortraitMode() && wasOnItemDetail &&
savedIndex >= 0 && savedIndex < itemCount) {
selectInventoryItem(savedIndex);