#pragma once #include "items/Item.h" #include "quest/QuestJournal.h" #include "LocationGameState.h" #include "ISaveable.h" #include "external/nlohmann/json.hpp" #include #include #include #include namespace FRG { // The tutorial is one linear sequence: each step shows its hint until the player // performs the action, then moves on to the next step (GameState::completeTutorialStep). // Values are saved; AdvanceDialogue..PickUpItem keep the numbers of the old Step0..Step4. enum class TutorialStep { AdvanceDialogue, // dorm: "click to advance the dialogue" RotateCamera, // dorm Walk, // dorm: tap the floor Zoom, // dorm; interactive objects stay locked until this is done PickUpItem, // dorm: take the phone or the journal EnterDarklands, // uni_interior: press the darklands button (once the spell note is picked up) DarklandsHealth, // uni_interior darklands: health bar hint, until the player walks DarklandsExplore, // uni_interior darklands: no hint, until the ghost zone is reached DarklandsAttack, // uni_interior darklands: attack hint, until an enemy is killed Completed // no more hints; a new game starts here when the tutorial is disabled }; struct StoredChatMessage { std::string text; bool incoming; }; struct GameState : public ISaveable { static constexpr int kSchemaVersion = 5; // --- World --- // Campaign being played (see CampaignRegistry). std::string campaignId = "main"; // Pure authoritative location data. Live Location/render/script instances are // private runtime storage in InternalGameServer and never enter GameState. std::unordered_map locations; // Players are campaign-level entities and remain stable across locations. PlayerStateMap players; 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 globalInts; std::unordered_map globalFloats; // --- Quest system --- Quest::QuestJournal questJournal; // --- Tutorial --- TutorialStep tutorialStep = TutorialStep::AdvanceDialogue; // Hints that run alongside the linear steps, until the tutorial is Completed. bool tutorialPhoneChatScreenOpened = false; // "open the messenger" hint is done bool tutorialJournalScreenOpened = false; // "open the journal" hint is done bool tutorialNeedOpenTaxiScreen = false; // "call a taxi" hint is showing bool tutorialTaxiHintDone = false; // a taxi was ordered; never show the hint again bool isTutorialActive() const { return tutorialStep != TutorialStep::Completed; } // Moves to the next step if `step` is the current one; returns whether it did. bool completeTutorialStep(TutorialStep step); CharacterState* findCharacter( const std::string& locationId, const std::string& characterId); const CharacterState* findCharacter( const std::string& locationId, const std::string& characterId) const; // --- Phone / chat --- bool chatUnread[3] = { true, true, true }; std::string chatPreviewMsg[3]; std::vector chatHistory[3]; bool taxiIsCalled = false; void save(nlohmann::json& out) const override; void load(const nlohmann::json& in) override; }; } // namespace FRG