Add config settings for rendering transparency
This commit is contained in:
parent
d768075a26
commit
486d1b7335
10 changed files with 201 additions and 47 deletions
|
@ -369,7 +369,7 @@
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>559</width>
|
<width>559</width>
|
||||||
<height>548</height>
|
<height>560</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_16">
|
<layout class="QVBoxLayout" name="verticalLayout_16">
|
||||||
|
@ -602,7 +602,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="0" column="1">
|
||||||
<widget class="UIntHexSpinBox" name="spinBox_MetatileIdMask" native="true">
|
<widget class="UIntHexSpinBox" name="spinBox_MetatileIdMask">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>The mask used to read/write metatile IDs in map data.</string>
|
<string>The mask used to read/write metatile IDs in map data.</string>
|
||||||
</property>
|
</property>
|
||||||
|
@ -616,7 +616,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="UIntHexSpinBox" name="spinBox_CollisionMask" native="true">
|
<widget class="UIntHexSpinBox" name="spinBox_CollisionMask">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>The mask used to read/write collision values in map data.</string>
|
<string>The mask used to read/write collision values in map data.</string>
|
||||||
</property>
|
</property>
|
||||||
|
@ -630,7 +630,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="1">
|
<item row="2" column="1">
|
||||||
<widget class="UIntHexSpinBox" name="spinBox_ElevationMask" native="true">
|
<widget class="UIntHexSpinBox" name="spinBox_ElevationMask">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>The mask used to read/write elevation values in map data.</string>
|
<string>The mask used to read/write elevation values in map data.</string>
|
||||||
</property>
|
</property>
|
||||||
|
@ -742,7 +742,7 @@
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>559</width>
|
<width>559</width>
|
||||||
<height>568</height>
|
<height>798</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_12">
|
<layout class="QVBoxLayout" name="verticalLayout_12">
|
||||||
|
@ -775,6 +775,86 @@
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</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>
|
<item>
|
||||||
<widget class="QFrame" name="frame_WarningTilesetsTab">
|
<widget class="QFrame" name="frame_WarningTilesetsTab">
|
||||||
<property name="styleSheet">
|
<property name="styleSheet">
|
||||||
|
@ -810,14 +890,14 @@
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout_2">
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
<item row="4" column="1">
|
<item row="4" column="1">
|
||||||
<widget class="UIntHexSpinBox" name="spinBox_LayerTypeMask" native="true">
|
<widget class="UIntHexSpinBox" name="spinBox_LayerTypeMask">
|
||||||
<property name="toolTip">
|
<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>
|
<string>The mask used to read/write Layer Type from the metatile's attributes data. If 0, this attribute is disabled.</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1">
|
<item row="3" column="1">
|
||||||
<widget class="UIntHexSpinBox" name="spinBox_BehaviorMask" native="true">
|
<widget class="UIntHexSpinBox" name="spinBox_BehaviorMask">
|
||||||
<property name="toolTip">
|
<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>
|
<string>The mask used to read/write Metatile Behavior from the metatile's attributes data. If 0, this attribute is disabled.</string>
|
||||||
</property>
|
</property>
|
||||||
|
@ -864,7 +944,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="7" column="1">
|
<item row="7" column="1">
|
||||||
<widget class="UIntHexSpinBox" name="spinBox_TerrainTypeMask" native="true">
|
<widget class="UIntHexSpinBox" name="spinBox_TerrainTypeMask">
|
||||||
<property name="toolTip">
|
<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>
|
<string>The mask used to read/write Terrain Type from the metatile's attributes data. If 0, this attribute is disabled.</string>
|
||||||
</property>
|
</property>
|
||||||
|
@ -891,7 +971,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="1">
|
<item row="5" column="1">
|
||||||
<widget class="UIntHexSpinBox" name="spinBox_EncounterTypeMask" native="true">
|
<widget class="UIntHexSpinBox" name="spinBox_EncounterTypeMask">
|
||||||
<property name="toolTip">
|
<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>
|
<string>The mask used to read/write Encounter Type from the metatile's attributes data. If 0, this attribute is disabled.</string>
|
||||||
</property>
|
</property>
|
||||||
|
@ -1549,10 +1629,15 @@
|
||||||
<header>noscrollspinbox.h</header>
|
<header>noscrollspinbox.h</header>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>UIntHexSpinBox</class>
|
<class>UIntSpinBox</class>
|
||||||
<extends>QWidget</extends>
|
<extends>QAbstractSpinBox</extends>
|
||||||
<header>uintspinbox.h</header>
|
<header>uintspinbox.h</header>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>UIntHexSpinBox</class>
|
||||||
|
<extends>UIntSpinBox</extends>
|
||||||
|
<header location="global">uintspinbox.h</header>
|
||||||
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="../resources/images.qrc"/>
|
<include location="../resources/images.qrc"/>
|
||||||
|
|
|
@ -301,6 +301,7 @@ public:
|
||||||
this->prefabImportPrompted = false;
|
this->prefabImportPrompted = false;
|
||||||
this->tilesetsHaveCallback = true;
|
this->tilesetsHaveCallback = true;
|
||||||
this->tilesetsHaveIsCompressed = true;
|
this->tilesetsHaveIsCompressed = true;
|
||||||
|
this->setTransparentPixelsBlack = true;
|
||||||
this->filePaths.clear();
|
this->filePaths.clear();
|
||||||
this->eventIconPaths.clear();
|
this->eventIconPaths.clear();
|
||||||
this->pokemonIconPaths.clear();
|
this->pokemonIconPaths.clear();
|
||||||
|
@ -310,6 +311,9 @@ public:
|
||||||
this->blockMetatileIdMask = 0x03FF;
|
this->blockMetatileIdMask = 0x03FF;
|
||||||
this->blockCollisionMask = 0x0C00;
|
this->blockCollisionMask = 0x0C00;
|
||||||
this->blockElevationMask = 0xF000;
|
this->blockElevationMask = 0xF000;
|
||||||
|
this->unusedTileNormal = 0x3014;
|
||||||
|
this->unusedTileCovered = 0x0000;
|
||||||
|
this->unusedTileSplit = 0x0000;
|
||||||
this->identifiers.clear();
|
this->identifiers.clear();
|
||||||
this->readKeys.clear();
|
this->readKeys.clear();
|
||||||
}
|
}
|
||||||
|
@ -362,6 +366,7 @@ public:
|
||||||
bool prefabImportPrompted;
|
bool prefabImportPrompted;
|
||||||
bool tilesetsHaveCallback;
|
bool tilesetsHaveCallback;
|
||||||
bool tilesetsHaveIsCompressed;
|
bool tilesetsHaveIsCompressed;
|
||||||
|
bool setTransparentPixelsBlack;
|
||||||
int metatileAttributesSize;
|
int metatileAttributesSize;
|
||||||
uint32_t metatileBehaviorMask;
|
uint32_t metatileBehaviorMask;
|
||||||
uint32_t metatileTerrainTypeMask;
|
uint32_t metatileTerrainTypeMask;
|
||||||
|
@ -370,6 +375,9 @@ public:
|
||||||
uint16_t blockMetatileIdMask;
|
uint16_t blockMetatileIdMask;
|
||||||
uint16_t blockCollisionMask;
|
uint16_t blockCollisionMask;
|
||||||
uint16_t blockElevationMask;
|
uint16_t blockElevationMask;
|
||||||
|
uint16_t unusedTileNormal;
|
||||||
|
uint16_t unusedTileCovered;
|
||||||
|
uint16_t unusedTileSplit;
|
||||||
bool mapAllowFlagsEnabled;
|
bool mapAllowFlagsEnabled;
|
||||||
QString collisionSheetPath;
|
QString collisionSheetPath;
|
||||||
int collisionSheetWidth;
|
int collisionSheetWidth;
|
||||||
|
|
|
@ -19,6 +19,8 @@ public:
|
||||||
uint16_t rawValue() const;
|
uint16_t rawValue() const;
|
||||||
|
|
||||||
static int getIndexInTileset(int);
|
static int getIndexInTileset(int);
|
||||||
|
|
||||||
|
static const uint16_t maxValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool operator==(const Tile &a, const Tile &b) {
|
inline bool operator==(const Tile &a, const Tile &b) {
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
|
|
||||||
QImage getCollisionMetatileImage(Block);
|
QImage getCollisionMetatileImage(Block);
|
||||||
QImage getCollisionMetatileImage(int, int);
|
QImage getCollisionMetatileImage(int, int);
|
||||||
QImage getMetatileImage(uint16_t, 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*, QList<int>, 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 getTileImage(uint16_t, Tileset*, Tileset*);
|
||||||
QImage getPalettedTileImage(uint16_t, Tileset*, Tileset*, int, bool useTruePalettes = false);
|
QImage getPalettedTileImage(uint16_t, Tileset*, Tileset*, int, bool useTruePalettes = false);
|
||||||
QImage getGreyscaleTileImage(uint16_t tile, Tileset *primaryTileset, Tileset *secondaryTileset);
|
QImage getGreyscaleTileImage(uint16_t tile, Tileset *primaryTileset, Tileset *secondaryTileset);
|
||||||
|
|
|
@ -720,6 +720,12 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) {
|
||||||
this->blockCollisionMask = getConfigUint32(key, value, 0, Block::maxValue);
|
this->blockCollisionMask = getConfigUint32(key, value, 0, Block::maxValue);
|
||||||
} else if (key == "block_elevation_mask") {
|
} else if (key == "block_elevation_mask") {
|
||||||
this->blockElevationMask = getConfigUint32(key, value, 0, Block::maxValue);
|
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") {
|
} else if (key == "enable_map_allow_flags") {
|
||||||
this->mapAllowFlagsEnabled = getConfigBool(key, value);
|
this->mapAllowFlagsEnabled = getConfigBool(key, value);
|
||||||
#ifdef CONFIG_BACKWARDS_COMPATABILITY
|
#ifdef CONFIG_BACKWARDS_COMPATABILITY
|
||||||
|
@ -752,6 +758,8 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) {
|
||||||
this->tilesetsHaveCallback = getConfigBool(key, value);
|
this->tilesetsHaveCallback = getConfigBool(key, value);
|
||||||
} else if (key == "tilesets_have_is_compressed") {
|
} else if (key == "tilesets_have_is_compressed") {
|
||||||
this->tilesetsHaveIsCompressed = getConfigBool(key, value);
|
this->tilesetsHaveIsCompressed = getConfigBool(key, value);
|
||||||
|
} else if (key == "set_transparent_pixels_black") {
|
||||||
|
this->setTransparentPixelsBlack = getConfigBool(key, value);
|
||||||
} else if (key == "event_icon_path_object") {
|
} else if (key == "event_icon_path_object") {
|
||||||
this->eventIconPaths[Event::Group::Object] = value;
|
this->eventIconPaths[Event::Group::Object] = value;
|
||||||
} else if (key == "event_icon_path_warp") {
|
} 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_callback", QString::number(this->tilesetsHaveCallback));
|
||||||
map.insert("tilesets_have_is_compressed", QString::number(this->tilesetsHaveIsCompressed));
|
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_attributes_size", QString::number(this->metatileAttributesSize));
|
||||||
map.insert("metatile_behavior_mask", "0x" + QString::number(this->metatileBehaviorMask, 16).toUpper());
|
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_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_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_collision_mask", "0x" + QString::number(this->blockCollisionMask, 16).toUpper());
|
||||||
map.insert("block_elevation_mask", "0x" + QString::number(this->blockElevationMask, 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("enable_map_allow_flags", QString::number(this->mapAllowFlagsEnabled));
|
||||||
map.insert("event_icon_path_object", this->eventIconPaths[Event::Group::Object]);
|
map.insert("event_icon_path_object", this->eventIconPaths[Event::Group::Object]);
|
||||||
map.insert("event_icon_path_warp", this->eventIconPaths[Event::Group::Warp]);
|
map.insert("event_icon_path_warp", this->eventIconPaths[Event::Group::Warp]);
|
||||||
|
|
|
@ -1,5 +1,17 @@
|
||||||
#include "tile.h"
|
#include "tile.h"
|
||||||
#include "project.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() :
|
Tile::Tile() :
|
||||||
tileId(0),
|
tileId(0),
|
||||||
|
@ -16,18 +28,17 @@
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
Tile::Tile(uint16_t raw) :
|
Tile::Tile(uint16_t raw) :
|
||||||
tileId(raw & 0x3FF),
|
tileId(bitsTileId.unpack(raw)),
|
||||||
xflip((raw >> 10) & 1),
|
xflip(bitsXFlip.unpack(raw)),
|
||||||
yflip((raw >> 11) & 1),
|
yflip(bitsYFlip.unpack(raw)),
|
||||||
palette((raw >> 12) & 0xF)
|
palette(bitsPalette.unpack(raw))
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
uint16_t Tile::rawValue() const {
|
uint16_t Tile::rawValue() const {
|
||||||
return static_cast<uint16_t>(
|
return bitsTileId.pack(this->tileId)
|
||||||
(this->tileId & 0x3FF)
|
| bitsXFlip.pack(this->xflip)
|
||||||
| ((this->xflip & 1) << 10)
|
| bitsYFlip.pack(this->yflip)
|
||||||
| ((this->yflip & 1) << 11)
|
| bitsPalette.pack(this->palette);
|
||||||
| ((this->palette & 0xF) << 12));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int Tile::getIndexInTileset(int tileId) {
|
int Tile::getIndexInTileset(int tileId) {
|
||||||
|
|
|
@ -2096,19 +2096,29 @@ bool Project::readFieldmapProperties() {
|
||||||
fileWatcher.addPath(root + "/" + filename);
|
fileWatcher.addPath(root + "/" + filename);
|
||||||
const QMap<QString, int> defines = parser.readCDefinesByName(filename, names);
|
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);
|
auto it = defines.find(name);
|
||||||
if (it != defines.end()) {
|
if (it != defines.end()) {
|
||||||
*dest = it.value();
|
*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 {
|
} else {
|
||||||
logWarn(QString("Value for tileset property '%1' not found. Using default (%2) instead.").arg(name).arg(*dest));
|
logWarn(QString("Value for tileset property '%1' not found. Using default (%2) instead.").arg(name).arg(*dest));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
loadDefine(numTilesPrimaryName, &Project::num_tiles_primary);
|
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);
|
loadDefine(numTilesTotalName, &Project::num_tiles_total, 2, 1024); // 1024 is fixed because we store tile IDs in a 10-bit field.
|
||||||
loadDefine(numMetatilesPrimaryName, &Project::num_metatiles_primary);
|
loadDefine(numPalsPrimaryName, &Project::num_pals_primary, 1, Project::num_pals_total - 1);
|
||||||
loadDefine(numPalsPrimaryName, &Project::num_pals_primary);
|
loadDefine(numTilesPrimaryName, &Project::num_tiles_primary, 1, Project::num_tiles_total - 1);
|
||||||
loadDefine(numPalsTotalName, &Project::num_pals_total);
|
|
||||||
|
// 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);
|
auto it = defines.find(maxMapSizeName);
|
||||||
if (it != defines.end()) {
|
if (it != defines.end()) {
|
||||||
|
@ -3020,12 +3030,12 @@ void Project::applyParsedLimits() {
|
||||||
Block::setLayout();
|
Block::setLayout();
|
||||||
Metatile::setLayout(this);
|
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.defaultMetatileId = qMin(projectConfig.defaultMetatileId, Block::getMaxMetatileId());
|
||||||
projectConfig.defaultElevation = qMin(projectConfig.defaultElevation, Block::getMaxElevation());
|
projectConfig.defaultElevation = qMin(projectConfig.defaultElevation, Block::getMaxElevation());
|
||||||
projectConfig.defaultCollision = qMin(projectConfig.defaultCollision, Block::getMaxCollision());
|
projectConfig.defaultCollision = qMin(projectConfig.defaultCollision, Block::getMaxCollision());
|
||||||
projectConfig.collisionSheetHeight = qMin(projectConfig.collisionSheetHeight, Block::getMaxElevation() + 1);
|
projectConfig.collisionSheetHeight = qMin(qMax(projectConfig.collisionSheetHeight, 1), Block::getMaxElevation() + 1);
|
||||||
projectConfig.collisionSheetWidth = qMin(projectConfig.collisionSheetWidth, Block::getMaxCollision() + 1);
|
projectConfig.collisionSheetWidth = qMin(qMax(projectConfig.collisionSheetWidth, 1), Block::getMaxCollision() + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Project::hasUnsavedChanges() {
|
bool Project::hasUnsavedChanges() {
|
||||||
|
|
|
@ -17,8 +17,8 @@ QImage getMetatileImage(
|
||||||
uint16_t metatileId,
|
uint16_t metatileId,
|
||||||
Tileset *primaryTileset,
|
Tileset *primaryTileset,
|
||||||
Tileset *secondaryTileset,
|
Tileset *secondaryTileset,
|
||||||
QList<int> layerOrder,
|
const QList<int> &layerOrder,
|
||||||
QList<float> layerOpacity,
|
const QList<float> &layerOpacity,
|
||||||
bool useTruePalettes)
|
bool useTruePalettes)
|
||||||
{
|
{
|
||||||
Metatile* metatile = Tileset::getMetatile(metatileId, primaryTileset, secondaryTileset);
|
Metatile* metatile = Tileset::getMetatile(metatileId, primaryTileset, secondaryTileset);
|
||||||
|
@ -34,8 +34,8 @@ QImage getMetatileImage(
|
||||||
Metatile *metatile,
|
Metatile *metatile,
|
||||||
Tileset *primaryTileset,
|
Tileset *primaryTileset,
|
||||||
Tileset *secondaryTileset,
|
Tileset *secondaryTileset,
|
||||||
QList<int> layerOrder,
|
const QList<int> &layerOrder,
|
||||||
QList<float> layerOpacity,
|
const QList<float> &layerOpacity,
|
||||||
bool useTruePalettes)
|
bool useTruePalettes)
|
||||||
{
|
{
|
||||||
QImage metatile_image(16, 16, QImage::Format_RGBA8888);
|
QImage metatile_image(16, 16, QImage::Format_RGBA8888);
|
||||||
|
@ -44,13 +44,15 @@ QImage getMetatileImage(
|
||||||
return metatile_image;
|
return metatile_image;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The GBA renders transparent pixels using palette 0 color 0. We have access to that color (palettes.value(0).value(0))
|
|
||||||
// but all 3 games actually overwrite this color with black when loading the tileset palettes, so we fill the metatile
|
|
||||||
// image with black so that any pixels we don't render will reveal the correct color.
|
|
||||||
metatile_image.fill(Qt::black);
|
|
||||||
|
|
||||||
QList<QList<QRgb>> palettes = Tileset::getBlockPalettes(primaryTileset, secondaryTileset, useTruePalettes);
|
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);
|
QPainter metatile_painter(&metatile_image);
|
||||||
const int numLayers = 3; // When rendering, metatiles always have 3 layers
|
const int numLayers = 3; // When rendering, metatiles always have 3 layers
|
||||||
uint32_t layerType = metatile->layerType();
|
uint32_t layerType = metatile->layerType();
|
||||||
|
@ -66,25 +68,25 @@ QImage getMetatileImage(
|
||||||
tile = metatile->tiles.value(tileOffset + (l * 4));
|
tile = metatile->tiles.value(tileOffset + (l * 4));
|
||||||
} else {
|
} else {
|
||||||
// "Vanilla" metatiles only have 8 tiles, but render 12.
|
// "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)
|
switch (layerType)
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
case METATILE_LAYER_MIDDLE_TOP:
|
case METATILE_LAYER_MIDDLE_TOP:
|
||||||
if (l == 0)
|
if (l == 0)
|
||||||
tile = Tile(0x3014);
|
tile = Tile(projectConfig.unusedTileNormal);
|
||||||
else // Tiles are on layers 1 and 2
|
else // Tiles are on layers 1 and 2
|
||||||
tile = metatile->tiles.value(tileOffset + ((l - 1) * 4));
|
tile = metatile->tiles.value(tileOffset + ((l - 1) * 4));
|
||||||
break;
|
break;
|
||||||
case METATILE_LAYER_BOTTOM_MIDDLE:
|
case METATILE_LAYER_BOTTOM_MIDDLE:
|
||||||
if (l == 2)
|
if (l == 2)
|
||||||
tile = Tile();
|
tile = Tile(projectConfig.unusedTileCovered);
|
||||||
else // Tiles are on layers 0 and 1
|
else // Tiles are on layers 0 and 1
|
||||||
tile = metatile->tiles.value(tileOffset + (l * 4));
|
tile = metatile->tiles.value(tileOffset + (l * 4));
|
||||||
break;
|
break;
|
||||||
case METATILE_LAYER_BOTTOM_TOP:
|
case METATILE_LAYER_BOTTOM_TOP:
|
||||||
if (l == 1)
|
if (l == 1)
|
||||||
tile = Tile();
|
tile = Tile(projectConfig.unusedTileSplit);
|
||||||
else // Tiles are on layers 0 and 2
|
else // Tiles are on layers 0 and 2
|
||||||
tile = metatile->tiles.value(tileOffset + ((l == 0 ? 0 : 1) * 4));
|
tile = metatile->tiles.value(tileOffset + ((l == 0 ? 0 : 1) * 4));
|
||||||
break;
|
break;
|
||||||
|
@ -101,7 +103,7 @@ QImage getMetatileImage(
|
||||||
|
|
||||||
// Colorize the metatile tiles with its palette.
|
// Colorize the metatile tiles with its palette.
|
||||||
if (tile.palette < palettes.length()) {
|
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++) {
|
for (int j = 0; j < palette.length(); j++) {
|
||||||
tile_image.setColor(j, palette.value(j));
|
tile_image.setColor(j, palette.value(j));
|
||||||
}
|
}
|
||||||
|
@ -141,7 +143,7 @@ QImage getTileImage(uint16_t tileId, Tileset *primaryTileset, Tileset *secondary
|
||||||
return tileset->tiles.value(index, QImage());
|
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);
|
QImage tileImage = getTileImage(tileId, primaryTileset, secondaryTileset);
|
||||||
if (tileImage.isNull()) {
|
if (tileImage.isNull()) {
|
||||||
tileImage = QImage(8, 8, QImage::Format_RGBA8888);
|
tileImage = QImage(8, 8, QImage::Format_RGBA8888);
|
||||||
|
|
|
@ -82,6 +82,8 @@ void ProjectSettingsEditor::connectSignals() {
|
||||||
}
|
}
|
||||||
for (auto checkBox : ui->centralwidget->findChildren<QCheckBox *>())
|
for (auto checkBox : ui->centralwidget->findChildren<QCheckBox *>())
|
||||||
connect(checkBox, &QCheckBox::stateChanged, this, &ProjectSettingsEditor::markEdited);
|
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 *>())
|
for (auto lineEdit : ui->centralwidget->findChildren<QLineEdit *>())
|
||||||
connect(lineEdit, &QLineEdit::textEdited, this, &ProjectSettingsEditor::markEdited);
|
connect(lineEdit, &QLineEdit::textEdited, this, &ProjectSettingsEditor::markEdited);
|
||||||
for (auto spinBox : ui->centralwidget->findChildren<NoScrollSpinBox *>())
|
for (auto spinBox : ui->centralwidget->findChildren<NoScrollSpinBox *>())
|
||||||
|
@ -135,6 +137,9 @@ void ProjectSettingsEditor::initUi() {
|
||||||
ui->spinBox_MetatileIdMask->setMaximum(Block::maxValue);
|
ui->spinBox_MetatileIdMask->setMaximum(Block::maxValue);
|
||||||
ui->spinBox_CollisionMask->setMaximum(Block::maxValue);
|
ui->spinBox_CollisionMask->setMaximum(Block::maxValue);
|
||||||
ui->spinBox_ElevationMask->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.
|
// 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.
|
// 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_OutputIsCompressed->setChecked(projectConfig.tilesetsHaveIsCompressed);
|
||||||
ui->checkBox_DisableWarning->setChecked(porymapConfig.warpBehaviorWarningDisabled);
|
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
|
// Set spin box values
|
||||||
ui->spinBox_Elevation->setValue(projectConfig.defaultElevation);
|
ui->spinBox_Elevation->setValue(projectConfig.defaultElevation);
|
||||||
ui->spinBox_Collision->setValue(projectConfig.defaultCollision);
|
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_MetatileIdMask->setValue(projectConfig.blockMetatileIdMask & ui->spinBox_MetatileIdMask->maximum());
|
||||||
ui->spinBox_CollisionMask->setValue(projectConfig.blockCollisionMask & ui->spinBox_CollisionMask->maximum());
|
ui->spinBox_CollisionMask->setValue(projectConfig.blockCollisionMask & ui->spinBox_CollisionMask->maximum());
|
||||||
ui->spinBox_ElevationMask->setValue(projectConfig.blockElevationMask & ui->spinBox_ElevationMask->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
|
// Set (and sync) border metatile IDs
|
||||||
this->setBorderMetatileIds(false, projectConfig.newMapBorderMetatileIds);
|
this->setBorderMetatileIds(false, projectConfig.newMapBorderMetatileIds);
|
||||||
|
@ -511,6 +525,7 @@ void ProjectSettingsEditor::save() {
|
||||||
projectConfig.tilesetsHaveCallback = ui->checkBox_OutputCallback->isChecked();
|
projectConfig.tilesetsHaveCallback = ui->checkBox_OutputCallback->isChecked();
|
||||||
projectConfig.tilesetsHaveIsCompressed = ui->checkBox_OutputIsCompressed->isChecked();
|
projectConfig.tilesetsHaveIsCompressed = ui->checkBox_OutputIsCompressed->isChecked();
|
||||||
porymapConfig.warpBehaviorWarningDisabled = ui->checkBox_DisableWarning->isChecked();
|
porymapConfig.warpBehaviorWarningDisabled = ui->checkBox_DisableWarning->isChecked();
|
||||||
|
projectConfig.setTransparentPixelsBlack = ui->radioButton_RenderBlack->isChecked();
|
||||||
|
|
||||||
// Save spin box settings
|
// Save spin box settings
|
||||||
projectConfig.defaultElevation = ui->spinBox_Elevation->value();
|
projectConfig.defaultElevation = ui->spinBox_Elevation->value();
|
||||||
|
@ -525,6 +540,9 @@ void ProjectSettingsEditor::save() {
|
||||||
projectConfig.blockMetatileIdMask = ui->spinBox_MetatileIdMask->value();
|
projectConfig.blockMetatileIdMask = ui->spinBox_MetatileIdMask->value();
|
||||||
projectConfig.blockCollisionMask = ui->spinBox_CollisionMask->value();
|
projectConfig.blockCollisionMask = ui->spinBox_CollisionMask->value();
|
||||||
projectConfig.blockElevationMask = ui->spinBox_ElevationMask->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
|
// Save line edit settings
|
||||||
projectConfig.prefabFilepath = ui->lineEdit_PrefabsPath->text();
|
projectConfig.prefabFilepath = ui->lineEdit_PrefabsPath->text();
|
||||||
|
|
|
@ -99,7 +99,13 @@ void TilesetEditor::initUi() {
|
||||||
this->paletteId = ui->spinBox_paletteSelector->value();
|
this->paletteId = ui->spinBox_paletteSelector->value();
|
||||||
this->ui->spinBox_paletteSelector->setMinimum(0);
|
this->ui->spinBox_paletteSelector->setMinimum(0);
|
||||||
this->ui->spinBox_paletteSelector->setMaximum(Project::getNumPalettesTotal() - 1);
|
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->setAttributesUi();
|
||||||
this->setMetatileLabelValidator();
|
this->setMetatileLabelValidator();
|
||||||
|
|
Loading…
Reference in a new issue