code:
#include "Database/Serializable/ArmorsSerializer.hpp"

void ArmorsSerializer::serialize(std::ofstream& os) const {
  try {
    nlohmann::ordered_json data;

    if (m_data.armors().empty()) {
      data.push_back(nullptr);
      os << data.dump(4);
      return;
    }

    for (const Armor& armor : m_data.armors()) {
      if (armor.isValid()) {
        data.push_back(armor);
      } else {
        data.push_back(nullptr);
      }
    }

    os << data.dump(4);
  } catch (...) {}
}

void ArmorsSerializer::deserialize(std::ifstream& is) {
  try {
    nlohmann::ordered_json data = nlohmann::ordered_json::parse(is);
    m_data.armors().reserve(data.size());
    m_data.disableSignals();

    int i = 0;
    for (const auto& [_, value] : data.items()) {
      Armor& armor = m_data.armors().emplace_back();
      armor.m_isValid = value != nullptr;
      if (armor.isValid()) {
        value.get_to(armor);
      } else {
        armor.m_id = i;
      }
      ++i;
    }
    m_data.enableSignals();
  } catch (...) {}
}


code:
#pragma once
#include "Database/Armors.hpp"
#include "Database/Serializable/ISerializable.hpp"

class ArmorsSerializer final : public ITypedSerializable<Armors> {
public:
  explicit ArmorsSerializer(const std::string_view filepath)
  : ITypedSerializable(filepath) {}
  ArmorsSerializer(const Armors& armors, const std::string_view filepath)
  : ITypedSerializable(armors, filepath) {}
  void serialize(std::ofstream& os) const override;
  void deserialize(std::ifstream& is) override;
};

repo_name: Antidote/openrpgeditor

license: mit


code:
#include "Database/Serializable/ClassesSerializer.hpp"

void ClassesSerializer::serialize(std::ofstream& os) const {
  try {
    nlohmann::ordered_json data;

    if (m_data.classes().empty()) {
      data.push_back(nullptr);
      os << data.dump(4);
      return;
    }

    for (const Class& cls : m_data.classes()) {
      if (cls.isValid()) {
        data.push_back(cls);
      } else {
        data.push_back(nullptr);
      }
    }

    os << data.dump(4);
  } catch (...) {}
}

void ClassesSerializer::deserialize(std::ifstream& is) {
  try {
    nlohmann::ordered_json data = nlohmann::ordered_json::parse(is);
    m_data.classes().reserve(data.size());
    m_data.disableSignals();

    int i = 0;
    for (const auto& [_, value] : data.items()) {
      Class& cls = m_data.classes().emplace_back();
      cls.m_isValid = value != nullptr;
      cls.disableSignals();
      if (cls.isValid()) {
        value.get_to(cls);
      } else {
        cls.m_id = i;
      }
      cls.enableSignals();
      ++i;
    }
    m_data.disableSignals();
  } catch (...) {}
}


code:
#pragma once

#include "Database/Classes.hpp"
#include "Database/Serializable/ISerializable.hpp"

class ClassesSerializer final : public ITypedSerializable<Classes> {
public:
  explicit ClassesSerializer(const std::string_view filepath)
  : ITypedSerializable(filepath) {}
  ClassesSerializer(const Classes& classes, const std::string_view filepath)
  : ITypedSerializable(classes, filepath) {}
  void serialize(std::ofstream& os) const override;
  void deserialize(std::ifstream& is) override;
};


code:
#include "Database/Serializable/CommonEventsSerializer.hpp"

void CommonEventsSerializer::serialize(std::ofstream& os) const {
  try {
    nlohmann::ordered_json data;

    if (m_data.events().empty()) {
      data.push_back(nullptr);
      os << data.dump(4);
      return;
    }
    for (const auto& event : m_data.events()) {
      data.push_back(event);
    }

    os << data.dump(4);
  } catch (...) {}
}

void CommonEventsSerializer::deserialize(std::ifstream& is) {
  try {
    nlohmann::ordered_json data = nlohmann::ordered_json::parse(is);
    m_data.events().reserve(data.size());
    m_data.disableSignals();

    for (const auto& [_, value] : data.items()) {
      value.get_to(m_data.events().emplace_back());
    }
    m_data.enableSignals();
  } catch (...) {}
}


code:
#pragma once

#include "Database/CommonEvents.hpp"
#include "Database/Serializable/ISerializable.hpp"

class CommonEventsSerializer final : public ITypedSerializable<CommonEvents> {
public:
  explicit CommonEventsSerializer(const std::string_view filepath)
  : ITypedSerializable(filepath) {}
  CommonEventsSerializer(const CommonEvents& commonEvents, const std::string_view filepath)
  : ITypedSerializable(commonEvents, filepath) {}
  void serialize(std::ofstream& os) const override;
  void deserialize(std::ifstream& is) override;
};


code:
#include "Database/Serializable/EnemiesSerializer.hpp"

void EnemiesSerializer::serialize(std::ofstream& os) const {
  try {
    nlohmann::ordered_json data;

    if (m_data.enemies().empty()) {
      data.push_back(nullptr);
      os << data.dump(4);
      return;
    }

    for (const Enemy& enemy : m_data.enemies()) {
      if (enemy.isValid()) {
        data.push_back(enemy);
      } else {
        data.push_back(nullptr);
      }
    }

    os << data.dump(4);
  } catch (...) {}
}

void EnemiesSerializer::deserialize(std::ifstream& is) {
  try {
    nlohmann::ordered_json data = nlohmann::ordered_json::parse(is);
    m_data.enemies().reserve(data.size());
    m_data.disableSignals();

    int i = 0;
    for (const auto& [_, value] : data.items()) {
      Enemy& enemy = m_data.enemies().emplace_back();
      enemy.m_isValid = value != nullptr;
      if (enemy.isValid()) {
        value.get_to(enemy);
      } else {
        enemy.m_id = i;
      }
      ++i;
    }
    m_data.enableSignals();
  } catch (...) {}
}


code:
#pragma once
#include "Database/Enemies.hpp"
#include "Database/Serializable/ISerializable.hpp"

class EnemiesSerializer final : public ITypedSerializable<Enemies> {
public:
  explicit EnemiesSerializer(const std::string_view filepath)
  : ITypedSerializable(filepath) {}
  EnemiesSerializer(const Enemies& enemies, const std::string_view filepath)
  : ITypedSerializable(enemies, filepath) {}
  void serialize(std::ofstream& os) const override;
  void deserialize(std::ifstream& is) override;
};


code:
#include "Database/Serializable/FileQueue.hpp"

#include "Database/Log.hpp"

#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <random>

#include <filesystem>
#include <iostream>
#include <magic_enum/magic_enum.hpp>
#include <ranges>

FileQueue::FileQueue() {}
FileQueue::~FileQueue() {}

void FileQueue::setBasepath(const std::string_view basePath) { m_basePath = basePath; }

std::string FileQueue::getBasepath() const { return m_basePath; }

bool FileQueue::canInsert(const std::shared_ptr<ISerializable>& fileData, const std::deque<Task>& queue) {
  if (const auto& it = std::ranges::find_if(queue, [&fileData](const Task& task) { return task.filepath() == fileData->filepath(); }); it != queue.end()) {
    RPGM_WARN("Adding a new {} task for {} which is already in the queue for {}!", magic_enum::enum_name(fileData->operation()), fileData->filepath(), magic_enum::enum_name(it->operation()));
    return false;
  }
  return true;
}
bool FileQueue::enqueue(const std::shared_ptr<ISerializable>& fileData, const TaskCallback& callback, const bool sync) {
  if (m_basePath.empty()) {
    return false;
  }

  if (sync) {
    if (fileData->operation() == ISerializable::Operation::Read) {
      processReadTask(fileData, callback);
    } else if (fileData->operation() == ISerializable::Operation::Write) {
      processWriteTask(fileData, callback);
    }
    return true;
  }

  if (!m_taskQueue.empty() && m_currentOperation != fileData->operation()) {
    if (canInsert(fileData, m_pendingQueue)) {
      m_pendingQueue.emplace_back(fileData, callback);
      m_totalTasks++;
      return true;
    }
  }
  if (!canInsert(fileData, m_taskQueue))
    return false;

  m_taskQueue.emplace_back(fileData, callback);
  m_totalTasks++;
  m_currentOperation = fileData->operation();

  return true;
}

float FileQueue::progress() const { return m_totalTasks == 0 ? 0.f : (static_cast<float>(m_completedTasks) / static_cast<float>(m_totalTasks) * 100.f); }

void FileQueue::reset() {
  m_totalTasks = 0;
  m_completedTasks = 0;
  m_taskQueue = {};
}

void FileQueue::proc() {
  // If we run out of tasks switch to the pending tasks, if any
  if (m_taskQueue.empty() && !m_pendingQueue.empty()) {
    m_taskQueue = m_pendingQueue;
    m_pendingQueue = {};
    m_currentOperation = m_pendingOperation;
    m_totalTasks = m_taskQueue.size();
  }

  if (m_taskQueue.empty()) {
    return;
  }

  const Task task = std::move(m_taskQueue.front());
  m_taskQueue.pop_front();
  m_currentFilePath = task.filepath();
  // Process the task based on the specific queue type
  if (task.operation() == ISerializable::Operation::Read) {
    processReadTask(task.fileData(), task.callback());
  } else if (task.operation() == ISerializable::Operation::Write) {
    processWriteTask(task.fileData(), task.callback());
  }
  m_completedTasks++;
}

namespace fs = std::filesystem;

std::string generateRandomFilename(const std::string& prefix, const int length = 10) {
  static const std::string allowedChars =
      "abcdefghijklmnopqrstuvwxyz"
      "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
      "0123456789";

  std::random_device rd;
  std::mt19937 generator(rd());
  std::uniform_int_distribution<int> distribution(0, static_cast<int>(allowedChars.size()) - 1);

  std::string randomString;
  for (int i = 0; i < length; ++i) {
    randomString += allowedChars[distribution(generator)];
  }

  return prefix + randomString;
}

void FileQueue::processReadTask(const std::shared_ptr<ISerializable>& fileData, const TaskCallback& callback) const {

  const fs::path filePath = fs::path(m_basePath) / fs::path(fileData->filepath());
  if (std::ifstream inFile(filePath, std::ios::binary); inFile) {
    fileData->deserialize(inFile);
    callback(fileData);
  } else {
    RPGM_WARN("Error: Failed to open file for deserialization: {}", fileData->filepath());
  }
}

void FileQueue::processWriteTask(const std::shared_ptr<ISerializable>& fileData, const TaskCallback& callback) const {
  if (m_basePath.empty()) {
    return;
  }

  // Generate a temporary file path in the base directory
  const fs::path tempFilePath = fs::temp_directory_path() / fs::path(generateRandomFilename("ore"));
  const fs::path finalFilePath = fs::path(m_basePath) / fs::path(fileData->filepath());

  if (std::ofstream outFile(tempFilePath, std::ios::binary); outFile) {
    // Serialize data to the temporary file
    fileData->serialize(outFile);

    // Now that the data is serialized, attempt to move the temporary file to the final destination
    outFile.close();

    // Rename the temporary file to the target destination if the write succeeded
    try {
      if (fs::exists(finalFilePath)) {
        fs::remove(finalFilePath); // Remove the existing file if it exists
      }

      fs::rename(tempFilePath, finalFilePath);
      callback(fileData); // Callback with the successfully serialized data
    } catch (const std::exception& e) {
      try {
        if (fs::exists(finalFilePath)) {
          fs::remove(finalFilePath); // Remove the existing file if it exists
        }
        fs::copy(tempFilePath, finalFilePath); // Rename temp file to the target file
        fs::remove(tempFilePath);
        callback(fileData); // Callback with the successfully serialized data
      } catch (const std::exception& e) {}
    }
  }
}


code:
#pragma once

#include "Database/Serializable/ISerializable.hpp"
#include "Database/Serializable/Task.hpp"
#include <condition_variable>
#include <deque>
#include <functional>
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include <ranges>
// Base class for a threaded file queue (for serialization and deserialization).
class FileQueue {
public:
  using TaskCallback = std::function<void(std::shared_ptr<ISerializable>)>;

  FileQueue();
  virtual ~FileQueue();

  // Set the base path for file operations
  void setBasepath(const std::string_view basePath);

  // Get the current base path
  std::string getBasepath() const;

  // Enqueue a task to the queue
  bool enqueue(const std::shared_ptr<ISerializable>& fileData, const TaskCallback& callback, bool sync = false);

  // Reset and clear the queue, also aborts current tasks
  void reset();

  bool hasTasks() const { return !m_taskQueue.empty(); }

  // Get the current progress (0 to 100)
  float progress() const;

  int currentTaskNumber() const { return m_completedTasks + 1; }
  int totalTasks() const { return m_totalTasks; }
  std::string_view currentTaskName() const { return m_currentFilePath; }

  bool hasTask(const std::string_view path) const {
    return std::ranges::find_if(m_taskQueue, [&path](const Task& task) { return task.filepath() == path; }) != m_taskQueue.end();
  }

  // The worker thread method
  void proc();

  static FileQueue& instance() {
    static FileQueue instance;
    return instance;
  }

  ISerializable::Operation operationType() const { return m_currentOperation; }

protected:
  // The worker loop, to be implemented in subclasses (serialization/deserialization)
  void processReadTask(const std::shared_ptr<ISerializable>& fileData, const TaskCallback& callback) const;
  void processWriteTask(const std::shared_ptr<ISerializable>& fileData, const TaskCallback& callback) const;
  static bool canInsert(const std::shared_ptr<ISerializable>& fileData, const std::deque<Task>& queue);

  // Current base path for file operations
  std::string m_basePath;

  // Progress tracking
  int m_totalTasks = 0;
  int m_completedTasks = 0;

  // Task queue
  std::deque<Task> m_taskQueue;
  std::deque<Task> m_pendingQueue;

  ISerializable::Operation m_currentOperation = ISerializable::Operation::Read;
  ISerializable::Operation m_pendingOperation = ISerializable::Operation::Write;
  std::string m_currentFilePath;
};


code:
#pragma once

#include <fstream>
#include <string>

class ISerializable {
public:
  enum class Operation {
    Read,
    Write,
  };
  explicit ISerializable(const std::string_view filepath, Operation operation)
  : m_filepath(filepath)
  , m_operation(operation) {}

  virtual ~ISerializable() = default;

  // Virtual methods for serialization and deserialization
  virtual void serialize(std::ofstream& outFile) const = 0; // Serialize the object to an output stream
  virtual void deserialize(std::ifstream& inFile) = 0;      // Deserialize the object from an input stream

  // Getter for the file path associated with this object
  [[nodiscard]] virtual std::string_view filepath() const { return m_filepath; }

  [[nodiscard]] Operation operation() const { return m_operation; }

private:
  std::string m_filepath;
  Operation m_operation;
};

template <typename T>
class ITypedSerializable : public ISerializable {
public:
  explicit ITypedSerializable(const std::string_view filepath)
  : ISerializable(filepath, Operation::Read) {}
  ITypedSerializable(const T& data, const std::string_view filepath)
  : ISerializable(filepath, Operation::Write)
  , m_data(data) {}

  const T& data() const { return m_data; }

protected:
  T m_data;
};


code:
#include "Database/Serializable/ItemsSerializer.hpp"

void ItemsSerializer::serialize(std::ofstream& os) const {
  try {
    nlohmann::ordered_json data;

    if (m_data.items().empty()) {
      data.push_back(nullptr);
      os << data.dump(4);
      return;
    }

    for (const Item& item : m_data.items()) {
      if (item.isValid()) {
        data.push_back(item);
      } else {
        data.push_back(nullptr);
      }
    }

    os << data.dump(4);
  } catch (...) {}
}

void ItemsSerializer::deserialize(std::ifstream& is) {
  try {
    nlohmann::ordered_json data = nlohmann::ordered_json::parse(is);
    m_data.items().reserve(data.size());
    m_data.disableSignals();

    int i = 0;
    for (const auto& [_, value] : data.items()) {
      Item& item = m_data.items().emplace_back();
      item.m_isValid = value != nullptr;
      if (item.isValid()) {
        value.get_to(item);
      } else {
        item.m_id = i;
      }
      ++i;
      m_data.enableSignals();
    }
  } catch (...) {}
}


code:
#pragma once
#include "Database/Items.hpp"
#include "Database/Serializable/ISerializable.hpp"

class ItemsSerializer final : public ITypedSerializable<Items> {
public:
  explicit ItemsSerializer(const std::string_view filepath)
  : ITypedSerializable(filepath) {}
  ItemsSerializer(const Items& items, const std::string_view filepath)
  : ITypedSerializable(items, filepath) {}
  void serialize(std::ofstream& os) const override;
  void deserialize(std::ifstream& is) override;
};


code:
#include "Database/Serializable/MapInfosSerializer.hpp"
#include "Database/Serializable/MapSerializer.hpp"

void MapInfosSerializer::serialize(std::ofstream& outFile) const {
  nlohmann::ordered_json data{nullptr};

  for (int i = 1; i < m_data.m_mapinfos.size(); ++i) {
    data.push_back(m_data.m_mapinfos[i]);
  }

  outFile << data.dump(4);
}

void MapInfosSerializer::deserialize(std::ifstream& inFile) {
  try {
    nlohmann::ordered_json data = nlohmann::ordered_json::parse(inFile);
    m_data.mapInfos().reserve(data.size());

    for (const auto& [_, value] : data.items()) {
      auto& mapinfo = m_data.mapInfos().emplace_back();
      value.get_to(mapinfo);
    }
    m_data.mapInfos()[0].emplace();
    m_data.mapInfos().shrink_to_fit();
    m_data.buildTree();
  } catch (...) {}
}


code:
#pragma once
#include "Database/MapInfos.hpp"
#include "Database/Serializable/ISerializable.hpp"

class MapInfosSerializer final : public ITypedSerializable<MapInfos> {
public:
  explicit MapInfosSerializer(const std::string_view filepath)
  : ITypedSerializable(filepath) {}
  MapInfosSerializer(const MapInfos& mapInfos, const std::string_view filepath)
  : ITypedSerializable(mapInfos, filepath) {}

  void serialize(std::ofstream& outFile) const override;
  void deserialize(std::ifstream& inFile) override;
};


code:
#include "Database/Serializable/MapSerializer.hpp"

#include <iostream>

void MapSerializer::serialize(std::ofstream& os) const {
  try {
    if (os.is_open()) {
      const nlohmann::ordered_json data = m_data;
      os << data.dump(4);
    }
  } catch (const std::exception& e) { std::cerr << "Error while writing to file \"" << filepath() << "\" " << e.what() << std::endl; }
}

void MapSerializer::deserialize(std::ifstream& is) {
  try {
    if (is.is_open()) {
      const auto data = nlohmann::ordered_json::parse(is);
      data.get_to(m_data);
      m_data.m_isValid = true;
    }
  } catch (const std::exception& e) { std::cerr << "Error while reading from file \"" << filepath() << "\"" << e.what() << std::endl; }
}


code:
#pragma once

#include "Database/Map.hpp"
#include "Database/Serializable/ISerializable.hpp"

class MapSerializer final : public ITypedSerializable<Map> {
public:
  explicit MapSerializer(const std::string_view filename, const int mapId)
  : ITypedSerializable(filename)
  , m_mapId(mapId) {}
  MapSerializer(const Map& map, const int mapId, const std::string_view filename)
  : ITypedSerializable(map, filename)
  , m_mapId(mapId) {}

  void serialize(std::ofstream& os) const override;
  void deserialize(std::ifstream& is) override;

  [[nodiscard]] int mapId() const { return m_mapId; }

private:
  int m_mapId = 0;
};


code:
#include "Database/Serializable/SkillsSerializer.hpp"

#include <iostream>

void SkillsSerializer::serialize(std::ofstream& os) const {
  try {
    nlohmann::ordered_json data;
    if (m_data.skills().empty()) {
      data.push_back(nullptr);
      os << data.dump(4);
      return;
    }

    for (const Skill& skill : m_data.skills()) {
      if (skill.isValid()) {
        data.push_back(skill);
      } else {
        data.push_back(nullptr);
      }
    }

    os << data.dump(4);
  } catch (...) {}
}

void SkillsSerializer::deserialize(std::ifstream& is) {
  try {
    nlohmann::ordered_json data = nlohmann::ordered_json::parse(is);
    m_data.skills().reserve(data.size());

    int i = 0;
    for (const auto& [_, value] : data.items()) {
      Skill& skill = m_data.skills().emplace_back();
      skill.m_isValid = value != nullptr;
      if (skill.isValid()) {
        value.get_to(skill);
      } else {
        skill.m_id = i;
      }
      ++i;
    }
  } catch (...) {}
}


code:
#pragma once

#include "Database/Serializable/ISerializable.hpp"
#include "Database/Skills.hpp"

class SkillsSerializer final : public ITypedSerializable<Skills> {
public:
  explicit SkillsSerializer(const std::string_view filepath)
  : ITypedSerializable(filepath) {}
  SkillsSerializer(const Skills& actors, const std::string_view filepath)
  : ITypedSerializable(actors, filepath) {}
  void serialize(std::ofstream& os) const override;
  void deserialize(std::ifstream& is) override;
};


code:
#include "Database/Serializable/StatesSerializer.hpp"

void StatesSerializer::serialize(std::ofstream& os) const {
  try {
    nlohmann::ordered_json data;
    if (m_data.states().empty()) {
      data.push_back(nullptr);
      os << data.dump(4);
      return;
    }

    for (const State& state : m_data.states()) {
      if (state.isValid()) {
        data.push_back(state);
      } else {
        data.push_back(nullptr);
      }
    }

    os << data.dump(4);
  } catch (...) {}
}

void StatesSerializer::deserialize(std::ifstream& is) {
  try {
    nlohmann::ordered_json data = nlohmann::ordered_json::parse(is);
    m_data.states().reserve(data.size());
    m_data.disableSignals();

    int i = 0;
    for (const auto& [_, value] : data.items()) {
      State& state = m_data.states().emplace_back();
      state.m_isValid = value != nullptr;
      state.disableSignals();
      if (state.isValid()) {
        value.get_to(state);
      } else {
        state.m_id = i;
      }
      ++i;
      state.enableSignals();
    }
    m_data.disableSignals();
  } catch (...) {}
}


code:
#pragma once
#include "Database/Serializable/ISerializable.hpp"
#include "Database/States.hpp"

class StatesSerializer final : public ITypedSerializable<States> {
public:
  explicit StatesSerializer(const std::string_view filepath)
  : ITypedSerializable(filepath) {}
  StatesSerializer(const States& states, const std::string_view filepath)
  : ITypedSerializable(states, filepath) {}
  void serialize(std::ofstream& os) const override;
  void deserialize(std::ifstream& is) override;
};


code:
#include "SystemSerializer.hpp"

void SystemSerializer::serialize(std::ofstream& os) const {
  // try {
  nlohmann::ordered_json data = m_data;
  if (m_data.m_encryptionKey && (m_data.m_hasEncryptedAudio || m_data.m_hasEncryptedImages)) {
    data["hasEncryptedImages"] = m_data.m_hasEncryptedImages ? *m_data.m_hasEncryptedImages : false;
    data["hasEncryptedAudio"] = m_data.m_hasEncryptedAudio ? *m_data.m_hasEncryptedAudio : false;
    data["encryptionKey"] = *m_data.m_encryptionKey;
  }
  os << data.dump(4);
  // } catch (...) {}
}

void SystemSerializer::deserialize(std::ifstream& is) {
  try {
    nlohmann::ordered_json data = nlohmann::ordered_json::parse(is);
    m_data = data.get<System>();
    data["hasEncryptedImages"].get_to(m_data.m_hasEncryptedImages);
    data["hasEncryptedAudio"].get_to(m_data.m_hasEncryptedAudio);
    data["encryptionKey"].get_to(m_data.m_encryptionKey);
  } catch (...) {}
}


code:
#pragma once

#include "Database/Serializable/ISerializable.hpp"
#include "Database/System.hpp"

class SystemSerializer final : public ITypedSerializable<System> {
public:
  explicit SystemSerializer(const std::string_view filepath)
  : ITypedSerializable(filepath) {}
  SystemSerializer(const System& system, const std::string_view filepath)
  : ITypedSerializable(system, filepath) {}
  void serialize(std::ofstream& os) const override;
  void deserialize(std::ifstream& is) override;
};


code:
#pragma once

#include <functional>
#include <memory>

// Forward declaration of ISerializable, which represents a file data object.
class ISerializable;

// Task metadata and associated callback
class Task {
public:
  Task() = default;
  Task(const Task&) = default;
  Task& operator=(const Task&) = default;
  Task(Task&&) = default;
  Task& operator=(Task&&) = default;

  Task(const std::shared_ptr<ISerializable>& fileData, const std::function<void(std::shared_ptr<ISerializable>)>& callback)
  : m_fileData(fileData)
  , m_callback(callback) {}

  std::string_view filepath() const { return m_fileData->filepath(); }
  std::shared_ptr<ISerializable> fileData() const { return m_fileData; }
  ISerializable::Operation operation() const { return m_fileData->operation(); }
  std::function<void(std::shared_ptr<ISerializable>)> callback() const { return m_callback; }

private:
  std::shared_ptr<ISerializable> m_fileData;                      // The file data (serializable object)
  std::function<void(std::shared_ptr<ISerializable>)> m_callback; // Callback when the task is complete
};


code:
#include "Database/Serializable/TilesetsSerializer.hpp"

void TilesetsSerializer::serialize(std::ofstream& os) const {
  try {
    nlohmann::ordered_json data;
    if (m_data.tilesets().empty()) {
      data.push_back(nullptr);
      os << data.dump(4);
      return;
    }

    for (const Tileset& tileset : m_data.tilesets()) {
      if (tileset.isValid()) {
        data.push_back(tileset);
      } else {
        data.push_back(nullptr);
      }
    }

    os << data.dump(4);
  } catch (...) {}
}

void TilesetsSerializer::deserialize(std::ifstream& is) {
  try {
    nlohmann::ordered_json data = nlohmann::ordered_json::parse(is);
    m_data.tilesets().reserve(data.size());

    int i = 0;
    for (const auto& [_, value] : data.items()) {
      Tileset& tileset = m_data.tilesets().emplace_back();
      tileset.m_isValid = value != nullptr;
      if (tileset.isValid()) {
        value.get_to(tileset);
      } else {
        tileset.m_id = i;
      }
      ++i;
    }
  } catch (...) {}
}


code:
#pragma once

#include "Database/Serializable/ISerializable.hpp"
#include "Database/Tilesets.hpp"

class TilesetsSerializer final : public ITypedSerializable<Tilesets> {
public:
  explicit TilesetsSerializer(const std::string_view filepath)
  : ITypedSerializable(filepath) {}
  TilesetsSerializer(const Tilesets& tilesets, const std::string_view filepath)
  : ITypedSerializable(tilesets, filepath) {}
  void serialize(std::ofstream& os) const override;
  void deserialize(std::ifstream& is) override;
};


code:
#include "Database/Serializable/TroopsSerializer.hpp"

void TroopsSerializer::serialize(std::ofstream& os) const {
  try {
    nlohmann::ordered_json data;

    if (m_data.troops().empty()) {
      data.push_back(nullptr);
      os << data.dump(4);
      return;
    }

    for (const Troop& troop : m_data.troops()) {
      if (troop.isValid()) {
        data.push_back(troop);
      } else {
        data.push_back(nullptr);
      }
    }

    os << data.dump(4);
  } catch (...) {}
}

void TroopsSerializer::deserialize(std::ifstream& is) {
  try {
    nlohmann::ordered_json data = nlohmann::json::parse(is);
    m_data.troops().reserve(data.size());

    int i = 0;
    for (const auto& [_, value] : data.items()) {
      Troop& troop = m_data.troops().emplace_back();
      troop.m_isValid = value != nullptr;
      if (troop.m_isValid) {
        value.get_to(troop);
      } else {
        troop.m_id = i;
      }
      ++i;
    }
  } catch (...) {}
}


code:
#pragma once
#include "Database/Serializable/ISerializable.hpp"
#include "Database/Troops.hpp"

class TroopsSerializer final : public ITypedSerializable<Troops> {
public:
  explicit TroopsSerializer(const std::string_view filepath)
  : ITypedSerializable(filepath) {}
  TroopsSerializer(const Troops& troops, const std::string_view filepath)
  : ITypedSerializable(troops, filepath) {}
  void serialize(std::ofstream& os) const override;
  void deserialize(std::ifstream& is) override;
};


code:
#include "Database/Serializable/WeaponsSerializer.hpp"

void WeaponsSerializer::serialize(std::ofstream& os) const {
  try {
    nlohmann::ordered_json data;

    if (m_data.weapons().empty()) {
      data.push_back(nullptr);
      os << data.dump(4);
      return;
    }

    for (const Weapon& weapon : m_data.weapons()) {
      if (weapon.isValid()) {
        data.push_back(weapon);
      } else {
        data.push_back(nullptr);
      }
    }

    os << data.dump(4);
  } catch (...) {}
}

void WeaponsSerializer::deserialize(std::ifstream& is) {
  try {
    nlohmann::ordered_json data = nlohmann::ordered_json::parse(is);
    m_data.weapons().reserve(data.size());

    int i = 0;
    for (const auto& [_, value] : data.items()) {
      Weapon& weapon = m_data.weapons().emplace_back();
      weapon.m_isValid = value != nullptr;
      if (weapon.isValid()) {
        value.get_to(weapon);
      } else {
        weapon.m_id = i;
      }
      ++i;
    }
  } catch (...) {}
}


code:
#pragma once
#include "Database/Serializable/ISerializable.hpp"
#include "Database/Weapons.hpp"

class WeaponsSerializer final : public ITypedSerializable<Weapons> {
public:
  explicit WeaponsSerializer(const std::string_view filepath)
  : ITypedSerializable(filepath) {}
  WeaponsSerializer(const Weapons& weapons, const std::string_view filepath)
  : ITypedSerializable(weapons, filepath) {}
  void serialize(std::ofstream& os) const override;
  void deserialize(std::ifstream& is) override;
};


code:
#include "Skill.hpp"

Skill::Skill(const Skill& other)
: IModifiable(other)
, m_id(other.m_id)
, m_animationId(other.m_animationId)
, m_damage(other.m_damage)
, m_description(other.m_description)
, m_effects(other.m_effects)
, m_iconIndex(other.m_iconIndex)
, m_message1(other.m_message1)
, m_message2(other.m_message2)
, m_mpCost(other.m_mpCost)
, m_name(other.m_name)
, m_note(other.m_note)
, m_occasion(other.m_occasion)
, m_hitType(other.m_hitType)
, m_repeats(other.m_repeats)
, m_requiredWtypeId1(other.m_requiredWtypeId1)
, m_requiredWtypeId2(other.m_requiredWtypeId2)
, m_scope(other.m_scope)
, m_speed(other.m_speed)
, m_stypeId(other.m_stypeId)
, m_successRate(other.m_successRate)
, m_tpCost(other.m_tpCost)
, m_tpGain(other.m_tpGain)
, m_oldid(other.m_oldid)
, m_olddescription(other.m_olddescription)
, m_oldeffects(other.m_oldeffects)
, m_oldiconIndex(other.m_oldiconIndex)
, m_oldmessage1(other.m_oldmessage1)
, m_oldmessage2(other.m_oldmessage2)
, m_oldmpCost(other.m_oldmpCost)
, m_oldname(other.m_oldname)
, m_oldnote(other.m_oldnote)
, m_oldoccasion(other.m_oldoccasion)
, m_oldhitType(other.m_oldhitType)
, m_oldrepeats(other.m_oldrepeats)
, m_oldrequiredWtypeId1(other.m_oldrequiredWtypeId1)
, m_oldrequiredWtypeId2(other.m_oldrequiredWtypeId2)
, m_oldscope(other.m_oldscope)
, m_oldspeed(other.m_oldspeed)
, m_oldstypeId(other.m_oldstypeId)
, m_oldsuccessRate(other.m_oldsuccessRate)
, m_oldtpCost(other.m_oldtpCost)
, m_oldtpGain(other.m_oldtpGain)
, m_isValid(other.m_isValid) {}
Skill& Skill::operator=(const Skill& other) {
  IModifiable::operator=(other);
  m_id = other.m_id;
  m_animationId = other.m_animationId;
  m_damage = other.m_damage;
  m_description = other.m_description;
  m_effects = other.m_effects;
  m_iconIndex = other.m_iconIndex;
  m_message1 = other.m_message1;
  m_message2 = other.m_message2;
  m_mpCost = other.m_mpCost;
  m_name = other.m_name;
  m_note = other.m_note;
  m_occasion = other.m_occasion;
  m_hitType = other.m_hitType;
  m_repeats = other.m_repeats;
  m_requiredWtypeId1 = other.m_requiredWtypeId1;
  m_requiredWtypeId2 = other.m_requiredWtypeId2;
  m_scope = other.m_scope;
  m_speed = other.m_speed;
  m_stypeId = other.m_stypeId;
  m_successRate = other.m_successRate;
  m_tpCost = other.m_tpCost;
  m_tpGain = other.m_tpGain;
  m_oldid = other.m_oldid;
  m_olddescription = other.m_olddescription;
  m_oldeffects = other.m_oldeffects;
  m_oldiconIndex = other.m_oldiconIndex;
  m_oldmessage1 = other.m_oldmessage1;
  m_oldmessage2 = other.m_oldmessage2;
  m_oldmpCost = other.m_oldmpCost;
  m_oldname = other.m_oldname;
  m_oldnote = other.m_oldnote;
  m_oldoccasion = other.m_oldoccasion;
  m_oldhitType = other.m_oldhitType;
  m_oldrepeats = other.m_oldrepeats;
  m_oldrequiredWtypeId1 = other.m_oldrequiredWtypeId1;
  m_oldrequiredWtypeId2 = other.m_oldrequiredWtypeId2;
  m_oldscope = other.m_oldscope;
  m_oldspeed = other.m_oldspeed;
  m_oldstypeId = other.m_oldstypeId;
  m_oldsuccessRate = other.m_oldsuccessRate;
  m_oldtpCost = other.m_oldtpCost;
  m_oldtpGain = other.m_oldtpGain;
  m_isValid = other.m_isValid;
  return *this;
}
Skill::Skill(Skill&& other) noexcept
: IModifiable(std::move(other))
, m_id(other.m_id)
, m_animationId(other.m_animationId)
, m_damage(std::move(other.m_damage))
, m_description(std::move(other.m_description))
, m_effects(std::move(other.m_effects))
, m_iconIndex(other.m_iconIndex)
, m_message1(other.m_message1)
, m_message2(other.m_message2)
, m_mpCost(other.m_mpCost)
, m_name(std::move(other.m_name))
, m_note(std::move(other.m_note))
, m_occasion(other.m_occasion)
, m_hitType(other.m_hitType)
, m_repeats(other.m_repeats)
, m_requiredWtypeId1(other.m_requiredWtypeId1)
, m_requiredWtypeId2(other.m_requiredWtypeId2)
, m_scope(other.m_scope)
, m_speed(other.m_speed)
, m_stypeId(other.m_stypeId)
, m_successRate(other.m_successRate)
, m_tpCost(other.m_tpCost)
, m_tpGain(other.m_tpGain)
, m_oldid(other.m_oldid)
, m_oldanimationId(other.m_oldanimationId)
, m_olddamage(std::move(other.m_olddamage))
, m_olddescription(other.m_olddescription)
, m_oldeffects(std::move(other.m_oldeffects))
, m_oldiconIndex(other.m_oldiconIndex)
, m_oldmessage1(std::move(other.m_oldmessage1))
, m_oldmessage2(std::move(other.m_oldmessage2))
, m_oldmpCost(other.m_oldmpCost)
, m_oldname(other.m_oldname)
, m_oldnote(other.m_oldnote)
, m_oldoccasion(other.m_oldoccasion)
, m_oldhitType(other.m_oldhitType)
, m_oldrepeats(other.m_oldrepeats)
, m_oldrequiredWtypeId1(other.m_oldrequiredWtypeId1)
, m_oldrequiredWtypeId2(other.m_oldrequiredWtypeId2)
, m_oldscope(other.m_oldscope)
, m_oldspeed(other.m_oldspeed)
, m_oldstypeId(other.m_oldstypeId)
, m_oldsuccessRate(other.m_oldsuccessRate)
, m_oldtpCost(other.m_oldtpCost)
, m_oldtpGain(other.m_oldtpGain)
, m_isValid(other.m_isValid) {}

Skill& Skill::operator=(Skill&& other) noexcept {
  IModifiable::operator=(std::move(other));
  m_id = other.m_id;
  m_animationId = other.m_animationId;
  m_damage = std::move(other.m_damage);
  m_description = std::move(other.m_description);
  m_effects = std::move(other.m_effects);
  m_iconIndex = other.m_iconIndex;
  m_message1 = std::move(other.m_message1);
  m_message2 = std::move(other.m_message2);
  m_mpCost = other.m_mpCost;
  m_name = std::move(other.m_name);
  m_note = std::move(other.m_note);
  m_occasion = other.m_occasion;
  m_hitType = other.m_hitType;
  m_repeats = other.m_repeats;
  m_requiredWtypeId1 = other.m_requiredWtypeId1;
  m_requiredWtypeId2 = other.m_requiredWtypeId2;
  m_scope = other.m_scope;
  m_speed = other.m_speed;
  m_stypeId = other.m_stypeId;
  m_successRate = other.m_successRate;
  m_tpCost = other.m_tpCost;
  m_tpGain = other.m_tpGain;
  m_oldid = other.m_oldid;
  m_oldanimationId = other.m_oldanimationId;
  m_olddamage = std::move(other.m_olddamage);
  m_olddescription = std::move(other.m_olddescription);
  m_oldeffects = std::move(other.m_oldeffects);
  m_oldiconIndex = other.m_oldiconIndex;
  m_oldmessage1 = std::move(other.m_oldmessage1);
  m_oldmessage2 = std::move(other.m_oldmessage2);
  m_oldmpCost = other.m_oldmpCost;
  m_oldname = std::move(other.m_oldname);
  m_oldnote = std::move(other.m_oldnote);
  m_oldoccasion = other.m_oldoccasion;
  m_oldhitType = other.m_oldhitType;
  m_oldrepeats = other.m_oldrepeats;
  m_oldrequiredWtypeId1 = other.m_oldrequiredWtypeId1;
  m_oldrequiredWtypeId2 = other.m_oldrequiredWtypeId2;
  m_oldscope = other.m_oldscope;
  m_oldspeed = other.m_oldspeed;
  m_stypeId = other.m_stypeId;
  m_successRate = other.m_successRate;
  m_tpCost = other.m_tpCost;
  m_tpGain = other.m_tpGain;
  m_isValid = other.m_isValid;
  return *this;
}

[[nodiscard]] int Skill::id() const { return m_id; }
void Skill::setId(const int id) {
  if (m_id == id) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(id);
  m_id = id;
  if (!signalsDisabled()) {
  emit_signal(idModified(), this, id);
  }
  setModified(true);
}

[[nodiscard]] int Skill::animationId() const { return m_animationId; }
void Skill::setAnimationId(int id) {
  if (m_animationId == id) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(animationId);
  m_animationId = id;
  if (!signalsDisabled()) {
  emit_signal(animationIdModified(), this, id);
  }
  setModified();
}

Damage& Skill::damage() { return m_damage; }
void Skill::setDamage(const Damage& damage) {
  if (m_damage == damage) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(damage);
  m_damage = damage;
  if (!signalsDisabled()) {
  emit_signal(damageModified(), this, damage);
  }
  setModified();
}

[[nodiscard]] const std::string& Skill::description() const { return m_description; }
void Skill::setDescription(const std::string& description) {
  if (m_description == description) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(description);
  m_description = description;
  if (!signalsDisabled()) {
  emit_signal(descriptionModified(), this, m_description);
  }
  setModified();
}

std::vector<Effect>& Skill::effects() { return m_effects; }
void Skill::setEffects(const std::vector<Effect>& effects) {
  if (m_effects == effects) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(effects);
  m_effects = effects;
  if (!signalsDisabled()) {
  emit_signal(effectsModified(), this, m_effects);
  }
  setModified();
}

[[nodiscard]] int Skill::iconIndex() const { return m_iconIndex; }
void Skill::setIconIndex(int iconIndex) {
  if (m_iconIndex == iconIndex) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(iconIndex);
  m_iconIndex = iconIndex;
  if (!signalsDisabled()) {
  emit_signal(iconIndexModified(), this, m_iconIndex);
  }
  setModified();
}

[[nodiscard]] const std::string& Skill::message1() const { return m_message1; }
void Skill::setMessage1(const std::string& message1) {
  if (m_message1 == message1) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(message1);
  m_message1 = message1;
  if (!signalsDisabled()) {
  emit_signal(message1Modified(), this, m_message1);
  }
  setModified();
}

[[nodiscard]] const std::string& Skill::message2() const { return m_message2; }
void Skill::setMessage2(const std::string& message2) {
  if (m_message2 == message2) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(message2);
  m_message2 = message2;
  if (!signalsDisabled()) {
  emit_signal(message2Modified(), this, m_message2);
  }
  setModified();
}

[[nodiscard]] int Skill::mpCost() const { return m_mpCost; }
void Skill::setMpCost(int mpCost) {
  if (m_mpCost == mpCost) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(mpCost);
  m_mpCost = mpCost;
  if (!signalsDisabled()) {
  emit_signal(mpCostModified(), this, m_mpCost);
  }
  setModified();
}

[[nodiscard]] const std::string& Skill::name() const { return m_name; }
void Skill::setName(const std::string& name) {
  if (m_name == name) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(name);
  m_name = name;
  if (!signalsDisabled()) {
  emit_signal(nameModified(), this, m_name);
  }
  setModified();
}

[[nodiscard]] const std::string& Skill::note() const { return m_note; }
void Skill::setNote(const std::string& note) {
  if (m_note == note) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(note);
  m_note = note;
  if (!signalsDisabled()) {
  emit_signal(noteModified(), this, m_note);
  }
  setModified();
}

[[nodiscard]] Occasion Skill::occasion() const { return m_occasion; }
void Skill::setOccasion(Occasion occasion) {
  if (m_occasion == occasion) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(occasion);
  m_occasion = occasion;
  if (!signalsDisabled()) {
  emit_signal(occasionModified(), this, m_occasion);
  }
  setModified();
}

[[nodiscard]] HitType Skill::hitType() const { return m_hitType; }
void Skill::setHitType(HitType hitType) {
  if (m_hitType == hitType) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(hitType);
  m_hitType = hitType;
  if (!signalsDisabled()) {
  emit_signal(hitTypeModified(), this, m_hitType);
  }
  setModified();
}

[[nodiscard]] int Skill::repeats() const { return m_repeats; }
void Skill::setRepeats(int repeats) {
  if (m_repeats == repeats) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(repeats);
  m_repeats = repeats;
  if (!signalsDisabled()) {
  emit_signal(repeatsModified(), this, m_repeats);
  }
  setModified();
}

[[nodiscard]] int Skill::requiredWtypeId1() const { return m_requiredWtypeId1; }
void Skill::setRequiredWtypeId1(int requiredWtypeId1) {
  if (m_requiredWtypeId1 == requiredWtypeId1) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(requiredWtypeId1);
  m_requiredWtypeId1 = requiredWtypeId1;
  if (!signalsDisabled()) {
  emit_signal(requiredWtypeId1Modified(), this, m_requiredWtypeId1);
  }
  setModified();
}

[[nodiscard]] int Skill::requiredWtypeId2() const { return m_requiredWtypeId2; }
void Skill::setRequiredWtypeId2(int requiredWtypeId2) {
  if (m_requiredWtypeId2 == requiredWtypeId2) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(requiredWtypeId2);
  m_requiredWtypeId2 = requiredWtypeId2;
  if (!signalsDisabled()) {
  emit_signal(requiredWtypeId2Modified(), this, m_requiredWtypeId2);
  }
  setModified();
}

[[nodiscard]] Scope Skill::scope() const { return m_scope; }
void Skill::setScope(Scope scope) {
  if (m_scope == scope) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(scope);
  m_scope = scope;
  if (!signalsDisabled()) {
  emit_signal(scopeModified(), this, m_scope);
  }
  setModified();
}

[[nodiscard]] int Skill::speed() const { return m_speed; }
void Skill::setSpeed(int speed) {
  if (m_speed == speed) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(speed);
  m_speed = speed;
  if (!signalsDisabled()) {
  emit_signal(speedModified(), this, m_speed);
  }
  setModified();
}

[[nodiscard]] int Skill::stypeId() const { return m_stypeId; }
void Skill::setStypeId(int stypeId) {
  if (m_stypeId == stypeId) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(stypeId);
  m_stypeId = stypeId;
  if (!signalsDisabled()) {
  emit_signal(stypeIdModified(), this, m_stypeId);
  }
  setModified();
}

[[nodiscard]] int Skill::successRate() const { return m_successRate; }
void Skill::setSuccessRate(int successRate) {
  if (m_successRate == successRate) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(successRate);
  m_successRate = successRate;
  if (!signalsDisabled()) {
  emit_signal(successRateModified(), this, m_successRate);
  }
  setModified();
}

[[nodiscard]] int Skill::tpCost() const { return m_tpCost; }
void Skill::setTpCost(int tpCost) {
  if (m_tpCost == tpCost) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(tpCost);
  m_tpCost = tpCost;
  if (!signalsDisabled()) {
  emit_signal(tpCostModified(), this, m_tpCost);
  }
  setModified();
}

[[nodiscard]] int Skill::tpGain() const { return m_tpGain; }
void Skill::setTpGain(int tpGain) {
  if (m_tpGain == tpGain) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(tpGain);
  m_tpGain = tpGain;
  if (!signalsDisabled()) {
  emit_signal(tpGainModified(), this, m_tpGain);
  }
  setModified();
}

[[nodiscard]] bool Skill::isValid() const { return m_isValid; }
void Skill::setValid(const bool isValid) { m_isValid = isValid; }

void Skill::restoreOriginal() {
  IModifiable::restoreOriginal();
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(id);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(animationId);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(damage);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(description);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(effects);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(iconIndex);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(message1);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(message2);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(mpCost);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(name);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(note);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(occasion);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(hitType);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(repeats);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(requiredWtypeId1);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(requiredWtypeId2);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(scope);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(speed);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(stypeId);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(successRate);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(tpCost);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(tpGain);
}
void Skill::acceptChanges() {
  IModifiable::acceptChanges();
  MODIFIABLE_ACCEPT_VALUE(id);
  MODIFIABLE_ACCEPT_VALUE(animationId);
  MODIFIABLE_ACCEPT_VALUE(damage);
  MODIFIABLE_ACCEPT_VALUE(description);
  MODIFIABLE_ACCEPT_VALUE(effects);
  MODIFIABLE_ACCEPT_VALUE(iconIndex);
  MODIFIABLE_ACCEPT_VALUE(message1);
  MODIFIABLE_ACCEPT_VALUE(message2);
  MODIFIABLE_ACCEPT_VALUE(mpCost);
  MODIFIABLE_ACCEPT_VALUE(name);
  MODIFIABLE_ACCEPT_VALUE(note);
  MODIFIABLE_ACCEPT_VALUE(occasion);
  MODIFIABLE_ACCEPT_VALUE(hitType);
  MODIFIABLE_ACCEPT_VALUE(repeats);
  MODIFIABLE_ACCEPT_VALUE(requiredWtypeId1);
  MODIFIABLE_ACCEPT_VALUE(requiredWtypeId2);
  MODIFIABLE_ACCEPT_VALUE(scope);
  MODIFIABLE_ACCEPT_VALUE(speed);
  MODIFIABLE_ACCEPT_VALUE(stypeId);
  MODIFIABLE_ACCEPT_VALUE(successRate);
  MODIFIABLE_ACCEPT_VALUE(tpCost);
  MODIFIABLE_ACCEPT_VALUE(tpGain);
}
nlohmann::ordered_json Skill::serializeOldValues() const {
  return {
      MODIFIABLE_SERIALIZE_VALUE(id),
      MODIFIABLE_SERIALIZE_VALUE(animationId),
      MODIFIABLE_SERIALIZE_VALUE(damage),
      MODIFIABLE_SERIALIZE_VALUE(description),
      MODIFIABLE_SERIALIZE_VALUE_CHILD_ARRAY(effects),
      MODIFIABLE_SERIALIZE_VALUE(iconIndex),
      MODIFIABLE_SERIALIZE_VALUE(message1),
      MODIFIABLE_SERIALIZE_VALUE(message2),
      MODIFIABLE_SERIALIZE_VALUE(mpCost),
      MODIFIABLE_SERIALIZE_VALUE(name),
      MODIFIABLE_SERIALIZE_VALUE(note),
      MODIFIABLE_SERIALIZE_VALUE(occasion),
      MODIFIABLE_SERIALIZE_VALUE(hitType),
      MODIFIABLE_SERIALIZE_VALUE(repeats),
      MODIFIABLE_SERIALIZE_VALUE(requiredWtypeId1),
      MODIFIABLE_SERIALIZE_VALUE(requiredWtypeId2),
      MODIFIABLE_SERIALIZE_VALUE(scope),
      MODIFIABLE_SERIALIZE_VALUE(speed),
      MODIFIABLE_SERIALIZE_VALUE(stypeId),
      MODIFIABLE_SERIALIZE_VALUE(successRate),
      MODIFIABLE_SERIALIZE_VALUE(tpCost),
      MODIFIABLE_SERIALIZE_VALUE(tpGain),
  };
}

rpgmutils::signal<void(Skill*, int)>& Skill::idModified() {
  if (!m_idModified) {
    m_idModified.emplace();
  }
  return *m_idModified;
}
rpgmutils::signal<void(Skill*, int)>& Skill::animationIdModified() {
  if (!m_animationIdModified) {
    m_animationIdModified.emplace();
  }
  return *m_animationIdModified;
}
rpgmutils::signal<void(Skill*, const Damage&)>& Skill::damageModified() {
  if (!m_damageModified) {
    m_damageModified.emplace();
  }
  return *m_damageModified;
}
rpgmutils::signal<void(Skill*, const std::string&)>& Skill::descriptionModified() {
  if (!m_descriptionModified) {
    m_descriptionModified.emplace();
  }
  return *m_descriptionModified;
}
rpgmutils::signal<void(Skill*, const std::vector<Effect>&)>& Skill::effectsModified() {
  if (!m_effectsModified) {
    m_effectsModified.emplace();
  }
  return *m_effectsModified;
}
rpgmutils::signal<void(Skill*, int)>& Skill::iconIndexModified() {
  if (!m_iconIndexModified) {
    m_iconIndexModified.emplace();
  }
  return *m_iconIndexModified;
}
rpgmutils::signal<void(Skill*, const std::string&)>& Skill::message1Modified() {
  if (!m_message1Modified) {
    m_message1Modified.emplace();
  }
  return *m_message1Modified;
}
rpgmutils::signal<void(Skill*, const std::string&)>& Skill::message2Modified() {
  if (!m_message2Modified) {
    m_message2Modified.emplace();
  }
  return *m_message2Modified;
}
rpgmutils::signal<void(Skill*, int)>& Skill::mpCostModified() {
  if (!m_mpCostModified) {
    m_mpCostModified.emplace();
  }
  return *m_mpCostModified;
}
rpgmutils::signal<void(Skill*, const std::string&)>& Skill::nameModified() {
  if (!m_nameModified) {
    m_nameModified.emplace();
  }
  return *m_nameModified;
}
rpgmutils::signal<void(Skill*, const std::string&)>& Skill::noteModified() {
  if (!m_noteModified) {
    m_noteModified.emplace();
  }
  return *m_noteModified;
}
rpgmutils::signal<void(Skill*, Occasion)>& Skill::occasionModified() {
  if (!m_occasionModified) {
    m_occasionModified.emplace();
  }
  return *m_occasionModified;
}
rpgmutils::signal<void(Skill*, HitType)>& Skill::hitTypeModified() {
  if (!m_hitTypeModified) {
    m_hitTypeModified.emplace();
  }
  return *m_hitTypeModified;
}
rpgmutils::signal<void(Skill*, int)>& Skill::repeatsModified() {
  if (!m_repeatsModified) {
    m_repeatsModified.emplace();
  }
  return *m_repeatsModified;
}
rpgmutils::signal<void(Skill*, int)>& Skill::requiredWtypeId1Modified() {
  if (!m_requiredWtypeId1Modified) {
    m_requiredWtypeId1Modified.emplace();
  }
  return *m_requiredWtypeId1Modified;
}
rpgmutils::signal<void(Skill*, int)>& Skill::requiredWtypeId2Modified() {
  if (!m_requiredWtypeId2Modified) {
    m_requiredWtypeId2Modified.emplace();
  }
  return *m_requiredWtypeId2Modified;
}
rpgmutils::signal<void(Skill*, Scope)>& Skill::scopeModified() {
  if (!m_scopeModified) {
    m_scopeModified.emplace();
  }
  return *m_scopeModified;
}
rpgmutils::signal<void(Skill*, int)>& Skill::speedModified() {
  if (!m_speedModified) {
    m_speedModified.emplace();
  }
  return *m_speedModified;
}
rpgmutils::signal<void(Skill*, int)>& Skill::stypeIdModified() {
  if (!m_stypeIdModified) {
    m_stypeIdModified.emplace();
  }
  return *m_stypeIdModified;
}
rpgmutils::signal<void(Skill*, int)>& Skill::successRateModified() {
  if (!m_successRateModified) {
    m_successRateModified.emplace();
  }
  return *m_successRateModified;
}
rpgmutils::signal<void(Skill*, int)>& Skill::tpCostModified() {
  if (!m_tpCostModified) {
    m_tpCostModified.emplace();
  }
  return *m_tpCostModified;
}
rpgmutils::signal<void(Skill*, int)>& Skill::tpGainModified() {
  if (!m_tpGainModified) {
    m_tpGainModified.emplace();
  }
  return *m_tpGainModified;
}

void to_json(nlohmann::ordered_json& j, const Skill& skill) {
  j = {
      {"id", skill.m_id},
      {"animationId", skill.m_animationId},
      {"damage", skill.m_damage},
      {"description", skill.m_description},
      {"effects", skill.m_effects},
      {"hitType", skill.m_hitType},
      {"iconIndex", skill.m_iconIndex},
      {"message1", skill.m_message1},
      {"message2", skill.m_message2},
      {"mpCost", skill.m_mpCost},
      {"name", skill.m_name},
      {"note", skill.m_note},
      {"occasion", skill.m_occasion},
      {"repeats", skill.m_repeats},
      {"requiredWtypeId1", skill.m_requiredWtypeId1},
      {"requiredWtypeId2", skill.m_requiredWtypeId2},
      {"scope", skill.m_scope},
      {"speed", skill.m_speed},
      {"stypeId", skill.m_stypeId},
      {"successRate", skill.m_successRate},
      {"tpCost", skill.m_tpCost},
      {"tpGain", skill.m_tpGain},
  };
}

void from_json(const nlohmann::ordered_json& j, Skill& skill) {
  skill.m_id = j.value("id", skill.m_id);
  skill.m_animationId = j.value("animationId", skill.m_animationId);
  skill.m_damage = j.value("damage", skill.m_damage);
  skill.m_description = j.value("description", skill.m_description);
  skill.m_effects = j.value("effects", skill.m_effects);
  skill.m_hitType = j.value("hitType", skill.m_hitType);
  skill.m_iconIndex = j.value("iconIndex", skill.m_iconIndex);
  skill.m_message1 = j.value("message1", skill.m_message1);
  skill.m_message2 = j.value("message2", skill.m_message2);
  skill.m_mpCost = j.value("mpCost", skill.m_mpCost);
  skill.m_name = j.value("name", skill.m_name);
  skill.m_note = j.value("note", skill.m_note);
  skill.m_occasion = j.value("occasion", skill.m_occasion);
  skill.m_repeats = j.value("repeats", skill.m_repeats);
  skill.m_scope = j.value("scope", skill.m_scope);
  skill.m_speed = j.value("speed", skill.m_speed);
  skill.m_stypeId = j.value("stypeId", skill.m_stypeId);
  skill.m_successRate = j.value("successRate", skill.m_successRate);
  skill.m_tpCost = j.value("tpCost", skill.m_tpCost);
  skill.m_tpGain = j.value("tpGain", skill.m_tpGain);
}


code:
#pragma once

#include "Database/Damage.hpp"
#include "Database/Effect.hpp"
#include "Database/Globals.hpp"

#include <nlohmann/json.hpp>
#include <string>

class Skill final : public IModifiable {
  friend class SkillsSerializer;
  friend void to_json(nlohmann::ordered_json& j, const Skill& skill);
  friend void from_json(const nlohmann::ordered_json& j, Skill& skill);

public:
  Skill() = default;
  Skill(const Skill& other);
  Skill& operator=(const Skill& other);
  Skill(Skill&& other) noexcept;
  Skill& operator=(Skill&& other) noexcept;

  [[nodiscard]] int id() const;
  void setId(int id);

  [[nodiscard]] int animationId() const;
  void setAnimationId(int id);

  Damage& damage();
  void setDamage(const Damage& damage);

  [[nodiscard]] const std::string& description() const;
  void setDescription(const std::string& description);

  std::vector<Effect>& effects();
  void setEffects(const std::vector<Effect>& effects);

  [[nodiscard]] int iconIndex() const;
  void setIconIndex(int iconIndex);

  [[nodiscard]] const std::string& message1() const;
  void setMessage1(const std::string& message1);

  [[nodiscard]] const std::string& message2() const;
  void setMessage2(const std::string& message2);

  [[nodiscard]] int mpCost() const;
  void setMpCost(int mpCost);

  [[nodiscard]] const std::string& name() const;
  void setName(const std::string& name);

  [[nodiscard]] const std::string& note() const;
  void setNote(const std::string& note);

  [[nodiscard]] Occasion occasion() const;
  void setOccasion(Occasion occasion);

  [[nodiscard]] HitType hitType() const;
  void setHitType(HitType hitType);

  [[nodiscard]] int repeats() const;
  void setRepeats(int repeats);

  [[nodiscard]] int requiredWtypeId1() const;
  void setRequiredWtypeId1(int requiredWtypeId1);

  [[nodiscard]] int requiredWtypeId2() const;
  void setRequiredWtypeId2(int requiredWtypeId2);

  [[nodiscard]] Scope scope() const;
  void setScope(Scope scope);

  [[nodiscard]] int speed() const;
  void setSpeed(int speed);

  [[nodiscard]] int stypeId() const;
  void setStypeId(int stypeId);

  [[nodiscard]] int successRate() const;
  void setSuccessRate(int successRate);

  [[nodiscard]] int tpCost() const;
  void setTpCost(int tpCost);

  [[nodiscard]] int tpGain() const;
  void setTpGain(int tpGain);

  [[nodiscard]] bool isValid() const;
  void setValid(bool isValid);

  void restoreOriginal() override;
  void acceptChanges() override;
  nlohmann::ordered_json serializeOldValues() const override;

  bool isModified() const override { return IModifiable::isModified() | m_damage.isModified(); }

  rpgmutils::signal<void(Skill*, int)>& idModified();
  rpgmutils::signal<void(Skill*, int)>& animationIdModified();
  rpgmutils::signal<void(Skill*, const Damage&)>& damageModified();
  rpgmutils::signal<void(Skill*, const std::string&)>& descriptionModified();
  rpgmutils::signal<void(Skill*, const std::vector<Effect>&)>& effectsModified();
  rpgmutils::signal<void(Skill*, int)>& iconIndexModified();
  rpgmutils::signal<void(Skill*, const std::string&)>& message1Modified();
  rpgmutils::signal<void(Skill*, const std::string&)>& message2Modified();
  rpgmutils::signal<void(Skill*, int)>& mpCostModified();
  rpgmutils::signal<void(Skill*, const std::string&)>& nameModified();
  rpgmutils::signal<void(Skill*, const std::string&)>& noteModified();
  rpgmutils::signal<void(Skill*, Occasion)>& occasionModified();
  rpgmutils::signal<void(Skill*, HitType)>& hitTypeModified();
  rpgmutils::signal<void(Skill*, int)>& repeatsModified();
  rpgmutils::signal<void(Skill*, int)>& requiredWtypeId1Modified();
  rpgmutils::signal<void(Skill*, int)>& requiredWtypeId2Modified();
  rpgmutils::signal<void(Skill*, Scope)>& scopeModified();
  rpgmutils::signal<void(Skill*, int)>& speedModified();
  rpgmutils::signal<void(Skill*, int)>& stypeIdModified();
  rpgmutils::signal<void(Skill*, int)>& successRateModified();
  rpgmutils::signal<void(Skill*, int)>& tpCostModified();
  rpgmutils::signal<void(Skill*, int)>& tpGainModified();

private:
  int m_id;
  int m_animationId;
  Damage m_damage;
  std::string m_description;
  std::vector<Effect> m_effects;
  int m_iconIndex;
  std::string m_message1;
  std::string m_message2;
  int m_mpCost;
  std::string m_name;
  std::string m_note;
  Occasion m_occasion;
  HitType m_hitType;
  int m_repeats;
  int m_requiredWtypeId1; // In System?
  int m_requiredWtypeId2; // "" ""
  Scope m_scope;
  int m_speed;
  int m_stypeId; // As defined in System
  int m_successRate;
  int m_tpCost;
  int m_tpGain;

  std::optional<int> m_oldid;
  std::optional<int> m_oldanimationId;
  std::optional<Damage> m_olddamage;
  std::optional<std::string> m_olddescription;
  std::optional<std::vector<Effect>> m_oldeffects;
  std::optional<int> m_oldiconIndex;
  std::optional<std::string> m_oldmessage1;
  std::optional<std::string> m_oldmessage2;
  std::optional<int> m_oldmpCost;
  std::optional<std::string> m_oldname;
  std::optional<std::string> m_oldnote;
  std::optional<Occasion> m_oldoccasion;
  std::optional<HitType> m_oldhitType;
  std::optional<int> m_oldrepeats;
  std::optional<int> m_oldrequiredWtypeId1;
  std::optional<int> m_oldrequiredWtypeId2;
  std::optional<Scope> m_oldscope;
  std::optional<int> m_oldspeed;
  std::optional<int> m_oldstypeId;
  std::optional<int> m_oldsuccessRate;
  std::optional<int> m_oldtpCost;
  std::optional<int> m_oldtpGain;

  std::optional<rpgmutils::signal<void(Skill*, int)>> m_idModified;
  std::optional<rpgmutils::signal<void(Skill*, int)>> m_animationIdModified;
  std::optional<rpgmutils::signal<void(Skill*, const Damage&)>> m_damageModified;
  std::optional<rpgmutils::signal<void(Skill*, const std::string&)>> m_descriptionModified;
  std::optional<rpgmutils::signal<void(Skill*, const std::vector<Effect>&)>> m_effectsModified;
  std::optional<rpgmutils::signal<void(Skill*, int)>> m_iconIndexModified;
  std::optional<rpgmutils::signal<void(Skill*, const std::string&)>> m_message1Modified;
  std::optional<rpgmutils::signal<void(Skill*, const std::string&)>> m_message2Modified;
  std::optional<rpgmutils::signal<void(Skill*, int)>> m_mpCostModified;
  std::optional<rpgmutils::signal<void(Skill*, const std::string&)>> m_nameModified;
  std::optional<rpgmutils::signal<void(Skill*, const std::string&)>> m_noteModified;
  std::optional<rpgmutils::signal<void(Skill*, Occasion)>> m_occasionModified;
  std::optional<rpgmutils::signal<void(Skill*, HitType)>> m_hitTypeModified;
  std::optional<rpgmutils::signal<void(Skill*, int)>> m_repeatsModified;
  std::optional<rpgmutils::signal<void(Skill*, int)>> m_requiredWtypeId1Modified;
  std::optional<rpgmutils::signal<void(Skill*, int)>> m_requiredWtypeId2Modified;
  std::optional<rpgmutils::signal<void(Skill*, Scope)>> m_scopeModified;
  std::optional<rpgmutils::signal<void(Skill*, int)>> m_speedModified;
  std::optional<rpgmutils::signal<void(Skill*, int)>> m_stypeIdModified;
  std::optional<rpgmutils::signal<void(Skill*, int)>> m_successRateModified;
  std::optional<rpgmutils::signal<void(Skill*, int)>> m_tpCostModified;
  std::optional<rpgmutils::signal<void(Skill*, int)>> m_tpGainModified;

  /*!
   * @name m_isValid
   * @details
   * Indicates that this is an actual valid entry and not a dummy
   * When making a new entry make sure to set this to true or it won't be
   * serialized.
   */
  bool m_isValid{false};
};


code: #include "Database/Skills.hpp"


code:
#pragma once

#include "Database/Skill.hpp"

class Skills : public IModifiable {
public:
  [[nodiscard]] Skill* skill(const int id) {
    for (auto& skill : m_skills) {
      if (skill.id() == id && skill.isValid()) {
        return &skill;
      }
    }
    return nullptr;
  }

  [[nodiscard]] const Skill* skill(const int id) const {
    for (const auto& skill : m_skills) {
      if (skill.id() == id && skill.isValid()) {
        return &skill;
      }
    }

    return nullptr;
  }

  std::vector<Skill>& skills() { return m_skills; }
  const std::vector<Skill>& skills() const { return m_skills; }

  int count() const { return m_skills.size() - 1; }

  void resize(int newSize) {
    ++newSize;
    const int oldSize = m_skills.size();
    m_skills.resize(newSize);
    if (newSize > oldSize) {
      for (int i = oldSize; i < m_skills.size(); ++i) {
        m_skills[i].setId(i);
      }
    }
  }

  bool isModified() const override {
    return IModifiable::isModified() | std::ranges::any_of(m_skills, [](const Skill& skill) { return skill.isModified(); });
  }

private:
  std::vector<Skill> m_skills;
};

void to_json(nlohmann::ordered_json& j, const Skill& skill);
void from_json(const nlohmann::ordered_json& j, Skill& skill);


code:
#include "Database/State.hpp"

State::State(const State& other)
: IModifiable(other)
, m_id(other.m_id)
, m_autoRemovalTiming(other.m_autoRemovalTiming)
, m_iconIndex(other.m_iconIndex)
, m_minTurns(other.m_minTurns)
, m_maxTurns(other.m_maxTurns)
, m_message1(other.m_message1)
, m_message2(other.m_message2)
, m_message3(other.m_message3)
, m_message4(other.m_message4)
, m_motion(other.m_motion)
, m_name(other.m_name)
, m_note(other.m_note)
, m_overlay(other.m_overlay)
, m_priority(other.m_priority)
, m_releaseByDamage(other.m_releaseByDamage)
, m_removeAtBattleEnd(other.m_removeAtBattleEnd)
, m_removeByDamage(other.m_removeByDamage)
, m_removeByRestriction(other.m_removeByRestriction)
, m_removeByWalking(other.m_removeByWalking)
, m_restriction(other.m_restriction)
, m_stepsToRemove(other.m_stepsToRemove)
, m_traits(other.m_traits)
, m_oldid(other.m_oldid)
, m_oldautoRemovalTiming(other.m_oldautoRemovalTiming)
, m_oldiconIndex(other.m_oldiconIndex)
, m_oldminTurns(other.m_oldminTurns)
, m_oldmaxTurns(other.m_oldmaxTurns)
, m_oldmessage1(other.m_oldmessage1)
, m_oldmessage2(other.m_oldmessage2)
, m_oldmessage3(other.m_oldmessage3)
, m_oldmessage4(other.m_oldmessage4)
, m_oldmotion(other.m_oldmotion)
, m_oldname(other.m_oldname)
, m_oldnote(other.m_oldnote)
, m_oldoverlay(other.m_oldoverlay)
, m_oldpriority(other.m_oldpriority)
, m_oldreleaseByDamage(other.m_oldreleaseByDamage)
, m_oldremoveAtBattleEnd(other.m_oldremoveAtBattleEnd)
, m_oldremoveByDamage(other.m_oldremoveByDamage)
, m_oldremoveByRestriction(other.m_oldremoveByRestriction)
, m_oldremoveByWalking(other.m_oldremoveByWalking)
, m_oldrestriction(other.m_oldrestriction)
, m_oldstepsToRemove(other.m_oldstepsToRemove)
, m_oldtraits(other.m_oldtraits)
, m_isValid(other.m_isValid) {}
State& State::operator=(const State& other) {
  IModifiable::operator=(other);
  m_id = other.m_id;
  m_autoRemovalTiming = other.m_autoRemovalTiming;
  m_iconIndex = other.m_iconIndex;
  m_minTurns = other.m_minTurns;
  m_maxTurns = other.m_maxTurns;
  m_message1 = other.m_message1;
  m_message2 = other.m_message2;
  m_message3 = other.m_message3;
  m_message4 = other.m_message4;
  m_motion = other.m_motion;
  m_name = other.m_name;
  m_note = other.m_note;
  m_overlay = other.m_overlay;
  m_priority = other.m_priority;
  m_releaseByDamage = other.m_releaseByDamage;
  m_removeAtBattleEnd = other.m_removeAtBattleEnd;
  m_removeByDamage = other.m_removeByDamage;
  m_removeByRestriction = other.m_removeByRestriction;
  m_removeByWalking = other.m_removeByWalking;
  m_restriction = other.m_restriction;
  m_stepsToRemove = other.m_stepsToRemove;
  m_traits = other.m_traits;
  m_oldid = other.m_oldid;
  m_oldautoRemovalTiming = other.m_oldautoRemovalTiming;
  m_oldiconIndex = other.m_oldiconIndex;
  m_oldminTurns = other.m_oldminTurns;
  m_oldmaxTurns = other.m_oldmaxTurns;
  m_oldmessage1 = other.m_oldmessage1;
  m_oldmessage2 = other.m_oldmessage2;
  m_oldmessage3 = other.m_oldmessage3;
  m_oldmessage4 = other.m_oldmessage4;
  m_oldmotion = other.m_oldmotion;
  m_oldname = other.m_oldname;
  m_oldnote = other.m_oldnote;
  m_oldoverlay = other.m_oldoverlay;
  m_oldpriority = other.m_oldpriority;
  m_oldreleaseByDamage = other.m_oldreleaseByDamage;
  m_oldremoveAtBattleEnd = other.m_oldremoveAtBattleEnd;
  m_oldremoveByDamage = other.m_oldremoveByDamage;
  m_oldremoveByRestriction = other.m_oldremoveByRestriction;
  m_oldremoveByWalking = other.m_oldremoveByWalking;
  m_oldrestriction = other.m_oldrestriction;
  m_oldstepsToRemove = other.m_oldstepsToRemove;
  m_oldtraits = other.m_oldtraits;
  m_isValid = other.m_isValid;
  return *this;
}
State::State(State&& other) noexcept
: IModifiable(std::move(other))
, m_id(other.m_id)
, m_autoRemovalTiming(other.m_autoRemovalTiming)
, m_iconIndex(other.m_iconIndex)
, m_minTurns(other.m_minTurns)
, m_maxTurns(other.m_maxTurns)
, m_message1(std::move(other.m_message1))
, m_message2(std::move(other.m_message2))
, m_message3(std::move(other.m_message3))
, m_message4(std::move(other.m_message4))
, m_motion(other.m_motion)
, m_name(std::move(other.m_name))
, m_note(std::move(other.m_note))
, m_overlay(other.m_overlay)
, m_priority(other.m_priority)
, m_releaseByDamage(other.m_releaseByDamage)
, m_removeAtBattleEnd(other.m_removeAtBattleEnd)
, m_removeByDamage(other.m_removeByDamage)
, m_removeByRestriction(other.m_removeByRestriction)
, m_removeByWalking(other.m_removeByWalking)
, m_restriction(other.m_restriction)
, m_stepsToRemove(other.m_stepsToRemove)
, m_traits(std::move(other.m_traits))
, m_oldid(other.m_oldid)
, m_oldautoRemovalTiming(other.m_oldautoRemovalTiming)
, m_oldiconIndex(other.m_oldiconIndex)
, m_oldminTurns(other.m_oldminTurns)
, m_oldmaxTurns(other.m_oldmaxTurns)
, m_oldmessage1(std::move(other.m_oldmessage1))
, m_oldmessage2(std::move(other.m_oldmessage2))
, m_oldmessage3(std::move(other.m_oldmessage3))
, m_oldmessage4(std::move(other.m_oldmessage4))
, m_oldmotion(other.m_oldmotion)
, m_oldname(std::move(other.m_oldname))
, m_oldnote(std::move(other.m_oldnote))
, m_oldoverlay(other.m_oldoverlay)
, m_oldpriority(other.m_oldpriority)
, m_oldreleaseByDamage(other.m_oldreleaseByDamage)
, m_oldremoveAtBattleEnd(other.m_oldremoveAtBattleEnd)
, m_oldremoveByDamage(other.m_oldremoveByDamage)
, m_oldremoveByRestriction(other.m_oldremoveByRestriction)
, m_oldremoveByWalking(other.m_oldremoveByWalking)
, m_oldrestriction(other.m_oldrestriction)
, m_oldstepsToRemove(other.m_oldstepsToRemove)
, m_oldtraits(std::move(other.m_oldtraits))
, m_isValid(other.m_isValid) {}
State& State::operator=(State&& other) noexcept {
  IModifiable::operator=(std::move(other));
  m_id = other.m_id;
  m_autoRemovalTiming = other.m_autoRemovalTiming;
  m_iconIndex = other.m_iconIndex;
  m_minTurns = other.m_minTurns;
  m_maxTurns = other.m_maxTurns;
  m_message1 = std::move(other.m_message1);
  m_message2 = std::move(other.m_message2);
  m_message3 = std::move(other.m_message3);
  m_message4 = std::move(other.m_message4);
  m_motion = other.m_motion;
  m_name = std::move(other.m_name);
  m_note = std::move(other.m_note);
  m_overlay = other.m_overlay;
  m_priority = other.m_priority;
  m_releaseByDamage = other.m_releaseByDamage;
  m_removeAtBattleEnd = other.m_removeAtBattleEnd;
  m_removeByDamage = other.m_removeByDamage;
  m_removeByRestriction = other.m_removeByRestriction;
  m_removeByWalking = other.m_removeByWalking;
  m_restriction = other.m_restriction;
  m_stepsToRemove = other.m_stepsToRemove;
  m_traits = std::move(other.m_traits);
  m_oldid = other.m_oldid;
  m_oldautoRemovalTiming = other.m_oldautoRemovalTiming;
  m_oldiconIndex = other.m_oldiconIndex;
  m_oldminTurns = other.m_oldminTurns;
  m_oldmaxTurns = other.m_oldmaxTurns;
  m_oldmessage1 = std::move(other.m_oldmessage1);
  m_oldmessage2 = std::move(other.m_oldmessage2);
  m_oldmessage3 = std::move(other.m_oldmessage3);
  m_oldmessage4 = std::move(other.m_oldmessage4);
  m_oldmotion = other.m_oldmotion;
  m_oldname = std::move(other.m_oldname);
  m_oldnote = std::move(other.m_oldnote);
  m_oldoverlay = other.m_oldoverlay;
  m_oldpriority = other.m_oldpriority;
  m_oldreleaseByDamage = other.m_oldreleaseByDamage;
  m_oldremoveAtBattleEnd = other.m_oldremoveAtBattleEnd;
  m_oldremoveByDamage = other.m_oldremoveByDamage;
  m_oldremoveByRestriction = other.m_oldremoveByRestriction;
  m_oldremoveByWalking = other.m_oldremoveByWalking;
  m_oldrestriction = other.m_oldrestriction;
  m_oldstepsToRemove = other.m_oldstepsToRemove;
  m_oldtraits = std::move(other.m_oldtraits);
  m_isValid = other.m_isValid;
  return *this;
}

[[nodiscard]] int State::id() const { return m_id; }
void State::setId(int id) {
  if (m_id == id) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(id);
  m_id = id;
  if (!signalsDisabled()) {
  emit_signal(idModified(), this, id);
  }
  setModified();
}

[[nodiscard]] AutoRemovalTiming State::autoRemovalTiming() const { return m_autoRemovalTiming; }
void State::setAutoRemovalTiming(AutoRemovalTiming autoRemovalTiming) {
  if (m_autoRemovalTiming == autoRemovalTiming) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(autoRemovalTiming);
  m_autoRemovalTiming = autoRemovalTiming;
  if (!signalsDisabled()) {
  emit_signal(autoRemovalTimingModified(), this, autoRemovalTiming);
  }
  setModified();
}

[[nodiscard]] int State::chanceByDamage() const { return m_chanceByDamage; }
void State::setChanceByDamage(int chanceByDamage) {
  if (m_chanceByDamage == chanceByDamage) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(chanceByDamage);
  m_chanceByDamage = chanceByDamage;
  if (!signalsDisabled()) {
  emit_signal(chanceByDamageModified(), this, chanceByDamage);
  }
  setModified();
}

[[nodiscard]] int State::iconIndex() const { return m_iconIndex; }
void State::setIconIndex(int iconIndex) {
  if (m_iconIndex == iconIndex) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(iconIndex);
  m_iconIndex = iconIndex;
  if (!signalsDisabled()) {
  emit_signal(iconIndexModified(), this, iconIndex);
  }
  setModified();
}

[[nodiscard]] int State::minTurns() const { return m_minTurns; }
void State::setMinTurns(int minTurns) {
  if (m_minTurns == minTurns) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(minTurns);
  m_minTurns = minTurns;
  if (!signalsDisabled()) {
  emit_signal(minTurnsModified(), this, m_minTurns);
  }
  setModified();
}

[[nodiscard]] int State::maxTurns() const { return m_maxTurns; }
void State::setMaxTurns(int maxTurns) {
  if (m_maxTurns == maxTurns) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(maxTurns);
  m_maxTurns = maxTurns;
  if (!signalsDisabled()) {
  emit_signal(maxTurnsModified(), this, m_maxTurns);
  }
  setModified();
}

[[nodiscard]] const std::string& State::message1() const { return m_message1; }
void State::setMessage1(const std::string& message1) {
  if (m_message1 == message1) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(message1);
  m_message1 = message1;
  if (!signalsDisabled()) {
  emit_signal(message1Modified(), this, m_message1);
  }
  setModified();
}

[[nodiscard]] const std::string& State::message2() const { return m_message2; }
void State::setMessage2(const std::string& message2) {
  if (m_message2 == message2) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(message2);
  m_message2 = message2;
  if (!signalsDisabled()) {
  emit_signal(message2Modified(), this, m_message2);
  }
  setModified();
}
[[nodiscard]] const std::string& State::message3() const { return m_message3; }
void State::setMessage3(const std::string& message3) {
  if (m_message3 == message3) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(message3);
  m_message3 = message3;
  if (!signalsDisabled()) {
  emit_signal(message3Modified(), this, m_message3);
  }
  setModified();
}

[[nodiscard]] const std::string& State::message4() const { return m_message4; }
void State::setMessage4(const std::string& message4) {
  if (m_message4 == message4) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(message4);
  m_message4 = message4;
  if (!signalsDisabled()) {
  emit_signal(message4Modified(), this, m_message4);
  }
  setModified();
}

[[nodiscard]] StateMotion State::motion() const { return m_motion; }
void State::setMotion(StateMotion motion) {
  if (m_motion == motion) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(motion);
  m_motion = motion;
  if (!signalsDisabled()) {
  emit_signal(motionModified(), this, m_motion);
  }
  setModified();
}

[[nodiscard]] const std::string& State::name() const { return m_name; }
void State::setName(const std::string& name) {
  if (m_name == name) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(name);
  m_name = name;
  if (!signalsDisabled()) {
  emit_signal(nameModified(), this, m_name);
  }
  setModified();
}

[[nodiscard]] const std::string& State::note() const { return m_note; }
void State::setNote(const std::string& note) {
  if (m_note == note) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(note);
  m_note = note;
  if (!signalsDisabled()) {
  emit_signal(noteModified(), this, m_note);
  }
  setModified();
}

[[nodiscard]] StateOverlay State::overlay() const { return m_overlay; }
void State::setOverlay(StateOverlay overlay) {
  if (m_overlay == overlay) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(overlay);
  m_overlay = overlay;
  if (!signalsDisabled()) {
  emit_signal(overlayModified(), this, m_overlay);
  }
  setModified();
}

[[nodiscard]] int State::priority() const { return m_priority; }
void State::setPriority(int priority) {
  if (m_priority == priority) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(priority);
  m_priority = priority;
  if (!signalsDisabled()) {
  emit_signal(priorityModified(), this, m_priority);
  }
  setModified();
}

[[nodiscard]] bool State::releaseByDamage() const { return m_releaseByDamage; }
void State::setReleaseByDamage(bool releaseByDamage) {
  if (m_releaseByDamage == releaseByDamage) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(releaseByDamage);
  m_releaseByDamage = releaseByDamage;
  if (!signalsDisabled()) {
  emit_signal(releaseByDamageModified(), this, m_releaseByDamage);
  }
  setModified();
}

[[nodiscard]] bool State::removeAtBattleEnd() const { return m_removeAtBattleEnd; }
void State::setRemoveAtBattleEnd(bool removeAtBattleEnd) {
  if (m_removeAtBattleEnd == removeAtBattleEnd) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(removeAtBattleEnd);
  m_removeAtBattleEnd = removeAtBattleEnd;
  if (!signalsDisabled()) {
  emit_signal(removeAtBattleEndModified(), this, m_removeAtBattleEnd);
  }
  setModified();
}

[[nodiscard]] bool State::removeByDamage() const { return m_removeByDamage; }
void State::setRemoveByDamage(bool removeByDamage) {
  if (m_removeByDamage == removeByDamage) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(removeByDamage);
  m_removeByDamage = removeByDamage;
  if (!signalsDisabled()) {
  emit_signal(removeByDamageModified(), this, m_removeByDamage);
  }
  setModified();
}

[[nodiscard]] bool State::removeByRestriction() const { return m_removeByRestriction; }
void State::setRemoveByRestriction(bool removeByRestriction) {
  if (m_removeByRestriction == removeByRestriction) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(removeByRestriction);
  m_removeByRestriction = removeByRestriction;
  if (!signalsDisabled()) {
  emit_signal(removeByRestrictionModified(), this, m_removeByRestriction);
  }
  setModified();
}

[[nodiscard]] bool State::removeByWalking() const { return m_removeByWalking; }
void State::setRemoveByWalking(bool removeByWalking) {
  if (m_removeByWalking == removeByWalking) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(removeByWalking);
  m_removeByWalking = removeByWalking;
  if (!signalsDisabled()) {
  emit_signal(removeByWalkingModified(), this, m_removeByWalking);
  }
  setModified();
}

[[nodiscard]] StateRestriction State::restriction() const { return m_restriction; }
void State::setRestriction(StateRestriction restriction) {
  if (m_restriction == restriction) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(restriction);
  m_restriction = restriction;
  if (!signalsDisabled()) {
  emit_signal(restrictionModified(), this, m_restriction);
  }
  setModified();
}

[[nodiscard]] int State::stepsToRemove() const { return m_stepsToRemove; }
void State::setStepsToRemove(int stepsToRemove) {
  if (m_stepsToRemove == stepsToRemove) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(stepsToRemove);
  m_stepsToRemove = stepsToRemove;
  if (!signalsDisabled()) {
  emit_signal(stepsToRemoveModified(), this, m_stepsToRemove);
  }
  setModified();
}

[[nodiscard]] std::vector<Trait>& State::traits() { return m_traits; }
void State::setTraits(const std::vector<Trait>& traits) {
  if (m_traits == traits) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(traits);
  m_traits = traits;
  if (!signalsDisabled()) {
  emit_signal(traitsModified(), this, m_traits);
  }
  setModified();
}

void State::restoreOriginal() {
  IModifiable::restoreOriginal();
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(id);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(autoRemovalTiming);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(chanceByDamage);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(iconIndex);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(minTurns);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(maxTurns);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(message1);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(message2);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(message3);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(message4);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(motion);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(name);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(note);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(overlay);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(priority);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(releaseByDamage);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(removeAtBattleEnd);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(removeByDamage);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(removeByRestriction);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(removeByWalking);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(restriction);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(stepsToRemove);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(traits);

  for (auto& trait : m_traits) {
    trait.restoreOriginal();
  }
}
void State::acceptChanges() {
  IModifiable::acceptChanges();
  MODIFIABLE_ACCEPT_VALUE(id);
  MODIFIABLE_ACCEPT_VALUE(autoRemovalTiming);
  MODIFIABLE_ACCEPT_VALUE(chanceByDamage);
  MODIFIABLE_ACCEPT_VALUE(iconIndex);
  MODIFIABLE_ACCEPT_VALUE(minTurns);
  MODIFIABLE_ACCEPT_VALUE(maxTurns);
  MODIFIABLE_ACCEPT_VALUE(message1);
  MODIFIABLE_ACCEPT_VALUE(message2);
  MODIFIABLE_ACCEPT_VALUE(message3);
  MODIFIABLE_ACCEPT_VALUE(message4);
  MODIFIABLE_ACCEPT_VALUE(motion);
  MODIFIABLE_ACCEPT_VALUE(name);
  MODIFIABLE_ACCEPT_VALUE(note);
  MODIFIABLE_ACCEPT_VALUE(overlay);
  MODIFIABLE_ACCEPT_VALUE(priority);
  MODIFIABLE_ACCEPT_VALUE(releaseByDamage);
  MODIFIABLE_ACCEPT_VALUE(removeAtBattleEnd);
  MODIFIABLE_ACCEPT_VALUE(removeByDamage);
  MODIFIABLE_ACCEPT_VALUE(removeByRestriction);
  MODIFIABLE_ACCEPT_VALUE(removeByWalking);
  MODIFIABLE_ACCEPT_VALUE(restriction);
  MODIFIABLE_ACCEPT_VALUE(stepsToRemove);
  MODIFIABLE_ACCEPT_VALUE(traits);

  for (auto& trait : m_traits) {
    trait.acceptChanges();
  }
}

nlohmann::ordered_json State::serializeOldValues() const {
  return {
      MODIFIABLE_SERIALIZE_VALUE(id),
      MODIFIABLE_SERIALIZE_VALUE(autoRemovalTiming),
      MODIFIABLE_SERIALIZE_VALUE(chanceByDamage),
      MODIFIABLE_SERIALIZE_VALUE(iconIndex),
      MODIFIABLE_SERIALIZE_VALUE(minTurns),
      MODIFIABLE_SERIALIZE_VALUE(maxTurns),
      MODIFIABLE_SERIALIZE_VALUE(message1),
      MODIFIABLE_SERIALIZE_VALUE(message2),
      MODIFIABLE_SERIALIZE_VALUE(message3),
      MODIFIABLE_SERIALIZE_VALUE(message4),
      MODIFIABLE_SERIALIZE_VALUE(motion),
      MODIFIABLE_SERIALIZE_VALUE(name),
      MODIFIABLE_SERIALIZE_VALUE(note),
      MODIFIABLE_SERIALIZE_VALUE(overlay),
      MODIFIABLE_SERIALIZE_VALUE(priority),
      MODIFIABLE_SERIALIZE_VALUE(releaseByDamage),
      MODIFIABLE_SERIALIZE_VALUE(removeAtBattleEnd),
      MODIFIABLE_SERIALIZE_VALUE(removeByDamage),
      MODIFIABLE_SERIALIZE_VALUE(removeByRestriction),
      MODIFIABLE_SERIALIZE_VALUE(removeByWalking),
      MODIFIABLE_SERIALIZE_VALUE(restriction),
      MODIFIABLE_SERIALIZE_VALUE(stepsToRemove),
      MODIFIABLE_SERIALIZE_VALUE_CHILD_ARRAY(traits),
  };
}
rpgmutils::signal<void(State*, int)>& State::idModified() {
  if (!m_idModified) {
    m_idModified.emplace();
  }
  return *m_idModified;
}
rpgmutils::signal<void(State*, AutoRemovalTiming)>& State::autoRemovalTimingModified() {
  if (!m_autoRemovalTimingModified) {
    m_autoRemovalTimingModified.emplace();
  }
  return *m_autoRemovalTimingModified;
}
rpgmutils::signal<void(State*, int)>& State::chanceByDamageModified() {
  if (!m_chanceByDamageModified) {
    m_chanceByDamageModified.emplace();
  }
  return *m_chanceByDamageModified;
}
rpgmutils::signal<void(State*, int)>& State::iconIndexModified() {
  if (!m_iconIndexModified) {
    m_iconIndexModified.emplace();
  }
  return *m_iconIndexModified;
}
rpgmutils::signal<void(State*, int)>& State::minTurnsModified() {
  if (!m_minTurnsModified) {
    m_minTurnsModified.emplace();
  }
  return *m_minTurnsModified;
}
rpgmutils::signal<void(State*, int)>& State::maxTurnsModified() {
  if (!m_maxTurnsModified) {
    m_maxTurnsModified.emplace();
  }
  return *m_maxTurnsModified;
}
rpgmutils::signal<void(State*, const std::string&)>& State::message1Modified() {
  if (!m_message1Modified) {
    m_message1Modified.emplace();
  }
  return *m_message1Modified;
}
rpgmutils::signal<void(State*, const std::string&)>& State::message2Modified() {
  if (!m_message2Modified) {
    m_message2Modified.emplace();
  }
  return *m_message2Modified;
}
rpgmutils::signal<void(State*, const std::string&)>& State::message3Modified() {
  if (!m_message3Modified) {
    m_message3Modified.emplace();
  }
  return *m_message3Modified;
}
rpgmutils::signal<void(State*, const std::string&)>& State::message4Modified() {
  if (!m_message4Modified) {
    m_message4Modified.emplace();
  }
  return *m_message4Modified;
}
rpgmutils::signal<void(State*, StateMotion)>& State::motionModified() {
  if (!m_motionModified) {
    m_motionModified.emplace();
  }
  return *m_motionModified;
}
rpgmutils::signal<void(State*, const std::string&)>& State::nameModified() {
  if (!m_nameModified) {
    m_nameModified.emplace();
  }
  return *m_nameModified;
}
rpgmutils::signal<void(State*, const std::string&)>& State::noteModified() {
  if (!m_noteModified) {
    m_noteModified.emplace();
  }
  return *m_noteModified;
}
rpgmutils::signal<void(State*, StateOverlay)>& State::overlayModified() {
  if (!m_overlayModified) {
    m_overlayModified.emplace();
  }
  return *m_overlayModified;
}
rpgmutils::signal<void(State*, int)>& State::priorityModified() {
  if (!m_priorityModified) {
    m_priorityModified.emplace();
  }
  return *m_priorityModified;
}
rpgmutils::signal<void(State*, bool)>& State::releaseByDamageModified() {
  if (!m_releaseByDamageModified) {
    m_releaseByDamageModified.emplace();
  }
  return *m_releaseByDamageModified;
}
rpgmutils::signal<void(State*, bool)>& State::removeAtBattleEndModified() {
  if (!m_removeAtBattleEndModified) {
    m_removeAtBattleEndModified.emplace();
  }
  return *m_removeAtBattleEndModified;
}
rpgmutils::signal<void(State*, bool)>& State::removeByDamageModified() {
  if (!m_removeByDamageModified) {
    m_removeByDamageModified.emplace();
  }
  return *m_removeByDamageModified;
}
rpgmutils::signal<void(State*, bool)>& State::removeByRestrictionModified() {
  if (!m_removeByRestrictionModified) {
    m_removeByRestrictionModified.emplace();
  }
  return *m_removeByRestrictionModified;
}
rpgmutils::signal<void(State*, bool)>& State::removeByWalkingModified() {
  if (!m_removeByWalkingModified) {
    m_removeByWalkingModified.emplace();
  }
  return *m_removeByWalkingModified;
}
rpgmutils::signal<void(State*, StateRestriction)>& State::restrictionModified() {
  if (!m_restrictionModified) {
    m_restrictionModified.emplace();
  }
  return *m_restrictionModified;
}
rpgmutils::signal<void(State*, int)>& State::stepsToRemoveModified() {
  if (!m_stepsToRemoveModified) {
    m_stepsToRemoveModified.emplace();
  }
  return *m_stepsToRemoveModified;
}
rpgmutils::signal<void(State*, const std::vector<Trait>&)>& State::traitsModified() {
  if (!m_traitsModified) {
    m_traitsModified.emplace();
  }
  return *m_traitsModified;
}
void to_json(nlohmann::ordered_json& j, const State& state) {
  j = {
      {"id", state.m_id},
      {"autoRemovalTiming", state.m_autoRemovalTiming},
      {"chanceByDamage", state.m_chanceByDamage},
      {"iconIndex", state.m_iconIndex},
      {"maxTurns", state.m_maxTurns},
      {"message1", state.m_message1},
      {"message2", state.m_message2},
      {"message3", state.m_message3},
      {"message4", state.m_message4},
      {"minTurns", state.m_minTurns},
      {"motion", state.m_motion},
      {"name", state.m_name},
      {"note", state.m_note},
      {"overlay", state.m_overlay},
      {"priority", state.m_priority},
      {"releaseByDamage", state.m_releaseByDamage},
      {"removeAtBattleEnd", state.m_removeAtBattleEnd},
      {"removeByDamage", state.m_removeByDamage},
      {"removeByRestriction", state.m_removeByRestriction},
      {"removeByWalking", state.m_removeByWalking},
      {"restriction", state.m_restriction},
      {"stepsToRemove", state.m_stepsToRemove},
      {"traits", state.m_traits},
  };
}

void from_json(const nlohmann::ordered_json& j, State& state) {
  state.m_id = j.value("id", state.m_id);
  state.m_autoRemovalTiming = j.value("autoRemovalTiming", state.m_autoRemovalTiming);
  state.m_chanceByDamage = j.value("chanceByDamage", state.m_chanceByDamage);
  state.m_iconIndex = j.value("iconIndex", state.m_iconIndex);
  state.m_maxTurns = j.value("maxTurns", state.m_maxTurns);
  state.m_message1 = j.value("message1", state.m_message1);
  state.m_message2 = j.value("message2", state.m_message2);
  state.m_message3 = j.value("message3", state.m_message3);
  state.m_message4 = j.value("message4", state.m_message4);
  state.m_minTurns = j.value("minTurns", state.m_minTurns);
  state.m_motion = j.value("motion", state.m_motion);
  state.m_name = j.value("name", state.m_name);
  state.m_note = j.value("note", state.m_note);
  state.m_overlay = j.value("overlay", state.m_overlay);
  state.m_priority = j.value("priority", state.m_priority);
  state.m_releaseByDamage = j.value("releaseByDamage", state.m_releaseByDamage);
  state.m_removeAtBattleEnd = j.value("removeAtBattleEnd", state.m_removeAtBattleEnd);
  state.m_removeByRestriction = j.value("removeByRestriction", state.m_removeByRestriction);
  state.m_removeByWalking = j.value("removeByWalking", state.m_removeByWalking);
  state.m_restriction = j.value("restriction", state.m_restriction);
  state.m_stepsToRemove = j.value("stepsToRemove", state.m_stepsToRemove);
  state.m_traits = j.value("traits", state.m_traits);
}


code:
#pragma once

#include "Database/Globals.hpp"
#include "Database/Trait.hpp"

class State final : public IModifiable {
  friend class StatesSerializer;
  friend void to_json(nlohmann::ordered_json& j, const State& state);
  friend void from_json(const nlohmann::ordered_json& j, State& state);

public:
  State() = default;
  State(const State& other);
  State& operator=(const State& other);
  State(State&& other) noexcept;
  State& operator=(State&& other) noexcept;

  [[nodiscard]] int id() const;
  void setId(int id);

  [[nodiscard]] AutoRemovalTiming autoRemovalTiming() const;
  void setAutoRemovalTiming(AutoRemovalTiming autoRemovalTiming);

  [[nodiscard]] int chanceByDamage() const;
  void setChanceByDamage(int chanceByDamage);

  [[nodiscard]] int iconIndex() const;
  void setIconIndex(int iconIndex);

  [[nodiscard]] int minTurns() const;
  void setMinTurns(int minTurns);

  [[nodiscard]] int maxTurns() const;
  void setMaxTurns(int maxTurns);

  [[nodiscard]] const std::string& message1() const;
  void setMessage1(const std::string& message1);

  [[nodiscard]] const std::string& message2() const;
  void setMessage2(const std::string& message2);
  [[nodiscard]] const std::string& message3() const;
  void setMessage3(const std::string& message3);

  [[nodiscard]] const std::string& message4() const;
  void setMessage4(const std::string& message4);

  [[nodiscard]] StateMotion motion() const;
  void setMotion(StateMotion motion);

  [[nodiscard]] const std::string& name() const;
  void setName(const std::string& name);

  [[nodiscard]] const std::string& note() const;
  void setNote(const std::string& note);

  [[nodiscard]] StateOverlay overlay() const;
  void setOverlay(StateOverlay overlay);

  [[nodiscard]] int priority() const;
  void setPriority(int priority);

  [[nodiscard]] bool releaseByDamage() const;
  void setReleaseByDamage(bool releaseByDamage);

  [[nodiscard]] bool removeAtBattleEnd() const;
  void setRemoveAtBattleEnd(bool removeAtBattleEnd);

  [[nodiscard]] bool removeByDamage() const;
  void setRemoveByDamage(bool removeByDamage);

  [[nodiscard]] bool removeByRestriction() const;
  void setRemoveByRestriction(bool removeByRestriction);

  [[nodiscard]] bool removeByWalking() const;
  void setRemoveByWalking(bool removeByWalking);

  [[nodiscard]] StateRestriction restriction() const;
  void setRestriction(StateRestriction restriction);

  [[nodiscard]] int stepsToRemove() const;
  void setStepsToRemove(int stepsToRemove);

  [[nodiscard]] std::vector<Trait>& traits();
  void setTraits(const std::vector<Trait>& traits);

  void restoreOriginal() override;
  void acceptChanges() override;
  nlohmann::ordered_json serializeOldValues() const override;

  bool isModified() const override {
    return IModifiable::isModified() | std::ranges::any_of(m_traits, [](const Trait& trait) { return trait.isModified(); });
  }

  [[nodiscard]] bool isValid() const { return m_isValid; }
  void setValid(const bool isValid) { m_isValid = isValid; }

  rpgmutils::signal<void(State*, int)>& idModified();
  rpgmutils::signal<void(State*, AutoRemovalTiming)>& autoRemovalTimingModified();
  rpgmutils::signal<void(State*, int)>& chanceByDamageModified();
  rpgmutils::signal<void(State*, int)>& iconIndexModified();
  rpgmutils::signal<void(State*, int)>& minTurnsModified();
  rpgmutils::signal<void(State*, int)>& maxTurnsModified();
  rpgmutils::signal<void(State*, const std::string&)>& message1Modified();
  rpgmutils::signal<void(State*, const std::string&)>& message2Modified();
  rpgmutils::signal<void(State*, const std::string&)>& message3Modified();
  rpgmutils::signal<void(State*, const std::string&)>& message4Modified();
  rpgmutils::signal<void(State*, StateMotion)>& motionModified();
  rpgmutils::signal<void(State*, const std::string&)>& nameModified();
  rpgmutils::signal<void(State*, const std::string&)>& noteModified();
  rpgmutils::signal<void(State*, StateOverlay)>& overlayModified();
  rpgmutils::signal<void(State*, int)>& priorityModified();
  rpgmutils::signal<void(State*, bool)>& releaseByDamageModified();
  rpgmutils::signal<void(State*, bool)>& removeAtBattleEndModified();
  rpgmutils::signal<void(State*, bool)>& removeByDamageModified();
  rpgmutils::signal<void(State*, bool)>& removeByRestrictionModified();
  rpgmutils::signal<void(State*, bool)>& removeByWalkingModified();
  rpgmutils::signal<void(State*, StateRestriction)>& restrictionModified();
  rpgmutils::signal<void(State*, int)>& stepsToRemoveModified();
  rpgmutils::signal<void(State*, const std::vector<Trait>&)>& traitsModified();

private:
  int m_id = 0;
  AutoRemovalTiming m_autoRemovalTiming = AutoRemovalTiming::None;
  int m_chanceByDamage = 100;
  int m_iconIndex = 0;
  int m_minTurns = 1;
  int m_maxTurns = 1;
  std::string m_message1;
  std::string m_message2;
  std::string m_message3;
  std::string m_message4;
  StateMotion m_motion = StateMotion::Normal;
  std::string m_name;
  std::string m_note;
  StateOverlay m_overlay = StateOverlay::None;
  int m_priority = 50;
  bool m_releaseByDamage;
  bool m_removeAtBattleEnd = false;
  bool m_removeByDamage = false;
  bool m_removeByRestriction = false;
  bool m_removeByWalking = false;
  StateRestriction m_restriction = StateRestriction::None;
  int m_stepsToRemove = 1000;
  std::vector<Trait> m_traits;

  std::optional<int> m_oldid;
  std::optional<AutoRemovalTiming> m_oldautoRemovalTiming;
  std::optional<int> m_oldchanceByDamage;
  std::optional<int> m_oldiconIndex;
  std::optional<int> m_oldminTurns;
  std::optional<int> m_oldmaxTurns;
  std::optional<std::string> m_oldmessage1;
  std::optional<std::string> m_oldmessage2;
  std::optional<std::string> m_oldmessage3;
  std::optional<std::string> m_oldmessage4;
  std::optional<StateMotion> m_oldmotion;
  std::optional<std::string> m_oldname;
  std::optional<std::string> m_oldnote;
  std::optional<StateOverlay> m_oldoverlay;
  std::optional<int> m_oldpriority;
  std::optional<bool> m_oldreleaseByDamage;
  std::optional<bool> m_oldremoveAtBattleEnd;
  std::optional<bool> m_oldremoveByDamage;
  std::optional<bool> m_oldremoveByRestriction;
  std::optional<bool> m_oldremoveByWalking;
  std::optional<StateRestriction> m_oldrestriction;
  std::optional<int> m_oldstepsToRemove;
  std::optional<std::vector<Trait>> m_oldtraits;

  std::optional<rpgmutils::signal<void(State*, int)>> m_idModified;
  std::optional<rpgmutils::signal<void(State*, AutoRemovalTiming)>> m_autoRemovalTimingModified;
  std::optional<rpgmutils::signal<void(State*, int)>> m_chanceByDamageModified;
  std::optional<rpgmutils::signal<void(State*, int)>> m_iconIndexModified;
  std::optional<rpgmutils::signal<void(State*, int)>> m_minTurnsModified;
  std::optional<rpgmutils::signal<void(State*, int)>> m_maxTurnsModified;
  std::optional<rpgmutils::signal<void(State*, const std::string&)>> m_message1Modified;
  std::optional<rpgmutils::signal<void(State*, const std::string&)>> m_message2Modified;
  std::optional<rpgmutils::signal<void(State*, const std::string&)>> m_message3Modified;
  std::optional<rpgmutils::signal<void(State*, const std::string&)>> m_message4Modified;
  std::optional<rpgmutils::signal<void(State*, StateMotion)>> m_motionModified;
  std::optional<rpgmutils::signal<void(State*, const std::string&)>> m_nameModified;
  std::optional<rpgmutils::signal<void(State*, const std::string&)>> m_noteModified;
  std::optional<rpgmutils::signal<void(State*, StateOverlay)>> m_overlayModified;
  std::optional<rpgmutils::signal<void(State*, int)>> m_priorityModified;
  std::optional<rpgmutils::signal<void(State*, bool)>> m_releaseByDamageModified;
  std::optional<rpgmutils::signal<void(State*, bool)>> m_removeAtBattleEndModified;
  std::optional<rpgmutils::signal<void(State*, bool)>> m_removeByDamageModified;
  std::optional<rpgmutils::signal<void(State*, bool)>> m_removeByRestrictionModified;
  std::optional<rpgmutils::signal<void(State*, bool)>> m_removeByWalkingModified;
  std::optional<rpgmutils::signal<void(State*, StateRestriction)>> m_restrictionModified;
  std::optional<rpgmutils::signal<void(State*, int)>> m_stepsToRemoveModified;
  std::optional<rpgmutils::signal<void(State*, const std::vector<Trait>&)>> m_traitsModified;

  /*!
   * @name m_isValid
   * @details
   * Indicates that this is an actual valid entry and not a dummy
   * When making a new entry make sure to set this to true or it won't be
   * serialized.
   */
  bool m_isValid{false};
};
void to_json(nlohmann::ordered_json& j, const State& state);
void from_json(const nlohmann::ordered_json& j, State& state);


code: #include "Database/States.hpp"


code:
#pragma once

#include "Database/State.hpp"

#include <vector>

class States final : public IModifiable {
public:
  [[nodiscard]] State* state(int id) {
    for (auto& state : m_states) {
      if (state.id() == id && state.isValid()) {
        return &state;
      }
    }
    return nullptr;
  }

  [[nodiscard]] const State* state(int id) const {
    for (auto& state : m_states) {
      if (state.id() == id && state.isValid()) {
        return &state;
      }
    }

    return nullptr;
  }

  [[nodiscard]] int count() const { return m_states.size() - 1; }

  [[nodiscard]] std::vector<State>& states() { return m_states; }
  [[nodiscard]] const std::vector<State>& states() const { return m_states; }

  void resize(int newSize) {
    ++newSize;
    const int oldSize = m_states.size();
    m_states.resize(newSize);
    if (newSize > oldSize) {
      for (int i = oldSize; i < m_states.size(); ++i) {
        m_states[i].setId(i);
      }
    }
  }

  bool isModified() const override {
    return IModifiable::isModified() | std::ranges::any_of(m_states, [](const State& state) { return state.isModified(); });
  }

private:
  std::vector<State> m_states;
};