mirror of
https://github.com/huderlem/porymap.git
synced 2026-07-18 16:32:15 -05:00
Redesign new tileset dialog
This commit is contained in:
parent
b7c34a67e5
commit
9c40b04ad5
|
|
@ -2928,6 +2928,7 @@
|
|||
<addaction name="action_NewMap"/>
|
||||
<addaction name="action_NewLayout"/>
|
||||
<addaction name="actionNew_Tileset"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionTileset_Editor"/>
|
||||
<addaction name="actionRegion_Map_Editor"/>
|
||||
<addaction name="separator"/>
|
||||
|
|
|
|||
|
|
@ -6,238 +6,152 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>216</height>
|
||||
<width>450</width>
|
||||
<height>209</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Add new Tileset</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="gridWidget" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>216</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="verticalSpacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="4" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QFrame" name="frame">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>380</width>
|
||||
<height>161</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<widget class="QWidget" name="formLayoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>380</width>
|
||||
<height>155</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="horizontalSpacing">
|
||||
<number>6</number>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QFrame" name="form">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Shape::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Shadow::Raised</enum>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_FriendlyName">
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>10</number>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="lineEdit_FriendlyName">
|
||||
<property name="clearButtonEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>10</number>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="label_NameError">
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>10</number>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color: rgb(255, 0, 0)</string>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="nameLabel">
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_SymbolName">
|
||||
<property name="text">
|
||||
<string>Symbol Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="label_SymbolNameDisplay">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_Type">
|
||||
<property name="text">
|
||||
<string>Type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="NoScrollComboBox" name="comboBox_Type">
|
||||
<property name="editable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Primary</string>
|
||||
</property>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="nameLineEdit">
|
||||
<property name="clearButtonEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Secondary</string>
|
||||
</property>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="typeLabel">
|
||||
<property name="text">
|
||||
<string>Type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="typeComboBox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Primary</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Secondary</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="pathLabel">
|
||||
<property name="text">
|
||||
<string>Path</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="pathLineEdit">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="clearButtonEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="symbolNameLabel">
|
||||
<property name="text">
|
||||
<string>Symbol Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLineEdit" name="symbolNameLineEdit">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="clearButtonEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="fillLabel">
|
||||
<property name="text">
|
||||
<string>Checkerboard Fill</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QCheckBox" name="fillCheckBox">
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
<property name="centerButtons">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_CheckerboardFill">
|
||||
<property name="text">
|
||||
<string>Checkerboard Fill</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QCheckBox" name="checkBox_CheckerboardFill"/>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>1</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_GenericError">
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color: rgb(255, 0, 0)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
|
||||
</property>
|
||||
<property name="centerButtons">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>NoScrollComboBox</class>
|
||||
<extends>QComboBox</extends>
|
||||
<header>noscrollcombobox.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>NewTilesetDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>NewTilesetDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
|
|
|||
|
|
@ -38,8 +38,6 @@ public:
|
|||
QList<QList<QRgb>> palettes;
|
||||
QList<QList<QRgb>> palettePreviews;
|
||||
|
||||
bool hasUnsavedTilesImage;
|
||||
|
||||
static Tileset* getMetatileTileset(int, Tileset*, Tileset*);
|
||||
static Tileset* getTileTileset(int, Tileset*, Tileset*);
|
||||
static Metatile* getMetatile(int, Tileset*, Tileset*);
|
||||
|
|
@ -56,10 +54,25 @@ public:
|
|||
static QHash<int, QString> getHeaderMemberMap(bool usingAsm);
|
||||
static QString getExpectedDir(QString tilesetName, bool isSecondary);
|
||||
QString getExpectedDir();
|
||||
|
||||
void load();
|
||||
void loadMetatiles();
|
||||
void loadMetatileAttributes();
|
||||
void loadTilesImage(QImage *importedImage = nullptr);
|
||||
void loadPalettes();
|
||||
|
||||
void save();
|
||||
void saveMetatileAttributes();
|
||||
void saveMetatiles();
|
||||
void saveTilesImage();
|
||||
void savePalettes();
|
||||
|
||||
bool appendToHeaders(QString root, QString friendlyName, bool usingAsm);
|
||||
bool appendToGraphics(QString root, QString friendlyName, bool usingAsm);
|
||||
bool appendToMetatiles(QString root, QString friendlyName, bool usingAsm);
|
||||
|
||||
void setTilesImage(const QImage &image);
|
||||
|
||||
void setMetatiles(const QList<Metatile*> &metatiles);
|
||||
void addMetatile(Metatile* metatile);
|
||||
|
||||
|
|
@ -72,6 +85,7 @@ public:
|
|||
|
||||
private:
|
||||
QList<Metatile*> m_metatiles;
|
||||
bool m_hasUnsavedTilesImage = false;
|
||||
};
|
||||
|
||||
#endif // TILESET_H
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
#include "mapimageexporter.h"
|
||||
#include "filterchildrenproxymodel.h"
|
||||
#include "maplistmodels.h"
|
||||
#include "newtilesetdialog.h"
|
||||
#include "shortcutseditor.h"
|
||||
#include "preferenceeditor.h"
|
||||
#include "projectsettingseditor.h"
|
||||
|
|
@ -191,6 +190,7 @@ private slots:
|
|||
void onNewMapGroupCreated(const QString &groupName);
|
||||
void onNewMapSectionCreated(const QString &idName);
|
||||
void onNewLayoutCreated(Layout *layout);
|
||||
void onNewTilesetCreated(Tileset *tileset);
|
||||
void onMapLoaded(Map *map);
|
||||
void onMapRulerStatusChanged(const QString &);
|
||||
void applyUserShortcuts();
|
||||
|
|
@ -412,7 +412,6 @@ private:
|
|||
|
||||
QObjectList shortcutableObjects() const;
|
||||
void addCustomHeaderValue(QString key, QJsonValue value, bool isNew = false);
|
||||
int insertTilesetLabel(QStringList * list, QString label);
|
||||
|
||||
void checkForUpdates(bool requestedByUser);
|
||||
void setDivingMapsVisible(bool visible);
|
||||
|
|
|
|||
|
|
@ -139,6 +139,7 @@ public:
|
|||
|
||||
Map *createNewMap(const Project::NewMapSettings &mapSettings, const Map* toDuplicate = nullptr);
|
||||
Layout *createNewLayout(const Layout::Settings &layoutSettings, const Layout* toDuplicate = nullptr);
|
||||
Tileset *createNewTileset(const QString &friendlyName, bool secondary, bool checkerboardFill);
|
||||
bool isIdentifierUnique(const QString &identifier) const;
|
||||
QString getProjectTitle();
|
||||
|
||||
|
|
@ -167,10 +168,7 @@ public:
|
|||
bool loadMapLayout(Map*);
|
||||
bool loadLayoutTilesets(Layout *);
|
||||
void loadTilesetAssets(Tileset*);
|
||||
void loadTilesetTiles(Tileset*, QImage);
|
||||
void loadTilesetMetatiles(Tileset*);
|
||||
void loadTilesetMetatileLabels(Tileset*);
|
||||
void loadTilesetPalettes(Tileset*);
|
||||
void readTilesetPaths(Tileset* tileset);
|
||||
|
||||
void saveLayout(Layout *);
|
||||
|
|
@ -188,10 +186,6 @@ public:
|
|||
void saveHealLocations(Map*);
|
||||
void saveTilesets(Tileset*, Tileset*);
|
||||
void saveTilesetMetatileLabels(Tileset*, Tileset*);
|
||||
void saveTilesetMetatileAttributes(Tileset*);
|
||||
void saveTilesetMetatiles(Tileset*);
|
||||
void saveTilesetTilesImage(Tileset*);
|
||||
void saveTilesetPalettes(Tileset*);
|
||||
void appendTilesetLabel(const QString &label, const QString &isSecondaryStr);
|
||||
bool readTilesetLabels();
|
||||
bool readTilesetMetatileLabels();
|
||||
|
|
@ -282,6 +276,7 @@ signals:
|
|||
void mapLoaded(Map *map);
|
||||
void mapCreated(Map *newMap, const QString &groupName);
|
||||
void layoutCreated(Layout *newLayout);
|
||||
void tilesetCreated(Tileset *newTileset);
|
||||
void mapGroupAdded(const QString &groupName);
|
||||
void mapSectionAdded(const QString &idName);
|
||||
void mapSectionIdNamesChanged(const QStringList &idNames);
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ private:
|
|||
|
||||
void refresh();
|
||||
void saveSettings();
|
||||
bool isExistingLayout() const;
|
||||
|
||||
private slots:
|
||||
void dialogButtonClicked(QAbstractButton *button);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@
|
|||
#define NEWTILESETDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include "project.h"
|
||||
#include <QAbstractButton>
|
||||
|
||||
class Project;
|
||||
class Tileset;
|
||||
|
||||
namespace Ui {
|
||||
class NewTilesetDialog;
|
||||
|
|
@ -15,20 +18,17 @@ class NewTilesetDialog : public QDialog
|
|||
public:
|
||||
explicit NewTilesetDialog(Project *project, QWidget *parent = nullptr);
|
||||
~NewTilesetDialog();
|
||||
QString path;
|
||||
QString fullSymbolName;
|
||||
QString friendlyName;
|
||||
bool isSecondary;
|
||||
bool checkerboardFill;
|
||||
|
||||
private slots:
|
||||
void NameOrSecondaryChanged();
|
||||
void SecondaryChanged();
|
||||
void FillChanged();
|
||||
virtual void accept() override;
|
||||
|
||||
private:
|
||||
Ui::NewTilesetDialog *ui;
|
||||
Project *project = nullptr;
|
||||
const QString symbolPrefix;
|
||||
|
||||
bool validateName(bool allowEmpty = false);
|
||||
void onFriendlyNameChanged(const QString &friendlyName);
|
||||
void dialogButtonClicked(QAbstractButton *button);
|
||||
};
|
||||
|
||||
#endif // NEWTILESETDIALOG_H
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include "project.h"
|
||||
#include "log.h"
|
||||
#include "config.h"
|
||||
#include "imageproviders.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QImage>
|
||||
|
|
@ -23,7 +24,7 @@ Tileset::Tileset(const Tileset &other)
|
|||
metatileLabels(other.metatileLabels),
|
||||
palettes(other.palettes),
|
||||
palettePreviews(other.palettePreviews),
|
||||
hasUnsavedTilesImage(false)
|
||||
m_hasUnsavedTilesImage(other.m_hasUnsavedTilesImage)
|
||||
{
|
||||
for (auto tile : other.tiles) {
|
||||
tiles.append(tile.copy());
|
||||
|
|
@ -397,3 +398,184 @@ QHash<int, QString> Tileset::getHeaderMemberMap(bool usingAsm)
|
|||
map.insert(metatileAttrPosition, "metatileAttributes");
|
||||
return map;
|
||||
}
|
||||
|
||||
void Tileset::loadMetatiles() {
|
||||
clearMetatiles();
|
||||
|
||||
QFile metatiles_file(this->metatiles_path);
|
||||
if (!metatiles_file.open(QIODevice::ReadOnly)) {
|
||||
logError(QString("Could not open '%1' for reading.").arg(this->metatiles_path));
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray data = metatiles_file.readAll();
|
||||
int tilesPerMetatile = projectConfig.getNumTilesInMetatile();
|
||||
int bytesPerMetatile = 2 * tilesPerMetatile;
|
||||
int num_metatiles = data.length() / bytesPerMetatile;
|
||||
for (int i = 0; i < num_metatiles; i++) {
|
||||
auto metatile = new Metatile;
|
||||
int index = i * bytesPerMetatile;
|
||||
for (int j = 0; j < tilesPerMetatile; j++) {
|
||||
uint16_t tileRaw = static_cast<unsigned char>(data[index++]);
|
||||
tileRaw |= static_cast<unsigned char>(data[index++]) << 8;
|
||||
metatile->tiles.append(Tile(tileRaw));
|
||||
}
|
||||
m_metatiles.append(metatile);
|
||||
}
|
||||
}
|
||||
|
||||
void Tileset::saveMetatiles() {
|
||||
QFile metatiles_file(this->metatiles_path);
|
||||
if (!metatiles_file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
||||
logError(QString("Could not open '%1' for writing.").arg(this->metatiles_path));
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray data;
|
||||
int numTiles = projectConfig.getNumTilesInMetatile();
|
||||
for (const auto &metatile : m_metatiles) {
|
||||
for (int i = 0; i < numTiles; i++) {
|
||||
uint16_t tile = metatile->tiles.at(i).rawValue();
|
||||
data.append(static_cast<char>(tile));
|
||||
data.append(static_cast<char>(tile >> 8));
|
||||
}
|
||||
}
|
||||
metatiles_file.write(data);
|
||||
}
|
||||
|
||||
void Tileset::loadMetatileAttributes() {
|
||||
QFile attrs_file(this->metatile_attrs_path);
|
||||
if (!attrs_file.open(QIODevice::ReadOnly)) {
|
||||
logError(QString("Could not open '%1' for reading.").arg(this->metatile_attrs_path));
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray data = attrs_file.readAll();
|
||||
int attrSize = projectConfig.metatileAttributesSize;
|
||||
int numMetatiles = m_metatiles.length();
|
||||
int numMetatileAttrs = data.length() / attrSize;
|
||||
if (numMetatiles != numMetatileAttrs) {
|
||||
logWarn(QString("Metatile count %1 does not match metatile attribute count %2 in %3").arg(numMetatiles).arg(numMetatileAttrs).arg(this->name));
|
||||
}
|
||||
|
||||
for (int i = 0; i < qMin(numMetatiles, numMetatileAttrs); i++) {
|
||||
uint32_t attributes = 0;
|
||||
for (int j = 0; j < attrSize; j++)
|
||||
attributes |= static_cast<unsigned char>(data.at(i * attrSize + j)) << (8 * j);
|
||||
m_metatiles.at(i)->setAttributes(attributes);
|
||||
}
|
||||
}
|
||||
|
||||
void Tileset::saveMetatileAttributes() {
|
||||
QFile attrs_file(this->metatile_attrs_path);
|
||||
if (!attrs_file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
||||
logError(QString("Could not open '%1' for writing.").arg(this->metatile_attrs_path));
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray data;
|
||||
for (const auto &metatile : m_metatiles) {
|
||||
uint32_t attributes = metatile->getAttributes();
|
||||
for (int i = 0; i < projectConfig.metatileAttributesSize; i++)
|
||||
data.append(static_cast<char>(attributes >> (8 * i)));
|
||||
}
|
||||
attrs_file.write(data);
|
||||
}
|
||||
|
||||
void Tileset::loadTilesImage(QImage *importedImage) {
|
||||
QImage image;
|
||||
if (importedImage) {
|
||||
image = *importedImage;
|
||||
m_hasUnsavedTilesImage = true;
|
||||
} else if (QFile::exists(this->tilesImagePath)) {
|
||||
// No image provided, load from file path.
|
||||
image = QImage(this->tilesImagePath).convertToFormat(QImage::Format_Indexed8, Qt::ThresholdDither);
|
||||
} else {
|
||||
// Use default image
|
||||
image = QImage(8, 8, QImage::Format_Indexed8);
|
||||
}
|
||||
|
||||
// Validate image contains 16 colors.
|
||||
int colorCount = image.colorCount();
|
||||
if (colorCount > 16) {
|
||||
flattenTo4bppImage(&image);
|
||||
} else if (colorCount < 16) {
|
||||
QVector<QRgb> colorTable = image.colorTable();
|
||||
for (int i = colorTable.length(); i < 16; i++) {
|
||||
colorTable.append(Qt::black);
|
||||
}
|
||||
image.setColorTable(colorTable);
|
||||
}
|
||||
|
||||
QList<QImage> tiles;
|
||||
int w = 8;
|
||||
int h = 8;
|
||||
for (int y = 0; y < image.height(); y += h)
|
||||
for (int x = 0; x < image.width(); x += w) {
|
||||
QImage tile = image.copy(x, y, w, h);
|
||||
tiles.append(tile);
|
||||
}
|
||||
this->tilesImage = image;
|
||||
this->tiles = tiles;
|
||||
}
|
||||
|
||||
void Tileset::saveTilesImage() {
|
||||
// Only write the tiles image if it was changed.
|
||||
// Porymap will only ever change an existing tiles image by importing a new one.
|
||||
if (!m_hasUnsavedTilesImage)
|
||||
return;
|
||||
|
||||
if (!this->tilesImage.save(this->tilesImagePath, "PNG")) {
|
||||
logError(QString("Failed to save tiles image '%1'").arg(this->tilesImagePath));
|
||||
return;
|
||||
}
|
||||
|
||||
m_hasUnsavedTilesImage = false;
|
||||
}
|
||||
|
||||
void Tileset::loadPalettes() {
|
||||
this->palettes.clear();
|
||||
this->palettePreviews.clear();
|
||||
|
||||
for (int i = 0; i < Project::getNumPalettesTotal(); i++) {
|
||||
QList<QRgb> palette;
|
||||
QString path = this->palettePaths.value(i);
|
||||
if (!path.isEmpty()) {
|
||||
bool error = false;
|
||||
palette = PaletteUtil::parse(path, &error);
|
||||
if (error) palette.clear();
|
||||
}
|
||||
if (palette.isEmpty()) {
|
||||
// Either the palette failed to load, or no palette exists.
|
||||
// We expect tilesets to have a certain number of palettes,
|
||||
// so fill this palette with dummy colors.
|
||||
for (int j = 0; j < 16; j++) {
|
||||
palette.append(qRgb(j * 16, j * 16, j * 16));
|
||||
}
|
||||
}
|
||||
this->palettes.append(palette);
|
||||
this->palettePreviews.append(palette);
|
||||
}
|
||||
}
|
||||
|
||||
void Tileset::savePalettes() {
|
||||
int numPalettes = qMin(this->palettePaths.length(), this->palettes.length());
|
||||
for (int i = 0; i < numPalettes; i++) {
|
||||
PaletteUtil::writeJASC(this->palettePaths.at(i), this->palettes.at(i).toVector(), 0, 16);
|
||||
}
|
||||
}
|
||||
|
||||
void Tileset::load() {
|
||||
loadMetatiles();
|
||||
loadMetatileAttributes();
|
||||
loadTilesImage();
|
||||
loadPalettes();
|
||||
}
|
||||
|
||||
// Because metatile labels are global (and handled by the project) we don't save them here.
|
||||
void Tileset::save() {
|
||||
saveMetatiles();
|
||||
saveMetatileAttributes();
|
||||
saveTilesImage();
|
||||
savePalettes();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include "filedialog.h"
|
||||
#include "newmapdialog.h"
|
||||
#include "newlayoutdialog.h"
|
||||
#include "newtilesetdialog.h"
|
||||
|
||||
#include <QClipboard>
|
||||
#include <QDirIterator>
|
||||
|
|
@ -407,8 +408,8 @@ void MainWindow::initMapList() {
|
|||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
// Create add map/layout button
|
||||
// TODO: Tool tip
|
||||
QPushButton *buttonAdd = new QPushButton(QIcon(":/icons/add.ico"), "");
|
||||
buttonAdd->setToolTip("Create New Map");
|
||||
connect(buttonAdd, &QPushButton::clicked, this, &MainWindow::openNewMapDialog);
|
||||
layout->addWidget(buttonAdd);
|
||||
|
||||
|
|
@ -623,6 +624,7 @@ bool MainWindow::openProject(QString dir, bool initial) {
|
|||
connect(project, &Project::mapLoaded, this, &MainWindow::onMapLoaded);
|
||||
connect(project, &Project::mapCreated, this, &MainWindow::onNewMapCreated);
|
||||
connect(project, &Project::layoutCreated, this, &MainWindow::onNewLayoutCreated);
|
||||
connect(project, &Project::tilesetCreated, this, &MainWindow::onNewTilesetCreated);
|
||||
connect(project, &Project::mapGroupAdded, this, &MainWindow::onNewMapGroupCreated);
|
||||
connect(project, &Project::mapSectionAdded, this, &MainWindow::onNewMapSectionCreated);
|
||||
connect(project, &Project::mapSectionIdNamesChanged, this->mapHeaderForm, &MapHeaderForm::setLocations);
|
||||
|
|
@ -925,7 +927,6 @@ void MainWindow::setLayoutOnlyMode(bool layoutOnly) {
|
|||
|
||||
// setLayout, but with a visible error message in case of failure.
|
||||
// Use when the user is specifically requesting a layout to open.
|
||||
// TODO: Update the various functions taking layout IDs to take layout names (to mirror the equivalent map functions, this discrepancy is confusing atm)
|
||||
bool MainWindow::userSetLayout(QString layoutId) {
|
||||
if (!setLayout(layoutId)) {
|
||||
QMessageBox msgBox(this);
|
||||
|
|
@ -1439,6 +1440,21 @@ void MainWindow::onNewMapSectionCreated(const QString &idName) {
|
|||
// TODO: Refresh Region Map Editor's map section dropdown, if it's open
|
||||
}
|
||||
|
||||
void MainWindow::onNewTilesetCreated(Tileset *tileset) {
|
||||
QString message = QString("Created a new tileset named %1.").arg(tileset->name);
|
||||
logInfo(message);
|
||||
statusBar()->showMessage(message);
|
||||
|
||||
// Refresh tileset combo boxes
|
||||
if (!tileset->is_secondary) {
|
||||
int index = this->editor->project->primaryTilesetLabels.indexOf(tileset->name);
|
||||
ui->comboBox_PrimaryTileset->insertItem(index, tileset->name);
|
||||
} else {
|
||||
int index = this->editor->project->secondaryTilesetLabels.indexOf(tileset->name);
|
||||
ui->comboBox_SecondaryTileset->insertItem(index, tileset->name);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::openNewMapDialog() {
|
||||
auto dialog = new NewMapDialog(this->editor->project, this);
|
||||
dialog->open();
|
||||
|
|
@ -1450,119 +1466,9 @@ void MainWindow::openNewLayoutDialog() {
|
|||
dialog->open();
|
||||
}
|
||||
|
||||
// Insert label for newly-created tileset into sorted list of existing labels
|
||||
int MainWindow::insertTilesetLabel(QStringList * list, QString label) {
|
||||
int i = 0;
|
||||
for (; i < list->length(); i++)
|
||||
if (list->at(i) > label) break;
|
||||
list->insert(i, label);
|
||||
return i;
|
||||
}
|
||||
|
||||
void MainWindow::on_actionNew_Tileset_triggered() {
|
||||
NewTilesetDialog *createTilesetDialog = new NewTilesetDialog(editor->project, this);
|
||||
if(createTilesetDialog->exec() == QDialog::Accepted){
|
||||
if(createTilesetDialog->friendlyName.isEmpty()) {
|
||||
logError(QString("Tried to create a directory with an empty name."));
|
||||
QMessageBox msgBox(this);
|
||||
msgBox.setText("Failed to add new tileset.");
|
||||
QString message = QString("The given name was empty.");
|
||||
msgBox.setInformativeText(message);
|
||||
msgBox.setDefaultButton(QMessageBox::Ok);
|
||||
msgBox.setIcon(QMessageBox::Icon::Critical);
|
||||
msgBox.exec();
|
||||
return;
|
||||
}
|
||||
QString fullDirectoryPath = editor->project->root + "/" + createTilesetDialog->path;
|
||||
QDir directory;
|
||||
if(directory.exists(fullDirectoryPath)) {
|
||||
logError(QString("Could not create tileset \"%1\", the folder \"%2\" already exists.").arg(createTilesetDialog->friendlyName, fullDirectoryPath));
|
||||
QMessageBox msgBox(this);
|
||||
msgBox.setText("Failed to add new tileset.");
|
||||
QString message = QString("The folder for tileset \"%1\" already exists. View porymap.log for specific errors.").arg(createTilesetDialog->friendlyName);
|
||||
msgBox.setInformativeText(message);
|
||||
msgBox.setDefaultButton(QMessageBox::Ok);
|
||||
msgBox.setIcon(QMessageBox::Icon::Critical);
|
||||
msgBox.exec();
|
||||
return;
|
||||
}
|
||||
if (editor->project->tilesetLabelsOrdered.contains(createTilesetDialog->fullSymbolName)) {
|
||||
logError(QString("Could not create tileset \"%1\", the symbol \"%2\" already exists.").arg(createTilesetDialog->friendlyName, createTilesetDialog->fullSymbolName));
|
||||
QMessageBox msgBox(this);
|
||||
msgBox.setText("Failed to add new tileset.");
|
||||
QString message = QString("The symbol for tileset \"%1\" (\"%2\") already exists.").arg(createTilesetDialog->friendlyName, createTilesetDialog->fullSymbolName);
|
||||
msgBox.setInformativeText(message);
|
||||
msgBox.setDefaultButton(QMessageBox::Ok);
|
||||
msgBox.setIcon(QMessageBox::Icon::Critical);
|
||||
msgBox.exec();
|
||||
return;
|
||||
}
|
||||
directory.mkdir(fullDirectoryPath);
|
||||
directory.mkdir(fullDirectoryPath + "/palettes");
|
||||
Tileset newSet;
|
||||
newSet.name = createTilesetDialog->fullSymbolName;
|
||||
newSet.tilesImagePath = fullDirectoryPath + "/tiles.png";
|
||||
newSet.metatiles_path = fullDirectoryPath + "/metatiles.bin";
|
||||
newSet.metatile_attrs_path = fullDirectoryPath + "/metatile_attributes.bin";
|
||||
newSet.is_secondary = createTilesetDialog->isSecondary;
|
||||
int numMetatiles = createTilesetDialog->isSecondary ? (Project::getNumMetatilesTotal() - Project::getNumMetatilesPrimary()) : Project::getNumMetatilesPrimary();
|
||||
QImage tilesImage(":/images/blank_tileset.png");
|
||||
editor->project->loadTilesetTiles(&newSet, tilesImage);
|
||||
int tilesPerMetatile = projectConfig.getNumTilesInMetatile();
|
||||
for(int i = 0; i < numMetatiles; ++i) {
|
||||
Metatile *mt = new Metatile();
|
||||
for(int j = 0; j < tilesPerMetatile; ++j){
|
||||
Tile tile = Tile();
|
||||
if (createTilesetDialog->checkerboardFill) {
|
||||
// Create a checkerboard-style dummy tileset
|
||||
if (((i / 8) % 2) == 0)
|
||||
tile.tileId = ((i % 2) == 0) ? 1 : 2;
|
||||
else
|
||||
tile.tileId = ((i % 2) == 1) ? 1 : 2;
|
||||
}
|
||||
mt->tiles.append(tile);
|
||||
}
|
||||
newSet.addMetatile(mt);
|
||||
}
|
||||
for(int i = 0; i < 16; ++i) {
|
||||
QList<QRgb> currentPal;
|
||||
for(int i = 0; i < 16;++i) {
|
||||
currentPal.append(qRgb(0,0,0));
|
||||
}
|
||||
newSet.palettes.append(currentPal);
|
||||
newSet.palettePreviews.append(currentPal);
|
||||
QString fileName = QString("%1.pal").arg(i, 2, 10, QLatin1Char('0'));
|
||||
newSet.palettePaths.append(fullDirectoryPath+"/palettes/" + fileName);
|
||||
}
|
||||
newSet.palettes[0][1] = qRgb(255,0,255);
|
||||
newSet.palettePreviews[0][1] = qRgb(255,0,255);
|
||||
exportIndexed4BPPPng(newSet.tilesImage, newSet.tilesImagePath);
|
||||
editor->project->saveTilesetMetatiles(&newSet);
|
||||
editor->project->saveTilesetMetatileAttributes(&newSet);
|
||||
editor->project->saveTilesetPalettes(&newSet);
|
||||
|
||||
//append to tileset specific files
|
||||
newSet.appendToHeaders(editor->project->root, createTilesetDialog->friendlyName, editor->project->usingAsmTilesets);
|
||||
newSet.appendToGraphics(editor->project->root, createTilesetDialog->friendlyName, editor->project->usingAsmTilesets);
|
||||
newSet.appendToMetatiles(editor->project->root, createTilesetDialog->friendlyName, editor->project->usingAsmTilesets);
|
||||
|
||||
if (!createTilesetDialog->isSecondary) {
|
||||
int index = insertTilesetLabel(&editor->project->primaryTilesetLabels, createTilesetDialog->fullSymbolName);
|
||||
this->ui->comboBox_PrimaryTileset->insertItem(index, createTilesetDialog->fullSymbolName);
|
||||
} else {
|
||||
int index = insertTilesetLabel(&editor->project->secondaryTilesetLabels, createTilesetDialog->fullSymbolName);
|
||||
this->ui->comboBox_SecondaryTileset->insertItem(index, createTilesetDialog->fullSymbolName);
|
||||
}
|
||||
editor->project->tilesetLabelsOrdered.append(createTilesetDialog->fullSymbolName);
|
||||
|
||||
QMessageBox msgBox(this);
|
||||
msgBox.setText("Successfully created tileset.");
|
||||
QString message = QString("Tileset \"%1\" was created successfully.").arg(createTilesetDialog->friendlyName);
|
||||
msgBox.setInformativeText(message);
|
||||
msgBox.setDefaultButton(QMessageBox::Ok);
|
||||
msgBox.setIcon(QMessageBox::Icon::Information);
|
||||
msgBox.exec();
|
||||
}
|
||||
auto dialog = new NewTilesetDialog(editor->project, this);
|
||||
dialog->open();
|
||||
}
|
||||
|
||||
void MainWindow::updateTilesetEditor() {
|
||||
|
|
|
|||
253
src/project.cpp
253
src/project.cpp
|
|
@ -122,19 +122,13 @@ QString Project::getProjectTitle() {
|
|||
}
|
||||
|
||||
void Project::clearMapCache() {
|
||||
for (auto *map : mapCache.values()) {
|
||||
if (map)
|
||||
delete map;
|
||||
}
|
||||
mapCache.clear();
|
||||
qDeleteAll(this->mapCache);
|
||||
this->mapCache.clear();
|
||||
}
|
||||
|
||||
void Project::clearTilesetCache() {
|
||||
for (auto *tileset : tilesetCache.values()) {
|
||||
if (tileset)
|
||||
delete tileset;
|
||||
}
|
||||
tilesetCache.clear();
|
||||
qDeleteAll(this->tilesetCache);
|
||||
this->tilesetCache.clear();
|
||||
}
|
||||
|
||||
Map* Project::loadMap(QString mapName) {
|
||||
|
|
@ -1005,25 +999,21 @@ void Project::saveHealLocationsConstants() {
|
|||
|
||||
void Project::saveTilesets(Tileset *primaryTileset, Tileset *secondaryTileset) {
|
||||
saveTilesetMetatileLabels(primaryTileset, secondaryTileset);
|
||||
saveTilesetMetatileAttributes(primaryTileset);
|
||||
saveTilesetMetatileAttributes(secondaryTileset);
|
||||
saveTilesetMetatiles(primaryTileset);
|
||||
saveTilesetMetatiles(secondaryTileset);
|
||||
saveTilesetTilesImage(primaryTileset);
|
||||
saveTilesetTilesImage(secondaryTileset);
|
||||
saveTilesetPalettes(primaryTileset);
|
||||
saveTilesetPalettes(secondaryTileset);
|
||||
if (primaryTileset)
|
||||
primaryTileset->save();
|
||||
if (secondaryTileset)
|
||||
secondaryTileset->save();
|
||||
}
|
||||
|
||||
void Project::updateTilesetMetatileLabels(Tileset *tileset) {
|
||||
// Erase old labels, then repopulate with new labels
|
||||
const QString prefix = tileset->getMetatileLabelPrefix();
|
||||
metatileLabelsMap[tileset->name].clear();
|
||||
this->metatileLabelsMap[tileset->name].clear();
|
||||
for (int metatileId : tileset->metatileLabels.keys()) {
|
||||
if (tileset->metatileLabels[metatileId].isEmpty())
|
||||
continue;
|
||||
QString label = prefix + tileset->metatileLabels[metatileId];
|
||||
metatileLabelsMap[tileset->name][label] = metatileId;
|
||||
this->metatileLabelsMap[tileset->name][label] = metatileId;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1082,59 +1072,6 @@ void Project::saveTilesetMetatileLabels(Tileset *primaryTileset, Tileset *second
|
|||
saveTextFile(root + "/" + filename, outputText);
|
||||
}
|
||||
|
||||
void Project::saveTilesetMetatileAttributes(Tileset *tileset) {
|
||||
QFile attrs_file(tileset->metatile_attrs_path);
|
||||
if (attrs_file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
||||
QByteArray data;
|
||||
for (const auto &metatile : tileset->metatiles()) {
|
||||
uint32_t attributes = metatile->getAttributes();
|
||||
for (int i = 0; i < projectConfig.metatileAttributesSize; i++)
|
||||
data.append(static_cast<char>(attributes >> (8 * i)));
|
||||
}
|
||||
attrs_file.write(data);
|
||||
} else {
|
||||
logError(QString("Could not save tileset metatile attributes file '%1'").arg(tileset->metatile_attrs_path));
|
||||
}
|
||||
}
|
||||
|
||||
void Project::saveTilesetMetatiles(Tileset *tileset) {
|
||||
QFile metatiles_file(tileset->metatiles_path);
|
||||
if (metatiles_file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
||||
QByteArray data;
|
||||
int numTiles = projectConfig.getNumTilesInMetatile();
|
||||
for (const auto &metatile : tileset->metatiles()) {
|
||||
for (int i = 0; i < numTiles; i++) {
|
||||
uint16_t tile = metatile->tiles.at(i).rawValue();
|
||||
data.append(static_cast<char>(tile));
|
||||
data.append(static_cast<char>(tile >> 8));
|
||||
}
|
||||
}
|
||||
metatiles_file.write(data);
|
||||
} else {
|
||||
tileset->clearMetatiles();
|
||||
logError(QString("Could not open tileset metatiles file '%1'").arg(tileset->metatiles_path));
|
||||
}
|
||||
}
|
||||
|
||||
void Project::saveTilesetTilesImage(Tileset *tileset) {
|
||||
// Only write the tiles image if it was changed.
|
||||
// Porymap will only ever change an existing tiles image by importing a new one.
|
||||
if (tileset->hasUnsavedTilesImage) {
|
||||
if (!tileset->tilesImage.save(tileset->tilesImagePath, "PNG")) {
|
||||
logError(QString("Failed to save tiles image '%1'").arg(tileset->tilesImagePath));
|
||||
return;
|
||||
}
|
||||
tileset->hasUnsavedTilesImage = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Project::saveTilesetPalettes(Tileset *tileset) {
|
||||
for (int i = 0; i < Project::getNumPalettesTotal(); i++) {
|
||||
QString filepath = tileset->palettePaths.at(i);
|
||||
PaletteUtil::writeJASC(filepath, tileset->palettes.at(i).toVector(), 0, 16);
|
||||
}
|
||||
}
|
||||
|
||||
bool Project::loadLayoutTilesets(Layout *layout) {
|
||||
layout->tileset_primary = getTileset(layout->tileset_primary_label);
|
||||
if (!layout->tileset_primary) {
|
||||
|
|
@ -1465,18 +1402,9 @@ void Project::loadTilesetAssets(Tileset* tileset) {
|
|||
if (tileset->name.isNull()) {
|
||||
return;
|
||||
}
|
||||
this->readTilesetPaths(tileset);
|
||||
QImage image;
|
||||
if (QFile::exists(tileset->tilesImagePath)) {
|
||||
image = QImage(tileset->tilesImagePath).convertToFormat(QImage::Format_Indexed8, Qt::ThresholdDither);
|
||||
flattenTo4bppImage(&image);
|
||||
} else {
|
||||
image = QImage(8, 8, QImage::Format_Indexed8);
|
||||
}
|
||||
this->loadTilesetTiles(tileset, image);
|
||||
this->loadTilesetMetatiles(tileset);
|
||||
this->loadTilesetMetatileLabels(tileset);
|
||||
this->loadTilesetPalettes(tileset);
|
||||
readTilesetPaths(tileset);
|
||||
loadTilesetMetatileLabels(tileset);
|
||||
tileset->load();
|
||||
}
|
||||
|
||||
void Project::readTilesetPaths(Tileset* tileset) {
|
||||
|
|
@ -1536,84 +1464,89 @@ void Project::readTilesetPaths(Tileset* tileset) {
|
|||
}
|
||||
}
|
||||
|
||||
void Project::loadTilesetPalettes(Tileset* tileset) {
|
||||
QList<QList<QRgb>> palettes;
|
||||
QList<QList<QRgb>> palettePreviews;
|
||||
for (int i = 0; i < tileset->palettePaths.length(); i++) {
|
||||
QString path = tileset->palettePaths.value(i);
|
||||
bool error = false;
|
||||
QList<QRgb> palette = PaletteUtil::parse(path, &error);
|
||||
if (error) {
|
||||
for (int j = 0; j < 16; j++) {
|
||||
palette.append(qRgb(j * 16, j * 16, j * 16));
|
||||
Tileset *Project::createNewTileset(const QString &friendlyName, bool secondary, bool checkerboardFill) {
|
||||
auto tileset = new Tileset();
|
||||
tileset->name = projectConfig.getIdentifier(ProjectIdentifier::symbol_tilesets_prefix) + friendlyName;
|
||||
tileset->is_secondary = secondary;
|
||||
|
||||
// Create tileset directories
|
||||
const QString fullDirectoryPath = QString("%1/%2").arg(this->root).arg(tileset->getExpectedDir());
|
||||
QDir directory;
|
||||
if (!directory.mkpath(fullDirectoryPath)) {
|
||||
logError(QString("Failed to create directory '%1' for new tileset '%2'").arg(fullDirectoryPath).arg(tileset->name));
|
||||
delete tileset;
|
||||
return nullptr;
|
||||
}
|
||||
const QString palettesPath = fullDirectoryPath + "/palettes";
|
||||
if (!directory.mkpath(palettesPath)) {
|
||||
logError(QString("Failed to create palettes directory '%1' for new tileset '%2'").arg(palettesPath).arg(tileset->name));
|
||||
delete tileset;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
tileset->tilesImagePath = fullDirectoryPath + "/tiles.png";
|
||||
tileset->metatiles_path = fullDirectoryPath + "/metatiles.bin";
|
||||
tileset->metatile_attrs_path = fullDirectoryPath + "/metatile_attributes.bin";
|
||||
|
||||
// Set default tiles image
|
||||
QImage tilesImage(":/images/blank_tileset.png");
|
||||
tileset->loadTilesImage(&tilesImage);
|
||||
//exportIndexed4BPPPng(tileset->tilesImage, tileset->tilesImagePath); // TODO: Make sure we can now properly handle the 8bpp images that get written without this.
|
||||
|
||||
// Create default metatiles
|
||||
const int numMetatiles = tileset->is_secondary ? (Project::getNumMetatilesTotal() - Project::getNumMetatilesPrimary()) : Project::getNumMetatilesPrimary();
|
||||
const int tilesPerMetatile = projectConfig.getNumTilesInMetatile();
|
||||
for (int i = 0; i < numMetatiles; ++i) {
|
||||
auto metatile = new Metatile();
|
||||
for(int j = 0; j < tilesPerMetatile; ++j){
|
||||
Tile tile = Tile();
|
||||
if (checkerboardFill) {
|
||||
// Create a checkerboard-style dummy tileset
|
||||
if (((i / 8) % 2) == 0)
|
||||
tile.tileId = ((i % 2) == 0) ? 1 : 2;
|
||||
else
|
||||
tile.tileId = ((i % 2) == 1) ? 1 : 2;
|
||||
}
|
||||
metatile->tiles.append(tile);
|
||||
}
|
||||
|
||||
palettes.append(palette);
|
||||
palettePreviews.append(palette);
|
||||
}
|
||||
tileset->palettes = palettes;
|
||||
tileset->palettePreviews = palettePreviews;
|
||||
}
|
||||
|
||||
void Project::loadTilesetTiles(Tileset *tileset, QImage image) {
|
||||
QList<QImage> tiles;
|
||||
int w = 8;
|
||||
int h = 8;
|
||||
for (int y = 0; y < image.height(); y += h)
|
||||
for (int x = 0; x < image.width(); x += w) {
|
||||
QImage tile = image.copy(x, y, w, h);
|
||||
tiles.append(tile);
|
||||
}
|
||||
tileset->tilesImage = image;
|
||||
tileset->tiles = tiles;
|
||||
}
|
||||
|
||||
void Project::loadTilesetMetatiles(Tileset* tileset) {
|
||||
QFile metatiles_file(tileset->metatiles_path);
|
||||
if (metatiles_file.open(QIODevice::ReadOnly)) {
|
||||
QByteArray data = metatiles_file.readAll();
|
||||
int tilesPerMetatile = projectConfig.getNumTilesInMetatile();
|
||||
int bytesPerMetatile = 2 * tilesPerMetatile;
|
||||
int num_metatiles = data.length() / bytesPerMetatile;
|
||||
QList<Metatile*> metatiles;
|
||||
for (int i = 0; i < num_metatiles; i++) {
|
||||
Metatile *metatile = new Metatile;
|
||||
int index = i * bytesPerMetatile;
|
||||
for (int j = 0; j < tilesPerMetatile; j++) {
|
||||
uint16_t tileRaw = static_cast<unsigned char>(data[index++]);
|
||||
tileRaw |= static_cast<unsigned char>(data[index++]) << 8;
|
||||
metatile->tiles.append(Tile(tileRaw));
|
||||
}
|
||||
metatiles.append(metatile);
|
||||
}
|
||||
tileset->setMetatiles(metatiles);
|
||||
} else {
|
||||
tileset->clearMetatiles();
|
||||
logError(QString("Could not open tileset metatiles file '%1'").arg(tileset->metatiles_path));
|
||||
tileset->addMetatile(metatile);
|
||||
}
|
||||
|
||||
QFile attrs_file(tileset->metatile_attrs_path);
|
||||
if (attrs_file.open(QIODevice::ReadOnly)) {
|
||||
QByteArray data = attrs_file.readAll();
|
||||
int num_metatiles = tileset->numMetatiles();
|
||||
int attrSize = projectConfig.metatileAttributesSize;
|
||||
int num_metatileAttrs = data.length() / attrSize;
|
||||
if (num_metatiles != num_metatileAttrs) {
|
||||
logWarn(QString("Metatile count %1 does not match metatile attribute count %2 in %3").arg(num_metatiles).arg(num_metatileAttrs).arg(tileset->name));
|
||||
if (num_metatileAttrs > num_metatiles)
|
||||
num_metatileAttrs = num_metatiles;
|
||||
// Create default palettes
|
||||
for(int i = 0; i < 16; ++i) {
|
||||
QList<QRgb> currentPal;
|
||||
for(int i = 0; i < 16;++i) {
|
||||
currentPal.append(qRgb(0,0,0));
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_metatileAttrs; i++) {
|
||||
uint32_t attributes = 0;
|
||||
for (int j = 0; j < attrSize; j++)
|
||||
attributes |= static_cast<unsigned char>(data.at(i * attrSize + j)) << (8 * j);
|
||||
tileset->metatileAt(i)->setAttributes(attributes);
|
||||
}
|
||||
} else {
|
||||
logError(QString("Could not open tileset metatile attributes file '%1'").arg(tileset->metatile_attrs_path));
|
||||
tileset->palettes.append(currentPal);
|
||||
tileset->palettePreviews.append(currentPal);
|
||||
tileset->palettePaths.append(QString("%1/%2.pal").arg(palettesPath).arg(i, 2, 10, QLatin1Char('0')));
|
||||
}
|
||||
tileset->palettes[0][1] = qRgb(255,0,255);
|
||||
tileset->palettePreviews[0][1] = qRgb(255,0,255);
|
||||
|
||||
// Update tileset label arrays
|
||||
QStringList *labelList = tileset->is_secondary ? &this->secondaryTilesetLabels : &this->primaryTilesetLabels;
|
||||
for (int i = 0; i < labelList->length(); i++) {
|
||||
if (labelList->at(i) > tileset->name) {
|
||||
labelList->insert(i, tileset->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
this->tilesetLabelsOrdered.append(tileset->name);
|
||||
|
||||
// TODO: Ideally we wouldn't save new Tilesets immediately
|
||||
// Append to tileset specific files
|
||||
tileset->appendToHeaders(this->root, friendlyName, this->usingAsmTilesets);
|
||||
tileset->appendToGraphics(this->root, friendlyName, this->usingAsmTilesets);
|
||||
tileset->appendToMetatiles(this->root, friendlyName, this->usingAsmTilesets);
|
||||
|
||||
tileset->save();
|
||||
|
||||
this->tilesetCache.insert(tileset->name, tileset);
|
||||
|
||||
emit tilesetCreated(tileset);
|
||||
return tileset;
|
||||
}
|
||||
|
||||
QString Project::findMetatileLabelsTileset(QString label) {
|
||||
|
|
@ -1658,9 +1591,9 @@ void Project::loadTilesetMetatileLabels(Tileset* tileset) {
|
|||
QString metatileLabelPrefix = tileset->getMetatileLabelPrefix();
|
||||
|
||||
// Reverse map for faster lookup by metatile id
|
||||
for (QString labelName : metatileLabelsMap[tileset->name].keys()) {
|
||||
auto metatileId = metatileLabelsMap[tileset->name][labelName];
|
||||
tileset->metatileLabels[metatileId] = labelName.replace(metatileLabelPrefix, "");
|
||||
for (auto it = this->metatileLabelsMap[tileset->name].constBegin(); it != this->metatileLabelsMap[tileset->name].constEnd(); it++) {
|
||||
QString labelName = it.key();
|
||||
tileset->metatileLabels[it.value()] = labelName.replace(metatileLabelPrefix, "");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -337,7 +337,7 @@ void MainWindow::refreshAfterPaletteChange(Tileset *tileset) {
|
|||
this->editor->map_item->draw(true);
|
||||
this->editor->updateMapBorder();
|
||||
this->editor->updateMapConnections();
|
||||
this->editor->project->saveTilesetPalettes(tileset);
|
||||
tileset->savePalettes();
|
||||
}
|
||||
|
||||
void MainWindow::setTilesetPalette(Tileset *tileset, int paletteIndex, QList<QList<int>> colors) {
|
||||
|
|
@ -576,14 +576,14 @@ void MainWindow::setSecondaryTileset(QString tileset) {
|
|||
|
||||
void MainWindow::saveMetatilesByMetatileId(int metatileId) {
|
||||
Tileset * tileset = Tileset::getMetatileTileset(metatileId, this->editor->layout->tileset_primary, this->editor->layout->tileset_secondary);
|
||||
if (this->editor->project && tileset)
|
||||
this->editor->project->saveTilesetMetatiles(tileset);
|
||||
if (tileset)
|
||||
tileset->saveMetatiles();
|
||||
}
|
||||
|
||||
void MainWindow::saveMetatileAttributesByMetatileId(int metatileId) {
|
||||
Tileset * tileset = Tileset::getMetatileTileset(metatileId, this->editor->layout->tileset_primary, this->editor->layout->tileset_secondary);
|
||||
if (this->editor->project && tileset)
|
||||
this->editor->project->saveTilesetMetatileAttributes(tileset);
|
||||
if (tileset)
|
||||
tileset->saveMetatileAttributes();
|
||||
|
||||
// If the tileset editor is open it needs to be refreshed with the new changes.
|
||||
// Rather than do a full refresh (which is costly) we tell the editor it will need
|
||||
|
|
@ -811,9 +811,6 @@ QJSValue MainWindow::getTilePixels(int tileId) {
|
|||
// Editing map header
|
||||
//=====================
|
||||
|
||||
// TODO: Connect signals from new function calls to update UI
|
||||
// TODO: Is the error-checking for known constant names still reasonable / needed? (you can type anything after all)
|
||||
|
||||
QString MainWindow::getSong() {
|
||||
if (!this->editor || !this->editor->map)
|
||||
return QString();
|
||||
|
|
@ -823,10 +820,6 @@ QString MainWindow::getSong() {
|
|||
void MainWindow::setSong(QString song) {
|
||||
if (!this->editor || !this->editor->map || !this->editor->project)
|
||||
return;
|
||||
if (!this->editor->project->songNames.contains(song)) {
|
||||
logError(QString("Unknown song '%1'").arg(song));
|
||||
return;
|
||||
}
|
||||
this->editor->map->header()->setSong(song);
|
||||
}
|
||||
|
||||
|
|
@ -867,10 +860,6 @@ QString MainWindow::getWeather() {
|
|||
void MainWindow::setWeather(QString weather) {
|
||||
if (!this->editor || !this->editor->map || !this->editor->project)
|
||||
return;
|
||||
if (!this->editor->project->weatherNames.contains(weather)) {
|
||||
logError(QString("Unknown weather '%1'").arg(weather));
|
||||
return;
|
||||
}
|
||||
this->editor->map->header()->setWeather(weather);
|
||||
}
|
||||
|
||||
|
|
@ -883,10 +872,6 @@ QString MainWindow::getType() {
|
|||
void MainWindow::setType(QString type) {
|
||||
if (!this->editor || !this->editor->map || !this->editor->project)
|
||||
return;
|
||||
if (!this->editor->project->mapTypes.contains(type)) {
|
||||
logError(QString("Unknown map type '%1'").arg(type));
|
||||
return;
|
||||
}
|
||||
this->editor->map->header()->setType(type);
|
||||
}
|
||||
|
||||
|
|
@ -899,10 +884,6 @@ QString MainWindow::getBattleScene() {
|
|||
void MainWindow::setBattleScene(QString battleScene) {
|
||||
if (!this->editor || !this->editor->map || !this->editor->project)
|
||||
return;
|
||||
if (!this->editor->project->mapBattleScenes.contains(battleScene)) {
|
||||
logError(QString("Unknown battle scene '%1'").arg(battleScene));
|
||||
return;
|
||||
}
|
||||
this->editor->map->header()->setBattleScene(battleScene);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,50 +1,87 @@
|
|||
#include "newtilesetdialog.h"
|
||||
#include "ui_newtilesetdialog.h"
|
||||
#include "project.h"
|
||||
#include "imageexport.h"
|
||||
|
||||
const QString lineEdit_ErrorStylesheet = "QLineEdit { background-color: rgba(255, 0, 0, 25%) }";
|
||||
|
||||
NewTilesetDialog::NewTilesetDialog(Project* project, QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::NewTilesetDialog)
|
||||
ui(new Ui::NewTilesetDialog),
|
||||
symbolPrefix(projectConfig.getIdentifier(ProjectIdentifier::symbol_tilesets_prefix))
|
||||
{
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
setModal(true);
|
||||
ui->setupUi(this);
|
||||
this->setFixedSize(this->width(), this->height());
|
||||
this->project = project;
|
||||
|
||||
ui->checkBox_CheckerboardFill->setChecked(porymapConfig.tilesetCheckerboardFill);
|
||||
ui->label_SymbolNameDisplay->setText(this->symbolPrefix);
|
||||
ui->comboBox_Type->setMinimumContentsLength(12);
|
||||
|
||||
//only allow characters valid for a symbol
|
||||
static const QRegularExpression expression("[_A-Za-z0-9]+$"); // TODO: Incorrect, allows digits at beginning
|
||||
QRegularExpressionValidator *validator = new QRegularExpressionValidator(expression);
|
||||
this->ui->nameLineEdit->setValidator(validator);
|
||||
static const QRegularExpression expression("[A-Za-z_]+[\\w]*");
|
||||
QRegularExpressionValidator *validator = new QRegularExpressionValidator(expression, this);
|
||||
ui->lineEdit_FriendlyName->setValidator(validator);
|
||||
|
||||
bool checkerboard = porymapConfig.tilesetCheckerboardFill;
|
||||
this->ui->fillCheckBox->setChecked(checkerboard);
|
||||
this->checkerboardFill = checkerboard;
|
||||
connect(ui->lineEdit_FriendlyName, &QLineEdit::textChanged, this, &NewTilesetDialog::onFriendlyNameChanged);
|
||||
connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &NewTilesetDialog::dialogButtonClicked);
|
||||
|
||||
connect(this->ui->nameLineEdit, &QLineEdit::textChanged, this, &NewTilesetDialog::NameOrSecondaryChanged);
|
||||
connect(this->ui->typeComboBox, &QComboBox::currentTextChanged, this, &NewTilesetDialog::SecondaryChanged);
|
||||
connect(this->ui->fillCheckBox, &QCheckBox::stateChanged, this, &NewTilesetDialog::FillChanged);
|
||||
//connect(this->ui->toolButton, &QToolButton::clicked, this, &NewTilesetDialog::ChangeFilePath);
|
||||
this->SecondaryChanged();
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
NewTilesetDialog::~NewTilesetDialog()
|
||||
{
|
||||
porymapConfig.tilesetCheckerboardFill = ui->checkBox_CheckerboardFill->isChecked();
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void NewTilesetDialog::SecondaryChanged(){
|
||||
this->isSecondary = (this->ui->typeComboBox->currentIndex() == 1);
|
||||
NameOrSecondaryChanged();
|
||||
void NewTilesetDialog::onFriendlyNameChanged(const QString &friendlyName) {
|
||||
// When the tileset name is changed, update this label to display the full symbol name.
|
||||
ui->label_SymbolNameDisplay->setText(this->symbolPrefix + friendlyName);
|
||||
|
||||
validateName(true);
|
||||
}
|
||||
|
||||
// TODO: No validation
|
||||
void NewTilesetDialog::NameOrSecondaryChanged() {
|
||||
this->friendlyName = this->ui->nameLineEdit->text();
|
||||
this->fullSymbolName = projectConfig.getIdentifier(ProjectIdentifier::symbol_tilesets_prefix) + this->friendlyName;
|
||||
this->ui->symbolNameLineEdit->setText(this->fullSymbolName);
|
||||
this->path = Tileset::getExpectedDir(this->fullSymbolName, this->isSecondary);
|
||||
this->ui->pathLineEdit->setText(this->path);
|
||||
bool NewTilesetDialog::validateName(bool allowEmpty) {
|
||||
const QString friendlyName = ui->lineEdit_FriendlyName->text();
|
||||
const QString symbolName = ui->label_SymbolNameDisplay->text();
|
||||
|
||||
QString errorText;
|
||||
if (friendlyName.isEmpty()) {
|
||||
if (!allowEmpty) errorText = QString("%1 cannot be empty.").arg(ui->label_FriendlyName->text());
|
||||
} else if (!this->project->isIdentifierUnique(symbolName)) {
|
||||
errorText = QString("%1 '%2' is not unique.").arg(ui->label_SymbolName->text()).arg(symbolName);
|
||||
}
|
||||
|
||||
bool isValid = errorText.isEmpty();
|
||||
ui->label_NameError->setText(errorText);
|
||||
ui->label_NameError->setVisible(!isValid);
|
||||
ui->lineEdit_FriendlyName->setStyleSheet(!isValid ? lineEdit_ErrorStylesheet : "");
|
||||
return isValid;
|
||||
}
|
||||
|
||||
void NewTilesetDialog::FillChanged() {
|
||||
this->checkerboardFill = this->ui->fillCheckBox->isChecked();
|
||||
porymapConfig.tilesetCheckerboardFill = this->checkerboardFill;
|
||||
void NewTilesetDialog::dialogButtonClicked(QAbstractButton *button) {
|
||||
auto role = ui->buttonBox->buttonRole(button);
|
||||
if (role == QDialogButtonBox::RejectRole){
|
||||
reject();
|
||||
} else if (role == QDialogButtonBox::AcceptRole) {
|
||||
accept();
|
||||
}
|
||||
}
|
||||
|
||||
void NewTilesetDialog::accept() {
|
||||
if (!validateName())
|
||||
return;
|
||||
|
||||
bool secondary = ui->comboBox_Type->currentIndex() == 1;
|
||||
Tileset *tileset = this->project->createNewTileset(ui->lineEdit_FriendlyName->text(), secondary, ui->checkBox_CheckerboardFill->isChecked());
|
||||
if (!tileset) {
|
||||
ui->label_GenericError->setText(QString("Failed to create tileset. See %1 for details.").arg(getLogPath()));
|
||||
ui->label_GenericError->setVisible(true);
|
||||
return;
|
||||
}
|
||||
ui->label_GenericError->setVisible(false);
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -716,22 +716,9 @@ void TilesetEditor::importTilesetTiles(Tileset *tileset, bool primary) {
|
|||
image = image.convertToFormat(QImage::Format::Format_Indexed8, colorTable);
|
||||
}
|
||||
|
||||
// Validate image is properly indexed to 16 colors.
|
||||
int colorCount = image.colorCount();
|
||||
if (colorCount > 16) {
|
||||
flattenTo4bppImage(&image);
|
||||
} else if (colorCount < 16) {
|
||||
QVector<QRgb> colorTable = image.colorTable();
|
||||
for (int i = colorTable.length(); i < 16; i++) {
|
||||
colorTable.append(Qt::black);
|
||||
}
|
||||
image.setColorTable(colorTable);
|
||||
}
|
||||
|
||||
this->project->loadTilesetTiles(tileset, image);
|
||||
tileset->loadTilesImage(&image);
|
||||
this->refresh();
|
||||
this->hasUnsavedChanges = true;
|
||||
tileset->hasUnsavedTilesImage = true;
|
||||
}
|
||||
|
||||
void TilesetEditor::closeEvent(QCloseEvent *event)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user