code:
#pragma once

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

class Weapon final : IModifiable {
  friend class WeaponsSerializer;
  friend void to_json(nlohmann::ordered_json& j, const Weapon& w);
  friend void from_json(const nlohmann::ordered_json& j, Weapon& w);

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

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

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

  [[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();
  void setTraits(const std::vector<Trait>& traits);

  [[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);

  std::array<int, 8>& params();
  void setParams(const std::array<int, 8>& params);
  [[nodiscard]] int param(int idx) const;
  void setParam(int idx, int param);
  [[nodiscard]] int price() const;
  void setPrice(int price);

  [[nodiscard]] int wtypeId() const;
  void setWtypeId(int wtypeId);

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

  rpgmutils::signal<void(Weapon*, int)>& idModified();
  rpgmutils::signal<void(Weapon*, int)>& animationIdModified();
  rpgmutils::signal<void(Weapon*, const std::string&)>& descriptionModified();
  rpgmutils::signal<void(Weapon*, int)>& etypeIdModified();
  rpgmutils::signal<void(Weapon*, const std::vector<Trait>&)>& traitsModified();
  rpgmutils::signal<void(Weapon*, int)>& iconIndexModified();
  rpgmutils::signal<void(Weapon*, const std::string&)>& nameModified();
  rpgmutils::signal<void(Weapon*, const std::string&)>& noteModified();
  rpgmutils::signal<void(Weapon*, const std::array<int, 8>&)>& paramsModified();
  rpgmutils::signal<void(Weapon*, int)>& priceModified();
  rpgmutils::signal<void(Weapon*, int)>& wtypeIdModified();

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

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

private:
  int m_id;
  int m_animationId;
  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;
  int m_wtypeId;

  std::optional<int> m_oldid;
  std::optional<int> m_oldanimationId;
  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;
  std::optional<int> m_oldprice;
  std::optional<int> m_oldwtypeId;

  std::optional<rpgmutils::signal<void(Weapon*, int)>> m_idModified;
  std::optional<rpgmutils::signal<void(Weapon*, int)>> m_animationIdModified;
  std::optional<rpgmutils::signal<void(Weapon*, const std::string&)>> m_descriptionModified;
  std::optional<rpgmutils::signal<void(Weapon*, int)>> m_etypeIdModified;
  std::optional<rpgmutils::signal<void(Weapon*, const std::vector<Trait>&)>> m_traitsModified;
  std::optional<rpgmutils::signal<void(Weapon*, int)>> m_iconIndexModified;
  std::optional<rpgmutils::signal<void(Weapon*, const std::string&)>> m_nameModified;
  std::optional<rpgmutils::signal<void(Weapon*, const std::string&)>> m_noteModified;
  std::optional<rpgmutils::signal<void(Weapon*, const std::array<int, 8>&)>> m_paramsModified;
  std::optional<rpgmutils::signal<void(Weapon*, int)>> m_priceModified;
  std::optional<rpgmutils::signal<void(Weapon*, int)>> m_wtypeIdModified;

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

void to_json(nlohmann::ordered_json& j, const Weapon& w);
void from_json(const nlohmann::ordered_json& j, Weapon& w);


code: #include "Database/Weapons.hpp"

repo_name: Antidote/openrpgeditor

license: mit


code:
#pragma once

#include "Database/Weapon.hpp"

class Weapons final : public IModifiable {
public:
  std::vector<Weapon>& weapons() { return m_weapons; }
  [[nodiscard]] const std::vector<Weapon>& weapons() const { return m_weapons; }
  [[nodiscard]] Weapon* weapon(const int id) {
    for (auto& weapon : m_weapons) {
      if (weapon.id() == id && weapon.isValid()) {
        return &weapon;
      }
    }
    return nullptr;
  }

  [[nodiscard]] const Weapon* weapon(const int id) const {
    for (const auto& weapon : m_weapons) {
      if (weapon.id() == id && weapon.isValid()) {
        return &weapon;
      }
    }

    return nullptr;
  }

  std::vector<Weapon>& weaponList() { return m_weapons; }
  [[nodiscard]] const std::vector<Weapon>& weaponList() const { return m_weapons; }

  [[nodiscard]] int count() const { return static_cast<int>(m_weapons.size() - 1); }

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

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

private:
  std::vector<Weapon> m_weapons;
};


code:
#include "Utils/JavaMath.hpp"

#include <cassert>
#include <chrono>
#include <ctime>
#include <random>

namespace Math {
static auto gen = std::mt19937(std::time(nullptr) & 0x7FFFFFFF);
static bool use2k = false;

void use2kRandom(bool use) { use2k = use; }

double random() {
  auto ret = randomInt(0x7FFFFFFF) / static_cast<double>(0x7FFFFFFF);
  return ret;
}

uint32_t randomInt(const uint32_t max) {
  if (!use2k) {
    return std::uniform_int_distribution<uint32_t>(0, max - 1)(gen);
  }

  if (max == 0xFFFFFFFF) {
    return gen() & 0x7FFFFFFF;
  }

  uint32_t rem = -max % max;
  while (true) {
    uint32_t n = static_cast<uint32_t>(gen());
    if (n >= rem) {
      return n % max;
    }
  }
}
double mod(const double x, const int y) { return fmod(fmod(x, y) + y, y); }

void seed(const int seed) { gen = std::mt19937(seed); }

} // namespace Math


code:
#pragma once
#include <cstdint>

namespace Math {
double random();
uint32_t randomInt(uint32_t max);
double mod(double x, int y);
void seed(int seed);
void use2kRandom(bool use);
} // namespace Math


code:
#pragma once
#include "nano_signal_slot.hpp"

namespace rpgmutils {
using SignalPolicy = Nano::ST_Policy_Safe;
template <typename Signature>
using signal = Nano::Signal<Signature, SignalPolicy>;
using observer = Nano::Observer<SignalPolicy>;
} // namespace rpgmutils

template <typename Signal, typename... Args>
static inline void emit_signal(Signal& signal, Args... args) {
  signal.fire(std::forward<Args>(args)...);
}


code:
#pragma once

#include <array> // std::array
#include <string>
#include <string_view>
#include <utility> // std::index_sequence

template <std::size_t... Idxs>
constexpr auto substring_as_array(std::string_view str, std::index_sequence<Idxs...>) {
  return std::array{str[Idxs]..., '\n'};
}

template <typename T>
constexpr auto type_name_array() {
#if defined(__clang__)
  constexpr auto prefix = std::string_view{"[T = "};
  constexpr auto suffix = std::string_view{"]"};
  constexpr auto function = std::string_view{__PRETTY_FUNCTION__};
#elif defined(__GNUC__)
  constexpr auto prefix = std::string_view{"with T = "};
  constexpr auto suffix = std::string_view{"]"};
  constexpr auto function = std::string_view{__PRETTY_FUNCTION__};
#elif defined(_MSC_VER)
  constexpr auto prefix = std::string_view{"type_name_array<"};
  constexpr auto suffix = std::string_view{">(void)"};
  constexpr auto function = std::string_view{__FUNCSIG__};
#else
#error Unsupported compiler
#endif

  constexpr auto start = function.find(prefix) + prefix.size();
  constexpr auto end = function.rfind(suffix);

  static_assert(start < end);

  constexpr auto name = function.substr(start, (end - start));
  return substring_as_array(name, std::make_index_sequence<name.size()>{});
}

template <typename T>
struct type_name_holder {
  static inline constexpr auto value = type_name_array<T>();
};

template <typename T>
constexpr auto type_name() -> std::string_view {
  constexpr auto& value = type_name_holder<T>::value;
  return std::string_view{value.data(), value.size()};
}


code:
#define SDL_MAIN_HANDLED

#include <exception>

#include <iostream>

#if defined(_WIN32) && defined(DEBUG)
#include <fcntl.h>
#include <io.h>
#include <windows.h>
#endif

#include "Core/Application.hpp"
#include "Core/Debug/Instrumentor.hpp"
#include "Core/Log.hpp"
#include <orei18n.hpp>

#include "Database/Globals.hpp"

#if defined(_WIN32) && defined(ZLIB_DEBUG)
// This is needed due to freetype's gzip not defining these
#include <stdlib.h>
#ifndef verbose
#define verbose 0
#endif
extern "C" int z_verbose = verbose;

extern "C" void z_error(char* m) {
  fprintf(stderr, "%s\n", m);
  exit(1);
}

#endif

// template <typename E>
// void translateEnum(bool withValue = false) {
//   std::cout << "enum class " << magic_enum::enum_type_name<E>() << " {" << std::endl;
//   for (const auto& v : magic_enum::enum_values<E>()) {
//     const auto decoName = DecodeEnumName(magic_enum::enum_name(v));
//     const auto undecoName = UndectorateEnumName(magic_enum::enum_name(v));
//     std::cout << std::format("  trENUM(\"{}\", ", decoName);
//     if (decoName != undecoName) {
//       std::cout << std::format("trENUM(\"{}\", ", undecoName);
//     }
//     std::cout << magic_enum::enum_name(v) << ")";
//     if (decoName != undecoName) {
//       std::cout << ")" << std::endl;
//     }
//     if (withValue) {
//       std::cout << " = " << static_cast<int>(v);
//     }
//     std::cout << "," << std::endl;
//   }
//   std::cout << "};" << std::endl;
// }

int main() {
  srand(time(NULL));
#if defined(_WIN32) && defined(DEBUG)
  AllocConsole();

  HANDLE handle_out = GetStdHandle(STD_OUTPUT_HANDLE);
  int hCrt = _open_osfhandle((size_t)handle_out, _O_TEXT);
  FILE* hf_out = _fdopen(hCrt, "w");
  setvbuf(hf_out, NULL, _IONBF, 1);
  *stdout = *hf_out;

  HANDLE handle_in = GetStdHandle(STD_INPUT_HANDLE);
  hCrt = _open_osfhandle((size_t)handle_in, _O_TEXT);
  FILE* hf_in = _fdopen(hCrt, "r");
  setvbuf(hf_in, NULL, _IONBF, 128);
  *stdin = *hf_in;

  HANDLE handle_err = GetStdHandle(STD_ERROR_HANDLE);
  hCrt = _open_osfhandle((size_t)handle_err, _O_TEXT);
  FILE* hf_err = _fdopen(hCrt, "r");
  setvbuf(hf_err, NULL, _IONBF, 128);
  *stderr = *hf_err;
#endif

#ifndef DEBUG
  try {
#endif
    // APP_PROFILE_BEGIN_SESSION_WITH_FILE("App", "profile.json");
    {
      App::Application app;
      app.run();
    }

    // APP_PROFILE_END_SESSION();
#ifndef DEBUG
  } catch (std::exception& e) { APP_ERROR("Main process terminated with: {}", e.what()); }
#endif

  return 0;
}


code:
#include <string>

#include "App/ProjectInfo.hpp"

namespace App {

const char COMPANY_NAMESPACE[] = "@PROJECT_COMPANY_NAMESPACE@";
const char APP_NAME[] = "@CMAKE_PROJECT_NAME@";

}  // namespace App


code:
#pragma once

namespace App {

extern const char COMPANY_NAMESPACE[];
extern const char APP_NAME[];

} // namespace App


code:
# include(${PROJECT_SOURCE_DIR}/cmake/StaticAnalyzers.cmake)

configure_file(App/ProjectInfo.cpp.in App/ProjectInfo.cpp @ONLY)

add_subdirectory(Math)

#add_library(CommonUI INTERFACE #STATIC
#        Core/CommonUI/IWidget.hpp
#)

#target_include_directories(
#        CommonUI
#        PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
#        ${SFML_SOURCE_DIR}/include/SFML
#        ${clip_SOURCE_DIR}
#        ${athena_SOURCE_DIR}/include
#        ${cpuid_SOURCE_DIR}
#        ${CMAKE_SOURCE_DIR}/extern/angelscript/angelscript/include
#        ${CMAKE_SOURCE_DIR}/extern/inih
#        ${CMAKE_SOURCE_DIR}/extern/angelscript/add_on
#        "${CMAKE_SOURCE_DIR}/extern/nano-signal-slot"
#)
#target_compile_features(CommonUI PRIVATE cxx_std_23)
#target_link_libraries(
#        CommonUI
#        PUBLIC fmt::fmt-header-only
#        spdlog::spdlog
#        SDL3::SDL3
#        SDL3_image::SDL3_image
#        imgui-setup
#        magic_enum
#        nlohmann_json::nlohmann_json
#        librpgm
#        freetype
#        clip
#        sfml-audio
#        oremath
#        libcurl
#        cpuid
#        angelscript
#        angelscript_addons
#        athena-core
#        lcf
#        inih
#)

add_library(
        orecore STATIC
        Core/Application.cpp
        Core/Application.hpp
        Core/CommonUI/ActionsEditor.cpp
        Core/CommonUI/ActionsEditor.hpp
        Core/CommonUI/CharacterPicker.cpp
        Core/CommonUI/CharacterPicker.hpp
        Core/CommonUI/ColorFlashPicker.cpp
        Core/CommonUI/ColorFlashPicker.hpp
        Core/CommonUI/ColorTonePicker.cpp
        Core/CommonUI/ColorTonePicker.hpp
        Core/CommonUI/CreateNewProjectDialog.cpp
        Core/CommonUI/CreateNewProjectDialog.hpp
        Core/CommonUI/EffectsEditor.cpp
        Core/CommonUI/EffectsEditor.hpp
        Core/CommonUI/EventCommandEditor.cpp
        Core/CommonUI/EventCommandEditor.hpp
        Core/CommonUI/GameWindowColorPicker.cpp
        Core/CommonUI/GameWindowColorPicker.hpp
        Core/CommonUI/GridAtlasPicker.cpp
        Core/CommonUI/GridAtlasPicker.hpp
        Core/CommonUI/IDialogController.hpp
        Core/CommonUI/ImagePicker.cpp
        Core/CommonUI/ImagePicker.hpp
        Core/CommonUI/ObjectPicker.hpp
        Core/CommonUI/ObjectPicker.inl
        Core/CommonUI/TextEditor.cpp
        Core/CommonUI/TextEditor.hpp
        Core/CommonUI/TilesetPicker.cpp
        Core/CommonUI/TilesetPicker.hpp
        Core/CommonUI/TraitsEditor.cpp
        Core/CommonUI/TraitsEditor.hpp
        Core/CommonUI/VariableSwitchPicker.cpp
        Core/CommonUI/VariableSwitchPicker.hpp
        Core/CommonUI/Directory.cpp
        Core/CommonUI/Directory.hpp
        Core/CommonUI/FacePicker.cpp
        Core/CommonUI/FacePicker.hpp
        Core/DatabaseEditor.cpp
        Core/DatabaseEditor.hpp
        Core/DatabaseEditor/DBActorsTab.cpp
        Core/DatabaseEditor/DBActorsTab.hpp
        Core/DatabaseEditor/DBAnimationsTab.cpp
        Core/DatabaseEditor/DBAnimationsTab.hpp
        Core/DatabaseEditor/DBArmorsTab.cpp
        Core/DatabaseEditor/DBArmorsTab.hpp
        Core/DatabaseEditor/DBClassesTab.cpp
        Core/DatabaseEditor/DBClassesTab.hpp
        Core/DatabaseEditor/DBCommonEventsTab.cpp
        Core/DatabaseEditor/DBCommonEventsTab.hpp
        Core/DatabaseEditor/DBEnemiesTab.cpp
        Core/DatabaseEditor/DBEnemiesTab.hpp
        Core/DatabaseEditor/DBGameConstantsTab.cpp
        Core/DatabaseEditor/DBGameConstantsTab.hpp
        Core/DatabaseEditor/DBItemsTab.cpp
        Core/DatabaseEditor/DBItemsTab.hpp
        Core/DatabaseEditor/DBSkillsTab.cpp
        Core/DatabaseEditor/DBSkillsTab.hpp
        Core/DatabaseEditor/DBStatesTab.cpp
        Core/DatabaseEditor/DBStatesTab.hpp
        Core/DatabaseEditor/DBSystemTab.cpp
        Core/DatabaseEditor/DBSystemTab.hpp
        Core/DatabaseEditor/DBTemplatesTab.cpp
        Core/DatabaseEditor/DBTemplatesTab.hpp
        Core/DatabaseEditor/DBTermsTab.cpp
        Core/DatabaseEditor/DBTermsTab.hpp
        Core/DatabaseEditor/DBTilesetsTab.cpp
        Core/DatabaseEditor/DBTilesetsTab.hpp
        Core/DatabaseEditor/DBTroopsTab.cpp
        Core/DatabaseEditor/DBTroopsTab.hpp
        Core/DatabaseEditor/DBTypesTab.cpp
        Core/DatabaseEditor/DBTypesTab.hpp
        Core/DatabaseEditor/DBWeaponsTab.cpp
        Core/DatabaseEditor/DBWeaponsTab.hpp
        Core/Debug/Instrumentor.hpp
        Core/DownloadManager.cpp
        Core/DownloadManager.cpp
        Core/DownloadManager.hpp
        Core/DownloadManager.hpp
        Core/EventCommands/Dialog_AbortBattle.cpp
        Core/EventCommands/Dialog_AbortBattle.hpp
        Core/EventCommands/Dialog_BattleProcessing.cpp
        Core/EventCommands/Dialog_BattleProcessing.hpp
        Core/EventCommands/Dialog_BreakLoop.cpp
        Core/EventCommands/Dialog_BreakLoop.hpp
        Core/EventCommands/Dialog_ChangeActorImages.cpp
        Core/EventCommands/Dialog_ChangeActorImages.hpp
        Core/EventCommands/Dialog_ChangeArmors.cpp
        Core/EventCommands/Dialog_ChangeArmors.hpp
        Core/EventCommands/Dialog_ChangeBattleBack.cpp
        Core/EventCommands/Dialog_ChangeBattleBack.hpp
        Core/EventCommands/Dialog_ChangeBattleBGM.cpp
        Core/EventCommands/Dialog_ChangeBattleBGM.hpp
        Core/EventCommands/Dialog_ChangeClass.cpp
        Core/EventCommands/Dialog_ChangeClass.hpp
        Core/EventCommands/Dialog_ChangeDefeatME.cpp
        Core/EventCommands/Dialog_ChangeDefeatME.hpp
        Core/EventCommands/Dialog_ChangeEncounter.cpp
        Core/EventCommands/Dialog_ChangeEncounter.hpp
        Core/EventCommands/Dialog_ChangeEnemyHP.cpp
        Core/EventCommands/Dialog_ChangeEnemyHP.hpp
        Core/EventCommands/Dialog_ChangeEnemyMP.cpp
        Core/EventCommands/Dialog_ChangeEnemyMP.hpp
        Core/EventCommands/Dialog_ChangeEnemyState.cpp
        Core/EventCommands/Dialog_ChangeEnemyState.hpp
        Core/EventCommands/Dialog_ChangeEnemyTP.cpp
        Core/EventCommands/Dialog_ChangeEnemyTP.hpp
        Core/EventCommands/Dialog_ChangeEquipment.cpp
        Core/EventCommands/Dialog_ChangeEquipment.hpp
        Core/EventCommands/Dialog_ChangeEXP.cpp
        Core/EventCommands/Dialog_ChangeEXP.hpp
        Core/EventCommands/Dialog_ChangeFormationAccess.cpp
        Core/EventCommands/Dialog_ChangeFormationAccess.hpp
        Core/EventCommands/Dialog_ChangeGold.cpp
        Core/EventCommands/Dialog_ChangeGold.hpp
        Core/EventCommands/Dialog_ChangeHP.cpp
        Core/EventCommands/Dialog_ChangeHP.hpp
        Core/EventCommands/Dialog_ChangeItems.cpp
        Core/EventCommands/Dialog_ChangeItems.hpp
        Core/EventCommands/Dialog_ChangeLevel.cpp
        Core/EventCommands/Dialog_ChangeLevel.hpp
        Core/EventCommands/Dialog_ChangeMapDisplayName.cpp
        Core/EventCommands/Dialog_ChangeMapDisplayName.hpp
        Core/EventCommands/Dialog_ChangeMenuAccess.cpp
        Core/EventCommands/Dialog_ChangeMenuAccess.hpp
        Core/EventCommands/Dialog_ChangeMP.cpp
        Core/EventCommands/Dialog_ChangeMP.hpp
        Core/EventCommands/Dialog_ChangeName.cpp
        Core/EventCommands/Dialog_ChangeName.hpp
        Core/EventCommands/Dialog_ChangeNickname.cpp
        Core/EventCommands/Dialog_ChangeNickname.hpp
        Core/EventCommands/Dialog_ChangeParallax.cpp
        Core/EventCommands/Dialog_ChangeParallax.hpp
        Core/EventCommands/Dialog_ChangeParameter.cpp
        Core/EventCommands/Dialog_ChangeParameter.hpp
        Core/EventCommands/Dialog_ChangePartyMember.cpp
        Core/EventCommands/Dialog_ChangePartyMember.hpp
        Core/EventCommands/Dialog_ChangePlayerFollowers.cpp
        Core/EventCommands/Dialog_ChangePlayerFollowers.hpp
        Core/EventCommands/Dialog_ChangePluginCommand.cpp
        Core/EventCommands/Dialog_ChangePluginCommand.hpp
        Core/EventCommands/Dialog_ChangeProfile.cpp
        Core/EventCommands/Dialog_ChangeProfile.hpp
        Core/EventCommands/Dialog_ChangeSaveAccess.cpp
        Core/EventCommands/Dialog_ChangeSaveAccess.hpp
        Core/EventCommands/Dialog_ChangeSkill.cpp
        Core/EventCommands/Dialog_ChangeSkill.hpp
        Core/EventCommands/Dialog_ChangeState.cpp
        Core/EventCommands/Dialog_ChangeState.hpp
        Core/EventCommands/Dialog_ChangeTileset.cpp
        Core/EventCommands/Dialog_ChangeTileset.hpp
        Core/EventCommands/Dialog_ChangeTP.cpp
        Core/EventCommands/Dialog_ChangeTP.hpp
        Core/EventCommands/Dialog_ChangeTransparency.cpp
        Core/EventCommands/Dialog_ChangeTransparency.hpp
        Core/EventCommands/Dialog_ChangeVehicleBGM.cpp
        Core/EventCommands/Dialog_ChangeVehicleBGM.hpp
        Core/EventCommands/Dialog_ChangeVehicleImage.cpp
        Core/EventCommands/Dialog_ChangeVehicleImage.hpp
        Core/EventCommands/Dialog_ChangeVictoryME.cpp
        Core/EventCommands/Dialog_ChangeVictoryME.hpp
        Core/EventCommands/Dialog_ChangeWeapons.cpp
        Core/EventCommands/Dialog_ChangeWeapons.hpp
        Core/EventCommands/Dialog_ChangeWindowColor.cpp
        Core/EventCommands/Dialog_ChangeWindowColor.hpp
        Core/EventCommands/Dialog_Comment.cpp
        Core/EventCommands/Dialog_Comment.hpp
        Core/EventCommands/Dialog_CommonEvent.cpp
        Core/EventCommands/Dialog_CommonEvent.hpp
        Core/EventCommands/Dialog_ConditionalBranch.cpp
        Core/EventCommands/Dialog_ConditionalBranch.hpp
        Core/EventCommands/Dialog_ControlSelfSwitch.cpp
        Core/EventCommands/Dialog_ControlSelfSwitch.hpp
        Core/EventCommands/Dialog_ControlSwitches.cpp
        Core/EventCommands/Dialog_ControlSwitches.hpp
        Core/EventCommands/Dialog_ControlVariables.cpp
        Core/EventCommands/Dialog_ControlVariables.hpp
        Core/EventCommands/Dialog_EnemyAppear.cpp
        Core/EventCommands/Dialog_EnemyAppear.hpp
        Core/EventCommands/Dialog_EnemyRecoverAll.cpp
        Core/EventCommands/Dialog_EnemyRecoverAll.hpp
        Core/EventCommands/Dialog_EnemyTransform.cpp
        Core/EventCommands/Dialog_EnemyTransform.hpp
        Core/EventCommands/Dialog_EraseEvent.cpp
        Core/EventCommands/Dialog_EraseEvent.hpp
        Core/EventCommands/Dialog_ErasePicture.cpp
        Core/EventCommands/Dialog_ErasePicture.hpp
        Core/EventCommands/Dialog_ExitEventProcessing.cpp
        Core/EventCommands/Dialog_ExitEventProcessing.hpp
        Core/EventCommands/Dialog_FadeinScreen.cpp
        Core/EventCommands/Dialog_FadeinScreen.hpp
        Core/EventCommands/Dialog_FadeoutBGM.cpp
        Core/EventCommands/Dialog_FadeoutBGM.hpp
        Core/EventCommands/Dialog_FadeoutBGS.cpp
        Core/EventCommands/Dialog_FadeoutBGS.hpp
        Core/EventCommands/Dialog_FadeoutScreen.cpp
        Core/EventCommands/Dialog_FadeoutScreen.hpp
        Core/EventCommands/Dialog_FlashScreen.cpp
        Core/EventCommands/Dialog_FlashScreen.hpp
        Core/EventCommands/Dialog_GameData.cpp
        Core/EventCommands/Dialog_GameData.hpp
        Core/EventCommands/Dialog_GameOver.cpp
        Core/EventCommands/Dialog_GameOver.hpp
        Core/EventCommands/Dialog_GatherFollowers.cpp
        Core/EventCommands/Dialog_GatherFollowers.hpp
        Core/EventCommands/Dialog_GetOnOffVehicle.cpp
        Core/EventCommands/Dialog_GetOnOffVehicle.hpp
        Core/EventCommands/Dialog_InputNumber.cpp
        Core/EventCommands/Dialog_InputNumber.hpp
        Core/EventCommands/Dialog_JumpToLabel.cpp
        Core/EventCommands/Dialog_JumpToLabel.hpp
        Core/EventCommands/Dialog_Label.cpp
        Core/EventCommands/Dialog_Label.hpp
        Core/EventCommands/Dialog_Loop.cpp
        Core/EventCommands/Dialog_Loop.hpp
        Core/EventCommands/Dialog_MovePicture.cpp
        Core/EventCommands/Dialog_MovePicture.hpp
        Core/EventCommands/Dialog_NameInputProcessing.cpp
        Core/EventCommands/Dialog_NameInputProcessing.hpp
        Core/EventCommands/Dialog_OpenMenuScreen.cpp
        Core/EventCommands/Dialog_OpenMenuScreen.hpp
        Core/EventCommands/Dialog_OpenSaveScreen.cpp
        Core/EventCommands/Dialog_OpenSaveScreen.hpp
        Core/EventCommands/Dialog_PlayBGM.cpp
        Core/EventCommands/Dialog_PlayBGM.hpp
        Core/EventCommands/Dialog_PlayBGS.cpp
        Core/EventCommands/Dialog_PlayBGS.hpp
        Core/EventCommands/Dialog_PlayME.cpp
        Core/EventCommands/Dialog_PlayME.hpp
        Core/EventCommands/Dialog_PlayMovie.cpp
        Core/EventCommands/Dialog_PlayMovie.hpp
        Core/EventCommands/Dialog_PlaySE.cpp
        Core/EventCommands/Dialog_PlaySE.hpp
        Core/EventCommands/Dialog_RecoverAll.cpp
        Core/EventCommands/Dialog_RecoverAll.hpp
        Core/EventCommands/Dialog_ReplayBGM.cpp
        Core/EventCommands/Dialog_ReplayBGM.hpp
        Core/EventCommands/Dialog_ReturnToTitle.cpp
        Core/EventCommands/Dialog_ReturnToTitle.hpp
        Core/EventCommands/Dialog_RotatePicture.cpp
        Core/EventCommands/Dialog_RotatePicture.hpp
        Core/EventCommands/Dialog_SaveBGM.cpp
        Core/EventCommands/Dialog_SaveBGM.hpp
        Core/EventCommands/Dialog_Script.cpp
        Core/EventCommands/Dialog_Script.hpp
        Core/EventCommands/Dialog_ScrollMap.cpp
        Core/EventCommands/Dialog_ScrollMap.hpp
        Core/EventCommands/Dialog_SelectItem.cpp
        Core/EventCommands/Dialog_SelectItem.hpp
        Core/EventCommands/Dialog_SetEventLocation.cpp
        Core/EventCommands/Dialog_SetEventLocation.hpp
        Core/EventCommands/Dialog_SetMovementRoute.cpp
        Core/EventCommands/Dialog_SetMovementRoute.hpp
        Core/EventCommands/Dialog_SetVehicleLocation.cpp
        Core/EventCommands/Dialog_SetVehicleLocation.hpp
        Core/EventCommands/Dialog_SetWeatherEffect.cpp
        Core/EventCommands/Dialog_SetWeatherEffect.hpp
        Core/EventCommands/Dialog_ShakeScreen.cpp
        Core/EventCommands/Dialog_ShakeScreen.hpp
        Core/EventCommands/Dialog_ShopProcessing.cpp
        Core/EventCommands/Dialog_ShopProcessing.hpp
        Core/EventCommands/Dialog_ShopProcessing_Goods.cpp
        Core/EventCommands/Dialog_ShopProcessing_Goods.hpp
        Core/EventCommands/Dialog_ShowAnimation.cpp
        Core/EventCommands/Dialog_ShowAnimation.hpp
        Core/EventCommands/Dialog_ShowBalloonIcon.cpp
        Core/EventCommands/Dialog_ShowBalloonIcon.hpp
        Core/EventCommands/Dialog_ShowBattleAnimation.cpp
        Core/EventCommands/Dialog_ShowBattleAnimation.hpp
        Core/EventCommands/Dialog_ShowChoice.cpp
        Core/EventCommands/Dialog_ShowChoice.hpp
        Core/EventCommands/Dialog_ShowPicture.cpp
        Core/EventCommands/Dialog_ShowPicture.hpp
        Core/EventCommands/Dialog_ShowScrollingText.cpp
        Core/EventCommands/Dialog_ShowScrollingText.hpp
        Core/EventCommands/Dialog_ShowText.cpp
        Core/EventCommands/Dialog_ShowText.hpp
        Core/EventCommands/Dialog_StopSE.cpp
        Core/EventCommands/Dialog_StopSE.hpp
        Core/EventCommands/Dialog_ControlTimer.cpp
        Core/EventCommands/Dialog_ControlTimer.hpp
        Core/EventCommands/Dialog_TintPicture.cpp
        Core/EventCommands/Dialog_TintPicture.hpp
        Core/EventCommands/Dialog_TintScreen.cpp
        Core/EventCommands/Dialog_TintScreen.hpp
        Core/EventCommands/Dialog_TransferPlayer.cpp
        Core/EventCommands/Dialog_TransferPlayer.hpp
        Core/EventCommands/Dialog_Wait.cpp
        Core/EventCommands/Dialog_Wait.hpp
        Core/EventCommands/IEventDialogController.hpp
        Core/EventCommands/MovementRoute/Dialog_MovementChangeBlendMode.cpp
        Core/EventCommands/MovementRoute/Dialog_MovementChangeBlendMode.hpp
        Core/EventCommands/MovementRoute/Dialog_MovementChangeFrequency.cpp
        Core/EventCommands/MovementRoute/Dialog_MovementChangeFrequency.hpp
        Core/EventCommands/MovementRoute/Dialog_MovementChangeImage.cpp
        Core/EventCommands/MovementRoute/Dialog_MovementChangeImage.hpp
        Core/EventCommands/MovementRoute/Dialog_MovementChangeOpacity.cpp
        Core/EventCommands/MovementRoute/Dialog_MovementChangeOpacity.hpp
        Core/EventCommands/MovementRoute/Dialog_MovementChangeSpeed.cpp
        Core/EventCommands/MovementRoute/Dialog_MovementChangeSpeed.hpp
        Core/EventCommands/MovementRoute/Dialog_MovementJump.cpp
        Core/EventCommands/MovementRoute/Dialog_MovementJump.hpp
        Core/EventCommands/MovementRoute/Dialog_MovementScript.cpp
        Core/EventCommands/MovementRoute/Dialog_MovementScript.hpp
        Core/EventCommands/MovementRoute/Dialog_MovementSwitchOFF.cpp
        Core/EventCommands/MovementRoute/Dialog_MovementSwitchOFF.hpp
        Core/EventCommands/MovementRoute/Dialog_MovementSwitchON.cpp
        Core/EventCommands/MovementRoute/Dialog_MovementSwitchON.hpp
        Core/EventCommands/MovementRoute/Dialog_MovementWait.cpp
        Core/EventCommands/MovementRoute/Dialog_MovementWait.hpp
        Core/EventEditor.cpp
        Core/EventEditor.hpp
        Core/EventEditor/EVPage.cpp
        Core/EventEditor/EVPage.hpp
        Core/EventListView.cpp
        Core/EventListView.hpp
        Core/FirstBootWizard.cpp
        Core/FirstBootWizard.hpp
        Core/FirstBootWizard/IWizardPage.hpp
        Core/FirstBootWizard/ProjectLocationPage.cpp
        Core/FirstBootWizard/ProjectLocationPage.hpp
        Core/FirstBootWizard/RPGMakerLocationAndVersionPage.cpp
        Core/FirstBootWizard/RPGMakerLocationAndVersionPage.hpp
        Core/FirstBootWizard/UISettingsPage.cpp
        Core/FirstBootWizard/UISettingsPage.hpp
        Core/FirstBootWizard/WelcomePage.cpp
        Core/FirstBootWizard/WelcomePage.hpp
        Core/Graphics/CharacterSheet.cpp
        Core/Graphics/CharacterSheet.hpp
        Core/Graphics/CheckerboardTexture.cpp
        Core/Graphics/CheckerboardTexture.hpp
        Core/Graphics/CheckeredCompositeTexture.cpp
        Core/Graphics/CheckeredCompositeTexture.hpp
        Core/Graphics/ColorTonePreview.cpp
        Core/Graphics/ColorTonePreview.hpp
        Core/Graphics/FaceSheet.cpp
        Core/Graphics/FaceSheet.hpp
        Core/Graphics/GameWindowBackground.cpp
        Core/Graphics/GameWindowBackground.hpp
        Core/Graphics/IconSheet.cpp
        Core/Graphics/IconSheet.hpp
        Core/Graphics/Image.cpp
        Core/Graphics/Image.hpp
        Core/Graphics/image_loader.cpp
        Core/Graphics/image_loader.h
        Core/Graphics/RenderImage.cpp
        Core/Graphics/RenderImage.hpp
        Core/Graphics/SideViewBattlerSheet.cpp
        Core/Graphics/SideViewBattlerSheet.hpp
        Core/Graphics/SimpleRect.hpp
        Core/Graphics/stb_image.h
        Core/Graphics/Texture.cpp
        Core/Graphics/Texture.hpp
        Core/Graphics/ParallaxTexture.cpp
        Core/Graphics/ParallaxTexture.hpp
        Core/ImGuiExt/ImGuiNotify.hpp
        Core/ImGuiExt/ImGuiParsedText.cpp
        Core/ImGuiExt/ImGuiParsedText.hpp
        Core/ImGuiExt/ImGuiUtils.cpp
        Core/ImGuiExt/ImGuiUtils.hpp
        Core/ImGuiExt/TextParser.cpp
        Core/ImGuiExt/TextParser.hpp
        Core/Log.cpp
        Core/Log.hpp
        Core/MapEditor.cpp
        Core/MapEditor.hpp
        Core/MapEditor/MapCursor.cpp
        Core/MapEditor/MapCursor.hpp
        Core/MapEditor/MapEvent.cpp
        Core/MapEditor/MapEvent.hpp
        Core/MapEditor/MapProperties.cpp
        Core/MapEditor/MapProperties.hpp
        Core/MapEditor/MapRenderer.cpp
        Core/MapEditor/MapRenderer.hpp
        Core/MapListView.cpp
        Core/MapListView.hpp
        Core/NWJSVersionManager.cpp
        Core/NWJSVersionManager.hpp
        Core/MainWindow.cpp
        Core/MainWindow.hpp
        Core/ResourceManager.cpp
        Core/ResourceManager.hpp
        Core/Resources.hpp
        Core/Settings.cpp
        Core/Settings.hpp
        Core/SettingsDialog.cpp
        Core/SettingsDialog.hpp
        Core/SettingsDialog/GeneralSettingsTab.cpp
        Core/SettingsDialog/GeneralSettingsTab.hpp
        Core/SettingsDialog/RPGMakerLocationAndVersionTab.cpp
        Core/SettingsDialog/RPGMakerLocationAndVersionTab.hpp
        Core/SettingsDialog/ISettingsTab.hpp
        Core/Sound.cpp
        Core/Sound.hpp
        Core/TemplateEditor/Dialog/TemplateName.cpp
        Core/TemplateEditor/Dialog/TemplateName.hpp
        Core/TemplateEditor/Dialog/TemplatesEvent.cpp
        Core/TemplateEditor/Dialog/TemplatesEvent.hpp
        Core/TemplateEditor/TemplatesCommand.cpp
        Core/TemplateEditor/TemplatesCommand.hpp
        Core/TemplateEditor/TemplatesEventProperties.cpp
        Core/TemplateEditor/TemplatesEventProperties.hpp
        Core/TemplateEditor/TemplatesTint.cpp
        Core/TemplateEditor/TemplatesTint.hpp
        Core/TroopsEventEditor/TroopsEVEditor.cpp
        Core/TroopsEventEditor/TroopsEVEditor.hpp
        Core/TroopsEventEditor/TroopsEVPage.cpp
        Core/TroopsEventEditor/TroopsEVPage.hpp
        Core/Tilemap/ITileView.cpp
        Core/Tilemap/ITileView.hpp
        Core/UndoStack.cpp
        Core/UndoStack.hpp
        Core/Window.cpp
        Core/Window.hpp
        Core/Tilemap/TilePalette.cpp
        Core/Tilemap/TilePalette.hpp
        Core/Tilemap/TileRenderHelper.cpp
        Core/Tilemap/TileRenderHelper.hpp
        Core/Graphics/TileMarker.cpp
        Core/Graphics/TileMarker.hpp
        Core/SettingsDialog/UISettingsTab.cpp
        Core/SettingsDialog/UISettingsTab.hpp
        Core/EditorPlugin/EditorPluginManager.cpp
        Core/EditorPlugin/EditorPluginManager.hpp
        Core/DirectoryMonitor.cpp
        Core/DirectoryMonitor.hpp
        Core/Script/ScriptEngine.cpp
        Core/Script/ScriptEngine.hpp
        Core/EditorPlugin/EditorPlugin.cpp
        Core/EditorPlugin/EditorPlugin.hpp
        Core/Script/ByteCodeStream.cpp
        Core/Script/ByteCodeStream.hpp
        Core/Script/Bindings.cpp
        Core/Script/Bindings.hpp
        Core/Tilemap/TilemapView.cpp
        Core/Tilemap/TilemapView.hpp
        Core/CommonUI/SoundPicker.cpp
        Core/CommonUI/SoundPicker.hpp
        Core/ImGuiExt/imgui-knobs.h
        Core/ImGuiExt/imgui-knobs.cpp
        Core/ImGuiExt/imgui_neo_internal.h
        Core/ImGuiExt/imgui_neo_internal.cpp
        Core/ImGuiExt/imgui_neo_sequencer.h
        Core/ImGuiExt/imgui_neo_sequencer.cpp
        Core/DatabaseEditor/DBDocTab.cpp
        Core/DatabaseEditor/DBDocTab.hpp
        Core/EventSearcher.cpp
        Core/EventSearcher.hpp
        Core/ReferenceSearch.cpp
        Core/ReferenceSearch.hpp
        Core/DatabaseEditor/DBMappingsTab.cpp
        Core/DatabaseEditor/DBMappingsTab.hpp
        Core/CommonUI/Preview.cpp
        Core/CommonUI/Preview.hpp
        Core/Interpreter.cpp
        Core/Interpreter.hpp
        Core/LibLCF.cpp
        Core/LibLCF.hpp
        Core/DatabaseEditor/DBLocaleTab.cpp
        Core/DatabaseEditor/DBLocaleTab.hpp

        Core/Script/ImGui/ImGuiMath.cpp
        Core/Script/ImGui/ImGuiMath.hpp
        Core/Script/ImGui/ImGuiCoreOperations.cpp
        Core/Script/ImGui/ImGuiCoreOperations.hpp
        Core/LCF_Importer/LCF_Importer.cpp
        Core/LCF_Importer/LCF_Importer.hpp
        Core/LCF_Importer/LCF_Mapping.cpp
        Core/LCF_Importer/LCF_Mapping.hpp
        Core/EventCommands/MovementRoute/Dialogue_MovementPlaySE.cpp
        Core/EventCommands/MovementRoute/Dialogue_MovementPlaySE.hpp
        Core/CommonUI/GroupBox.cpp
        Core/CommonUI/GroupBox.hpp
        Core/CommonUI/AudioEditor.cpp
        Core/CommonUI/AudioEditor.hpp
        Core/CommonUI/EventTilePicker.cpp
        Core/CommonUI/EventTilePicker.hpp
)

target_include_directories(
        orecore
        PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
        ${SFML_SOURCE_DIR}/include/SFML
        ${clip_SOURCE_DIR}
        ${athena_SOURCE_DIR}/include
        ${cpuid_SOURCE_DIR}
        ${CMAKE_SOURCE_DIR}/extern/angelscript/angelscript/include
        ${CMAKE_SOURCE_DIR}/extern/inih
        ${CMAKE_SOURCE_DIR}/extern/angelscript/add_on
        "${CMAKE_SOURCE_DIR}/extern/nano-signal-slot"
)
target_compile_features(orecore PRIVATE cxx_std_23)
target_link_libraries(
        orecore
        PRIVATE
        fmt::fmt-header-only
        spdlog::spdlog_header_only
        PUBLIC
        # CommonUI
        SDL3::SDL3
        SDL3_image::SDL3_image
        imgui-setup
        magic_enum
        nlohmann_json::nlohmann_json
        librpgm
        freetype
        clip
        sfml-audio
        oremath
        libcurl
        cpuid
        angelscript
        angelscript_addons
        athena-core
        lcf
        inih
)
target_compile_definitions(orecore PUBLIC GETTEXT_DOMAIN="${GETTEXT_DOMAIN}" GETTEXT_OUTPUT_DIR="${GETTEXT_OUTPUT_DIR}")

# Define set of OS specific files to include
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
    target_sources(orecore PRIVATE Platform/Windows/Resources.cpp)
elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
    target_sources(orecore PRIVATE Platform/Mac/Resources.cpp)
elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux")
    target_sources(orecore PRIVATE Platform/Linux/Resources.cpp)
endif ()

if (CMAKE_CXX_COMPILER_ID STREQUAL "GCC")
    target_compile_options(orecore PRIVATE -Wno-deprecated-enum-enum-conversion)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "CLANG")
    target_compile_options(orecore PRIVATE -Wno-deprecated-enum-enum-conversion)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
    target_compile_options(orecore PRIVATE /utf-8)
endif ()

add_executable(${NAME} WIN32 MACOSX_BUNDLE App/Main.cpp App/ProjectInfo.cpp
        App/ProjectInfo.hpp)

if (CMAKE_CXX_COMPILER_ID STREQUAL "GCC")
    target_compile_options(${NAME} PRIVATE -Wno-deprecated-enum-enum-conversion)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "CLANG")
    target_compile_options(${NAME} PRIVATE -Wno-deprecated-enum-enum-conversion)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
    target_compile_options(${NAME} PRIVATE /utf-8)
endif ()

include(${PROJECT_SOURCE_DIR}/src/openrpgeditor/cmake/AppAssets.cmake)
include(${PROJECT_SOURCE_DIR}/src/openrpgeditor/cmake/Packaging.cmake)

target_include_directories(${NAME} PUBLIC)
target_include_directories(
        ${NAME}
        PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
        ${SFML_SOURCE_DIR}/include/SFML
        ${cpuid_SOURCE_DIR}
        ${CMAKE_SOURCE_DIR}/extern/inih
        ${CMAKE_SOURCE_DIR}/extern/angelscript/angelscript/include
        ${CMAKE_SOURCE_DIR}/extern/angelscript/add_on
        "${CMAKE_SOURCE_DIR}/extern/nano-signal-slot")

target_compile_features(${NAME} PRIVATE cxx_std_23)
target_link_libraries(
        ${NAME}
        PRIVATE
        fmt::fmt-header-only
        spdlog::spdlog_header_only
        PUBLIC
        #       CommonUI
        orecore
        SDL3::SDL3
        SDL3_image::SDL3_image
        imgui-setup
        magic_enum
        nlohmann_json::nlohmann_json
        librpgm
        librpgmeventcmds
        librpgmmovementroutecmds
        freetype
        sfml-audio
        cpuid
        angelscript
        angelscript_addons
        athena-core
        inih
)
target_compile_definitions(${NAME} PUBLIC GETTEXT_DOMAIN="${GETTEXT_DOMAIN}" GETTEXT_OUTPUT_DIR="${GETTEXT_OUTPUT_DIR}")

if (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL
        "RelWithDebugInfo")
    add_compile_definitions(ZLIB_DEBUG)
endif ()

if (CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
    add_compile_definitions(_DEBUG DEBUG)
endif ()

if (WIN32)
    add_compile_definitions(_CRT_SECURE_NO_WARNINGS=1)
endif ()


code:
#include "Application.hpp"

#include "App/ProjectInfo.hpp"

#include "Core/Debug/Instrumentor.hpp"
#include "Core/EditorPlugin/EditorPluginManager.hpp"
#include "Core/Log.hpp"
#include "Core/Resources.hpp"
#include "Core/Script/ScriptEngine.hpp"
#include "Core/Settings.hpp"
#include "Core/Window.hpp"
#include "misc/freetype/imgui_freetype.h"

#include "Database/Serializable/FileQueue.hpp"
#include "FirstBootWizard/ProjectLocationPage.hpp"
#include "FirstBootWizard/RPGMakerLocationAndVersionPage.hpp"
#include "FirstBootWizard/UISettingsPage.hpp"
#include "FirstBootWizard/WelcomePage.hpp"
#include "Script/Bindings.hpp"
#include "Utils/JavaMath.hpp"

#include <backends/imgui_impl_sdl3.h>
#include <backends/imgui_impl_sdlrenderer3.h>
#include <IconsFontAwesome6.h>
#include <imgui.h>
#include <imgui_internal.h>
#include <implot.h>
#include <iostream>
#include <libcpuid/libcpuid.h>
#include <memory>
#include <moloader/moloader.hpp>
#include <SDL3/SDL.h>

#include <orei18n.hpp>
#include <string>

namespace App {
constexpr auto SettingsFilename = "config.json"sv;
Application* APP = nullptr;
void Application::loadSettings() { m_settings.load(m_userConfigPath + SettingsFilename.data()); }

Application::Application() {
  const auto curlocale = std::locale("en_US.UTF-8");
  std::cout << "System locale: " << curlocale.name() << std::endl;
  std::locale::global(curlocale);
  if (!cpuid_present()) {
    std::cerr << "CPU does not support this CPU" << std::endl;
  } else {
    std::cout << magic_enum::enum_name<cpu_vendor_t>(cpuid_get_vendor()) << std::endl;
  }
#if !defined(WIN32) && !defined(APPLE)
  // We want the compositor to be enabled
  SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0");
  /* TODO This needs fixes before we can switch to wayland, for now force x11 */
  // SDL_SetHint(SDL_HINT_VIDEO_DRIVER, "wayland");
  SDL_SetHint(SDL_HINT_VIDEO_DRIVER, "x11");
#endif

  const unsigned int init_flags{SDL_INIT_VIDEO | SDL_INIT_GAMEPAD};
  if (!SDL_Init(init_flags)) {
    APP_ERROR("Error: {}\n", SDL_GetError());
    m_exitStatus = ExitStatus::Failure;
  }

  const char* conf_path = SDL_GetPrefPath(COMPANY_NAMESPACE, APP_NAME);
  m_userConfigPath = std::string{conf_path};
  SDL_free((void*)conf_path);

  Math::seed(1);
  Math::use2kRandom(true);

  loadSettings();
  if (m_settings.locale.empty()) {
    m_settings.locale = curlocale.name();
  }
  APP_DEBUG("User config path: {}", m_userConfigPath);
  // TODO: Detect system locale and automatically use that on first boot
  moloader::load(m_userConfigPath + "/locales/" + m_settings.locale + ".mo");

  m_window = std::make_unique<Window>(Window::Settings{kApplicationTitle, m_settings.window.w, m_settings.window.h, m_settings.window.x, m_settings.window.y, m_settings.window.maximized});
  APP = this;
  if (!ScriptEngine::instance()->initialize()) {
    APP_FATAL("Failed to initialize script manager");
    abort();
  }
  EditorPluginManager::instance()->initialize();
}

Application::~Application() {
  ImGui_ImplSDLRenderer3_Shutdown();
  ImGui_ImplSDL3_Shutdown();
  ImPlot::DestroyContext();
  ImGui::DestroyContext();

  SDL_Quit();
}

void Application::updateScale() {
  ImGui::GetStyle() = ImGuiStyle();
  updateGuiColors();

  ImGuiStyle& style = ImGui::GetStyle();

  // General window settings
  style.WindowPadding = ImVec2(6, 6);
  style.WindowRounding = 2.5f;
  style.FrameRounding = 2.5f;
  style.ScrollbarRounding = 2.5f;
  style.GrabRounding = 2.5f;
  style.TabRounding = 2.5f;
  style.WindowBorderSize = 0.5f;
  style.FrameBorderSize = 1.0f;
  style.PopupBorderSize = 1.0f;
  style.PopupRounding = 2.5f;

  // Additional styles
  style.FramePadding = ImVec2(4.0f, 3.f);
  style.ItemSpacing = ImVec2(4.0f, 3.f);
  style.IndentSpacing = 10.0f;
  style.ScrollbarSize = 12.f;
  const auto scale = std::max(SDL_GetWindowPixelDensity(m_window->getNativeWindow()), SDL_GetWindowDisplayScale(m_window->getNativeWindow()));
  style.ScaleAllSizes(scale);
  style.CurveTessellationTol = 0.1f;
  style.CircleTessellationMaxError = 0.1f;
}

void Application::updateGuiColors() {
  ImGuiStyle& style = ImGui::GetStyle();
  ImVec4* colors = style.Colors;

  const auto theme = SDL_GetSystemTheme();

  if (theme == SDL_SYSTEM_THEME_DARK) {
    // Setting the colors
    colors[ImGuiCol_Text] = ImVec4(0.95f, 0.95f, 0.95f, 1.00f);
    colors[ImGuiCol_TextDisabled] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);
    colors[ImGuiCol_WindowBg] = ImVec4(0.13f, 0.13f, 0.13f, 1.00f);
    colors[ImGuiCol_ChildBg] = ImVec4(0.10f, 0.10f, 0.10f, 1.00f);
    colors[ImGuiCol_PopupBg] = ImVec4(0.18f, 0.18f, 0.18f, 1.f);
    colors[ImGuiCol_Border] = ImVec4(0.30f, 0.30f, 0.30f, 1.00f);
    colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
    colors[ImGuiCol_FrameBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);
    colors[ImGuiCol_FrameBgHovered] = ImVec4(0.25f, 0.25f, 0.25f, 1.00f);
    colors[ImGuiCol_FrameBgActive] = ImVec4(0.30f, 0.30f, 0.30f, 1.00f);
    colors[ImGuiCol_TitleBg] = ImVec4(0.10f, 0.10f, 0.10f, 1.00f);
    colors[ImGuiCol_TitleBgActive] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);
    colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.10f, 0.10f, 0.10f, 1.00f);
    colors[ImGuiCol_MenuBarBg] = ImVec4(0.15f, 0.15f, 0.15f, 1.00f);
    colors[ImGuiCol_ScrollbarBg] = ImVec4(0.10f, 0.10f, 0.10f, 1.00f);
    colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);
    colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.25f, 0.25f, 0.25f, 1.00f);
    colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.30f, 0.30f, 0.30f, 1.00f);

    // Accent colors changed to darker olive-green/grey shades
    colors[ImGuiCol_CheckMark] = ImVec4(0.45f, 0.45f, 0.45f, 1.00f);        // Dark gray for check marks
    colors[ImGuiCol_SliderGrab] = ImVec4(0.45f, 0.45f, 0.45f, 1.00f);       // Dark gray for sliders
    colors[ImGuiCol_SliderGrabActive] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f); // Slightly lighter gray when active
    colors[ImGuiCol_Button] = ImVec4(0.25f, 0.25f, 0.25f, 1.00f);           // Button background (dark gray)
    colors[ImGuiCol_ButtonHovered] = ImVec4(0.30f, 0.30f, 0.30f, 1.00f);    // Button hover state
    colors[ImGuiCol_ButtonActive] = ImVec4(0.35f, 0.35f, 0.35f, 1.00f);     // Button active state
    colors[ImGuiCol_Header] = ImVec4(0.40f, 0.40f, 0.40f, 1.00f);           // Dark gray for menu headers
    colors[ImGuiCol_HeaderHovered] = ImVec4(0.35, 0.35, 0.35, 1.00f);       // Slightly darker on hover
    colors[ImGuiCol_HeaderActive] = ImVec4(0.30f, 0.30f, 0.30f, 1.00f);     // Darker gray when active
    colors[ImGuiCol_Separator] = ImVec4(0.30f, 0.30f, 0.30f, 1.00f);        // Separators in dark gray
    colors[ImGuiCol_SeparatorHovered] = ImVec4(0.35f, 0.35f, 0.35f, 1.00f);
    colors[ImGuiCol_SeparatorActive] = ImVec4(0.40f, 0.40f, 0.40f, 1.00f);
    colors[ImGuiCol_ResizeGrip] = ImVec4(0.45f, 0.45f, 0.45f, 1.00f); // Resize grips in dark gray
    colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
    colors[ImGuiCol_ResizeGripActive] = ImVec4(0.55f, 0.55f, 0.55f, 1.00f);
    colors[ImGuiCol_Tab] = ImVec4(0.18f, 0.18f, 0.18f, 1.00f);        // Tabs background
    colors[ImGuiCol_TabHovered] = ImVec4(0.40f, 0.40f, 0.40f, 1.00f); // Darker gray on hover
    colors[ImGuiCol_TabActive] = ImVec4(0.40f, 0.40f, 0.40f, 1.00f);
    colors[ImGuiCol_TabUnfocused] = ImVec4(0.18f, 0.18f, 0.18f, 1.00f);
    colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.40f, 0.40f, 0.40f, 1.00f);
    colors[ImGuiCol_TableHeaderBg] = ImVec4(0.19f, 0.19f, 0.20f, 1.00f);
    colors[ImGuiCol_TableBorderStrong] = ImVec4(0.31f, 0.31f, 0.35f, 1.00f);
    colors[ImGuiCol_TableBorderLight] = ImVec4(0.23f, 0.23f, 0.25f, 1.00f);
    colors[ImGuiCol_DockingPreview] = ImVec4(0.45f, 0.45f, 0.45f, 1.00f); // Docking preview in gray
    colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.18f, 0.18f, 0.18f, 1.00f); // Empty dock background
  } else if (theme == SDL_SYSTEM_THEME_LIGHT) {
    // Setting the colors (Light version)
    colors[ImGuiCol_Text] = ImVec4(0.10f, 0.10f, 0.10f, 1.00f);
    colors[ImGuiCol_TextDisabled] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);
    colors[ImGuiCol_WindowBg] = ImVec4(0.95f, 0.95f, 0.95f, 1.00f); // Light background
    colors[ImGuiCol_ChildBg] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);
    colors[ImGuiCol_PopupBg] = ImVec4(0.98f, 0.98f, 0.98f, 1.00f);
    colors[ImGuiCol_Border] = ImVec4(0.70f, 0.70f, 0.70f, 1.00f);
    colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
    colors[ImGuiCol_FrameBg] = ImVec4(0.85f, 0.85f, 0.85f, 1.00f); // Light frame background
    colors[ImGuiCol_FrameBgHovered] = ImVec4(0.80f, 0.80f, 0.80f, 1.00f);
    colors[ImGuiCol_FrameBgActive] = ImVec4(0.75f, 0.75f, 0.75f, 1.00f);
    colors[ImGuiCol_TitleBg] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);
    colors[ImGuiCol_TitleBgActive] = ImVec4(0.85f, 0.85f, 0.85f, 1.00f);
    colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);
    colors[ImGuiCol_MenuBarBg] = ImVec4(0.95f, 0.95f, 0.95f, 1.00f);
    colors[ImGuiCol_ScrollbarBg] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);
    colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.80f, 0.80f, 0.80f, 1.00f);
    colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.75f, 0.75f, 0.75f, 1.00f);
    colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.70f, 0.70f, 0.70f, 1.00f);

    // Accent colors with a soft pastel gray-green
    colors[ImGuiCol_CheckMark] = ImVec4(0.55f, 0.65f, 0.55f, 1.00f); // Soft gray-green for check marks
    colors[ImGuiCol_SliderGrab] = ImVec4(0.55f, 0.65f, 0.55f, 1.00f);
    colors[ImGuiCol_SliderGrabActive] = ImVec4(0.60f, 0.70f, 0.60f, 1.00f);
    colors[ImGuiCol_Button] = ImVec4(0.85f, 0.85f, 0.85f, 1.00f); // Light button background
    colors[ImGuiCol_ButtonHovered] = ImVec4(0.80f, 0.80f, 0.80f, 1.00f);
    colors[ImGuiCol_ButtonActive] = ImVec4(0.75f, 0.75f, 0.75f, 1.00f);
    colors[ImGuiCol_Header] = ImVec4(0.75f, 0.75f, 0.75f, 1.00f);
    colors[ImGuiCol_HeaderHovered] = ImVec4(0.70f, 0.70f, 0.70f, 1.00f);
    colors[ImGuiCol_HeaderActive] = ImVec4(0.65f, 0.65f, 0.65f, 1.00f);
    colors[ImGuiCol_Separator] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);
    colors[ImGuiCol_SeparatorHovered] = ImVec4(0.65f, 0.65f, 0.65f, 1.00f);
    colors[ImGuiCol_SeparatorActive] = ImVec4(0.70f, 0.70f, 0.70f, 1.00f);
    colors[ImGuiCol_ResizeGrip] = ImVec4(0.55f, 0.65f, 0.55f, 1.00f); // Accent color for resize grips
    colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.60f, 0.70f, 0.60f, 1.00f);
    colors[ImGuiCol_ResizeGripActive] = ImVec4(0.65f, 0.75f, 0.65f, 1.00f);
    colors[ImGuiCol_Tab] = ImVec4(0.85f, 0.85f, 0.85f, 1.00f); // Tabs background
    colors[ImGuiCol_TabHovered] = ImVec4(0.80f, 0.80f, 0.80f, 1.00f);
    colors[ImGuiCol_TabActive] = ImVec4(0.75f, 0.75f, 0.75f, 1.00f);
    colors[ImGuiCol_TabUnfocused] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);
    colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.75f, 0.75f, 0.75f, 1.00f);
    colors[ImGuiCol_DockingPreview] = ImVec4(0.55f, 0.65f, 0.55f, 1.00f); // Docking preview in gray-green
    colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);
  } else {
    colors[ImGuiCol_Text] = ImVec4(0.95f, 0.96f, 0.98f, 1.00f);
    colors[ImGuiCol_TextDisabled] = ImVec4(0.36f, 0.42f, 0.47f, 1.00f);
    colors[ImGuiCol_WindowBg] = ImVec4(0.11f, 0.15f, 0.17f, 1.00f);
    colors[ImGuiCol_ChildBg] = ImVec4(0.15f, 0.18f, 0.22f, 1.00f);
    colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.94f);
    colors[ImGuiCol_Border] = ImVec4(0.08f, 0.10f, 0.12f, 1.00f);
    colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
    colors[ImGuiCol_FrameBg] = ImVec4(0.20f, 0.25f, 0.29f, 1.00f);
    colors[ImGuiCol_FrameBgHovered] = ImVec4(0.12f, 0.20f, 0.28f, 1.00f);
    colors[ImGuiCol_FrameBgActive] = ImVec4(0.09f, 0.12f, 0.14f, 1.00f);
    colors[ImGuiCol_TitleBg] = ImVec4(0.09f, 0.12f, 0.14f, 0.65f);
    colors[ImGuiCol_TitleBgActive] = ImVec4(0.08f, 0.10f, 0.12f, 1.00f);
    colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 0.51f);
    colors[ImGuiCol_MenuBarBg] = ImVec4(0.15f, 0.18f, 0.22f, 1.00f);
    colors[ImGuiCol_ScrollbarBg] = ImVec4(0.02f, 0.02f, 0.02f, 0.39f);
    colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.20f, 0.25f, 0.29f, 1.00f);
    colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.18f, 0.22f, 0.25f, 1.00f);
    colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.09f, 0.21f, 0.31f, 1.00f);
    colors[ImGuiCol_CheckMark] = ImVec4(0.28f, 0.56f, 1.00f, 1.00f);
    colors[ImGuiCol_SliderGrab] = ImVec4(0.28f, 0.56f, 1.00f, 1.00f);
    colors[ImGuiCol_SliderGrabActive] = ImVec4(0.37f, 0.61f, 1.00f, 1.00f);
    colors[ImGuiCol_Button] = ImVec4(0.20f, 0.25f, 0.29f, 1.00f);
    colors[ImGuiCol_ButtonHovered] = ImVec4(0.28f, 0.56f, 1.00f, 1.00f);
    colors[ImGuiCol_ButtonActive] = ImVec4(0.06f, 0.53f, 0.98f, 1.00f);
    colors[ImGuiCol_Header] = ImVec4(0.19f, 0.30f, 0.38f, 0.97f);
    colors[ImGuiCol_HeaderHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.80f);
    colors[ImGuiCol_HeaderActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
    colors[ImGuiCol_Separator] = ImVec4(0.20f, 0.25f, 0.29f, 1.00f);
    colors[ImGuiCol_SeparatorHovered] = ImVec4(0.10f, 0.40f, 0.75f, 0.78f);
    colors[ImGuiCol_SeparatorActive] = ImVec4(0.10f, 0.40f, 0.75f, 1.00f);
    colors[ImGuiCol_ResizeGrip] = ImVec4(0.26f, 0.59f, 0.98f, 0.25f);
    colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);
    colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
    colors[ImGuiCol_Tab] = ImVec4(0.11f, 0.15f, 0.17f, 1.00f);
    colors[ImGuiCol_TabHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.80f);
    colors[ImGuiCol_TabActive] = ImVec4(0.20f, 0.25f, 0.29f, 1.00f);
    colors[ImGuiCol_TabUnfocused] = ImVec4(0.11f, 0.15f, 0.17f, 1.00f);
    colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.11f, 0.15f, 0.17f, 1.00f);
    colors[ImGuiCol_DockingPreview] = ImVec4(0.26f, 0.59f, 0.98f, 0.70f);
    colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);
    colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
    colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
    colors[ImGuiCol_PlotHistogram] = ImVec4(0.06f, 0.53f, 0.98f, 1.00f);
    colors[ImGuiCol_PlotHistogramHovered] = ImVec4(0.28f, 0.56f, 1.00f, 1.00f);
    colors[ImGuiCol_TableHeaderBg] = ImVec4(0.19f, 0.19f, 0.20f, 1.00f);
    colors[ImGuiCol_TableBorderStrong] = ImVec4(0.31f, 0.31f, 0.35f, 1.00f);
    colors[ImGuiCol_TableBorderLight] = ImVec4(0.23f, 0.23f, 0.25f, 1.00f);
    colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
    colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.06f);
    colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
    colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
    colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
    colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
    colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
    colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
  }
}

void Application::updateFonts() {
  ImGuiIO& io{ImGui::GetIO()};
  // ImGUI font
  const float scale = std::max(SDL_GetWindowPixelDensity(m_window->getNativeWindow()), SDL_GetWindowDisplayScale(m_window->getNativeWindow()));
  const float font_size = m_settings.fontSize;
  const float mono_font_size = m_settings.monoFontSize;
  const std::string font_path{Resources::font_path("MPLUSRounded1c-Medium.ttf").generic_string()};
  const std::string font_path_sinhala{Resources::font_path("NotoSansSinhala-Medium.ttf").generic_string()};
  const std::string font_path_jetbrains{Resources::font_path("JetBrainsMono-Medium.ttf").generic_string()};
  const std::string font_path_awesome{Resources::font_path("Font Awesome 6 Free-Solid-900.otf").generic_string()};
  const std::string font_path_mono{Resources::font_path("mplus-1m-regular.ttf").generic_string()};

  static constexpr ImWchar specialChar[] = {
      /* clang-format off */
      0x0001, 0xFFFF,
      0
      /* clang-format on */
  };

  ImVector<ImWchar> ranges;
  ImFontGlyphRangesBuilder builder;
  builder.AddRanges(specialChar);
  builder.AddRanges(io.Fonts->GetGlyphRangesDefault());
  builder.AddRanges(io.Fonts->GetGlyphRangesJapanese());
  builder.BuildRanges(&ranges);
  ImFontConfig config;
  config.RasterizerDensity = scale;
  io.Fonts->Clear();
  config.MergeMode = false;
  m_mainFont = io.FontDefault = io.Fonts->AddFontFromFileTTF(font_path.c_str(), font_size, &config, ranges.Data);
  io.Fonts->Build();
  config.MergeMode = true;
  io.Fonts->AddFontFromFileTTF(font_path_sinhala.c_str(), font_size, &config, ranges.Data);
  io.Fonts->AddFontFromFileTTF(font_path_jetbrains.c_str(), font_size, &config, ranges.Data);
  io.Fonts->AddFontFromFileTTF(font_path_awesome.c_str(), font_size, &config, ranges.Data);
  io.Fonts->Build();

  config = ImFontConfig();
  // config.FontBuilderFlags = ImGuiFreeTypeBuilderFlags_Bold;
  config.MergeMode = false;
  m_monoFont = io.Fonts->AddFontFromFileTTF(font_path_mono.c_str(), mono_font_size, &config, ranges.Data);
  io.Fonts->Build();
  config.MergeMode = true;
  io.Fonts->AddFontFromFileTTF(font_path_sinhala.c_str(), mono_font_size, &config, ranges.Data);
  io.Fonts->AddFontFromFileTTF(font_path_jetbrains.c_str(), mono_font_size, &config, ranges.Data);
  io.Fonts->AddFontFromFileTTF(font_path_awesome.c_str(), mono_font_size, &config, ranges.Data);
  io.Fonts->Build();

  updateScale();
}

void Application::serializeSettings() {
  if (!Database::instance()) {
    size_t len = 0;
    const char* state = ImGui::SaveIniSettingsToMemory(&len);
    m_settings.imguiState = {state, len};
  }
  m_settings.serialize(m_userConfigPath + SettingsFilename.data());
}
ExitStatus Application::run() {
  /* Do an initial clear */
  SDL_SetRenderDrawColor(m_window->getNativeRenderer(), 28, 38, 43, 255);
  SDL_RenderClear(m_window->getNativeRenderer());
  SDL_RenderPresent(m_window->getNativeRenderer());

  if (m_exitStatus == ExitStatus::Failure) {
    return m_exitStatus;
  }

  // Setup Dear ImGui context
  IMGUI_CHECKVERSION();
  ImGui::CreateContext();
  /* Allocate a much larger temp buffer for large strings */
  // ImGui::GetCurrentContext()->TempBuffer.resize(8192 * 3 + 2);
  ImPlot::CreateContext();
  ImGuiIO& io{ImGui::GetIO()};

  io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard | ImGuiConfigFlags_DockingEnable | ImGuiConfigFlags_ViewportsEnable | ImGuiConfigFlags_NavEnableGamepad | ImGuiConfigFlags_DpiEnableScaleFonts |
                    ImGuiConfigFlags_DpiEnableScaleViewports;
  io.ConfigWindowsMoveFromTitleBarOnly = true;
  io.ConfigInputTrickleEventQueue = false;
  // io.ConfigDockingWithShift = true;
  io.IniFilename = nullptr;

  //  Setup Platform/Renderer backends
  ImGui_ImplSDL3_InitForSDLRenderer(m_window->getNativeWindow(), m_window->getNativeRenderer());
  ImGui_ImplSDLRenderer3_Init(m_window->getNativeRenderer());
  SDL_GL_SetSwapInterval(0);

  ImGui::LoadIniSettingsFromMemory(m_settings.imguiState.c_str(), m_settings.imguiState.length());
  updateFonts();

  m_running = true;

  m_project.emplace();
  bool needsInitialProjectLoad =
      !Settings::instance()->lastProject.empty() && std::filesystem::is_regular_file(Settings::instance()->lastProject) &&
      (std::filesystem::path(Settings::instance()->lastProject).extension() == ".rpgproject" || std::filesystem::path(Settings::instance()->lastProject).extension() == ".rmmzproject");
  // Check if we need to run the FirstBoot wizard
  if (!Settings::instance()->ranFirstBootWizard) {
    m_firstBootWizard.emplace();
    m_firstBootWizard->addPage(std::make_shared<WelcomePage>());
    m_firstBootWizard->addPage(std::make_shared<UISettingsPage>());
    m_firstBootWizard->addPage(std::make_shared<RPGMakerLocationAndVersionPage>());
    m_firstBootWizard->addPage(std::make_shared<ProjectLocationPage>());
  }

  SDL_ShowWindow(m_window->getNativeWindow());
  SDL_RaiseWindow(m_window->getNativeWindow());
  SDL_FlashWindow(m_window->getNativeWindow(), SDL_FLASH_UNTIL_FOCUSED);

  float frameTime = 0.f;
  float saveTime = 0.f;
  while (m_running || FileQueue::instance().hasTasks() || m_projectSerialize || m_projectCloseRequest) {
    if (m_fontUpdateRequested) {
      updateFonts();
      m_fontUpdateRequested = !m_fontUpdateDelay;
      m_fontUpdateDelay = 0;
    }
    EditorPluginManager::instance()->initializeAllPlugins();

    SDL_Event event{};
    SDL_PumpEvents();
    while (SDL_PollEvent(&event) == 1) {
      ImGui_ImplSDL3_ProcessEvent(&event);

      if (event.type == SDL_EVENT_QUIT) {
        stop();
      }

      if (event.type == SDL_EVENT_SYSTEM_THEME_CHANGED) {
        updateGuiColors();
      }

      if (event.type >= SDL_EVENT_WINDOW_SHOWN && event.type <= SDL_EVENT_WINDOW_HDR_STATE_CHANGED && event.window.windowID == SDL_GetWindowID(m_window->getNativeWindow())) {
        onEvent(event.window);
      }
    }

    // Start the Dear ImGui frame
    ImGui_ImplSDLRenderer3_NewFrame();
    ImGui_ImplSDL3_NewFrame();

    ImGui::NewFrame();
    ImGui::GetCurrentContext()->CurrentDpiScale = std::max(SDL_GetWindowPixelDensity(m_window->getNativeWindow()), SDL_GetWindowDisplayScale(m_window->getNativeWindow()));

    frameTime += ImGui::GetIO().DeltaTime;
    saveTime += ImGui::GetIO().DeltaTime;

    if (m_firstBootWizard && m_firstBootWizard->draw()) {
      Settings::instance()->ranFirstBootWizard = true;
      m_firstBootWizard.reset();
    }

    if (!m_firstBootWizard) {
      m_project->draw(m_doQuit && m_userClosed, m_projectCloseRequest);

      if (needsInitialProjectLoad) {
        m_project->load(Settings::instance()->lastProject, std::filesystem::path(Settings::instance()->lastProject).remove_filename().generic_string());
        needsInitialProjectLoad = false;
      }
      if (m_doQuit || m_projectCloseRequest) {
        if (m_project && !m_userClosed) {
          const auto [closed, quit, serialize] = m_project->close(true);
          if (closed && quit) {
            m_running = !m_doQuit;
            m_userClosed = true;
          } else if (closed) {
            m_doQuit = false;
            m_running = true;
            m_projectCloseRequest = false;
            m_userClosed = false;
          }
          m_projectSerialize = serialize;
          if (m_projectSerialize) {
            Database::instance()->serializeProject();
          }
        }
        saveTime = 0.f;
      }

      if (!FileQueue::instance().hasTasks() && m_userClosed && m_projectCloseRequest) {
        // Actually close the project to revert active state
        if (m_project) {
          m_project->close();
        }
        if (m_doQuit) {
          m_project.reset();
        }
        m_projectCloseRequest = false;
        m_userClosed = false;
      }

      if (ImGui::GetIO().WantSaveIniSettings && saveTime >= 5.f) {
        serializeSettings();
        saveTime = 0.f;
      }
    }

    // Rendering
    ImGui::Render();

    ImGui::UpdatePlatformWindows();

    SDL_SetRenderDrawColor(m_window->getNativeRenderer(),                       //
                           ImGui::GetStyleColorVec4(ImGuiCol_WindowBg).x * 255, //
                           ImGui::GetStyleColorVec4(ImGuiCol_WindowBg).y * 255, //
                           ImGui::GetStyleColorVec4(ImGuiCol_WindowBg).z * 255, 255);
    SDL_RenderClear(m_window->getNativeRenderer());
    ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), m_window->getNativeRenderer());
    SDL_RenderPresent(m_window->getNativeRenderer());

    // Process file queues at every 1000th of a second to prevent UI stalls
#ifndef DEBUG
    if (frameTime >= (1.f / 1000.f)) {
#else
    if (frameTime >= (1.f / 20.f)) {
#endif
      FileQueue::instance().proc();
      frameTime = 0.f;
    }

    if (io.WantSaveIniSettings && saveTime >= (1.f / 60.f)) {
      serializeSettings();
      saveTime = 0.f;
    }
  }

  EditorPluginManager::instance()->shutdownAllPlugins();
  assert(!FileQueue::instance().hasTasks());
  FileQueue::instance().reset();
  return m_exitStatus;
}

void Application::stop() {
  m_doQuit = true;
  m_projectCloseRequest = true;
}

void Application::onEvent(const SDL_WindowEvent& event) {
  switch (event.type) {
  case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
    return onClose();
  case SDL_EVENT_WINDOW_MINIMIZED:
    return onMinimize();
  case SDL_EVENT_WINDOW_SHOWN:
    return onShown();
  case SDL_EVENT_WINDOW_RESIZED: {
    if (!(SDL_GetWindowFlags(m_window->getNativeWindow()) & SDL_WINDOW_MAXIMIZED)) {
      Settings::instance()->window.w = event.data1;
      Settings::instance()->window.h = event.data2;
    }
    Settings::instance()->window.maximized = SDL_GetWindowFlags(m_window->getNativeWindow()) & SDL_WINDOW_MAXIMIZED;
    break;
  }
  case SDL_EVENT_WINDOW_MOVED: {
    Settings::instance()->window.x = event.data1;
    Settings::instance()->window.y = event.data2;
    Settings::instance()->window.maximized = SDL_GetWindowFlags(m_window->getNativeWindow()) & SDL_WINDOW_MAXIMIZED;
    break;
  }
  case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED:
  case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: {
    updateScale();
    break;
  }
  case SDL_EVENT_WINDOW_MAXIMIZED: {
    Settings::instance()->window.maximized = SDL_GetWindowFlags(m_window->getNativeWindow()) & SDL_WINDOW_MAXIMIZED;
    break;
  }
  default:
    // Do nothing otherwise
    return;
  }
}

void Application::onMinimize() { m_minimized = true; }

void Application::onShown() { m_minimized = false; }

void Application::onClose() { stop(); }

void Application::cancelShutdown() {
  FileQueue::instance().reset();
  m_running = true;
  m_userClosed = false;
  m_projectSerialize = false;
  m_projectCloseRequest = false;
  m_doQuit = false;
}

} // namespace App


code:
#pragma once

#include "FirstBootWizard.hpp"

#include <SDL3/SDL.h>

#include <memory>
#include <string>

#include "Core/Window.hpp"
#include "MainWindow.hpp"

#include <optional>

struct ImFont;
class MainWindow;

namespace App {
#define kApplicationTitle trNOOP("Open RPG Editor")

enum class ExitStatus : int { Success = 0, Failure = 1 };

class Application {
public:
  void loadSettings();
  void serializeSettings();
  explicit Application();
  ~Application();
  void updateScale();
  void updateGuiColors();
  void requestFontUpdate() {
    m_fontUpdateRequested = true;
    m_fontUpdateDelay = 1;
  }

  bool fontUpdateRequestPerformed() const { return m_fontUpdateDelay <= 0 && !m_fontUpdateRequested; }

  Application(const Application&) = delete;
  Application(Application&&) = delete;
  Application& operator=(Application other) = delete;
  Application& operator=(Application&& other) = delete;

  ExitStatus run();
  void stop();

  void onEvent(const SDL_WindowEvent& event);
  void onMinimize();
  void onShown();
  void onClose();

  const std::unique_ptr<Window>& getWindow() const { return m_window; }

  [[nodiscard]] ImFont* getMainFont() const { return m_mainFont; }
  [[nodiscard]] ImFont* getMonoFont() const { return m_monoFont; }

  std::string_view userConfigPath() const { return m_userConfigPath; };

  void cancelShutdown();

  void closeProject() {
    m_userClosed = false;
    m_projectCloseRequest = true;
  }

private:
  void updateFonts();
  ExitStatus m_exitStatus{ExitStatus::Success};
  std::unique_ptr<Window> m_window{nullptr};

  bool m_running{true};
  bool m_minimized{false};
  std::string m_userConfigPath;
  Settings m_settings;
  std::optional<MainWindow> m_project;
  ImFont* m_mainFont{};
  ImFont* m_monoFont{};
  std::optional<FirstBootWizard> m_firstBootWizard;
  bool m_fontUpdateRequested{false};
  int m_fontUpdateDelay = 1;
  bool m_projectCloseRequest{false};
  bool m_userClosed{false};
  bool m_projectSerialize{false};
  bool m_doQuit{false};
};

extern Application* APP;

} // namespace App


code:
#include "Core/CommonUI/ActionsEditor.hpp"
#include "Core/DatabaseEditor.hpp"

#include <format>

#include "imgui.h"

using namespace std::string_view_literals;

static constexpr auto ActionsEditorPopupId = "Actions Edit"sv;

void ActionsEditor::draw(DatabaseEditor* dbEditor) {
  if (m_actions == nullptr) {
    return;
  }

  ImGui::BeginGroup();
  {
    ImGui::SeparatorText("Action Patterns");
    if (ImGui::BeginTable("##orpg_actions_editor", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY,
                          ImVec2{ImGui::GetContentRegionMax().x - 15, ImGui::GetContentRegionMax().y - 600})) {
      ImGui::TableSetupColumn("Skill");
      ImGui::TableSetupColumn("Condition");
      ImGui::TableSetupColumn("Rating");
      ImGui::TableHeadersRow();

      for (auto& action : *m_actions) {
        ImGui::PushID(&action);
        ImGui::TableNextRow();
        if (ImGui::TableNextColumn()) {
          if (ImGui::Selectable(std::format("{}##action_{}", Database::instance()->skillNameOrId(action.skillId()), reinterpret_cast<uintptr_t>(&action)).c_str(), m_selectedAction == &action,
                                ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowDoubleClick)) {
            m_selectedAction = &action;
            if (ImGui::GetMouseClickedCount(ImGuiMouseButton_Left) >= 2) {
              m_isNewEntry = false;
              m_updateTab = true;
              m_tempEffectAssigned = false;
              ImGui::OpenPopup(ActionsEditorPopupId.data());
            }
          }
          if (ImGui::IsKeyPressed(ImGuiKey_Delete) && m_selectedAction == &action && m_deletingAction == nullptr) {
            m_deletingAction = &action;
            m_selectedAction = nullptr;
          }
          drawPopup(dbEditor);
        }
        if (ImGui::TableNextColumn()) {
          switch (action.conditionType()) {
          case ActionCondition::Always: {
            ImGui::Text("Always");
            break;
          }
          case ActionCondition::Turn: {
            ImGui::Text("Turn %i + %i * X", static_cast<int>(action.conditionParam1()), static_cast<int>(action.conditionParam2()));
            break;
          }
          case ActionCondition::HP: {
            ImGui::Text("HP %i%% ~ %i%%", static_cast<int>(action.conditionParam1() * 100), static_cast<int>(action.conditionParam2() * 100));
            break;
          }
          case ActionCondition::MP: {
            ImGui::Text("MP %i%% ~ %i%%", static_cast<int>(action.conditionParam1() * 100), static_cast<int>(action.conditionParam2() * 100));
            break;
          }
          case ActionCondition::State: {
            ImGui::Text("State %s", Database::instance()->stateNameOrId(static_cast<int>(action.conditionParam1())).c_str());
            break;
          }
          case ActionCondition::Party_Level: {
            ImGui::Text("Party Level >= %i", static_cast<int>(action.conditionParam1()));
            break;
          }
          case ActionCondition::Switch: {
            ImGui::Text("%s", Database::instance()->switchNameOrId(static_cast<int>(action.conditionParam1())).c_str());
            break;
          }
          default:
            // ImGui::Text("%i %g", effect.dataId, effect.value1);
            break;
          }
        }
        if (ImGui::TableNextColumn()) {
          ImGui::Text("%i", action.rating());
        }

        ImGui::PopID();
      }

      /* Dummy entry for adding new actions */
      ImGui::TableNextRow();
      if (ImGui::TableNextColumn()) {
        if (ImGui::Selectable("##actions_editor_action_dummy", false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowDoubleClick)) {
          if (ImGui::GetMouseClickedCount(ImGuiMouseButton_Left) >= 2) {
            // m_tempAction = Action();
            m_isNewEntry = true;
            m_updateTab = true;
            m_tempEffectAssigned = false;
            m_selectedAction = &m_tempAction;
            ImGui::OpenPopup(ActionsEditorPopupId.data());
          }
        }
      }
      drawPopup(dbEditor);
      ImGui::EndTable();
    }
  }
  ImGui::EndGroup();

  if (m_deletingAction) {
    std::erase_if(*m_actions, [&](const auto& trait) { return &trait == m_deletingAction; });
    m_deletingAction = nullptr;
  }
}

void ActionsEditor::drawPopup(DatabaseEditor* dbEditor) {
  if (m_selectedAction == nullptr || m_actions == nullptr) {
    return;
  }
  if (ImGui::BeginPopupModal(ActionsEditorPopupId.data(), nullptr, ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize)) {
    if (!m_isNewEntry && !m_tempEffectAssigned) {
      // We're not a new entry so copy our values so we can restore them if we cancel
      m_tempAction = *m_selectedAction;
      m_tempEffectAssigned = true;
    }
    ImGui::BeginGroup();
    {
      ImGui::BeginChild("##orpg_actions_upper_child", ImVec2(0, 0), ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize, ImGuiWindowFlags_NoBackground);
      {
        ImGui::SeparatorText("Skill and Rating");
        ImGui::BeginGroup();
        {
          ImGui::Text("Skill:");
          ImGui::SameLine();
          ImGui::Dummy(ImVec2{160, 0});
          ImGui::SameLine();
          ImGui::Text("Rating:");
          ImGui::EndGroup();
        }
        ImGui::BeginGroup();
        {
          // Skill Button - Picker
          if (ImGui::Button(Database::instance()->skillNameOrId(m_selectedAction->skillId()).c_str(), ImVec2{200, 0})) {
            m_skillPicker.emplace("Skill"sv, Database::instance()->skills.skills(), m_selectedAction->skillId());
            m_skillPicker->setOpen(true);
          }
          ImGui::SameLine();
          ImGui::SetNextItemWidth(150);
          int tmpInt = m_selectedAction->rating();
          if (ImGui::InputInt("##action_rating", &tmpInt)) {
            m_selectedAction->setRating(tmpInt);
          }
          ImGui::SeparatorText("Conditions");
          ImGui::EndGroup();
        }
      }
      ImGui::EndChild();
      ImGui::BeginChild("##orpg_actions_left_child", ImVec2(0, 0), ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize, ImGuiWindowFlags_NoBackground);
      {

        if (ImGui::RadioButton("Always", m_selectedAction->conditionType() == ActionCondition::Always)) {
          if (m_selectedAction->conditionType() != ActionCondition::Always) {
            // TODO
            m_selectedAction->setConditionType(ActionCondition::Always);
          }
        }
        if (ImGui::RadioButton("Turn", m_selectedAction->conditionType() == ActionCondition::Turn)) {
          if (m_selectedAction->conditionType() != ActionCondition::Turn) {
            m_selectedAction->setConditionParam1(0);
            m_selectedAction->setConditionParam2(0);
            m_selectedAction->setConditionType(ActionCondition::Turn);
          }
        }
        if (ImGui::RadioButton("HP", m_selectedAction->conditionType() == ActionCondition::HP)) {
          if (m_selectedAction->conditionType() != ActionCondition::HP) {
            m_selectedAction->setConditionParam1(0);
            m_selectedAction->setConditionParam2(1);
            m_selectedAction->setConditionType(ActionCondition::HP);
          }
        }
        // MP
        if (ImGui::RadioButton("MP", m_selectedAction->conditionType() == ActionCondition::MP)) {
          if (m_selectedAction->conditionType() != ActionCondition::MP) {
            m_selectedAction->setConditionParam1(0);
            m_selectedAction->setConditionParam2(1);
            m_selectedAction->setConditionType(ActionCondition::MP);
          }
        }
        // State
        if (ImGui::RadioButton("State", m_selectedAction->conditionType() == ActionCondition::State)) {
          if (m_selectedAction->conditionType() != ActionCondition::State) {
            m_selectedAction->setConditionParam1(1); // State id
            m_selectedAction->setConditionParam2(0);
            m_selectedAction->setConditionType(ActionCondition::State);
          }
        }
        // Party Level
        if (ImGui::RadioButton("Party Level", m_selectedAction->conditionType() == ActionCondition::Party_Level)) {
          if (m_selectedAction->conditionType() != ActionCondition::Party_Level) {
            m_selectedAction->setConditionParam1(1);
            m_selectedAction->setConditionParam2(0);
            m_selectedAction->setConditionType(ActionCondition::Party_Level);
          }
        }
        // Switch
        if (ImGui::RadioButton("Switch", m_selectedAction->conditionType() == ActionCondition::Switch)) {
          if (m_selectedAction->conditionType() != ActionCondition::Switch) {
            m_selectedAction->setConditionParam1(1);
            m_selectedAction->setConditionParam2(0);
            m_selectedAction->setConditionType(ActionCondition::Switch);
          }
        }
        ImGui::EndChild();
        ImGui::SameLine();
        ImGui::BeginChild("##orpg_actions_right_child", ImVec2(0, 0), ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize, ImGuiWindowFlags_NoBackground);
        {
          // Always
          // => No Widget
          ImGui::NewLine();
          ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 13.f);

          // Turn
          ImGui::BeginDisabled(m_selectedAction->conditionType() != ActionCondition::Turn);
          {
            int tmpInt = m_selectedAction->conditionType() == ActionCondition::Turn ? static_cast<int>(m_selectedAction->conditionParam1()) : 0;
            ImGui::SetNextItemWidth(230);
            if (ImGui::InputInt("##action_turn_value1", &tmpInt)) {
              m_selectedAction->setConditionParam1(static_cast<float>(tmpInt));
            }
            ImGui::SameLine();
            ImGui::Text("~");
            ImGui::SameLine();
            int tmpInt2 = m_selectedAction->conditionType() == ActionCondition::Turn ? static_cast<int>(m_selectedAction->conditionParam2()) : 0;
            ImGui::SameLine();
            ImGui::SetNextItemWidth(230);
            if (ImGui::InputInt("##action_turn_value2", &tmpInt2)) {
              m_selectedAction->setConditionParam2(static_cast<float>(tmpInt2));
            }
          }
          ImGui::EndDisabled();
          // HP
          ImGui::BeginDisabled(m_selectedAction->conditionType() != ActionCondition::HP);
          {
            int tmpInt = m_selectedAction->conditionType() == ActionCondition::HP ? static_cast<int>(m_selectedAction->conditionParam1() / 100) : 0;
            ImGui::SetNextItemWidth(230);
            if (ImGui::SliderInt("##action_hp_value1", &tmpInt, 0, 100, m_selectedAction->conditionType() == ActionCondition::HP ? "%d%%" : "")) {
              m_selectedAction->setConditionParam1(static_cast<float>(tmpInt / 100.0));
            }
            ImGui::SameLine();
            ImGui::Text("~");
            ImGui::SameLine();
            int tmpInt2 = m_selectedAction->conditionType() == ActionCondition::HP ? static_cast<int>(m_selectedAction->conditionParam2() * 100) : 0;
            ImGui::SetNextItemWidth(230);
            if (ImGui::SliderInt("##action_hp_value2", &tmpInt2, 0, 100, m_selectedAction->conditionType() == ActionCondition::HP ? "%d%%" : "")) {
              m_selectedAction->setConditionParam2(static_cast<float>(tmpInt2 / 100.0));
            }
          }
          ImGui::EndDisabled();
          // MP
          ImGui::BeginDisabled(m_selectedAction->conditionType() != ActionCondition::MP);
          {
            int tmpInt = m_selectedAction->conditionType() == ActionCondition::MP ? static_cast<int>(m_selectedAction->conditionParam1() * 100) : 0;
            ImGui::SetNextItemWidth(230);
            if (ImGui::SliderInt("##action_mp_value1", &tmpInt, 0, 100, m_selectedAction->conditionType() == ActionCondition::MP ? "%d%%" : "")) {
              m_selectedAction->setConditionParam1(static_cast<float>(tmpInt / 100.0));
            }
            ImGui::SameLine();
            ImGui::Text("~");
            ImGui::SameLine();
            int tmpInt2 = m_selectedAction->conditionType() == ActionCondition::MP ? static_cast<int>(m_selectedAction->conditionParam2() * 100) : 0;
            ImGui::SameLine();
            ImGui::SetNextItemWidth(230);
            if (ImGui::SliderInt("##action_mp_value2", &tmpInt2, 0, 100, m_selectedAction->conditionType() == ActionCondition::MP ? "%d%%" : "")) {
              m_selectedAction->setConditionParam2(static_cast<float>(tmpInt2 / 100.0));
            }
          }
          ImGui::EndDisabled();
          // State
          ImGui::BeginDisabled(m_selectedAction->conditionType() != ActionCondition::State);
          {
            // State Button - Picker
            if (ImGui::Button(m_selectedAction->conditionType() == ActionCondition::State ? Database::instance()->stateNameOrId(static_cast<int>(m_selectedAction->conditionParam1())).c_str()
                                                                                : "##actions_skill_selection",
                              ImVec2{ImGui::GetContentRegionAvail().x, 0})) {
              m_statePicker.emplace("State"sv, Database::instance()->states.states(), m_selectedAction->conditionParam1());
              m_statePicker->setOpen(true);
            }
          }
          ImGui::EndDisabled();

          // Party Level
          ImGui::BeginDisabled(m_selectedAction->conditionType() != ActionCondition::Party_Level);
          {
            int tmpInt = m_selectedAction->conditionType() == ActionCondition::Party_Level ? static_cast<int>(m_selectedAction->conditionParam1()) : 0;
            ImGui::SetNextItemWidth(230);
            if (ImGui::InputInt("##action_party_level_value1", &tmpInt)) {
              m_selectedAction->setConditionParam1(static_cast<float>(tmpInt));
            }
            ImGui::SameLine();
            ImGui::Text("or above");
          }
          ImGui::EndDisabled();

          // Switch
          ImGui::BeginDisabled(m_selectedAction->conditionType() != ActionCondition::Switch);
          {
            // Switch Button - Picker
            if (ImGui::Button(m_selectedAction->conditionType() == ActionCondition::Switch ? Database::instance()->switchNameOrId(static_cast<int>(m_selectedAction->conditionParam1())).c_str()
                                                                                : "##actions_switch_selection",
                              ImVec2{ImGui::GetContentRegionAvail().x, 0})) {
              picker.emplace("Switches", Database::instance()->system.switches(), m_selectedAction->conditionParam1());
              picker->setOpen(true);
            }
          }
          ImGui::EndDisabled();
        }
        ImGui::EndChild();
        ImGui::EndGroup();
      }
      ImGui::Separator();
      ImGui::BeginGroup();
      {
        if (ImGui::Button("OK")) {
          if (m_isNewEntry) {
            m_actions->push_back(m_tempAction);
          }
          m_isNewEntry = false;
          ImGui::CloseCurrentPopup();
        }
        ImGui::SameLine();
        if (ImGui::Button("Cancel")) {
          if (!m_isNewEntry) {
            // Restore values to their unmodified state;
            *m_selectedAction = m_tempAction;
          }
          m_isNewEntry = false;
          ImGui::CloseCurrentPopup();
        }
      }
      ImGui::EndGroup();
    }

    if (m_skillPicker) {
      if (auto [closed, confirmed] = m_skillPicker->draw(); closed) {
        if (confirmed) {
          m_selectedAction->setSkillId(m_skillPicker->selection());
        }
        m_skillPicker.reset();
      }
    }
    if (m_statePicker) {
      if (auto [closed, confirmed] = m_statePicker->draw(); closed) {
        if (confirmed) {
          m_selectedAction->setConditionParam1(static_cast<float>(m_statePicker->selection()));
        }
        m_statePicker.reset();
      }
    }
    if (picker) {
      auto [closed, confirmed] = picker->draw();
      if (closed) {
        if (confirmed) {
          m_selectedAction->setConditionParam1(static_cast<float>(picker->selection()));
        }
        picker.reset();
      }
    }
    ImGui::EndPopup();
  }
  m_updateTab = false;
}


code:
#pragma once

#include "Core/CommonUI/ObjectPicker.hpp"
#include "Core/CommonUI/VariableSwitchPicker.hpp"
#include "Database/CommonEvents.hpp"
#include "Database/Enemies.hpp"
#include "Database/Skills.hpp"
#include "Database/States.hpp"

#include <vector>

class Action;
struct DatabaseEditor;

struct ActionsEditor {
  void setActions(std::vector<Enemy::Action>* actions) { m_actions = actions; }
  void draw(DatabaseEditor* dbEditor);

private:
  void drawPopup(DatabaseEditor* dbEditor);
  std::vector<Enemy::Action>* m_actions = nullptr;
  Enemy::Action* m_selectedAction = nullptr;
  bool m_updateTab{false};
  bool m_isNewEntry{false};
  bool m_tempEffectAssigned{false};
  Enemy::Action m_tempAction;
  Enemy::Action* m_deletingAction = nullptr;
  std::optional<ObjectPicker<State>> m_statePicker;
  std::optional<ObjectPicker<Skill>> m_skillPicker;
  std::optional<VariableSwitchPicker> picker;
};


code:
#include "Core/CommonUI/AudioEditor.hpp"

#include "Core/ImGuiExt/imgui-knobs.h"
#include "Core/ImGuiExt/ImGuiUtils.hpp"
#include "Core/Log.hpp"
#include "imgui.h"
#include "imgui_internal.h"

#include <IconsFontAwesome6.h>
using namespace std::string_view_literals;
std::tuple<bool, bool> AudioEditor::draw(const std::string_view title, const std::string_view className) {
  if (m_open) {
    ImGui::OpenPopup(std::format("###{}", className).c_str());
  }

  ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
  const auto maxSize = ImVec2{(ImGui::CalcTextSize("#").x * 60) + (ImGui::GetStyle().FramePadding.x * 4), (ImGui::GetTextLineHeightWithSpacing() * 30) + (ImGui::GetStyle().FramePadding.y * 4)};
  ImGui::SetNextWindowSize(maxSize, ImGuiCond_Appearing);
  ImGui::SetNextWindowSizeConstraints(maxSize, {FLT_MAX, FLT_MAX});
  if (ImGui::BeginPopupModal(std::format("{}###{}", title, className).c_str(), &m_open, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_AlwaysAutoResize)) {
    ImGui::BeginHorizontal("##audioRenderer_layout", ImGui::GetContentRegionAvail());
    {
      if (ImGui::BeginTable("##bgm_audio_contents", 1, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY,
                            {ImGui::GetContentRegionAvail().x * .75f, ImGui::GetContentRegionAvail().y})) {

        ImGui::TableSetupScrollFreeze(1, 0);
        ImGui::TableSetupColumn(trNOOP("File"));
        ImGui::TableNextRow();
        ImGui::TableNextColumn();

        ImGui::BeginDisabled(m_audioDir.value().isParentDirectory());
        if (ImGui::Selectable("\u21B0 ..", false, static_cast<int>(ImGuiSelectableFlags_SelectOnNav) | ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_SelectOnClick)) {
          if (ImGui::GetMouseClickedCount(ImGuiMouseButton_Left) >= 2) {
            m_audioDir.value().moveUp();
            m_audios = m_audioDir->getDirectoryContents();
            m_folders = m_audioDir.value().getDirectories();
            m_selected = 0;
            m_selectedFolder = -1;
          }
        }
        ImGui::EndDisabled();
        for (int i = 0; i < m_folders.size(); ++i) {
          const auto& folderName = std::format("{} {}", ICON_FA_FOLDER_OPEN, m_folders[i]);
          if (ImGui::Selectable(folderName.c_str(), m_selectedFolder == i,
                                static_cast<int>(ImGuiSelectableFlags_SelectOnNav) | ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_SelectOnClick)) {
            m_selectedFolder = i;
          }
          if (m_selectedFolder == i && ImGui::GetMouseClickedCount(ImGuiMouseButton_Left) >= 2) {
            m_audioDir->setDirectory(i);
            m_folders = m_audioDir.value().getDirectories();
            m_audios = m_audioDir.value().getDirectoryContents();
            m_selected = 0;
            m_selectedFolder = -1;
          }
        }

        if (ImGui::SelectableWithBorder(trNOOP("None"), m_selected == 0, ImGuiSelectableFlags_AllowOverlap | ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowDoubleClick)) {
          m_selected = 0;
          m_audio.setName("");
        }

        for (int n = 0; n < m_audios.size(); n++) {
          ImGui::TableNextColumn();
          const bool isSelected = (m_selected == n + 1);
          if (ImGui::SelectableWithBorder(m_audios.at(n).c_str(), isSelected, ImGuiSelectableFlags_AllowOverlap | ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowDoubleClick)) {
            m_selected = n + 1;
            m_audio.setName(m_audioDir.value().isParentDirectory() ? m_audios.at(m_selected - 1) : m_audioDir.value().getPathPrefix() + '/' + m_audios.at(m_selected - 1));
            if (ImGui::GetMouseClickedCount(ImGuiMouseButton_Left) >= 2) {
              playAudio(m_audio.name().c_str());
            }
            if (isSelected)
              ImGui::SetItemDefaultFocus();
          }
        }
        ImGui::EndTable();
      }
      ImGui::Spring();
      ImGui::BeginVertical("##audioRenderer_buttons", {-1, -1});
      {
        ImGui::BeginHorizontal("##audioRenderer_play_stop", {-1, -1});
        {
          ImGui::Spring(0.5f);
          if (const auto ret = ImGui::ButtonGroup("##audioRenderer_play_stop", {ICON_FA_PLAY, ICON_FA_STOP}); ret == 0 && m_selected > 0) {
            playAudio(m_audio.name().c_str());
          } else if (ret == 1) {
            stopAudio();
          }
          ImGui::Spring(0.5f);
        }
        ImGui::EndHorizontal();

        ImGui::Separator();
        int volume = m_audio.volume();
        if (ImGuiKnobs::KnobInt(trNOOP("Volume"), &volume, 0, 100, 1, "%d%%", ImGuiKnobVariant_WiperDot, ImGui::GetContentRegionAvail().x, ImGuiKnobFlags_AlwaysClamp)) {
          m_audio.setVolume(volume);
          setVolume(m_audio.volume());
        }
        ImGui::Separator();
        int pitch = m_audio.pitch();
        if (ImGuiKnobs::KnobInt(trNOOP("Pitch"), &pitch, 50, 150, 1, "%d%%", ImGuiKnobVariant_WiperDot, ImGui::GetContentRegionAvail().x, ImGuiKnobFlags_AlwaysClamp)) {
          m_audio.setPitch(pitch);
          setPitch(m_audio.pitch());
        }
        ImGui::Separator();
        int pan = m_audio.pan();
        if (ImGuiKnobs::KnobInt(trNOOP("Pan"), &pan, -100, 100, 1, "%d", ImGuiKnobVariant_WiperDot, ImGui::GetContentRegionAvail().x, ImGuiKnobFlags_AlwaysClamp)) {
          m_audio.setPan(pan);
          setPanning(m_audio.pan());
        }
        ImGui::Spring();
        ImGui::SeparatorEx(ImGuiSeparatorFlags_Horizontal, ImGui::GetDPIScaledValue(1.5));
        ImGui::BeginHorizontal("##audioRenderer_confirm", {-1, -1});
        {
          ImGui::Spring(0.5f);
          if (const auto ret = ImGui::ButtonGroup("##change_hp_dialog_buttons", {trNOOP("OK"), trNOOP("Cancel")}); ret == 0) {
            m_confirmed = true;
            m_open = false;
          } else if (ret == 1) {
            m_confirmed = false;
            m_open = false;
          }
          ImGui::Spring(0.5f);
        }
        ImGui::EndHorizontal();
        ImGui::EndVertical();
      }
    }
    ImGui::EndHorizontal();
    ImGui::EndPopup();
  }
  return {!m_open, m_confirmed};
}


code:
#pragma once
#include "Core/CommonUI/Directory.hpp"
#include "Core/Log.hpp"
#include "Core/ResourceManager.hpp"
#include "Core/Settings.hpp"
#include "Core/Sound.hpp"
#include "Database/Database.hpp"
#include "Database/Globals.hpp"
#include <iostream>

namespace fs = std::filesystem;
struct AudioEditor {
  enum class AudioType {
    SE,
    BGM,
    ME,
    BGS,
    Vocals,
  };
  AudioEditor() = delete;
  explicit AudioEditor(const EventCode code, Audio& audio)
  : m_audio(audio) {
    if (code == EventCode::Play_SE) {
      m_audioDir.emplace("audio/se", ".ogg", m_audio.name());
      m_audioType = AudioType::SE;
    } else if (code == EventCode::Play_BGM || code == EventCode::Change_Battle_BGM || code == EventCode::Change_Vehicle_BGM) {
      m_audioDir.emplace("audio/bgm", ".ogg", m_audio.name());
      m_audioType = AudioType::BGM;
    } else if (code == EventCode::Change_Defeat_ME || code == EventCode::Change_Victory_ME || code == EventCode::Play_ME) {
      m_audioDir.emplace("audio/me", ".ogg", m_audio.name());
      m_audioType = AudioType::ME;
    } else if (code == EventCode::Play_BGS) {
      m_audioDir.emplace("audio/bgs", ".ogg", m_audio.name());
      m_audioType = AudioType::BGS;
    } else {
      APP_INFO("Audio code not implemented");
    }
    m_audios = m_audioDir.value().getDirectoryContents();
    m_folders = m_audioDir.value().getDirectories();
    std::string audioName = m_audioDir->getFileName(m_audio.name());
    for (int i = 0; i < m_audios.size(); ++i) {
      if (audioName == m_audios[i]) {
        m_selected = i;
      }
    }
  }
  std::tuple<bool, bool> draw(std::string_view title, std::string_view className);

  void setOpen(const bool open) {
    m_open = open;
    m_confirmed = false;
  }

private:
  bool m_confirmed{false};
  bool m_open{false};
  AudioType m_audioType;

  int m_selected = 0;
  Audio& m_audio;

  Sound m_sound;

  std::tuple<bool, bool> result;
  std::vector<std::string> m_audios;

  int m_selectedFolder{-1};
  std::optional<Directory> m_audioDir;
  std::vector<std::string> m_folders;

  bool playAudio(const std::string& path) {
    // Load and play music
    switch (m_audioType) {
    case AudioType::SE:
      m_sound = Sound(ResourceManager::instance()->loadSE(path));
      break;
    case AudioType::BGM:
      m_sound = Sound(ResourceManager::instance()->loadBGM(path));
      break;
    case AudioType::ME:
      m_sound = Sound(ResourceManager::instance()->loadME(path));
      break;
    case AudioType::BGS:
      m_sound = Sound(ResourceManager::instance()->loadBGS(path));
      break;
    case AudioType::Vocals:
      // TODO
      break;
    }

    setVolume(m_audio.volume());
    setPanning(m_audio.pan());
    setPitch(m_audio.pitch());
    m_sound.play();
    return true;
  }
  void setVolume(const int volume) {
    m_sound.setVolume(static_cast<float>(volume)); // 0% to 100%
  }
  void setPanning(const int value) { m_sound.setPan(static_cast<float>(value) / 100.0f); }
  void setPitch(const int value) { m_sound.setPitch(value / 100.f); }
  void stopAudio() { m_sound.stop(); }
};