diff --git a/forms/regionmapeditor.ui b/forms/regionmapeditor.ui index 88856e07..517e6e0c 100644 --- a/forms/regionmapeditor.ui +++ b/forms/regionmapeditor.ui @@ -6,8 +6,8 @@ 0 0 - 829 - 644 + 957 + 535 @@ -15,98 +15,94 @@ - - - - - - - 30 - 50 - - - - - 30 - 160 - - - - Qt::StrongFocus - - - 10 - - - 100 - - - 30 - - - Qt::Vertical - - - QSlider::NoTicks - - - 1 - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - 30 - 0 - - - - - 30 - 160 - - - - Qt::StrongFocus - - - 10 - - - 100 - - - 30 - - - Qt::Vertical - - - QSlider::NoTicks - - - 1 - - - - + + + + QFrame::NoFrame + + + QFrame::Plain + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Region + + + + + + + QComboBox::AdjustToContents + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Crop View to Map Size + + + false + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + 169 + 20 + + + + + + - + @@ -158,12 +154,909 @@ + + + + + + Qt::Horizontal + + + false + + + + + 393 + 251 + + + + 0 + + + + Background Image + + + + + + + 1 + 1 + + + + true + + + + + 0 + 0 + 466 + 350 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 166 + 16 + + + + + + + + + 0 + 0 + + + + false + + + false + + + QAbstractScrollArea::AdjustIgnored + + + QGraphicsView::NoDrag + + + + + + + Qt::Vertical + + + + 16 + 166 + + + + + + + + Qt::Vertical + + + + 16 + 166 + + + + + + + + Qt::Horizontal + + + + 166 + 16 + + + + + + + + + + + + + Map Layout + + + + + + + 1 + 0 + + + + true + + + + + 0 + 0 + 466 + 350 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + false + + + false + + + QAbstractScrollArea::AdjustIgnored + + + QGraphicsView::NoDrag + + + + + + + Qt::Horizontal + + + + 166 + 16 + + + + + + + + Qt::Vertical + + + + 16 + 166 + + + + + + + + Qt::Horizontal + + + + 166 + 16 + + + + + + + + Qt::Vertical + + + + 16 + 166 + + + + + + + + + + + + + Map Entries + + + + + + + 1 + 0 + + + + true + + + + + 0 + 0 + 466 + 350 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + false + + + false + + + QAbstractScrollArea::AdjustIgnored + + + QGraphicsView::NoDrag + + + + + + + Qt::Horizontal + + + + 166 + 16 + + + + + + + + Qt::Vertical + + + + 16 + 166 + + + + + + + + Qt::Horizontal + + + + 166 + 16 + + + + + + + + Qt::Vertical + + + + 16 + 166 + + + + + + + + + + + + + + + 320 + 200 + + + + 0 + + + + + + + + 0 + 0 + + + + + 162 + 162 + + + + Qt::ScrollBarAlwaysOn + + + Qt::ScrollBarAsNeeded + + + QAbstractScrollArea::AdjustIgnored + + + true + + + Qt::AlignHCenter|Qt::AlignTop + + + + true + + + + 8 + 0 + 278 + 341 + + + + + 0 + 0 + + + + + QLayout::SetDefaultConstraint + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + true + + + + 0 + 0 + + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + QAbstractScrollArea::AdjustIgnored + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + QFrame::Panel + + + QFrame::Sunken + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + h-flip + + + + + + + v-flip + + + + + + + 15 + + + + + + + palette + + + + + + + + + + + + + + false + + + + 0 + 0 + + + + + 300 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + 1 + + + + + + + + + Layer + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + QLayout::SetNoConstraint + + + + + + 0 + 0 + + + + <html><head/><body><p>The section of the region map which the map is grouped under. This also determines the name of the map that is display when the player enters it.</p></body></html> + + + true + + + + + + + Map Section + + + + + + + + + + 0 + 0 + + + + Delete Square + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + Layout Height + + + + + + + Layout Width + + + + + + + + + + + + + + + + true + + + + 0 + 0 + + + + + 300 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + 1 + + + + + + QLayout::SetNoConstraint + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + width + + + + + + + + + + + + + height + + + + + + + + + + true + + + + 0 + 0 + + + + <html><head/><body><p>The section of the region map which the map is grouped under. This also determines the name of the map that is display when the player enters it.</p></body></html> + + + true + + + + + + + Dimensions + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + x + + + + + + + + + + + + + y + + + + + + + + + + Location + + + + + + + Map Section + + + + + + + Map Name + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + - + 30 - 0 + 50 @@ -195,1140 +1088,21 @@ + + + + Qt::Vertical + + + + 20 + 40 + + + + - - - - Qt::Vertical - - - false - - - - Qt::Horizontal - - - false - - - - - 393 - 251 - - - - 0 - - - - Background Image - - - - - - - 1 - 1 - - - - true - - - - - 0 - 0 - 350 - 225 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Horizontal - - - - 166 - 16 - - - - - - - - - 0 - 0 - - - - false - - - false - - - QAbstractScrollArea::AdjustIgnored - - - QGraphicsView::NoDrag - - - - - - - Qt::Vertical - - - - 16 - 166 - - - - - - - - Qt::Vertical - - - - 16 - 166 - - - - - - - - Qt::Horizontal - - - - 166 - 16 - - - - - - - - - - - - - Map Layout - - - - - - - 1 - 0 - - - - true - - - - - 0 - 0 - 350 - 225 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - false - - - false - - - QAbstractScrollArea::AdjustIgnored - - - QGraphicsView::NoDrag - - - - - - - Qt::Horizontal - - - - 166 - 16 - - - - - - - - Qt::Vertical - - - - 16 - 166 - - - - - - - - Qt::Horizontal - - - - 166 - 16 - - - - - - - - Qt::Vertical - - - - 16 - 166 - - - - - - - - - - - - - Map Entries - - - - - - - 1 - 0 - - - - true - - - - - 0 - 0 - 350 - 225 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - false - - - false - - - QAbstractScrollArea::AdjustIgnored - - - QGraphicsView::NoDrag - - - - - - - Qt::Horizontal - - - - 166 - 16 - - - - - - - - Qt::Vertical - - - - 16 - 166 - - - - - - - - Qt::Horizontal - - - - 166 - 16 - - - - - - - - Qt::Vertical - - - - 16 - 166 - - - - - - - - - - - - - - - 320 - 200 - - - - 0 - - - - - - - - 0 - 0 - - - - - 162 - 162 - - - - Qt::ScrollBarAlwaysOn - - - Qt::ScrollBarAsNeeded - - - QAbstractScrollArea::AdjustIgnored - - - true - - - Qt::AlignHCenter|Qt::AlignTop - - - - true - - - - 8 - 0 - 278 - 262 - - - - - 0 - 0 - - - - - QLayout::SetDefaultConstraint - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - true - - - - 0 - 0 - - - - Qt::ScrollBarAlwaysOff - - - Qt::ScrollBarAlwaysOff - - - QAbstractScrollArea::AdjustIgnored - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - - false - - - - 0 - 0 - - - - - 300 - 0 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - 1 - - - - - - QLayout::SetNoConstraint - - - - - Map Section - - - - - - - - 0 - 0 - - - - <html><head/><body><p>The section of the region map which the map is grouped under. This also determines the name of the map that is display when the player enters it.</p></body></html> - - - true - - - QComboBox::NoInsert - - - - - - - Map Name - - - - - - - true - - - - - - - City Map - - - - - - - <html><head/><body><p>The map type is a general attribute, which is used for many different things. For example. it determines whether biking or running is allowed.</p></body></html> - - - true - - - - - - - - - Delete Square - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - - true - - - - 0 - 0 - - - - - 300 - 0 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - 1 - - - - - - QLayout::SetNoConstraint - - - - - Map Section - - - - - - - true - - - - 0 - 0 - - - - <html><head/><body><p>The section of the region map which the map is grouped under. This also determines the name of the map that is display when the player enters it.</p></body></html> - - - true - - - QComboBox::NoInsert - - - - - - - Location - - - - - - - Dimensions - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - x - - - - - - - - - - - - - y - - - - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - width - - - - - - - - - - - - - height - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - Qt::Horizontal - - - false - - - - - QLayout::SetDefaultConstraint - - - - - - - City Map: - - - - - - - - - - - 0 - 0 - - - - <html><head/><body><p>Add a new city map.</p></body></html> - - - - - - - :/icons/add.ico - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - 1 - 0 - - - - - 102 - 102 - - - - true - - - - - 0 - 0 - 441 - 230 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Horizontal - - - - 166 - 16 - - - - - - - - Qt::Vertical - - - - 16 - 166 - - - - - - - - - 0 - 0 - - - - false - - - false - - - QAbstractScrollArea::AdjustIgnored - - - QGraphicsView::NoDrag - - - - - - - Qt::Vertical - - - - 16 - 166 - - - - - - - - Qt::Horizontal - - - - 166 - 16 - - - - - - - - - - - - - - 0 - 0 - - - - - 82 - 82 - - - - Qt::ScrollBarAlwaysOn - - - Qt::ScrollBarAsNeeded - - - QAbstractScrollArea::AdjustIgnored - - - true - - - Qt::AlignHCenter|Qt::AlignTop - - - - true - - - - 8 - 0 - 255 - 274 - - - - - 0 - 0 - - - - - QLayout::SetDefaultConstraint - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 20 - 40 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - true - - - - 0 - 0 - - - - Qt::ScrollBarAlwaysOff - - - Qt::ScrollBarAlwaysOff - - - QAbstractScrollArea::AdjustIgnored - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - @@ -1336,7 +1110,7 @@ 0 0 - 829 + 957 22 @@ -1362,8 +1136,6 @@ Tools - - @@ -1433,6 +1205,13 @@ + + + NoScrollComboBox + QWidget +
noscrollcombobox.h
+
+
diff --git a/forms/regionmappropertiesdialog.ui b/forms/regionmappropertiesdialog.ui new file mode 100644 index 00000000..7b627774 --- /dev/null +++ b/forms/regionmappropertiesdialog.ui @@ -0,0 +1,729 @@ + + + RegionMapPropertiesDialog + + + + 0 + 0 + 564 + 1016 + + + + + 0 + 0 + + + + Region Map Properties + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + alias + + + + + + + + + + color: #ff5c33 + + + + + + + + + + color: #ff5c33 + + + + + + + + + + color: #ff5c33 + + + + + + + + + + Tilemap Properties + + + + + + format + + + + + + + + plain + + + + + 4bpp + + + + + 8bpp + + + + + + + + color: #ff5c33 + + + + + + + + + + width + + + + + + + + 0 + 0 + + + + + 75 + 0 + + + + 255 + + + + + + + color: #ff5c33 + + + + + + + + + + height + + + + + + + + 0 + 0 + + + + + 75 + 0 + + + + 255 + + + + + + + color: #ff5c33 + + + + + + + + + + tileset path + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 350 + 0 + + + + + + + + ... + + + + + + + + + + color: #ff5c33 + + + + + + + + + + tilemap path + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + ... + + + + + + + + + + color: #ff5c33 + + + + + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + ... + + + + + + + + + + palette path + + + + + + + + + + Layout Properties + + + true + + + + + + format + + + + + + + + C array + + + + + binary + + + + + + + + color: #ff5c33 + + + + + + + + + + layout path + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + ... + + + + + + + + + + color: #ff5c33 + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + width + + + + + + + + 0 + 0 + + + + + 75 + 0 + + + + 0 + + + 255 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + left offset + + + + + + + 255 + + + + + + + Qt::Horizontal + + + + 85 + 2 + + + + + + + + + + + color: #ff5c33 + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + height + + + + + + + + 0 + 0 + + + + + 75 + 0 + + + + 0 + + + 255 + + + + + + + Qt::Horizontal + + + + 87 + 13 + + + + + + + + top offset + + + + + + + + + + Qt::Horizontal + + + + 87 + 13 + + + + + + + + + + + color: #ff5c33 + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + width + + + + + + + + 0 + 0 + + + + + 75 + 0 + + + + + + + + height + + + + + + + + 0 + 0 + + + + + 75 + 0 + + + + + + + + + + buttonBox + accepted() + RegionMapPropertiesDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + RegionMapPropertiesDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/include/core/regionmap.h b/include/core/regionmap.h index 03059266..f16f35f2 100644 --- a/include/core/regionmap.h +++ b/include/core/regionmap.h @@ -3,7 +3,6 @@ #define REGIONMAP_H #include "map.h" -#include "project.h" #include "tilemaptileselector.h" #include "history.h" @@ -15,6 +14,11 @@ #include #include +#include +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 map_squares; - History history; + History history; // TODO - QMap sMapNamesMap; - QMap mapSecToMapEntry; - QVector 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 *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 getTile(int index); unsigned getTileId(int x, int y); + shared_ptr 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 getTiles(); void setTiles(QVector 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 *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 layout_layers; + + //QList map_squares; + QList> 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> 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 diff --git a/include/project.h b/include/project.h index a081733b..97730481 100644 --- a/include/project.h +++ b/include/project.h @@ -9,6 +9,7 @@ #include "wildmoninfo.h" #include "parseutil.h" #include "orderedjson.h" +#include "regionmap.h" #include #include diff --git a/include/ui/regionmapeditor.h b/include/ui/regionmapeditor.h index 1dee23f7..32c74adc 100644 --- a/include/ui/regionmapeditor.h +++ b/include/ui/regionmapeditor.h @@ -6,6 +6,8 @@ #include "regionmaplayoutpixmapitem.h" #include "regionmapentriespixmapitem.h" #include "regionmap.h" +#include "orderedjson.h" +#include "project.h" #include #include @@ -24,9 +26,10 @@ public: explicit RegionMapEditor(QWidget *parent = 0, Project *pro = nullptr); ~RegionMapEditor(); - RegionMap *region_map; + RegionMap *region_map = nullptr; + tsl::ordered_map region_maps; - bool loadRegionMapData(); + bool load(); bool loadCityMaps(); void setCurrentSquareOptions(); @@ -56,9 +59,11 @@ private: Ui::RegionMapEditor *ui; Project *project; + poryjson::Json rmConfigJson; + History 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 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); diff --git a/include/ui/regionmappropertiesdialog.h b/include/ui/regionmappropertiesdialog.h new file mode 100644 index 00000000..6097d842 --- /dev/null +++ b/include/ui/regionmappropertiesdialog.h @@ -0,0 +1,47 @@ +#ifndef REGIONMAPPROPERTIESDIALOG_H +#define REGIONMAPPROPERTIESDIALOG_H + +#include "orderedjson.h" + +#include +#include + +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 diff --git a/include/ui/tilemaptileselector.h b/include/ui/tilemaptileselector.h index 95650e4b..ab266fbc 100644 --- a/include/ui/tilemaptileselector.h +++ b/include/ui/tilemaptileselector.h @@ -1,29 +1,146 @@ +#pragma once #ifndef TILEMAPTILESELECTOR_H #define TILEMAPTILESELECTOR_H #include "selectablepixmapitem.h" +#include "paletteutil.h" + +#include +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 palette; + QImage tileImg(shared_ptr tile); protected: void mousePressEvent(QGraphicsSceneMouseEvent*); diff --git a/porymap.pro b/porymap.pro index b73c4ee6..2a86653e 100644 --- a/porymap.pro +++ b/porymap.pro @@ -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 diff --git a/resources/icons/empty_cross_cursor.ico b/resources/icons/empty_cross_cursor.ico new file mode 100644 index 00000000..01b03b54 Binary files /dev/null and b/resources/icons/empty_cross_cursor.ico differ diff --git a/resources/icons/pencil_bw.ico b/resources/icons/pencil_bw.ico new file mode 100644 index 00000000..7d79243b Binary files /dev/null and b/resources/icons/pencil_bw.ico differ diff --git a/resources/text.qrc b/resources/text.qrc new file mode 100644 index 00000000..211598e4 --- /dev/null +++ b/resources/text.qrc @@ -0,0 +1,7 @@ + + + text/region_map_default_emerald.json + text/region_map_default_firered.json + text/region_map_default_ruby.json + + diff --git a/resources/text/region_map_default_emerald.json b/resources/text/region_map_default_emerald.json new file mode 100644 index 00000000..a25e459c --- /dev/null +++ b/resources/text/region_map_default_emerald.json @@ -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" + } +} diff --git a/resources/text/region_map_default_firered.json b/resources/text/region_map_default_firered.json new file mode 100644 index 00000000..aa5e199e --- /dev/null +++ b/resources/text/region_map_default_firered.json @@ -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" + } +} diff --git a/resources/text/region_map_default_ruby.json b/resources/text/region_map_default_ruby.json new file mode 100644 index 00000000..e69de29b diff --git a/src/core/parseutil.cpp b/src/core/parseutil.cpp index 4302147b..f81547ec 100644 --- a/src/core/parseutil.cpp +++ b/src/core/parseutil.cpp @@ -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'; diff --git a/src/core/regionmap.cpp b/src/core/regionmap.cpp index f8dbcac1..88116268 100644 --- a/src/core/regionmap.cpp +++ b/src/core/regionmap.cpp @@ -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 #include +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 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(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(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(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 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 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("(?static)?\\s?(?const)?\\s?(?[A-Za-z0-9_]+)?\\s+(?