mirror of
https://github.com/huderlem/porymap.git
synced 2026-03-21 17:45:44 -05:00
New JSON config format
This commit is contained in:
parent
e3e42f39f0
commit
3571b4ea4b
647
include/config.h
647
include/config.h
|
|
@ -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
22
include/core/basegame.h
Normal 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
|
||||
|
|
@ -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
342
include/core/converter.h
Normal 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
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
#include <QPixmap>
|
||||
#include <QString>
|
||||
#include <QUndoStack>
|
||||
#include <QJsonObject>
|
||||
|
||||
class Map;
|
||||
class LayoutPixmapItem;
|
||||
|
|
|
|||
|
|
@ -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
30
include/core/orderedset.h
Normal 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
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
#include "map.h"
|
||||
#include "tilemaptileselector.h"
|
||||
#include "history.h"
|
||||
#include "config.h"
|
||||
|
||||
#include <QStringList>
|
||||
#include <QString>
|
||||
|
|
|
|||
27
include/core/scriptsettings.h
Normal file
27
include/core/scriptsettings.h
Normal 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
|
||||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -54,7 +54,6 @@ private:
|
|||
void commitEditHistory();
|
||||
void commitEditHistory(int paletteId);
|
||||
void updateEditHistoryActions();
|
||||
void restoreWindowState();
|
||||
void invalidateCache();
|
||||
void closeEvent(QCloseEvent*);
|
||||
void setColorInputTitles(bool show);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ private:
|
|||
|
||||
void initUi();
|
||||
void connectSignals();
|
||||
void restoreWindowState();
|
||||
void save();
|
||||
void refresh();
|
||||
void closeEvent(QCloseEvent*);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -114,7 +114,6 @@ private:
|
|||
void initMetatileLayersItem();
|
||||
void initShortcuts();
|
||||
void initExtraShortcuts();
|
||||
void restoreWindowState();
|
||||
void initMetatileHistory();
|
||||
void setTilesets(QString primaryTilesetLabel, QString secondaryTilesetLabel);
|
||||
void reset();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -47,8 +47,9 @@ VERSION = 6.3.0
|
|||
DEFINES += PORYMAP_VERSION=\\\"$$VERSION\\\"
|
||||
|
||||
SOURCES += src/core/advancemapparser.cpp \
|
||||
src/config/legacy.cpp \
|
||||
src/core/basegame.cpp \
|
||||
src/core/block.cpp \
|
||||
src/ui/resizelayoutpopup.cpp \
|
||||
src/core/bitpacker.cpp \
|
||||
src/core/blockdata.cpp \
|
||||
src/core/events.cpp \
|
||||
|
|
@ -113,7 +114,7 @@ SOURCES += src/core/advancemapparser.cpp \
|
|||
src/ui/layoutpixmapitem.cpp \
|
||||
src/ui/prefabcreationdialog.cpp \
|
||||
src/ui/regionmappixmapitem.cpp \
|
||||
src/ui/citymappixmapitem.cpp \
|
||||
src/ui/resizelayoutpopup.cpp \
|
||||
src/ui/mapheaderform.cpp \
|
||||
src/ui/metatilelayersitem.cpp \
|
||||
src/ui/metatileselector.cpp \
|
||||
|
|
@ -168,6 +169,7 @@ HEADERS += include/core/advancemapparser.h \
|
|||
include/core/block.h \
|
||||
include/core/bitpacker.h \
|
||||
include/core/blockdata.h \
|
||||
include/core/converter.h \
|
||||
include/core/events.h \
|
||||
include/core/filedialog.h \
|
||||
include/core/history.h \
|
||||
|
|
@ -233,7 +235,6 @@ HEADERS += include/core/advancemapparser.h \
|
|||
include/ui/mapview.h \
|
||||
include/ui/prefabcreationdialog.h \
|
||||
include/ui/regionmappixmapitem.h \
|
||||
include/ui/citymappixmapitem.h \
|
||||
include/ui/colorinputwidget.h \
|
||||
include/ui/metatilelayersitem.h \
|
||||
include/ui/metatileselector.h \
|
||||
|
|
|
|||
1422
src/config.cpp
1422
src/config.cpp
File diff suppressed because it is too large
Load Diff
423
src/config/legacy.cpp
Normal file
423
src/config/legacy.cpp
Normal file
|
|
@ -0,0 +1,423 @@
|
|||
#include "config.h"
|
||||
#include "log.h"
|
||||
#include "tile.h"
|
||||
#include "block.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QDir>
|
||||
#include <QColor>
|
||||
|
||||
|
||||
QString getLegacyFilename(const QString &newFilename) {
|
||||
static const QMap<QString, QString> map = {
|
||||
{QStringLiteral("settings.json"), QStringLiteral("porymap.cfg")},
|
||||
{QStringLiteral("shortcuts.json"), QStringLiteral("porymap.shortcuts.cfg")},
|
||||
{QStringLiteral("porymap.project.json"), QStringLiteral("porymap.project.cfg")},
|
||||
{QStringLiteral("porymap.user.json"), QStringLiteral("porymap.user.cfg")},
|
||||
};
|
||||
return map.value(newFilename);
|
||||
}
|
||||
|
||||
bool KeyValueConfigBase::loadLegacy() {
|
||||
const QString oldFilename = getLegacyFilename(filename());
|
||||
if (oldFilename.isEmpty()) return false;
|
||||
|
||||
QDir dir(root());
|
||||
QFile file(dir.absoluteFilePath(oldFilename));
|
||||
if (!file.exists() || !file.open(QIODevice::ReadOnly)) return false;
|
||||
|
||||
QTextStream in(&file);
|
||||
static const QRegularExpression re("^(?<key>[^=]+)=(?<value>.*)$");
|
||||
while (!in.atEnd()) {
|
||||
QString line = in.readLine().trimmed();
|
||||
int commentIndex = line.indexOf("#");
|
||||
if (commentIndex >= 0) {
|
||||
line = line.left(commentIndex).trimmed();
|
||||
}
|
||||
|
||||
if (line.length() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QRegularExpressionMatch match = re.match(line);
|
||||
if (match.hasMatch()) {
|
||||
parseLegacyKeyValue(match.captured("key").trimmed(), match.captured("value").trimmed());
|
||||
}
|
||||
}
|
||||
logInfo(QString("Loaded legacy config file '%1'").arg(oldFilename));
|
||||
|
||||
// Save before deleting the old config file to ensure no data is lost.
|
||||
if (save()/* && !file.remove()*/) { // TODO: Once non-legacy loading is complete, we can uncomment this.
|
||||
//logWarn(QString("Failed to delete old config file '%1'.").arg(oldFilename));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool toBool(const QString &value) {
|
||||
return (value.toInt(nullptr, 0) != 0);
|
||||
}
|
||||
|
||||
int toInt(const QString &value, int min = INT_MIN, int max = INT_MAX, int defaultValue = 0) {
|
||||
bool ok;
|
||||
int result = value.toInt(&ok, 0);
|
||||
if (!ok) result = defaultValue;
|
||||
return qBound(min, result, max);
|
||||
}
|
||||
|
||||
uint32_t toUInt(const QString &value, uint32_t min = 0, uint32_t max = UINT_MAX, uint32_t defaultValue = 0) {
|
||||
bool ok;
|
||||
uint32_t result = value.toUInt(&ok, 0);
|
||||
if (!ok) result = defaultValue;
|
||||
return qBound(min, result, max);
|
||||
}
|
||||
|
||||
QColor toColor(const QString &value, const QColor &defaultValue = QColor(Qt::black)) {
|
||||
if (value.isEmpty()) return QColor();
|
||||
QColor color = QColor("#" + value);
|
||||
if (!color.isValid()) color = defaultValue;
|
||||
return color;
|
||||
}
|
||||
|
||||
bool PorymapConfig::parseLegacyKeyValue(const QString &key, const QString &value) {
|
||||
if (key == "recent_project") {
|
||||
this->recentProjects = value.split(",", Qt::SkipEmptyParts);
|
||||
this->recentProjects.removeDuplicates();
|
||||
} else if (key == "project_manually_closed") {
|
||||
this->projectManuallyClosed = toBool(value);
|
||||
} else if (key == "reopen_on_launch") {
|
||||
this->reopenOnLaunch = toBool(value);
|
||||
} else if (key == "pretty_cursors") {
|
||||
this->prettyCursors = toBool(value);
|
||||
} else if (key == "map_list_tab") {
|
||||
this->mapListTab = toInt(value, 0, 2, 0);
|
||||
} else if (key == "map_list_edit_groups_enabled") {
|
||||
this->mapListEditGroupsEnabled = toBool(value);
|
||||
} else if (key.startsWith("map_list_hide_empty_enabled/")) {
|
||||
bool ok;
|
||||
int tab = key.mid(QStringLiteral("map_list_hide_empty_enabled/").length()).toInt(&ok, 0);
|
||||
if (ok && toBool(value)) this->mapListTabsHidingEmptyFolders.insert(tab);
|
||||
} else if (key == "map_list_layouts_sorted") {
|
||||
this->mapListLayoutsSorted = toBool(value);
|
||||
} else if (key == "map_list_locations_sorted") {
|
||||
this->mapListLocationsSorted = toBool(value);
|
||||
} else if (key == "mirror_connecting_maps") {
|
||||
this->mirrorConnectingMaps = toBool(value);
|
||||
} else if (key == "show_dive_emerge_maps") {
|
||||
this->showDiveEmergeMaps = toBool(value);
|
||||
} else if (key == "dive_emerge_map_opacity") {
|
||||
this->diveEmergeMapOpacity = toInt(value, 10, 90, 30);
|
||||
} else if (key == "dive_map_opacity") {
|
||||
this->diveMapOpacity = toInt(value, 10, 90, 15);
|
||||
} else if (key == "emerge_map_opacity") {
|
||||
this->emergeMapOpacity = toInt(value, 10, 90, 15);
|
||||
} else if (key == "collision_opacity") {
|
||||
this->collisionOpacity = toInt(value, 0, 100, 50);
|
||||
} else if (key == "tileset_editor_geometry") {
|
||||
} else if (key == "metatiles_zoom") {
|
||||
this->metatilesZoom = toInt(value, 10, 100, 30);
|
||||
} else if (key == "collision_zoom") {
|
||||
this->collisionZoom = toInt(value, 10, 100, 30);
|
||||
} else if (key == "tileset_editor_metatiles_zoom") {
|
||||
this->tilesetEditorMetatilesZoom = toInt(value, 10, 100, 30);
|
||||
} else if (key == "tileset_editor_tiles_zoom") {
|
||||
this->tilesetEditorTilesZoom = toInt(value, 10, 100, 30);
|
||||
} else if (key == "tileset_editor_layer_orientation") {
|
||||
// Being explicit here to avoid casting out-of-range values.
|
||||
this->tilesetEditorLayerOrientation = (toInt(value) == static_cast<int>(Qt::Horizontal)) ? Qt::Horizontal : Qt::Vertical;
|
||||
} else if (key == "show_player_view") {
|
||||
this->showPlayerView = toBool(value);
|
||||
} else if (key == "show_cursor_tile") {
|
||||
this->showCursorTile = toBool(value);
|
||||
} else if (key == "show_border") {
|
||||
this->showBorder = toBool(value);
|
||||
} else if (key == "show_grid") {
|
||||
this->showGrid = toBool(value);
|
||||
} else if (key == "show_tileset_editor_metatile_grid") {
|
||||
this->showTilesetEditorMetatileGrid = toBool(value);
|
||||
} else if (key == "show_tileset_editor_layer_grid") {
|
||||
this->showTilesetEditorLayerGrid = toBool(value);
|
||||
} else if (key == "show_tileset_editor_divider") {
|
||||
this->showTilesetEditorDivider = toBool(value);
|
||||
} else if (key == "show_tileset_editor_raw_attributes") {
|
||||
this->showTilesetEditorRawAttributes = toBool(value);
|
||||
} else if (key == "show_palette_editor_unused_colors") {
|
||||
this->showPaletteEditorUnusedColors = toBool(value);
|
||||
} else if (key == "monitor_files") {
|
||||
this->monitorFiles = toBool(value);
|
||||
} else if (key == "tileset_checkerboard_fill") {
|
||||
this->tilesetCheckerboardFill = toBool(value);
|
||||
} else if (key == "new_map_header_section_expanded") {
|
||||
this->newMapHeaderSectionExpanded = toBool(value);
|
||||
} else if (key == "theme") {
|
||||
this->theme = value;
|
||||
} else if (key == "wild_mon_chart_theme") {
|
||||
this->wildMonChartTheme = value;
|
||||
} else if (key == "text_editor_open_directory") {
|
||||
this->textEditorOpenFolder = value;
|
||||
} else if (key == "text_editor_goto_line") {
|
||||
this->textEditorGotoLine = value;
|
||||
} else if (key == "palette_editor_bit_depth") {
|
||||
this->paletteEditorBitDepth = toInt(value, 15, 24, 24);
|
||||
if (this->paletteEditorBitDepth != 15 && this->paletteEditorBitDepth != 24){
|
||||
this->paletteEditorBitDepth = 24;
|
||||
}
|
||||
} else if (key == "project_settings_tab") {
|
||||
this->projectSettingsTab = toInt(value, 0);
|
||||
} else if (key == "load_all_event_scripts") { // Old setting replaced by script_autocomplete_mode
|
||||
this->scriptAutocompleteMode = toBool(value) ? ScriptAutocompleteMode::All : ScriptAutocompleteMode::MapOnly;
|
||||
} else if (key == "script_autocomplete_mode") {
|
||||
this->scriptAutocompleteMode = static_cast<ScriptAutocompleteMode>(toInt(value, ScriptAutocompleteMode::MapOnly, ScriptAutocompleteMode::All));
|
||||
} else if (key == "warp_behavior_warning_disabled") {
|
||||
this->warpBehaviorWarningDisabled = toBool(value);
|
||||
} else if (key == "event_delete_warning_disabled") {
|
||||
this->eventDeleteWarningDisabled = toBool(value);
|
||||
} else if (key == "event_overlay_enabled") {
|
||||
this->eventOverlayEnabled = toBool(value);
|
||||
} else if (key == "check_for_updates") {
|
||||
this->checkForUpdates = toBool(value);
|
||||
} else if (key == "show_project_loading_screen") {
|
||||
this->showProjectLoadingScreen = toBool(value);
|
||||
} else if (key == "last_update_check_time") {
|
||||
this->lastUpdateCheckTime = QDateTime::fromString(value).toLocalTime();
|
||||
} else if (key == "last_update_check_version") {
|
||||
auto version = QVersionNumber::fromString(value);
|
||||
if (version.segmentCount() != 3) {
|
||||
this->lastUpdateCheckVersion = porymapVersion;
|
||||
} else {
|
||||
this->lastUpdateCheckVersion = version;
|
||||
}
|
||||
} else if (key.startsWith("rate_limit_time/")) {
|
||||
static const QRegularExpression regex("\\brate_limit_time/(?<url>.+)");
|
||||
QRegularExpressionMatch match = regex.match(key);
|
||||
if (match.hasMatch()) {
|
||||
this->rateLimitTimes.insert(match.captured("url"), QDateTime::fromString(value).toLocalTime());
|
||||
}
|
||||
} else if (key == "event_selection_shape_mode") {
|
||||
if (value == "mask") {
|
||||
this->eventSelectionShapeMode = QGraphicsPixmapItem::MaskShape;
|
||||
} else if (value == "bounding_rect") {
|
||||
this->eventSelectionShapeMode = QGraphicsPixmapItem::BoundingRectShape;
|
||||
}
|
||||
} else if (key == "shown_in_game_reload_message") {
|
||||
this->shownInGameReloadMessage = toBool(value);
|
||||
} else if (key == "grid_width") {
|
||||
this->gridSettings.width = toUInt(value);
|
||||
} else if (key == "grid_height") {
|
||||
this->gridSettings.height = toUInt(value);
|
||||
} else if (key == "grid_x") {
|
||||
this->gridSettings.offsetX = toInt(value, 0, 999);
|
||||
} else if (key == "grid_y") {
|
||||
this->gridSettings.offsetY = toInt(value, 0, 999);
|
||||
} else if (key == "grid_style") {
|
||||
this->gridSettings.style = GridSettings::getStyleFromName(value);
|
||||
} else if (key == "grid_color") {
|
||||
this->gridSettings.color = toColor(value);
|
||||
} else if (key == "status_bar_log_types") {
|
||||
this->statusBarLogTypes.clear();
|
||||
auto typeStrings = value.split(",", Qt::SkipEmptyParts);
|
||||
for (const auto &typeString : typeStrings) {
|
||||
LogType type = static_cast<LogType>(toInt(typeString, 0, 2));
|
||||
this->statusBarLogTypes.insert(type);
|
||||
}
|
||||
} else if (key == "application_font") {
|
||||
this->applicationFont = QFont();
|
||||
this->applicationFont.fromString(value);
|
||||
} else if (key == "map_list_font") {
|
||||
this->mapListFont = QFont();
|
||||
this->mapListFont.fromString(value);
|
||||
} else if (key == "image_export_color_space_id") {
|
||||
this->imageExportColorSpaceId = toInt(value, 0, 8);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProjectConfig::parseLegacyKeyValue(const QString &key, const QString &value_) {
|
||||
QString value(value_);
|
||||
if (key == "base_game_version") {
|
||||
this->baseGameVersion = BaseGame::stringToVersion(value);
|
||||
} else if (key == "use_poryscript") {
|
||||
this->usePoryScript = toBool(value);
|
||||
} else if (key == "use_custom_border_size") {
|
||||
this->useCustomBorderSize = toBool(value);
|
||||
} else if (key == "enable_event_weather_trigger") {
|
||||
this->eventWeatherTriggerEnabled = toBool(value);
|
||||
} else if (key == "enable_event_secret_base") {
|
||||
this->eventSecretBaseEnabled = toBool(value);
|
||||
} else if (key == "enable_hidden_item_quantity") {
|
||||
this->hiddenItemQuantityEnabled = toBool(value);
|
||||
} else if (key == "enable_hidden_item_requires_itemfinder") {
|
||||
this->hiddenItemRequiresItemfinderEnabled = toBool(value);
|
||||
} else if (key == "enable_heal_location_respawn_data") {
|
||||
this->healLocationRespawnDataEnabled = toBool(value);
|
||||
} else if (key == "enable_event_clone_object" || key == "enable_object_event_in_connection") {
|
||||
this->eventCloneObjectEnabled = toBool(value);
|
||||
} else if (key == "enable_floor_number") {
|
||||
this->floorNumberEnabled = toBool(value);
|
||||
} else if (key == "create_map_text_file") {
|
||||
this->createMapTextFileEnabled = toBool(value);
|
||||
} else if (key == "enable_triple_layer_metatiles") {
|
||||
this->tripleLayerMetatilesEnabled = toBool(value);
|
||||
} else if (key == "default_metatile") {
|
||||
this->defaultMetatileId = toUInt(value, 0, Block::MaxValue);
|
||||
} else if (key == "default_elevation") {
|
||||
this->defaultElevation = toUInt(value, 0, Block::MaxValue);
|
||||
} else if (key == "default_collision") {
|
||||
this->defaultCollision = toUInt(value, 0, Block::MaxValue);
|
||||
} else if (key == "default_map_width") {
|
||||
this->defaultMapSize.setWidth(toInt(value, 1));
|
||||
} else if (key == "default_map_height") {
|
||||
this->defaultMapSize.setHeight(toInt(value, 1));
|
||||
} else if (key == "new_map_border_metatiles") {
|
||||
this->newMapBorderMetatileIds.clear();
|
||||
QList<QString> metatileIds = value.split(",");
|
||||
for (int i = 0; i < metatileIds.size(); i++) {
|
||||
int metatileId = toUInt(metatileIds.at(i), 0, Block::MaxValue);
|
||||
this->newMapBorderMetatileIds.append(metatileId);
|
||||
}
|
||||
} else if (key == "default_primary_tileset") {
|
||||
this->defaultPrimaryTileset = value;
|
||||
} else if (key == "default_secondary_tileset") {
|
||||
this->defaultSecondaryTileset = value;
|
||||
} else if (key == "metatile_attributes_size") {
|
||||
int size = toInt(value, 1, 4, 2);
|
||||
if (!(size & (size - 1))) this->metatileAttributesSize = size;
|
||||
} else if (key == "metatile_behavior_mask") {
|
||||
this->metatileBehaviorMask = toUInt(value);
|
||||
} else if (key == "metatile_terrain_type_mask") {
|
||||
this->metatileTerrainTypeMask = toUInt(value);
|
||||
} else if (key == "metatile_encounter_type_mask") {
|
||||
this->metatileEncounterTypeMask = toUInt(value);
|
||||
} else if (key == "metatile_layer_type_mask") {
|
||||
this->metatileLayerTypeMask = toUInt(value);
|
||||
} else if (key == "block_metatile_id_mask") {
|
||||
this->blockMetatileIdMask = toUInt(value, 0, Block::MaxValue);
|
||||
} else if (key == "block_collision_mask") {
|
||||
this->blockCollisionMask = toUInt(value, 0, Block::MaxValue);
|
||||
} else if (key == "block_elevation_mask") {
|
||||
this->blockElevationMask = toUInt(value, 0, Block::MaxValue);
|
||||
} else if (key == "unused_tile_normal") {
|
||||
this->unusedTileNormal = toUInt(value, 0, Tile::MaxValue);
|
||||
} else if (key == "unused_tile_covered") {
|
||||
this->unusedTileCovered = toUInt(value, 0, Tile::MaxValue);
|
||||
} else if (key == "unused_tile_split") {
|
||||
this->unusedTileSplit = toUInt(value, 0, Tile::MaxValue);
|
||||
} else if (key == "enable_map_allow_flags") {
|
||||
this->mapAllowFlagsEnabled = toBool(value);
|
||||
} else if (key.startsWith("path/")) {
|
||||
auto k = reverseDefaultPaths(key.mid(QStringLiteral("path/").length()));
|
||||
if (k != static_cast<ProjectFilePath>(-1)) {
|
||||
this->setFilePath(k, value);
|
||||
}
|
||||
} else if (key.startsWith("ident/")) {
|
||||
auto identifierId = reverseDefaultIdentifier(key.mid(QStringLiteral("ident/").length()));
|
||||
if (identifierId != static_cast<ProjectIdentifier>(-1)) {
|
||||
this->setIdentifier(identifierId, value);
|
||||
}
|
||||
} else if (key.startsWith("global_constant/")) {
|
||||
this->globalConstants.insert(key.mid(QStringLiteral("global_constant/").length()), value);
|
||||
} else if (key == "global_constants_filepaths") {
|
||||
this->globalConstantsFilepaths = value.split(",", Qt::SkipEmptyParts);
|
||||
} else if (key == "tilesets_have_callback") {
|
||||
this->tilesetsHaveCallback = toBool(value);
|
||||
} else if (key == "tilesets_have_is_compressed") {
|
||||
this->tilesetsHaveIsCompressed = toBool(value);
|
||||
} else if (key == "set_transparent_pixels_black") { // Old setting replaced by transparency_color
|
||||
this->transparencyColor = toBool(value) ? QColor(Qt::black) : QColor();
|
||||
} else if (key == "transparency_color") {
|
||||
this->transparencyColor = toColor(value);
|
||||
} else if (key == "preserve_matching_only_data") {
|
||||
this->preserveMatchingOnlyData = toBool(value);
|
||||
} else if (key == "event_icon_path_object") {
|
||||
this->eventIconPaths[Event::Group::Object] = value;
|
||||
} else if (key == "event_icon_path_warp") {
|
||||
this->eventIconPaths[Event::Group::Warp] = value;
|
||||
} else if (key == "event_icon_path_coord") {
|
||||
this->eventIconPaths[Event::Group::Coord] = value;
|
||||
} else if (key == "event_icon_path_bg") {
|
||||
this->eventIconPaths[Event::Group::Bg] = value;
|
||||
} else if (key == "event_icon_path_heal") {
|
||||
this->eventIconPaths[Event::Group::Heal] = value;
|
||||
} else if (key.startsWith("pokemon_icon_path/")) {
|
||||
this->pokemonIconPaths.insert(key.mid(QStringLiteral("pokemon_icon_path/").length()), value);
|
||||
} else if (key == "events_tab_icon_path") {
|
||||
this->eventsTabIconPath = value;
|
||||
} else if (key == "collision_sheet_path") {
|
||||
this->collisionSheetPath = value;
|
||||
} else if (key == "collision_sheet_width") {
|
||||
this->collisionSheetSize.setWidth(toInt(value, 1, Block::MaxValue));
|
||||
} else if (key == "collision_sheet_height") {
|
||||
this->collisionSheetSize.setHeight(toInt(value, 1, Block::MaxValue));
|
||||
} else if (key == "player_view_north") {
|
||||
this->playerViewDistance.setTop(toInt(value, 0, INT_MAX, GBA_V_DIST_TO_CENTER));
|
||||
} else if (key == "player_view_south") {
|
||||
this->playerViewDistance.setBottom(toInt(value, 0, INT_MAX, GBA_V_DIST_TO_CENTER));
|
||||
} else if (key == "player_view_west") {
|
||||
this->playerViewDistance.setLeft(toInt(value, 0, INT_MAX, GBA_H_DIST_TO_CENTER));
|
||||
} else if (key == "player_view_east") {
|
||||
this->playerViewDistance.setRight(toInt(value, 0, INT_MAX, GBA_H_DIST_TO_CENTER));
|
||||
} else if (key == "warp_behaviors") {
|
||||
this->warpBehaviors.clear();
|
||||
value.remove(" ");
|
||||
const QStringList behaviorList = value.split(",", Qt::SkipEmptyParts);
|
||||
for (auto s : behaviorList)
|
||||
this->warpBehaviors.insert(toUInt(s));
|
||||
} else if (key == "max_events_per_group") {
|
||||
this->maxEventsPerGroup = toInt(value, 1, INT_MAX, 255);
|
||||
} else if (key == "metatile_selector_width") {
|
||||
this->metatileSelectorWidth = toInt(value, 1, INT_MAX, 8);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Read input from the config to get the script paths and whether each is enabled or disbled.
|
||||
// The format is a comma-separated list of paths. Each path can be followed (before the comma)
|
||||
// by a :0 or :1 to indicate whether it should be disabled or enabled, respectively. If neither
|
||||
// follow, it's assumed the script should be enabled.
|
||||
QList<ScriptSettings> parseCustomScripts(const QString &input) {
|
||||
QMap<QString,bool> customScripts;
|
||||
const QList<QString> paths = input.split(",", Qt::SkipEmptyParts);
|
||||
for (QString path : paths) {
|
||||
// Read and remove suffix
|
||||
bool enabled = !path.endsWith(":0");
|
||||
if (!enabled || path.endsWith(":1"))
|
||||
path.chop(2);
|
||||
|
||||
if (!path.isEmpty()) {
|
||||
// If a path is repeated only its last instance will be considered.
|
||||
customScripts.insert(path, enabled);
|
||||
}
|
||||
}
|
||||
QList<ScriptSettings> settingsList;
|
||||
for (auto it = customScripts.begin(); it != customScripts.end(); it++) {
|
||||
settingsList.append({
|
||||
.path = it.key(),
|
||||
.enabled = it.value(),
|
||||
.userOnly = true,
|
||||
});
|
||||
}
|
||||
return settingsList;
|
||||
}
|
||||
|
||||
bool UserConfig::parseLegacyKeyValue(const QString &key, const QString &value) {
|
||||
if (key == "recent_map_or_layout") {
|
||||
this->recentMapOrLayout = value;
|
||||
} else if (key == "use_encounter_json") {
|
||||
this->useEncounterJson = toBool(value);
|
||||
} else if (key == "custom_scripts") {
|
||||
this->customScripts = parseCustomScripts(value);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ShortcutsConfig::parseLegacyKeyValue(const QString &key, const QString &value) {
|
||||
QStringList keySequences = value.split(' ');
|
||||
for (auto keySequence : keySequences)
|
||||
user_shortcuts.insert(key, keySequence);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
#include "log.h"
|
||||
#include "project.h"
|
||||
#include "maplayout.h"
|
||||
#include "config.h"
|
||||
|
||||
Layout *AdvanceMapParser::parseLayout(const QString &filepath, bool *error, const Project *project)
|
||||
{
|
||||
|
|
@ -118,18 +119,18 @@ QList<Metatile*> AdvanceMapParser::parseMetatiles(const QString &filepath, bool
|
|||
|
||||
int projIdOffset = in.length() - 4;
|
||||
int metatileSize = 16;
|
||||
BaseGameVersion version;
|
||||
BaseGame::Version version;
|
||||
if (in.at(projIdOffset + 0) == 'R'
|
||||
&& in.at(projIdOffset + 1) == 'S'
|
||||
&& in.at(projIdOffset + 2) == 'E'
|
||||
&& in.at(projIdOffset + 3) == ' ') {
|
||||
// ruby and emerald are handled equally here.
|
||||
version = BaseGameVersion::pokeemerald;
|
||||
version = BaseGame::Version::pokeemerald;
|
||||
} else if (in.at(projIdOffset + 0) == 'F'
|
||||
&& in.at(projIdOffset + 1) == 'R'
|
||||
&& in.at(projIdOffset + 2) == 'L'
|
||||
&& in.at(projIdOffset + 3) == 'G') {
|
||||
version = BaseGameVersion::pokefirered;
|
||||
version = BaseGame::Version::pokefirered;
|
||||
} else {
|
||||
*error = true;
|
||||
logError(QString("Detected unsupported game type from .bvd file. Last 4 bytes of file must be 'RSE ' or 'FRLG'."));
|
||||
|
|
|
|||
63
src/core/basegame.cpp
Normal file
63
src/core/basegame.cpp
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
#include "basegame.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
|
||||
// If a string exclusively contains one version name we assume its identity,
|
||||
// otherwise we leave it unknown and we'll need the user to tell us the version.
|
||||
BaseGame::Version BaseGame::stringToVersion(const QString &input_) {
|
||||
static const QMap<Version, QStringList> versionDetectNames = {
|
||||
{Version::pokeruby, {"ruby", "sapphire"}},
|
||||
{Version::pokefirered, {"firered", "leafgreen"}},
|
||||
{Version::pokeemerald, {"emerald"}},
|
||||
};
|
||||
|
||||
const QString input(input_.toLower());
|
||||
BaseGame::Version version = BaseGame::Version::none;
|
||||
for (auto it = versionDetectNames.begin(); it != versionDetectNames.end(); it++) {
|
||||
// Compare the given string to all the possible names for this game version
|
||||
for (const auto &name : it.value()) {
|
||||
if (input.contains(name)) {
|
||||
if (version != BaseGame::Version::none) {
|
||||
// The given string matches multiple versions, so we can't be sure which it is.
|
||||
return BaseGame::Version::none;
|
||||
}
|
||||
version = it.key();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// We finished checking the names for each version; the name either matched 1 version or none.
|
||||
return version;
|
||||
}
|
||||
|
||||
// TODO: Make sure empty string is ok everywhere this is used
|
||||
QString BaseGame::versionToString(BaseGame::Version version) {
|
||||
static const QMap<Version, QString> map = {
|
||||
{Version::pokeruby, "pokeruby"},
|
||||
{Version::pokefirered, "pokefirered"},
|
||||
{Version::pokeemerald, "pokeemerald"},
|
||||
};
|
||||
return map.value(version);
|
||||
}
|
||||
|
||||
QString BaseGame::getPlayerIconPath(BaseGame::Version version, int character) {
|
||||
if (version == BaseGame::Version::pokeemerald) {
|
||||
static const QStringList paths = { QStringLiteral(":/icons/player/brendan_em.ico"),
|
||||
QStringLiteral(":/icons/player/may_em.ico"), };
|
||||
return paths.value(character);
|
||||
} else if (version == BaseGame::Version::pokefirered) {
|
||||
static const QStringList paths = { QStringLiteral(":/icons/player/red.ico"),
|
||||
QStringLiteral(":/icons/player/green.ico"), };
|
||||
return paths.value(character);
|
||||
} else if (version == BaseGame::Version::pokeruby) {
|
||||
static const QStringList paths = { QStringLiteral(":/icons/player/brendan_rs.ico"),
|
||||
QStringLiteral(":/icons/player/may_rs.ico"), };
|
||||
return paths.value(character);
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
QIcon BaseGame::getPlayerIcon(BaseGame::Version baseGameVersion, int character) {
|
||||
return QIcon(getPlayerIconPath(baseGameVersion, character));
|
||||
}
|
||||
|
|
@ -2,12 +2,10 @@
|
|||
#include "bitpacker.h"
|
||||
#include "config.h"
|
||||
|
||||
// Upper limit for metatile ID, collision, and elevation masks. Used externally.
|
||||
const uint16_t Block::maxValue = 0xFFFF;
|
||||
|
||||
static BitPacker bitsMetatileId = BitPacker(0x3FF);
|
||||
static BitPacker bitsCollision = BitPacker(0xC00);
|
||||
static BitPacker bitsElevation = BitPacker(0xF000);
|
||||
static BitPacker bitsMetatileId = BitPacker(Block::DefaultMetatileIdMask);
|
||||
static BitPacker bitsCollision = BitPacker(Block::DefaultCollisionMask);
|
||||
static BitPacker bitsElevation = BitPacker(Block::DefaultElevationMask);
|
||||
|
||||
Block::Block() :
|
||||
m_metatileId(0),
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ QPoint Metatile::coordFromPixmapCoord(const QPointF &pixelCoord) {
|
|||
|
||||
static int numMetatileIdChars = 4;
|
||||
QString Metatile::getMetatileIdString(uint16_t metatileId) {
|
||||
return /*porymapConfig.displayIdsHexadecimal*/ConfigDisplayIdsHexadecimal
|
||||
return porymapConfig.displayIdsHexadecimal
|
||||
? Util::toHexString(metatileId, numMetatileIdChars)
|
||||
: QString::number(metatileId);
|
||||
};
|
||||
|
|
@ -62,6 +62,10 @@ QString Metatile::getLayerName(int layerNum) {
|
|||
return layerTitles.value(layerNum);
|
||||
}
|
||||
|
||||
int Metatile::numLayers() {
|
||||
return projectConfig.tripleLayerMetatilesEnabled ? 3 : 2;
|
||||
}
|
||||
|
||||
// Read and pack together this metatile's attributes.
|
||||
uint32_t Metatile::getAttributes() const {
|
||||
uint32_t data = 0;
|
||||
|
|
@ -81,8 +85,8 @@ void Metatile::setAttributes(uint32_t data) {
|
|||
}
|
||||
|
||||
// Unpack and insert metatile attributes from the given data using a vanilla layout. For AdvanceMap import
|
||||
void Metatile::setAttributes(uint32_t data, BaseGameVersion version) {
|
||||
const auto vanillaPackers = (version == BaseGameVersion::pokefirered) ? attributePackersFRLG : attributePackersRSE;
|
||||
void Metatile::setAttributes(uint32_t data, BaseGame::Version version) {
|
||||
const auto vanillaPackers = (version == BaseGame::Version::pokefirered) ? attributePackersFRLG : attributePackersRSE;
|
||||
for (auto i = vanillaPackers.cbegin(), end = vanillaPackers.cend(); i != end; i++){
|
||||
const auto packer = i.value();
|
||||
this->setAttribute(i.key(), packer.unpack(data));
|
||||
|
|
@ -95,12 +99,12 @@ void Metatile::setAttribute(Metatile::Attr attr, uint32_t value) {
|
|||
this->attributes.insert(attr, packer.clamp(value));
|
||||
}
|
||||
|
||||
int Metatile::getDefaultAttributesSize(BaseGameVersion version) {
|
||||
return (version == BaseGameVersion::pokefirered) ? 4 : 2;
|
||||
int Metatile::getDefaultAttributesSize(BaseGame::Version version) {
|
||||
return (version == BaseGame::Version::pokefirered) ? 4 : 2;
|
||||
}
|
||||
|
||||
uint32_t Metatile::getDefaultAttributesMask(BaseGameVersion version, Metatile::Attr attr) {
|
||||
const auto vanillaPackers = (version == BaseGameVersion::pokefirered) ? attributePackersFRLG : attributePackersRSE;
|
||||
uint32_t Metatile::getDefaultAttributesMask(BaseGame::Version version, Metatile::Attr attr) {
|
||||
const auto vanillaPackers = (version == BaseGame::Version::pokefirered) ? attributePackersFRLG : attributePackersRSE;
|
||||
return vanillaPackers.value(attr).mask();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,11 +2,6 @@
|
|||
#include "project.h"
|
||||
#include "bitpacker.h"
|
||||
|
||||
bool ConfigDisplayIdsHexadecimal = true;
|
||||
|
||||
// Upper limit for raw value (i.e., uint16_t max).
|
||||
const uint16_t Tile::maxValue = 0xFFFF;
|
||||
|
||||
// At the moment these are fixed, and not exposed to the user.
|
||||
// We're only using them for convenience when converting between raw values.
|
||||
// The actual job of clamping Tile's members to correct values is handled by the widths in the bit field.
|
||||
|
|
@ -76,7 +71,7 @@ QString Tile::toString() const {
|
|||
}
|
||||
|
||||
QString Tile::getTileIdString(uint16_t tileId) {
|
||||
return /*porymapConfig.displayIdsHexadecimal*/ConfigDisplayIdsHexadecimal
|
||||
return porymapConfig.displayIdsHexadecimal
|
||||
? Util::toHexString(tileId, 3)
|
||||
: QString::number(tileId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ void Tileset::resizeMetatiles(int newNumMetatiles) {
|
|||
while (m_metatiles.length() > newNumMetatiles) {
|
||||
delete m_metatiles.takeLast();
|
||||
}
|
||||
const int numTiles = projectConfig.getNumTilesInMetatile();
|
||||
const int numTiles = Metatile::maxTiles();
|
||||
while (m_metatiles.length() < newNumMetatiles) {
|
||||
m_metatiles.append(new Metatile(numTiles));
|
||||
}
|
||||
|
|
@ -322,7 +322,7 @@ bool Tileset::appendToHeaders(const QString &filepath, const QString &friendlyNa
|
|||
dataString.append(QString("\t.4byte gTilesetTiles_%1\n").arg(friendlyName));
|
||||
dataString.append(QString("\t.4byte gTilesetPalettes_%1\n").arg(friendlyName));
|
||||
dataString.append(QString("\t.4byte gMetatiles_%1\n").arg(friendlyName));
|
||||
if (projectConfig.baseGameVersion == BaseGameVersion::pokefirered) {
|
||||
if (projectConfig.baseGameVersion == BaseGame::Version::pokefirered) {
|
||||
dataString.append("\t.4byte NULL @ animation callback\n");
|
||||
dataString.append(QString("\t.4byte gMetatileAttributes_%1\n").arg(friendlyName));
|
||||
} else {
|
||||
|
|
@ -439,7 +439,7 @@ QHash<int, QString> Tileset::getHeaderMemberMap(bool usingAsm)
|
|||
int paddingOffset = usingAsm ? 1 : 0;
|
||||
|
||||
// The position of metatileAttributes changes between games
|
||||
bool isPokefirered = (projectConfig.baseGameVersion == BaseGameVersion::pokefirered);
|
||||
bool isPokefirered = (projectConfig.baseGameVersion == BaseGame::Version::pokefirered);
|
||||
int metatileAttrPosition = (isPokefirered ? 6 : 5) + paddingOffset;
|
||||
|
||||
auto map = QHash<int, QString>();
|
||||
|
|
@ -461,7 +461,7 @@ bool Tileset::loadMetatiles() {
|
|||
}
|
||||
|
||||
QByteArray data = file.readAll();
|
||||
int tilesPerMetatile = projectConfig.getNumTilesInMetatile();
|
||||
int tilesPerMetatile = Metatile::maxTiles();
|
||||
int bytesPerMetatile = Tile::sizeInBytes() * tilesPerMetatile;
|
||||
int numMetatiles = data.length() / bytesPerMetatile;
|
||||
if (numMetatiles > maxMetatiles()) {
|
||||
|
|
@ -493,7 +493,7 @@ bool Tileset::saveMetatiles() {
|
|||
}
|
||||
|
||||
QByteArray data;
|
||||
int numTiles = projectConfig.getNumTilesInMetatile();
|
||||
int numTiles = Metatile::maxTiles();
|
||||
for (const auto &metatile : m_metatiles) {
|
||||
for (int i = 0; i < numTiles; i++) {
|
||||
uint16_t tile = metatile->tiles.value(i).rawValue();
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ MainWindow::MainWindow(QWidget *parent) :
|
|||
|
||||
void MainWindow::initialize() {
|
||||
this->initWindow();
|
||||
this->installEventFilter(new GeometrySaver(this));
|
||||
if (porymapConfig.reopenOnLaunch && !porymapConfig.projectManuallyClosed && this->openProject(porymapConfig.getRecentProject(), true)) {
|
||||
on_toolButton_Paint_clicked();
|
||||
}
|
||||
|
|
@ -92,7 +93,7 @@ void MainWindow::initialize() {
|
|||
if (porymapConfig.checkForUpdates)
|
||||
this->checkForUpdates(false);
|
||||
|
||||
this->restoreWindowState();
|
||||
this->resizeWithinScreen();
|
||||
this->show();
|
||||
}
|
||||
|
||||
|
|
@ -109,13 +110,6 @@ MainWindow::~MainWindow()
|
|||
}
|
||||
|
||||
void MainWindow::saveGlobalConfigs() {
|
||||
porymapConfig.setMainGeometry(
|
||||
this->saveGeometry(),
|
||||
this->saveState(),
|
||||
this->ui->splitter_map->saveState(),
|
||||
this->ui->splitter_main->saveState(),
|
||||
this->ui->splitter_Metatiles->saveState()
|
||||
);
|
||||
porymapConfig.save();
|
||||
shortcutsConfig.save();
|
||||
}
|
||||
|
|
@ -185,6 +179,7 @@ void MainWindow::setWindowDisabled(bool disabled) {
|
|||
}
|
||||
|
||||
void MainWindow::initWindow() {
|
||||
porymapConfig = PorymapConfig();
|
||||
porymapConfig.load();
|
||||
this->initLogStatusBar();
|
||||
this->initCustomUI();
|
||||
|
|
@ -227,6 +222,7 @@ void MainWindow::initWindow() {
|
|||
void MainWindow::initShortcuts() {
|
||||
initExtraShortcuts();
|
||||
|
||||
shortcutsConfig = ShortcutsConfig();
|
||||
shortcutsConfig.load();
|
||||
shortcutsConfig.setDefaultShortcuts(shortcutableObjects());
|
||||
applyUserShortcuts();
|
||||
|
|
@ -303,9 +299,8 @@ void MainWindow::applyUserShortcuts() {
|
|||
|
||||
void MainWindow::initLogStatusBar() {
|
||||
removeLogStatusBar(this->statusBar());
|
||||
auto logTypes = QSet<LogType>(porymapConfig.statusBarLogTypes.begin(), porymapConfig.statusBarLogTypes.end());
|
||||
if (!logTypes.isEmpty()) {
|
||||
addLogStatusBar(this->statusBar(), logTypes);
|
||||
if (!porymapConfig.statusBarLogTypes.isEmpty()) {
|
||||
addLogStatusBar(this->statusBar(), porymapConfig.statusBarLogTypes.toQSet());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -320,7 +315,7 @@ void MainWindow::initCustomUI() {
|
|||
|
||||
static const QMap<int, QIcon> mainTabIcons = {
|
||||
{MainTab::Map, QIcon(QStringLiteral(":/icons/minimap.ico"))},
|
||||
{MainTab::Events, ProjectConfig::getPlayerIcon(BaseGameVersion::pokefirered, 0)}, // Arbitrary default
|
||||
{MainTab::Events, BaseGame::getPlayerIcon(BaseGame::Version::pokefirered, 0)}, // Arbitrary default
|
||||
{MainTab::Header, QIcon(QStringLiteral(":/icons/application_form_edit.ico"))},
|
||||
{MainTab::Connections, QIcon(QStringLiteral(":/icons/connections.ico"))},
|
||||
{MainTab::WildPokemon, QIcon(QStringLiteral(":/icons/tall_grass.ico"))},
|
||||
|
|
@ -591,9 +586,9 @@ void MainWindow::initMapList() {
|
|||
|
||||
// Initialize settings from config
|
||||
ui->mapListToolBar_Groups->setEditsAllowed(porymapConfig.mapListEditGroupsEnabled);
|
||||
for (auto i = porymapConfig.mapListHideEmptyEnabled.constBegin(); i != porymapConfig.mapListHideEmptyEnabled.constEnd(); i++) {
|
||||
auto toolbar = getMapListToolBar(i.key());
|
||||
if (toolbar) toolbar->setEmptyFoldersVisible(!i.value());
|
||||
for (const auto &tab : porymapConfig.mapListTabsHidingEmptyFolders) {
|
||||
auto toolbar = getMapListToolBar(tab);
|
||||
if (toolbar) toolbar->setEmptyFoldersVisible(false);
|
||||
}
|
||||
|
||||
// Update config if map list settings change
|
||||
|
|
@ -601,13 +596,16 @@ void MainWindow::initMapList() {
|
|||
porymapConfig.mapListEditGroupsEnabled = allowed;
|
||||
});
|
||||
connect(ui->mapListToolBar_Groups, &MapListToolBar::emptyFoldersVisibleChanged, [](bool visible) {
|
||||
porymapConfig.mapListHideEmptyEnabled[MapListTab::Groups] = !visible;
|
||||
if (visible) porymapConfig.mapListTabsHidingEmptyFolders.erase(MapListTab::Groups);
|
||||
else porymapConfig.mapListTabsHidingEmptyFolders.insert(MapListTab::Groups);
|
||||
});
|
||||
connect(ui->mapListToolBar_Locations, &MapListToolBar::emptyFoldersVisibleChanged, [](bool visible) {
|
||||
porymapConfig.mapListHideEmptyEnabled[MapListTab::Locations] = !visible;
|
||||
if (visible) porymapConfig.mapListTabsHidingEmptyFolders.erase(MapListTab::Locations);
|
||||
else porymapConfig.mapListTabsHidingEmptyFolders.insert(MapListTab::Locations);
|
||||
});
|
||||
connect(ui->mapListToolBar_Layouts, &MapListToolBar::emptyFoldersVisibleChanged, [](bool visible) {
|
||||
porymapConfig.mapListHideEmptyEnabled[MapListTab::Layouts] = !visible;
|
||||
if (visible) porymapConfig.mapListTabsHidingEmptyFolders.erase(MapListTab::Layouts);
|
||||
else porymapConfig.mapListTabsHidingEmptyFolders.insert(MapListTab::Layouts);
|
||||
});
|
||||
|
||||
// When map list search filter is cleared we want the current map/layout in the editor to be visible in the list.
|
||||
|
|
@ -729,19 +727,8 @@ void MainWindow::loadUserSettings() {
|
|||
refreshRecentProjectsMenu();
|
||||
}
|
||||
|
||||
void MainWindow::restoreWindowState() {
|
||||
QMap<QString, QByteArray> geometry = porymapConfig.getMainGeometry();
|
||||
const QByteArray mainWindowGeometry = geometry.value("main_window_geometry");
|
||||
if (!mainWindowGeometry.isEmpty()) {
|
||||
logInfo("Restoring main window geometry from previous session.");
|
||||
restoreGeometry(mainWindowGeometry);
|
||||
restoreState(geometry.value("main_window_state"));
|
||||
ui->splitter_map->restoreState(geometry.value("map_splitter_state"));
|
||||
ui->splitter_main->restoreState(geometry.value("main_splitter_state"));
|
||||
ui->splitter_Metatiles->restoreState(geometry.value("metatiles_splitter_state"));
|
||||
}
|
||||
|
||||
// Resize the window if it exceeds the available screen size.
|
||||
// Resize the window if it exceeds the available screen size.
|
||||
void MainWindow::resizeWithinScreen() {
|
||||
auto screen = windowHandle() ? windowHandle()->screen() : QGuiApplication::primaryScreen();
|
||||
if (!screen) return;
|
||||
const QRect screenGeometry = screen->availableGeometry();
|
||||
|
|
@ -800,14 +787,14 @@ bool MainWindow::openProject(QString dir, bool initial) {
|
|||
logInfo("Aborted project open.");
|
||||
return false;
|
||||
}
|
||||
|
||||
const QString openMessage = QString("Opening %1").arg(projectString);
|
||||
logInfo(openMessage);
|
||||
logInfo(QString("Opening %1").arg(projectString));
|
||||
|
||||
if (porymapConfig.showProjectLoadingScreen) porysplash->start();
|
||||
|
||||
porysplash->showLoadingMessage("config");
|
||||
if (!projectConfig.load(dir) || !userConfig.load(dir)) {
|
||||
projectConfig = ProjectConfig(dir);
|
||||
userConfig = UserConfig(dir);
|
||||
if (!projectConfig.load() || !userConfig.load()) {
|
||||
showProjectOpenFailure();
|
||||
porysplash->stop();
|
||||
return false;
|
||||
|
|
@ -845,9 +832,6 @@ bool MainWindow::openProject(QString dir, bool initial) {
|
|||
porysplash->stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only create the config files once the project has opened successfully in case the user selected an invalid directory
|
||||
this->editor->project->saveConfig();
|
||||
|
||||
updateWindowTitle();
|
||||
|
||||
|
|
@ -899,24 +883,24 @@ bool MainWindow::checkProjectSanity(Project *project) {
|
|||
|
||||
bool MainWindow::checkProjectVersion(Project *project) {
|
||||
QString error;
|
||||
int projectVersion = project->getSupportedMajorVersion(&error);
|
||||
if (projectVersion < 0) {
|
||||
// Failed to identify a supported major version.
|
||||
QVersionNumber minimumVersion = project->getMinimumVersion(&error);
|
||||
if (!error.isEmpty()) {
|
||||
// Failed to identify a supported version.
|
||||
// We can't draw any conclusions from this, so we don't consider the project to be invalid.
|
||||
QString msg = QStringLiteral("Failed to check project version");
|
||||
logWarn(error.isEmpty() ? msg : QString("%1: '%2'").arg(msg).arg(error));
|
||||
logWarn(QString("Failed to check project version: '%1'").arg(error));
|
||||
} else {
|
||||
QString msg = QStringLiteral("Successfully checked project version. ");
|
||||
logInfo(msg + ((projectVersion != 0) ? QString("Supports at least Porymap v%1").arg(projectVersion)
|
||||
: QStringLiteral("Too old for any Porymap version")));
|
||||
|
||||
if (projectVersion < porymapVersion.majorVersion() && projectConfig.forcedMajorVersion < porymapVersion.majorVersion()) {
|
||||
if (minimumVersion.isNull()) {
|
||||
logInfo(QStringLiteral("Successfully checked project version. Too old for any Porymap version"));
|
||||
} else {
|
||||
logInfo(QString("Successfully checked project version. Supports at least Porymap v%1").arg(minimumVersion.toString()));
|
||||
}
|
||||
if (minimumVersion > porymapVersion || minimumVersion.majorVersion() != porymapVersion.majorVersion()) {
|
||||
// We were unable to find the necessary changes for Porymap's current major version.
|
||||
// Unless they have explicitly suppressed this message, warn the user that this might mean their project is missing breaking changes.
|
||||
// Note: Do not report 'projectVersion' to the user in this message. We've already logged it for troubleshooting.
|
||||
// Note: Do not report 'minimumVersion' to the user in this message. We've already logged it for troubleshooting.
|
||||
// It is very plausible that the user may have reproduced the required changes in an
|
||||
// unknown commit, rather than merging the required changes directly from the base repo.
|
||||
// In this case the 'projectVersion' may actually be too old to use for their repo.
|
||||
// In this case the 'minimumVersion' may actually be too old to use for their repo.
|
||||
ErrorMessage msgBox(QStringLiteral("Your project may be incompatible!"), porysplash);
|
||||
msgBox.setTextFormat(Qt::RichText);
|
||||
msgBox.setInformativeText(QString("Make sure '%1' has all the required changes for Porymap version %2.<br>"
|
||||
|
|
@ -929,7 +913,7 @@ bool MainWindow::checkProjectVersion(Project *project) {
|
|||
return false;
|
||||
}
|
||||
// User opted to try with this version anyway. Don't warn them about this version again.
|
||||
projectConfig.forcedMajorVersion = porymapVersion.majorVersion();
|
||||
projectConfig.minimumVersion = QVersionNumber(porymapVersion.majorVersion());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
@ -939,7 +923,7 @@ void MainWindow::showProjectOpenFailure() {
|
|||
if (!this->isVisible()){
|
||||
// The main window is not visible during the initial project open; the splash screen is busy providing visual feedback.
|
||||
// If project opening fails we can immediately display the empty main window (which we need anyway to parent messages to).
|
||||
restoreWindowState();
|
||||
resizeWithinScreen();
|
||||
show();
|
||||
}
|
||||
RecentErrorMessage::show(QStringLiteral("There was an error opening the project."), this);
|
||||
|
|
@ -1491,7 +1475,7 @@ bool MainWindow::setProjectUI() {
|
|||
this->mapGroupModel = new MapGroupModel(editor->project);
|
||||
this->groupListProxyModel = new FilterChildrenProxyModel();
|
||||
this->groupListProxyModel->setSourceModel(this->mapGroupModel);
|
||||
this->groupListProxyModel->setHideEmpty(porymapConfig.mapListHideEmptyEnabled[MapListTab::Groups]);
|
||||
this->groupListProxyModel->setHideEmpty(porymapConfig.mapListTabsHidingEmptyFolders.contains(MapListTab::Groups));
|
||||
ui->mapList->setModel(groupListProxyModel);
|
||||
|
||||
this->ui->mapList->setItemDelegateForColumn(0, new GroupNameDelegate(this->editor->project, this));
|
||||
|
|
@ -1500,14 +1484,14 @@ bool MainWindow::setProjectUI() {
|
|||
this->mapLocationModel = new MapLocationModel(editor->project);
|
||||
this->locationListProxyModel = new FilterChildrenProxyModel();
|
||||
this->locationListProxyModel->setSourceModel(this->mapLocationModel);
|
||||
this->locationListProxyModel->setHideEmpty(porymapConfig.mapListHideEmptyEnabled[MapListTab::Locations]);
|
||||
this->locationListProxyModel->setHideEmpty(porymapConfig.mapListTabsHidingEmptyFolders.contains(MapListTab::Locations));
|
||||
ui->locationList->setModel(locationListProxyModel);
|
||||
setMapListSorted(ui->locationList, porymapConfig.mapListLocationsSorted);
|
||||
|
||||
this->layoutTreeModel = new LayoutTreeModel(editor->project);
|
||||
this->layoutListProxyModel = new FilterChildrenProxyModel();
|
||||
this->layoutListProxyModel->setSourceModel(this->layoutTreeModel);
|
||||
this->layoutListProxyModel->setHideEmpty(porymapConfig.mapListHideEmptyEnabled[MapListTab::Layouts]);
|
||||
this->layoutListProxyModel->setHideEmpty(porymapConfig.mapListTabsHidingEmptyFolders.contains(MapListTab::Layouts));
|
||||
ui->layoutList->setModel(layoutListProxyModel);
|
||||
setMapListSorted(ui->layoutList, porymapConfig.mapListLayoutsSorted);
|
||||
|
||||
|
|
@ -1524,7 +1508,7 @@ bool MainWindow::setProjectUI() {
|
|||
if (eventTabIcon.isNull()) {
|
||||
// We randomly choose between the available characters for ~flavor~.
|
||||
// For now, this correctly assumes all versions have 2 icons.
|
||||
eventTabIcon = ProjectConfig::getPlayerIcon(projectConfig.baseGameVersion, QRandomGenerator::global()->bounded(0, 2));
|
||||
eventTabIcon = BaseGame::getPlayerIcon(projectConfig.baseGameVersion, QRandomGenerator::global()->bounded(0, 2));
|
||||
}
|
||||
ui->mainTabBar->setTabIcon(MainTab::Events, eventTabIcon);
|
||||
|
||||
|
|
@ -2259,7 +2243,7 @@ void MainWindow::on_mapViewTab_tabBarClicked(int index)
|
|||
} else if (index == MapViewTab::Collision) {
|
||||
refreshCollisionSelector();
|
||||
} else if (index == MapViewTab::Prefabs) {
|
||||
if (projectConfig.prefabFilepath.isEmpty() && !projectConfig.prefabImportPrompted) {
|
||||
if (userConfig.prefabsFilepath.isEmpty() && !userConfig.prefabsImportPrompted) {
|
||||
// User hasn't set up prefabs and hasn't been prompted before.
|
||||
// Ask if they'd like to import the default prefabs file.
|
||||
if (prefab.tryImportDefaultPrefabs(this, projectConfig.baseGameVersion))
|
||||
|
|
@ -3299,7 +3283,8 @@ bool MainWindow::closeProject() {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
editor->closeProject();
|
||||
logInfo(QString("Closing project '%1'").arg(this->editor->project->root));
|
||||
this->editor->closeProject();
|
||||
clearProjectUI();
|
||||
refreshRecentProjectsMenu();
|
||||
setWindowDisabled(true);
|
||||
|
|
|
|||
|
|
@ -46,6 +46,11 @@ Project::~Project()
|
|||
}
|
||||
|
||||
void Project::setRoot(const QString &dir) {
|
||||
// This is not currently designed to actually change the root folder.
|
||||
// It will not appropriately update instances of the root stored elsewhere,
|
||||
// like in projectConfig or userConfig.
|
||||
Q_ASSERT(this->root.isEmpty());
|
||||
|
||||
this->root = dir;
|
||||
FileDialog::setDirectory(dir);
|
||||
this->parser.setRoot(dir);
|
||||
|
|
@ -72,20 +77,21 @@ bool Project::sanityCheck() {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Porymap projects have no standardized way for Porymap to determine whether they're compatible as of the latest breaking changes.
|
||||
// We can use the project's git history (if it has one, and we're able to get it) to make a reasonable guess.
|
||||
// We know the hashes of the commits in the base repos that contain breaking changes, so if we find one of these then the project
|
||||
// should support at least up to that Porymap major version. If this fails for any reason it returns a version of -1.
|
||||
int Project::getSupportedMajorVersion(QString *errorOut) {
|
||||
QVersionNumber Project::getMinimumVersion(QString *errorOut) const {
|
||||
if (!projectConfig.minimumVersion.isNull()) return projectConfig.minimumVersion;
|
||||
|
||||
// No explicitly supported version, we can use the project's git history (if it has one, and we're able to get it) to make a reasonable guess.
|
||||
// We know the hashes of the commits in the base repos that contain breaking changes, so if we find one of these then the project
|
||||
// should support at least up to that Porymap major version. If this fails for any reason it returns a version of -1.
|
||||
|
||||
// This has relatively tight timeout windows (500ms for each process, compared to the default 30,000ms). This version check
|
||||
// is not important enough to significantly slow down project launch, we'd rather just timeout.
|
||||
const int timeoutLimit = 500;
|
||||
const int failureVersion = -1;
|
||||
QString gitName = "git";
|
||||
constexpr int TimeoutLimit = 500;
|
||||
const QString gitName = QStringLiteral("git");
|
||||
QString gitPath = QStandardPaths::findExecutable(gitName);
|
||||
if (gitPath.isEmpty()) {
|
||||
if (errorOut) *errorOut = QString("Unable to locate %1.").arg(gitName);
|
||||
return failureVersion;
|
||||
return QVersionNumber();
|
||||
}
|
||||
|
||||
QProcess process;
|
||||
|
|
@ -98,7 +104,7 @@ int Project::getSupportedMajorVersion(QString *errorOut) {
|
|||
// We'll get the root commit, then compare it to the known root commits for the base project repos.
|
||||
process.setArguments({ "-c", QString("safe.directory=%1").arg(this->root), "rev-list", "--max-parents=0", "HEAD" });
|
||||
process.start();
|
||||
if (!process.waitForFinished(timeoutLimit) || process.exitStatus() != QProcess::ExitStatus::NormalExit || process.exitCode() != 0) {
|
||||
if (!process.waitForFinished(TimeoutLimit) || process.exitStatus() != QProcess::ExitStatus::NormalExit || process.exitCode() != 0) {
|
||||
if (errorOut) {
|
||||
*errorOut = QStringLiteral("Failed to identify commit history");
|
||||
if (process.error() != QProcess::UnknownError && !process.errorString().isEmpty()) {
|
||||
|
|
@ -109,7 +115,7 @@ int Project::getSupportedMajorVersion(QString *errorOut) {
|
|||
if (!error.isEmpty()) errorOut->append(QString(": %1").arg(error));
|
||||
}
|
||||
}
|
||||
return failureVersion;
|
||||
return QVersionNumber();
|
||||
}
|
||||
const QString rootCommit = QString(process.readLine()).remove('\n');
|
||||
|
||||
|
|
@ -145,22 +151,22 @@ int Project::getSupportedMajorVersion(QString *errorOut) {
|
|||
if (!historyMap.contains(rootCommit)) {
|
||||
// Either this repo does not share history with one of the base repos, or we got some unexpected result.
|
||||
if (errorOut) *errorOut = QStringLiteral("Unrecognized commit history");
|
||||
return failureVersion;
|
||||
return QVersionNumber();
|
||||
}
|
||||
|
||||
// We now know which base repo that the user's repo shares history with.
|
||||
// Next we check to see if it contains the changes required to support particular major versions of Porymap.
|
||||
// We'll start with the most recent major version and work backwards.
|
||||
for (const auto &pair : historyMap.value(rootCommit)) {
|
||||
int versionNum = pair.first;
|
||||
QString commitHash = pair.second;
|
||||
const QVersionNumber version = QVersionNumber(pair.first);
|
||||
const QString commitHash = pair.second;
|
||||
if (commitHash.isEmpty()) {
|
||||
// An empty commit hash means 'consider any point in the history a supported version'
|
||||
return versionNum;
|
||||
return version;
|
||||
}
|
||||
process.setArguments({ "-c", QString("safe.directory=%1").arg(this->root), "merge-base", "--is-ancestor", commitHash, "HEAD" });
|
||||
process.start();
|
||||
if (!process.waitForFinished(timeoutLimit) || process.exitStatus() != QProcess::ExitStatus::NormalExit) {
|
||||
if (!process.waitForFinished(TimeoutLimit) || process.exitStatus() != QProcess::ExitStatus::NormalExit) {
|
||||
if (errorOut) {
|
||||
*errorOut = QStringLiteral("Failed to search commit history");
|
||||
if (process.error() != QProcess::UnknownError && !process.errorString().isEmpty()) {
|
||||
|
|
@ -171,15 +177,15 @@ int Project::getSupportedMajorVersion(QString *errorOut) {
|
|||
if (!error.isEmpty()) errorOut->append(QString(": %1").arg(error));
|
||||
}
|
||||
}
|
||||
return failureVersion;
|
||||
return QVersionNumber();
|
||||
}
|
||||
if (process.exitCode() == 0) {
|
||||
// Identified a supported major version
|
||||
return versionNum;
|
||||
return version;
|
||||
}
|
||||
}
|
||||
// We recognized the commit history, but it's too old for any version of Porymap to support.
|
||||
return 0;
|
||||
return QVersionNumber();
|
||||
}
|
||||
|
||||
bool Project::load() {
|
||||
|
|
@ -1553,7 +1559,7 @@ Tileset *Project::createNewTileset(QString name, bool secondary, bool checkerboa
|
|||
tileset->loadTilesImage(&tilesImage);
|
||||
|
||||
// Create default metatiles
|
||||
const int tilesPerMetatile = projectConfig.getNumTilesInMetatile();
|
||||
const int tilesPerMetatile = Metatile::maxTiles();
|
||||
for (int i = 0; i < tileset->maxMetatiles(); ++i) {
|
||||
auto metatile = new Metatile();
|
||||
for(int j = 0; j < tilesPerMetatile; ++j){
|
||||
|
|
@ -1645,8 +1651,8 @@ bool Project::readTilesetMetatileLabels() {
|
|||
for (auto i = defines.constBegin(); i != defines.constEnd(); i++) {
|
||||
QString label = i.key();
|
||||
uint32_t metatileId = i.value();
|
||||
if (metatileId > Block::maxValue) {
|
||||
metatileId &= Block::maxValue;
|
||||
if (metatileId > Block::MaxValue) {
|
||||
metatileId &= Block::MaxValue;
|
||||
logWarn(QString("Value of metatile label '%1' truncated to %2").arg(label).arg(Metatile::getMetatileIdString(metatileId)));
|
||||
}
|
||||
QString tilesetName = findMetatileLabelsTileset(label);
|
||||
|
|
@ -3205,7 +3211,7 @@ QPixmap Project::getEventPixmap(Event::Group group) {
|
|||
QPixmap defaultIcon = QPixmap(defaultIcons.copy(static_cast<int>(group) * defaultWidth, 0, defaultWidth, defaultHeight));
|
||||
|
||||
// Custom event icons may be provided by the user.
|
||||
QString customIconPath = projectConfig.getEventIconPath(group);
|
||||
QString customIconPath = projectConfig.eventIconPaths.value(group);
|
||||
if (customIconPath.isEmpty()) {
|
||||
// No custom icon specified, use the default icon.
|
||||
pixmap = defaultIcon;
|
||||
|
|
@ -3297,7 +3303,7 @@ QString Project::getDefaultSpeciesIconPath(const QString &species) {
|
|||
|
||||
// We failed to find a default icon path, this species will use a placeholder icon.
|
||||
// If the user has no custom icon path for this species, tell them they can provide one.
|
||||
if (path.isEmpty() && projectConfig.getPokemonIconPath(species).isEmpty()) {
|
||||
if (path.isEmpty() && projectConfig.pokemonIconPaths.value(species).isEmpty()) {
|
||||
logWarn(QString("Failed to find Pokémon icon for '%1'. The filepath can be specified under 'Options->Project Settings'").arg(species));
|
||||
}
|
||||
return path;
|
||||
|
|
@ -3338,7 +3344,7 @@ QPixmap Project::getSpeciesIcon(const QString &species) {
|
|||
QPixmap pixmap;
|
||||
if (!QPixmapCache::find(species, &pixmap)) {
|
||||
// Prefer path from config. If not present, use the path parsed from project files
|
||||
QString path = Project::getExistingFilepath(projectConfig.getPokemonIconPath(species));
|
||||
QString path = Project::getExistingFilepath(projectConfig.pokemonIconPaths.value(species));
|
||||
if (path.isEmpty()) {
|
||||
path = getDefaultSpeciesIconPath(species);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -715,7 +715,7 @@ void MainWindow::setMetatileAttributes(int metatileId, int attributes) {
|
|||
}
|
||||
|
||||
int MainWindow::calculateTileBounds(int * tileStart, int * tileEnd) {
|
||||
int maxNumTiles = projectConfig.getNumTilesInMetatile();
|
||||
int maxNumTiles = Metatile::maxTiles();
|
||||
if (*tileEnd >= maxNumTiles || *tileEnd < 0)
|
||||
*tileEnd = maxNumTiles - 1;
|
||||
if (*tileStart >= maxNumTiles || *tileStart < 0)
|
||||
|
|
|
|||
|
|
@ -198,7 +198,12 @@ bool ScriptUtility::getSmartPathsEnabled() {
|
|||
}
|
||||
|
||||
QList<QString> ScriptUtility::getCustomScripts() {
|
||||
return userConfig.getCustomScriptPaths();
|
||||
QList<QString> paths;
|
||||
for (const auto& settings : userConfig.customScripts)
|
||||
paths.append(settings.path);
|
||||
for (const auto& settings : projectConfig.customScripts)
|
||||
paths.append(settings.path);
|
||||
return paths;
|
||||
}
|
||||
|
||||
QList<int> ScriptUtility::getMetatileLayerOrder() {
|
||||
|
|
|
|||
|
|
@ -22,11 +22,11 @@ Scripting::Scripting(MainWindow *mainWindow)
|
|||
: QObject(mainWindow), mainWindow(mainWindow), engine(new QJSEngine(this))
|
||||
{
|
||||
this->engine->installExtensions(QJSEngine::ConsoleExtension);
|
||||
const QStringList paths = userConfig.getCustomScriptPaths();
|
||||
const QList<bool> enabled = userConfig.getCustomScriptsEnabled();
|
||||
for (int i = 0; i < paths.length(); i++) {
|
||||
if (enabled.value(i, true))
|
||||
loadScript(paths.at(i));
|
||||
|
||||
const QStringList paths = ScriptSettings::filter(userConfig.customScripts)
|
||||
+ ScriptSettings::filter(projectConfig.customScripts);
|
||||
for (const auto& path : paths) {
|
||||
loadScript(path);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -103,10 +103,10 @@ void Scripting::populateGlobalObject() {
|
|||
constants.setProperty("max_secondary_metatiles", Project::getNumMetatilesSecondary());
|
||||
constants.setProperty("num_primary_palettes", Project::getNumPalettesPrimary());
|
||||
constants.setProperty("num_secondary_palettes", Project::getNumPalettesSecondary());
|
||||
constants.setProperty("layers_per_metatile", projectConfig.getNumLayersInMetatile());
|
||||
constants.setProperty("tiles_per_metatile", projectConfig.getNumTilesInMetatile());
|
||||
constants.setProperty("layers_per_metatile", Metatile::numLayers());
|
||||
constants.setProperty("tiles_per_metatile", Metatile::maxTiles());
|
||||
|
||||
constants.setProperty("base_game_version", projectConfig.getBaseGameVersionString());
|
||||
constants.setProperty("base_game_version", BaseGame::versionToString(projectConfig.baseGameVersion));
|
||||
|
||||
// Read out behavior values into constants object
|
||||
QJSValue behaviorsArray = instance->engine->newObject();
|
||||
|
|
|
|||
|
|
@ -1,112 +0,0 @@
|
|||
#include "citymappixmapitem.h"
|
||||
#include "imageproviders.h"
|
||||
#include "config.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QPainter>
|
||||
#include <QDebug>
|
||||
|
||||
void CityMapPixmapItem::init() {
|
||||
width_ = 10;
|
||||
height_ = 10;
|
||||
|
||||
QFile binFile(file);
|
||||
if (!binFile.open(QIODevice::ReadOnly)) return;
|
||||
|
||||
data = binFile.readAll();
|
||||
if (projectConfig.baseGameVersion == BaseGameVersion::pokeruby) {
|
||||
for (int i = 0; i < data.size(); i++)
|
||||
data[i] = data[i] ^ 0x80;
|
||||
}
|
||||
|
||||
binFile.close();
|
||||
}
|
||||
|
||||
void CityMapPixmapItem::draw() {
|
||||
QImage image(width_ * 8, height_ * 8, QImage::Format_RGBA8888);
|
||||
|
||||
// TODO: construct temporary tile from this based on the id?
|
||||
// QPainter painter(&image);
|
||||
// for (int i = 0; i < data.size() / 2; i++) {
|
||||
// QImage img = this->tile_selector->tileImg(data[i * 2]);// need to skip every other tile
|
||||
// int x = i % width_;
|
||||
// int y = i / width_;
|
||||
// QPoint pos = QPoint(x * 8, y * 8);
|
||||
// painter.drawImage(pos, img);
|
||||
// }
|
||||
// painter.end();
|
||||
|
||||
this->setPixmap(QPixmap::fromImage(image));
|
||||
}
|
||||
|
||||
void CityMapPixmapItem::save() {
|
||||
QFile binFile(file);
|
||||
if (!binFile.open(QIODevice::WriteOnly)) {
|
||||
logError(QString("Cannot save city map tilemap to %1.").arg(file));
|
||||
return;
|
||||
}
|
||||
if (projectConfig.baseGameVersion == BaseGameVersion::pokeruby) {
|
||||
for (int i = 0; i < data.size(); i++)
|
||||
data[i] = data[i] ^ 0x80;
|
||||
}
|
||||
binFile.write(data);
|
||||
binFile.close();
|
||||
}
|
||||
|
||||
void CityMapPixmapItem::paint(QGraphicsSceneMouseEvent *event) {
|
||||
QPointF pos = event->pos();
|
||||
int x = static_cast<int>(pos.x()) / 8;
|
||||
int y = static_cast<int>(pos.y()) / 8;
|
||||
int index = getIndexAt(x, y);
|
||||
data[index] = static_cast<uint8_t>(this->tile_selector->selectedTile);
|
||||
|
||||
draw();
|
||||
}
|
||||
|
||||
void CityMapPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) {
|
||||
emit mouseEvent(event, this);
|
||||
}
|
||||
|
||||
void CityMapPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
|
||||
QPointF pos = event->pos();
|
||||
int x = static_cast<int>(pos.x()) / 8;
|
||||
int y = static_cast<int>(pos.y()) / 8;
|
||||
if (x < width_ && x >= 0
|
||||
&& y < height_ && y >= 0) {
|
||||
emit this->hoveredRegionMapTileChanged(x, y);
|
||||
emit mouseEvent(event, this);
|
||||
}
|
||||
}
|
||||
|
||||
void CityMapPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
|
||||
emit mouseEvent(event, this);
|
||||
}
|
||||
|
||||
QVector<uint8_t> CityMapPixmapItem::getTiles() {
|
||||
QVector<uint8_t> tiles;
|
||||
for (auto tile : data) {
|
||||
tiles.append(tile);
|
||||
}
|
||||
return tiles;
|
||||
}
|
||||
|
||||
void CityMapPixmapItem::setTiles(QVector<uint8_t> tiles) {
|
||||
QByteArray newData;
|
||||
for (auto tile : tiles) {
|
||||
newData.append(tile);
|
||||
}
|
||||
this->data = newData;
|
||||
}
|
||||
|
||||
int CityMapPixmapItem::getIndexAt(int x, int y) {
|
||||
return 2 * (x + y * this->width_);
|
||||
}
|
||||
|
||||
int CityMapPixmapItem::width() {
|
||||
return this->width_;
|
||||
}
|
||||
|
||||
int CityMapPixmapItem::height() {
|
||||
return this->height_;
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
#include "editor.h"
|
||||
#include "shortcut.h"
|
||||
#include "filedialog.h"
|
||||
#include "eventfilters.h"
|
||||
|
||||
#include <QDir>
|
||||
|
||||
|
|
@ -18,10 +19,10 @@ CustomScriptsEditor::CustomScriptsEditor(QWidget *parent) :
|
|||
// This property seems to be reset if we don't set it programmatically
|
||||
ui->list->setDragDropMode(QAbstractItemView::NoDragDrop);
|
||||
|
||||
const QStringList paths = userConfig.getCustomScriptPaths();
|
||||
const QList<bool> enabled = userConfig.getCustomScriptsEnabled();
|
||||
for (int i = 0; i < paths.length(); i++)
|
||||
this->displayScript(paths.at(i), enabled.value(i, true));
|
||||
for (const auto& settings : projectConfig.customScripts)
|
||||
displayScript(settings);
|
||||
for (const auto& settings : userConfig.customScripts)
|
||||
displayScript(settings);
|
||||
|
||||
connect(ui->button_Help, &QAbstractButton::clicked, this, &CustomScriptsEditor::openManual);
|
||||
connect(ui->button_CreateNewScript, &QAbstractButton::clicked, this, &CustomScriptsEditor::createNewScript);
|
||||
|
|
@ -29,8 +30,8 @@ CustomScriptsEditor::CustomScriptsEditor(QWidget *parent) :
|
|||
connect(ui->button_RefreshScripts, &QAbstractButton::clicked, this, &CustomScriptsEditor::userRefreshScripts);
|
||||
connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &CustomScriptsEditor::dialogButtonClicked);
|
||||
|
||||
this->initShortcuts();
|
||||
this->restoreWindowState();
|
||||
initShortcuts();
|
||||
installEventFilter(new GeometrySaver(this));
|
||||
}
|
||||
|
||||
CustomScriptsEditor::~CustomScriptsEditor()
|
||||
|
|
@ -60,7 +61,6 @@ void CustomScriptsEditor::initShortcuts() {
|
|||
shortcut_refresh->setObjectName("shortcut_refresh");
|
||||
shortcut_refresh->setWhatsThis("Refresh Scripts");
|
||||
|
||||
shortcutsConfig.load();
|
||||
shortcutsConfig.setDefaultShortcuts(shortcutableObjects());
|
||||
applyUserShortcuts();
|
||||
}
|
||||
|
|
@ -87,26 +87,16 @@ void CustomScriptsEditor::applyUserShortcuts() {
|
|||
shortcut->setKeys(shortcutsConfig.userShortcuts(shortcut));
|
||||
}
|
||||
|
||||
void CustomScriptsEditor::restoreWindowState() {
|
||||
logInfo("Restoring custom scripts editor geometry from previous session.");
|
||||
const QMap<QString, QByteArray> geometry = porymapConfig.getCustomScriptsEditorGeometry();
|
||||
this->restoreGeometry(geometry.value("custom_scripts_editor_geometry"));
|
||||
this->restoreState(geometry.value("custom_scripts_editor_state"));
|
||||
}
|
||||
|
||||
void CustomScriptsEditor::displayScript(const QString &filepath, bool enabled) {
|
||||
auto item = new QListWidgetItem();
|
||||
auto widget = new CustomScriptsListItem();
|
||||
|
||||
widget->ui->checkBox_Enable->setChecked(enabled);
|
||||
widget->ui->lineEdit_filepath->setText(filepath);
|
||||
void CustomScriptsEditor::displayScript(const ScriptSettings& settings) {
|
||||
auto item = new QListWidgetItem(ui->list);
|
||||
auto widget = new CustomScriptsListItem(settings, ui->list);
|
||||
item->setSizeHint(widget->sizeHint());
|
||||
|
||||
connect(widget->ui->b_Choose, &QAbstractButton::clicked, [this, item](bool) { this->replaceScript(item); });
|
||||
connect(widget->ui->b_Edit, &QAbstractButton::clicked, [this, item](bool) { this->openScript(item); });
|
||||
connect(widget->ui->b_Delete, &QAbstractButton::clicked, [this, item](bool) { this->removeScript(item); });
|
||||
connect(widget->ui->checkBox_Enable, &QCheckBox::toggled, this, &CustomScriptsEditor::markEdited);
|
||||
connect(widget->ui->lineEdit_filepath, &QLineEdit::textEdited, this, &CustomScriptsEditor::markEdited);
|
||||
connect(widget, &CustomScriptsListItem::clickedChooseScript, [this, item] { this->replaceScript(item); });
|
||||
connect(widget, &CustomScriptsListItem::clickedEditScript, [this, item] { this->openScript(item); });
|
||||
connect(widget, &CustomScriptsListItem::clickedDeleteScript, [this, item] { this->removeScript(item); });
|
||||
connect(widget, &CustomScriptsListItem::toggledEnable, this, &CustomScriptsEditor::markEdited);
|
||||
connect(widget, &CustomScriptsListItem::pathEdited, this, &CustomScriptsEditor::markEdited);
|
||||
|
||||
// Per the Qt manual, for performance reasons QListWidget::setItemWidget shouldn't be used with non-static items.
|
||||
// There's an assumption here that users won't have enough scripts for that to be a problem.
|
||||
|
|
@ -122,7 +112,7 @@ QString CustomScriptsEditor::getScriptFilepath(QListWidgetItem * item, bool abso
|
|||
auto widget = dynamic_cast<CustomScriptsListItem *>(ui->list->itemWidget(item));
|
||||
if (!widget) return QString();
|
||||
|
||||
QString path = widget->ui->lineEdit_filepath->text();
|
||||
QString path = widget->path();
|
||||
if (absolutePath) {
|
||||
QFileInfo fileInfo(path);
|
||||
if (fileInfo.isRelative())
|
||||
|
|
@ -134,13 +124,13 @@ QString CustomScriptsEditor::getScriptFilepath(QListWidgetItem * item, bool abso
|
|||
void CustomScriptsEditor::setScriptFilepath(QListWidgetItem * item, QString filepath) const {
|
||||
auto widget = dynamic_cast<CustomScriptsListItem *>(ui->list->itemWidget(item));
|
||||
if (widget) {
|
||||
widget->ui->lineEdit_filepath->setText(Util::stripPrefix(filepath, this->baseDir));
|
||||
widget->setPath(Util::stripPrefix(filepath, this->baseDir));
|
||||
}
|
||||
}
|
||||
|
||||
bool CustomScriptsEditor::getScriptEnabled(QListWidgetItem * item) const {
|
||||
auto widget = dynamic_cast<CustomScriptsListItem *>(ui->list->itemWidget(item));
|
||||
return widget && widget->ui->checkBox_Enable->isChecked();
|
||||
return widget && widget->scriptEnabled();
|
||||
}
|
||||
|
||||
QString CustomScriptsEditor::chooseScript(QString dir) {
|
||||
|
|
@ -187,7 +177,9 @@ void CustomScriptsEditor::displayNewScript(QString filepath) {
|
|||
}
|
||||
}
|
||||
|
||||
this->displayScript(filepath, true);
|
||||
ScriptSettings settings;
|
||||
settings.path = filepath;
|
||||
this->displayScript(settings);
|
||||
this->markEdited();
|
||||
}
|
||||
|
||||
|
|
@ -253,19 +245,23 @@ void CustomScriptsEditor::save() {
|
|||
if (!this->hasUnsavedChanges)
|
||||
return;
|
||||
|
||||
QStringList paths;
|
||||
QList<bool> enabledStates;
|
||||
QList<ScriptSettings> userScripts;
|
||||
QList<ScriptSettings> projectScripts;
|
||||
for (int i = 0; i < ui->list->count(); i++) {
|
||||
auto item = ui->list->item(i);
|
||||
const QString path = this->getScriptFilepath(item, false);
|
||||
if (!path.isEmpty()) {
|
||||
paths.append(path);
|
||||
enabledStates.append(this->getScriptEnabled(item));
|
||||
}
|
||||
auto widget = dynamic_cast<CustomScriptsListItem *>(ui->list->itemWidget(item));
|
||||
if (!widget) continue;
|
||||
const ScriptSettings settings = widget->getSettings();
|
||||
if (settings.userOnly) userScripts.append(settings);
|
||||
else projectScripts.append(settings);
|
||||
}
|
||||
|
||||
userConfig.setCustomScripts(paths, enabledStates);
|
||||
userConfig.customScripts = userScripts;
|
||||
userConfig.save();
|
||||
|
||||
projectConfig.customScripts = projectScripts;
|
||||
projectConfig.save();
|
||||
|
||||
this->hasUnsavedChanges = false;
|
||||
this->refreshScripts();
|
||||
}
|
||||
|
|
@ -295,9 +291,4 @@ void CustomScriptsEditor::closeEvent(QCloseEvent* event) {
|
|||
if (result == QMessageBox::Yes)
|
||||
this->save();
|
||||
}
|
||||
|
||||
porymapConfig.setCustomScriptsEditorGeometry(
|
||||
this->saveGeometry(),
|
||||
this->saveState()
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,50 @@ CustomScriptsListItem::CustomScriptsListItem(QWidget *parent) :
|
|||
ui(new Ui::CustomScriptsListItem)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
connect(ui->b_Choose, &QAbstractButton::clicked, this, &CustomScriptsListItem::clickedChooseScript);
|
||||
connect(ui->b_Edit, &QAbstractButton::clicked, this, &CustomScriptsListItem::clickedEditScript);
|
||||
connect(ui->b_Delete, &QAbstractButton::clicked, this, &CustomScriptsListItem::clickedDeleteScript);
|
||||
connect(ui->checkBox_Enable, &QCheckBox::toggled, this, &CustomScriptsListItem::toggledEnable);
|
||||
connect(ui->lineEdit_filepath, &QLineEdit::textEdited, this, &CustomScriptsListItem::pathEdited);
|
||||
}
|
||||
|
||||
CustomScriptsListItem::~CustomScriptsListItem()
|
||||
CustomScriptsListItem::CustomScriptsListItem(const ScriptSettings& settings, QWidget *parent) :
|
||||
CustomScriptsListItem(parent)
|
||||
{
|
||||
setSettings(settings);
|
||||
}
|
||||
|
||||
CustomScriptsListItem::~CustomScriptsListItem() {
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void CustomScriptsListItem::setPath(const QString& text) {
|
||||
ui->lineEdit_filepath->setText(text);
|
||||
}
|
||||
|
||||
QString CustomScriptsListItem::path() const {
|
||||
return ui->lineEdit_filepath->text();
|
||||
}
|
||||
|
||||
void CustomScriptsListItem::setScriptEnabled(bool enabled) {
|
||||
ui->checkBox_Enable->setChecked(enabled);
|
||||
}
|
||||
|
||||
bool CustomScriptsListItem::scriptEnabled() const {
|
||||
return ui->checkBox_Enable->isChecked();
|
||||
}
|
||||
|
||||
void CustomScriptsListItem::setSettings(const ScriptSettings& settings) {
|
||||
setPath(settings.path);
|
||||
setScriptEnabled(settings.enabled);
|
||||
// TODO: Read userOnly
|
||||
}
|
||||
|
||||
ScriptSettings CustomScriptsListItem::getSettings() const {
|
||||
return {
|
||||
.path = path(),
|
||||
.enabled = scriptEnabled(),
|
||||
.userOnly = true, // TODO
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
#include "eventfilters.h"
|
||||
#include "config.h"
|
||||
#include "log.h"
|
||||
|
||||
#include <QGraphicsSceneWheelEvent>
|
||||
|
||||
|
|
@ -16,10 +18,40 @@ bool MapSceneEventFilter::eventFilter(QObject*, QEvent *event) {
|
|||
}
|
||||
|
||||
|
||||
|
||||
bool ActiveWindowFilter::eventFilter(QObject*, QEvent *event) {
|
||||
if (event->type() == QEvent::WindowActivate) {
|
||||
emit activated();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool GeometrySaver::eventFilter(QObject *object, QEvent *event) {
|
||||
if (event->spontaneous()) return false;
|
||||
|
||||
auto w = qobject_cast<QWidget*>(object);
|
||||
if (!w) return false;
|
||||
|
||||
if (event->type() == QEvent::Polish) {
|
||||
// Note: Restoring geometry in QEvent::Show would be too late,
|
||||
// and the widget would briefly appear with the old geometry.
|
||||
porymapConfig.restoreGeometry(w);
|
||||
} else if (event->type() == QEvent::Show) {
|
||||
if (m_loggingEnabled && !w->windowTitle().isEmpty()) {
|
||||
logInfo(QString("Opening window: %1").arg(w->windowTitle()));
|
||||
}
|
||||
m_wasShown.insert(object);
|
||||
} else if (event->type() == QEvent::Close && m_wasShown.contains(object)) {
|
||||
// There are situations where a window might be 'closed' without
|
||||
// ever actually having been opened (for example, the Shortcuts Editor
|
||||
// will quietly construct windows to get their shortcuts, and those windows
|
||||
// can later be closed without having been displayed).
|
||||
// We don't want to save the geometry of these windows, or log that they closed,
|
||||
// so we've checked to make sure the widget was displayed before proceeding.
|
||||
porymapConfig.saveGeometry(w);
|
||||
if (m_loggingEnabled && !w->windowTitle().isEmpty()) {
|
||||
logInfo(QString("Closing window: %1").arg(w->windowTitle()));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -74,6 +74,28 @@ QVector<qreal> GridSettings::getDashPattern(uint length) const {
|
|||
}
|
||||
}
|
||||
|
||||
QJsonObject GridSettings::toJson() const {
|
||||
QJsonObject obj;
|
||||
obj["width"] = static_cast<qint64>(this->width);
|
||||
obj["height"] = static_cast<qint64>(this->height);
|
||||
obj["offsetX"] = this->offsetX;
|
||||
obj["offsetY"] = this->offsetY;
|
||||
obj["style"] = getStyleName(this->style);
|
||||
obj["color"] = this->color.name();
|
||||
return obj;
|
||||
}
|
||||
|
||||
GridSettings GridSettings::fromJson(const QJsonObject &obj) {
|
||||
GridSettings settings;
|
||||
settings.width = obj["width"].toInt();
|
||||
settings.height = obj["height"].toInt();
|
||||
settings.offsetX = obj["offsetX"].toInt();
|
||||
settings.offsetY = obj["offsetY"].toInt();
|
||||
settings.style = getStyleFromName(obj["style"].toString());
|
||||
settings.color = QColor(obj["color"].toString());
|
||||
return settings;
|
||||
}
|
||||
|
||||
|
||||
|
||||
GridSettingsDialog::GridSettingsDialog(QWidget *parent) :
|
||||
|
|
|
|||
|
|
@ -23,16 +23,17 @@ void MetatileLayersItem::setOrientation(Qt::Orientation orientation) {
|
|||
// Generate a table of tile positions that allows us to map between
|
||||
// the index of a tile in the metatile and its position in this layer view.
|
||||
this->tilePositions.clear();
|
||||
const int numLayers = Metatile::numLayers();
|
||||
if (this->orientation == Qt::Horizontal) {
|
||||
// Tiles are laid out horizontally, with the bottom layer on the left:
|
||||
// 0 1 4 5 8 9
|
||||
// 2 3 6 7 10 11
|
||||
for (int layer = 0; layer < projectConfig.getNumLayersInMetatile(); layer++)
|
||||
for (int layer = 0; layer < numLayers; layer++)
|
||||
for (int y = 0; y < Metatile::tileHeight(); y++)
|
||||
for (int x = 0; x < Metatile::tileWidth(); x++) {
|
||||
this->tilePositions.append(QPoint(x + layer * Metatile::tileWidth(), y));
|
||||
}
|
||||
maxWidth *= projectConfig.getNumLayersInMetatile();
|
||||
maxWidth *= numLayers;
|
||||
} else if (this->orientation == Qt::Vertical) {
|
||||
// Tiles are laid out vertically, with the bottom layer on the bottom:
|
||||
// 8 9
|
||||
|
|
@ -41,12 +42,12 @@ void MetatileLayersItem::setOrientation(Qt::Orientation orientation) {
|
|||
// 6 7
|
||||
// 0 1
|
||||
// 2 3
|
||||
for (int layer = projectConfig.getNumLayersInMetatile() - 1; layer >= 0; layer--)
|
||||
for (int layer = numLayers - 1; layer >= 0; layer--)
|
||||
for (int y = 0; y < Metatile::tileHeight(); y++)
|
||||
for (int x = 0; x < Metatile::tileWidth(); x++) {
|
||||
this->tilePositions.append(QPoint(x, y + layer * Metatile::tileHeight()));
|
||||
}
|
||||
maxHeight *= projectConfig.getNumLayersInMetatile();
|
||||
maxHeight *= numLayers;
|
||||
}
|
||||
setMaxSelectionSize(maxWidth, maxHeight);
|
||||
update();
|
||||
|
|
@ -61,7 +62,7 @@ void MetatileLayersItem::draw() {
|
|||
|
||||
// Draw tile images
|
||||
const Metatile* metatile = getMetatile();
|
||||
int numTiles = qMin(projectConfig.getNumTilesInMetatile(), metatile ? metatile->tiles.length() : 0);
|
||||
int numTiles = qMin(Metatile::maxTiles(), metatile ? metatile->tiles.length() : 0);
|
||||
for (int i = 0; i < numTiles; i++) {
|
||||
Tile tile = metatile->tiles.at(i);
|
||||
QImage tileImage = getPalettedTileImage(tile.tileId,
|
||||
|
|
@ -79,7 +80,7 @@ void MetatileLayersItem::draw() {
|
|||
painter.setPen(Qt::white);
|
||||
const int layerWidth = this->cellWidth * Metatile::tileWidth();
|
||||
const int layerHeight = this->cellHeight * Metatile::tileHeight();
|
||||
for (int i = 1; i < projectConfig.getNumLayersInMetatile(); i++) {
|
||||
for (int i = 1; i < Metatile::numLayers(); i++) {
|
||||
if (this->orientation == Qt::Vertical) {
|
||||
int y = i * layerHeight;
|
||||
painter.drawLine(0, y, layerWidth, y);
|
||||
|
|
|
|||
|
|
@ -41,18 +41,16 @@ NewLayoutDialog::NewLayoutDialog(Project *project, const Layout *layoutToCopy, Q
|
|||
|
||||
refresh();
|
||||
|
||||
if (porymapConfig.newLayoutDialogGeometry.isEmpty()){
|
||||
if (!porymapConfig.restoreGeometry(this)) {
|
||||
// On first display resize to fit contents a little better
|
||||
adjustSize();
|
||||
} else {
|
||||
restoreGeometry(porymapConfig.newLayoutDialogGeometry);
|
||||
}
|
||||
ui->lineEdit_Name->setFocus();
|
||||
}
|
||||
|
||||
NewLayoutDialog::~NewLayoutDialog()
|
||||
{
|
||||
porymapConfig.newLayoutDialogGeometry = saveGeometry();
|
||||
porymapConfig.saveGeometry(this);
|
||||
saveSettings();
|
||||
delete ui;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include "ui_newmapdialog.h"
|
||||
#include "config.h"
|
||||
#include "validator.h"
|
||||
#include "eventfilters.h"
|
||||
|
||||
#include <QMap>
|
||||
#include <QSet>
|
||||
|
|
@ -61,7 +62,7 @@ NewMapDialog::NewMapDialog(Project *project, const Map *mapToCopy, QWidget *pare
|
|||
connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &NewMapDialog::dialogButtonClicked);
|
||||
|
||||
refresh();
|
||||
restoreGeometry(porymapConfig.newMapDialogGeometry);
|
||||
installEventFilter(new GeometrySaver(this));
|
||||
ui->lineEdit_Name->setFocus();
|
||||
}
|
||||
|
||||
|
|
@ -91,7 +92,6 @@ NewMapDialog::NewMapDialog(Project *project, int mapListTab, const QString &mapL
|
|||
|
||||
NewMapDialog::~NewMapDialog()
|
||||
{
|
||||
porymapConfig.newMapDialogGeometry = saveGeometry();
|
||||
saveSettings();
|
||||
delete ui;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ PaletteEditor::PaletteEditor(Project *project, Tileset *primaryTileset, Tileset
|
|||
ui->actionRedo->setShortcuts({ui->actionRedo->shortcut(), QKeySequence("Ctrl+Shift+Z")});
|
||||
|
||||
refreshPaletteId();
|
||||
restoreWindowState();
|
||||
installEventFilter(new GeometrySaver(this));
|
||||
}
|
||||
|
||||
PaletteEditor::~PaletteEditor() {
|
||||
|
|
@ -159,13 +159,6 @@ void PaletteEditor::commitEditHistory(int paletteId) {
|
|||
updateEditHistoryActions();
|
||||
}
|
||||
|
||||
void PaletteEditor::restoreWindowState() {
|
||||
logInfo("Restoring palette editor geometry from previous session.");
|
||||
QMap<QString, QByteArray> geometry = porymapConfig.getPaletteEditorGeometry();
|
||||
restoreGeometry(geometry.value("palette_editor_geometry"));
|
||||
restoreState(geometry.value("palette_editor_state"));
|
||||
}
|
||||
|
||||
void PaletteEditor::updateEditHistoryActions() {
|
||||
int paletteId = currentPaletteId();
|
||||
// We have an initial commit that shouldn't be available to Undo, so we ignore that.
|
||||
|
|
@ -279,11 +272,6 @@ void PaletteEditor::setColorInputTitles(bool showUnused) {
|
|||
}
|
||||
|
||||
void PaletteEditor::closeEvent(QCloseEvent*) {
|
||||
porymapConfig.setPaletteEditorGeometry(
|
||||
saveGeometry(),
|
||||
saveState()
|
||||
);
|
||||
|
||||
// Opening the color search window then closing the Palette Editor sets
|
||||
// focus to the main editor window instead of the parent (Tileset Editor).
|
||||
// Make sure the parent is active when we close.
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ const QString defaultFilepath = "prefabs.json";
|
|||
|
||||
void Prefab::loadPrefabs() {
|
||||
this->items.clear();
|
||||
QString filepath = projectConfig.prefabFilepath;
|
||||
QString filepath = userConfig.prefabsFilepath;
|
||||
if (filepath.isEmpty()) return;
|
||||
|
||||
ParseUtil parser;
|
||||
|
|
@ -87,10 +87,10 @@ void Prefab::loadPrefabs() {
|
|||
}
|
||||
|
||||
void Prefab::savePrefabs() {
|
||||
if (projectConfig.prefabFilepath.isEmpty())
|
||||
projectConfig.prefabFilepath = defaultFilepath;
|
||||
if (userConfig.prefabsFilepath.isEmpty())
|
||||
userConfig.prefabsFilepath = defaultFilepath;
|
||||
|
||||
QString filepath = projectConfig.prefabFilepath;
|
||||
QString filepath = userConfig.prefabsFilepath;
|
||||
|
||||
QFileInfo info(filepath);
|
||||
if (info.isRelative()) {
|
||||
|
|
@ -283,9 +283,9 @@ void Prefab::addPrefab(MetatileSelection selection, Layout *layout, QString name
|
|||
this->updatePrefabUi(layout);
|
||||
}
|
||||
|
||||
bool Prefab::tryImportDefaultPrefabs(QWidget * parent, BaseGameVersion version, QString filepath) {
|
||||
bool Prefab::tryImportDefaultPrefabs(QWidget * parent, BaseGame::Version version, QString filepath) {
|
||||
// Ensure we have default prefabs for the project's game version.
|
||||
if (version != BaseGameVersion::pokeruby && version != BaseGameVersion::pokeemerald && version != BaseGameVersion::pokefirered)
|
||||
if (version != BaseGame::Version::pokeruby && version != BaseGame::Version::pokeemerald && version != BaseGame::Version::pokefirered)
|
||||
return false;
|
||||
|
||||
if (filepath.isEmpty())
|
||||
|
|
@ -316,17 +316,17 @@ bool Prefab::tryImportDefaultPrefabs(QWidget * parent, BaseGameVersion version,
|
|||
QMessageBox::question(parent,
|
||||
QApplication::applicationName(),
|
||||
QString("Would you like to import the default prefabs for %1? %2.")
|
||||
.arg(projectConfig.getBaseGameVersionString(version))
|
||||
.arg(BaseGame::versionToString(version))
|
||||
.arg(fileWarning),
|
||||
QMessageBox::Yes | QMessageBox::No);
|
||||
|
||||
bool acceptedImport = (prompt == QMessageBox::Yes);
|
||||
if (acceptedImport) {
|
||||
// Sets up the default prefabs.json filepath.
|
||||
projectConfig.prefabFilepath = filepath;
|
||||
userConfig.prefabsFilepath = filepath;
|
||||
QFile prefabsFile(absFilepath);
|
||||
if (!prefabsFile.open(QIODevice::WriteOnly)) {
|
||||
projectConfig.prefabFilepath = QString();
|
||||
userConfig.prefabsFilepath = QString();
|
||||
|
||||
logError(QString("Error: Could not open %1 for writing").arg(absFilepath));
|
||||
QMessageBox messageBox(parent);
|
||||
|
|
@ -340,13 +340,13 @@ bool Prefab::tryImportDefaultPrefabs(QWidget * parent, BaseGameVersion version,
|
|||
ParseUtil parser;
|
||||
QString content;
|
||||
switch (version) {
|
||||
case BaseGameVersion::pokeruby:
|
||||
case BaseGame::Version::pokeruby:
|
||||
content = parser.readTextFile(":/text/prefabs_default_ruby.json");
|
||||
break;
|
||||
case BaseGameVersion::pokefirered:
|
||||
case BaseGame::Version::pokefirered:
|
||||
content = parser.readTextFile(":/text/prefabs_default_firered.json");
|
||||
break;
|
||||
case BaseGameVersion::pokeemerald:
|
||||
case BaseGame::Version::pokeemerald:
|
||||
content = parser.readTextFile(":/text/prefabs_default_emerald.json");
|
||||
break;
|
||||
default:
|
||||
|
|
@ -359,7 +359,7 @@ bool Prefab::tryImportDefaultPrefabs(QWidget * parent, BaseGameVersion version,
|
|||
this->loadPrefabs();
|
||||
}
|
||||
|
||||
projectConfig.prefabImportPrompted = true;
|
||||
userConfig.prefabsImportPrompted = true;
|
||||
return acceptedImport;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ void PreferenceEditor::updateFields() {
|
|||
ui->checkBox_StatusWarnings->setChecked(porymapConfig.statusBarLogTypes.find(LogType::LOG_WARN) != logTypeEnd);
|
||||
ui->checkBox_StatusInformation->setChecked(porymapConfig.statusBarLogTypes.find(LogType::LOG_INFO) != logTypeEnd);
|
||||
|
||||
if (/*porymapConfig.displayIdsHexadecimal*/ConfigDisplayIdsHexadecimal) {
|
||||
if (porymapConfig.displayIdsHexadecimal) {
|
||||
ui->radioButton_Hexadecimal->setChecked(true);
|
||||
} else {
|
||||
ui->radioButton_Decimal->setChecked(true);
|
||||
|
|
@ -141,7 +141,7 @@ void PreferenceEditor::saveFields() {
|
|||
porymapConfig.checkForUpdates = ui->checkBox_CheckForUpdates->isChecked();
|
||||
porymapConfig.eventDeleteWarningDisabled = ui->checkBox_DisableEventWarning->isChecked();
|
||||
porymapConfig.showProjectLoadingScreen = ui->checkBox_ShowProjectLoadingScreen->isChecked();
|
||||
/*porymapConfig.displayIdsHexadecimal*/ConfigDisplayIdsHexadecimal = ui->radioButton_Hexadecimal->isChecked();
|
||||
porymapConfig.displayIdsHexadecimal = ui->radioButton_Hexadecimal->isChecked();
|
||||
|
||||
porymapConfig.statusBarLogTypes.clear();
|
||||
if (ui->checkBox_StatusErrors->isChecked()) porymapConfig.statusBarLogTypes.insert(LogType::LOG_ERROR);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include "filedialog.h"
|
||||
#include "newdefinedialog.h"
|
||||
#include "utility.h"
|
||||
#include "eventfilters.h"
|
||||
|
||||
#include <QAbstractButton>
|
||||
#include <QFormLayout>
|
||||
|
|
@ -27,9 +28,9 @@ ProjectSettingsEditor::ProjectSettingsEditor(QWidget *parent, Project *project)
|
|||
this->initUi();
|
||||
this->createProjectPathsTable();
|
||||
this->createProjectIdentifiersTable();
|
||||
this->installEventFilter(new GeometrySaver(this));
|
||||
this->connectSignals();
|
||||
this->refresh();
|
||||
this->restoreWindowState();
|
||||
}
|
||||
|
||||
ProjectSettingsEditor::~ProjectSettingsEditor()
|
||||
|
|
@ -110,16 +111,21 @@ void ProjectSettingsEditor::initUi() {
|
|||
ui->comboBox_IconSpecies->addItems(project->speciesNames);
|
||||
ui->comboBox_WarpBehaviors->addItems(project->metatileBehaviorMap.keys());
|
||||
}
|
||||
ui->comboBox_BaseGameVersion->addItems(ProjectConfig::versionStrings);
|
||||
// TODO: We don't need to keep converting these to/from strings, just include the value as data.
|
||||
ui->comboBox_BaseGameVersion->addItems({
|
||||
BaseGame::versionToString(BaseGame::Version::pokeruby),
|
||||
BaseGame::versionToString(BaseGame::Version::pokefirered),
|
||||
BaseGame::versionToString(BaseGame::Version::pokeemerald),
|
||||
});
|
||||
ui->comboBox_AttributesSize->addItems({"1", "2", "4"});
|
||||
|
||||
ui->comboBox_EventsTabIcon->addItem("Automatic", "");
|
||||
ui->comboBox_EventsTabIcon->addItem("Brendan (Emerald)", ProjectConfig::getPlayerIconPath(BaseGameVersion::pokeemerald, 0));
|
||||
ui->comboBox_EventsTabIcon->addItem("Brendan (R/S)", ProjectConfig::getPlayerIconPath(BaseGameVersion::pokeruby, 0));
|
||||
ui->comboBox_EventsTabIcon->addItem("May (Emerald)", ProjectConfig::getPlayerIconPath(BaseGameVersion::pokeemerald, 1));
|
||||
ui->comboBox_EventsTabIcon->addItem("May (R/S)", ProjectConfig::getPlayerIconPath(BaseGameVersion::pokeruby, 1));
|
||||
ui->comboBox_EventsTabIcon->addItem("Red", ProjectConfig::getPlayerIconPath(BaseGameVersion::pokefirered, 0));
|
||||
ui->comboBox_EventsTabIcon->addItem("Green", ProjectConfig::getPlayerIconPath(BaseGameVersion::pokefirered, 1));
|
||||
ui->comboBox_EventsTabIcon->addItem("Brendan (Emerald)", BaseGame::getPlayerIconPath(BaseGame::Version::pokeemerald, 0));
|
||||
ui->comboBox_EventsTabIcon->addItem("Brendan (R/S)", BaseGame::getPlayerIconPath(BaseGame::Version::pokeruby, 0));
|
||||
ui->comboBox_EventsTabIcon->addItem("May (Emerald)", BaseGame::getPlayerIconPath(BaseGame::Version::pokeemerald, 1));
|
||||
ui->comboBox_EventsTabIcon->addItem("May (R/S)", BaseGame::getPlayerIconPath(BaseGame::Version::pokeruby, 1));
|
||||
ui->comboBox_EventsTabIcon->addItem("Red", BaseGame::getPlayerIconPath(BaseGame::Version::pokefirered, 0));
|
||||
ui->comboBox_EventsTabIcon->addItem("Green", BaseGame::getPlayerIconPath(BaseGame::Version::pokefirered, 1));
|
||||
ui->comboBox_EventsTabIcon->addItem("Custom", "Custom");
|
||||
connect(ui->comboBox_EventsTabIcon, QOverload<int>::of(&NoScrollComboBox::currentIndexChanged), [this](int index) {
|
||||
bool usingCustom = (index == ui->comboBox_EventsTabIcon->findText("Custom"));
|
||||
|
|
@ -147,12 +153,12 @@ void ProjectSettingsEditor::initUi() {
|
|||
ui->spinBox_Collision->setMaximum(Block::getMaxCollision());
|
||||
ui->spinBox_MaxElevation->setMaximum(Block::getMaxElevation());
|
||||
ui->spinBox_MaxCollision->setMaximum(Block::getMaxCollision());
|
||||
ui->spinBox_MetatileIdMask->setMaximum(Block::maxValue);
|
||||
ui->spinBox_CollisionMask->setMaximum(Block::maxValue);
|
||||
ui->spinBox_ElevationMask->setMaximum(Block::maxValue);
|
||||
ui->spinBox_UnusedTileNormal->setMaximum(Tile::maxValue);
|
||||
ui->spinBox_UnusedTileCovered->setMaximum(Tile::maxValue);
|
||||
ui->spinBox_UnusedTileSplit->setMaximum(Tile::maxValue);
|
||||
ui->spinBox_MetatileIdMask->setMaximum(Block::MaxValue);
|
||||
ui->spinBox_CollisionMask->setMaximum(Block::MaxValue);
|
||||
ui->spinBox_ElevationMask->setMaximum(Block::MaxValue);
|
||||
ui->spinBox_UnusedTileNormal->setMaximum(Tile::MaxValue);
|
||||
ui->spinBox_UnusedTileCovered->setMaximum(Tile::MaxValue);
|
||||
ui->spinBox_UnusedTileSplit->setMaximum(Tile::MaxValue);
|
||||
ui->spinBox_MaxEvents->setMaximum(INT_MAX);
|
||||
ui->spinBox_MapWidth->setMaximum(INT_MAX);
|
||||
ui->spinBox_MapHeight->setMaximum(INT_MAX);
|
||||
|
|
@ -434,13 +440,6 @@ QString ProjectSettingsEditor::chooseProjectFile(const QString &defaultFilepath)
|
|||
return path.remove(0, this->baseDir.length());
|
||||
}
|
||||
|
||||
void ProjectSettingsEditor::restoreWindowState() {
|
||||
logInfo("Restoring project settings editor geometry from previous session.");
|
||||
const QMap<QString, QByteArray> geometry = porymapConfig.getProjectSettingsEditorGeometry();
|
||||
this->restoreGeometry(geometry.value("project_settings_editor_geometry"));
|
||||
this->restoreState(geometry.value("project_settings_editor_state"));
|
||||
}
|
||||
|
||||
// Set UI states using config data
|
||||
void ProjectSettingsEditor::refresh() {
|
||||
this->refreshing = true; // Block signals
|
||||
|
|
@ -448,12 +447,12 @@ void ProjectSettingsEditor::refresh() {
|
|||
// Set combo box texts
|
||||
ui->comboBox_DefaultPrimaryTileset->setTextItem(projectConfig.defaultPrimaryTileset);
|
||||
ui->comboBox_DefaultSecondaryTileset->setTextItem(projectConfig.defaultSecondaryTileset);
|
||||
ui->comboBox_BaseGameVersion->setTextItem(projectConfig.getBaseGameVersionString());
|
||||
ui->comboBox_BaseGameVersion->setTextItem(BaseGame::versionToString(projectConfig.baseGameVersion));
|
||||
ui->comboBox_AttributesSize->setTextItem(QString::number(projectConfig.metatileAttributesSize));
|
||||
this->updateAttributeLimits(ui->comboBox_AttributesSize->currentText());
|
||||
|
||||
this->prevIconSpecies = QString();
|
||||
this->editedPokemonIconPaths = projectConfig.getPokemonIconPaths();
|
||||
this->editedPokemonIconPaths = projectConfig.pokemonIconPaths;
|
||||
this->updatePokemonIconPath(ui->comboBox_IconSpecies->currentText());
|
||||
|
||||
// Set check box states
|
||||
|
|
@ -511,13 +510,13 @@ void ProjectSettingsEditor::refresh() {
|
|||
this->setBorderMetatileIds(true, projectConfig.newMapBorderMetatileIds);
|
||||
|
||||
// Set line edit texts
|
||||
ui->lineEdit_PrefabsPath->setText(projectConfig.prefabFilepath);
|
||||
ui->lineEdit_PrefabsPath->setText(userConfig.prefabsFilepath);
|
||||
ui->lineEdit_CollisionGraphics->setText(projectConfig.collisionSheetPath);
|
||||
ui->lineEdit_ObjectsIcon->setText(projectConfig.getEventIconPath(Event::Group::Object));
|
||||
ui->lineEdit_WarpsIcon->setText(projectConfig.getEventIconPath(Event::Group::Warp));
|
||||
ui->lineEdit_TriggersIcon->setText(projectConfig.getEventIconPath(Event::Group::Coord));
|
||||
ui->lineEdit_BGsIcon->setText(projectConfig.getEventIconPath(Event::Group::Bg));
|
||||
ui->lineEdit_HealLocationsIcon->setText(projectConfig.getEventIconPath(Event::Group::Heal));
|
||||
ui->lineEdit_ObjectsIcon->setText(projectConfig.eventIconPaths.value(Event::Group::Object));
|
||||
ui->lineEdit_WarpsIcon->setText(projectConfig.eventIconPaths.value(Event::Group::Warp));
|
||||
ui->lineEdit_TriggersIcon->setText(projectConfig.eventIconPaths.value(Event::Group::Coord));
|
||||
ui->lineEdit_BGsIcon->setText(projectConfig.eventIconPaths.value(Event::Group::Bg));
|
||||
ui->lineEdit_HealLocationsIcon->setText(projectConfig.eventIconPaths.value(Event::Group::Heal));
|
||||
for (auto lineEdit : ui->scrollAreaContents_ProjectPaths->findChildren<QLineEdit*>())
|
||||
lineEdit->setText(projectConfig.getCustomFilePath(lineEdit->objectName()));
|
||||
for (auto lineEdit : ui->scrollAreaContents_Identifiers->findChildren<QLineEdit*>())
|
||||
|
|
@ -556,7 +555,7 @@ void ProjectSettingsEditor::save() {
|
|||
// Save combo box settings
|
||||
projectConfig.defaultPrimaryTileset = ui->comboBox_DefaultPrimaryTileset->currentText();
|
||||
projectConfig.defaultSecondaryTileset = ui->comboBox_DefaultSecondaryTileset->currentText();
|
||||
projectConfig.baseGameVersion = projectConfig.stringToBaseGameVersion(ui->comboBox_BaseGameVersion->currentText());
|
||||
projectConfig.baseGameVersion = BaseGame::stringToVersion(ui->comboBox_BaseGameVersion->currentText());
|
||||
projectConfig.metatileAttributesSize = ui->comboBox_AttributesSize->currentText().toInt();
|
||||
|
||||
// Save check box settings
|
||||
|
|
@ -603,13 +602,13 @@ void ProjectSettingsEditor::save() {
|
|||
projectConfig.metatileSelectorWidth = ui->spinBox_MetatileSelectorWidth->value();
|
||||
|
||||
// Save line edit settings
|
||||
projectConfig.prefabFilepath = ui->lineEdit_PrefabsPath->text();
|
||||
userConfig.prefabsFilepath = ui->lineEdit_PrefabsPath->text();
|
||||
projectConfig.collisionSheetPath = ui->lineEdit_CollisionGraphics->text();
|
||||
projectConfig.setEventIconPath(Event::Group::Object, ui->lineEdit_ObjectsIcon->text());
|
||||
projectConfig.setEventIconPath(Event::Group::Warp, ui->lineEdit_WarpsIcon->text());
|
||||
projectConfig.setEventIconPath(Event::Group::Coord, ui->lineEdit_TriggersIcon->text());
|
||||
projectConfig.setEventIconPath(Event::Group::Bg, ui->lineEdit_BGsIcon->text());
|
||||
projectConfig.setEventIconPath(Event::Group::Heal, ui->lineEdit_HealLocationsIcon->text());
|
||||
projectConfig.eventIconPaths[Event::Group::Object] = ui->lineEdit_ObjectsIcon->text();
|
||||
projectConfig.eventIconPaths[Event::Group::Warp] = ui->lineEdit_WarpsIcon->text();
|
||||
projectConfig.eventIconPaths[Event::Group::Coord] = ui->lineEdit_TriggersIcon->text();
|
||||
projectConfig.eventIconPaths[Event::Group::Bg] = ui->lineEdit_BGsIcon->text();
|
||||
projectConfig.eventIconPaths[Event::Group::Heal] = ui->lineEdit_HealLocationsIcon->text();
|
||||
for (auto lineEdit : ui->scrollAreaContents_ProjectPaths->findChildren<QLineEdit*>())
|
||||
projectConfig.setFilePath(lineEdit->objectName(), lineEdit->text());
|
||||
for (auto lineEdit : ui->scrollAreaContents_Identifiers->findChildren<QLineEdit*>())
|
||||
|
|
@ -623,7 +622,7 @@ void ProjectSettingsEditor::save() {
|
|||
projectConfig.warpBehaviors.clear();
|
||||
const QStringList behaviorNames = this->getWarpBehaviorsList();
|
||||
for (auto name : behaviorNames)
|
||||
projectConfig.warpBehaviors.append(project->metatileBehaviorMap.value(name));
|
||||
projectConfig.warpBehaviors.insert(project->metatileBehaviorMap.value(name));
|
||||
|
||||
// Save border metatile IDs
|
||||
projectConfig.newMapBorderMetatileIds = this->getBorderMetatileIds(ui->checkBox_EnableCustomBorderSize->isChecked());
|
||||
|
|
@ -633,7 +632,7 @@ void ProjectSettingsEditor::save() {
|
|||
if (this->project->speciesNames.contains(species))
|
||||
this->editedPokemonIconPaths.insert(species, ui->lineEdit_PokemonIcon->text());
|
||||
for (auto i = this->editedPokemonIconPaths.cbegin(), end = this->editedPokemonIconPaths.cend(); i != end; i++)
|
||||
projectConfig.setPokemonIconPath(i.key(), i.value());
|
||||
projectConfig.pokemonIconPaths[i.key()] = i.value();
|
||||
|
||||
QString eventsTabIconPath;
|
||||
QVariant data = ui->comboBox_EventsTabIcon->currentData();
|
||||
|
|
@ -773,9 +772,9 @@ QString ProjectSettingsEditor::stripProjectDir(QString s) {
|
|||
|
||||
void ProjectSettingsEditor::importDefaultPrefabsClicked(bool) {
|
||||
// If the prompt is accepted the prefabs file will be created and its filepath will be saved in the config.
|
||||
BaseGameVersion version = projectConfig.stringToBaseGameVersion(ui->comboBox_BaseGameVersion->currentText());
|
||||
BaseGame::Version version = BaseGame::stringToVersion(ui->comboBox_BaseGameVersion->currentText());
|
||||
if (prefab.tryImportDefaultPrefabs(this, version, ui->lineEdit_PrefabsPath->text())) {
|
||||
ui->lineEdit_PrefabsPath->setText(projectConfig.prefabFilepath); // Refresh with new filepath
|
||||
ui->lineEdit_PrefabsPath->setText(userConfig.prefabsFilepath); // Refresh with new filepath
|
||||
this->hasUnsavedChanges = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -815,8 +814,9 @@ bool ProjectSettingsEditor::promptRestoreDefaults() {
|
|||
|
||||
// Restore defaults by resetting config in memory, refreshing the UI, then restoring the config.
|
||||
// Don't want to save changes until user accepts them.
|
||||
// TODO: Maybe give the project settings editor it's own copy of the config then.
|
||||
ProjectConfig tempProject = projectConfig;
|
||||
projectConfig.reset(projectConfig.stringToBaseGameVersion(versionText));
|
||||
projectConfig.setVersionSpecificDefaults(BaseGame::stringToVersion(versionText));
|
||||
this->refresh();
|
||||
projectConfig = tempProject;
|
||||
|
||||
|
|
@ -863,11 +863,6 @@ void ProjectSettingsEditor::closeEvent(QCloseEvent* event) {
|
|||
return;
|
||||
}
|
||||
|
||||
porymapConfig.setProjectSettingsEditorGeometry(
|
||||
this->saveGeometry(),
|
||||
this->saveState()
|
||||
);
|
||||
|
||||
if (this->projectNeedsReload) {
|
||||
// Note: Declining this prompt with changes that need a reload may cause problems
|
||||
if (this->prompt("Settings saved, reload project to apply changes?") == QMessageBox::Yes)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include "config.h"
|
||||
#include "log.h"
|
||||
#include "utility.h"
|
||||
#include "eventfilters.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QDialog>
|
||||
|
|
@ -33,7 +34,7 @@ RegionMapEditor::RegionMapEditor(QWidget *parent, Project *project) :
|
|||
|
||||
this->configFilepath = QString("%1/%2").arg(this->project->root).arg(projectConfig.getFilePath(ProjectFilePath::json_region_porymap_cfg));
|
||||
this->initShortcuts();
|
||||
this->restoreWindowState();
|
||||
this->installEventFilter(new GeometrySaver(this));
|
||||
}
|
||||
|
||||
RegionMapEditor::~RegionMapEditor()
|
||||
|
|
@ -56,13 +57,6 @@ RegionMapEditor::~RegionMapEditor()
|
|||
delete ui;
|
||||
}
|
||||
|
||||
void RegionMapEditor::restoreWindowState() {
|
||||
logInfo("Restoring region map editor geometry from previous session.");
|
||||
QMap<QString, QByteArray> geometry = porymapConfig.getRegionMapEditorGeometry();
|
||||
this->restoreGeometry(geometry.value("region_map_editor_geometry"));
|
||||
this->restoreState(geometry.value("region_map_editor_state"));
|
||||
}
|
||||
|
||||
void RegionMapEditor::initShortcuts() {
|
||||
auto *shortcut_RM_Options_delete = new Shortcut(
|
||||
{QKeySequence("Del"), QKeySequence("Backspace")}, this, SLOT(on_pushButton_RM_Options_delete_clicked()));
|
||||
|
|
@ -80,7 +74,6 @@ void RegionMapEditor::initShortcuts() {
|
|||
ui->menuEdit->addAction(undoAction);
|
||||
ui->menuEdit->addAction(redoAction);
|
||||
|
||||
shortcutsConfig.load();
|
||||
shortcutsConfig.setDefaultShortcuts(shortcutableObjects());
|
||||
applyUserShortcuts();
|
||||
|
||||
|
|
@ -144,13 +137,13 @@ void buildFireredDefaults(poryjson::Json &json) {
|
|||
poryjson::Json RegionMapEditor::buildDefaultJson() {
|
||||
poryjson::Json defaultJson;
|
||||
switch (projectConfig.baseGameVersion) {
|
||||
case BaseGameVersion::pokeemerald:
|
||||
case BaseGame::Version::pokeemerald:
|
||||
buildEmeraldDefaults(defaultJson);
|
||||
break;
|
||||
case BaseGameVersion::pokeruby:
|
||||
case BaseGame::Version::pokeruby:
|
||||
buildRubyDefaults(defaultJson);
|
||||
break;
|
||||
case BaseGameVersion::pokefirered:
|
||||
case BaseGame::Version::pokefirered:
|
||||
buildFireredDefaults(defaultJson);
|
||||
break;
|
||||
default:
|
||||
|
|
@ -285,13 +278,13 @@ bool RegionMapEditor::buildConfigDialog() {
|
|||
// for sake of convenience, option to just use defaults for each basegame version
|
||||
QPushButton *config_useProjectDefault = nullptr;
|
||||
switch (projectConfig.baseGameVersion) {
|
||||
case BaseGameVersion::pokefirered:
|
||||
case BaseGame::Version::pokefirered:
|
||||
config_useProjectDefault = new QPushButton("\nUse pokefirered defaults\n");
|
||||
break;
|
||||
case BaseGameVersion::pokeemerald:
|
||||
case BaseGame::Version::pokeemerald:
|
||||
config_useProjectDefault = new QPushButton("\nUse pokeemerald defaults\n");
|
||||
break;
|
||||
case BaseGameVersion::pokeruby:
|
||||
case BaseGame::Version::pokeruby:
|
||||
config_useProjectDefault = new QPushButton("\nUse pokeruby defaults\n");
|
||||
break;
|
||||
default:
|
||||
|
|
@ -1251,11 +1244,6 @@ void RegionMapEditor::closeEvent(QCloseEvent *event)
|
|||
} else {
|
||||
event->accept();
|
||||
}
|
||||
|
||||
porymapConfig.setRegionMapEditorGeometry(
|
||||
this->saveGeometry(),
|
||||
this->saveState()
|
||||
);
|
||||
}
|
||||
|
||||
void RegionMapEditor::on_verticalSlider_Zoom_Map_Image_valueChanged(int val) {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include "multikeyedit.h"
|
||||
#include "message.h"
|
||||
#include "log.h"
|
||||
#include "eventfilters.h"
|
||||
|
||||
#include <QGroupBox>
|
||||
#include <QFormLayout>
|
||||
|
|
@ -22,6 +23,7 @@ ShortcutsEditor::ShortcutsEditor(QWidget *parent) :
|
|||
{
|
||||
ui->setupUi(this);
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
installEventFilter(new GeometrySaver(this));
|
||||
main_container = ui->scrollAreaWidgetContents_Shortcuts;
|
||||
auto *main_layout = new QVBoxLayout(main_container);
|
||||
main_layout->setSpacing(12);
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ TilesetEditor::TilesetEditor(Project *project, Layout *layout, QWidget *parent)
|
|||
|
||||
ActiveWindowFilter *filter = new ActiveWindowFilter(this);
|
||||
connect(filter, &ActiveWindowFilter::activated, this, &TilesetEditor::onWindowActivated);
|
||||
this->installEventFilter(filter);
|
||||
installEventFilter(filter);
|
||||
|
||||
setTilesets(this->layout->tileset_primary_label, this->layout->tileset_secondary_label);
|
||||
|
||||
|
|
@ -75,7 +75,7 @@ TilesetEditor::TilesetEditor(Project *project, Layout *layout, QWidget *parent)
|
|||
initShortcuts();
|
||||
setMetatileLayerOrientation(porymapConfig.tilesetEditorLayerOrientation);
|
||||
this->metatileSelector->select(0);
|
||||
restoreWindowState();
|
||||
installEventFilter(new GeometrySaver(this));
|
||||
}
|
||||
|
||||
TilesetEditor::~TilesetEditor()
|
||||
|
|
@ -233,7 +233,7 @@ void TilesetEditor::setMetatileLayerOrientation(Qt::Orientation orientation) {
|
|||
|
||||
int numTilesWide = Metatile::tileWidth();
|
||||
int numTilesTall = Metatile::tileHeight();
|
||||
int numLayers = projectConfig.getNumLayersInMetatile();
|
||||
int numLayers = Metatile::numLayers();
|
||||
if (horizontal) {
|
||||
numTilesWide *= numLayers;
|
||||
} else {
|
||||
|
|
@ -339,8 +339,6 @@ void TilesetEditor::initSelectedTileItem() {
|
|||
|
||||
void TilesetEditor::initShortcuts() {
|
||||
initExtraShortcuts();
|
||||
|
||||
shortcutsConfig.load();
|
||||
shortcutsConfig.setDefaultShortcuts(shortcutableObjects());
|
||||
applyUserShortcuts();
|
||||
}
|
||||
|
|
@ -379,14 +377,6 @@ void TilesetEditor::applyUserShortcuts() {
|
|||
shortcut->setKeys(shortcutsConfig.userShortcuts(shortcut));
|
||||
}
|
||||
|
||||
void TilesetEditor::restoreWindowState() {
|
||||
logInfo("Restoring tileset editor geometry from previous session.");
|
||||
QMap<QString, QByteArray> geometry = porymapConfig.getTilesetEditorGeometry();
|
||||
this->restoreGeometry(geometry.value("tileset_editor_geometry"));
|
||||
this->restoreState(geometry.value("tileset_editor_state"));
|
||||
this->ui->splitter->restoreState(geometry.value("tileset_editor_splitter_state"));
|
||||
}
|
||||
|
||||
void TilesetEditor::onWindowActivated() {
|
||||
// User may have made layout edits since window was last focused, so update counts
|
||||
if (this->metatileSelector) {
|
||||
|
|
@ -853,11 +843,6 @@ void TilesetEditor::closeEvent(QCloseEvent *event)
|
|||
|
||||
if (event->isAccepted()) {
|
||||
if (this->paletteEditor) this->paletteEditor->close();
|
||||
porymapConfig.setTilesetEditorGeometry(
|
||||
this->saveGeometry(),
|
||||
this->saveState(),
|
||||
this->ui->splitter->saveState()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -930,7 +915,7 @@ bool TilesetEditor::replaceMetatile(uint16_t metatileId, const Metatile &src, QS
|
|||
|
||||
// Update tile usage if any tiles changed
|
||||
if (this->tileSelector && this->tileSelector->showUnused) {
|
||||
int numTiles = projectConfig.getNumTilesInMetatile();
|
||||
int numTiles = qMin(src.tiles.length(), dest->tiles.length());
|
||||
for (int i = 0; i < numTiles; i++) {
|
||||
if (src.tiles[i].tileId != dest->tiles[i].tileId) {
|
||||
this->tileSelector->usedTiles[src.tiles[i].tileId] += 1;
|
||||
|
|
@ -995,7 +980,7 @@ void TilesetEditor::on_actionRedo_triggered() {
|
|||
void TilesetEditor::on_actionCut_triggered()
|
||||
{
|
||||
this->copyMetatile(true);
|
||||
this->pasteMetatile(Metatile(projectConfig.getNumTilesInMetatile()), "");
|
||||
this->pasteMetatile(Metatile(Metatile::maxTiles()), "");
|
||||
}
|
||||
|
||||
void TilesetEditor::on_actionCopy_triggered()
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include "config.h"
|
||||
#include "utility.h"
|
||||
#include "message.h"
|
||||
#include "eventfilters.h"
|
||||
|
||||
static const QString baseWindowTitle = QString("Wild Pokémon Summary Charts");
|
||||
|
||||
|
|
@ -25,6 +26,7 @@ WildMonChart::WildMonChart(QWidget *parent, const EncounterTableModel *table) :
|
|||
ui->setupUi(this);
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
setWindowFlags(Qt::Window);
|
||||
installEventFilter(new GeometrySaver(this));
|
||||
|
||||
connect(ui->button_Help, &QAbstractButton::clicked, this, &WildMonChart::showHelpDialog);
|
||||
|
||||
|
|
@ -49,8 +51,6 @@ WildMonChart::WildMonChart(QWidget *parent, const EncounterTableModel *table) :
|
|||
porymapConfig.wildMonChartTheme = ui->comboBox_Theme->currentText();
|
||||
}
|
||||
|
||||
restoreGeometry(porymapConfig.wildMonChartGeometry);
|
||||
|
||||
setTable(table);
|
||||
};
|
||||
|
||||
|
|
@ -461,9 +461,4 @@ void WildMonChart::showHelpDialog() {
|
|||
InfoMessage::show(text, informativeText, this);
|
||||
}
|
||||
|
||||
void WildMonChart::closeEvent(QCloseEvent *event) {
|
||||
porymapConfig.wildMonChartGeometry = saveGeometry();
|
||||
QWidget::closeEvent(event);
|
||||
}
|
||||
|
||||
#endif // QT_CHARTS_LIB
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user