Clean up custom Block layouts
This commit is contained in:
parent
5a3907bf56
commit
60fb1a246e
16 changed files with 268 additions and 164 deletions
|
@ -21,7 +21,7 @@
|
|||
<item>
|
||||
<widget class="QTabWidget" name="mainTabs">
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab_General">
|
||||
<attribute name="title">
|
||||
|
@ -370,7 +370,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>531</width>
|
||||
<height>545</height>
|
||||
<height>587</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_16">
|
||||
|
@ -603,20 +603,6 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_CollisionMask">
|
||||
<property name="text">
|
||||
<string>Collision</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_ElevationMask">
|
||||
<property name="text">
|
||||
<string>Elevation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="UIntHexSpinBox" name="spinBox_MetatileIdMask" native="true">
|
||||
<property name="toolTip">
|
||||
|
@ -624,6 +610,13 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_CollisionMask">
|
||||
<property name="text">
|
||||
<string>Collision</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="UIntHexSpinBox" name="spinBox_CollisionMask" native="true">
|
||||
<property name="toolTip">
|
||||
|
@ -631,6 +624,13 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_ElevationMask">
|
||||
<property name="text">
|
||||
<string>Elevation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="UIntHexSpinBox" name="spinBox_ElevationMask" native="true">
|
||||
<property name="toolTip">
|
||||
|
@ -638,6 +638,19 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_OverlapWarningBlocks">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color : red;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>These masks have overlapping bits. This may result in unexpected value changes.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -731,7 +744,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>528</width>
|
||||
<height>522</height>
|
||||
<height>568</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_12">
|
||||
|
@ -799,10 +812,17 @@
|
|||
<string>Metatiles</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_LayerTypeMask">
|
||||
<property name="text">
|
||||
<string>Layer Type mask</string>
|
||||
<item row="4" column="1">
|
||||
<widget class="UIntHexSpinBox" name="spinBox_LayerTypeMask" native="true">
|
||||
<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">
|
||||
<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>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -816,24 +836,33 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="NoScrollComboBox" name="comboBox_AttributesSize">
|
||||
<property name="editable">
|
||||
<bool>false</bool>
|
||||
<item row="1" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="UIntHexSpinBox" name="spinBox_EncounterTypeMask" native="true">
|
||||
<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 name="sizeType">
|
||||
<enum>QSizePolicy::Maximum</enum>
|
||||
</property>
|
||||
</widget>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>10</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_BehaviorMask">
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_EncounterTypeMask">
|
||||
<property name="text">
|
||||
<string>Behavior mask</string>
|
||||
<string>Encounter Type mask</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="checkBox_EnableTripleLayerMetatiles">
|
||||
<property name="text">
|
||||
<string>Enable Triple Layer Metatiles</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -844,18 +873,46 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_BehaviorMask">
|
||||
<property name="text">
|
||||
<string>Behavior mask</string>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>15</height>
|
||||
</size>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_OverlapWarningMetatiles">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color : red;</string>
|
||||
</property>
|
||||
</spacer>
|
||||
<property name="text">
|
||||
<string>These masks have overlapping bits. This may result in unexpected value changes.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="UIntHexSpinBox" name="spinBox_EncounterTypeMask" native="true">
|
||||
<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>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_LayerTypeMask">
|
||||
<property name="text">
|
||||
<string>Layer Type mask</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="NoScrollComboBox" name="comboBox_AttributesSize">
|
||||
<property name="editable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="label_TerrainTypeMask">
|
||||
|
@ -864,33 +921,21 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_EncounterTypeMask">
|
||||
<property name="text">
|
||||
<string>Encounter Type mask</string>
|
||||
<item row="9" column="0">
|
||||
<spacer name="verticalSpacer_6">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="UIntHexSpinBox" name="spinBox_BehaviorMask" native="true">
|
||||
<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 name="sizeType">
|
||||
<enum>QSizePolicy::MinimumExpanding</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="UIntHexSpinBox" name="spinBox_LayerTypeMask" native="true">
|
||||
<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 name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>1</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="checkBox_EnableTripleLayerMetatiles">
|
||||
<property name="text">
|
||||
<string>Enable Triple Layer Metatiles</string>
|
||||
</property>
|
||||
</widget>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
|
|
|
@ -286,10 +286,10 @@ public:
|
|||
int getNumTilesInMetatile();
|
||||
void setDefaultMetatileId(uint16_t metatileId);
|
||||
uint16_t getDefaultMetatileId();
|
||||
void setDefaultElevation(int elevation);
|
||||
int getDefaultElevation();
|
||||
void setDefaultCollision(int collision);
|
||||
int getDefaultCollision();
|
||||
void setDefaultElevation(uint16_t elevation);
|
||||
uint16_t getDefaultElevation();
|
||||
void setDefaultCollision(uint16_t collision);
|
||||
uint16_t getDefaultCollision();
|
||||
void setNewMapBorderMetatileIds(QList<uint16_t> metatileIds);
|
||||
QList<uint16_t> getNewMapBorderMetatileIds();
|
||||
QString getDefaultPrimaryTileset();
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
#define BITPACKER_H
|
||||
|
||||
#include <QList>
|
||||
//#include <cstdint>
|
||||
|
||||
class BitPacker
|
||||
{
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
|
||||
class Project;
|
||||
|
||||
// TODO: Reevaluate enums
|
||||
enum {
|
||||
METATILE_LAYER_MIDDLE_TOP,
|
||||
METATILE_LAYER_BOTTOM_MIDDLE,
|
||||
|
@ -72,6 +71,7 @@ public:
|
|||
static int getIndexInTileset(int);
|
||||
static QPoint coordFromPixmapCoord(const QPointF &pixelCoord);
|
||||
static uint32_t getDefaultAttributesMask(BaseGameVersion version, Metatile::Attr attr);
|
||||
static uint32_t getMaxAttributesMask();
|
||||
static int getDefaultAttributesSize(BaseGameVersion version);
|
||||
static void setLayout(Project*);
|
||||
static QString getMetatileIdString(uint16_t metatileId) {
|
||||
|
|
|
@ -372,7 +372,7 @@ private:
|
|||
void initMapSortOrder();
|
||||
void initShortcuts();
|
||||
void initExtraShortcuts();
|
||||
bool setProjectSpecificUI();
|
||||
void setProjectSpecificUI();
|
||||
void setWildEncountersUIEnabled(bool enabled);
|
||||
void loadUserSettings();
|
||||
void applyMapListFilter(QString filterText);
|
||||
|
|
|
@ -216,6 +216,7 @@ public:
|
|||
QString getDefaultSecondaryTilesetLabel();
|
||||
|
||||
void setImportExportPath(QString filename);
|
||||
void applyParsedLimits();
|
||||
|
||||
static int getNumTilesPrimary();
|
||||
static int getNumTilesTotal();
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <QMainWindow>
|
||||
#include "project.h"
|
||||
#include "ui_projectsettingseditor.h"
|
||||
|
||||
class NoScrollComboBox;
|
||||
class QAbstractButton;
|
||||
|
@ -55,6 +56,7 @@ private:
|
|||
void chooseFile(QLineEdit * filepathEdit, const QString &description, const QString &extensions);
|
||||
QString stripProjectDir(QString s);
|
||||
void disableParsedSetting(QWidget * widget, const QString &name, const QString &filepath);
|
||||
void updateMaskOverlapWarning(QLabel * warning, QList<UIntSpinBox*> masks);
|
||||
|
||||
private slots:
|
||||
void dialogButtonClicked(QAbstractButton *button);
|
||||
|
@ -63,6 +65,8 @@ private slots:
|
|||
void updatePokemonIconPath(const QString &species);
|
||||
void markEdited();
|
||||
void on_mainTabs_tabBarClicked(int index);
|
||||
void updateBlockMaskOverlapWarning();
|
||||
void updateAttributeMaskOverlapWarning();
|
||||
};
|
||||
|
||||
#endif // PROJECTSETTINGSEDITOR_H
|
||||
|
|
|
@ -1043,21 +1043,21 @@ uint16_t ProjectConfig::getDefaultMetatileId() {
|
|||
return this->defaultMetatileId;
|
||||
}
|
||||
|
||||
void ProjectConfig::setDefaultElevation(int elevation) {
|
||||
void ProjectConfig::setDefaultElevation(uint16_t elevation) {
|
||||
this->defaultElevation = elevation;
|
||||
this->save();
|
||||
}
|
||||
|
||||
int ProjectConfig::getDefaultElevation() {
|
||||
uint16_t ProjectConfig::getDefaultElevation() {
|
||||
return this->defaultElevation;
|
||||
}
|
||||
|
||||
void ProjectConfig::setDefaultCollision(int collision) {
|
||||
void ProjectConfig::setDefaultCollision(uint16_t collision) {
|
||||
this->defaultCollision = collision;
|
||||
this->save();
|
||||
}
|
||||
|
||||
int ProjectConfig::getDefaultCollision() {
|
||||
uint16_t ProjectConfig::getDefaultCollision() {
|
||||
return this->defaultCollision;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ void BitPacker::setMask(uint32_t mask) {
|
|||
m_maxValue = (m_setBits.length() >= 32) ? UINT_MAX : ((1 << m_setBits.length()) - 1);
|
||||
}
|
||||
|
||||
// Given an arbitrary value to set for this bitfield member, returns a bounded value that can later be packed losslessly.
|
||||
// Given an arbitrary value to set for this bitfield member, returns a (potentially truncated) value that can later be packed losslessly.
|
||||
uint32_t BitPacker::clamp(uint32_t value) const {
|
||||
return (m_maxValue == UINT_MAX) ? value : (value % (m_maxValue + 1));
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ static BitPacker bitsElevation = BitPacker(0xF000);
|
|||
Block::Block() :
|
||||
m_metatileId(0),
|
||||
m_collision(0),
|
||||
m_elevation(0)
|
||||
m_elevation(0)
|
||||
{ }
|
||||
|
||||
Block::Block(uint16_t metatileId, uint16_t collision, uint16_t elevation) :
|
||||
|
@ -46,32 +46,10 @@ uint16_t Block::rawValue() const {
|
|||
| bitsElevation.pack(m_elevation);
|
||||
}
|
||||
|
||||
// TODO: After parsing, recalc config (or parsed!) values that depend on max collision/elevation
|
||||
/* - newMapMetatileId
|
||||
- newMapElevation
|
||||
- newMapCollision
|
||||
- newMapBorderMetatileIds
|
||||
- collisionSheetWidth
|
||||
- collisionSheetHeight
|
||||
- NUM_METATILES_IN_PRIMARY
|
||||
- event elevations
|
||||
- metatile labels?
|
||||
|
||||
*/
|
||||
// TODO: Settings editor -- disable UI & restore after refresh
|
||||
void Block::setLayout() {
|
||||
bitsMetatileId.setMask(projectConfig.getBlockMetatileIdMask());
|
||||
bitsCollision.setMask(projectConfig.getBlockCollisionMask());
|
||||
bitsElevation.setMask(projectConfig.getBlockElevationMask());
|
||||
|
||||
// Some settings may need to be reevaluated based on the layout
|
||||
/*uint16_t metatileId = projectConfig.getNewMapMetatileId();
|
||||
if (bitsMetatileId.clamp(metatileId) != metatileId)
|
||||
projectConfig.setNewMapMetatileId(bitsMetatileId.clamp(metatileId));
|
||||
uint16_t metatileId = projectConfig.getNewMapMetatileId();
|
||||
if (bitsMetatileId.clamp(metatileId) != metatileId)
|
||||
projectConfig.setNewMapMetatileId(bitsMetatileId.clamp(metatileId));*/
|
||||
|
||||
}
|
||||
|
||||
bool Block::operator ==(Block other) const {
|
||||
|
|
|
@ -82,73 +82,66 @@ uint32_t Metatile::getDefaultAttributesMask(BaseGameVersion version, Metatile::A
|
|||
return vanillaPackers.value(attr).mask();
|
||||
}
|
||||
|
||||
bool Metatile::doMasksOverlap(QList<uint32_t> 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::setLayout(Project * project) {
|
||||
// Read masks from the config and limit them based on the specified attribute size.
|
||||
const QHash<int, uint32_t> maxMasks = {
|
||||
uint32_t Metatile::getMaxAttributesMask() {
|
||||
static const QHash<int, uint32_t> maxMasks = {
|
||||
{1, 0xFF},
|
||||
{2, 0xFFFF},
|
||||
{4, 0xFFFFFFFF},
|
||||
};
|
||||
uint32_t maxMask = maxMasks.value(projectConfig.getMetatileAttributesSize(), 0);
|
||||
uint32_t behaviorMask = projectConfig.getMetatileBehaviorMask() & maxMask;
|
||||
uint32_t terrainTypeMask = projectConfig.getMetatileTerrainTypeMask() & maxMask;
|
||||
uint32_t encounterTypeMask = projectConfig.getMetatileEncounterTypeMask() & maxMask;
|
||||
uint32_t layerTypeMask = projectConfig.getMetatileLayerTypeMask() & maxMask;
|
||||
|
||||
// TODO: Overlap handling to settings editor; set red text box with similar text warning if overlapping
|
||||
// Overlapping masks are technically ok, but probably not intended.
|
||||
// Additionally, Porymap will not properly reflect that the values are linked.
|
||||
if (doMasksOverlap({behaviorMask, terrainTypeMask, encounterTypeMask, layerTypeMask})) {
|
||||
logWarn("Metatile attribute masks are overlapping. This may result in unexpected attribute values.");
|
||||
}
|
||||
return maxMasks.value(projectConfig.getMetatileAttributesSize(), 0);
|
||||
}
|
||||
|
||||
void Metatile::setLayout(Project * project) {
|
||||
uint32_t behaviorMask = projectConfig.getMetatileBehaviorMask();
|
||||
uint32_t terrainTypeMask = projectConfig.getMetatileTerrainTypeMask();
|
||||
uint32_t encounterTypeMask = projectConfig.getMetatileEncounterTypeMask();
|
||||
uint32_t layerTypeMask = projectConfig.getMetatileLayerTypeMask();
|
||||
|
||||
// Calculate mask of bits not used by standard behaviors so we can preserve this data.
|
||||
uint32_t unusedMask = ~(behaviorMask | terrainTypeMask | encounterTypeMask | layerTypeMask);
|
||||
unusedMask &= maxMask;
|
||||
unusedMask &= Metatile::getMaxAttributesMask();
|
||||
|
||||
BitPacker packer = BitPacker(unusedMask);
|
||||
attributePackers.clear();
|
||||
attributePackers.insert(Metatile::Attr::Unused, unusedMask);
|
||||
|
||||
// TODO: Test displaying 32 bit behavior
|
||||
// TODO: Logging masks to hex
|
||||
// Validate metatile behavior mask
|
||||
packer.setMask(behaviorMask);
|
||||
if (behaviorMask && !project->metatileBehaviorMapInverse.isEmpty()) {
|
||||
uint32_t maxBehavior = project->metatileBehaviorMapInverse.lastKey();
|
||||
if (packer.clamp(maxBehavior) != maxBehavior)
|
||||
logWarn(QString("Metatile Behavior mask '%1' is insufficient to contain all available options.").arg(behaviorMask));
|
||||
logWarn(QString("Metatile Behavior mask '0x%1' is insufficient to contain all available options.")
|
||||
.arg(QString::number(behaviorMask, 16).toUpper()));
|
||||
}
|
||||
attributePackers.insert(Metatile::Attr::Behavior, packer);
|
||||
|
||||
// Validate terrain type mask
|
||||
packer.setMask(terrainTypeMask);
|
||||
const uint32_t maxTerrainType = NUM_METATILE_TERRAIN_TYPES - 1;
|
||||
if (terrainTypeMask && packer.clamp(maxTerrainType) != maxTerrainType)
|
||||
logWarn(QString("Metatile Terrain Type mask '%1' is insufficient to contain all %2 available options.").arg(terrainTypeMask).arg(maxTerrainType + 1));
|
||||
if (terrainTypeMask && packer.clamp(maxTerrainType) != maxTerrainType) {
|
||||
logWarn(QString("Metatile Terrain Type mask '0x%1' is insufficient to contain all %2 available options.")
|
||||
.arg(QString::number(terrainTypeMask, 16).toUpper())
|
||||
.arg(maxTerrainType + 1));
|
||||
}
|
||||
attributePackers.insert(Metatile::Attr::TerrainType, packer);
|
||||
|
||||
// Validate encounter type mask
|
||||
packer.setMask(encounterTypeMask);
|
||||
const uint32_t maxEncounterType = NUM_METATILE_ENCOUNTER_TYPES - 1;
|
||||
if (encounterTypeMask && packer.clamp(maxEncounterType) != maxEncounterType)
|
||||
logWarn(QString("Metatile Encounter Type mask '%1' is insufficient to contain all %2 available options.").arg(encounterTypeMask).arg(maxEncounterType + 1));
|
||||
if (encounterTypeMask && packer.clamp(maxEncounterType) != maxEncounterType) {
|
||||
logWarn(QString("Metatile Encounter Type mask '0x%1' is insufficient to contain all %2 available options.")
|
||||
.arg(QString::number(encounterTypeMask, 16).toUpper())
|
||||
.arg(maxEncounterType + 1));
|
||||
}
|
||||
attributePackers.insert(Metatile::Attr::EncounterType, packer);
|
||||
|
||||
// Validate terrain type mask
|
||||
packer.setMask(layerTypeMask);
|
||||
const uint32_t maxLayerType = NUM_METATILE_LAYER_TYPES - 1;
|
||||
if (layerTypeMask && packer.clamp(maxLayerType) != maxLayerType)
|
||||
logWarn(QString("Metatile Layer Type mask '%1' is insufficient to contain all %2 available options.").arg(layerTypeMask).arg(maxLayerType + 1));
|
||||
if (layerTypeMask && packer.clamp(maxLayerType) != maxLayerType) {
|
||||
logWarn(QString("Metatile Layer Type mask '0x%1' is insufficient to contain all %2 available options.")
|
||||
.arg(QString::number(layerTypeMask, 16).toUpper())
|
||||
.arg(maxLayerType + 1));
|
||||
}
|
||||
attributePackers.insert(Metatile::Attr::LayerType, packer);
|
||||
}
|
||||
|
|
|
@ -373,7 +373,7 @@ void MainWindow::setWildEncountersUIEnabled(bool enabled) {
|
|||
}
|
||||
|
||||
// Update the UI using information we've read from the user's project files.
|
||||
bool MainWindow::setProjectSpecificUI()
|
||||
void MainWindow::setProjectSpecificUI()
|
||||
{
|
||||
this->setWildEncountersUIEnabled(userConfig.getEncounterJsonActive());
|
||||
|
||||
|
@ -397,7 +397,6 @@ bool MainWindow::setProjectSpecificUI()
|
|||
editor->setCollisionGraphics();
|
||||
ui->spinBox_SelectedElevation->setMaximum(Block::getMaxElevation());
|
||||
ui->spinBox_SelectedCollision->setMaximum(Block::getMaxCollision());
|
||||
return true;
|
||||
}
|
||||
|
||||
void MainWindow::mapSortOrder_changed(QAction *action)
|
||||
|
@ -543,7 +542,6 @@ bool MainWindow::openProject(QString dir) {
|
|||
}
|
||||
|
||||
this->projectOpenFailure = !(loadDataStructures()
|
||||
&& setProjectSpecificUI()
|
||||
&& populateMapList()
|
||||
&& setInitialMap());
|
||||
|
||||
|
@ -948,8 +946,8 @@ bool MainWindow::loadDataStructures() {
|
|||
&& project->readEventGraphics()
|
||||
&& project->readSongNames();
|
||||
|
||||
Block::setLayout();
|
||||
Metatile::setLayout(project);
|
||||
project->applyParsedLimits();
|
||||
setProjectSpecificUI();
|
||||
Scripting::populateGlobalObject(this);
|
||||
|
||||
return success && loadProjectCombos();
|
||||
|
|
|
@ -27,7 +27,7 @@ using OrderedJsonDoc = poryjson::JsonDoc;
|
|||
|
||||
int Project::num_tiles_primary = 512;
|
||||
int Project::num_tiles_total = 1024;
|
||||
int Project::num_metatiles_primary = 512; // TODO: Verify fits within max
|
||||
int Project::num_metatiles_primary = 512;
|
||||
int Project::num_pals_primary = 6;
|
||||
int Project::num_pals_total = 13;
|
||||
int Project::max_map_data_size = 10240; // 0x2800
|
||||
|
@ -1865,7 +1865,7 @@ bool Project::readTilesetLabels() {
|
|||
}
|
||||
|
||||
bool Project::readTilesetProperties() {
|
||||
static const QStringList definePrefixes{ "\\bNUM_" };
|
||||
QStringList definePrefixes{ "\\bNUM_" };
|
||||
QString filename = projectConfig.getFilePath(ProjectFilePath::constants_fieldmap);
|
||||
fileWatcher.addPath(root + "/" + filename);
|
||||
QMap<QString, int> defines = parser.readCDefines(filename, definePrefixes);
|
||||
|
@ -1916,7 +1916,7 @@ bool Project::readTilesetProperties() {
|
|||
// Read data masks for Blocks and metatile attributes.
|
||||
bool Project::readFieldmapMasks() {
|
||||
// We're looking for the suffix "_MASK". Technically our "prefix" is the whole define.
|
||||
static const QStringList definePrefixes{ "\\b\\w+_MASK" };
|
||||
QStringList definePrefixes{ "\\b\\w+_MASK" };
|
||||
QString globalFieldmap = projectConfig.getFilePath(ProjectFilePath::global_fieldmap);
|
||||
fileWatcher.addPath(root + "/" + globalFieldmap);
|
||||
QMap<QString, int> defines = parser.readCDefines(globalFieldmap, definePrefixes);
|
||||
|
@ -1990,7 +1990,7 @@ bool Project::readFieldmapMasks() {
|
|||
}
|
||||
|
||||
bool Project::readMaxMapDataSize() {
|
||||
static const QStringList definePrefixes{ "\\bMAX_" };
|
||||
QStringList definePrefixes{ "\\bMAX_" };
|
||||
QString filename = projectConfig.getFilePath(ProjectFilePath::constants_fieldmap); // already in fileWatcher from readTilesetProperties
|
||||
QMap<QString, int> defines = parser.readCDefines(filename, definePrefixes);
|
||||
|
||||
|
@ -2333,7 +2333,7 @@ bool Project::readMiscellaneousConstants() {
|
|||
|
||||
QString filename = projectConfig.getFilePath(ProjectFilePath::constants_global);
|
||||
fileWatcher.addPath(root + "/" + filename);
|
||||
static const QStringList definePrefixes("\\bOBJECT_");
|
||||
QStringList definePrefixes("\\bOBJECT_");
|
||||
QMap<QString, int> defines = parser.readCDefines(filename, definePrefixes);
|
||||
|
||||
auto it = defines.find("OBJECT_EVENT_TEMPLATES_COUNT");
|
||||
|
@ -2705,3 +2705,32 @@ void Project::setImportExportPath(QString filename)
|
|||
{
|
||||
this->importExportPath = QFileInfo(filename).absolutePath();
|
||||
}
|
||||
|
||||
// The values of some config fields can limit the values of other config fields
|
||||
// (for example, metatile attributes size limits the metatile attribute masks).
|
||||
// Others depend on information in the project (for example the default metatile ID
|
||||
// can be limited by fieldmap defines)
|
||||
// Once we've read data from the project files we can adjust these accordingly.
|
||||
void Project::applyParsedLimits() {
|
||||
// Avoid repeatedly writing the config file
|
||||
projectConfig.setSaveDisabled(true);
|
||||
|
||||
uint32_t maxMask = Metatile::getMaxAttributesMask();
|
||||
projectConfig.setMetatileBehaviorMask(projectConfig.getMetatileBehaviorMask() & maxMask);
|
||||
projectConfig.setMetatileTerrainTypeMask(projectConfig.getMetatileTerrainTypeMask() & maxMask);
|
||||
projectConfig.setMetatileEncounterTypeMask(projectConfig.getMetatileEncounterTypeMask() & maxMask);
|
||||
projectConfig.setMetatileLayerTypeMask(projectConfig.getMetatileLayerTypeMask() & maxMask);
|
||||
|
||||
Block::setLayout();
|
||||
Metatile::setLayout(this);
|
||||
|
||||
Project::num_metatiles_primary = qMin(Project::num_metatiles_primary, Block::getMaxMetatileId() + 1);
|
||||
projectConfig.setDefaultMetatileId(qMin(projectConfig.getDefaultMetatileId(), Block::getMaxMetatileId()));
|
||||
projectConfig.setDefaultElevation(qMin(projectConfig.getDefaultElevation(), Block::getMaxElevation()));
|
||||
projectConfig.setDefaultCollision(qMin(projectConfig.getDefaultCollision(), Block::getMaxCollision()));
|
||||
projectConfig.setCollisionSheetHeight(qMin(projectConfig.getCollisionSheetHeight(), Block::getMaxElevation() + 1));
|
||||
projectConfig.setCollisionSheetWidth(qMin(projectConfig.getCollisionSheetWidth(), Block::getMaxCollision() + 1));
|
||||
|
||||
projectConfig.setSaveDisabled(false);
|
||||
projectConfig.save();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#include "projectsettingseditor.h"
|
||||
#include "ui_projectsettingseditor.h"
|
||||
#include "config.h"
|
||||
#include "noscrollcombobox.h"
|
||||
#include "prefab.h"
|
||||
|
@ -55,6 +54,15 @@ void ProjectSettingsEditor::connectSignals() {
|
|||
connect(ui->button_HealspotsIcon, &QAbstractButton::clicked, [this](bool) { this->chooseImageFile(ui->lineEdit_HealspotsIcon); });
|
||||
connect(ui->button_PokemonIcon, &QAbstractButton::clicked, [this](bool) { this->chooseImageFile(ui->lineEdit_PokemonIcon); });
|
||||
|
||||
// Display a warning if a mask value overlaps with another mask in its group.
|
||||
connect(ui->spinBox_MetatileIdMask, &UIntSpinBox::textChanged, this, &ProjectSettingsEditor::updateBlockMaskOverlapWarning);
|
||||
connect(ui->spinBox_CollisionMask, &UIntSpinBox::textChanged, this, &ProjectSettingsEditor::updateBlockMaskOverlapWarning);
|
||||
connect(ui->spinBox_ElevationMask, &UIntSpinBox::textChanged, this, &ProjectSettingsEditor::updateBlockMaskOverlapWarning);
|
||||
connect(ui->spinBox_BehaviorMask, &UIntSpinBox::textChanged, this, &ProjectSettingsEditor::updateAttributeMaskOverlapWarning);
|
||||
connect(ui->spinBox_LayerTypeMask, &UIntSpinBox::textChanged, this, &ProjectSettingsEditor::updateAttributeMaskOverlapWarning);
|
||||
connect(ui->spinBox_EncounterTypeMask, &UIntSpinBox::textChanged, this, &ProjectSettingsEditor::updateAttributeMaskOverlapWarning);
|
||||
connect(ui->spinBox_TerrainTypeMask, &UIntSpinBox::textChanged, this, &ProjectSettingsEditor::updateAttributeMaskOverlapWarning);
|
||||
|
||||
// Record that there are unsaved changes if any of the settings are modified
|
||||
for (auto combo : ui->centralwidget->findChildren<NoScrollComboBox *>()){
|
||||
if (combo != ui->comboBox_IconSpecies) // Changes to the icon species combo box are just for info display, don't mark as unsaved
|
||||
|
@ -112,7 +120,6 @@ void ProjectSettingsEditor::initUi() {
|
|||
ui->spinBox_Collision->setMaximum(Block::getMaxCollision());
|
||||
ui->spinBox_MaxElevation->setMaximum(Block::getMaxElevation());
|
||||
ui->spinBox_MaxCollision->setMaximum(Block::getMaxCollision());
|
||||
//ui->spinBox_MetatileIdMask->setMinimum(0x1);
|
||||
ui->spinBox_MetatileIdMask->setMaximum(Block::maxValue);
|
||||
ui->spinBox_CollisionMask->setMaximum(Block::maxValue);
|
||||
ui->spinBox_ElevationMask->setMaximum(Block::maxValue);
|
||||
|
@ -189,6 +196,47 @@ QList<uint16_t> ProjectSettingsEditor::getBorderMetatileIds(bool customSize) {
|
|||
return metatileIds;
|
||||
}
|
||||
|
||||
// Show/hide warning for overlapping mask values. These are technically ok, but probably not intended.
|
||||
// Additionally, Porymap will not properly reflect that the values are linked.
|
||||
void ProjectSettingsEditor::updateMaskOverlapWarning(QLabel * warning, QList<UIntSpinBox*> masks) {
|
||||
// Find any overlapping masks
|
||||
QMap<int, bool> overlapping;
|
||||
for (int i = 0; i < masks.length(); i++)
|
||||
for (int j = i + 1; j < masks.length(); j++) {
|
||||
if (masks.at(i)->value() & masks.at(j)->value())
|
||||
overlapping[i] = overlapping[j] = true;
|
||||
}
|
||||
|
||||
// It'de nice if we could style this as a persistent red border around the line edit for any
|
||||
// overlapping masks. As it is editing the border undesirably modifies the arrow buttons.
|
||||
// This stylesheet will just highlight the currently selected line edit, which is fine enough.
|
||||
static const QString styleSheet = "QAbstractSpinBox { selection-background-color: rgba(255, 0, 0, 25%) }";
|
||||
|
||||
// Update warning display
|
||||
if (warning) warning->setHidden(overlapping.isEmpty());
|
||||
for (int i = 0; i < masks.length(); i++)
|
||||
masks.at(i)->setStyleSheet(overlapping.contains(i) ? styleSheet : "");
|
||||
}
|
||||
|
||||
void ProjectSettingsEditor::updateBlockMaskOverlapWarning() {
|
||||
const auto masks = QList<UIntSpinBox*>{
|
||||
ui->spinBox_MetatileIdMask,
|
||||
ui->spinBox_CollisionMask,
|
||||
ui->spinBox_ElevationMask,
|
||||
};
|
||||
this->updateMaskOverlapWarning(ui->label_OverlapWarningBlocks, masks);
|
||||
}
|
||||
|
||||
void ProjectSettingsEditor::updateAttributeMaskOverlapWarning() {
|
||||
const auto masks = QList<UIntSpinBox*>{
|
||||
ui->spinBox_BehaviorMask,
|
||||
ui->spinBox_LayerTypeMask,
|
||||
ui->spinBox_EncounterTypeMask,
|
||||
ui->spinBox_TerrainTypeMask,
|
||||
};
|
||||
this->updateMaskOverlapWarning(ui->label_OverlapWarningMetatiles, masks);
|
||||
}
|
||||
|
||||
void ProjectSettingsEditor::updateAttributeLimits(const QString &attrSize) {
|
||||
static const QMap<QString, uint32_t> limits {
|
||||
{"1", 0xFF},
|
||||
|
|
|
@ -113,7 +113,7 @@ void TilesetEditor::initUi() {
|
|||
|
||||
void TilesetEditor::setAttributesUi() {
|
||||
// Behavior
|
||||
if (projectConfig.getMetatileBehaviorMask() != 0) {
|
||||
if (projectConfig.getMetatileBehaviorMask()) {
|
||||
for (int num : project->metatileBehaviorMapInverse.keys()) {
|
||||
this->ui->comboBox_metatileBehaviors->addItem(project->metatileBehaviorMapInverse[num], num);
|
||||
}
|
||||
|
|
|
@ -104,29 +104,37 @@ void UIntSpinBox::onEditFinished() {
|
|||
QString input = this->lineEdit()->text();
|
||||
|
||||
auto state = this->validate(input, pos);
|
||||
if (state == QValidator::Invalid)
|
||||
return;
|
||||
|
||||
auto newValue = m_value;
|
||||
if (state == QValidator::Acceptable) {
|
||||
// Valid input
|
||||
m_value = this->valueFromText(input);
|
||||
newValue = this->valueFromText(input);
|
||||
} else if (state == QValidator::Intermediate) {
|
||||
// User has deleted all the number text.
|
||||
// If they did this by selecting all text and then hitting delete
|
||||
// make sure to put the cursor back in front of the prefix.
|
||||
m_value = m_minimum;
|
||||
newValue = m_minimum;
|
||||
this->lineEdit()->setCursorPosition(m_prefix.length());
|
||||
}
|
||||
if (newValue != m_value) {
|
||||
m_value = newValue;
|
||||
emit this->valueChanged(m_value);
|
||||
}
|
||||
emit this->textChanged(input);
|
||||
}
|
||||
|
||||
void UIntSpinBox::stepBy(int steps)
|
||||
{
|
||||
auto new_value = m_value;
|
||||
if (steps < 0 && new_value + steps > new_value) {
|
||||
new_value = 0;
|
||||
} else if (steps > 0 && new_value + steps < new_value) {
|
||||
new_value = UINT_MAX;
|
||||
void UIntSpinBox::stepBy(int steps) {
|
||||
auto newValue = m_value;
|
||||
if (steps < 0 && newValue + steps > newValue) {
|
||||
newValue = 0;
|
||||
} else if (steps > 0 && newValue + steps < newValue) {
|
||||
newValue = UINT_MAX;
|
||||
} else {
|
||||
new_value += steps;
|
||||
newValue += steps;
|
||||
}
|
||||
this->setValue(new_value);
|
||||
this->setValue(newValue);
|
||||
}
|
||||
|
||||
QString UIntSpinBox::stripped(QString input) const {
|
||||
|
@ -144,8 +152,9 @@ QValidator::State UIntSpinBox::validate(QString &input, int &pos) const {
|
|||
if (copy.isEmpty())
|
||||
return QValidator::Intermediate;
|
||||
|
||||
// Editing the prefix (if not deleting all text) is not allowed
|
||||
if (pos < m_prefix.length())
|
||||
// Editing the prefix (if not deleting all text) is not allowed.
|
||||
// Nor is editing beyond the maximum value's character limit.
|
||||
if (pos < m_prefix.length() || pos > (m_numChars + m_prefix.length()))
|
||||
return QValidator::Invalid;
|
||||
|
||||
bool ok;
|
||||
|
|
Loading…
Reference in a new issue