Merge branch 'master' of github.com:huderlem/porymap

This commit is contained in:
Diegoisawesome 2018-10-04 11:03:33 -05:00
commit 54a9d8b23b
27 changed files with 3458 additions and 287 deletions

1536
forms/paletteeditor.ui Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,232 +1,445 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TilesetEditor</class>
<widget class="QDialog" name="TilesetEditor">
<widget class="QMainWindow" name="TilesetEditor">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1022</width>
<height>386</height>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
<string>MainWindow</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0" colspan="3">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QScrollArea" name="scrollArea">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>329</width>
<height>339</height>
</rect>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QScrollArea" name="scrollArea_Metatiles">
<property name="widgetResizable">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>26</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1">
<widget class="QGraphicsView" name="graphicsView"/>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>26</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>120</height>
</size>
</property>
</spacer>
</item>
</layout>
<widget class="QWidget" name="scrollAreaWidgetContents_2">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>386</width>
<height>539</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="2">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="1">
<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="1" column="1">
<widget class="QGraphicsView" name="graphicsView_Metatiles">
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
</widget>
</item>
<item row="0" column="1">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</widget>
</item>
<item row="0" column="1">
<widget class="QFrame" name="frame_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>2</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QScrollArea" name="scrollArea_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents_2">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>316</width>
<height>319</height>
</rect>
</item>
<item>
<widget class="QFrame" name="frame_2">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item alignment="Qt::AlignTop">
<widget class="QFrame" name="frame_4">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Tile Properties</string>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>21</width>
<height>20</height>
</size>
<property name="flat">
<bool>false</bool>
</property>
</spacer>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Palette</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="spinBox_paletteSelector"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>X Flip</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Y Flip</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_7">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Selection</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="checkBox_xFlip">
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="checkBox_yFlip">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QGraphicsView" name="graphicsView_selectedTile">
<property name="minimumSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="1">
<widget class="QGraphicsView" name="graphicsView_2"/>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Metatile Properties</string>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>21</width>
<height>20</height>
</size>
<property name="flat">
<bool>false</bool>
</property>
</spacer>
</item>
<item row="1" column="1">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
<property name="checkable">
<bool>false</bool>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>100</height>
</size>
</property>
</spacer>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Bottom/Top</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QGraphicsView" name="graphicsView_metatileLayers">
<property name="minimumSize">
<size>
<width>64</width>
<height>32</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>64</width>
<height>32</height>
</size>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Metatile Behavior</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QComboBox" name="comboBox_metatileBehaviors"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Layer Type</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QComboBox" name="comboBox_layerType"/>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QFrame" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QScrollArea" name="scrollArea_Tiles">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>386</width>
<height>359</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="2">
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1">
<spacer name="verticalSpacer_3">
<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="1" column="1">
<widget class="QGraphicsView" name="graphicsView_Tiles">
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
</widget>
</item>
<item row="2" column="1">
<spacer name="verticalSpacer_4">
<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="1" column="0">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>21</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionSave_Tileset"/>
<addaction name="separator"/>
</widget>
<widget class="QMenu" name="menuTools">
<property name="title">
<string>Tools</string>
</property>
<addaction name="actionImport_Primary_Tiles"/>
<addaction name="actionImport_Secondary_Tiles"/>
<addaction name="actionChange_Metatiles_Count"/>
<addaction name="actionChange_Palettes"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuTools"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<action name="actionSave_Tileset">
<property name="text">
<string>Save Tileset</string>
</property>
<property name="shortcut">
<string>Ctrl+S</string>
</property>
</action>
<action name="actionImport_Primary_Tiles">
<property name="text">
<string>Import Primary Tiles</string>
</property>
</action>
<action name="actionImport_Secondary_Tiles">
<property name="text">
<string>Import Secondary Tiles</string>
</property>
</action>
<action name="actionChange_Metatiles_Count">
<property name="text">
<string>Change Number of Metatiles</string>
</property>
</action>
<action name="actionChange_Palettes">
<property name="text">
<string>Change Palettes</string>
</property>
</action>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>TilesetEditor</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>TilesetEditor</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>

View file

@ -10,7 +10,10 @@ public:
Metatile();
public:
QList<Tile> *tiles = nullptr;
uint8_t behavior;
uint8_t layerType;
Metatile *copy();
static int getBlockIndex(int);
};

View file

@ -17,16 +17,24 @@ public:
QString tiles_label;
QString palettes_label;
QString metatiles_label;
QString metatiles_path;
QString callback_label;
QString metatile_attrs_label;
QString metatile_attrs_path;
QString tilesImagePath;
QImage tilesImage;
QList<QString> palettePaths;
QList<QImage> *tiles = nullptr;
QList<Metatile*> *metatiles = nullptr;
QList<QList<QRgb>> *palettes = nullptr;
Tileset* copy();
static Tileset* getBlockTileset(int, Tileset*, Tileset*);
static Metatile* getMetatile(int, Tileset*, Tileset*);
static QList<QList<QRgb>> getBlockPalettes(Tileset*, Tileset*);
static QList<QRgb> getPalette(int, Tileset*, Tileset*);
};
#endif // TILESET_H

View file

@ -68,9 +68,10 @@ public:
void updateDiveMap(QString mapName);
void updateEmergeMap(QString mapName);
void setSelectedConnectionFromMap(QString mapName);
void updatePrimaryTileset(QString tilesetLabel);
void updateSecondaryTileset(QString tilesetLabel);
void updatePrimaryTileset(QString tilesetLabel, bool forceLoad = false);
void updateSecondaryTileset(QString tilesetLabel, bool forceLoad = false);
void toggleBorderVisibility(bool visible);
Tileset *getCurrentMapPrimaryTileset();
DraggablePixmapItem *addMapEvent(Event *event);
void selectMapEvent(DraggablePixmapItem *object);

View file

@ -12,6 +12,7 @@
#include "project.h"
#include "map.h"
#include "editor.h"
#include "tileseteditor.h"
namespace Ui {
class MainWindow;
@ -42,6 +43,7 @@ private slots:
void onLoadMapRequested(QString, QString);
void onMapChanged(Map *map);
void onMapNeedsRedrawing();
void onTilesetsSaved(QString, QString);
void on_action_Save_triggered();
void on_tabWidget_2_currentChanged(int index);
@ -92,6 +94,7 @@ private slots:
void onAddNewMapToGroupClick(QAction* triggeredAction);
void onTilesetChanged(QString);
void currentMetatilesSelectionChanged();
void onTilesetEditorClosed();
void on_action_Export_Map_Image_triggered();
@ -123,8 +126,11 @@ private slots:
void resetMapViewScale();
void on_actionTileset_Editor_triggered();
private:
Ui::MainWindow *ui;
TilesetEditor *tilesetEditor = nullptr;
QStandardItemModel *mapListModel;
QList<QStandardItem*> *mapGroupsModel;
QMap<QString, QModelIndex> mapListIndexes;
@ -153,6 +159,7 @@ private:
void initEditor();
void loadUserSettings();
void openRecentProject();
void updateTilesetEditor();
};
enum MapListUserRoles {

View file

@ -40,14 +40,16 @@ public:
QStringList *secretBaseIds = nullptr;
QStringList *bgEventFacingDirections = nullptr;
QStringList mapsWithConnections;
QMap<QString, int> metatileBehaviorMap;
QMap<int, QString> metatileBehaviorMapInverse;
QMap<QString, Map*> *map_cache;
Map* loadMap(QString);
Map* getMap(QString);
QMap<QString, Tileset*> *tileset_cache = nullptr;
Tileset* loadTileset(QString);
Tileset* getTileset(QString);
Tileset* loadTileset(QString, Tileset *tileset = nullptr);
Tileset* getTileset(QString, bool forceLoad = false);
Blockdata* readBlockdata(QString);
void loadBlockdata(Map*);
@ -72,6 +74,8 @@ public:
void readMapsWithConnections();
void loadMapTilesets(Map*);
void loadTilesetAssets(Tileset*);
void loadTilesetTiles(Tileset*, QImage);
void loadTilesetMetatiles(Tileset*);
void saveBlockdata(Map*);
void saveMapBorder(Map*);
@ -83,6 +87,7 @@ public:
void saveMapGroupsTable();
void saveMapConstantsHeader();
void saveHealLocationStruct(Map*);
void saveTilesets(Tileset*, Tileset*);
QList<QStringList>* parseAsm(QString text);
QStringList getSongNames();
@ -100,9 +105,11 @@ public:
void readCoordEventWeatherNames();
void readSecretBaseIds();
void readBgEventFacingDirections();
void readMetatileBehaviors();
void loadEventPixmaps(QList<Event*> objects);
QMap<QString, int> getEventObjGfxConstants();
QString fixPalettePath(QString path);
QString fixGraphicPath(QString path);
void readMapEvents(Map *map);
@ -127,6 +134,10 @@ private:
QString getMapLayoutFilepath(QString);
void saveMapHeader(Map*);
void saveMapConnections(Map*);
void saveTilesetMetatileAttributes(Tileset*);
void saveTilesetMetatiles(Tileset*);
void saveTilesetTilesImage(Tileset*);
void saveTilesetPalettes(Tileset*, bool);
void updateMapsWithConnections(Map*);
void saveMapsWithConnections();
void saveMapLayoutsTable();

View file

@ -8,7 +8,8 @@
QImage getCollisionMetatileImage(Block);
QImage getCollisionMetatileImage(int, int);
QImage getMetatileImage(int, Tileset*, Tileset*);
QImage getTileImage(int, Tileset*, Tileset*);
QImage getMetatileImage(uint16_t, Tileset*, Tileset*);
QImage getTileImage(uint16_t, Tileset*, Tileset*);
QImage getColoredTileImage(uint16_t, Tileset*, Tileset*, int);
#endif // IMAGEPROVIDERS_H

View file

@ -0,0 +1,29 @@
#ifndef METATILELAYERSITEM_H
#define METATILELAYERSITEM_H
#include "tileset.h"
#include <QGraphicsPixmapItem>
#include <QGraphicsSceneMouseEvent>
class MetatileLayersItem : public QObject, public QGraphicsPixmapItem {
Q_OBJECT
public:
MetatileLayersItem(Metatile *metatile, Tileset *primaryTileset, Tileset *secondaryTileset) {
this->metatile = metatile;
this->primaryTileset = primaryTileset;
this->secondaryTileset = secondaryTileset;
}
void draw();
void setTilesets(Tileset*, Tileset*);
void setMetatile(Metatile*);
private:
Metatile* metatile;
Tileset *primaryTileset;
Tileset *secondaryTileset;
signals:
void tileChanged(int, int);
protected:
void mousePressEvent(QGraphicsSceneMouseEvent*);
};
#endif // METATILELAYERSITEM_H

View file

@ -0,0 +1,43 @@
#ifndef PALETTEEDITOR_H
#define PALETTEEDITOR_H
#include <QMainWindow>
#include <QSlider>
#include <QFrame>
#include "project.h"
namespace Ui {
class PaletteEditor;
}
class PaletteEditor : public QMainWindow {
Q_OBJECT
public:
explicit PaletteEditor(Project*, Tileset*, Tileset*, QWidget *parent = nullptr);
~PaletteEditor();
void setPaletteId(int);
private:
Ui::PaletteEditor *ui;
Project *project = nullptr;
QList<QList<QSlider*>> sliders;
QList<QFrame*> frames;
Tileset *primaryTileset;
Tileset *secondaryTileset;
void disableSliderSignals();
void enableSliderSignals();
void initColorSliders();
void refreshColorSliders();
void refreshColors();
void refreshColor(int);
void setColor(int);
signals:
void closed();
void changedPaletteColor();
void changedPalette(int);
private slots:
void on_spinBox_PaletteId_valueChanged(int arg1);
};
#endif // PALETTEEDITOR_H

View file

@ -1,22 +1,90 @@
#ifndef TILESETEDITOR_H
#define TILESETEDITOR_H
#include <QDialog>
#include <QMainWindow>
#include "project.h"
#include "paletteeditor.h"
#include "tileseteditormetatileselector.h"
#include "tileseteditortileselector.h"
#include "metatilelayersitem.h"
namespace Ui {
class TilesetEditor;
}
class TilesetEditor : public QDialog
class TilesetEditor : public QMainWindow
{
Q_OBJECT
public:
explicit TilesetEditor(QWidget *parent = nullptr);
explicit TilesetEditor(Project*, QString, QString, QWidget *parent = nullptr);
~TilesetEditor();
void setTilesets(QString, QString);
void init(Project*, QString, QString);
private slots:
void onHoveredMetatileChanged(uint16_t);
void onHoveredMetatileCleared();
void onSelectedMetatileChanged(uint16_t);
void onHoveredTileChanged(uint16_t);
void onHoveredTileCleared();
void onSelectedTilesChanged();
void onMetatileLayerTileChanged(int, int);
void onPaletteEditorClosed();
void onPaletteEditorChangedPaletteColor();
void onPaletteEditorChangedPalette(int);
void on_spinBox_paletteSelector_valueChanged(int arg1);
void on_checkBox_xFlip_stateChanged(int arg1);
void on_checkBox_yFlip_stateChanged(int arg1);
void on_comboBox_metatileBehaviors_currentIndexChanged(const QString &arg1);
void on_comboBox_layerType_currentIndexChanged(int index);
void on_actionSave_Tileset_triggered();
void on_actionImport_Primary_Tiles_triggered();
void on_actionImport_Secondary_Tiles_triggered();
void on_actionChange_Metatiles_Count_triggered();
void on_actionChange_Palettes_triggered();
private:
void closeEvent(QCloseEvent*);
void initMetatileSelector();
void initTileSelector();
void initSelectedTileItem();
void initMetatileLayersItem();
void drawSelectedTiles();
void importTilesetTiles(Tileset*, bool);
void refresh();
Ui::TilesetEditor *ui;
TilesetEditorMetatileSelector *metatileSelector = nullptr;
TilesetEditorTileSelector *tileSelector = nullptr;
MetatileLayersItem *metatileLayersItem = nullptr;
PaletteEditor *paletteEditor = nullptr;
Project *project = nullptr;
Metatile *metatile = nullptr;
int paletteId;
bool tileXFlip;
bool tileYFlip;
bool hasUnsavedChanges;
Tileset *primaryTileset = nullptr;
Tileset *secondaryTileset = nullptr;
QGraphicsScene *metatilesScene = nullptr;
QGraphicsScene *tilesScene = nullptr;
QGraphicsScene *selectedTileScene = nullptr;
QGraphicsPixmapItem *selectedTilePixmapItem = nullptr;
QGraphicsScene *metatileLayersScene = nullptr;
signals:
void tilesetsSaved(QString, QString);
void closed();
};
#endif // TILESETEDITOR_H

View file

@ -0,0 +1,44 @@
#ifndef TILESETEDITORMETATILESELECTOR_H
#define TILESETEDITORMETATILESELECTOR_H
#include "selectablepixmapitem.h"
#include "tileset.h"
class TilesetEditorMetatileSelector: public SelectablePixmapItem {
Q_OBJECT
public:
TilesetEditorMetatileSelector(Tileset *primaryTileset, Tileset *secondaryTileset): SelectablePixmapItem(32, 32, 1, 1) {
this->primaryTileset = primaryTileset;
this->secondaryTileset = secondaryTileset;
this->numMetatilesWide = 8;
setAcceptHoverEvents(true);
}
void draw();
void select(uint16_t metatileId);
void setTilesets(Tileset*, Tileset*);
uint16_t getSelectedMetatile();
protected:
void mousePressEvent(QGraphicsSceneMouseEvent*);
void mouseMoveEvent(QGraphicsSceneMouseEvent*);
void mouseReleaseEvent(QGraphicsSceneMouseEvent*);
void hoverMoveEvent(QGraphicsSceneHoverEvent*);
void hoverLeaveEvent(QGraphicsSceneHoverEvent*);
private:
Tileset *primaryTileset = nullptr;
Tileset *secondaryTileset = nullptr;
uint16_t selectedMetatile;
int numMetatilesWide;
void updateSelectedMetatile();
uint16_t getMetatileId(int x, int y);
QPoint getMetatileIdCoords(uint16_t);
uint16_t getValidMetatileId(uint16_t);
signals:
void hoveredMetatileChanged(uint16_t);
void hoveredMetatileCleared();
void selectedMetatileChanged(uint16_t);
};
#endif // TILESETEDITORMETATILESELECTOR_H

View file

@ -0,0 +1,52 @@
#ifndef TILESETEDITORTILESELECTOR_H
#define TILESETEDITORTILESELECTOR_H
#include "selectablepixmapitem.h"
#include "tileset.h"
class TilesetEditorTileSelector: public SelectablePixmapItem {
Q_OBJECT
public:
TilesetEditorTileSelector(Tileset *primaryTileset, Tileset *secondaryTileset): SelectablePixmapItem(16, 16, 2, 2) {
this->primaryTileset = primaryTileset;
this->secondaryTileset = secondaryTileset;
this->numTilesWide = 16;
this->paletteId = 0;
this->xFlip = false;
this->yFlip = false;
setAcceptHoverEvents(true);
}
void draw();
void select(uint16_t metatileId);
void setTilesets(Tileset*, Tileset*);
void setPaletteId(int);
void setTileFlips(bool, bool);
QList<uint16_t> getSelectedTiles();
protected:
void mousePressEvent(QGraphicsSceneMouseEvent*);
void mouseMoveEvent(QGraphicsSceneMouseEvent*);
void mouseReleaseEvent(QGraphicsSceneMouseEvent*);
void hoverMoveEvent(QGraphicsSceneHoverEvent*);
void hoverLeaveEvent(QGraphicsSceneHoverEvent*);
private:
Tileset *primaryTileset;
Tileset *secondaryTileset;
QList<uint16_t> selectedTiles;
int numTilesWide;
int paletteId;
bool xFlip;
bool yFlip;
void updateSelectedTiles();
uint16_t getTileId(int x, int y);
QPoint getTileCoords(uint16_t);
QList<QRgb> getCurPaletteTable();
signals:
void hoveredTileChanged(uint16_t);
void hoveredTileCleared();
void selectedTilesChanged();
};
#endif // TILESETEDITORTILESELECTOR_H

View file

@ -35,13 +35,17 @@ SOURCES += src/core/block.cpp \
src/ui/imageproviders.cpp \
src/ui/mappixmapitem.cpp \
src/ui/mapsceneeventfilter.cpp \
src/ui/metatilelayersitem.cpp \
src/ui/metatileselector.cpp \
src/ui/movementpermissionsselector.cpp \
src/ui/neweventtoolbutton.cpp \
src/ui/noscrollcombobox.cpp \
src/ui/noscrollspinbox.cpp \
src/ui/paletteeditor.cpp \
src/ui/selectablepixmapitem.cpp \
src/ui/tileseteditor.cpp \
src/ui/tileseteditormetatileselector.cpp \
src/ui/tileseteditortileselector.cpp \
src/editor.cpp \
src/main.cpp \
src/mainwindow.cpp \
@ -71,13 +75,17 @@ HEADERS += include/core/block.h \
include/ui/imageproviders.h \
include/ui/mappixmapitem.h \
include/ui/mapsceneeventfilter.h \
include/ui/metatilelayersitem.h \
include/ui/metatileselector.h \
include/ui/movementpermissionsselector.h \
include/ui/neweventtoolbutton.h \
include/ui/noscrollcombobox.h \
include/ui/noscrollspinbox.h \
include/ui/paletteeditor.h \
include/ui/selectablepixmapitem.h \
include/ui/tileseteditor.h \
include/ui/tileseteditormetatileselector.h \
include/ui/tileseteditortileselector.h \
include/editor.h \
include/mainwindow.h \
include/project.h \
@ -85,7 +93,8 @@ HEADERS += include/core/block.h \
FORMS += forms/mainwindow.ui \
forms/eventpropertiesframe.ui \
forms/tileseteditor.ui
forms/tileseteditor.ui \
forms/paletteeditor.ui
RESOURCES += \
resources/images.qrc

View file

@ -226,7 +226,7 @@ QPixmap Map::renderBorder() {
}
QPixmap Map::renderConnection(MapConnection connection) {
render();
render(true);
int x, y, w, h;
if (connection.direction == "up") {
x = 0;

View file

@ -7,6 +7,17 @@ Metatile::Metatile()
tiles = new QList<Tile>;
}
Metatile* Metatile::copy() {
Metatile *copy = new Metatile;
copy->behavior = this->behavior;
copy->layerType = this->layerType;
copy->tiles = new QList<Tile>;
for (Tile tile : *this->tiles) {
copy->tiles->append(tile);
}
return copy;
}
int Metatile::getBlockIndex(int index) {
if (index < Project::getNumMetatilesPrimary()) {
return index;

View file

@ -11,6 +11,43 @@ Tileset::Tileset()
}
Tileset* Tileset::copy() {
Tileset *copy = new Tileset;
copy->name = this->name;
copy->is_compressed = this->is_compressed;
copy->is_secondary = this->is_secondary;
copy->padding = this->padding;
copy->tiles_label = this->tiles_label;
copy->palettes_label = this->palettes_label;
copy->metatiles_label = this->metatiles_label;
copy->metatiles_path = this->metatiles_path;
copy->callback_label = this->callback_label;
copy->metatile_attrs_label = this->metatile_attrs_label;
copy->metatile_attrs_path = this->metatile_attrs_path;
copy->tilesImage = this->tilesImage.copy();
copy->tilesImagePath = this->tilesImagePath;
for (int i = 0; i < this->palettePaths.length(); i++) {
copy->palettePaths.append(this->palettePaths.at(i));
}
copy->tiles = new QList<QImage>;
for (QImage tile : *this->tiles) {
copy->tiles->append(tile.copy());
}
copy->metatiles = new QList<Metatile*>;
for (Metatile *metatile : *this->metatiles) {
copy->metatiles->append(metatile->copy());
}
copy->palettes = new QList<QList<QRgb>>;
for (QList<QRgb> palette : *this->palettes) {
QList<QRgb> copyPalette;
for (QRgb color : palette) {
copyPalette.append(color);
}
copy->palettes->append(copyPalette);
}
return copy;
}
Tileset* Tileset::getBlockTileset(int metatile_index, Tileset *primaryTileset, Tileset *secondaryTileset) {
if (metatile_index < Project::getNumMetatilesPrimary()) {
return primaryTileset;
@ -39,3 +76,14 @@ QList<QList<QRgb>> Tileset::getBlockPalettes(Tileset *primaryTileset, Tileset *s
}
return palettes;
}
QList<QRgb> Tileset::getPalette(int paletteId, Tileset *primaryTileset, Tileset *secondaryTileset) {
QList<QRgb> paletteTable;
Tileset *tileset = paletteId < Project::getNumPalettesPrimary()
? primaryTileset
: secondaryTileset;
for (int i = 0; i < tileset->palettes->at(paletteId).length(); i++) {
paletteTable.append(tileset->palettes->at(paletteId).at(i));
}
return paletteTable;
}

View file

@ -961,22 +961,22 @@ void Editor::updateDiveEmergeMap(QString mapName, QString direction) {
ui->label_NumConnections->setText(QString::number(map->connections.length()));
}
void Editor::updatePrimaryTileset(QString tilesetLabel)
void Editor::updatePrimaryTileset(QString tilesetLabel, bool forceLoad)
{
if (map->layout->tileset_primary_label != tilesetLabel)
if (map->layout->tileset_primary_label != tilesetLabel || forceLoad)
{
map->layout->tileset_primary_label = tilesetLabel;
map->layout->tileset_primary = project->getTileset(tilesetLabel);
map->layout->tileset_primary = project->getTileset(tilesetLabel, forceLoad);
emit tilesetChanged(map->name);
}
}
void Editor::updateSecondaryTileset(QString tilesetLabel)
void Editor::updateSecondaryTileset(QString tilesetLabel, bool forceLoad)
{
if (map->layout->tileset_secondary_label != tilesetLabel)
if (map->layout->tileset_secondary_label != tilesetLabel || forceLoad)
{
map->layout->tileset_secondary_label = tilesetLabel;
map->layout->tileset_secondary = project->getTileset(tilesetLabel);
map->layout->tileset_secondary = project->getTileset(tilesetLabel, forceLoad);
emit tilesetChanged(map->name);
}
}
@ -987,6 +987,12 @@ void Editor::toggleBorderVisibility(bool visible)
this->setConnectionsVisibility(visible);
}
Tileset* Editor::getCurrentMapPrimaryTileset()
{
QString tilesetLabel = map->layout->tileset_primary_label;
return project->getTileset(tilesetLabel);
}
void DraggablePixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *mouse) {
active = true;
last_x = static_cast<int>(mouse->pos().x() + this->pos().x()) / 16;

View file

@ -50,7 +50,7 @@ MainWindow::~MainWindow()
}
void MainWindow::initExtraShortcuts() {
new QShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Z), this, SLOT(redo()));
new QShortcut(QKeySequence("Ctrl+Shift+Z"), this, SLOT(redo()));
new QShortcut(QKeySequence("Ctrl+0"), this, SLOT(resetMapViewScale()));
ui->actionZoom_In->setShortcuts({QKeySequence("Ctrl++"), QKeySequence("Ctrl+=")});
}
@ -200,6 +200,7 @@ void MainWindow::setMap(QString map_name, bool scrollTreeView) {
setRecentMap(map_name);
updateMapList();
updateTilesetEditor();
}
void MainWindow::redrawMapScene()
@ -410,6 +411,7 @@ void MainWindow::loadDataStructures() {
project->readSecretBaseIds();
project->readBgEventFacingDirections();
project->readMapsWithConnections();
project->readMetatileBehaviors();
project->readTilesetProperties();
}
@ -525,6 +527,12 @@ void MainWindow::onTilesetChanged(QString mapName)
setMap(mapName);
}
void MainWindow::updateTilesetEditor() {
if (this->tilesetEditor) {
this->tilesetEditor->setTilesets(editor->ui->comboBox_PrimaryTileset->currentText(), editor->ui->comboBox_SecondaryTileset->currentText());
}
}
void MainWindow::currentMetatilesSelectionChanged()
{
ui->graphicsView_currentMetatileSelection->setFixedSize(editor->scene_current_metatile_selection_item->pixmap().width() + 2, editor->scene_current_metatile_selection_item->pixmap().height() + 2);
@ -1099,6 +1107,11 @@ void MainWindow::onMapNeedsRedrawing() {
redrawMapScene();
}
void MainWindow::onTilesetsSaved(QString primaryTilesetLabel, QString secondaryTilesetLabel) {
this->editor->updatePrimaryTileset(primaryTilesetLabel, true);
this->editor->updateSecondaryTileset(secondaryTilesetLabel, true);
}
void MainWindow::on_action_Export_Map_Image_triggered()
{
QString defaultFilepath = QString("%1/%2.png").arg(editor->project->root).arg(editor->map->name);
@ -1223,3 +1236,27 @@ void MainWindow::on_checkBox_ToggleBorder_stateChanged(int selected)
bool visible = selected != 0;
editor->toggleBorderVisibility(visible);
}
void MainWindow::on_actionTileset_Editor_triggered()
{
if (!this->tilesetEditor) {
this->tilesetEditor = new TilesetEditor(this->editor->project, this->editor->map->layout->tileset_primary_label, this->editor->map->layout->tileset_secondary_label, this);
connect(this->tilesetEditor, SIGNAL(tilesetsSaved(QString, QString)), this, SLOT(onTilesetsSaved(QString, QString)));
connect(this->tilesetEditor, SIGNAL(closed()), this, SLOT(onTilesetEditorClosed()));
}
if (!this->tilesetEditor->isVisible()) {
this->tilesetEditor->show();
} else if (this->tilesetEditor->isMinimized()) {
this->tilesetEditor->showNormal();
} else {
this->tilesetEditor->activateWindow();
}
}
void MainWindow::onTilesetEditorClosed() {
if (this->tilesetEditor) {
delete this->tilesetEditor;
this->tilesetEditor = nullptr;
}
}

View file

@ -576,6 +576,77 @@ void Project::saveHealLocationStruct(Map *map) {
saveTextFile(root + "/include/constants/heal_locations.h", constants_text);
}
void Project::saveTilesets(Tileset *primaryTileset, Tileset *secondaryTileset) {
saveTilesetMetatileAttributes(primaryTileset);
saveTilesetMetatileAttributes(secondaryTileset);
saveTilesetMetatiles(primaryTileset);
saveTilesetMetatiles(secondaryTileset);
saveTilesetTilesImage(primaryTileset);
saveTilesetTilesImage(secondaryTileset);
saveTilesetPalettes(primaryTileset, true);
saveTilesetPalettes(secondaryTileset, false);
}
void Project::saveTilesetMetatileAttributes(Tileset *tileset) {
QFile attrs_file(tileset->metatile_attrs_path);
if (attrs_file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
QByteArray data;
for (Metatile *metatile : *tileset->metatiles) {
data.append(static_cast<char>(metatile->behavior));
data.append(static_cast<char>((metatile->layerType << 4) & 0xF0));
}
attrs_file.write(data);
} else {
qDebug() << 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;
for (Metatile *metatile : *tileset->metatiles) {
for (int i = 0; i < 8; i++) {
Tile tile = metatile->tiles->at(i);
uint16_t value = static_cast<uint16_t>((tile.tile & 0x3ff)
| ((tile.xflip & 1) << 10)
| ((tile.yflip & 1) << 11)
| ((tile.palette & 0xf) << 12));
data.append(static_cast<char>(value & 0xff));
data.append(static_cast<char>((value >> 8) & 0xff));
}
}
metatiles_file.write(data);
} else {
tileset->metatiles = new QList<Metatile*>;
qDebug() << QString("Could not open tileset metatiles file '%1'").arg(tileset->metatiles_path);
}
}
void Project::saveTilesetTilesImage(Tileset *tileset) {
tileset->tilesImage.save(tileset->tilesImagePath);
}
void Project::saveTilesetPalettes(Tileset *tileset, bool primary) {
int startPaletteId = primary ? 0 : Project::getNumPalettesPrimary();
int endPaletteId = primary ? Project::getNumPalettesPrimary() : Project::getNumPalettesTotal();
for (int i = startPaletteId; i < endPaletteId; i++) {
QString filepath = tileset->palettePaths.at(i);
QString content = "JASC-PAL\r\n";
content += "0100\r\n";
content += "16\r\n";
for (int j = 0; j < 16; j++) {
QRgb color = tileset->palettes->at(i).at(j);
content += QString("%1 %2 %3\r\n")
.arg(qRed(color))
.arg(qGreen(color))
.arg(qBlue(color));
}
saveTextFile(filepath, content);
}
}
void Project::loadMapTilesets(Map* map) {
if (map->layout->has_unsaved_changes) {
return;
@ -585,12 +656,14 @@ void Project::loadMapTilesets(Map* map) {
map->layout->tileset_secondary = getTileset(map->layout->tileset_secondary_label);
}
Tileset* Project::loadTileset(QString label) {
Tileset* Project::loadTileset(QString label, Tileset *tileset) {
ParseUtil *parser = new ParseUtil;
QString headers_text = readTextFile(root + "/data/tilesets/headers.inc");
QStringList *values = getLabelValues(parser->parseAsm(headers_text), label);
Tileset *tileset = new Tileset;
if (tileset == nullptr) {
tileset = new Tileset;
}
tileset->name = label;
tileset->is_compressed = values->value(0);
tileset->is_secondary = values->value(1);
@ -750,7 +823,8 @@ void Project::loadTilesetAssets(Tileset* tileset) {
if (tileset->name.isNull()) {
return;
}
QString dir_path = root + "/data/tilesets/" + category + "/" + tileset->name.replace("gTileset_", "").toLower();
QString tilesetName = tileset->name;
QString dir_path = root + "/data/tilesets/" + category + "/" + tilesetName.replace("gTileset_", "").toLower();
QString graphics_text = readTextFile(root + "/data/tilesets/graphics.inc");
QList<QStringList> *graphics = parser->parseAsm(graphics_text);
@ -767,53 +841,94 @@ void Project::loadTilesetAssets(Tileset* tileset) {
}
}
QStringList *palette_paths = new QStringList;
if (!palettes_values->isEmpty()) {
for (int i = 0; i < palettes_values->length(); i++) {
QString value = palettes_values->value(i);
palette_paths->append(root + "/" + value.section('"', 1, 1));
tileset->palettePaths.append(this->fixPalettePath(root + "/" + value.section('"', 1, 1)));
}
} else {
QString palettes_dir_path = dir_path + "/palettes";
for (int i = 0; i < 16; i++) {
palette_paths->append(palettes_dir_path + "/" + QString("%1").arg(i, 2, 10, QLatin1Char('0')) + ".gbapal");
tileset->palettePaths.append(palettes_dir_path + "/" + QString("%1").arg(i, 2, 10, QLatin1Char('0')) + ".pal");
}
}
QString metatiles_path;
QString metatile_attrs_path;
QString metatiles_text = readTextFile(root + "/data/tilesets/metatiles.inc");
QList<QStringList> *metatiles_macros = parser->parseAsm(metatiles_text);
QStringList *metatiles_values = getLabelValues(metatiles_macros, tileset->metatiles_label);
if (!metatiles_values->isEmpty()) {
metatiles_path = root + "/" + metatiles_values->value(0).section('"', 1, 1);
tileset->metatiles_path = root + "/" + metatiles_values->value(0).section('"', 1, 1);
} else {
metatiles_path = dir_path + "/metatiles.bin";
tileset->metatiles_path = dir_path + "/metatiles.bin";
}
QStringList *metatile_attrs_values = getLabelValues(metatiles_macros, tileset->metatile_attrs_label);
if (!metatile_attrs_values->isEmpty()) {
metatile_attrs_path = root + "/" + metatile_attrs_values->value(0).section('"', 1, 1);
tileset->metatile_attrs_path = root + "/" + metatile_attrs_values->value(0).section('"', 1, 1);
} else {
metatile_attrs_path = dir_path + "/metatile_attributes.bin";
tileset->metatile_attrs_path = dir_path + "/metatile_attributes.bin";
}
// tiles
tiles_path = fixGraphicPath(tiles_path);
QImage *image = new QImage(tiles_path);
//image->setColor(0, qRgb(0xff, 0, 0)); // debug
tileset->tilesImagePath = tiles_path;
QImage image = QImage(tileset->tilesImagePath);
this->loadTilesetTiles(tileset, image);
this->loadTilesetMetatiles(tileset);
// palettes
QList<QList<QRgb>> *palettes = new QList<QList<QRgb>>;
for (int i = 0; i < tileset->palettePaths.length(); i++) {
QList<QRgb> palette;
QString path = tileset->palettePaths.value(i);
QString text = readTextFile(path);
if (!text.isNull()) {
QStringList lines = text.split(QRegExp("[\r\n]"),QString::SkipEmptyParts);
if (lines.length() == 19 && lines[0] == "JASC-PAL" && lines[1] == "0100" && lines[2] == "16") {
for (int j = 0; j < 16; j++) {
QStringList rgb = lines[j + 3].split(QRegExp(" "), QString::SkipEmptyParts);
if (rgb.length() != 3) {
qDebug() << QString("Invalid tileset palette RGB value: '%1'").arg(lines[j + 3]);
palette.append(qRgb((j - 3) * 16, (j - 3) * 16, (j - 3) * 16));
} else {
int red = rgb[0].toInt();
int green = rgb[1].toInt();
int blue = rgb[2].toInt();
QRgb color = qRgb(red, green, blue);
palette.append(color);
}
}
} else {
qDebug() << QString("Invalid JASC-PAL palette file for tileset.");
for (int j = 0; j < 16; j++) {
palette.append(qRgb(j * 16, j * 16, j * 16));
}
}
} else {
for (int j = 0; j < 16; j++) {
palette.append(qRgb(j * 16, j * 16, j * 16));
}
qDebug() << QString("Could not open palette path '%1'").arg(path);
}
palettes->append(palette);
}
tileset->palettes = palettes;
}
void Project::loadTilesetTiles(Tileset *tileset, QImage image) {
QList<QImage> *tiles = new QList<QImage>;
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);
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;
}
// metatiles
QFile metatiles_file(metatiles_path);
void Project::loadTilesetMetatiles(Tileset* tileset) {
QFile metatiles_file(tileset->metatiles_path);
if (metatiles_file.open(QIODevice::ReadOnly)) {
QByteArray data = metatiles_file.readAll();
int num_metatiles = data.length() / 16;
@ -837,11 +952,10 @@ void Project::loadTilesetAssets(Tileset* tileset) {
tileset->metatiles = metatiles;
} else {
tileset->metatiles = new QList<Metatile*>;
qDebug() << QString("Could not open '%1'").arg(metatiles_path);
qDebug() << QString("Could not open tileset metatiles file '%1'").arg(tileset->metatiles_path);
}
QFile attrs_file(metatile_attrs_path);
//qDebug() << metatile_attrs_path;
QFile attrs_file(tileset->metatile_attrs_path);
if (attrs_file.open(QIODevice::ReadOnly)) {
QByteArray data = attrs_file.readAll();
int num_metatiles = tileset->metatiles->count();
@ -851,41 +965,14 @@ void Project::loadTilesetAssets(Tileset* tileset) {
if (num_metatiles > num_metatileAttrs)
num_metatiles = num_metatileAttrs;
}
} else {
qDebug() << QString("Could not open '%1'").arg(metatile_attrs_path);
}
// palettes
QList<QList<QRgb>> *palettes = new QList<QList<QRgb>>;
for (int i = 0; i < palette_paths->length(); i++) {
QString path = palette_paths->value(i);
// the palettes are not compressed. this should never happen. it's only a precaution.
path = path.replace(QRegExp("\\.lz$"), "");
// TODO default to .pal (JASC-PAL)
// just use .gbapal for now
QFile file(path);
QList<QRgb> palette;
if (file.open(QIODevice::ReadOnly)) {
QByteArray data = file.readAll();
for (int j = 0; j < 16; j++) {
uint16_t word = data[j*2] & 0xff;
word += (data[j*2 + 1] & 0xff) << 8;
int red = word & 0x1f;
int green = (word >> 5) & 0x1f;
int blue = (word >> 10) & 0x1f;
QRgb color = qRgb(red * 8, green * 8, blue * 8);
palette.append(color);
}
} else {
for (int j = 0; j < 16; j++) {
palette.append(qRgb(j * 16, j * 16, j * 16));
}
qDebug() << QString("Could not open palette path '%1'").arg(path);
for (int i = 0; i < num_metatileAttrs; i++) {
int value = (static_cast<unsigned char>(data.at(i * 2 + 1)) << 8) | static_cast<unsigned char>(data.at(i * 2));
tileset->metatiles->at(i)->behavior = value & 0xFF;
tileset->metatiles->at(i)->layerType = (value & 0xF000) >> 12;
}
palettes->append(palette);
} else {
qDebug() << QString("Could not open tileset metatile attributes file '%1'").arg(tileset->metatile_attrs_path);
}
tileset->palettes = palettes;
}
Blockdata* Project::readBlockdata(QString path) {
@ -913,11 +1000,16 @@ Map* Project::getMap(QString map_name) {
}
}
Tileset* Project::getTileset(QString label) {
Tileset* Project::getTileset(QString label, bool forceLoad) {
Tileset *existingTileset = nullptr;
if (tileset_cache->contains(label)) {
existingTileset = tileset_cache->value(label);
}
if (existingTileset && !forceLoad) {
return tileset_cache->value(label);
} else {
Tileset *tileset = loadTileset(label);
Tileset *tileset = loadTileset(label, existingTileset);
return tileset;
}
}
@ -1249,6 +1341,22 @@ void Project::readBgEventFacingDirections() {
readCDefinesSorted(filepath, prefixes, bgEventFacingDirections);
}
void Project::readMetatileBehaviors() {
this->metatileBehaviorMap.clear();
this->metatileBehaviorMapInverse.clear();
QString filepath = root + "/include/constants/metatile_behaviors.h";
QString text = readTextFile(filepath);
if (!text.isNull()) {
QStringList prefixes = (QStringList() << "MB_");
this->metatileBehaviorMap = readCDefines(text, prefixes);
for (QString defineName : this->metatileBehaviorMap.keys()) {
this->metatileBehaviorMapInverse.insert(this->metatileBehaviorMap[defineName], defineName);
}
} else {
qDebug() << "Failed to read C defines file: " << filepath;
}
}
void Project::readCDefinesSorted(QString filepath, QStringList prefixes, QStringList* definesToSet) {
QString text = readTextFile(filepath);
if (!text.isNull()) {
@ -1324,6 +1432,11 @@ QMap<QString, int> Project::getEventObjGfxConstants() {
return constants;
}
QString Project::fixPalettePath(QString path) {
path = path.replace(QRegExp("\\.gbapal$"), ".pal");
return path;
}
QString Project::fixGraphicPath(QString path) {
path = path.replace(QRegExp("\\.lz$"), "");
path = path.replace(QRegExp("\\.[1248]bpp$"), ".png");

View file

@ -13,7 +13,7 @@ QImage getCollisionMetatileImage(int collision, int elevation) {
return collisionImage.toImage();
}
QImage getMetatileImage(int tile, Tileset *primaryTileset, Tileset *secondaryTileset) {
QImage getMetatileImage(uint16_t tile, Tileset *primaryTileset, Tileset *secondaryTileset) {
QImage metatile_image(16, 16, QImage::Format_RGBA8888);
Metatile* metatile = Tileset::getMetatile(tile, primaryTileset, secondaryTileset);
@ -71,7 +71,7 @@ QImage getMetatileImage(int tile, Tileset *primaryTileset, Tileset *secondaryTil
return metatile_image;
}
QImage getTileImage(int tile, Tileset *primaryTileset, Tileset *secondaryTileset) {
QImage getTileImage(uint16_t tile, Tileset *primaryTileset, Tileset *secondaryTileset) {
Tileset *tileset = Tileset::getBlockTileset(tile, primaryTileset, secondaryTileset);
int local_index = Metatile::getBlockIndex(tile);
if (!tileset || !tileset->tiles) {
@ -79,3 +79,19 @@ QImage getTileImage(int tile, Tileset *primaryTileset, Tileset *secondaryTileset
}
return tileset->tiles->value(local_index, QImage());
}
QImage getColoredTileImage(uint16_t tile, Tileset *primaryTileset, Tileset *secondaryTileset, int paletteId) {
QList<QRgb> palette = Tileset::getPalette(paletteId, primaryTileset, secondaryTileset);
QImage tileImage = getTileImage(tile, primaryTileset, secondaryTileset);
if (tileImage.isNull()) {
tileImage = QImage(16, 16, QImage::Format_RGBA8888);
QPainter painter(&tileImage);
painter.fillRect(0, 0, 16, 16, palette.at(0));
} else {
for (int i = 0; i < 16; i++) {
tileImage.setColor(i, palette.at(i));
}
}
return tileImage;
}

View file

@ -0,0 +1,45 @@
#include "metatilelayersitem.h"
#include "imageproviders.h"
#include <QPainter>
void MetatileLayersItem::draw() {
const QList<QPoint> tileCoords = QList<QPoint>{
QPoint(0, 0),
QPoint(16, 0),
QPoint(0, 16),
QPoint(16, 16),
QPoint(32, 0),
QPoint(48, 0),
QPoint(32, 16),
QPoint(48, 16),
};
QPixmap pixmap(64, 32);
QPainter painter(&pixmap);
for (int i = 0; i < 8; i++) {
Tile tile = this->metatile->tiles->at(i);
QImage tileImage = getColoredTileImage(tile.tile, this->primaryTileset, this->secondaryTileset, tile.palette)
.mirrored(tile.xflip, tile.yflip)
.scaled(16, 16);
painter.drawImage(tileCoords.at(i), tileImage);
}
this->setPixmap(pixmap);
}
void MetatileLayersItem::setMetatile(Metatile *metatile) {
this->metatile = metatile;
}
void MetatileLayersItem::setTilesets(Tileset *primaryTileset, Tileset *secondaryTileset) {
this->primaryTileset = primaryTileset;
this->secondaryTileset = secondaryTileset;
this->draw();
}
void MetatileLayersItem::mousePressEvent(QGraphicsSceneMouseEvent *event) {
QPointF pos = event->pos();
int x = static_cast<int>(pos.x()) / 16;
int y = static_cast<int>(pos.y()) / 16;
emit this->tileChanged(x, y);
}

176
src/ui/paletteeditor.cpp Normal file
View file

@ -0,0 +1,176 @@
#include "paletteeditor.h"
#include "ui_paletteeditor.h"
PaletteEditor::PaletteEditor(Project *project, Tileset *primaryTileset, Tileset *secondaryTileset, QWidget *parent) :
QMainWindow(parent),
ui(new Ui::PaletteEditor)
{
this->project = project;
this->primaryTileset = primaryTileset;
this->secondaryTileset = secondaryTileset;
this->ui->setupUi(this);
this->ui->spinBox_PaletteId->setMinimum(0);
this->ui->spinBox_PaletteId->setMaximum(Project::getNumPalettesTotal() - 1);
this->sliders.clear();
for (int i = 0; i < 16; i++) {
this->sliders.append(QList<QSlider*>());
}
this->sliders[0].append(this->ui->horizontalSlider);
this->sliders[0].append(this->ui->horizontalSlider_2);
this->sliders[0].append(this->ui->horizontalSlider_3);
this->sliders[1].append(this->ui->horizontalSlider_4);
this->sliders[1].append(this->ui->horizontalSlider_5);
this->sliders[1].append(this->ui->horizontalSlider_6);
this->sliders[2].append(this->ui->horizontalSlider_7);
this->sliders[2].append(this->ui->horizontalSlider_8);
this->sliders[2].append(this->ui->horizontalSlider_9);
this->sliders[3].append(this->ui->horizontalSlider_10);
this->sliders[3].append(this->ui->horizontalSlider_11);
this->sliders[3].append(this->ui->horizontalSlider_12);
this->sliders[4].append(this->ui->horizontalSlider_13);
this->sliders[4].append(this->ui->horizontalSlider_14);
this->sliders[4].append(this->ui->horizontalSlider_15);
this->sliders[5].append(this->ui->horizontalSlider_16);
this->sliders[5].append(this->ui->horizontalSlider_17);
this->sliders[5].append(this->ui->horizontalSlider_18);
this->sliders[6].append(this->ui->horizontalSlider_19);
this->sliders[6].append(this->ui->horizontalSlider_20);
this->sliders[6].append(this->ui->horizontalSlider_21);
this->sliders[7].append(this->ui->horizontalSlider_22);
this->sliders[7].append(this->ui->horizontalSlider_23);
this->sliders[7].append(this->ui->horizontalSlider_24);
this->sliders[8].append(this->ui->horizontalSlider_25);
this->sliders[8].append(this->ui->horizontalSlider_26);
this->sliders[8].append(this->ui->horizontalSlider_27);
this->sliders[9].append(this->ui->horizontalSlider_28);
this->sliders[9].append(this->ui->horizontalSlider_29);
this->sliders[9].append(this->ui->horizontalSlider_30);
this->sliders[10].append(this->ui->horizontalSlider_31);
this->sliders[10].append(this->ui->horizontalSlider_32);
this->sliders[10].append(this->ui->horizontalSlider_33);
this->sliders[11].append(this->ui->horizontalSlider_34);
this->sliders[11].append(this->ui->horizontalSlider_35);
this->sliders[11].append(this->ui->horizontalSlider_36);
this->sliders[12].append(this->ui->horizontalSlider_37);
this->sliders[12].append(this->ui->horizontalSlider_38);
this->sliders[12].append(this->ui->horizontalSlider_39);
this->sliders[13].append(this->ui->horizontalSlider_40);
this->sliders[13].append(this->ui->horizontalSlider_41);
this->sliders[13].append(this->ui->horizontalSlider_42);
this->sliders[14].append(this->ui->horizontalSlider_43);
this->sliders[14].append(this->ui->horizontalSlider_44);
this->sliders[14].append(this->ui->horizontalSlider_45);
this->sliders[15].append(this->ui->horizontalSlider_46);
this->sliders[15].append(this->ui->horizontalSlider_47);
this->sliders[15].append(this->ui->horizontalSlider_48);
this->frames.clear();
this->frames.append(this->ui->frame);
this->frames.append(this->ui->frame_2);
this->frames.append(this->ui->frame_3);
this->frames.append(this->ui->frame_4);
this->frames.append(this->ui->frame_5);
this->frames.append(this->ui->frame_6);
this->frames.append(this->ui->frame_7);
this->frames.append(this->ui->frame_8);
this->frames.append(this->ui->frame_9);
this->frames.append(this->ui->frame_10);
this->frames.append(this->ui->frame_11);
this->frames.append(this->ui->frame_12);
this->frames.append(this->ui->frame_13);
this->frames.append(this->ui->frame_14);
this->frames.append(this->ui->frame_15);
this->frames.append(this->ui->frame_16);
this->initColorSliders();
this->refreshColorSliders();
this->refreshColors();
}
PaletteEditor::~PaletteEditor()
{
delete ui;
}
void PaletteEditor::disableSliderSignals() {
for (int i = 0; i < this->sliders.length(); i++) {
this->sliders.at(i).at(0)->blockSignals(true);
this->sliders.at(i).at(1)->blockSignals(true);
this->sliders.at(i).at(2)->blockSignals(true);
}
}
void PaletteEditor::enableSliderSignals() {
for (int i = 0; i < this->sliders.length(); i++) {
this->sliders.at(i).at(0)->blockSignals(false);
this->sliders.at(i).at(1)->blockSignals(false);
this->sliders.at(i).at(2)->blockSignals(false);
}
}
void PaletteEditor::initColorSliders() {
for (int i = 0; i < 16; i++) {
connect(this->sliders[i][0], &QSlider::valueChanged, [=](int) { this->setColor(i); });
connect(this->sliders[i][1], &QSlider::valueChanged, [=](int) { this->setColor(i); });
connect(this->sliders[i][2], &QSlider::valueChanged, [=](int) { this->setColor(i); });
}
}
void PaletteEditor::refreshColorSliders() {
disableSliderSignals();
int paletteNum = this->ui->spinBox_PaletteId->value();
for (int i = 0; i < 16; i++) {
QRgb color;
if (paletteNum < Project::getNumPalettesPrimary()) {
color = this->primaryTileset->palettes->at(paletteNum).at(i);
} else {
color = this->secondaryTileset->palettes->at(paletteNum).at(i);
}
this->sliders[i][0]->setValue(qRed(color) / 8);
this->sliders[i][1]->setValue(qGreen(color) / 8);
this->sliders[i][2]->setValue(qBlue(color) / 8);
}
enableSliderSignals();
}
void PaletteEditor::refreshColors() {
for (int i = 0; i < 16; i++) {
this->refreshColor(i);
}
}
void PaletteEditor::refreshColor(int colorIndex) {
QString stylesheet = QString("background-color: rgb(%1, %2, %3);")
.arg(this->sliders[colorIndex][0]->value() * 8)
.arg(this->sliders[colorIndex][1]->value() * 8)
.arg(this->sliders[colorIndex][2]->value() * 8);
this->frames[colorIndex]->setStyleSheet(stylesheet);
}
void PaletteEditor::setPaletteId(int paletteId) {
this->ui->spinBox_PaletteId->blockSignals(true);
this->ui->spinBox_PaletteId->setValue(paletteId);
this->refreshColorSliders();
this->refreshColors();
this->ui->spinBox_PaletteId->blockSignals(false);
}
void PaletteEditor::setColor(int colorIndex) {
int paletteNum = this->ui->spinBox_PaletteId->value();
int red = this->sliders[colorIndex][0]->value() * 8;
int green = this->sliders[colorIndex][1]->value() * 8;
int blue = this->sliders[colorIndex][2]->value() * 8;
Tileset *tileset = paletteNum < Project::getNumPalettesPrimary()
? this->primaryTileset
: this->secondaryTileset;
(*tileset->palettes)[paletteNum][colorIndex] = qRgb(red, green, blue);
this->refreshColor(colorIndex);
emit this->changedPaletteColor();
}
void PaletteEditor::on_spinBox_PaletteId_valueChanged(int paletteId) {
this->refreshColorSliders();
this->refreshColors();
emit this->changedPalette(paletteId);
}

View file

@ -80,6 +80,10 @@ void SelectablePixmapItem::updateSelection(int x, int y)
QPoint SelectablePixmapItem::getCellPos(QPointF pos)
{
if (pos.x() < 0) pos.setX(0);
if (pos.y() < 0) pos.setY(0);
if (pos.x() >= this->pixmap().width()) pos.setX(this->pixmap().width() - 1);
if (pos.y() >= this->pixmap().height()) pos.setY(this->pixmap().height() - 1);
return QPoint(static_cast<int>(pos.x()) / this->cellWidth,
static_cast<int>(pos.y()) / this->cellHeight);
}

View file

@ -1,14 +1,460 @@
#include "tileseteditor.h"
#include "ui_tileseteditor.h"
#include "imageproviders.h"
#include <QFileDialog>
#include <QDebug>
#include <QMessageBox>
#include <QDialogButtonBox>
TilesetEditor::TilesetEditor(QWidget *parent) :
QDialog(parent),
TilesetEditor::TilesetEditor(Project *project, QString primaryTilesetLabel, QString secondaryTilesetLabel, QWidget *parent) :
QMainWindow(parent),
ui(new Ui::TilesetEditor)
{
ui->setupUi(this);
this->init(project, primaryTilesetLabel, secondaryTilesetLabel);
}
TilesetEditor::~TilesetEditor()
{
delete ui;
}
void TilesetEditor::init(Project *project, QString primaryTilesetLabel, QString secondaryTilesetLabel) {
ui->setupUi(this);
this->project = project;
this->hasUnsavedChanges = false;
this->tileXFlip = ui->checkBox_xFlip->isChecked();
this->tileYFlip = ui->checkBox_yFlip->isChecked();
this->paletteId = ui->spinBox_paletteSelector->value();
Tileset *primaryTileset = project->getTileset(primaryTilesetLabel);
Tileset *secondaryTileset = project->getTileset(secondaryTilesetLabel);
if (this->primaryTileset) delete this->primaryTileset;
if (this->secondaryTileset) delete this->secondaryTileset;
this->primaryTileset = primaryTileset->copy();
this->secondaryTileset = secondaryTileset->copy();
QList<QString> sortedBehaviors;
for (int num : project->metatileBehaviorMapInverse.keys()) {
this->ui->comboBox_metatileBehaviors->addItem(project->metatileBehaviorMapInverse[num], num);
}
this->ui->comboBox_layerType->addItem("Normal - Middle/Top", 0);
this->ui->comboBox_layerType->addItem("Covered - Bottom/Middle", 1);
this->ui->comboBox_layerType->addItem("Split - Bottom/Top", 2);
this->ui->spinBox_paletteSelector->setMinimum(0);
this->ui->spinBox_paletteSelector->setMaximum(Project::getNumPalettesTotal() - 1);
this->initMetatileSelector();
this->initMetatileLayersItem();
this->initTileSelector();
this->initSelectedTileItem();
this->metatileSelector->select(0);
}
void TilesetEditor::setTilesets(QString primaryTilesetLabel, QString secondaryTilesetLabel) {
delete this->primaryTileset;
delete this->secondaryTileset;
Tileset *primaryTileset = project->getTileset(primaryTilesetLabel);
Tileset *secondaryTileset = project->getTileset(secondaryTilesetLabel);
this->primaryTileset = primaryTileset->copy();
this->secondaryTileset = secondaryTileset->copy();
this->refresh();
}
void TilesetEditor::refresh() {
this->metatileSelector->setTilesets(this->primaryTileset, this->secondaryTileset);
this->tileSelector->setTilesets(this->primaryTileset, this->secondaryTileset);
this->metatileLayersItem->setTilesets(this->primaryTileset, this->secondaryTileset);
this->metatileSelector->select(this->metatileSelector->getSelectedMetatile());
this->drawSelectedTiles();
this->ui->graphicsView_Tiles->setSceneRect(0, 0, this->tileSelector->pixmap().width() + 2, this->tileSelector->pixmap().height() + 2);
this->ui->graphicsView_Tiles->setFixedSize(this->tileSelector->pixmap().width() + 2, this->tileSelector->pixmap().height() + 2);
this->ui->graphicsView_Metatiles->setSceneRect(0, 0, this->metatileSelector->pixmap().width() + 2, this->metatileSelector->pixmap().height() + 2);
this->ui->graphicsView_Metatiles->setFixedSize(this->metatileSelector->pixmap().width() + 2, this->metatileSelector->pixmap().height() + 2);
this->ui->graphicsView_selectedTile->setFixedSize(this->selectedTilePixmapItem->pixmap().width(), this->selectedTilePixmapItem->pixmap().height());
}
void TilesetEditor::initMetatileSelector()
{
this->metatileSelector = new TilesetEditorMetatileSelector(this->primaryTileset, this->secondaryTileset);
connect(this->metatileSelector, SIGNAL(hoveredMetatileChanged(uint16_t)),
this, SLOT(onHoveredMetatileChanged(uint16_t)));
connect(this->metatileSelector, SIGNAL(hoveredMetatileCleared()),
this, SLOT(onHoveredMetatileCleared()));
connect(this->metatileSelector, SIGNAL(selectedMetatileChanged(uint16_t)),
this, SLOT(onSelectedMetatileChanged(uint16_t)));
this->metatilesScene = new QGraphicsScene;
this->metatilesScene->addItem(this->metatileSelector);
this->metatileSelector->draw();
this->ui->graphicsView_Metatiles->setScene(this->metatilesScene);
this->ui->graphicsView_Metatiles->setFixedSize(this->metatileSelector->pixmap().width() + 2, this->metatileSelector->pixmap().height() + 2);
}
void TilesetEditor::initTileSelector()
{
this->tileSelector = new TilesetEditorTileSelector(this->primaryTileset, this->secondaryTileset);
connect(this->tileSelector, SIGNAL(hoveredTileChanged(uint16_t)),
this, SLOT(onHoveredTileChanged(uint16_t)));
connect(this->tileSelector, SIGNAL(hoveredTileCleared()),
this, SLOT(onHoveredTileCleared()));
connect(this->tileSelector, SIGNAL(selectedTilesChanged()),
this, SLOT(onSelectedTilesChanged()));
this->tilesScene = new QGraphicsScene;
this->tilesScene->addItem(this->tileSelector);
this->tileSelector->select(0);
this->tileSelector->draw();
this->ui->graphicsView_Tiles->setScene(this->tilesScene);
this->ui->graphicsView_Tiles->setFixedSize(this->tileSelector->pixmap().width() + 2, this->tileSelector->pixmap().height() + 2);
}
void TilesetEditor::initSelectedTileItem() {
this->selectedTileScene = new QGraphicsScene;
this->drawSelectedTiles();
this->ui->graphicsView_selectedTile->setScene(this->selectedTileScene);
this->ui->graphicsView_selectedTile->setFixedSize(this->selectedTilePixmapItem->pixmap().width(), this->selectedTilePixmapItem->pixmap().height());
}
void TilesetEditor::drawSelectedTiles() {
if (!this->selectedTileScene) {
return;
}
this->selectedTileScene->clear();
QList<uint16_t> tiles = this->tileSelector->getSelectedTiles();
QPoint dimensions = this->tileSelector->getSelectionDimensions();
QImage selectionImage(16 * dimensions.x(), 16 * dimensions.y(), QImage::Format_RGBA8888);
QPainter painter(&selectionImage);
int tileIndex = 0;
for (int j = 0; j < dimensions.y(); j++) {
for (int i = 0; i < dimensions.x(); i++) {
QImage tileImage = getColoredTileImage(tiles.at(tileIndex), this->primaryTileset, this->secondaryTileset, this->paletteId)
.mirrored(this->tileXFlip, this->tileYFlip)
.scaled(16, 16);
tileIndex++;
painter.drawImage(i * 16, j * 16, tileImage);
}
}
this->selectedTilePixmapItem = new QGraphicsPixmapItem(QPixmap::fromImage(selectionImage));
this->selectedTileScene->addItem(this->selectedTilePixmapItem);
this->ui->graphicsView_selectedTile->setFixedSize(this->selectedTilePixmapItem->pixmap().width(), this->selectedTilePixmapItem->pixmap().height());
}
void TilesetEditor::initMetatileLayersItem() {
Metatile *metatile = Tileset::getMetatile(this->metatileSelector->getSelectedMetatile(), this->primaryTileset, this->secondaryTileset);
this->metatileLayersItem = new MetatileLayersItem(metatile, this->primaryTileset, this->secondaryTileset);
connect(this->metatileLayersItem, SIGNAL(tileChanged(int, int)),
this, SLOT(onMetatileLayerTileChanged(int, int)));
this->metatileLayersScene = new QGraphicsScene;
this->metatileLayersScene->addItem(this->metatileLayersItem);
this->ui->graphicsView_metatileLayers->setScene(this->metatileLayersScene);
}
void TilesetEditor::onHoveredMetatileChanged(uint16_t metatileId) {
QString message = QString("Metatile: 0x%1")
.arg(QString("%1").arg(metatileId, 3, 16, QChar('0')).toUpper());
this->ui->statusbar->showMessage(message);
}
void TilesetEditor::onHoveredMetatileCleared() {
this->ui->statusbar->clearMessage();
}
void TilesetEditor::onSelectedMetatileChanged(uint16_t metatileId) {
this->metatile = Tileset::getMetatile(metatileId, this->primaryTileset, this->secondaryTileset);
this->metatileLayersItem->setMetatile(metatile);
this->metatileLayersItem->draw();
this->ui->comboBox_metatileBehaviors->setCurrentIndex(this->ui->comboBox_metatileBehaviors->findData(this->metatile->behavior));
this->ui->comboBox_layerType->setCurrentIndex(this->ui->comboBox_layerType->findData(this->metatile->layerType));
}
void TilesetEditor::onHoveredTileChanged(uint16_t tile) {
QString message = QString("Tile: 0x%1")
.arg(QString("%1").arg(tile, 3, 16, QChar('0')).toUpper());
this->ui->statusbar->showMessage(message);
}
void TilesetEditor::onHoveredTileCleared() {
this->ui->statusbar->clearMessage();
}
void TilesetEditor::onSelectedTilesChanged() {
this->drawSelectedTiles();
}
void TilesetEditor::onMetatileLayerTileChanged(int x, int y) {
int maxTileIndex = x < 2 ? 3 : 7;
QPoint dimensions = this->tileSelector->getSelectionDimensions();
QList<uint16_t> tiles = this->tileSelector->getSelectedTiles();
int selectedTileIndex = 0;
for (int j = 0; j < dimensions.y(); j++) {
for (int i = 0; i < dimensions.x(); i++) {
int tileIndex = ((x + i) / 2 * 4) + ((y + j) * 2) + ((x + i) % 2);
if (tileIndex <= maxTileIndex) {
Tile tile = this->metatile->tiles->at(tileIndex);
tile.tile = tiles.at(selectedTileIndex);
tile.xflip = this->tileXFlip;
tile.yflip = this->tileYFlip;
tile.palette = this->paletteId;
(*this->metatile->tiles)[tileIndex] = tile;
}
selectedTileIndex++;
}
}
this->metatileSelector->draw();
this->metatileLayersItem->draw();
this->hasUnsavedChanges = true;
}
void TilesetEditor::on_spinBox_paletteSelector_valueChanged(int paletteId)
{
this->ui->spinBox_paletteSelector->blockSignals(true);
this->ui->spinBox_paletteSelector->setValue(paletteId);
this->ui->spinBox_paletteSelector->blockSignals(false);
this->paletteId = paletteId;
this->tileSelector->setPaletteId(paletteId);
this->drawSelectedTiles();
if (this->paletteEditor) {
this->paletteEditor->setPaletteId(paletteId);
}
}
void TilesetEditor::on_checkBox_xFlip_stateChanged(int checked)
{
this->tileXFlip = checked;
this->tileSelector->setTileFlips(this->tileXFlip, this->tileYFlip);
this->drawSelectedTiles();
}
void TilesetEditor::on_checkBox_yFlip_stateChanged(int checked)
{
this->tileYFlip = checked;
this->tileSelector->setTileFlips(this->tileXFlip, this->tileYFlip);
this->drawSelectedTiles();
}
void TilesetEditor::on_comboBox_metatileBehaviors_currentIndexChanged(const QString &metatileBehavior)
{
if (this->metatile) {
this->metatile->behavior = static_cast<uint8_t>(project->metatileBehaviorMap[metatileBehavior]);
}
}
void TilesetEditor::on_comboBox_layerType_currentIndexChanged(int layerType)
{
if (this->metatile) {
this->metatile->layerType = static_cast<uint8_t>(layerType);
}
}
void TilesetEditor::on_actionSave_Tileset_triggered()
{
this->project->saveTilesets(this->primaryTileset, this->secondaryTileset);
emit this->tilesetsSaved(this->primaryTileset->name, this->secondaryTileset->name);
this->ui->statusbar->showMessage(QString("Saved primary and secondary Tilesets!"), 5000);
this->hasUnsavedChanges = false;
}
void TilesetEditor::on_actionImport_Primary_Tiles_triggered()
{
this->importTilesetTiles(this->primaryTileset, true);
}
void TilesetEditor::on_actionImport_Secondary_Tiles_triggered()
{
this->importTilesetTiles(this->secondaryTileset, false);
}
void TilesetEditor::importTilesetTiles(Tileset *tileset, bool primary) {
QString descriptor = primary ? "primary" : "secondary";
QString descriptorCaps = primary ? "Primary" : "Secondary";
QString filepath = QFileDialog::getOpenFileName(
this,
QString("Import %1 Tileset Tiles Image").arg(descriptorCaps),
this->project->root,
"Image Files (*.png)");
if (filepath.isEmpty()) {
return;
}
qDebug() << QString("Importing %1 tileset tiles '%2'").arg(descriptor).arg(filepath);
// Validate image dimensions.
QImage image = QImage(filepath);
if (image.width() == 0 || image.height() == 0 || image.width() % 8 != 0 || image.height() % 8 != 0) {
QMessageBox msgBox(this);
msgBox.setText("Failed to import tiles.");
msgBox.setInformativeText(QString("The image dimensions (%1 x %2) are invalid. Width and height must be multiples of 8 pixels.")
.arg(image.width())
.arg(image.height()));
msgBox.setDefaultButton(QMessageBox::Ok);
msgBox.setIcon(QMessageBox::Icon::Critical);
msgBox.exec();
return;
}
// Validate image is properly indexed to 16 colors.
if (image.colorCount() != 16) {
QMessageBox msgBox(this);
msgBox.setText("Failed to import tiles.");
msgBox.setInformativeText(QString("The image must be indexed and contain 16 total colors. The provided image has %1 indexed colors.")
.arg(image.colorCount()));
msgBox.setDefaultButton(QMessageBox::Ok);
msgBox.setIcon(QMessageBox::Icon::Critical);
msgBox.exec();
return;
}
// Validate total number of tiles in image.
int numTilesWide = image.width() / 16;
int numTilesHigh = image.height() / 16;
int totalTiles = numTilesHigh * numTilesWide;
int maxAllowedTiles = primary ? Project::getNumTilesPrimary() : Project::getNumTilesTotal() - Project::getNumTilesPrimary();
if (totalTiles > maxAllowedTiles) {
QMessageBox msgBox(this);
msgBox.setText("Failed to import tiles.");
msgBox.setInformativeText(QString("The maximum number of tiles allowed in the %1 tileset is %2, but the provided image contains %3 total tiles.")
.arg(descriptor)
.arg(maxAllowedTiles)
.arg(totalTiles));
msgBox.setDefaultButton(QMessageBox::Ok);
msgBox.setIcon(QMessageBox::Icon::Critical);
msgBox.exec();
return;
}
this->project->loadTilesetTiles(tileset, image);
this->project->loadTilesetMetatiles(tileset);
this->refresh();
this->hasUnsavedChanges = true;
}
void TilesetEditor::closeEvent(QCloseEvent *event)
{
bool close = true;
if (this->hasUnsavedChanges) {
QMessageBox::StandardButton result = QMessageBox::question(this, "porymap",
"Discard unsaved Tileset changes?",
QMessageBox::No | QMessageBox::Yes,
QMessageBox::Yes);
close = result == QMessageBox::Yes;
}
if (close) {
event->accept();
emit closed();
} else {
event->ignore();
}
}
void TilesetEditor::on_actionChange_Metatiles_Count_triggered()
{
QDialog dialog(this, Qt::WindowTitleHint | Qt::WindowCloseButtonHint);
dialog.setWindowTitle("Change Number of Metatiles");
dialog.setWindowModality(Qt::NonModal);
QFormLayout form(&dialog);
QSpinBox *primarySpinBox = new QSpinBox();
QSpinBox *secondarySpinBox = new QSpinBox();
primarySpinBox->setMinimum(1);
secondarySpinBox->setMinimum(1);
primarySpinBox->setMaximum(Project::getNumMetatilesPrimary());
secondarySpinBox->setMaximum(Project::getNumMetatilesTotal() - Project::getNumMetatilesPrimary());
primarySpinBox->setValue(this->primaryTileset->metatiles->length());
secondarySpinBox->setValue(this->secondaryTileset->metatiles->length());
form.addRow(new QLabel("Primary Tileset"), primarySpinBox);
form.addRow(new QLabel("Secondary Tileset"), secondarySpinBox);
QDialogButtonBox buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dialog);
connect(&buttonBox, SIGNAL(accepted()), &dialog, SLOT(accept()));
connect(&buttonBox, SIGNAL(rejected()), &dialog, SLOT(reject()));
form.addRow(&buttonBox);
if (dialog.exec() == QDialog::Accepted) {
int numPrimaryMetatiles = primarySpinBox->value();
int numSecondaryMetatiles = secondarySpinBox->value();
while (this->primaryTileset->metatiles->length() > numPrimaryMetatiles) {
Metatile *metatile = this->primaryTileset->metatiles->takeLast();
delete metatile;
}
while (this->primaryTileset->metatiles->length() < numPrimaryMetatiles) {
Tile tile;
tile.palette = 0;
tile.tile = 0;
tile.xflip = 0;
tile.yflip = 0;
Metatile *metatile = new Metatile;
metatile->behavior = 0;
metatile->layerType = 0;
for (int i = 0; i < 8; i++) {
metatile->tiles->append(tile);
}
this->primaryTileset->metatiles->append(metatile);
}
while (this->secondaryTileset->metatiles->length() > numSecondaryMetatiles) {
Metatile *metatile = this->secondaryTileset->metatiles->takeLast();
delete metatile;
}
while (this->secondaryTileset->metatiles->length() < numSecondaryMetatiles) {
Tile tile;
tile.palette = 0;
tile.tile = 0;
tile.xflip = 0;
tile.yflip = 0;
Metatile *metatile = new Metatile;
metatile->behavior = 0;
metatile->layerType = 0;
for (int i = 0; i < 8; i++) {
metatile->tiles->append(tile);
}
this->secondaryTileset->metatiles->append(metatile);
}
this->refresh();
this->hasUnsavedChanges = true;
}
}
void TilesetEditor::onPaletteEditorClosed() {
if (this->paletteEditor) {
delete this->paletteEditor;
this->paletteEditor = nullptr;
}
}
void TilesetEditor::on_actionChange_Palettes_triggered()
{
if (!this->paletteEditor) {
this->paletteEditor = new PaletteEditor(this->project, this->primaryTileset, this->secondaryTileset, this);
connect(this->paletteEditor, SIGNAL(closed()), this, SLOT(onPaletteEditorClosed()));
connect(this->paletteEditor, SIGNAL(changedPaletteColor()), this, SLOT(onPaletteEditorChangedPaletteColor()));
connect(this->paletteEditor, SIGNAL(changedPalette(int)), this, SLOT(onPaletteEditorChangedPalette(int)));
}
if (!this->paletteEditor->isVisible()) {
this->paletteEditor->show();
} else if (this->paletteEditor->isMinimized()) {
this->paletteEditor->showNormal();
} else {
this->paletteEditor->activateWindow();
}
}
void TilesetEditor::onPaletteEditorChangedPaletteColor() {
this->refresh();
this->hasUnsavedChanges = true;
}
void TilesetEditor::onPaletteEditorChangedPalette(int paletteId) {
this->on_spinBox_paletteSelector_valueChanged(paletteId);
}

View file

@ -0,0 +1,114 @@
#include "tileseteditormetatileselector.h"
#include "imageproviders.h"
#include "project.h"
#include <QPainter>
void TilesetEditorMetatileSelector::draw() {
if (!this->primaryTileset || !this->primaryTileset->metatiles
|| !this->secondaryTileset || !this->secondaryTileset->metatiles) {
this->setPixmap(QPixmap());
}
int primaryLength = this->primaryTileset->metatiles->length();
int length_ = primaryLength + this->secondaryTileset->metatiles->length();
int height_ = length_ / this->numMetatilesWide;
QImage image(this->numMetatilesWide * 32, height_ * 32, QImage::Format_RGBA8888);
QPainter painter(&image);
for (int i = 0; i < length_; i++) {
int tile = i;
if (i >= primaryLength) {
tile += Project::getNumMetatilesPrimary() - primaryLength;
}
QImage metatile_image = getMetatileImage(tile, this->primaryTileset, this->secondaryTileset).scaled(32, 32);
int map_y = i / this->numMetatilesWide;
int map_x = i % this->numMetatilesWide;
QPoint metatile_origin = QPoint(map_x * 32, map_y * 32);
painter.drawImage(metatile_origin, metatile_image);
}
painter.end();
this->setPixmap(QPixmap::fromImage(image));
this->drawSelection();
}
void TilesetEditorMetatileSelector::select(uint16_t metatileId) {
metatileId = this->getValidMetatileId(metatileId);
QPoint coords = this->getMetatileIdCoords(metatileId);
SelectablePixmapItem::select(coords.x(), coords.y(), 0, 0);
this->selectedMetatile = metatileId;
emit selectedMetatileChanged(metatileId);
}
void TilesetEditorMetatileSelector::setTilesets(Tileset *primaryTileset, Tileset *secondaryTileset) {
this->primaryTileset = primaryTileset;
this->secondaryTileset = secondaryTileset;
this->draw();
}
void TilesetEditorMetatileSelector::updateSelectedMetatile() {
QPoint origin = this->getSelectionStart();
this->selectedMetatile = this->getMetatileId(origin.x(), origin.y());
}
uint16_t TilesetEditorMetatileSelector::getSelectedMetatile() {
return this->selectedMetatile;
}
uint16_t TilesetEditorMetatileSelector::getMetatileId(int x, int y) {
int index = y * this->numMetatilesWide + x;
if (index < this->primaryTileset->metatiles->length()) {
return static_cast<uint16_t>(index);
} else {
return static_cast<uint16_t>(Project::getNumMetatilesPrimary() + index - this->primaryTileset->metatiles->length());
}
}
void TilesetEditorMetatileSelector::mousePressEvent(QGraphicsSceneMouseEvent *event) {
SelectablePixmapItem::mousePressEvent(event);
this->updateSelectedMetatile();
emit selectedMetatileChanged(this->selectedMetatile);
}
void TilesetEditorMetatileSelector::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
SelectablePixmapItem::mouseMoveEvent(event);
this->updateSelectedMetatile();
QPoint pos = this->getCellPos(event->pos());
uint16_t metatileId = this->getMetatileId(pos.x(), pos.y());
emit hoveredMetatileChanged(metatileId);
emit selectedMetatileChanged(metatileId);
}
void TilesetEditorMetatileSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
SelectablePixmapItem::mouseReleaseEvent(event);
this->updateSelectedMetatile();
emit selectedMetatileChanged(this->selectedMetatile);
}
void TilesetEditorMetatileSelector::hoverMoveEvent(QGraphicsSceneHoverEvent *event) {
QPoint pos = this->getCellPos(event->pos());
uint16_t metatileId = this->getMetatileId(pos.x(), pos.y());
emit this->hoveredMetatileChanged(metatileId);
}
void TilesetEditorMetatileSelector::hoverLeaveEvent(QGraphicsSceneHoverEvent*) {
emit this->hoveredMetatileCleared();
}
QPoint TilesetEditorMetatileSelector::getMetatileIdCoords(uint16_t metatileId) {
int index = metatileId < Project::getNumMetatilesPrimary()
? metatileId
: metatileId - Project::getNumMetatilesPrimary() + this->primaryTileset->metatiles->length();
return QPoint(index % this->numMetatilesWide, index / this->numMetatilesWide);
}
uint16_t TilesetEditorMetatileSelector::getValidMetatileId(uint16_t metatileId) {
if (metatileId >= Project::getNumMetatilesTotal()
|| (metatileId < Project::getNumMetatilesPrimary() && metatileId >= this->primaryTileset->metatiles->length())
|| (metatileId < Project::getNumMetatilesTotal() && metatileId >= Project::getNumMetatilesPrimary() + this->secondaryTileset->metatiles->length()))
{
return 0;
}
return metatileId;
}

View file

@ -0,0 +1,130 @@
#include "tileseteditortileselector.h"
#include "imageproviders.h"
#include "project.h"
#include <QPainter>
void TilesetEditorTileSelector::draw() {
if (!this->primaryTileset || !this->primaryTileset->tiles
|| !this->secondaryTileset || !this->secondaryTileset->tiles) {
this->setPixmap(QPixmap());
}
int totalTiles = Project::getNumTilesTotal();
int primaryLength = this->primaryTileset->tiles->length();
int secondaryLength = this->secondaryTileset->tiles->length();
int height = totalTiles / this->numTilesWide;
QList<QRgb> palette = Tileset::getPalette(this->paletteId, this->primaryTileset, this->secondaryTileset);
QImage image(this->numTilesWide * 16, height * 16, QImage::Format_RGBA8888);
QPainter painter(&image);
for (uint16_t tile = 0; tile < totalTiles; tile++) {
QImage tileImage;
if (tile < primaryLength) {
tileImage = getColoredTileImage(tile, this->primaryTileset, this->secondaryTileset, this->paletteId).scaled(16, 16);
} else if (tile < Project::getNumTilesPrimary()) {
tileImage = QImage(16, 16, QImage::Format_RGBA8888);
tileImage.fill(palette.at(0));
} else if (tile < Project::getNumTilesPrimary() + secondaryLength) {
tileImage = getColoredTileImage(tile, this->primaryTileset, this->secondaryTileset, this->paletteId).scaled(16, 16);
} else {
tileImage = QImage(16, 16, QImage::Format_RGBA8888);
QPainter painter(&tileImage);
painter.fillRect(0, 0, 16, 16, palette.at(0));
}
int y = tile / this->numTilesWide;
int x = tile % this->numTilesWide;
QPoint origin = QPoint(x * 16, y * 16);
painter.drawImage(origin, tileImage.mirrored(this->xFlip, this->yFlip));
}
painter.end();
this->setPixmap(QPixmap::fromImage(image));
this->drawSelection();
}
void TilesetEditorTileSelector::select(uint16_t tile) {
QPoint coords = this->getTileCoords(tile);
SelectablePixmapItem::select(coords.x(), coords.y(), 0, 0);
this->updateSelectedTiles();
emit selectedTilesChanged();
}
void TilesetEditorTileSelector::setTilesets(Tileset *primaryTileset, Tileset *secondaryTileset) {
this->primaryTileset = primaryTileset;
this->secondaryTileset = secondaryTileset;
this->draw();
}
void TilesetEditorTileSelector::setPaletteId(int paletteId) {
this->paletteId = paletteId;
this->draw();
}
void TilesetEditorTileSelector::setTileFlips(bool xFlip, bool yFlip) {
this->xFlip = xFlip;
this->yFlip = yFlip;
this->draw();
}
void TilesetEditorTileSelector::updateSelectedTiles() {
this->selectedTiles.clear();
QPoint origin = this->getSelectionStart();
QPoint dimensions = this->getSelectionDimensions();
for (int j = 0; j < dimensions.y(); j++) {
for (int i = 0; i < dimensions.x(); i++) {
uint16_t metatileId = this->getTileId(origin.x() + i, origin.y() + j);
this->selectedTiles.append(metatileId);
}
}
}
QList<uint16_t> TilesetEditorTileSelector::getSelectedTiles() {
return this->selectedTiles;
}
uint16_t TilesetEditorTileSelector::getTileId(int x, int y) {
return static_cast<uint16_t>(y * this->numTilesWide + x);
}
void TilesetEditorTileSelector::mousePressEvent(QGraphicsSceneMouseEvent *event) {
SelectablePixmapItem::mousePressEvent(event);
this->updateSelectedTiles();
emit selectedTilesChanged();
}
void TilesetEditorTileSelector::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
SelectablePixmapItem::mouseMoveEvent(event);
this->updateSelectedTiles();
QPoint pos = this->getCellPos(event->pos());
uint16_t tile = this->getTileId(pos.x(), pos.y());
emit hoveredTileChanged(tile);
emit selectedTilesChanged();
}
void TilesetEditorTileSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
SelectablePixmapItem::mouseReleaseEvent(event);
this->updateSelectedTiles();
emit selectedTilesChanged();
}
void TilesetEditorTileSelector::hoverMoveEvent(QGraphicsSceneHoverEvent *event) {
QPoint pos = this->getCellPos(event->pos());
uint16_t tile = this->getTileId(pos.x(), pos.y());
emit this->hoveredTileChanged(tile);
}
void TilesetEditorTileSelector::hoverLeaveEvent(QGraphicsSceneHoverEvent*) {
emit this->hoveredTileCleared();
}
QPoint TilesetEditorTileSelector::getTileCoords(uint16_t tile) {
if (tile >= Project::getNumTilesTotal())
{
// Invalid tile.
return QPoint(0, 0);
}
return QPoint(tile % this->numTilesWide, tile / this->numTilesWide);
}