188 lines
6.5 KiB
C++
188 lines
6.5 KiB
C++
#include "save.hpp"
|
|
#include "util.hpp"
|
|
|
|
#include <tinyxml2/tinyxml2.h>
|
|
|
|
#include "../../log.hpp"
|
|
|
|
#include <format>
|
|
|
|
#ifdef __EMSCRIPTEN__
|
|
#include "../../util/web_filesystem.hpp"
|
|
#endif
|
|
|
|
using namespace game::util;
|
|
|
|
using namespace tinyxml2;
|
|
|
|
namespace game::resource::xml
|
|
{
|
|
Save::Save(const std::filesystem::path& path)
|
|
{
|
|
XMLDocument document;
|
|
auto pathString = path.string();
|
|
|
|
// Fail silently if there's no save.
|
|
auto result = document.LoadFile(pathString.c_str());
|
|
|
|
if (result == XML_ERROR_FILE_NOT_FOUND || result == XML_ERROR_FILE_COULD_NOT_BE_OPENED) return;
|
|
|
|
if (result != XML_SUCCESS)
|
|
{
|
|
logger.error(
|
|
std::format("Could not initialize character save file: {} ({})", pathString, document.ErrorStr()));
|
|
return;
|
|
}
|
|
|
|
if (auto root = document.RootElement())
|
|
{
|
|
query_bool_attribute(root, "IsPostgame", &isPostgame);
|
|
query_bool_attribute(root, "IsAlternateSpritesheet", &isAlternateSpritesheet);
|
|
|
|
if (auto element = root->FirstChildElement("Character"))
|
|
{
|
|
|
|
element->QueryFloatAttribute("Weight", &weight);
|
|
element->QueryFloatAttribute("Calories", &calories);
|
|
element->QueryFloatAttribute("Capacity", &capacity);
|
|
element->QueryFloatAttribute("DigestionRate", &digestionRate);
|
|
element->QueryFloatAttribute("EatSpeed", &eatSpeed);
|
|
query_bool_attribute(element, "IsDigesting", &isDigesting);
|
|
element->QueryFloatAttribute("DigestionProgress", &digestionProgress);
|
|
element->QueryIntAttribute("DigestionTimer", &digestionTimer);
|
|
element->QueryFloatAttribute("TotalCaloriesConsumed", &totalCaloriesConsumed);
|
|
element->QueryIntAttribute("TotalFoodItemsEaten", &totalFoodItemsEaten);
|
|
}
|
|
|
|
if (auto element = root->FirstChildElement("Play"))
|
|
{
|
|
element->QueryIntAttribute("TotalPlays", &totalPlays);
|
|
element->QueryIntAttribute("HighScore", &highScore);
|
|
element->QueryIntAttribute("BestCombo", &bestCombo);
|
|
|
|
if (auto child = element->FirstChildElement("Grades"))
|
|
{
|
|
for (auto gradeChild = child->FirstChildElement("Grade"); gradeChild;
|
|
gradeChild = gradeChild->NextSiblingElement("Grade"))
|
|
{
|
|
int id{};
|
|
gradeChild->QueryIntAttribute("ID", &id);
|
|
gradeChild->QueryIntAttribute("Count", &gradeCounts[id]);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (auto element = root->FirstChildElement("Inventory"))
|
|
{
|
|
for (auto child = element->FirstChildElement("Item"); child; child = child->NextSiblingElement("Item"))
|
|
{
|
|
int id{};
|
|
int quantity{};
|
|
child->QueryIntAttribute("ID", &id);
|
|
child->QueryIntAttribute("Quantity", &quantity);
|
|
|
|
inventory[id] = quantity;
|
|
}
|
|
}
|
|
|
|
if (auto element = root->FirstChildElement("Items"))
|
|
{
|
|
for (auto child = element->FirstChildElement("Item"); child; child = child->NextSiblingElement("Item"))
|
|
{
|
|
Item item{};
|
|
child->QueryIntAttribute("ID", &item.id);
|
|
child->QueryIntAttribute("ChewCount", &item.chewCount);
|
|
child->QueryFloatAttribute("PositionX", &item.position.x);
|
|
child->QueryFloatAttribute("PositionY", &item.position.y);
|
|
child->QueryFloatAttribute("VelocityX", &item.velocity.x);
|
|
child->QueryFloatAttribute("VelocityY", &item.velocity.y);
|
|
child->QueryFloatAttribute("Rotation", &item.rotation);
|
|
items.emplace_back(std::move(item));
|
|
}
|
|
}
|
|
}
|
|
|
|
logger.info(std::format("Initialized character save file: {}", pathString));
|
|
|
|
isValid = true;
|
|
}
|
|
|
|
bool Save::is_valid() const { return isValid; }
|
|
|
|
void Save::serialize(const std::filesystem::path& path)
|
|
{
|
|
XMLDocument document;
|
|
auto pathString = path.string();
|
|
|
|
auto element = document.NewElement("Save");
|
|
element->SetAttribute("IsPostgame", isPostgame ? "true" : "false");
|
|
element->SetAttribute("IsAlternateSpritesheet", isAlternateSpritesheet ? "true" : "false");
|
|
|
|
auto characterElement = element->InsertNewChildElement("Character");
|
|
characterElement->SetAttribute("Weight", weight);
|
|
characterElement->SetAttribute("Calories", calories);
|
|
characterElement->SetAttribute("Capacity", capacity);
|
|
characterElement->SetAttribute("DigestionRate", digestionRate);
|
|
characterElement->SetAttribute("EatSpeed", eatSpeed);
|
|
characterElement->SetAttribute("IsDigesting", isDigesting ? "true" : "false");
|
|
characterElement->SetAttribute("DigestionProgress", digestionProgress);
|
|
characterElement->SetAttribute("DigestionTimer", digestionTimer);
|
|
characterElement->SetAttribute("TotalCaloriesConsumed", totalCaloriesConsumed);
|
|
characterElement->SetAttribute("TotalFoodItemsEaten", totalFoodItemsEaten);
|
|
|
|
auto playElement = element->InsertNewChildElement("Play");
|
|
|
|
playElement->SetAttribute("TotalPlays", totalPlays);
|
|
playElement->SetAttribute("HighScore", highScore);
|
|
playElement->SetAttribute("BestCombo", bestCombo);
|
|
|
|
auto gradesElement = playElement->InsertNewChildElement("Grades");
|
|
|
|
for (auto& [i, count] : gradeCounts)
|
|
{
|
|
auto gradeElement = gradesElement->InsertNewChildElement("Grade");
|
|
gradeElement->SetAttribute("ID", i);
|
|
gradeElement->SetAttribute("Count", count);
|
|
}
|
|
|
|
auto inventoryElement = element->InsertNewChildElement("Inventory");
|
|
|
|
for (auto& [id, quantity] : inventory)
|
|
{
|
|
auto itemElement = inventoryElement->InsertNewChildElement("Item");
|
|
|
|
itemElement->SetAttribute("ID", id);
|
|
itemElement->SetAttribute("Quantity", quantity);
|
|
}
|
|
|
|
auto itemsElement = element->InsertNewChildElement("Items");
|
|
|
|
for (auto& item : items)
|
|
{
|
|
auto itemElement = itemsElement->InsertNewChildElement("Item");
|
|
|
|
itemElement->SetAttribute("ID", item.id);
|
|
itemElement->SetAttribute("ChewCount", item.chewCount);
|
|
itemElement->SetAttribute("PositionX", item.position.x);
|
|
itemElement->SetAttribute("PositionY", item.position.y);
|
|
itemElement->SetAttribute("VelocityX", item.velocity.x);
|
|
itemElement->SetAttribute("VelocityY", item.velocity.y);
|
|
itemElement->SetAttribute("Rotation", item.rotation);
|
|
}
|
|
|
|
document.InsertFirstChild(element);
|
|
|
|
if (document.SaveFile(pathString.c_str()) != XML_SUCCESS)
|
|
{
|
|
logger.error(std::format("Failed to save character save file: {} ({})", pathString, document.ErrorStr()));
|
|
return;
|
|
}
|
|
|
|
logger.info(std::format("Saved character save file: {}", pathString));
|
|
|
|
#ifdef __EMSCRIPTEN__
|
|
web_filesystem::flush_async();
|
|
#endif
|
|
}
|
|
}
|