code:
#include "Database/Map.hpp"

#include "Database/Event.hpp"
#include "Database/EventPage.hpp"
#include <fstream>

Map::Encounter::Encounter(const Encounter& other)
: IModifiable(other)
, m_regionSet(other.m_regionSet)
, m_troopId(other.m_troopId)
, m_weight(other.m_weight)
, m_oldregionSet(other.m_oldregionSet)
, m_oldtroopId(other.m_oldtroopId)
, m_oldweight(other.m_oldweight) {}

Map::Encounter& Map::Encounter::operator=(const Encounter& other) {
  IModifiable::operator=(other);
  m_regionSet = other.m_regionSet;
  m_troopId = other.m_troopId;
  m_weight = other.m_weight;
  m_oldregionSet = other.m_oldregionSet;
  m_oldtroopId = other.m_oldtroopId;
  m_oldweight = other.m_oldweight;
  return *this;
}
Map::Encounter::Encounter(Encounter&& other) noexcept
: IModifiable(std::move(other))
, m_regionSet(std::move(other.m_regionSet))
, m_troopId(other.m_troopId)
, m_weight(other.m_weight)
, m_oldregionSet(std::move(other.m_oldregionSet))
, m_oldtroopId(other.m_oldtroopId)
, m_oldweight(other.m_oldweight) {}
Map::Encounter& Map::Encounter::operator=(Encounter&& other) noexcept {
  IModifiable::operator=(std::move(other));
  m_regionSet = std::move(other.m_regionSet);
  m_troopId = other.m_troopId;
  m_weight = other.m_weight;
  m_oldregionSet = std::move(other.m_oldregionSet);
  m_oldtroopId = other.m_oldtroopId;
  m_oldweight = other.m_oldweight;
  return *this;
}

int Map::Encounter::troopId() const { return m_troopId; }

void Map::Encounter::setTroopId(int id) {
  if (m_troopId == id) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(troopId);
  m_troopId = id;
  if (!signalsDisabled()) {
    emit_signal(troopIdModified(), this, id);
  }
  setModified();
}

int Map::Encounter::weight() const { return m_weight; }
void Map::Encounter::setWeight(int weight) {
  if (m_weight == weight) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(weight);
  m_weight = weight;
  if (!signalsDisabled()) {
    emit_signal(weightModified(), this, weight);
  }
  setModified();
}

const std::vector<int>& Map::Encounter::regionSet() { return m_regionSet; }
void Map::Encounter::setRegionSet(const std::vector<int>& regionSet) {
  if (m_regionSet == regionSet) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(regionSet);
  m_regionSet = regionSet;
  if (!signalsDisabled()) {
    emit_signal(regionSetModified(), this, regionSet);
  }
  setModified();
}
void Map::Encounter::setRegion(int idx, int region) {
  if (idx >= m_regionSet.size()) {
    m_regionSet.resize(idx + 1);
  }
  if (region == m_regionSet[idx]) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(regionSet);
  m_regionSet[idx] = region;
  if (!signalsDisabled()) {
    emit_signal(regionModified(), this, idx, region);
  }
  setModified();
}

void Map::Encounter::restoreOriginal() {
  IModifiable::restoreOriginal();
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(regionSet);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(troopId);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(weight);
}
void Map::Encounter::acceptChanges() {
  IModifiable::acceptChanges();
  MODIFIABLE_ACCEPT_VALUE(regionSet);
  MODIFIABLE_ACCEPT_VALUE(troopId);
  MODIFIABLE_ACCEPT_VALUE(weight);
}
nlohmann::ordered_json Map::Encounter::serializeOldValues() const {
  return {
      MODIFIABLE_SERIALIZE_VALUE(regionSet),
      MODIFIABLE_SERIALIZE_VALUE(troopId),
      MODIFIABLE_SERIALIZE_VALUE(weight),
  };
}

rpgmutils::signal<void(Map::Encounter*, const std::vector<int>&)>& Map::Encounter::regionSetModified() {
  if (!m_regionSetModified) {
    m_regionSetModified.emplace();
  }
  return *m_regionSetModified;
}
rpgmutils::signal<void(Map::Encounter*, int, int)>& Map::Encounter::regionModified() {
  if (!m_regionModified) {
    m_regionModified.emplace();
  }
  return *m_regionModified;
}
rpgmutils::signal<void(Map::Encounter*, int)>& Map::Encounter::troopIdModified() {
  if (!m_troopIdModified) {
    m_troopIdModified.emplace();
  }
  return *m_troopIdModified;
}
rpgmutils::signal<void(Map::Encounter*, int)>& Map::Encounter::weightModified() {
  if (!m_weightModified) {
    m_weightModified.emplace();
  }
  return *m_weightModified;
}

Map::Map(const Map& other)
: IModifiable(other)
, m_autoplayBgm(other.m_autoplayBgm)
, m_autoplayBgs(other.m_autoplayBgs)
, m_battleback1Name(other.m_battleback1Name)
, m_battleback2Name(other.m_battleback2Name)
, m_bgm(other.m_bgm)
, m_bgs(other.m_bgs)
, m_disableDashing(other.m_disableDashing)
, m_encounterList(other.m_encounterList)
, m_encounterStep(other.m_encounterStep)
, m_note(other.m_note)
, m_parallaxLoopX(other.m_parallaxLoopX)
, m_parallaxLoopY(other.m_parallaxLoopY)
, m_parallaxName(other.m_parallaxName)
, m_parallaxShow(other.m_parallaxShow)
, m_parallaxSx(other.m_parallaxSx)
, m_parallaxSy(other.m_parallaxSy)
, m_scrollType(other.m_scrollType)
, m_specifyBattleback(other.m_specifyBattleback)
, m_tilesetId(other.m_tilesetId)
, m_width(other.m_width)
, m_height(other.m_height)
, m_data(other.m_data)
, m_events(other.m_events)
, m_oldautoplayBgm(other.m_oldautoplayBgm)
, m_oldautoplayBgs(other.m_oldautoplayBgs)
, m_oldbattleback1Name(other.m_oldbattleback1Name)
, m_oldbattleback2Name(other.m_oldbattleback2Name)
, m_oldbgm(other.m_oldbgm)
, m_oldbgs(other.m_oldbgs)
, m_olddisableDashing(other.m_olddisableDashing)
, m_oldencounterList(other.m_oldencounterList)
, m_oldencounterStep(other.m_oldencounterStep)
, m_oldnote(other.m_oldnote)
, m_oldparallaxLoopX(other.m_oldparallaxLoopX)
, m_oldparallaxLoopY(other.m_oldparallaxLoopY)
, m_oldparallaxName(other.m_oldparallaxName)
, m_oldparallaxShow(other.m_oldparallaxShow)
, m_oldparallaxSx(other.m_oldparallaxSx)
, m_oldparallaxSy(other.m_oldparallaxSy)
, m_oldscrollType(other.m_oldscrollType)
, m_oldspecifyBattleback(other.m_oldspecifyBattleback)
, m_oldtilesetId(other.m_oldtilesetId)
, m_oldwidth(other.m_oldwidth)
, m_oldheight(other.m_oldheight)
, m_olddata(other.m_olddata)
, m_oldevents(std::move(other.m_oldevents)) {}

Map& Map::operator=(const Map& other) {
  IModifiable::operator=(other);
  m_autoplayBgm = other.m_autoplayBgm;
  m_autoplayBgs = other.m_autoplayBgs;
  m_battleback1Name = other.m_battleback1Name;
  m_battleback2Name = other.m_battleback2Name;
  m_bgm = other.m_bgm;
  m_bgs = other.m_bgs;
  m_disableDashing = other.m_disableDashing;
  m_encounterList = other.m_encounterList;
  m_encounterStep = other.m_encounterStep;
  m_note = other.m_note;
  m_parallaxLoopX = other.m_parallaxLoopX;
  m_parallaxLoopY = other.m_parallaxLoopY;
  m_parallaxName = other.m_parallaxName;
  m_parallaxShow = other.m_parallaxShow;
  m_parallaxSx = other.m_parallaxSx;
  m_parallaxSy = other.m_parallaxSy;
  m_scrollType = other.m_scrollType;
  m_specifyBattleback = other.m_specifyBattleback;
  m_tilesetId = other.m_tilesetId;
  m_width = other.m_width;
  m_height = other.m_height;
  m_data = other.m_data;
  m_events = other.m_events;
  m_oldautoplayBgm = other.m_oldautoplayBgm;
  m_oldautoplayBgs = other.m_oldautoplayBgs;
  m_oldbattleback1Name = other.m_oldbattleback1Name;
  m_oldbattleback2Name = other.m_oldbattleback2Name;
  m_oldbgm = other.m_oldbgm;
  m_oldbgs = other.m_oldbgs;
  m_olddisableDashing = other.m_olddisableDashing;
  m_oldencounterList = other.m_oldencounterList;
  m_oldencounterStep = other.m_oldencounterStep;
  m_oldnote = other.m_oldnote;
  m_oldparallaxLoopX = other.m_oldparallaxLoopX;
  m_oldparallaxLoopY = other.m_oldparallaxLoopY;
  m_oldparallaxName = other.m_oldparallaxName;
  m_oldparallaxShow = other.m_oldparallaxShow;
  m_oldparallaxSx = other.m_oldparallaxSx;
  m_oldparallaxSy = other.m_oldparallaxSy;
  m_oldscrollType = other.m_oldscrollType;
  m_oldspecifyBattleback = other.m_oldspecifyBattleback;
  m_oldtilesetId = other.m_oldtilesetId;
  m_oldwidth = other.m_oldwidth;
  m_oldheight = other.m_oldheight;
  m_olddata = other.m_olddata;
  return *this;
}
Map::Map(Map&& other) noexcept
: IModifiable(std::move(other))
, m_autoplayBgm(other.m_autoplayBgm)
, m_autoplayBgs(other.m_autoplayBgs)
, m_battleback1Name(std::move(other.m_battleback1Name))
, m_battleback2Name(std::move(other.m_battleback2Name))
, m_bgm(std::move(other.m_bgm))
, m_bgs(std::move(other.m_bgs))
, m_disableDashing(other.m_disableDashing)
, m_encounterList(std::move(other.m_encounterList))
, m_encounterStep(other.m_encounterStep)
, m_note(std::move(other.m_note))
, m_parallaxLoopX(other.m_parallaxLoopX)
, m_parallaxLoopY(other.m_parallaxLoopY)
, m_parallaxName(std::move(other.m_parallaxName))
, m_parallaxShow(other.m_parallaxShow)
, m_parallaxSx(other.m_parallaxSx)
, m_parallaxSy(other.m_parallaxSy)
, m_scrollType(other.m_scrollType)
, m_specifyBattleback(other.m_specifyBattleback)
, m_tilesetId(other.m_tilesetId)
, m_width(other.m_width)
, m_height(other.m_height)
, m_data(std::move(other.m_data))
, m_events(std::move(other.m_events))
, m_oldautoplayBgm(other.m_oldautoplayBgm)
, m_oldautoplayBgs(other.m_oldautoplayBgs)
, m_oldbattleback1Name(std::move(other.m_oldbattleback1Name))
, m_oldbattleback2Name(std::move(other.m_oldbattleback2Name))
, m_oldbgm(std::move(other.m_oldbgm))
, m_oldbgs(std::move(other.m_oldbgs))
, m_olddisableDashing(other.m_olddisableDashing)
, m_oldencounterList(std::move(other.m_oldencounterList))
, m_oldencounterStep(other.m_oldencounterStep)
, m_oldnote(std::move(other.m_oldnote))
, m_oldparallaxLoopX(other.m_oldparallaxLoopX)
, m_oldparallaxLoopY(other.m_oldparallaxLoopY)
, m_oldparallaxName(std::move(other.m_oldparallaxName))
, m_oldparallaxShow(other.m_oldparallaxShow)
, m_oldparallaxSx(other.m_oldparallaxSx)
, m_oldparallaxSy(other.m_oldparallaxSy)
, m_oldscrollType(other.m_oldscrollType)
, m_oldspecifyBattleback(other.m_oldspecifyBattleback)
, m_oldtilesetId(other.m_oldtilesetId)
, m_oldwidth(other.m_oldwidth)
, m_oldheight(other.m_oldheight)
, m_olddata(other.m_olddata)
, m_oldevents(std::move(other.m_oldevents)) {};
Map& Map::operator=(Map&& other) noexcept {
  IModifiable::operator=(std::move(other));
  m_autoplayBgm = other.m_autoplayBgm;
  m_autoplayBgs = other.m_autoplayBgs;
  m_battleback1Name = std::move(other.m_battleback1Name);
  m_battleback2Name = std::move(other.m_battleback2Name);
  m_bgm = std::move(other.m_bgm);
  m_bgs = std::move(other.m_bgs);
  m_disableDashing = other.m_disableDashing;
  m_encounterList = std::move(other.m_encounterList);
  m_encounterStep = other.m_encounterStep;
  m_note = std::move(other.m_note);
  m_parallaxLoopX = other.m_parallaxLoopX;
  m_parallaxLoopY = other.m_parallaxLoopY;
  m_parallaxName = std::move(other.m_parallaxName);
  m_parallaxShow = other.m_parallaxShow;
  m_parallaxSx = other.m_parallaxSx;
  m_parallaxSy = other.m_parallaxSy;
  m_scrollType = other.m_scrollType;
  m_specifyBattleback = other.m_specifyBattleback;
  m_tilesetId = other.m_tilesetId;
  m_width = other.m_width;
  m_height = other.m_height;
  m_data = std::move(other.m_data);
  m_events = std::move(other.m_events);
  m_oldautoplayBgm = other.m_oldautoplayBgm;
  m_oldautoplayBgs = other.m_oldautoplayBgs;
  m_oldbattleback1Name = std::move(other.m_oldbattleback1Name);
  m_oldbattleback2Name = std::move(other.m_oldbattleback2Name);
  m_oldbgm = std::move(other.m_oldbgm);
  m_oldbgs = std::move(other.m_oldbgs);
  m_olddisableDashing = other.m_olddisableDashing;
  m_oldencounterList = std::move(other.m_oldencounterList);
  m_oldencounterStep = other.m_oldencounterStep;
  m_oldnote = std::move(other.m_oldnote);
  m_oldparallaxLoopX = other.m_oldparallaxLoopX;
  m_oldparallaxLoopY = other.m_oldparallaxLoopY;
  m_oldparallaxName = std::move(other.m_oldparallaxName);
  m_oldparallaxShow = other.m_oldparallaxShow;
  m_oldparallaxSx = other.m_oldparallaxSx;
  m_oldparallaxSy = other.m_oldparallaxSy;
  m_oldscrollType = other.m_oldscrollType;
  m_oldspecifyBattleback = other.m_oldspecifyBattleback;
  m_oldtilesetId = other.m_oldtilesetId;
  m_oldwidth = other.m_oldwidth;
  m_oldheight = other.m_oldheight;
  return *this;
}

[[nodiscard]] bool Map::autoplayBgm() const { return m_autoplayBgm; }
void Map::setAutoplayBgm(const bool value) {
  if (value == m_autoplayBgm) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(autoplayBgm);
  m_autoplayBgm = value;
  if (!signalsDisabled()) {
    emit_signal(autoplayBgmModified(), this, value);
  }
  setModified();
}

[[nodiscard]] bool Map::autoplayBgs() const { return m_autoplayBgs; }
void Map::setAutoplayBgs(const bool value) {
  if (value == m_autoplayBgs) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(autoplayBgs);
  m_autoplayBgs = value;
  if (!signalsDisabled()) {
    emit_signal(autoplayBgsModified(), this, value);
  }
  setModified();
}

const std::string& Map::battleback1Name() const { return m_battleback1Name; }
void Map::setBattleback1Name(const std::string& value) {
  if (value == m_battleback1Name) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(battleback1Name);
  m_battleback1Name = value;
  if (!signalsDisabled()) {
    emit_signal(battleback1NameModified(), this, value);
  }
  setModified();
}

const std::string& Map::battleback2Name() const { return m_battleback2Name; }
void Map::setBattleback2Name(const std::string& value) {
  if (value == m_battleback2Name) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(battleback2Name);
  m_battleback2Name = value;
  if (!signalsDisabled()) {
    emit_signal(battleback2NameModified(), this, value);
  }
  setModified();
}

Audio& Map::bgm() { return m_bgm; }
void Map::setBgm(const Audio& bgm) {
  if (bgm == m_bgm) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(bgm);
  m_bgm = bgm;
  if (!signalsDisabled()) {
    emit_signal(bgmModified(), this, bgm);
  }
  setModified();
}

Audio& Map::bgs() { return m_bgs; }
void Map::setBgs(const Audio& bgs) {
  if (bgs == m_bgs) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(bgs);
  m_bgs = bgs;
  if (!signalsDisabled()) {
    emit_signal(bgsModified(), this, bgs);
  }
  setModified();
}

bool Map::disableDashing() const { return m_disableDashing; }
void Map::setDisableDashing(bool value) {
  if (value == m_disableDashing) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(disableDashing);
  m_disableDashing = value;
  if (!signalsDisabled()) {
    emit_signal(disableDashingModified(), this, value);
  }
  setModified();
}

const std::string& Map::displayName() const { return m_displayName; }
void Map::setDisplayName(const std::string& value) {
  if (value == m_displayName) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(displayName);
  m_displayName = value;
  if (!signalsDisabled()) {
    emit_signal(displayNameModified(), this, value);
  }
  setModified();
}

std::vector<Map::Encounter>& Map::encounterList() { return m_encounterList; }
void Map::setEncounterList(const std::vector<Encounter>& value) {
  if (value == m_encounterList) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(encounterList);
  m_encounterList = value;
  if (!signalsDisabled()) {
    emit_signal(encounterListModified(), this, value);
  }
  setModified();
}

Map::Encounter& Map::encounter(int id) {
  assert(id >= 0 && id < m_encounterList.size());
  return m_encounterList[id];
}

void Map::setEncounter(int id, const Encounter& encounter) {
  assert(id >= 0 && id < m_encounterList.size());
  if (m_encounterList[id] == encounter) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(encounterList);
  m_encounterList[id] = encounter;
  if (!signalsDisabled()) {
    emit_signal(encounterListModified(), this, m_encounterList);
  }
  setModified();
}

[[nodiscard]] int Map::encounterStep() const { return m_encounterStep; }
void Map::setEncounterStep(int value) {
  if (value == m_encounterStep) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(encounterStep);
  m_encounterStep = value;
  if (!signalsDisabled()) {
    emit_signal(encounterStepModified(), this, value);
  }
  setModified();
}

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

[[nodiscard]] bool Map::parallaxLoopX() const { return m_parallaxLoopX; }
void Map::setParallaxLoopX(bool value) {
  if (value == m_parallaxLoopX) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(parallaxLoopX);
  m_parallaxLoopX = value;
  if (!signalsDisabled()) {
    emit_signal(parallaxLoopXModified(), this, value);
  }
  setModified();
}

[[nodiscard]] bool Map::parallaxLoopY() const { return m_parallaxLoopY; }
void Map::setParallaxLoopY(bool value) {
  if (value == m_parallaxLoopY) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(parallaxLoopY);
  m_parallaxLoopY = value;
  if (!signalsDisabled()) {
    emit_signal(parallaxLoopYModified(), this, value);
  }
  setModified();
}

const std::string& Map::parallaxName() const { return m_parallaxName; }
void Map::setParallaxName(const std::string& value) {
  if (value == m_parallaxName) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(parallaxName);
  m_parallaxName = value;
  if (!signalsDisabled()) {
    emit_signal(parallaxNameModified(), this, value);
  }
  setModified();
}

[[nodiscard]] bool Map::parallaxShow() const { return m_parallaxShow; }
void Map::setParallaxShow(bool value) {
  if (value == m_parallaxShow) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(parallaxShow);
  m_parallaxShow = value;
  if (!signalsDisabled()) {
    emit_signal(parallaxShowModified(), this, value);
  }
  setModified();
}

[[nodiscard]] int Map::parallaxSx() const { return m_parallaxSx; }
void Map::setParallaxSx(int value) {
  if (value == m_parallaxSx) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(parallaxSx);
  m_parallaxSx = value;
  if (!signalsDisabled()) {
    emit_signal(parallaxSxModified(), this, value);
  }
  setModified();
}

[[nodiscard]] int Map::parallaxSy() const { return m_parallaxSy; }
void Map::setParallaxSy(int value) {
  if (value == m_parallaxSy) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(parallaxSy);
  m_parallaxSy = value;
  if (!signalsDisabled()) {
    emit_signal(parallaxSyModified(), this, value);
  }
  setModified();
}

[[nodiscard]] ScrollType Map::scrollType() const { return m_scrollType; }
void Map::setScrollType(ScrollType value) {
  if (value == m_scrollType) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(scrollType);
  m_scrollType = value;
  if (!signalsDisabled()) {
    emit_signal(scrollTypeModified(), this, value);
  }
  setModified();
}

[[nodiscard]] bool Map::specifyBattleback() const { return m_specifyBattleback; }
void Map::setSpecifyBattleback(bool value) {
  if (value == m_specifyBattleback) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(specifyBattleback);
  m_specifyBattleback = value;
  if (!signalsDisabled()) {
    emit_signal(specifyBattlebackModified(), this, value);
  }
  setModified();
}

[[nodiscard]] int Map::tilesetId() const { return m_tilesetId; }
void Map::setTilesetId(int value) {
  if (value == m_tilesetId) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(tilesetId);
  m_tilesetId = value;
  if (!signalsDisabled()) {
    emit_signal(tilesetIdModified(), this, value);
  }
  setModified();
}

[[nodiscard]] int Map::width() const { return m_width; }
void Map::setWidth(int value) {
  if (value == m_width) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(width);
  m_width = value;
  if (!signalsDisabled()) {
    emit_signal(widthModified(), this, value);
  }
  setModified();
}

[[nodiscard]] int Map::height() const { return m_height; }
void Map::setHeight(int value) {
  if (value == m_height) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(height);
  m_height = value;
  if (!signalsDisabled()) {
    emit_signal(heightModified(), this, value);
  }
  setModified();
}

const std::vector<std::optional<int>>& Map::data() const { return m_data; }
void Map::setData(const std::vector<std::optional<int>>& value) {
  if (value == m_data) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(data);
  m_data = value;
  if (!signalsDisabled()) {
    emit_signal(dataModified(), this, value);
  }
  setModified();
}
void Map::setTileAt(const int id, const int x, const int y, const int layer) {
  if (x < 0 || x >= m_width) {
    return;
  }
  if (y < 0 || y >= m_height) {
    return;
  }
  if (layer >= 6) {
    return;
  }
  auto& tile = m_data[(layer * m_height + y) * m_width + x];
  if (tile && *tile == id) {
    return;
  }
  tile = id;
  if (!signalsDisabled()) {
    emit_signal(dataModified(), this, m_data);
  }
  setModified();
}

const std::vector<std::optional<Event>>& Map::events() const { return m_events; }
void Map::setEvents(const std::vector<std::optional<Event>>& value) {
  if (value == m_events) {
    return;
  }
  MODIFIABLE_SET_OLD_VALUE(events);
  m_events = value;
  if (!signalsDisabled()) {
    emit_signal(eventsModified(), this, value);
  }
  setModified();
}

void Map::restoreOriginal() {
  IModifiable::restoreOriginal();
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(autoplayBgm);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(autoplayBgs);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(battleback1Name);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(battleback2Name);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(bgm);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(bgs);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(disableDashing);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(displayName);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(encounterList);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(note);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(parallaxLoopX);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(parallaxLoopY);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(parallaxName);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(parallaxSx);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(parallaxSy);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(scrollType);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(specifyBattleback);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(tilesetId);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(width);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(height);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(data);
  MODIFIABLE_RESTORE_ORIGINAL_VALUE(events);

  m_bgm.restoreOriginal();
  m_bgs.restoreOriginal();
  std::ranges::for_each(m_encounterList, [&](auto& e) { e.restoreOriginal(); });
  std::ranges::for_each(m_events, [&](auto& e) {
    if (e)
      e->restoreOriginal();
  });
}
void Map::acceptChanges() {
  IModifiable::acceptChanges();
  MODIFIABLE_ACCEPT_VALUE(autoplayBgm);
  MODIFIABLE_ACCEPT_VALUE(autoplayBgs);
  MODIFIABLE_ACCEPT_VALUE(battleback1Name);
  MODIFIABLE_ACCEPT_VALUE(battleback2Name);
  MODIFIABLE_ACCEPT_VALUE(bgm);
  MODIFIABLE_ACCEPT_VALUE(bgs);
  MODIFIABLE_ACCEPT_VALUE(disableDashing);
  MODIFIABLE_ACCEPT_VALUE(displayName);
  MODIFIABLE_ACCEPT_VALUE(encounterList);
  MODIFIABLE_ACCEPT_VALUE(note);
  MODIFIABLE_ACCEPT_VALUE(parallaxLoopX);
  MODIFIABLE_ACCEPT_VALUE(parallaxLoopY);
  MODIFIABLE_ACCEPT_VALUE(parallaxName);
  MODIFIABLE_ACCEPT_VALUE(parallaxSx);
  MODIFIABLE_ACCEPT_VALUE(parallaxSy);
  MODIFIABLE_ACCEPT_VALUE(scrollType);
  MODIFIABLE_ACCEPT_VALUE(specifyBattleback);
  MODIFIABLE_ACCEPT_VALUE(tilesetId);
  MODIFIABLE_ACCEPT_VALUE(width);
  MODIFIABLE_ACCEPT_VALUE(height);
  MODIFIABLE_ACCEPT_VALUE(data);
  MODIFIABLE_ACCEPT_VALUE(events);

  m_bgm.acceptChanges();
  m_bgs.acceptChanges();
  std::ranges::for_each(m_encounterList, [&](auto& e) { e.acceptChanges(); });
  std::ranges::for_each(m_events, [&](auto& e) {
    if (e)
      e->acceptChanges();
  });
}
nlohmann::ordered_json Map::serializeOldValues() const {
  return {
      MODIFIABLE_SERIALIZE_VALUE(autoplayBgm),
      MODIFIABLE_SERIALIZE_VALUE(autoplayBgs),
      MODIFIABLE_SERIALIZE_VALUE(battleback1Name),
      MODIFIABLE_SERIALIZE_VALUE(battleback2Name),
      MODIFIABLE_SERIALIZE_CHILD_VALUE(bgm),
      MODIFIABLE_SERIALIZE_CHILD_VALUE(bgs),
      MODIFIABLE_SERIALIZE_VALUE(disableDashing),
      MODIFIABLE_SERIALIZE_VALUE(displayName),
      MODIFIABLE_SERIALIZE_VALUE_CHILD_ARRAY(encounterList),
      MODIFIABLE_SERIALIZE_VALUE(encounterStep),
      MODIFIABLE_SERIALIZE_VALUE(note),
      MODIFIABLE_SERIALIZE_VALUE(parallaxLoopX),
      MODIFIABLE_SERIALIZE_VALUE(parallaxLoopY),
      MODIFIABLE_SERIALIZE_VALUE(parallaxName),
      MODIFIABLE_SERIALIZE_VALUE(parallaxSx),
      MODIFIABLE_SERIALIZE_VALUE(parallaxSx),
      MODIFIABLE_SERIALIZE_VALUE(scrollType),
      MODIFIABLE_SERIALIZE_VALUE(specifyBattleback),
      MODIFIABLE_SERIALIZE_VALUE(tilesetId),
      MODIFIABLE_SERIALIZE_VALUE(width),
      MODIFIABLE_SERIALIZE_VALUE(height),
      MODIFIABLE_SERIALIZE_VALUE(data),
      MODIFIABLE_SERIALIZE_VALUE_CHILD_ARRAY_OPTIONAL(events),
  };
}

rpgmutils::signal<void(Map*, bool)>& Map::autoplayBgmModified() {
  if (!m_autoplayBgmModified) {
    m_autoplayBgmModified.emplace();
  }
  return *m_autoplayBgmModified;
}
rpgmutils::signal<void(Map*, bool)>& Map::autoplayBgsModified() {
  if (!m_autoplayBgsModified) {
    m_autoplayBgsModified.emplace();
  }
  return *m_autoplayBgsModified;
}
rpgmutils::signal<void(Map*, const std::string&)>& Map::battleback1NameModified() {
  if (!m_battleback1NameModified) {
    m_battleback1NameModified.emplace();
  }
  return *m_battleback1NameModified;
}
rpgmutils::signal<void(Map*, const std::string&)>& Map::battleback2NameModified() {
  if (!m_battleback2NameModified) {
    m_battleback2NameModified.emplace();
  }
  return *m_battleback2NameModified;
}
rpgmutils::signal<void(Map*, Audio)>& Map::bgmModified() {
  if (!m_bgmModified) {
    m_bgmModified.emplace();
  }
  return *m_bgmModified;
}
rpgmutils::signal<void(Map*, Audio)>& Map::bgsModified() {
  if (!m_bgsModified) {
    m_bgsModified.emplace();
  }
  return *m_bgsModified;
}
rpgmutils::signal<void(Map*, bool)>& Map::disableDashingModified() {
  if (!m_disableDashingModified) {
    m_disableDashingModified.emplace();
  }
  return *m_disableDashingModified;
}
rpgmutils::signal<void(Map*, const std::string&)>& Map::displayNameModified() {
  if (!m_displayNameModified) {
    m_displayNameModified.emplace();
  }
  return *m_displayNameModified;
}
rpgmutils::signal<void(Map*, const std::vector<Map::Encounter>&)>& Map::encounterListModified() {
  if (!m_encounterListModified) {
    m_encounterListModified.emplace();
  }
  return *m_encounterListModified;
}
rpgmutils::signal<void(Map*, int)>& Map::encounterStepModified() {
  if (!m_encounterStepModified) {
    m_encounterStepModified.emplace();
  }
  return *m_encounterStepModified;
}
rpgmutils::signal<void(Map*, const std::string&)>& Map::noteModified() {
  if (!m_noteModified) {
    m_noteModified.emplace();
  }
  return *m_noteModified;
}
rpgmutils::signal<void(Map*, bool)>& Map::parallaxLoopXModified() {
  if (!m_parallaxLoopXModified) {
    m_parallaxLoopXModified.emplace();
  }
  return *m_parallaxLoopXModified;
}
rpgmutils::signal<void(Map*, bool)>& Map::parallaxLoopYModified() {
  if (!m_parallaxLoopYModified) {
    m_parallaxLoopYModified.emplace();
  }
  return *m_parallaxLoopYModified;
}
rpgmutils::signal<void(Map*, const std::string&)>& Map::parallaxNameModified() {
  if (!m_parallaxNameModified) {
    m_parallaxNameModified.emplace();
  }
  return *m_parallaxNameModified;
}
rpgmutils::signal<void(Map*, bool)>& Map::parallaxShowModified() {
  if (!m_parallaxShowModified) {
    m_parallaxShowModified.emplace();
  }
  return *m_parallaxShowModified;
}
rpgmutils::signal<void(Map*, int)>& Map::parallaxSxModified() {
  if (!m_parallaxSxModified) {
    m_parallaxSxModified.emplace();
  }
  return *m_parallaxSxModified;
}
rpgmutils::signal<void(Map*, int)>& Map::parallaxSyModified() {
  if (!m_parallaxSyModified) {
    m_parallaxSyModified.emplace();
  }
  return *m_parallaxSyModified;
}
rpgmutils::signal<void(Map*, ScrollType)>& Map::scrollTypeModified() {
  if (!m_scrollTypeModified) {
    m_scrollTypeModified.emplace();
  }
  return *m_scrollTypeModified;
}
rpgmutils::signal<void(Map*, bool)>& Map::specifyBattlebackModified() {
  if (!m_specifyBattlebackModified) {
    m_specifyBattlebackModified.emplace();
  }
  return *m_specifyBattlebackModified;
}
rpgmutils::signal<void(Map*, int)>& Map::tilesetIdModified() {
  if (!m_tilesetIdModified) {
    m_tilesetIdModified.emplace();
  }
  return *m_tilesetIdModified;
}
rpgmutils::signal<void(Map*, int)>& Map::widthModified() {
  if (!m_widthModified) {
    m_widthModified.emplace();
  }
  return *m_widthModified;
}
rpgmutils::signal<void(Map*, int)>& Map::heightModified() {
  if (!m_heightModified) {
    m_heightModified.emplace();
  }
  return *m_heightModified;
}
rpgmutils::signal<void(Map*, const std::vector<std::optional<int>>&)>& Map::dataModified() {
  if (!m_dataModified) {
    m_dataModified.emplace();
  }
  return *m_dataModified;
}
rpgmutils::signal<void(Map*, const std::vector<std::optional<Event>>&)>& Map::eventsModified() {
  if (!m_eventsModified) {
    m_eventsModified.emplace();
  }
  return *m_eventsModified;
}

void Map::resize(const int newWidth, const int newHeight) {
  static constexpr int MaxLayers = 6;
  std::vector<std::optional<int>> newData;
  newData.resize(MaxLayers * newWidth * newHeight);
  std::ranges::fill(newData.begin(), newData.end(), 0);

  for (int y = 0; y < newHeight; ++y) {
    for (int x = 0; x < newWidth; ++x) {
      if (x < m_width && y < m_height) {
        for (int z = 0; z < MaxLayers; ++z) {
          const int si = (z * m_height + y) * m_width + x;
          const int di = (z * newHeight + y) * newWidth + x;
          newData[di] = m_data[si];
        }
      }
    }
  }

  std::ranges::for_each(m_events, [&](auto& event) {
    if (event->x() >= newWidth || event->y() >= newHeight) {
      event.reset();
    }
  });

  m_width = newWidth;
  m_height = newHeight;
  m_data = std::move(newData);
}

[[nodiscard]] std::vector<Event*> Map::getSorted() {
  std::vector<Event*> ret;
  for (auto& e : m_events) {
    if (e) {
      ret.push_back(&e.value());
    }
  }
  std::ranges::sort(ret, [](const Event* a, const Event* b) {
    const int aZ = static_cast<int>(a->page(0)->priorityType()) * 2 + 1;
    const int bZ = static_cast<int>(b->page(0)->priorityType()) * 2 + 1;
    if (aZ != bZ) {
      return aZ < bZ;
    }

    if (a->y() != b->y()) {
      return a->y() < b->y();
    }
    return a->renderer()->spriteId() < b->renderer()->spriteId();
  });
  return ret;
}

[[nodiscard]] std::vector<Event*> Map::getRenderSorted() {
  std::vector<Event*> ret;
  for (auto& e : m_events) {
    if (e) {
      ret.push_back(&e.value());
    }
  }
  std::ranges::sort(ret, [](const Event* a, const Event* b) {
    if (a->renderer()->screenZ() != b->renderer()->screenZ()) {
      return a->renderer()->screenZ() < b->renderer()->screenZ();
    }
    if (a->renderer()->y() != b->renderer()->y()) {
      return a->renderer()->y() < b->renderer()->y();
    }
    return a->renderer()->spriteId() < b->renderer()->spriteId();
  });
  return ret;
}

std::vector<Event*> Map::eventsAtNoThrough(const int x, const int y) {
  std::vector<Event*> ret;
  std::ranges::for_each(m_events, [&ret, &x, &y](auto& ev) {
    if (ev && ev->x() == x && ev->y() == y && !ev->renderer()->isThrough()) {
      ret.push_back(&ev.value());
    }
  });
  return ret;
}

std::vector<Event*> Map::eventsAtRenderPosNoThrough(const int x, const int y) {
  std::vector<Event*> ret;
  std::ranges::for_each(m_events, [&ret, &x, &y](auto& ev) {
    if (ev && ev->renderer()->x() == x && ev->renderer()->y() == y && !ev->renderer()->isThrough()) {
      ret.push_back(&ev.value());
    }
  });
  return ret;
}

void Map::setDummyEvent(const EventImage& image, const int x, const int y) {
  if (!m_events.empty() && !m_events[0]) {
    m_events[0].emplace();
  }
  EventPage page;
  /* We don't care about the state of this object, so disable signals */
  m_events[0]->disableSignals();
  page.setImage(image);
  m_events[0]->setId(0);
  m_events[0]->setPages({page});
  m_events[0]->setX(x);
  m_events[0]->setY(y);
}

Event* Map::event(int id) {

  if (const auto it = std::ranges::find_if(m_events, [&id](const auto& ev) { return ev && ev->id() == id; }); it != m_events.end()) {
    return &it->value();
  }
  return nullptr;
}

Event* Map::eventAt(int x, int y) {
  if (const auto it = std::ranges::find_if(m_events, [&x, &y](const auto& ev) { return ev && ev->x() == x && ev->y() == y; }); it != m_events.end()) {
    return &it->value();
  }

  return nullptr;
}

std::vector<Event*> Map::eventsAt(const int x, const int y) {
  std::vector<Event*> ret;
  std::ranges::for_each(m_events, [&ret, &x, &y](auto& ev) {
    if (ev && ev->x() == x && ev->y() == y) {
      ret.push_back(&ev.value());
    }
  });
  return ret;
}

Event* Map::createNewEvent() {
  if (m_events.empty()) {
    m_events.emplace_back();
  }
  const auto it = std::find_if(m_events.begin() + 1, m_events.end(), [](const auto& ev) { return !ev; });
  Event* ret;
  if (it != m_events.end()) {
    *it = Event();
    (*it)->setId(it - m_events.begin());
    ret = &it->value();
  } else {
    ret = &m_events.emplace_back(Event()).value();
    ret->setId(m_events.size() - 1);
  }
  ret->setName(std::format("EV{:03}", ret->id()));
  ret->addPage({});
  ret->modified().connect<&Map::onEventModified>(this);
  ret->connectAllSignals();

  m_isDirty = true;
  return ret;
}
Event* Map::createEventFromTemplate(const Event& ev) {
  // Inserts new template
  bool isNull{false};
  if (ev.id() == m_events.size()) {
    m_events.emplace_back(ev);
  } else {
    if (m_events.size() < ev.id()) {
      while (m_events.size() <= ev.id()) {
        m_events.emplace_back(std::nullopt);
      }
    }
    if (m_events.at(ev.id()).has_value()) {
      m_events.insert(m_events.begin() + ev.id(), ev);
    } else {
      isNull = true;
      m_events.at(ev.id()) = ev;
    }
  }
  // Resort and rename
  if (!isNull) {
    for (int i = ev.id() + 1; i < m_events.size(); ++i) {
      if (m_events.at(i).has_value()) {
        m_events.at(i)->setId(i);
        if (m_events.at(i)->name().contains("EV")) {
          m_events.at(i)->setName(std::format("EV{:03} ", i));
        }
      }
    }
  }
  return &*m_events.at(ev.id());
}

void Map::deleteEvent(int id) {
  if (const auto it = std::ranges::find_if(m_events, [&id](const auto& ev) { return ev && ev->id() == id; }); it != m_events.end()) {
    it->reset();
    m_isDirty = true;
  }
}

int Map::findOrMakeFreeId() const {
  if (m_events.empty()) {
    return 1;
  }

  for (int i = 1; i < m_events.size(); ++i) {
    if (!m_events[i]) {
      return i;
    }
  }

  return m_events.size();
}

void Map::connectAllEvents() {
  for (auto& ev : m_events) {
    if (!ev || ev->id() == 0) {
      continue;
    }

    // Disconnect just in case
    ev->modified().disconnect<&Map::onEventModified>(this);
    ev->modified().connect<&Map::onEventModified>(this);
    ev->connectAllSignals();
  }
}

void Map::onEventModified(IModifiable*) { emit_signal(modified(), this); }

void to_json(nlohmann::ordered_json& json, const Map& map) {
  json = {
      {"autoplayBgm", map.m_autoplayBgm},
      {"autoplayBgs", map.m_autoplayBgs},
      {"battleback1Name", map.m_battleback1Name},
      {"battleback2Name", map.m_battleback2Name},
      {"bgm", map.m_bgm},
      {"bgs", map.m_bgs},
      {"disableDashing", map.m_disableDashing},
      {"displayName", map.m_displayName},
      {"encounterList", map.m_encounterList},
      {"encounterStep", map.m_encounterStep},
      {"height", map.m_height},
      {"note", map.m_note},
      {"parallaxLoopX", map.m_parallaxLoopX},
      {"parallaxLoopY", map.m_parallaxLoopY},
      {"parallaxName", map.m_parallaxName},
      {"parallaxShow", map.m_parallaxShow},
      {"parallaxSx", map.m_parallaxSx},
      {"parallaxSy", map.m_parallaxSy},
      {"scrollType", map.m_scrollType},
      {"specifyBattleback", map.m_specifyBattleback},
      {"tilesetId", map.m_tilesetId},
      {"width", map.m_width},
      {"data", map.m_data},
  };
  for (const auto& event : map.m_events) {
    if (!event || event->id() == 0) {
      json["events"].push_back(nullptr);
    } else {
      json["events"].push_back(event->clone());
    }
  }
}
void from_json(const nlohmann::ordered_json& json, Map& map) {
  map.m_autoplayBgm = json.value("autoplayBgm", map.m_autoplayBgm);
  map.m_autoplayBgs = json.value("autoplayBgs", map.m_autoplayBgs);
  map.m_battleback1Name = json.value("battleback1Name", map.m_battleback1Name);
  map.m_battleback2Name = json.value("battleback2Name", map.m_battleback2Name);
  map.m_bgm = json.value("bgm", map.m_bgm);
  map.m_bgs = json.value("bgs", map.m_bgs);
  map.m_disableDashing = json.value("disableDashing", map.m_disableDashing);
  map.m_displayName = json.value("displayName", map.m_displayName);
  map.m_encounterList = json.value("encounterList", map.m_encounterList);
  map.m_encounterStep = json.value("encounterStep", map.m_encounterStep);
  map.m_height = json.value("height", map.m_height);
  map.m_note = json.value("note", map.m_note);
  map.m_parallaxLoopX = json.value("parallaxLoopX", map.m_parallaxLoopX);
  map.m_parallaxLoopY = json.value("parallaxLoopY", map.m_parallaxLoopY);
  map.m_parallaxName = json.value("parallaxName", map.m_parallaxName);
  map.m_parallaxShow = json.value("parallaxShow", map.m_parallaxShow);
  map.m_parallaxSx = json.value("parallaxSx", map.m_parallaxSx);
  map.m_parallaxSy = json.value("parallaxSy", map.m_parallaxSy);
  map.m_scrollType = json.value("scrollType", map.m_scrollType);
  map.m_specifyBattleback = json.value("specifyBattleback", map.m_specifyBattleback);
  map.m_tilesetId = json.value("tilesetId", map.m_tilesetId);
  map.m_width = json.value("width", map.m_width);
  map.m_data = json.value("data", map.m_data);
  map.m_events = json.value("events", map.m_events);
}

void to_json(nlohmann::ordered_json& json, const Map::Encounter& encounter) {
  json = {
      {"regionSet", encounter.m_regionSet},
      {"troopId", encounter.m_troopId},
      {"weight", encounter.m_weight},
  };
}
void from_json(const nlohmann::ordered_json& json, Map::Encounter& encounter) {
  encounter.m_regionSet = json.value("regionSet", encounter.m_regionSet);
  encounter.m_troopId = json.value("troopId", encounter.m_troopId);
  encounter.m_weight = json.value("weight", encounter.m_weight);
}


code:
#pragma once

#include "Database/Audio.hpp"
#include "Database/Event.hpp"
#include "Database/EventCommands/EventDummy.hpp"
#include "Database/Globals.hpp"

#include <format>
#include <string>

#include "nlohmann/json.hpp"

class EventImage;
/**
 * @class Map
 * A discrete map unit, contains all information for a given area such as events, tiles, and encounters
 */
class Map final : public IModifiable {
  friend class MapSerializer;

public:
  friend void to_json(nlohmann::ordered_json& json, const Map& map);
  friend void from_json(const nlohmann::ordered_json& json, Map& map);
  /**
   * @class Encounter
   * Defines encounter probabilities for troops in a given set of regions
   * Regions are a group of tiles defined in the map data and applied to the map
   * by the map creator using the Region tileset (Tab "R")
   */
  class Encounter final : public IModifiable {
    friend void to_json(nlohmann::ordered_json& json, const Encounter& encounter);
    friend void from_json(const nlohmann::ordered_json& json, Encounter& encounter);

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

    int troopId() const;
    void setTroopId(int id);

    int weight() const;
    void setWeight(int weight);

    const std::vector<int>& regionSet();
    void setRegionSet(const std::vector<int>& regionSet);
    void setRegion(int idx, int region);

    rpgmutils::signal<void(Encounter*, const std::vector<int>&)>& regionSetModified();
    rpgmutils::signal<void(Encounter*, int, int)>& regionModified();
    rpgmutils::signal<void(Encounter*, int)>& troopIdModified();
    rpgmutils::signal<void(Encounter*, int)>& weightModified();

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

    bool operator==(const Encounter& other) const { return m_regionSet == other.m_regionSet && m_troopId == other.m_troopId && m_weight == other.m_weight; }

  private:
    std::vector<int> m_regionSet{};
    int m_troopId{};
    int m_weight{};
    std::optional<std::vector<int>> m_oldregionSet;
    std::optional<int> m_oldtroopId;
    std::optional<int> m_oldweight;
    std::optional<rpgmutils::signal<void(Encounter*, const std::vector<int>&)>> m_regionSetModified;
    std::optional<rpgmutils::signal<void(Encounter*, int, int)>> m_regionModified;
    std::optional<rpgmutils::signal<void(Encounter*, int)>> m_troopIdModified;
    std::optional<rpgmutils::signal<void(Encounter*, int)>> m_weightModified;
  };
  Map() = default;
  Map(const Map& other);
  Map& operator=(const Map& other);
  Map(Map&& other) noexcept;
  Map& operator=(Map&& other) noexcept;

  /* Functions */
  /**
   * Specifies whether the Background Music defined in autoPlayBgm plays when the map is loaded.
   * @sa autoPlayBgm
   * @return True if autoplay is enabled False otherwise.
   */
  [[nodiscard]] bool autoplayBgm() const;
  /**
   * Sets whether the music defined in autoplayBgm plays on map load.
   * @param value True if the bgm should play automatically, False otherwise
   */
  void setAutoplayBgm(const bool value);

  [[nodiscard]] bool autoplayBgs() const;
  void setAutoplayBgs(const bool value);

  const std::string& battleback1Name() const;
  void setBattleback1Name(const std::string& value);

  const std::string& battleback2Name() const;
  void setBattleback2Name(const std::string& value);

  Audio& bgm();
  void setBgm(const Audio& bgm);

  Audio& bgs();
  void setBgs(const Audio& bgs);

  bool disableDashing() const;
  void setDisableDashing(bool value);

  const std::string& displayName() const;
  void setDisplayName(const std::string& value);

  std::vector<Encounter>& encounterList();
  void setEncounterList(const std::vector<Encounter>& value);

  Encounter& encounter(int id);

  void setEncounter(int id, const Encounter& encounter);

  [[nodiscard]] int encounterStep() const;
  void setEncounterStep(int value);

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

  [[nodiscard]] bool parallaxLoopX() const;
  void setParallaxLoopX(bool value);

  [[nodiscard]] bool parallaxLoopY() const;
  void setParallaxLoopY(bool value);

  const std::string& parallaxName() const;
  void setParallaxName(const std::string& value);

  [[nodiscard]] bool parallaxShow() const;
  void setParallaxShow(bool value);

  [[nodiscard]] int parallaxSx() const;
  void setParallaxSx(int value);

  [[nodiscard]] int parallaxSy() const;
  void setParallaxSy(int value);

  [[nodiscard]] ScrollType scrollType() const;
  void setScrollType(ScrollType value);

  [[nodiscard]] bool specifyBattleback() const;
  void setSpecifyBattleback(bool value);

  [[nodiscard]] int tilesetId() const;
  void setTilesetId(int value);

  [[nodiscard]] int width() const;
  void setWidth(int value);

  [[nodiscard]] int height() const;
  void setHeight(int value);

  const std::vector<std::optional<int>>& data() const;
  void setData(const std::vector<std::optional<int>>& value);
  void setTileAt(int id, int x, int y, int layer);

  const std::vector<std::optional<Event>>& events() const;
  void setEvents(const std::vector<std::optional<Event>>& value);

  void restoreOriginal() override;
  void acceptChanges() override;
  nlohmann::ordered_json serializeOldValues() const override;
  bool isModified() const override {
    bool isModified = IModifiable::isModified();
    isModified |= std::ranges::any_of(m_events, [](const std::optional<Event>& value) {
      /* Ignore the dummy added to the start map to avoid reporting as dirty when we're not */
      return value && value->id() != 0 && value->isModified();
    });
    isModified |= std::ranges::any_of(m_encounterList, [](const Encounter& value) { return value.isModified(); });
    isModified |= m_bgm.isModified();
    isModified |= m_bgs.isModified();
    return isModified;
  }

  rpgmutils::signal<void(Map*, bool)>& autoplayBgmModified();
  rpgmutils::signal<void(Map*, bool)>& autoplayBgsModified();
  rpgmutils::signal<void(Map*, const std::string&)>& battleback1NameModified();
  rpgmutils::signal<void(Map*, const std::string&)>& battleback2NameModified();
  rpgmutils::signal<void(Map*, Audio)>& bgmModified();
  rpgmutils::signal<void(Map*, Audio)>& bgsModified();
  rpgmutils::signal<void(Map*, bool)>& disableDashingModified();
  rpgmutils::signal<void(Map*, const std::string&)>& displayNameModified();
  rpgmutils::signal<void(Map*, const std::vector<Encounter>&)>& encounterListModified();
  rpgmutils::signal<void(Map*, int)>& encounterStepModified();
  rpgmutils::signal<void(Map*, const std::string&)>& noteModified();
  rpgmutils::signal<void(Map*, bool)>& parallaxLoopXModified();
  rpgmutils::signal<void(Map*, bool)>& parallaxLoopYModified();
  rpgmutils::signal<void(Map*, const std::string&)>& parallaxNameModified();
  rpgmutils::signal<void(Map*, bool)>& parallaxShowModified();
  rpgmutils::signal<void(Map*, int)>& parallaxSxModified();
  rpgmutils::signal<void(Map*, int)>& parallaxSyModified();
  rpgmutils::signal<void(Map*, ScrollType)>& scrollTypeModified();
  rpgmutils::signal<void(Map*, bool)>& specifyBattlebackModified();
  rpgmutils::signal<void(Map*, int)>& tilesetIdModified();
  rpgmutils::signal<void(Map*, int)>& widthModified();
  rpgmutils::signal<void(Map*, int)>& heightModified();
  rpgmutils::signal<void(Map*, const std::vector<std::optional<int>>&)>& dataModified();
  rpgmutils::signal<void(Map*, const std::vector<std::optional<Event>>&)>& eventsModified();

  /* Utility functions */
  [[nodiscard]] std::vector<Event*> getSorted();

  [[nodiscard]] std::vector<Event*> getRenderSorted();

  void setDummyEvent(const EventImage& image, int x, int y);
  Event* event(int id);

  Event* eventAt(int x, int y);

  std::vector<Event*> eventsAt(const int x, const int y);

  std::vector<Event*> eventsAtNoThrough(int x, int y);
  std::vector<Event*> eventsAtRenderPosNoThrough(int x, int y);

  Event* createNewEvent();
  Event* createEventFromTemplate(const Event& ev);

  void deleteEvent(int id);

  int findOrMakeFreeId() const;

  void resize(int newWidth, int newHeight);

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

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

  void connectAllEvents();

private:
  Map(const Map& other, int i)
  : IModifiable(other) {
    m_autoplayBgm = other.m_autoplayBgm;
    m_autoplayBgs = other.m_autoplayBgs;
    m_battleback1Name = other.m_battleback1Name;
    m_battleback2Name = other.m_battleback2Name;
    m_bgm = other.m_bgm;
    m_bgs = other.m_bgs;
    m_disableDashing = other.m_disableDashing;
    m_encounterList = other.m_encounterList;
    m_encounterStep = other.m_encounterStep;
    m_note = other.m_note;
    m_parallaxLoopX = other.m_parallaxLoopX;
    m_parallaxLoopY = other.m_parallaxLoopY;
    m_parallaxName = other.m_parallaxName;
    m_parallaxShow = other.m_parallaxShow;
    m_parallaxSx = other.m_parallaxSx;
    m_parallaxSy = other.m_parallaxSy;
    m_scrollType = other.m_scrollType;
    m_specifyBattleback = other.m_specifyBattleback;
    m_tilesetId = other.m_tilesetId;
    m_width = other.m_width;
    m_height = other.m_height;
    m_data = other.m_data;
    for (const auto& e : other.m_events) {
      if (e) {
        m_events.emplace_back(e->clone());
      } else {
        m_events.emplace_back();
      }
    }
  }
  bool m_autoplayBgm{};
  bool m_autoplayBgs{};
  std::string m_battleback1Name;
  std::string m_battleback2Name;
  Audio m_bgm;
  Audio m_bgs;
  bool m_disableDashing{};
  std::string m_displayName;
  std::vector<Encounter> m_encounterList;
  int m_encounterStep{};
  std::string m_note;
  bool m_parallaxLoopX{};
  bool m_parallaxLoopY{};
  std::string m_parallaxName;
  bool m_parallaxShow{};
  int m_parallaxSx{};
  int m_parallaxSy{};
  ScrollType m_scrollType{};
  bool m_specifyBattleback{};
  int m_tilesetId{};
  int m_width{};
  int m_height{};
  std::vector<std::optional<int>> m_data;
  std::vector<std::optional<Event>> m_events;

  std::optional<bool> m_oldautoplayBgm;
  std::optional<bool> m_oldautoplayBgs;
  std::optional<std::string> m_oldbattleback1Name;
  std::optional<std::string> m_oldbattleback2Name;
  std::optional<Audio> m_oldbgm;
  std::optional<Audio> m_oldbgs;
  std::optional<bool> m_olddisableDashing;
  std::optional<std::string> m_olddisplayName;
  std::optional<std::vector<Encounter>> m_oldencounterList;
  std::optional<int> m_oldencounterStep;
  std::optional<std::string> m_oldnote;
  std::optional<bool> m_oldparallaxLoopX;
  std::optional<bool> m_oldparallaxLoopY;
  std::optional<std::string> m_oldparallaxName;
  std::optional<bool> m_oldparallaxShow;
  std::optional<int> m_oldparallaxSx;
  std::optional<int> m_oldparallaxSy;
  std::optional<ScrollType> m_oldscrollType;
  std::optional<bool> m_oldspecifyBattleback;
  std::optional<int> m_oldtilesetId;
  std::optional<int> m_oldwidth;
  std::optional<int> m_oldheight;
  std::optional<std::vector<std::optional<int>>> m_olddata;
  std::optional<std::vector<std::optional<Event>>> m_oldevents;

  std::optional<rpgmutils::signal<void(Map*, bool)>> m_autoplayBgmModified;
  std::optional<rpgmutils::signal<void(Map*, bool)>> m_autoplayBgsModified;
  std::optional<rpgmutils::signal<void(Map*, const std::string&)>> m_battleback1NameModified;
  std::optional<rpgmutils::signal<void(Map*, const std::string&)>> m_battleback2NameModified;
  std::optional<rpgmutils::signal<void(Map*, Audio)>> m_bgmModified;
  std::optional<rpgmutils::signal<void(Map*, Audio)>> m_bgsModified;
  std::optional<rpgmutils::signal<void(Map*, bool)>> m_disableDashingModified;
  std::optional<rpgmutils::signal<void(Map*, const std::string&)>> m_displayNameModified;
  std::optional<rpgmutils::signal<void(Map*, const std::vector<Encounter>&)>> m_encounterListModified;
  std::optional<rpgmutils::signal<void(Map*, int)>> m_encounterStepModified;
  std::optional<rpgmutils::signal<void(Map*, const std::string&)>> m_noteModified;
  std::optional<rpgmutils::signal<void(Map*, bool)>> m_parallaxLoopXModified;
  std::optional<rpgmutils::signal<void(Map*, bool)>> m_parallaxLoopYModified;
  std::optional<rpgmutils::signal<void(Map*, const std::string&)>> m_parallaxNameModified;
  std::optional<rpgmutils::signal<void(Map*, bool)>> m_parallaxShowModified;
  std::optional<rpgmutils::signal<void(Map*, int)>> m_parallaxSxModified;
  std::optional<rpgmutils::signal<void(Map*, int)>> m_parallaxSyModified;
  std::optional<rpgmutils::signal<void(Map*, ScrollType)>> m_scrollTypeModified;
  std::optional<rpgmutils::signal<void(Map*, bool)>> m_specifyBattlebackModified;
  std::optional<rpgmutils::signal<void(Map*, int)>> m_tilesetIdModified;
  std::optional<rpgmutils::signal<void(Map*, int)>> m_widthModified;
  std::optional<rpgmutils::signal<void(Map*, int)>> m_heightModified;
  std::optional<rpgmutils::signal<void(Map*, const std::vector<std::optional<int>>&)>> m_dataModified;
  std::optional<rpgmutils::signal<void(Map*, const std::vector<std::optional<Event>>&)>> m_eventsModified;

  bool m_isDirty = false;
  bool m_isValid{false};

  void onEventModified(IModifiable* event);
};

void to_json(nlohmann::ordered_json& json, const Map& map);
void from_json(const nlohmann::ordered_json& json, Map& map);
void to_json(nlohmann::ordered_json& json, const Map::Encounter& encounter);
void from_json(const nlohmann::ordered_json& json, Map::Encounter& encounter);

repo_name: Antidote/openrpgeditor

license: mit


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

Map* MapInfo::map(const bool loadSync) {
  if (!m_map) {
    const auto path = std::format("data/Map{:03}.json", id());
    if (loadSync || !FileQueue::instance().hasTask(path)) {
      FileQueue::instance().enqueue(
          std::make_shared<MapSerializer>(path, id()),
          [this, &loadSync](const std::shared_ptr<ISerializable>& serializer) {
            m_map = std::make_unique<Map>(std::dynamic_pointer_cast<MapSerializer>(serializer)->data().clone());
            m_map->modified().connect<&MapInfo::onMapModified>(this);
            m_map->connectAllEvents();
            // RPGM_INFO("Map{:03} loaded{}", id(), loadSync ? " (SYNC)" : "");
          },
          loadSync);
    }
    return nullptr;
  }
  return m_map.get();
}

void MapInfo::setId(const int id) {
  m_id = id;
  emit_signal(mapInfoModified(), this, m_map.get());
}

void MapInfo::setName(const std::string& name) {
  m_name = name;
  emit_signal(mapInfoModified(), this, m_map.get());
}

[[nodiscard]] const Map* MapInfo::map(const bool loadSync) const { return const_cast<MapInfo*>(this)->map(loadSync); }

rpgmutils::signal<void(MapInfo*, Map*)>& MapInfo::mapInfoModified() {
  if (!m_mapInfoModified) {
    m_mapInfoModified.emplace();
  }
  return *m_mapInfoModified;
}

void MapInfo::onMapModified(IModifiable* map) {
  const auto mapPtr = dynamic_cast<Map*>(map);
  if (!mapPtr) {
    RPGM_ERROR("Unable to cast modifiable to map in MapInfo, how??");
    return;
  }
  emit_signal(mapInfoModified(), this, mapPtr);
}

void to_json(nlohmann::ordered_json& json, const MapInfo& mapinfo) {
  json = {
      {"id", mapinfo.m_id},           {"expanded", mapinfo.m_expanded}, {"name", mapinfo.m_name}, {"order", mapinfo.m_order}, {"parentId", mapinfo.m_parentId},
      {"scrollX", mapinfo.m_scrollX}, {"scrollY", mapinfo.m_scrollY},
  };
}

void from_json(const nlohmann::ordered_json& json, MapInfo& mapinfo) {
  mapinfo.m_id = json.value("id", mapinfo.m_id);
  mapinfo.m_expanded = json.value("expanded", mapinfo.m_expanded);
  mapinfo.m_name = json.value("name", mapinfo.m_name);
  mapinfo.m_order = json.value("order", mapinfo.m_order);
  mapinfo.m_parentId = json.value("parentId", mapinfo.m_parentId);
  mapinfo.m_scrollX = json.value("scrollX", mapinfo.m_scrollX);
  mapinfo.m_scrollY = json.value("scrollY", mapinfo.m_scrollY);
}


code:
#pragma once

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

class ISerializable;
class MapInfo final : public IModifiable {
  friend class MapInfosSerializer;
  friend void to_json(nlohmann::ordered_json& json, const MapInfo& mapinfo);
  friend void from_json(const nlohmann::ordered_json& json, MapInfo& mapinfo);
  friend class MapInfos; // TODO: Remove this

public:
  MapInfo() = default;
  MapInfo(const MapInfo& other)
  : IModifiable(other)
  , m_expanded(other.m_expanded)
  , m_id(other.m_id)
  , m_name(other.m_name)
  , m_order(other.m_order)
  , m_parentId(other.m_parentId)
  , m_scrollX(other.m_scrollX)
  , m_scrollY(other.m_scrollY)
  , m_children(other.m_children) {
    if (other.m_map) {
      m_map = std::make_unique<Map>(other.m_map->clone());
    }
  };
  MapInfo& operator=(const MapInfo& other) {
    IModifiable::operator=(other);
    m_expanded = other.m_expanded;
    m_id = other.m_id;
    m_name = other.m_name;
    m_order = other.m_order;
    m_parentId = other.m_parentId;
    m_scrollX = other.m_scrollX;
    m_scrollY = other.m_scrollY;
    if (other.m_map) {
      m_map = std::make_unique<Map>(other.m_map->clone());
    }
    return *this;
  }
  MapInfo(MapInfo&& other) noexcept
  : IModifiable(other)
  , m_expanded(other.m_expanded)
  , m_id(other.m_id)
  , m_name(std::move(other.m_name))
  , m_order(other.m_order)
  , m_parentId(other.m_parentId)
  , m_scrollX(other.m_scrollX)
  , m_scrollY(other.m_scrollY)
  , m_children(std::move(other.m_children)) {
    if (other.m_map) {
      m_map = std::make_unique<Map>(other.m_map->clone());
    }
  };
  MapInfo& operator=(MapInfo&& other) noexcept {
    IModifiable::operator=(std::move(other));
    m_expanded = other.m_expanded;
    m_id = other.m_id;
    m_name = std::move(other.m_name);
    m_order = other.m_order;
    m_parentId = other.m_parentId;
    m_scrollX = other.m_scrollX;
    m_scrollY = other.m_scrollY;
    if (other.m_map) {
      m_map = std::make_unique<Map>(other.m_map->clone());
    }
    return *this;
  }

  [[nodiscard]] int id() const { return m_id; }
  void setId(const int id);
  [[nodiscard]] bool expanded() const { return m_expanded; }
  void setExpanded(const bool expanded) { m_expanded = expanded; }

  [[nodiscard]] const std::string& name() const { return m_name; }
  void setName(const std::string& name);

  [[nodiscard]] int order() const { return m_order; }
  void setOrder(const int order) { m_order = order; }

  [[nodiscard]] int parentId() const { return m_parentId; }
  void setParentId(const int parentId) { m_parentId = parentId; }

  [[nodiscard]] double scrollX() const { return m_scrollX; }
  void setScrollX(const double scrollX) { m_scrollX = scrollX; }

  [[nodiscard]] double scrollY() const { return m_scrollY; }
  void setScrollY(const double scrollY) { m_scrollY = scrollY; }

  nlohmann::ordered_json serializeOldValues() const override { return clone(); }

  std::vector<MapInfo*>& children() { return m_children; }
  [[nodiscard]] const std::vector<MapInfo*>& children() const { return m_children; }

  Map* map(bool loadSync = false);
  [[nodiscard]] const Map* map(bool loadSync = false) const;

  bool mapLoaded() const { return m_map != nullptr; }

  Event* event(const int id) { return m_map ? m_map->event(id) : nullptr; }
  [[nodiscard]] const Event* event(const int id) const { return m_map ? m_map->event(id) : nullptr; }

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

  bool isModified() const override { return IModifiable::isModified() | (m_map ? m_map->isModified() : 0); }

  void closeIfNotModified() {
    if (!isModified()) {
      m_map.reset();
    }
  }

  rpgmutils::signal<void(MapInfo*, Map*)>& mapInfoModified();

private:
  MapInfo(const MapInfo& other, int)
  : IModifiable(other)
  , m_expanded(other.m_expanded)
  , m_id(other.m_id)
  , m_name(other.m_name)
  , m_order(other.m_order)
  , m_parentId(other.m_parentId)
  , m_scrollX(other.m_scrollX)
  , m_scrollY(other.m_scrollY) {};
  friend void recursiveSort(MapInfo& in);
  friend class MapInfos;
  friend class MapInfosSerializer;

  bool m_expanded{};
  int m_id{};
  std::string m_name;
  int m_order{};
  int m_parentId{};
  double m_scrollX{};
  double m_scrollY{};

  std::vector<MapInfo*> m_children;
  std::unique_ptr<Map> m_map;

  std::optional<rpgmutils::signal<void(MapInfo*, Map*)>> m_mapInfoModified;
  void onMapModified(IModifiable* map);
};
void to_json(nlohmann::ordered_json& json, const MapInfo& mapinfo);
void from_json(const nlohmann::ordered_json& json, MapInfo& mapinfo);


code:
#include "Database/MapInfos.hpp"

#include "Database/Database.hpp"
#include "Database/Map.hpp"
#include "Serializable/FileQueue.hpp"
#include "Serializable/MapSerializer.hpp"

#include <fstream>

using json = nlohmann::ordered_json;

MapInfos MapInfos::load(std::string_view filename) {
  std::ifstream file(filename.data());
  json data = json::parse(file);
  MapInfos mapinfos;
  mapinfos.m_mapinfos.reserve(data.size());

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

bool MapInfos::serialize(std::string_view filename) {
  // TODO: Temporary file for atomic writes

  std::ofstream file(filename.data());
  json data{nullptr};

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

  file << data.dump(4);

  return true;
}

void recursiveSort(MapInfo& in) {
  std::ranges::sort(in.m_children, [](const MapInfo* a, const MapInfo* b) { return a->order() < b->order(); });

  for (auto& mapInfo : in.m_children) {
    recursiveSort(*mapInfo);
  }
}

void MapInfos::buildTree(const bool reset) {
  if (reset) {
    for (auto& mapInfo : m_mapinfos) {
      if (!mapInfo) {
        continue;
      }
      mapInfo->m_children.clear();
    }
  }
  for (auto& mapInfo : m_mapinfos) {
    if (!mapInfo || mapInfo->id() == 0) {
      continue;
    }
    if (mapInfo->parentId() >= 0 && mapInfo->parentId() < m_mapinfos.size()) {
      m_mapinfos[mapInfo->parentId()]->m_children.push_back(&mapInfo.value());
    }
  }

  recursiveSort(root());
}

void recursiveRebuildOrdering(MapInfo& in, int& order) {
  in.setOrder(order++);
  for (auto* mapinfo : in.children()) {
    if (mapinfo) {
      recursiveRebuildOrdering(*mapinfo, order);
    }
  }
}

void MapInfos::rebuildOrdering() {
  if (m_mapinfos.empty()) {
    return;
  }

  int order = 0;
  recursiveRebuildOrdering(m_mapinfos[0].value(), order);
}

void MapInfos::loadAllMaps() {
  for (const auto& mapinfo : m_mapinfos) {
    if (mapinfo && mapinfo->id() != 0 && !mapinfo->mapLoaded()) {
      //      if (auto map = Database::instance()->loadMap(mapinfo->id); map.m_isValid) {
      //        mapinfo->m_map = std::make_unique<Map>(map);
      //      }
      FileQueue::instance().enqueue(std::make_shared<MapSerializer>(std::format("data/Map{:03}.json", mapinfo->id()), mapinfo->id()),
                                    [this]<typename T>(T&& handle) { mapLoadCallback(std::forward<T>(handle)); });
    }
  }
}

static std::mutex MapInfosMutex;
void MapInfos::mapLoadCallback(const std::shared_ptr<ISerializable>& data) {
  if (const auto mapSerializable = std::dynamic_pointer_cast<MapSerializer>(data)) {
    std::lock_guard lock(MapInfosMutex);
    auto& mapinfo = m_mapinfos[mapSerializable->mapId()];
    if (const auto& map = mapSerializable->data(); mapinfo && map.isValid()) {
      mapinfo->m_map = std::make_unique<Map>(map);
    }
  }
}

void MapInfos::connectSignals() {
  for (auto& mapinfo : m_mapinfos) {
    if (!mapinfo) {
      continue;
    }
    // Disconnect just in case we already have a connection
    mapinfo->mapInfoModified().disconnect<&MapInfos::onMapInfoModified>(this);
    mapinfo->mapInfoModified().connect<&MapInfos::onMapInfoModified>(this);
  }
}

void MapInfos::onMapInfoModified(MapInfo* mapInfo, Map* map) { emit_signal(modified(), this); }


code:
#pragma once

#include "Database/MapInfo.hpp"

class MapInfos final : public IModifiable {
public:
  friend class MapInfosSerializer;
  static MapInfos load(std::string_view filepath);
  bool serialize(std::string_view filename);

  [[nodiscard]] MapInfo* map(int id) {
    for (auto& item : m_mapinfos) {
      if (item->id() == id) {
        return &item.value();
      }
    }
    return nullptr;
  }

  [[nodiscard]] const MapInfo* map(int id) const {
    for (const auto& set : m_mapinfos) {
      if (set->id() == id) {
        return &set.value();
      }
    }

    return nullptr;
  }

  bool empty() const { return m_mapinfos.empty(); }

  MapInfo& root() { return m_mapinfos[0].value(); }
  const MapInfo& root() const { return m_mapinfos[0].value(); }

  void buildTree(bool reset = false);

  std::vector<std::optional<MapInfo>>& mapInfos() { return m_mapinfos; }
  const std::vector<std::optional<MapInfo>>& mapInfos() const { return m_mapinfos; }

  void setCurrentMap(MapInfo* map) { m_currentMap = map; }
  MapInfo* currentMap() { return m_currentMap; }
  const MapInfo* currentMap() const { return m_currentMap; }

  void loadAllMaps();

  void rebuildOrdering();

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

  void closeAllUnmodified() {
    for (auto& info : m_mapinfos) {
      if (!info) {
        continue;
      }
      info->closeIfNotModified();
    }
  }

  void connectSignals();

private:
  std::vector<std::optional<MapInfo>> m_mapinfos;
  MapInfo* m_currentMap = nullptr;

  void mapLoadCallback(const std::shared_ptr<ISerializable>& data);
  void onMapInfoModified(MapInfo* mapInfo, Map* map);
};