diff --git a/CHANGELOG.md b/CHANGELOG.md index d12def26..8f6dd2b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ The **"Breaking Changes"** listed below are changes that have been made in the d ## [Unreleased] ### Added +- Add new config options for reorganizing metatile attributes. - Add `setScale` to the scripting API. - Add option to turn off the checkerboard fill for new tilesets. diff --git a/docsrc/manual/settings-and-options.rst b/docsrc/manual/settings-and-options.rst index bd949c13..3679f3d7 100644 --- a/docsrc/manual/settings-and-options.rst +++ b/docsrc/manual/settings-and-options.rst @@ -48,6 +48,7 @@ your project root as ``porymap.user.cfg``. You should add this file to your giti ``enable_hidden_item_requires_itemfinder``, 1 if ``pokefirered``, project, yes, Adds ``Requires Itemfinder`` to Hidden Item events ``enable_heal_location_respawn_data``, 1 if ``pokefirered``, project, yes, Adds ``Respawn Map`` and ``Respawn NPC`` to Heal Location events ``enable_floor_number``, 1 if ``pokefirered``, project, yes, Adds ``Floor Number`` to map headers + ``enable_map_allow_flags``, 1 if not ``pokeruby``, project, yes, "Adds ``Allow Running``, ``Allow Biking``, and ``Allow Dig & Escape Rope`` to map headers" ``create_map_text_file``, 1 if not ``pokeemerald``, project, yes, A ``text.inc`` or ``text.pory`` file will be created for any new map ``enable_triple_layer_metatiles``, 0, project, yes, Enables triple-layer metatiles (See https://github.com/pret/pokeemerald/wiki/Triple-layer-metatiles) ``new_map_metatile``, 1, project, yes, The metatile id that will be used to fill new maps @@ -60,5 +61,10 @@ your project root as ``porymap.user.cfg``. You should add this file to your giti ``prefabs_import_prompted``, 0, project, no, Keeps track of whether or not the project was prompted for importing default prefabs ``tilesets_have_callback``, 1, project, yes, Whether new tileset headers should have the ``callback`` field ``tilesets_have_is_compressed``, 1, project, yes, Whether new tileset headers should have the ``isCompressed`` field + ``metatile_attributes_size``, 2 or 4, project, yes, The number of attribute bytes each metatile has + ``metatile_behavior_mask``, ``0xFF`` or ``0x1FF``, project, yes, The mask for the metatile Behavior attribute + ``metatile_encounter_type_mask``, ``0x0`` or ``0x7000000``, project, yes, The mask for the metatile Encounter Type attribute + ``metatile_layer_type_mask``, ``0xF000`` or ``0x60000000``, project, yes, The mask for the metatile Layer Type attribute + ``metatile_terrain_type_mask``, ``0x0`` or ``0x3E00``, project, yes, The mask for the metatile Terrain Type attribute Some of these settings can be toggled manually in porymap via the *Options* menu. diff --git a/include/config.h b/include/config.h index 20b83af9..6d8ce245 100644 --- a/include/config.h +++ b/include/config.h @@ -36,6 +36,7 @@ protected: virtual void setUnreadKeys() = 0; bool getConfigBool(QString key, QString value); int getConfigInteger(QString key, QString value, int min, int max, int defaultValue); + long getConfigLong(QString key, QString value, long min, long max, long defaultValue); }; class PorymapConfig: public KeyValueConfigBase @@ -268,6 +269,12 @@ public: bool getTilesetsHaveCallback(); void setTilesetsHaveIsCompressed(bool has); bool getTilesetsHaveIsCompressed(); + int getMetatileAttributesSize(); + uint32_t getMetatileBehaviorMask(); + uint32_t getMetatileTerrainTypeMask(); + uint32_t getMetatileEncounterTypeMask(); + uint32_t getMetatileLayerTypeMask(); + bool getMapAllowFlagsEnabled(); protected: virtual QString getConfigFilepath() override; virtual void parseConfigKeyValue(QString key, QString value) override; @@ -299,6 +306,12 @@ private: bool prefabImportPrompted; bool tilesetsHaveCallback; bool tilesetsHaveIsCompressed; + int metatileAttributesSize; + uint32_t metatileBehaviorMask; + uint32_t metatileTerrainTypeMask; + uint32_t metatileEncounterTypeMask; + uint32_t metatileLayerTypeMask; + bool enableMapAllowFlags; }; extern ProjectConfig projectConfig; diff --git a/include/core/metatile.h b/include/core/metatile.h index eb315ab8..f606dcdb 100644 --- a/include/core/metatile.h +++ b/include/core/metatile.h @@ -8,6 +8,8 @@ #include #include +class Project; + enum { METATILE_LAYER_MIDDLE_TOP, METATILE_LAYER_BOTTOM_MIDDLE, @@ -30,6 +32,29 @@ enum { NUM_METATILE_TERRAIN_TYPES }; +class MetatileAttr +{ +public: + MetatileAttr(); + MetatileAttr(uint32_t mask, int shift); + +public: + uint32_t mask; + int shift; + + // Given the raw value for all attributes of a metatile + // Returns the extracted value for this attribute + uint32_t fromRaw(uint32_t raw) const { return (raw & this->mask) >> this->shift; } + + // Given a value for this attribute + // Returns the raw value to OR together with the other attributes + uint32_t toRaw(uint32_t value) const { return (value << this->shift) & this->mask; } + + // Given an arbitrary value to set for an attribute + // Returns a bounded value for that attribute + uint32_t getClamped(int value) const { return static_cast(value) & (this->mask >> this->shift); } +}; + class Metatile { public: @@ -40,19 +65,54 @@ public: public: QList tiles; - uint16_t behavior; // 8 bits RSE, 9 bits FRLG - uint8_t layerType; - uint8_t encounterType; // FRLG only - uint8_t terrainType; // FRLG only + uint32_t behavior; + uint32_t terrainType; + uint32_t encounterType; + uint32_t layerType; uint32_t unusedAttributes; QString label; + uint32_t getAttributes(); + void setAttributes(uint32_t data); void setAttributes(uint32_t data, BaseGameVersion version); - uint32_t getAttributes(BaseGameVersion version); + + void setBehavior(int value) { this->behavior = behaviorAttr.getClamped(value); } + void setTerrainType(int value) { this->terrainType = terrainTypeAttr.getClamped(value); } + void setEncounterType(int value) { this->encounterType = encounterTypeAttr.getClamped(value); } + void setLayerType(int value) { this->layerType = layerTypeAttr.getClamped(value); } + + static uint32_t getBehaviorMask() { return behaviorAttr.mask; } + static uint32_t getTerrainTypeMask() { return terrainTypeAttr.mask; } + static uint32_t getEncounterTypeMask() { return encounterTypeAttr.mask; } + static uint32_t getLayerTypeMask() { return layerTypeAttr.mask; } + static uint32_t getBehaviorMask(BaseGameVersion version); + static uint32_t getTerrainTypeMask(BaseGameVersion version); + static uint32_t getEncounterTypeMask(BaseGameVersion version); + static uint32_t getLayerTypeMask(BaseGameVersion version); static int getIndexInTileset(int); static QPoint coordFromPixmapCoord(const QPointF &pixelCoord); - static int getAttributesSize(BaseGameVersion version); + static int getDefaultAttributesSize(BaseGameVersion version); + static void setCustomLayout(Project*); + +private: + // Stores how each attribute should be laid out for all metatiles, according to the user's config + static MetatileAttr behaviorAttr; + static MetatileAttr terrainTypeAttr; + static MetatileAttr encounterTypeAttr; + static MetatileAttr layerTypeAttr; + + static uint32_t unusedAttrMask; + + // Stores how each attribute should be laid out for all metatiles, according to the vanilla games + // Used to set default config values and import maps with AdvanceMap + static const QHash defaultLayoutFRLG; + static const QHash defaultLayoutRSE; + static const QHash*> defaultLayouts; + + static void setCustomAttributeLayout(MetatileAttr *, uint32_t, uint32_t); + static bool isMaskTooSmall(MetatileAttr *, int); + static bool doMasksOverlap(QList); }; inline bool operator==(const Metatile &a, const Metatile &b) { diff --git a/include/project.h b/include/project.h index c5599489..4e97a9bd 100644 --- a/include/project.h +++ b/include/project.h @@ -231,7 +231,6 @@ public: private: void updateMapLayout(Map*); - void setNewMapHeader(Map* map, int mapIndex); void setNewMapBlockdata(Map* map); void setNewMapBorder(Map *map); void setNewMapEvents(Map *map); diff --git a/include/ui/tileseteditor.h b/include/ui/tileseteditor.h index 9ac4bf9f..fcdd3bed 100644 --- a/include/ui/tileseteditor.h +++ b/include/ui/tileseteditor.h @@ -111,9 +111,7 @@ private slots: private: void initUi(); - void setMetatileBehaviors(); - void setMetatileLayersUi(); - void setVersionSpecificUi(); + void setAttributesUi(); void setMetatileLabelValidator(); void initMetatileSelector(); void initTileSelector(); diff --git a/src/config.cpp b/src/config.cpp index 7dee8cbb..9b48664a 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -136,7 +136,7 @@ void KeyValueConfigBase::save() { bool KeyValueConfigBase::getConfigBool(QString key, QString value) { bool ok; - int result = value.toInt(&ok); + int result = value.toInt(&ok, 0); if (!ok || (result != 0 && result != 1)) { logWarn(QString("Invalid config value for %1: '%2'. Must be 0 or 1.").arg(key).arg(value)); } @@ -145,7 +145,17 @@ bool KeyValueConfigBase::getConfigBool(QString key, QString value) { int KeyValueConfigBase::getConfigInteger(QString key, QString value, int min, int max, int defaultValue) { bool ok; - int result = value.toInt(&ok); + int result = value.toInt(&ok, 0); + if (!ok) { + logWarn(QString("Invalid config value for %1: '%2'. Must be an integer.").arg(key).arg(value)); + return defaultValue; + } + return qMin(max, qMax(min, result)); +} + +long KeyValueConfigBase::getConfigLong(QString key, QString value, long min, long max, long defaultValue) { + bool ok; + long result = value.toLong(&ok, 0); if (!ok) { logWarn(QString("Invalid config value for %1: '%2'. Must be an integer.").arg(key).arg(value)); return defaultValue; @@ -561,6 +571,23 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) { this->defaultPrimaryTileset = value; } else if (key == "default_secondary_tileset") { this->defaultSecondaryTileset = value; + } else if (key == "metatile_attributes_size") { + int size = getConfigInteger(key, value, 1, 4, 2); + if (size & (size - 1)) { + logWarn(QString("Invalid config value for %1: must be 1, 2, or 4").arg(key)); + return; // Don't set a default now, it will be set later based on the base game version + } + this->metatileAttributesSize = size; + } else if (key == "metatile_behavior_mask") { + this->metatileBehaviorMask = getConfigLong(key, value, 0, 0xFFFFFFFF, 0); + } else if (key == "metatile_terrain_type_mask") { + this->metatileTerrainTypeMask = getConfigLong(key, value, 0, 0xFFFFFFFF, 0); + } else if (key == "metatile_encounter_type_mask") { + this->metatileEncounterTypeMask = getConfigLong(key, value, 0, 0xFFFFFFFF, 0); + } else if (key == "metatile_layer_type_mask") { + this->metatileLayerTypeMask = getConfigLong(key, value, 0, 0xFFFFFFFF, 0); + } else if (key == "enable_map_allow_flags") { + this->enableMapAllowFlags = getConfigBool(key, value); #ifdef CONFIG_BACKWARDS_COMPATABILITY } else if (key == "recent_map") { userConfig.setRecentMap(value); @@ -611,6 +638,12 @@ void ProjectConfig::setUnreadKeys() { if (!readKeys.contains("create_map_text_file")) this->createMapTextFile = (this->baseGameVersion != BaseGameVersion::pokeemerald); if (!readKeys.contains("new_map_border_metatiles")) this->newMapBorderMetatileIds = isPokefirered ? DEFAULT_BORDER_FRLG : DEFAULT_BORDER_RSE; if (!readKeys.contains("default_secondary_tileset")) this->defaultSecondaryTileset = isPokefirered ? "gTileset_PalletTown" : "gTileset_Petalburg"; + if (!readKeys.contains("metatile_attributes_size")) this->metatileAttributesSize = Metatile::getDefaultAttributesSize(this->baseGameVersion); + if (!readKeys.contains("metatile_behavior_mask")) this->metatileBehaviorMask = Metatile::getBehaviorMask(this->baseGameVersion); + if (!readKeys.contains("metatile_terrain_type_mask")) this->metatileTerrainTypeMask = Metatile::getTerrainTypeMask(this->baseGameVersion); + if (!readKeys.contains("metatile_encounter_type_mask")) this->metatileEncounterTypeMask = Metatile::getEncounterTypeMask(this->baseGameVersion); + if (!readKeys.contains("metatile_layer_type_mask")) this-> metatileLayerTypeMask = Metatile::getLayerTypeMask(this->baseGameVersion); + if (!readKeys.contains("enable_map_allow_flags")) this->enableMapAllowFlags = (this->baseGameVersion != BaseGameVersion::pokeruby); } QMap ProjectConfig::getKeyValueMap() { @@ -642,6 +675,12 @@ QMap ProjectConfig::getKeyValueMap() { } map.insert("tilesets_have_callback", QString::number(this->tilesetsHaveCallback)); map.insert("tilesets_have_is_compressed", QString::number(this->tilesetsHaveIsCompressed)); + map.insert("metatile_attributes_size", QString::number(this->metatileAttributesSize)); + map.insert("metatile_behavior_mask", "0x" + QString::number(this->metatileBehaviorMask, 16).toUpper()); + map.insert("metatile_terrain_type_mask", "0x" + QString::number(this->metatileTerrainTypeMask, 16).toUpper()); + map.insert("metatile_encounter_type_mask", "0x" + QString::number(this->metatileEncounterTypeMask, 16).toUpper()); + map.insert("metatile_layer_type_mask", "0x" + QString::number(this->metatileLayerTypeMask, 16).toUpper()); + map.insert("enable_map_allow_flags", QString::number(this->enableMapAllowFlags)); return map; } @@ -891,6 +930,30 @@ bool ProjectConfig::getTilesetsHaveIsCompressed() { return this->tilesetsHaveIsCompressed; } +int ProjectConfig::getMetatileAttributesSize() { + return this->metatileAttributesSize; +} + +uint32_t ProjectConfig::getMetatileBehaviorMask() { + return this->metatileBehaviorMask; +} + +uint32_t ProjectConfig::getMetatileTerrainTypeMask() { + return this->metatileTerrainTypeMask; +} + +uint32_t ProjectConfig::getMetatileEncounterTypeMask() { + return this->metatileEncounterTypeMask; +} + +uint32_t ProjectConfig::getMetatileLayerTypeMask() { + return this->metatileLayerTypeMask; +} + +bool ProjectConfig::getMapAllowFlagsEnabled() { + return this->enableMapAllowFlags; +} + UserConfig userConfig; diff --git a/src/core/metatile.cpp b/src/core/metatile.cpp index 6264fda2..160b0c42 100644 --- a/src/core/metatile.cpp +++ b/src/core/metatile.cpp @@ -2,19 +2,56 @@ #include "tileset.h" #include "project.h" +const QHash Metatile::defaultLayoutFRLG = { + {"behavior", MetatileAttr(0x000001FF, 0) }, + {"terrainType", MetatileAttr(0x00003E00, 9) }, + {"encounterType", MetatileAttr(0x07000000, 24) }, + {"layerType", MetatileAttr(0x60000000, 29) }, +}; + +const QHash Metatile::defaultLayoutRSE = { + {"behavior", MetatileAttr(0x00FF, 0) }, + {"terrainType", MetatileAttr() }, + {"encounterType", MetatileAttr() }, + {"layerType", MetatileAttr(0xF000, 12) }, +}; + +const QHash*> Metatile::defaultLayouts = { + { BaseGameVersion::pokeruby, &defaultLayoutRSE }, + { BaseGameVersion::pokefirered, &defaultLayoutFRLG }, + { BaseGameVersion::pokeemerald, &defaultLayoutRSE }, +}; + +MetatileAttr Metatile::behaviorAttr; +MetatileAttr Metatile::terrainTypeAttr; +MetatileAttr Metatile::encounterTypeAttr; +MetatileAttr Metatile::layerTypeAttr; + +uint32_t Metatile::unusedAttrMask = 0; + +MetatileAttr::MetatileAttr() : + mask(0), + shift(0) +{ } + +MetatileAttr::MetatileAttr(uint32_t mask, int shift) : + mask(mask), + shift(shift) +{ } + Metatile::Metatile() : behavior(0), - layerType(0), - encounterType(0), terrainType(0), + encounterType(0), + layerType(0), unusedAttributes(0) { } Metatile::Metatile(const int numTiles) : behavior(0), - layerType(0), - encounterType(0), terrainType(0), + encounterType(0), + layerType(0), unusedAttributes(0) { Tile tile = Tile(); @@ -37,50 +74,120 @@ QPoint Metatile::coordFromPixmapCoord(const QPointF &pixelCoord) { return QPoint(x, y); } -int Metatile::getAttributesSize(BaseGameVersion version) { - return (version == BaseGameVersion::pokefirered) ? 4 : 2; +// Set the layout of a metatile attribute using the mask read from the config file +void Metatile::setCustomAttributeLayout(MetatileAttr * attr, uint32_t mask, uint32_t max) { + if (mask > max) { + uint32_t oldMask = mask; + mask &= max; + logWarn(QString("Metatile attribute mask '0x%1' has been truncated to '0x%2'") + .arg(QString::number(oldMask, 16).toUpper()) + .arg(QString::number(mask, 16).toUpper())); + } + attr->mask = mask; + attr->shift = mask ? log2(mask & ~(mask - 1)) : 0; // Get position of the least significant set bit } -// RSE attributes -const uint16_t behaviorMask_RSE = 0x00FF; -const uint16_t layerTypeMask_RSE = 0xF000; -const int behaviorShift_RSE = 0; -const int layerTypeShift_RSE = 12; +// For checking whether a metatile attribute mask can contain all the available hard-coded options +bool Metatile::isMaskTooSmall(MetatileAttr * attr, int max) { + if (attr->mask == 0 || max <= 0) return false; -// FRLG attributes -const uint32_t behaviorMask_FRLG = 0x000001FF; -const uint32_t terrainTypeMask = 0x00003E00; -const uint32_t encounterTypeMask = 0x07000000; -const uint32_t layerTypeMask_FRLG = 0x60000000; -const int behaviorShift_FRLG = 0; -const int terrainTypeShift = 9; -const int encounterTypeShift = 24; -const int layerTypeShift_FRLG = 29; + // Get position of the most significant set bit + uint32_t n = log2(max); -uint32_t Metatile::getAttributes(BaseGameVersion version) { - uint32_t attributes = this->unusedAttributes; - if (version == BaseGameVersion::pokefirered) { - attributes |= (behavior << behaviorShift_FRLG) & behaviorMask_FRLG; - attributes |= (terrainType << terrainTypeShift) & terrainTypeMask; - attributes |= (encounterType << encounterTypeShift) & encounterTypeMask; - attributes |= (layerType << layerTypeShift_FRLG) & layerTypeMask_FRLG; - } else { - attributes |= (behavior << behaviorShift_RSE) & behaviorMask_RSE; - attributes |= (layerType << layerTypeShift_RSE) & layerTypeMask_RSE; + // Get a mask for all values 0 to max. + // This may fail for n > 30, but that's not possible here. + uint32_t rangeMask = (1 << (n + 1)) - 1; + + return attr->getClamped(rangeMask) != rangeMask; +} + +bool Metatile::doMasksOverlap(QList masks) { + for (int i = 0; i < masks.length(); i++) + for (int j = i + 1; j < masks.length(); j++) { + if (masks.at(i) & masks.at(j)) + return true; } + return false; +} + +void Metatile::setCustomLayout(Project * project) { + // Get the maximum size of any attribute mask + const QHash maxMasks = { + {1, 0xFF}, + {2, 0xFFFF}, + {4, 0xFFFFFFFF}, + }; + const uint32_t maxMask = maxMasks.value(projectConfig.getMetatileAttributesSize(), 0); + + // Set custom attribute masks from the config file + setCustomAttributeLayout(&Metatile::behaviorAttr, projectConfig.getMetatileBehaviorMask(), maxMask); + setCustomAttributeLayout(&Metatile::terrainTypeAttr, projectConfig.getMetatileTerrainTypeMask(), maxMask); + setCustomAttributeLayout(&Metatile::encounterTypeAttr, projectConfig.getMetatileEncounterTypeMask(), maxMask); + setCustomAttributeLayout(&Metatile::layerTypeAttr, projectConfig.getMetatileLayerTypeMask(), maxMask); + + // Set mask for preserving any attribute bits not used by Porymap + Metatile::unusedAttrMask = ~(getBehaviorMask() | getTerrainTypeMask() | getEncounterTypeMask() | getLayerTypeMask()); + Metatile::unusedAttrMask &= maxMask; + + // Overlapping masks are technically ok, but probably not intended. + // Additionally, Porymap will not properly reflect that the values are linked. + if (doMasksOverlap({getBehaviorMask(), getTerrainTypeMask(), getEncounterTypeMask(), getLayerTypeMask()})) { + logWarn("Metatile attribute masks are overlapping. This may result in unexpected attribute values."); + } + + // Warn the user if they have set a nonzero mask that is too small to contain its available options. + // They'll be allowed to select the options, but they'll be truncated to a different value when revisited. + if (!project->metatileBehaviorMapInverse.isEmpty()) { + int maxBehavior = project->metatileBehaviorMapInverse.lastKey(); + if (isMaskTooSmall(&Metatile::behaviorAttr, maxBehavior)) + logWarn(QString("Metatile Behavior mask is too small to contain all %1 available options.").arg(maxBehavior)); + } + if (isMaskTooSmall(&Metatile::terrainTypeAttr, NUM_METATILE_TERRAIN_TYPES - 1)) + logWarn(QString("Metatile Terrain Type mask is too small to contain all %1 available options.").arg(NUM_METATILE_TERRAIN_TYPES)); + if (isMaskTooSmall(&Metatile::encounterTypeAttr, NUM_METATILE_ENCOUNTER_TYPES - 1)) + logWarn(QString("Metatile Encounter Type mask is too small to contain all %1 available options.").arg(NUM_METATILE_ENCOUNTER_TYPES)); + if (isMaskTooSmall(&Metatile::layerTypeAttr, NUM_METATILE_LAYER_TYPES - 1)) + logWarn(QString("Metatile Layer Type mask is too small to contain all %1 available options.").arg(NUM_METATILE_LAYER_TYPES)); +} + +uint32_t Metatile::getAttributes() { + uint32_t attributes = this->unusedAttributes & Metatile::unusedAttrMask; + attributes |= Metatile::behaviorAttr.toRaw(this->behavior); + attributes |= Metatile::terrainTypeAttr.toRaw(this->terrainType); + attributes |= Metatile::encounterTypeAttr.toRaw(this->encounterType); + attributes |= Metatile::layerTypeAttr.toRaw(this->layerType); return attributes; } -void Metatile::setAttributes(uint32_t data, BaseGameVersion version) { - if (version == BaseGameVersion::pokefirered) { - this->behavior = (data & behaviorMask_FRLG) >> behaviorShift_FRLG; - this->terrainType = (data & terrainTypeMask) >> terrainTypeShift; - this->encounterType = (data & encounterTypeMask) >> encounterTypeShift; - this->layerType = (data & layerTypeMask_FRLG) >> layerTypeShift_FRLG; - this->unusedAttributes = data & ~(behaviorMask_FRLG | terrainTypeMask | layerTypeMask_FRLG | encounterTypeMask); - } else { - this->behavior = (data & behaviorMask_RSE) >> behaviorShift_RSE; - this->layerType = (data & layerTypeMask_RSE) >> layerTypeShift_RSE; - this->unusedAttributes = data & ~(behaviorMask_RSE | layerTypeMask_RSE); - } +void Metatile::setAttributes(uint32_t data) { + this->behavior = Metatile::behaviorAttr.fromRaw(data); + this->terrainType = Metatile::terrainTypeAttr.fromRaw(data); + this->encounterType = Metatile::encounterTypeAttr.fromRaw(data); + this->layerType = Metatile::layerTypeAttr.fromRaw(data); + this->unusedAttributes = data & Metatile::unusedAttrMask; +} + +// Read attributes using a vanilla layout, then set them using the user's layout. For AdvanceMap import +void Metatile::setAttributes(uint32_t data, BaseGameVersion version) { + const auto defaultLayout = Metatile::defaultLayouts.value(version); + this->setBehavior(defaultLayout->value("behavior").fromRaw(data)); + this->setTerrainType(defaultLayout->value("terrainType").fromRaw(data)); + this->setEncounterType(defaultLayout->value("encounterType").fromRaw(data)); + this->setLayerType(defaultLayout->value("layerType").fromRaw(data)); +} + +int Metatile::getDefaultAttributesSize(BaseGameVersion version) { + return (version == BaseGameVersion::pokefirered) ? 4 : 2; +} +uint32_t Metatile::getBehaviorMask(BaseGameVersion version) { + return Metatile::defaultLayouts.value(version)->value("behavior").mask; +} +uint32_t Metatile::getTerrainTypeMask(BaseGameVersion version) { + return Metatile::defaultLayouts.value(version)->value("terrainType").mask; +} +uint32_t Metatile::getEncounterTypeMask(BaseGameVersion version) { + return Metatile::defaultLayouts.value(version)->value("encounterType").mask; +} +uint32_t Metatile::getLayerTypeMask(BaseGameVersion version) { + return Metatile::defaultLayouts.value(version)->value("layerType").mask; } diff --git a/src/core/metatileparser.cpp b/src/core/metatileparser.cpp index fc242e82..5b399328 100644 --- a/src/core/metatileparser.cpp +++ b/src/core/metatileparser.cpp @@ -42,7 +42,7 @@ QList MetatileParser::parse(QString filepath, bool *error, bool prima return { }; } - int attrSize = Metatile::getAttributesSize(version); + int attrSize = Metatile::getDefaultAttributesSize(version); int maxMetatiles = primaryTileset ? Project::getNumMetatilesPrimary() : Project::getNumMetatilesTotal() - Project::getNumMetatilesPrimary(); int numMetatiles = static_cast(in.at(0)) | (static_cast(in.at(1)) << 8) | diff --git a/src/core/parseutil.cpp b/src/core/parseutil.cpp index 1f933e7c..83954f8e 100644 --- a/src/core/parseutil.cpp +++ b/src/core/parseutil.cpp @@ -410,7 +410,7 @@ int ParseUtil::gameStringToInt(QString gameString, bool * ok) { return 1; if (QString::compare(gameString, "FALSE", Qt::CaseInsensitive) == 0) return 0; - return gameString.toInt(ok); + return gameString.toInt(ok, 0); } bool ParseUtil::gameStringToBool(QString gameString, bool * ok) { diff --git a/src/core/tileset.cpp b/src/core/tileset.cpp index 50623efd..49b3f1f1 100644 --- a/src/core/tileset.cpp +++ b/src/core/tileset.cpp @@ -240,8 +240,8 @@ bool Tileset::appendToMetatiles(QString root, QString friendlyName, bool usingAs } else { // Append to C file dataString.append(QString("const u16 gMetatiles_%1[] = INCBIN_U16(\"%2\");\n").arg(friendlyName, metatilesPath)); - QString attrSize = (projectConfig.getBaseGameVersion() == BaseGameVersion::pokefirered) ? "32" : "16"; - dataString.append(QString("const u%1 gMetatileAttributes_%2[] = INCBIN_U%1(\"%3\");\n").arg(attrSize, friendlyName, metatileAttrsPath)); + QString numBits = QString::number(projectConfig.getMetatileAttributesSize() * 8); + dataString.append(QString("const u%1 gMetatileAttributes_%2[] = INCBIN_U%1(\"%3\");\n").arg(numBits, friendlyName, metatileAttrsPath)); } file.write(dataString.toUtf8()); file.flush(); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index a36e0255..1543ac10 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -376,36 +376,13 @@ void MainWindow::setProjectSpecificUIVisibility() ui->actionUse_Poryscript->setChecked(projectConfig.getUsePoryScript()); this->setWildEncountersUIEnabled(userConfig.getEncounterJsonActive()); - switch (projectConfig.getBaseGameVersion()) - { - case BaseGameVersion::pokeruby: - ui->checkBox_AllowRunning->setVisible(false); - ui->checkBox_AllowBiking->setVisible(false); - ui->checkBox_AllowEscaping->setVisible(false); - ui->label_AllowRunning->setVisible(false); - ui->label_AllowBiking->setVisible(false); - ui->label_AllowEscaping->setVisible(false); - ui->actionRegion_Map_Editor->setVisible(true); - break; - case BaseGameVersion::pokeemerald: - ui->checkBox_AllowRunning->setVisible(true); - ui->checkBox_AllowBiking->setVisible(true); - ui->checkBox_AllowEscaping->setVisible(true); - ui->label_AllowRunning->setVisible(true); - ui->label_AllowBiking->setVisible(true); - ui->label_AllowEscaping->setVisible(true); - ui->actionRegion_Map_Editor->setVisible(true); - break; - case BaseGameVersion::pokefirered: - ui->checkBox_AllowRunning->setVisible(true); - ui->checkBox_AllowBiking->setVisible(true); - ui->checkBox_AllowEscaping->setVisible(true); - ui->label_AllowRunning->setVisible(true); - ui->label_AllowBiking->setVisible(true); - ui->label_AllowEscaping->setVisible(true); - ui->actionRegion_Map_Editor->setVisible(true); - break; - } + bool hasFlags = projectConfig.getMapAllowFlagsEnabled(); + ui->checkBox_AllowRunning->setVisible(hasFlags); + ui->checkBox_AllowBiking->setVisible(hasFlags); + ui->checkBox_AllowEscaping->setVisible(hasFlags); + ui->label_AllowRunning->setVisible(hasFlags); + ui->label_AllowBiking->setVisible(hasFlags); + ui->label_AllowEscaping->setVisible(hasFlags); ui->newEventToolButton->newWeatherTriggerAction->setVisible(projectConfig.getEventWeatherTriggerEnabled()); ui->newEventToolButton->newSecretBaseAction->setVisible(projectConfig.getEventSecretBaseEnabled()); @@ -807,11 +784,9 @@ void MainWindow::displayMapProperties() { ui->comboBox_Type->setCurrentText(map->type); ui->comboBox_BattleScene->setCurrentText(map->battle_scene); ui->checkBox_ShowLocation->setChecked(map->show_location); - if (projectConfig.getBaseGameVersion() != BaseGameVersion::pokeruby) { - ui->checkBox_AllowRunning->setChecked(map->allowRunning); - ui->checkBox_AllowBiking->setChecked(map->allowBiking); - ui->checkBox_AllowEscaping->setChecked(map->allowEscaping); - } + ui->checkBox_AllowRunning->setChecked(map->allowRunning); + ui->checkBox_AllowBiking->setChecked(map->allowBiking); + ui->checkBox_AllowEscaping->setChecked(map->allowEscaping); ui->spinBox_FloorNumber->setValue(map->floorNumber); // Custom fields table. @@ -940,6 +915,7 @@ bool MainWindow::loadDataStructures() { && project->readEventGraphics() && project->readSongNames(); + Metatile::setCustomLayout(project); Scripting::populateGlobalObject(this); return success && loadProjectCombos(); diff --git a/src/project.cpp b/src/project.cpp index 702d29ec..2856c40c 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -165,7 +165,7 @@ const QSet defaultTopLevelMapFields = { QSet Project::getTopLevelMapFields() { QSet topLevelMapFields = defaultTopLevelMapFields; - if (projectConfig.getBaseGameVersion() != BaseGameVersion::pokeruby) { + if (projectConfig.getMapAllowFlagsEnabled()) { topLevelMapFields.insert("allow_cycling"); topLevelMapFields.insert("allow_escaping"); topLevelMapFields.insert("allow_running"); @@ -200,7 +200,7 @@ bool Project::loadMapData(Map* map) { map->show_location = ParseUtil::jsonToBool(mapObj["show_map_name"]); map->battle_scene = ParseUtil::jsonToQString(mapObj["battle_scene"]); - if (projectConfig.getBaseGameVersion() != BaseGameVersion::pokeruby) { + if (projectConfig.getMapAllowFlagsEnabled()) { map->allowBiking = ParseUtil::jsonToBool(mapObj["allow_cycling"]); map->allowEscaping = ParseUtil::jsonToBool(mapObj["allow_escaping"]); map->allowRunning = ParseUtil::jsonToBool(mapObj["allow_running"]); @@ -375,26 +375,6 @@ QString Project::readMapLocation(QString map_name) { return ParseUtil::jsonToQString(mapObj["region_map_section"]); } -void Project::setNewMapHeader(Map* map, int mapIndex) { - map->layoutId = QString("%1").arg(mapIndex); - map->location = mapSectionValueToName.value(0); - map->requiresFlash = false; - map->weather = weatherNames.value(0, "WEATHER_NONE"); - map->type = mapTypes.value(0, "MAP_TYPE_NONE"); - map->song = defaultSong; - map->show_location = true; - if (projectConfig.getBaseGameVersion() != BaseGameVersion::pokeruby) { - map->allowBiking = true; - map->allowEscaping = false; - map->allowRunning = true; - } - if (projectConfig.getFloorNumberEnabled()) { - map->floorNumber = 0; - } - - map->battle_scene = mapBattleScenes.value(0, "MAP_BATTLE_SCENE_NORMAL"); -} - bool Project::loadLayout(MapLayout *layout) { // Force these to run even if one fails bool loadedTilesets = loadLayoutTilesets(layout); @@ -981,10 +961,9 @@ void Project::saveTilesetMetatileAttributes(Tileset *tileset) { QFile attrs_file(tileset->metatile_attrs_path); if (attrs_file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { QByteArray data; - BaseGameVersion version = projectConfig.getBaseGameVersion(); - int attrSize = Metatile::getAttributesSize(version); + int attrSize = projectConfig.getMetatileAttributesSize(); for (Metatile *metatile : tileset->metatiles) { - uint32_t attributes = metatile->getAttributes(version); + uint32_t attributes = metatile->getAttributes(); for (int i = 0; i < attrSize; i++) data.append(static_cast(attributes >> (8 * i))); } @@ -1265,7 +1244,7 @@ void Project::saveMap(Map *map) { mapObj["requires_flash"] = map->requiresFlash; mapObj["weather"] = map->weather; mapObj["map_type"] = map->type; - if (projectConfig.getBaseGameVersion() != BaseGameVersion::pokeruby) { + if (projectConfig.getMapAllowFlagsEnabled()) { mapObj["allow_cycling"] = map->allowBiking; mapObj["allow_escaping"] = map->allowEscaping; mapObj["allow_running"] = map->allowRunning; @@ -1539,9 +1518,7 @@ void Project::loadTilesetMetatiles(Tileset* tileset) { if (attrs_file.open(QIODevice::ReadOnly)) { QByteArray data = attrs_file.readAll(); int num_metatiles = tileset->metatiles.count(); - - BaseGameVersion version = projectConfig.getBaseGameVersion(); - int attrSize = Metatile::getAttributesSize(version); + int attrSize = projectConfig.getMetatileAttributesSize(); int num_metatileAttrs = data.length() / attrSize; if (num_metatiles != num_metatileAttrs) { logWarn(QString("Metatile count %1 does not match metatile attribute count %2 in %3").arg(num_metatiles).arg(num_metatileAttrs).arg(tileset->name)); @@ -1553,7 +1530,7 @@ void Project::loadTilesetMetatiles(Tileset* tileset) { uint32_t attributes = 0; for (int j = 0; j < attrSize; j++) attributes |= static_cast(data.at(i * attrSize + j)) << (8 * j); - tileset->metatiles.at(i)->setAttributes(attributes, version); + tileset->metatiles.at(i)->setAttributes(attributes); } } else { logError(QString("Could not open tileset metatile attributes file '%1'").arg(tileset->metatile_attrs_path)); @@ -2079,8 +2056,8 @@ bool Project::readHealLocations() { HealLocation healLocation; if (match.hasMatch()) { QString mapName = match.captured("map"); - int x = match.captured("x").toInt(); - int y = match.captured("y").toInt(); + int x = match.captured("x").toInt(nullptr, 0); + int y = match.captured("y").toInt(nullptr, 0); healLocation = HealLocation(idName, mapName, this->healLocations.size() + 1, x, y); } else { // This heal location has data, but is missing from the location table and won't be displayed by Porymap. @@ -2100,7 +2077,7 @@ bool Project::readHealLocations() { QRegularExpression respawnNPCRegex(QString("%1(?[0-9]+)").arg(initializerPattern)); match = respawnNPCRegex.match(text); if (match.hasMatch()) - healLocation.respawnNPC = match.captured("npc").toInt(); + healLocation.respawnNPC = match.captured("npc").toInt(nullptr, 0); } this->healLocations.append(healLocation); @@ -2483,11 +2460,11 @@ bool Project::readEventGraphics() { QRegularExpressionMatch dimensionMatch = re.match(dimensions_label); QRegularExpressionMatch oamTablesMatch = re.match(subsprites_label); if (oamTablesMatch.hasMatch()) { - eventGraphics->spriteWidth = oamTablesMatch.captured(1).toInt(); - eventGraphics->spriteHeight = oamTablesMatch.captured(2).toInt(); + eventGraphics->spriteWidth = oamTablesMatch.captured(1).toInt(nullptr, 0); + eventGraphics->spriteHeight = oamTablesMatch.captured(2).toInt(nullptr, 0); } else if (dimensionMatch.hasMatch()) { - eventGraphics->spriteWidth = dimensionMatch.captured(1).toInt(); - eventGraphics->spriteHeight = dimensionMatch.captured(2).toInt(); + eventGraphics->spriteWidth = dimensionMatch.captured(1).toInt(nullptr, 0); + eventGraphics->spriteHeight = dimensionMatch.captured(2).toInt(nullptr, 0); } else { eventGraphics->spriteWidth = eventGraphics->spritesheet.width(); eventGraphics->spriteHeight = eventGraphics->spritesheet.height(); diff --git a/src/scriptapi/apimap.cpp b/src/scriptapi/apimap.cpp index b133a74b..0adbded8 100644 --- a/src/scriptapi/apimap.cpp +++ b/src/scriptapi/apimap.cpp @@ -635,10 +635,9 @@ int MainWindow::getMetatileLayerType(int metatileId) { void MainWindow::setMetatileLayerType(int metatileId, int layerType) { Metatile * metatile = this->getMetatile(metatileId); - uint8_t u_layerType = static_cast(layerType); - if (!metatile || metatile->layerType == u_layerType || u_layerType >= NUM_METATILE_LAYER_TYPES) + if (!metatile) return; - metatile->layerType = u_layerType; + metatile->setLayerType(layerType); this->saveMetatileAttributesByMetatileId(metatileId); } @@ -651,10 +650,9 @@ int MainWindow::getMetatileEncounterType(int metatileId) { void MainWindow::setMetatileEncounterType(int metatileId, int encounterType) { Metatile * metatile = this->getMetatile(metatileId); - uint8_t u_encounterType = static_cast(encounterType); - if (!metatile || metatile->encounterType == u_encounterType || u_encounterType >= NUM_METATILE_ENCOUNTER_TYPES) + if (!metatile) return; - metatile->encounterType = u_encounterType; + metatile->setEncounterType(encounterType); this->saveMetatileAttributesByMetatileId(metatileId); } @@ -667,10 +665,9 @@ int MainWindow::getMetatileTerrainType(int metatileId) { void MainWindow::setMetatileTerrainType(int metatileId, int terrainType) { Metatile * metatile = this->getMetatile(metatileId); - uint8_t u_terrainType = static_cast(terrainType); - if (!metatile || metatile->terrainType == u_terrainType || u_terrainType >= NUM_METATILE_TERRAIN_TYPES) + if (!metatile) return; - metatile->terrainType = u_terrainType; + metatile->setTerrainType(terrainType); this->saveMetatileAttributesByMetatileId(metatileId); } @@ -683,10 +680,9 @@ int MainWindow::getMetatileBehavior(int metatileId) { void MainWindow::setMetatileBehavior(int metatileId, int behavior) { Metatile * metatile = this->getMetatile(metatileId); - uint16_t u_behavior = static_cast(behavior); - if (!metatile || metatile->behavior == u_behavior) + if (!metatile) return; - metatile->behavior = u_behavior; + metatile->setBehavior(behavior); this->saveMetatileAttributesByMetatileId(metatileId); } @@ -694,15 +690,14 @@ int MainWindow::getMetatileAttributes(int metatileId) { Metatile * metatile = this->getMetatile(metatileId); if (!metatile) return -1; - return metatile->getAttributes(projectConfig.getBaseGameVersion()); + return metatile->getAttributes(); } void MainWindow::setMetatileAttributes(int metatileId, int attributes) { Metatile * metatile = this->getMetatile(metatileId); - uint32_t u_attributes = static_cast(attributes); if (!metatile) return; - metatile->setAttributes(u_attributes, projectConfig.getBaseGameVersion()); + metatile->setAttributes(attributes); this->saveMetatileAttributesByMetatileId(metatileId); } diff --git a/src/ui/newmappopup.cpp b/src/ui/newmappopup.cpp index 3c2545b2..85f74235 100644 --- a/src/ui/newmappopup.cpp +++ b/src/ui/newmappopup.cpp @@ -48,7 +48,7 @@ void NewMapPopup::init() { ui->spinBox_NewMap_Floor_Number->setMaximum(127); // Hide config specific ui elements - bool hasFlags = (projectConfig.getBaseGameVersion() != BaseGameVersion::pokeruby); + bool hasFlags = projectConfig.getMapAllowFlagsEnabled(); ui->checkBox_NewMap_Allow_Running->setVisible(hasFlags); ui->checkBox_NewMap_Allow_Biking->setVisible(hasFlags); ui->checkBox_NewMap_Allow_Escape_Rope->setVisible(hasFlags); @@ -293,7 +293,7 @@ void NewMapPopup::on_pushButton_NewMap_Accept_clicked() { newMap->needsHealLocation = true; } - if (projectConfig.getBaseGameVersion() != BaseGameVersion::pokeruby) { + if (projectConfig.getMapAllowFlagsEnabled()) { newMap->allowRunning = this->ui->checkBox_NewMap_Allow_Running->isChecked(); newMap->allowBiking = this->ui->checkBox_NewMap_Allow_Biking->isChecked(); newMap->allowEscaping = this->ui->checkBox_NewMap_Allow_Escape_Rope->isChecked(); diff --git a/src/ui/tileseteditor.cpp b/src/ui/tileseteditor.cpp index cf6203cb..6dcdea0e 100644 --- a/src/ui/tileseteditor.cpp +++ b/src/ui/tileseteditor.cpp @@ -99,9 +99,7 @@ void TilesetEditor::initUi() { this->ui->spinBox_paletteSelector->setMinimum(0); this->ui->spinBox_paletteSelector->setMaximum(Project::getNumPalettesTotal() - 1); - this->setMetatileBehaviors(); - this->setMetatileLayersUi(); - this->setVersionSpecificUi(); + this->setAttributesUi(); this->setMetatileLabelValidator(); this->initMetatileSelector(); @@ -113,43 +111,55 @@ void TilesetEditor::initUi() { this->restoreWindowState(); } -void TilesetEditor::setMetatileBehaviors() { - for (int num : project->metatileBehaviorMapInverse.keys()) { - this->ui->comboBox_metatileBehaviors->addItem(project->metatileBehaviorMapInverse[num], num); - } -} - -void TilesetEditor::setMetatileLayersUi() { - if (!projectConfig.getTripleLayerMetatilesEnabled()) { - this->ui->comboBox_layerType->addItem("Normal - Middle/Top", METATILE_LAYER_MIDDLE_TOP); - this->ui->comboBox_layerType->addItem("Covered - Bottom/Middle", METATILE_LAYER_BOTTOM_MIDDLE); - this->ui->comboBox_layerType->addItem("Split - Bottom/Top", METATILE_LAYER_BOTTOM_TOP); +void TilesetEditor::setAttributesUi() { + // Behavior + if (Metatile::getBehaviorMask()) { + for (int num : project->metatileBehaviorMapInverse.keys()) { + this->ui->comboBox_metatileBehaviors->addItem(project->metatileBehaviorMapInverse[num], num); + } } else { - this->ui->comboBox_layerType->setVisible(false); - this->ui->label_layerType->setVisible(false); - this->ui->label_BottomTop->setText("Bottom/Middle/Top"); + this->ui->comboBox_metatileBehaviors->setVisible(false); + this->ui->label_metatileBehavior->setVisible(false); } -} -void TilesetEditor::setVersionSpecificUi() { - if (projectConfig.getBaseGameVersion() == BaseGameVersion::pokefirered) { - this->ui->comboBox_encounterType->setVisible(true); - this->ui->label_encounterType->setVisible(true); - this->ui->comboBox_encounterType->addItem("None", ENCOUNTER_NONE); - this->ui->comboBox_encounterType->addItem("Land", ENCOUNTER_LAND); - this->ui->comboBox_encounterType->addItem("Water", ENCOUNTER_WATER); - this->ui->comboBox_terrainType->setVisible(true); - this->ui->label_terrainType->setVisible(true); + // Terrain Type + if (Metatile::getTerrainTypeMask()) { this->ui->comboBox_terrainType->addItem("Normal", TERRAIN_NONE); this->ui->comboBox_terrainType->addItem("Grass", TERRAIN_GRASS); this->ui->comboBox_terrainType->addItem("Water", TERRAIN_WATER); this->ui->comboBox_terrainType->addItem("Waterfall", TERRAIN_WATERFALL); } else { - this->ui->comboBox_encounterType->setVisible(false); - this->ui->label_encounterType->setVisible(false); this->ui->comboBox_terrainType->setVisible(false); this->ui->label_terrainType->setVisible(false); } + + // Encounter Type + if (Metatile::getEncounterTypeMask()) { + this->ui->comboBox_encounterType->addItem("None", ENCOUNTER_NONE); + this->ui->comboBox_encounterType->addItem("Land", ENCOUNTER_LAND); + this->ui->comboBox_encounterType->addItem("Water", ENCOUNTER_WATER); + } else { + this->ui->comboBox_encounterType->setVisible(false); + this->ui->label_encounterType->setVisible(false); + } + + // Layer Type + if (!projectConfig.getTripleLayerMetatilesEnabled()) { + this->ui->comboBox_layerType->addItem("Normal - Middle/Top", METATILE_LAYER_MIDDLE_TOP); + this->ui->comboBox_layerType->addItem("Covered - Bottom/Middle", METATILE_LAYER_BOTTOM_MIDDLE); + this->ui->comboBox_layerType->addItem("Split - Bottom/Top", METATILE_LAYER_BOTTOM_TOP); + if (!Metatile::getLayerTypeMask()) { + // User doesn't have triple layer metatiles, but has no layer type attribute. + // Porymap is still using the layer type value to render metatiles, and with + // no mask set every metatile will be "Middle/Top", so just display the combo + // box but prevent the user from changing the value. + this->ui->comboBox_layerType->setEnabled(false); + } + } else { + this->ui->comboBox_layerType->setVisible(false); + this->ui->label_layerType->setVisible(false); + this->ui->label_BottomTop->setText("Bottom/Middle/Top"); + } } void TilesetEditor::setMetatileLabelValidator() { @@ -373,13 +383,9 @@ void TilesetEditor::onSelectedMetatileChanged(uint16_t metatileId) { this->ui->graphicsView_metatileLayers->setFixedSize(this->metatileLayersItem->pixmap().width() + 2, this->metatileLayersItem->pixmap().height() + 2); this->ui->lineEdit_metatileLabel->setText(this->metatile->label); setComboValue(this->ui->comboBox_metatileBehaviors, this->metatile->behavior); - if (!projectConfig.getTripleLayerMetatilesEnabled()) { - setComboValue(this->ui->comboBox_layerType, this->metatile->layerType); - } - if (projectConfig.getBaseGameVersion() == BaseGameVersion::pokefirered) { - setComboValue(this->ui->comboBox_encounterType, this->metatile->encounterType); - setComboValue(this->ui->comboBox_terrainType, this->metatile->terrainType); - } + setComboValue(this->ui->comboBox_layerType, this->metatile->layerType); + setComboValue(this->ui->comboBox_encounterType, this->metatile->encounterType); + setComboValue(this->ui->comboBox_terrainType, this->metatile->terrainType); } void TilesetEditor::onHoveredTileChanged(uint16_t tile) { @@ -512,11 +518,11 @@ void TilesetEditor::on_comboBox_metatileBehaviors_currentTextChanged(const QStri // This function can also be called when the user selects // a different metatile. Stop this from being considered a change. - if (this->metatile->behavior == static_cast(behavior)) + if (this->metatile->behavior == static_cast(behavior)) return; Metatile *prevMetatile = new Metatile(*this->metatile); - this->metatile->behavior = behavior; + this->metatile->setBehavior(behavior); MetatileHistoryItem *commit = new MetatileHistoryItem(this->getSelectedMetatileId(), prevMetatile, new Metatile(*this->metatile)); metatileHistory.push(commit); @@ -552,7 +558,7 @@ void TilesetEditor::on_comboBox_layerType_activated(int layerType) { if (this->metatile) { Metatile *prevMetatile = new Metatile(*this->metatile); - this->metatile->layerType = static_cast(layerType); + this->metatile->setLayerType(layerType); MetatileHistoryItem *commit = new MetatileHistoryItem(this->getSelectedMetatileId(), prevMetatile, new Metatile(*this->metatile)); metatileHistory.push(commit); @@ -565,7 +571,7 @@ void TilesetEditor::on_comboBox_encounterType_activated(int encounterType) { if (this->metatile) { Metatile *prevMetatile = new Metatile(*this->metatile); - this->metatile->encounterType = static_cast(encounterType); + this->metatile->setEncounterType(encounterType); MetatileHistoryItem *commit = new MetatileHistoryItem(this->getSelectedMetatileId(), prevMetatile, new Metatile(*this->metatile)); metatileHistory.push(commit); @@ -577,7 +583,7 @@ void TilesetEditor::on_comboBox_terrainType_activated(int terrainType) { if (this->metatile) { Metatile *prevMetatile = new Metatile(*this->metatile); - this->metatile->terrainType = static_cast(terrainType); + this->metatile->setTerrainType(terrainType); MetatileHistoryItem *commit = new MetatileHistoryItem(this->getSelectedMetatileId(), prevMetatile, new Metatile(*this->metatile)); metatileHistory.push(commit);