Merge pull request #655 from GriffinRichards/fix-transparency

Add setting for how to render fully transparent pixels
This commit is contained in:
GriffinR 2024-12-28 01:34:26 -05:00 committed by GitHub
commit 7a36fcc2ba
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 207 additions and 56 deletions

View file

@ -369,7 +369,7 @@
<x>0</x>
<y>0</y>
<width>559</width>
<height>548</height>
<height>560</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_16">
@ -602,7 +602,7 @@
</widget>
</item>
<item row="0" column="1">
<widget class="UIntHexSpinBox" name="spinBox_MetatileIdMask" native="true">
<widget class="UIntHexSpinBox" name="spinBox_MetatileIdMask">
<property name="toolTip">
<string>The mask used to read/write metatile IDs in map data.</string>
</property>
@ -616,7 +616,7 @@
</widget>
</item>
<item row="1" column="1">
<widget class="UIntHexSpinBox" name="spinBox_CollisionMask" native="true">
<widget class="UIntHexSpinBox" name="spinBox_CollisionMask">
<property name="toolTip">
<string>The mask used to read/write collision values in map data.</string>
</property>
@ -630,7 +630,7 @@
</widget>
</item>
<item row="2" column="1">
<widget class="UIntHexSpinBox" name="spinBox_ElevationMask" native="true">
<widget class="UIntHexSpinBox" name="spinBox_ElevationMask">
<property name="toolTip">
<string>The mask used to read/write elevation values in map data.</string>
</property>
@ -742,7 +742,7 @@
<x>0</x>
<y>0</y>
<width>559</width>
<height>568</height>
<height>798</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_12">
@ -775,6 +775,86 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_TransparentPixelRendering">
<property name="title">
<string>Transparent Pixel Rendering</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_22">
<item>
<widget class="QRadioButton" name="radioButton_RenderBlack">
<property name="toolTip">
<string>Fully transparent pixels will be rendered as black pixels (the Pokémon games do this by default)</string>
</property>
<property name="text">
<string>Render as black</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioButton_RenderFirstPalColor">
<property name="toolTip">
<string>Fully transparent pixels will be rendered using the first palette color (this the default behavior for the GBA)</string>
</property>
<property name="text">
<string>Render using first palette color</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_UnusedLayerRendering">
<property name="title">
<string>Unused Layer Rendering</string>
</property>
<layout class="QFormLayout" name="formLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_UnusedTileNorma">
<property name="text">
<string>Normal</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="UIntHexSpinBox" name="spinBox_UnusedTileNormal">
<property name="toolTip">
<string>This raw tile value will be used to fill the unused bottom layer of Normal metatiles</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_UnusedTileCovered">
<property name="text">
<string>Covered</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="UIntHexSpinBox" name="spinBox_UnusedTileCovered">
<property name="toolTip">
<string>This raw tile value will be used to fill the unused top layer of Covered metatiles</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_UnusedTileSplit">
<property name="text">
<string>Split</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="UIntHexSpinBox" name="spinBox_UnusedTileSplit">
<property name="toolTip">
<string>This raw tile value will be used to fill the unused middle layer of Split metatiles</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_WarningTilesetsTab">
<property name="styleSheet">
@ -810,14 +890,14 @@
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="4" column="1">
<widget class="UIntHexSpinBox" name="spinBox_LayerTypeMask" native="true">
<widget class="UIntHexSpinBox" name="spinBox_LayerTypeMask">
<property name="toolTip">
<string>The mask used to read/write Layer Type from the metatile's attributes data. If 0, this attribute is disabled.</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="UIntHexSpinBox" name="spinBox_BehaviorMask" native="true">
<widget class="UIntHexSpinBox" name="spinBox_BehaviorMask">
<property name="toolTip">
<string>The mask used to read/write Metatile Behavior from the metatile's attributes data. If 0, this attribute is disabled.</string>
</property>
@ -864,7 +944,7 @@
</widget>
</item>
<item row="7" column="1">
<widget class="UIntHexSpinBox" name="spinBox_TerrainTypeMask" native="true">
<widget class="UIntHexSpinBox" name="spinBox_TerrainTypeMask">
<property name="toolTip">
<string>The mask used to read/write Terrain Type from the metatile's attributes data. If 0, this attribute is disabled.</string>
</property>
@ -891,7 +971,7 @@
</widget>
</item>
<item row="5" column="1">
<widget class="UIntHexSpinBox" name="spinBox_EncounterTypeMask" native="true">
<widget class="UIntHexSpinBox" name="spinBox_EncounterTypeMask">
<property name="toolTip">
<string>The mask used to read/write Encounter Type from the metatile's attributes data. If 0, this attribute is disabled.</string>
</property>
@ -1549,10 +1629,15 @@
<header>noscrollspinbox.h</header>
</customwidget>
<customwidget>
<class>UIntHexSpinBox</class>
<extends>QWidget</extends>
<class>UIntSpinBox</class>
<extends>QAbstractSpinBox</extends>
<header>uintspinbox.h</header>
</customwidget>
<customwidget>
<class>UIntHexSpinBox</class>
<extends>UIntSpinBox</extends>
<header location="global">uintspinbox.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="../resources/images.qrc"/>

View file

@ -301,6 +301,7 @@ public:
this->prefabImportPrompted = false;
this->tilesetsHaveCallback = true;
this->tilesetsHaveIsCompressed = true;
this->setTransparentPixelsBlack = true;
this->filePaths.clear();
this->eventIconPaths.clear();
this->pokemonIconPaths.clear();
@ -310,6 +311,9 @@ public:
this->blockMetatileIdMask = 0x03FF;
this->blockCollisionMask = 0x0C00;
this->blockElevationMask = 0xF000;
this->unusedTileNormal = 0x3014;
this->unusedTileCovered = 0x0000;
this->unusedTileSplit = 0x0000;
this->identifiers.clear();
this->readKeys.clear();
}
@ -362,6 +366,7 @@ public:
bool prefabImportPrompted;
bool tilesetsHaveCallback;
bool tilesetsHaveIsCompressed;
bool setTransparentPixelsBlack;
int metatileAttributesSize;
uint32_t metatileBehaviorMask;
uint32_t metatileTerrainTypeMask;
@ -370,6 +375,9 @@ public:
uint16_t blockMetatileIdMask;
uint16_t blockCollisionMask;
uint16_t blockElevationMask;
uint16_t unusedTileNormal;
uint16_t unusedTileCovered;
uint16_t unusedTileSplit;
bool mapAllowFlagsEnabled;
QString collisionSheetPath;
int collisionSheetWidth;

View file

@ -19,6 +19,8 @@ public:
uint16_t rawValue() const;
static int getIndexInTileset(int);
static const uint16_t maxValue;
};
inline bool operator==(const Tile &a, const Tile &b) {

View file

@ -8,8 +8,8 @@
QImage getCollisionMetatileImage(Block);
QImage getCollisionMetatileImage(int, int);
QImage getMetatileImage(uint16_t, Tileset*, Tileset*, QList<int>, QList<float>, bool useTruePalettes = false);
QImage getMetatileImage(Metatile*, Tileset*, Tileset*, QList<int>, QList<float>, bool useTruePalettes = false);
QImage getMetatileImage(uint16_t, Tileset*, Tileset*, const QList<int>&, const QList<float>&, bool useTruePalettes = false);
QImage getMetatileImage(Metatile*, Tileset*, Tileset*, const QList<int>&, const QList<float>&, bool useTruePalettes = false);
QImage getTileImage(uint16_t, Tileset*, Tileset*);
QImage getPalettedTileImage(uint16_t, Tileset*, Tileset*, int, bool useTruePalettes = false);
QImage getGreyscaleTileImage(uint16_t tile, Tileset *primaryTileset, Tileset *secondaryTileset);

View file

@ -720,6 +720,12 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) {
this->blockCollisionMask = getConfigUint32(key, value, 0, Block::maxValue);
} else if (key == "block_elevation_mask") {
this->blockElevationMask = getConfigUint32(key, value, 0, Block::maxValue);
} else if (key == "unused_tile_normal") {
this->unusedTileNormal = getConfigUint32(key, value, 0, Tile::maxValue);
} else if (key == "unused_tile_covered") {
this->unusedTileCovered = getConfigUint32(key, value, 0, Tile::maxValue);
} else if (key == "unused_tile_split") {
this->unusedTileSplit = getConfigUint32(key, value, 0, Tile::maxValue);
} else if (key == "enable_map_allow_flags") {
this->mapAllowFlagsEnabled = getConfigBool(key, value);
#ifdef CONFIG_BACKWARDS_COMPATABILITY
@ -752,6 +758,8 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) {
this->tilesetsHaveCallback = getConfigBool(key, value);
} else if (key == "tilesets_have_is_compressed") {
this->tilesetsHaveIsCompressed = getConfigBool(key, value);
} else if (key == "set_transparent_pixels_black") {
this->setTransparentPixelsBlack = getConfigBool(key, value);
} else if (key == "event_icon_path_object") {
this->eventIconPaths[Event::Group::Object] = value;
} else if (key == "event_icon_path_warp") {
@ -839,6 +847,7 @@ QMap<QString, QString> ProjectConfig::getKeyValueMap() {
}
map.insert("tilesets_have_callback", QString::number(this->tilesetsHaveCallback));
map.insert("tilesets_have_is_compressed", QString::number(this->tilesetsHaveIsCompressed));
map.insert("set_transparent_pixels_black", QString::number(this->setTransparentPixelsBlack));
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());
@ -847,6 +856,9 @@ QMap<QString, QString> ProjectConfig::getKeyValueMap() {
map.insert("block_metatile_id_mask", "0x" + QString::number(this->blockMetatileIdMask, 16).toUpper());
map.insert("block_collision_mask", "0x" + QString::number(this->blockCollisionMask, 16).toUpper());
map.insert("block_elevation_mask", "0x" + QString::number(this->blockElevationMask, 16).toUpper());
map.insert("unused_tile_normal", "0x" + QString::number(this->unusedTileNormal, 16).toUpper());
map.insert("unused_tile_covered", "0x" + QString::number(this->unusedTileCovered, 16).toUpper());
map.insert("unused_tile_split", "0x" + QString::number(this->unusedTileSplit, 16).toUpper());
map.insert("enable_map_allow_flags", QString::number(this->mapAllowFlagsEnabled));
map.insert("event_icon_path_object", this->eventIconPaths[Event::Group::Object]);
map.insert("event_icon_path_warp", this->eventIconPaths[Event::Group::Warp]);

View file

@ -1,5 +1,17 @@
#include "tile.h"
#include "project.h"
#include "bitpacker.h"
// 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.
const BitPacker bitsTileId = BitPacker(0x03FF);
const BitPacker bitsXFlip = BitPacker(0x0400);
const BitPacker bitsYFlip = BitPacker(0x0800);
const BitPacker bitsPalette = BitPacker(0xF000);
Tile::Tile() :
tileId(0),
@ -16,18 +28,17 @@
{ }
Tile::Tile(uint16_t raw) :
tileId(raw & 0x3FF),
xflip((raw >> 10) & 1),
yflip((raw >> 11) & 1),
palette((raw >> 12) & 0xF)
tileId(bitsTileId.unpack(raw)),
xflip(bitsXFlip.unpack(raw)),
yflip(bitsYFlip.unpack(raw)),
palette(bitsPalette.unpack(raw))
{ }
uint16_t Tile::rawValue() const {
return static_cast<uint16_t>(
(this->tileId & 0x3FF)
| ((this->xflip & 1) << 10)
| ((this->yflip & 1) << 11)
| ((this->palette & 0xF) << 12));
return bitsTileId.pack(this->tileId)
| bitsXFlip.pack(this->xflip)
| bitsYFlip.pack(this->yflip)
| bitsPalette.pack(this->palette);
}
int Tile::getIndexInTileset(int tileId) {

View file

@ -2096,19 +2096,29 @@ bool Project::readFieldmapProperties() {
fileWatcher.addPath(root + "/" + filename);
const QMap<QString, int> defines = parser.readCDefinesByName(filename, names);
auto loadDefine = [defines](const QString name, int * dest) {
auto loadDefine = [defines](const QString name, int * dest, int min, int max) {
auto it = defines.find(name);
if (it != defines.end()) {
*dest = it.value();
if (*dest < min) {
logWarn(QString("Value for tileset property '%1' (%2) is below the minimum (%3). Defaulting to minimum.").arg(name).arg(*dest).arg(min));
*dest = min;
} else if (*dest > max) {
logWarn(QString("Value for tileset property '%1' (%2) is above the maximum (%3). Defaulting to maximum.").arg(name).arg(*dest).arg(max));
*dest = max;
}
} else {
logWarn(QString("Value for tileset property '%1' not found. Using default (%2) instead.").arg(name).arg(*dest));
}
};
loadDefine(numTilesPrimaryName, &Project::num_tiles_primary);
loadDefine(numTilesTotalName, &Project::num_tiles_total);
loadDefine(numMetatilesPrimaryName, &Project::num_metatiles_primary);
loadDefine(numPalsPrimaryName, &Project::num_pals_primary);
loadDefine(numPalsTotalName, &Project::num_pals_total);
loadDefine(numPalsTotalName, &Project::num_pals_total, 2, INT_MAX); // In reality the max would be 16, but as far as Porymap is concerned it doesn't matter.
loadDefine(numTilesTotalName, &Project::num_tiles_total, 2, 1024); // 1024 is fixed because we store tile IDs in a 10-bit field.
loadDefine(numPalsPrimaryName, &Project::num_pals_primary, 1, Project::num_pals_total - 1);
loadDefine(numTilesPrimaryName, &Project::num_tiles_primary, 1, Project::num_tiles_total - 1);
// This maximum is overly generous, because until we parse the appropriate masks from the project
// we don't actually know what the maximum number of metatiles is.
loadDefine(numMetatilesPrimaryName, &Project::num_metatiles_primary, 1, 0xFFFF - 1);
auto it = defines.find(maxMapSizeName);
if (it != defines.end()) {
@ -3020,12 +3030,12 @@ void Project::applyParsedLimits() {
Block::setLayout();
Metatile::setLayout(this);
Project::num_metatiles_primary = qMin(Project::num_metatiles_primary, Block::getMaxMetatileId() + 1);
Project::num_metatiles_primary = qMin(qMax(Project::num_metatiles_primary, 1), Block::getMaxMetatileId() + 1);
projectConfig.defaultMetatileId = qMin(projectConfig.defaultMetatileId, Block::getMaxMetatileId());
projectConfig.defaultElevation = qMin(projectConfig.defaultElevation, Block::getMaxElevation());
projectConfig.defaultCollision = qMin(projectConfig.defaultCollision, Block::getMaxCollision());
projectConfig.collisionSheetHeight = qMin(projectConfig.collisionSheetHeight, Block::getMaxElevation() + 1);
projectConfig.collisionSheetWidth = qMin(projectConfig.collisionSheetWidth, Block::getMaxCollision() + 1);
projectConfig.collisionSheetHeight = qMin(qMax(projectConfig.collisionSheetHeight, 1), Block::getMaxElevation() + 1);
projectConfig.collisionSheetWidth = qMin(qMax(projectConfig.collisionSheetWidth, 1), Block::getMaxCollision() + 1);
}
bool Project::hasUnsavedChanges() {

View file

@ -17,8 +17,8 @@ QImage getMetatileImage(
uint16_t metatileId,
Tileset *primaryTileset,
Tileset *secondaryTileset,
QList<int> layerOrder,
QList<float> layerOpacity,
const QList<int> &layerOrder,
const QList<float> &layerOpacity,
bool useTruePalettes)
{
Metatile* metatile = Tileset::getMetatile(metatileId, primaryTileset, secondaryTileset);
@ -34,8 +34,8 @@ QImage getMetatileImage(
Metatile *metatile,
Tileset *primaryTileset,
Tileset *secondaryTileset,
QList<int> layerOrder,
QList<float> layerOpacity,
const QList<int> &layerOrder,
const QList<float> &layerOpacity,
bool useTruePalettes)
{
QImage metatile_image(16, 16, QImage::Format_RGBA8888);
@ -43,10 +43,16 @@ QImage getMetatileImage(
metatile_image.fill(Qt::magenta);
return metatile_image;
}
metatile_image.fill(Qt::black);
QList<QList<QRgb>> palettes = Tileset::getBlockPalettes(primaryTileset, secondaryTileset, useTruePalettes);
// We need to fill the metatile image with something so that if any transparent
// tile pixels line up across layers we will still have something to render.
// The GBA renders transparent pixels using palette 0 color 0. We have this color,
// but all 3 games actually overwrite it with black when loading the tileset palettes,
// so we have a setting to choose between these two behaviors.
metatile_image.fill(projectConfig.setTransparentPixelsBlack ? QColor("black") : QColor(palettes.value(0).value(0)));
QPainter metatile_painter(&metatile_image);
const int numLayers = 3; // When rendering, metatiles always have 3 layers
uint32_t layerType = metatile->layerType();
@ -54,7 +60,6 @@ QImage getMetatileImage(
for (int y = 0; y < 2; y++)
for (int x = 0; x < 2; x++) {
int l = layerOrder.size() >= numLayers ? layerOrder[layer] : layer;
int bottomLayer = layerOrder.size() >= numLayers ? layerOrder[0] : 0;
// Get the tile to render next
Tile tile;
@ -63,25 +68,25 @@ QImage getMetatileImage(
tile = metatile->tiles.value(tileOffset + (l * 4));
} else {
// "Vanilla" metatiles only have 8 tiles, but render 12.
// The remaining 4 tiles are rendered either as tile 0 or 0x3014 (tile 20, palette 3) depending on layer type.
// The remaining 4 tiles are rendered using user-specified tiles depending on layer type.
switch (layerType)
{
default:
case METATILE_LAYER_MIDDLE_TOP:
if (l == 0)
tile = Tile(0x3014);
tile = Tile(projectConfig.unusedTileNormal);
else // Tiles are on layers 1 and 2
tile = metatile->tiles.value(tileOffset + ((l - 1) * 4));
break;
case METATILE_LAYER_BOTTOM_MIDDLE:
if (l == 2)
tile = Tile();
tile = Tile(projectConfig.unusedTileCovered);
else // Tiles are on layers 0 and 1
tile = metatile->tiles.value(tileOffset + (l * 4));
break;
case METATILE_LAYER_BOTTOM_TOP:
if (l == 1)
tile = Tile();
tile = Tile(projectConfig.unusedTileSplit);
else // Tiles are on layers 0 and 2
tile = metatile->tiles.value(tileOffset + ((l == 0 ? 0 : 1) * 4));
break;
@ -91,18 +96,14 @@ QImage getMetatileImage(
QImage tile_image = getTileImage(tile.tileId, primaryTileset, secondaryTileset);
if (tile_image.isNull()) {
// Some metatiles specify tiles that are outside the valid range.
// These are treated as completely transparent, so they can be skipped without
// being drawn unless they're on the bottom layer, in which case we need
// a placeholder because garbage will be drawn otherwise.
if (l == bottomLayer) {
metatile_painter.fillRect(x * 8, y * 8, 8, 8, palettes.value(0).value(0));
}
// The way the GBA will render these depends on what's in memory (which Porymap can't know)
// so we treat them as if they were transparent.
continue;
}
// Colorize the metatile tiles with its palette.
if (tile.palette < palettes.length()) {
QList<QRgb> palette = palettes.value(tile.palette);
const QList<QRgb> palette = palettes.value(tile.palette);
for (int j = 0; j < palette.length(); j++) {
tile_image.setColor(j, palette.value(j));
}
@ -121,12 +122,10 @@ QImage getMetatileImage(
}
}
// The top layer of the metatile has its first color displayed at transparent.
if (l != bottomLayer) {
// Color 0 is displayed as transparent.
QColor color(tile_image.color(0));
color.setAlpha(0);
tile_image.setColor(0, color.rgba());
}
metatile_painter.drawImage(origin, tile_image.mirrored(tile.xflip, tile.yflip));
}
@ -144,7 +143,7 @@ QImage getTileImage(uint16_t tileId, Tileset *primaryTileset, Tileset *secondary
return tileset->tiles.value(index, QImage());
}
QImage getColoredTileImage(uint16_t tileId, Tileset *primaryTileset, Tileset *secondaryTileset, QList<QRgb> palette) {
QImage getColoredTileImage(uint16_t tileId, Tileset *primaryTileset, Tileset *secondaryTileset, const QList<QRgb> &palette) {
QImage tileImage = getTileImage(tileId, primaryTileset, secondaryTileset);
if (tileImage.isNull()) {
tileImage = QImage(8, 8, QImage::Format_RGBA8888);

View file

@ -82,6 +82,8 @@ void ProjectSettingsEditor::connectSignals() {
}
for (auto checkBox : ui->centralwidget->findChildren<QCheckBox *>())
connect(checkBox, &QCheckBox::stateChanged, this, &ProjectSettingsEditor::markEdited);
for (auto radioButton : ui->centralwidget->findChildren<QRadioButton *>())
connect(radioButton, &QRadioButton::toggled, this, &ProjectSettingsEditor::markEdited);
for (auto lineEdit : ui->centralwidget->findChildren<QLineEdit *>())
connect(lineEdit, &QLineEdit::textEdited, this, &ProjectSettingsEditor::markEdited);
for (auto spinBox : ui->centralwidget->findChildren<NoScrollSpinBox *>())
@ -135,6 +137,9 @@ void ProjectSettingsEditor::initUi() {
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);
// The values for some of the settings we provide in this window can be determined using constants in the user's projects.
// If the user has these constants we disable these settings in the UI -- they can modify them using their constants.
@ -442,6 +447,12 @@ void ProjectSettingsEditor::refresh() {
ui->checkBox_OutputIsCompressed->setChecked(projectConfig.tilesetsHaveIsCompressed);
ui->checkBox_DisableWarning->setChecked(porymapConfig.warpBehaviorWarningDisabled);
// Radio buttons
if (projectConfig.setTransparentPixelsBlack)
ui->radioButton_RenderBlack->setChecked(true);
else
ui->radioButton_RenderFirstPalColor->setChecked(true);
// Set spin box values
ui->spinBox_Elevation->setValue(projectConfig.defaultElevation);
ui->spinBox_Collision->setValue(projectConfig.defaultCollision);
@ -455,6 +466,9 @@ void ProjectSettingsEditor::refresh() {
ui->spinBox_MetatileIdMask->setValue(projectConfig.blockMetatileIdMask & ui->spinBox_MetatileIdMask->maximum());
ui->spinBox_CollisionMask->setValue(projectConfig.blockCollisionMask & ui->spinBox_CollisionMask->maximum());
ui->spinBox_ElevationMask->setValue(projectConfig.blockElevationMask & ui->spinBox_ElevationMask->maximum());
ui->spinBox_UnusedTileNormal->setValue(projectConfig.unusedTileNormal);
ui->spinBox_UnusedTileCovered->setValue(projectConfig.unusedTileCovered);
ui->spinBox_UnusedTileSplit->setValue(projectConfig.unusedTileSplit);
// Set (and sync) border metatile IDs
this->setBorderMetatileIds(false, projectConfig.newMapBorderMetatileIds);
@ -511,6 +525,7 @@ void ProjectSettingsEditor::save() {
projectConfig.tilesetsHaveCallback = ui->checkBox_OutputCallback->isChecked();
projectConfig.tilesetsHaveIsCompressed = ui->checkBox_OutputIsCompressed->isChecked();
porymapConfig.warpBehaviorWarningDisabled = ui->checkBox_DisableWarning->isChecked();
projectConfig.setTransparentPixelsBlack = ui->radioButton_RenderBlack->isChecked();
// Save spin box settings
projectConfig.defaultElevation = ui->spinBox_Elevation->value();
@ -525,6 +540,9 @@ void ProjectSettingsEditor::save() {
projectConfig.blockMetatileIdMask = ui->spinBox_MetatileIdMask->value();
projectConfig.blockCollisionMask = ui->spinBox_CollisionMask->value();
projectConfig.blockElevationMask = ui->spinBox_ElevationMask->value();
projectConfig.unusedTileNormal = ui->spinBox_UnusedTileNormal->value();
projectConfig.unusedTileCovered = ui->spinBox_UnusedTileCovered->value();
projectConfig.unusedTileSplit = ui->spinBox_UnusedTileSplit->value();
// Save line edit settings
projectConfig.prefabFilepath = ui->lineEdit_PrefabsPath->text();

View file

@ -99,7 +99,13 @@ void TilesetEditor::initUi() {
this->paletteId = ui->spinBox_paletteSelector->value();
this->ui->spinBox_paletteSelector->setMinimum(0);
this->ui->spinBox_paletteSelector->setMaximum(Project::getNumPalettesTotal() - 1);
this->ui->actionShow_Tileset_Divider->setChecked(porymapConfig.showTilesetEditorDivider);
// TODO: The dividing line at the moment is only accurate if the number of primary metatiles is divisible by 8.
// If it's not, the secondary metatiles will wrap above the line. This has other problems (like skewing
// metatile groups the user may have designed) so this should be fixed by filling the primary metatiles
// image with invalid magenta metatiles until it's divisible by 8. Then the line can be re-enabled as-is.
this->ui->actionShow_Tileset_Divider->setChecked(/*porymapConfig.showTilesetEditorDivider*/false);
this->ui->actionShow_Tileset_Divider->setVisible(false);
this->setAttributesUi();
this->setMetatileLabelValidator();