New JSON config format

This commit is contained in:
GriffinR
2026-02-06 23:47:06 -05:00
parent e3e42f39f0
commit 3571b4ea4b
56 changed files with 2005 additions and 1883 deletions

View File

@@ -17,8 +17,19 @@
#include <QColorSpace>
#include <set>
#include "converter.h"
#include "events.h"
#include "gridsettings.h"
#include "scriptsettings.h"
#include "log.h"
#include "basegame.h"
#include "orderedset.h"
#include "block.h"
// TODO: Go through and re-test the default window geometries.
// TODO: Value validation? Make sure anything no longer validated can't cause problems.
// TODO: Documentation
// TODO: File splits
extern const QVersionNumber porymapVersion;
@@ -26,8 +37,6 @@ extern const QVersionNumber porymapVersion;
#define GBA_H_DIST_TO_CENTER ((240-16)/2)
#define GBA_V_DIST_TO_CENTER ((160-16)/2)
#define CONFIG_BACKWARDS_COMPATABILITY
enum ScriptAutocompleteMode {
MapOnly,
MapAndCommon,
@@ -37,159 +46,241 @@ enum ScriptAutocompleteMode {
class KeyValueConfigBase
{
public:
bool save();
bool load(const QString &dir = QString());
void setRoot(const QString &dir);
QString root() const { return m_root; }
QString filepath() const { return m_filepath; }
QString filename() const { return m_filename; }
explicit KeyValueConfigBase(const QString &filename)
explicit KeyValueConfigBase(const QString& filename)
: m_root(QString()),
m_filename(filename),
m_filepath(filename)
{ };
virtual ~KeyValueConfigBase() {};
virtual void reset() = 0;
protected:
virtual void parseConfigKeyValue(QString key, QString value) = 0;
virtual QMap<QString, QString> getKeyValueMap() = 0;
virtual void init() = 0;
virtual void setUnreadKeys() = 0;
static bool getConfigBool(const QString &key, const QString &value);
static int getConfigInteger(const QString &key, const QString &value, int min = INT_MIN, int max = INT_MAX, int defaultValue = 0);
static uint32_t getConfigUint32(const QString &key, const QString &value, uint32_t min = 0, uint32_t max = UINT_MAX, uint32_t defaultValue = 0);
static QColor getConfigColor(const QString &key, const QString &value, const QColor &defaultValue = QColor(Qt::black));
static QString toConfigColor(const QColor &color);
// Writes the contents of the config to disk.
// Returns true if saving was successful, false otherwise.
virtual bool save();
// Loads the contents of the config from disk.
// The file to load from depends on the file name given to
// the constructor, and the root given to setRoot, if any.
// A successful load includes initializing an empty or non-existing file.
virtual bool load();
virtual QJsonObject toJson() const;
virtual void loadFromJson(const QJsonObject& obj);
void setRoot(const QString& dir);
QString root() const { return m_root; }
QString filepath() const { return m_filepath; }
QString filename() const { return m_filename; }
protected:
virtual void initializeFromEmpty() {};
virtual QJsonObject getDefaultJson() const { return QJsonObject(); }
struct FieldManager {
QJsonValue (*get)(const KeyValueConfigBase*);
QStringList (*set)(KeyValueConfigBase*, const QJsonValue&);
};
template <typename ConfigType, auto Member>
static FieldManager makeFieldManager() {
// Deduce the type from the pointer to the member variable.
using T = std::remove_reference_t<decltype(std::declval<ConfigType>().*Member)>;
return FieldManager{
.get = [](const KeyValueConfigBase* base) -> QJsonValue {
auto c = static_cast<const ConfigType*>(base);
return Converter<T>::toJson(c->*Member);
},
.set = [](KeyValueConfigBase* base, const QJsonValue& json) {
QStringList errors;
const T value = Converter<T>::fromJson(json, &errors);
if (errors.isEmpty()) static_cast<ConfigType*>(base)->*Member = value;
return errors;
}
};
}
virtual const QHash<QString,FieldManager>& registeredFields() const {
static QHash<QString,FieldManager> empty;
return empty;
}
virtual bool parseJsonKeyValue(const QString& key, const QJsonValue& value);
virtual bool parseLegacyKeyValue(const QString& , const QString& ) {return false;}
QString m_root;
QString m_filename;
QString m_filepath;
private:
bool loadLegacy();
// TODO: Make this setting accessible somewhere
bool m_saveAllFields = true;
};
class PorymapConfig: public KeyValueConfigBase
{
public:
PorymapConfig();
virtual void reset() override;
void addRecentProject(QString project);
void setRecentProjects(QStringList projects);
QString getRecentProject();
QStringList getRecentProjects();
void setMainGeometry(QByteArray, QByteArray, QByteArray, QByteArray, QByteArray);
void setTilesetEditorGeometry(QByteArray, QByteArray, QByteArray);
void setPaletteEditorGeometry(QByteArray, QByteArray);
void setRegionMapEditorGeometry(QByteArray, QByteArray);
void setProjectSettingsEditorGeometry(QByteArray, QByteArray);
void setCustomScriptsEditorGeometry(QByteArray, QByteArray);
QMap<QString, QByteArray> getMainGeometry();
QMap<QString, QByteArray> getTilesetEditorGeometry();
QMap<QString, QByteArray> getPaletteEditorGeometry();
QMap<QString, QByteArray> getRegionMapEditorGeometry();
QMap<QString, QByteArray> getProjectSettingsEditorGeometry();
QMap<QString, QByteArray> getCustomScriptsEditorGeometry();
PorymapConfig() : KeyValueConfigBase(QStringLiteral("settings.json")) {
setRoot(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
// Initialize defaults not available at compile time.
this->mapListFont = defaultMapListFont();
}
virtual bool save() override;
virtual void loadFromJson(const QJsonObject& obj) override;
void addRecentProject(const QString& project);
void setRecentProjects(const QStringList& projects);
QString getRecentProject() const;
const QStringList& getRecentProjects() const;
void saveGeometry(const QWidget* widget, const QString& keyPrefix = QString(), bool recursive = true);
bool restoreGeometry(QWidget* widget, const QString& keyPrefix = QString(), bool recursive = true) const;
static QFont defaultMapListFont() { return QFontDatabase::systemFont(QFontDatabase::FixedFont); }
bool reopenOnLaunch;
bool projectManuallyClosed;
int mapListTab;
bool mapListEditGroupsEnabled;
QMap<int, bool> mapListHideEmptyEnabled;
bool mapListLayoutsSorted;
bool mapListLocationsSorted;
bool prettyCursors;
bool mirrorConnectingMaps;
bool showDiveEmergeMaps;
int diveEmergeMapOpacity;
int diveMapOpacity;
int emergeMapOpacity;
int collisionOpacity;
int collisionZoom;
int metatilesZoom;
int tilesetEditorMetatilesZoom;
int tilesetEditorTilesZoom;
Qt::Orientation tilesetEditorLayerOrientation;
bool showPlayerView;
bool showCursorTile;
bool showBorder;
bool showGrid;
bool showTilesetEditorMetatileGrid;
bool showTilesetEditorLayerGrid;
bool showTilesetEditorDivider;
bool showTilesetEditorRawAttributes;
bool showPaletteEditorUnusedColors;
bool monitorFiles;
bool tilesetCheckerboardFill;
bool newMapHeaderSectionExpanded;
QString theme;
bool reopenOnLaunch = true;
bool projectManuallyClosed = false;
int mapListTab = 0;
bool mapListEditGroupsEnabled = false;
OrderedSet<int> mapListTabsHidingEmptyFolders;
bool mapListLayoutsSorted = true;
bool mapListLocationsSorted = true;
bool prettyCursors = true;
bool mirrorConnectingMaps = true;
bool showDiveEmergeMaps = false;
int diveEmergeMapOpacity = 30;
int diveMapOpacity = 15;
int emergeMapOpacity = 15;
int collisionOpacity = 50;
int collisionZoom = 30;
int metatilesZoom = 30;
int tilesetEditorMetatilesZoom = 30;
int tilesetEditorTilesZoom = 30;
Qt::Orientation tilesetEditorLayerOrientation = Qt::Vertical;
bool showPlayerView = false;
bool showCursorTile = true;
bool showBorder = true;
bool showGrid = false;
bool showTilesetEditorMetatileGrid = false;
bool showTilesetEditorLayerGrid = true;
bool showTilesetEditorDivider = true;
bool showTilesetEditorRawAttributes = false;
bool showPaletteEditorUnusedColors = false;
bool monitorFiles = true;
bool tilesetCheckerboardFill = true;
bool newMapHeaderSectionExpanded = false;
bool displayIdsHexadecimal = true;
QString theme = QStringLiteral("default");
QString wildMonChartTheme;
QString textEditorOpenFolder;
QString textEditorGotoLine;
int paletteEditorBitDepth;
int projectSettingsTab;
ScriptAutocompleteMode scriptAutocompleteMode;
bool warpBehaviorWarningDisabled;
bool eventDeleteWarningDisabled;
bool eventOverlayEnabled;
bool checkForUpdates;
bool showProjectLoadingScreen;
int paletteEditorBitDepth = 24;
int projectSettingsTab = 0;
ScriptAutocompleteMode scriptAutocompleteMode = ScriptAutocompleteMode::MapOnly;
bool warpBehaviorWarningDisabled = false;
bool eventDeleteWarningDisabled = false;
bool eventOverlayEnabled = false;
bool checkForUpdates = true;
bool showProjectLoadingScreen = true;
QDateTime lastUpdateCheckTime;
QVersionNumber lastUpdateCheckVersion;
QMap<QUrl, QDateTime> rateLimitTimes;
QGraphicsPixmapItem::ShapeMode eventSelectionShapeMode;
QByteArray wildMonChartGeometry;
QByteArray newMapDialogGeometry;
QByteArray newLayoutDialogGeometry;
bool shownInGameReloadMessage;
QGraphicsPixmapItem::ShapeMode eventSelectionShapeMode = QGraphicsPixmapItem::MaskShape;
bool shownInGameReloadMessage = false;
GridSettings gridSettings;
// Prefer over QSet to prevent shuffling elements when writing the config file.
std::set<LogType> statusBarLogTypes;
OrderedSet<LogType> statusBarLogTypes = { LogType::LOG_ERROR, LogType::LOG_WARN };
QFont applicationFont;
QFont mapListFont;
int imageExportColorSpaceId;
#ifdef Q_OS_MACOS
// Since the release of the Retina display, Apple products use the Display P3 color space by default.
// If we don't use this for exported images (which by default will either have no color space or the sRGB
// color space) then they may appear to have different colors than the same image displayed in Porymap.
int imageExportColorSpaceId = static_cast<int>(QColorSpace::DisplayP3);
#else
// As of writing Qt has no way to get a reasonable color space from the user's environment,
// so we export images without one and let them handle it.
int imageExportColorSpaceId = 0;
#endif
QMap<QString,QString> trustedScriptHashes;
#define REGISTER(key, member) {QStringLiteral(key), makeFieldManager<PorymapConfig, &PorymapConfig::member>()}
const QHash<QString,FieldManager>& registeredFields() const override {
static const QHash<QString,FieldManager> fields = {
REGISTER("reopen_on_launch", reopenOnLaunch),
REGISTER("project_manually_closed", projectManuallyClosed),
REGISTER("map_list_tab", mapListTab),
REGISTER("map_list_edit_groups_enabled", mapListEditGroupsEnabled),
REGISTER("map_list_tabs_hiding_empty_folders", mapListTabsHidingEmptyFolders),
REGISTER("map_list_layouts_sorted", mapListLayoutsSorted),
REGISTER("map_list_locations_sorted", mapListLocationsSorted),
REGISTER("pretty_cursors", prettyCursors),
REGISTER("mirror_connecting_maps", mirrorConnectingMaps),
REGISTER("show_dive_emerge_maps", showDiveEmergeMaps),
REGISTER("dive_emerge_map_opacity", diveEmergeMapOpacity),
REGISTER("dive_map_opacity", diveMapOpacity),
REGISTER("emerge_map_opacity", emergeMapOpacity),
REGISTER("collision_opacity", collisionOpacity),
REGISTER("collision_zoom", collisionZoom),
REGISTER("metatiles_zoom", metatilesZoom),
REGISTER("tileset_editor_metatiles_zoom", tilesetEditorMetatilesZoom),
REGISTER("tileset_editor_tiles_zoom", tilesetEditorTilesZoom),
REGISTER("tileset_editor_layer_orientation", tilesetEditorLayerOrientation),
REGISTER("show_player_view", showPlayerView),
REGISTER("show_cursor_tile", showCursorTile),
REGISTER("show_border", showBorder),
REGISTER("show_grid", showGrid),
REGISTER("show_tileset_editor_metatile_grid", showTilesetEditorMetatileGrid),
REGISTER("show_tileset_editor_layer_grid", showTilesetEditorLayerGrid),
REGISTER("show_tileset_editor_divider", showTilesetEditorDivider),
REGISTER("show_tileset_editor_raw_attributes", showTilesetEditorRawAttributes),
REGISTER("show_palette_editor_unused_colors", showPaletteEditorUnusedColors),
REGISTER("monitor_files", monitorFiles),
REGISTER("tileset_checkerboard_fill", tilesetCheckerboardFill),
REGISTER("new_map_header_section_expanded", newMapHeaderSectionExpanded),
REGISTER("display_ids_hexadecimal", displayIdsHexadecimal),
REGISTER("theme", theme),
REGISTER("wild_mon_chart_theme", wildMonChartTheme),
REGISTER("text_editor_open_folder", textEditorOpenFolder),
REGISTER("text_editor_goto_line", textEditorGotoLine),
REGISTER("palette_editor_bit_depth", paletteEditorBitDepth),
REGISTER("project_settings_tab", projectSettingsTab),
REGISTER("script_autocomplete_mode", scriptAutocompleteMode),
REGISTER("warp_behavior_warning_disabled", warpBehaviorWarningDisabled),
REGISTER("event_delete_warning_disabled", eventDeleteWarningDisabled),
REGISTER("event_overlay_enabled", eventOverlayEnabled),
REGISTER("check_for_updates", checkForUpdates),
REGISTER("show_project_loading_screen", showProjectLoadingScreen),
REGISTER("last_update_check_time", lastUpdateCheckTime),
REGISTER("last_update_check_version", lastUpdateCheckVersion),
REGISTER("rate_limit_times", rateLimitTimes),
REGISTER("event_selection_shape_mode", eventSelectionShapeMode),
REGISTER("shown_in_game_reload_message", shownInGameReloadMessage),
REGISTER("map_grid", gridSettings),
REGISTER("status_bar_log_types", statusBarLogTypes),
REGISTER("application_font", applicationFont),
REGISTER("map_list_font", mapListFont),
REGISTER("image_export_color_space_id", imageExportColorSpaceId),
REGISTER("trusted_script_hashes", trustedScriptHashes),
REGISTER("recent_projects", recentProjects),
REGISTER("geometry", savedGeometryMap),
REGISTER("geometry_version", geometryVersion),
};
return fields;
};
#undef REGISTER
protected:
virtual void parseConfigKeyValue(QString key, QString value) override;
virtual QMap<QString, QString> getKeyValueMap() override;
virtual void init() override {};
virtual void setUnreadKeys() override {};
virtual bool parseLegacyKeyValue(const QString& key, const QString& value) override;
virtual QJsonObject getDefaultJson() const override;
private:
QString stringFromByteArray(const QByteArray&);
QByteArray bytesFromString(const QString&);
QStringList recentProjects;
QByteArray mainWindowGeometry;
QByteArray mainWindowState;
QByteArray mapSplitterState;
QByteArray mainSplitterState;
QByteArray metatilesSplitterState;
QByteArray tilesetEditorGeometry;
QByteArray tilesetEditorState;
QByteArray tilesetEditorSplitterState;
QByteArray paletteEditorGeometry;
QByteArray paletteEditorState;
QByteArray regionMapEditorGeometry;
QByteArray regionMapEditorState;
QByteArray projectSettingsEditorGeometry;
QByteArray projectSettingsEditorState;
QByteArray customScriptsEditorGeometry;
QByteArray customScriptsEditorState;
QMap<QString, QByteArray> savedGeometryMap;
int geometryVersion = 0;
};
extern PorymapConfig porymapConfig;
enum BaseGameVersion {
none,
pokeruby,
pokefirered,
pokeemerald,
};
enum ProjectIdentifier {
symbol_facing_directions,
symbol_obj_event_gfx_pointers,
@@ -301,134 +392,154 @@ enum ProjectFilePath {
class ProjectConfig: public KeyValueConfigBase
{
public:
ProjectConfig();
virtual void reset() override {
this->baseGameVersion = BaseGameVersion::pokeemerald;
// Reset non-version-specific settings
this->usePoryScript = false;
this->tripleLayerMetatilesEnabled = false;
this->defaultMetatileId = 1;
this->defaultElevation = 3;
this->defaultCollision = 0;
this->defaultMapSize = QSize(20,20);
this->defaultPrimaryTileset = "gTileset_General";
this->prefabFilepath = QString();
this->prefabImportPrompted = false;
this->tilesetsHaveCallback = true;
this->tilesetsHaveIsCompressed = true;
this->transparencyColor = QColor(Qt::black);
this->preserveMatchingOnlyData = false;
this->filePaths.clear();
this->eventIconPaths.clear();
this->pokemonIconPaths.clear();
this->eventsTabIconPath = QString();
this->collisionSheetPath = QString();
this->collisionSheetSize = QSize(2, 16);
this->playerViewDistance = QMargins(GBA_H_DIST_TO_CENTER, GBA_V_DIST_TO_CENTER, GBA_H_DIST_TO_CENTER, GBA_V_DIST_TO_CENTER);
this->blockMetatileIdMask = 0x03FF;
this->blockCollisionMask = 0x0C00;
this->blockElevationMask = 0xF000;
this->unusedTileNormal = 0x3014;
this->unusedTileCovered = 0x0000;
this->unusedTileSplit = 0x0000;
this->maxEventsPerGroup = 255;
this->forcedMajorVersion = 0;
this->metatileSelectorWidth = 8;
this->globalConstantsFilepaths.clear();
this->globalConstants.clear();
this->identifiers.clear();
this->readKeys.clear();
ProjectConfig(const QString& root = QString()) : KeyValueConfigBase(QStringLiteral("porymap.project.json")) {
setRoot(root);
}
ProjectConfig(BaseGame::Version version, const QString& root = QString()) : ProjectConfig(root) {
setVersionSpecificDefaults(version);
}
static const QMap<ProjectIdentifier, QPair<QString, QString>> defaultIdentifiers;
static const QMap<ProjectFilePath, QPair<QString, QString>> defaultPaths;
static const QStringList versionStrings;
static BaseGameVersion stringToBaseGameVersion(const QString &string);
static QString getPlayerIconPath(BaseGameVersion baseGameVersion, int character);
static QIcon getPlayerIcon(BaseGameVersion baseGameVersion, int character);
virtual bool save() override;
virtual void loadFromJson(const QJsonObject& obj) override;
QString projectDir() const { return m_root; } // Alias for root()
void reset(BaseGameVersion baseGameVersion);
void setFilePath(ProjectFilePath pathId, const QString &path);
void setFilePath(const QString &pathId, const QString &path);
QString getCustomFilePath(ProjectFilePath pathId);
QString getCustomFilePath(const QString &pathId);
QString getFilePath(ProjectFilePath pathId);
void setIdentifier(ProjectIdentifier id, QString text);
void setIdentifier(const QString &id, const QString &text);
QString getCustomIdentifier(ProjectIdentifier id);
QString getCustomIdentifier(const QString &id);
QString getIdentifier(ProjectIdentifier id);
QString getBaseGameVersionString(BaseGameVersion version);
QString getBaseGameVersionString();
int getNumLayersInMetatile();
int getNumTilesInMetatile();
void setEventIconPath(Event::Group group, const QString &path);
QString getEventIconPath(Event::Group group);
void setPokemonIconPath(const QString &species, const QString &path);
QString getPokemonIconPath(const QString &species);
QMap<QString, QString> getPokemonIconPaths();
void setVersionSpecificDefaults(BaseGame::Version baseGameVersion);
BaseGameVersion baseGameVersion;
bool usePoryScript;
bool useCustomBorderSize;
bool eventWeatherTriggerEnabled;
bool eventSecretBaseEnabled;
bool hiddenItemQuantityEnabled;
bool hiddenItemRequiresItemfinderEnabled;
bool healLocationRespawnDataEnabled;
bool eventCloneObjectEnabled;
bool floorNumberEnabled;
bool createMapTextFileEnabled;
bool tripleLayerMetatilesEnabled;
uint16_t defaultMetatileId;
uint16_t defaultElevation;
uint16_t defaultCollision;
QSize defaultMapSize;
void setFilePath(ProjectFilePath pathId, const QString& path);
void setFilePath(const QString& pathId, const QString& path);
QString getCustomFilePath(ProjectFilePath pathId);
QString getCustomFilePath(const QString& pathId);
QString getFilePath(ProjectFilePath pathId);
void setIdentifier(ProjectIdentifier id, const QString& text);
void setIdentifier(const QString& id, const QString& text);
QString getCustomIdentifier(ProjectIdentifier id);
QString getCustomIdentifier(const QString& id);
QString getIdentifier(ProjectIdentifier id);
static const QMap<ProjectIdentifier, QPair<QString, QString>> defaultIdentifiers;
static const QMap<ProjectFilePath, QPair<QString, QString>> defaultPaths;
BaseGame::Version baseGameVersion = BaseGame::Version::none;
bool usePoryScript = false;
bool useCustomBorderSize = false;
bool eventWeatherTriggerEnabled = false;
bool eventSecretBaseEnabled = false;
bool hiddenItemQuantityEnabled = false;
bool hiddenItemRequiresItemfinderEnabled = false;
bool healLocationRespawnDataEnabled = false;
bool eventCloneObjectEnabled = false;
bool floorNumberEnabled = false;
bool createMapTextFileEnabled = false;
bool tripleLayerMetatilesEnabled = false;
uint16_t defaultMetatileId = 1;
uint16_t defaultElevation = 3;
uint16_t defaultCollision = 0;
QSize defaultMapSize = QSize(20,20);
QList<uint16_t> newMapBorderMetatileIds;
QString defaultPrimaryTileset;
QString defaultPrimaryTileset = QStringLiteral("gTileset_General");
QString defaultSecondaryTileset;
QString prefabFilepath;
bool prefabImportPrompted;
bool tilesetsHaveCallback;
bool tilesetsHaveIsCompressed;
QColor transparencyColor;
bool preserveMatchingOnlyData;
int metatileAttributesSize;
uint32_t metatileBehaviorMask;
uint32_t metatileTerrainTypeMask;
uint32_t metatileEncounterTypeMask;
uint32_t metatileLayerTypeMask;
uint16_t blockMetatileIdMask;
uint16_t blockCollisionMask;
uint16_t blockElevationMask;
uint16_t unusedTileNormal;
uint16_t unusedTileCovered;
uint16_t unusedTileSplit;
bool mapAllowFlagsEnabled;
bool tilesetsHaveCallback = true;
bool tilesetsHaveIsCompressed = true;
QColor transparencyColor = QColorConstants::Black;
bool preserveMatchingOnlyData = false;
int metatileAttributesSize = 2;
uint32_t metatileBehaviorMask = 0;
uint32_t metatileTerrainTypeMask = 0;
uint32_t metatileEncounterTypeMask = 0;
uint32_t metatileLayerTypeMask = 0;
uint16_t blockMetatileIdMask = Block::DefaultMetatileIdMask;
uint16_t blockCollisionMask = Block::DefaultCollisionMask;
uint16_t blockElevationMask = Block::DefaultElevationMask;
uint16_t unusedTileNormal = 0x3014;
uint16_t unusedTileCovered = 0x0000;
uint16_t unusedTileSplit = 0x0000;
bool mapAllowFlagsEnabled = true;
QString eventsTabIconPath;
QString collisionSheetPath;
QSize collisionSheetSize;
QMargins playerViewDistance;
QList<uint32_t> warpBehaviors;
int maxEventsPerGroup;
int forcedMajorVersion;
int metatileSelectorWidth;
QSize collisionSheetSize = QSize(2, 16);
QMargins playerViewDistance = QMargins(GBA_H_DIST_TO_CENTER, GBA_V_DIST_TO_CENTER, GBA_H_DIST_TO_CENTER, GBA_V_DIST_TO_CENTER);
OrderedSet<uint32_t> warpBehaviors;
int maxEventsPerGroup = 255;
int metatileSelectorWidth = 8;
QStringList globalConstantsFilepaths;
QMap<QString,QString> globalConstants;
protected:
virtual void parseConfigKeyValue(QString key, QString value) override;
virtual QMap<QString, QString> getKeyValueMap() override;
virtual void init() override;
virtual void setUnreadKeys() override;
private:
QStringList readKeys;
QMap<ProjectIdentifier, QString> identifiers;
QMap<ProjectFilePath, QString> filePaths;
QList<ScriptSettings> customScripts;
QMap<Event::Group, QString> eventIconPaths;
QMap<QString, QString> pokemonIconPaths;
QVersionNumber minimumVersion;
#define REGISTER(key, member) {QStringLiteral(key), makeFieldManager<ProjectConfig, &ProjectConfig::member>()}
const QHash<QString,FieldManager>& registeredFields() const override {
static const QHash<QString,FieldManager> fields = {
REGISTER("base_game_version", baseGameVersion),
REGISTER("use_poryscript", usePoryScript),
REGISTER("use_custom_border_size", useCustomBorderSize),
REGISTER("enable_event_weather_trigger", eventWeatherTriggerEnabled),
REGISTER("enable_event_secret_base", eventSecretBaseEnabled),
REGISTER("enable_hidden_item_quantity", hiddenItemQuantityEnabled),
REGISTER("enable_hidden_item_requires_itemfinder", hiddenItemRequiresItemfinderEnabled),
REGISTER("enable_heal_location_respawn_data", healLocationRespawnDataEnabled),
REGISTER("enable_event_clone_object", eventCloneObjectEnabled),
REGISTER("enable_floor_number", floorNumberEnabled),
REGISTER("create_map_text_file", createMapTextFileEnabled),
REGISTER("enable_triple_layer_metatiles", tripleLayerMetatilesEnabled),
REGISTER("default_metatile_id", defaultMetatileId),
REGISTER("default_elevation", defaultElevation),
REGISTER("default_collision", defaultCollision),
REGISTER("default_map_size", defaultMapSize),
REGISTER("new_map_border_metatiles", newMapBorderMetatileIds),
REGISTER("default_primary_tileset", defaultPrimaryTileset),
REGISTER("default_secondary_tileset", defaultSecondaryTileset),
REGISTER("tilesets_have_callback", tilesetsHaveCallback),
REGISTER("tilesets_have_is_compressed", tilesetsHaveIsCompressed),
REGISTER("transparency_color", transparencyColor),
REGISTER("preserve_matching_only_data", preserveMatchingOnlyData),
REGISTER("metatile_attributes_size", metatileAttributesSize),
REGISTER("metatile_behavior_mask", metatileBehaviorMask),
REGISTER("metatile_terrain_type_mask", metatileTerrainTypeMask),
REGISTER("metatile_encounter_type_mask", metatileEncounterTypeMask),
REGISTER("metatile_layer_type_mask", metatileLayerTypeMask),
REGISTER("block_metatile_id_mask", blockMetatileIdMask),
REGISTER("block_collision_mask", blockCollisionMask),
REGISTER("block_elevation_mask", blockElevationMask),
REGISTER("unused_tile_normal", unusedTileNormal),
REGISTER("unused_tile_covered", unusedTileCovered),
REGISTER("unused_tile_split", unusedTileSplit),
REGISTER("enable_map_allow_flags", mapAllowFlagsEnabled),
REGISTER("events_tab_icon_path", eventsTabIconPath),
REGISTER("collision_sheet_path", collisionSheetPath),
REGISTER("collision_sheet_size", collisionSheetSize),
REGISTER("player_view_distance", playerViewDistance),
REGISTER("warp_behaviors", warpBehaviors),
REGISTER("max_events_per_group", maxEventsPerGroup),
REGISTER("metatile_selector_width", metatileSelectorWidth),
REGISTER("global_constants_filepaths", globalConstantsFilepaths),
REGISTER("global_constants", globalConstants),
REGISTER("custom_scripts", customScripts),
REGISTER("event_icon_paths", eventIconPaths),
REGISTER("pokemon_icon_paths", pokemonIconPaths),
REGISTER("minimum_version", minimumVersion),
REGISTER("custom_identifiers", identifiers),
REGISTER("custom_file_paths", filePaths),
};
return fields;
}
#undef REGISTER
protected:
virtual bool parseLegacyKeyValue(const QString& key, const QString& value) override;
virtual QJsonObject getDefaultJson() const override;
virtual void initializeFromEmpty() override;
private:
ProjectFilePath reverseDefaultPaths(const QString& str);
ProjectIdentifier reverseDefaultIdentifier(const QString& str);
QMap<ProjectIdentifier, QString> identifiers;
QMap<ProjectFilePath, QString> filePaths;
};
extern ProjectConfig projectConfig;
@@ -436,36 +547,36 @@ extern ProjectConfig projectConfig;
class UserConfig: public KeyValueConfigBase
{
public:
UserConfig();
virtual void reset() override {
this->recentMapOrLayout = QString();
this->useEncounterJson = true;
this->customScripts.clear();
this->readKeys.clear();
UserConfig(const QString& root = QString()) : KeyValueConfigBase(QStringLiteral("porymap.user.json")) {
setRoot(root);
}
virtual void loadFromJson(const QJsonObject& obj) override;
QString projectDir() const { return m_root; } // Alias for root()
void parseCustomScripts(QString input);
QString outputCustomScripts();
void setCustomScripts(QStringList scripts, QList<bool> enabled);
QStringList getCustomScriptPaths();
QList<bool> getCustomScriptsEnabled();
QString recentMapOrLayout;
bool useEncounterJson;
QString prefabsFilepath;
bool prefabsImportPrompted = false;
bool useEncounterJson = true;
QList<ScriptSettings> customScripts;
protected:
virtual void parseConfigKeyValue(QString key, QString value) override;
virtual QMap<QString, QString> getKeyValueMap() override;
virtual void init() override;
virtual void setUnreadKeys() override;
#ifdef CONFIG_BACKWARDS_COMPATABILITY
friend class ProjectConfig;
#endif
virtual bool parseLegacyKeyValue(const QString& key, const QString& value) override;
virtual QJsonObject getDefaultJson() const override;
private:
QStringList readKeys;
QMap<QString, bool> customScripts;
#define REGISTER(key, member) {QStringLiteral(key), makeFieldManager<UserConfig, &UserConfig::member>()}
const QHash<QString,FieldManager>& registeredFields() const override {
static const QHash<QString,FieldManager> fields = {
REGISTER("recent_map_or_layout", recentMapOrLayout),
REGISTER("prefabs_filepath", prefabsFilepath),
REGISTER("prefabs_import_prompted", prefabsImportPrompted),
REGISTER("use_encounter_json", useEncounterJson),
REGISTER("custom_scripts", customScripts),
};
return fields;
}
#undef REGISTER
};
extern UserConfig userConfig;
@@ -476,26 +587,24 @@ class Shortcut;
class ShortcutsConfig : public KeyValueConfigBase
{
public:
ShortcutsConfig();
virtual void reset() override {
ShortcutsConfig() : KeyValueConfigBase(QStringLiteral("shortcuts.json")) {
setRoot(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
user_shortcuts.clear();
}
virtual QJsonObject toJson() const override;
virtual void loadFromJson(const QJsonObject& obj) override;
// Call this before applying user shortcuts so that the user can restore defaults.
void setDefaultShortcuts(const QObjectList &objects);
void setDefaultShortcuts(const QObjectList& objects);
QList<QKeySequence> defaultShortcuts(const QObject *object) const;
void setUserShortcuts(const QObjectList &objects);
void setUserShortcuts(const QMultiMap<const QObject *, QKeySequence> &objects_keySequences);
void setUserShortcuts(const QObjectList& objects);
void setUserShortcuts(const QMultiMap<const QObject *, QKeySequence>& objects_keySequences);
QList<QKeySequence> userShortcuts(const QObject *object) const;
protected:
virtual void parseConfigKeyValue(QString key, QString value) override;
virtual QMap<QString, QString> getKeyValueMap() override;
virtual void init() override { };
virtual void setUnreadKeys() override { };
virtual bool parseLegacyKeyValue(const QString& key, const QString& value) override;
virtual QJsonObject getDefaultJson() const override;
private:
QMultiMap<QString, QKeySequence> user_shortcuts;
@@ -509,11 +618,11 @@ private:
QString cfgKey(const QObject *object) const;
QList<QKeySequence> currentShortcuts(const QObject *object) const;
void storeShortcutsFromList(StoreType storeType, const QObjectList &objects);
void storeShortcutsFromList(StoreType storeType, const QObjectList& objects);
void storeShortcuts(
StoreType storeType,
const QString &cfgKey,
const QList<QKeySequence> &keySequences);
const QString& cfgKey,
const QList<QKeySequence>& keySequences);
};
extern ShortcutsConfig shortcutsConfig;

22
include/core/basegame.h Normal file
View File

@@ -0,0 +1,22 @@
#pragma once
#ifndef BASEGAMEVERSION_H
#define BASEGAMEVERSION_H
#include <QString>
#include <QIcon>
namespace BaseGame {
enum Version {
none, // TODO: Go through and make sure this is a valid state
pokeruby,
pokefirered,
pokeemerald,
};
Version stringToVersion(const QString &string);
QString versionToString(Version version);
QString getPlayerIconPath(Version version, int character);
QIcon getPlayerIcon(Version version, int character);
};
#endif // BASEGAMEVERSION_H

View File

@@ -26,7 +26,12 @@ public:
static uint16_t getMaxCollision();
static uint16_t getMaxElevation();
static const uint16_t maxValue;
// Upper limit for metatile ID, collision, and elevation masks. Used externally.
static constexpr uint16_t MaxValue = 0xFFFF;
static constexpr uint16_t DefaultMetatileIdMask = 0x03FF;
static constexpr uint16_t DefaultCollisionMask = 0x0C00;
static constexpr uint16_t DefaultElevationMask = 0xF000;
private:
uint16_t m_metatileId;

342
include/core/converter.h Normal file
View File

@@ -0,0 +1,342 @@
#pragma once
#ifndef CONVERTER_H
#define CONVERTER_H
#include <QJsonValue>
#include "magic_enum.hpp"
#include "orderedset.h"
#include "scriptsettings.h"
#include "gridsettings.h"
#include "basegame.h"
/*
These are templates for type conversion to/from JSON,
though other type conversions can be implemented here too.
This is mostly useful when converting the type is complicated,
or when the type is generalized away.
## Example Usage ##
QSize size;
QJsonValue json = Converter<QSize>::toJson(size);
QSize sameSize = Converter<QSize>::fromJson(json);
## Adding a new conversion ##
To add a new type conversion, add a new 'Converter' template:
template <>
struct Converter<NewType> : DefaultConverter<NewType> {
// And re-implement any of the desired conversion functions.
// Any functions not implemented will be inherited from DefaultConverter.
static QJsonValue toJson(const NewType& value) {
// your conversion to JSON
return QJsonValue();
}
static NewType fromJson(const QJsonValue& json, QStringList* errors = nullptr) {
// your conversion from JSON
return NewType();
}
};
Note: When serializing to/from JSON, anything that can be serialized to/from
a string is trivially serializable for JSON. In this case, rather than
inheriting from 'DefaultConverter' and reimplementing 'toJson' and 'fromJson',
you can inherit from 'DefaultStringConverter' and/or reimplement 'toString'/'fromString'.
Appropriately implementing 'toString'/'fromString' has the added benefit that your type
can automatically be used as a JSON key if it for example appears as the key in a QMap.
*/
template <typename T>
struct DefaultConverter {
// Defaults to straightforward QJsonValue construction.
// This handles most of the primitive types.
static QJsonValue toJson(const T& value) {
return QJsonValue{value};
}
static T fromJson(const QJsonValue& json, QStringList* errors = nullptr) {
const QVariant v = json.toVariant();
if (!v.canConvert<T>()) {
if (errors) errors->append(QString("Can't convert from type '%1'").arg(v.typeName()));
// Failed conversion will return a default-constructed object below
}
return v.value<T>();
}
// Default to identity
static QString toString(const T& value) {return value;}
static T fromString(const QString& string, QStringList* = nullptr) {return string;}
};
template <typename T, typename Enable = void>
struct Converter : DefaultConverter<T> {};
// This template implements JSON conversion by first converting the data to/from a string.
// This allows any type that can describe how to stringify itself to automatically also
// support JSON conversion with no additional work.
template <typename T>
struct DefaultStringConverter : DefaultConverter<T> {
static QJsonValue toJson(const T& value) {
return Converter<QString>::toJson(Converter<T>::toString(value));
}
static T fromJson(const QJsonValue& json, QStringList* errors = nullptr) {
const auto string = Converter<QString>::fromJson(json, errors);
return Converter<T>::fromString(string, errors);
}
// Many types have a 'toString' function, so we default to trying that.
static QString toString(const T& value) {
return value.toString();
}
};
template <>
struct Converter<QUrl> : DefaultStringConverter<QUrl> {};
template <>
struct Converter<QKeySequence> : DefaultStringConverter<QKeySequence> {};
template <>
struct Converter<uint32_t> : DefaultConverter<uint32_t> {
// Constructing a QJsonValue from uint32_t is ambiguous, so we need an explicit cast.
static QJsonValue toJson(uint32_t value) {
return QJsonValue{static_cast<qint64>(value)};
}
};
// Template for generic enum values.
// Converts JSON -> string/int -> enum, handling the unsafe conversion if the int is out of range of the enum.
// Qt has a system for this (Q_ENUM) but they don't use it for all their internal enums, so we use magic_enum instead.
template <typename T>
struct Converter<T, std::enable_if_t<std::is_enum_v<T>>> : DefaultStringConverter<T> {
static QString toString(const T& value) {
const std::string s = std::string(magic_enum::enum_name(value));
return QString::fromStdString(s);
}
static T fromString(const QString& string, QStringList* errors = nullptr) {
auto e = magic_enum::enum_cast<T>(string.toStdString(), magic_enum::case_insensitive);
if (!e.has_value()) {
if (errors) errors->append(QString("'%1' is not a named enum value.").arg(string));
return magic_enum::enum_value<T>(0);
}
return e.value();
}
// When reading from JSON, handle either the named enum or an enum's number value.
static T fromJson(const QJsonValue& json, QStringList* errors = nullptr) {
if (json.isString()) return Converter<T>::fromString(json.toString());
auto value = Converter<int>::fromJson(json, errors);
auto e = magic_enum::enum_cast<T>(value);
if (!e.has_value()) {
if (errors) errors->append(QString("'%1' is out of range of enum.").arg(QString::number(value)));
return magic_enum::enum_value<T>(0);
}
return e.value();
}
};
template <>
struct Converter<QVersionNumber> : DefaultStringConverter<QVersionNumber> {
static QVersionNumber fromString(const QString& string, QStringList* = nullptr) {
return QVersionNumber::fromString(string);
}
};
template <>
struct Converter<QDateTime> : DefaultStringConverter<QDateTime> {
static QString toString(const QDateTime& value) {
return value.toUTC().toString();
}
static QDateTime fromString(const QString& string, QStringList* = nullptr) {
return QDateTime::fromString(string).toLocalTime();
}
};
template <>
struct Converter<QColor> : DefaultStringConverter<QColor> {
static QString toString(const QColor& value) {
return value.name();
}
static QColor fromString(const QString& string, QStringList* errors = nullptr) {
const QColor color(string);
if (!color.isValid()) {
if (errors) errors->append(QString("'%1' is not a valid color.").arg(string));
return QColorConstants::Black;
}
return color;
}
};
template <>
struct Converter<QFont> : DefaultStringConverter<QFont> {
static QFont fromString(const QString& string, QStringList* errors = nullptr) {
QFont font;
if (!font.fromString(string) && errors) {
errors->append(QString("'%1' is not a valid font description.").arg(string));
}
return font;
}
};
template <>
struct Converter<BaseGame::Version> : DefaultStringConverter<BaseGame::Version> {
static QString toString(const BaseGame::Version& value) {
return BaseGame::versionToString(value);
}
static BaseGame::Version fromString(const QString& string, QStringList* = nullptr) {
return BaseGame::stringToVersion(string);
}
};
template <typename T>
struct Converter<QList<T>> : DefaultConverter<QList<T>> {
static QJsonValue toJson(const QList<T>& list) {
QJsonArray arr;
for (auto& elem : list) arr.append(Converter<T>::toJson(elem));
return arr;
}
static QList<T> fromJson(const QJsonValue& json, QStringList* errors = nullptr) {
const auto arr = Converter<QJsonArray>::fromJson(json, errors);
QList<T> list;
for (auto& elem : arr) list.append(Converter<T>::fromJson(elem, errors));
return list;
}
};
template <typename K, typename V>
struct Converter<QMap<K,V>> : DefaultConverter<QMap<K,V>> {
static QJsonObject toJson(const QMap<K,V>& map) {
QJsonObject obj;
for (auto it = map.begin(); it != map.end(); it++) {
const QString key = Converter<K>::toString(it.key());
obj[key] = Converter<V>::toJson(it.value());
}
return obj;
}
static QMap<K,V> fromJson(const QJsonValue& json, QStringList* errors = nullptr) {
const auto obj = Converter<QJsonObject>::fromJson(json, errors);
QMap<K,V> map;
for (auto it = obj.begin(); it != obj.end(); it++) {
const auto key = Converter<K>::fromString(it.key(), errors);
map.insert(key, Converter<V>::fromJson(it.value(), errors));
}
return map;
}
};
template <typename K, typename V>
struct Converter<QMultiMap<K,V>> : DefaultConverter<QMultiMap<K,V>> {
static QJsonObject toJson(const QMultiMap<K,V>& map) {
QJsonObject obj;
for (const auto& uniqueKey : map.uniqueKeys()) {
const QString key = Converter<K>::toString(uniqueKey);
obj[key] = Converter<QList<V>>::toJson(map.values(uniqueKey));
}
return obj;
}
static QMultiMap<K,V> fromJson(const QJsonValue& json, QStringList* errors = nullptr) {
const auto obj = Converter<QJsonObject>::fromJson(json, errors);
QMultiMap<K,V> map;
for (auto it = obj.begin(); it != obj.end(); it++) {
const auto key = Converter<K>::fromString(it.key(), errors);
const auto values = Converter<QList<V>>::fromJson(it.value(), errors);
for (const auto& value : values) map.insert(key, value);
}
return map;
}
};
template <typename T>
struct Converter<OrderedSet<T>> : DefaultConverter<OrderedSet<T>> {
static QJsonValue toJson(const OrderedSet<T>& set) {
QJsonArray arr;
for (auto& elem : set) arr.append(Converter<T>::toJson(elem));
return arr;
}
static OrderedSet<T> fromJson(const QJsonValue& json, QStringList* errors = nullptr) {
const auto arr = Converter<QJsonArray>::fromJson(json, errors);
OrderedSet<T> set;
for (auto& elem : arr) set.insert(Converter<T>::fromJson(elem, errors));
return set;
}
};
template <>
struct Converter<QSize> : DefaultConverter<QSize> {
static QJsonValue toJson(const QSize& value) {
QJsonObject obj;
obj["width"] = value.width();
obj["height"] = value.height();
return obj;
}
static QSize fromJson(const QJsonValue& json, QStringList* errors = nullptr) {
const auto obj = Converter<QJsonObject>::fromJson(json, errors);
QSize size;
size.setWidth(obj.value("width").toInt());
size.setHeight(obj.value("height").toInt());
return size;
}
};
template <>
struct Converter<QMargins> : DefaultConverter<QMargins> {
static QJsonValue toJson(const QMargins& value) {
QJsonObject obj;
obj["top"] = value.top();
obj["bottom"] = value.bottom();
obj["left"] = value.left();
obj["right"] = value.right();
return obj;
}
static QMargins fromJson(const QJsonValue& json, QStringList* errors = nullptr) {
const auto obj = Converter<QJsonObject>::fromJson(json, errors);
QMargins margins;
margins.setTop(obj.value("top").toInt());
margins.setBottom(obj.value("bottom").toInt());
margins.setLeft(obj.value("left").toInt());
margins.setRight(obj.value("right").toInt());
return margins;
}
};
template <>
struct Converter<ScriptSettings> : DefaultConverter<ScriptSettings> {
static QJsonValue toJson(const ScriptSettings& value) {
QJsonObject obj;
obj["path"] = value.path;
obj["enabled"] = value.enabled;
return obj;
}
static ScriptSettings fromJson(const QJsonValue& json, QStringList* errors = nullptr) {
const auto obj = Converter<QJsonObject>::fromJson(json, errors);
ScriptSettings settings;
settings.path = obj.value("path").toString();
settings.enabled = obj.value("enabled").toBool();
return settings;
}
};
template <>
struct Converter<GridSettings> : DefaultConverter<GridSettings> {
static QJsonValue toJson(const GridSettings& value) {
return value.toJson();
}
static GridSettings fromJson(const QJsonValue& json, QStringList* errors = nullptr) {
const auto obj = Converter<QJsonObject>::fromJson(json, errors);
return GridSettings::fromJson(obj);
}
};
template <>
struct Converter<QByteArray> : DefaultConverter<QByteArray> {
static QJsonValue toJson(const QByteArray& value) {
return QString::fromLocal8Bit(value.toBase64());
}
static QByteArray fromJson(const QJsonValue& json, QStringList* errors = nullptr) {
const auto s = Converter<QString>::fromJson(json, errors);
return QByteArray::fromBase64(s.toLocal8Bit());
}
};
#endif // CONVERTER_H

View File

@@ -8,6 +8,7 @@
#include <QPixmap>
#include <QString>
#include <QUndoStack>
#include <QJsonObject>
class Map;
class LayoutPixmapItem;

View File

@@ -3,11 +3,13 @@
#define METATILE_H
#include "tile.h"
#include "config.h"
#include "basegame.h"
#include "bitpacker.h"
#include <QImage>
#include <QPoint>
#include <QString>
#include <QMap>
#include <QSet>
class Project;
@@ -41,7 +43,7 @@ public:
uint32_t getAttributes() const;
uint32_t getAttribute(Metatile::Attr attr) const { return this->attributes.value(attr, 0); }
void setAttributes(uint32_t data);
void setAttributes(uint32_t data, BaseGameVersion version);
void setAttributes(uint32_t data, BaseGame::Version version);
void setAttribute(Metatile::Attr attr, uint32_t value);
// For convenience
@@ -56,17 +58,18 @@ public:
static int getIndexInTileset(int);
static QPoint coordFromPixmapCoord(const QPointF &pixelCoord);
static uint32_t getDefaultAttributesMask(BaseGameVersion version, Metatile::Attr attr);
static uint32_t getDefaultAttributesMask(BaseGame::Version version, Metatile::Attr attr);
static uint32_t getMaxAttributesMask();
static int getDefaultAttributesSize(BaseGameVersion version);
static int getDefaultAttributesSize(BaseGame::Version version);
static void setLayout(Project*);
static QString getMetatileIdString(uint16_t metatileId);
static QString getMetatileIdStrings(const QList<uint16_t> &metatileIds);
static QString getLayerName(int layerNum);
static int numLayers();
static constexpr int tileWidth() { return 2; }
static constexpr int tileHeight() { return 2; }
static constexpr int tilesPerLayer() { return Metatile::tileWidth() * Metatile::tileHeight(); }
static int maxTiles() { return Metatile::numLayers() * Metatile::tilesPerLayer(); }
static constexpr int pixelWidth() { return Metatile::tileWidth() * Tile::pixelWidth(); }
static constexpr int pixelHeight() { return Metatile::tileHeight() * Tile::pixelHeight(); }
static constexpr QSize pixelSize() { return QSize(pixelWidth(), pixelHeight()); }

30
include/core/orderedset.h Normal file
View File

@@ -0,0 +1,30 @@
#pragma once
#ifndef ORDERED_SET_H
#define ORDERED_SET_H
#include <QJsonArray>
template <typename T>
class OrderedSet : public std::set<T>
{
using std::set<T>::set;
public:
// Not introduced to std::set until C++20
#if __cplusplus < 202002L
bool contains(const T& value) const {
return this->find(value) != this->end();
}
#endif
QSet<T> toQSet() const {
return QSet<T>(this->begin(), this->end());
}
static QSet<T> fromQSet(const QSet<T>& set) {
return OrderedSet<T>(set.begin(), set.end());
}
bool isEmpty() const {
return this->empty();
}
};
#endif // ORDERED_SET_H

View File

@@ -5,6 +5,7 @@
#include "map.h"
#include "tilemaptileselector.h"
#include "history.h"
#include "config.h"
#include <QStringList>
#include <QString>

View File

@@ -0,0 +1,27 @@
#pragma once
#ifndef SCRIPTSETTINGS_H
#define SCRIPTSETTINGS_H
#include <QString>
#include <QList>
// Holds the basic user-provided information about a plug-in script.
struct ScriptSettings {
QString path;
bool enabled = true;
// Scripts can either by specific to the project, or specific to the user.
// This allows projects to send scripts downstream to their users,
// while still allowing them to use their own personal scripts.
bool userOnly = true;
static QStringList filter(const QList<ScriptSettings>& scripts) {
QStringList paths;
for (auto& script : scripts) {
if (script.enabled) paths.append(script.path);
}
return paths;
}
};
#endif // SCRIPTSETTINGS_H

View File

@@ -5,9 +5,6 @@
#include <QObject>
#include <QSize>
// TODO: Replace once config refactoring is complete.
extern bool ConfigDisplayIdsHexadecimal;
class Tile
{
public:
@@ -30,7 +27,8 @@ public:
QString toString() const;
static QString getTileIdString(uint16_t tileId);
static const uint16_t maxValue;
// Upper limit for raw value (i.e., uint16_t max).
static constexpr uint16_t MaxValue = 0xFFFF;
static constexpr int pixelWidth() { return 8; }
static constexpr int pixelHeight() { return 8; }

View File

@@ -21,6 +21,19 @@ namespace Util {
QColorSpace toColorSpace(int colorSpaceInt);
QString mkpath(const QString& dirPath);
QString getFileHash(const QString &filepath);
// Given a QMap<T,QString>, erases all entries with empty strings.
// Returns the number of entries erased.
template <typename T>
int removeEmptyStrings(QMap<T,QString> *map) {
if (!map) return 0;
int numRemoved = 0;
for (auto it = map->begin(); it != map->end();) {
if (it.value().isEmpty()) it = map->erase(it);
else {it++; numRemoved++;}
}
return numRemoved;
}
}
#endif // UTILITY_H

View File

@@ -440,7 +440,7 @@ private:
void initShortcuts();
void initExtraShortcuts();
void loadUserSettings();
void restoreWindowState();
void resizeWithinScreen();
void setTheme(QString);
void updateTilesetEditor();
Event::Group getEventGroupFromTabWidget(QWidget *tab);

View File

@@ -105,7 +105,7 @@ public:
void clearHealLocations();
bool sanityCheck();
int getSupportedMajorVersion(QString *errorOut = nullptr);
QVersionNumber getMinimumVersion(QString *errorOut = nullptr) const;
bool load();
QMap<QString, Tileset*> tilesetCache;

View File

@@ -50,7 +50,7 @@ public:
Q_INVOKABLE bool getBorderVisibility();
Q_INVOKABLE void setSmartPathsEnabled(bool visible);
Q_INVOKABLE bool getSmartPathsEnabled();
Q_INVOKABLE QList<QString> getCustomScripts();
static Q_INVOKABLE QList<QString> getCustomScripts();
Q_INVOKABLE QList<int> getMetatileLayerOrder();
Q_INVOKABLE void setMetatileLayerOrder(const QList<int> &order);
Q_INVOKABLE QList<float> getMetatileLayerOpacity();

View File

@@ -1,56 +0,0 @@
#ifndef CITYMAPPIXMAPITEM_H
#define CITYMAPPIXMAPITEM_H
#include "tilemaptileselector.h"
#include <QGraphicsPixmapItem>
#include <QByteArray>
#include <QVector>
class CityMapPixmapItem : public QObject, public QGraphicsPixmapItem {
Q_OBJECT
private:
using QGraphicsPixmapItem::paint;
public:
CityMapPixmapItem(QString fname, TilemapTileSelector *tile_selector) {
this->file = fname;
this->tile_selector = tile_selector;
setAcceptHoverEvents(true);
init();
}
TilemapTileSelector *tile_selector;
QString file;
QByteArray data;
void init();
void save();
void create(QString);
virtual void paint(QGraphicsSceneMouseEvent *);
virtual void draw();
int getIndexAt(int, int);
int width();
int height();
QVector<uint8_t> getTiles();
void setTiles(QVector<uint8_t>);
private:
int width_;
int height_;
signals:
void mouseEvent(QGraphicsSceneMouseEvent *, CityMapPixmapItem *);
void hoveredRegionMapTileChanged(int x, int y);
void hoveredRegionMapTileCleared();
protected:
void mousePressEvent(QGraphicsSceneMouseEvent*);
void mouseMoveEvent(QGraphicsSceneMouseEvent*);
void mouseReleaseEvent(QGraphicsSceneMouseEvent*);
};
#endif // CITYMAPPIXMAPITEM_H

View File

@@ -33,7 +33,7 @@ private:
bool hasUnsavedChanges = false;
const QString baseDir;
void displayScript(const QString &filepath, bool enabled);
void displayScript(const ScriptSettings &settings);
void displayNewScript(QString filepath);
QString chooseScript(QString dir);
void removeScript(QListWidgetItem * item);
@@ -46,7 +46,6 @@ private:
int prompt(const QString &text, QMessageBox::StandardButton defaultButton);
void save();
void closeEvent(QCloseEvent*);
void restoreWindowState();
void initShortcuts();
QObjectList shortcutableObjects() const;
void openManual();

View File

@@ -2,6 +2,7 @@
#define CUSTOMSCRIPTSLISTITEM_H
#include <QFrame>
#include "scriptsettings.h"
namespace Ui {
class CustomScriptsListItem;
@@ -13,9 +14,26 @@ class CustomScriptsListItem : public QFrame
public:
explicit CustomScriptsListItem(QWidget *parent = nullptr);
explicit CustomScriptsListItem(const ScriptSettings& settings, QWidget *parent = nullptr);
~CustomScriptsListItem();
public:
void setSettings(const ScriptSettings& settings);
ScriptSettings getSettings() const;
void setPath(const QString& text);
QString path() const;
void setScriptEnabled(bool enabled);
bool scriptEnabled() const;
signals:
void clickedChooseScript();
void clickedEditScript();
void clickedDeleteScript();
void toggledEnable(bool checked);
void pathEdited(const QString& text);
private:
Ui::CustomScriptsListItem *ui;
};

View File

@@ -1,5 +1,6 @@
#include <QObject>
#include <QEvent>
#include <QSet>
/// Ctrl+Wheel = zoom
@@ -16,7 +17,6 @@ public slots:
};
/// Emits a signal when a window gets activated / regains focus
class ActiveWindowFilter : public QObject {
Q_OBJECT
@@ -27,3 +27,15 @@ public:
signals:
void activated();
};
class GeometrySaver : public QObject {
Q_OBJECT
public:
GeometrySaver(QObject *parent, bool enableLogging = true)
: QObject(parent), m_loggingEnabled(enableLogging) {}
bool eventFilter(QObject *obj, QEvent *event) override;
private:
bool m_loggingEnabled = true;
QSet<QObject*> m_wasShown;
};

View File

@@ -3,10 +3,12 @@
#include <QDialog>
#include <QAbstractButton>
#include <QJsonObject>
#include "metatile.h"
class GridSettings {
public:
explicit GridSettings() {};
constexpr GridSettings() {};
~GridSettings() {};
enum Style {
@@ -17,15 +19,18 @@ public:
Dots,
};
uint width = 16;
uint height = 16;
uint width = Metatile::pixelWidth();
uint height = Metatile::pixelHeight();
int offsetX = 0;
int offsetY = 0;
Style style = Style::Solid;
QColor color = Qt::black;
QColor color = QColorConstants::Black;
QVector<qreal> getHorizontalDashPattern() const { return this->getDashPattern(this->width); }
QVector<qreal> getVerticalDashPattern() const { return this->getDashPattern(this->height); }
QJsonObject toJson() const;
static GridSettings fromJson(const QJsonObject &obj);
static QString getStyleName(Style style);
static GridSettings::Style getStyleFromName(const QString &name);
private:

View File

@@ -84,7 +84,12 @@ private:
QImage m_previewImage;
bool m_previewUpdateQueued = false;
QList<int> m_layerOrder;
ProjectConfig m_savedConfig;
struct SavedConfigSettings {
QColor transparencyColor;
uint16_t unusedTileNormal = 0;
uint16_t unusedTileCovered = 0;
uint16_t unusedTileSplit = 0;
} m_savedConfig;
QList<QRadioButton*> m_transparencyButtons;
void populate(const Settings &settings);

View File

@@ -54,7 +54,6 @@ private:
void commitEditHistory();
void commitEditHistory(int paletteId);
void updateEditHistoryActions();
void restoreWindowState();
void invalidateCache();
void closeEvent(QCloseEvent*);
void setColorInputTitles(bool show);

View File

@@ -25,7 +25,7 @@ public:
void addPrefab(MetatileSelection selection, Layout *layout, QString name);
void updatePrefabUi(QPointer<Layout> layout);
void clearPrefabUi();
bool tryImportDefaultPrefabs(QWidget * parent, BaseGameVersion version, QString filepath = "");
bool tryImportDefaultPrefabs(QWidget * parent, BaseGame::Version version, QString filepath = "");
private:
QPointer<MetatileSelector> selector;

View File

@@ -40,7 +40,6 @@ private:
void initUi();
void connectSignals();
void restoreWindowState();
void save();
void refresh();
void closeEvent(QCloseEvent*);

View File

@@ -2,7 +2,6 @@
#define REGIONMAPEDITOR_H
#include "regionmappixmapitem.h"
#include "citymappixmapitem.h"
#include "regionmaplayoutpixmapitem.h"
#include "regionmapentriespixmapitem.h"
#include "regionmap.h"
@@ -118,7 +117,6 @@ private:
void setRegionMap(RegionMap *map);
void setLocations(const QStringList &locations);
void restoreWindowState();
void closeEvent(QCloseEvent* event);
void setTileHFlip(bool enabled);

View File

@@ -114,7 +114,6 @@ private:
void initMetatileLayersItem();
void initShortcuts();
void initExtraShortcuts();
void restoreWindowState();
void initMetatileHistory();
void setTilesets(QString primaryTilesetLabel, QString secondaryTilesetLabel);
void reset();

View File

@@ -19,8 +19,6 @@ public:
explicit WildMonChart(QWidget *parent, const EncounterTableModel *table);
~WildMonChart();
virtual void closeEvent(QCloseEvent *event) override;
public slots:
void setTable(const EncounterTableModel *table);
void clearTable();