begin overhaul of region map editor

- use a configurator to give users more options
- support firered (tilemaps, layout layers)
- add support for 4bpp and 8bpp tilemaps
- use new region map entries json format
- temporarily disabled city tilemap editor code
This commit is contained in:
garak 2022-03-01 15:32:44 -05:00 committed by garakmon
parent c1e3415417
commit 1334369906
24 changed files with 3468 additions and 1976 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,729 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>RegionMapPropertiesDialog</class>
<widget class="QDialog" name="RegionMapPropertiesDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>564</width>
<height>1016</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Region Map Properties</string>
</property>
<layout class="QFormLayout" name="formLayout_4">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>alias</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="config_alias"/>
</item>
<item row="1" column="1">
<widget class="QLabel" name="message_alias">
<property name="styleSheet">
<string notr="true">color: #ff5c33</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="message_width">
<property name="styleSheet">
<string notr="true">color: #ff5c33</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QLabel" name="message_height">
<property name="styleSheet">
<string notr="true">color: #ff5c33</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="8" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Tilemap Properties</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>format</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="config_tilemapFormat">
<item>
<property name="text">
<string>plain</string>
</property>
</item>
<item>
<property name="text">
<string>4bpp</string>
</property>
</item>
<item>
<property name="text">
<string>8bpp</string>
</property>
</item>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="message_tilemapFormat">
<property name="styleSheet">
<string notr="true">color: #ff5c33</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>width</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="config_tilemapWidth">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>75</width>
<height>0</height>
</size>
</property>
<property name="maximum">
<number>255</number>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="message_tilemapWidth">
<property name="styleSheet">
<string notr="true">color: #ff5c33</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>height</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="config_tilemapHeight">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>75</width>
<height>0</height>
</size>
</property>
<property name="maximum">
<number>255</number>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="message_tilemapHeight">
<property name="styleSheet">
<string notr="true">color: #ff5c33</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>tileset path</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<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="QLineEdit" name="config_tilemapImagePath">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>350</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="browse_tilesetImagePath">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="7" column="1">
<widget class="QLabel" name="message_tilemapImagePath">
<property name="styleSheet">
<string notr="true">color: #ff5c33</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>tilemap path</string>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QFrame" name="frame_2">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<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="QLineEdit" name="config_tilemapBinPath"/>
</item>
<item>
<widget class="QPushButton" name="browse_tilemapBinPath">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="9" column="1">
<widget class="QLabel" name="message_tilemapBinPath">
<property name="styleSheet">
<string notr="true">color: #ff5c33</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="10" column="1">
<widget class="QFrame" name="frame_3">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<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="QLineEdit" name="config_tilemapPalettePath"/>
</item>
<item>
<widget class="QPushButton" name="browse_tilemapPalettePath">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="10" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>palette path</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="10" column="0" colspan="2">
<widget class="QGroupBox" name="group_layout">
<property name="title">
<string>Layout Properties</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>format</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="config_layoutFormat">
<item>
<property name="text">
<string>C array</string>
</property>
</item>
<item>
<property name="text">
<string>binary</string>
</property>
</item>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="message_layoutFormat">
<property name="styleSheet">
<string notr="true">color: #ff5c33</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>layout path</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QFrame" name="frame_5">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<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="QLineEdit" name="config_layoutPath"/>
</item>
<item>
<widget class="QPushButton" name="browse_layoutPath">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="message_layoutPath">
<property name="styleSheet">
<string notr="true">color: #ff5c33</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QWidget" name="widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_7">
<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="QLabel" name="label_16">
<property name="text">
<string>width</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="config_layoutWidth">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>75</width>
<height>0</height>
</size>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>255</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_6">
<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>
<widget class="QLabel" name="label_12">
<property name="text">
<string>left offset</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="config_leftOffs">
<property name="maximum">
<number>255</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>85</width>
<height>2</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="message_layoutWidth">
<property name="styleSheet">
<string notr="true">color: #ff5c33</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QWidget" name="widget_2" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_8">
<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="QLabel" name="label_17">
<property name="text">
<string>height</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="config_layoutHeight">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>75</width>
<height>0</height>
</size>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>255</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>87</width>
<height>13</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_13">
<property name="text">
<string>top offset</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="config_topOffs"/>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>87</width>
<height>13</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="7" column="1">
<widget class="QLabel" name="message_layoutHeight">
<property name="styleSheet">
<string notr="true">color: #ff5c33</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="13" column="0">
<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="13" column="1">
<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="3" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>width</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="config_width">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>75</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>height</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QSpinBox" name="config_height">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>75</width>
<height>0</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>RegionMapPropertiesDialog</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>RegionMapPropertiesDialog</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>
</ui>

View file

@ -3,7 +3,6 @@
#define REGIONMAP_H
#include "map.h"
#include "project.h"
#include "tilemaptileselector.h"
#include "history.h"
@ -15,6 +14,11 @@
#include <QGraphicsScene>
#include <QGraphicsView>
#include <memory>
using std::shared_ptr;
class Project;
enum RegionMapEditorBox {
BackgroundImage = 1,
CityMapImage = 2,
@ -41,71 +45,44 @@ public:
~RegionMapHistoryItem() {}
};
class RegionMapEntry
struct LayoutSquare
{
public:
RegionMapEntry()=default;
RegionMapEntry(int x_, int y_, int width_, int height_, QString name_) {
this-> x = x_;
this-> y = y_;
this-> width = width_;
this-> height = height_;
this-> name = name_;
}
QString map_section;
int x;
int y;
int width;
int height;
QString name;
void setX(int);
void setY(int);
void setWidth(int);
void setHeight(int);
bool has_map = false;
};
class RegionMapSquare
struct MapSectionEntry
{
public:
int x = -1;
int y = -1;
uint8_t tile_img_id = 0x00;
uint8_t secid = 0x00;
bool has_map = false;
bool has_city_map = false;
bool duplicated = false;
QString map_name;
QString mapsec;
QString city_map_name;
QString name = "";
int x = 0;
int y = 0;
int width = 1;
int height = 1;
bool valid = false;
};
class RegionMap
{
public:
RegionMap() = default;
RegionMap() = delete;
RegionMap(Project *);
Project *project = nullptr;
QVector<RegionMapSquare> map_squares;
History<RegionMapHistoryItem*> history;
History<RegionMapHistoryItem*> history; // TODO
QMap<QString, QString> sMapNamesMap;
QMap<QString, RegionMapEntry> mapSecToMapEntry;
QVector<QString> sMapNames;
bool loadMapData(poryjson::Json);
bool loadTilemap(poryjson::Json);
bool loadLayout(poryjson::Json);
bool loadEntries();
const int padLeft = 1;
const int padRight = 3;
const int padTop = 2;
const int padBottom = 3;
bool init(Project*);
bool readBkgImgBin();
bool readLayout();
void setEntries(tsl::ordered_map<QString, MapSectionEntry> *entries) { this->region_map_entries = entries; }
MapSectionEntry getEntry(QString section);
void save();
void saveTileImages();
void saveBkgImgBin();
void saveTilemap();
void saveLayout();
void saveOptions(int id, QString sec, QString name, int x, int y);
@ -115,38 +92,115 @@ public:
void clearImage();
void replaceSectionId(unsigned oldId, unsigned newId);
int width();
int height();
QSize imgSize();
unsigned getTileId(int index);
shared_ptr<TilemapTile> getTile(int index);
unsigned getTileId(int x, int y);
shared_ptr<TilemapTile> getTile(int x, int y);
bool squareHasMap(int index);
QString squareMapSection(int index);
int squareX(int index);
int squareY(int index);
bool squareInLayout(int x, int y);
int firstLayoutIndex() { return this->offset_left + this->offset_top * this->tilemap_width; }
void setTileId(int index, unsigned id);
void setTile(int index, TilemapTile &tile);
void setTileData(int index, unsigned id, bool hFlip, bool vFlip, int palette);
int getMapSquareIndex(int x, int y);
QString palPath();
QString pngPath();
void setTemporaryPngPath(QString);
QString cityTilesPath();
void setTemporaryCityTilesPath(QString);
QString entriesPath() { return this->entries_path; }
QVector<uint8_t> getTiles();
void setTiles(QVector<uint8_t> tileIds);
QStringList getLayers() { return this->layout_layers; }
void setLayer(QString layer) { this->current_layer = layer; }
QString getLayer() { return this->current_layer; }
QString fixCase(QString);
private:
int layout_width_;
int layout_height_;
int img_width_;
int img_height_;
int padLeft() { return this->offset_left; }
int padTop() { return this->offset_top; }
int padRight() { return this->tilemap_width - this->layout_width - this->offset_left; }
int padBottom() { return this->tilemap_height - this->layout_height - this->offset_top; }
QString region_map_png_path;
QString region_map_bin_path;
QString region_map_entries_path;
QString region_map_layout_bin_path;
int tilemapWidth() { return this->tilemap_width; }
int tilemapHeight() { return this->tilemap_height; }
int tilemapSize() { return this->tilemap_width * this->tilemap_height; }
int tilemapBytes();
int tilemapToLayoutIndex(int index);
TilemapFormat tilemapFormat() { return this->tilemap_format; }
int pixelWidth() { return this->tilemap_width * 8; }
int pixelHeight() { return this->tilemap_height * 8; }
QString fullPath(QString local);
private:
tsl::ordered_map<QString, MapSectionEntry> *region_map_entries = nullptr;
QString alias;
int tilemap_width;
int tilemap_height;
// default is 32x20 (or 30x20 / screen size??)
int region_width;
int region_height;
// default is 28x15
int layout_width;
int layout_height;
int offset_left;
int offset_top;
//int ;
//int img_height_;
TilemapFormat tilemap_format;
enum class LayoutFormat { None, Binary, CArray };
LayoutFormat layout_format;
QString tileset_path;
QString tilemap_path;
QString palette_path = "";
QString entries_path;
QString layout_path;
// TODO: default values?
QString layout_array_label;
bool layout_uses_layers = false;
//QList<QString> layout_layers;
//QList<RegionMapSquare> map_squares;
QList<shared_ptr<TilemapTile>> tilemap;
// what about separate array for layout
// and a pointer to an entries / map sections object (from project? or editor?)
QStringList layout_layers;
QString current_layer;
// TODO: qstring, or {section name, x, y, section_id, has_map}
QMap<QString, QList<LayoutSquare>> layouts; // key: layer, value: layout list
// TODO
QString city_map_tiles_path;
// todo: no???? why would i be doing this it's pointless
// let the user figure this shit out
bool region_map_png_needs_saving = false;
bool city_map_png_needs_saving = false;
int img_index_(int x, int y);
int layout_index_(int x, int y);
int get_tilemap_index(int x, int y);
int get_layout_index(int x, int y);
};
#endif // REGIONMAP_H

View file

@ -9,6 +9,7 @@
#include "wildmoninfo.h"
#include "parseutil.h"
#include "orderedjson.h"
#include "regionmap.h"
#include <QStringList>
#include <QList>

View file

@ -6,6 +6,8 @@
#include "regionmaplayoutpixmapitem.h"
#include "regionmapentriespixmapitem.h"
#include "regionmap.h"
#include "orderedjson.h"
#include "project.h"
#include <QMainWindow>
#include <QGraphicsSceneMouseEvent>
@ -24,9 +26,10 @@ public:
explicit RegionMapEditor(QWidget *parent = 0, Project *pro = nullptr);
~RegionMapEditor();
RegionMap *region_map;
RegionMap *region_map = nullptr;
tsl::ordered_map<QString, RegionMap *> region_maps;
bool loadRegionMapData();
bool load();
bool loadCityMaps();
void setCurrentSquareOptions();
@ -56,9 +59,11 @@ private:
Ui::RegionMapEditor *ui;
Project *project;
poryjson::Json rmConfigJson;
History<RegionMapHistoryItem*> history;
int currIndex;
int currIndex = 0;
unsigned selectedCityTile;
unsigned selectedImageTile;
QString activeEntry;
@ -86,6 +91,17 @@ private:
RegionMapPixmapItem *region_map_item = nullptr;
CityMapPixmapItem *city_map_item = nullptr;
bool loadRegionMapEntries();
bool saveRegionMapEntries();
tsl::ordered_map<QString, MapSectionEntry> region_map_entries;
void buildConfigDialog();
poryjson::Json configRegionMapDialog();
void buildUpdateConfigDialog();
poryjson::Json buildDefaultJson();
poryjson::Json getJsonFromAlias(QString alias);
bool loadRegionMapData();
void initShortcuts();
void displayRegionMap();
void displayRegionMapImage();
@ -94,11 +110,11 @@ private:
void displayRegionMapLayoutOptions();
void updateRegionMapLayoutOptions(int index);
void displayRegionMapTileSelector();
void updateLayerDisplayed();
void displayCityMapTileSelector();
void displayCityMap(QString name);
void displayRegionMapEntryOptions();
void updateRegionMapEntryOptions(QString);
void importTileImage(bool city = false);
bool createCityMap(QString name);
bool tryInsertNewMapEntry(QString);
@ -114,21 +130,22 @@ private slots:
void on_action_RegionMap_ClearImage_triggered();
void on_action_RegionMap_ClearLayout_triggered();
void on_action_Swap_triggered();
void on_action_Import_RegionMap_ImageTiles_triggered();
void on_action_Import_CityMap_ImageTiles_triggered();
void on_tabWidget_Region_Map_currentChanged(int);
void on_pushButton_RM_Options_delete_clicked();
void on_comboBox_RM_ConnectedMap_textActivated(const QString &);
void on_comboBox_RM_Entry_MapSection_textActivated(const QString &);
void on_comboBox_regionSelector_textActivated(const QString &);
void on_comboBox_layoutLayer_textActivated(const QString &);
void on_spinBox_RM_Entry_x_valueChanged(int);
void on_spinBox_RM_Entry_y_valueChanged(int);
void on_spinBox_RM_Entry_width_valueChanged(int);
void on_spinBox_RM_Entry_height_valueChanged(int);
void on_spinBox_tilePalette_valueChanged(int);
void on_checkBox_tileHFlip_stateChanged(int);
void on_checkBox_tileVFlip_stateChanged(int);
void on_pushButton_CityMap_add_clicked();
void on_verticalSlider_Zoom_Map_Image_valueChanged(int);
void on_verticalSlider_Zoom_Image_Tiles_valueChanged(int);
void on_verticalSlider_Zoom_City_Map_valueChanged(int);
void on_verticalSlider_Zoom_City_Tiles_valueChanged(int);
void on_comboBox_CityMap_picker_currentTextChanged(const QString &);
void on_lineEdit_RM_MapName_textEdited(const QString &);
void onHoveredRegionMapTileChanged(int x, int y);

View file

@ -0,0 +1,47 @@
#ifndef REGIONMAPPROPERTIESDIALOG_H
#define REGIONMAPPROPERTIESDIALOG_H
#include "orderedjson.h"
#include <QDialog>
#include <QFileDialog>
class Project;
namespace Ui {
class RegionMapPropertiesDialog;
}
class RegionMapPropertiesDialog : public QDialog
{
Q_OBJECT
public:
explicit RegionMapPropertiesDialog(QWidget *parent = nullptr);
~RegionMapPropertiesDialog();
void setProject(Project *project) { this->project = project; }
void setProperties(poryjson::Json object);
poryjson::Json saveToJson();
virtual void accept() override;
private:
Ui::RegionMapPropertiesDialog *ui;
Project *project = nullptr;
void hideMessages();
QString browse(QString filter, QFileDialog::FileMode mode);
private slots:
void on_browse_tilesetImagePath_clicked();
void on_browse_tilemapBinPath_clicked();
void on_browse_tilemapPalettePath_clicked();
void on_browse_layoutPath_clicked();
//void on_buttonBox_accepted();
};
#endif // REGIONMAPPROPERTIESDIALOG_H

View file

@ -1,29 +1,146 @@
#pragma once
#ifndef TILEMAPTILESELECTOR_H
#define TILEMAPTILESELECTOR_H
#include "selectablepixmapitem.h"
#include "paletteutil.h"
#include <memory>
using std::shared_ptr;
enum class TilemapFormat { Plain, BPP_4, BPP_8 };
class TilemapTile {
unsigned raw_ = 0;
unsigned id_ = 0;
bool hFlip_ = false;
bool vFlip_ = false;
int palette_ = 0;
protected:
TilemapTile(unsigned raw, unsigned id, bool hFlip, bool vFlip, int palette) : raw_(raw), id_(id), hFlip_(hFlip), vFlip_(vFlip), palette_(palette) {}
virtual ~TilemapTile() {}
public:
TilemapTile()=default;
TilemapTile(TilemapTile &other) {
this->raw_ = other.raw();
this->id_ = other.id();
this->hFlip_ = other.hFlip();
this->vFlip_ = other.vFlip();
this->palette_ = other.palette();
}
TilemapTile &operator=(const TilemapTile &other) {
this->raw_ = other.raw();
this->id_ = other.id();
this->hFlip_ = other.hFlip();
this->vFlip_ = other.vFlip();
this->palette_ = other.palette();
return *this;
}
virtual void copy(TilemapTile &other) {
this->raw_ = other.raw();
this->id_ = other.id();
this->hFlip_ = other.hFlip();
this->vFlip_ = other.vFlip();
this->palette_ = other.palette();
}
virtual unsigned raw() const { return this->raw_; }
virtual unsigned id() const { return this->id_; }
virtual bool hFlip() const { return this->hFlip_; }
virtual bool vFlip() const { return this->vFlip_; }
virtual int palette() const { return this->palette_; }
virtual void setId(unsigned id) { this->id_ = id; }
virtual void setHFlip(bool hFlip) { this->hFlip_ = hFlip; }
virtual void setVFlip(bool vFlip) { this->vFlip_ = vFlip; }
virtual void setPalette(int palette) { this->palette_ = palette; }
virtual QString info() {
return QString("Tile: 0x") + QString("%1 ").arg(this->id(), 4, 16, QChar('0')).toUpper();
}
};
class PlainTile : public TilemapTile {
public:
PlainTile(unsigned raw) : TilemapTile(raw, raw, false, false, 0) {}
~PlainTile() {}
};
class BPP4Tile : public TilemapTile {
public:
BPP4Tile(unsigned raw) : TilemapTile(
raw,
raw & 0x3ff, // tileId
!!(raw & 0x0400), // hFlip
!!(raw & 0x0800), // vFlip
(raw & 0xf000) >> 12 // palette
) {}
~BPP4Tile() {}
virtual QString info() override {
return TilemapTile::info() + QString("hFlip: %1 vFlip: %2 palette: %3").arg(this->hFlip()).arg(this->vFlip()).arg(this->palette());
}
};
class BPP8Tile : public TilemapTile {
public:
BPP8Tile(unsigned raw) : TilemapTile(
raw,
raw & 0x3ff, // tileId
!!(raw & 0x0400), // hFlip
!!(raw & 0x0800), // vFlip
0 // palette
) {}
~BPP8Tile() {}
virtual QString info() override {
return TilemapTile::info() + QString("hFlip: %1 vFlip: %2").arg(this->hFlip()).arg(this->vFlip());
}
};
class TilemapTileSelector: public SelectablePixmapItem {
Q_OBJECT
public:
TilemapTileSelector(QPixmap pixmap_): SelectablePixmapItem(8, 8, 1, 1) {
this->tilemap = pixmap_;
this->setPixmap(this->tilemap);
this->numTilesWide = tilemap.width() / 8;
TilemapTileSelector(QString tilesetFilepath, TilemapFormat format, QString palFilepath): SelectablePixmapItem(8, 8, 1, 1) {
this->tileset = QImage(tilesetFilepath);
this->format = format;
bool err;
this->palette = PaletteUtil::parse(palFilepath, &err);
this->setPixmap(QPixmap::fromImage(this->tileset));
this->numTilesWide = this->tileset.width() / 8;
this->selectedTile = 0x00;
setAcceptHoverEvents(true);
}
void draw();
void select(unsigned tileId);
unsigned getSelectedTile();
void setPalette();
int pixelWidth;
int pixelHeight;
void select(unsigned tileId);
unsigned selectedTile = 0;
QPixmap tilemap;
QImage tileImg(unsigned tileId);
void selectVFlip(bool hFlip) { this->tile_hFlip = hFlip; }
bool tile_hFlip = false;
void selectHFlip(bool vFlip) { this->tile_vFlip = vFlip; }
bool tile_vFlip = false;
void selectPalette(int palette) { this->tile_palette = palette; }
int tile_palette = 0;
QImage tileset;
TilemapFormat format = TilemapFormat::Plain;
QList<QRgb> palette;
QImage tileImg(shared_ptr<TilemapTile> tile);
protected:
void mousePressEvent(QGraphicsSceneMouseEvent*);

View file

@ -77,6 +77,7 @@ SOURCES += src/core/block.cpp \
src/ui/shortcutseditor.cpp \
src/ui/multikeyedit.cpp \
src/ui/preferenceeditor.cpp \
src/ui/regionmappropertiesdialog.cpp \
src/config.cpp \
src/editor.cpp \
src/main.cpp \
@ -151,6 +152,7 @@ HEADERS += include/core/block.h \
include/ui/shortcutseditor.h \
include/ui/multikeyedit.h \
include/ui/preferenceeditor.h \
include/ui/regionmappropertiesdialog.h \
include/config.h \
include/editor.h \
include/mainwindow.h \
@ -169,11 +171,13 @@ FORMS += forms/mainwindow.ui \
forms/newtilesetdialog.ui \
forms/mapimageexporter.ui \
forms/shortcutseditor.ui \
forms/preferenceeditor.ui
forms/preferenceeditor.ui \
forms/regionmappropertiesdialog.ui
RESOURCES += \
resources/images.qrc \
resources/themes.qrc
resources/themes.qrc \
resources/text.qrc
INCLUDEPATH += include
INCLUDEPATH += include/core

Binary file not shown.

After

Width:  |  Height:  |  Size: 574 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

7
resources/text.qrc Normal file
View file

@ -0,0 +1,7 @@
<RCC>
<qresource prefix="/">
<file>text/region_map_default_emerald.json</file>
<file>text/region_map_default_firered.json</file>
<file>text/region_map_default_ruby.json</file>
</qresource>
</RCC>

View file

@ -0,0 +1,40 @@
{
"region_maps": [
{
"alias": "hoenn",
"width": 32,
"height": 20,
"tilemap": {
"width": 64,
"height": 64,
"format": "plain",
"tileset_path": "graphics/pokenav/region_map.png",
"tilemap_path": "graphics/pokenav/region_map_map.bin"
},
"layout": {
"width": 28,
"height": 15,
"offset_left": 1,
"offset_top": 2,
"format": "binary",
"path": "graphics/pokenav/region_map_section_layout.bin"
}
},
{
"alias": "pokedex area screen",
"width": 32,
"height": 20,
"tilemap": {
"width": 32,
"height": 32,
"format": "8bpp",
"tileset_path": "graphics/pokedex/region_map.png",
"tilemap_path": "graphics/pokedex/region_map.bin"
},
"layout": null
}
],
"entries": {
"path": "src/data/region_map/region_map_sections.json"
}
}

View file

@ -0,0 +1,91 @@
{
"region_maps": [
{
"alias": "kanto",
"width": 30,
"height": 20,
"tilemap": {
"width": 30,
"height": 20,
"format": "4bpp",
"tileset_path": "graphics/region_map/region_map.png",
"tilemap_path": "graphics/region_map/kanto.bin",
"palette": "graphics/region_map/region_map.pal"
},
"layout": {
"width": 22,
"height": 15,
"offset_left": 4,
"offset_top": 4,
"format": "C array",
"path": "src/data/region_map/region_map_layout_kanto.h"
}
},
{
"alias": "sevii_123",
"width": 30,
"height": 20,
"tilemap": {
"width": 30,
"height": 20,
"format": "4bpp",
"tileset_path": "graphics/region_map/region_map.png",
"tilemap_path": "graphics/region_map/sevii_123.bin",
"palette": "graphics/region_map/region_map.pal"
},
"layout": {
"width": 22,
"height": 15,
"offset_left": 4,
"offset_top": 4,
"format": "C array",
"path": "src/data/region_map/region_map_layout_sevii_123.h"
}
},
{
"alias": "sevii_45",
"width": 30,
"height": 20,
"tilemap": {
"width": 30,
"height": 20,
"format": "4bpp",
"tileset_path": "graphics/region_map/region_map.png",
"tilemap_path": "graphics/region_map/sevii_45.bin",
"palette": "graphics/region_map/region_map.pal"
},
"layout": {
"width": 22,
"height": 15,
"offset_left": 4,
"offset_top": 4,
"format": "C array",
"path": "src/data/region_map/region_map_layout_sevii_45.h"
}
},
{
"alias": "sevii_67",
"width": 30,
"height": 20,
"tilemap": {
"width": 30,
"height": 20,
"format": "4bpp",
"tileset_path": "graphics/region_map/region_map.png",
"tilemap_path": "graphics/region_map/sevii_67.bin",
"palette": "graphics/region_map/region_map.pal"
},
"layout": {
"width": 22,
"height": 15,
"offset_left": 4,
"offset_top": 4,
"format": "C array",
"path": "src/data/region_map/region_map_layout_sevii_67.h"
}
}
],
"revion_map_sections": {
"path": "src/data/region_map/region_map_sections.json"
}
}

View file

@ -34,6 +34,9 @@ QString ParseUtil::readTextFile(const QString &path) {
return QString();
}
QTextStream in(&file);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
in.setCodec("UTF-8");
#endif // Qt6 defaults to UTF-8, but setCodec is renamed to setEncoding
QString text = "";
while (!in.atEnd()) {
text += in.readLine() + '\n';

View file

@ -1,6 +1,7 @@
#include "regionmap.h"
#include "regionmapeditor.h"
#include "paletteutil.h"
#include "project.h"
#include "log.h"
#include "config.h"
@ -11,6 +12,8 @@
#include <QImage>
#include <math.h>
using std::make_shared;
static bool ensureRegionMapFileExists(QString filepath) {
if (!QFile::exists(filepath)) {
logError(QString("Region map file does not exist: %1").arg(filepath));
@ -19,390 +22,420 @@ static bool ensureRegionMapFileExists(QString filepath) {
return true;
}
bool RegionMap::init(Project *pro) {
QString path = pro->root;
this->project = pro;
RegionMap::RegionMap(Project *project) {
this->project = project;
}
QSize dimensions = porymapConfig.getRegionMapDimensions();
img_width_ = dimensions.width();
img_height_ = dimensions.height();
bool RegionMap::loadMapData(poryjson::Json data) {
poryjson::Json::object mapObject = data.object_items();
layout_width_ = img_width_ - this->padLeft - this->padRight;
layout_height_ = img_height_ - this->padTop - this->padBottom;
this->alias = mapObject["alias"].string_value();
this->region_width = mapObject["width"].int_value();
this->region_height = mapObject["height"].int_value();
region_map_bin_path = path + "/graphics/pokenav/region_map_map.bin";
region_map_png_path = path + "/graphics/pokenav/region_map.png";
region_map_entries_path = path + "/src/data/region_map/region_map_entries.h";
region_map_layout_bin_path = path + "/graphics/pokenav/region_map_section_layout.bin";
city_map_tiles_path = path + "/graphics/pokenav/zoom_tiles.png";
bool allFilesExist = ensureRegionMapFileExists(region_map_bin_path)
&& ensureRegionMapFileExists(region_map_png_path)
&& ensureRegionMapFileExists(region_map_entries_path)
&& ensureRegionMapFileExists(region_map_layout_bin_path)
&& ensureRegionMapFileExists(city_map_tiles_path);
poryjson::Json tilemapJson = mapObject["tilemap"];
poryjson::Json layoutJson = mapObject["layout"];
return allFilesExist
&& readBkgImgBin()
&& readLayout();
this->tilemap.clear();
this->layout_layers.clear();
this->layouts.clear();
loadTilemap(tilemapJson);
loadLayout(layoutJson);
}
int RegionMap::tilemapBytes() {
// bytes per tile multiplier
int multiplier = 1;
switch (tilemap_format) {
case TilemapFormat::Plain:
multiplier = 1;
break;
case TilemapFormat::BPP_4:
multiplier = 2;
break;
case TilemapFormat::BPP_8:
multiplier = 2;
break;
}
return tilemapSize() * multiplier;
}
bool RegionMap::loadTilemap(poryjson::Json tilemapJson) {
bool errored = false;
poryjson::Json::object tilemapObject = tilemapJson.object_items();
this->tilemap_width = tilemapObject["width"].int_value();
this->tilemap_height = tilemapObject["height"].int_value();
QString tilemapFormat = tilemapObject["format"].string_value();
QMap<QString, TilemapFormat> formatsMap = { {"plain", TilemapFormat::Plain}, {"4bpp", TilemapFormat::BPP_4}, {"8bpp", TilemapFormat::BPP_8} };
this->tilemap_format = formatsMap[tilemapFormat];
this->tileset_path = tilemapObject["tileset_path"].string_value();
this->tilemap_path = tilemapObject["tilemap_path"].string_value();
//! TODO: set a config option for this
if (tilemapObject.contains("palette")) {
this->palette_path = tilemapObject["palette"].string_value();
}
QFile tilemapFile(fullPath(tilemap_path));
if (!tilemapFile.open(QIODevice::ReadOnly)) {
logError(QString("Failed to open region map tilemap file %1.").arg(tilemap_path));
return false;
}
QDataStream dataStream(&tilemapFile);
dataStream.setByteOrder(QDataStream::LittleEndian);
if (tilemapFile.size() < tilemapBytes()) {
logError(QString("The region map tilemap at %1 is too small.").arg(tilemap_path));
return false;
}
this->tilemap.resize(tilemapSize());
switch (this->tilemap_format) {
case TilemapFormat::Plain:
for (int i = 0; i < tilemapBytes(); i++) {
uint8_t tile;
dataStream >> tile;
this->tilemap[i] = make_shared<PlainTile>(tile);
}
break;
case TilemapFormat::BPP_4:
for (int i = 0; i < tilemapBytes(); i+=2) {
uint16_t tile;
dataStream >> tile;
this->tilemap[i / 2] = make_shared<BPP4Tile>(tile & 0xffff);
}
break;
case TilemapFormat::BPP_8:
for (int i = 0; i < tilemapBytes(); i+=2) {
uint16_t tile;
dataStream >> tile;
this->tilemap[i / 2] = make_shared<BPP8Tile>(tile & 0xffff);
}
break;
}
tilemapFile.close();
return !errored;
}
bool RegionMap::loadLayout(poryjson::Json layoutJson) {
if (layoutJson.is_null()) {
this->layout_format = LayoutFormat::None;
return true;
}
poryjson::Json::object layoutObject = layoutJson.object_items();
QString layoutFormat = layoutObject["format"].string_value();
QMap<QString, LayoutFormat> layoutFormatMap = { {"binary", LayoutFormat::Binary}, {"C array", LayoutFormat::CArray} };
this->layout_format = layoutFormatMap[layoutFormat];
this->layout_path = layoutObject["path"].string_value();
this->layout_width = layoutObject["width"].int_value();
this->layout_height = layoutObject["height"].int_value();
this->offset_left = layoutObject["offset_left"].int_value();
this->offset_top = layoutObject["offset_top"].int_value();
bool errored = false;
switch (this->layout_format) {
case LayoutFormat::Binary:
{
// TODO: only one layer supported for binary layouts (change or no?)
QFile binFile(fullPath(this->layout_path));
if (!binFile.open(QIODevice::ReadOnly)) {
logError(QString("Failed to read region map layout binary file %1").arg(this->layout_path));
return false;
}
QByteArray mapBinData = binFile.readAll();
binFile.close();
if (mapBinData.size() != this->layout_width * this->layout_height) {
logError("Region map layout file size does not match given dimensions for " + this->alias);
return false;
}
// for layouts with only a single layer, it is called main
this->layout_layers.append("main");
QList<LayoutSquare> layout;
for (int y = 0; y < this->layout_height; y++) {
for (int x = 0; x < this->layout_width; x++) {
int bin_index = x + y * this->layout_width;
uint8_t square_section_id = mapBinData.at(bin_index);
QString square_section_name = project->mapSectionValueToName.value(square_section_id);
LayoutSquare square;
square.map_section = square_section_name;
square.has_map = (square_section_name != "MAPSEC_NONE" && !square_section_name.isEmpty());
square.x = x;
square.y = y;
layout.append(square);
}
}
this->layouts["main"] = layout;
break;
}
case LayoutFormat::CArray:
{
// TODO: pokeruby / non-layered style C array or just an array of mapsections
ParseUtil parser;
QString text = parser.readTextFile(fullPath(this->layout_path));
QRegularExpression re("(?<qual_1>static)?\\s?(?<qual_2>const)?\\s?(?<type>[A-Za-z0-9_]+)?\\s+(?<label>[A-Za-z0-9_]+)"
"(\\[(?<const_1>[A-Za-z0-9_]+)\\])?(\\[(?<const_2>[A-Za-z0-9_]+)\\])?(\\[(?<const_3>[A-Za-z0-9_]+)\\])?\\s+=");
// check for layers, extract info
QRegularExpressionMatch match = re.match(text);
if (match.hasMatch()) {
// TODO: keep track of labels and consts
QString qualifiers = match.captured("qual_1") + match.captured("qual_2");
QString type = match.captured("type");
QString label = match.captured("label");
QStringList constants;
if (!match.captured("const_1").isNull()) constants.append(match.captured("const_1"));
if (!match.captured("const_2").isNull()) constants.append(match.captured("const_2"));
if (!match.captured("const_3").isNull()) constants.append(match.captured("const_3"));
// find layers
QRegularExpression reLayers("(?<layer>\\[(?<label>LAYER_[A-Za-z0-9_]+)\\][^\\[\\]]+)");
QRegularExpressionMatchIterator i = reLayers.globalMatch(text);
while (i.hasNext()) {
QRegularExpressionMatch m = i.next();
QString layerName = m.captured("label");
QString layerLayout = m.captured("layer");
QRegularExpression rowRe("{(?<row>[A-Z0-9_, ]+)}");
QRegularExpressionMatchIterator j = rowRe.globalMatch(layerLayout);
this->layout_layers.append(layerName);
QList<LayoutSquare> layout;
int y = 0;
while (j.hasNext()) {
QRegularExpressionMatch n = j.next();
QString row = n.captured("row");
QStringList rowSections = row.split(',');
int x = 0;
for (QString section : rowSections) {
QString square_section_name = section.trimmed();
int square_index = get_tilemap_index(x, y);
LayoutSquare square;
square.map_section = square_section_name;
square.has_map = (square_section_name != "MAPSEC_NONE" && !square_section_name.isEmpty());
square.x = x;
square.y = y;
layout.append(square);
x++;
}
y++;
}
this->layouts[layerName] = layout;
}
} else {
logError("Region map layout is not readable.");
return false;
}
break;
}
}
this->current_layer = this->layout_layers.first();
return !errored;
}
void RegionMap::save() {
logInfo("Saving region map data.");
saveTileImages();
saveBkgImgBin();
saveLayout();
porymapConfig.setRegionMapDimensions(this->img_width_, this->img_height_);
// TODO
}
void RegionMap::saveTileImages() {
if (region_map_png_needs_saving) {
QFile backgroundTileFile(pngPath());
if (backgroundTileFile.open(QIODevice::ReadOnly)) {
QByteArray imageData = backgroundTileFile.readAll();
QImage pngImage = QImage::fromData(imageData);
this->region_map_png_path = project->root + "/graphics/pokenav/region_map.png";
pngImage.save(pngPath());
void RegionMap::saveTilemap() {
// TODO
PaletteUtil::writeJASC(project->root + "/graphics/pokenav/region_map.pal", pngImage.colorTable(), 0x70, 0x20);
}
region_map_png_needs_saving = false;
}
if (city_map_png_needs_saving) {
QFile cityTileFile(cityTilesPath());
if (cityTileFile.open(QIODevice::ReadOnly)) {
QByteArray imageData = cityTileFile.readAll();
QImage cityTilesImage = QImage::fromData(imageData);
this->city_map_tiles_path = project->root + "/graphics/pokenav/zoom_tiles.png";
cityTilesImage.save(cityTilesPath());
}
city_map_png_needs_saving = false;
}
}
bool RegionMap::readBkgImgBin() {
map_squares.clear();
QFile binFile(region_map_bin_path);
if (!binFile.open(QIODevice::ReadOnly)) {
logError(QString("Failed to open region map map file %1.").arg(region_map_bin_path));
return false;
}
QByteArray mapBinData = binFile.readAll();
binFile.close();
if (mapBinData.size() < img_height_ * img_width_) {
logError(QString("The region map tilemap at %1 is too small.").arg(region_map_bin_path));
return false;
}
for (int m = 0; m < img_height_; m++) {
for (int n = 0; n < img_width_; n++) {
RegionMapSquare square;
square.tile_img_id = mapBinData.at(n + m * img_width_ * 2);
map_squares.append(square);
}
}
return true;
}
void RegionMap::saveBkgImgBin() {
QByteArray data(pow(img_width_ * 2, 2),0);
for (int m = 0; m < img_height_; m++) {
for (int n = 0; n < img_width_; n++) {
data[n + m * img_width_ * 2] = map_squares[n + m * img_width_].tile_img_id;
}
}
QFile file(region_map_bin_path);
if (!file.open(QIODevice::WriteOnly)) return;
file.write(data);
file.close();
}
bool RegionMap::readLayout() {
sMapNames.clear();
sMapNamesMap.clear();
mapSecToMapEntry.clear();
QFile file(region_map_entries_path);
if (!file.open(QIODevice::ReadOnly)) {
logError(QString("Failed to read region map entries file %1").arg(region_map_entries_path));
return false;
}
QMap<QString, QString> qmap;
bool mapNamesQualified = false, mapEntriesQualified = false;
QTextStream in(&file);
while (!in.atEnd()) {
QString line = in.readLine();
if (line.contains(QRegularExpression(".*sMapName.*="))) {
QRegularExpression reBefore("sMapName_(.*)\\[");
QRegularExpression reAfter("_\\(\"(.*)\"");
QString const_name = reBefore.match(line).captured(1);
QString full_name = reAfter.match(line).captured(1);
sMapNames.append(const_name);
sMapNamesMap.insert(const_name, full_name);
if (!mapNamesQualified) {
project->dataQualifiers.insert("region_map_entries_names", project->getDataQualifiers(line, "sMapName_" + const_name));
mapNamesQualified = true;
}
} else if (line.contains("MAPSEC")) {
QRegularExpression reBefore("\\[(.*)\\]");
QRegularExpression reAfter("{(.*)}");
QStringList entry = reAfter.match(line).captured(1).remove(" ").split(",");
QString mapsec = reBefore.match(line).captured(1);
QString insertion = entry[4].remove("sMapName_");
qmap.insert(mapsec, sMapNamesMap.value(insertion));
mapSecToMapEntry[mapsec] = {
// x y width height name
entry[0].toInt(), entry[1].toInt(), entry[2].toInt(), entry[3].toInt(), insertion
};
} else if (line.contains("gRegionMapEntries")) {
if (!mapEntriesQualified) {
project->dataQualifiers.insert("region_map_entries", project->getDataQualifiers(line, "gRegionMapEntries"));
mapEntriesQualified = true;
}
}
}
file.close();
project->mapSecToMapHoverName = qmap;
QFile binFile(region_map_layout_bin_path);
if (!binFile.open(QIODevice::ReadOnly)) {
logError(QString("Failed to read region map layout file %1").arg(region_map_layout_bin_path));
return false;
}
QByteArray mapBinData = binFile.readAll();
binFile.close();
for (int y = 0; y < layout_height_; y++) {
for (int x = 0; x < layout_width_; x++) {
int i = img_index_(x,y);
if (i >= map_squares.size()) {
continue;
}
int layoutIndex = layout_index_(x,y);
if (layoutIndex >= mapBinData.size()) {
continue;
}
uint8_t id = static_cast<uint8_t>(mapBinData.at(layoutIndex));
map_squares[i].secid = id;
QString secname = project->mapSectionValueToName.value(id);
if (secname != "MAPSEC_NONE") {
map_squares[i].has_map = true;
}
map_squares[i].mapsec = secname;
map_squares[i].map_name = sMapNamesMap.value(mapSecToMapEntry.value(secname).name);
map_squares[i].x = x;
map_squares[i].y = y;
}
}
return true;
}
void RegionMap::saveLayout() {
QString entries_text;
QString layout_text;
// TODO
entries_text += "#ifndef GUARD_DATA_REGION_MAP_REGION_MAP_ENTRIES_H\n";
entries_text += "#define GUARD_DATA_REGION_MAP_REGION_MAP_ENTRIES_H\n\n";
for (auto sName : sMapNames) {
entries_text += QString("%1%2u8 sMapName_")
.arg(project->dataQualifiers.value("region_map_entries_names").isStatic ? "static " : "")
.arg(project->dataQualifiers.value("region_map_entries_names").isConst ? "const " : "")
+ sName + "[] = _(\"" + sMapNamesMap.value(sName) + "\");\n";
}
entries_text += QString("\n%1%2struct RegionMapLocation gRegionMapEntries[] = {\n")
.arg(project->dataQualifiers.value("region_map_entries").isStatic ? "static " : "")
.arg(project->dataQualifiers.value("region_map_entries").isConst ? "const " : "");
int longest = 1;
for (auto sec : project->mapSectionNameToValue.keys()) {
if (sec.length() > longest) longest = sec.length();
}
for (auto sec : project->mapSectionNameToValue.keys()) {
if (!mapSecToMapEntry.contains(sec) || sec == "MAPSEC_NONE") continue;
RegionMapEntry entry = mapSecToMapEntry.value(sec);
entries_text += " [" + sec + QString("]%1= {").arg(QString(" ").repeated(1 + longest - sec.length()))
+ QString::number(entry.x) + ", " + QString::number(entry.y) + ", "
+ QString::number(entry.width) + ", " + QString::number(entry.height) + ", sMapName_" + entry.name + "},\n";
}
entries_text += "};\n\n#endif // GUARD_DATA_REGION_MAP_REGION_MAP_ENTRIES_H\n";
project->saveTextFile(region_map_entries_path, entries_text);
QByteArray data;
for (int m = 0; m < layout_height_; m++) {
for (int n = 0; n < layout_width_; n++) {
int i = img_index_(n,m);
data.append(map_squares[i].secid);
}
}
QFile bfile(region_map_layout_bin_path);
if (!bfile.open(QIODevice::WriteOnly)) return;
bfile.write(data);
bfile.close();
}
void RegionMap::saveOptions(int id, QString sec, QString name, int x, int y) {
resetSquare(id);
int index = getMapSquareIndex(x + this->padLeft, y + this->padTop);
if (!sec.isEmpty()) {
// Validate the input section name.
if (!project->mapSectionNameToValue.contains(sec)) {
sec = "MAPSEC_NONE";
name = QString();
}
this->map_squares[index].has_map = sec == "MAPSEC_NONE" ? false : true;
this->map_squares[index].secid = static_cast<uint8_t>(project->mapSectionNameToValue.value(sec));
this->map_squares[index].mapsec = sec;
if (!name.isEmpty()) {
this->map_squares[index].map_name = name;
this->project->mapSecToMapHoverName.insert(sec, name);
QString sName = fixCase(sec);
sMapNamesMap.insert(sName, name);
if (!mapSecToMapEntry.keys().contains(sec)) {
sMapNames.append(sName);
RegionMapEntry entry(x, y, 1, 1, sName);
mapSecToMapEntry.insert(sec, entry);
}
}
this->map_squares[index].x = x;
this->map_squares[index].y = y;
this->map_squares[index].duplicated = false;
}
// TODO
}
void RegionMap::resetSquare(int index) {
this->map_squares[index].mapsec = "MAPSEC_NONE";
this->map_squares[index].map_name = QString();
this->map_squares[index].has_map = false;
this->map_squares[index].secid = static_cast<uint8_t>(project->mapSectionNameToValue.value("MAPSEC_NONE"));
this->map_squares[index].has_city_map = false;
this->map_squares[index].city_map_name = QString();
this->map_squares[index].duplicated = false;
// TODO
}
void RegionMap::clearLayout() {
for (int i = 0; i < map_squares.size(); i++)
resetSquare(i);
// TODO
}
void RegionMap::clearImage() {
for (int i = 0; i < map_squares.size(); i++)
this->map_squares[i].tile_img_id = 0x00;
// TODO
}
void RegionMap::replaceSectionId(unsigned oldId, unsigned newId) {
for (auto &square : map_squares) {
if (square.secid == oldId) {
square.has_map = false;
square.secid = newId;
QString secname = project->mapSectionValueToName.value(newId);
if (secname != "MAPSEC_NONE") square.has_map = true;
square.mapsec = secname;
square.map_name = sMapNamesMap.value(mapSecToMapEntry.value(secname).name);
}
}
// TODO
}
void RegionMap::resize(int newWidth, int newHeight) {
QVector<RegionMapSquare> new_squares;
// TODO
for (int y = 0; y < newHeight; y++) {
for (int x = 0; x < newWidth; x++) {
RegionMapSquare square;
if (x < img_width_ && y < img_height_) {
square = map_squares[getMapSquareIndex(x, y)];
} else if (x < newWidth - this->padRight && y < newHeight - this->padBottom) {
square.tile_img_id = 0;
square.x = x;
square.y = y;
square.mapsec = "MAPSEC_NONE";
} else {
square.tile_img_id = 0;
}
new_squares.append(square);
}
}
this->map_squares = new_squares;
this->img_width_ = newWidth;
this->img_height_ = newHeight;
this->layout_width_ = newWidth - this->padLeft - this->padRight;
this->layout_height_ = newHeight - this->padTop - this->padBottom;
}
QVector<uint8_t> RegionMap::getTiles() {
QVector<uint8_t> tileIds;
for (auto square : map_squares) {
tileIds.append(square.tile_img_id);
}
// TODO: change this to use TilemapTile instead of uint8_t
return tileIds;
}
void RegionMap::setTiles(QVector<uint8_t> tileIds) {
if (tileIds.size() != map_squares.size()) return;
// TODO
int i = 0;
for (uint8_t tileId : tileIds) {
map_squares[i].tile_img_id = tileId;
i++;
}
}
// Layout coords to image index.
int RegionMap::img_index_(int x, int y) {
return ((x + this->padLeft) + (y + this->padTop) * img_width_);
int RegionMap::get_tilemap_index(int x, int y) {
return ((x + this->offset_left) + (y + this->offset_top) * this->tilemap_width);
}
// Layout coords to layout index.
int RegionMap::layout_index_(int x, int y) {
return (x + y * layout_width_);
int RegionMap::get_layout_index(int x, int y) {
return x + y * this->tilemap_width;
}
int RegionMap::width() {
return this->img_width_;
unsigned RegionMap::getTileId(int index) {
if (index < tilemap.size()) {
return tilemap[index]->id();
}
return 0;
}
int RegionMap::height() {
return this->img_height_;
}
shared_ptr<TilemapTile> RegionMap::getTile(int index) {
if (index < tilemap.size()) {
return tilemap[index];
}
QSize RegionMap::imgSize() {
return QSize(img_width_ * 8, img_height_ * 8);
return nullptr;
}
unsigned RegionMap::getTileId(int x, int y) {
return map_squares.at(x + y * img_width_).tile_img_id;
int index = x + y * tilemap_width;
return getTileId(index);
}
shared_ptr<TilemapTile> RegionMap::getTile(int x, int y) {
int index = x + y * tilemap_width;
return getTile(index);
}
void RegionMap::setTileId(int index, unsigned id) {
if (index < tilemap.size()) {
tilemap[index]->setId(id);
}
}
void RegionMap::setTile(int index, TilemapTile &tile) {
if (index < tilemap.size()) {
tilemap[index]->copy(tile);
}
}
void RegionMap::setTileData(int index, unsigned id, bool hFlip, bool vFlip, int palette) {
if (index < tilemap.size()) {
tilemap[index]->setId(id);
tilemap[index]->setHFlip(hFlip);
tilemap[index]->setVFlip(vFlip);
tilemap[index]->setPalette(palette);
}
}
int RegionMap::tilemapToLayoutIndex(int index) {
int x = index % this->tilemap_width;
if (x < this->offset_left) return -1;
int y = index / this->tilemap_width;
if (y < this->offset_top) return -1;
int layoutX = x - this->offset_left;
if (layoutX >= this->layout_width) return -1;
int layoutY = y - this->offset_top;
if (layoutY >= this->layout_height) return -1;
return layoutX + layoutY * this->layout_width;
}
bool RegionMap::squareHasMap(int index) {
int layoutIndex = tilemapToLayoutIndex(index);
return (layoutIndex < 0 || !this->layouts.contains(this->current_layer)) ? false : this->layouts[this->current_layer][layoutIndex].has_map;
}
QString RegionMap::squareMapSection(int index) {
int layoutIndex = tilemapToLayoutIndex(index);
return (layoutIndex < 0 || !this->layouts.contains(this->current_layer)) ? QString() : this->layouts[this->current_layer][layoutIndex].map_section;
}
int RegionMap::squareX(int index) {
int layoutIndex = tilemapToLayoutIndex(index);
return (layoutIndex < 0 || !this->layouts.contains(this->current_layer)) ? -1 : this->layouts[this->current_layer][layoutIndex].x;
}
int RegionMap::squareY(int index) {
int layoutIndex = tilemapToLayoutIndex(index);
return (layoutIndex < 0 || !this->layouts.contains(this->current_layer)) ? -1 : this->layouts[this->current_layer][layoutIndex].y;
}
bool RegionMap::squareInLayout(int x, int y) {
return !((x < this->offset_left) || (x >= (this->offset_left + this->layout_width))
|| (y < this->offset_top) || (y >= (this->offset_top + this->layout_height)));
}
MapSectionEntry RegionMap::getEntry(QString section) {
if (this->region_map_entries->contains(section))
return this->region_map_entries->operator[](section);
else
return MapSectionEntry();
}
QString RegionMap::palPath() {
return this->project->root + "/" + this->palette_path;
}
QString RegionMap::pngPath() {
return this->region_map_png_path;
}
void RegionMap::setTemporaryPngPath(QString path) {
this->region_map_png_path = path;
this->region_map_png_needs_saving = true;
return this->project->root + "/" + this->tileset_path;
}
QString RegionMap::cityTilesPath() {
return this->city_map_tiles_path;
}
// TODO
void RegionMap::setTemporaryCityTilesPath(QString path) {
this->city_map_tiles_path = path;
this->city_map_png_needs_saving = true;
return QString();
}
// From x, y of image.
int RegionMap::getMapSquareIndex(int x, int y) {
int index = (x + y * img_width_);
return index < map_squares.length() ? index : 0;
int index = (x + y * this->tilemap_width);
return ((index < tilemap.length()) && (index >= 0)) ? index : 0;
}
// For turning a MAPSEC_NAME into a unique identifier sMapName-style variable.
@ -425,18 +458,6 @@ QString RegionMap::fixCase(QString caps) {
return camel;
}
void RegionMapEntry::setX(const int val) {
this->x = val;
}
void RegionMapEntry::setY(int val) {
this->y = val;
}
void RegionMapEntry::setWidth(int val) {
this->width = val;
}
void RegionMapEntry::setHeight(int val) {
this->height = val;
QString RegionMap::fullPath(QString local) {
return this->project->root + "/" + local;
}

View file

@ -388,7 +388,7 @@ void MainWindow::setProjectSpecificUIVisibility()
ui->label_AllowEscapeRope->setVisible(true);
// TODO: pokefirered is not set up for the Region Map Editor and vice versa.
// porymap will crash on attempt. Remove below once resolved
ui->actionRegion_Map_Editor->setVisible(false);
ui->actionRegion_Map_Editor->setVisible(true);
break;
}
@ -1799,13 +1799,13 @@ void MainWindow::connectSubEditorsToShortcutsEditor() {
tilesetEditor, &TilesetEditor::applyUserShortcuts);
// TODO: Remove this check when the region map editor supports pokefirered.
if (projectConfig.getBaseGameVersion() != BaseGameVersion::pokefirered) {
//if (projectConfig.getBaseGameVersion() != BaseGameVersion::pokefirered) {
if (!regionMapEditor)
initRegionMapEditor();
if (regionMapEditor)
connect(shortcutsEditor, &ShortcutsEditor::shortcutsSaved,
regionMapEditor, &RegionMapEditor::applyUserShortcuts);
}
//}
}
void MainWindow::on_actionPencil_triggered()
@ -3150,8 +3150,7 @@ void MainWindow::on_actionRegion_Map_Editor_triggered() {
bool MainWindow::initRegionMapEditor() {
this->regionMapEditor = new RegionMapEditor(this, this->editor->project);
bool success = this->regionMapEditor->loadRegionMapData()
&& this->regionMapEditor->loadCityMaps();
bool success = this->regionMapEditor->load();
if (!success) {
delete this->regionMapEditor;
this->regionMapEditor = nullptr;

View file

@ -26,15 +26,16 @@ void CityMapPixmapItem::init() {
void CityMapPixmapItem::draw() {
QImage image(width_ * 8, height_ * 8, QImage::Format_RGBA8888);
QPainter painter(&image);
for (int i = 0; i < data.size() / 2; i++) {
QImage img = this->tile_selector->tileImg(data[i * 2]);// need to skip every other tile
int x = i % width_;
int y = i / width_;
QPoint pos = QPoint(x * 8, y * 8);
painter.drawImage(pos, img);
}
painter.end();
// TODO: construct temporary tile from this based on the id?
// QPainter painter(&image);
// for (int i = 0; i < data.size() / 2; i++) {
// QImage img = this->tile_selector->tileImg(data[i * 2]);// need to skip every other tile
// int x = i % width_;
// int y = i / width_;
// QPoint pos = QPoint(x * 8, y * 8);
// painter.drawImage(pos, img);
// }
// painter.end();
this->setPixmap(QPixmap::fromImage(image));
}

File diff suppressed because it is too large Load diff

View file

@ -3,12 +3,12 @@
void RegionMapEntriesPixmapItem::draw() {
if (!region_map) return;
RegionMapEntry entry = region_map->mapSecToMapEntry.value(currentSection);
MapSectionEntry entry = this->region_map->getEntry(currentSection);
bool selectingEntry = false;
int entry_x, entry_y, entry_w, entry_h;
if (entry.name.isEmpty() || entry.name == "MAPSEC_NONE") {
if (!entry.valid || entry.name == "MAPSEC_NONE") {
entry_x = entry_y = 0;
entry_w = entry_h = 1;
} else {
@ -17,25 +17,25 @@ void RegionMapEntriesPixmapItem::draw() {
entry_w = entry.width, entry_h = entry.height;
}
QImage image(region_map->width() * 8, region_map->height() * 8, QImage::Format_RGBA8888);
QImage image(region_map->tilemapWidth() * 8, region_map->tilemapHeight() * 8, QImage::Format_RGBA8888);
QPainter painter(&image);
for (int i = 0; i < region_map->map_squares.size(); i++) {
QImage bottom_img = this->tile_selector->tileImg(region_map->map_squares[i].tile_img_id);
for (int i = 0; i < region_map->tilemapSize(); i++) {
QImage bottom_img = this->tile_selector->tileImg(region_map->getTile(i));
QImage top_img(8, 8, QImage::Format_RGBA8888);
int x = i % region_map->width();
int y = i / region_map->width();
int x = i % region_map->tilemapWidth();
int y = i / region_map->tilemapWidth();
bool insideEntry = false;
if (selectingEntry) {
if (x == entry_x + this->region_map->padLeft && y == entry_y + this->region_map->padTop)
if (x == entry_x + this->region_map->padLeft() && y == entry_y + this->region_map->padTop())
insideEntry = true;
else if (x - this->region_map->padLeft - entry_x < entry_w && x >= entry_x + this->region_map->padLeft
&& y - this->region_map->padTop - entry_y < entry_h && y >= entry_y + this->region_map->padTop)
else if (x - this->region_map->padLeft() - entry_x < entry_w && x >= entry_x + this->region_map->padLeft()
&& y - this->region_map->padTop() - entry_y < entry_h && y >= entry_y + this->region_map->padTop())
insideEntry = true;
}
if (insideEntry) {
top_img.fill(QColor(255, 68, 68));
} else if (region_map->map_squares[i].has_map) {
} else if (region_map->squareHasMap(i)) {
top_img.fill(Qt::gray);
} else {
top_img.fill(Qt::black);
@ -44,7 +44,7 @@ void RegionMapEntriesPixmapItem::draw() {
painter.setOpacity(1);
painter.drawImage(pos, bottom_img);
painter.save();
painter.setOpacity(0.55);
painter.setOpacity(0.65);
painter.drawImage(pos, top_img);
painter.restore();
}
@ -63,25 +63,26 @@ void RegionMapEntriesPixmapItem::select(int x, int y) {
this->selectedTile = index;
this->updateSelectedTile();
emit selectedTileChanged(this->region_map->map_squares[index].mapsec);
emit selectedTileChanged(this->region_map->squareMapSection(index));
}
void RegionMapEntriesPixmapItem::select(int index) {
int x = index % this->region_map->width();
int y = index / this->region_map->width();
int x = index % this->region_map->tilemapWidth();
int y = index / this->region_map->tilemapWidth();
SelectablePixmapItem::select(x, y, 0, 0);
this->selectedTile = index;
this->updateSelectedTile();
emit selectedTileChanged(this->region_map->map_squares[index].mapsec);
emit selectedTileChanged(this->region_map->squareMapSection(index));
}
void RegionMapEntriesPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) {
QPoint pos = this->getCellPos(event->pos());
int x = pos.x() - this->region_map->padLeft;
int y = pos.y() - this->region_map->padTop;
int x = pos.x() - this->region_map->padLeft();
int y = pos.y() - this->region_map->padTop();
RegionMapEntry entry = this->region_map->mapSecToMapEntry.value(currentSection);
//RegionMapEntry entry = this->region_map->mapSecToMapEntry.value(currentSection);
MapSectionEntry entry = this->region_map->getEntry(currentSection);
pressedX = x - entry.x;
pressedY = y - entry.y;
if (entry.x == x && entry.y == y) {
@ -92,6 +93,7 @@ void RegionMapEntriesPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event
}
}
// TODO: update this function, especially bounds check
void RegionMapEntriesPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
if (!draggingEntry) {
event->ignore();
@ -99,14 +101,14 @@ void RegionMapEntriesPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
}
QPoint pos = this->getCellPos(event->pos());
int new_x = pos.x() - this->region_map->padLeft - pressedX;
int new_y = pos.y() - this->region_map->padTop - pressedY;
int new_x = pos.x() - this->region_map->padLeft() - pressedX;
int new_y = pos.y() - this->region_map->padTop() - pressedY;
RegionMapEntry entry = this->region_map->mapSecToMapEntry.value(currentSection);
MapSectionEntry entry = this->region_map->getEntry(currentSection);
// check to make sure not moving out of bounds
if (new_x + entry.width > this->region_map->width() - this->region_map->padLeft - this->region_map->padRight
|| new_y + entry.height > this->region_map->height() - this->region_map->padTop - this->region_map->padBottom
if (new_x + entry.width > this->region_map->tilemapWidth() - this->region_map->padLeft() //- this->region_map->padRight
|| new_y + entry.height > this->region_map->tilemapHeight() - this->region_map->padTop() //- this->region_map->padBottom
|| new_x < 0 || new_y < 0)
return;

View file

@ -3,24 +3,29 @@
void RegionMapLayoutPixmapItem::draw() {
if (!region_map) return;
QImage image(region_map->width() * 8, region_map->height() * 8, QImage::Format_RGBA8888);
QImage image(region_map->pixelWidth(), region_map->pixelHeight(), QImage::Format_RGBA8888);
QPainter painter(&image);
for (int i = 0; i < region_map->map_squares.size(); i++) {
QImage bottom_img = this->tile_selector->tileImg(region_map->map_squares[i].tile_img_id);
for (int i = 0; i < region_map->tilemapSize(); i++) {
QImage bottom_img = this->tile_selector->tileImg(region_map->getTile(i));
QImage top_img(8, 8, QImage::Format_RGBA8888);
if (region_map->map_squares[i].has_map) {
if (region_map->squareHasMap(i)) {
top_img.fill(Qt::gray);
} else {
top_img.fill(Qt::black);
}
int x = i % region_map->width();
int y = i / region_map->width();
int x = i % region_map->tilemapWidth();
int y = i / region_map->tilemapWidth();
QPoint pos = QPoint(x * 8, y * 8);
painter.setOpacity(1);
painter.drawImage(pos, bottom_img);
painter.save();
painter.setOpacity(0.55);
if (region_map->squareInLayout(x, y)) {
painter.setOpacity(0.55);
} else {
painter.setOpacity(0.8);
}
painter.drawImage(pos, top_img);
painter.restore();
}
@ -40,8 +45,8 @@ void RegionMapLayoutPixmapItem::select(int x, int y) {
}
void RegionMapLayoutPixmapItem::select(int index) {
int x = index % this->region_map->width();
int y = index / this->region_map->width();
int x = index % this->region_map->tilemapWidth();
int y = index / this->region_map->tilemapWidth();
SelectablePixmapItem::select(x, y, 0, 0);
this->selectedTile = index;
this->updateSelectedTile();
@ -51,15 +56,15 @@ void RegionMapLayoutPixmapItem::select(int index) {
void RegionMapLayoutPixmapItem::highlight(int x, int y, int red) {
this->highlightedTile = red;
SelectablePixmapItem::select(x + this->region_map->padLeft, y + this->region_map->padTop, 0, 0);
SelectablePixmapItem::select(x + this->region_map->padLeft(), y + this->region_map->padTop(), 0, 0);
draw();
}
void RegionMapLayoutPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) {
QPoint pos = this->getCellPos(event->pos());
int index = this->region_map->getMapSquareIndex(pos.x(), pos.y());
if (this->region_map->map_squares[index].x >= 0
&& this->region_map->map_squares[index].y >= 0) {
if (this->region_map->squareX(index) >= 0
&& this->region_map->squareY(index) >= 0) {
SelectablePixmapItem::mousePressEvent(event);
this->updateSelectedTile();
emit selectedTileChanged(this->selectedTile);

View file

@ -3,13 +3,13 @@
void RegionMapPixmapItem::draw() {
if (!region_map) return;
QImage image(region_map->width() * 8, region_map->height() * 8, QImage::Format_RGBA8888);
QImage image(region_map->pixelWidth(), region_map->pixelHeight(), QImage::Format_RGBA8888);
QPainter painter(&image);
for (int i = 0; i < region_map->map_squares.size(); i++) {
QImage img = this->tile_selector->tileImg(region_map->map_squares[i].tile_img_id);
int x = i % region_map->width();
int y = i / region_map->width();
for (int i = 0; i < region_map->tilemapSize(); i++) {
QImage img = this->tile_selector->tileImg(region_map->getTile(i));
int x = i % region_map->tilemapWidth();
int y = i / region_map->tilemapWidth();
QPoint pos = QPoint(x * 8, y * 8);
painter.drawImage(pos, img);
}
@ -23,8 +23,13 @@ void RegionMapPixmapItem::paint(QGraphicsSceneMouseEvent *event) {
QPointF pos = event->pos();
int x = static_cast<int>(pos.x()) / 8;
int y = static_cast<int>(pos.y()) / 8;
int index = x + y * region_map->width();
this->region_map->map_squares[index].tile_img_id = this->tile_selector->selectedTile;
int index = x + y * region_map->tilemapWidth();
this->region_map->setTileData(index,
this->tile_selector->selectedTile,
this->tile_selector->tile_hFlip,
this->tile_selector->tile_vFlip,
this->tile_selector->tile_palette
);
draw();
}
}
@ -54,8 +59,8 @@ void RegionMapPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
QPointF pos = event->pos();
int x = static_cast<int>(pos.x()) / 8;
int y = static_cast<int>(pos.y()) / 8;
if (x < this->region_map->width() && x >= 0
&& y < this->region_map->height() && y >= 0) {
if (x < this->region_map->tilemapWidth() && x >= 0
&& y < this->region_map->tilemapHeight() && y >= 0) {
emit this->hoveredRegionMapTileChanged(x, y);
emit mouseEvent(event, this);
}

View file

@ -0,0 +1,263 @@
#include "project.h"
#include "regionmappropertiesdialog.h"
#include "ui_regionmappropertiesdialog.h"
RegionMapPropertiesDialog::RegionMapPropertiesDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::RegionMapPropertiesDialog)
{
ui->setupUi(this);
this->hideMessages();
}
RegionMapPropertiesDialog::~RegionMapPropertiesDialog()
{
delete ui;
}
void RegionMapPropertiesDialog::hideMessages() {
ui->message_alias->setVisible(false);
ui->message_width->setVisible(false);
ui->message_height->setVisible(false);
ui->message_tilemapFormat->setVisible(false);
ui->message_tilemapWidth->setVisible(false);
ui->message_tilemapHeight->setVisible(false);
ui->message_tilemapImagePath->setVisible(false);
ui->message_tilemapBinPath->setVisible(false);
ui->message_layoutFormat->setVisible(false);
ui->message_layoutPath->setVisible(false);
ui->message_layoutWidth->setVisible(false);
ui->message_layoutHeight->setVisible(false);
this->adjustSize();
}
QString RegionMapPropertiesDialog::browse(QString filter, QFileDialog::FileMode mode) {
if (!this->project) return QString();
QFileDialog browser;
browser.setFileMode(mode);
QString filepath = browser.getOpenFileName(this, "Select a File", this->project->root, filter);
// remove the project root from the filepath
return filepath.replace(this->project->root + "/", "");
}
// TODO: test for missing fields/invalid cases
void RegionMapPropertiesDialog::setProperties(poryjson::Json json) {
poryjson::Json::object object = json.object_items();
// Region Map Properties
ui->config_alias->setText(object["alias"].string_value());
ui->config_width->setValue(object["width"].int_value());
ui->config_height->setValue(object["height"].int_value());
// Tilemap properties
poryjson::Json::object tilemap = object["tilemap"].object_items();
ui->config_tilemapFormat->setCurrentText(tilemap["format"].string_value());
ui->config_tilemapWidth->setValue(tilemap["width"].int_value());
ui->config_tilemapHeight->setValue(tilemap["height"].int_value());
ui->config_tilemapImagePath->setText(tilemap["tileset_path"].string_value());
ui->config_tilemapBinPath->setText(tilemap["tilemap_path"].string_value());
if (tilemap.contains("palette"))
ui->config_tilemapPalettePath->setText(tilemap["palette"].string_value());
// Layout props
if (object["layout"].is_null()) {
ui->group_layout->setChecked(false);
} else {
poryjson::Json::object layout = object["layout"].object_items();
ui->config_layoutFormat->setCurrentText(layout["format"].string_value());
ui->config_layoutPath->setText(layout["path"].string_value());
ui->config_layoutWidth->setValue(layout["width"].int_value());
ui->config_layoutHeight->setValue(layout["height"].int_value());
ui->config_leftOffs->setValue(layout["offset_left"].int_value());
ui->config_topOffs->setValue(layout["offset_top"].int_value());
}
}
poryjson::Json RegionMapPropertiesDialog::saveToJson() {
poryjson::Json::object config;
// TODO: make sure next comment is not a lie
// data should already be verified and valid at this point
config["alias"] = ui->config_alias->text();
config["width"] = ui->config_width->value();
config["height"] = ui->config_height->value();
poryjson::Json::object tilemapObject;
tilemapObject["width"] = ui->config_tilemapWidth->value();
tilemapObject["height"] = ui->config_tilemapHeight->value();
tilemapObject["format"] = ui->config_tilemapFormat->currentText();
tilemapObject["tileset_path"] = ui->config_tilemapImagePath->text();
tilemapObject["tilemap_path"] = ui->config_tilemapBinPath->text();
tilemapObject["palette"] = ui->config_tilemapPalettePath->text();
config["tilemap"] = tilemapObject;
if (ui->group_layout->isChecked()) {
poryjson::Json::object layoutObject;
layoutObject["format"] = ui->config_layoutFormat->currentText();
layoutObject["path"] = ui->config_layoutPath->text();
layoutObject["width"] = ui->config_layoutWidth->value();
layoutObject["height"] = ui->config_layoutHeight->value();
layoutObject["offset_left"] = ui->config_leftOffs->value();
layoutObject["offset_top"] = ui->config_topOffs->value();
config["layout"] = layoutObject;
} else {
config["layout"] = nullptr;
}
return poryjson::Json(config);
}
void RegionMapPropertiesDialog::on_browse_tilesetImagePath_clicked() {
QString path = browse("Images (*.png *.bmp)", QFileDialog::ExistingFile);
if (!path.isEmpty()) {
ui->config_tilemapImagePath->setText(path);
}
}
void RegionMapPropertiesDialog::on_browse_tilemapBinPath_clicked() {
QString path = browse("Binary (*.bin *.tilemap *.4bpp *.8bpp)", QFileDialog::AnyFile);
if (!path.isEmpty()) {
ui->config_tilemapBinPath->setText(path);
}
}
void RegionMapPropertiesDialog::on_browse_tilemapPalettePath_clicked() {
QString path = browse("Text (*.pal)", QFileDialog::AnyFile);
if (!path.isEmpty()) {
ui->config_tilemapPalettePath->setText(path);
}
}
void RegionMapPropertiesDialog::on_browse_layoutPath_clicked() {
if (ui->config_layoutFormat->currentIndex() == 0) {
QString path = browse("Text File (*.h *.c *.inc *.txt)", QFileDialog::AnyFile);
if (!path.isEmpty()) {
ui->config_layoutPath->setText(path);
}
} else {
QString path = browse("Binary (*.bin)", QFileDialog::AnyFile);
if (!path.isEmpty()) {
ui->config_layoutPath->setText(path);
}
}
}
void RegionMapPropertiesDialog::accept() {
bool valid = true;
// verify alias is okay
QString alias = ui->config_alias->text();
if (alias.isEmpty()) {
valid = false;
ui->message_alias->setText("alias cannot be empty");
ui->message_alias->setVisible(true);
} else {
QRegularExpression re("[A-Za-z0-9_\\- ]+");
int temp = 0;
QRegularExpressionValidator v(re, 0);
if (v.validate(alias, temp) != QValidator::Acceptable) {
ui->message_alias->setText("this alias is not an acceptable string");
ui->message_alias->setVisible(true);
valid = false;
}
else { // no issues detected
ui->message_alias->setText("");
ui->message_alias->setVisible(false);
}
}
// tilemap properties
// tilemap dimensions are specific: 64x32, 32x64, 16x16, 32x32, 64x64, 128x128
// TODO: why is 30x20 allowed in firered?
int tilemapWidth = ui->config_tilemapWidth->value();
int tilemapHeight = ui->config_tilemapHeight->value();
QMap<int, QList<int>> acceptableDimensions = { {16, {16}}, {30, {20}}, {32, {32, 64}}, {64, {32, 64}}, {128, {128}} };
if (!acceptableDimensions.contains(tilemapWidth)) {
// width not valid
valid = false;
ui->message_tilemapWidth->setText("tilemap width not valid, must be one of [16, 32, 64, 128]");
ui->message_tilemapWidth->setVisible(true);
} else {
ui->message_tilemapWidth->setText("");
ui->message_tilemapWidth->setVisible(false);
// check width, height combo
if (!acceptableDimensions.value(tilemapWidth).contains(tilemapHeight)) {
valid = false;
QString options = "";
for (int i : acceptableDimensions.value(tilemapWidth))
options += QString::number(i) + ", ";
options.chop(2);
ui->message_tilemapHeight->setText("tilemap height not valid, must be one of [" + options + "]");
ui->message_tilemapHeight->setVisible(true);
}
else { // no issues detected
ui->message_tilemapHeight->setText("");
ui->message_tilemapHeight->setVisible(false);
}
}
// layout dimensions
// need to verify <= tilemap dimensions, only req here
int width = ui->config_layoutWidth->value() + ui->config_leftOffs->value();
int height = ui->config_layoutHeight->value() + ui->config_topOffs->value();
if (width > tilemapWidth) {
valid = false;
ui->message_layoutWidth->setText("layout map width + left offset cannot be larger than tilemap width");
ui->message_layoutWidth->setVisible(true);
} else {
ui->message_layoutWidth->setText("");
ui->message_layoutWidth->setVisible(false);
}
if (height > tilemapHeight) {
valid = false;
ui->message_layoutHeight->setText("layout map height + top offset cannot be larger than tilemap height");
ui->message_layoutHeight->setVisible(true);
} else {
ui->message_layoutHeight->setText("");
ui->message_layoutHeight->setVisible(false);
}
// make sure paths arent empty
if (ui->config_tilemapImagePath->text().isEmpty()) {
valid = false;
ui->message_tilemapImagePath->setText("this path cannot be empty");
ui->message_tilemapImagePath->setVisible(true);
} else {
ui->message_tilemapImagePath->setText("");
ui->message_tilemapImagePath->setVisible(false);
}
if (ui->config_tilemapBinPath->text().isEmpty()) {
valid = false;
ui->message_tilemapBinPath->setText("this path cannot be empty");
ui->message_tilemapBinPath->setVisible(true);
} else {
ui->message_tilemapBinPath->setText("");
ui->message_tilemapBinPath->setVisible(false);
}
// layout path only needs to exist if layout is enabled (obviously)
if (ui->group_layout->isChecked()) {
if (ui->config_layoutPath->text().isEmpty()) {
valid = false;
ui->message_layoutPath->setText("this path cannot be empty");
ui->message_layoutPath->setVisible(true);
} else {
ui->message_layoutPath->setText("");
ui->message_layoutPath->setVisible(false);
}
} else {
ui->message_layoutPath->setText("");
ui->message_layoutPath->setVisible(false);
}
// TODO: limitations for map width/height if implementing
if (valid) {
QDialog::accept();
}
}

View file

@ -3,16 +3,16 @@
#include <QDebug>
void TilemapTileSelector::draw() {
size_t width_ = this->tilemap.width();
size_t width_ = this->tileset.width();
this->pixelWidth = width_;
size_t height_ = this->tilemap.height();
size_t height_ = this->tileset.height();
this->pixelHeight = height_;
size_t ntiles_ = (width_/8) * (height_/8);
this->numTilesWide = width_ / 8;
this->numTiles = ntiles_;
this->setPixmap(tilemap);
this->setPixmap(QPixmap::fromImage(tileset));
this->drawSelection();
}
@ -28,10 +28,6 @@ void TilemapTileSelector::updateSelectedTile() {
this->selectedTile = this->getTileId(origin.x(), origin.y());
}
unsigned TilemapTileSelector::getSelectedTile() {
return this->selectedTile;
}
unsigned TilemapTileSelector::getTileId(int x, int y) {
unsigned index = y * this->numTilesWide + x;
return index < this->numTiles ? index : this->numTiles % index;
@ -42,9 +38,49 @@ QPoint TilemapTileSelector::getTileIdCoords(unsigned tileId) {
return QPoint(index % this->numTilesWide, index / this->numTilesWide);
}
QImage TilemapTileSelector::tileImg(unsigned tileId) {
QImage TilemapTileSelector::tileImg(shared_ptr<TilemapTile> tile) {
// TODO: this is slow on the entries tab, so maybe do not do all of the redraw for every section change
unsigned tileId = tile->id();
QPoint pos = getTileIdCoords(tileId);
return this->tilemap.copy(pos.x() * 8, pos.y() * 8, 8, 8).toImage();
QImage tilesetImage = this->tileset;
tilesetImage.convertTo(QImage::Format::Format_Indexed8);
// TODO: bounds check on the palette copying
switch(this->format) {
case TilemapFormat::Plain:
{
// TODO: even allow palettes for Plain tiles?
// 1 x palette x any colors
break;
}
case TilemapFormat::BPP_4:
{
// before Qt 6, the color table is a QVector which is deprecated now, and this method does not exits
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
tilesetImage.setColorTable(this->palette.toVector().mid(tile->palette() * 16, 16));
#else
tilesetImage.setColorTable(this->palette.mid(tile->palette() * 16, 16));
#endif
break;
}
case TilemapFormat::BPP_8:
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
tilesetImage.setColorTable(this->palette.toVector());
#else
tilesetImage.setColorTable(this->palette);
#endif
break;
}
default: break;
}
// take a tile from the tileset
QImage img = tilesetImage.copy(pos.x() * 8, pos.y() * 8, 8, 8);
img = img.mirrored(tile->hFlip(), tile->vFlip());
return img;
}
void TilemapTileSelector::mousePressEvent(QGraphicsSceneMouseEvent *event) {