Major refactoring - internal server state machine implemented
This commit is contained in:
1 parent
71c654e5b6
commit
66e93d46f4
48 files changed
+3948
-3696
No files matched your search
+70
-44
@@ -2,23 +2,10 @@
|
||||
#include <ctime>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace FRG {
|
||||
|
||||
// Saves made before the tutorial rework kept the dorm and the university tutorials
|
||||
// as two state machines (tutorialStep 0..6 + uniIntTutorialState 0..4); map them
|
||||
// onto the single TutorialStep sequence.
|
||||
static TutorialStep migrateLegacyTutorialStep(int dormStep, int uniIntState)
|
||||
{
|
||||
if (dormStep <= 4) return static_cast<TutorialStep>(dormStep); // Step0..Step4 kept their values
|
||||
switch (uniIntState) {
|
||||
case 2: return TutorialStep::DarklandsHealth; // DarklandsActive
|
||||
case 3: return TutorialStep::DarklandsAttack; // DarklandsStep13
|
||||
case 4: return TutorialStep::Completed; // DarklandsFull
|
||||
default: return TutorialStep::EnterDarklands; // Step10 / Step11
|
||||
}
|
||||
}
|
||||
|
||||
bool GameState::completeTutorialStep(TutorialStep step)
|
||||
{
|
||||
if (tutorialStep != step || step == TutorialStep::Completed) return false;
|
||||
@@ -26,8 +13,29 @@ bool GameState::completeTutorialStep(TutorialStep step)
|
||||
return true;
|
||||
}
|
||||
|
||||
CharacterState* GameState::findCharacter(
|
||||
const std::string& locationId, const std::string& characterId)
|
||||
{
|
||||
auto player = players.find(characterId);
|
||||
if (player != players.end() && player->second) return player->second.get();
|
||||
auto location = locations.find(locationId);
|
||||
return location == locations.end()
|
||||
? nullptr : location->second.findCharacter(characterId, players);
|
||||
}
|
||||
|
||||
const CharacterState* GameState::findCharacter(
|
||||
const std::string& locationId, const std::string& characterId) const
|
||||
{
|
||||
auto player = players.find(characterId);
|
||||
if (player != players.end() && player->second) return player->second.get();
|
||||
auto location = locations.find(locationId);
|
||||
return location == locations.end()
|
||||
? nullptr : location->second.findCharacter(characterId, players);
|
||||
}
|
||||
|
||||
void GameState::save(nlohmann::json& out) const
|
||||
{
|
||||
out["schemaVersion"] = kSchemaVersion;
|
||||
// Timestamp
|
||||
std::time_t now = std::time(nullptr);
|
||||
std::tm* tm = std::localtime(&now);
|
||||
@@ -37,7 +45,7 @@ void GameState::save(nlohmann::json& out) const
|
||||
|
||||
// --- World ---
|
||||
out["campaignId"] = campaignId;
|
||||
out["currentLocationName"] = currentLocationName;
|
||||
out["currentLocationId"] = currentLocationName;
|
||||
out["isDarklands"] = isDarklands;
|
||||
out["isNight"] = isNight;
|
||||
out["isDawn"] = isDawn;
|
||||
@@ -87,16 +95,21 @@ void GameState::save(nlohmann::json& out) const
|
||||
questJournal.save(questJson);
|
||||
out["questJournal"] = std::move(questJson);
|
||||
|
||||
// --- Players ---
|
||||
out["players"] = nlohmann::json::object();
|
||||
for (const auto& [id, player] : players) {
|
||||
if (!player) continue;
|
||||
nlohmann::json playerJson;
|
||||
player->save(playerJson);
|
||||
out["players"][id] = std::move(playerJson);
|
||||
}
|
||||
|
||||
// --- Locations ---
|
||||
// Unloaded locations keep their last stored state; loaded ones are saved live.
|
||||
nlohmann::json locsJson = nlohmann::json::object();
|
||||
for (const auto& [name, stored] : storedLocationStates) locsJson[name] = stored;
|
||||
for (const auto& [name, loc] : locations) {
|
||||
if (loc) {
|
||||
nlohmann::json locJson;
|
||||
loc->save(locJson);
|
||||
locsJson[name] = std::move(locJson);
|
||||
}
|
||||
for (const auto& [name, location] : locations) {
|
||||
nlohmann::json locJson;
|
||||
location.save(locJson);
|
||||
locsJson[name] = std::move(locJson);
|
||||
}
|
||||
out["locations"] = std::move(locsJson);
|
||||
|
||||
@@ -105,12 +118,16 @@ void GameState::save(nlohmann::json& out) const
|
||||
|
||||
void GameState::load(const nlohmann::json& in)
|
||||
{
|
||||
const int schemaVersion = in.value("schemaVersion", -1);
|
||||
if (schemaVersion != kSchemaVersion)
|
||||
throw std::runtime_error(
|
||||
"Unsupported game-state schema version: " +
|
||||
std::to_string(schemaVersion));
|
||||
//logger() << "GameState::load step 1" << std::endl;
|
||||
|
||||
// --- World ---
|
||||
// Literal default: saves made before campaigns existed belong to the main game.
|
||||
campaignId = in.value("campaignId", std::string("main"));
|
||||
currentLocationName = in.value("currentLocationName", currentLocationName);
|
||||
campaignId = in.at("campaignId").get<std::string>();
|
||||
currentLocationName = in.at("currentLocationId").get<std::string>();
|
||||
isDarklands = in.value("isDarklands", isDarklands);
|
||||
isNight = in.value("isNight", isNight);
|
||||
isDawn = in.value("isDawn", isDawn);
|
||||
@@ -142,11 +159,8 @@ void GameState::load(const nlohmann::json& in)
|
||||
//logger() << "GameState::load step 5" << std::endl;
|
||||
|
||||
// --- Tutorial ---
|
||||
if (in.contains("uniIntTutorialState")) {
|
||||
tutorialStep = migrateLegacyTutorialStep(in.value("tutorialStep", 0), in["uniIntTutorialState"].get<int>());
|
||||
} else {
|
||||
tutorialStep = static_cast<TutorialStep>(in.value("tutorialStep", static_cast<int>(tutorialStep)));
|
||||
}
|
||||
tutorialStep = static_cast<TutorialStep>(
|
||||
in.at("tutorialStep").get<int>());
|
||||
tutorialPhoneChatScreenOpened = in.value("tutorialPhoneChatScreenOpened", tutorialPhoneChatScreenOpened);
|
||||
tutorialJournalScreenOpened = in.value("tutorialJournalScreenOpened", tutorialJournalScreenOpened);
|
||||
tutorialNeedOpenTaxiScreen = in.value("tutorialNeedOpenTaxiScreen", tutorialNeedOpenTaxiScreen);
|
||||
@@ -193,20 +207,32 @@ void GameState::load(const nlohmann::json& in)
|
||||
|
||||
logger() << "GameState::load step 10" << std::endl;
|
||||
|
||||
// --- Locations ---
|
||||
// Every location's state is stored (applied when the location loads); the ones
|
||||
// already loaded get it right away.
|
||||
storedLocationStates.clear();
|
||||
if (in.contains("locations") && in["locations"].is_object()) {
|
||||
for (const auto& [name, locJson] : in["locations"].items()) {
|
||||
//logger() << "GameState::load step 10.1: " << name << std::endl;
|
||||
storedLocationStates[name] = locJson;
|
||||
// --- Players ---
|
||||
const auto& playerValues = in.at("players");
|
||||
if (!playerValues.is_object() || !playerValues.contains("player"))
|
||||
throw std::runtime_error("Game state requires players.player");
|
||||
for (const auto& [id, value] : playerValues.items()) {
|
||||
auto& player = players[id];
|
||||
if (!player) player = std::make_shared<CharacterState>();
|
||||
player->load(value);
|
||||
}
|
||||
for (auto it = players.begin(); it != players.end();) {
|
||||
if (!playerValues.contains(it->first)) it = players.erase(it);
|
||||
else ++it;
|
||||
}
|
||||
|
||||
auto it = locations.find(name);
|
||||
if (it != locations.end() && it->second) {
|
||||
it->second->load(locJson);
|
||||
}
|
||||
}
|
||||
// --- Locations ---
|
||||
const auto& locationValues = in.at("locations");
|
||||
if (!locationValues.is_object() ||
|
||||
!locationValues.contains(currentLocationName))
|
||||
throw std::runtime_error(
|
||||
"Game state does not contain current location '" +
|
||||
currentLocationName + "'");
|
||||
for (const auto& [id, value] : locationValues.items())
|
||||
locations[id].load(value);
|
||||
for (auto it = locations.begin(); it != locations.end();) {
|
||||
if (!locationValues.contains(it->first)) it = locations.erase(it);
|
||||
else ++it;
|
||||
}
|
||||
//logger() << "GameState::load step 11" << std::endl;
|
||||
|
||||
|
||||
Reference in new issue
Block a user