code:
#include "Database/Armor.hpp"

Armor::Armor(const Armor& other)
: IModifiable(other)
, m_id(other.m_id)
, m_atypeId(other.m_atypeId)
, m_description(other.m_description)
, m_etypeId(other.m_etypeId)
, m_traits(other.m_traits)
, m_iconIndex(other.m_iconIndex)
, m_name(other.m_name)
, m_note(other.m_note)
, m_params(other.m_params)
, m_price(other.m_price)
, m_oldid(other.m_oldid)
, m_oldatypeId(other.m_oldatypeId)
, m_olddescription(other.m_olddescription)
, m_oldetypeId(other.m_oldetypeId)
, m_oldtraits(other.m_oldtraits)
, m_oldiconIndex(other.m_oldiconIndex)
, m_oldname(other.m_oldname)
, m_oldnote(other.m_oldnote)
, m_oldparams(other.m_oldparams)
, m_oldprice(other.m_oldprice)
, m_isValid(other.m_isValid) {}

Armor& Armor::operator=(const Armor& other) {
  IModifiable::operator=(other);
  m_id = other.m_id;
  m_atypeId = other.m_atypeId;
  m_description = other.m_description;
  m_etypeId = other.m_etypeId;
  m_traits = other.m_traits;
  m_iconIndex = other.m_iconIndex;
  m_name = other.m_name;
  m_note = other.m_note;
  m_params = other.m_params;
  m_price = other.m_price;
  m_oldid = other.m_oldid;
  m_oldatypeId = other.m_oldatypeId;
  m_olddescription = other.m_olddescription;
  m_oldetypeId = other.m_oldetypeId;
  m_oldtraits = other.m_oldtraits;
  m_oldiconIndex = other.m_oldiconIndex;
  m_oldname = other.m_oldname;
  m_oldnote = other.m_oldnote;
  m_oldparams = other.m_oldparams;
  m_oldprice = other.m_oldprice;
  m_isValid = other.m_isValid;
  return *this;
}
Armor::Armor(Armor&& other) noexcept
: IModifiable(other)
, m_id(other.m_id)
, m_atypeId(other.m_atypeId)
, m_description(other.m_description)
, m_etypeId(other.m_etypeId)
, m_traits(other.m_traits)
, m_iconIndex(other.m_iconIndex)
, m_name(other.m_name)
, m_note(other.m_note)
, m_params(other.m_params)
, m_price(other.m_price)
, m_oldid(other.m_oldid)
, m_oldatypeId(other.m_oldatypeId)
, m_olddescription(other.m_olddescription)
, m_oldetypeId(other.m_oldetypeId)
, m_oldtraits(other.m_oldtraits)
, m_oldiconIndex(other.m_oldiconIndex)
, m_oldname(other.m_oldname)
, m_oldnote(other.m_oldnote)
, m_oldparams(other.m_oldparams)
, m_oldprice(other.m_oldprice)
, m_isValid(other.m_isValid) {}

Armor& Armor::operator=(Armor&& other) noexcept {
  IModifiable::operator=(std::move(other));
  m_id = other.m_id;
  m_atypeId = other.m_atypeId;
  m_description = other.m_description;
  m_etypeId = other.m_etypeId;
  m_traits = other.m_traits;
  m_iconIndex = other.m_iconIndex;
  m_name = other.m_name;
  m_note = other.m_note;
  m_params = other.m_params;
  m_price = other.m_price;
  m_oldid = other.m_oldid;
  m_oldatypeId = other.m_oldatypeId;
  m_olddescription = other.m_olddescription;
  m_oldetypeId = other.m_oldetypeId;
  m_oldtraits = other.m_oldtraits;
  m_oldiconIndex = other.m_oldiconIndex;
  m_oldname = other.m_oldname;
  m_oldnote = other.m_oldnote;
  m_oldparams = other.m_oldparams;
  m_oldprice = other.m_oldprice;
  m_isValid = other.m_isValid;
  return *this;
}

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

[[nodiscard]] int Armor::atypeId() const { return m_atypeId; }
void Armor::setAtypeId(const int atypeId) {
  MODIFIABLE_SET_OLD_VALUE(atypeId);
  m_atypeId = atypeId;
  if (!signalsDisabled()) {
   emit_signal(atypeModified(), this, m_atypeId);
  }
  setModified();
}

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

[[nodiscard]] int Armor::etypeId() const { return m_etypeId; }
void Armor::setEtypeId(const int etypeId) {
  MODIFIABLE_SET_OLD_VALUE(etypeId);
  m_etypeId = etypeId;
  if (!signalsDisabled()) {
   emit_signal(etypeModified(), this, m_etypeId);
  }
  setModified();
}

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

Trait& Armor::trait(const int index) {
  assert(index >= 0 && index < m_traits.size());
  return m_traits[index];
}

const Trait& Armor::trait(const int index) const {
  assert(index >= 0 && index < m_traits.size());
  return m_traits[index];
}

void Armor::setTrait(const int index, const Trait& trait) {
  assert(index >= 0 && index < m_traits.size());
  MODIFIABLE_SET_OLD_VALUE(traits);
  m_traits[index] = trait;
  if (!signalsDisabled()) {
   emit_signal(traitModified(), this, index, trait);
  }
  setModified();
}

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

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

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

[[nodiscard]] const std::array<int, 8>& Armor::params() const { return m_params; }
void Armor::setParams(const std::array<int, 8>& params) {
  MODIFIABLE_SET_OLD_VALUE(params);
  m_params = params;
  if (!signalsDisabled()) {
   emit_signal(paramsModified(), this, m_params);
  }
  setModified();
}
[[nodiscard]] int Armor::param(const int index) const {
  assert(index >= 0 && index < m_params.size());
  return m_params[index];
}

void Armor::setParam(const int index, const int param) {
  assert(param >= 0 && param < m_params.size());
  MODIFIABLE_SET_OLD_VALUE(params);
  m_params[index] = param;
  if (!signalsDisabled()) {
   emit_signal(paramModified(), this, index, param);
  }
  setModified();
}

[[nodiscard]] int Armor::price() const { return m_price; }
void Armor::setPrice(const int price) {
  MODIFIABLE_SET_OLD_VALUE(price);
  m_price = price;
  if (!signalsDisabled()) {
   emit_signal(priceModified(), this, m_price);
  }
  setModified();
}
[[nodiscard]] bool Armor::isValid() const { return m_isValid; }
void Armor::setValid(const bool isValid) { m_isValid = isValid; }

void Armor::restoreOriginal() {
  IModifiable::restoreOriginal();
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(id);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(atypeId);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(description);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(etypeId);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(traits);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(iconIndex);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(name);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(note);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(params);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(price);
}

void Armor::acceptChanges() {
  IModifiable::acceptChanges();
  MODIFIABLE_ACCEPT_VALUE(id);
  MODIFIABLE_ACCEPT_VALUE(atypeId);
  MODIFIABLE_ACCEPT_VALUE(description);
  MODIFIABLE_ACCEPT_VALUE(etypeId);
  MODIFIABLE_ACCEPT_VALUE(iconIndex);
  MODIFIABLE_ACCEPT_VALUE(name);
  MODIFIABLE_ACCEPT_VALUE(note);
  MODIFIABLE_ACCEPT_VALUE(params);
  MODIFIABLE_ACCEPT_VALUE(price);
}

nlohmann::ordered_json Armor::serializeOldValues() const {
  return {
      MODIFIABLE_SERIALIZE_VALUE(id),   MODIFIABLE_SERIALIZE_VALUE(atypeId), MODIFIABLE_SERIALIZE_VALUE(description), MODIFIABLE_SERIALIZE_VALUE(etypeId), MODIFIABLE_SERIALIZE_VALUE(iconIndex),
      MODIFIABLE_SERIALIZE_VALUE(name), MODIFIABLE_SERIALIZE_VALUE(note),    MODIFIABLE_SERIALIZE_VALUE(params),      MODIFIABLE_SERIALIZE_VALUE(price),
  };
}

// SIGNALS
rpgmutils::signal<void(Armor*, int)>& Armor::idModified() {
  if (!m_idModified) {
    m_idModified.emplace();
  }
  return *m_idModified;
}

rpgmutils::signal<void(Armor*, int)>& Armor::atypeModified() {
  if (!m_atypeModified) {
    m_atypeModified.emplace();
  }
  return *m_atypeModified;
}

rpgmutils::signal<void(Armor*, std::string_view)>& Armor::descriptionModified() {
  if (!m_descriptionModified) {
    m_descriptionModified.emplace();
  }
  return *m_descriptionModified;
}

rpgmutils::signal<void(Armor*, int)>& Armor::etypeModified() {
  if (!m_etypeModified) {
    m_etypeModified.emplace();
  }
  return *m_etypeModified;
}

rpgmutils::signal<void(Armor*, const std::vector<Trait>&)>& Armor::traitsModified() {
  if (!m_traitsModified) {
    m_traitsModified.emplace();
  }
  return *m_traitsModified;
}

rpgmutils::signal<void(Armor*, int, const Trait&)>& Armor::traitModified() {
  if (!m_traitModified) {
    m_traitModified.emplace();
  }
  return *m_traitModified;
}

rpgmutils::signal<void(Armor*, int)>& Armor::iconIndexModified() {
  if (!m_iconIndexModified) {
    m_iconIndexModified.emplace();
  }
  return *m_iconIndexModified;
}

rpgmutils::signal<void(Armor*, std::string_view)>& Armor::nameModified() {
  if (!m_nameModified) {
    m_nameModified.emplace();
  }
  return *m_nameModified;
}
rpgmutils::signal<void(Armor*, std::string_view)>& Armor::noteModified() {
  if (!m_noteModified) {
    m_noteModified.emplace();
  }
  return *m_noteModified;
}
rpgmutils::signal<void(Armor*, const std::array<int, 8>&)>& Armor::paramsModified() {
  if (!m_paramsModified) {
    m_paramsModified.emplace();
  }
  return *m_paramsModified;
}
rpgmutils::signal<void(Armor*, int, int)>& Armor::paramModified() {
  if (!m_paramModified) {
    m_paramModified.emplace();
  }
  return *m_paramModified;
}
rpgmutils::signal<void(Armor*, int)>& Armor::priceModified() {
  if (!m_priceModified) {
    m_priceModified.emplace();
  }
  return *m_priceModified;
}

void to_json(nlohmann::ordered_json& out, const Armor& armor) {
  out = {
      {"id", armor.m_id},     {"atypeId", armor.m_atypeId}, {"description", armor.m_description}, {"etypeId", armor.m_etypeId}, {"traits", armor.m_traits}, {"iconIndex", armor.m_iconIndex},
      {"name", armor.m_name}, {"note", armor.m_note},       {"params", armor.m_params},           {"price", armor.m_price},
  };
}

void from_json(const nlohmann::ordered_json& from, Armor& armor) {
  armor.m_id = from.value("id", armor.m_id);
  armor.m_atypeId = from.value("atypeId", armor.m_atypeId);
  armor.m_description = from.value("description", armor.m_description);
  armor.m_etypeId = from.value("etypeId", armor.m_etypeId);
  armor.m_traits = from.value("traits", armor.m_traits);
  armor.m_iconIndex = from.value("iconIndex", armor.m_iconIndex);
  armor.m_name = from.value("name", armor.m_name);
  armor.m_note = from.value("note", armor.m_note);
  armor.m_params = from.value("params", armor.m_params);
  armor.m_price = from.value("price", armor.m_price);
}


code:
#pragma once

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

class Armor final : public IModifiable {
  friend class ArmorsSerializer;
  friend void to_json(nlohmann::ordered_json& out, const Armor& armor);
  friend void from_json(const nlohmann::ordered_json& from, Armor& armor);

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

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

  [[nodiscard]] int atypeId() const;
  void setAtypeId(int atypeId);

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

  [[nodiscard]] int etypeId() const;
  void setEtypeId(int etypeId);

  [[nodiscard]] std::vector<Trait>& traits();
  [[nodiscard]] const std::vector<Trait>& traits() const;
  void setTraits(const std::vector<Trait>& traits);
  [[nodiscard]] Trait& trait(int index);
  [[nodiscard]] const Trait& trait(int index) const;
  void setTrait(int index, const Trait& trait);

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

  [[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]] const std::array<int, 8>& params() const;
  void setParams(const std::array<int, 8>& params);
  [[nodiscard]] int param(int index) const;
  void setParam(int index, int param);

  [[nodiscard]] int price() const;
  void setPrice(int price);

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

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

  rpgmutils::signal<void(Armor*, int)>& idModified();
  rpgmutils::signal<void(Armor*, int)>& atypeModified();
  rpgmutils::signal<void(Armor*, std::string_view)>& descriptionModified();
  rpgmutils::signal<void(Armor*, int)>& etypeModified();
  rpgmutils::signal<void(Armor*, const std::vector<Trait>&)>& traitsModified();
  rpgmutils::signal<void(Armor*, int, const Trait&)>& traitModified();
  rpgmutils::signal<void(Armor*, int)>& iconIndexModified();
  rpgmutils::signal<void(Armor*, std::string_view)>& nameModified();
  rpgmutils::signal<void(Armor*, std::string_view)>& noteModified();
  rpgmutils::signal<void(Armor*, const std::array<int, 8>&)>& paramsModified();
  rpgmutils::signal<void(Armor*, int, int)>& paramModified();
  rpgmutils::signal<void(Armor*, int)>& priceModified();

private:
  int m_id{};
  int m_atypeId{};
  std::string m_description;
  int m_etypeId{};
  std::vector<Trait> m_traits;
  int m_iconIndex{};
  std::string m_name;
  std::string m_note;
  std::array<int, 8> m_params{}; // Attack, Defence, M.Attack, M.Defence, Agility, Luck, Max HP, Max MP
  int m_price{};

  std::optional<int> m_oldid;
  std::optional<int> m_oldatypeId;
  std::optional<std::string> m_olddescription;
  std::optional<int> m_oldetypeId;
  std::optional<std::vector<Trait>> m_oldtraits;
  std::optional<int> m_oldiconIndex;
  std::optional<std::string> m_oldname;
  std::optional<std::string> m_oldnote;
  std::optional<std::array<int, 8>> m_oldparams; // Attack, Defence, M.Attack, M.Defence, Agility, Luck, Max HP, Max MP
  std::optional<int> m_oldprice;

  std::optional<rpgmutils::signal<void(Armor*, int)>> m_idModified;
  std::optional<rpgmutils::signal<void(Armor*, int)>> m_atypeModified;
  std::optional<rpgmutils::signal<void(Armor*, std::string_view)>> m_descriptionModified;
  std::optional<rpgmutils::signal<void(Armor*, int)>> m_etypeModified;
  std::optional<rpgmutils::signal<void(Armor*, const std::vector<Trait>&)>> m_traitsModified;
  std::optional<rpgmutils::signal<void(Armor*, int, const Trait&)>> m_traitModified;
  std::optional<rpgmutils::signal<void(Armor*, int)>> m_iconIndexModified;
  std::optional<rpgmutils::signal<void(Armor*, std::string_view)>> m_nameModified;
  std::optional<rpgmutils::signal<void(Armor*, std::string_view)>> m_noteModified;
  std::optional<rpgmutils::signal<void(Armor*, const std::array<int, 8>&)>> m_paramsModified;
  std::optional<rpgmutils::signal<void(Armor*, int, int)>> m_paramModified;
  std::optional<rpgmutils::signal<void(Armor*, int)>> m_priceModified;
  /*!
   * @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& out, const Armor& armor);
void from_json(const nlohmann::ordered_json& from, Armor& armor);

repo_name: Antidote/openrpgeditor

license: mit


code: #include "Armors.hpp"


code:
#pragma once

#include "Database/Armor.hpp"
#include <nlohmann/json.hpp>

class Armors final : public IModifiable {
public:
  [[nodiscard]] std::vector<Armor>& armors() { return m_armors; }
  [[nodiscard]] const std::vector<Armor>& armors() const { return m_armors; }

  [[nodiscard]] Armor* armor(const int id) {
    for (auto& armor : m_armors) {
      if (armor.id() == id && armor.isValid()) {
        return &armor;
      }
    }
    return nullptr;
  }

  [[nodiscard]] const Armor* armor(const int id) const {
    for (const auto& armor : m_armors) {
      if (armor.id() == id && armor.isValid()) {
        return &armor;
      }
    }

    return nullptr;
  }

  std::vector<Armor>& armorList() { return m_armors; }
  const std::vector<Armor>& armorList() const { return m_armors; }
  int count() const { return m_armors.size() - 1; }

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

  bool isModified() const override {
    return IModifiable::isModified() | std::ranges::any_of(m_armors, [](const auto& arm) { return arm.isModified(); });
  }

private:
  std::vector<Armor> m_armors;
};


code:
#include "Database/Audio.hpp"

Audio::Audio(const Audio& other)
: IModifiable(other)
, m_name(other.m_name)
, m_pan(other.m_pan)
, m_pitch(other.m_pitch)
, m_volume(other.m_volume)
, m_oldname(other.m_oldname)
, m_oldpan(other.m_oldpan)
, m_oldvolume(other.m_oldvolume) {}

Audio& Audio::operator=(const Audio& other) {
  m_name = other.m_name;
  m_pan = other.m_pan;
  m_pitch = other.m_pitch;
  m_volume = other.m_volume;
  m_oldname = other.m_oldname;
  m_oldpan = other.m_oldpan;
  m_oldvolume = other.m_oldvolume;
  return *this;
}

Audio::Audio(Audio&& other) noexcept
: IModifiable(std::move(other))
, m_name(std::move(other.m_name))
, m_pan(other.m_pan)
, m_pitch(other.m_pitch)
, m_volume(other.m_volume)
, m_oldname(std::move(other.m_oldname))
, m_oldpan(other.m_oldpan)
, m_oldvolume(other.m_oldvolume) {}

Audio& Audio::operator=(Audio&& other) noexcept {
  IModifiable::operator=(std::move(other));
  m_name = std::move(other.m_name);
  m_pan = other.m_pan;
  m_pitch = other.m_pitch;
  m_volume = other.m_volume;
  m_oldname = std::move(other.m_oldname);
  m_oldpan = other.m_oldpan;
  m_oldvolume = other.m_oldvolume;
  return *this;
}

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

int Audio::pan() const { return m_pan; }
void Audio::setPan(int pan) {
  MODIFIABLE_SET_OLD_VALUE(pan);
  m_pan = pan;
  if (!signalsDisabled()) {
    emit_signal(panModified(), this, pan);
  }
  setModified();
}

int Audio::pitch() const { return m_pitch; }
void Audio::setPitch(int pitch) {
  MODIFIABLE_SET_OLD_VALUE(pitch);
  m_pitch = pitch;
  if (!signalsDisabled()) {
    emit_signal(pitchModified(), this, pitch);
  }
  setModified();
}
int Audio::volume() const { return m_volume; }
void Audio::setVolume(int volume) {
  MODIFIABLE_SET_OLD_VALUE(volume);
  m_volume = volume;
  if (!signalsDisabled()) {
   emit_signal( volumeModified(), this, volume);
  }
  setModified();
}

void Audio::restoreOriginal() {
  IModifiable::restoreOriginal();
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(name);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(pan);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(pitch);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(volume);
}

void Audio::acceptChanges() {
  IModifiable::acceptChanges();
  MODIFIABLE_ACCEPT_VALUE(name);
  MODIFIABLE_ACCEPT_VALUE(pan);
  MODIFIABLE_ACCEPT_VALUE(pitch);
  MODIFIABLE_ACCEPT_VALUE(volume);
}

nlohmann::ordered_json Audio::serializeOldValues() const {
  return {
      MODIFIABLE_SERIALIZE_VALUE(name),
      MODIFIABLE_SERIALIZE_VALUE(pan),
      MODIFIABLE_SERIALIZE_VALUE(pitch),
      MODIFIABLE_SERIALIZE_VALUE(volume),
  };
}

rpgmutils::signal<void(Audio*, std::string_view)>& Audio::nameModified() {
  if (!m_nameModified) {
    m_nameModified.emplace();
  }
  return *m_nameModified;
}

rpgmutils::signal<void(Audio*, int)>& Audio::panModified() {
  if (!m_panModified) {
    m_panModified.emplace();
  }
  return *m_panModified;
}

rpgmutils::signal<void(Audio*, int)>& Audio::pitchModified() {
  if (!m_pitchModified) {
    m_pitchModified.emplace();
  }
  return *m_pitchModified;
}

rpgmutils::signal<void(Audio*, int)>& Audio::volumeModified() {
  if (!m_volumeModified) {
    m_volumeModified.emplace();
  }
  return *m_volumeModified;
}

void to_json(nlohmann::ordered_json& out, const Audio& audio) {
  out = {
      {"name", audio.m_name},
      {"pan", audio.m_pan},
      {"pitch", audio.m_pitch},
      {"volume", audio.m_volume},
  };
}

void from_json(const nlohmann::ordered_json& from, Audio& audio) {
  audio.m_name = from.value("name", audio.m_name);
  audio.m_pan = from.value("pan", audio.m_pan);
  audio.m_pitch = from.value("pitch", audio.m_pitch);
  audio.m_volume = from.value("volume", audio.m_volume);
}


code:
#pragma once

#include "Database/IModifiable.hpp"

#include <string>

#include "nlohmann/json.hpp"

class Audio final : public IModifiable {
  friend void to_json(nlohmann::ordered_json& out, const Audio& audio);
  friend void from_json(const nlohmann::ordered_json& from, Audio& audio);

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

  const std::string& name() const;
  void setName(const std::string& name);

  int pan() const;
  void setPan(int pan);

  int pitch() const;
  void setPitch(int pitch);

  int volume() const;
  void setVolume(int volume);

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

  rpgmutils::signal<void(Audio*, std::string_view)>& nameModified();
  rpgmutils::signal<void(Audio*, int)>& panModified();
  rpgmutils::signal<void(Audio*, int)>& pitchModified();
  rpgmutils::signal<void(Audio*, int)>& volumeModified();

  bool operator==(const Audio& other) const { return m_name == other.m_name && m_pan == other.m_pan && m_pitch == other.m_pitch && m_volume == other.m_volume; }

private:
  std::string m_name; // base name of the file (without extension) in audio/se
  int m_pan = 0;
  int m_pitch = 100;
  int m_volume = 100;

  std::optional<std::string> m_oldname;
  std::optional<int> m_oldpan;
  std::optional<int> m_oldpitch;
  std::optional<int> m_oldvolume;

  std::optional<rpgmutils::signal<void(Audio*, std::string_view)>> m_nameModified;
  std::optional<rpgmutils::signal<void(Audio*, int)>> m_panModified;
  std::optional<rpgmutils::signal<void(Audio*, int)>> m_pitchModified;
  std::optional<rpgmutils::signal<void(Audio*, int)>> m_volumeModified;
};
void to_json(nlohmann::json& out, const Audio& audio);
void from_json(const nlohmann::json& from, Audio& audio);


code:
#include "Database/Battler.hpp"

Battler::Battler(const Battler& other)
: IModifiable(other)
, m_actorId(other.m_actorId)
, m_equips(other.m_equips)
, m_level(other.m_level)
, m_oldactorId(other.m_oldactorId)
, m_oldequips(other.m_oldequips)
, m_oldlevel(other.m_oldlevel) {}

Battler& Battler::operator=(const Battler& other) {
  IModifiable::operator=(other);
  m_actorId = other.m_actorId;
  m_equips = other.m_equips;
  m_level = other.m_level;
  m_oldactorId = other.m_oldactorId;
  m_oldequips = other.m_oldequips;
  m_oldlevel = other.m_oldlevel;
  return *this;
}

Battler::Battler(Battler&& other) noexcept
: IModifiable(std::move(other))
, m_actorId(other.m_actorId)
, m_equips(std::move(other.m_equips))
, m_level(other.m_level)
, m_oldactorId(other.m_oldactorId)
, m_oldequips(std::move(other.m_oldequips))
, m_oldlevel(other.m_oldlevel) {}

Battler& Battler::operator=(Battler&& other) noexcept {
  IModifiable::operator=(std::move(other));
  m_actorId = other.m_actorId;
  m_equips = std::move(other.m_equips);
  m_level = other.m_level;
  m_oldactorId = other.m_oldactorId;
  m_oldequips = std::move(other.m_oldequips);
  m_oldlevel = other.m_oldlevel;
  return *this;
}
int Battler::actorId() const { return m_actorId; }
void Battler::setActorId(int actorId) {
  MODIFIABLE_SET_OLD_VALUE(actorId);
  m_actorId = actorId;
  if (!signalsDisabled()) {
    emit_signal(actorIdModified(), this, actorId);
  }
  setModified();
}

const std::vector<int>& Battler::equips() const { return m_equips; }
void Battler::setEquips(const std::vector<int>& equips) {
  MODIFIABLE_SET_OLD_VALUE(equips);
  m_equips = equips;
  if (!signalsDisabled()) {
    emit_signal(equipsModified(), this, m_equips);
  }
  setModified();
}
void Battler::setEquip(int index, int equip) {
  assert(index >= 0 && index < m_equips.size());
  MODIFIABLE_SET_OLD_VALUE(equips);
  m_equips[index] = equip;
  if (!signalsDisabled()) {
    emit_signal(equipModified(), this, index, equip);
  }
  setModified();
}

int Battler::level() const { return m_level; }
void Battler::setLevel(int level) {
  MODIFIABLE_SET_OLD_VALUE(level);
  m_level = level;
  if (!signalsDisabled()) {
    emit_signal(levelModified(), this, level);
  }
  setModified();
}

void Battler::restoreOriginal() {
  IModifiable::restoreOriginal();
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(actorId);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(equips);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(level);
}

void Battler::acceptChanges() {
  IModifiable::acceptChanges();
  MODIFIABLE_ACCEPT_VALUE(actorId);
  MODIFIABLE_ACCEPT_VALUE(equips);
  MODIFIABLE_ACCEPT_VALUE(level);
}

nlohmann::ordered_json Battler::serializeOldValues() const {
  return {
      MODIFIABLE_SERIALIZE_VALUE(actorId),
      MODIFIABLE_SERIALIZE_VALUE(equips),
      MODIFIABLE_SERIALIZE_VALUE(level),
  };
}

rpgmutils::signal<void(Battler*, int)>& Battler::actorIdModified() {
  if (!m_actorIdModified) {
    m_actorIdModified.emplace();
  }
  return *m_actorIdModified;
}

rpgmutils::signal<void(Battler*, const std::vector<int>&)>& Battler::equipsModified() {
  if (!m_equipsModified) {
    m_equipsModified.emplace();
  }
  return *m_equipsModified;
}
rpgmutils::signal<void(Battler*, int, int)>& Battler::equipModified() {
  if (!m_equipModified) {
    m_equipModified.emplace();
  }
  return *m_equipModified;
}

rpgmutils::signal<void(Battler*, int)>& Battler::levelModified() {
  if (!m_levelModified) {
    m_levelModified.emplace();
  }
  return *m_levelModified;
}

void to_json(nlohmann::ordered_json& out, const Battler& battler) {
  out = {
      {"actorId", battler.m_actorId},
      {"equips", battler.m_equips},
      {"level", battler.m_level},
  };
}
void from_json(const nlohmann::ordered_json& from, Battler& battler) {
  battler.m_actorId = from.value("actorId", battler.m_actorId);
  battler.m_equips = from.value("equips", battler.m_equips);
  battler.m_level = from.value("level", battler.m_level);
}


code:
#pragma once
#include "Database/IModifiable.hpp"
#include "nlohmann/json.hpp"

class Battler final : public IModifiable {
  friend void to_json(nlohmann::ordered_json& out, const Battler& battler);
  friend void from_json(const nlohmann::ordered_json& from, Battler& battler);

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

  int actorId() const;
  void setActorId(int actorId);

  const std::vector<int>& equips() const;
  void setEquips(const std::vector<int>& equips);
  void setEquip(int index, int equip);

  int level() const;
  void setLevel(int level);

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

  rpgmutils::signal<void(Battler*, int)>& actorIdModified();
  rpgmutils::signal<void(Battler*, const std::vector<int>&)>& equipsModified();
  rpgmutils::signal<void(Battler*, int, int)>& equipModified();
  rpgmutils::signal<void(Battler*, int)>& levelModified();

  bool operator==(const Battler& other) const { return m_actorId == other.m_actorId && m_equips == other.m_equips && m_level == other.m_level; }

private:
  int m_actorId{};
  std::vector<int> m_equips;
  int m_level{};

  std::optional<int> m_oldactorId;
  std::optional<std::vector<int>> m_oldequips;
  std::optional<int> m_oldlevel;

  std::optional<rpgmutils::signal<void(Battler*, int)>> m_actorIdModified;
  std::optional<rpgmutils::signal<void(Battler*, const std::vector<int>&)>> m_equipsModified;
  std::optional<rpgmutils::signal<void(Battler*, int, int)>> m_equipModified;
  std::optional<rpgmutils::signal<void(Battler*, int)>> m_levelModified;
};

void to_json(nlohmann::ordered_json& out, const Battler& battler);
void from_json(const nlohmann::ordered_json& from, Battler& battler);


code:
#include "Database/Class.hpp"

Class::Learning::Learning(const Learning& other)
: IModifiable(other)
, m_level(other.m_level)
, m_note(other.m_note)
, m_skillId(other.m_skillId) {}

Class::Learning& Class::Learning::operator=(const Learning& other) {
  IModifiable::operator=(other);
  m_level = other.m_level;
  m_note = other.m_note;
  m_skillId = other.m_skillId;
  return *this;
}

Class::Learning::Learning(Learning&& other) noexcept
: IModifiable(other)
, m_level(other.m_level)
, m_note(std::move(other.m_note))
, m_skillId(other.m_skillId) {}

int Class::Learning::level() const { return m_level; }
void Class::Learning::setLevel(const int level) {
  MODIFIABLE_SET_OLD_VALUE(level);
  m_level = level;
  if (!signalsDisabled()) {
    emit_signal(levelModified(), this, level);
  }
  setModified();
}

const std::string& Class::Learning::note() const { return m_note; }

void Class::Learning::setNote(const std::string& note) {
  MODIFIABLE_SET_OLD_VALUE(note);
  m_note = note;
  if (!signalsDisabled()) {
    emit_signal(noteModified(), this, note);
  }
  setModified();
}

int Class::Learning::skillId() const { return m_skillId; }

void Class::Learning::setSkillId(const int skillId) {
  MODIFIABLE_SET_OLD_VALUE(skillId);
  m_skillId = skillId;
  if (!signalsDisabled()) {
    emit_signal(skillIdModified(), this, skillId);
  }
  setModified();
}
void Class::Learning::restoreOriginal() {
  IModifiable::restoreOriginal();
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(level);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(note);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(skillId);
}
void Class::Learning::acceptChanges() {
  IModifiable::acceptChanges();
  MODIFIABLE_ACCEPT_VALUE(level);
  MODIFIABLE_ACCEPT_VALUE(note);
  MODIFIABLE_ACCEPT_VALUE(skillId);
}
nlohmann::ordered_json Class::Learning::serializeOldValues() const {
  return {
      MODIFIABLE_SERIALIZE_VALUE(level),
      MODIFIABLE_SERIALIZE_VALUE(note),
      MODIFIABLE_SERIALIZE_VALUE(skillId),
  };
}

rpgmutils::signal<void(Class::Learning*, int)>& Class::Learning::levelModified() {
  if (!m_levelModified) {
    m_levelModified.emplace();
  }
  return *m_levelModified;
}

rpgmutils::signal<void(Class::Learning*, const std::string&)>& Class::Learning::noteModified() {
  if (!m_noteModified) {
    m_noteModified.emplace();
  }
  return *m_noteModified;
}

rpgmutils::signal<void(Class::Learning*, int)>& Class::Learning::skillIdModified() {
  if (!m_skillIdModified) {
    m_skillIdModified.emplace();
  }
  return *m_skillIdModified;
}

Class::Learning& Class::Learning::operator=(Learning&& other) noexcept {
  IModifiable::operator=(std::move(other));
  m_level = other.m_level;
  m_note = std::move(other.m_note);
  m_skillId = other.m_skillId;
  return *this;
}

Class::Class(const Class& other)
: IModifiable(other)
, m_id(other.m_id)
, m_expParams(other.m_expParams)
, m_traits(other.m_traits)
, m_learnings(other.m_learnings)
, m_name(other.m_name)
, m_note(other.m_note)
, m_params(other.m_params)
, m_oldid(other.m_oldid)
, m_oldexpParams(other.m_oldexpParams)
, m_oldtraits(other.m_oldtraits)
, m_oldlearnings(other.m_oldlearnings)
, m_oldname(other.m_oldname)
, m_oldnote(other.m_oldnote)
, m_oldparams(other.m_oldparams)
, m_isValid(other.m_isValid) {}

Class& Class::operator=(const Class& other) {
  IModifiable::operator=(other);
  m_id = other.m_id;
  m_expParams = other.m_expParams;
  m_traits = other.m_traits;
  m_learnings = other.m_learnings;
  m_name = other.m_name;
  m_note = other.m_note;
  m_params = other.m_params;
  m_oldid = other.m_oldid;
  m_oldexpParams = other.m_oldexpParams;
  m_oldtraits = other.m_oldtraits;
  m_oldlearnings = other.m_oldlearnings;
  m_oldname = other.m_oldname;
  m_oldnote = other.m_oldnote;
  m_oldparams = other.m_oldparams;
  m_isValid = other.m_isValid;
  return *this;
}

Class::Class(Class&& other) noexcept
: IModifiable(other)
, m_id(other.m_id)
, m_expParams(std::move(other.m_expParams))
, m_traits(std::move(other.m_traits))
, m_learnings(std::move(other.m_learnings))
, m_name(std::move(other.m_name))
, m_note(std::move(other.m_note))
, m_params(std::move(other.m_params))
, m_oldid(other.m_oldid)
, m_oldexpParams(std::move(other.m_oldexpParams))
, m_oldtraits(std::move(other.m_oldtraits))
, m_oldlearnings(std::move(other.m_oldlearnings))
, m_oldname(std::move(other.m_oldname))
, m_oldnote(std::move(other.m_oldnote))
, m_oldparams(std::move(other.m_oldparams))
, m_isValid(other.m_isValid) {}

Class& Class::operator=(Class&& other) noexcept {
  IModifiable::operator=(std::move(other));
  m_id = other.m_id;
  m_expParams = std::move(other.m_expParams);
  m_traits = std::move(other.m_traits);
  m_learnings = std::move(other.m_learnings);
  m_name = std::move(other.m_name);
  m_note = std::move(other.m_note);
  m_params = std::move(other.m_params);
  m_oldid = other.m_oldid;
  m_oldexpParams = std::move(other.m_oldexpParams);
  m_oldtraits = std::move(other.m_oldtraits);
  m_oldlearnings = std::move(other.m_oldlearnings);
  m_oldname = std::move(other.m_oldname);
  m_oldnote = std::move(other.m_oldnote);
  m_oldparams = std::move(other.m_oldparams);
  m_isValid = other.m_isValid;
  return *this;
}

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

const std::array<int, 4>& Class::expParams() const { return m_expParams; }
void Class::setExpParams(const std::array<int, 4>& expParams) {
  MODIFIABLE_SET_OLD_VALUE(expParams);
  m_expParams = expParams;
  if (!signalsDisabled()) {
   emit_signal( expParamsModified(), this, expParams);
  }
  setModified();
}

void Class::setExpParam(const int index, const int value) {
  assert(index >= 0 && index < m_expParams.size());
  MODIFIABLE_SET_OLD_VALUE(expParams);
  m_expParams[index] = value;
  if (!signalsDisabled()) {
    emit_signal(expParamModified(), this, index, value);
  }
  setModified();
}

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

const std::vector<Class::Learning>& Class::learnings() const { return m_learnings; }
void Class::setLearnings(const std::vector<Learning>& learnings) {
  MODIFIABLE_SET_OLD_VALUE(learnings);
  m_learnings = learnings;
  if (!signalsDisabled()) {
    emit_signal(learningsModified(), this, learnings);
  }
  setModified();
}

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

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

[[nodiscard]] const std::array<std::array<int, 99>, 8>& Class::params() const { return m_params; }
void Class::setParams(const std::array<std::array<int, 99>, 8>& params) {
  MODIFIABLE_SET_OLD_VALUE(params);
  m_params = params;
  if (!signalsDisabled()) {
    emit_signal(paramsModified(), this, params);
  }
  setModified();
}

bool Class::isValid() const { return m_isValid; }
void Class::setValid(const bool isValid) { m_isValid = isValid; }

void Class::restoreOriginal() {
  IModifiable::restoreOriginal();
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(id);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(expParams);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(traits);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(learnings);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(name);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(note);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(params);
}

void Class::acceptChanges() {
  IModifiable::acceptChanges();
  MODIFIABLE_ACCEPT_VALUE(id);
  MODIFIABLE_ACCEPT_VALUE(expParams);
  MODIFIABLE_ACCEPT_VALUE(traits);
  MODIFIABLE_ACCEPT_VALUE(learnings);
  MODIFIABLE_ACCEPT_VALUE(name);
  MODIFIABLE_ACCEPT_VALUE(note);
  MODIFIABLE_ACCEPT_VALUE(params);
}

nlohmann::ordered_json Class::serializeOldValues() const {
  return {
      MODIFIABLE_SERIALIZE_VALUE(id),   MODIFIABLE_SERIALIZE_VALUE(expParams), MODIFIABLE_SERIALIZE_VALUE(traits), MODIFIABLE_SERIALIZE_VALUE_CHILD_ARRAY(learnings),
      MODIFIABLE_SERIALIZE_VALUE(name), MODIFIABLE_SERIALIZE_VALUE(note),      MODIFIABLE_SERIALIZE_VALUE(params),
  };
}

rpgmutils::signal<void(Class*, int)>& Class::idModified() {
  if (!m_idModified) {
    m_idModified.emplace();
  }
  return *m_idModified;
}

rpgmutils::signal<void(Class*, const std::array<int, 4>&)>& Class::expParamsModified() {
  if (!m_expParamsModified) {
    m_expParamsModified.emplace();
  }
  return *m_expParamsModified;
}

rpgmutils::signal<void(Class*, int, int)>& Class::expParamModified() {
  if (!m_expParamModified) {
    m_expParamModified.emplace();
  }
  return *m_expParamModified;
}

rpgmutils::signal<void(Class*, const std::vector<Trait>&)>& Class::traitsModified() {
  if (!m_traitsModified) {
    m_traitsModified.emplace();
  }
  return *m_traitsModified;
}

rpgmutils::signal<void(Class*, const std::vector<Class::Learning>&)>& Class::learningsModified() {
  if (!m_learningsModified) {
    m_learningsModified.emplace();
  }
  return *m_learningsModified;
}

rpgmutils::signal<void(Class*, const std::string&)>& Class::nameModified() {
  if (!m_nameModified) {
    m_nameModified.emplace();
  }
  return *m_nameModified;
}

rpgmutils::signal<void(Class*, const std::string&)>& Class::noteModified() {
  if (!m_noteModified) {
    m_noteModified.emplace();
  }
  return *m_noteModified;
}

rpgmutils::signal<void(Class*, const std::array<std::array<int, 99>, 8>&)>& Class::paramsModified() {
  if (!m_paramsModified) {
    m_paramsModified.emplace();
  }
  return *m_paramsModified;
}

void to_json(nlohmann::ordered_json& to, const Class::Learning& learning) {
  to = {
      {"level", learning.m_level},
      {"note", learning.m_note},
      {"skillId", learning.m_skillId},
  };
}
void from_json(const nlohmann::ordered_json& from, Class::Learning& learning) {
  learning.m_level = from.value("level", learning.m_level);
  learning.m_note = from.value("note", learning.m_note);
  learning.m_skillId = from.value("skillId", learning.m_skillId);
}

void to_json(nlohmann::ordered_json& to, const Class& cls) {
  to = {
      {"id", cls.m_id}, {"expParams", cls.m_expParams}, {"traits", cls.m_traits}, {"learnings", cls.m_learnings}, {"name", cls.m_name}, {"note", cls.m_note}, {"params", cls.m_params},
  };
}

void from_json(const nlohmann::ordered_json& from, Class& cls) {
  cls.m_id = from.value("id", cls.m_id);
  cls.m_expParams = from.value("expParams", cls.m_expParams);
  cls.m_traits = from.value("traits", cls.m_traits);
  cls.m_learnings = from.value("learnings", cls.m_learnings);
  cls.m_name = from.value("name", cls.m_name);
  cls.m_note = from.value("note", cls.m_note);
  cls.m_params = from.value("params", cls.m_params);
}


code:
#pragma once

#include "Database/Trait.hpp"
#include <nlohmann/json.hpp>
#include <string>

class Class final : public IModifiable {
  friend class ClassesSerializer;
  friend void to_json(nlohmann::ordered_json& to, const Class& cls);
  friend void from_json(const nlohmann::ordered_json& from, Class& cls);

public:
  class Learning final : public IModifiable {
    friend void to_json(nlohmann::ordered_json& to, const Learning& learning);
    friend void from_json(const nlohmann::ordered_json& from, Learning& learning);

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

    int level() const;
    void setLevel(int level);

    const std::string& note() const;
    void setNote(const std::string& note);

    int skillId() const;
    void setSkillId(int skillId);

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

    rpgmutils::signal<void(Learning*, int)>& levelModified();
    rpgmutils::signal<void(Learning*, const std::string&)>& noteModified();
    rpgmutils::signal<void(Learning*, int)>& skillIdModified();

  private:
    int m_level{};
    std::string m_note;
    int m_skillId{};

    std::optional<int> m_oldlevel;
    std::optional<std::string> m_oldnote;
    std::optional<int> m_oldskillId;
    std::optional<rpgmutils::signal<void(Learning*, int)>> m_levelModified;
    std::optional<rpgmutils::signal<void(Learning*, const std::string&)>> m_noteModified;
    std::optional<rpgmutils::signal<void(Learning*, int)>> m_skillIdModified;
  };

  Class() = default;
  Class(const Class& other);
  Class& operator=(const Class& other);
  Class(Class&& other) noexcept;
  Class& operator=(Class&& other) noexcept;

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

  [[nodiscard]] const std::array<int, 4>& expParams() const;
  void setExpParams(const std::array<int, 4>& expParams);
  void setExpParam(const int index, const int value);

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

  [[nodiscard]] const std::vector<Learning>& learnings() const;
  void setLearnings(const std::vector<Learning>& learnings);

  [[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]] const std::array<std::array<int, 99>, 8>& params() const;
  void setParams(const std::array<std::array<int, 99>, 8>& params);

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

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

  rpgmutils::signal<void(Class*, int)>& idModified();
  rpgmutils::signal<void(Class*, const std::array<int, 4>&)>& expParamsModified();
  rpgmutils::signal<void(Class*, int, int)>& expParamModified();
  rpgmutils::signal<void(Class*, const std::vector<Trait>&)>& traitsModified();
  rpgmutils::signal<void(Class*, const std::vector<Learning>&)>& learningsModified();
  rpgmutils::signal<void(Class*, const std::string&)>& nameModified();
  rpgmutils::signal<void(Class*, const std::string&)>& noteModified();
  rpgmutils::signal<void(Class*, const std::array<std::array<int, 99>, 8>&)>& paramsModified();

private:
  int m_id{};
  std::array<int, 4> m_expParams{}; // Base, Extra, Acceleration A, Acceleration B
  std::vector<Trait> m_traits;
  std::vector<Learning> m_learnings;
  std::string m_name;
  std::string m_note;
  std::array<std::array<int, 99>, 8> m_params;

  std::optional<int> m_oldid;
  std::optional<std::array<int, 4>> m_oldexpParams; // Base, Extra, Acceleration A, Acceleration B
  std::optional<std::vector<Trait>> m_oldtraits;
  std::optional<std::vector<Learning>> m_oldlearnings;
  std::optional<std::string> m_oldname;
  std::optional<std::string> m_oldnote;
  std::optional<std::array<std::array<int, 99>, 8>> m_oldparams;

  std::optional<rpgmutils::signal<void(Class*, int)>> m_idModified;
  std::optional<rpgmutils::signal<void(Class*, const std::array<int, 4>&)>> m_expParamsModified;
  std::optional<rpgmutils::signal<void(Class*, int, int)>> m_expParamModified;
  std::optional<rpgmutils::signal<void(Class*, const std::vector<Trait>&)>> m_traitsModified;
  std::optional<rpgmutils::signal<void(Class*, const std::vector<Learning>&)>> m_learningsModified;
  std::optional<rpgmutils::signal<void(Class*, const std::string&)>> m_nameModified;
  std::optional<rpgmutils::signal<void(Class*, const std::string&)>> m_noteModified;
  std::optional<rpgmutils::signal<void(Class*, const std::array<std::array<int, 99>, 8>&)>> m_paramsModified;

  /*!
   * @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& to, const Class::Learning& learning);
void from_json(const nlohmann::ordered_json& from, Class::Learning& learning);
void to_json(nlohmann::ordered_json& to, const Class& cls);
void from_json(const nlohmann::ordered_json& from, Class& cls);


code: #include "Database/Classes.hpp"


code:
#pragma once

#include "Database/Class.hpp"

#include <vector>
class Classes final : public IModifiable {
public:
  [[nodiscard]] Class* classType(const int id) {
    for (auto& cls : m_classes) {
      if (cls.id() == id && cls.isValid()) {
        return &cls;
      }
    }
    return nullptr;
  }

  [[nodiscard]] const Class* classType(const int id) const {
    for (const auto& cls : m_classes) {
      if (cls.id() == id && cls.isValid()) {
        return &cls;
      }
    }

    return nullptr;
  }

  const std::vector<Class>& classes() const { return m_classes; }
  std::vector<Class>& classes() { return m_classes; }

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

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

  bool isModified() const override {
    return IModifiable::isModified() | std::ranges::any_of(m_classes, [](const auto& cls) { return cls.isModified(); });
  }

private:
  std::vector<Class> m_classes;
};


code:
#include "Database/ColorFormatter.hpp"

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

#include <string_view>

using namespace std::string_view_literals;
// clang-format off
namespace solarized {
constexpr auto indigo  = "&push-color=#8d66c6;"sv;
constexpr auto blue    = "&push-color=#268BD2;"sv;
constexpr auto red     = "&push-color=#cb6a6a;"sv;
constexpr auto yellow  = "&push-color=#B58900;"sv;
constexpr auto orange  = "&push-color=#CB4B16;"sv;
constexpr auto magenta = "&push-color=#D33682;"sv;
constexpr auto cyan    = "&push-color=#2AA198;"sv;
constexpr auto violet  = "&push-color=#6C71C4;"sv;
constexpr auto base00  = "&push-color=#657B83;"sv;
constexpr auto base01  = "&push-color=#586E75;"sv;
constexpr auto base02  = "&push-color=#073642;"sv;
constexpr auto base03  = "&push-color=#000236;"sv;
constexpr auto base0   = "&push-color=#839496;"sv;
constexpr auto base1   = "&push-color=#96A1A1;"sv;
constexpr auto base2   = "&push-color=#EEE8D5;"sv;
constexpr auto base3   = "&push-color=#FDF6E3;"sv;
constexpr auto purple  = "&push-color=#817DFF;"sv;
constexpr auto maroon  = "&push-color=#c87a7a;"sv;
}
namespace raw {
constexpr auto indigo      = "&push-color=#4b0082;"sv;
constexpr auto blue        = "&push-color=#0000ff;"sv;
constexpr auto red         = "&push-color=#ff0000;"sv;
constexpr auto darkorange  = "&push-color=#ff8c00;"sv;
constexpr auto magenta     = "&push-color=#ff00ff;"sv;
constexpr auto maroon      = "&push-color=#800000;"sv;
constexpr auto olive       = "&push-color=#808000;"sv;
constexpr auto crimson     = "&push-color=#dc143c;"sv;
constexpr auto purple      = "&push-color=#800080;"sv;
constexpr auto teal        = "&push-color=#008080;"sv;
constexpr auto deeppink    = "&push-color=#ff1493;"sv;
constexpr auto dodgerblue  = "&push-color=#1e90ff;"sv;
constexpr auto darkviolet  = "&push-color=#9400d3;"sv;
constexpr auto gray        = "&push-color=#808080;"sv;
constexpr auto green       = "&push-color=#008000;"sv;
constexpr auto white       = "&push-color=#FFFFFF;"sv;
}
// clang-format on

static std::map<EventCode, std::string_view> CommandColors{
    // clang-format off
  {EventCode::Event_Dummy,              raw::white},
  {EventCode::Show_Text,                solarized::indigo},
  {EventCode::Next_Text,                solarized::indigo},
  {EventCode::Show_Choices,             solarized::indigo},
  {EventCode::Input_Number,             solarized::indigo},
  {EventCode::When_Selected,            solarized::indigo},
  {EventCode::When_Cancel,              solarized::indigo},
  {EventCode::End_del_ShowChoices,      solarized::indigo},
  {EventCode::Select_Item,              solarized::indigo},
  {EventCode::Show_Scrolling_Text,      solarized::indigo},
  {EventCode::Next_Scrolling_Text,      solarized::indigo},
  {EventCode::Comment,                  raw::green},
  {EventCode::Next_Comment,             raw::green},
  {EventCode::Conditional_Branch,       solarized::blue},
  {EventCode::Else,                     solarized::blue},
  {EventCode::End,                      solarized::blue},
  {EventCode::Loop,                     solarized::blue},
  {EventCode::Repeat_Above,             solarized::blue},
  {EventCode::Break_Loop,               solarized::blue},
  /*{EventCode::Continue_Loop,            solarized::blue}, Unused */
  {EventCode::Exit_Event_Processing,    solarized::blue},
  {EventCode::Common_Event,             solarized::blue},
  {EventCode::Label,                    solarized::blue},
  {EventCode::Jump_To_Label,            solarized::blue},
  {EventCode::Control_Switches,         solarized::red},
  {EventCode::Control_Variables,        solarized::red},
  {EventCode::Control_Self_Switch,      solarized::red},
  {EventCode::Control_Timer,            solarized::red},
  {EventCode::Change_Gold,              raw::darkorange},
  {EventCode::Change_Items,             raw::darkorange},
  {EventCode::Change_Weapons,           raw::darkorange},
  {EventCode::Change_Armors,            raw::darkorange},
  {EventCode::Change_Party_Member,      raw::darkorange},
  {EventCode::Change_Battle_BGM,        solarized::magenta},
  {EventCode::Change_Victory_ME,        solarized::magenta},
  {EventCode::Change_Save_Access,       solarized::magenta},
  {EventCode::Change_Menu_Access,       solarized::magenta},
  {EventCode::Change_Encounter, solarized::magenta},
  {EventCode::Change_Formation_Access,  solarized::magenta},
  {EventCode::Change_Window_Color,      solarized::magenta},
  {EventCode::Change_Defeat_ME,         solarized::magenta},
  {EventCode::Change_Vehicle_BGM,      solarized::magenta},
  {EventCode::Transfer_Player,          solarized::maroon},
  {EventCode::Set_Vehicle_Location,     solarized::maroon},
  {EventCode::Set_Event_Location,       solarized::maroon},
  {EventCode::Scroll_Map,               solarized::maroon},
  {EventCode::Set_Movement_Route,       solarized::maroon},
  {EventCode::Movement_Route_Step,      solarized::maroon},
  {EventCode::Get_on_fwsl_off_Vehicle,  solarized::maroon},
  {EventCode::Change_Transparency,      solarized::maroon},
  {EventCode::Change_Player_Followers,  solarized::maroon},
  {EventCode::Gather_Followers,         solarized::maroon},
  {EventCode::Show_Animation,           solarized::maroon},
  {EventCode::Show_Balloon_Icon,        solarized::maroon},
  {EventCode::Erase_Event,              solarized::maroon},
  {EventCode::Fadeout_Screen,           raw::olive},
  {EventCode::Fadein_Screen,            raw::olive},
  {EventCode::Tint_Screen,              raw::olive},
  {EventCode::Flash_Screen,             raw::olive},
  {EventCode::Shake_Screen,             raw::olive},
  {EventCode::Wait,                     raw::crimson},
  {EventCode::Show_Picture,             solarized::purple},
  {EventCode::Move_Picture,             solarized::purple},
  {EventCode::Rotate_Picture,           solarized::purple},
  {EventCode::Tint_Picture,             solarized::purple},
  {EventCode::Erase_Picture,            solarized::purple},
  {EventCode::Set_Weather_Effect,       raw::olive},
  {EventCode::Play_BGM,                 raw::teal},
  {EventCode::Fadeout_BGM,              raw::teal},
  {EventCode::Save_BGM,                 raw::teal},
  {EventCode::Resume_BGM,               raw::teal},
  {EventCode::Play_BGS,                 raw::teal},
  {EventCode::Fadeout_BGS,              raw::teal},
  {EventCode::Play_ME,                  raw::teal},
  {EventCode::Play_SE,                  raw::teal},
  {EventCode::Stop_SE,                  raw::teal},
  {EventCode::Play_Movie,               raw::teal},
  {EventCode::Change_Map_Display_Name,  raw::deeppink},
  {EventCode::Change_Tileset,           raw::deeppink},
  {EventCode::Change_Battle_Back,       raw::deeppink},
  {EventCode::Change_Parallax,          raw::deeppink},
  {EventCode::Get_Location_Info,        raw::deeppink},
  {EventCode::Battle_Processing,        raw::dodgerblue},
  {EventCode::If_Win,                   raw::dodgerblue},
  {EventCode::If_Escape,                raw::dodgerblue},
  {EventCode::If_Lose,                  raw::dodgerblue},
  {EventCode::End_del_Battle_Processing,raw::dodgerblue},
  {EventCode::Shop_Processing,          raw::dodgerblue},
  {EventCode::Shop_Processing_Good,     raw::dodgerblue},
  {EventCode::Name_Input_Processing,    raw::dodgerblue},
  {EventCode::Change_HP,                raw::darkorange},
  {EventCode::Change_MP,                raw::darkorange},
  {EventCode::Change_TP,                raw::darkorange},
  {EventCode::Change_State,             raw::darkorange},
  {EventCode::Recover_All,              raw::darkorange},
  {EventCode::Change_EXP,               raw::darkorange},
  {EventCode::Change_Level,             raw::darkorange},
  {EventCode::Change_Parameter,         raw::darkorange},
  {EventCode::Change_Skill,             raw::darkorange},
  {EventCode::Change_Equipment,         raw::darkorange},
  {EventCode::Change_Name,              raw::darkorange},
  {EventCode::Change_Class,             raw::darkorange},
  {EventCode::Change_Actor_Images,      solarized::magenta},
  {EventCode::Change_Vehicle_Image,     solarized::magenta},
  {EventCode::Change_Nickname,          raw::darkorange},
  {EventCode::Change_Profile,           raw::darkorange},
  {EventCode::Change_Enemy_HP,          solarized::violet},
  {EventCode::Change_Enemy_MP,          solarized::violet},
  {EventCode::Change_Enemy_TP,          solarized::violet},
  {EventCode::Change_Enemy_State,       solarized::violet},
  {EventCode::Enemy_Recover_All,        solarized::violet},
  {EventCode::Enemy_Appear,             solarized::violet},
  {EventCode::Enemy_Transform,          solarized::violet},
  {EventCode::Show_Battle_Animation,    solarized::violet},
  {EventCode::Force_Action,             solarized::violet},
  {EventCode::Abort_Battle,             solarized::violet},
  {EventCode::Open_Menu_Screen,         raw::dodgerblue},
  {EventCode::Open_Save_Screen,         raw::dodgerblue},
  {EventCode::Return_To_Title_Screen,   raw::dodgerblue},
  {EventCode::Game_Over,                raw::dodgerblue},
  {EventCode::Script,                   solarized::indigo},
  {EventCode::Next_Script,              solarized::indigo},
  {EventCode::Plugin_Command_del_MV,         solarized::indigo},
  {EventCode::Plugin_Command_del_MZ,         solarized::indigo},
  {EventCode::Next_PluginMZ_Command,    solarized::indigo},
    // clang-format on
};
/**
 *
 * @param command
 * @param color
 * @return Returns an ImGui formatted string for the specific command name.
 */
std::string ColorFormatter::getColorCode(const EventCode command, const bool color) {
  if (!color) {
    return "";
  }

  if (CommandColors.contains(command)) {
    return CommandColors[command].data();
  }
  return raw::gray.data(); // Default to no color
}
/**
 *
 * @return Returns Pop() for ImGui color encoding. Used at the end of color encoding to indicate the end of it.
 */
std::string ColorFormatter::popColor(const bool color) {
  return color ? "&pop-color;" : ""; // Default to no color
}
/**
 *
 * @param preset
 * @param color
 * @return Input: Color enum, returns an ImGui formatted string based off the enum.
 */
std::string ColorFormatter::getColor(const FormatColor preset, const bool color) {
  if (!color) {
    return {};
  }
  switch (preset) {
  case FormatColor::Gray:
    return raw::gray.data();
  case FormatColor::Red:
    return solarized::red.data();
  case FormatColor::Default:
  default:
    return "&push-color=#FFFF00;";
  }
}


code:
#ifndef COLOR_FORMATTER_H
#define COLOR_FORMATTER_H

#include "Globals.hpp"

#include <string>

struct ColorFormatter {
  static std::string getColorCode(EventCode command, bool color = true);
  static std::string popColor(bool color = true);
  static std::string getColor(FormatColor preset, bool color = true);
};

#endif // COLOR_FORMATTER_H


code:
#include "Database/CommandParser.hpp"

#include "Database/EventCommands.hpp"

#include <iostream>

using json = nlohmann::ordered_json;

std::vector<std::shared_ptr<IEventCommand>> CommandParser::parse(const json& _json) {
  std::vector<std::shared_ptr<IEventCommand>> ret;
  parser = _json;

  while (index < parser.size()) {
    EventCode code = EventCode::Event_Dummy;
    currentCommand().at("code").get_to(code);
    const auto parameters = currentCommand().value("parameters", nlohmann::json());
    const auto indent = currentCommand().value("indent", std::optional<int>{});

    // std::cout << "Processing: " << magic_enum::enum_name(code) << " (" << static_cast<int>(code) << ")" << std::endl;
    switch (code) {
    case EventCode::Event_Dummy:
      ret.emplace_back(new EventDummy(indent));
      break;
    case EventCode::Show_Text: {
      auto text = dynamic_cast<ShowTextCommand*>(ret.emplace_back(new ShowTextCommand(indent, parameters)).get());
      while (nextEventCommand() == EventCode::Next_Text) {
        ++index;
        auto nextIndent = currentCommand().value("indent", std::optional<int>{});
        auto nextParameters = currentCommand().value("parameters", nlohmann::json{});
        text->addText(new NextTextCommand(nextIndent, nextParameters));
      }
      break;
    }
    case EventCode::Show_Choices:
      ret.emplace_back(new ShowChoiceCommand(indent, parameters));
      break;

    case EventCode::When_Selected:
      ret.emplace_back(new WhenSelectedCommand(indent, parameters));
      break;
    case EventCode::When_Cancel:
      ret.emplace_back(new WhenCancelCommand(indent));
      break;
    case EventCode::End_del_ShowChoices:
      ret.emplace_back(new ShowChoicesEndCommand(indent));
      break;
    case EventCode::Input_Number:
      ret.emplace_back(new InputNumberCommand(indent, parameters));
      break;
    case EventCode::Select_Item:
      ret.emplace_back(new SelectItemCommand(indent, parameters));
      break;
    case EventCode::Show_Scrolling_Text: {
      auto text = dynamic_cast<ShowScrollTextCommand*>(ret.emplace_back(new ShowScrollTextCommand(indent, parameters)).get());
      while (nextEventCommand() == EventCode::Next_Scrolling_Text) {
        ++index;
        auto nextIndent = currentCommand().value("indent", std::optional<int>{});
        auto nextParameters = currentCommand().value("parameters", nlohmann::json{});
        text->addText(new NextScrollingTextCommand(nextIndent, nextParameters));
      }
      break;
    }
    case EventCode::Comment: {
      auto text = dynamic_cast<CommentCommand*>(ret.emplace_back(new CommentCommand(indent, parameters)).get());
      while (nextEventCommand() == EventCode::Next_Comment) {
        ++index;
        auto nextIndent = currentCommand().value("indent", std::optional<int>{});
        auto nextParameters = currentCommand().value("parameters", nlohmann::json{});
        text->addText(new NextCommentCommand(nextIndent, nextParameters));
      }
      break;
    }
    case EventCode::Conditional_Branch:
      ret.emplace_back(new ConditionalBranchCommand(indent, parameters));
      break;
    case EventCode::Else:
      ret.emplace_back(new ElseCommand(indent));
      break;
    case EventCode::Loop:
      ret.emplace_back(new LoopCommand(indent, parameters));
      break;
    case EventCode::Repeat_Above:
      ret.emplace_back(new RepeatAboveCommand(indent));
      break;
    case EventCode::Break_Loop:
      ret.emplace_back(new BreakLoopCommand(indent));
      break;
    case EventCode::Exit_Event_Processing:
      ret.emplace_back(new ExitEventProcessingCommand(indent));
      break;
    case EventCode::Common_Event:
      ret.emplace_back(new CommonEventCommand(indent, parameters));
      break;
    case EventCode::Label:
      ret.emplace_back(new LabelCommand(indent, parameters));
      break;
    case EventCode::Jump_To_Label:
      ret.emplace_back(new JumpToLabelCommand(indent, parameters));
      break;
    case EventCode::Control_Switches:
      ret.emplace_back(new ControlSwitches(indent, parameters));
      break;
    case EventCode::Control_Variables:
      ret.emplace_back(new ControlVariables(indent, parameters));
      break;
    case EventCode::Control_Self_Switch:
      ret.emplace_back(new ControlSelfSwitchCommand(indent, parameters));
      break;
    case EventCode::Control_Timer:
      ret.emplace_back(new ControlTimerCommand(indent, parameters));
      break;
    case EventCode::Change_Gold:
      ret.emplace_back(new ChangeGoldCommand(indent, parameters));
      break;
    case EventCode::Change_Items:
      ret.emplace_back(new ChangeItemsCommand(indent, parameters));
      break;
    case EventCode::Change_Weapons:
      ret.emplace_back(new ChangeWeaponsCommand(indent, parameters));
      break;
    case EventCode::Change_Armors:
      ret.emplace_back(new ChangeArmorsCommand(indent, parameters));
      break;
    case EventCode::Change_Party_Member:
      ret.emplace_back(new ChangePartyMemberCommand(indent, parameters));
      break;
    case EventCode::Change_Battle_BGM:
      ret.emplace_back(new ChangeBattleBGMCommand(indent, parameters));
      break;
    case EventCode::Change_Victory_ME:
      ret.emplace_back(new ChangeVictoryMECommand(indent, parameters));
      break;
    case EventCode::Change_Save_Access:
      ret.emplace_back(new ChangeSaveAccessCommand(indent, parameters));
      break;
    case EventCode::Change_Menu_Access:
      ret.emplace_back(new ChangeMenuAccessCommand(indent, parameters));
      break;
    case EventCode::Change_Encounter:
      ret.emplace_back(new ChangeEncounterDisableCommand(indent, parameters));
      break;
    case EventCode::Change_Formation_Access:
      ret.emplace_back(new ChangeFormationAccessCommand(indent, parameters));
      break;
    case EventCode::Change_Window_Color:
      ret.emplace_back(new ChangeWindowColorCommand(indent, parameters));
      break;
    case EventCode::Change_Defeat_ME:
      ret.emplace_back(new ChangeDefeatMECommand(indent, parameters));
      break;
    case EventCode::Change_Vehicle_BGM:
      ret.emplace_back(new ChangeVehicleBGMCommand(indent, parameters));
      break;
    case EventCode::Transfer_Player:
      ret.emplace_back(new TransferPlayerCommand(indent, parameters));
      break;
    case EventCode::Set_Vehicle_Location:
      ret.emplace_back(new SetVehicleLocationCommand(indent, parameters));
      break;
    case EventCode::Set_Event_Location:
      ret.emplace_back(new SetEventLocationCommand(indent, parameters));
      break;
    case EventCode::Scroll_Map:
      ret.emplace_back(new ScrollMapCommand(indent, parameters));
      break;
    case EventCode::Set_Movement_Route: {
      auto route = dynamic_cast<SetMovementRouteCommand*>(ret.emplace_back(new SetMovementRouteCommand(indent, parameters)).get());
      while (nextEventCommand() == EventCode::Movement_Route_Step) {
        ++index;
        auto nextIndent = currentCommand().value("indent", std::optional<int>{});
        auto nextParameters = currentCommand().value("parameters", nlohmann::json{});
        route->addStep(new MovementRouteStepCommand(nextIndent, nextParameters));
      }
      break;
    }
    case EventCode::Get_on_fwsl_off_Vehicle:
      ret.emplace_back(new GetOnOffVehicleCommand(indent));
      break;
    case EventCode::Change_Transparency:
      ret.emplace_back(new ChangeTransparencyCommand(indent, parameters));
      break;
    case EventCode::Show_Animation:
      ret.emplace_back(new ShowAnimationCommand(indent, parameters));
      break;
    case EventCode::Show_Balloon_Icon:
      ret.emplace_back(new ShowBalloonIconCommand(indent, parameters));
      break;
    case EventCode::Erase_Event:
      ret.emplace_back(new EraseEventCommand(indent));
      break;
    case EventCode::Change_Player_Followers:
      ret.emplace_back(new ChangePlayerFollowersCommand(indent, parameters));
      break;
    case EventCode::Gather_Followers:
      ret.emplace_back(new GatherFollowersCommand(indent));
      break;
    case EventCode::Fadeout_Screen:
      ret.emplace_back(new FadeoutScreenCommand(indent));
      break;
    case EventCode::Fadein_Screen:
      ret.emplace_back(new FadeinScreenCommand(indent));
      break;
    case EventCode::Tint_Screen:
      ret.emplace_back(new TintScreenCommand(indent, parameters));
      break;
    case EventCode::Flash_Screen:
      ret.emplace_back(new FlashScreenCommand(indent, parameters));
      break;
    case EventCode::Shake_Screen:
      ret.emplace_back(new ShakeScreenCommand(indent, parameters));
      break;
    case EventCode::Wait:
      ret.emplace_back(new WaitCommand(indent, parameters));
      break;
    case EventCode::Show_Picture:
      ret.emplace_back(new ShowPictureCommand(indent, parameters));
      break;
    case EventCode::Move_Picture:
      ret.emplace_back(new MovePictureCommand(indent, parameters));
      break;
    case EventCode::Rotate_Picture:
      ret.emplace_back(new RotatePictureCommand(indent, parameters));
      break;
    case EventCode::Tint_Picture:
      ret.emplace_back(new TintPictureCommand(indent, parameters));
      break;
    case EventCode::Set_Weather_Effect:
      ret.emplace_back(new SetWeatherEffectCommand(indent, parameters));
      break;
    case EventCode::Erase_Picture:
      ret.emplace_back(new ErasePictureCommand(indent, parameters));
      break;
    case EventCode::Play_BGM:
      ret.emplace_back(new PlayBGMCommand(indent, parameters));
      break;
    case EventCode::Fadeout_BGM:
      ret.emplace_back(new FadeoutBGM(indent, parameters));
      break;
    case EventCode::Save_BGM:
      ret.emplace_back(new SaveBGMCommand(indent));
      break;
    case EventCode::Resume_BGM:
      ret.emplace_back(new ResumeBGMCommand(indent));
      break;
    case EventCode::Play_BGS:
      ret.emplace_back(new PlayBGSCommand(indent, parameters));
      break;
    case EventCode::Fadeout_BGS:
      ret.emplace_back(new FadeoutBGS(indent, parameters));
      break;
    case EventCode::Play_ME:
      ret.emplace_back(new PlayMECommand(indent, parameters));
      break;
    case EventCode::Play_SE:
      ret.emplace_back(new PlaySECommand(indent, parameters));
      break;
    case EventCode::Stop_SE:
      ret.emplace_back(new StopSECommand(indent));
      break;
    case EventCode::Play_Movie:
      ret.emplace_back(new PlayMovieCommand(indent, parameters));
      break;
    case EventCode::Change_Map_Display_Name:
      ret.emplace_back(new ChangeMapNameDisplayCommand(indent, parameters));
      break;
    case EventCode::Change_Tileset:
      ret.emplace_back(new ChangeTilesetCommand(indent, parameters));
      break;
    case EventCode::Change_Battle_Back:
      ret.emplace_back(new ChangeBattlebackCommand(indent, parameters));
      break;
    case EventCode::Change_Parallax:
      ret.emplace_back(new ChangeParallaxCommand(indent, parameters));
      break;
    case EventCode::Get_Location_Info:
      ret.emplace_back(new GetLocationInfoCommand(indent, parameters));
      break;
    case EventCode::Battle_Processing:
      ret.emplace_back(new BattleProcessingCommand(indent, parameters));
      break;
    case EventCode::If_Win:
      ret.emplace_back(new IfWinCommand(indent));
      break;
    case EventCode::If_Escape:
      ret.emplace_back(new IfEscapeCommand(indent));
      break;
    case EventCode::If_Lose:
      ret.emplace_back(new IfLoseCommand(indent));
      break;
    case EventCode::End_del_Battle_Processing:
      ret.emplace_back(new EndBattleProcessingCommand(indent));
      break;
    case EventCode::Shop_Processing: {
      auto* shop = dynamic_cast<ShopProcessingCommand*>(ret.emplace_back(new ShopProcessingCommand(indent, parameters)).get());
      while (nextEventCommand() == EventCode::Shop_Processing_Good) {
        ++index;
        auto nextIndent = currentCommand().value("indent", std::optional<int>{});
        auto nextParameters = currentCommand().value("parameters", nlohmann::json{});
        shop->addGood(std::make_shared<ShopProcessingGoodCommand>(nextIndent, nextParameters));
      }
      break;
    }
    case EventCode::Name_Input_Processing:
      ret.emplace_back(new NameInputCommand(indent, parameters));
      break;
    case EventCode::Change_HP:
      ret.emplace_back(new ChangeHPCommand(indent, parameters));
      break;
    case EventCode::Change_MP:
      ret.emplace_back(new ChangeMPCommand(indent, parameters));
      break;
    case EventCode::Change_TP:
      ret.emplace_back(new ChangeTPCommand(indent, parameters));
      break;
    case EventCode::Change_EXP:
      ret.emplace_back(new ChangeEXPCommand(indent, parameters));
      break;
    case EventCode::Change_Level:
      ret.emplace_back(new ChangeLevelCommand(indent, parameters));
      break;
    case EventCode::Change_Parameter:
      ret.emplace_back(new ChangeParameterCommand(indent, parameters));
      break;
    case EventCode::Recover_All:
      ret.emplace_back(new RecoverAllCommand(indent, parameters));
      break;
    case EventCode::Change_Name:
      ret.emplace_back(new ChangeNameCommand(indent, parameters));
      break;
    case EventCode::Change_Class:
      ret.emplace_back(new ChangeClassCommand(indent, parameters));
      break;
    case EventCode::Change_State:
      ret.emplace_back(new ChangeStateCommand(indent, parameters));
      break;
    case EventCode::Change_Skill:
      ret.emplace_back(new ChangeSkillCommand(indent, parameters));
      break;
    case EventCode::Change_Equipment:
      ret.emplace_back(new ChangeEquipmentCommand(indent, parameters));
      break;
    case EventCode::Change_Profile:
      ret.emplace_back(new ChangeProfileCommand(indent, parameters));
      break;
    case EventCode::Change_Nickname:
      ret.emplace_back(new ChangeNicknameCommand(indent, parameters));
      break;
    case EventCode::Game_Over:
      ret.emplace_back(new GameOverCommand(indent));
      break;
    case EventCode::Return_To_Title_Screen:
      ret.emplace_back(new ReturnToTitleCommand(indent));
      break;
    case EventCode::Open_Menu_Screen:
      ret.emplace_back(new OpenMenuCommand(indent));
      break;
    case EventCode::Open_Save_Screen:
      ret.emplace_back(new OpenSaveCommand(indent));
      break;
    case EventCode::Abort_Battle:
      ret.emplace_back(new AbortBattleCommand(indent));
      break;
    case EventCode::Enemy_Recover_All:
      ret.emplace_back(new EnemyRecoverAllCommand(indent, parameters));
      break;
    case EventCode::Enemy_Appear:
      ret.emplace_back(new EnemyAppearCommand(indent, parameters));
      break;
    case EventCode::Enemy_Transform:
      ret.emplace_back(new EnemyTransformCommand(indent, parameters));
      break;
    case EventCode::Change_Enemy_HP:
      ret.emplace_back(new ChangeEnemyHPCommand(indent, parameters));
      break;
    case EventCode::Change_Enemy_MP:
      ret.emplace_back(new ChangeEnemyMPCommand(indent, parameters));
      break;
    case EventCode::Change_Enemy_TP:
      ret.emplace_back(new ChangeEnemyTPCommand(indent, parameters));
      break;
    case EventCode::Change_Enemy_State:
      ret.emplace_back(new ChangeEnemyStateCommand(indent, parameters));
      break;
    case EventCode::Force_Action:
      ret.emplace_back(new ForceActionCommand(indent, parameters));
      break;
    case EventCode::Show_Battle_Animation:
      ret.emplace_back(new ShowBattleAnimationCommand(indent, parameters));
      break;
    case EventCode::Change_Vehicle_Image:
      ret.emplace_back(new ChangeVehicleImageCommand(indent, parameters));
      break;
    case EventCode::Change_Actor_Images:
      ret.emplace_back(new ChangeActorImageCommand(indent, parameters));
      break;
    case EventCode::Script: {
      auto end = dynamic_cast<ScriptCommand*>(ret.emplace_back(new ScriptCommand(indent, parameters)).get());
      while (nextEventCommand() == EventCode::Next_Script) {
        ++index;
        auto nextIndent = currentCommand().value("indent", std::optional<int>{});
        auto nextParameters = currentCommand().value("parameters", nlohmann::json{});
        end->addText(new NextScriptCommand(nextIndent, nextParameters));
      }
      break;
    }
    case EventCode::Plugin_Command_del_MV:
      ret.emplace_back(new PluginCommandMV(indent, parameters));
      break;
    case EventCode::End:
      ret.emplace_back(new EndCommand(indent));
      break;
      // MovementRoute commands
    case EventCode::Move_Down:
      ret.emplace_back(new MovementMoveDownCommand(indent));
      break;
    case EventCode::Move_Left:
      ret.emplace_back(new MovementMoveLeftCommand(indent));
      break;
    case EventCode::Move_Right:
      ret.emplace_back(new MovementMoveRightCommand(indent));
      break;
    case EventCode::Move_Up:
      ret.emplace_back(new MovementMoveUpCommand(indent));
      break;
    case EventCode::Move_Lower_Left:
      ret.emplace_back(new MovementMoveLowerLeftCommand(indent));
      break;
    case EventCode::Move_Lower_Right:
      ret.emplace_back(new MovementMoveLowerRightCommand(indent));
      break;
    case EventCode::Move_Upper_Left:
      ret.emplace_back(new MovementMoveUpperLeftCommand(indent));
      break;
    case EventCode::Move_Upper_Right:
      ret.emplace_back(new MovementMoveUpperRightCommand(indent));
      break;
    case EventCode::Move_at_Random:
      ret.emplace_back(new MovementMoveAtRandomCommand(indent));
      break;
    case EventCode::Move_toward_Player:
      ret.emplace_back(new MovementMoveTowardPlayerCommand(indent));
      break;
    case EventCode::Move_away_from_Player:
      ret.emplace_back(new MovementMoveAwayFromPlayerCommand(indent));
      break;
    case EventCode::_1_Step_Forward:
      ret.emplace_back(new MovementMove1StepForwardCommand(indent));
      break;
    case EventCode::_1_Step_Backward:
      ret.emplace_back(new MovementMove1StepBackwardCommand(indent));
      break;
    case EventCode::Jump:
      ret.emplace_back(new MovementJumpCommand(indent, parameters));
      break;
    case EventCode::Wait_del_Movement:
      ret.emplace_back(new MovementWaitCommand(indent, parameters));
      break;
    case EventCode::Turn_Down:
      ret.emplace_back(new MovementTurnDownCommand(indent));
      break;
    case EventCode::Turn_Left:
      ret.emplace_back(new MovementTurnLeftCommand(indent));
      break;
    case EventCode::Turn_Right:
      ret.emplace_back(new MovementTurnRightCommand(indent));
      break;
    case EventCode::Turn_Up:
      ret.emplace_back(new MovementTurnUpCommand(indent));
      break;
    case EventCode::Turn_90_deg_Right:
      ret.emplace_back(new MovementTurn90DegRightCommand(indent));
      break;
    case EventCode::Turn_90_deg_Left:
      ret.emplace_back(new MovementTurn90DegLeftCommand(indent));
      break;
    case EventCode::Turn_180_deg:
      ret.emplace_back(new MovementTurn180DegCommand(indent));
      break;
    case EventCode::Turn_90_deg_Left_or_Right:
      ret.emplace_back(new MovementTurn90DegLeftOrRightCommand(indent));
      break;
    case EventCode::Turn_at_Random:
      ret.emplace_back(new MovementTurnAtRandomCommand(indent));
      break;
    case EventCode::Turn_toward_Player:
      ret.emplace_back(new MovementTurnTowardPlayerCommand(indent));
      break;
    case EventCode::Turn_away_from_Player:
      ret.emplace_back(new MovementTurnAwayFromPlayerCommand(indent, parameters));
      break;
    case EventCode::Switch_ON:
      ret.emplace_back(new MovementSwitchONCommand(indent, parameters));
      break;
    case EventCode::Switch_OFF:
      ret.emplace_back(new MovementSwitchOFFCommand(indent, parameters));
      break;
    case EventCode::Speed:
      ret.emplace_back(new MovementSpeedCommand(indent, parameters));
      break;
    case EventCode::Frequency:
      ret.emplace_back(new MovementFrequencyCommand(indent, parameters));
      break;
    case EventCode::Walking_Animation_ON:
      ret.emplace_back(new MovementWalkingAnimationONCommand(indent));
      break;
    case EventCode::Walking_Animation_OFF:
      ret.emplace_back(new MovementWalkingAnimationOFFCommand(indent));
      break;
    case EventCode::Stepping_Animation_ON:
      ret.emplace_back(new MovementSteppingAnimationONCommand(indent));
      break;
    case EventCode::Stepping_Animation_OFF:
      ret.emplace_back(new MovementSteppingAnimationOFFCommand(indent, parameters));
      break;
    case EventCode::Direction_Fix_ON:
      ret.emplace_back(new MovementDirectionFixONCommand(indent));
      break;
    case EventCode::Direction_Fix_OFF:
      ret.emplace_back(new MovementDirectionFixOFFCommand(indent));
      break;
    case EventCode::Through_ON:
      ret.emplace_back(new MovementThroughONCommand(indent));
      break;
    case EventCode::Through_OFF:
      ret.emplace_back(new MovementThroughOFFCommand(indent));
      break;
    case EventCode::Transparent_ON:
      ret.emplace_back(new MovementTransparentONCommand(indent));
      break;
    case EventCode::Transparent_OFF:
      ret.emplace_back(new MovementTransparentOFFCommand(indent));
      break;
    case EventCode::Change_Image:
      ret.emplace_back(new MovementChangeImageCommand(indent, parameters));
      break;
    case EventCode::Change_Opacity:
      ret.emplace_back(new MovementChangeOpacityCommand(indent, parameters));
      break;
    case EventCode::Change_Blend_Mode:
      ret.emplace_back(new MovementChangeBlendModeCommand(indent, parameters));
      break;
    case EventCode::Play_SE_del_Movement:
      ret.emplace_back(new MovementPlaySECommand(indent, parameters));
      break;
    case EventCode::Script_del_Movement: {
      ret.emplace_back(new MovementScriptCommand(indent, parameters));
      break;
    }
    default:
      UnhandledEventCommand* end = dynamic_cast<UnhandledEventCommand*>(ret.emplace_back(new UnhandledEventCommand()).get());
      end->m_indent = parser[index].value("indent", std::optional<int>{});
      end->m_code = code;
      end->data = parser[index];
      std::cout << "Unhandled command: " << magic_enum::enum_name(code) << " (" << static_cast<int>(code) << ")" << std::endl;
      break;
    }
    ++index;
  }
  return ret;
}

void CommandParser::serialize(nlohmann::ordered_json& data, const std::vector<std::shared_ptr<IEventCommand>>& list, const bool movementRoute, const bool oldValues) {
  for (const auto& cmd : list) {
    if (!cmd) {
      continue;
    }
    const auto code = cmd->code();
    bool doParameters = true;
    if ((code >= EventCode::Move_Down && code <= EventCode::Script_del_Movement) || movementRoute) {
      doParameters = code == EventCode::Jump || code == EventCode::Change_Blend_Mode || code == EventCode::Change_Image || code == EventCode::Change_Opacity || code == EventCode::Frequency ||
                     code == EventCode::Speed || code == EventCode::Play_SE_del_Movement || code == EventCode::Wait_del_Movement || code == EventCode::Script_del_Movement ||
                     code == EventCode::Switch_ON || code == EventCode::Switch_OFF || code == EventCode::Event_Dummy;
    }
    cmd->serialize(data.emplace_back(), movementRoute ? code != EventCode::Event_Dummy : true, doParameters);

    switch (code) {
    case EventCode::Show_Scrolling_Text: {
      auto text = dynamic_cast<ShowScrollTextCommand*>(cmd.get());
      for (const auto& next : text->text) {
        if (!next) {
          continue;
        }
        next->serialize(data.emplace_back(), true, true, oldValues);
      }
      break;
    }
    case EventCode::Show_Text: {
      auto text = dynamic_cast<ShowTextCommand*>(cmd.get());
      for (const auto& next : text->text) {
        if (!next) {
          continue;
        }
        next->serialize(data.emplace_back(), true, true, oldValues);
      }
      break;
    }
    case EventCode::Comment: {
      auto text = dynamic_cast<CommentCommand*>(cmd.get());
      for (const auto& next : text->nextComments) {
        if (!next) {
          continue;
        }
        next->serialize(data.emplace_back(), true, true, oldValues);
      }
      break;
    }
    case EventCode::Script: {
      auto text = dynamic_cast<ScriptCommand*>(cmd.get());
      for (const auto& next : text->moreScript) {
        if (!next) {
          continue;
        }
        next->serialize(data.emplace_back(), true, true, oldValues);
      }
      break;
    }
    case EventCode::Set_Movement_Route: {
      auto route = dynamic_cast<SetMovementRouteCommand*>(cmd.get());
      for (const auto& next : route->editNodes) {
        if (!next) {
          continue;
        }
        next->serialize(data.emplace_back(), true, true, oldValues);
      }
      break;
    }
    default:
      break;
    }
  }
}


code:
#pragma once
#include "Database/EventCommands/IEventCommand.hpp"

struct CommandParser {
  nlohmann::json parser;
  int index = 0;

  std::vector<std::shared_ptr<IEventCommand>> parse(const nlohmann::ordered_json& data);
  static void serialize(nlohmann::ordered_json& data, const std::vector<std::shared_ptr<IEventCommand>>& list, bool movementRoute = false, bool oldValues = false);

  EventCode nextEventCommand() {
    if (index + 1 >= parser.size()) {
      return EventCode::Event_Dummy;
    }
    EventCode ret;
    parser[index + 1].at("code").get_to(ret);
    return ret;
  }
  nlohmann::json& currentCommand() { return parser[index]; }
};


code:
#include "Database/CommonEvent.hpp"

CommonEvent::CommonEvent(const CommonEvent& other)
: IModifiable(other)
, m_id(other.m_id)
, m_name(other.m_name)
, m_commands(other.m_commands)
, m_switchId(other.m_switchId)
, m_trigger(other.m_trigger)
, m_oldid(other.m_oldid)
, m_oldname(other.m_oldname)
, m_oldcommands(other.m_oldcommands)
, m_oldswitchId(other.m_oldswitchId)
, m_oldtrigger(other.m_oldtrigger) {}
CommonEvent& CommonEvent::operator=(const CommonEvent& other) {
  IModifiable::operator=(other);
  m_id = other.m_id;
  m_name = other.m_name;
  m_commands = other.m_commands;
  m_switchId = other.m_switchId;
  m_trigger = other.m_trigger;
  m_oldid = other.m_oldid;
  m_oldname = other.m_oldname;
  m_oldcommands = other.m_oldcommands;
  m_oldswitchId = other.m_oldswitchId;
  m_oldtrigger = other.m_oldtrigger;
  return *this;
}
CommonEvent::CommonEvent(CommonEvent&& other) noexcept
: IModifiable(std::move(other))
, m_id(other.m_id)
, m_name(std::move(other.m_name))
, m_commands(std::move(other.m_commands))
, m_switchId(other.m_switchId)
, m_trigger(other.m_trigger)
, m_oldid(other.m_oldid)
, m_oldname(std::move(other.m_oldname))
, m_oldcommands(std::move(other.m_oldcommands))
, m_oldswitchId(other.m_oldswitchId)
, m_oldtrigger(other.m_oldtrigger) {}

CommonEvent& CommonEvent::operator=(CommonEvent&& other) noexcept {
  IModifiable::operator=(std::move(other));
  m_id = other.m_id;
  m_name = std::move(other.m_name);
  m_commands = std::move(other.m_commands);
  m_switchId = other.m_switchId;
  m_trigger = other.m_trigger;
  m_oldid = other.m_oldid;
  m_oldname = std::move(other.m_oldname);
  m_oldcommands = std::move(other.m_oldcommands);
  m_oldswitchId = other.m_oldswitchId;
  m_oldtrigger = other.m_oldtrigger;
  return *this;
}

[[nodiscard]] int CommonEvent::id() const { return m_id; }
void CommonEvent::setId(const int id) { m_id = id; }

std::vector<std::shared_ptr<IEventCommand>>& CommonEvent::commands() { return m_commands; }
const std::vector<std::shared_ptr<IEventCommand>>& CommonEvent::commands() const { return m_commands; }
void CommonEvent::setCommands(const std::vector<std::shared_ptr<IEventCommand>>& commands) {
  if (m_commands == commands) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(commands);
  m_commands = commands;
  if (!signalsDisabled()) {
  emit_signal(commandsModified(), this, commands);
  }
  setModified(true);
}

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

int CommonEvent::switchId() const { return m_switchId; }
void CommonEvent::setSwitchId(const int id) {
  if (m_switchId == id) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(id);
  m_switchId = id;
  if (!signalsDisabled()) {
  emit_signal(switchIdModified(), this, id);
  }
  setModified(true);
}

CommonEventTriggerType CommonEvent::trigger() const { return m_trigger; }
void CommonEvent::setTrigger(const CommonEventTriggerType trigger) {
  if (m_trigger == trigger) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(trigger);
  m_trigger = trigger;
  if (!signalsDisabled()) {
  emit_signal(triggerModified(), this, trigger);
  }
  setModified(true);
}

void CommonEvent::restoreOriginal() {
  IModifiable::restoreOriginal();
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(id);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(name);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(commands);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(switchId);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(trigger);

  for (const auto& command : m_commands) {
    command->restoreOriginal();
  }
}
void CommonEvent::acceptChanges() {
  IModifiable::acceptChanges();
  MODIFIABLE_ACCEPT_VALUE(id);
  MODIFIABLE_ACCEPT_VALUE(name);
  MODIFIABLE_ACCEPT_VALUE(commands);
  MODIFIABLE_ACCEPT_VALUE(switchId);
  MODIFIABLE_ACCEPT_VALUE(trigger);
  for (const auto& command : m_commands) {
    command->acceptChanges();
  }
}
nlohmann::ordered_json CommonEvent::serializeOldValues() const {
  nlohmann::ordered_json j;
  j["id"] = m_id;
  j["name"] = m_name;
  CommandParser::serialize(j["list"], m_commands, false, true);
  j["switchId"] = m_switchId;
  j["trigger"] = m_trigger;
  return j;
}

rpgmutils::signal<void(CommonEvent*, int)>& CommonEvent::idModified() {
  if (!m_idModified) {
    m_idModified.emplace();
  }
  return *m_idModified;
}
rpgmutils::signal<void(CommonEvent*, const std::vector<std::shared_ptr<IEventCommand>>&)>& CommonEvent::commandsModified() {
  if (!m_commandsModified) {
    m_commandsModified.emplace();
  }
  return *m_commandsModified;
}
rpgmutils::signal<void(CommonEvent*, const std::string&)>& CommonEvent::nameModified() {
  if (!m_nameModified) {
    m_nameModified.emplace();
  }
  return *m_nameModified;
}
rpgmutils::signal<void(CommonEvent*, int)>& CommonEvent::switchIdModified() {
  if (!m_switchIdModified) {
    m_switchIdModified.emplace();
  }
  return *m_switchIdModified;
}
rpgmutils::signal<void(CommonEvent*, CommonEventTriggerType)>& CommonEvent::triggerModified() {
  if (!m_triggerModified) {
    m_triggerModified.emplace();
  }
  return *m_triggerModified;
}

void to_json(nlohmann::ordered_json& to, const CommonEvent& event) {
  CommandParser parser;
  nlohmann::ordered_json commands;
  parser.serialize(commands, event.commands());

  to = nlohmann::ordered_json{
      {"id", event.m_id}, {"list", commands}, {"name", event.m_name}, {"switchId", event.m_switchId}, {"trigger", event.m_trigger},
  };
}
void from_json(const nlohmann::ordered_json& from, CommonEvent& event) {
  event.m_id = from.value("id", event.m_id);
  event.m_name = from.value("name", event.m_name);
  CommandParser parser;
  event.m_commands = parser.parse(from.at("list"));
  event.m_switchId = from.value("switchId", event.m_switchId);
  event.m_trigger = from.value("trigger", event.m_trigger);
}


code:
#pragma once

#include "Database/CommandParser.hpp"
#include "Database/Globals.hpp"
#include "EventCommands/EventDummy.hpp"

class CommonEvent final : public IModifiable {
  friend class CommonEventsSerializer;
  friend void to_json(nlohmann::ordered_json& to, const CommonEvent& event);
  friend void from_json(const nlohmann::ordered_json& from, CommonEvent& event);

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

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

  std::vector<std::shared_ptr<IEventCommand>>& commands();
  const std::vector<std::shared_ptr<IEventCommand>>& commands() const;
  void setCommands(const std::vector<std::shared_ptr<IEventCommand>>& commands);

  const std::string& name() const;
  void setName(const std::string& name);

  int switchId() const;
  void setSwitchId(int id);

  CommonEventTriggerType trigger() const;
  void setTrigger(CommonEventTriggerType trigger);

  bool setReference(int targetId, int newId, SearchType type) override {
    if (targetId == switchId()) {
      setSwitchId(newId);
      return true;
    }
    return false;
  }

  void restoreOriginal() override;
  void acceptChanges() override;
  nlohmann::ordered_json serializeOldValues() const override;
  bool isModified() const override {
    return IModifiable::isModified() | std::ranges::any_of(m_commands, [](const std::shared_ptr<IEventCommand>& command) { return command->isModified(); });
  }
  rpgmutils::signal<void(CommonEvent*, int)>& idModified();
  rpgmutils::signal<void(CommonEvent*, const std::vector<std::shared_ptr<IEventCommand>>&)>& commandsModified();
  rpgmutils::signal<void(CommonEvent*, const std::string&)>& nameModified();
  rpgmutils::signal<void(CommonEvent*, int)>& switchIdModified();
  rpgmutils::signal<void(CommonEvent*, CommonEventTriggerType)>& triggerModified();

  CommonEvent clone() const { return CommonEvent(*this, 1); }

private:
  CommonEvent(const CommonEvent& other, int)
  : IModifiable(other) {
    m_id = other.m_id;
    m_name = other.m_name;
    m_switchId = other.m_switchId;
    m_trigger = other.m_trigger;
    for (const auto& command : other.m_commands) {
      m_commands.emplace_back(command->clone());
    }
  };
  int m_id{};
  std::vector<std::shared_ptr<IEventCommand>> m_commands;
  std::string m_name{};
  int m_switchId{};
  CommonEventTriggerType m_trigger;

  std::optional<int> m_oldid;
  std::optional<std::vector<std::shared_ptr<IEventCommand>>> m_oldcommands;
  std::optional<std::string> m_oldname;
  std::optional<int> m_oldswitchId;
  std::optional<CommonEventTriggerType> m_oldtrigger;

  std::optional<rpgmutils::signal<void(CommonEvent*, int)>> m_idModified;
  std::optional<rpgmutils::signal<void(CommonEvent*, const std::vector<std::shared_ptr<IEventCommand>>&)>> m_commandsModified;
  std::optional<rpgmutils::signal<void(CommonEvent*, const std::string&)>> m_nameModified;
  std::optional<rpgmutils::signal<void(CommonEvent*, int)>> m_switchIdModified;
  std::optional<rpgmutils::signal<void(CommonEvent*, CommonEventTriggerType)>> m_triggerModified;
};

void to_json(nlohmann::ordered_json& to, const CommonEvent& event);
void from_json(const nlohmann::ordered_json& from, CommonEvent& event);


code: #include "Database/CommonEvents.hpp"


code:
#pragma once

#include "Database/CommonEvent.hpp"

class CommonEvents final : public IModifiable {
public:
  std::vector<std::optional<CommonEvent>>& events() { return m_events; }
  const std::vector<std::optional<CommonEvent>>& events() const { return m_events; }

  [[nodiscard]] CommonEvent* event(const int id) {
    for (auto& event : m_events) {
      if (event && event->id() == id) {
        return &event.value();
      }
    }
    return nullptr;
  }

  [[nodiscard]] const CommonEvent* event(const int id) const {
    for (const auto& event : m_events) {
      if (event && event->id() == id) {
        return &event.value();
      }
    }

    return nullptr;
  }

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

  void resize(int newSize) {
    ++newSize;
    const int oldSize = m_events.size();
    m_events.resize(newSize);
    if (newSize > oldSize) {
      for (int i = oldSize; i < m_events.size(); ++i) {
        // TODO: CommonEvent constructor
        m_events[i] = CommonEvent();
        m_events[i]->commands().emplace_back(new EventDummy());
        m_events[i]->setId(i);
      }
    }
  }
  void swapReference(int targetId, int newId) {
    if (targetId >= 0 && targetId < m_events.size() && newId >= 0 && newId < m_events.size()) {
      m_events[targetId].value().setId(newId);
      m_events[newId].value().setId(targetId);
      std::swap(m_events[targetId], m_events[newId]);
    }
  }
  [[nodiscard]] bool isUpdating() const { return m_isUpdating; }
  void setUpdate(bool update) { m_isUpdating = update; }

  bool isModified() const {
    return std::ranges::any_of(m_events, [](const auto& event) { return event && event->isModified(); });
  }

private:
  std::vector<std::optional<CommonEvent>> m_events;
  bool m_isUpdating{false};
};


code:
#include "Database/Damage.hpp"

Damage::Damage(const Damage& other)
: IModifiable(other)
, m_critical(other.m_critical)
, m_elementId(other.m_elementId)
, m_formula(other.m_formula)
, m_type(other.m_type)
, m_variance(other.m_variance)
, m_oldcritical(other.m_oldcritical)
, m_oldelementId(other.m_oldelementId)
, m_oldformula(other.m_oldformula)
, m_oldtype(other.m_oldtype)
, m_oldvariance(other.m_oldvariance) {}

Damage& Damage::operator=(const Damage& other) {
  IModifiable::operator=(other);
  m_critical = other.m_critical;
  m_elementId = other.m_elementId;
  m_formula = other.m_formula;
  m_type = other.m_type;
  m_variance = other.m_variance;
  m_oldcritical = other.m_oldcritical;
  m_oldelementId = other.m_oldelementId;
  m_oldformula = other.m_oldformula;
  m_oldtype = other.m_oldtype;
  m_oldvariance = other.m_oldvariance;
  return *this;
}

Damage::Damage(Damage&& other) noexcept
: IModifiable(std::move(other))
, m_critical(other.m_critical)
, m_elementId(other.m_elementId)
, m_formula(std::move(other.m_formula))
, m_type(other.m_type)
, m_variance(other.m_variance)
, m_oldcritical(other.m_oldcritical)
, m_oldelementId(other.m_oldelementId)
, m_oldformula(std::move(other.m_oldformula))
, m_oldtype(other.m_oldtype)
, m_oldvariance(other.m_oldvariance) {}

Damage& Damage::operator=(Damage&& other) noexcept {
  IModifiable::operator=(std::move(other));
  m_critical = other.m_critical;
  m_elementId = other.m_elementId;
  m_formula = std::move(other.m_formula);
  m_type = other.m_type;
  m_variance = other.m_variance;
  m_oldcritical = other.m_oldcritical;
  m_oldelementId = other.m_oldelementId;
  m_oldformula = std::move(other.m_oldformula);
  m_oldtype = other.m_oldtype;
  m_oldvariance = other.m_oldvariance;
  return *this;
}

bool Damage::critical() const { return m_critical; }
void Damage::setCritical(const bool critical) {
  MODIFIABLE_SET_OLD_VALUE(critical);
  m_critical = critical;
  if (!signalsDisabled()) {
   emit_signal(criticalModified(), this, critical);
  }
  setModified();
}

int Damage::elementId() const { return m_elementId; }
void Damage::setElementId(const int elementId) {
  MODIFIABLE_SET_OLD_VALUE(elementId);
  m_elementId = elementId;
  if (!signalsDisabled()) {
   emit_signal(elementIdModified(), this, elementId);
  }
  setModified();
}

const std::string& Damage::formula() const { return m_formula; }
void Damage::setFormula(const std::string& formula) {
  MODIFIABLE_SET_OLD_VALUE(formula);
  m_formula = formula;
  if (!signalsDisabled()) {
   emit_signal(formulaModified(), this, formula);
  }
  setModified();
}

DamageType Damage::type() const { return m_type; }
void Damage::setType(const DamageType type) {
  MODIFIABLE_SET_OLD_VALUE(type);
  m_type = type;
  if (!signalsDisabled()) {
   emit_signal(typeModified(), this, type);
  }
  setModified();
}

int Damage::variance() const { return m_variance; }

void Damage::setVariance(const int variance) {
  MODIFIABLE_SET_OLD_VALUE(variance);
  m_variance = variance;
  if (!signalsDisabled()) {
   emit_signal(varianceModified(), this, variance);
  }
  setModified();
}

void Damage::restoreOriginal() {
  IModifiable::restoreOriginal();
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(critical);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(elementId);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(formula);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(type);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(variance);
}

void Damage::acceptChanges() {
  IModifiable::acceptChanges();
  MODIFIABLE_ACCEPT_VALUE(critical);
  MODIFIABLE_ACCEPT_VALUE(elementId);
  MODIFIABLE_ACCEPT_VALUE(formula);
  MODIFIABLE_ACCEPT_VALUE(type);
  MODIFIABLE_ACCEPT_VALUE(variance);
}

nlohmann::ordered_json Damage::serializeOldValues() const {
  return {
      MODIFIABLE_SERIALIZE_VALUE(critical), MODIFIABLE_SERIALIZE_VALUE(elementId), MODIFIABLE_SERIALIZE_VALUE(formula), MODIFIABLE_SERIALIZE_VALUE(type), MODIFIABLE_SERIALIZE_VALUE(variance),
  };
}

rpgmutils::signal<void(Damage*, bool)>& Damage::criticalModified() {
  if (!m_criticalModified) {
    m_criticalModified.emplace();
  }
  return *m_criticalModified;
}

rpgmutils::signal<void(Damage*, int)>& Damage::elementIdModified() {
  if (!m_elementIdModified) {
    m_elementIdModified.emplace();
  }
  return *m_elementIdModified;
}

rpgmutils::signal<void(Damage*, const std::string&)>& Damage::formulaModified() {
  if (!m_formulaModified) {
    m_formulaModified.emplace();
  }
  return *m_formulaModified;
}

rpgmutils::signal<void(Damage*, DamageType)>& Damage::typeModified() {
  if (!m_typeModified) {
    m_typeModified.emplace();
  }
  return *m_typeModified;
}

rpgmutils::signal<void(Damage*, int)>& Damage::varianceModified() {
  if (!m_varianceModified) {
    m_varianceModified.emplace();
  }
  return *m_varianceModified;
}

void to_json(nlohmann::ordered_json& j, const Damage& damage) {
  j = {
      {"critical", damage.m_critical}, {"elementId", damage.m_elementId}, {"formula", damage.m_formula}, {"type", damage.m_type}, {"variance", damage.m_variance},
  };
}
void from_json(const nlohmann::ordered_json& j, Damage& damage) {
  damage.m_critical = j.value("critical", damage.m_critical);
  damage.m_elementId = j.value("elementId", damage.m_elementId);
  damage.m_formula = j.value("formula", damage.m_formula);
  damage.m_type = j.value("type", damage.m_type);
  damage.m_variance = j.value("variance", damage.m_variance);
}